Skip to content

File numeric_opfunc.c

File List > cubrid > src > query > numeric_opfunc.c

Go to the documentation of this file

/*
 * Copyright 2008 Search Solution Corporation
 * Copyright 2016 CUBRID Corporation
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */

/*
 * 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 <float.h>
#include <math.h>
#include <assert.h>
#include <stdio.h>
#include <string.h>

#include "mprec.h"
#include "numeric_opfunc.h"
#include "tz_support.h"
#include "db_date.h"
#include "memory_alloc.h"
#include "system_parameter.h"
#include "byte_order.h"
#include "object_primitive.h"
#include "object_representation.h"

#if defined (__cplusplus)
#include <cmath>
#endif

#include "dbtype.h"
// XXX: SHOULD BE THE LAST INCLUDE HEADER
#include "memory_wrapper.hpp"

#if defined (SUPPRESS_STRLEN_WARNING)
#define strlen(s1)  ((int) strlen(s1))
#endif /* defined (SUPPRESS_STRLEN_WARNING) */

/* the multipler of long NUMERIC, internal used */
#define DB_LONG_NUMERIC_MULTIPLIER 2

#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)                  ((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 const double numeric_Pow_of_10[10] = {
  1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9
};

typedef enum fp_value_type
{
  FP_VALUE_TYPE_NUMBER,
  FP_VALUE_TYPE_INFINITE,
  FP_VALUE_TYPE_NAN,
  FP_VALUE_TYPE_ZERO
}
FP_VALUE_TYPE;

static bool numeric_is_negative (DB_C_NUMERIC arg);
static void numeric_copy (DB_C_NUMERIC dest, DB_C_NUMERIC source);
static void numeric_copy_long (DB_C_NUMERIC dest, DB_C_NUMERIC source, bool is_long_num);
static void numeric_increase (DB_C_NUMERIC answer);
static void numeric_increase_long (DB_C_NUMERIC answer, bool is_long_num);
static void numeric_decrease (DB_C_NUMERIC answer);
static void numeric_zero (DB_C_NUMERIC answer, int size);
static void numeric_init_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, bool is_long_num);
static int numeric_compare_pos (DB_C_NUMERIC arg1, DB_C_NUMERIC arg2);
static void numeric_negate (DB_C_NUMERIC answer);
static void numeric_negate_long (DB_C_NUMERIC answer, bool is_long_num);
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_bigint (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,
                  bool is_long_num);
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, bool is_long_num);
static int numeric_scale_dec (const DB_C_NUMERIC arg, int dscale, DB_C_NUMERIC answer);
static int numeric_scale_dec_long (DB_C_NUMERIC answer, int dscale, bool is_long_num);
static int numeric_common_prec_scale (const DB_VALUE * dbv1, const DB_VALUE * dbv2, DB_VALUE * dbv1_common,
                      DB_VALUE * dbv2_common);
static int numeric_prec_scale_when_overflow (const DB_VALUE * dbv1, const 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);
static FP_VALUE_TYPE get_fp_value_type (double d);
static int numeric_internal_real_to_num (double adouble, int dst_scale, DB_C_NUMERIC num, int *prec, int *scale,
                     bool is_float);
static void numeric_get_integral_part (const DB_C_NUMERIC num, const int src_prec, const int src_scale,
                       const int dst_prec, DB_C_NUMERIC dest);
static void numeric_get_fractional_part (const DB_C_NUMERIC num, const int src_scale, const int dst_prec,
                     DB_C_NUMERIC dest);
static bool numeric_is_fraction_part_zero (const DB_C_NUMERIC num, const int scale);
static bool numeric_is_longnum_value (DB_C_NUMERIC arg);
static int numeric_longnum_to_shortnum (DB_C_NUMERIC answer, DB_C_NUMERIC long_arg);
static void numeric_shortnum_to_longnum (DB_C_NUMERIC long_answer, DB_C_NUMERIC arg);
static int get_significant_digit (DB_BIGINT i);
/*
 * 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)
{
  numeric_copy_long (dest, source, false);
}

/*
 * numeric_copy_long () -
 *   return:
 *   dest(out)  : DB_C_NUMERIC value
 *   source(in) : DB_C_NUMERIC value
 *   is_long_num(in): is long NUMERIC
 * Note: This routine returns source copied into dest.
 */
static void
numeric_copy_long (DB_C_NUMERIC dest, DB_C_NUMERIC source, bool is_long_num)
{
  int num_cnt = 1;

  if (dest != source)
    {
      if (source == NULL || dest == NULL)
    {
      assert (0);
      return;
    }

      if (is_long_num)
    {
      num_cnt = DB_LONG_NUMERIC_MULTIPLIER;
    }
      memcpy (dest, source, DB_NUMERIC_BUF_SIZE * num_cnt);
    }
}

/*
 * 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)
{
  numeric_increase_long (answer, false);
}

/*
 * numeric_increase_long () -
 *   return:
 *   answer(in/out) : DB_C_NUMERIC value
 *   is_long_num(in): is long NUMERIC
 *
 * Note: This routine increments a numeric value.
 */
static void
numeric_increase_long (DB_C_NUMERIC answer, bool is_long_num)
{
  int carry = 1;
  int digit;

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

/*
 * numeric_decrease () -
 *   return:
 *   answer(in/out) : DB_C_NUMERIC value
 *
 * Note: This routine decrements a numeric value.
 */
static void
numeric_decrease (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] == 0xff) ? 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_negative_one () -
 *   return:
 *   answer(in) : DB_C_NUMERIC value
 *   size(in)   :
 *
 * Note: This routine make a numeric value as -1
 */
static void
numeric_negative_one (DB_C_NUMERIC answer, int size)
{
  memset (answer, 0xff, size);
}

/*
 * numeric_init_dec_str () -
 *   return:
 *   answer(in/out) : (IN/OUT) ptr to a DEC_STRING
 *
 * Note: Fills a DEC_STRING with -1 constant bytes and zero rightmost byte
 *
 *       digits:[00][01][02]......[73][74][75]
 *       values: -1  -1  -1 ...... -1  -1   0
 */
static void
numeric_init_dec_str (DEC_STRING * answer)
{
  /* sizeof(answer->digits[0]) == 1 */
  memset (answer->digits, -1, TWICE_NUM_MAX_PREC);

  /* Set first element to 0 */
  answer->digits[TWICE_NUM_MAX_PREC - 1] = 0;
}

