// Copyright (c) 2021 Silvano DAL ZILIO // // MIT License package rudd import ( "fmt" "io" "sort" "text/tabwriter" ) var hsizes []string = []string{"k", "M", "G"} // humanSize returns a human-readable version of a size in bytes func humanSize(b int, unit uintptr) string { b = b * int(unit) if b < 0 { return "(error)" } if b < 1000 { return fmt.Sprintf("%d B", b) } c := 0 s := float64(b) / 1000.0 for { if s < 1000 { return fmt.Sprintf("%.1f %sB", s, hsizes[c]) } s = float64(s) / 1000.0 c++ if c >= len(hsizes) { return fmt.Sprintf("%.1f TB", s) } } } // Print writes a textual representation of the BDD with roots in n to an output // stream. The result is a table with rows giving the levels and ids of the // nodes and its low and high part. // // We print all the nodes in b if n is absent (len(n) == 0), so a call to // b.Print(os.Stdout) prints a table containing all the active nodes of the BDD // to the standard output. We also simply print the string "True" and "False", // respectively, if len(n) == 1 and n[0] is the constant True or False. func (b *BDD) Print(w io.Writer, n ...Node) { if mesg := b.Error(); mesg != "" { fmt.Fprintf(w, "Error: %s\n", mesg) return } if len(n) == 1 && n[0] != nil { if *n[0] == 0 { fmt.Fprintln(w, "False") return } if *n[0] == 1 { fmt.Fprintln(w, "True") return } } // we build a slice of nodes sorted by ids nodes := make([][4]int, 0) err := b.Allnodes(func(id, level, low, high int) error { i := sort.Search(len(nodes), func(i int) bool { return nodes[i][0] >= id }) nodes = append(nodes, [4]int{}) copy(nodes[i+1:], nodes[i:]) nodes[i] = [4]int{id, level, low, high} return nil }, n...) if err != nil { fmt.Fprintln(w, err.Error()) return } printSet(w, nodes) } func printSet(w io.Writer, nodes [][4]int) { tw := tabwriter.NewWriter(w, 0, 0, 0, ' ', 0) for _, n := range nodes { if n[0] > 1 { fmt.Fprintf(tw, "%d\t[%d\t] ? \t%d\t : %d\n", n[0], n[1], n[2], n[3]) } } tw.Flush() } // Dot writes a graph-like description of the BDD with roots in n to an output // stream using Graphviz's dot format. The behavior of Dot is very similar to // the one of Print. In particular, we include all the active nodes of b if n is // absent (len(n) == 0). func (b *BDD) Dot(w io.Writer, n ...Node) error { if mesg := b.Error(); mesg != "" { fmt.Fprintf(w, "Error: %s\n", mesg) return fmt.Errorf(mesg) } // we write the result by visiting each node but we never draw edges to the // False constant. fmt.Fprintln(w, "digraph G {") fmt.Fprintln(w, "1 [shape=box, label=\"1\", style=filled, shape=box, height=0.3, width=0.3];") _ = b.Allnodes(func(id, level, low, high int) error { if id > 1 { fmt.Fprintf(w, "%d %s\n", id, dotlabel(id, level)) if low != 0 { fmt.Fprintf(w, "%d -> %d [style=dotted];\n", id, low) } if high != 0 { fmt.Fprintf(w, "%d -> %d [style=filled];\n", id, high) } } return nil }, n...) fmt.Fprintln(w, "}") return nil } func dotlabel(a int, b int) string { return fmt.Sprintf(`[label=< %d [%d] >];`, b, a) }