#include <NTL/mat_ZZ_pE.h>

#include <NTL/new.h>

NTL_START_IMPL

void add(mat_ZZ_pE& X, const mat_ZZ_pE& A, const mat_ZZ_pE& B)
{
   long n = A.NumRows();
   long m = A.NumCols();

   if (B.NumRows() != n || B.NumCols() != m)
      Error("matrix add: dimension mismatch");

   X.SetDims(n, m);

   long i, j;
   for (i = 1; i <= n; i++)
      for (j = 1; j <= m; j++)
         add(X(i,j), A(i,j), B(i,j));
}

void sub(mat_ZZ_pE& X, const mat_ZZ_pE& A, const mat_ZZ_pE& B)
{
   long n = A.NumRows();
   long m = A.NumCols();

   if (B.NumRows() != n || B.NumCols() != m)
      Error("matrix sub: dimension mismatch");

   X.SetDims(n, m);

   long i, j;
   for (i = 1; i <= n; i++)
      for (j = 1; j <= m; j++)
         sub(X(i,j), A(i,j), B(i,j));
}

void negate(mat_ZZ_pE& X, const mat_ZZ_pE& A)
{
   long n = A.NumRows();
   long m = A.NumCols();

   X.SetDims(n, m);

   long i, j;
   for (i = 1; i <= n; i++)
      for (j = 1; j <= m; j++)
         negate(X(i,j), A(i,j));
}

void mul_aux(mat_ZZ_pE& X, const mat_ZZ_pE& A, const mat_ZZ_pE& B)
{
   long n = A.NumRows();
   long l = A.NumCols();
   long m = B.NumCols();

   if (l != B.NumRows())
      Error("matrix mul: dimension mismatch");

   X.SetDims(n, m);

   long i, j, k;
   ZZ_pX acc, tmp;

   for (i = 1; i <= n; i++) {
      for (j = 1; j <= m; j++) {
         clear(acc);
         for(k = 1; k <= l; k++) {
            mul(tmp, rep(A(i,k)), rep(B(k,j)));
            add(acc, acc, tmp);
         }
         conv(X(i,j), acc);
      }
   }
}

void mul(mat_ZZ_pE& X, const mat_ZZ_pE& A, const mat_ZZ_pE& B)
{
   if (&X == &A || &X == &B) {
      mat_ZZ_pE tmp;
      mul_aux(tmp, A, B);
      X = tmp;
   }
   else
      mul_aux(X, A, B);
}

static
void mul_aux(vec_ZZ_pE& x, const mat_ZZ_pE& A, const vec_ZZ_pE& b)
{
   long n = A.NumRows();
   long l = A.NumCols();

   if (l != b.length())
      Error("matrix mul: dimension mismatch");

   x.SetLength(n);

   long i, k;
   ZZ_pX acc, tmp;

   for (i = 1; i <= n; i++) {
      clear(acc);
      for (k = 1; k <= l; k++) {
         mul(tmp, rep(A(i,k)), rep(b(k)));
         add(acc, acc, tmp);
      }
      conv(x(i), acc);
   }
}

void mul(vec_ZZ_pE& x, const mat_ZZ_pE& A, const vec_ZZ_pE& b)
{
   if (&b == &x || A.position1(x) != -1) {
      vec_ZZ_pE tmp;
      mul_aux(tmp, A, b);
      x = tmp;
   }
   else
      mul_aux(x, A, b);
}

static
void mul_aux(vec_ZZ_pE& x, const vec_ZZ_pE& a, const mat_ZZ_pE& B)
{
   long n = B.NumRows();
   long l = B.NumCols();

   if (n != a.length())
      Error("matrix mul: dimension mismatch");

   x.SetLength(l);

   long i, k;
   ZZ_pX acc, tmp;

   for (i = 1; i <= l; i++) {
      clear(acc);
      for (k = 1; k <= n; k++) {
         mul(tmp, rep(a(k)), rep(B(k,i)));
         add(acc, acc, tmp);
      }
      conv(x(i), acc);
   }
}

void mul(vec_ZZ_pE& x, const vec_ZZ_pE& a, const mat_ZZ_pE& B)
{
   if (&a == &x) {
      vec_ZZ_pE tmp;
      mul_aux(tmp, a, B);
      x = tmp;
   }
   else
      mul_aux(x, a, B);

}

void ident(mat_ZZ_pE& X, long n)
{
   X.SetDims(n, n);
   long i, j;

   for (i = 1; i <= n; i++)
      for (j = 1; j <= n; j++)
         if (i == j)
            set(X(i, j));
         else
            clear(X(i, j));
}

