Skip to content

Commit 4a38773

Browse files
committed
Add i256 and u256 bigint types
1 parent a98982e commit 4a38773

File tree

3 files changed

+314
-0
lines changed

3 files changed

+314
-0
lines changed

src/int/big.rs

Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
//! Integers used for wide operations, larger than `u128`.
2+
3+
#![allow(unused)]
4+
5+
use crate::int::{DInt, HInt, Int, MinInt};
6+
use core::{fmt, ops};
7+
8+
const WORD_LO_MASK: u64 = 0x00000000ffffffff;
9+
const WORD_HI_MASK: u64 = 0xffffffff00000000;
10+
const WORD_FULL_MASK: u64 = 0xffffffffffffffff;
11+
const U128_LO_MASK: u128 = u64::MAX as u128;
12+
const U128_HI_MASK: u128 = (u64::MAX as u128) << 64;
13+
14+
/// A 256-bit unsigned integer represented as 4 64-bit limbs.
15+
///
16+
/// Each limb is a native-endian number, but the array is little-limb-endian.
17+
#[allow(non_camel_case_types)]
18+
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
19+
pub struct u256(pub [u64; 4]);
20+
21+
impl u256 {
22+
pub const MAX: Self = Self([u64::MAX, u64::MAX, u64::MAX, u64::MAX]);
23+
24+
/// Reinterpret as a signed integer
25+
pub fn signed(self) -> i256 {
26+
i256(self.0)
27+
}
28+
}
29+
30+
/// A 256-bit signed integer represented as 4 64-bit limbs.
31+
///
32+
/// Each limb is a native-endian number, but the array is little-limb-endian.
33+
#[allow(non_camel_case_types)]
34+
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
35+
pub struct i256(pub [u64; 4]);
36+
37+
impl i256 {
38+
/// Reinterpret as an unsigned integer
39+
pub fn unsigned(self) -> u256 {
40+
u256(self.0)
41+
}
42+
}
43+
44+
impl MinInt for u256 {
45+
type OtherSign = i256;
46+
47+
type UnsignedInt = u256;
48+
49+
const SIGNED: bool = false;
50+
const BITS: u32 = 256;
51+
const ZERO: Self = Self([0u64; 4]);
52+
const ONE: Self = Self([1, 0, 0, 0]);
53+
const MIN: Self = Self([0u64; 4]);
54+
const MAX: Self = Self([u64::MAX; 4]);
55+
}
56+
57+
impl MinInt for i256 {
58+
type OtherSign = u256;
59+
60+
type UnsignedInt = u256;
61+
62+
const SIGNED: bool = false;
63+
const BITS: u32 = 256;
64+
const ZERO: Self = Self([0u64; 4]);
65+
const ONE: Self = Self([1, 0, 0, 0]);
66+
const MIN: Self = Self([0, 0, 0, 1 << 63]);
67+
const MAX: Self = Self([u64::MAX, u64::MAX, u64::MAX, u64::MAX << 1]);
68+
}
69+
70+
macro_rules! impl_common {
71+
($ty:ty) => {
72+
impl ops::BitOr for $ty {
73+
type Output = Self;
74+
75+
fn bitor(mut self, rhs: Self) -> Self::Output {
76+
self.0[0] |= rhs.0[0];
77+
self.0[1] |= rhs.0[1];
78+
self.0[2] |= rhs.0[2];
79+
self.0[3] |= rhs.0[3];
80+
self
81+
}
82+
}
83+
84+
impl ops::Not for $ty {
85+
type Output = Self;
86+
87+
fn not(self) -> Self::Output {
88+
Self([!self.0[0], !self.0[1], !self.0[2], !self.0[3]])
89+
}
90+
}
91+
92+
impl ops::Shl<u32> for $ty {
93+
type Output = Self;
94+
95+
fn shl(self, rhs: u32) -> Self::Output {
96+
todo!()
97+
}
98+
}
99+
};
100+
}
101+
102+
impl_common!(i256);
103+
impl_common!(u256);
104+
105+
macro_rules! word {
106+
(1, $val:expr) => {
107+
(($val >> (32 * 3)) & Self::from(WORD_LO_MASK)) as u64
108+
};
109+
(2, $val:expr) => {
110+
(($val >> (32 * 2)) & Self::from(WORD_LO_MASK)) as u64
111+
};
112+
(3, $val:expr) => {
113+
(($val >> (32 * 1)) & Self::from(WORD_LO_MASK)) as u64
114+
};
115+
(4, $val:expr) => {
116+
(($val >> (32 * 0)) & Self::from(WORD_LO_MASK)) as u64
117+
};
118+
}
119+
120+
impl HInt for u128 {
121+
type D = u256;
122+
123+
fn widen(self) -> Self::D {
124+
let w0 = self & u128::from(u64::MAX);
125+
let w1 = (self >> u64::BITS) & u128::from(u64::MAX);
126+
u256([w0 as u64, w1 as u64, 0, 0])
127+
}
128+
129+
fn zero_widen(self) -> Self::D {
130+
self.widen()
131+
}
132+
133+
fn zero_widen_mul(self, rhs: Self) -> Self::D {
134+
let product11: u64 = word!(1, self) * word!(1, rhs);
135+
let product12: u64 = word!(1, self) * word!(2, rhs);
136+
let product13: u64 = word!(1, self) * word!(3, rhs);
137+
let product14: u64 = word!(1, self) * word!(4, rhs);
138+
let product21: u64 = word!(2, self) * word!(1, rhs);
139+
let product22: u64 = word!(2, self) * word!(2, rhs);
140+
let product23: u64 = word!(2, self) * word!(3, rhs);
141+
let product24: u64 = word!(2, self) * word!(4, rhs);
142+
let product31: u64 = word!(3, self) * word!(1, rhs);
143+
let product32: u64 = word!(3, self) * word!(2, rhs);
144+
let product33: u64 = word!(3, self) * word!(3, rhs);
145+
let product34: u64 = word!(3, self) * word!(4, rhs);
146+
let product41: u64 = word!(4, self) * word!(1, rhs);
147+
let product42: u64 = word!(4, self) * word!(2, rhs);
148+
let product43: u64 = word!(4, self) * word!(3, rhs);
149+
let product44: u64 = word!(4, self) * word!(4, rhs);
150+
151+
let sum0: u128 = u128::from(product44);
152+
let sum1: u128 = u128::from(product34) + u128::from(product43);
153+
let sum2: u128 = u128::from(product24) + u128::from(product33) + u128::from(product42);
154+
let sum3: u128 = u128::from(product14)
155+
+ u128::from(product23)
156+
+ u128::from(product32)
157+
+ u128::from(product41);
158+
let sum4: u128 = u128::from(product13) + u128::from(product22) + u128::from(product31);
159+
let sum5: u128 = u128::from(product12) + u128::from(product21);
160+
let sum6: u128 = u128::from(product11);
161+
162+
let r0: u128 =
163+
(sum0 & u128::from(WORD_FULL_MASK)) + ((sum1 & u128::from(WORD_LO_MASK)) << 32);
164+
let r1: u128 = (sum0 >> 64)
165+
+ ((sum1 >> 32) & u128::from(WORD_FULL_MASK))
166+
+ (sum2 & u128::from(WORD_FULL_MASK))
167+
+ ((sum3 << 32) & u128::from(WORD_HI_MASK));
168+
169+
let (lo, carry) = r0.overflowing_add(r1 << 64);
170+
let hi = (r1 >> 64)
171+
+ (sum1 >> 96)
172+
+ (sum2 >> 64)
173+
+ (sum3 >> 32)
174+
+ sum4
175+
+ (sum5 << 32)
176+
+ (sum6 << 64)
177+
+ u128::from(carry);
178+
179+
u256([
180+
(lo & U128_LO_MASK) as u64,
181+
((lo >> 64) & U128_LO_MASK) as u64,
182+
(hi & U128_LO_MASK) as u64,
183+
((hi >> 64) & U128_LO_MASK) as u64,
184+
])
185+
}
186+
187+
fn widen_mul(self, rhs: Self) -> Self::D {
188+
self.zero_widen_mul(rhs)
189+
}
190+
}
191+
192+
impl HInt for i128 {
193+
type D = i256;
194+
195+
fn widen(self) -> Self::D {
196+
let mut ret = self.unsigned().zero_widen().signed();
197+
if self.is_negative() {
198+
ret.0[2] = u64::MAX;
199+
ret.0[3] = u64::MAX;
200+
}
201+
ret
202+
}
203+
204+
fn zero_widen(self) -> Self::D {
205+
self.unsigned().zero_widen().signed()
206+
}
207+
208+
fn zero_widen_mul(self, rhs: Self) -> Self::D {
209+
self.unsigned().zero_widen_mul(rhs.unsigned()).signed()
210+
}
211+
212+
fn widen_mul(self, rhs: Self) -> Self::D {
213+
unimplemented!("signed i128 widening multiply is not used")
214+
}
215+
}
216+
217+
impl DInt for u256 {
218+
type H = u128;
219+
220+
fn lo(self) -> Self::H {
221+
let mut tmp = [0u8; 16];
222+
tmp[..8].copy_from_slice(&self.0[0].to_le_bytes());
223+
tmp[8..].copy_from_slice(&self.0[1].to_le_bytes());
224+
u128::from_le_bytes(tmp)
225+
}
226+
227+
fn hi(self) -> Self::H {
228+
let mut tmp = [0u8; 16];
229+
tmp[..8].copy_from_slice(&self.0[2].to_le_bytes());
230+
tmp[8..].copy_from_slice(&self.0[3].to_le_bytes());
231+
u128::from_le_bytes(tmp)
232+
}
233+
}
234+
235+
impl DInt for i256 {
236+
type H = i128;
237+
238+
fn lo(self) -> Self::H {
239+
let mut tmp = [0u8; 16];
240+
tmp[..8].copy_from_slice(&self.0[0].to_le_bytes());
241+
tmp[8..].copy_from_slice(&self.0[1].to_le_bytes());
242+
i128::from_le_bytes(tmp)
243+
}
244+
245+
fn hi(self) -> Self::H {
246+
let mut tmp = [0u8; 16];
247+
tmp[..8].copy_from_slice(&self.0[2].to_le_bytes());
248+
tmp[8..].copy_from_slice(&self.0[3].to_le_bytes());
249+
i128::from_le_bytes(tmp)
250+
}
251+
}

