1use super::{MODULUS, R_2};
4use crate::arithmetic::util::{adc, mac, sbb};
5
6pub type Fe = [u64; 4];
8
9#[inline]
11pub const fn fe_from_montgomery(w: &Fe) -> Fe {
12    montgomery_reduce(&[w[0], w[1], w[2], w[3], 0, 0, 0, 0])
13}
14
15#[inline]
17pub const fn fe_to_montgomery(w: &Fe) -> Fe {
18    fe_mul(w, R_2.as_words())
19}
20
21pub const fn fe_add(a: &Fe, b: &Fe) -> Fe {
23    let (w0, carry) = adc(a[0], b[0], 0);
25    let (w1, carry) = adc(a[1], b[1], carry);
26    let (w2, carry) = adc(a[2], b[2], carry);
27    let (w3, w4) = adc(a[3], b[3], carry);
28
29    let modulus = MODULUS.as_words();
31    sub_inner(
32        &[w0, w1, w2, w3, w4],
33        &[modulus[0], modulus[1], modulus[2], modulus[3], 0],
34    )
35}
36
37pub const fn fe_sub(a: &Fe, b: &Fe) -> Fe {
39    sub_inner(&[a[0], a[1], a[2], a[3], 0], &[b[0], b[1], b[2], b[3], 0])
40}
41
42pub const fn fe_mul(a: &Fe, b: &Fe) -> Fe {
44    let (w0, carry) = mac(0, a[0], b[0], 0);
45    let (w1, carry) = mac(0, a[0], b[1], carry);
46    let (w2, carry) = mac(0, a[0], b[2], carry);
47    let (w3, w4) = mac(0, a[0], b[3], carry);
48
49    let (w1, carry) = mac(w1, a[1], b[0], 0);
50    let (w2, carry) = mac(w2, a[1], b[1], carry);
51    let (w3, carry) = mac(w3, a[1], b[2], carry);
52    let (w4, w5) = mac(w4, a[1], b[3], carry);
53
54    let (w2, carry) = mac(w2, a[2], b[0], 0);
55    let (w3, carry) = mac(w3, a[2], b[1], carry);
56    let (w4, carry) = mac(w4, a[2], b[2], carry);
57    let (w5, w6) = mac(w5, a[2], b[3], carry);
58
59    let (w3, carry) = mac(w3, a[3], b[0], 0);
60    let (w4, carry) = mac(w4, a[3], b[1], carry);
61    let (w5, carry) = mac(w5, a[3], b[2], carry);
62    let (w6, w7) = mac(w6, a[3], b[3], carry);
63
64    montgomery_reduce(&[w0, w1, w2, w3, w4, w5, w6, w7])
65}
66
67pub const fn fe_neg(w: &Fe) -> Fe {
69    fe_sub(&[0, 0, 0, 0], w)
70}
71
72pub const fn fe_square(w: &Fe) -> Fe {
74    fe_mul(w, w)
75}
76
77#[inline]
117#[allow(clippy::too_many_arguments)]
118const fn montgomery_reduce(r: &[u64; 8]) -> Fe {
119    let r0 = r[0];
120    let r1 = r[1];
121    let r2 = r[2];
122    let r3 = r[3];
123    let r4 = r[4];
124    let r5 = r[5];
125    let r6 = r[6];
126    let r7 = r[7];
127    let modulus = MODULUS.as_words();
128
129    let (r1, carry) = mac(r1, r0, modulus[1], r0);
130    let (r2, carry) = adc(r2, 0, carry);
131    let (r3, carry) = mac(r3, r0, modulus[3], carry);
132    let (r4, carry2) = adc(r4, 0, carry);
133
134    let (r2, carry) = mac(r2, r1, modulus[1], r1);
135    let (r3, carry) = adc(r3, 0, carry);
136    let (r4, carry) = mac(r4, r1, modulus[3], carry);
137    let (r5, carry2) = adc(r5, carry2, carry);
138
139    let (r3, carry) = mac(r3, r2, modulus[1], r2);
140    let (r4, carry) = adc(r4, 0, carry);
141    let (r5, carry) = mac(r5, r2, modulus[3], carry);
142    let (r6, carry2) = adc(r6, carry2, carry);
143
144    let (r4, carry) = mac(r4, r3, modulus[1], r3);
145    let (r5, carry) = adc(r5, 0, carry);
146    let (r6, carry) = mac(r6, r3, modulus[3], carry);
147    let (r7, r8) = adc(r7, carry2, carry);
148
149    sub_inner(
151        &[r4, r5, r6, r7, r8],
152        &[modulus[0], modulus[1], modulus[2], modulus[3], 0],
153    )
154}
155
156#[inline]
157#[allow(clippy::too_many_arguments)]
158const fn sub_inner(l: &[u64; 5], r: &[u64; 5]) -> Fe {
159    let (w0, borrow) = sbb(l[0], r[0], 0);
160    let (w1, borrow) = sbb(l[1], r[1], borrow);
161    let (w2, borrow) = sbb(l[2], r[2], borrow);
162    let (w3, borrow) = sbb(l[3], r[3], borrow);
163    let (_, borrow) = sbb(l[4], r[4], borrow);
164
165    let modulus = MODULUS.as_words();
169    let (w0, carry) = adc(w0, modulus[0] & borrow, 0);
170    let (w1, carry) = adc(w1, modulus[1] & borrow, carry);
171    let (w2, carry) = adc(w2, modulus[2] & borrow, carry);
172    let (w3, _) = adc(w3, modulus[3] & borrow, carry);
173
174    [w0, w1, w2, w3]
175}