-
Notifications
You must be signed in to change notification settings - Fork 0
/
classnumber.py
159 lines (127 loc) · 3.85 KB
/
classnumber.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
from classgroup import *
from gmpy2 import kronecker, floor, ceil, mpfr, sqrt
from math import pi, log
from sympy import factorint
from primesieve import primes
"""
Being lazy here and using imports for finding the
first n primes (for the Euler product) and for
facotring (for reducing the computed element order)
"""
DEBUG = False
def euler_product(Cl, b=10, P_bound=18):
fps = []
sqrt_D = sqrt(abs(Cl.D))
P = max(2**P_bound, int(sqrt(sqrt_D)))
sqrt_P = sqrt(mpfr(P))
Q = sqrt_D / pi
for p in primes(P):
ls = kronecker(Cl.D, p)
Qp = (1 - (ls / p))
Q /= Qp
# compute some random elements
# using small primes while we
# loop
if len(fps) < b and ls == 1:
if Cl.check_prime(p):
fps.append(Cl.lift_a(p, check=False))
if DEBUG: print(f"Euler product: {Q}")
B = floor(Q*(1 + 1/(2*sqrt_P)))
C = ceil(Q*(1 - 1/(2*sqrt_P)))
return mpz(Q), mpz(B), mpz(C), fps
def baby_steps_giant_step(g,e,B1,C1,Q1):
# Set bounds, I increase by 0.5% each
# side herefor good luck...
b,c = int(1.005*B1), int(0.995*C1)
t = ceil((b - c) / 2)
q = int(ceil(sqrt(t)))
if DEBUG: print(f"Baby step bounds: {int(t),b,c}")
# Baby Steps
Id = g.parent.identity()
x, xr = g**e, Id
baby_steps = {}
for r in range(q):
baby_steps[xr] = r
xr *= x
if xr == Id:
return r + 1
"""
I cannot get Cohen's Giant steps to find a result
Below is my own interpretation which needs to look
at both
Q1 ± (sq + r) ?= h
It looks like Cohen's misses some...
For anyone reading, alg 5.4.10 seems to ask for
```
y = xr**2
z = x**Q1
n = Q1
while n <= B1:
if z in baby_steps:
return n - baby_steps[z]
z_inv = z.inverse()
if z_inv in baby_steps:
return n + baby_steps[z_pos_inv]
z *= y
n += 2*q
raise ValueError(f"Group order is not within the bound {B1, C1}")
```
But this fails about 50% of the time.
"""
# Compute giant steps
# We look for s such that
# h = Q1 ± (2qs + r)
y = xr**2
z_pos = x**Q1
z_neg = z_pos.inverse()
for s in range(q//2 + 1):
z_pos_inv = z_pos.inverse()
z_neg_inv = z_neg.inverse()
if z_pos in baby_steps:
return Q1 + 2*s*q - baby_steps[z_pos]
elif z_neg in baby_steps:
return -Q1 + 2*s*q - baby_steps[z_neg]
elif z_pos_inv in baby_steps:
return Q1 + 2*s*q + baby_steps[z_pos_inv]
elif z_neg_inv in baby_steps:
return -Q1 + 2*s*q + baby_steps[z_neg_inv]
z_pos *= y
z_neg *= y
raise ValueError(f"Group order is not within the bound {B1, C1}")
def reduce_element_order(g, e, n):
Id = g.parent.identity()
ps = factorint(n)
for p in ps:
for e in range(ps[p]):
if g**(n // p) == Id:
n = n // p
return n
def class_number(Cl):
Q, B, C, fps = euler_product(Cl)
e = 1
B1, C1, Q1 = B, C, Q
for g in fps:
g = Cl.random_element(upper_bound=2**15)
n = baby_steps_giant_step(g, e, B1, C1, Q1)
n = reduce_element_order(g, e, n)
e = e*n
if e > (B - C):
h = e*floor(B / e)
return int(h)
B1 = mpz(floor(B1 / n))
C1 = mpz(ceil(C1 / n))
raise ValueError("Group order cannot be found, algorithm failed...")
return None
if __name__ == '__main__':
p = random_prime(10**15)
Cl = ImaginaryClassGroup(-p)
print(f"Computing: h({Cl.D})")
h = class_number(Cl)
print(f"h = {h}")
score = 0
goal = 1000
for _ in range(goal):
if Cl.random_element(upper_bound=2**8)**h == Cl.identity():
score += 1
if score != goal:
print(f"Failed... score = {score}")