require File.expand_path(File.join(File.dirname(__FILE__),'helper.rb'))

module TestFunctions

  module_function

  def exp(x,c=nil)

    i, lasts, s, fact, num = 0, 0, 1, 1, 1
    DecNum.local_context(c) do
      # result context
      DecNum.local_context do |context|
        # working context
        context.precision += 2
        context.rounding = DecNum::ROUND_HALF_EVEN
        while s != lasts
          lasts = s
          i += 1
          fact *= i
          num *= x
          s += num / fact
        end
      end
      +s
    end
  end

  def exp1(x, c=nil)
    return DecNum(BigDecimal("NaN")) if x.infinite? || x.nan?
    y = nil
    ext = 2
    DecNum.local_context(c) do |context|
      n = (context.precision += ext)

      one  = DecNum("1")
      x1 = one
      y  = one
      d  = y
      z  = one
      i  = 0
      while d.nonzero? && ((m = n - (y.fractional_exponent - d.fractional_exponent).abs) > 0)
        m = ext if m < ext
        x1  *= x
        i += 1
        z *= i

        #d  = x1.divide(z,:precision=>m)
        context.precision = m
        d = x1/z
        context.precision = n

        y += d
      end
    end
    return +y
  end
end

def return_from_local_context
  DecNum.context do |local_context|
    local_context.precision += 2
    return
  end
end

