symbolic_sets_integers/
lib.rs

1// -*- coding: utf-8 -*-
2//-------------------------------------------------------------------------------------------------
3// SPDX-FileCopyrightText: © 2024 Walland Heavy Research
4// SPDX-License-Identifier: AGPL-3.0-only
5//-------------------------------------------------------------------------------------------------
6
7//! Provides examples and test cases for the [`symbolic-sets`][symbolic_sets] crate.
8
9use std::fmt::Debug;
10use std::fmt::Display;
11
12use bitvec::BitArr;
13use symbolic_sets::anf::AnfSet;
14use symbolic_sets::anf::Simplifiable;
15use symbolic_sets::anf::Simplification;
16use symbolic_sets::Property;
17
18// Note: ARRAY_SIZE isn't that large, so we could easily implement these sets exhaustively using a
19// bitvec.  But we are purposefully doing it via a variety of different Property implementations to
20// test that the ANF logic works as expected.
21
22/// The number of elements that can be stored in a test set
23const ARRAY_SIZE: usize = 1024;
24
25/// An [`AnfSet`] that can store integers up to a certain size
26pub type TestSet = AnfSet<TestProperty>;
27
28/// A [`BitArray`][bitvec::array::BitArray] that can exhaustively store all of the integers in a
29/// test set.
30pub type TestArray = BitArr!(for ARRAY_SIZE);
31
32/// An extension trait that adds useful methods to a [`TestSet`].
33pub trait TestSetExt {
34    /// Returns an exhaustive list of all of the elements in a test set as a [`TestArray`].
35    fn to_array(&self) -> TestArray;
36
37    /// Returns a test set containing only a single integer.
38    fn only(element: u16) -> TestSet;
39
40    /// Returns a test set containing all even integers.
41    fn is_even() -> TestSet;
42
43    /// Returns a test set containing all integers greater than a particular value.
44    fn greater_than(element: u16) -> TestSet;
45}
46
47impl TestSetExt for TestSet {
48    fn to_array(&self) -> TestArray {
49        let mut result = TestArray::ZERO;
50        if self.includes_universe() {
51            result = !result;
52        }
53        for clause in self.clauses() {
54            let mut clause_bits = !TestArray::ZERO;
55            for property in clause {
56                clause_bits &= property.to_array();
57            }
58            result ^= clause_bits;
59        }
60        result
61    }
62
63    fn only(element: u16) -> TestSet {
64        TestProperty::from(SingleNumber(element)).into()
65    }
66
67    fn is_even() -> TestSet {
68        TestProperty::from(IsEven).into()
69    }
70
71    fn greater_than(element: u16) -> TestSet {
72        TestProperty::from(GreaterThan(element)).into()
73    }
74}
75
76#[doc(hidden)]
77#[derive(Clone, Eq, Ord, PartialEq, PartialOrd)]
78pub enum TestProperty {
79    SingleNumber(SingleNumber),
80    IsEven(IsEven),
81    GreaterThan(GreaterThan),
82}
83
84impl TestProperty {
85    fn to_array(&self) -> TestArray {
86        match self {
87            TestProperty::SingleNumber(prop) => prop.to_array(),
88            TestProperty::IsEven(prop) => prop.to_array(),
89            TestProperty::GreaterThan(prop) => prop.to_array(),
90        }
91    }
92
93    fn contains(&self, element: u16) -> bool {
94        match self {
95            TestProperty::SingleNumber(prop) => prop.is_satisfied(element),
96            TestProperty::IsEven(prop) => prop.is_satisfied(element),
97            TestProperty::GreaterThan(prop) => prop.is_satisfied(element),
98        }
99    }
100}
101
102impl Debug for TestProperty {
103    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
104        match self {
105            TestProperty::SingleNumber(prop) => write!(f, "{:?}", prop),
106            TestProperty::IsEven(prop) => write!(f, "{:?}", prop),
107            TestProperty::GreaterThan(prop) => write!(f, "{:?}", prop),
108        }
109    }
110}
111
112impl Display for TestProperty {
113    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
114        match self {
115            TestProperty::SingleNumber(prop) => write!(f, "{}", prop),
116            TestProperty::IsEven(prop) => write!(f, "{}", prop),
117            TestProperty::GreaterThan(prop) => write!(f, "{}", prop),
118        }
119    }
120}
121
122impl Property for TestProperty {
123    type Element = u16;
124
125    fn is_satisfied(&self, element: &u16) -> bool {
126        self.contains(*element)
127    }
128}
129
130impl Simplifiable for TestProperty {
131    fn intersection(self, other: Self) -> Simplification<Self> {
132        if self < other {
133            self.intersection_ordered(other)
134        } else {
135            other.intersection_ordered(self)
136        }
137    }
138
139    fn symmetric_difference(self, other: Self) -> Simplification<Self> {
140        if self < other {
141            self.symmetric_difference_ordered(other)
142        } else {
143            other.symmetric_difference_ordered(self)
144        }
145    }
146}
147
148impl TestProperty {
149    fn intersection_ordered(self, other: Self) -> Simplification<Self> {
150        match self {
151            TestProperty::SingleNumber(prop) => prop.intersection_ordered(other),
152            TestProperty::IsEven(prop) => prop.intersection_ordered(other),
153            TestProperty::GreaterThan(prop) => prop.intersection_ordered(other),
154        }
155    }
156
157    fn symmetric_difference_ordered(self, other: Self) -> Simplification<Self> {
158        match self {
159            TestProperty::SingleNumber(prop) => prop.symmetric_difference_ordered(other),
160            TestProperty::IsEven(prop) => prop.symmetric_difference_ordered(other),
161            TestProperty::GreaterThan(prop) => prop.symmetric_difference_ordered(other),
162        }
163    }
164}
165
166//-------------------------------------------------------------------------------------------------
167// Property: Equals a single number
168
169#[doc(hidden)]
170#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
171pub struct SingleNumber(u16);
172
173impl From<SingleNumber> for TestProperty {
174    fn from(prop: SingleNumber) -> TestProperty {
175        TestProperty::SingleNumber(prop)
176    }
177}
178
179impl SingleNumber {
180    fn to_array(self) -> TestArray {
181        let mut result = TestArray::ZERO;
182        result.set(self.0 as usize, true);
183        result
184    }
185
186    fn is_satisfied(self, element: u16) -> bool {
187        self.0 == element
188    }
189
190    fn intersection_ordered(self, other: TestProperty) -> Simplification<TestProperty> {
191        // This handles all other property types correctly:
192        if other.contains(self.0) {
193            // (i ∩ A) == i where i ∈ A
194            return Simplification::Replace(self.into());
195        }
196        // (i ∩ A) == ∅ where i ∉ A
197        Simplification::Remove
198    }
199
200    fn symmetric_difference_ordered(self, other: TestProperty) -> Simplification<TestProperty> {
201        // (i ⊕ i) has already been handled by the caller
202        // (i ⊕ j) cannot be simplified
203        // (i ⊕ even) cannot be simplified
204        // (i ⊕ >j) cannot be simplified
205        Simplification::Keep(self.into(), other)
206    }
207}
208
209impl Display for SingleNumber {
210    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
211        write!(f, "{}", self.0)
212    }
213}
214
215//-------------------------------------------------------------------------------------------------
216// Property: Number is even
217
218#[doc(hidden)]
219#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
220pub struct IsEven;
221
222impl From<IsEven> for TestProperty {
223    fn from(prop: IsEven) -> TestProperty {
224        TestProperty::IsEven(prop)
225    }
226}
227
228impl IsEven {
229    fn to_array(self) -> TestArray {
230        let mut result = TestArray::ZERO;
231        for i in (0..ARRAY_SIZE).step_by(2) {
232            result.set(i, true);
233        }
234        result
235    }
236
237    fn is_satisfied(self, element: u16) -> bool {
238        (element % 2) == 0
239    }
240
241    fn intersection_ordered(self, other: TestProperty) -> Simplification<TestProperty> {
242        // (even ∩ i) is out of order and will be called reversed
243        // (even ∩ even) has already been handled by the caller
244        // (even ∩ >i) can't be simplified
245        Simplification::Keep(self.into(), other)
246    }
247
248    fn symmetric_difference_ordered(self, other: TestProperty) -> Simplification<TestProperty> {
249        // (even ⊕ i) is out of order and will be called reversed
250        // (even ⊕ even) has already been handled by the caller
251        // (even ⊕ >i) can't be simplified
252        Simplification::Keep(self.into(), other)
253    }
254}
255
256impl Display for IsEven {
257    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
258        write!(f, "even")
259    }
260}
261
262//-------------------------------------------------------------------------------------------------
263// Property: Greater than
264
265#[doc(hidden)]
266#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
267pub struct GreaterThan(u16);
268
269impl From<GreaterThan> for TestProperty {
270    fn from(prop: GreaterThan) -> TestProperty {
271        TestProperty::GreaterThan(prop)
272    }
273}
274
275impl GreaterThan {
276    fn to_array(self) -> TestArray {
277        let mut result = TestArray::ZERO;
278        for i in (self.0 as usize + 1)..ARRAY_SIZE {
279            result.set(i, true);
280        }
281        result
282    }
283
284    fn is_satisfied(self, element: u16) -> bool {
285        element > self.0
286    }
287
288    fn intersection_ordered(self, other: TestProperty) -> Simplification<TestProperty> {
289        // (>i ∩ j) is out of order and will be called reversed
290        // (>i ∩ even) is out of order and will be called reversed
291        // (>i ∩ >i) has already been handled by the caller
292
293        match other {
294            TestProperty::GreaterThan(other) => {
295                // (>i ∩ >j) == >max(i,j)
296                if self.0 > other.0 {
297                    Simplification::Replace(self.into())
298                } else {
299                    Simplification::Replace(other.into())
300                }
301            }
302
303            _ => Simplification::Remove,
304        }
305    }
306
307    fn symmetric_difference_ordered(self, other: TestProperty) -> Simplification<TestProperty> {
308        // (>i ⊕ j) is out of order and will be called reversed
309        // (>i ⊕ even) is out of order and will be called reversed
310        // (>i ⊕ >i) has already been handled by the caller
311        // (>i ⊕ >j) cannot be simplified
312        Simplification::Keep(self.into(), other)
313    }
314}
315
316impl Display for GreaterThan {
317    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
318        write!(f, ">{}", self.0)
319    }
320}
321
322//-------------------------------------------------------------------------------------------------
323// Tests
324
325mod proptest_support {
326    use super::*;
327
328    use std::ops::Range;
329
330    use proptest::prelude::*;
331    use proptest::strategy::Map;
332    use symbolic_sets::proptest::ArbitraryElement;
333
334    impl Arbitrary for SingleNumber {
335        type Parameters = ();
336        type Strategy = Map<Range<u16>, fn(u16) -> SingleNumber>;
337        fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
338            (0..ARRAY_SIZE as u16).prop_map(SingleNumber)
339        }
340    }
341
342    impl Arbitrary for IsEven {
343        type Parameters = ();
344        type Strategy = Just<IsEven>;
345        fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
346            Just(IsEven)
347        }
348    }
349
350    impl Arbitrary for GreaterThan {
351        type Parameters = ();
352        type Strategy = Map<Range<u16>, fn(u16) -> GreaterThan>;
353        fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
354            (0..ARRAY_SIZE as u16).prop_map(GreaterThan)
355        }
356    }
357
358    impl Arbitrary for TestProperty {
359        type Parameters = ();
360        type Strategy = BoxedStrategy<TestProperty>;
361        fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
362            prop_oneof![
363                any::<SingleNumber>().prop_map_into(), //
364                any::<IsEven>().prop_map_into(),
365                any::<GreaterThan>().prop_map_into(),
366            ]
367            .boxed()
368        }
369    }
370
371    impl ArbitraryElement for TestProperty {
372        type Element = u16;
373        type Strategy = BoxedStrategy<u16>;
374        fn arbitrary_element(set: &TestSet) -> Self::Strategy {
375            let elements = set.to_array();
376            let size = elements.count_ones();
377            (0..size)
378                .prop_map(move |idx| {
379                    elements
380                        .iter_ones()
381                        .nth(idx)
382                        .expect("Index should be valid") as u16
383                })
384                .boxed()
385        }
386    }
387}
388
389#[cfg(test)]
390mod tests {
391    use super::*;
392    symbolic_sets::test_anf_set!(TestProperty);
393    symbolic_sets::test_anf_set_with_elements!(TestProperty);
394}