text:
/*
 * This file belongs to the Galois project, a C++ library for exploiting
 * parallelism. The code is being released under the terms of the 3-Clause BSD
 * License (a copy is located in LICENSE.txt at the top-level directory).
 *
 * Copyright (C) 2018, The University of Texas at Austin. All rights reserved.
 * UNIVERSITY EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES CONCERNING THIS
 * SOFTWARE AND DOCUMENTATION, INCLUDING ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR ANY PARTICULAR PURPOSE, NON-INFRINGEMENT AND WARRANTIES OF
 * PERFORMANCE, AND ANY WARRANTY THAT MIGHT OTHERWISE ARISE FROM COURSE OF
 * DEALING OR USAGE OF TRADE.  NO WARRANTY IS EITHER EXPRESS OR IMPLIED WITH
 * RESPECT TO THE USE OF THE SOFTWARE OR DOCUMENTATION. Under no circumstances
 * shall University be liable for incidental, special, indirect, direct or
 * consequential damages or loss of profits, interruption of business, or
 * related expenses which may arise from use of Software or Documentation,
 * including but not limited to those resulting from defects in Software and/or
 * Documentation, or loss or inaccuracy of data of any kind.
 */

#include <iostream>
#include <random>
#include <cmath>
#include <algorithm>
#include <utility>
#include <unordered_map>
#include <map>
#include <atomic>
#include <vector>

#include "galois/Galois.h"
#include "galois/Graph/Graph.h"
#include "galois/Graph/LCGraph.h"
#include "galois/ParallelSTL/ParallelSTL.h"
#include "llvm//Support/CommandLine.h"
#include "Lonestar/BoilerPlate.h"
#include "galois/runtime/Network.h"
#include "galois/Timer.h"
#include "galois/Timer.h"
#include "galois/Graph/FileGraph.h"
// Distributed Galois
#include "galois/graphs/Graph3.h"
#include "galois/runtime/DistSupport.h"

#include <boost/iterator/transform_iterator.hpp>
#define LATENT_VECTOR_SIZE 2
typedef struct Node {
  // double* latent_vector; //latent vector to be learned
  double latent_vector[LATENT_VECTOR_SIZE]; // latent vector to be learned
  unsigned int
      updates; // number of updates made to this node (only used by movie nodes)
  unsigned int edge_offset; // if a movie's update is interrupted, where to
                            // start when resuming
  unsigned int ID;
  bool operator==(const Node& other) const { return (ID == other.ID); }
  bool operator<(const Node& other) const { return (ID < other.ID); }
} Node;

using std::cout;
using std::endl;
namespace std {

template <>
struct hash<Node> {
  std::size_t operator()(const Node& other) const {
    using std::hash;
    using std::size_t;
    return (hash<unsigned int>()(other.ID));
  }
};
} // namespace std

// local computation graph (can't add nodes/edges at runtime)
// node data is Node, edge data is unsigned int... [movie--->user]

// typedef galois::graphs::LC_Numa_Graph<Node, unsigned int> Graph;
// typedef Graph::GraphNode GNode;
/*typedef galois::graphs::FileGraph Graph;
typedef uint64_t GNode;
Graph File_graph;
*/

// Distributed Graph Nodes.
typedef galois::graphs::ThirdGraph<Node, unsigned int,
                                   galois::graphs::EdgeDirection::Out>
    DGraph;
typedef DGraph::NodeHandle DGNode;
typedef typename DGraph::pointer Graphp;

// typedef galois::graphs::FileGraph FGraph;
// typedef galois::graphs::FileGraph::GraphNode FileGNode;

typedef galois::graphs::LC_CSR_Graph<Node, unsigned int> Graph;
typedef Graph::GraphNode GNode;

Graph graph;

// TODO : replace maps with unordered_map
std::unordered_map<GNode, Node> lookup;
std::unordered_map<GNode, DGNode> mapping;
std::unordered_map<Node, DGNode> llookup;
std::unordered_map<Node, DGNode> rlookup;
std::set<Node> requested;

volatile unsigned prog_barrier = 0;
// std::atomic<unsigned> prog_barrier;
unsigned int num_movie_nodes = 0;

using namespace galois::runtime;
typedef galois::runtime::LL::SimpleLock<true> SLock;
SLock slock;
SLock pblock;

// unsigned int LATENT_VECTOR_SIZE = 2;
double LEARNING_RATE           = 0.001;
double DECAY_RATE              = 0.9;
double LAMBDA                  = 0.001;
unsigned int MAX_MOVIE_UPDATES = 1;
unsigned int NUM_RATINGS       = 0;
static const double MINVAL     = -1e+100;
static const double MAXVAL     = 1e+100;

double vector_dot(const Node& movie_data, const Node& user_data) {
  const double* __restrict__ movie_latent = movie_data.latent_vector;
  const double* __restrict__ user_latent  = user_data.latent_vector;

  double dp = 0.0;
  for (int i = 0; i < LATENT_VECTOR_SIZE; ++i)
    dp += user_latent[i] * movie_latent[i];
  // dp += user_data.latent_vector[i] * movie_data.latent_vector[i];
  assert(std::isnormal(dp));
  return dp;
}

double calcPrediction(const Node& movie_data, const Node& user_data) {
  double pred = vector_dot(movie_data, user_data);
  pred        = std::min(MAXVAL, pred);
  pred        = std::max(MINVAL, pred);
  return pred;
}

inline void doGradientUpdate(Node& movie_data, Node& user_data,
                             unsigned int edge_rating) {
  double* __restrict__ movie_latent = movie_data.latent_vector;
  double step_size                  = LEARNING_RATE * 1.5 /
                     (1.0 + DECAY_RATE * pow(movie_data.updates + 1, 1.5));
  double* __restrict__ user_latent = user_data.latent_vector;

  double cur_error = edge_rating - vector_dot(movie_data, user_data);

  for (unsigned int i = 0; i < LATENT_VECTOR_SIZE; i++) {
    double prev_movie_val = movie_latent[i];
    double prev_user_val  = user_latent[i];
    movie_latent[i] +=
        step_size * (cur_error * prev_user_val - LAMBDA * prev_movie_val);
    user_latent[i] +=
        step_size * (cur_error * prev_movie_val - LAMBDA * prev_user_val);
  }
}

