1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
// Copyright 2024 Ulvetanna Inc.

use std::ops::Deref;

use crate::{
	linear_transformation::{FieldLinearTransformation, Transformation},
	packed::PackedBinaryField,
};

/// Value that can be multiplied by itself
pub trait Square {
	/// Returns the value multiplied by itself
	fn square(self) -> Self;
}

/// Value that can be inverted
pub trait InvertOrZero {
	/// Returns the inverted value or zero in case when `self` is zero
	fn invert_or_zero(self) -> Self;
}

/// Value that can be multiplied by alpha
pub trait MulAlpha {
	/// Multiply self by alpha
	fn mul_alpha(self) -> Self;
}

/// Value that can be filled with `Scalar`
pub trait Broadcast<Scalar> {
	/// Set `scalar`` value to all the positions
	fn broadcast(scalar: Scalar) -> Self;
}

/// Multiplication that is parameterized with some some strategy.
pub trait TaggedMul<Strategy> {
	fn mul(self, rhs: Self) -> Self;
}

macro_rules! impl_mul_with {
	($name:ident @ $strategy:ty) => {
		impl std::ops::Mul for $name {
			type Output = Self;

			#[inline]
			fn mul(self, rhs: Self) -> Self {
				$crate::tracing::trace_multiplication!($name);

				$crate::arithmetic_traits::TaggedMul::<$strategy>::mul(self, rhs)
			}
		}
	};
	($name:ty => $bigger:ty) => {
		impl std::ops::Mul for $name {
			type Output = Self;

			#[inline]
			fn mul(self, rhs: Self) -> Self {
				$crate::arch::portable::packed::mul_as_bigger_type::<_, $bigger>(self, rhs)
			}
		}
	};
}

pub(crate) use impl_mul_with;

/// Square operation that is parameterized with some some strategy.
pub trait TaggedSquare<Strategy> {
	fn square(self) -> Self;
}

macro_rules! impl_square_with {
	($name:ident @ $strategy:ty) => {
		impl $crate::arithmetic_traits::Square for $name {
			#[inline]
			fn square(self) -> Self {
				$crate::arithmetic_traits::TaggedSquare::<$strategy>::square(self)
			}
		}
	};
	($name:ty => $bigger:ty) => {
		impl $crate::arithmetic_traits::Square for $name {
			#[inline]
			fn square(self) -> Self {
				$crate::arch::portable::packed::square_as_bigger_type::<_, $bigger>(self)
			}
		}
	};
}

pub(crate) use impl_square_with;

/// Invert or zero operation that is parameterized with some some strategy.
pub trait TaggedInvertOrZero<Strategy> {
	fn invert_or_zero(self) -> Self;
}

macro_rules! impl_invert_with {
	($name:ident @ $strategy:ty) => {
		impl $crate::arithmetic_traits::InvertOrZero for $name {
			#[inline]
			fn invert_or_zero(self) -> Self {
				$crate::arithmetic_traits::TaggedInvertOrZero::<$strategy>::invert_or_zero(self)
			}
		}
	};
	($name:ty => $bigger:ty) => {
		impl $crate::arithmetic_traits::InvertOrZero for $name {
			#[inline]
			fn invert_or_zero(self) -> Self {
				$crate::arch::portable::packed::invert_as_bigger_type::<_, $bigger>(self)
			}
		}
	};
}

pub(crate) use impl_invert_with;

/// Multiply by alpha operation that is parameterized with some some strategy.
pub trait TaggedMulAlpha<Strategy> {
	fn mul_alpha(self) -> Self;
}

macro_rules! impl_mul_alpha_with {
	($name:ident @ $strategy:ty) => {
		impl $crate::arithmetic_traits::MulAlpha for $name {
			#[inline]
			fn mul_alpha(self) -> Self {
				$crate::arithmetic_traits::TaggedMulAlpha::<$strategy>::mul_alpha(self)
			}
		}
	};
	($name:ty => $bigger:ty) => {
		impl $crate::arithmetic_traits::MulAlpha for $name {
			#[inline]
			fn mul_alpha(self) -> Self {
				$crate::arch::portable::packed::mul_alpha_as_bigger_type::<_, $bigger>(self)
			}
		}
	};
}

pub(crate) use impl_mul_alpha_with;

/// Linear transformation factory that is parameterized with some strategy.
#[allow(private_bounds)]
pub trait TaggedPackedTransformationFactory<Strategy, OP>: PackedBinaryField
where
	OP: PackedBinaryField,
{
	type PackedTransformation<Data: Deref<Target = [OP::Scalar]>>: Transformation<Self, OP>;

	fn make_packed_transformation<Data: Deref<Target = [OP::Scalar]>>(
		transformation: FieldLinearTransformation<OP::Scalar, Data>,
	) -> Self::PackedTransformation<Data>;
}

macro_rules! impl_transformation_with_strategy {
	($name:ty, $strategy:ty) => {
		impl<OP> $crate::linear_transformation::PackedTransformationFactory<OP> for $name
		where
			OP: $crate::packed::PackedBinaryField
				+ $crate::underlier::WithUnderlier<
					Underlier = <$name as $crate::underlier::WithUnderlier>::Underlier,
				>,
		{
			type PackedTransformation<Data: std::ops::Deref<Target = [OP::Scalar]>> =
				<Self as $crate::arithmetic_traits::TaggedPackedTransformationFactory<
					$strategy,
					OP,
				>>::PackedTransformation<Data>;

			fn make_packed_transformation<Data: std::ops::Deref<Target = [OP::Scalar]>>(
				transformation: $crate::linear_transformation::FieldLinearTransformation<
					OP::Scalar,
					Data,
				>,
			) -> Self::PackedTransformation<Data> {
				<Self as $crate::arithmetic_traits::TaggedPackedTransformationFactory<
					$strategy,
					OP,
				>>::make_packed_transformation(transformation)
			}
		}
	};
}

pub(crate) use impl_transformation_with_strategy;