use std::ops::{BitAnd, ShrAssign};
macro_rules! impl_iter {
($iter:ident) => {
impl<N> $iter<N> {
#[cfg(debug_assertions)]
pub fn new(n: N) -> Self
where
N: Default + PartialOrd,
{
assert!(n >= N::default(), "n must not be negative");
$iter(n)
}
#[cfg(not(debug_assertions))]
pub fn new(n: N) -> Self {
$iter(n)
}
}
impl<N> Iterator for $iter<N>
where
N: Clone + From<u8> + BitAnd<Output = N> + ShrAssign + PartialEq,
{
type Item = char;
fn next(&mut self) -> Option<Self::Item> {
if self.0 == 0.into() {
return None;
}
let digit = self.0.clone() & Self::MASK.into();
self.0 >>= Self::WIDTH.into();
for maybe_digit in 0..=Self::MASK {
if digit == maybe_digit.into() {
if maybe_digit < 10 {
return Some((maybe_digit + b'0') as char);
} else {
return Some((maybe_digit - 10 + b'a') as char);
}
}
}
panic!(
"no digit matched when computing {}",
std::any::type_name::<$iter<N>>()
);
}
}
};
}
pub struct BinIter<N>(N);
impl<N> BinIter<N> {
const WIDTH: u8 = 1;
const MASK: u8 = 1;
}
impl_iter!(BinIter);
pub struct OctIter<N>(N);
impl<N> OctIter<N> {
const WIDTH: u8 = 3;
const MASK: u8 = 0b0111;
}
impl_iter!(OctIter);
pub struct HexIter<N>(N);
impl<N> HexIter<N> {
const WIDTH: u8 = 4;
const MASK: u8 = 0b1111;
}
impl_iter!(HexIter);
pub struct DecIter(Vec<char>);
impl DecIter {
pub fn new<N>(n: N) -> (DecIter, Option<DecIter>)
where
N: ToString,
{
let s = n.to_string();
debug_assert!(s.chars().all(|c| c == '.' || ('0'..='9').contains(&c)));
debug_assert!(s.chars().filter(|&c| c == '.').count() <= 1);
let mut found_decimal = false;
let (left, mut right): (Vec<_>, Vec<_>) = s.chars().partition(|&c| {
found_decimal |= c == '.';
!found_decimal
});
right.reverse();
right.pop();
let right = if right.is_empty() || right == ['0'] {
None
} else {
Some(DecIter(right))
};
(DecIter(left), right)
}
}
impl Iterator for DecIter {
type Item = char;
fn next(&mut self) -> Option<Self::Item> {
self.0.pop()
}
}
macro_rules! impl_for {
(unsigned_int $type:ident) => {
mod $type {
use super::{BinIter, DecIter, HexIter, OctIter};
use crate::Numeric;
impl Numeric for $type {
type BinIter = BinIter<$type>;
type OctIter = OctIter<$type>;
type DecLeftIter = DecIter;
type DecRightIter = DecIter;
type HexIter = HexIter<$type>;
fn binary(&self) -> Option<Self::BinIter> {
Some(BinIter::new(*self))
}
fn octal(&self) -> Option<Self::OctIter> {
Some(OctIter::new(*self))
}
fn hex(&self) -> Option<Self::HexIter> {
Some(HexIter::new(*self))
}
fn decimal(&self) -> (Self::DecLeftIter, Option<Self::DecRightIter>) {
DecIter::new(*self)
}
fn is_negative(&self) -> bool {
false
}
}
}
};
(signed_int $type:ident) => {
mod $type {
use super::{BinIter, DecIter, HexIter, OctIter};
use crate::Numeric;
impl Numeric for $type {
type BinIter = BinIter<$type>;
type OctIter = OctIter<$type>;
type DecLeftIter = DecIter;
type DecRightIter = DecIter;
type HexIter = HexIter<$type>;
fn binary(&self) -> Option<Self::BinIter> {
Some(BinIter::new(*self))
}
fn octal(&self) -> Option<Self::OctIter> {
Some(OctIter::new(*self))
}
fn hex(&self) -> Option<Self::HexIter> {
Some(HexIter::new(*self))
}
fn decimal(&self) -> (Self::DecLeftIter, Option<Self::DecRightIter>) {
DecIter::new(self.abs())
}
fn is_negative(&self) -> bool {
*self < 0
}
}
}
};
(float $type:ident) => {
mod $type {
use super::DecIter;
use crate::Numeric;
use std::iter::Empty;
impl Numeric for $type {
type BinIter = Empty<char>;
type OctIter = Empty<char>;
type DecLeftIter = DecIter;
type DecRightIter = DecIter;
type HexIter = Empty<char>;
fn binary(&self) -> Option<Self::BinIter> {
None
}
fn octal(&self) -> Option<Self::OctIter> {
None
}
fn hex(&self) -> Option<Self::HexIter> {
None
}
fn decimal(&self) -> (Self::DecLeftIter, Option<Self::DecRightIter>) {
DecIter::new(self.abs())
}
fn is_negative(&self) -> bool {
*self < 0.0
}
}
}
};
}
impl_for!(unsigned_int u8);
impl_for!(unsigned_int u16);
impl_for!(unsigned_int u32);
impl_for!(unsigned_int u64);
impl_for!(unsigned_int u128);
impl_for!(unsigned_int usize);
impl_for!(signed_int i16);
impl_for!(signed_int i32);
impl_for!(signed_int i64);
impl_for!(signed_int i128);
impl_for!(signed_int isize);
impl_for!(float f32);
impl_for!(float f64);
#[cfg(test)]
mod tests {
macro_rules! suite_for {
(dec: int $( $type_int:ident ),+ ; float $( $type_float:ident ),+ ) => {
#[allow(non_snake_case)]
mod DecIter {
use super::super::DecIter as Iter;
use std::convert::TryInto;
$(
#[test]
fn $type_int() {
for n in 1..=$type_int::MAX.min(1024.try_into().unwrap_or($type_int::MAX)) {
let expect = format!("{}", n);
let (left, right) = Iter::new(n);
assert!(right.is_none(), "ints should not have a fractional part");
let mut digits: Vec<_> = left.map(|d| d.to_string()).collect();
digits.reverse();
let actual = digits.join("");
dbg!(&actual, &expect);
assert_eq!(actual, expect);
}
}
)+
$(
#[test]
fn $type_float() {
for n in 1..=1024 {
let n = n as $type_float / 20.0;
let expect = format!("{}", n);
let (left, right) = Iter::new(n);
let mut actual: Vec<_> = left.map(|d| d.to_string()).collect();
actual.reverse();
if let Some(right) = right {
actual.push('.'.into());
actual.extend(right.map(|d| d.to_string()));
}
let actual = actual.join("");
dbg!(&actual, &expect);
assert_eq!(actual, expect);
}
}
)+
}
};
($iter:ident, $fmt:literal, $( $test_type:ident ),+) => {
#[allow(non_snake_case)]
mod $iter {
use super::super::$iter as Iter;
use std::convert::TryInto;
$(
#[test]
fn $test_type() {
for n in 1..=$test_type::MAX.min(1024.try_into().unwrap_or($test_type::MAX)) {
let expect = format!($fmt, n);
let mut digits: Vec<_> = Iter::new(n).map(|d| d.to_string()).collect();
digits.reverse();
let actual = digits.join("");
dbg!(&actual, &expect);
assert_eq!(actual, expect);
}
}
)+
}
};
}
suite_for!(BinIter, "{:b}", u8, u16, u32, u64, u128, usize, i16, i32, i64, i128, isize);
suite_for!(OctIter, "{:o}", u8, u16, u32, u64, u128, usize, i16, i32, i64, i128, isize);
suite_for!(HexIter, "{:x}", u8, u16, u32, u64, u128, usize, i16, i32, i64, i128, isize);
suite_for!(dec: int u8, u16, u32, u64, u128, usize, i16, i32, i64, i128, isize; float f32, f64);
}