src/int/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@ use core::ops;
33
mod specialized_div_rem;
44

55
pub mod addsub;
6+
mod big;
67
pub mod leading_zeros;
78
pub mod mul;
89
pub mod sdiv;
910
pub mod shift;
1011
pub mod udiv;
1112

13+
pub use big::{i256, u256};
1214
pub use leading_zeros::__clzsi2;
1315

1416
public_test_dep! {

testcrate/tests/big.rs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
use compiler_builtins::int::{i256, u256, HInt, MinInt};
2+
3+
const LOHI_SPLIT: u128 = 0xaaaaaaaaaaaaaaaaffffffffffffffff;
4+
5+
/// Print a `u256` as hex since we can't add format implementations
6+
fn hexu(v: u256) -> String {
7+
format!(
8+
"0x{:016x}{:016x}{:016x}{:016x}",
9+
v.0[3], v.0[2], v.0[1], v.0[0]
10+
)
11+
}
12+
13+
#[test]
14+
fn widen_u128() {
15+
assert_eq!(u128::MAX.widen(), u256([u64::MAX, u64::MAX, 0, 0]));
16+
assert_eq!(
17+
LOHI_SPLIT.widen(),
18+
u256([u64::MAX, 0xaaaaaaaaaaaaaaaa, 0, 0])
19+
);
20+
}
21+
22+
#[test]
23+
fn widen_i128() {
24+
assert_eq!((-1i128).widen(), u256::MAX.signed());
25+
assert_eq!(
26+
(LOHI_SPLIT as i128).widen(),
27+
i256([u64::MAX, 0xaaaaaaaaaaaaaaaa, u64::MAX, u64::MAX])
28+
);
29+
assert_eq!((-1i128).zero_widen().unsigned(), (u128::MAX).widen());
30+
}
31+
32+
#[test]
33+
fn widen_mul_u128() {
34+
let tests = [
35+
(u128::MAX / 2, 2_u128, u256([u64::MAX - 1, u64::MAX, 0, 0])),
36+
(u128::MAX, 2_u128, u256([u64::MAX - 1, u64::MAX, 1, 0])),
37+
(u128::MAX, u128::MAX, u256([1, 0, u64::MAX - 1, u64::MAX])),
38+
(u128::MIN, u128::MIN, u256::ZERO),
39+
(1234, 0, u256::ZERO),
40+
(0, 1234, u256::ZERO),
41+
];
42+
43+
let mut errors = Vec::new();
44+
for (i, (a, b, exp)) in tests.iter().copied().enumerate() {
45+
let res = a.widen_mul(b);
46+
let res_z = a.zero_widen_mul(b);
47+
assert_eq!(res, res_z);
48+
if res != exp {
49+
errors.push((i, a, b, exp, res));
50+
}
51+
}
52+
53+
for (i, a, b, exp, res) in &errors {
54+
eprintln!(
55+
"FAILURE ({i}): {a:#034x} * {b:#034x} = {} got {}",
56+
hexu(*exp),
57+
hexu(*res)
58+
);
59+
}
60+
assert!(errors.is_empty());
61+
}

0 commit comments

Comments
 (0)