use core::fmt;
use crate::int::Int;
use crate::mod_int::ModInt;
use crate::ring::Field;
fn field_mul_small<F: Field>(x: &F, mut n: u64) -> F {
let mut acc = x.zero();
let mut base = x.clone();
while n > 0 {
if n & 1 == 1 {
acc = acc + base.clone();
}
n >>= 1;
if n > 0 {
base = base.clone() + base.clone();
}
}
acc
}
#[derive(Clone)]
pub struct EllipticCurve<F: Field> {
a: F,
b: F,
}
impl<F: Field> EllipticCurve<F> {
pub fn new(a: F, b: F) -> Option<EllipticCurve<F>> {
let curve = EllipticCurve { a, b };
if curve.discriminant().is_zero() {
None
} else {
Some(curve)
}
}
#[inline]
pub fn a(&self) -> &F {
&self.a
}
#[inline]
pub fn b(&self) -> &F {
&self.b
}
pub fn discriminant(&self) -> F {
let a3 = self.a.clone() * self.a.clone() * self.a.clone();
let b2 = self.b.clone() * self.b.clone();
let inner = field_mul_small(&a3, 4) + field_mul_small(&b2, 27);
-field_mul_small(&inner, 16)
}
pub fn j_invariant(&self) -> F {
let a3 = self.a.clone() * self.a.clone() * self.a.clone();
let b2 = self.b.clone() * self.b.clone();
let denom = field_mul_small(&a3, 4) + field_mul_small(&b2, 27);
field_mul_small(&a3, 6912) / denom
}
pub fn identity(&self) -> Point<F> {
Point {
curve: self.clone(),
coords: None,
}
}
fn rhs(&self, x: &F) -> F {
x.clone() * x.clone() * x.clone() + self.a.clone() * x.clone() + self.b.clone()
}
pub fn point(&self, x: F, y: F) -> Option<Point<F>> {
let p = Point {
curve: self.clone(),
coords: Some((x, y)),
};
if p.is_on_curve() { Some(p) } else { None }
}
}
impl<F: Field + fmt::Display> fmt::Display for EllipticCurve<F> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "y² = x³ + {}·x + {}", self.a, self.b)
}
}
impl<F: Field + fmt::Debug> fmt::Debug for EllipticCurve<F> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "EllipticCurve {{ a: {:?}, b: {:?} }}", self.a, self.b)
}
}
impl<F: Field> PartialEq for EllipticCurve<F> {
fn eq(&self, other: &Self) -> bool {
self.a == other.a && self.b == other.b
}
}
#[derive(Clone)]
pub struct Point<F: Field> {
curve: EllipticCurve<F>,
coords: Option<(F, F)>,
}
impl<F: Field> Point<F> {
#[inline]
pub fn curve(&self) -> &EllipticCurve<F> {
&self.curve
}
#[inline]
pub fn is_infinity(&self) -> bool {
self.coords.is_none()
}
#[inline]
pub fn coordinates(&self) -> Option<(&F, &F)> {
self.coords.as_ref().map(|(x, y)| (x, y))
}
#[inline]
pub fn x(&self) -> Option<&F> {
self.coords.as_ref().map(|(x, _)| x)
}
#[inline]
pub fn y(&self) -> Option<&F> {
self.coords.as_ref().map(|(_, y)| y)
}
pub fn is_on_curve(&self) -> bool {
match &self.coords {
None => true,
Some((x, y)) => y.clone() * y.clone() == self.curve.rhs(x),
}
}
pub fn neg(&self) -> Point<F> {
match &self.coords {
None => self.clone(),
Some((x, y)) => Point {
curve: self.curve.clone(),
coords: Some((x.clone(), -y.clone())),
},
}
}
pub fn double(&self) -> Point<F> {
let (x, y) = match &self.coords {
None => return self.clone(),
Some(p) => p,
};
if y.is_zero() {
return self.curve.identity();
}
let three_x2 = field_mul_small(&(x.clone() * x.clone()), 3);
let num = three_x2 + self.curve.a.clone();
let den = y.clone() + y.clone();
let lambda = num / den;
self.line_result(&lambda, x, x, y)
}
pub fn add(&self, rhs: &Point<F>) -> Point<F> {
assert!(
self.curve == rhs.curve,
"Point::add: points lie on different curves"
);
let (x1, y1) = match &self.coords {
None => return rhs.clone(),
Some(p) => p,
};
let (x2, y2) = match &rhs.coords {
None => return self.clone(),
Some(p) => p,
};
if x1 == x2 {
if y1 == y2 {
return self.double();
}
return self.curve.identity();
}
let lambda = (y2.clone() - y1.clone()) / (x2.clone() - x1.clone());
self.line_result(&lambda, x1, x2, y1)
}
fn line_result(&self, lambda: &F, x1: &F, x2: &F, y1: &F) -> Point<F> {
let x3 = lambda.clone() * lambda.clone() - x1.clone() - x2.clone();
let y3 = lambda.clone() * (x1.clone() - x3.clone()) - y1.clone();
Point {
curve: self.curve.clone(),
coords: Some((x3, y3)),
}
}
pub fn scalar_mul(&self, k: &Int) -> Point<F> {
if k.is_zero() || self.is_infinity() {
return self.curve.identity();
}
let mag = k.abs();
let mut result = self.curve.identity();
let base = self.clone();
let mut i = mag.bit_len();
while i > 0 {
i -= 1;
result = result.double();
if mag.bit(i) {
result = result.add(&base);
}
}
if k.is_negative() {
result.neg()
} else {
result
}
}
}
impl<F: Field> PartialEq for Point<F> {
fn eq(&self, other: &Self) -> bool {
self.curve == other.curve && self.coords == other.coords
}
}
impl<F: Field + fmt::Display> fmt::Display for Point<F> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.coords {
None => write!(f, "O"),
Some((x, y)) => write!(f, "({}, {})", x, y),
}
}
}
impl<F: Field + fmt::Debug> fmt::Debug for Point<F> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.coords {
None => write!(f, "Point(O)"),
Some((x, y)) => write!(f, "Point({:?}, {:?})", x, y),
}
}
}
impl<F: Field> core::ops::Add for Point<F> {
type Output = Point<F>;
#[inline]
fn add(self, rhs: Point<F>) -> Point<F> {
Point::add(&self, &rhs)
}
}
impl<F: Field> core::ops::Add<&Point<F>> for &Point<F> {
type Output = Point<F>;
#[inline]
fn add(self, rhs: &Point<F>) -> Point<F> {
Point::add(self, rhs)
}
}
impl<F: Field> core::ops::Neg for Point<F> {
type Output = Point<F>;
#[inline]
fn neg(self) -> Point<F> {
Point::neg(&self)
}
}
impl<F: Field> core::ops::Neg for &Point<F> {
type Output = Point<F>;
#[inline]
fn neg(self) -> Point<F> {
Point::neg(self)
}
}
impl EllipticCurve<ModInt> {
#[inline]
pub fn field_prime(&self) -> Int {
self.a.modulus()
}
pub fn point_from_x(&self, x: &ModInt) -> Option<Point<ModInt>> {
let p = self.field_prime();
let rhs = self.rhs(x);
let y = rhs.to_int().sqrt_mod(&p)?;
Some(Point {
curve: self.clone(),
coords: Some((x.clone(), x.of(y))),
})
}
pub fn curve_order(&self) -> Int {
let p = self.field_prime();
let mut count = Int::ONE;
let mut xi = self.a.of(Int::ZERO);
let one = self.a.of(Int::ONE);
let mut x = Int::ZERO;
while x < p {
let rhs = self.rhs(&xi);
if rhs.is_zero() {
count += Int::ONE; } else {
let leg = rhs.to_int().legendre(&p);
count += Int::from(1 + leg);
}
xi += one.clone();
x += Int::ONE;
}
count
}
pub fn order_of_point(&self, point: &Point<ModInt>) -> Int {
assert!(
*point.curve() == *self,
"order_of_point: point lies on a different curve"
);
if point.is_infinity() {
return Int::ONE;
}
let mut order = self.curve_order();
for q in order.clone().factorize() {
loop {
let (quot, rem) = order.div_rem_trunc(&q);
if !rem.is_zero() {
break;
}
if !point.scalar_mul(").is_infinity() {
break;
}
order = quot;
}
}
order
}
}