/*
 * Copyright (C) 2008 Search Solution Corporation. All rights reserved by Search Solution.
 *
 *   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; version 2 of the License.
 *
 *  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
 *
 */

/*
 * numeric_opfunc.c - Basig manipulations of DB_NUMERIC type data
 */

#ident "$Id$"

/* The bits in the character string of a DB_NUMERIC are the binary digits of
 * the number. The LSB's of the DB_NUMERIC are in buf[DB_NUMERIC_BUF_SIZE-1].
 */

#include <math.h>
#include <assert.h>
#include <stdio.h>
#include <string.h>

#include "numeric_opfunc.h"
#include "db.h"
#include "db_date.h"
#include "memory_alloc.h"
#include "system_parameter.h"

#if defined(SERVER_MODE)
#include "thread_impl.h"
#endif

/* this must be the last header file included!!! */
#include "dbval.h"

#define CARRYOVER(arg)		((arg) >> 8)
#define GET_LOWER_BYTE(arg)	((arg) & 0xff)
#define NUMERIC_ABS(a)		((a) >= 0 ? a : -a)
#define TWICE_NUM_MAX_PREC	(2*DB_MAX_NUMERIC_PRECISION)
#define SECONDS_IN_A_DAY	  (int)(24L * 60L * 60L)

#define ROUND(x)                  (int)((x) > 0 ? ((x) + .5) : ((x) - .5))

typedef struct dec_string DEC_STRING;
struct dec_string
{
  char digits[TWICE_NUM_MAX_PREC];
};

static const char fast_mod[20] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
  0, 1, 2, 3, 4, 5, 6, 7, 8, 9
};

static DEC_STRING powers_of_2[DB_NUMERIC_BUF_SIZE * 16];
#if !defined(SERVER_MODE)
static bool initialized_2 = false;
#endif
static unsigned char powers_of_10[TWICE_NUM_MAX_PREC +
				  1][DB_NUMERIC_BUF_SIZE];
#if !defined(SERVER_MODE)
static bool initialized_10 = false;
#endif

static double numeric_Upper_limit[10] = {
  (double) DB_INT32_MAX,
  (double) DB_INT32_MAX / 1e1,
  (double) DB_INT32_MAX / 1e2,
  (double) DB_INT32_MAX / 1e3,
  (double) DB_INT32_MAX / 1e4,
  (double) DB_INT32_MAX / 1e5,
  (double) DB_INT32_MAX / 1e6,
  (double) DB_INT32_MAX / 1e7,
  (double) DB_INT32_MAX / 1e8,
  (double) DB_INT32_MAX / 1e9
};
static double numeric_Pow_of_10[10] = {
  1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9
};

static bool numeric_is_negative (DB_C_NUMERIC arg);
static void numeric_copy (DB_C_NUMERIC dest, DB_C_NUMERIC source);
static void numeric_increase (DB_C_NUMERIC answer);
static void numeric_zero (DB_C_NUMERIC answer, int size);
static void numeric_zero_dec_str (DEC_STRING * answer);
static void numeric_add_dec_str (DEC_STRING * arg1,
				 DEC_STRING * arg2, DEC_STRING * answer);
static void numeric_init_pow_of_2_helper (void);
#if defined(SERVER_MODE)
static void numeric_init_pow_of_2 (void);
#endif
static DEC_STRING *numeric_get_pow_of_2 (int exp);
static void numeric_init_pow_of_10_helper (void);
#if defined(SERVER_MODE)
static void numeric_init_pow_of_10 (void);
#endif
static DB_C_NUMERIC numeric_get_pow_of_10 (int exp);
static void numeric_double_shift_bit (DB_C_NUMERIC arg1, DB_C_NUMERIC arg2,
				      int numbits,
				      DB_C_NUMERIC lsb, DB_C_NUMERIC msb);
static int numeric_compare_pos (DB_C_NUMERIC arg1, DB_C_NUMERIC arg2);
static void numeric_negate (DB_C_NUMERIC answer);
static void numeric_shift_byte (DB_C_NUMERIC arg,
				int numbytes, DB_C_NUMERIC answer,
				int length);
static bool numeric_is_zero (DB_C_NUMERIC arg);
static bool numeric_is_long (DB_C_NUMERIC arg);
static bool numeric_is_bit_set (DB_C_NUMERIC arg, int pos);
static bool numeric_overflow (DB_C_NUMERIC arg, int exp);
static void numeric_add (DB_C_NUMERIC arg1,
			 DB_C_NUMERIC arg2, DB_C_NUMERIC answer, int size);
static void numeric_sub (DB_C_NUMERIC arg1,
			 DB_C_NUMERIC arg2, DB_C_NUMERIC answer, int size);
static void numeric_mul (DB_C_NUMERIC a1,
			 DB_C_NUMERIC a2,
			 bool * positive_flag, DB_C_NUMERIC answer);
static void numeric_long_div (DB_C_NUMERIC a1, DB_C_NUMERIC a2,
			      DB_C_NUMERIC answer, DB_C_NUMERIC remainder);
static void numeric_div (DB_C_NUMERIC arg1, DB_C_NUMERIC arg2,
			 DB_C_NUMERIC answer, DB_C_NUMERIC remainder);
static int numeric_compare (DB_C_NUMERIC arg1, DB_C_NUMERIC arg2);
static int numeric_scale_by_ten (DB_C_NUMERIC arg);
static int numeric_scale_dec (DB_C_NUMERIC arg,
			      int dscale, DB_C_NUMERIC answer);
static int numeric_common_prec_scale (DB_VALUE * dbv1,
				      DB_VALUE * dbv2,
				      DB_VALUE * dbv1_common,
				      DB_VALUE * dbv2_common);
static int
numeric_prec_scale_when_overflow (DB_VALUE * dbv1,
				  DB_VALUE * dbv2,
				  DB_VALUE * dbv1_common,
				  DB_VALUE * dbv2_common);
static void numeric_coerce_big_num_to_dec_str (unsigned char *num,
					       char *dec_str);
static int numeric_get_msb_for_dec (int src_prec, int src_scale,
				    unsigned char *src, int *dest_prec,
				    int *dest_scale, DB_C_NUMERIC dest);
static int numeric_fast_convert (double adouble, int dst_scale,
				 DB_C_NUMERIC num, int *prec, int *scale);

/*
 * numeric_is_negative () -
 *   return: true, false
 *   arg(in) : DB_C_NUMERIC value
 */
static bool
numeric_is_negative (DB_C_NUMERIC arg)
{
  return (arg[0] & 0x80) ? true : false;
}

/*
 * numeric_copy () -
 *   return:
 *   dest(out)  : DB_C_NUMERIC value
 *   source(in) : DB_C_NUMERIC value
 * Note: This routine returns source copied into dest.
 */
static void
numeric_copy (DB_C_NUMERIC dest, DB_C_NUMERIC source)
{
  memcpy (dest, source, DB_NUMERIC_BUF_SIZE);	/* sizeof(source[0]) == 1 */
}

/*
 * numeric_increase () -
 *   return:
 *   answer(in/out) : DB_C_NUMERIC value
 *
 * Note: This routine increments a numeric value.
 */
static void
numeric_increase (DB_C_NUMERIC answer)
{
  int carry = 1;
  int digit;

  /*  Loop through answer as long as there is a carry  */
  for (digit = DB_NUMERIC_BUF_SIZE - 1; digit >= 0 && carry == 1; digit--)
    {
      answer[digit] += 1;
      carry = (answer[digit] == 0) ? 1 : 0;
    }
}

/*
 * numeric_zero () -
 *   return:
 *   answer(in) : DB_C_NUMERIC value
 *   size(in)   :
 *
 * Note: This routine zeroes out a numeric value and returns the result
 */
static void
numeric_zero (DB_C_NUMERIC answer, int size)
{
  memset (answer, 0, size);	/* sizeof(answer[0]) == 1 */
}

/*
 * numeric_zero_dec_str () -
 *   return:
 *   answer(in/out) : (IN/OUT) ptr to a DEC_STRING
 *
 * Note: This routine zeroes out a DEC_STRING returns the result
 */
static void
numeric_zero_dec_str (DEC_STRING * answer)
{
  /* sizeof(answer->digits[0]) == 1 */
  memset (answer->digits, 0, TWICE_NUM_MAX_PREC);
}

/*
 * numeric_add_dec_str () -
 *   arg1(in)   : ptr to a DEC_STRING
 *   arg2(in)   : ptr to a DEC_STRING
 *   answer(out): ptr to a DEC_STRING
 *
 * Note: This routine adds two DEC_STRINGs and returns the result.  It assumes
 *       that arg1 and arg2 have the same scaling.
 */

static void
numeric_add_dec_str (DEC_STRING * arg1, DEC_STRING * arg2,
		     DEC_STRING * answer)
{
  unsigned int answer_bit = 0;
  int digit;

  /*  Loop through the characters setting answer  */
  for (digit = TWICE_NUM_MAX_PREC - 1; digit >= 0; digit--)
    {
      answer_bit = (arg1->digits[digit] + arg2->digits[digit]) +
	(answer_bit >= 10);
      answer->digits[digit] = fast_mod[answer_bit];
    }
}

/*
 * numeric_init_pow_of_2_helper () -
 *   return:
 */
static void
numeric_init_pow_of_2_helper (void)
{
  unsigned int i;

  numeric_zero_dec_str (&(powers_of_2[0]));

  /* Set first element to 1 */
  powers_of_2[0].digits[TWICE_NUM_MAX_PREC - 1] = 1;

  /* Loop through array elements setting each one to twice the prior */
  for (i = 1; i < DB_NUMERIC_BUF_SIZE * 16; i++)
    {
      numeric_add_dec_str (&(powers_of_2[i - 1]), &(powers_of_2[i - 1]),
			   &(powers_of_2[i]));
    }
}

#if defined(SERVER_MODE)
/*
 * numeric_init_pow_of_2 () -
 *   return:
 */
static void
numeric_init_pow_of_2 (void)
{
  numeric_init_pow_of_2_helper ();
}
#endif

/*
 * numeric_get_pow_of_2 () -
 *   return: DEC_STRING containing the equivalent base 10 representation
 *   exp(in)    : positive integer exponent base 2
 *
 * Note: This routine returns a DEC_STRING that holds the base 10 digits of a
 *       power of 2.
 */
static DEC_STRING *
numeric_get_pow_of_2 (int exp)
{
#if !defined(SERVER_MODE)
  /*  If this is the first time to call this routine, initialize  */
  if (!initialized_2)
    {
      numeric_init_pow_of_2_helper ();
      initialized_2 = true;
    }
#endif

  /*  Return the appropriate power of 2  */
  return &powers_of_2[exp];
}

/*
 * numeric_init_pow_of_10_helper () -
 *   return:
 */
static void
numeric_init_pow_of_10_helper (void)
{
  int i;

  numeric_zero (powers_of_10[0], DB_NUMERIC_BUF_SIZE);

  /* Set first element to 1 */
  powers_of_10[0][DB_NUMERIC_BUF_SIZE - 1] = 1;

  /* Loop through elements setting each one to 10 times the prior */
  for (i = 1; i < TWICE_NUM_MAX_PREC + 1; i++)
    {
      numeric_scale_dec (powers_of_10[i - 1], 1, powers_of_10[i]);
    }
}

#if defined(SERVER_MODE)
/*
 * numeric_init_pow_of_10 () -
 *   return:
 */
static void
numeric_init_pow_of_10 (void)
{
  numeric_init_pow_of_10_helper ();
}
#endif

/*
 * numeric_get_pow_of_10 () -
 *   return: DB_C_NUMERIC containing the equivalent base 2 representation
 *   exp(in)    : positive integer exponent base 10
 *
 * Note: This routine returns a DB_C_NUMERIC that holds the base 2 digits of a
 *       power of 10.
 */
static DB_C_NUMERIC
numeric_get_pow_of_10 (int exp)
{
#if !defined(SERVER_MODE)
  /*  If this is the first time to call this routine, initialize  */
  if (!initialized_10)
    {
      numeric_init_pow_of_10_helper ();
      initialized_10 = true;
    }
#endif

  /*  Return the appropriate power of 10  */
  return powers_of_10[exp];
}

#if defined(SERVER_MODE)
/*
 * numeric_init_power_value_string () -
 *   return:
 */
