tasm_lib/arithmetic/i128/
lt.rs

1use triton_vm::prelude::*;
2
3use crate::arithmetic;
4use crate::prelude::*;
5
6/// Less-than operator for `i128`
7///
8/// # Behavior
9///
10/// ```text
11/// BEFORE: _ [rhs: i128] [lhs: i128]
12/// AFTER:  _ (lhs < rhs)
13/// ```
14///
15/// # Preconditions
16///
17///  - `rhs` and `lhs` consists of 4 `u32`s.
18///
19/// # Postconditions
20///
21///  - `result = lhs < rhs` is `true` or `false`.
22///
23/// # Panics
24///
25///  - If preconditions are not met.
26#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
27pub struct Lt;
28
29impl BasicSnippet for Lt {
30    fn inputs(&self) -> Vec<(DataType, String)> {
31        ["rhs", "lhs"]
32            .map(|s| (DataType::I128, s.to_string()))
33            .to_vec()
34    }
35
36    fn outputs(&self) -> Vec<(DataType, String)> {
37        vec![(DataType::Bool, "result".to_string())]
38    }
39
40    fn entrypoint(&self) -> String {
41        "tasmlib_arithmetic_i128_lt".to_owned()
42    }
43
44    fn code(&self, library: &mut Library) -> Vec<LabelledInstruction> {
45        let entrypoint = self.entrypoint();
46        let u128_lt = library.import(Box::new(arithmetic::u128::lt::Lt));
47
48        const TWO_POW_31: u32 = 1 << 31;
49        let flip_msbs = triton_asm!(
50            // _ [rhs: i128] [lhs: i128]
51            pick 3
52            push {TWO_POW_31}
53            xor
54            place 3
55
56            pick 7
57            push {TWO_POW_31}
58            xor
59            place 7
60            // _ [rhs+(1<<127): u128] [rhs+(1<<127): u128]
61        );
62
63        triton_asm!(
64            {entrypoint}:
65                // _ [rhs] [lhs]
66
67                /* Flip most-significant bit, then do u128-lt. */
68
69                {&flip_msbs}
70                // _ [rhs'] [lhs']
71
72                call {u128_lt}
73                // _ (rhs > lhs)
74
75                return
76        )
77    }
78}
79
80#[cfg(test)]
81mod tests {
82    use std::collections::HashSet;
83
84    use triton_vm::error::InstructionError;
85    use triton_vm::error::OpStackError;
86
87    use super::*;
88    use crate::test_helpers::negative_test;
89    use crate::test_prelude::*;
90
91    impl Closure for Lt {
92        type Args = (i128, i128);
93
94        fn rust_shadow(&self, stack: &mut Vec<BFieldElement>) {
95            let (rhs, lhs) = pop_encodable::<Self::Args>(stack);
96            push_encodable(stack, &(lhs < rhs));
97        }
98
99        fn pseudorandom_args(&self, seed: [u8; 32], _: Option<BenchmarkCase>) -> Self::Args {
100            StdRng::from_seed(seed).random()
101        }
102
103        fn corner_case_args(&self) -> Vec<Self::Args> {
104            let points_with_plus_minus_one = [
105                i128::MIN,
106                -(1 << 96),
107                -(1 << 64),
108                -(1 << 32),
109                -1,
110                1,
111                1 << 32,
112                1 << 64,
113                1 << 96,
114                1 << 126,
115                i128::MAX,
116            ]
117            .into_iter()
118            .flat_map(|p| [p.checked_sub(1), Some(p), p.checked_add(1)])
119            .flatten()
120            .collect::<HashSet<_>>()
121            .into_iter()
122            .collect_vec();
123
124            points_with_plus_minus_one
125                .iter()
126                .cartesian_product(&points_with_plus_minus_one)
127                .map(|(&l, &r)| (l, r))
128                .collect()
129        }
130    }
131
132    #[test]
133    fn i128_lt_proptest() {
134        ShadowedClosure::new(Lt).test();
135    }
136
137    #[proptest]
138    fn input_not_u32_words_negative_test(
139        #[strategy(0usize..8)] stack_index_bad_word: usize,
140        #[strategy(arb())]
141        #[filter(#bad_word.value() > u32::MAX as u64)]
142        bad_word: BFieldElement,
143    ) {
144        fn init_state_not_u32_words(
145            stack_index_bad_word: usize,
146            bad_word: BFieldElement,
147        ) -> InitVmState {
148            let mut stack = Lt.set_up_test_stack(rand::random());
149            let last_elem_index = stack.len() - 1;
150            stack[last_elem_index - stack_index_bad_word] = bad_word;
151
152            InitVmState::with_stack(stack)
153        }
154
155        negative_test(
156            &ShadowedClosure::new(Lt),
157            init_state_not_u32_words(stack_index_bad_word, bad_word),
158            &[InstructionError::OpStackError(
159                OpStackError::FailedU32Conversion(bad_word),
160            )],
161        );
162    }
163
164    #[test]
165    fn i128_lt_unit_test_min_max() {
166        let init_stack = Lt.set_up_test_stack((i128::MAX, i128::MIN));
167        let mut expected_end_stack = Lt.init_stack_for_isolated_run();
168        push_encodable(&mut expected_end_stack, &true);
169
170        let stdin = &[];
171        test_rust_equivalence_given_complete_state(
172            &ShadowedClosure::new(Lt),
173            &init_stack,
174            stdin,
175            &NonDeterminism::default(),
176            &None,
177            Some(&expected_end_stack),
178        );
179    }
180
181    #[test]
182    fn i128_lt_unit_test_zero_zero() {
183        let init_stack = Lt.set_up_test_stack((0, 0));
184        let mut expected_end_stack = Lt.init_stack_for_isolated_run();
185        push_encodable(&mut expected_end_stack, &false);
186
187        let stdin = &[];
188        test_rust_equivalence_given_complete_state(
189            &ShadowedClosure::new(Lt),
190            &init_stack,
191            stdin,
192            &NonDeterminism::default(),
193            &None,
194            Some(&expected_end_stack),
195        );
196    }
197
198    #[proptest]
199    fn equal_elements_are_not_lt_prop(value: i128) {
200        let init_stack = Lt.set_up_test_stack((value, value));
201        let mut expected_end_stack = Lt.init_stack_for_isolated_run();
202        push_encodable(&mut expected_end_stack, &false);
203
204        let stdin = &[];
205        test_rust_equivalence_given_complete_state(
206            &ShadowedClosure::new(Lt),
207            &init_stack,
208            stdin,
209            &NonDeterminism::default(),
210            &None,
211            Some(&expected_end_stack),
212        );
213    }
214}
215
216#[cfg(test)]
217mod benches {
218    use super::*;
219    use crate::test_prelude::*;
220
221    #[test]
222    fn benchmark() {
223        ShadowedClosure::new(Lt).bench();
224    }
225}