foxmath 0.1.1__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.1/src/foxmath.egg-info → foxmath-0.2.0}/PKG-INFO +37 -10
- {foxmath-0.1.1 → foxmath-0.2.0}/README.md +36 -9
- {foxmath-0.1.1 → foxmath-0.2.0}/pyproject.toml +2 -1
- {foxmath-0.1.1 → foxmath-0.2.0}/src/foxmath/foxmath.py +62 -6
- {foxmath-0.1.1 → foxmath-0.2.0/src/foxmath.egg-info}/PKG-INFO +37 -10
- {foxmath-0.1.1 → foxmath-0.2.0}/src/foxmath.egg-info/entry_points.txt +1 -0
- {foxmath-0.1.1 → foxmath-0.2.0}/tests/test_cli.py +33 -0
- {foxmath-0.1.1 → foxmath-0.2.0}/tests/test_foxmath.py +38 -5
- {foxmath-0.1.1 → foxmath-0.2.0}/LICENSE +0 -0
- {foxmath-0.1.1 → foxmath-0.2.0}/setup.cfg +0 -0
- {foxmath-0.1.1 → foxmath-0.2.0}/src/foxmath/__init__.py +0 -0
- {foxmath-0.1.1 → foxmath-0.2.0}/src/foxmath.egg-info/SOURCES.txt +0 -0
- {foxmath-0.1.1 → foxmath-0.2.0}/src/foxmath.egg-info/dependency_links.txt +0 -0
- {foxmath-0.1.1 → 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"}
|
|
@@ -112,17 +159,26 @@ def main():
|
|
|
112
159
|
if cmd == "legendre" or args.command == "legendre":
|
|
113
160
|
ls = legendre_symbol(args.a, args.p)
|
|
114
161
|
result.update({"command": "legendre", "a": args.a, "p": args.p, "value": ls})
|
|
115
|
-
|
|
162
|
+
if not json_output:
|
|
163
|
+
print(f"Legendre ({args.a}/{args.p}) = {ls}")
|
|
116
164
|
|
|
117
165
|
elif cmd == "pi" or args.command == "pi":
|
|
118
|
-
approx =
|
|
166
|
+
approx = euler_hermann_pi_approx(args.terms)
|
|
119
167
|
result.update({"command": "pi", "terms": args.terms, "approx": str(approx)})
|
|
120
|
-
|
|
168
|
+
if not json_output:
|
|
169
|
+
print(f"π ≈ {approx} ({args.terms} terms)")
|
|
121
170
|
|
|
122
171
|
elif cmd == "ecadd" or args.command == "ecadd":
|
|
123
172
|
x3, y3 = ec_point_add(args.x1, args.y1, args.x2, args.y2, args.a, args.p)
|
|
124
173
|
result.update({"command": "ecadd", "result": (x3, y3)})
|
|
125
|
-
|
|
174
|
+
if not json_output:
|
|
175
|
+
print(f"Result point: ({x3}, {y3})")
|
|
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})")
|
|
126
182
|
|
|
127
183
|
except Exception as e:
|
|
128
184
|
print(f"Error: {e}", file=sys.stderr)
|
|
@@ -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
|
|
|
@@ -38,6 +38,15 @@ class TestJsonFlag(unittest.TestCase):
|
|
|
38
38
|
self.assertEqual(code, 0)
|
|
39
39
|
self.assertNotIn("{", out)
|
|
40
40
|
|
|
41
|
+
def test_json_output_is_pure_json_no_preamble(self):
|
|
42
|
+
# Regression check: --json used to still print the human-readable
|
|
43
|
+
# line first, breaking anything piping stdout into a JSON parser.
|
|
44
|
+
code, out, err = run("pi", "--terms", "20", "--json")
|
|
45
|
+
self.assertEqual(code, 0)
|
|
46
|
+
import json
|
|
47
|
+
parsed = json.loads(out) # raises if there's any leading text
|
|
48
|
+
self.assertEqual(parsed["command"], "pi")
|
|
49
|
+
|
|
41
50
|
|
|
42
51
|
class TestSymlinkInvocation(unittest.TestCase):
|
|
43
52
|
def setUp(self):
|
|
@@ -70,6 +79,12 @@ class TestSymlinkInvocation(unittest.TestCase):
|
|
|
70
79
|
self.assertEqual(code, 0)
|
|
71
80
|
self.assertIn("Result point:", out)
|
|
72
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
|
+
|
|
73
88
|
def test_symlink_with_json(self):
|
|
74
89
|
script = self._make_symlink_script("foxmath-legendre")
|
|
75
90
|
code, out, err = run("2", "7", "--json", script=script)
|
|
@@ -89,6 +104,24 @@ class TestNormalUsage(unittest.TestCase):
|
|
|
89
104
|
self.assertEqual(code, 1)
|
|
90
105
|
self.assertIn("point at infinity", err)
|
|
91
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
|
+
|
|
92
125
|
|
|
93
126
|
if __name__ == "__main__":
|
|
94
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
|