void
numeric_init_power_value_string (void)
{
  numeric_init_pow_of_2 ();
  numeric_init_pow_of_10 ();
}
#endif

/*
 * numeric_double_shift_bit () -
 *   return:
 *   arg1(in)   : DB_C_NUMERIC
 *   arg2(in)   : DB_C_NUMERIC
 *   numbits(in): integer number of bits to shift
 *   lsb(out)   : DB_C_NUMERIC
 *   msb(out)   : DB_C_NUMERIC
 *
 * Note: This routine returns lsb, msb shifted by numbits from arg1, arg2.
 *       Bits that are shifted out of arg1 are placed into LSB of arg2.
 */
static void
numeric_double_shift_bit (DB_C_NUMERIC arg1, DB_C_NUMERIC arg2,
			  int numbits, DB_C_NUMERIC lsb, DB_C_NUMERIC msb)
{
  unsigned char local_arg1[DB_NUMERIC_BUF_SIZE];	/* copy of a DB_C_NUMERIC */
  unsigned char local_arg2[DB_NUMERIC_BUF_SIZE];	/* copy of a DB_C_NUMERIC */
  unsigned int digit;

  /*  Copy args into local variables */
  numeric_copy (local_arg1, arg1);
  numeric_copy (local_arg2, arg2);

  /*  Loop through all but last word of msb shifting bits  */
  for (digit = 0; digit < DB_NUMERIC_BUF_SIZE - 1; digit++)
    {
      msb[digit] = (local_arg2[digit] << numbits) |
	(local_arg2[digit + 1] >> (8 - numbits));
    }

  /*  Do last word of msb separately using upper word of lsb  */
  msb[DB_NUMERIC_BUF_SIZE - 1] =
    (local_arg2[DB_NUMERIC_BUF_SIZE - 1] << numbits) |
    (local_arg1[0] >> (8 - numbits));

  /*  Loop through all but last word of lsb shifting bits  */
  for (digit = 0; digit < DB_NUMERIC_BUF_SIZE - 1; digit++)
    {
      lsb[digit] = (local_arg1[digit] << numbits) |
	(local_arg1[digit + 1] >> (8 - numbits));
    }

  /*  Do last word of lsb separately.  */
  lsb[DB_NUMERIC_BUF_SIZE - 1] =
    local_arg1[DB_NUMERIC_BUF_SIZE - 1] << numbits;
}

/*
 * numeric_compare_pos () -
 *   return: Integer flag indicating whether arg1 is less than arg2
 *   arg1(in)   : DB_C_NUMERIC
 *   arg2(in)   : DB_C_NUMERIC
 *
 * Note: This routine compares two positive DB_C_NUMERIC values.
 *       This function returns:
 *          -1   if    arg1 < arg2
 *           0   if    arg1 = arg2 and
 *           1   if    arg1 > arg2.
 */
static int
numeric_compare_pos (DB_C_NUMERIC arg1, DB_C_NUMERIC arg2)
{
  unsigned int digit;

  /*  Loop through bytes looking for the largest  */
  for (digit = 0; digit < DB_NUMERIC_BUF_SIZE; digit++)
    {
      if (arg1[digit] != arg2[digit])
	{
	  return (arg1[digit] > arg2[digit]) ? 1 : (-1);
	}
    }

  /*  If all bytes have been compared, then args are equal  */
  return (0);
}

/*
 * numeric_negate () -
 *   return:
 *   answer(in/out) : DB_C_NUMERIC
 *
 * Note: This routine returns the negative (2's complement) of arg in answer.
 *       The argument answer is modified in place.
 */
static void
numeric_negate (DB_C_NUMERIC answer)
{
  unsigned int digit;

  /*  Complement all bits of answer  */
  for (digit = 0; digit < DB_NUMERIC_BUF_SIZE; digit++)
    {
      answer[digit] = ~(answer[digit]);
    }

  /*  Add one to answer  */
  numeric_increase (answer);
}

/*
 * numeric_shift_byte () -
 *   return:
 *   arg(in)    : DB_C_NUMERIC
 *   numbytes(in): integer number of bytes to shift
 *   answer(out) : DB_C_NUMERIC
 *   length(in) : Length in bytes of answer
 *
 * Note: This routine returns arg shifted by numbytes in answer.  Empty bytes
 *       are zero filled.
 */
static void
numeric_shift_byte (DB_C_NUMERIC arg,
		    int numbytes, DB_C_NUMERIC answer, int length)
{
  int digit;
  int first;
  int last;

  /*  Loop through bytes in answer setting to 0 or arg1  */
  first = length - DB_NUMERIC_BUF_SIZE - numbytes;
  last = length - numbytes - 1;
  for (digit = 0; digit < length; digit++)
    {
      if (first <= digit && digit <= last)
	{
	  answer[digit] = arg[digit - first];
	}
      else
	{
	  answer[digit] = 0;
	}
    }
}

/*
 * numeric_is_zero () -
 *   return: bool
 *   arg(in)    : DB_C_NUMERIC
 *
 * Note: This routine checks if arg = 0.
 *       This function returns:
 *           true   if    arg1 = 0 and
 *           false  otherwise.
 */
static bool
numeric_is_zero (DB_C_NUMERIC arg)
{
  unsigned int digit;

  /*  Loop through arg's bits looking for non-zero values  */
  for (digit = 0; digit < DB_NUMERIC_BUF_SIZE; digit++)
    {
      if (arg[digit] != 0)
	{
	  return (false);
	}
    }

  return (true);
}

/*
 * numeric_is_long () -
 *   return: bool
 *   arg(in)    : DB_C_NUMERIC
 *
 * Note: This routine checks if -2**31 < arg < 2**31-1
 */
static bool
numeric_is_long (DB_C_NUMERIC arg)
{
  unsigned int digit;
  unsigned char pad;

  /* Check for early return */
  if (DB_NUMERIC_BUF_SIZE < 5)
    {
      return (true);
    }

  /*  Get pad value */
  pad = arg[0];
  if (pad != 0xff && pad != 0)
    {
      return (false);
    }

  /*
   * Loop through arg's bits except the 32 LSB looking for non-sign
   * extended values
   */
  for (digit = 1; digit < DB_NUMERIC_BUF_SIZE - 4; digit++)
    {
      if (arg[digit] != pad)
	{
	  return (false);
	}
    }

  return (arg[digit] & 0x80) == (pad & 0x80) ? true : false;
}

/*
 * numeric_is_bit_set () -
 *   return: bool
 *   arg(in)    : DB_C_NUMERIC
 *   pos(in)    : position of the bit inside arg
 *
 * Note: This routine checks if pos'th bit of arg is 1.
 */
static bool
numeric_is_bit_set (DB_C_NUMERIC arg, int pos)
{
  return ((arg[pos / 8]) & (0x01 << (7 - (pos % 8)))) ? true : false;
}

/*
 * numeric_overflow () -
 *   return: bool
 *   arg(in)    : DB_C_NUMERIC
 *   exp(in)    : exponent (base 10) of domain
 *
 * Note: This routine checks to see if arg overflows a domain of precision exp.
 */
static bool
numeric_overflow (DB_C_NUMERIC arg, int exp)
{
  unsigned char narg[DB_NUMERIC_BUF_SIZE];	/* copy of a DB_C_NUMERIC */

  if (numeric_is_negative (arg))
    {
      numeric_copy (narg, arg);
      numeric_negate (narg);
      return (numeric_compare_pos (narg, numeric_get_pow_of_10 (exp)) >=
	      0) ? true : false;
    }
  else
    {
      return (numeric_compare_pos (arg, numeric_get_pow_of_10 (exp)) >=
	      0) ? true : false;
    }
}

/*
 * numeric_add () -
 *   return:
 *   arg1(in)   : DB_C_NUMERIC
 *   arg2(in)   : DB_C_NUMERIC
 *   answer(out): DB_C_NUMERIC
 *   size(in)   : int
 *
 * Note: This routine adds two numerics and returns the result.  It assumes
 *       that arg1 and arg2 have the same scaling.
 */
static void
numeric_add (DB_C_NUMERIC arg1, DB_C_NUMERIC arg2, DB_C_NUMERIC answer,
	     int size)
{
  unsigned int answer_bit = 0;
  int digit;

  /*  Loop through the characters setting answer  */
  for (digit = size - 1; digit >= 0; digit--)
    {
      answer_bit = (arg1[digit] + arg2[digit]) + CARRYOVER (answer_bit);
      answer[digit] = GET_LOWER_BYTE (answer_bit);
    }
}

/*
 * numeric_sub () -
 *   return:
 *   arg1(in)   : DB_C_NUMERIC
 *   arg2(in)   : DB_C_NUMERIC
 *   answer(out): DB_C_NUMERIC
 *   size(in)   : int
 *
 * Note: This routine subtracts arg2 from arg1 returns the result.
 *       It assumes that arg1 and arg2 have the same scaling.
 */
static void
numeric_sub (DB_C_NUMERIC arg1, DB_C_NUMERIC arg2, DB_C_NUMERIC answer,
	     int size)
{
  unsigned char neg_arg2[2 * DB_NUMERIC_BUF_SIZE];	/* copy of a DB_C_NUMERIC */

  /*  Make arg2 negative (use 2's complement)  */
  numeric_copy (neg_arg2, arg2);
  numeric_negate (neg_arg2);

  /*  Add arg1 and neg_arg2  */
  numeric_add (arg1, neg_arg2, answer, size);
}

/*
 * numeric_mul () -
 *   return:
 *   a1(in)     : DB_C_NUMERIC
 *   a2(in)     : DB_C_NUMERIC
 *   positive_ans(out): bool if the answer's is positive (true)
 *                      or negative (false)
 *   answer(out) : DB_C_NUMERIC
 *
 * Note: This routine multiplies two numerics and returns the results.
 */
static void
numeric_mul (DB_C_NUMERIC a1,
	     DB_C_NUMERIC a2, bool * positive_ans, DB_C_NUMERIC answer)
{
  unsigned int answer_bit;
  int digit1;
  int digit2;
  int shift;
  unsigned char temp_term[2 * DB_NUMERIC_BUF_SIZE];	/* copy of DB_C_NUMERIC */
  unsigned char temp_arg1[2 * DB_NUMERIC_BUF_SIZE];	/* copy of DB_C_NUMERIC */
  unsigned char temp_arg2[2 * DB_NUMERIC_BUF_SIZE];	/* copy of DB_C_NUMERIC */
  unsigned char arg1[DB_NUMERIC_BUF_SIZE];	/* copy of DB_C_NUMERIC */
  unsigned char arg2[DB_NUMERIC_BUF_SIZE];	/* copy of DB_C_NUMERIC */

  /*  Initialize the answer  */
  numeric_zero (answer, 2 * DB_NUMERIC_BUF_SIZE);
  *positive_ans = true;

  /*  Check if either arg = 0  */
  if (numeric_is_zero (a1) || numeric_is_zero (a2))
    {
      return;
    }

  /*  If arg1 is negative, toggle sign and make arg1 positive  */
  numeric_copy (arg1, a1);
  numeric_copy (arg2, a2);
  if (numeric_is_negative (arg1))
    {
      numeric_negate (arg1);
      *positive_ans = false;
    }

  /*  If arg2 is negative, toggle sign and make arg2 positive  */
  if (numeric_is_negative (arg2))
    {
      numeric_negate (arg2);
      *positive_ans = !(*positive_ans);
    }

  /* Initialize temporary variables */
  numeric_zero (temp_arg2, DB_NUMERIC_BUF_SIZE);
  numeric_copy (temp_arg2 + DB_NUMERIC_BUF_SIZE, arg2);

  /*  Loop through the 8-bit digits of temp_arg2  */
  shift = 0;
  for (digit2 = (2 * DB_NUMERIC_BUF_SIZE) - 1; digit2 >= 0; digit2--)
    {
      if (temp_arg2[digit2] != 0)
	{
	  answer_bit = 0;
	  numeric_shift_byte (arg1, shift, temp_arg1,
			      2 * DB_NUMERIC_BUF_SIZE);

	  /*  Loop through the 8-bit digits of temp_arg1  */
	  for (digit1 = (2 * DB_NUMERIC_BUF_SIZE - 1); digit1 >= 0; digit1--)
	    {
	      /* the unsigned int casts are necessary here to avoid 16 bit integer
	       * overflow during the multiplication on PC's
	       */
	      answer_bit = ((unsigned int) temp_arg1[digit1] *
			    (unsigned int) temp_arg2[digit2]) +
		(unsigned int) CARRYOVER (answer_bit);
	      temp_term[digit1] = GET_LOWER_BYTE (answer_bit);
	    }
	  numeric_add (temp_term, answer, answer, 2 * DB_NUMERIC_BUF_SIZE);
	}
      shift++;
    }
}

