/* Copyright (C) 2017 IBM Corp.
 * 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.
 */
/**********************************************************************
 * tools.cpp: misc. utilities used within the main program
 ***********************************************************************/
#include <NTL/version.h>
#include "tools.h"
#include "timing.h"
NTL_CLIENT

// A is an n x n matrix, we attempt to compute its inverse mod p^r.
// If A is invertible, X is set to A^{-1} and return value is true.
// Otherwise, return value is false.
// zz_p::modulus() is assumed to be p^r, for p prime, r >= 1.

bool ppInvert(Mat<zz_p>& X, const Mat<zz_p>& A, long p, long r)
{
#if ((NTL_MAJOR_VERSION>9)||((NTL_MAJOR_VERSION==9) && (NTL_MINOR_VERSION>=7)))
  // for recent enough versions of NTL, just use native calls
  FHE_TIMER_START;
  zz_p d;
  relaxed_inv(d, X, A);    // X = A^{-1}
  return d != 0;

#else
  // for older versions of NTL, use Hensel lifting

  if (r == 1) { // use native inversion from NTL
    zz_p d;
    inv(d, X, A);    // X = A^{-1}
    return d != 0;
  }

  // begin by inverting A modulo p

  // convert to long for a safe transaltion to mod-p objects
  Mat<long> tmp;
  conv(tmp, A);
  {
    zz_pPush push(p);  // temporarily work mod p
    zz_p d;

    Mat<zz_p> A1, Inv1;
    conv(A1, tmp);      // Recover A as a Mat<zz_p> object modulo p
    inv(d, Inv1, A1);   // Inv1 = A^{-1} (mod p)
    if (d ==0) return false;
    conv(tmp, Inv1); // convert to long for translation to a mod-p^r object
  }
  // back to working mod p^r

  Mat<zz_p> XX;
  conv(XX, tmp); // XX = A^{-1} (mod p)

  // Now lift the solution modulo p^r

  // Compute the "correction factor" Z, s.t. XX*A = I - Z (mod p^r)
  long n = A.NumRows();

  const Mat<zz_p> I = ident_mat_zz_p(n);
  Mat<zz_p> T;

  Mat<zz_p> Z; // = I - XX*A
  mul(T, XX, A);
  sub(Z, I, T);

  // The inverse of A is ( I+(Z)+(Z)^2+...+(Z)^{r-1} )*XX (mod p^r). We use
  // O(log r) products to compute it as (I+Z)* (I+(Z)^2)* (I+(Z)^4)*...* XX

  long e = NextPowerOfTwo(r); // 2^e is smallest power of two >= r

  Mat<zz_p> prod; // = I + Z;
  add(prod, I, Z);
  for (long i=1; i<e; i++) {
    sqr(Z, Z);     // = (Z)^{2^i}

    // prod *= (I+Z) = sum_{j=0}^{2^{i+1}-1} (Z)^j
    mul(T, prod, Z);
    add(prod, prod, T);
  }

  mul(X, prod, XX); // X = A^{-1} mod p^r
  return true;
#endif
}

// Preconditions:
//   * MM and M have same dimensions
//   0 <= MM[i][j] < P
//   gcd(p, P) = 1, where p = zz_p::modulus()
//   p*P can be represented as a long
// Postcondition:
//   0 <= MM'[i][j] < P*p
//   MM'[i][j] = MM[i][j] mod P and MM'[i][j] = M[i][j] mod p
//
//  Here, MM' is the new value of MM and MM is the original value

void CRT(Mat<long>& MM, long P, const Mat<zz_p>& M)
{
   long n = MM.NumRows();
   long m = MM.NumCols();

   if (M.NumRows() != n || M.NumCols() != m)
      LogicError("CRT: dimension mismatch");

   if (P == 1) {
      conv(MM, M);
      return;
   }

   long p = zz_p::modulus();
   mulmod_t pinv = zz_p::ModulusInverse();

   sp_reduce_struct p_aux = sp_PrepRem(p);
   // FIXME: NTL should make this visible

   long P_inv = InvMod(rem(cast_unsigned(P), p, p_aux), p);
   mulmod_precon_t P_inv_precon = PrepMulModPrecon(P_inv, p, pinv);

   for (long i = 0; i < n; i++) {
      for (long j = 0; j < m; j++) {
         long x = MM[i][j];
         long y = rep(M[i][j]);

         // compute h = (y-x)*P_inv mod p
         long x_reduced = rem(cast_unsigned(x), p, p_aux);
         long diff = SubMod(y, x_reduced, p);
         long h = MulModPrecon(diff, P_inv, p, P_inv_precon);

         MM[i][j] = x + P*h;
      }
   }
}

// Returns a list of prime factors and their multiplicity,
// N = \prod_i (factors[i].a)^{factors[i].b}
void factorize(Vec< Pair<long,long> > &factors, long N)
{
  factors.SetLength(0);

  if (N < 2) return;

  PrimeSeq s;
  long n = N;
  while (n > 1) {
    if (ProbPrime(n)) { // n itself is a prime, add (n,1) to the list
      append(factors, cons(n, 1L));
      return;
    }

    long p = s.next();
    if ((n % p) == 0) { // p divides n, find its multiplicity
      long e = 1;
      n = n/p;
      while ((n % p) == 0) {
        n = n/p;
        e++;
      }
      append(factors, cons(p, e)); // add (p,e) to the list
    }
  }
}

// VJS: I re-worked this to use zz_p::modulus(), instead of
// passing p as a parameter

// Convert to zz_p and invert mod p, assumes that p is easy to factor
// Returns true on success, false on failure
bool invMod(Mat<zz_p>& X, const Mat<long>& Y)
{
  long p = zz_p::modulus();
  Mat<long> XX(INIT_SIZE, Y.NumRows(), Y.NumCols());
  clear(XX);
  long PP = 1;

  Vec< Pair<long,long> > factors;
  factorize(factors, p);  // prime-power factorization

  { zz_pPush push; // backup modulus

    for (long i=0; i<factors.length(); i++) {
      long pr = power_long(factors[i].a, factors[i].b);
      zz_p::init(pr);
      mat_zz_p Ymod, Xmod;
      conv(Ymod, Y);             // convert to mat_zz_p
      if (!ppInvert(Xmod, Ymod, factors[i].a, factors[i].b)) // invert mod pi^ei
        return false; // inversion failed
      CRT(XX, PP, Xmod);
      PP *= pr;
    }
  }
  // modulus restored

  conv(X, XX); // convert result to mat_zz_p
  return true; // succeeded
}

// Experiment with faster random number generation...maybe we can use this elsewhere,
// like in helib.

#define RBH_BUFSZ (1024)

struct RandomBndHelper {

   long p;
   long nb;
   unsigned long mask;

   RandomStream *str;

   RandomBndHelper() : p(0) { }

   void init(long _p)
   {
      if (_p <= 1) LogicError("RandomBndHelper::init: bad args");

      if (!p) {
         str = &GetCurrentRandomStream();
      }

      p = _p;
      long l = NumBits(p-1);
      nb = (l+7)/8;
      mask = (1UL << l)-1UL;
   }

   long next()
   {
      unsigned char buf[NTL_BITS_PER_LONG/8];
      long tmp;

      do {
         str->get(buf, nb);

         unsigned long word = 0;
         for (long i = nb-1; i >= 0; i--) word = (word << 8) | buf[i];

         tmp = long(word & mask);
      } while (tmp >= p);

      return tmp;
   }
};

void RandomFill(Mat<zz_p>& X)
{
    FHE_TIMER_START;

   long n = X.NumRows();
   long m = X.NumCols();

   RandomBndHelper H;
   H.init(zz_p::modulus());

   for (long i = 0; i < n; i++) {
      zz_p *row = X[i].elts();
      for (long j = 0; j < m; j++) {
         row[j].LoopHole() = H.next();
      }
   }
}

// equivalent to conv(X, A), but assumes entries of A
// are already reduced
void conv_reduced(Mat<zz_p>& X, const Mat<long>& A)
{
   long n = A.NumRows();
   long m = A.NumCols();

   X.SetDims(n, m);
   for (long i = 0; i < n; i++) {
      zz_p *Xrow = X[i].elts();
      const long *Arow = A[i].elts();
      for (long j = 0; j < m; j++)
         Xrow[j].LoopHole() = Arow[j];
   }
}

// generates a random pair of nxn matrices with X*Y = I
// assumes zz_p::modulus() is easy to factor
void GenerateMatrixPair(Mat<zz_p>& X, Mat<zz_p>& Y, long n)
{
  FHE_TIMER_START;

   if (n <= 0) LogicError("GenerateMatrixPair: bad args");

   Mat<long> XX, YY;
   XX.SetDims(n, n);
   clear(XX);

   YY.SetDims(n, n);
   clear(YY);

   long P = 1;

   for (long p = 2, f = zz_p::modulus(); f > 1; p++) {
      if (f % p == 0) {
         long r = 1;
         long pr = p;
         f /= p;

         while (f % p == 0) {
            r++;
            pr *= p;
            f /= p;
         }

         zz_pPush push(pr);

         Mat<zz_p> X1, Y1;
         X1.SetDims(n, n);
         Y1.SetDims(n, n);

         do {
            RandomFill(X1);
         } while (!ppInvert(Y1, X1, p, r));

         CRT(YY, P, Y1);
         CRT(XX, P, X1);
         P *= pr;
      }
   }

   conv_reduced(X, XX);
   conv_reduced(Y, YY);

   FHE_TIMER_STOP;
}

// advance the input stream beyond white spaces and a single instance of cc
void seekPastChar(std::istream& str, int cc)
{
   int c = str.get();
   while (isspace(c)) c = str.get();
   if (c != cc) {
     std::cerr << "Searching for cc='"<<(char)cc<<"' (ascii "<<cc<<")"
	       << ", found c='"<<(char)c<<"' (ascii "<<c<<")\n";
     exit(1);
   }
}

#ifdef TEST_TOOLS
int main()
{
  long n = 200;

  long f = 3L*3L*3L*5L*7L*7L*7L*7L;

  zz_p::init(f);

  Mat<zz_p> X, Y, I;
  GenerateMatrixPair(X, Y, n);
  I = ident_mat_zz_p(n);

  if (X*Y == I)
    cerr << "GOOD1\n";
  else
    cerr << "BAD1\n";

  Mat<long> M(INIT_SIZE, n, n);
  do {
    for (long i=0; i<n; i++) for (long j=0; j<n; j++)
      M[i][j] = RandomBnd(100) - 50; // [-50,50)
  } while (!invMod(Y, M));

  conv(X, M);
  if (X*Y == I)
    cerr << "GOOD2\n";
  else
    cerr << "BAD2\n";
}

