use std::marker::PhantomData;
use brk_types::{Cents, CentsSigned, Dollars, Sats, StoredF32, StoredU64};
use vecdb::{BinaryTransform, unlikely};
use crate::internal::FixedRatio;
pub struct RatioU64<P>(PhantomData<P>);
impl<P: FixedRatio> BinaryTransform<StoredU64, StoredU64, P> for RatioU64<P> {
#[inline(always)]
fn apply(numerator: StoredU64, denominator: StoredU64) -> P {
if *denominator > 0 {
P::from(*numerator as f64 / *denominator as f64)
} else {
P::default()
}
}
}
pub struct RatioSats<P>(PhantomData<P>);
impl<P: FixedRatio> BinaryTransform<Sats, Sats, P> for RatioSats<P> {
#[inline(always)]
fn apply(numerator: Sats, denominator: Sats) -> P {
if *denominator > 0 {
P::from(*numerator as f64 / *denominator as f64)
} else {
P::default()
}
}
}
pub struct RatioCents<P>(PhantomData<P>);
impl<P: FixedRatio> BinaryTransform<Cents, Cents, P> for RatioCents<P> {
#[inline(always)]
fn apply(numerator: Cents, denominator: Cents) -> P {
let denominator = f64::from(denominator);
if unlikely(denominator == 0.0) {
P::default()
} else {
P::from(f64::from(numerator) / denominator)
}
}
}
pub struct RatioDollars<P>(PhantomData<P>);
impl<P: FixedRatio> BinaryTransform<Dollars, Dollars, P> for RatioDollars<P> {
#[inline(always)]
fn apply(numerator: Dollars, denominator: Dollars) -> P {
let ratio = f64::from(numerator) / f64::from(denominator);
if ratio.is_finite() {
P::from(ratio)
} else {
P::default()
}
}
}
pub struct RatioCentsSignedCents<P>(PhantomData<P>);
impl<P: FixedRatio> BinaryTransform<CentsSigned, Cents, P> for RatioCentsSignedCents<P> {
#[inline(always)]
fn apply(numerator: CentsSigned, denominator: Cents) -> P {
let denominator = f64::from(denominator);
if unlikely(denominator == 0.0) {
P::default()
} else {
P::from(numerator.inner() as f64 / denominator)
}
}
}
pub struct RatioDiffF32<P>(PhantomData<P>);
impl<P: FixedRatio> BinaryTransform<StoredF32, StoredF32, P> for RatioDiffF32<P> {
#[inline(always)]
fn apply(value: StoredF32, base: StoredF32) -> P {
if base.is_nan() || *base == 0.0 {
P::default()
} else {
P::from((*value / *base - 1.0) as f64)
}
}
}
pub struct RatioDiffDollars<P>(PhantomData<P>);
impl<P: FixedRatio> BinaryTransform<Dollars, Dollars, P> for RatioDiffDollars<P> {
#[inline(always)]
fn apply(close: Dollars, base: Dollars) -> P {
let base = f64::from(base);
if base == 0.0 {
P::default()
} else {
P::from(f64::from(close) / base - 1.0)
}
}
}
pub struct RatioDiffCents<P>(PhantomData<P>);
impl<P: FixedRatio> BinaryTransform<Cents, Cents, P> for RatioDiffCents<P> {
#[inline(always)]
fn apply(close: Cents, base: Cents) -> P {
let base = f64::from(base);
if base == 0.0 {
P::default()
} else {
P::from(f64::from(close) / base - 1.0)
}
}
}
#[cfg(test)]
mod tests {
use brk_types::PartsPerMillion32;
use super::*;
#[test]
fn cents_ratios_propagate_nan() {
assert!(RatioCents::<PartsPerMillion32>::apply(Cents::NAN, Cents::new(100)).is_nan());
assert!(RatioCents::<PartsPerMillion32>::apply(Cents::new(100), Cents::NAN).is_nan());
assert!(
RatioCentsSignedCents::<PartsPerMillion32>::apply(CentsSigned::new(100), Cents::NAN,)
.is_nan()
);
}
}