/*
 * numeric_long_div () -
 *   return:
 *   a1(in)     : DB_C_NUMERIC             (numerator)
 *   a2(in)     : DB_C_NUMERIC             (denominator)
 *   answer(in) : DB_C_NUMERIC
 *   remainder(in)      : DB_C_NUMERIC
 *
 * Note: This routine divides two numeric values and returns the
 *       result and remainder.  This algorithm is based on the algorithm in
 *       "<Mark's Book>".
 */
static void
numeric_long_div (DB_C_NUMERIC a1, DB_C_NUMERIC a2,
		  DB_C_NUMERIC answer, DB_C_NUMERIC remainder)
{
  unsigned int nbit;
  unsigned char arg1[DB_NUMERIC_BUF_SIZE];	/* copy of a DB_C_NUMERIC */
  unsigned char arg2[DB_NUMERIC_BUF_SIZE];	/* copy of a DB_C_NUMERIC */
  unsigned char neg_arg2[DB_NUMERIC_BUF_SIZE];	/* copy of a DB_C_NUMERIC */
  int neg_sign = 0;
  int neg_remainder = false;

  /*  Copy inputs to local variables */
  numeric_copy (arg1, a1);
  numeric_copy (arg2, a2);

  /*  If arg1 is negative, toggle sign and make arg1 positive  */
  if (numeric_is_negative (arg1))
    {
      numeric_negate (arg1);
      neg_sign = ~neg_sign;
      neg_remainder = true;
    }

  /*  If arg2 is negative, toggle sign and make arg2 positive  */
  if (numeric_is_negative (arg2))
    {
      numeric_negate (arg2);
      neg_sign = ~neg_sign;
    }

  /*  Initialize variables   */
  numeric_coerce_int_to_num (0, remainder);
  numeric_copy (answer, arg1);
  numeric_copy (neg_arg2, arg2);
  numeric_negate (neg_arg2);

  /*  Shift *answer and *remainder.  Bits shifted out of *answer  *
   *  are placed into *remainder.                                 */
    /*****  NEEDS TO BE UPGRADED TO SHIFT SO THAT FIRST NON-ZERO BIT OF *****/
    /*****  REMAINDER IS AT LEAST EQUAL TO FIRST NON_ZERO BIT OF ARG2.  *****/
    /*****  DON'T DO THIS ONE BIT AT A TIME.                            *****/
  for (nbit = 0; nbit < DB_NUMERIC_BUF_SIZE * 8; nbit++)
    {
      numeric_double_shift_bit (answer, remainder, 1, answer, remainder);

      /* If remainder >= arg2, subtract arg2 from remainder and increment
       * the answer.  */
      if (numeric_compare_pos (remainder, arg2) >= 0)
	{
	  numeric_add (remainder, neg_arg2, remainder, DB_NUMERIC_BUF_SIZE);
	  answer[DB_NUMERIC_BUF_SIZE - 1] += 1;
	}
    }

  /*  If the sign is negative,  negate the answer  */
  if (neg_sign)
    {
      numeric_negate (answer);
    }

  /*  If the remainder is negative, negate it */
  if (neg_remainder)
    {
      numeric_negate (remainder);
    }
}

/*
 * numeric_div () -
 *   return:
 *   arg1(in)   : DB_C_NUMERIC             (numerator)
 *   arg2(in)   : DB_C_NUMERIC             (denominator)

 *   answer(in) : DB_C_NUMERIC
 *   remainder(in)      : DB_C_NUMERIC
 *
 * Note: This routine divides two numeric values and returns
 *       the result and remainder.  The division is broken down into 5 cases.
 *       Given arg1/arg2:
 *       a) if arg2 = 0, then SIGFPE ??, +/- MAX_NUM_DATA ??
 *       b) if arg1 = 0, then answer = remainder = 0
 *       c) if arg1, arg2 can be represented as a int
 *                       then answer = arg1/arg2,  remainder = arg1%arg2
 *       d) Otherwise, perform long division
 */
static void
numeric_div (DB_C_NUMERIC arg1, DB_C_NUMERIC arg2,
	     DB_C_NUMERIC answer, DB_C_NUMERIC remainder)
{
  int long_arg1;
  int long_arg2;

  /* Case 1 - arg2 = 0 */
  if (numeric_is_zero (arg2))
    {
      /* SIGFPE ??, +/- MAX_NUM_DATA ?? */
    }

  /* Case 2 - arg1 = 0.  Set answer and remainder to 0.  */
  else if (numeric_is_zero (arg1))
    {
      numeric_coerce_int_to_num (0, remainder);
      numeric_coerce_int_to_num (0, answer);
    }

  /* Case 3 - arg1, arg2 are long ints. Do machine divide */
  else if (numeric_is_long (arg1) && numeric_is_long (arg2))
    {
      numeric_coerce_num_to_int (arg1, &long_arg1);
      numeric_coerce_num_to_int (arg2, &long_arg2);
      numeric_coerce_int_to_num ((long_arg1 / long_arg2), answer);
      numeric_coerce_int_to_num ((long_arg1 % long_arg2), remainder);
    }

  /* Default case: perform long division  */
  else
    {
      numeric_long_div (arg1, arg2, answer, remainder);
    }
}

/*
 * numeric_compare () -
 *   return:
 *   arg1(in)   : DB_C_NUMERIC
 *   arg2(in)   : DB_C_NUMERIC
 *
 * Note: This routine compares two DB_C_NUMERIC values.
 *       This function returns:
 *          -1   if    arg1 < arg2
 *           0   if    arg1 = arg2 and
 *           1   if    arg1 > arg2.
 */
static int
numeric_compare (DB_C_NUMERIC arg1, DB_C_NUMERIC arg2)
{
  unsigned char narg1[DB_NUMERIC_BUF_SIZE];
  unsigned char narg2[DB_NUMERIC_BUF_SIZE];
  int arg1_sign, arg2_sign;	/* 0 if positive */

  arg1_sign = numeric_is_negative (arg1) ? 1 : 0;
  arg2_sign = numeric_is_negative (arg2) ? 1 : 0;

  if (arg1_sign < arg2_sign)
    {				/* arg1 >= 0, arg2 < 0 */
      return (1);
    }
  else if (arg1_sign > arg2_sign)
    {				/* arg1 < 0, arg2 >= 0 */
      return (-1);
    }
  else
    {
      if (arg1_sign == 0)
	{			/* arg1 >= 0, arg2 >= 0 */
	  return numeric_compare_pos (arg1, arg2);
	}
      else
	{			/* arg1 < 0, arg2 < 0 */
	  numeric_copy (narg1, arg1);	/* need copy? */
	  numeric_negate (narg1);
	  numeric_copy (narg2, arg2);	/* need copy? */
	  numeric_negate (narg2);
	  return -numeric_compare_pos (narg1, narg2);
	}
    }
}

/*
 * numeric_scale_by_ten () -
 *   return: NO_ERROR, or ER_code (ER_NUM_OVERFLOW)
 *   arg(in/out)    : ptr to a DB_NUMERIC structure
 *
 * Note: This routine scales arg by a factor of ten.
 */
static int
numeric_scale_by_ten (DB_C_NUMERIC arg)
{
  int i, answer;
  bool negative = false;
  int ret = NO_ERROR;

  answer = 0;
  if (numeric_is_negative (arg))
    {
      negative = true;
      numeric_negate (arg);
    }

  i = DB_NUMERIC_BUF_SIZE;
  while (i--)
    {
      answer = (10 * arg[i]) + CARRYOVER (answer);
      arg[i] = GET_LOWER_BYTE (answer);
    }

  if ((int) arg[0] > 0x7f)
    {
      ret = ER_NUM_OVERFLOW;
      goto exit_on_error;
    }

  if (negative)
    {
      numeric_negate (arg);
    }

end:

  return ret;

exit_on_error:

  if (ret == NO_ERROR)
    {
      ret = er_errid ();
      if (ret == NO_ERROR)
	{
	  ret = ER_FAILED;
	}
    }
  goto end;
}

/*
 * numeric_scale_dec () -
 *   return: NO_ERROR, or ER_code
 *   arg(in)    : ptr to a DB_C_NUMERIC structure
 *   dscale(in) : integer scaling factor (positive)
 *   answer(in) : ptr to a DB_C_NUMERIC structure
 *
 * Note: This routine returns a numeric value that has been scaled by the
 *       given number of decimal places.  The result is returned in answer.
 */
static int
numeric_scale_dec (DB_C_NUMERIC arg, int dscale, DB_C_NUMERIC answer)
{
  int loop;
  int ret = NO_ERROR;

  if (dscale >= 0)
    {
      numeric_copy (answer, arg);
      for (loop = 0; loop < dscale && ret == NO_ERROR; loop++)
	{
	  ret = numeric_scale_by_ten (answer);
	}
      if (ret != NO_ERROR)
	{
	  goto exit_on_error;
	}
    }

end:

  return ret;

exit_on_error:

  if (ret == NO_ERROR)
    {
      ret = er_errid ();
      if (ret == NO_ERROR)
	{
	  ret = ER_FAILED;
	}
    }
  goto end;
}

/*
 * numeric_common_prec_scale () -
 *   return: NO_ERROR, or ER_code
 *     Errors:
 *       ER_NUM_OVERFLOW          - if scaling would exceed max scale
 *   dbv1(in)   : ptr to a DB_VALUE structure of type DB_TYPE_NUMERIC
 *   dbv2(in)   : ptr to a DB_VALUE structure of type DB_TYPE_NUMERIC
 *   dbv1_common(in)    : ptr to a DB_VALUE structure of type DB_TYPE_NUMERIC
 *   dbv2_common(in)    : ptr to a DB_VALUE structure of type DB_TYPE_NUMERIC
 *
 * Note: This routine returns two DB_VALUE's of type numeric with the same
 *       scale.  dbv1_common, dbv2_common are set to dbv1, dbv2 respectively
 *       when an error occurs.
 */
static int
numeric_common_prec_scale (DB_VALUE * dbv1,
			   DB_VALUE * dbv2,
			   DB_VALUE * dbv1_common, DB_VALUE * dbv2_common)
{
  unsigned char temp[DB_NUMERIC_BUF_SIZE];	/* copy of a DB_C_NUMERIC */
  int scale1, scale2;
  int prec1, prec2;
  int cprec;
  int scale_diff;
  int ret = NO_ERROR;

  /* If scales already match, merely copy them and return */
  scale1 = DB_VALUE_SCALE (dbv1);
  scale2 = DB_VALUE_SCALE (dbv2);
  prec1 = DB_VALUE_PRECISION (dbv1);
  prec2 = DB_VALUE_PRECISION (dbv2);
  if (scale1 == scale2)
    {
      cprec = MAX (prec1, prec2);
      DB_MAKE_NUMERIC (dbv1_common, db_locate_numeric (dbv1), cprec, scale1);
      DB_MAKE_NUMERIC (dbv2_common, db_locate_numeric (dbv2), cprec, scale2);
    }

  /* Otherwise scale and reset the numbers */
  else if (scale1 < scale2)
    {
      scale_diff = scale2 - scale1;
      prec1 = scale_diff + prec1;
      if (prec1 > DB_MAX_NUMERIC_PRECISION)
	{
	  ret = ER_NUM_OVERFLOW;
	  er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_NUM_OVERFLOW, 0);
	  goto exit_on_error;
	}
      numeric_scale_dec (db_locate_numeric (dbv1), scale_diff, temp);
      cprec = MAX (prec1, prec2);
      DB_MAKE_NUMERIC (dbv1_common, temp, cprec, scale2);
      DB_MAKE_NUMERIC (dbv2_common, db_locate_numeric (dbv2), cprec, scale2);
    }
  else
    {
      scale_diff = scale1 - scale2;
      prec2 = scale_diff + prec2;
      if (prec2 > DB_MAX_NUMERIC_PRECISION)
	{
	  ret = ER_NUM_OVERFLOW;
	  er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_NUM_OVERFLOW, 0);
	  goto exit_on_error;
	}
      numeric_scale_dec (db_locate_numeric (dbv2), scale_diff, temp);
      cprec = MAX (prec1, prec2);
      DB_MAKE_NUMERIC (dbv2_common, temp, cprec, scale1);
      DB_MAKE_NUMERIC (dbv1_common, db_locate_numeric (dbv1), cprec, scale1);
    }

