1use core::fmt;
13use subtle::{Choice, ConditionallySelectable, ConstantTimeEq};
14
15use crate::curve_jacobi_intersection::JacobiIntersectionCurve;
16use crate::point_ops::{PointAdd, PointOps};
17use fp::field_ops::FieldOps;
18
19#[derive(Debug, Clone, Copy)]
21pub struct JacobiIntersectionPoint<F: FieldOps> {
22 pub s: F,
24 pub c: F,
26 pub d: F,
28}
29
30
31impl<F> fmt::Display for JacobiIntersectionPoint<F>
32where
33 F: FieldOps + fmt::Display,
34{
35 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
36 if self.is_identity() {
37 if f.alternate() {
38 write!(f, "JacobiIntersectionPoint {{ O = (0,1,1) }}")
39 } else {
40 write!(f, "O")
41 }
42 } else if f.alternate() {
43 write!(
44 f,
45 "JacobiIntersectionPoint {{ s = {}, c = {}, d = {} }}",
46 self.s, self.c, self.d
47 )
48 } else {
49 write!(f, "({}, {}, {})", self.s, self.c, self.d)
50 }
51 }
52}
53
54impl<F: FieldOps> PartialEq for JacobiIntersectionPoint<F> {
55 fn eq(&self, other: &Self) -> bool {
56 self.s == other.s && self.c == other.c && self.d == other.d
57 }
58}
59
60impl<F: FieldOps> Eq for JacobiIntersectionPoint<F> {}
61
62impl<F: FieldOps> JacobiIntersectionPoint<F> {
63 pub fn new(s: F, c: F, d: F) -> Self {
81 Self { s, c, d }
82 }
83
84 pub fn identity() -> Self {
86 Self {
87 s: F::zero(),
88 c: F::one(),
89 d: F::one(),
90 }
91 }
92
93 pub fn is_identity(&self) -> bool {
103 self.s == F::zero() && self.c == F::one() && self.d == F::one()
104 }
105}
106
107impl<F> ConditionallySelectable for JacobiIntersectionPoint<F>
108where
109 F: FieldOps + Copy,
110{
111 fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
112 Self {
113 s: F::conditional_select(&a.s, &b.s, choice),
114 c: F::conditional_select(&a.c, &b.c, choice),
115 d: F::conditional_select(&a.d, &b.d, choice),
116 }
117 }
118
119 fn conditional_assign(&mut self, other: &Self, choice: Choice) {
120 self.s.conditional_assign(&other.s, choice);
121 self.c.conditional_assign(&other.c, choice);
122 self.d.conditional_assign(&other.d, choice);
123 }
124
125 fn conditional_swap(a: &mut Self, b: &mut Self, choice: Choice) {
126 F::conditional_swap(&mut a.s, &mut b.s, choice);
127 F::conditional_swap(&mut a.c, &mut b.c, choice);
128 F::conditional_swap(&mut a.d, &mut b.d, choice);
129 }
130}
131
132impl<F> ConstantTimeEq for JacobiIntersectionPoint<F>
133where
134 F: FieldOps + Copy + ConstantTimeEq,
135{
136 fn ct_eq(&self, other: &Self) -> Choice {
137 self.s.ct_eq(&other.s) & self.c.ct_eq(&other.c) & self.d.ct_eq(&other.d)
138 }
139
140 fn ct_ne(&self, other: &Self) -> Choice {
141 !self.ct_eq(other)
142 }
143}
144
145impl<F: FieldOps> JacobiIntersectionPoint<F> {
146 pub fn negate(&self, _curve: &JacobiIntersectionCurve<F>) -> Self {
148 Self::new(-self.s, self.c, self.d)
149 }
150
151 pub fn add(&self, other: &Self, curve: &JacobiIntersectionCurve<F>) -> Self {
160 let d1s2 = self.d * other.s;
161 let denom = <F as FieldOps>::square(&other.c) + <F as FieldOps>::square(&d1s2);
162 let denom_inv = denom
163 .invert()
164 .into_option()
165 .expect("Jacobi-intersection addition denominator vanished");
166
167 let s3 = (other.c * self.s * other.d + self.d * other.s * self.c) * denom_inv;
168 let c3 = (other.c * self.c - self.d * other.s * self.s * other.d) * denom_inv;
169 let d3 = (self.d * other.d - curve.a * self.s * self.c * other.s * other.c) * denom_inv;
170
171 Self::new(s3, c3, d3)
172 }
173
174 pub fn double(&self, curve: &JacobiIntersectionCurve<F>) -> Self {
183 let ds = self.d * self.s;
184 let denom = <F as FieldOps>::square(&self.c) + <F as FieldOps>::square(&ds);
185 let denom_inv = denom
186 .invert()
187 .into_option()
188 .expect("Jacobi-intersection doubling denominator vanished");
189
190 let two = <F as FieldOps>::double(&F::one());
191 let s_sq = <F as FieldOps>::square(&self.s);
192 let c_sq = <F as FieldOps>::square(&self.c);
193 let d_sq = <F as FieldOps>::square(&self.d);
194
195 let s3 = (two * self.c * self.s * self.d) * denom_inv;
196 let c3 = (c_sq - d_sq * s_sq) * denom_inv;
197 let d3 = (d_sq - curve.a * s_sq * c_sq) * denom_inv;
198
199 Self::new(s3, c3, d3)
200 }
201
202 pub fn scalar_mul(&self, k: &[u64], curve: &JacobiIntersectionCurve<F>) -> Self {
216 let mut result = Self::identity();
217
218 for &limb in k.iter().rev() {
219 for bit in (0..64).rev() {
220 let doubled = result.double(curve);
221 let added = doubled.add(self, curve);
222 let choice = Choice::from(((limb >> bit) & 1) as u8);
223 result = Self::conditional_select(&doubled, &added, choice);
224 }
225 }
226
227 result
228 }
229}
230
231impl<F: FieldOps> PointOps for JacobiIntersectionPoint<F> {
232 type BaseField = F;
233 type Curve = JacobiIntersectionCurve<F>;
234
235 fn identity(_curve: &Self::Curve) -> Self {
236 JacobiIntersectionPoint::<F>::identity()
237 }
238
239 fn is_identity(&self) -> bool {
240 JacobiIntersectionPoint::<F>::is_identity(self)
241 }
242
243 fn negate(&self, curve: &Self::Curve) -> Self {
244 JacobiIntersectionPoint::<F>::negate(self, curve)
245 }
246
247 fn scalar_mul(&self, k: &[u64], curve: &Self::Curve) -> Self {
248 JacobiIntersectionPoint::<F>::scalar_mul(self, k, curve)
249 }
250}
251
252impl<F: FieldOps> PointAdd for JacobiIntersectionPoint<F> {
253 fn add(&self, other: &Self, curve: &Self::Curve) -> Self {
254 JacobiIntersectionPoint::<F>::add(self, other, curve)
255 }
256}