use std::collections::HashMap;
use super::{FourStateValue, StateCellId};
pub type Site = (u64, u64);
struct Layer {
mask: Vec<u64>,
value: FourStateValue,
sites: Vec<Site>,
}
impl Layer {
fn new(width_words: usize, value: FourStateValue) -> Self {
Layer {
mask: vec![0; width_words],
value,
sites: Vec::new(),
}
}
fn is_empty(&self) -> bool {
self.mask.iter().all(|&m| m == 0)
}
}
struct Forced {
driven: FourStateValue,
restores: bool,
assign: Option<Layer>,
force: Option<Layer>,
}
impl Forced {
fn visible(&self) -> FourStateValue {
let mut v = self.driven.clone();
for layer in [&self.assign, &self.force].into_iter().flatten() {
v = merge(&v, &layer.value, &layer.mask);
}
v
}
}
#[derive(Default)]
pub struct ForceTable {
forced: HashMap<StateCellId, Forced>,
rows: HashMap<(StateCellId, u32), Forced>,
active_sites: HashMap<Site, usize>,
bypass: bool,
}
impl ForceTable {
pub(crate) fn filter(&mut self, cell: StateCellId, new: FourStateValue) -> FourStateValue {
if self.bypass {
return new;
}
match self.forced.get_mut(&cell) {
Some(e) => {
e.driven = new;
e.visible()
}
None => new,
}
}
pub(crate) fn filter_row(
&mut self,
cell: StateCellId,
at: u32,
new: FourStateValue,
) -> FourStateValue {
if self.bypass {
return new;
}
match self.rows.get_mut(&(cell, at)) {
Some(e) => {
e.driven = new;
e.visible()
}
None => new,
}
}
pub(crate) fn contains(&self, cell: StateCellId) -> bool {
self.forced.contains_key(&cell)
}
pub(crate) fn driven_value(&self, cell: StateCellId) -> Option<FourStateValue> {
self.forced.get(&cell).map(|e| e.driven.clone())
}
pub(crate) fn is_empty(&self) -> bool {
self.forced.is_empty() && self.rows.is_empty()
}
#[allow(clippy::too_many_arguments)]
pub fn force(
&mut self,
cell: StateCellId,
value: FourStateValue,
mask: Vec<u64>,
restores: bool,
site: Site,
current: FourStateValue,
procedural: bool,
) -> FourStateValue {
let e = self.forced.entry(cell).or_insert_with(|| Forced {
driven: current,
restores,
assign: None,
force: None,
});
apply_force(e, &mut self.active_sites, value, mask, site, procedural)
}
#[allow(clippy::too_many_arguments)]
pub fn force_row(
&mut self,
cell: StateCellId,
at: u32,
value: FourStateValue,
mask: Vec<u64>,
restores: bool,
site: Site,
current: FourStateValue,
procedural: bool,
) -> FourStateValue {
let e = self.rows.entry((cell, at)).or_insert_with(|| Forced {
driven: current,
restores,
assign: None,
force: None,
});
apply_force(e, &mut self.active_sites, value, mask, site, procedural)
}
pub fn release(
&mut self,
cell: StateCellId,
mask: &[u64],
procedural: bool,
) -> Option<FourStateValue> {
let e = self.forced.get_mut(&cell)?;
let out = apply_release(e, &mut self.active_sites, mask, procedural);
if e.assign.is_none() && e.force.is_none() {
self.forced.remove(&cell);
}
out
}
pub fn release_row(
&mut self,
cell: StateCellId,
at: u32,
mask: &[u64],
procedural: bool,
) -> Option<FourStateValue> {
let e = self.rows.get_mut(&(cell, at))?;
let out = apply_release(e, &mut self.active_sites, mask, procedural);
if e.assign.is_none() && e.force.is_none() {
self.rows.remove(&(cell, at));
}
out
}
pub fn site_active(&self, site: Site) -> bool {
self.active_sites.contains_key(&site)
}
pub fn set_bypass(&mut self, on: bool) {
self.bypass = on;
}
}
fn apply_force(
e: &mut Forced,
active_sites: &mut HashMap<Site, usize>,
value: FourStateValue,
mask: Vec<u64>,
site: Site,
procedural: bool,
) -> FourStateValue {
let slot = if procedural {
&mut e.assign
} else {
&mut e.force
};
let layer = slot.get_or_insert_with(|| Layer::new(mask.len(), value.clone()));
layer.value = merge(&layer.value, &value, &mask);
for (m, n) in layer.mask.iter_mut().zip(&mask) {
*m |= n;
}
if !layer.sites.contains(&site) {
layer.sites.push(site);
*active_sites.entry(site).or_insert(0) += 1;
}
e.visible()
}
fn apply_release(
e: &mut Forced,
active_sites: &mut HashMap<Site, usize>,
mask: &[u64],
procedural: bool,
) -> Option<FourStateValue> {
let before = e.visible();
let slot = if procedural {
&mut e.assign
} else {
&mut e.force
};
let layer = slot.as_mut()?;
for (m, r) in layer.mask.iter_mut().zip(mask) {
*m &= !r;
}
let mut freed = Vec::new();
if layer.is_empty() {
freed = std::mem::take(&mut layer.sites);
*slot = None;
}
for site in freed {
if let Some(n) = active_sites.get_mut(&site) {
*n -= 1;
if *n == 0 {
active_sites.remove(&site);
}
}
}
if !e.restores {
e.driven = before.clone();
}
let after = e.visible();
(!same(&after, &before)).then_some(after)
}
fn same(a: &FourStateValue, b: &FourStateValue) -> bool {
match (a, b) {
(FourStateValue::Narrow(x), FourStateValue::Narrow(y)) => x == y,
(
FourStateValue::Wide { aval: xa, bval: xb },
FourStateValue::Wide { aval: ya, bval: yb },
) => xa == ya && xb == yb,
_ => false,
}
}
fn merge(base: &FourStateValue, forced: &FourStateValue, mask: &[u64]) -> FourStateValue {
match (base, forced) {
(FourStateValue::Narrow(b), FourStateValue::Narrow(v)) => {
let m = u128::from(mask.first().copied().unwrap_or(0));
let m = m | (m << 64);
FourStateValue::Narrow((b & !m) | (v & m))
}
(
FourStateValue::Wide { aval: ba, bval: bb },
FourStateValue::Wide { aval: fa, bval: fb },
) => {
let pick = |b: &[u64], f: &[u64]| -> Vec<u64> {
b.iter()
.enumerate()
.map(|(i, &w)| {
let m = mask.get(i).copied().unwrap_or(0);
(w & !m) | (f.get(i).copied().unwrap_or(0) & m)
})
.collect()
};
FourStateValue::Wide {
aval: pick(ba, fa),
bval: pick(bb, fb),
}
}
_ => base.clone(),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn forced_bits_win_and_a_released_net_returns_to_its_driven_value() {
let mut t = ForceTable::default();
let cell = StateCellId(3);
let now = FourStateValue::Narrow(0x5);
let written = t.force(
cell,
FourStateValue::Narrow(0xA0),
vec![0xF0],
true,
(9, 7),
now,
false,
);
assert!(matches!(written, FourStateValue::Narrow(0xA5)));
assert!(t.site_active((9, 7)));
assert!(
!t.site_active((8, 7)),
"another instance's statement is not in effect"
);
assert!(matches!(
t.filter(cell, FourStateValue::Narrow(0x33)),
FourStateValue::Narrow(0xA3)
));
assert!(matches!(
t.release(cell, &[0xF0], false),
Some(FourStateValue::Narrow(0x33))
));
assert!(!t.site_active((9, 7)));
assert!(matches!(
t.filter(cell, FourStateValue::Narrow(0x44)),
FourStateValue::Narrow(0x44)
));
}
#[test]
fn a_released_variable_keeps_its_value() {
let mut t = ForceTable::default();
let cell = StateCellId(4);
t.force(
cell,
FourStateValue::Narrow(9),
vec![0xF],
false,
(1, 1),
FourStateValue::Narrow(2),
false,
);
assert!(matches!(
t.filter(cell, FourStateValue::Narrow(7)),
FourStateValue::Narrow(9)
));
assert!(t.release(cell, &[0xF], false).is_none());
assert!(matches!(
t.filter(cell, FourStateValue::Narrow(7)),
FourStateValue::Narrow(7)
));
}
#[test]
fn force_overrides_assign_and_release_restores_it() {
let mut t = ForceTable::default();
let cell = StateCellId(5);
let n = |v: u128| FourStateValue::Narrow(v);
let assign = (1, 10);
let force = (1, 20);
assert!(matches!(
t.force(cell, n(3), vec![0xF], false, assign, n(1), true),
FourStateValue::Narrow(3)
));
assert!(
matches!(t.filter(cell, n(8)), FourStateValue::Narrow(3)),
"assign beats writes"
);
assert!(matches!(
t.force(cell, n(5), vec![0xF], false, force, n(3), false),
FourStateValue::Narrow(5)
));
assert!(matches!(
t.force(cell, n(6), vec![0xF], false, assign, n(5), true),
FourStateValue::Narrow(5)
));
assert!(
matches!(
t.release(cell, &[0xF], false),
Some(FourStateValue::Narrow(6))
),
"the assign shows through"
);
assert!(t.site_active(assign) && !t.site_active(force));
t.force(cell, n(9), vec![0xF], false, force, n(6), false);
assert!(t.release(cell, &[0xF], true).is_none());
assert!(t.release(cell, &[0xF], false).is_none(), "keeps 9");
assert!(
matches!(t.filter(cell, n(2)), FourStateValue::Narrow(2)),
"nothing holds it now"
);
}
}