use crate::internal::assignment::Assignment::{self, Decision, Derivation};
use crate::internal::incompatibility::{Incompatibility, Relation};
use crate::internal::memory::Memory;
use crate::package::Package;
use crate::range::Range;
use crate::term::Term;
use crate::type_aliases::{Map, SelectedDependencies};
use crate::version::Version;
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub struct DecisionLevel(u32);
impl std::ops::Add<DecisionLevel> for DecisionLevel {
type Output = DecisionLevel;
fn add(self, other: DecisionLevel) -> DecisionLevel {
DecisionLevel(self.0 + other.0)
}
}
impl std::ops::SubAssign<DecisionLevel> for DecisionLevel {
fn sub_assign(&mut self, other: DecisionLevel) {
self.0 -= other.0
}
}
#[derive(Clone)]
pub struct DatedAssignment<P: Package, V: Version> {
decision_level: DecisionLevel,
assignment: Assignment<P, V>,
}
pub struct SatisfierAndPreviousHistory<'a, P: Package, V: Version> {
satisfier: DatedAssignment<P, V>,
previous_history: &'a [DatedAssignment<P, V>],
}
#[derive(Clone)]
pub struct PartialSolution<P: Package, V: Version> {
decision_level: DecisionLevel,
history: Vec<DatedAssignment<P, V>>,
memory: Memory<P, V>,
}
impl<P: Package, V: Version> PartialSolution<P, V> {
pub fn empty() -> Self {
Self {
decision_level: DecisionLevel(0),
history: Vec::new(),
memory: Memory::empty(),
}
}
fn add_assignment(&mut self, assignment: Assignment<P, V>) {
self.decision_level = match assignment {
Decision { .. } => self.decision_level + DecisionLevel(1),
Derivation { .. } => self.decision_level,
};
self.memory.add_assignment(&assignment);
self.history.push(DatedAssignment {
decision_level: self.decision_level,
assignment,
});
}
pub fn add_decision(&mut self, package: P, version: V) {
self.add_assignment(Decision { package, version });
}
pub fn add_derivation(&mut self, package: P, cause: Incompatibility<P, V>) {
self.add_assignment(Derivation { package, cause });
}
pub fn extract_solution(&self) -> Option<SelectedDependencies<P, V>> {
self.memory.extract_solution()
}
pub fn term_intersection_for_package(&mut self, package: &P) -> Option<&Term<V>> {
self.memory.term_intersection_for_package(package)
}
pub fn backtrack(&mut self, decision_level: DecisionLevel) {
let pos = self
.history
.iter()
.rposition(|dated_assignment| dated_assignment.decision_level == decision_level)
.unwrap_or(self.history.len() - 1);
*self = Self::from_assignments(
std::mem::take(&mut self.history)
.into_iter()
.take(pos + 1)
.map(|dated_assignment| dated_assignment.assignment),
);
}
fn from_assignments(assignments: impl Iterator<Item = Assignment<P, V>>) -> Self {
let mut partial_solution = Self::empty();
assignments.for_each(|a| partial_solution.add_assignment(a));
partial_solution
}
pub fn potential_packages(&mut self) -> Option<impl Iterator<Item = (&P, &Range<V>)>> {
let mut iter = self.memory.potential_packages().peekable();
if iter.peek().is_some() {
Some(iter)
} else {
None
}
}
pub fn add_version(
&mut self,
package: P,
version: V,
new_incompatibilities: &[Incompatibility<P, V>],
) {
let not_satisfied = |incompat: &Incompatibility<P, V>| {
incompat.relation(|p| {
if p == &package {
Some(Term::exact(version.clone()))
} else {
self.memory.term_intersection_for_package(p).cloned()
}
}) != Relation::Satisfied
};
if new_incompatibilities.iter().all(not_satisfied) {
self.add_decision(package, version);
}
}
pub fn relation(&mut self, incompat: &Incompatibility<P, V>) -> Relation<P, V> {
incompat.relation(|package| self.memory.term_intersection_for_package(package).cloned())
}
pub fn find_satisfier_and_previous_satisfier_level(
&self,
incompat: &Incompatibility<P, V>,
) -> (Assignment<P, V>, DecisionLevel, DecisionLevel) {
let SatisfierAndPreviousHistory {
satisfier,
previous_history,
} = Self::find_satisfier(incompat, self.history.as_slice())
.expect("We should always find a satisfier if called in the right context.");
let previous_satisfier_level =
Self::find_previous_satisfier(incompat, &satisfier.assignment, previous_history);
(
satisfier.assignment,
satisfier.decision_level,
previous_satisfier_level,
)
}
fn find_satisfier<'a>(
incompat: &Incompatibility<P, V>,
history: &'a [DatedAssignment<P, V>],
) -> Option<SatisfierAndPreviousHistory<'a, P, V>> {
Self::find_satisfier_helper(incompat, Self::new_accum_satisfied_from(incompat), history)
}
fn find_previous_satisfier<'a>(
incompat: &Incompatibility<P, V>,
satisfier: &Assignment<P, V>,
previous_assignments: &'a [DatedAssignment<P, V>],
) -> DecisionLevel {
let package = satisfier.package().clone();
let incompat_term = incompat.get(&package).expect("This should exist");
let satisfier_term = satisfier.as_term();
let is_satisfied = satisfier_term.subset_of(incompat_term);
let mut accum_satisfied = Self::new_accum_satisfied_from(incompat);
accum_satisfied.insert(package, (is_satisfied, satisfier_term));
Self::find_satisfier_helper(incompat, accum_satisfied, previous_assignments).map_or(
DecisionLevel(1),
|satisfier_and_previous_history| {
satisfier_and_previous_history
.satisfier
.decision_level
.max(DecisionLevel(1))
},
)
}
fn new_accum_satisfied_from(incompat: &Incompatibility<P, V>) -> Map<P, (bool, Term<V>)> {
incompat
.iter()
.map(|(p, _)| (p.clone(), (false, Term::any())))
.collect()
}
pub fn find_satisfier_helper<'a>(
incompat: &Incompatibility<P, V>,
accum_satisfied: Map<P, (bool, Term<V>)>,
all_assignments: &'a [DatedAssignment<P, V>],
) -> Option<SatisfierAndPreviousHistory<'a, P, V>> {
let mut accum_satisfied = accum_satisfied;
for (idx, dated_assignment) in all_assignments.iter().enumerate() {
let package = dated_assignment.assignment.package();
let incompat_term = match incompat.get(package) {
None => continue,
Some(i) => i,
};
let (is_satisfied, accum_term) = match accum_satisfied.get_mut(package) {
None => panic!("A package in incompat should always exist in accum_satisfied"),
Some((true, _)) => continue, Some(x) => x,
};
*accum_term = accum_term.intersection(&dated_assignment.assignment.as_term());
*is_satisfied = accum_term.subset_of(incompat_term);
if *is_satisfied && accum_satisfied.iter().all(|(_, (satisfied, _))| *satisfied) {
return Some(SatisfierAndPreviousHistory {
satisfier: dated_assignment.clone(),
previous_history: &all_assignments[0..idx],
});
}
}
None
}
}