text: from unittest import TestCase
  
  from parameterized import parameterized
  
  from pyecsca.ec.params import get_params
  from pyecsca.ec.mult import (
      LTRMultiplier,
      RTLMultiplier,
      LadderMultiplier,
      BinaryNAFMultiplier,
      WindowNAFMultiplier,
      SimpleLadderMultiplier,
      DifferentialLadderMultiplier,
      CoronMultiplier,
  )
  from pyecsca.ec.point import InfinityPoint
  from .utils import cartesian
  
  
  class ScalarMultiplierTests(TestCase):
      def setUp(self):
          self.secp128r1 = get_params("secg", "secp128r1", "projective")
          self.base = self.secp128r1.generator
          self.coords = self.secp128r1.curve.coordinate_model
  
          self.curve25519 = get_params("other", "Curve25519", "xz")
          self.base25519 = self.curve25519.generator
          self.coords25519 = self.curve25519.curve.coordinate_model
  
      def get_formulas(self, coords, *names):
          return [coords.formulas[name] for name in names if name is not None]
  
      def assertPointEquality(self, one, other, scale):
          if scale:
              self.assertEqual(one, other)
          else:
              assert one.equals(other)
  
      def do_basic_test(
          self, mult_class, params, base, add, dbl, scale, neg=None, **kwargs
      ):
          mult = mult_class(
              *self.get_formulas(params.curve.coordinate_model, add, dbl, neg, scale),
              **kwargs
          )
          mult.init(params, base)
          res = mult.multiply(314)
          other = mult.multiply(157)
          mult.init(params, other)
          other = mult.multiply(2)
          self.assertPointEquality(res, other, scale)
          mult.init(params, base)
          self.assertEqual(InfinityPoint(params.curve.coordinate_model), mult.multiply(0))
          return res
  
      @parameterized.expand(
          [
              ("scaled", "add-1998-cmo", "dbl-1998-cmo", "z"),
              ("complete", "add-2016-rcb", "dbl-2016-rcb", None),
              ("none", "add-1998-cmo", "dbl-1998-cmo", None),
          ]
      )
      def test_rtl(self, name, add, dbl, scale):
          self.do_basic_test(RTLMultiplier, self.secp128r1, self.base, add, dbl, scale)
  
      @parameterized.expand(
          [
              ("scaled", "add-1998-cmo", "dbl-1998-cmo", "z"),
              ("complete", "add-2016-rcb", "dbl-2016-rcb", None),
              ("none", "add-1998-cmo", "dbl-1998-cmo", None),
          ]
      )
      def test_ltr(self, name, add, dbl, scale):
          a = self.do_basic_test(
              LTRMultiplier, self.secp128r1, self.base, add, dbl, scale
          )
          b = self.do_basic_test(
              LTRMultiplier, self.secp128r1, self.base, add, dbl, scale, always=True
          )
          c = self.do_basic_test(
              LTRMultiplier, self.secp128r1, self.base, add, dbl, scale, complete=False
          )
          d = self.do_basic_test(
              LTRMultiplier,
              self.secp128r1,
              self.base,
              add,
              dbl,
              scale,
              always=True,
              complete=False,
          )
          self.assertPointEquality(a, b, scale)
          self.assertPointEquality(b, c, scale)
          self.assertPointEquality(c, d, scale)
  
      @parameterized.expand(
          [
              ("scaled", "add-1998-cmo", "dbl-1998-cmo", "z"),
              ("complete", "add-2016-rcb", "dbl-2016-rcb", None),
              ("none", "add-1998-cmo", "dbl-1998-cmo", None),
          ]
      )
      def test_coron(self, name, add, dbl, scale):
          self.do_basic_test(CoronMultiplier, self.secp128r1, self.base, add, dbl, scale)
  
      def test_ladder(self):
          a = self.do_basic_test(
              LadderMultiplier,
              self.curve25519,
              self.base25519,
              "ladd-1987-m",
              "dbl-1987-m",
              "scale",
          )
          b = self.do_basic_test(
              LadderMultiplier,
              self.curve25519,
              self.base25519,
              "ladd-1987-m",
              "dbl-1987-m",
              "scale",
              complete=False,
          )
          self.assertPointEquality(a, b, True)
  
      @parameterized.expand(
          [
              ("scaled", "add-1998-cmo", "dbl-1998-cmo", "z"),
              ("complete", "add-2016-rcb", "dbl-2016-rcb", None),
              ("none", "add-1998-cmo", "dbl-1998-cmo", None),
          ]
      )
      def test_simple_ladder(self, name, add, dbl, scale):
          self.do_basic_test(
              SimpleLadderMultiplier, self.secp128r1, self.base, add, dbl, scale
          )
  
      @parameterized.expand(
          [
              ("15", 15, True),
              ("15", 15, False),
              ("2355498743", 2355498743, True),
              ("2355498743", 2355498743, False),
              (
                  "325385790209017329644351321912443757746",
                  325385790209017329644351321912443757746,
                  True,
              ),
              (
                  "325385790209017329644351321912443757746",
                  325385790209017329644351321912443757746,
                  False,
              ),
          ]
      )
      def test_ladder_differential(self, name, num, complete):
          ladder = LadderMultiplier(
              self.coords25519.formulas["ladd-1987-m"],
              self.coords25519.formulas["dbl-1987-m"],
              self.coords25519.formulas["scale"],
              complete=complete,
          )
          differential = DifferentialLadderMultiplier(
              self.coords25519.formulas["dadd-1987-m"],
              self.coords25519.formulas["dbl-1987-m"],
              self.coords25519.formulas["scale"],
              complete=complete,
          )
          ladder.init(self.curve25519, self.base25519)
          res_ladder = ladder.multiply(num)
          differential.init(self.curve25519, self.base25519)
          res_differential = differential.multiply(num)
          self.assertEqual(res_ladder, res_differential)
          self.assertEqual(InfinityPoint(self.coords25519), differential.multiply(0))
  
      @parameterized.expand(
          [
              ("scaled", "add-1998-cmo", "dbl-1998-cmo", "neg", "z"),
              ("complete", "add-2016-rcb", "dbl-2016-rcb", "neg", None),
              ("none", "add-1998-cmo", "dbl-1998-cmo", "neg", None),
          ]
      )
      def test_binary_naf(self, name, add, dbl, neg, scale):
          self.do_basic_test(
              BinaryNAFMultiplier, self.secp128r1, self.base, add, dbl, scale, neg
          )
  
      @parameterized.expand(
          [
              ("scaled3", "add-1998-cmo", "dbl-1998-cmo", "neg", 3, "z"),
              ("none3", "add-1998-cmo", "dbl-1998-cmo", "neg", 3, None),
              ("complete3", "add-2016-rcb", "dbl-2016-rcb", "neg", 3, None),
              ("scaled5", "add-1998-cmo", "dbl-1998-cmo", "neg", 5, "z"),
              ("none5", "add-1998-cmo", "dbl-1998-cmo", "neg", 5, None),
              ("complete5", "add-2016-rcb", "dbl-2016-rcb", "neg", 5, None),
          ]
      )
      def test_window_naf(self, name, add, dbl, neg, width, scale):
          formulas = self.get_formulas(self.coords, add, dbl, neg, scale)
          mult = WindowNAFMultiplier(*formulas[:3], width, *formulas[3:])
          mult.init(self.secp128r1, self.base)
          res = mult.multiply(157 * 789)
          other = mult.multiply(157)
          mult.init(self.secp128r1, other)
          other = mult.multiply(789)
          self.assertPointEquality(res, other, scale)
          mult.init(self.secp128r1, self.base)
          self.assertEqual(InfinityPoint(self.coords), mult.multiply(0))
  
          mult = WindowNAFMultiplier(
              *formulas[:3], width, *formulas[3:], precompute_negation=True
          )
          mult.init(self.secp128r1, self.base)
          res_precompute = mult.multiply(157 * 789)
          self.assertPointEquality(res_precompute, res, scale)
  
      @parameterized.expand(
          cartesian(
              [
                  ("10", 10),
                  ("2355498743", 2355498743),
                  (
                      "325385790209017329644351321912443757746",
                      325385790209017329644351321912443757746,
                  ),
              ],
              [("add-1998-cmo", "dbl-1998-cmo"), ("add-2016-rcb", "dbl-2016-rcb")],
          )
      )
      def test_basic_multipliers(self, name, num, add, dbl):
          ltr = LTRMultiplier(
              self.coords.formulas[add],
              self.coords.formulas[dbl],
              self.coords.formulas["z"],
          )
          with self.assertRaises(ValueError):
              ltr.multiply(1)
          ltr.init(self.secp128r1, self.base)
          res_ltr = ltr.multiply(num)
          rtl = RTLMultiplier(
              self.coords.formulas[add],
              self.coords.formulas["dbl-1998-cmo"],
              self.coords.formulas["z"],
          )
          with self.assertRaises(ValueError):
              rtl.multiply(1)
          rtl.init(self.secp128r1, self.base)
          res_rtl = rtl.multiply(num)
          self.assertEqual(res_ltr, res_rtl)
  
          ltr_always = LTRMultiplier(
              self.coords.formulas[add],
              self.coords.formulas[dbl],
              self.coords.formulas["z"],
              always=True,
          )
          rtl_always = RTLMultiplier(
              self.coords.formulas[add],
              self.coords.formulas[dbl],
              self.coords.formulas["z"],
              always=True,
          )
          ltr_always.init(self.secp128r1, self.base)
          rtl_always.init(self.secp128r1, self.base)
          res_ltr_always = ltr_always.multiply(num)
          res_rtl_always = rtl_always.multiply(num)
          self.assertEqual(res_ltr, res_ltr_always)
          self.assertEqual(res_rtl, res_rtl_always)
  
          bnaf = BinaryNAFMultiplier(
              self.coords.formulas[add],
              self.coords.formulas[dbl],
              self.coords.formulas["neg"],
              self.coords.formulas["z"],
          )
          with self.assertRaises(ValueError):
              bnaf.multiply(1)
          bnaf.init(self.secp128r1, self.base)
          res_bnaf = bnaf.multiply(num)
          self.assertEqual(res_bnaf, res_ltr)
  
          wnaf = WindowNAFMultiplier(
              self.coords.formulas[add],
              self.coords.formulas[dbl],
              self.coords.formulas["neg"],
              3,
              self.coords.formulas["z"],
          )
          with self.assertRaises(ValueError):
              wnaf.multiply(1)
          wnaf.init(self.secp128r1, self.base)
          res_wnaf = wnaf.multiply(num)
          self.assertEqual(res_wnaf, res_ltr)
  
          ladder = SimpleLadderMultiplier(
              self.coords.formulas[add],
              self.coords.formulas[dbl],
              self.coords.formulas["z"],
          )
          with self.assertRaises(ValueError):
              ladder.multiply(1)
          ladder.init(self.secp128r1, self.base)
          res_ladder = ladder.multiply(num)
          self.assertEqual(res_ladder, res_ltr)
  
          coron = CoronMultiplier(
              self.coords.formulas[add],
              self.coords.formulas[dbl],
              self.coords.formulas["z"],
          )
          with self.assertRaises(ValueError):
              coron.multiply(1)
          coron.init(self.secp128r1, self.base)
          res_coron = coron.multiply(num)
          self.assertEqual(res_coron, res_ltr)
  
      def test_init_fail(self):
          mult = DifferentialLadderMultiplier(
              self.coords25519.formulas["dadd-1987-m"],
              self.coords25519.formulas["dbl-1987-m"],
              self.coords25519.formulas["scale"],
          )
          with self.assertRaises(ValueError):
              mult.init(self.secp128r1, self.base)
  
          with self.assertRaises(ValueError):
              LadderMultiplier(
                  self.coords25519.formulas["ladd-1987-m"],
                  scl=self.coords25519.formulas["scale"],
                  complete=False,
              )
  