/// Compute `limbs/R` (mod l), where R is the Montgomery modulus 2^260 public static UnpackedScalar montgomery_reduce(UInt128[] limbs) { (UInt128, ulong) part1(UInt128 sum) { var p = ((ulong)sum) * (Constant.LFACTOR) & (((ulong)1 << 52) - 1); return((sum + m(p, Constant.L.value.Span[0])) >> 52, p); } (UInt128, ulong) part2(UInt128 sum) { var w = ((ulong)sum) & (((ulong)1 << 52) - 1); return(sum >> 52, w); } // note: l3 is zero, so its multiplies can be skipped var l = Constant.L; var ls = l.value.Span; // the first half computes the Montgomery adjustment factor n, and begins adding n*l to make limbs divisible by R UInt128 carry; ulong n0; (carry, n0) = part1(limbs[0]); ulong n1; (carry, n1) = part1(carry + limbs[1] + m(n0, ls[1])); ulong n2; (carry, n2) = part1(carry + limbs[2] + m(n0, ls[2]) + m(n1, ls[1])); ulong n3; (carry, n3) = part1(carry + limbs[3] + m(n1, ls[2]) + m(n2, ls[1])); ulong n4; (carry, n4) = part1(carry + limbs[4] + m(n0, ls[4]) + m(n2, ls[2]) + m(n3, ls[1])); // limbs is divisible by R now, so we can divide by R by simply storing the upper half as the result ulong r0; (carry, r0) = part2(carry + limbs[5] + m(n1, ls[4]) + m(n3, ls[2]) + m(n4, ls[1])); ulong r1; (carry, r1) = part2(carry + limbs[6] + m(n2, ls[4]) + m(n4, ls[2])); ulong r2; (carry, r2) = part2(carry + limbs[7] + m(n3, ls[4])); ulong r3; (carry, r3) = part2(carry + limbs[8] + m(n4, ls[4])); var r4 = (ulong)carry; // result may be >= l, so attempt to subtract l return(UnpackedScalar.sub(new UnpackedScalar(new ulong[] { r0, r1, r2, r3, r4 }), l)); }
/// Compute `a + b` (mod l) public static UnpackedScalar add(UnpackedScalar a, UnpackedScalar b) { var sum = UnpackedScalar.zero(); var sums = sum.value.Span; var mask = ((ulong)1 << 52) - 1; var asp = a.value.Span; var bsp = b.value.Span; // a + b ulong carry = 0; for (int i = 0; i < 5; i++) { carry = asp[i] + bsp[i] + (carry >> 52); sums[i] = carry & mask; } // subtract l if the sum is >= l return(UnpackedScalar.sub(sum, Constant.L)); }
public static Scalar operator -(Scalar rhs) { return(UnpackedScalar.sub(one().unpack(), rhs.unpack()).pack()); }