mulAdd method

ProjectiveECCPoint mulAdd(
  1. BigInt selfMul,
  2. AbstractPoint otherPoint,
  3. BigInt otherMul
)

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 the otherPoint by.

Returns:

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);
}