qir_stdlib/
bigints.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4use crate::update_counts;
5use num_bigint::BigInt;
6use std::{mem::ManuallyDrop, rc::Rc};
7
8#[unsafe(no_mangle)]
9pub extern "C" fn __quantum__rt__bigint_create_i64(input: i64) -> *const BigInt {
10    Rc::into_raw(Rc::new(input.into()))
11}
12
13#[unsafe(no_mangle)]
14pub unsafe extern "C" fn __quantum__rt__bigint_create_array(
15    size: u32,
16    input: *const u8,
17) -> *const BigInt {
18    unsafe {
19        Rc::into_raw(Rc::new(BigInt::from_signed_bytes_le(
20            std::slice::from_raw_parts(input, size as usize),
21        )))
22    }
23}
24
25#[unsafe(no_mangle)]
26pub unsafe extern "C" fn __quantum__rt__bigint_get_data(input: *const BigInt) -> *const u8 {
27    unsafe { ManuallyDrop::new((*input).to_signed_bytes_le()).as_ptr() }
28}
29
30#[unsafe(no_mangle)]
31pub unsafe extern "C" fn __quantum__rt__bigint_get_length(input: *const BigInt) -> u32 {
32    unsafe {
33        let size = (*input).to_signed_bytes_le().len();
34        size.try_into()
35            .expect("Length of bigint representation too large for 32-bit integer.")
36    }
37}
38
39#[unsafe(no_mangle)]
40pub unsafe extern "C" fn __quantum__rt__bigint_update_reference_count(
41    input: *const BigInt,
42    update: i32,
43) {
44    unsafe {
45        update_counts(input, update, false);
46    }
47}
48
49#[unsafe(no_mangle)]
50pub unsafe extern "C" fn __quantum__rt__bigint_negate(input: *const BigInt) -> *const BigInt {
51    unsafe { Rc::into_raw(Rc::new(&(*input) * -1)) }
52}
53
54#[unsafe(no_mangle)]
55pub unsafe extern "C" fn __quantum__rt__bigint_add(
56    lhs: *const BigInt,
57    rhs: *const BigInt,
58) -> *const BigInt {
59    unsafe { Rc::into_raw(Rc::new(&(*lhs) + &(*rhs))) }
60}
61
62#[unsafe(no_mangle)]
63pub unsafe extern "C" fn __quantum__rt__bigint_subtract(
64    lhs: *const BigInt,
65    rhs: *const BigInt,
66) -> *const BigInt {
67    unsafe { Rc::into_raw(Rc::new(&(*lhs) - &(*rhs))) }
68}
69
70#[unsafe(no_mangle)]
71pub unsafe extern "C" fn __quantum__rt__bigint_multiply(
72    lhs: *const BigInt,
73    rhs: *const BigInt,
74) -> *const BigInt {
75    unsafe { Rc::into_raw(Rc::new(&(*lhs) * &(*rhs))) }
76}
77
78#[unsafe(no_mangle)]
79pub unsafe extern "C" fn __quantum__rt__bigint_divide(
80    lhs: *const BigInt,
81    rhs: *const BigInt,
82) -> *const BigInt {
83    unsafe { Rc::into_raw(Rc::new(&(*lhs) / &(*rhs))) }
84}
85
86#[unsafe(no_mangle)]
87pub unsafe extern "C" fn __quantum__rt__bigint_modulus(
88    lhs: *const BigInt,
89    rhs: *const BigInt,
90) -> *const BigInt {
91    unsafe { Rc::into_raw(Rc::new(&(*lhs) % &(*rhs))) }
92}
93
94#[unsafe(no_mangle)]
95pub unsafe extern "C" fn __quantum__rt__bigint_power(
96    base: *const BigInt,
97    exponent: u32,
98) -> *const BigInt {
99    unsafe { Rc::into_raw(Rc::new((*base).pow(exponent))) }
100}
101
102#[unsafe(no_mangle)]
103pub unsafe extern "C" fn __quantum__rt__bigint_bitand(
104    lhs: *const BigInt,
105    rhs: *const BigInt,
106) -> *const BigInt {
107    unsafe { Rc::into_raw(Rc::new(&(*lhs) & &(*rhs))) }
108}
109
110#[unsafe(no_mangle)]
111pub unsafe extern "C" fn __quantum__rt__bigint_bitor(
112    lhs: *const BigInt,
113    rhs: *const BigInt,
114) -> *const BigInt {
115    unsafe { Rc::into_raw(Rc::new(&(*lhs) | &(*rhs))) }
116}
117
118#[unsafe(no_mangle)]
119pub unsafe extern "C" fn __quantum__rt__bigint_bitxor(
120    lhs: *const BigInt,
121    rhs: *const BigInt,
122) -> *const BigInt {
123    unsafe { Rc::into_raw(Rc::new(&(*lhs) ^ &(*rhs))) }
124}
125
126#[unsafe(no_mangle)]
127pub unsafe extern "C" fn __quantum__rt__bigint_bitnot(input: *const BigInt) -> *const BigInt {
128    unsafe { Rc::into_raw(Rc::new(!&(*input))) }
129}
130
131#[unsafe(no_mangle)]
132pub unsafe extern "C" fn __quantum__rt__bigint_shiftleft(
133    input: *const BigInt,
134    amount: u64,
135) -> *const BigInt {
136    unsafe { Rc::into_raw(Rc::new(&(*input) << amount)) }
137}
138
139#[unsafe(no_mangle)]
140pub unsafe extern "C" fn __quantum__rt__bigint_shiftright(
141    input: *const BigInt,
142    amount: u64,
143) -> *const BigInt {
144    unsafe { Rc::into_raw(Rc::new(&(*input) >> amount)) }
145}
146
147#[unsafe(no_mangle)]
148pub unsafe extern "C" fn __quantum__rt__bigint_equal(
149    lhs: *const BigInt,
150    rhs: *const BigInt,
151) -> bool {
152    unsafe { (*lhs) == (*rhs) }
153}
154
155#[unsafe(no_mangle)]
156pub unsafe extern "C" fn __quantum__rt__bigint_greater(
157    lhs: *const BigInt,
158    rhs: *const BigInt,
159) -> bool {
160    unsafe { (*lhs) > (*rhs) }
161}
162
163#[unsafe(no_mangle)]
164pub unsafe extern "C" fn __quantum__rt__bigint_greater_eq(
165    lhs: *const BigInt,
166    rhs: *const BigInt,
167) -> bool {
168    unsafe { (*lhs) >= (*rhs) }
169}
170
171#[cfg(test)]
172mod tests {
173    use std::convert::TryInto;
174
175    use super::*;
176
177    #[test]
178    fn test_bigint_create_from_int() {
179        let bigint_0 = __quantum__rt__bigint_create_i64(42);
180        unsafe {
181            assert_eq!(*bigint_0, (42).into());
182            // Note that the test must decrement the reference count on any created items to ensure
183            // they are cleaned up and tests can pass with address sanitizer.
184            __quantum__rt__bigint_update_reference_count(bigint_0, -1);
185        }
186    }
187
188    #[test]
189    fn test_bigint_create_from_array() {
190        let bytes = 9_223_372_036_854_775_807_i64.to_le_bytes();
191        unsafe {
192            let bigint_1 =
193                __quantum__rt__bigint_create_array(bytes.len().try_into().unwrap(), bytes.as_ptr());
194            assert_eq!(*bigint_1, (9_223_372_036_854_775_807_i64).into());
195            __quantum__rt__bigint_update_reference_count(bigint_1, -1);
196        }
197    }
198
199    #[test]
200    fn test_bigint_arithmetic() {
201        let bigint_0 = __quantum__rt__bigint_create_i64(42);
202        let bigint_1 = __quantum__rt__bigint_create_i64(3);
203        unsafe {
204            let bigint_2 = __quantum__rt__bigint_add(bigint_0, bigint_1);
205            assert_eq!(*bigint_2, (45).into());
206            let bigint_3 = __quantum__rt__bigint_subtract(bigint_2, bigint_1);
207            assert_eq!(*bigint_3, (42).into());
208            let bigint_4 = __quantum__rt__bigint_divide(bigint_3, bigint_1);
209            assert_eq!(*bigint_4, (14).into());
210            let bigint_5 = __quantum__rt__bigint_multiply(bigint_4, bigint_1);
211            assert_eq!(*bigint_5, (42).into());
212            let bigint_6 = __quantum__rt__bigint_modulus(bigint_5, bigint_1);
213            assert_eq!(*bigint_6, (0).into());
214            let bigint_7 = __quantum__rt__bigint_negate(bigint_5);
215            assert_eq!(*bigint_7, (-42).into());
216            let bigint_8 = __quantum__rt__bigint_power(bigint_7, 3);
217            assert_eq!(*bigint_8, (-74088).into());
218            __quantum__rt__bigint_update_reference_count(bigint_8, -1);
219            __quantum__rt__bigint_update_reference_count(bigint_7, -1);
220            __quantum__rt__bigint_update_reference_count(bigint_6, -1);
221            __quantum__rt__bigint_update_reference_count(bigint_5, -1);
222            __quantum__rt__bigint_update_reference_count(bigint_4, -1);
223            __quantum__rt__bigint_update_reference_count(bigint_3, -1);
224            __quantum__rt__bigint_update_reference_count(bigint_2, -1);
225            __quantum__rt__bigint_update_reference_count(bigint_1, -1);
226            __quantum__rt__bigint_update_reference_count(bigint_0, -1);
227        }
228    }
229
230    #[test]
231    fn test_bigint_bitops() {
232        let bigint_0 = __quantum__rt__bigint_create_i64(42);
233        let bigint_1 = __quantum__rt__bigint_create_i64(3);
234        unsafe {
235            let bigint_2 = __quantum__rt__bigint_bitand(bigint_0, bigint_1);
236            assert_eq!(*bigint_2, (2).into());
237            let bigint_3 = __quantum__rt__bigint_bitor(bigint_0, bigint_1);
238            assert_eq!(*bigint_3, (43).into());
239            let bigint_4 = __quantum__rt__bigint_bitxor(bigint_0, bigint_3);
240            assert_eq!(*bigint_4, (1).into());
241            let bigint_5 = __quantum__rt__bigint_bitnot(bigint_4);
242            assert_eq!(*bigint_5, (-2).into());
243            let bigint_6 = __quantum__rt__bigint_shiftleft(bigint_0, 2);
244            assert_eq!(*bigint_6, (168).into());
245            let bigint_7 = __quantum__rt__bigint_shiftright(bigint_6, 3);
246            assert_eq!(*bigint_7, (21).into());
247            __quantum__rt__bigint_update_reference_count(bigint_7, -1);
248            __quantum__rt__bigint_update_reference_count(bigint_6, -1);
249            __quantum__rt__bigint_update_reference_count(bigint_5, -1);
250            __quantum__rt__bigint_update_reference_count(bigint_4, -1);
251            __quantum__rt__bigint_update_reference_count(bigint_3, -1);
252            __quantum__rt__bigint_update_reference_count(bigint_2, -1);
253            __quantum__rt__bigint_update_reference_count(bigint_1, -1);
254            __quantum__rt__bigint_update_reference_count(bigint_0, -1);
255        }
256    }
257
258    #[test]
259    fn test_bigint_comparisons() {
260        let bigint_0 = __quantum__rt__bigint_create_i64(42);
261        let bigint_1 = __quantum__rt__bigint_create_i64(43);
262        let bigint_2 = __quantum__rt__bigint_create_i64(42);
263        unsafe {
264            assert!(__quantum__rt__bigint_greater(bigint_1, bigint_0));
265            assert!(!__quantum__rt__bigint_greater(bigint_0, bigint_1));
266            assert!(__quantum__rt__bigint_equal(bigint_0, bigint_2));
267            assert!(__quantum__rt__bigint_greater_eq(bigint_0, bigint_2));
268            assert!(__quantum__rt__bigint_greater_eq(bigint_1, bigint_2));
269            assert!(!__quantum__rt__bigint_greater_eq(bigint_0, bigint_1));
270            __quantum__rt__bigint_update_reference_count(bigint_2, -1);
271            __quantum__rt__bigint_update_reference_count(bigint_1, -1);
272            __quantum__rt__bigint_update_reference_count(bigint_0, -1);
273        }
274    }
275}