1use std::{
12    fmt::Debug,
13    hash::Hash,
14    iter, mem,
15    ops::{Index, IndexMut},
16    sync::{Arc, Mutex},
17};
18
19use crate::core_relations::{
20    BaseValue, BaseValueId, BaseValues, ColumnId, Constraint, ContainerValue, ContainerValues,
21    CounterId, Database, DisplacedTable, DisplacedTableWithProvenance, ExecutionState,
22    ExternalFunction, ExternalFunctionId, MergeVal, Offset, PlanStrategy, RuleSetReport,
23    SortedWritesTable, TableId, TaggedRowBuffer, Value, WrappedTable,
24};
25use crate::numeric_id::{DenseIdMap, DenseIdMapWithReuse, IdVec, NumericId, define_id};
26use egglog_core_relations as core_relations;
27use egglog_numeric_id as numeric_id;
28use hashbrown::HashMap;
29use indexmap::{IndexMap, IndexSet, map::Entry};
30use log::info;
31use once_cell::sync::Lazy;
32pub use proof_format::{EqProofId, ProofStore, TermProofId};
33use proof_spec::{ProofReason, ProofReconstructionState, ReasonSpecId};
34use smallvec::SmallVec;
35use web_time::{Duration, Instant};
36
37pub mod macros;
38pub mod proof_format;
39pub(crate) mod proof_spec;
40pub(crate) mod rule;
41pub(crate) mod syntax;
42#[cfg(test)]
43mod tests;
44
45pub use rule::{Function, QueryEntry, RuleBuilder};
46pub use syntax::{SourceExpr, SourceSyntax, TopLevelLhsExpr};
47use thiserror::Error;
48
49#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
50pub enum ColumnTy {
51    Id,
52    Base(BaseValueId),
53}
54
55define_id!(pub RuleId, u32, "An egglog-style rule");
56define_id!(pub FunctionId, u32, "An id representing an egglog function");
57define_id!(pub(crate) Timestamp, u32, "An abstract timestamp used to track execution of egglog rules");
58impl Timestamp {
59    fn to_value(self) -> Value {
60        Value::new(self.rep())
61    }
62}
63
64#[derive(Clone)]
66pub struct EGraph {
67    db: Database,
68    uf_table: TableId,
69    id_counter: CounterId,
70    reason_counter: CounterId,
71    timestamp_counter: CounterId,
72    rules: DenseIdMapWithReuse<RuleId, RuleInfo>,
73    funcs: DenseIdMap<FunctionId, FunctionInfo>,
74    panic_message: SideChannel<String>,
75    panic_funcs: HashMap<String, ExternalFunctionId>,
81    proof_specs: IdVec<ReasonSpecId, Arc<ProofReason>>,
82    cong_spec: ReasonSpecId,
83    reason_tables: IndexMap<usize , TableId>,
86    term_tables: IndexMap<usize , TableId>,
87    tracing: bool,
88}
89
90pub type Result<T> = std::result::Result<T, anyhow::Error>;
91
92impl Default for EGraph {
93    fn default() -> Self {
94        let mut db = Database::new();
95        let uf_table = db.add_table(DisplacedTable::default(), iter::empty(), iter::empty());
96        EGraph::create_internal(db, uf_table, false)
97    }
98}
99
100pub struct FunctionConfig {
102    pub schema: Vec<ColumnTy>,
104    pub default: DefaultVal,
106    pub merge: MergeFn,
108    pub name: String,
110    pub can_subsume: bool,
112}
113
114impl EGraph {
115    pub fn with_tracing() -> EGraph {
121        let mut db = Database::new();
122        let uf_table = db.add_table(
123            DisplacedTableWithProvenance::default(),
124            iter::empty(),
125            iter::empty(),
126        );
127        EGraph::create_internal(db, uf_table, true)
128    }
129
130    fn create_internal(mut db: Database, uf_table: TableId, tracing: bool) -> EGraph {
131        let id_counter = db.add_counter();
132        let trace_counter = db.add_counter();
133        let ts_counter = db.add_counter();
134        db.inc_counter(ts_counter);
136        let mut proof_specs = IdVec::default();
137        let cong_spec = proof_specs.push(Arc::new(ProofReason::CongRow));
138
139        Self {
140            db,
141            uf_table,
142            id_counter,
143            reason_counter: trace_counter,
144            timestamp_counter: ts_counter,
145            rules: Default::default(),
146            funcs: Default::default(),
147            panic_message: Default::default(),
148            panic_funcs: Default::default(),
149            proof_specs,
150            cong_spec,
151            reason_tables: Default::default(),
152            term_tables: Default::default(),
153            tracing,
154        }
155    }
156
157    fn next_ts(&self) -> Timestamp {
158        Timestamp::from_usize(self.db.read_counter(self.timestamp_counter))
159    }
160
161    fn inc_ts(&mut self) {
162        self.db.inc_counter(self.timestamp_counter);
163    }
164
165    pub fn base_values_mut(&mut self) -> &mut BaseValues {
168        self.db.base_values_mut()
169    }
170
171    pub fn container_values_mut(&mut self) -> &mut ContainerValues {
174        self.db.container_values_mut()
175    }
176
177    pub fn container_values(&self) -> &ContainerValues {
179        self.db.container_values()
180    }
181
182    pub fn get_container_value<C: ContainerValue>(&mut self, val: C) -> Value {
184        self.register_container_ty::<C>();
185        self.db
186            .with_execution_state(|state| state.clone().container_values().register_val(val, state))
187    }
188
189    pub fn register_container_ty<C: ContainerValue>(&mut self) {
194        let uf_table = self.uf_table;
195        let ts_counter = self.timestamp_counter;
196        self.db.container_values_mut().register_type::<C>(
197            self.id_counter,
198            move |state, old, new| {
199                if old != new {
200                    let next_ts = Value::from_usize(state.read_counter(ts_counter));
201                    state.stage_insert(uf_table, &[old, new, next_ts]);
202                    std::cmp::min(old, new)
203                } else {
204                    old
205                }
206            },
207        );
208    }
209
210    pub fn base_values(&self) -> &BaseValues {
212        self.db.base_values()
213    }
214
215    pub fn base_value_constant<T>(&self, x: T) -> QueryEntry
217    where
218        T: BaseValue,
219    {
220        QueryEntry::Const {
221            val: self.base_values().get(x),
222            ty: ColumnTy::Base(self.base_values().get_ty::<T>()),
223        }
224    }
225
226    pub fn register_external_func(
227        &mut self,
228        func: impl ExternalFunction + 'static,
229    ) -> ExternalFunctionId {
230        self.db.add_external_function(func)
231    }
232
233    pub fn free_external_func(&mut self, func: ExternalFunctionId) {
234        self.db.free_external_function(func)
235    }
236
237    pub fn fresh_id(&mut self) -> Value {
239        Value::from_usize(self.db.inc_counter(self.id_counter))
240    }
241
242    fn get_canon_in_uf(&self, val: Value) -> Value {
246        let table = self.db.get_table(self.uf_table);
247        let row = table.get_row(&[val]);
248        row.map(|row| row.vals[1]).unwrap_or(val)
249    }
250
251    pub fn get_canon_repr(&self, val: Value, ty: ColumnTy) -> Value {
256        match ty {
257            ColumnTy::Id => self.get_canon_in_uf(val),
258            ColumnTy::Base(_) => val,
259        }
260    }
261
262    fn term_table(&mut self, table: TableId) -> TableId {
263        let spec = self.db.get_table(table).spec();
264        match self.term_tables.entry(spec.n_keys) {
265            Entry::Occupied(o) => *o.get(),
266            Entry::Vacant(v) => {
267                let table = SortedWritesTable::new(
268                    spec.n_keys + 1,     spec.n_keys + 1 + 2, None,
271                    vec![], Box::new(|_, _, _, _| false),
273                );
274                let table_id = self.db.add_table(table, iter::empty(), iter::empty());
275                *v.insert(table_id)
276            }
277        }
278    }
279
280    fn reason_table(&mut self, spec: &ProofReason) -> TableId {
281        let arity = spec.arity();
282        match self.reason_tables.entry(arity) {
283            Entry::Occupied(o) => *o.get(),
284            Entry::Vacant(v) => {
285                let table = SortedWritesTable::new(
286                    arity,
287                    arity + 1, None,
289                    vec![], Box::new(|_, _, _, _| false),
291                );
292                let table_id = self.db.add_table(table, iter::empty(), iter::empty());
293                *v.insert(table_id)
294            }
295        }
296    }
297
298    pub fn add_values(&mut self, values: impl IntoIterator<Item = (FunctionId, Vec<Value>)>) {
307        self.add_values_with_desc("", values)
308    }
309
310    pub fn add_term(&mut self, func: FunctionId, inputs: &[Value], desc: &str) -> Value {
317        let info = &self.funcs[func];
318        let schema_math = SchemaMath {
319            tracing: self.tracing,
320            subsume: info.can_subsume,
321            func_cols: info.schema.len(),
322        };
323        let mut extended_row = Vec::new();
324        extended_row.extend_from_slice(inputs);
325        let term = self.tracing.then(|| {
326            let reason = self.get_fiat_reason(desc);
327            self.get_term(func, inputs, reason)
328        });
329        let res = term.unwrap_or_else(|| self.fresh_id());
330        schema_math.write_table_row(
331            &mut extended_row,
332            RowVals {
333                timestamp: self.next_ts().to_value(),
334                ret_val: Some(res),
335                proof: term,
336                subsume: schema_math.subsume.then_some(NOT_SUBSUMED),
337            },
338        );
339        extended_row[schema_math.ret_val_col()] = res;
340        let table_id = self.funcs[func].table;
341        self.db
342            .get_table(table_id)
343            .new_buffer()
344            .stage_insert(&extended_row);
345        self.flush_updates();
346        self.get_canon_in_uf(res)
347    }
348
349    fn get_term(&mut self, func: FunctionId, key: &[Value], reason: Value) -> Value {
354        let table_id = self.funcs[func].table;
355        let term_table_id = self.term_table(table_id);
356        let table = self.db.get_table(term_table_id);
357        let mut term_key = Vec::with_capacity(key.len() + 1);
358        term_key.push(Value::new(func.rep()));
359        term_key.extend(key);
360        if let Some(row) = table.get_row(&term_key) {
361            row.vals[row.vals.len() - 2]
362        } else {
363            let result = Value::from_usize(self.db.inc_counter(self.id_counter));
364            term_key.push(result);
365            term_key.push(reason);
366            self.db
367                .get_table(term_table_id)
368                .new_buffer()
369                .stage_insert(&term_key);
370            self.db.merge_table(term_table_id);
371            result
372        }
373    }
374
375    pub fn lookup_id(&self, func: FunctionId, key: &[Value]) -> Option<Value> {
378        let info = &self.funcs[func];
379        let schema_math = SchemaMath {
380            tracing: self.tracing,
381            subsume: info.can_subsume,
382            func_cols: info.schema.len(),
383        };
384        let table_id = info.table;
385        let table = self.db.get_table(table_id);
386        let row = table.get_row(key)?;
387        Some(row.vals[schema_math.ret_val_col()])
388    }
389
390    fn get_fiat_reason(&mut self, desc: &str) -> Value {
391        let reason = Arc::new(ProofReason::Fiat { desc: desc.into() });
392        let reason_table = self.reason_table(&reason);
393        let reason_spec_id = self.proof_specs.push(reason);
394        let reason_id = Value::from_usize(self.db.inc_counter(self.reason_counter));
395        self.db
396            .get_table(reason_table)
397            .new_buffer()
398            .stage_insert(&[Value::new(reason_spec_id.rep()), reason_id]);
399        self.db.merge_table(reason_table);
400        reason_id
401    }
402
403    pub fn add_values_with_desc(
413        &mut self,
414        desc: &str,
415        values: impl IntoIterator<Item = (FunctionId, Vec<Value>)>,
416    ) {
417        let mut extended_row = Vec::<Value>::new();
418        let reason_id = self.tracing.then(|| self.get_fiat_reason(desc));
419        let mut bufs = DenseIdMap::default();
420        for (func, row) in values.into_iter() {
421            let table_info = &self.funcs[func];
422            let schema_math = SchemaMath {
423                tracing: self.tracing,
424                subsume: table_info.can_subsume,
425                func_cols: table_info.schema.len(),
426            };
427            let table_id = table_info.table;
428            let term_id = reason_id.map(|reason| {
429                let term_id = self.get_term(func, &row[0..schema_math.num_keys()], reason);
431                let buf = bufs.get_or_insert(self.uf_table, || {
432                    self.db.get_table(self.uf_table).new_buffer()
433                });
434                buf.stage_insert(&[
436                    *row.last().unwrap(),
437                    term_id,
438                    self.next_ts().to_value(),
439                    reason,
440                ]);
441                term_id
442            });
443            extended_row.extend_from_slice(&row);
444            schema_math.write_table_row(
445                &mut extended_row,
446                RowVals {
447                    timestamp: self.next_ts().to_value(),
448                    proof: term_id,
449                    subsume: schema_math.subsume.then_some(NOT_SUBSUMED),
450                    ret_val: None, },
452            );
453            let buf = bufs.get_or_insert(table_id, || self.db.get_table(table_id).new_buffer());
454            buf.stage_insert(&extended_row);
455            extended_row.clear();
456        }
457        mem::drop(bufs);
459        self.flush_updates();
460    }
461
462    pub fn approx_table_size(&self, table: FunctionId) -> usize {
463        self.db.estimate_size(self.funcs[table].table, None)
464    }
465
466    pub fn table_size(&self, table: FunctionId) -> usize {
467        self.db.get_table(self.funcs[table].table).len()
468    }
469
470    pub fn explain_term(&mut self, id: Value, store: &mut ProofStore) -> Result<TermProofId> {
479        if !self.tracing {
480            return Err(ProofReconstructionError::TracingNotEnabled.into());
481        }
482        let mut state = ProofReconstructionState::new(store);
483        Ok(self.explain_term_inner(id, &mut state))
484    }
485
486    pub fn explain_terms_equal(
493        &mut self,
494        id1: Value,
495        id2: Value,
496        store: &mut ProofStore,
497    ) -> Result<EqProofId> {
498        if !self.tracing {
499            return Err(ProofReconstructionError::TracingNotEnabled.into());
500        }
501        let mut state = ProofReconstructionState::new(store);
502        if self.get_canon_in_uf(id1) != self.get_canon_in_uf(id2) {
503            let mut buf = Vec::<u8>::new();
506            let term_id_1 = self.reconstruct_term(id1, ColumnTy::Id, &mut state);
507            let term_id_2 = self.reconstruct_term(id2, ColumnTy::Id, &mut state);
508            store.termdag.print_term(term_id_1, &mut buf).unwrap();
509            let term1 = String::from_utf8(buf).unwrap();
510            let mut buf = Vec::<u8>::new();
511            store.termdag.print_term(term_id_2, &mut buf).unwrap();
512            let term2 = String::from_utf8(buf).unwrap();
513            return Err(
514                ProofReconstructionError::EqualityExplanationOfUnequalTerms { term1, term2 }.into(),
515            );
516        }
517        Ok(self.explain_terms_equal_inner(id1, id2, &mut state))
518    }
519
520    pub fn for_each(&self, table: FunctionId, mut f: impl FnMut(FunctionRow<'_>)) {
524        self.for_each_while(table, |row| {
525            f(row);
526            true
527        });
528    }
529
530    pub fn for_each_while(&self, table: FunctionId, mut f: impl FnMut(FunctionRow<'_>) -> bool) {
533        let info = &self.funcs[table];
534        let table = self.funcs[table].table;
535        let schema_math = SchemaMath {
536            tracing: self.tracing,
537            subsume: info.can_subsume,
538            func_cols: info.schema.len(),
539        };
540        let imp = self.db.get_table(table);
541        let all = imp.all();
542        let mut cur = Offset::new(0);
543        let mut buf = TaggedRowBuffer::new(imp.spec().arity());
544        macro_rules! drain_buf {
549            ($buf:expr) => {
550                for (_, row) in $buf.non_stale() {
551                    let subsumed =
552                        schema_math.subsume && row[schema_math.subsume_col()] == SUBSUMED;
553                    if !f(FunctionRow {
554                        vals: &row[0..schema_math.func_cols],
555                        subsumed,
556                    }) {
557                        return;
558                    }
559                }
560                $buf.clear();
561            };
562        }
563        while let Some(next) = imp.scan_bounded(all.as_ref(), cur, 32, &mut buf) {
564            drain_buf!(buf);
565            cur = next;
566        }
567        drain_buf!(buf);
568    }
569
570    pub fn dump_debug_info(&self) {
574        info!("=== View Tables ===");
575        for (id, info) in self.funcs.iter() {
576            let table = self.db.get_table(info.table);
577            self.scan_table(table, |row| {
578                info!(
579                    "View Table {name} / {id:?} / {table:?}: {row:?}",
580                    name = info.name,
581                    table = info.table
582                )
583            });
584        }
585
586        info!("=== Term Tables ===");
587        for (_, table_id) in &self.term_tables {
588            let table = self.db.get_table(*table_id);
589            self.scan_table(table, |row| {
590                let name = &self.funcs[FunctionId::new(row[0].rep())].name;
591                let row = &row[1..];
592                info!("Term Table {table_id:?}: {name}, {row:?}")
593            });
594        }
595
596        info!("=== Reason Tables ===");
597        for (_, table_id) in &self.reason_tables {
598            let table = self.db.get_table(*table_id);
599            self.scan_table(table, |row| {
600                let spec = self.proof_specs[ReasonSpecId::new(row[0].rep())].as_ref();
601                let row = &row[1..];
602                info!("Reason Table {table_id:?}: {spec:?}, {row:?}")
603            });
604        }
605    }
606
607    fn scan_table(&self, table: &WrappedTable, mut f: impl FnMut(&[Value])) {
609        const BATCH_SIZE: usize = 128;
610        let all = table.all();
611        let mut cur = Offset::new(0);
612        let mut out = TaggedRowBuffer::new(table.spec().arity());
613        while let Some(next) = table.scan_bounded(all.as_ref(), cur, BATCH_SIZE, &mut out) {
614            out.non_stale().for_each(|(_, row)| f(row));
615            out.clear();
616            cur = next;
617        }
618        out.non_stale().for_each(|(_, row)| f(row));
619    }
620
621    pub fn add_table(&mut self, config: FunctionConfig) -> FunctionId {
623        let FunctionConfig {
624            schema,
625            default,
626            merge,
627            name,
628            can_subsume,
629        } = config;
630        assert!(
631            !schema.is_empty(),
632            "must have at least one column in schema"
633        );
634        let to_rebuild: Vec<ColumnId> = schema
635            .iter()
636            .enumerate()
637            .filter(|(_, ty)| matches!(ty, ColumnTy::Id))
638            .map(|(i, _)| ColumnId::from_usize(i))
639            .collect();
640        let schema_math = SchemaMath {
641            tracing: self.tracing,
642            subsume: can_subsume,
643            func_cols: schema.len(),
644        };
645        let n_args = schema_math.num_keys();
646        let n_cols = schema_math.table_columns();
647        let next_func_id = self.funcs.next_id();
648        let mut read_deps = IndexSet::<TableId>::new();
649        let mut write_deps = IndexSet::<TableId>::new();
650        merge.fill_deps(self, &mut read_deps, &mut write_deps);
651        let merge_fn = merge.to_callback(schema_math, &name, self);
652        let table = SortedWritesTable::new(
653            n_args,
654            n_cols,
655            Some(ColumnId::from_usize(schema.len())),
656            to_rebuild,
657            merge_fn,
658        );
659        let table_id =
660            self.db
661                .add_table(table, read_deps.iter().copied(), write_deps.iter().copied());
662
663        let res = self.funcs.push(FunctionInfo {
664            table: table_id,
665            schema: schema.clone(),
666            incremental_rebuild_rules: Default::default(),
667            nonincremental_rebuild_rule: RuleId::new(!0),
668            default_val: default,
669            can_subsume,
670            name: name.into(),
671        });
672        debug_assert_eq!(res, next_func_id);
673        let incremental_rebuild_rules = self.incremental_rebuild_rules(res, &schema);
674        let nonincremental_rebuild_rule = self.nonincremental_rebuild(res, &schema);
675        let info = &mut self.funcs[res];
676        info.incremental_rebuild_rules = incremental_rebuild_rules;
677        info.nonincremental_rebuild_rule = nonincremental_rebuild_rule;
678        res
679    }
680
681    pub fn run_rules(&mut self, rules: &[RuleId]) -> Result<IterationReport> {
685        let ts = self.next_ts();
686
687        let rule_set_report = run_rules_impl(&mut self.db, &mut self.rules, rules, ts)?;
688        if let Some(message) = self.panic_message.lock().unwrap().take() {
689            return Err(PanicError(message).into());
690        }
691
692        let mut iteration_report = IterationReport {
693            changed: rule_set_report.changed,
694            rule_reports: rule_set_report.rule_reports.into_iter().collect(),
695            search_and_apply_time: rule_set_report.search_and_apply_time,
696            merge_time: rule_set_report.merge_time,
697            rebuild_time: Duration::ZERO,
698        };
699        if !iteration_report.changed {
700            return Ok(iteration_report);
701        }
702
703        let rebuild_timer = Instant::now();
704        self.rebuild()?;
705        iteration_report.rebuild_time = rebuild_timer.elapsed();
706
707        if let Some(message) = self.panic_message.lock().unwrap().take() {
708            return Err(PanicError(message).into());
709        }
710
711        Ok(iteration_report)
712    }
713
714    fn rebuild(&mut self) -> Result<()> {
715        fn do_parallel() -> bool {
716            #[cfg(test)]
717            {
718                use rand::Rng;
719                rand::rng().random_bool(0.5)
720            }
721            #[cfg(not(test))]
722            {
723                rayon::current_num_threads() > 1
724            }
725        }
726        if self.db.get_table(self.uf_table).rebuilder(&[]).is_some() {
727            let mut tables = Vec::with_capacity(self.funcs.next_id().index());
729            for (_, func) in self.funcs.iter() {
730                tables.push(func.table);
731            }
732            loop {
733                let container_rebuild = self.db.rebuild_containers(self.uf_table);
765                let table_rebuild =
766                    self.db
767                        .apply_rebuild(self.uf_table, &tables, self.next_ts().to_value());
768                self.inc_ts();
769                if !table_rebuild && !container_rebuild {
770                    break;
771                }
772            }
773            return Ok(());
774        }
775        if do_parallel() {
776            return self.rebuild_parallel();
777        }
778        let start = Instant::now();
779
780        let mut changed = true;
782        while changed {
783            changed = false;
784            self.inc_ts();
787            let ts = self.next_ts();
788            for (_, info) in self.funcs.iter_mut() {
789                let last_rebuilt_at = self.rules[info.nonincremental_rebuild_rule].last_run_at;
790                let table_size = self.db.estimate_size(info.table, None);
791                let uf_size = self.db.estimate_size(
792                    self.uf_table,
793                    Some(Constraint::GeConst {
794                        col: ColumnId::new(2),
795                        val: last_rebuilt_at.to_value(),
796                    }),
797                );
798                if incremental_rebuild(uf_size, table_size, false) {
799                    marker_incremental_rebuild(|| -> Result<()> {
800                        for rule in &info.incremental_rebuild_rules {
805                            changed |= run_rules_impl(&mut self.db, &mut self.rules, &[*rule], ts)?
806                                .changed;
807                        }
808                        self.rules[info.nonincremental_rebuild_rule].last_run_at = ts;
810                        Ok(())
811                    })?;
812                } else {
813                    marker_nonincremental_rebuild(|| -> Result<()> {
814                        changed |= run_rules_impl(
815                            &mut self.db,
816                            &mut self.rules,
817                            &[info.nonincremental_rebuild_rule],
818                            ts,
819                        )?
820                        .changed;
821                        for rule in &info.incremental_rebuild_rules {
822                            self.rules[*rule].last_run_at = ts;
823                        }
824                        Ok(())
825                    })?;
826                }
827            }
828        }
829        log::info!("rebuild took {:?}", start.elapsed());
830        Ok(())
831    }
832
833    fn rebuild_parallel(&mut self) -> Result<()> {
838        let start = Instant::now();
839        #[derive(Default)]
840        struct RebuildState {
841            nonincremental: Vec<FunctionId>,
842            incremental: DenseIdMap<usize, SmallVec<[FunctionId; 2]>>,
843        }
844
845        impl RebuildState {
846            fn clear(&mut self) {
847                self.nonincremental.clear();
848                self.incremental.iter_mut().for_each(|(_, v)| v.clear());
849            }
850        }
851
852        let mut changed = true;
853        let mut state = RebuildState::default();
854        let mut scratch = Vec::new();
855        while changed {
856            changed = false;
857            state.clear();
858            self.inc_ts();
859            for (func, info) in self.funcs.iter_mut() {
862                let last_rebuilt_at = self.rules[info.nonincremental_rebuild_rule].last_run_at;
863                let table_size = self.db.estimate_size(info.table, None);
864                let uf_size = self.db.estimate_size(
865                    self.uf_table,
866                    Some(Constraint::GeConst {
867                        col: ColumnId::new(2),
868                        val: last_rebuilt_at.to_value(),
869                    }),
870                );
871                if incremental_rebuild(uf_size, table_size, true) {
872                    for (i, _) in info.incremental_rebuild_rules.iter().enumerate() {
873                        state.incremental.get_or_default(i).push(func);
874                    }
875                } else {
876                    state.nonincremental.push(func);
877                }
878            }
879            let ts = self.next_ts();
880            for func in state.nonincremental.iter().copied() {
881                scratch.push(self.funcs[func].nonincremental_rebuild_rule);
882                for rule in &self.funcs[func].incremental_rebuild_rules {
883                    self.rules[*rule].last_run_at = ts;
884                }
885            }
886            changed |= run_rules_impl(&mut self.db, &mut self.rules, &scratch, ts)?.changed;
887            scratch.clear();
888            let ts = self.next_ts();
889            for (i, funcs) in state.incremental.iter() {
890                for func in funcs.iter().copied() {
891                    let info = &mut self.funcs[func];
892                    scratch.push(info.incremental_rebuild_rules[i]);
893                    self.rules[info.nonincremental_rebuild_rule].last_run_at = ts;
894                }
895                changed |= run_rules_impl(&mut self.db, &mut self.rules, &scratch, ts)?.changed;
896                scratch.clear();
897            }
898        }
899        log::info!("rebuild took {:?}", start.elapsed());
900        Ok(())
901    }
902
903    fn incremental_rebuild_rules(&mut self, table: FunctionId, schema: &[ColumnTy]) -> Vec<RuleId> {
904        schema
905            .iter()
906            .enumerate()
907            .filter_map(|(i, ty)| match ty {
908                ColumnTy::Id => {
909                    Some(self.incremental_rebuild_rule(table, schema, ColumnId::from_usize(i)))
910                }
911                ColumnTy::Base(_) => None,
912            })
913            .collect()
914    }
915
916    fn incremental_rebuild_rule(
917        &mut self,
918        table: FunctionId,
919        schema: &[ColumnTy],
920        col: ColumnId,
921    ) -> RuleId {
922        let subsume = self.funcs[table].can_subsume;
923        let table_id = self.funcs[table].table;
924        let uf_table = self.uf_table;
925        let mut rb = self.new_rule(&format!("incremental rebuild {table:?}, {col:?}"), true);
927        rb.set_plan_strategy(PlanStrategy::MinCover);
928        let mut vars = Vec::<QueryEntry>::with_capacity(schema.len());
929        for ty in schema {
930            vars.push(rb.new_var(*ty).into());
931        }
932        let canon_val = rb.new_var(ColumnTy::Id);
933        let subsume_var = subsume.then(|| rb.new_var(ColumnTy::Id));
934        rb.add_atom_with_timestamp_and_func(
935            table_id,
936            Some(table),
937            subsume_var.map(QueryEntry::from),
938            &vars,
939        );
940        rb.add_atom_with_timestamp_and_func(
941            uf_table,
942            None,
943            None,
944            &[vars[col.index()].clone(), canon_val.into()],
945        );
946        rb.set_focus(1); let mut canon = Vec::<QueryEntry>::with_capacity(schema.len());
950        for (i, (var, ty)) in vars.iter().zip(schema.iter()).enumerate() {
951            canon.push(if i == col.index() {
952                canon_val.into()
953            } else if let ColumnTy::Id = ty {
954                rb.lookup_uf(var.clone()).unwrap().into()
955            } else {
956                var.clone()
957            })
958        }
959
960        rb.rebuild_row(table, &vars, &canon, subsume_var);
962        rb.build_internal(None)
963    }
964
965    fn nonincremental_rebuild(&mut self, table: FunctionId, schema: &[ColumnTy]) -> RuleId {
966        let can_subsume = self.funcs[table].can_subsume;
967        let table_id = self.funcs[table].table;
968        let mut rb = self.new_rule(&format!("nonincremental rebuild {table:?}"), false);
969        rb.set_plan_strategy(PlanStrategy::MinCover);
970        let mut vars = Vec::<QueryEntry>::with_capacity(schema.len());
971        for ty in schema {
972            vars.push(rb.new_var(*ty).into());
973        }
974        let subsume_var = can_subsume.then(|| rb.new_var(ColumnTy::Id));
975        rb.add_atom_with_timestamp_and_func(
976            table_id,
977            Some(table),
978            subsume_var.map(QueryEntry::from),
979            &vars,
980        );
981        let mut lhs = SmallVec::<[QueryEntry; 4]>::new();
982        let mut rhs = SmallVec::<[QueryEntry; 4]>::new();
983        let mut canon = Vec::<QueryEntry>::with_capacity(schema.len());
984        for (var, ty) in vars.iter().zip(schema.iter()) {
985            canon.push(if let ColumnTy::Id = ty {
986                lhs.push(var.clone());
987                let canon_var = QueryEntry::from(rb.lookup_uf(var.clone()).unwrap());
988                rhs.push(canon_var.clone());
989                canon_var
990            } else {
991                var.clone()
992            })
993        }
994        rb.check_for_update(&lhs, &rhs).unwrap();
995        rb.rebuild_row(table, &vars, &canon, subsume_var);
996        rb.build_internal(None) }
998
999    pub fn with_execution_state<R>(&self, f: impl FnOnce(&mut ExecutionState<'_>) -> R) -> R {
1005        self.db.with_execution_state(f)
1006    }
1007
1008    pub fn flush_updates(&mut self) -> bool {
1011        let updated = self.db.merge_all();
1012        self.inc_ts();
1013        self.rebuild().unwrap();
1014        updated
1015    }
1016}
1017
1018#[derive(Clone)]
1019struct RuleInfo {
1020    last_run_at: Timestamp,
1021    query: rule::Query,
1022    cached_plan: Option<CachedPlanInfo>,
1023    desc: Arc<str>,
1024}
1025
1026#[derive(Clone)]
1027struct CachedPlanInfo {
1028    plan: Arc<core_relations::CachedPlan>,
1029    atom_mapping: Vec<core_relations::AtomId>,
1032}
1033
1034#[derive(Clone)]
1035struct FunctionInfo {
1036    table: TableId,
1037    schema: Vec<ColumnTy>,
1038    incremental_rebuild_rules: Vec<RuleId>,
1039    nonincremental_rebuild_rule: RuleId,
1040    default_val: DefaultVal,
1041    can_subsume: bool,
1042    name: Arc<str>,
1043}
1044
1045impl FunctionInfo {
1046    fn ret_ty(&self) -> ColumnTy {
1047        self.schema.last().copied().unwrap()
1048    }
1049}
1050
1051#[derive(Copy, Clone)]
1053pub enum DefaultVal {
1054    FreshId,
1056    Fail,
1058    Const(Value),
1060}
1061
1062pub enum MergeFn {
1064    AssertEq,
1066    UnionId,
1068    Primitive(ExternalFunctionId, Vec<MergeFn>),
1071    Function(FunctionId, Vec<MergeFn>),
1074    Old,
1076    New,
1078    Const(Value),
1082}
1083
1084impl MergeFn {
1085    fn fill_deps(
1086        &self,
1087        egraph: &EGraph,
1088        read_deps: &mut IndexSet<TableId>,
1089        write_deps: &mut IndexSet<TableId>,
1090    ) {
1091        use MergeFn::*;
1092        match self {
1093            Primitive(_, args) => {
1094                args.iter()
1095                    .for_each(|arg| arg.fill_deps(egraph, read_deps, write_deps));
1096            }
1097            Function(func, args) => {
1098                read_deps.insert(egraph.funcs[*func].table);
1099                write_deps.insert(egraph.funcs[*func].table);
1100                args.iter()
1101                    .for_each(|arg| arg.fill_deps(egraph, read_deps, write_deps));
1102            }
1103            UnionId if !egraph.tracing => {
1104                write_deps.insert(egraph.uf_table);
1105            }
1106            UnionId | AssertEq | Old | New | Const(..) => {}
1107        }
1108    }
1109
1110    fn to_callback(
1111        &self,
1112        schema_math: SchemaMath,
1113        function_name: &str,
1114        egraph: &mut EGraph,
1115    ) -> Box<core_relations::MergeFn> {
1116        assert!(
1117            !egraph.tracing || matches!(self, MergeFn::UnionId),
1118            "proofs aren't supported for non-union merge functions"
1119        );
1120
1121        let resolved = self.resolve(function_name, egraph);
1122
1123        Box::new(move |state, cur, new, out| {
1124            let timestamp = new[schema_math.ts_col()];
1125
1126            let mut changed = false;
1127
1128            let ret_val = {
1129                let cur = cur[schema_math.ret_val_col()];
1130                let new = new[schema_math.ret_val_col()];
1131                let out = resolved.run(state, cur, new, timestamp);
1132                changed |= cur != out;
1133                out
1134            };
1135
1136            let subsume = schema_math.subsume.then(|| {
1137                let cur = cur[schema_math.subsume_col()];
1138                let new = new[schema_math.subsume_col()];
1139                let out = combine_subsumed(cur, new);
1140                changed |= cur != out;
1141                out
1142            });
1143
1144            if changed {
1145                out.extend_from_slice(new);
1146                schema_math.write_table_row(
1147                    out,
1148                    RowVals {
1149                        timestamp,
1150                        proof: None,
1151                        subsume,
1152                        ret_val: Some(ret_val),
1153                    },
1154                );
1155            }
1156
1157            changed
1158        })
1159    }
1160
1161    fn resolve(&self, function_name: &str, egraph: &mut EGraph) -> ResolvedMergeFn {
1162        match self {
1163            MergeFn::Const(v) => ResolvedMergeFn::Const(*v),
1164            MergeFn::Old => ResolvedMergeFn::Old,
1165            MergeFn::New => ResolvedMergeFn::New,
1166            MergeFn::AssertEq => ResolvedMergeFn::AssertEq {
1167                panic: egraph.new_panic(format!(
1168                    "Illegal merge attempted for function {function_name}"
1169                )),
1170            },
1171            MergeFn::UnionId => ResolvedMergeFn::UnionId {
1172                uf_table: egraph.uf_table,
1173                tracing: egraph.tracing,
1174            },
1175            MergeFn::Primitive(prim, args) => ResolvedMergeFn::Primitive {
1180                prim: *prim,
1181                args: args
1182                    .iter()
1183                    .map(|arg| arg.resolve(function_name, egraph))
1184                    .collect::<Vec<_>>(),
1185                panic: egraph.new_panic(format!(
1186                    "Merge function for {function_name} primitive call failed"
1187                )),
1188            },
1189            MergeFn::Function(func, args) => {
1190                let func_info = &egraph.funcs[*func];
1191                assert_eq!(
1192                    func_info.schema.len(),
1193                    args.len() + 1,
1194                    "Merge function for {function_name} must match function arity for {}",
1195                    func_info.name
1196                );
1197                ResolvedMergeFn::Function {
1198                    func: TableAction::new(egraph, *func),
1199                    panic: egraph.new_panic(format!(
1200                        "Lookup on {} failed in the merge function for {function_name}",
1201                        func_info.name
1202                    )),
1203                    args: args
1204                        .iter()
1205                        .map(|arg| arg.resolve(function_name, egraph))
1206                        .collect::<Vec<_>>(),
1207                }
1208            }
1209        }
1210    }
1211}
1212
1213enum ResolvedMergeFn {
1218    Const(Value),
1219    Old,
1220    New,
1221    AssertEq {
1222        panic: ExternalFunctionId,
1223    },
1224    UnionId {
1225        uf_table: TableId,
1226        tracing: bool,
1227    },
1228    Primitive {
1229        prim: ExternalFunctionId,
1230        args: Vec<ResolvedMergeFn>,
1231        panic: ExternalFunctionId,
1232    },
1233    Function {
1234        func: TableAction,
1235        args: Vec<ResolvedMergeFn>,
1236        panic: ExternalFunctionId,
1237    },
1238}
1239
1240impl ResolvedMergeFn {
1241    fn run(&self, state: &mut ExecutionState, cur: Value, new: Value, ts: Value) -> Value {
1242        match self {
1243            ResolvedMergeFn::Const(v) => *v,
1244            ResolvedMergeFn::Old => cur,
1245            ResolvedMergeFn::New => new,
1246            ResolvedMergeFn::AssertEq { panic } => {
1247                if cur != new {
1248                    let res = state.call_external_func(*panic, &[]);
1249                    assert_eq!(res, None);
1250                }
1251                cur
1252            }
1253            ResolvedMergeFn::UnionId { uf_table, tracing } => {
1254                if cur != new && !tracing {
1255                    state.stage_insert(*uf_table, &[cur, new, ts]);
1258                    std::cmp::min(cur, new)
1261                } else {
1262                    cur
1263                }
1264            }
1265            ResolvedMergeFn::Primitive { prim, args, panic } => {
1270                let args = args
1271                    .iter()
1272                    .map(|arg| arg.run(state, cur, new, ts))
1273                    .collect::<Vec<_>>();
1274
1275                match state.call_external_func(*prim, &args) {
1276                    Some(result) => result,
1277                    None => {
1278                        let res = state.call_external_func(*panic, &[]);
1279                        assert_eq!(res, None);
1280                        cur
1281                    }
1282                }
1283            }
1284            ResolvedMergeFn::Function { func, args, panic } => {
1285                if cur == new {
1287                    return cur;
1288                }
1289
1290                let args = args
1291                    .iter()
1292                    .map(|arg| arg.run(state, cur, new, ts))
1293                    .collect::<Vec<_>>();
1294
1295                func.lookup(state, &args).unwrap_or_else(|| {
1296                    let res = state.call_external_func(*panic, &[]);
1297                    assert_eq!(res, None);
1298                    cur
1299                })
1300            }
1301        }
1302    }
1303}
1304
1305#[derive(Debug, PartialEq, Eq, Hash)]
1309pub struct TableAction {
1310    table: TableId,
1311    table_math: SchemaMath,
1312    default: Option<MergeVal>,
1313    timestamp: CounterId,
1314    scratch: Vec<Value>,
1315}
1316
1317impl Clone for TableAction {
1318    fn clone(&self) -> Self {
1319        Self {
1320            table: self.table,
1321            table_math: self.table_math,
1322            default: self.default,
1323            timestamp: self.timestamp,
1324            scratch: Vec::new(),
1325        }
1326    }
1327}
1328
1329impl TableAction {
1330    pub fn new(egraph: &EGraph, func: FunctionId) -> TableAction {
1333        assert!(!egraph.tracing, "proofs not supported yet");
1334
1335        let func_info = &egraph.funcs[func];
1336        TableAction {
1337            table: func_info.table,
1338            table_math: SchemaMath {
1339                func_cols: func_info.schema.len(),
1340                subsume: func_info.can_subsume,
1341                tracing: egraph.tracing,
1342            },
1343            default: match &func_info.default_val {
1344                DefaultVal::FreshId => Some(MergeVal::Counter(egraph.id_counter)),
1345                DefaultVal::Fail => None,
1346                DefaultVal::Const(val) => Some(MergeVal::Constant(*val)),
1347            },
1348            timestamp: egraph.timestamp_counter,
1349            scratch: Vec::new(),
1350        }
1351    }
1352
1353    pub fn lookup(&self, state: &mut ExecutionState, key: &[Value]) -> Option<Value> {
1357        match self.default {
1358            Some(default) => {
1359                let timestamp =
1360                    MergeVal::Constant(Value::from_usize(state.read_counter(self.timestamp)));
1361                let mut merge_vals = SmallVec::<[MergeVal; 3]>::new();
1362                SchemaMath {
1363                    func_cols: 1,
1364                    ..self.table_math
1365                }
1366                .write_table_row(
1367                    &mut merge_vals,
1368                    RowVals {
1369                        timestamp,
1370                        proof: None,
1371                        subsume: self
1372                            .table_math
1373                            .subsume
1374                            .then_some(MergeVal::Constant(NOT_SUBSUMED)),
1375                        ret_val: Some(default),
1376                    },
1377                );
1378                Some(
1379                    state.predict_val(self.table, key, merge_vals.iter().copied())
1380                        [self.table_math.ret_val_col()],
1381                )
1382            }
1383            None => state
1384                .get_table(self.table)
1385                .get_row(key)
1386                .map(|row| row.vals[self.table_math.ret_val_col()]),
1387        }
1388    }
1389
1390    pub fn insert(&mut self, state: &mut ExecutionState, row: impl Iterator<Item = Value>) {
1392        let ts = Value::from_usize(state.read_counter(self.timestamp));
1393        self.scratch.clear();
1394        self.scratch.extend(row);
1395        self.table_math.write_table_row(
1396            &mut self.scratch,
1397            RowVals {
1398                timestamp: ts,
1399                proof: None,
1400                subsume: self.table_math.subsume.then_some(NOT_SUBSUMED),
1401                ret_val: None,
1402            },
1403        );
1404        state.stage_insert(self.table, &self.scratch);
1405    }
1406
1407    pub fn remove(&self, state: &mut ExecutionState, key: &[Value]) {
1409        state.stage_remove(self.table, key);
1410    }
1411
1412    pub fn subsume(&mut self, state: &mut ExecutionState, key: impl Iterator<Item = Value>) {
1414        let ts = Value::from_usize(state.read_counter(self.timestamp));
1415        self.scratch.clear();
1416        self.scratch.extend(key);
1417
1418        let ret_val = self
1419            .lookup(state, &self.scratch)
1420            .expect("subsume lookup failed");
1421
1422        self.table_math.write_table_row(
1423            &mut self.scratch,
1424            RowVals {
1425                timestamp: ts,
1426                proof: None,
1427                subsume: Some(SUBSUMED),
1428                ret_val: Some(ret_val),
1429            },
1430        );
1431        state.stage_insert(self.table, &self.scratch);
1432    }
1433}
1434
1435#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1437pub struct UnionAction {
1438    table: TableId,
1439    timestamp: CounterId,
1440}
1441
1442impl UnionAction {
1443    pub fn new(egraph: &EGraph) -> UnionAction {
1446        assert!(!egraph.tracing, "proofs not supported yet");
1447        UnionAction {
1448            table: egraph.uf_table,
1449            timestamp: egraph.timestamp_counter,
1450        }
1451    }
1452
1453    pub fn union(&self, state: &mut ExecutionState, x: Value, y: Value) {
1455        let ts = Value::from_usize(state.read_counter(self.timestamp));
1456        state.stage_insert(self.table, &[x, y, ts]);
1457    }
1458}
1459
1460fn run_rules_impl(
1461    db: &mut Database,
1462    rule_info: &mut DenseIdMapWithReuse<RuleId, RuleInfo>,
1463    rules: &[RuleId],
1464    next_ts: Timestamp,
1465) -> Result<RuleSetReport> {
1466    for rule in rules {
1467        let info = &mut rule_info[*rule];
1468        if info.cached_plan.is_none() {
1469            info.cached_plan = Some(info.query.build_cached_plan(db, &info.desc)?);
1470        }
1471    }
1472    let mut rsb = db.new_rule_set();
1473    for rule in rules {
1474        let info = &mut rule_info[*rule];
1475        let cached_plan = info.cached_plan.as_ref().unwrap();
1476        info.query
1477            .add_rules_from_cached(&mut rsb, info.last_run_at, cached_plan)?;
1478        info.last_run_at = next_ts;
1479    }
1480    let ruleset = rsb.build();
1481    Ok(db.run_rule_set(&ruleset))
1482}
1483
1484#[inline(never)]
1488fn marker_incremental_rebuild<R>(f: impl FnOnce() -> R) -> R {
1489    f()
1490}
1491
1492#[inline(never)]
1493fn marker_nonincremental_rebuild<R>(f: impl FnOnce() -> R) -> R {
1494    f()
1495}
1496
1497pub type SideChannel<T> = Arc<Mutex<Option<T>>>;
1500
1501#[derive(Clone)]
1507struct GetFirstMatch(SideChannel<Vec<Value>>);
1508
1509impl ExternalFunction for GetFirstMatch {
1510    fn invoke(&self, _: &mut core_relations::ExecutionState, args: &[Value]) -> Option<Value> {
1511        let mut guard = self.0.lock().unwrap();
1512        if guard.is_some() {
1513            return None;
1514        }
1515        *guard = Some(args.to_vec());
1516        Some(Value::new(0))
1517    }
1518}
1519
1520struct LazyPanic<F>(Arc<Lazy<String, F>>, SideChannel<String>);
1531
1532impl<F: FnOnce() -> String + Send> ExternalFunction for LazyPanic<F> {
1533    fn invoke(&self, _: &mut core_relations::ExecutionState, args: &[Value]) -> Option<Value> {
1534        assert!(args.is_empty());
1535        let mut guard = self.1.lock().unwrap();
1536        if guard.is_none() {
1537            *guard = Some(Lazy::force(&self.0).clone());
1538        }
1539        None
1540    }
1541}
1542
1543impl<F> Clone for LazyPanic<F> {
1544    fn clone(&self) -> Self {
1545        LazyPanic(self.0.clone(), self.1.clone())
1546    }
1547}
1548
1549#[derive(Clone)]
1554struct Panic(String, SideChannel<String>);
1555
1556impl EGraph {
1557    pub fn new_panic(&mut self, message: String) -> ExternalFunctionId {
1559        *self
1560            .panic_funcs
1561            .entry(message.to_string())
1562            .or_insert_with(|| {
1563                let panic = Panic(message, self.panic_message.clone());
1564                self.db.add_external_function(panic)
1565            })
1566    }
1567
1568    pub fn new_panic_lazy(
1569        &mut self,
1570        message: impl FnOnce() -> String + Send + 'static,
1571    ) -> ExternalFunctionId {
1572        let lazy = Lazy::new(message);
1573        let panic = LazyPanic(Arc::new(lazy), self.panic_message.clone());
1574        self.db.add_external_function(panic)
1575    }
1576}
1577
1578impl ExternalFunction for Panic {
1579    fn invoke(&self, _: &mut core_relations::ExecutionState, args: &[Value]) -> Option<Value> {
1580        assert!(args.is_empty());
1582
1583        let mut guard = self.1.lock().unwrap();
1584        if guard.is_none() {
1585            *guard = Some(self.0.clone());
1586        }
1587        None
1588    }
1589}
1590
1591#[derive(Error, Debug)]
1592enum ProofReconstructionError {
1593    #[error(
1594        "attempting to explain a row without tracing enabled. Try constructing with `EGraph::with_tracing`"
1595    )]
1596    TracingNotEnabled,
1597    #[error("attempting to construct a proof that {term1} = {term2}, but they are not equal")]
1598    EqualityExplanationOfUnequalTerms { term1: String, term2: String },
1599}
1600
1601fn incremental_rebuild(uf_size: usize, table_size: usize, parallel: bool) -> bool {
1604    if parallel {
1605        uf_size <= (table_size / 16)
1606    } else {
1607        uf_size <= (table_size / 8)
1608    }
1609}
1610
1611pub(crate) const SUBSUMED: Value = Value::new_const(1);
1612pub(crate) const NOT_SUBSUMED: Value = Value::new_const(0);
1613fn combine_subsumed(v1: Value, v2: Value) -> Value {
1614    std::cmp::max(v1, v2)
1615}
1616
1617#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1628struct SchemaMath {
1629    tracing: bool,
1631    subsume: bool,
1633    func_cols: usize,
1635}
1636
1637struct RowVals<T> {
1642    timestamp: T,
1644    proof: Option<T>,
1646    subsume: Option<T>,
1648    ret_val: Option<T>,
1651}
1652
1653#[derive(Clone, Debug)]
1655pub struct FunctionRow<'a> {
1656    pub vals: &'a [Value],
1657    pub subsumed: bool,
1658}
1659
1660impl SchemaMath {
1661    fn write_table_row<T: Clone>(
1662        &self,
1663        row: &mut impl HasResizeWith<T>,
1664        RowVals {
1665            timestamp,
1666            proof,
1667            subsume,
1668            ret_val,
1669        }: RowVals<T>,
1670    ) {
1671        row.resize_with(self.table_columns(), || timestamp.clone());
1672        row[self.ts_col()] = timestamp;
1673        if let Some(ret_val) = ret_val {
1674            row[self.ret_val_col()] = ret_val;
1675        }
1676        if let Some(proof_id) = proof {
1677            row[self.proof_id_col()] = proof_id;
1678        } else {
1679            assert!(
1680                !self.tracing,
1681                "proof_id must be provided if tracing is enabled"
1682            );
1683        }
1684        if let Some(subsume) = subsume {
1685            row[self.subsume_col()] = subsume;
1686        } else {
1687            assert!(
1688                !self.subsume,
1689                "subsume flag must be provided if subsumption is enabled"
1690            );
1691        }
1692    }
1693
1694    fn num_keys(&self) -> usize {
1695        self.func_cols - 1
1696    }
1697
1698    fn table_columns(&self) -> usize {
1699        self.func_cols + 1 + if self.tracing { 1 } else { 0 } + if self.subsume { 1 } else { 0 }
1700    }
1701
1702    #[track_caller]
1703    fn proof_id_col(&self) -> usize {
1704        assert!(self.tracing);
1705        self.func_cols + 1
1706    }
1707
1708    fn ret_val_col(&self) -> usize {
1709        self.func_cols - 1
1710    }
1711
1712    fn ts_col(&self) -> usize {
1713        self.func_cols
1714    }
1715
1716    #[track_caller]
1717    fn subsume_col(&self) -> usize {
1718        assert!(self.subsume);
1719        if self.tracing {
1720            self.func_cols + 2
1721        } else {
1722            self.func_cols + 1
1723        }
1724    }
1725}
1726
1727#[derive(Error, Debug)]
1728#[error("Panic: {0}")]
1729struct PanicError(String);
1730
1731trait HasResizeWith<T>:
1734    AsMut<[T]> + AsRef<[T]> + Index<usize, Output = T> + IndexMut<usize, Output = T>
1735{
1736    fn resize_with<F>(&mut self, new_size: usize, f: F)
1737    where
1738        F: FnMut() -> T;
1739}
1740
1741impl<T> HasResizeWith<T> for Vec<T> {
1742    fn resize_with<F>(&mut self, new_size: usize, f: F)
1743    where
1744        F: FnMut() -> T,
1745    {
1746        self.resize_with(new_size, f);
1747    }
1748}
1749
1750impl<T, A: smallvec::Array<Item = T>> HasResizeWith<T> for SmallVec<A> {
1751    fn resize_with<F>(&mut self, new_size: usize, f: F)
1752    where
1753        F: FnMut() -> T,
1754    {
1755        self.resize_with(new_size, f);
1756    }
1757}
1758
1759#[derive(Debug, Default)]
1763pub struct IterationReport {
1764    pub changed: bool,
1765    pub rule_reports: HashMap<String, RuleReport>,
1766    pub search_and_apply_time: Duration,
1767    pub merge_time: Duration,
1768    pub rebuild_time: Duration,
1769}
1770
1771pub use crate::core_relations::RuleReport;