require "spec" require "big" require "complex" require "../support/number" describe "Number" do {% for number_type in BUILTIN_NUMBER_TYPES %} it_unchecked_initializes_from_value_to {{number_type}} it_initializes_from_value_to {{number_type}} {% end %} it_can_convert_between({{BUILTIN_NUMBER_TYPES}}, {{BUILTIN_NUMBER_TYPES}}) describe "significant" do it "10 base" do 1234.567.significant(1).should eq(1000) 1234.567.significant(2).should eq(1200) 1234.567.significant(3).should eq(1230) 1234.567.significant(4).should eq(1235) 1234.567.significant(5).should be_close(1234.6, 1e-7) 1234.567.significant(6).should eq(1234.57) 1234.567.significant(7).should eq(1234.567) 123.456789.significant(5).should eq(123.46) 0.000123456789.significant(3).should eq 0.000123 0.123456789.significant(4).should eq 0.1235 1.23456789.significant(2).should eq 1.2 end it "2 base" do -1763.116.significant(2, base: 2).should eq(-1536.0) 753.155.significant(3, base: 2).should eq(768.0) 15.159.significant(1, base: 2).should eq(16.0) end it "8 base" do -1763.116.significant(2, base: 8).should eq(-1792.0) 753.155.significant(3, base: 8).should eq(752.0) 15.159.significant(1, base: 8).should eq(16.0) end it "preserves type" do 123.significant(2).should eq(120) 123.significant(2).should be_a(Int32) 0.significant(1).should be_a(Int32) end end describe "#round" do it "rounds to nearest integer" do 5.5.round.should eq(6) 0.4.round.should eq(0) -2.8.round.should eq(-3) 0.0.round.should eq(0) 0.49999999999999994.round.should eq(0) -1763.116.round.should eq(-1763) 753.155.round.should eq(753) 15.151.round.should eq(15) end it "infinity Float64" do Float64::INFINITY.round.should eq Float64::INFINITY Float64::INFINITY.round(digits: 0).should eq Float64::INFINITY Float64::INFINITY.round(digits: 3).should eq Float64::INFINITY Float64::INFINITY.round(digits: -3).should eq Float64::INFINITY (-Float64::INFINITY).round.should eq -Float64::INFINITY (-Float64::INFINITY).round(digits: 0).should eq -Float64::INFINITY (-Float64::INFINITY).round(digits: 3).should eq -Float64::INFINITY (-Float64::INFINITY).round(digits: -3).should eq -Float64::INFINITY end {% if compare_versions(Crystal::VERSION, "0.36.1") > 0 %} it "infinity Float32" do Float32::INFINITY.round.should eq Float32::INFINITY Float32::INFINITY.round(digits: 0).should eq Float32::INFINITY Float32::INFINITY.round(digits: 3).should eq Float32::INFINITY Float32::INFINITY.round(digits: -3).should eq Float32::INFINITY (-Float32::INFINITY).round.should eq -Float32::INFINITY (-Float32::INFINITY).round(digits: 0).should eq -Float32::INFINITY (-Float32::INFINITY).round(digits: 3).should eq -Float32::INFINITY (-Float32::INFINITY).round(digits: -3).should eq -Float32::INFINITY end {% else %} pending "infinity Float32" {% end %} it "nan" do Float64::NAN.round.nan?.should be_true Float32::NAN.round.nan?.should be_true end it "rounds to digits" do 5.5.round(0).should eq(6) 5.7.round(1).should eq(5.7) 1.2345678.round(2).should eq(1.23) 123456.78.round(-2).should eq(123500) # rounded up -123456.78.round(-2).should eq(-123500) -1763.116.round(2).should eq(-1763.12) 753.155.round(2).should eq(753.16) 15.151.round(2).should eq(15.15) 0.8346268.round(-1).should eq(0) end it { 42.0.round(300).should eq(42.0) } pending "edge cases" do 42.0.round(308).should eq(42.0) 42.0.round(309).should eq(42.0) 1.0e307.round(2).should eq(1.0e307) 0.42.round(2**30).should eq(0.42) end it "preserves type" do 1.round.should be_a(Int32) 1_u8.round.should be_a(UInt8) 1.0_f32.round.should be_a(Float32) end it "negative digits" do 123.round(-2).should eq(100) 123.round(-3).should eq(0) 523.round(-3).should eq(1000) 123.456.round(-2).should eq(100) 123_456.123456.round(-5).should eq(100_000) end it "accepts unsigned precision" do 123.round(UInt8.new(3)).should eq(123) 11.308.round(UInt8.new(3)).should eq(11.308) 11.308.round(UInt8.new(2)).should eq(11.31) end it "handle medium amount of digits" do 1.098765432109876543210987654321.round(15).should eq(1.098765432109876) 1.098765432109876543210987654321.round(21).should eq(1.098765432109876543211) 6543210987654321.0.round(-15).should eq(7000000000000000.0) end describe "rounding modes" do it "to_zero" do -1.5.round(:to_zero).should eq -1.0 -1.0.round(:to_zero).should eq -1.0 -0.9.round(:to_zero).should eq 0.0 -0.5.round(:to_zero).should eq 0.0 -0.1.round(:to_zero).should eq 0.0 0.0.round(:to_zero).should eq 0.0 0.1.round(:to_zero).should eq 0.0 0.5.round(:to_zero).should eq 0.0 0.9.round(:to_zero).should eq 0.0 1.0.round(:to_zero).should eq 1.0 1.5.round(:to_zero).should eq 1.0 end it "to_positive" do -1.5.round(:to_positive).should eq -1.0 -1.0.round(:to_positive).should eq -1.0 -0.9.round(:to_positive).should eq 0.0 -0.5.round(:to_positive).should eq 0.0 -0.1.round(:to_positive).should eq 0.0 0.0.round(:to_positive).should eq 0.0 0.1.round(:to_positive).should eq 1.0 0.5.round(:to_positive).should eq 1.0 0.9.round(:to_positive).should eq 1.0 1.0.round(:to_positive).should eq 1.0 1.5.round(:to_positive).should eq 2.0 end it "to_negative" do -1.5.round(:to_negative).should eq -2.0 -1.0.round(:to_negative).should eq -1.0 -0.9.round(:to_negative).should eq -1.0 -0.5.round(:to_negative).should eq -1.0 -0.1.round(:to_negative).should eq -1.0 0.0.round(:to_negative).should eq 0.0 0.1.round(:to_negative).should eq 0.0 0.5.round(:to_negative).should eq 0.0 0.9.round(:to_negative).should eq 0.0 1.0.round(:to_negative).should eq 1.0 1.5.round(:to_negative).should eq 1.0 end it "ties_even" do -2.5.round(:ties_even).should eq -2.0 -1.5.round(:ties_even).should eq -2.0 -1.0.round(:ties_even).should eq -1.0 -0.9.round(:ties_even).should eq -1.0 -0.5.round(:ties_even).should eq 0.0 -0.1.round(:ties_even).should eq 0.0 0.0.round(:ties_even).should eq 0.0 0.1.round(:ties_even).should eq 0.0 0.5.round(:ties_even).should eq 0.0 0.9.round(:ties_even).should eq 1.0 1.0.round(:ties_even).should eq 1.0 1.5.round(:ties_even).should eq 2.0 2.5.round(:ties_even).should eq 2.0 end it "ties_away" do -2.5.round(:ties_away).should eq -3.0 -1.5.round(:ties_away).should eq -2.0 -1.0.round(:ties_away).should eq -1.0 -0.9.round(:ties_away).should eq -1.0 -0.5.round(:ties_away).should eq -1.0 -0.1.round(:ties_away).should eq 0.0 0.0.round(:ties_away).should eq 0.0 0.1.round(:ties_away).should eq 0.0 0.5.round(:ties_away).should eq 1.0 0.9.round(:ties_away).should eq 1.0 1.0.round(:ties_away).should eq 1.0 1.5.round(:ties_away).should eq 2.0 2.5.round(:ties_away).should eq 3.0 end it "default (=ties_even)" do -2.5.round.should eq -2.0 -1.5.round.should eq -2.0 -1.0.round.should eq -1.0 -0.9.round.should eq -1.0 -0.5.round.should eq 0.0 -0.1.round.should eq 0.0 0.0.round.should eq 0.0 0.1.round.should eq 0.0 0.5.round.should eq 0.0 0.9.round.should eq 1.0 1.0.round.should eq 1.0 1.5.round.should eq 2.0 2.5.round.should eq 2.0 end end describe "with digits" do it "to_zero" do 12.345.round(-1, mode: :to_zero).should eq 10 12.345.round(0, mode: :to_zero).should eq 12 12.345.round(1, mode: :to_zero).should eq 12.3 12.345.round(2, mode: :to_zero).should eq 12.34 -12.345.round(-1, mode: :to_zero).should eq -10 -12.345.round(0, mode: :to_zero).should eq -12 -12.345.round(1, mode: :to_zero).should eq -12.3 -12.345.round(2, mode: :to_zero).should eq -12.34 end it "to_positive" do 12.345.round(-1, mode: :to_positive).should eq 20 12.345.round(0, mode: :to_positive).should eq 13 12.345.round(1, mode: :to_positive).should eq 12.4 12.345.round(2, mode: :to_positive).should eq 12.35 -12.345.round(-1, mode: :to_positive).should eq -10 -12.345.round(0, mode: :to_positive).should eq -12 -12.345.round(1, mode: :to_positive).should eq -12.3 -12.345.round(2, mode: :to_positive).should eq -12.34 end it "to_negative" do 12.345.round(-1, mode: :to_negative).should eq 10 12.345.round(0, mode: :to_negative).should eq 12 12.345.round(1, mode: :to_negative).should eq 12.3 12.345.round(2, mode: :to_negative).should eq 12.34 -12.345.round(-1, mode: :to_negative).should eq -20 -12.345.round(0, mode: :to_negative).should eq -13 -12.345.round(1, mode: :to_negative).should eq -12.4 -12.345.round(2, mode: :to_negative).should eq -12.35 end it "ties_away" do 13.825.round(-1, mode: :ties_away).should eq 10 13.825.round(0, mode: :ties_away).should eq 14 13.825.round(1, mode: :ties_away).should eq 13.8 13.825.round(2, mode: :ties_away).should eq 13.83 -13.825.round(-1, mode: :ties_away).should eq -10 -13.825.round(0, mode: :ties_away).should eq -14 -13.825.round(1, mode: :ties_away).should eq -13.8 -13.825.round(2, mode: :ties_away).should eq -13.83 end it "ties_even" do 15.255.round(-1, mode: :ties_even).should eq 20 15.255.round(0, mode: :ties_even).should eq 15 15.255.round(1, mode: :ties_even).should eq 15.3 15.255.round(2, mode: :ties_even).should eq 15.26 -15.255.round(-1, mode: :ties_even).should eq -20 -15.255.round(0, mode: :ties_even).should eq -15 -15.255.round(1, mode: :ties_even).should eq -15.3 -15.255.round(2, mode: :ties_even).should eq -15.26 end end describe "base" do it "2" do -1763.116.round(2, base: 2).should eq(-1763.0) 753.155.round(2, base: 2).should eq(753.25) 15.159.round(2, base: 2).should eq(15.25) 753.155.round(-5, base: 2).should eq(768) end it "8" do -1763.116.round(2, base: 8).should eq(-1763.109375) 753.155.round(1, base: 8).should eq(753.125) 15.159.round(0, base: 8).should eq(15.0) end end end describe "#round_even" do it { -2.5.round_even.should eq -2.0 } it { -1.5.round_even.should eq -2.0 } it { -1.0.round_even.should eq -1.0 } it { -0.9.round_even.should eq -1.0 } it { -0.5.round_even.should eq -0.0 } it { -0.1.round_even.should eq 0.0 } it { 0.0.round_even.should eq 0.0 } it { 0.1.round_even.should eq 0.0 } it { 0.5.round_even.should eq 0.0 } it { 0.9.round_even.should eq 1.0 } it { 1.0.round_even.should eq 1.0 } it { 1.5.round_even.should eq 2.0 } it { 2.5.round_even.should eq 2.0 } it { 1.round_even.should eq 1 } it { 1.round_even.should be_a(Int32) } it { 1_u8.round_even.should be_a(UInt8) } it { 1_f32.round_even.should be_a(Float32) } end describe "#round_away" do it { -2.5.round_away.should eq -3.0 } it { -1.5.round_away.should eq -2.0 } it { -1.0.round_away.should eq -1.0 } it { -0.9.round_away.should eq -1.0 } it { -0.5.round_away.should eq -1.0 } it { -0.1.round_away.should eq 0.0 } it { 0.0.round_away.should eq 0.0 } it { 0.1.round_away.should eq 0.0 } it { 0.5.round_away.should eq 1.0 } it { 0.9.round_away.should eq 1.0 } it { 1.0.round_away.should eq 1.0 } it { 1.5.round_away.should eq 2.0 } it { 2.5.round_away.should eq 3.0 } it { 1.round_away.should eq 1 } it { 1.round_away.should be_a(Int32) } it { 1_u8.round_away.should be_a(UInt8) } it { 1_f32.round_away.should be_a(Float32) } end it "gives the absolute value" do 123.abs.should eq(123) -123.abs.should eq(123) end it "gives the square of a value" do 2.abs2.should eq(4) -2.abs2.should eq(4) 2.5.abs2.should eq(6.25) -2.5.abs2.should eq(6.25) end it "gives the sign" do 123.sign.should eq(1) -123.sign.should eq(-1) 0.sign.should eq(0) end it "divides and calculates the modulo" do 11.divmod(3).should eq({3, 2}) 11.divmod(-3).should eq({-4, -1}) 10.divmod(2).should eq({5, 0}) 11.divmod(2).should eq({5, 1}) 10.divmod(-2).should eq({-5, 0}) 11.divmod(-2).should eq({-6, -1}) -10.divmod(2).should eq({-5, 0}) -11.divmod(2).should eq({-6, 1}) -10.divmod(-2).should eq({5, 0}) -11.divmod(-2).should eq({5, -1}) end it "compare the numbers" do 10.<=>(10).should eq(0) 10.<=>(11).should eq(-1) 11.<=>(10).should eq(1) end it "creates an array with [] and some elements" do ary = Int64[1, 2, 3] ary.should eq([1, 2, 3]) ary[0].should be_a(Int64) end it "creates an array with [] and no elements" do ary = Int64[] ary.should eq([] of Int64) ary << 1_i64 ary.should eq([1]) end it "creates a slice" do slice = Int8.slice(1, 2, 300) slice.should be_a(Slice(Int8)) slice.size.should eq(3) slice[0].should eq(1) slice[1].should eq(2) slice[2].should eq(300.to_u8!) end it "creates a static array" do ary = Int8.static_array(1, 2, 300) ary.should be_a(StaticArray(Int8, 3)) ary.size.should eq(3) ary[0].should eq(1) ary[1].should eq(2) ary[2].should eq(300.to_u8!) end it "#zero?" do 0.zero?.should be_true 0.0.zero?.should be_true 0_f32.zero?.should be_true 1.zero?.should be_false 1.0.zero?.should be_false 1f32.zero?.should be_false end it "#positive?" do 1.positive?.should be_true 1.0.positive?.should be_true 0.positive?.should be_false 0.0.positive?.should be_false -1.positive?.should be_false -1.1.positive?.should be_false end it "#negative?" do 1.negative?.should be_false 1.0.negative?.should be_false 0.negative?.should be_false 0.0.negative?.should be_false -1.negative?.should be_true -1.1.negative?.should be_true end describe "#step" do it_iterates "basic Int", [1, 2, 3, 4, 5], 1.step(to: 5) it_iterates "basic Float", [1.0, 2.0, 3.0, 4.0, 5.0], 1.0.step(to: 5.0) it_iterates "single value Int", [1], 1.step(to: 1) it_iterates "single value Float", [1.0], 1.0.step(to: 1.0) it_iterates "single value by Int", [1], 1.step(to: 1, by: 2) it_iterates "single value by Float", [1.0], 1.0.step(to: 1.0, by: 2.0) it_iterates "single value Int by Float", [1.0], 1.step(to: 1, by: 2.0) it_iterates "single value Float by Int", [1.0], 1.0.step(to: 1.0, by: 2) it_iterates "negative Int", [-1, -2, -3, -4, -5], -1.step(to: -5) it_iterates "negative Float", [-1.0, -2.0, -3.0, -4.0, -5.0], -1.0.step(to: -5.0) it_iterates "downto Int", [3, 2, 1, 0], 3.step(to: 0) it_iterates "downto Int by", [3, 2, 1, 0], 3.step(to: 0, by: -1) it_iterates "downto UInt", [3, 2, 1, 0] of UInt8, 3u8.step(to: 0) it_iterates "downto UInt by", [3, 2, 1, 0] of UInt8, 3u8.step(to: 0, by: -1) it_iterates "downto Float", [3.0, 2.0, 1.0, 0.0], 3.0.step(to: 0) it_iterates "downto Float by", [3.0, 2.0, 1.0, 0.0], 3.0.step(to: 0, by: -1) it_iterates "by Int", [1, 3, 5], 1.step(to: 5, by: 2) it_iterates "by Float", [1.0, 3.0, 5.0], 1.0.step(to: 5.0, by: 2.0) it_iterates "by Float half", [1.0, 2.5, 4.0], 1.0.step(to: 5.0, by: 1.5) it_iterates "negative by Int", [-1, -3, -5], -1.step(to: -5, by: -2) it_iterates "negative by Float", [-1.0, -3.0, -5.0], -1.0.step(to: -5.0, by: -2.0) it_iterates "negative by Float half", [-1.0, -2.5, -4.0], -1.0.step(to: -5.0, by: -1.5) it_iterates "missing end Int", [1, 3], 1.step(to: 4, by: 2) it_iterates "missing end Float", [1.0, 3.0], 1.0.step(to: 4.0, by: 2.0) it_iterates "missing end UInt", [3, 1] of UInt8, 3u8.step(to: 0, by: -2) it_iterates "Int to Float", [1, 2, 3, 4, 5], 1.step(to: 5.0) it_iterates "Int to Float by", [1, 2, 3, 4, 5], 1.step(to: 5.0, by: 1) it_iterates "Float to Int", [1.0, 2.0, 3.0, 4.0, 5.0], 1.0.step(to: 5) it_iterates "Float to Int by", [1.0, 2.0, 3.0, 4.0, 5.0], 1.0.step(to: 5, by: 1) it_iterates "Int by Float", [1.0, 3.0, 5.0], 1.step(to: 5, by: 2.0) it_iterates "Float by Int", [1.0, 3.0, 5.0], 1.0.step(to: 5.0, by: 2) it_iterates "over zero Int", [-1, 0, 1], -1.step(to: 1) it_iterates "over zero Float", [-1.0, 0.0, 1.0], -1.0.step(to: 1.0) it_iterates "at max Int", [Int8::MAX - 2, Int8::MAX - 1, Int8::MAX], (Int8::MAX - 2).step(to: Int8::MAX) it_iterates "over max Int", [Int8::MAX - 1], (Int8::MAX - 1).step(to: Int8::MAX, by: 2) it_iterates "at min Int", [Int8::MIN + 2, Int8::MIN + 1, Int8::MIN], (Int8::MIN + 2).step(to: Int8::MIN) it_iterates "over min Int", [Int8::MIN + 1], (Int8::MIN + 1).step(to: Int8::MIN, by: -2) it "by zero yielding" do yielded = false expect_raises(ArgumentError, "Zero step size") do 0.step(to: 1, by: 0) { yielded = true } end yielded.should be_false end it "by zero iterator" do expect_raises(ArgumentError, "Zero step size") do 0.step(to: 1, by: 0) end end it_iterates "empty if `by` and `to` are opposed", [] of Int32, 1.step(to: 2, by: -1) it_iterates "empty if `to` can't be compared", [] of Float64, 1.0.step(to: Float64::NAN) it_iterates "empty if `to` can't be compared by", [] of Float64, 1.0.step(to: Float64::NAN, by: 1.0) it_iterates "empty if `self` can't be compared", [] of Float64, Float64::NAN.step(to: 1.0) it_iterates "empty if `self` can't be compared by", [] of Float64, Float64::NAN.step(to: 1.0, by: 1.0) describe "exclusive" do it_iterates "basic Int", [1, 2, 3, 4], 1.step(to: 5, exclusive: true) it_iterates "basic Float", [1.0, 2.0, 3.0, 4.0], 1.0.step(to: 5.0, exclusive: true) it_iterates "single value Int", [] of Int32, 1.step(to: 1, exclusive: true) it_iterates "single value Float", [] of Float64, 1.0.step(to: 1.0, exclusive: true) end describe "without limit" do describe "iterator" do it "basic" do iter = 0.step 5.times do iter.next end iter.next.should eq(5) end it "raises overflow error" do iter = (Int8::MAX - 1).step iter.next.should eq Int8::MAX - 1 iter.next.should eq Int8::MAX expect_raises(OverflowError) do iter.next end end end describe "yielding" do it "basic" do i = 1 1.step do |x| x.should eq(i) break if x >= 10 i += 1 end i.should eq 10 end it "raises overflow error" do ary = [] of Int8 expect_raises(OverflowError) do (Int8::MAX - 1).step do |x| ary << x end end ary.should eq [Int8::MAX - 1, Int8::MAX] end end end describe "whole range" do it { (UInt8::MIN..UInt8::MAX).each.count { true }.should eq(256) } it_iterates "UInt8 upwards", (UInt8::MIN.to_i..UInt8::MAX.to_i).map(&.to_u8), (UInt8::MIN..UInt8::MAX).step(by: 1) it_iterates "UInt8 downwards", (UInt8::MIN.to_i..UInt8::MAX.to_i).map(&.to_u8).reverse!, (UInt8::MAX..UInt8::MIN).step(by: -1) it { (Int8::MIN..Int8::MAX).each.count { true }.should eq(256) } it_iterates "Int8 upwards", (Int8::MIN.to_i..Int8::MAX.to_i).map(&.to_i8), (Int8::MIN..Int8::MAX).step(by: 1) it_iterates "Int8 downwards", (Int8::MIN.to_i..Int8::MAX.to_i).map(&.to_i8).reverse!, (Int8::MAX..Int8::MIN).step(by: -1) it { (Int16::MIN..Int16::MAX).each.count { true }.should eq(65536) } it_iterates "Int16 upwards", (Int16::MIN.to_i..Int16::MAX.to_i).map(&.to_i16), (Int16::MIN..Int16::MAX).step(by: 1) it_iterates "Int16 downwards", (Int16::MIN.to_i..Int16::MAX.to_i).map(&.to_i16).reverse!, (Int16::MAX..Int16::MIN).step(by: -1) end it_iterates "towards limit [max-4, max-2, max]", [Int32::MAX - 4, Int32::MAX - 2, Int32::MAX], (Int32::MAX - 4).step(to: Int32::MAX, by: 2) it_iterates "towards limit [max-4, max-2, max)", [Int32::MAX - 4, Int32::MAX - 2], (Int32::MAX - 4).step(to: Int32::MAX, by: 2, exclusive: true) it_iterates "towards limit [max-3, max-1, max)", [Int32::MAX - 3, Int32::MAX - 1], (Int32::MAX - 3).step(to: Int32::MAX, by: 2) end floor_division_returns_lhs_type {{BUILTIN_NUMBER_TYPES}}, {{BUILTIN_NUMBER_TYPES}} division_between_returns {{BUILTIN_INTEGER_TYPES}}, {{BUILTIN_INTEGER_TYPES}}, Float64 division_between_returns {{BUILTIN_INTEGER_TYPES}}, [Float32], Float32 division_between_returns [Float32], {{BUILTIN_INTEGER_TYPES}}, Float32 division_between_returns {{BUILTIN_INTEGER_TYPES}}, [Float64], Float64 division_between_returns [Float64], {{BUILTIN_INTEGER_TYPES}}, Float64 division_between_returns [Float32], [Float32], Float32 division_between_returns {{BUILTIN_FLOAT_TYPES}}, [Float64], Float64 division_between_returns [Float64], {{BUILTIN_FLOAT_TYPES}}, Float64 end