/*

    This file is part of the Maude 3 interpreter.

    Copyright 1997-2023 SRI International, Menlo Park, CA 94025, USA.

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.

*/

//
//	Now a mismomer - contains random stuff that doesn't fit
//	anywhere else.
//
#include <float.h>  // for DBL_MAX
#include "macros.hh"
#include "vector.hh"
#include "mathStuff.hh"
#include "decBuf.hh"
#include "binBuf.hh"
#include "flagSet.hh"

bool globalAdvisoryFlag = true;
bool globalVerboseFlag = false;
bool globalDebugFlag = false;
//
//	We use this to dump return values that the compiler
//	thinks should not be discarded and would otherwise warn about.
//
int returnValueDump;

const char*
int64ToString(int64_t i, int base)
{
  Assert(base >= 2 && base <= 36, "bad base " << base);
  const int MAX_STRING_SIZE = 64 + 1;  // 64 bits + sign
  const char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
  static char text[MAX_STRING_SIZE + 1];
  char* p = text + MAX_STRING_SIZE;
  bool negative = false;
  uint64_t u = i;
  if (i < 0)
    {
      u = static_cast<uint64_t>(- (i + 1)) + 1;
      negative = true;
    }
  do
    {
      *(--p) = digits[u % base];
      u /= base;
    }
  while (u > 0);
  if (negative)
    *(--p) = '-';
  return p;
}

int64_t
stringToInt64(const char* s, bool& error, int base)
{
  Assert(base >= 2 && base <= 36, "bad base " << base);
  error = true;
  bool negative = false;
  switch (*s)
    {
    case '-':
      negative = true;
      // fall thru
    case '+':
      ++s;
    }
  int64_t r = 0;
  for (;;)
    {
      char c = *s++;
      if (c == '\0')
	break;
      int v;
      if (isdigit(c))
	v = c - '0';
      else if (isupper(c))
	v = c - 'A' + 10;
      else if (islower(c))
	v = c - 'a' + 10;
      else
	{
	  error = true;
	  break;
	}
      if (v >= base)
	{
	  error = true;
	  break;
	}
      if (r > (INT64_MAX - v) / base)
	{
	  if (negative && *s == '\0')  // check for INT64_MIN
	    {
	      int64_t t = -(INT64_MIN + base + v);
	      if (t % base == 0 && t / base == r - 1)
		return INT64_MIN;
	    }
	  error = true;
	  break;
	}
      r = r * base + v;
      error = false;
    }
  return negative ? -r : r;
}

bool
looksLikeFloat(const char* s)
{
  bool digits = false;
  bool dot = false;
  char c = *s++;
  if (c == '+' || c == '-')
    c = *s++;
  if (strcmp(s - 1, "Infinity") == 0)
    return true;
  if (isdigit(c))
    {
      digits = true;
      do
	c = *s++;
      while (isdigit(c));
    }
  if (c == '.')
    {
      dot = true;
      c = *s++;
      if (isdigit(c))
	{
	  digits = true;
	  do
	    c = *s++;
	  while (isdigit(c));
	}
    }
  if (!digits)
    return false;
  if (c == 'e' || c == 'E')
    {
      c = *s++;
      if (c == '+' || c == '-')
	c = *s++;
      if (!isdigit(c))
	return false;
      do
	c = *s++;
      while (isdigit(c));
    }
  else
    {
      if (!dot)
	return false;
    }
  return c == '\0';
}

const char*
doubleToString(double d)
{
  if (::isnan(d))
    return "NaN";
  if (::isinf(d))
    return (d < 0) ? "-Infinity" : "Infinity";
  if (d == 0.0)
    return "0.0";

  static char buffer[DOUBLE_TEXT_SIZE + 1] = "-";
  int decPt;
  int sign;
  //
  //	Get 17 significant digits.
  //
  const int significantDigits = 17;
  correctEcvt(d, significantDigits, buffer + 2, decPt, sign);
  //
  //	Insert decimal point.
  //
  buffer[1] = buffer[2];
  buffer[2] = '.';
  //
  //	Strip trailing zeros.
  //
  int next = 2 + significantDigits;
  while (next > 4 && buffer[next - 1] == '0')
    --next;
  //
  //	 Add exponent if needed.
  //
  int exponent = decPt - 1;
  if (exponent != 0)
    {
      buffer[next++] = 'e';
      if (exponent > 0)
	buffer[next++] = '+';
      strcpy(buffer + next, int64ToString(exponent, 10));
    }
  else
    buffer[next] = '\0';
  return (sign < 0) ? buffer : (buffer + 1);
}

double
stringToDouble(const char* s, bool& error)
{
  if (looksLikeFloat(s))
    {
      error = false;
      //
      //	Some versions of atof() don't handle Infinity.
      //
      char c = s[0];
      if (c == 'I' || ((c == '+' || c == '-') && s[1] == 'I'))
	{
	  double inf = DBL_MAX * DBL_MAX;
	  return (c == '-') ? (- inf) : inf;
	}
      return atof(s);
    }
  else
    {
      error = true;
      return 0;
    }
}

