num_base 0.4.2

Crate for manipulating with numbers (integers) in different bases.
Documentation
#[cfg(test)]
mod tests;

mod error;
pub use error::BasedError;

mod builder;
pub use builder::BasedBuilder;

use crate::{ Alphabet, AlphabetError };

/// Number with its base and `Alphabet`.
#[derive(Debug, Clone)]
pub struct Based {
    /// Digits of `Based` number in a String.
    pub val: String,
    /// `Based` number's base as usize.
    pub base: usize,
    /// `Alphabet` of `Based` number.
    pub alphabet: Alphabet,
}

use std::fmt;
impl fmt::Display for Based {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match &self.alphabet {
            Alphabet::Custom(abc) => write!(f, "{}_{} (\"{}\")", self.val, self.base, abc),
            _ => write!(f, "{}_{}", self.val, self.base),
        }
    }
}

impl PartialEq for Based {
    fn eq(&self, other: &Self) -> bool {
        let n1 = self.clone().value().unwrap();
        let n2 = other.clone().value().unwrap();

        n1 == n2
    }
}

impl Based {
    /// Creates a new `Based` number.
    ///
    /// ```
    /// # use num_base::Based;
    /// let num = Based::new("1100101", 2);
    /// ```
    pub fn new<S: Into<String>>(val: S, base: usize) -> Self {
        Based::new_unchecked(val, base).check().unwrap()
    }

    /// Creates a new `Based` number.
    ///```
    /// # use num_base::Based;
    /// let num = Based::new_unchecked("1100101", 2);
    /// ```
    /// **See: [check()](Self::check()) or [is_valid()](Self::is_valid())**.
    pub fn new_unchecked<S: Into<String>>(val: S, base: usize) -> Self {
        Self {
            val: val.into(),
            base,
            alphabet: Alphabet::Default,
        }
    }

    /// Creates a new `Based` number with a non-default `Alphabet`.
    /// ```
    /// # use num_base::Based;
    /// let num = Based::new_abc("bbaabab", 2, "ab");
    /// ```
    pub fn new_abc<S, A>(val: S, base: usize, abc: A) -> Self
        where S: Into<String>, A: Into<Alphabet>
    {
        Based::new_abc_unchecked(val, base, abc).check().unwrap()
    }

    /// Creates a new `Based` number with a non-default `Alphabet`.
    /// ```
    /// # use num_base::Based;
    /// let num = Based::new_abc_unchecked("bbaabab", 2, "ab");
    /// ```
    /// **See: [check()](Self::check()) or [is_valid()](Self::is_valid())**.
    pub fn new_abc_unchecked<S, A>(val: S, base: usize, abc: A) -> Self
        where S: Into<String>, A: Into<Alphabet>
    {
        Self {
            val: val.into(),
            base,
            alphabet: abc.into(),
        }
    }

    /// Returns a default [`BasedBuilder`].
    pub fn builder() -> BasedBuilder {
        BasedBuilder::default()
    }

    /// Converts `Based` number to different base.
    ///
    /// ```
    /// # use num_base::Based;
    /// #
    /// let num = Based::new("101", 10).to(2).unwrap();
    ///
    /// assert_eq!(num.val, "1100101");
    /// ```
    pub fn to(self, base_to: usize) -> Result<Self, BasedError> {
        let num = self.check()?;
        if num.base == base_to { return Ok(num) }

        Ok(match (num.base, base_to) {
            (10, _) => num.convert_from_base_10(base_to)?,
            (_, 10) => num.convert_to_base_10()?,
            _ => num.convert_to_base_10()?.convert_from_base_10(base_to)?,
        })
    }

    fn convert_from_base_10(self, base_to: usize) -> Result<Self, BasedError> {
        let alphabet = self.alphabet;

        let mut cache: usize = match alphabet {
            Alphabet::Default | Alphabet::UpperCase | Alphabet::Decimal => self.val.parse()?,
            Alphabet::Custom(_) | Alphabet::Latin | Alphabet::LatinUpperCase => {
                self.val.chars().map(|c| {
                    alphabet.chars_index(c).unwrap().to_string()
                }).collect::<String>().parse()?
            },
        };

        let mut remainder = String::new();

        while cache > 0 {
            remainder.insert_str(0, &alphabet.nth_char(cache % base_to)?.to_string());
            match base_to {
                1 => cache -= 1,
                _ => cache /= base_to,
            }
        }

        Ok( Self { val: remainder, base: base_to, alphabet } )
    }

