#include <NTL/ZZ.h>
#include <NTL/vec_ZZ.h>
#include <NTL/Lazy.h>
#include <NTL/fileio.h>

NTL_START_IMPL

const ZZ& ZZ::zero()
{
   NTL_THREAD_LOCAL static ZZ z;
   return z;
}

const ZZ& ZZ_expo(long e)
{
   NTL_THREAD_LOCAL static ZZ expo_helper;
   conv(expo_helper, e);
   return expo_helper;
}

void AddMod(ZZ& x, const ZZ& a, long b, const ZZ& n)
{
   NTL_ZZRegister(B);
   conv(B, b);
   AddMod(x, a, B, n);
}

void SubMod(ZZ& x, const ZZ& a, long b, const ZZ& n)
{
   NTL_ZZRegister(B);
   conv(B, b);
   SubMod(x, a, B, n);
}

void SubMod(ZZ& x, long a, const ZZ& b, const ZZ& n)
{
   NTL_ZZRegister(A);
   conv(A, a);
   SubMod(x, A, b, n);
}

// ****** input and output

NTL_THREAD_LOCAL static long iodigits = 0;
NTL_THREAD_LOCAL static long ioradix = 0;

// iodigits is the greatest integer such that 10^{iodigits} < NTL_WSP_BOUND
// ioradix = 10^{iodigits}

static void InitZZIO()
{
   long x;

   x = (NTL_WSP_BOUND-1)/10;
   iodigits = 0;
   ioradix = 1;

   while (x) {
      x = x / 10;
      iodigits++;
      ioradix = ioradix * 10;
   }

   if (iodigits <= 0) TerminalError("problem with I/O");
}

istream& operator>>(istream& s, ZZ& x)
{
   long c;
   long cval;
   long sign;
   long ndigits;
   long acc;
   NTL_ZZRegister(a);

   if (!s) NTL_INPUT_ERROR(s, "bad ZZ input");

   if (!iodigits) InitZZIO();

   a = 0;

   SkipWhiteSpace(s);
   c = s.peek();

   if (c == '-') {
      sign = -1;
      s.get();
      c = s.peek();
   }
   else
      sign = 1;

   cval = CharToIntVal(c);

   if (cval < 0 || cval > 9) NTL_INPUT_ERROR(s, "bad ZZ input");

   ndigits = 0;
   acc = 0;
   while (cval >= 0 && cval <= 9) {
      acc = acc*10 + cval;
      ndigits++;

      if (ndigits == iodigits) {
         mul(a, a, ioradix);
         add(a, a, acc);
         ndigits = 0;
         acc = 0;
      }

      s.get();
      c = s.peek();
      cval = CharToIntVal(c);
   }

   if (ndigits != 0) {
      long mpy = 1;
      while (ndigits > 0) {
         mpy = mpy * 10;
         ndigits--;
      }

      mul(a, a, mpy);
      add(a, a, acc);
   }

   if (sign == -1)
      negate(a, a);

   x = a;
   return s;
}

// The class _ZZ_local_stack should be defined in an empty namespace,
// but since I don't want to rely on namespaces, we just give it a funny
// name to avoid accidental name clashes.

struct _ZZ_local_stack {
   long top;
   Vec<long> data;

   _ZZ_local_stack() { top = -1; }

   long pop() { return data[top--]; }
   long empty() { return (top == -1); }
   void push(long x);
};

void _ZZ_local_stack::push(long x)
{
   if (top+1 >= data.length())
      data.SetLength(max(32, long(1.414*data.length())));

   top++;
   data[top] = x;
}

static
void PrintDigits(ostream& s, long d, long justify)
{
   NTL_THREAD_LOCAL static Vec<char> buf(INIT_SIZE, iodigits);

   long i = 0;

   while (d) {
      buf[i] = IntValToChar(d % 10);
      d = d / 10;
      i++;
   }

   if (justify) {
      long j = iodigits - i;
      while (j > 0) {
         s << "0";
         j--;
      }
   }

   while (i > 0) {
      i--;
      s << buf[i];
   }
}

ostream& operator<<(ostream& s, const ZZ& a)
{
   ZZ b;
   _ZZ_local_stack S;
   long r;
   long k;

   if (!iodigits) InitZZIO();

   b = a;

   k = sign(b);

   if (k == 0) {
      s << "0";
      return s;
   }

   if (k < 0) {
      s << "-";
      negate(b, b);
   }

   do {
      r = DivRem(b, b, ioradix);
      S.push(r);
   } while (!IsZero(b));

   r = S.pop();
   PrintDigits(s, r, 0);

   while (!S.empty()) {
      r = S.pop();
      PrintDigits(s, r, 1);
   }

   return s;
}

long GCD(long a, long b)
{
   long u, v, t, x;

   if (a < 0) {
      if (a < -NTL_MAX_LONG) ResourceError("GCD: integer overflow");
      a = -a;
   }

   if (b < 0) {
      if (b < -NTL_MAX_LONG) ResourceError("GCD: integer overflow");
      b = -b;
   }

   if (b==0)
      x = a;
   else {
      u = a;
      v = b;
      do {
         t = u % v;
         u = v;
         v = t;
      } while (v != 0);

      x = u;
   }

   return x;
}

void XGCD(long& d, long& s, long& t, long a, long b)
{
   long  u, v, u0, v0, u1, v1, u2, v2, q, r;

   long aneg = 0, bneg = 0;

   if (a < 0) {
      if (a < -NTL_MAX_LONG) ResourceError("XGCD: integer overflow");
      a = -a;
      aneg = 1;
   }

   if (b < 0) {
      if (b < -NTL_MAX_LONG) ResourceError("XGCD: integer overflow");
      b = -b;
      bneg = 1;
   }

   u1=1; v1=0;
   u2=0; v2=1;
   u = a; v = b;

   while (v != 0) {
      q = u / v;
      r = u % v;
      u = v;
      v = r;
      u0 = u2;
      v0 = v2;
      u2 =  u1 - q*u2;
      v2 = v1- q*v2;
      u1 = u0;
      v1 = v0;
   }

   if (aneg)
      u1 = -u1;

   if (bneg)
      v1 = -v1;

   d = u;
   s = u1;
   t = v1;
}

long InvMod(long a, long n)
{
   long d, s, t;

   XGCD(d, s, t, a, n);
   if (d != 1) InvModError("InvMod: inverse undefined");
   if (s < 0)
      return s + n;
   else
      return s;
}

long PowerMod(long a, long ee, long n)
{
   long x, y;

   unsigned long e;

   if (ee < 0)
      e = - ((unsigned long) ee);
   else
      e = ee;

   x = 1;
   y = a;
   while (e) {
      if (e & 1) x = MulMod(x, y, n);
      y = MulMod(y, y, n);
      e = e >> 1;
   }

   if (ee < 0) x = InvMod(x, n);

   return x;
}

long ProbPrime(long n, long NumTests)
{
   long m, x, y, z;
   long i, j, k;

   if (n <= 1) return 0;

   if (n == 2) return 1;
   if (n % 2 == 0) return 0;

   if (n == 3) return 1;
   if (n % 3 == 0) return 0;

   if (n == 5) return 1;
   if (n % 5 == 0) return 0;

   if (n == 7) return 1;
   if (n % 7 == 0) return 0;

   if (n >= NTL_SP_BOUND) {
      return ProbPrime(to_ZZ(n), NumTests);
   }

   m = n - 1;
   k = 0;
   while((m & 1) == 0) {
      m = m >> 1;
      k++;
   }

   // n - 1 == 2^k * m, m odd

   for (i = 0; i < NumTests; i++) {
      do {
         x = RandomBnd(n);
      } while (x == 0);
      // x == 0 is not a useful candidtae for a witness!

      if (x == 0) continue;
      z = PowerMod(x, m, n);
      if (z == 1) continue;

      j = 0;
      do {
         y = z;
         z = MulMod(y, y, n);
         j++;
      } while (j != k && z != 1);

      if (z != 1 || y !=  n-1) return 0;
   }

   return 1;
}

long MillerWitness(const ZZ& n, const ZZ& x)
{
   ZZ m, y, z;
   long j, k;

   if (x == 0) return 0;

   add(m, n, -1);
   k = MakeOdd(m);
   // n - 1 == 2^k * m, m odd

   PowerMod(z, x, m, n);
   if (z == 1) return 0;

   j = 0;
   do {
      y = z;
      SqrMod(z, y, n);
      j++;
   } while (j != k && z != 1);

   if (z != 1) return 1;
   add(y, y, 1);
   if (y != n) return 1;
   return 0;
}

// ComputePrimeBound computes a reasonable bound for trial
// division in the Miller-Rabin test.
// It is computed a bit on the "low" side, since being a bit
// low doesn't hurt much, but being too high can hurt a lot.

static
long ComputePrimeBound(long bn)
{
   long wn = (bn+NTL_ZZ_NBITS-1)/NTL_ZZ_NBITS;

   long fn;

   if (wn <= 36)
      fn = wn/4 + 1;
   else
      fn = long(1.67*sqrt(double(wn)));

   long prime_bnd;

   if (NumBits(bn) + NumBits(fn) > NTL_SP_NBITS)
      prime_bnd = NTL_SP_BOUND;
   else
      prime_bnd = bn*fn;

   return prime_bnd;
}

long ProbPrime(const ZZ& n, long NumTrials)
{
   if (n <= 1) return 0;

   if (n.SinglePrecision()) {
      return ProbPrime(to_long(n), NumTrials);
   }

   long prime_bnd = ComputePrimeBound(NumBits(n));

   PrimeSeq s;
   long p;

   p = s.next();
   while (p && p < prime_bnd) {
      if (rem(n, p) == 0)
         return 0;

      p = s.next();
   }

   ZZ W;
   W = 2;

   // first try W == 2....the exponentiation
   // algorithm runs slightly faster in this case

   if (MillerWitness(n, W))
      return 0;

   long i;

   for (i = 0; i < NumTrials; i++) {
      do {
         RandomBnd(W, n);
      } while (W == 0);
      // W == 0 is not a useful candidate for a witness!

      if (MillerWitness(n, W))
         return 0;
   }

   return 1;
}

void RandomPrime(ZZ& n, long l, long NumTrials)
{
   if (l <= 1)
      LogicError("RandomPrime: l out of range");

   if (l == 2) {
      if (RandomBnd(2))
         n = 3;
      else
         n = 2;

      return;
   }

   do {
      RandomLen(n, l);
      if (!IsOdd(n)) add(n, n, 1);
   } while (!ProbPrime(n, NumTrials));
}

void NextPrime(ZZ& n, const ZZ& m, long NumTrials)
{
   ZZ x;

   if (m <= 2) {
      n = 2;
      return;
   }

   x = m;

   while (!ProbPrime(x, NumTrials))
      add(x, x, 1);

   n = x;
}

long NextPrime(long m, long NumTrials)
{
   long x;

   if (m <= 2)
      return 2;

   x = m;

   while (x < NTL_SP_BOUND && !ProbPrime(x, NumTrials))
      x++;

   if (x >= NTL_SP_BOUND)
      ResourceError("NextPrime: no more primes");

   return x;
}

long NextPowerOfTwo(long m)
{
   long k;
   unsigned long n, um;

   if (m < 0) return 0;

   um = m;
   n = 1;
   k = 0;

   while (n < um) {
      n = n << 1;
      k++;
   }

   if (k >= NTL_BITS_PER_LONG-1)
      ResourceError("NextPowerOfTwo: overflow");

   return k;
}

long NumBits(long a)
{
   unsigned long aa;
   if (a < 0)
      aa = - ((unsigned long) a);
   else
      aa = a;

   long k = 0;
   while (aa) {
      k++;
      aa = aa >> 1;
   }

   return k;
}

long bit(long a, long k)
{
   unsigned long aa;
   if (a < 0)
      aa = - ((unsigned long) a);
   else
      aa = a;

   if (k < 0 || k >= NTL_BITS_PER_LONG)
      return 0;
   else
      return long((aa >> k) & 1);
}

long divide(ZZ& q, const ZZ& a, const ZZ& b)
{
   NTL_ZZRegister(qq);
   NTL_ZZRegister(r);

   if (IsZero(b)) {
      if (IsZero(a)) {
         clear(q);
         return 1;
      }
      else
         return 0;
   }

   if (IsOne(b)) {
      q = a;
      return 1;
   }

   DivRem(qq, r, a, b);
   if (!IsZero(r)) return 0;
   q = qq;
   return 1;
}

long divide(const ZZ& a, const ZZ& b)
{
   NTL_ZZRegister(r);

   if (IsZero(b)) return IsZero(a);
   if (IsOne(b)) return 1;

   rem(r, a, b);
   return IsZero(r);
}

long divide(ZZ& q, const ZZ& a, long b)
{
   NTL_ZZRegister(qq);

   if (!b) {
      if (IsZero(a)) {
         clear(q);
         return 1;
      }
      else
         return 0;
   }

   if (b == 1) {
      q = a;
      return 1;
   }

   long r = DivRem(qq, a, b);
   if (r) return 0;
   q = qq;
   return 1;
}

long divide(const ZZ& a, long b)
{
   if (!b) return IsZero(a);
   if (b == 1) {
      return 1;
   }

   long r = rem(a,  b);
   return (r == 0);
}

void InvMod(ZZ& x, const ZZ& a, const ZZ& n)
{
   // NOTE: the underlying LIP routines write to the first argument,
   // even if inverse is undefined

   NTL_ZZRegister(xx);
   if (InvModStatus(xx, a, n))
      InvModError("InvMod: inverse undefined", a, n);
   x = xx;
}

void PowerMod(ZZ& x, const ZZ& a, const ZZ& e, const ZZ& n)
{
   // NOTE: this ensures that all modular inverses are computed
   // in the routine InvMod above, rather than the LIP-internal
   // modular inverse routine
   if (e < 0) {
      ZZ a_inv;
      ZZ e_neg;

      InvMod(a_inv, a, n);
      negate(e_neg, e);
      LowLevelPowerMod(x, a_inv, e_neg, n);
   }
   else
      LowLevelPowerMod(x, a, e, n);
}