void
correctEcvt(double d, int nrDigits, char buffer[], int& decPt, int& sign)
{
  union Bits
  {
    uint64_t i;
    double d;
  };
  
  const uint64_t bit52 = (1ULL << 52);
  const uint64_t topBit = (1ULL << 63);
  
  //
  //	Break IEEE-754 double into sign/exponent/mantissa.
  //
  Bits bits;
  bits.d = d;
  sign = (bits.i >> 63) ? -1 : 1;  // top bit = 1 -> -ve
  uint64_t mantissa = bits.i & (bit52 - 1);  // 52 LSBs
  int exponent = (static_cast<int>(bits.i >> 52) & 0x7FF);  // remaining 11 bits
  if (exponent == 2047)
    {
      strcpy(buffer, (mantissa == 0) ? "Infinity" : "NaN");
      decPt = 0;
      return;
    }
  if (exponent == 0)
    {
      if (mantissa == 0)  // +/- zero
	{
	  for (int i = 0; i < nrDigits; i++)
	    buffer[i] = '0';
	  buffer[nrDigits] = '\0';
	  decPt = 0;
	  sign = 0;
	  return;
	}
      exponent = -1022;  // denormalized number
    }
  else
    {
      exponent -= 1023;  // unbiased exponent
      mantissa |= bit52;  // implied leading 1
    }
  DecBuf dec;
  BinBuf bin;
  dec.zero();
  mantissa <<= 11;  // now the 53 MSBs
  ++exponent;  // number of bits to the left of the binary point
  if (exponent > 0)
    {
      //
      //	Case 1: Integer part which is converted by shift and add
      //	on a decimal buffer; plus a possible fraction part.
      //	We convert integer part of exponent bits, 60 bits at a time
      //	because 10 * 2^60 < 2^64.
      //
      {
	int shift = min(exponent, 60);
	dec.shiftAndAdd(shift, mantissa >> (64 - shift));
	mantissa <<= shift;
	exponent -= shift;
      }
      while (exponent > 0)
	{
	  int shift = min(exponent, 60);
	  dec.shiftAndAdd(shift, 0);
	  exponent -= shift;
	}
      if (nrDigits <= 0)  // exact conversion
	{
	  decPt = dec.nrDigits();  // can't increase because we don't round
	  if (mantissa == 0)
	    dec.ascii(buffer, decPt - dec.nrTrailingZeros());
	  else
	    {
	      bin.init(mantissa, 0);
	      do
		dec.insertDigit(bin.times10());
	      while (bin.gtZero());
	      dec.ascii(buffer, dec.nrDigits());
	    }
	}
      else  // round to nrDigits using IEEE-754 "round to nearest" convention
	{
	  int extraDigitsNeeded = nrDigits - dec.nrDigits();
	  if (extraDigitsNeeded <= 0)
	    {
	      //
	      //	We have enough digits so round using fractional part
	      //	of mantissa.
	      //
	      int cmpHalf = (mantissa == topBit) ? 0 : ((mantissa < topBit) ? -1 : 1);
	      dec.round(nrDigits, mantissa > 0, cmpHalf);
	      decPt = dec.nrDigits();
	    }
	  else
	    {
	      //
	      //	Make more digits by converting fraction.
	      //
	      bin.init(mantissa, 0);
	      for (int i = 0; i < extraDigitsNeeded; i++)
		dec.insertDigit(bin.times10());
	      //
	      //	Round using part of fraction left in bin.
	      //
	      dec.round(nrDigits, bin.gtZero(), bin.cmpHalf());
	      decPt = dec.nrDigits() - extraDigitsNeeded;
	    }
	  dec.ascii(buffer, nrDigits);
	}
    }
  else
    {
      //
      //	Case 2: No integer part. Fraction is converted by
      //	repeatedly multiplying it by 10.
      //
      bin.init(mantissa, -exponent);
      decPt = 1;
      int digit;
      do
	{
	  digit = bin.times10();
	  --decPt;
	}
      while (digit == 0);
      dec.insertDigit(digit);
      if (nrDigits <= 0)  // exact conversion
	{
	  while (bin.gtZero())
	    dec.insertDigit(bin.times10());
	  dec.ascii(buffer, dec.nrDigits());
	}
      else  // round to nrDigits using IEEE-754 "round to nearest" convention
	{
	  for (int i = 1; i < nrDigits; i++)
	    dec.insertDigit(bin.times10());
	  //
	  //	Round using part of fraction left in bin.
	  //
	  dec.round(nrDigits, bin.gtZero(), bin.cmpHalf());
	  decPt += (dec.nrDigits() - nrDigits);  // rounding may have given us an extra digit
	  dec.ascii(buffer, nrDigits);
	}
    }
}

void
selfCheckFailHandler()
{
  std::cerr << "\nMaude self-check failure.\nPlease submit a bug report to: " << PACKAGE_BUGREPORT <<
    "\nPlease include the platform details, Maude version, and a file\n'crash.maude' that can be loaded to reproduce the crash (it may load\n\
other files). Do not bother trying to simplify your example unless the\nruntime to the bug being visible is greater than 10 seconds.\n" << endl;
  exit(1);
}
