foxmath 0.1.2__tar.gz → 0.2.0__tar.gz
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.
- {foxmath-0.1.2/src/foxmath.egg-info → foxmath-0.2.0}/PKG-INFO +37 -10
- {foxmath-0.1.2 → foxmath-0.2.0}/README.md +36 -9
- {foxmath-0.1.2 → foxmath-0.2.0}/pyproject.toml +2 -1
- {foxmath-0.1.2 → foxmath-0.2.0}/src/foxmath/foxmath.py +56 -3
- {foxmath-0.1.2 → foxmath-0.2.0/src/foxmath.egg-info}/PKG-INFO +37 -10
- {foxmath-0.1.2 → foxmath-0.2.0}/src/foxmath.egg-info/entry_points.txt +1 -0
- {foxmath-0.1.2 → foxmath-0.2.0}/tests/test_cli.py +24 -0
- {foxmath-0.1.2 → foxmath-0.2.0}/tests/test_foxmath.py +38 -5
- {foxmath-0.1.2 → foxmath-0.2.0}/LICENSE +0 -0
- {foxmath-0.1.2 → foxmath-0.2.0}/setup.cfg +0 -0
- {foxmath-0.1.2 → foxmath-0.2.0}/src/foxmath/__init__.py +0 -0
- {foxmath-0.1.2 → foxmath-0.2.0}/src/foxmath.egg-info/SOURCES.txt +0 -0
- {foxmath-0.1.2 → foxmath-0.2.0}/src/foxmath.egg-info/dependency_links.txt +0 -0
- {foxmath-0.1.2 → foxmath-0.2.0}/src/foxmath.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: foxmath
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Minimalist CLI for math & cryptography exploration 🦊
|
|
5
5
|
Author-email: Abhrankan Chakrabarti <abhrankan@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -27,11 +27,12 @@ Built with curiosity — for students, tinkerers, and crypto/math enthusiasts.
|
|
|
27
27
|
|
|
28
28
|
## Features
|
|
29
29
|
- Legendre symbol
|
|
30
|
-
-
|
|
30
|
+
- Euler/Hermann Machin-like π approximation
|
|
31
31
|
- Elliptic curve point addition (over finite fields)
|
|
32
|
-
-
|
|
32
|
+
- Chinese Remainder Theorem solver (handles non-coprime moduli)
|
|
33
|
+
- Symlink-friendly (foxmath-legendre, foxmath-pi, foxmath-ecadd, foxmath-crt)
|
|
33
34
|
- JSON output mode
|
|
34
|
-
- Coming soon:
|
|
35
|
+
- Coming soon: continued fractions, challenge mode, more curves
|
|
35
36
|
|
|
36
37
|
## Installation
|
|
37
38
|
|
|
@@ -49,24 +50,50 @@ pip install -e .
|
|
|
49
50
|
## Usage Examples
|
|
50
51
|
|
|
51
52
|
```bash
|
|
52
|
-
# Basic
|
|
53
53
|
foxmath pi --terms 200
|
|
54
|
+
```
|
|
55
|
+
```
|
|
56
|
+
π ≈ 3.1415926535897932384626433832795028841971693993752 (200 terms)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
```bash
|
|
54
60
|
foxmath legendre 5 17
|
|
61
|
+
```
|
|
62
|
+
```
|
|
63
|
+
Legendre (5/17) = -1
|
|
64
|
+
```
|
|
55
65
|
|
|
66
|
+
```bash
|
|
56
67
|
# Symlinks (optional)
|
|
57
68
|
foxmath-ecadd --x1 1 --y1 2 --x2 3 --y2 4 --p 17
|
|
69
|
+
```
|
|
70
|
+
```
|
|
71
|
+
Result point: (14, 2)
|
|
72
|
+
```
|
|
58
73
|
|
|
59
|
-
|
|
60
|
-
|
|
74
|
+
```bash
|
|
75
|
+
# Chinese Remainder Theorem
|
|
76
|
+
foxmath crt --r 2 3 2 --m 3 5 7
|
|
77
|
+
```
|
|
78
|
+
```
|
|
79
|
+
x ≡ 23 (mod 105)
|
|
61
80
|
```
|
|
62
81
|
|
|
63
|
-
|
|
82
|
+
```bash
|
|
83
|
+
# JSON output
|
|
84
|
+
foxmath pi --terms 100 --json
|
|
64
85
|
```
|
|
65
|
-
|
|
86
|
+
```json
|
|
87
|
+
{
|
|
88
|
+
"tool": "foxmath",
|
|
89
|
+
"command": "pi",
|
|
90
|
+
"terms": 100,
|
|
91
|
+
"approx": "3.1415926535897932384626433832795028841971693993752"
|
|
92
|
+
}
|
|
66
93
|
```
|
|
67
94
|
|
|
68
95
|
## Why FoxMath?
|
|
69
|
-
Because math and cryptography are more fun when you can play with them instantly in the terminal — with the same reliability as FoxPipe and fox-vault.
|
|
96
|
+
Because math and cryptography are more fun when you can play with them instantly in the terminal — with the same reliability as [FoxPipe](https://github.com/foxhackerzdevs/FoxPipe) and [fox-vault](https://github.com/foxhackerzdevs/fox-vault).
|
|
70
97
|
|
|
71
98
|
**Simple. Practical. Reliable.**
|
|
72
99
|
|
|
@@ -8,11 +8,12 @@ Built with curiosity — for students, tinkerers, and crypto/math enthusiasts.
|
|
|
8
8
|
|
|
9
9
|
## Features
|
|
10
10
|
- Legendre symbol
|
|
11
|
-
-
|
|
11
|
+
- Euler/Hermann Machin-like π approximation
|
|
12
12
|
- Elliptic curve point addition (over finite fields)
|
|
13
|
-
-
|
|
13
|
+
- Chinese Remainder Theorem solver (handles non-coprime moduli)
|
|
14
|
+
- Symlink-friendly (foxmath-legendre, foxmath-pi, foxmath-ecadd, foxmath-crt)
|
|
14
15
|
- JSON output mode
|
|
15
|
-
- Coming soon:
|
|
16
|
+
- Coming soon: continued fractions, challenge mode, more curves
|
|
16
17
|
|
|
17
18
|
## Installation
|
|
18
19
|
|
|
@@ -30,24 +31,50 @@ pip install -e .
|
|
|
30
31
|
## Usage Examples
|
|
31
32
|
|
|
32
33
|
```bash
|
|
33
|
-
# Basic
|
|
34
34
|
foxmath pi --terms 200
|
|
35
|
+
```
|
|
36
|
+
```
|
|
37
|
+
π ≈ 3.1415926535897932384626433832795028841971693993752 (200 terms)
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
```bash
|
|
35
41
|
foxmath legendre 5 17
|
|
42
|
+
```
|
|
43
|
+
```
|
|
44
|
+
Legendre (5/17) = -1
|
|
45
|
+
```
|
|
36
46
|
|
|
47
|
+
```bash
|
|
37
48
|
# Symlinks (optional)
|
|
38
49
|
foxmath-ecadd --x1 1 --y1 2 --x2 3 --y2 4 --p 17
|
|
50
|
+
```
|
|
51
|
+
```
|
|
52
|
+
Result point: (14, 2)
|
|
53
|
+
```
|
|
39
54
|
|
|
40
|
-
|
|
41
|
-
|
|
55
|
+
```bash
|
|
56
|
+
# Chinese Remainder Theorem
|
|
57
|
+
foxmath crt --r 2 3 2 --m 3 5 7
|
|
58
|
+
```
|
|
59
|
+
```
|
|
60
|
+
x ≡ 23 (mod 105)
|
|
42
61
|
```
|
|
43
62
|
|
|
44
|
-
|
|
63
|
+
```bash
|
|
64
|
+
# JSON output
|
|
65
|
+
foxmath pi --terms 100 --json
|
|
45
66
|
```
|
|
46
|
-
|
|
67
|
+
```json
|
|
68
|
+
{
|
|
69
|
+
"tool": "foxmath",
|
|
70
|
+
"command": "pi",
|
|
71
|
+
"terms": 100,
|
|
72
|
+
"approx": "3.1415926535897932384626433832795028841971693993752"
|
|
73
|
+
}
|
|
47
74
|
```
|
|
48
75
|
|
|
49
76
|
## Why FoxMath?
|
|
50
|
-
Because math and cryptography are more fun when you can play with them instantly in the terminal — with the same reliability as FoxPipe and fox-vault.
|
|
77
|
+
Because math and cryptography are more fun when you can play with them instantly in the terminal — with the same reliability as [FoxPipe](https://github.com/foxhackerzdevs/FoxPipe) and [fox-vault](https://github.com/foxhackerzdevs/fox-vault).
|
|
51
78
|
|
|
52
79
|
**Simple. Practical. Reliable.**
|
|
53
80
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "foxmath"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.2.0"
|
|
8
8
|
description = "Minimalist CLI for math & cryptography exploration 🦊"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = {text = "MIT"}
|
|
@@ -31,3 +31,4 @@ foxmath = "foxmath.foxmath:main"
|
|
|
31
31
|
foxmath-legendre = "foxmath.foxmath:main"
|
|
32
32
|
foxmath-pi = "foxmath.foxmath:main"
|
|
33
33
|
foxmath-ecadd = "foxmath.foxmath:main"
|
|
34
|
+
foxmath-crt = "foxmath.foxmath:main"
|
|
@@ -27,7 +27,7 @@ def _arctan_series(inv_n: Decimal, terms: int) -> Decimal:
|
|
|
27
27
|
total += sign * inv_n**(2 * k + 1) / (2 * k + 1)
|
|
28
28
|
return total
|
|
29
29
|
|
|
30
|
-
def
|
|
30
|
+
def euler_hermann_pi_approx(terms: int = 100) -> Decimal:
|
|
31
31
|
"""
|
|
32
32
|
Fast-converging π approximation via the Euler/Hermann Machin-like
|
|
33
33
|
identity: pi/4 = arctan(1/2) + arctan(1/3).
|
|
@@ -54,6 +54,48 @@ def ec_point_add(x1, y1, x2, y2, a, p):
|
|
|
54
54
|
y3 = (lam * (x1 - x3) - y1) % p
|
|
55
55
|
return x3, y3
|
|
56
56
|
|
|
57
|
+
def _extended_gcd(a: int, b: int):
|
|
58
|
+
"""Returns (g, x, y) such that a*x + b*y == g == gcd(a, b)."""
|
|
59
|
+
old_r, r = a, b
|
|
60
|
+
old_s, s = 1, 0
|
|
61
|
+
old_t, t = 0, 1
|
|
62
|
+
while r != 0:
|
|
63
|
+
q = old_r // r
|
|
64
|
+
old_r, r = r, old_r - q * r
|
|
65
|
+
old_s, s = s, old_s - q * s
|
|
66
|
+
old_t, t = t, old_t - q * t
|
|
67
|
+
return old_r, old_s, old_t
|
|
68
|
+
|
|
69
|
+
def _combine_crt(r1: int, m1: int, r2: int, m2: int):
|
|
70
|
+
"""Combine x≡r1 (mod m1) and x≡r2 (mod m2) into a single x≡r (mod lcm(m1,m2))."""
|
|
71
|
+
g, p, _ = _extended_gcd(m1, m2)
|
|
72
|
+
if (r2 - r1) % g != 0:
|
|
73
|
+
raise ValueError(
|
|
74
|
+
f"No solution exists: moduli {m1} and {m2} are inconsistent "
|
|
75
|
+
f"for remainders {r1} and {r2}."
|
|
76
|
+
)
|
|
77
|
+
lcm = m1 // g * m2
|
|
78
|
+
x = (r1 + (r2 - r1) // g * p % (m2 // g) * m1) % lcm
|
|
79
|
+
return x, lcm
|
|
80
|
+
|
|
81
|
+
def crt_solve(remainders, moduli):
|
|
82
|
+
"""
|
|
83
|
+
Chinese Remainder Theorem, generalized to non-pairwise-coprime moduli.
|
|
84
|
+
Returns (x, m) such that x is the unique solution mod m = lcm(moduli),
|
|
85
|
+
or raises ValueError if the system is inconsistent.
|
|
86
|
+
"""
|
|
87
|
+
if len(remainders) != len(moduli):
|
|
88
|
+
raise ValueError("remainders and moduli must have the same length")
|
|
89
|
+
if not remainders:
|
|
90
|
+
raise ValueError("need at least one congruence")
|
|
91
|
+
if any(m <= 0 for m in moduli):
|
|
92
|
+
raise ValueError("all moduli must be positive")
|
|
93
|
+
|
|
94
|
+
r, m = remainders[0] % moduli[0], moduli[0]
|
|
95
|
+
for ri, mi in zip(remainders[1:], moduli[1:]):
|
|
96
|
+
r, m = _combine_crt(r, m, ri % mi, mi)
|
|
97
|
+
return r, m
|
|
98
|
+
|
|
57
99
|
# ------------------- CLI -------------------
|
|
58
100
|
|
|
59
101
|
def get_command():
|
|
@@ -92,7 +134,7 @@ def main():
|
|
|
92
134
|
leg.add_argument("p", type=int, help="Odd prime p")
|
|
93
135
|
|
|
94
136
|
# Pi
|
|
95
|
-
pi_cmd = subparsers.add_parser("pi", help="
|
|
137
|
+
pi_cmd = subparsers.add_parser("pi", help="Euler/Hermann Machin-like π approximation")
|
|
96
138
|
pi_cmd.add_argument("--terms", type=int, default=100)
|
|
97
139
|
|
|
98
140
|
# Elliptic curve add
|
|
@@ -104,6 +146,11 @@ def main():
|
|
|
104
146
|
ec.add_argument("--a", type=int, default=-3, help="Curve parameter a")
|
|
105
147
|
ec.add_argument("--p", type=int, required=True, help="Prime modulus")
|
|
106
148
|
|
|
149
|
+
# Chinese Remainder Theorem
|
|
150
|
+
crt = subparsers.add_parser("crt", help="Solve a system of congruences x = r (mod m)")
|
|
151
|
+
crt.add_argument("--r", type=int, nargs="+", required=True, help="Remainders, e.g. --r 2 3 2")
|
|
152
|
+
crt.add_argument("--m", type=int, nargs="+", required=True, help="Moduli, e.g. --m 3 5 7")
|
|
153
|
+
|
|
107
154
|
args = parser.parse_args(argv)
|
|
108
155
|
|
|
109
156
|
result = {"tool": "foxmath"}
|
|
@@ -116,7 +163,7 @@ def main():
|
|
|
116
163
|
print(f"Legendre ({args.a}/{args.p}) = {ls}")
|
|
117
164
|
|
|
118
165
|
elif cmd == "pi" or args.command == "pi":
|
|
119
|
-
approx =
|
|
166
|
+
approx = euler_hermann_pi_approx(args.terms)
|
|
120
167
|
result.update({"command": "pi", "terms": args.terms, "approx": str(approx)})
|
|
121
168
|
if not json_output:
|
|
122
169
|
print(f"π ≈ {approx} ({args.terms} terms)")
|
|
@@ -127,6 +174,12 @@ def main():
|
|
|
127
174
|
if not json_output:
|
|
128
175
|
print(f"Result point: ({x3}, {y3})")
|
|
129
176
|
|
|
177
|
+
elif cmd == "crt" or args.command == "crt":
|
|
178
|
+
x, m = crt_solve(args.r, args.m)
|
|
179
|
+
result.update({"command": "crt", "r": args.r, "m": args.m, "x": x, "mod": m})
|
|
180
|
+
if not json_output:
|
|
181
|
+
print(f"x ≡ {x} (mod {m})")
|
|
182
|
+
|
|
130
183
|
except Exception as e:
|
|
131
184
|
print(f"Error: {e}", file=sys.stderr)
|
|
132
185
|
sys.exit(1)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: foxmath
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Minimalist CLI for math & cryptography exploration 🦊
|
|
5
5
|
Author-email: Abhrankan Chakrabarti <abhrankan@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -27,11 +27,12 @@ Built with curiosity — for students, tinkerers, and crypto/math enthusiasts.
|
|
|
27
27
|
|
|
28
28
|
## Features
|
|
29
29
|
- Legendre symbol
|
|
30
|
-
-
|
|
30
|
+
- Euler/Hermann Machin-like π approximation
|
|
31
31
|
- Elliptic curve point addition (over finite fields)
|
|
32
|
-
-
|
|
32
|
+
- Chinese Remainder Theorem solver (handles non-coprime moduli)
|
|
33
|
+
- Symlink-friendly (foxmath-legendre, foxmath-pi, foxmath-ecadd, foxmath-crt)
|
|
33
34
|
- JSON output mode
|
|
34
|
-
- Coming soon:
|
|
35
|
+
- Coming soon: continued fractions, challenge mode, more curves
|
|
35
36
|
|
|
36
37
|
## Installation
|
|
37
38
|
|
|
@@ -49,24 +50,50 @@ pip install -e .
|
|
|
49
50
|
## Usage Examples
|
|
50
51
|
|
|
51
52
|
```bash
|
|
52
|
-
# Basic
|
|
53
53
|
foxmath pi --terms 200
|
|
54
|
+
```
|
|
55
|
+
```
|
|
56
|
+
π ≈ 3.1415926535897932384626433832795028841971693993752 (200 terms)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
```bash
|
|
54
60
|
foxmath legendre 5 17
|
|
61
|
+
```
|
|
62
|
+
```
|
|
63
|
+
Legendre (5/17) = -1
|
|
64
|
+
```
|
|
55
65
|
|
|
66
|
+
```bash
|
|
56
67
|
# Symlinks (optional)
|
|
57
68
|
foxmath-ecadd --x1 1 --y1 2 --x2 3 --y2 4 --p 17
|
|
69
|
+
```
|
|
70
|
+
```
|
|
71
|
+
Result point: (14, 2)
|
|
72
|
+
```
|
|
58
73
|
|
|
59
|
-
|
|
60
|
-
|
|
74
|
+
```bash
|
|
75
|
+
# Chinese Remainder Theorem
|
|
76
|
+
foxmath crt --r 2 3 2 --m 3 5 7
|
|
77
|
+
```
|
|
78
|
+
```
|
|
79
|
+
x ≡ 23 (mod 105)
|
|
61
80
|
```
|
|
62
81
|
|
|
63
|
-
|
|
82
|
+
```bash
|
|
83
|
+
# JSON output
|
|
84
|
+
foxmath pi --terms 100 --json
|
|
64
85
|
```
|
|
65
|
-
|
|
86
|
+
```json
|
|
87
|
+
{
|
|
88
|
+
"tool": "foxmath",
|
|
89
|
+
"command": "pi",
|
|
90
|
+
"terms": 100,
|
|
91
|
+
"approx": "3.1415926535897932384626433832795028841971693993752"
|
|
92
|
+
}
|
|
66
93
|
```
|
|
67
94
|
|
|
68
95
|
## Why FoxMath?
|
|
69
|
-
Because math and cryptography are more fun when you can play with them instantly in the terminal — with the same reliability as FoxPipe and fox-vault.
|
|
96
|
+
Because math and cryptography are more fun when you can play with them instantly in the terminal — with the same reliability as [FoxPipe](https://github.com/foxhackerzdevs/FoxPipe) and [fox-vault](https://github.com/foxhackerzdevs/fox-vault).
|
|
70
97
|
|
|
71
98
|
**Simple. Practical. Reliable.**
|
|
72
99
|
|
|
@@ -79,6 +79,12 @@ class TestSymlinkInvocation(unittest.TestCase):
|
|
|
79
79
|
self.assertEqual(code, 0)
|
|
80
80
|
self.assertIn("Result point:", out)
|
|
81
81
|
|
|
82
|
+
def test_crt_symlink(self):
|
|
83
|
+
script = self._make_symlink_script("foxmath-crt")
|
|
84
|
+
code, out, err = run("--r", "2", "3", "2", "--m", "3", "5", "7", script=script)
|
|
85
|
+
self.assertEqual(code, 0)
|
|
86
|
+
self.assertIn("x ≡ 23 (mod 105)", out)
|
|
87
|
+
|
|
82
88
|
def test_symlink_with_json(self):
|
|
83
89
|
script = self._make_symlink_script("foxmath-legendre")
|
|
84
90
|
code, out, err = run("2", "7", "--json", script=script)
|
|
@@ -98,6 +104,24 @@ class TestNormalUsage(unittest.TestCase):
|
|
|
98
104
|
self.assertEqual(code, 1)
|
|
99
105
|
self.assertIn("point at infinity", err)
|
|
100
106
|
|
|
107
|
+
def test_crt(self):
|
|
108
|
+
code, out, err = run("crt", "--r", "2", "3", "2", "--m", "3", "5", "7")
|
|
109
|
+
self.assertEqual(code, 0)
|
|
110
|
+
self.assertIn("x ≡ 23 (mod 105)", out)
|
|
111
|
+
|
|
112
|
+
def test_crt_json_is_pure_json(self):
|
|
113
|
+
code, out, err = run("crt", "--r", "2", "3", "2", "--m", "3", "5", "7", "--json")
|
|
114
|
+
self.assertEqual(code, 0)
|
|
115
|
+
import json
|
|
116
|
+
parsed = json.loads(out)
|
|
117
|
+
self.assertEqual(parsed["x"], 23)
|
|
118
|
+
self.assertEqual(parsed["mod"], 105)
|
|
119
|
+
|
|
120
|
+
def test_crt_inconsistent_system_errors(self):
|
|
121
|
+
code, out, err = run("crt", "--r", "1", "2", "--m", "4", "6")
|
|
122
|
+
self.assertEqual(code, 1)
|
|
123
|
+
self.assertIn("inconsistent", err)
|
|
124
|
+
|
|
101
125
|
|
|
102
126
|
if __name__ == "__main__":
|
|
103
127
|
unittest.main()
|
|
@@ -12,7 +12,7 @@ from decimal import Decimal
|
|
|
12
12
|
|
|
13
13
|
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "src")))
|
|
14
14
|
|
|
15
|
-
from foxmath.foxmath import legendre_symbol,
|
|
15
|
+
from foxmath.foxmath import legendre_symbol, euler_hermann_pi_approx, ec_point_add, crt_solve
|
|
16
16
|
|
|
17
17
|
# Reference pi digits (mpmath, 250 dps of working precision), "3" + fractional digits.
|
|
18
18
|
PI_REFERENCE = Decimal(
|
|
@@ -47,16 +47,16 @@ class TestLegendreSymbol(unittest.TestCase):
|
|
|
47
47
|
legendre_symbol(3, 1)
|
|
48
48
|
|
|
49
49
|
|
|
50
|
-
class
|
|
50
|
+
class TestEulerHermannPiApprox(unittest.TestCase):
|
|
51
51
|
def test_converges_to_pi(self):
|
|
52
52
|
# Regression check: this used to converge to 16*arctan(1/2) =~ 7.418,
|
|
53
53
|
# not pi, due to a double-counted scaling factor.
|
|
54
|
-
approx =
|
|
54
|
+
approx = euler_hermann_pi_approx(100)
|
|
55
55
|
self.assertLess(abs(approx - PI_REFERENCE), Decimal("1e-40"))
|
|
56
56
|
|
|
57
57
|
def test_more_terms_is_more_accurate(self):
|
|
58
|
-
err_10 = abs(
|
|
59
|
-
err_50 = abs(
|
|
58
|
+
err_10 = abs(euler_hermann_pi_approx(10) - PI_REFERENCE)
|
|
59
|
+
err_50 = abs(euler_hermann_pi_approx(50) - PI_REFERENCE)
|
|
60
60
|
self.assertLess(err_50, err_10)
|
|
61
61
|
|
|
62
62
|
|
|
@@ -82,5 +82,38 @@ class TestECPointAdd(unittest.TestCase):
|
|
|
82
82
|
ec_point_add(3, 6, 3, self.P - 6, self.A, self.P)
|
|
83
83
|
|
|
84
84
|
|
|
85
|
+
class TestCRT(unittest.TestCase):
|
|
86
|
+
def test_classic_coprime_case(self):
|
|
87
|
+
x, m = crt_solve([2, 3, 2], [3, 5, 7])
|
|
88
|
+
self.assertEqual((x, m), (23, 105))
|
|
89
|
+
self.assertEqual(x % 3, 2)
|
|
90
|
+
self.assertEqual(x % 5, 3)
|
|
91
|
+
self.assertEqual(x % 7, 2)
|
|
92
|
+
|
|
93
|
+
def test_non_coprime_consistent(self):
|
|
94
|
+
x, m = crt_solve([2, 2], [4, 6])
|
|
95
|
+
self.assertEqual((x, m), (2, 12))
|
|
96
|
+
|
|
97
|
+
def test_non_coprime_inconsistent_raises(self):
|
|
98
|
+
with self.assertRaises(ValueError):
|
|
99
|
+
crt_solve([1, 2], [4, 6])
|
|
100
|
+
|
|
101
|
+
def test_single_congruence(self):
|
|
102
|
+
self.assertEqual(crt_solve([5], [11]), (5, 11))
|
|
103
|
+
|
|
104
|
+
def test_mismatched_lengths_raises(self):
|
|
105
|
+
with self.assertRaises(ValueError):
|
|
106
|
+
crt_solve([2, 3], [3, 5, 7])
|
|
107
|
+
|
|
108
|
+
def test_empty_raises(self):
|
|
109
|
+
with self.assertRaises(ValueError):
|
|
110
|
+
crt_solve([], [])
|
|
111
|
+
|
|
112
|
+
def test_negative_remainder_normalized(self):
|
|
113
|
+
x, m = crt_solve([-1, 3], [5, 7])
|
|
114
|
+
self.assertEqual(x % 5, 4) # -1 mod 5 == 4
|
|
115
|
+
self.assertEqual(x % 7, 3)
|
|
116
|
+
|
|
117
|
+
|
|
85
118
|
if __name__ == "__main__":
|
|
86
119
|
unittest.main()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|