use crate::update_counts;
use crate::Pauli;
use num_bigint::BigInt;
use std::{
ffi::{CStr, CString},
os::raw::{c_char, c_double},
rc::Rc,
};
#[no_mangle]
pub unsafe extern "C" fn __quantum__rt__string_create(str: *mut c_char) -> *const CString {
let cstring = CString::new(CStr::from_ptr(str).to_owned()).expect("Failed to create %String");
Rc::into_raw(Rc::new(cstring))
}
#[no_mangle]
pub unsafe extern "C" fn __quantum__rt__string_get_data(str: *const CString) -> *const c_char {
(*str).as_bytes_with_nul().as_ptr().cast::<c_char>()
}
#[no_mangle]
pub unsafe extern "C" fn __quantum__rt__string_get_length(str: *const CString) -> u32 {
(*str)
.as_bytes()
.len()
.try_into()
.expect("String length is too large for 32-bit integer.")
}
#[no_mangle]
pub unsafe extern "C" fn __quantum__rt__string_update_reference_count(
str: *const CString,
update: i32,
) {
update_counts(str, update, false);
}
#[no_mangle]
pub unsafe extern "C" fn __quantum__rt__string_concatenate(
s1: *const CString,
s2: *const CString,
) -> *const CString {
let mut new_str = (*s1).clone().into_bytes();
new_str.extend_from_slice((*s2).to_bytes());
Rc::into_raw(Rc::new(
CString::new(new_str).expect("Unable to convert string"),
))
}
#[no_mangle]
pub unsafe extern "C" fn __quantum__rt__string_equal(
s1: *const CString,
s2: *const CString,
) -> bool {
*s1 == *s2
}
pub(crate) fn convert<T>(input: &T) -> *const CString
where
T: ToString,
{
unsafe {
__quantum__rt__string_create(
CString::new(input.to_string())
.expect("Unable to allocate string for conversion.")
.as_bytes_with_nul()
.as_ptr() as *mut c_char,
)
}
}
#[no_mangle]
pub extern "C" fn __quantum__rt__int_to_string(input: i64) -> *const CString {
convert(&input)
}
pub(crate) fn double_to_string(input: c_double) -> String {
if (input.floor() - input.ceil()).abs() < c_double::EPSILON {
format!("{input:.1}")
} else {
format!("{input}")
}
}
#[no_mangle]
pub extern "C" fn __quantum__rt__double_to_string(input: c_double) -> *const CString {
convert(&double_to_string(input))
}
#[no_mangle]
pub extern "C" fn __quantum__rt__bool_to_string(input: bool) -> *const CString {
convert(&input)
}
#[no_mangle]
pub extern "C" fn __quantum__rt__pauli_to_string(input: Pauli) -> *const CString {
match input {
Pauli::I => convert(&"PauliI"),
Pauli::X => convert(&"PauliX"),
Pauli::Y => convert(&"PauliY"),
Pauli::Z => convert(&"PauliZ"),
}
}
#[no_mangle]
pub unsafe extern "C" fn __quantum__rt__bigint_to_string(input: *const BigInt) -> *const CString {
convert(&*input)
}
#[cfg(test)]
mod tests {
use std::mem::ManuallyDrop;
use super::*;
use crate::bigints::{
__quantum__rt__bigint_create_array, __quantum__rt__bigint_create_i64,
__quantum__rt__bigint_update_reference_count,
};
#[test]
fn test_string_create() {
let orig_str = CString::new("Test String").unwrap();
let str = unsafe {
__quantum__rt__string_create(
orig_str.as_bytes_with_nul().as_ptr() as *mut std::ffi::c_char
)
};
assert_eq!(orig_str.to_str().unwrap(), "Test String");
drop(orig_str);
assert!(!str.is_null());
unsafe {
assert_eq!(
Rc::from_raw(str)
.to_str()
.expect("Unable to convert input string"),
"Test String"
);
}
}
#[test]
fn test_string_get_data() {
let str = unsafe {
__quantum__rt__string_create(
CString::new("Data").unwrap().as_bytes_with_nul().as_ptr() as *mut c_char
)
};
unsafe {
assert_eq!(
CStr::from_ptr(__quantum__rt__string_get_data(str))
.to_str()
.unwrap(),
"Data"
);
}
unsafe {
__quantum__rt__string_update_reference_count(str, -1);
}
}
#[test]
fn test_string_get_length() {
let str = unsafe {
__quantum__rt__string_create(
CString::new("Data").unwrap().as_bytes_with_nul().as_ptr() as *mut c_char
)
};
assert_eq!(unsafe { __quantum__rt__string_get_length(str) }, 4);
unsafe {
__quantum__rt__string_update_reference_count(str, -1);
}
}
#[test]
fn test_string_update_reference_count() {
unsafe {
let str = __quantum__rt__string_create(
CString::new("Data").unwrap().as_bytes_with_nul().as_ptr() as *mut c_char,
);
let rc = ManuallyDrop::new(Rc::from_raw(str));
assert_eq!(Rc::strong_count(&rc), 1);
__quantum__rt__string_update_reference_count(str, 2);
assert_eq!(Rc::strong_count(&rc), 3);
__quantum__rt__string_update_reference_count(str, -2);
assert_eq!(Rc::strong_count(&rc), 1);
__quantum__rt__string_update_reference_count(str, -1);
}
}
#[test]
fn test_string_concatenate() {
unsafe {
let str1 = __quantum__rt__string_create(
CString::new("Hello").unwrap().as_bytes_with_nul().as_ptr() as *mut c_char,
);
let str2 = __quantum__rt__string_create(
CString::new(", World!")
.unwrap()
.as_bytes_with_nul()
.as_ptr() as *mut c_char,
);
let str3 = __quantum__rt__string_concatenate(str1, str2);
let rc = ManuallyDrop::new(Rc::from_raw(str3));
assert_eq!(Rc::strong_count(&rc), 1);
assert_eq!(
CStr::from_ptr(__quantum__rt__string_get_data(str3))
.to_str()
.unwrap(),
"Hello, World!"
);
__quantum__rt__string_update_reference_count(str3, -1);
let rc = ManuallyDrop::new(Rc::from_raw(str2));
assert_eq!(Rc::strong_count(&rc), 1);
assert_eq!(
CStr::from_ptr(__quantum__rt__string_get_data(str2))
.to_str()
.unwrap(),
", World!"
);
__quantum__rt__string_update_reference_count(str2, -1);
let rc = ManuallyDrop::new(Rc::from_raw(str1));
assert_eq!(Rc::strong_count(&rc), 1);
assert_eq!(
CStr::from_ptr(__quantum__rt__string_get_data(str1))
.to_str()
.unwrap(),
"Hello"
);
__quantum__rt__string_update_reference_count(str1, -1);
}
}
#[test]
fn test_string_equal() {
unsafe {
let str1 = __quantum__rt__string_create(
CString::new("Data").unwrap().as_bytes_with_nul().as_ptr() as *mut c_char,
);
let str2 = __quantum__rt__string_create(
CString::new("Data").unwrap().as_bytes_with_nul().as_ptr() as *mut c_char,
);
let str3 = __quantum__rt__string_create(
CString::new("Not Data")
.unwrap()
.as_bytes_with_nul()
.as_ptr() as *mut c_char,
);
assert!(__quantum__rt__string_equal(str1, str2));
assert!(!__quantum__rt__string_equal(str1, str3));
let rc = ManuallyDrop::new(Rc::from_raw(str3));
assert_eq!(Rc::strong_count(&rc), 1);
assert_eq!(
CStr::from_ptr(__quantum__rt__string_get_data(str3))
.to_str()
.unwrap(),
"Not Data"
);
__quantum__rt__string_update_reference_count(str3, -1);
let rc = ManuallyDrop::new(Rc::from_raw(str2));
assert_eq!(Rc::strong_count(&rc), 1);
assert_eq!(
CStr::from_ptr(__quantum__rt__string_get_data(str2))
.to_str()
.unwrap(),
"Data"
);
__quantum__rt__string_update_reference_count(str2, -1);
let rc = ManuallyDrop::new(Rc::from_raw(str1));
assert_eq!(Rc::strong_count(&rc), 1);
assert_eq!(
CStr::from_ptr(__quantum__rt__string_get_data(str1))
.to_str()
.unwrap(),
"Data"
);
__quantum__rt__string_update_reference_count(str1, -1);
}
}
#[test]
fn test_to_string() {
let str0 = __quantum__rt__int_to_string(42_i64);
unsafe {
assert_eq!(
CStr::from_ptr(__quantum__rt__string_get_data(str0))
.to_str()
.unwrap(),
"42"
);
}
let str1 = __quantum__rt__double_to_string(4.2_f64);
unsafe {
assert_eq!(
CStr::from_ptr(__quantum__rt__string_get_data(str1))
.to_str()
.unwrap(),
"4.2"
);
}
let str1_1 = __quantum__rt__double_to_string(4.0_f64);
unsafe {
assert_eq!(
CStr::from_ptr(__quantum__rt__string_get_data(str1_1))
.to_str()
.unwrap(),
"4.0"
);
}
let str1_2 = __quantum__rt__double_to_string(0.1_f64);
unsafe {
assert_eq!(
CStr::from_ptr(__quantum__rt__string_get_data(str1_2))
.to_str()
.unwrap(),
"0.1"
);
}
let str1_3 = __quantum__rt__double_to_string(0.100_000_000_01_f64);
unsafe {
assert_eq!(
CStr::from_ptr(__quantum__rt__string_get_data(str1_3))
.to_str()
.unwrap(),
"0.10000000001"
);
}
let str2 = __quantum__rt__bool_to_string(false);
unsafe {
assert_eq!(
CStr::from_ptr(__quantum__rt__string_get_data(str2))
.to_str()
.unwrap(),
"false"
);
}
let str3 = __quantum__rt__pauli_to_string(Pauli::Z);
unsafe {
assert_eq!(
CStr::from_ptr(__quantum__rt__string_get_data(str3))
.to_str()
.unwrap(),
"PauliZ"
);
}
let input4 = __quantum__rt__bigint_create_i64(400_002);
unsafe {
let str4 = __quantum__rt__bigint_to_string(input4);
assert_eq!(
CStr::from_ptr(__quantum__rt__string_get_data(str4))
.to_str()
.unwrap(),
"400002"
);
__quantum__rt__string_update_reference_count(str4, -1);
}
unsafe {
let bytes = [0x18, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF3, 0x01];
let input5 =
__quantum__rt__bigint_create_array(bytes.len().try_into().unwrap(), bytes.as_ptr());
let str5 = __quantum__rt__bigint_to_string(input5);
assert_eq!(
CStr::from_ptr(__quantum__rt__string_get_data(str5))
.to_str()
.unwrap(),
"9223372036854775807000"
);
__quantum__rt__string_update_reference_count(str0, -1);
__quantum__rt__string_update_reference_count(str1, -1);
__quantum__rt__string_update_reference_count(str1_1, -1);
__quantum__rt__string_update_reference_count(str1_2, -1);
__quantum__rt__string_update_reference_count(str1_3, -1);
__quantum__rt__string_update_reference_count(str2, -1);
__quantum__rt__string_update_reference_count(str3, -1);
__quantum__rt__bigint_update_reference_count(input4, -1);
__quantum__rt__bigint_update_reference_count(input5, -1);
}
}
}