/*
** Copyright (C) 2018 Martin Brain
**
** See the file LICENSE for licensing information.
*/

/*
** unpackedFloat.h
**
** Martin Brain
** martin.brain@cs.ox.ac.uk
** 03/06/14
**
** The working representation of a floating-point number.  This is
** from the packed representation in a few ways:
**
**  1. Explicit flags for NaN, Inf and Zero.
**  2. Significand is biased.
**  3. Hidden bit is explicit.
**  4. Subnormals are normalised.
**
** This makes numbers more uniform and makes it easier to implement
** compact and efficient algorithms.
*/

#include "symfpu/utils/common.h"
#include "symfpu/utils/properties.h"
#include "symfpu/utils/numberOfRoundingModes.h"

#include "symfpu/core/ite.h"
#include "symfpu/core/operations.h"

// For debugging only
#include <iostream>

#ifndef SYMFPU_UNPACKED_FLOAT
#define SYMFPU_UNPACKED_FLOAT

namespace symfpu {

  template<class t>
    class unpackedFloat {
  public :
    // Typedef the names from the traits for convenience
    typedef typename t::bwt bwt;
    typedef typename t::fpt fpt;
    typedef typename t::prop prop;
    typedef typename t::sbv sbv;
    typedef typename t::ubv ubv;


  protected :
    // TODO : protect these again
  public :
    prop nan;
    prop inf;
    prop zero;

    prop sign;
    sbv exponent;
    ubv significand;
  protected :

    // It is possible, but hopefully as difficult as possible to create,
    // via the constructor an invalid unpacked float

    // A piecewise / literal constructor using fpclass
    enum fpclass { FPCLASS_NAN, FPCLASS_INF, FPCLASS_ZERO, FPCLASS_NUMBER };

    unpackedFloat (const fpclass c, const prop &s, const sbv &exp, const ubv &signif) : 
      nan(c == FPCLASS_NAN), inf(c == FPCLASS_INF), zero(c == FPCLASS_ZERO),
      sign(s), exponent(exp), significand(signif)
      {}


    // Should only be used by ite
    friend ite<prop, unpackedFloat<t> >;

    // TODO : See above -- this should only be used by ite
  public :
    unpackedFloat (const prop &iteNaN, const prop &iteInf, const prop &iteZero,
		   const prop &iteSign, const sbv &iteExponent, const ubv &iteSignificand) :
      nan(iteNaN), inf(iteInf), zero(iteZero),
      sign(iteSign), exponent(iteExponent), significand(iteSignificand)
      {}
  private :

    // Used for special values
    // However this will also be passed through the operations, thus
    // if it is also a valid normal number then it will make proving
    // invariants easier.  In this case it is the value 1.0.

    static sbv defaultExponent(const fpt &fmt) {
      return sbv::zero(unpackedFloat<t>::exponentWidth(fmt));
    }

    static ubv defaultSignificand(const fpt &fmt) {
      bwt significandWidth = unpackedFloat<t>::significandWidth(fmt);

      return ubv::one(significandWidth) << ubv(significandWidth, (significandWidth - 1));
    }



  public :
    unpackedFloat (const prop &s, const sbv &exp, const ubv &signif) : 
      nan(false), inf(false), zero(false),
      sign(s), exponent(exp), significand(signif)
      {}

    unpackedFloat (const unpackedFloat<t> &old) :
      nan(old.nan), inf(old.inf), zero(old.zero),
      sign(old.sign), exponent(old.exponent), significand(old.significand)
      {}

    // Copy and over-write sign
    unpackedFloat (const unpackedFloat<t> &old, const prop &s) : 
      nan(old.nan), inf(old.inf), zero(old.zero),
      sign(ITE(old.nan, old.sign, s)), exponent(old.exponent), significand(old.significand)
      {}

    // Swap back-ends
      template <class s> friend class unpackedFloat;

    template <class s>
    unpackedFloat (const unpackedFloat<s> &old) :
      nan(old.nan), inf(old.inf), zero(old.zero),
      sign(old.sign), exponent(old.exponent), significand(old.significand)
      {}
      

    static unpackedFloat<t> makeZero(const fpt &fmt, const prop &s) {
      return unpackedFloat<t>(FPCLASS_ZERO, s, defaultExponent(fmt), defaultSignificand(fmt));
    }

    static unpackedFloat<t> makeInf(const fpt &fmt, const prop &s) {
      return unpackedFloat<t>(FPCLASS_INF, s, defaultExponent(fmt), defaultSignificand(fmt));
    }

    static unpackedFloat<t> makeNaN(const fpt &fmt) {
      return unpackedFloat<t>(FPCLASS_NAN, false, defaultExponent(fmt), defaultSignificand(fmt));
    }