end:

  return ret;

exit_on_error:

  if (ret == NO_ERROR)
    {
      ret = er_errid ();
      if (ret == NO_ERROR)
	{
	  ret = ER_FAILED;
	}
    }
  goto end;
}

/*
 * numeric_prec_scale_when_overflow () -
 *   return: NO_ERROR, or ER_code
 *   dbv1(in)   :
 *   dbv2(in)   :
 *   dbv1_common(in)    :
 *   dbv2_common(in)    :
 */
static int
numeric_prec_scale_when_overflow (DB_VALUE * dbv1,
				  DB_VALUE * dbv2,
				  DB_VALUE * dbv1_common,
				  DB_VALUE * dbv2_common)
{
  int prec1, scale1, prec2, scale2;
  int prec, scale;
  unsigned char num1[DB_NUMERIC_BUF_SIZE], num2[DB_NUMERIC_BUF_SIZE];
  unsigned char temp[DB_NUMERIC_BUF_SIZE];
  int ret = NO_ERROR;

  prec1 = DB_VALUE_PRECISION (dbv1);
  prec2 = DB_VALUE_PRECISION (dbv2);
  scale1 = DB_VALUE_SCALE (dbv1);
  scale2 = DB_VALUE_SCALE (dbv2);

  scale = MAX (scale1, scale2);
  prec = DB_MAX_NUMERIC_PRECISION;

  numeric_copy (num1, db_locate_numeric (dbv1));
  numeric_copy (num2, db_locate_numeric (dbv2));

  ret = numeric_coerce_num_to_num (num1, prec1, scale1, prec, scale, temp);
  if (ret != NO_ERROR)
    {
      goto exit_on_error;
    }
  DB_MAKE_NUMERIC (dbv1_common, temp, prec, scale);

  ret = numeric_coerce_num_to_num (num2, prec2, scale2, prec, scale, temp);
  if (ret != NO_ERROR)
    {
      goto exit_on_error;
    }
  DB_MAKE_NUMERIC (dbv2_common, temp, prec, scale);

end:

  return ret;

exit_on_error:

  if (ret == NO_ERROR)
    {
      ret = er_errid ();
      if (ret == NO_ERROR)
	{
	  ret = ER_FAILED;
	}
    }
  goto end;
}

/*
 * numeric_coerce_big_num_to_dec_str () -
 *   return:
 *   num(in)    : buffer twice the size of a DB_C_NUMERIC
 *   dec_str(out): returned string of decimal digits as ASCII chars
 *
 * Note: This routine converts a DB_C_NUMERIC into a character string that is
 *       TWICE_NUM_MAX_PREC characters long that contains the decimal digits of
 *       the numeric encoded as ASCII characters.
 *       THIS ROUTINE ASSUMES THAT THE NUMERIC BUFFER REPRESENTS A POSITIVE
 *       VALUE.
 */
static void
numeric_coerce_big_num_to_dec_str (unsigned char *num, char *dec_str)
{
  DEC_STRING *bit_value;
  DEC_STRING result;
  unsigned int i;

  /*  Loop through the bits of the numeric building up string */
  numeric_zero_dec_str (&result);
  for (i = 0; i < DB_NUMERIC_BUF_SIZE * 16; i++)
    {
      if (numeric_is_bit_set (num, i))
	{
	  bit_value =
	    numeric_get_pow_of_2 ((DB_NUMERIC_BUF_SIZE * 16) - i - 1);
	  numeric_add_dec_str (bit_value, &result, &result);
	}
    }

  /*  Convert result into ASCII array */
  for (i = 0; i < TWICE_NUM_MAX_PREC; i++)
    {
      *dec_str = result.digits[i] + '0';
      dec_str++;
    }

  /*  Null terminate  */
  *dec_str = '\0';
}

/*
 * numeric_get_msb_for_dec () -
 *   return:
 *     Errors:
 *       ER_NUM_OVERFLOW          - if src exceeds max precision
 *   src_prec(in)       : int precision of src
 *   src_scale(in)      : int scale of src
 *   src(in)    : buffer to NUMERIC twice the length of the maximum
 *   dest_prec(out)      : ptr to a int precision of dest
 *   dest_scale(out)     : ptr to a int scale of dest
 *   dest(out)   : DB_C_NUMERIC
 *
 * Note: This routine returns a DB_C_NUMERIC along with the precision and
 *       scale of the MSB of the source.  Round-off occurs as long as the scale
 *       of the destination >= 0.
 * Note: it is assumed that src represents a positive number
 */
static int
numeric_get_msb_for_dec (int src_prec,
			 int src_scale,
			 unsigned char *src,
			 int *dest_prec, int *dest_scale, DB_C_NUMERIC dest)
{
  int ret = NO_ERROR;
  char dec_digits[TWICE_NUM_MAX_PREC + 2];

  /* If src precision fits without truncation, merely set dest to the lower
   * half of the source buffer and return */
  if (src_prec <= DB_MAX_NUMERIC_PRECISION)
    {
      numeric_copy (dest, &(src[DB_NUMERIC_BUF_SIZE]));
      *dest_prec = src_prec;
      *dest_scale = src_scale;
    }

  /*  The remaining cases are for when the precision of the source
   *  overflows.
   */

  /* Case 1:  The scale of the source does *not* overflow */
  else if (src_scale <= DB_MAX_NUMERIC_PRECISION)
    {
      /* If upper half of *src is zero, merely copy, reset precision,
       * and return
       */
      if (numeric_is_zero (src) && src[DB_NUMERIC_BUF_SIZE] <= 0x7F)
	{
	  numeric_copy (dest, &(src[DB_NUMERIC_BUF_SIZE]));
	  *dest_prec = DB_MAX_NUMERIC_PRECISION;
	  *dest_scale = src_scale;
	}
      else
	{
	  /* Can't truncate answer - expected results must maintain
	   * the proper amount of scaling
	   */
	  return (ER_NUM_OVERFLOW);
	}
    }

  /* Case 2:  The scale of the source overflows.
   * This means the number can't overflow as long as truncation
   * occurs. Reduce the scale and precision by the same amount.
   */
  else
    {
      int truncation_diff = src_prec - DB_MAX_NUMERIC_PRECISION;

      *dest_scale = src_scale - truncation_diff;
      *dest_prec = DB_MAX_NUMERIC_PRECISION;

      /* Truncate the obsolete trailing digits.
       * (Note: numeric_coerce_big_num_to_dec_str is guaranteed ro return
       *        a NULL-terminated buffer that is TWICE_NUM_MAX_PREC
       *        characters long.)
       */
      numeric_coerce_big_num_to_dec_str (src, dec_digits);
      dec_digits[TWICE_NUM_MAX_PREC - truncation_diff] = '\0';
      numeric_coerce_dec_str_to_num (dec_digits, dest);
    }

  return ret;
}

/*
 * numeric_db_value_add () -
 *   return: NO_ERROR, or ER_code
 *     Errors:
 *       ER_OBJ_INVALID_ARGUMENTS - if dbv1, dbv2, or answer are NULL or
 *                                  are not DB_TYPE_NUMERIC
 *   dbv1(in)   : ptr to a DB_VALUE structure of type DB_TYPE_NUMERIC
 *   dbv2(in)   : ptr to a DB_VALUE structure of type DB_TYPE_NUMERIC
 *   answer(out): ptr to a DB_VALUE structure of type DB_TYPE_NUMERIC
 *
 * Note: This routine adds the numeric values of two DB_VALUE structs and
 * returns the results in answer. The answer will be returned as either the
 * common type of dbv1 and dbv2 or as the common type of dbv1 and dbv2 with
 * an extra decimal place of precision if the sum requires it due to carry.
 * The answer is set to a NULL-valued DB_C_NUMERIC's when an error occurs.
 *
 */
int
numeric_db_value_add (DB_VALUE * dbv1, DB_VALUE * dbv2, DB_VALUE * answer)
{
  DB_VALUE dbv1_common, dbv2_common;
  int ret = NO_ERROR;
  unsigned int prec;
  unsigned char temp[DB_NUMERIC_BUF_SIZE];	/* Copy of a DB_C_NUMERIC */

  /* Check for bad inputs */
  if (answer == NULL)
    {
      return ER_OBJ_INVALID_ARGUMENTS;
    }
  if (dbv1 == NULL || DB_VALUE_TYPE (dbv1) != DB_TYPE_NUMERIC)
    {
      DB_MAKE_NULL (answer);
      return ER_OBJ_INVALID_ARGUMENTS;
    }
  if (dbv2 == NULL || DB_VALUE_TYPE (dbv2) != DB_TYPE_NUMERIC)
    {
      DB_MAKE_NULL (answer);
      return ER_OBJ_INVALID_ARGUMENTS;
    }

  /* Check for NULL values */
  if (DB_IS_NULL (dbv1) || DB_IS_NULL (dbv2))
    {
      db_value_domain_init (answer, DB_TYPE_NUMERIC,
			    DB_DEFAULT_PRECISION, DB_DEFAULT_SCALE);
      return NO_ERROR;
    }

  /* Coerce, if necessary, to make prec & scale match */
  ret = numeric_common_prec_scale (dbv1, dbv2, &dbv1_common, &dbv2_common);
  if (ret == ER_NUM_OVERFLOW)
    {
      ret = numeric_prec_scale_when_overflow (dbv1, dbv2,
					      &dbv1_common, &dbv2_common);
      if (ret != NO_ERROR)
	{
	  goto exit_on_error;
	}
    }
  else if (ret != NO_ERROR)
    {
      goto exit_on_error;
    }

  /* Perform the addition */
  numeric_add (db_locate_numeric (&dbv1_common),
	       db_locate_numeric (&dbv2_common), temp, DB_NUMERIC_BUF_SIZE);
  /*
   * Update the domin information of the answer. Check to see if precision
   * needs to be updated due to carry
   */
  prec = DB_VALUE_PRECISION (&dbv1_common);
  if (numeric_overflow (temp, prec))
    {
      if (prec < DB_MAX_NUMERIC_PRECISION)
	{
	  prec++;
	}
      else
	{
	  ret = ER_NUM_OVERFLOW;
	  er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_NUM_OVERFLOW, 0);
	  goto exit_on_error;
	}
    }
  DB_MAKE_NUMERIC (answer, temp, prec, DB_VALUE_SCALE (&dbv1_common));

end:

  return ret;

exit_on_error:

  db_value_domain_init (answer, DB_TYPE_NUMERIC,
			DB_DEFAULT_PRECISION, DB_DEFAULT_SCALE);

  if (ret == NO_ERROR)
    {
      ret = er_errid ();
      if (ret == NO_ERROR)
	{
	  ret = ER_FAILED;
	}
    }
  goto end;
}

/*
 * numeric_db_value_sub () -
 *   return: NO_ERROR, or ER_code
 *     Errors:
 *       ER_OBJ_INVALID_ARGUMENTS - if dbv1, dbv2, or answer are NULL or
 *                                  are not DB_TYPE_NUMERIC
 *   dbv1(in)   : ptr to a DB_VALUE structure of type DB_TYPE_NUMERIC
 *   dbv2(in)   : ptr to a DB_VALUE structure of type DB_TYPE_NUMERIC
 *   answer(out): ptr to a DB_VALUE structure of type DB_TYPE_NUMERIC
 *
 * Note: This routine subtracts the numeric values of two DB_VALUE's and
 * returns the results in answer. The answer will be returned as either the
 * common type of dbv1 and dbv2 or as the common type of dbv1 and dbv2 with
 * an extra decimal place of precision if the sum requires it due to carry.
 *
 * The answer is set to a NULL-valued DB_C_NUMERIC's when an error occurs.
 */
