#![warn(clippy::all, clippy::pedantic, clippy::cargo)]
use std::error;
use std::fmt;
use std::ops::Add;
use std::ops::Div;
use std::ops::Mul;
use std::ops::Sub;
pub const UNICODE_KAKTOVIK_NUMERAL_ZERO: char = '\u{1D2C0}';
pub const UNICODE_KAKTOVIK_NUMERAL_ONE: char = '\u{1D2C1}';
pub const UNICODE_KAKTOVIK_NUMERAL_TWO: char = '\u{1D2C2}';
pub const UNICODE_KAKTOVIK_NUMERAL_THREE: char = '\u{1D2C3}';
pub const UNICODE_KAKTOVIK_NUMERAL_FOUR: char = '\u{1D2C4}';
pub const UNICODE_KAKTOVIK_NUMERAL_FIVE: char = '\u{1D2C5}';
pub const UNICODE_KAKTOVIK_NUMERAL_SIX: char = '\u{1D2C6}';
pub const UNICODE_KAKTOVIK_NUMERAL_SEVEN: char = '\u{1D2C7}';
pub const UNICODE_KAKTOVIK_NUMERAL_EIGHT: char = '\u{1D2C8}';
pub const UNICODE_KAKTOVIK_NUMERAL_NINE: char = '\u{1D2C9}';
pub const UNICODE_KAKTOVIK_NUMERAL_TEN: char = '\u{1D2CA}';
pub const UNICODE_KAKTOVIK_NUMERAL_ELEVEN: char = '\u{1D2CB}';
pub const UNICODE_KAKTOVIK_NUMERAL_TWELVE: char = '\u{1D2CC}';
pub const UNICODE_KAKTOVIK_NUMERAL_THIRTEEN: char = '\u{1D2CD}';
pub const UNICODE_KAKTOVIK_NUMERAL_FOURTEEN: char = '\u{1D2CE}';
pub const UNICODE_KAKTOVIK_NUMERAL_FIFTEEN: char = '\u{1D2CF}';
pub const UNICODE_KAKTOVIK_NUMERAL_SIXTEEN: char = '\u{1D2D0}';
pub const UNICODE_KAKTOVIK_NUMERAL_SEVENTEEN: char = '\u{1D2D1}';
pub const UNICODE_KAKTOVIK_NUMERAL_EIGHTEEN: char = '\u{1D2D2}';
pub const UNICODE_KAKTOVIK_NUMERAL_NINETEEN: char = '\u{1D2D3}';
pub const KAKTOVIK_NUMERALS: [char; 20] = [
UNICODE_KAKTOVIK_NUMERAL_ZERO,
UNICODE_KAKTOVIK_NUMERAL_ONE,
UNICODE_KAKTOVIK_NUMERAL_TWO,
UNICODE_KAKTOVIK_NUMERAL_THREE,
UNICODE_KAKTOVIK_NUMERAL_FOUR,
UNICODE_KAKTOVIK_NUMERAL_FIVE,
UNICODE_KAKTOVIK_NUMERAL_SIX,
UNICODE_KAKTOVIK_NUMERAL_SEVEN,
UNICODE_KAKTOVIK_NUMERAL_EIGHT,
UNICODE_KAKTOVIK_NUMERAL_NINE,
UNICODE_KAKTOVIK_NUMERAL_TEN,
UNICODE_KAKTOVIK_NUMERAL_ELEVEN,
UNICODE_KAKTOVIK_NUMERAL_TWELVE,
UNICODE_KAKTOVIK_NUMERAL_THIRTEEN,
UNICODE_KAKTOVIK_NUMERAL_FOURTEEN,
UNICODE_KAKTOVIK_NUMERAL_FIFTEEN,
UNICODE_KAKTOVIK_NUMERAL_SIXTEEN,
UNICODE_KAKTOVIK_NUMERAL_SEVENTEEN,
UNICODE_KAKTOVIK_NUMERAL_EIGHTEEN,
UNICODE_KAKTOVIK_NUMERAL_NINETEEN,
];
pub type KaktovikResult<T> = Result<T, KaktovikErr>;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum KaktovikErr {
IllegalChar(char),
InputIsEmpty,
InputTooLong,
}
impl error::Error for KaktovikErr {
fn description(&self) -> &str {
match *self {
KaktovikErr::IllegalChar(_) => "Illegal char",
KaktovikErr::InputIsEmpty => "Input is empty",
KaktovikErr::InputTooLong => "Input too long",
}
}
fn cause(&self) -> Option<&dyn error::Error> {
None
}
}
impl fmt::Display for KaktovikErr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
KaktovikErr::IllegalChar(ref c) => write!(f, "Illegal character: {}", &c),
KaktovikErr::InputIsEmpty => write!(f, "Input is empty"),
KaktovikErr::InputTooLong => write!(f, "Input too long"),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct KaktovikNum {
n: usize,
}
impl KaktovikNum {
#[must_use]
pub fn new(n: usize) -> Self {
Self { n }
}
pub fn parse(s: &str) -> Result<Self, KaktovikErr> {
fn kaktovik_to_dec(digit: char) -> Result<usize, KaktovikErr> {
let index = KAKTOVIK_NUMERALS.iter().position(|&r| r == digit);
match index {
Some(i) => Ok(i),
None => Err(KaktovikErr::IllegalChar(digit)),
}
}
if s.is_empty() {
return Err(KaktovikErr::InputIsEmpty);
};
let mut return_val: usize = 0;
for (i, token) in s.chars().rev().enumerate() {
let Ok(n) = u32::try_from(i) else { return Err(KaktovikErr::InputTooLong) };
let dec_value: usize = kaktovik_to_dec(token)? * 20_usize.pow(n);
return_val += dec_value;
}
Ok(Self { n: return_val })
}
#[must_use]
pub fn to_decimal(&self) -> usize {
self.n
}
#[must_use]
pub fn to_kaktovik(&self) -> String {
let mut kaktovik_numeral = vec![];
let mut remainder = self.n;
while remainder > 0 {
let digit = remainder % 20;
kaktovik_numeral.push(KAKTOVIK_NUMERALS[digit]);
remainder /= 20;
}
kaktovik_numeral.iter().rev().collect::<String>()
}
}
impl Add for KaktovikNum {
type Output = Self;
fn add(self, other: Self) -> Self {
Self {
n: self.n + other.n,
}
}
}
impl Sub for KaktovikNum {
type Output = Self;
fn sub(self, other: Self) -> Self {
Self {
n: self.n - other.n,
}
}
}
impl Mul for KaktovikNum {
type Output = Self;
fn mul(self, other: Self) -> Self {
Self {
n: self.n * other.n,
}
}
}
impl Div for KaktovikNum {
type Output = Self;
fn div(self, other: Self) -> Self {
Self {
n: self.n / other.n,
}
}
}
impl fmt::Display for KaktovikNum {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.to_kaktovik())
}
}
#[macro_export]
macro_rules! kaktovik_string {
($($x:expr),+ $(,)?) => {
{
let v = vec![ $( $x ),* ];
v.iter().collect::<String>()
}
};
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new() {
let result = KaktovikNum::new(42);
assert_eq!(result, KaktovikNum { n: 42 });
}
#[test]
fn test_parse_good() {
let s = &kaktovik_string![
UNICODE_KAKTOVIK_NUMERAL_TWO,
UNICODE_KAKTOVIK_NUMERAL_TWO,
UNICODE_KAKTOVIK_NUMERAL_NINETEEN,
];
let result = KaktovikNum::parse(s).unwrap();
assert_eq!(result, KaktovikNum { n: 859 });
}
#[test]
fn test_parse_empty() {
let actual = KaktovikNum::parse("").err();
let expected = Some(KaktovikErr::InputIsEmpty);
assert_eq!(actual, expected);
}
#[test]
fn test_parse_failure() {
let actual = KaktovikNum::parse("a").err();
let expected = Some(KaktovikErr::IllegalChar('a'));
assert_eq!(actual, expected);
}
#[test]
fn test_to_kaktovik() {
let k = KaktovikNum::new(859);
let result = k.to_kaktovik();
let expected = kaktovik_string![
UNICODE_KAKTOVIK_NUMERAL_TWO,
UNICODE_KAKTOVIK_NUMERAL_TWO,
UNICODE_KAKTOVIK_NUMERAL_NINETEEN,
];
assert_eq!(result, expected);
}
#[test]
fn test_to_decimal() {
let actual: usize = 859;
let expected = KaktovikNum::new(actual).to_decimal();
assert_eq!(actual, expected);
}
#[test]
fn test_to_kaktovik_max() {
let k = KaktovikNum::new(usize::MAX);
let result = k.to_kaktovik();
let expected = kaktovik_string![
UNICODE_KAKTOVIK_NUMERAL_ELEVEN,
UNICODE_KAKTOVIK_NUMERAL_FIVE,
UNICODE_KAKTOVIK_NUMERAL_THREE,
UNICODE_KAKTOVIK_NUMERAL_ELEVEN,
UNICODE_KAKTOVIK_NUMERAL_NINETEEN,
UNICODE_KAKTOVIK_NUMERAL_SEVENTEEN,
UNICODE_KAKTOVIK_NUMERAL_ZERO,
UNICODE_KAKTOVIK_NUMERAL_SEVEN,
UNICODE_KAKTOVIK_NUMERAL_ELEVEN,
UNICODE_KAKTOVIK_NUMERAL_FOURTEEN,
UNICODE_KAKTOVIK_NUMERAL_FOUR,
UNICODE_KAKTOVIK_NUMERAL_THIRTEEN,
UNICODE_KAKTOVIK_NUMERAL_NINETEEN,
UNICODE_KAKTOVIK_NUMERAL_ZERO,
UNICODE_KAKTOVIK_NUMERAL_FIFTEEN,
];
assert_eq!(result, expected);
}
#[test]
fn test_add() {
let k1 = KaktovikNum::new(1);
let k2 = KaktovikNum::new(2);
let sum = k1 + k2;
assert_eq!(sum.n, 3);
}
#[test]
fn test_sub() {
let k1 = KaktovikNum::new(8);
let k2 = KaktovikNum::new(3);
let sum = k1 - k2;
assert_eq!(sum.n, 5);
}
#[test]
fn test_mul() {
let k1 = KaktovikNum::new(3);
let k2 = KaktovikNum::new(7);
let sum = k1 * k2;
assert_eq!(sum.n, 21);
}
#[test]
fn test_div() {
let k1 = KaktovikNum::new(21);
let k2 = KaktovikNum::new(3);
let sum = k1 / k2;
assert_eq!(sum.n, 7);
}
}