    inline const prop & getNaN(void) const { return this->nan; }
    inline const prop & getInf(void) const { return this->inf; }
    inline const prop & getZero(void) const { return this->zero; }
    inline const prop & getSign(void) const { return this->sign; }
    inline const sbv & getExponent(void) const { return this->exponent; }
    inline const ubv & getSignificand(void) const { return this->significand; }

    //    inline unpackedFloat<t> changeSign(const prop &newSign) {
    //      return unpackedFloat<t>(*this, newSign);
    //    }




    // Get the number of bits in the unpacked format corresponding to a
    // given packed format.  These are the unpacked counter-parts of
    //  format.exponentWidth() and format.significandWidth()

    static bwt exponentWidth(const fpt &format) {

      // Note that there is one more exponent above 0 than there is
      // below.  This is the opposite of 2's compliment but this is not
      // a problem because the highest packed exponent corresponds to
      // inf and NaN and is thus does not need to be represented in the
      // unpacked format.
      // However we do need to increase it to allow subnormals (packed)
      // to be normalised.

      bwt width = format.exponentWidth();

      // Could be improved to remove overflow concerns
      uint64_t minimumExponent = ((1 << (width - 1)) - 2) + (format.significandWidth() - 1);

      // Increase width until even the smallest subnormal can be normalised
      while ((1UL << (width - 1)) < minimumExponent) {
	++width;
      }

      return width;
    }

    static bwt significandWidth(const fpt &format) {
      // Hidden bit is already included in the floating-point format
      return format.significandWidth();
    }




    // These should all evaluate to a literal value but are given as
    // sbv's to make their use easier and to avoid concerns of overflow.

    static sbv bias(const fpt &format) {
      bwt w(exponentWidth(format));
      sbv one(sbv::one(w));

      return (one << sbv(w,(format.exponentWidth() - 1))) - one;
    }

    
    static sbv maxNormalExponent(const fpt &format) {
      return bias(format);
    }

    static sbv minNormalExponent(const fpt &format) {
      return -(bias(format) - sbv::one(exponentWidth(format)));
    }

    static sbv maxSubnormalExponent(const fpt &format) {
      return -bias(format);
    }

    static sbv minSubnormalExponent(const fpt &format) {
      return maxSubnormalExponent(format) - sbv(exponentWidth(format),(significandWidth(format) - 2));
    } 

    // Note the different return type as this is used for iteration in remainder
    static bwt maximumExponentDifference(const fpt &format) {
      bwt maxNormalExp = (1ULL << (format.exponentWidth() - 1)) - 1;
      bwt minSubnormalExp = -maxNormalExp - (significandWidth(format) - 2);
      return maxNormalExp - minSubnormalExp;
    }
    
    // knownInFormat uses the format invariant to simplify the test
    inline prop inNormalRange(const fpt &format, const prop &knownInFormat) const {
      return ((minNormalExponent(format) <= exponent) &&
	      ((exponent <= maxNormalExponent(format) || knownInFormat)));
    }

    // knownInFormat uses the format invariant to simplify the test
    inline prop inSubnormalRange(const fpt &format, const prop &knownInFormat) const {
      // To share tests with the inNormalRange test...
      prop upperBound(!(minNormalExponent(format) <= exponent));
      INVARIANT(upperBound == (exponent <= maxSubnormalExponent(format)));

      return (((minSubnormalExponent(format) <= exponent) || knownInFormat) &&
	      upperBound);
    }

    inline prop inNormalOrSubnormalRange(const fpt &format, const prop &knownInFormat) const {
      return ((minSubnormalExponent(format) <= exponent) &&
	      (exponent <= maxNormalExponent(format))) || knownInFormat;
    }


    
    // The amount needed to normalise the number
    inline sbv getSubnormalAmount(const fpt &format) const {
      return max<t>(minNormalExponent(format) - exponent,
		    sbv::zero(exponent.getWidth()));
    }

    inline prop isPositiveInf (void) const {
      return this->inf && !this->sign;
    }

    inline prop isNegativeInf (void) const {
      return this->inf && this->sign;
    }



    // Likewise, this is a convenience function
    static ubv leadingOne(const bwt sigWidth) {
      return ubv::one(sigWidth) << ubv(sigWidth, (sigWidth - 1));
    }

    static ubv nanPattern(const bwt sigWidth) {
      return ubv::one(sigWidth) << ubv(sigWidth, (sigWidth - 1)); // For a qNaN, change for sNaN
    }



    unpackedFloat<t> extend (const bwt expExtension, const bwt sigExtension) const {
      return unpackedFloat<t>(this->nan, 
			      this->inf,
			      this->zero,
			      this->sign,
			      this->exponent.extend(expExtension),
			      this->significand.extend(sigExtension) << ubv((this->significand.getWidth() + sigExtension), sigExtension));
    }