#endif // ifdef TEST_TOOLS


/*********************************************************************
* Software License Agreement (BSD License)
*
*  Copyright (c) 2010, Rice University
*  All rights reserved.
*
*  Redistribution and use in source and binary forms, with or without
*  modification, are permitted provided that the following conditions
*  are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the Rice University nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
*  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
*  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
*  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
*  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
*  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
*  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
*  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
*  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
*  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
*  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
*  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
*  POSSIBILITY OF SUCH DAMAGE.
*********************************************************************/

/* Author: Elizabeth Fudge */

#include <ompl/base/goals/GoalState.h>
#include <ompl/base/spaces/SE2StateSpace.h>
#include <ompl/base/spaces/DiscreteStateSpace.h>
#include <ompl/base/spaces/TimeStateSpace.h>
#include <ompl/control/spaces/RealVectorControlSpace.h>
#include <ompl/control/SimpleSetup.h>
#include <ompl/config.h>
#include <iostream>
#include <limits>
#include <boost/math/constants/constants.hpp>

namespace ob = ompl::base;
namespace oc = ompl::control;
using namespace std;

void propagate(const oc::SpaceInformation *si, const ob::State *state,
    const oc::Control* control, const double duration, ob::State *result)
{
    static double timeStep = .01;
    int nsteps = ceil(duration / timeStep);
    double dt = duration / nsteps;
    const double *u = control->as<oc::RealVectorControlSpace::ControlType>()->values;

    ob::CompoundStateSpace::StateType& s = *result->as<ob::CompoundStateSpace::StateType>();
    ob::SE2StateSpace::StateType& se2 = *s.as<ob::SE2StateSpace::StateType>(0);
    ob::RealVectorStateSpace::StateType& velocity = *s.as<ob::RealVectorStateSpace::StateType>(1);
    // ob::DiscreteStateSpace::StateType& gear = *s.as<ob::DiscreteStateSpace::StateType>(2);
    ob::TimeStateSpace::StateType& timeSpace = *s.as<ob::TimeStateSpace::StateType>(2);

    si->getStateSpace()->copyState(result, state);
    for(int i = 0; i < nsteps; i++)
    {
        se2.setX(se2.getX() + dt * velocity.values[0] * cos(se2.getYaw()));
        se2.setY(se2.getY() + dt * velocity.values[0] * sin(se2.getYaw()));
        se2.setYaw(se2.getYaw() + dt * u[0]);
        // velocity.values[0] = velocity.values[0] + dt * (u[1]*gear.value);
        velocity.values[0] = velocity.values[0] + dt * u[1];
        timeSpace.position = timeSpace.position + duration;

        // 'guards' - conditions to change gears
        // if (gear.value > 0)
        // {
        //     if (gear.value < 3 && velocity.values[0] > 10*(gear.value + 1))
        //         gear.value++;
        //     else if (gear.value > 1 && velocity.values[0] < 10*gear.value)
        //         gear.value--;
        // }

        if (!si->satisfiesBounds(result))
            return;
    }
}

bool canPropagateBackward ()
{
    return false;
}

// The free space consists of two narrow corridors connected at right angle.
// To make the turn, the car will have to downshift.
bool isStateValid(const oc::SpaceInformation *si, const ob::State *state)
{
    const ob::SE2StateSpace::StateType *se2 = state->as<ob::CompoundState>()->as<ob::SE2StateSpace::StateType>(0);
    const ob::TimeStateSpace::StateType *timeSpace = state->as<ob::CompoundState>()->as<ob::TimeStateSpace::StateType>(2);

    // check that time isnt used in the first x seconds
    // return si->satisfiesBounds(state) && (se2->getX() < -80. || se2->getY() > 80.);

    // check if bounds are satisfied
    if (si->satisfiesBounds(state)) {
        // is it in an occupied time?
        if (timeSpace->position > 30 && timeSpace->position < 70) {
            // check against larger borders
            if ((se2->getX() > -85. && se2->getX() < -15 && se2->getY() > -85. && se2->getY() < -15) ||
                (se2->getX() > -85. && se2->getX() < -15 && se2->getY() > 15. && se2->getY() < 85) ||
                (se2->getX() > 15. && se2->getX() < 85 && se2->getY() > 15. && se2->getY() < 85)) {
                    return false;
            } else {
                return true;
            }
        // not in occupied time
        } else {
            // check against regular borders
            if ((se2->getX() > -80. && se2->getX() < -20 && se2->getY() > -80. && se2->getY() < -20) ||
                (se2->getX() > -80. && se2->getX() < -20 && se2->getY() > 20. && se2->getY() < 80) ||
                (se2->getX() > 20. && se2->getX() < 80 && se2->getY() > 20. && se2->getY() < 80)) {
                return false;
            } else {
                return true;
            }
        }
    } else {
        return false;
    }
}

int main(int, char**)
{
    // plan for hybrid car in SE(2) with discrete gears
    ob::StateSpacePtr SE2(new ob::SE2StateSpace());
    ob::StateSpacePtr velocity(new ob::RealVectorStateSpace(1));
    ob::StateSpacePtr timeSpace(new ob::TimeStateSpace());
    // set the range for gears: [-1,3] inclusive
    // ob::StateSpacePtr gear(new ob::DiscreteStateSpace(-1,3));
    // ob::StateSpacePtr stateSpace = SE2 + velocity + gear;
    ob::StateSpacePtr stateSpace = SE2 + velocity + timeSpace;

    // state space for 2nd robot
    ob::StateSpacePtr SE22(new ob::SE2StateSpace());
    ob::StateSpacePtr velocity2(new ob::RealVectorStateSpace(1));
    ob::StateSpacePtr timeSpace2(new ob::TimeStateSpace());
    ob::StateSpacePtr stateSpace2 = SE2 + velocity + timeSpace;

    // set the bounds for the R^2 part of SE(2)
    ob::RealVectorBounds bounds(2);
    bounds.setLow(-100);
    bounds.setHigh(100);
    SE2->as<ob::SE2StateSpace>()->setBounds(bounds);

    // set bounds for robot 2
    SE22->as<ob::SE2StateSpace>()->setBounds(bounds);

    // set the bounds for the velocity
    ob::RealVectorBounds velocityBound(1);
    velocityBound.setLow(0);
    velocityBound.setHigh(60);
    velocity->as<ob::RealVectorStateSpace>()->setBounds(velocityBound);

    // set the bounds for robot 2
    velocity2->as<ob::RealVectorStateSpace>()->setBounds(velocityBound);

    // set the bounds for the time
    // currently not setting bounds, time can go forever
    timeSpace->as<ob::TimeStateSpace>()->setBounds(0., 100.);

    // create start and goal states
    ob::ScopedState<> start(stateSpace);
    ob::ScopedState<> goal(stateSpace);

    // Both start and goal are states with high velocity with the car in third gear.
    // However, to make the turn, the car cannot stay in third gear and will have to
    // shift to first gear.
    start[0] = 90.; // position
    start[1] = -90.;
    start[2] = boost::math::constants::pi<double>() / 2; // orientation
    start[3] = 40.; // velocity
    // start[4] = 0.; // time
    // start->as<ob::CompoundState>()->as<ob::DiscreteStateSpace::StateType>(2)->value = 3; // gear

    goal[0] = 90.; // position
    goal[1] = 90.;
    goal[2] = 0.; // orientation
    goal[3] = 40.; // velocity
    // goal[4] = 5.; // time
    // goal->as<ob::CompoundState>()->as<ob::DiscreteStateSpace::StateType>(2)->value = 3; // gear

    oc::ControlSpacePtr cmanifold(new oc::RealVectorControlSpace(stateSpace, 2));

    // set the bounds for the control manifold
    ob::RealVectorBounds cbounds(2);
    // bounds for steering input
    cbounds.setLow(0, -1.);
    cbounds.setHigh(0, 1.);
    // bounds for brake/gas input
    cbounds.setLow(1, -20.);
    cbounds.setHigh(1, 20.);
    cmanifold->as<oc::RealVectorControlSpace>()->setBounds(cbounds);

    oc::SimpleSetup setup(cmanifold);
    setup.setStartAndGoalStates(start, goal, 5.);
    setup.setStateValidityChecker(boost::bind(&isStateValid, setup.getSpaceInformation().get(), _1));
    setup.setStatePropagator(boost::bind(&propagate, setup.getSpaceInformation().get(), _1, _2, _3, _4));
    setup.getSpaceInformation()->setPropagationStepSize(.1);
    setup.getSpaceInformation()->setMinMaxControlDuration(2, 3);

    // try to solve the problem
    if (setup.solve(30))
    {
        // print the (approximate) solution path: print states along the path
        // and controls required to get from one state to the next
        oc::PathControl& path(setup.getSolutionPath());

        // print out full state on solution path
        // (format: x, y, theta, v, u0, u1, dt)
        cout << "X\tY\tA\tT\tV\tU0\tU1\n";
        for(unsigned int i = 0; i < path.getStateCount(); ++i)
        {
            const ob::State* state = path.getState(i);
            const ob::SE2StateSpace::StateType *se2 = state->as<ob::CompoundState>()->as<ob::SE2StateSpace::StateType>(0);
            const ob::RealVectorStateSpace::StateType *velocity = state->as<ob::CompoundState>()->as<ob::RealVectorStateSpace::StateType>(1);
            // const ob::DiscreteStateSpace::StateType *gear = state->as<ob::CompoundState>()->as<ob::DiscreteStateSpace::StateType>(2);
            const ob::TimeStateSpace::StateType *timeSpace = state->as<ob::CompoundState>()->as<ob::TimeStateSpace::StateType>(2);
            // cout << se2->getX() << ' ' << se2->getY() << ' ' << se2->getYaw() << ' ' << velocity->values[0] << ' ' << gear->value << ' ';
            // cout << se2->getX() << ' ' << se2->getY() << ' ' << se2->getYaw() << ' ' << velocity->values[0] << ' ' << timeSpace->position << ' ';
            cout << setprecision(2) << se2->getX() << '\t' << se2->getY() << '\t' << se2->getYaw() << '\t' << timeSpace->position << '\t' << velocity->values[0];

            if (i == 0)
                // null controls applied for zero seconds to get to start state
                cout << "0 0 0";
            else
            {
                // print controls and control duration needed to get from state i-1 to state i
                const double* u = path.getControl(i - 1)->as<oc::RealVectorControlSpace::ControlType>()->values;
                cout << u[0] << ' ' << u[1] << ' ' << path.getControlDuration(i - 1);
            }

            cout << endl;
        }

        if (!setup.haveExactSolutionPath())
        {
            cout << "Solution is approximate. Distance to actual goal is " << setup.getProblemDefinition()->getSolutionDifference() << endl;
        }
    }

    return 0;
}


