Skip to content

Commit

Permalink
PR updates
Browse files Browse the repository at this point in the history
  • Loading branch information
turboladen committed Sep 13, 2024
1 parent 6ab3ce2 commit caae3c2
Show file tree
Hide file tree
Showing 7 changed files with 276 additions and 6 deletions.
6 changes: 2 additions & 4 deletions crates/api/src/measurement/ops/mul_div.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,10 @@ use crate::{convertible::Convertible, measurement::Measurement};
fn mul_measurements(lhs: &Measurement, rhs: &Measurement) -> Measurement {
let converted_rhs = rhs.convert_to(&lhs.unit);
let actual_rhs = converted_rhs.as_ref().unwrap_or(rhs);
let new_value = lhs.value * actual_rhs.value;
let new_unit = &lhs.unit * &actual_rhs.unit;

Measurement {
value: new_value,
unit: new_unit,
value: lhs.value * actual_rhs.value,
unit: &lhs.unit * &actual_rhs.unit,
}
}

Expand Down
214 changes: 214 additions & 0 deletions crates/api/src/term.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2046,6 +2046,220 @@ mod tests {
}
}

mod set_annotation {
use super::*;

macro_rules! test_non_annotation {
($subject_variant:ident, $expected_variant:ident, $($params:tt)+) => {
let mut subject = Term::$subject_variant($subject_variant {
$($params)+
});
let _ = subject.set_annotation("bar");

if let Term::$expected_variant($expected_variant { annotation, .. }) = subject {
assert_eq!(annotation.as_str(), "bar");
} else {
panic!("Unexpected type! {:#?}", subject);
}
}
}

macro_rules! test_annotation {
($subject_variant:ident, $($params:tt)+) => {
let mut subject = Term::$subject_variant($subject_variant {
annotation: "foo".into(),
$($params)+
});
let _ = subject.set_annotation("bar");

if let Term::$subject_variant($subject_variant { annotation, .. }) = subject {
assert_eq!(annotation.as_str(), "bar");
} else {
panic!("Unexpected type! {:#?}", subject);
}
}
}

#[test]
fn annotation_test() {
let mut subject = Term::Annotation("foo".into());
let _ = subject.set_annotation("bar");

assert_eq!(subject, Term::Annotation("bar".into()));
}

#[test]
fn atom_test() {
let mut subject = Term::Atom(Atom::Meter);
let _ = subject.set_annotation("bar");

assert_eq!(
subject,
Term::AtomAnnotation(AtomAnnotation::new(Atom::Meter, "bar".into()))
);
}

#[test]
fn atom_annotation_test() {
test_annotation!(AtomAnnotation, atom: Atom::Meter);
}

#[test]
fn atom_exponent_test() {
test_non_annotation!(AtomExponent, AtomExponentAnnotation, atom: Atom::Meter, exponent: 2);
}

#[test]
fn atom_exponent_annotation_test() {
test_annotation!(AtomExponentAnnotation, atom: Atom::Meter, exponent: 2);
}

#[test]
fn prefix_atom_test() {
test_non_annotation!(PrefixAtom, PrefixAtomAnnotation, prefix: Prefix::Yocto, atom: Atom::Meter);
}

#[test]
fn prefix_atom_annotation_test() {
test_annotation!(PrefixAtomAnnotation, prefix: Prefix::Yocto, atom: Atom::Meter);
}

#[test]
fn prefix_atom_exponent_test() {
test_non_annotation!(
PrefixAtomExponent,
PrefixAtomExponentAnnotation,
prefix: Prefix::Yocto,
atom: Atom::Meter,
exponent: 2
);
}

#[test]
fn prefix_atom_exponent_annotation_test() {
test_annotation!(PrefixAtomExponentAnnotation,
prefix: Prefix::Yocto,
atom: Atom::Meter,
exponent: 2
);
}

#[test]
fn factor_test() {
let mut subject = Term::Factor(2);
let _ = subject.set_annotation("bar");

if let Term::FactorAnnotation(FactorAnnotation { annotation, .. }) = subject {
assert_eq!(annotation.as_str(), "bar");
} else {
panic!("Unexpected type! {subject:#?}");
}
}

#[test]
fn factor_annotation_test() {
test_annotation!(FactorAnnotation, factor: 2);
}

#[test]
fn factor_exponent_test() {
test_non_annotation!(
FactorExponent,
FactorExponentAnnotation,
factor: 4,
exponent: 2
);
}

#[test]
fn factor_exponent_annotation_test() {
test_annotation!(FactorExponentAnnotation,
factor: 4,
exponent: 2
);
}

#[test]
fn factor_atom_test() {
test_non_annotation!(
FactorAtom,
FactorAtomAnnotation,
factor: 4,
atom: Atom::Meter,
);
}

#[test]
fn factor_atom_annotation_test() {
test_annotation!(FactorAtomAnnotation,
factor: 4,
atom: Atom::Meter,
);
}

#[test]
fn factor_atom_exponent_test() {
test_non_annotation!(
FactorAtomExponent,
FactorAtomExponentAnnotation,
factor: 4,
atom: Atom::Meter,
exponent: 2
);
}

#[test]
fn factor_atom_exponent_annotation_test() {
test_annotation!(FactorAtomExponentAnnotation,
factor: 4,
atom: Atom::Meter,
exponent: 2
);
}

#[test]
fn factor_prefix_atom_test() {
test_non_annotation!(
FactorPrefixAtom,
FactorPrefixAtomAnnotation,
factor: 4,
prefix: Prefix::Yocto,
atom: Atom::Meter,
);
}

#[test]
fn factor_prefix_atom_annotation_test() {
test_annotation!(FactorPrefixAtomAnnotation,
factor: 4,
prefix: Prefix::Yocto,
atom: Atom::Meter,
);
}

#[test]
fn factor_prefix_atom_exponent_test() {
test_non_annotation!(
FactorPrefixAtomExponent,
FactorPrefixAtomExponentAnnotation,
factor: 4,
prefix: Prefix::Yocto,
atom: Atom::Meter,
exponent: 2
);
}

#[test]
fn factor_prefix_atom_exponent_annotation_test() {
test_annotation!(FactorPrefixAtomExponentAnnotation,
factor: 4,
prefix: Prefix::Yocto,
atom: Atom::Meter,
exponent: 2
);
}
}

