diff options
Diffstat (limited to 'spec/ruby/core/integer')
79 files changed, 4291 insertions, 0 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 new file mode 100644 index 0000000000..edce4b15e7 --- /dev/null +++ b/spec/ruby/core/integer/allbits_spec.rb @@ -0,0 +1,37 @@ +require_relative '../../spec_helper' + +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 "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 + -> { + (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 new file mode 100644 index 0000000000..e0449074a2 --- /dev/null +++ b/spec/ruby/core/integer/anybits_spec.rb @@ -0,0 +1,36 @@ +require_relative '../../spec_helper' + +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 "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 + -> { + (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 new file mode 100644 index 0000000000..eb633fba78 --- /dev/null +++ b/spec/ruby/core/integer/ceil_spec.rb @@ -0,0 +1,24 @@ +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 + + 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 new file mode 100644 index 0000000000..39cafe2874 --- /dev/null +++ b/spec/ruby/core/integer/chr_spec.rb @@ -0,0 +1,257 @@ +require_relative '../../spec_helper' + +describe "Integer#chr without argument" do + it "returns a String" do + 17.chr.should be_an_instance_of(String) + end + + it "returns a new String for each call" do + 82.chr.should_not equal(82.chr) + end + + it "raises a RangeError is self is less than 0" do + -> { -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 + describe "and self is between 0 and 127 (inclusive)" do + it "returns a US-ASCII String" do + (0..127).each do |c| + c.chr.encoding.should == Encoding::US_ASCII + end + end + + it "returns a String encoding self interpreted as a US-ASCII codepoint" do + (0..127).each do |c| + c.chr.bytes.to_a.should == [c] + end + end + end + + describe "and self is between 128 and 255 (inclusive)" do + it "returns a binary String" do + (128..255).each do |c| + c.chr.encoding.should == Encoding::BINARY + end + end + + it "returns a String containing self interpreted as a byte" do + (128..255).each do |c| + c.chr.bytes.to_a.should == [c] + end + end + end + + it "raises a RangeError is self is greater than 255" do + -> { 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 + + describe "when Encoding.default_internal is not nil" do + before do + @default_internal = Encoding.default_internal + end + + after do + Encoding.default_internal = @default_internal + end + + describe "and self is between 0 and 127 (inclusive)" do + it "returns a US-ASCII String" do + (0..127).each do |c| + Encoding.default_internal = Encoding::UTF_8 + c.chr.encoding.should == Encoding::US_ASCII + + Encoding.default_internal = Encoding::SHIFT_JIS + c.chr.encoding.should == Encoding::US_ASCII + end + end + + it "returns a String encoding self interpreted as a US-ASCII codepoint" do + (0..127).each do |c| + Encoding.default_internal = Encoding::UTF_8 + c.chr.bytes.to_a.should == [c] + + Encoding.default_internal = Encoding::SHIFT_JIS + c.chr.bytes.to_a.should == [c] + end + end + end + + describe "and self is between 128 and 255 (inclusive)" do + it "returns a binary String" do + (128..255).each do |c| + Encoding.default_internal = Encoding::UTF_8 + c.chr.encoding.should == Encoding::BINARY + + Encoding.default_internal = Encoding::SHIFT_JIS + c.chr.encoding.should == Encoding::BINARY + end + end + + it "returns a String containing self interpreted as a byte" do + (128..255).each do |c| + Encoding.default_internal = Encoding::UTF_8 + c.chr.bytes.to_a.should == [c] + + Encoding.default_internal = Encoding::SHIFT_JIS + c.chr.bytes.to_a.should == [c] + end + end + end + + describe "and self is greater than 255" do + it "returns a String with the default internal encoding" do + Encoding.default_internal = Encoding::UTF_8 + 0x0100.chr.encoding.should == Encoding::UTF_8 + 0x3000.chr.encoding.should == Encoding::UTF_8 + + Encoding.default_internal = Encoding::SHIFT_JIS + 0x8140.chr.encoding.should == Encoding::SHIFT_JIS + 0xFC4B.chr.encoding.should == Encoding::SHIFT_JIS + end + + it "returns a String encoding self interpreted as a codepoint in the default internal encoding" do + Encoding.default_internal = Encoding::UTF_8 + 0x0100.chr.bytes.to_a.should == [0xC4, 0x80] + 0x3000.chr.bytes.to_a.should == [0xE3, 0x80, 0x80] + + Encoding.default_internal = Encoding::SHIFT_JIS + 0x8140.chr.bytes.to_a.should == [0x81, 0x40] # Smallest assigned CP932 codepoint greater than 255 + 0xFC4B.chr.bytes.to_a.should == [0xFC, 0x4B] # Largest assigned CP932 codepoint + end + + # #5864 + it "raises RangeError if self is invalid as a codepoint in the default internal encoding" do + [ [0x0100, "US-ASCII"], + [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) + -> { integer.chr }.should raise_error(RangeError, /(invalid codepoint|out of char range)/) + end + end + end + end +end + +describe "Integer#chr with an encoding argument" do + it "returns a String" do + 900.chr(Encoding::UTF_8).should be_an_instance_of(String) + end + + it "returns a new String for each call" do + 8287.chr(Encoding::UTF_8).should_not equal(8287.chr(Encoding::UTF_8)) + end + + it "accepts a String as an argument" do + -> { 0xA4A2.chr('euc-jp') }.should_not raise_error + end + + it "converts a String to an Encoding as Encoding.find does" do + ['utf-8', 'UTF-8', 'Utf-8'].each do |encoding| + 7894.chr(encoding).encoding.should == Encoding::UTF_8 + end + end + + # http://redmine.ruby-lang.org/issues/4869 + it "raises a RangeError is self is less than 0" do + -> { -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::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 + 0x0080.chr(Encoding::UTF_8).encoding.should == Encoding::UTF_8 + 0x00FF.chr(Encoding::UTF_8).encoding.should == Encoding::UTF_8 + 0x0100.chr(Encoding::UTF_8).encoding.should == Encoding::UTF_8 + 0x3000.chr(Encoding::UTF_8).encoding.should == Encoding::UTF_8 + + 0x0000.chr(Encoding::SHIFT_JIS).encoding.should == Encoding::SHIFT_JIS + 0x007F.chr(Encoding::SHIFT_JIS).encoding.should == Encoding::SHIFT_JIS + 0x00A1.chr(Encoding::SHIFT_JIS).encoding.should == Encoding::SHIFT_JIS + 0x00DF.chr(Encoding::SHIFT_JIS).encoding.should == Encoding::SHIFT_JIS + 0x8140.chr(Encoding::SHIFT_JIS).encoding.should == Encoding::SHIFT_JIS + 0xFC4B.chr(Encoding::SHIFT_JIS).encoding.should == Encoding::SHIFT_JIS + end + + it "returns a String encoding self interpreted as a codepoint in the specified encoding" do + 0x0000.chr(Encoding::US_ASCII).bytes.to_a.should == [0x00] + 0x007F.chr(Encoding::US_ASCII).bytes.to_a.should == [0x7F] + + 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] + 0x0080.chr(Encoding::UTF_8).bytes.to_a.should == [0xC2, 0x80] + 0x00FF.chr(Encoding::UTF_8).bytes.to_a.should == [0xC3, 0xBF] + 0x0100.chr(Encoding::UTF_8).bytes.to_a.should == [0xC4, 0x80] + 0x3000.chr(Encoding::UTF_8).bytes.to_a.should == [0xE3, 0x80, 0x80] + + 0x0000.chr(Encoding::SHIFT_JIS).bytes.to_a.should == [0x00] + 0x007F.chr(Encoding::SHIFT_JIS).bytes.to_a.should == [0x7F] + 0x00A1.chr(Encoding::SHIFT_JIS).bytes.to_a.should == [0xA1] + 0x00DF.chr(Encoding::SHIFT_JIS).bytes.to_a.should == [0xDF] + 0x8140.chr(Encoding::SHIFT_JIS).bytes.to_a.should == [0x81, 0x40] # Smallest assigned CP932 codepoint greater than 255 + 0xFC4B.chr(Encoding::SHIFT_JIS).bytes.to_a.should == [0xFC, 0x4B] # Largest assigned CP932 codepoint + end + + # #5864 + it "raises RangeError if self is invalid as a codepoint in the specified encoding" do + -> { 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 new file mode 100644 index 0000000000..c1477d0757 --- /dev/null +++ b/spec/ruby/core/integer/denominator_spec.rb @@ -0,0 +1,20 @@ +require_relative '../../spec_helper' + +describe "Integer#denominator" do + # The Numeric child classes override this method, so their behaviour is + # specified in the appropriate place + before :each do + @numbers = [ + 20, # Integer + -2709, # Negative Integer + 99999999**99, # Bignum + -99999**621, # Negative BigNum + 0, + 1 + ] + end + + it "returns 1" do + @numbers.each {|number| number.denominator.should == 1} + end +end diff --git a/spec/ruby/core/integer/digits_spec.rb b/spec/ruby/core/integer/digits_spec.rb new file mode 100644 index 0000000000..3d0a64c25f --- /dev/null +++ b/spec/ruby/core/integer/digits_spec.rb @@ -0,0 +1,41 @@ +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 new file mode 100644 index 0000000000..0d5e16e986 --- /dev/null +++ b/spec/ruby/core/integer/divide_spec.rb @@ -0,0 +1,126 @@ +require_relative '../../spec_helper' +require_relative 'shared/arithmetic_coerce' + +describe "Integer#/" do + 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 new file mode 100644 index 0000000000..987704b02e --- /dev/null +++ b/spec/ruby/core/integer/downto_spec.rb @@ -0,0 +1,69 @@ +require_relative '../../spec_helper' + +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 } + result.should == [] + end + + it "yields once when stop equals self" do + result = [] + 5.downto(5) { |x| result << x } + result.should == [5] + end + + it "yields while decreasing self until it is less than stop" do + result = [] + 5.downto(2) { |x| result << x } + result.should == [5, 4, 3, 2] + end + + it "yields while decreasing self until it less than ceil for a Float endpoint" do + result = [] + 9.downto(1.3) {|i| result << i} + 3.downto(-1.3) {|i| result << i} + result.should == [9, 8, 7, 6, 5, 4, 3, 2, 3, 2, 1, 0, -1] + end + + it "raises an ArgumentError for invalid endpoints" do + -> {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 + it "returns an Enumerator" do + result = [] + + enum = 5.downto(2) + enum.each { |i| result << i } + + result.should == [5, 4, 3, 2] + end + + describe "returned Enumerator" do + describe "size" do + it "raises an ArgumentError for invalid endpoints" do + enum = 1.downto("A") + -> { enum.size }.should raise_error(ArgumentError) + enum = 1.downto(nil) + -> { enum.size }.should raise_error(ArgumentError) + end + + it "returns self - stop + 1" do + 10.downto(5).size.should == 6 + 10.downto(1).size.should == 10 + 10.downto(0).size.should == 11 + 0.downto(0).size.should == 1 + -3.downto(-5).size.should == 3 + end + + it "returns 0 when stop > self" do + 4.downto(5).size.should == 0 + -5.downto(0).size.should == 0 + -5.downto(-3).size.should == 0 + end + end + end + end +end diff --git a/spec/ruby/core/integer/dup_spec.rb b/spec/ruby/core/integer/dup_spec.rb new file mode 100644 index 0000000000..7f4d512465 --- /dev/null +++ b/spec/ruby/core/integer/dup_spec.rb @@ -0,0 +1,13 @@ +require_relative '../../spec_helper' + +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 new file mode 100644 index 0000000000..a314cc6b19 --- /dev/null +++ b/spec/ruby/core/integer/even_spec.rb @@ -0,0 +1,40 @@ +require_relative '../../spec_helper' + +describe "Integer#even?" do + 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 + 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 + + 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 + + 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/fixtures/classes.rb b/spec/ruby/core/integer/fixtures/classes.rb new file mode 100644 index 0000000000..6ebfbd1565 --- /dev/null +++ b/spec/ruby/core/integer/fixtures/classes.rb @@ -0,0 +1,4 @@ +module IntegerSpecs + class CoerceError < StandardError + end +end diff --git a/spec/ruby/core/integer/floor_spec.rb b/spec/ruby/core/integer/floor_spec.rb new file mode 100644 index 0000000000..8fb84d58cb --- /dev/null +++ b/spec/ruby/core/integer/floor_spec.rb @@ -0,0 +1,13 @@ +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 + + 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 new file mode 100644 index 0000000000..961f1c5c2e --- /dev/null +++ b/spec/ruby/core/integer/gcd_spec.rb @@ -0,0 +1,69 @@ +require_relative '../../spec_helper' + +describe "Integer#gcd" do + it "returns self if equal to the argument" do + 1.gcd(1).should == 1 + 398.gcd(398).should == 398 + end + + it "returns an Integer" do + 36.gcd(6).should be_kind_of(Integer) + 4.gcd(20981).should be_kind_of(Integer) + end + + it "returns the greatest common divisor of self and argument" do + 10.gcd(5).should == 5 + 200.gcd(20).should == 20 + end + + it "returns a positive integer even if self is negative" do + -12.gcd(6).should == 6 + -100.gcd(100).should == 100 + end + + it "returns a positive integer even if the argument is negative" do + 12.gcd(-6).should == 6 + 100.gcd(-100).should == 100 + end + + it "returns a positive integer even if both self and argument are negative" do + -12.gcd(-6).should == 6 + -100.gcd(-100).should == 100 + end + + it "accepts a Bignum argument" do + bignum = 9999**99 + 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(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 + -> { 12.gcd }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if given more than one argument" do + -> { 12.gcd(30, 20) }.should raise_error(ArgumentError) + end + + it "raises a TypeError unless the argument is an Integer" do + -> { 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 new file mode 100644 index 0000000000..ebf45e55a1 --- /dev/null +++ b/spec/ruby/core/integer/gcdlcm_spec.rb @@ -0,0 +1,53 @@ +require_relative '../../spec_helper' + +describe "Integer#gcdlcm" do + it "returns [self, self] if self is equal to the argument" do + 1.gcdlcm(1).should == [1, 1] + 398.gcdlcm(398).should == [398, 398] + end + + it "returns an Array" do + 36.gcdlcm(6).should be_kind_of(Array) + 4.gcdlcm(20981).should be_kind_of(Array) + end + + it "returns a two-element Array" do + 36.gcdlcm(876).size.should == 2 + 29.gcdlcm(17).size.should == 2 + end + + it "returns the greatest common divisor of self and argument as the first element" do + 10.gcdlcm(5)[0].should == 10.gcd(5) + 200.gcdlcm(20)[0].should == 200.gcd(20) + end + + it "returns the least common multiple of self and argument as the last element" do + 10.gcdlcm(5)[1].should == 10.lcm(5) + 200.gcdlcm(20)[1].should == 200.lcm(20) + end + + it "accepts a Bignum argument" do + bignum = 91999**99 + 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(Integer) + bignum.gcdlcm(99).should == [bignum.gcd(99), bignum.lcm(99)] + end + + it "raises an ArgumentError if not given an argument" do + -> { 12.gcdlcm }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if given more than one argument" do + -> { 12.gcdlcm(30, 20) }.should raise_error(ArgumentError) + end + + it "raises a TypeError unless the argument is an Integer" do + -> { 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 new file mode 100644 index 0000000000..f0179e566d --- /dev/null +++ b/spec/ruby/core/integer/gt_spec.rb @@ -0,0 +1,43 @@ +require_relative '../../spec_helper' +require_relative 'shared/comparison_coerce' + +describe "Integer#>" do + 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 new file mode 100644 index 0000000000..6237fc2c51 --- /dev/null +++ b/spec/ruby/core/integer/gte_spec.rb @@ -0,0 +1,43 @@ +require_relative '../../spec_helper' +require_relative 'shared/comparison_coerce' + +describe "Integer#>=" do + 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 new file mode 100644 index 0000000000..2d5d2e3e92 --- /dev/null +++ b/spec/ruby/core/integer/integer_spec.rb @@ -0,0 +1,20 @@ +require_relative '../../spec_helper' + +describe "Integer" do + it "includes Comparable" do + Integer.include?(Comparable).should == true + 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.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 new file mode 100644 index 0000000000..296a001c8c --- /dev/null +++ b/spec/ruby/core/integer/lcm_spec.rb @@ -0,0 +1,58 @@ +require_relative '../../spec_helper' + +describe "Integer#lcm" do + it "returns self if equal to the argument" do + 1.lcm(1).should == 1 + 398.lcm(398).should == 398 + end + + it "returns an Integer" do + 36.lcm(6).should be_kind_of(Integer) + 4.lcm(20981).should be_kind_of(Integer) + end + + it "returns the least common multiple of self and argument" do + 200.lcm(2001).should == 400200 + 99.lcm(90).should == 990 + end + + it "returns a positive integer even if self is negative" do + -12.lcm(6).should == 12 + -100.lcm(100).should == 100 + end + + it "returns a positive integer even if the argument is negative" do + 12.lcm(-6).should == 12 + 100.lcm(-100).should == 100 + end + + it "returns a positive integer even if both self and argument are negative" do + -12.lcm(-6).should == 12 + -100.lcm(-100).should == 100 + end + + it "accepts a Bignum argument" do + bignum = 9999**99 + 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(Integer) + bignum.lcm(99).should == bignum + end + + it "raises an ArgumentError if not given an argument" do + -> { 12.lcm }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if given more than one argument" do + -> { 12.lcm(30, 20) }.should raise_error(ArgumentError) + end + + it "raises a TypeError unless the argument is an Integer" do + -> { 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 new file mode 100644 index 0000000000..c182f82555 --- /dev/null +++ b/spec/ruby/core/integer/lt_spec.rb @@ -0,0 +1,45 @@ +require_relative '../../spec_helper' +require_relative 'shared/comparison_coerce' + +describe "Integer#<" do + 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 new file mode 100644 index 0000000000..65b71d77f2 --- /dev/null +++ b/spec/ruby/core/integer/lte_spec.rb @@ -0,0 +1,53 @@ +require_relative '../../spec_helper' +require_relative 'shared/comparison_coerce' + +describe "Integer#<=" do + 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 new file mode 100644 index 0000000000..6072ba7c8b --- /dev/null +++ b/spec/ruby/core/integer/minus_spec.rb @@ -0,0 +1,60 @@ +require_relative '../../spec_helper' +require_relative 'shared/arithmetic_coerce' + +describe "Integer#-" do + 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 new file mode 100644 index 0000000000..5037d27458 --- /dev/null +++ b/spec/ruby/core/integer/multiply_spec.rb @@ -0,0 +1,45 @@ +require_relative '../../spec_helper' +require_relative 'shared/arithmetic_coerce' + +describe "Integer#*" do + 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 new file mode 100644 index 0000000000..63c4e67893 --- /dev/null +++ b/spec/ruby/core/integer/next_spec.rb @@ -0,0 +1,6 @@ +require_relative '../../spec_helper' +require_relative 'shared/next' + +describe "Integer#next" do + 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 new file mode 100644 index 0000000000..685759ffa3 --- /dev/null +++ b/spec/ruby/core/integer/nobits_spec.rb @@ -0,0 +1,36 @@ +require_relative '../../spec_helper' + +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 "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 + -> { + (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 new file mode 100644 index 0000000000..f4149d770e --- /dev/null +++ b/spec/ruby/core/integer/numerator_spec.rb @@ -0,0 +1,18 @@ +require_relative '../../spec_helper' + +describe "Integer#numerator" do + before :all do + @numbers = [ + 0, + 29871, + 99999999999999**99, + 72628191273, + ].map{|n| [-n, n]}.flatten + end + + it "returns self" do + @numbers.each do |number| + number.numerator.should == number + end + end +end diff --git a/spec/ruby/core/integer/odd_spec.rb b/spec/ruby/core/integer/odd_spec.rb new file mode 100644 index 0000000000..dd779fa44b --- /dev/null +++ b/spec/ruby/core/integer/odd_spec.rb @@ -0,0 +1,38 @@ +require_relative '../../spec_helper' + +describe "Integer#odd?" do + 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 + + 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 new file mode 100644 index 0000000000..bcb57bea98 --- /dev/null +++ b/spec/ruby/core/integer/ord_spec.rb @@ -0,0 +1,17 @@ +require_relative '../../spec_helper' + +describe "Integer#ord" do + it "returns self" do + 20.ord.should eql(20) + 40.ord.should eql(40) + + 0.ord.should eql(0) + (-10).ord.should eql(-10) + + ?a.ord.should eql(97) + ?Z.ord.should eql(90) + + bignum_value.ord.should eql(bignum_value) + (-bignum_value).ord.should eql(-bignum_value) + end +end diff --git a/spec/ruby/core/integer/plus_spec.rb b/spec/ruby/core/integer/plus_spec.rb new file mode 100644 index 0000000000..38428e56c5 --- /dev/null +++ b/spec/ruby/core/integer/plus_spec.rb @@ -0,0 +1,75 @@ +require_relative '../../spec_helper' +require_relative 'shared/arithmetic_coerce' + +describe "Integer#+" do + 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 new file mode 100644 index 0000000000..86750ebfd1 --- /dev/null +++ b/spec/ruby/core/integer/pred_spec.rb @@ -0,0 +1,11 @@ +require_relative '../../spec_helper' + +describe "Integer#pred" do + it "returns the Integer equal to self - 1" do + 0.pred.should eql(-1) + -1.pred.should eql(-2) + bignum_value.pred.should eql(bignum_value(-1)) + fixnum_min.pred.should eql(fixnum_min - 1) + 20.pred.should eql(19) + end +end diff --git a/spec/ruby/core/integer/rationalize_spec.rb b/spec/ruby/core/integer/rationalize_spec.rb new file mode 100644 index 0000000000..09d741af33 --- /dev/null +++ b/spec/ruby/core/integer/rationalize_spec.rb @@ -0,0 +1,39 @@ +require_relative '../../spec_helper' + +describe "Integer#rationalize" do + before :all do + @numbers = [ + 0, + 29871, + 99999999999999**99, + -72628191273, + ] + end + + it "returns a Rational object" do + @numbers.each do |number| + number.rationalize.should be_an_instance_of(Rational) + end + end + + it "uses self as the numerator" do + @numbers.each do |number| + number.rationalize.numerator.should == number + end + end + + it "uses 1 as the denominator" do + @numbers.each do |number| + number.rationalize.denominator.should == 1 + end + end + + it "ignores a single argument" do + 1.rationalize(0.1).should == Rational(1,1) + end + + it "raises ArgumentError when passed more than one argument" do + -> { 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 new file mode 100644 index 0000000000..189384f11a --- /dev/null +++ b/spec/ruby/core/integer/round_spec.rb @@ -0,0 +1,81 @@ +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 + + # redmine:5228 + it "returns itself rounded if passed a negative value" do + +249.round(-2).should eql(+200) + -249.round(-2).should eql(-200) + (+25 * 10**70 - 1).round(-71).should eql(+20 * 10**70) + (-25 * 10**70 + 1).round(-71).should eql(-20 * 10**70) + end + + it "returns itself rounded to nearest if passed a negative value" do + +250.round(-2).should eql(+300) + -250.round(-2).should eql(-300) + (+25 * 10**70).round(-71).should eql(+30 * 10**70) + (-25 * 10**70).round(-71).should eql(-30 * 10**70) + 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 + -> { 42.round(Float::INFINITY) }.should raise_error(RangeError) + end + + it "raises a RangeError when passed a beyond signed int" do + -> { 42.round(1<<31) }.should raise_error(RangeError) + end + + it "raises a TypeError when passed a String" do + -> { 42.round("4") }.should raise_error(TypeError) + end + + it "raises a TypeError when its argument cannot be converted to an Integer" do + -> { 42.round(nil) }.should raise_error(TypeError) + end + + it "calls #to_int on the argument to convert it to an Integer" do + obj = mock("Object") + obj.should_receive(:to_int).and_return(0) + 42.round(obj) + end + + it "raises a TypeError when #to_int does not return an Integer" do + obj = mock("Object") + obj.stub!(:to_int).and_return([]) + -> { 42.round(obj) }.should raise_error(TypeError) + 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 + + 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/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/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 new file mode 100644 index 0000000000..56d1819f84 --- /dev/null +++ b/spec/ruby/core/integer/shared/integer_rounding.rb @@ -0,0 +1,19 @@ +describe :integer_rounding_positive_precision, shared: true do + it "returns self if not passed a precision" do + [2, -4, 10**70, -10**100].each do |v| + v.send(@method).should eql(v) + end + 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 + + 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/shared/next.rb b/spec/ruby/core/integer/shared/next.rb new file mode 100644 index 0000000000..85b83d6965 --- /dev/null +++ b/spec/ruby/core/integer/shared/next.rb @@ -0,0 +1,25 @@ +describe :integer_next, shared: true do + it "returns the next larger positive Fixnum" do + 2.send(@method).should == 3 + end + + it "returns the next larger negative Fixnum" do + (-2).send(@method).should == -1 + end + + it "returns the next larger positive Bignum" do + bignum_value.send(@method).should == bignum_value(1) + end + + it "returns the next larger negative Bignum" do + (-bignum_value(1)).send(@method).should == -bignum_value + end + + it "overflows a Fixnum to a Bignum" do + fixnum_max.send(@method).should == fixnum_max + 1 + end + + it "underflows a Bignum to a Fixnum" do + (fixnum_min - 1).send(@method).should == fixnum_min + end +end diff --git a/spec/ruby/core/integer/shared/to_i.rb b/spec/ruby/core/integer/shared/to_i.rb new file mode 100644 index 0000000000..7b974cd3a7 --- /dev/null +++ b/spec/ruby/core/integer/shared/to_i.rb @@ -0,0 +1,8 @@ +describe :integer_to_i, shared: true do + it "returns self" do + 10.send(@method).should eql(10) + (-15).send(@method).should eql(-15) + bignum_value.send(@method).should eql(bignum_value) + (-bignum_value).send(@method).should eql(-bignum_value) + 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 new file mode 100644 index 0000000000..4149ca2cc9 --- /dev/null +++ b/spec/ruby/core/integer/sqrt_spec.rb @@ -0,0 +1,31 @@ +require_relative '../../spec_helper' + +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 "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 "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 + -> { 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 new file mode 100644 index 0000000000..9ae9a14fe7 --- /dev/null +++ b/spec/ruby/core/integer/succ_spec.rb @@ -0,0 +1,6 @@ +require_relative '../../spec_helper' +require_relative 'shared/next' + +describe "Integer#succ" do + 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 new file mode 100644 index 0000000000..356340592b --- /dev/null +++ b/spec/ruby/core/integer/times_spec.rb @@ -0,0 +1,79 @@ +require_relative '../../spec_helper' + +describe "Integer#times" do + it "returns self" do + 5.times {}.should == 5 + 9.times {}.should == 9 + 9.times { |n| n - 2 }.should == 9 + end + + it "yields each value from 0 to self - 1" do + a = [] + 9.times { |i| a << i } + -2.times { |i| a << i } + a.should == [0, 1, 2, 3, 4, 5, 6, 7, 8] + end + + it "skips the current iteration when encountering 'next'" do + a = [] + 3.times do |i| + next if i == 1 + a << i + end + a.should == [0, 2] + end + + it "skips all iterations when encountering 'break'" do + a = [] + 5.times do |i| + break if i == 3 + a << i + end + a.should == [0, 1, 2] + end + + it "skips all iterations when encountering break with an argument and returns that argument" do + 9.times { break 2 }.should == 2 + end + + it "executes a nested while loop containing a break expression" do + a = [false] + b = 1.times do |i| + while true + a.shift or break + end + end + a.should == [] + b.should == 1 + end + + it "executes a nested #times" do + a = 0 + b = 3.times do |i| + 2.times { a += 1 } + end + a.should == 6 + b.should == 3 + end + + it "returns an Enumerator" do + result = [] + + enum = 3.times + enum.each { |i| result << i } + + result.should == [0, 1, 2] + end + + describe "when no block is given" do + describe "returned Enumerator" do + describe "size" do + it "returns self" do + 5.times.size.should == 5 + 10.times.size.should == 10 + 0.times.size.should == 0 + end + end + end + end +end 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 new file mode 100644 index 0000000000..1a8e477b3c --- /dev/null +++ b/spec/ruby/core/integer/to_i_spec.rb @@ -0,0 +1,6 @@ +require_relative '../../spec_helper' +require_relative 'shared/to_i' + +describe "Integer#to_i" do + 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 new file mode 100644 index 0000000000..4b7eccad3a --- /dev/null +++ b/spec/ruby/core/integer/to_int_spec.rb @@ -0,0 +1,6 @@ +require_relative '../../spec_helper' +require_relative 'shared/to_i' + +describe "Integer#to_int" do + 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 new file mode 100644 index 0000000000..7732bedca1 --- /dev/null +++ b/spec/ruby/core/integer/to_r_spec.rb @@ -0,0 +1,26 @@ +require_relative '../../spec_helper' + +describe "Integer#to_r" do + it "returns a Rational object" do + 309.to_r.should be_an_instance_of(Rational) + end + + it "constructs a rational number with self as the numerator" do + 34.to_r.numerator.should == 34 + end + + it "constructs a rational number with 1 as the denominator" do + 298.to_r.denominator.should == 1 + end + + it "works even if self is a Bignum" do + bignum = 99999**999 + bignum.should be_an_instance_of(Integer) + bignum.to_r.should == Rational(bignum, 1) + end + + it "raises an ArgumentError if given any arguments" do + -> { 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 new file mode 100644 index 0000000000..db16e74be4 --- /dev/null +++ b/spec/ruby/core/integer/truncate_spec.rb @@ -0,0 +1,19 @@ +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 + + 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 new file mode 100644 index 0000000000..dd6995e94b --- /dev/null +++ b/spec/ruby/core/integer/upto_spec.rb @@ -0,0 +1,69 @@ +require_relative '../../spec_helper' + +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 } + result.should == [] + end + + it "yields once when stop equals self" do + result = [] + 5.upto(5) { |x| result << x } + result.should == [5] + end + + it "yields while increasing self until it is less than stop" do + result = [] + 2.upto(5) { |x| result << x } + result.should == [2, 3, 4, 5] + end + + it "yields while increasing self until it is greater than floor of a Float endpoint" do + result = [] + 9.upto(13.3) {|i| result << i} + -5.upto(-1.3) {|i| result << i} + result.should == [9,10,11,12,13,-5,-4,-3,-2] + end + + it "raises an ArgumentError for non-numeric endpoints" do + -> { 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 + it "returns an Enumerator" do + result = [] + + enum = 2.upto(5) + enum.each { |i| result << i } + + result.should == [2, 3, 4, 5] + end + + describe "returned Enumerator" do + describe "size" do + it "raises an ArgumentError for non-numeric endpoints" do + enum = 1.upto("A") + -> { enum.size }.should raise_error(ArgumentError) + enum = 1.upto(nil) + -> { enum.size }.should raise_error(ArgumentError) + end + + it "returns stop - self + 1" do + 5.upto(10).size.should == 6 + 1.upto(10).size.should == 10 + 0.upto(10).size.should == 11 + 0.upto(0).size.should == 1 + -5.upto(-3).size.should == 3 + end + + it "returns 0 when stop < self" do + 5.upto(4).size.should == 0 + 0.upto(-5).size.should == 0 + -3.upto(-5).size.should == 0 + end + end + end + end +end 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 |