/*
 * 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;
  char arg1_dec, arg2_dec;

  /* Loop through the characters setting answer */
  for (digit = TWICE_NUM_MAX_PREC - 1; digit >= 0; digit--)
    {
      arg1_dec = arg1->digits[digit];
      arg2_dec = arg2->digits[digit];

      if (arg1_dec == -1)
    {
      arg1_dec = 0;

      if (answer_bit < 10)
        {
          break;        /* pass through the leftmost digits */
        }
    }

      if (arg2_dec == -1)
    {
      /* is not first element */
      assert (digit < TWICE_NUM_MAX_PREC - 1);

      arg2_dec = 0;
    }

      assert (arg1_dec >= 0);
      assert (arg2_dec >= 0);

      answer_bit = (arg1_dec + arg2_dec) + (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_init_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_init_dec_str (&(powers_of_2[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)
{
  assert (exp < (int) (DB_NUMERIC_BUF_SIZE * 16 - 3));  /* exp < 253 */

#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)
{
  assert (exp < (int) sizeof (powers_of_10));

#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
 *   is_long_num(in) : is long 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.
 *       only arg1 and lsb may be long NUMERIC.
 */
static void
numeric_double_shift_bit (DB_C_NUMERIC arg1, DB_C_NUMERIC arg2, int numbits, DB_C_NUMERIC lsb, DB_C_NUMERIC msb,
              bool is_long_num)
{
  /* the largest buf size of DB_C_NUMERIC */
  unsigned char local_arg1[DB_NUMERIC_BUF_SIZE * DB_LONG_NUMERIC_MULTIPLIER];
  unsigned char local_arg2[DB_NUMERIC_BUF_SIZE];    /* copy of a DB_C_NUMERIC */
  unsigned int digit;
  unsigned int buf_size;

  if (is_long_num)
    {
      buf_size = DB_NUMERIC_BUF_SIZE * DB_LONG_NUMERIC_MULTIPLIER;
    }
  else
    {
      buf_size = DB_NUMERIC_BUF_SIZE;
    }

  /* Copy args into local variables */
  numeric_copy_long (local_arg1, arg1, is_long_num);
  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 < buf_size - 1; digit++)
    {
      lsb[digit] = (local_arg1[digit] << numbits) | (local_arg1[digit + 1] >> (8 - numbits));
    }

  /* Do last word of lsb separately.  */
  lsb[buf_size - 1] = local_arg1[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)
{
  numeric_negate_long (answer, false);
}

/*
 * numeric_negate_long () -
 *   return:
 *   answer(in/out) : DB_C_NUMERIC
 *   is_long_num(in): is long 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_long (DB_C_NUMERIC answer, bool is_long_num)
{
  unsigned int digit;
  unsigned int buf_size;

  if (is_long_num)
    {
      buf_size = DB_NUMERIC_BUF_SIZE * DB_LONG_NUMERIC_MULTIPLIER;
    }
  else
    {
      buf_size = DB_NUMERIC_BUF_SIZE;
    }

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

  /* Add one to answer */
  numeric_increase_long (answer, is_long_num);
}

/*
 * 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;

  /* 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 - sizeof (int); digit++)
    {
      if (arg[digit] != pad)
    {
      return (false);
    }
    }

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

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

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

  /*
   * Loop through arg's bits except the 64 LSB looking for non-sign
   * extended values
   */
  for (digit = 1; digit < DB_NUMERIC_BUF_SIZE - sizeof (DB_BIGINT); 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
 *   is_long_num(in)    : is a1 and answer is long 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>".
 *       Only a1(the dividend) and answer(the quotient) can be long numeric.
 */
static void
numeric_long_div (DB_C_NUMERIC a1, DB_C_NUMERIC a2, DB_C_NUMERIC answer, DB_C_NUMERIC remainder, bool is_long_num)
{
  unsigned int nbit, total_bit;
  unsigned int buf_size;
  /* the largest buf size for DB_C_NUMERIC */
  unsigned char arg1[DB_LONG_NUMERIC_MULTIPLIER * DB_NUMERIC_BUF_SIZE];
  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;

  /* calculate basic variables */
  if (is_long_num)
    {
      buf_size = DB_NUMERIC_BUF_SIZE * DB_LONG_NUMERIC_MULTIPLIER;
    }
  else
    {
      buf_size = DB_NUMERIC_BUF_SIZE;
    }

  total_bit = buf_size * 8;

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

  /* If arg1 is negative, toggle sign and make arg1 positive */
  if (numeric_is_negative (arg1))
    {
      numeric_negate_long (arg1, is_long_num);
      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_long (answer, arg1, is_long_num);
  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 < total_bit; nbit++)
    {
      numeric_double_shift_bit (answer, remainder, 1, answer, remainder, is_long_num);

      /* 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[buf_size - 1] += 1;
    }
    }

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

  /* 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)
{
  /* 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))
    {
      int long_arg1, 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);
    }

  /* Case 4 - arg1, arg2 are bigints. Do machine divide */
  else if (numeric_is_bigint (arg1) && numeric_is_bigint (arg2))
    {
      DB_BIGINT bi_arg1, bi_arg2;

      numeric_coerce_num_to_bigint (arg1, 0, &bi_arg1);
      numeric_coerce_num_to_bigint (arg2, 0, &bi_arg2);
      numeric_coerce_bigint_to_num ((bi_arg1 / bi_arg2), answer);
      numeric_coerce_bigint_to_num ((bi_arg1 % bi_arg2), remainder);
    }

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

/*
 * numeric_is_longnum_value ()
 *   return:
 *   arg(in)   : DB_C_NUMERIC
 *
 * Note: This routine check whether the value numeric is long NUMERIC.
 *       Attention: the arg should be long NUMERIC.
 */
static bool
numeric_is_longnum_value (DB_C_NUMERIC arg)
{
  int total_nums = (DB_LONG_NUMERIC_MULTIPLIER - 1) * DB_NUMERIC_BUF_SIZE;
  int i;

  if (numeric_is_negative (arg))
    {
      for (i = 0; i < total_nums; i++)
    {
      if (arg[i] != 0xff)
        {
          return true;
        }
    }

      if (!(arg[i] & 0x80))
    {
      return true;
    }

    }
  else
    {
      for (i = 0; i < total_nums; i++)
    {
      if (arg[i] != 0)
        {
          return true;
        }
    }

      if (arg[i] & 0x80)
    {
      return true;
    }
    }

  return false;
}

/*
 * numeric_shortnum_to_longnum ()
 *   return:
 *   long_answer(out): the long NUMERIC
 *   arg(in)         : DB_C_NUMERIC
 *
 * Note: This routine translate a normal NUMERIC to long NUMERIC.
 *       Attention: the long_answer should be long NUMERIC.
 */
static void
numeric_shortnum_to_longnum (DB_C_NUMERIC long_answer, DB_C_NUMERIC arg)
{
  bool is_negative;
  int i;

  is_negative = numeric_is_negative (arg);
  for (i = 0; i < DB_LONG_NUMERIC_MULTIPLIER - 1; i++)
    {
      if (is_negative)
    {
      numeric_negative_one (long_answer + i * DB_NUMERIC_BUF_SIZE, DB_NUMERIC_BUF_SIZE);
    }
      else
    {
      numeric_zero (long_answer + i * DB_NUMERIC_BUF_SIZE, DB_NUMERIC_BUF_SIZE);
    }
    }
  numeric_copy (long_answer + i * DB_NUMERIC_BUF_SIZE, arg);
}


/*
 * numeric_longnum_to_shortnum ()
 *   return:
 *  answer(out): DB_C_NUMERIC
 *   arg(in)   : long NUMERIC
 *
 * Note: This routine translate a long NUMERIC to normal NUMERIC.
 *       Attention: the long_answer should be long NUMERIC.
 */
static int
numeric_longnum_to_shortnum (DB_C_NUMERIC answer, DB_C_NUMERIC long_arg)
{
  if (numeric_is_longnum_value (long_arg))
    {
      return ER_IT_DATA_OVERFLOW;
    }

  numeric_copy (answer, long_arg + (DB_LONG_NUMERIC_MULTIPLIER - 1) * DB_NUMERIC_BUF_SIZE);
  return NO_ERROR;
}

/*
 * 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_IT_DATA_OVERFLOW)
 *   arg(in/out)    : ptr to a DB_NUMERIC structure
 *   is_long_num(in): is long NUMERIC
 *
 * Note: This routine scales arg by a factor of ten.
 */
static int
numeric_scale_by_ten (DB_C_NUMERIC arg, bool is_long_num)
{
  int i, answer;
  bool negative = false;

  answer = 0;
  if (numeric_is_negative (arg))
    {
      negative = true;
      numeric_negate_long (arg, is_long_num);
    }

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

  if ((int) arg[0] > 0x7f)
    {
      return ER_IT_DATA_OVERFLOW;
    }

  if (negative)
    {
      numeric_negate_long (arg, is_long_num);
    }

  return NO_ERROR;
}

/*
 * 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 (const DB_C_NUMERIC arg, int dscale, DB_C_NUMERIC answer)
{
  int ret = NO_ERROR;

  if (dscale >= 0)
    {
      numeric_copy (answer, arg);
      ret = numeric_scale_dec_long (answer, dscale, false);
    }

  return ret;
}

/*
 * numeric_scale_dec_long () -
 *   return: NO_ERROR, or ER_code
 *   answer(in/out) : ptr to a DB_C_NUMERIC structure
 *   dscale(in) : integer scaling factor (positive)
 *   is_long_num: is long NUMERIC
 *
 * 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_long (DB_C_NUMERIC answer, int dscale, bool is_long_num)
{
  int loop;
  int ret = NO_ERROR;

  if (dscale >= 0)
    {
      for (loop = 0; loop < dscale && ret == NO_ERROR; loop++)
    {
      ret = numeric_scale_by_ten (answer, is_long_num);
    }
      if (ret != NO_ERROR)
    {
      return ret;
    }
    }

  return ret;
}

/*
 * numeric_common_prec_scale () -
 *   return: NO_ERROR, or ER_code
 *     Errors:
 *       ER_IT_DATA_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(out): ptr to a DB_VALUE structure of type DB_TYPE_NUMERIC
 *   dbv2_common(out): 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 (const DB_VALUE * dbv1, const 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;
  TP_DOMAIN *domain;

  /* 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)
    {
      domain = tp_domain_resolve_default (DB_TYPE_NUMERIC);
      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_IT_DATA_OVERFLOW, 1, pr_type_name (TP_DOMAIN_TYPE (domain)));
      return ER_IT_DATA_OVERFLOW;
    }
      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)
    {
      domain = tp_domain_resolve_default (DB_TYPE_NUMERIC);
      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_IT_DATA_OVERFLOW, 1, pr_type_name (TP_DOMAIN_TYPE (domain)));
      return ER_IT_DATA_OVERFLOW;
    }
      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);
    }

  return NO_ERROR;
}

/*
 * numeric_prec_scale_when_overflow () -
 *   return: NO_ERROR, or ER_code
 *   dbv1(in)   :
 *   dbv2(in)   :
 *   dbv1_common(out)    :
 *   dbv2_common(out)    :
 */
static int
numeric_prec_scale_when_overflow (const DB_VALUE * dbv1, const 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;

  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)
    {
      return ret;
    }
  db_make_numeric (dbv1_common, temp, prec, scale);

  ret = numeric_coerce_num_to_num (num2, prec2, scale2, prec, scale, temp);
  if (ret != NO_ERROR)
    {
      return ret;
    }
  db_make_numeric (dbv2_common, temp, prec, scale);

  return ret;
}

/*
 * 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_init_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++)
    {
      if (result.digits[i] == -1)
    {
      result.digits[i] = 0;
    }
      assert (result.digits[i] >= 0);

      *dec_str = result.digits[i] + '0';
      dec_str++;
    }

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

/*
 * numeric_get_msb_for_dec () -
 *   return:
 *     Errors:
 *       ER_IT_DATA_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_IT_DATA_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 (const DB_VALUE * dbv1, const 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 */
  TP_DOMAIN *domain;

  /* 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_IT_DATA_OVERFLOW)
    {
      ret = numeric_prec_scale_when_overflow (dbv1, dbv2, &dbv1_common, &dbv2_common);
      if (ret != NO_ERROR)
    {
      goto exit_on_error;
    }
      else
    {
      er_clear ();
    }
    }
  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
    {
      domain = tp_domain_resolve_default (DB_TYPE_NUMERIC);
      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_IT_DATA_OVERFLOW, 1, pr_type_name (TP_DOMAIN_TYPE (domain)));
      ret = ER_IT_DATA_OVERFLOW;
      goto exit_on_error;
    }
    }
  db_make_numeric (answer, temp, prec, DB_VALUE_SCALE (&dbv1_common));

  return ret;

exit_on_error:

  db_value_domain_init (answer, DB_TYPE_NUMERIC, DB_DEFAULT_PRECISION, DB_DEFAULT_SCALE);

  return (ret == NO_ERROR && (ret = er_errid ()) == NO_ERROR) ? ER_FAILED : ret;
}

/*
 * 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 (const DB_VALUE * dbv1, const 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 */
  TP_DOMAIN *domain;

  /* 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_IT_DATA_OVERFLOW)
    {
      ret = numeric_prec_scale_when_overflow (dbv1, dbv2, &dbv1_common, &dbv2_common);
      if (ret != NO_ERROR)
    {
      goto exit_on_error;
    }
      else
    {
      er_clear ();
    }
    }
  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
    {
      domain = tp_domain_resolve_default (DB_TYPE_NUMERIC);
      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_IT_DATA_OVERFLOW, 1, pr_type_name (TP_DOMAIN_TYPE (domain)));
      ret = ER_IT_DATA_OVERFLOW;
      goto exit_on_error;
    }
    }
  db_make_numeric (answer, temp, prec, DB_VALUE_SCALE (&dbv1_common));

  return ret;

exit_on_error:

  db_value_domain_init (answer, DB_TYPE_NUMERIC, DB_DEFAULT_PRECISION, DB_DEFAULT_SCALE);

  return (ret == NO_ERROR && (ret = er_errid ()) == NO_ERROR) ? ER_FAILED : ret;
}

/*
 * 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 multiplies 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 (const DB_VALUE * dbv1, const 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);

  return ret;

exit_on_error:

  db_value_domain_init (answer, DB_TYPE_NUMERIC, DB_DEFAULT_PRECISION, DB_DEFAULT_SCALE);

  return (ret == NO_ERROR && (ret = er_errid ()) == NO_ERROR) ? ER_FAILED : ret;
}

/*
 * 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 (const DB_VALUE * dbv1, const DB_VALUE * dbv2, DB_VALUE * answer)
{
  int prec;
  int max_scale, scale1, scale2;
  unsigned char long_dbv1_copy[DB_LONG_NUMERIC_MULTIPLIER * DB_NUMERIC_BUF_SIZE];
  unsigned char long_temp_quo[DB_LONG_NUMERIC_MULTIPLIER * DB_NUMERIC_BUF_SIZE];
  unsigned char dbv1_copy[DB_NUMERIC_BUF_SIZE]; /* Copy of a DB_C_NUMERIC */
  unsigned char dbv2_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;
  TP_DOMAIN *domain;
  DB_C_NUMERIC divisor_p;

  /* 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_shortnum_to_longnum (long_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_long (long_dbv1_copy, scaleup, true);
      if (ret != NO_ERROR)
    {           /* overflow */
      goto exit_on_error;
    }
    }

  /*
   * 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_get_bool_value (PRM_ID_COMPAT_NUMERIC_DIVISION_SCALE) && 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_long (long_dbv1_copy, new_scale - scale, true);
      if (ret != NO_ERROR)
    {
      goto exit_on_error;
    }

      scale = new_scale;
      prec = new_prec;
    }

  if (numeric_is_longnum_value (long_dbv1_copy))
    {
      /* only the dividend and quotient maybe long numeric, divisor must be numeric */
      numeric_long_div (long_dbv1_copy, db_locate_numeric (dbv2), long_temp_quo, temp_rem, true);
      ret = numeric_longnum_to_shortnum (temp_quo, long_temp_quo);
      if (ret != NO_ERROR)
    {
      goto exit_on_error;
    }
    }
  else
    {
      numeric_longnum_to_shortnum (dbv1_copy, long_dbv1_copy);
      numeric_div (dbv1_copy, db_locate_numeric (dbv2), temp_quo, temp_rem);
    }

  /* round! Check if remainder is larger than or equal to 2*divisor. i.e. rem / divisor >= 0.5 */

  /* first convert to positive number Note that reminder and dbv2 must be numeric, so we don't consider long numeric. */
  if (numeric_is_negative (temp_rem))
    {
      numeric_negate (temp_rem);
    }

  if (numeric_is_negative (db_locate_numeric (dbv2)))
    {
      numeric_copy (dbv2_copy, db_locate_numeric (dbv2));
      numeric_negate (dbv2_copy);
      divisor_p = dbv2_copy;
    }
  else
    {
      divisor_p = db_locate_numeric (dbv2);
    }

  numeric_add (temp_rem, temp_rem, temp_rem, DB_NUMERIC_BUF_SIZE);
  if (numeric_compare (temp_rem, divisor_p) >= 0)
    {
      if (numeric_is_negative (temp_quo))
    {
      /* for negative number */
      numeric_decrease (temp_quo);
    }
      else
    {
      numeric_increase (temp_quo);
    }
    }

  if (numeric_overflow (temp_quo, prec))
    {
      if (prec < DB_MAX_NUMERIC_PRECISION)
    {
      prec++;
    }
      else
    {
      domain = tp_domain_resolve_default (DB_TYPE_NUMERIC);
      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_IT_DATA_OVERFLOW, 1, pr_type_name (TP_DOMAIN_TYPE (domain)));
      ret = ER_IT_DATA_OVERFLOW;
      goto exit_on_error;
    }
    }

  db_make_numeric (answer, temp_quo, prec, scale);

  return ret;

exit_on_error:

  db_value_domain_init (answer, DB_TYPE_NUMERIC, DB_DEFAULT_PRECISION, DB_DEFAULT_SCALE);

  return (ret == NO_ERROR && (ret = er_errid ()) == NO_ERROR) ? ER_FAILED : ret;
}

/*
 * 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_is_positive () -
 *   return: 1 (>= 0), 0 (< 0), error code (error)
 *   dbvalue(in): ptr to a DB_VALUE of type DB_TYPE_NUMERIC
 */
int
numeric_db_value_is_positive (const DB_VALUE * dbvalue)
{
  int ret;

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

  ret = numeric_is_negative ((DB_C_NUMERIC) db_locate_numeric (dbvalue));

  return !ret;
}

/*
 * 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 (for dbv*) or
 *                  DB_TYPE_INTEGER (for answer);
 *   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 (const DB_VALUE * dbv1, const DB_VALUE * dbv2, DB_VALUE * answer)
{
  int ret = NO_ERROR;
  int prec1 = 0, prec2 = 0, scale1 = 0, scale2 = 0;
  int prec_common = 0, scale_common = 0;
  int cmp_rez = 0;

  /* 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;
    }

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

  if (prec1 == prec2 && scale1 == scale2)
    {
      /* Simple case. Just compare two numbers. */
      cmp_rez = numeric_compare (db_locate_numeric (dbv1), db_locate_numeric (dbv2));
      db_make_int (answer, cmp_rez);
      return NO_ERROR;
    }
  else
    {
      DB_VALUE dbv1_common, dbv2_common;

      /* First try to coerce to common prec/scale numbers and compare. */
      ret = numeric_common_prec_scale (dbv1, dbv2, &dbv1_common, &dbv2_common);
      if (ret == NO_ERROR)
    {
      cmp_rez = numeric_compare (db_locate_numeric (&dbv1_common), db_locate_numeric (&dbv2_common));
      db_make_int (answer, cmp_rez);
      return NO_ERROR;
    }
      else if (ret == ER_IT_DATA_OVERFLOW)
    {
      /* For example, if we want to compare a NUMERIC(31,2) with a NUMERIC(21, 14) the common precision and scale
       * is (43, 14) which is an overflow. To avoid this issue we compare the integral parts and the fractional
       * parts of dbv1 and dbv2 separately. */
      unsigned char num1_integ[DB_NUMERIC_BUF_SIZE];
      unsigned char num2_integ[DB_NUMERIC_BUF_SIZE];
      unsigned char num1_frac[DB_NUMERIC_BUF_SIZE];
      unsigned char num2_frac[DB_NUMERIC_BUF_SIZE];

      er_clear ();      /* reset ER_IT_DATA_OVERFLOW */

      if (prec1 - scale1 < prec2 - scale2)
        {
          prec_common = prec2 - scale2;
        }
      else
        {
          prec_common = prec1 - scale1;
        }

      if (scale1 > scale2)
        {
          scale_common = scale1;
        }
      else
        {
          scale_common = scale2;
        }

      /* first compare integral parts */
      numeric_get_integral_part (db_locate_numeric (dbv1), prec1, scale1, prec_common, num1_integ);
      numeric_get_integral_part (db_locate_numeric (dbv2), prec2, scale2, prec_common, num2_integ);
      cmp_rez = numeric_compare (num1_integ, num2_integ);
      if (cmp_rez != 0)
        {
          /* if the integral parts differ, we don't need to compare fractional parts */
          db_make_int (answer, cmp_rez);
          return NO_ERROR;
        }

      /* the integral parts are equal, now compare fractional parts */
      numeric_get_fractional_part (db_locate_numeric (dbv1), scale1, scale_common, num1_frac);
      numeric_get_fractional_part (db_locate_numeric (dbv2), scale2, scale_common, num2_frac);

      /* compare fractional parts and return the result */
      cmp_rez = numeric_compare (num1_frac, num2_frac);
      db_make_int (answer, cmp_rez);
    }
      else
    {
      db_make_null (answer);
      return ER_FAILED;
    }
    }

  return NO_ERROR;
}

