use core::{
cmp::{max, min},
ops::{AddAssign, DivAssign, MulAssign, SubAssign},
};
use num_traits::{Bounded, CheckedAdd, CheckedMul, CheckedSub, One, Zero};
use crate::{
FromDigit, FromRadix10, FromRadix10Checked, FromRadix10Signed, FromRadix10SignedChecked,
FromRadix16, FromRadix16Checked, Sign,
};
pub struct Integer<I>(pub I);
impl<I> FromRadix10 for Integer<I>
where
I: Zero + One + AddAssign + MulAssign,
{
fn from_radix_10(text: &[u8]) -> (Self, usize) {
let mut index = 0;
let mut number = I::zero();
while index != text.len() {
if let Some(digit) = Integer::<I>::from_digit(text[index]) {
number *= nth(10);
number += digit.0;
index += 1;
} else {
break;
}
}
(Integer(number), index)
}
}
impl<I> FromRadix10Signed for Integer<I>
where
I: Zero + One + AddAssign + SubAssign + MulAssign,
{
fn from_radix_10_signed(text: &[u8]) -> (Self, usize) {
let mut index;
let mut number = I::zero();
let (sign, offset) = text
.first()
.and_then(|&byte| Sign::try_from(byte))
.map(|sign| (sign, 1))
.unwrap_or((Sign::Plus, 0));
index = offset;
match sign {
Sign::Plus => {
while index != text.len() {
if let Some(digit) = Integer::<I>::from_digit(text[index]) {
number *= nth(10);
number += digit.0;
index += 1;
} else {
break;
}
}
}
Sign::Minus => {
while index != text.len() {
if let Some(digit) = Integer::<I>::from_digit(text[index]) {
number *= nth(10);
number -= digit.0;
index += 1;
} else {
break;
}
}
}
}
(Integer(number), index)
}
}
impl<I> FromDigit for Integer<I>
where
I: Zero + One,
{
fn from_digit(digit: u8) -> Option<Self> {
match digit {
b'0' => Some(Integer(nth(0))),
b'1' => Some(Integer(nth(1))),
b'2' => Some(Integer(nth(2))),
b'3' => Some(Integer(nth(3))),
b'4' => Some(Integer(nth(4))),
b'5' => Some(Integer(nth(5))),
b'6' => Some(Integer(nth(6))),
b'7' => Some(Integer(nth(7))),
b'8' => Some(Integer(nth(8))),
b'9' => Some(Integer(nth(9))),
_ => None,
}
}
}
fn nth<I>(n: u8) -> I
where
I: Zero + One,
{
let mut i = I::zero();
for _ in 0..n {
i = i + I::one();
}
i
}
impl<I> FromRadix10SignedChecked for Integer<I>
where
I: Zero
+ One
+ AddAssign
+ MulAssign
+ SubAssign
+ CheckedAdd
+ CheckedSub
+ CheckedMul
+ MaxNumDigits,
{
fn from_radix_10_signed_checked(text: &[u8]) -> (Option<Self>, usize) {
let mut index;
let mut number = I::zero();
let (sign, offset) = text
.first()
.and_then(|&byte| Sign::try_from(byte))
.map(|sign| (sign, 1))
.unwrap_or((Sign::Plus, 0));
index = offset;
match sign {
Sign::Plus => {
let max_safe_digits = max(1, I::max_num_digits(nth(10))) - 1;
let max_safe_index = min(text.len(), max_safe_digits + offset);
while index != max_safe_index {
if let Some(digit) = Integer::<I>::from_digit(text[index]) {
number *= nth(10);
number += digit.0;
index += 1;
} else {
break;
}
}
let mut number = Some(number);
while index != text.len() {
if let Some(digit) = Integer::<I>::from_digit(text[index]) {
number = number.and_then(|n| n.checked_mul(&nth(10)));
number = number.and_then(|n| n.checked_add(&digit.0));
index += 1;
} else {
break;
}
}
(number.map(Integer), index)
}
Sign::Minus => {
let max_safe_digits = max(1, I::max_num_digits_negative(nth(10))) - 1;
let max_safe_index = min(text.len(), max_safe_digits + offset);
while index != max_safe_index {
if let Some(digit) = Integer::<I>::from_digit(text[index]) {
number *= nth(10);
number -= digit.0;
index += 1;
} else {
break;
}
}
let mut number = Some(number);
while index != text.len() {
if let Some(digit) = Integer::<I>::from_digit(text[index]) {
number = number.and_then(|n| n.checked_mul(&nth(10)));
number = number.and_then(|n| n.checked_sub(&digit.0));
index += 1;
} else {
break;
}
}
(number.map(Integer), index)
}
}
}
}
impl<I> FromRadix10Checked for Integer<I>
where
I: Zero + One + FromRadix10 + CheckedMul + CheckedAdd + MaxNumDigits,
{
fn from_radix_10_checked(text: &[u8]) -> (Option<Self>, usize) {
let max_safe_digits = max(1, I::max_num_digits_negative(nth(10))) - 1;
let (number, mut index) = I::from_radix_10(&text[..min(text.len(), max_safe_digits)]);
let mut number = Some(number);
while index != text.len() {
if let Some(digit) = Integer::<I>::from_digit(text[index]) {
number = number.and_then(|n| n.checked_mul(&nth(10)));
number = number.and_then(|n| n.checked_add(&digit.0));
index += 1;
} else {
break;
}
}
(number.map(Integer), index)
}
}
fn ascii_to_hexdigit<I>(character: u8) -> Option<I>
where
I: Zero + One,
{
match character.to_ascii_uppercase() {
b'0' => Some(nth(0)),
b'1' => Some(nth(1)),
b'2' => Some(nth(2)),
b'3' => Some(nth(3)),
b'4' => Some(nth(4)),
b'5' => Some(nth(5)),
b'6' => Some(nth(6)),
b'7' => Some(nth(7)),
b'8' => Some(nth(8)),
b'9' => Some(nth(9)),
b'A' => Some(nth(10)),
b'B' => Some(nth(11)),
b'C' => Some(nth(12)),
b'D' => Some(nth(13)),
b'E' => Some(nth(14)),
b'F' => Some(nth(15)),
_ => None,
}
}
impl<I> FromRadix16 for Integer<I>
where
I: Zero + One + AddAssign + MulAssign,
{
fn from_radix_16(text: &[u8]) -> (Self, usize) {
let mut index = 0;
let mut number = I::zero();
while index != text.len() {
if let Some(digit) = ascii_to_hexdigit(text[index]) {
number *= nth(16);
number += digit;
index += 1;
} else {
break;
}
}
(Integer(number), index)
}
}
impl<I> FromRadix16Checked for Integer<I>
where
I: Zero + One + FromRadix16 + CheckedMul + CheckedAdd + MaxNumDigits,
{
fn from_radix_16_checked(text: &[u8]) -> (Option<Self>, usize) {
let max_safe_digits = max(1, I::max_num_digits_negative(nth(16))) - 1;
let (number, mut index) = I::from_radix_16(&text[..min(text.len(), max_safe_digits)]);
let mut number = Some(number);
while index != text.len() {
if let Some(digit) = ascii_to_hexdigit(text[index]) {
number = number.and_then(|n| n.checked_mul(&nth(16)));
number = number.and_then(|n| n.checked_add(&digit));
index += 1;
} else {
break;
}
}
(number.map(Integer), index)
}
}
trait MaxNumDigits {
fn max_num_digits(radix: Self) -> usize;
fn max_num_digits_negative(radix: Self) -> usize;
}
impl<I> MaxNumDigits for Integer<I>
where
I: Bounded + Zero + DivAssign + Ord + Copy,
{
fn max_num_digits(radix: Integer<I>) -> usize {
let mut max = I::max_value();
let mut d = 0;
while max > I::zero() {
d += 1;
max /= radix.0;
}
d
}
fn max_num_digits_negative(radix: Integer<I>) -> usize {
let mut min = I::min_value();
let mut d = 0;
while min < I::zero() {
d += 1;
min /= radix.0;
}
d
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn max_digits() {
assert_eq!(10, Integer::<i32>::max_num_digits(Integer(10)));
assert_eq!(10, Integer::<u32>::max_num_digits(Integer(10)));
assert_eq!(19, Integer::<i64>::max_num_digits(Integer(10)));
assert_eq!(20, Integer::<u64>::max_num_digits(Integer(10)));
assert_eq!(3, Integer::<u8>::max_num_digits(Integer(10)));
assert_eq!(3, Integer::<i8>::max_num_digits(Integer(10)));
}
#[test]
fn max_digits_negative() {
assert_eq!(10, Integer::<i32>::max_num_digits_negative(Integer(10)));
assert_eq!(0, Integer::<u32>::max_num_digits_negative(Integer(10)));
assert_eq!(19, Integer::<i64>::max_num_digits_negative(Integer(10)));
assert_eq!(0, Integer::<u64>::max_num_digits_negative(Integer(10)));
assert_eq!(0, Integer::<u8>::max_num_digits_negative(Integer(10)));
assert_eq!(3, Integer::<i8>::max_num_digits_negative(Integer(10)));
}
}