1use std::fmt;
7use std::io;
8use std::path::Path;
9use std::sync::Arc;
10
11use crate::pla::PLASerializable;
12use crate::EspressoConfig;
13
14pub type CubeIterator<'a> = Box<dyn Iterator<Item = (Vec<Option<bool>>, Vec<Option<bool>>)> + 'a>;
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub enum PLAType {
20 F = 1,
22 FD = 3,
24 FR = 5,
26 FDR = 7,
28}
29
30impl PLAType {
31 pub fn has_f(&self) -> bool {
33 matches!(self, PLAType::F | PLAType::FD | PLAType::FR | PLAType::FDR)
34 }
35
36 pub fn has_d(&self) -> bool {
38 matches!(self, PLAType::FD | PLAType::FDR)
39 }
40
41 pub fn has_r(&self) -> bool {
43 matches!(self, PLAType::FR | PLAType::FDR)
44 }
45}
46
47#[derive(Debug, Clone, Copy, PartialEq, Eq)]
49pub(crate) enum CubeType {
50 F, D, R, }
54
55#[derive(Clone, Debug)]
57pub struct Cube {
58 pub(crate) inputs: Arc<[Option<bool>]>,
59 pub(crate) outputs: Arc<[bool]>, pub(crate) cube_type: CubeType,
61}
62
63impl Cube {
64 pub(crate) fn new(inputs: Vec<Option<bool>>, outputs: Vec<bool>, cube_type: CubeType) -> Self {
65 Cube {
66 inputs: inputs.into(),
67 outputs: outputs.into(),
68 cube_type,
69 }
70 }
71}
72
73pub(crate) trait Minimizable: Send + Sync {
76 fn num_inputs(&self) -> usize;
78
79 fn num_outputs(&self) -> usize;
81
82 fn cover_type(&self) -> PLAType;
84
85 fn internal_cubes_iter<'a>(&'a self) -> Box<dyn Iterator<Item = &'a Cube> + 'a>;
87
88 fn set_cubes(&mut self, cubes: Vec<Cube>);
90}
91
92pub trait Cover: Send + Sync {
94 fn num_inputs(&self) -> usize;
96
97 fn num_outputs(&self) -> usize;
99
100 fn num_cubes(&self) -> usize;
102
103 fn cover_type(&self) -> PLAType;
105
106 fn cubes_iter<'a>(&'a self) -> CubeIterator<'a>;
109
110 fn minimize(&mut self) -> io::Result<()>;
112
113 fn minimize_with_config(&mut self, config: &EspressoConfig) -> io::Result<()>;
115
116 fn to_pla_string(&self, pla_type: PLAType) -> io::Result<String>;
118
119 fn to_pla_file<P: AsRef<Path>>(&self, path: P, pla_type: PLAType) -> io::Result<()>;
121}
122
123impl<T: Minimizable + PLASerializable> Cover for T {
125 fn num_inputs(&self) -> usize {
126 Minimizable::num_inputs(self)
127 }
128
129 fn num_outputs(&self) -> usize {
130 Minimizable::num_outputs(self)
131 }
132
133 fn num_cubes(&self) -> usize {
134 let cover_type = self.cover_type();
135 if cover_type.has_r() {
136 self.internal_cubes_iter().count()
137 } else {
138 self.internal_cubes_iter()
140 .filter(|cube| cube.cube_type == CubeType::F)
141 .count()
142 }
143 }
144
145 fn cover_type(&self) -> PLAType {
146 Minimizable::cover_type(self)
147 }
148
149 fn cubes_iter<'a>(&'a self) -> CubeIterator<'a> {
150 let cover_type = self.cover_type();
153 Box::new(
154 self.internal_cubes_iter()
155 .filter(move |cube| {
156 cover_type != PLAType::F || cube.cube_type == CubeType::F
158 })
159 .map(|cube| {
160 let inputs = cube.inputs.to_vec();
162 let outputs: Vec<Option<bool>> =
163 cube.outputs.iter().map(|&b| Some(b)).collect();
164 (inputs, outputs)
165 }),
166 )
167 }
168
169 fn minimize(&mut self) -> io::Result<()> {
170 let config = EspressoConfig::default();
171 self.minimize_with_config(&config)
172 }
173
174 fn minimize_with_config(&mut self, config: &EspressoConfig) -> io::Result<()> {
175 use crate::r#unsafe::{UnsafeCover, UnsafeEspresso};
176
177 let mut f_cubes = Vec::new();
179 let mut d_cubes = Vec::new();
180 let mut r_cubes = Vec::new();
181
182 for cube in self.internal_cubes_iter() {
183 let input_vec: Vec<u8> = cube
184 .inputs
185 .iter()
186 .map(|&opt| match opt {
187 Some(false) => 0,
188 Some(true) => 1,
189 None => 2,
190 })
191 .collect();
192
193 let output_vec: Vec<u8> = cube
195 .outputs
196 .iter()
197 .map(|&b| if b { 1 } else { 0 })
198 .collect();
199
200 match cube.cube_type {
202 CubeType::F => f_cubes.push((input_vec, output_vec)),
203 CubeType::D => d_cubes.push((input_vec, output_vec)),
204 CubeType::R => r_cubes.push((input_vec, output_vec)),
205 }
206 }
207
208 let mut esp =
210 UnsafeEspresso::new_with_config(self.num_inputs(), self.num_outputs(), config);
211
212 let f_cover = UnsafeCover::build_from_cubes(f_cubes, self.num_inputs(), self.num_outputs());
214 let d_cover = if !d_cubes.is_empty() {
215 Some(UnsafeCover::build_from_cubes(
216 d_cubes,
217 self.num_inputs(),
218 self.num_outputs(),
219 ))
220 } else {
221 None
222 };
223 let r_cover = if !r_cubes.is_empty() {
224 Some(UnsafeCover::build_from_cubes(
225 r_cubes,
226 self.num_inputs(),
227 self.num_outputs(),
228 ))
229 } else {
230 None
231 };
232
233 let (f_result, d_result, r_result) = esp.minimize(f_cover, d_cover, r_cover);
235
236 let mut all_cubes = Vec::new();
238 all_cubes.extend(f_result.to_cubes(self.num_inputs(), self.num_outputs(), CubeType::F));
239 all_cubes.extend(d_result.to_cubes(self.num_inputs(), self.num_outputs(), CubeType::D));
240 all_cubes.extend(r_result.to_cubes(self.num_inputs(), self.num_outputs(), CubeType::R));
241
242 self.set_cubes(all_cubes);
244 Ok(())
245 }
246
247 fn to_pla_string(&self, pla_type: PLAType) -> io::Result<String> {
248 PLASerializable::to_pla_string(self, pla_type)
249 }
250
251 fn to_pla_file<P: AsRef<Path>>(&self, path: P, pla_type: PLAType) -> io::Result<()> {
252 PLASerializable::to_pla_file(self, path, pla_type)
253 }
254}
255
256pub trait CoverTypeMarker: Send + Sync + Clone {
258 const PLA_TYPE: PLAType;
259}
260
261#[derive(Clone, Copy, Debug)]
263pub struct FType;
264impl CoverTypeMarker for FType {
265 const PLA_TYPE: PLAType = PLAType::F;
266}
267
268#[derive(Clone, Copy, Debug)]
270pub struct FDType;
271impl CoverTypeMarker for FDType {
272 const PLA_TYPE: PLAType = PLAType::FD;
273}
274
275#[derive(Clone, Copy, Debug)]
277pub struct FRType;
278impl CoverTypeMarker for FRType {
279 const PLA_TYPE: PLAType = PLAType::FR;
280}
281
282#[derive(Clone, Copy, Debug)]
284pub struct FDRType;
285impl CoverTypeMarker for FDRType {
286 const PLA_TYPE: PLAType = PLAType::FDR;
287}
288
289#[derive(Clone)]
322pub struct CoverBuilder<const INPUTS: usize, const OUTPUTS: usize, T: CoverTypeMarker = FDType> {
323 cubes: Vec<Cube>,
325 _marker: std::marker::PhantomData<T>,
326}
327
328impl<const INPUTS: usize, const OUTPUTS: usize, T: CoverTypeMarker>
329 CoverBuilder<INPUTS, OUTPUTS, T>
330{
331 pub fn new() -> Self {
333 CoverBuilder {
334 cubes: Vec::new(),
335 _marker: std::marker::PhantomData,
336 }
337 }
338
339 pub fn add_cube(
351 &mut self,
352 inputs: &[Option<bool>; INPUTS],
353 outputs: &[Option<bool>; OUTPUTS],
354 ) -> &mut Self {
355 let mut f_outputs = Vec::with_capacity(OUTPUTS);
358 let mut d_outputs = Vec::with_capacity(OUTPUTS);
359 let mut r_outputs = Vec::with_capacity(OUTPUTS);
360 let mut has_f = false;
361 let mut has_d = false;
362 let mut has_r = false;
363
364 let pla_type = T::PLA_TYPE;
365 for &out in outputs.iter() {
366 match out {
367 Some(true) if pla_type.has_f() => {
368 f_outputs.push(true);
370 d_outputs.push(false);
371 r_outputs.push(false);
372 has_f = true;
373 }
374 Some(false) if pla_type.has_r() => {
375 f_outputs.push(false);
377 d_outputs.push(false);
378 r_outputs.push(true);
379 has_r = true;
380 }
381 None if pla_type.has_d() => {
382 f_outputs.push(false);
384 d_outputs.push(true);
385 r_outputs.push(false);
386 has_d = true;
387 }
388 _ => {
389 f_outputs.push(false);
391 d_outputs.push(false);
392 r_outputs.push(false);
393 }
394 }
395 }
396
397 let inputs_vec = inputs.to_vec();
399 if has_f {
400 self.cubes
401 .push(Cube::new(inputs_vec.clone(), f_outputs, CubeType::F));
402 }
403 if has_d {
404 self.cubes
405 .push(Cube::new(inputs_vec.clone(), d_outputs, CubeType::D));
406 }
407 if has_r {
408 self.cubes
409 .push(Cube::new(inputs_vec, r_outputs, CubeType::R));
410 }
411
412 self
413 }
414}
415
416impl<const INPUTS: usize, const OUTPUTS: usize, T: CoverTypeMarker> Minimizable
418 for CoverBuilder<INPUTS, OUTPUTS, T>
419{
420 fn num_inputs(&self) -> usize {
421 INPUTS
422 }
423
424 fn num_outputs(&self) -> usize {
425 OUTPUTS
426 }
427
428 fn cover_type(&self) -> PLAType {
429 T::PLA_TYPE
430 }
431
432 fn internal_cubes_iter<'a>(&'a self) -> Box<dyn Iterator<Item = &'a Cube> + 'a> {
433 Box::new(self.cubes.iter())
434 }
435
436 fn set_cubes(&mut self, cubes: Vec<Cube>) {
437 let pla_type = T::PLA_TYPE;
439 self.cubes = cubes
440 .into_iter()
441 .filter(|cube| match cube.cube_type {
442 CubeType::F => pla_type.has_f(),
443 CubeType::D => pla_type.has_d(),
444 CubeType::R => pla_type.has_r(),
445 })
446 .collect();
447 }
448}
449
450impl<const INPUTS: usize, const OUTPUTS: usize, T: CoverTypeMarker> PLASerializable
452 for CoverBuilder<INPUTS, OUTPUTS, T>
453{
454}
455
456impl<const INPUTS: usize, const OUTPUTS: usize, T: CoverTypeMarker> Default
457 for CoverBuilder<INPUTS, OUTPUTS, T>
458{
459 fn default() -> Self {
460 Self::new()
461 }
462}
463
464impl<const INPUTS: usize, const OUTPUTS: usize, T: CoverTypeMarker> fmt::Debug
465 for CoverBuilder<INPUTS, OUTPUTS, T>
466{
467 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
468 f.debug_struct("CoverBuilder")
469 .field("inputs", &INPUTS)
470 .field("outputs", &OUTPUTS)
471 .field("cover_type", &T::PLA_TYPE)
472 .field("num_cubes", &self.num_cubes())
473 .finish()
474 }
475}