/* test_arrays.cpp - Test Adept's array functionality

    Copyright (C) 2016-2018 European Centre for Medium-Range Weather Forecasts

    Author: Robin Hogan <r.j.hogan@ecmwf.int>

  Copying and distribution of this file, with or without modification,
  are permitted in any medium without royalty provided the copyright
  notice and this notice are preserved.  This file is offered as-is,
  without any warranty.

  This program can be compiled to run in three ways: (1) normal
  compilation tests inactive arrays, (2) with -DALL_ACTIVE tests
  active arrays, and (3) "-DALL_ACTIVE -DADEPT_RECORDING_PAUSABLE"
  tests whether a "paused" recording correctly records nothing to the
  automatic-differentiation stack.

*/

#include <iostream>
#include <complex>

#define ADEPT_BOUNDS_CHECKING 1

#include <adept_arrays.h>

// The following controls whether to use active variables or not
//#define ALL_ACTIVE 1
//#define MARVEL_STYLE 1
//#define ALL_COMPLEX 1

using namespace adept;

int
main(int argc, const char** argv) {
  using namespace adept;
#ifdef ALL_ACTIVE
#define IsActive true
  Stack stack;
#else
#define IsActive false
#endif

#define HEADING(MESSAGE)						\
  std::cout << "====================================================================\n" \
	    << "   TESTING " << MESSAGE << "\n"

#define COMMA ,

#define SIMPLE_EVAL(MESSAGE, TYPE, X, INIT, EXPR)			\
  std::cout << "--------------------------------------------------------------------\n" \
	    << "### " << MESSAGE << "\n### " << #EXPR << "\n";	\
  try {								\
    TYPE X;								\
    if (INIT) {								\
      X = test. X;							\
    }									\
    std::cout << "Evaluating " << #EXPR << "\n";			\
    std::cout.flush();						\
    EXPR;								\
    if (should_fail) { std::cout << "*** INCORRECT OUTCOME\n";	\
      anomalous_results++;						\
    }									\
  } catch (adept::exception e) {					\
    std::cout << "*** Failed with: " << e.what() << "\n";		\
    if (!should_fail) { std::cout << "*** INCORRECT OUTCOME\n";	\
      anomalous_results++;						\
    }									\
    else {								\
      std::cout << "*** Correct behaviour\n";				\
    }									\
  }

#define EVAL(MESSAGE, TYPE, X, INIT, EXPR)				\
  std::cout << "--------------------------------------------------------------------\n" \
	    << "### " << MESSAGE << "\n### " << #EXPR << "\n";	\
  try {								\
    TYPE X;								\
    if (INIT) {								\
      X = test. X;							\
      std::cout << #TYPE << " " << #X << " = " << X << "\n";		\
    }									\
    else {								\
      std::cout << #TYPE << " " << #X << " = " << X << "\n";		\
    }									\
    std::cout << "Evaluating " << #EXPR << "\n";			\
    std::cout.flush();						\
    EXPR;								\
    std::cout << "Result: " << #X << " = " << X << "\n";		\
    if (should_fail) { std::cout << "*** INCORRECT OUTCOME\n";	\
      anomalous_results++;						\
    }									\
  } catch (adept::exception e) {					\
    std::cout << "*** Failed with: " << e.what() << "\n";		\
    if (!should_fail) { std::cout << "*** INCORRECT OUTCOME\n";	\
      anomalous_results++;						\
    }									\
    else {								\
      std::cout << "*** Correct behaviour\n";				\
    }									\
  }

#define EVAL2(MESSAGE, TYPEX, X, INITX, TYPEY, Y, EXPR)			\
  std::cout << "--------------------------------------------------------------------\n" \
	    << "### " << MESSAGE << "\n### " << #EXPR << "\n";	\
  try {									\
    TYPEX X;								\
    if (INITX) {							\
      X = test. X;							\
      std::cout << #TYPEX << " " << #X << " = " << X << "\n";		\
    }									\
    else {								\
      std::cout << #TYPEX << " " << #X << " = " << X << "\n";		\
    }									\
    TYPEY Y; Y = test. Y;						\
    std::cout << #TYPEY << " " << #Y << " = " << Y << "\n";		\
    std::cout << "Evaluating " << #EXPR << "\n";			\
    std::cout.flush();							\
    EXPR;								\
    std::cout << "Result: " << #X << " = " << X << "\n";		\
    if (should_fail) { std::cout << "*** INCORRECT OUTCOME\n";	        \
      anomalous_results++;						\
    }									\
  } catch (adept::exception e) {					\
    std::cout << "*** Failed with: " << e.what() << "\n";		\
    if (!should_fail) { std::cout << "*** INCORRECT OUTCOME\n";		\
      anomalous_results++;						\
    }									\
    else {								\
      std::cout << "*** Correct behaviour\n";				\
    }									\
  }

#define EVAL3(MESSAGE, TYPEX, X, INITX, TYPEY, Y, TYPEZ, Z, EXPR)	\
  std::cout << "--------------------------------------------------------------------\n" \
	    << "### " << MESSAGE << "\n### " << #EXPR << "\n"; \
  try {									\
    TYPEX X;								\
    if (INITX) {							\
      X = test. X;							\
      std::cout << #TYPEX << " " << #X << " = " << X << "\n";		\
    }									\
    else {								\
      std::cout << #TYPEX << " " << #X << " = " << X << "\n";		\
    }									\
    TYPEY Y; Y.link( test. Y );						\
    TYPEZ Z; Z.link( test. Z );						\
    std::cout << #TYPEY << " " << #Y << " = " << Y << "\n";		\
    std::cout << #TYPEZ << " " << #Z << " = " << Z << "\n";		\
    std::cout << "Evaluating " << #EXPR << "\n";			\
    std::cout.flush();							\
    EXPR;								\
    std::cout << "Result: " << #X << " = " << X << "\n";		\
    if (should_fail) { std::cout << "*** INCORRECT OUTCOME\n";	        \
      anomalous_results++;						\
    }									\
  } catch (adept::exception e) {					\
    std::cout << "*** Failed with: " << e.what() << "\n";		\
    if (!should_fail) { std::cout << "*** INCORRECT OUTCOME\n";		\
      anomalous_results++;						\
    }									\
    else {								\
      std::cout << "*** Correct behaviour\n";				\
    }									\
  }

#define EVAL_NO_TRAP(MESSAGE, TYPE, X, INIT, EXPR)				\
  std::cout << "--------------------------------------------------------------------\n" \
	    << "### " << MESSAGE << "\n### " << #EXPR << "\n";	\
  {									\
    TYPE X;								\
    if (INIT) {								\
      X = test. X;							\
      std::cout << #TYPE << " " << #X << " = " << X << "\n";		\
    }									\
    else {								\
      std::cout << #TYPE << " " << #X << " = " << X << "\n";		\
    }									\
    std::cout << "Evaluating " << #EXPR << "\n";			\
    std::cout.flush();						\
    EXPR;								\
    std::cout << "Result: " << #X << " = " << X << "\n";		\
    if (should_fail) { std::cout << "*** INCORRECT OUTCOME\n";	\
      anomalous_results++;						\
    }									\
  }

#define EVAL2_NO_TRAP(MESSAGE, TYPEX, X, INITX, TYPEY, Y, EXPR)			\
  std::cout << "--------------------------------------------------------------------\n" \
	    << "### " << MESSAGE << "\n###  " << #EXPR << "\n";	\
  {									\
    TYPEX X;								\
    if (INITX) {								\
      X = test. X;							\
      std::cout << #TYPEX << " " << #X << " = " << X << "\n";		\
    }									\
    else {								\
      std::cout << #TYPEX << " " << #X << " = " << X << "\n";		\
    }									\
    TYPEY Y; Y = test. Y;						\
    std::cout << #TYPEY << " " << #Y << " = " << Y << "\n";		\
    std::cout << "Evaluating " << #EXPR << "\n";			\
    std::cout.flush();							\
    EXPR;								\
    std::cout << "Result: " << #X << " = " << X << "\n";		\
    if (should_fail) { std::cout << "*** INCORRECT OUTCOME\n";	\
      anomalous_results++;						\
    }									\
  }

#ifndef ALL_COMPLEX

#ifdef ALL_ACTIVE
#ifndef MARVEL_STYLE
  typedef aReal myReal;
  typedef aMatrix myMatrix;
  typedef aVector myVector;
  typedef aSymmMatrix mySymmMatrix;
  //typedef aSquareMatrix mySymmMatrix;
  typedef aDiagMatrix myDiagMatrix;
  typedef aTridiagMatrix myTridiagMatrix;
  typedef aLowerMatrix myLowerMatrix;
  typedef aUpperMatrix myUpperMatrix;
  typedef SpecialMatrix<Real,BandEngine<ROW_MAJOR,2,1>,true> myOddBandMatrix;
  typedef aArray3D myArray3D;
#else
  typedef aReal myReal;
  typedef Array<2,aReal,false> myMatrix;
  typedef Array<1,aReal,false> myVector;
  typedef SpecialMatrix<aReal,SquareEngine<ROW_MAJOR>,false> mySymmMatrix;
  typedef SpecialMatrix<aReal,BandEngine<ROW_MAJOR,0,0>,false> myDiagMatrix;
  typedef SpecialMatrix<aReal,BandEngine<ROW_MAJOR,1,1>,false> myTridiagMatrix;
  typedef SpecialMatrix<aReal,internal::LowerEngine<ROW_MAJOR>, false> myLowerMatrix;
  typedef SpecialMatrix<aReal,internal::UpperEngine<ROW_MAJOR>, false> myUpperMatrix;
  typedef SpecialMatrix<aReal,BandEngine<ROW_MAJOR,2,1>,false> myOddBandMatrix;

#endif
#else

  typedef Real   myReal;
  typedef Matrix myMatrix;
  typedef Vector myVector;
  typedef Array3D myArray3D;

  typedef SymmMatrix mySymmMatrix;
  //typedef SquareMatrix mySymmMatrix;
  typedef DiagMatrix myDiagMatrix;
  typedef TridiagMatrix myTridiagMatrix;
  typedef LowerMatrix myLowerMatrix;
  typedef UpperMatrix myUpperMatrix;
  typedef SpecialMatrix<Real,BandEngine<ROW_MAJOR,2,1>,false> myOddBandMatrix;

  /*
  typedef SpecialMatrix<Real,SymmEngine<ROW_UPPER_COL_LOWER>,false> mySymmMatrix;
  typedef SpecialMatrix<Real,BandEngine<COL_MAJOR,0,0>,false> myDiagMatrix;
  typedef SpecialMatrix<Real,BandEngine<COL_MAJOR,1,1>,false> myTridiagMatrix;
  typedef SpecialMatrix<Real,BandEngine<COL_MAJOR,2,1>,false> myOddBandMatrix;
  */

#endif

#else
  typedef std::complex<Real> myReal;
  typedef Array<1,std::complex<Real>,IsActive> myVector;
  typedef Array<2,std::complex<Real>,IsActive> myMatrix;
  typedef Array<3,std::complex<Real>,IsActive> myArray3D;
  typedef SpecialMatrix<std::complex<Real>,SquareEngine<ROW_MAJOR>,IsActive> mySymmMatrix;
  typedef SpecialMatrix<std::complex<Real>,BandEngine<ROW_MAJOR,0,0>,IsActive> myDiagMatrix;
  typedef SpecialMatrix<std::complex<Real>,BandEngine<ROW_MAJOR,1,1>,IsActive> myTridiagMatrix;
  typedef SpecialMatrix<std::complex<Real>,internal::LowerEngine<ROW_MAJOR>, IsActive> myLowerMatrix;
  typedef SpecialMatrix<std::complex<Real>,internal::UpperEngine<ROW_MAJOR>, IsActive> myUpperMatrix;
  typedef SpecialMatrix<std::complex<Real>,BandEngine<ROW_MAJOR,2,1>,IsActive> myOddBandMatrix;

#endif

  struct Test {

    bool b;
    boolVector B;
    int c;
    myReal x;
    myVector v, w, vlong;
    myMatrix M, N;
    myMatrix Mstrided;
    myMatrix S;
    mySymmMatrix O, P;
    myDiagMatrix D, E;
    myTridiagMatrix T, TT;
    myLowerMatrix L, LL;
    myUpperMatrix U, UU;
    myOddBandMatrix Q, R;
    intVector index;
    myArray3D A;

#define MINI_TEST
#ifdef MINI_TEST
#define DIM1 3
#define DIM2 2
#define DIM3 5
#define DIMLONG 12
#else
#define DIM1 12
#define DIM2 10
#define DIM3 15
#define DIMLONG 20
#endif
    Test() {
#ifdef ALL_COMPLEX
#define I std::complex<Real>(0.0,1.0)
#else
#define I 0.0
#endif
      b = false;
      B.resize(DIM1); B = false;
      c = 0;
      x = -2;
      v.resize(DIM1);
      vlong.resize(DIMLONG); vlong = linspace(1,DIMLONG,DIMLONG);
      w.resize(DIM1);
      M.resize(DIM2,DIM1);
      myMatrix Mtmp(DIM2*3,DIM1*2);
      Mstrided.link(Mtmp(stride(0,end,3),stride(0,end,2)));
      N.resize(DIM2,DIM1);
      S.resize(DIM1,DIM1);
      O.resize(DIM1);
      Q.resize(DIM3);
      index.resize(DIM2);
      v(0) = 2.0 + 3.0*I; v(1) = 3; v(2) = 5;
      w(0) = 7.0 + 4.0*I; w(1) = 11; w(2) = 13;
      M(0,0) = 2.0 + 3.0*I; M(0,1) = 3; M(0,2) = 5;
      M(1,0) = 7; M(1,1) = 11; M(1,2) = 13;
      Mstrided = M;
      N(0,0) = 17.0+5.0*I; N(0,1) = 19; N(0,2) = 23;
      N(1,0) = 29; N(1,1) = 31; N(1,2) = 37;
      S(0,0) = 2.0+3.0*I; S(0,1) = 3; S(0,2) = 5;
      S(1,0) = 7.0+4.0*I; S(1,1) = 11; S(1,2) = 13;
      S(2,0) = 17; S(2,1) = 19; S(2,2) = 23;

      O(0,0) = 7.0+3.0*I;
      O(1,0) = 2; O(1,1) = 11;
      O(2,0) = 3; O(2,1) = 5; O(2,2) = 13;

      P = 14.0 - O;

      Q.diag_vector(-2) = 1;
      Q.diag_vector(-1) = 2;
      Q.diag_vector(0)  = 3;
      Q.diag_vector(1)  = 4;

      D = S;
      T = S;
      L = S;
      U = S;

      A.resize(DIM2,DIM1,DIM2);
      A << 2.0+3.0*I, 3, 5, 7, 11, 13,
	17, 19, 23, 29, 31,37;

      index << 1, 0;
    }
  };

#ifdef ALL_ACTIVE
#ifndef ADEPT_RECORDING_PAUSABLE
  stack.new_recording();
#else
  stack.pause_recording();
#endif
#endif

  Test test;

  bool should_fail=false;
  int anomalous_results=0;

  std::cout << adept::configuration();

#ifdef ALL_ACTIVE
  std::cout << "Testing ACTIVE arrays\n";
#else
  std::cout << "Testing INACTIVE arrays\n";
#endif
#ifdef ALL_COMPLEX
  std::cout << "Testing COMPLEX arrays\n";
#endif

  HEADING("ARRAY FUNCTIONALITY");
  EVAL("Array \"resize\" member function", myMatrix, M, true, M.resize(1,5));

  should_fail=true;
  EVAL("Array \"resize\" with invalid dimensions", myMatrix, M, true, M.resize(1));
  should_fail=false;
  EVAL("Array \"resize\" with \"dimensions\" function", myMatrix, M, true, M.resize(dimensions(4,2)));

  EVAL("Array \"clear\" member function", myMatrix, M, true, M.clear());

#ifdef ADEPT_CXX11_FEATURES
  HEADING("INITIALIZER LISTS (C++11 ONLY)");
  EVAL("Vector assignment to initializer list from empty", myVector, v,
       false, v = {1 COMMA 2});
  EVAL("Vector assignment to initializer list with underfill", myVector, v,
       true, v = {1.0 COMMA 2.0});
  should_fail = true;
  EVAL("Vector assignment to initializer list with overfill (SHOULD FAIL)", myVector, v,
    true, v = {1.0 COMMA 2.0 COMMA 3.0 COMMA 4.0});
  should_fail = false;
  EVAL("Matrix assignment to initializer list from empty", myMatrix, M,
    false, M = { {1 COMMA 2} COMMA {3 COMMA 4} });
  EVAL("Matrix assignment to initializer list with underfill", myMatrix, M,
    true, M = { {1.0 COMMA 2.0} COMMA {3.0 COMMA 4.0} });
  should_fail = true;
  EVAL("Matrix assignment to initializer list with overfill (SHOULD FAIL)", myMatrix, M,
    true, M = { {1.0 COMMA 2.0 COMMA 3.0 COMMA 4.0} });
  should_fail = false;
  EVAL("Initializer list in expression", myVector, v,
    true, v = v + Vector({1.0 COMMA 2.0 COMMA 3.0}));
  EVAL2("Indexed matrix assigned to initializer list", myMatrix, M, true, intVector, index,
	M(index,index) = {{1 COMMA 2} COMMA {3 COMMA 4}});

#endif

  HEADING("BASIC EXPRESSIONS");
  EVAL2("Vector assignment to vector from empty", myVector, v, false, myVector, w, v = w);
  EVAL2("Vector assignment to expression from empty", myVector, v, false, myVector, w, v = log(w) + 1.0);

  /*
  should_fail=true;
  EVAL("Vector = operator from empty (SHOULD FAIL)", myVector, v, false, v = 1.0);
  EVAL("Vector += operator from empty (SHOULD FAIL)", myVector, v, false, v += 1.0);
  should_fail=false;
  */

  EVAL("Matrix *= operator", myMatrix, M, true, M *= 0.5);
  EVAL2("Matrix = scalar", myMatrix, M, true, myReal, x, M = x);
  EVAL2("Matrix = scalar expression", myMatrix, M, true, myReal, x, M = (10.0*x));
#ifndef ALL_COMPLEX
  HEADING("BASIC FUNCTIONS");
  EVAL2("max", myVector, v, true, myVector, w, v = max(v,w/3.0));
  EVAL2("min", myVector, v, true, myVector, w, v = min(v,w/3.0));
#endif

  HEADING("ARRAY SLICING");
  EVAL2("Array indexing rvalue", myReal, x, true, myMatrix, M, x = M(1,end-1));

  should_fail=true;
  EVAL2("Array indexing rvalue out of range (SHOULD FAIL)", myReal, x, true, myMatrix, M, x = M(1,3));
  should_fail=false;

  EVAL("Array indexing lvalue", myMatrix, M, true, M(1,end-1) *= -1.0);

  EVAL2("contiguous subarray rvalue", myVector, v, false, myMatrix, M, v = M(__,end));
  EVAL("contiguous subarray lvalue", myMatrix, M, true, M(end-1,__) /= 2.0);
  EVAL2("contiguous subarray rvalue and lvalue", myMatrix, M, true, myMatrix, N, M(__,1) = N(__,2));
  EVAL2("contiguous subarray rvalue using range", myVector, v, false, myMatrix, M, v = 2.0 * M(1,range(1,2)));
  EVAL2("contiguous subarray lvalue using range", myMatrix, M, true, myVector, v, M(end-1,range(0,1)) = log(v(range(1,2))));
  EVAL2("contiguous subarray rvalue using subset", myMatrix, M, false, myMatrix, N, M = 2.0 * N.subset(1,1,1,2));
  EVAL("contiguous subarray lvalue using subset", myVector, v, true, v.subset(end-1,end) *= 10.0);
  EVAL2("regular subarray rvalue", myVector, v, false, myVector, w, v = w(stride(end,0,-1)));
  EVAL2("regular subarray lvalue", myMatrix, M, true, myVector, w, M(0,stride(0,end,2)) *= w(stride(end,0,-2)));
#ifndef ALL_COMPLEX
  EVAL2("irregular subarray rvalue", myMatrix, M, false, myMatrix, N, M = N(stride(1,0,-1),find(N(0,__)>18)));
  EVAL("irregular subarray lvalue", myMatrix, M, true, M(stride(1,0,-1),find(M(0,__)>4)) = 0);
#endif
  EVAL("slice leading dimension", myMatrix, M, true, M[end] = 0);
  EVAL("slice two dimensions", myMatrix, M, true, M[end][0] = 0);
  EVAL2("diag_vector member function as rvalue", myVector, v, false, myMatrix, S, v = diag_vector(S,1));
  EVAL2("diag_vector member function as lvalue", myMatrix, S, true, myVector, v, S.diag_vector() += v);
  EVAL2("diag_matrix member function", myMatrix, S, false, myVector, v, S = v.diag_matrix());
  EVAL2("diag_matrix external function", myMatrix, S, false, myVector, v, S = diag_matrix(v));
  EVAL2("transpose as rvalue via T member function", myMatrix, N, false, myMatrix, M, N = 2.0 * M.T());
  EVAL2("transpose as rvalue via permute member function", myMatrix, N, false, myMatrix, M, N = 2.0 * M.permute(1,0));
  EVAL3("matrix indexing (scalar,non-contiguous)", myVector, v, false, myMatrix, N, intVector, index, v = N(1,index));
  EVAL3("matrix indexing (non-contiguous,scalar)", myVector, v, false, myMatrix, N, intVector, index, v = N(index,1));
  EVAL3("2D arbitrary index as rvalue", myMatrix, M, false, myMatrix, N, intVector, index, M = const_cast<const myMatrix&>(N)(index,index));
  EVAL3("2D arbitrary index as lvalue assigned to scalar expression", myMatrix, M, true, myMatrix, N, intVector, index, M(index,index) = 2.0*(myReal)(4.0));
  EVAL3("2D arbitrary index as lvalue", myMatrix, M, true, myMatrix, N, intVector, index, M(index,index) = N(__,range(1,2)));
  EVAL2("2D arbitrary index as lvalue with assign-multiply operator", myMatrix, M, true, intVector, index, M(index,index) *= 10.0);
  EVAL2("2D arbitrary index as lvalue with aliased right-hand-side", myMatrix, M, true, intVector, index, M(index,index) = M(__,range(0,1)));
  EVAL2("2D arbitrary index as lvalue with aliased right-hand-side and eval function", myMatrix, M, true, intVector, index, M(index,index) = eval(M(__,range(0,1))));
  EVAL2("reshape member function", myMatrix, M, false, myVector, vlong, M >>= vlong.reshape(3,4));
  should_fail=true;
  EVAL2("reshape member function with invalid dimensions", myMatrix, M, false, myVector, vlong, M >>= vlong.reshape(5,5));
  should_fail=false;
  EVAL("end/2 indexing", myVector, vlong, true, vlong(range(end/2,end)) = 0.0);
  EVAL("end/2 indexing", myVector, vlong, true, vlong(range(0,end/2)) = 0.0);
  EVAL("end/2 indexing", myVector, vlong, true, vlong.subset(end/2,end) = 0.0);

  HEADING("REDUCTION OPERATIONS");
  EVAL2("full sum", myReal, x, true, myMatrix, M, x = sum(M));
  EVAL2("full product", myReal, x, true, myMatrix, M, x = product(M));
#ifndef ALL_COMPLEX
  EVAL2("full maxval", myReal, x, true, myMatrix, M, x = maxval(M));
  EVAL2("full minval", myReal, x, true, myMatrix, M, x = minval(M));
#endif
  EVAL2("full norm2", myReal, x, true, myMatrix, M, x = norm2(M));
  EVAL2("1-dimension mean", myVector, v, false, myMatrix, M, v = 0.5 * mean(M,0));
  EVAL2("1-dimension norm2", myVector, v, false, myMatrix, M, v = norm2(M,1));
  EVAL2("dot product", myReal, x, true, myVector, w, x = dot_product(w,w(stride(end,0,-1))));
  EVAL2("dot product on expressions", myReal, x, true, myVector, w, x = dot_product(2.0*w,w(stride(end,0,-1))+1.0));
#ifndef ALL_COMPLEX
  EVAL2("1-dimension maxval", myVector, v, false, myMatrix, M, v = maxval(M,1));
  EVAL2("1-dimension minval", myVector, v, false, myMatrix, M, v = minval(M,1));
  EVAL2("1D interpolation", myVector, v, true, myVector, w, v = interp(value(v), w, Vector(value(w)/3.0)));
  EVAL2("1D interpolation", myVector, v, true, myVector, w, v = interp(value(v), w, value(w)/3.0));
  EVAL2("all reduction", bool, b, true, myMatrix, M, b = all(M > 8.0));
  EVAL2("any reduction", bool, b, true, myMatrix, M, b = any(M > 8.0));
  EVAL2("count reduction", int, c, true, myMatrix, M, c = count(M > 8.0));
  EVAL2("1-dimension all reduction", boolVector, B, false, myMatrix, M, B = all(M > 8.0, 1));
  EVAL2("1-dimension any reduction", boolVector, B, false, myMatrix, M, B = any(M > 8.0, 1));
  EVAL2("1-dimension count reduction", intVector, index, false, myMatrix, M, index = count(M > 8.0, 1));
  HEADING("CONDITIONAL OPERATIONS");
  EVAL2("where construct, scalar right-hand-side", myMatrix, M, true, myMatrix, N, M.where(N > 20) = 0);
  EVAL2("where construct, expression right-hand-side", myMatrix, M, true, myMatrix, N, M.where(N > 20) = -N);
  EVAL2("where construct, scalar either-or right-hand-side", myMatrix, M, true, myMatrix, N, M.where(N > 20) = either_or(0,1));
  EVAL2("where construct, expression either-or right-hand-side", myMatrix, M, true, myMatrix, N, M.where(N > 20) = either_or(-N,N));
  EVAL_NO_TRAP("find construct, scalar right-hand-side", myVector, v, true, v(find(v > 3.5)) = 0);
  EVAL("find construct, expression right-hand-side", myVector, v, true, v(find(v > 3.5)) = -v(range(end,end)));
  EVAL("find construct, multiply-assign right-hand-side", myVector, v, true, v(find(v != 5.0)) *= 10.0);
#endif
  HEADING("SPECIAL SQUARE MATRICES");
  EVAL("SymmMatrix \"resize\" member function", mySymmMatrix, O, true, O.resize(5));

  should_fail = true;
  EVAL("SymmMatrix \"resize\" with invalid dimensions", mySymmMatrix, O, true, O.resize(4,5));
  should_fail = false;

  EVAL("SymmMatrix \"clear\" member function", mySymmMatrix, O, true, O.clear());
  EVAL2("SymmMatrix assign from dense matrix", mySymmMatrix, O, false, myMatrix, S, O = S);
  EVAL2("DiagMatrix assign from dense matrix", myDiagMatrix, D, false, myMatrix, S, D = S);
  EVAL2("TridiagMatrix assign from dense matrix", myTridiagMatrix, T, false, myMatrix, S, T = S);
  EVAL2("LowerMatrix assign from dense matrix", myLowerMatrix, L, false, myMatrix, S, L = S);
  EVAL2("UpperMatrix assign from dense matrix", myUpperMatrix, U, false, myMatrix, S, U = S);
  EVAL("SymmMatrix += operator", mySymmMatrix, O, true, O += 3.0);
  EVAL("DiagMatrix += operator", myDiagMatrix, D, true, D += 3.0);
  EVAL("TridiagMatrix += operator", myTridiagMatrix, T, true, T += 3.0);
  EVAL("LowerMatrix += operator", myLowerMatrix, L, true, L += 3.0);
  EVAL("UpperMatrix += operator", myUpperMatrix, U, true, U += 3.0);
  EVAL2("SymmMatrix as rvalue", myMatrix, M, false, mySymmMatrix, O, M = O);
  EVAL2("DiagMatrix as rvalue", myMatrix, M, false, myDiagMatrix, D, M = D);
  EVAL2("TridiagMatrix as rvalue", myMatrix, M, false, myTridiagMatrix, T, M = T);
  EVAL2("LowerMatrix as rvalue", myMatrix, M, false, myLowerMatrix, L, M = L);
  EVAL2("UpperMatrix as rvalue", myMatrix, M, false, myUpperMatrix, U, M = U);
  EVAL("SymmMatrix assign from scalar expression", mySymmMatrix, O, true, O = 2.0*(myReal)(4.0));
  EVAL("UpperMatrix assign from scalar expression", myUpperMatrix, U, true, U = 2.0*(myReal)(4.0));

  EVAL("SymmMatrix diag_vector member function as lvalue (upper)", mySymmMatrix, O, true, O.diag_vector(1) = 0);
  EVAL("SymmMatrix diag_vector member function as lvalue (lower)", mySymmMatrix, O, true, O.diag_vector(-2) += 10.0);
  EVAL("DiagMatrix diag_vector member function as lvalue", myDiagMatrix, D, true, D.diag_vector() = 0.0);

  should_fail = true;
  EVAL("DiagMatrix diag_vector member function incorrectly using offdiagonal", myDiagMatrix, D, true, D.diag_vector(1) = 0.0);
  should_fail = false;

  EVAL("TridiagMatrix diag_vector member function as lvalue (upper)", myTridiagMatrix, T, true, T.diag_vector(1) += 10.0);
  EVAL("TridiagMatrix diag_vector member function as lvalue (lower)", myTridiagMatrix, T, true, T.diag_vector(-1) = 0.0);
  EVAL("LowerMatrix diag_vector member function as lvalue (lower)", myLowerMatrix, L, true, L.diag_vector(-1) = 0.0);

  should_fail = true;
  EVAL("LowerMatrix diag_vector member function as lvalue (upper)", myLowerMatrix, L, true, L.diag_vector(1) = 0.0);
  EVAL("UpperMatrix diag_vector member function as lvalue (lower)", myUpperMatrix, U, true, U.diag_vector(-1) = 0.0);
  should_fail = false;

  EVAL("UpperMatrix diag_vector member function as lvalue (upper)", myUpperMatrix, U, true, U.diag_vector(1) = 0.0);
  EVAL("Odd band matrix \"diag_vector\" member function", myOddBandMatrix, Q, true, Q.diag_vector(1) = -1.0);
  EVAL("Odd band matrix \"diag_vector\" member function", myOddBandMatrix, Q, true, Q.diag_vector(0) = -1.0);
  EVAL("Odd band matrix \"diag_vector\" member function", myOddBandMatrix, Q, true, Q.diag_vector(-1) = -1.0);
  EVAL("Odd band matrix \"diag_vector\" member function", myOddBandMatrix, Q, true, Q.diag_vector(-2) = -1.0);

  EVAL2("Array submatrix_on_diagonal member function", myMatrix, M, false, myMatrix, S, M = S.submatrix_on_diagonal(1,2));
  EVAL("Array submatrix_on_diagonal member function as lvalue", myMatrix, S, true, S.submatrix_on_diagonal(0,1) = 0.0);

  should_fail = true;
  EVAL2("Array submatrix_on_diagonal member function to non-square matrix", myMatrix, M, false, myMatrix, N, M = N.submatrix_on_diagonal(1,2));
  should_fail = false;

  EVAL2("SymmMatrix submatrix_on_diagonal member function", mySymmMatrix, P, false, mySymmMatrix, O, P = O.submatrix_on_diagonal(1,2));
  EVAL2("DiagMatrix submatrix_on_diagonal member function", myDiagMatrix, E, false, myDiagMatrix, D, E = D.submatrix_on_diagonal(1,2));
  EVAL2("TridiagMatrix submatrix_on_diagonal member function", myTridiagMatrix, TT, false, myTridiagMatrix, T, TT = T.submatrix_on_diagonal(1,2));
  EVAL2("LowerMatrix submatrix_on_diagonal member function", myLowerMatrix, LL, false, myLowerMatrix, L, LL = L.submatrix_on_diagonal(1,2));
  EVAL2("UpperMatrix submatrix_on_diagonal member function", myUpperMatrix, UU, false, myUpperMatrix, U, UU = U.submatrix_on_diagonal(1,2));
  EVAL2("Odd band matrix submatrix_on_diagonal member function", myOddBandMatrix, R, false, myOddBandMatrix, Q, R = Q.submatrix_on_diagonal(1,3));
  EVAL("Odd band matrix submatrix_on_diagonal as lvalue", myOddBandMatrix, Q, true, Q.submatrix_on_diagonal(1,3) = -1);
  EVAL2("SymmMatrix transpose as rvalue via T member function", mySymmMatrix, P, false, mySymmMatrix, O, P = O.T());
  EVAL2("DiagMatrix transpose as rvalue via T member function", myDiagMatrix, E, false, myDiagMatrix, D, E = D.T());
  EVAL2("TridiagMatrix transpose as rvalue via T member function", myTridiagMatrix, TT, false, myTridiagMatrix, T, TT = T.T());
  EVAL2("LowerMatrix transpose as rvalue via T member function", myUpperMatrix, U, false, myLowerMatrix, L, U = L.T());
  EVAL2("UpperMatrix transpose as rvalue via T member function", myLowerMatrix, L, false, myUpperMatrix, U, L = U.T());

  HEADING("EXPANSION OPERATIONS");
  EVAL2("Outer product", myMatrix, M, false, myVector, v, M = outer_product(v,v));
  EVAL2("Outer product on indexed array", myMatrix, M, false, myVector, v, M = outer_product(v,v(stride(end,0,-1))));
  EVAL2("Outer product on expressions", myMatrix, M, false, myVector, v, M = outer_product(2.0*v,v-1.0));
  EVAL2("Vector spread of dimension 0", myMatrix, M, false, myVector, v, M = spread<0>(v,2));
  EVAL2("Vector spread of dimension 1", myMatrix, M, false, myVector, v, M = spread<1>(v,2));
  EVAL2("Vector spread with expression argument", myMatrix, M, false, myVector, v, M = spread<1>(v*2.0,2));
  EVAL2("Matrix spread of dimension 0", myArray3D, A, false, myMatrix, M, A = spread<0>(M,2));
  EVAL2("Matrix spread of dimension 1", myArray3D, A, false, myMatrix, M, A = spread<1>(M,2));
  EVAL2("Matrix spread of dimension 2", myArray3D, A, false, myMatrix, M, A = spread<2>(M,2));

#ifndef ALL_COMPLEX

#ifndef MARVEL_STYLE
  if (adept::have_matrix_multiplication()) {
    HEADING("MATRIX MULTIPLICATION");
    EVAL3("Matrix-Vector multiplication", myVector, w, false, myMatrix, M, myVector, v, w = M ** v);
    EVAL3("Matrix-Vector multiplication with strided matrix", myVector, w, false, myMatrix, Mstrided, myVector, v, w = Mstrided ** v);
    EVAL2("Matrix-Matrix multiplication", myMatrix, M, false, myMatrix, N, M = N.T() ** N);
    EVAL2("Matrix-Matrix multiplication with matmul", myMatrix, M, false, myMatrix, N, M = matmul(N.T(), N));

    should_fail = true;
    EVAL2("Matrix-Matrix multiplication with inner dimension mismatch", myMatrix, M, false, myMatrix, N, M = N ** N);
    should_fail = false;

    // TESTING!
    EVAL2("Matrix-Matrix-Vector multiplication", myVector, v, true, myMatrix, S, v = S ** S ** v);

    EVAL2("Matrix-Matrix-Vector multiplication", myVector, v, false, myMatrix, S, v = S ** log(S) ** S(0,__));
    EVAL2("Vector-Matrix multiplication", myVector, v, true, myMatrix, S, v = v ** S);
    EVAL2("Vector-Matrix multiplication with matmul", myVector, v, true, myMatrix, S, v = matmul(v, S));
    EVAL2("SymmMatrix-Vector multiplication", myVector, v, true, mySymmMatrix, O, v = O ** v);
    EVAL2("SymmMatrix-Matrix multiplication", myMatrix, S, true, mySymmMatrix, O, S = O ** S);
    EVAL2("Vector-SymmMatrix multiplication", myVector, v, true, mySymmMatrix, O, v = v ** O);
    EVAL2("Matrix-SymmMatrix multiplication", myMatrix, M, true, mySymmMatrix, O, M = M ** O);
    EVAL2("DiagMatrix-Vector multiplication", myVector, v, true, myDiagMatrix, D, v = D ** v);
    EVAL2("TridiagMatrix-Vector multiplication", myVector, v, true, myTridiagMatrix, T, v = T ** v);
    EVAL2("TridiagMatrix-Matrix multiplication", myMatrix, S, true, myTridiagMatrix, T, S = T ** S);

    EVAL2("LowerMatrix-Matrix multiplication", myMatrix, S, true, myLowerMatrix, L, S = L ** S);

    EVAL2("Vector-TridiagMatrix multiplication", myVector, v, true, myTridiagMatrix, T, v = v ** T);
    EVAL2("Matrix-TridiagMatrix multiplication", myMatrix, M, true, myTridiagMatrix, T, M = M ** T);
  }
  else {
    std::cout << "NO MATRIX MULTIPLICATION TESTS PERFORMED BECAUSE ADEPT COMPILED WITHOUT LAPACK\n";
  }

#ifndef ALL_ACTIVE
  if (adept::have_linear_algebra()) {
    HEADING("LINEAR ALGEBRA");
    EVAL2("Solving general linear equations Ax=b", myVector, v, true, myMatrix, S, v = solve(S,v));
    EVAL2("Solving general linear equations Ax=b with expression arguments", myVector, v, true, myMatrix, S, v = solve(S,2*v));

    EVAL2("Solving general linear equations AX=B", myMatrix, M, true, myMatrix, S, M.T() = solve(S,M.T()));
    EVAL2("Solving general linear equations AX=B with expression arguments", myMatrix, M, true, myMatrix, S, M.T() = solve(2.0 * S,2.0 * M.T()));
    EVAL2("Solving linear equations Ax=b with symmetric A", myVector, v, true, mySymmMatrix, O, v = solve(O,v));
    EVAL2("Solving linear equations AX=B with symmetric A", myMatrix, M, true, mySymmMatrix, O, M.T() = solve(O,M.T()));
    EVAL3("Solving linear equations AX=B with symmetric A and B", myMatrix, S, false, mySymmMatrix, O, mySymmMatrix, P, S = solve(O,P));
    EVAL2("Solving linear equations Ax=b with upper-triangular A", myVector, v, true, myUpperMatrix, U, v = solve(U,v));
    EVAL2("Invert general matrix", myMatrix, M, false, myMatrix, S, M = inv(S));
    EVAL2("Invert symmetric matrix", mySymmMatrix, P, false, mySymmMatrix, O, P = inv(O));
  }
  else {
    std::cout << "NO LINEAR ALGEBRA TESTS PERFORMED BECAUSE ADEPT COMPILED WITHOUT LAPACK\n";
  }
#else
  std::cout << "NO LINEAR ALGEBRA TESTS PERFORMED BECAUSE ACTIVE ARRAYS NOT YET SUPPORTED\n";
#endif
#else
  std::cout << "NO MATRIX TESTS PERFORMED BECAUSE USING MARVEL-STYLE ACTIVE ARRAYS\n";
#endif

#endif

  HEADING("FILLING ARRAYS");
  EVAL("Fill vector with \"<<\"", myVector, v, true, (v << 0.1, 0.2));

  should_fail = true;
  EVAL("Overfill vector with \"<<\"", myVector, v, true, (v << 0.1, 0.2, 0.3, 0.4));
  should_fail = false;

  EVAL("Underfill matrix with \"<<\"", myMatrix, M, true, (M << 0.1, 0.2, 0.3, 0.4, 0.5));
  EVAL("Fill matrix with \"<<\"", myMatrix, M, true, (M << 0.1, 0.2, 0.3, 0.4, 0.5, 0.6));

  should_fail = true;
  EVAL("Overfill matrix with \"<<\"", myMatrix, M, true, (M << 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0));
  should_fail = false;

  EVAL("Fill vector with vectors using \"<<\"", myVector, v, true, v << v(range(1,2)) << 0.1);
  EVAL2("Fill matrix with vector using \"<<\"", myMatrix, M, true, myVector, v, M << 0.1 << 0.2 << 0.3 << v);
  EVAL2("Fill matrix with vector using \"<<\"", myMatrix, S, true, myVector, v, S << v << v << v);
  EVAL("Assign array using range", myVector, v, false, v = range(3,6));

  HEADING("PRINTING WITH PLAIN STYLE");
  adept::set_array_print_style(PRINT_STYLE_PLAIN);
  SIMPLE_EVAL("Printing empty vector", myVector, v, false, std::cout << v << '\n');
  SIMPLE_EVAL("Printing vector", myVector, v, true, std::cout << v << '\n');
  SIMPLE_EVAL("Printing matrix", myMatrix, M, true, std::cout << M << '\n');
  SIMPLE_EVAL("Printing 3D array", myArray3D, A, true, std::cout << A << '\n');

  HEADING("PRINTING WITH CSV STYLE");
  adept::set_array_print_style(PRINT_STYLE_CSV);
  SIMPLE_EVAL("Printing empty vector", myVector, v, false, std::cout << v << '\n');
  SIMPLE_EVAL("Printing vector", myVector, v, true, std::cout << v << '\n');
  SIMPLE_EVAL("Printing matrix", myMatrix, M, true, std::cout << M << '\n');
  SIMPLE_EVAL("Printing 3D array", myArray3D, A, true, std::cout << A << '\n');

  HEADING("PRINTING WITH CURLY STYLE");
  adept::set_array_print_style(PRINT_STYLE_CURLY);
  SIMPLE_EVAL("Printing empty vector", myVector, v, false, std::cout << v << '\n');
  SIMPLE_EVAL("Printing vector", myVector, v, true, std::cout << v << '\n');
  SIMPLE_EVAL("Printing matrix", myMatrix, M, true, std::cout << M << '\n');
  SIMPLE_EVAL("Printing 3D array", myArray3D, A, true, std::cout << A << '\n');

  HEADING("PRINTING WITH MATLAB STYLE");
  adept::set_array_print_style(PRINT_STYLE_MATLAB);
  SIMPLE_EVAL("Printing empty vector", myVector, v, false, std::cout << v << '\n');
  SIMPLE_EVAL("Printing vector", myVector, v, true, std::cout << v << '\n');
  SIMPLE_EVAL("Printing matrix", myMatrix, M, true, std::cout << M << '\n');
  SIMPLE_EVAL("Printing 3D array", myArray3D, A, true, std::cout << A << '\n');
  adept::set_array_print_style(PRINT_STYLE_CURLY);

  HEADING("EXPRESSION PRINTING");
  EVAL("Send expression to standard output", myMatrix, M, true,
       std::cout << M(0,__) + M(1,__) << '\n');
  EVAL("Send scalar expression to standard output", myVector, v, true,
       std::cout << v(0) + v(1) << '\n');

#ifdef ADEPT_BOUNDS_CHECKING
  HEADING("BOUNDS CHECKING");
  should_fail = true;
  EVAL("Access vector out of bounds", myVector, v, true, v(0) = v(4));
  EVAL("Access vector out of bounds", myVector, v, true, v(0) = v(end-4));
  EVAL("Access matrix out of bounds", myMatrix, M, true, M(0,0) = M(0,-1));
  EVAL("Access matrix out of bounds", myMatrix, M, true, M(0,0) = M(end+1,1));
  should_fail = false;
#endif

  std::cout << "====================================================================\n";
#ifdef ALL_ACTIVE
  std::cout << stack;
  std::cout << "====================================================================\n";
#endif

  if (anomalous_results > 0) {
    std::cout << "*** In terms of run-time errors, there were " << anomalous_results << " incorrect results\n";
  }
  else {
    std::cout << "In terms of run-time errors, all tests were passed\n";
  }

#ifdef ALL_ACTIVE
#ifdef ADEPT_RECORDING_PAUSABLE
  if (stack.n_statements() > 1) {
    std::cout << "*** Stack contains " << stack.n_statements()-1
	      << " statements and " << stack.n_operations()
	      << " operations but both should be 0 because recording has been paused\n";
    return 1;
  }
#endif
#endif
  if (anomalous_results > 0) {
    return 1;
  }
  else {
    return 0;
  }
}


