use crate::proof::{Proof, ProofNodeId, ProofStep};
#[cfg(feature = "arena")]
use bumpalo::Bump;
#[cfg(feature = "arena")]
use std::ptr::NonNull;
#[cfg(feature = "arena")]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ArenaProofStepId(pub u32);
#[derive(Debug, Default)]
pub struct Recorder {
proof: Proof,
#[cfg(feature = "arena")]
arena: Bump,
#[cfg(feature = "arena")]
arena_steps: Vec<NonNull<ProofStep>>,
}
impl Recorder {
#[must_use]
pub fn new() -> Self {
Self {
proof: Proof::new(),
#[cfg(feature = "arena")]
arena: Bump::new(),
#[cfg(feature = "arena")]
arena_steps: Vec::new(),
}
}
pub fn record_step(&mut self, step: ProofStep) -> ProofNodeId {
match step {
ProofStep::Axiom { conclusion } => self.proof.add_axiom(conclusion),
ProofStep::Inference {
rule,
premises,
conclusion,
args,
} => self.proof.add_inference_with_args(
rule,
premises.into_vec(),
args.into_vec(),
conclusion,
),
}
}
#[must_use]
pub const fn proof(&self) -> &Proof {
&self.proof
}
pub fn proof_mut(&mut self) -> &mut Proof {
&mut self.proof
}
#[must_use]
pub fn into_proof(self) -> Proof {
self.proof
}
pub fn clear(&mut self) {
self.proof.clear();
#[cfg(feature = "arena")]
{
self.arena.reset();
self.arena_steps.clear();
}
}
#[cfg(feature = "arena")]
pub fn record_step_arena(&mut self, step: ProofStep) -> Option<ArenaProofStepId> {
let index = u32::try_from(self.arena_steps.len()).ok()?;
let allocated = self.arena.alloc(step);
self.arena_steps.push(NonNull::from(allocated));
Some(ArenaProofStepId(index))
}
#[cfg(feature = "arena")]
#[must_use]
pub fn get(&self, id: ArenaProofStepId) -> Option<&ProofStep> {
self.arena_steps.get(id.0 as usize).map(|ptr| {
unsafe { ptr.as_ref() }
})
}
}
#[cfg(all(test, feature = "arena"))]
mod arena_tests {
use super::*;
use crate::validation::FormatValidator;
use smallvec::SmallVec;
#[test]
fn arena_recorded_proof_passes_checker() {
let mut recorder = Recorder::new();
let left = recorder.record_step(ProofStep::Axiom {
conclusion: "p".to_string(),
});
let right = recorder.record_step(ProofStep::Axiom {
conclusion: "q".to_string(),
});
let arena_id = recorder.record_step_arena(ProofStep::Inference {
rule: "and_intro".to_string(),
premises: SmallVec::from_vec(vec![left, right]),
conclusion: "(and p q)".to_string(),
args: SmallVec::new(),
});
assert!(arena_id.is_some());
recorder.record_step(ProofStep::Inference {
rule: "and_intro".to_string(),
premises: SmallVec::from_vec(vec![left, right]),
conclusion: "(and p q)".to_string(),
args: SmallVec::new(),
});
let validator = FormatValidator::new();
assert!(validator.validate_proof(recorder.proof()).is_ok());
}
#[test]
fn arena_step_returns_valid_id() {
let mut recorder = Recorder::new();
let maybe_step_id = recorder.record_step_arena(ProofStep::Axiom {
conclusion: "p".to_string(),
});
let Some(step_id) = maybe_step_id else {
panic!("arena should allocate a proof step id");
};
match recorder.get(step_id) {
Some(ProofStep::Axiom { conclusion }) => assert_eq!(conclusion, "p"),
_ => panic!("expected arena-recorded axiom"),
}
}
}