Skip to content

Commit

Permalink
Add precision parameter to Int#to_s (#10926)
Browse files Browse the repository at this point in the history
Co-authored-by: Johannes Müller <[email protected]>
  • Loading branch information
HertzDevil and straight-shoota authored Jul 26, 2021
1 parent 87b4e9e commit 32f034f
Show file tree
Hide file tree
Showing 4 changed files with 376 additions and 184 deletions.
95 changes: 87 additions & 8 deletions spec/std/big/big_int_spec.cr
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
require "spec"
require "big"

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

describe "BigInt" do
it "creates with a value of zero" do
BigInt.new.to_s.should eq("0")
Expand Down Expand Up @@ -328,14 +335,86 @@ describe "BigInt" do
result.to_s.should eq("10715086071862673209484250490600018105614048117055336074437503883703510511249361224931983788156958581275946729175531468251871452856923140435984577574698574803934567774824230985421074605062371141877954182153046474983581941267398767559165543946077062914571196477686542167660429831652624386837205668069376")
end

it "does to_s in the given base" do
a = BigInt.new("1234567890123456789")
b = "1000100100010000100001111010001111101111010011000000100010101"
c = "112210f47de98115"
d = "128gguhuuj08l"
a.to_s(2).should eq(b)
a.to_s(16).should eq(c)
a.to_s(32).should eq(d)
describe "#to_s" do
context "base and upcase parameters" do
a = BigInt.new("1234567890123456789")
it_converts_to_s a, "1000100100010000100001111010001111101111010011000000100010101", base: 2
it_converts_to_s a, "112210f47de98115", base: 16
it_converts_to_s a, "112210F47DE98115", base: 16, upcase: true
it_converts_to_s a, "128gguhuuj08l", base: 32
it_converts_to_s a, "128GGUHUUJ08L", base: 32, upcase: true
it_converts_to_s a, "1tckI1NfUnH", base: 62

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

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

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

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

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

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

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

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

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

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

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

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

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

it "does to_big_f" do
Expand Down
244 changes: 117 additions & 127 deletions spec/std/int_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ require "./spec_helper"
{% end %}
require "spec/helpers/iterate"

private def to_s_with_io(num)
String.build { |io| num.to_s(io) }
end

private def to_s_with_io(num, base, upcase = false)
String.build { |io| num.to_s(io, base, upcase: upcase) }
private macro it_converts_to_s(num, str, **opts)
it {{ "converts #{num} to #{str}" }} do
num = {{ num }}
str = {{ str }}
num.to_s({{ opts.double_splat }}).should eq(str)
String.build { |io| num.to_s(io, {{ opts.double_splat }}) }.should eq(str)
end
end

describe "Int" do
Expand Down Expand Up @@ -166,79 +167,116 @@ describe "Int" do
it "doesn't silently overflow" { 2_000_000.lcm(3_000_000).should eq(6_000_000) }
end

describe "to_s in base" do
it { 12.to_s(2).should eq("1100") }
it { -12.to_s(2).should eq("-1100") }
it { -123456.to_s(2).should eq("-11110001001000000") }
it { 1234.to_s(16).should eq("4d2") }
it { -1234.to_s(16).should eq("-4d2") }
it { 1234.to_s(36).should eq("ya") }
it { -1234.to_s(36).should eq("-ya") }
it { 1234.to_s(16, upcase: true).should eq("4D2") }
it { -1234.to_s(16, upcase: true).should eq("-4D2") }
it { 1234.to_s(36, upcase: true).should eq("YA") }
it { -1234.to_s(36, upcase: true).should eq("-YA") }
it { 0.to_s(2).should eq("0") }
it { 0.to_s(16).should eq("0") }
it { 1.to_s(2).should eq("1") }
it { 1.to_s(16).should eq("1") }
it { 0.to_s(62).should eq("0") }
it { 1.to_s(62).should eq("1") }
it { 10.to_s(62).should eq("a") }
it { 35.to_s(62).should eq("z") }
it { 36.to_s(62).should eq("A") }
it { 61.to_s(62).should eq("Z") }
it { 62.to_s(62).should eq("10") }
it { 97.to_s(62).should eq("1z") }
it { 3843.to_s(62).should eq("ZZ") }

