use std::borrow::Cow;
use duplicate::duplicate;
use ref_ops::{RefAdd, RefMul, RefSub};
#[cfg(feature = "shadowing")]
use std::ops::{Add, Div, Mul, Sub};
#[cfg(feature = "shadowing")]
use crate::symbolica_utils::SerializableAtom;
#[cfg(feature = "shadowing")]
use symbolica::{
atom::Atom,
domains::float::{Complex as SymbolicaComplex, Real},
state::State,
};
use crate::{
complex::{Complex, R},
contraction::RefZero,
};
pub trait SmallestUpgrade<T> {
type LCM;
type Order;
fn upgrade(self) -> Self::LCM;
}
pub struct LessThan;
pub struct GreaterThan;
pub struct Equal;
pub trait TryFromUpgrade<T> {
fn try_from_upgrade(value: &T) -> Option<Self>
where
Self: Sized;
}
pub trait TrySmallestUpgrade<T> {
type LCM: Clone;
fn try_upgrade(&self) -> Option<Cow<Self::LCM>>;
}
#[cfg(feature = "shadowing")]
impl<O: Real, U: Real, T: Real> TrySmallestUpgrade<SymbolicaComplex<T>> for SymbolicaComplex<U>
where
U: TrySmallestUpgrade<T, LCM = O>,
T: TrySmallestUpgrade<U, LCM = O>,
{
type LCM = SymbolicaComplex<O>;
fn try_upgrade(&self) -> Option<Cow<Self::LCM>>
where
Self::LCM: Clone,
{
let re = self.re.try_upgrade()?;
let im = self.im.try_upgrade()?;
Some(Cow::Owned(SymbolicaComplex::new(
re.into_owned(),
im.into_owned(),
)))
}
}
impl<O: R + Clone, U: R, T: R> TrySmallestUpgrade<Complex<T>> for Complex<U>
where
U: TrySmallestUpgrade<T, LCM = O>,
T: TrySmallestUpgrade<U, LCM = O>,
{
type LCM = Complex<O>;
fn try_upgrade(&self) -> Option<Cow<Self::LCM>>
where
Self::LCM: Clone,
{
let re = self.re.try_upgrade()?;
let im = self.im.try_upgrade()?;
Some(Cow::Owned(Complex::new(re.into_owned(), im.into_owned())))
}
}
impl<T, U> TryFromUpgrade<T> for U
where
T: TrySmallestUpgrade<U, LCM = U>,
U: Clone,
{
fn try_from_upgrade(value: &T) -> Option<Self> {
let cow = value.try_upgrade()?;
Some(cow.into_owned())
}
}
pub trait TryIntoUpgrade<T> {
fn try_into_upgrade(&self) -> Option<T>;
}
impl<T, U> TryIntoUpgrade<U> for T
where
U: TryFromUpgrade<T>,
{
fn try_into_upgrade(&self) -> Option<U> {
U::try_from_upgrade(self)
}
}
duplicate! {
[equal;
[i8];
[i16];
[i32];
[f64];
]
impl TrySmallestUpgrade<equal> for equal {
type LCM = equal;
fn try_upgrade(&self) -> Option<Cow<Self::LCM>>
where
Self::LCM: Clone {
Some(Cow::Borrowed(self))
}
}
}
#[cfg(feature = "shadowing")]
impl TrySmallestUpgrade<Atom> for Atom {
type LCM = Atom;
fn try_upgrade(&self) -> Option<Cow<Self::LCM>>
where
Self::LCM: Clone,
{
Some(Cow::Borrowed(self))
}
}
#[cfg(feature = "shadowing")]
impl TrySmallestUpgrade<SerializableAtom> for SerializableAtom {
type LCM = SerializableAtom;
fn try_upgrade(&self) -> Option<Cow<Self::LCM>>
where
Self::LCM: Clone,
{
Some(Cow::Borrowed(self))
}
}
duplicate! {
[smaller larger;
[f32] [f64];
[i32] [f64];]
impl TrySmallestUpgrade<larger> for smaller {
type LCM = larger;
fn try_upgrade(&self) -> Option<Cow<Self::LCM>>
where
Self::LCM: Clone {
Some(Cow::Owned(larger::from(*self)))
}
}
impl TrySmallestUpgrade<smaller> for larger {
type LCM = larger;
fn try_upgrade(&self) -> Option<Cow<Self::LCM>>
where
Self::LCM: Clone {
Some(Cow::Borrowed(self))
}
}
}
impl<T> TrySmallestUpgrade<Complex<T>> for T
where
T: RefZero + Clone,
{
type LCM = Complex<T>;
fn try_upgrade(&self) -> Option<Cow<Self::LCM>> {
let new = Complex::new(self.clone(), self.ref_zero());
Some(Cow::Owned(new))
}
}
#[cfg(feature = "shadowing")]
impl TrySmallestUpgrade<Atom> for f64 {
type LCM = Atom;
fn try_upgrade(&self) -> Option<Cow<Self::LCM>> {
let natrat = symbolica::domains::rational::Rational::from(*self);
let symrat = Atom::new_num(symbolica::coefficient::Coefficient::from(natrat));
Some(Cow::Owned(symrat))
}
}
#[cfg(feature = "shadowing")]
impl TrySmallestUpgrade<SerializableAtom> for f64 {
type LCM = SerializableAtom;
fn try_upgrade(&self) -> Option<Cow<Self::LCM>> {
let natrat = symbolica::domains::rational::Rational::from(*self);
let symrat = Atom::new_num(symbolica::coefficient::Coefficient::from(natrat)).into();
Some(Cow::Owned(symrat))
}
}
#[cfg(feature = "shadowing")]
impl TrySmallestUpgrade<Atom> for i32 {
type LCM = Atom;
fn try_upgrade(&self) -> Option<Cow<Self::LCM>> {
let symnum = Atom::new_num(*self);
Some(Cow::Owned(symnum))
}
}
#[cfg(feature = "shadowing")]
impl TrySmallestUpgrade<SerializableAtom> for i32 {
type LCM = SerializableAtom;
fn try_upgrade(&self) -> Option<Cow<Self::LCM>> {
let symnum = Atom::new_num(*self).into();
Some(Cow::Owned(symnum))
}
}
#[cfg(feature = "shadowing")]
impl TrySmallestUpgrade<Atom> for Complex<f64> {
type LCM = Atom;
fn try_upgrade(&self) -> Option<Cow<Self::LCM>> {
let real: Cow<'_, Atom> = <f64 as TrySmallestUpgrade<Atom>>::try_upgrade(&self.re)?;
let imag: Cow<'_, Atom> = <f64 as TrySmallestUpgrade<Atom>>::try_upgrade(&self.im)?;
let i = Atom::new_var(State::I);
let symrat = (i * imag.as_ref()) + real.as_ref();
Some(Cow::Owned(symrat))
}
}
#[cfg(feature = "shadowing")]
impl Add for SerializableAtom {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
SerializableAtom::from(self.0 + rhs.0)
}
}
#[cfg(feature = "shadowing")]
impl<'a> Add<&SerializableAtom> for &'a SerializableAtom {
type Output = SerializableAtom;
fn add(self, rhs: &SerializableAtom) -> Self::Output {
SerializableAtom::from(&self.0 + &rhs.0)
}
}
#[cfg(feature = "shadowing")]
impl Add<&SerializableAtom> for SerializableAtom {
type Output = SerializableAtom;
fn add(self, rhs: &SerializableAtom) -> Self::Output {
SerializableAtom::from(&self.0 + &rhs.0)
}
}
#[cfg(feature = "shadowing")]
impl<'a> Add<SerializableAtom> for &'a SerializableAtom {
type Output = SerializableAtom;
fn add(self, rhs: SerializableAtom) -> Self::Output {
SerializableAtom::from(&self.0 + &rhs.0)
}
}
#[cfg(feature = "shadowing")]
impl Sub for SerializableAtom {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
SerializableAtom::from(self.0 - rhs.0)
}
}
#[cfg(feature = "shadowing")]
impl<'a> Sub<&SerializableAtom> for &'a SerializableAtom {
type Output = SerializableAtom;
fn sub(self, rhs: &SerializableAtom) -> Self::Output {
SerializableAtom::from(&self.0 - &rhs.0)
}
}
#[cfg(feature = "shadowing")]
impl Sub<&SerializableAtom> for SerializableAtom {
type Output = SerializableAtom;
fn sub(self, rhs: &SerializableAtom) -> Self::Output {
SerializableAtom::from(&self.0 - &rhs.0)
}
}
#[cfg(feature = "shadowing")]
impl<'a> Sub<SerializableAtom> for &'a SerializableAtom {
type Output = SerializableAtom;
fn sub(self, rhs: SerializableAtom) -> Self::Output {
SerializableAtom::from(&self.0 - &rhs.0)
}
}
#[cfg(feature = "shadowing")]
impl Mul for SerializableAtom {
type Output = Self;
fn mul(self, rhs: Self) -> Self::Output {
SerializableAtom::from(self.0 * rhs.0)
}
}
#[cfg(feature = "shadowing")]
impl<'a> Mul<&SerializableAtom> for &'a SerializableAtom {
type Output = SerializableAtom;
fn mul(self, rhs: &SerializableAtom) -> Self::Output {
SerializableAtom::from(&self.0 * &rhs.0)
}
}
#[cfg(feature = "shadowing")]
impl Mul<&SerializableAtom> for SerializableAtom {
type Output = SerializableAtom;
fn mul(self, rhs: &SerializableAtom) -> Self::Output {
SerializableAtom::from(&self.0 * &rhs.0)
}
}
#[cfg(feature = "shadowing")]
impl<'a> Mul<SerializableAtom> for &'a SerializableAtom {
type Output = SerializableAtom;
fn mul(self, rhs: SerializableAtom) -> Self::Output {
SerializableAtom::from(&self.0 * &rhs.0)
}
}
#[cfg(feature = "shadowing")]
impl Div for SerializableAtom {
type Output = Self;
fn div(self, rhs: Self) -> Self::Output {
SerializableAtom::from(self.0 / rhs.0)
}
}
#[cfg(feature = "shadowing")]
impl<'a> Div<&SerializableAtom> for &'a SerializableAtom {
type Output = SerializableAtom;
fn div(self, rhs: &SerializableAtom) -> Self::Output {
SerializableAtom::from(&self.0 / &rhs.0)
}
}
#[cfg(feature = "shadowing")]
impl Div<&SerializableAtom> for SerializableAtom {
type Output = SerializableAtom;
fn div(self, rhs: &SerializableAtom) -> Self::Output {
SerializableAtom::from(&self.0 / &rhs.0)
}
}
#[cfg(feature = "shadowing")]
impl<'a> Div<SerializableAtom> for &'a SerializableAtom {
type Output = SerializableAtom;
fn div(self, rhs: SerializableAtom) -> Self::Output {
SerializableAtom::from(&self.0 / &rhs.0)
}
}
#[cfg(feature = "shadowing")]
impl TrySmallestUpgrade<SerializableAtom> for Complex<f64> {
type LCM = SerializableAtom;
fn try_upgrade(&self) -> Option<Cow<Self::LCM>> {
let real: Cow<'_, SerializableAtom> =
<f64 as TrySmallestUpgrade<SerializableAtom>>::try_upgrade(&self.re)?;
let imag: Cow<'_, SerializableAtom> =
<f64 as TrySmallestUpgrade<SerializableAtom>>::try_upgrade(&self.im)?;
let i = SerializableAtom::from(Atom::new_var(State::I));
let symrat = (i * imag.as_ref()) + real.as_ref();
Some(Cow::Owned(symrat))
}
}
#[cfg(feature = "shadowing")]
duplicate! {
[smaller larger;
[f64] [Atom];
[f64] [SerializableAtom];
[i32] [Atom];
[i32] [SerializableAtom];
[Complex<f64>] [Atom];
[Complex<f64>] [SerializableAtom];
]
impl TrySmallestUpgrade<smaller> for larger {
type LCM = larger;
fn try_upgrade(&self) -> Option<Cow<Self::LCM>>
where
Self::LCM: Clone {
Some(Cow::Borrowed(self))
}
}
}
duplicate! {
[smaller larger;
[f64] [Complex<f64>];
]
impl TrySmallestUpgrade<smaller> for larger {
type LCM = larger;
fn try_upgrade(&self) -> Option<Cow<Self::LCM>>
where
Self::LCM: Clone {
Some(Cow::Borrowed(self))
}
}
}
pub trait FallibleMul<T = Self> {
type Output;
fn mul_fallible(&self, rhs: &T) -> Option<Self::Output>;
}
impl<T, U> FallibleMul<T> for U
where
U: TrySmallestUpgrade<T>,
T: TrySmallestUpgrade<U, LCM = <U as TrySmallestUpgrade<T>>::LCM>,
U::LCM: Clone,
U::LCM: for<'b> RefMul<&'b U::LCM, Output = U::LCM>,
{
type Output = U::LCM;
fn mul_fallible(&self, rhs: &T) -> Option<Self::Output> {
let lhs = self.try_upgrade()?;
let rhs = rhs.try_upgrade()?;
Some(lhs.as_ref().ref_mul(rhs.as_ref()))
}
}
pub trait FallibleAdd<T> {
type Output;
fn add_fallible(&self, rhs: &T) -> Option<Self::Output>;
}
impl<T, U> FallibleAdd<T> for U
where
U: TrySmallestUpgrade<T>,
T: TrySmallestUpgrade<U, LCM = U::LCM>,
U::LCM: for<'b> RefAdd<&'b U::LCM, Output = U::LCM>,
{
type Output = U::LCM;
fn add_fallible(&self, rhs: &T) -> Option<Self::Output> {
let lhs = self.try_upgrade()?;
let rhs = rhs.try_upgrade()?;
Some(lhs.as_ref().ref_add(rhs.as_ref()))
}
}
pub trait FallibleSub<T> {
type Output;
fn sub_fallible(&self, rhs: &T) -> Option<Self::Output>;
}
impl<T, U> FallibleSub<T> for U
where
U: TrySmallestUpgrade<T>,
T: TrySmallestUpgrade<U, LCM = <U as TrySmallestUpgrade<T>>::LCM>,
U::LCM: Clone,
U::LCM: for<'b> RefSub<&'b U::LCM, Output = U::LCM>,
{
type Output = U::LCM;
fn sub_fallible(&self, rhs: &T) -> Option<Self::Output> {
let lhs = self.try_upgrade()?;
let rhs = rhs.try_upgrade()?;
Some(lhs.as_ref().ref_sub(rhs.as_ref()))
}
}
pub trait FallibleAddAssign<T = Self> {
fn add_assign_fallible(&mut self, rhs: &T);
}
impl<T, U> FallibleAddAssign<T> for U
where
U: TrySmallestUpgrade<T, LCM = U>,
T: TrySmallestUpgrade<U, LCM = U>,
U::LCM: Clone,
U::LCM: for<'b> RefAdd<&'b U::LCM, Output = U::LCM>,
{
fn add_assign_fallible(&mut self, rhs: &T) {
let lhs = self.try_upgrade().unwrap();
let rhs = rhs.try_upgrade().unwrap();
let out = lhs.as_ref().ref_add(rhs.as_ref());
*self = out;
}
}
pub trait FallibleSubAssign<T> {
fn sub_assign_fallible(&mut self, rhs: &T);
}
impl<T, U> FallibleSubAssign<T> for U
where
U: TrySmallestUpgrade<T, LCM = U>,
T: TrySmallestUpgrade<U, LCM = U>,
U::LCM: Clone,
U::LCM: for<'b> RefSub<&'b U::LCM, Output = U::LCM>,
{
fn sub_assign_fallible(&mut self, rhs: &T) {
let lhs = self.try_upgrade().unwrap();
let rhs = rhs.try_upgrade().unwrap();
let out = lhs.as_ref().ref_sub(rhs.as_ref());
*self = out;
}
}
#[cfg(test)]
mod test {
#[cfg(feature = "shadowing")]
use ahash::{HashMap, HashMapExt};
#[cfg(feature = "shadowing")]
use symbolica::{atom::Atom, state::State};
#[cfg(feature = "shadowing")]
use crate::complex::Complex;
use super::*;
#[test]
fn i32_arithmetic() {
let a: i32 = 4;
let b: i32 = 4;
let mut c = a.mul_fallible(&b).unwrap();
c.add_assign_fallible(&a);
c.sub_assign_fallible(&b);
let d = b.sub_fallible(&a);
let e = a.add_fallible(&b);
assert_eq!(c, 16);
assert_eq!(d, Some(0));
assert_eq!(e, Some(8));
}
#[test]
#[cfg(feature = "shadowing")]
fn test_fallible_mul() {
let a: i32 = 4;
let b: f64 = 4.;
let mut c: f64 = a.mul_fallible(&b).unwrap();
c.add_assign_fallible(&a);
let d: Option<f64> = b.mul_fallible(&a);
let a: &i32 = &a;
let e: Option<f64> = a.mul_fallible(&b);
assert_eq!(c, 20.);
assert_eq!(d, Some(16.));
assert_eq!(e, Some(16.));
let a = Atom::parse("a(2)").unwrap();
let b = &Atom::parse("b(1)").unwrap();
let mut f = a.mul_fallible(&4.).unwrap();
f.add_assign_fallible(b);
let i = Atom::new_var(State::I);
f.add_assign_fallible(&i);
let function_map = HashMap::new();
let mut cache = HashMap::new();
let mut const_map = HashMap::new();
const_map.insert(i.as_view(), Complex::<f64>::new(0., 1.).into());
const_map.insert(a.as_view(), Complex::<f64>::new(3., 1.).into());
const_map.insert(b.as_view(), Complex::<f64>::new(3., 1.).into());
let ev: symbolica::domains::float::Complex<f64> = f
.as_view()
.evaluate(|r| r.into(), &const_map, &function_map, &mut cache)
.unwrap();
println!("{}", ev);
let g = Complex::new(0.1, 3.);
let mut h = a.sub_fallible(&g).unwrap();
h.add_assign_fallible(&a);
let _f = a.mul_fallible(&a);
Atom::default();
println!("{}", h);
}
}