1
use std::collections::HashSet;
2
use std::fmt;
3
use std::io;
4
use std::io::Write;
5

            
6
use itertools::Itertools;
7

            
8
use crate::Data;
9
use crate::Ldd;
10
use crate::Storage;
11
use crate::iterators::*;
12

            
13
/// Print the vector set represented by the LDD.
14
1000
pub fn fmt_node<'a>(storage: &'a Storage, ldd: &Ldd) -> LddDisplay<'a> {
15
1000
    LddDisplay {
16
1000
        storage,
17
1000
        ldd: ldd.clone(),
18
1000
    }
19
1000
}
20

            
21
/// Prints the given LDD is the 'dot' format, which can be converted into an image using 'GraphViz'.
22
pub fn print_dot(storage: &Storage, f: &mut impl Write, ldd: &Ldd) -> io::Result<()> {
23
    write!(
24
        f,
25
        r#"
26
digraph "DD" {{
27
graph [dpi = 300];
28
center = true;
29
edge [dir = forward];
30

            
31
"#
32
    )?;
33

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

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

            
49
    writeln!(f, "}}")
50
}
51

            
52
pub struct LddDisplay<'a> {
53
    storage: &'a Storage,
54
    ldd: Ldd,
55
}
56

            
57
impl fmt::Display for LddDisplay<'_> {
58
1000
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59
1000
        writeln!(f, "{{")?;
60
1000
        print(self.storage, &self.ldd, f)?;
61
1000
        write!(f, "}}")
62
1000
    }
63
}
64

            
65
/// Print the vector set represented by the LDD.
66
1000
fn print(storage: &Storage, ldd: &Ldd, f: &mut fmt::Formatter<'_>) -> fmt::Result {
67
12310
    for vector in iter(storage, ldd) {
68
12310
        writeln!(f, "[{}]", vector.iter().format(" "))?;
69
    }
70

            
71
1000
    Ok(())
72
1000
}
73

            
74
#[allow(clippy::mutable_key_type)]
75
fn print_node(storage: &Storage, f: &mut impl Write, marked: &mut HashSet<Ldd>, ldd: &Ldd) -> io::Result<()> {
76
    if marked.contains(ldd) || ldd == storage.empty_set() || ldd == storage.empty_vector() {
77
        Ok(())
78
    } else {
79
        // Print the node values
80
        write!(f, "{} [shape=record, label=\"", ldd.index())?;
81

            
82
        let mut first = true;
83
        for Data(value, _, _) in iter_right(storage, ldd) {
84
            if !first {
85
                write!(f, "|")?;
86
            }
87

            
88
            write!(f, "<{value}> {value}")?;
89
            first = false;
90
        }
91
        writeln!(f, "\"];")?;
92

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

            
107
        // Print all nodes.
108
        for Data(_, down, _) in iter_right(storage, ldd) {
109
            print_node(storage, f, marked, &down)?;
110
        }
111

            
112
        Ok(())
113
    }
114
}