1
use std::collections::HashSet;
2
use std::fmt;
3

            
4
use itertools::Itertools;
5
use oxidd::ldd::LDDFunction;
6
use streaming_iterator::StreamingIterator;
7

            
8
use crate::Data;
9
use crate::iter;
10
use crate::iter_right;
11

            
12
/// Helper struct for displaying LDDs in DOT format.
13
pub struct LddDot<'a> {
14
    ldd: &'a LDDFunction,
15
}
16

            
17
impl<'a> LddDot<'a> {
18
    pub fn new(ldd: &'a LDDFunction) -> LddDot<'a> {
19
        LddDot { ldd }
20
    }
21
}
22

            
23
impl fmt::Display for LddDot<'_> {
24
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
25
        write!(
26
            f,
27
            r#"
28
    digraph "DD" {{
29
    graph [dpi = 300];
30
    center = true;
31
    edge [dir = forward];
32

            
33
    "#
34
        )?;
35

            
36
        // Every node must be printed once, so keep track of already printed ones.
37
        #[allow(clippy::mutable_key_type)]
38
        let mut marked: HashSet<LDDFunction> = HashSet::new();
39

            
40
        // We don't show these nodes in the output since every right most node is 'false' and every bottom node is 'true'.
41
        // or in our terms empty_set and empty_vector. However, if the LDD itself is 'false' or 'true' we just show the single
42
        // node for clarity.
43
        if self.ldd.is_empty() {
44
            writeln!(f, "0 [shape=record, label=\"False\"];")?;
45
        } else if self.ldd.is_empty_vector() {
46
            writeln!(f, "1 [shape=record, label=\"True\"];")?;
47
        } else {
48
            print_node(f, &mut marked, self.ldd)?;
49
        }
50

            
51
        writeln!(f, "}}")
52
    }
53
}
54

            
55
/// Helper struct for displaying LDDs.
56
pub struct LddDisplay<'a> {
57
    ldd: &'a LDDFunction,
58
}
59

            
60
impl<'a> LddDisplay<'a> {
61
300
    pub fn new(ldd: &'a LDDFunction) -> LddDisplay<'a> {
62
300
        LddDisplay { ldd }
63
300
    }
64
}
65

            
66
impl fmt::Display for LddDisplay<'_> {
67
300
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68
300
        writeln!(f, "{{")?;
69

            
70
300
        let mut iter = iter(self.ldd);
71
8937
        while let Some(vector) = iter.next() {
72
8637
            writeln!(f, "\t[{}]", vector.iter().format(" "))?;
73
        }
74
300
        write!(f, "}}")
75
300
    }
76
}
77

            
78
#[allow(clippy::mutable_key_type)]
79
fn print_node(f: &mut fmt::Formatter<'_>, marked: &mut HashSet<LDDFunction>, ldd: &LDDFunction) -> fmt::Result {
80
    if marked.contains(ldd) || ldd.is_empty() || ldd.is_empty_vector() {
81
        Ok(())
82
    } else {
83
        // Print the node values
84
        write!(f, "{} [shape=record, label=\"", ldd.id())?;
85

            
86
        let mut first = true;
87
        for Data(value, _, _) in iter_right(ldd) {
88
            if !first {
89
                write!(f, "|")?;
90
            }
91

            
92
            write!(f, "<{value}> {value}")?;
93
            first = false;
94
        }
95
        writeln!(f, "\"];")?;
96

            
97
        // Print the edges.
98
        for Data(value, down, _) in iter_right(ldd) {
99
            if !down.is_empty() && !down.is_empty_vector() {
100
                writeln!(
101
                    f,
102
                    "{}:{} -> {}:{};",
103
                    ldd.id(),
104
                    value,
105
                    down.id(),
106
                    down.node().expect("down is an inner node").0
107
                )?;
108
            }
109
        }
110

            
111
        // Print all nodes.
112
        for Data(_, down, _) in iter_right(ldd) {
113
            print_node(f, marked, &down)?;
114
        }
115

            
116
        Ok(())
117
    }
118
}