use super::dist::SELECTOR_BASE;
use super::ir::Expr;
use super::priority::SOFT_BASE;
use super::registry::{Outcome, ProgBlock, Program};
use std::ops::Range;
#[derive(Clone, Debug, PartialEq, Eq, Default, serde::Serialize, serde::Deserialize)]
pub struct BlockItems {
pub dists: Range<usize>,
pub softs: Range<usize>,
pub before: Range<usize>,
pub real_conds: Range<usize>,
pub real_dists: Range<usize>,
pub real_before: Range<usize>,
pub disables: Range<usize>,
}
impl BlockItems {
pub(super) fn shifted(&self, by: &ItemOffsets) -> BlockItems {
let mv = |r: &Range<usize>, n: usize| r.start + n..r.end + n;
BlockItems {
dists: mv(&self.dists, by.dists),
softs: mv(&self.softs, by.softs),
before: mv(&self.before, by.before),
real_conds: mv(&self.real_conds, 0),
real_dists: mv(&self.real_dists, 0),
real_before: mv(&self.real_before, 0),
disables: mv(&self.disables, by.disables),
}
}
}
pub(super) struct ItemOffsets {
pub dists: usize,
pub softs: usize,
pub before: usize,
pub disables: usize,
}
fn covered<'a>(
len: usize,
ranges: impl Iterator<Item = &'a Range<usize>>,
) -> Result<Vec<bool>, Outcome> {
let mut gone = vec![false; len];
for r in ranges {
gone.get_mut(r.clone()).ok_or(Outcome::Failed)?.fill(true);
}
Ok(gone)
}
fn renumber(gone: &[bool]) -> Vec<Option<u32>> {
let mut next = 0u32;
gone.iter()
.map(|&g| {
(!g).then(|| {
next += 1;
next - 1
})
})
.collect()
}
fn keep<T: Clone>(items: &[T], gone: &[bool]) -> Vec<T> {
items
.iter()
.zip(gone)
.filter(|&(_, &g)| !g)
.map(|(x, _)| x.clone())
.collect()
}
pub(super) fn without_blocks(program: &Program, off: &[&ProgBlock]) -> Result<Program, Outcome> {
let gone_conj = covered(program.conjuncts.len(), off.iter().map(|b| &b.conjuncts))?;
let gone_dist = covered(program.dists.len(), off.iter().map(|b| &b.items.dists))?;
let gone_soft = covered(program.softs.len(), off.iter().map(|b| &b.items.softs))?;
let gone_before = covered(program.before.len(), off.iter().map(|b| &b.items.before))?;
let gone_rc = covered(
program.real.conds.len(),
off.iter().map(|b| &b.items.real_conds),
)?;
let gone_rd = covered(
program.real.dists.len(),
off.iter().map(|b| &b.items.real_dists),
)?;
let gone_rb = covered(
program.real.before.len(),
off.iter().map(|b| &b.items.real_before),
)?;
let gone_disable = covered(program.n_disables, off.iter().map(|b| &b.items.disables))?;
let conj_at = renumber(&gone_conj);
let dist_at = renumber(&gone_dist);
let soft_at = renumber(&gone_soft);
let mut dangling = false;
let mut conjuncts: Vec<Expr> = Vec::with_capacity(program.conjuncts.len());
for (c, _) in program
.conjuncts
.iter()
.zip(&gone_conj)
.filter(|&(_, &g)| !g)
{
conjuncts.push(c.substitute_vars(&mut |v, t| {
let (base, at) = if v >= SOFT_BASE {
(SOFT_BASE, &soft_at)
} else if v >= SELECTOR_BASE {
(SELECTOR_BASE, &dist_at)
} else {
return Expr::Var(v, t);
};
match at.get((v - base) as usize).copied().flatten() {
Some(n) => Expr::Var(base + n, t),
None => {
dangling = true;
Expr::Var(v, t)
}
}
}));
}
if dangling {
return Err(Outcome::Failed);
}
let mut out = program.clone();
out.conjuncts = conjuncts;
for v in &mut out.vars {
if let Some(d) = v.domain_conjunct {
v.domain_conjunct =
Some(conj_at.get(d).copied().flatten().ok_or(Outcome::Failed)? as usize);
}
}
out.before = keep(&program.before, &gone_before);
out.dists = keep(&program.dists, &gone_dist);
out.softs = Vec::with_capacity(program.softs.len());
for (s, _) in program.softs.iter().zip(&gone_soft).filter(|&(_, &g)| !g) {
let mut s = s.clone();
s.dists = s
.dists
.iter()
.map(|&d| dist_at.get(d as usize).copied().flatten())
.collect::<Option<_>>()
.ok_or(Outcome::Failed)?;
s.disabled = s
.disabled_by
.iter()
.any(|&d| gone_disable.get(d as usize) == Some(&false));
out.softs.push(s);
}
out.real.conds = keep(&program.real.conds, &gone_rc);
out.real.dists = keep(&program.real.dists, &gone_rd);
out.real.before = keep(&program.real.before, &gone_rb);
out.blocks = Vec::new();
Ok(out)
}