    // Moves the leading 1 up to the correct position, adjusting the
    // exponent as required.
    unpackedFloat<t> normaliseUp (const fpt &/*format*/) const {
      PRECONDITION(!(nan || inf || zero));  // Should not be attempting to normalise these.

      normaliseShiftResult<t> normal(normaliseShift<t>(this->significand));

      bwt exponentWidth(this->exponent.getWidth());
      INVARIANT(normal.shiftAmount.getWidth() < exponentWidth); // May loose data / be incorrect for very small exponents and very large significands
      
      sbv signedAlignAmount(normal.shiftAmount.resize(exponentWidth).toSigned());
      sbv correctedExponent(this->exponent - signedAlignAmount);

      // Optimisation : could move the zero detect version in if used in all cases
      //  catch - it zero detection in unpacking is different.
      return unpackedFloat<t>(this->sign, correctedExponent, normal.normalised);
    }

    
    unpackedFloat<t> normaliseUpDetectZero (const fpt &format) const {
      PRECONDITION(!(nan || inf || zero));  // Should not be attempting to normalise these.

      normaliseShiftResult<t> normal(normaliseShift<t>(this->significand));

      bwt exponentWidth(this->exponent.getWidth());
      INVARIANT(normal.shiftAmount.getWidth() < exponentWidth); // May loose data / be incorrect for very small exponents and very large significands
      
      sbv signedAlignAmount(normal.shiftAmount.resize(exponentWidth).toSigned());
      sbv correctedExponent(this->exponent - signedAlignAmount);

      return ITE(normal.isZero,
		 unpackedFloat<t>::makeZero(format, this->sign),
		 unpackedFloat<t>(this->sign, correctedExponent, normal.normalised));
    }

#if 0
    // Moves the leading 1 up to the correct position, adjusting the
    // exponent as required.
    unpackedFloat<t> normaliseUp (const fpt &/*format*/) const {
      PRECONDITION(!(nan || inf || zero));  // Should not be attempting to normalise these.

      ubv alignAmount(countLeadingZeros<t>(this->significand));
      
      ubv alignedSignificand(this->significand.modularLeftShift(alignAmount)); // CLZ means data is not lost

      sbv signedAlignAmount(alignAmount.extract(this->exponent.getWidth() - 1,0).toSigned());
      // May loose data / be incorrect for very small exponents and very large significands
      sbv correctedExponent(this->exponent - signedAlignAmount);

      // Optimisation : could move the zero detect version in if used in all cases
      return unpackedFloat<t>(this->sign, correctedExponent, alignedSignificand);
    }

    unpackedFloat<t> normaliseUpDetectZero (const fpt &format) const {
      unpackedFloat<t> normal(this->normaliseUp(format));

      return ITE(this->significand.isAllZeros(),
		 unpackedFloat<t>::makeZero(format, this->sign),
		 normal);
    }
#endif

    
#if 0
    unpackedFloat<t> normaliseUp (const fpt &format) const {
      PRECONDITION(!(nan || inf || zero));  // Should not be attempting to normalise these.

      unpackedFloat<t> working(*this);
      bwt sigWidth = unpackedFloat<t>::significandWidth(format);
      bwt exWidth = unpackedFloat<t>::exponentWidth(format);
     
      // TODO : is range checking needed here?  Only in obscure use cases.

      for (bwt power = previousPowerOfTwo(sigWidth); power != 0; power >>= 1) {
	bwt rem = sigWidth - power;

	INVARIANT(rem > 0);

	ubv mask(ubv::allOnes(power).extend(rem) << rem);
	prop shiftNeeded((mask & working.significand).isAllZeros());

	// Has to be modular as in the case it is not needed,
	// performing the shift will loose information.
	working.significand = ITE(shiftNeeded, working.significand.modularLeftShift(power), working.significand);
	working.exponent = ITE(shiftNeeded, working.exponent - sbv(exWidth,power), working.exponent);
        // Optimisation : rather than add each cycle, build shiftNeeded into a number and add once.
      }

      return working;
    }
#endif

    

