use core::fmt;
use std::sync::Arc;
use serde_json::{json, Value};
use super::{Expression, IExpression, Integer, EXPRESSION_INSTANCES};
#[derive(PartialEq, Eq, Hash)]
pub struct Product {
_factors: Vec<Expression>,
}
impl Product {
pub fn factors(&self) -> &Vec<Expression> {
&self._factors
}
pub fn of(factors: &[Expression]) -> Result<Expression, ()> {
let id = {
let mut tmp = String::from("product");
for s in factors {
tmp += &s.as_stringable().id();
}
tmp
};
let mut instances = EXPRESSION_INSTANCES.lock().unwrap();
let result = instances.get(&id);
if let Some(result) = result {
return Ok(result.clone());
}
let result = Product {
_factors: factors.to_vec(),
};
match result.rep_ok() {
true => {
let pointer = Expression::Product(Arc::new(result));
instances.insert(id, pointer.clone());
Ok(pointer)
}
false => Err(()),
}
}
fn rep_ok(&self) -> bool {
self._factors.len() >= 2
}
}
pub fn product_of(factors: &[Expression]) -> Expression {
if factors.is_empty() {
return Integer::of(1);
}
if factors.len() == 1 {
return factors[0].clone();
}
Product::of(factors).unwrap()
}
pub fn product_of_iter(factors: &mut dyn Iterator<Item = Expression>) -> Expression {
let factors = factors.collect::<Vec<Expression>>();
if factors.is_empty() {
return Integer::of(1);
}
if factors.len() == 1 {
return factors[0].clone();
}
Product::of(&factors).unwrap()
}
impl IExpression for Product {
fn to_unambigious_string(&self) -> String {
let mut result = String::from("");
result += "(";
result += &self._factors[0].as_stringable().to_unambigious_string();
result += " * ";
result += &self
._factors
.iter()
.skip(1)
.map(|f| f.as_stringable().to_unambigious_string())
.reduce(|a, b| a + " * " + &b)
.unwrap();
result + ")"
}
fn id(&self) -> String {
let mut suffix = String::new();
for exp in &self._factors {
suffix += &exp.as_stringable().id()
}
format!("product{}", suffix)
}
fn to_json(&self) -> Value {
json!(&mut [json!("Product")]
.into_iter()
.chain(self._factors.iter().map(|factor| factor.to_json()))
.collect::<Vec<Value>>())
}
}
impl fmt::Debug for Product {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let factors = self
._factors
.iter()
.map(|f| format!("{:?}", f))
.reduce(|a, b| a + " " + &b)
.unwrap();
write!(f, "*({:?})", factors)
}
}
#[cfg(test)]
mod tests {
use crate::expressions::Integer;
use super::*;
#[test]
fn factor_cardinality_invariant() {
let legal = Product::of(&[Integer::of(0), Integer::of(1)]);
assert!(legal.is_ok());
let illegal = Product::of(&[Integer::of(0)]);
assert!(illegal.is_err());
}
#[test]
fn id_generation_preserves_children() {
let product = Product::of(&[Integer::of(1), Integer::of(2)]).unwrap();
let product2 = Product::of(&[Integer::of(1), Integer::of(2)]).unwrap();
assert_eq!(
product.as_stringable().id(),
product2.as_stringable().id(),
"Id not preserved"
);
assert_eq!(product, product2, "Flywheel not working");
}
}