#include <algorithm>
#include <iostream>
#include <map>
#include <vector>
#include <cstdlib>
#include <ctime>
#include <sys/wait.h>
#include <unistd.h>
//---------------------------------------------------------------------------
using namespace std;
//---------------------------------------------------------------------------
/// A customer
struct Customer { double discount; char name[30]; unsigned country; };
/// A country
struct Country { double tax; char name[30]; };
/// An item
struct Item { double price; char name[30]; };
/// An order
struct Order { unsigned customer,item,date; double price; };
//---------------------------------------------------------------------------
/// The database
vector<Customer> customers;
vector<Country> countries;
vector<Item> items;
vector<Order> orders;
//---------------------------------------------------------------------------
static void newOrder(unsigned customerId,unsigned itemId)
   // Place a new order (or replace an existing one if needed)
{
   // Find the customer record
   Customer& customer=customers[customerId];

   // Find the associated country
   Country& country=countries[customer.country];

   // Lookup the item
   Item& item=items[itemId];

   // Prepare the order
   Order o;
   o.customer=customerId;
   o.item=itemId;
   o.date=time(0);
   o.price=item.price*(1.0+country.tax)*(1.0-customer.discount);

   // Place an order or replace an existing one if too large
   if (orders.size()<10000000) {
      orders.push_back(o);
   } else {
      orders[random()%orders.size()]=o;
   }
}
//---------------------------------------------------------------------------
static void doOltp()
   // Place a random order
{
   newOrder(random()%customers.size(),random()%items.size());
}
//--------------------------------------------------------------------------
static inline bool orderSecondDesc(const pair<unsigned,double>& a,const pair<unsigned,double>& b) { return a.second>b.second; }
//---------------------------------------------------------------------------
static void olapQuery()
   // Perform a simple olap query
{
   // Compute the spending per customer using a simple aggregation
   map<unsigned,double> spendings;
   for (auto& o:orders)
      spendings[o.customer]+=o.price;

   // Sort by spending
   vector<pair<unsigned,double> > sortedSpendings;
   for (auto& s:spendings)
      sortedSpendings.push_back(s);
   sort(sortedSpendings.begin(),sortedSpendings.end(),orderSecondDesc);

   // Now we have the top-k, do something with it
   for (unsigned index=0,limit=min(10ul,sortedSpendings.size());index<limit;++index)
      cout << sortedSpendings[index].first << " " << sortedSpendings[index].second << " ";
   cout << endl;
}
//---------------------------------------------------------------------------
static pid_t lastQuery;
//---------------------------------------------------------------------------
static void waitForOlapIfAny()
   // Wait for the last olap query if any
{
   if (lastQuery) {
      int status;
      waitpid(lastQuery,&status,0);
      lastQuery=0;
   }
}
//---------------------------------------------------------------------------
static void doOlap()
   // Start a new olap query
{
   waitForOlapIfAny();

   // Create a new snapshot using the OS
   pid_t pid=fork();
   if (pid==0) {
      olapQuery();
      exit(0);
   } else {
      lastQuery=pid;
   }
}
//---------------------------------------------------------------------------
static void populateDB()
   // Create some toy data
{
   for (unsigned index=0;index<100;index++) {
      Country c;
      c.tax=static_cast<double>(index)/120.0;
      c.name[0]='A'+(index%20);
      c.name[1]='A'+(index/20);
      c.name[2]=0;
      countries.push_back(c);
   }
   for (unsigned index=0;index<10000;index++) {
      Customer c;
      c.discount=static_cast<double>(index%10)/100.0;
      c.name[0]='A'+(index%20);
      c.name[1]='A'+((index/20)%20);
      c.name[2]='A'+((index/400)%20);
      c.name[3]='A'+(index/8000);
      c.name[4]=0;
      c.country=99-((index+27)%100);
      customers.push_back(c);
   }
   for (unsigned index=0;index<10000;index++) {
      Item i;
      i.price=((index*index)+5*index+17)%10000+1;
      i.name[0]='A'+(index%20);
      i.name[1]='A'+((index/20)%20);
      i.name[2]='A'+((index/400)%20);
      i.name[3]='A'+(index/8000);
      i.name[4]=0;
      items.push_back(i);
   }

}
//---------------------------------------------------------------------------
int main()
{
   populateDB();

   static const unsigned totalTransactions = 1000000;
   static const unsigned olapEvery = 10000;

   for (unsigned index=0,index2=0;index<totalTransactions;index++,++index2) {
      if (index2>=olapEvery) {
         doOlap();
         index2=0;
      } else {
         doOltp();
      }
   }

   waitForOlapIfAny();
}
//---------------------------------------------------------------------------