/*
 * 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_bigint_to_num () -
 *   return:
 *   arg(in)    : unsigned bigint value
 *   answer(out): DB_C_NUMERIC
 *
 * Note: This routine converts 64 bit integer into DB_C_NUMERIC format and
 * returns the result.
 */
void
numeric_coerce_bigint_to_num (DB_BIGINT 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 64 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);
  answer[DB_NUMERIC_BUF_SIZE - 5] = ((arg >> 32) & 0xff);
  answer[DB_NUMERIC_BUF_SIZE - 6] = ((arg >> 40) & 0xff);
  answer[DB_NUMERIC_BUF_SIZE - 7] = ((arg >> 48) & 0xff);
  answer[DB_NUMERIC_BUF_SIZE - 8] = ((arg >> 56) & 0xff);

  /* Pad extra bytes of answer accordingly */
  for (digit = DB_NUMERIC_BUF_SIZE - 9; 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 */
  *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_num_to_bigint () -
 *   return:
 *   arg(in)    : ptr to a DB_C_NUMERIC
 *   answer(out): ptr to an bigint
 *
 * Note: This routine converts a numeric into an bigint returns the result.
 * If arg overflows answer, answer is set to +/- MAXINT.
 */
int
numeric_coerce_num_to_bigint (DB_C_NUMERIC arg, int scale, DB_BIGINT * answer)
{
  DB_NUMERIC zero_scale_numeric, numeric_rem, numeric_tmp;

  zero_scale_numeric.d.buf[0] = '\0';
  numeric_rem.d.buf[0] = '\0';
  numeric_tmp.d.buf[0] = '\0';

  DB_C_NUMERIC zero_scale_arg = zero_scale_numeric.d.buf;
  DB_C_NUMERIC rem = numeric_rem.d.buf;
  DB_C_NUMERIC tmp = numeric_tmp.d.buf;
  unsigned int i;
  char *ptr;

  if (scale >= (int) (sizeof (powers_of_10) / sizeof (powers_of_10[0])))
    {
      return ER_IT_DATA_OVERFLOW;
    }

  if (scale > 0)
    {
      numeric_div (arg, numeric_get_pow_of_10 (scale), zero_scale_arg, rem);
      if (!numeric_is_zero (rem))
    {
      /* The signs of the input, quotient(except for zero), and remainder will be the same. 
         Here, we force 'rem' to be negative */
      if (!numeric_is_negative (rem))
        {
          numeric_negate (rem);
        }

      /* round */
      /* If (10^'scale' + 'rem' + 'rem') <= 0 (where 'rem' is a negative remainder), round up; otherwise, disregard. 
       * If adding the negative remainder twice to a power of 10 results in a negative value, the remainder is considered large enough to round up.
       * Otherwise, it is disregarded. 
       * Note: Since the remainder is expressed as a negative value, addition is used instead of subtraction.
       */
      numeric_add (numeric_get_pow_of_10 (scale), rem, tmp, DB_NUMERIC_BUF_SIZE);
      numeric_add (tmp, rem, tmp, DB_NUMERIC_BUF_SIZE);
      if (numeric_is_negative (tmp) || numeric_is_zero (tmp))
        {
          if (numeric_is_negative (arg))
        {
          numeric_decrease (zero_scale_arg);
        }
          else
        {
          numeric_increase (zero_scale_arg);
        }
        }
    }
    }
  else
    {
      zero_scale_arg = arg;
    }

  if (!numeric_is_bigint (zero_scale_arg))
    {
      return ER_IT_DATA_OVERFLOW;
    }

  /* Copy the lower 64 bits into answer */
  ptr = (char *) answer;
  for (i = 0; i < sizeof (DB_BIGINT); i++)
    {
#if OR_BYTE_ORDER == OR_LITTLE_ENDIAN
      ptr[i] = zero_scale_arg[DB_NUMERIC_BUF_SIZE - (i + 1)];
#else
      ptr[sizeof (DB_BIGINT) - (i + 1)] = zero_scale_arg[DB_NUMERIC_BUF_SIZE - (i + 1)];
#endif
    }

  return NO_ERROR;
}

/*
 * 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_init_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++)
    {
      if (result.digits[i] == -1)
    {
      result.digits[i] = 0;
    }
      assert (result.digits[i] >= 0);

      *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];  /* 2: Sign, Null terminate */

  /* Convert the numeric to a decimal string */
  numeric_coerce_num_to_dec_str (num, num_string);

  /* Convert the decimal string into a double */
  /* Problem at precision with line below */
  /* 123.445 was converted to 123.444999999999999999 */
  *adouble = atof (num_string) / pow (10.0, scale);

  /* TODO: [CUBRIDSUS-2637] revert to early code for now. adouble = atof (num_string); for (i = 0; i < scale; i++)
   * adouble /= 10; */
}