int
numeric_db_value_sub (DB_VALUE * dbv1, DB_VALUE * dbv2, DB_VALUE * answer)
{
  DB_VALUE dbv1_common, dbv2_common;
  int ret = NO_ERROR;
  unsigned int prec;
  unsigned char temp[DB_NUMERIC_BUF_SIZE];	/* Copy of a DB_C_NUMERIC */

  /* Check for bad inputs */
  if (answer == NULL)
    {
      return ER_OBJ_INVALID_ARGUMENTS;
    }
  if (dbv1 == NULL || DB_VALUE_TYPE (dbv1) != DB_TYPE_NUMERIC)
    {
      DB_MAKE_NULL (answer);
      return ER_OBJ_INVALID_ARGUMENTS;
    }
  if (dbv2 == NULL || DB_VALUE_TYPE (dbv2) != DB_TYPE_NUMERIC)
    {
      DB_MAKE_NULL (answer);
      return ER_OBJ_INVALID_ARGUMENTS;
    }

  /* Check for NULL values */
  if (DB_IS_NULL (dbv1) || DB_IS_NULL (dbv2))
    {
      db_value_domain_init (answer, DB_TYPE_NUMERIC,
			    DB_DEFAULT_PRECISION, DB_DEFAULT_SCALE);
      return NO_ERROR;
    }

  /* Coerce, if necessary, to make prec & scale match */
  ret = numeric_common_prec_scale (dbv1, dbv2, &dbv1_common, &dbv2_common);
  if (ret == ER_NUM_OVERFLOW)
    {
      ret = numeric_prec_scale_when_overflow (dbv1, dbv2,
					      &dbv1_common, &dbv2_common);
      if (ret != NO_ERROR)
	{
	  goto exit_on_error;
	}
    }
  else if (ret != NO_ERROR)
    {
      goto exit_on_error;
    }

  /* Perform the subtraction */
  numeric_sub (db_locate_numeric (&dbv1_common),
	       db_locate_numeric (&dbv2_common), temp, DB_NUMERIC_BUF_SIZE);
  /*
   * Update the domin information of the answer. Check to see if precision
   * needs to be updated due to carry
   */
  prec = DB_VALUE_PRECISION (&dbv1_common);
  if (numeric_overflow (temp, prec))
    {
      if (prec < DB_MAX_NUMERIC_PRECISION)
	{
	  prec++;
	}
      else
	{
	  ret = ER_NUM_OVERFLOW;
	  er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_NUM_OVERFLOW, 0);
	  goto exit_on_error;
	}
    }
  DB_MAKE_NUMERIC (answer, temp, prec, DB_VALUE_SCALE (&dbv1_common));

end:

  return ret;

exit_on_error:

  db_value_domain_init (answer, DB_TYPE_NUMERIC,
			DB_DEFAULT_PRECISION, DB_DEFAULT_SCALE);

  if (ret == NO_ERROR)
    {
      ret = er_errid ();
      if (ret == NO_ERROR)
	{
	  ret = ER_FAILED;
	}
    }
  goto end;
}

/*
 * numeric_db_value_mul () -
 *   return: NO_ERROR, or ER_code
 *     Errors:
 *       ER_OBJ_INVALID_ARGUMENTS - if dbv1, dbv2, or answer are NULL or
 *                                  are not DB_TYPE_NUMERIC
 *   dbv1(in)   : ptr to a DB_VALUE structure of type DB_TYPE_NUMERIC
 *   dbv2(in)   : ptr to a DB_VALUE structure of type DB_TYPE_NUMERIC
 *   answer(out): ptr to a DB_VALUE structure of type DB_TYPE_NUMERIC
 *
 * Note: This routine mulitiplies the numeric values of two DB_VALUE's and
 * returns the results in answer. The answer will be returned as either the
 * common type of dbv1 and dbv2 or as the common type of dbv1 and dbv2 with
 * a extra decimal places of precision if the product requires it to avoid
 * loss of data.
 *
 * The answer is set to a NULL-valued DB_C_NUMERIC's when an error occurs.
 */
int
numeric_db_value_mul (DB_VALUE * dbv1, DB_VALUE * dbv2, DB_VALUE * answer)
{
  int ret = NO_ERROR;
  int prec;
  int scale;
  bool positive_ans;
  unsigned char temp[2 * DB_NUMERIC_BUF_SIZE];	/* Copy of a DB_C_NUMERIC */
  unsigned char result[DB_NUMERIC_BUF_SIZE];	/* Copy of a DB_C_NUMERIC */

  /* Check for bad inputs */
  if (answer == NULL)
    {
      return ER_OBJ_INVALID_ARGUMENTS;
    }
  if (dbv1 == NULL || DB_VALUE_TYPE (dbv1) != DB_TYPE_NUMERIC)
    {
      DB_MAKE_NULL (answer);
      return ER_OBJ_INVALID_ARGUMENTS;
    }
  if (dbv2 == NULL || DB_VALUE_TYPE (dbv2) != DB_TYPE_NUMERIC)
    {
      DB_MAKE_NULL (answer);
      return ER_OBJ_INVALID_ARGUMENTS;
    }

  /* Check for NULL values */
  if (DB_IS_NULL (dbv1) || DB_IS_NULL (dbv2))
    {
      db_value_domain_init (answer, DB_TYPE_NUMERIC,
			    DB_DEFAULT_PRECISION, DB_DEFAULT_SCALE);
      return NO_ERROR;
    }

  /* Perform the multiplication */
  numeric_mul (db_locate_numeric (dbv1), db_locate_numeric (dbv2),
	       &positive_ans, temp);
  /* Check for overflow.  Reset precision & scale if necessary */
  prec = DB_VALUE_PRECISION (dbv1) + DB_VALUE_PRECISION (dbv2) + 1;
  scale = DB_VALUE_SCALE (dbv1) + DB_VALUE_SCALE (dbv2);
  ret = numeric_get_msb_for_dec (prec, scale, temp, &prec, &scale, result);
  if (ret != NO_ERROR)
    {
      goto exit_on_error;
    }

  /* If no error, make the answer */
  if (!positive_ans)
    {
      numeric_negate (result);
    }
  DB_MAKE_NUMERIC (answer, result, prec, scale);

end:

  return ret;

exit_on_error:

  db_value_domain_init (answer, DB_TYPE_NUMERIC,
			DB_DEFAULT_PRECISION, DB_DEFAULT_SCALE);

  if (ret == NO_ERROR)
    {
      ret = er_errid ();
      if (ret == NO_ERROR)
	{
	  ret = ER_FAILED;
	}
    }
  goto end;
}

/*
 * numeric_db_value_div () -
 *   return: NO_ERROR, or ER_code
 *     Errors:
 *       ER_OBJ_INVALID_ARGUMENTS - if dbv1, dbv2, or answer are NULL or
 *                                  are not DB_TYPE_NUMERIC
 *   dbv1(in)   : ptr to a DB_VALUE structure of type DB_TYPE_NUMERIC
 *   dbv2(in)   : ptr to a DB_VALUE structure of type DB_TYPE_NUMERIC
 *   answer(out): ptr to a DB_VALUE structure of type DB_TYPE_NUMERIC
 *
 * Note: This routine divides the numeric values of two DB_VALUE's and
 * returns the results in answer. The answer will be returned as either the
 * common type of dbv1 and dbv2 or as the common type of dbv1 and dbv2 with
 * a extra decimal places of precision if the quotient requires it to avoid
 * loss of data.
 *
 * The answer is set to a NULL-valued DB_C_NUMERIC's when an error occurs.
 */
int
numeric_db_value_div (DB_VALUE * dbv1, DB_VALUE * dbv2, DB_VALUE * answer)
{
  int prec;
  int max_scale, scale1, scale2;
  unsigned char dbv1_copy[DB_NUMERIC_BUF_SIZE];	/* Copy of a DB_C_NUMERIC */
  unsigned char temp_quo[DB_NUMERIC_BUF_SIZE];	/* Copy of a DB_C_NUMERIC */
  unsigned char temp_rem[DB_NUMERIC_BUF_SIZE];	/* Copy of a DB_C_NUMERIC */
  int scale, scaleup = 0;
  int ret = NO_ERROR;

  /* Check for bad inputs */
  if (answer == NULL)
    {
      return ER_OBJ_INVALID_ARGUMENTS;
    }
  if (dbv1 == NULL || DB_VALUE_TYPE (dbv1) != DB_TYPE_NUMERIC)
    {
      DB_MAKE_NULL (answer);
      return ER_OBJ_INVALID_ARGUMENTS;
    }
  if (dbv2 == NULL || DB_VALUE_TYPE (dbv2) != DB_TYPE_NUMERIC)
    {
      DB_MAKE_NULL (answer);
      return ER_OBJ_INVALID_ARGUMENTS;
    }

  /* Check for NULL values */
  if (DB_IS_NULL (dbv1) || DB_IS_NULL (dbv2))
    {
      db_value_domain_init (answer, DB_TYPE_NUMERIC,
			    DB_DEFAULT_PRECISION, DB_DEFAULT_SCALE);
      return NO_ERROR;
    }

  /* In order to maintain the proper number of scaling in the output,
   * find the maximum scale of the two args and make sure that the scale
   * of dbv1 exceeds the scale of dbv2 by that amount.
   */
  numeric_copy (dbv1_copy, db_locate_numeric (dbv1));
  scale1 = DB_VALUE_SCALE (dbv1);
  scale2 = DB_VALUE_SCALE (dbv2);
  max_scale = MAX (scale1, scale2);
  if (scale2 > 0)
    {
      scaleup = (max_scale + scale2) - scale1;
      ret = numeric_scale_dec (dbv1_copy, scaleup, dbv1_copy);
      if (ret != NO_ERROR)
	{			/* overflow */
	  goto exit_on_error;
	}
    }

  if (PRM_COMPAT_NUMERIC_DIVISION_SCALE)
    {
      numeric_div (dbv1_copy, db_locate_numeric (dbv2), temp_quo, temp_rem);
    }

  /*
   * Update the domain information of the answer. Check to see if precision
   * needs to be updated due to carry
   */
  prec = DB_VALUE_PRECISION (dbv1) + scaleup;
  scale = max_scale;
  if (prec > DB_MAX_NUMERIC_PRECISION)
    {
      prec = DB_MAX_NUMERIC_PRECISION;
    }

  if (!PRM_COMPAT_NUMERIC_DIVISION_SCALE)
    {
      if (scale < DB_DEFAULT_NUMERIC_DIVISION_SCALE)
	{
	  int new_scale, new_prec;
	  int scale_delta;
	  scale_delta = DB_DEFAULT_NUMERIC_DIVISION_SCALE - scale;
	  new_scale = scale + scale_delta;
	  new_prec = prec + scale_delta;
	  if (new_prec > DB_MAX_NUMERIC_PRECISION)
	    {
	      new_scale -= (new_prec - DB_MAX_NUMERIC_PRECISION);
	      new_prec = DB_MAX_NUMERIC_PRECISION;
	    }

	  ret = numeric_scale_dec (dbv1_copy, new_scale - scale, dbv1_copy);
	  if (ret != NO_ERROR)
	    {
	      goto exit_on_error;
	    }

	  scale = new_scale;
	  prec = new_prec;
	}

      numeric_div (dbv1_copy, db_locate_numeric (dbv2), temp_quo, temp_rem);
    }

  if (numeric_overflow (temp_quo, prec))
    {
      if (prec < DB_MAX_NUMERIC_PRECISION)
	{
	  prec++;
	}
      else
	{
	  ret = ER_NUM_OVERFLOW;
	  er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_NUM_OVERFLOW, 0);
	  goto exit_on_error;
	}
    }

  DB_MAKE_NUMERIC (answer, temp_quo, prec, scale);

end:

  return ret;

exit_on_error:

  db_value_domain_init (answer, DB_TYPE_NUMERIC,
			DB_DEFAULT_PRECISION, DB_DEFAULT_SCALE);

  if (ret == NO_ERROR)
    {
      ret = er_errid ();
      if (ret == NO_ERROR)
	{
	  ret = ER_FAILED;
	}
    }
  goto end;
}

/*
 * numeric_db_value_negate () -
 *   return: NO_ERROR, or ER_code
             The argument answer is modified in place.
 *     Errors:
 *       ER_OBJ_INVALID_ARGUMENTS - answer is not DB_TYPE_NUMERIC
 *   answer(in/out) : ptr to a DB_VALUE of type DB_TYPE_NUMERIC
 *
 * Note: This routine returns the negative (2's complement) of arg in answer.
 */
