Skip to content

Commit

Permalink
Fix unitless conversions (see #298)
Browse files Browse the repository at this point in the history
  • Loading branch information
printfn committed Jun 19, 2024
1 parent 0dcfe94 commit cfe32d2
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 61 deletions.
10 changes: 0 additions & 10 deletions core/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ pub(crate) enum FendError {
ExpectedARationalNumber,
CannotConvertToInteger,
ComplexToInteger,
NumberWithUnitToInt,
InexactNumberToInt,
ExpectedANumber,
ExpectedABool(&'static str),
Expand Down Expand Up @@ -74,16 +73,13 @@ pub(crate) enum FendError {
ExpectedAString,
ExpectedARealNumber,
ConversionRhsNumerical,
FactorialUnitless,
ModuloForPositiveInts,
ExpUnitless,
IncompatibleConversion {
from: String,
to: String,
from_base: String,
to_base: String,
},
ModuloUnitless,
RootsOfNegativeNumbers,
NonIntegerNegRoots,
CannotConvertValueTo(&'static str),
Expand All @@ -104,13 +100,8 @@ impl fmt::Display for FendError {
match self {
Self::Interrupted => write!(f, "interrupted"),
Self::ParseError(e) => write!(f, "{e}"),
Self::FactorialUnitless => {
write!(f, "factorial is only supported for unitless numbers")
}
Self::DeserializationError => write!(f, "failed to deserialize object"),
Self::ModuloUnitless => write!(f, "modulo is only supported for unitless numbers"),
Self::FactorialComplex => write!(f, "factorial is not supported for complex numbers"),
Self::ExpUnitless => write!(f, "exponentiation is only supported for unitless numbers"),
Self::IoError(_) => write!(f, "I/O error"),
Self::InvalidBasePrefix => write!(
f,
Expand Down Expand Up @@ -191,7 +182,6 @@ impl fmt::Display for FendError {
Self::ExpectedARationalNumber => write!(f, "expected a rational number"),
Self::CannotConvertToInteger => write!(f, "number cannot be converted to an integer"),
Self::ComplexToInteger => write!(f, "cannot convert complex number to integer"),
Self::NumberWithUnitToInt => write!(f, "cannot convert number with unit to integer"),
Self::InexactNumberToInt => write!(f, "cannot convert inexact number to integer"),
Self::ExpectedANumber => write!(f, "expected a number"),
Self::InvalidDiceSyntax => write!(f, "invalid dice syntax, try e.g. `4d6`"),
Expand Down
88 changes: 37 additions & 51 deletions core/src/num/unit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,7 @@ impl Value {
}

pub(crate) fn try_as_usize<I: Interrupt>(self, int: &I) -> FResult<usize> {
if !self.is_unitless(int)? {
return Err(FendError::NumberWithUnitToInt);
}
self.try_as_usize_unit(int)
self.into_unitless_complex(int)?.try_as_usize(int)
}

pub(crate) fn try_as_usize_unit<I: Interrupt>(self, int: &I) -> FResult<usize> {
Expand Down Expand Up @@ -160,16 +157,13 @@ impl Value {
}

pub(crate) fn factorial<I: Interrupt>(self, int: &I) -> FResult<Self> {
if !self.is_unitless(int)? {
return Err(FendError::FactorialUnitless);
}
Ok(Self {
value: Dist::from(self.value.one_point()?.factorial(int)?),
unit: self.unit,
unit: Unit::unitless(),
exact: self.exact,
base: self.base,
format: self.format,
simplifiable: self.simplifiable,
value: Dist::from(self.into_unitless_complex(int)?.factorial(int)?),
})
}

Expand Down Expand Up @@ -280,74 +274,59 @@ impl Value {
}

fn modulo<I: Interrupt>(self, rhs: Self, int: &I) -> FResult<Self> {
if !self.is_unitless(int)? || !rhs.is_unitless(int)? {
return Err(FendError::ModuloUnitless);
}
Ok(Self {
value: Dist::from(
self.value
.one_point()?
.modulo(rhs.value.one_point()?, int)?,
),
unit: self.unit,
unit: Unit::unitless(),
exact: self.exact && rhs.exact,
base: self.base,
format: self.format,
simplifiable: self.simplifiable,
value: Dist::from(
self.into_unitless_complex(int)?
.modulo(rhs.into_unitless_complex(int)?, int)?,
),
})
}

fn bitwise<I: Interrupt>(self, rhs: Self, op: BitwiseBop, int: &I) -> FResult<Self> {
if !self.is_unitless(int)? || !rhs.is_unitless(int)? {
return Err(FendError::ExpectedAUnitlessNumber);
}
Ok(Self {
value: Dist::from(
self.value
.one_point()?
.bitwise(rhs.value.one_point()?, op, int)?,
),
unit: self.unit,
unit: Unit::unitless(),
exact: self.exact && rhs.exact,
base: self.base,
format: self.format,
simplifiable: self.simplifiable,
value: Dist::from(self.into_unitless_complex(int)?.bitwise(
rhs.into_unitless_complex(int)?,
op,
int,
)?),
})
}

pub(crate) fn combination<I: Interrupt>(self, rhs: Self, int: &I) -> FResult<Self> {
if !self.is_unitless(int)? || !rhs.is_unitless(int)? {
return Err(FendError::ExpectedAUnitlessNumber);
}
Ok(Self {
value: Dist::from(
self.value
.one_point()?
.combination(rhs.value.one_point()?, int)?,
),
unit: self.unit,
unit: Unit::unitless(),
exact: self.exact && rhs.exact,
base: self.base,
format: self.format,
simplifiable: self.simplifiable,
value: Dist::from(
self.into_unitless_complex(int)?
.combination(rhs.into_unitless_complex(int)?, int)?,
),
})
}

pub(crate) fn permutation<I: Interrupt>(self, rhs: Self, int: &I) -> FResult<Self> {
if !self.is_unitless(int)? || !rhs.is_unitless(int)? {
return Err(FendError::ExpectedAUnitlessNumber);
}
Ok(Self {
value: Dist::from(
self.value
.one_point()?
.permutation(rhs.value.one_point()?, int)?,
),
unit: self.unit,
unit: Unit::unitless(),
exact: self.exact && rhs.exact,
base: self.base,
format: self.format,
simplifiable: self.simplifiable,
value: Dist::from(
self.into_unitless_complex(int)?
.permutation(rhs.into_unitless_complex(int)?, int)?,
),
})
}

Expand Down Expand Up @@ -393,14 +372,13 @@ impl Value {
}

pub(crate) fn pow<I: Interrupt>(self, rhs: Self, int: &I) -> FResult<Self> {
if !rhs.is_unitless(int)? {
return Err(FendError::ExpUnitless);
}
let rhs_exact = rhs.exact;
let rhs = rhs.into_unitless_complex(int)?;
let mut new_components = vec![];
let mut exact_res = true;
for unit_exp in self.unit.components {
let exponent = Exact::new(unit_exp.exponent, self.exact)
.mul(&Exact::new(rhs.value.clone().one_point()?, rhs.exact), int)?;
.mul(&Exact::new(rhs.clone(), rhs_exact), int)?;
exact_res = exact_res && exponent.exact;
new_components.push(UnitExponent {
unit: unit_exp.unit,
Expand All @@ -410,11 +388,11 @@ impl Value {
let new_unit = Unit {
components: new_components,
};
let value = self.value.one_point()?.pow(rhs.value.one_point()?, int)?;
let value = self.value.one_point()?.pow(rhs, int)?;
Ok(Self {
value: value.value.into(),
unit: new_unit,
exact: self.exact && rhs.exact && exact_res && value.exact,
exact: self.exact && rhs_exact && exact_res && value.exact,
base: self.base,
format: self.format,
simplifiable: self.simplifiable,
Expand Down Expand Up @@ -489,6 +467,14 @@ impl Value {
self.convert_to(Self::unitless(), int)
}

fn into_unitless_complex<I: Interrupt>(mut self, int: &I) -> FResult<Complex> {
self = self.remove_unit_scaling(int)?;
if !self.is_unitless(int)? {
return Err(FendError::ExpectedAUnitlessNumber);
}
self.value.one_point()
}

fn apply_fn_exact<I: Interrupt>(
mut self,
f: impl FnOnce(Complex, &I) -> FResult<Exact<Complex>>,
Expand Down
6 changes: 6 additions & 0 deletions core/tests/integration_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5972,3 +5972,9 @@ fn modulo_percent() {
test_eval("5%4", "1");
test_eval("(104857566-103811072+1) % (1024*1024/512)", "2015");
}

#[test]
fn modulo_unitless() {
test_eval("5 mod (4k)", "5");
test_eval("(4k)^2", "16000000");
}

0 comments on commit cfe32d2

Please sign in to comment.