/* Copyright (C) 2012-2020 IBM Corp.
 * This program is 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
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. See accompanying LICENSE file.
 */
#include <iostream>
#include <cassert>
#include <fstream>
#include <vector>
#include <cmath>
#include <algorithm>
#include <NTL/BasicThreadPool.h>
NTL_CLIENT

#include <helib/helib.h>

#include <helib/intraSlot.h>
#include <helib/binaryArith.h>
#include <helib/ArgMap.h>

#ifdef HELIB_DEBUG
#include <helib/debugging.h>
#endif

using namespace helib;

// define flags FLAG_PRINT_ZZX, FLAG_PRINT_POLY, FLAG_PRINT_VEC, functions
//        decryptAndPrint(ostream, ctxt, sk, ea, flags)
//        decryptAndCompare(ctxt, sk, ea, pa);

static std::vector<zzX> unpackSlotEncoding; // a global variable
static bool verbose=false;

static long mValues[][15] = {
// { p, phi(m),   m,   d, m1, m2, m3,    g1,   g2,   g3, ord1,ord2,ord3, B,c}
  {  2,    48,   105, 12,   3, 35,  0,    71,    76,    0,   2,  2,   0, 25, 2},
  {  2 ,  600,  1023, 10,  11, 93,  0,   838,   584,    0,  10,  6,   0, 25, 2},
  {  2,  2304,  4641, 24,   7,  3,221,  3979,  3095, 3760,   6,  2,  -8, 25, 3},
  {  2,  5460,  8193, 26,8193,  0,  0,    46,     0,    0, 210,  0,   0, 25, 3},
  {  2,  8190,  8191, 13,8191,  0,  0,    39,     0,    0, 630,  0,   0, 25, 3},
  {  2, 10752, 11441, 48,  17,673,  0,  4712,  2024,    0,  16,-14,   0, 25, 3},
  {  2, 15004, 15709, 22,  23,683,  0,  4099, 13663,    0,  22, 31,   0, 25, 3},
  {  2, 27000, 32767, 15,  31,  7,151, 11628, 28087,25824,  30,  6, -10, 28, 4}
};

void test15for4(SecKey& secKey);
void testProduct(SecKey& secKey, long bitSize1, long bitSize2,
                 long outSize, bool bootstrap = false);
void testAdd(SecKey& secKey, long bitSize1, long bitSize2,
             long outSize, bool bootstrap = false);

int main(int argc, char *argv[])
{
  ArgMap amap;
  long prm=1;
  amap.arg("prm", prm, "parameter size (0-tiny,...,7-huge)");
  long bitSize = 5;
  amap.arg("bitSize", bitSize, "bitSize of input integers (<=32)");
  long bitSize2 = 0;
  amap.arg("bitSize2", bitSize2, "bitSize of 2nd input integer (<=32)",
           "same as bitSize");
  long outSize = 0;
  amap.arg("outSize", outSize, "bitSize of output integers", "as many as needed");
  long nTests = 2;
  amap.arg("nTests", nTests, "number of tests to run");
  bool bootstrap = false;
  amap.arg("bootstrap", bootstrap, "test multiplication with bootstrapping");
  long seed=0;
  amap.arg("seed", seed, "PRG seed");
  long nthreads=1;
  amap.arg("nthreads", nthreads, "number of threads");
  amap.arg("verbose", verbose, "print more information");

  long tests2avoid = 1;
  amap.arg("tests2avoid", tests2avoid, "bitmap of tests to disable (1-15for4, 2-add, 4-multiply");

  amap.parse(argc, argv);
  assert(prm >= 0 && prm < 5);
  if (seed) NTL::SetSeed(ZZ(seed));
  if (nthreads>1) NTL::SetNumThreads(nthreads);

  if (bitSize<=0) bitSize=5;
  else if (bitSize>32) bitSize=32;
  if (bitSize2<=0) bitSize2=bitSize;
  else if (bitSize2>32) bitSize2=32;

  long* vals = mValues[prm];
  long p = vals[0];
  //  long phim = vals[1];
  long m = vals[2];

  NTL::Vec<long> mvec;
  append(mvec, vals[4]);
  if (vals[5]>1) append(mvec, vals[5]);
  if (vals[6]>1) append(mvec, vals[6]);

  std::vector<long> gens;
  gens.push_back(vals[7]);
  if (vals[8]>1) gens.push_back(vals[8]);
  if (vals[9]>1) gens.push_back(vals[9]);

  std::vector<long> ords;
  ords.push_back(vals[10]);
  if (abs(vals[11])>1) ords.push_back(vals[11]);
  if (abs(vals[12])>1) ords.push_back(vals[12]);

  long B = vals[13];
  long c = vals[14];

  // Compute the number of levels
  long L;
  if (bootstrap) L=900; // that should be enough
  else {
    double nBits =
      (outSize>0 && outSize<2*bitSize)? outSize : (2*bitSize);
    double three4twoLvls = log(nBits/2) / log(1.5);
    double add2NumsLvls = log(nBits) / log(2.0);
    L = (5 + ceil(three4twoLvls + add2NumsLvls))*30;
  }

  if (verbose) {
    cout <<"input bitSizes="<<bitSize<<','<<bitSize2
         <<", output size bound="<<outSize
         <<", running "<<nTests<<" tests for each function\n";
    if (nthreads>1) cout << "  using "<<NTL::AvailableThreads()<<" threads\n";
    cout << "computing key-independent tables..." << std::flush;
  }
  Context context(m, p, /*r=*/1, gens, ords);
  buildModChain(context, L, c,/*willBeBootstrappable=*/bootstrap);
  if (bootstrap) {
    context.makeBootstrappable(mvec, /*t=*/0);
  }
  buildUnpackSlotEncoding(unpackSlotEncoding, *context.ea);
  if (verbose) {
    cout << " done.\n";
    context.zMStar.printout();
    cout << " L="<<L<<", B="<<B<<endl;
    cout << "\ncomputing key-dependent tables..." << std::flush;
  }
  SecKey secKey(context);
  secKey.GenSecKey();
  addSome1DMatrices(secKey); // compute key-switching matrices
  addFrbMatrices(secKey);
  if (bootstrap) secKey.genRecryptData();
  if (verbose) cout << " done\n";

  activeContext = &context; // make things a little easier sometimes
#ifdef HELIB_DEBUG
  dbgEa = context.ea;
  dbgKey = &secKey;
#endif

  if (!(tests2avoid & 1)) {
    for (long i=0; i<nTests; i++)
      test15for4(secKey);
    cout << "GOOD\n";
  }
  if (!(tests2avoid & 2)) {
    for (long i=0; i<nTests; i++)
      testAdd(secKey, bitSize, bitSize2, outSize, bootstrap);
    cout << "GOOD\n";
  }
  if (!(tests2avoid & 4)) {
    for (long i=0; i<nTests; i++)
      testProduct(secKey, bitSize, bitSize2, outSize, bootstrap);
    cout << "GOOD\n";
  }
  if (verbose) printAllTimers(cout);
  return 0;
}

void test15for4(SecKey& secKey)
{
  std::vector<Ctxt> inBuf(15, Ctxt(secKey));
  std::vector<Ctxt*> inPtrs(15, nullptr);

  std::vector<Ctxt> outBuf(5, Ctxt(secKey));

  long sum=0;
  std::string inputBits = "(";
  for (int i=0; i<15; i++) {
    if (NTL::RandomBnd(10)>0) { // leave empty with small probability
      inPtrs[i] = &(inBuf[i]);
      long bit = NTL::RandomBnd(2);  // a random bit
      secKey.Encrypt(inBuf[i], ZZX(bit));
      inputBits += std::to_string(bit) + ",";
      sum += bit;
    }
    else inputBits += "-,";
  }
  inputBits += ")";

  // Add these bits
  if (verbose) {
    cout << endl;
    CheckCtxt(inBuf[lsize(inBuf)-1], "b4 15for4");
  }
  long numOutputs
    = fifteenOrLess4Four(CtPtrs_vectorCt(outBuf), CtPtrs_vectorPt(inPtrs));
  if (verbose)
    CheckCtxt(outBuf[lsize(outBuf)-1], "after 15for4");

  // Check the result
  long sum2=0;
  for (int i=0; i<numOutputs; i++) {
    ZZX poly;
    secKey.Decrypt(poly, outBuf[i]);
    sum2 += to_long(ConstTerm(poly)) << i;
  }
  if (sum != sum2) {
    cout << "BAD\n";
    if (verbose) {
      cout << "  15to4: inputs="<<inputBits<<", sum="<<sum
           << " but sum2="<<sum2<<endl;
    }
    exit(0);
  }
  else if (verbose)
    cout << "15to4 succeeded, sum"<<inputBits<<"="<<sum2<<endl;
}

void testProduct(SecKey& secKey, long bitSize, long bitSize2,
                 long outSize, bool bootstrap)
{
  const Context& context = secKey.getContext();
  const EncryptedArray& ea = *(context.ea);
  long mask = (outSize? ((1L<<outSize)-1) : -1);

  // Choose two random n-bit integers
  long pa = RandomBits_long(bitSize);
  long pb = RandomBits_long(bitSize2);

  // Encrypt the individual bits
  NTL::Vec<Ctxt> eProduct, enca, encb;

  resize(enca, bitSize, Ctxt(secKey));
  for (long i=0; i<bitSize; i++) {
    secKey.Encrypt(enca[i], ZZX((pa>>i)&1));
    if (bootstrap) { // put them at a lower level
      enca[i].bringToSet(context.getCtxtPrimes(5));
    }
  }
  resize(encb, bitSize2, Ctxt(secKey));
  for (long i=0; i<bitSize2; i++) {
    secKey.Encrypt(encb[i], ZZX((pb>>i)&1));
    if (bootstrap) { // put them at a lower level
      encb[i].bringToSet(context.getCtxtPrimes(5));
    }
  }
  if (verbose) {
    cout << "\n  bits-size "<<bitSize<<'+'<<bitSize2;
    if (outSize>0) cout << "->"<<outSize;
    CheckCtxt(encb[0], "b4 multiplication");
  }
  // Test positive multiplication
  vector<long> slots;
  {CtPtrs_VecCt eep(eProduct);  // A wrappers around the output vector
  multTwoNumbers(eep,CtPtrs_VecCt(enca),CtPtrs_VecCt(encb),/*negative=*/false,
                 outSize, &unpackSlotEncoding);
  decryptBinaryNums(slots, eep, secKey, ea);
  } // get rid of the wrapper
  if (verbose)
    CheckCtxt(eProduct[lsize(eProduct)-1], "after multiplication");
  long pProd = pa*pb;
  if (slots[0] != ((pa*pb)&mask)) {
    cout << "BAD\n";
    if (verbose)
      cout << "Positive product error: pa="<<pa<<", pb="<<pb
           << ", but product="<<slots[0]
           << " (should be "<<pProd<<'&'<<mask<<'='<<(pProd&mask)<<")\n";
    exit(0);
  }
  else if (verbose) {
    cout << "positive product succeeded: ";
    if (outSize) cout << "bottom "<<outSize<<" bits of ";
    cout << pa<<"*"<<pb<<"="<<slots[0]<<endl;
  }
  // Test negative multiplication
  secKey.Encrypt(encb[bitSize2-1], ZZX(1));
  decryptBinaryNums(slots, CtPtrs_VecCt(encb), secKey, ea, /*negative=*/true);
  pb = slots[0];
  eProduct.kill();
  {CtPtrs_VecCt eep(eProduct);  // A wrappers around the output vector
  multTwoNumbers(eep,CtPtrs_VecCt(enca),CtPtrs_VecCt(encb),/*negative=*/true,
                 outSize, &unpackSlotEncoding);
  decryptBinaryNums(slots, eep, secKey, ea, /*negative=*/true);
  } // get rid of the wrapper
  if (verbose)
    CheckCtxt(eProduct[lsize(eProduct)-1], "after multiplication");
  pProd = pa*pb;
  if ((slots[0]&mask) != (pProd&mask)) {
    cout << "BAD\n";
    if (verbose)
      cout << "Negative product error: pa="<<pa<<", pb="<<pb
           << ", but product="<<slots[0]
           << " (should be "<<pProd<<'&'<<mask<<'='<<(pProd&mask)<<")\n";
    exit(0);
  }
  else if (verbose) {
    cout << "negative product succeeded: ";
    if (outSize) cout << "bottom "<<outSize<<" bits of ";
    cout << pa<<"*"<<pb<<"="<<slots[0]<<endl;
  }

#ifdef HELIB_DEBUG
  const Ctxt* minCtxt = nullptr;
  long minLvl=1000;
  for (const Ctxt& c: eProduct) {
    long lvl = c.logOfPrimeSet();
    if (lvl < minLvl) {
      minCtxt = &c;
      minLvl = lvl;
    }
  }
  decryptAndPrint((cout<<" after multiplication: "), *minCtxt, secKey, ea,0);
  cout << endl;
#endif
}

