#[allow(unused_imports)]
use super::{BinaryStorage, ComputeStorage, StackValue, StackEvaluator, DECIMAL_DP_PROMOTION_THRESHOLD};
use super::compute::{upscale_to_compute, compute_add, compute_subtract, compute_multiply, compute_divide, compute_negate};
use super::domain::{
binary_from_storage, binary_to_storage,
decimal_from_storage, decimal_to_storage,
ternary_from_storage, ternary_to_storage,
shadow_negate, shadow_add, shadow_subtract, shadow_multiply, shadow_divide,
};
use crate::fixed_point::universal::tier_types::CompactShadow;
#[allow(unused_imports)]
use crate::fixed_point::domains::symbolic::rational::rational_number::{RationalNumber, OverflowDetected};
use crate::deployment_profiles::DeploymentProfile;
use crate::fixed_point::router::fractal_topology::{
classify, route_binary_op, coerce_to_decimal, OpId, DomainChoice,
};
impl StackEvaluator {
pub(crate) fn negate_value(&mut self, value: StackValue) -> Result<StackValue, OverflowDetected> {
match value {
StackValue::BinaryCompute(tier, val, ref shadow) => {
Ok(StackValue::BinaryCompute(tier, compute_negate(val), shadow_negate(shadow)))
}
StackValue::DecimalCompute(tier, val, ref shadow) => {
use crate::fixed_point::domains::decimal_fixed::transcendental::decimal_compute_neg;
Ok(StackValue::DecimalCompute(tier, decimal_compute_neg(val), shadow_negate(shadow)))
}
StackValue::Binary(tier, val, ref shadow) => {
let binary = binary_from_storage(tier, &val)?;
let result = binary.negate()?;
let (new_tier, storage) = binary_to_storage(&result);
Ok(StackValue::Binary(new_tier, storage, shadow_negate(shadow)))
}
StackValue::Decimal(dec, val, ref shadow) => {
let decimal = decimal_from_storage(dec, &val)?;
let result = decimal.negate()?;
let (new_dec, storage) = decimal_to_storage(&result);
Ok(StackValue::Decimal(new_dec, storage, shadow_negate(shadow)))
}
StackValue::Ternary(tier, val, ref shadow) => {
let ternary = ternary_from_storage(tier, &val)?;
let result = ternary.negate()?;
let (new_tier, storage) = ternary_to_storage(&result);
Ok(StackValue::Ternary(new_tier, storage, shadow_negate(shadow)))
}
StackValue::Symbolic(s) => {
Ok(StackValue::Symbolic(s.try_negate()?))
}
StackValue::Error(e) => Ok(StackValue::Error(e)),
}
}
pub(crate) fn add_values(&mut self, left: StackValue, right: StackValue) -> Result<StackValue, OverflowDetected> {
use crate::fixed_point::domains::decimal_fixed::transcendental::{
decimal_compute_add, decimal_upscale_to_compute,
};
match (&left, &right) {
(StackValue::DecimalCompute(t1, v1, s1), StackValue::DecimalCompute(_t2, v2, s2)) => {
return Ok(StackValue::DecimalCompute(*t1, decimal_compute_add(*v1, *v2), shadow_add(s1, s2)));
}
(StackValue::DecimalCompute(t1, v1, s1), StackValue::Decimal(dp, scaled, s2)) => {
let v2 = decimal_upscale_to_compute(*scaled, *dp)?;
return Ok(StackValue::DecimalCompute(*t1, decimal_compute_add(*v1, v2), shadow_add(s1, s2)));
}
(StackValue::Decimal(dp, scaled, s1), StackValue::DecimalCompute(t2, v2, s2)) => {
let v1 = decimal_upscale_to_compute(*scaled, *dp)?;
return Ok(StackValue::DecimalCompute(*t2, decimal_compute_add(v1, *v2), shadow_add(s1, s2)));
}
_ => {}
}
match (&left, &right) {
(StackValue::BinaryCompute(t1, v1, s1), StackValue::BinaryCompute(_t2, v2, s2)) => {
return Ok(StackValue::BinaryCompute(*t1, compute_add(*v1, *v2), shadow_add(s1, s2)));
}
(StackValue::BinaryCompute(t1, v1, s1), StackValue::Binary(_, v2, s2)) => {
let v2_compute = upscale_to_compute(*v2);
return Ok(StackValue::BinaryCompute(*t1, compute_add(*v1, v2_compute), shadow_add(s1, s2)));
}
(StackValue::Binary(_, v1, s1), StackValue::BinaryCompute(t2, v2, s2)) => {
let v1_compute = upscale_to_compute(*v1);
return Ok(StackValue::BinaryCompute(*t2, compute_add(v1_compute, *v2), shadow_add(s1, s2)));
}
(StackValue::BinaryCompute(t1, v1, s1), other) | (other, StackValue::BinaryCompute(t1, v1, s1)) => {
let other_compute = self.to_compute_storage(other)?;
return Ok(StackValue::BinaryCompute(*t1, compute_add(*v1, other_compute), shadow_add(s1, &other.shadow())));
}
_ => {}
}
if left.domain_type() == right.domain_type() {
match (&left, &right) {
(StackValue::Binary(t1, v1, s1), StackValue::Binary(t2, v2, s2)) => {
let binary_a = binary_from_storage(*t1, v1)?;
let binary_b = binary_from_storage(*t2, v2)?;
let result = binary_a.add(&binary_b)?;
let (tier, storage) = binary_to_storage(&result);
Ok(StackValue::Binary(tier, storage, shadow_add(s1, s2)))
}
(StackValue::Decimal(d1, v1, s1), StackValue::Decimal(d2, v2, s2)) => {
match (decimal_from_storage(*d1, v1), decimal_from_storage(*d2, v2)) {
(Ok(decimal_a), Ok(decimal_b)) => {
match decimal_a.add(&decimal_b) {
Ok(result) => {
let (dec, storage) = decimal_to_storage(&result);
Ok(StackValue::Decimal(dec, storage, shadow_add(s1, s2)))
}
Err(_) => self.add_via_rational(left, right),
}
}
_ => self.add_via_rational(left, right),
}
}
(StackValue::Ternary(t1, v1, s1), StackValue::Ternary(t2, v2, s2)) => {
let ternary_a = ternary_from_storage(*t1, v1)?;
let ternary_b = ternary_from_storage(*t2, v2)?;
let result = ternary_a.add(&ternary_b)?;
let (tier, storage) = ternary_to_storage(&result);
Ok(StackValue::Ternary(tier, storage, shadow_add(s1, s2)))
}
_ => {
self.add_via_rational(left, right)
}
}
} else {
if let Some((cl, cr)) = self.try_route_coerce(OpId::Add, &left, &right) {
self.add_values(cl, cr)
} else {
self.add_via_rational(left, right)
}
}
}
pub(crate) fn subtract_values(&mut self, left: StackValue, right: StackValue) -> Result<StackValue, OverflowDetected> {
use crate::fixed_point::domains::decimal_fixed::transcendental::{
decimal_compute_sub, decimal_upscale_to_compute,
};
match (&left, &right) {
(StackValue::DecimalCompute(t1, v1, s1), StackValue::DecimalCompute(_t2, v2, s2)) => {
return Ok(StackValue::DecimalCompute(*t1, decimal_compute_sub(*v1, *v2), shadow_subtract(s1, s2)));
}
(StackValue::DecimalCompute(t1, v1, s1), StackValue::Decimal(dp, scaled, s2)) => {
let v2 = decimal_upscale_to_compute(*scaled, *dp)?;
return Ok(StackValue::DecimalCompute(*t1, decimal_compute_sub(*v1, v2), shadow_subtract(s1, s2)));
}
(StackValue::Decimal(dp, scaled, s1), StackValue::DecimalCompute(t2, v2, s2)) => {
let v1 = decimal_upscale_to_compute(*scaled, *dp)?;
return Ok(StackValue::DecimalCompute(*t2, decimal_compute_sub(v1, *v2), shadow_subtract(s1, s2)));
}
_ => {}
}
match (&left, &right) {
(StackValue::BinaryCompute(t1, v1, s1), StackValue::BinaryCompute(_t2, v2, s2)) => {
return Ok(StackValue::BinaryCompute(*t1, compute_subtract(*v1, *v2), shadow_subtract(s1, s2)));
}
(StackValue::BinaryCompute(t1, v1, s1), StackValue::Binary(_, v2, s2)) => {
let v2_compute = upscale_to_compute(*v2);
return Ok(StackValue::BinaryCompute(*t1, compute_subtract(*v1, v2_compute), shadow_subtract(s1, s2)));
}
(StackValue::Binary(_, v1, s1), StackValue::BinaryCompute(t2, v2, s2)) => {
let v1_compute = upscale_to_compute(*v1);
return Ok(StackValue::BinaryCompute(*t2, compute_subtract(v1_compute, *v2), shadow_subtract(s1, s2)));
}
(StackValue::BinaryCompute(t1, v1, s1), other) => {
let other_compute = self.to_compute_storage(other)?;
return Ok(StackValue::BinaryCompute(*t1, compute_subtract(*v1, other_compute), shadow_subtract(s1, &other.shadow())));
}
(other, StackValue::BinaryCompute(t2, v2, s2)) => {
let other_compute = self.to_compute_storage(other)?;
return Ok(StackValue::BinaryCompute(*t2, compute_subtract(other_compute, *v2), shadow_subtract(&other.shadow(), s2)));
}
_ => {}
}
if left.domain_type() == right.domain_type() {
match (&left, &right) {
(StackValue::Binary(t1, v1, s1), StackValue::Binary(t2, v2, s2)) => {
let binary_a = binary_from_storage(*t1, v1)?;
let binary_b = binary_from_storage(*t2, v2)?;
let result = binary_a.subtract(&binary_b)?;
let (tier, storage) = binary_to_storage(&result);
Ok(StackValue::Binary(tier, storage, shadow_subtract(s1, s2)))
}
(StackValue::Decimal(d1, v1, s1), StackValue::Decimal(d2, v2, s2)) => {
match (decimal_from_storage(*d1, v1), decimal_from_storage(*d2, v2)) {
(Ok(decimal_a), Ok(decimal_b)) => {
match decimal_a.subtract(&decimal_b) {
Ok(result) => {
let (dec, storage) = decimal_to_storage(&result);
Ok(StackValue::Decimal(dec, storage, shadow_subtract(s1, s2)))
}
Err(_) => self.subtract_via_rational(left, right),
}
}
_ => self.subtract_via_rational(left, right),
}
}
(StackValue::Ternary(t1, v1, s1), StackValue::Ternary(t2, v2, s2)) => {
let ternary_a = ternary_from_storage(*t1, v1)?;
let ternary_b = ternary_from_storage(*t2, v2)?;
let result = ternary_a.subtract(&ternary_b)?;
let (tier, storage) = ternary_to_storage(&result);
Ok(StackValue::Ternary(tier, storage, shadow_subtract(s1, s2)))
}
_ => {
self.subtract_via_rational(left, right)
}
}
} else {
if let Some((cl, cr)) = self.try_route_coerce(OpId::Sub, &left, &right) {
self.subtract_values(cl, cr)
} else {
self.subtract_via_rational(left, right)
}
}
}
pub(crate) fn multiply_values(&mut self, left: StackValue, right: StackValue) -> Result<StackValue, OverflowDetected> {
use crate::fixed_point::domains::decimal_fixed::transcendental::{
decimal_compute_mul, decimal_upscale_to_compute,
};
match (&left, &right) {
(StackValue::DecimalCompute(t1, v1, s1), StackValue::DecimalCompute(_t2, v2, s2)) => {
return Ok(StackValue::DecimalCompute(*t1, decimal_compute_mul(*v1, *v2), shadow_multiply(s1, s2)));
}
(StackValue::DecimalCompute(t1, v1, s1), StackValue::Decimal(dp, scaled, s2)) => {
let v2 = decimal_upscale_to_compute(*scaled, *dp)?;
return Ok(StackValue::DecimalCompute(*t1, decimal_compute_mul(*v1, v2), shadow_multiply(s1, s2)));
}
(StackValue::Decimal(dp, scaled, s1), StackValue::DecimalCompute(t2, v2, s2)) => {
let v1 = decimal_upscale_to_compute(*scaled, *dp)?;
return Ok(StackValue::DecimalCompute(*t2, decimal_compute_mul(v1, *v2), shadow_multiply(s1, s2)));
}
_ => {}
}
match (&left, &right) {
(StackValue::BinaryCompute(t1, v1, s1), StackValue::BinaryCompute(_t2, v2, s2)) => {
return Ok(StackValue::BinaryCompute(*t1, compute_multiply(*v1, *v2), shadow_multiply(s1, s2)));
}
(StackValue::BinaryCompute(t1, v1, s1), StackValue::Binary(_, v2, s2)) => {
let v2_compute = upscale_to_compute(*v2);
return Ok(StackValue::BinaryCompute(*t1, compute_multiply(*v1, v2_compute), shadow_multiply(s1, s2)));
}
(StackValue::Binary(_, v1, s1), StackValue::BinaryCompute(t2, v2, s2)) => {
let v1_compute = upscale_to_compute(*v1);
return Ok(StackValue::BinaryCompute(*t2, compute_multiply(v1_compute, *v2), shadow_multiply(s1, s2)));
}
(StackValue::BinaryCompute(t1, v1, s1), other) => {
let other_compute = self.to_compute_storage(other)?;
return Ok(StackValue::BinaryCompute(*t1, compute_multiply(*v1, other_compute), shadow_multiply(s1, &other.shadow())));
}
(other, StackValue::BinaryCompute(t2, v2, s2)) => {
let other_compute = self.to_compute_storage(other)?;
return Ok(StackValue::BinaryCompute(*t2, compute_multiply(other_compute, *v2), shadow_multiply(&other.shadow(), s2)));
}
_ => {}
}
if left.domain_type() == right.domain_type() {
match (&left, &right) {
(StackValue::Binary(t1, v1, s1), StackValue::Binary(t2, v2, s2)) => {
let binary_a = binary_from_storage(*t1, v1)?;
let binary_b = binary_from_storage(*t2, v2)?;
let result = binary_a.multiply(&binary_b)?;
let (tier, storage) = binary_to_storage(&result);
Ok(StackValue::Binary(tier, storage, shadow_multiply(s1, s2)))
}
(StackValue::Decimal(d1, v1, s1), StackValue::Decimal(d2, v2, s2)) => {
let dp_result = *d1 as u16 + *d2 as u16;
if dp_result > DECIMAL_DP_PROMOTION_THRESHOLD {
return self.multiply_via_rational(left, right);
}
match (decimal_from_storage(*d1, v1), decimal_from_storage(*d2, v2)) {
(Ok(decimal_a), Ok(decimal_b)) => {
match decimal_a.multiply(&decimal_b) {
Ok(result) => {
let (dp, storage) = decimal_to_storage(&result);
Ok(StackValue::Decimal(dp, storage, shadow_multiply(s1, s2)))
}
Err(_) => self.multiply_via_rational(left, right),
}
}
_ => self.multiply_via_rational(left, right),
}
}
(StackValue::Ternary(t1, v1, s1), StackValue::Ternary(t2, v2, s2)) => {
let ternary_a = ternary_from_storage(*t1, v1)?;
let ternary_b = ternary_from_storage(*t2, v2)?;
let result = ternary_a.multiply(&ternary_b)?;
let (tier, storage) = ternary_to_storage(&result);
Ok(StackValue::Ternary(tier, storage, shadow_multiply(s1, s2)))
}
_ => {
self.multiply_via_rational(left, right)
}
}
} else {
if let Some((cl, cr)) = self.try_route_coerce(OpId::Mul, &left, &right) {
self.multiply_values(cl, cr)
} else {
self.multiply_via_rational(left, right)
}
}
}
pub(crate) fn divide_values(&mut self, left: StackValue, right: StackValue) -> Result<StackValue, OverflowDetected> {
use crate::fixed_point::domains::decimal_fixed::transcendental::{
decimal_compute_div, decimal_upscale_to_compute,
};
match (&left, &right) {
(StackValue::DecimalCompute(t1, v1, s1), StackValue::DecimalCompute(_t2, v2, s2)) => {
return Ok(StackValue::DecimalCompute(*t1, decimal_compute_div(*v1, *v2)?, shadow_divide(s1, s2)));
}
(StackValue::DecimalCompute(t1, v1, s1), StackValue::Decimal(dp, scaled, s2)) => {
let v2 = decimal_upscale_to_compute(*scaled, *dp)?;
return Ok(StackValue::DecimalCompute(*t1, decimal_compute_div(*v1, v2)?, shadow_divide(s1, s2)));
}
(StackValue::Decimal(dp, scaled, s1), StackValue::DecimalCompute(t2, v2, s2)) => {
let v1 = decimal_upscale_to_compute(*scaled, *dp)?;
return Ok(StackValue::DecimalCompute(*t2, decimal_compute_div(v1, *v2)?, shadow_divide(s1, s2)));
}
_ => {}
}
match (&left, &right) {
(StackValue::BinaryCompute(t1, v1, s1), StackValue::BinaryCompute(_t2, v2, s2)) => {
return Ok(StackValue::BinaryCompute(*t1, compute_divide(*v1, *v2)?, shadow_divide(s1, s2)));
}
(StackValue::BinaryCompute(t1, v1, s1), StackValue::Binary(_, v2, s2)) => {
let v2_compute = upscale_to_compute(*v2);
return Ok(StackValue::BinaryCompute(*t1, compute_divide(*v1, v2_compute)?, shadow_divide(s1, s2)));
}
(StackValue::Binary(_, v1, s1), StackValue::BinaryCompute(t2, v2, s2)) => {
let v1_compute = upscale_to_compute(*v1);
return Ok(StackValue::BinaryCompute(*t2, compute_divide(v1_compute, *v2)?, shadow_divide(s1, s2)));
}
(StackValue::BinaryCompute(t1, v1, s1), other) => {
let other_compute = self.to_compute_storage(other)?;
return Ok(StackValue::BinaryCompute(*t1, compute_divide(*v1, other_compute)?, shadow_divide(s1, &other.shadow())));
}
(other, StackValue::BinaryCompute(t2, v2, s2)) => {
let other_compute = self.to_compute_storage(other)?;
return Ok(StackValue::BinaryCompute(*t2, compute_divide(other_compute, *v2)?, shadow_divide(&other.shadow(), s2)));
}
_ => {}
}
match (&left, &right) {
(StackValue::Symbolic(l), StackValue::Symbolic(r)) => {
Ok(StackValue::Symbolic(l.try_divide(r)?))
}
(StackValue::Binary(t1, v1, s1), StackValue::Binary(t2, v2, s2)) => {
let binary_a = binary_from_storage(*t1, v1)?;
let binary_b = binary_from_storage(*t2, v2)?;
let result = binary_a.divide(&binary_b)?;
let (tier, storage) = binary_to_storage(&result);
Ok(StackValue::Binary(tier, storage, shadow_divide(s1, s2)))
}
(StackValue::Decimal(d1, v1, s1), StackValue::Decimal(d2, v2, s2)) => {
match (decimal_from_storage(*d1, v1), decimal_from_storage(*d2, v2)) {
(Ok(decimal_a), Ok(decimal_b)) => {
match decimal_a.divide(&decimal_b) {
Ok(result) => {
let (dp, storage) = decimal_to_storage(&result);
Ok(StackValue::Decimal(dp, storage, shadow_divide(s1, s2)))
}
Err(_) => self.divide_via_rational(left, right),
}
}
_ => self.divide_via_rational(left, right),
}
}
(StackValue::Ternary(t1, v1, s1), StackValue::Ternary(t2, v2, s2)) => {
let ternary_a = ternary_from_storage(*t1, v1)?;
let ternary_b = ternary_from_storage(*t2, v2)?;
let result = ternary_a.divide(&ternary_b)?;
let (tier, storage) = ternary_to_storage(&result);
Ok(StackValue::Ternary(tier, storage, shadow_divide(s1, s2)))
}
_ => {
if let Some((cl, cr)) = self.try_route_coerce(OpId::Div, &left, &right) {
self.divide_values(cl, cr)
} else {
self.divide_via_rational(left, right)
}
}
}
}
pub(crate) fn add_via_rational(&mut self, left: StackValue, right: StackValue) -> Result<StackValue, OverflowDetected> {
let l_rational = left.to_rational()?;
let r_rational = right.to_rational()?;
let result = l_rational.try_add(&r_rational)?;
Ok(StackValue::Symbolic(result))
}
pub(crate) fn subtract_via_rational(&mut self, left: StackValue, right: StackValue) -> Result<StackValue, OverflowDetected> {
let l_rational = left.to_rational()?;
let r_rational = right.to_rational()?;
let result = l_rational.try_subtract(&r_rational)?;
Ok(StackValue::Symbolic(result))
}
pub(crate) fn multiply_via_rational(&mut self, left: StackValue, right: StackValue) -> Result<StackValue, OverflowDetected> {
let l_rational = left.to_rational()?;
let r_rational = right.to_rational()?;
match l_rational.try_multiply(&r_rational) {
Ok(result) => Ok(StackValue::Symbolic(result)),
Err(OverflowDetected::TierOverflow) | Err(OverflowDetected::PrecisionLimit) => {
let tier = self.profile_max_binary_tier();
let l_compute = self.to_compute_storage(&left)?;
let r_compute = self.to_compute_storage(&right)?;
Ok(StackValue::BinaryCompute(tier, compute_multiply(l_compute, r_compute), CompactShadow::None))
}
Err(e) => Err(e),
}
}
pub(crate) fn divide_via_rational(&mut self, left: StackValue, right: StackValue) -> Result<StackValue, OverflowDetected> {
let l_rational = left.to_rational()?;
let r_rational = right.to_rational()?;
match l_rational.try_divide(&r_rational) {
Ok(result) => Ok(StackValue::Symbolic(result)),
Err(OverflowDetected::TierOverflow) | Err(OverflowDetected::PrecisionLimit) => {
let tier = self.profile_max_binary_tier();
let l_compute = self.to_compute_storage(&left)?;
let r_compute = self.to_compute_storage(&right)?;
Ok(StackValue::BinaryCompute(tier, compute_divide(l_compute, r_compute)?, CompactShadow::None))
}
Err(e) => Err(e),
}
}
fn try_route_coerce(
&self,
op: OpId,
left: &StackValue,
right: &StackValue,
) -> Option<(StackValue, StackValue)> {
let lc = classify(left);
let rc = classify(right);
match route_binary_op(op, lc, rc) {
DomainChoice::Decimal => {
let dl = coerce_to_decimal(left)?;
let dr = coerce_to_decimal(right)?;
Some((dl, dr))
}
DomainChoice::Binary => {
let bl = self.coerce_to_binary_sv(left).ok()?;
let br = self.coerce_to_binary_sv(right).ok()?;
Some((bl, br))
}
DomainChoice::Symbolic => None,
}
}
fn coerce_to_binary_sv(&self, value: &StackValue) -> Result<StackValue, OverflowDetected> {
match value {
StackValue::Binary(..) | StackValue::BinaryCompute(..) => Ok(value.clone()),
StackValue::Decimal(dp, scaled, shadow) => {
let binary_storage = super::decimal_to_binary_storage(*dp, *scaled)?;
let tier = self.profile_max_binary_tier();
Ok(StackValue::Binary(tier, binary_storage, *shadow))
}
_ => {
let storage = self.to_binary_storage(value)?;
let tier = self.profile_max_binary_tier();
Ok(StackValue::Binary(tier, storage, value.shadow()))
}
}
}
pub(crate) fn profile_max_binary_tier(&self) -> u8 {
match self.deployment_profile {
DeploymentProfile::Realtime => 1, DeploymentProfile::Compact => 2, DeploymentProfile::Embedded => 3, DeploymentProfile::Balanced => 4, DeploymentProfile::Scientific => 5, DeploymentProfile::Custom => 3, }
}
pub(crate) fn profile_max_ternary_tier(&self) -> u8 {
match self.deployment_profile {
DeploymentProfile::Realtime => 1, DeploymentProfile::Compact => 2, DeploymentProfile::Embedded => 3, DeploymentProfile::Balanced => 4, DeploymentProfile::Scientific => 5, DeploymentProfile::Custom => 3, }
}
}