it "raises on base 1" do
expect_raises(ArgumentError, "Invalid base 1") { 123.to_s(1) }
end

it "raises on base 37" do
expect_raises(ArgumentError, "Invalid base 37") { 123.to_s(37) }
end

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

it { to_s_with_io(12, 2).should eq("1100") }
it { to_s_with_io(-12, 2).should eq("-1100") }
it { to_s_with_io(-123456, 2).should eq("-11110001001000000") }
it { to_s_with_io(1234, 16).should eq("4d2") }
it { to_s_with_io(-1234, 16).should eq("-4d2") }
it { to_s_with_io(1234, 36).should eq("ya") }
it { to_s_with_io(-1234, 36).should eq("-ya") }
it { to_s_with_io(1234, 16, upcase: true).should eq("4D2") }
it { to_s_with_io(-1234, 16, upcase: true).should eq("-4D2") }
it { to_s_with_io(1234, 36, upcase: true).should eq("YA") }
it { to_s_with_io(-1234, 36, upcase: true).should eq("-YA") }
it { to_s_with_io(0, 2).should eq("0") }
it { to_s_with_io(0, 16).should eq("0") }
it { to_s_with_io(1, 2).should eq("1") }
it { to_s_with_io(1, 16).should eq("1") }
it { to_s_with_io(0, 62).should eq("0") }
it { to_s_with_io(1, 62).should eq("1") }
it { to_s_with_io(10, 62).should eq("a") }
it { to_s_with_io(35, 62).should eq("z") }
it { to_s_with_io(36, 62).should eq("A") }
it { to_s_with_io(61, 62).should eq("Z") }
it { to_s_with_io(62, 62).should eq("10") }
it { to_s_with_io(97, 62).should eq("1z") }
it { to_s_with_io(3843, 62).should eq("ZZ") }

it "raises on base 1 with io" do
expect_raises(ArgumentError, "Invalid base 1") { to_s_with_io(123, 1) }
end

it "raises on base 37 with io" do
expect_raises(ArgumentError, "Invalid base 37") { to_s_with_io(123, 37) }
end

it "raises on base 62 with upcase with io" do
expect_raises(ArgumentError, "upcase must be false for base 62") { to_s_with_io(12, 62, upcase: true) }
describe "#to_s" do
it_converts_to_s 0, "0"
it_converts_to_s 1, "1"

context "extrema for various int sizes" do
it_converts_to_s 127_i8, "127"
it_converts_to_s -128_i8, "-128"

it_converts_to_s 32767_i16, "32767"
it_converts_to_s -32768_i16, "-32768"

it_converts_to_s 2147483647, "2147483647"
it_converts_to_s -2147483648, "-2147483648"

it_converts_to_s 9223372036854775807_i64, "9223372036854775807"
it_converts_to_s -9223372036854775808_i64, "-9223372036854775808"

it_converts_to_s 255_u8, "255"
it_converts_to_s 65535_u16, "65535"
it_converts_to_s 4294967295_u32, "4294967295"
it_converts_to_s 18446744073709551615_u64, "18446744073709551615"
end