int
numeric_db_value_negate (DB_VALUE * answer)
{
  /* Check for NULL value */
  if (DB_IS_NULL (answer))
    {
      return NO_ERROR;
    }

  /* Check for bad inputs */
  if (answer == NULL || DB_VALUE_TYPE (answer) != DB_TYPE_NUMERIC)
    {
      return ER_OBJ_INVALID_ARGUMENTS;
    }

  /* Perform the negation */
  numeric_negate (db_locate_numeric (answer));

  return NO_ERROR;
}

/*
 * numeric_db_value_abs () -
 *   return:
 *   src_num(in)        :
 *   dest_num(in)       :
 */
void
numeric_db_value_abs (DB_C_NUMERIC src_num, DB_C_NUMERIC dest_num)
{
  numeric_copy (dest_num, src_num);
  if (numeric_is_negative (src_num))
    {
      numeric_negate (dest_num);
    }
}

/*
 * numeric_db_value_compare () -
 *   return: NO_ERROR, or ER_code
 *     Errors:
 *       ER_OBJ_INVALID_ARGUMENTS - if dbv1, dbv2, or answer are NULL or
 *                                  are not DB_TYPE_NUMERIC
 *   dbv1(in)   : ptr to a DB_VALUE of type DB_TYPE_NUMERIC
 *   dbv2(in)   : ptr to a DB_VALUE of type DB_TYPE_NUMERIC
 *   answer(out): ptr to a DB_VALUE of type DB_TYPE_INTEGER
 *
 * Note: This routine compares two numeric DB_VALUE's and sets the value of
 * answer accordingly. This function returns:
 *          -1   if    dbv1 < dbv2
 *           0   if    dbv1 = dbv2 and
 *           1   if    dbv1 > dbv2.
 */
int
numeric_db_value_compare (DB_VALUE * dbv1, DB_VALUE * dbv2, DB_VALUE * answer)
{
  DB_VALUE dbv1_common, dbv2_common;
  int ret = NO_ERROR;

  /* Check for bad inputs */
  if (answer == NULL)
    {
      return ER_OBJ_INVALID_ARGUMENTS;
    }
  if (dbv1 == NULL || DB_VALUE_TYPE (dbv1) != DB_TYPE_NUMERIC)
    {
      DB_MAKE_NULL (answer);
      return ER_OBJ_INVALID_ARGUMENTS;
    }
  if (dbv2 == NULL || DB_VALUE_TYPE (dbv2) != DB_TYPE_NUMERIC)
    {
      DB_MAKE_NULL (answer);
      return ER_OBJ_INVALID_ARGUMENTS;
    }

  /* Check for NULL values */
  if (DB_IS_NULL (dbv1) || DB_IS_NULL (dbv2))
    {
      db_value_domain_init (answer, DB_TYPE_INTEGER,
			    DB_DEFAULT_PRECISION, DB_DEFAULT_SCALE);
      return NO_ERROR;
    }

  /* Coerce, if necessary, to make prec & scale match */
  ret = numeric_common_prec_scale (dbv1, dbv2, &dbv1_common, &dbv2_common);
  if (ret != NO_ERROR)
    {
      ret = ER_NUM_OVERFLOW;
      goto exit_on_error;
    }

  /* Perform the comparison */
  DB_MAKE_INTEGER (answer,
		   numeric_compare (db_locate_numeric (&dbv1_common),
				    db_locate_numeric (&dbv2_common)));

end:

  return ret;

exit_on_error:

  db_value_domain_init (answer, DB_TYPE_INTEGER,
			DB_DEFAULT_PRECISION, DB_DEFAULT_SCALE);

  if (ret == NO_ERROR)
    {
      ret = er_errid ();
      if (ret == NO_ERROR)
	{
	  ret = ER_FAILED;
	}
    }
  goto end;
}

/*
 * numeric_coerce_int_to_num () -
 *   return:
 *   arg(in)    : unsigned int value
 *   answer(out): DB_C_NUMERIC
 *
 * Note: This routine converts 32 bit integer into DB_C_NUMERIC format and
 * returns the result.
 */
void
numeric_coerce_int_to_num (int arg, DB_C_NUMERIC answer)
{
  unsigned char pad;
  int digit;
  /*  Check for negative/positive and set pad accordingly  */
  pad = (arg >= 0) ? 0 : 0xff;
  /*  Copy the lower 32 bits into answer  */
  answer[DB_NUMERIC_BUF_SIZE - 1] = ((arg) & 0xff);
  answer[DB_NUMERIC_BUF_SIZE - 2] = ((arg >> 8) & 0xff);
  answer[DB_NUMERIC_BUF_SIZE - 3] = ((arg >> 16) & 0xff);
  answer[DB_NUMERIC_BUF_SIZE - 4] = ((arg >> 24) & 0xff);
  /*  Pad extra bytes of answer accordingly  */
  for (digit = DB_NUMERIC_BUF_SIZE - 5; digit >= 0; digit--)
    {
      answer[digit] = pad;
    }
}

/*
 * numeric_coerce_num_to_int () -
 *   return:
 *   arg(in)    : ptr to a DB_C_NUMERIC
 *   answer(out): ptr to an integer
 *
 * Note: This routine converts a numeric into an integer returns the result.
 * If arg overflows answer, answer is set to +/- MAXINT.
 */
void
numeric_coerce_num_to_int (DB_C_NUMERIC arg, int *answer)
{
  int digit;
  unsigned char pad;

  /*  Check for negative/positive and overflow  */
  pad = (numeric_is_negative (arg)) ? 0xff : 0;
  for (digit = DB_NUMERIC_BUF_SIZE - 5; digit >= 1; digit--)
    {
      if (arg[digit] != pad)
	{
	  if (pad == 0xff)
	    {
	      *answer = ~0;
	    }
	  else
	    {
	      *answer = ~0 >> 1;
	    }
	  return;
	}
    }

  /*  Copy the lower 32 bits into answer
   *  The unsigned int casts are necessary to avoid 16 bit integer overflow
   *  on the PC's
   */
  *answer = (arg[DB_NUMERIC_BUF_SIZE - 1]) +
    (((unsigned int) (arg[DB_NUMERIC_BUF_SIZE - 2])) << 8) +
    (((unsigned int) (arg[DB_NUMERIC_BUF_SIZE - 3])) << 16) +
    (((unsigned int) (arg[DB_NUMERIC_BUF_SIZE - 4])) << 24);
}

/*
 * numeric_coerce_dec_str_to_num () -
 *   return:
 *   dec_str(in): char * holds positive decimal digits as ASCII chars
 *   result(out): ptr to a DB_C_NUMERIC
 *
 * Note: This routine converts a character string that contains the positive
 * decimal digits of a numeric encoded as ASCII characters.
 */
void
numeric_coerce_dec_str_to_num (const char *dec_str, DB_C_NUMERIC result)
{
  unsigned char big_chunk[DB_NUMERIC_BUF_SIZE];	/* copy of a DB_C_NUMERIC */
  int ntot_digits;
  int ndigits;
  int dec_dig;
  int chunk_value;
  char temp_buffer[10];
  char *chunk;
  bool is_negative = false;

  /*  Zero out the result */
  numeric_zero (result, DB_NUMERIC_BUF_SIZE);
  /* Check for a negative number. Negative sign must be in the first
   * decimal place
   */
  if (*dec_str == '-')
    {
      is_negative = true;
      dec_str++;
    }

  /*  Loop through string reading 9 decimal digits at a time */
  ntot_digits = strlen ((char *) dec_str);
  chunk = (char *) dec_str + ntot_digits;
  for (dec_dig = ntot_digits - 1; dec_dig >= 0; dec_dig -= 9)
    {
      ndigits = MIN (dec_dig + 1, 9);
      chunk -= ndigits;
      memcpy (temp_buffer, chunk, ndigits);
      temp_buffer[ndigits] = '\0';
      chunk_value = (int) atol (temp_buffer);
      if (chunk_value != 0)
	{
	  numeric_coerce_int_to_num (chunk_value, big_chunk);
	  /* Scale the number if not first time through */
	  if (dec_dig != ntot_digits - 1)
	    {
	      numeric_scale_dec (big_chunk, ntot_digits - dec_dig - 1,
				 big_chunk);
	    }
	  numeric_add (big_chunk, result, result, DB_NUMERIC_BUF_SIZE);
	}
    }

  /* If negative, negate the result */
  if (is_negative)
    {
      numeric_negate (result);
    }
}

/*
 * numeric_coerce_num_to_dec_str () -
 *   return:
 *   num(in)    : DB_C_NUMERIC
 *   dec_str(out): returned string of decimal digits as ASCII chars
 *
 * Note: This routine converts a DB_C_NUMERIC into a character string that
 * contains the decimal digits of the numeric encoded as ASCII characters.
 */
void
numeric_coerce_num_to_dec_str (DB_C_NUMERIC num, char *dec_str)
{
  unsigned char local_num[DB_NUMERIC_BUF_SIZE];	/* copy of a DB_C_NUMERIC */
  DEC_STRING *bit_value;
  DEC_STRING result;
  unsigned int i, j;

  /*  Check if the number is negative */
  numeric_copy (local_num, num);
  if (numeric_is_negative (local_num))
    {
      *dec_str = '-';
      dec_str++;
      numeric_negate (local_num);
    }

  /*  Loop through the bits of the numeric building up string */
  numeric_zero_dec_str (&result);
  for (i = 0; i < DB_NUMERIC_BUF_SIZE * 8; i += 8)
    {
      if (local_num[i / 8] == 0)
	{
	  continue;
	}
      for (j = 0; j < 8; j++)
	{
	  if (numeric_is_bit_set (local_num, i + j))
	    {
	      bit_value =
		numeric_get_pow_of_2 ((DB_NUMERIC_BUF_SIZE * 8) - (i + j) -
				      1);
	      numeric_add_dec_str (bit_value, &result, &result);
	    }
	}
    }

  /*  Convert result into ASCII array */
  for (i = 0; i < TWICE_NUM_MAX_PREC; i++)
    {
      *dec_str = result.digits[i] + '0';
      dec_str++;
    }

  /*  Null terminate  */
  *dec_str = '\0';
}

/*
 * numeric_coerce_num_to_double () -
 *   return:
 *   num(in)    : DB_C_NUMERIC
 *   scale(in)  : integer value of the scale
 *   adouble(out): ptr to the returned double value
 *
 * Note: This routine converts a DB_C_NUMERIC into a double precision value.
 */
void
numeric_coerce_num_to_double (DB_C_NUMERIC num, int scale, double *adouble)
{
  char num_string[TWICE_NUM_MAX_PREC + 2];
  /* Convert the numeric to a decimal string */
  numeric_coerce_num_to_dec_str (num, num_string);
  /* Convert the decimal string into a double */
  *adouble = atof (num_string) / pow (10.0, scale);
}

/*
 * numeric_fast_convert () -
 *   return:
 *   adouble(in)        :
 *   dst_scale(in)      :
 *   num(in)    :
 *   prec(in)   :
 *   scale(in)  :
 */
