tasm_lib/arithmetic/u160/
div_mod.rs1use triton_vm::prelude::*;
2
3use crate::arithmetic;
4use crate::prelude::*;
5
6#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)]
11pub struct DivMod;
12
13impl DivMod {
14 pub const DIVISION_BY_ZERO_ERROR_ID: i128 = 590;
15 pub const REMAINDER_TOO_BIG: i128 = 591;
16 pub const EULER_EQUATION_ERROR: i128 = 592;
17}
18
19impl BasicSnippet for DivMod {
20 fn parameters(&self) -> Vec<(DataType, String)> {
21 vec![
22 (DataType::U160, "numerator".to_owned()),
23 (DataType::U160, "denominator".to_owned()),
24 ]
25 }
26
27 fn return_values(&self) -> Vec<(DataType, String)> {
28 vec![
29 (DataType::U160, "quotient".to_owned()),
30 (DataType::U160, "remainder".to_owned()),
31 ]
32 }
33
34 fn entrypoint(&self) -> String {
35 "tasmlib_arithmetic_u160_div_mod".to_string()
36 }
37
38 fn code(&self, library: &mut Library) -> Vec<LabelledInstruction> {
39 let lt = library.import(Box::new(arithmetic::u160::lt::Lt));
40 let safe_add = library.import(Box::new(arithmetic::u160::safe_add::SafeAdd));
41 let safe_mul = library.import(Box::new(arithmetic::u160::safe_mul::SafeMul));
42 let compare = DataType::U160.compare();
43
44 let remainder_pointer = library.kmalloc(5);
45 let quotient_pointer = library.kmalloc(5);
46
47 let divine_u160 = triton_asm!(
48 divine 5
49 );
50 let input_sanitation = triton_asm!(
51 dup 4
52 pop_count
53 dup 4
56 pop_count
57 dup 4
60 pop_count
61 dup 4
64 pop_count
65 dup 4
68 pop_count
69 pop 5
72 );
74
75 triton_asm!(
76 {self.entrypoint()}:
79 {&triton_asm![dup 4; 5]}
84 {&triton_asm![push 0; 5]}
85 {&compare}
88 push 0
91 eq
92 assert error_id {Self::DIVISION_BY_ZERO_ERROR_ID}
93 {&divine_u160}
98 {&input_sanitation}
99 {&divine_u160}
100 {&input_sanitation}
101 {&triton_asm![dup 14; 5]}
106 {&triton_asm![dup 9; 5]}
107 call {lt}
110 assert error_id {Self::REMAINDER_TOO_BIG}
113 push {remainder_pointer.write_address()}
118 write_mem 5
119 pop 1
120 {&triton_asm![dup 4; 5]}
123 push {quotient_pointer.write_address()}
126 write_mem 5
127 pop 1
128 call {safe_mul}
131 push {remainder_pointer.read_address()}
134 read_mem 5
135 pop 1
136 call {safe_add}
139 {&compare}
142 assert error_id {Self::EULER_EQUATION_ERROR}
145 push {quotient_pointer.read_address()}
150 read_mem 5
151 pop 1
152 push {remainder_pointer.read_address()}
155 read_mem 5
156 pop 1
157 return
160 )
161 }
162}
163
164#[cfg(test)]
165mod tests {
166 use num::BigUint;
167 use num::Integer;
168 use num_traits::Zero;
169 use rand::rngs::StdRng;
170
171 use super::*;
172 use crate::arithmetic::u160::u128_to_u160;
173 use crate::library::STATIC_MEMORY_FIRST_ADDRESS;
174 use crate::test_helpers::negative_test;
175 use crate::test_prelude::Algorithm;
176 use crate::test_prelude::*;
177
178 #[macro_rules_attr::apply(test)]
179 fn std_test() {
180 ShadowedAlgorithm::new(DivMod).test()
181 }
182
183 #[macro_rules_attr::apply(test)]
184 fn too_big_remainder() {
185 let numerator = 10;
186 let denominator = 3;
187 let stack = DivMod.stack(u128_to_u160(numerator), u128_to_u160(denominator));
188
189 let too_big_remainder = 4;
190 let too_small_quotient = 2;
191 let nondeterminism = DivMod::nondeterminism(
192 u128_to_u160(too_small_quotient).map(|x| bfe!(x)),
193 u128_to_u160(too_big_remainder).map(|x| bfe!(x)),
194 );
195
196 test_assertion_failure(
197 &ShadowedAlgorithm::new(DivMod),
198 InitVmState {
199 nondeterminism,
200 stack,
201 ..Default::default()
202 },
203 &[DivMod::REMAINDER_TOO_BIG],
204 )
205 }
206
207 #[macro_rules_attr::apply(test)]
208 fn euler_equation_error() {
209 let numerator = 1u128 << 99;
210 let denominator = 1u128 << 45;
211 let stack = DivMod.stack(u128_to_u160(numerator), u128_to_u160(denominator));
212
213 let correct_remainder = 0;
214 let bad_quotient = 1u128 << 43;
215 let nondeterminism = DivMod::nondeterminism(
216 u128_to_u160(bad_quotient).map(|x| bfe!(x)),
217 u128_to_u160(correct_remainder).map(|x| bfe!(x)),
218 );
219
220 test_assertion_failure(
221 &ShadowedAlgorithm::new(DivMod),
222 InitVmState {
223 nondeterminism,
224 stack,
225 ..Default::default()
226 },
227 &[DivMod::EULER_EQUATION_ERROR],
228 )
229 }
230
231 #[macro_rules_attr::apply(test)]
232 fn division_by_zero() {
233 let numerator = u128_to_u160(52);
234 let denominator = u128_to_u160(0);
235 let state = DivMod.prepare_state(numerator, denominator);
236 test_assertion_failure(
237 &ShadowedAlgorithm::new(DivMod),
238 state.into(),
239 &[DivMod::DIVISION_BY_ZERO_ERROR_ID],
240 );
241 }
242
243 #[macro_rules_attr::apply(test)]
244 fn bad_encoding() {
245 let not_u32 = bfe!(1u64 << 32);
246 let valid_u32 = bfe!(14);
247 let mut nondeterminism = DivMod::nondeterminism([valid_u32; 5], [valid_u32; 5]);
248 let stack = DivMod.stack(u128_to_u160(4), u128_to_u160(4));
249
250 for i in 0..10 {
251 nondeterminism.individual_tokens[i] = not_u32;
252 negative_test(
253 &ShadowedAlgorithm::new(DivMod),
254 InitVmState {
255 nondeterminism: nondeterminism.clone(),
256 stack: stack.clone(),
257 ..Default::default()
258 },
259 &[InstructionError::OpStackError(
260 OpStackError::FailedU32Conversion(not_u32),
261 )],
262 );
263
264 nondeterminism.individual_tokens[i] = valid_u32;
265 }
266 }
267
268 impl DivMod {
269 fn nondeterminism(
270 quotient: [BFieldElement; 5],
271 remainder: [BFieldElement; 5],
272 ) -> NonDeterminism {
273 let individual_tokens = [quotient, remainder].concat();
274
275 NonDeterminism {
276 individual_tokens,
277 digests: Default::default(),
278 ram: Default::default(),
279 }
280 }
281
282 fn stack(&self, numerator: [u32; 5], denominator: [u32; 5]) -> Vec<BFieldElement> {
283 let mut stack = self.init_stack_for_isolated_run();
284 push_encodable(&mut stack, &numerator);
285 push_encodable(&mut stack, &denominator);
286
287 stack
288 }
289
290 fn prepare_state(
291 &self,
292 numerator: [u32; 5],
293 denominator: [u32; 5],
294 ) -> AlgorithmInitialState {
295 let stack = self.stack(numerator, denominator);
296
297 let numerator: BigUint = BigUint::new(numerator.to_vec());
298 let denominator: BigUint = BigUint::new(denominator.to_vec());
299
300 let (quotient, remainder) = if denominator.is_zero() {
301 (0u32.into(), 0u32.into())
305 } else {
306 numerator.div_rem(&denominator)
307 };
308
309 let mut quotient = quotient.to_u32_digits();
310 quotient.resize(5, 0);
311 quotient.reverse();
312 let quotient: [u32; 5] = quotient.try_into().unwrap();
313
314 let mut remainder = remainder.to_u32_digits();
315 remainder.resize(5, 0);
316 remainder.reverse();
317 let remainder: [u32; 5] = remainder.try_into().unwrap();
318
319 let nondeterminism = Self::nondeterminism(
320 quotient.encode().try_into().unwrap(),
321 remainder.encode().try_into().unwrap(),
322 );
323
324 AlgorithmInitialState {
325 stack,
326 nondeterminism,
327 }
328 }
329
330 fn edge_case_points() -> Vec<[u32; 5]> {
331 [2, 1 << 32, 1 << 96, u128::MAX]
332 .into_iter()
333 .flat_map(|p| [p.checked_sub(1), Some(p), p.checked_add(1)])
334 .flatten()
335 .map(u128_to_u160)
336 .chain([[u32::MAX; 5]])
337 .collect()
338 }
339 }
340
341 impl Algorithm for DivMod {
342 fn rust_shadow(
343 &self,
344 stack: &mut Vec<BFieldElement>,
345 memory: &mut HashMap<BFieldElement, BFieldElement>,
346 nondeterminism: &NonDeterminism,
347 ) -> Result<(), RustShadowError> {
348 let denominator: [u32; 5] = pop_encodable(stack)?;
349 let denominator: BigUint = BigUint::new(denominator.to_vec());
350 if denominator.is_zero() {
351 return Err(RustShadowError::Other);
352 }
353
354 let numerator: [u32; 5] = pop_encodable(stack)?;
355 let numerator: BigUint = BigUint::new(numerator.to_vec());
356
357 let quotient = &nondeterminism.individual_tokens[0..5];
358 let mut quotient: [u32; 5] = *TasmObject::decode_iter(&mut quotient.iter().cloned())
359 .map_err(|_| RustShadowError::DecodingError)?;
360 quotient.reverse();
361 let quotient: BigUint = BigUint::new(quotient.to_vec());
362
363 let remainder = &nondeterminism.individual_tokens[5..10];
364 let mut remainder: [u32; 5] = *TasmObject::decode_iter(&mut remainder.iter().cloned())
365 .map_err(|_| RustShadowError::DecodingError)?;
366 remainder.reverse();
367 let remainder: BigUint = BigUint::new(remainder.to_vec());
368
369 if remainder >= denominator {
370 return Err(RustShadowError::Other);
371 }
372 if numerator != quotient.clone() * denominator + remainder.clone() {
373 return Err(RustShadowError::Other);
374 }
375
376 let mut quotient = quotient.to_u32_digits();
377 quotient.resize(5, 0);
378 let quotient: [u32; 5] = quotient.try_into().map_err(|_| RustShadowError::Other)?;
379
380 let mut remainder = remainder.to_u32_digits();
381 remainder.resize(5, 0);
382 let remainder: [u32; 5] = remainder.try_into().map_err(|_| RustShadowError::Other)?;
383
384 encode_to_memory(memory, STATIC_MEMORY_FIRST_ADDRESS - bfe!(4), &remainder);
386 encode_to_memory(memory, STATIC_MEMORY_FIRST_ADDRESS - bfe!(9), "ient);
387
388 push_encodable(stack, "ient);
389 push_encodable(stack, &remainder);
390 Ok(())
391 }
392
393 fn pseudorandom_initial_state(
394 &self,
395 seed: [u8; 32],
396 _bench_case: Option<crate::test_prelude::BenchmarkCase>,
397 ) -> AlgorithmInitialState {
398 let mut rng = StdRng::from_seed(seed);
399 let numerator: [u32; 5] = rng.random();
400 let mut denominator: [u32; 5] = rng.random();
401
402 let denominator_div = rng.next_u32();
404 denominator[4] /= denominator_div;
405
406 self.prepare_state(numerator, denominator)
407 }
408
409 fn corner_case_initial_states(&self) -> Vec<AlgorithmInitialState> {
410 let edge_case_points = Self::edge_case_points();
411
412 edge_case_points
413 .iter()
414 .cartesian_product(&edge_case_points)
415 .map(|(l, r)| self.prepare_state(*l, *r))
416 .collect()
417 }
418 }
419}
420
421#[cfg(test)]
422mod benches {
423 use super::*;
424 use crate::test_prelude::*;
425
426 #[macro_rules_attr::apply(test)]
427 fn benchmark() {
428 ShadowedAlgorithm::new(DivMod).bench()
429 }
430}