#[test]
fn as_str_test() {
assert_eq!(UNITY.as_cow_str(), "1");
Expand Down
14 changes: 14 additions & 0 deletions crates/api/src/term/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ use super::{
Exponent, Factor,
};

/// A builder for `Term`s. An alternative for building a `Term` is the `term!()` macro. Note that
/// it allows you to construct invalid `Term`s (ex. a `Term` with only a `Prefix` is invalid), and
/// thus the onus is put on you to call this correctly.
///
#[derive(Default, Debug)]
pub struct Builder {
factor: Option<Factor>,
Expand All @@ -21,6 +25,8 @@ pub struct Builder {
}

impl Builder {
/// Set the `factor` of the `Term`.
///
#[must_use]
pub fn factor(self, factor: Factor) -> Self {
Self {
Expand All @@ -29,6 +35,8 @@ impl Builder {
}
}

/// Set the `prefix` of the `Term`.
///
#[must_use]
pub fn prefix(self, prefix: Prefix) -> Self {
Self {
Expand All @@ -37,6 +45,8 @@ impl Builder {
}
}

/// Set the `atom` of the `Term`.
///
#[must_use]
pub fn atom(self, atom: Atom) -> Self {
Self {
Expand All @@ -45,6 +55,8 @@ impl Builder {
}
}

/// Set the `exponent` of the `Term`.
///
#[must_use]
pub fn exponent(self, exponent: Exponent) -> Self {
Self {
Expand All @@ -53,6 +65,8 @@ impl Builder {
}
}

/// Set the `annotation` of the `Term`.
///
#[must_use]
pub fn annotation<T>(self, annotation: T) -> Self
where
Expand Down
19 changes: 19 additions & 0 deletions crates/api/src/term/term_reduce.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,33 @@ pub(crate) enum ReducedTerm {
NotReducible,
}

/// This trait helps for implementing `Term` reducing/canceling across `Term` and its variants.
///
pub(crate) trait TermReduce: crate::composable::ComposablyEq<Term> {
/// It relies on the `ComposablyEq` trait to determine if `self` and `rhs` are of the same
/// prefix+atom combination. `wise_units` is conservative in this algorithm in that it won't
/// reduce/cancel `Term`s that are the same dimension but different prefix+atom combos, as
/// that falls more into the "unit conversion" category.
///
/// 1. If `self` and `rhs` are `ComposablyEq` and their resulting exponent is 0, then
/// `term_reduce` considers `self` and `rhs` to have canceled each other out.
/// 2. If `self` and `rhs` are `ComposablyEq` and their resulting exponent is non-zero, then
/// the two `Term`s have been combined into a new `Term` with the resulting exponent.
/// 3. If `self` and `rhs` are not `ComposablyEq`, they can't be reduced.
///
fn term_reduce(&self, rhs: &Term) -> ReducedTerm {
match self.composably_eq(rhs) {
Some(0) => ReducedTerm::ReducedAway,
Some(exponent) => ReducedTerm::ReducedToTerm(self.build(exponent)),
None => ReducedTerm::NotReducible,
}
}

/// This determines how to build a new `Term` in the case that `self` and `rhs` are
/// `ComposablyEq` and their resulting exponent is non-zero. For example, it allows handling
/// the case when `self` is a non-exponent type (ex. `Term::Atom`), but `exponent` is non-one;
/// in this case, the resulting `Term` should be a `Term::AtomExponent`.
///
fn build(&self, exponent: Exponent) -> Term;
}

Expand Down
23 changes: 22 additions & 1 deletion crates/api/src/term/variants.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
/// Defining `Term` as an `enum` gives us better control over the behavior of operations on `Unit`s
/// given certain `Term` variants. The structs that represent those variants are defined here,
/// along with some `traits` that help defining similar functionality across those types.
///
mod atom_annotation;
mod atom_exponent;
mod atom_exponent_annotation;
Expand Down Expand Up @@ -43,18 +47,29 @@ use super::{Annotation, Exponent, Factor, Term};
// ╭────────╮
// │ Traits │
// ╰────────╯
/// A helper trait for defining behavior of `Term` variants when it comes to defining `Pow`
/// behavior for each variant. All variants have different behavior for when `self.pow(0)`,
/// `self.pow(1)`, and `self.pow(42`. This type makes it cleaner to define that behavior.
///
pub enum PowOutput<Z, O, R> {
Zero(Z),
One(O),
Rest(R),
}

impl<Z, O, R> PowOutput<Z, O, R> {
/// Like `Option::unwrap_err()` or `Result::unwrap_err()` it returns the contents of
/// `PowOutput::Rest` or panics if `PowOutput` is of a different variant.
///
/// # Panics
///
/// This panics if `self` is `PowOutput::Zero` or `PowOutput::One`.
///
pub fn unwrap_rest(self) -> R {
if let Self::Rest(inner) = self {
inner
} else {
unreachable!()
panic!("Unwrapped PowOutput but no Rest variant value exists");
}
}
}
Expand All @@ -72,6 +87,12 @@ where
}
}

/// A helper traits for defining behavior of `Term` variants when it comes to defining `Inv`
/// behavior for each variant. This helps in cases where the result of `self.inv()` results in an
/// `exponent` of `1`--in many cases, we want to alter the variant of the `Term` to use
/// a non-`Exponent` variant. For example, if a `Term::AtomExponent` calls `pow(x)` and the
/// resulting exponent is 1, the new `Term` should be `Term::Atom`.
///
pub enum InvOutput<O, R> {
One(O),
Rest(R),
Expand Down
5 changes: 5 additions & 0 deletions crates/api/src/unit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,12 +206,17 @@ impl Unit {
self.simplify().to_string()
}

/// Convenience method for just getting internal `Terms` that have a non-negative exponent.
/// Used internally by `AsFraction`.
///
pub fn numerator_terms(&self) -> impl Iterator<Item = &Term> {
self.terms
.iter()
.filter(|term| !term.effective_exponent().is_negative())
}

/// Convenience method for just getting internal `Terms` that have a negative exponent.
///
pub fn denominator_terms(&self) -> impl Iterator<Item = &Term> {
self.terms
.iter()
Expand Down
Loading

0 comments on commit caae3c2

Please sign in to comment.