static int
numeric_fast_convert (double adouble,
		      int dst_scale, DB_C_NUMERIC num, int *prec, int *scale)
{


  double scaled_double;
  int scaled_int, estimated_precision;
  scaled_double =
    (adouble * numeric_Pow_of_10[dst_scale]) + (adouble < 0.0 ? -0.5 : 0.5);
  scaled_int = (int) scaled_double;
  num[DB_NUMERIC_BUF_SIZE - 1] = (scaled_int >> 0) & 0xff;
  num[DB_NUMERIC_BUF_SIZE - 2] = (scaled_int >> 8) & 0xff;
  num[DB_NUMERIC_BUF_SIZE - 3] = (scaled_int >> 16) & 0xff;
  num[DB_NUMERIC_BUF_SIZE - 4] = (scaled_int >> 24) & 0xff;
  memset (num, (scaled_int < 0) ? 0xff : 0x0, DB_NUMERIC_BUF_SIZE - 4);
  /*
   * Now try to make an educated guess at the actual precision.  The
   * actual value of scaled_int is no longer of much interest, just so
   * long as the general magnitude is maintained (i.e., make sure you
   * keep the same number of significant decimal digits).
   */
  if (scaled_int < 0)
    {
      scaled_int = (scaled_int == DB_INT32_MIN) ? DB_INT32_MAX : -scaled_int;
    }

  if (scaled_int < 10L)
    {
      estimated_precision = 1;
    }
  else if (scaled_int < 100L)
    {
      estimated_precision = 2;
    }
  else if (scaled_int < 1000L)
    {
      estimated_precision = 3;
    }
  else if (scaled_int < 10000L)
    {
      estimated_precision = 4;
    }
  else if (scaled_int < 100000L)
    {
      estimated_precision = 5;
    }
  else if (scaled_int < 1000000L)
    {
      estimated_precision = 6;
    }
  else if (scaled_int < 10000000L)
    {
      estimated_precision = 7;
    }
  else if (scaled_int < 100000000L)
    {
      estimated_precision = 8;
    }
  else if (scaled_int < 1000000000L)
    {
      estimated_precision = 9;
    }
  else
    {
      estimated_precision = 10;
    }

  /*
   * No matter what we think it is, it has to be at least as big as the
   * scale.
   */
  if (estimated_precision < dst_scale)
    {
      estimated_precision = dst_scale;
    }

  *prec = estimated_precision;
  *scale = dst_scale;
  return NO_ERROR;
}

/*
 * numeric_internal_double_to_num () -
 *   return: NO_ERROR, or ER_code
 *   adouble(in)        :
 *   dst_scale(in)      :
 *   num(in)    :
 *   prec(in)   :
 *   scale(in)  :
 */
int
numeric_internal_double_to_num (double adouble,
				int dst_scale,
				DB_C_NUMERIC num, int *prec, int *scale)
{
  char dbl_string[TWICE_NUM_MAX_PREC + 2];
  char num_string[TWICE_NUM_MAX_PREC + 1];
  int i, len;

  /* Assert that hard coded values for DB_MAX_NUMERIC_PRECISION are correct */
  assert (DB_MAX_NUMERIC_PRECISION == 38);

  /* Check for DB_NUMERIC overflow */
  if (NUMERIC_ABS (adouble) > 1.0E38)
    {
      return (ER_NUM_OVERFLOW);
    }

  /*
   * See if this combination of value and scale will permit the use of
   * a fast conversion.  If the scale and value are small enough, we
   * can do the conversion using native multiplication and casting, and
   * that's a *lot* faster than going through the other stuff.
   */
  if (dst_scale < 10
      && NUMERIC_ABS (adouble) < numeric_Upper_limit[dst_scale])
    {
      return numeric_fast_convert (adouble, dst_scale, num, prec, scale);
    }

  /*
   * Print the double to a string.  Don't bother getting any more
   * digits of scale than we'll need for the eventual answer; this pays
   * off big in avoided calls to numeric_add_dec_str later on (because
   * numeric_coerce_num_to_dec_str doesn't have to work on so many digits).
   */
  sprintf (dbl_string, "%38.*f", dst_scale, adouble);
  /* Remove the decimal point, track the prec & scale */
  *prec = 0;
  *scale = 0;
  for (i = 0, len = strlen (dbl_string); i < len; i++)
    {
      if (dbl_string[i] == '.')
	{
	  *scale = len - (i + 1);
	}
      else if (dbl_string[i] != ' ')
	{
	  num_string[*prec] = dbl_string[i];
	  (*prec)++;
	}
    }
  /* Have already checked for overflow, so if
   * precision > DB_MAX_NUMERIC_PRECISION, then must have extra
   * digits at the (least significant) end of the numeric string.
   * If so, silently truncate and reset precision and scale.
   */
  if (*prec > DB_MAX_NUMERIC_PRECISION
      && (*prec - *scale) <= DB_MAX_NUMERIC_PRECISION)
    {
      *scale = *scale - (*prec - DB_MAX_NUMERIC_PRECISION);
      if (*scale < 0)
	{
	  *scale = 0;
	}
      *prec = DB_MAX_NUMERIC_PRECISION;
    }
  num_string[*prec] = '\0';
  /* Convert to a DB_C_NUMERIC */
  numeric_coerce_dec_str_to_num (num_string, num);

  return NO_ERROR;
}

/*
 * numeric_coerce_double_to_num () -
 *   return:
 *   adouble(in): ptr to the returned double value
 *   num(out)   : DB_C_NUMERIC
 *   prec(out)  : integer value of the precision
 *   scale(out) : integer value of the scale
 *
 * Note: This routine converts a double precision value into a DB_C_NUMERIC.
 *     Works via the static routine numeric_internal_double_to_num (), which is
 *     also called from numeric_db_value_coerce_to_num () so that we can exploit info
 *     about the scale of the destination.
 */
int
numeric_coerce_double_to_num (double adouble,
			      DB_C_NUMERIC num, int *prec, int *scale)
{
  /*
   *   return numeric_internal_double_to_num(adouble, DB_MAX_NUMERIC_PRECISION,
   */
  return numeric_internal_double_to_num (adouble, 16, num, prec, scale);
}

/*
 * numeric_coerce_string_to_num () -
 *   return:
 *   astring(in) : ptr to the input character string (NULL term)
 *   result(out) : DB_VALUE of type numeric
 *
 * Note: This routine converts a string into a DB_VALUE.
 */
int
numeric_coerce_string_to_num (const char *astring, DB_VALUE * result)
{
  char num_string[TWICE_NUM_MAX_PREC + 1];
  unsigned char num[DB_NUMERIC_BUF_SIZE];
  int i;
  int astring_length = strlen (astring);
  int prec = 0;
  int scale = 0;
  bool leading_zeroes = true;
  bool sign_found = false;
  bool negate_value = false;
  bool pad_character_zero = false;
  int ret = NO_ERROR;

  /* Remove the decimal point, track the prec & scale */
  prec = 0;
  scale = 0;
  for (i = 0; i < astring_length && ret == NO_ERROR; i++)
    {
      if (astring[i] == '.')
	{
	  leading_zeroes = false;
	  scale = astring_length - (i + 1);
	}
      else if (leading_zeroes)
	{			/* Look for 1st digit between 1 & 9 */
	  if (astring[i] >= '1' && astring[i] <= '9')
	    {
	      leading_zeroes = false;
	      num_string[prec] = astring[i];
	      if (++prec > DB_MAX_NUMERIC_PRECISION)
		{
		  break;
		}
	    }
	  else if (astring[i] == '+' || astring[i] == '-')
	    {			/* sign found */
	      if (!sign_found)
		{
		  sign_found = true;
		  if (astring[i] == '-')
		    {
		      negate_value = true;
		    }
		}
	      else
		{		/* Duplicate sign characters */
		  ret = DOMAIN_INCOMPATIBLE;
		}
	    }
	  else if (astring[i] == '0')
	    {
	      /* leading pad '0' found */
	      pad_character_zero = true;
	    }
	  else if (astring[i] == ' ')
	    {
	      /* Just skip this.  OK to have leading spaces */
	      ;
	    }
	  else
	    {
	      /*  Stray Non-numeric compatible character */
	      ret = DOMAIN_INCOMPATIBLE;
	    }
	}
      else
	{
	  if (astring[i] >= '0' && astring[i] <= '9')
	    {
	      num_string[prec] = astring[i];
	      if (++prec > DB_MAX_NUMERIC_PRECISION)
		{
		  break;
		}
	    }
	  else if (astring[i] != ' ' && astring[i] != ',')
	    {
	      /* Illegal digit */
	      if (scale > 0)
		{
		  scale--;
		}
	      ret = DOMAIN_INCOMPATIBLE;
	    }
	}
    }

  /* If there is no for overflow, try to parse the decimal string */
  if (ret == NO_ERROR)
    {
      if (prec > DB_MAX_NUMERIC_PRECISION)
	{
	  ret = ER_NUM_OVERFLOW;
	  er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_NUM_OVERFLOW, 0);
	  goto exit_on_error;
	}

      if (prec == 0 && pad_character_zero)
	{
	  prec = 1;
	  num_string[0] = '0';
	  num_string[prec] = '\0';
	  numeric_coerce_dec_str_to_num (num_string, num);
	}
      else
	{
	  num_string[prec] = '\0';
	  numeric_coerce_dec_str_to_num (num_string, num);
	}
    }

  /* Make the return value */
  if (negate_value)
    {
      numeric_negate (num);
    }
  DB_MAKE_NUMERIC (result, num, prec, scale);

end:

  return ret;

exit_on_error:

  db_value_domain_init (result, DB_TYPE_NUMERIC,
			DB_DEFAULT_NUMERIC_PRECISION,
			DB_DEFAULT_NUMERIC_SCALE);

  if (ret == NO_ERROR)
    {
      ret = er_errid ();
      if (ret == NO_ERROR)
	{
	  ret = ER_FAILED;
	}
    }
  goto end;
}

/*
 * numeric_coerce_num_to_num () -
 *   return: NO_ERROR, or ER_code
 *   src_num(in)        : DB_C_NUMERIC
 *   src_prec(in)       : integer value of the precision
 *   src_scale(in)      : integer value of the scale
 *   dest_prec(in)      : integer value of the precision
 *   dest_scale(in)     : integer value of the scale
 *   dest_num(out)      : DB_C_NUMERIC
 * Note: This routine converts a numeric of a given precision and scale to
 * another precision and scale.
 */
int
numeric_coerce_num_to_num (DB_C_NUMERIC src_num,
			   int src_prec,
			   int src_scale,
			   int dest_prec, int dest_scale,
			   DB_C_NUMERIC dest_num)
{
  int ret = NO_ERROR;
  char num_string[DB_MAX_NUMERIC_PRECISION * 4];
  int scale_diff;
  int orig_length;
  int i, len;
  bool round_up = false;
  bool negate_answer;

  /* Check for trivial case */
  if (src_prec <= dest_prec && src_scale == dest_scale)
    {
      numeric_copy (dest_num, src_num);
      return NO_ERROR;
    }

  /* If src is negative, coerce the positive part now so that rounding
   * is always done in the correct 'direction'.
   */
  if (numeric_is_negative (src_num))
    {
      negate_answer = true;
      numeric_copy (dest_num, src_num);
      numeric_negate (dest_num);
    }
  else
    {
      negate_answer = false;
      numeric_copy (dest_num, src_num);
    }

  /* Convert the src_num into a decimal string */
  numeric_coerce_num_to_dec_str (dest_num, num_string);
  /* Scale the number */
  if (src_scale < dest_scale)
    {				/* add trailing zeroes */
      scale_diff = dest_scale - src_scale;
      orig_length = strlen (num_string);
      for (i = 0; i < scale_diff; i++)
	{
	  num_string[orig_length + i] = '0';
	}
      num_string[orig_length + scale_diff] = '\0';
    }
  else if (dest_scale < src_scale)
    {				/* Truncate and prepare for rounding */
      scale_diff = src_scale - dest_scale;
      orig_length = strlen (num_string);
      if (num_string[orig_length - scale_diff] >= '5'
	  && num_string[orig_length - scale_diff] <= '9')
	{
	  round_up = true;
	}
      num_string[orig_length - scale_diff] = '\0';
    }

  /*
   * Check to see if the scaled number 'fits' into the desired precision
   * and scaling by looking for significant digits prior to the last
   * 'precision' digits.
   */
  for (i = 0, len = strlen (num_string) - dest_prec; i < len; i++)
    {
      if (num_string[i] >= '1' && num_string[i] <= '9')
	{
	  ret = ER_NUM_OVERFLOW;
	  goto exit_on_error;
	}
    }

  /* Convert scaled string into destination */
  numeric_coerce_dec_str_to_num (num_string, dest_num);
  /* Round up, if necessary */
  if (round_up)
    {
      numeric_increase (dest_num);
    }

  /* Negate the answer, if necessary */
  if (negate_answer)
    {
      numeric_negate (dest_num);
    }

end:

  return ret;

exit_on_error:

  if (ret == NO_ERROR)
    {
      ret = er_errid ();
      if (ret == NO_ERROR)
	{
	  ret = ER_FAILED;
	}
    }
  goto end;
}

