Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

runtime/common/quantity: Add Mul/Div implementations #3744

Merged
merged 2 commits into from
Mar 2, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .changelog/3744.internal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
runtime/common/quantity: Add Mul/Div implementations
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
5 changes: 4 additions & 1 deletion common.mk
Original file line number Diff line number Diff line change
Expand Up @@ -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.
# [email protected]:oasisprotocol/<repo-name>.git.
GIT_ORIGIN_REMOTE ?= origin
Expand Down Expand Up @@ -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"; \
Expand Down
137 changes: 130 additions & 7 deletions runtime/src/common/quantity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -19,11 +19,16 @@ impl Quantity {
pub fn checked_sub(&self, other: &Quantity) -> Option<Quantity> {
// 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<Quantity> {
// 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)
}
}

Expand Down Expand Up @@ -67,6 +72,72 @@ impl<'a> AddAssign<&'a Quantity> for Quantity {
}
}

impl AddAssign<Quantity> for Quantity {
fn add_assign(&mut self, other: Quantity) {
self.0 += other.0;
}
}

impl Add<u64> for Quantity {
type Output = Quantity;

fn add(mut self, other: u64) -> Quantity {
self += other;
self
}
}

impl AddAssign<u64> 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<Quantity> for Quantity {
fn mul_assign(&mut self, rhs: Quantity) {
self.0 *= rhs.0;
}
}

impl Mul<u64> for Quantity {
type Output = Quantity;

fn mul(mut self, other: u64) -> Quantity {
self *= other;
self
}
}

impl MulAssign<u64> 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)
Expand Down Expand Up @@ -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);
}
}