#![allow(clippy::cast_possible_truncation, clippy::needless_range_loop)]
#[cfg(not(any(feature = "critical-section", feature = "std")))]
compile_error!("`basepoint-table` feature requires either `critical-section` or `std`");
use super::LookupTable;
use crate::{PrimeCurveParams, ProjectivePoint, Radix16Decomposition, Radix16Digits, Scalar};
use core::ops::Deref;
use elliptic_curve::{
FieldBytesSize, array::typenum::Unsigned, ff::PrimeField, group::Group,
subtle::ConditionallySelectable,
};
#[cfg(feature = "critical-section")]
use once_cell::sync::Lazy as LazyLock;
#[cfg(all(feature = "std", not(feature = "critical-section")))]
use std::sync::LazyLock;
#[derive(Debug)]
pub struct BasepointTable<Point, const WINDOW_SIZE: usize> {
tables: LazyLock<[LookupTable<Point>; WINDOW_SIZE]>,
}
impl<Point, const WINDOW_SIZE: usize> BasepointTable<Point, WINDOW_SIZE>
where
Point: ConditionallySelectable + Default + Group,
{
pub const fn new() -> Self {
fn init_table<Point, const N: usize>() -> [LookupTable<Point>; N]
where
Point: ConditionallySelectable + Default + Group,
{
const {
assert!(
N as u32 == 1 + Point::Scalar::NUM_BITS.div_ceil(8),
"incorrectly sized basepoint table"
);
}
let mut generator = Point::generator();
let mut res = [LookupTable::<Point>::default(); N];
for i in 0..N {
res[i] = LookupTable::new(generator);
for _ in 0..8 {
generator = generator.double();
}
}
res
}
Self {
tables: LazyLock::new(init_table),
}
}
}
impl<C: PrimeCurveParams, const WINDOW_SIZE: usize>
BasepointTable<ProjectivePoint<C>, WINDOW_SIZE>
{
pub fn mul(&self, k: &Scalar<C>) -> ProjectivePoint<C> {
let digits = Radix16Decomposition::<Radix16Digits<C>>::new(k);
let len = FieldBytesSize::<C>::USIZE;
let mut acc = self[len].select(digits[len * 2]);
let mut acc2 = ProjectivePoint::<C>::IDENTITY;
for i in (0..len).rev() {
acc2 += &self[i].select(digits[i * 2 + 1]);
acc += &self[i].select(digits[i * 2]);
}
for _ in 0..4 {
acc2 = acc2.double();
}
acc + acc2
}
pub fn mul_vartime(&self, k: &Scalar<C>) -> ProjectivePoint<C> {
let digits = Radix16Decomposition::<Radix16Digits<C>>::new(k);
let len = FieldBytesSize::<C>::USIZE;
let mut acc = self[len].select_vartime(digits[len * 2]);
let mut acc2 = ProjectivePoint::<C>::IDENTITY;
for i in (0..len).rev() {
acc2 += &self[i].select_vartime(digits[i * 2 + 1]);
acc += &self[i].select_vartime(digits[i * 2]);
}
for _ in 0..4 {
acc2 = acc2.double();
}
acc + acc2
}
}
impl<Point, const WINDOW_SIZE: usize> Default for BasepointTable<Point, WINDOW_SIZE>
where
Point: ConditionallySelectable + Default + Group,
{
fn default() -> Self {
Self::new()
}
}
impl<Point, const WINDOW_SIZE: usize> Deref for BasepointTable<Point, WINDOW_SIZE> {
type Target = [LookupTable<Point>; WINDOW_SIZE];
#[inline]
fn deref(&self) -> &[LookupTable<Point>; WINDOW_SIZE] {
&self.tables
}
}