/*
inline void doGradientUpdate(Node& movie_data, Node& user_data, unsigned int
edge_rating)
{
        double step_size = LEARNING_RATE * 1.5 / (1.0 + DECAY_RATE *
pow(movie_data.updates + 1, 1.5)); double* __restrict__ movie_latent =
movie_data.latent_vector; double* __restrict__ user_latent =
user_data.latent_vector;

    //calculate error
        double cur_error = - edge_rating;
        for(unsigned int i = 0; i < LATENT_VECTOR_SIZE; i++)
        {
                cur_error += user_latent[i] * movie_latent[i];
        }

    //This is a gradient step
        for(unsigned int i = 0; i < LATENT_VECTOR_SIZE; i++)
        {
                double prev_movie_val = movie_latent[i];

                movie_latent[i] -= step_size * (cur_error * user_latent[i] +
LAMBDA * prev_movie_val); user_latent[i] -= step_size * (cur_error *
prev_movie_val + LAMBDA * user_latent[i]);
        }
}

*/

static void progBarrier_landing_pad(RecvBuffer& buf) {
  gDeserialize(buf, prog_barrier);
  ++prog_barrier;
}

static void program_barrier() {
  SendBuffer b;
  gSerialize(b, prog_barrier);
  getSystemNetworkInterface().broadcast(progBarrier_landing_pad, b);

  // unsigned old_val = prog_barrier;
  // unsigned new_val =  ++prog_barrier;
  prog_barrier++;
  // prog_barrier.compare_exchange_strong(old_val,new_val);
  printf("Entering barrier..%d\n", prog_barrier);
  do {
    // std::cout << "inside do loop\n";
    getSystemLocalDirectory().makeProgress();
    getSystemRemoteDirectory().makeProgress();
    getSystemNetworkInterface().handleReceives();
  } while (prog_barrier != networkHostNum);

  prog_barrier = 0;
  printf("Left barrier..\n");
}

void verify() {
  typedef galois::GAccumulator<double> AccumDouble;
  AccumDouble rms;
  cout << "Host:" << networkHostID << " is verifying before SGD..\n";
  // galois::do_all(graph.begin(), graph.begin()+num_movie_nodes, [&] (GNode n)
  // {
  for (auto ni = graph.begin(), ei = graph.begin() + num_movie_nodes; ni != ei;
       ++ni) {
    for (auto ii = graph.edge_begin(*ni); ii != graph.edge_end(*ni); ++ii) {
      GNode m       = graph.getEdgeDst(ii);
      double pred   = calcPrediction(graph.getData(*ni), graph.getData(m));
      double rating = graph.getEdgeData(ii);
      if (!std::isnormal(pred))
        std::cout << "Denormal Warning\n";
      rms += ((pred - rating) * (pred - rating));
    }
  }
  cout << "Reached end..\n" << endl;
  double total_rms      = rms.reduce();
  double normalized_rms = sqrt(total_rms / NUM_RATINGS);
  std::cout << "RMSE Total: " << total_rms << " Normalized: " << normalized_rms
            << std::endl;
}
void verify(Graphp g) {
  typedef galois::GAccumulator<double> AccumDouble;
  AccumDouble rms;
  cout << "Host:" << networkHostID << " is verifying after SGD..\n";
  // galois::do_all(g, [&g,&rms] (DGNode n) {
  auto ei = g->begin();
  std::advance(ei, num_movie_nodes);
  for (auto ni = g->begin(); ni != ei; ++ni) {
    for (auto ii = g->edge_begin(*ni); ii != g->edge_end(*ni); ++ii) {

      DGNode m      = g->getEdgeDst(ii);
      double pred   = calcPrediction(g->getData(*ni), g->getData(m));
      double rating = ii->getValue();
      if (!std::isnormal(pred))
        std::cout << "Denormal Warning\n";
      rms += ((pred - rating) * (pred - rating));
    }
  }
  cout << "Reached end..\n" << endl;
  double total_rms      = rms.reduce();
  double normalized_rms = sqrt(total_rms / NUM_RATINGS);
  std::cout << "RMSE Total: " << total_rms << " Normalized: " << normalized_rms
            << std::endl;
}
struct dummy_func2 {
  dummy_func2() {}
  void operator()(const DGNode& item, galois::UserContext<DGNode>& ctx) {
    cout << "Host:" << networkHostID << " is doing tp..\n";
  }
};
struct dummy_func {
  dummy_func() {}
  void operator()(GNode& item, galois::UserContext<GNode>& ctx) {}
};
void printNode(const Node& t) {
  cout << "ID: " << t.ID << endl;
  cout << "Edge_offset: " << t.edge_offset << endl;
  cout << "Updates: " << t.updates << endl;
  for (int i = 0; i < LATENT_VECTOR_SIZE; i++) {
    cout << " " << t.latent_vector[i] << endl;
  }
}

struct printN {
  Graphp g;
  printN(Graphp g_) : g(g_) {}

  void operator()(const DGNode& data, galois::UserContext<DGNode>& ctx) {
    printNode(g->getData(data));
  }
};
double RMS = 0.0;
struct verify_before {
  Graphp g;
  // double& rms;
  verify_before(Graphp g_) : g(g_) {}

  void operator()(const DGNode& movie, galois::UserContext<DGNode>& ctx) {
    cout << "Done..\n";
    /*for(auto ii = g->edge_begin(movie); ii != g->edge_end(movie); ++ii){

    cout<<"Done..\n";
    const DGNode& m = g->getEdgeDst(ii);
    cout<<"Done..\n";
    double pred = calcPrediction(g->getData(movie), g->getData(m));
    cout<<"Done..\n";
    double rating = ii->getValue();
    cout<<"Done..\n";
    if(!std::isnormal(pred))
        std::cout << "Denormal Warning\n";
    RMS += ((pred - rating)*(pred - rating));
    cout<<"Done..\n";

    }*/
    // cout<<networkHostID<<": RMS value = "<<rms<<endl;
  }
};
/* Operator */
unsigned count_done = 0;
struct sgd_algo {
  galois::GAccumulator<size_t> numNodes;
  // unsigned int num_movie_nodes;

