Montgomery Reduction
Montgomery reduction provides an efficient way to perform modular arithmetic, particularly modular multiplication a⋅bmodn. This is crucial when the modulus n is large and many operations are needed, as in cryptography. Montgomery reduction excels when n is odd and aims to replace slow divisions by n.
The Core Idea
The method shifts calculations into a special "Montgomery domain" defined by an auxiliary modulus R. Arithmetic happens within this domain, where the reduction step is cleverly transformed into a division by R. Since R is chosen as a power of 2, this division is just a fast bit shift and the remainder is equivalent to its last few bits. Results are converted back to the standard representation when the sequence of operations is complete.
The Math Explained
The Montgomery Representation
We choose an auxiliary modulus R=bw (where b is the base, usually 2) such that R>n. Critically, n and R must be coprime: gcd(n,R)=1. In this case, this just implies n must be odd.
Definition 1. The Montgomery representation xˉ of an integer x (where 0≤x<n) is xˉ=x⋅Rmodn.
Since n and R are coprime, we can define the following conversion functions:
fromMont(xˉ): Converts xˉ back to standard form: x=xˉ⋅R−1modn. This operation is also called Montgomery Reduction.
toMont(x): Converts x into Montgomery form: xˉ=x⋅bwmodn. This calculation itself can be optimized with Montgomery Reduction.
Lemma 1. Remainder of the division by R=2w can be efficiently calculated as xmodR=lowbitsw(x) where lowbitsw(x) takes the least significant w bits of x.
Lemma 2. Quotient ⌊x/R⌋ can be efficiently computed using bit shift: x≫w.
Montgomery Multiplication
Given two numbers in Montgomery form, aˉ=a⋅Rmodn and bˉ=b⋅Rmodn. We would like to calculate cˉ≡(a⋅b)⋅Rmodn. This can be calculated using aˉ⋅bˉ⋅R−1 since:
To do the Montgomery Multiplication efficiently, we need to be able to calculate:
To get the actual c=a⋅bmodn, we will have to additionally convert back from montgomery form using fromMont(cˉ).
Montgomery Reduction
Definition 2. Montgomery Reduction is an efficient reduction method to calculate T⋅R−1modn for a given T<n⋅R. This operation can also be denoted as:
The key idea to perform Montgomery Reduction efficiently is to find an integer m such that T+m⋅n is exactly divisible by R. Then the desired result can be computed as:
What will happen if we precompute R−1 to compute T⋅R−1?
We need to find m such that T+m⋅n≡0modR
To calculate m efficiently, we use a precomputed value n′=−n−1modR. This n′ exists because gcd(n,R)=1. Now, we have m:=T⋅n′modR and REDC can be defined as:
And we have:
This means we just need to subtract n once if ((T+m⋅n)≫w)≥n to get the remaindermodn.
Overview of REDC(T):
REDC(T):Given T<n⋅R, and precomputed n′:=−n−1modR.
Calculate m=(T⋅n′)modR=lowbitsw(lowbitsw(T)⋅n′). Essentially, a w-bit non extended multiplication.
Compute x=R(T+m⋅n). (Involves one extended multiplication m⋅n, 3 limb additions, and one right shift by w).
Return x−n if x≥n, otherwise return x.
Subtraction Variant REDC'(T)
REDC'(T)This is a clever optimization to avoid a carry check when adding T+m⋅n together. Remember that the range of allowed T is [0,nR) and typically stored in 2 limbs (Tlow,Thigh). This is also the same for m⋅n; let's denote it as (mnlow,mnhigh). Then, x=R(T+mn) is calculated as following:
Sum the low limbs. sumlow=Tlow+mnlow.
Sum the high limbs. sumhigh=Thigh+mnhigh.
Add 1 to sumhigh if there was a carry in the low sum.
Set x=(sumlow+(sumhigh≪w))≫w (which is simply x=sumhigh)
The sum of low limbs is actually guaranteed to be 0 since T+m⋅n≡0modR. But we still need to calculate the sum of low limbs to check if it has a carry or not. Notice that, either the sum can be 0 or 2w for it to have w zero bits.
What if it was subtraction instead? Suppose we use another mneg to make T−mneg⋅n≡0modR. In this case, the x=R(T−mnegn) will be calculated as follows:
Subtract the low limbs. sublow=Tlow−mnegnlow
Subtract the high limbs. subhigh=Thigh−mnegnhigh
Subtract 1 from the subhigh if there was an underflow in the low subtraction.
Set x=(sublow+(subhigh≪w))≫w (which is simply x=subhigh).
Again, the low subtraction should have w trailing zero bits, so it can be only 0 because −2w is actually impossible. This is due to the simple fact that both operands are w-bit integer which are in the range [0,2w). Hence, the subtraction will never underflow. With this observation, the calculation is simplified to:
Subtract the high limbs. subhigh=Thigh−mnegnhigh
Set x=(sublow+(subhigh≪w))≫w (which is simply x=subhigh)
This effectively replaces 3 addition operations with 1 subtraction and such m′ can be easily calculated as:
Thus, in this scheme, our precomputed value would be n−1modR. Also, the range can be calculated as follows:
Overview of REDC'(T):
Given T<n⋅R, and precomputed ninv:=n−1modR.
Calculate m=(T⋅ninv)modR=lowbitsw(lowbitsw(T)⋅ninv). Essentially, a w-bit non extended multiplication.
Compute x=(T−m⋅n)/R. (Involves one extended multiplication m⋅n, 1 limb subtraction, and one right shift by k).
Return x+n if x<0, otherwise return x.
Subtraction variant can be only applied to single precision case where n<R. Otherwise, Tlow is actually multi-precision and Tlow−mnlow is non-zero and only the last limb will be zero.
Summary of Montgomery Reduction
With some precomputation and using Montgomery Reduction REDC, we can optimize the doMontMul(aˉ,bˉ), toMont(x), and fromMont(xˉ) operations as following:
Precompute Rsq′:=R2modn and n′:=−(n−1)modR:
Multi-precision Montgomery Reduction
Separated Operands Scanning (SOS)
Iterative Partial Reduction
In a multi-precision setting, suppose the modulo n has l limbs. Let R=Bl, where w is limb bit width and B=2w. To calculate T⋅R−1=T⋅B−lmodn, the multi-precision Montgomery Reduction operates iteratively on:
where Ti represents limb values. At each iteration, the limbs of T are reduced one by one which is done by partial reduction. The first partial reduction looks like so:
And after the next partial reduction, we will have:
and so on. After l iterations, we have T⋅B−lmodn with l limbs. The values of limbs change at each round because Ti(i)⋅B−1modn is also a multi-precision integer.
How to multiply by the inverse of B?
At round i, we need to calculate (Ti−1(i−1)⋅B−1modn). This can be done efficiently, similar to the single-precision case by adding some multiple of n to make it divisible by B:
Precompute n′:=−n−1modB.
Calculate mi:=Ti−1(i−1)⋅n′modB. Then we have Ti−1(i−1)+mi⋅n≡Ti−1(i−1)−Ti−1(i−1)⋅n−1⋅n≡0modB
Calculate Ti−1(i−1)⋅B−1≡(Ti−1(i−1)+mi⋅n)⋅B−1≡(Ti−1(i−1)+mi⋅n)≫wmodn .
Since mi<B and Ti−1(i−1)<B, we have
which means after step 3, we don't have to worry about subtracting n.
How many limbs are needed?
While trying to reduce, we add mi⋅n every round which can cause potential overflow in the largest limb. Let's calculate how much we added in total. If we omit dividing by B, at each round, we can see that at each round i, we add mi⋅n⋅Bi−1. This can be confusing but if you think of round i as adding some value mi⋅n to convert the (i−1)-th limb to 0, it can be more easier to see.
This gives that total sum can be T+n⋅R<R2+n⋅R<2R2=2⋅22lw=22lw+1 which needs 2l+1 limbs. For T, we already need 2l limbs and after the first step, the least significant limb will be 0's which can be discarded. In the first round, we have a maximum value of T+n<n⋅R+n=n(R+1)≤(R−1)(R+1)=R2−1 so it fits within the 2l limbs. So, if we discard the least significant limb after first round, we don't actually need extra limb space to handle the overflowing.
Summary of the Iteration step
A complete round i of partial reduction can be summarized by the following steps:
Compute mi=n′⋅Ti−1(i−1)modR (which costs 1×1 limb multiplication).
Compute mi⋅n (which costs 1×l limb multiplication).
Set T(i)=(T(i−1)+mi⋅n)≫w.
Now, let's calculate the upper bound on the final result of the reductions. First of all, we saw above that, the total added value has an upper bound of n⋅R, and using T<n⋅R, we have:
Therefore, the reduced value may require one additional subtraction to bring the value within the range [0,…,n−1]. Since we need l+1 limb multiplications for steps 1 and 2, over l rounds, we will need a total of l2+l limb multiplications.
SOS with Ingonyama Optimization
We have seen that we need to do l2+l multiplications per round for multi-precision Montgomery Reduction. However, Ingonyama showed that we can reduce it to l2+1. Let's see what happens if we just precompute B′=B−1modn and multiply the free coefficients with B′. Then, the partial reduction round i becomes:
Compute mi′=Ti−1(i−1)⋅B′ which is 1×l limb multiplication, resulting in a l+1 limb integer.
Set T(i)=(T(i−1)≫w)+mi′.
Then, similar to the previous calculation, the upper bound on the total addition will become:
So it hasn't changed. Which means the upper bound on the value after l reductions will also be the same:
This gives us the total cost of l⋅l=l2 limb multiplications. Ingonyama's article mentions that this reduction cannot be applied in the last step because T(l)=(T(l−1)≫w)+mi′ will result in a l+1 limb integer while in the traditional case, we have l limbs. Indeed it is true since we add it after the bit shift unlike the original version but if the result is less than 2⋅n, it shouldn't matter.
Coarsely Integrated Operands Scanning (CIOS)
In the SOS method above, we iteratively divided t by B l times where B denotes the limb base (2w) and l denotes the number of the limbs. l iterations gives the desired result t⋅R−1modn since R=Bl. Now we can discard the lower l limbs, which is technically equivalent to dividing by R.
Naively speaking, SOS algorithm follows this sequence:
Multiply two large integers in Montgomery form using a textbook method.
Perform Montgomery reduction to produce cˉ=aˉ⋅bˉ from aˉ⋅bˉ⋅R.
which results in its namesake "separated" in SOS.
CIOS, on the other hand, is different from SOS in that it interleaves the multiplication and reduction steps, limb by limb.
Pseudocode
Now we alternate between iterations of the outer loops for multiplication and reduction.
Interleaving multiplication and reduction is valid since the value of m in the i-th iteration of the outer loop for reduction depends only on the value t[i], which is completely computed by the i-th iteration of the outer loop for the multiplication.
Cost analysis
Addition
4l2+4l+2
4l2+4l+2
4l2+4l+2
Multiplication
2l2+l
2l2+l
2l2+1
Memory space
l+3
2l+2
2l+2
SOS with Ingonyama's optimization saves l−1 multiplications (2l2+1 vs. 2l2+l) whereas CIOS saves l−1 memory space compared to SOS (2l+2 vs. l+3) owing to not storing the total t. CIOS can be further optimized using EdMSM's trick.
References
Written by Batzorig Zorigoo and Carson Lee of Fractalyze
Last updated
