1use std::num::NonZeroUsize;
2
3#[derive(Debug, Clone, PartialEq, Eq)]
5pub struct DdminInput<C> {
6 candidates: Vec<C>,
7 options: DdminOptions,
8}
9
10impl<C> DdminInput<C> {
11 #[must_use]
13 pub const fn new(candidates: Vec<C>, options: DdminOptions) -> Self {
14 Self { candidates, options }
15 }
16
17 pub(crate) const fn options(&self) -> DdminOptions {
18 self.options
19 }
20
21 pub(crate) fn into_candidates(self) -> Vec<C> {
22 self.candidates
23 }
24}
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28pub struct DdminOptions {
29 initial_granularity: NonZeroUsize,
30 max_oracle_calls: Option<usize>,
31}
32
33impl DdminOptions {
34 #[must_use]
40 pub const fn new(initial_granularity: NonZeroUsize, max_oracle_calls: Option<usize>) -> Self {
41 Self { initial_granularity, max_oracle_calls }
42 }
43
44 #[must_use]
46 pub const fn initial_granularity(self) -> NonZeroUsize {
47 self.initial_granularity
48 }
49
50 #[must_use]
52 pub const fn max_oracle_calls(self) -> Option<usize> {
53 self.max_oracle_calls
54 }
55}
56
57impl Default for DdminOptions {
58 fn default() -> Self {
59 Self { initial_granularity: NonZeroUsize::MIN, max_oracle_calls: None }
60 }
61}
62
63#[derive(Debug, Clone, PartialEq, Eq)]
65pub enum OracleOutcome {
66 Interesting,
68 NotInteresting,
70 Unresolved(UnresolvedReason),
72}
73
74#[derive(Debug, Clone, Copy, PartialEq, Eq)]
76pub enum UnresolvedReason {
77 Timeout,
79 MaterializationFailed,
81 OracleFailed,
83 InvalidCandidateSet,
85 NonDeterministic,
87}
88
89pub trait DdminOracle<C> {
91 fn evaluate(&mut self, remaining: &[C]) -> OracleOutcome;
95}
96
97impl<C, F> DdminOracle<C> for F
98where
99 F: FnMut(&[C]) -> OracleOutcome,
100{
101 fn evaluate(&mut self, remaining: &[C]) -> OracleOutcome {
102 self(remaining)
103 }
104}
105
106#[derive(Debug, Clone, PartialEq, Eq)]
108pub struct DdminOutput<C> {
109 remaining: Vec<C>,
110 removed: Vec<C>,
111 stats: DdminStats,
112 guarantee: DdminGuarantee,
113}
114
115impl<C> DdminOutput<C> {
116 pub(crate) const fn new(
117 remaining: Vec<C>,
118 removed: Vec<C>,
119 stats: DdminStats,
120 guarantee: DdminGuarantee,
121 ) -> Self {
122 Self { remaining, removed, stats, guarantee }
123 }
124
125 #[must_use]
127 pub fn remaining(&self) -> &[C] {
128 &self.remaining
129 }
130
131 #[must_use]
133 pub fn removed(&self) -> &[C] {
134 &self.removed
135 }
136
137 #[must_use]
139 pub const fn stats(&self) -> DdminStats {
140 self.stats
141 }
142
143 #[must_use]
145 pub const fn guarantee(&self) -> DdminGuarantee {
146 self.guarantee
147 }
148}
149
150#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
152pub struct DdminStats {
153 oracle_calls: usize,
154 interesting_trials: usize,
155 not_interesting_trials: usize,
156 unresolved_trials: usize,
157}
158
159impl DdminStats {
160 pub(crate) const fn record(&mut self, outcome: &OracleOutcome) {
161 self.oracle_calls = self.oracle_calls.saturating_add(1);
162 match outcome {
163 OracleOutcome::Interesting => {
164 self.interesting_trials = self.interesting_trials.saturating_add(1);
165 }
166 OracleOutcome::NotInteresting => {
167 self.not_interesting_trials = self.not_interesting_trials.saturating_add(1);
168 }
169 OracleOutcome::Unresolved(_) => {
170 self.unresolved_trials = self.unresolved_trials.saturating_add(1);
171 }
172 }
173 }
174
175 #[must_use]
177 pub const fn oracle_calls(self) -> usize {
178 self.oracle_calls
179 }
180
181 #[must_use]
183 pub const fn interesting_trials(self) -> usize {
184 self.interesting_trials
185 }
186
187 #[must_use]
189 pub const fn not_interesting_trials(self) -> usize {
190 self.not_interesting_trials
191 }
192
193 #[must_use]
195 pub const fn unresolved_trials(self) -> usize {
196 self.unresolved_trials
197 }
198}
199
200#[derive(Debug, Clone, Copy, PartialEq, Eq)]
202pub enum DdminGuarantee {
203 OneMinimalWithinCandidateSet,
205 Incomplete(DdminStopReason),
207}
208
209#[derive(Debug, Clone, Copy, PartialEq, Eq)]
211pub enum DdminStopReason {
212 MaxOracleCallsReached,
214 BaselineNotInteresting,
216}