use super::small_rational::SmallRational;
use super::xgmp;
use gmp_mpfr_sys::gmp::{self, mpq_t};
use inner::{Inner, InnerMut};
use integer::Integer;
use ops::{Assign, DivFromAssign, NegAssign, Pow, PowAssign, SubFromAssign};
use std::cmp::Ordering;
use std::error::Error;
use std::ffi::CStr;
use std::fmt::{self, Binary, Debug, Display, Formatter, LowerHex, Octal,
UpperHex};
use std::i32;
use std::mem;
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Shl,
ShlAssign, Shr, ShrAssign, Sub, SubAssign};
use std::os::raw::{c_char, c_int};
use std::str::FromStr;
pub struct Rational {
inner: mpq_t,
}
impl Default for Rational {
fn default() -> Rational {
Rational::new()
}
}
impl Clone for Rational {
fn clone(&self) -> Rational {
let mut ret = Rational::new();
ret.assign(self);
ret
}
fn clone_from(&mut self, source: &Rational) {
self.assign(source);
}
}
impl Drop for Rational {
fn drop(&mut self) {
unsafe {
gmp::mpq_clear(self.inner_mut());
}
}
}
impl Rational {
pub fn new() -> Rational {
let mut inner: mpq_t = unsafe { mem::uninitialized() };
unsafe {
gmp::mpq_init(&mut inner);
}
Rational { inner: inner }
}
pub fn from_f32(val: f32) -> Option<Rational> {
Rational::from_f64(val as f64)
}
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
}
}
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::ParseRationalError as Error;
use self::ParseErrorKind as Kind;
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: radix,
};
Ok(v)
}
pub fn to_integer(&self) -> Integer {
let mut i = Integer::new();
self.copy_to_integer(&mut i);
i
}
pub fn copy_to_integer(&self, i: &mut Integer) {
unsafe {
gmp::mpz_set_q(i.inner_mut(), self.inner());
}
}
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
}
}
pub fn to_f64(&self) -> f64 {
unsafe { gmp::mpq_get_d(self.inner()) }
}
pub fn to_string_radix(&self, radix: i32) -> String {
make_string(self, radix, false)
}
pub fn assign_f32(&mut self, val: f32) -> Result<(), ()> {
self.assign_f64(val as f64)
}
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(())
}
}
pub fn assign_str(&mut self, src: &str) -> Result<(), ParseRationalError> {
self.assign_str_radix(src, 10)
}
pub fn assign_str_radix(
&mut self,
src: &str,
radix: i32,
) -> Result<(), ParseRationalError> {
self.assign(Rational::valid_str_radix(src, radix)?);
Ok(())
}
pub fn numer(&self) -> &Integer {
unsafe {
let ptr = gmp::mpq_numref_const(self.inner());
&*(ptr as *const Integer)
}
}
pub fn denom(&self) -> &Integer {
unsafe {
let ptr = gmp::mpq_denref_const(self.inner());
&*(ptr as *const Integer)
}
}
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,
}
}
}
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 _),
)
}
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)
}
pub fn sign(&self) -> Ordering {
self.numer().sign()
}
math_op1! {
Rational;
gmp::mpq_abs;
fn abs();
fn abs_ref -> AbsRef;
}
math_op1! {
Rational;
xgmp::mpq_inv_check_0;
fn recip();
fn recip_ref -> RecipRef;
}
pub fn ceil_ref(&self) -> CeilRef {
CeilRef { ref_self: self }
}
pub fn floor_ref(&self) -> FloorRef {
FloorRef { ref_self: self }
}
pub fn round_ref(&self) -> RoundRef {
RoundRef { ref_self: self }
}
pub fn trunc_ref(&self) -> TruncRef {
TruncRef { ref_self: self }
}
math_op1! {
Rational;
xgmp::mpq_fract;
fn fract();
fn fract_ref -> FractRef;
}
pub fn fract_trunc(&mut self, trunc: &mut Integer) {
unsafe {
xgmp::mpq_fract_trunc(
self.inner_mut(),
trunc.inner_mut(),
self.inner(),
);
}
}
pub fn fract_trunc_ref(&self) -> FractTruncRef {
FractTruncRef { ref_self: self }
}
}
from_borrow! { &'a Rational => Rational }
impl From<Integer> for Rational {
fn from(val: Integer) -> Rational {
Rational::from((val, 1.into()))
}
}
from_borrow! { &'a Integer => Rational }
impl From<(Integer, Integer)> for Rational {
fn from((mut num, mut den): (Integer, Integer)) -> Rational {
assert_ne!(den.sign(), Ordering::Equal, "division by zero");
let mut dst: Rational = unsafe { mem::uninitialized() };
{
let mut num_den = dst.as_mut_numer_denom();
mem::swap(&mut num, num_den.num());
mem::swap(&mut den, num_den.den());
}
mem::forget(num);
mem::forget(den);
dst
}
}
from_borrow! { (&'a Integer, &'a Integer) => Rational }
macro_rules! from {
{ $Src:ty => $Dst:ty } => {
impl From<$Src> for $Dst {
fn from(t: $Src) -> $Dst {
let mut ret = <$Dst>::new();
ret.assign(t);
ret
}
}
}
}
from! { i32 => Rational }
from! { i64 => Rational }
from! { u32 => Rational }
from! { u64 => Rational }
from! { (i32, i32) => Rational }
from! { (i64, i64) => Rational }
from! { (i32, u32) => Rational }
from! { (i64, u64) => Rational }
from! { (u32, i32) => Rational }
from! { (u64, i64) => Rational }
from! { (u32, u32) => Rational }
from! { (u64, u64) => Rational }
impl FromStr for Rational {
type Err = ParseRationalError;
fn from_str(src: &str) -> Result<Rational, ParseRationalError> {
let mut r = Rational::new();
r.assign_str(src)?;
Ok(r)
}
}
impl Display for Rational {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
fmt_radix(self, f, 10, false, "")
}
}
impl Debug for Rational {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
fmt_radix(self, f, 10, false, "")
}
}
impl Binary for Rational {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
fmt_radix(self, f, 2, false, "0b")
}
}
impl Octal for Rational {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
fmt_radix(self, f, 8, false, "0o")
}
}
impl LowerHex for Rational {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
fmt_radix(self, f, 16, false, "0x")
}
}
impl UpperHex for Rational {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
fmt_radix(self, f, 16, true, "0x")
}
}
impl Assign for Rational {
fn assign(&mut self, mut other: Rational) {
self.assign(&other);
mem::swap(self, &mut other);
}
}
impl<'a> Assign<&'a Rational> for Rational {
fn assign(&mut self, other: &'a Rational) {
unsafe {
gmp::mpq_set(self.inner_mut(), other.inner());
}
}
}
impl<'a> Assign<&'a Integer> for Rational {
fn assign(&mut self, val: &'a Integer) {
unsafe {
gmp::mpq_set_z(self.inner_mut(), val.inner());
}
}
}
macro_rules! assign {
{ $T:ty } => {
impl Assign<$T> for Rational {
fn assign(&mut self, t: $T) {
let num_den =
unsafe { self.as_mut_numer_denom_no_canonicalization() };
num_den.0.assign(t);
num_den.1.assign(1);
}
}
};
}
assign!{ Integer }
assign!{ i32 }
assign!{ i64 }
assign!{ u32 }
assign!{ u64 }
impl<T, U> Assign<(T, U)> for Rational
where
Integer: Assign<T>,
Integer: Assign<U>,
{
fn assign(&mut self, (num, den): (T, U)) {
let mut num_den = self.as_mut_numer_denom();
num_den.num().assign(num);
num_den.den().assign(den);
}
}
ref_math_op1! { Rational; gmp::mpq_abs; struct AbsRef {} }
ref_math_op1! { Rational; xgmp::mpq_inv_check_0; struct RecipRef {} }
pub struct CeilRef<'a> {
ref_self: &'a Rational,
}
impl<'a> Assign<CeilRef<'a>> for Integer {
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,
}
impl<'a> Assign<FloorRef<'a>> for Integer {
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,
}
impl<'a> Assign<RoundRef<'a>> for Integer {
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,
}
impl<'a> Assign<TruncRef<'a>> for Integer {
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> Assign<FractTruncRef<'a>> for (&'a mut Rational, &'a mut Integer) {
fn assign(&mut self, src: FractTruncRef<'a>) {
unsafe {
xgmp::mpq_fract_trunc(
self.0.inner_mut(),
self.1.inner_mut(),
src.ref_self.inner(),
);
}
}
}
arith_unary! { Rational; gmp::mpq_neg; Neg neg; NegAssign neg_assign; NegRef }
arith_binary! { Rational; gmp::mpq_add; Add add; AddAssign add_assign; AddRef }
arith_noncommut! {
Rational;
gmp::mpq_sub;
Sub sub;
SubAssign sub_assign;
SubFromAssign sub_from_assign;
SubRef
}
arith_binary! { Rational; gmp::mpq_mul; Mul mul; MulAssign mul_assign; MulRef }
arith_noncommut! {
Rational;
gmp::mpq_div;
Div div;
DivAssign div_assign;
DivFromAssign div_from_assign;
DivRef
}
arith_prim! {
Rational;
xgmp::mpq_mul_2exp_si;
Shl shl;
ShlAssign shl_assign;
i32;
ShlRefI32
}
arith_prim! {
Rational;
xgmp::mpq_div_2exp_si;
Shr shr;
ShrAssign shr_assign;
i32;
ShrRefI32
}
arith_prim! {
Rational; xgmp::mpq_pow_si; Pow pow; PowAssign pow_assign; i32; PowRefI32
}
arith_prim! {
Rational; gmp::mpq_mul_2exp; Shl shl; ShlAssign shl_assign; u32; ShlRefU32
}
arith_prim! {
Rational; gmp::mpq_div_2exp; Shr shr; ShrAssign shr_assign; u32; ShrRefU32
}
arith_prim! {
Rational; xgmp::mpq_pow_ui; Pow pow; PowAssign pow_assign; u32; PowRefU32
}
impl Eq for Rational {}
impl Ord for Rational {
fn cmp(&self, other: &Rational) -> Ordering {
let ord = unsafe { gmp::mpq_cmp(self.inner(), other.inner()) };
ord.cmp(&0)
}
}
impl PartialEq for Rational {
fn eq(&self, other: &Rational) -> bool {
unsafe { gmp::mpq_equal(self.inner(), other.inner()) != 0 }
}
}
impl PartialOrd for Rational {
fn partial_cmp(&self, other: &Rational) -> Option<Ordering> {
Some(self.cmp(other))
}
}
macro_rules! cmp {
{ $T:ty, $eq:expr, $cmp:expr } => {
impl PartialEq<$T> for Rational {
fn eq(&self, other: &$T) -> bool {
$eq(self.inner(), other)
}
}
impl PartialEq<Rational> for $T {
fn eq(&self, other: &Rational) -> bool {
other.eq(self)
}
}
impl PartialOrd<$T> for Rational {
fn partial_cmp(&self, other: &$T) -> Option<Ordering> {
Some($cmp(self.inner(), other))
}
}
impl PartialOrd<Rational> for $T {
fn partial_cmp(&self, other: &Rational) -> Option<Ordering> {
other.partial_cmp(self).map(Ordering::reverse)
}
}
}
}
cmp! {
Integer,
|r, t: &Integer| unsafe { gmp::mpq_cmp_z(r, t.inner()) } == 0,
|r, t: &Integer| unsafe { gmp::mpq_cmp_z(r, t.inner()) }.cmp(&0)
}
cmp! {
i32,
|r, t: &i32| unsafe { gmp::mpq_cmp_si(r, (*t).into(), 1) } == 0,
|r, t: &i32| unsafe { gmp::mpq_cmp_si(r, (*t).into(), 1) }.cmp(&0)
}
cmp! {
u32,
|r, t: &u32| unsafe { gmp::mpq_cmp_ui(r, (*t).into(), 1) } == 0,
|r, t: &u32| unsafe { gmp::mpq_cmp_ui(r, (*t).into(), 1) }.cmp(&0)
}
macro_rules! cmp_small_rat {
{ $T:ty } => {
cmp! {
$T,
|r, t: &$T| unsafe {
gmp::mpq_equal(r, SmallRational::from(*t).inner())
} != 0,
|r, t: &$T| unsafe {
gmp::mpq_cmp(r, SmallRational::from(*t).inner())
}.cmp(&0)
}
};
}
cmp_small_rat! { i64 }
cmp_small_rat! { u64 }
cmp_small_rat! { (i32, i32) }
cmp_small_rat! { (i64, i64) }
cmp_small_rat! { (i32, u32) }
cmp_small_rat! { (i64, u64) }
cmp_small_rat! { (u32, i32) }
cmp_small_rat! { (u64, i64) }
cmp_small_rat! { (u32, u32) }
cmp_small_rat! { (u64, u64) }
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)
}
}
fn fmt_radix(
r: &Rational,
f: &mut Formatter,
radix: i32,
to_upper: bool,
prefix: &str,
) -> fmt::Result {
let s = make_string(r, radix, to_upper);
let (neg, buf) = if s.starts_with('-') {
(true, &s[1..])
} else {
(false, &s[..])
};
f.pad_integral(!neg, prefix, buf)
}
#[derive(Clone, Debug)]
pub struct ValidRational<'a> {
bytes: &'a [u8],
radix: i32,
}
from_borrow! { ValidRational<'a> => Rational }
impl<'a> Assign<ValidRational<'a>> for Rational {
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, Debug, Eq, PartialEq)]
pub struct ParseRationalError {
kind: ParseErrorKind,
}
#[derive(Clone, Debug, Eq, PartialEq)]
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",
}
}
}
impl Display for ParseRationalError {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Debug::fmt(self, f)
}
}
pub struct MutNumerDenom<'a> {
num: &'a mut Integer,
den_place: &'a mut Integer,
den_actual: Integer,
}
impl<'a> MutNumerDenom<'a> {
pub fn num(&mut self) -> &mut Integer {
self.num
}
pub fn den(&mut self) -> &mut Integer {
&mut self.den_actual
}
pub fn num_den(&mut self) -> (&mut Integer, &mut Integer) {
(self.num, &mut self.den_actual)
}
}
impl<'a> Drop for MutNumerDenom<'a> {
fn drop(&mut self) {
assert_ne!(self.den_actual.sign(), 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);
}
}
}
unsafe impl Send for Rational {}
unsafe impl Sync for Rational {}
impl Inner for Rational {
type Output = mpq_t;
fn inner(&self) -> &mpq_t {
&self.inner
}
}
impl InnerMut for Rational {
unsafe fn inner_mut(&mut self) -> &mut mpq_t {
&mut self.inner
}
}