espresso_logic/cover/
conversions.rs

1//! Trait implementations for Cover
2//!
3//! This module provides conversions and trait implementations for [`Cover`],
4//! including PLA I/O, Debug formatting, and conversions from expressions.
5
6use super::cubes::{Cube, CubeType};
7use super::labels::LabelManager;
8use super::Cover;
9use super::CoverType;
10use std::collections::BTreeSet;
11use std::fmt;
12use std::sync::Arc;
13
14// Implement PLASerialisable for Cover (used for PLA I/O)
15impl super::pla::PLASerialisable for Cover {
16    type CubesIter<'a> = std::slice::Iter<'a, Cube>;
17
18    fn num_inputs(&self) -> usize {
19        self.num_inputs
20    }
21
22    fn num_outputs(&self) -> usize {
23        self.num_outputs
24    }
25
26    fn internal_cubes_iter(&self) -> Self::CubesIter<'_> {
27        self.cubes.iter()
28    }
29
30    fn get_input_labels(&self) -> Option<&[Arc<str>]> {
31        if self.input_labels.is_empty() {
32            None
33        } else {
34            Some(self.input_labels.as_slice())
35        }
36    }
37
38    fn get_output_labels(&self) -> Option<&[Arc<str>]> {
39        if self.output_labels.is_empty() {
40            None
41        } else {
42            Some(self.output_labels.as_slice())
43        }
44    }
45
46    fn create_from_pla_parts(
47        num_inputs: usize,
48        num_outputs: usize,
49        input_labels: Vec<Arc<str>>,
50        output_labels: Vec<Arc<str>>,
51        cubes: Vec<Cube>,
52        cover_type: CoverType,
53    ) -> Self {
54        Cover {
55            num_inputs,
56            num_outputs,
57            input_labels: LabelManager::from_labels(input_labels),
58            output_labels: LabelManager::from_labels(output_labels),
59            cubes,
60            cover_type,
61        }
62    }
63}
64
65/// Convert a `BoolExpr` into a `Cover` with a single output named "out"
66///
67/// This conversion uses the BDD representation for efficient DNF extraction.
68///
69/// # Examples
70///
71/// ```
72/// use espresso_logic::{BoolExpr, Cover};
73///
74/// let a = BoolExpr::variable("a");
75/// let b = BoolExpr::variable("b");
76/// let expr = a.and(&b);
77///
78/// let cover: Cover = expr.into();
79/// assert_eq!(cover.num_outputs(), 1);
80/// ```
81impl From<crate::expression::BoolExpr> for Cover {
82    fn from(expr: crate::expression::BoolExpr) -> Self {
83        let mut cover = Cover::new(CoverType::F);
84        cover
85            .add_expr(&expr, "out")
86            .expect("Adding expression to new cover should not fail");
87        cover
88    }
89}
90
91/// Convert a `&BoolExpr` into a `Cover` with a single output named "out"
92///
93/// This conversion extracts the cubes from the internal BDD representation without
94/// requiring ownership of the expression.
95///
96/// # Examples
97///
98/// ```
99/// use espresso_logic::{BoolExpr, Cover};
100///
101/// let a = BoolExpr::variable("a");
102/// // Note: BoolExpr uses BDD internally (v3.1.1+)
103///
104/// let cover = Cover::from(&a);
105/// assert_eq!(cover.num_outputs(), 1);
106/// ```
107impl From<&crate::expression::BoolExpr> for Cover {
108    fn from(expr: &crate::expression::BoolExpr) -> Self {
109        // Convert to DNF
110        let dnf = crate::cover::dnf::Dnf::from(expr);
111        let cubes = dnf.cubes();
112
113        // Collect all variables from the DNF cubes
114        let mut all_vars = BTreeSet::new();
115        for product_term in cubes {
116            for var in product_term.keys() {
117                all_vars.insert(Arc::clone(var));
118            }
119        }
120
121        // Create cover with proper dimensions
122        let var_vec: Vec<Arc<str>> = all_vars.into_iter().collect();
123        let var_refs: Vec<&str> = var_vec.iter().map(|s| s.as_ref()).collect();
124        let mut cover = Cover::with_labels(CoverType::F, &var_refs, &["out"]);
125
126        // Add cubes to cover
127        for product_term in cubes {
128            let mut inputs = vec![None; cover.num_inputs()];
129            for (var, &polarity) in product_term {
130                if let Some(idx) = cover.input_labels.find_position(var) {
131                    inputs[idx] = Some(polarity);
132                }
133            }
134
135            let outputs = vec![true; cover.num_outputs()];
136            cover.cubes.push(Cube::new(&inputs, &outputs, CubeType::F));
137        }
138
139        cover
140    }
141}
142
143impl fmt::Debug for Cover {
144    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
145        f.debug_struct("Cover")
146            .field("num_inputs", &self.num_inputs)
147            .field("num_outputs", &self.num_outputs)
148            .field("cover_type", &self.cover_type)
149            .field("num_cubes", &self.num_cubes())
150            .field("input_labels", &self.input_labels)
151            .field("output_labels", &self.output_labels)
152            .finish()
153    }
154}