/*
 * 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_get_integral_part  () - return the integral part of a numeric
 *   return: NO_ERROR, or ER_code
 *   num(in)       : the numeric from which to get the integral part
 *   src_prec(in)  : the precision of num
 *   src_scale(in) : the scale of num
 *   dst_prec(in)  : the desired precision of the result
 *   dest(out)     : the result
 *
 * Note: This function returns a NUMERIC value of precision dst_prec and
 *   0 scale representing the integral part of the num number.
 */
static void
numeric_get_integral_part (const DB_C_NUMERIC num, const int src_prec, const int src_scale, const int dst_prec,
               DB_C_NUMERIC dest)
{
  char dec_str[DB_MAX_NUMERIC_PRECISION * 4];
  char new_dec_num[DB_MAX_NUMERIC_PRECISION + 1];
  int i = 0;

  /* the number of digits of the result */
  const int res_num_digits = src_prec - src_scale;

  assert (src_prec - src_scale <= dst_prec);
  assert (num != dest);

  numeric_zero (dest, DB_NUMERIC_BUF_SIZE);
  memset (new_dec_num, 0, DB_MAX_NUMERIC_PRECISION + 1);

  /* 1. get the dec representation of the numeric value */
  numeric_coerce_num_to_dec_str (num, dec_str);

  /* 2. "zero" the MSB of new_dec_num. */
  for (i = 0; i < dst_prec - res_num_digits; i++)
    {
      new_dec_num[i] = '0';
    }

  /* 3. copy the integral digits from dec_str to the end of the new_dec_num */
  for (i = 0; i < res_num_digits; i++)
    {
      const int idx_new_dec = dst_prec - res_num_digits + i;
      const int idx_dec_str = strlen (dec_str) - src_prec + i;
      new_dec_num[idx_new_dec] = dec_str[idx_dec_str];
    }

  numeric_coerce_dec_str_to_num (new_dec_num, dest);
  if (numeric_is_negative (num))
    {
      numeric_negate (dest);
    }
}