  struct Process : public galois::runtime::Lockable {
    Graphp g;
    sgd_algo* self;
    Process() {}
    // sgd(Graphp _g) : g(_g) {}
    Process(sgd_algo* s, Graphp _g) : g(_g), self(s) {}
    // void operator()(const DGNode& n, galois::UserContext<DGNode>&)
    // {(*this)(n);}
    void operator()(const DGNode& movie, galois::UserContext<DGNode>& ctx) {

      /* For checking if graph is correct  */
      /* DGraph::edge_iterator edge_begin = g->edge_begin(movie);
       DGraph::edge_iterator edge_end = g->edge_end(movie);

       Node& movie_data = g->getData(movie);
       std::cout << "Movie : " << movie_data.ID <<"\t";

       for(auto ii = edge_begin; ii != edge_end; ++ii)
       {
         DGNode user = g->getEdgeDst(ii);
         Node& user_data = g->getData(user);
         unsigned int egde_data = ii->getValue();
         std::cout << "User : " <<  user_data.ID <<"\t";

       }
     std::cout << "\n";
     */
      // cout<<"Entered Process..\n"<<endl;

      Node& movie_data = g->getData(movie);
      // printNode(movie_data);
      DGraph::edge_iterator edge_it  = g->edge_begin(movie);
      DGraph::edge_iterator edge_end = g->edge_end(movie);

      std::advance(edge_it, movie_data.edge_offset);
      DGNode user     = g->getEdgeDst(edge_it);
      Node& user_data = g->getData(user);

      unsigned int edge_rating = edge_it->getValue();

      // Call the gradient routine
      doGradientUpdate(movie_data, user_data, edge_rating);

      ++edge_it;
      ++movie_data.edge_offset;

      // This is the last user
      if (edge_it == g->edge_end(movie)) // galois::MethodFlag::NONE))
      {
        // start back at the first edge again

        movie_data.edge_offset = 0;

        movie_data.updates++;
        cout << "Done with this movie.. count = " << ++count_done
             << " host = " << networkHostID << endl;
        if (movie_data.updates < MAX_MOVIE_UPDATES)
          ctx.push(movie);
      } else {
        ctx.push(movie);
      }
    }

    typedef int tt_has_serialize;
    void serialize(galois::runtime::SerializeBuffer& s) const {
      gSerialize(s, g);
    }
    void deserialize(galois::runtime::DeSerializeBuffer& s) {
      gDeserialize(s, g);
    }
  };
  void operator()(Graphp g) {
    DGraph::iterator ii = g->begin();
    std::advance(ii, num_movie_nodes);
    // std::advance(ii,2);

    Graph::iterator jj = graph.begin();
    std::advance(jj, num_movie_nodes);

    Node& dg_movie = g->getData(*ii);
    Node& g_movie  = graph.getData(*jj);

    std::cout << "dg_movie = " << dg_movie.ID << "\n";
    std::cout << "g_movie = " << g_movie.ID << "\n";
    std::cout << "num movie nodes  = " << num_movie_nodes << "\n";

    unsigned k = 0;
    /*for(auto it = g->begin(),ee=g->end();it != ee; ++k,++it) {
        printNode(g->getData(*it));
    }*/
    // std::cout<<"Value of k= "<<k<<std::endl;
    // galois::for_each(g->begin(), ii, printN(g),"Printing nodes");

    // galois::for_each(g, Process(this,g), "Process");
    galois::for_each(g->begin(), ii, Process(this, g), "SGD Process");
    // galois::for_each(g->begin(), ii, verify_before(g), "Verifying");

    // Verification routine
    std::cout << "Running Verification after completion\n";
    // verify(g);

    // program_barrier();
    // std::cout << "number of nodes = "<<numNodes.reduce() << "\n";
  }
};

/*void fillNode(Node& node) {
    unsigned int seed = 42;
    std::default_random_engine eng(seed);
    std::uniform_real_distribution<double> random_lv_value(0,0.1);
   double *lv = new double[LATENT_VECTOR_SIZE];
    for(int i=0;i<LATENT_VECTOR_SIZE;i++) {
    lv[i] = random_lv_value(eng);
    }
    node.latent_vector = lv;
}*/
struct create_nodes {
  Graphp g;
  SLock& l;
  create_nodes(Graphp _g, SLock& _l) : g(_g), l(_l) {}

  void operator()(GNode& item, galois::UserContext<GNode>& ctx) {
    //	cout<<"In create nodes..\n";
    Node node = graph.getData(item);
    // fillNode(node);
    if (node.ID == 1)
      std::cout << "node ID = " << node.ID << "Host =====>" << networkHostID
                << std::endl;
    DGNode n = g->createNode(node);
    g->addNode(n);
    if (node.ID == 1) {
      Node dNode = g->getData(n);

      std::cout << "node ID og dgraph = " << dNode.ID << "Host =====>"
                << networkHostID << std::endl;
    }
    /*Check that we have the right nodes*/
    /*	l.lock();
        mapping[item] = n;
        llookup[lookup[item]] = n;
        l.unlock();
    */
  }
};

unsigned int initializeGraphData(Graph& g) {
  unsigned int seed = 42;
  std::default_random_engine eng(seed);
  std::uniform_real_distribution<double> random_lv_value(0, 0.1);

  unsigned int num_movie_nodes = 0;
  unsigned int num_user_nodes  = 0;
  unsigned index               = 0;
  unsigned int numRatings      = 0;
  //  for all movie and user nodes in the graph
  for (Graph::iterator i = g.begin(), end = g.end(); i != end; ++i) {
    Graph::GraphNode gnode = *i;
    Node& data             = g.getData(gnode);

    data.ID = index;
    ++index;
    data.updates = 0;

    // fill latent vectors with random values
    double* lv = new double[LATENT_VECTOR_SIZE];
    for (int i = 0; i < LATENT_VECTOR_SIZE; i++) {
      lv[i] = random_lv_value(eng);
    }
    for (int i = 0; i < LATENT_VECTOR_SIZE; i++) {
      data.latent_vector[i] = lv[i];
    }
    // data.latent_vector = lv;
    unsigned int num_edges = g.edge_end(gnode) - g.edge_begin(gnode);
    // std::cout << "num edges = " << num_edges << "\n";
    numRatings += num_edges;
    if (num_edges > 0)
      ++num_movie_nodes;
    else
      ++num_user_nodes;

    //     data.edge_offset = 0;
  }

  NUM_RATINGS = numRatings;
  return num_movie_nodes;
}

void giveDGraph(Graphp graph);

static void recvRemoteNode_landing_pad(RecvBuffer& buf) {
  DGNode n;
  // unsigned num;
  Node node;
  uint32_t host;
  gDeserialize(buf, host, node, n);
  slock.lock();
  rlookup[node] = n;
  slock.unlock();
}

static void getRemoteNode_landing_pad(RecvBuffer& buf) {
  // unsigned num;
  Node node;
  uint32_t host;
  gDeserialize(buf, host, node);
  SendBuffer b;
  slock.lock();
  gSerialize(b, networkHostID, node, llookup[node]);
  slock.unlock();
  getSystemNetworkInterface().send(host, recvRemoteNode_landing_pad, b);
}

