Skip to main content

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 parameters(&self) -> Vec<(DataType, String)> {
31        ["rhs", "lhs"]
32            .map(|s| (DataType::I128, s.to_string()))
33            .to_vec()
34    }
35
36    fn return_values(&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>) -> Result<(), RustShadowError> {
95            let (rhs, lhs) = pop_encodable::<Self::Args>(stack)?;
96            push_encodable(stack, &(lhs < rhs));
97            Ok(())
98        }
99
100        fn pseudorandom_args(&self, seed: [u8; 32], _: Option<BenchmarkCase>) -> Self::Args {
101            StdRng::from_seed(seed).random()
102        }
103
104        fn corner_case_args(&self) -> Vec<Self::Args> {
105            let points_with_plus_minus_one = [
106                i128::MIN,
107                -(1 << 96),
108                -(1 << 64),
109                -(1 << 32),
110                -1,
111                1,
112                1 << 32,
113                1 << 64,
114                1 << 96,
115                1 << 126,
116                i128::MAX,
117            ]
118            .into_iter()
119            .flat_map(|p| [p.checked_sub(1), Some(p), p.checked_add(1)])
120            .flatten()
121            .collect::<HashSet<_>>()
122            .into_iter()
123            .collect_vec();
124
125            points_with_plus_minus_one
126                .iter()
127                .cartesian_product(&points_with_plus_minus_one)
128                .map(|(&l, &r)| (l, r))
129                .collect()
130        }
131    }
132
133    #[macro_rules_attr::apply(test)]
134    fn i128_lt_proptest() {
135        ShadowedClosure::new(Lt).test();
136    }
137
138    #[macro_rules_attr::apply(proptest)]
139    fn input_not_u32_words_negative_test(
140        #[strategy(0usize..8)] stack_index_bad_word: usize,
141        #[strategy(arb())]
142        #[filter(#bad_word.value() > u32::MAX as u64)]
143        bad_word: BFieldElement,
144    ) {
145        fn init_state_not_u32_words(
146            stack_index_bad_word: usize,
147            bad_word: BFieldElement,
148        ) -> InitVmState {
149            let mut stack = Lt.set_up_test_stack(rand::random());
150            let last_elem_index = stack.len() - 1;
151            stack[last_elem_index - stack_index_bad_word] = bad_word;
152
153            InitVmState::with_stack(stack)
154        }
155
156        negative_test(
157            &ShadowedClosure::new(Lt),
158            init_state_not_u32_words(stack_index_bad_word, bad_word),
159            &[InstructionError::OpStackError(
160                OpStackError::FailedU32Conversion(bad_word),
161            )],
162        );
163    }
164
165    #[macro_rules_attr::apply(test)]
166    fn i128_lt_unit_test_min_max() {
167        let init_stack = Lt.set_up_test_stack((i128::MAX, i128::MIN));
168        let mut expected_end_stack = Lt.init_stack_for_isolated_run();
169        push_encodable(&mut expected_end_stack, &true);
170
171        let stdin = &[];
172        test_rust_equivalence_given_complete_state(
173            &ShadowedClosure::new(Lt),
174            &init_stack,
175            stdin,
176            &NonDeterminism::default(),
177            &None,
178            Some(&expected_end_stack),
179        );
180    }
181
182    #[macro_rules_attr::apply(test)]
183    fn i128_lt_unit_test_zero_zero() {
184        let init_stack = Lt.set_up_test_stack((0, 0));
185        let mut expected_end_stack = Lt.init_stack_for_isolated_run();
186        push_encodable(&mut expected_end_stack, &false);
187
188        let stdin = &[];
189        test_rust_equivalence_given_complete_state(
190            &ShadowedClosure::new(Lt),
191            &init_stack,
192            stdin,
193            &NonDeterminism::default(),
194            &None,
195            Some(&expected_end_stack),
196        );
197    }
198
199    #[macro_rules_attr::apply(proptest)]
200    fn equal_elements_are_not_lt_prop(value: i128) {
201        let init_stack = Lt.set_up_test_stack((value, value));
202        let mut expected_end_stack = Lt.init_stack_for_isolated_run();
203        push_encodable(&mut expected_end_stack, &false);
204
205        let stdin = &[];
206        test_rust_equivalence_given_complete_state(
207            &ShadowedClosure::new(Lt),
208            &init_stack,
209            stdin,
210            &NonDeterminism::default(),
211            &None,
212            Some(&expected_end_stack),
213        );
214    }
215}
216
217#[cfg(test)]
218mod benches {
219    use super::*;
220    use crate::test_prelude::*;
221
222    #[macro_rules_attr::apply(test)]
223    fn benchmark() {
224        ShadowedClosure::new(Lt).bench();
225    }
226}