elliptic-curve-databases 0.8.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. elliptic_curve_databases/__init__.py +152 -0
  2. elliptic_curve_databases/data/__init__.py +1 -0
  3. elliptic_curve_databases/data/cremona/cremona_mini.db +0 -0
  4. elliptic_curve_databases/data/ellcurves/rank0 +30427 -0
  5. elliptic_curve_databases/data/ellcurves/rank1 +31871 -0
  6. elliptic_curve_databases/data/ellcurves/rank10 +6 -0
  7. elliptic_curve_databases/data/ellcurves/rank11 +6 -0
  8. elliptic_curve_databases/data/ellcurves/rank12 +1 -0
  9. elliptic_curve_databases/data/ellcurves/rank14 +1 -0
  10. elliptic_curve_databases/data/ellcurves/rank15 +1 -0
  11. elliptic_curve_databases/data/ellcurves/rank17 +1 -0
  12. elliptic_curve_databases/data/ellcurves/rank19 +1 -0
  13. elliptic_curve_databases/data/ellcurves/rank2 +2388 -0
  14. elliptic_curve_databases/data/ellcurves/rank20 +1 -0
  15. elliptic_curve_databases/data/ellcurves/rank21 +1 -0
  16. elliptic_curve_databases/data/ellcurves/rank22 +1 -0
  17. elliptic_curve_databases/data/ellcurves/rank23 +1 -0
  18. elliptic_curve_databases/data/ellcurves/rank24 +1 -0
  19. elliptic_curve_databases/data/ellcurves/rank28 +1 -0
  20. elliptic_curve_databases/data/ellcurves/rank3 +836 -0
  21. elliptic_curve_databases/data/ellcurves/rank4 +10 -0
  22. elliptic_curve_databases/data/ellcurves/rank5 +5 -0
  23. elliptic_curve_databases/data/ellcurves/rank6 +5 -0
  24. elliptic_curve_databases/data/ellcurves/rank7 +5 -0
  25. elliptic_curve_databases/data/ellcurves/rank8 +6 -0
  26. elliptic_curve_databases/data/ellcurves/rank9 +7 -0
  27. elliptic_curve_databases-0.8.3.dist-info/METADATA +130 -0
  28. elliptic_curve_databases-0.8.3.dist-info/RECORD +32 -0
  29. elliptic_curve_databases-0.8.3.dist-info/WHEEL +5 -0
  30. elliptic_curve_databases-0.8.3.dist-info/licenses/DATA_LICENSES.md +24 -0
  31. elliptic_curve_databases-0.8.3.dist-info/licenses/LICENSE +338 -0
  32. elliptic_curve_databases-0.8.3.dist-info/top_level.txt +1 -0
