1use crate::ec::GoodCurve;
2use crate::ec::Point;
3use crate::utils::is_odd;
4use ark_ff::Field;
5use rand::Rng;
6
7fn double_point_x<F: Field>(px: F, a: F, bb: F) -> Option<F> {
12 assert!(is_odd::<F>());
13 let pxpx = px.square();
14 let pypy = px * (pxpx + a * px + bb);
15 if pypy.is_zero() {
16 return None;
17 }
18 Some((pxpx - bb).square() / pypy.double().double())
19}
20
21fn half_point_x<F: Field>(qx: F, a: F, bb: F) -> Option<F> {
26 assert!(is_odd::<F>());
27 let roots = fi_roots(1, qx, a, bb).or_else(|| fi_roots(2, qx, a, bb))?;
28 roots
29 .into_iter()
30 .find(|&x| (x * (x.square() + a * x + bb)).sqrt().is_some())
31}
32
33fn roots<F: Field>(a: F, b: F, c: F) -> Option<[F; 2]> {
36 assert!(is_odd::<F>());
37 let discriminant = b.square() - (a * c).double().double();
38 let discriminant_sqrt = discriminant.sqrt()?;
39 let two_inv = a.double().inverse()?;
40 Some([
41 (-b + discriminant_sqrt) * two_inv,
42 (-b - discriminant_sqrt) * two_inv,
43 ])
44}
45
46fn fi_roots<F: Field>(i: usize, qx: F, a: F, bb: F) -> Option<[F; 2]> {
50 assert!(i == 1 || i == 2);
51 let delta = qx.square() + a * qx + bb;
52 let delta_sqrt = delta.sqrt()?;
53 let one = F::one();
54 let x_coeff = -(qx.double() + (-one).pow([i as u64]) * delta_sqrt.double());
55 roots(one, x_coeff, bb)
56}
57
58fn _find_two_sylow_subgroup<F: Field>(a: F, bb: F) -> (u32, u32, Option<F>, Option<F>) {
66 assert!(is_odd::<F>());
67 let discriminant = a.square() - bb.double().double();
68 assert!(!discriminant.is_zero());
69 let one = F::one();
70 let zero = F::zero();
71
72 if discriminant.sqrt().is_some() {
73 let Some([r1, r2]) = roots(F::one(), a, bb) else { unreachable!() };
78 let r0 = zero;
79
80 let p4_cond1 = bb.sqrt().and_then(|b| (a - b.double()).sqrt()).is_some();
82 let p4_cond2 = r1.sqrt().and_then(|_| (r1.double() + a).sqrt()).is_some();
83 let p4_cond3 = r2.sqrt().and_then(|_| (r2.double() + a).sqrt()).is_some();
84
85 if !p4_cond1 && !p4_cond2 && !p4_cond3 {
86 return (1, 1, Some(zero), Some(r1));
88 }
89
90 let p4_x0 = roots(one, r0, -bb).map(|[x, _]| x).unwrap();
93 let p4_x1 = roots(one, -r1.double(), bb).map(|[x, _]| x).unwrap();
94 let p4_x2 = roots(one, -r2.double(), bb).map(|[x, _]| x).unwrap();
95
96 let has_half_point = |x: F| -> bool {
97 if x.sqrt().is_none() {
99 return false;
100 }
101 let delta = x.square() + a * x + bb;
102 let delta_sqrt = match delta.sqrt() {
103 Some(sqrt) => sqrt,
104 None => return false,
105 };
106 (x.double() + a + delta_sqrt.double()).sqrt().is_some()
107 };
108
109 let mut k = 2;
110 let mut h = 1;
111 let mut acc0 = p4_x0;
112 let mut acc1 = p4_x1;
113 let mut acc2 = p4_x2;
114 loop {
115 let acc0_has_half_point = has_half_point(acc0);
116 let acc1_has_half_point = has_half_point(acc1);
117 let acc2_has_half_point = has_half_point(acc2);
118
119 if !acc0_has_half_point && !acc1_has_half_point && !acc2_has_half_point {
120 return (h, h, Some(acc0), Some(acc1));
122 }
123
124 if acc0_has_half_point && acc1_has_half_point && acc2_has_half_point {
125 acc0 = half_point_x(acc0, a, bb).unwrap();
127 acc1 = half_point_x(acc1, a, bb).unwrap();
128 acc2 = half_point_x(acc2, a, bb).unwrap();
129 k += 1;
130 h = k;
131 continue;
132 }
133
134 if h == 1 {
135 let _x2 = match (
138 acc0_has_half_point,
139 acc1_has_half_point,
140 acc2_has_half_point,
141 ) {
142 (false, _, _) => r0,
143 (_, false, _) => r1,
144 (_, _, false) => r2,
145 _ => unreachable!(),
146 };
147
148 todo!()
149 }
150
151 todo!()
152 }
153
154 } else if let Some(b) = bb.sqrt() {
156 let p4_x = match ((a + b.double()).sqrt(), (a - b.double()).sqrt()) {
158 (Some(_), _) => b,
159 (_, Some(_)) => -b,
160 _ => unreachable!(),
161 };
162
163 if double_point_x(p4_x, a, bb).is_none() {
164 return (1, 0, Some(zero), None);
165 }
166
167 let mut k = 2;
168 let mut acc = p4_x;
169 while let Some(x) = half_point_x(acc, a, bb) {
170 k += 1;
171 acc = x;
172 }
173
174 (k, 0, Some(acc), None)
175 } else {
176 (0, 0, None, None)
177 }
178}
179
180fn cyclic_two_sylow_subgroup<F: Field>(a: F, bb: F) -> (u32, Option<F>) {
191 assert!(is_odd::<F>());
192 let discriminant = a.square() - bb.double().double();
193 assert!(!discriminant.is_zero());
194
195 if let (Some(b), None) = (bb.sqrt(), discriminant.sqrt()) {
196 let p4_x = match ((a + b.double()).sqrt(), (a - b.double()).sqrt()) {
198 (Some(_), _) => b,
199 (_, Some(_)) => -b,
200 _ => unreachable!(),
201 };
202
203 if double_point_x(p4_x, a, bb).is_none() {
204 return (1, Some(F::zero()));
205 }
206
207 let mut k = 2;
208 let mut acc = p4_x;
209 while let Some(x) = half_point_x(acc, a, bb) {
210 k += 1;
211 acc = x;
212 }
213
214 (k, Some(acc))
215 } else {
216 (0, None)
217 }
218}
219
220pub fn find_curve<F: Field>(mut rng: impl Rng, k: u32) -> (u32, Point<GoodCurve<F>>) {
225 let k = k.max(2);
226 if is_odd::<F>() {
227 loop {
228 let a = F::rand(&mut rng);
230 let bb = F::rand(&mut rng);
231 let (n, x) = cyclic_two_sylow_subgroup(a, bb);
234 if n >= k {
235 let x = x.unwrap();
236 let yy = x * (x.square() + a * x + bb);
237 let y = yy.sqrt().unwrap();
238 let good_curve = GoodCurve::new_odd(a, bb);
239 let p = Point::new(x, y, good_curve);
240 return (n, p);
241 }
242 }
243 } else {
244 todo!()
245 }
246}