use crate::error::FuelError;
use crate::fraction::Fraction;
use nom::{
branch::alt,
bytes::complete::tag,
character::complete::{char, digit1, space0},
combinator::{map_res, opt},
sequence::preceded,
Err, IResult,
};
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
use std::str::FromStr;
use std::{
cmp::Ordering,
fmt,
ops::{Add, AddAssign, Div, Mul, MulAssign, Neg, Sub, SubAssign},
result,
};
pub const EXPONENT: usize = 18; pub const DENOMINATOR: i64 = 1_000_000_000_000_000_000;
pub const INTLIMIT: usize = 18; pub const HEXLIMIT: usize = 36;
pub const MAXVALUE: i128 = i128::MAX; pub const MAXRANGE: u128 = MAXVALUE as u128;
pub const MINVALUE: i128 = -MAXVALUE - 1; pub const MINRANGE: u128 = MAXRANGE + 1;
pub const DECSHOWN: usize = 1;
#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq)] pub struct Fuel {
pub units: i128,
}
pub type FuelResult = result::Result<Fuel, FuelError>;
impl Serialize for Fuel {
fn serialize<S>(&self, serializer: S) -> result::Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(self.to_string().as_ref())
}
}
impl<'d> Deserialize<'d> for Fuel {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'d>,
{
let s = String::deserialize(deserializer)?;
Fuel::from_str(&s).map_err(|e| de::Error::custom(e.to_string()))
}
}
impl Fuel {
pub fn new(units: i128) -> Self {
Fuel { units }
}
pub fn zero() -> Self {
Fuel { units: 0 }
}
fn parse_hex(input: &str) -> IResult<&str, (u128, u128), nom::error::Error<&str>> {
map_res(preceded(tag("0x"), digit1), |hex: &str| {
u128::from_str_radix(hex, 16).map(|val| (val, 0))
})(input)
}
fn parse_decimal(input: &str) -> IResult<&str, (u128, u128)> {
let (input, int_part) = opt(digit1)(input)?;
let (input, frac_part) = opt(preceded(char('.'), digit1))(input)?;
let int_part = int_part.unwrap_or("0");
let frac_part = frac_part.unwrap_or("0");
if int_part.len() > INTLIMIT {
return Err(Err::Error(nom::error::Error::new(
input,
nom::error::ErrorKind::TooLarge,
)));
}
let mantissa = DENOMINATOR as u128
* int_part.parse::<u128>().map_err(|_| {
Err::Error(nom::error::Error::new(input, nom::error::ErrorKind::Digit))
})?;
let fraction = format!("{:0<exponent$.exponent$}", frac_part, exponent = EXPONENT)
.parse::<u128>()
.map_err(|_| Err::Error(nom::error::Error::new(input, nom::error::ErrorKind::Digit)))?;
Ok((input, (mantissa, fraction)))
}
#[allow(clippy::type_complexity)]
fn parse_fuel(
input: &str,
) -> IResult<&str, (Option<&str>, (u128, u128)), nom::error::Error<&str>> {
let (input, _) = space0(input)?;
let (input, sign) = opt(alt((tag("+"), tag("-"))))(input)?;
let (input, _) = space0(input)?;
let (input, value) = alt((Self::parse_hex, Self::parse_decimal))(input)?;
let (input, _) = space0(input)?;
Ok((input, (sign, value)))
}
pub fn check(amount: &str) -> Result<bool, FuelError> {
let clean_amount = amount.replace('_', "");
match Self::parse_fuel(&clean_amount) {
Ok((_, (sign, (_, _)))) => match sign {
Some("-") => Err(FuelError::Range(format!(
"Invalid negative amount {}",
amount
))),
_ => Ok(true),
},
Err(_) => Err(FuelError::Range(format!(
"Invalid Holo fuel amount {}",
amount
))),
}
}
}
pub fn u128_to_i128(
negative: bool,
mantissa: u128,
fraction: u128,
range: u128,
) -> Result<i128, FuelError> {
match mantissa.checked_add(fraction) {
Some(u_units) => {
if u_units > range {
Err(FuelError::Range(format!(
"Exceeded range for Holo fuel mantissa {}, fraction {}",
mantissa, fraction
)))
} else if negative {
match u_units.cmp(&MINRANGE) {
Ordering::Greater => Err(FuelError::Range(format!(
"Underflow for Holo fuel negative mantissa {}, fraction {}",
mantissa, fraction
))),
Ordering::Less => Ok(-(u_units as i128)),
Ordering::Equal => Ok(MINVALUE),
}
} else {
Ok(u_units as i128)
}
}
None => Err(FuelError::Range(format!(
"Overflow for Holo fuel mantissa {}, fraction {}",
mantissa, fraction
))),
}
}
impl FromStr for Fuel {
type Err = FuelError;
fn from_str(amount: &str) -> Result<Self, Self::Err> {
let clean_amount = amount.replace('_', "");
match Self::parse_fuel(&clean_amount) {
Ok((_, (sign, (mantissa, fraction)))) => {
let units_res = match sign {
Some("-") => u128_to_i128(true, mantissa, fraction, MINRANGE),
_ => u128_to_i128(false, mantissa, fraction, MAXRANGE),
};
match units_res {
Ok(units) => Ok(Fuel { units }),
Err(e) => Err(e),
}
}
Err(_) => Err(FuelError::Range(format!(
"Invalid Holo fuel amount {}",
amount
))),
}
}
}
impl From<i128> for Fuel {
fn from(units: i128) -> Fuel {
Fuel { units }
}
}
impl From<&mut Fuel> for Fuel {
fn from(other: &mut Fuel) -> Fuel {
Fuel { units: other.units }
}
}
impl From<&Fuel> for Fraction {
fn from(fuel: &Fuel) -> Fraction {
Fraction {
numerator: fuel.units,
denominator: DENOMINATOR.into(),
}
}
}
impl fmt::Display for Fuel {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let sign = if self.units < 0 { "-" } else { "" };
let whole = self.units / (DENOMINATOR as i128); let fraction = self.units - whole * (DENOMINATOR as i128); if fraction == 0 {
write!(f, "{}{}", sign, whole.abs())
} else {
let decimals = format!("{:0>exponent$}", fraction.abs(), exponent = EXPONENT);
let decimals = decimals.trim_end_matches('0'); write!(
f,
"{}{}.{:0<decshown$}",
sign,
whole.abs(),
decimals,
decshown = DECSHOWN
)
}
}
}
impl fmt::Debug for Fuel {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Fuel({})", self)
}
}
impl Neg for Fuel {
type Output = FuelResult;
fn neg(self) -> FuelResult {
Ok(match self.units.checked_neg() {
Some(units) => Fuel { units },
None => {
return Err(FuelError::Range(format!(
"Overflow in negation of Holo fuel amount {}",
self
)))
}
})
}
}
impl Neg for &Fuel {
type Output = FuelResult;
fn neg(self) -> FuelResult {
-*self
}
}
impl Add for Fuel {
type Output = FuelResult;
fn add(self, rhs: Fuel) -> Self::Output {
Ok(match self.units.checked_add(rhs.units) {
Some(units) => Fuel { units },
None => {
return Err(FuelError::Range(format!(
"Overflow in addition of Holo fuel amount {} + {}",
self, rhs
)))
}
})
}
}
impl Add<&Fuel> for Fuel {
type Output = FuelResult;
fn add(self, rhs: &Fuel) -> Self::Output {
self + *rhs
}
}
impl Add<Fuel> for &Fuel {
type Output = FuelResult;
fn add(self, rhs: Fuel) -> Self::Output {
*self + rhs
}
}
impl Add for &Fuel {
type Output = FuelResult;
fn add(self, rhs: &Fuel) -> Self::Output {
*self + *rhs
}
}
impl Add<FuelResult> for Fuel {
type Output = FuelResult;
fn add(self, other: Self::Output) -> Self::Output {
match other {
Ok(rhs) => self + rhs,
Err(rhs_e) => Err(rhs_e),
}
}
}
impl Add<FuelResult> for &Fuel {
type Output = FuelResult;
fn add(self, other: Self::Output) -> Self::Output {
match other {
Ok(rhs) => *self + rhs,
Err(rhs_e) => Err(rhs_e),
}
}
}
impl Add<&FuelResult> for Fuel {
type Output = FuelResult;
fn add(self, other: &Self::Output) -> Self::Output {
match other {
Ok(rhs) => self + *rhs,
Err(rhs_e) => Err(rhs_e.clone()),
}
}
}
impl Add<&FuelResult> for &Fuel {
type Output = FuelResult;
fn add(self, other: &Self::Output) -> Self::Output {
match other {
Ok(rhs) => *self + *rhs,
Err(rhs_e) => Err(rhs_e.clone()),
}
}
}
impl Add<Fuel> for FuelResult {
type Output = FuelResult;
fn add(self, rhs: Fuel) -> Self::Output {
match self {
Ok(lhs) => lhs + rhs,
Err(lhs_e) => Err(lhs_e),
}
}
}
impl Add<&Fuel> for FuelResult {
type Output = FuelResult;
fn add(self, rhs: &Fuel) -> Self::Output {
match self {
Ok(lhs) => lhs + *rhs,
Err(lhs_e) => Err(lhs_e),
}
}
}
impl Add<Fuel> for &FuelResult {
type Output = FuelResult;
fn add(self, rhs: Fuel) -> Self::Output {
match self {
Ok(lhs) => *lhs + rhs,
Err(lhs_e) => Err(lhs_e.clone()),
}
}
}
impl Add<&Fuel> for &FuelResult {
type Output = FuelResult;
fn add(self, rhs: &Fuel) -> Self::Output {
match self {
Ok(lhs) => *lhs + *rhs,
Err(lhs_e) => Err(lhs_e.clone()),
}
}
}
impl AddAssign<Fuel> for FuelResult {
fn add_assign(&mut self, rhs: Fuel) {
*self = match self {
Ok(lhs) => Fuel::from(lhs) + rhs,
Err(lhs_e) => Err(lhs_e.clone()),
};
}
}
impl AddAssign<&Fuel> for FuelResult {
fn add_assign(&mut self, rhs: &Fuel) {
*self = match self {
Ok(lhs) => Fuel::from(lhs) + rhs,
Err(lhs_e) => Err(lhs_e.clone()),
};
}
}
impl Sub for Fuel {
type Output = FuelResult;
fn sub(self, rhs: Fuel) -> Self::Output {
Ok(match self.units.checked_sub(rhs.units) {
Some(units) => Fuel { units },
None => {
return Err(FuelError::Range(format!(
"Overflow in subtraction of Holo fuel amount {} - {}",
self, rhs
)))
}
})
}
}
impl Sub<&Fuel> for Fuel {
type Output = FuelResult;
fn sub(self, rhs: &Fuel) -> Self::Output {
self - *rhs
}
}
impl Sub<Fuel> for &Fuel {
type Output = FuelResult;
fn sub(self, rhs: Fuel) -> Self::Output {
*self - rhs
}
}
impl Sub for &Fuel {
type Output = FuelResult;
fn sub(self, rhs: &Fuel) -> Self::Output {
*self - *rhs
}
}
impl Sub<FuelResult> for Fuel {
type Output = FuelResult;
fn sub(self, other: Self::Output) -> Self::Output {
match other {
Ok(rhs) => self - rhs,
Err(rhs_e) => Err(rhs_e),
}
}
}
impl Sub<FuelResult> for &Fuel {
type Output = FuelResult;
fn sub(self, other: Self::Output) -> Self::Output {
match other {
Ok(rhs) => *self - rhs,
Err(rhs_e) => Err(rhs_e),
}
}
}
impl Sub<&FuelResult> for Fuel {
type Output = FuelResult;
fn sub(self, other: &Self::Output) -> Self::Output {
match other {
Ok(rhs) => self - *rhs,
Err(rhs_e) => Err(rhs_e.clone()),
}
}
}
impl Sub<&FuelResult> for &Fuel {
type Output = FuelResult;
fn sub(self, other: &Self::Output) -> Self::Output {
match other {
Ok(rhs) => *self - *rhs,
Err(rhs_e) => Err(rhs_e.clone()),
}
}
}
impl Sub<Fuel> for FuelResult {
type Output = FuelResult;
fn sub(self, rhs: Fuel) -> Self::Output {
match self {
Ok(lhs) => lhs - rhs,
Err(lhs_e) => Err(lhs_e),
}
}
}
impl Sub<&Fuel> for FuelResult {
type Output = FuelResult;
fn sub(self, rhs: &Fuel) -> Self::Output {
match self {
Ok(lhs) => lhs - *rhs,
Err(lhs_e) => Err(lhs_e),
}
}
}
impl Sub<Fuel> for &FuelResult {
type Output = FuelResult;
fn sub(self, rhs: Fuel) -> Self::Output {
match self {
Ok(lhs) => *lhs - rhs,
Err(lhs_e) => Err(lhs_e.clone()),
}
}
}
impl Sub<&Fuel> for &FuelResult {
type Output = FuelResult;
fn sub(self, rhs: &Fuel) -> Self::Output {
match self {
Ok(lhs) => *lhs - *rhs,
Err(lhs_e) => Err(lhs_e.clone()),
}
}
}
impl SubAssign<Fuel> for FuelResult {
fn sub_assign(&mut self, rhs: Fuel) {
*self = match self {
Ok(lhs) => Fuel::from(lhs) - rhs,
Err(lhs_e) => Err(lhs_e.clone()),
};
}
}
impl SubAssign<&Fuel> for FuelResult {
fn sub_assign(&mut self, rhs: &Fuel) {
*self = match self {
Ok(lhs) => Fuel::from(lhs) - rhs,
Err(lhs_e) => Err(lhs_e.clone()),
};
}
}
impl Mul<Fraction> for Fuel {
type Output = FuelResult;
#[allow(clippy::suspicious_arithmetic_impl)]
fn mul(self, rhs: Fraction) -> Self::Output {
match self.units.checked_div(rhs.denominator) {
Some(quotient) => match quotient.checked_mul(rhs.numerator) {
Some(units) => match self
.units
.checked_rem(rhs.denominator)
.and_then(|e| e.checked_mul(rhs.numerator))
.and_then(|e| {
if e >= 0 {
e.checked_add(rhs.denominator - 1)
} else {
e.checked_sub(rhs.denominator - 1)
}
})
.and_then(|e| e.checked_div(rhs.denominator))
{
Some(extra) => Ok(Fuel {
units: units + extra,
}),
None => Err(FuelError::FractionOverflow((self, rhs))),
},
None => Err(FuelError::FractionOverflow((self, rhs))),
},
None => Err(FuelError::FractionOverflow((self, rhs))),
}
}
}
impl Mul<&Fraction> for Fuel {
type Output = FuelResult;
fn mul(self, rhs: &Fraction) -> Self::Output {
self * *rhs
}
}
impl Mul<Fraction> for &Fuel {
type Output = FuelResult;
fn mul(self, rhs: Fraction) -> Self::Output {
*self * rhs
}
}
impl Mul<&Fraction> for &Fuel {
type Output = FuelResult;
fn mul(self, rhs: &Fraction) -> Self::Output {
*self * *rhs
}
}
impl MulAssign<Fraction> for FuelResult {
fn mul_assign(&mut self, rhs: Fraction) {
*self = match self {
Ok(lhs) => Fuel::from(lhs) * rhs,
Err(lhs_e) => Err(lhs_e.clone()),
};
}
}
impl MulAssign<&Fraction> for FuelResult {
fn mul_assign(&mut self, rhs: &Fraction) {
*self = match self {
Ok(lhs) => Fuel::from(lhs) * rhs,
Err(lhs_e) => Err(lhs_e.clone()),
};
}
}
impl Div<Fraction> for Fuel {
type Output = FuelResult;
#[allow(clippy::suspicious_arithmetic_impl)]
fn div(self, rhs: Fraction) -> Self::Output {
self * Fraction {
numerator: rhs.denominator,
denominator: rhs.numerator,
}
}
}
impl Div<&Fraction> for Fuel {
type Output = FuelResult;
fn div(self, rhs: &Fraction) -> Self::Output {
self / *rhs
}
}
impl Div<Fraction> for &Fuel {
type Output = FuelResult;
fn div(self, rhs: Fraction) -> Self::Output {
*self / rhs
}
}
impl Div<&Fraction> for &Fuel {
type Output = FuelResult;
fn div(self, rhs: &Fraction) -> Self::Output {
*self / *rhs
}
}
#[cfg(test)]
pub mod tests {
use crate::fuel::{self, u128_to_i128, Fuel, FuelResult};
use std::str::FromStr;
#[test]
fn fuel_check_test() {
let _ = Fuel::check("1.0").unwrap();
match Fuel::check("-1") {
Ok(f) => panic!(
"Expected failure due to fuel being a negative value: ♓{}",
f
),
Err(e) => assert_eq!(
format!("{}", e),
"HoloFuel Range Error: Invalid negative amount -1"
),
}
}
#[test]
fn fuel_smoke_test() {
let f1 = Fuel::from_str("0.012");
assert_eq!(format!("{:?}", f1), "Ok(Fuel(0.012))");
let f1 = Fuel::from_str("1.0").unwrap();
assert_eq!(f1.units, fuel::DENOMINATOR as i128);
let d1 = format!("{}", f1);
assert_eq!(
d1,
match fuel::DECSHOWN {
1 => "1",
2 => "1",
3 => "1",
4 => "1",
5 => "1",
6 => "1",
7 => "1",
8 => "1",
_ => "unknown",
}
);
let f2 = Fuel::from(-1234567890987654321);
assert_eq!(f2.units, -1234567890987654321_i128);
let d2 = format!("{}", f2);
assert_eq!(
d2,
match fuel::DECSHOWN {
6 => "-1234.567890",
_ => "-1.234567890987654321",
}
);
let f3 = Fuel::from_str("999.5").unwrap();
assert_eq!(f3.units, 999_500_000_000_000_000_000_i128);
let d3 = format!("{}", f3);
assert_eq!(
d3,
match fuel::DECSHOWN {
1 => "999.5",
2 => "999.50",
3 => "999.500",
4 => "999.5000",
5 => "999.50000",
6 => "999.500000",
7 => "999.5000000",
8 => "999.50000000",
_ => "unknown",
}
);
let f4 = Fuel::from_str("-1234.5678901234567890123456").unwrap();
assert_eq!(f4.units, -1234567890123456789012);
let d4 = format!("{}", f4);
assert_eq!(d4, "-1234.567890123456789012");
assert_eq!(
fuel::MAXVALUE,
170_141_183_460_469_231_731_687_303_715_884_105_727
);
assert_eq!(
fuel::MAXRANGE,
170_141_183_460_469_231_731_687_303_715_884_105_727
);
assert_eq!(
fuel::MINVALUE,
-170_141_183_460_469_231_731_687_303_715_884_105_728
);
assert_eq!(
fuel::MINRANGE,
170_141_183_460_469_231_731_687_303_715_884_105_728
);
assert_eq!(
format!(
"{:?}",
Fuel::from_str("-1701411834604692.31731687303715884105728")
),
"Ok(Fuel(-1701411834604692.317316873037158841))"
);
assert_eq!(
format!(
"{:?}",
Fuel::from_str("-1701411834604692.31731687303715884105728")
),
"Ok(Fuel(-1701411834604692.317316873037158841))"
);
match Fuel::from_str( "0x80000000000000000000000000000000" ) { Ok(f) => panic!( "Expected failure due to fuel::MAXRANGE did not occur: ♓{}", f ),
Err(e) => assert_eq!( format!("{}", e ),
"HoloFuel Range Error: Exceeded range for Holo fuel mantissa 170141183460469231731687303715884105728, fraction 0" ),
}
assert_eq!(
format!("{:?}", Fuel::from_str("9999999999999999.9999999999999999")),
"Ok(Fuel(9999999999999999.9999999999999999))"
);
assert_eq!(
format!("{:?}", Fuel::from_str("-9999999999999999.9999999999999999")),
"Ok(Fuel(-9999999999999999.9999999999999999))"
);
match Fuel::from_str("1_000_000_000_000_000_000") {
Ok(f) => panic!(
"Expected failure due to fuel::MINVRANGE did not occur: ♓{}",
f
),
Err(e) => assert_eq!(
format!("{}", e),
"HoloFuel Range Error: Invalid Holo fuel amount 1_000_000_000_000_000_000"
),
}
}
#[test]
fn fuel_operators() {
let sum = Fuel::from_str("1.23").unwrap() + Fuel::from_str("-1000").unwrap();
match &sum {
Ok(ref f) => assert_eq!(format!("{}", f), "-998.77"),
Err(e) => panic!("Expected success, not {}", e),
}
let sum2 = sum + Fuel::from_str("100").unwrap();
match &sum2 {
Ok(ref f) => assert_eq!(format!("{}", f), "-898.77"),
Err(e) => panic!("Expected success, not {}", e),
}
let sum3 = Fuel::from_str("-1111.23").unwrap() + sum2;
match &sum3 {
Ok(ref f) => assert_eq!(format!("{}", f), "-2010"),
Err(e) => panic!("Expected success, not {}", e),
}
match Fuel::from(1_000_000_000_000_000_000) + Fuel::from(1) {
Ok(f) => assert_eq!(format!("{}", f), "1.000000000000000001"),
Err(e) => panic!("Expected success, not {}", e),
}
match Fuel::from(1_000_000_000_000_000_000) + &Fuel::from(2) {
Ok(f) => assert_eq!(format!("{}", f), "1.000000000000000002"),
Err(e) => panic!("Expected success, not {}", e),
}
match &Fuel::from(2_000_000_000_000_000_000) + Fuel::from(1) {
Ok(f) => assert_eq!(format!("{}", f), "2.000000000000000001"),
Err(e) => panic!("Expected success, not {}", e),
}
match &Fuel::from(2_000_000_000_000_000_000) + &Fuel::from(2) {
Ok(f) => assert_eq!(format!("{}", f), "2.000000000000000002"),
Err(e) => panic!("Expected success, not {}", e),
}
match Fuel::from_str("1") + Fuel::from(1) {
Ok(f) => assert_eq!(format!("{}", f), "1.000000000000000001"),
Err(e) => panic!("Expected success, not {}", e),
}
match Fuel::from_str("1") + &Fuel::from(1) {
Ok(f) => assert_eq!(format!("{}", f), "1.000000000000000001"),
Err(e) => panic!("Expected success, not {}", e),
}
match &Fuel::from_str("1") + Fuel::from(1) {
Ok(f) => assert_eq!(format!("{}", f), "1.000000000000000001"),
Err(e) => panic!("Expected success, not {}", e),
}
match &Fuel::from_str("1") + &Fuel::from(1) {
Ok(f) => assert_eq!(format!("{}", f), "1.000000000000000001"),
Err(e) => panic!("Expected success, not {}", e),
}
match Fuel::from(1) + Fuel::from_str("1") {
Ok(f) => assert_eq!(format!("{}", f), "1.000000000000000001"),
Err(e) => panic!("Expected success, not {}", e),
}
match &Fuel::from(1) + Fuel::from_str("1") {
Ok(f) => assert_eq!(format!("{}", f), "1.000000000000000001"),
Err(e) => panic!("Expected success, not {}", e),
}
match Fuel::from(1) + &Fuel::from_str("1") {
Ok(f) => assert_eq!(format!("{}", f), "1.000000000000000001"),
Err(e) => panic!("Expected success, not {}", e),
}
match &Fuel::from(1) + &Fuel::from_str("1") {
Ok(f) => assert_eq!(format!("{}", f), "1.000000000000000001"),
Err(e) => panic!("Expected success, not {}", e),
}
let mut fa = Fuel::from_str("1");
fa += Fuel::from(1);
match fa {
Ok(f) => assert_eq!(format!("{}", f), "1.000000000000000001"),
Err(e) => panic!("Expected success, not {}", e),
}
fa += &Fuel::from(-2);
match fa {
Ok(f) => assert_eq!(format!("{}", f), "0.999999999999999999"),
Err(e) => panic!("Expected success, not {}", e),
}
match Fuel::from(1_000_000_000_000_000_000) - Fuel::from(1) {
Ok(f) => assert_eq!(format!("{}", f), "0.999999999999999999"),
Err(e) => panic!("Expected success, not {}", e),
}
match Fuel::from(1_000_000_000_000_000_000) - &Fuel::from(2) {
Ok(f) => assert_eq!(format!("{}", f), "0.999999999999999998"),
Err(e) => panic!("Expected success, not {}", e),
}
match &Fuel::from(2_000_000_000_000_000_000) - Fuel::from(1) {
Ok(f) => assert_eq!(format!("{}", f), "1.999999999999999999"),
Err(e) => panic!("Expected success, not {}", e),
}
match &Fuel::from(2_000_000_000_000_000_000) - &Fuel::from(2) {
Ok(f) => assert_eq!(format!("{}", f), "1.999999999999999998"),
Err(e) => panic!("Expected success, not {}", e),
}
match Fuel::from_str("1") - Fuel::from(1) {
Ok(f) => assert_eq!(format!("{}", f), "0.999999999999999999"),
Err(e) => panic!("Expected success, not {}", e),
}
match Fuel::from_str("1") - &Fuel::from(1) {
Ok(f) => assert_eq!(format!("{}", f), "0.999999999999999999"),
Err(e) => panic!("Expected success, not {}", e),
}
match &Fuel::from_str("1") - Fuel::from(1) {
Ok(f) => assert_eq!(format!("{}", f), "0.999999999999999999"),
Err(e) => panic!("Expected success, not {}", e),
}
match &Fuel::from_str("1") - &Fuel::from(1) {
Ok(f) => assert_eq!(format!("{}", f), "0.999999999999999999"),
Err(e) => panic!("Expected success, not {}", e),
}
match Fuel::from(1) - Fuel::from_str("1") {
Ok(f) => assert_eq!(format!("{}", f), "-0.999999999999999999"),
Err(e) => panic!("Expected success, not {}", e),
}
match &Fuel::from(1) - Fuel::from_str("1") {
Ok(f) => assert_eq!(format!("{}", f), "-0.999999999999999999"),
Err(e) => panic!("Expected success, not {}", e),
}
match Fuel::from(1) - &Fuel::from_str("1") {
Ok(f) => assert_eq!(format!("{}", f), "-0.999999999999999999"),
Err(e) => panic!("Expected success, not {}", e),
}
match &Fuel::from(1) - &Fuel::from_str("1") {
Ok(f) => assert_eq!(format!("{}", f), "-0.999999999999999999"),
Err(e) => panic!("Expected success, not {}", e),
}
let mut fa = Fuel::from_str("1");
fa -= Fuel::from(1);
match fa {
Ok(f) => assert_eq!(format!("{}", f), "0.999999999999999999"),
Err(e) => panic!("Expected success, not {}", e),
}
fa -= &Fuel::from(2_000_000_000_000_000_001);
match fa {
Ok(f) => assert_eq!(format!("{}", f), "-1.000000000000000002"),
Err(e) => panic!("Expected success, not {}", e),
}
fa -= &Fuel::from(-2_000_000_000_000_000_003);
match fa {
Ok(f) => assert_eq!(format!("{}", f), "1.000000000000000001"),
Err(e) => panic!("Expected success, not {}", e),
}
match Fuel::from( u128_to_i128( true, fuel::MINRANGE, 0_u128, fuel::MINRANGE ).unwrap() ) - Fuel::from( 1 ) {
Ok(f) => panic!( "Expected failure, not {}", f ),
Err(e) => assert_eq!( format!( "{}", e ),
"HoloFuel Range Error: Overflow in subtraction of Holo fuel amount -170141183460469231731.687303715884105728 - 0.000000000000000001" ),
}
match Fuel::from( u128_to_i128( true, fuel::MINRANGE, 0_u128, fuel::MINRANGE ).unwrap() ) - Fuel::from( 1 ) + Fuel::from( 1 ) {
Ok(f) => panic!( "Expected failure, not {}", f ),
Err(e) => assert_eq!( format!( "{}", e ),
"HoloFuel Range Error: Overflow in subtraction of Holo fuel amount -170141183460469231731.687303715884105728 - 0.000000000000000001" ),
}
assert_eq!(
format!("{}", (-Fuel::from(fuel::MAXVALUE)).unwrap()),
"-170141183460469231731.687303715884105727"
);
assert_eq!(
format!("{}", (-&Fuel::from(fuel::MAXVALUE)).unwrap()),
"-170141183460469231731.687303715884105727"
);
assert_eq!(
format!("{:?}", -&Fuel::from(fuel::MINVALUE)),
"Err(Range(\"Overflow in negation of Holo fuel amount -170141183460469231731.687303715884105728\"))"
);
}
#[test]
fn fuel_comparisons() {
assert!(Fuel::from(1_000_001) > Fuel::from(1_000_000));
assert!(Fuel::from(1_000_000) < Fuel::from(1_000_001));
assert!(Fuel::from(1_000_001) == Fuel::from(1_000_001));
assert!(Fuel::from(1_000_000) <= Fuel::from(1_000_001));
assert!(Fuel::from(1_000_001) >= Fuel::from(1_000_000));
assert!(Fuel::from(1_000_000) == Fuel::from(1_000_000));
assert!(Fuel::from(1_000_000) <= Fuel::from(1_000_000));
assert!(Fuel::from(1_000_000) >= Fuel::from(1_000_000));
}
use crate::fraction::Fraction;
#[test]
fn fuel_compute_fees() {
let feepct = Fraction::new(35, 1000).unwrap().reduce();
assert_eq!((feepct.numerator, feepct.denominator), (7, 200));
let feeamt = Fuel { units: 399 } * &feepct;
match &feeamt {
Ok(ref f) => assert_eq!(format!("{}", f), "0.000000000000000014"), Err(e) => panic!("Expected success, not {}", e),
}
let inv_feepct = Fraction {
denominator: feepct.numerator,
numerator: feepct.denominator,
};
let feeamt = Fuel { units: 399 } / &inv_feepct;
match &feeamt {
Ok(ref f) => assert_eq!(format!("{}", f), "0.000000000000000014"),
Err(e) => panic!("Expected success, not {}", e),
}
let amount = Fuel {
units: fuel::MAXVALUE,
};
let feeamt = amount * &feepct;
match feeamt {
Ok(f) => assert_eq!(format!("{}", f), "5954941421116423110.609055630055943701"), Err(e) => panic!("Expected success, not {}", e),
};
match Fuel::new(fuel::MINVALUE) * &feepct {
Ok(f) => assert_eq!(format!("{}", f), "-5954941421116423110.609055630055943701"),
Err(e) => panic!("Expected success, not {}", e),
};
match Fuel::from(fuel::MAXVALUE) * Fraction::new(35, 1000).unwrap().reduce() {
Ok(f) => assert_eq!(format!("{}", f), "5954941421116423110.609055630055943701"),
Err(e) => panic!("Expected success, not {}", e),
};
match &Fuel::from(fuel::MAXVALUE) * Fraction::new(35, 1000).unwrap().reduce() {
Ok(f) => assert_eq!(format!("{}", f), "5954941421116423110.609055630055943701"),
Err(e) => panic!("Expected success, not {}", e),
};
match Fuel::from(fuel::MAXVALUE) * &Fraction::new(35, 1000).unwrap().reduce() {
Ok(f) => assert_eq!(format!("{}", f), "5954941421116423110.609055630055943701"),
Err(e) => panic!("Expected success, not {}", e),
};
match &Fuel::from(fuel::MAXVALUE) * &Fraction::new(35, 1000).unwrap().reduce() {
Ok(ref f) => assert_eq!(format!("{}", f), "5954941421116423110.609055630055943701"),
Err(e) => panic!("Expected success, not {}", e),
};
match Fuel::from(fuel::MAXVALUE) / Fraction::new(1000, 35).unwrap().reduce() {
Ok(f) => assert_eq!(format!("{}", f), "5954941421116423110.609055630055943701"),
Err(e) => panic!("Expected success, not {}", e),
};
match &Fuel::from(fuel::MAXVALUE) / Fraction::new(1000, 35).unwrap().reduce() {
Ok(f) => assert_eq!(format!("{}", f), "5954941421116423110.609055630055943701"),
Err(e) => panic!("Expected success, not {}", e),
};
match Fuel::from(fuel::MAXVALUE) / &Fraction::new(1000, 35).unwrap().reduce() {
Ok(f) => assert_eq!(format!("{}", f), "5954941421116423110.609055630055943701"),
Err(e) => panic!("Expected success, not {}", e),
};
match &Fuel::from(fuel::MAXVALUE) / &Fraction::new(1000, 35).unwrap().reduce() {
Ok(ref f) => assert_eq!(format!("{}", f), "5954941421116423110.609055630055943701"),
Err(e) => panic!("Expected success, not {}", e),
};
let mut feeamt: FuelResult = Ok(Fuel::from(fuel::MAXVALUE));
feeamt *= feepct;
match &feeamt {
Ok(ref f) => assert_eq!(format!("{}", f), "5954941421116423110.609055630055943701"),
Err(e) => panic!("Expected success, not {}", e),
};
assert_eq!(
format!(
"{}",
(Fuel::new(100) * Fraction::new(fuel::MAXVALUE, 2).unwrap()).unwrap_err()
),
"HoloFuel overflow in ♓0.0000000000000001 * 170141183460469231731687303715884105727/2"
);
}
}