/*
 * numeric_db_value_coerce_to_num () -
 *   return: NO_ERROR, or ER_code
 *   src(in)     : ptr to a DB_VALUE of some numerical type
 *   dest(in/out): ptr to a DB_VALUE of type DB_TYPE_NUMERIC
 *   data_status(out): ptr to a DB_DATA_STATUS value
 *
 * Note: This routine converts a DB_VALUE of some numerical type into a
 * DB_VALUE of type DB_TYPE_NUMERIC.  The precision and scale fields of
 * are assumed to represent the desired values of the output.  If they are
 * set to DB_DEFAULT_PRECISION/SCALE, the default values are implied. If
 * they are set to 0, the precision and scale are set to be the maximum
 * amount necessary in order to preserve as much data as possible.
 */
int
numeric_db_value_coerce_to_num (DB_VALUE * src,
				DB_VALUE * dest, DB_DATA_STATUS * data_status)
{
  int ret = NO_ERROR;
  unsigned char num[DB_NUMERIC_BUF_SIZE];	/* copy of a DB_C_NUMERIC */
  int precision, scale;
  int desired_precision, desired_scale;

  *data_status = DATA_STATUS_OK;
  desired_precision = DB_VALUE_PRECISION (dest);
  desired_scale = DB_VALUE_SCALE (dest);
  /* Check for a non NULL src and a dest whose type is DB_TYPE_NUMERIC */
  /* Switch on the src type */
  switch (DB_VALUE_TYPE (src))
    {
    case DB_TYPE_DOUBLE:
      {
	double adouble = DB_GET_DOUBLE (src);
	ret = numeric_internal_double_to_num (adouble, desired_scale, num,
					      &precision, &scale);
	break;
      }

    case DB_TYPE_FLOAT:
      {
	double adouble = (double) DB_GET_FLOAT (src);
	ret = numeric_internal_double_to_num (adouble, desired_scale, num,
					      &precision, &scale);
	break;
      }

    case DB_TYPE_MONETARY:
      {
	double adouble = db_value_get_monetary_amount_as_double (src);
	ret = numeric_internal_double_to_num (adouble, desired_scale, num,
					      &precision, &scale);
	break;
      }

    case DB_TYPE_INTEGER:
      {
	int anint = DB_GET_INT (src);
	numeric_coerce_int_to_num (anint, num);
	precision = 10;
	scale = 0;
	break;
      }

    case DB_TYPE_SMALLINT:
      {
	int anint = (int) DB_GET_SMALLINT (src);
	numeric_coerce_int_to_num (anint, num);
	precision = 5;
	scale = 0;
	break;
      }

    case DB_TYPE_NUMERIC:
      {
	precision = DB_VALUE_PRECISION (src);
	scale = DB_VALUE_SCALE (src);
	numeric_copy (num, db_locate_numeric (src));
	break;
      }

    default:
      ret = DOMAIN_INCOMPATIBLE;
      break;
    }

  /* Make the destination value */
  if (ret == NO_ERROR)
    {
      /* Make the intermediate value */
      DB_MAKE_NUMERIC (dest, num, precision, scale);
      ret = numeric_coerce_num_to_num (db_locate_numeric (dest),
				       DB_VALUE_PRECISION (dest),
				       DB_VALUE_SCALE (dest),
				       desired_precision, desired_scale, num);
      if (ret != NO_ERROR)
	{
	  goto exit_on_error;
	}

      DB_MAKE_NUMERIC (dest, num, desired_precision, desired_scale);
    }

end:

  return ret;

exit_on_error:

  if (ret == NO_ERROR)
    {
      ret = er_errid ();
      if (ret == NO_ERROR)
	{
	  ret = ER_FAILED;
	}
    }
  goto end;
}

/*
 * numeric_db_value_coerce_from_num () -
 *   return: NO_ERROR, or ER_code
 *   src(in)     : ptr to a DB_VALUE of type DB_TYPE_NUMERIC
 *   dest(out)   : ptr to a DB_VALUE of some numerical type
 *   data_status(out): ptr to a DB_DATA_STATUS value
 *
 * Note: This routine converts a DB_VALUE of type DB_TYPE_NUMERIC into some
 * numerical type.
 */
int
numeric_db_value_coerce_from_num (DB_VALUE * src,
				  DB_VALUE * dest,
				  DB_DATA_STATUS * data_status)
{
  int ret = NO_ERROR;

  *data_status = DATA_STATUS_OK;
  /* Check for a DB_TYPE_NUMERIC src and a non NULL numerical dest */
  /* Switch on the dest type */
  switch (DB_VALUE_DOMAIN_TYPE (dest))
    {
    case DB_TYPE_DOUBLE:
      {
	double adouble;
	numeric_coerce_num_to_double (db_locate_numeric (src),
				      DB_VALUE_SCALE (src), &adouble);
	if (OR_CHECK_DOUBLE_OVERFLOW (adouble))
	  {
	    ret = ER_NUM_OVERFLOW;
	    goto exit_on_error;
	  }
	DB_MAKE_DOUBLE (dest, adouble);
	break;
      }

    case DB_TYPE_FLOAT:
      {
	double adouble;
	numeric_coerce_num_to_double (db_locate_numeric (src),
				      DB_VALUE_SCALE (src), &adouble);
	if (OR_CHECK_FLOAT_OVERFLOW (adouble))
	  {
	    ret = ER_NUM_OVERFLOW;
	    goto exit_on_error;
	  }
	DB_MAKE_FLOAT (dest, (float) adouble);
	break;
      }

    case DB_TYPE_MONETARY:
      {
	double adouble;
	numeric_coerce_num_to_double (db_locate_numeric (src),
				      DB_VALUE_SCALE (src), &adouble);
	DB_MAKE_MONETARY_AMOUNT (dest, adouble);
	break;
      }

    case DB_TYPE_INTEGER:
      {
	double adouble;
	numeric_coerce_num_to_double (db_locate_numeric (src),
				      DB_VALUE_SCALE (src), &adouble);
	if (OR_CHECK_INT_OVERFLOW (adouble))
	  {
	    ret = ER_NUM_OVERFLOW;
	    goto exit_on_error;
	  }
	DB_MAKE_INTEGER (dest, (int) ROUND (adouble));
	break;
      }

    case DB_TYPE_SMALLINT:
      {
	double adouble;
	numeric_coerce_num_to_double (db_locate_numeric (src),
				      DB_VALUE_SCALE (src), &adouble);
	if (OR_CHECK_SHORT_OVERFLOW (adouble))
	  {
	    ret = ER_NUM_OVERFLOW;
	    goto exit_on_error;
	  }
	DB_MAKE_SMALLINT (dest, (DB_C_SHORT) ROUND (adouble));
	break;
      }

    case DB_TYPE_NUMERIC:
      {
	ret = numeric_db_value_coerce_to_num (src, dest, data_status);
	break;
      }

    case DB_TYPE_CHAR:
      {
	char *return_string = NULL;
	char *temp_str;
	int size = 0;

	temp_str = numeric_db_value_print (src);
	size = strlen (temp_str);
	return_string = (char *) db_private_alloc (NULL, size + 1);
	strcpy (return_string, temp_str);
	DB_MAKE_CHAR (dest, size, return_string, size);
	dest->need_clear = true;
	break;
      }

    case DB_TYPE_VARCHAR:
      {
	char *return_string = NULL;
	char *temp_str;
	int size = 0;

	temp_str = numeric_db_value_print (src);
	size = strlen (temp_str);
	return_string = (char *) db_private_alloc (NULL, size + 1);
	strcpy (return_string, temp_str);
	DB_MAKE_VARCHAR (dest, size, return_string, size);
	dest->need_clear = true;
	break;
      }

    case DB_TYPE_NCHAR:
      {
	char *return_string = NULL;
	char *temp_str;
	int size = 0;

	temp_str = numeric_db_value_print (src);
	size = strlen (temp_str);
	return_string = (char *) db_private_alloc (NULL, size + 1);
	strcpy (return_string, temp_str);
	DB_MAKE_NCHAR (dest, size, return_string, size);
	dest->need_clear = true;
	break;
      }

    case DB_TYPE_VARNCHAR:
      {
	char *return_string = NULL;
	char *temp_str;
	int size = 0;

	temp_str = numeric_db_value_print (src);
	size = strlen (temp_str);
	return_string = (char *) db_private_alloc (NULL, size + 1);
	strcpy (return_string, temp_str);
	DB_MAKE_VARNCHAR (dest, size, return_string, size);
	dest->need_clear = true;
	break;
      }

    case DB_TYPE_TIME:
      {
	double adouble;
	DB_TIME v_time;
	int hour, minute, second;
	numeric_coerce_num_to_double (db_locate_numeric (src),
				      DB_VALUE_SCALE (src), &adouble);
	v_time = (int) (adouble + 0.5) % SECONDS_IN_A_DAY;
	db_time_decode (&v_time, &hour, &minute, &second);
	DB_MAKE_TIME (dest, hour, minute, second);
	break;
      }

    case DB_TYPE_DATE:
      {
	double adouble;
	DB_DATE v_date;
	int year, month, day;
	numeric_coerce_num_to_double (db_locate_numeric (src),
				      DB_VALUE_SCALE (src), &adouble);
	v_date = (DB_DATE) (adouble);
	db_date_decode (&v_date, &month, &day, &year);
	DB_MAKE_DATE (dest, month, day, year);
	break;
      }

    case DB_TYPE_TIMESTAMP:
      {
	double adouble;
	DB_TIMESTAMP v_timestamp;
	numeric_coerce_num_to_double (db_locate_numeric (src),
				      DB_VALUE_SCALE (src), &adouble);
	v_timestamp = (DB_TIMESTAMP) (adouble);
	DB_MAKE_TIMESTAMP (dest, v_timestamp);
	break;
      }

    default:
      ret = DOMAIN_INCOMPATIBLE;
      break;
    }

end:

  return ret;

exit_on_error:

  if (ret == NO_ERROR)
    {
      ret = er_errid ();
      if (ret == NO_ERROR)
	{
	  ret = ER_FAILED;
	}
    }
  goto end;
}

/*
 * numeric_db_value_print () -
 *   return: a static character buffer that contains the numeric printed in an
 *           ASCII format.
 *   val(in)    : DB_VALUE of type numeric to print
 *
 * Note: returns the null-terminated string form of val
 */
char *
numeric_db_value_print (DB_VALUE * val)
{
#if defined(SERVER_MODE)
  THREAD_ENTRY *th_entry;
  char *buf;
#else /* SERVER_MODE */
  static char buf[81];
#endif /* SERVER_MODE */
  char temp[80];
  int nbuf;
  int temp_size;
  int i;
  bool found_first_non_zero = false;
  int scale = db_value_scale (val);

#if defined(SERVER_MODE)
  th_entry = thread_get_thread_entry_info ();
  buf = th_entry->qp_num_buf;
#endif /* SERVER_MODE */

  /* Retrieve raw decimal string */
  numeric_coerce_num_to_dec_str (DB_GET_NUMERIC (val), temp);
  /* Remove the extra padded zeroes and add the decimal point */
  nbuf = 0;
  temp_size = strlen (temp);
  for (i = 0; i < temp_size; i++)
    {
      /* Add the negative sign */
      if (temp[i] == '-')
	{
	  buf[nbuf] = '-';
	  nbuf++;
	}

      /* Add decimal point */
      if (i == temp_size - scale)
	{
	  buf[nbuf] = '.';
	  nbuf++;
	}

      /* Check to see if the first significant digit has been found */
      if (!found_first_non_zero && temp[i] >= '1' && temp[i] <= '9')
	{
	  found_first_non_zero = true;
	}

      /* Remove leading zeroes */
      if (found_first_non_zero || i >= temp_size - scale - 1)
	{
	  buf[nbuf] = temp[i];
	  nbuf++;
	}
    }

  /* Null terminate */
  buf[nbuf] = '\0';
  return buf;
}

/*
 * numeric_db_value_is_zero () -
 *   return: bool
 *   arg(in)    : DB_VALUE of type DB_NUMERIC
 *
 * Note: This routine checks if arg = 0.
 *       This function returns:
 *           true   if    arg1 = 0 and
 *           false  otherwise.
 *
 */
bool
numeric_db_value_is_zero (const DB_VALUE * arg)
{
  if (!arg || DB_IS_NULL (arg))	/* NULL values are not 0 */
    {
      return false;
    }
  else
    {
      return (numeric_is_zero ((DB_C_NUMERIC) DB_GET_NUMERIC (arg)));
    }
}
