1
use itertools::Itertools;
2
use streaming_iterator::StreamingIterator;
3
use std::collections::HashSet;
4
use std::fmt;
5

            
6
use crate::Data;
7
use crate::Ldd;
8
use crate::Storage;
9
use crate::iterators::*;
10

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

            
17
impl<'a> LddDot<'a> {
18
    pub fn new(storage: &'a Storage, ldd: &'a Ldd) -> LddDot<'a> {
19
        LddDot { storage, 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<Ldd> = 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 == self.storage.empty_set() {
44
            writeln!(f, "0 [shape=record, label=\"False\"];")?;
45
        } else if self.ldd == self.storage.empty_vector() {
46
            writeln!(f, "1 [shape=record, label=\"True\"];")?;
47
        } else {
48
            print_node(self.storage, 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
    storage: &'a Storage,
58
    ldd: &'a Ldd,
59
}
60

            
61
impl<'a> LddDisplay<'a> {
62
1500
    pub fn new(storage: &'a Storage, ldd: &'a Ldd) -> LddDisplay<'a> {
63
1500
        LddDisplay { storage, ldd }
64
1500
    }
65
}
66

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

            
71
1500
        let mut iter = iter(self.storage, self.ldd);
72
38269
        while let Some(vector) = iter.next() {
73
36769
            writeln!(f, "[{}]", vector.iter().format(" "))?;
74
        }
75
1500
        write!(f, "}}")
76
1500
    }
77
}
78

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

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

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

            
98
        // Print the edges.
99
        for Data(value, down, _) in iter_right(storage, ldd) {
100
            if down != *storage.empty_set() && down != *storage.empty_vector() {
101
                writeln!(
102
                    f,
103
                    "{}:{} -> {}:{};",
104
                    ldd.index(),
105
                    value,
106
                    down.index(),
107
                    storage.get_ref(&down).0
108
                )?;
109
            }
110
        }
111

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

            
117
        Ok(())
118
    }
119
}