require "spec"
require "big"

private def it_converts_to_s(num, str, *, file = __FILE__, line = __LINE__, **opts)
  it file: file, line: line do
    num.to_s(**opts).should eq(str), file: file, line: line
    String.build { |io| num.to_s(io, **opts) }.should eq(str), file: file, line: line
  end
end

describe "BigInt" do
  describe "#integer?" do
    it { BigInt.zero.integer?.should be_true }
    it { 12345.to_big_i.integer?.should be_true }
  end

  it "creates with a value of zero" do
    BigInt.new.to_s.should eq("0")
  end

  it "creates from signed ints" do
    BigInt.new(-1_i8).to_s.should eq("-1")
    BigInt.new(-1_i16).to_s.should eq("-1")
    BigInt.new(-1_i32).to_s.should eq("-1")
    BigInt.new(-1_i64).to_s.should eq("-1")
  end

  it "creates from unsigned ints" do
    BigInt.new(1_u8).to_s.should eq("1")
    BigInt.new(1_u16).to_s.should eq("1")
    BigInt.new(1_u32).to_s.should eq("1")
    BigInt.new(1_u64).to_s.should eq("1")
  end

  it "creates from string" do
    BigInt.new("12345678").to_s.should eq("12345678")
    BigInt.new("123_456_78").to_s.should eq("12345678")
    BigInt.new("+12345678").to_s.should eq("12345678")
    BigInt.new("-12345678").to_s.should eq("-12345678")
  end

  it "raises if creates from string but invalid" do
    expect_raises ArgumentError, "Invalid BigInt: 123 hello 456" do
      BigInt.new("123 hello 456")
    end
  end

  it "raises if creating from infinity" do
    expect_raises(ArgumentError, "Can only construct from a finite number") { BigInt.new(Float32::INFINITY) }
    expect_raises(ArgumentError, "Can only construct from a finite number") { BigInt.new(Float64::INFINITY) }
  end

  it "raises if creating from NaN" do
    expect_raises(ArgumentError, "Can only construct from a finite number") { BigInt.new(Float32::NAN) }
    expect_raises(ArgumentError, "Can only construct from a finite number") { BigInt.new(Float64::NAN) }
  end

  it "creates from float" do
    BigInt.new(12.3).to_s.should eq("12")
  end

  describe ".from_digits" do
    it "creates from digits" do
      BigInt.from_digits([] of Int32).should eq(0.to_big_i)
      BigInt.from_digits([3, 2, 1]).should eq(123.to_big_i)
      BigInt.from_digits({3, 2, 1}, base: 16).should eq(0x123.to_big_i)
      BigInt.from_digits((0xfa..0xff), base: 256).should eq(0xfffefdfcfbfa.to_big_i)
      BigInt.from_digits([UInt128::MAX, UInt128::MAX, UInt128::MAX, UInt128::MAX], base: 2.to_big_i ** 128).should eq(2.to_big_i ** 512 - 1)
      BigInt.from_digits(Deque{1, 0.to_big_i, 0, 1.to_big_i}, base: 2).should eq(9.to_big_i)
    end

    it "raises for base less than 2" do
      [-1, 0, 1].each do |base|
        expect_raises(ArgumentError, "Invalid base #{base}") do
          BigInt.from_digits([1, 2, 3], base)
        end
      end
    end

    it "raises for digits greater than base" do
      expect_raises(ArgumentError, "Invalid digit 2 for base 2") do
        BigInt.from_digits([1, 0, 2], 2)
      end

      expect_raises(ArgumentError, "Invalid digit 10 for base 2") do
        BigInt.from_digits([1, 0, 10], 2)
      end
    end

    it "raises for negative digits" do
      expect_raises(ArgumentError, "Invalid digit -1") do
        BigInt.from_digits([1, 2, -1])
      end
    end
  end

  it "compares" do
    1.to_big_i.should eq(1.to_big_i)
    1.to_big_i.should eq(1)
    1.to_big_i.should eq(1_u8)

    [3.to_big_i, 2.to_big_i, 10.to_big_i, 4, 8_u8].sort.should eq([2, 3, 4, 8, 10])
  end

  it "compares against float" do
    1.to_big_i.should eq(1.0)
    1.to_big_i.should eq(1.0_f32)
    1.to_big_i.should_not eq(1.1)
    1.0.should eq(1.to_big_i)
    1.0_f32.should eq(1.to_big_i)
    1.1.should_not eq(1.to_big_i)

    [1.1, 1.to_big_i, 3.to_big_i, 2.2].sort.should eq([1, 1.1, 2.2, 3])

    (1.to_big_i <=> Float64::NAN).should be_nil
    (1.to_big_i <=> Float32::NAN).should be_nil
    (Float64::NAN <=> 1.to_big_i).should be_nil
    (Float32::NAN <=> 1.to_big_i).should be_nil

    typeof(1.to_big_i <=> Float64::NAN).should eq(Int32?)
    typeof(1.to_big_i <=> Float32::NAN).should eq(Int32?)
    typeof(Float64::NAN <=> 1.to_big_i).should eq(Int32?)
    typeof(Float32::NAN <=> 1.to_big_i).should eq(Int32?)

    typeof(1.to_big_i <=> 1.to_big_f).should eq(Int32)
    typeof(1.to_big_f <=> 1.to_big_i).should eq(Int32)
  end

  it "divides and calculates the modulo" do
    11.to_big_i.divmod(3.to_big_i).should eq({3, 2})
    11.to_big_i.divmod(-3.to_big_i).should eq({-4, -1})

    11.to_big_i.divmod(3_i32).should eq({3, 2})
    11.to_big_i.divmod(-3_i32).should eq({-4, -1})

    10.to_big_i.divmod(2).should eq({5, 0})
    11.to_big_i.divmod(2).should eq({5, 1})

    10.to_big_i.divmod(2.to_big_i).should eq({5, 0})
    11.to_big_i.divmod(2.to_big_i).should eq({5, 1})

    10.to_big_i.divmod(-2).should eq({-5, 0})
    11.to_big_i.divmod(-2).should eq({-6, -1})

    -10.to_big_i.divmod(2).should eq({-5, 0})
    -11.to_big_i.divmod(2).should eq({-6, 1})

    -10.to_big_i.divmod(-2).should eq({5, 0})
    -11.to_big_i.divmod(-2).should eq({5, -1})
  end

  it "adds" do
    (1.to_big_i + 2.to_big_i).should eq(3.to_big_i)
    (1.to_big_i + 2).should eq(3.to_big_i)
    (1.to_big_i + 2_u8).should eq(3.to_big_i)
    (5.to_big_i + (-2_i64)).should eq(3.to_big_i)
    (5.to_big_i + Int64::MAX).should be > Int64::MAX.to_big_i
    (5.to_big_i + Int64::MAX).should eq(Int64::MAX.to_big_i + 5)

    (2 + 1.to_big_i).should eq(3.to_big_i)

    (1.to_big_i &+ 2.to_big_i).should eq(3.to_big_i)
    (1.to_big_i &+ 2).should eq(3.to_big_i)
    (1.to_big_i &+ 2_u8).should eq(3.to_big_i)
    (5.to_big_i &+ (-2_i64)).should eq(3.to_big_i)
    (5.to_big_i &+ Int64::MAX).should be > Int64::MAX.to_big_i
    (5.to_big_i &+ Int64::MAX).should eq(Int64::MAX.to_big_i &+ 5)

    (2 &+ 1.to_big_i).should eq(3.to_big_i)
  end

  it "subs" do
    (5.to_big_i - 2.to_big_i).should eq(3.to_big_i)
    (5.to_big_i - 2).should eq(3.to_big_i)
    (5.to_big_i - 2_u8).should eq(3.to_big_i)
    (5.to_big_i - (-2_i64)).should eq(7.to_big_i)
    (-5.to_big_i - Int64::MAX).should be < -Int64::MAX.to_big_i
    (-5.to_big_i - Int64::MAX).should eq(-Int64::MAX.to_big_i - 5)

    (5 - 1.to_big_i).should eq(4.to_big_i)
    (-5 - 1.to_big_i).should eq(-6.to_big_i)

    (5.to_big_i &- 2.to_big_i).should eq(3.to_big_i)
    (5.to_big_i &- 2).should eq(3.to_big_i)
    (5.to_big_i &- 2_u8).should eq(3.to_big_i)
    (5.to_big_i &- (-2_i64)).should eq(7.to_big_i)
    (-5.to_big_i &- Int64::MAX).should be < -Int64::MAX.to_big_i
    (-5.to_big_i &- Int64::MAX).should eq(-Int64::MAX.to_big_i &- 5)

    (5 &- 1.to_big_i).should eq(4.to_big_i)
    (-5 &- 1.to_big_i).should eq(-6.to_big_i)
  end

  it "negates" do
    (-(-123.to_big_i)).should eq(123.to_big_i)
  end

  it "multiplies" do
    (2.to_big_i * 3.to_big_i).should eq(6.to_big_i)
    (2.to_big_i * 3).should eq(6.to_big_i)
    (2.to_big_i * 3_u8).should eq(6.to_big_i)
    (3 * 2.to_big_i).should eq(6.to_big_i)
    (3_u8 * 2.to_big_i).should eq(6.to_big_i)
    (2.to_big_i * Int64::MAX).should eq(2.to_big_i * Int64::MAX.to_big_i)

    (2.to_big_i &* 3.to_big_i).should eq(6.to_big_i)
    (2.to_big_i &* 3).should eq(6.to_big_i)
    (2.to_big_i &* 3_u8).should eq(6.to_big_i)
    (3 &* 2.to_big_i).should eq(6.to_big_i)
    (3_u8 &* 2.to_big_i).should eq(6.to_big_i)
    (2.to_big_i &* Int64::MAX).should eq(2.to_big_i &* Int64::MAX.to_big_i)
  end

  it "gets absolute value" do
    (-10.to_big_i.abs).should eq(10.to_big_i)
  end

  it "gets factorial value" do
    0.to_big_i.factorial.should eq(1.to_big_i)
    5.to_big_i.factorial.should eq(120.to_big_i)
    100.to_big_i.factorial.should eq("93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000".to_big_i)
  end

  it "raises if factorial of negative" do
    expect_raises ArgumentError do
      -1.to_big_i.factorial
    end

    expect_raises ArgumentError do
      "-93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000".to_big_i.factorial
    end
  end

  it "raises if factorial of 2^64" do
    expect_raises ArgumentError do
      (LibGMP::UI::MAX.to_big_i + 1).factorial
    end
  end

  it "divides" do
    (10.to_big_i / 3.to_big_i).should be_close(3.3333.to_big_f, 0.0001)
    (10.to_big_i / 3).should be_close(3.3333.to_big_f, 0.0001)
    (10 / 3.to_big_i).should be_close(3.3333.to_big_f, 0.0001)
    ((Int64::MAX.to_big_i * 2.to_big_i) / Int64::MAX).should eq(2.to_big_i)
  end

  it "divides" do
    (10.to_big_i // 3.to_big_i).should eq(3.to_big_i)
    (10.to_big_i // 3).should eq(3.to_big_i)
    (10 // 3.to_big_i).should eq(3.to_big_i)
    ((Int64::MAX.to_big_i * 2.to_big_i) // Int64::MAX).should eq(2.to_big_i)
  end

  it "divides with negative numbers" do
    (7.to_big_i / 2).should eq(3.5.to_big_f)
    (7.to_big_i / 2.to_big_i).should eq(3.5.to_big_f)
    (7.to_big_i / -2).should eq(-3.5.to_big_f)
    (7.to_big_i / -2.to_big_i).should eq(-3.5.to_big_f)
    (-7.to_big_i / 2).should eq(-3.5.to_big_f)
    (-7.to_big_i / 2.to_big_i).should eq(-3.5.to_big_f)
    (-7.to_big_i / -2).should eq(3.5.to_big_f)
    (-7.to_big_i / -2.to_big_i).should eq(3.5.to_big_f)

    (-6.to_big_i / 2).should eq(-3.to_big_f)
    (6.to_big_i / -2).should eq(-3.to_big_f)
    (-6.to_big_i / -2).should eq(3.to_big_f)
  end

  it "divides with negative numbers" do
    (7.to_big_i // 2).should eq(3.to_big_i)
    (7.to_big_i // 2.to_big_i).should eq(3.to_big_i)
    (7.to_big_i // -2).should eq(-4.to_big_i)
    (7.to_big_i // -2.to_big_i).should eq(-4.to_big_i)
    (-7.to_big_i // 2).should eq(-4.to_big_i)
    (-7.to_big_i // 2.to_big_i).should eq(-4.to_big_i)
    (-7.to_big_i // -2).should eq(3.to_big_i)
    (-7.to_big_i // -2.to_big_i).should eq(3.to_big_i)

    (-6.to_big_i // 2).should eq(-3.to_big_i)
    (6.to_big_i // -2).should eq(-3.to_big_i)
    (-6.to_big_i // -2).should eq(3.to_big_i)
  end

  it "tdivs" do
    5.to_big_i.tdiv(3).should eq(1)
    -5.to_big_i.tdiv(3).should eq(-1)
    5.to_big_i.tdiv(-3).should eq(-1)
    -5.to_big_i.tdiv(-3).should eq(1)
  end

  it "does modulo" do
    (10.to_big_i % 3.to_big_i).should eq(1.to_big_i)
    (10.to_big_i % 3).should eq(1.to_big_i)
    (10.to_big_i % 3u8).should eq(1.to_big_i)
    (10 % 3.to_big_i).should eq(1.to_big_i)
  end

  it "does modulo with negative numbers" do
    (7.to_big_i % 2).should eq(1.to_big_i)
    (7.to_big_i % 2.to_big_i).should eq(1.to_big_i)
    (7.to_big_i % -2).should eq(-1.to_big_i)
    (7.to_big_i % -2.to_big_i).should eq(-1.to_big_i)
    (-7.to_big_i % 2).should eq(1.to_big_i)
    (-7.to_big_i % 2.to_big_i).should eq(1.to_big_i)
    (-7.to_big_i % -2).should eq(-1.to_big_i)
    (-7.to_big_i % -2.to_big_i).should eq(-1.to_big_i)

    (6.to_big_i % 2).should eq(0.to_big_i)
    (6.to_big_i % -2).should eq(0.to_big_i)
    (-6.to_big_i % 2).should eq(0.to_big_i)
    (-6.to_big_i % -2).should eq(0.to_big_i)
  end

  it "does remainder with negative numbers" do
    5.to_big_i.remainder(3).should eq(2)
    -5.to_big_i.remainder(3).should eq(-2)
    5.to_big_i.remainder(-3).should eq(2)
    -5.to_big_i.remainder(-3).should eq(-2)
  end

  it "#bit" do
    x = 123.to_big_i
    x.bit(-10.to_big_i ** 99).should eq(0)
    x.bit(-(2.to_big_i ** 64)).should eq(0)
    x.bit(-1).should eq(0)
    x.bit(0).should eq(1)
    x.bit(2).should eq(0)
    x.bit(3).should eq(1)
    x.bit(6).should eq(1)
    x.bit(7).should eq(0)
    x.bit(64).should eq(0)
    x.bit(2.to_big_i ** 64).should eq(0)
    x.bit(10.to_big_i ** 99).should eq(0)

    x = ~(123.to_big_i)
    x.bit(-10.to_big_i ** 99).should eq(0)
    x.bit(-(2.to_big_i ** 64)).should eq(0)
    x.bit(-1).should eq(0)
    x.bit(0).should eq(0)
    x.bit(2).should eq(1)
    x.bit(3).should eq(0)
    x.bit(6).should eq(0)
    x.bit(7).should eq(1)
    x.bit(64).should eq(1)
    x.bit(2.to_big_i ** 64).should eq(1)
    x.bit(10.to_big_i ** 99).should eq(1)
  end

  it "does bitwise and" do
    (123.to_big_i & 321).should eq(65)
    (BigInt.new("96238761238973286532") & 86325735648).should eq(69124358272)
  end

  it "does bitwise or" do
    (123.to_big_i | 4).should eq(127)
    (BigInt.new("96238761238986532") | 8632573).should eq(96238761247506429)
  end

  it "does bitwise xor" do
    (123.to_big_i ^ 50).should eq(73)
    (BigInt.new("96238761238986532") ^ 8632573).should eq(96238761247393753)
  end

  it "does bitwise not" do
    (~123).should eq(-124)

    a = BigInt.new("192623876123689865327")
    b = BigInt.new("-192623876123689865328")
    (~a).should eq(b)
  end

  it "does bitwise right shift" do
    (123.to_big_i >> 4).should eq(7)
    (123456.to_big_i >> 8).should eq(482)
  end

  it "does bitwise left shift" do
    (123.to_big_i << 4).should eq(1968)
    (123456.to_big_i << 8).should eq(31604736)
  end

  it "raises if divides by zero" do
    expect_raises DivisionByZeroError do
      10.to_big_i / 0.to_big_i
    end

    expect_raises DivisionByZeroError do
      10.to_big_i / 0
    end

    expect_raises DivisionByZeroError do
      10 / 0.to_big_i
    end
  end

  it "raises if divides by zero" do
    expect_raises DivisionByZeroError do
      10.to_big_i // 0.to_big_i
    end

    expect_raises DivisionByZeroError do
      10.to_big_i // 0
    end

    expect_raises DivisionByZeroError do
      10 // 0.to_big_i
    end
  end

  it "raises if mods by zero" do
    expect_raises DivisionByZeroError do
      10.to_big_i % 0.to_big_i
    end

    expect_raises DivisionByZeroError do
      10.to_big_i % 0
    end

    expect_raises DivisionByZeroError do
      10 % 0.to_big_i
    end
  end

  it "exponentiates" do
    result = (2.to_big_i ** 1000)
    result.should be_a(BigInt)
    result.to_s.should eq("10715086071862673209484250490600018105614048117055336074437503883703510511249361224931983788156958581275946729175531468251871452856923140435984577574698574803934567774824230985421074605062371141877954182153046474983581941267398767559165543946077062914571196477686542167660429831652624386837205668069376")
  end

  describe "#to_s" do
    context "base and upcase parameters" do
      a = BigInt.new("1234567890123456789")
      it_converts_to_s a, "1000100100010000100001111010001111101111010011000000100010101", base: 2
      it_converts_to_s a, "112210f47de98115", base: 16
      it_converts_to_s a, "112210F47DE98115", base: 16, upcase: true
      it_converts_to_s a, "128gguhuuj08l", base: 32
      it_converts_to_s a, "128GGUHUUJ08L", base: 32, upcase: true
      it_converts_to_s a, "1tckI1NfUnH", base: 62

      # ensure case is same as for primitive integers
      it_converts_to_s 10.to_big_i, 10.to_s(62), base: 62

      it_converts_to_s (-a), "-1000100100010000100001111010001111101111010011000000100010101", base: 2
      it_converts_to_s (-a), "-112210f47de98115", base: 16
      it_converts_to_s (-a), "-112210F47DE98115", base: 16, upcase: true
      it_converts_to_s (-a), "-128gguhuuj08l", base: 32
      it_converts_to_s (-a), "-128GGUHUUJ08L", base: 32, upcase: true
      it_converts_to_s (-a), "-1tckI1NfUnH", base: 62

      it_converts_to_s 16.to_big_i ** 1000, "1#{"0" * 1000}", base: 16

      it "raises on base 1" do
        expect_raises(ArgumentError, "Invalid base 1") { a.to_s(1) }
        expect_raises(ArgumentError, "Invalid base 1") { a.to_s(IO::Memory.new, 1) }
      end

      it "raises on base 37" do
        expect_raises(ArgumentError, "Invalid base 37") { a.to_s(37) }
        expect_raises(ArgumentError, "Invalid base 37") { a.to_s(IO::Memory.new, 37) }
      end

      it "raises on base 62 with upcase" do
        expect_raises(ArgumentError, "upcase must be false for base 62") { a.to_s(62, upcase: true) }
        expect_raises(ArgumentError, "upcase must be false for base 62") { a.to_s(IO::Memory.new, 62, upcase: true) }
      end
    end

    context "precision parameter" do
      it_converts_to_s 0.to_big_i, "", precision: 0
      it_converts_to_s 0.to_big_i, "0", precision: 1
      it_converts_to_s 0.to_big_i, "00", precision: 2
      it_converts_to_s 0.to_big_i, "00000", precision: 5
      it_converts_to_s 0.to_big_i, "0" * 200, precision: 200

      it_converts_to_s 1.to_big_i, "1", precision: 0
      it_converts_to_s 1.to_big_i, "1", precision: 1
      it_converts_to_s 1.to_big_i, "01", precision: 2
      it_converts_to_s 1.to_big_i, "00001", precision: 5
      it_converts_to_s 1.to_big_i, "#{"0" * 199}1", precision: 200

      it_converts_to_s 2.to_big_i, "2", precision: 0
      it_converts_to_s 2.to_big_i, "2", precision: 1
      it_converts_to_s 2.to_big_i, "02", precision: 2
      it_converts_to_s 2.to_big_i, "00002", precision: 5
      it_converts_to_s 2.to_big_i, "#{"0" * 199}2", precision: 200

      it_converts_to_s (-1).to_big_i, "-1", precision: 0
      it_converts_to_s (-1).to_big_i, "-1", precision: 1
      it_converts_to_s (-1).to_big_i, "-01", precision: 2
      it_converts_to_s (-1).to_big_i, "-00001", precision: 5
      it_converts_to_s (-1).to_big_i, "-#{"0" * 199}1", precision: 200

      it_converts_to_s 85.to_big_i, "85", precision: 0
      it_converts_to_s 85.to_big_i, "85", precision: 1
      it_converts_to_s 85.to_big_i, "85", precision: 2
      it_converts_to_s 85.to_big_i, "085", precision: 3
      it_converts_to_s 85.to_big_i, "0085", precision: 4
      it_converts_to_s 85.to_big_i, "00085", precision: 5
      it_converts_to_s 85.to_big_i, "#{"0" * 198}85", precision: 200

      it_converts_to_s (-85).to_big_i, "-85", precision: 0
      it_converts_to_s (-85).to_big_i, "-85", precision: 1
      it_converts_to_s (-85).to_big_i, "-85", precision: 2
      it_converts_to_s (-85).to_big_i, "-085", precision: 3
      it_converts_to_s (-85).to_big_i, "-0085", precision: 4
      it_converts_to_s (-85).to_big_i, "-00085", precision: 5
      it_converts_to_s (-85).to_big_i, "-#{"0" * 198}85", precision: 200

      it_converts_to_s 123.to_big_i, "123", precision: 0
      it_converts_to_s 123.to_big_i, "123", precision: 1
      it_converts_to_s 123.to_big_i, "123", precision: 2
      it_converts_to_s 123.to_big_i, "00123", precision: 5
      it_converts_to_s 123.to_big_i, "#{"0" * 197}123", precision: 200

      a = 2.to_big_i ** 1024 - 1
      it_converts_to_s a, "#{"1" * 1024}", base: 2, precision: 1023
      it_converts_to_s a, "#{"1" * 1024}", base: 2, precision: 1024
      it_converts_to_s a, "0#{"1" * 1024}", base: 2, precision: 1025
      it_converts_to_s a, "#{"0" * 976}#{"1" * 1024}", base: 2, precision: 2000

      it_converts_to_s (-a), "-#{"1" * 1024}", base: 2, precision: 1023
      it_converts_to_s (-a), "-#{"1" * 1024}", base: 2, precision: 1024
      it_converts_to_s (-a), "-0#{"1" * 1024}", base: 2, precision: 1025
      it_converts_to_s (-a), "-#{"0" * 976}#{"1" * 1024}", base: 2, precision: 2000
    end
  end

  it "does to_big_f" do
    a = BigInt.new("1234567890123456789")
    a.to_big_f.should eq(BigFloat.new("1234567890123456789.0"))
  end

  describe "#inspect" do
    it { "2".to_big_i.inspect.should eq("2") }
  end

  it "does gcd and lcm" do
    # 3 primes
    a = BigInt.new("48112959837082048697")
    b = BigInt.new("12764787846358441471")
    c = BigInt.new("36413321723440003717")
    abc = a * b * c
    a_17 = a * 17

    (abc * b).gcd(abc * c).should eq(abc)
    abc.gcd(a_17).should eq(a)
    (abc * b).lcm(abc * c).should eq(abc * b * c)
    (abc * b).gcd(abc * c).should be_a(BigInt)

    (a_17).gcd(17).should eq(17)
    (-a_17).gcd(17).should eq(17)
    (17).gcd(a_17).should eq(17)
    (17).gcd(-a_17).should eq(17)

    (a_17).lcm(17).should eq(a_17)
    (-a_17).lcm(17).should eq(a_17)
    (17).lcm(a_17).should eq(a_17)
    (17).lcm(-a_17).should eq(a_17)

    (a_17).gcd(17).should be_a(Int::Unsigned)
  end

  it "can use Number::[]" do
    a = BigInt[146, "3464", 97, "545"]
    b = [BigInt.new(146), BigInt.new(3464), BigInt.new(97), BigInt.new(545)]
    a.should eq(b)
  end

  describe "#to_i" do
    it "converts to Int32" do
      BigInt.new(1234567890).to_i.should(be_a(Int32)).should eq(1234567890)
      expect_raises(OverflowError) { BigInt.new(2147483648).to_i }
      expect_raises(OverflowError) { BigInt.new(-2147483649).to_i }
    end
  end

  describe "#to_i!" do
    it "converts to Int32" do
      BigInt.new(1234567890).to_i!.should(be_a(Int32)).should eq(1234567890)
      BigInt.new(2147483648).to_i!.should eq(Int32::MIN)
      BigInt.new(-2147483649).to_i!.should eq(Int32::MAX)
    end
  end

  describe "#to_u" do
    it "converts to UInt32" do
      BigInt.new(1234567890).to_u.should(be_a(UInt32)).should eq(1234567890_u32)
      expect_raises(OverflowError) { BigInt.new(4294967296).to_u }
      expect_raises(OverflowError) { BigInt.new(-1).to_u }
    end
  end

  describe "#to_u!" do
    it "converts to UInt32" do
      BigInt.new(1234567890).to_u!.should(be_a(UInt32)).should eq(1234567890_u32)
      BigInt.new(4294967296).to_u!.should eq(0_u32)
      BigInt.new(-1).to_u!.should eq(UInt32::MAX)
    end
  end

  {% for n in [8, 16, 32, 64, 128] %}
    describe "#to_u{{n}}" do
      it "converts to UInt{{n}}" do
        (0..{{n - 1}}).each do |i|
          (1.to_big_i << i).to_u{{n}}.should eq(UInt{{n}}.new(1) << i)
        end

        UInt{{n}}::MIN.to_big_i.to_u{{n}}.should eq(UInt{{n}}::MIN)
        UInt{{n}}::MAX.to_big_i.to_u{{n}}.should eq(UInt{{n}}::MAX)
      end

      it "raises OverflowError" do
        expect_raises(OverflowError) { (1.to_big_i << {{n}}).to_u{{n}} }
        expect_raises(OverflowError) { (-1.to_big_i).to_u{{n}} }
        expect_raises(OverflowError) { (-1.to_big_i << {{n}}).to_u{{n}} }
      end
    end

    describe "#to_i{{n}}" do
      it "converts to Int{{n}}" do
        (0..{{n - 2}}).each do |i|
          (1.to_big_i << i).to_i{{n}}.should eq(Int{{n}}.new(1) << i)
          (-1.to_big_i << i).to_i{{n}}.should eq(Int{{n}}.new(-1) << i)
        end

        Int{{n}}.zero.to_big_i.to_i{{n}}.should eq(Int{{n}}.zero)
        Int{{n}}::MAX.to_big_i.to_i{{n}}.should eq(Int{{n}}::MAX)
        Int{{n}}::MIN.to_big_i.to_i{{n}}.should eq(Int{{n}}::MIN)
      end

      it "raises OverflowError" do
        expect_raises(OverflowError) { (Int{{n}}::MAX.to_big_i + 1).to_i{{n}} }
        expect_raises(OverflowError) { (Int{{n}}::MIN.to_big_i - 1).to_i{{n}} }
        expect_raises(OverflowError) { (1.to_big_i << {{n}}).to_i{{n}} }
        expect_raises(OverflowError) { (-1.to_big_i << {{n}}).to_i{{n}} }
      end
    end

    describe "#to_u{{n}}!" do
      it "converts to UInt{{n}}" do
        (0..{{n - 1}}).each do |i|
          (1.to_big_i << i).to_u{{n}}!.should eq(UInt{{n}}.new(1) << i)
        end

        UInt{{n}}::MAX.to_big_i.to_u{{n}}!.should eq(UInt{{n}}::MAX)
      end

      it "converts modulo (2 ** {{n}})" do
        (1.to_big_i << {{n}}).to_u{{n}}!.should eq(UInt{{n}}.new(0))
        (-1.to_big_i).to_u{{n}}!.should eq(UInt{{n}}::MAX)
        (-1.to_big_i << {{n}}).to_u{{n}}!.should eq(UInt{{n}}.new(0))
        (123.to_big_i - (1.to_big_i << {{n}})).to_u{{n}}!.should eq(UInt{{n}}.new(123))
        (123.to_big_i + (1.to_big_i << {{n}})).to_u{{n}}!.should eq(UInt{{n}}.new(123))
        (123.to_big_i - (1.to_big_i << {{n + 2}})).to_u{{n}}!.should eq(UInt{{n}}.new(123))
        (123.to_big_i + (1.to_big_i << {{n + 2}})).to_u{{n}}!.should eq(UInt{{n}}.new(123))
      end
    end

    describe "#to_i{{n}}!" do
      it "converts to Int{{n}}" do
        (0..126).each do |i|
          (1.to_big_i << i).to_i{{n}}!.should eq(Int{{n}}.new(1) << i)
          (-1.to_big_i << i).to_i{{n}}!.should eq(Int{{n}}.new(-1) << i)
        end

        Int{{n}}::MAX.to_big_i.to_i{{n}}!.should eq(Int{{n}}::MAX)
        Int{{n}}::MIN.to_big_i.to_i{{n}}!.should eq(Int{{n}}::MIN)
      end

      it "converts modulo (2 ** {{n}})" do
        (1.to_big_i << {{n - 1}}).to_i{{n}}!.should eq(Int{{n}}::MIN)
        (1.to_big_i << {{n}}).to_i{{n}}!.should eq(Int{{n}}.new(0))
        (-1.to_big_i << {{n}}).to_i{{n}}!.should eq(Int{{n}}.new(0))
        (123.to_big_i - (1.to_big_i << {{n}})).to_i{{n}}!.should eq(Int{{n}}.new(123))
        (123.to_big_i + (1.to_big_i << {{n}})).to_i{{n}}!.should eq(Int{{n}}.new(123))
        (123.to_big_i - (1.to_big_i << {{n + 2}})).to_i{{n}}!.should eq(Int{{n}}.new(123))
        (123.to_big_i + (1.to_big_i << {{n + 2}})).to_i{{n}}!.should eq(Int{{n}}.new(123))
      end
    end
  {% end %}

  it "does String#to_big_i" do
    "123456789123456789".to_big_i.should eq(BigInt.new("123456789123456789"))
    "abcabcabcabcabcabc".to_big_i(base: 16).should eq(BigInt.new("3169001976782853491388"))
  end

  it "does popcount" do
    5.to_big_i.popcount.should eq(2)
  end

  it "#trailing_zeros_count" do
    "00000000000000001000000000001000".to_big_i(base: 2).trailing_zeros_count.should eq(3)
  end

  it "#hash" do
    b1 = 5.to_big_i
    b2 = 5.to_big_i
    b3 = -6.to_big_i

    b1.hash.should eq(b2.hash)
    b1.hash.should_not eq(b3.hash)

    b3.hash.should eq((-6).hash)
  end

  it "clones" do
    x = 1.to_big_i
    x.clone.should eq(x)
  end

  describe "#humanize_bytes" do
    it { BigInt.new("1180591620717411303424").humanize_bytes.should eq("1.0ZiB") }
    it { BigInt.new("1208925819614629174706176").humanize_bytes.should eq("1.0YiB") }
  end

  it "has unsafe_shr (#8691)" do
    BigInt.new(8).unsafe_shr(1).should eq(4)
  end

  describe "#digits" do
    it "works for positive numbers or zero" do
      0.to_big_i.digits.should eq([0])
      1.to_big_i.digits.should eq([1])
      10.to_big_i.digits.should eq([0, 1])
      123.to_big_i.digits.should eq([3, 2, 1])
      123456789.to_big_i.digits.should eq([9, 8, 7, 6, 5, 4, 3, 2, 1])
    end

    it "works with a base" do
      123.to_big_i.digits(16).should eq([11, 7])
    end

    it "raises for invalid base" do
      [1, 0, -1].each do |base|
        expect_raises(ArgumentError, "Invalid base #{base}") do
          123.to_big_i.digits(base)
        end
      end
    end

    it "raises for negative numbers" do
      expect_raises(ArgumentError, "Can't request digits of negative number") do
        -123.to_big_i.digits
      end
    end
  end

  describe "#divisible_by?" do
    it { 0.to_big_i.divisible_by?(0).should be_true }
    it { 0.to_big_i.divisible_by?(1).should be_true }
    it { 0.to_big_i.divisible_by?(-1).should be_true }
    it { 0.to_big_i.divisible_by?(0.to_big_i).should be_true }
    it { 0.to_big_i.divisible_by?(1.to_big_i).should be_true }
    it { 0.to_big_i.divisible_by?((-1).to_big_i).should be_true }

    it { 135.to_big_i.divisible_by?(0).should be_false }
    it { 135.to_big_i.divisible_by?(1).should be_true }
    it { 135.to_big_i.divisible_by?(2).should be_false }
    it { 135.to_big_i.divisible_by?(3).should be_true }
    it { 135.to_big_i.divisible_by?(4).should be_false }
    it { 135.to_big_i.divisible_by?(5).should be_true }
    it { 135.to_big_i.divisible_by?(135).should be_true }
    it { 135.to_big_i.divisible_by?(270).should be_false }

    it { "100000000000000000000000000000000".to_big_i.divisible_by?("4294967296".to_big_i).should be_true }
    it { "100000000000000000000000000000000".to_big_i.divisible_by?("8589934592".to_big_i).should be_false }
    it { "100000000000000000000000000000000".to_big_i.divisible_by?("23283064365386962890625".to_big_i).should be_true }
    it { "100000000000000000000000000000000".to_big_i.divisible_by?("116415321826934814453125".to_big_i).should be_false }
  end
end

describe "BigInt Math" do
  it "sqrt" do
    Math.sqrt(BigInt.new("1" + "0"*48)).should eq(BigFloat.new("1" + "0"*24))
  end

  it "isqrt" do
    Math.isqrt(BigInt.new("1" + "0"*48)).should eq(BigInt.new("1" + "0"*24))
  end

  it "pw2ceil" do
    Math.pw2ceil("-100000000000000000000000000000000".to_big_i).should eq(1.to_big_i)
    Math.pw2ceil(-1234567.to_big_i).should eq(1.to_big_i)
    Math.pw2ceil(-1.to_big_i).should eq(1.to_big_i)
    Math.pw2ceil(0.to_big_i).should eq(1.to_big_i)
    Math.pw2ceil(1.to_big_i).should eq(1.to_big_i)
    Math.pw2ceil(2.to_big_i).should eq(2.to_big_i)
    Math.pw2ceil(3.to_big_i).should eq(4.to_big_i)
    Math.pw2ceil(4.to_big_i).should eq(4.to_big_i)
    Math.pw2ceil(5.to_big_i).should eq(8.to_big_i)
    Math.pw2ceil(32.to_big_i).should eq(32.to_big_i)
    Math.pw2ceil(33.to_big_i).should eq(64.to_big_i)
    Math.pw2ceil(64.to_big_i).should eq(64.to_big_i)
    Math.pw2ceil(2.to_big_i ** 12345 - 1).should eq(2.to_big_i ** 12345)
    Math.pw2ceil(2.to_big_i ** 12345).should eq(2.to_big_i ** 12345)
    Math.pw2ceil(2.to_big_i ** 12345 + 1).should eq(2.to_big_i ** 12346)
  end
end