static void create_remote_graph_edges(Graphp dgraph) {
  printf("host: %u creating all edges on HOST =>\n", networkHostID);
  unsigned count  = 0;
  unsigned scount = 0;
  unsigned rcount = 0;

  unsigned cc = 0;

  /*auto start = graph.begin();
  std::advance(start,0);
  DGNode d_start = mapping[*start];
  Node d_node = dgraph->getData(d_start);

  auto d_begin = dgraph->begin();
  Node d_start_node = dgraph->getData(*d_begin);
  std::cout << "---------------d graph mapping at = >" <<d_node.ID <<std::endl;
  std::cout << "---------------d graph starting at = >" <<d_start_node.ID
  <<std::endl;
  */
  // rlookup.clear();
  // assert(!rlookup.size());
  auto dg_it = dgraph->begin();
  for (auto ii = graph.begin(); ii != graph.end(); ++ii) {
    mapping[*ii] = *dg_it;
    ++dg_it;
  }
  for (auto ii = graph.begin(); ii != graph.end(); ++ii) {
    Graph::edge_iterator vv = graph.edge_begin(*ii);
    Graph::edge_iterator ev = graph.edge_end(*ii);
    scount++;
    for (Graph::edge_iterator jj = vv; jj != ev; ++jj) {
      // Node& node = lookup[graph.getEdgeDst(jj)];
      unsigned int edge_data = graph.getEdgeData(jj);

      dgraph->addEdge(mapping[*ii], mapping[graph.getEdgeDst(jj)], edge_data);
      count++;
    }
  }
  std::cout << "host=" << networkHostID << "count = " << count << "\n";

  printf("host: %u nodes %u and edges %u remote edges %u\n", networkHostID,
         scount, count, rcount);
  printf("host: %u done creating local edges\n", networkHostID);
  // giveDGraph(dgraph);
}

static void create_dist_graph(Graphp dgraph, std::string inputFile) {
  SLock lk;
  prog_barrier = 0;
  uint64_t block, f, l;
  Graph::iterator first, last;
  std::cout << "Done making graph HOST = " << networkHostID << "\n";
  std::cout << "Number of movie nodes=" << num_movie_nodes << std::endl;
  unsigned size = 0;
  cout << "Number of nodes = " << std::distance(graph.begin(), graph.end())
       << endl;
  // std::cout << "host = "<< networkHostID << " f  = "<< f << " l = " << l
  // <<"\n";

  // create the nodes
  if (networkHostID == 0) {
    printf("host: %u creating nodes\n", networkHostID);

    galois::for_each(graph.begin(), graph.end(), create_nodes(dgraph, lk));
    // printf ("%lu nodes in %u host with block size %lu\n", mapping.size(),
    // networkHostID, block);
    // create the local edges
  } else {

    galois::for_each(graph.begin(), graph.end(), dummy_func());
  }
}
static void getDGraph_landing_pad(RecvBuffer& buf) {
  Graphp dgraph;
  gDeserialize(buf, dgraph);
  printf("%d has received DistGraph..\n", networkHostID);
}
void giveDGraph(Graphp dgraph) {
  if (networkHostNum > 1) {
    SendBuffer b;
    gSerialize(b, dgraph);
    getSystemNetworkInterface().broadcast(getDGraph_landing_pad, b);
    printf("Handling receives...\n");
    getSystemNetworkInterface().handleReceives();
    printf("Done Handling receives...\n");
  }
}
static void readInputGraph_landing_pad(RecvBuffer& buf) {
  Graphp dgraph;
  std::string inputFile;
  gDeserialize(buf, inputFile, dgraph);
  create_dist_graph(dgraph, inputFile);
  printf("1..Done creating dist graph..\n");
}

void readInputGraph(Graphp dgraph, std::string inputFile) {
  std::cout << "NetworkHostNum=" << networkHostNum << std::endl;
  if (networkHostNum > 1) {
    SendBuffer b;
    gSerialize(b, inputFile, dgraph);
    getSystemNetworkInterface().broadcast(readInputGraph_landing_pad, b);
    printf("Handling receives...\n");
    getSystemNetworkInterface().handleReceives();
    printf("Done Handling receives...\n");
  }

  create_dist_graph(dgraph, inputFile);
  printf("0..Done creating dist graph.. HOST --->%d\n", networkHostID);
}

void readGraph(Graphp dgraph, std::string inputFile) {

  readInputGraph(dgraph, inputFile);
}

void verify_(Graphp g) {
  // if(networkHostID == 0)
  auto ii = g->begin();
  std::advance(ii, num_movie_nodes);

  cout << "Done..\n";
  if (networkHostID == 0)
    galois::for_each(g->begin(), ii, verify_before(g), "Verifying");
  else
    galois::for_each(g->begin(), ii, dummy_func2());
}

