/* Release 3.10 */
/*-------------------------------------------------------------------------*
*                                                                          *
*  BCD.CPP - BCD number class implementation.       		           *
*                                                                          *
*  COPYRIGHT (C) 1989..1992 Clarion Software Corporation.                  *
*  All Rights Reserved                                                     *
*                                                                          *
*--------------------------------------------------------------------------*/

#include <clib.h>
#include <bcd.hpp>

#pragma module(init_prio=>22)

 bcd::bcd(int x) {

 int_to_dec((unsigned long) abs(x));
 if(x < 0)
   bcd_array[9]|=0x80;
}

 bcd::bcd(unsigned x) {

 int_to_dec((unsigned long) x);
}

 bcd::bcd(long x) {

 int_to_dec((unsigned long) labs(x));
 if(x < 0)
   bcd_array[9]|=0x80;
}

 bcd::bcd(unsigned long x) {

 int_to_dec(x);
}

bcd bcd::operator-() {

  bcd_array[9]^=0x80;
  return *this;
}

ostream& operator<<(ostream& os, bcd& b) {

  os << real(b);
  return os;
}

istream& operator>>(istream& is, bcd& b) {

  long double n;

  is  >> n;
  b.real_to_dec(n);
  return is;
}

void bcd::bcd_copy_to(unsigned char *source) {

  int n=0;

  while(n < 10) {
    bcd_array[n] = source[n];
    ++n;
  }
}


void bcd::bcd_copy_from(unsigned char *dest) {

  int n=0;

  while(n < 10) {
    dest[n] = bcd_array[n];
    ++n;
  }
}

void bcd::int_to_dec(unsigned long x) {

  char s[32];
  int l, n, c, p;
  unsigned char cv;

  if(!x)
    exp=ExpZero;
  else
    exp=110 + (int)log10((double) (x));
  ultoa(x, s, 10);
  l=strlen(s);
  p=0;
  while(p < 10) {
    bcd_array[p++]=0;
  }
  p=8;
  n=0;
  while(n < l) {
    c=s[n++];
    cv=(c-0x30)<<4;
    if(n == l) {
      bcd_array[p]=cv;
      break;
    }
    c=s[n++];
    cv|=(c-0x30);
    bcd_array[p--]=cv;
  }
}

#pragma save
#pragma call(reg_param=>(ax))
extern "C" long double _cvt_bcd(unsigned char/* <_SS>*/ *);
#pragma restore

long double bcd::dec_to_real() {

  long double n;
  unsigned char bcd_temp[10];

  bcd_copy_from(bcd_temp);
  n=_cvt_bcd(bcd_temp);
  n*= pow10((double)(exp-127));
  return n;
}

void bcd::real_to_dec(long double x, int decimals) {

  long double zero = 0;
  long double norm;
  int k,clear,n;
  unsigned char bcd_temp[10];

  if ( x < zero ) {
    x = -x;
    bcd_temp[9] = 0x80;
  }
  else
    bcd_temp[9] = 0;

  if ( x == zero )
    k = -10000;
  else {
    k = 1 + (int) floorl( log10l(x) + 0.001 );
  }

  while (1) {
    norm = x;
    if ( k != -10000 )
      norm *= _pow_int(10,18-k);

    _bcd( norm, (char near *)bcd_temp );
    if ( k == -10000 ) {
      k = 1;
      break;
    }
    if ( ( bcd_temp[8] >> 4 ) != 0 )
      break;
    k -= 1;
  }
  exp = 109 + k;

  if((k < 0) && (decimals != _BcdMaxDecimals)) {
    clear = abs(k) + decimals;
    n = 0;
    while(clear) {
      bcd_temp[n] &= ~0x0F;
      if(--clear == 0)
        break;
      bcd_temp[n] &= ~0xF0;
      --clear;
      ++n;
    }
  }
  bcd_copy_to(bcd_temp);
}

