public class LongElementaryModMath extends Object
long
data.Modular addition and subtraction are trivial, when the modulus is less than 2^{63} and overflow can be detected easily.
Modular multiplication is more complicated, and since it is usually the single most time consuming operation in the whole program execution, the very core of the Number Theoretic Transform (NTT), it should be carefully optimized.
The algorithm for multiplying two long
s and taking the
remainder is not entirely obvious. The basic problem is to get the
full 128bit result of multiplying two 64bit integers. It would be
possible to do this by splitting the arguments to high and low 32bit
words and performing four multiplications. The performance of this
solution would be not very good.
Another approach is to use long
s only for getting the lowest
64 bits of the result. Casting the operands to double
and
multiplying as floatingpoint numbers, we can get the highest (roughly) 52
bits of the result. However since only 116 bits can be acquired this
way, it would be possible to only use 58 bits in each of the multiplication
operands (not the full 64 or 63 bits). Furthermore, roundoff errors in
the floatingpoint multiplications, as allowed by the IEEE specification,
actually prevent getting even 52 of the top bits accurately, and actually
only 57 bits can be used in the multiplication operands. This is the
approach chosen in this implementation.
The first observation is that since the modulus is practically constant, it should be more efficient to calculate (once) the inverse of the modulus, and then subsequently multiply by the inverse modulus instead of dividing by the modulus.
The second observation is that to get the remainder of the division, we don't necessarily need the actual result of the division (we just want the remainder). So, we should discard the topmost 50 bits of the full 114bit result whenever possible, to save a few operations.
The basic approach is to get an approximation of a * b / modulus
(using floatingpoint operands, that is double
s). The approximation
should be within +1 or 1 of the correct result. We first calculate
a * b  approximateDivision * modulus
to get the initial remainder.
This calculation can use the lowest 64 bits only and is done using long
s.
It is enough to use a double
to do the approximate division, as it eliminates
at least 51 bits from the top of the 114bit multiplication result, leaving at
most 63 bits in the remainder. The calculation result  approximateDivision * modulus
must then be done once more to reduce the remainder since the original multiplication operands
are only 57bit numbers. The second reduction reduces the results to the correct value ±modulus.
It is then easy to detect the case when the approximate division was off by one (and the
remainder is ±modulus
off) as the final step of the algorithm.
Constructor and Description 

LongElementaryModMath()
Default constructor.

Modifier and Type  Method and Description 

long 
getModulus()
Get the modulus.

long 
modAdd(long a,
long b)
Modular addition.

long 
modMultiply(long a,
long b)
Modular multiplication.

long 
modSubtract(long a,
long b)
Modular subtraction.

void 
setModulus(long modulus)
Set the modulus.

public final long modMultiply(long a, long b)
a
 First operand.b
 Second operand.a * b % modulus
public final long modAdd(long a, long b)
a
 First operand.b
 Second operand.(a + b) % modulus
public final long modSubtract(long a, long b)
a
 First operand.b
 Second operand.(a  b + modulus) % modulus
public final long getModulus()
public final void setModulus(long modulus)
modulus
 The modulus.Copyright © 2017. All rights reserved.