"""Provides a coordinate model class."""
from ast import parse, Module
from importlib_resources.abc import Traversable
from importlib_resources import as_file
from typing import List, Any, MutableMapping
from public import public
from .formula.base import Formula
from .formula.efd import (
EFDFormula,
AdditionEFDFormula,
DoublingEFDFormula,
TriplingEFDFormula,
DifferentialAdditionEFDFormula,
LadderEFDFormula,
ScalingEFDFormula,
NegationEFDFormula,
)
[docs]
@public
class CoordinateModel:
"""A coordinate system for a particular model(form) of an elliptic curve."""
name: str
"""Name of the coordinate model"""
full_name: str
"""Full name."""
curve_model: Any
"""The curve model."""
variables: List[str]
"""Variables that the coordinate model uses."""
satisfying: List[Module]
"""Relationship between the coordinate system and affine coordinates."""
toaffine: List[Module]
"""Map to affine coordinates from system coordinates."""
tosystem: List[Module]
"""Map from coordinate system to affine coordinates."""
homogweights: MutableMapping[str, int]
"""Weights that homogenize the coordinates."""
parameters: List[str]
"""Coordinate system parameters."""
assumptions: List[Module]
"""Assumptions that need to hold for the curve to use this coordinate system,
also used to compute the values of the coordinate system parameters."""
neutral: List[Module]
"""Coordinates of the neutral point in the coordinate system, might contain expressions of parameters."""
formulas: MutableMapping[str, Formula]
"""Formulas available on the coordinate system."""
def __str__(self):
return f"{self.curve_model.shortname}/{self.name}"
def __repr__(self):
return (
f'{self.__class__.__name__}("{self.name}", curve_model={self.curve_model})'
)
[docs]
@public
class AffineCoordinateModel(CoordinateModel):
name = "affine"
full_name = "Affine coordinates"
def __init__(self, curve_model: Any):
self.curve_model = curve_model
self.variables = ["x", "y"]
self.satisfying = []
self.toaffine = []
self.tosystem = []
self.parameters = []
self.assumptions = []
self.neutral = []
self.formulas = {}
def __eq__(self, other):
if not isinstance(other, AffineCoordinateModel):
return False
return self.curve_model == other.curve_model
def __hash__(self):
return hash((self.curve_model, self.name))
class EFDCoordinateModel(CoordinateModel):
"""A coordinate model from [EFD]_ data."""
def __new__(cls, *args, **kwargs):
_, name, curve_model = args
if name in curve_model.coordinates:
return curve_model.coordinates[name]
return object.__new__(cls)
def __init__(self, dir_path: Traversable, name: str, curve_model: Any):
self.name = name
self.curve_model = curve_model
self.variables = []
self.satisfying = []
self.toaffine = []
self.tosystem = []
self.homogweights = {}
self.parameters = []
self.assumptions = []
self.neutral = []
self.formulas = {}
for entry in dir_path.iterdir():
with as_file(entry) as file_path:
if entry.is_dir():
self.__read_formula_dir(file_path, file_path.stem)
else:
self.__read_coordinates_file(file_path)
def __read_formula_dir(self, dir_path: Traversable, formula_type):
for entry in dir_path.iterdir():
with as_file(entry) as fpath:
if fpath.suffix == ".op3":
continue
formula_types = {
"addition": AdditionEFDFormula,
"doubling": DoublingEFDFormula,
"tripling": TriplingEFDFormula,
"diffadd": DifferentialAdditionEFDFormula,
"ladder": LadderEFDFormula,
"scaling": ScalingEFDFormula,
"negation": NegationEFDFormula,
}
cls = formula_types.get(formula_type, EFDFormula)
self.formulas[fpath.stem] = cls(
fpath, fpath.with_suffix(".op3"), fpath.stem, self
)
def __read_coordinates_file(self, file_path: Traversable):
with file_path.open("rb") as f:
line = f.readline().decode("ascii").rstrip()
while line:
if line.startswith("name"):
self.full_name = line[5:]
elif line.startswith("variable"):
self.variables.append(line[9:])
elif line.startswith("neutral"):
try:
code = parse(line[8:].replace("^", "**"), mode="exec")
self.neutral.append(code)
except SyntaxError:
pass
elif line.startswith("satisfying"):
try:
code = parse(line[11:].replace("^", "**"), mode="exec")
self.satisfying.append(code)
except SyntaxError:
pass
elif line.startswith("toaffine"):
try:
code = parse(line[9:].replace("^", "**"), mode="exec")
self.toaffine.append(code)
except SyntaxError:
pass
elif line.startswith("tosystem"):
try:
code = parse(line[9:].replace("^", "**"), mode="exec")
self.tosystem.append(code)
except SyntaxError:
pass
elif line.startswith("homogweight"):
try:
var, weight = line[11:].split("=")
self.homogweights[var.strip()] = int(weight)
except SyntaxError:
pass
elif line.startswith("parameter"):
self.parameters.append(line[10:])
elif line.startswith("assume"):
self.assumptions.append(
parse(line[7:].replace("^", "**"), mode="exec")
)
line = f.readline().decode("ascii").rstrip()
def __getnewargs__(self):
return None, self.name, self.curve_model
def __getstate__(self):
return {}
def __eq__(self, other):
if not isinstance(other, EFDCoordinateModel):
return False
return self.curve_model == other.curve_model and self.name == other.name
def __hash__(self):
return hash((self.curve_model, self.name))