void determinant(ZZ_pE& d, const mat_ZZ_pE& M_in)
{
   long k, n;
   long i, j;
   long pos;
   ZZ_pX t1, t2;
   ZZ_pX *x, *y;

   const ZZ_pXModulus& p = ZZ_pE::modulus();

   n = M_in.NumRows();

   if (M_in.NumCols() != n)
      Error("determinant: nonsquare matrix");

   if (n == 0) {
      set(d);
      return;
   }

   vec_ZZ_pX *M = NTL_NEW_OP vec_ZZ_pX[n];

   for (i = 0; i < n; i++) {
      M[i].SetLength(n);
      for (j = 0; j < n; j++) {
         M[i][j].rep.SetMaxLength(2*deg(p)-1);
         M[i][j] = rep(M_in[i][j]);
      }
   }

   ZZ_pX det;
   set(det);

   for (k = 0; k < n; k++) {
      pos = -1;
      for (i = k; i < n; i++) {
         rem(t1, M[i][k], p);
         M[i][k] = t1;
         if (pos == -1 && !IsZero(t1))
            pos = i;
      }

      if (pos != -1) {
         if (k != pos) {
            swap(M[pos], M[k]);
            negate(det, det);
         }

         MulMod(det, det, M[k][k], p);

         // make M[k, k] == -1 mod p, and make row k reduced

         InvMod(t1, M[k][k], p);
         negate(t1, t1);
         for (j = k+1; j < n; j++) {
            rem(t2, M[k][j], p);
            MulMod(M[k][j], t2, t1, p);
         }

         for (i = k+1; i < n; i++) {
            // M[i] = M[i] + M[k]*M[i,k]

            t1 = M[i][k];   // this is already reduced

            x = M[i].elts() + (k+1);
            y = M[k].elts() + (k+1);

            for (j = k+1; j < n; j++, x++, y++) {
               // *x = *x + (*y)*t1

               mul(t2, *y, t1);
               add(*x, *x, t2);
            }
         }
      }
      else {
         clear(d);
         goto done;
      }
   }

   conv(d, det);

done:
   delete[] M;
}

long IsIdent(const mat_ZZ_pE& A, long n)
{
   if (A.NumRows() != n || A.NumCols() != n)
      return 0;

   long i, j;

   for (i = 1; i <= n; i++)
      for (j = 1; j <= n; j++)
         if (i != j) {
            if (!IsZero(A(i, j))) return 0;
         }
         else {
            if (!IsOne(A(i, j))) return 0;
         }

   return 1;
}

void transpose(mat_ZZ_pE& X, const mat_ZZ_pE& A)
{
   long n = A.NumRows();
   long m = A.NumCols();

   long i, j;

   if (&X == & A) {
      if (n == m)
         for (i = 1; i <= n; i++)
            for (j = i+1; j <= n; j++)
               swap(X(i, j), X(j, i));
      else {
         mat_ZZ_pE tmp;
         tmp.SetDims(m, n);
         for (i = 1; i <= n; i++)
            for (j = 1; j <= m; j++)
               tmp(j, i) = A(i, j);
         X.kill();
         X = tmp;
      }
   }
   else {
      X.SetDims(m, n);
      for (i = 1; i <= n; i++)
         for (j = 1; j <= m; j++)
            X(j, i) = A(i, j);
   }
}

void solve(ZZ_pE& d, vec_ZZ_pE& X,
           const mat_ZZ_pE& A, const vec_ZZ_pE& b)

{
   long n = A.NumRows();
   if (A.NumCols() != n)
      Error("solve: nonsquare matrix");

   if (b.length() != n)
      Error("solve: dimension mismatch");

   if (n == 0) {
      set(d);
      X.SetLength(0);
      return;
   }

   long i, j, k, pos;
   ZZ_pX t1, t2;
   ZZ_pX *x, *y;

   const ZZ_pXModulus& p = ZZ_pE::modulus();

   vec_ZZ_pX *M = NTL_NEW_OP vec_ZZ_pX[n];

   for (i = 0; i < n; i++) {
      M[i].SetLength(n+1);
      for (j = 0; j < n; j++) {
         M[i][j].rep.SetMaxLength(2*deg(p)-1);
         M[i][j] = rep(A[j][i]);
      }
      M[i][n].rep.SetMaxLength(2*deg(p)-1);
      M[i][n] = rep(b[i]);
   }

   ZZ_pX det;
   set(det);

   for (k = 0; k < n; k++) {
      pos = -1;
      for (i = k; i < n; i++) {
         rem(t1, M[i][k], p);
         M[i][k] = t1;
         if (pos == -1 && !IsZero(t1)) {
            pos = i;
         }
      }

      if (pos != -1) {
         if (k != pos) {
            swap(M[pos], M[k]);
            negate(det, det);
         }

         MulMod(det, det, M[k][k], p);

         // make M[k, k] == -1 mod p, and make row k reduced

         InvMod(t1, M[k][k], p);
         negate(t1, t1);
         for (j = k+1; j <= n; j++) {
            rem(t2, M[k][j], p);
            MulMod(M[k][j], t2, t1, p);
         }

         for (i = k+1; i < n; i++) {
            // M[i] = M[i] + M[k]*M[i,k]

            t1 = M[i][k];   // this is already reduced

            x = M[i].elts() + (k+1);
            y = M[k].elts() + (k+1);

            for (j = k+1; j <= n; j++, x++, y++) {
               // *x = *x + (*y)*t1

               mul(t2, *y, t1);
               add(*x, *x, t2);
            }
         }
      }
      else {
         clear(d);
         goto done;
      }
   }

   X.SetLength(n);
   for (i = n-1; i >= 0; i--) {
      clear(t1);
      for (j = i+1; j < n; j++) {
         mul(t2, rep(X[j]), M[i][j]);
         add(t1, t1, t2);
      }
      sub(t1, t1, M[i][n]);
      conv(X[i], t1);
   }

   conv(d, det);

done:
   delete[] M;
}

void inv(ZZ_pE& d, mat_ZZ_pE& X, const mat_ZZ_pE& A)
{
   long n = A.NumRows();
   if (A.NumCols() != n)
      Error("inv: nonsquare matrix");

   if (n == 0) {
      set(d);
      X.SetDims(0, 0);
      return;
   }

   long i, j, k, pos;
   ZZ_pX t1, t2;
   ZZ_pX *x, *y;

   const ZZ_pXModulus& p = ZZ_pE::modulus();

   vec_ZZ_pX *M = NTL_NEW_OP vec_ZZ_pX[n];

   for (i = 0; i < n; i++) {
      M[i].SetLength(2*n);
      for (j = 0; j < n; j++) {
         M[i][j].rep.SetMaxLength(2*deg(p)-1);
         M[i][j] = rep(A[i][j]);
         M[i][n+j].rep.SetMaxLength(2*deg(p)-1);
         clear(M[i][n+j]);
      }
      set(M[i][n+i]);
   }

   ZZ_pX det;
   set(det);

   for (k = 0; k < n; k++) {
      pos = -1;
      for (i = k; i < n; i++) {
         rem(t1, M[i][k], p);
         M[i][k] = t1;
         if (pos == -1 && !IsZero(t1)) {
            pos = i;
         }
      }

      if (pos != -1) {
         if (k != pos) {
            swap(M[pos], M[k]);
            negate(det, det);
         }

         MulMod(det, det, M[k][k], p);

         // make M[k, k] == -1 mod p, and make row k reduced

         InvMod(t1, M[k][k], p);
         negate(t1, t1);
         for (j = k+1; j < 2*n; j++) {
            rem(t2, M[k][j], p);
            MulMod(M[k][j], t2, t1, p);
         }

         for (i = k+1; i < n; i++) {
            // M[i] = M[i] + M[k]*M[i,k]

            t1 = M[i][k];   // this is already reduced

            x = M[i].elts() + (k+1);
            y = M[k].elts() + (k+1);

            for (j = k+1; j < 2*n; j++, x++, y++) {
               // *x = *x + (*y)*t1

               mul(t2, *y, t1);
               add(*x, *x, t2);
            }
         }
      }
      else {
         clear(d);
         goto done;
      }
   }

   X.SetDims(n, n);
   for (k = 0; k < n; k++) {
      for (i = n-1; i >= 0; i--) {
         clear(t1);
         for (j = i+1; j < n; j++) {
            mul(t2, rep(X[j][k]), M[i][j]);
            add(t1, t1, t2);
         }
         sub(t1, t1, M[i][n+k]);
         conv(X[i][k], t1);
      }
   }

   conv(d, det);

done:
   delete[] M;
}

long gauss(mat_ZZ_pE& M_in, long w)
{
   long k, l;
   long i, j;
   long pos;
   ZZ_pX t1, t2, t3;
   ZZ_pX *x, *y;

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

   if (w < 0 || w > m)
      Error("gauss: bad args");

   const ZZ_pXModulus& p = ZZ_pE::modulus();

   vec_ZZ_pX *M = NTL_NEW_OP vec_ZZ_pX[n];

   for (i = 0; i < n; i++) {
      M[i].SetLength(m);
      for (j = 0; j < m; j++) {
         M[i][j].rep.SetMaxLength(2*deg(p)-1);
         M[i][j] = rep(M_in[i][j]);
      }
   }

   l = 0;
   for (k = 0; k < w && l < n; k++) {

      pos = -1;
      for (i = l; i < n; i++) {
         rem(t1, M[i][k], p);
         M[i][k] = t1;
         if (pos == -1 && !IsZero(t1)) {
            pos = i;
         }
      }

      if (pos != -1) {
         swap(M[pos], M[l]);

         InvMod(t3, M[l][k], p);
         negate(t3, t3);

         for (j = k+1; j < m; j++) {
            rem(M[l][j], M[l][j], p);
         }

         for (i = l+1; i < n; i++) {
            // M[i] = M[i] + M[l]*M[i,k]*t3

            MulMod(t1, M[i][k], t3, p);

            clear(M[i][k]);

            x = M[i].elts() + (k+1);
            y = M[l].elts() + (k+1);

            for (j = k+1; j < m; j++, x++, y++) {
               // *x = *x + (*y)*t1

               mul(t2, *y, t1);
               add(t2, t2, *x);
               *x = t2;
            }
         }

         l++;
      }
   }

   for (i = 0; i < n; i++)
      for (j = 0; j < m; j++)
         conv(M_in[i][j], M[i][j]);

   delete [] M;

   return l;
}

long gauss(mat_ZZ_pE& M)
{
   return gauss(M, M.NumCols());
}

void image(mat_ZZ_pE& X, const mat_ZZ_pE& A)
{
   mat_ZZ_pE M;
   M = A;
   long r = gauss(M);
   M.SetDims(r, M.NumCols());
   X = M;
}

void kernel(mat_ZZ_pE& X, const mat_ZZ_pE& A)
{
   long m = A.NumRows();
   long n = A.NumCols();

   mat_ZZ_pE M;
   long r;

   transpose(M, A);
   r = gauss(M);

   X.SetDims(m-r, m);

   long i, j, k, s;
   ZZ_pX t1, t2;

   ZZ_pE T3;

   vec_long D;
   D.SetLength(m);
   for (j = 0; j < m; j++) D[j] = -1;

   vec_ZZ_pE inverses;
   inverses.SetLength(m);

   j = -1;
   for (i = 0; i < r; i++) {
      do {
         j++;
      } while (IsZero(M[i][j]));

      D[j] = i;
      inv(inverses[j], M[i][j]);
   }

   for (k = 0; k < m-r; k++) {
      vec_ZZ_pE& v = X[k];
      long pos = 0;
      for (j = m-1; j >= 0; j--) {
         if (D[j] == -1) {
            if (pos == k)
               set(v[j]);
            else
               clear(v[j]);
            pos++;
         }
         else {
            i = D[j];

            clear(t1);

            for (s = j+1; s < m; s++) {
               mul(t2, rep(v[s]), rep(M[i][s]));
               add(t1, t1, t2);
            }

            conv(T3, t1);
            mul(T3, T3, inverses[j]);
            negate(v[j], T3);
         }
      }
   }
}

void mul(mat_ZZ_pE& X, const mat_ZZ_pE& A, const ZZ_pE& b_in)
{
   ZZ_pE b = b_in;
   long n = A.NumRows();
   long m = A.NumCols();

   X.SetDims(n, m);

   long i, j;
   for (i = 0; i < n; i++)
      for (j = 0; j < m; j++)
         mul(X[i][j], A[i][j], b);
}

void mul(mat_ZZ_pE& X, const mat_ZZ_pE& A, const ZZ_p& b_in)
{
   NTL_ZZ_pRegister(b);
   b = b_in;
   long n = A.NumRows();
   long m = A.NumCols();

   X.SetDims(n, m);

   long i, j;
   for (i = 0; i < n; i++)
      for (j = 0; j < m; j++)
         mul(X[i][j], A[i][j], b);
}

void mul(mat_ZZ_pE& X, const mat_ZZ_pE& A, long b_in)
{
   NTL_ZZ_pRegister(b);
   b = b_in;
   long n = A.NumRows();
   long m = A.NumCols();

   X.SetDims(n, m);

   long i, j;
   for (i = 0; i < n; i++)
      for (j = 0; j < m; j++)
         mul(X[i][j], A[i][j], b);
}

void diag(mat_ZZ_pE& X, long n, const ZZ_pE& d_in)
{
   ZZ_pE d = d_in;
   X.SetDims(n, n);
   long i, j;

   for (i = 1; i <= n; i++)
      for (j = 1; j <= n; j++)
         if (i == j)
            X(i, j) = d;
         else
            clear(X(i, j));
}

long IsDiag(const mat_ZZ_pE& A, long n, const ZZ_pE& d)
{
   if (A.NumRows() != n || A.NumCols() != n)
      return 0;

   long i, j;

   for (i = 1; i <= n; i++)
      for (j = 1; j <= n; j++)
         if (i != j) {
            if (!IsZero(A(i, j))) return 0;
         }
         else {
            if (A(i, j) != d) return 0;
         }

   return 1;
}

long IsZero(const mat_ZZ_pE& a)
{
   long n = a.NumRows();
   long i;

   for (i = 0; i < n; i++)
      if (!IsZero(a[i]))
         return 0;

   return 1;
}

void clear(mat_ZZ_pE& x)
{
   long n = x.NumRows();
   long i;
   for (i = 0; i < n; i++)
      clear(x[i]);
}

mat_ZZ_pE operator+(const mat_ZZ_pE& a, const mat_ZZ_pE& b)
{
   mat_ZZ_pE res;
   add(res, a, b);
   NTL_OPT_RETURN(mat_ZZ_pE, res);
}

mat_ZZ_pE operator*(const mat_ZZ_pE& a, const mat_ZZ_pE& b)
{
   mat_ZZ_pE res;
   mul_aux(res, a, b);
   NTL_OPT_RETURN(mat_ZZ_pE, res);
}

mat_ZZ_pE operator-(const mat_ZZ_pE& a, const mat_ZZ_pE& b)
{
   mat_ZZ_pE res;
   sub(res, a, b);
   NTL_OPT_RETURN(mat_ZZ_pE, res);
}

mat_ZZ_pE operator-(const mat_ZZ_pE& a)
{
   mat_ZZ_pE res;
   negate(res, a);
   NTL_OPT_RETURN(mat_ZZ_pE, res);
}

vec_ZZ_pE operator*(const mat_ZZ_pE& a, const vec_ZZ_pE& b)
{
   vec_ZZ_pE res;
   mul_aux(res, a, b);
   NTL_OPT_RETURN(vec_ZZ_pE, res);
}

vec_ZZ_pE operator*(const vec_ZZ_pE& a, const mat_ZZ_pE& b)
{
   vec_ZZ_pE res;
   mul_aux(res, a, b);
   NTL_OPT_RETURN(vec_ZZ_pE, res);
}

void inv(mat_ZZ_pE& X, const mat_ZZ_pE& A)
{
   ZZ_pE d;
   inv(d, X, A);
   if (d == 0) Error("inv: non-invertible matrix");
}

void power(mat_ZZ_pE& X, const mat_ZZ_pE& A, const ZZ& e)
{
   if (A.NumRows() != A.NumCols()) Error("power: non-square matrix");

   if (e == 0) {
      ident(X, A.NumRows());
      return;
   }

   mat_ZZ_pE T1, T2;
   long i, k;

   k = NumBits(e);
   T1 = A;

   for (i = k-2; i >= 0; i--) {
      sqr(T2, T1);
      if (bit(e, i))
         mul(T1, T2, A);
      else
         T1 = T2;
   }

   if (e < 0)
      inv(X, T1);
   else
      X = T1;
}

NTL_END_IMPL


#include <nav_msgs/Odometry.h>
#include <ros/ros.h>
#include <tf2/LinearMath/Quaternion.h>
#include <tf2_ros/static_transform_broadcaster.h>
#include <Eigen/Core>
#include <cmath>
#include <iostream>

using namespace Eigen;

double deg2rad(const double degree) { return degree * M_PI / 180.0; }

double rad2deg(const double radian) { return radian * 180.0 / M_PI; }

double angle_limit_pi(double angle)
{
  while (angle >= M_PI) { angle -= 2 * M_PI; }
  while (angle <= -M_PI) { angle += 2 * M_PI; }
  return angle;
}

double sdlab_uniform()
{
  double ret = ((double)rand() + 1.0) / ((double)RAND_MAX + 2.0);
  return ret;
}

// gauss noise
double gauss(double mu, double sigma)
{
  double z = std::sqrt(-2.0 * std::log(sdlab_uniform())) * std::sin(2.0 * M_PI * sdlab_uniform());
  return mu + sigma * z;
}

class FakeSensorPublisher
{
public:
  FakeSensorPublisher()
  : grund_truth(Matrix<double, 3, 1>::Zero()),
    odom(Matrix<double, 3, 1>::Zero()),
    gps(Matrix<double, 3, 1>::Zero())
  {
    Q << 0.1, 0, 0, deg2rad(30);
    Q = Q * Q;
    R << 2.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, deg2rad(5);
    R = R * R;

    ground_truth_pub_ = pnh_.advertise<nav_msgs::Odometry>("grund_truth", 10);
    odom_pub_ = pnh_.advertise<nav_msgs::Odometry>("odom", 10);
    gps_pub_ = pnh_.advertise<nav_msgs::Odometry>("gps", 10);

    previous_stamp_ = ros::Time::now();

    timer_ = nh_.createTimer(ros::Duration(0.01), &FakeSensorPublisher::timerCallback, this);
  }
  ~FakeSensorPublisher() {}
  nav_msgs::Odometry inputToNavMsgs(Matrix<double, 3, 1> pose_2d, Matrix<double, 2, 1> input)
  {
    nav_msgs::Odometry msg;
    msg.header.frame_id = "world";
    msg.child_frame_id = "odom";
    msg.pose.pose.position.x = pose_2d[0];
    msg.pose.pose.position.y = pose_2d[1];

    tf2::Quaternion quat;
    quat.setRPY(0.0, 0.0, angle_limit_pi(pose_2d[2]));
    msg.pose.pose.orientation.w = quat.w();
    msg.pose.pose.orientation.x = quat.x();
    msg.pose.pose.orientation.y = quat.y();
    msg.pose.pose.orientation.z = quat.z();
    msg.twist.twist.linear.x = input[0];
    msg.twist.twist.angular.z = input[1];

    return msg;
  }
  void timerCallback(const ros::TimerEvent & e)
  {
    ros::Time current_stamp = ros::Time::now();

    nav_msgs::Odometry grund_truth_msg;
    nav_msgs::Odometry odom_msg;
    nav_msgs::Odometry gps_msgs;

    Matrix<double, 2, 1> u(1.0, deg2rad(5));

    const double dt = current_stamp.toSec() - previous_stamp_.toSec();

    // ground truth
    ROS_INFO("delta time: %f", dt);
    grund_truth = motionModel(grund_truth, u, dt);
    grund_truth_msg = inputToNavMsgs(grund_truth, u);

    // dead recogning
    Matrix<double, 2, 1> ud = motionNoise(u, Q);
    odom = motionModel(odom, ud, dt);
    odom_msg = inputToNavMsgs(odom, ud);

    // observation
    Matrix<double, 3, 1> gps = observationNoise(grund_truth, R);
    gps_msgs = inputToNavMsgs(gps, Matrix<double, 2, 1>::Zero());

    ground_truth_pub_.publish(grund_truth_msg);
    odom_pub_.publish(odom_msg);
    gps_pub_.publish(gps_msgs);

    previous_stamp_ = current_stamp;
  }
  Matrix<double, 3, 1> motionModel(Matrix<double, 3, 1> x, Matrix<double, 2, 1> u, double dt)
  {
    Matrix<double, 3, 3> F = Matrix<double, 3, 3>::Identity();
    Matrix<double, 3, 2> B;
    B << dt * std::cos(x[2]), 0, dt * std::sin(x[2]), 0, 0, dt;

    x = F * x + B * u;
    x[2] = angle_limit_pi(x[2]);
    return x;
  }
  Matrix<double, 2, 1> motionNoise(Matrix<double, 2, 1> u, Matrix<double, 2, 2> Q)
  {
    Matrix<double, 2, 1> uw(gauss(0.0, Q(0, 0)), gauss(0.0, Q(1, 1)));
    return u + uw;
  }
  Matrix<double, 3, 1> observationNoise(Matrix<double, 3, 1> x, Matrix<double, 3, 3> R)
  {
    Matrix<double, 3, 1> xw(gauss(0.0, R(0, 0)), gauss(0.0, R(1, 1)), gauss(0.0, R(2, 2)));
    return x + xw;
  }

private:
  ros::NodeHandle nh_{};
  ros::NodeHandle pnh_{"~"};
  ros::Timer timer_;
  ros::Time previous_stamp_;

  ros::Publisher ground_truth_pub_;
  ros::Publisher odom_pub_;
  ros::Publisher gps_pub_;

  Matrix<double, 2, 2> Q;
  Matrix<double, 3, 3> R;
  Matrix<double, 3, 1> grund_truth;
  Matrix<double, 3, 1> odom;
  Matrix<double, 3, 1> gps;
};

int main(int argc, char ** argv)
{
  ros::init(argc, argv, "fake_sensor_publisher_node");
  FakeSensorPublisher fake_sensor_publisher;
  ros::spin();
  return 0;
}


#include "sampler.h"
#include "params.h"
#include <NTL/ZZ_pX.h>
#include <NTL/mat_ZZ_p.h>
#include <cassert>

void Sampler::get_ternary_vector(vector<int>& vec)
{

    for(int i=0; i<vec.size(); i++)
        vec[i] = ternary_sampler(rand_engine);
}

void Sampler::get_ternary_matrix(vector<vector<int>>& mat)
{

    for(int i=0; i<mat.size(); i++)
    {
        vector<int>& row = mat[i];
        get_ternary_vector(row);
    }
}

void Sampler::get_binary_vector(vector<int>& vec)
{
    for(int i=0; i<vec.size(); i++)
        vec[i] = binary_sampler(rand_engine);
}

void Sampler::get_uniform_vector(vector<int>& vec)
{
    for(int i=0; i<vec.size(); i++)
        vec[i] = mod_q_base_sampler(rand_engine);
}

void Sampler::get_uniform_matrix(vector<vector<int>>& mat)
{
    for(int i=0; i<mat.size(); i++)
    {
        vector<int>& row = mat[i];
        get_uniform_vector(row);
    }
}

void Sampler::get_gaussian_vector(vector<int>& vec, double st_dev)
{
    normal_distribution<double> gaussian_sampler(0.0, st_dev);
    for(size_t i=0; i<vec.size(); i++)
        vec[i] = static_cast<int>(round(gaussian_sampler(rand_engine)));
}

void Sampler::get_gaussian_matrix(vector<vector<int>>& mat, double st_dev)
{
    for(size_t i=0; i<mat.size(); i++)
    {
        vector<int>& row = mat[i];
        get_gaussian_vector(row, st_dev);
    }
}

void Sampler::get_invertible_vector(vector<int>& vec, vector<int>& vec_inv, int scale, int shift)
{
    //polynomial with the coefficient vector vec (will be generated later)
    ZZ_pX poly;
    //element of Z_(q_boot)
    ZZ_p coef;
    coef.init(ZZ(q_boot));
    //the inverse of poly modulo poly_mod (will be generated later)
    ZZ_pX inv_poly;
    //random sampling
    while (true)
    {
        //create the polynomial with the coefficient vector of the desired form
        SetCoeff(poly, 0, ternary_sampler(rand_engine)*scale + shift);
        for (size_t i = 1; i < vec.size(); i++)
        {
            coef = ternary_sampler(rand_engine)*scale;
            SetCoeff(poly, i, coef);
        }
        //test invertibility
        try
        {
            InvMod(inv_poly, poly, Param::get_def_poly());
            break;
        }
        catch(...)
        {
            cout << "Polynomial " << poly << " isn't a unit" << endl;
            continue;
        }
    }
    //cout << "Poly: " << poly << endl;
    //cout << "Poly inverse: " << inv_poly << endl;
    //extract the coefficient vector of poly
    int tmp_coef;
    for (int i = 0; i <= deg(poly); i++)
    {
        tmp_coef = conv<long>(poly[i]);
        if (tmp_coef > half_q_boot)
            tmp_coef -= q_boot;
        vec[i] = tmp_coef;
    }

    for (int i = 0; i <= deg(inv_poly); i++)
    {
        tmp_coef = conv<long>(inv_poly[i]);
        if (tmp_coef > half_q_boot)
            tmp_coef -= q_boot;
        vec_inv[i] = tmp_coef;
    }

    //cout << "Vector:" << vec << endl;
    //cout << "Inverse vector:" << vec_inv << endl;
}

void Sampler::get_invertible_matrix(vector<vector<int>>& mat, vector<vector<int>>& mat_inv, int scale, int shift)
{
    //check that the input matrices are squares
    assert(mat[0].size() == mat.size());
    assert(mat_inv[0].size() == mat_inv.size());
    //check that both input matrices have the same dimension
    assert(mat.size() == mat_inv.size());

    //number of rows of the input matrix
    int dim = mat.size();

    //element of Z_(q_boot)
    ZZ_p coef;
    coef.init(ZZ(param.q_base));

    //candidate matrix
    mat_ZZ_p tmp_mat(INIT_SIZE, dim, dim);

    //candidate inverse matrix
    mat_ZZ_p tmp_mat_inv(INIT_SIZE, dim, dim);

    //sampling and testing
    while (true)
    {
        //sampling
        for (int i = 0; i < dim; i++)
        {
            Vec<ZZ_p>& row = tmp_mat[i];
            for (int j = 0; j < dim; j++)
            {
                coef = ternary_sampler(rand_engine)*scale;
                if (i==j)
                    coef += ZZ_p(shift);
                row[j] = coef;
            }
        }
        //test invertibility
        try
        {
            inv(tmp_mat_inv, tmp_mat);
            break;
        }
        catch(...)
        {
            cout << "Matrix " << tmp_mat << " is singular" << endl;
            continue;
        }
    }
    //lift mod q representation to integers
    int tmp_coef;
    for (int i = 0; i < dim; i++)
    {
        Vec<ZZ_p>& tmp_row = tmp_mat[i];
        Vec<ZZ_p>& tmp_row_inv = tmp_mat_inv[i];
        vector<int>& row = mat[i];
        vector<int>& row_inv = mat_inv[i];
        for (int j = 0; j < dim; j++)
        {
            tmp_coef = conv<long>(tmp_row[j]);
            if (tmp_coef > param.half_q_base)
                tmp_coef -= param.q_base;
            row[j] = tmp_coef;

            tmp_coef = conv<long>(tmp_row_inv[j]);
            if (tmp_coef > param.half_q_base)
                tmp_coef -= param.q_base;
            row_inv[j] = tmp_coef;
        }
    }
}


#pragma once

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

#include <boost/math/constants/constants.hpp>
#include <boost/math/policies/error_handling.hpp>
#include <boost/math/special_functions/math_fwd.hpp>
#include <boost/math/tools/config.hpp>
#include <cmath>

#include "mpfr/import_std_math.hpp"

namespace boost {
namespace math {

// Recurrance relation for Hermite polynomials:
template <class T1, class T2, class T3>
inline typename tools::promote_args<T1, T2, T3>::type
hermiten_next(unsigned n, T1 x, T2 Hn, T3 Hnm1)
{
  typedef T1 numeric_t;

  const numeric_t fn = 2 / numeric_t(n + 1);
  const numeric_t fnm = numeric_t(n) / (n + 1);
  return ::math::sqrt(fn) * x * Hn - ::math::sqrt(fnm) * Hnm1;
}

namespace detail {

// Implement Hermite polynomials via recurrance:
template <class T>
T
hermiten_imp(unsigned n, T x)
{
  static const T pi = boost::math::constants::pi<T>();
  static const T pif = ::math::pow(pi, (T)-0.25);
  T p0 = pif;

  if (n == 0) return p0;

  T p1 = sqrt(T(2)) * x * pif;

  unsigned c = 1;

  while (c < n) {
    std::swap(p0, p1);
    p1 = hermiten_next(c, x, p0, p1);
    ++c;
  }
  return p1;
}

}  // namespace detail

template <class T, class Policy>
inline typename tools::promote_args<T>::type
hermiten(unsigned n, T x, const Policy&)
{
  typedef typename tools::promote_args<T>::type result_type;
  typedef typename policies::evaluation<result_type, Policy>::type value_type;
  return policies::checked_narrowing_cast<result_type, Policy>(
      detail::hermiten_imp(n, static_cast<value_type>(x)),
      "boost::math::hermiten<%1%>(unsigned, %1%)");
}

template <class T>
inline typename tools::promote_args<T>::type
hermiten(unsigned n, T x)
{
  return boost::math::hermiten(n, x, policies::policy<>());
}

}  // namespace math
}  // namespace boost


#pragma once

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

DFG_ROOT_NS_BEGIN { DFG_SUB_NS(math) {

// Returns sign of a value: 1 if value is positive, 0 if value is 0, -1 if value is < 0.
// Note: Behaviour for NaN's is unspecified.
// See also: std::signbit, std::copysign (C++11)
template <class T>
int sign(const T& val)
{
	return boost::math::sign(val);
	/*
	if (val > 0)
		return 1;
	else if (val == 0)
		return 0;
	else
		return -1;
		*/
}

template <class T>
auto signBit(const T& val) -> decltype(boost::math::signbit(val))
{
	return boost::math::signbit(val);
}

// Returns value whose absolute value is from val0 and sign from val1.
template <class T0, class T1>
auto signCopied(const T0& val0, const T1& val1) -> decltype(boost::math::copysign(val0, val1))
{
	return boost::math::copysign(val0, val1);
}

// Returns val with sign changed.
template <class T>
auto signChanged(const T& val) -> decltype(boost::math::changesign(val))
{
	return boost::math::changesign(val);
}

}} // module namespace


/*
 * position3.hpp
 *
 *  Created on: Dec 9, 2013
 *      Author: joost
 */

#ifndef POSITION3_HPP_
#define POSITION3_HPP_

#include <math.h>
#include <boost/serialization/serialization.hpp>
#include <boost/serialization/utility.hpp>
#include <sferes/dbg/dbg.hpp>

namespace sferes
{
  namespace gen
  {
    namespace spatial
    {
      class Pos
      {
        public:
          Pos() {
          }
          Pos(float x, float y, float z) : _x(x), _y(y), _z(z) {
          }
          float dist(const Pos& p) const
          {
            float x = _x - p._x;
            float y = _y - p._y;
            float z = _z - p._z;
            return sqrt(x * x + y * y + z * z);
          }
          float x() const { return _x; }
          float y() const { return _y; }
          float z() const { return _z; }

          void setX(const float& x){_x = x;};
          void setY(const float& y){_y = y;};
          void setZ(const float& z){_z = z;};

          void moveX(const float& dx){_x += dx;};
          void moveY(const float& dy){_y += dy;};
          void moveZ(const float& dz){_z += dz;};

          void translate(const float& dx, const float& dy, const float& dz){
        	  moveX(dx);
        	  moveY(dy);
        	  moveZ(dz);
          }