#ifdef NTL_EXCEPTIONS

void InvModError(const char *s, const ZZ& a, const ZZ& n)
{
   throw InvModErrorObject(s, a, n);
}

#else

void InvModError(const char *s, const ZZ& a, const ZZ& n)
{
   TerminalError(s);
}

#endif

long RandomPrime_long(long l, long NumTrials)
{
   if (l <= 1 || l >= NTL_BITS_PER_LONG)
      ResourceError("RandomPrime: length out of range");

   long n;
   do {
      n = RandomLen_long(l);
   } while (!ProbPrime(n, NumTrials));

   return n;
}

static Lazy< Vec<char> > lowsieve_storage;
// This is a GLOBAL VARIABLE

PrimeSeq::PrimeSeq()
{
   movesieve = 0;
   pshift = -1;
   pindex = -1;
   exhausted = 0;
}

long PrimeSeq::next()
{
   if (exhausted) {
      return 0;
   }

   if (pshift < 0) {
      shift(0);
      return 2;
   }

   for (;;) {
      const char *p = movesieve;
      long i = pindex;

      while ((++i) < NTL_PRIME_BND) {
         if (p[i]) {
            pindex = i;
            return pshift + 2 * i + 3;
         }
      }

      long newshift = pshift + 2*NTL_PRIME_BND;

      if (newshift > 2 * NTL_PRIME_BND * (2 * NTL_PRIME_BND + 1)) {
         /* end of the road */
         exhausted = 1;
         return 0;
      }

      shift(newshift);
   }
}

void PrimeSeq::shift(long newshift)
{
   long i;
   long j;
   long jstep;
   long jstart;
   long ibound;
   char *p;

   if (!lowsieve_storage.built())
      start();

   const char *lowsieve = lowsieve_storage->elts();

   if (newshift < 0) {
      pshift = -1;
   }
   else if (newshift == 0) {
      pshift = 0;
      movesieve = lowsieve;
   }
   else if (newshift != pshift) {
      if (movesieve_mem.length() == 0) {
         movesieve_mem.SetLength(NTL_PRIME_BND);
      }

      pshift = newshift;
      movesieve = p = movesieve_mem.elts();
      for (i = 0; i < NTL_PRIME_BND; i++)
         p[i] = 1;

      jstep = 3;
      ibound = pshift + 2 * NTL_PRIME_BND + 1;
      for (i = 0; jstep * jstep <= ibound; i++) {
         if (lowsieve[i]) {
            if (!((jstart = (pshift + 2) / jstep + 1) & 1))
               jstart++;
            if (jstart <= jstep)
               jstart = jstep;
            jstart = (jstart * jstep - pshift - 3) / 2;
            for (j = jstart; j < NTL_PRIME_BND; j += jstep)
               p[j] = 0;
         }
         jstep += 2;
      }
   }

   pindex = -1;
   exhausted = 0;
}

void PrimeSeq::start()
{
   long i;
   long j;
   long jstep;
   long jstart;
   long ibnd;
   char *p;

   do {
      Lazy< Vec<char> >::Builder builder(lowsieve_storage);
      if (!builder()) break;

      UniquePtr< Vec<char> > ptr;
      ptr.make();
      ptr->SetLength(NTL_PRIME_BND);

      p = ptr->elts();

      for (i = 0; i < NTL_PRIME_BND; i++)
         p[i] = 1;

      jstep = 1;
      jstart = -1;
      ibnd = (SqrRoot(2 * NTL_PRIME_BND + 1) - 3) / 2;
      for (i = 0; i <= ibnd; i++) {
         jstart += 2 * ((jstep += 2) - 1);
         if (p[i])
            for (j = jstart; j < NTL_PRIME_BND; j += jstep)
               p[j] = 0;
      }

      builder.move(ptr);
   } while (0);

}

void PrimeSeq::reset(long b)
{
   if (b > (2*NTL_PRIME_BND+1)*(2*NTL_PRIME_BND+1)) {
      exhausted = 1;
      return;
   }

   if (b <= 2) {
      shift(-1);
      return;
   }

   if ((b & 1) == 0) b++;

   shift(((b-3) / (2*NTL_PRIME_BND))* (2*NTL_PRIME_BND));
   pindex = (b - pshift - 3)/2 - 1;
}

long Jacobi(const ZZ& aa, const ZZ& nn)
{
   ZZ a, n;
   long t, k;
   long d;

   a = aa;
   n = nn;
   t = 1;

   while (a != 0) {
      k = MakeOdd(a);
      d = trunc_long(n, 3);
      if ((k & 1) && (d == 3 || d == 5)) t = -t;

      if (trunc_long(a, 2) == 3 && (d & 3) == 3) t = -t;
      swap(a, n);
      rem(a, a, n);
   }

   if (n == 1)
      return t;
   else
      return 0;
}

void SqrRootMod(ZZ& x, const ZZ& aa, const ZZ& nn)
{
   if (aa == 0 || aa == 1) {
      x = aa;
      return;
   }

   // at this point, we must have nn >= 5

   if (trunc_long(nn, 2) == 3) {  // special case, n = 3 (mod 4)
      ZZ n, a, e, z;

      n = nn;
      a  = aa;

      add(e, n, 1);
      RightShift(e, e, 2);

      PowerMod(z, a, e, n);
      x = z;

      return;
   }

   ZZ n, m;
   int h, nlen;

   n = nn;
   nlen = NumBits(n);

   sub(m, n, 1);
   h = MakeOdd(m);  // h >= 2

   if (nlen > 50 && h < SqrRoot(nlen)) {
      long i, j;
      ZZ a, b, a_inv, c, r, m1, d;

      a = aa;
      InvMod(a_inv, a, n);

      if (h == 2)
         b = 2;
      else {
         do {
            RandomBnd(b, n);
         } while (Jacobi(b, n) != -1);
      }

      PowerMod(c, b, m, n);

      add(m1, m, 1);
      RightShift(m1, m1, 1);
      PowerMod(r, a, m1, n);

      for (i = h-2; i >= 0; i--) {
         SqrMod(d, r, n);
         MulMod(d, d, a_inv, n);
         for (j = 0; j < i; j++)
            SqrMod(d, d, n);
         if (!IsOne(d))
            MulMod(r, r, c, n);
         SqrMod(c, c, n);
      }

      x = r;
      return;
   }

   long i, k;
   ZZ ma, t, u, v, e;
   ZZ t1, t2, t3, t4;

   n = nn;
   NegateMod(ma, aa, n);

   // find t such that t^2 - 4*a is not a square

   MulMod(t1, ma, 4, n);
   do {
      RandomBnd(t, n);
      SqrMod(t2, t, n);
      AddMod(t2, t2, t1, n);
   } while (Jacobi(t2, n) != -1);

   // compute u*X + v = X^{(n+1)/2} mod f, where f = X^2 - t*X + a

   add(e, n, 1);
   RightShift(e, e, 1);

   u = 0;
   v = 1;

   k = NumBits(e);

   for (i = k - 1; i >= 0; i--) {
      add(t2, u, v);
      sqr(t3, t2);  // t3 = (u+v)^2
      sqr(t1, u);
      sqr(t2, v);
      sub(t3, t3, t1);
      sub(t3, t3, t2); // t1 = u^2, t2 = v^2, t3 = 2*u*v
      rem(t1, t1, n);
      mul(t4, t1, t);
      add(t4, t4, t3);
      rem(u, t4, n);

      mul(t4, t1, ma);
      add(t4, t4, t2);
      rem(v, t4, n);

      if (bit(e, i)) {
         MulMod(t1, u, t, n);
         AddMod(t1, t1, v, n);
         MulMod(v, u, ma, n);
         u = t1;
      }

   }

   x = v;
}

// Chinese Remaindering.
//
// This version in new to v3.7, and is significantly
// simpler and faster than the previous version.
//
// This function takes as input g, a, G, p,
// such that a > 0, 0 <= G < p, and gcd(a, p) = 1.
// It computes a' = a*p and g' such that
//   * g' = g (mod a);
//   * g' = G (mod p);
//   * -a'/2 < g' <= a'/2.
// It then sets g := g' and a := a', and returns 1 iff g has changed.
//
// Under normal use, the input value g satisfies -a/2 < g <= a/2;
// however, this was not documented or enforced in earlier versions,
// so to maintain backward compatability, no restrictions are placed
// on g.  This routine runs faster, though, if -a/2 < g <= a/2,
// and the first thing the routine does is to make this condition
// hold.
//
// Also, under normal use, both a and p are odd;  however, the routine
// will still work even if this is not so.
//
// The routine is based on the following simple fact.
//
// Let -a/2 < g <= a/2, and let h satisfy
//   * g + a h = G (mod p);
//   * -p/2 < h <= p/2.
// Further, if p = 2*h and g > 0, set
//   g' := g - a h;
// otherwise, set
//   g' := g + a h.
// Then g' so defined satisfies the above requirements.
//
// It is trivial to see that g's satisfies the congruence conditions.
// The only thing is to check that the "balancing" condition
// -a'/2 < g' <= a'/2 also holds.

long CRT(ZZ& gg, ZZ& a, long G, long p)
{
   if (p >= NTL_SP_BOUND) {
      ZZ GG, pp;
      conv(GG, G);
      conv(pp, p);
      return CRT(gg, a, GG, pp);
   }

   long modified = 0;

   NTL_ZZRegister(g);

   if (!CRTInRange(gg, a)) {
      modified = 1;
      ZZ a1;
      rem(g, gg, a);
      RightShift(a1, a, 1);
      if (g > a1) sub(g, g, a);
   }
   else
      g = gg;

   long p1;
   p1 = p >> 1;

   long a_inv;
   a_inv = rem(a, p);
   a_inv = InvMod(a_inv, p);

   long h;
   h = rem(g, p);
   h = SubMod(G, h, p);
   h = MulMod(h, a_inv, p);
   if (h > p1)
      h = h - p;

   if (h != 0) {
      modified = 1;

      if (!(p & 1) && g > 0 && (h == p1))
         MulSubFrom(g, a, h);
      else
         MulAddTo(g, a, h);
   }

   mul(a, a, p);
   gg = g;

   return modified;
}

long CRT(ZZ& gg, ZZ& a, const ZZ& G, const ZZ& p)
{
   long modified = 0;

   ZZ g;

   if (!CRTInRange(gg, a)) {
      modified = 1;
      ZZ a1;
      rem(g, gg, a);
      RightShift(a1, a, 1);
      if (g > a1) sub(g, g, a);
   }
   else
      g = gg;

   ZZ p1;
   RightShift(p1, p, 1);

   ZZ a_inv;
   rem(a_inv, a, p);
   InvMod(a_inv, a_inv, p);

   ZZ h;
   rem(h, g, p);
   SubMod(h, G, h, p);
   MulMod(h, h, a_inv, p);
   if (h > p1)
      sub(h, h, p);

   if (h != 0) {
      modified = 1;
      ZZ ah;
      mul(ah, a, h);

      if (!IsOdd(p) && g > 0 &&  (h == p1))
         sub(g, g, ah);
      else
         add(g, g, ah);
   }

   mul(a, a, p);
   gg = g;

   return modified;
}

void sub(ZZ& x, const ZZ& a, long b)
{
   NTL_ZZRegister(B);
   conv(B, b);
   sub(x, a, B);
}

void sub(ZZ& x, long a, const ZZ& b)
{
   NTL_ZZRegister(A);
   conv(A, a);
   sub(x, A, b);
}

void power2(ZZ& x, long e)
{
   if (e < 0) ArithmeticError("power2: negative exponent");
   set(x);
   LeftShift(x, x, e);
}

void conv(ZZ& x, const char *s)
{
   long c;
   long cval;
   long sign;
   long ndigits;
   long acc;
   long i = 0;

   NTL_ZZRegister(a);

   if (!s) InputError("bad ZZ input");

   if (!iodigits) InitZZIO();

   a = 0;

   c = s[i];
   while (IsWhiteSpace(c)) {
      i++;
      c = s[i];
   }

   if (c == '-') {
      sign = -1;
      i++;
      c = s[i];
   }
   else
      sign = 1;

   cval = CharToIntVal(c);
   if (cval < 0 || cval > 9) InputError("bad ZZ input");

   ndigits = 0;
   acc = 0;
   while (cval >= 0 && cval <= 9) {
      acc = acc*10 + cval;
      ndigits++;

      if (ndigits == iodigits) {
         mul(a, a, ioradix);
         add(a, a, acc);
         ndigits = 0;
         acc = 0;
      }

      i++;
      c = s[i];
      cval = CharToIntVal(c);
   }

   if (ndigits != 0) {
      long mpy = 1;
      while (ndigits > 0) {
         mpy = mpy * 10;
         ndigits--;
      }

      mul(a, a, mpy);
      add(a, a, acc);
   }

   if (sign == -1)
      negate(a, a);

   x = a;
}

void bit_and(ZZ& x, const ZZ& a, long b)
{
   NTL_ZZRegister(B);
   conv(B, b);
   bit_and(x, a, B);
}

void bit_or(ZZ& x, const ZZ& a, long b)
{
   NTL_ZZRegister(B);
   conv(B, b);
   bit_or(x, a, B);
}

void bit_xor(ZZ& x, const ZZ& a, long b)
{
   NTL_ZZRegister(B);
   conv(B, b);
   bit_xor(x, a, B);
}

long power_long(long a, long e)
{
   if (e < 0) ArithmeticError("power_long: negative exponent");

   if (e == 0) return 1;

   if (a == 1) return 1;
   if (a == -1) {
      if (e & 1)
         return -1;
      else
         return 1;
   }

   // no overflow check --- result is computed correctly
   // modulo word size

   unsigned long res = 1;
   unsigned long aa = a;
   long i;

   for (i = 0; i < e; i++)
      res *= aa;

   return to_long(res);
}

//  RANDOM NUMBER GENERATION

