use crate::cast::cast;
use crate::ext::xmpq;
use crate::ext::xmpz;
use crate::integer::big as big_integer;
use crate::misc;
use crate::{Assign, Integer};
use gmp_mpfr_sys::gmp::{self, mpq_t};
use std::cmp::Ordering;
use std::error::Error;
use std::i32;
use std::marker::PhantomData;
use std::mem;
use std::ops::{Add, AddAssign, Deref, Mul, MulAssign};
use std::ptr;
#[repr(transparent)]
pub struct Rational {
inner: mpq_t,
}
fn _static_assertions() {
static_assert_size!(Rational, mpq_t);
static_assert_size!(BorrowRational<'_>, mpq_t);
}
macro_rules! rat_op_int {
(
$func:path;
$(#[$attr:meta])*
fn $method:ident($($param:ident: $T:ty),*);
$(#[$attr_mut:meta])*
fn $method_mut:ident;
$(#[$attr_ref:meta])*
fn $method_ref:ident -> $Incomplete:ident;
) => {
$(#[$attr])*
#[inline]
pub fn $method(mut self, $($param: $T),*) -> Rational {
self.$method_mut($($param),*);
self
}
$(#[$attr_mut])*
#[inline]
pub fn $method_mut(&mut self, $($param: $T),*) {
$func(None, Some(self), None, $($param),*);
}
$(#[$attr_ref])*
#[inline]
pub fn $method_ref(&self, $($param: $T),*) -> $Incomplete<'_> {
$Incomplete {
ref_self: self,
$($param,)*
}
}
};
}
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(Some(self), None, Some(src.ref_self), $(src.$param),*);
}
}
from_assign! { $Incomplete<'_> => Integer }
};
}
macro_rules! rat_op_rat_int {
(
$func:path;
$(#[$attr:meta])*
fn $method:ident($int:ident $(, $param:ident: $T:ty),*);
$(#[$attr_mut:meta])*
fn $method_mut:ident;
$(#[$attr_ref:meta])*
fn $method_ref:ident -> $Incomplete:ident;
) => {
$(#[$attr])*
#[inline]
pub fn $method(
mut self,
mut $int: Integer,
$($param: $T,)*
) -> (Self, Integer) {
self.$method_mut(&mut $int);
(self, $int)
}
$(#[$attr_mut])*
#[inline]
pub fn $method_mut(&mut self, $int: &mut Integer, $($param: $T),*) {
$func(self, $int, None, $($param),*);
}
$(#[$attr_ref])*
#[inline]
pub fn $method_ref(&self, $($param: $T),*) -> $Incomplete<'_> {
$Incomplete {
ref_self: self,
$($param,)*
}
}
};
}
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, Some(src.ref_self), $(src.$param),*);
}
}
impl Assign<$Incomplete<'_>> for (Rational, Integer) {
#[inline]
fn assign(&mut self, src: $Incomplete<'_>) {
<(&mut Rational, &mut Integer) as Assign<$Incomplete<'_>>>::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 as Default>::default();
<Self as Assign<$Incomplete<'_>>>::assign(&mut dst, src);
dst
}
}
};
}
impl Rational {
#[inline]
pub fn new() -> Self {
unsafe {
let mut ret: Rational = mem::uninitialized();
gmp::mpq_init(ret.as_raw_mut());
ret
}
}
#[inline]
pub unsafe fn from_raw(raw: mpq_t) -> Self {
Rational { inner: raw }
}
#[inline]
pub fn into_raw(self) -> mpq_t {
let ret = self.inner;
mem::forget(self);
ret
}
#[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> {
Rational::from_f64(value.into())
}
#[inline]
pub fn from_f64(value: f64) -> Option<Self> {
if value.is_finite() {
let mut r = Rational::new();
r.assign_f64(value).unwrap();
Some(r)
} else {
None
}
}
#[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>(src: S) -> Result<ParseIncomplete, ParseRationalError>
where
S: AsRef<[u8]>,
{
parse(src.as_ref(), 10)
}
#[inline]
pub fn parse_radix<S>(
src: S,
radix: i32,
) -> Result<ParseIncomplete, ParseRationalError>
where
S: AsRef<[u8]>,
{
parse(src.as_ref(), radix)
}
#[inline]
pub fn to_f32(&self) -> f32 {
misc::trunc_f64_to_f32(self.to_f64())
}
#[inline]
pub fn to_f64(&self) -> f64 {
unsafe { gmp::mpq_get_d(self.as_raw()) }
}
#[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() {
unsafe {
gmp::mpq_set_d(self.as_raw_mut(), val);
}
Ok(())
} else {
Err(())
}
}
pub unsafe fn from_canonical<Num, Den>(num: Num, den: Den) -> Self
where
Integer: From<Num> + From<Den>,
{
let mut dst: Rational = mem::uninitialized();
{
let (dnum, dden) = dst.as_mut_numer_denom_no_canonicalization();
ptr::write(dnum, Integer::from(num));
ptr::write(dden, Integer::from(den));
}
dst
}
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 {
unsafe { &*cast_ptr!(gmp::mpq_numref_const(self.as_raw()), Integer) }
}
#[inline]
pub fn denom(&self) -> &Integer {
unsafe { &*cast_ptr!(gmp::mpq_denref_const(self.as_raw()), Integer) }
}
pub fn mutate_numer_denom<F>(&mut self, func: F)
where
F: FnOnce(&mut Integer, &mut Integer),
{
unsafe {
let numer_ptr =
cast_ptr_mut!(gmp::mpq_numref(self.as_raw_mut()), Integer);
let denom_ptr =
cast_ptr_mut!(gmp::mpq_denref(self.as_raw_mut()), Integer);
func(&mut *numer_ptr, &mut *denom_ptr);
assert_ne!(
self.denom().cmp0(),
Ordering::Equal,
"division by zero"
);
gmp::mpq_canonicalize(self.as_raw_mut());
}
}
#[inline]
pub unsafe fn as_mut_numer_denom_no_canonicalization(
&mut self,
) -> (&mut Integer, &mut Integer) {
(
&mut *cast_ptr_mut!(gmp::mpq_numref(self.as_raw_mut()), Integer),
&mut *cast_ptr_mut!(gmp::mpq_denref(self.as_raw_mut()), Integer),
)
}
#[inline]
pub fn into_numer_denom(self) -> (Integer, Integer) {
let raw = self.into_raw();
unsafe {
let num = gmp::mpq_numref_const(&raw).read();
let den = gmp::mpq_denref_const(&raw).read();
(Integer::from_raw(num), Integer::from_raw(den))
}
}
#[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<'_> {
assert_ne!(self.cmp0(), Ordering::Equal, "division by zero");
let mut inner: mpq_t = unsafe { mem::uninitialized() };
unsafe {
let mut dst_num = self.denom().as_raw().read();
let mut dst_den = self.numer().as_raw().read();
if dst_den.size < 0 {
dst_den.size = dst_den.size.wrapping_neg();
dst_num.size = dst_num.size.checked_neg().expect("overflow");
}
gmp::mpq_numref(&mut inner).write(dst_num);
gmp::mpq_denref(&mut inner).write(dst_den);
}
BorrowRational { inner, phantom: PhantomData }
}
#[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 }
}
math_op1! {
xmpq::abs;
fn abs();
fn abs_mut;
fn abs_ref -> AbsIncomplete;
}
rat_op_int! {
xmpq::signum;
fn signum();
fn signum_mut;
fn signum_ref -> SignumIncomplete;
}
#[inline]
pub fn clamp<'a, 'b, Min, Max>(mut self, min: &'a Min, max: &'b Max) -> Self
where
Self: 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
Self: 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,
) -> ClampIncomplete<'a, Min, Max>
where
Self: PartialOrd<Min>
+ PartialOrd<Max>
+ Assign<&'a Min>
+ Assign<&'a Max>,
{
ClampIncomplete { ref_self: self, min, max }
}
math_op1! {
xmpq::inv;
fn recip();
fn recip_mut;
fn recip_ref -> RecipIncomplete;
}
rat_op_int! {
xmpq::trunc;
fn trunc();
fn trunc_mut;
fn trunc_ref -> TruncIncomplete;
}
math_op1! {
xmpq::trunc_fract;
fn rem_trunc();
fn rem_trunc_mut;
fn rem_trunc_ref -> RemTruncIncomplete;
}
rat_op_rat_int! {
xmpq::trunc_fract_whole;
fn fract_trunc(trunc);
fn fract_trunc_mut;
fn fract_trunc_ref -> FractTruncIncomplete;
}
rat_op_int! {
xmpq::ceil;
fn ceil();
fn ceil_mut;
fn ceil_ref -> CeilIncomplete;
}
math_op1! {
xmpq::ceil_fract;
fn rem_ceil();
fn rem_ceil_mut;
fn rem_ceil_ref -> RemCeilIncomplete;
}
rat_op_rat_int! {
xmpq::ceil_fract_whole;
fn fract_ceil(ceil);
fn fract_ceil_mut;
fn fract_ceil_ref -> FractCeilIncomplete;
}
rat_op_int! {
xmpq::floor;
fn floor();
fn floor_mut;
fn floor_ref -> FloorIncomplete;
}
math_op1! {
xmpq::floor_fract;
fn rem_floor();
fn rem_floor_mut;
fn rem_floor_ref -> RemFloorIncomplete;
}
rat_op_rat_int! {
xmpq::floor_fract_whole;
fn fract_floor(floor);
fn fract_floor_mut;
fn fract_floor_ref -> FractFloorIncomplete;
}
rat_op_int! {
xmpq::round;
fn round();
fn round_mut;
fn round_ref -> RoundIncomplete;
}
math_op1! {
xmpq::round_fract;
fn rem_round();
fn rem_round_mut;
fn rem_round_ref -> RemRoundIncomplete;
}
rat_op_rat_int! {
xmpq::round_fract_whole;
fn fract_round(round);
fn fract_round_mut;
fn fract_round_ref -> FractRoundIncomplete;
}
math_op1! {
xmpq::square;
fn square();
fn square_mut;
fn square_ref -> SquareIncomplete;
}
}
#[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; struct SignumIncomplete {} }
#[derive(Debug)]
pub struct ClampIncomplete<'a, Min, Max>
where
Rational:
PartialOrd<Min> + PartialOrd<Max> + Assign<&'a Min> + Assign<&'a Max>,
{
ref_self: &'a Rational,
min: &'a Min,
max: &'a Max,
}
impl<'a, Min, Max> Assign<ClampIncomplete<'a, Min, Max>> for Rational
where
Self: PartialOrd<Min> + PartialOrd<Max> + Assign<&'a Min> + Assign<&'a Max>,
{
#[inline]
fn assign(&mut self, src: ClampIncomplete<'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);
}
}
}
impl<'a, Min, Max> From<ClampIncomplete<'a, Min, Max>> for Rational
where
Self: PartialOrd<Min> + PartialOrd<Max> + Assign<&'a Min> + Assign<&'a Max>,
{
#[inline]
fn from(src: ClampIncomplete<'a, 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; 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; 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; 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; 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: mpq_t,
phantom: PhantomData<&'a Rational>,
}
impl Deref for BorrowRational<'_> {
type Target = Rational;
#[inline]
fn deref(&self) -> &Rational {
let ptr = cast_ptr!(&self.inner, Rational);
unsafe { &*ptr }
}
}
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;
let cap_for_den_nul =
if is_whole { 1 } else { big_integer::req_chars(den, radix, 2) };
let cap = big_integer::req_chars(num, radix, cap_for_den_nul);
s.reserve(cap);
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);
}
}
#[derive(Debug)]
pub struct ParseIncomplete {
is_negative: bool,
digits: Vec<u8>,
den_start: usize,
radix: i32,
}
impl Assign<ParseIncomplete> for Rational {
#[inline]
fn assign(&mut self, src: ParseIncomplete) {
let (str, n) = (src.digits.as_ptr(), src.den_start);
if n == 0 {
xmpq::set_0(self);
return;
}
unsafe {
let (num, den) = self.as_mut_numer_denom_no_canonicalization();
xmpz::realloc_for_mpn_set_str(num, n, src.radix);
let size = gmp::mpn_set_str(num.inner_mut().d, str, n, src.radix);
num.inner_mut().size =
cast(if src.is_negative { -size } else { size });
let (str, n) = (str.offset(cast(n)), src.digits.len() - n);
if n == 0 {
xmpz::set_1(den);
return;
}
xmpz::realloc_for_mpn_set_str(den, n, src.radix);
let size = gmp::mpn_set_str(den.inner_mut().d, str, n, src.radix);
den.inner_mut().size = cast(size);
}
unsafe {
gmp::mpq_canonicalize(self.as_raw_mut());
}
}
}
from_assign! { ParseIncomplete => Rational }
fn parse(
bytes: &[u8],
radix: i32,
) -> Result<ParseIncomplete, ParseRationalError> {
use self::ParseErrorKind as Kind;
use self::ParseRationalError as Error;
assert!(radix >= 2 && radix <= 36, "radix out of range");
let bradix: u8 = cast(radix);
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 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",
}
}
}