/*
@copyright Louis Dionne 2015
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/hana.hpp>

#include <test/seq.hpp>

#include <boost/hana/bool.hpp>
#include <boost/hana/functional/always.hpp>
#include <boost/hana/tuple.hpp>

#include <laws/applicative.hpp>
#include <laws/base.hpp>
#include <laws/comparable.hpp>
#include <laws/foldable.hpp>
#include <laws/functor.hpp>
#include <laws/iterable.hpp>
#include <laws/monad.hpp>
#include <laws/monad_plus.hpp>
#include <laws/orderable.hpp>
#include <laws/searchable.hpp>
#include <laws/sequence.hpp>
using namespace boost::hana;

int main() {
    using boost::hana::size_t;
    using test::ct_eq;
    using test::ct_ord;

    auto eqs = make<tuple_tag>(
          test::seq()
        , test::seq(ct_eq<0>{})
        , test::seq(ct_eq<0>{}, ct_eq<1>{})
        , test::seq(ct_eq<0>{}, ct_eq<1>{}, ct_eq<2>{})
        , test::seq(ct_eq<0>{}, ct_eq<1>{}, ct_eq<2>{}, ct_eq<3>{})
    );
    (void)eqs;

    auto nested_eqs = make<tuple_tag>(
          test::seq()
        , test::seq(
            test::seq(ct_eq<0>{}))
        , test::seq(
            test::seq(ct_eq<0>{}),
            test::seq(ct_eq<1>{}, ct_eq<2>{}))
        , test::seq(
            test::seq(ct_eq<0>{}),
            test::seq(ct_eq<1>{}, ct_eq<2>{}),
            test::seq(ct_eq<3>{}, ct_eq<4>{}))
    );
    (void)nested_eqs;

    auto eq_keys = make<tuple_tag>(ct_eq<0>{}, ct_eq<3>{}, ct_eq<10>{});
    (void)eq_keys;

    auto predicates = make<tuple_tag>(
        equal.to(ct_eq<0>{}), equal.to(ct_eq<3>{}), equal.to(ct_eq<10>{}),
        always(true_c), always(false_c)
    );
    (void)predicates;

    auto ords = make<tuple_tag>(
          test::seq()
        , test::seq(ct_ord<0>{})
        , test::seq(ct_ord<0>{}, ct_ord<1>{})
        , test::seq(ct_ord<0>{}, ct_ord<1>{}, ct_ord<2>{})
        , test::seq(ct_ord<0>{}, ct_ord<1>{}, ct_ord<2>{}, ct_ord<3>{})
    );
    (void)ords;

    //////////////////////////////////////////////////////////////////////////
    // Comparable, Orderable
    //////////////////////////////////////////////////////////////////////////
#ifdef BOOST_HANA_TEST_ORDERABLE
    test::TestComparable<test::Seq>{eqs};
    test::TestOrderable<test::Seq>{ords};
#endif

#ifdef BOOST_HANA_TEST_ITERABLE
    //////////////////////////////////////////////////////////////////////////
    // Foldable
    //////////////////////////////////////////////////////////////////////////
    test::TestFoldable<test::Seq>{eqs};

    //////////////////////////////////////////////////////////////////////////
    // Iterable
    //////////////////////////////////////////////////////////////////////////
    {
        test::TestIterable<test::Seq>{eqs};
    }
#endif

    //////////////////////////////////////////////////////////////////////////
    // Searchable
    //////////////////////////////////////////////////////////////////////////
#ifdef BOOST_HANA_TEST_SEARCHABLE
    {
        test::TestSearchable<test::Seq>{eqs, eq_keys};

        auto bools = make<tuple_tag>(
              test::seq(true_c)
            , test::seq(false_c)
            , test::seq(true_c, true_c)
            , test::seq(true_c, false_c)
            , test::seq(false_c, true_c)
            , test::seq(false_c, false_c)
        );
        test::TestSearchable<test::Seq>{bools, make<tuple_tag>(true_c, false_c)};
    }
#endif

    //////////////////////////////////////////////////////////////////////////
    // Functor, Applicative, Monad
    //////////////////////////////////////////////////////////////////////////
#ifdef BOOST_HANA_TEST_MONAD
    test::TestFunctor<test::Seq>{eqs, eq_keys};
    test::TestApplicative<test::Seq>{eqs};
    test::TestMonad<test::Seq>{eqs, nested_eqs};
#endif

    //////////////////////////////////////////////////////////////////////////
    // MonadPlus
    //////////////////////////////////////////////////////////////////////////
#ifdef BOOST_HANA_TEST_MONAD_PLUS
    test::TestMonadPlus<test::Seq>{eqs, predicates, eq_keys};
#endif

    //////////////////////////////////////////////////////////////////////////
    // Sequence
    //////////////////////////////////////////////////////////////////////////
#ifdef BOOST_HANA_TEST_SEQUENCE
    test::TestSequence<test::Seq>{};
#endif
}


#include <boost/math/special_functions.hpp>


// Copyright (c) 2015 The Prux Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <boost/random/uniform_int.hpp>
#include <boost/random/mersenne_twister.hpp>

#include "policy/policy.h"
#include "arith_uint256.h"
#include "prux.h"
#include "txmempool.h"
#include "util.h"
#include "validation.h"

unsigned int CalculatePruxNextWorkRequired(const CBlockIndex* pindexLast, int64_t nFirstBlockTime, const Consensus::Params& params)
{
int64_t nActualTimespan = pindexLast->GetBlockTime() - nFirstBlockTime;
int64_t nReTargetHistoryFact = 6;
int64_t nTargetTimespan = 15 * 60; // NyanCoin: 3 hours

        if (pindexLast->nHeight >= 7770000) {
        nReTargetHistoryFact = 2;
        nTargetTimespan = 15 * 60;
    } else if (pindexLast->nHeight >= 7331700) {
        nReTargetHistoryFact = 6;
        nTargetTimespan = 5 * 60 * 60;
    } else {
        nReTargetHistoryFact = 2;
        nTargetTimespan = 60 * 6;
    }

   if (pindexLast->nHeight > 15000)
        // obtain average actual timespan
        nActualTimespan = (pindexLast->GetBlockTime() - nFirstBlockTime)/nReTargetHistoryFact;
    else
        nActualTimespan = pindexLast->GetBlockTime() - nFirstBlockTime;

    if (nActualTimespan < nTargetTimespan/4)
        nActualTimespan = nTargetTimespan/4;
    if (nActualTimespan > nTargetTimespan*4)
        nActualTimespan = nTargetTimespan*4;

    // Retarget
    arith_uint256 bnNew;
    bnNew.SetCompact(pindexLast->nBits);
    bnNew *= nActualTimespan;
    bnNew /= nTargetTimespan;

const arith_uint256 bnPowLimit = UintToArith256(params.powLimit);
if (bnNew > bnPowLimit)
    bnNew = bnPowLimit;

return bnNew.GetCompact();
}

bool CheckAuxPowProofOfWork(const CBlockHeader& block, const Consensus::Params& params)
{
    /* Except for legacy blocks with full version 1, ensure that
       the chain ID is correct.  Legacy blocks are not allowed since
       the merge-mining start, which is checked in AcceptBlockHeader
       where the height is known.  */
    if (!block.IsLegacy() && params.fStrictChainId && block.GetChainId() != params.nAuxpowChainId)
        return error("%s : block does not have our chain ID"
                     " (got %d, expected %d, full nVersion %d)",
                     __func__, block.GetChainId(),
                     params.nAuxpowChainId, block.nVersion);

    /* If there is no auxpow, just check the block hash.  */
    if (!block.auxpow) {
        if (block.IsAuxpow())
            return error("%s : no auxpow on block with auxpow version",
                         __func__);

        if (!CheckProofOfWork(block.GetPoWHash(), block.nBits, params))
            return error("%s : non-AUX proof of work failed", __func__);

        return true;
    }

    /* We have auxpow.  Check it.  */

    if (!block.IsAuxpow())
        return error("%s : auxpow on block with non-auxpow version", __func__);

    if (!block.auxpow->check(block.GetHash(), block.GetChainId(), params))
        return error("%s : AUX POW is not valid", __func__);
    if (!CheckProofOfWork(block.auxpow->getParentBlockPoWHash(), block.nBits, params))
        return error("%s : AUX proof of work failed", __func__);

    return true;
}