// Idea for this PRNG.  Iteratively hash seed using md5
// to get 256 bytes to initialize arc4.
// Then use arc4 to get a pseudo-random byte stream.

// I've taken care that the pseudo-random numbers generated by
// the routines RandomBnd, RandomBits, and RandomLen
// are completely platform independent.

// I make use of the md5 compression function,
// which I've modified to work on 64-bit machines

/*
 *  BEGIN RSA's md5 stuff
 *
 */

/*
 **********************************************************************
 ** md5.c                                                            **
 ** RSA Data Security, Inc. MD5 Message Digest Algorithm             **
 ** Created: 2/17/90 RLR                                             **
 ** Revised: 1/91 SRD,AJ,BSK,JT Reference C Version                  **
 **********************************************************************
 */

/*
 **********************************************************************
 ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. **
 **                                                                  **
 ** License to copy and use this software is granted provided that   **
 ** it is identified as the "RSA Data Security, Inc. MD5 Message     **
 ** Digest Algorithm" in all material mentioning or referencing this **
 ** software or this function.                                       **
 **                                                                  **
 ** License is also granted to make and use derivative works         **
 ** provided that such works are identified as "derived from the RSA **
 ** Data Security, Inc. MD5 Message Digest Algorithm" in all         **
 ** material mentioning or referencing the derived work.             **
 **                                                                  **
 ** RSA Data Security, Inc. makes no representations concerning      **
 ** either the merchantability of this software or the suitability   **
 ** of this software for any particular purpose.  It is provided "as **
 ** is" without express or implied warranty of any kind.             **
 **                                                                  **
 ** These notices must be retained in any copies of any part of this **
 ** documentation and/or software.                                   **
 **********************************************************************
 */

#if (NTL_BITS_PER_LONG <= 32)
#define TRUNC32(x) (x)
#else
#define TRUNC32(x) ((x) & ((1UL << 32)-1UL))
#endif

/* F, G and H are basic MD5 functions: selection, majority, parity */
#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
#define H(x, y, z) ((x) ^ (y) ^ (z))
#define I(x, y, z) (TRUNC32((y) ^ ((x) | (~z))))

/* ROTATE_LEFT rotates x left n bits */
#define ROTATE_LEFT(x, n) (TRUNC32(((x) << (n)) | ((x) >> (32-(n)))))

/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4 */
/* Rotation is separate from addition to prevent recomputation */
#define FF(a, b, c, d, x, s, ac) \
  {(a) = TRUNC32((a) + F((b), (c), (d)) + (x) + (ac)); \
   (a) = ROTATE_LEFT((a), (s)); \
   (a) = TRUNC32((a) + (b)); \
  }
#define GG(a, b, c, d, x, s, ac) \
  {(a) = TRUNC32((a) + G((b), (c), (d)) + (x) + (ac)); \
   (a) = ROTATE_LEFT((a), (s)); \
   (a) = TRUNC32((a) + (b)); \
  }
#define HH(a, b, c, d, x, s, ac) \
  {(a) = TRUNC32((a) + H((b), (c), (d)) + (x) + (ac)); \
   (a) = ROTATE_LEFT((a), (s)); \
   (a) = TRUNC32((a) + (b)); \
  }
#define II(a, b, c, d, x, s, ac) \
  {(a) = TRUNC32((a) + I((b), (c), (d)) + (x) + (ac)); \
   (a) = ROTATE_LEFT((a), (s)); \
   (a) = TRUNC32((a) + (b)); \
  }

static
void MD5_default_IV(unsigned long *buf)
{
   buf[0] = 0x67452301UL;
   buf[1] = 0xefcdab89UL;
   buf[2] = 0x98badcfeUL;
   buf[3] = 0x10325476UL;
}

/* Basic MD5 step. Transform buf based on in.
 */

static
void MD5_compress(unsigned long *buf, unsigned long *in)
{
  unsigned long a = buf[0], b = buf[1], c = buf[2], d = buf[3];

  /* Round 1 */
#define S11 7
#define S12 12
#define S13 17
#define S14 22
  FF ( a, b, c, d, in[ 0], S11, 3614090360UL); /* 1 */
  FF ( d, a, b, c, in[ 1], S12, 3905402710UL); /* 2 */
  FF ( c, d, a, b, in[ 2], S13,  606105819UL); /* 3 */
  FF ( b, c, d, a, in[ 3], S14, 3250441966UL); /* 4 */
  FF ( a, b, c, d, in[ 4], S11, 4118548399UL); /* 5 */
  FF ( d, a, b, c, in[ 5], S12, 1200080426UL); /* 6 */
  FF ( c, d, a, b, in[ 6], S13, 2821735955UL); /* 7 */
  FF ( b, c, d, a, in[ 7], S14, 4249261313UL); /* 8 */
  FF ( a, b, c, d, in[ 8], S11, 1770035416UL); /* 9 */
  FF ( d, a, b, c, in[ 9], S12, 2336552879UL); /* 10 */
  FF ( c, d, a, b, in[10], S13, 4294925233UL); /* 11 */
  FF ( b, c, d, a, in[11], S14, 2304563134UL); /* 12 */
  FF ( a, b, c, d, in[12], S11, 1804603682UL); /* 13 */
  FF ( d, a, b, c, in[13], S12, 4254626195UL); /* 14 */
  FF ( c, d, a, b, in[14], S13, 2792965006UL); /* 15 */
  FF ( b, c, d, a, in[15], S14, 1236535329UL); /* 16 */

  /* Round 2 */
#define S21 5
#define S22 9
#define S23 14
#define S24 20
  GG ( a, b, c, d, in[ 1], S21, 4129170786UL); /* 17 */
  GG ( d, a, b, c, in[ 6], S22, 3225465664UL); /* 18 */
  GG ( c, d, a, b, in[11], S23,  643717713UL); /* 19 */
  GG ( b, c, d, a, in[ 0], S24, 3921069994UL); /* 20 */
  GG ( a, b, c, d, in[ 5], S21, 3593408605UL); /* 21 */
  GG ( d, a, b, c, in[10], S22,   38016083UL); /* 22 */
  GG ( c, d, a, b, in[15], S23, 3634488961UL); /* 23 */
  GG ( b, c, d, a, in[ 4], S24, 3889429448UL); /* 24 */
  GG ( a, b, c, d, in[ 9], S21,  568446438UL); /* 25 */
  GG ( d, a, b, c, in[14], S22, 3275163606UL); /* 26 */
  GG ( c, d, a, b, in[ 3], S23, 4107603335UL); /* 27 */
  GG ( b, c, d, a, in[ 8], S24, 1163531501UL); /* 28 */
  GG ( a, b, c, d, in[13], S21, 2850285829UL); /* 29 */
  GG ( d, a, b, c, in[ 2], S22, 4243563512UL); /* 30 */
  GG ( c, d, a, b, in[ 7], S23, 1735328473UL); /* 31 */
  GG ( b, c, d, a, in[12], S24, 2368359562UL); /* 32 */

  /* Round 3 */
#define S31 4
#define S32 11
#define S33 16
#define S34 23
  HH ( a, b, c, d, in[ 5], S31, 4294588738UL); /* 33 */
  HH ( d, a, b, c, in[ 8], S32, 2272392833UL); /* 34 */
  HH ( c, d, a, b, in[11], S33, 1839030562UL); /* 35 */
  HH ( b, c, d, a, in[14], S34, 4259657740UL); /* 36 */
  HH ( a, b, c, d, in[ 1], S31, 2763975236UL); /* 37 */
  HH ( d, a, b, c, in[ 4], S32, 1272893353UL); /* 38 */
  HH ( c, d, a, b, in[ 7], S33, 4139469664UL); /* 39 */
  HH ( b, c, d, a, in[10], S34, 3200236656UL); /* 40 */
  HH ( a, b, c, d, in[13], S31,  681279174UL); /* 41 */
  HH ( d, a, b, c, in[ 0], S32, 3936430074UL); /* 42 */
  HH ( c, d, a, b, in[ 3], S33, 3572445317UL); /* 43 */
  HH ( b, c, d, a, in[ 6], S34,   76029189UL); /* 44 */
  HH ( a, b, c, d, in[ 9], S31, 3654602809UL); /* 45 */
  HH ( d, a, b, c, in[12], S32, 3873151461UL); /* 46 */
  HH ( c, d, a, b, in[15], S33,  530742520UL); /* 47 */
  HH ( b, c, d, a, in[ 2], S34, 3299628645UL); /* 48 */

  /* Round 4 */
#define S41 6
#define S42 10
#define S43 15
#define S44 21
  II ( a, b, c, d, in[ 0], S41, 4096336452UL); /* 49 */
  II ( d, a, b, c, in[ 7], S42, 1126891415UL); /* 50 */
  II ( c, d, a, b, in[14], S43, 2878612391UL); /* 51 */
  II ( b, c, d, a, in[ 5], S44, 4237533241UL); /* 52 */
  II ( a, b, c, d, in[12], S41, 1700485571UL); /* 53 */
  II ( d, a, b, c, in[ 3], S42, 2399980690UL); /* 54 */
  II ( c, d, a, b, in[10], S43, 4293915773UL); /* 55 */
  II ( b, c, d, a, in[ 1], S44, 2240044497UL); /* 56 */
  II ( a, b, c, d, in[ 8], S41, 1873313359UL); /* 57 */
  II ( d, a, b, c, in[15], S42, 4264355552UL); /* 58 */
  II ( c, d, a, b, in[ 6], S43, 2734768916UL); /* 59 */
  II ( b, c, d, a, in[13], S44, 1309151649UL); /* 60 */
  II ( a, b, c, d, in[ 4], S41, 4149444226UL); /* 61 */
  II ( d, a, b, c, in[11], S42, 3174756917UL); /* 62 */
  II ( c, d, a, b, in[ 2], S43,  718787259UL); /* 63 */
  II ( b, c, d, a, in[ 9], S44, 3951481745UL); /* 64 */

  buf[0] = TRUNC32(buf[0] + a);
  buf[1] = TRUNC32(buf[1] + b);
  buf[2] = TRUNC32(buf[2] + c);
  buf[3] = TRUNC32(buf[3] + d);
}

/*
 *  END RSA's md5 stuff
 *
 */

static
void words_from_bytes(unsigned long *txtl, const unsigned char *txtc, long n)
{
   long i;
   unsigned long v;

   for (i = 0; i < n; i++) {
      v = txtc[4*i];
      v += ((unsigned long) (txtc[4*i+1])) << 8;
      v += ((unsigned long) (txtc[4*i+2])) << 16;
      v += ((unsigned long) (txtc[4*i+3])) << 24;
      txtl[i] = v;
   }
}

static
void bytes_from_words(unsigned char *txtc, const unsigned long *txtl, long n)
{
   long i;
   unsigned long v;

   for (i = 0; i < n; i++) {
      v = txtl[i];
      txtc[4*i] = v & 255;
      v = v >> 8;
      txtc[4*i+1] = v & 255;
      v = v >> 8;
      txtc[4*i+2] = v & 255;
      v = v >> 8;
      txtc[4*i+3] = v & 255;
   }
}

static
void MD5_compress1(unsigned long *buf, unsigned char *in, long n)
{
   unsigned long txtl[16];
   unsigned char txtc[64];
   long i, j, k;

   if (n < 0) n = 0;

   i = 0;
   while (i < n) {
      k = n-i;
      if (k > 64) k = 64;
      for (j = 0; j < k; j++)
         txtc[j] = in[i+j];
      for (; j < 64; j++)
         txtc[j] = 0;
      words_from_bytes(txtl, txtc, 16);
      MD5_compress(buf, txtl);
      i += k;
   }
}

// the "cipherpunk" version of arc4

struct _ZZ_arc4_key
{
    unsigned char state[256];
    unsigned char x;
    unsigned char y;
};

static inline
void swap_byte(unsigned char *a, unsigned char *b)
{
    unsigned char swapByte;

    swapByte = *a;
    *a = *b;
    *b = swapByte;
}

static
void prepare_key(unsigned char *key_data_ptr,
                 long key_data_len, _ZZ_arc4_key *key)
{
    unsigned char index1;
    unsigned char index2;
    unsigned char* state;
    long counter;

    state = &key->state[0];
    for(counter = 0; counter < 256; counter++)
       state[counter] = counter;
    key->x = 0;
    key->y = 0;
    index1 = 0;
    index2 = 0;
    for(counter = 0; counter < 256; counter++)
    {
         index2 = (key_data_ptr[index1] + state[counter] + index2) & 255;
         swap_byte(&state[counter], &state[index2]);

         index1 = (index1 + 1) % key_data_len;
    }
}

static
void arc4(unsigned char *buffer_ptr, long buffer_len, _ZZ_arc4_key *key)
{
    unsigned char x;
    unsigned char y;
    unsigned char* state;
    unsigned char xorIndex;
    long counter;

    x = key->x;
    y = key->y;

    state = &key->state[0];
    for(counter = 0; counter < buffer_len; counter ++)
    {
         x = (x + 1) & 255;
         y = (state[x] + y) & 255;
         swap_byte(&state[x], &state[y]);

         xorIndex = (state[x] + state[y]) & 255;

         buffer_ptr[counter] = state[xorIndex];
     }
     key->x = x;
     key->y = y;
}

// global state information for PRNG

NTL_THREAD_LOCAL static long ran_initialized = 0;
NTL_THREAD_LOCAL static _ZZ_arc4_key ran_key;

static const unsigned long default_md5_tab[16] = {
744663023UL, 1011602954UL, 3163087192UL, 3383838527UL,
3305324122UL, 3197458079UL, 2266495600UL, 2760303563UL,
346234297UL, 1919920720UL, 1896169861UL, 2192176675UL,
2027150322UL, 2090160759UL, 2134858730UL, 1131796244UL
};

