I’m writing a powMod function which I have to use quite intensively. The starting point is a custom pow function:
// Compute power using multiplication and square.
// pow (*) (^2) 1 x n = x^n
let pow mul sq one x n =
let rec loop x' n' acc =
match n' with
| 0 -> acc
| _ -> let q = n'/2
let r = n'%2
let x2 = sq x'
if r = 0 then
loop x2 q acc
else
loop x2 q (mul x' acc)
loop x n one
After inspecting the range of my input, I chose int64 because it is big enough to represent output and I can avoid expensive calculation with bigint:
let mulMod m a b = (a*b)%m
let squareMod m a = mulMod m a a
let powMod m = pow (mulMod m) (squareMod m) 1L
I assume that modulo (m) is bigger than multipliers (a, b) and functions work on non-negative numbers only. The powMod function is correct for most cases; however, the problem lies in the mulMod function where a*b may be over int64 range but (a*b)%m is not.
The below example demonstrates the overflow issue:
let a = (pown 2L 40) - 1L
let b = (pown 2L 32) - 1L
let p = powMod a b 2 // p = -8589934591L -- wrong
Is there any way to avoid int64 overflow without resorting to bigint type?
The problem that you have is that all of your intermediate calculations are implicitly mod 264, and it’s not generally true that
which is what you’re calculating.
I can’t think of a simple way to do the correct calculation using just 64-bit numbers, but you don’t have to go all the way up to bigints; if
aandbhave at most 64 bits, then their full product has at most 128 bits, so you can keep track of the product in two 64-bit integers (here bundled as a custom struct):Having said that, since
bigints will give you the correct answer, why not just usebigints to do the intermediate calculation and convert to long at the end (which is guaranteed to be a lossless conversion given m’s range)? I suspect that the performance penalty for using bigints should be acceptable for most applications (compared to the headache of maintaining your own math routines).