          float& operator [] (const size_t& index){
        	  switch(index){
        	  case 0:
        		  return _x;
        	  case 1:
        		  return _y;
        	  case 2:
        		  return _z;
        	  default: dbg::sentinel(DBG_HERE);
        	  }
        	  dbg::sentinel(DBG_HERE);
        	  throw 0;
          }

          Pos& operator *= (const float& scalar){
        	  _x*=scalar;
        	  _y*=scalar;
        	  _z*=scalar;
        	  return *this;
          }

          Pos& operator += (const float& scalar){
        	  _x+=scalar;
        	  _y+=scalar;
        	  _z+=scalar;
        	  return *this;
          }

          Pos& operator += (const Pos& other){
        	  _x+=other.x();
        	  _y+=other.y();
        	  _z+=other.z();
        	  return *this;
          }

          template<class Archive>
          void serialize(Archive& ar, const unsigned int version)
          {
            ar& BOOST_SERIALIZATION_NVP(_x);
            ar& BOOST_SERIALIZATION_NVP(_y);
            ar& BOOST_SERIALIZATION_NVP(_z);
          }
          bool operator == (const Pos &p)
          { return _x == p._x && _y == p._y && _z == p._z; }
        protected:
          float _x, _y, _z;
      };

      Pos operator+(const Pos& lhs, const Pos& rhs){
    	Pos result (lhs);
      	result+=rhs;
      	return result;
      }

      Pos operator*(const Pos& lhs, const float& rhs){
    	Pos result (lhs);
      	result*=rhs;
      	return result;
      }

      Pos operator*(const float& rhs, const Pos& lhs){
      	return lhs*rhs;
      }

      std::ostream& operator<<(std::ostream& is, const Pos& obj){
          is << obj.x() << " " << obj.y() << " " << obj.z();
          return is;
      }
    }
  }
}

#endif /* POSITION3_HPP_ */


// Filename: matrix_free_3.cpp (part of MTL4)

#include <iostream>
#include <cassert>
#include <boost/numeric/mtl/mtl.hpp>

struct poisson2D_dirichlet
{
    poisson2D_dirichlet(int m, int n) : m(m), n(n), s(m * n) {}

    template <typename VectorIn, typename VectorOut, typename Assign>
    void mult(const VectorIn& v, VectorOut& w, Assign) const
    {
	assert(int(size(v)) == m * n);
	assert(size(v) == size(w));

	// Inner domain
	for (int i= 1; i < m-1; i++)
	    for (int j= 1, k= i * n + j; j < n-1; j++, k++)
		Assign::apply(w[k], 4 * v[k] - v[k-n] - v[k+n] - v[k-1] - v[k+1]);

	// Upper border
	for (int j= 1; j < n-1; j++)
	    Assign::apply(w[j], 4 * v[j] - v[j+n] - v[j-1] - v[j+1]);

	// Lower border
	for (int j= 1, k= (m-1) * n + j; j < n-1; j++, k++)
	    Assign::apply(w[k], 4 * v[k] - v[k-n] - v[k-1] - v[k+1]);

	// Left border
	for (int i= 1, k= n; i < m-1; i++, k+= n)
	    Assign::apply(w[k], 4 * v[k] - v[k-n] - v[k+n] - v[k+1]);

	// Right border
	for (int i= 1, k= n+n-1; i < m-1; i++, k+= n)
	    Assign::apply(w[k], 4 * v[k] - v[k-n] - v[k+n] - v[k-1]);

	// Corners
	Assign::apply(w[0], 4 * v[0] - v[1] - v[n]);
	Assign::apply(w[n-1], 4 * v[n-1] - v[n-2] - v[2*n - 1]);
	Assign::apply(w[(m-1)*n], 4 * v[(m-1)*n] - v[(m-2)*n] - v[(m-1)*n+1]);
	Assign::apply(w[m*n-1], 4 * v[m*n-1] - v[m*n-2] - v[m*n-n-1]);
    }

    template <typename VectorIn>
    mtl::vec::mat_cvec_multiplier<poisson2D_dirichlet, VectorIn> operator*(const VectorIn& v) const
    {	return mtl::vec::mat_cvec_multiplier<poisson2D_dirichlet, VectorIn>(*this, v);    }

    int m, n, s;
};

inline std::size_t size(const poisson2D_dirichlet& A) { return A.s * A.s; }
inline std::size_t num_rows(const poisson2D_dirichlet& A) { return A.s; }
inline std::size_t num_cols(const poisson2D_dirichlet& A) { return A.s; }

namespace mtl {

    template <>
    struct Collection<poisson2D_dirichlet>
    {
	typedef double value_type;
	typedef int    size_type;
    };

    namespace ashape {
	template <> struct ashape_aux<poisson2D_dirichlet>
	{	typedef nonscal type;    };
    }
}

int main(int, char**)
{
    using namespace std;
    typedef mtl::dense_vector<double> vt;

    vt v(20);
    iota(v);
    cout << "v is " << v << endl;

    poisson2D_dirichlet A(4, 5);
    vt                  w2(20);

    w2= A * v;
    cout << "A * v is " << w2 << endl;

    w2+= A * v;
    cout << "w2+= A * v is " << w2 << endl;

    w2-= A * v;
    cout << "w2-= A * v is " << w2 << endl;

    return 0;
}