    // Is a well formed unpacked struct of the given format?
    // The format is needed to ensure that subnormals are correct.
    // This invariant does not hold at all points in the code!
    prop valid(const fpt &format) const {

      bwt exWidth = exponentWidth(format);
      bwt sigWidth = significandWidth(format);

      PRECONDITION((exWidth == exponent.getWidth()) &&
		   (sigWidth == significand.getWidth()));

      // At most one flag is true
      prop atMostOneFlag(!(nan && inf) && !(nan && zero) && !(inf && zero));

      // If one flag is true then exponent and significand are defaults
      prop oneFlag(nan || inf || zero);
      prop exponentIsDefault(defaultExponent(format) == exponent);
      prop significandIsDefault(defaultSignificand(format) == significand);
      prop flagImpliesDefaultExponent(IMPLIES(oneFlag, exponentIsDefault));
      prop flagImpliesDefaultSignificand(IMPLIES(oneFlag, significandIsDefault));

      // NaN has sign = 0
      prop NaNImpliesSignFalse(IMPLIES(nan, !sign));

      // Exponent is in range
      prop exponentInRange(inNormalOrSubnormalRange(format, prop(false)));

      // Has a leading one
      prop hasLeadingOne(!(leadingOne(unpackedFloat<t>::significandWidth(format)) & significand).isAllZeros());

      // Subnormal numbers require an additional check to make sure they
      // do not have an unrepresentable amount of significand bits.
      sbv subnormalAmount(this->getSubnormalAmount(format));
      INVARIANT((sbv::zero(exWidth) <= subnormalAmount) &&
		(subnormalAmount <= sbv(exWidth,sigWidth)));

      // Invariant implies this following steps do not loose data
      ubv mask(orderEncode<t>(subnormalAmount.toUnsigned().matchWidth(significand)));

      prop correctlyAbbreviated((mask & significand).isAllZeros());

      prop subnormalImpliesTrailingZeros(IMPLIES(inSubnormalRange(format, prop(false)), correctlyAbbreviated));

      
      return (atMostOneFlag &&
	      (flagImpliesDefaultExponent && flagImpliesDefaultSignificand) &&
	      NaNImpliesSignFalse &&
	      exponentInRange &&
	      hasLeadingOne &&
	      subnormalImpliesTrailingZeros);
    }

      

      /* Older version
       * Correct but replaced with a version which gives more propagation friendly assertions.
       */
#if 0
    prop valid(const fpt &format) const {

      bwt exWidth = exponentWidth(format);
      bwt sigWidth = significandWidth(format);

      PRECONDITION((exWidth == exponent.getWidth()) &&
		   (sigWidth == significand.getWidth()));

      prop hasLeadingOne(!(leadingOne(unpackedFloat<t>::significandWidth(format)) & significand).isAllZeros());



      // Subnormal numbers require an additional check to make sure they
      // do not have an unrepresentable amount of significand bits.
      sbv subnormalAmount(this->getSubnormalAmount(format));
      INVARIANT((sbv::zero(exWidth) <= subnormalAmount) &&
		(subnormalAmount <= sbv(exWidth,sigWidth)));

      // Invariant implies this following steps do not loose data
      ubv trimmedSubnormalAmount(subnormalAmount.toUnsigned().extract(positionOfLeadingOne(sigWidth),0));
      ubv mask(trimmedSubnormalAmount.orderEncode(sigWidth));

      prop correctlyAbbreviated((mask & significand).isAllZeros());



      prop normalCase   (!nan && !inf && !zero && inNormalRange(format, prop(false))    && hasLeadingOne);
      prop subnormalCase(!nan && !inf && !zero && inSubnormalRange(format, prop(false)) && hasLeadingOne && correctlyAbbreviated);


    
      prop exponentIsDefault(defaultExponent(format) == exponent);
      prop significandIsDefault(defaultSignificand(format) == significand);

      prop NaNCase ( nan && !inf && !zero && exponentIsDefault && significandIsDefault && !sign);
      prop InfCase (!nan &&  inf && !zero && exponentIsDefault && significandIsDefault);
      prop ZeroCase(!nan && !inf &&  zero && exponentIsDefault && significandIsDefault);

      return (NaNCase || InfCase || ZeroCase || normalCase || subnormalCase);

    }
#endif

    
    // Just for debugging
    void print (void) const {
      std::cerr << "nan : " << this->nan << '\t'
		<< "inf : " << this->inf << '\t'
		<< "zero : " << this->zero << '\t'
		<< "sign : " << this->sign << '\t'
		<< "exponent : " << this->exponent << '\t'
		<< "significand : " << this->significand << std::endl;
    }

  };

template <class t>
  struct ite<typename t::prop, unpackedFloat<t> > {					
  static const unpackedFloat<t> iteOp (const typename t::prop &cond,		
			    const unpackedFloat<t> &l,					
			    const unpackedFloat<t> &r) {				
    return unpackedFloat<t>(ITE(cond, l.nan, r.nan),
			    ITE(cond, l.inf, r.inf),
			    ITE(cond, l.zero, r.zero),
			    ITE(cond, l.sign, r.sign),
			    ITE(cond, l.exponent, r.exponent),
			    ITE(cond, l.significand, r.significand));
    }
 };




}

#endif
