1use crate::Float;
4
5pub trait FuzzyOps {
7 fn t(&self, a: Float, b: Float) -> Float;
9
10 fn s(&self, a: Float, b: Float) -> Float;
12
13 fn c(&self, a: Float) -> Float;
15}
16
17#[cfg(feature = "ops-minmax")]
18pub struct MinMax;
19#[cfg(feature = "ops-minmax")]
20impl FuzzyOps for MinMax {
21 fn t(&self, a: Float, b: Float) -> Float {
22 a.min(b)
23 }
24
25 fn s(&self, a: Float, b: Float) -> Float {
26 a.max(b)
27 }
28
29 fn c(&self, a: Float) -> Float {
30 1.0 - a
31 }
32}
33
34#[cfg(feature = "ops-product")]
35pub struct MinMax;
36#[cfg(feature = "ops-product")]
37impl FuzzyOps for MinMax {
38 fn t(&self, a: Float, b: Float) -> Float {
39 a * b
40 }
41
42 fn s(&self, a: Float, b: Float) -> Float {
43 a + b - a * b
44 }
45
46 fn c(&self, a: Float) -> Float {
47 1.0 - a
48 }
49}
50
51#[cfg(feature = "ops-lukasiewicz")]
52pub struct MinMax;
53#[cfg(feature = "ops-lukasiewicz")]
54impl FuzzyOps for MinMax {
55 fn t(&self, a: Float, b: Float) -> Float {
56 (a + b - 1.0).max(0.0)
57 }
58
59 fn s(&self, a: Float, b: Float) -> Float {
60 (a + b).min(1.0)
61 }
62
63 fn c(&self, a: Float) -> Float {
64 1.0 - a
65 }
66}
67
68#[cfg(feature = "ops-dyn")]
69#[derive(Clone, Copy, Debug)]
70pub enum Ops {
72 MinMax,
77 Product,
82 Lukasiewicz,
87}
88#[cfg(feature = "ops-dyn")]
89impl FuzzyOps for Ops {
91 fn t(&self, a: Float, b: Float) -> Float {
93 match self {
94 Ops::MinMax => a.min(b),
95 Ops::Product => a * b,
96 Ops::Lukasiewicz => (a + b - 1.0).max(0.0),
97 }
98 }
99
100 fn s(&self, a: Float, b: Float) -> Float {
102 match self {
103 Ops::MinMax => a.max(b),
104 Ops::Product => a + b - a * b,
105 Ops::Lukasiewicz => (a + b).min(1.0),
106 }
107 }
108
109 fn c(&self, a: Float) -> Float {
111 1.0 - a
112 }
113}
114
115#[cfg(feature = "ops-dyn")]
116#[cfg(test)]
117mod tests_dyn_ops {
118 use crate::ops::*;
119
120 #[test]
124 fn red_minmax_defaults_and_or_not() {
125 let v = crate::ops::Ops::MinMax;
126
127 assert_eq!(v.t(0.2, 0.8), 0.2);
129 assert_eq!(v.s(0.2, 0.8), 0.8);
130 assert_eq!(v.c(0.2), 0.8);
131
132 assert_eq!(v.t(0.0, 1.0), 0.0);
134 assert_eq!(v.s(0.0, 1.0), 1.0);
135 assert_eq!(v.c(0.0), 1.0);
136 assert_eq!(v.c(1.0), 0.0);
137 }
138
139 #[test]
140 fn product_ops_and_or_not_matches_code() {
141 let v = Ops::Product;
142 let eps = crate::Float::EPSILON;
143 assert!((v.t(0.2, 0.8) - 0.16).abs() < eps);
145 assert!((v.s(0.1, 0.2) - 0.28).abs() < eps);
147 assert!((v.c(0.2) - 0.8).abs() < eps);
149 }
150
151 #[test]
152 fn lukasiewicz_ops_and_or_not_matches_code() {
153 let v = Ops::Lukasiewicz;
154 let eps = crate::Float::EPSILON;
155 assert!((v.t(0.2, 0.8) - 0.0).abs() < eps);
157 assert!((v.t(0.8, 0.3) - 0.1).abs() < eps);
158 assert!((v.s(0.2, 0.8) - 1.0).abs() < eps);
160 assert!((v.s(0.4, 0.4) - 0.8).abs() < eps);
161 assert!((v.c(0.2) - 0.8).abs() < eps);
163 }
164}