use {Assign, Integer};
use ext::gmp as xgmp;
use gmp_mpfr_sys::gmp::{self, mpq_t};
use inner::{Inner, InnerMut};
use std::cmp::Ordering;
use std::error::Error;
use std::ffi::CStr;
use std::i32;
use std::marker::PhantomData;
use std::mem;
use std::ops::Deref;
use std::os::raw::{c_char, c_int};
pub struct Rational {
inner: mpq_t,
}
impl Rational {
#[inline]
pub fn new() -> Rational {
unsafe {
let mut ret: Rational = mem::uninitialized();
gmp::mpq_init(ret.inner_mut());
ret
}
}
#[inline]
pub fn from_f32(val: f32) -> Option<Rational> {
Rational::from_f64(val.into())
}
#[inline]
pub fn from_f64(val: f64) -> Option<Rational> {
if val.is_finite() {
let mut r = Rational::new();
r.assign_f64(val).unwrap();
Some(r)
} else {
None
}
}
#[inline]
pub fn from_str_radix(
src: &str,
radix: i32,
) -> Result<Rational, ParseRationalError> {
let mut r = Rational::new();
r.assign_str_radix(src, radix)?;
Ok(r)
}
pub fn valid_str_radix(
src: &str,
radix: i32,
) -> Result<ValidRational, ParseRationalError> {
use self::ParseErrorKind as Kind;
use self::ParseRationalError as Error;
assert!(radix >= 2 && radix <= 36, "radix out of range");
let bytes = src.as_bytes();
let (skip_plus, iter) = match bytes.get(0) {
Some(&b'+') => (&bytes[1..], bytes[1..].iter()),
Some(&b'-') => (bytes, bytes[1..].iter()),
_ => (bytes, bytes.iter()),
};
let mut got_digit = false;
let mut denom = false;
let mut denom_non_zero = false;
for b in iter {
if *b == b'/' {
if denom {
return Err(Error {
kind: Kind::TooManySlashes,
});
}
if !got_digit {
return Err(Error {
kind: Kind::NumerNoDigits,
});
}
got_digit = false;
denom = true;
continue;
}
let digit_value = match *b {
b'0'...b'9' => *b - b'0',
b'a'...b'z' => *b - b'a' + 10,
b'A'...b'Z' => *b - b'A' + 10,
_ => Err(Error {
kind: Kind::InvalidDigit,
})?,
};
if digit_value >= radix as u8 {
return Err(Error {
kind: Kind::InvalidDigit,
});
}
got_digit = true;
if denom && digit_value > 0 {
denom_non_zero = true;
}
}
if !got_digit && denom {
return Err(Error {
kind: Kind::DenomNoDigits,
});
} else if !got_digit {
return Err(Error {
kind: Kind::NoDigits,
});
}
if denom && !denom_non_zero {
return Err(Error {
kind: Kind::DenomZero,
});
}
let v = ValidRational {
bytes: skip_plus,
radix,
};
Ok(v)
}
#[inline]
pub fn to_integer(&self) -> Integer {
let mut i = Integer::new();
self.copy_to_integer(&mut i);
i
}
#[inline]
pub fn copy_to_integer(&self, i: &mut Integer) {
unsafe {
gmp::mpz_set_q(i.inner_mut(), self.inner());
}
}
#[inline]
pub fn to_f32(&self) -> f32 {
let f = self.to_f64();
if !f.is_nan() {
let u = unsafe { mem::transmute::<_, u64>(f) };
let trunc_u = u & (!0 << 29);
let trunc_f = unsafe { mem::transmute::<_, f64>(trunc_u) };
trunc_f as f32
} else {
f as f32
}
}
#[inline]
pub fn to_f64(&self) -> f64 {
unsafe { gmp::mpq_get_d(self.inner()) }
}
#[inline]
pub fn to_string_radix(&self, radix: i32) -> String {
make_string(self, radix, false)
}
#[inline]
pub fn assign_f32(&mut self, val: f32) -> Result<(), ()> {
self.assign_f64(val.into())
}
#[inline]
pub fn assign_f64(&mut self, val: f64) -> Result<(), ()> {
if val.is_finite() {
unsafe {
gmp::mpq_set_d(self.inner_mut(), val);
}
Ok(())
} else {
Err(())
}
}
#[inline]
pub fn assign_str(&mut self, src: &str) -> Result<(), ParseRationalError> {
self.assign_str_radix(src, 10)
}
#[inline]
pub fn assign_str_radix(
&mut self,
src: &str,
radix: i32,
) -> Result<(), ParseRationalError> {
self.assign(Rational::valid_str_radix(src, radix)?);
Ok(())
}
#[inline]
pub fn numer(&self) -> &Integer {
unsafe {
let ptr = gmp::mpq_numref_const(self.inner());
&*(ptr as *const Integer)
}
}
#[inline]
pub fn denom(&self) -> &Integer {
unsafe {
let ptr = gmp::mpq_denref_const(self.inner());
&*(ptr as *const Integer)
}
}
#[inline]
pub fn as_numer_denom(&self) -> (&Integer, &Integer) {
(self.numer(), self.denom())
}
pub fn as_mut_numer_denom(&mut self) -> MutNumerDenom {
unsafe {
let numer_ptr = gmp::mpq_numref(self.inner_mut());
let denom_ptr = gmp::mpq_denref(self.inner_mut());
let mut acting_denom = Integer::from(1);
mem::swap(acting_denom.inner_mut(), &mut *denom_ptr);
MutNumerDenom {
num: &mut *(numer_ptr as *mut Integer),
den_place: &mut *(denom_ptr as *mut Integer),
den_actual: acting_denom,
}
}
}
#[inline]
pub unsafe fn as_mut_numer_denom_no_canonicalization(
&mut self,
) -> (&mut Integer, &mut Integer) {
(
&mut *(gmp::mpq_numref(self.inner_mut()) as *mut _),
&mut *(gmp::mpq_denref(self.inner_mut()) as *mut _),
)
}
#[inline]
pub fn into_numer_denom(mut self) -> (Integer, Integer) {
let (mut numer, mut denom) = unsafe { mem::uninitialized() };
{
let mut self_numer_denom = self.as_mut_numer_denom();
mem::swap(&mut numer, self_numer_denom.num());
mem::swap(&mut denom, self_numer_denom.den());
mem::forget(self_numer_denom);
}
mem::forget(self);
(numer, denom)
}
#[inline]
pub fn as_neg(&self) -> BorrowRational {
let mut ret = BorrowRational {
inner: self.inner,
phantom: PhantomData,
};
let size = self.numer().inner().size.checked_neg().expect("overflow");
unsafe {
(*gmp::mpq_numref(&mut ret.inner)).size = size;
}
ret
}
#[inline]
pub fn as_abs(&self) -> BorrowRational {
let mut ret = BorrowRational {
inner: self.inner,
phantom: PhantomData,
};
let size = self.numer().inner().size.checked_abs().expect("overflow");
unsafe {
(*gmp::mpq_numref(&mut ret.inner)).size = size;
}
ret
}
pub fn as_recip(&self) -> BorrowRational {
let (self_num, self_den) = self.as_numer_denom();
let (self_num, self_den) = (self_num.inner(), self_den.inner());
assert_ne!(self_num.size, 0, "division by zero");
let mut ret = BorrowRational {
inner: unsafe { mem::uninitialized() },
phantom: PhantomData,
};
let (ret_num, ret_den) = unsafe {
let num = &mut *gmp::mpq_numref(&mut ret.inner);
let den = &mut *gmp::mpq_denref(&mut ret.inner);
(num, den)
};
*ret_num = *self_den;
*ret_den = *self_num;
if self_num.size < 0 {
ret_num.size = self_den.size.checked_neg().expect("overflow");
ret_den.size = self_num.size.checked_neg().expect("overflow");
}
ret
}
pub fn cmp0(&self) -> Ordering {
self.numer().cmp0()
}
math_op1! {
Rational;
gmp::mpq_abs;
fn abs();
fn abs_mut;
fn abs_ref -> AbsRef;
}
#[inline]
pub fn clamp<'a, 'b, Min, Max>(
mut self,
min: &'a Min,
max: &'b Max,
) -> Rational
where
Rational: PartialOrd<Min>
+ PartialOrd<Max>
+ Assign<&'a Min>
+ Assign<&'b Max>,
{
self.clamp_mut(min, max);
self
}
pub fn clamp_mut<'a, 'b, Min, Max>(&mut self, min: &'a Min, max: &'b Max)
where
Rational: PartialOrd<Min>
+ PartialOrd<Max>
+ Assign<&'a Min>
+ Assign<&'b Max>,
{
if (&*self).lt(min) {
self.assign(min);
assert!(!(&*self).gt(max), "minimum larger than maximum");
} else if (&*self).gt(max) {
self.assign(max);
assert!(!(&*self).lt(min), "minimum larger than maximum");
}
}
#[inline]
pub fn clamp_ref<'a, Min, Max>(
&'a self,
min: &'a Min,
max: &'a Max,
) -> ClampRef<'a, Min, Max>
where
Rational: PartialOrd<Min>
+ PartialOrd<Max>
+ Assign<&'a Min>
+ Assign<&'a Max>,
{
ClampRef {
ref_self: self,
min,
max,
}
}
math_op1! {
Rational;
xgmp::mpq_inv_check_0;
fn recip();
fn recip_mut;
fn recip_ref -> RecipRef;
}
#[inline]
pub fn ceil(self) -> Integer {
let (mut num, den) = self.into_numer_denom();
unsafe {
xgmp::num_den_ceil(num.inner_mut(), num.inner(), den.inner());
}
num
}
#[inline]
pub fn ceil_mut(&mut self) {
unsafe {
let (num, den) = self.as_mut_numer_denom_no_canonicalization();
xgmp::num_den_ceil(num.inner_mut(), num.inner(), den.inner());
den.assign(1);
}
}
#[inline]
pub fn ceil_ref(&self) -> CeilRef {
CeilRef { ref_self: self }
}
#[inline]
pub fn floor(self) -> Integer {
let (mut num, den) = self.into_numer_denom();
unsafe {
xgmp::num_den_floor(num.inner_mut(), num.inner(), den.inner());
}
num
}
#[inline]
pub fn floor_mut(&mut self) {
unsafe {
let (num, den) = self.as_mut_numer_denom_no_canonicalization();
xgmp::num_den_floor(num.inner_mut(), num.inner(), den.inner());
den.assign(1);
}
}
#[inline]
pub fn floor_ref(&self) -> FloorRef {
FloorRef { ref_self: self }
}
#[inline]
pub fn round(self) -> Integer {
let (mut num, den) = self.into_numer_denom();
unsafe {
xgmp::num_den_round(num.inner_mut(), num.inner(), den.inner());
}
num
}
#[inline]
pub fn round_mut(&mut self) {
unsafe {
let (num, den) = self.as_mut_numer_denom_no_canonicalization();
xgmp::num_den_round(num.inner_mut(), num.inner(), den.inner());
den.assign(1);
}
}
#[inline]
pub fn round_ref(&self) -> RoundRef {
RoundRef { ref_self: self }
}
#[inline]
pub fn trunc(self) -> Integer {
let (mut num, den) = self.into_numer_denom();
unsafe {
xgmp::num_den_trunc(num.inner_mut(), num.inner(), den.inner());
}
num
}
#[inline]
pub fn trunc_mut(&mut self) {
unsafe {
let (num, den) = self.as_mut_numer_denom_no_canonicalization();
xgmp::num_den_trunc(num.inner_mut(), num.inner(), den.inner());
den.assign(1);
}
}
#[inline]
pub fn trunc_ref(&self) -> TruncRef {
TruncRef { ref_self: self }
}
math_op1! {
Rational;
xgmp::mpq_fract;
fn fract();
fn fract_mut;
fn fract_ref -> FractRef;
}
#[inline]
pub fn fract_trunc(mut self, mut trunc: Integer) -> (Rational, Integer) {
self.fract_trunc_mut(&mut trunc);
(self, trunc)
}
#[inline]
pub fn fract_trunc_mut(&mut self, trunc: &mut Integer) {
unsafe {
xgmp::mpq_fract_trunc(
self.inner_mut(),
trunc.inner_mut(),
self.inner(),
);
}
}
#[inline]
pub fn fract_trunc_ref(&self) -> FractTruncRef {
FractTruncRef { ref_self: self }
}
#[inline]
pub fn fract_floor(mut self, mut floor: Integer) -> (Rational, Integer) {
self.fract_floor_mut(&mut floor);
(self, floor)
}
#[inline]
pub fn fract_floor_mut(&mut self, floor: &mut Integer) {
unsafe {
xgmp::mpq_fract_floor(
self.inner_mut(),
floor.inner_mut(),
self.inner(),
);
}
}
#[inline]
pub fn fract_floor_ref(&self) -> FractFloorRef {
FractFloorRef { ref_self: self }
}
#[inline]
pub fn fract_ceil(mut self, mut ceil: Integer) -> (Rational, Integer) {
self.fract_ceil_mut(&mut ceil);
(self, ceil)
}
#[inline]
pub fn fract_ceil_mut(&mut self, ceil: &mut Integer) {
unsafe {
xgmp::mpq_fract_ceil(
self.inner_mut(),
ceil.inner_mut(),
self.inner(),
);
}
}
#[inline]
pub fn fract_ceil_ref(&self) -> FractCeilRef {
FractCeilRef { ref_self: self }
}
}
ref_math_op1! { Rational; gmp::mpq_abs; struct AbsRef {} }
#[derive(Clone, Copy)]
pub struct ClampRef<'a, Min, Max>
where
Rational: PartialOrd<Min>
+ PartialOrd<Max>
+ Assign<&'a Min>
+ Assign<&'a Max>,
Min: 'a,
Max: 'a,
{
ref_self: &'a Rational,
min: &'a Min,
max: &'a Max,
}
impl<'a, Min, Max> From<ClampRef<'a, Min, Max>> for Rational
where
Rational: PartialOrd<Min>
+ PartialOrd<Max>
+ Assign<&'a Min>
+ Assign<&'a Max>,
Min: 'a,
Max: 'a,
{
#[inline]
fn from(t: ClampRef<'a, Min, Max>) -> Rational {
let mut ret = Rational::new();
ret.assign(t);
ret
}
}
impl<'a, Min, Max> Assign<ClampRef<'a, Min, Max>> for Rational
where
Rational: PartialOrd<Min>
+ PartialOrd<Max>
+ Assign<&'a Min>
+ Assign<&'a Max>,
Min: 'a,
Max: 'a,
{
#[inline]
fn assign(&mut self, src: ClampRef<'a, Min, Max>) {
if src.ref_self.lt(src.min) {
self.assign(src.min);
assert!(!(&*self).gt(src.max), "minimum larger than maximum");
} else if src.ref_self.gt(src.max) {
self.assign(src.max);
assert!(!(&*self).lt(src.min), "minimum larger than maximum");
} else {
self.assign(src.ref_self);
}
}
}
ref_math_op1! { Rational; xgmp::mpq_inv_check_0; struct RecipRef {} }
pub struct CeilRef<'a> {
ref_self: &'a Rational,
}
from_borrow! { CeilRef<'a> => Integer }
impl<'a> Assign<CeilRef<'a>> for Integer {
#[inline]
fn assign(&mut self, src: CeilRef<'a>) {
unsafe {
xgmp::mpq_ceil(self.inner_mut(), src.ref_self.inner());
}
}
}
pub struct FloorRef<'a> {
ref_self: &'a Rational,
}
from_borrow! { FloorRef<'a> => Integer }
impl<'a> Assign<FloorRef<'a>> for Integer {
#[inline]
fn assign(&mut self, src: FloorRef<'a>) {
unsafe {
xgmp::mpq_floor(self.inner_mut(), src.ref_self.inner());
}
}
}
pub struct RoundRef<'a> {
ref_self: &'a Rational,
}
from_borrow! { RoundRef<'a> => Integer }
impl<'a> Assign<RoundRef<'a>> for Integer {
#[inline]
fn assign(&mut self, src: RoundRef<'a>) {
unsafe {
xgmp::mpq_round(self.inner_mut(), src.ref_self.inner());
}
}
}
pub struct TruncRef<'a> {
ref_self: &'a Rational,
}
from_borrow! { TruncRef<'a> => Integer }
impl<'a> Assign<TruncRef<'a>> for Integer {
#[inline]
fn assign(&mut self, src: TruncRef<'a>) {
unsafe {
xgmp::mpq_trunc(self.inner_mut(), src.ref_self.inner());
}
}
}
ref_math_op1! { Rational; xgmp::mpq_fract; struct FractRef {} }
pub struct FractTruncRef<'a> {
ref_self: &'a Rational,
}
impl<'a> From<FractTruncRef<'a>> for (Rational, Integer) {
#[inline]
fn from(src: FractTruncRef<'a>) -> (Rational, Integer) {
let mut pair =
<(Rational, Integer) as Default>::default();
(&mut pair.0, &mut pair.1).assign(src);
pair
}
}
impl<'a> Assign<FractTruncRef<'a>> for (&'a mut Rational, &'a mut Integer) {
#[inline]
fn assign(&mut self, src: FractTruncRef<'a>) {
unsafe {
xgmp::mpq_fract_trunc(
self.0.inner_mut(),
self.1.inner_mut(),
src.ref_self.inner(),
);
}
}
}
pub struct FractFloorRef<'a> {
ref_self: &'a Rational,
}
impl<'a> From<FractFloorRef<'a>> for (Rational, Integer) {
#[inline]
fn from(src: FractFloorRef<'a>) -> (Rational, Integer) {
let mut pair =
<(Rational, Integer) as Default>::default();
(&mut pair.0, &mut pair.1).assign(src);
pair
}
}
impl<'a> Assign<FractFloorRef<'a>> for (&'a mut Rational, &'a mut Integer) {
#[inline]
fn assign(&mut self, src: FractFloorRef<'a>) {
unsafe {
xgmp::mpq_fract_floor(
self.0.inner_mut(),
self.1.inner_mut(),
src.ref_self.inner(),
);
}
}
}
pub struct FractCeilRef<'a> {
ref_self: &'a Rational,
}
impl<'a> From<FractCeilRef<'a>> for (Rational, Integer) {
#[inline]
fn from(src: FractCeilRef<'a>) -> (Rational, Integer) {
let mut pair =
<(Rational, Integer) as Default>::default();
(&mut pair.0, &mut pair.1).assign(src);
pair
}
}
impl<'a> Assign<FractCeilRef<'a>> for (&'a mut Rational, &'a mut Integer) {
#[inline]
fn assign(&mut self, src: FractCeilRef<'a>) {
unsafe {
xgmp::mpq_fract_ceil(
self.0.inner_mut(),
self.1.inner_mut(),
src.ref_self.inner(),
);
}
}
}
#[derive(Clone, Copy)]
pub struct BorrowRational<'a> {
inner: mpq_t,
phantom: PhantomData<&'a Rational>,
}
impl<'a> Deref for BorrowRational<'a> {
type Target = Rational;
#[inline]
fn deref(&self) -> &Rational {
let ptr = (&self.inner) as *const _ as *const _;
unsafe { &*ptr }
}
}
pub fn make_string(r: &Rational, radix: i32, to_upper: bool) -> String {
assert!(radix >= 2 && radix <= 36, "radix out of range");
let (num, den) = r.as_numer_denom();
let n_size = unsafe { gmp::mpz_sizeinbase(num.inner(), radix) };
let d_size = unsafe { gmp::mpz_sizeinbase(den.inner(), radix) };
let size = n_size.checked_add(d_size).unwrap().checked_add(3).unwrap();
let mut buf = Vec::<u8>::with_capacity(size);
let case_radix = if to_upper { -radix } else { radix };
unsafe {
buf.set_len(size);
gmp::mpq_get_str(
buf.as_mut_ptr() as *mut c_char,
case_radix as c_int,
r.inner(),
);
let nul_index = buf.iter().position(|&x| x == 0).unwrap();
buf.set_len(nul_index);
String::from_utf8_unchecked(buf)
}
}
#[derive(Clone, Debug)]
pub struct ValidRational<'a> {
bytes: &'a [u8],
radix: i32,
}
impl<'a> Assign<ValidRational<'a>> for Rational {
#[inline]
fn assign(&mut self, rhs: ValidRational) {
let mut v = Vec::<u8>::with_capacity(rhs.bytes.len() + 1);
v.extend_from_slice(rhs.bytes);
v.push(0);
let err = unsafe {
let c_str = CStr::from_bytes_with_nul_unchecked(&v);
gmp::mpq_set_str(self.inner_mut(), c_str.as_ptr(), rhs.radix.into())
};
assert_eq!(err, 0);
unsafe {
gmp::mpq_canonicalize(self.inner_mut());
}
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct ParseRationalError {
kind: ParseErrorKind,
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
enum ParseErrorKind {
InvalidDigit,
NoDigits,
NumerNoDigits,
DenomNoDigits,
TooManySlashes,
DenomZero,
}
impl Error for ParseRationalError {
fn description(&self) -> &str {
use self::ParseErrorKind::*;
match self.kind {
InvalidDigit => "invalid digit found in string",
NoDigits => "string has no digits",
NumerNoDigits => "string has no digits for numerator",
DenomNoDigits => "string has no digits for denominator",
TooManySlashes => "more than one / found in string",
DenomZero => "string has zero denominator",
}
}
}
pub struct MutNumerDenom<'a> {
num: &'a mut Integer,
den_place: &'a mut Integer,
den_actual: Integer,
}
impl<'a> MutNumerDenom<'a> {
#[inline]
pub fn num(&mut self) -> &mut Integer {
self.num
}
#[inline]
pub fn den(&mut self) -> &mut Integer {
&mut self.den_actual
}
#[inline]
pub fn num_den(&mut self) -> (&mut Integer, &mut Integer) {
(self.num, &mut self.den_actual)
}
}
impl<'a> Drop for MutNumerDenom<'a> {
#[inline]
fn drop(&mut self) {
assert_ne!(self.den_actual.cmp0(), Ordering::Equal, "division by zero");
unsafe {
mem::swap(&mut self.den_actual, self.den_place);
let rat_num = self.num.inner_mut();
let rat_den = self.den_place.inner_mut();
let mut canon: mpq_t = mem::uninitialized();
let canon_num_ptr = gmp::mpq_numref(&mut canon);
let canon_den_ptr = gmp::mpq_denref(&mut canon);
mem::swap(rat_num, &mut *canon_num_ptr);
mem::swap(rat_den, &mut *canon_den_ptr);
gmp::mpq_canonicalize(&mut canon);
mem::swap(rat_num, &mut *canon_num_ptr);
mem::swap(rat_den, &mut *canon_den_ptr);
}
}
}
impl Inner for Rational {
type Output = mpq_t;
#[inline]
fn inner(&self) -> &mpq_t {
&self.inner
}
}
impl InnerMut for Rational {
#[inline]
unsafe fn inner_mut(&mut self) -> &mut mpq_t {
&mut self.inner
}
}