Skip to content

Commit 314e646

Browse files
authored
Add Int#tdivmod (#16258)
1 parent a9726d3 commit 314e646

File tree

2 files changed

+116
-7
lines changed

2 files changed

+116
-7
lines changed

spec/std/int_spec.cr

Lines changed: 82 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -521,7 +521,61 @@ describe "Int" do
521521
end
522522

523523
describe "divmod" do
524-
it { 5.divmod(3).should eq({1, 2}) }
524+
it "returns a Tuple of two elements containing the quotient and modulus obtained by dividing self by argument" do
525+
5.divmod(3).should eq({1, 2})
526+
-5.divmod(3).should eq({-2, 1})
527+
5.divmod(-3).should eq({-2, -1})
528+
-5.divmod(-3).should eq({1, -2})
529+
end
530+
531+
it "preserves type of lhs" do
532+
{% for type in [UInt8, UInt16, UInt32, UInt64, Int8, Int16, Int32, Int64, UInt128, Int128] %}
533+
{{type}}.new(7).divmod(2).should be_a(Tuple({{type}}, {{type}}))
534+
{% end %}
535+
end
536+
537+
it "raises when divides by zero" do
538+
expect_raises(DivisionByZeroError) { 5.divmod(0) }
539+
end
540+
541+
it "raises when divides Int::MIN by -1" do
542+
expect_raises(ArgumentError) { Int8::MIN.divmod(-1) }
543+
expect_raises(ArgumentError) { Int16::MIN.divmod(-1) }
544+
expect_raises(ArgumentError) { Int32::MIN.divmod(-1) }
545+
expect_raises(ArgumentError) { Int64::MIN.divmod(-1) }
546+
expect_raises(ArgumentError) { Int128::MIN.divmod(-1) }
547+
548+
(UInt8::MIN.divmod(-1)).should eq({0, 0})
549+
end
550+
end
551+
552+
describe "tdivmod" do
553+
it "returns a Tuple of two elements containing the quotient and modulus obtained by dividing self by argument using truncated division" do
554+
5.tdivmod(3).should eq({1, 2})
555+
-5.tdivmod(3).should eq({-1, -2})
556+
5.tdivmod(-3).should eq({-1, 2})
557+
-5.tdivmod(-3).should eq({1, -2})
558+
end
559+
560+
it "preserves type of lhs" do
561+
{% for type in [UInt8, UInt16, UInt32, UInt64, Int8, Int16, Int32, Int64, UInt128, Int128] %}
562+
{{type}}.new(7).tdivmod(2).should be_a(Tuple({{type}}, {{type}}))
563+
{% end %}
564+
end
565+
566+
it "raises when divides by zero" do
567+
expect_raises(DivisionByZeroError) { 5.tdivmod(0) }
568+
end
569+
570+
it "raises when divides Int::MIN by -1" do
571+
expect_raises(ArgumentError) { Int8::MIN.tdivmod(-1) }
572+
expect_raises(ArgumentError) { Int16::MIN.tdivmod(-1) }
573+
expect_raises(ArgumentError) { Int32::MIN.tdivmod(-1) }
574+
expect_raises(ArgumentError) { Int64::MIN.tdivmod(-1) }
575+
expect_raises(ArgumentError) { Int128::MIN.tdivmod(-1) }
576+
577+
(UInt8::MIN.tdivmod(-1)).should eq({0, 0})
578+
end
525579
end
526580

527581
describe "fdiv" do
@@ -757,11 +811,33 @@ describe "Int" do
757811
end
758812
end
759813

760-
it "tdivs" do
761-
5.tdiv(3).should eq(1)
762-
-5.tdiv(3).should eq(-1)
763-
5.tdiv(-3).should eq(-1)
764-
-5.tdiv(-3).should eq(1)
814+
describe "tdiv" do
815+
it "divides self by argument using truncated division" do
816+
5.tdiv(3).should eq(1)
817+
-5.tdiv(3).should eq(-1)
818+
5.tdiv(-3).should eq(-1)
819+
-5.tdiv(-3).should eq(1)
820+
end
821+
822+
it "preserves type of lhs" do
823+
{% for type in [UInt8, UInt16, UInt32, UInt64, Int8, Int16, Int32, Int64, UInt128, Int128] %}
824+
{{type}}.new(7).tdiv(2).should be_a({{type}})
825+
{% end %}
826+
end
827+
828+
it "raises when divides by zero" do
829+
expect_raises(DivisionByZeroError) { 5.tdiv(0) }
830+
end
831+
832+
it "raises when divides Int::MIN by -1" do
833+
expect_raises(ArgumentError) { Int8::MIN.tdiv(-1) }
834+
expect_raises(ArgumentError) { Int16::MIN.tdiv(-1) }
835+
expect_raises(ArgumentError) { Int32::MIN.tdiv(-1) }
836+
expect_raises(ArgumentError) { Int64::MIN.tdiv(-1) }
837+
expect_raises(ArgumentError) { Int128::MIN.tdiv(-1) }
838+
839+
(UInt8::MIN.tdiv(-1)).should eq(0)
840+
end
765841
end
766842

767843
it "holds true that x == q*y + r" do

src/int.cr

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ struct Int
118118
#
119119
# In truncated division, given two integers x and y:
120120
# * `q = x.tdiv(y)` is rounded toward zero
121-
# * `r = x.remainder(y)` has the sign of the first argument
121+
# * `r = x.remainder(y)` has the sign of x
122122
# * `x == q*y + r`
123123
#
124124
# For example:
@@ -152,6 +152,39 @@ struct Int
152152
{% end %}
153153
end
154154

155+
# Returns a `Tuple` of two elements containing the quotient
156+
# and modulus obtained by dividing `self` by *number* using
157+
# truncated division.
158+
#
159+
# ```
160+
# 11.tdivmod(3) # => {3, 2}
161+
# 11.tdivmod(-3) # => {-4, -1}
162+
# ```
163+
#
164+
# In truncated division, given two integers x and y:
165+
# * `q = x.tdiv(y)` is rounded toward zero
166+
# * `r = x.remainder(y)` has the sign of x
167+
# * `x == q*y + r`
168+
#
169+
# For example:
170+
#
171+
# ```text
172+
# x y x / y x % y
173+
# 5 3 1 2
174+
# -5 3 -1 -2
175+
# 5 -3 -1 2
176+
# -5 -3 1 -2
177+
# ```
178+
#
179+
# Raises if *other* is `0`, or if *other* is `-1` and
180+
# `self` is signed and is the minimum value for that
181+
# integer type.
182+
def tdivmod(other : Int)
183+
check_div_argument other
184+
185+
{unsafe_div(other), unsafe_mod(other)}
186+
end
187+
155188
def fdiv(other) : Float64
156189
to_f / other
157190
end

0 commit comments

Comments
 (0)