scheinerman/Mods.jl

incorrect answer: CRT(Mod{2^64}(0), Mod{2^128-1}(1))

Closed this issue · 2 comments

Here's the incorrect computation:

julia> using Mods

julia> m = UInt128(2)^64
0x00000000000000010000000000000000

julia> n = UInt128(2)^128-1
0xffffffffffffffffffffffffffffffff

julia> a = Mod{m}(0)
Mod{18446744073709551616}(0)

julia> b = Mod{n}(1)
Mod{340282366920938463463374607431768211455}(0)

julia> CRT(a, b)
0

That's obviously incorrect since 0 ≠ 1 (mod n). The correct answer is 2^128.

Well, this is an interesting problem that I'm not sure how to solve. The issue is already apparent when you define b = Mod{n}(1) and the result is Mod{n}(0). (So I don't think this has anything to do with CRT.)

A while back I looked into having the modulus be any sort of integer, including a BigInt, but as I recall one can't use a BigInt as a type parameter. Has that changed?

@StefanKarpinski Is the solution to restrict the modulus to be an Int (or an Int128)? I'm not sure how to proceed.

No, having non-primitive (immutable really) types as type parameters is unlikely to happen. Consider: if you have a = big(123) and b = big(123) are Mod{a} and Mod{b} the same type or not? What if you do Base.GMP.flipsign!(a, -1)? After that you have a == -123 and b == 123 still.

I guess the issue here is doing arithmetic with the modulus and having that arithmetic overflow? The best that can be done is probably either what you're doing (correct answer modulo the type), or a checked arithmetic error. Having a version of CRT solving that doesn't involve type parameters would also be handy, but this package may not be the place for it.