use ahash::HashMap;
use crate::{
representations::{
Add, AsAtomView, Atom, AtomSet, AtomView, Fun, Identifier, Mul, OwnedAdd, OwnedMul, Pow,
Var,
},
state::{State, Workspace},
};
impl<'a, P: AtomSet> AtomView<'a, P> {
pub fn collect(
&self,
x: Identifier,
workspace: &Workspace<P>,
state: &State,
key_map: Option<Box<dyn Fn(AtomView<P>, &mut Atom<P>)>>,
coeff_map: Option<Box<dyn Fn(AtomView<P>, &mut Atom<P>)>>,
out: &mut Atom<P>,
) {
let (h, rest) = self.coefficient_list(x, workspace, state);
let mut add_h = workspace.new_atom();
let add = add_h.to_add();
fn map_key_coeff<P: AtomSet>(
key: AtomView<P>,
coeff: Atom<P>,
workspace: &Workspace<P>,
key_map: &Option<Box<dyn Fn(AtomView<P>, &mut Atom<P>)>>,
coeff_map: &Option<Box<dyn Fn(AtomView<P>, &mut Atom<P>)>>,
add: &mut P::OA,
) {
let mut mul_h = workspace.new_atom();
let mul = mul_h.to_mul();
if let Some(key_map) = &key_map {
let mut handle = workspace.new_atom();
key_map(key, &mut handle);
mul.extend(handle.as_view());
} else {
mul.extend(key);
}
if let Some(coeff_map) = &coeff_map {
let mut handle = workspace.new_atom();
coeff_map(coeff.as_view(), &mut handle);
mul.extend(handle.as_view());
} else {
mul.extend(coeff.as_view());
}
mul.set_dirty(true);
add.extend(mul_h.as_view());
}
for (key, coeff) in h {
map_key_coeff(key, coeff, workspace, &key_map, &coeff_map, add);
}
if key_map.is_some() {
let key = workspace.new_num(1);
map_key_coeff(key.as_view(), rest, workspace, &key_map, &coeff_map, add);
} else if let Some(coeff_map) = coeff_map {
let mut handle = workspace.new_atom();
coeff_map(rest.as_view(), &mut handle);
add.extend(handle.as_view());
} else {
add.extend(rest.as_view());
}
add.set_dirty(true);
add_h.as_view().normalize(workspace, state, out);
}
pub fn coefficient_list(
&self,
x: Identifier,
workspace: &Workspace<P>,
state: &State,
) -> (Vec<(AtomView<'a, P>, Atom<P>)>, Atom<P>) {
let mut h = HashMap::default();
let mut rest = workspace.new_num(0);
match self {
AtomView::Add(a) => {
for arg in a.iter() {
arg.collect_factor(x, workspace, state, &mut h, &mut rest)
}
}
_ => self.collect_factor(x, workspace, state, &mut h, &mut rest),
}
(
h.into_iter().collect(),
Atom::new_from_view(&rest.as_view()),
)
}
fn has_key(&self, x: Identifier) -> bool {
match self {
AtomView::Var(v) => v.get_name() == x,
AtomView::Fun(f) => f.get_name() == x,
AtomView::Pow(p) => {
let (base, _) = p.get_base_exp();
match base {
AtomView::Var(v) => v.get_name() == x,
AtomView::Fun(f) => f.get_name() == x,
_ => false,
}
}
AtomView::Mul(_) => unreachable!("Mul is not a factor"),
_ => false,
}
}
fn collect_factor(
&self,
x: Identifier,
workspace: &Workspace<P>,
state: &State,
h: &mut HashMap<AtomView<'a, P>, Atom<P>>,
rest: &mut Atom<P>,
) {
match self {
AtomView::Add(_) => {}
AtomView::Mul(m) => {
if m.iter().any(|a| a.has_key(x)) {
let mut collected = workspace.new_atom();
let mul = collected.to_mul();
let mut bracket = None;
for a in m.iter() {
if bracket.is_none() && a.has_key(x) {
bracket = Some(a);
} else {
mul.extend(a);
}
}
mul.set_dirty(true);
let mut col_n = workspace.new_atom();
collected.as_view().normalize(workspace, state, &mut col_n);
h.entry(bracket.unwrap())
.and_modify(|e| {
let mut res = workspace.new_atom();
e.add(state, workspace, col_n.as_view(), &mut res);
std::mem::swap(e, &mut res);
})
.or_insert(Atom::new_from_view(&col_n.as_view()));
return;
}
}
_ => {
if self.has_key(x) {
let col_n = workspace.new_num(1);
h.entry(*self)
.and_modify(|e| {
let mut res = workspace.new_atom();
e.add(state, workspace, col_n.as_view(), &mut res);
std::mem::swap(e, &mut res);
})
.or_insert(Atom::new_from_view(&col_n.as_view()));
return;
}
}
}
let mut new_atom = workspace.new_atom();
rest.add(state, workspace, *self, &mut new_atom);
std::mem::swap(rest, new_atom.get_mut());
}
}