require 'test/unit'

class TestBignum < Test::Unit::TestCase
  def setup
    @verbose = $VERBOSE
    $VERBOSE = nil
  end

  def teardown
    $VERBOSE = @verbose
  end

  def fact(n)
    return 1 if n == 0
    f = 1
    while n>0
      f *= n
      n -= 1
    end
    return f
  end

  def test_bignum
    $x = fact(40)
    assert_equal($x, $x)
    assert_equal($x, fact(40))
    assert($x < $x+2)
    assert($x > $x-2)
    assert_equal(815915283247897734345611269596115894272000000000, $x)
    assert_not_equal(815915283247897734345611269596115894272000000001, $x)
    assert_equal(815915283247897734345611269596115894272000000001, $x+1)
    assert_equal(335367096786357081410764800000, $x/fact(20))
    $x = -$x
    assert_equal(-815915283247897734345611269596115894272000000000, $x)
    assert_equal(2-(2**32), -(2**32-2))
    assert_equal(2**32 - 5, (2**32-3)-2)

    for i in 1000..1014
      assert_equal(2 ** i, 1 << i)
    end

    n1 = 1 << 1000
    for i in 1000..1014
      assert_equal(n1, 1 << i)
      n1 *= 2
    end

    n2=n1
    for i in 1..10
      n1 = n1 / 2
      n2 = n2 >> 1
      assert_equal(n1, n2)
    end

    for i in 4000..4096
      n1 = 1 << i;
      assert_equal(n1-1, (n1**2-1) / (n1+1))
    end
  end

  def test_calc
    b = 10**80
    a = b * 9 + 7
    assert_equal(7, a.modulo(b))
    assert_equal(-b + 7, a.modulo(-b))
    assert_equal(b + -7, (-a).modulo(b))
    assert_equal(-7, (-a).modulo(-b))
    assert_equal(7, a.remainder(b))
    assert_equal(7, a.remainder(-b))
    assert_equal(-7, (-a).remainder(b))
    assert_equal(-7, (-a).remainder(-b))

    assert_equal(10000000000000000000100000000000000000000, 10**40+10**20)
    assert_equal(100000000000000000000, 10**40/10**20)

    a = 677330545177305025495135714080
    b = 14269972710765292560
    assert_equal(0, a % b)
    assert_equal(0, -a % b)
  end

  def shift_test(a)
    b = a / (2 ** 32)
    c = a >> 32
    assert_equal(b, c)

    b = a * (2 ** 32)
    c = a << 32
    assert_equal(b, c)
  end

  def test_shift
    shift_test(-4518325415524767873)
    shift_test(-0xfffffffffffffffff)
  end

  def test_to_s
    assert_equal("fvvvvvvvvvvvv" ,18446744073709551615.to_s(32), "[ruby-core:10686]")
    assert_equal("g000000000000" ,18446744073709551616.to_s(32), "[ruby-core:10686]")
    assert_equal("3w5e11264sgsf" ,18446744073709551615.to_s(36), "[ruby-core:10686]")
    assert_equal("3w5e11264sgsg" ,18446744073709551616.to_s(36), "[ruby-core:10686]")
    assert_equal("nd075ib45k86f" ,18446744073709551615.to_s(31), "[ruby-core:10686]")
    assert_equal("nd075ib45k86g" ,18446744073709551616.to_s(31), "[ruby-core:10686]")
    assert_equal("1777777777777777777777" ,18446744073709551615.to_s(8))
    assert_equal("-1777777777777777777777" ,-18446744073709551615.to_s(8))
  end


  T_ZERO = (2**32).coerce(0).first
  T_ONE  = (2**32).coerce(1).first
  T_MONE = (2**32).coerce(-1).first
  T31  = 2**31   # 2147483648
  T31P = T31 - 1 # 2147483647
  T32  = 2**32   # 4294967296
  T32P = T32 - 1 # 4294967295
  T64  = 2**64   # 18446744073709551616
  T64P = T64 - 1 # 18446744073709551615

  def test_big_2comp
    assert_equal("-4294967296", (~T32P).to_s)
    assert_equal("..f00000000", "%x" % -T32)
  end

  def test_int2inum
    assert_equal([T31P], [T31P].pack("I").unpack("I"))
    assert_equal([T31P], [T31P].pack("i").unpack("i"))
  end

  def test_quad_pack
    assert_equal([    1], [    1].pack("q").unpack("q"))
    assert_equal([-   1], [-   1].pack("q").unpack("q"))
    assert_equal([ T31P], [ T31P].pack("q").unpack("q"))
    assert_equal([-T31P], [-T31P].pack("q").unpack("q"))
    assert_equal([ T64P], [ T64P].pack("Q").unpack("Q"))
    assert_equal([    0], [ T64 ].pack("Q").unpack("Q"))
  end

  def test_str_to_inum
    assert_equal(1, " +1".to_i)
    assert_equal(-1, " -1".to_i)
    assert_equal(0, "++1".to_i)
    assert_equal(73, "111".oct)
    assert_equal(273, "0x111".oct)
    assert_equal(7, "0b111".oct)
    assert_equal(73, "0o111".oct)
    assert_equal(111, "0d111".oct)
    assert_equal(73, "0111".oct)
    assert_equal(111, Integer("111"))
    assert_equal(13, "111".to_i(3))
    assert_raise(ArgumentError) { "111".to_i(37) }
    assert_equal(1333, "111".to_i(36))
    assert_equal(1057, "111".to_i(32))
    assert_equal(0, "00a".to_i)
    assert_equal(1, Integer("1 "))
    assert_raise(ArgumentError) { Integer("1_") }
    assert_raise(ArgumentError) { Integer("1__") }
    assert_raise(ArgumentError) { Integer("1_0 x") }
    assert_equal(T31P, "1111111111111111111111111111111".to_i(2))
    assert_equal(0_2, '0_2'.to_i)
    assert_equal(00_2, '00_2'.to_i)
    assert_equal(00_02, '00_02'.to_i)
  end

  def test_to_s2
    assert_raise(ArgumentError) { T31P.to_s(37) }
    assert_equal("9" * 32768, (10**32768-1).to_s)
    assert_raise(RangeError) { Process.wait(1, T64P) }
    assert_equal("0", T_ZERO.to_s)
    assert_equal("1", T_ONE.to_s)
  end

  def test_to_f
    assert_nothing_raised { T31P.to_f.to_i }
    assert_raise(FloatDomainError) { (1024**1024).to_f.to_i }
  end

  def test_cmp
    assert(T31P > 1)
    assert(T31P < 2147483648.0)
    assert(T31P < T64P)
    assert(T64P > T31P)
    assert_raise(ArgumentError) { T31P < "foo" }
  end

  def test_eq
    assert(T31P != 1)
    assert(T31P == 2147483647.0)
    assert(T31P != "foo")
  end

  def test_eql
    assert(T31P.eql?(T31P))
  end

  def test_convert
    assert_equal([255], [T_MONE].pack("C").unpack("C"))
    assert_equal([0], [T32].pack("C").unpack("C"))
    assert_raise(RangeError) { 0.to_s(T32) }
    assert_raise(Errno::EINVAL) do
      begin Process.wait(0, T32P); rescue Errno::ECHILD; end
      begin Process.wait(0, T64P); rescue Errno::ECHILD; end
    end
    assert_raise(RangeError) do
      begin Process.wait(0, T32); rescue Errno::ECHILD; end
      begin Process.wait(0, T64); rescue Errno::ECHILD; end
    end
    assert_raise(RangeError) do
      begin Process.wait(0, -T32P); rescue Errno::ECHILD; end
      begin Process.wait(0, -T64P); rescue Errno::ECHILD; end
    end
  end

  def test_sub
    assert_equal(-T31, T32 - (T32 + T31))
  end

  def test_plus
    assert_equal(T32.to_f, T32P + 1.0)
    assert_raise(TypeError) { T32 + "foo" }
  end

  def test_minus
    assert_equal(T32P.to_f, T32 - 1.0)
    assert_raise(TypeError) { T32 - "foo" }
  end

  def test_mul
    assert_equal(T32.to_f, T32 * 1.0)
    assert_raise(TypeError) { T32 * "foo" }
  end

  def test_divrem
    assert_equal(0, T32 / T64)
  end

  def test_div
    assert_equal(T32.to_f, T32 / 1.0)
    assert_raise(TypeError) { T32 / "foo" }
    assert_equal(0x20000000, 0x40000001.div(2.0), "[ruby-dev:34553]")
  end

  def test_idiv
    assert_equal(715827882, 1073741824.div(Rational(3,2)), ' [ruby-dev:34066]')
  end

  def test_modulo
    assert_raise(TypeError) { T32 % "foo" }
  end

  def test_remainder
    assert_equal(0, T32.remainder(1))
    assert_raise(TypeError) { T32.remainder("foo") }
  end

  def test_divmod
    assert_equal([T32, 0], T32.divmod(1))
    assert_equal([2, 0], T32.divmod(T31))
    assert_raise(TypeError) { T32.divmod("foo") }
  end

  def test_quo
    assert_equal(T32.to_f, T32.quo(1))
    assert_equal(T32.to_f, T32.quo(1.0))
    assert_equal(T32.to_f, T32.quo(T_ONE))

    assert_raise(TypeError) { T32.quo("foo") }

    assert_equal(1024**1024, (1024**1024).quo(1))
    assert_equal(1024**1024, (1024**1024).quo(1.0))
    assert_equal(1024**1024*2, (1024**1024*2).quo(1))
    inf = 1 / 0.0; nan = inf / inf

    assert((1024**1024*2).quo(nan).nan?)
  end

  def test_pow
    assert_equal(1.0, T32 ** 0.0)
    assert_equal(1.0 / T32, T32 ** -1)
    assert((T32 ** T32).infinite?)
    assert((T32 ** (2**30-1)).infinite?)

    ### rational changes the behavior of Bignum#**
    #assert_raise(TypeError) { T32**"foo" }
    assert_raise(TypeError, ArgumentError) { T32**"foo" }
  end

  def test_and
    assert_equal(0, T32 & 1)
    assert_equal(-T32, (-T32) & (-T31))
    assert_equal(0, T32 & T64)
  end

  def test_or
    assert_equal(T32 + 1, T32 | 1)
    assert_equal(T32 + T31, T32 | T31)
    assert_equal(-T31, (-T32) | (-T31))
    assert_equal(T64 + T32, T32 | T64)
  end

  def test_xor
    assert_equal(T32 + 1, T32 ^ 1)
    assert_equal(T32 + T31, T32 ^ T31)
    assert_equal(T31, (-T32) ^ (-T31))
    assert_equal(T64 + T32, T32 ^ T64)
  end

  def test_shift2
    assert_equal(2**33, (2**32) <<  1)
    assert_equal(2**31, (2**32) << -1)
    assert_equal(2**33, (2**32) <<  1.0)
    assert_equal(2**31, (2**32) << -1.0)
    assert_equal(2**33, (2**32) << T_ONE)
    assert_equal(2**31, (2**32) << T_MONE)
    assert_equal(2**31, (2**32) >>  1)
    assert_equal(2**33, (2**32) >> -1)
    assert_equal(2**31, (2**32) >>  1.0)
    assert_equal(2**33, (2**32) >> -1.0)
    assert_equal(2**31, (2**32) >> T_ONE)
    assert_equal(2**33, (2**32) >> T_MONE)
    assert_equal( 0,  (2**32) >> (2**32))
    assert_equal(-1, -(2**32) >> (2**32))
    assert_equal( 0,  (2**32) >> 128)
    assert_equal(-1, -(2**32) >> 128)
    assert_equal( 0,  (2**31) >> 32)
    assert_equal(-1, -(2**31) >> 32)
  end

  def test_aref
    assert_equal(0, (2**32)[0])
    assert_equal(0, (2**32)[2**32])
    assert_equal(0, (2**32)[-(2**32)])
    assert_equal(0, (2**32)[T_ZERO])
    assert_equal(0, (-(2**64))[0])
    assert_equal(1, (-2**256)[256])
  end

  def test_hash
    assert_nothing_raised { T31P.hash }
  end

  def test_coerce
    assert_equal([T64P, T31P], T31P.coerce(T64P))
    assert_raise(TypeError) { T31P.coerce(nil) }
  end

  def test_abs
    assert_equal(T31P, (-T31P).abs)
  end

  def test_size
    assert(T31P.size.is_a?(Integer))
  end

  def test_odd
    assert_equal(true, (2**32+1).odd?)
    assert_equal(false, (2**32).odd?)
  end

  def test_even
    assert_equal(false, (2**32+1).even?)
    assert_equal(true, (2**32).even?)
  end

  def interrupt
    time = Time.now
    start_flag = false
    end_flag = false
    thread = Thread.new do
      start_flag = true
      yield
      end_flag = true
    end
    sleep 1
    thread.raise
    thread.join rescue nil
    start_flag && !end_flag && Time.now - time < 10
  end

  def test_interrupt
    assert(interrupt { (65536 ** 65536).to_s })
  end

  def test_too_big_to_s
    if (big = 2**31-1).is_a?(Fixnum)
      return
    end
    e = assert_raise(RangeError) {(1 << big).to_s}
    assert_match(/too big to convert/, e.message)
  end
end