CAmount GetPruxBlockSubsidy(int nHeight, const Consensus::Params& consensusParams, uint256 prevHash)
{

    CAmount nSubsidy = 0.009595 * COIN;

    // Subsidy is cut in half every 210,000 blocks which will occur approximately every 4 years.
    nSubsidy >>= (nHeight / 5959595);
    return nSubsidy;

}

CAmount GetPruxMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowFree)
{
    {
        LOCK(mempool.cs);
        uint256 hash = tx.GetHash();
        double dPriorityDelta = 0;
        CAmount nFeeDelta = 0;
        mempool.ApplyDeltas(hash, dPriorityDelta, nFeeDelta);
        if (dPriorityDelta > 0 || nFeeDelta > 0)
            return 0;
    }

    CAmount nMinFee = ::minRelayTxFee.GetFee(nBytes);
    nMinFee += GetPruxDustFee(tx.vout, ::minRelayTxFee);

    if (fAllowFree)
    {
        // There is a free transaction area in blocks created by most miners,
        // * If we are relaying we allow transactions up to DEFAULT_BLOCK_PRIORITY_SIZE - 1000
        //   to be considered to fall into this category. We don't want to encourage sending
        //   multiple transactions instead of one big transaction to avoid fees.
        if (nBytes < (DEFAULT_BLOCK_PRIORITY_SIZE - 1000))
            nMinFee = 0;
    }

    if (!MoneyRange(nMinFee))
        nMinFee = MAX_MONEY;
    return nMinFee;
}

