From 5b9dfd26fa9c82961726958fd4cc92c063b3a49e Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Sun, 6 Feb 2022 19:42:18 +0100 Subject: [PATCH 01/10] add copy to operate_to! on BigInts --- src/implementations/BigInt.jl | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/implementations/BigInt.jl b/src/implementations/BigInt.jl index b2cfbb71..cf155fc5 100644 --- a/src/implementations/BigInt.jl +++ b/src/implementations/BigInt.jl @@ -26,6 +26,12 @@ promote_operation(::typeof(+), ::Vararg{Type{BigInt},N}) where {N} = BigInt function operate_to!(output::BigInt, ::typeof(+), a::BigInt, b::BigInt) return Base.GMP.MPZ.add!(output, a, b) end +function operate_to!(output::BigInt, ::typeof(copy), a::BigInt) + return Base.GMP.MPZ.set!(output, a) +end +function operate_to!(output::BigInt, ::typeof(copy), a::Int) + return Base.GMP.MPZ.set_si!(output, a) +end # - From 1c9f37508f6e0b0421ad40fa6da104015600b98f Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Sun, 6 Feb 2022 19:42:44 +0100 Subject: [PATCH 02/10] add unary minus on BigInt --- src/implementations/BigInt.jl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/implementations/BigInt.jl b/src/implementations/BigInt.jl index cf155fc5..e3163fce 100644 --- a/src/implementations/BigInt.jl +++ b/src/implementations/BigInt.jl @@ -41,6 +41,10 @@ function operate_to!(output::BigInt, ::typeof(-), a::BigInt, b::BigInt) return Base.GMP.MPZ.sub!(output, a, b) end +function operate_to!(output::BigInt, ::typeof(-), a::BigInt) + return Base.GMP.MPZ.neg!(output, a) +end + # * promote_operation(::typeof(*), ::Vararg{Type{BigInt},N}) where {N} = BigInt From db3bc6640b1ecd3baf3dd914a2744a07d8ae6d43 Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Sun, 6 Feb 2022 19:45:04 +0100 Subject: [PATCH 03/10] add // to fallback operate --- src/interface.jl | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/interface.jl b/src/interface.jl index fc1c5f5d..7085cf01 100644 --- a/src/interface.jl +++ b/src/interface.jl @@ -172,9 +172,7 @@ function operate( ) where {N} return op(x, y, args...) end - -operate(op::Union{typeof(-),typeof(/)}, x, y) where {N} = op(x, y) - +operate(op::Union{typeof(-),typeof(/),typeof(//)}, x, y) = op(x, y) operate(::typeof(convert), ::Type{T}, x) where {T} = convert(T, x) operate(::typeof(convert), ::Type{T}, x::T) where {T} = copy_if_mutable(x) From 6833e462038af8db2e2257004539e2a848db371d Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Sun, 6 Feb 2022 19:46:52 +0100 Subject: [PATCH 04/10] add more complete rational arithmetic --- src/implementations/Rational.jl | 119 +++++++++++++++++++++++++++++--- test/rational.jl | 17 +++++ test/runtests.jl | 5 ++ 3 files changed, 131 insertions(+), 10 deletions(-) create mode 100644 test/rational.jl diff --git a/src/implementations/Rational.jl b/src/implementations/Rational.jl index 8bdd6d5b..d5d1b419 100644 --- a/src/implementations/Rational.jl +++ b/src/implementations/Rational.jl @@ -28,15 +28,30 @@ function operate!(::typeof(one), x::Rational) end # + - function promote_operation( - ::typeof(+), + ::Union{typeof(+),typeof(-)}, ::Type{Rational{S}}, - ::Type{Rational{T}}, + ::Type{Rational{T}} ) where {S,T} return Rational{promote_sum_mul(S, T)} end +function promote_operation( + op::Union{typeof(+),typeof(-)}, + ::Type{Rational{S}}, + ::Type{I} +) where {S,I<:Integer} + return promote_operation(op, Rational{S}, Rational{I}) +end + +function promote_operation( + op::Union{typeof(+),typeof(-)}, + ::Type{I}, + ::Type{Rational{S}} +) where {S,I<:Integer} + return promote_operation(op, Rational{S}, Rational{I}) +end + function operate_to!(output::Rational, ::typeof(+), x::Rational, y::Rational) xd, yd = Base.divgcd(promote(x.den, y.den)...) # TODO: Use `checked_mul` and `checked_add` like in Base @@ -46,16 +61,26 @@ function operate_to!(output::Rational, ::typeof(+), x::Rational, y::Rational) return output end -# - +function operate_to!(output::Rational, ::typeof(+), x::Rational, y::Integer) + # TODO Use `checked_mul` and `checked_add` like in Base + operate_to!(output.num, *, x.den, y) + operate!(+, output.num, x.num) + operate_to!(output.den, *, x.den, oftype(x.den, 1)) + return output +end -function promote_operation( - ::typeof(-), - ::Type{Rational{S}}, - ::Type{Rational{T}}, -) where {S,T} - return Rational{promote_sum_mul(S, T)} +function operate_to!(output::Rational, ::typeof(+), y::Integer, x::Rational) + return operate_to!(output, +, x, y) end +# unary - +function operate_to!(output::Rational, ::typeof(-), x::Rational) + operate_to!(output.num, -, x.num) + operate_to!(output.den, copy, x.den) + return output +end + +# binary - function operate_to!(output::Rational, ::typeof(-), x::Rational, y::Rational) xd, yd = Base.divgcd(promote(x.den, y.den)...) # TODO: Use `checked_mul` and `checked_sub` like in Base @@ -65,6 +90,22 @@ function operate_to!(output::Rational, ::typeof(-), x::Rational, y::Rational) return output end +function operate_to!(output::Rational, ::typeof(-), x::Rational, y::Integer) + # TODO Use `checked_mul` and `checked_sub` like in Base + operate_to!(output.num, *, x.den, y) + operate!(-, output.num) + operate!(+, output.num, x.num) + operate_to!(output.den, copy, x.den) + return output +end + +function operate_to!(output::Rational, ::typeof(-), y::Integer, x::Rational) + # TODO Use `checked_mul` and `checked_sub` like in Base + operate_to!(output, -, x, y) + operate_to!(output, -, output) + return output +end + # * function promote_operation( @@ -75,6 +116,22 @@ function promote_operation( return Rational{promote_operation(*, S, T)} end +function promote_operation( + ::typeof(*), + ::Type{Rational{S}}, + ::Type{I} +) where {S,I<:Integer} + return promote_operation(*, Rational{S}, Rational{I}) +end + +function promote_operation( + ::typeof(*), + ::Type{I}, + ::Type{Rational{S}} +) where {S,I<:Integer} + return promote_operation(*, Rational{S}, Rational{I}) +end + function operate_to!(output::Rational, ::typeof(*), x::Rational, y::Rational) xn, yd = Base.divgcd(promote(x.num, y.den)...) xd, yn = Base.divgcd(promote(x.den, y.num)...) @@ -83,6 +140,48 @@ function operate_to!(output::Rational, ::typeof(*), x::Rational, y::Rational) return output end +function operate_to!(output::Rational, ::typeof(*), x::Rational, y::Integer) + xn = x.num + xd, yn = Base.divgcd(promote(x.den, y)...) + operate_to!(output.num, *, xn, yn) + operate_to!(output.den, copy, x.den) + return output +end + +function operate_to!(output::Rational, ::typeof(*), y::Integer, x::Rational) + return operate_to!(output, *, x, y) +end + +# // +function operate_to!(output::Rational, op::Union{typeof(/), typeof(//)}, x::Rational, y::Rational) + xn, yn = Base.divgcd(promote(x.num, y.num)...) + xd, yd = Base.divgcd(promote(x.den, y.den)...) + operate_to!(output.num, *, xn, yd) + operate_to!(output.den, *, xd, yn) + return output +end + +function operate_to!(output::Rational, op::Union{typeof(/), typeof(//)}, x::Rational, y::Integer) + xn, yn = Base.divgcd(promote(x.num, y)...) + operate_to!(output.num, copy, xn) + operate_to!(output.den, *, x.den, yn) + return output +end + +function operate_to!(output::Rational, op::Union{typeof(/), typeof(//)}, x::Integer, y::Rational) + xn, yd = Base.divgcd(promote(x, y.den)...) + operate_to!(output.num, *, xn, yd) + operate_to!(output.den, copy, y.num) + return output +end + +function operate_to!(output::Rational, op::Union{typeof(/), typeof(//)}, x::Integer, y::Integer) + n, d = Base.divgcd(promote(x, y)...) + operate_to!(output.num, copy, n) + operate_to!(output.den, copy, d) + return output +end + # gcd function promote_operation( diff --git a/test/rational.jl b/test/rational.jl new file mode 100644 index 00000000..cb760a44 --- /dev/null +++ b/test/rational.jl @@ -0,0 +1,17 @@ +for op in (+, -, *, //) + for (a,b) in ( + (2//3, 5), (2, 3//5), (2//3, 5//7), + (big(2)//3, 5), (big(2), 3//5), (big(2)//3, 5//7), + ) + @test MA.operate_to!!(MA.copy_if_mutable(op(a,b)), op, a, b) == op(a, b) + @test MA.operate_to!!(MA.copy_if_mutable(op(b,a)), op, b, a) == op(b, a) + end +end + +op = // +for (a,b) in ( + (2,3), (big(2), 3), (2, big(3)) + ) + @test MA.operate_to!!(MA.copy_if_mutable(op(a,b)), op, a, b) == op(a,b) + @test MA.operate_to!!(MA.copy_if_mutable(op(b,a)), op, b, a) == op(b,a) +end diff --git a/test/runtests.jl b/test/runtests.jl index 442c2089..c45d22d6 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -14,6 +14,11 @@ end @testset "BigInt" begin include("big.jl") end + +@testset "Rational" begin + include("rational.jl") +end + @testset "Broadcast" begin include("broadcast.jl") end From d4f8d2e875bb0dda7ea1c43af09bd1fd91f92141 Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Sun, 6 Feb 2022 19:56:35 +0100 Subject: [PATCH 05/10] use Base.promote_op in promote_operation_fallback --- src/interface.jl | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/src/interface.jl b/src/interface.jl index 7085cf01..dc242750 100644 --- a/src/interface.jl +++ b/src/interface.jl @@ -20,28 +20,22 @@ function promote_operation_fallback( end function promote_operation_fallback( - ::typeof(/), + op::Function, ::Type{S}, ::Type{T}, ) where {S,T} - return typeof(zero(S) / oneunit(T)) + U = Base.promote_op(op, S, T) + return return U == Union{} ? typeof(op(oneunit(S), oneunit(T))) : U end # Julia v1.0.x has trouble with inference with the `Vararg` method, see # https://travis-ci.org/jump-dev/JuMP.jl/jobs/617606373 -function promote_operation_fallback( - op::F, - ::Type{S}, - ::Type{T}, -) where {F<:Function,S,T} - return typeof(op(zero(S), zero(T))) -end - function promote_operation_fallback( op::F, args::Vararg{Type,N}, ) where {F<:Function,N} - return typeof(op(zero.(args)...)) + U = Base.promote_op(op, args...) + return return U == Union{} ? typeof(op(oneunit.(args)...)) : U end promote_operation_fallback(::typeof(*), ::Type{T}) where {T} = T From e3268b4a61f97fa38d450f71bcafaa37b27e137d Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Mon, 7 Feb 2022 10:39:07 +0100 Subject: [PATCH 06/10] reducing allocations always feels great ;) --- test/broadcast.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/broadcast.jl b/test/broadcast.jl index 37ea93b5..5be18360 100644 --- a/test/broadcast.jl +++ b/test/broadcast.jl @@ -28,7 +28,7 @@ end if VERSION >= v"1.5" # FIXME This should not allocate but I couldn't figure out where these # 240 come from. - alloc_test(() -> MA.broadcast!!(+, a, b), 240) + alloc_test(() -> MA.broadcast!!(+, a, b), 80) alloc_test(() -> MA.broadcast!!(+, a, c), 0) end end From 17b8ee6bc452a215739e2d04236ff06106acbda0 Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Tue, 8 Feb 2022 09:22:03 +1300 Subject: [PATCH 07/10] Update BigInt.jl --- src/implementations/BigInt.jl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/implementations/BigInt.jl b/src/implementations/BigInt.jl index e3163fce..83d3d6f4 100644 --- a/src/implementations/BigInt.jl +++ b/src/implementations/BigInt.jl @@ -26,11 +26,13 @@ promote_operation(::typeof(+), ::Vararg{Type{BigInt},N}) where {N} = BigInt function operate_to!(output::BigInt, ::typeof(+), a::BigInt, b::BigInt) return Base.GMP.MPZ.add!(output, a, b) end + function operate_to!(output::BigInt, ::typeof(copy), a::BigInt) - return Base.GMP.MPZ.set!(output, a) + return Base.GMP.MPZ.set!(output, a) end + function operate_to!(output::BigInt, ::typeof(copy), a::Int) - return Base.GMP.MPZ.set_si!(output, a) + return Base.GMP.MPZ.set_si!(output, a) end # - From c812a9337ab54071b804dcc5d33e029f31572f2a Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Tue, 8 Feb 2022 09:25:45 +1300 Subject: [PATCH 08/10] Update Rational.jl --- src/implementations/Rational.jl | 44 +++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/src/implementations/Rational.jl b/src/implementations/Rational.jl index d5d1b419..0e0aa44d 100644 --- a/src/implementations/Rational.jl +++ b/src/implementations/Rational.jl @@ -28,10 +28,11 @@ function operate!(::typeof(one), x::Rational) end # + + function promote_operation( ::Union{typeof(+),typeof(-)}, ::Type{Rational{S}}, - ::Type{Rational{T}} + ::Type{Rational{T}}, ) where {S,T} return Rational{promote_sum_mul(S, T)} end @@ -39,7 +40,7 @@ end function promote_operation( op::Union{typeof(+),typeof(-)}, ::Type{Rational{S}}, - ::Type{I} + ::Type{I}, ) where {S,I<:Integer} return promote_operation(op, Rational{S}, Rational{I}) end @@ -47,7 +48,7 @@ end function promote_operation( op::Union{typeof(+),typeof(-)}, ::Type{I}, - ::Type{Rational{S}} + ::Type{Rational{S}}, ) where {S,I<:Integer} return promote_operation(op, Rational{S}, Rational{I}) end @@ -74,6 +75,7 @@ function operate_to!(output::Rational, ::typeof(+), y::Integer, x::Rational) end # unary - + function operate_to!(output::Rational, ::typeof(-), x::Rational) operate_to!(output.num, -, x.num) operate_to!(output.den, copy, x.den) @@ -81,6 +83,7 @@ function operate_to!(output::Rational, ::typeof(-), x::Rational) end # binary - + function operate_to!(output::Rational, ::typeof(-), x::Rational, y::Rational) xd, yd = Base.divgcd(promote(x.den, y.den)...) # TODO: Use `checked_mul` and `checked_sub` like in Base @@ -119,7 +122,7 @@ end function promote_operation( ::typeof(*), ::Type{Rational{S}}, - ::Type{I} + ::Type{I}, ) where {S,I<:Integer} return promote_operation(*, Rational{S}, Rational{I}) end @@ -127,7 +130,7 @@ end function promote_operation( ::typeof(*), ::Type{I}, - ::Type{Rational{S}} + ::Type{Rational{S}}, ) where {S,I<:Integer} return promote_operation(*, Rational{S}, Rational{I}) end @@ -149,11 +152,17 @@ function operate_to!(output::Rational, ::typeof(*), x::Rational, y::Integer) end function operate_to!(output::Rational, ::typeof(*), y::Integer, x::Rational) - return operate_to!(output, *, x, y) + return operate_to!(output, *, x, y) end # // -function operate_to!(output::Rational, op::Union{typeof(/), typeof(//)}, x::Rational, y::Rational) + +function operate_to!( + output::Rational, + op::Union{typeof(/),typeof(//)}, + x::Rational, + y::Rational, +) xn, yn = Base.divgcd(promote(x.num, y.num)...) xd, yd = Base.divgcd(promote(x.den, y.den)...) operate_to!(output.num, *, xn, yd) @@ -161,21 +170,36 @@ function operate_to!(output::Rational, op::Union{typeof(/), typeof(//)}, x::Rati return output end -function operate_to!(output::Rational, op::Union{typeof(/), typeof(//)}, x::Rational, y::Integer) +function operate_to!( + output::Rational, + op::Union{typeof(/),typeof(//)}, + x::Rational, + y::Integer, +) xn, yn = Base.divgcd(promote(x.num, y)...) operate_to!(output.num, copy, xn) operate_to!(output.den, *, x.den, yn) return output end -function operate_to!(output::Rational, op::Union{typeof(/), typeof(//)}, x::Integer, y::Rational) +function operate_to!( + output::Rational, + op::Union{typeof(/),typeof(//)}, + x::Integer, + y::Rational, +) xn, yd = Base.divgcd(promote(x, y.den)...) operate_to!(output.num, *, xn, yd) operate_to!(output.den, copy, y.num) return output end -function operate_to!(output::Rational, op::Union{typeof(/), typeof(//)}, x::Integer, y::Integer) +function operate_to!( + output::Rational, + op::Union{typeof(/),typeof(//)}, + x::Integer, + y::Integer, +) n, d = Base.divgcd(promote(x, y)...) operate_to!(output.num, copy, n) operate_to!(output.den, copy, d) From b6ab947f4754173901d78775cb277cf70ecb125c Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Tue, 8 Feb 2022 10:32:31 +1300 Subject: [PATCH 09/10] Update rational.jl --- test/rational.jl | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/test/rational.jl b/test/rational.jl index cb760a44..eb328911 100644 --- a/test/rational.jl +++ b/test/rational.jl @@ -1,17 +1,21 @@ for op in (+, -, *, //) for (a,b) in ( - (2//3, 5), (2, 3//5), (2//3, 5//7), - (big(2)//3, 5), (big(2), 3//5), (big(2)//3, 5//7), - ) - @test MA.operate_to!!(MA.copy_if_mutable(op(a,b)), op, a, b) == op(a, b) - @test MA.operate_to!!(MA.copy_if_mutable(op(b,a)), op, b, a) == op(b, a) + (2 // 3, 5), + (2, 3 // 5), + (2 // 3, 5 // 7), + (big(2) // 3, 5), + (big(2), 3 // 5), + (big(2) // 3, 5 // 7), + ) + @test MA.operate_to!!(MA.copy_if_mutable(op(a, b)), op, a, b) == + op(a, b) + @test MA.operate_to!!(MA.copy_if_mutable(op(b, a)), op, b, a) == + op(b, a) end end op = // -for (a,b) in ( - (2,3), (big(2), 3), (2, big(3)) - ) +for (a,b) in ((2, 3), (big(2), 3), (2, big(3))) @test MA.operate_to!!(MA.copy_if_mutable(op(a,b)), op, a, b) == op(a,b) @test MA.operate_to!!(MA.copy_if_mutable(op(b,a)), op, b, a) == op(b,a) end From 4533ae396e9c7348710869fb67925abde8e19142 Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Tue, 8 Feb 2022 10:32:49 +1300 Subject: [PATCH 10/10] Update rational.jl --- test/rational.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/rational.jl b/test/rational.jl index eb328911..9fb00e6d 100644 --- a/test/rational.jl +++ b/test/rational.jl @@ -15,7 +15,7 @@ for op in (+, -, *, //) end op = // -for (a,b) in ((2, 3), (big(2), 3), (2, big(3))) - @test MA.operate_to!!(MA.copy_if_mutable(op(a,b)), op, a, b) == op(a,b) - @test MA.operate_to!!(MA.copy_if_mutable(op(b,a)), op, b, a) == op(b,a) +for (a, b) in ((2, 3), (big(2), 3), (2, big(3))) + @test MA.operate_to!!(MA.copy_if_mutable(op(a, b)), op, a, b) == op(a, b) + @test MA.operate_to!!(MA.copy_if_mutable(op(b, a)), op, b, a) == op(b, a) end