static
void build_arc4_tab(unsigned char *seed_bytes, const ZZ& s)
{
   long nb = NumBytes(s);

   unsigned char *txt;

   Vec<unsigned char> txt_storage;
   txt_storage.SetLength(nb + 68);
   txt = txt_storage.elts();

   BytesFromZZ(txt + 4, s, nb);

   bytes_from_words(txt + nb + 4, default_md5_tab, 16);

   unsigned long buf[4];

   unsigned long i;
   for (i = 0; i < 16; i++) {
      MD5_default_IV(buf);
      bytes_from_words(txt, &i, 1);

      MD5_compress1(buf, txt, nb + 68);

      bytes_from_words(seed_bytes + 16*i, buf, 4);
   }
}

void SetSeed(const ZZ& s)
{
   unsigned char seed_bytes[256];

   build_arc4_tab(seed_bytes, s);
   prepare_key(seed_bytes, 256, &ran_key);

   // we discard the first 1024 bytes of the arc4 stream, as this is
   // recommended practice.

   arc4(seed_bytes, 256, &ran_key);
   arc4(seed_bytes, 256, &ran_key);
   arc4(seed_bytes, 256, &ran_key);
   arc4(seed_bytes, 256, &ran_key);

   ran_initialized = 1;
}

static
void ran_bytes(unsigned char *bytes, long n)
{
   if (!ran_initialized) {
      ZZ x;
      const string& id = UniqueID();
      ZZFromBytes(x, (const unsigned char *) id.c_str(), id.length());
      SetSeed(x);
   }
   arc4(bytes, n, &ran_key);
}

unsigned long RandomWord()
{
   unsigned char buf[NTL_BITS_PER_LONG/8];
   long i;
   unsigned long res;

   ran_bytes(buf, NTL_BITS_PER_LONG/8);

   res = 0;
   for (i = NTL_BITS_PER_LONG/8 - 1; i >= 0; i--) {
      res = res << 8;
      res = res | buf[i];
   }

   return res;
}

long RandomBits_long(long l)
{
   if (l <= 0) return 0;
   if (l >= NTL_BITS_PER_LONG)
      ResourceError("RandomBits: length too big");

   unsigned char buf[NTL_BITS_PER_LONG/8];
   unsigned long res;
   long i;

   long nb = (l+7)/8;
   ran_bytes(buf, nb);

   res = 0;
   for (i = nb - 1; i >= 0; i--) {
      res = res << 8;
      res = res | buf[i];
   }

   return long(res & ((1UL << l)-1UL));
}

unsigned long RandomBits_ulong(long l)
{
   if (l <= 0) return 0;
   if (l > NTL_BITS_PER_LONG)
      ResourceError("RandomBits: length too big");

   unsigned char buf[NTL_BITS_PER_LONG/8];
   unsigned long res;
   long i;

   long nb = (l+7)/8;
   ran_bytes(buf, nb);

   res = 0;
   for (i = nb - 1; i >= 0; i--) {
      res = res << 8;
      res = res | buf[i];
   }

   if (l < NTL_BITS_PER_LONG)
      res = res & ((1UL << l)-1UL);

   return res;
}

long RandomLen_long(long l)
{
   if (l <= 0) return 0;
   if (l == 1) return 1;
   if (l >= NTL_BITS_PER_LONG)
      ResourceError("RandomLen: length too big");

   return RandomBits_long(l-1) + (1L << (l-1));
}

void RandomBits(ZZ& x, long l)
{
   if (l <= 0) {
      x = 0;
      return;
   }

   if (NTL_OVERFLOW(l, 1, 0))
      ResourceError("RandomBits: length too big");

   long nb = (l+7)/8;

   NTL_THREAD_LOCAL static Vec<unsigned char> buf_mem;
   Vec<unsigned char>::Watcher watch_buf_mem(buf_mem);

   buf_mem.SetLength(nb);
   unsigned char *buf = buf_mem.elts();

   ran_bytes(buf, nb);

   NTL_ZZRegister(res);

   ZZFromBytes(res, buf, nb);
   trunc(res, res, l);

   x = res;
}

void RandomLen(ZZ& x, long l)
{
   if (l <= 0) {
      x = 0;
      return;
   }

   if (l == 1) {
      x = 1;
      return;
   }

   if (NTL_OVERFLOW(l, 1, 0))
      ResourceError("RandomLen: length too big");

   // pre-allocate space to avoid two allocations
   long nw = (l + NTL_ZZ_NBITS - 1)/NTL_ZZ_NBITS;
   x.SetSize(nw);

   RandomBits(x, l-1);
   SetBit(x, l-1);
}

const long RandomBndExcess = 8;

void RandomBnd(ZZ& x, const ZZ& bnd)
{
   if (bnd <= 1) {
      x = 0;
      return;
   }

   long k = NumBits(bnd);

   if (weight(bnd) == 1) {
      RandomBits(x, k-1);
      return;
   }

   long l = k + RandomBndExcess;

   NTL_ZZRegister(t);
   NTL_ZZRegister(r);
   NTL_ZZRegister(t1);

   do {
      RandomBits(t, l);
      rem(r, t, bnd);
      sub(t1, bnd, r);
      add(t, t, t1);
   } while (NumBits(t) > l);

   x = r;
}

long RandomBnd(long bnd)
{
   if (bnd <= 1) return 0;

   long k = NumBits(bnd);

   if (((bnd - 1) & bnd) == 0)
      return RandomBits_long(k-1);

   long l = k + RandomBndExcess;

   if (l > NTL_BITS_PER_LONG-2) {
      NTL_ZZRegister(Bnd);
      NTL_ZZRegister(res);

      Bnd = bnd;
      RandomBnd(res, Bnd);
      return to_long(res);
   }

   long t, r;

   do {
      t = RandomBits_long(l);
      r = t % bnd;
   } while (t + bnd - r > (1L << l));

   return r;
}

// More prime generation stuff...

static
double Log2(double x)
{
   NTL_THREAD_LOCAL static double log2 = log(2.0);
   return log(x)/log2;
}

// Define p(k,t) to be the conditional probability that a random, odd, k-bit
// number is composite, given that it passes t iterations of the
// Miller-Rabin test.
// This routine returns 0 or 1, and if it returns 1 then
// p(k,t) <= 2^{-n}.
// This basically encodes the estimates of Damgard, Landrock, and Pomerance;
// it uses floating point arithmetic, but is coded in such a way
// that its results should be correct, assuming that the log function
// is computed with reasonable precision.
//
// It is assumed that k >= 3 and t >= 1; if this does not hold,
// then 0 is returned.

static
long ErrBoundTest(long kk, long tt, long nn)

{
   const double fudge = (1.0 + 1024.0/NTL_FDOUBLE_PRECISION);
   const double log2_3 = Log2(3.0);
   const double log2_7 = Log2(7.0);
   const double log2_20 = Log2(20.0);

   double k = kk;
   double t = tt;
   double n = nn;

   if (k < 3 || t < 1) return 0;
   if (n < 1) return 1;

   // the following test is largely academic
   if (9*t > NTL_FDOUBLE_PRECISION) LogicError("ErrBoundTest: t too big");

   double log2_k = Log2(k);

   if ((n + log2_k)*fudge <= 2*t)
      return 1;

   if ((2*log2_k + 4.0 + n)*fudge <= 2*sqrt(k))
      return 2;

   if ((t == 2 && k >= 88) || (3 <= t && 9*t <= k && k >= 21)) {
      if ((1.5*log2_k + t + 4.0 + n)*fudge <= 0.5*Log2(t) + 2*(sqrt(t*k)))
         return 3;
   }

   if (k <= 9*t && 4*t <= k && k >= 21) {
      if ( ((log2_3 + log2_7 + log2_k + n)*fudge <= log2_20 + 5*t)  &&
           ((log2_3 + (15.0/4.0)*log2_k + n)*fudge <= log2_7 + k/2 + 2*t) &&
           ((2*log2_3 + 2 + log2_k + n)*fudge <= k/4 + 3*t) )
         return 4;
   }

   if (4*t >= k && k >= 21) {
      if (((15.0/4.0)*log2_k + n)*fudge <= log2_7 + k/2 + 2*t)
         return 5;
   }

   return 0;
}

void GenPrime(ZZ& n, long k, long err)
{
   if (k <= 1) LogicError("GenPrime: bad length");

   if (k > (1L << 20)) ResourceError("GenPrime: length too large");

   if (err < 1) err = 1;
   if (err > 512) err = 512;

   if (k == 2) {
      if (RandomBnd(2))
         n = 3;
      else
         n = 2;

      return;
   }

   long t;

   t = 1;
   while (!ErrBoundTest(k, t, err))
      t++;

   RandomPrime(n, k, t);
}

long GenPrime_long(long k, long err)
{
   if (k <= 1) LogicError("GenPrime: bad length");

   if (k >= NTL_BITS_PER_LONG) ResourceError("GenPrime: length too large");

   if (err < 1) err = 1;
   if (err > 512) err = 512;

   if (k == 2) {
      if (RandomBnd(2))
         return 3;
      else
         return 2;
   }

   long t;

   t = 1;
   while (!ErrBoundTest(k, t, err))
      t++;

   return RandomPrime_long(k, t);
}

void GenGermainPrime(ZZ& n, long k, long err)
{
   if (k <= 1) LogicError("GenGermainPrime: bad length");

   if (k > (1L << 20)) ResourceError("GenGermainPrime: length too large");

   if (err < 1) err = 1;
   if (err > 512) err = 512;

   if (k == 2) {
      if (RandomBnd(2))
         n = 3;
      else
         n = 2;

      return;
   }

   long prime_bnd = ComputePrimeBound(k);

   if (NumBits(prime_bnd) >= k/2)
      prime_bnd = (1L << (k/2-1));

   ZZ two;
   two = 2;

   ZZ n1;

   PrimeSeq s;

   ZZ iter;
   iter = 0;

   for (;;) {
      iter++;

      RandomLen(n, k);
      if (!IsOdd(n)) add(n, n, 1);

      s.reset(3);
      long p;

      long sieve_passed = 1;

      p = s.next();
      while (p && p < prime_bnd) {
         long r = rem(n, p);

         if (r == 0) {
            sieve_passed = 0;
            break;
         }

         // test if 2*r + 1 = 0 (mod p)
         if (r == p-r-1) {
            sieve_passed = 0;
            break;
         }

         p = s.next();
      }

      if (!sieve_passed) continue;

      if (MillerWitness(n, two)) continue;

      // n1 = 2*n+1
      mul(n1, n, 2);
      add(n1, n1, 1);

      if (MillerWitness(n1, two)) continue;

      // now do t M-R iterations...just to make sure

      // First compute the appropriate number of M-R iterations, t
      // The following computes t such that
      //       p(k,t)*8/k <= 2^{-err}/(5*iter^{1.25})
      // which suffices to get an overall error probability of 2^{-err}.
      // Note that this method has the advantage of not requiring
      // any assumptions on the density of Germain primes.

      long err1 = max(1, err + 7 + (5*NumBits(iter) + 3)/4 - NumBits(k));
      long t;
      t = 1;
      while (!ErrBoundTest(k, t, err1))
         t++;

      ZZ W;
      long MR_passed = 1;

      long i;
      for (i = 1; i <= t; i++) {
         do {
            RandomBnd(W, n);
         } while (W == 0);
         // W == 0 is not a useful candidate witness!

         if (MillerWitness(n, W)) {
            MR_passed = 0;
            break;
         }
      }

      if (MR_passed) break;
   }
}

long GenGermainPrime_long(long k, long err)
{
   if (k >= NTL_BITS_PER_LONG-1)
      ResourceError("GenGermainPrime_long: length too long");

   ZZ n;
   GenGermainPrime(n, k, err);
   return to_long(n);
}

NTL_END_IMPL


/*
LICENSE: see isogeometric_application/LICENSE.txt
*/

//
//   Project Name:        Kratos
//   Last modified by:    $Author: hbui $
//   Date:                $Date: Nov 24, 2017 $
//   Revision:            $Revision: 1.0 $
//
//

// System includes
#include <string>

// External includes
#include <boost/foreach.hpp>
#include <boost/python.hpp>
#include <boost/python/stl_iterator.hpp>
#include <boost/python/operators.hpp>

// Project includes
#include "includes/define.h"
#include "python/pointer_vector_set_python_interface.h"
#include "custom_utilities/patch.h"
#include "custom_utilities/control_grid_utility.h"
#include "custom_utilities/hbsplines/deprecated_hb_mesh.h"
#include "custom_utilities/hbsplines/hbsplines_basis_function.h"
#include "custom_utilities/hbsplines/hbsplines_fespace.h"
#include "custom_utilities/hbsplines/hbsplines_patch_utility.h"
#include "custom_utilities/hbsplines/hbsplines_refinement_utility.h"
#include "custom_utilities/import_export/multi_hbsplines_patch_matlab_exporter.h"
#include "custom_python/iga_define_python.h"
#include "custom_python/add_hbsplines_to_python.h"
#include "custom_python/add_point_based_control_grid_to_python.h"
#include "custom_python/add_import_export_to_python.h"

