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 theotherPointby.
Returns:
- A new ProjectiveECCPoint representing the result of the operation.
Implementation
ProjectiveECCPoint mulAdd(
BigInt selfMul,
ProjectivePointNative otherPoint,
BigInt otherMul,
) {
if (otherPoint.isZero() || otherMul == BigInt.zero) {
return this * selfMul;
}
if (selfMul == BigInt.zero) {
return (otherPoint * otherMul).cast<ProjectiveECCPoint>();
}
final other = switch (otherPoint) {
final ProjectiveECCPoint point => point,
_ => ProjectiveECCPoint.fromAffine(otherPoint),
};
_precomputeIfNeeded();
other._precomputeIfNeeded();
if (_precompute.isNotEmpty && other._precompute.isNotEmpty) {
return (this * selfMul + other * otherMul).cast<ProjectiveECCPoint>();
}
final order = this.order;
if (order != null) {
selfMul = selfMul % order;
otherMul = otherMul % order;
}
BigInt x3 = BigInt.zero;
BigInt y3 = BigInt.zero;
BigInt z3 = BigInt.one;
final BigInt p = curve.p;
final BigInt a = curve.a;
scale();
final BigInt x1 = _coords[0];
final BigInt y1 = _coords[1];
final BigInt z1 = _coords[2];
other.scale();
final BigInt x2 = other._coords[0];
final BigInt y2 = other._coords[1];
final 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:
final List<BigInt> mAmB = _addPoints(x1, -y1, z1, x2, -y2, z2, p);
final List<BigInt> pAmB = _addPoints(x1, y1, z1, x2, -y2, z2, p);
final List<BigInt> mApB = [pAmB[0], -pAmB[1], pAmB[2]];
final 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 = ECDSAUtils.computeNAF(selfMul).reversed.toList();
List<BigInt> otherNaf = ECDSAUtils.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++) {
final BigInt A = selfNaf[i];
final 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);
}