unitforge 0.3.17__tar.gz → 0.3.18__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. {unitforge-0.3.17 → unitforge-0.3.18}/Cargo.lock +1 -1
  2. {unitforge-0.3.17 → unitforge-0.3.18}/Cargo.toml +1 -1
  3. {unitforge-0.3.17 → unitforge-0.3.18}/PKG-INFO +1 -1
  4. {unitforge-0.3.17 → unitforge-0.3.18}/src/impl_macros.rs +48 -27
  5. unitforge-0.3.18/src/internal.rs +16 -0
  6. {unitforge-0.3.17 → unitforge-0.3.18}/src/lib.rs +1 -0
  7. {unitforge-0.3.17 → unitforge-0.3.18}/src/prelude.rs +5 -1
  8. {unitforge-0.3.17 → unitforge-0.3.18}/src/small_linalg/matrix3.rs +5 -13
  9. {unitforge-0.3.17 → unitforge-0.3.18}/tests/matrix3.rs +12 -0
  10. {unitforge-0.3.17 → unitforge-0.3.18}/tests/quantities.rs +15 -0
  11. {unitforge-0.3.17 → unitforge-0.3.18}/.gitignore +0 -0
  12. {unitforge-0.3.17 → unitforge-0.3.18}/.gitlab-ci.yml +0 -0
  13. {unitforge-0.3.17 → unitforge-0.3.18}/Readme.md +0 -0
  14. {unitforge-0.3.17 → unitforge-0.3.18}/Readme_py.md +0 -0
  15. {unitforge-0.3.17 → unitforge-0.3.18}/pyproject.toml +0 -0
  16. {unitforge-0.3.17 → unitforge-0.3.18}/python/unitforge/__init__.py +0 -0
  17. {unitforge-0.3.17 → unitforge-0.3.18}/scripts/generate_python_stubs.py +0 -0
  18. {unitforge-0.3.17 → unitforge-0.3.18}/src/quantities/acceleration.rs +0 -0
  19. {unitforge-0.3.17 → unitforge-0.3.18}/src/quantities/angle.rs +0 -0
  20. {unitforge-0.3.17 → unitforge-0.3.18}/src/quantities/angular_acceleration.rs +0 -0
  21. {unitforge-0.3.17 → unitforge-0.3.18}/src/quantities/angular_velocity.rs +0 -0
  22. {unitforge-0.3.17 → unitforge-0.3.18}/src/quantities/area.rs +0 -0
  23. {unitforge-0.3.17 → unitforge-0.3.18}/src/quantities/area_of_moment.rs +0 -0
  24. {unitforge-0.3.17 → unitforge-0.3.18}/src/quantities/charge.rs +0 -0
  25. {unitforge-0.3.17 → unitforge-0.3.18}/src/quantities/compliance.rs +0 -0
  26. {unitforge-0.3.17 → unitforge-0.3.18}/src/quantities/density.rs +0 -0
  27. {unitforge-0.3.17 → unitforge-0.3.18}/src/quantities/distance.rs +0 -0
  28. {unitforge-0.3.17 → unitforge-0.3.18}/src/quantities/force.rs +0 -0
  29. {unitforge-0.3.17 → unitforge-0.3.18}/src/quantities/force_area.rs +0 -0
  30. {unitforge-0.3.17 → unitforge-0.3.18}/src/quantities/force_distance.rs +0 -0
  31. {unitforge-0.3.17 → unitforge-0.3.18}/src/quantities/force_div_distance_power_four.rs +0 -0
  32. {unitforge-0.3.17 → unitforge-0.3.18}/src/quantities/force_per_volume.rs +0 -0
  33. {unitforge-0.3.17 → unitforge-0.3.18}/src/quantities/force_stress.rs +0 -0
  34. {unitforge-0.3.17 → unitforge-0.3.18}/src/quantities/force_volume.rs +0 -0
  35. {unitforge-0.3.17 → unitforge-0.3.18}/src/quantities/inverse_angle.rs +0 -0
  36. {unitforge-0.3.17 → unitforge-0.3.18}/src/quantities/inverse_area.rs +0 -0
  37. {unitforge-0.3.17 → unitforge-0.3.18}/src/quantities/inverse_distance.rs +0 -0
  38. {unitforge-0.3.17 → unitforge-0.3.18}/src/quantities/inverse_stress.rs +0 -0
  39. {unitforge-0.3.17 → unitforge-0.3.18}/src/quantities/mass.rs +0 -0
  40. {unitforge-0.3.17 → unitforge-0.3.18}/src/quantities/mass_div_time_squared_volume.rs +0 -0
  41. {unitforge-0.3.17 → unitforge-0.3.18}/src/quantities/mass_per_distance.rs +0 -0
  42. {unitforge-0.3.17 → unitforge-0.3.18}/src/quantities/rotational_stiffness.rs +0 -0
  43. {unitforge-0.3.17 → unitforge-0.3.18}/src/quantities/stiffness.rs +0 -0
  44. {unitforge-0.3.17 → unitforge-0.3.18}/src/quantities/strain.rs +0 -0
  45. {unitforge-0.3.17 → unitforge-0.3.18}/src/quantities/stress.rs +0 -0
  46. {unitforge-0.3.17 → unitforge-0.3.18}/src/quantities/stress_squared.rs +0 -0
  47. {unitforge-0.3.17 → unitforge-0.3.18}/src/quantities/time.rs +0 -0
  48. {unitforge-0.3.17 → unitforge-0.3.18}/src/quantities/velocity.rs +0 -0
  49. {unitforge-0.3.17 → unitforge-0.3.18}/src/quantities/velocity_squared.rs +0 -0
  50. {unitforge-0.3.17 → unitforge-0.3.18}/src/quantities/voltage.rs +0 -0
  51. {unitforge-0.3.17 → unitforge-0.3.18}/src/quantities/volume.rs +0 -0
  52. {unitforge-0.3.17 → unitforge-0.3.18}/src/quantities.template.rs +0 -0
  53. {unitforge-0.3.17 → unitforge-0.3.18}/src/relations.rs +0 -0
  54. {unitforge-0.3.17 → unitforge-0.3.18}/src/small_linalg/bindings/matrix3_py.template.rs +0 -0
  55. {unitforge-0.3.17 → unitforge-0.3.18}/src/small_linalg/bindings/vector3_py.template.rs +0 -0
  56. {unitforge-0.3.17 → unitforge-0.3.18}/src/small_linalg/matrix2.rs +0 -0
  57. {unitforge-0.3.17 → unitforge-0.3.18}/src/small_linalg/matrix2x3.rs +0 -0
  58. {unitforge-0.3.17 → unitforge-0.3.18}/src/small_linalg/matrix3x2.rs +0 -0
  59. {unitforge-0.3.17 → unitforge-0.3.18}/src/small_linalg/vector2.rs +0 -0
  60. {unitforge-0.3.17 → unitforge-0.3.18}/src/small_linalg/vector3.rs +0 -0
  61. {unitforge-0.3.17 → unitforge-0.3.18}/src/tests/quantity_unit_tests.rs +0 -0
  62. {unitforge-0.3.17 → unitforge-0.3.18}/src/tests/serde_tests.rs +0 -0
  63. {unitforge-0.3.17 → unitforge-0.3.18}/tests/matrix2.rs +0 -0
  64. {unitforge-0.3.17 → unitforge-0.3.18}/tests/matrix2x3.rs +0 -0
  65. {unitforge-0.3.17 → unitforge-0.3.18}/tests/matrix3x2.rs +0 -0
  66. {unitforge-0.3.17 → unitforge-0.3.18}/tests/ndarray_dot.rs +0 -0
  67. {unitforge-0.3.17 → unitforge-0.3.18}/tests/small_linalg_interfaces.rs +0 -0
  68. {unitforge-0.3.17 → unitforge-0.3.18}/tests/test_quantities.py +0 -0
  69. {unitforge-0.3.17 → unitforge-0.3.18}/tests/test_small_linalg.py +0 -0
  70. {unitforge-0.3.17 → unitforge-0.3.18}/tests/typecheck/mypy_smoke.py +0 -0
  71. {unitforge-0.3.17 → unitforge-0.3.18}/tests/vector2.rs +0 -0
  72. {unitforge-0.3.17 → unitforge-0.3.18}/tests/vector3.rs +0 -0
  73. {unitforge-0.3.17 → unitforge-0.3.18}/unitforge_macros/Cargo.toml +0 -0
  74. {unitforge-0.3.17 → unitforge-0.3.18}/unitforge_macros/src/lib.rs +0 -0
