/*
 * gfanlib_circuittabletypeint.h
 *
 *  Created on: Apr 10, 2016
 *      Author: anders
 */

#ifndef GFANLIB_CIRCUITTABLEINT_H_
#define GFANLIB_CIRCUITTABLEINT_H_

#include <cstdint>
#include <exception>
#include <sstream>
#include <assert.h>

namespace gfan{

class MVMachineIntegerOverflow: public std::exception
{
  virtual const char* what() const throw()
  {
    return "Exception: Overflow (or possible future overflow) in 32/64 bit integers in tropical homotopy.";
  }
};

extern MVMachineIntegerOverflow MVMachineIntegerOverflow;

/*
 * The philosophy here is that if this class overflows, then the computation needs to be restarted. Therefore
 * all overflows must be caught.
 */
class CircuitTableInt32{
 public:
        class Divisor{
        public:
                int32_t v;
                int shift;
                int32_t multiplicativeInverse;
                Divisor(CircuitTableInt32 const &a)// for exact division
                { // A good place to read about these tricks seems to be the book "Hacker's Delight" by Warren.
                        v=a.v;
                        shift=0;
                        uint32_t t=v;
                        assert(t);
                        while(!(t&1)){t>>=        1;shift++;}
                        uint32_t inverse=t;
                        while(t*inverse!=1)inverse*=2-t*inverse;
                        multiplicativeInverse=inverse;
                }
        };
        class Double{
        public:
                int64_t v;
                Double():v(0){};
                Double(int64_t a):v(a){};
                Double &operator+=(Double a){v+=a.v;return *this;}
                Double &operator-=(Double a){v-=a.v;return *this;}
                CircuitTableInt32 castToSingle()const;
                bool isZero()const{return v==0;}
                bool isNegative()const{return v<0;}
                Double operator-()const{Double ret;ret.v=-v;return ret;}
                friend Double operator-(Double const &a, Double const &b){return Double(a.v-b.v);}
                friend Double operator+(Double const &a, Double const &b){return Double(a.v+b.v);}
                Double &addWithOverflowCheck(Double const &a)
                {
                        if(a.v<0 || v<0 || a.v>1000000000000000000 || v>1000000000000000000) throw MVMachineIntegerOverflow;
                        v+=a.v;
                        return *this;
                }
                std::string toString()const{std::stringstream s;s<<v;return s.str();}
        };
 private:
public:
        int32_t v;
        friend CircuitTableInt32 operator+(CircuitTableInt32 const &a, CircuitTableInt32 const &b){return CircuitTableInt32(a.v+b.v);}
        friend CircuitTableInt32 operator-(CircuitTableInt32 const &a, CircuitTableInt32 const &b){return CircuitTableInt32(a.v-b.v);}
        friend CircuitTableInt32 operator*(CircuitTableInt32 const &a, CircuitTableInt32 const &b){return CircuitTableInt32(a.v*b.v);}
 public:
        // In the code products of CircuitTableInt32 objects are summed. To avoid overflows one of the factors must "fit in half" and the "number of summands" may not exceed a certain number. The bounds are specified in the following functions:
        bool fitsInHalf()const{return v>-30000 && v<30000;}// Is it better to allow only non-negative numbers?
        static bool isSuitableSummationNumber(int numberOfSummands){return numberOfSummands<30000;}
        CircuitTableInt32(){v=0;};
        CircuitTableInt32(CircuitTableInt32 const &m):v(m.v){}
        CircuitTableInt32(int32_t val):v(val){};
        class CircuitTableInt32 &operator=(int32_t a){v=a;return *this;}
        CircuitTableInt32 operator-()const{CircuitTableInt32 ret;ret.v=-v;return ret;}
        CircuitTableInt32 &operator-=(CircuitTableInt32 a){v-=a.v;return *this;}
        CircuitTableInt32 &operator+=(CircuitTableInt32 a){v+=a.v;return *this;}
        CircuitTableInt32 &operator*=(CircuitTableInt32 a){v*=a.v;return *this;}
        friend bool operator<=(CircuitTableInt32 const &a, CircuitTableInt32 const &b){return a.v<=b.v;}
        friend bool operator==(CircuitTableInt32 const &a, CircuitTableInt32 const &b){return a.v==b.v;}
        bool isZero()const{return v==0;}
        bool isOne()const{return v==1;}
        bool isNonZero()const{return v!=0;}
        bool isNegative()const{return v<0;}
        bool isPositive()const{return v>0;}
        friend int determinantSign1(CircuitTableInt32 const &a, CircuitTableInt32 const &c, CircuitTableInt32 const &b, CircuitTableInt32 const &d)//NOTICE MIXED ORDER. MUST WORK BY EXTENDING
        {
                int64_t r=((int64_t)a.v)*((int64_t)c.v)-((int64_t)b.v)*((int64_t)d.v);
                if(r>0)return 1;
                if(r<0)return -1;
                return 0;
        }
        friend int determinantSign2(CircuitTableInt32::Double const &a, CircuitTableInt32 const &c, CircuitTableInt32::Double const &b, CircuitTableInt32 const &d)//NOTICE MIXED ORDER. MUST WORK BY EXTENDING
        {
                int64_t r1=(a.v)*((int64_t)c.v);
                int64_t r2=(b.v)*((int64_t)d.v);
                if(r1>r2)return 1;
                if(r1<r2)return -1;
                return 0;
        }
        std::string toString()const{std::stringstream s;s<<v;return s.str();}
        Double extend()const{Double ret;ret.v=v;return ret;}
        CircuitTableInt32 &maddWithOverflowChecking(CircuitTableInt32 const &a, CircuitTableInt32 const&b){Double t=this->extend();t+=extendedMultiplication(a,b);*this=t.castToSingle();return *this;}
        CircuitTableInt32 &msubWithOverflowChecking(CircuitTableInt32 const &a, CircuitTableInt32 const&b){Double s=this->extend();s-=extendedMultiplication(a,b);*this=s.castToSingle();return *this;}
        CircuitTableInt32 &subWithOverflowChecking(CircuitTableInt32 const &a){Double t=this->extend();t-=a.extend();*this=t.castToSingle();return *this;}
        CircuitTableInt32 &addWithOverflowChecking(CircuitTableInt32 const &a){Double t=this->extend();t+=a.extend();*this=t.castToSingle();return *this;}
        CircuitTableInt32 negatedWithOverflowChecking()const{return (-extend()).castToSingle();}
        friend Double extendedMultiplication(CircuitTableInt32 const &a, CircuitTableInt32 const &b){Double ret;ret.v=((int64_t)a.v)*((int64_t)b.v);return ret;}
        friend Double extendedMultiplication(CircuitTableInt32 const &a, int32_t b){Double ret;ret.v=((int64_t)a.v)*((int64_t)b);return ret;}//to be removed?
        friend CircuitTableInt32 min(CircuitTableInt32 const &a, CircuitTableInt32 const &b){return (a.v>=b.v)?b:a;}
        friend CircuitTableInt32 max(CircuitTableInt32 const &a, CircuitTableInt32 const &b){return (a.v<=b.v)?b:a;}
        friend CircuitTableInt32 negabs(CircuitTableInt32 const &a){return min(a,-a);}
        friend CircuitTableInt32 dotDiv(CircuitTableInt32 const &s, CircuitTableInt32 const &a, CircuitTableInt32 const &t, CircuitTableInt32 const &b, CircuitTableInt32::Divisor const &q)
        {
                return CircuitTableInt32(((((int32_t)(((uint64_t)(((int64_t)s.v)*((int64_t)a.v)+((int64_t)t.v)*((int64_t)b.v)))>>q.shift)))*q.multiplicativeInverse));
        }
        friend void dotDivAssign(CircuitTableInt32 &s, CircuitTableInt32 const &a, CircuitTableInt32 const &t, CircuitTableInt32 const &b, CircuitTableInt32::Divisor const &q)//as above but assigning to first argument. This helps the vectorizer.
        {
                s.v=((((int32_t)(((uint64_t)(((int64_t)s.v)*((int64_t)a.v)+((int64_t)t.v)*((int64_t)b.v)))>>q.shift)))*q.multiplicativeInverse);
        }
        static int MIN(int32_t a, int32_t b)
        {
                return (a<b)?a:b;
        }
        static int MAX(int32_t a, int32_t b)
        {
                return (a>b)?a:b;
        }
        static CircuitTableInt32 computeNegativeBound(CircuitTableInt32 * __restrict__ Ai, int w)
        {
                CircuitTableInt32 M=0;
                CircuitTableInt32 m=0;
                for(int j=0;j<w;j++)
                {
                        m.v=MIN(m.v,Ai[j].v);
                        M.v=MAX(M.v,Ai[j].v);
                }
                return min(m,-M);
        }
        static CircuitTableInt32 quickNoShiftBounded(CircuitTableInt32 * __restrict__ a, CircuitTableInt32 * __restrict__ b, CircuitTableInt32 s, CircuitTableInt32 t, CircuitTableInt32::Divisor denominatorDivisor,int c)
        {
                CircuitTableInt32 *aa=a;
                CircuitTableInt32 *bb=b;
                CircuitTableInt32 max=0;
            CircuitTableInt32 min=0;
            CircuitTableInt32 ret=0;
            for(int i=0;i<c;i++)
              {
                    aa[i].v=((s.v*denominatorDivisor.multiplicativeInverse)*aa[i].v+
                                    (t.v*denominatorDivisor.multiplicativeInverse)*bb[i].v);
                    min.v=MIN(min.v,aa[i].v);
                    max.v=MAX(max.v,aa[i].v);
              }
            if(-max<=min)min=-max;
            ret=min;
            return ret;
        }
        static CircuitTableInt32 dotDivVector(CircuitTableInt32 * __restrict__ aa, CircuitTableInt32 * __restrict__ bb, CircuitTableInt32 s, CircuitTableInt32 t, CircuitTableInt32::Divisor denominatorDivisor,int c, CircuitTableInt32 boundA, CircuitTableInt32 boundB)__attribute__ ((always_inline))//assigning to first argument. The return negative of the return value is an upper bound on the absolute values of the produced entries
        {
                while(((s.v|t.v)&1)==0 && denominatorDivisor.shift>0){s.v>>=1;t.v>>=1;denominatorDivisor.shift--;denominatorDivisor.v>>=1;}

                CircuitTableInt32 max=0;
                CircuitTableInt32 min=0;

          // This is a bound on the absolute value of the sums of products before dividing by the denominator
          // The calculation can overflow, but since we are casting to unsigned long, this is not a problem.
          uint64_t positiveResultBoundTimesD=(negabs(t).v*((int64_t)boundA.v)+negabs(s).v*((int64_t)boundB.v));

          /* The first case is the case where we know that the intermediate results fit in 32bit before shifting down.
           * In other words, the shifted-in bits of the results are equal.
           * In that case the intermediate result can be computed with 32bits.
           */
          if(positiveResultBoundTimesD<((((int64_t)0x40000000)*denominatorDivisor.v)>>denominatorDivisor.shift))//check this carefully
          {//FAST VERSION
                  // debug<<s.v<<" "<<t.v<<" "<<denominatorDivisor.v<<" "<<denominatorDivisor.shift<<"\n";
                  if(denominatorDivisor.shift==0)return quickNoShiftBounded(aa,bb,s,t,denominatorDivisor,c);
                  for(int i=0;i<c;i++)
                  {
                          aa[i].v=((s.v*denominatorDivisor.multiplicativeInverse)*aa[i].v+
                                          (t.v*denominatorDivisor.multiplicativeInverse)*bb[i].v)>>denominatorDivisor.shift;
                          min.v=MIN(min.v,aa[i].v);
                          max.v=MAX(max.v,aa[i].v);
                  }
                  if(-max<=min)min=-max;
                  return min;
          }
          else
          {
                  /* The next case would be to check if the result is known to fit in 32bit.
                   * In that case intermediate results would be handled in 64 bit,
                   * but the final result would not have to be checked for 32 bit overflows.
                   */
                  if(positiveResultBoundTimesD<((((int64_t)0x40000000)*denominatorDivisor.v)))
                  {
                          for(int i=0;i<c;i++)
                          {
                                  aa[i].v=((((int32_t)(((uint64_t)(((int64_t)s.v)*((int64_t)aa[i].v)+((int64_t)t.v)*((int64_t)bb[i].v)))>>denominatorDivisor.shift)))*denominatorDivisor.multiplicativeInverse);
                                  if(max<=aa[i])max=aa[i];
                                  if(aa[i]<=min)min=aa[i];
                          }
                          if(-max<=min)min=-max;
                  }
                  /* The last case would be to where we don't know that the results will fit in 32 bit.
                   * Since we need to compute max and min anyway, we can compute these quantities in 64bit
                   * and just check if they fit in 32bit at the end.
                   */
                  else
                  {
                          bool doesOverflow=(((uint32_t)t.v)==0x80000000);
                          int64_t min64=0;
                          int64_t max64=0;
                          for(int i=0;i<c;i++)
                          {
                                  // In the unlikely event that all the numbers to be multiplied are -2^31, the following code will overflow. Therefore the overflow flag was set as above.
                                  int64_t temp=(((int64_t)s.v)*((int64_t)aa[i].v)+((int64_t)t.v)*((int64_t)bb[i].v))/denominatorDivisor.v;
                                  if(max64<=temp)max64=temp;
                                  if(temp<=min64)min64=temp;
                                  aa[i].v=temp;
                          }
                          if(-max64<=min64)min64=-max64;
                          if(min64<=-0x7fffffff)doesOverflow=true;
                          if(doesOverflow)throw MVMachineIntegerOverflow;
                          min=min64;
                  }
          }
          return min;
        }
};

inline CircuitTableInt32 CircuitTableInt32::Double::castToSingle()const//casts and checks precission
{
        if(v>=0x7fffffff || -v>=0x7fffffff) throw MVMachineIntegerOverflow;
        return CircuitTableInt32(v);
}
}


#endif /* GFANLIB_CIRCUITTABLETYPEINT_H_ */