void testAdd(SecKey& secKey, long bitSize1, long bitSize2,
             long outSize, bool bootstrap)
{
  const Context& context = secKey.getContext();
  const EncryptedArray& ea = *(context.ea);
  long mask = (outSize? ((1L<<outSize)-1) : -1);

  // Choose two random n-bit integers
  long pa = RandomBits_long(bitSize1);
  long pb = RandomBits_long(bitSize2);

  // Encrypt the individual bits
  NTL::Vec<Ctxt> eSum, enca, encb;

  resize(enca, bitSize1, Ctxt(secKey));
  for (long i=0; i<bitSize1; i++) {
    secKey.Encrypt(enca[i], ZZX((pa>>i)&1));
    if (bootstrap) { // put them at a lower level
      enca[i].bringToSet(context.getCtxtPrimes(5));
    }
  }
  resize(encb, bitSize2, Ctxt(secKey));
  for (long i=0; i<bitSize2; i++) {
    secKey.Encrypt(encb[i], ZZX((pb>>i)&1));
    if (bootstrap) { // put them at a lower level
      encb[i].bringToSet(context.getCtxtPrimes(5));
    }
  }
  if (verbose) {
    cout << "\n  bits-size "<<bitSize1<<'+'<<bitSize2;
    if (outSize>0) cout << "->"<<outSize;
    cout <<endl;
    CheckCtxt(encb[0], "b4 addition");
  }

  // Test addition
  vector<long> slots;
  {CtPtrs_VecCt eep(eSum);  // A wrapper around the output vector
  addTwoNumbers(eep, CtPtrs_VecCt(enca), CtPtrs_VecCt(encb),
                outSize, &unpackSlotEncoding);
  decryptBinaryNums(slots, eep, secKey, ea);
  } // get rid of the wrapper
  if (verbose) CheckCtxt(eSum[lsize(eSum)-1], "after addition");
  long pSum = pa+pb;
  if (slots[0] != ((pa+pb)&mask)) {
    cout << "BAD\n";
    if (verbose)
      cout << "addTwoNums error: pa="<<pa<<", pb="<<pb
           << ", but pSum="<<slots[0]
           << " (should be ="<<(pSum&mask)<<")\n";
    exit(0);
  }
  else if (verbose) {
    cout << "addTwoNums succeeded: ";
    if (outSize) cout << "bottom "<<outSize<<" bits of ";
    cout << pa<<"+"<<pb<<"="<<slots[0]<<endl;
  }

#ifdef HELIB_DEBUG
  const Ctxt* minCtxt = nullptr;
  long minLvl=1000;
  for (const Ctxt& c: eSum) {
    long lvl = c.logOfPrimeSet();
    if (lvl < minLvl) {
      minCtxt = &c;
      minLvl = lvl;
    }
  }
  decryptAndPrint((cout<<" after addition: "), *minCtxt, secKey, ea,0);
  cout << endl;
#endif
}


#include <blitz/array.h>

using namespace blitz;

int main()
{
    Array<int,2> A(8,8);
    A = 0;

    Array<int,2> B = A(Range(1,7,3), Range(1,5,2));
    B = 1;

    cout << "A = " << A << endl;
    return 0;
}


#include <fstream>
#include <boost/config.hpp>
#include <boost/version.hpp>

// CGAL headers
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Regular_triangulation_2.h>
#include <CGAL/IO/WKT.h>

#include <CGAL/point_generators_2.h>
// Qt headers
#include <QtGui>
#include <QString>
#include <QActionGroup>
#include <QFileDialog>
#include <QInputDialog>

// GraphicsView items and event filters (input classes)

#include "RegularTriangulationRemoveVertex.h"
#include <CGAL/Qt/GraphicsViewCircleInput.h>
#include <CGAL/Qt/RegularTriangulationGraphicsItem.h>
#include <CGAL/Qt/PowerdiagramGraphicsItem.h>

// for viewportsBbox
#include <CGAL/Qt/utility.h>
// the two base classes
#include "ui_Regular_triangulation_2.h"
#include <CGAL/Qt/DemosMainWindow.h>

typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef K::Point_2                                          Point_2;
typedef K::Weighted_point_2                                 Weighted_point_2;
typedef K::Point_2                                          Circle_2;
typedef K::Iso_rectangle_2                                  Iso_rectangle_2;

typedef CGAL::Regular_triangulation_2<K>                    Regular;

class MainWindow :
  public CGAL::Qt::DemosMainWindow,
  public Ui::Regular_triangulation_2
{
  Q_OBJECT

private:
  Regular dt;
  QGraphicsScene scene;

  CGAL::Qt::RegularTriangulationGraphicsItem<Regular> * dgi;
  CGAL::Qt::PowerdiagramGraphicsItem<Regular> * vgi;

  CGAL::Qt::RegularTriangulationRemoveVertex<Regular> * trv;
  CGAL::Qt::GraphicsViewCircleInput<K> * pi;
public:
  MainWindow();

public Q_SLOTS:

  void processInput(CGAL::Object o);

  void on_actionShowRegular_toggled(bool checked);

  void on_actionShowPowerdiagram_toggled(bool checked);

  void on_actionInsertPoint_toggled(bool checked);

  void on_actionInsertRandomPoints_triggered();

  void on_actionLoadPoints_triggered();

  void on_actionSavePoints_triggered();

  void on_actionClear_triggered();

  void on_actionRecenter_triggered();

Q_SIGNALS:
  void changed();
};