@@ -369,7 +369,7 @@ checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3"
369
369
 
370
370
  [[package]]
371
371
  name = "unitforge"
372
- version = "0.3.17"
372
+ version = "0.3.18"
373
373
  dependencies = [
374
374
  "ndarray",
375
375
  "num-traits",
@@ -4,7 +4,7 @@ crate-type = ["cdylib", "rlib"]
4
4
 
5
5
  [package]
6
6
  name = "unitforge"
7
- version = "0.3.17"
7
+ version = "0.3.18"
8
8
  edition = "2021"
9
9
  authors = ["Dr.Q team <info@askdrq.com>", "Henrik Stromberg <henrik@askdrq.com>"]
10
10
  description = "A library for unit and quantity consistent computations in Rust"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: unitforge
3
- Version: 0.3.17
3
+ Version: 0.3.18
4
4
  Classifier: Programming Language :: Python :: 3
5
5
  Classifier: Programming Language :: Rust
6
6
  Classifier: License :: OSI Approved :: MIT License
@@ -44,6 +44,9 @@ macro_rules! impl_quantity {
44
44
 
45
45
  impl PhysicsQuantity for $type {
46
46
  fn as_f64(&self) -> f64 {
47
+ if self.multiplier.abs() < f64::EPSILON {
48
+ return 0.0;
49
+ }
47
50
  self.multiplier * 10_f64.powi(self.power)
48
51
  }
49
52
 
@@ -67,7 +70,7 @@ macro_rules! impl_quantity {
67
70
  let (multiplier, power) = Self::split_value(value);
68
71
  let r = $type {
69
72
  multiplier: multiplier * unit_multiplier,
70
- power: power + unit_power,
73
+ power: $crate::internal::saturating_power_add(power, unit_power),
71
74
  };
72
75
  r
73
76
  }
@@ -89,6 +92,9 @@ macro_rules! impl_quantity {
89
92
  }
90
93
 
91
94
  fn get_value(&self) -> f64 {
95
+ if self.multiplier.abs() < f64::EPSILON {
96
+ return 0.0;
97
+ }
92
98
  self.multiplier * 10_f64.powi(self.power)
93
99
  }
94
100
 
@@ -105,8 +111,12 @@ macro_rules! impl_quantity {
105
111
  }
106
112
 
107
113
  fn to(&self, unit: Self::Unit) -> f64 {
114
+ if self.multiplier.abs() < f64::EPSILON {
115
+ return 0.0;
116
+ }
108
117
  let (unit_multiplier, unit_power) = unit.base_per_x();
109
- self.multiplier / unit_multiplier * 10_f64.powi(self.power - unit_power)
118
+ self.multiplier / unit_multiplier
119
+ * $crate::internal::pow10_delta(self.power, unit_power)
110
120
  }
111
121
 
112
122
  fn abs(self) -> Self {
@@ -140,6 +150,9 @@ macro_rules! impl_quantity {
140
150
  }
141
151
 
142
152
  fn from_exponential(multiplier: f64, power: i32) -> Self {
153
+ if multiplier.abs() < f64::EPSILON {
154
+ return Self::zero();
155
+ }
143
156
  Self { multiplier, power }
144
157
  }
145
158
 
@@ -165,11 +178,14 @@ macro_rules! impl_quantity {
165
178
 
166
179
  fn optimize(&mut self) {
167
180
  if self.multiplier.abs() < f64::EPSILON {
181
+ self.multiplier = 0.0;
182
+ self.power = 0;
168
183
  return;
169
184
  }
170
185
  let power_on_multiplier = self.multiplier.abs().log10().round() as i32;
171
186
  self.multiplier /= 10_f64.powi(power_on_multiplier);
172
- self.power += power_on_multiplier;
187
+ self.power =
188
+ $crate::internal::saturating_power_add(self.power, power_on_multiplier);
173
189
  }
174
190
 
175
191
  const INFINITY: Self = Self {
@@ -300,7 +316,7 @@ macro_rules! impl_quantity {
300
316
  other,
301
317
  &Self {
302
318
  multiplier: self.multiplier,
303
- power: self.power.clone() - 9,
319
+ power: $crate::internal::saturating_power_sub(self.power, 9),
304
320
  },
305
321
  )
306
322
  }
@@ -334,8 +350,9 @@ macro_rules! impl_quantity {
334
350
 
335
351
  fn add(self, other: Self) -> Self {
336
352
  let common_power = self.power.max(other.power);
337
- let multiplier = self.multiplier * 10_f64.powi(self.power - common_power)
338
- + other.multiplier * 10_f64.powi(other.power - common_power);
353
+ let multiplier = self.multiplier
354
+ * $crate::internal::pow10_delta(self.power, common_power)
355
+ + other.multiplier * $crate::internal::pow10_delta(other.power, common_power);
339
356
  let mut res = Self {
340
357
  multiplier,
341
358
  power: common_power,
@@ -349,9 +366,10 @@ macro_rules! impl_quantity {
349
366
  type Output = Self;
350
367
 
351
368
  fn sub(self, other: Self) -> Self {
352
- let common_power = (self.power + other.power) / 2;
353
- let multiplier = self.multiplier * 10_f64.powi(self.power - common_power)
354
- - other.multiplier * 10_f64.powi(other.power - common_power);
369
+ let common_power = $crate::internal::average_power(self.power, other.power);
370
+ let multiplier = self.multiplier
371
+ * $crate::internal::pow10_delta(self.power, common_power)
372
+ - other.multiplier * $crate::internal::pow10_delta(other.power, common_power);
355
373
 
356
374
  let mut res = Self {
357
375
  multiplier,
@@ -369,7 +387,7 @@ macro_rules! impl_quantity {
369
387
  let (rhs_multiplier, rhs_power) = Self::split_value(rhs);
370
388
  Self {
371
389
  multiplier: self.multiplier / rhs_multiplier,
372
- power: self.power - rhs_power,
390
+ power: $crate::internal::saturating_power_sub(self.power, rhs_power),
373
391
  }
374
392
  }
375
393
  }
@@ -381,7 +399,7 @@ macro_rules! impl_quantity {
381
399
  let (rhs_multiplier, rhs_power) = Self::split_value(rhs);
382
400
  Self {
383
401
  multiplier: self.multiplier * rhs_multiplier,
384
- power: self.power + rhs_power,
402
+ power: $crate::internal::saturating_power_add(self.power, rhs_power),
385
403
  }
386
404
  }
387
405
  }
@@ -396,9 +414,10 @@ macro_rules! impl_quantity {
396
414
 
397
415
  impl AddAssign for $type {
398
416
  fn add_assign(&mut self, other: Self) {
399
- let common_power = (self.power + other.power) / 2;
400
- self.multiplier = self.multiplier * 10_f64.powi(self.power - common_power)
401
- + other.multiplier * 10_f64.powi(other.power - common_power);
417
+ let common_power = $crate::internal::average_power(self.power, other.power);
418
+ self.multiplier = self.multiplier
419
+ * $crate::internal::pow10_delta(self.power, common_power)
420
+ + other.multiplier * $crate::internal::pow10_delta(other.power, common_power);
402
421
  self.power = common_power;
403
422
  self.optimize();
404
423
  }
@@ -406,9 +425,10 @@ macro_rules! impl_quantity {
406
425
 
407
426
  impl SubAssign for $type {
408
427
  fn sub_assign(&mut self, other: Self) {
409
- let common_power = (self.power + other.power) / 2;
410
- self.multiplier = self.multiplier * 10_f64.powi(self.power - common_power)
411
- - other.multiplier * 10_f64.powi(other.power - common_power);
428
+ let common_power = $crate::internal::average_power(self.power, other.power);
429
+ self.multiplier = self.multiplier
430
+ * $crate::internal::pow10_delta(self.power, common_power)
431
+ - other.multiplier * $crate::internal::pow10_delta(other.power, common_power);
412
432
  self.power = common_power;
413
433
  self.optimize();
414
434
  }
@@ -418,7 +438,7 @@ macro_rules! impl_quantity {
418
438
  fn mul_assign(&mut self, rhs: f64) {
419
439
  let (rhs_multiplier, rhs_power) = Self::split_value(rhs);
420
440
  self.multiplier *= rhs_multiplier;
421
- self.power += rhs_power;
441
+ self.power = $crate::internal::saturating_power_add(self.power, rhs_power);
422
442
  }
423
443
  }
424
444
 
@@ -426,7 +446,7 @@ macro_rules! impl_quantity {
426
446
  fn div_assign(&mut self, rhs: f64) {
427
447
  let (rhs_multiplier, rhs_power) = Self::split_value(rhs);
428
448
  self.multiplier /= rhs_multiplier;
429
- self.power -= rhs_power;
449
+ self.power = $crate::internal::saturating_power_sub(self.power, rhs_power);
430
450
  }
431
451
  }
432
452
 
@@ -594,10 +614,10 @@ macro_rules! impl_quantity {
594
614
 
595
615
  impl $type {
596
616
  pub fn optimal_unit(&self, choices: HashSet<$unit>) -> Option<$unit> {
597
- let mut min_value = i32::MAX;
617
+ let mut min_value = i64::MAX;
598
618
  let mut best_unit = None;
599
619
  for unit in choices {
600
- let difference = (self.power - unit.base_per_x().1).abs();
620
+ let difference = (i64::from(self.power) - i64::from(unit.base_per_x().1)).abs();
601
621
  if difference < min_value {
602
622
  min_value = difference;
603
623
  best_unit = Some(unit);
@@ -650,7 +670,8 @@ macro_rules! impl_div_with_self_to_f64 {
650
670
  type Output = f64;
651
671
 
652
672
  fn div(self, rhs: Self) -> Self::Output {
653
- (self.multiplier / rhs.multiplier) * 10_f64.powi(self.power - rhs.power)
673
+ (self.multiplier / rhs.multiplier)
674
+ * $crate::internal::pow10_delta(self.power, rhs.power)
654
675
  }
655
676
  }
656
677
  };
@@ -665,7 +686,7 @@ macro_rules! impl_mul {
665
686
  fn mul(self, rhs: $rhs) -> Self::Output {
666
687
  <$result>::from_exponential(
667
688
  self.multiplier * rhs.multiplier,
668
- self.power + rhs.power,
689
+ $crate::internal::saturating_power_add(self.power, rhs.power),
669
690
  )
670
691
  }
671
692
  }
@@ -676,7 +697,7 @@ macro_rules! impl_mul {
676
697
  fn mul(self, rhs: $lhs) -> Self::Output {
677
698
  <$result>::from_exponential(
678
699
  self.multiplier * rhs.multiplier,
679
- self.power + rhs.power,
700
+ $crate::internal::saturating_power_add(self.power, rhs.power),
680
701
  )
681
702
  }
682
703
  }
@@ -774,7 +795,7 @@ macro_rules! impl_mul_with_self {
774
795
  fn mul(self, rhs: $lhs) -> Self::Output {
775
796
  <$result>::from_exponential(
776
797
  self.multiplier * rhs.multiplier,
777
- self.power + rhs.power,
798
+ $crate::internal::saturating_power_add(self.power, rhs.power),
778
799
  )
779
800
  }
780
801
  }
@@ -831,7 +852,7 @@ macro_rules! impl_div {
831
852
  fn div(self, rhs: $rhs) -> Self::Output {
832
853
  <$result>::from_exponential(
833
854
  self.multiplier / rhs.multiplier,
834
- self.power - rhs.power,
855
+ $crate::internal::saturating_power_sub(self.power, rhs.power),
835
856
  )
836
857
  }
837
858
  }
@@ -889,7 +910,7 @@ macro_rules! impl_sqrt {
889
910
  } else {
890
911
  <$res>::from_exponential(
891
912
  self.multiplier.sqrt() * 10_f64.sqrt(),
892
- (self.power - 1) / 2,
913
+ $crate::internal::saturating_power_sub(self.power, 1) / 2,
893
914
  )
894
915
  }
895
916
  }
@@ -0,0 +1,16 @@
1
+ pub fn saturating_power_add(lhs: i32, rhs: i32) -> i32 {
2
+ lhs.saturating_add(rhs)
3
+ }
4
+
5
+ pub fn saturating_power_sub(lhs: i32, rhs: i32) -> i32 {
6
+ lhs.saturating_sub(rhs)
7
+ }
8
+
9
+ pub fn average_power(lhs: i32, rhs: i32) -> i32 {
10
+ let average = (i64::from(lhs) + i64::from(rhs)) / 2;
11
+ average.clamp(i64::from(i32::MIN), i64::from(i32::MAX)) as i32
12
+ }
13
+
14
+ pub fn pow10_delta(lhs: i32, rhs: i32) -> f64 {
15
+ 10_f64.powi(saturating_power_sub(lhs, rhs))
16
+ }
@@ -2,6 +2,7 @@
2
2
  pub mod impl_macros;
3
3
  pub use impl_macros::*;
4
4
 
5
+ mod internal;
5
6
  pub mod prelude;
6
7
  mod relations;
7
8
 
@@ -252,7 +252,11 @@ impl PhysicsQuantity for f64 {
252
252
  }
253
253
 
254
254
  fn from_exponential(multiplier: f64, power: i32) -> Self {
255
- multiplier * 10_f64.powi(power)
255
+ if multiplier.abs() < f64::EPSILON {
256
+ 0.0
257
+ } else {
258
+ multiplier * 10_f64.powi(power)
259
+ }
256
260
  }
257
261
 
258
262
  fn min(self, other: Self) -> Self {
@@ -405,7 +405,10 @@ where
405
405
  R: PhysicsQuantity,
406
406
  {
407
407
  pub fn inverse(&self) -> Option<Matrix3<R>> {
408
- let det = self.det(); // det is of type V
408
+ let det = self.det();
409
+ if det.is_nan() || det.as_f64().abs() < f64::EPSILON {
410
+ return None;
411
+ }
409
412
 
410
413
  let a = self[(0, 0)];
411
414
  let b = self[(0, 1)];
@@ -435,18 +438,7 @@ where
435
438
  ],
436
439
  ];
437
440
 
438
- if (inv[0][0]
439
- + inv[0][1]
440
- + inv[0][2]
441
- + inv[1][0]
442
- + inv[1][1]
443
- + inv[1][2]
444
- + inv[2][0]
445
- + inv[2][1]
446
- + inv[2][2])
447
- .as_f64()
448
- .is_nan()
449
- {
441
+ if inv.iter().flatten().any(|entry| entry.is_nan()) {
450
442
  None
451
443
  } else {
452
444
  Some(Matrix3::new(inv))
@@ -90,6 +90,18 @@ mod matrix3_tests {
90
90
  assert!(diff.get_power() < -12);
91
91
  }
92
92
 
93
+ #[test]
94
+ fn test_inverse_singular_large_exponents_returns_none() {
95
+ let huge = Distance::from_exponential(1.0, i32::MAX / 2);
96
+ let zero = Distance::from_raw(0.0);
97
+ let one = Distance::from_raw(1.0);
98
+ let matrix: Matrix3<Distance> =
99
+ Matrix3::new([[one, zero, zero], [zero, huge, huge], [zero, huge, huge]]);
100
+
101
+ let inverse: Option<Matrix3<InverseDistance>> = matrix.inverse();
102
+ assert_eq!(inverse, None);
103
+ }
104
+
93
105
  #[test]
94
106
  fn test_dot() {
95
107
  let matrix = Matrix3::new([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]]);
@@ -2,6 +2,21 @@ pub fn approx_eq(a: f64, b: f64, epsilon: f64) -> bool {
2
2
  (a - b).abs() < epsilon
3
3
  }
4
4
 
5
+ #[cfg(test)]
6
+ mod test_overflow_safety {
7
+ use unitforge::prelude::*;
8
+ use unitforge::quantities::{Distance, DistanceUnit};
9
+
10
+ #[test]
11
+ fn test_subtraction_with_large_powers_does_not_overflow() {
12
+ let lhs = Distance::from_exponential(1.0, i32::MAX);
13
+ let rhs = Distance::from_exponential(1.0, i32::MAX);
14
+ let diff = lhs - rhs;
15
+
16
+ assert_eq!(diff.to(DistanceUnit::m), 0.0);
17
+ }
18
+ }
19
+
5
20
  #[cfg(test)]
6
21
  mod test_force {
7
22
  use ndarray::{arr1, arr2};
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes