cairo_vm/hint_processor/builtin_hint_processor/vrf/
fq.rs

1//! Fq stands for "a finite field of q elements"
2
3use crate::{
4    hint_processor::builtin_hint_processor::uint256_utils::Uint256,
5    hint_processor::{
6        builtin_hint_processor::secp::bigint_utils::Uint512,
7        hint_processor_definition::HintReference,
8    },
9    math_utils::div_mod_unsigned,
10    serde::deserialize_program::ApTracking,
11    stdlib::{collections::HashMap, prelude::*},
12    types::errors::math_errors::MathError,
13    vm::{errors::hint_errors::HintError, vm_core::VirtualMachine},
14};
15use num_bigint::BigUint;
16use num_integer::div_rem;
17use num_traits::{One, Zero};
18
19/// Implements hint:
20/// ```python
21/// def split(num: int, num_bits_shift: int, length: int):
22///     a = []
23///     for _ in range(length):
24///         a.append( num & ((1 << num_bits_shift) - 1) )
25///         num = num >> num_bits_shift
26///     return tuple(a)
27///
28/// def pack(z, num_bits_shift: int) -> int:
29///     limbs = (z.low, z.high)
30///     return sum(limb << (num_bits_shift * i) for i, limb in enumerate(limbs))
31///
32/// def pack_extended(z, num_bits_shift: int) -> int:
33///     limbs = (z.d0, z.d1, z.d2, z.d3)
34///     return sum(limb << (num_bits_shift * i) for i, limb in enumerate(limbs))
35///
36/// x = pack_extended(ids.x, num_bits_shift = 128)
37/// div = pack(ids.div, num_bits_shift = 128)
38///
39/// quotient, remainder = divmod(x, div)
40///
41/// quotient_split = split(quotient, num_bits_shift=128, length=4)
42///
43/// ids.quotient.d0 = quotient_split[0]
44/// ids.quotient.d1 = quotient_split[1]
45/// ids.quotient.d2 = quotient_split[2]
46/// ids.quotient.d3 = quotient_split[3]
47///
48/// remainder_split = split(remainder, num_bits_shift=128, length=2)
49/// ids.remainder.low = remainder_split[0]
50/// ids.remainder.high = remainder_split[1]
51/// ```
52pub fn uint512_unsigned_div_rem(
53    vm: &mut VirtualMachine,
54    ids_data: &HashMap<String, HintReference>,
55    ap_tracking: &ApTracking,
56) -> Result<(), HintError> {
57    let x = Uint512::from_var_name("x", vm, ids_data, ap_tracking)?.pack();
58    let div = Uint256::from_var_name("div", vm, ids_data, ap_tracking)?.pack();
59
60    // Main logic:
61    //  quotient, remainder = divmod(x, div)
62    if div.is_zero() {
63        return Err(MathError::DividedByZero.into());
64    }
65    let (quotient, remainder) = div_rem(x, div);
66
67    Uint512::from(&quotient).insert_from_var_name("quotient", vm, ids_data, ap_tracking)?;
68    Uint256::from(&remainder).insert_from_var_name("remainder", vm, ids_data, ap_tracking)
69}
70
71/// Implements hint:
72/// ```python
73/// from starkware.python.math_utils import div_mod
74///
75/// def split(a: int):
76/// return (a & ((1 << 128) - 1), a >> 128)
77///
78/// def pack(z, num_bits_shift: int) -> int:
79/// limbs = (z.low, z.high)
80/// return sum(limb << (num_bits_shift * i) for i, limb in enumerate(limbs))
81///
82/// a = pack(ids.a, 128)
83/// b = pack(ids.b, 128)
84/// p = pack(ids.p, 128)
85/// # For python3.8 and above the modular inverse can be computed as follows:
86/// # b_inverse_mod_p = pow(b, -1, p)
87/// # Instead we use the python3.7-friendly function div_mod from starkware.python.math_utils
88/// b_inverse_mod_p = div_mod(1, b, p)
89///
90/// b_inverse_mod_p_split = split(b_inverse_mod_p)
91///
92/// ids.b_inverse_mod_p.low = b_inverse_mod_p_split[0]
93/// ids.b_inverse_mod_p.high = b_inverse_mod_p_split[1]
94/// ```
95pub fn inv_mod_p_uint256(
96    vm: &mut VirtualMachine,
97    ids_data: &HashMap<String, HintReference>,
98    ap_tracking: &ApTracking,
99) -> Result<(), HintError> {
100    // 'a' is not used here or in following hints, so we skip it
101    let b = Uint256::from_var_name("b", vm, ids_data, ap_tracking)?.pack();
102    let p = Uint256::from_var_name("p", vm, ids_data, ap_tracking)?.pack();
103
104    // Main logic:
105    let b_inverse_mod_p = div_mod_unsigned(&BigUint::one(), &b, &p)?;
106
107    let res = Uint256::from(&b_inverse_mod_p);
108    res.insert_from_var_name("b_inverse_mod_p", vm, ids_data, ap_tracking)
109}
110
111#[cfg(test)]
112mod tests {
113    use super::*;
114    use crate::any_box;
115    use crate::hint_processor::builtin_hint_processor::builtin_hint_processor_definition::BuiltinHintProcessor;
116    use crate::hint_processor::builtin_hint_processor::builtin_hint_processor_definition::HintProcessorData;
117    use crate::hint_processor::builtin_hint_processor::hint_code;
118    use crate::hint_processor::hint_processor_definition::HintProcessorLogic;
119    use crate::types::errors::math_errors::MathError;
120    use crate::utils::test_utils::*;
121    use assert_matches::assert_matches;
122
123    #[cfg(target_arch = "wasm32")]
124    use wasm_bindgen_test::*;
125
126    #[test]
127    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
128    fn test_uint512_unsigned_div_rem_ok() {
129        let hint_code = hint_code::UINT512_UNSIGNED_DIV_REM;
130        let mut vm = vm_with_range_check!();
131
132        vm.segments = segments![
133            ((1, 0), 2363463),
134            ((1, 1), 566795),
135            ((1, 2), 8760799),
136            ((1, 3), 62362634),
137            ((1, 4), 8340843),
138            ((1, 5), 124152)
139        ];
140        // Create hint_data
141        let ids_data =
142            non_continuous_ids_data![("x", 0), ("div", 4), ("quotient", 6), ("remainder", 10)];
143        assert_matches!(
144            run_hint!(vm, ids_data, hint_code, exec_scopes_ref!()),
145            Ok(())
146        );
147        //Check hint memory inserts
148        check_memory![
149            vm.segments.memory,
150            // quotient
151            ((1, 6), 158847186690949537631480225217589612243),
152            ((1, 7), 105056890940778813909974456334651647691),
153            ((1, 8), 502),
154            ((1, 9), 0),
155            // remainder
156            ((1, 10), ("235556430256711128858231095164527378198", 10)),
157            ((1, 11), 83573),
158        ];
159    }
160
161    #[test]
162    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
163    fn test_uint512_unsigned_div_rem_div_is_zero() {
164        let hint_code = hint_code::UINT512_UNSIGNED_DIV_REM;
165        let mut vm = vm_with_range_check!();
166
167        vm.segments = segments![
168            ((1, 0), 2363463),
169            ((1, 1), 566795),
170            ((1, 2), 8760799),
171            ((1, 3), 62362634),
172            ((1, 4), 0),
173            ((1, 5), 0)
174        ];
175        // Create hint_data
176        let ids_data =
177            non_continuous_ids_data![("x", 0), ("div", 4), ("quotient", 6), ("remainder", 10)];
178        assert_matches!(
179            run_hint!(vm, ids_data, hint_code, exec_scopes_ref!()),
180            Err(HintError::Math(MathError::DividedByZero))
181        );
182    }
183
184    #[test]
185    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
186    fn test_inv_mod_p_uint256_ok() {
187        let hint_code = hint_code::INV_MOD_P_UINT256;
188        let mut vm = vm_with_range_check!();
189
190        vm.segments = segments![
191            ((1, 0), 2363463),
192            ((1, 1), 566795),
193            ((1, 2), 8760799),
194            ((1, 3), 62362634),
195            ((1, 4), 8340842),
196            ((1, 5), 124152)
197        ];
198        // Create hint_data
199        let ids_data =
200            non_continuous_ids_data![("a", 0), ("b", 2), ("p", 4), ("b_inverse_mod_p", 6)];
201        assert_matches!(
202            run_hint!(vm, ids_data, hint_code, exec_scopes_ref!()),
203            Ok(())
204        );
205        //Check hint memory inserts
206        check_memory![
207            vm.segments.memory,
208            // b_inverse_mod_p
209            ((1, 6), ("320134454404400884259649806286603992559", 10)),
210            ((1, 7), 106713),
211        ];
212    }
213
214    #[test]
215    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
216    fn test_inv_mod_p_uint256_igcdex_not_1() {
217        let hint_code = hint_code::INV_MOD_P_UINT256;
218        let mut vm = vm_with_range_check!();
219
220        vm.segments = segments![
221            ((1, 0), 2363463),
222            ((1, 1), 566795),
223            ((1, 2), 1),
224            ((1, 3), 1),
225            ((1, 4), 1),
226            ((1, 5), 1)
227        ];
228        // Create hint_data
229        let ids_data =
230            non_continuous_ids_data![("a", 0), ("b", 2), ("p", 4), ("b_inverse_mod_p", 6)];
231        assert_matches!(
232            run_hint!(vm, ids_data, hint_code, exec_scopes_ref!()),
233            Err(HintError::Math(MathError::DivModIgcdexNotZero(_)))
234        );
235    }
236}