1use ferray_core::Array;
6use ferray_core::dimension::Dimension;
7use ferray_core::dtype::Element;
8use ferray_core::error::FerrayResult;
9
10use crate::helpers::{binary_map_op, unary_map_op};
11
12pub trait Logical {
14 fn is_truthy(&self) -> bool;
16}
17
18impl Logical for bool {
19 #[inline]
20 fn is_truthy(&self) -> bool {
21 *self
22 }
23}
24
25macro_rules! impl_logical_numeric {
26 ($($ty:ty),*) => {
27 $(
28 impl Logical for $ty {
29 #[inline]
30 fn is_truthy(&self) -> bool {
31 *self != 0 as $ty
32 }
33 }
34 )*
35 };
36}
37
38impl_logical_numeric!(i8, i16, i32, i64, i128, u8, u16, u32, u64, u128);
39
40impl Logical for f32 {
41 #[inline]
42 fn is_truthy(&self) -> bool {
43 *self != 0.0
44 }
45}
46
47impl Logical for f64 {
48 #[inline]
49 fn is_truthy(&self) -> bool {
50 *self != 0.0
51 }
52}
53
54impl Logical for num_complex::Complex<f32> {
55 #[inline]
56 fn is_truthy(&self) -> bool {
57 self.re != 0.0 || self.im != 0.0
58 }
59}
60
61impl Logical for num_complex::Complex<f64> {
62 #[inline]
63 fn is_truthy(&self) -> bool {
64 self.re != 0.0 || self.im != 0.0
65 }
66}
67
68pub fn logical_and<T, D>(a: &Array<T, D>, b: &Array<T, D>) -> FerrayResult<Array<bool, D>>
70where
71 T: Element + Logical + Copy,
72 D: Dimension,
73{
74 binary_map_op(a, b, |x, y| x.is_truthy() && y.is_truthy())
75}
76
77pub fn logical_or<T, D>(a: &Array<T, D>, b: &Array<T, D>) -> FerrayResult<Array<bool, D>>
79where
80 T: Element + Logical + Copy,
81 D: Dimension,
82{
83 binary_map_op(a, b, |x, y| x.is_truthy() || y.is_truthy())
84}
85
86pub fn logical_xor<T, D>(a: &Array<T, D>, b: &Array<T, D>) -> FerrayResult<Array<bool, D>>
88where
89 T: Element + Logical + Copy,
90 D: Dimension,
91{
92 binary_map_op(a, b, |x, y| x.is_truthy() ^ y.is_truthy())
93}
94
95pub fn logical_not<T, D>(input: &Array<T, D>) -> FerrayResult<Array<bool, D>>
97where
98 T: Element + Logical + Copy,
99 D: Dimension,
100{
101 unary_map_op(input, |x| !x.is_truthy())
102}
103
104pub fn all<T, D>(input: &Array<T, D>) -> bool
106where
107 T: Element + Logical,
108 D: Dimension,
109{
110 input.iter().all(|x| x.is_truthy())
111}
112
113pub fn any<T, D>(input: &Array<T, D>) -> bool
115where
116 T: Element + Logical,
117 D: Dimension,
118{
119 input.iter().any(|x| x.is_truthy())
120}
121
122#[cfg(test)]
123mod tests {
124 use super::*;
125 use ferray_core::dimension::Ix1;
126
127 fn arr1_bool(data: Vec<bool>) -> Array<bool, Ix1> {
128 let n = data.len();
129 Array::from_vec(Ix1::new([n]), data).unwrap()
130 }
131
132 fn arr1_i32(data: Vec<i32>) -> Array<i32, Ix1> {
133 let n = data.len();
134 Array::from_vec(Ix1::new([n]), data).unwrap()
135 }
136
137 #[test]
138 fn test_logical_and() {
139 let a = arr1_bool(vec![true, true, false, false]);
140 let b = arr1_bool(vec![true, false, true, false]);
141 let r = logical_and(&a, &b).unwrap();
142 assert_eq!(r.as_slice().unwrap(), &[true, false, false, false]);
143 }
144
145 #[test]
146 fn test_logical_or() {
147 let a = arr1_bool(vec![true, true, false, false]);
148 let b = arr1_bool(vec![true, false, true, false]);
149 let r = logical_or(&a, &b).unwrap();
150 assert_eq!(r.as_slice().unwrap(), &[true, true, true, false]);
151 }
152
153 #[test]
154 fn test_logical_xor() {
155 let a = arr1_bool(vec![true, true, false, false]);
156 let b = arr1_bool(vec![true, false, true, false]);
157 let r = logical_xor(&a, &b).unwrap();
158 assert_eq!(r.as_slice().unwrap(), &[false, true, true, false]);
159 }
160
161 #[test]
162 fn test_logical_not() {
163 let a = arr1_bool(vec![true, false, true]);
164 let r = logical_not(&a).unwrap();
165 assert_eq!(r.as_slice().unwrap(), &[false, true, false]);
166 }
167
168 #[test]
169 fn test_logical_and_numeric() {
170 let a = arr1_i32(vec![1, 1, 0, 0]);
171 let b = arr1_i32(vec![1, 0, 1, 0]);
172 let r = logical_and(&a, &b).unwrap();
173 assert_eq!(r.as_slice().unwrap(), &[true, false, false, false]);
174 }
175
176 #[test]
177 fn test_all() {
178 let a = arr1_bool(vec![true, true, true]);
179 assert!(all(&a));
180 let b = arr1_bool(vec![true, false, true]);
181 assert!(!all(&b));
182 }
183
184 #[test]
185 fn test_any() {
186 let a = arr1_bool(vec![false, false, true]);
187 assert!(any(&a));
188 let b = arr1_bool(vec![false, false, false]);
189 assert!(!any(&b));
190 }
191
192 #[test]
193 fn test_all_numeric() {
194 let a = arr1_i32(vec![1, 2, 3]);
195 assert!(all(&a));
196 let b = arr1_i32(vec![1, 0, 3]);
197 assert!(!all(&b));
198 }
199}