pub trait NumberWidth {
fn signed_digit_count(&self, base: u64) -> i64;
fn signed_width(&self) -> u64 {
self.signed_width_base(10)
}
fn signed_width_base(&self, base: u64) -> u64 {
let count = match self.signed_digit_count(base) {
count if count < 0 => (-count + 1) as u64,
count => count as u64,
};
count.max(1)
}
fn width(&self) -> u64 {
self.width_base(10)
}
fn width_base(&self, base: u64) -> u64 {
(self.signed_digit_count(base).abs() as u64).max(1)
}
}
fn digit_count(num: u64, base: u64) -> u64 {
let mut width = 0;
let mut cur = num;
while cur > 0 {
cur /= base;
width += 1;
}
width
}
#[test]
fn test_digit_count() {
for base in 2..255 {
assert_eq!(digit_count(0, base), 0);
for num in 1..base {
assert_eq!(digit_count(num, base), 1);
}
let mut num: u64 = 1;
for i in 1.. {
num = match num.checked_mul(base) {
Some(num) => num,
None => break,
};
assert_eq!(digit_count(num - 1, base), i);
assert_eq!(digit_count(num, base), i + 1);
}
}
}
fn signed_digit_count(num: i64, base: u64) -> i64 {
let sign = num.signum();
let num = num.checked_abs().map(|num| num as u64).unwrap_or(i64::MAX as u64 + 1);
let digit_count = digit_count(num, base);
digit_count as i64 * sign
}
#[test]
fn test_signed_digit_count() {
for base in 2..255 {
assert_eq!(signed_digit_count(0, base), 0);
for num in 1..base {
assert_eq!(signed_digit_count(num as i64, base), 1);
}
let mut num: i64 = 1;
for i in 1.. {
num = match num.checked_mul(base as i64) {
Some(num) => num,
None => break,
};
assert_eq!(signed_digit_count(num - 1, base), i);
assert_eq!(signed_digit_count(num, base), i + 1);
}
let mut num: i64 = -1;
for i in 1.. {
num = match num.checked_mul(base as i64) {
Some(num) => num,
None => break,
};
assert_eq!(signed_digit_count(num + 1, base), -i);
assert_eq!(signed_digit_count(num, base), -i - 1);
}
}
}
#[test]
fn can_determine_width_u8() {
for num in 0..u8::MAX {
assert_eq!(num.width() as usize, num.to_string().len());
assert_eq!(num.signed_width() as usize, num.to_string().len());
}
}
#[test]
fn can_determine_width_u16() {
for num in 0..u16::MAX {
assert_eq!(num.width() as usize, num.to_string().len());
assert_eq!(num.signed_width() as usize, num.to_string().len());
}
}
#[test]
fn can_determine_width_u32() {
assert_eq!(0u32.width(), 1);
assert_eq!(10u32.width(), 2);
assert_eq!(100u32.width(), 3);
assert_eq!(0x0u32.width_base(16), 1);
assert_eq!(0xFu32.width_base(16), 1);
assert_eq!(0xFABu32.width_base(16), 3);
assert_eq!(0xCAFEu32.width_base(16), 4);
}
#[test]
fn can_determine_width_u64() {
assert_eq!(0u64.width(), 1);
assert_eq!(10u64.width(), 2);
assert_eq!(100u64.width(), 3);
assert_eq!(0x0u64.width_base(16), 1);
assert_eq!(0xFu64.width_base(16), 1);
assert_eq!(0xFABu64.width_base(16), 3);
assert_eq!(0xDEADBEEFu64.width_base(16), 8);
}
#[test]
fn can_determine_width_i8() {
for num in i8::MIN..i8::MAX {
assert_eq!(num.signed_width() as usize, num.to_string().len());
if num >= 0 {
assert_eq!(num.width() as usize, num.to_string().len());
}
}
}
#[test]
fn can_determine_width_i16() {
for num in i16::MIN..i16::MAX {
assert_eq!(num.signed_width() as usize, num.to_string().len());
if num >= 0 {
assert_eq!(num.width() as usize, num.to_string().len());
}
}
}
#[test]
fn can_determine_width_i32() {
assert_eq!(0i32.width(), 1);
assert_eq!(10i32.width(), 2);
assert_eq!(100i32.width(), 3);
assert_eq!((-233i32).width(), 3);
assert_eq!((-233i32).signed_width(), 4);
assert_eq!(0x0i32.width_base(16), 1);
assert_eq!(0xFi32.width_base(16), 1);
assert_eq!(0xFABi32.width_base(16), 3);
assert_eq!(0xCAFEi32.width_base(16), 4);
assert_eq!((-0xCAFEi32).signed_width_base(16), 5);
}
#[test]
fn can_determine_width_i64() {
assert_eq!(0i64.width(), 1);
assert_eq!(10i64.width(), 2);
assert_eq!(100i64.width(), 3);
assert_eq!((-233i64).width(), 3);
assert_eq!((-233i64).signed_width(), 4);
assert_eq!(0x0i64.width_base(16), 1);
assert_eq!(0xFi64.width_base(16), 1);
assert_eq!(0xFABi64.width_base(16), 3);
assert_eq!(0xCAFEi64.width_base(16), 4);
assert_eq!((-0xCAFEi64).signed_width_base(16), 5);
}
impl NumberWidth for u8 {
fn signed_digit_count(&self, base: u64) -> i64 {
digit_count((*self).into(), base) as i64
}
}
impl NumberWidth for u16 {
fn signed_digit_count(&self, base: u64) -> i64 {
digit_count((*self).into(), base) as i64
}
}
impl NumberWidth for u32 {
fn signed_digit_count(&self, base: u64) -> i64 {
digit_count((*self).into(), base) as i64
}
}
impl NumberWidth for u64 {
fn signed_digit_count(&self, base: u64) -> i64 {
digit_count(*self, base) as i64
}
}
impl NumberWidth for i8 {
fn signed_digit_count(&self, base: u64) -> i64 {
signed_digit_count((*self).into(), base)
}
}
impl NumberWidth for i16 {
fn signed_digit_count(&self, base: u64) -> i64 {
signed_digit_count((*self).into(), base)
}
}
impl NumberWidth for i32 {
fn signed_digit_count(&self, base: u64) -> i64 {
signed_digit_count((*self).into(), base)
}
}
impl NumberWidth for i64 {
fn signed_digit_count(&self, base: u64) -> i64 {
signed_digit_count(*self, base)
}
}