diff --git a/.changelog/3744.internal.md b/.changelog/3744.internal.md new file mode 100644 index 00000000000..893b4fe77a9 --- /dev/null +++ b/.changelog/3744.internal.md @@ -0,0 +1 @@ +runtime/common/quantity: Add Mul/Div implementations diff --git a/Makefile b/Makefile index d711d94fc43..f121b04c953 100644 --- a/Makefile +++ b/Makefile @@ -72,7 +72,7 @@ lint-git: @$(CHECK_GITLINT) lint-md: - @npx markdownlint-cli '**/*.md' --ignore .changelog/ + @npx markdownlint-cli@$(MARKDOWNLINT_CLI_VERSION) '**/*.md' --ignore .changelog/ lint-changelog: @$(CHECK_CHANGELOG_FRAGMENTS) diff --git a/common.mk b/common.mk index 625dfba41d8..3bdfaee2e92 100644 --- a/common.mk +++ b/common.mk @@ -44,6 +44,9 @@ define CONFIRM_ACTION = fi endef +# Version of the markdownlint-cli package to use. +MARKDOWNLINT_CLI_VERSION := 0.26.0 + # Name of git remote pointing to the canonical upstream git repository, i.e. # git@github.com:oasisprotocol/.git. GIT_ORIGIN_REMOTE ?= origin @@ -188,7 +191,7 @@ CHANGELOG_FRAGMENTS_BREAKING := $(wildcard .changelog/*breaking*.md) define CHECK_CHANGELOG_FRAGMENTS = exit_status=0; \ $(ECHO) "$(CYAN)*** Running markdownlint-cli for Change Log fragments... $(OFF)"; \ - npx markdownlint-cli --config .changelog/.markdownlint.yml .changelog/ || exit_status=$$?; \ + npx markdownlint-cli@$(MARKDOWNLINT_CLI_VERSION) --config .changelog/.markdownlint.yml .changelog/ || exit_status=$$?; \ $(ECHO) "$(CYAN)*** Running gitlint for Change Log fragments: $(OFF)"; \ for fragment in $(CHANGELOG_FRAGMENTS_NON_TRIVIAL); do \ $(ECHO) "- $$fragment"; \ diff --git a/runtime/src/common/quantity.rs b/runtime/src/common/quantity.rs index fa422f43c5a..0d00968302d 100644 --- a/runtime/src/common/quantity.rs +++ b/runtime/src/common/quantity.rs @@ -2,11 +2,11 @@ use core::cmp::Ordering::{Equal, Greater, Less}; use std::{ fmt, - ops::{Add, AddAssign, Sub}, + ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub}, }; use num_bigint::BigUint; -use num_traits::{Num, Zero}; +use num_traits::{CheckedDiv, CheckedSub, Num, Zero}; use serde; /// An arbitrary precision unsigned integer. @@ -19,11 +19,16 @@ impl Quantity { pub fn checked_sub(&self, other: &Quantity) -> Option { // NOTE: This does not implemented the num_traits::CheckedSub trait because this forces // one to also implement Sub which we explicitly don't want to do. - match self.0.cmp(&other.0) { - Less => None, - Equal => Some(Zero::zero()), - Greater => Some(Quantity(self.0.clone().sub(&other.0))), - } + self.0.checked_sub(&other.0).map(Quantity) + } + + /// Divides two numbers, checking for underflow, overflow and division by zero. If any of that + /// happens, `None` is returned. + #[inline] + pub fn checked_div(&self, other: &Quantity) -> Option { + // NOTE: This does not implemented the num_traits::CheckedDiv trait because this forces + // one to also implement Div which we explicitly don't want to do. + self.0.checked_div(&other.0).map(Quantity) } } @@ -67,6 +72,72 @@ impl<'a> AddAssign<&'a Quantity> for Quantity { } } +impl AddAssign for Quantity { + fn add_assign(&mut self, other: Quantity) { + self.0 += other.0; + } +} + +impl Add for Quantity { + type Output = Quantity; + + fn add(mut self, other: u64) -> Quantity { + self += other; + self + } +} + +impl AddAssign for Quantity { + fn add_assign(&mut self, other: u64) { + self.0 += other; + } +} + +impl Mul for Quantity { + type Output = Quantity; + + fn mul(mut self, rhs: Quantity) -> Quantity { + self *= &rhs; + self + } +} + +impl<'a> Mul<&'a Quantity> for Quantity { + type Output = Quantity; + + fn mul(mut self, rhs: &Quantity) -> Quantity { + self *= rhs; + self + } +} + +impl<'a> MulAssign<&'a Quantity> for Quantity { + fn mul_assign(&mut self, rhs: &Quantity) { + self.0 *= &rhs.0; + } +} + +impl MulAssign for Quantity { + fn mul_assign(&mut self, rhs: Quantity) { + self.0 *= rhs.0; + } +} + +impl Mul for Quantity { + type Output = Quantity; + + fn mul(mut self, other: u64) -> Quantity { + self *= other; + self + } +} + +impl MulAssign for Quantity { + fn mul_assign(&mut self, other: u64) { + self.0 *= other; + } +} + impl fmt::Display for Quantity { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.0.fmt(f) @@ -160,4 +231,56 @@ mod test { assert_eq!(dec, q, "serialization should round-trip"); } } + + #[test] + fn test_ops() { + // Add. + assert_eq!( + Quantity::from(1000) + Quantity::from(2000), + Quantity::from(3000) + ); + + let mut a = Quantity::from(1000); + a += Quantity::from(42); + assert_eq!(a, Quantity::from(1042)); + a += &Quantity::from(42); + assert_eq!(a, Quantity::from(1084)); + + let mut a = Quantity::from(1000); + a += 42; + assert_eq!(a, Quantity::from(1042)); + + // Sub. + let a = Quantity::from(1000); + assert_eq!( + a.checked_sub(&Quantity::from(42)), + Some(Quantity::from(958)) + ); + assert_eq!(a.checked_sub(&Quantity::from(1100)), None); + + // Mul. + assert_eq!( + Quantity::from(1000) * Quantity::from(1000), + Quantity::from(1_000_000) + ); + + let mut a = Quantity::from(1000); + a *= Quantity::from(1000); + assert_eq!(a, Quantity::from(1_000_000)); + a *= &Quantity::from(1000); + assert_eq!(a, Quantity::from(1_000_000_000)); + + let mut a = Quantity::from(1000); + a *= 1000; + assert_eq!(a, Quantity::from(1_000_000)); + + // Div. + let a = Quantity::from(1000); + assert_eq!(a.checked_div(&Quantity::from(3)), Some(Quantity::from(333))); + assert_eq!( + a.checked_div(&Quantity::from(1001)), + Some(Quantity::from(0)) + ); + assert_eq!(a.checked_div(&Quantity::from(0)), None); + } }