mulAdd method
Multiplies this point by a scalar value selfMul
and adds another point
otherPoint
multiplied by otherMul
.
Parameters:
selfMul
: Scalar value to multiply this point by.otherPoint
: The other point to multiply.otherMul
: Scalar value to multiply theotherPoint
by.
Returns:
- A new ProjectiveECCPoint representing the result of the operation.
Implementation
ProjectiveECCPoint mulAdd(
BigInt selfMul, AbstractPoint otherPoint, BigInt otherMul) {
if (otherPoint.isInfinity || otherMul == BigInt.zero) {
return this * selfMul;
}
if (selfMul == BigInt.zero) {
return (otherPoint * otherMul) as ProjectiveECCPoint;
}
ProjectiveECCPoint other;
if (otherPoint is AffinePointt) {
other = ProjectiveECCPoint.fromAffine(otherPoint);
} else {
other = otherPoint as ProjectiveECCPoint;
}
_precomputeIfNeeded();
other._precomputeIfNeeded();
if (_precompute.isNotEmpty && other._precompute.isNotEmpty) {
return (this * selfMul + other * otherMul) as ProjectiveECCPoint;
}
if (order != null) {
selfMul = selfMul % order!;
otherMul = otherMul % order!;
}
// (X3, Y3, Z3) is the accumulator
BigInt x3 = BigInt.zero;
BigInt y3 = BigInt.zero;
BigInt z3 = BigInt.one;
BigInt p = curve.p;
BigInt a = curve.a;
scale();
BigInt x1 = _coords[0];
BigInt y1 = _coords[1];
BigInt z1 = _coords[2];
other.scale();
BigInt x2 = other._coords[0];
BigInt y2 = other._coords[1];
BigInt z2 = other._coords[2];
// with NAF we have 3 options: no add, subtract, add
// so with 2 points, we have 9 combinations:
// 0, -A, +A, -B, -A-B, +A-B, +B, -A+B, +A+B
// so we need 4 combined points:
List<BigInt> mAmB = _addPoints(x1, -y1, z1, x2, -y2, z2, p);
List<BigInt> pAmB = _addPoints(x1, y1, z1, x2, -y2, z2, p);
List<BigInt> mApB = [pAmB[0], -pAmB[1], pAmB[2]];
List<BigInt> pApB = [mAmB[0], -mAmB[1], mAmB[2]];
if (pApB[1] == BigInt.zero || pApB[2] == BigInt.zero) {
return (this * selfMul + other * otherMul) as ProjectiveECCPoint;
}
List<BigInt> selfNaf = BigintUtils.computeNAF(selfMul).reversed.toList();
List<BigInt> otherNaf = BigintUtils.computeNAF(otherMul).reversed.toList();
if (selfNaf.length < otherNaf.length) {
selfNaf =
List.filled(otherNaf.length - selfNaf.length, BigInt.zero) + selfNaf;
} else if (selfNaf.length > otherNaf.length) {
otherNaf =
List.filled(selfNaf.length - otherNaf.length, BigInt.zero) + otherNaf;
}
for (int i = 0; i < selfNaf.length; i++) {
BigInt A = selfNaf[i];
BigInt B = otherNaf[i];
List<BigInt> result = _double(x3, y3, z3, p, a);
// conditions ordered from most to least likely
if (A == BigInt.zero) {
if (B == BigInt.zero) {
// Do nothing.
} else if (B < BigInt.zero) {
result = _addPoints(result[0], result[1], result[2], x2, -y2, z2, p);
} else {
assert(B > BigInt.zero);
result = _addPoints(result[0], result[1], result[2], x2, y2, z2, p);
}
} else if (A < BigInt.zero) {
if (B == BigInt.zero) {
result = _addPoints(result[0], result[1], result[2], x1, -y1, z1, p);
} else if (B < BigInt.zero) {
result = _addPoints(
result[0], result[1], result[2], mAmB[0], mAmB[1], mAmB[2], p);
} else {
assert(B > BigInt.zero);
result = _addPoints(
result[0], result[1], result[2], mApB[0], mApB[1], mApB[2], p);
}
} else {
assert(A > BigInt.zero);
if (B == BigInt.zero) {
result = _addPoints(result[0], result[1], result[2], x1, y1, z1, p);
} else if (B < BigInt.zero) {
result = _addPoints(
result[0], result[1], result[2], pAmB[0], pAmB[1], pAmB[2], p);
} else {
assert(B > BigInt.zero);
result = _addPoints(
result[0], result[1], result[2], pApB[0], pApB[1], pApB[2], p);
}
}
x3 = result[0];
y3 = result[1];
z3 = result[2];
}
if (y3 == BigInt.zero || z3 == BigInt.zero) {
return ProjectiveECCPoint.infinity(curve);
}
return ProjectiveECCPoint._(curve, [x3, y3, z3], order: order);
}