/*
 * numeric_get_fractional_part  () - return the fractional part of a numeric
 *   return: NO_ERROR, or ER_code
 *   num(in)       : the numeric from which to get the fractional part
 *   src_prec(in)  : the precision of num
 *   src_scale(in) : the scale of num
 *   dst_scale(in) : the desired scale of the result
 *   dest(out)     : the result
 *
 * Note:  This function returns a numeric with precision dst_scale and scale 0
 *    which contains the fractional part of a numeric
 */
static void
numeric_get_fractional_part (const DB_C_NUMERIC num, const int src_scale, const int dst_scale, DB_C_NUMERIC dest)
{
  char dec_str[DB_MAX_NUMERIC_PRECISION * 4];
  char new_dec_num[DB_MAX_NUMERIC_PRECISION + 1];
  int i = 0;

  assert (src_scale <= dst_scale);
  assert (num != dest);

  numeric_zero (dest, DB_NUMERIC_BUF_SIZE);
  memset (new_dec_num, 0, DB_MAX_NUMERIC_PRECISION + 1);

  /* 1. get the dec representation of the numeric value */
  numeric_coerce_num_to_dec_str (num, dec_str);

  /* 2. copy all scale digits to the beginning of the new_dec_num buffer */
  for (i = 0; i < src_scale; i++)
    {
      new_dec_num[i] = dec_str[strlen (dec_str) - src_scale + i];
    }

  /* 3. add 0's for the reminder of the dst_scale */
  for (i = src_scale; i < dst_scale; i++)
    {
      new_dec_num[i] = '0';
    }

  /* 4. null-terminate the string */
  new_dec_num[dst_scale] = '\0';

  numeric_coerce_dec_str_to_num (new_dec_num, dest);
  if (numeric_is_negative (num))
    {
      numeric_negate (dest);
    }
}