int main(int argc, char** argv) {
  if (argc < 3) {
    std::cout << "Usage: <input binary gr file> <thread count>" << std::endl;
    return -1;
  }

  std::cout << "start reading and building Graph\n";
  // read command line parameters
  // const char* inputFile = argv[1];
  std::string inputFile    = argv[1];
  unsigned int threadCount = atoi(argv[2]);

  // how many threads Galois should use
  galois::setActiveThreads(threadCount);
  // FGraph fg;
  // fg.structureFromFile(inputFile);
  // std::cout<<"Reached here..\n";
  // graph.structureFromGraph(fg);
  // std::cout<<"Reached here too..\n";

  // prints out the number of conflicts at the end of the program
  std::cout << "structureFromFile\n";
  graph.structureFromFile(inputFile);

  num_movie_nodes = initializeGraphData(graph);
  std::cout << "num_movie_nodes = " << num_movie_nodes << "\n";
  verify();
  galois::StatManager statManager;
  galois::runtime::networkStart();

  Graphp dgraph = DGraph::allocate();

  galois::StatTimer Tinitial("Initialization Time");
  Tinitial.start();
  readGraph(dgraph, inputFile);
  Tinitial.stop();

  if (networkHostID == 0) {
    std::cout << "create_remote_graph_edges host--->" << networkHostID << "\n";
    create_remote_graph_edges(dgraph);
    std::cout << "Done reading and building Graph\n";
    // std::cout<< "Running Verification before \n";
    // verify(dgraph);
  }
  // if(networkHostID == 0)
  // verify_(dgraph);

  std::cout << "num_movie_nodes = " << num_movie_nodes << "\n";
  std::cout << "calling sgd \n";
  // program_barrier();
  galois::StatTimer T1("DistGraph transfer Time");
  T1.start();
  // giveDGraph(dgraph);;
  T1.stop();
  galois::StatTimer T("Sgd Time");
  T.start();
  // sgd_algo()(dgraph);
  T.stop();
  // double rms=0.0;
  galois::StatTimer T2("Sgd Time");
  T2.start();
  verify_(dgraph);
  T2.stop();
  cout << "Value of rms = " << RMS << endl;

  // allocate local computation graph
  /*	FGraph f;
      f.structureFromFile(inputFile);
      LCGraph localGraph;
      localGraph.allocateFrom(f);
      localGraph.constructFrom(f, 0, 1);
      //read structure of graph & edge weights; nodes not initialized
      //galois::graphs::readGraph(localGraph, inputFile);
      //g_ptr = &g;

      //typedef galois::graphs::FileGraph::GraphNode FileGNode;
      for(auto ii = localGraph.begin(); ii != localGraph.end(); ++ii) {
          Node& data = localGraph.getData(*ii);
          std::cout << data.updates <<"\n";

      }

      //fill each node's id & initialize the latent vectors
      unsigned int num_movie_nodes = initializeGraphData(localGraph);

      Graphp g = Graph::allocate();

      //galois::for_each<>(boost::counting_iterator<int>(0),
     boost::counting_iterator<int>(100), op(g));

      //galois::for_each<>(f.begin(), f.end(), AddNodes(g,f));

      std::cout << "Done making graph\n";

      galois::StatTimer timer;
      timer.start();

      //do the SGD computation in parallel
      //the initial worklist contains all the movie nodes
      //the movie nodes are located at the top num_movie_nodes nodes in the
     graph
      //the worklist is a priority queue ordered by the number of updates done
     to a movie
      //the projCount functor provides the priority function on each node
      //Graphp::iterator ii = g.begin();
      //std::advance(ii,num_movie_nodes); //advance moves passed in iterator
      galois::for_each(g.begin(), ii, sgd(g),
                           galois::wl<galois::worklists::OrderedByIntegerMetric
                           <projCount,
     galois::worklists::PerSocketChunkLIFO<32>>>());

      timer.stop();

      std::cout << "SUMMARY Movies " << num_movie_nodes <<
          " Users " << g->size() - num_movie_nodes<<
          " Threads " << threadCount <<
          " Time " << timer.get()/1000.0 << std::endl;

  */
  printf("Reached here, before terminate..\n");
  galois::runtime::networkTerminate();
  printf("Reached here, after terminate..\n");
  // verify(dgraph);
  return 0;
}

max_stars_repo_name: lineagech/Galois

max_issues_repo_name: lineagech/Galois

max_forks_repo_name: lineagech/Galois


text:
#pragma once

#include <boost/config.hpp>

#if BOOST_MSVC
#pragma warning(disable: 4456 4459)
#endif

ext: hpp

max_stars_repo_name: ompu/ompu

max_issues_repo_name: ompu/ompu

max_forks_repo_name: ompu/ompu


text:
//---------------------------------------------------------------------------//
// Copyright (c) 2018-2021 Mikhail Komarov <nemo@nil.foundation>
// Copyright (c) 2020-2021 Nikita Kaskov <nbering@nil.foundation>
//
// MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//---------------------------------------------------------------------------//

#define BOOST_TEST_MODULE element_fp2_test

#include <chrono>
#include <boost/test/unit_test.hpp>

#include <nil/crypto3/algebra/curves/mnt4.hpp>
#include <nil/crypto3/algebra/fields/mnt4/base_field.hpp>
#include <nil/crypto3/algebra/fields/mnt4/scalar_field.hpp>
#include <nil/crypto3/algebra/fields/arithmetic_params/mnt4.hpp>
#include <nil/crypto3/algebra/curves/params/multiexp/mnt4.hpp>
#include <nil/crypto3/algebra/curves/params/wnaf/mnt4.hpp>
#include <nil/crypto3/algebra/curves/mnt6.hpp>
#include <nil/crypto3/algebra/fields/mnt6/base_field.hpp>
#include <nil/crypto3/algebra/fields/mnt6/scalar_field.hpp>
#include <nil/crypto3/algebra/fields/arithmetic_params/mnt6.hpp>
#include <nil/crypto3/algebra/curves/params/multiexp/mnt6.hpp>
#include <nil/crypto3/algebra/curves/params/wnaf/mnt6.hpp>
#include <nil/crypto3/algebra/random_element.hpp>

#include <nil/crypto3/zk/components/blueprint.hpp>

#include <nil/crypto3/zk/components/algebra/fields/element_fp2.hpp>

#include "arithmetic.hpp"

#include "../../verify_r1cs_scheme.hpp"

using namespace nil::crypto3;
using namespace nil::crypto3::zk;
using namespace nil::crypto3::algebra;

BOOST_AUTO_TEST_SUITE(field_element_arithmetic_component_test_suite)

BOOST_AUTO_TEST_CASE(field_element_mul_component_test_mnt4_case) {
    using curve_type = typename curves::mnt4<298>;
    using field_type = typename curve_type::template g2_type<>::field_type;
    using base_field_type = typename curve_type::base_field_type;

    std::size_t tries_quantity = 10;
    std::cout << "Starting element Fp2 mul component test for MNT4-298 " << tries_quantity << " times ..." << std::endl;
    auto begin = std::chrono::high_resolution_clock::now();

    for (std::size_t i = 0; i < tries_quantity; i++){
        typename field_type::value_type a_value = random_element<field_type>();
        typename field_type::value_type b_value = random_element<field_type>();

        components::blueprint<base_field_type> bp = test_field_element_mul<field_type,
            components::element_fp2,
            components::element_fp2_mul>(a_value, b_value);

        BOOST_CHECK(bp.is_satisfied());

        BOOST_CHECK(verify_component<typename curve_type::chained_on_curve_type>(bp));
    }
    auto end = std::chrono::high_resolution_clock::now();
    auto elapsed = std::chrono::duration_cast<std::chrono::nanoseconds>(end - begin);
    std::cout << "Element Fp2 mul component test for MNT4-298 finished, average time: " << elapsed.count() * 1e-9 / tries_quantity << std::endl;
}

BOOST_AUTO_TEST_CASE(field_element_squared_component_test_mnt4_case) {
    using curve_type = typename curves::mnt4<298>;
    using field_type = typename curve_type::template g2_type<>::field_type;
    using base_field_type = typename curve_type::base_field_type;

    std::size_t tries_quantity = 10;
    std::cout << "Starting element Fp2 squared component test for MNT4-298 " << tries_quantity << " times ..." << std::endl;
    auto begin = std::chrono::high_resolution_clock::now();

    for (std::size_t i = 0; i < tries_quantity; i++){
        typename field_type::value_type a_value = random_element<field_type>();

        components::blueprint<base_field_type> bp = test_field_element_squared<field_type,
            components::element_fp2,
            components::element_fp2_squared>(a_value);

        BOOST_CHECK(bp.is_satisfied());

        BOOST_CHECK(verify_component<typename curve_type::chained_on_curve_type>(bp));
    }
    auto end = std::chrono::high_resolution_clock::now();
    auto elapsed = std::chrono::duration_cast<std::chrono::nanoseconds>(end - begin);
    std::cout << "Element Fp2 squared component test for MNT4-298 finished, average time: " << elapsed.count() * 1e-9 / tries_quantity << std::endl;
}