namespace Kratos
{

namespace Python
{

using namespace boost::python;

////////////////////////////////////////

// template<int TDim>
// std::size_t HBSplinesBasisFunction_GetId(HBSplinesBasisFunction<TDim>& rDummy)
// {
//     return rDummy.Id();
// }

template<int TDim>
void HBSplinesBasisFunction_SetId(HBSplinesBasisFunction<TDim>& rDummy, std::size_t Id)
{
    // DO NOTHING
}

template<int TDim>
std::size_t HBSplinesBasisFunction_GetLevel(HBSplinesBasisFunction<TDim>& rDummy)
{
    return rDummy.Level();
}

// template<int TDim>
// std::size_t HBSplinesBasisFunction_GetEquationId(HBSplinesBasisFunction<TDim>& rDummy)
// {
//     return rDummy.EquationId();
// }

// template<int TDim>
// void HBSplinesBasisFunction_SetEquationId(HBSplinesBasisFunction<TDim>& rDummy, std::size_t EquationId)
// {
//     rDummy.SetEquationId(EquationId);
// }

template<int TDim>
boost::python::list HBSplinesFESpace_ExtractBoundaryBfsByFlag(HBSplinesFESpace<TDim>& rDummy, std::size_t boundary_id)
{
    typedef typename HBSplinesFESpace<TDim>::bf_t bf_t;

    std::vector<bf_t> bf_list = rDummy.ExtractBoundaryBfsByFlag(boundary_id);

    boost::python::list Output;
    for (std::size_t i = 0; i < bf_list.size(); ++i)
        Output.append(bf_list[i]);

    return Output;
}

template<int TDim>
std::size_t HBSplinesFESpace_MaxLevel(HBSplinesFESpace<TDim>& rDummy)
{
    return rDummy.MaxLevel();
}

template<int TDim>
typename HBSplinesBasisFunction<TDim>::Pointer HBSplinesFESpace_GetItem(HBSplinesFESpace<TDim>& rDummy, std::size_t i)
{
    return rDummy[i];
}

////////////////////////////////////////

template<int TDim>
void HBSplinesPatchUtility_ListBoundaryBfs(HBSplinesPatchUtility& rDummy,
    typename HBSplinesFESpace<TDim>::Pointer pFESpace, BoundarySide side)
{
    rDummy.ListBoundaryBfs<TDim>(std::cout, pFESpace, side);
}

template<int TDim>
typename Patch<TDim>::Pointer HBSplinesPatchUtility_CreatePatchFromBSplines(HBSplinesPatchUtility& rDummy,
    typename Patch<TDim>::Pointer pPatch)
{
    return HBSplinesPatchUtility::CreatePatchFromBSplines<TDim>(pPatch);
}

template<int TDim>
typename HBSplinesFESpace<TDim>::bf_t HBSplinesPatchUtility_GetBfByEquationId(HBSplinesPatchUtility& rDummy,
    typename MultiPatch<TDim>::Pointer pMultiPatch, const std::size_t& EquationId)
{
    return rDummy.GetBfByEquationId<TDim>(pMultiPatch, EquationId);
}

template<int TDim>
void HBSplinesPatchUtility_ReportDuplicatedEquationId(HBSplinesPatchUtility& rDummy,
    typename MultiPatch<TDim>::Pointer pMultiPatch, const bool& throw_error)
{
    return rDummy.ReportDuplicatedEquationId<TDim>(pMultiPatch, throw_error);
}

////////////////////////////////////////

template<int TDim>
void HBSplinesRefinementUtility_Refine(HBSplinesRefinementUtility& rDummy,
        typename Patch<TDim>::Pointer pPatch, const std::size_t& Id, const int& EchoLevel)
{
    rDummy.Refine<TDim>(pPatch, Id, EchoLevel);
}

template<int TDim>
void HBSplinesRefinementUtility_RefineBf(HBSplinesRefinementUtility& rDummy,
        typename Patch<TDim>::Pointer pPatch, typename HBSplinesFESpace<TDim>::bf_t p_bf, const int& EchoLevel)
{
    rDummy.Refine<TDim>(pPatch, p_bf, EchoLevel);
}

template<int TDim>
void HBSplinesRefinementUtility_RefineWindow(HBSplinesRefinementUtility& rDummy,
        typename Patch<TDim>::Pointer pPatch, boost::python::list& window, const int& EchoLevel)
{
    std::vector<std::vector<double> > window_vector;
    std::size_t cnt1 = 0, cnt2 = 0;
    typedef boost::python::stl_input_iterator<boost::python::list> iterator_value_type;
    BOOST_FOREACH(const iterator_value_type::value_type& vect, std::make_pair(iterator_value_type(window), iterator_value_type() ) )
    {
        typedef boost::python::stl_input_iterator<double> iterator_value_type2;
        std::vector<double> win_vect;
        BOOST_FOREACH(const iterator_value_type2::value_type& v, std::make_pair(iterator_value_type2(vect), iterator_value_type2() ) )
        {
            win_vect.push_back(v);
        }
        window_vector.push_back(win_vect);
    }
    rDummy.RefineWindow<TDim>(pPatch, window_vector, EchoLevel);
}

template<int TDim>
void HBSplinesRefinementUtility_LinearDependencyRefine(HBSplinesRefinementUtility& rDummy,
        typename Patch<TDim>::Pointer pPatch, const std::size_t& refine_cycle, const int& EchoLevel)
{
    rDummy.LinearDependencyRefine<TDim>(pPatch, refine_cycle, EchoLevel);
}

////////////////////////////////////////

// template<typename TDataType, class TFESpaceType>
// typename ControlGrid<TDataType>::Pointer ControlGridUtility_CreatePointBasedControlGrid(
//         ControlGridUtility& rDummy,
//         const Variable<TDataType>& rVariable, typename TFESpaceType::Pointer pFESpace)
// {
//     return rDummy.CreatePointBasedControlGrid<TDataType, TFESpaceType>(rVariable, pFESpace);
// }

////////////////////////////////////////

template<int TDim>
void IsogeometricApplication_AddHBSplinesSpaceToPython()
{

    std::stringstream ss;

    ss.str(std::string());
    ss << "HBSplinesBasisFunction" << TDim << "D";
    class_<HBSplinesBasisFunction<TDim>, typename HBSplinesBasisFunction<TDim>::Pointer, boost::noncopyable>
    (ss.str().c_str(), init<const std::size_t&, const std::size_t&>())
    // .add_property("Id", HBSplinesBasisFunction_GetId<TDim>, HBSplinesBasisFunction_SetId<TDim>)
    .add_property("Id", Isogeometric_GetId<HBSplinesBasisFunction<TDim> >, HBSplinesBasisFunction_SetId<TDim>)
    // .add_property("EquationId", HBSplinesBasisFunction_GetEquationId<TDim>, HBSplinesBasisFunction_SetEquationId<TDim>)
    .add_property("EquationId", Isogeometric_GetEquationId<HBSplinesBasisFunction<TDim>>, Isogeometric_SetEquationId<HBSplinesBasisFunction<TDim>>)
    .def("Weight", &HBSplinesBasisFunction<TDim>::Weight)
    .def("Level", &HBSplinesBasisFunction_GetLevel<TDim>)
    .def(self_ns::str(self))
    ;

    ss.str(std::string());
    ss << "HBSplinesFESpace" << TDim << "D";
//    typename FESpace<TDim-1>::Pointer(HBSplinesFESpace<TDim>::*pointer_to_ConstructBoundaryFESpace1)(const BoundarySide& side) const = &HBSplinesFESpace<TDim>::ConstructBoundaryFESpace;
    // typename FESpace<TDim-1>::Pointer(HBSplinesFESpace<TDim>::*pointer_to_ConstructBoundaryFESpace2)(const BoundarySide& side, const BoundaryRotation& rotation) const = &HBSplinesFESpace<TDim>::ConstructBoundaryFESpace;
    class_<HBSplinesFESpace<TDim>, typename HBSplinesFESpace<TDim>::Pointer, bases<FESpace<TDim> >, boost::noncopyable>
    (ss.str().c_str(), init<>())
    .def("__getitem__", &HBSplinesFESpace_GetItem<TDim>)
    .def("GetBoundaryBfs", &HBSplinesFESpace_ExtractBoundaryBfsByFlag<TDim>) // deprecated
    .def("ExtractBoundaryBfsByFlag", &HBSplinesFESpace_ExtractBoundaryBfsByFlag<TDim>)
//    .def("ConstructBoundaryFESpace", pointer_to_ConstructBoundaryFESpace1)
    // .def("ConstructBoundaryFESpace", pointer_to_ConstructBoundaryFESpace2)
    .def("UpdateCells", &HBSplinesFESpace<TDim>::UpdateCells)
    .def("MaxLevel", &HBSplinesFESpace_MaxLevel<TDim>)
    .def("SetMaxLevel", &HBSplinesFESpace<TDim>::SetMaxLevel)
    .def("GetBfByEquationId", &HBSplinesFESpace<TDim>::pGetBfByEquationId)
    .def("HasBfByEquationId", &HBSplinesFESpace<TDim>::HasBfByEquationId)
    .def("HasBfById", &HBSplinesFESpace<TDim>::HasBfById)
    .def(self_ns::str(self))
    ;

    IsogeometricApplication_AddPointBasedControlGrid_Helper<Variable<double>, HBSplinesFESpace<TDim> >::Execute();
    IsogeometricApplication_AddPointBasedControlGrid_Helper<Variable<array_1d<double, 3> >, HBSplinesFESpace<TDim> >::Execute();
    IsogeometricApplication_AddPointBasedControlGrid_Helper<Variable<Vector>, HBSplinesFESpace<TDim> >::Execute();

    ////////////////////OLD H-SPLINES////////////////////////

    ss.str(std::string());
    ss << "DeprecatedHBMesh" << TDim << "D";
    class_<DeprecatedHBMesh<TDim>, bases<Patch<TDim> > >
    // class_<DeprecatedHBMesh<TDim>, typename DeprecatedHBMesh<TDim>::Pointer, bases<Patch<TDim> > >
    (ss.str().c_str(), init<const std::size_t&, const std::string&>())
    .def("SetEchoLevel", &DeprecatedHBMesh<TDim>::SetEchoLevel)
    .def("ReadMesh", &DeprecatedHBMesh<TDim>::ReadMesh)
    /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
    .def("Refine", &DeprecatedHBMesh<TDim>::Refine) // use this for debugging only, use RefineNodes and LinearDependencyRefine instead
    .def("RefineNodes", &DeprecatedHBMesh<TDim>::RefineNodes)
    .def("LinearDependencyRefine", &DeprecatedHBMesh<TDim>::LinearDependencyRefine)
    .def("BuildMesh", &DeprecatedHBMesh<TDim>::BuildMesh)
    /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
    .def("ExportCellTopology", &DeprecatedHBMesh<TDim>::ExportCellTopology)
    .def("ExportCellGeology", &DeprecatedHBMesh<TDim>::ExportCellGeology)
    //    .def("ExportRefinedDomain", &DeprecatedHBMesh<TDim>::ExportRefinedDomain)
    .def("ExportSupportDomain", &DeprecatedHBMesh<TDim>::ExportSupportDomain)
    .def("ExportMatlab", &DeprecatedHBMesh<TDim>::ExportMatlab)
    /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
    .def("ExportMDPA", &DeprecatedHBMesh<TDim>::ExportMDPA)
    .def("ExportMDPA2", &DeprecatedHBMesh<TDim>::ExportMDPA2)
    .def("ExportPostMDPA", &DeprecatedHBMesh<TDim>::ExportPostMDPA)
    .def("ExportCellGeologyAsPostMDPA", &DeprecatedHBMesh<TDim>::ExportCellGeologyAsPostMDPA)
    /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
    .def("PrintKnotVectors", &DeprecatedHBMesh<TDim>::PrintKnotVectors)
    .def("PrintCells", &DeprecatedHBMesh<TDim>::PrintCells)
    .def("PrintBasisFuncs", &DeprecatedHBMesh<TDim>::PrintBasisFuncs)
    .def("PrintRefinementHistory", &DeprecatedHBMesh<TDim>::PrintRefinementHistory)
    .def("CheckNestedSpace", &DeprecatedHBMesh<TDim>::CheckNestedSpace)
    .def(self_ns::str(self))
    ;

    ss.str(std::string());
    ss << "DeprecatedHBMesh" << TDim << "DPointer";
    class_<typename DeprecatedHBMesh<TDim>::Pointer>
    (ss.str().c_str(), init<typename DeprecatedHBMesh<TDim>::Pointer>())
    .def("GetReference", GetReference<DeprecatedHBMesh<TDim> >, return_value_policy<reference_existing_object>())
    .def(self_ns::str(self))
    ;

}

////////////////////////////////////////

void IsogeometricApplication_AddHBSplinesToPython()
{

    /////////////////////////////////////////////////////////////////
    ///////////////////////HIERARCHICAL BSplines/////////////////////
    /////////////////////////////////////////////////////////////////

    IsogeometricApplication_AddHBSplinesSpaceToPython<1>();
    IsogeometricApplication_AddHBSplinesSpaceToPython<2>();
    IsogeometricApplication_AddHBSplinesSpaceToPython<3>();

    class_<HBSplinesPatchUtility, HBSplinesPatchUtility::Pointer, boost::noncopyable>
    ("HBSplinesPatchUtility", init<>())
    .def("CreatePatchFromBSplines", &HBSplinesPatchUtility_CreatePatchFromBSplines<2>)
    .def("CreatePatchFromBSplines", &HBSplinesPatchUtility_CreatePatchFromBSplines<3>)
    .def("ListBoundaryBfs", &HBSplinesPatchUtility_ListBoundaryBfs<2>)
    .def("ListBoundaryBfs", &HBSplinesPatchUtility_ListBoundaryBfs<3>)
    .def("GetBfByEquationId", &HBSplinesPatchUtility_GetBfByEquationId<2>)
    .def("GetBfByEquationId", &HBSplinesPatchUtility_GetBfByEquationId<3>)
    .def("ReportDuplicatedEquationId", &HBSplinesPatchUtility_ReportDuplicatedEquationId<2>)
    .def("ReportDuplicatedEquationId", &HBSplinesPatchUtility_ReportDuplicatedEquationId<3>)
    .def(self_ns::str(self))
    ;

    class_<HBSplinesRefinementUtility, typename HBSplinesRefinementUtility::Pointer, boost::noncopyable>
    ("HBSplinesRefinementUtility", init<>())
    .def("Refine", &HBSplinesRefinementUtility_Refine<2>)
    .def("Refine", &HBSplinesRefinementUtility_Refine<3>)
    .def("Refine", &HBSplinesRefinementUtility_RefineBf<2>)
    .def("Refine", &HBSplinesRefinementUtility_RefineBf<3>)
    .def("RefineWindow", &HBSplinesRefinementUtility_RefineWindow<2>)
    .def("RefineWindow", &HBSplinesRefinementUtility_RefineWindow<3>)
    .def("LinearDependencyRefine", &HBSplinesRefinementUtility_LinearDependencyRefine<2>)
    .def("LinearDependencyRefine", &HBSplinesRefinementUtility_LinearDependencyRefine<3>)
    .def(self_ns::str(self))
    ;

    class_<MultiHBSplinesPatchMatlabExporter, MultiHBSplinesPatchMatlabExporter::Pointer, boost::noncopyable>
    ("MultiHBSplinesPatchMatlabExporter", init<>())
    .def("Export", &MultiPatchExporter_Export<1, MultiHBSplinesPatchMatlabExporter, Patch<1> >)
    .def("Export", &MultiPatchExporter_Export<2, MultiHBSplinesPatchMatlabExporter, Patch<2> >)
    .def("Export", &MultiPatchExporter_Export<3, MultiHBSplinesPatchMatlabExporter, Patch<3> >)
    .def("Export", &MultiPatchExporter_Export<1, MultiHBSplinesPatchMatlabExporter, MultiPatch<1> >)
    .def("Export", &MultiPatchExporter_Export<2, MultiHBSplinesPatchMatlabExporter, MultiPatch<2> >)
    .def("Export", &MultiPatchExporter_Export<3, MultiHBSplinesPatchMatlabExporter, MultiPatch<3> >)
    .def(self_ns::str(self))
    ;

}

}  // namespace Python.

} // Namespace Kratos


