/*
 * Some useful routines to do with numbers
 * Copyright (C) 2000 Andrew Hunter
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *  
 *  This library 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
 *  Lesser General Public License for more details.
 *  
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

/*
 * A bunch of useful routines for dealing with doubles (to make up for 
 * holes/problems in PalmOS/MathLib)
 *
 * Oh, and this is released under the Lesser GPL, so if you are one of 
 * those silly people who writes proprietary software, you can use
 * these functions there (note that in order to do this, you must
 * package this into a library)
 */

#include <PalmOS.h>

#include "MathLib.h"
#include "number.h"

/*                          ----=====88888=====----                          */

/*
 * Argh! _d_itod kills my PalmIII for some reason. So here's how you
 * do the conversion by hand (grr)
 */
double GetDouble(Int32 number)
{
  union  DoubleType d;
  UInt32 larger, shift;
  UInt32 sign;

  /* Trivial case... */
  if (number == 0)
    return 0.0;

  /* Deal with the sign */
  if (number < 0)
    {
      number = -number;
      sign = 1;
    }
  else
    sign = 0;

  /* Find the most significant set bit */
  larger = 1; shift = 0;
  while (number >= larger)
    {
      shift++;
      larger<<=1;
    }
  shift--;
  d.bits.sign = sign;

  /* MSB gives us the exponent */
  d.bits.exp  = shift+1023;
  if (shift <= 20)
    {
      /* Shift in the number (case 1) */
      d.bits.manH = number<<(20-shift);
      d.bits.manL = 0;
    }
  else
    {
      /* Shift in the number (case 2) */
      d.bits.manH = number>>(shift-20);
      d.bits.manL = number<<(52-shift);
    }
 
  return d.d;
}

/*                          ----=====88888=====----                          */

#if 0
static int charType[256] =
{
  0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 
  0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 1,1,1,1, 1,1,1,1, 1,1,0,0, 0,0,0,0, 
  0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 
  0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 
  0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 
  0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 
  0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 
  0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0
};

extern double StrToD(Char* string)
{
  /*
   * Eep! Complicated
   *
   * PalmOS's floating point support is, not to put too fine a
   * point on it, piss poor. So, we parse the number ourselves. We 
   * even convert from integer to double ourselves, as _d_itod
   * doesn't seem to work...
   */
  double num;      /* The final number */
  Int32 temp;      /* Integer parsing is much quicker... */
  UInt32 x, mult;  /* Position and multiplier */
  Boolean flag;    /* False if this is the first chunk of 8 characters */
  UInt32  pos;     /* Position in the string */
  
  num = 0;
  flag = false;
  
  /* Parse the integer part of the number */
  while (charType[string[pos]] == 1)
    {
      x=0; temp=0; mult = 1;
      /* Parse the next 8 characters */
      while (charType[string[pos]] == 1 &&
	     x<8)
	{
	  temp *= 10;
	  temp += (string[pos++] - 48);
	  x++;
	  mult *= 10;
	}
      /* Add them to the double */
      if (flag == true)
	num *= GetDouble(mult);
      num += GetDouble(temp);
      flag = true;
    }
  
  /* Parse the fractional part of the number */
  if (string[pos] == '.')
    {
      mult = 0;
      pos++;
      while (charType[string[pos]] == 1)
	{
	  x=0; temp=0;
	  /* Parse the next 8 characters */
	  while (charType[string[pos]] == 1 &&
		 x<8)
	    {
	      temp *= 10;
	      temp += (string[pos++] - 48);
	      x++;
	      mult++; /* Well, divide, really */
	    }
	  /* Add them to the double */
	  num += GetDouble(temp)/pow(10.0, GetDouble(mult));
	  flag = true;
	}
      
      if (string[pos] == '.')
	return num;
    }
  
  /* Exponent part */
  if (string[pos] == 'e' ||
      string[pos] == 'E')
    {
      pos++;
      if (charType[string[pos]] == 1)
	{
	  temp = 0;
	  while (charType[string[pos]] == 1 &&
		 string[pos] != '.')
	    {
	      temp *= 10;
	      temp += (string[pos++] - 48);
	    }
	  num *= pow(10.0, GetDouble(temp));
	}
    }

  return num;
}
#endif

/*                          ----=====88888=====----                          */

/*
 * The algorithm works as follows:
 *
 * Calculate exponent (integer) and mantissa (real) for the number such that
 *   mantissa*10^(exponent+1)=number
 * For very large/small numbers, always use exponent mode
 * Each digit in the number can be produced by multiplying the
 *   mantissa repeatedly by 10 and taking the integer part
 *
 * This algorithm is a bit slow on a machine with no hardware floating 
 * point, however. It may be worth noting that 9 digits of mantissa
 * can be stored in an Int32.
 */