BOOST_AUTO_TEST_SUITE_END()

ext: cpp


text:
// Copyright (c) 2015 Ionel Gog <ionel.gog@cl.cam.ac.uk>

/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR
 * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT
 * LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR
 * A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT.
 *
 * See the Apache Version 2.0 License for specific language governing
 * permissions and limitations under the License.
 */

#include "translation/translator_viff.h"

#include <boost/lexical_cast.hpp>
#include <ctemplate/template.h>
#include <sys/time.h>

#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <string>

#include "base/common.h"
#include "ir/column.h"
#include "ir/condition_tree.h"

namespace musketeer {
namespace translator {

  using ctemplate::mutable_default_template_cache;

  map<string, Relation*> TranslatorViff::relations;

  TranslatorViff::TranslatorViff(const op_nodes& dag,
                                 const string& class_name):
    TranslatorInterface(dag, class_name) {
  }

  string TranslatorViff::GetBinaryPath(OperatorInterface* op) {
    return op->get_code_dir() + class_name + "_code/" + class_name + ".py";
  }

  string TranslatorViff::GetSourcePath(OperatorInterface* op) {
    return op->get_code_dir() + class_name + "_code/" + class_name + ".py";
  }

  // taken from translator_hadoop presumably along with a bug
  set<pair<Relation*, string>> TranslatorViff::GetInputRelsAndPaths(const op_nodes& dag) {
    queue<shared_ptr<OperatorNode>> to_visit;
    set<shared_ptr<OperatorNode>> visited;
    set<pair<Relation*, string>> input_rels_paths;
    set<string> known_rels;
    for (op_nodes::const_iterator it = dag.begin(); it != dag.end(); ++it) {
      to_visit.push(*it);
      visited.insert(*it);
    }
    while (!to_visit.empty()) {
      shared_ptr<OperatorNode> cur_node = to_visit.front();
      to_visit.pop();
      OperatorInterface* op = cur_node->get_operator();
      vector<Relation*> relations = op->get_relations();
      string output_relation = op->get_output_relation()->get_name();
      for (vector<Relation*>::iterator rel_it = relations.begin();
           rel_it != relations.end(); ++rel_it) {
        string rel_name = (*rel_it)->get_name();
        if (known_rels.insert(rel_name).second) {
          input_rels_paths.insert(make_pair(*rel_it, op->CreateInputPath(*rel_it)));
        }
      }
      known_rels.insert(output_relation);
      if (!cur_node->IsLeaf()) {
        op_nodes children = cur_node->get_loop_children();
        op_nodes non_loop_children = cur_node->get_children();
        children.insert(children.end(), non_loop_children.begin(),
                        non_loop_children.end());
        for (op_nodes::iterator it = children.begin(); it != children.end();
             ++it) {
          if (visited.insert(*it).second) {
            to_visit.push(*it);
          }
        }
      }
    }
    return input_rels_paths;
  }

  vector<Relation*>* TranslatorViff::DetermineInputsSpark(const op_nodes& dag,
                                                           set<string>* inputs,
                                                           set<string>* visited) {
    vector<Relation*> *input_rels_out = new vector<Relation*>;
    queue<shared_ptr<OperatorNode> > to_visit;
    for (op_nodes::const_iterator it = dag.begin(); it != dag.end(); ++it) {
      to_visit.push(*it);
      Relation* rel = (*it)->get_operator()->get_output_relation();
      visited->insert(rel->get_name());
      relations[rel->get_name()] = rel;
    }
    while (!to_visit.empty()) {
      shared_ptr<OperatorNode> node = to_visit.front();
      to_visit.pop();
      OperatorInterface* op = node->get_operator();
      LOG(INFO) << "VISITED: " << op->get_output_relation()->get_name();
      vector<Relation*> input_rels = op->get_relations();
      for (vector<Relation*>::iterator it = input_rels.begin();
           it != input_rels.end(); ++it) {
        // Rel is an input if rel not visited or doesn't have parents and
        // not already marked as an input.
        if ((node->get_parents().size() == 0 ||
             !IsGeneratedByOp((*it)->get_name(), node->get_parents())) &&
            visited->find((*it)->get_name()) == visited->end() &&
            inputs->find((*it)->get_name()) == inputs->end()) {
          LOG(INFO) << (*it)->get_name() << " is an input";
          inputs->insert((*it)->get_name());
          input_rels_out->push_back(*it);
          relations[(*it)->get_name()]= *it;
        } else {
          relations[(*it)->get_name()] = *it;
          LOG(INFO) << (*it)->get_name() << " is not an input";
        }
      }
      op_nodes non_loop_children = node->get_children();
      op_nodes children = node->get_loop_children();
      children.insert(children.end(), non_loop_children.begin(),
                      non_loop_children.end());
      for (op_nodes::iterator it = children.begin(); it != children.end();
           ++it) {
        bool can_add = true;
        vector<Relation*> input_rels = (*it)->get_operator()->get_relations();
        for (vector<Relation*>::iterator input_it = input_rels.begin();
             input_it != input_rels.end(); ++input_it) {
          if (visited->find((*input_it)->get_name()) == visited->end() &&
              inputs->find((*input_it)->get_name()) == inputs->end() &&
              IsGeneratedByOp((*input_it)->get_name(), node->get_parents())) {
            can_add = false;
            break;
          }
        }
        if (can_add || op->get_type() == WHILE_OP ||
            (*it)->get_operator()->get_type() == WHILE_OP) {
          Relation* rel = (*it)->get_operator()->get_output_relation();
          relations[rel->get_name()] = rel;
          if (visited->insert(rel->get_name()).second) {
            to_visit.push(*it);
          }
        }
      }
    }
    return input_rels_out;
  }

  // Check if all the inputs of the operator have been processed.
  bool TranslatorViff::CanSchedule(OperatorInterface* op,
                                   set<string>* processed) {
    string output = op->get_output_relation()->get_name();
    if (processed->find(output) != processed->end()) {
      LOG(INFO) << "Operator already scheduled";
      return false;
    }
    vector<Relation*> inputs = op->get_relations();
    for (vector<Relation*>::iterator it = inputs.begin(); it != inputs.end();
         ++it) {
      if (processed->find((*it)->get_name()) == processed->end()) {
        LOG(INFO) << "Cannot schedule yet: " << (*it)->get_name()
                  << " is missing";
        return false;
      }
    }
    LOG(INFO) << "Can Schedule ";
    return true;
  }