CAmount GetPruxDustFee(const std::vector<CTxOut> &vout, CFeeRate &baseFeeRate) {
    CAmount nFee = 0;

    // To limit dust spam, add base fee for each output less than a COIN
    BOOST_FOREACH(const CTxOut& txout, vout)
        if (txout.IsDust(::minRelayTxFee))
            nFee += baseFeeRate.GetFeePerK();

    return nFee;
}


#include <chrono>
#include <cmath>
#include <Eigen/Dense>
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include <random>
#include <sstream>
#include <turbotrack.hpp>

#include "shader.hpp"

int nr_vertices;

#ifdef __EMSCRIPTEN__
	#include <emscripten.h>
#endif

// void GLAPIENTRY MessageCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length,
// 	const GLchar* message, const void* userParam) {
// 	//if (type != GL_DEBUG_TYPE_ERROR) return;
// 	fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n",
// 		(type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""),
// 		type, severity, message);
// }
// glEnable(GL_DEBUG_OUTPUT);
// glDebugMessageCallback(MessageCallback, 0);

void error_callback(int /* error */, const char* description) {
	std::cerr << "Error: " << description << '\n';
}

void resize_callback(GLFWwindow*, int width, int height) {
	glViewport(0, 0, width, height);
}

void key_callback(GLFWwindow* window, int key, int /* scancode */, int action, int /* mods */) {
	if (action == GLFW_PRESS) {
		switch (key) {
		case GLFW_KEY_ESCAPE:
		case GLFW_KEY_Q:
			glfwSetWindowShouldClose(window, true);
			break;
		}
	}
}

