use std::{iter::Sum, ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}};
use rust_decimal::Decimal;
use crate::{t_types::{MoneyValue, Quotation}, QuotationExt};
const DIVIDER_SCALE: u32 = 9;
const DIVIDER: i128 = 10i128.pow(DIVIDER_SCALE);
fn from_quotation(q: Quotation) -> i128 {
q.units as i128 * DIVIDER + q.nano as i128
}
fn to_quotation(n: i128) -> Quotation {
Quotation { units: (n/DIVIDER) as i64, nano: (n % DIVIDER) as i32}
}
impl Add for Quotation {
type Output = Quotation;
fn add(self, rhs: Self) -> Self::Output {
let this = from_quotation(self);
let rhs = from_quotation(rhs);
to_quotation(this + rhs)
}
}
impl Sub for Quotation {
type Output = Quotation;
fn sub(self, rhs: Self) -> Self::Output {
let this = from_quotation(self);
let rhs = from_quotation(rhs);
to_quotation(this - rhs)
}
}
impl<Rhs: Into<i128>> Mul<Rhs> for Quotation {
type Output = Quotation;
fn mul(self, rhs: Rhs) -> Self::Output {
let this = from_quotation(self);
to_quotation(this * rhs.into())
}
}
impl Mul<Quotation> for Quotation {
type Output = Quotation;
fn mul(self, rhs: Quotation) -> Self::Output {
let this = from_quotation(self);
let rhs = from_quotation(rhs);
to_quotation(this * rhs / DIVIDER)
}
}
impl<Rhs: Into<i128>> Div<Rhs> for Quotation {
type Output = Quotation;
fn div(self, rhs: Rhs) -> Self::Output {
let this = from_quotation(self);
to_quotation(this / rhs.into())
}
}
impl Div<Quotation> for Quotation {
type Output = Quotation;
fn div(self, rhs: Quotation) -> Self::Output {
let this = from_quotation(self);
let rhs = from_quotation(rhs);
to_quotation(this * DIVIDER / rhs)
}
}
impl From<MoneyValue> for Quotation {
fn from(value: MoneyValue) -> Self {
Self { units: value.units, nano: value.nano }
}
}
impl<T> MulAssign<T> for Quotation where Quotation: Mul<T>, <Quotation as Mul<T>>::Output: Into<Self> {
fn mul_assign(&mut self, rhs: T) {
*self = (*self * rhs).into()
}
}
impl<T> DivAssign<T> for Quotation where Quotation: Div<T>, <Quotation as Div<T>>::Output: Into<Self> {
fn div_assign(&mut self, rhs: T) {
*self = (*self / rhs).into()
}
}
impl<T> AddAssign<T> for Quotation where Quotation: Add<T>, <Quotation as Add<T>>::Output: Into<Self> {
fn add_assign(&mut self, rhs: T) {
*self = (*self + rhs).into()
}
}
impl<T> SubAssign<T> for Quotation where Quotation: Sub<T>, <Quotation as Sub<T>>::Output: Into<Self> {
fn sub_assign(&mut self, rhs: T) {
*self = (*self - rhs).into()
}
}
impl std::fmt::Display for Quotation {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let fract: f32 = self.nano as f32 / DIVIDER as f32;
write!(f, "{}", self.units as f32 + fract)
}
}
impl Sum for Quotation {
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
to_quotation(iter.map(|x|from_quotation(x)).sum())
}
}
impl std::fmt::Display for MoneyValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let q = Quotation {units: self.units, nano: self.nano};
write!(f, "{} {}", q, self.currency)
}
}
impl From<f64> for Quotation {
fn from(value: f64) -> Self {
Quotation { units: value.trunc() as i64, nano: (value.fract() * DIVIDER as f64) as i32 }
}
}
impl Into<f64> for Quotation {
fn into(self) -> f64 {
self.units as f64 + self.nano as f64/DIVIDER as f64
}
}
impl From<(i64, u32)> for Quotation {
fn from((m,e): (i64,u32)) -> Self {
q(m as i128, e)
}
}
impl QuotationExt for Quotation {
fn floor(&self, increment: Quotation) -> Self {
let mut del = *self/increment;
if del.nano == 0 {
return *self;
}
del.nano = 0;
del * increment
}
fn round(&self, increment: Quotation) -> Self {
let floor = self.floor(increment);
if (*self - floor) * 2 >= increment {
floor + increment
} else {
floor
}
}
}
impl Into<Decimal> for Quotation {
fn into(self) -> Decimal {
let v: i128 = from_quotation(self);
Decimal::from_i128_with_scale(v, DIVIDER_SCALE)
}
}
impl From<Decimal> for Quotation {
fn from(value: Decimal) -> Self {
q(value.mantissa(), value.scale())
}
}
#[test]
fn test_fromdec() {
let d = Decimal::from_str_exact("1.25").unwrap();
let quot = Quotation::from(d);
assert_eq!(quot, Quotation::from(1.25f64));
let d = Decimal::from_str_exact("-1.256").unwrap();
let quot = Quotation::from(d);
assert_eq!(quot, Quotation::from(-1.256f64))
}
#[test]
fn test_todec() {
let qq = Quotation::from(2.5);
let d: Decimal = qq.into();
assert_eq!(Decimal::from_str_exact("2.5").unwrap(), d);
let qq = Quotation::from(-2.5);
let d: Decimal = qq.into();
assert_eq!(Decimal::from_str_exact("-2.5").unwrap(), d);
}
fn q(m: i128, e: u32) -> Quotation {
to_quotation(m * DIVIDER/10i128.pow(e))
}
#[test]
fn test_round() {
let x = q(11, 0);
let i = q(3,0);
assert_eq!(x.floor(i), q(9,0));
assert_eq!(x.round(i), q(12,0));
let i = q(3,1);
assert_eq!(x.floor(i), q(108,1));
assert_eq!(x.round(i), q(111,1));
let i = q(5, 1);
assert_eq!(x.floor(i), x);
assert_eq!(x.round(i), x);
}