Skip to content

Commit 5f4b31d

Browse files
committed
add types
1 parent 5ebdb17 commit 5f4b31d

File tree

1 file changed

+379
-0
lines changed

1 file changed

+379
-0
lines changed

ff/src/biginteger/types.rs

Lines changed: 379 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,379 @@
1+
/// Right-hand instruction input value used by zkVM instruction logic.
2+
///
3+
/// Captures the semantic signedness of the right operand at XLEN width.
4+
/// - `Unsigned(u64)`: operand is interpreted as an XLEN-bit unsigned word
5+
/// - `Signed(i64)`: operand is interpreted as an XLEN-bit two's-complement signed word
6+
///
7+
/// Helper methods provide width-aware projections to `u64`/`i64` and a
8+
/// canonical unsigned representation for lookup key construction.
9+
use allocative::Allocative;
10+
use ark_serialize::{
11+
CanonicalDeserialize, CanonicalSerialize, Compress, SerializationError, Valid, Validate,
12+
};
13+
14+
#[derive(Copy, Clone, Debug, PartialEq, Eq, Allocative)]
15+
pub enum U64OrI64 {
16+
Unsigned(u64),
17+
Signed(i64),
18+
}
19+
20+
#[derive(Copy, Clone, Debug, PartialEq, Eq, Allocative)]
21+
pub enum U128OrI128 {
22+
Unsigned(u128),
23+
Signed(i128),
24+
}
25+
26+
impl U64OrI64 {
27+
/// Return the value as an unsigned 64-bit word (XLEN=64 view).
28+
#[inline]
29+
pub fn as_u64(&self) -> u64 {
30+
match *self {
31+
U64OrI64::Unsigned(u) => u,
32+
U64OrI64::Signed(s) => s as u64,
33+
}
34+
}
35+
36+
/// Return the value as an unsigned 32-bit word (XLEN=32 view).
37+
#[inline]
38+
pub fn as_u32(&self) -> u32 {
39+
match *self {
40+
U64OrI64::Unsigned(u) => u as u32,
41+
U64OrI64::Signed(s) => s as u32,
42+
}
43+
}
44+
45+
/// Return the value as an unsigned 8-bit word (XLEN=8 view).
46+
#[inline]
47+
pub fn as_u8(&self) -> u8 {
48+
match *self {
49+
U64OrI64::Unsigned(u) => u as u8,
50+
U64OrI64::Signed(s) => s as u8,
51+
}
52+
}
53+
54+
/// Return the value as a signed 64-bit word (XLEN=64 view).
55+
#[inline]
56+
pub fn as_i64(&self) -> i64 {
57+
match *self {
58+
U64OrI64::Unsigned(u) => u as i64,
59+
U64OrI64::Signed(s) => s,
60+
}
61+
}
62+
63+
/// Return the value as a signed 32-bit word (XLEN=32 view).
64+
/// This is a truncating conversion.
65+
#[inline]
66+
pub fn as_i32(&self) -> i32 {
67+
match *self {
68+
U64OrI64::Unsigned(u) => u as i32,
69+
U64OrI64::Signed(s) => s as i32,
70+
}
71+
}
72+
73+
/// Return the value as a signed 8-bit word (XLEN=8 view).
74+
/// This is a truncating conversion.
75+
#[inline]
76+
pub fn as_i8(&self) -> i8 {
77+
match *self {
78+
U64OrI64::Unsigned(u) => u as i8,
79+
U64OrI64::Signed(s) => s as i8,
80+
}
81+
}
82+
83+
/// Return the value widened to i128.
84+
#[inline]
85+
pub fn as_i128(&self) -> i128 {
86+
match *self {
87+
U64OrI64::Unsigned(u) => u as i128,
88+
U64OrI64::Signed(s) => s as i128,
89+
}
90+
}
91+
92+
/// Return a canonical unsigned representation suitable for lookup keys.
93+
///
94+
/// This is the XLEN-masked view of the value, promoted to `u128`.
95+
#[inline]
96+
pub fn to_u128_lookup<const XLEN: usize>(&self) -> u128 {
97+
match XLEN {
98+
64 => self.as_u64() as u128,
99+
32 => self.as_u32() as u128,
100+
8 => self.as_u8() as u128,
101+
_ => panic!("{XLEN}-bit word size is unsupported"),
102+
}
103+
}
104+
}
105+
106+
impl U128OrI128 {
107+
#[inline]
108+
pub fn as_u128(&self) -> u128 {
109+
match *self {
110+
U128OrI128::Unsigned(u) => u,
111+
U128OrI128::Signed(s) => s as u128,
112+
}
113+
}
114+
115+
#[inline]
116+
pub fn as_i128(&self) -> i128 {
117+
match *self {
118+
U128OrI128::Unsigned(u) => u as i128,
119+
U128OrI128::Signed(s) => s,
120+
}
121+
}
122+
}
123+
124+
impl core::cmp::PartialOrd for U64OrI64 {
125+
#[inline]
126+
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
127+
Some(self.cmp(other))
128+
}
129+
}
130+
131+
impl core::cmp::Ord for U64OrI64 {
132+
#[inline]
133+
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
134+
match (self, other) {
135+
(U64OrI64::Unsigned(a), U64OrI64::Unsigned(b)) => a.cmp(b),
136+
(U64OrI64::Signed(a), U64OrI64::Signed(b)) => a.cmp(b),
137+
(U64OrI64::Unsigned(a), U64OrI64::Signed(b)) => {
138+
if *b < 0 {
139+
core::cmp::Ordering::Greater
140+
} else {
141+
a.cmp(&(*b as u64))
142+
}
143+
}
144+
(U64OrI64::Signed(a), U64OrI64::Unsigned(b)) => {
145+
if *a < 0 {
146+
core::cmp::Ordering::Less
147+
} else {
148+
(*a as u64).cmp(b)
149+
}
150+
}
151+
}
152+
}
153+
}
154+
155+
impl core::cmp::PartialOrd for U128OrI128 {
156+
#[inline]
157+
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
158+
Some(self.cmp(other))
159+
}
160+
}
161+
162+
impl core::cmp::Ord for U128OrI128 {
163+
#[inline]
164+
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
165+
match (self, other) {
166+
(U128OrI128::Unsigned(a), U128OrI128::Unsigned(b)) => a.cmp(b),
167+
(U128OrI128::Signed(a), U128OrI128::Signed(b)) => a.cmp(b),
168+
(U128OrI128::Unsigned(a), U128OrI128::Signed(b)) => {
169+
if *b < 0 {
170+
core::cmp::Ordering::Greater
171+
} else {
172+
a.cmp(&(*b as u128))
173+
}
174+
}
175+
(U128OrI128::Signed(a), U128OrI128::Unsigned(b)) => {
176+
if *a < 0 {
177+
core::cmp::Ordering::Less
178+
} else {
179+
(*a as u128).cmp(b)
180+
}
181+
}
182+
}
183+
}
184+
}
185+
186+
impl Default for U64OrI64 {
187+
fn default() -> Self {
188+
U64OrI64::Unsigned(0)
189+
}
190+
}
191+
192+
impl Default for U128OrI128 {
193+
fn default() -> Self {
194+
U128OrI128::Unsigned(0)
195+
}
196+
}
197+
198+
impl Valid for U64OrI64 {
199+
fn check(&self) -> Result<(), SerializationError> {
200+
Ok(())
201+
}
202+
}
203+
204+
impl Valid for U128OrI128 {
205+
fn check(&self) -> Result<(), SerializationError> {
206+
Ok(())
207+
}
208+
}
209+
210+
impl CanonicalSerialize for U64OrI64 {
211+
fn serialize_with_mode<W: std::io::Write>(
212+
&self,
213+
mut writer: W,
214+
compress: Compress,
215+
) -> Result<(), SerializationError> {
216+
match self {
217+
U64OrI64::Unsigned(u) => {
218+
0u8.serialize_with_mode(&mut writer, compress)?;
219+
u.serialize_with_mode(writer, compress)
220+
}
221+
U64OrI64::Signed(s) => {
222+
1u8.serialize_with_mode(&mut writer, compress)?;
223+
s.serialize_with_mode(writer, compress)
224+
}
225+
}
226+
}
227+
228+
fn serialized_size(&self, compress: Compress) -> usize {
229+
0u8.serialized_size(compress)
230+
+ match self {
231+
U64OrI64::Unsigned(u) => u.serialized_size(compress),
232+
U64OrI64::Signed(s) => s.serialized_size(compress),
233+
}
234+
}
235+
}
236+
237+
impl CanonicalDeserialize for U64OrI64 {
238+
fn deserialize_with_mode<R: std::io::Read>(
239+
mut reader: R,
240+
compress: Compress,
241+
_validate: Validate,
242+
) -> Result<Self, SerializationError> {
243+
let tag = u8::deserialize_with_mode(&mut reader, compress, Validate::No)?;
244+
match tag {
245+
0 => {
246+
let u = u64::deserialize_with_mode(reader, compress, Validate::No)?;
247+
Ok(U64OrI64::Unsigned(u))
248+
}
249+
1 => {
250+
let s = i64::deserialize_with_mode(reader, compress, Validate::No)?;
251+
Ok(U64OrI64::Signed(s))
252+
}
253+
_ => Err(SerializationError::InvalidData),
254+
}
255+
}
256+
}
257+
258+
impl CanonicalSerialize for U128OrI128 {
259+
fn serialize_with_mode<W: std::io::Write>(
260+
&self,
261+
mut writer: W,
262+
compress: Compress,
263+
) -> Result<(), SerializationError> {
264+
match self {
265+
U128OrI128::Unsigned(u) => {
266+
0u8.serialize_with_mode(&mut writer, compress)?;
267+
u.serialize_with_mode(writer, compress)
268+
}
269+
U128OrI128::Signed(s) => {
270+
1u8.serialize_with_mode(&mut writer, compress)?;
271+
s.serialize_with_mode(writer, compress)
272+
}
273+
}
274+
}
275+
276+
fn serialized_size(&self, compress: Compress) -> usize {
277+
0u8.serialized_size(compress)
278+
+ match self {
279+
U128OrI128::Unsigned(u) => u.serialized_size(compress),
280+
U128OrI128::Signed(s) => s.serialized_size(compress),
281+
}
282+
}
283+
}
284+
285+
impl CanonicalDeserialize for U128OrI128 {
286+
fn deserialize_with_mode<R: std::io::Read>(
287+
mut reader: R,
288+
compress: Compress,
289+
_validate: Validate,
290+
) -> Result<Self, SerializationError> {
291+
let tag = u8::deserialize_with_mode(&mut reader, compress, Validate::No)?;
292+
match tag {
293+
0 => {
294+
let u = u128::deserialize_with_mode(reader, compress, Validate::No)?;
295+
Ok(U128OrI128::Unsigned(u))
296+
}
297+
1 => {
298+
let s = i128::deserialize_with_mode(reader, compress, Validate::No)?;
299+
Ok(U128OrI128::Signed(s))
300+
}
301+
_ => Err(SerializationError::InvalidData),
302+
}
303+
}
304+
}
305+
306+
#[cfg(test)]
307+
/// Validate that specialized projections as_{u,i}{8,32,64} are equivalent to
308+
/// widening to i128 and narrowing back for both Unsigned and Signed variants
309+
/// under XLEN views 8, 32, and 64.
310+
mod tests {
311+
use super::U64OrI64 as RIV;
312+
use rand::Rng;
313+
314+
fn check_equivalence(v: RIV) {
315+
let i128_wide = v.as_i128();
316+
317+
// XLEN=8
318+
assert_eq!(i128_wide as u8, v.as_u8());
319+
assert_eq!(i128_wide as i8, v.as_i8());
320+
321+
// XLEN=32
322+
assert_eq!(i128_wide as u32, v.as_u32());
323+
assert_eq!(i128_wide as i32, v.as_i32());
324+
325+
// XLEN=64
326+
assert_eq!(i128_wide as u64, v.as_u64());
327+
assert_eq!(i128_wide as i64, v.as_i64());
328+
}
329+
330+
#[test]
331+
fn projections_match_i128_path_unsigned() {
332+
let cases: &[u64] = &[
333+
0,
334+
1,
335+
0x7F,
336+
0x80,
337+
0xFF,
338+
0x7FFF_FFFF,
339+
0x8000_0000,
340+
0xFFFF_FFFF,
341+
0x7FFF_FFFF_FFFF_FFFF,
342+
0x8000_0000_0000_0000,
343+
0xFFFF_FFFF_FFFF_FFFF,
344+
];
345+
for &u in cases {
346+
check_equivalence(RIV::Unsigned(u));
347+
}
348+
349+
let mut rng = rand::thread_rng();
350+
for _ in 0..100 {
351+
check_equivalence(RIV::Unsigned(rng.gen()));
352+
}
353+
}
354+
355+
#[test]
356+
fn projections_match_i128_path_signed() {
357+
let cases: &[i64] = &[
358+
0,
359+
1,
360+
-1,
361+
i64::MIN,
362+
i64::MAX,
363+
-128,
364+
127,
365+
-0x8000_0000,
366+
0x7FFF_FFFF,
367+
];
368+
for &s in cases {
369+
check_equivalence(RIV::Signed(s));
370+
}
371+
372+
let mut rng = rand::thread_rng();
373+
for _ in 0..100 {
374+
check_equivalence(RIV::Signed(rng.gen()));
375+
}
376+
}
377+
}
378+
379+

0 commit comments

Comments
 (0)