bool mouse_down = false;
Eigen::Vector2f last_mouse_pos;
bool last_mouse_pos_set = false;
Eigen::Quaternionf current_rot;
void mouse_button_callback(GLFWwindow* window, int button, int action, int mods) {
	if (button == 0) {
		switch (action) {
		case GLFW_PRESS: {
			mouse_down = true;
			last_mouse_pos_set = false;
			break;}
		case GLFW_RELEASE: {
			mouse_down = false;
			break;}
		}
	}
}

void cursor_pos_callback(GLFWwindow* window, double xpos, double ypos) {
	int width, height;
	glfwGetWindowSize(window, &width, &height);

	int mindim = std::min(width, height);

	float x = 2 * (xpos - width  / 2) / mindim;
	float y = - 2 * (ypos - height / 2) / mindim;

	if (mouse_down) {
		Eigen::Vector2f new_mouse_pos(x, y);

		if (last_mouse_pos_set) {
			current_rot = turbotrack::mouse_move(last_mouse_pos, new_mouse_pos) * current_rot;
		}

		last_mouse_pos = new_mouse_pos;
		last_mouse_pos_set = true;
	}
}

void process_input(GLFWwindow* /* window */) {
}

// state
GLFWwindow* window;
unsigned int VAO;
ShaderProgram shader_program;
int viewmat_location, projmat_location;

