use core::num::NonZeroU8;
use midenc_hir::{self as hir, FxHashMap, hashbrown};
use smallvec::SmallVec;
use super::{SolverError, SolverOptions, Stack, ValueOrAlias};
use crate::{Constraint, OperandStack};
#[derive(Debug)]
pub struct SolverContext {
options: SolverOptions,
stack: Stack,
expected: Stack,
copies: CopyInfo,
}
impl SolverContext {
pub fn new(
expected: &[hir::ValueRef],
constraints: &[Constraint],
stack: &OperandStack,
options: SolverOptions,
) -> Result<Self, SolverError> {
let stack = Stack::from(stack);
let mut expected_output = Stack::default();
let mut copies = CopyInfo::default();
for (value, constraint) in expected.iter().rev().zip(constraints.iter().rev()) {
let value = ValueOrAlias::from(*value);
match constraint {
Constraint::Move => {
expected_output.push(value);
}
Constraint::Copy => {
expected_output.push(copies.push(value));
}
}
}
let context = Self {
options,
stack,
expected: expected_output,
copies,
};
let is_solved = !context.copies.copies_required()
&& context.stack.len() >= context.expected.len()
&& context.is_solved(&context.stack);
if is_solved {
return Err(SolverError::AlreadySolved);
}
Ok(context)
}
#[allow(unused)]
#[inline(always)]
pub fn options(&self) -> &SolverOptions {
&self.options
}
#[inline]
pub fn arity(&self) -> usize {
self.expected.len()
}
#[inline(always)]
pub fn copies(&self) -> &CopyInfo {
&self.copies
}
#[inline(always)]
pub fn stack(&self) -> &Stack {
&self.stack
}
#[inline(always)]
pub fn expected(&self) -> &Stack {
&self.expected
}
#[inline(always)]
pub fn is_strict(&self) -> bool {
self.options.strict
}
pub fn is_solved(&self, pending: &Stack) -> bool {
debug_assert!(pending.len() >= self.expected.len());
let is_strict_solution =
self.expected.iter().rev().eq(pending.iter().rev().take(self.expected.len()));
is_strict_solution || (!self.is_strict() && self.is_non_strict_solution(pending))
}
fn is_non_strict_solution(&self, pending: &Stack) -> bool {
let mut expected = self.expected.iter().rev().collect::<SmallVec<[_; 4]>>();
let mut pending = pending.iter().rev().take(expected.len()).collect::<SmallVec<[_; 4]>>();
expected.sort();
pending.sort();
expected == pending
}
}
#[derive(Debug, Default)]
pub struct CopyInfo {
copies: FxHashMap<ValueOrAlias, u8>,
num_copies: u8,
}
impl CopyInfo {
#[inline(always)]
pub fn len(&self) -> usize {
self.num_copies as usize
}
#[inline(always)]
pub fn is_empty(&self) -> bool {
self.num_copies == 0
}
pub fn push(&mut self, value: ValueOrAlias) -> ValueOrAlias {
use hashbrown::hash_map::Entry;
assert!(!value.is_alias());
self.num_copies += 1;
match self.copies.entry(value) {
Entry::Vacant(entry) => {
entry.insert(1);
value.copy(unsafe { NonZeroU8::new_unchecked(1) })
}
Entry::Occupied(mut entry) => {
let next_id = entry.get_mut();
*next_id += 1;
value.copy(unsafe { NonZeroU8::new_unchecked(*next_id) })
}
}
}
pub fn has_copies(&self, value: &ValueOrAlias) -> bool {
let value = value.unaliased();
self.copies.get(&value).map(|count| *count > 0).unwrap_or(false)
}
pub fn copies_required(&self) -> bool {
self.copies.values().any(|count| *count > 0)
}
}