/*
 * numeric_is_fraction_part_zero () - check if fractional part of a numeric is
 *                    equal to 0
 * return : boolean
 * num (in)   : numeric value
 * scale (in) : scale of the numeric
 */
static bool
numeric_is_fraction_part_zero (const DB_C_NUMERIC num, const int scale)
{
  int i, len = 0;
  char dec_str[(2 * DB_MAX_NUMERIC_PRECISION) + 4];
  numeric_coerce_num_to_dec_str (num, dec_str);
  len = strlen (dec_str);
  for (i = 0; i < scale; i++)
    {
      if (dec_str[len - scale + i] != '0')
    {
      return false;
    }
    }
  return true;
}

/*
 * 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)
{
  return numeric_internal_real_to_num (adouble, dst_scale, num, prec, scale, false);
}


/*
 * numeric_internal_float_to_num () - converts a float to a DB_C_NUMERIC
 *
 * return: NO_ERROR or ER_code
 * afloat(in): floating-point value to be converted to NUMERIC
 * dst_scale(in): expected scale for the destination NUMERIC type
 * num(in): an allocated DB_C_NUMERIC to be filled with the converted numeric
 *      value
 * prec(out): resulting precision of the converted value
 * scale(out): resulting scale of the converted value
 */
int
numeric_internal_float_to_num (float afloat, int dst_scale, DB_C_NUMERIC num, int *prec, int *scale)
{
  return numeric_internal_real_to_num (afloat, dst_scale, num, prec, scale, true);
}

/*
 * fp_value_type() - returns the type of a given value of type double, as one
 *           of the above enumerators.
 *
 * returns: the type of the passed-in floating-point value
 * d(in):   floating-point value whose type is to be returned
 */
FP_VALUE_TYPE
get_fp_value_type (double d)
{
#ifdef WINDOWS
  /* actually the following symbols are dependent on the _MSC macro, not the WINDOWS macro */
  switch (_fpclass (d))
    {
    case _FPCLASS_NINF: /* -Inf */
    case _FPCLASS_PINF: /* +Inf */
      return FP_VALUE_TYPE_INFINITE;

    case _FPCLASS_SNAN: /* signaling NaN */
    case _FPCLASS_QNAN: /* quiet NaN */
      return FP_VALUE_TYPE_NAN;

    case _FPCLASS_NZ:       /* -0 */
    case _FPCLASS_PZ:       /* +0 */
      return FP_VALUE_TYPE_ZERO;

    default:
      return FP_VALUE_TYPE_NUMBER;
    }
#else
  switch (std::fpclassify (d))
    {
    case FP_INFINITE:
      return FP_VALUE_TYPE_INFINITE;
    case FP_NAN:
      return FP_VALUE_TYPE_NAN;
    case FP_ZERO:
      return FP_VALUE_TYPE_ZERO;
    default:
      return FP_VALUE_TYPE_NUMBER;
    }
#endif
}

/*
 * numeric_internal_real_to_num() - converts a floating point value (float or
 *                  double) to a DB_C_NUMERIC.
 *
 * return: NO_ERROR or ER_code
 * adouble(in): floating-point value to be converted to NUMERIC. May be either
 *      float promoted to double, or a double.
 * dst_scale(in):   expected scale of the destination NUMERIC data type
 * prec(out):       resulting precision of the converted value
 * scale(out):      resulting scale of the converted value
 * is_float(in):    indicates adouble is a float promoted to double
 */
int
numeric_internal_real_to_num (double adouble, int dst_scale, DB_C_NUMERIC num, int *prec, int *scale, bool is_float)
{
  char numeric_str[MAX (TP_DOUBLE_AS_CHAR_LENGTH + 1, DB_MAX_NUMERIC_PRECISION + 4)];
  int i = 0;

  switch (get_fp_value_type (adouble))
    {
    case FP_VALUE_TYPE_INFINITE:
      return ER_IT_DATA_OVERFLOW;
    case FP_VALUE_TYPE_NAN:
    case FP_VALUE_TYPE_ZERO:
      /* currently CUBRID returns 0 for a NaN converted to NUMERIC (??) */
      *scale = dst_scale;
      *prec = dst_scale ? dst_scale : 1;

      while (i < *prec)
    {
      numeric_str[i++] = '0';
    }
      numeric_str[i] = '\0';

      numeric_coerce_dec_str_to_num (numeric_str, num);
      return NO_ERROR;
    default:
      /* compare against pow(10, DB_MAX_NUMERIC_PRECISION) to check for overflow/underflow before actual conversion */
      if (NUMERIC_ABS (adouble) > DB_NUMERIC_OVERFLOW_LIMIT)
    {
      return ER_IT_DATA_OVERFLOW;
    }
      else
    {
      if (NUMERIC_ABS (adouble) < DB_NUMERIC_UNDERFLOW_LIMIT)
        {
          /* the floating-point number underflows any possible CUBRID NUMERIC domain type, so just return 0 with no
           * other conversion */
          *scale = dst_scale;
          *prec = dst_scale ? dst_scale : 1;

          while (i < *prec)
        {
          numeric_str[i++] = '0';
        }
          numeric_str[i] = '\0';

          numeric_coerce_dec_str_to_num ("0", num);
          return NO_ERROR;
        }
      else
        {
          /* adouble might fit into a CUBRID NUMERIC domain type with sufficient precision. Invoke _dtoa() to get
           * the sequence of digits and the decimal point position */
          int decpt, sign;
          char *rve;
          int ndigits;

          if (is_float)
        {
          _dtoa (adouble, 0, TP_FLOAT_MANTISA_DECIMAL_PRECISION, &decpt, &sign, &rve, numeric_str + 1, 0);

          numeric_str[TP_FLOAT_MANTISA_DECIMAL_PRECISION + 1] = '\0';
        }
          else
        {
          _dtoa (adouble, 0, TP_DOUBLE_MANTISA_DECIMAL_PRECISION, &decpt, &sign, &rve, numeric_str + 1, 0);

          numeric_str[TP_DOUBLE_MANTISA_DECIMAL_PRECISION + 1] = '\0';
        }

          /* shift the digits in the sequence to make room for and to reach the decimal point */
          ndigits = strlen (numeric_str + 1);

          if (decpt <= 0)
        {
          char *dst = MIN (numeric_str + 1 + ndigits - decpt,
                   numeric_str + sizeof numeric_str / sizeof numeric_str[0] - 1), *src = dst + decpt;

          *prec = MIN (DB_MAX_NUMERIC_PRECISION, -decpt + ndigits);
          *scale = *prec;

          /* actually rounding should also be performed if value gets truncated. */
          *dst = '\0';
          dst--;
          src--;

          /* shift all digits in the string */
          while (src >= numeric_str + 1)
            {
              *dst = *src;
              dst--;
              src--;
            }

          /* prepend 0s from right to left until the decimal point position is reached */
          while (dst > numeric_str)
            {
              *dst-- = '0';
            }
        }
          else
        {
          /* the numer is greater than 1, either insert the decimal point at the correct position in the digits
           * sequence, or append 0s to the digits from left to right until the decimal point is reached. */

          if (decpt > DB_MAX_NUMERIC_PRECISION)
            {
              /* should not happen since overflow has been checked for previously */
              return ER_IT_DATA_OVERFLOW;
            }
          else
            {
              if (decpt < ndigits)
            {
              *prec = ndigits;
              *scale = ndigits - decpt;
            }
              else
            {
              /* append 0s to the digits sequence until the decimal point is reached */

              char *dst = numeric_str + 1 + decpt, *src = numeric_str + 1 + ndigits;

              while (src != dst)
                {
                  *src++ = '0';
                }

              *src = '\0';

              *prec = decpt;
              *scale = 0;
            }
            }
        }

          /* append zeroes until dst_scale is reached */
          while (*prec < DB_MAX_NUMERIC_PRECISION && *scale < dst_scale)
        {
          numeric_str[1 + *prec] = '0';
          (*prec)++;
          (*scale)++;
        }

          numeric_str[1 + *prec] = '\0';

          /* The number without sign is now written in decimal in numeric_str */

          if (sign)
        {
          numeric_str[0] = '-';
          numeric_coerce_dec_str_to_num (numeric_str, num);
        }
          else
        {
          numeric_coerce_dec_str_to_num (numeric_str + 1, num);
        }

          return NO_ERROR;
        }
    }
      break;
    }
}