class TestBasic < Minitest::Test


  def setup
    initialize_context
  end


  def test_basic

    DecNum.context.precision = 4
    assert_equal 4, DecNum.context.precision
    return_from_local_context
    assert_equal 4, DecNum.context.precision
    assert_equal DecNum("0.3333"), DecNum(1)/DecNum(3)
    DecNum.context.precision = 10
    assert_equal 10, DecNum.context.precision
    assert_equal DecNum("0.3333333333"), DecNum(1)/DecNum(3)
    DecNum.local_context {
      assert_equal 10, DecNum.context.precision
      DecNum.context.precision = 3
      assert_equal 3, DecNum.context.precision
      assert_equal DecNum("0.333"), DecNum(1)/DecNum(3)
    }
    assert_equal 10, DecNum.context.precision
    assert_equal "0.3333333333", (DecNum(1)/DecNum(3)).to_s
    assert_equal DecNum("0.3333333333"), DecNum(1)/DecNum(3)

    DecNum.local_context(:precision=>4) {
      assert_equal 4, DecNum.context.precision
    }
    assert_equal 10, DecNum.context.precision

    DecNum.context(:precision=>4) {
      assert_equal 4, DecNum.context.precision
    }
    assert_equal 10, DecNum.context.precision

    DecNum.context(:extra_precision=>4) {
      assert_equal 14, DecNum.context.precision
    }
    assert_equal 10, DecNum.context.precision

    DecNum.local_context(DecNum::BasicContext) {
      assert_equal :half_up, DecNum.context.rounding
      assert_equal 9, DecNum.context.precision
    }
    assert_equal :half_even, DecNum.context.rounding
    assert_equal 10, DecNum.context.precision

    DecNum.context(DecNum::BasicContext) {
      assert_equal :half_up, DecNum.context.rounding
      assert_equal 9, DecNum.context.precision
    }
    assert_equal :half_even, DecNum.context.rounding
    assert_equal 10, DecNum.context.precision

    DecNum.local_context(DecNum::BasicContext, :precision=>4) {
      assert_equal :half_up, DecNum.context.rounding
      assert_equal 4, DecNum.context.precision
    }
    assert_equal :half_even, DecNum.context.rounding
    assert_equal 10, DecNum.context.precision

    DecNum.context(DecNum::BasicContext, :precision=>4) {
      assert_equal :half_up, DecNum.context.rounding
      assert_equal 4, DecNum.context.precision
    }
    assert_equal :half_even, DecNum.context.rounding
    assert_equal 10, DecNum.context.precision

    context_a = DecNum::BasicContext
    assert_equal :half_up, context_a.rounding
    assert_equal 9, context_a.precision
    context_b = context_a[precision: 11]
    assert_equal 9, context_a.precision
    assert_equal 11, context_b.precision
    assert_equal :half_up, context_a.rounding
    assert_equal :half_up, context_b.rounding

    assert_equal DecNum("0."+"3"*100), DecNum(1)./(DecNum(3),DecNum.Context(:precision=>100))
    assert_equal 10, DecNum.context.precision
    assert_equal DecNum("0.33"), DecNum(1).divide(DecNum(3),DecNum.Context(:precision=>2))
    assert_equal DecNum("0.3333333333"), DecNum(1)/DecNum(3)

    assert_equal DecNum("11.0"), DecNum(11).abs
    assert_equal DecNum("11.0"), DecNum(-11).abs
    assert_equal DecNum("-11.0"), -DecNum(11)

    assert_equal [-11,0], DecNum("-11").to_int_scale

    assert_equal [-110,-1], DecNum("-11.0").to_int_scale
    assert_equal [-11,-1], DecNum("-1.1").to_int_scale
    assert_equal [-110,0], DecNum("-110").to_int_scale
    assert_equal [110,-1], DecNum("11.0").to_int_scale
    assert_equal [11,-1], DecNum("1.1").to_int_scale
    assert_equal [110,0], DecNum("110").to_int_scale

    assert_equal [-11,0], DecNum("-11.0").reduce.to_int_scale
    assert_equal [-11,-1], DecNum("-1.1").reduce.to_int_scale
    assert_equal [-11,1], DecNum("-110").reduce.to_int_scale
    assert_equal [11,0], DecNum("11.0").reduce.to_int_scale
    assert_equal [11,-1], DecNum("1.1").reduce.to_int_scale
    assert_equal [11,1], DecNum("110").reduce.to_int_scale


    assert_equal DecNum('2.1'), DecNum('2.1').remainder(DecNum('3'))
    assert_equal DecNum('-2.1'), DecNum('-2.1').remainder(DecNum('3'))
    assert_equal DecNum('2.1'), DecNum('2.1').remainder(DecNum('-3'))
    assert_equal DecNum('-2.1'), DecNum('-2.1').remainder(DecNum('-3'))
    assert_equal DecNum('1'), DecNum('10').remainder(DecNum('3'))
    assert_equal DecNum('-1'), DecNum('-10').remainder(DecNum('3'))
    assert_equal DecNum('1'), DecNum('10').remainder(DecNum('-3'))
    assert_equal DecNum('-1'), DecNum('-10').remainder(DecNum('-3'))
    assert_equal DecNum('0.2'), DecNum('10.2').remainder(DecNum('1'))
    assert_equal DecNum('0.1'), DecNum('10').remainder(DecNum('0.3'))
    assert_equal DecNum('1.0'), DecNum('3.6').remainder(DecNum('1.3'))
    assert_equal DecNum('2'), DecNum('2').remainder(DecNum('3'))
    assert_equal DecNum('1'), DecNum('10').remainder(DecNum('3'))
    assert_equal DecNum('.1'), DecNum('1').remainder(DecNum('0.3'))

    assert_equal DecNum('0'), DecNum('2.1').divide_int(DecNum('3'))
    assert_equal DecNum('0'), DecNum('-2.1').divide_int(DecNum('3'))
    assert_equal DecNum('0'), DecNum('2.1').divide_int(DecNum('-3'))
    assert_equal DecNum('0'), DecNum('-2.1').divide_int(DecNum('-3'))
    assert_equal DecNum('3'), DecNum('10').divide_int(DecNum('3'))
    assert_equal DecNum('-3'), DecNum('-10').divide_int(DecNum('3'))
    assert_equal DecNum('-3'), DecNum('10').divide_int(DecNum('-3'))
    assert_equal DecNum('3'), DecNum('-10').divide_int(DecNum('-3'))
    assert_equal DecNum('10'), DecNum('10.2').divide_int(DecNum('1'))
    assert_equal DecNum('33'), DecNum('10').divide_int(DecNum('0.3'))
    assert_equal DecNum('2'), DecNum('3.6').divide_int(DecNum('1.3'))
    assert_equal DecNum('0'), DecNum('2').divide_int(DecNum('3'))
    assert_equal DecNum('3'), DecNum('10').divide_int(DecNum('3'))
    assert_equal DecNum('3'), DecNum('1').divide_int(DecNum('0.3'))

    assert_equal DecNum('2.1'), DecNum('2.1').modulo(DecNum('3'))
    assert_equal DecNum('0.9'), DecNum('-2.1').modulo(DecNum('3'))
    assert_equal DecNum('-0.9'), DecNum('2.1').modulo(DecNum('-3'))
    assert_equal DecNum('-2.1'), DecNum('-2.1').modulo(DecNum('-3'))
    assert_equal DecNum('1'), DecNum('10').modulo(DecNum('3'))
    assert_equal DecNum('2'), DecNum('-10').modulo(DecNum('3'))
    assert_equal DecNum('-2'), DecNum('10').modulo(DecNum('-3'))
    assert_equal DecNum('-1'), DecNum('-10').modulo(DecNum('-3'))
    assert_equal DecNum('0.2'), DecNum('10.2').modulo(DecNum('1'))
    assert_equal DecNum('0.1'), DecNum('10').modulo(DecNum('0.3'))
    assert_equal DecNum('1.0'), DecNum('3.6').modulo(DecNum('1.3'))
    assert_equal DecNum('2'), DecNum('2').modulo(DecNum('3'))
    assert_equal DecNum('1'), DecNum('10').modulo(DecNum('3'))
    assert_equal DecNum('.1'), DecNum('1').modulo(DecNum('0.3'))

    assert_equal DecNum('0'), DecNum('2.1').div(DecNum('3'))
    assert_equal DecNum('-1'), DecNum('-2.1').div(DecNum('3'))
    assert_equal DecNum('-1'), DecNum('2.1').div(DecNum('-3'))
    assert_equal DecNum('0'), DecNum('-2.1').div(DecNum('-3'))
    assert_equal DecNum('3'), DecNum('10').div(DecNum('3'))
    assert_equal DecNum('-4'), DecNum('-10').div(DecNum('3'))
    assert_equal DecNum('-4'), DecNum('10').div(DecNum('-3'))
    assert_equal DecNum('3'), DecNum('-10').div(DecNum('-3'))
    assert_equal DecNum('10'), DecNum('10.2').div(DecNum('1'))
    assert_equal DecNum('33'), DecNum('10').div(DecNum('0.3'))
    assert_equal DecNum('2'), DecNum('3.6').div(DecNum('1.3'))
    assert_equal DecNum('0'), DecNum('2').div(DecNum('3'))
    assert_equal DecNum('3'), DecNum('10').div(DecNum('3'))
    assert_equal DecNum('3'), DecNum('1').div(DecNum('0.3'))

    assert_equal DecNum('-0.9'), DecNum('2.1').remainder_near(DecNum('3'))
    assert_equal DecNum('-2'), DecNum('10').remainder_near(DecNum('6'))
    assert_equal DecNum('1'), DecNum('10').remainder_near(DecNum('3'))
    assert_equal DecNum('-1'), DecNum('-10').remainder_near(DecNum('3'))
    assert_equal DecNum('0.2'), DecNum('10.2').remainder_near(DecNum('1'))
    assert_equal DecNum('0.1'), DecNum('10').remainder_near(DecNum('0.3'))
    assert_equal DecNum('-0.3'), DecNum('3.6').remainder_near(DecNum('1.3'))


   assert_equal 2, DecNum('123.4567').adjusted_exponent
   assert_equal 2, DecNum('123.45670').adjusted_exponent
   assert_equal 2, DecNum.context.scaleb(DecNum('1.2345670'),2).adjusted_exponent
   assert_equal 2, DecNum.context.scaleb(DecNum('1.2345670'),DecNum('2')).adjusted_exponent
   assert_equal DecNum(2), DecNum.context.logb(DecNum('123.4567'))
   assert_equal DecNum(2), DecNum.context.logb(DecNum('123.45670'))
   assert_equal 3, DecNum('123.4567').fractional_exponent
   assert_equal 3, DecNum('123.45670').fractional_exponent

   assert_equal(-7, DecNum.context.normalized_integral_exponent(DecNum('123.4567')))
   assert_equal(1234567000, DecNum.context.normalized_integral_significand(DecNum('123.4567')))

   assert_equal 7, DecNum('123.4567').number_of_digits
   assert_equal 8, DecNum('123.45670').number_of_digits
   assert_equal 7, DecNum('123.45670').reduce.number_of_digits

   assert_equal 1234567, DecNum('123.4567').integral_significand
   assert_equal 12345670, DecNum('123.45670').integral_significand
   assert_equal 1234567, DecNum('123.45670').reduce.integral_significand

   assert_equal 2, DecNum('-123.4567').adjusted_exponent
   assert_equal 2, DecNum('-123.45670').adjusted_exponent
   assert_equal 2, DecNum.context.scaleb(DecNum('-1.2345670'),2).adjusted_exponent
   assert_equal 2, DecNum.context.scaleb(DecNum('-1.2345670'),DecNum('2')).adjusted_exponent
   assert_equal DecNum(2), DecNum.context.logb(DecNum('-123.4567'))
   assert_equal DecNum(2), DecNum.context.logb(DecNum('-123.45670'))
   assert_equal 3, DecNum('-123.4567').fractional_exponent
   assert_equal 3, DecNum('-123.45670').fractional_exponent

   assert_equal(-7, DecNum.context.normalized_integral_exponent(DecNum('-123.4567')))
   assert_equal(1234567000, DecNum.context.normalized_integral_significand(DecNum('-123.4567')))

   assert_equal 7, DecNum('-123.4567').number_of_digits
   # assert_equal 9, DecNum('123.45670').number_of_digits # not with BigDecimal
   assert_equal 7, DecNum('-123.45670').reduce.number_of_digits

   assert_equal(1234567, DecNum('-123.4567').integral_significand)
   #assert_equal(12345670, DecNum('-123.45670').integral_significand) # not with BigDecimal
   assert_equal(1234567, DecNum('-123.45670').reduce.integral_significand)

   x = DecNum('123.4567')
   assert_equal x, DecNum(x.integral_significand)*10**x.integral_exponent
   assert_equal x, DecNum(DecNum.context.normalized_integral_significand(x))*10**DecNum.context.normalized_integral_exponent(x)

   assert_equal x, DecNum.context.scaleb(x.integral_significand, x.integral_exponent)
   assert_equal x, DecNum.context.scaleb(DecNum.context.normalized_integral_significand(x),DecNum.context.normalized_integral_exponent(x))

   x = DecNum('-123.4567')
   assert_equal x.abs, DecNum(x.integral_significand)*10**x.integral_exponent
   assert_equal x.abs, DecNum(DecNum.context.normalized_integral_significand(x))*10**DecNum.context.normalized_integral_exponent(x)

   assert_equal x.abs, DecNum.context.scaleb(x.integral_significand, x.integral_exponent)
   assert_equal x.abs, DecNum.context.scaleb(DecNum.context.normalized_integral_significand(x),DecNum.context.normalized_integral_exponent(x))

   DecNum.context.precision = 3
   DecNum.context.rounding = :half_up
   assert_equal DecNum("100"), (+DecNum('100.4'))
   assert_equal DecNum("101"), (+DecNum('101.4'))
   assert_equal DecNum("101"), (+DecNum('100.5'))
   assert_equal DecNum("102"), (+DecNum('101.5'))
   assert_equal DecNum("-101"), (+DecNum('-100.5'))
   assert_equal DecNum("-102"), (+DecNum('-101.5'))
   assert_equal DecNum("-100"), (+DecNum('-100.4'))
   assert_equal DecNum("-101"), (+DecNum('-101.4'))
   DecNum.context.rounding = :half_even
   assert_equal DecNum("100"), (+DecNum('100.5'))
   assert_equal DecNum("101"), (+DecNum('100.51'))
   assert_equal DecNum("101"), (+DecNum('100.6'))
   assert_equal DecNum("102"), (+DecNum('101.5'))
   assert_equal DecNum("101"), (+DecNum('101.4'))
   assert_equal DecNum("-100"), (+DecNum('-100.5'))
   assert_equal DecNum("-102"), (+DecNum('-101.5'))
   assert_equal DecNum("-101"), (+DecNum('-101.4'))
   DecNum.context.rounding = :half_down
   assert_equal DecNum("100"), (+DecNum('100.5'))
   assert_equal DecNum("101"), (+DecNum('101.5'))
   assert_equal DecNum("-100"), (+DecNum('-100.5'))
   assert_equal DecNum("-101"), (+DecNum('-101.5'))
   DecNum.context.rounding = :down
   assert_equal DecNum("100"), (+DecNum('100.9'))
   assert_equal DecNum("101"), (+DecNum('101.9'))
   assert_equal DecNum("-100"), (+DecNum('-100.9'))
   assert_equal DecNum("-101"), (+DecNum('-101.9'))
   DecNum.context.rounding = :up
   assert_equal DecNum("101"), (+DecNum('100.1'))
   assert_equal DecNum("102"), (+DecNum('101.1'))
   assert_equal DecNum("-101"), (+DecNum('-100.1'))
   assert_equal DecNum("-102"), (+DecNum('-101.1'))
   DecNum.context.rounding = :floor
   assert_equal DecNum("100"), (+DecNum('100.9'))
   assert_equal DecNum("101"), (+DecNum('101.9'))
   assert_equal DecNum("-101"), (+DecNum('-100.9'))
   assert_equal DecNum("-102"), (+DecNum('-101.9'))
   DecNum.context.rounding = :ceiling
   assert_equal DecNum("101"), (+DecNum('100.1'))
   assert_equal DecNum("102"), (+DecNum('101.1'))
   assert_equal DecNum("-100"), (+DecNum('-100.1'))
   assert_equal DecNum("-101"), (+DecNum('-101.1'))

  end

  def test_exp
    DecNum.context.precision = 100
    DecNum.context.rounding = :half_even
    e_100 = "2.718281828459045235360287471352662497757247093699959574966967627724076630353547594571382178525166427"
    assert_equal e_100, TestFunctions.exp(DecNum(1)).to_s
    assert_equal DecNum(e_100), TestFunctions.exp(DecNum(1))
    assert_equal e_100, TestFunctions.exp1(DecNum(1)).to_s
    assert_equal DecNum(e_100), TestFunctions.exp1(DecNum(1))
  end

  def test_special
    nan = DecNum(0)/DecNum(0)
    inf_pos = DecNum(1)/DecNum(0)
    inf_neg = DecNum(-1)/DecNum(0)
    zero_pos = DecNum(0)
    zero_neg = DecNum('-0')
    pos = DecNum(1)
    neg = DecNum(-1)

    assert nan.nan?
    assert nan.special?
    assert !nan.zero?
    assert !nan.finite?
    assert !nan.infinite?
    assert inf_pos.infinite?
    assert !inf_pos.finite?
    assert !inf_pos.nan?
    assert inf_pos.special?
    assert inf_neg.infinite?
    assert !inf_neg.finite?
    assert !inf_neg.nan?
    assert inf_neg.special?
    assert zero_pos.finite?
    assert zero_neg.finite?
    assert zero_pos.zero?
    assert zero_neg.zero?
    assert !pos.zero?
    assert !neg.zero?
    assert pos.nonzero?
    assert neg.nonzero?

    #assert nan.sign.nil?
    assert_equal(+1, inf_pos.sign)
    assert_equal(-1, inf_neg.sign)
    assert_equal(+1, zero_pos.sign)
    assert_equal(-1, zero_neg.sign)
    assert_equal(+1, pos.sign)
    assert_equal(-1, neg.sign)
  end

  def test_special_constructors
    [DecNum, BinNum, Float].each do |num_class|
      context = num_class.context
      assert context.nan.nan?
      assert context.zero.zero?
      assert_equal(+1, context.sign(context.zero))
      assert context.zero(+1).zero?
      assert_equal(+1, context.sign(context.zero(+1)))
      assert context.zero(-1).zero?
      assert_equal(-1, context.sign(context.zero(-1)))
      assert context.infinity.infinite?
      assert_equal(+1, context.sign(context.infinity))
      assert context.infinity(+1).infinite?
      assert_equal(+1, context.sign(context.infinity(+1)))
      assert context.infinity(-1).infinite?
      assert_equal(-1, context.sign(context.infinity(-1)))
      assert_equal context.Num(1)/2, context.one_half
    end
  end

  def test_context_parameters
    #DecNum.context = DecNum.Context
    DecNum.context.precision = 3
    DecNum.context.rounding = :half_even
    x = DecNum(1)
    y = DecNum(3)
    assert_equal DecNum('0.333'), x.divide(y)
    assert_equal DecNum('0.3333'), x.divide(y, DecNum.Context(:precision=>4))
    assert_equal DecNum('0.333'), x.divide(y)
    assert_equal DecNum('0.33333'), x.divide(y, :precision=>5)
  end

  def test_integer

    %w{ 0 0E10 0E-10 12.0 12 120E-1 12.1E10 }.each do |x|
      assert DecNum(x).integral?, "DecNum('#{x}').integral?"
    end

    [12, Rational(6,2)].each do |x|
      assert DecNum(x).integral?, "DecNum(#{x}).integral?"
    end

    %w{ NaN Inf 12.1 121E-1 }.each do |x|
      assert !DecNum(x).integral?, "!DecNum('#{x}').integral?"
    end

    [ Rational(121,10) ].each do |x|
      assert !DecNum(x).integral?, "!DecNum(#{x}).integral?"
    end

  end

  def test_context_constructor
    DecNum.context.precision = 8
    DecNum.context.rounding = :half_even
    context = DecNum.context[precision: 4]
    assert_equal "1000", context.Num('0.1', :fixed).split[1].to_s
    assert_equal "1000", context.Num('0.1', :fixed, :base => 10).split[1].to_s
    assert_equal "10000000", DecNum('0.1', :fixed).split[1].to_s
  end

  def test_normalizetion
    DecNum.context.precision = 20
    DecNum.context.emin = -1000
    x = DecNum('5.10000000000E-502')
    assert_equal [1, 510000000000, -513], x.split
    context = DecNum.context(precision: 12, emin: -499)
    assert_equal [1, 510000000, -510], x.normalize(context).split
    assert_equal [1, 510000000, -510], context.normalize(x).split
    DecNum.context = context
    assert_equal [1, 510000000, -510], x.normalize(context).split
    assert_equal [1, 510000000, -510], context.normalize(x).split
    assert_equal [1, 510000000000, -513], x.split
  end

end
