kimchi_stubs/arkworks/
pasta_fq.rs

1use crate::{arkworks::CamlBigInteger256, caml::caml_bytes_string::CamlBytesString};
2use ark_ff::{FftField, Field, One, PrimeField, UniformRand, Zero};
3use ark_poly::{EvaluationDomain, Radix2EvaluationDomain as Domain};
4use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
5use core::{
6    cmp::Ordering::{Equal, Greater, Less},
7    convert::{TryFrom, TryInto},
8    ops::Deref,
9};
10use mina_curves::pasta::{
11    fields::{fft::FpParameters, fq::FqParameters as Fq_params},
12    Fq,
13};
14use num_bigint::BigUint;
15use rand::rngs::StdRng;
16
17//
18// Fq <-> CamlFq
19//
20
21#[derive(Clone, Copy, ocaml_gen::CustomType)]
22pub struct CamlFq(pub Fq);
23
24unsafe impl<'a> ocaml::FromValue<'a> for CamlFq {
25    fn from_value(value: ocaml::Value) -> Self {
26        let x: ocaml::Pointer<Self> = ocaml::FromValue::from_value(value);
27        *x.as_ref()
28    }
29}
30
31impl CamlFq {
32    unsafe extern "C" fn caml_pointer_finalize(v: ocaml::Raw) {
33        let ptr = v.as_pointer::<Self>();
34        ptr.drop_in_place()
35    }
36
37    unsafe extern "C" fn ocaml_compare(x: ocaml::Raw, y: ocaml::Raw) -> i32 {
38        let x = x.as_pointer::<Self>();
39        let y = y.as_pointer::<Self>();
40        match x.as_ref().0.into_bigint().cmp(&y.as_ref().0.into_bigint()) {
41            core::cmp::Ordering::Less => -1,
42            core::cmp::Ordering::Equal => 0,
43            core::cmp::Ordering::Greater => 1,
44        }
45    }
46}
47
48ocaml::custom!(CamlFq {
49    finalize: CamlFq::caml_pointer_finalize,
50    compare: CamlFq::ocaml_compare,
51});
52
53//
54// Handy implementations
55//
56
57impl Deref for CamlFq {
58    type Target = Fq;
59
60    fn deref(&self) -> &Self::Target {
61        &self.0
62    }
63}
64
65impl From<Fq> for CamlFq {
66    fn from(x: Fq) -> Self {
67        CamlFq(x)
68    }
69}
70
71impl From<&Fq> for CamlFq {
72    fn from(x: &Fq) -> Self {
73        CamlFq(*x)
74    }
75}
76
77impl From<CamlFq> for Fq {
78    fn from(camlfq: CamlFq) -> Fq {
79        camlfq.0
80    }
81}
82
83impl From<&CamlFq> for Fq {
84    fn from(camlfq: &CamlFq) -> Fq {
85        camlfq.0
86    }
87}
88
89impl TryFrom<CamlBigInteger256> for CamlFq {
90    type Error = ocaml::Error;
91    fn try_from(x: CamlBigInteger256) -> Result<Self, Self::Error> {
92        Fq::from_bigint(x.0)
93            .map(Into::into)
94            .ok_or(ocaml::Error::Message(
95                "TryFrom<CamlBigInteger256>: integer is larger than order",
96            ))
97    }
98}
99
100//
101// Helpers
102//
103
104#[ocaml_gen::func]
105#[ocaml::func]
106pub fn caml_pasta_fq_size_in_bits() -> ocaml::Int {
107    Fq_params::MODULUS_BITS as isize
108}
109
110#[ocaml_gen::func]
111#[ocaml::func]
112pub fn caml_pasta_fq_size() -> CamlBigInteger256 {
113    Fq_params::MODULUS.into()
114}
115
116#[ocaml_gen::func]
117#[ocaml::func]
118pub fn caml_pasta_fq_add(x: ocaml::Pointer<CamlFq>, y: ocaml::Pointer<CamlFq>) -> CamlFq {
119    CamlFq(x.as_ref().0 + y.as_ref().0)
120}
121
122#[ocaml_gen::func]
123#[ocaml::func]
124pub fn caml_pasta_fq_sub(x: ocaml::Pointer<CamlFq>, y: ocaml::Pointer<CamlFq>) -> CamlFq {
125    CamlFq(x.as_ref().0 - y.as_ref().0)
126}
127
128#[ocaml_gen::func]
129#[ocaml::func]
130pub fn caml_pasta_fq_negate(x: ocaml::Pointer<CamlFq>) -> CamlFq {
131    CamlFq(-x.as_ref().0)
132}
133
134#[ocaml_gen::func]
135#[ocaml::func]
136pub fn caml_pasta_fq_mul(x: ocaml::Pointer<CamlFq>, y: ocaml::Pointer<CamlFq>) -> CamlFq {
137    CamlFq(x.as_ref().0 * y.as_ref().0)
138}
139
140#[ocaml_gen::func]
141#[ocaml::func]
142pub fn caml_pasta_fq_div(x: ocaml::Pointer<CamlFq>, y: ocaml::Pointer<CamlFq>) -> CamlFq {
143    CamlFq(x.as_ref().0 / y.as_ref().0)
144}
145
146#[ocaml_gen::func]
147#[ocaml::func]
148pub fn caml_pasta_fq_inv(x: ocaml::Pointer<CamlFq>) -> Option<CamlFq> {
149    x.as_ref().0.inverse().map(CamlFq)
150}
151
152#[ocaml_gen::func]
153#[ocaml::func]
154pub fn caml_pasta_fq_square(x: ocaml::Pointer<CamlFq>) -> CamlFq {
155    CamlFq(x.as_ref().0.square())
156}
157
158#[ocaml_gen::func]
159#[ocaml::func]
160pub fn caml_pasta_fq_is_square(x: ocaml::Pointer<CamlFq>) -> bool {
161    let s = x.as_ref().0.pow(Fq_params::MODULUS_MINUS_ONE_DIV_TWO);
162    s.is_zero() || s.is_one()
163}
164
165#[ocaml_gen::func]
166#[ocaml::func]
167pub fn caml_pasta_fq_sqrt(x: ocaml::Pointer<CamlFq>) -> Option<CamlFq> {
168    x.as_ref().0.sqrt().map(CamlFq)
169}
170
171#[ocaml_gen::func]
172#[ocaml::func]
173pub fn caml_pasta_fq_of_int(i: ocaml::Int) -> CamlFq {
174    CamlFq(Fq::from(i as u64))
175}
176
177//
178// Conversion methods
179//
180
181#[ocaml_gen::func]
182#[ocaml::func]
183pub fn caml_pasta_fq_to_string(x: ocaml::Pointer<CamlFq>) -> String {
184    CamlBigInteger256(x.as_ref().into_bigint()).to_string()
185}
186
187#[ocaml_gen::func]
188#[ocaml::func]
189pub fn caml_pasta_fq_of_string(s: CamlBytesString) -> Result<CamlFq, ocaml::Error> {
190    let biguint = BigUint::parse_bytes(s.0, 10).ok_or(ocaml::Error::Message(
191        "caml_pasta_fq_of_string: couldn't parse input",
192    ))?;
193    let camlbigint: CamlBigInteger256 = biguint
194        .try_into()
195        .map_err(|_| ocaml::Error::Message("caml_pasta_fq_of_string: Biguint is too large"))?;
196    CamlFq::try_from(camlbigint).map_err(|_| ocaml::Error::Message("caml_pasta_fq_of_string"))
197}
198
199//
200// Data methods
201//
202
203#[ocaml_gen::func]
204#[ocaml::func]
205pub fn caml_pasta_fq_print(x: ocaml::Pointer<CamlFq>) {
206    println!("{}", CamlBigInteger256(x.as_ref().0.into_bigint()));
207}
208
209#[ocaml_gen::func]
210#[ocaml::func]
211pub fn caml_pasta_fq_print_rust(x: ocaml::Pointer<CamlFq>) {
212    println!("{}", x.as_ref().0);
213}
214
215#[ocaml_gen::func]
216#[ocaml::func]
217pub fn caml_pasta_fq_copy(mut x: ocaml::Pointer<CamlFq>, y: ocaml::Pointer<CamlFq>) {
218    *x.as_mut() = *y.as_ref()
219}
220
221#[ocaml_gen::func]
222#[ocaml::func]
223pub fn caml_pasta_fq_mut_add(mut x: ocaml::Pointer<CamlFq>, y: ocaml::Pointer<CamlFq>) {
224    x.as_mut().0 += y.as_ref().0;
225}
226
227#[ocaml_gen::func]
228#[ocaml::func]
229pub fn caml_pasta_fq_mut_sub(mut x: ocaml::Pointer<CamlFq>, y: ocaml::Pointer<CamlFq>) {
230    x.as_mut().0 -= y.as_ref().0;
231}
232
233#[ocaml_gen::func]
234#[ocaml::func]
235pub fn caml_pasta_fq_mut_mul(mut x: ocaml::Pointer<CamlFq>, y: ocaml::Pointer<CamlFq>) {
236    x.as_mut().0 *= y.as_ref().0;
237}
238
239#[ocaml_gen::func]
240#[ocaml::func]
241pub fn caml_pasta_fq_mut_square(mut x: ocaml::Pointer<CamlFq>) {
242    x.as_mut().0.square_in_place();
243}
244
245#[ocaml_gen::func]
246#[ocaml::func]
247pub fn caml_pasta_fq_compare(x: ocaml::Pointer<CamlFq>, y: ocaml::Pointer<CamlFq>) -> ocaml::Int {
248    match x.as_ref().0.into_bigint().cmp(&y.as_ref().0.into_bigint()) {
249        Less => -1,
250        Equal => 0,
251        Greater => 1,
252    }
253}
254
255#[ocaml_gen::func]
256#[ocaml::func]
257pub fn caml_pasta_fq_equal(x: ocaml::Pointer<CamlFq>, y: ocaml::Pointer<CamlFq>) -> bool {
258    x.as_ref().0 == y.as_ref().0
259}
260
261#[ocaml_gen::func]
262#[ocaml::func]
263pub fn caml_pasta_fq_random() -> CamlFq {
264    let fq: Fq = UniformRand::rand(&mut rand::thread_rng());
265    CamlFq(fq)
266}
267
268#[ocaml_gen::func]
269#[ocaml::func]
270pub fn caml_pasta_fq_rng(i: ocaml::Int) -> CamlFq {
271    // We only care about entropy here, so we force a conversion i32 -> u32.
272    let i: u64 = (i as u32).into();
273    let mut rng: StdRng = rand::SeedableRng::seed_from_u64(i);
274    let fq: Fq = UniformRand::rand(&mut rng);
275    CamlFq(fq)
276}
277
278#[ocaml_gen::func]
279#[ocaml::func]
280pub fn caml_pasta_fq_to_bigint(x: ocaml::Pointer<CamlFq>) -> CamlBigInteger256 {
281    CamlBigInteger256(x.as_ref().0.into_bigint())
282}
283
284#[ocaml_gen::func]
285#[ocaml::func]
286pub fn caml_pasta_fq_of_bigint(x: CamlBigInteger256) -> Result<CamlFq, ocaml::Error> {
287    Fq::from_bigint(x.0).map(CamlFq).ok_or_else(|| {
288        let err = format!(
289            "caml_pasta_fq_of_bigint was given an invalid CamlBigInteger256: {}",
290            x.0
291        );
292        ocaml::Error::Error(err.into())
293    })
294}
295
296#[ocaml_gen::func]
297#[ocaml::func]
298pub fn caml_pasta_fq_two_adic_root_of_unity() -> CamlFq {
299    let res: Fq = FftField::TWO_ADIC_ROOT_OF_UNITY;
300    CamlFq(res)
301}
302
303#[ocaml_gen::func]
304#[ocaml::func]
305pub fn caml_pasta_fq_domain_generator(log2_size: ocaml::Int) -> Result<CamlFq, ocaml::Error> {
306    Domain::new(1 << log2_size)
307        .map(|x| CamlFq(x.group_gen))
308        .ok_or(ocaml::Error::Message("caml_pasta_fq_domain_generator"))
309}
310
311#[ocaml_gen::func]
312#[ocaml::func]
313pub fn caml_pasta_fq_to_bytes(x: ocaml::Pointer<CamlFq>) -> [u8; core::mem::size_of::<Fq>()] {
314    let mut res = [0u8; core::mem::size_of::<Fq>()];
315    x.as_ref().0.serialize_compressed(&mut res[..]).unwrap();
316    res
317}
318
319#[ocaml_gen::func]
320#[ocaml::func]
321pub fn caml_pasta_fq_of_bytes(x: &[u8]) -> Result<CamlFq, ocaml::Error> {
322    let x = Fq::deserialize_compressed(x)?;
323    Ok(CamlFq(x))
324}
325
326#[ocaml_gen::func]
327#[ocaml::func]
328pub fn caml_pasta_fq_deep_copy(x: CamlFq) -> CamlFq {
329    x
330}