use core::mem::MaybeUninit;
use super::arith::Field;
use super::p256_x86::has_adx;
use super::point::{AffinePoint, Point};
macro_rules! joint {
($name:ident, $curve:ty, $fp:path, $limbs:literal, $buf:literal, $dbl:literal,
$double:path, $add:path, $points:ident) => {
pub(crate) fn $name(
g: &[AffinePoint<$curve>],
q: &[AffinePoint<$curve>],
g_naf: &[i8],
q_naf: &[i8],
) -> Option<Point<$curve>> {
if !has_adx() {
return None;
}
use super::$points as pt;
const _: () = assert!(
pt::DOUBLE_IN_PLACE_X == pt::ADD_AFFINE_X1
&& pt::DOUBLE_IN_PLACE_Y == pt::ADD_AFFINE_Y1
&& pt::DOUBLE_IN_PLACE_Z == pt::ADD_AFFINE_Z1
&& pt::ADD_AFFINE_Y1 == pt::ADD_AFFINE_X1 + 1
&& pt::ADD_AFFINE_Z1 == pt::ADD_AFFINE_X1 + 2
&& pt::ADD_AFFINE_X2 == pt::ADD_AFFINE_X1 + 3
&& pt::ADD_AFFINE_Y2 == pt::ADD_AFFINE_X1 + 4
&& pt::ADD_AFFINE_Y3 == pt::ADD_AFFINE_X3 + 1
&& pt::ADD_AFFINE_Z3 == pt::ADD_AFFINE_X3 + 2
&& pt::ADD_AFFINE_H == pt::ADD_AFFINE_X3 + 3
&& pt::ADD_AFFINE_R == pt::ADD_AFFINE_H + 1,
);
debug_assert_eq!(g_naf.len(), q_naf.len());
let mut buf = [const { MaybeUninit::<u64>::uninit() }; $buf];
let put = |buf: &mut [MaybeUninit<u64>; $buf], slot: usize, w: &[u64; $limbs]| {
for (j, v) in w.iter().enumerate() {
buf[$limbs * slot + j].write(*v);
}
};
let get = |buf: &[MaybeUninit<u64>; $buf], slot: usize| -> [u64; $limbs] {
let mut w = [0u64; $limbs];
for (j, v) in w.iter_mut().enumerate() {
*v = unsafe { buf[$limbs * slot + j].assume_init() };
}
w
};
let double = |buf: &mut [MaybeUninit<u64>; $buf]| {
let ip: &mut [MaybeUninit<u64>; $dbl] = (&mut buf[..$dbl])
.try_into()
.expect("the doubling's slots are a prefix of the buffer");
unsafe { $double(ip) };
};
let mut at_infinity = true;
for i in (0..g_naf.len()).rev() {
if !at_infinity {
double(&mut buf);
}
for (naf, table) in [(g_naf, g), (q_naf, q)] {
let digit = naf[i];
if digit == 0 {
continue;
}
let entry = &table[(digit.unsigned_abs() >> 1) as usize];
let y = if digit > 0 { entry.y } else { entry.y.neg() };
if at_infinity {
put(&mut buf, pt::ADD_AFFINE_X1, &entry.x.0);
put(&mut buf, pt::ADD_AFFINE_Y1, &y.0);
put(&mut buf, pt::ADD_AFFINE_Z1, &<$fp>::ONE.0);
at_infinity = false;
continue;
}
put(&mut buf, pt::ADD_AFFINE_X2, &entry.x.0);
put(&mut buf, pt::ADD_AFFINE_Y2, &y.0);
unsafe { $add(&mut buf) };
if get(&buf, pt::ADD_AFFINE_H) != [0; $limbs] {
for k in 0..3 {
let c = get(&buf, pt::ADD_AFFINE_X3 + k);
put(&mut buf, pt::ADD_AFFINE_X1 + k, &c);
}
} else if get(&buf, pt::ADD_AFFINE_R) == [0; $limbs] {
double(&mut buf);
} else {
at_infinity = true;
}
}
}
if at_infinity {
return Some(Point::identity());
}
let [x, y, z] = [
get(&buf, pt::ADD_AFFINE_X1),
get(&buf, pt::ADD_AFFINE_Y1),
get(&buf, pt::ADD_AFFINE_Z1),
];
Some(Point::from_coordinates($fp(x), $fp(y), $fp(z)))
}
};
}
joint!(
p256,
crate::p256::P256,
crate::p256::Fp,
4,
80,
48,
super::p256_x86_point::double_in_place_adx,
super::p256_x86_point::add_affine_adx,
p256_x86_point
);
joint!(
p384,
crate::p384::P384,
crate::p384::Fp,
6,
126,
78,
super::p384_x86_point::double_in_place_adx,
super::p384_x86_point::add_affine_adx,
p384_x86_point
);