const int fps_nr_frames_to_average = 100;
int fps_nr_frame_since_last = fps_nr_frames_to_average;
std::chrono::steady_clock::time_point fps_last_time;

Eigen::Matrix4f projection(float aspect, float near, float far, float d) {
	const float nmf = near - far;

	Eigen::Matrix4f m;
	m <<
		d / aspect, 0,                   0,                     0,
		         0, d,                   0,                     0,
		         0, 0,  (near + far) / nmf,  2 * near * far / nmf,
		         0, 0,                  -1,                     0;

	return m;
}

void iteration() {
	// if (fps_nr_frame_since_last >= fps_nr_frames_to_average) {
	// 	const auto now = std::chrono::steady_clock::now();
	// 	std::cout << 1. / (std::chrono::duration_cast<std::chrono::duration<double>>(now - fps_last_time).count() / fps_nr_frame_since_last) << "fps\n";

	// 	fps_last_time = now;
	// 	fps_nr_frame_since_last = 0;
	// }
	// ++fps_nr_frame_since_last;

	process_input(window);

	Eigen::Matrix4f viewmat;
	viewmat <<
		current_rot.toRotationMatrix(), Eigen::Vector3f::Zero(),
		0, 0, 0, 1;

	viewmat(2,3) = -2;

	glUniformMatrix4fv(viewmat_location, 1, GL_FALSE, viewmat.data());

	int width, height;
	glfwGetWindowSize(window, &width, &height);

	Eigen::Matrix4f projmat = projection((float)width / height, 0.01, 100, 2.);
	glUniformMatrix4fv(projmat_location, 1, GL_FALSE, projmat.data());

	glClearColor(0.098f, 0.086f, 0.023f, 1.0f);
	glClear(GL_COLOR_BUFFER_BIT);

	shader_program.use();
	glBindVertexArray(VAO);
	glDrawArrays(GL_POINTS, 0, nr_vertices);

	glfwSwapBuffers(window);
	glfwPollEvents();
}

const int window_init_width = 1000;
const int window_init_height = 800;

int main() {
	glfwInit();
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
	glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);

	glfwSetErrorCallback(error_callback);

	window = glfwCreateWindow(window_init_width, window_init_height, "points", NULL, NULL);
	if (window == NULL) {
		std::cerr << "Failed to create GLFW window\n";
		glfwTerminate();
		return -1;
	}
	glfwMakeContextCurrent(window);

	if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
		std::cerr << "Failed to initialize GLAD\n";
		return -1;
	}

	current_rot = Eigen::Quaternionf(1, 0, 0, 0);

	glViewport(0, 0, window_init_width, window_init_height);
	glfwSetFramebufferSizeCallback(window, resize_callback);
	glfwSetKeyCallback(window, key_callback);
	glfwSetMouseButtonCallback(window, mouse_button_callback);
	glfwSetCursorPosCallback(window, cursor_pos_callback);

	glDepthMask(GL_FALSE);

	glEnable(GL_PROGRAM_POINT_SIZE);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glEnable(GL_BLEND);

	shader_program.add_shader("shaders/points.v.glsl", GL_VERTEX_SHADER);
	shader_program.add_shader("shaders/points.f.glsl", GL_FRAGMENT_SHADER);
	shader_program.link();
	shader_program.use();

	glGenVertexArrays(1, &VAO);
	unsigned int VBO;
	glGenBuffers(1, &VBO);
	glBindVertexArray(VAO);
	glBindBuffer(GL_ARRAY_BUFFER, VBO);

	// std::vector<float> vertices;
	// float f;
	// std::ios_base::sync_with_stdio(false);
	// std::cin.tie(0);
	// int i = 0;
	// while (std::cin >> f) {
	// 	f *= -1;
	// 	if (i == 2)
	// 		f += 2;

	// 	vertices.push_back(f);

	// 	i = (i + 1) % 3;
	// }
	// nr_vertices = vertices.size() / 3;
	// std::cout << nr_vertices << " vertices\n\n" << std::flush;
	// std::mt19937 gen {0};
	// for (int i = 0; i < nr_vertices; ++i) {
	// 	std::normal_distribution<float> d{0,0.3};
	// 	const float x = d(gen);
	// 	const float y = d(gen);
	// 	vertices[3 * i + 0] = x;
	// 	vertices[3 * i + 1] = y;
	// 	vertices[3 * i + 2] = (x*x + y*y) / 3 - 0.2;
	// }

	std::string pc = load_file("pointclouds/workbench.pointcloud");
	nr_vertices = pc.size() / sizeof(float) / 3;

	float *pc_data = const_cast<float*>(reinterpret_cast<const float*>(pc.c_str()));

	for (int i = 0; i < nr_vertices; ++i) {
		pc_data[3 * i + 0] *= 0.2;
		pc_data[3 * i + 1] *= 0.2;
		pc_data[3 * i + 2] *= -0.2;

		pc_data[3 * i + 0] += 0;
		pc_data[3 * i + 1] += 0;
		pc_data[3 * i + 2] += 1;
	}

	std::cout << nr_vertices << " vertices\n\n" << std::flush;

	// copy data to buffer
	glBufferData(GL_ARRAY_BUFFER, 3 * nr_vertices * sizeof(float), pc.c_str(), GL_STATIC_DRAW);

	int attribute_location_position=glGetAttribLocation(shader_program.program, "position");
	glVertexAttribPointer(attribute_location_position, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
	glEnableVertexAttribArray(attribute_location_position);
	viewmat_location = glGetUniformLocation(shader_program.program, "viewmat");
	projmat_location = glGetUniformLocation(shader_program.program, "projmat");
	glBindVertexArray(0);

	#ifdef __EMSCRIPTEN__
		emscripten_set_main_loop(iteration, 0, true);
	#else
		while(!glfwWindowShouldClose(window)) {
			iteration();
		}
	#endif

	glfwTerminate();
}