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

class TestDefineConversions < Minitest::Test


  def setup
    initialize_context
  end

  def test_convert_to
    d = DecNum('1.1')
    x = d.convert_to(Rational)
    assert x.is_a?(Rational)
    assert_equal d.to_r, x

    d = DecNum('11')
    x = d.convert_to(Integer)
    assert x.is_a?(Integer)
    assert_equal d.to_i, x

    d = DecNum('11')
    x = d.convert_to(Float)
    assert x.is_a?(Float)
    assert_equal d.to_f, x
  end


  def test_big_decimal_conversions

    DecNum.local_context do

      DecNum.context.define_conversion_from(BigDecimal) do |x, context|
        DecNum(x.to_s) # or use x.split etc.
      end
      assert DecNum('0') == BigDecimal('0')
      assert_equal BigDecimal('0'), DecNum('0')
      assert_equal BigDecimal('1.2345'), DecNum('1.2345')
      assert_equal BigDecimal('-1.2345'), DecNum('-1.2345')
      assert_equal BigDecimal('1.2345'), DecNum('0.0012345000E3')
      assert_equal DecNum('7.1'), BigDecimal('7')+DecNum('0.1')
      assert_equal DecNum('7.1'), DecNum('7')+BigDecimal('0.1')
      assert_equal DecNum('1.1'), DecNum(BigDecimal('1.1'))
      assert DecNum(BigDecimal('1.1')).is_a?(DecNum)

      DecNum.context.define_conversion_to(BigDecimal) do |x|
        BigDecimal(x.to_s) # TODO: use x.split and handle special values
      end

      ['0.1', '-0.1', '0.0', '1234567.1234567', '-1234567.1234567', '1.234E7', '1.234E-7'].each do |n|
        f = BigDecimal(n)
        d = DecNum(n)
        c = d.convert_to(BigDecimal)
        assert c.is_a?(BigDecimal)
        assert_equal f, c
      end
    end

    assert_raises(TypeError, RuntimeError) { DecNum('0') == BigDecimal('0') }
    unless Num < Numeric
      # BigDecimal#eql? is weird
      refute_equal BigDecimal('0'), DecNum('0')
      refute_equal BigDecimal('1.2345'), DecNum('1.2345')
      refute_equal BigDecimal('-1.2345'), DecNum('-1.2345')
      refute_equal BigDecimal('1.2345'), DecNum('0.0012345000E3')
      assert_raises(TypeError) { BigDecimal('7')+DecNum('0.1') }
    end
    assert_raises(TypeError, RuntimeError) { DecNum('7')+BigDecimal('0.1') }
    assert_raises(TypeError, RuntimeError) { DecNum(BigDecimal('1.1')) }

    ['0.1', '-0.1', '0.0', '1234567.1234567', '-1234567.1234567', '1.234E7', '1.234E-7'].each do |n|
      assert_raises(TypeError, RuntimeError) { DecNum(n).convert_to(BigDecimal) }
    end

  end

  def test_float_conversions

    # Exact Float to DecNum conversion limited to context precision
    # => DecNum('0.1') != DecNum(0.1) unless precision is low enough
    DecNum.context.define_conversion_from(Float) do |x, context|
      s,e = Math.frexp(x)
      significand = Math.ldexp(s, Float::MANT_DIG).to_i
      exponent = e - Float::MANT_DIG
      # the number is (as a Rational) significand * exponent**Float::RADIX
      DecNum(significand*(Float::RADIX**exponent ))
    end

    assert_equal 0.0, DecNum('0')
    assert_equal DecNum('0'), 0.0
    assert_equal 1234.5, DecNum('1234.5')
    assert_equal DecNum('1234.5'), 1234.5
    assert_equal(-1234.5, DecNum('-1234.5'))
    assert_equal 1234.5, DecNum('0.0012345000E6')
    assert_equal DecNum('7.1'), 7.0+DecNum('0.1')
    DecNum.local_context(:precision=>12) do
      assert_equal DecNum('7.1'), DecNum('7')+0.1
    end
    assert_equal DecNum('11'), DecNum(11.0)
    assert_instance_of DecNum, DecNum(11.0)

    DecNum.context.define_conversion_from(Float) do |x, context|
      DecNum.context(context, :exact=>true) do
        s,e = Math.frexp(x)
        s = Math.ldexp(s, Float::MANT_DIG).to_i
        e -= Float::MANT_DIG
        DecNum(s*(Float::RADIX**e))
      end
    end

    assert_equal '0.1000000000000000055511151231257827021181583404541015625', DecNum(0.1).to_s
    assert_equal '1.100000000000000088817841970012523233890533447265625', DecNum(1.1).to_s

  end

  def test_conversion_types

    assert_instance_of DecNum, DecNum(1)+3
    assert_instance_of DecNum, 3+DecNum(1)
    assert_instance_of DecNum, Rational(1,5)+DecNum(1)
    assert_instance_of DecNum, DecNum(1)+Rational(1,5)
    assert_instance_of Float, DecNum(1)+3.0
    assert_instance_of Float, 3.0+DecNum(1)

    DecNum.context.define_conversion_from(Float) do |x, context|
      s,e = Math.frexp(x)
      significand = Math.ldexp(s, Float::MANT_DIG).to_i
      exponent = e - Float::MANT_DIG
      # the number is (as a Rational) significand * exponent**Float::RADIX
      DecNum(significand*(Float::RADIX**exponent ))
    end

    assert_instance_of DecNum, DecNum(1)+3.0
    assert_instance_of DecNum, 3.0+DecNum(1)

    DecNum.context.define_conversion_from(BigDecimal) do |x, context|
      DecNum(x.to_s) # or use x.split etc.
    end

    assert_instance_of DecNum, DecNum(1)+BigDecimal('3')
    assert_instance_of DecNum, BigDecimal('3')+DecNum(1)

  end

end