use std::marker::PhantomData;
use std::sync::atomic;
use std::sync::atomic::AtomicU64;
use crate::VarNo;
pub fn new_substitution_id() -> u32 {
static ID: AtomicU64 = AtomicU64::new(0);
let id = ID.fetch_add(1, atomic::Ordering::Relaxed);
if id > u32::MAX as u64 {
panic!(
"Too many `Substitution` structs\n\
\n\
To allow caching across multiple results, we assign each \
substitution a unique 32 bit integer. Now, so many substitutions \
have been created that we cannot guarantee uniqueness anymore."
);
}
id as u32
}
pub trait Substitution {
type Replacement;
fn id(&self) -> u32;
fn pairs(&self) -> impl ExactSizeIterator<Item = (VarNo, Self::Replacement)>;
#[inline]
fn map<V, R, F>(&self, f: F) -> MapSubst<'_, Self, F>
where
F: Fn((VarNo, Self::Replacement)) -> (V, R),
{
MapSubst { inner: self, f }
}
}
impl<T: Substitution> Substitution for &T {
type Replacement = T::Replacement;
#[inline]
fn id(&self) -> u32 {
(*self).id()
}
#[inline]
fn pairs(&self) -> impl ExactSizeIterator<Item = (VarNo, Self::Replacement)> {
(*self).pairs()
}
}
#[derive(Debug)]
pub struct Subst<F, VS = Vec<VarNo>, RS = Vec<F>> {
id: u32,
vars: VS,
replacements: RS,
phantom: PhantomData<fn(&F)>,
}
impl<F, VS: Copy, RS: Copy> Copy for Subst<F, VS, RS> {}
impl<F, VS: Clone, RS: Clone> Clone for Subst<F, VS, RS> {
fn clone(&self) -> Self {
Self {
id: self.id,
vars: self.vars.clone(),
replacements: self.replacements.clone(),
phantom: PhantomData,
}
}
}
impl<'a, F, VS: AsRef<[VarNo]>, RS: AsRef<[F]>> Substitution for &'a Subst<F, VS, RS> {
type Replacement = &'a F;
#[inline]
fn id(&self) -> u32 {
self.id
}
#[inline]
fn pairs(&self) -> impl ExactSizeIterator<Item = (VarNo, Self::Replacement)> {
self.vars
.as_ref()
.iter()
.copied()
.zip(self.replacements.as_ref())
}
}
impl<F, VS: AsRef<[VarNo]>, RS: AsRef<[F]>> Subst<F, VS, RS> {
#[track_caller]
pub fn new(vars: VS, replacements: RS) -> Self {
assert_eq!(
vars.as_ref().len(),
replacements.as_ref().len(),
"`vars` and `replacements` must have the same length"
);
Self {
id: new_substitution_id(),
vars,
replacements,
phantom: PhantomData,
}
}
}
#[derive(Clone, Copy, Debug)]
pub struct MapSubst<'a, S: ?Sized, F> {
inner: &'a S,
f: F,
}
impl<R, S: Substitution + ?Sized, F: Fn((VarNo, S::Replacement)) -> (VarNo, R)> Substitution
for MapSubst<'_, S, F>
{
type Replacement = R;
#[inline]
fn id(&self) -> u32 {
self.inner.id()
}
#[inline]
fn pairs(&self) -> impl ExactSizeIterator<Item = (VarNo, Self::Replacement)> {
self.inner.pairs().map(&self.f)
}
}