MainWindow::MainWindow()
  : DemosMainWindow()
{
  setupUi(this);

  // Add a GraphicItem for the regular triangulation
  dgi = new CGAL::Qt::RegularTriangulationGraphicsItem<Regular>(&dt);

  QObject::connect(this, SIGNAL(changed()),
                   dgi, SLOT(modelChanged()));

  dgi->setVerticesPen(QPen(Qt::red, 0, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
  dgi->setEdgesPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
  scene.addItem(dgi);

  // Add a GraphicItem for the Powerdiagram diagram
  vgi = new CGAL::Qt::PowerdiagramGraphicsItem<Regular>(&dt);

  QObject::connect(this, SIGNAL(changed()),
                   vgi, SLOT(modelChanged()));

  vgi->setEdgesPen(QPen(Qt::blue, 0, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
  scene.addItem(vgi);
  vgi->hide();

  // Setup input handlers. They get events before the scene gets them
  // and the input they generate is passed to the triangulation with
  // the signal/slot mechanism
  pi = new CGAL::Qt::GraphicsViewCircleInput<K>(this, &scene, 1); // emits center/radius

  QObject::connect(pi, SIGNAL(generate(CGAL::Object)),
                   this, SLOT(processInput(CGAL::Object)));

  trv = new CGAL::Qt::RegularTriangulationRemoveVertex<Regular>(&dt, this);
  QObject::connect(trv, SIGNAL(modelChanged()),
                   this, SIGNAL(changed()));

  //
  // Manual handling of actions
  //
  QObject::connect(this->actionQuit, SIGNAL(triggered()),
                   this, SLOT(close()));

  // We put mutually exclusive actions in an QActionGroup
  QActionGroup* ag = new QActionGroup(this);
  ag->addAction(this->actionInsertPoint);

  // Check two actions
  this->actionInsertPoint->setChecked(true);
  this->actionShowRegular->setChecked(true);

  //
  // Setup the scene and the view
  //
  scene.setItemIndexMethod(QGraphicsScene::NoIndex);
  scene.setSceneRect(-100, -100, 100, 100);
  this->graphicsView->setScene(&scene);
  this->graphicsView->setMouseTracking(true);

  // Turn the vertical axis upside down
  this->graphicsView->transform().scale(1, -1);

  // The navigation adds zooming and translation functionality to the
  // QGraphicsView
  this->addNavigation(this->graphicsView);

  this->setupStatusBar();
  this->setupOptionsMenu();
  this->addAboutDemo(":/cgal/help/about_Regular_triangulation_2.html");
  this->addAboutCGAL();
}

void
MainWindow::processInput(CGAL::Object o)
{
  std::pair<Point_2, K::FT > center_sqr;
  if(CGAL::assign(center_sqr, o)){
    Regular::Point wp(center_sqr.first, center_sqr.second);
    dt.insert(wp);
  }

  Q_EMIT( changed());
}

/*
 *  Qt Automatic Connections
 *  https://doc.qt.io/qt-5/designer-using-a-ui-file.html#automatic-connections
 *
 *  setupUi(this) generates connections to the slots named
 *  "on_<action_name>_<signal_name>"
 */
void
MainWindow::on_actionInsertPoint_toggled(bool checked)
{
  if(checked){
    scene.installEventFilter(pi);
    scene.installEventFilter(trv);
  } else {
    scene.removeEventFilter(pi);
    scene.removeEventFilter(trv);
  }
}

void
MainWindow::on_actionShowRegular_toggled(bool checked)
{
  dgi->setVisibleEdges(checked);
}

void
MainWindow::on_actionShowPowerdiagram_toggled(bool checked)
{
  vgi->setVisible(checked);
}

void
MainWindow::on_actionClear_triggered()
{
  dt.clear();
  Q_EMIT( changed());
}

void
MainWindow::on_actionInsertRandomPoints_triggered()
{
  QRectF rect = CGAL::Qt::viewportsBbox(&scene);
  CGAL::Qt::Converter<K> convert;
  Iso_rectangle_2 isor = convert(rect);
  CGAL::Random_points_in_iso_rectangle_2<Point_2> pg((isor.min)(), (isor.max)());
  CGAL::Random rnd(CGAL::get_default_random());

  const int number_of_points =
    QInputDialog::getInt(this,
                             tr("Number of random points"),
                             tr("Enter number of random points"), 100, 0);

  // wait cursor
  QApplication::setOverrideCursor(Qt::WaitCursor);
  std::vector<Weighted_point_2> points;
  points.reserve(number_of_points);
  for(int i = 0; i < number_of_points; ++i){
    Weighted_point_2 wp(*pg++, rnd.get_double(0, 500));
    points.push_back(wp);
  }
  dt.insert(points.begin(), points.end());
  // default cursor
  QApplication::setOverrideCursor(Qt::ArrowCursor);
  Q_EMIT( changed());
}

void
MainWindow::on_actionLoadPoints_triggered()
{
  QString fileName = QFileDialog::getOpenFileName(this,
                                                  tr("Open Points file"),
                                                  ".",
                                                  tr("Weighted Points (*.wpts.cgal);;"
                                                     "WKT files (*.wkt *.WKT);;"
                                                     "All (*)"));

  if(! fileName.isEmpty()){
    std::ifstream ifs(qPrintable(fileName));
    std::vector<Weighted_point_2> points;
    if(fileName.endsWith(".wkt",Qt::CaseInsensitive))
    {
      std::vector<K::Point_3> points_3;
      CGAL::IO::read_multi_point_WKT(ifs, points_3);
      for(const K::Point_3& p : points_3)
      {
        points.push_back(Weighted_point_2(K::Point_2(p.x(), p.y()), p.z()));
      }
    }
    else
    {
      Weighted_point_2 p;
      while(ifs >> p) {
        points.push_back(p);
      }
    }
    dt.insert(points.begin(), points.end());

    actionRecenter->trigger();
    Q_EMIT( changed());
  }
}

void
MainWindow::on_actionSavePoints_triggered()
{
  QString fileName = QFileDialog::getSaveFileName(this,
                                                  tr("Save points"),
                                                  ".reg.cgal",
                                                  tr("Weighted Points (*.wpts.cgal);;"
                                                     "WKT files (*.wkt *.WKT);;"
                                                     "All (*)"));
  if(! fileName.isEmpty()){
    std::ofstream ofs(qPrintable(fileName));
    if(fileName.endsWith(".wkt",Qt::CaseInsensitive))
    {
      std::vector<K::Point_3> points_3;
      for(Regular::Finite_vertices_iterator
          vit = dt.finite_vertices_begin(),
          end = dt.finite_vertices_end();
          vit!= end; ++vit)
      {
        points_3.push_back(K::Point_3(vit->point().x(),
                                      vit->point().y(),
                                      vit->point().weight()));
      }
      CGAL::IO::write_multi_point_WKT(ofs, points_3);
    }
    else
    {
      for(Regular::Finite_vertices_iterator
          vit = dt.finite_vertices_begin(),
          end = dt.finite_vertices_end();
          vit!= end; ++vit)
      {
        ofs << vit->point() << std::endl;
      }
    }
  }
}

void
MainWindow::on_actionRecenter_triggered()
{
  this->graphicsView->setSceneRect(dgi->boundingRect());
  this->graphicsView->fitInView(dgi->boundingRect(), Qt::KeepAspectRatio);
}

#include "Regular_triangulation_2.moc"
#include <CGAL/Qt/resources.h>

int main(int argc, char **argv)
{
  QApplication app(argc, argv);

  app.setOrganizationDomain("geometryfactory.com");
  app.setOrganizationName("GeometryFactory");
  app.setApplicationName("Regular_triangulation_2 demo");

  // Import resources from libCGAL (Qt5).
  CGAL_QT_INIT_RESOURCES;

  MainWindow mainWindow;
  mainWindow.show();

  QStringList args = app.arguments();
  args.removeAt(0);
  Q_FOREACH(QString filename, args) {
    mainWindow.open(filename);
  }

  return app.exec();
}


#include <blitz/array.h>

BZ_USING_NAMESPACE(blitz)

int main()
{
  Array<complex<float>,2> test(4,4) ;
 Array<float,2> test2(2,2) ;

 test2 = real(test(Range(0,1),Range(0,1))) ;
 Array<float,2> test3;
 test3.reference(test2(Range(0,1),Range(0,1)));

 return 0;
}


#ifndef SKYLARK_INNER_HPP
#define SKYLARK_INNER_HPP

#include <boost/mpi.hpp>

namespace skylark { namespace base {

template<typename T>
inline elem::Base<T> Nrm2(const elem::Matrix<T>& x) {
    return elem::Nrm2(x);
}

template<typename T>
inline elem::Base<T> Nrm2(const elem::DistMatrix<T>& x) {
    return elem::Nrm2(x);
}

template<typename T>
inline elem::Base<T> Nrm2(const elem::DistMatrix<T, elem::VC, elem::STAR>& x) {
    boost::mpi::communicator comm(x.DistComm(), boost::mpi::comm_attach);
    T local = elem::Nrm2(x.LockedMatrix());
    T snrm = boost::mpi::all_reduce(comm, local * local, std::plus<T>());
    return sqrt(snrm);
}

template<typename T>
inline elem::Base<T> Nrm2(const elem::DistMatrix<T, elem::VR, elem::STAR>& x) {
    boost::mpi::communicator comm(x.DistComm(), boost::mpi::comm_attach);
    T local = elem::Nrm2(x.LockedMatrix());
    T snrm = boost::mpi::all_reduce(comm, local * local, std::plus<T>());
    return sqrt(snrm);
}

template<typename T>
inline elem::Base<T> Nrm2(const elem::DistMatrix<T, elem::STAR, elem::STAR>& x) {
    return elem::Nrm2(x.LockedMatrix());
}

template<typename T>
inline void ColumnNrm2(const elem::Matrix<T>& A,
    elem::Matrix<elem::Base<T> >& N) {

    double *n = N.Buffer();
    const double *a = A.LockedBuffer();
    for(int j = 0; j < A.Width(); j++) {
        n[j] = 0.0;
        for(int i = 0; i < A.Height(); i++)
            n[j] += a[j * A.LDim() + i] * elem::Conj(a[j * A.LDim() + i]);
        n[j] = sqrt(n[j]);
    }
}

template<typename T>
inline void ColumnNrm2(const elem::DistMatrix<T, elem::STAR, elem::STAR>& A,
    elem::DistMatrix<elem::Base<T>, elem::STAR, elem::STAR>& N) {

    double *n = N.Buffer();
    const double *a = A.LockedBuffer();
    for(int j = 0; j < A.Width(); j++) {
        n[j] = 0.0;
        for(int i = 0; i < A.LocalHeight(); i++)
            n[j] += a[j * A.LDim() + i] * elem::Conj(a[j * A.LDim() + i]);
        n[j] = sqrt(n[j]);
    }
}

template<typename T>
inline void ColumnNrm2(const elem::DistMatrix<T, elem::VC, elem::STAR>& A,
    elem::DistMatrix<elem::Base<T>, elem::STAR, elem::STAR>& N) {

    std::vector<T> n(A.Width(), 1);
    const elem::Matrix<T> &Al = A.LockedMatrix();
    const double *a = Al.LockedBuffer();
    for(int j = 0; j < Al.Width(); j++) {
        n[j] = 0.0;
        for(int i = 0; i < Al.Height(); i++)
            n[j] += a[j * Al.LDim() + i] * elem::Conj(a[j * Al.LDim() + i]);
    }
    N.Resize(A.Width(), 1);
    elem::Zero(N);
    boost::mpi::communicator comm(N.Grid().Comm(), boost::mpi::comm_attach);
    boost::mpi::all_reduce(comm, n.data(), A.Width(), N.Buffer(), std::plus<T>());
    for(int j = 0; j < A.Width(); j++)
        N.Set(j, 0, sqrt(N.Get(j, 0)));
}

template<typename T>
inline void ColumnDot(const elem::Matrix<T>& A, const elem::Matrix<T>& B,
    elem::Matrix<elem::Base<T> >& N) {

    // TODO just assuming sizes are OK for now.

    double *n = N.Buffer();
    const double *a = A.LockedBuffer();
    const double *b = B.LockedBuffer();
    for(int j = 0; j < A.Width(); j++) {
        n[j] = 0.0;
        for(int i = 0; i < A.Height(); i++)
            n[j] += a[j * A.LDim() + i] * elem::Conj(b[j * B.LDim() + i]);
    }
}

template<typename T>
inline void ColumnDot(const elem::DistMatrix<T, elem::STAR, elem::STAR>& A,
    const elem::DistMatrix<T, elem::STAR, elem::STAR>& B,
    elem::DistMatrix<elem::Base<T>, elem::STAR, elem::STAR>& N) {

    // TODO just assuming sizes are OK for now.

    double *n = N.Buffer();
    const double *a = A.LockedBuffer();
    const double *b = B.LockedBuffer();
    for(int j = 0; j < A.Width(); j++) {
        n[j] = 0.0;
        for(int i = 0; i < A.LocalHeight(); i++)
            n[j] += a[j * A.LDim() + i] * elem::Conj(b[j * B.LDim() + i]);
    }
}

template<typename T>
inline void ColumnDot(const elem::DistMatrix<T, elem::VC, elem::STAR>& A,
    const elem::DistMatrix<T, elem::VC, elem::STAR>& B,
    elem::DistMatrix<elem::Base<T>, elem::STAR, elem::STAR>& N) {

    // TODO just assuming sizes are OK for now.

    std::vector<T> n(A.Width(), 1);
    const elem::Matrix<T> &Al = A.LockedMatrix();
    const double *a = Al.LockedBuffer();
    const elem::Matrix<T> &Bl = B.LockedMatrix();
    const double *b = Bl.LockedBuffer();
   for(int j = 0; j < Al.Width(); j++) {
        n[j] = 0.0;
        for(int i = 0; i < Al.Height(); i++)
            n[j] += a[j * Al.LDim() + i] * elem::Conj(b[j * Bl.LDim() + i]);
    }
    N.Resize(A.Width(), 1);
    elem::Zero(N);
    boost::mpi::communicator comm(N.Grid().Comm(), boost::mpi::comm_attach);
    boost::mpi::all_reduce(comm, n.data(), A.Width(), N.Buffer(), std::plus<T>());
}

template<typename T>
inline void RowDot(const elem::Matrix<T>& A, const elem::Matrix<T>& B,
    elem::Matrix<elem::Base<T> >& N) {

    // TODO just assuming sizes are OK for now.

    double *n = N.Buffer();
    const double *a = A.LockedBuffer();
    const double *b = B.LockedBuffer();
    for(int i = 0; i < A.Height(); i++)
        n[i] = 0.0;

    for(int j = 0; j < A.Width(); j++) {
        for(int i = 0; i < A.Height(); i++)
            n[i] += a[j * A.LDim() + i] * elem::Conj(b[j * B.LDim() + i]);
    }
}

} } // namespace skylark::base

#endif // SKYLARK_INNER_HPP


/*
 * monte.cpp - monte carlo simulation of acoustic phonon scattering
 */

/*
 * HISTORY
 * 16-Oct-88 Jeffrey Trull (jt1j) at Carnegie-Mellon University
 *       Created
 * 23-May-18 Jeffrey Trull (edaskel@att.net)
 *       Converted to Modern C++, fixed bugs
 */

// Note to interested readers:
// This program is an updated version of a programming assignment from a semiconductor
// physics class in 1988... The textbook was Michael Shur's "GaAs Devices and Circuits".
// The assignment was to estimate the electron mobility in the material by simulating
// one particular scattering mechanism: acoustic phonons. This was done not because they
// are the dominant mechanism, but because it makes for a tractable problem :)
// The general approach followed is given in Chapter 2 of Shur's textbook; another helpful
// source I (in retrospect) found is:
// Jacoboni and Reggiani: Monte Carlo Method in Transport (Rev Mod Phys, July 1983)
// which gives a more detailed explanation/motivation of the process

#include <vector>
#include <iostream>
#include <random>

#include <cmath>

#include <boost/units/systems/si.hpp>
#include <boost/units/cmath.hpp>
#include <boost/units/io.hpp>
#include <boost/accumulators/accumulators.hpp>
#include <boost/accumulators/statistics/stats.hpp>
#include <boost/accumulators/statistics/weighted_mean.hpp>

#include "monte.hpp"

using namespace boost::units;
using namespace boost::units::si;
using namespace monte;

using vector = vector_str<float>;

vector                kinit;         /* k vector prior to collision */
float                 capgamma_f;    /* total scattering rate */
int                   numtrials;     /* number of scattering events to perform */
quantity<frequency>   maxlambda;     /* highest lambda ever calculated */
int                   num_real_events;

int main(int argc, char **argv)
    /* initialize, then perform a number of scattering events specified by the
     * user.  Print relevant statistics after the run
     */
{
    (void)argc;          /* unused */
    (void)argv;

    float    phi, theta;
    float    seed;

    int      cur_trial;

    num_real_events = 0;     /* initialize some statistics */
    maxlambda = quantity<frequency>{0};

    /* get number of trials from user */
    numtrials = 0;
    int scanf_err = 1;
    while ((numtrials <= 0) || (scanf_err != 1)) {
        printf("Number of scattering events to perform: ");
        scanf_err = scanf("%d", &numtrials);
    }
    std::vector<quantity<si::time, float>> scat_times(numtrials);
    std::vector<quantity<si::velocity, float>> vel_mags(numtrials);

    /* get the total scattering rate from the user */
    capgamma_f = 0.0;
    scanf_err = 1;
    while ((capgamma_f <= 0.0) || (scanf_err != 1)) {
        printf("Total scattering rate: ");
        scanf_err = scanf("%f", &capgamma_f);
    }
    quantity<frequency> capgamma = capgamma_f * hertz;

    /* get a seed for the random number generator */
    seed = 0.0;
    scanf_err = 1;
    while ((seed <= 0.0) || (scanf_err != 1)) {
        printf("Random number generator seed : ");
        scanf_err = scanf("%f", &seed);
    }
    std::mt19937 randeng(seed);
    std::uniform_real_distribution<double> drand_dist(0, 1.0);
    auto drand = [&]() { return drand_dist(randeng); };

    /* initialize and begin scattering */
    cur_trial = 0;

    quantity<wavenumber>    lastkx;

    // Use Boost.Accumulators to produce an average velocity
    using namespace boost::accumulators;
    accumulator_set<quantity<velocity>,      // what we are storing
                    stats<tag::mean>,        // what we want to calculate
                    quantity<si::time>>      // the weight for each data point
        vel_acc;

    while (cur_trial < numtrials) {
        /* determine the time until the scattering event */
        quantity<si::time> ts = -(1.0/capgamma)*log(drand());
        lastkx = kinit.x;

        /* accelerate the particle accordingly */
        kinit.x = kinit.x - accel_const * ts;

        /* record the current value of ts and the velocity */
        scat_times[cur_trial] = ts;
        vel_mags[cur_trial] = vel_const * kinit.x;
        cur_trial++;

        /* do average velocity calculations */
        quantity<velocity> cur_avg = vel_const * (lastkx + kinit.x)/2.0;
        vel_acc(cur_avg, weight = ts);

        /* determine the new acoustic scattering rate lambda */
        quantity<energy, float> energy = kinit.get_energy();
        quantity<frequency> lambda = scatter_const * sqrt(energy);
        if (lambda > maxlambda) {
            maxlambda = lambda;              /* possibly useful statistic */
        }

        /* determine if acoustic scattering event occurred */
        if (drand() >= (lambda/capgamma)) {
            /* no, keep going */
            // this is a "self-scattering" event, a fake event that makes inverting the simulation
            // (choosing a flight time at random instead of using fixed intervals and testing
            // each one) work out right
            continue;
        }
        num_real_events++;

        /* determine the angles of the new vector */

        // If I understand this correctly we are calculating the angle theta between
        // the original k (taken facing along the Z axis) and k'
        // theta is the polar angle (declination from Z ("up"))
        // Taking dP(theta)d(theta) ~ sin(theta)d(theta), we need integration constants
        // that give the right total from 0 to pi and the right values at the extremes
        // (0 at 0, 1 at pi). P(theta) = 0.5*(1-cos(theta)) fits.
        theta = acos(1 - 2.0*drand());
        // phi, the "azimuthal angle" (from x in the direction of y) is uniformly distributed
        phi = two_pi * drand();
        // This is consistent with the approach in "Bulk Monte Carlo Method Described",
        // https://nanohub.org/resources/4844/download/montecarlocodedescribed.pdf
        // Should I have used the more complex procedure described in Shur p23?

        /* determine the resultant vector and replace */
        kinit = kinit.collision_result(theta, phi);
    }

    /* now print out results - good idea to pipe this through more */
    std::cout << num_real_events << " real events out of " << numtrials << ", maximum lambda " << maxlambda << "\n" ;
    std::cout << "average x velocity " << no_prefix << mean(vel_acc) << "\n";
    std::cout << "estimated mobility: " << ((mean(vel_acc) / Efield) / ((cm*cm)/(volt*second))).value() << " cm^2/Vs\n";
    std::cout << "event\t\tscattering time\t\tvelocity\n";
    for (cur_trial = 0; cur_trial < numtrials; cur_trial++) {
        std::cout << cur_trial << "\t\t" << engineering_prefix << scat_times[cur_trial] << "\t\t" << no_prefix << vel_mags[cur_trial] << "\n";
    }
}


// Copyright 2014 Vinzenz Feenstra
//
// 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
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef GUARD_LSL_RUNTIME_VECTOR_HH_INCLUDED
#define GUARD_LSL_RUNTIME_VECTOR_HH_INCLUDED

#include <boost/numeric/ublas/matrix.hpp>

namespace lsl {
namespace runtime {

    struct Vector {
        double x, y, z;
    };

    inline Vector add(Vector l, Vector r) {
        return Vector{
            l.x + r.x,
            l.y + r.y,
            l.z + r.z
        };
    }

    inline Vector add(Vector l, double r) {
        return Vector{
            l.x + r,
            l.y + r,
            l.z + r
        };
    }

    inline Vector sub(Vector l, Vector r) {
        return Vector{
            l.x - r.x,
            l.y - r.y,
            l.z - r.z
        };
    }

    inline Vector sub(Vector l, double r) {
        return Vector{
            l.x - r,
            l.y - r,
            l.z - r
        };
    }

    inline Vector mul(Vector l, Vector r) {
        return Vector{
            l.x * r.x,
            l.y * r.y,
            l.z * r.z
        };
    }

    inline Vector mul(Vector l, double r) {
        return Vector{
            l.x * r,
            l.y * r,
            l.z * r
        };
    }

    inline Vector div(Vector l, Vector r) {
        return Vector{
            l.x / r.x,
            l.y / r.y,
            l.z / r.z
        };
    }

    inline Vector div(Vector l, double r) {
        return Vector{
            l.x / r,
            l.y / r,
            l.z / r
        };
    }

    inline Vector cross(Vector l, Vector r) {
        return Vector{
            l.y * r.z - l.z * r.y,
            l.z * r.x - l.x * r.z,
            l.x * r.y - l.y * r.x
        };
    }

    inline double dot(Vector l, Vector r) {
        return (l.x * r.x) + (l.y * r.y) + (l.z * r.z);
    }

    inline double mag(Vector v) {
        return sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
    }

    inline Vector norm(Vector v) {
        double m = mag(v);
        if(m > 0.) {
            double inv_mag = 1.f / m;
            return mul(v, inv_mag);
        }
        return Vector{0., 0., 0.};
    }

    inline Vector operator * (Vector const & a, Vector const & b) {
        return mul(a, b);
    }
    inline Vector operator / (Vector const & a, Vector const & b) {
        return div(a, b);
    }
    inline Vector operator + (Vector const & a, Vector const & b) {
        return add(a, b);
    }
    inline Vector operator - (Vector const & a, Vector const & b) {
        return sub(a, b);
    }
    inline Vector operator * (Vector const & a, double b) {
        return mul(a, b);
    }
    inline Vector operator / (Vector const & a, double b) {
        return div(a, b);
    }
    inline Vector operator + (Vector const & a, double b) {
        return add(a, b);
    }
    inline Vector operator - (Vector const & a, double b) {
        return sub(a, b);
    }

    inline double dist(Vector l, Vector r) {
        return mag(l - r);
    }

    inline bool operator == (Vector const & a, Vector const & b) {
        return a.x == b.x && a.y == b.y && a.z == b.z;
    }

    inline bool operator < (Vector const & a, Vector const & b) {
        return mag(a) < mag(b);
    }

}}

#endif //GUARD_LSL_RUNTIME_VECTOR_HH_INCLUDED


//==================================================================================================
/*!

  Copyright 2015 NumScale SAS
  Copyright 2015 J.T. Lapreste

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
*/
//==================================================================================================
#include <boost/simd/function/scalar/asind.hpp>
#include <simd_test.hpp>
#include <boost/simd/constant/inf.hpp>
#include <boost/simd/constant/minf.hpp>
#include <boost/simd/constant/nan.hpp>
#include <boost/simd/constant/one.hpp>
#include <boost/simd/constant/mone.hpp>
#include <boost/simd/constant/zero.hpp>
#include <boost/simd/constant/mzero.hpp>

STF_CASE_TPL (" asind",  STF_IEEE_TYPES)
{
  namespace bs = boost::simd;
  namespace bd = boost::dispatch;
  using bs::asind;

  using r_t = decltype(asind(T()));

  // return type conformity test
  STF_TYPE_IS(r_t, T);

  // specific values tests
#ifndef BOOST_SIMD_NO_INVALIDS
  STF_ULP_EQUAL(asind(bs::Inf<T>()), bs::Nan<r_t>(), 0);
  STF_ULP_EQUAL(asind(bs::Minf<T>()), bs::Nan<r_t>(), 0);
  STF_ULP_EQUAL(asind(bs::Nan<T>()), bs::Nan<r_t>(), 0);
#endif
  STF_ULP_EQUAL(asind(bs::Half<T>()), T(30), 0.5);
  STF_ULP_EQUAL(asind(bs::Mhalf<T>()), T(-30), 0.5);
  STF_ULP_EQUAL(asind(bs::Mone<T>()), T(-90), 0.5);
  STF_ULP_EQUAL(asind(bs::One<T>()), T(90), 0.5);
  STF_ULP_EQUAL(asind(bs::Zero<T>()), bs::Zero<r_t>(), 0.5);
}


//=======================================================================
// Copyright 2015 - 2020 Jeff Linahan
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================
#include "lemmas.h"
#include "graphutil.h"
#include "strutil.h"
#include "Partition.h"
#include "BFSVisitor.h"
#include <boost/graph/copy.hpp>
#include <boost/graph/graph_utility.hpp>
using namespace std;
using namespace boost;

/* Let G be any planar graph with nonnegative vertex costs summing to no more than one.
Suppose G has a spanning tree of radius r.
Then the vertices of G can be partitioned into three sets A, B, C such that no edge joins a vertex in A with a vertex in B, neither A nor B has total cost exceeding 2/3,
and C contains no more than 2r+1 vertices, one the root of the tree */
// r is spanning tree radius
Partition lemma2(GraphCR g_orig, vector<vertex_t> const& cycle, BFSVisitorData const& visdata_orig, vertex_t costzero)
{
        cout << "lemma2 graph:\n";
        print_graph(g_orig);
        print_graph_addresses(g_orig);
        uint n = num_vertices(g_orig);

        auto prop_map = get(vertex_index, g_orig);
        for( vertex_t v : cycle ) cout << "cyclevert: " << v << " propmap: " << prop_map[v] << endl;

        // if g contains only two vertices then return trivially
        if( n <= 2 ){
                VertIter vit, vjt;
                tie(vit, vjt) = vertices(g_orig);
                Partition p;
                p.a.insert(*vit);
                if( ++vit != vjt ) p.b.insert(*vit);
                return p;
        }

        Graph g_shrink2(g_orig);

        auto g_shrink2_prop_map = get(vertex_index, g_shrink2);
        auto g_orig_prop_map = get(vertex_index, g_orig);

        // find shrinkroot
        vertex_t shrinkroot, costzero2;
        for( auto[vit, vjt] = vertices(g_shrink2); vit != vjt; ++vit ){
                if( g_shrink2_prop_map[*vit] == g_orig_prop_map[visdata_orig.root] ) shrinkroot = *vit;
                if( g_shrink2_prop_map[*vit] == g_orig_prop_map[costzero] ) costzero2 = *vit;
        }

	BFSVisitorData visdata(&g_shrink2, shrinkroot);
        breadth_first_search(g_shrink2, visdata.root, boost::visitor(BFSVisitor(visdata)));
	uint r = visdata_orig.num_levels-1; // spanning tree radius

	//BOOST_ASSERT(visdata.num_levels == visdata_orig.num_levels);
	/* Let G be any planar graph with nonnegative vertex costs summing to no more than one.
	Suppose G has a spanning tree of radius r.
	Then the vertices of G can be partitioned into three sets A, B, C, such that no edge joins a vertex A with a vertex in B, neither A nor B has a total cost exceeding 2/3, and C contains no more than 2r+1 vertices, one the root of the tree. */

        // decrement descendent costs from costzero
        vertex_t ansc = costzero2;
        while( ansc ){
                --visdata.verts[ansc].descendant_cost;
                ansc = visdata.verts.find(ansc)->second.parent;
        }

        make_max_planar(g_shrink2);
        EmbedStruct em(&g_shrink2);

	/* Proof.  Assume no vertex has cost exceeding 1/3; otherwise the lemma is true.
	Embed G in the plane.  Make each face a triangle by adding a suitable number of additional edges.
	Any nontree edge (including each of the added edges) forms a simple cycle with some of the tree edges.
	This cycle is of length at most 2r+1 if it contains the root of the tree, at most 2r-1 otherwise.
	The cycle divides the plane (and the graph) into two parts, the inside and the outside of the cycle.
	We claim that at least one such cycle separates the graph so that neither the inside nor the outside contains vertices whose total cost exceeds 2/3.  This proves the lemma. */

        vector<edge_t> nontree_edges;
        for( auto[ei, ei_end] = edges(g_shrink2); ei != ei_end; ++ei ){
                if( source(*ei, g_shrink2) == target(*ei, g_shrink2) ) continue; // ignore workaround ciruclar nodes
                if( !visdata.is_tree_edge(*ei) ) nontree_edges.push_back(*ei);
        }

        vector<CycleCost> ccs;
        for( edge_t e : nontree_edges ){
                vector<vertex_t> cycle = visdata.get_cycle(source(e, g_shrink2), target(e, g_shrink2));
                CycleCost cc = compute_cycle_cost(cycle, g_shrink2, visdata, em);
                ccs.push_back(cc);
        }

        uint mindiff = UINT_MAX;
        uint min_index = UINT_MAX;
        for( uint i = 0; i < nontree_edges.size(); ++i ){
                uint costdiff = abs((int)ccs[i].inside - (int)ccs[i].outside);
                if( costdiff < mindiff ){
                        mindiff = costdiff;
                        min_index = i;
                }
        }

        edge_t xz = nontree_edges[min_index];
        cout << "xz: " << source(xz, g_shrink2) << ", " << target(xz, g_shrink2) << '\n';

        BOOST_ASSERT(ccs[min_index].inside <= 2*n/3);
        BOOST_ASSERT(ccs[min_index].outside <= 2*n/3);
        auto cycle2 = visdata.get_cycle(xz);

        uint cyclelengthlimit = find(STLALL(cycle2), visdata.root) != cycle2.end() ? 2*r+1 : 2*r-1;
        BOOST_ASSERT(cycle2.size() <= cyclelengthlimit);

        auto p = Partition(cycle2, g_shrink2, em);

        map<vertex_t, vertex_t> shrink2orig;

        auto[vit, vjt] = vertices(g_shrink2);
        for( ;vit != vjt; ++vit ){
                uint i = g_shrink2_prop_map[*vit];
                for( auto[vit2, vjt2] = vertices(g_orig); vit2 != vjt2; ++vit2 ){
                        if( g_orig_prop_map[*vit2] == i ){
                                shrink2orig[*vit] = *vit2;
                        }

                }
        }

        Partition porig;
        for( vertex_t v : p.a ) porig.a.insert(shrink2orig[v]); // Let A be all verts inside the cycle
        for( vertex_t v : p.b ) porig.b.insert(shrink2orig[v]); // Let B be all verts outside the cycle
        for( vertex_t v : p.c ) porig.c.insert(shrink2orig[v]); // Let C be all verts on the cycle

	/*Proof of claim.
	Let (x,z) be the nontree edge whose cycle minimizes the maximum cost either inside or outside the cycle.  Break ties by choosing the nontree edge whose cycle has the smallest number of faces on the same side as the maximum cost.  If ties remain, choose arbitrarily.

	Suppose without loss of generality that the graph is embedded so that the cost inside the cycle (x z) cycle is at least as great as the cost outside the cycle.  If the vertices inside the cycle have total cost not exceeding 2/3, the claim is true.  Suppose the vertices inside the cycle have total cost exceeding 2/3.  We show by case analysis that this contradicts the choice of (x, z).
	Consider the face which has (x, z) as a boundary edge and lies inside the cycle.
	This face is a triangle; let y be its third vertex.
	The properties of (x, y) and (y, z) determine which of the following case applies.
	Figure 4 illustrates the cases.

	1) Both (x, y) and (y, z) lie on the cycle.  Then the face (x, y, z) is the cycle, which is impossible since vertices lie inside the cycle.
	2) One of (x, y) and (y, z) (say (x, y)) lies on the cycle. Then (y, z) is a nontree edge defining a cycle which contains within it the same vertices as the original cycle but one less face. This contradicts the choice of (x, z).
	3) Neither (x, y) nor (y, z) lies on the cycle.
		a) Both (x, y) and (y, z) are tree edges.  This is impossible since the tree itself contains no cycles.
		b) One of (x, y) and (y, z) (say x, y) is a tree edge.  Then (y, z) is a nontree edge defining a cycle which contains one less vertex (namely y) within it than the original cycle.
	The inside of the (y, z) cycle contains no more cost and one less face than the inside of the (x, z) cycle.  Thus if the cost inside the (y, z) cycle is greater than the cost outside the cycle, (y, z)
	would have been chosen in place of (x, z).
	On the other hand, suppose the cost inside the (y, z) cycle is no greater than the cost outside.
	The cost outside the (y, z) cycle is equal to the cost outside the (x, z) cycle plus the cost of y.
	Since both the cost outside the (x, z) cycle and the cost of y are less than 1/3,
	the cost outside the (y, z) cycle is less than 2/3, and (y, z) would have been chosen in place of (x, z).
		c) Neither (x, y) nor (y, z) is a tree edge. Then each of (x, y) and (y, z) defines a cycle, and every vertex inside the (x, z) cycle is either inside the (x, y) cycle, inside the (y, z) cycle, or on the boundary of both.
		Of the (x, y) and (y, z) cycles, choose the one (say (x, y)) which has inside it more total cost. The (x, y) cycle has no more cost and strictly fewer faces inside it than the (x, z) cycle.
		Thus if the cost inside the (x, y) cycle is greater than the cost outside, (x, y) would have been chosen in place of (x, z).
	On the other hand, suppose the cost inside the (x, y) cycle is no greater than the cost outside.
	Since the inside of the (x, z) cycle has cost exceeding 2/3, the (x, y) cycle and its inside together have cost exceeding 1/3, and the outside of the (x, y) cycle has cost less than 2/3.
	Thus (x, y) would have been chosen in place of (x, z).
	Thus all cases are impossible, and the (x, z) cycle satisfies the claim. */

        p.verify_sizes_lemma2(r, visdata.root);

        return porig;
}

vertex_t choose_costzero(Graph& g, BFSVisitorData const& visdata, uint l1)
{
        vertex_t v = Graph::null_vertex();
        auto[vit, vjt] = vertices(g);
        for( VertIter next = vit; vit != vjt; vit = next ){
                ++next;
                if( !visdata.verts.contains(*vit) ){
                        cout << "ignoring vertex: " << *vit << '\n';
                        continue;
                }

                uint level = visdata.verts.find(*vit)->second.level;
                if( level <= l1 ){
                        v = *vit;
                        break;
                }
        }
        return v;
}

// called when the middle part exceeds 2/3
Partition lemma3_exceeds23(Graph& g_shrink2, BFSVisitorData const& vis_data_shrunken, uint l1, uint l2, vector<vertex_t> const& fundamental_cycle)
{
        cout << "g_shrink2:\n";
        print_graph(g_shrink2);
        print_graph_addresses(g_shrink2);

        cout << "middle partition has cost exceeding 2/3\n";

        //delete all verts on level l2 and above
        VertIter vit, vjt;
        tie(vit, vjt) = vertices(g_shrink2);
        for( VertIter next = vit; vit != vjt; vit = next ){
                ++next;
                if( !vis_data_shrunken.verts.contains(*vit) ){
                        cout << "ignoring vertex: " << *vit << '\n';
                }
                if( vis_data_shrunken.verts.find(*vit)->second.level >= l2 ){
                        //cout << "killing vertex " << vmap_shrunk.vert2uint[*vit] << " of level l2 or above: " << vis_data_orig.verts.find(*vit)->second.level << " >= " << l[2] << '\n';
                        kill_vertex(*vit, g_shrink2);
                }
        }

        // shrink all verts on levels l1 and below to a single vertex of cost zero

        // find a vertex to call cost zero
        vertex_t costzero = choose_costzero(g_shrink2, vis_data_shrunken, l1);

        BOOST_ASSERT(costzero != Graph::null_vertex());
        auto prop_map = get(vertex_index, g_shrink2);
        cout << "costzero: " << costzero << " propmap: " << prop_map[costzero] << '\n';

        // contract
        tie(vit, vjt) = vertices(g_shrink2);
        for( VertIter next = vit; vit != vjt; vit = next ){
                ++next;
                if( !vis_data_shrunken.verts.contains(*vit) ){
                        cout << "ignoring vertex: " << *vit << '\n';
                }

                uint level = vis_data_shrunken.verts.find(*vit)->second.level;
                if( level <= l1 && *vit != costzero ){
                        costzero = contract_vertices(costzero, *vit, g_shrink2);
                }
        }

        // The new graph has a spanning tree radius of l2 - l1 -1 whose root corresponds to vertices on levels l1 and below in the original graph
        uint r = l2 - l1 - 1;
	BOOST_ASSERT(r == vis_data_shrunken.num_levels-1);
        // Apply Lemma 2 to the new graph.  Let A*, B*, C* be the resulting vertex partition
        Partition star_p = lemma2(g_shrink2, fundamental_cycle, vis_data_shrunken, costzero);
        BOOST_ASSERT(star_p.verify_sizes_lemma2(r, vis_data_shrunken.root));
        vertex_t star_root;

        Partition p;
        p.a = star_p.a.size() > star_p.b.size() ? star_p.a : star_p.b;

        p.c = star_p.c;
        p.c.erase(star_root);
        // add verts on levels l1 and l2 in the original graph
        tie(vit, vjt) = vertices(g_shrink2);
        for( VertIter next = vit; vit != vjt; vit = next ){
                ++next;
                BOOST_ASSERT(vis_data_shrunken.verts.contains(*vit));
                if( vis_data_shrunken.verts.find(*vit)->second.level == l2 ||
                        vis_data_shrunken.verts.find(*vit)->second.level == l1 ){
                                p.c.insert(*vit);
                }
        }

        // all verts not already in A or C go in B
        tie(vit, vjt) = vertices(g_shrink2);
        for( VertIter next = vit; vit != vjt; vit = next ){
                ++next;
                if( p.a.find(*vit) == p.a.end() &&
                        p.c.find(*vit) == p.c.end() ){
                                p.b.insert(*vit);

                        }
        }

        return p;
        /* By Lemma 2, A has total cost <= 2/3
        But A and C* have total cost >= 1/3, so B also has total cost <= 2/3
        Futhermore, C contains no more than L[l1] + L[l2] + 2(l2 - l1 - 1) */
}

Partition lemma3_l1greaterequall2(GraphCR g_shrink2, BFSVisitorData const& vis_data_orig, uint l1, uint r)
{
        //cout << "l1 is greater than or equal to l2\n";

        Partition p;
        for( auto[vei, vend] = vertices(g_shrink2); vei != vend; ++vei ){
                vertex_t v = *vei;
                if( ! vis_data_orig.verts.contains(v) ){
			cout << "ignoring non bfs vertex: " << v << '\n';
			continue;
		}
                uint level = vis_data_orig.verts.find(v)->second.level;

                //cout << "level of " << ii << ": " << vis_data_orig.verts.find(v)->second.level << "  ";
                if( level <  l1 ){                 cout << " belongs to first part\n";  p.a.insert(v); continue; }
                if( level >= l1+1 && level <= r ){ cout << " belongs to middle part\n"; p.b.insert(v); continue; }
                if( level == l1 ){                 cout << " belongs to last part\n";   p.c.insert(v); continue; }
                BOOST_ASSERT(0);
        }
        /*cout << "A = all verts on levels 0    thru l1-1\n";
        cout << "B = all verts on levels l1+1 thru r\n";
        cout << "C = all verts on level l1\n";*/
        return p;
}

Partition lemma3_lessequal23(set<vertex_t> const& first_part, set<vertex_t> const& middle_part, set<vertex_t> const& last_part, set<vertex_t>& deleted_part, Graph const* g)
{
        cout << "middle partition has cost <= 2/3\n";

        Partition p;
        p.a = first_part;
        p.b = middle_part;
        p.c = last_part;

        set<vertex_t> const* costly_part;
        set<vertex_t> const* other1;
        set<vertex_t> const* other2;
        p.get_most_costly_part(&costly_part, &other1, &other2);

        p.print(g);
        cout <<   "A = most costly part of the 3 : "; for( auto& a : *costly_part ) cout << a << ' ';
        cout << "\nB = remaining 2 parts         : "; for( auto& b : *other1      ) cout << b << ' '; for( auto& b : *other2 ) cout << b << ' ';
        cout << "\nC = deleted verts on l1 and l2: "; for( auto& v : deleted_part ) cout << v << ' '; cout << '\n';

        Partition p2;
        p2.a = *costly_part;
        p2.c = deleted_part;
        p2.b = *other1;
        p2.b.insert(other2->begin(), other2->end());
        p2.print(g);
        return p2;
}

/* Let G be any n-vertex connected planar graph having nonegative vertex consts summing to no more than one.
Suppose that the vertices of G are partitioned into levels according to their distance from some vertex v, and that L(l) denotes the number of vertices on level l.
If r is the maximum distance of any vertex from v, let r+1 be an additional level containing no vertices.
Given any two levels l1 and l2 such that levels 0 through l1-1 have total cost not exceeding 2/3 and levels l2+1 through r+1 have total cost not exceeding 2/3,
it is possible to find a partition A, B, C of the vertices of G such that no edge joins a vertex in A with a vertex in B, neither A nor B has total cost exceeding 2/3, and C contains no more than L(l1)+L(l2)+max{0,2(l2-l1-1)} vertices. */
Partition lemma3(GraphCR g_orig, vector<uint> const& L, uint l1, uint l2, uint r, BFSVisitorData const& vis_data_orig, BFSVisitorData const& vis_data_shrunken, vector<vertex_t> const& cycle, Graph* g_shrunk)
{
        uint n = vis_data_orig.verts.size();
        uint n_orig = num_vertices(g_orig);
        cout << "n: " << n << '\n';
        cout << "n_orig: " << n_orig << '\n';

        if( n != n_orig ){
                // more than one connected component, we need to recompute l1 and l2

                uint total = 0;
                uint level = 0;
                while( total < 2*n_orig/3 ) total += L[level++];

                total = 0;
                level = r+1;
                while( total < 2*n_orig/3 ){
                        total += L[level];
			if( 0 == level ) break;
			--level;
                }

                l2 = level;
        }

        uint cost_0thrul1m1 = 0;
        uint cost_l2p1thrur1 = 0;
        for( uint i = 0; i < r; ++i ){
                if( i+1 < l1 ) cost_0thrul1m1 += L[i];
                if( i >= l2+1 ) cost_l2p1thrur1 += L[i];
        }
        uint costlimit = 2*n/3;
        BOOST_ASSERT(cost_0thrul1m1 <= costlimit);
        BOOST_ASSERT(cost_l2p1thrur1 <= costlimit);

        //BOOST_ASSERT(vis_data_shrunken.assert_data());
        //BOOST_ASSERT(vis_data_orig.assert_data());
        //BOOST_ASSERT(n == n_orig);

        //uint total_cost = 0;

        auto prop_map = get(vertex_index, g_orig); // writing to this property map has side effects in the graph

        Partition p;
        if( l1 >= l2 ){
                p = lemma3_l1greaterequall2(g_orig, vis_data_orig, l1, r);
        } else {
                cout << "l1 < l2\n";

                set<vertex_t> first_part, middle_part, last_part, deleted_part;
                VertIter vei, vend;
                for( tie(vei, vend) = vertices(g_orig); vei != vend; ++vei ){
                        vertex_t v = *vei;
                        if( !vis_data_orig.verts.contains(v) ){
                                cout << "lemmas.cpp: ignoring bad vertex: " << v << " prop_map: " << prop_map[v] << '\n';
                                continue;
                        }

                        uint level = vis_data_orig.verts.find(v)->second.level;

                        cout << "level of " << v << " prop_map " << prop_map[v] << " is : " << level << ", ";
                        fflush(stdout);
                        if( level == l1 || level == l2 ){     cout << v << " is deleted\n";                 deleted_part.insert(v); continue;}
                        if( level <  l1 ){                    cout << v << " belongs to first part (A)\n";  first_part.insert(v);   continue;}
                        if( level >= l1+1 && level <= l2-1 ){ cout << v << " belongs to middle part (B)\n"; middle_part.insert(v);  continue;}
                        if( level >  l2   ){                  cout << v << " belongs to last part (C)\n";   last_part.insert(v);    continue;}
                        BOOST_ASSERT(0);
                }

                uint ptotal = first_part.size() + middle_part.size() + last_part.size() + deleted_part.size();
                cout << "ptotal: " << ptotal << '\n';
                BOOST_ASSERT(ptotal == n);

                //the only part which can have cost > 2/3 is the middle part (B)
                cout << "first part size: " << first_part.size() << '\n';
                cout << "middle part size: " << middle_part.size() << '\n';
                cout << "third part size: " << last_part.size() << '\n';
                BOOST_ASSERT(first_part.size() <= 2*n/3);
                BOOST_ASSERT(last_part.size() <= 2*n/3);
                //p.print();

                p = middle_part.size() <= 2*n/3                                          ?
                    lemma3_lessequal23(first_part, middle_part, last_part, deleted_part, &g_orig) :
                    lemma3_exceeds23(*g_shrunk, vis_data_shrunken, l1, l2, cycle);
        }

        BOOST_ASSERT(p.verify_edges(g_orig));
	BOOST_ASSERT(p.verify_sizes_lemma3(L, l1, l2));
        return p;
}


/*
 [auto_generated]
 boost/numeric/odeint/stepper/adams_bashforth_moulton.hpp

 [begin_description]
 Implementation of the Adams-Bashforth-Moulton method, a predictor-corrector multistep method.
 [end_description]

 Copyright 2009-2011 Karsten Ahnert
 Copyright 2009-2011 Mario Mulansky

 Distributed under the Boost Software License, Version 1.0.
 (See accompanying file LICENSE_1_0.txt or
 copy at http://www.boost.org/LICENSE_1_0.txt)
 */

#ifndef BOOST_NUMERIC_ODEINT_STEPPER_ADAMS_BASHFORTH_MOULTON_HPP_INCLUDED
#define BOOST_NUMERIC_ODEINT_STEPPER_ADAMS_BASHFORTH_MOULTON_HPP_INCLUDED

#include <boost/numeric/odeint/util/bind.hpp>

#include <boost/numeric/odeint/stepper/stepper_categories.hpp>
#include <boost/numeric/odeint/algebra/range_algebra.hpp>
#include <boost/numeric/odeint/algebra/default_operations.hpp>

#include <boost/numeric/odeint/util/state_wrapper.hpp>
#include <boost/numeric/odeint/util/resizer.hpp>

#include <boost/numeric/odeint/stepper/adams_bashforth.hpp>
#include <boost/numeric/odeint/stepper/adams_moulton.hpp>

namespace boost {
namespace numeric {
namespace odeint {

template<
size_t Steps ,
class State ,
class Value = double ,
class Deriv = State ,
class Time = Value ,
class Algebra = range_algebra ,
class Operations = default_operations ,
class Resizer = initially_resizer
>
class adams_bashforth_moulton
{

#ifndef DOXYGEN_SKIP
    BOOST_STATIC_ASSERT(( Steps > 0 ));
    BOOST_STATIC_ASSERT(( Steps < 9 ));
#endif

public :

    typedef State state_type;
    typedef state_wrapper< state_type > wrapped_state_type;
    typedef Value value_type;
    typedef Deriv deriv_type;
    typedef state_wrapper< deriv_type > wrapped_deriv_type;
    typedef Time time_type;
    typedef Algebra algebra_type;
    typedef Operations operations_type;
    typedef Resizer resizer_type;
    typedef stepper_tag stepper_category;

    static const size_t steps = Steps;
#ifndef DOXYGEN_SKIP
    typedef adams_bashforth< steps , state_type , value_type , deriv_type , time_type , algebra_type , operations_type , resizer_type > adams_bashforth_type;
    typedef adams_moulton< steps , state_type , value_type , deriv_type , time_type , algebra_type , operations_type , resizer_type > adams_moulton_type;
#endif //DOXYGEN_SKIP
    typedef unsigned short order_type;
    static const order_type order_value = steps + 1;

    /** \brief Constructs the adams_bashforth class. */
    adams_bashforth_moulton( void )
    : m_adams_bashforth() , m_adams_moulton( m_adams_bashforth.algebra() )
    { }

    adams_bashforth_moulton( const algebra_type &algebra )
    : m_adams_bashforth( algebra ) , m_adams_moulton( m_adams_bashforth.algebra() )
    { }

    order_type order( void ) const { return order_value; }

    template< class System , class StateInOut >
    void do_step( System system , StateInOut &x , time_type t , time_type dt )
    {
        m_adams_bashforth.do_step( system , x , t , dt );
        m_adams_moulton.do_step( system , x , t , dt , m_adams_bashforth.step_storage() );
    }

    /**
     * \brief Second version to solve the forwarding problem, can be called with Boost.Range as StateInOut.
     */
    template< class System , class StateInOut >
    void do_step( System system , const StateInOut &x , time_type t , time_type dt )
    {
        m_adams_bashforth.do_step( system , x , t , dt );
        m_adams_moulton.do_step( system , x , t , dt , m_adams_bashforth.step_storage() );
    }

    template< class System , class StateIn , class StateOut >
    void do_step( System system , const StateIn &in , time_type t , const StateOut &out , time_type dt )
    {
        m_adams_bashforth.do_step( system , in , t , out , dt );
        m_adams_moulton.do_step( system , out , t , dt , m_adams_bashforth.step_storage() );
    }

    /**
     * \brief Second version to solve the forwarding problem, can be called with Boost.Range as StateOut.
     */
    template< class System , class StateIn , class StateOut >
    void do_step( System system , const StateIn &in , time_type t , StateOut &out , time_type dt )
    {
        m_adams_bashforth.do_step( system , in , t , out , dt );
        m_adams_moulton.do_step( system , out , t , dt , m_adams_bashforth.step_storage() );
    }

    template< class StateType >
    void adjust_size( const StateType &x )
    {
        m_adams_bashforth.adjust_size( x );
        m_adams_moulton.adjust_size( x );
    }

    template< class ExplicitStepper , class System , class StateIn >
    void initialize( ExplicitStepper explicit_stepper , System system , StateIn &x , time_type &t , time_type dt )
    {
        m_adams_bashforth.initialize( explicit_stepper , system , x , t , dt );
    }

    template< class System , class StateIn >
    void initialize( System system , StateIn &x , time_type &t , time_type dt )
    {
        m_adams_bashforth.initialize( system , x , t , dt );
    }

private:

    adams_bashforth_type m_adams_bashforth;
    adams_moulton_type m_adams_moulton;
};

/********* DOXYGEN ********/

/**
 * \class adams_bashforth_moulton
 * \brief The Adams-Bashforth-Moulton multistep algorithm.
 *
 * The Adams-Bashforth method is a multi-step predictor-corrector algorithm
 * with configurable step number. The step number is specified as template
 * parameter Steps and it then uses the result from the previous Steps steps.
 * See also
 * en.wikipedia.org/wiki/Linear_multistep_method.
 * Currently, a maximum of Steps=8 is supported.
 * The method is explicit and fulfills the Stepper concept. Step size control
 * or continuous output are not provided.
 *
 * This class derives from algebra_base and inherits its interface via
 * CRTP (current recurring template pattern). For more details see
 * algebra_stepper_base.
 *
 * \tparam Steps The number of steps (maximal 8).
 * \tparam State The state type.
 * \tparam Value The value type.
 * \tparam Deriv The type representing the time derivative of the state.
 * \tparam Time The time representing the independent variable - the time.
 * \tparam Algebra The algebra type.
 * \tparam Operations The operations type.
 * \tparam Resizer The resizer policy type.
 * \tparam InitializingStepper The stepper for the first two steps.
 */

    /**
     * \fn adams_bashforth_moulton::adams_bashforth_moulton( const algebra_type &algebra )
     * \brief Constructs the adams_bashforth class. This constructor can be used as a default
     * constructor if the algebra has a default constructor.
     * \param algebra A copy of algebra is made and stored.
     */

    /**
     * \fn adams_bashforth_moulton::order( void ) const
     * \brief Returns the order of the algorithm, which is equal to the number of steps+1.
     * \return order of the method.
     */

    /**
     * \fn adams_bashforth_moulton::do_step( System system , StateInOut &x , time_type t , time_type dt )
     * \brief This method performs one step. It transforms the result in-place.
     *
     * \param system The system function to solve, hence the r.h.s. of the ordinary differential equation. It must fulfill the
     *               Simple System concept.
     * \param x The state of the ODE which should be solved. After calling do_step the result is updated in x.
     * \param t The value of the time, at which the step should be performed.
     * \param dt The step size.
     */

    /**
     * \fn adams_bashforth_moulton::do_step( System system , const StateIn &in , time_type t , const StateOut &out , time_type dt )
     * \brief The method performs one step with the stepper passed by Stepper. The state of the ODE is updated out-of-place.
     *
     * \param system The system function to solve, hence the r.h.s. of the ODE. It must fulfill the
     *               Simple System concept.
     * \param in The state of the ODE which should be solved. in is not modified in this method
     * \param t The value of the time, at which the step should be performed.
     * \param out The result of the step is written in out.
     * \param dt The step size.
     */

    /**
     * \fn adams_bashforth_moulton::adjust_size( const StateType &x )
     * \brief Adjust the size of all temporaries in the stepper manually.
     * \param x A state from which the size of the temporaries to be resized is deduced.
     */

    /**
     * \fn adams_bashforth_moulton::initialize( ExplicitStepper explicit_stepper , System system , StateIn &x , time_type &t , time_type dt )
     * \brief Initialized the stepper. Does Steps-1 steps with the explicit_stepper to fill the buffer.
     * \note The state x and time t are updated to the values after Steps-1 initial steps.
     * \param explicit_stepper the stepper used to fill the buffer of previous step results
     * \param system The system function to solve, hence the r.h.s. of the ordinary differential equation. It must fulfill the
     *               Simple System concept.
     * \param x The initial state of the ODE which should be solved, updated after in this method.
     * \param t The initial time, updated in this method.
     * \param dt The step size.
     */

    /**
     * \fn adams_bashforth_moulton::initialize( System system , StateIn &x , time_type &t , time_type dt )
     * \brief Initialized the stepper. Does Steps-1 steps using the standard initializing stepper
     * of the underlying adams_bashforth stepper.
     * \param system The system function to solve, hence the r.h.s. of the ordinary differential equation. It must fulfill the
     *               Simple System concept.
     * \param x The state of the ODE which should be solved. After calling do_step the result is updated in x.
     * \param t The value of the time, at which the step should be performed.
     * \param dt The step size.
     */

} // odeint
} // numeric
} // boost

#endif // BOOST_NUMERIC_ODEINT_STEPPER_ADAMS_BASHFORTH_MOULTON_HPP_INCLUDED


//  (C) Copyright John Maddock 2006.
//  Use, modification and distribution are subject to the
//  Boost Software License, Version 1.0. (See accompanying file
//  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

#include <boost/math/bindings/rr.hpp>
#include <boost/test/included/prg_exec_monitor.hpp>
#include <boost/math/special_functions/gamma.hpp>
#include <boost/math/special_functions/erf.hpp> // for inverses
#include <boost/math/constants/constants.hpp>
#include <boost/math/tools/test.hpp>
#include <fstream>

#include <boost/math/tools/test_data.hpp>

using namespace boost::math::tools;
using namespace std;

float external_f;
float force_truncate(const float* f)
{
   external_f = *f;
   return external_f;
}

float truncate_to_float(boost::math::ntl::RR r)
{
   float f = boost::math::tools::real_cast<float>(r);
   return force_truncate(&f);
}

struct erf_data_generator
{
   boost::math::tuple<boost::math::ntl::RR, boost::math::ntl::RR> operator()(boost::math::ntl::RR z)
   {
      // very naively calculate spots using the gamma function at high precision:
      int sign = 1;
      if(z < 0)
      {
         sign = -1;
         z = -z;
      }
      boost::math::ntl::RR g1, g2;
      g1 = boost::math::tgamma_lower(boost::math::ntl::RR(0.5), z * z);
      g1 /= sqrt(boost::math::constants::pi<boost::math::ntl::RR>());
      g1 *= sign;

      if(z < 0.5)
      {
         g2 = 1 - (sign * g1);
      }
      else
      {
         g2 = boost::math::tgamma(boost::math::ntl::RR(0.5), z * z);
         g2 /= sqrt(boost::math::constants::pi<boost::math::ntl::RR>());
      }
      if(sign < 1)
         g2 = 2 - g2;
      return boost::math::make_tuple(g1, g2);
   }
};

double double_factorial(int N)
{
   double result = 1;
   while(N > 2)
   {
      N -= 2;
      result *= N;
   }
   return result;
}

void asymptotic_limit(int Bits)
{
   //
   // The following block of code estimates how large z has
   // to be before we can use the asymptotic expansion for
   // erf/erfc and still get convergence: the series becomes
   // divergent eventually so we have to be careful!
   //
   double result = (std::numeric_limits<double>::max)();
   int terms = 0;
   for(int n = 1; n < 15; ++n)
   {
      double lim = (Bits-n) * log(2.0) - log(sqrt(3.14)) + log(double_factorial(2*n+1));
      double x = 1;
      while(x*x + (2*n+1)*log(x) <= lim)
         x += 0.1;
      if(x < result)
      {
         result = x;
         terms = n;
      }
   }

   std::cout << "Erf asymptotic limit for "
      << Bits << " bit numbers is "
      << result << " after approximately "
      << terms << " terms." << std::endl;

   result = (std::numeric_limits<double>::max)();
   terms = 0;
   for(int n = 1; n < 30; ++n)
   {
      double x = pow(double_factorial(2*n+1)/pow(2.0, n-Bits), 1 / (2.0*n));
      if(x < result)
      {
         result = x;
         terms = n;
      }
   }

   std::cout << "Erfc asymptotic limit for "
      << Bits << " bit numbers is "
      << result << " after approximately "
      << terms << " terms." << std::endl;
}

boost::math::tuple<boost::math::ntl::RR, boost::math::ntl::RR> erfc_inv(boost::math::ntl::RR r)
{
   boost::math::ntl::RR x = exp(-r * r);
   x = NTL::RoundToPrecision(x.value(), 64);
   std::cout << x << "   ";
   boost::math::ntl::RR result = boost::math::erfc_inv(x);
   std::cout << result << std::endl;
   return boost::math::make_tuple(x, result);
}

int cpp_main(int argc, char*argv [])
{
   boost::math::ntl::RR::SetPrecision(1000);
   boost::math::ntl::RR::SetOutputPrecision(40);

   parameter_info<boost::math::ntl::RR> arg1;
   test_data<boost::math::ntl::RR> data;

   bool cont;
   std::string line;

   if(argc >= 2)
   {
      if(strcmp(argv[1], "--limits") == 0)
      {
         asymptotic_limit(24);
         asymptotic_limit(53);
         asymptotic_limit(64);
         asymptotic_limit(106);
         asymptotic_limit(113);
         return 0;
      }
      else if(strcmp(argv[1], "--erf_inv") == 0)
      {
         boost::math::ntl::RR (*f)(boost::math::ntl::RR);
         f = boost::math::erf_inv;
         std::cout << "Welcome.\n"
            "This program will generate spot tests for the inverse erf function:\n";
         std::cout << "Enter the number of data points: ";
         int points;
         std::cin >> points;
         data.insert(f, make_random_param(boost::math::ntl::RR(-1), boost::math::ntl::RR(1), points));
      }
      else if(strcmp(argv[1], "--erfc_inv") == 0)
      {
         boost::math::tuple<boost::math::ntl::RR, boost::math::ntl::RR> (*f)(boost::math::ntl::RR);
         f = erfc_inv;
         std::cout << "Welcome.\n"
            "This program will generate spot tests for the inverse erfc function:\n";
         std::cout << "Enter the maximum *result* expected from erfc_inv: ";
         double max_val;
         std::cin >> max_val;
         std::cout << "Enter the number of data points: ";
         int points;
         std::cin >> points;
         parameter_info<boost::math::ntl::RR> arg = make_random_param(boost::math::ntl::RR(0), boost::math::ntl::RR(max_val), points);
         arg.type |= dummy_param;
         data.insert(f, arg);
      }
   }
   else
   {
      std::cout << "Welcome.\n"
         "This program will generate spot tests for the erf and erfc functions:\n"
         "  erf(z) and erfc(z)\n\n";

      do{
         if(0 == get_user_parameter_info(arg1, "a"))
            return 1;
         data.insert(erf_data_generator(), arg1);

         std::cout << "Any more data [y/n]?";
         std::getline(std::cin, line);
         boost::algorithm::trim(line);
         cont = (line == "y");
      }while(cont);
   }

   std::cout << "Enter name of test data file [default=erf_data.ipp]";
   std::getline(std::cin, line);
   boost::algorithm::trim(line);
   if(line == "")
      line = "erf_data.ipp";
   std::ofstream ofs(line.c_str());
   write_code(ofs, data, "erf_data");

   return 0;
}

/* Output for asymptotic limits:

Erf asymptotic limit for 24 bit numbers is 2.8 after approximately 6 terms.
Erfc asymptotic limit for 24 bit numbers is 4.12064 after approximately 17 terms.
Erf asymptotic limit for 53 bit numbers is 4.3 after approximately 11 terms.
Erfc asymptotic limit for 53 bit numbers is 6.19035 after approximately 29 terms.
Erf asymptotic limit for 64 bit numbers is 4.8 after approximately 12 terms.
Erfc asymptotic limit for 64 bit numbers is 7.06004 after approximately 29 terms.
Erf asymptotic limit for 106 bit numbers is 6.5 after approximately 14 terms.
Erfc asymptotic limit for 106 bit numbers is 11.6626 after approximately 29 terms.
Erf asymptotic limit for 113 bit numbers is 6.8 after approximately 14 terms.
Erfc asymptotic limit for 113 bit numbers is 12.6802 after approximately 29 terms.
*/


// The contents of this file are in the public domain. See LICENSE_FOR_EXAMPLE_PROGRAMS.txt
/*
    This is an example illustrating the use of the deep learning tools from the dlib C++
    Library.  I'm assuming you have already read the dnn_introduction_ex.cpp, the
    dnn_introduction2_ex.cpp and the dnn_introduction3_ex.cpp examples.  In this example
    program we are going to show how one can train Generative Adversarial Networks (GANs).  In
    particular, we will train a Deep Convolutional Generative Adversarial Network (DCGAN) like
    the one introduced in this paper:
    "Unsupervised Representation Learning with Deep Convolutional Generative Adversarial Networks"
    by Alec Radford, Luke Metz, Soumith Chintala.

    The main idea is that there are two neural networks training at the same time:
    - the generator is in charge of generating images that look as close as possible to the
      ones from the dataset.
    - the discriminator will decide whether an image is fake (created by the generator) or real
      (selected from the dataset).

    Each training iteration alternates between training the discriminator and the generator.
    We first train the discriminator with real and fake images and then use the gradient from
    the discriminator to update the generator.

    In this example, we are going to learn how to generate digits from the MNIST dataset, but
    the same code can be run using the Fashion MNIST datset:
    https://github.com/zalandoresearch/fashion-mnist
*/

#include <algorithm>
#include <iostream>

#include <dlib/data_io.h>
#include <dlib/dnn.h>
#include <dlib/gui_widgets.h>
#include <dlib/matrix.h>

using namespace std;
using namespace dlib;

// Some helper definitions for the noise generation
const size_t noise_size = 100;
using noise_t = std::array<matrix<float, 1, 1>, noise_size>;

noise_t make_noise(dlib::rand& rnd)
{
    noise_t noise;
    for (auto& n : noise)
    {
        n = rnd.get_random_gaussian();
    }
    return noise;
}

// A convolution with custom padding
template<long num_filters, long kernel_size, int stride, int padding, typename SUBNET>
using conp = add_layer<con_<num_filters, kernel_size, kernel_size, stride, stride, padding, padding>, SUBNET>;

// A transposed convolution to with custom padding
template<long num_filters, long kernel_size, int stride, int padding, typename SUBNET>
using contp = add_layer<cont_<num_filters, kernel_size, kernel_size, stride, stride, padding, padding>, SUBNET>;

// The generator is made of a bunch of deconvolutional layers.  Its input is a 1 x 1 x k noise
// tensor, and the output is the generated image.  The loss layer does not matter for the
// training, we just stack a compatible one on top to be able to have a () operator on the
// generator.
using generator_type =
    loss_binary_log_per_pixel<
    sig<contp<1, 4, 2, 1,
    relu<bn_con<contp<64, 4, 2, 1,
    relu<bn_con<contp<128, 3, 2, 1,
    relu<bn_con<contp<256, 4, 1, 0,
    input<noise_t>
    >>>>>>>>>>>>;

// Now, let's proceed to define the discriminator, whose role will be to decide whether an
// image is fake or not.
using discriminator_type =
    loss_binary_log<
    conp<1, 3, 1, 0,
    leaky_relu<bn_con<conp<256, 4, 2, 1,
    leaky_relu<bn_con<conp<128, 4, 2, 1,
    leaky_relu<conp<64, 4, 2, 1,
    input<matrix<unsigned char>>
    >>>>>>>>>>;

// Some helper functions to generate and get the images from the generator
matrix<unsigned char> generate_image(generator_type& net, const noise_t& noise)
{
    const matrix<float> output = net(noise);
    matrix<unsigned char> image;
    assign_image(image, 255 * output);
    return image;
}

std::vector<matrix<unsigned char>> get_generated_images(const tensor& out)
{
    std::vector<matrix<unsigned char>> images;
    for (long n = 0; n < out.num_samples(); ++n)
    {
        matrix<float> output = image_plane(out, n);
        matrix<unsigned char> image;
        assign_image(image, 255 * output);
        images.push_back(std::move(image));
    }
    return images;
}

#if defined(BUILD_MONOLITHIC)
#define main(cnt, arr)      dlib_dnn_dcgan_train_ex_main(cnt, arr)
#endif

int main(int argc, const char** argv)
try
{
    // This example is going to run on the MNIST dataset.
    if (argc != 2)
    {
        cout << "This example needs the MNIST dataset to run!" << endl;
        cout << "You can get MNIST from http://yann.lecun.com/exdb/mnist/" << endl;
        cout << "Download the 4 files that comprise the dataset, decompress them, and" << endl;
        cout << "put them in a folder.  Then give that folder as input to this program." << endl;
        return EXIT_FAILURE;
    }

    // MNIST is broken into two parts, a training set of 60000 images and a test set of 10000
    // images.  Each image is labeled so that we know what hand written digit is depicted.
    // These next statements load the dataset into memory.
    std::vector<matrix<unsigned char>> training_images;
    std::vector<unsigned long>         training_labels;
    std::vector<matrix<unsigned char>> testing_images;
    std::vector<unsigned long>         testing_labels;
    load_mnist_dataset(argv[1], training_images, training_labels, testing_images, testing_labels);

    // Fix the random generator seeds for network initialization and noise
    srand(1234);
    dlib::rand rnd(std::rand());

    // Instantiate both generator and discriminator
    generator_type generator;
    discriminator_type discriminator;
    // setup all leaky_relu_ layers in the discriminator to have alpha = 0.2
    visit_computational_layers(discriminator, [](leaky_relu_& l){ l = leaky_relu_(0.2); });
    // Remove the bias learning from all bn_ inputs in both networks
    disable_duplicative_biases(generator);
    disable_duplicative_biases(discriminator);
    // Forward random noise so that we see the tensor size at each layer
    discriminator(generate_image(generator, make_noise(rnd)));
    cout << "generator (" << count_parameters(generator) << " parameters)" << endl;
    cout << generator << endl;
    cout << "discriminator (" << count_parameters(discriminator) << " parameters)" << endl;
    cout << discriminator << endl;

    // The solvers for the generator and discriminator networks.  In this example, we are going to
    // train the networks manually, so we don't need to use a dnn_trainer.  Note that the
    // discriminator could be trained using a dnn_trainer, but not the generator, since its
    // training process is a bit particular.
    std::vector<adam> g_solvers(generator.num_computational_layers, adam(0, 0.5, 0.999));
    std::vector<adam> d_solvers(discriminator.num_computational_layers, adam(0, 0.5, 0.999));
    double learning_rate = 2e-4;

    // Resume training from last sync file
    size_t iteration = 0;
    if (file_exists("dcgan_sync"))
    {
        deserialize("dcgan_sync") >> generator >> discriminator >> iteration;
    }

    const size_t minibatch_size = 64;
    const std::vector<float> real_labels(minibatch_size, 1);
    const std::vector<float> fake_labels(minibatch_size, -1);
    dlib::image_window win;
    resizable_tensor real_samples_tensor, fake_samples_tensor, noises_tensor;
    running_stats<double> g_loss, d_loss;
    while (iteration < 50000)
    {
        // Train the discriminator with real images
        std::vector<matrix<unsigned char>> real_samples;
        while (real_samples.size() < minibatch_size)
        {
            auto idx = rnd.get_random_32bit_number() % training_images.size();
            real_samples.push_back(training_images[idx]);
        }
        // The following lines are equivalent to calling train_one_step(real_samples, real_labels)
        discriminator.to_tensor(real_samples.begin(), real_samples.end(), real_samples_tensor);
        d_loss.add(discriminator.compute_loss(real_samples_tensor, real_labels.begin()));
        discriminator.back_propagate_error(real_samples_tensor);
        discriminator.update_parameters(d_solvers, learning_rate);

        // Train the discriminator with fake images
        // 1. Generate some random noise
        std::vector<noise_t> noises;
        while (noises.size() < minibatch_size)
        {
            noises.push_back(make_noise(rnd));
        }
        // 2. Convert noises into a tensor
        generator.to_tensor(noises.begin(), noises.end(), noises_tensor);
        // 3. Forward the noise through the network and convert the outputs into images.
        const auto fake_samples = get_generated_images(generator.forward(noises_tensor));
        // 4. Finally train the discriminator.  The following lines are equivalent to calling
        // train_one_step(fake_samples, fake_labels)
        discriminator.to_tensor(fake_samples.begin(), fake_samples.end(), fake_samples_tensor);
        d_loss.add(discriminator.compute_loss(fake_samples_tensor, fake_labels.begin()));
        discriminator.back_propagate_error(fake_samples_tensor);
        discriminator.update_parameters(d_solvers, learning_rate);

        // Train the generator
        // This part is the essence of the Generative Adversarial Networks.  Until now, we have
        // just trained a binary classifier that the generator is not aware of.  But now, the
        // discriminator is going to give feedback to the generator on how it should update
        // itself to generate more realistic images.  The following lines perform the same
        // actions as train_one_step() except for the network update part.  They can also be
        // seen as test_one_step() plus the error back propagation.

        // Forward the fake samples and compute the loss with real labels
        g_loss.add(discriminator.compute_loss(fake_samples_tensor, real_labels.begin()));
        // Back propagate the error to fill the final data gradient
        discriminator.back_propagate_error(fake_samples_tensor);
        // Get the gradient that will tell the generator how to update itself
        const tensor& d_grad = discriminator.get_final_data_gradient();
        generator.back_propagate_error(noises_tensor, d_grad);
        generator.update_parameters(g_solvers, learning_rate);

        // At some point, we should see that the generated images start looking like samples from
        // the MNIST dataset
        if (++iteration % 1000 == 0)
        {
            serialize("dcgan_sync") << generator << discriminator << iteration;
            std::cout <<
                "step#: " << iteration <<
                "\tdiscriminator loss: " << d_loss.mean() * 2 <<
                "\tgenerator loss: " << g_loss.mean() << '\n';
            win.set_image(tile_images(fake_samples));
            win.set_title("DCGAN step#: " + to_string(iteration));
            d_loss.clear();
            g_loss.clear();
        }
    }

    // Once the training has finished, we don't need the discriminator any more. We just keep the
    // generator.
    generator.clean();
    serialize("dcgan_mnist.dnn") << generator;

    // To test the generator, we just forward some random noise through it and visualize the
    // output.
    while (!win.is_closed())
    {
        const auto image = generate_image(generator, make_noise(rnd));
        const auto real = discriminator(image) > 0;
        win.set_image(image);
        cout << "The discriminator thinks it's " << (real ? "real" : "fake");
        cout << ". Hit enter to generate a new image";
        cin.get();
    }

    return EXIT_SUCCESS;
}
catch(exception& e)
{
    cout << e.what() << endl;
    return EXIT_FAILURE;
}


#include <stan/math/opencl/double_d.hpp>
#include <gtest/gtest.h>
#include <limits>
#include <Eigen/Core>

#define EXPECT_NORMALIZED(a) \
  EXPECT_LT(std::abs(a.low), \
            std::abs(a.high) * std::numeric_limits<double>::epsilon());

TEST(double_d, add_dd_dd_test) {
  using stan::math::internal::add_dd_dd;
  using stan::math::internal::double_d;
  double eps = std::numeric_limits<double>::epsilon();
  double h_eps = eps * 0.5;
  double_d a{1.0, h_eps};
  double_d c{h_eps, 0.0};
  double_d d{-1.0, -h_eps + eps * h_eps};

  // simple
  double_d res = add_dd_dd(a, a);
  EXPECT_NORMALIZED(res);
  EXPECT_EQ(res.high, 2.0);
  EXPECT_EQ(res.low, eps);

  // carry
  res = add_dd_dd(a, c);
  EXPECT_NORMALIZED(res);
  EXPECT_EQ(res.high, 1.0 + eps);
  EXPECT_EQ(res.low, 0.0);

  // cancelation
  res = add_dd_dd(a, d);
  EXPECT_NORMALIZED(res);
  EXPECT_EQ(res.high, eps * h_eps);
  EXPECT_EQ(res.low, 0.0);
}

TEST(double_d, mul_dd_dd_test) {
  using stan::math::internal::double_d;
  using stan::math::internal::mul_dd_dd;
  double eps = std::numeric_limits<double>::epsilon();
  double h_eps = eps * 0.5;
  double_d a{1.0, h_eps * 0.001};
  double_d b{h_eps, h_eps * h_eps * h_eps * h_eps};
  double_d c{1.0, h_eps};
  double_d d{1.0, -h_eps};

  // simple
  double_d res = mul_dd_dd(a, b);
  EXPECT_NORMALIZED(res);
  EXPECT_EQ(res.high, h_eps);
  EXPECT_EQ(res.low, h_eps * h_eps * 0.001);

  // carry
  res = mul_dd_dd(c, c);
  EXPECT_NORMALIZED(res);
  EXPECT_EQ(res.high, 1.0 + eps);
  EXPECT_EQ(res.low, 0.0);

  // cancelation
  res = mul_dd_dd(c, d);
  EXPECT_NORMALIZED(res);
  EXPECT_EQ(res.high, 1.0);
  EXPECT_EQ(res.low, 0.0);
}

TEST(double_d, div_dd_dd_test) {
  using stan::math::internal::div_dd_dd;
  using stan::math::internal::double_d;
  double eps = std::numeric_limits<double>::epsilon();
  double h_eps = eps * 0.5;
  double_d a{1.0, h_eps};
  double_d b{1.0, h_eps * (1 - eps)};
  double_d c{0.0, 0.0};
  double_d d{1.0, -h_eps};

  // simple
  double_d res = div_dd_dd(a, a);
  EXPECT_NORMALIZED(res);
  EXPECT_EQ(res.high, 1.0);
  EXPECT_EQ(res.low, 0.0);

  res = div_dd_dd(a, b);
  EXPECT_NORMALIZED(res);
  EXPECT_EQ(res.high, 1.0);
  EXPECT_EQ(res.low, h_eps * eps);

  // div by zero
  res = div_dd_dd(a, c);
  EXPECT_EQ(res.high, std::numeric_limits<double>::infinity());
  EXPECT_EQ(res.low, 0);
}

// this is the best test we have, but it relies on the nonstandard gcc extension
// quadmath, so it is not run by default
#ifdef GCC_QUADMATH_TEST
#include <quadmath.h>

#define EXPECT_DD_F128_EQ(dd, f128) \
  tmp = (dd);                       \
  EXPECT_NEAR((__float128)tmp.high + tmp.low, (f128), 1e-30 * 1e30)

TEST(double_d, all) {
  using stan::math::internal::double_d;
  for (int i = 0; i < 10000000; i++) {
    double_d tmp;
    double high = Eigen::MatrixXd::Random(0, 0).coeff(0, 0) * 1e30;
    double low = Eigen::MatrixXd::Random(0, 0).coeff(0, 0) * 1e-20 * 1e30;
    double_d dd = high;
    dd.low = low;
    __float128 f128 = high;
    f128 += low;
    EXPECT_DD_F128_EQ(dd, f128);

    double high2 = Eigen::MatrixXd::Random(0, 0).coeff(0, 0);
    double low2 = Eigen::MatrixXd::Random(0, 0).coeff(0, 0) * 1e-20;
    double_d dd2 = high2;
    dd2.low = low2;
    __float128 f1282 = high2;
    f1282 += low2;
    EXPECT_DD_F128_EQ(dd2, f1282);

    EXPECT_DD_F128_EQ(dd + dd2, f128 + f1282);
    EXPECT_DD_F128_EQ(dd - dd2, f128 - f1282);
    EXPECT_DD_F128_EQ(dd * dd2, f128 * f1282);
    EXPECT_DD_F128_EQ(dd / dd2, f128 / f1282);

    __float128 f1283 = high;
    __float128 f1284 = high2;
    EXPECT_DD_F128_EQ(stan::math::internal::mul_d_d(high, high2),
                      f1283 * f1284);
  }
}
#endif

#ifdef STAN_OPENCL

#include <stan/math/opencl/prim.hpp>
static const std::string double_d_test_kernel_code
    = STRINGIFY(__kernel void double_d_test_kernel(__global double_d *C,
                                                   const __global double_d *A,
                                                   const __global double *B) {
        const int i = get_global_id(0);
        C[i] = mul_dd_d(A[i], B[i]);
      });

const stan::math::opencl_kernels::kernel_cl<
    stan::math::opencl_kernels::out_buffer,
    stan::math::opencl_kernels::in_buffer,
    stan::math::opencl_kernels::in_buffer>
    double_d_test_kernel("double_d_test_kernel",
                         {stan::math::internal::double_d_src,
                          double_d_test_kernel_code});

TEST(double_d, opencl) {
  using stan::math::internal::double_d;
  using VectorXdd = Eigen::Matrix<double_d, -1, 1>;
  int n = 10;
  VectorXdd a(n);
  Eigen::VectorXd b = Eigen::VectorXd::Random(n);
  for (int i = 0; i < n; i++) {
    a[i].high = Eigen::VectorXd::Random(1)[0];
    a[i].low = Eigen::VectorXd::Random(1)[0] * 1e-17;
  }
  stan::math::matrix_cl<double_d> a_cl(a);
  stan::math::matrix_cl<double> b_cl(b);
  stan::math::matrix_cl<double_d> c_cl(n, 1);
  double_d_test_kernel(cl::NDRange(n), c_cl, a_cl, b_cl);
  VectorXdd c = stan::math::from_matrix_cl(c_cl);
  for (int i = 0; i < n; i++) {
    double_d correct = a[i] * b[i];
    EXPECT_EQ(c[i].high, correct.high);
    EXPECT_NEAR(c[i].low, correct.low, 1e-30);
  }
}

#endif