aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe/src/dependency_resolver/rule_watch_graph.rs
blob: 5428ca15027ca2d02518cff86f9ce39d686f3a46 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
//! ref: composer/src/Composer/DependencyResolver/RuleWatchGraph.php

use std::any::Any;

use indexmap::IndexMap;

use crate::dependency_resolver::decisions::Decisions;
use crate::dependency_resolver::multi_conflict_rule::MultiConflictRule;
use crate::dependency_resolver::rule::Rule;
use crate::dependency_resolver::rule_watch_chain::RuleWatchChain;
use crate::dependency_resolver::rule_watch_node::RuleWatchNode;

#[derive(Debug)]
pub struct RuleWatchGraph {
    pub(crate) watch_chains: IndexMap<i64, RuleWatchChain>,
}

impl RuleWatchGraph {
    pub fn new() -> Self {
        Self {
            watch_chains: IndexMap::new(),
        }
    }

    pub fn insert(&mut self, node: RuleWatchNode) {
        if node.get_rule().is_assertion() {
            return;
        }

        if (node.get_rule().as_any() as &dyn Any).downcast_ref::<MultiConflictRule>().is_none() {
            for literal in [node.watch1, node.watch2] {
                if !self.watch_chains.contains_key(&literal) {
                    self.watch_chains.insert(literal, RuleWatchChain::new());
                }
                self.watch_chains.get_mut(&literal).unwrap().unshift(node.clone());
            }
        } else {
            for literal in node.get_rule().get_literals() {
                if !self.watch_chains.contains_key(&literal) {
                    self.watch_chains.insert(literal, RuleWatchChain::new());
                }
                self.watch_chains.get_mut(&literal).unwrap().unshift(node.clone());
            }
        }
    }

    pub fn propagate_literal(&mut self, decided_literal: i64, level: i64, decisions: &mut Decisions) -> Option<Box<dyn Rule>> {
        let literal = -decided_literal;

        if !self.watch_chains.contains_key(&literal) {
            return None;
        }

        let chain = self.watch_chains.get_mut(&literal).unwrap();

        chain.rewind();
        while chain.valid() {
            let node = chain.current();
            if (node.get_rule().as_any() as &dyn Any).downcast_ref::<MultiConflictRule>().is_none() {
                let other_watch = node.get_other_watch(literal);

                if !node.get_rule().is_disabled() && !decisions.satisfy(other_watch) {
                    let rule_literals = node.get_rule().get_literals();

                    let alternative_literals: Vec<i64> = rule_literals.into_iter()
                        .filter(|&rule_literal| {
                            literal != rule_literal
                                && other_watch != rule_literal
                                && !decisions.conflict(rule_literal)
                        })
                        .collect();

                    if !alternative_literals.is_empty() {
                        let first_alternative = alternative_literals[0];
                        self.move_watch(literal, first_alternative, node);
                        continue;
                    }

                    if decisions.conflict(other_watch) {
                        return Some(chain.current().get_rule_boxed());
                    }

                    decisions.decide(other_watch, level, chain.current().get_rule_boxed());
                }
            } else {
                for other_literal in node.get_rule().get_literals() {
                    if literal != other_literal && !decisions.satisfy(other_literal) {
                        if decisions.conflict(other_literal) {
                            return Some(node.get_rule_boxed());
                        }

                        decisions.decide(other_literal, level, node.get_rule_boxed());
                    }
                }
            }

            chain.next();
        }

        None
    }

    pub(crate) fn move_watch(&mut self, from_literal: i64, to_literal: i64, node: RuleWatchNode) {
        if !self.watch_chains.contains_key(&to_literal) {
            self.watch_chains.insert(to_literal, RuleWatchChain::new());
        }

        node.move_watch(from_literal, to_literal);
        self.watch_chains.get_mut(&from_literal).unwrap().remove();
        self.watch_chains.get_mut(&to_literal).unwrap().unshift(node);
    }
}