Task: Montgomery reduction



C#: using System;
using System.Numerics;

namespace MontgomeryReduction {
    public static class Helper {
        public static int BitLength(this BigInteger v) {
            if (v < 0) {
                v *= -1;
            }

            int result = 0;
            while (v > 0) {
                v >>= 1;
                result++;
            }

            return result;
        }
    }

    struct Montgomery {
        public static readonly int BASE = 2;

        public BigInteger m;
        public BigInteger rrm;
        public int n;

        public Montgomery(BigInteger m) {
            if (m < 0 || m.IsEven) throw new ArgumentException();

            this.m = m;
            n = m.BitLength();
            rrm = (BigInteger.One << (n * 2)) % m;
        }

        public BigInteger Reduce(BigInteger t) {
            var a = t;

            for (int i = 0; i < n; i++) {
                if (!a.IsEven) a += m;
                a = a >> 1;
            }
            if (a >= m) a -= m;
            return a;
        }
    }

    class Program {
        static void Main(string[] args) {
            var m = BigInteger.Parse("750791094644726559640638407699");
            var x1 = BigInteger.Parse("540019781128412936473322405310");
            var x2 = BigInteger.Parse("515692107665463680305819378593");

            var mont = new Montgomery(m);
            var t1 = x1 * mont.rrm;
            var t2 = x2 * mont.rrm;

            var r1 = mont.Reduce(t1);
            var r2 = mont.Reduce(t2);
            var r = BigInteger.One << mont.n;

            Console.WriteLine("b :  {0}", Montgomery.BASE);
            Console.WriteLine("n :  {0}", mont.n);
            Console.WriteLine("r :  {0}", r);
            Console.WriteLine("m :  {0}", mont.m);
            Console.WriteLine("t1:  {0}", t1);
            Console.WriteLine("t2:  {0}", t2);
            Console.WriteLine("r1:  {0}", r1);
            Console.WriteLine("r2:  {0}", r2);
            Console.WriteLine();
            Console.WriteLine("Original x1       : {0}", x1);
            Console.WriteLine("Recovered from r1 : {0}", mont.Reduce(r1));
            Console.WriteLine("Original x2       : {0}", x2);
            Console.WriteLine("Recovered from r2 : {0}", mont.Reduce(r2));

            Console.WriteLine();
            Console.WriteLine("Montgomery computation of x1 ^ x2 mod m :");
            var prod = mont.Reduce(mont.rrm);
            var @base = mont.Reduce(x1 * mont.rrm);
            var exp = x2;
            while (exp.BitLength() > 0) {
                if (!exp.IsEven) prod = mont.Reduce(prod * @base);
                exp >>= 1;
                @base = mont.Reduce(@base * @base);
            }
            Console.WriteLine(mont.Reduce(prod));
            Console.WriteLine();
            Console.WriteLine("Alternate computation of x1 ^ x2 mod m :");
            Console.WriteLine(BigInteger.ModPow(x1, x2, m));
        }
    }
}


Go: package main

import (
    "fmt"
    "math/big"
    "math/rand"
    "time"
)


type mont struct {
    n  uint     
    m  *big.Int 
    r2 *big.Int 
}


func newMont(m *big.Int) *mont {
    if m.Bit(0) != 1 {
        return nil
    }
    n := uint(m.BitLen())
    x := big.NewInt(1)
    x.Sub(x.Lsh(x, n), m)
    return &mont{n, new(big.Int).Set(m), x.Mod(x.Mul(x, x), m)}
}


func (m mont) reduce(t *big.Int) *big.Int {
    a := new(big.Int).Set(t)
    for i := uint(0); i < m.n; i++ {
        if a.Bit(0) == 1 {
            a.Add(a, m.m)
        }
        a.Rsh(a, 1)
    }
    if a.Cmp(m.m) >= 0 {
        a.Sub(a, m.m)
    }
    return a
}


func main() {
    const n = 100 

    
    rnd := rand.New(rand.NewSource(time.Now().UnixNano()))
    one := big.NewInt(1)
    r1 := new(big.Int).Lsh(one, n-1)
    r2 := new(big.Int).Lsh(one, n-2)
    m := new(big.Int)
    m.Or(r1, m.Or(m.Lsh(m.Rand(rnd, r2), 1), one))

    
    mr := newMont(m)

    
    
    x1 := new(big.Int).Rand(rnd, m)
    x2 := new(big.Int).Rand(rnd, m)

    
    
    t1 := new(big.Int).Mul(x1, mr.r2)
    t2 := new(big.Int).Mul(x2, mr.r2)

    
    r1 = mr.reduce(t1)
    r2 = mr.reduce(t2)

    
    fmt.Println("b:  2")
    fmt.Println("n: ", mr.n)
    fmt.Println("r: ", new(big.Int).Lsh(one, mr.n))
    fmt.Println("m: ", mr.m)
    fmt.Println("t1:", t1)
    fmt.Println("t2:", t2)
    fmt.Println("r1:", r1)
    fmt.Println("r2:", r2)

    
    fmt.Println()
    fmt.Println("Original x1:       ", x1)
    fmt.Println("Recovererd from r1:", mr.reduce(r1))
    fmt.Println("Original x2:       ", x2)
    fmt.Println("Recovererd from r2:", mr.reduce(r2))

    
    fmt.Println("\nMontgomery computation of x1 ^ x2 mod m:")
    
    
    prod := mr.reduce(mr.r2)             
    base := mr.reduce(t1.Mul(x1, mr.r2)) 
    exp := new(big.Int).Set(x2)          
    for exp.BitLen() > 0 {
        if exp.Bit(0) == 1 {
            prod = mr.reduce(prod.Mul(prod, base))
        }
        exp.Rsh(exp, 1)
        base = mr.reduce(base.Mul(base, base))
    }
    fmt.Println(mr.reduce(prod))

    
    fmt.Println("\nLibrary-based computation of x1 ^ x2 mod m:")
    fmt.Println(new(big.Int).Exp(x1, x2, m))
}