#if defined (ENABLE_UNUSED_FUNCTION)
/*
 * 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);
}
#endif /* ENABLE_UNUSED_FUNCTION */

/*
 * numeric_coerce_string_to_num () -
 *   return:
 *   astring(in) : ptr to the input character string
 *   astring_length(in) : length of the input character string
 *   codeset(in) : codeset of string
 *   result(out) : DB_VALUE of type numeric
 *
 * Note: This routine converts a string into a DB_VALUE.
 *   It is not localized in relation to fractional and digit
 *   grouping symbols.
 */
int
numeric_coerce_string_to_num (const char *astring, int astring_length, INTL_CODESET codeset, DB_VALUE * result)
{
  char num_string[TWICE_NUM_MAX_PREC + 1];
  unsigned char num[DB_NUMERIC_BUF_SIZE];
  int i;
  int prec = 0;
  int scale = 0;
  bool leading_zeroes = true;
  bool sign_found = false;
  bool negate_value = false;
  bool pad_character_zero = false;
  bool trailing_spaces = false;
  bool decimal_part = false;
  int ret = NO_ERROR;
  int skip_size = 1;
  TP_DOMAIN *domain;

  /* Remove the decimal point, track the prec & scale */
  prec = 0;
  scale = 0;
  for (i = 0; i < astring_length && ret == NO_ERROR; i += skip_size)
    {
      skip_size = 1;
      if (astring[i] == '.')
    {
      leading_zeroes = false;
      decimal_part = true;
      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 (intl_is_space (astring + i, NULL, codeset, &skip_size))
        {
          /* Just skip this.  OK to have leading spaces */
          ;
        }
      else
        {
          /* Stray Non-numeric compatible character */
          ret = DOMAIN_INCOMPATIBLE;
        }
    }
      else
    {
      /* Only space character should be allowed on trailer. If the first space character is shown after digits, we
       * consider it as the beginning of trailer. */
      if (trailing_spaces && !intl_is_space (astring + i, NULL, codeset, &skip_size))
        {
          ret = DOMAIN_INCOMPATIBLE;
        }
      else if (intl_is_space (astring + i, NULL, codeset, &skip_size))
        {
          if (!trailing_spaces)
        {
          trailing_spaces = true;
        }
          /* Decrease scale if decimal part exists. */
          scale -= skip_size;
          if (scale < 0)
        {
          scale = 0;
        }
        }
      else if (astring[i] == ',')
        {
          /* Accept ',' character on integer part. */
          if (decimal_part)
        {
          ret = DOMAIN_INCOMPATIBLE;
        }
        }
      else if (astring[i] >= '0' && astring[i] <= '9')
        {
          num_string[prec] = astring[i];
          if (++prec > DB_MAX_NUMERIC_PRECISION)
        {
          break;
        }
        }
      else
        {
          /* Characters excluding digit, space and comma are not acceptable. */
          ret = DOMAIN_INCOMPATIBLE;
        }
    }
    }

  if (ret != NO_ERROR)
    {
      goto exit_on_error;
    }

  /* If there is no overflow, try to parse the decimal string */
  if (prec > DB_MAX_NUMERIC_PRECISION)
    {
      domain = tp_domain_resolve_default (DB_TYPE_NUMERIC);
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IT_DATA_OVERFLOW, 1, pr_type_name (TP_DOMAIN_TYPE (domain)));
      ret = ER_IT_DATA_OVERFLOW;
      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);

  return ret;

exit_on_error:

  db_value_domain_init (result, DB_TYPE_NUMERIC, DB_DEFAULT_NUMERIC_PRECISION, DB_DEFAULT_NUMERIC_SCALE);

  return (ret == NO_ERROR && (ret = er_errid ()) == NO_ERROR) ? ER_FAILED : ret;
}

/*
 * 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;

  if (src_num == NULL)
    {
      return ER_FAILED;
    }

  /* 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_IT_DATA_OVERFLOW;
      goto exit_on_error;
    }
    }

  /* only when all number are 9, round up will led overflow. */
  if (round_up)
    {
      bool is_all_nine = true;
      for (len = strlen (num_string), i = len - dest_prec; i < len; i++)
    {
      if (num_string[i] != '9')
        {
          is_all_nine = false;
          break;
        }
    }
      if (is_all_nine)
    {
      ret = ER_IT_DATA_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);
    }

  return ret;

exit_on_error:

  return (ret == NO_ERROR && (ret = er_errid ()) == NO_ERROR) ? ER_FAILED : ret;
}

/*
 * get_significant_digit () -
 *   return: significant digit of integer value
 *   i(in) :
 */
static int
get_significant_digit (DB_BIGINT i)
{
  int n = 0;

  do
    {
      n++;
      i /= 10;
    }
  while (i != 0);

  return n;
}

