1#![doc = document_features::document_features!()]
12#![forbid(unsafe_code)]
13#![warn(missing_docs)]
14#![allow(clippy::needless_lifetimes)]
16
17use oxidd_core::HasLevel;
18use oxidd_core::InnerNode;
19use oxidd_core::LevelNo;
20use oxidd_core::Manager;
21
22mod recursor;
23
24#[cfg(feature = "complement-edge")]
25pub mod complement_edge;
26#[cfg(feature = "simple")]
27pub mod simple;
28
29#[inline]
34fn set_pop<'a, M: Manager>(
35 manager: &'a M,
36 set: Borrowed<'a, M::Edge>,
37 until: LevelNo,
38) -> Borrowed<'a, M::Edge>
39where
40 M::InnerNode: HasLevel,
41{
42 match manager.get_node(&set) {
43 oxidd_core::Node::Inner(n) => {
44 if n.level() >= until {
45 set
46 } else {
47 set_pop(manager, n.child(0), until)
51 }
52 }
53 oxidd_core::Node::Terminal(_) => set,
54 }
55}
56
57#[cfg(feature = "statistics")]
60struct StatCounters {
61 calls: std::sync::atomic::AtomicI64,
62 cache_queries: std::sync::atomic::AtomicI64,
63 cache_hits: std::sync::atomic::AtomicI64,
64 reduced: std::sync::atomic::AtomicI64,
65}
66
67#[cfg(feature = "statistics")]
68impl StatCounters {
69 #[allow(clippy::declare_interior_mutable_const)]
70 const INIT: StatCounters = StatCounters {
71 calls: std::sync::atomic::AtomicI64::new(0),
72 cache_queries: std::sync::atomic::AtomicI64::new(0),
73 cache_hits: std::sync::atomic::AtomicI64::new(0),
74 reduced: std::sync::atomic::AtomicI64::new(0),
75 };
76
77 fn print<O: oxidd_core::Countable + std::fmt::Debug>(counters: &[Self]) {
78 for (i, ctrs) in counters.iter().enumerate() {
80 let calls = ctrs.calls.swap(0, std::sync::atomic::Ordering::Relaxed);
81 let cache_queries = ctrs
82 .cache_queries
83 .swap(0, std::sync::atomic::Ordering::Relaxed);
84 let cache_hits = ctrs
85 .cache_hits
86 .swap(0, std::sync::atomic::Ordering::Relaxed);
87 let reduced = ctrs.reduced.swap(0, std::sync::atomic::Ordering::Relaxed);
88
89 if calls == 0 {
90 continue;
91 }
92
93 let terminal_percent = (calls - cache_queries) as f32 / calls as f32 * 100.0;
94 let cache_hit_percent = cache_hits as f32 / cache_queries as f32 * 100.0;
95 let op = <O as oxidd_core::Countable>::from_usize(i);
96 eprintln!(" {op:?}: calls: {calls}, cache queries: {cache_queries} ({terminal_percent} % terminal cases), cache hits: {cache_hits} ({cache_hit_percent} %), reduced: {reduced}");
97 }
98 }
99}
100
101macro_rules! stat {
102 (call $op:expr) => {
103 let _ = $op as usize;
104 #[cfg(feature = "statistics")]
105 STAT_COUNTERS[$op as usize]
106 .calls
107 .fetch_add(1, ::std::sync::atomic::Ordering::Relaxed);
108 };
109 (cache_query $op:expr) => {
110 let _ = $op as usize;
111 #[cfg(feature = "statistics")]
112 STAT_COUNTERS[$op as usize]
113 .cache_queries
114 .fetch_add(1, ::std::sync::atomic::Ordering::Relaxed);
115 };
116 (cache_hit $op:expr) => {
117 let _ = $op as usize;
118 #[cfg(feature = "statistics")]
119 STAT_COUNTERS[$op as usize]
120 .cache_hits
121 .fetch_add(1, ::std::sync::atomic::Ordering::Relaxed);
122 };
123 (reduced $op:expr) => {
124 let _ = $op as usize;
125 #[cfg(feature = "statistics")]
126 STAT_COUNTERS[$op as usize]
127 .reduced
128 .fetch_add(1, ::std::sync::atomic::Ordering::Relaxed);
129 };
130}
131
132use oxidd_core::util::Borrowed;
133pub(crate) use stat;