#ifndef ZSVM_SPHERICAL_ECG_JACOBI_CONTEXT_HPP
#define ZSVM_SPHERICAL_ECG_JACOBI_CONTEXT_HPP

// C++ standard library headers
#include <cstddef> // for std::size_t
#include <vector>

// Eigen linear algebra library headers
#include <Eigen/Core>

// Project-specific headers
#include "Restrict.hpp"
#include "JacobiCoordinates.hpp"
#include "PackedLinearAlgebra.hpp"
#include "Particle.hpp"
#include "Permutation.hpp"

namespace zsvm {

    template <typename T>
    class SphericalECGJacobiContext {

    private: // =============================================== MEMBER VARIABLES

        typedef Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic> MatrixXT;

        const std::size_t num_particles;
        const std::size_t num_pairs;
        const std::size_t num_permutations;
        const std::size_t matrix_size;
        const long long int space_dimension;
        const T dimension_factor;
        const T kinetic_factor;

        const std::vector<T> inverse_masses;
        const std::vector<T> weight_vectors;
        const std::vector<T> weight_matrices;
        const std::vector<T> charge_products;
        const std::vector<T> permutation_signs;
        const std::vector<T> permutation_matrices;

        std::vector<T> vax;
        std::vector<T> vbx;
        std::vector<T> vcx;
        std::vector<T> vdx;

        const packed_determinant_inverse_function<T> packed_determinant_inverse;
        const packed_kinetic_trace_function<T> packed_kinetic_trace;
        const packed_quadratic_form_function<T> packed_quadratic_form;
        const packed_permutation_conjugate_function<T> packed_permutation_conjugate;

    private: // ============================================ FACTORY CONSTRUCTOR

        T gamma(const T &x) {
            using std::tgamma;
            return tgamma(x);
        }

        explicit SphericalECGJacobiContext(
                const std::vector<T> &charges,
                std::size_t num_permutations,
                long long int space_dimension,
                const MatrixXT &inverse_mass_matrix,
                const MatrixXT &pairwise_weight_vectors,
                const std::vector<T> &permutation_sign_vector,
                const std::vector<MatrixXT> &permutation_matrix_vector)
                : num_particles(charges.size()),
                  num_pairs(num_particles * (num_particles - 1) / 2),
                  num_permutations(num_permutations),
                  matrix_size((num_particles - 1) * (num_particles - 1)),
                  space_dimension(space_dimension),
                  dimension_factor(gamma((space_dimension - static_cast<T>(1)) /
                                         static_cast<T>(2)) /
                                   gamma(space_dimension / static_cast<T>(2))),
                  kinetic_factor(space_dimension / (2 * dimension_factor)),
                  inverse_masses(num_particles - 1),
                  weight_vectors(num_pairs * (num_particles - 1)),
                  weight_matrices(num_pairs * num_pairs),
                  charge_products(num_pairs),
                  permutation_signs(num_permutations),
                  permutation_matrices(num_permutations * matrix_size),
                  vax(num_pairs),
                  vbx(num_pairs),
                  vcx(num_pairs),
                  vdx(num_pairs),
                  packed_determinant_inverse(
                          PackedDeterminantInverse<T>::functions()[
                                  num_particles - 2]),
                  packed_kinetic_trace(
                          PackedKineticTrace<T>::functions()[
                                  num_particles - 2]),
                  packed_quadratic_form(
                          PackedQuadraticForm<T>::functions()[
                                  num_particles - 2]),
                  packed_permutation_conjugate(
                          PackedPermutationConjugate<T>::functions()[
                                  num_particles - 2]) {
            auto inverse_mass_pointer = const_cast<T *>(inverse_masses.data());
            for (std::size_t i = 0; i < num_particles - 1; ++i) {
                inverse_mass_pointer[i] = inverse_mass_matrix(i, i);
            }
            auto weight_vector_pointer = const_cast<T *>(weight_vectors.data());
            for (std::size_t p = 0, k = 0; p < num_pairs; ++p) {
                for (std::size_t i = 0; i < num_particles - 1; ++i, ++k) {
                    weight_vector_pointer[k] = pairwise_weight_vectors(i, p);
                }
            }
            auto weight_matrix_pointer = const_cast<T *>(weight_matrices.data());
            for (std::size_t p = 0, k = 0; p < num_pairs; ++p) {
                for (std::size_t i = 0; i < num_particles - 1; ++i) {
                    for (std::size_t j = 0; j <= i; ++j, ++k) {
                        weight_matrix_pointer[k] =
                                pairwise_weight_vectors(i, p) *
                                pairwise_weight_vectors(j, p);
                    }
                }
            }
            auto charge_product_pointer = const_cast<T *>(charge_products.data());
            for (std::size_t i = 0, k = 0; i < num_particles - 1; ++i) {
                for (std::size_t j = i + 1; j < num_particles; ++j, ++k) {
                    charge_product_pointer[k] = charges[i] * charges[j];
                }
            }
            auto permutation_sign_pointer = const_cast<T *>(permutation_signs.data());
            for (std::size_t p = 0; p < num_permutations; ++p) {
                permutation_sign_pointer[p] = permutation_sign_vector[p];
            }
            auto permutation_matrix_pointer = const_cast<T *>(permutation_matrices.data());
            for (std::size_t p = 0, k = 0; p < num_permutations; ++p) {
                for (std::size_t i = 0; i < num_particles - 1; ++i) {
                    for (std::size_t j = 0; j < num_particles - 1; ++j, ++k) {
                        permutation_matrix_pointer[k] =
                                permutation_matrix_vector[p](j, i);
                    }
                }
            }
        }

    public: // =========================================== STATIC FACTORY METHOD

        static SphericalECGJacobiContext create(
                const std::vector<zsvm::Particle<T>> &particles,
                const std::string &mass_carrier,
                const std::string &charge_carrier,
                long long int space_dimension) {
            const std::size_t num_particles = particles.size();
            if (num_particles < 2) {
                throw std::invalid_argument(
                        "Attempted to construct SphericalECGJacobiContext "
                        "with fewer than 2 particles");
            }
            std::vector<T> masses(num_particles);
            std::vector<T> charges(num_particles);
            for (std::size_t i = 0; i < num_particles; ++i) {
                masses[i] = particles[i].carriers.at(mass_carrier);
                charges[i] = particles[i].carriers.at(charge_carrier);
            }
            const std::vector<std::vector<std::size_t>> allowed_permutations =
                    dznl::invariant_permutations(particles);
            std::vector<T> permutation_signs;
            for (const auto &permutation : allowed_permutations) {
                const std::size_t signature =
                        dznl::count_changes(particles, permutation) / 2 +
                        dznl::count_inversions(permutation);
                permutation_signs.push_back((signature % 2 == 0) ? +1 : -1);
            }
            return SphericalECGJacobiContext(
                    charges,
                    allowed_permutations.size(),
                    space_dimension,
                    jaco::reduced_inverse_mass_matrix(masses),
                    jaco::pairwise_weights(masses),
                    permutation_signs,
                    jaco::permutation_matrices(masses, allowed_permutations));
        }

    private: // ================================== MATRIX ELEMENT HELPER METHODS

        void matrix_element_kernel(
                T &RESTRICT overlap_kernel,
                T &RESTRICT hamiltonian_kernel) {
            using std::sqrt;
            T *RESTRICT const ax = vax.data();
            T *RESTRICT const bx = vbx.data();
            T *RESTRICT const cx = vcx.data();
            T *RESTRICT const dx = vdx.data();
            for (std::size_t i = 0; i < num_pairs; ++i) {
                cx[i] = ax[i] + bx[i];
            }
            overlap_kernel = half_inverse_pow(
                    packed_determinant_inverse(cx, dx), space_dimension);
            hamiltonian_kernel = kinetic_factor * packed_kinetic_trace(
                    ax, bx, dx, inverse_masses.data());
            for (std::size_t k = 0; k < num_pairs; ++k) {
                const T alpha = packed_quadratic_form(
                        dx, weight_vectors.data() + (num_particles - 1) * k);
                hamiltonian_kernel += charge_products[k] / sqrt(2 * alpha);
            }
            hamiltonian_kernel *= dimension_factor * overlap_kernel;
        }

    public: // ========================================== MATRIX ELEMENT METHODS

        void gaussian_parameter_matrix(
                const T *RESTRICT correlation_coefficients,
                T *RESTRICT result) const {
            using std::exp;
            for (std::size_t p = 0; p < num_pairs; ++p) { result[p] = 0; }
            for (std::size_t p = 0, k = 0; p < num_pairs; ++p) {
                const T c = exp(correlation_coefficients[p]);
                for (std::size_t q = 0; q < num_pairs; ++q, ++k) {
                    result[q] += c * weight_matrices[k];
                }
            }
        }

        void evaluate_matrix_elements(
                T &RESTRICT overlap_element,
                T &RESTRICT hamiltonian_element,
                const T *a, const T *b) {
            T *RESTRICT const ax = vax.data();
            T *RESTRICT const bx = vbx.data();
            overlap_element = hamiltonian_element = 0;
            T overlap_kernel, hamiltonian_kernel;
            for (std::size_t i = 0; i < num_permutations; ++i) {
                for (std::size_t j = 0; j < num_permutations; ++j) {
                    const T sign = permutation_signs[i] * permutation_signs[j];
                    packed_permutation_conjugate(
                            a, permutation_matrices.data() + i * matrix_size,
                            ax);
                    packed_permutation_conjugate(
                            b, permutation_matrices.data() + j * matrix_size,
                            bx);
                    matrix_element_kernel(overlap_kernel, hamiltonian_kernel);
                    overlap_element += sign * overlap_kernel;
                    hamiltonian_element += sign * hamiltonian_kernel;
                }
            }
        }

    }; // class SphericalECGJacobiContext

} // namespace zsvm

#endif // ZSVM_SPHERICAL_ECG_JACOBI_CONTEXT_HPP


#include <boost/graph/dijkstra_shortest_paths.hpp>
#include <boost/graph/kruskal_min_spanning_tree.hpp>
#include <boost/graph/connected_components.hpp>
#include <boost/graph/metric_tsp_approx.hpp>
#include "cPathFinder.h"
using namespace boost;

void cPathFinder::clear()
{
    myGraph.clear();
    myDirGraph.clear();
    myMaxNegCost = 0;
}

void cPathFinder::start(int start)
{
    myStart = start;
}
void cPathFinder::start(const std::string &start)
{
    myStart = find(start);
    if (myStart < 0)
        throw std::runtime_error("cPathFinder::bad start node");
}
int cPathFinder::start() const
{
    return myStart;
}

void cPathFinder::paths(int start)
{
    if (!myfDirected)
        pathsT(start, myGraph);
    else
        pathsT(start, myDirGraph);
}
template <typename T>
void cPathFinder::pathsT(int start, T &g)
{
    // run dijkstra algorithm
    myPred.clear();
    myDist.clear();
    myPred.resize(num_vertices(g));
    myDist.resize(num_vertices(g));
    dijkstra_shortest_paths(
        g,
        start,
        weight_map(get(&cEdge::myCost, g))
            .predecessor_map(boost::make_iterator_property_map(
                myPred.begin(), get(boost::vertex_index, g)))
            .distance_map(boost::make_iterator_property_map(
                myDist.begin(), get(boost::vertex_index, g))));
    // std::cout << "<-cPathFinder::path ";
}

void cPathFinder::path()
{
    if (myStart < 0)
        throw std::runtime_error("cPathFinder::path start node undefined");

    // run the Dijsktra algorithm
    paths(myStart);

    if (myEnd >= 0)

        // pick out the path from source to destination
        pathPick(myEnd);

    else
    {
        std::cout << "Hop count from root to every node:\n";
        for (int kv = 0; kv < num_vertices(myGraph); kv++)
        {
            if (kv == myStart)
                continue;
            pathPick(kv);
            //std::cout << pathText() << "\n";
            std::cout << nodeName(myStart) << " to "
                      << nodeName(kv) << " "
                      << myPath.size() - 1 << "\n";
        }
    }
}

int cPathFinder::distance(int end)
{
    if (0 > end || end >> (int)myDist.size())
        return -1;
    return myDist[end];
}

void cPathFinder::span()
{
    typedef graph_traits<graph_t>::edge_descriptor edge_t;
    std::vector<edge_t> spanning_tree;
    kruskal_minimum_spanning_tree(
        myGraph,
        std::back_inserter(spanning_tree),
        weight_map(get(&cEdge::myCost, myGraph)));

    std::cout << "spanning_tree " << spanning_tree.size() << "\n";
    mySpanCost = 0;
    mySpan.clear();
    for (auto e : spanning_tree)
    {
        mySpanCost += myGraph[e].myCost;
        std::vector<int> ve{
            (int)source(e, myGraph),
            (int)target(e, myGraph)};
        mySpan.push_back(ve);
    }
}

