use std::{collections::hash_map::Entry, fmt::Display};
use ahash::HashMap;
use crate::{
Interner, NameId, SolvableId,
internal::{
arena::ArenaId,
id::{SolvableOrRootId, VariableId},
},
};
pub struct VariableMap {
next_id: usize,
solvable_to_variable: HashMap<SolvableId, VariableId>,
origins: HashMap<VariableId, VariableOrigin>,
}
#[derive(Clone, Debug)]
pub enum VariableOrigin {
Root,
Solvable(SolvableId),
ForbidMultiple(NameId),
AtLeastOne(NameId),
}
impl Default for VariableMap {
fn default() -> Self {
let mut origins = HashMap::default();
origins.insert(VariableId::root(), VariableOrigin::Root);
Self {
next_id: 1, solvable_to_variable: HashMap::default(),
origins,
}
}
}
impl VariableMap {
pub fn intern_solvable(&mut self, solvable_id: SolvableId) -> VariableId {
match self.solvable_to_variable.entry(solvable_id) {
Entry::Occupied(entry) => *entry.get(),
Entry::Vacant(entry) => {
let id = self.next_id;
self.next_id += 1;
let variable_id = VariableId::from_usize(id);
entry.insert(variable_id);
self.origins
.insert(variable_id, VariableOrigin::Solvable(solvable_id));
variable_id
}
}
}
#[cfg(feature = "diagnostics")]
pub fn count(&self) -> usize {
self.next_id
}
#[cfg(feature = "diagnostics")]
pub fn size_in_bytes(&self) -> usize {
self.origins.capacity() * std::mem::size_of::<(VariableId, VariableOrigin)>()
+ self.solvable_to_variable.capacity() * std::mem::size_of::<(SolvableId, VariableId)>()
}
pub fn intern_solvable_or_root(&mut self, solvable_or_root_id: SolvableOrRootId) -> VariableId {
match solvable_or_root_id.solvable() {
Some(solvable_id) => self.intern_solvable(solvable_id),
None => VariableId::root(),
}
}
pub fn alloc_forbid_multiple_variable(&mut self, name: NameId) -> VariableId {
let id = self.next_id;
self.next_id += 1;
let variable_id = VariableId::from_usize(id);
self.origins
.insert(variable_id, VariableOrigin::ForbidMultiple(name));
variable_id
}
pub fn alloc_at_least_one_variable(&mut self, name: NameId) -> VariableId {
let id = self.next_id;
self.next_id += 1;
let variable_id = VariableId::from_usize(id);
self.origins
.insert(variable_id, VariableOrigin::AtLeastOne(name));
variable_id
}
pub fn origin(&self, variable_id: VariableId) -> VariableOrigin {
self.origins[&variable_id].clone()
}
}
impl VariableId {
pub fn as_solvable(self, variable_map: &VariableMap) -> Option<SolvableId> {
variable_map.origin(self).as_solvable()
}
pub fn as_solvable_or_root(self, variable_map: &VariableMap) -> Option<SolvableOrRootId> {
variable_map.origin(self).as_solvable_or_root()
}
pub fn display<'i, I: Interner>(
self,
variable_map: &'i VariableMap,
interner: &'i I,
) -> VariableDisplay<'i, I> {
VariableDisplay {
variable: self,
interner,
variable_map,
}
}
}
pub struct VariableDisplay<'i, I: Interner> {
variable: VariableId,
interner: &'i I,
variable_map: &'i VariableMap,
}
impl<I: Interner> Display for VariableDisplay<'_, I> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.variable_map.origin(self.variable) {
VariableOrigin::Root => write!(f, "root"),
VariableOrigin::Solvable(solvable_id) => {
write!(f, "{}", self.interner.display_solvable(solvable_id))
}
VariableOrigin::ForbidMultiple(name) => {
write!(f, "forbid-multiple({})", self.interner.display_name(name))
}
VariableOrigin::AtLeastOne(name) => {
write!(f, "any-of({})", self.interner.display_name(name))
}
}
}
}
impl VariableOrigin {
pub fn as_solvable(&self) -> Option<SolvableId> {
match self {
VariableOrigin::Solvable(solvable_id) => Some(*solvable_id),
_ => None,
}
}
pub fn as_solvable_or_root(&self) -> Option<SolvableOrRootId> {
match self {
VariableOrigin::Solvable(solvable_id) => Some((*solvable_id).into()),
VariableOrigin::Root => Some(SolvableOrRootId::root()),
_ => None,
}
}
}