    fn convert_to_base_10(self) -> Result<Self, BasedError> {
        let alphabet = self.alphabet;

        let mut cache: usize = 0;

        for (i, c) in self.val.chars().enumerate() {
            let exponent: u32 = (self.val.chars().count() - 1 - i) as u32;
            cache += alphabet.chars_index(c)?
                        .checked_mul(self.base.pow(exponent))
                        .expect("Overflow (number is bigger than usize)");
        }

        let abc_len = alphabet.get().len();

        let cache: Result<String, _> = cache.to_string().chars().map(|c| {
            let char_val = Alphabet::Decimal.chars_index(c)?;

            if abc_len < char_val {
                panic!("Too small Alphabet (add {} more characters)", char_val - abc_len + 1)
            }

            alphabet.nth_char(char_val)
        }).collect();

        Ok( Self { val: cache?, base: 10, alphabet } )
    }

    /// Returns `Based` number's value in base 10.
    pub fn value(self) -> Result<usize, BasedError> {
        Ok(self.to(10)?.convert_to_abc(Alphabet::Default)?.val.parse()?)
    }

    /// Converts Based number's Alphabet without changing its [value()](Self::value()).
    ///
    /// ```
    /// # use num_base::Based;
    /// #
    /// let num = Based::new("101", 10).convert_abc("abcdefghijklmnopqrstuvwxyz").unwrap();
    ///
    /// assert_eq!(num.val, "bab");
    /// assert_eq!(num.value().unwrap(), 101);
    /// ```
    #[deprecated(since="0.4.2", note="Renamed to [`convert_to_abc()`](Self::convert_to_abc())")]
    pub fn convert_abc<A: Into<Alphabet>>(self, abc: A) -> Result<Self, BasedError> {
        self.convert_to_abc(abc)
    }

    /// Converts Based number's Alphabet without changing its [value()](Self::value()).
    ///
    /// ```
    /// # use num_base::Based;
    /// #
    /// let num = Based::new("101", 10).convert_abc("abcdefghijklmnopqrstuvwxyz").unwrap();
    ///
    /// assert_eq!(num.val, "bab");
    /// assert_eq!(num.value().unwrap(), 101);
    /// ```
    pub fn convert_to_abc<A: Into<Alphabet>>(self, abc: A) -> Result<Self, BasedError> {
        let alphabet_to = abc.into();

        let output: Result<String, _> = self.val.chars().map(|c| {
            alphabet_to.nth_char(self.alphabet.chars_index(c)?)
        }).collect();

        Ok(Self {
            val: output?,
            base: self.base,
            alphabet: alphabet_to,
        })
    }

    /// Checks for:
    /// - characters (in `val`) with higher value than `base`,
    /// - Base 0.
    pub fn check(self) -> Result<Self, BasedError> {
        if self.base == 0 {
            return Err( BasedError::BaseZero )
        }

        for c in self.val.chars() {
            let char_val = self.alphabet.chars_index(c).unwrap();

            if char_val > self.base - 1 {
                return Err( AlphabetError::TooHighValue(c).into() )
            }
        }
        Ok(self)
    }

    /// Checks for:
    /// - characters (in `val`) with higher value than `base`,
    /// - Base 0.
    ///
    /// Use [check()](Self::check()) to get error info.
    pub fn is_valid(&self) -> bool {
        if self.base == 0 {
            return false
        }

        for c in self.val.chars() {
            let char_val = self.alphabet.chars_index(c).unwrap();

            if char_val > self.base - 1 {
                return false
            }
        }
        true
    }

    /*/// Checks if conversion converts back to the same number.
    fn check_to(self, base_to: usize) -> bool {
        let base_from = self.base;
        let num = self.clone().to(base_to);

        self.val == num.to(base_from).val
    }*/
}