std::vector<int> cPathFinder::pathPick(int end)
{
    myPath.clear();
    // std::cout << "->cPathFinder::pathPick "
    //     << myStart <<" " << end << "\n";

    if (end < 0)
        throw std::runtime_error("cPathFinder::pathPick bad end node");
    if (myPred[end] == end)
        throw std::runtime_error("There is no path from " + std::to_string(myStart) + " to " + std::to_string(end));

    // pick out path, starting at goal and finishing at start
    myPath.push_back(end);
    int prev = end;
    while (1)
    {
        //std::cout << prev << " " << myPred[prev] << ", ";
        int next = myPred[prev];
        myPath.push_back(next);
        if (next == myStart)
            break;
        prev = next;
    }

    // reverse so path goes from start to goal
    std::reverse(myPath.begin(), myPath.end());

    return myPath;
}

void cPathFinder::tsp()
{
    if (myfDirected)
        throw std::runtime_error(
            "cPathFinder::tsp does not handle directed graphs");

    double len = 0; //length of the tour
    auto tour_visitor = make_tsp_tour_len_visitor(
        myGraph,
        std::back_inserter(myPath),
        len,
        get(&cEdge::myCost, myGraph));
    metric_tsp_approx(
        myGraph,
        get(&cEdge::myCost, myGraph),
        get(vertex_index, myGraph),
        tour_visitor);
    // metric_tsp_approx_from_vertex(
    //     myGraph,
    //     myStart,
    //     get(&cEdge::myCost, myGraph),
    //     get(vertex_index, myGraph),
    //     tour_visitor);
}

void cPathFinder::addLink(
    int u,
    int v,
    float cost)
{
    if (u < 0 || v < 0)
        throw std::runtime_error("cPathFinder::addLink bad node");
    if (!myfDirected)
        myGraph[add_edge(u, v, myGraph).first].myCost = cost;
    else
        myDirGraph[add_edge(u, v, myDirGraph).first].myCost = cost;
}

void cPathFinder::addLink(
    const std::string &su,
    const std::string &sv,
    float cost)
{
    addLink(
        findoradd(su),
        findoradd(sv),
        cost);
}

int cPathFinder::findoradd(const std::string &name)
{
    int n = find(name);
    if (n < 0)
        n = add_vertex(name, myGraph);
    return n;
}

int cPathFinder::addNode(const std::string &name)
{
    if (myfDirected)
        return add_vertex(name, myDirGraph);
    else
        return add_vertex(name, myGraph);
}

void cPathFinder::deleteNode(int n)
{
    if (myfDirected)
        return remove_vertex(n, myDirGraph);
    else
        return remove_vertex(n, myGraph);
}
int cPathFinder::find(const std::string &name)
{
    for (int n = 0; n < num_vertices(myGraph); n++)
    {
        //std::cout << myGraph[n].myName << " ";
        if (myGraph[n].myName == name)
        {
            return n;
        }
    }
    //std::cout << name << " not found\n";
    return -1;
}

int cPathFinder::nodeCount()
{
    if (!myfDirected)
        return num_vertices(myGraph);
    else
        return num_vertices(myDirGraph);
}
int cPathFinder::linkCount()
{
    if (!myfDirected)
        return num_edges(myGraph);
    else
        return num_edges(myDirGraph);
}

std::string cPathFinder::linksText()
{
    if (!myfDirected)
        return linksTextT(myGraph);
    else
        return linksTextT(myDirGraph);
}
template <typename T>
std::string cPathFinder::linksTextT(T &g)
{
    std::stringstream ss;
    typename graph_traits<T>::edge_iterator ei, ei_end;
    for (tie(ei, ei_end) = edges(g); ei != ei_end; ++ei)
    {
        std::string un = g[source(*ei, g)].myName;
        if (un == "???")
            un = std::to_string(source(*ei, g));
        std::string vn = g[target(*ei, g)].myName;
        if (vn == "???")
            vn = std::to_string(target(*ei, g));
        ss << "("
           << un << ","
           << vn << ","
           << g[*ei].myCost
           << ") ";
    }
    ss << "\n";
    return ss.str();
}

bool cPathFinder::IsAdjacent(int u, int v)
{
    if (u < 0 || v < 0)
        return false;
    return edge(u, v, myGraph).second;
}

bool cPathFinder::IsConnected()
{
    return (1 == islandCount());
}
int cPathFinder::islandCount()
{
    std::vector<int> component(boost::num_vertices(myGraph));
    return boost::connected_components(myGraph, &component[0]);
}

std::string cPathFinder::nodeName(int n) const
{
    std::string sn = myGraph[n].myName;
    if (sn == "???")
        sn = std::to_string(n);
    return sn;
}

std::string cPathFinder::nodeColor(int n) const
{
    return myGraph[n].myColor;
}
void cPathFinder::nodeColor(int n, const std::string &color)
{
    if (myfDirected)
        myDirGraph[n].myColor = color;
    else
        myGraph[n].myColor = color;
}
std::string cPathFinder::pathText()
{
    std::stringstream ss;
    for (auto n : myPath)
    {
        std::string sn;
        if (myfDirected)
        {
            sn = myDirGraph[n].myName;
        }
        else
        {
            sn = myGraph[n].myName;
        }
        if (sn == "???")
            sn = std::to_string(n);
        ss << sn << " -> ";
        //ss << std::to_string(n) << " -> ";
    }

    if (myPath.size())
    {
        //std::cout << "dbg " << myDist[myPath.back()] << " " << myMaxNegCost << " " << myPath.size() << "\n";
        ss << " Cost is "
           << myDist[myPath.back()] + myMaxNegCost * (myPath.size() - 1)
           << "\n";
    }

    return ss.str();
}

std::string cPathFinder::spanText()
{
    // std::cout << "spanText " << mySpan.size()
    //           << " cost " << mySpanCost << "\n";

    std::stringstream ss;
    for (auto e : mySpan)
    {
        ss << nodeName(e[0])
           << " - "
           << nodeName(e[1])
           << ", ";
    }
    ss << " cost " << mySpanCost << "\n";
    return ss.str();
}

void cPathFinder::makeCostsPositive(int cost)
{
    graph_traits<graph_t>::edge_iterator ei, ei_end;
    for (tie(ei, ei_end) = edges(myGraph); ei != ei_end; ++ei)
    {
        myGraph[*ei].myCost -= cost;
    }
    myMaxNegCost = cost;
}

void cPathFinder::makeComplete()
{
    for (int u = 0; u < linkCount(); u++)
    {
        for (int v = u + 1; v < linkCount(); v++)
        {
            if (!edge(u, v, myGraph).second)
                addLink(
                    u, v,
                    INT_MAX);
        }
    }
}

std::string cPathFinder::pathViz()
{
    return pathViz(myPath);
}

std::string cPathFinder::pathViz(
    const std::vector<int> &vp,
    bool all)
{
    if (myfDirected)
        return pathVizT(vp, all, myDirGraph);
    else
        return pathVizT(vp, all, myGraph);
}

template <typename T>
std::string cPathFinder::pathVizT(
    const std::vector<int> &vp,
    bool all,
    T &g)
{
    std::string graphvizgraph = "graph";
    std::string graphvizlink = "--";
    if (myfDirected)
    {
        graphvizgraph = "digraph";
        graphvizlink = "->";
    }

    std::stringstream f;
    f << graphvizgraph << " G {\n";
    for (int v = *vertices(g).first; v != *vertices(g).second; ++v)
    {
        f << g[v].myName
          << " [color=\"" << g[v].myColor << "\"  penwidth = 3.0 ];\n";
    }

    // loop over links
    typename graph_traits<T>::edge_iterator ei, ei_end;
    for (tie(ei, ei_end) = edges(g); ei != ei_end; ++ei)
    {
        // check if link between two nodes on path
        bool onpath = false;
        int src = source(*ei, g);
        int dst = target(*ei, g);
        auto pathItsrc = std::find(vp.begin(), vp.end(), src);
        auto pathItdst = std::find(vp.begin(), vp.end(), dst);
        if (pathItsrc != vp.end() && pathItdst != vp.end())
        {
            if (myfDirected)
            {
                if (pathItsrc == pathItdst - 1)
                    onpath = true;
            }
            else
            {
                if (pathItsrc == pathItdst + 1 || pathItsrc == pathItdst - 1)
                    onpath = true;
            }
        }

        if (all)
        {
            f << g[src].myName << graphvizlink
              << g[dst].myName << " ";
            if (onpath)
                f << "[color=\"red\"] ";
            f << ";\n";
        }
        else
        {
            if (onpath)
                f << g[src].myName << graphvizlink
                  << g[dst].myName << " ;\n";
        }
    }

    f << "}\n";
    return f.str();
}

std::string cPathFinder::spanViz(bool all)
{
    std::stringstream f;
    f << "graph G {\n";
    for (int v = *vertices(myGraph).first; v != *vertices(myGraph).second; ++v)
    {
        auto sn = myGraph[v].myName;
        int x = atoi(sn.substr(1).c_str());
        int y = atoi(sn.substr(sn.find("x") + 1).c_str());
        f << sn
          << " [pos=\""
          << std::to_string(x)
          << ","
          << std::to_string(y)
          << "\"];\n";
    }

    graph_traits<graph_t>::edge_iterator ei, ei_end;
    for (tie(ei, ei_end) = edges(myGraph); ei != ei_end; ++ei)
    {
        auto src = myGraph[source(*ei, myGraph)].myName;
        auto dst = myGraph[target(*ei, myGraph)].myName;
        bool span = false;
        for (auto &se : mySpan)
        {
            if (src == myGraph[se[0]].myName && dst == myGraph[se[1]].myName ||
                src == myGraph[se[1]].myName && dst == myGraph[se[0]].myName)
            {
                span = true;
                break;
            }
        }
        if (all)
        {
            f << src << "--"
              << dst << " ";
            if (span)
                f << "[color=\"red\"]";
            f << ";\n";
        }
        else if (span)
        {
            f << src << "--"
              << dst
              << " [color=\"red\"];\n";
        }
    }
    f << "}\n";
    return f.str();
}

void cPathFinder::cams()
{
    //     APPROXIMATION-VERTEX-COVER(G)=
    // C = ∅
    // E'= G.E

    // while E' ≠ ∅:
    //     let (u, v) be an arbitrary edge of E'
    //     C = C ∪ {u, v}
    //     remove from E' every edge incident on either u or v

    // remove all leaf nodes from C
    // return C

    // store indices of nodes that cover links
    std::set<int> setCover;

    // graph of links between covering nodes
    graph_t cover;

    // working copy on input graph
    auto work = myGraph;

    myPath.clear();

    graph_traits<graph_t>::out_edge_iterator ei, ei_end;

    // loop until all links are covered
    while (num_edges(work))
    {
        // select first link in working graph
        auto it = edges(work).first;
        int u = source(*it, work);
        int v = target(*it, work);

        // add  non leaf nodes on selected link to cover
        if (out_degree(u, myGraph) > 1)
            setCover.insert(u);
        if (out_degree(v, myGraph) > 1)
            setCover.insert(v);

        // remove all links that can be seen from new cover nodes
        for (boost::tie(ei, ei_end) = out_edges(u, work); ei != ei_end; ++ei)
        {
            remove_edge(*ei, work);
        }
        for (boost::tie(ei, ei_end) = out_edges(v, work); ei != ei_end; ++ei)
        {
            remove_edge(*ei, work);
        }
    }

    std::cout << "nodes that cover all vertices:\n";
    for (int u : setCover)
    {
        // exclude edge nodes, their link will be covered from other end
        std::cout << u << " ";
        myGraph[u].myColor = "red";
    }
    std::cout << "\n";
}

void cPathFinder::hills(
    const std::vector<std::vector<float>> &gheight)
{

    // cost links according to change in height they incur
    int rowCount = gheight.size();
    if (!rowCount)
        throw std::runtime_error(
            "cPathFinder::hills bad grid");
    int colCount = gheight[0].size();

    graph_traits<dir_graph_t>::edge_iterator ei, ei_end;
    for (boost::tie(ei, ei_end) = edges(myDirGraph); ei != ei_end; ++ei)
    {
        int source = boost::source(*ei, myDirGraph);
        int target = boost::target(*ei, myDirGraph);
        int srow = source / colCount;
        int scol = source - srow * colCount;
        int trow = target / colCount;
        int tcol = target - trow * colCount;
        float sh = gheight[srow][scol];
        float th = gheight[trow][tcol];
        float delta = th - sh;
        myDirGraph[*ei].myCost = 1 + delta * delta;
    }

    path();

    std::cout << "hills " << pathText() << "\n";
}

void cPathFinder::gsingh()
{
    //loop over source nodes
    for (int src = 0; src < nodeCount(); src++)
    {
        // Run Dijsktra
        myStart = src;
        paths(src);

        // loop over destination nodes
        for (int dst = 0; dst < nodeCount(); dst++)
        {
            if (src == dst)
                continue;

            // display paths that visit 5 nodes
            if (pathPick(dst).size() == 5)
                std::cout << pathText();
        }
    }
}
void cPathFinder::shaun()
{
    int MaxNegCost = 0;
    graph_traits<graph_t>::edge_iterator ei, ei_end;
    for (boost::tie(ei, ei_end) = edges(myGraph); ei != ei_end; ++ei)
    {
        int source = boost::source(*ei, myGraph);
        int target = boost::target(*ei, myGraph);
        int cost = atoi(myGraph[target].myColor.c_str()) - atoi(myGraph[source].myColor.c_str());
        myDirGraph[add_edge(source, target, myDirGraph).first].myCost = cost;
        myDirGraph[add_edge(target, source, myDirGraph).first].myCost = -cost;
        if (cost < MaxNegCost)
            MaxNegCost = cost;
        if (-cost < MaxNegCost)
            MaxNegCost = -cost;
    }
    graph_traits<dir_graph_t>::edge_iterator dei, dei_end;
    for (boost::tie(dei, dei_end) = edges(myDirGraph); dei != dei_end; ++dei)
    {
        std::cout << boost::source(*dei, myDirGraph) << " -> "
                  << boost::target(*dei, myDirGraph) << " cost "
                  << myDirGraph[*dei].myCost << " converts to ";

        myDirGraph[*dei].myCost -= MaxNegCost;
        myDirGraph[*dei].myCost += 2 * MaxNegCost;
        myDirGraph[*dei].myCost *= -1;

        std::cout
            << myDirGraph[*dei].myCost << "\n\n";
    }
    myfDirected = true;
    //tsp();
}


