use crate::Write;
use std::any::TypeId;
use std::iter::zip;
use super::*;
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct VecContainer {
pub do_rebuild: bool,
pub data: Vec<Value>,
}
impl ContainerValue for VecContainer {
fn rebuild_contents(&mut self, rebuilder: &dyn ValueRebuilder) -> bool {
if self.do_rebuild {
rebuilder.rebuild_slice(&mut self.data)
} else {
false
}
}
fn iter(&self) -> impl Iterator<Item = Value> + '_ {
self.data.iter().copied()
}
}
#[derive(Clone, Debug)]
pub struct VecSort {
name: String,
element: ArcSort,
}
impl VecSort {
pub fn element(&self) -> ArcSort {
self.element.clone()
}
}
fn vec_term_children(termdag: &TermDag, term: TermId) -> Option<Vec<TermId>> {
match termdag.get(term) {
Term::App(head, children) if head == "vec-of" => Some(children.clone()),
Term::App(head, _) if head == "vec-empty" => Some(vec![]),
_ => None,
}
}
fn vec_term(termdag: &mut TermDag, children: Vec<TermId>) -> TermId {
if children.is_empty() {
termdag.app("vec-empty".into(), vec![])
} else {
termdag.app("vec-of".into(), children)
}
}
impl Presort for VecSort {
fn presort_name() -> &'static str {
"Vec"
}
fn reserved_primitives() -> Vec<&'static str> {
vec![
"vec-of",
"vec-append",
"vec-empty",
"vec-push",
"vec-pop",
"vec-not-contains",
"vec-contains",
"vec-length",
"vec-get",
"vec-set",
"vec-remove",
"vec-union",
"vec-range",
"unstable-vec-map",
]
}
fn make_sort(
typeinfo: &mut TypeInfo,
name: String,
args: &[Expr],
span: Span,
) -> Result<ArcSort, TypeError> {
if let [Expr::Var(arg_span, e)] = args {
let e = typeinfo
.get_sort_by_name(e)
.ok_or(TypeError::UndefinedSort(e.clone(), arg_span.clone()))?;
let out = Self {
name,
element: e.clone(),
};
Ok(out.to_arcsort())
} else {
Err(TypeError::BadPresortArguments(
Self::presort_name().to_owned(),
span,
))
}
}
}
impl ContainerSort for VecSort {
type Container = VecContainer;
fn name(&self) -> &str {
&self.name
}
fn inner_sorts(&self) -> Vec<ArcSort> {
vec![self.element.clone()]
}
fn is_eq_container_sort(&self) -> bool {
self.element.is_eq_sort() || self.element.is_eq_container_sort()
}
fn inner_values(
&self,
container_values: &ContainerValues,
value: Value,
) -> Vec<(ArcSort, Value)> {
let val = container_values
.get_val::<VecContainer>(value)
.unwrap()
.clone();
val.data
.iter()
.map(|e| (self.element.clone(), *e))
.collect()
}
fn register_primitives(&self, eg: &mut EGraph) {
let arc: Arc<dyn Sort> = self.clone().to_arcsort();
let vec_of_validator = |termdag: &mut TermDag, args: &[TermId]| -> Option<TermId> {
Some(vec_term(termdag, args.to_vec()))
};
let vec_empty_validator = |termdag: &mut TermDag, _args: &[TermId]| -> Option<TermId> {
Some(termdag.app("vec-empty".into(), vec![]))
};
let vec_length_validator = |termdag: &mut TermDag, args: &[TermId]| -> Option<TermId> {
let [vec] = args else { return None };
let len = vec_term_children(termdag, *vec)?.len() as i64;
Some(termdag.lit(Literal::Int(len)))
};
let vec_get_validator = |termdag: &mut TermDag, args: &[TermId]| -> Option<TermId> {
let [vec, index] = args else { return None };
let Term::Lit(Literal::Int(index)) = termdag.get(*index) else {
return None;
};
let index = usize::try_from(*index).ok()?;
vec_term_children(termdag, *vec)?.get(index).copied()
};
let vec_contains_validator = |termdag: &mut TermDag, args: &[TermId]| -> Option<TermId> {
let [vec, value] = args else { return None };
vec_term_children(termdag, *vec)?
.contains(value)
.then(|| termdag.lit(Literal::Unit))
};
let vec_not_contains_validator =
|termdag: &mut TermDag, args: &[TermId]| -> Option<TermId> {
let [vec, value] = args else { return None };
let contains = vec_term_children(termdag, *vec)?.contains(value);
(!contains).then(|| termdag.lit(Literal::Unit))
};
add_primitive_with_validator!(eg, "vec-empty" = {self.clone(): VecSort} | | -> @VecContainer (arc) { VecContainer {
do_rebuild: self.ctx.is_eq_container_sort(),
data: Vec::new()
} }, vec_empty_validator);
add_primitive_with_validator!(eg, "vec-of" = {self.clone(): VecSort} [xs: # (self.element()) ] -> @VecContainer (arc) { VecContainer {
do_rebuild: self.ctx.is_eq_container_sort(),
data: xs .collect()
} }, vec_of_validator);
add_primitive!(eg, "vec-append" = {self.clone(): VecSort} [xs: @VecContainer (arc)] -> @VecContainer (arc) { VecContainer {
do_rebuild: self.ctx.is_eq_container_sort(),
data: xs.flat_map(|x| x.data).collect()
} });
add_primitive!(eg, "vec-push" = |mut xs: @VecContainer (arc), x: # (self.element())| -> @VecContainer (arc) {{ xs.data.push(x); xs }});
add_primitive!(eg, "vec-pop" = |mut xs: @VecContainer (arc) | -> @VecContainer (arc) {{ xs.data.pop(); xs }});
add_primitive_with_validator!(eg, "vec-length" = |xs: @VecContainer (arc)| -> i64 { xs.data.len() as i64 }, vec_length_validator);
add_primitive_with_validator!(eg, "vec-contains" = |xs: @VecContainer (arc), x: # (self.element())| -?> () { ( xs.data.contains(&x)).then_some(()) }, vec_contains_validator);
add_primitive_with_validator!(eg, "vec-not-contains" = |xs: @VecContainer (arc), x: # (self.element())| -?> () { (!xs.data.contains(&x)).then_some(()) }, vec_not_contains_validator);
add_primitive_with_validator!(eg, "vec-get" = | xs: @VecContainer (arc), i: i64 | -?> # (self.element()) { xs.data.get(i as usize).copied() }, vec_get_validator);
add_primitive!(eg, "vec-set" = |mut xs: @VecContainer (arc), i: i64, x: # (self.element())| -?> @VecContainer (arc) {{ let idx = usize::try_from(i).ok()?; if idx >= xs.data.len() { None } else { xs.data[idx] = x; Some(xs) } }});
add_primitive!(eg, "vec-remove" = |mut xs: @VecContainer (arc), i: i64 | -?> @VecContainer (arc) {{ let idx = usize::try_from(i).ok()?; if idx >= xs.data.len() { None } else { xs.data.remove(idx); Some(xs) } }});
if self.element.is_eq_sort() {
eg.add_write_primitive(
Union {
name: "vec-union".into(),
vec: arc.clone(),
},
None,
);
}
if self.element.name() == "i64" {
add_primitive!(eg, "vec-range" = {self.clone(): VecSort} |end: i64| -> @VecContainer (arc) { VecContainer {
do_rebuild: self.ctx.is_eq_container_sort(),
data: {
let end: usize = end.try_into().unwrap_or(0);
(0..end)
.map(|i| state.base_values().get::<i64>(i as i64))
.collect()
}
} });
}
let all_vec_sorts = eg
.type_info
.get_arcsorts_by(|f| f.value_type() == Some(TypeId::of::<VecContainer>()));
for fn_sort in eg.type_info.get_sorts::<FunctionSort>() {
for vec_sort in &all_vec_sorts {
try_registering_vec_map(eg, fn_sort.clone(), vec_sort.clone(), arc.clone());
if vec_sort.name() != arc.name() {
try_registering_vec_map(eg, fn_sort.clone(), arc.clone(), vec_sort.clone());
}
}
}
}
fn reconstruct_termdag(
&self,
_container_values: &ContainerValues,
_value: Value,
termdag: &mut TermDag,
element_terms: Vec<TermId>,
) -> TermId {
vec_term(termdag, element_terms)
}
fn rebuild_container_normalizer(&self) -> Option<(String, PrimitiveValidator)> {
Some((
"vec-of".to_owned(),
Arc::new(|termdag: &mut TermDag, args: &[TermId]| {
Some(vec_term(termdag, args.to_vec()))
}),
))
}
fn serialized_name(&self, _container_values: &ContainerValues, _: Value) -> String {
"vec-of".to_owned()
}
}
pub(crate) fn try_registering_vec_map(
eg: &mut EGraph,
fn_: Arc<FunctionSort>,
input_vec: ArcSort,
output_vec: ArcSort,
) {
if fn_.inputs().len() != 1
|| fn_.inputs()[0].name() != input_vec.inner_sorts()[0].name()
|| fn_.output().name() != output_vec.inner_sorts()[0].name()
{
return;
}
eg.add_pure_primitive(
VecMap {
name: "unstable-vec-map".into(),
vec: input_vec,
output_vec,
fn_: fn_.clone(),
},
None,
);
}
pub(crate) fn register_vec_primitives_for_function(eg: &mut EGraph, fn_: Arc<FunctionSort>) {
let all_vec_sorts = eg
.type_info
.get_arcsorts_by(|f| f.value_type() == Some(TypeId::of::<VecContainer>()));
for input_vec in &all_vec_sorts {
for output_vec in &all_vec_sorts {
try_registering_vec_map(eg, fn_.clone(), input_vec.clone(), output_vec.clone());
}
}
}
#[derive(Clone)]
struct VecMap {
name: String,
vec: ArcSort,
output_vec: ArcSort,
fn_: Arc<FunctionSort>,
}
impl Primitive for VecMap {
fn name(&self) -> &str {
&self.name
}
fn get_type_constraints(&self, span: &Span) -> Box<dyn TypeConstraint> {
SimpleTypeConstraint::new(
&self.name,
vec![self.fn_.clone(), self.vec.clone(), self.output_vec.clone()],
span.clone(),
)
.into_box()
}
}
impl PurePrim for VecMap {
fn apply<'a, 'db>(
&self,
mut state: crate::PureState<'a, 'db>,
args: &[Value],
) -> Option<Value> {
let fc = state
.container_values()
.get_val::<FunctionContainer>(args[0])
.unwrap()
.clone();
let vec = state
.container_values()
.get_val::<VecContainer>(args[1])
.unwrap()
.clone();
let mut new_data = Vec::with_capacity(vec.data.len());
for v in vec.data {
if let Some(mapped) = state.apply_function(&fc, &[v]) {
new_data.push(mapped);
}
}
let new_vec = VecContainer {
do_rebuild: self.output_vec.is_eq_container_sort(),
data: new_data,
};
Some(state.register_container(new_vec))
}
}
#[derive(Clone)]
struct Union {
name: String,
vec: ArcSort,
}
impl Primitive for Union {
fn name(&self) -> &str {
&self.name
}
fn get_type_constraints(&self, span: &Span) -> Box<dyn TypeConstraint> {
SimpleTypeConstraint::new(
self.name(),
vec![self.vec.clone(), self.vec.clone(), self.vec.clone()],
span.clone(),
)
.into_box()
}
}
impl WritePrim for Union {
fn apply<'a, 'db>(
&self,
mut state: crate::WriteState<'a, 'db>,
args: &[Value],
) -> Option<Value> {
let left = state
.container_values()
.get_val::<VecContainer>(args[0])?
.clone()
.data;
let right = state
.container_values()
.get_val::<VecContainer>(args[1])?
.clone()
.data;
if left.len() != right.len() {
return None;
}
for (l, r) in zip(left, right) {
state.union(l, r).ok()?;
}
Some(args[0])
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_vec_make_expr() {
let mut egraph = EGraph::default();
let outputs = egraph
.parse_and_run_program(
None,
r#"
(sort IVec (Vec i64))
(let v0 (vec-empty))
(let v1 (vec-of 1 2 3 4))
(extract v0)
(extract v1)
"#,
)
.unwrap();
egraph
.parse_and_run_program(
None,
&format!(
r#"
(check (= v0 {}))
(check (= v1 {}))
"#,
outputs[0], outputs[1],
),
)
.unwrap();
}
}