void FormatDouble(double    number,
		  Char*     buffer,
		  UInt16    buflen,
		  NumFormat format,
		  UInt16    places)
{
  union DoubleType d;
  UInt16 pos, place, digit;
  double mantissa, exponent;
  double dplaces, round;

  /* Make sure that there is space for the number */
  if (buflen<5)
    {
      buffer[0] = '\0';
      return;
    }

  pos = 0;

  /* Deal with NaN */
  if (isnan(number))
    {
      buffer[0] = 'N';
      buffer[1] = 'a';
      buffer[2] = 'N';
      buffer[3] = '\0';
      return;
    }

  /* And infinities... */
  if (isinf(number))
    {
      if (d.bits.sign == 1)
	buffer[0] = '-';
      else
	buffer[0] = '+';
      buffer[1] = 'i';
      buffer[2] = 'n';
      buffer[3] = 'f';
      buffer[4] = '\0';
      return;
    }

  if (number < 0)
    {
      buffer[0] = '-';
      pos++;
      number = -number;
    }

  /* Get the radix 10 form */
  exponent = trunc(log10(number));
  if (number < 1.0 && number > -1.0 && exponent == 0.0)
    exponent = -1.0; 

  if (exponent >= 0)
    mantissa = number*pow(10.0, -(exponent+1.0));
  else
    mantissa = number;

  dplaces = GetDouble(places);

  if (format == FormatFloat || format == FormatFixed)
    {
      if (exponent >= dplaces || exponent <= -dplaces)
	{
	  format = FormatExponent;
	}
    }

  switch (format)
    {
      /* Rounding (float/fixed) */
    case FormatFloat:
    case FormatFixed:
      round = pow(0.1, dplaces+exponent+2.0)*5.0;
      mantissa+=round;
      if (mantissa >= 1.0)
	{
	  exponent += 1.0;
	  mantissa /= 10.0;
	}
      break;

      /* Rounding (exponent) */
    case FormatExponent:
      round = pow(0.1, dplaces+2.0)*5.0;
      mantissa+=round;
      if (mantissa >= 1.0)
	{
	  exponent += 1.0;
	  mantissa /= 10.0;
	}
      break;
    }

  place = 0;
  switch (format)
    {
      /* Print the decimal part (Float, fixed forms) */
    case FormatFloat:
    case FormatFixed:
      if (exponent < 0.0)
	{
	  buffer[pos++] = '0';
	  place++;
	}
      else
	{
	  Int32 texp;

	  texp = (Int32)exponent;
	  while (texp >= 0)
	    {
	      texp--;
	      mantissa*=10;
	      digit     = (Int16)(trunc(mantissa))+48;
	      mantissa -= trunc(mantissa);
	      buffer[pos++] = digit;

	      place++;
	    }
	}

      buffer[pos++] = '.';
      break;

      /* Print the decimal part (Exponent form) */
    case FormatExponent:
      mantissa *= 10;
      digit     = (UInt16)(trunc(mantissa))+48;
      mantissa -= trunc(mantissa);
     
      buffer[pos++] = digit;
      buffer[pos++] = '.';
      break;
    }

  /* The decimal part */
  switch (format)
    {
    case FormatFloat:
      // place = 0;
      while (place < places)
	{
	  mantissa *= 10;
	  digit     = (UInt16)(trunc(mantissa))+48;
	  mantissa -= trunc(mantissa);	    
	  buffer[pos++] = digit;
	  place++;
	}
      while (buffer[--pos] == '0');
      pos++;
      break;

    case FormatFixed:
      place = 0;
      while (place < places)
	{
	  mantissa *= 10;
	  digit     = (UInt16)(trunc(mantissa))+48;
	  mantissa -= trunc(mantissa);
	  buffer[pos++] = digit;
	  place++;
	}
      break;
  
    case FormatExponent:
      {
	Int16  texp;
	
	place = 0;
	while (place < places)
	  {
	    mantissa *= 10;
	    digit     = (UInt16)(trunc(mantissa))+48;
	    mantissa -= trunc(mantissa);
	    buffer[pos++] = digit;
	    place++;
	  }
	
	texp = exponent;
	buffer[pos++] = 'e';
	if (texp == 0)
	  buffer[pos++] = '0';
	else
	  {
	    UInt16 len;
	    UInt16 x;
	    
	    if (texp < 0)
	      {
		texp = -texp;
		buffer[pos++] = '-';
	      }
	    
	    len = (UInt16) trunc(log10(GetDouble(texp)))+1;
	    pos += len;
	    x = 1;
	    
	    while (texp > 0)
	      {
		digit = (texp%10)+48;
		texp /= 10;
		buffer[pos-x] = digit;
		x++;
	      }
	  }
      }
      break;
    }

  buffer[pos++] = '\0';
}