  void TranslatorViff::TranslateDAG(string* code, const op_nodes& next_set,
                                    set<shared_ptr<OperatorNode>>* leaves,
                                    set<string>* processed) {
    for (op_nodes::const_iterator it = next_set.begin(); it != next_set.end(); ++it) {
      shared_ptr<OperatorNode> node = *it;
      OperatorInterface* op = (*it)->get_operator();
      string output_rel = op->get_output_relation()->get_name();
      LOG(INFO) << "Translating for " << output_rel;
      if (CanSchedule(op, processed)) {
        ViffJobCode* job_code = dynamic_cast<ViffJobCode*>(TranslateOperator(op));
        *code += job_code->get_code();
        processed->insert(output_rel);
        if (node->IsLeaf()) {
          leaves->insert(node);
        }
        else {
          TranslateDAG(code, node->get_children(), leaves, processed);
        }
      }
      else {
        LOG(INFO) << "Cannot schedule operator yet: "
                  << op->get_output_relation()->get_name();
      }
    }
  }

  string TranslatorViff::TranslateImportAndUtils() {
    string import_and_utils;
    TemplateDictionary dict("import_and_utils");
    ExpandTemplate(FLAGS_viff_templates_dir + "ImportAndUtilsTemplate.py",
                   ctemplate::DO_NOT_STRIP, &dict, &import_and_utils);
    return import_and_utils;
  }

  string TranslatorViff::TranslateCloseProtocol() {
    string close_protocol;
    TemplateDictionary dict("close_protocol");
    ExpandTemplate(FLAGS_viff_templates_dir + "CloseProtocolTemplate.py",
                   ctemplate::DO_NOT_STRIP, &dict, &close_protocol);
    return close_protocol;
  }

  string TranslatorViff::TranslateMain() {
    string main;
    TemplateDictionary dict("main");
    dict.SetValue("VIFF_CONFIG_LOC", FLAGS_viff_config_loc);
    ExpandTemplate(FLAGS_viff_templates_dir + "MainTemplate.py",
                   ctemplate::DO_NOT_STRIP, &dict, &main);
    return main;
  }

  string TranslatorViff::TranslateInput(set<pair<Relation*, string>> input_rels_paths) {
    string input_rels_code = "";
    for (set<pair<Relation*, string>>::iterator it = input_rels_paths.begin();
         it != input_rels_paths.end(); ++it) {
      string in_code;
      LOG(INFO) << "My MPC ID: " << FLAGS_data_owner_id;
      LOG(INFO) << "Owner string of input relation " << it->first->get_name() << ": " << it->first->get_owner_string();
      string input_flag = it->first->has_owner(FLAGS_data_owner_id) ? "True" : "False";
      TemplateDictionary dict("input");
      dict.SetValue("REL", it->first->get_name());
      dict.SetValue("INPUT_PATH", it->second);
      dict.SetValue("INPUT_FLAG", input_flag);
      ExpandTemplate(FLAGS_viff_templates_dir + "InputTemplate.py",
                     ctemplate::DO_NOT_STRIP, &dict, &in_code);
      input_rels_code += in_code;
    }
    return input_rels_code;
  }

  string TranslatorViff::TranslateOutput(set<shared_ptr<OperatorNode>> leaves) {
    string store_code = "";
    for (set<shared_ptr<OperatorNode>>::iterator i = leaves.begin(); i != leaves.end(); ++i) {
      TemplateDictionary dict("store");
      dict.SetValue("REL", (*i)->get_operator()->get_output_relation()->get_name());
      dict.SetValue("OUTPUT_PATH", (*i)->get_operator()->get_output_path());
      string cur_code;
      ExpandTemplate(FLAGS_viff_templates_dir + "OutputTemplate.py",
                     ctemplate::DO_NOT_STRIP, &dict, &cur_code);
      store_code += cur_code;
    }
    return store_code;
  }

  string TranslatorViff::GenerateCode() {
    LOG(INFO) << "Viff generate code";
    shared_ptr<OperatorNode> op_node = dag[0];
    OperatorInterface* op = op_node->get_operator();
    std::vector<Relation*> v = op->get_relations();

    string import_and_utils = TranslateImportAndUtils();
    set<pair<Relation*, string>> input_rels_paths = GetInputRelsAndPaths(dag);
    string inputs = TranslateInput(input_rels_paths);

    string protocol_ops;
    set<shared_ptr<OperatorNode>> leaves = set<shared_ptr<OperatorNode>>();
    set<string> proc;

    for (set<pair<Relation*, string>>::iterator i = input_rels_paths.begin();
         i != input_rels_paths.end(); ++i) {
      proc.insert(i->first->get_name());
    }

    TranslateDAG(&protocol_ops, dag, &leaves, &proc);
    string output = TranslateOutput(leaves);
    string close_protocol = TranslateCloseProtocol();
    string main = TranslateMain();
    string code = import_and_utils + inputs + protocol_ops + output
                  + close_protocol + main;

    return WriteToFiles(op, code);
  }

  ViffJobCode* TranslatorViff::Translate(SelectOperatorMPC* op) {
    // TODO(nikolaj): Implement non-dummy version
    TemplateDictionary dict("select");
    Relation* input_rel = op->get_relations()[0];
    string input_name = input_rel->get_name();
    dict.SetValue("OUT_REL", op->get_output_relation()->get_name());
    dict.SetValue("IN_REL", input_name);
    string code;
    ExpandTemplate(FLAGS_viff_templates_dir + "SelectTemplate.py",
                   ctemplate::DO_NOT_STRIP, &dict, &code);
    ViffJobCode* job_code = new ViffJobCode(op, code);
    return job_code;
  }

  ViffJobCode* TranslatorViff::Translate(AggOperatorMPC* op) {
    TemplateDictionary dict("aggsec");
    Relation* input_rel = op->get_relations()[0];
    string input_name = input_rel->get_name();
    vector<Column*> group_by_cols = op->get_group_bys();
    Column* agg_col = op->get_columns()[0];
    string agg_op = GenerateAggMPCOp(op->get_operator());
    dict.SetValue("OUT_REL", op->get_output_relation()->get_name());
    dict.SetValue("IN_REL", input_name);
    dict.SetValue("GROUP_BY_COL", boost::lexical_cast<string>(group_by_cols[0]->get_index()));
    dict.SetValue("AGG_COL", boost::lexical_cast<string>(agg_col->get_index()));
    dict.SetValue("AGG_OP", agg_op);
    string code;
    ExpandTemplate(FLAGS_viff_templates_dir + "AggMPCTemplate.py",
                   ctemplate::DO_NOT_STRIP, &dict, &code);
    ViffJobCode* job_code = new ViffJobCode(op, code);
    return job_code;
  }

