use crate::{
ext::{xmpq, xmpz},
integer::big as big_integer,
misc::{UnwrappedAs, UnwrappedCast},
Assign, Integer,
};
use az::{Cast, CheckedCast};
use core::{
cmp::Ordering,
fmt::{Display, Formatter, Result as FmtResult},
marker::PhantomData,
mem::{ManuallyDrop, MaybeUninit},
ops::{Add, AddAssign, Deref, Mul, MulAssign},
};
use gmp_mpfr_sys::gmp::{self, mpq_t};
use std::error::Error;
#[repr(transparent)]
pub struct Rational {
inner: mpq_t,
}
static_assert_same_layout!(Rational, mpq_t);
static_assert_same_layout!(BorrowRational<'_>, mpq_t);
macro_rules! ref_rat_op_int {
(
$func:path;
$(#[$attr_ref:meta])*
struct $Incomplete:ident { $($param:ident: $T:ty),* }
) => {
$(#[$attr_ref])*
#[derive(Debug)]
pub struct $Incomplete<'a> {
ref_self: &'a Rational,
$($param: $T,)*
}
impl Assign<$Incomplete<'_>> for Integer {
#[inline]
fn assign(&mut self, src: $Incomplete<'_>) {
$func(self, src.ref_self, $(src.$param),*);
}
}
from_assign! { $Incomplete<'_> => Integer }
};
}
macro_rules! ref_rat_op_rat_int {
(
$func:path;
$(#[$attr_ref:meta])*
struct $Incomplete:ident { $($param:ident: $T:ty),* }
) => {
$(#[$attr_ref])*
#[derive(Debug)]
pub struct $Incomplete<'a> {
ref_self: &'a Rational,
$($param: $T,)*
}
impl Assign<$Incomplete<'_>> for (&mut Rational, & mut Integer) {
#[inline]
fn assign(&mut self, src: $Incomplete<'_>) {
$func(self.0, self.1, src.ref_self, $(src.$param),*);
}
}
impl Assign<$Incomplete<'_>> for (Rational, Integer) {
#[inline]
fn assign(&mut self, src: $Incomplete<'_>) {
Assign::assign(&mut (&mut self.0, &mut self.1), src);
}
}
impl From<$Incomplete<'_>> for (Rational, Integer) {
#[inline]
fn from(src: $Incomplete<'_>) -> Self {
let mut dst = Self::default();
Assign::assign(&mut dst, src);
dst
}
}
};
}
impl Rational {
#[inline]
pub fn new() -> Self {
unsafe {
let mut ret = MaybeUninit::uninit();
gmp::mpq_init(cast_ptr_mut!(ret.as_mut_ptr(), mpq_t));
ret.assume_init()
}
}
#[inline]
pub const unsafe fn from_raw(raw: mpq_t) -> Self {
Rational { inner: raw }
}
#[inline]
pub fn into_raw(self) -> mpq_t {
let m = ManuallyDrop::new(self);
m.inner
}
#[inline]
pub fn as_raw(&self) -> *const mpq_t {
&self.inner
}
#[inline]
pub fn as_raw_mut(&mut self) -> *mut mpq_t {
&mut self.inner
}
#[inline]
pub fn from_f32(value: f32) -> Option<Self> {
value.checked_cast()
}
#[inline]
pub fn from_f64(value: f64) -> Option<Self> {
value.checked_cast()
}
#[inline]
pub fn from_str_radix(src: &str, radix: i32) -> Result<Self, ParseRationalError> {
Ok(Rational::from(Rational::parse_radix(src, radix)?))
}
#[inline]
pub fn parse<S: AsRef<[u8]>>(src: S) -> Result<ParseIncomplete, ParseRationalError> {
parse(src.as_ref(), 10)
}
#[inline]
pub fn parse_radix<S: AsRef<[u8]>>(
src: S,
radix: i32,
) -> Result<ParseIncomplete, ParseRationalError> {
parse(src.as_ref(), radix)
}
#[inline]
pub fn to_f32(&self) -> f32 {
self.cast()
}
#[inline]
pub fn to_f64(&self) -> f64 {
self.cast()
}
#[inline]
pub fn to_string_radix(&self, radix: i32) -> String {
let mut s = String::new();
append_to_string(&mut s, self, radix, false);
s
}
#[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() {
xmpq::set_f64(self, val);
Ok(())
} else {
Err(())
}
}
pub unsafe fn from_canonical<Num, Den>(num: Num, den: Den) -> Self
where
Integer: From<Num> + From<Den>,
{
let (num, den) = (Integer::from(num), Integer::from(den));
let mut dst = MaybeUninit::uninit();
xmpq::write_num_den_unchecked(&mut dst, num, den);
dst.assume_init()
}
pub unsafe fn assign_canonical<Num, Den>(&mut self, num: Num, den: Den)
where
Integer: Assign<Num> + Assign<Den>,
{
let (dst_num, dst_den) = self.as_mut_numer_denom_no_canonicalization();
dst_num.assign(num);
dst_den.assign(den);
}
#[inline]
pub fn numer(&self) -> &Integer {
xmpq::numref_const(self)
}
#[inline]
pub fn denom(&self) -> &Integer {
xmpq::denref_const(self)
}
pub fn mutate_numer_denom<F>(&mut self, func: F)
where
F: FnOnce(&mut Integer, &mut Integer),
{
unsafe {
let (num, den) = xmpq::numref_denref(self);
func(num, den);
}
xmpq::canonicalize(self);
}
#[inline]
pub unsafe fn as_mut_numer_denom_no_canonicalization(
&mut self,
) -> (&mut Integer, &mut Integer) {
xmpq::numref_denref(self)
}
#[inline]
pub fn into_numer_denom(self) -> (Integer, Integer) {
let raw = self.into_raw();
unsafe { (Integer::from_raw(raw.num), Integer::from_raw(raw.den)) }
}
pub fn as_neg(&self) -> BorrowRational<'_> {
let mut raw = self.inner;
raw.num.size = raw.num.size.checked_neg().expect("overflow");
unsafe { BorrowRational::from_raw(raw) }
}
pub fn as_abs(&self) -> BorrowRational<'_> {
let mut raw = self.inner;
raw.num.size = raw.num.size.checked_abs().expect("overflow");
unsafe { BorrowRational::from_raw(raw) }
}
pub fn as_recip(&self) -> BorrowRational<'_> {
assert_ne!(self.cmp0(), Ordering::Equal, "division by zero");
let mut raw = mpq_t {
num: self.inner.den,
den: self.inner.num,
};
if raw.den.size < 0 {
raw.den.size = raw.den.size.wrapping_neg();
raw.num.size = raw.num.size.checked_neg().expect("overflow");
}
unsafe { BorrowRational::from_raw(raw) }
}
#[inline]
pub fn cmp0(&self) -> Ordering {
self.numer().cmp0()
}
#[inline]
pub fn cmp_abs(&self, other: &Self) -> Ordering {
self.as_abs().cmp(&*other.as_abs())
}
#[inline]
pub fn sum<'a, I>(values: I) -> SumIncomplete<'a, I>
where
I: Iterator<Item = &'a Self>,
{
SumIncomplete { values }
}
#[inline]
pub fn dot<'a, I>(values: I) -> DotIncomplete<'a, I>
where
I: Iterator<Item = (&'a Self, &'a Self)>,
{
DotIncomplete { values }
}
#[inline]
pub fn product<'a, I>(values: I) -> ProductIncomplete<'a, I>
where
I: Iterator<Item = &'a Self>,
{
ProductIncomplete { values }
}
#[inline]
pub fn abs(mut self) -> Self {
self.abs_mut();
self
}
#[inline]
pub fn abs_mut(&mut self) {
xmpq::abs(self, ());
}
#[inline]
pub fn abs_ref(&self) -> AbsIncomplete<'_> {
AbsIncomplete { ref_self: self }
}
#[inline]
pub fn signum(mut self) -> Rational {
self.signum_mut();
self
}
#[inline]
pub fn signum_mut(&mut self) {
xmpq::signum(self);
}
#[inline]
pub fn signum_ref(&self) -> SignumIncomplete<'_> {
SignumIncomplete { ref_self: self }
}
#[inline]
pub fn clamp<Min, Max>(mut self, min: &Min, max: &Max) -> Self
where
Self: PartialOrd<Min> + PartialOrd<Max> + for<'a> Assign<&'a Min> + for<'a> Assign<&'a Max>,
{
self.clamp_mut(min, max);
self
}
pub fn clamp_mut<Min, Max>(&mut self, min: &Min, max: &Max)
where
Self: PartialOrd<Min> + PartialOrd<Max> + for<'a> Assign<&'a Min> + for<'a> Assign<&'a 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<'min, 'max, Min, Max>(
&self,
min: &'min Min,
max: &'max Max,
) -> ClampIncomplete<'_, 'min, 'max, Min, Max>
where
Self: PartialOrd<Min> + PartialOrd<Max> + for<'a> Assign<&'a Min> + for<'a> Assign<&'a Max>,
{
ClampIncomplete {
ref_self: self,
min,
max,
}
}
#[inline]
pub fn recip(mut self) -> Self {
self.recip_mut();
self
}
#[inline]
pub fn recip_mut(&mut self) {
xmpq::inv(self, ());
}
#[inline]
pub fn recip_ref(&self) -> RecipIncomplete<'_> {
RecipIncomplete { ref_self: self }
}
#[inline]
pub fn trunc(mut self) -> Rational {
self.trunc_mut();
self
}
#[inline]
pub fn trunc_mut(&mut self) {
xmpq::trunc(self);
}
#[inline]
pub fn trunc_ref(&self) -> TruncIncomplete<'_> {
TruncIncomplete { ref_self: self }
}
#[inline]
pub fn rem_trunc(mut self) -> Self {
self.rem_trunc_mut();
self
}
#[inline]
pub fn rem_trunc_mut(&mut self) {
xmpq::trunc_fract(self, ());
}
#[inline]
pub fn rem_trunc_ref(&self) -> RemTruncIncomplete<'_> {
RemTruncIncomplete { ref_self: self }
}
#[inline]
pub fn fract_trunc(mut self, mut trunc: Integer) -> (Self, Integer) {
self.fract_trunc_mut(&mut trunc);
(self, trunc)
}
#[inline]
pub fn fract_trunc_mut(&mut self, trunc: &mut Integer) {
xmpq::trunc_fract_whole(self, trunc, ());
}
#[inline]
pub fn fract_trunc_ref(&self) -> FractTruncIncomplete<'_> {
FractTruncIncomplete { ref_self: self }
}
#[inline]
pub fn ceil(mut self) -> Rational {
self.ceil_mut();
self
}
#[inline]
pub fn ceil_mut(&mut self) {
xmpq::ceil(self);
}
#[inline]
pub fn ceil_ref(&self) -> CeilIncomplete<'_> {
CeilIncomplete { ref_self: self }
}
#[inline]
pub fn rem_ceil(mut self) -> Self {
self.rem_ceil_mut();
self
}
#[inline]
pub fn rem_ceil_mut(&mut self) {
xmpq::ceil_fract(self, ());
}
#[inline]
pub fn rem_ceil_ref(&self) -> RemCeilIncomplete<'_> {
RemCeilIncomplete { ref_self: self }
}
#[inline]
pub fn fract_ceil(mut self, mut ceil: Integer) -> (Self, Integer) {
self.fract_ceil_mut(&mut ceil);
(self, ceil)
}
#[inline]
pub fn fract_ceil_mut(&mut self, ceil: &mut Integer) {
xmpq::ceil_fract_whole(self, ceil, ());
}
#[inline]
pub fn fract_ceil_ref(&self) -> FractCeilIncomplete<'_> {
FractCeilIncomplete { ref_self: self }
}
#[inline]
pub fn floor(mut self) -> Rational {
self.floor_mut();
self
}
#[inline]
pub fn floor_mut(&mut self) {
xmpq::floor(self);
}
#[inline]
pub fn floor_ref(&self) -> FloorIncomplete<'_> {
FloorIncomplete { ref_self: self }
}
#[inline]
pub fn rem_floor(mut self) -> Self {
self.rem_floor_mut();
self
}
#[inline]
pub fn rem_floor_mut(&mut self) {
xmpq::floor_fract(self, ());
}
#[inline]
pub fn rem_floor_ref(&self) -> RemFloorIncomplete<'_> {
RemFloorIncomplete { ref_self: self }
}
#[inline]
pub fn fract_floor(mut self, mut floor: Integer) -> (Self, Integer) {
self.fract_floor_mut(&mut floor);
(self, floor)
}
#[inline]
pub fn fract_floor_mut(&mut self, floor: &mut Integer) {
xmpq::floor_fract_whole(self, floor, ());
}
#[inline]
pub fn fract_floor_ref(&self) -> FractFloorIncomplete<'_> {
FractFloorIncomplete { ref_self: self }
}
#[inline]
pub fn round(mut self) -> Rational {
self.round_mut();
self
}
#[inline]
pub fn round_mut(&mut self) {
xmpq::round(self);
}
#[inline]
pub fn round_ref(&self) -> RoundIncomplete<'_> {
RoundIncomplete { ref_self: self }
}
#[inline]
pub fn rem_round(mut self) -> Self {
self.rem_round_mut();
self
}
#[inline]
pub fn rem_round_mut(&mut self) {
xmpq::round_fract(self, ());
}
#[inline]
pub fn rem_round_ref(&self) -> RemRoundIncomplete<'_> {
RemRoundIncomplete { ref_self: self }
}
#[inline]
pub fn fract_round(mut self, mut round: Integer) -> (Self, Integer) {
self.fract_round_mut(&mut round);
(self, round)
}
#[inline]
pub fn fract_round_mut(&mut self, round: &mut Integer) {
xmpq::round_fract_whole(self, round, ());
}
#[inline]
pub fn fract_round_ref(&self) -> FractRoundIncomplete<'_> {
FractRoundIncomplete { ref_self: self }
}
#[inline]
pub fn square(mut self) -> Self {
self.square_mut();
self
}
#[inline]
pub fn square_mut(&mut self) {
xmpq::square(self, ());
}
#[inline]
pub fn square_ref(&self) -> SquareIncomplete<'_> {
SquareIncomplete { ref_self: self }
}
}
#[derive(Debug)]
pub struct SumIncomplete<'a, I>
where
I: Iterator<Item = &'a Rational>,
{
values: I,
}
impl<'a, I> Assign<SumIncomplete<'a, I>> for Rational
where
I: Iterator<Item = &'a Self>,
{
fn assign(&mut self, mut src: SumIncomplete<'a, I>) {
match src.values.next() {
Some(first) => {
self.assign(first);
}
None => {
self.assign(0u32);
return;
}
}
self.add_assign(src);
}
}
impl<'a, I> From<SumIncomplete<'a, I>> for Rational
where
I: Iterator<Item = &'a Self>,
{
fn from(mut src: SumIncomplete<'a, I>) -> Self {
let mut dst = match src.values.next() {
Some(first) => first.clone(),
None => return Rational::new(),
};
dst.add_assign(src);
dst
}
}
impl<'a, I> Add<SumIncomplete<'a, I>> for Rational
where
I: Iterator<Item = &'a Self>,
{
type Output = Self;
#[inline]
fn add(mut self, rhs: SumIncomplete<'a, I>) -> Self {
self.add_assign(rhs);
self
}
}
impl<'a, I> AddAssign<SumIncomplete<'a, I>> for Rational
where
I: Iterator<Item = &'a Self>,
{
fn add_assign(&mut self, src: SumIncomplete<'a, I>) {
for i in src.values {
self.add_assign(i);
}
}
}
#[derive(Debug)]
pub struct DotIncomplete<'a, I>
where
I: Iterator<Item = (&'a Rational, &'a Rational)>,
{
values: I,
}
impl<'a, I> Assign<DotIncomplete<'a, I>> for Rational
where
I: Iterator<Item = (&'a Rational, &'a Rational)>,
{
fn assign(&mut self, mut src: DotIncomplete<'a, I>) {
match src.values.next() {
Some(first) => {
self.assign(first.0 * first.1);
}
None => {
self.assign(0u32);
return;
}
}
self.add_assign(src);
}
}
impl<'a, I> From<DotIncomplete<'a, I>> for Rational
where
I: Iterator<Item = (&'a Rational, &'a Rational)>,
{
fn from(mut src: DotIncomplete<'a, I>) -> Self {
let mut dst = match src.values.next() {
Some(first) => Rational::from(first.0 * first.1),
None => return Rational::new(),
};
dst.add_assign(src);
dst
}
}
impl<'a, I> Add<DotIncomplete<'a, I>> for Rational
where
I: Iterator<Item = (&'a Rational, &'a Rational)>,
{
type Output = Self;
#[inline]
fn add(mut self, rhs: DotIncomplete<'a, I>) -> Self {
self.add_assign(rhs);
self
}
}
impl<'a, I> AddAssign<DotIncomplete<'a, I>> for Rational
where
I: Iterator<Item = (&'a Rational, &'a Rational)>,
{
fn add_assign(&mut self, src: DotIncomplete<'a, I>) {
let mut mul = Rational::new();
for i in src.values {
#[allow(clippy::suspicious_op_assign_impl)]
mul.assign(i.0 * i.1);
AddAssign::add_assign(self, &mul);
}
}
}
#[derive(Debug)]
pub struct ProductIncomplete<'a, I>
where
I: Iterator<Item = &'a Rational>,
{
values: I,
}
impl<'a, I> Assign<ProductIncomplete<'a, I>> for Rational
where
I: Iterator<Item = &'a Self>,
{
fn assign(&mut self, mut src: ProductIncomplete<'a, I>) {
match src.values.next() {
Some(first) => {
self.assign(first);
}
None => {
self.assign(1u32);
return;
}
}
self.mul_assign(src);
}
}
impl<'a, I> From<ProductIncomplete<'a, I>> for Rational
where
I: Iterator<Item = &'a Self>,
{
fn from(mut src: ProductIncomplete<'a, I>) -> Self {
let mut dst = match src.values.next() {
Some(first) => first.clone(),
None => return Rational::from(1),
};
dst.mul_assign(src);
dst
}
}
impl<'a, I> Mul<ProductIncomplete<'a, I>> for Rational
where
I: Iterator<Item = &'a Self>,
{
type Output = Self;
#[inline]
fn mul(mut self, rhs: ProductIncomplete<'a, I>) -> Self {
self.mul_assign(rhs);
self
}
}
impl<'a, I> MulAssign<ProductIncomplete<'a, I>> for Rational
where
I: Iterator<Item = &'a Self>,
{
fn mul_assign(&mut self, mut src: ProductIncomplete<'a, I>) {
let mut other = match src.values.next() {
Some(next) => Rational::from(&*self * next),
None => return,
};
loop {
match src.values.next() {
Some(next) => {
self.assign(&other * next);
}
None => {
self.assign(other);
return;
}
}
match src.values.next() {
Some(next) => {
other.assign(&*self * next);
}
None => {
return;
}
}
if self.cmp0() == Ordering::Equal {
return;
}
}
}
}
ref_math_op1! { Rational; xmpq::abs; struct AbsIncomplete {} }
ref_rat_op_int! { xmpq::signum_int; struct SignumIncomplete {} }
#[derive(Debug)]
pub struct ClampIncomplete<'s, 'min, 'max, Min, Max>
where
Rational: PartialOrd<Min> + PartialOrd<Max> + for<'a> Assign<&'a Min> + for<'a> Assign<&'a Max>,
{
ref_self: &'s Rational,
min: &'min Min,
max: &'max Max,
}
impl<Min, Max> Assign<ClampIncomplete<'_, '_, '_, Min, Max>> for Rational
where
Self: PartialOrd<Min> + PartialOrd<Max> + for<'a> Assign<&'a Min> + for<'a> Assign<&'a Max>,
{
#[inline]
fn assign(&mut self, src: ClampIncomplete<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);
}
}
}
impl<Min, Max> From<ClampIncomplete<'_, '_, '_, Min, Max>> for Rational
where
Self: PartialOrd<Min> + PartialOrd<Max> + for<'a> Assign<&'a Min> + for<'a> Assign<&'a Max>,
{
#[inline]
fn from(src: ClampIncomplete<Min, Max>) -> Self {
let mut dst = Rational::new();
dst.assign(src);
dst
}
}
ref_math_op1! { Rational; xmpq::inv; struct RecipIncomplete {} }
ref_rat_op_int! { xmpq::trunc_int; struct TruncIncomplete {} }
ref_math_op1! { Rational; xmpq::trunc_fract; struct RemTruncIncomplete {} }
ref_rat_op_rat_int! { xmpq::trunc_fract_whole; struct FractTruncIncomplete {} }
ref_rat_op_int! { xmpq::ceil_int; struct CeilIncomplete {} }
ref_math_op1! { Rational; xmpq::ceil_fract; struct RemCeilIncomplete {} }
ref_rat_op_rat_int! { xmpq::ceil_fract_whole; struct FractCeilIncomplete {} }
ref_rat_op_int! { xmpq::floor_int; struct FloorIncomplete {} }
ref_math_op1! { Rational; xmpq::floor_fract; struct RemFloorIncomplete {} }
ref_rat_op_rat_int! { xmpq::floor_fract_whole; struct FractFloorIncomplete {} }
ref_rat_op_int! { xmpq::round_int; struct RoundIncomplete {} }
ref_math_op1! { Rational; xmpq::round_fract; struct RemRoundIncomplete {} }
ref_rat_op_rat_int! { xmpq::round_fract_whole; struct FractRoundIncomplete {} }
ref_math_op1! { Rational; xmpq::square; struct SquareIncomplete {} }
#[derive(Debug)]
#[repr(transparent)]
pub struct BorrowRational<'a> {
inner: ManuallyDrop<Rational>,
phantom: PhantomData<&'a Rational>,
}
impl BorrowRational<'_> {
pub(crate) const unsafe fn from_raw<'a>(raw: mpq_t) -> BorrowRational<'a> {
BorrowRational {
inner: ManuallyDrop::new(Rational { inner: raw }),
phantom: PhantomData,
}
}
}
impl Deref for BorrowRational<'_> {
type Target = Rational;
#[inline]
fn deref(&self) -> &Rational {
&*self.inner
}
}
pub(crate) fn append_to_string(s: &mut String, r: &Rational, radix: i32, to_upper: bool) {
let (num, den) = (r.numer(), r.denom());
let is_whole = *den == 1;
if !is_whole {
let cap_for_den_nul = big_integer::req_chars(den, radix, 2);
let cap = big_integer::req_chars(num, radix, cap_for_den_nul);
s.reserve(cap);
};
let reserved_ptr = s.as_ptr();
big_integer::append_to_string(s, num, radix, to_upper);
if !is_whole {
s.push('/');
big_integer::append_to_string(s, den, radix, to_upper);
debug_assert_eq!(reserved_ptr, s.as_ptr());
#[cfg(not(debug_assertions))]
{
let _ = reserved_ptr;
}
}
}
#[derive(Debug)]
pub struct ParseIncomplete {
is_negative: bool,
digits: Vec<u8>,
den_start: usize,
radix: i32,
}
impl Assign<ParseIncomplete> for Rational {
fn assign(&mut self, src: ParseIncomplete) {
let num_len = src.den_start;
if num_len == 0 {
xmpq::set_0(self);
return;
}
let den_len = src.digits.len() - num_len;
let num_str = src.digits.as_ptr();
unsafe {
let (num, den) = self.as_mut_numer_denom_no_canonicalization();
xmpz::realloc_for_mpn_set_str(num, num_len, src.radix);
let size = gmp::mpn_set_str(num.inner_mut().d, num_str, num_len, src.radix);
num.inner_mut().size = (if src.is_negative { -size } else { size }).unwrapped_cast();
if den_len == 0 {
xmpz::set_1(den);
return;
}
let den_str = num_str.offset(num_len.unwrapped_cast());
xmpz::realloc_for_mpn_set_str(den, den_len, src.radix);
let size = gmp::mpn_set_str(den.inner_mut().d, den_str, den_len, src.radix);
den.inner_mut().size = size.unwrapped_cast();
xmpq::canonicalize(self);
}
}
}
from_assign! { ParseIncomplete => Rational }
fn parse(bytes: &[u8], radix: i32) -> Result<ParseIncomplete, ParseRationalError> {
use self::{ParseErrorKind as Kind, ParseRationalError as Error};
assert!(radix >= 2 && radix <= 36, "radix out of range");
let bradix = radix.unwrapped_as::<u8>();
let mut digits = Vec::with_capacity(bytes.len() + 1);
let mut has_sign = false;
let mut is_negative = false;
let mut has_digits = false;
let mut den_start = None;
for &b in bytes {
if b == b'/' {
if den_start.is_some() {
return Err(Error {
kind: Kind::TooManySlashes,
});
}
if !has_digits {
return Err(Error {
kind: Kind::NumerNoDigits,
});
}
has_digits = false;
den_start = Some(digits.len());
continue;
}
let digit = match b {
b'+' if den_start.is_none() && !has_sign && !has_digits => {
has_sign = true;
continue;
}
b'-' if den_start.is_none() && !has_sign && !has_digits => {
is_negative = true;
has_sign = true;
continue;
}
b'_' if has_digits => continue,
b' ' | b'\t' | b'\n' | 0x0b | 0x0c | 0x0d => continue,
b'0'..=b'9' => b - b'0',
b'a'..=b'z' => b - b'a' + 10,
b'A'..=b'Z' => b - b'A' + 10,
_ => bradix,
};
if digit >= bradix {
return Err(Error {
kind: Kind::InvalidDigit,
});
}
has_digits = true;
if digit > 0 || (!digits.is_empty() && den_start != Some(digits.len())) {
digits.push(digit);
}
}
if !has_digits {
return Err(Error {
kind: if den_start.is_some() {
Kind::DenomNoDigits
} else {
Kind::NoDigits
},
});
}
if den_start == Some(digits.len()) {
return Err(Error {
kind: Kind::DenomZero,
});
}
let den_start = den_start.unwrap_or_else(|| digits.len());
Ok(ParseIncomplete {
is_negative,
digits,
den_start,
radix,
})
}
#[derive(Debug)]
pub struct ParseRationalError {
kind: ParseErrorKind,
}
#[derive(Debug)]
enum ParseErrorKind {
InvalidDigit,
NoDigits,
NumerNoDigits,
DenomNoDigits,
TooManySlashes,
DenomZero,
}
impl ParseRationalError {
fn desc(&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 Error for ParseRationalError {
fn description(&self) -> &str {
self.desc()
}
}
impl Display for ParseRationalError {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
Display::fmt(self.desc(), f)
}
}