diff options
Diffstat (limited to 'spec/ruby/core/integer')
78 files changed, 3412 insertions, 499 deletions
diff --git a/spec/ruby/core/integer/abs_spec.rb b/spec/ruby/core/integer/abs_spec.rb new file mode 100644 index 0000000000..c40356db12 --- /dev/null +++ b/spec/ruby/core/integer/abs_spec.rb @@ -0,0 +1,6 @@ +require_relative '../../spec_helper' +require_relative 'shared/abs' + +describe "Integer#abs" do + it_behaves_like :integer_abs, :abs +end diff --git a/spec/ruby/core/integer/allbits_spec.rb b/spec/ruby/core/integer/allbits_spec.rb index a8ae19be15..edce4b15e7 100644 --- a/spec/ruby/core/integer/allbits_spec.rb +++ b/spec/ruby/core/integer/allbits_spec.rb @@ -1,39 +1,37 @@ -require File.expand_path('../../../spec_helper', __FILE__) +require_relative '../../spec_helper' -ruby_version_is '2.5' do - describe "Integer#allbits?" do - it "returns true iff all the bits of the argument are set in the receiver" do - 42.allbits?(42).should == true - 0b1010_1010.allbits?(0b1000_0010).should == true - 0b1010_1010.allbits?(0b1000_0001).should == false - 0b1000_0010.allbits?(0b1010_1010).should == false - (0b1010_1010 | bignum_value).allbits?(0b1000_0010 | bignum_value).should == true - (0b1010_1010 | bignum_value).allbits?(0b1000_0001 | bignum_value).should == false - (0b1000_0010 | bignum_value).allbits?(0b1010_1010 | bignum_value).should == false - end +describe "Integer#allbits?" do + it "returns true if and only if all the bits of the argument are set in the receiver" do + 42.allbits?(42).should == true + 0b1010_1010.allbits?(0b1000_0010).should == true + 0b1010_1010.allbits?(0b1000_0001).should == false + 0b1000_0010.allbits?(0b1010_1010).should == false + (0b1010_1010 | bignum_value).allbits?(0b1000_0010 | bignum_value).should == true + (0b1010_1010 | bignum_value).allbits?(0b1000_0001 | bignum_value).should == false + (0b1000_0010 | bignum_value).allbits?(0b1010_1010 | bignum_value).should == false + end - it "handles negative values using two's complement notation" do - (~0b1).allbits?(42).should == true - (-42).allbits?(-42).should == true - (~0b1010_1010).allbits?(~0b1110_1011).should == true - (~0b1010_1010).allbits?(~0b1000_0010).should == false - (~(0b1010_1010 | bignum_value)).allbits?(~(0b1110_1011 | bignum_value)).should == true - (~(0b1010_1010 | bignum_value)).allbits?(~(0b1000_0010 | bignum_value)).should == false - end + it "handles negative values using two's complement notation" do + (~0b1).allbits?(42).should == true + (-42).allbits?(-42).should == true + (~0b1010_1010).allbits?(~0b1110_1011).should == true + (~0b1010_1010).allbits?(~0b1000_0010).should == false + (~(0b1010_1010 | bignum_value)).allbits?(~(0b1110_1011 | bignum_value)).should == true + (~(0b1010_1010 | bignum_value)).allbits?(~(0b1000_0010 | bignum_value)).should == false + end - it "coerces the rhs using to_int" do - obj = mock("the int 0b10") - obj.should_receive(:to_int).and_return(0b10) - 0b110.allbits?(obj).should == true - end + it "coerces the rhs using to_int" do + obj = mock("the int 0b10") + obj.should_receive(:to_int).and_return(0b10) + 0b110.allbits?(obj).should == true + end - it "raises a TypeError when given a non-Integer" do - lambda { - (obj = mock('10')).should_receive(:coerce).any_number_of_times.and_return([42,10]) - 13.allbits?(obj) - }.should raise_error(TypeError) - lambda { 13.allbits?("10") }.should raise_error(TypeError) - lambda { 13.allbits?(:symbol) }.should raise_error(TypeError) - end + it "raises a TypeError when given a non-Integer" do + -> { + (obj = mock('10')).should_receive(:coerce).any_number_of_times.and_return([42,10]) + 13.allbits?(obj) + }.should raise_error(TypeError) + -> { 13.allbits?("10") }.should raise_error(TypeError) + -> { 13.allbits?(:symbol) }.should raise_error(TypeError) end end diff --git a/spec/ruby/core/integer/anybits_spec.rb b/spec/ruby/core/integer/anybits_spec.rb index e2fd009a5f..e0449074a2 100644 --- a/spec/ruby/core/integer/anybits_spec.rb +++ b/spec/ruby/core/integer/anybits_spec.rb @@ -1,38 +1,36 @@ -require File.expand_path('../../../spec_helper', __FILE__) +require_relative '../../spec_helper' -ruby_version_is '2.5' do - describe "Integer#anybits?" do - it "returns true iff all the bits of the argument are set in the receiver" do - 42.anybits?(42).should == true - 0b1010_1010.anybits?(0b1000_0010).should == true - 0b1010_1010.anybits?(0b1000_0001).should == true - 0b1000_0010.anybits?(0b0010_1100).should == false - different_bignum = (2 * bignum_value) & (~bignum_value) - (0b1010_1010 | different_bignum).anybits?(0b1000_0010 | bignum_value).should == true - (0b1010_1010 | different_bignum).anybits?(0b0010_1100 | bignum_value).should == true - (0b1000_0010 | different_bignum).anybits?(0b0010_1100 | bignum_value).should == false - end +describe "Integer#anybits?" do + it "returns true if and only if all the bits of the argument are set in the receiver" do + 42.anybits?(42).should == true + 0b1010_1010.anybits?(0b1000_0010).should == true + 0b1010_1010.anybits?(0b1000_0001).should == true + 0b1000_0010.anybits?(0b0010_1100).should == false + different_bignum = (2 * bignum_value) & (~bignum_value) + (0b1010_1010 | different_bignum).anybits?(0b1000_0010 | bignum_value).should == true + (0b1010_1010 | different_bignum).anybits?(0b0010_1100 | bignum_value).should == true + (0b1000_0010 | different_bignum).anybits?(0b0010_1100 | bignum_value).should == false + end - it "handles negative values using two's complement notation" do - (~42).anybits?(42).should == false - (-42).anybits?(-42).should == true - (~0b100).anybits?(~0b1).should == true - (~(0b100 | bignum_value)).anybits?(~(0b1 | bignum_value)).should == true - end + it "handles negative values using two's complement notation" do + (~42).anybits?(42).should == false + (-42).anybits?(-42).should == true + (~0b100).anybits?(~0b1).should == true + (~(0b100 | bignum_value)).anybits?(~(0b1 | bignum_value)).should == true + end - it "coerces the rhs using to_int" do - obj = mock("the int 0b10") - obj.should_receive(:to_int).and_return(0b10) - 0b110.anybits?(obj).should == true - end + it "coerces the rhs using to_int" do + obj = mock("the int 0b10") + obj.should_receive(:to_int).and_return(0b10) + 0b110.anybits?(obj).should == true + end - it "raises a TypeError when given a non-Integer" do - lambda { - (obj = mock('10')).should_receive(:coerce).any_number_of_times.and_return([42,10]) - 13.anybits?(obj) - }.should raise_error(TypeError) - lambda { 13.anybits?("10") }.should raise_error(TypeError) - lambda { 13.anybits?(:symbol) }.should raise_error(TypeError) - end + it "raises a TypeError when given a non-Integer" do + -> { + (obj = mock('10')).should_receive(:coerce).any_number_of_times.and_return([42,10]) + 13.anybits?(obj) + }.should raise_error(TypeError) + -> { 13.anybits?("10") }.should raise_error(TypeError) + -> { 13.anybits?(:symbol) }.should raise_error(TypeError) end end diff --git a/spec/ruby/core/integer/bit_and_spec.rb b/spec/ruby/core/integer/bit_and_spec.rb new file mode 100644 index 0000000000..e7face39ac --- /dev/null +++ b/spec/ruby/core/integer/bit_and_spec.rb @@ -0,0 +1,97 @@ +require_relative '../../spec_helper' + +describe "Integer#&" do + context "fixnum" do + it "returns self bitwise AND other" do + (256 & 16).should == 0 + (2010 & 5).should == 0 + (65535 & 1).should == 1 + (0xffff & bignum_value + 0xffff_ffff).should == 65535 + end + + it "returns self bitwise AND other when one operand is negative" do + ((1 << 33) & -1).should == (1 << 33) + (-1 & (1 << 33)).should == (1 << 33) + + ((-(1<<33)-1) & 5).should == 5 + (5 & (-(1<<33)-1)).should == 5 + end + + it "returns self bitwise AND other when both operands are negative" do + (-5 & -1).should == -5 + (-3 & -4).should == -4 + (-12 & -13).should == -16 + (-13 & -12).should == -16 + end + + it "returns self bitwise AND a bignum" do + (-1 & 2**64).should == 18446744073709551616 + end + + it "coerces the rhs and calls #coerce" do + obj = mock("fixnum bit and") + obj.should_receive(:coerce).with(6).and_return([6, 3]) + (6 & obj).should == 2 + end + + it "raises a TypeError when passed a Float" do + -> { (3 & 3.4) }.should raise_error(TypeError) + end + + it "raises a TypeError and does not call #to_int when defined on an object" do + obj = mock("fixnum bit and") + obj.should_not_receive(:to_int) + + -> { 3 & obj }.should raise_error(TypeError) + end + end + + context "bignum" do + before :each do + @bignum = bignum_value(5) + end + + it "returns self bitwise AND other" do + @bignum = bignum_value(5) + (@bignum & 3).should == 1 + (@bignum & 52).should == 4 + (@bignum & bignum_value(9921)).should == 18446744073709551617 + + ((2*bignum_value) & 1).should == 0 + ((2*bignum_value) & (2*bignum_value)).should == 36893488147419103232 + end + + it "returns self bitwise AND other when one operand is negative" do + ((2*bignum_value) & -1).should == (2*bignum_value) + ((4*bignum_value) & -1).should == (4*bignum_value) + (@bignum & -0xffffffffffffff5).should == 18446744073709551617 + (@bignum & -@bignum).should == 1 + (@bignum & -0x8000000000000000).should == 18446744073709551616 + end + + it "returns self bitwise AND other when both operands are negative" do + (-@bignum & -0x4000000000000005).should == -23058430092136939525 + (-@bignum & -@bignum).should == -18446744073709551621 + (-@bignum & -0x4000000000000000).should == -23058430092136939520 + end + + it "returns self bitwise AND other when both are negative and a multiple in bitsize of Fixnum::MIN" do + val = - ((1 << 93) - 1) + (val & val).should == val + + val = - ((1 << 126) - 1) + (val & val).should == val + end + + it "raises a TypeError when passed a Float" do + -> { (@bignum & 3.4) }.should raise_error(TypeError) + end + + it "raises a TypeError and does not call #to_int when defined on an object" do + obj = mock("bignum bit and") + obj.should_not_receive(:to_int) + + -> { @bignum & obj }.should raise_error(TypeError) + end + end +end diff --git a/spec/ruby/core/integer/bit_length_spec.rb b/spec/ruby/core/integer/bit_length_spec.rb new file mode 100644 index 0000000000..827007b7e0 --- /dev/null +++ b/spec/ruby/core/integer/bit_length_spec.rb @@ -0,0 +1,76 @@ +require_relative '../../spec_helper' + +describe "Integer#bit_length" do + context "fixnum" do + it "returns the position of the leftmost bit of a positive number" do + 0.bit_length.should == 0 + 1.bit_length.should == 1 + 2.bit_length.should == 2 + 3.bit_length.should == 2 + 4.bit_length.should == 3 + n = fixnum_max.bit_length + fixnum_max[n].should == 0 + fixnum_max[n - 1].should == 1 + + 0.bit_length.should == 0 + 1.bit_length.should == 1 + 0xff.bit_length.should == 8 + 0x100.bit_length.should == 9 + (2**12 - 1).bit_length.should == 12 + (2**12).bit_length.should == 13 + (2**12 + 1).bit_length.should == 13 + end + + it "returns the position of the leftmost 0 bit of a negative number" do + -1.bit_length.should == 0 + -2.bit_length.should == 1 + -3.bit_length.should == 2 + -4.bit_length.should == 2 + -5.bit_length.should == 3 + n = fixnum_min.bit_length + fixnum_min[n].should == 1 + fixnum_min[n - 1].should == 0 + + (-2**12 - 1).bit_length.should == 13 + (-2**12).bit_length.should == 12 + (-2**12 + 1).bit_length.should == 12 + -0x101.bit_length.should == 9 + -0x100.bit_length.should == 8 + -0xff.bit_length.should == 8 + -2.bit_length.should == 1 + -1.bit_length.should == 0 + end + end + + context "bignum" do + it "returns the position of the leftmost bit of a positive number" do + (2**1000-1).bit_length.should == 1000 + (2**1000).bit_length.should == 1001 + (2**1000+1).bit_length.should == 1001 + + (2**10000-1).bit_length.should == 10000 + (2**10000).bit_length.should == 10001 + (2**10000+1).bit_length.should == 10001 + + (1 << 100).bit_length.should == 101 + (1 << 100).succ.bit_length.should == 101 + (1 << 100).pred.bit_length.should == 100 + (1 << 10000).bit_length.should == 10001 + end + + it "returns the position of the leftmost 0 bit of a negative number" do + (-2**10000-1).bit_length.should == 10001 + (-2**10000).bit_length.should == 10000 + (-2**10000+1).bit_length.should == 10000 + + (-2**1000-1).bit_length.should == 1001 + (-2**1000).bit_length.should == 1000 + (-2**1000+1).bit_length.should == 1000 + + ((-1 << 100)-1).bit_length.should == 101 + ((-1 << 100)-1).succ.bit_length.should == 100 + ((-1 << 100)-1).pred.bit_length.should == 101 + ((-1 << 10000)-1).bit_length.should == 10001 + end + end +end diff --git a/spec/ruby/core/integer/bit_or_spec.rb b/spec/ruby/core/integer/bit_or_spec.rb new file mode 100644 index 0000000000..fdf8a191e5 --- /dev/null +++ b/spec/ruby/core/integer/bit_or_spec.rb @@ -0,0 +1,89 @@ +require_relative '../../spec_helper' + +describe "Integer#|" do + context "fixnum" do + it "returns self bitwise OR other" do + (1 | 0).should == 1 + (5 | 4).should == 5 + (5 | 6).should == 7 + (248 | 4096).should == 4344 + (0xffff | bignum_value + 0xf0f0).should == 0x1_0000_0000_0000_ffff + end + + it "returns self bitwise OR other when one operand is negative" do + ((1 << 33) | -1).should == -1 + (-1 | (1 << 33)).should == -1 + + ((-(1<<33)-1) | 5).should == -8589934593 + (5 | (-(1<<33)-1)).should == -8589934593 + end + + it "returns self bitwise OR other when both operands are negative" do + (-5 | -1).should == -1 + (-3 | -4).should == -3 + (-12 | -13).should == -9 + (-13 | -12).should == -9 + end + + it "returns self bitwise OR a bignum" do + (-1 | 2**64).should == -1 + end + + it "coerces the rhs and calls #coerce" do + obj = mock("fixnum bit or") + obj.should_receive(:coerce).with(6).and_return([6, 3]) + (6 | obj).should == 7 + end + + it "raises a TypeError when passed a Float" do + -> { (3 | 3.4) }.should raise_error(TypeError) + end + + it "raises a TypeError and does not call #to_int when defined on an object" do + obj = mock("integer bit or") + obj.should_not_receive(:to_int) + + -> { 3 | obj }.should raise_error(TypeError) + end + end + + context "bignum" do + before :each do + @bignum = bignum_value(11) + end + + it "returns self bitwise OR other" do + (@bignum | 2).should == 18446744073709551627 + (@bignum | 9).should == 18446744073709551627 + (@bignum | bignum_value).should == 18446744073709551627 + end + + it "returns self bitwise OR other when one operand is negative" do + (@bignum | -0x40000000000000000).should == -55340232221128654837 + (@bignum | -@bignum).should == -1 + (@bignum | -0x8000000000000000).should == -9223372036854775797 + end + + it "returns self bitwise OR other when both operands are negative" do + (-@bignum | -0x4000000000000005).should == -1 + (-@bignum | -@bignum).should == -18446744073709551627 + (-@bignum | -0x4000000000000000).should == -11 + end + + it "raises a TypeError when passed a Float" do + not_supported_on :opal do + -> { + bignum_value | bignum_value(0xffff).to_f + }.should raise_error(TypeError) + end + -> { @bignum | 9.9 }.should raise_error(TypeError) + end + + it "raises a TypeError and does not call #to_int when defined on an object" do + obj = mock("bignum bit or") + obj.should_not_receive(:to_int) + + -> { @bignum | obj }.should raise_error(TypeError) + end + end +end diff --git a/spec/ruby/core/integer/bit_xor_spec.rb b/spec/ruby/core/integer/bit_xor_spec.rb new file mode 100644 index 0000000000..1f46bc52f3 --- /dev/null +++ b/spec/ruby/core/integer/bit_xor_spec.rb @@ -0,0 +1,93 @@ +require_relative '../../spec_helper' + +describe "Integer#^" do + context "fixnum" do + it "returns self bitwise EXCLUSIVE OR other" do + (3 ^ 5).should == 6 + (-2 ^ -255).should == 255 + (5 ^ bignum_value + 0xffff_ffff).should == 0x1_0000_0000_ffff_fffa + end + + it "returns self bitwise XOR other when one operand is negative" do + ((1 << 33) ^ -1).should == -8589934593 + (-1 ^ (1 << 33)).should == -8589934593 + + ((-(1<<33)-1) ^ 5).should == -8589934598 + (5 ^ (-(1<<33)-1)).should == -8589934598 + end + + it "returns self bitwise XOR other when both operands are negative" do + (-5 ^ -1).should == 4 + (-3 ^ -4).should == 1 + (-12 ^ -13).should == 7 + (-13 ^ -12).should == 7 + end + + it "returns self bitwise EXCLUSIVE OR a bignum" do + (-1 ^ 2**64).should == -18446744073709551617 + end + + it "coerces the rhs and calls #coerce" do + obj = mock("fixnum bit xor") + obj.should_receive(:coerce).with(6).and_return([6, 3]) + (6 ^ obj).should == 5 + end + + it "raises a TypeError when passed a Float" do + -> { (3 ^ 3.4) }.should raise_error(TypeError) + end + + it "raises a TypeError and does not call #to_int when defined on an object" do + obj = mock("integer bit xor") + obj.should_not_receive(:to_int) + + -> { 3 ^ obj }.should raise_error(TypeError) + end + end + + context "bignum" do + before :each do + @bignum = bignum_value(18) + end + + it "returns self bitwise EXCLUSIVE OR other" do + (@bignum ^ 2).should == 18446744073709551632 + (@bignum ^ @bignum).should == 0 + (@bignum ^ 14).should == 18446744073709551644 + end + + it "returns self bitwise EXCLUSIVE OR other when one operand is negative" do + (@bignum ^ -0x40000000000000000).should == -55340232221128654830 + (@bignum ^ -@bignum).should == -4 + (@bignum ^ -0x8000000000000000).should == -27670116110564327406 + end + + it "returns self bitwise EXCLUSIVE OR other when both operands are negative" do + (-@bignum ^ -0x40000000000000000).should == 55340232221128654830 + (-@bignum ^ -@bignum).should == 0 + (-@bignum ^ -0x4000000000000000).should == 23058430092136939502 + end + + it "returns self bitwise EXCLUSIVE OR other when all bits are 1 and other value is negative" do + (9903520314283042199192993791 ^ -1).should == -9903520314283042199192993792 + (784637716923335095479473677900958302012794430558004314111 ^ -1).should == + -784637716923335095479473677900958302012794430558004314112 + end + + it "raises a TypeError when passed a Float" do + not_supported_on :opal do + -> { + bignum_value ^ bignum_value(0xffff).to_f + }.should raise_error(TypeError) + end + -> { @bignum ^ 14.5 }.should raise_error(TypeError) + end + + it "raises a TypeError and does not call #to_int when defined on an object" do + obj = mock("bignum bit xor") + obj.should_not_receive(:to_int) + + -> { @bignum ^ obj }.should raise_error(TypeError) + end + end +end diff --git a/spec/ruby/core/integer/case_compare_spec.rb b/spec/ruby/core/integer/case_compare_spec.rb new file mode 100644 index 0000000000..e5dde2c64a --- /dev/null +++ b/spec/ruby/core/integer/case_compare_spec.rb @@ -0,0 +1,6 @@ +require_relative '../../spec_helper' +require_relative 'shared/equal' + +describe "Integer#===" do + it_behaves_like :integer_equal, :=== +end diff --git a/spec/ruby/core/integer/ceil_spec.rb b/spec/ruby/core/integer/ceil_spec.rb index 31c56f378d..eb633fba78 100644 --- a/spec/ruby/core/integer/ceil_spec.rb +++ b/spec/ruby/core/integer/ceil_spec.rb @@ -1,21 +1,24 @@ -require File.expand_path('../../../spec_helper', __FILE__) -require File.expand_path('../shared/to_i', __FILE__) -require File.expand_path('../shared/integer_rounding', __FILE__) +require_relative '../../spec_helper' +require_relative 'shared/to_i' +require_relative 'shared/integer_rounding' +require_relative 'shared/integer_ceil_precision' describe "Integer#ceil" do - it_behaves_like(:integer_to_i, :ceil) - it_behaves_like(:integer_rounding_positive_precision, :ceil) + it_behaves_like :integer_to_i, :ceil + it_behaves_like :integer_rounding_positive_precision, :ceil - ruby_version_is "2.4" do - context "precision argument specified as part of the ceil method is negative" do - it "returns the smallest integer greater than self with at least precision.abs trailing zeros" do - 18.ceil(-1).should eql(20) - 18.ceil(-2).should eql(100) - 18.ceil(-3).should eql(1000) - -1832.ceil(-1).should eql(-1830) - -1832.ceil(-2).should eql(-1800) - -1832.ceil(-3).should eql(-1000) - end + context "with precision" do + it_behaves_like :integer_ceil_precision, :Integer + end + + context "precision argument specified as part of the ceil method is negative" do + it "returns the smallest integer greater than self with at least precision.abs trailing zeros" do + 18.ceil(-1).should eql(20) + 18.ceil(-2).should eql(100) + 18.ceil(-3).should eql(1000) + -1832.ceil(-1).should eql(-1830) + -1832.ceil(-2).should eql(-1800) + -1832.ceil(-3).should eql(-1000) end end end diff --git a/spec/ruby/core/integer/ceildiv_spec.rb b/spec/ruby/core/integer/ceildiv_spec.rb new file mode 100644 index 0000000000..c6e22a457d --- /dev/null +++ b/spec/ruby/core/integer/ceildiv_spec.rb @@ -0,0 +1,20 @@ +require_relative '../../spec_helper' + +describe "Integer#ceildiv" do + it "returns a quotient of division which is rounded up to the nearest integer" do + 0.ceildiv(3).should eql(0) + 1.ceildiv(3).should eql(1) + 3.ceildiv(3).should eql(1) + 4.ceildiv(3).should eql(2) + + 4.ceildiv(-3).should eql(-1) + -4.ceildiv(3).should eql(-1) + -4.ceildiv(-3).should eql(2) + + 3.ceildiv(1.2).should eql(3) + 3.ceildiv(6/5r).should eql(3) + + (10**100-11).ceildiv(10**99-1).should eql(10) + (10**100-9).ceildiv(10**99-1).should eql(11) + end +end diff --git a/spec/ruby/core/integer/chr_spec.rb b/spec/ruby/core/integer/chr_spec.rb index 50a678608e..39cafe2874 100644 --- a/spec/ruby/core/integer/chr_spec.rb +++ b/spec/ruby/core/integer/chr_spec.rb @@ -1,4 +1,4 @@ -require File.expand_path('../../../spec_helper', __FILE__) +require_relative '../../spec_helper' describe "Integer#chr without argument" do it "returns a String" do @@ -10,8 +10,12 @@ describe "Integer#chr without argument" do end it "raises a RangeError is self is less than 0" do - lambda { -1.chr }.should raise_error(RangeError) - lambda { -bignum_value.chr }.should raise_error(RangeError) + -> { -1.chr }.should raise_error(RangeError, /-1 out of char range/) + -> { (-bignum_value).chr }.should raise_error(RangeError, /bignum out of char range/) + end + + it "raises a RangeError if self is too large" do + -> { 2206368128.chr(Encoding::UTF_8) }.should raise_error(RangeError, /2206368128 out of char range/) end describe "when Encoding.default_internal is nil" do @@ -30,9 +34,9 @@ describe "Integer#chr without argument" do end describe "and self is between 128 and 255 (inclusive)" do - it "returns an ASCII-8BIT String" do + it "returns a binary String" do (128..255).each do |c| - c.chr.encoding.should == Encoding::ASCII_8BIT + c.chr.encoding.should == Encoding::BINARY end end @@ -44,8 +48,8 @@ describe "Integer#chr without argument" do end it "raises a RangeError is self is greater than 255" do - lambda { 256.chr }.should raise_error(RangeError) - lambda { bignum_value.chr }.should raise_error(RangeError) + -> { 256.chr }.should raise_error(RangeError, /256 out of char range/) + -> { bignum_value.chr }.should raise_error(RangeError, /bignum out of char range/) end end @@ -81,13 +85,13 @@ describe "Integer#chr without argument" do end describe "and self is between 128 and 255 (inclusive)" do - it "returns an ASCII-8BIT String" do + it "returns a binary String" do (128..255).each do |c| Encoding.default_internal = Encoding::UTF_8 - c.chr.encoding.should == Encoding::ASCII_8BIT + c.chr.encoding.should == Encoding::BINARY Encoding.default_internal = Encoding::SHIFT_JIS - c.chr.encoding.should == Encoding::ASCII_8BIT + c.chr.encoding.should == Encoding::BINARY end end @@ -126,14 +130,14 @@ describe "Integer#chr without argument" do # #5864 it "raises RangeError if self is invalid as a codepoint in the default internal encoding" do [ [0x0100, "US-ASCII"], - [0x0100, "ASCII-8BIT"], + [0x0100, "BINARY"], [0x0100, "EUC-JP"], [0xA1A0, "EUC-JP"], [0x0100, "ISO-8859-9"], [620, "TIS-620"] ].each do |integer, encoding_name| Encoding.default_internal = Encoding.find(encoding_name) - lambda { integer.chr }.should raise_error(RangeError) + -> { integer.chr }.should raise_error(RangeError, /(invalid codepoint|out of char range)/) end end end @@ -150,7 +154,7 @@ describe "Integer#chr with an encoding argument" do end it "accepts a String as an argument" do - lambda { 0xA4A2.chr('euc-jp') }.should_not raise_error + -> { 0xA4A2.chr('euc-jp') }.should_not raise_error end it "converts a String to an Encoding as Encoding.find does" do @@ -161,18 +165,22 @@ describe "Integer#chr with an encoding argument" do # http://redmine.ruby-lang.org/issues/4869 it "raises a RangeError is self is less than 0" do - lambda { -1.chr(Encoding::UTF_8) }.should raise_error(RangeError) - lambda { -bignum_value.chr(Encoding::EUC_JP) }.should raise_error(RangeError) + -> { -1.chr(Encoding::UTF_8) }.should raise_error(RangeError, /-1 out of char range/) + -> { (-bignum_value).chr(Encoding::EUC_JP) }.should raise_error(RangeError, /bignum out of char range/) + end + + it "raises a RangeError if self is too large" do + -> { 2206368128.chr(Encoding::UTF_8) }.should raise_error(RangeError, /2206368128 out of char range/) end it "returns a String with the specified encoding" do 0x0000.chr(Encoding::US_ASCII).encoding.should == Encoding::US_ASCII 0x007F.chr(Encoding::US_ASCII).encoding.should == Encoding::US_ASCII - 0x0000.chr(Encoding::ASCII_8BIT).encoding.should == Encoding::ASCII_8BIT - 0x007F.chr(Encoding::ASCII_8BIT).encoding.should == Encoding::ASCII_8BIT - 0x0080.chr(Encoding::ASCII_8BIT).encoding.should == Encoding::ASCII_8BIT - 0x00FF.chr(Encoding::ASCII_8BIT).encoding.should == Encoding::ASCII_8BIT + 0x0000.chr(Encoding::BINARY).encoding.should == Encoding::BINARY + 0x007F.chr(Encoding::BINARY).encoding.should == Encoding::BINARY + 0x0080.chr(Encoding::BINARY).encoding.should == Encoding::BINARY + 0x00FF.chr(Encoding::BINARY).encoding.should == Encoding::BINARY 0x0000.chr(Encoding::UTF_8).encoding.should == Encoding::UTF_8 0x007F.chr(Encoding::UTF_8).encoding.should == Encoding::UTF_8 @@ -193,10 +201,10 @@ describe "Integer#chr with an encoding argument" do 0x0000.chr(Encoding::US_ASCII).bytes.to_a.should == [0x00] 0x007F.chr(Encoding::US_ASCII).bytes.to_a.should == [0x7F] - 0x0000.chr(Encoding::ASCII_8BIT).bytes.to_a.should == [0x00] - 0x007F.chr(Encoding::ASCII_8BIT).bytes.to_a.should == [0x7F] - 0x0080.chr(Encoding::ASCII_8BIT).bytes.to_a.should == [0x80] - 0x00FF.chr(Encoding::ASCII_8BIT).bytes.to_a.should == [0xFF] + 0x0000.chr(Encoding::BINARY).bytes.to_a.should == [0x00] + 0x007F.chr(Encoding::BINARY).bytes.to_a.should == [0x7F] + 0x0080.chr(Encoding::BINARY).bytes.to_a.should == [0x80] + 0x00FF.chr(Encoding::BINARY).bytes.to_a.should == [0xFF] 0x0000.chr(Encoding::UTF_8).bytes.to_a.should == [0x00] 0x007F.chr(Encoding::UTF_8).bytes.to_a.should == [0x7F] @@ -215,25 +223,35 @@ describe "Integer#chr with an encoding argument" do # #5864 it "raises RangeError if self is invalid as a codepoint in the specified encoding" do - [ [0x80, "US-ASCII"], - [0x0100, "ASCII-8BIT"], - [0x0100, "EUC-JP"], - [0xA1A0, "EUC-JP"], - [0xA1, "EUC-JP"], - [0x80, "SHIFT_JIS"], - [0xE0, "SHIFT_JIS"], - [0x0100, "ISO-8859-9"], - [620, "TIS-620"], - [0xD800, "UTF-8"], - [0xDBFF, "UTF-8"], - [0xDC00, "UTF-8"], - [0xDFFF, "UTF-8"], - [0xD800, "UTF-16"], - [0xDBFF, "UTF-16"], - [0xDC00, "UTF-16"], - [0xDFFF, "UTF-16"], - ].each do |integer, encoding_name| - lambda { integer.chr(encoding_name) }.should raise_error(RangeError) - end + -> { 0x80.chr("US-ASCII") }.should raise_error(RangeError) + -> { 0x0100.chr("BINARY") }.should raise_error(RangeError) + -> { 0x0100.chr("EUC-JP") }.should raise_error(RangeError) + -> { 0xA1A0.chr("EUC-JP") }.should raise_error(RangeError) + -> { 0xA1.chr("EUC-JP") }.should raise_error(RangeError) + -> { 0x80.chr("SHIFT_JIS") }.should raise_error(RangeError) + -> { 0xE0.chr("SHIFT_JIS") }.should raise_error(RangeError) + -> { 0x0100.chr("ISO-8859-9") }.should raise_error(RangeError) + -> { 620.chr("TIS-620") }.should raise_error(RangeError) + # UTF-16 surrogate range + -> { 0xD800.chr("UTF-8") }.should raise_error(RangeError) + -> { 0xDBFF.chr("UTF-8") }.should raise_error(RangeError) + -> { 0xDC00.chr("UTF-8") }.should raise_error(RangeError) + -> { 0xDFFF.chr("UTF-8") }.should raise_error(RangeError) + # UTF-16 surrogate range + -> { 0xD800.chr("UTF-16") }.should raise_error(RangeError) + -> { 0xDBFF.chr("UTF-16") }.should raise_error(RangeError) + -> { 0xDC00.chr("UTF-16") }.should raise_error(RangeError) + -> { 0xDFFF.chr("UTF-16") }.should raise_error(RangeError) + end + + it 'returns a String encoding self interpreted as a codepoint in the CESU-8 encoding' do + # see more details here https://en.wikipedia.org/wiki/CESU-8 + # code points from U+0000 to U+FFFF is encoded in the same way as in UTF-8 + 0x0045.chr(Encoding::CESU_8).bytes.should == 0x0045.chr(Encoding::UTF_8).bytes + + # code points in range from U+10000 to U+10FFFF is CESU-8 data containing a 6-byte surrogate pair, + # which decodes to a 4-byte UTF-8 string + 0x10400.chr(Encoding::CESU_8).bytes.should != 0x10400.chr(Encoding::UTF_8).bytes + 0x10400.chr(Encoding::CESU_8).bytes.to_a.should == [0xED, 0xA0, 0x81, 0xED, 0xB0, 0x80] end end diff --git a/spec/ruby/core/integer/coerce_spec.rb b/spec/ruby/core/integer/coerce_spec.rb new file mode 100644 index 0000000000..1d6dc9713f --- /dev/null +++ b/spec/ruby/core/integer/coerce_spec.rb @@ -0,0 +1,91 @@ +require_relative '../../spec_helper' + +describe "Integer#coerce" do + context "fixnum" do + describe "when given a Fixnum" do + it "returns an array containing two Fixnums" do + 1.coerce(2).should == [2, 1] + 1.coerce(2).map { |i| i.class }.should == [Integer, Integer] + end + end + + describe "when given a String" do + it "raises an ArgumentError when trying to coerce with a non-number String" do + -> { 1.coerce(":)") }.should raise_error(ArgumentError) + end + + it "returns an array containing two Floats" do + 1.coerce("2").should == [2.0, 1.0] + 1.coerce("-2").should == [-2.0, 1.0] + end + end + + it "raises a TypeError when trying to coerce with nil" do + -> { 1.coerce(nil) }.should raise_error(TypeError) + end + + it "tries to convert the given Object into a Float by using #to_f" do + (obj = mock('1.0')).should_receive(:to_f).and_return(1.0) + 2.coerce(obj).should == [1.0, 2.0] + + (obj = mock('0')).should_receive(:to_f).and_return('0') + -> { 2.coerce(obj).should == [1.0, 2.0] }.should raise_error(TypeError) + end + + it "raises a TypeError when given an Object that does not respond to #to_f" do + -> { 1.coerce(mock('x')) }.should raise_error(TypeError) + -> { 1.coerce(1..4) }.should raise_error(TypeError) + -> { 1.coerce(:test) }.should raise_error(TypeError) + end + end + + context "bignum" do + it "coerces other to a Bignum and returns [other, self] when passed a Fixnum" do + a = bignum_value + ary = a.coerce(2) + + ary[0].should be_kind_of(Integer) + ary[1].should be_kind_of(Integer) + ary.should == [2, a] + end + + it "returns [other, self] when passed a Bignum" do + a = bignum_value + b = bignum_value + ary = a.coerce(b) + + ary[0].should be_kind_of(Integer) + ary[1].should be_kind_of(Integer) + ary.should == [b, a] + end + + it "raises a TypeError when not passed a Fixnum or Bignum" do + a = bignum_value + + -> { a.coerce(nil) }.should raise_error(TypeError) + -> { a.coerce(mock('str')) }.should raise_error(TypeError) + -> { a.coerce(1..4) }.should raise_error(TypeError) + -> { a.coerce(:test) }.should raise_error(TypeError) + end + + it "coerces both values to Floats and returns [other, self] when passed a Float" do + a = bignum_value + a.coerce(1.2).should == [1.2, a.to_f] + end + + it "coerces both values to Floats and returns [other, self] when passed a String" do + a = bignum_value + a.coerce("123").should == [123.0, a.to_f] + end + + it "calls #to_f to coerce other to a Float" do + b = mock("bignum value") + b.should_receive(:to_f).and_return(1.2) + + a = bignum_value + ary = a.coerce(b) + + ary.should == [1.2, a.to_f] + end + end +end diff --git a/spec/ruby/core/integer/comparison_spec.rb b/spec/ruby/core/integer/comparison_spec.rb new file mode 100644 index 0000000000..408c8e52ce --- /dev/null +++ b/spec/ruby/core/integer/comparison_spec.rb @@ -0,0 +1,177 @@ +require_relative '../../spec_helper' + +describe "Integer#<=>" do + context "fixnum" do + it "returns -1 when self is less than the given argument" do + (-3 <=> -1).should == -1 + (-5 <=> 10).should == -1 + (-5 <=> -4.5).should == -1 + end + + it "returns 0 when self is equal to the given argument" do + (0 <=> 0).should == 0 + (954 <=> 954).should == 0 + (954 <=> 954.0).should == 0 + end + + it "returns 1 when self is greater than the given argument" do + (496 <=> 5).should == 1 + (200 <=> 100).should == 1 + (51 <=> 50.5).should == 1 + end + + it "returns nil when the given argument is not an Integer" do + (3 <=> mock('x')).should == nil + (3 <=> 'test').should == nil + end + end + + context "bignum" do + describe "with a Fixnum" do + it "returns -1 when other is larger" do + (-bignum_value <=> 2).should == -1 + end + + it "returns 1 when other is smaller" do + (bignum_value <=> 2).should == 1 + end + end + + describe "with a Bignum" do + describe "when other is negative" do + it "returns -1 when self is negative and other is larger" do + (-bignum_value(42) <=> -bignum_value).should == -1 + end + + it "returns 0 when other is equal" do + (-bignum_value <=> -bignum_value).should == 0 + end + + it "returns 1 when self is negative and other is smaller" do + (-bignum_value <=> -bignum_value(94)).should == 1 + end + + it "returns 1 when self is positive" do + (bignum_value <=> -bignum_value).should == 1 + end + end + + describe "when other is positive" do + it "returns -1 when self is negative" do + (-bignum_value <=> bignum_value).should == -1 + end + + it "returns -1 when self is positive and other is larger" do + (bignum_value <=> bignum_value(38)).should == -1 + end + + it "returns 0 when other is equal" do + (bignum_value <=> bignum_value).should == 0 + end + + it "returns 1 when other is smaller" do + (bignum_value(56) <=> bignum_value).should == 1 + end + end + end + + describe "with a Float" do + describe "when other is negative" do + it "returns -1 when self is negative and other is larger" do + (-bignum_value(0xffff) <=> -bignum_value.to_f).should == -1 + end + + it "returns 0 when other is equal" do + (-bignum_value <=> -bignum_value.to_f).should == 0 + end + + it "returns 1 when self is negative and other is smaller" do + (-bignum_value <=> -bignum_value(0xffef).to_f).should == 1 + end + + it "returns 1 when self is positive" do + (bignum_value <=> -bignum_value.to_f).should == 1 + end + end + + describe "when other is positive" do + it "returns -1 when self is negative" do + (-bignum_value <=> bignum_value.to_f).should == -1 + end + + it "returns -1 when self is positive and other is larger" do + (bignum_value <=> bignum_value(0xfffe).to_f).should == -1 + end + + it "returns 0 when other is equal" do + (bignum_value <=> bignum_value.to_f).should == 0 + end + + it "returns 1 when other is smaller" do + (bignum_value(0xfeff) <=> bignum_value.to_f).should == 1 + end + end + end + + describe "with an Object" do + before :each do + @big = bignum_value + @num = mock("value for Integer#<=>") + end + + it "calls #coerce on other" do + @num.should_receive(:coerce).with(@big).and_return([@big.to_f, 2.5]) + @big <=> @num + end + + it "lets the exception go through if #coerce raises an exception" do + @num.should_receive(:coerce).with(@big).and_raise(RuntimeError.new("my error")) + -> { + @big <=> @num + }.should raise_error(RuntimeError, "my error") + end + + it "raises an exception if #coerce raises a non-StandardError exception" do + @num.should_receive(:coerce).with(@big).and_raise(Exception) + -> { @big <=> @num }.should raise_error(Exception) + end + + it "returns nil if #coerce does not return an Array" do + @num.should_receive(:coerce).with(@big).and_return(nil) + (@big <=> @num).should be_nil + end + + it "returns -1 if the coerced value is larger" do + @num.should_receive(:coerce).with(@big).and_return([@big, bignum_value(10)]) + (@big <=> @num).should == -1 + end + + it "returns 0 if the coerced value is equal" do + @num.should_receive(:coerce).with(@big).and_return([@big, bignum_value]) + (@big <=> @num).should == 0 + end + + it "returns 1 if the coerced value is smaller" do + @num.should_receive(:coerce).with(@big).and_return([@big, 22]) + (@big <=> @num).should == 1 + end + end + + # The tests below are taken from matz's revision 23730 for Ruby trunk + it "returns 1 when self is Infinity and other is a Bignum" do + (infinity_value <=> Float::MAX.to_i*2).should == 1 + end + + it "returns -1 when self is negative and other is Infinity" do + (-Float::MAX.to_i*2 <=> infinity_value).should == -1 + end + + it "returns 1 when self is negative and other is -Infinity" do + (-Float::MAX.to_i*2 <=> -infinity_value).should == 1 + end + + it "returns -1 when self is -Infinity and other is negative" do + (-infinity_value <=> -Float::MAX.to_i*2).should == -1 + end + end +end diff --git a/spec/ruby/core/integer/complement_spec.rb b/spec/ruby/core/integer/complement_spec.rb new file mode 100644 index 0000000000..baf5e6c457 --- /dev/null +++ b/spec/ruby/core/integer/complement_spec.rb @@ -0,0 +1,20 @@ +require_relative '../../spec_helper' + +describe "Integer#~" do + context "fixnum" do + it "returns self with each bit flipped" do + (~0).should == -1 + (~1221).should == -1222 + (~-2).should == 1 + (~-599).should == 598 + end + end + + context "bignum" do + it "returns self with each bit flipped" do + (~bignum_value(48)).should == -18446744073709551665 + (~(-bignum_value(21))).should == 18446744073709551636 + (~bignum_value(1)).should == -18446744073709551618 + end + end +end diff --git a/spec/ruby/core/integer/constants_spec.rb b/spec/ruby/core/integer/constants_spec.rb new file mode 100644 index 0000000000..937806c72f --- /dev/null +++ b/spec/ruby/core/integer/constants_spec.rb @@ -0,0 +1,13 @@ +require_relative '../../spec_helper' + +describe "Fixnum" do + it "is no longer defined" do + Object.should_not.const_defined?(:Fixnum) + end +end + +describe "Bignum" do + it "is no longer defined" do + Object.should_not.const_defined?(:Bignum) + end +end diff --git a/spec/ruby/core/integer/denominator_spec.rb b/spec/ruby/core/integer/denominator_spec.rb index 6d080ac81f..c1477d0757 100644 --- a/spec/ruby/core/integer/denominator_spec.rb +++ b/spec/ruby/core/integer/denominator_spec.rb @@ -1,4 +1,4 @@ -require File.expand_path('../../../spec_helper', __FILE__) +require_relative '../../spec_helper' describe "Integer#denominator" do # The Numeric child classes override this method, so their behaviour is diff --git a/spec/ruby/core/integer/digits_spec.rb b/spec/ruby/core/integer/digits_spec.rb index 3546a654eb..3d0a64c25f 100644 --- a/spec/ruby/core/integer/digits_spec.rb +++ b/spec/ruby/core/integer/digits_spec.rb @@ -1,34 +1,41 @@ -require File.expand_path('../../../spec_helper', __FILE__) - -ruby_version_is "2.4" do - describe "Integer#digits" do - it "returns an array of place values in base-10 by default" do - 12345.digits.should == [5,4,3,2,1] - end - - it "returns digits by place value of a given radix" do - 12345.digits(7).should == [4,6,6,0,5] - end - - it "converts the radix with #to_int" do - 12345.digits(mock_int(2)).should == [1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1] - end - - it "returns [0] when called on 0, regardless of base" do - 0.digits.should == [0] - 0.digits(7).should == [0] - end - - it "raises ArgumentError when calling with a radix less than 2" do - lambda { 12345.digits(1) }.should raise_error(ArgumentError) - end - - it "raises ArgumentError when calling with a negative radix" do - lambda { 12345.digits(-2) }.should raise_error(ArgumentError) - end - - it "raises Math::DomainError when calling digits on a negative number" do - lambda { -12345.digits(7) }.should raise_error(Math::DomainError) - end +require_relative '../../spec_helper' + +describe "Integer#digits" do + it "returns an array of place values in base-10 by default" do + 12345.digits.should == [5,4,3,2,1] + end + + it "returns digits by place value of a given radix" do + 12345.digits(7).should == [4,6,6,0,5] + end + + it "converts the radix with #to_int" do + 12345.digits(mock_int(2)).should == [1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1] + end + + it "returns [0] when called on 0, regardless of base" do + 0.digits.should == [0] + 0.digits(7).should == [0] + end + + it "raises ArgumentError when calling with a radix less than 2" do + -> { 12345.digits(1) }.should raise_error(ArgumentError) + end + + it "raises ArgumentError when calling with a negative radix" do + -> { 12345.digits(-2) }.should raise_error(ArgumentError) + end + + it "raises Math::DomainError when calling digits on a negative number" do + -> { -12345.digits(7) }.should raise_error(Math::DomainError) + end + + it "returns integer values > 9 when base is above 10" do + 1234.digits(16).should == [2, 13, 4] + end + + it "can be used with base > 37" do + 1234.digits(100).should == [34, 12] + 980099.digits(100).should == [99, 0, 98] end end diff --git a/spec/ruby/core/integer/div_spec.rb b/spec/ruby/core/integer/div_spec.rb new file mode 100644 index 0000000000..2eb9c0623b --- /dev/null +++ b/spec/ruby/core/integer/div_spec.rb @@ -0,0 +1,154 @@ +require_relative '../../spec_helper' + +describe "Integer#div" do + context "fixnum" do + it "returns self divided by the given argument as an Integer" do + 2.div(2).should == 1 + 1.div(2).should == 0 + 5.div(2).should == 2 + end + + it "rounds towards -inf" do + 8192.div(10).should == 819 + 8192.div(-10).should == -820 + (-8192).div(10).should == -820 + (-8192).div(-10).should == 819 + end + + it "means (x / y).floor" do + 5.div(2).should == (5 / 2).floor + 5.div(2.0).should == (5 / 2.0).floor + 5.div(-2).should == (5 / -2).floor + + 5.div(100).should == (5 / 100).floor + 5.div(100.0).should == (5 / 100.0).floor + 5.div(-100).should == (5 / -100).floor + end + + it "calls #coerce and #div if argument responds to #coerce" do + x = mock("x") + y = mock("y") + result = mock("result") + + y.should_receive(:coerce).and_return([x, y]) + x.should_receive(:div).with(y).and_return(result) + + 10.div(y).should == result + end + + it "coerces self and the given argument to Floats and returns self divided by other as Integer" do + 1.div(0.2).should == 5 + 1.div(0.16).should == 6 + 1.div(0.169).should == 5 + -1.div(50.4).should == -1 + 1.div(bignum_value).should == 0 + 1.div(Rational(1, 5)).should == 5 + end + + it "raises a ZeroDivisionError when the given argument is 0 and a Float" do + -> { 0.div(0.0) }.should raise_error(ZeroDivisionError) + -> { 10.div(0.0) }.should raise_error(ZeroDivisionError) + -> { -10.div(0.0) }.should raise_error(ZeroDivisionError) + end + + it "raises a ZeroDivisionError when the given argument is 0 and not a Float" do + -> { 13.div(0) }.should raise_error(ZeroDivisionError) + -> { 13.div(-0) }.should raise_error(ZeroDivisionError) + end + + it "raises a TypeError when given a non-numeric argument" do + -> { 13.div(mock('10')) }.should raise_error(TypeError) + -> { 5.div("2") }.should raise_error(TypeError) + -> { 5.div(:"2") }.should raise_error(TypeError) + -> { 5.div([]) }.should raise_error(TypeError) + end + end + + context "bignum" do + before :each do + @bignum = bignum_value(88) + end + + it "returns self divided by other" do + @bignum.div(4).should == 4611686018427387926 + @bignum.div(Rational(4, 1)).should == 4611686018427387926 + @bignum.div(bignum_value(2)).should == 1 + + (-(10**50)).div(-(10**40 + 1)).should == 9999999999 + (10**50).div(10**40 + 1).should == 9999999999 + + (-10**50).div(10**40 + 1).should == -10000000000 + (10**50).div(-(10**40 + 1)).should == -10000000000 + end + + it "handles fixnum_min / -1" do + (fixnum_min / -1).should == -fixnum_min + (fixnum_min / -1).should > 0 + + int_min = -2147483648 + (int_min / -1).should == 2147483648 + end + + it "calls #coerce and #div if argument responds to #coerce" do + x = mock("x") + y = mock("y") + result = mock("result") + + y.should_receive(:coerce).and_return([x, y]) + x.should_receive(:div).with(y).and_return(result) + + @bignum.div(y).should == result + end + + it "means (x / y).floor" do + @bignum.div(2).should == (@bignum / 2).floor + @bignum.div(-2).should == (@bignum / -2).floor + + @bignum.div(@bignum+1).should == (@bignum / (@bignum+1)).floor + @bignum.div(-(@bignum+1)).should == (@bignum / -(@bignum+1)).floor + + @bignum.div(2.0).should == (@bignum / 2.0).floor + @bignum.div(100.0).should == (@bignum / 100.0).floor + end + + it "looses precision if passed Float argument" do + @bignum.div(1).should_not == @bignum.div(1.0) + @bignum.div(4).should_not == @bignum.div(4.0) + @bignum.div(21).should_not == @bignum.div(21.0) + end + + it "raises a TypeError when given a non-numeric" do + -> { @bignum.div(mock("10")) }.should raise_error(TypeError) + -> { @bignum.div("2") }.should raise_error(TypeError) + -> { @bignum.div(:symbol) }.should raise_error(TypeError) + end + + it "returns a result of integer division of self by a float argument" do + @bignum.div(4294967295.5).should eql(4294967296) + not_supported_on :opal do + @bignum.div(4294967295.0).should eql(4294967297) + @bignum.div(bignum_value(88).to_f).should eql(1) + @bignum.div((-bignum_value(88)).to_f).should eql(-1) + end + end + + # #5490 + it "raises ZeroDivisionError if the argument is 0 and is a Float" do + -> { @bignum.div(0.0) }.should raise_error(ZeroDivisionError) + -> { @bignum.div(-0.0) }.should raise_error(ZeroDivisionError) + end + + it "raises ZeroDivisionError if the argument is 0 and is not a Float" do + -> { @bignum.div(0) }.should raise_error(ZeroDivisionError) + -> { @bignum.div(-0) }.should raise_error(ZeroDivisionError) + end + end + + context "rational" do + it "returns self divided by the given argument as an Integer" do + 2.div(6/5r).should == 1 + 1.div(6/5r).should == 0 + 5.div(6/5r).should == 4 + end + end +end diff --git a/spec/ruby/core/integer/divide_spec.rb b/spec/ruby/core/integer/divide_spec.rb index 6c81d5474f..0d5e16e986 100644 --- a/spec/ruby/core/integer/divide_spec.rb +++ b/spec/ruby/core/integer/divide_spec.rb @@ -1,7 +1,126 @@ -require File.expand_path('../../../spec_helper', __FILE__) -require File.expand_path('../shared/arithmetic_exception_in_coerce', __FILE__) +require_relative '../../spec_helper' +require_relative 'shared/arithmetic_coerce' describe "Integer#/" do - it_behaves_like :integer_arithmetic_exception_in_coerce, :/ -end + it_behaves_like :integer_arithmetic_coerce_not_rescue, :/ + + context "fixnum" do + it "returns self divided by the given argument" do + (2 / 2).should == 1 + (3 / 2).should == 1 + end + + it "supports dividing negative numbers" do + (-1 / 10).should == -1 + (-1 / 10**10).should == -1 + (-1 / 10**20).should == -1 + end + + it "preservers sign correctly" do + (4 / 3).should == 1 + (4 / -3).should == -2 + (-4 / 3).should == -2 + (-4 / -3).should == 1 + (0 / -3).should == 0 + (0 / 3).should == 0 + end + + it "returns result the same class as the argument" do + (3 / 2).should == 1 + (3 / 2.0).should == 1.5 + (3 / Rational(2, 1)).should == Rational(3, 2) + end + + it "raises a ZeroDivisionError if the given argument is zero and not a Float" do + -> { 1 / 0 }.should raise_error(ZeroDivisionError) + end + + it "does NOT raise ZeroDivisionError if the given argument is zero and is a Float" do + (1 / 0.0).to_s.should == 'Infinity' + (-1 / 0.0).to_s.should == '-Infinity' + end + + it "coerces fixnum and return self divided by other" do + (-1 / 50.4).should be_close(-0.0198412698412698, TOLERANCE) + (1 / bignum_value).should == 0 + end + + it "raises a TypeError when given a non-Integer" do + -> { 13 / mock('10') }.should raise_error(TypeError) + -> { 13 / "10" }.should raise_error(TypeError) + -> { 13 / :symbol }.should raise_error(TypeError) + end + end + + context "bignum" do + before :each do + @bignum = bignum_value(88) + end + + it "returns self divided by other" do + (@bignum / 4).should == 4611686018427387926 + + (@bignum / bignum_value(2)).should == 1 + (-(10**50) / -(10**40 + 1)).should == 9999999999 + ((10**50) / (10**40 + 1)).should == 9999999999 + + ((-10**50) / (10**40 + 1)).should == -10000000000 + ((10**50) / -(10**40 + 1)).should == -10000000000 + end + + it "preservers sign correctly" do + (4 / bignum_value).should == 0 + (4 / -bignum_value).should == -1 + (-4 / bignum_value).should == -1 + (-4 / -bignum_value).should == 0 + (0 / bignum_value).should == 0 + (0 / -bignum_value).should == 0 + end + + it "returns self divided by Float" do + not_supported_on :opal do + (bignum_value(88) / 4294967295.0).should be_close(4294967297.0, TOLERANCE) + end + (bignum_value(88) / 4294967295.5).should be_close(4294967296.5, TOLERANCE) + end + + it "returns result the same class as the argument" do + (@bignum / 4).should == 4611686018427387926 + (@bignum / 4.0).should be_close(4611686018427387926, TOLERANCE) + (@bignum / Rational(4, 1)).should == Rational(4611686018427387926, 1) + end + + it "does NOT raise ZeroDivisionError if other is zero and is a Float" do + (bignum_value / 0.0).to_s.should == 'Infinity' + (bignum_value / -0.0).to_s.should == '-Infinity' + end + + it "raises a ZeroDivisionError if other is zero and not a Float" do + -> { @bignum / 0 }.should raise_error(ZeroDivisionError) + end + + it "raises a TypeError when given a non-numeric" do + -> { @bignum / mock('10') }.should raise_error(TypeError) + -> { @bignum / "2" }.should raise_error(TypeError) + -> { @bignum / :symbol }.should raise_error(TypeError) + end + end + + it "coerces the RHS and calls #coerce" do + obj = mock("integer plus") + obj.should_receive(:coerce).with(6).and_return([6, 3]) + (6 / obj).should == 2 + end + + it "coerces the RHS and calls #coerce even if it's private" do + obj = Object.new + class << obj + private def coerce(n) + [n, 3] + end + end + + (6 / obj).should == 2 + end +end diff --git a/spec/ruby/core/integer/divmod_spec.rb b/spec/ruby/core/integer/divmod_spec.rb new file mode 100644 index 0000000000..7a489e9344 --- /dev/null +++ b/spec/ruby/core/integer/divmod_spec.rb @@ -0,0 +1,117 @@ +require_relative '../../spec_helper' + +describe "Integer#divmod" do + context "fixnum" do + it "returns an Array containing quotient and modulus obtained from dividing self by the given argument" do + 13.divmod(4).should == [3, 1] + 4.divmod(13).should == [0, 4] + + 13.divmod(4.0).should == [3, 1] + 4.divmod(13.0).should == [0, 4] + + 1.divmod(2.0).should == [0, 1.0] + 200.divmod(bignum_value).should == [0, 200] + end + + it "raises a ZeroDivisionError when the given argument is 0" do + -> { 13.divmod(0) }.should raise_error(ZeroDivisionError) + -> { 0.divmod(0) }.should raise_error(ZeroDivisionError) + -> { -10.divmod(0) }.should raise_error(ZeroDivisionError) + end + + it "raises a ZeroDivisionError when the given argument is 0 and a Float" do + -> { 0.divmod(0.0) }.should raise_error(ZeroDivisionError) + -> { 10.divmod(0.0) }.should raise_error(ZeroDivisionError) + -> { -10.divmod(0.0) }.should raise_error(ZeroDivisionError) + end + + it "raises a TypeError when given a non-Integer" do + -> { + (obj = mock('10')).should_receive(:to_int).any_number_of_times.and_return(10) + 13.divmod(obj) + }.should raise_error(TypeError) + -> { 13.divmod("10") }.should raise_error(TypeError) + -> { 13.divmod(:symbol) }.should raise_error(TypeError) + end + end + + context "bignum" do + before :each do + @bignum = bignum_value(55) + end + + # Based on MRI's test/test_integer.rb (test_divmod), + # MRI maintains the following property: + # if q, r = a.divmod(b) ==> + # assert(0 < b ? (0 <= r && r < b) : (b < r && r <= 0)) + # So, r is always between 0 and b. + it "returns an Array containing quotient and modulus obtained from dividing self by the given argument" do + @bignum.divmod(4).should == [4611686018427387917, 3] + @bignum.divmod(13).should == [1418980313362273205, 6] + + @bignum.divmod(4.5).should == [4099276460824344576, 2.5] + + not_supported_on :opal do + @bignum.divmod(4.0).should == [4611686018427387904, 0.0] + @bignum.divmod(13.0).should == [1418980313362273280, 3.0] + + @bignum.divmod(2.0).should == [9223372036854775808, 0.0] + end + + @bignum.divmod(bignum_value).should == [1, 55] + + (-(10**50)).divmod(-(10**40 + 1)).should == [9999999999, -9999999999999999999999999999990000000001] + (10**50).divmod(10**40 + 1).should == [9999999999, 9999999999999999999999999999990000000001] + + (-10**50).divmod(10**40 + 1).should == [-10000000000, 10000000000] + (10**50).divmod(-(10**40 + 1)).should == [-10000000000, -10000000000] + end + + describe "with q = floor(x/y), a = q*b + r," do + it "returns [q,r] when a < 0, b > 0 and |a| < b" do + a = -@bignum + 1 + b = @bignum + a.divmod(b).should == [-1, 1] + end + + it "returns [q,r] when a > 0, b < 0 and a > |b|" do + b = -@bignum + 1 + a = @bignum + a.divmod(b).should == [-2, -@bignum + 2] + end + + it "returns [q,r] when a > 0, b < 0 and a < |b|" do + a = @bignum - 1 + b = -@bignum + a.divmod(b).should == [-1, -1] + end + + it "returns [q,r] when a < 0, b < 0 and |a| < |b|" do + a = -@bignum + 1 + b = -@bignum + a.divmod(b).should == [0, -@bignum + 1] + end + end + + it "raises a ZeroDivisionError when the given argument is 0" do + -> { @bignum.divmod(0) }.should raise_error(ZeroDivisionError) + -> { (-@bignum).divmod(0) }.should raise_error(ZeroDivisionError) + end + + # Behaviour established as correct in r23953 + it "raises a FloatDomainError if other is NaN" do + -> { @bignum.divmod(nan_value) }.should raise_error(FloatDomainError) + end + + it "raises a ZeroDivisionError when the given argument is 0 and a Float" do + -> { @bignum.divmod(0.0) }.should raise_error(ZeroDivisionError) + -> { (-@bignum).divmod(0.0) }.should raise_error(ZeroDivisionError) + end + + it "raises a TypeError when the given argument is not an Integer" do + -> { @bignum.divmod(mock('10')) }.should raise_error(TypeError) + -> { @bignum.divmod("10") }.should raise_error(TypeError) + -> { @bignum.divmod(:symbol) }.should raise_error(TypeError) + end + end +end diff --git a/spec/ruby/core/integer/downto_spec.rb b/spec/ruby/core/integer/downto_spec.rb index 6b7b353760..987704b02e 100644 --- a/spec/ruby/core/integer/downto_spec.rb +++ b/spec/ruby/core/integer/downto_spec.rb @@ -1,6 +1,6 @@ -require File.expand_path('../../../spec_helper', __FILE__) +require_relative '../../spec_helper' -describe "Integer#downto [stop] when self and stop are Fixnums" do +describe "Integer#downto [stop] when self and stop are Integers" do it "does not yield when stop is greater than self" do result = [] 5.downto(6) { |x| result << x } @@ -27,8 +27,8 @@ describe "Integer#downto [stop] when self and stop are Fixnums" do end it "raises an ArgumentError for invalid endpoints" do - lambda {1.downto("A") {|x| p x } }.should raise_error(ArgumentError) - lambda {1.downto(nil) {|x| p x } }.should raise_error(ArgumentError) + -> {1.downto("A") {|x| p x } }.should raise_error(ArgumentError) + -> {1.downto(nil) {|x| p x } }.should raise_error(ArgumentError) end describe "when no block is given" do @@ -45,9 +45,9 @@ describe "Integer#downto [stop] when self and stop are Fixnums" do describe "size" do it "raises an ArgumentError for invalid endpoints" do enum = 1.downto("A") - lambda { enum.size }.should raise_error(ArgumentError) + -> { enum.size }.should raise_error(ArgumentError) enum = 1.downto(nil) - lambda { enum.size }.should raise_error(ArgumentError) + -> { enum.size }.should raise_error(ArgumentError) end it "returns self - stop + 1" do diff --git a/spec/ruby/core/integer/dup_spec.rb b/spec/ruby/core/integer/dup_spec.rb index f46bdf89bd..7f4d512465 100644 --- a/spec/ruby/core/integer/dup_spec.rb +++ b/spec/ruby/core/integer/dup_spec.rb @@ -1,10 +1,13 @@ -require File.expand_path('../../../spec_helper', __FILE__) +require_relative '../../spec_helper' -ruby_version_is '2.4' do - describe "Integer#dup" do - it "returns self" do - int = 2 - int.dup.should equal(int) - end +describe "Integer#dup" do + it "returns self for small integers" do + integer = 1_000 + integer.dup.should equal(integer) + end + + it "returns self for large integers" do + integer = 4_611_686_018_427_387_905 + integer.dup.should equal(integer) end end diff --git a/spec/ruby/core/integer/element_reference_spec.rb b/spec/ruby/core/integer/element_reference_spec.rb new file mode 100644 index 0000000000..cb7e0dc9b0 --- /dev/null +++ b/spec/ruby/core/integer/element_reference_spec.rb @@ -0,0 +1,188 @@ +require_relative '../../spec_helper' + +describe "Integer#[]" do + context "fixnum" do + it "behaves like (n >> b) & 1" do + 0b101[1].should == 0 + 0b101[2].should == 1 + end + + it "returns 1 if the nth bit is set" do + 15[1].should == 1 + end + + it "returns 1 if the nth bit is set (in two's-complement representation)" do + (-1)[1].should == 1 + end + + it "returns 0 if the nth bit is not set" do + 8[2].should == 0 + end + + it "returns 0 if the nth bit is not set (in two's-complement representation)" do + (-2)[0].should == 0 + end + + it "returns 0 if the nth bit is greater than the most significant bit" do + 2[3].should == 0 + end + + it "returns 1 if self is negative and the nth bit is greater than the most significant bit" do + (-1)[3].should == 1 + end + + it "returns 0 when passed a negative argument" do + 3[-1].should == 0 + (-1)[-1].should == 0 + end + + it "calls #to_int to convert the argument to an Integer and returns 1 if the nth bit is set" do + obj = mock('1') + obj.should_receive(:to_int).and_return(1) + + 2[obj].should == 1 + end + + it "calls #to_int to convert the argument to an Integer and returns 0 if the nth bit is set" do + obj = mock('0') + obj.should_receive(:to_int).and_return(0) + + 2[obj].should == 0 + end + + it "accepts a Float argument and returns 0 if the bit at the truncated value is not set" do + 13[1.3].should == 0 + end + + it "accepts a Float argument and returns 1 if the bit at the truncated value is set" do + 13[2.1].should == 1 + end + + it "raises a TypeError when passed a String" do + -> { 3["3"] }.should raise_error(TypeError) + end + + it "raises a TypeError when #to_int does not return an Integer" do + obj = mock('asdf') + obj.should_receive(:to_int).and_return("asdf") + -> { 3[obj] }.should raise_error(TypeError) + end + + it "calls #to_int to coerce a String to a Bignum and returns 0" do + obj = mock('bignum value') + obj.should_receive(:to_int).and_return(bignum_value) + + 3[obj].should == 0 + end + + it "returns 0 when passed a Float in the range of a Bignum" do + 3[bignum_value.to_f].should == 0 + end + + context "when index and length passed" do + it "returns specified number of bits from specified position" do + 0b101001101[2, 4].should == 0b0011 + 0b101001101[2, 5].should == 0b10011 + 0b101001101[2, 7].should == 0b1010011 + end + + it "ensures n[i, len] equals to (n >> i) & ((1 << len) - 1)" do + n = 0b101001101; i = 2; len = 4 + n[i, len].should == (n >> i) & ((1 << len) - 1) + end + + it "moves start position to the most significant bits when negative index passed" do + 0b000001[-1, 4].should == 0b10 + 0b000001[-2, 4].should == 0b100 + 0b000001[-3, 4].should == 0b1000 + end + + it "ignores negative length" do + 0b101001101[1, -1].should == 0b10100110 + 0b101001101[2, -1].should == 0b1010011 + 0b101001101[3, -1].should == 0b101001 + + 0b101001101[3, -5].should == 0b101001 + 0b101001101[3, -15].should == 0b101001 + 0b101001101[3, -125].should == 0b101001 + end + end + + context "when range passed" do + it "returns bits specified by range" do + 0b101001101[2..5].should == 0b0011 + 0b101001101[2..6].should == 0b10011 + 0b101001101[2..8].should == 0b1010011 + end + + it "ensures n[i..j] equals to (n >> i) & ((1 << (j - i + 1)) - 1)" do + n = 0b101001101; i = 2; j = 5 + n[i..j].should == (n >> i) & ((1 << (j - i + 1)) - 1) + end + + it "ensures n[i..] equals to (n >> i)" do + eval("0b101001101[3..]").should == 0b101001101 >> 3 + end + + it "moves lower boundary to the most significant bits when negative value passed" do + 0b000001[-1, 4].should == 0b10 + 0b000001[-2, 4].should == 0b100 + 0b000001[-3, 4].should == 0b1000 + end + + it "ignores upper boundary smaller than lower boundary" do + 0b101001101[4..1].should == 0b10100 + 0b101001101[4..2].should == 0b10100 + 0b101001101[-4..-5].should == 0b1010011010000 + end + + it "raises FloatDomainError if any boundary is infinity" do + -> { 0x0001[3..Float::INFINITY] }.should raise_error(FloatDomainError, /Infinity/) + -> { 0x0001[-Float::INFINITY..3] }.should raise_error(FloatDomainError, /-Infinity/) + end + + context "when passed (..i)" do + it "returns 0 if all i bits equal 0" do + eval("0b10000[..1]").should == 0 + eval("0b10000[..2]").should == 0 + eval("0b10000[..3]").should == 0 + end + + it "raises ArgumentError if any of i bit equals 1" do + -> { + eval("0b111110[..3]") + }.should raise_error(ArgumentError, /The beginless range for Integer#\[\] results in infinity/) + end + end + end + end + + context "bignum" do + before :each do + @bignum = bignum_value(4996) + end + + it "returns the nth bit in the binary representation of self" do + @bignum[2].should == 1 + @bignum[9.2].should == 1 + @bignum[21].should == 0 + @bignum[0xffffffff].should == 0 + @bignum[-0xffffffff].should == 0 + end + + it "tries to convert the given argument to an Integer using #to_int" do + @bignum[1.3].should == @bignum[1] + + (obj = mock('2')).should_receive(:to_int).at_least(1).and_return(2) + @bignum[obj].should == 1 + end + + it "raises a TypeError when the given argument can't be converted to Integer" do + obj = mock('asdf') + -> { @bignum[obj] }.should raise_error(TypeError) + + obj.should_receive(:to_int).and_return("asdf") + -> { @bignum[obj] }.should raise_error(TypeError) + end + end +end diff --git a/spec/ruby/core/integer/equal_value_spec.rb b/spec/ruby/core/integer/equal_value_spec.rb new file mode 100644 index 0000000000..67a73713af --- /dev/null +++ b/spec/ruby/core/integer/equal_value_spec.rb @@ -0,0 +1,6 @@ +require_relative '../../spec_helper' +require_relative 'shared/equal' + +describe "Integer#==" do + it_behaves_like :integer_equal, :== +end diff --git a/spec/ruby/core/integer/even_spec.rb b/spec/ruby/core/integer/even_spec.rb index c14cf84947..a314cc6b19 100644 --- a/spec/ruby/core/integer/even_spec.rb +++ b/spec/ruby/core/integer/even_spec.rb @@ -1,20 +1,40 @@ -require File.expand_path('../../../spec_helper', __FILE__) +require_relative '../../spec_helper' describe "Integer#even?" do - it "returns true for a Fixnum when it is an even number" do - (-2).even?.should be_true - (-1).even?.should be_false + context "fixnum" do + it "returns true for a Fixnum when it is an even number" do + (-2).even?.should be_true + (-1).even?.should be_false - 0.even?.should be_true - 1.even?.should be_false - 2.even?.should be_true + 0.even?.should be_true + 1.even?.should be_false + 2.even?.should be_true + end + + it "returns true for a Bignum when it is an even number" do + bignum_value(0).even?.should be_true + bignum_value(1).even?.should be_false + + (-bignum_value(0)).even?.should be_true + (-bignum_value(1)).even?.should be_false + end end - it "returns true for a Bignum when it is an even number" do - bignum_value(0).even?.should be_true - bignum_value(1).even?.should be_false + context "bignum" do + it "returns true if self is even and positive" do + (10000**10).even?.should be_true + end + + it "returns true if self is even and negative" do + (-10000**10).even?.should be_true + end + + it "returns false if self is odd and positive" do + (9879**976).even?.should be_false + end - (-bignum_value(0)).even?.should be_true - (-bignum_value(1)).even?.should be_false + it "returns false if self is odd and negative" do + (-9879**976).even?.should be_false + end end end diff --git a/spec/ruby/core/integer/exponent_spec.rb b/spec/ruby/core/integer/exponent_spec.rb new file mode 100644 index 0000000000..c610661aff --- /dev/null +++ b/spec/ruby/core/integer/exponent_spec.rb @@ -0,0 +1,7 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' +require_relative 'shared/exponent' + +describe "Integer#**" do + it_behaves_like :integer_exponent, :** +end diff --git a/spec/ruby/core/integer/fdiv_spec.rb b/spec/ruby/core/integer/fdiv_spec.rb new file mode 100644 index 0000000000..d9ea2fdf8d --- /dev/null +++ b/spec/ruby/core/integer/fdiv_spec.rb @@ -0,0 +1,100 @@ +require_relative '../../spec_helper' + +describe "Integer#fdiv" do + it "performs floating-point division between self and a fixnum" do + 8.fdiv(7).should be_close(1.14285714285714, TOLERANCE) + end + + it "performs floating-point division between self and a bignum" do + 8.fdiv(bignum_value).should be_close(8.673617379884035e-19, TOLERANCE) + end + + it "performs floating-point division between self bignum and a bignum" do + num = 1000000000000000000000000000000000048148248609680896326399448564623182963452541226153892315137780403285956264146010000000000000000000000000000000000048148248609680896326399448564623182963452541226153892315137780403285956264146010000000000000000000000000000000000048148248609680896326399448564623182963452541226153892315137780403285956264146009 + den = 2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + num.fdiv(den).should == 500.0 + end + + it "rounds to the correct value for bignums" do + den = 9 * 10**342 + + num = 1 * 10**344 + num.fdiv(den).should == 11.11111111111111 + + num = 1 * 10**343 + num.fdiv(den).should == 1.1111111111111112 + + num = 1 * 10**342 + num.fdiv(den).should == 0.1111111111111111 + + num = 2 * 10**342 + num.fdiv(den).should == 0.2222222222222222 + + num = 3 * 10**342 + num.fdiv(den).should == 0.3333333333333333 + + num = 4 * 10**342 + num.fdiv(den).should == 0.4444444444444444 + + num = 5 * 10**342 + num.fdiv(den).should == 0.5555555555555556 + + num = 6 * 10**342 + num.fdiv(den).should == 0.6666666666666666 + + num = 7 * 10**342 + num.fdiv(den).should == 0.7777777777777778 + + num = 8 * 10**342 + num.fdiv(den).should == 0.8888888888888888 + + num = 9 * 10**342 + num.fdiv(den).should == 1.0 + + num = -5 * 10**342 + num.fdiv(den).should == -0.5555555555555556 + end + + it "rounds to the correct float for bignum denominators" do + 1.fdiv(10**324).should == 0.0 + 1.fdiv(10**323).should == 1.0e-323 + end + + it "performs floating-point division between self and a Float" do + 8.fdiv(9.0).should be_close(0.888888888888889, TOLERANCE) + end + + it "returns NaN when the argument is NaN" do + -1.fdiv(nan_value).nan?.should be_true + 1.fdiv(nan_value).nan?.should be_true + end + + it "returns Infinity when the argument is 0" do + 1.fdiv(0).infinite?.should == 1 + end + + it "returns -Infinity when the argument is 0 and self is negative" do + -1.fdiv(0).infinite?.should == -1 + end + + it "returns Infinity when the argument is 0.0" do + 1.fdiv(0.0).infinite?.should == 1 + end + + it "returns -Infinity when the argument is 0.0 and self is negative" do + -1.fdiv(0.0).infinite?.should == -1 + end + + it "raises a TypeError when argument isn't numeric" do + -> { 1.fdiv(mock('non-numeric')) }.should raise_error(TypeError) + end + + it "raises an ArgumentError when passed multiple arguments" do + -> { 1.fdiv(6,0.2) }.should raise_error(ArgumentError) + end + + it "follows the coercion protocol" do + (obj = mock('10')).should_receive(:coerce).with(1).and_return([1, 10]) + 1.fdiv(obj).should == 0.1 + end +end diff --git a/spec/ruby/core/integer/floor_spec.rb b/spec/ruby/core/integer/floor_spec.rb index 9babcd9a3e..8fb84d58cb 100644 --- a/spec/ruby/core/integer/floor_spec.rb +++ b/spec/ruby/core/integer/floor_spec.rb @@ -1,21 +1,13 @@ -require File.expand_path('../../../spec_helper', __FILE__) -require File.expand_path('../shared/to_i', __FILE__) -require File.expand_path('../shared/integer_rounding', __FILE__) +require_relative '../../spec_helper' +require_relative 'shared/to_i' +require_relative 'shared/integer_rounding' +require_relative 'shared/integer_floor_precision' describe "Integer#floor" do - it_behaves_like(:integer_to_i, :floor) - it_behaves_like(:integer_rounding_positive_precision, :floor) + it_behaves_like :integer_to_i, :floor + it_behaves_like :integer_rounding_positive_precision, :floor - ruby_version_is "2.4" do - context "precision argument specified as part of the floor method is negative" do - it "returns the largest integer less than self with at least precision.abs trailing zeros" do - 1832.floor(-1).should eql(1830) - 1832.floor(-2).should eql(1800) - 1832.floor(-3).should eql(1000) - -1832.floor(-1).should eql(-1840) - -1832.floor(-2).should eql(-1900) - -1832.floor(-3).should eql(-2000) - end - end + context "with precision" do + it_behaves_like :integer_floor_precision, :Integer end end diff --git a/spec/ruby/core/integer/gcd_spec.rb b/spec/ruby/core/integer/gcd_spec.rb index 5428c94ad1..961f1c5c2e 100644 --- a/spec/ruby/core/integer/gcd_spec.rb +++ b/spec/ruby/core/integer/gcd_spec.rb @@ -1,4 +1,4 @@ -require File.expand_path('../../../spec_helper', __FILE__) +require_relative '../../spec_helper' describe "Integer#gcd" do it "returns self if equal to the argument" do @@ -33,26 +33,37 @@ describe "Integer#gcd" do it "accepts a Bignum argument" do bignum = 9999**99 - bignum.should be_kind_of(Bignum) + bignum.should be_kind_of(Integer) 99.gcd(bignum).should == 99 end it "works if self is a Bignum" do bignum = 9999**99 - bignum.should be_kind_of(Bignum) + bignum.should be_kind_of(Integer) bignum.gcd(99).should == 99 end + it "doesn't cause an integer overflow" do + [2 ** (1.size * 8 - 2), 0x8000000000000000].each do |max| + [max - 1, max, max + 1].each do |num| + num.gcd(num).should == num + (-num).gcd(num).should == num + (-num).gcd(-num).should == num + num.gcd(-num).should == num + end + end + end + it "raises an ArgumentError if not given an argument" do - lambda { 12.gcd }.should raise_error(ArgumentError) + -> { 12.gcd }.should raise_error(ArgumentError) end it "raises an ArgumentError if given more than one argument" do - lambda { 12.gcd(30, 20) }.should raise_error(ArgumentError) + -> { 12.gcd(30, 20) }.should raise_error(ArgumentError) end it "raises a TypeError unless the argument is an Integer" do - lambda { 39.gcd(3.8) }.should raise_error(TypeError) - lambda { 45872.gcd([]) }.should raise_error(TypeError) + -> { 39.gcd(3.8) }.should raise_error(TypeError) + -> { 45872.gcd([]) }.should raise_error(TypeError) end end diff --git a/spec/ruby/core/integer/gcdlcm_spec.rb b/spec/ruby/core/integer/gcdlcm_spec.rb index 86968499b0..ebf45e55a1 100644 --- a/spec/ruby/core/integer/gcdlcm_spec.rb +++ b/spec/ruby/core/integer/gcdlcm_spec.rb @@ -1,4 +1,4 @@ -require File.expand_path('../../../spec_helper', __FILE__) +require_relative '../../spec_helper' describe "Integer#gcdlcm" do it "returns [self, self] if self is equal to the argument" do @@ -28,26 +28,26 @@ describe "Integer#gcdlcm" do it "accepts a Bignum argument" do bignum = 91999**99 - bignum.should be_kind_of(Bignum) + bignum.should be_kind_of(Integer) 99.gcdlcm(bignum).should == [99.gcd(bignum), 99.lcm(bignum)] end it "works if self is a Bignum" do bignum = 9999**89 - bignum.should be_kind_of(Bignum) + bignum.should be_kind_of(Integer) bignum.gcdlcm(99).should == [bignum.gcd(99), bignum.lcm(99)] end it "raises an ArgumentError if not given an argument" do - lambda { 12.gcdlcm }.should raise_error(ArgumentError) + -> { 12.gcdlcm }.should raise_error(ArgumentError) end it "raises an ArgumentError if given more than one argument" do - lambda { 12.gcdlcm(30, 20) }.should raise_error(ArgumentError) + -> { 12.gcdlcm(30, 20) }.should raise_error(ArgumentError) end it "raises a TypeError unless the argument is an Integer" do - lambda { 39.gcdlcm(3.8) }.should raise_error(TypeError) - lambda { 45872.gcdlcm([]) }.should raise_error(TypeError) + -> { 39.gcdlcm(3.8) }.should raise_error(TypeError) + -> { 45872.gcdlcm([]) }.should raise_error(TypeError) end end diff --git a/spec/ruby/core/integer/gt_spec.rb b/spec/ruby/core/integer/gt_spec.rb index ec0834d346..f0179e566d 100644 --- a/spec/ruby/core/integer/gt_spec.rb +++ b/spec/ruby/core/integer/gt_spec.rb @@ -1,7 +1,43 @@ -require File.expand_path('../../../spec_helper', __FILE__) -require File.expand_path('../shared/comparison_exception_in_coerce', __FILE__) +require_relative '../../spec_helper' +require_relative 'shared/comparison_coerce' describe "Integer#>" do - it_behaves_like :integer_comparison_exception_in_coerce, :> -end + it_behaves_like :integer_comparison_coerce_not_rescue, :> + + context "fixnum" do + it "returns true if self is greater than the given argument" do + (13 > 2).should == true + (-500 > -600).should == true + + (1 > 5).should == false + (5 > 5).should == false + + (900 > bignum_value).should == false + (5 > 4.999).should == true + end + + it "raises an ArgumentError when given a non-Integer" do + -> { 5 > "4" }.should raise_error(ArgumentError) + -> { 5 > mock('x') }.should raise_error(ArgumentError) + end + end + context "bignum" do + before :each do + @bignum = bignum_value(732) + end + + it "returns true if self is greater than the given argument" do + (@bignum > (@bignum - 1)).should == true + (@bignum > 14.6).should == true + (@bignum > 10).should == true + + (@bignum > (@bignum + 500)).should == false + end + + it "raises an ArgumentError when given a non-Integer" do + -> { @bignum > "4" }.should raise_error(ArgumentError) + -> { @bignum > mock('str') }.should raise_error(ArgumentError) + end + end +end diff --git a/spec/ruby/core/integer/gte_spec.rb b/spec/ruby/core/integer/gte_spec.rb index a49535f823..6237fc2c51 100644 --- a/spec/ruby/core/integer/gte_spec.rb +++ b/spec/ruby/core/integer/gte_spec.rb @@ -1,7 +1,43 @@ -require File.expand_path('../../../spec_helper', __FILE__) -require File.expand_path('../shared/comparison_exception_in_coerce', __FILE__) +require_relative '../../spec_helper' +require_relative 'shared/comparison_coerce' describe "Integer#>=" do - it_behaves_like :integer_comparison_exception_in_coerce, :>= -end + it_behaves_like :integer_comparison_coerce_not_rescue, :>= + + context "fixnum" do + it "returns true if self is greater than or equal to the given argument" do + (13 >= 2).should == true + (-500 >= -600).should == true + + (1 >= 5).should == false + (2 >= 2).should == true + (5 >= 5).should == true + + (900 >= bignum_value).should == false + (5 >= 4.999).should == true + end + it "raises an ArgumentError when given a non-Integer" do + -> { 5 >= "4" }.should raise_error(ArgumentError) + -> { 5 >= mock('x') }.should raise_error(ArgumentError) + end + end + + context "bignum" do + before :each do + @bignum = bignum_value(14) + end + + it "returns true if self is greater than or equal to other" do + (@bignum >= @bignum).should == true + (@bignum >= (@bignum + 2)).should == false + (@bignum >= 5664.2).should == true + (@bignum >= 4).should == true + end + + it "raises an ArgumentError when given a non-Integer" do + -> { @bignum >= "4" }.should raise_error(ArgumentError) + -> { @bignum >= mock('str') }.should raise_error(ArgumentError) + end + end +end diff --git a/spec/ruby/core/integer/integer_spec.rb b/spec/ruby/core/integer/integer_spec.rb index 393f072563..2d5d2e3e92 100644 --- a/spec/ruby/core/integer/integer_spec.rb +++ b/spec/ruby/core/integer/integer_spec.rb @@ -1,22 +1,20 @@ -require File.expand_path('../../../spec_helper', __FILE__) +require_relative '../../spec_helper' describe "Integer" do it "includes Comparable" do Integer.include?(Comparable).should == true end - ruby_version_is "2.4" do - it "is the class of both small and large integers" do - 42.class.should equal(Integer) - bignum_value.class.should equal(Integer) - end + it "is the class of both small and large integers" do + 42.class.should equal(Integer) + bignum_value.class.should equal(Integer) end end describe "Integer#integer?" do it "returns true for Integers" do - 0.integer?.should == true - -1.integer?.should == true - bignum_value.integer?.should == true + 0.should.integer? + -1.should.integer? + bignum_value.should.integer? end end diff --git a/spec/ruby/core/integer/lcm_spec.rb b/spec/ruby/core/integer/lcm_spec.rb index ddf25d3853..296a001c8c 100644 --- a/spec/ruby/core/integer/lcm_spec.rb +++ b/spec/ruby/core/integer/lcm_spec.rb @@ -1,4 +1,4 @@ -require File.expand_path('../../../spec_helper', __FILE__) +require_relative '../../spec_helper' describe "Integer#lcm" do it "returns self if equal to the argument" do @@ -33,26 +33,26 @@ describe "Integer#lcm" do it "accepts a Bignum argument" do bignum = 9999**99 - bignum.should be_kind_of(Bignum) + bignum.should be_kind_of(Integer) 99.lcm(bignum).should == bignum end it "works if self is a Bignum" do bignum = 9999**99 - bignum.should be_kind_of(Bignum) + bignum.should be_kind_of(Integer) bignum.lcm(99).should == bignum end it "raises an ArgumentError if not given an argument" do - lambda { 12.lcm }.should raise_error(ArgumentError) + -> { 12.lcm }.should raise_error(ArgumentError) end it "raises an ArgumentError if given more than one argument" do - lambda { 12.lcm(30, 20) }.should raise_error(ArgumentError) + -> { 12.lcm(30, 20) }.should raise_error(ArgumentError) end it "raises a TypeError unless the argument is an Integer" do - lambda { 39.lcm(3.8) }.should raise_error(TypeError) - lambda { 45872.lcm([]) }.should raise_error(TypeError) + -> { 39.lcm(3.8) }.should raise_error(TypeError) + -> { 45872.lcm([]) }.should raise_error(TypeError) end end diff --git a/spec/ruby/core/integer/left_shift_spec.rb b/spec/ruby/core/integer/left_shift_spec.rb new file mode 100644 index 0000000000..86c2b18ae2 --- /dev/null +++ b/spec/ruby/core/integer/left_shift_spec.rb @@ -0,0 +1,211 @@ +require_relative '../../spec_helper' + +describe "Integer#<< (with n << m)" do + context "fixnum" do + it "returns n shifted left m bits when n > 0, m > 0" do + (1 << 1).should == 2 + end + + it "returns n shifted left m bits when n < 0, m > 0" do + (-1 << 1).should == -2 + (-7 << 1).should == -14 + (-42 << 2).should == -168 + end + + it "returns n shifted right m bits when n > 0, m < 0" do + (2 << -1).should == 1 + end + + it "returns n shifted right m bits when n < 0, m < 0" do + (-2 << -1).should == -1 + end + + it "returns 0 when n == 0" do + (0 << 1).should == 0 + end + + it "returns n when n > 0, m == 0" do + (1 << 0).should == 1 + end + + it "returns n when n < 0, m == 0" do + (-1 << 0).should == -1 + end + + it "returns 0 when n > 0, m < 0 and n < 2**-m" do + (3 << -2).should == 0 + (7 << -3).should == 0 + (127 << -7).should == 0 + + # To make sure the exponent is not truncated + (7 << -32).should == 0 + (7 << -64).should == 0 + end + + it "returns -1 when n < 0, m < 0 and n > -(2**-m)" do + (-3 << -2).should == -1 + (-7 << -3).should == -1 + (-127 << -7).should == -1 + + # To make sure the exponent is not truncated + (-7 << -32).should == -1 + (-7 << -64).should == -1 + end + + it "returns 0 when m < 0 and m is a Bignum" do + (3 << -bignum_value).should == 0 + end + + it "returns a Bignum == fixnum_max * 2 when fixnum_max << 1 and n > 0" do + result = fixnum_max << 1 + result.should be_an_instance_of(Integer) + result.should == fixnum_max * 2 + end + + it "returns a Bignum == fixnum_min * 2 when fixnum_min << 1 and n < 0" do + result = fixnum_min << 1 + result.should be_an_instance_of(Integer) + result.should == fixnum_min * 2 + end + + it "calls #to_int to convert the argument to an Integer" do + obj = mock("4") + obj.should_receive(:to_int).and_return(4) + (3 << obj).should == 48 + + obj = mock("to_int_neg_bignum") + obj.should_receive(:to_int).and_return(-bignum_value) + (3 << obj).should == 0 + end + + it "raises a TypeError when #to_int does not return an Integer" do + obj = mock("a string") + obj.should_receive(:to_int).and_return("asdf") + + -> { 3 << obj }.should raise_error(TypeError) + end + + it "raises a TypeError when passed nil" do + -> { 3 << nil }.should raise_error(TypeError) + end + + it "raises a TypeError when passed a String" do + -> { 3 << "4" }.should raise_error(TypeError) + end + end + + context "bignum" do + before :each do + @bignum = bignum_value * 8 # 2 ** 67 + end + + it "returns n shifted left m bits when n > 0, m > 0" do + (@bignum << 4).should == 2361183241434822606848 + end + + it "returns n shifted left m bits when n < 0, m > 0" do + (-@bignum << 9).should == -75557863725914323419136 + end + + it "returns n shifted right m bits when n > 0, m < 0" do + (@bignum << -1).should == 73786976294838206464 + end + + it "returns n shifted right m bits when n < 0, m < 0" do + (-@bignum << -2).should == -36893488147419103232 + end + + it "returns n when n > 0, m == 0" do + (@bignum << 0).should == @bignum + end + + it "returns n when n < 0, m == 0" do + (-@bignum << 0).should == -@bignum + end + + it "returns 0 when m < 0 and m == p where 2**p > n >= 2**(p-1)" do + (@bignum << -68).should == 0 + end + + it "returns a Fixnum == fixnum_max when (fixnum_max * 2) << -1 and n > 0" do + result = (fixnum_max * 2) << -1 + result.should be_an_instance_of(Integer) + result.should == fixnum_max + end + + it "returns a Fixnum == fixnum_min when (fixnum_min * 2) << -1 and n < 0" do + result = (fixnum_min * 2) << -1 + result.should be_an_instance_of(Integer) + result.should == fixnum_min + end + + it "calls #to_int to convert the argument to an Integer" do + obj = mock("4") + obj.should_receive(:to_int).and_return(4) + + (@bignum << obj).should == 2361183241434822606848 + end + + it "raises a TypeError when #to_int does not return an Integer" do + obj = mock("a string") + obj.should_receive(:to_int).and_return("asdf") + + -> { @bignum << obj }.should raise_error(TypeError) + end + + it "raises a TypeError when passed nil" do + -> { @bignum << nil }.should raise_error(TypeError) + end + + it "raises a TypeError when passed a String" do + -> { @bignum << "4" }.should raise_error(TypeError) + end + end + + context "when m is a bignum or larger than int" do + it "returns -1 when m < 0 and n < 0" do + (-1 << -bignum_value).should == -1 + (-1 << -(2**40)).should == -1 + + (-bignum_value << -bignum_value).should == -1 + (-bignum_value << -(2**40)).should == -1 + end + + it "returns 0 when m < 0 and n >= 0" do + (0 << -bignum_value).should == 0 + (1 << -bignum_value).should == 0 + (bignum_value << -bignum_value).should == 0 + + (0 << -(2**40)).should == 0 + (1 << -(2**40)).should == 0 + (bignum_value << -(2**40)).should == 0 + end + + it "returns 0 when m > 0 long and n == 0" do + (0 << (2**40)).should == 0 + end + + it "returns 0 when m > 0 bignum and n == 0" do + (0 << bignum_value).should == 0 + end + + it "raises RangeError when m > 0 and n != 0" do + # https://bugs.ruby-lang.org/issues/18518#note-9 + limit = RUBY_ENGINE == 'ruby' ? 2**67 : 2**32 + + coerce_long = mock("long") + coerce_long.stub!(:to_int).and_return(limit) + coerce_bignum = mock("bignum") + coerce_bignum.stub!(:to_int).and_return(bignum_value) + exps = [limit, coerce_long] + exps << bignum_value << coerce_bignum if bignum_value >= limit + + exps.each { |exp| + -> { (1 << exp) }.should raise_error(RangeError, 'shift width too big') + -> { (-1 << exp) }.should raise_error(RangeError, 'shift width too big') + -> { (bignum_value << exp) }.should raise_error(RangeError, 'shift width too big') + -> { (-bignum_value << exp) }.should raise_error(RangeError, 'shift width too big') + } + end + end +end diff --git a/spec/ruby/core/integer/lt_spec.rb b/spec/ruby/core/integer/lt_spec.rb index 3b778d7f6c..c182f82555 100644 --- a/spec/ruby/core/integer/lt_spec.rb +++ b/spec/ruby/core/integer/lt_spec.rb @@ -1,6 +1,45 @@ -require File.expand_path('../../../spec_helper', __FILE__) -require File.expand_path('../shared/comparison_exception_in_coerce', __FILE__) +require_relative '../../spec_helper' +require_relative 'shared/comparison_coerce' describe "Integer#<" do - it_behaves_like :integer_comparison_exception_in_coerce, :< + it_behaves_like :integer_comparison_coerce_not_rescue, :< + + context "fixnum" do + it "returns true if self is less than the given argument" do + (2 < 13).should == true + (-600 < -500).should == true + + (5 < 1).should == false + (5 < 5).should == false + + (900 < bignum_value).should == true + (5 < 4.999).should == false + end + + it "raises an ArgumentError when given a non-Integer" do + -> { 5 < "4" }.should raise_error(ArgumentError) + -> { 5 < mock('x') }.should raise_error(ArgumentError) + end + end + + context "bignum" do + before :each do + @bignum = bignum_value(32) + end + + it "returns true if self is less than the given argument" do + (@bignum < @bignum + 1).should == true + (-@bignum < -(@bignum - 1)).should == true + + (@bignum < 1).should == false + (@bignum < 5).should == false + + (@bignum < 4.999).should == false + end + + it "raises an ArgumentError when given a non-Integer" do + -> { @bignum < "4" }.should raise_error(ArgumentError) + -> { @bignum < mock('str') }.should raise_error(ArgumentError) + end + end end diff --git a/spec/ruby/core/integer/lte_spec.rb b/spec/ruby/core/integer/lte_spec.rb index d62780c78c..65b71d77f2 100644 --- a/spec/ruby/core/integer/lte_spec.rb +++ b/spec/ruby/core/integer/lte_spec.rb @@ -1,6 +1,53 @@ -require File.expand_path('../../../spec_helper', __FILE__) -require File.expand_path('../shared/comparison_exception_in_coerce', __FILE__) +require_relative '../../spec_helper' +require_relative 'shared/comparison_coerce' describe "Integer#<=" do - it_behaves_like :integer_comparison_exception_in_coerce, :<= + it_behaves_like :integer_comparison_coerce_not_rescue, :<= + + context "fixnum" do + it "returns true if self is less than or equal to other" do + (2 <= 13).should == true + (-600 <= -500).should == true + + (5 <= 1).should == false + (5 <= 5).should == true + (-2 <= -2).should == true + + (900 <= bignum_value).should == true + (5 <= 4.999).should == false + end + + it "raises an ArgumentError when given a non-Integer" do + -> { 5 <= "4" }.should raise_error(ArgumentError) + -> { 5 <= mock('x') }.should raise_error(ArgumentError) + end + end + + context "bignum" do + before :each do + @bignum = bignum_value(39) + end + + it "returns true if self is less than or equal to other" do + (@bignum <= @bignum).should == true + (-@bignum <= -(@bignum - 1)).should == true + + (@bignum <= 4.999).should == false + end + + it "returns false if compares with near float" do + (@bignum <= (@bignum + 0.0)).should == false + (@bignum <= (@bignum + 0.5)).should == false + end + + it "returns true for bignums compare to a bigger float" do + (18446744073709551616 <= 1.8446744073709552e+19).should == true + (@bignum <= (@bignum + 9999.0)).should == true + end + + it "raises an ArgumentError when given a non-Integer" do + -> { @bignum <= "4" }.should raise_error(ArgumentError) + -> { @bignum <= mock('str') }.should raise_error(ArgumentError) + end + end end diff --git a/spec/ruby/core/integer/magnitude_spec.rb b/spec/ruby/core/integer/magnitude_spec.rb new file mode 100644 index 0000000000..48cf1a8534 --- /dev/null +++ b/spec/ruby/core/integer/magnitude_spec.rb @@ -0,0 +1,6 @@ +require_relative '../../spec_helper' +require_relative 'shared/abs' + +describe "Integer#magnitude" do + it_behaves_like :integer_abs, :magnitude +end diff --git a/spec/ruby/core/integer/minus_spec.rb b/spec/ruby/core/integer/minus_spec.rb index 6e8fa69759..6072ba7c8b 100644 --- a/spec/ruby/core/integer/minus_spec.rb +++ b/spec/ruby/core/integer/minus_spec.rb @@ -1,7 +1,60 @@ -require File.expand_path('../../../spec_helper', __FILE__) -require File.expand_path('../shared/arithmetic_exception_in_coerce', __FILE__) +require_relative '../../spec_helper' +require_relative 'shared/arithmetic_coerce' describe "Integer#-" do - it_behaves_like :integer_arithmetic_exception_in_coerce, :- -end + it_behaves_like :integer_arithmetic_coerce_not_rescue, :- + + context "fixnum" do + it "returns self minus the given Integer" do + (5 - 10).should == -5 + (9237212 - 5_280).should == 9231932 + + (781 - 0.5).should == 780.5 + (2_560_496 - bignum_value).should == -18446744073706991120 + end + + it "raises a TypeError when given a non-Integer" do + -> { + (obj = mock('10')).should_receive(:to_int).any_number_of_times.and_return(10) + 13 - obj + }.should raise_error(TypeError) + -> { 13 - "10" }.should raise_error(TypeError) + -> { 13 - :symbol }.should raise_error(TypeError) + end + end + + context "bignum" do + before :each do + @bignum = bignum_value(314) + end + it "returns self minus the given Integer" do + (@bignum - 9).should == 18446744073709551921 + (@bignum - 12.57).should be_close(18446744073709551917.43, TOLERANCE) + (@bignum - bignum_value(42)).should == 272 + end + + it "raises a TypeError when given a non-Integer" do + -> { @bignum - mock('10') }.should raise_error(TypeError) + -> { @bignum - "10" }.should raise_error(TypeError) + -> { @bignum - :symbol }.should raise_error(TypeError) + end + end + + it "coerces the RHS and calls #coerce" do + obj = mock("integer plus") + obj.should_receive(:coerce).with(5).and_return([5, 10]) + (5 - obj).should == -5 + end + + it "coerces the RHS and calls #coerce even if it's private" do + obj = Object.new + class << obj + private def coerce(n) + [n, 10] + end + end + + (5 - obj).should == -5 + end +end diff --git a/spec/ruby/core/integer/modulo_spec.rb b/spec/ruby/core/integer/modulo_spec.rb new file mode 100644 index 0000000000..e263338e38 --- /dev/null +++ b/spec/ruby/core/integer/modulo_spec.rb @@ -0,0 +1,10 @@ +require_relative '../../spec_helper' +require_relative 'shared/modulo' + +describe "Integer#%" do + it_behaves_like :integer_modulo, :% +end + +describe "Integer#modulo" do + it_behaves_like :integer_modulo, :modulo +end diff --git a/spec/ruby/core/integer/multiply_spec.rb b/spec/ruby/core/integer/multiply_spec.rb index cfce2418a4..5037d27458 100644 --- a/spec/ruby/core/integer/multiply_spec.rb +++ b/spec/ruby/core/integer/multiply_spec.rb @@ -1,6 +1,45 @@ -require File.expand_path('../../../spec_helper', __FILE__) -require File.expand_path('../shared/arithmetic_exception_in_coerce', __FILE__) +require_relative '../../spec_helper' +require_relative 'shared/arithmetic_coerce' describe "Integer#*" do - it_behaves_like :integer_arithmetic_exception_in_coerce, :* + it_behaves_like :integer_arithmetic_coerce_not_rescue, :* + + context "fixnum" do + it "returns self multiplied by the given Integer" do + (4923 * 2).should == 9846 + (1342177 * 800).should == 1073741600 + (65536 * 65536).should == 4294967296 + + (256 * bignum_value).should == 4722366482869645213696 + (6712 * 0.25).should == 1678.0 + end + + it "raises a TypeError when given a non-Integer" do + -> { + (obj = mock('10')).should_receive(:to_int).any_number_of_times.and_return(10) + 13 * obj + }.should raise_error(TypeError) + -> { 13 * "10" }.should raise_error(TypeError) + -> { 13 * :symbol }.should raise_error(TypeError) + end + end + + context "bignum" do + before :each do + @bignum = bignum_value(772) + end + + it "returns self multiplied by the given Integer" do + (@bignum * (1/bignum_value(0xffff).to_f)).should be_close(1.0, TOLERANCE) + (@bignum * (1/bignum_value(0xffff).to_f)).should be_close(1.0, TOLERANCE) + (@bignum * 10).should == 184467440737095523880 + (@bignum * (@bignum - 40)).should == 340282366920938491207277694290934407024 + end + + it "raises a TypeError when given a non-Integer" do + -> { @bignum * mock('10') }.should raise_error(TypeError) + -> { @bignum * "10" }.should raise_error(TypeError) + -> { @bignum * :symbol }.should raise_error(TypeError) + end + end end diff --git a/spec/ruby/core/integer/next_spec.rb b/spec/ruby/core/integer/next_spec.rb index a34db0a6f6..63c4e67893 100644 --- a/spec/ruby/core/integer/next_spec.rb +++ b/spec/ruby/core/integer/next_spec.rb @@ -1,6 +1,6 @@ -require File.expand_path('../../../spec_helper', __FILE__) -require File.expand_path('../shared/next', __FILE__) +require_relative '../../spec_helper' +require_relative 'shared/next' describe "Integer#next" do - it_behaves_like(:integer_next, :next) + it_behaves_like :integer_next, :next end diff --git a/spec/ruby/core/integer/nobits_spec.rb b/spec/ruby/core/integer/nobits_spec.rb index cfa63a4b94..685759ffa3 100644 --- a/spec/ruby/core/integer/nobits_spec.rb +++ b/spec/ruby/core/integer/nobits_spec.rb @@ -1,38 +1,36 @@ -require File.expand_path('../../../spec_helper', __FILE__) +require_relative '../../spec_helper' -ruby_version_is '2.5' do - describe "Integer#nobits?" do - it "returns true iff all no bits of the argument are set in the receiver" do - 42.nobits?(42).should == false - 0b1010_1010.nobits?(0b1000_0010).should == false - 0b1010_1010.nobits?(0b1000_0001).should == false - 0b0100_0101.nobits?(0b1010_1010).should == true - different_bignum = (2 * bignum_value) & (~bignum_value) - (0b1010_1010 | different_bignum).nobits?(0b1000_0010 | bignum_value).should == false - (0b1010_1010 | different_bignum).nobits?(0b1000_0001 | bignum_value).should == false - (0b0100_0101 | different_bignum).nobits?(0b1010_1010 | bignum_value).should == true - end +describe "Integer#nobits?" do + it "returns true if and only if all no bits of the argument are set in the receiver" do + 42.nobits?(42).should == false + 0b1010_1010.nobits?(0b1000_0010).should == false + 0b1010_1010.nobits?(0b1000_0001).should == false + 0b0100_0101.nobits?(0b1010_1010).should == true + different_bignum = (2 * bignum_value) & (~bignum_value) + (0b1010_1010 | different_bignum).nobits?(0b1000_0010 | bignum_value).should == false + (0b1010_1010 | different_bignum).nobits?(0b1000_0001 | bignum_value).should == false + (0b0100_0101 | different_bignum).nobits?(0b1010_1010 | bignum_value).should == true + end - it "handles negative values using two's complement notation" do - (~0b1101).nobits?(0b1101).should == true - (-42).nobits?(-42).should == false - (~0b1101).nobits?(~0b10).should == false - (~(0b1101 | bignum_value)).nobits?(~(0b10 | bignum_value)).should == false - end + it "handles negative values using two's complement notation" do + (~0b1101).nobits?(0b1101).should == true + (-42).nobits?(-42).should == false + (~0b1101).nobits?(~0b10).should == false + (~(0b1101 | bignum_value)).nobits?(~(0b10 | bignum_value)).should == false + end - it "coerces the rhs using to_int" do - obj = mock("the int 0b10") - obj.should_receive(:to_int).and_return(0b10) - 0b110.nobits?(obj).should == false - end + it "coerces the rhs using to_int" do + obj = mock("the int 0b10") + obj.should_receive(:to_int).and_return(0b10) + 0b110.nobits?(obj).should == false + end - it "raises a TypeError when given a non-Integer" do - lambda { - (obj = mock('10')).should_receive(:coerce).any_number_of_times.and_return([42,10]) - 13.nobits?(obj) - }.should raise_error(TypeError) - lambda { 13.nobits?("10") }.should raise_error(TypeError) - lambda { 13.nobits?(:symbol) }.should raise_error(TypeError) - end + it "raises a TypeError when given a non-Integer" do + -> { + (obj = mock('10')).should_receive(:coerce).any_number_of_times.and_return([42,10]) + 13.nobits?(obj) + }.should raise_error(TypeError) + -> { 13.nobits?("10") }.should raise_error(TypeError) + -> { 13.nobits?(:symbol) }.should raise_error(TypeError) end end diff --git a/spec/ruby/core/integer/numerator_spec.rb b/spec/ruby/core/integer/numerator_spec.rb index 12550b9a31..f4149d770e 100644 --- a/spec/ruby/core/integer/numerator_spec.rb +++ b/spec/ruby/core/integer/numerator_spec.rb @@ -1,4 +1,4 @@ -require File.expand_path('../../../spec_helper', __FILE__) +require_relative '../../spec_helper' describe "Integer#numerator" do before :all do diff --git a/spec/ruby/core/integer/odd_spec.rb b/spec/ruby/core/integer/odd_spec.rb index 2aa76d054a..dd779fa44b 100644 --- a/spec/ruby/core/integer/odd_spec.rb +++ b/spec/ruby/core/integer/odd_spec.rb @@ -1,18 +1,38 @@ -require File.expand_path('../../../spec_helper', __FILE__) +require_relative '../../spec_helper' describe "Integer#odd?" do - it "returns true when self is an odd number" do - (-2).odd?.should be_false - (-1).odd?.should be_true + context "fixnum" do + it "returns true when self is an odd number" do + (-2).odd?.should be_false + (-1).odd?.should be_true - 0.odd?.should be_false - 1.odd?.should be_true - 2.odd?.should be_false + 0.odd?.should be_false + 1.odd?.should be_true + 2.odd?.should be_false - bignum_value(0).odd?.should be_false - bignum_value(1).odd?.should be_true + bignum_value(0).odd?.should be_false + bignum_value(1).odd?.should be_true - (-bignum_value(0)).odd?.should be_false - (-bignum_value(1)).odd?.should be_true + (-bignum_value(0)).odd?.should be_false + (-bignum_value(1)).odd?.should be_true + end + end + + context "bignum" do + it "returns true if self is odd and positive" do + (987279**19).odd?.should be_true + end + + it "returns true if self is odd and negative" do + (-9873389**97).odd?.should be_true + end + + it "returns false if self is even and positive" do + (10000000**10).odd?.should be_false + end + + it "returns false if self is even and negative" do + (-1000000**100).odd?.should be_false + end end end diff --git a/spec/ruby/core/integer/ord_spec.rb b/spec/ruby/core/integer/ord_spec.rb index 5941f513fd..bcb57bea98 100644 --- a/spec/ruby/core/integer/ord_spec.rb +++ b/spec/ruby/core/integer/ord_spec.rb @@ -1,4 +1,4 @@ -require File.expand_path('../../../spec_helper', __FILE__) +require_relative '../../spec_helper' describe "Integer#ord" do it "returns self" do diff --git a/spec/ruby/core/integer/plus_spec.rb b/spec/ruby/core/integer/plus_spec.rb index c292943d06..38428e56c5 100644 --- a/spec/ruby/core/integer/plus_spec.rb +++ b/spec/ruby/core/integer/plus_spec.rb @@ -1,6 +1,75 @@ -require File.expand_path('../../../spec_helper', __FILE__) -require File.expand_path('../shared/arithmetic_exception_in_coerce', __FILE__) +require_relative '../../spec_helper' +require_relative 'shared/arithmetic_coerce' describe "Integer#+" do - it_behaves_like :integer_arithmetic_exception_in_coerce, :+ + it_behaves_like :integer_arithmetic_coerce_not_rescue, :+ + + context "fixnum" do + it "returns self plus the given Integer" do + (491 + 2).should == 493 + (90210 + 10).should == 90220 + + (9 + bignum_value).should == 18446744073709551625 + (1001 + 5.219).should == 1006.219 + end + + it "raises a TypeError when given a non-Integer" do + -> { + (obj = mock('10')).should_receive(:to_int).any_number_of_times.and_return(10) + 13 + obj + }.should raise_error(TypeError) + -> { 13 + "10" }.should raise_error(TypeError) + -> { 13 + :symbol }.should raise_error(TypeError) + end + end + + context "bignum" do + before :each do + @bignum = bignum_value(76) + end + + it "returns self plus the given Integer" do + (@bignum + 4).should == 18446744073709551696 + (@bignum + 4.2).should be_close(18446744073709551696.2, TOLERANCE) + (@bignum + bignum_value(3)).should == 36893488147419103311 + end + + it "raises a TypeError when given a non-Integer" do + -> { @bignum + mock('10') }.should raise_error(TypeError) + -> { @bignum + "10" }.should raise_error(TypeError) + -> { @bignum + :symbol}.should raise_error(TypeError) + end + end + + it "can be redefined" do + code = <<~RUBY + class Integer + alias_method :old_plus, :+ + def +(other) + self - other + end + end + result = 1 + 2 + Integer.alias_method :+, :old_plus + print result + RUBY + ruby_exe(code).should == "-1" + end + + it "coerces the RHS and calls #coerce" do + obj = mock("integer plus") + obj.should_receive(:coerce).with(6).and_return([6, 3]) + (6 + obj).should == 9 + end + + it "coerces the RHS and calls #coerce even if it's private" do + obj = Object.new + class << obj + private def coerce(n) + [n, 3] + end + end + + (6 + obj).should == 9 + end end diff --git a/spec/ruby/core/integer/pow_spec.rb b/spec/ruby/core/integer/pow_spec.rb new file mode 100644 index 0000000000..ecaca01eff --- /dev/null +++ b/spec/ruby/core/integer/pow_spec.rb @@ -0,0 +1,51 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' +require_relative 'shared/exponent' + +describe "Integer#pow" do + context "one argument is passed" do + it_behaves_like :integer_exponent, :pow + end + + context "two arguments are passed" do + it "returns modulo of self raised to the given power" do + 2.pow(5, 12).should == 8 + 2.pow(6, 13).should == 12 + 2.pow(7, 14).should == 2 + 2.pow(8, 15).should == 1 + end + + it "works well with bignums" do + 2.pow(61, 5843009213693951).should eql 3697379018277258 + 2.pow(62, 5843009213693952).should eql 1551748822859776 + 2.pow(63, 5843009213693953).should eql 3103497645717974 + 2.pow(64, 5843009213693954).should eql 363986077738838 + end + + it "handles sign like #divmod does" do + 2.pow(5, 12).should == 8 + 2.pow(5, -12).should == -4 + -2.pow(5, 12).should == 4 + -2.pow(5, -12).should == -8 + end + + it "ensures all arguments are integers" do + -> { 2.pow(5, 12.0) }.should raise_error(TypeError, /2nd argument not allowed unless all arguments are integers/) + -> { 2.pow(5, Rational(12, 1)) }.should raise_error(TypeError, /2nd argument not allowed unless all arguments are integers/) + end + + it "raises TypeError for non-numeric value" do + -> { 2.pow(5, "12") }.should raise_error(TypeError) + -> { 2.pow(5, []) }.should raise_error(TypeError) + -> { 2.pow(5, nil) }.should raise_error(TypeError) + end + + it "raises a ZeroDivisionError when the given argument is 0" do + -> { 2.pow(5, 0) }.should raise_error(ZeroDivisionError) + end + + it "raises a RangeError when the first argument is negative and the second argument is present" do + -> { 2.pow(-5, 1) }.should raise_error(RangeError) + end + end +end diff --git a/spec/ruby/core/integer/pred_spec.rb b/spec/ruby/core/integer/pred_spec.rb index 8b16a7ae03..86750ebfd1 100644 --- a/spec/ruby/core/integer/pred_spec.rb +++ b/spec/ruby/core/integer/pred_spec.rb @@ -1,4 +1,4 @@ -require File.expand_path('../../../spec_helper', __FILE__) +require_relative '../../spec_helper' describe "Integer#pred" do it "returns the Integer equal to self - 1" do diff --git a/spec/ruby/core/integer/rationalize_spec.rb b/spec/ruby/core/integer/rationalize_spec.rb index 1ff4cfa454..09d741af33 100644 --- a/spec/ruby/core/integer/rationalize_spec.rb +++ b/spec/ruby/core/integer/rationalize_spec.rb @@ -1,4 +1,4 @@ -require File.expand_path('../../../spec_helper', __FILE__) +require_relative '../../spec_helper' describe "Integer#rationalize" do before :all do @@ -33,7 +33,7 @@ describe "Integer#rationalize" do end it "raises ArgumentError when passed more than one argument" do - lambda { 1.rationalize(0.1, 0.1) }.should raise_error(ArgumentError) - lambda { 1.rationalize(0.1, 0.1, 2) }.should raise_error(ArgumentError) + -> { 1.rationalize(0.1, 0.1) }.should raise_error(ArgumentError) + -> { 1.rationalize(0.1, 0.1, 2) }.should raise_error(ArgumentError) end end diff --git a/spec/ruby/core/integer/remainder_spec.rb b/spec/ruby/core/integer/remainder_spec.rb new file mode 100644 index 0000000000..757e42fbe8 --- /dev/null +++ b/spec/ruby/core/integer/remainder_spec.rb @@ -0,0 +1,51 @@ +require_relative '../../spec_helper' + +describe "Integer#remainder" do + context "fixnum" do + it "returns the remainder of dividing self by other" do + 5.remainder(3).should == 2 + 5.remainder(3.0).should == 2.0 + 5.remainder(Rational(3, 1)).should == Rational(2, 1) + end + + it "means x-y*(x/y).truncate" do + 5.remainder(3).should == 2 + 5.remainder(3.3).should be_close(1.7, TOLERANCE) + 5.remainder(3.7).should be_close(1.3, TOLERANCE) + end + + it "keeps sign of self" do + 5.remainder( 3).should == 2 + 5.remainder(-3).should == 2 + -5.remainder( 3).should == -2 + -5.remainder(-3).should == -2 + end + + it "raises TypeError if passed non-numeric argument" do + -> { 5.remainder("3") }.should raise_error(TypeError) + -> { 5.remainder(:"3") }.should raise_error(TypeError) + -> { 5.remainder([]) }.should raise_error(TypeError) + -> { 5.remainder(nil) }.should raise_error(TypeError) + end + end + + context "bignum" do + it "returns the remainder of dividing self by other" do + a = bignum_value(79) + a.remainder(2).should == 1 + a.remainder(97.345).should be_close(93.1349992295444, TOLERANCE) + a.remainder(bignum_value).should == 79 + end + + it "raises a ZeroDivisionError if other is zero and not a Float" do + -> { bignum_value(66).remainder(0) }.should raise_error(ZeroDivisionError) + end + + it "does raises ZeroDivisionError if other is zero and a Float" do + a = bignum_value(7) + b = bignum_value(32) + -> { a.remainder(0.0) }.should raise_error(ZeroDivisionError) + -> { b.remainder(-0.0) }.should raise_error(ZeroDivisionError) + end + end +end diff --git a/spec/ruby/core/integer/right_shift_spec.rb b/spec/ruby/core/integer/right_shift_spec.rb new file mode 100644 index 0000000000..c902674e2f --- /dev/null +++ b/spec/ruby/core/integer/right_shift_spec.rb @@ -0,0 +1,233 @@ +require_relative '../../spec_helper' + +describe "Integer#>> (with n >> m)" do + context "fixnum" do + it "returns n shifted right m bits when n > 0, m > 0" do + (2 >> 1).should == 1 + end + + it "returns n shifted right m bits when n < 0, m > 0" do + (-2 >> 1).should == -1 + (-7 >> 1).should == -4 + (-42 >> 2).should == -11 + end + + it "returns n shifted left m bits when n > 0, m < 0" do + (1 >> -1).should == 2 + end + + it "returns n shifted left m bits when n < 0, m < 0" do + (-1 >> -1).should == -2 + end + + it "returns 0 when n == 0" do + (0 >> 1).should == 0 + end + + it "returns n when n > 0, m == 0" do + (1 >> 0).should == 1 + end + + it "returns n when n < 0, m == 0" do + (-1 >> 0).should == -1 + end + + it "returns 0 when n > 0, m > 0 and n < 2**m" do + (3 >> 2).should == 0 + (7 >> 3).should == 0 + (127 >> 7).should == 0 + + # To make sure the exponent is not truncated + (7 >> 32).should == 0 + (7 >> 64).should == 0 + end + + it "returns -1 when n < 0, m > 0 and n > -(2**m)" do + (-3 >> 2).should == -1 + (-7 >> 3).should == -1 + (-127 >> 7).should == -1 + + # To make sure the exponent is not truncated + (-7 >> 32).should == -1 + (-7 >> 64).should == -1 + end + + it "returns a Bignum == fixnum_max * 2 when fixnum_max >> -1 and n > 0" do + result = fixnum_max >> -1 + result.should be_an_instance_of(Integer) + result.should == fixnum_max * 2 + end + + it "returns a Bignum == fixnum_min * 2 when fixnum_min >> -1 and n < 0" do + result = fixnum_min >> -1 + result.should be_an_instance_of(Integer) + result.should == fixnum_min * 2 + end + + it "calls #to_int to convert the argument to an Integer" do + obj = mock("2") + obj.should_receive(:to_int).and_return(2) + (8 >> obj).should == 2 + + obj = mock("to_int_bignum") + obj.should_receive(:to_int).and_return(bignum_value) + (8 >> obj).should == 0 + end + + it "raises a TypeError when #to_int does not return an Integer" do + obj = mock("a string") + obj.should_receive(:to_int).and_return("asdf") + + -> { 3 >> obj }.should raise_error(TypeError) + end + + it "raises a TypeError when passed nil" do + -> { 3 >> nil }.should raise_error(TypeError) + end + + it "raises a TypeError when passed a String" do + -> { 3 >> "4" }.should raise_error(TypeError) + end + end + + context "bignum" do + before :each do + @bignum = bignum_value * 8 # 2 ** 67 + end + + it "returns n shifted right m bits when n > 0, m > 0" do + (@bignum >> 1).should == 73786976294838206464 + end + + it "returns n shifted right m bits when n < 0, m > 0" do + (-@bignum >> 2).should == -36893488147419103232 + end + + it "respects twos complement signed shifting" do + # This explicit left hand value is important because it is the + # exact bit pattern that matters, so it's important it's right + # here to show the significance. + # + + (-42949672980000000000000 >> 14).should == -2621440001220703125 + (-42949672980000000000001 >> 14).should == -2621440001220703126 + # Note the off by one -------------------- ^^^^^^^^^^^^^^^^^^^^ + # This is because even though we discard the lowest bit, in twos + # complement it would influence the bits to the left of it. + + (-42949672980000000000000 >> 15).should == -1310720000610351563 + (-42949672980000000000001 >> 15).should == -1310720000610351563 + + (-0xfffffffffffffffff >> 32).should == -68719476736 + end + + it "respects twos complement signed shifting for very large values" do + giant = 42949672980000000000000000000000000000000000000000000000000000000000000000000000000000000000 + neg = -giant + + (giant >> 84).should == 2220446050284288846538547929770901490087453566957265138626098632812 + (neg >> 84).should == -2220446050284288846538547929770901490087453566957265138626098632813 + end + + it "returns n shifted left m bits when n > 0, m < 0" do + (@bignum >> -2).should == 590295810358705651712 + end + + it "returns n shifted left m bits when n < 0, m < 0" do + (-@bignum >> -3).should == -1180591620717411303424 + end + + it "returns n when n > 0, m == 0" do + (@bignum >> 0).should == @bignum + end + + it "returns n when n < 0, m == 0" do + (-@bignum >> 0).should == -@bignum + end + + it "returns 0 when m > 0 and m == p where 2**p > n >= 2**(p-1)" do + (@bignum >> 68).should == 0 + end + + it "returns a Fixnum == fixnum_max when (fixnum_max * 2) >> 1 and n > 0" do + result = (fixnum_max * 2) >> 1 + result.should be_an_instance_of(Integer) + result.should == fixnum_max + end + + it "returns a Fixnum == fixnum_min when (fixnum_min * 2) >> 1 and n < 0" do + result = (fixnum_min * 2) >> 1 + result.should be_an_instance_of(Integer) + result.should == fixnum_min + end + + it "calls #to_int to convert the argument to an Integer" do + obj = mock("2") + obj.should_receive(:to_int).and_return(2) + + (@bignum >> obj).should == 36893488147419103232 + end + + it "raises a TypeError when #to_int does not return an Integer" do + obj = mock("a string") + obj.should_receive(:to_int).and_return("asdf") + + -> { @bignum >> obj }.should raise_error(TypeError) + end + + it "raises a TypeError when passed nil" do + -> { @bignum >> nil }.should raise_error(TypeError) + end + + it "raises a TypeError when passed a String" do + -> { @bignum >> "4" }.should raise_error(TypeError) + end + end + + context "when m is a bignum or larger than int" do + it "returns -1 when m > 0 and n < 0" do + (-1 >> bignum_value).should == -1 + (-1 >> (2**40)).should == -1 + + (-bignum_value >> bignum_value).should == -1 + (-bignum_value >> (2**40)).should == -1 + end + + it "returns 0 when m > 0 and n >= 0" do + (0 >> bignum_value).should == 0 + (1 >> bignum_value).should == 0 + (bignum_value >> bignum_value).should == 0 + + (0 >> (2**40)).should == 0 + (1 >> (2**40)).should == 0 + (bignum_value >> (2**40)).should == 0 + end + + it "returns 0 when m < 0 long and n == 0" do + (0 >> -(2**40)).should == 0 + end + + it "returns 0 when m < 0 bignum and n == 0" do + (0 >> -bignum_value).should == 0 + end + + it "raises RangeError when m < 0 and n != 0" do + # https://bugs.ruby-lang.org/issues/18518#note-9 + limit = RUBY_ENGINE == 'ruby' ? 2**67 : 2**32 + + coerce_long = mock("long") + coerce_long.stub!(:to_int).and_return(-limit) + coerce_bignum = mock("bignum") + coerce_bignum.stub!(:to_int).and_return(-bignum_value) + exps = [-limit, coerce_long] + exps << -bignum_value << coerce_bignum if bignum_value >= limit + + exps.each { |exp| + -> { (1 >> exp) }.should raise_error(RangeError, 'shift width too big') + -> { (-1 >> exp) }.should raise_error(RangeError, 'shift width too big') + -> { (bignum_value >> exp) }.should raise_error(RangeError, 'shift width too big') + -> { (-bignum_value >> exp) }.should raise_error(RangeError, 'shift width too big') + } + end + end +end diff --git a/spec/ruby/core/integer/round_spec.rb b/spec/ruby/core/integer/round_spec.rb index 5a46e6cba6..189384f11a 100644 --- a/spec/ruby/core/integer/round_spec.rb +++ b/spec/ruby/core/integer/round_spec.rb @@ -1,18 +1,10 @@ -require File.expand_path('../../../spec_helper', __FILE__) -require File.expand_path('../shared/to_i', __FILE__) -require File.expand_path('../shared/integer_rounding', __FILE__) +require_relative '../../spec_helper' +require_relative 'shared/to_i' +require_relative 'shared/integer_rounding' describe "Integer#round" do - it_behaves_like(:integer_to_i, :round) - it_behaves_like(:integer_rounding_positive_precision, :round) - - ruby_version_is ""..."2.5" do # Not just since 2.4 - it "rounds itself as a float if passed a positive precision" do - [2, -4, 10**70, -10**100].each do |v| - v.round(42).should eql(v.to_f) - end - end - end + it_behaves_like :integer_to_i, :round + it_behaves_like :integer_rounding_positive_precision, :round # redmine:5228 it "returns itself rounded if passed a negative value" do @@ -29,26 +21,24 @@ describe "Integer#round" do (-25 * 10**70).round(-71).should eql(-30 * 10**70) end - platform_is_not wordsize: 32 do - it "raises a RangeError when passed a big negative value" do - lambda { 42.round(fixnum_min) }.should raise_error(RangeError) - end + it "raises a RangeError when passed a big negative value" do + -> { 42.round(min_long - 1) }.should raise_error(RangeError) end it "raises a RangeError when passed Float::INFINITY" do - lambda { 42.round(Float::INFINITY) }.should raise_error(RangeError) + -> { 42.round(Float::INFINITY) }.should raise_error(RangeError) end it "raises a RangeError when passed a beyond signed int" do - lambda { 42.round(1<<31) }.should raise_error(RangeError) + -> { 42.round(1<<31) }.should raise_error(RangeError) end it "raises a TypeError when passed a String" do - lambda { 42.round("4") }.should raise_error(TypeError) + -> { 42.round("4") }.should raise_error(TypeError) end it "raises a TypeError when its argument cannot be converted to an Integer" do - lambda { 42.round(nil) }.should raise_error(TypeError) + -> { 42.round(nil) }.should raise_error(TypeError) end it "calls #to_int on the argument to convert it to an Integer" do @@ -60,36 +50,32 @@ describe "Integer#round" do it "raises a TypeError when #to_int does not return an Integer" do obj = mock("Object") obj.stub!(:to_int).and_return([]) - lambda { 42.round(obj) }.should raise_error(TypeError) + -> { 42.round(obj) }.should raise_error(TypeError) end - ruby_version_is "2.4" do - it "returns different rounded values depending on the half option" do - 25.round(-1, half: :up).should eql(30) - 25.round(-1, half: :down).should eql(20) - 25.round(-1, half: :even).should eql(20) - 35.round(-1, half: :up).should eql(40) - 35.round(-1, half: :down).should eql(30) - 35.round(-1, half: :even).should eql(40) - (-25).round(-1, half: :up).should eql(-30) - (-25).round(-1, half: :down).should eql(-20) - (-25).round(-1, half: :even).should eql(-20) - end + it "returns different rounded values depending on the half option" do + 25.round(-1, half: :up).should eql(30) + 25.round(-1, half: :down).should eql(20) + 25.round(-1, half: :even).should eql(20) + 25.round(-1, half: nil).should eql(30) + 35.round(-1, half: :up).should eql(40) + 35.round(-1, half: :down).should eql(30) + 35.round(-1, half: :even).should eql(40) + 35.round(-1, half: nil).should eql(40) + (-25).round(-1, half: :up).should eql(-30) + (-25).round(-1, half: :down).should eql(-20) + (-25).round(-1, half: :even).should eql(-20) + (-25).round(-1, half: nil).should eql(-30) end - ruby_version_is "2.4"..."2.5" do - it "returns itself as a float if passed a positive precision and the half option" do - 35.round(1, half: :up).should eql(35.0) - 35.round(1, half: :down).should eql(35.0) - 35.round(1, half: :even).should eql(35.0) - end + it "returns itself if passed a positive precision and the half option" do + 35.round(1, half: :up).should eql(35) + 35.round(1, half: :down).should eql(35) + 35.round(1, half: :even).should eql(35) end - ruby_version_is "2.5" do - it "returns itself if passed a positive precision and the half option" do - 35.round(1, half: :up).should eql(35) - 35.round(1, half: :down).should eql(35) - 35.round(1, half: :even).should eql(35) - end + it "raises ArgumentError for an unknown rounding mode" do + -> { 42.round(-1, half: :foo) }.should raise_error(ArgumentError, /invalid rounding mode: foo/) + -> { 42.round(1, half: :foo) }.should raise_error(ArgumentError, /invalid rounding mode: foo/) end end diff --git a/spec/ruby/core/integer/shared/abs.rb b/spec/ruby/core/integer/shared/abs.rb new file mode 100644 index 0000000000..43844c9065 --- /dev/null +++ b/spec/ruby/core/integer/shared/abs.rb @@ -0,0 +1,18 @@ +describe :integer_abs, shared: true do + context "fixnum" do + it "returns self's absolute fixnum value" do + { 0 => [0, -0, +0], 2 => [2, -2, +2], 100 => [100, -100, +100] }.each do |key, values| + values.each do |value| + value.send(@method).should == key + end + end + end + end + + context "bignum" do + it "returns the absolute bignum value" do + bignum_value(39).send(@method).should == 18446744073709551655 + (-bignum_value(18)).send(@method).should == 18446744073709551634 + end + end +end diff --git a/spec/ruby/core/integer/shared/arithmetic_coerce.rb b/spec/ruby/core/integer/shared/arithmetic_coerce.rb new file mode 100644 index 0000000000..1260192df1 --- /dev/null +++ b/spec/ruby/core/integer/shared/arithmetic_coerce.rb @@ -0,0 +1,11 @@ +require_relative '../fixtures/classes' + +describe :integer_arithmetic_coerce_not_rescue, shared: true do + it "does not rescue exception raised in other#coerce" do + b = mock("numeric with failed #coerce") + b.should_receive(:coerce).and_raise(IntegerSpecs::CoerceError) + + # e.g. 1 + b + -> { 1.send(@method, b) }.should raise_error(IntegerSpecs::CoerceError) + end +end diff --git a/spec/ruby/core/integer/shared/arithmetic_exception_in_coerce.rb b/spec/ruby/core/integer/shared/arithmetic_exception_in_coerce.rb deleted file mode 100644 index dd3f55d444..0000000000 --- a/spec/ruby/core/integer/shared/arithmetic_exception_in_coerce.rb +++ /dev/null @@ -1,33 +0,0 @@ -require File.expand_path('../../fixtures/classes', __FILE__) - -describe :integer_arithmetic_exception_in_coerce, shared: true do - ruby_version_is ""..."2.5" do - it "rescues exception (StandardError and subclasses) raised in other#coerce and raises TypeError" do - b = mock("numeric with failed #coerce") - b.should_receive(:coerce).and_raise(IntegerSpecs::CoerceError) - - # e.g. 1 + b - -> { 1.send(@method, b) }.should raise_error(TypeError, /MockObject can't be coerced into #{1.class}/) - end - - it "does not rescue Exception and StandardError siblings raised in other#coerce" do - [Exception, NoMemoryError].each do |exception| - b = mock("numeric with failed #coerce") - b.should_receive(:coerce).and_raise(exception) - - # e.g. 1 + b - -> { 1.send(@method, b) }.should raise_error(exception) - end - end - end - - ruby_version_is "2.5" do - it "does not rescue exception raised in other#coerce" do - b = mock("numeric with failed #coerce") - b.should_receive(:coerce).and_raise(IntegerSpecs::CoerceError) - - # e.g. 1 + b - -> { 1.send(@method, b) }.should raise_error(IntegerSpecs::CoerceError) - end - end -end diff --git a/spec/ruby/core/integer/shared/comparison_coerce.rb b/spec/ruby/core/integer/shared/comparison_coerce.rb new file mode 100644 index 0000000000..af52f5e99b --- /dev/null +++ b/spec/ruby/core/integer/shared/comparison_coerce.rb @@ -0,0 +1,11 @@ +require_relative '../fixtures/classes' + +describe :integer_comparison_coerce_not_rescue, shared: true do + it "does not rescue exception raised in other#coerce" do + b = mock("numeric with failed #coerce") + b.should_receive(:coerce).and_raise(IntegerSpecs::CoerceError) + + # e.g. 1 > b + -> { 1.send(@method, b) }.should raise_error(IntegerSpecs::CoerceError) + end +end diff --git a/spec/ruby/core/integer/shared/comparison_exception_in_coerce.rb b/spec/ruby/core/integer/shared/comparison_exception_in_coerce.rb deleted file mode 100644 index f99d680d6d..0000000000 --- a/spec/ruby/core/integer/shared/comparison_exception_in_coerce.rb +++ /dev/null @@ -1,35 +0,0 @@ -require File.expand_path('../../fixtures/classes', __FILE__) - -describe :integer_comparison_exception_in_coerce, shared: true do - ruby_version_is ""..."2.5" do - it "rescues exception (StandardError and subclasses) raised in other#coerce and raises ArgumentError" do - b = mock("numeric with failed #coerce") - b.should_receive(:coerce).and_raise(IntegerSpecs::CoerceError) - - # e.g. 1 > b - -> { - -> { 1.send(@method, b) }.should raise_error(ArgumentError, /comparison of #{1.class} with MockObject failed/) - }.should complain(/Numerical comparison operators will no more rescue exceptions of #coerce/) - end - - it "does not rescue Exception and StandardError siblings raised in other#coerce" do - [Exception, NoMemoryError].each do |exception| - b = mock("numeric with failed #coerce") - b.should_receive(:coerce).and_raise(exception) - - # e.g. 1 > b - -> { 1.send(@method, b) }.should raise_error(exception) - end - end - end - - ruby_version_is "2.5" do - it "does not rescue exception raised in other#coerce" do - b = mock("numeric with failed #coerce") - b.should_receive(:coerce).and_raise(IntegerSpecs::CoerceError) - - # e.g. 1 > b - -> { 1.send(@method, b) }.should raise_error(IntegerSpecs::CoerceError) - end - end -end diff --git a/spec/ruby/core/integer/shared/equal.rb b/spec/ruby/core/integer/shared/equal.rb new file mode 100644 index 0000000000..ecee17831c --- /dev/null +++ b/spec/ruby/core/integer/shared/equal.rb @@ -0,0 +1,58 @@ +describe :integer_equal, shared: true do + context "fixnum" do + it "returns true if self has the same value as other" do + 1.send(@method, 1).should == true + 9.send(@method, 5).should == false + + # Actually, these call Float#==, Integer#== etc. + 9.send(@method, 9.0).should == true + 9.send(@method, 9.01).should == false + + 10.send(@method, bignum_value).should == false + end + + it "calls 'other == self' if the given argument is not an Integer" do + 1.send(@method, '*').should == false + + obj = mock('one other') + obj.should_receive(:==).any_number_of_times.and_return(false) + 1.send(@method, obj).should == false + + obj = mock('another') + obj.should_receive(:==).any_number_of_times.and_return(true) + 2.send(@method, obj).should == true + end + end + + context "bignum" do + before :each do + @bignum = bignum_value + end + + it "returns true if self has the same value as the given argument" do + @bignum.send(@method, @bignum).should == true + @bignum.send(@method, @bignum.to_f).should == true + + @bignum.send(@method, @bignum + 1).should == false + (@bignum + 1).send(@method, @bignum).should == false + + @bignum.send(@method, 9).should == false + @bignum.send(@method, 9.01).should == false + + @bignum.send(@method, bignum_value(10)).should == false + end + + it "calls 'other == self' if the given argument is not an Integer" do + obj = mock('not integer') + obj.should_receive(:==).and_return(true) + @bignum.send(@method, obj).should == true + end + + it "returns the result of 'other == self' as a boolean" do + obj = mock('not integer') + obj.should_receive(:==).exactly(2).times.and_return("woot", nil) + @bignum.send(@method, obj).should == true + @bignum.send(@method, obj).should == false + end + end +end diff --git a/spec/ruby/core/integer/shared/exponent.rb b/spec/ruby/core/integer/shared/exponent.rb new file mode 100644 index 0000000000..5ef6d686d8 --- /dev/null +++ b/spec/ruby/core/integer/shared/exponent.rb @@ -0,0 +1,144 @@ +describe :integer_exponent, shared: true do + context "fixnum" do + it "returns self raised to the given power" do + 2.send(@method, 0).should eql 1 + 2.send(@method, 1).should eql 2 + 2.send(@method, 2).should eql 4 + + 9.send(@method, 0.5).should eql 3.0 + 9.send(@method, Rational(1, 2)).should eql 3.0 + 5.send(@method, -1).to_f.to_s.should == '0.2' + + 2.send(@method, 40).should eql 1099511627776 + end + + it "overflows the answer to a bignum transparently" do + 2.send(@method, 29).should eql 536870912 + 2.send(@method, 30).should eql 1073741824 + 2.send(@method, 31).should eql 2147483648 + 2.send(@method, 32).should eql 4294967296 + + 2.send(@method, 61).should eql 2305843009213693952 + 2.send(@method, 62).should eql 4611686018427387904 + 2.send(@method, 63).should eql 9223372036854775808 + 2.send(@method, 64).should eql 18446744073709551616 + 8.send(@method, 23).should eql 590295810358705651712 + end + + it "raises negative numbers to the given power" do + (-2).send(@method, 29).should eql(-536870912) + (-2).send(@method, 30).should eql(1073741824) + (-2).send(@method, 31).should eql(-2147483648) + (-2).send(@method, 32).should eql(4294967296) + (-2).send(@method, 33).should eql(-8589934592) + + (-2).send(@method, 61).should eql(-2305843009213693952) + (-2).send(@method, 62).should eql(4611686018427387904) + (-2).send(@method, 63).should eql(-9223372036854775808) + (-2).send(@method, 64).should eql(18446744073709551616) + (-2).send(@method, 65).should eql(-36893488147419103232) + end + + it "can raise 1 to a bignum safely" do + 1.send(@method, 4611686018427387904).should eql 1 + end + + it "can raise -1 to a bignum safely" do + (-1).send(@method, 4611686018427387904).should eql(1) + (-1).send(@method, 4611686018427387905).should eql(-1) + end + + ruby_version_is ""..."3.4" do + it "returns Float::INFINITY when the number is too big" do + -> { + 2.send(@method, 427387904).should == Float::INFINITY + }.should complain(/warning: in a\*\*b, b may be too big/) + end + end + + ruby_version_is "3.4" do + it "raises an ArgumentError when the number is too big" do + -> { 100000000.send(@method, 1000000000) }.should raise_error(ArgumentError) + end + end + + it "raises a ZeroDivisionError for 0 ** -1" do + -> { 0.send(@method, -1) }.should raise_error(ZeroDivisionError) + -> { 0.send(@method, Rational(-1, 1)) }.should raise_error(ZeroDivisionError) + end + + it "returns Float::INFINITY for 0 ** -1.0" do + 0.send(@method, -1.0).should == Float::INFINITY + end + + it "raises a TypeError when given a non-numeric power" do + -> { 13.send(@method, "10") }.should raise_error(TypeError) + -> { 13.send(@method, :symbol) }.should raise_error(TypeError) + -> { 13.send(@method, nil) }.should raise_error(TypeError) + end + + it "coerces power and calls #**" do + num_2 = mock("2") + num_13 = mock("13") + num_2.should_receive(:coerce).with(13).and_return([num_13, num_2]) + num_13.should_receive(:**).with(num_2).and_return(169) + + 13.send(@method, num_2).should == 169 + end + + it "returns Float when power is Float" do + 2.send(@method, 2.0).should == 4.0 + end + + it "returns Rational when power is Rational" do + 2.send(@method, Rational(2, 1)).should == Rational(4, 1) + end + + it "returns a complex number when negative and raised to a fractional power" do + (-8).send(@method, 1.0/3) .should be_close(Complex(1, 1.73205), TOLERANCE) + (-8).send(@method, Rational(1, 3)).should be_close(Complex(1, 1.73205), TOLERANCE) + end + end + + context "bignum" do + before :each do + @bignum = bignum_value(47) + end + + it "returns self raised to other power" do + (@bignum.send(@method, 4)).should == 115792089237316196603666111261383895964500887398800252671052694326794607293761 + (@bignum.send(@method, 1.3)).should be_close(11109528802438156839288832.0, TOLERANCE) + end + + it "raises a TypeError when given a non-Integer" do + -> { @bignum.send(@method, mock('10')) }.should raise_error(TypeError) + -> { @bignum.send(@method, "10") }.should raise_error(TypeError) + -> { @bignum.send(@method, :symbol) }.should raise_error(TypeError) + end + + ruby_version_is ""..."3.4" do + it "switch to a Float when the values is too big" do + flt = nil + -> { + flt = @bignum.send(@method, @bignum) + }.should complain(/warning: in a\*\*b, b may be too big/) + flt.should be_kind_of(Float) + flt.infinite?.should == 1 + end + end + + ruby_version_is "3.4" do + it "does not switch to a Float when the values is too big" do + -> { + @bignum.send(@method, @bignum) + }.should raise_error(ArgumentError) + end + end + + it "returns a complex number when negative and raised to a fractional power" do + (-bignum_value).send(@method, (1.0/2)).should be_close(Complex(0.0, 4294967296.0), TOLERANCE) + (-@bignum).send(@method, (1.0/3)) .should be_close(Complex(1321122.9748145656, 2288252.1154253655), TOLERANCE) + (-@bignum).send(@method, Rational(1,3)).should be_close(Complex(1321122.9748145656, 2288252.1154253655), TOLERANCE) + end + end +end diff --git a/spec/ruby/core/integer/shared/integer_ceil_precision.rb b/spec/ruby/core/integer/shared/integer_ceil_precision.rb new file mode 100644 index 0000000000..9f31c2cf61 --- /dev/null +++ b/spec/ruby/core/integer/shared/integer_ceil_precision.rb @@ -0,0 +1,43 @@ +describe :integer_ceil_precision, shared: true do + context "precision is zero" do + it "returns integer self" do + send(@method, 0).ceil(0).should.eql?(0) + send(@method, 123).ceil(0).should.eql?(123) + send(@method, -123).ceil(0).should.eql?(-123) + end + end + + context "precision is positive" do + it "returns self" do + send(@method, 0).ceil(1).should.eql?(send(@method, 0)) + send(@method, 0).ceil(10).should.eql?(send(@method, 0)) + + send(@method, 123).ceil(10).should.eql?(send(@method, 123)) + send(@method, -123).ceil(10).should.eql?(send(@method, -123)) + end + end + + context "precision is negative" do + it "always returns 0 when self is 0" do + send(@method, 0).ceil(-1).should.eql?(0) + send(@method, 0).ceil(-10).should.eql?(0) + end + + it "returns largest integer less than self with at least precision.abs trailing zeros" do + send(@method, 123).ceil(-1).should.eql?(130) + send(@method, 123).ceil(-2).should.eql?(200) + send(@method, 123).ceil(-3).should.eql?(1000) + + send(@method, -123).ceil(-1).should.eql?(-120) + send(@method, -123).ceil(-2).should.eql?(-100) + send(@method, -123).ceil(-3).should.eql?(0) + end + + ruby_bug "#20654", ""..."3.4" do + it "returns 10**precision.abs when precision.abs is larger than the number digits of self" do + send(@method, 123).ceil(-20).should.eql?(100000000000000000000) + send(@method, 123).ceil(-50).should.eql?(100000000000000000000000000000000000000000000000000) + end + end + end +end diff --git a/spec/ruby/core/integer/shared/integer_floor_precision.rb b/spec/ruby/core/integer/shared/integer_floor_precision.rb new file mode 100644 index 0000000000..4c5888c6c4 --- /dev/null +++ b/spec/ruby/core/integer/shared/integer_floor_precision.rb @@ -0,0 +1,43 @@ +describe :integer_floor_precision, shared: true do + context "precision is zero" do + it "returns integer self" do + send(@method, 0).floor(0).should.eql?(0) + send(@method, 123).floor(0).should.eql?(123) + send(@method, -123).floor(0).should.eql?(-123) + end + end + + context "precision is positive" do + it "returns self" do + send(@method, 0).floor(1).should.eql?(send(@method, 0)) + send(@method, 0).floor(10).should.eql?(send(@method, 0)) + + send(@method, 123).floor(10).should.eql?(send(@method, 123)) + send(@method, -123).floor(10).should.eql?(send(@method, -123)) + end + end + + context "precision is negative" do + it "always returns 0 when self is 0" do + send(@method, 0).floor(-1).should.eql?(0) + send(@method, 0).floor(-10).should.eql?(0) + end + + it "returns largest integer less than self with at least precision.abs trailing zeros" do + send(@method, 123).floor(-1).should.eql?(120) + send(@method, 123).floor(-2).should.eql?(100) + send(@method, 123).floor(-3).should.eql?(0) + + send(@method, -123).floor(-1).should.eql?(-130) + send(@method, -123).floor(-2).should.eql?(-200) + send(@method, -123).floor(-3).should.eql?(-1000) + end + + ruby_bug "#20654", ""..."3.4" do + it "returns -(10**precision.abs) when self is negative and precision.abs is larger than the number digits of self" do + send(@method, -123).floor(-20).should.eql?(-100000000000000000000) + send(@method, -123).floor(-50).should.eql?(-100000000000000000000000000000000000000000000000000) + end + end + end +end diff --git a/spec/ruby/core/integer/shared/integer_rounding.rb b/spec/ruby/core/integer/shared/integer_rounding.rb index ecbda1bb4a..56d1819f84 100644 --- a/spec/ruby/core/integer/shared/integer_rounding.rb +++ b/spec/ruby/core/integer/shared/integer_rounding.rb @@ -5,27 +5,15 @@ describe :integer_rounding_positive_precision, shared: true do end end - ruby_version_is "2.4" do - it "returns self if passed a precision of zero" do - [2, -4, 10**70, -10**100].each do |v| - v.send(@method, 0).should eql(v) - end - end - end - - ruby_version_is "2.4"..."2.5" do - it "returns itself as a float if passed a positive precision" do - [2, -4, 10**70, -10**100].each do |v| - v.send(@method, 42).should eql(v.to_f) - end + it "returns self if passed a precision of zero" do + [2, -4, 10**70, -10**100].each do |v| + v.send(@method, 0).should eql(v) end end - ruby_version_is "2.5" do - it "returns itself if passed a positive precision" do - [2, -4, 10**70, -10**100].each do |v| - v.send(@method, 42).should eql(v) - end + it "returns itself if passed a positive precision" do + [2, -4, 10**70, -10**100].each do |v| + v.send(@method, 42).should eql(v) end end end diff --git a/spec/ruby/core/integer/shared/modulo.rb b/spec/ruby/core/integer/shared/modulo.rb new file mode 100644 index 0000000000..d91af1e924 --- /dev/null +++ b/spec/ruby/core/integer/shared/modulo.rb @@ -0,0 +1,114 @@ +describe :integer_modulo, shared: true do + context "fixnum" do + it "returns the modulus obtained from dividing self by the given argument" do + # test all possible combinations: + # - integer/double/bignum argument + # - positive/negative argument + # - positive/negative self + # - self greater/smaller than argument + + 13.send(@method, 4).should == 1 + 4.send(@method, 13).should == 4 + + 13.send(@method, 4.0).should == 1 + 4.send(@method, 13.0).should == 4 + + (-200).send(@method, 256).should == 56 + (-1000).send(@method, 512).should == 24 + + (-200).send(@method, -256).should == -200 + (-1000).send(@method, -512).should == -488 + + (200).send(@method, -256).should == -56 + (1000).send(@method, -512).should == -24 + + 13.send(@method, -4.0).should == -3.0 + 4.send(@method, -13.0).should == -9.0 + + -13.send(@method, -4.0).should == -1.0 + -4.send(@method, -13.0).should == -4.0 + + -13.send(@method, 4.0).should == 3.0 + -4.send(@method, 13.0).should == 9.0 + + 1.send(@method, 2.0).should == 1.0 + 200.send(@method, bignum_value).should == 200 + + 4.send(@method, bignum_value(10)).should == 4 + 4.send(@method, -bignum_value(10)).should == -18446744073709551622 + -4.send(@method, bignum_value(10)).should == 18446744073709551622 + -4.send(@method, -bignum_value(10)).should == -4 + end + + it "raises a ZeroDivisionError when the given argument is 0" do + -> { 13.send(@method, 0) }.should raise_error(ZeroDivisionError) + -> { 0.send(@method, 0) }.should raise_error(ZeroDivisionError) + -> { -10.send(@method, 0) }.should raise_error(ZeroDivisionError) + end + + it "raises a ZeroDivisionError when the given argument is 0 and a Float" do + -> { 0.send(@method, 0.0) }.should raise_error(ZeroDivisionError) + -> { 10.send(@method, 0.0) }.should raise_error(ZeroDivisionError) + -> { -10.send(@method, 0.0) }.should raise_error(ZeroDivisionError) + end + + it "raises a TypeError when given a non-Integer" do + -> { + (obj = mock('10')).should_receive(:to_int).any_number_of_times.and_return(10) + 13.send(@method, obj) + }.should raise_error(TypeError) + -> { 13.send(@method, "10") }.should raise_error(TypeError) + -> { 13.send(@method, :symbol) }.should raise_error(TypeError) + end + end + + context "bignum" do + before :each do + @bignum = bignum_value(10) + end + + it "returns the modulus obtained from dividing self by the given argument" do + # test all possible combinations: + # - integer/double/bignum argument + # - positive/negative argument + # - positive/negative self + # - self greater/smaller than argument + + @bignum.send(@method, 5).should == 1 + @bignum.send(@method, -5).should == -4 + (-@bignum).send(@method, 5).should == 4 + (-@bignum).send(@method, -5).should == -1 + + @bignum.send(@method, 2.22).should be_close(1.5603603603605034, TOLERANCE) + @bignum.send(@method, -2.22).should be_close(-0.6596396396394968, TOLERANCE) + (-@bignum).send(@method, 2.22).should be_close(0.6596396396394968, TOLERANCE) + (-@bignum).send(@method, -2.22).should be_close(-1.5603603603605034, TOLERANCE) + + @bignum.send(@method, @bignum + 10).should == 18446744073709551626 + @bignum.send(@method, -(@bignum + 10)).should == -10 + (-@bignum).send(@method, @bignum + 10).should == 10 + (-@bignum).send(@method, -(@bignum + 10)).should == -18446744073709551626 + + (@bignum + 10).send(@method, @bignum).should == 10 + (@bignum + 10).send(@method, -@bignum).should == -18446744073709551616 + (-(@bignum + 10)).send(@method, @bignum).should == 18446744073709551616 + (-(@bignum + 10)).send(@method, -@bignum).should == -10 + end + + it "raises a ZeroDivisionError when the given argument is 0" do + -> { @bignum.send(@method, 0) }.should raise_error(ZeroDivisionError) + -> { (-@bignum).send(@method, 0) }.should raise_error(ZeroDivisionError) + end + + it "raises a ZeroDivisionError when the given argument is 0 and a Float" do + -> { @bignum.send(@method, 0.0) }.should raise_error(ZeroDivisionError) + -> { -@bignum.send(@method, 0.0) }.should raise_error(ZeroDivisionError) + end + + it "raises a TypeError when given a non-Integer" do + -> { @bignum.send(@method, mock('10')) }.should raise_error(TypeError) + -> { @bignum.send(@method, "10") }.should raise_error(TypeError) + -> { @bignum.send(@method, :symbol) }.should raise_error(TypeError) + end + end +end diff --git a/spec/ruby/core/integer/size_spec.rb b/spec/ruby/core/integer/size_spec.rb new file mode 100644 index 0000000000..725e9eb062 --- /dev/null +++ b/spec/ruby/core/integer/size_spec.rb @@ -0,0 +1,34 @@ +require_relative '../../spec_helper' + +describe "Integer#size" do + platform_is c_long_size: 32 do + it "returns the number of bytes in the machine representation of self" do + -1.size.should == 4 + 0.size.should == 4 + 4091.size.should == 4 + end + end + + platform_is c_long_size: 64 do + it "returns the number of bytes in the machine representation of self" do + -1.size.should == 8 + 0.size.should == 8 + 4091.size.should == 8 + end + end + + context "bignum" do + it "returns the number of bytes required to hold the unsigned bignum data" do + # that is, n such that 256 * n <= val.abs < 256 * (n+1) + (256**7).size.should == 8 + (256**8).size.should == 9 + (256**9).size.should == 10 + (256**10).size.should == 11 + (256**10-1).size.should == 10 + (256**11).size.should == 12 + (256**12).size.should == 13 + (256**20-1).size.should == 20 + (256**40-1).size.should == 40 + end + end +end diff --git a/spec/ruby/core/integer/sqrt_spec.rb b/spec/ruby/core/integer/sqrt_spec.rb index b7d9ef441b..4149ca2cc9 100644 --- a/spec/ruby/core/integer/sqrt_spec.rb +++ b/spec/ruby/core/integer/sqrt_spec.rb @@ -1,33 +1,31 @@ -require File.expand_path('../../../spec_helper', __FILE__) +require_relative '../../spec_helper' -ruby_version_is "2.5" do - describe "Integer.sqrt" do - it "returns an integer" do - Integer.sqrt(10).should be_kind_of(Integer) - end +describe "Integer.sqrt" do + it "returns an integer" do + Integer.sqrt(10).should be_kind_of(Integer) + end - it "returns the integer square root of the argument" do - Integer.sqrt(0).should == 0 - Integer.sqrt(1).should == 1 - Integer.sqrt(24).should == 4 - Integer.sqrt(25).should == 5 - Integer.sqrt(10**400).should == 10**200 - end + it "returns the integer square root of the argument" do + Integer.sqrt(0).should == 0 + Integer.sqrt(1).should == 1 + Integer.sqrt(24).should == 4 + Integer.sqrt(25).should == 5 + Integer.sqrt(10**400).should == 10**200 + end - it "raises a Math::DomainError if the argument is negative" do - lambda { Integer.sqrt(-4) }.should raise_error(Math::DomainError) - end + it "raises a Math::DomainError if the argument is negative" do + -> { Integer.sqrt(-4) }.should raise_error(Math::DomainError) + end - it "accepts any argument that can be coerced to Integer" do - Integer.sqrt(10.0).should == 3 - end + it "accepts any argument that can be coerced to Integer" do + Integer.sqrt(10.0).should == 3 + end - it "converts the argument with #to_int" do - Integer.sqrt(mock_int(10)).should == 3 - end + it "converts the argument with #to_int" do + Integer.sqrt(mock_int(10)).should == 3 + end - it "raises a TypeError if the argument cannot be coerced to Integer" do - lambda { Integer.sqrt("test") }.should raise_error(TypeError) - end + it "raises a TypeError if the argument cannot be coerced to Integer" do + -> { Integer.sqrt("test") }.should raise_error(TypeError) end end diff --git a/spec/ruby/core/integer/succ_spec.rb b/spec/ruby/core/integer/succ_spec.rb index a1405684fc..9ae9a14fe7 100644 --- a/spec/ruby/core/integer/succ_spec.rb +++ b/spec/ruby/core/integer/succ_spec.rb @@ -1,6 +1,6 @@ -require File.expand_path('../../../spec_helper', __FILE__) -require File.expand_path('../shared/next', __FILE__) +require_relative '../../spec_helper' +require_relative 'shared/next' describe "Integer#succ" do - it_behaves_like(:integer_next, :succ) + it_behaves_like :integer_next, :succ end diff --git a/spec/ruby/core/integer/times_spec.rb b/spec/ruby/core/integer/times_spec.rb index d426bc008c..356340592b 100644 --- a/spec/ruby/core/integer/times_spec.rb +++ b/spec/ruby/core/integer/times_spec.rb @@ -1,4 +1,4 @@ -require File.expand_path('../../../spec_helper', __FILE__) +require_relative '../../spec_helper' describe "Integer#times" do it "returns self" do diff --git a/spec/ruby/core/integer/to_f_spec.rb b/spec/ruby/core/integer/to_f_spec.rb new file mode 100644 index 0000000000..9f1df9ada9 --- /dev/null +++ b/spec/ruby/core/integer/to_f_spec.rb @@ -0,0 +1,23 @@ +require_relative '../../spec_helper' + +describe "Integer#to_f" do + context "fixnum" do + it "returns self converted to a Float" do + 0.to_f.should == 0.0 + -500.to_f.should == -500.0 + 9_641_278.to_f.should == 9641278.0 + end + end + + context "bignum" do + it "returns self converted to a Float" do + bignum_value(0x4000_0aa0_0bb0_0000).to_f.should eql(23_058_441_774_644_068_352.0) + bignum_value(0x8000_0000_0000_0ccc).to_f.should eql(27_670_116_110_564_330_700.0) + (-bignum_value(99)).to_f.should eql(-18_446_744_073_709_551_715.0) + end + + it "converts number close to Float::MAX without exceeding MAX or producing NaN" do + (10**308).to_f.should == 10.0 ** 308 + end + end +end diff --git a/spec/ruby/core/integer/to_i_spec.rb b/spec/ruby/core/integer/to_i_spec.rb index 1e6f21f191..1a8e477b3c 100644 --- a/spec/ruby/core/integer/to_i_spec.rb +++ b/spec/ruby/core/integer/to_i_spec.rb @@ -1,6 +1,6 @@ -require File.expand_path('../../../spec_helper', __FILE__) -require File.expand_path('../shared/to_i', __FILE__) +require_relative '../../spec_helper' +require_relative 'shared/to_i' describe "Integer#to_i" do - it_behaves_like(:integer_to_i, :to_i) + it_behaves_like :integer_to_i, :to_i end diff --git a/spec/ruby/core/integer/to_int_spec.rb b/spec/ruby/core/integer/to_int_spec.rb index 5e87c86ccd..4b7eccad3a 100644 --- a/spec/ruby/core/integer/to_int_spec.rb +++ b/spec/ruby/core/integer/to_int_spec.rb @@ -1,6 +1,6 @@ -require File.expand_path('../../../spec_helper', __FILE__) -require File.expand_path('../shared/to_i', __FILE__) +require_relative '../../spec_helper' +require_relative 'shared/to_i' describe "Integer#to_int" do - it_behaves_like(:integer_to_i, :to_int) + it_behaves_like :integer_to_i, :to_int end diff --git a/spec/ruby/core/integer/to_r_spec.rb b/spec/ruby/core/integer/to_r_spec.rb index 0eb158b100..7732bedca1 100644 --- a/spec/ruby/core/integer/to_r_spec.rb +++ b/spec/ruby/core/integer/to_r_spec.rb @@ -1,4 +1,4 @@ -require File.expand_path('../../../spec_helper', __FILE__) +require_relative '../../spec_helper' describe "Integer#to_r" do it "returns a Rational object" do @@ -15,12 +15,12 @@ describe "Integer#to_r" do it "works even if self is a Bignum" do bignum = 99999**999 - bignum.should be_an_instance_of(Bignum) + bignum.should be_an_instance_of(Integer) bignum.to_r.should == Rational(bignum, 1) end it "raises an ArgumentError if given any arguments" do - lambda { 287.to_r(2) }.should raise_error(ArgumentError) - lambda { 9102826.to_r(309, [], 71) }.should raise_error(ArgumentError) + -> { 287.to_r(2) }.should raise_error(ArgumentError) + -> { 9102826.to_r(309, [], 71) }.should raise_error(ArgumentError) end end diff --git a/spec/ruby/core/integer/to_s_spec.rb b/spec/ruby/core/integer/to_s_spec.rb new file mode 100644 index 0000000000..ca08dad95c --- /dev/null +++ b/spec/ruby/core/integer/to_s_spec.rb @@ -0,0 +1,95 @@ +require_relative '../../spec_helper' + +describe "Integer#to_s" do + context "fixnum" do + context "when given a base" do + it "returns self converted to a String in the given base" do + 12345.to_s(2).should == "11000000111001" + 12345.to_s(8).should == "30071" + 12345.to_s(10).should == "12345" + 12345.to_s(16).should == "3039" + 95.to_s(16).should == "5f" + 12345.to_s(36).should == "9ix" + end + + it "raises an ArgumentError if the base is less than 2 or higher than 36" do + -> { 123.to_s(-1) }.should raise_error(ArgumentError) + -> { 123.to_s(0) }.should raise_error(ArgumentError) + -> { 123.to_s(1) }.should raise_error(ArgumentError) + -> { 123.to_s(37) }.should raise_error(ArgumentError) + end + end + + context "when no base given" do + it "returns self converted to a String using base 10" do + 255.to_s.should == '255' + 3.to_s.should == '3' + 0.to_s.should == '0' + -9002.to_s.should == '-9002' + end + end + + before :each do + @internal = Encoding.default_internal + end + + after :each do + Encoding.default_internal = @internal + end + + it "returns a String in US-ASCII encoding when Encoding.default_internal is nil" do + Encoding.default_internal = nil + 1.to_s.encoding.should equal(Encoding::US_ASCII) + end + + it "returns a String in US-ASCII encoding when Encoding.default_internal is not nil" do + Encoding.default_internal = Encoding::IBM437 + 1.to_s.encoding.should equal(Encoding::US_ASCII) + end + end + + context "bignum" do + describe "when given a base" do + it "returns self converted to a String using the given base" do + a = 2**64 + a.to_s(2).should == "10000000000000000000000000000000000000000000000000000000000000000" + a.to_s(8).should == "2000000000000000000000" + a.to_s(16).should == "10000000000000000" + a.to_s(32).should == "g000000000000" + end + + it "raises an ArgumentError if the base is less than 2 or higher than 36" do + -> { 123.to_s(-1) }.should raise_error(ArgumentError) + -> { 123.to_s(0) }.should raise_error(ArgumentError) + -> { 123.to_s(1) }.should raise_error(ArgumentError) + -> { 123.to_s(37) }.should raise_error(ArgumentError) + end + end + + describe "when given no base" do + it "returns self converted to a String using base 10" do + bignum_value(9).to_s.should == "18446744073709551625" + bignum_value.to_s.should == "18446744073709551616" + (-bignum_value(675)).to_s.should == "-18446744073709552291" + end + end + + before :each do + @internal = Encoding.default_internal + end + + after :each do + Encoding.default_internal = @internal + end + + it "returns a String in US-ASCII encoding when Encoding.default_internal is nil" do + Encoding.default_internal = nil + bignum_value.to_s.encoding.should equal(Encoding::US_ASCII) + end + + it "returns a String in US-ASCII encoding when Encoding.default_internal is not nil" do + Encoding.default_internal = Encoding::IBM437 + bignum_value.to_s.encoding.should equal(Encoding::US_ASCII) + end + end +end diff --git a/spec/ruby/core/integer/truncate_spec.rb b/spec/ruby/core/integer/truncate_spec.rb index 429ab1a312..db16e74be4 100644 --- a/spec/ruby/core/integer/truncate_spec.rb +++ b/spec/ruby/core/integer/truncate_spec.rb @@ -1,21 +1,19 @@ -require File.expand_path('../../../spec_helper', __FILE__) -require File.expand_path('../shared/to_i', __FILE__) -require File.expand_path('../shared/integer_rounding', __FILE__) +require_relative '../../spec_helper' +require_relative 'shared/to_i' +require_relative 'shared/integer_rounding' describe "Integer#truncate" do - it_behaves_like(:integer_to_i, :truncate) - it_behaves_like(:integer_rounding_positive_precision, :truncate) + it_behaves_like :integer_to_i, :truncate + it_behaves_like :integer_rounding_positive_precision, :truncate - ruby_version_is "2.4" do - context "precision argument specified as part of the truncate method is negative" do - it "returns an integer with at least precision.abs trailing zeros" do - 1832.truncate(-1).should eql(1830) - 1832.truncate(-2).should eql(1800) - 1832.truncate(-3).should eql(1000) - -1832.truncate(-1).should eql(-1830) - -1832.truncate(-2).should eql(-1800) - -1832.truncate(-3).should eql(-1000) - end + context "precision argument specified as part of the truncate method is negative" do + it "returns an integer with at least precision.abs trailing zeros" do + 1832.truncate(-1).should eql(1830) + 1832.truncate(-2).should eql(1800) + 1832.truncate(-3).should eql(1000) + -1832.truncate(-1).should eql(-1830) + -1832.truncate(-2).should eql(-1800) + -1832.truncate(-3).should eql(-1000) end end end diff --git a/spec/ruby/core/integer/try_convert_spec.rb b/spec/ruby/core/integer/try_convert_spec.rb new file mode 100644 index 0000000000..8a0ca671a9 --- /dev/null +++ b/spec/ruby/core/integer/try_convert_spec.rb @@ -0,0 +1,48 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' + +describe "Integer.try_convert" do + it "returns the argument if it's an Integer" do + x = 42 + Integer.try_convert(x).should equal(x) + end + + it "returns nil when the argument does not respond to #to_int" do + Integer.try_convert(Object.new).should be_nil + end + + it "sends #to_int to the argument and returns the result if it's nil" do + obj = mock("to_int") + obj.should_receive(:to_int).and_return(nil) + Integer.try_convert(obj).should be_nil + end + + it "sends #to_int to the argument and returns the result if it's an Integer" do + x = 234 + obj = mock("to_int") + obj.should_receive(:to_int).and_return(x) + Integer.try_convert(obj).should equal(x) + end + + it "sends #to_int to the argument and raises TypeError if it's not a kind of Integer" do + obj = mock("to_int") + obj.should_receive(:to_int).and_return(Object.new) + -> { + Integer.try_convert obj + }.should raise_error(TypeError, "can't convert MockObject to Integer (MockObject#to_int gives Object)") + end + + it "responds with a different error message when it raises a TypeError, depending on the type of the non-Integer object :to_int returns" do + obj = mock("to_int") + obj.should_receive(:to_int).and_return("A String") + -> { + Integer.try_convert obj + }.should raise_error(TypeError, "can't convert MockObject to Integer (MockObject#to_int gives String)") + end + + it "does not rescue exceptions raised by #to_int" do + obj = mock("to_int") + obj.should_receive(:to_int).and_raise(RuntimeError) + -> { Integer.try_convert obj }.should raise_error(RuntimeError) + end +end diff --git a/spec/ruby/core/integer/uminus_spec.rb b/spec/ruby/core/integer/uminus_spec.rb new file mode 100644 index 0000000000..7a9cfe89bf --- /dev/null +++ b/spec/ruby/core/integer/uminus_spec.rb @@ -0,0 +1,30 @@ +require_relative '../../spec_helper' + +describe "Integer#-@" do + context "fixnum" do + it "returns self as a negative value" do + 2.send(:-@).should == -2 + -2.should == -2 + -268435455.should == -268435455 + (--5).should == 5 + -8.send(:-@).should == 8 + end + + it "negates self at Fixnum/Bignum boundaries" do + (-fixnum_max).should == (0 - fixnum_max) + (-fixnum_max).should < 0 + (-fixnum_min).should == (0 - fixnum_min) + (-fixnum_min).should > 0 + end + end + + context "bignum" do + it "returns self as a negative value" do + bignum_value.send(:-@).should == -18446744073709551616 + (-bignum_value).send(:-@).should == 18446744073709551616 + + bignum_value(921).send(:-@).should == -18446744073709552537 + (-bignum_value(921).send(:-@)).should == 18446744073709552537 + end + end +end diff --git a/spec/ruby/core/integer/upto_spec.rb b/spec/ruby/core/integer/upto_spec.rb index 405f8e4817..dd6995e94b 100644 --- a/spec/ruby/core/integer/upto_spec.rb +++ b/spec/ruby/core/integer/upto_spec.rb @@ -1,6 +1,6 @@ -require File.expand_path('../../../spec_helper', __FILE__) +require_relative '../../spec_helper' -describe "Integer#upto [stop] when self and stop are Fixnums" do +describe "Integer#upto [stop] when self and stop are Integers" do it "does not yield when stop is less than self" do result = [] 5.upto(4) { |x| result << x } @@ -27,8 +27,8 @@ describe "Integer#upto [stop] when self and stop are Fixnums" do end it "raises an ArgumentError for non-numeric endpoints" do - lambda { 1.upto("A") {|x| p x} }.should raise_error(ArgumentError) - lambda { 1.upto(nil) {|x| p x} }.should raise_error(ArgumentError) + -> { 1.upto("A") {|x| p x} }.should raise_error(ArgumentError) + -> { 1.upto(nil) {|x| p x} }.should raise_error(ArgumentError) end describe "when no block is given" do @@ -45,9 +45,9 @@ describe "Integer#upto [stop] when self and stop are Fixnums" do describe "size" do it "raises an ArgumentError for non-numeric endpoints" do enum = 1.upto("A") - lambda { enum.size }.should raise_error(ArgumentError) + -> { enum.size }.should raise_error(ArgumentError) enum = 1.upto(nil) - lambda { enum.size }.should raise_error(ArgumentError) + -> { enum.size }.should raise_error(ArgumentError) end it "returns stop - self + 1" do diff --git a/spec/ruby/core/integer/zero_spec.rb b/spec/ruby/core/integer/zero_spec.rb new file mode 100644 index 0000000000..bd362c4181 --- /dev/null +++ b/spec/ruby/core/integer/zero_spec.rb @@ -0,0 +1,13 @@ +require_relative '../../spec_helper' + +describe "Integer#zero?" do + it "returns true if self is 0" do + 0.should.zero? + 1.should_not.zero? + -1.should_not.zero? + end + + it "Integer#zero? overrides Numeric#zero?" do + 42.method(:zero?).owner.should == Integer + end +end |
