arrow2/compute/
boolean_kleene.rs

1//! Boolean operators of [Kleene logic](https://en.wikipedia.org/wiki/Three-valued_logic#Kleene_and_Priest_logics).
2use crate::datatypes::DataType;
3use crate::scalar::BooleanScalar;
4use crate::{
5    array::{Array, BooleanArray},
6    bitmap::{binary, quaternary, ternary, unary, Bitmap, MutableBitmap},
7};
8
9/// Logical 'or' operation on two arrays with [Kleene logic](https://en.wikipedia.org/wiki/Three-valued_logic#Kleene_and_Priest_logics)
10/// # Panics
11/// This function panics iff the arrays have a different length
12/// # Example
13///
14/// ```rust
15/// use arrow2::array::BooleanArray;
16/// use arrow2::compute::boolean_kleene::or;
17///
18/// let a = BooleanArray::from(&[Some(true), Some(false), None]);
19/// let b = BooleanArray::from(&[None, None, None]);
20/// let or_ab = or(&a, &b);
21/// assert_eq!(or_ab, BooleanArray::from(&[Some(true), None, None]));
22/// ```
23pub fn or(lhs: &BooleanArray, rhs: &BooleanArray) -> BooleanArray {
24    assert_eq!(
25        lhs.len(),
26        rhs.len(),
27        "lhs and rhs must have the same length"
28    );
29
30    let lhs_values = lhs.values();
31    let rhs_values = rhs.values();
32
33    let lhs_validity = lhs.validity();
34    let rhs_validity = rhs.validity();
35
36    let validity = match (lhs_validity, rhs_validity) {
37        (Some(lhs_validity), Some(rhs_validity)) => {
38            Some(quaternary(
39                lhs_values,
40                rhs_values,
41                lhs_validity,
42                rhs_validity,
43                // see https://en.wikipedia.org/wiki/Three-valued_logic#Kleene_and_Priest_logics
44                |lhs, rhs, lhs_v, rhs_v| {
45                    // A = T
46                    (lhs & lhs_v) |
47                    // B = T
48                    (rhs & rhs_v) |
49                    // A = F & B = F
50                    (!lhs & lhs_v) & (!rhs & rhs_v)
51                },
52            ))
53        }
54        (Some(lhs_validity), None) => {
55            // B != U
56            Some(ternary(
57                lhs_values,
58                rhs_values,
59                lhs_validity,
60                // see https://en.wikipedia.org/wiki/Three-valued_logic#Kleene_and_Priest_logics
61                |lhs, rhs, lhs_v| {
62                    // A = T
63                    (lhs & lhs_v) |
64                    // B = T
65                    rhs |
66                    // A = F & B = F
67                    (!lhs & lhs_v) & !rhs
68                },
69            ))
70        }
71        (None, Some(rhs_validity)) => {
72            Some(ternary(
73                lhs_values,
74                rhs_values,
75                rhs_validity,
76                // see https://en.wikipedia.org/wiki/Three-valued_logic#Kleene_and_Priest_logics
77                |lhs, rhs, rhs_v| {
78                    // A = T
79                    lhs |
80                    // B = T
81                    (rhs & rhs_v) |
82                    // A = F & B = F
83                    !lhs & (!rhs & rhs_v)
84                },
85            ))
86        }
87        (None, None) => None,
88    };
89    BooleanArray::new(DataType::Boolean, lhs_values | rhs_values, validity)
90}
91
92/// Logical 'and' operation on two arrays with [Kleene logic](https://en.wikipedia.org/wiki/Three-valued_logic#Kleene_and_Priest_logics)
93/// # Panics
94/// This function panics iff the arrays have a different length
95/// # Example
96///
97/// ```rust
98/// use arrow2::array::BooleanArray;
99/// use arrow2::compute::boolean_kleene::and;
100///
101/// let a = BooleanArray::from(&[Some(true), Some(false), None]);
102/// let b = BooleanArray::from(&[None, None, None]);
103/// let and_ab = and(&a, &b);
104/// assert_eq!(and_ab, BooleanArray::from(&[None, Some(false), None]));
105/// ```
106pub fn and(lhs: &BooleanArray, rhs: &BooleanArray) -> BooleanArray {
107    assert_eq!(
108        lhs.len(),
109        rhs.len(),
110        "lhs and rhs must have the same length"
111    );
112
113    let lhs_values = lhs.values();
114    let rhs_values = rhs.values();
115
116    let lhs_validity = lhs.validity();
117    let rhs_validity = rhs.validity();
118
119    let validity = match (lhs_validity, rhs_validity) {
120        (Some(lhs_validity), Some(rhs_validity)) => {
121            Some(quaternary(
122                lhs_values,
123                rhs_values,
124                lhs_validity,
125                rhs_validity,
126                // see https://en.wikipedia.org/wiki/Three-valued_logic#Kleene_and_Priest_logics
127                |lhs, rhs, lhs_v, rhs_v| {
128                    // B = F
129                    (!rhs & rhs_v) |
130                    // A = F
131                    (!lhs & lhs_v) |
132                    // A = T & B = T
133                    (lhs & lhs_v) & (rhs & rhs_v)
134                },
135            ))
136        }
137        (Some(lhs_validity), None) => {
138            Some(ternary(
139                lhs_values,
140                rhs_values,
141                lhs_validity,
142                // see https://en.wikipedia.org/wiki/Three-valued_logic#Kleene_and_Priest_logics
143                |lhs, rhs, lhs_v| {
144                    // B = F
145                    !rhs |
146                    // A = F
147                    (!lhs & lhs_v) |
148                    // A = T & B = T
149                    (lhs & lhs_v) & rhs
150                },
151            ))
152        }
153        (None, Some(rhs_validity)) => {
154            Some(ternary(
155                lhs_values,
156                rhs_values,
157                rhs_validity,
158                // see https://en.wikipedia.org/wiki/Three-valued_logic#Kleene_and_Priest_logics
159                |lhs, rhs, rhs_v| {
160                    // B = F
161                    (!rhs & rhs_v) |
162                    // A = F
163                    !lhs |
164                    // A = T & B = T
165                    lhs & (rhs & rhs_v)
166                },
167            ))
168        }
169        (None, None) => None,
170    };
171    BooleanArray::new(DataType::Boolean, lhs_values & rhs_values, validity)
172}
173
174/// Logical 'or' operation on an array and a scalar value with [Kleene logic](https://en.wikipedia.org/wiki/Three-valued_logic#Kleene_and_Priest_logics)
175/// # Example
176///
177/// ```rust
178/// use arrow2::array::BooleanArray;
179/// use arrow2::scalar::BooleanScalar;
180/// use arrow2::compute::boolean_kleene::or_scalar;
181///
182/// let array = BooleanArray::from(&[Some(true), Some(false), None]);
183/// let scalar = BooleanScalar::new(Some(false));
184/// let result = or_scalar(&array, &scalar);
185/// assert_eq!(result, BooleanArray::from(&[Some(true), Some(false), None]));
186/// ```
187pub fn or_scalar(array: &BooleanArray, scalar: &BooleanScalar) -> BooleanArray {
188    match scalar.value() {
189        Some(true) => {
190            let mut values = MutableBitmap::new();
191            values.extend_constant(array.len(), true);
192            BooleanArray::new(DataType::Boolean, values.into(), None)
193        }
194        Some(false) => array.clone(),
195        None => {
196            let values = array.values();
197            let validity = match array.validity() {
198                Some(validity) => binary(values, validity, |value, validity| validity & value),
199                None => unary(values, |value| value),
200            };
201            BooleanArray::new(DataType::Boolean, values.clone(), Some(validity))
202        }
203    }
204}
205
206/// Logical 'and' operation on an array and a scalar value with [Kleene logic](https://en.wikipedia.org/wiki/Three-valued_logic#Kleene_and_Priest_logics)
207/// # Example
208///
209/// ```rust
210/// use arrow2::array::BooleanArray;
211/// use arrow2::scalar::BooleanScalar;
212/// use arrow2::compute::boolean_kleene::and_scalar;
213///
214/// let array = BooleanArray::from(&[Some(true), Some(false), None]);
215/// let scalar = BooleanScalar::new(None);
216/// let result = and_scalar(&array, &scalar);
217/// assert_eq!(result, BooleanArray::from(&[None, Some(false), None]));
218/// ```
219pub fn and_scalar(array: &BooleanArray, scalar: &BooleanScalar) -> BooleanArray {
220    match scalar.value() {
221        Some(true) => array.clone(),
222        Some(false) => {
223            let values = Bitmap::new_zeroed(array.len());
224            BooleanArray::new(DataType::Boolean, values, None)
225        }
226        None => {
227            let values = array.values();
228            let validity = match array.validity() {
229                Some(validity) => binary(values, validity, |value, validity| validity & !value),
230                None => unary(values, |value| !value),
231            };
232            BooleanArray::new(DataType::Boolean, array.values().clone(), Some(validity))
233        }
234    }
235}
236
237/// Returns whether any of the values in the array are `true`.
238///
239/// The output is unknown (`None`) if the array contains any null values and
240/// no `true` values.
241///
242/// # Example
243///
244/// ```
245/// use arrow2::array::BooleanArray;
246/// use arrow2::compute::boolean_kleene::any;
247///
248/// let a = BooleanArray::from(&[Some(true), Some(false)]);
249/// let b = BooleanArray::from(&[Some(false), Some(false)]);
250/// let c = BooleanArray::from(&[None, Some(false)]);
251///
252/// assert_eq!(any(&a), Some(true));
253/// assert_eq!(any(&b), Some(false));
254/// assert_eq!(any(&c), None);
255/// ```
256pub fn any(array: &BooleanArray) -> Option<bool> {
257    if array.is_empty() {
258        Some(false)
259    } else if array.null_count() > 0 {
260        if array.into_iter().any(|v| v == Some(true)) {
261            Some(true)
262        } else {
263            None
264        }
265    } else {
266        let vals = array.values();
267        Some(vals.unset_bits() != vals.len())
268    }
269}
270
271/// Returns whether all values in the array are `true`.
272///
273/// The output is unknown (`None`) if the array contains any null values and
274/// no `false` values.
275///
276/// # Example
277///
278/// ```
279/// use arrow2::array::BooleanArray;
280/// use arrow2::compute::boolean_kleene::all;
281///
282/// let a = BooleanArray::from(&[Some(true), Some(true)]);
283/// let b = BooleanArray::from(&[Some(false), Some(true)]);
284/// let c = BooleanArray::from(&[None, Some(true)]);
285///
286/// assert_eq!(all(&a), Some(true));
287/// assert_eq!(all(&b), Some(false));
288/// assert_eq!(all(&c), None);
289/// ```
290pub fn all(array: &BooleanArray) -> Option<bool> {
291    if array.is_empty() {
292        Some(true)
293    } else if array.null_count() > 0 {
294        if array.into_iter().any(|v| v == Some(false)) {
295            Some(false)
296        } else {
297            None
298        }
299    } else {
300        let vals = array.values();
301        Some(vals.unset_bits() == 0)
302    }
303}