use num_bigint::Sign;
use crate::{
aux::{Owned, Stack},
ext::math::{Integer, Natural},
runtime::model::Dir,
};
use std::ops::{Add, AddAssign, Sub, SubAssign};
#[derive(Owned!, Default)]
#[repr(transparent)]
pub struct Money(pub Natural);
#[derive(Stack!)]
#[repr(transparent)]
pub struct MoneyRef<'a>(pub &'a Natural);
#[derive(Owned!, Default)]
#[repr(transparent)]
pub struct Value(pub Integer);
#[derive(Owned!)]
pub struct Balance {
between: Dir,
amount: Value,
}
impl<'a> From<&'a Money> for MoneyRef<'a> {
fn from(value: &'a Money) -> Self {
Self(&value.0)
}
}
impl Money {
pub fn zero() -> Self {
Self::default()
}
}
impl Value {
#[must_use]
pub fn is_black(&self) -> bool {
self.0 >= 0.into()
}
#[must_use]
pub fn is_red(&self) -> bool {
!self.is_black()
}
pub fn flip(&mut self) {
self.0 *= -1;
}
#[must_use]
pub fn to_abs(self) -> Money {
Money(self.0.into_parts().1)
}
#[must_use]
pub fn abs(&self) -> MoneyRef<'_> {
MoneyRef(self.0.magnitude())
}
#[must_use]
pub fn sign(&self) -> Sign {
self.0.sign()
}
pub fn align_to(&mut self, dir: &Dir) {
if dir.would_reorder() {
self.flip();
}
}
}
impl From<Money> for Value {
fn from(money: Money) -> Self {
Self(money.0.into())
}
}
impl Balance {
pub(crate) fn new(between: Dir, amount: Value) -> Self {
Self { between, amount }
}
#[must_use]
pub fn between(&self) -> &Dir {
&self.between
}
#[must_use]
pub fn amount(&self) -> &Value {
&self.amount
}
pub fn flip(&mut self) {
self.between.flip();
self.amount.flip();
}
}
macro_rules! calc {
(+ : $lhs:ty, $rhs:ty $( as $inner:ty )? ) => {
impl Add<$rhs> for $lhs {
type Output = Self;
fn add(self, rhs: $rhs) -> Self {
Self(self.0 + $(<$inner>::from)?(rhs.0))
}
}
};
(- : $lhs:ty, $rhs:ty $( as $inner:ty )? ) => {
impl Sub<$rhs> for $lhs {
type Output = Self;
fn sub(self, rhs: $rhs) -> Self {
Self(self.0 - $(<$inner>::from)?(rhs.0))
}
}
};
(+= : $lhs:ty, $rhs:ty ) => {
impl AddAssign<$rhs> for $lhs {
fn add_assign(&mut self, rhs: $rhs) {
*self = self.clone() + rhs;
}
}
};
(-= : $lhs:ty, $rhs:ty ) => {
impl SubAssign<$rhs> for $lhs {
fn sub_assign(&mut self, rhs: $rhs) {
*self = self.clone() - rhs;
}
}
};
}
calc!(+ : Money, Money);
calc!(- : Money, Money);
calc!(+= : Money, Money);
calc!(-= : Money, Money);
calc!(+ : Value, Value);
calc!(- : Value, Value);
calc!(+= : Value, Value);
calc!(-= : Value, Value);
calc!(+ : Value, Money as Integer);
calc!(- : Value, Money as Integer);
calc!(+= : Value, Money);
calc!(-= : Value, Money);