use std::ops::{Add, DivAssign, MulAssign, Sub};
use atoi::{FromRadix10, FromRadix10Signed};
pub fn decimal_text_to_i128(text: &[u8], scale: usize) -> i128 {
decimal_text_to_integer(text, scale)
}
impl ToDecimal for i128 {
const ZERO: Self = 0;
const TEN: Self = 10;
}
pub fn decimal_text_to_i64(text: &[u8], scale: usize) -> i64 {
decimal_text_to_integer(text, scale)
}
impl ToDecimal for i64 {
const ZERO: Self = 0;
const TEN: Self = 10;
}
pub fn decimal_text_to_i32(text: &[u8], scale: usize) -> i32 {
decimal_text_to_integer(text, scale)
}
impl ToDecimal for i32 {
const ZERO: Self = 0;
const TEN: Self = 10;
}
fn decimal_text_to_integer<I>(text: &[u8], scale: usize) -> I
where
I: ToDecimal,
{
let (mut high, num_digits_high) = I::from_radix_10_signed(text);
let (low, num_digits_low) = if num_digits_high == text.len() {
(I::ZERO, 0)
} else {
I::from_radix_10(&text[(num_digits_high + 1)..])
};
for _ in 0..num_digits_low {
high *= I::TEN;
}
let mut n = if high < I::ZERO || (high == I::ZERO && text[0] == b'-') {
high - low
} else {
high + low
};
match num_digits_low.cmp(&scale) {
std::cmp::Ordering::Less => {
for _ in 0..(scale - num_digits_low) {
n *= I::TEN;
}
}
std::cmp::Ordering::Greater => {
for _ in 0..(num_digits_low - scale) {
n /= I::TEN;
}
}
std::cmp::Ordering::Equal => {}
}
n
}
trait ToDecimal:
FromRadix10
+ FromRadix10Signed
+ Add<Output = Self>
+ Sub<Output = Self>
+ MulAssign
+ DivAssign
+ Ord
{
const ZERO: Self;
const TEN: Self;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn decimal_is_represented_with_comma_as_radix() {
let actual = decimal_text_to_i128(b"10,00000", 5);
assert_eq!(1_000_000, actual);
}
#[test]
fn decimal_with_less_zeroes() {
let actual = decimal_text_to_i128(b"10.0", 5);
assert_eq!(1_000_000, actual);
}
#[test]
fn negative_decimal() {
let actual = decimal_text_to_i128(b"-10.00000", 5);
assert_eq!(-1_000_000, actual);
}
#[test]
fn negative_decimal_small() {
let actual = decimal_text_to_i128(b"-0.1", 5);
assert_eq!(-10000, actual);
}
#[test]
fn decimal_with_less_zeroes_i64() {
let actual = decimal_text_to_i64(b"10.0", 5);
assert_eq!(1_000_000, actual);
}
#[test]
fn negative_decimal_i64() {
let actual = decimal_text_to_i64(b"-10.00000", 5);
assert_eq!(-1_000_000, actual);
}
#[test]
fn negative_decimal_small_i64() {
let actual = decimal_text_to_i64(b"-0.1", 5);
assert_eq!(-10000, actual);
}
#[test]
fn decimal_with_less_zeroes_i32() {
let actual = decimal_text_to_i32(b"10.0", 5);
assert_eq!(1_000_000, actual);
}
#[test]
fn negative_decimal_i32() {
let actual = decimal_text_to_i32(b"-10.00000", 5);
assert_eq!(-1_000_000, actual);
}
#[test]
fn negative_decimal_small_i32() {
let actual = decimal_text_to_i32(b"-0.1", 5);
assert_eq!(-10000, actual);
}
#[test]
fn decimal_with_too_much_scale() {
let actual = decimal_text_to_i128(b"10.000000", 5);
assert_eq!(1_000_000, actual);
}
#[test]
fn decimal_with_too_much_scale_negative() {
let actual = decimal_text_to_i128(b"-10.123456", 5);
assert_eq!(-1_012_345, actual);
}
#[test]
fn decimal_with_too_much_scale_small_negative() {
let actual = decimal_text_to_i128(b"-0.123456", 5);
assert_eq!(-12345, actual);
}
}