/*
 * 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:
      {
    float adouble = (float) db_get_float (src);
    ret = numeric_internal_float_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 = get_significant_digit (anint);
    scale = 0;
    break;
      }

    case DB_TYPE_SMALLINT:
      {
    int anint = (int) db_get_short (src);

    numeric_coerce_int_to_num (anint, num);
    precision = get_significant_digit (anint);
    scale = 0;
    break;
      }

    case DB_TYPE_BIGINT:
      {
    DB_BIGINT bigint = db_get_bigint (src);

    numeric_coerce_bigint_to_num (bigint, num);
    precision = get_significant_digit (bigint);
    desired_precision = MAX (desired_precision, precision);
    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;
      }

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

    default:
      ret = ER_FAILED;
      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);
    }

  if (ret == ER_IT_DATA_OVERFLOW)
    {
      *data_status = DATA_STATUS_TRUNCATED;
    }

  return ret;

exit_on_error:

  if (ret == ER_IT_DATA_OVERFLOW)
    {
      *data_status = DATA_STATUS_TRUNCATED;
    }

  return (ret == NO_ERROR && (ret = er_errid ()) == NO_ERROR) ? ER_FAILED : ret;
}

/*
 * 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_IT_DATA_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_IT_DATA_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 (dest, DB_CURRENCY_DEFAULT, 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_IT_DATA_OVERFLOW;
        goto exit_on_error;
      }
    db_make_int (dest, (int) ROUND (adouble));
    break;
      }

    case DB_TYPE_BIGINT:
      {
    DB_BIGINT bint;

    ret = numeric_coerce_num_to_bigint (db_locate_numeric (src), DB_VALUE_SCALE (src), &bint);
    if (ret != NO_ERROR)
      {
        goto exit_on_error;
      }

    db_make_bigint (dest, bint);
    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_IT_DATA_OVERFLOW;
        goto exit_on_error;
      }
    db_make_short (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:
    case DB_TYPE_VARCHAR:
      {
    char *return_string = NULL;
    char str_buf[NUMERIC_MAX_STRING_SIZE];
    int size = 0;
    DB_TYPE type;

    numeric_db_value_print (src, str_buf);
    size = strlen (str_buf);
    return_string = (char *) db_private_alloc (NULL, size + 1);
    if (return_string == NULL)
      {
        assert (er_errid () != NO_ERROR);
        return er_errid ();
      }

    strcpy (return_string, str_buf);
    type = DB_VALUE_DOMAIN_TYPE (dest);
    if (type == DB_TYPE_CHAR)
      {
        db_make_char (dest, size, return_string, size, LANG_SYS_CODESET, LANG_SYS_COLLATION);
      }
    else if (type == DB_TYPE_VARCHAR)
      {
        db_make_varchar (dest, size, return_string, size, LANG_SYS_CODESET, LANG_SYS_COLLATION);
      }
    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;
      }

    case DB_TYPE_DATETIME:
      {
    DB_BIGINT bi, tmp_bi;
    DB_DATETIME v_datetime;

    ret = numeric_coerce_num_to_bigint (db_locate_numeric (src), DB_VALUE_SCALE (src), &bi);
    if (ret == NO_ERROR)
      {
        /* make datetime value from interval value */
        tmp_bi = (DB_BIGINT) (bi / MILLISECONDS_OF_ONE_DAY);
        if (OR_CHECK_INT_OVERFLOW (tmp_bi))
          {
        ret = ER_IT_DATA_OVERFLOW;
          }
        else
          {
        v_datetime.date = (int) tmp_bi;
        v_datetime.time = (int) (bi % MILLISECONDS_OF_ONE_DAY);
        db_make_datetime (dest, &v_datetime);
          }
      }
    break;
      }

    default:
      ret = DOMAIN_INCOMPATIBLE;
      break;
    }

  return ret;

exit_on_error:

  return (ret == NO_ERROR && (ret = er_errid ()) == NO_ERROR) ? ER_FAILED : ret;
}

/*
 * numeric_db_value_coerce_from_num_strict () - coerce a numeric to the type
 *                      of dest
 * return : error code or NO_ERROR
 * src (in) : the numeric value
 * dest(in/out) : the value to coerce to
 */
int
numeric_db_value_coerce_from_num_strict (DB_VALUE * src, DB_VALUE * dest)
{
  int ret = NO_ERROR;

  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))
      {
        return ER_FAILED;
      }
    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))
      {
        return ER_FAILED;
      }
    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);
    if (OR_CHECK_FLOAT_OVERFLOW (adouble))
      {
        return ER_FAILED;
      }
    db_make_monetary (dest, DB_CURRENCY_DEFAULT, 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))
      {
        return ER_FAILED;
      }
    if (!numeric_is_fraction_part_zero (db_locate_numeric (src), DB_VALUE_SCALE (src)))
      {
        return ER_FAILED;
      }
    db_make_int (dest, (int) (adouble));
    break;
      }

    case DB_TYPE_BIGINT:
      {
    DB_BIGINT bint;

    ret = numeric_coerce_num_to_bigint (db_locate_numeric (src), DB_VALUE_SCALE (src), &bint);
    if (ret != NO_ERROR)
      {
        return ER_FAILED;
      }

    if (!numeric_is_fraction_part_zero (db_locate_numeric (src), DB_VALUE_SCALE (src)))
      {
        return ER_FAILED;
      }
    db_make_bigint (dest, bint);
    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))
      {
        return ER_FAILED;
      }
    if (!numeric_is_fraction_part_zero (db_locate_numeric (src), DB_VALUE_SCALE (src)))
      {
        return ER_FAILED;
      }
    db_make_short (dest, (DB_C_SHORT) ROUND (adouble));
    break;
      }

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

    default:
      ret = ER_FAILED;
      break;
    }

  return ER_FAILED;
}

/*
 * 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 (const DB_VALUE * val, char *buf)
{
  char temp[80];
  int nbuf;
  int temp_size;
  int i;
  bool found_first_non_zero = false;
  int scale = db_value_scale (val);

  /* it should not be static because the parameter could be changed without broker restart */
  bool oracle_compat_number = prm_get_bool_value (PRM_ID_ORACLE_COMPAT_NUMBER_BEHAVIOR);

  assert (val != NULL && buf != NULL);

  if (DB_IS_NULL (val))
    {
      buf[0] = '\0';
      return buf;
    }

  /* 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 = (int) strnlen (temp, sizeof (temp));
  for (i = 0; i < temp_size; i++)
    {
      /* Add the negative sign */
      if (temp[i] == '-')
    {
      buf[nbuf++] = '-';
    }

      /* Add decimal point */
      if (i == temp_size - scale)
    {
      int k = temp_size - 1;

      if (oracle_compat_number)
        {
          /* remove trailing zero */
          while (k > i && temp[k] == '0')
        {
          k--;
        }

          temp_size = k + 1;
          if (temp[k] == '0')
        {
          continue;
        }
          else if (k >= i)
        {
          buf[nbuf++] = '.';
        }
        }
      else
        {
          buf[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];
    }
    }

  /* 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 (DB_IS_NULL (arg))     /* NULL values are not 0 */
    {
      return false;
    }
  else
    {
      return (numeric_is_zero (db_get_numeric (arg)));
    }
}

/*
 * numeric_db_value_increase () -
 *   return: NO_ERROR or Error status
 *   arg(in)    : DB_VALUE of type DB_NUMERIC
 *
 * Note: This routine increments a numeric value.
 *
 */
int
numeric_db_value_increase (DB_VALUE * arg)
{
  /* Check for bad inputs */
  if (DB_IS_NULL (arg) || DB_VALUE_TYPE (arg) != DB_TYPE_NUMERIC)
    {
      return ER_OBJ_INVALID_ARGUMENTS;
    }

  numeric_increase (db_get_numeric (arg));

  return NO_ERROR;
}