1use core::ops;
24
25#[derive(Debug, Copy, Clone, PartialEq)]
26pub struct Strength(f64);
27
28impl Strength {
29 pub const REQUIRED: Strength = Strength(1_001_001_000.0);
31
32 pub const STRONG: Strength = Strength(1_000_000.0);
35
36 pub const MEDIUM: Strength = Strength(1_000.0);
38
39 pub const WEAK: Strength = Strength(1.0);
41
42 pub const ZERO: Strength = Strength(0.0);
44
45 #[inline]
47 pub const fn new(value: f64) -> Self {
48 Self(value.clamp(0.0, Self::REQUIRED.value()))
49 }
50
51 #[inline]
56 pub const fn create(strong: f64, medium: f64, weak: f64, multiplier: f64) -> Self {
57 let strong = (strong * multiplier).clamp(0.0, 1000.0) * Self::STRONG.value();
58 let medium = (medium * multiplier).clamp(0.0, 1000.0) * Self::MEDIUM.value();
59 let weak = (weak * multiplier).clamp(0.0, 1000.0) * Self::WEAK.value();
60 Self::new(strong + medium + weak)
61 }
62
63 #[inline]
65 pub const fn value(&self) -> f64 {
66 self.0
67 }
68
69 #[inline]
71 pub const fn add(self, rhs: Self) -> Self {
72 Self::new(self.0 + rhs.0)
73 }
74
75 #[inline]
77 pub const fn sub(self, rhs: Self) -> Self {
78 Self::new(self.0 - rhs.0)
79 }
80
81 #[inline]
83 pub const fn mul_f64(self, rhs: f64) -> Self {
84 Self::new(self.0 * rhs)
85 }
86
87 #[inline]
89 pub const fn mul_f32(self, rhs: f32) -> Self {
90 Self::new(self.0 * rhs as f64)
91 }
92
93 #[inline]
95 pub const fn div_f64(self, rhs: f64) -> Self {
96 Self::new(self.0 / rhs)
97 }
98
99 #[inline]
101 pub const fn div_f32(self, rhs: f32) -> Self {
102 Self::new(self.0 / rhs as f64)
103 }
104}
105
106impl ops::Add<Strength> for Strength {
107 type Output = Self;
108
109 #[inline]
111 fn add(self, rhs: Self) -> Self {
112 Self::add(self, rhs)
113 }
114}
115
116impl ops::Sub<Strength> for Strength {
117 type Output = Strength;
118
119 #[inline]
121 fn sub(self, rhs: Strength) -> Strength {
122 Self::sub(self, rhs)
123 }
124}
125
126impl ops::AddAssign<Strength> for Strength {
127 #[inline]
129 fn add_assign(&mut self, rhs: Self) {
130 *self = *self + rhs;
131 }
132}
133
134impl ops::SubAssign<Strength> for Strength {
135 #[inline]
137 fn sub_assign(&mut self, rhs: Self) {
138 *self = *self - rhs;
139 }
140}
141
142impl ops::Mul<f64> for Strength {
143 type Output = Strength;
144
145 #[inline]
147 fn mul(self, rhs: f64) -> Strength {
148 self.mul_f64(rhs)
149 }
150}
151
152impl ops::Mul<Strength> for f64 {
153 type Output = Strength;
154
155 #[inline]
157 fn mul(self, rhs: Strength) -> Strength {
158 rhs.mul_f64(self)
159 }
160}
161
162impl ops::MulAssign<f64> for Strength {
163 #[inline]
166 fn mul_assign(&mut self, rhs: f64) {
167 *self = *self * rhs;
168 }
169}
170
171impl core::cmp::Ord for Strength {
172 #[inline]
173 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
174 self.0.partial_cmp(&other.0).unwrap()
175 }
176}
177
178impl core::cmp::PartialOrd for Strength {
179 #[inline]
180 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
181 Some(self.cmp(other))
182 }
183}
184
185impl core::cmp::Eq for Strength {}
186
187#[cfg(test)]
188mod tests {
189 use rstest::rstest;
190
191 use super::*;
192
193 #[rstest]
194 #[case::under(-1.0, Strength::ZERO)]
195 #[case::min(0.0, Strength::ZERO)]
196 #[case::weak(1.0, Strength::WEAK)]
197 #[case::medium(1_000.0, Strength::MEDIUM)]
198 #[case::strong(1_000_000.0, Strength::STRONG)]
199 #[case::required(1_001_001_000.0, Strength::REQUIRED)]
200 #[case::over(1_001_001_001.0, Strength::REQUIRED)]
201 fn new(#[case] value: f64, #[case] expected: Strength) {
202 let strength = Strength::new(value);
203 assert_eq!(strength, expected);
204 }
205
206 #[rstest]
207 #[case::all_zeroes(0.0, 0.0, 0.0, 1.0, Strength::ZERO)]
208 #[case::weak(0.0, 0.0, 1.0, 1.0, Strength::WEAK)]
209 #[case::medium(0.0, 1.0, 0.0, 1.0, Strength::MEDIUM)]
210 #[case::strong(1.0, 0.0, 0.0, 1.0, Strength::STRONG)]
211 #[case::weak_clip(0.0, 0.0, 1000.0, 2.0, Strength::MEDIUM)]
212 #[case::medium_clip(0.0, 1000.0, 0.0, 2.0, Strength::STRONG)]
213 #[case::strong_clip(1000.0, 0.0, 0.0, 2.0, 1000.0 * Strength::STRONG)]
214 #[case::all_non_zero(1.0, 1.0, 1.0, 1.0, Strength::STRONG + Strength::MEDIUM + Strength::WEAK)]
215 #[case::multiplier(1.0, 1.0, 1.0, 2.0, 2.0 * (Strength::STRONG + Strength::MEDIUM + Strength::WEAK))]
216 #[case::max(1000.0, 1000.0, 1000.0, 1.0, Strength::REQUIRED)]
217 fn create(
218 #[case] strong: f64,
219 #[case] medium: f64,
220 #[case] weak: f64,
221 #[case] multiplier: f64,
222 #[case] expected: Strength,
223 ) {
224 let strength = Strength::create(strong, medium, weak, multiplier);
225 assert_eq!(strength, expected);
226 }
227
228 #[rstest]
229 #[case::zero_plus_zero(Strength::ZERO, Strength::ZERO, Strength::ZERO)]
230 #[case::zero_plus_weak(Strength::ZERO, Strength::WEAK, Strength::WEAK)]
231 #[case::weak_plus_zero(Strength::WEAK, Strength::ZERO, Strength::WEAK)]
232 #[case::weak_plus_weak(Strength::WEAK, Strength::WEAK, Strength::new(2.0))]
233 #[case::weak_plus_medium(Strength::WEAK, Strength::MEDIUM, Strength::new(1001.0))]
234 #[case::medium_plus_strong(Strength::MEDIUM, Strength::STRONG, Strength::new(1_001_000.0))]
235 #[case::strong_plus_required(Strength::STRONG, Strength::REQUIRED, Strength::REQUIRED)]
236 fn add(#[case] lhs: Strength, #[case] rhs: Strength, #[case] expected: Strength) {
237 let result = lhs + rhs;
238 assert_eq!(result, expected);
239 }
240
241 #[rstest]
242 #[case::zero_plus_zero(Strength::ZERO, Strength::ZERO, Strength::ZERO)]
243 #[case::zero_plus_weak(Strength::ZERO, Strength::WEAK, Strength::WEAK)]
244 #[case::weak_plus_zero(Strength::WEAK, Strength::ZERO, Strength::WEAK)]
245 #[case::weak_plus_weak(Strength::WEAK, Strength::WEAK, Strength::new(2.0))]
246 #[case::weak_plus_medium(Strength::WEAK, Strength::MEDIUM, Strength::new(1001.0))]
247 #[case::medium_plus_strong(Strength::MEDIUM, Strength::STRONG, Strength::new(1_001_000.0))]
248 #[case::saturate_high(Strength::STRONG, Strength::REQUIRED, Strength::REQUIRED)]
249 fn add_assign(#[case] lhs: Strength, #[case] rhs: Strength, #[case] expected: Strength) {
250 let mut result = lhs;
251 result += rhs;
252 assert_eq!(result, expected);
253 }
254
255 #[rstest]
256 #[case::saturate_low(Strength::ZERO, Strength::WEAK, Strength::ZERO)]
257 #[case::zero_minus_zero(Strength::ZERO, Strength::ZERO, Strength::ZERO)]
258 #[case::weak_minus_zero(Strength::WEAK, Strength::ZERO, Strength::WEAK)]
259 #[case::weak_minus_weak(Strength::WEAK, Strength::WEAK, Strength::ZERO)]
260 #[case::medium_minus_weak(Strength::MEDIUM, Strength::WEAK, Strength::new(999.0))]
261 #[case::strong_minus_medium(Strength::STRONG, Strength::MEDIUM, Strength::new(999_000.0))]
262 #[case::required_minus_strong(
263 Strength::REQUIRED,
264 Strength::STRONG,
265 Strength::new(1_000_001_000.0)
266 )]
267 #[case::required_minus_required(Strength::REQUIRED, Strength::REQUIRED, Strength::ZERO)]
268 fn sub(#[case] lhs: Strength, #[case] rhs: Strength, #[case] expected: Strength) {
269 let result = lhs - rhs;
270 assert_eq!(result, expected);
271 }
272
273 #[rstest]
274 #[case::saturate_low(Strength::ZERO, Strength::WEAK, Strength::ZERO)]
275 #[case::zero_minus_zero(Strength::ZERO, Strength::ZERO, Strength::ZERO)]
276 #[case::weak_minus_zero(Strength::WEAK, Strength::ZERO, Strength::WEAK)]
277 #[case::weak_minus_weak(Strength::WEAK, Strength::WEAK, Strength::ZERO)]
278 #[case::medium_minus_weak(Strength::MEDIUM, Strength::WEAK, Strength::new(999.0))]
279 #[case::strong_minus_medium(Strength::STRONG, Strength::MEDIUM, Strength::new(999_000.0))]
280 #[case::required_minus_strong(
281 Strength::REQUIRED,
282 Strength::STRONG,
283 Strength::new(1_000_001_000.0)
284 )]
285 #[case::required_minus_required(Strength::REQUIRED, Strength::REQUIRED, Strength::ZERO)]
286 fn sub_assign(#[case] lhs: Strength, #[case] rhs: Strength, #[case] expected: Strength) {
287 let mut result = lhs;
288 result -= rhs;
289 assert_eq!(result, expected);
290 }
291
292 #[rstest]
293 #[case::negative(Strength::WEAK, -1.0, Strength::ZERO)]
294 #[case::zero_mul_zero(Strength::ZERO, 0.0, Strength::ZERO)]
295 #[case::zero_mul_one(Strength::ZERO, 1.0, Strength::ZERO)]
296 #[case::weak_mul_zero(Strength::WEAK, 0.0, Strength::ZERO)]
297 #[case::weak_mul_one(Strength::WEAK, 1.0, Strength::WEAK)]
298 #[case::weak_mul_two(Strength::WEAK, 2.0, Strength::new(2.0))]
299 #[case::medium_mul_half(Strength::MEDIUM, 0.5, Strength::new(500.0))]
300 #[case::strong_mul_two(Strength::STRONG, 2.0, Strength::new(2_000_000.0))]
301 #[case::required_mul_half(Strength::REQUIRED, 0.5, Strength::new(500_500_500.0))]
302 fn mul(#[case] lhs: Strength, #[case] rhs: f64, #[case] expected: Strength) {
303 let result = lhs * rhs;
304 assert_eq!(result, expected);
305 }
306
307 #[rstest]
308 #[case::negative(Strength::WEAK, -1.0, Strength::ZERO)]
309 #[case::zero_mul_zero(Strength::ZERO, 0.0, Strength::ZERO)]
310 #[case::zero_mul_one(Strength::ZERO, 1.0, Strength::ZERO)]
311 #[case::weak_mul_zero(Strength::WEAK, 0.0, Strength::ZERO)]
312 #[case::weak_mul_one(Strength::WEAK, 1.0, Strength::WEAK)]
313 #[case::weak_mul_two(Strength::WEAK, 2.0, Strength::new(2.0))]
314 #[case::medium_mul_half(Strength::MEDIUM, 0.5, Strength::new(500.0))]
315 #[case::strong_mul_two(Strength::STRONG, 2.0, Strength::new(2_000_000.0))]
316 #[case::required_mul_half(Strength::REQUIRED, 0.5, Strength::new(500_500_500.0))]
317 fn mul_assign(#[case] lhs: Strength, #[case] rhs: f64, #[case] expected: Strength) {
318 let mut result = lhs;
319 result *= rhs;
320 assert_eq!(result, expected);
321 }
322}