context "base and upcase parameters" do
it_converts_to_s 12, "1100", base: 2
it_converts_to_s -12, "-1100", base: 2
it_converts_to_s -123456, "-11110001001000000", base: 2
it_converts_to_s 1234, "4d2", base: 16
it_converts_to_s -1234, "-4d2", base: 16
it_converts_to_s 1234, "ya", base: 36
it_converts_to_s -1234, "-ya", base: 36
it_converts_to_s 1234, "4D2", base: 16, upcase: true
it_converts_to_s -1234, "-4D2", base: 16, upcase: true
it_converts_to_s 1234, "YA", base: 36, upcase: true
it_converts_to_s -1234, "-YA", base: 36, upcase: true
it_converts_to_s 0, "0", base: 2
it_converts_to_s 0, "0", base: 16
it_converts_to_s 1, "1", base: 2
it_converts_to_s 1, "1", base: 16
it_converts_to_s 0, "0", base: 62
it_converts_to_s 1, "1", base: 62
it_converts_to_s 10, "a", base: 62
it_converts_to_s 35, "z", base: 62
it_converts_to_s 36, "A", base: 62
it_converts_to_s 61, "Z", base: 62
it_converts_to_s 62, "10", base: 62
it_converts_to_s 97, "1z", base: 62
it_converts_to_s 3843, "ZZ", base: 62

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

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

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

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

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

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

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

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

it_converts_to_s 9223372036854775807_i64, "#{"1" * 63}", base: 2, precision: 62
it_converts_to_s 9223372036854775807_i64, "#{"1" * 63}", base: 2, precision: 63
it_converts_to_s 9223372036854775807_i64, "0#{"1" * 63}", base: 2, precision: 64
it_converts_to_s 9223372036854775807_i64, "#{"0" * 137}#{"1" * 63}", base: 2, precision: 200

it_converts_to_s -9223372036854775808_i64, "-1#{"0" * 63}", base: 2, precision: 63
it_converts_to_s -9223372036854775808_i64, "-1#{"0" * 63}", base: 2, precision: 64
it_converts_to_s -9223372036854775808_i64, "-01#{"0" * 63}", base: 2, precision: 65
it_converts_to_s -9223372036854775808_i64, "-#{"0" * 136}1#{"0" * 63}", base: 2, precision: 200

it "raises on negative precision" do
expect_raises(ArgumentError, "Precision must be non-negative") { 123.to_s(precision: -1) }
expect_raises(ArgumentError, "Precision must be non-negative") { 123.to_s(IO::Memory.new, precision: -1) }
end
end
end

Expand Down Expand Up @@ -358,54 +396,6 @@ describe "Int" do
end
end

describe "to_s" do
it "does to_s for various int sizes" do
0.to_s.should eq("0")
1.to_s.should eq("1")

127_i8.to_s.should eq("127")
-128_i8.to_s.should eq("-128")

32767_i16.to_s.should eq("32767")
-32768_i16.to_s.should eq("-32768")

2147483647.to_s.should eq("2147483647")
-2147483648.to_s.should eq("-2147483648")

9223372036854775807_i64.to_s.should eq("9223372036854775807")
-9223372036854775808_i64.to_s.should eq("-9223372036854775808")

255_u8.to_s.should eq("255")
65535_u16.to_s.should eq("65535")
4294967295_u32.to_s.should eq("4294967295")

18446744073709551615_u64.to_s.should eq("18446744073709551615")
end

it "does to_s for various int sizes with IO" do
to_s_with_io(0).should eq("0")
to_s_with_io(1).should eq("1")

to_s_with_io(127_i8).should eq("127")
to_s_with_io(-128_i8).should eq("-128")

to_s_with_io(32767_i16).should eq("32767")
to_s_with_io(-32768_i16).should eq("-32768")

to_s_with_io(2147483647).should eq("2147483647")
to_s_with_io(-2147483648).should eq("-2147483648")

to_s_with_io(9223372036854775807_i64).should eq("9223372036854775807")
to_s_with_io(-9223372036854775808_i64).should eq("-9223372036854775808")

to_s_with_io(255_u8).should eq("255")
to_s_with_io(65535_u16).should eq("65535")
to_s_with_io(4294967295_u32).should eq("4294967295")

to_s_with_io(18446744073709551615_u64).should eq("18446744073709551615")
end
end

describe "step" do
it "steps through limit" do
passed = false
Expand Down
Loading

0 comments on commit 32f034f

Please sign in to comment.