use std::fmt::{Display, Formatter};
use super::consts::*;
pub const BASE_10_DOT: char = DC_OVER_DOT_1;
pub const BASE_10_LINE: char = DC_OVER_LINE;
pub const BASE_12_DOT: char = DC_UNDER_DOT_1;
pub const BASE_12_LINE: char = DC_UNDER_LINE_H;
pub const MOD_UNITS: char = DC_UNDER_RING;
pub const PREF_DEC_IN: char = '#';
pub const SUFF_ORD_IN: char = '@';
pub const SUFF_SEQ_IN: char = '#';
type Digit = u8;
struct Digits {
negative: bool,
digits: Vec<Digit>,
}
impl Digits {
fn zero() -> Self {
Self {
negative: false,
digits: vec![0],
}
}
fn get(n: isize, base: Digit) -> Self {
if n == 0 {
Self::zero()
} else {
let negative: bool = n.is_negative();
let mut n: usize = n.unsigned_abs();
let mut digits = Vec::new();
while n != 0 {
digits.push((n % base as usize) as _);
n /= base as usize;
}
Self { negative, digits }
}
}
fn decimal(value: isize) -> Self {
Self::get(value, 10)
}
fn duodecimal(value: isize) -> Self {
Self::get(value, 12)
}
fn size(&self) -> usize {
self.negative as usize + self.digits.len() * 6
}
}
pub const fn find_index(slice: &[char]) -> Option<(char, usize)> {
let value: usize;
let chars: usize;
match slice {
[c10 @ '0'..='9', c01 @ '0'..='9', SUFF_SEQ_IN, ..] => {
let ones: usize = *c01 as usize - '0' as usize;
let tens: usize = *c10 as usize - '0' as usize;
value = 10 * tens + ones;
chars = 3;
}
[c01 @ '0'..='9', SUFF_SEQ_IN, ..] => {
let ones: usize = *c01 as usize - '0' as usize;
value = ones;
chars = 2;
}
_ => return None,
}
match value.checked_sub(1) {
Some(idx) if idx < SEQUENCE.len() => Some((SEQUENCE[idx], chars)),
_ => None,
}
}
#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub struct Numeral {
pub value: isize,
pub base_10: bool,
pub ordinal: bool,
pub lines: bool,
pub little_endian: bool,
}
impl Numeral {
pub const PREF_NEG_OUT: char = '-';
pub const PREF_POS_OUT: char = '+';
pub const SUFF_ORD_OUT: char = TEMA_TINCO.single_ex;
pub const fn new(value: isize, base_10: bool) -> Self {
Self {
value,
base_10,
ordinal: false,
lines: false,
little_endian: true,
}
}
pub const fn decimal(value: isize) -> Self {
Self::new(value, true)
}
pub const fn duodecimal(value: isize) -> Self {
Self::new(value, false)
}
pub const fn with_decimal(mut self, decimal: bool) -> Self {
self.base_10 = decimal;
self
}
pub const fn with_lines(mut self, lines: bool) -> Self {
self.lines = lines;
self
}
pub const fn with_ordinal(mut self, ordinal: bool) -> Self {
self.ordinal = ordinal;
self
}
}
impl Numeral {
pub fn parse(mut slice: &[char]) -> Option<(Self, usize)> {
let base_10: bool = match slice {
[PREF_DEC_IN, after @ ..] => {
slice = after;
true
}
_ => false,
};
let neg: bool = 0 < slice.len() && slice[0] == '-';
let end: usize = neg as usize
+ slice.iter()
.skip(neg as usize)
.take_while(|n| n.is_ascii_digit())
.count();
if end > neg as usize {
let value: isize = slice.iter()
.take(end)
.collect::<String>()
.parse()
.ok()?;
let ordinal = end < slice.len() && slice[end] == SUFF_ORD_IN;
let numeral = Self::new(value, base_10).with_ordinal(ordinal);
let chars = end
+ base_10 as usize + ordinal as usize ;
Some((numeral, chars))
} else {
None
}
}
}
impl Display for Numeral {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let mut value: Digits;
let size: usize;
let base_marker: char;
let mark_ones: bool;
if self.base_10 ^ f.alternate() {
value = Digits::decimal(self.value);
size = value.size() + 3;
if self.lines {
base_marker = BASE_10_LINE;
mark_ones = true;
} else {
base_marker = BASE_10_DOT;
mark_ones = true;
}
} else {
value = Digits::duodecimal(self.value);
size = value.size();
if self.lines {
base_marker = BASE_12_LINE;
mark_ones = true;
} else {
base_marker = BASE_12_DOT;
mark_ones = false;
}
}
let mut text = String::with_capacity(size + self.ordinal as usize * 3);
if value.negative {
text.push(Self::PREF_NEG_OUT);
} else if f.sign_plus() {
text.push(Self::PREF_POS_OUT);
}
if !self.little_endian {
value.digits.reverse();
}
match value.digits.as_slice() {
[] => {}
[digit] => {
text.push(NUMERAL[*digit as usize]);
text.push(base_marker);
}
[units, digits @ ..] if self.little_endian => {
text.push(NUMERAL[*units as usize]);
text.push(MOD_UNITS);
if mark_ones {
text.push(base_marker);
}
for digit in digits {
text.push(NUMERAL[*digit as usize]);
text.push(base_marker);
}
}
[digits @ .., units] => {
for digit in digits {
text.push(NUMERAL[*digit as usize]);
text.push(base_marker);
}
text.push(NUMERAL[*units as usize]);
text.push(MOD_UNITS);
if mark_ones {
text.push(base_marker);
}
}
}
if self.ordinal {
text.push(Self::SUFF_ORD_OUT);
}
Display::fmt(text.as_str(), f)
}
}
impl<N: Into<isize>> From<N> for Numeral {
fn from(value: N) -> Self {
Self::new(value.into(), false)
}
}