use crate::reduce::ReducibilityHint;
use crate::{Expr, Level, Name};
use std::collections::{HashMap, HashSet, VecDeque};
#[allow(dead_code)]
pub struct WorkStack<T> {
items: Vec<T>,
}
#[allow(dead_code)]
impl<T> WorkStack<T> {
pub fn new() -> Self {
Self { items: Vec::new() }
}
pub fn push(&mut self, item: T) {
self.items.push(item);
}
pub fn pop(&mut self) -> Option<T> {
self.items.pop()
}
pub fn is_empty(&self) -> bool {
self.items.is_empty()
}
pub fn len(&self) -> usize {
self.items.len()
}
}
#[allow(dead_code)]
pub struct MemoSlot<T: Clone> {
cached: Option<T>,
}
#[allow(dead_code)]
impl<T: Clone> MemoSlot<T> {
pub fn new() -> Self {
Self { cached: None }
}
pub fn get_or_compute(&mut self, f: impl FnOnce() -> T) -> &T {
if self.cached.is_none() {
self.cached = Some(f());
}
self.cached
.as_ref()
.expect("cached value must be initialized before access")
}
pub fn invalidate(&mut self) {
self.cached = None;
}
pub fn is_cached(&self) -> bool {
self.cached.is_some()
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct ConstantVal {
pub name: Name,
pub level_params: Vec<Name>,
pub ty: Expr,
}
#[allow(dead_code)]
pub struct DeclIndex {
map: std::collections::HashMap<String, usize>,
}
#[allow(dead_code)]
impl DeclIndex {
pub fn new() -> Self {
Self {
map: std::collections::HashMap::new(),
}
}
pub fn insert(&mut self, name: impl Into<String>, pos: usize) {
self.map.insert(name.into(), pos);
}
pub fn get(&self, name: &str) -> Option<usize> {
self.map.get(name).copied()
}
pub fn len(&self) -> usize {
self.map.len()
}
pub fn is_empty(&self) -> bool {
self.map.is_empty()
}
pub fn names(&self) -> Vec<&str> {
self.map.keys().map(|s| s.as_str()).collect()
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct AxiomVal {
pub common: ConstantVal,
pub is_unsafe: bool,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum QuotKind {
Type,
Mk,
Lift,
Ind,
}
#[derive(Clone, Debug, PartialEq)]
pub struct ConstructorVal {
pub common: ConstantVal,
pub induct: Name,
pub cidx: u32,
pub num_params: u32,
pub num_fields: u32,
pub is_unsafe: bool,
}
#[allow(dead_code)]
pub struct EventCounter {
counts: std::collections::HashMap<String, u64>,
}
#[allow(dead_code)]
impl EventCounter {
pub fn new() -> Self {
Self {
counts: std::collections::HashMap::new(),
}
}
pub fn inc(&mut self, event: &str) {
*self.counts.entry(event.to_string()).or_insert(0) += 1;
}
pub fn add(&mut self, event: &str, n: u64) {
*self.counts.entry(event.to_string()).or_insert(0) += n;
}
pub fn get(&self, event: &str) -> u64 {
self.counts.get(event).copied().unwrap_or(0)
}
pub fn total(&self) -> u64 {
self.counts.values().sum()
}
pub fn reset(&mut self) {
self.counts.clear();
}
pub fn event_names(&self) -> Vec<&str> {
self.counts.keys().map(|s| s.as_str()).collect()
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct TheoremVal {
pub common: ConstantVal,
pub value: Expr,
pub all: Vec<Name>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct RecursorVal {
pub common: ConstantVal,
pub all: Vec<Name>,
pub num_params: u32,
pub num_indices: u32,
pub num_motives: u32,
pub num_minors: u32,
pub rules: Vec<RecursorRule>,
pub k: bool,
pub is_unsafe: bool,
}
impl RecursorVal {
pub fn get_major_idx(&self) -> u32 {
self.num_params + self.num_motives + self.num_minors + self.num_indices
}
pub fn get_first_index_idx(&self) -> u32 {
self.num_params + self.num_motives + self.num_minors
}
pub fn get_rule(&self, ctor: &Name) -> Option<&RecursorRule> {
self.rules.iter().find(|r| &r.ctor == ctor)
}
}
#[allow(dead_code)]
pub struct ScopeStack {
names: Vec<String>,
}
#[allow(dead_code)]
impl ScopeStack {
pub fn new() -> Self {
Self { names: Vec::new() }
}
pub fn push(&mut self, name: impl Into<String>) {
self.names.push(name.into());
}
pub fn pop(&mut self) -> Option<String> {
self.names.pop()
}
pub fn current(&self) -> Option<&str> {
self.names.last().map(|s| s.as_str())
}
pub fn depth(&self) -> usize {
self.names.len()
}
pub fn is_empty(&self) -> bool {
self.names.is_empty()
}
pub fn path(&self) -> String {
self.names.join(".")
}
}
#[allow(dead_code)]
pub struct AnnotationTable {
map: std::collections::HashMap<String, Vec<String>>,
}
#[allow(dead_code)]
impl AnnotationTable {
pub fn new() -> Self {
Self {
map: std::collections::HashMap::new(),
}
}
pub fn annotate(&mut self, key: impl Into<String>, val: impl Into<String>) {
self.map.entry(key.into()).or_default().push(val.into());
}
pub fn get_all(&self, key: &str) -> &[String] {
self.map.get(key).map(|v| v.as_slice()).unwrap_or(&[])
}
pub fn num_keys(&self) -> usize {
self.map.len()
}
pub fn has(&self, key: &str) -> bool {
self.map.contains_key(key)
}
}
#[allow(dead_code)]
pub struct LoopClock {
start: std::time::Instant,
iters: u64,
}
#[allow(dead_code)]
impl LoopClock {
pub fn start() -> Self {
Self {
start: std::time::Instant::now(),
iters: 0,
}
}
pub fn tick(&mut self) {
self.iters += 1;
}
pub fn elapsed_us(&self) -> f64 {
self.start.elapsed().as_secs_f64() * 1e6
}
pub fn avg_us_per_iter(&self) -> f64 {
if self.iters == 0 {
return 0.0;
}
self.elapsed_us() / self.iters as f64
}
pub fn iters(&self) -> u64 {
self.iters
}
}
#[allow(dead_code)]
pub struct SparseBitSet {
words: Vec<u64>,
}
#[allow(dead_code)]
impl SparseBitSet {
pub fn new(capacity: usize) -> Self {
let words = (capacity + 63) / 64;
Self {
words: vec![0u64; words],
}
}
pub fn set(&mut self, i: usize) {
let word = i / 64;
let bit = i % 64;
if word < self.words.len() {
self.words[word] |= 1u64 << bit;
}
}
pub fn clear(&mut self, i: usize) {
let word = i / 64;
let bit = i % 64;
if word < self.words.len() {
self.words[word] &= !(1u64 << bit);
}
}
pub fn get(&self, i: usize) -> bool {
let word = i / 64;
let bit = i % 64;
self.words.get(word).is_some_and(|w| w & (1u64 << bit) != 0)
}
pub fn count_ones(&self) -> u32 {
self.words.iter().map(|w| w.count_ones()).sum()
}
pub fn union(&self, other: &SparseBitSet) -> SparseBitSet {
let len = self.words.len().max(other.words.len());
let mut result = SparseBitSet {
words: vec![0u64; len],
};
for i in 0..self.words.len() {
result.words[i] |= self.words[i];
}
for i in 0..other.words.len() {
result.words[i] |= other.words[i];
}
result
}
}
#[allow(dead_code)]
#[allow(missing_docs)]
pub struct StatCache<K: std::hash::Hash + Eq + Clone, V: Clone> {
pub inner: SimpleLruCache<K, V>,
pub hits: u64,
pub misses: u64,
}
#[allow(dead_code)]
impl<K: std::hash::Hash + Eq + Clone, V: Clone> StatCache<K, V> {
pub fn new(capacity: usize) -> Self {
Self {
inner: SimpleLruCache::new(capacity),
hits: 0,
misses: 0,
}
}
pub fn get(&mut self, key: &K) -> Option<&V> {
let result = self.inner.get(key);
if result.is_some() {
self.hits += 1;
} else {
self.misses += 1;
}
None
}
pub fn put(&mut self, key: K, val: V) {
self.inner.put(key, val);
}
pub fn hit_rate(&self) -> f64 {
let total = self.hits + self.misses;
if total == 0 {
return 0.0;
}
self.hits as f64 / total as f64
}
}
#[allow(dead_code)]
pub struct Slot<T> {
inner: Option<T>,
}
#[allow(dead_code)]
impl<T> Slot<T> {
pub fn empty() -> Self {
Self { inner: None }
}
pub fn fill(&mut self, val: T) {
assert!(self.inner.is_none(), "Slot: already filled");
self.inner = Some(val);
}
pub fn get(&self) -> Option<&T> {
self.inner.as_ref()
}
pub fn is_filled(&self) -> bool {
self.inner.is_some()
}
pub fn take(&mut self) -> Option<T> {
self.inner.take()
}
pub fn get_or_fill_with(&mut self, f: impl FnOnce() -> T) -> &T {
if self.inner.is_none() {
self.inner = Some(f());
}
self.inner
.as_ref()
.expect("inner value must be initialized before access")
}
}
#[allow(dead_code)]
#[allow(missing_docs)]
pub struct BeforeAfter<T> {
pub before: T,
pub after: T,
}
#[allow(dead_code)]
impl<T: PartialEq> BeforeAfter<T> {
pub fn new(before: T, after: T) -> Self {
Self { before, after }
}
pub fn unchanged(&self) -> bool {
self.before == self.after
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct RecursorRule {
pub ctor: Name,
pub nfields: u32,
pub rhs: Expr,
}
#[allow(dead_code)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Timestamp(u64);
#[allow(dead_code)]
impl Timestamp {
pub const fn from_us(us: u64) -> Self {
Self(us)
}
pub fn as_us(self) -> u64 {
self.0
}
pub fn elapsed_since(self, earlier: Timestamp) -> u64 {
self.0.saturating_sub(earlier.0)
}
}
#[allow(dead_code)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum DeclAttr {
Inline,
Simp,
Ext,
Reducible,
Unknown(String),
}
#[allow(dead_code)]
impl DeclAttr {
pub fn name(&self) -> &str {
match self {
DeclAttr::Inline => "inline",
DeclAttr::Simp => "simp",
DeclAttr::Ext => "ext",
DeclAttr::Reducible => "reducible",
DeclAttr::Unknown(s) => s.as_str(),
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum ConstantInfo {
Axiom(AxiomVal),
Definition(DefinitionVal),
Theorem(TheoremVal),
Opaque(OpaqueVal),
Inductive(InductiveVal),
Constructor(ConstructorVal),
Recursor(RecursorVal),
Quotient(QuotVal),
}
impl ConstantInfo {
pub fn name(&self) -> &Name {
&self.common().name
}
pub fn ty(&self) -> &Expr {
&self.common().ty
}
pub fn level_params(&self) -> &[Name] {
&self.common().level_params
}
pub fn common(&self) -> &ConstantVal {
match self {
ConstantInfo::Axiom(v) => &v.common,
ConstantInfo::Definition(v) => &v.common,
ConstantInfo::Theorem(v) => &v.common,
ConstantInfo::Opaque(v) => &v.common,
ConstantInfo::Inductive(v) => &v.common,
ConstantInfo::Constructor(v) => &v.common,
ConstantInfo::Recursor(v) => &v.common,
ConstantInfo::Quotient(v) => &v.common,
}
}
pub fn is_axiom(&self) -> bool {
matches!(self, ConstantInfo::Axiom(_))
}
pub fn is_definition(&self) -> bool {
matches!(self, ConstantInfo::Definition(_))
}
pub fn is_theorem(&self) -> bool {
matches!(self, ConstantInfo::Theorem(_))
}
pub fn is_opaque(&self) -> bool {
matches!(self, ConstantInfo::Opaque(_))
}
pub fn is_inductive(&self) -> bool {
matches!(self, ConstantInfo::Inductive(_))
}
pub fn is_constructor(&self) -> bool {
matches!(self, ConstantInfo::Constructor(_))
}
pub fn is_recursor(&self) -> bool {
matches!(self, ConstantInfo::Recursor(_))
}
pub fn is_quotient(&self) -> bool {
matches!(self, ConstantInfo::Quotient(_))
}
pub fn to_definition_val(&self) -> Option<&DefinitionVal> {
match self {
ConstantInfo::Definition(v) => Some(v),
_ => None,
}
}
pub fn to_inductive_val(&self) -> Option<&InductiveVal> {
match self {
ConstantInfo::Inductive(v) => Some(v),
_ => None,
}
}
pub fn to_constructor_val(&self) -> Option<&ConstructorVal> {
match self {
ConstantInfo::Constructor(v) => Some(v),
_ => None,
}
}
pub fn to_recursor_val(&self) -> Option<&RecursorVal> {
match self {
ConstantInfo::Recursor(v) => Some(v),
_ => None,
}
}
pub fn to_quotient_val(&self) -> Option<&QuotVal> {
match self {
ConstantInfo::Quotient(v) => Some(v),
_ => None,
}
}
pub fn to_axiom_val(&self) -> Option<&AxiomVal> {
match self {
ConstantInfo::Axiom(v) => Some(v),
_ => None,
}
}
pub fn to_theorem_val(&self) -> Option<&TheoremVal> {
match self {
ConstantInfo::Theorem(v) => Some(v),
_ => None,
}
}
pub fn to_opaque_val(&self) -> Option<&OpaqueVal> {
match self {
ConstantInfo::Opaque(v) => Some(v),
_ => None,
}
}
pub fn has_value(&self, allow_opaque: bool) -> bool {
match self {
ConstantInfo::Definition(_) | ConstantInfo::Theorem(_) => true,
ConstantInfo::Opaque(_) => allow_opaque,
_ => false,
}
}
pub fn value(&self) -> Option<&Expr> {
match self {
ConstantInfo::Definition(v) => Some(&v.value),
ConstantInfo::Theorem(v) => Some(&v.value),
ConstantInfo::Opaque(v) => Some(&v.value),
_ => None,
}
}
pub fn reducibility_hint(&self) -> ReducibilityHint {
match self {
ConstantInfo::Definition(v) => v.hints,
ConstantInfo::Theorem(_) | ConstantInfo::Opaque(_) | ConstantInfo::Axiom(_) => {
ReducibilityHint::Opaque
}
_ => ReducibilityHint::Opaque,
}
}
pub fn is_unsafe(&self) -> bool {
match self {
ConstantInfo::Axiom(v) => v.is_unsafe,
ConstantInfo::Definition(v) => v.safety == DefinitionSafety::Unsafe,
ConstantInfo::Opaque(v) => v.is_unsafe,
ConstantInfo::Inductive(v) => v.is_unsafe,
ConstantInfo::Constructor(v) => v.is_unsafe,
ConstantInfo::Recursor(v) => v.is_unsafe,
ConstantInfo::Theorem(_) | ConstantInfo::Quotient(_) => false,
}
}
pub fn is_structure_like(&self) -> bool {
match self {
ConstantInfo::Inductive(v) => v.ctors.len() == 1 && !v.is_rec && v.num_indices == 0,
_ => false,
}
}
}
impl ConstantInfo {
pub fn kind(&self) -> ConstantKind {
match self {
ConstantInfo::Axiom(_) => ConstantKind::Axiom,
ConstantInfo::Definition(_) => ConstantKind::Definition,
ConstantInfo::Theorem(_) => ConstantKind::Theorem,
ConstantInfo::Opaque(_) => ConstantKind::Opaque,
ConstantInfo::Inductive(_) => ConstantKind::Inductive,
ConstantInfo::Constructor(_) => ConstantKind::Constructor,
ConstantInfo::Recursor(_) => ConstantKind::Recursor,
ConstantInfo::Quotient(_) => ConstantKind::Quotient,
}
}
pub fn summarize(&self) -> ConstantSummary {
ConstantSummary {
name: self.name().clone(),
kind: self.kind(),
num_level_params: self.level_params().len(),
is_polymorphic: !self.level_params().is_empty(),
}
}
pub fn num_level_params(&self) -> usize {
self.level_params().len()
}
pub fn is_polymorphic(&self) -> bool {
!self.level_params().is_empty()
}
pub fn parent_inductive(&self) -> Option<&Name> {
match self {
ConstantInfo::Constructor(cv) => Some(&cv.induct),
_ => None,
}
}
pub fn inductive_num_params(&self) -> Option<usize> {
match self {
ConstantInfo::Inductive(iv) => Some(iv.num_params as usize),
_ => None,
}
}
pub fn inductive_num_indices(&self) -> Option<usize> {
match self {
ConstantInfo::Inductive(iv) => Some(iv.num_indices as usize),
_ => None,
}
}
pub fn inductive_constructors(&self) -> Option<&[Name]> {
match self {
ConstantInfo::Inductive(iv) => Some(&iv.ctors),
_ => None,
}
}
pub fn ctor_num_fields(&self) -> Option<usize> {
match self {
ConstantInfo::Constructor(cv) => Some(cv.num_fields as usize),
_ => None,
}
}
pub fn recursor_rules(&self) -> Option<&[RecursorRule]> {
match self {
ConstantInfo::Recursor(rv) => Some(&rv.rules),
_ => None,
}
}
pub fn display_string(&self) -> String {
let kind = self.kind().as_str();
if self.level_params().is_empty() {
format!("{} {}", kind, self.name())
} else {
let params: Vec<String> = self
.level_params()
.iter()
.map(|p| format!("{}", p))
.collect();
format!("{} {}.{{{}}} ", kind, self.name(), params.join(", "))
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum DefinitionSafety {
Safe,
Unsafe,
Partial,
}
impl DefinitionSafety {
pub fn is_safe(&self) -> bool {
matches!(self, DefinitionSafety::Safe)
}
pub fn is_partial(&self) -> bool {
matches!(self, DefinitionSafety::Partial)
}
pub fn as_str(&self) -> &'static str {
match self {
DefinitionSafety::Safe => "safe",
DefinitionSafety::Unsafe => "unsafe",
DefinitionSafety::Partial => "partial",
}
}
}
#[allow(dead_code)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct TypedId<T> {
pub(super) id: u32,
_phantom: std::marker::PhantomData<T>,
}
#[allow(dead_code)]
impl<T> TypedId<T> {
pub const fn new(id: u32) -> Self {
Self {
id,
_phantom: std::marker::PhantomData,
}
}
pub fn raw(&self) -> u32 {
self.id
}
}
#[allow(dead_code)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum DeclKind {
Theorem,
Definition,
Opaque,
Axiom,
Structure,
Inductive,
Recursive,
Instance,
}
#[allow(dead_code)]
impl DeclKind {
pub fn has_body(self) -> bool {
!matches!(self, DeclKind::Axiom)
}
pub fn is_type_decl(self) -> bool {
matches!(self, DeclKind::Structure | DeclKind::Inductive)
}
pub fn label(self) -> &'static str {
match self {
DeclKind::Theorem => "theorem",
DeclKind::Definition => "def",
DeclKind::Opaque => "opaque",
DeclKind::Axiom => "axiom",
DeclKind::Structure => "structure",
DeclKind::Inductive => "inductive",
DeclKind::Recursive => "recdef",
DeclKind::Instance => "instance",
}
}
}
#[allow(dead_code)]
pub struct StringInterner {
strings: Vec<String>,
map: std::collections::HashMap<String, u32>,
}
#[allow(dead_code)]
impl StringInterner {
pub fn new() -> Self {
Self {
strings: Vec::new(),
map: std::collections::HashMap::new(),
}
}
pub fn intern(&mut self, s: &str) -> u32 {
if let Some(&id) = self.map.get(s) {
return id;
}
let id = self.strings.len() as u32;
self.strings.push(s.to_string());
self.map.insert(s.to_string(), id);
id
}
pub fn get(&self, id: u32) -> Option<&str> {
self.strings.get(id as usize).map(|s| s.as_str())
}
pub fn len(&self) -> usize {
self.strings.len()
}
pub fn is_empty(&self) -> bool {
self.strings.is_empty()
}
}
#[allow(dead_code)]
pub struct RingBuffer<T> {
data: Vec<Option<T>>,
head: usize,
tail: usize,
count: usize,
capacity: usize,
}
#[allow(dead_code)]
impl<T> RingBuffer<T> {
pub fn new(capacity: usize) -> Self {
let mut data = Vec::with_capacity(capacity);
for _ in 0..capacity {
data.push(None);
}
Self {
data,
head: 0,
tail: 0,
count: 0,
capacity,
}
}
pub fn push(&mut self, val: T) {
if self.count == self.capacity {
self.data[self.head] = Some(val);
self.head = (self.head + 1) % self.capacity;
self.tail = (self.tail + 1) % self.capacity;
} else {
self.data[self.tail] = Some(val);
self.tail = (self.tail + 1) % self.capacity;
self.count += 1;
}
}
pub fn pop(&mut self) -> Option<T> {
if self.count == 0 {
return None;
}
let val = self.data[self.head].take();
self.head = (self.head + 1) % self.capacity;
self.count -= 1;
val
}
pub fn len(&self) -> usize {
self.count
}
pub fn is_empty(&self) -> bool {
self.count == 0
}
pub fn is_full(&self) -> bool {
self.count == self.capacity
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct InductiveVal {
pub common: ConstantVal,
pub num_params: u32,
pub num_indices: u32,
pub all: Vec<Name>,
pub ctors: Vec<Name>,
pub num_nested: u32,
pub is_rec: bool,
pub is_unsafe: bool,
pub is_reflexive: bool,
pub is_prop: bool,
}
#[allow(dead_code)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct SeqNum(u64);
#[allow(dead_code)]
impl SeqNum {
pub const ZERO: SeqNum = SeqNum(0);
pub fn next(self) -> SeqNum {
SeqNum(self.0 + 1)
}
pub fn value(self) -> u64 {
self.0
}
}
#[allow(dead_code)]
pub struct BiMap<A: std::hash::Hash + Eq + Clone, B: std::hash::Hash + Eq + Clone> {
forward: std::collections::HashMap<A, B>,
backward: std::collections::HashMap<B, A>,
}
#[allow(dead_code)]
impl<A: std::hash::Hash + Eq + Clone, B: std::hash::Hash + Eq + Clone> BiMap<A, B> {
pub fn new() -> Self {
Self {
forward: std::collections::HashMap::new(),
backward: std::collections::HashMap::new(),
}
}
pub fn insert(&mut self, a: A, b: B) {
self.forward.insert(a.clone(), b.clone());
self.backward.insert(b, a);
}
pub fn get_b(&self, a: &A) -> Option<&B> {
self.forward.get(a)
}
pub fn get_a(&self, b: &B) -> Option<&A> {
self.backward.get(b)
}
pub fn len(&self) -> usize {
self.forward.len()
}
pub fn is_empty(&self) -> bool {
self.forward.is_empty()
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct OpaqueVal {
pub common: ConstantVal,
pub value: Expr,
pub is_unsafe: bool,
pub all: Vec<Name>,
}
#[allow(dead_code)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum DeclVisibility {
Public,
Protected,
Private,
}
#[allow(dead_code)]
impl DeclVisibility {
pub fn is_externally_visible(self) -> bool {
matches!(self, DeclVisibility::Public)
}
}
#[allow(dead_code)]
pub struct IdDispenser<T> {
next: u32,
_phantom: std::marker::PhantomData<T>,
}
#[allow(dead_code)]
impl<T> IdDispenser<T> {
pub fn new() -> Self {
Self {
next: 0,
_phantom: std::marker::PhantomData,
}
}
#[allow(clippy::should_implement_trait)]
pub fn next(&mut self) -> TypedId<T> {
let id = TypedId::new(self.next);
self.next += 1;
id
}
pub fn count(&self) -> u32 {
self.next
}
}
#[allow(dead_code)]
pub struct DeclDependencies {
deps: Vec<std::collections::HashSet<usize>>,
}
#[allow(dead_code)]
impl DeclDependencies {
pub fn new(n: usize) -> Self {
Self {
deps: vec![std::collections::HashSet::new(); n],
}
}
pub fn add(&mut self, dependent: usize, dependency: usize) {
if dependent < self.deps.len() {
self.deps[dependent].insert(dependency);
}
}
pub fn deps_of(&self, idx: usize) -> &std::collections::HashSet<usize> {
static EMPTY: std::sync::OnceLock<std::collections::HashSet<usize>> =
std::sync::OnceLock::new();
self.deps
.get(idx)
.unwrap_or_else(|| EMPTY.get_or_init(std::collections::HashSet::new))
}
pub fn directly_depends(&self, dependent: usize, dependency: usize) -> bool {
self.deps_of(dependent).contains(&dependency)
}
pub fn total_edges(&self) -> usize {
self.deps.iter().map(|s| s.len()).sum()
}
}
#[allow(dead_code)]
pub struct IntervalSet {
intervals: Vec<(i64, i64)>,
}
#[allow(dead_code)]
impl IntervalSet {
pub fn new() -> Self {
Self {
intervals: Vec::new(),
}
}
pub fn add(&mut self, lo: i64, hi: i64) {
if lo > hi {
return;
}
let mut new_lo = lo;
let mut new_hi = hi;
let mut i = 0;
while i < self.intervals.len() {
let (il, ih) = self.intervals[i];
if ih < new_lo - 1 {
i += 1;
continue;
}
if il > new_hi + 1 {
break;
}
new_lo = new_lo.min(il);
new_hi = new_hi.max(ih);
self.intervals.remove(i);
}
self.intervals.insert(i, (new_lo, new_hi));
}
pub fn contains(&self, x: i64) -> bool {
self.intervals.iter().any(|&(lo, hi)| x >= lo && x <= hi)
}
pub fn num_intervals(&self) -> usize {
self.intervals.len()
}
pub fn cardinality(&self) -> i64 {
self.intervals.iter().map(|&(lo, hi)| hi - lo + 1).sum()
}
}
#[allow(dead_code)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Generation(u32);
#[allow(dead_code)]
impl Generation {
pub const INITIAL: Generation = Generation(0);
pub fn advance(self) -> Generation {
Generation(self.0 + 1)
}
pub fn number(self) -> u32 {
self.0
}
}
#[allow(dead_code)]
pub struct WorkQueue<T> {
items: std::collections::VecDeque<T>,
}
#[allow(dead_code)]
impl<T> WorkQueue<T> {
pub fn new() -> Self {
Self {
items: std::collections::VecDeque::new(),
}
}
pub fn enqueue(&mut self, item: T) {
self.items.push_back(item);
}
pub fn dequeue(&mut self) -> Option<T> {
self.items.pop_front()
}
pub fn is_empty(&self) -> bool {
self.items.is_empty()
}
pub fn len(&self) -> usize {
self.items.len()
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct DefinitionVal {
pub common: ConstantVal,
pub value: Expr,
pub hints: ReducibilityHint,
pub safety: DefinitionSafety,
pub all: Vec<Name>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct QuotVal {
pub common: ConstantVal,
pub kind: QuotKind,
}
#[derive(Debug, Clone)]
pub struct ConstantSummary {
pub name: Name,
pub kind: ConstantKind,
pub num_level_params: usize,
pub is_polymorphic: bool,
}
#[allow(dead_code)]
pub struct FrequencyTable<T: std::hash::Hash + Eq + Clone> {
counts: std::collections::HashMap<T, u64>,
}
#[allow(dead_code)]
impl<T: std::hash::Hash + Eq + Clone> FrequencyTable<T> {
pub fn new() -> Self {
Self {
counts: std::collections::HashMap::new(),
}
}
pub fn record(&mut self, item: T) {
*self.counts.entry(item).or_insert(0) += 1;
}
pub fn freq(&self, item: &T) -> u64 {
self.counts.get(item).copied().unwrap_or(0)
}
pub fn most_frequent(&self) -> Option<(&T, u64)> {
self.counts
.iter()
.max_by_key(|(_, &v)| v)
.map(|(k, &v)| (k, v))
}
pub fn total(&self) -> u64 {
self.counts.values().sum()
}
pub fn distinct(&self) -> usize {
self.counts.len()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ConstantKind {
Axiom,
Definition,
Theorem,
Opaque,
Inductive,
Constructor,
Recursor,
Quotient,
}
impl ConstantKind {
pub fn as_str(&self) -> &'static str {
match self {
ConstantKind::Axiom => "axiom",
ConstantKind::Definition => "definition",
ConstantKind::Theorem => "theorem",
ConstantKind::Opaque => "opaque",
ConstantKind::Inductive => "inductive",
ConstantKind::Constructor => "constructor",
ConstantKind::Recursor => "recursor",
ConstantKind::Quotient => "quotient",
}
}
pub fn has_body(&self) -> bool {
matches!(
self,
ConstantKind::Definition | ConstantKind::Theorem | ConstantKind::Opaque
)
}
pub fn is_inductive_family(&self) -> bool {
matches!(
self,
ConstantKind::Inductive | ConstantKind::Constructor | ConstantKind::Recursor
)
}
}
#[allow(dead_code)]
pub struct DeclFilter {
allowed_kinds: Option<Vec<DeclKind>>,
ns_prefix: Option<String>,
required_attrs: Vec<DeclAttr>,
}
#[allow(dead_code)]
impl DeclFilter {
pub fn accept_all() -> Self {
Self {
allowed_kinds: None,
ns_prefix: None,
required_attrs: Vec::new(),
}
}
pub fn with_kinds(mut self, kinds: Vec<DeclKind>) -> Self {
self.allowed_kinds = Some(kinds);
self
}
pub fn in_namespace(mut self, prefix: impl Into<String>) -> Self {
self.ns_prefix = Some(prefix.into());
self
}
pub fn with_attr(mut self, attr: DeclAttr) -> Self {
self.required_attrs.push(attr);
self
}
pub fn accepts(&self, sig: &DeclSignature, kind: DeclKind, attrs: &[DeclAttr]) -> bool {
if let Some(ref kinds) = self.allowed_kinds {
if !kinds.contains(&kind) {
return false;
}
}
if let Some(ref prefix) = self.ns_prefix {
if !sig.name.starts_with(prefix.as_str()) {
return false;
}
}
for req in &self.required_attrs {
if !attrs.contains(req) {
return false;
}
}
true
}
}
#[allow(dead_code)]
pub struct SimpleLruCache<K: std::hash::Hash + Eq + Clone, V: Clone> {
capacity: usize,
map: std::collections::HashMap<K, usize>,
keys: Vec<K>,
vals: Vec<V>,
order: Vec<usize>,
}
#[allow(dead_code)]
impl<K: std::hash::Hash + Eq + Clone, V: Clone> SimpleLruCache<K, V> {
pub fn new(capacity: usize) -> Self {
assert!(capacity > 0);
Self {
capacity,
map: std::collections::HashMap::new(),
keys: Vec::new(),
vals: Vec::new(),
order: Vec::new(),
}
}
pub fn put(&mut self, key: K, val: V) {
if let Some(&idx) = self.map.get(&key) {
self.vals[idx] = val;
self.order.retain(|&x| x != idx);
self.order.insert(0, idx);
return;
}
if self.keys.len() >= self.capacity {
let evict_idx = *self
.order
.last()
.expect("order list must be non-empty before eviction");
self.map.remove(&self.keys[evict_idx]);
self.order.pop();
self.keys[evict_idx] = key.clone();
self.vals[evict_idx] = val;
self.map.insert(key, evict_idx);
self.order.insert(0, evict_idx);
} else {
let idx = self.keys.len();
self.keys.push(key.clone());
self.vals.push(val);
self.map.insert(key, idx);
self.order.insert(0, idx);
}
}
pub fn get(&mut self, key: &K) -> Option<&V> {
let idx = *self.map.get(key)?;
self.order.retain(|&x| x != idx);
self.order.insert(0, idx);
Some(&self.vals[idx])
}
pub fn len(&self) -> usize {
self.keys.len()
}
pub fn is_empty(&self) -> bool {
self.keys.is_empty()
}
}
#[allow(dead_code)]
#[allow(missing_docs)]
pub struct DeclSignature {
pub name: String,
pub ty: String,
pub uparams: Vec<String>,
}
#[allow(dead_code)]
impl DeclSignature {
pub fn new(name: impl Into<String>, ty: impl Into<String>) -> Self {
Self {
name: name.into(),
ty: ty.into(),
uparams: Vec::new(),
}
}
pub fn with_uparam(mut self, u: impl Into<String>) -> Self {
self.uparams.push(u.into());
self
}
pub fn is_universe_poly(&self) -> bool {
!self.uparams.is_empty()
}
}
#[allow(dead_code)]
pub struct DiagMeta {
pub(super) entries: Vec<(String, String)>,
}
#[allow(dead_code)]
impl DiagMeta {
pub fn new() -> Self {
Self {
entries: Vec::new(),
}
}
pub fn add(&mut self, key: impl Into<String>, val: impl Into<String>) {
self.entries.push((key.into(), val.into()));
}
pub fn get(&self, key: &str) -> Option<&str> {
self.entries
.iter()
.find(|(k, _)| k == key)
.map(|(_, v)| v.as_str())
}
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
}