  ViffJobCode* TranslatorViff::Translate(JoinOperatorMPC* op) {
    TemplateDictionary dict("joinsec");
    vector<Relation*> relations = op->get_relations();
    dict.SetValue("OUT_REL", op->get_output_relation()->get_name());
    dict.SetValue("LEFT_REL", relations[0]->get_name());
    dict.SetValue("RIGHT_REL", relations[1]->get_name());
    dict.SetValue("LEFT_COL", boost::lexical_cast<string>(op->get_col_left()->get_index()));
    dict.SetValue("RIGHT_COL", boost::lexical_cast<string>(op->get_col_right()->get_index()));
    string code;
    ExpandTemplate(FLAGS_viff_templates_dir + "JoinMPCTemplate.py",
                   ctemplate::DO_NOT_STRIP, &dict, &code);
    ViffJobCode* job_code = new ViffJobCode(op, code);
    return job_code;
  }

  ViffJobCode* TranslatorViff::Translate(MulOperatorMPC* op) {
    return TranslateMathOp(op, op->get_values(), op->get_condition_tree(), "*");
  }

  ViffJobCode* TranslatorViff::Translate(DivOperatorMPC* op) {
    return TranslateMathOp(op, op->get_values(), op->get_condition_tree(), "/");
  }

  string TranslatorViff::GenerateColumnTypes(Relation* rel) {
    vector<Column*> cols = rel->get_columns();
    string types = "(";
    for (vector<Column*>::iterator i = cols.begin(); i != cols.end(); ++i) {
      types += (*i)->translateTypeConversionViff() + ", ";
    }
    return types + ")";
  }

  string TranslatorViff::GenerateAggMPCOp(const string& op) {
    if (op == "+") {
      return "sum";
    }
    else {
      LOG(ERROR) << "Unknown or unsupported agg operator: " << op;
      return "unknown";
    }
  }

  string TranslatorViff::GenerateColumns(vector<Column*> columns) {
    string col_string = "(";
    for (vector<Column*>::iterator i = columns.begin(); i != columns.end(); ++i) {
      col_string += (*i)->toString("viff") + ", ";
    }
    col_string += ")";
    LOG(INFO) << col_string;
    return col_string;
  }

  ViffJobCode* TranslatorViff::TranslateMathOp(OperatorInterface* op, vector<Value*> values,
                                               ConditionTree* condition_tree, string math_op) {
    Relation* input_rel = op->get_relations()[0];
    string input_name = input_rel->get_name();
    Relation* output_rel = op->get_output_relation();
    string lambda = GenerateLambda(math_op, input_rel,
                                   values[0], values[1], output_rel);
    LOG(INFO) << lambda;
    TemplateDictionary dict("math");
    dict.SetValue("OUT_REL",output_rel->get_name());
    dict.SetValue("IN_REL", input_name);
    dict.SetValue("LAMBDA", lambda);
    string code = "";
    ExpandTemplate(FLAGS_viff_templates_dir + "MathMPCTemplate.py",
                   ctemplate::DO_NOT_STRIP, &dict, &code);
    ViffJobCode* job_code = new ViffJobCode(op, code);
    return job_code;
  }

  string TranslatorViff::GenerateLambda(const string& op,
                                        Relation* rel, Value* left_val,
                                        Value* right_val, Relation* output_rel) {
    vector<Column*> columns = rel->get_columns();
    Column* left_column = dynamic_cast<Column*>(left_val);
    Column* right_column = dynamic_cast<Column*>(right_val);
    string left_value = "";
    string right_value = "";
    int32_t col_index_left = -1;
    int32_t col_index_right = -1;
    string maths = "lambda ";
    for (vector<Column*>::const_iterator it = columns.begin(); it != columns.end(); ++it) {
      int32_t col_index = (*it)->get_index();
      maths += "e" + boost::lexical_cast<string>(col_index + 1);
      if (it != columns.end() - 1) {
        maths += ", ";
      }
    }
    maths += ": [";

    string input_name = "e";

    // Assumption: no two constants
    if (left_column != NULL) {
      col_index_left = left_column->get_index();
    } else {
      left_value = left_val->get_value();
    }
    if (right_column != NULL) {
      col_index_right = right_column->get_index();
    } else {
      right_value = right_val->get_value();
    }
    uint32_t i = 0;
    for (vector<Column*>::const_iterator it = columns.begin(); it != columns.end(); ++it) {
      int32_t col_index = (*it)->get_index();
      if (col_index == col_index_left) {
        if (columns.size() > 1) {
          if (!op.compare("/")) {
            maths += "divide(" + input_name + boost::lexical_cast<string>(col_index + 1) + ", ";
          }
          else {
            maths += input_name + boost::lexical_cast<string>(col_index + 1) +
              " " + op + " ";
          }
        } else {
          maths += input_name + " " + op + " ";
        }
        if (col_index_right != -1) {
          if (columns.size() > 1) {
            if (!op.compare("/")) {
              maths += input_name + boost::lexical_cast<string>(col_index_right + 1) + ")";
            }
            else {
              maths += input_name + boost::lexical_cast<string>(col_index_right + 1);
            }
          } else {
            maths += input_name;
          }
        } else {
          maths += right_value;
        }
      } else if (col_index == col_index_right && (col_index_left == -1)) {
        if (columns.size() > 1) {
          maths += input_name + boost::lexical_cast<string>(col_index + 1) + " " + op +
              " " + left_value;
        } else {
          maths += input_name + " " + op + " " + left_value;
        }
      } else {
        if (columns.size() > 1) {
          maths += input_name + boost::lexical_cast<string>(col_index + 1);
        } else {
          maths += input_name;
        }
      }
      if (++i < columns.size()) {
        maths += ", ";
      }
    }
    maths += "]";
    return maths;
  }

  string TranslatorViff::WriteToFiles(OperatorInterface* op,
                                      const string& op_code) {
    ofstream job_file;
    string source_file = GetSourcePath(op);
    string path = op->get_code_dir() + class_name + "_code/";
    string create_dir = "mkdir -p " + path;
    std::system(create_dir.c_str());
    job_file.open(source_file.c_str());
    job_file << op_code;
    job_file.close();
    return source_file;
  }

} // namespace translator
} // namespace musketeer

ext: cc