#include <iostream>
#include <cmath>
#include <fstream>
#include <functional>
#include <boost/exception/diagnostic_information.hpp>
#include <boost/lexical_cast.hpp>
#include "fft.hpp"
#include "potgen.hpp"
#include "fileIO.hpp"
#include "potgen_args.h"
#include "profiling.hpp"
#include "correlation.hpp"
#include "discretize.hpp"

int main(int argc, const char* argv[])
{
	parse_parameters(argc, argv);

	PGOptions opt;

    // Get options from command line
	opt.randomSeed         = pargs::seed;
	opt.maxDerivativeOrder = pargs::derivative_order;
	opt.corrlength         = pargs::correlation_length;
	opt.numThreads         = pargs::threads;
	opt.verbose            = pargs::print_profile;

	// check that dimension is valid
	if( pargs::dim < 1 || pargs::dim > 3)
	{
		std::cerr << "invalid dimension " << pargs::dim << " specified\n";
		return EXIT_FAILURE;
	}

    try
	{
		// create output file, so if sth goes wrong we do not need to wait for the computation to finish
		// to issue an error
		std::fstream save(pargs::potential_outfile, std::fstream::out | std::fstream::binary);
		if( !save.good())
		{
			std::cerr << "could not open result file " << pargs::potential_outfile << " " << std::strerror(errno) << "\n";
			return EXIT_FAILURE;
		}

		// generate the correlation function
		opt.cor_fun = makeCorrelation( pargs::correlation_function, pargs::correlation_length, pargs::correlation_trafo);

		std::vector<std::size_t> extents(pargs::size.begin(), pargs::size.end());
		if(extents.size() == 1)
			extents.resize( pargs::dim, extents[0] );

		if( extents.size() != pargs::dim )
		{
			std::cerr << "Invalid number of size factors\n";
			exit(EXIT_FAILURE);
		}

		// debug output
		std::cout << "generate potential of size " << extents[0];
		for(int i = 1; i < pargs::dim; ++i)
			std::cout << "x"<<extents[i];
		std::cout << "\n";

		// make support area
		// we use the same aspect ratio as for the extents
		std::vector<double> support(pargs::dim);
		double min_ext = *std::min_element( extents.begin(), extents.end() );
		for(int i = 0; i < pargs::dim; ++i)
		{
			support[i] = (double)extents[i] / min_ext;
		}

		if(pargs::correlation_only)
		{
			auto grid = discretizeFunctionForFFT(extents, support, opt.cor_fun);

            // convert to real
            default_grid real_pot(extents, TransformationType::FFT_INDEX);

            auto it = real_pot.begin();
            for(const auto& value : grid)
            {
                *it = std::real(value);
                ++it;
            }

			std::cout << "saving correlation to " << pargs::potential_outfile << "\n";
            real_pot.dump(save);
			save.close();
		}
		else
		{
			auto pot = generatePotential(extents, support, opt);

			auto &pot_data = pot.getPotential();

			// control result
			std::cout << "Avg: " << std::accumulate(pot_data.begin(), pot_data.end(), 0.0) / (double) (pot_data.size())
					  << "\n";

			double variance = 0;
			double min = 0;
			double max = 0;;
			for (auto val : pot_data) {
				if (val > max) max = val;
				if (val < min) min = val;
				variance += val * val / pot_data.size();
			}

			std::cout << "Var: " << variance << "\n";

			pot.setStrength(pargs::strength);

			std::cout << "saving potential to " << pargs::potential_outfile << "\n";
			char write_buffer[1024 * 512];
			save.rdbuf()->pubsetbuf(write_buffer, sizeof(write_buffer));
			pot.writeToFile(save);
			save.close();
		}

		if( !pargs::no_wisdom )
		{
			saveFFTWisdom();
		}

		// profiling output
		if( pargs::print_profile )
		{
			ProfileRecord::print_profiling_data();
		}
	} catch ( boost::exception& e )
	{
		std::cerr << "an exception occurred: " << boost::diagnostic_information(e) << "\n";
		return 1;
	}
}


/**
 * \copyright
 * Copyright (c) 2012-2017, OpenGeoSys Community (http://www.opengeosys.org)
 *            Distributed under a Modified BSD License.
 *              See accompanying file LICENSE.txt or
 *              http://www.opengeosys.org/project/license
 */

#include "LevelSetFunction.h"

#include <boost/math/special_functions/sign.hpp>

#include "FractureProperty.h"

namespace
{
// Heaviside step function
inline double Heaviside(double v)
{
    return (v < 0.0) ? 0.0 : 1.0;
}

} // no named namespace

namespace ProcessLib
{
namespace LIE
{

double calculateLevelSetFunction(
        FractureProperty const& frac, double const* x_)
{
    Eigen::Map<Eigen::Vector3d const> x(x_, 3);
    return Heaviside(
                boost::math::sign(
                    frac.normal_vector.dot(x - frac.point_on_fracture)));
}

} // LIE
} // ProcessLib


#include <vector>
#include <iostream>
#include <cstdlib>
#include <cassert>
#include <fstream>
#include <sstream>
#include <algorithm>
#include <chrono>
#include <ctime>

#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/foreach.hpp>

using boost::property_tree::ptree;
using boost::property_tree::read_json;

template <typename T>
std::vector<T> as_vector(ptree const &pt, ptree::key_type const &key)
{
	std::vector<T> r;
	for (auto &item : pt.get_child(key))
		r.push_back(item.second.get_value<T>());
	return r;
}

template <typename T>
std::vector<std::vector<T>> asLayerVector(ptree const &pt, ptree::key_type const &key)
{
	std::vector<std::vector<T>> r;
	for (auto &item : pt.get_child(key))
	{
		std::vector<T> tmp;
		tmp.clear();
		for (auto &connectionWeight : item.second)
			tmp.push_back(connectionWeight.second.get_value<T>());
		r.push_back(tmp);
	}
	return r;
}

template <typename T>
void showVectorVals(std::string label, std::vector<T> &v)
{
	std::cout << label << " ";
	for (unsigned i = 0; i < v.size(); ++i)
	{
		std::cout << v[i] << " ";
	}
	std::cout << std::endl;
}

struct Connection
{
	double weight;
};

class Neuron;

typedef std::vector<Neuron> Layer;

// ****************** class Neuron ******************

class Neuron
{
  public:
	Neuron(unsigned numOutputs, unsigned myIndex);
	Neuron(std::vector<float> connectionWeights, unsigned myIndex);
	void setOutputVal(double val) { m_outputVal = val; }
	double getOutputVal(void) const { return m_outputVal; }
	void feedForward(const Layer &prevLayer, std::string);

  private:
	static double transferFunction(double x, std::string actFunc);
	// randomWeight: 0 - 1
	static double randomWeight(void) { return rand() / double(RAND_MAX); }
	double m_outputVal;
	std::vector<Connection> m_outputWeights;
	unsigned m_myIndex;
};

double Neuron::transferFunction(double x, std::string actFunc = "relu")
{
	if (actFunc == "relu")
	{
		return std::max(0.0, x);
	}
	else if (actFunc == "linear")
	{
		return x;
	}
}

void Neuron::feedForward(const Layer &prevLayer, std::string actFunc = "relu")
{
	double sum = 0.0;

	// Sum the previous layer's outputs (which are our inputs)
	// Include the bias node from the previous layer.

	for (unsigned n = 0; n < prevLayer.size(); ++n)
	{
		sum += prevLayer[n].getOutputVal() *
			   prevLayer[n].m_outputWeights[m_myIndex].weight;
	}

	m_outputVal = Neuron::transferFunction(sum, actFunc);
}

Neuron::Neuron(unsigned numOutputs, unsigned myIndex)
{
	for (unsigned c = 0; c < numOutputs; ++c)
	{
		m_outputWeights.push_back(Connection());
		m_outputWeights.back().weight = randomWeight();
	}

	m_myIndex = myIndex;
}

Neuron::Neuron(std::vector<float> connectionWeights, unsigned myIndex)
{
	for (auto &v : connectionWeights)
	{
		m_outputWeights.push_back(Connection());
		m_outputWeights.back().weight = v;
	}

	m_myIndex = myIndex;
}

// ****************** class Net ******************
class Net
{
  public:
	Net(const std::vector<std::vector<std::vector<float>>> &modelWeights);
	void feedForward(const std::vector<double> &inputVals);
	void getResults(std::vector<double> &resultVals) const;
	bool infer(std::vector<double> &data_in, std::vector<double> &out);

  private:
	std::vector<Layer> m_layers; //m_layers[layerNum][neuronNum]
};

void Net::getResults(std::vector<double> &resultVals) const
{
	resultVals.clear();

	for (unsigned n = 0; n < m_layers.back().size(); ++n)
	{
		resultVals.push_back(m_layers.back()[n].getOutputVal());
	}
}

void Net::feedForward(const std::vector<double> &inputVals)
{
	// Check the num of inputVals equal to neuronnum expect bias
	assert(inputVals.size() == m_layers[0].size() - 1);

	// Assign {latch} the input values into the input neurons
	for (unsigned i = 0; i < inputVals.size(); ++i)
	{
		m_layers[0][i].setOutputVal(inputVals[i]);
	}

	// Forward propagate
	for (unsigned layerNum = 1; layerNum < m_layers.size() - 1; ++layerNum)
	{
		Layer &prevLayer = m_layers[layerNum - 1];
		for (unsigned n = 0; n < m_layers[layerNum].size() - 1; ++n)
		{
			// std::cout << "n:" << n << '\n';
			m_layers[layerNum][n].feedForward(prevLayer, "relu");
		}
	}
	//output layer
	{
		unsigned linearOutputLayer = m_layers.size() - 1;
		Layer &prevLayer = m_layers[linearOutputLayer - 1];
		for (auto &n : m_layers[linearOutputLayer])
		{
			n.feedForward(prevLayer, "linear");
		}
	}
}

bool Net::infer(std::vector<double> &data_in, std::vector<double> &out)
{
	std::vector<double> inputVals, resultVals;
	int trainingPass = 0;
	auto t_start = std::chrono::high_resolution_clock::now();

	while (trainingPass < 1e6)
	{
		++trainingPass;
		// std::cout << "Pass:" << trainingPass << "\n";

		inputVals = data_in;

		this->feedForward(inputVals);

		// Collect the net's actual results:
		this->getResults(resultVals);
		// showVectorVals<double>("Outputs:", resultVals);

		// assert(targetVals.size() == topology.back());
	}
	auto t_end = std::chrono::high_resolution_clock::now();
	auto total = std::chrono::duration<float, std::milli>(t_end - t_start).count();
	std::cout << "totol time is " << total << "ms.\n";
	out = resultVals;
	return 0;
}

Net::Net(const std::vector<std::vector<std::vector<float>>> &modelWeight)
{
	unsigned numLayers = modelWeight.size();
	for (unsigned layerNum = 0; layerNum < numLayers; ++layerNum)
	{
		std::cout << "layer:" << layerNum << '\n';
		m_layers.push_back(Layer());

		std::vector<std::vector<float>> layerWeights = modelWeight[layerNum];
		for (unsigned neuronNum = 0; neuronNum < modelWeight[layerNum].size(); ++neuronNum)
		{
			m_layers.back().push_back(Neuron(layerWeights[neuronNum], neuronNum));
			// std::cout << "Mad a Neuron! Index:" << neuronNum << std::endl;
		}
		m_layers.back().back().setOutputVal(1.0);
	}
	//output layer
	std::cout << "layer:output\n";
	m_layers.push_back(Layer());
	for (unsigned neuronNum = 0; neuronNum < modelWeight[numLayers - 1][0].size(); ++neuronNum)
	{
		m_layers.back().push_back(Neuron(0, neuronNum));
		// std::cout << "Mad a Neuron! Index:" << neuronNum << std::endl;
	}
	m_layers.back().back().setOutputVal(1.0);

	assert(m_layers.size() == (modelWeight.size() + 1));
}

int main()
{
	std::ifstream myfile;
	myfile.open("data.json");
	// myfile.open("test.json");
	std::stringstream buffer;
	buffer << myfile.rdbuf();
	// std::cout << buffer.str() << '\n';

	ptree pt2;
	read_json(buffer, pt2);

	std::vector<std::vector<std::vector<float>>> modelWeights;
	std::vector<std::vector<float>> layer1, layer2, output;

	layer1 = asLayerVector<float>(pt2, "l1");
	layer2 = asLayerVector<float>(pt2, "l2");
	output = asLayerVector<float>(pt2, "output");

	modelWeights.push_back(layer1);
	modelWeights.push_back(layer2);
	modelWeights.push_back(output);

	Net myNet(modelWeights);

	std::vector<double> inputVals, resultVals;
	// int trainingPass = 0;
	// while (trainingPass < 1)
	// {
	// 	++trainingPass;
	// 	std::cout << "Pass:" << trainingPass << "\n";

	// 	inputVals = as_vector<double>(pt2, "in");
	// 	showVectorVals<double>(": Inputs :", inputVals);
	// 	myNet.feedForward(inputVals);

	// 	// Collect the net's actual results:
	// 	myNet.getResults(resultVals);
	// 	showVectorVals<double>("Outputs:", resultVals);

	// 	// assert(targetVals.size() == topology.back());
	// }
	inputVals = as_vector<double>(pt2, "in");
	showVectorVals<double>(": Inputs :", inputVals);
	myNet.infer(inputVals, resultVals);
	showVectorVals<double>("Outputs:", resultVals);

	std::cout << std::endl
			  << "Done" << std::endl;
}