@@ -0,0 +1,152 @@
1
+ """Access the elliptic-curve databases without depending on SageMath."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import ast
6
+ import sqlite3
7
+ from collections.abc import Iterator
8
+ from contextlib import contextmanager
9
+ from dataclasses import dataclass
10
+ from importlib.resources import as_file, files
11
+ from pathlib import Path
12
+ from typing import Any
13
+
14
+
15
+ __version__ = "0.8.3"
16
+ __all__ = [
17
+ "EllipticCurveRecord",
18
+ "available_ranks",
19
+ "cremona_mini_connection",
20
+ "cremona_mini_path",
21
+ "cremona_mini_resource",
22
+ "data_root",
23
+ "iter_rank",
24
+ "rank_path",
25
+ "rank_resource",
26
+ ]
27
+
28
+
29
+ @dataclass(frozen=True, slots=True)
30
+ class EllipticCurveRecord:
31
+ """One record from a rank-grouped elliptic-curve text database."""
32
+
33
+ conductor: int
34
+ isogeny_class: str
35
+ number: int
36
+ ainvs: tuple[int, int, int, int, int]
37
+ rank: int
38
+ torsion_order: int
39
+
40
+ @property
41
+ def class_label(self) -> str:
42
+ """Return the Cremona isogeny-class label, such as ``11a``."""
43
+ return f"{self.conductor}{self.isogeny_class}"
44
+
45
+ @property
46
+ def label(self) -> str:
47
+ """Return the full Cremona curve label, such as ``11a1``."""
48
+ return f"{self.class_label}{self.number}"
49
+
50
+
51
+ def data_root() -> Any:
52
+ """Return the package's data root as an importlib resource traversable."""
53
+ return files("elliptic_curve_databases.data")
54
+
55
+
56
+ def cremona_mini_resource() -> Any:
57
+ """Return the mini Cremona SQLite database resource."""
58
+ return data_root().joinpath("cremona", "cremona_mini.db")
59
+
60
+
61
+ def _validate_rank(rank: int) -> int:
62
+ if isinstance(rank, bool) or not isinstance(rank, int):
63
+ raise TypeError("rank must be an integer")
64
+ if rank < 0:
65
+ raise ValueError("rank must be nonnegative")
66
+ return rank
67
+
68
+
69
+ def available_ranks() -> tuple[int, ...]:
70
+ """Return the ranks for which a packaged text database is available."""
71
+ ranks = []
72
+ for resource in data_root().joinpath("ellcurves").iterdir():
73
+ suffix = resource.name.removeprefix("rank")
74
+ if resource.name.startswith("rank") and suffix.isdigit():
75
+ ranks.append(int(suffix))
76
+ return tuple(sorted(ranks))
77
+
78
+
79
+ def rank_resource(rank: int) -> Any:
80
+ """Return the text-database resource for ``rank``.
81
+
82
+ Raise :class:`FileNotFoundError` if this distribution has no data for the
83
+ requested rank.
84
+ """
85
+ rank = _validate_rank(rank)
86
+ if rank not in available_ranks():
87
+ raise FileNotFoundError(f"no elliptic-curve database for rank {rank}")
88
+ return data_root().joinpath("ellcurves", f"rank{rank}")
89
+
90
+
91
+ @contextmanager
92
+ def cremona_mini_path() -> Iterator[Path]:
93
+ """Yield a filesystem path to the mini Cremona database resource."""
94
+ with as_file(cremona_mini_resource()) as path:
95
+ yield path
96
+
97
+
98
+ @contextmanager
99
+ def rank_path(rank: int) -> Iterator[Path]:
100
+ """Yield a filesystem path to the text database for ``rank``."""
101
+ with as_file(rank_resource(rank)) as path:
102
+ yield path
103
+
104
+
105
+ @contextmanager
106
+ def cremona_mini_connection() -> Iterator[sqlite3.Connection]:
107
+ """Yield a read-only SQLite connection to the mini Cremona database."""
108
+ with cremona_mini_path() as path:
109
+ connection = sqlite3.connect(f"{path.resolve().as_uri()}?mode=ro", uri=True)
110
+ try:
111
+ connection.execute("PRAGMA query_only = ON")
112
+ yield connection
113
+ finally:
114
+ connection.close()
115
+
116
+
117
+ def _parse_record(line: str, source: str, line_number: int) -> EllipticCurveRecord:
118
+ try:
119
+ conductor, isogeny_class, number, encoded_ainvs, rank, torsion = line.split()
120
+ parsed_ainvs = ast.literal_eval(encoded_ainvs)
121
+ if (
122
+ not isinstance(parsed_ainvs, list)
123
+ or len(parsed_ainvs) != 5
124
+ or any(isinstance(value, bool) or not isinstance(value, int) for value in parsed_ainvs)
125
+ ):
126
+ raise ValueError("a-invariants must be a list of five integers")
127
+ ainvs = tuple(parsed_ainvs)
128
+ return EllipticCurveRecord(
129
+ conductor=int(conductor),
130
+ isogeny_class=isogeny_class,
131
+ number=int(number),
132
+ ainvs=ainvs,
133
+ rank=int(rank),
134
+ torsion_order=int(torsion),
135
+ )
136
+ except (SyntaxError, TypeError, ValueError) as error:
137
+ raise ValueError(f"malformed record at {source}:{line_number}") from error
138
+
139
+
140
+ def iter_rank(rank: int) -> Iterator[EllipticCurveRecord]:
141
+ """Iterate over the elliptic-curve records stored for ``rank``."""
142
+ rank = _validate_rank(rank)
143
+ resource = rank_resource(rank)
144
+ with resource.open("r", encoding="ascii") as source:
145
+ for line_number, line in enumerate(source, 1):
146
+ record = _parse_record(line, resource.name, line_number)
147
+ if record.rank != rank:
148
+ raise ValueError(
149
+ f"record at {resource.name}:{line_number} has rank "
150
+ f"{record.rank}, expected {rank}"
151
+ )
152
+ yield record
@@ -0,0 +1 @@
1
+ """Packaged elliptic-curve data resources."""