1use std::{
5 array,
6 iter::Sum,
7 marker::PhantomData,
8 ops::{Add, AddAssign, Sub, SubAssign},
9};
10
11use bytemuck::TransparentWrapper;
12
13use crate::{
14 BinaryField,
15 arch::PackedPrimitiveType,
16 arithmetic_traits::{InvertOrZero, Square, WideMul},
17 underlier::{Divisible, UnderlierType},
18};
19
20#[repr(transparent)]
33#[derive(TransparentWrapper)]
34#[transparent(T)]
35pub struct Divide<SubU, T, const N: usize>(T, PhantomData<SubU>);
36
37impl<U, SubU, F, const N: usize> Square for Divide<SubU, PackedPrimitiveType<U, F>, N>
38where
39 U: UnderlierType + Divisible<SubU>,
40 SubU: UnderlierType,
41 F: BinaryField,
42 PackedPrimitiveType<SubU, F>: Square,
43{
44 #[inline]
45 fn square(self) -> Self {
46 let val = Self::peel(self);
47 let squared = Divisible::<SubU>::value_iter(val.to_underlier()).map(|lane| {
48 PackedPrimitiveType::<SubU, F>::from_underlier(lane)
49 .square()
50 .to_underlier()
51 });
52 Self::wrap(PackedPrimitiveType::from_underlier(Divisible::<SubU>::from_iter(squared)))
53 }
54}
55
56impl<U, SubU, F, const N: usize> InvertOrZero for Divide<SubU, PackedPrimitiveType<U, F>, N>
57where
58 U: UnderlierType + Divisible<SubU>,
59 SubU: UnderlierType,
60 F: BinaryField,
61 PackedPrimitiveType<SubU, F>: InvertOrZero,
62{
63 #[inline]
64 fn invert_or_zero(self) -> Self {
65 let val = Self::peel(self);
66 let inverted = Divisible::<SubU>::value_iter(val.to_underlier()).map(|lane| {
67 PackedPrimitiveType::<SubU, F>::from_underlier(lane)
68 .invert_or_zero()
69 .to_underlier()
70 });
71 Self::wrap(PackedPrimitiveType::from_underlier(Divisible::<SubU>::from_iter(inverted)))
72 }
73}
74
75#[derive(Clone, Copy, Debug)]
79pub struct LaneWideProduct<O, const N: usize>(pub [O; N]);
80
81impl<O: Copy + Default, const N: usize> Default for LaneWideProduct<O, N> {
82 #[inline]
83 fn default() -> Self {
84 Self([O::default(); N])
85 }
86}
87
88impl<O: Copy + Add<Output = O>, const N: usize> Add for LaneWideProduct<O, N> {
89 type Output = Self;
90
91 #[inline]
92 fn add(self, rhs: Self) -> Self {
93 Self(array::from_fn(|i| self.0[i] + rhs.0[i]))
94 }
95}
96
97impl<O: Copy + Add<Output = O>, const N: usize> AddAssign for LaneWideProduct<O, N> {
98 #[inline]
99 fn add_assign(&mut self, rhs: Self) {
100 *self = *self + rhs;
101 }
102}
103
104impl<O: Copy + Sub<Output = O>, const N: usize> Sub for LaneWideProduct<O, N> {
105 type Output = Self;
106
107 #[inline]
108 fn sub(self, rhs: Self) -> Self {
109 Self(array::from_fn(|i| self.0[i] - rhs.0[i]))
110 }
111}
112
113impl<O: Copy + Sub<Output = O>, const N: usize> SubAssign for LaneWideProduct<O, N> {
114 #[inline]
115 fn sub_assign(&mut self, rhs: Self) {
116 *self = *self - rhs;
117 }
118}
119
120impl<O: Copy + Default + Add<Output = O>, const N: usize> Sum for LaneWideProduct<O, N> {
121 #[inline]
122 fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
123 iter.fold(Self::default(), |acc, x| acc + x)
124 }
125}
126
127impl<U, SubU, F, const N: usize> WideMul for Divide<SubU, PackedPrimitiveType<U, F>, N>
128where
129 U: UnderlierType + Divisible<SubU>,
130 SubU: UnderlierType,
131 F: BinaryField,
132 PackedPrimitiveType<SubU, F>: WideMul,
133 <PackedPrimitiveType<SubU, F> as WideMul>::Output: Copy + Default,
134{
135 type Output = LaneWideProduct<<PackedPrimitiveType<SubU, F> as WideMul>::Output, N>;
136
137 #[inline]
138 fn wide_mul(a: Self, b: Self) -> Self::Output {
139 debug_assert_eq!(N, <U as Divisible<SubU>>::N, "N must equal Divisible<SubU>::N");
140
141 let a = Self::peel(a).to_underlier();
142 let b = Self::peel(b).to_underlier();
143
144 let mut lanes = [<PackedPrimitiveType<SubU, F> as WideMul>::Output::default(); N];
145 for (slot, (lhs, rhs)) in lanes
146 .iter_mut()
147 .zip(Divisible::<SubU>::value_iter(a).zip(Divisible::<SubU>::value_iter(b)))
148 {
149 *slot = <PackedPrimitiveType<SubU, F> as WideMul>::wide_mul(
150 PackedPrimitiveType::from_underlier(lhs),
151 PackedPrimitiveType::from_underlier(rhs),
152 );
153 }
154 LaneWideProduct(lanes)
155 }
156
157 #[inline]
158 fn reduce(wide: Self::Output) -> Self {
159 let lanes = wide.0.into_iter().map(|product| {
160 <PackedPrimitiveType<SubU, F> as WideMul>::reduce(product).to_underlier()
161 });
162 Self::wrap(PackedPrimitiveType::from_underlier(Divisible::<SubU>::from_iter(lanes)))
163 }
164}
165
166#[repr(transparent)]
170#[derive(TransparentWrapper)]
171pub struct MulFromWideMul<T>(T);
172
173impl<P: WideMul> std::ops::Mul for MulFromWideMul<P> {
174 type Output = Self;
175
176 #[inline]
177 fn mul(self, rhs: Self) -> Self {
178 Self::wrap(P::reduce(P::wide_mul(Self::peel(self), Self::peel(rhs))))
179 }
180}