/* Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; version 2 of the License.

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

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */

#include "my_config.h"
#include <gtest/gtest.h>

#include "test_utils.h"

#include <my_decimal.h>

namespace my_decimal_unittest {

using my_testing::chars_2_decimal;
using my_testing::Server_initializer;
using my_testing::Mock_error_handler;

class DecimalTest : public ::testing::Test
{
protected:
  virtual void SetUp() { initializer.SetUp(); }
  virtual void TearDown() { initializer.TearDown(); }

  THD *thd() { return initializer.thd(); }

  Server_initializer initializer;

  my_decimal d1;
  my_decimal d2;
};


TEST_F(DecimalTest, CopyAndCompare)
{
  ulonglong val= 42;
  EXPECT_EQ(0, ulonglong2decimal(val, &d1));

  d2= d1;                                       // operator=()
  my_decimal d3(d1);                            // Copy constructor.

  EXPECT_EQ(0, my_decimal_cmp(&d1, &d2));
  EXPECT_EQ(0, my_decimal_cmp(&d2, &d3));
  EXPECT_EQ(0, my_decimal_cmp(&d3, &d1));

  ulonglong val1, val2, val3;
  EXPECT_EQ(0, decimal2ulonglong(&d1, &val1));
  EXPECT_EQ(0, decimal2ulonglong(&d2, &val2));
  EXPECT_EQ(0, decimal2ulonglong(&d3, &val3));
  EXPECT_EQ(val, val1);
  EXPECT_EQ(val, val2);
  EXPECT_EQ(val, val3);

  // The CTOR/operator=() generated by the compiler would fail here:
  val= 45;
  EXPECT_EQ(0, ulonglong2decimal(val, &d1));
  EXPECT_EQ(1, my_decimal_cmp(&d1, &d2));
  EXPECT_EQ(1, my_decimal_cmp(&d1, &d3));
}


TEST_F(DecimalTest, RoundOverflow)
{
  const char arg_str[]= "999999999";
  String str(arg_str, &my_charset_bin);

  EXPECT_EQ(E_DEC_OK,
            string2my_decimal(E_DEC_FATAL_ERROR, &str, &d1));
  d1.sanity_check();

  for (int ix= 0; ix < DECIMAL_MAX_POSSIBLE_PRECISION; ++ix)
  {
    my_decimal d3;
    const int expect=
      (ix + str.length() <= DECIMAL_MAX_POSSIBLE_PRECISION) ?
      E_DEC_OK : E_DEC_TRUNCATED;
    const bool do_truncate= true;
    EXPECT_EQ(expect,
              my_decimal_round(E_DEC_FATAL_ERROR, &d1, ix, do_truncate, &d3))
      << "ix:" << ix;
    d3.sanity_check();
    EXPECT_EQ(0, my_decimal_cmp(&d1, &d3));
  }
}


TEST_F(DecimalTest, Swap)
{
  ulonglong val1= 1;
  ulonglong val2= 2;
  EXPECT_EQ(0, ulonglong2decimal(val1, &d1));
  EXPECT_EQ(0, ulonglong2decimal(val2, &d2));
  my_decimal d1copy(d1);
  my_decimal d2copy(d2);
  EXPECT_EQ(0, my_decimal_cmp(&d1, &d1copy));
  EXPECT_EQ(0, my_decimal_cmp(&d2, &d2copy));
  d1.swap(d2);
  EXPECT_EQ(0, my_decimal_cmp(&d2, &d1copy));
  EXPECT_EQ(0, my_decimal_cmp(&d1, &d2copy));
}



TEST_F(DecimalTest, Multiply)
{
  const char arg1[]=
    "000000001."
    "10000000000000000000" "00000000000000000000" "00000000000000000000"
    "000000000000";
  const char arg2[]= "1.75";
  char buff[DECIMAL_MAX_STR_LENGTH];
  int bufsz;
  my_decimal prod;

  EXPECT_EQ(E_DEC_OK, chars_2_decimal(arg1, &d1));
  EXPECT_EQ(E_DEC_OK, chars_2_decimal(arg2, &d2));

  // Limit the precision, otherwise "1.75" will be truncated to "1."
  set_if_smaller(d1.frac, NOT_FIXED_DEC);
  set_if_smaller(d2.frac, NOT_FIXED_DEC);
  EXPECT_EQ(0, my_decimal_mul(E_DEC_FATAL_ERROR & ~E_DEC_OVERFLOW,
                              &prod, &d1, &d2));
  EXPECT_EQ(NOT_FIXED_DEC, d1.frac);
  EXPECT_EQ(2, d2.frac);
  EXPECT_EQ(NOT_FIXED_DEC, prod.frac);
  bufsz= sizeof(buff);
  EXPECT_EQ(0, decimal2string(&prod, buff, &bufsz, 0, 0, 0));
  EXPECT_STREQ("1.9250000000000000000000000000000", buff);
}


/*
  This is a simple iterative implementation based on addition and subtraction,
  for verifying the result of decimal_mod().

  decimal_mod() says:
  DESCRIPTION
    the modulus R in    R = M mod N
    is defined as

    0 <= |R| < |M|
    sign R == sign M
    R = M - k*N, where k is integer

    thus, there's no requirement for M or N to be integers
 */
int decimal_modulo(uint mask,
                   my_decimal *res,
                   const my_decimal *m,
                   const my_decimal *n)
{
  my_decimal abs_m(*m);
  my_decimal abs_n(*n);
  abs_m.sign(false);
  abs_n.sign(false);

  my_decimal r;
  my_decimal k1(abs_n);
  my_decimal kn(decimal_zero);
  my_decimal next_r(abs_m);
  int ret;
  do
  {
    r= next_r;

    my_decimal res;
    if ((ret= my_decimal_add(E_DEC_FATAL_ERROR, &res, &k1, &kn)) != E_DEC_OK)
    {
      ADD_FAILURE();
      return ret;
    }
    kn= res;

    if ((ret= my_decimal_sub(E_DEC_FATAL_ERROR,
                             &next_r, &abs_m, &kn) != E_DEC_OK))
    {
      ADD_FAILURE();
      return ret;
    }
  } while (my_decimal_cmp(&next_r, &decimal_zero) >= 0);
  r.sign(m->sign());
  *res= r;
  return 0;
}


struct Mod_data
{
  const char *a;
  const char *b;
  const char *result;
};

Mod_data mod_test_input[]=
{
  { "234"     , "10",      "4"      },
  { "234.567" , "10.555",  "2.357"  },
  { "-234.567", "10.555",  "-2.357" },
  { "234.567" , "-10.555", "2.357"  },
  { "-234.567", "-10.555", "-2.357" },
  { "999"     , "0.1",     "0.0"    },
  { "999"     , "0.7",     "0.1"    },
  { "10"      , "123",     "10"     },
  { NULL, NULL, NULL}
};


TEST_F(DecimalTest, Modulo)
{
  my_decimal expected_result;
  my_decimal xxx_result;
  my_decimal mod_result;
  char buff_x[DECIMAL_MAX_STR_LENGTH];
  char buff_m[DECIMAL_MAX_STR_LENGTH];

  for (Mod_data *pd= mod_test_input; pd->a; ++pd)
  {
    int bufsz_x= sizeof(buff_x);
    int bufsz_m= sizeof(buff_m);

    EXPECT_EQ(0, chars_2_decimal(pd->a, &d1));
    EXPECT_EQ(0, chars_2_decimal(pd->b, &d2));
    EXPECT_EQ(0, chars_2_decimal(pd->result, &expected_result));

    EXPECT_EQ(0, my_decimal_mod(E_DEC_FATAL_ERROR, &mod_result, &d1, &d2));
    EXPECT_EQ(0, decimal2string(&mod_result, buff_m, &bufsz_m, 0, 0, 0));
    EXPECT_EQ(0, my_decimal_cmp(&expected_result, &mod_result))
      << " a:" << pd->a
      << " b:" << pd->b
      << " expected:" << pd->result
      << " got mod:" << buff_m
      ;

    EXPECT_EQ(0, decimal_modulo(E_DEC_FATAL_ERROR, &xxx_result, &d1, &d2));
    EXPECT_EQ(0, decimal2string(&xxx_result, buff_x, &bufsz_x, 0, 0, 0));
    EXPECT_EQ(0, my_decimal_cmp(&expected_result, &xxx_result))
      << " a:" << pd->a
      << " b:" << pd->b
      << " expected:" << pd->result
      << " got mod:" << buff_m
      << " got xxx:" << buff_x
      ;
  }
}


TEST_F(DecimalTest, BinaryConversion)
{
  const int prec= 60;
  const int scale= 0;
  EXPECT_EQ(E_DEC_OK, chars_2_decimal("000000000", &d1));
  int binary_size= my_decimal_get_binary_size(prec, scale);
  uchar *bin= new uchar[binary_size];

  // Convert to binary, and back.
  EXPECT_EQ(E_DEC_OK, my_decimal2binary(E_DEC_FATAL_ERROR & ~E_DEC_OVERFLOW,
                                        &d1, bin, prec, scale));
  EXPECT_EQ(E_DEC_OK, binary2my_decimal(E_DEC_FATAL_ERROR & ~E_DEC_OVERFLOW,
                                        bin, &d2, prec, scale));
  EXPECT_GT(d2.precision(), 0U);
  EXPECT_EQ(0, my_decimal_cmp(&d1, &d2));

  // 0.0 * 0.0
  my_decimal product;
  EXPECT_EQ(E_DEC_OK, my_decimal_mul(E_DEC_FATAL_ERROR & ~E_DEC_OVERFLOW,
                                     &product, &d2, &d2));
  // 0.0 * (-0.0)
  my_decimal neg_prod;
  my_decimal d3(d2);
  d3.sign(true);
  EXPECT_EQ(E_DEC_OK, my_decimal_mul(E_DEC_FATAL_ERROR & ~E_DEC_OVERFLOW,
                                     &neg_prod, &d2, &d3));
  delete[] bin;
}

}
