Skip to main content

javm_guest_tests/tests/
arithmetic.rs

1//! Arithmetic test vectors: add, sub, mul, div, rem, wide multiply.
2//!
3//! Each operation has two faces:
4//!   - `<name>(input: &[u8], output: &mut [u8]) -> usize`: a byte-level
5//!     helper, the legacy unit-test entry point.
6//!   - `<name>_suite() -> u64`: runs the helper over a baked corpus
7//!     of `(a, b)` pairs and XOR-folds the u64 results into a single
8//!     fingerprint. Used by the three-way conformance harness.
9
10use crate::{read_u64, write_u64};
11
12/// Inputs reused by every binary u64 op (add, sub, mul, mul_upper_*,
13/// AND, OR, XOR). Mix of identity, overflow boundaries, and arbitrary
14/// patterns.
15pub(crate) const BINARY_U64_CASES: &[(u64, u64)] = &[
16    (0, 0),
17    (1, 2),
18    (1, 1),
19    (u64::MAX, 1),
20    (u64::MAX, u64::MAX),
21    (0xDEAD_BEEF_DEAD_BEEF, 0x0123_4567_89AB_CDEF),
22    (0x8000_0000_0000_0000, 0x8000_0000_0000_0000),
23];
24
25/// Inputs for the division/remainder ops (signed and unsigned).
26/// Includes the `÷0` and `i64::MIN ÷ -1` edge cases.
27pub(crate) const DIV_U64_CASES: &[(u64, u64)] = &[
28    (10, 3),
29    (u64::MAX, 1),
30    (u64::MAX, 2),
31    (100, 0),
32    (0, 7),
33    (i64::MIN as u64, (-1i64) as u64),
34];
35
36/// Run a binary u64-in / u64-out op over a corpus, XOR-fold results.
37pub(crate) fn run_binary_u64(cases: &[(u64, u64)], op: fn(&[u8], &mut [u8]) -> usize) -> u64 {
38    let mut acc = 0u64;
39    let mut input = [0u8; 16];
40    let mut output = [0u8; 8];
41    for (a, b) in cases {
42        input[0..8].copy_from_slice(&a.to_le_bytes());
43        input[8..16].copy_from_slice(&b.to_le_bytes());
44        let len = op(&input, &mut output);
45        debug_assert!(len == 8);
46        acc ^= u64::from_le_bytes(output);
47    }
48    acc
49}
50
51// -- Byte-level helpers --------------------------------------------------------
52
53pub fn add_u64(input: &[u8], output: &mut [u8]) -> usize {
54    let (mut off, mut out) = (0, 0);
55    let a = read_u64(input, &mut off);
56    let b = read_u64(input, &mut off);
57    write_u64(output, &mut out, a.wrapping_add(b));
58    out
59}
60
61pub fn sub_u64(input: &[u8], output: &mut [u8]) -> usize {
62    let (mut off, mut out) = (0, 0);
63    let a = read_u64(input, &mut off);
64    let b = read_u64(input, &mut off);
65    write_u64(output, &mut out, a.wrapping_sub(b));
66    out
67}
68
69pub fn mul_u64(input: &[u8], output: &mut [u8]) -> usize {
70    let (mut off, mut out) = (0, 0);
71    let a = read_u64(input, &mut off);
72    let b = read_u64(input, &mut off);
73    write_u64(output, &mut out, a.wrapping_mul(b));
74    out
75}
76
77pub fn mul_upper_uu(input: &[u8], output: &mut [u8]) -> usize {
78    let (mut off, mut out) = (0, 0);
79    let a = read_u64(input, &mut off);
80    let b = read_u64(input, &mut off);
81    let hi = ((a as u128).wrapping_mul(b as u128) >> 64) as u64;
82    write_u64(output, &mut out, hi);
83    out
84}
85
86pub fn mul_upper_ss(input: &[u8], output: &mut [u8]) -> usize {
87    let (mut off, mut out) = (0, 0);
88    let a = read_u64(input, &mut off) as i64;
89    let b = read_u64(input, &mut off) as i64;
90    let hi = ((a as i128).wrapping_mul(b as i128) >> 64) as u64;
91    write_u64(output, &mut out, hi);
92    out
93}
94
95pub fn div_u64(input: &[u8], output: &mut [u8]) -> usize {
96    let (mut off, mut out) = (0, 0);
97    let a = read_u64(input, &mut off);
98    let b = read_u64(input, &mut off);
99    let result = a.checked_div(b).unwrap_or(u64::MAX);
100    write_u64(output, &mut out, result);
101    out
102}
103
104pub fn rem_u64(input: &[u8], output: &mut [u8]) -> usize {
105    let (mut off, mut out) = (0, 0);
106    let a = read_u64(input, &mut off);
107    let b = read_u64(input, &mut off);
108    let result = if b == 0 { a } else { a % b };
109    write_u64(output, &mut out, result);
110    out
111}
112
113pub fn div_s64(input: &[u8], output: &mut [u8]) -> usize {
114    let (mut off, mut out) = (0, 0);
115    let a = read_u64(input, &mut off) as i64;
116    let b = read_u64(input, &mut off) as i64;
117    let result = if b == 0 {
118        -1i64 as u64
119    } else if a == i64::MIN && b == -1 {
120        a as u64 // overflow: return a unchanged
121    } else {
122        (a / b) as u64
123    };
124    write_u64(output, &mut out, result);
125    out
126}
127
128pub fn rem_s64(input: &[u8], output: &mut [u8]) -> usize {
129    let (mut off, mut out) = (0, 0);
130    let a = read_u64(input, &mut off) as i64;
131    let b = read_u64(input, &mut off) as i64;
132    let result = if b == 0 {
133        a as u64
134    } else if a == i64::MIN && b == -1 {
135        0u64
136    } else {
137        (a % b) as u64
138    };
139    write_u64(output, &mut out, result);
140    out
141}
142
143// -- Suites --------------------------------------------------------------------
144
145pub fn add_u64_suite() -> u64 {
146    run_binary_u64(BINARY_U64_CASES, add_u64)
147}
148pub fn sub_u64_suite() -> u64 {
149    run_binary_u64(BINARY_U64_CASES, sub_u64)
150}
151pub fn mul_u64_suite() -> u64 {
152    run_binary_u64(BINARY_U64_CASES, mul_u64)
153}
154pub fn mul_upper_uu_suite() -> u64 {
155    run_binary_u64(BINARY_U64_CASES, mul_upper_uu)
156}
157pub fn mul_upper_ss_suite() -> u64 {
158    run_binary_u64(BINARY_U64_CASES, mul_upper_ss)
159}
160pub fn div_u64_suite() -> u64 {
161    run_binary_u64(DIV_U64_CASES, div_u64)
162}
163pub fn rem_u64_suite() -> u64 {
164    run_binary_u64(DIV_U64_CASES, rem_u64)
165}
166pub fn div_s64_suite() -> u64 {
167    run_binary_u64(DIV_U64_CASES, div_s64)
168}
169pub fn rem_s64_suite() -> u64 {
170    run_binary_u64(DIV_U64_CASES, rem_s64)
171}