openzeppelin_crypto/arithmetic/
limb.rs1#![allow(clippy::cast_possible_truncation, clippy::cast_lossless)]
6
7use num_traits::ConstOne;
8
9pub type Limb = u64;
11
12pub type Limbs<const N: usize> = [Limb; N];
14
15pub type WideLimb = u128;
19
20#[inline(always)]
22#[must_use]
23pub const fn widening_mul(a: Limb, b: Limb) -> WideLimb {
24 #[cfg(not(target_family = "wasm"))]
25 {
26 a as WideLimb * b as WideLimb
27 }
28 #[cfg(target_family = "wasm")]
29 {
30 widening_mul_wasm(a, b)
31 }
32}
33
34#[inline(always)]
39#[doc(hidden)]
40#[allow(dead_code)]
41const fn widening_mul_wasm(a: Limb, b: Limb) -> WideLimb {
42 let a_lo = a as u32 as Limb;
43 let a_hi = a >> 32;
44 let b_lo = b as u32 as Limb;
45 let b_hi = b >> 32;
46
47 let lolo = (a_lo * b_lo) as WideLimb;
48 let lohi = ((a_lo * b_hi) as WideLimb) << 32;
49 let hilo = ((a_hi * b_lo) as WideLimb) << 32;
50 let hihi = ((a_hi * b_hi) as WideLimb) << 64;
51 (lolo | hihi) + (lohi + hilo)
52}
53
54#[inline(always)]
57#[must_use]
58pub const fn mac(a: Limb, b: Limb, c: Limb) -> (Limb, Limb) {
59 let a = a as WideLimb;
60 let tmp = a + widening_mul(b, c);
61 let carry = (tmp >> Limb::BITS) as Limb;
62 (tmp as Limb, carry)
63}
64
65#[inline(always)]
68#[must_use]
69pub const fn carrying_mac(
70 a: Limb,
71 b: Limb,
72 c: Limb,
73 carry: Limb,
74) -> (Limb, Limb) {
75 let a = a as WideLimb;
76 let carry = carry as WideLimb;
77 let tmp = a + widening_mul(b, c) + carry;
78 let carry = (tmp >> Limb::BITS) as Limb;
79 (tmp as Limb, carry)
80}
81
82#[inline(always)]
84#[must_use]
85pub const fn adc(a: Limb, b: Limb, carry: bool) -> (Limb, bool) {
86 let a = a as WideLimb;
87 let b = b as WideLimb;
88 let carry = carry as WideLimb;
89 let tmp = a + b + carry;
90 let carry = (tmp >> Limb::BITS) != 0;
91 (tmp as Limb, carry)
92}
93
94#[inline(always)]
96pub fn adc_assign(a: &mut Limb, b: Limb, carry: bool) -> bool {
97 let tmp = *a as WideLimb + b as WideLimb + carry as WideLimb;
98 *a = tmp as Limb;
99 let carry = tmp >> Limb::BITS;
100 carry != 0
101}
102
103#[inline(always)]
105#[must_use]
106pub const fn sbb(a: Limb, b: Limb, borrow: bool) -> (Limb, bool) {
107 let a = a as WideLimb;
108 let b = b as WideLimb;
109 let borrow = borrow as WideLimb;
110 let overflow_protection = WideLimb::ONE << Limb::BITS;
112 let tmp = overflow_protection + a - b - borrow;
113 let borrow = tmp >> Limb::BITS == 0;
114 (tmp as Limb, borrow)
116}
117
118#[inline(always)]
120pub fn sbb_assign(a: &mut Limb, b: Limb, borrow: bool) -> bool {
121 let (sub, borrow1) = a.overflowing_sub(b);
122 let (sub, borrow2) = sub.overflowing_sub(borrow as Limb);
123 *a = sub;
124 borrow1 | borrow2
125}
126
127#[cfg(test)]
128mod tests {
129 use proptest::prelude::*;
130
131 use super::*;
132
133 #[test]
134 fn check_widening_mul() {
135 proptest!(|(a: Limb, b: Limb)|{
136 let std_mul_result = widening_mul(a, b);
137 let wasm_mul_result = widening_mul_wasm(a, b);
138 prop_assert_eq!(std_mul_result, wasm_mul_result);
139 });
140 }
141}