use rug::{Integer, Rational};
use super::ffi;
use super::integer::{fmpz_get_rug, fmpz_set_rug};
pub(crate) struct FlintRational {
inner: ffi::Fmpq,
}
unsafe impl Send for FlintRational {}
unsafe impl Sync for FlintRational {}
impl FlintRational {
pub(crate) fn new() -> Self {
let mut inner = ffi::Fmpq { num: 0, den: 0 };
crate::flint::note_thread_uses_flint();
unsafe { ffi::fmpq_init(&mut inner) };
Self { inner }
}
pub(crate) fn from_rug(r: &Rational) -> Self {
let mut q = Self::new();
unsafe {
fmpz_set_rug(&mut q.inner.num, r.numer());
fmpz_set_rug(&mut q.inner.den, r.denom());
}
q
}
pub(crate) fn to_rug(&self) -> Rational {
let (num, den): (Integer, Integer) =
unsafe { (fmpz_get_rug(&self.inner.num), fmpz_get_rug(&self.inner.den)) };
Rational::from((num, den))
}
pub(crate) fn as_ptr(&self) -> *const ffi::Fmpq {
&self.inner
}
pub(crate) fn as_mut_ptr(&mut self) -> *mut ffi::Fmpq {
&mut self.inner
}
}
impl Drop for FlintRational {
fn drop(&mut self) {
unsafe { ffi::fmpq_clear(&mut self.inner) };
}
}
#[cfg(test)]
mod tests {
use super::*;
use proptest::prelude::*;
fn flint_printed(q: &FlintRational) -> String {
unsafe {
let raw = ffi::fmpq_get_str(std::ptr::null_mut(), 10, q.as_ptr());
let s = std::ffi::CStr::from_ptr(raw).to_string_lossy().into_owned();
ffi::flint_free(raw.cast());
s
}
}
fn check(r: &Rational) {
let q = FlintRational::from_rug(r);
assert_eq!(flint_printed(&q), r.to_string());
assert_eq!(&q.to_rug(), r);
}
fn big(negative: bool, limbs: &[u64]) -> Integer {
let m = Integer::from_digits(limbs, rug::integer::Order::Lsf);
if negative {
-m
} else {
m
}
}
#[test]
fn edge_values() {
let two64 = Integer::from(1) << 64u32;
for (n, d) in [
(Integer::new(), Integer::from(1)),
(Integer::from(-691), Integer::from(2730)),
(Integer::from(i64::MIN), Integer::from(u64::MAX)),
(-two64.clone(), two64.clone() + 1u32),
(
(Integer::from(1) << 5000u32) + 1u32,
Integer::from(3) << 700u32,
),
] {
check(&Rational::from((n, d)));
}
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(128))]
#[test]
fn prop_roundtrip(
n_neg in any::<bool>(),
n in prop::collection::vec(any::<u64>(), 0..60),
d in prop::collection::vec(any::<u64>(), 0..60),
) {
let den = big(false, &d);
prop_assume!(den != 0);
check(&Rational::from((big(n_neg, &n), den)));
}
}
}