1#[macro_export]
2macro_rules! impl_quantity {
3 ($type:ident, $unit:ident, $display_units:expr) => {
4 use ndarray::{Array1, Array2, ArrayView1, ArrayView2};
5 use num_traits::Zero;
6 use num_traits::{Float, FromPrimitive};
7 #[cfg(feature = "serde")]
8 use serde::{Deserialize, Serialize};
9 use std::cmp::Ordering;
10 use std::collections::HashSet;
11 use std::fmt;
12 use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
13 use $crate::prelude::*;
14
15 #[cfg(feature = "pyo3")]
16 use pyo3::pyclass;
17 #[cfg(feature = "pyo3")]
18 use pyo3::{
19 basic::CompareOp, exceptions::PyValueError, pymethods, types::PyType, Bound,
20 IntoPyObject, Py, PyAny, PyRef, PyResult,
21 };
22
23 #[cfg(feature = "strum")]
24 use strum_macros::EnumIter;
25 use $crate::small_linalg::{Matrix3, Vector3};
26 #[cfg(feature = "pyo3")]
27 use $crate::Quantity;
28
29 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
30 #[cfg(feature = "pyo3")]
31 #[pyclass(module = "unitforge")]
32 #[derive(Copy, Clone)]
33 pub struct $type {
34 pub(crate) multiplier: f64,
35 pub(crate) power: i32,
36 }
37 #[cfg(not(feature = "pyo3"))]
38 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
39 #[derive(Copy, Clone)]
40 pub struct $type {
41 pub(crate) multiplier: f64,
42 pub(crate) power: i32,
43 }
44
45 impl PhysicsQuantity for $type {
46 fn as_f64(&self) -> f64 {
47 self.multiplier * 10_f64.powi(self.power)
48 }
49
50 type Unit = $unit;
51
52 fn new(value: f64, unit: Self::Unit) -> $type {
53 if value.is_infinite() {
54 if value.is_sign_negative() {
55 return Self::NEG_INFINITY;
56 } else {
57 return Self::INFINITY;
58 }
59 }
60 if value.is_zero() {
61 return $type {
62 multiplier: 0.0,
63 power: 0,
64 };
65 }
66 let (unit_multiplier, unit_power) = unit.base_per_x();
67 let (multiplier, power) = Self::split_value(value);
68 let r = $type {
69 multiplier: multiplier * unit_multiplier,
70 power: power + unit_power,
71 };
72 r
73 }
74
75 fn split_value(v: f64) -> (f64, i32) {
76 if v.is_zero() {
77 (0.0, 0)
78 } else if v.is_infinite() {
79 if v > 0.0 {
80 (f64::INFINITY, 0)
81 } else {
82 (f64::NEG_INFINITY, 0)
83 }
84 } else {
85 let power = v.abs().log10().floor() as i32;
86 let multiplier = v / 10f64.powi(power);
87 (multiplier, power)
88 }
89 }
90
91 fn get_value(&self) -> f64 {
92 self.multiplier * 10_f64.powi(self.power)
93 }
94
95 fn get_power(&self) -> i32 {
96 self.power
97 }
98
99 fn get_multiplier(&self) -> f64 {
100 self.multiplier
101 }
102
103 fn get_tuple(&self) -> (f64, i32) {
104 (self.multiplier, self.power)
105 }
106
107 fn to(&self, unit: Self::Unit) -> f64 {
108 let (unit_multiplier, unit_power) = unit.base_per_x();
109 self.multiplier / unit_multiplier * 10_f64.powi(self.power - unit_power)
110 }
111
112 fn abs(self) -> Self {
113 Self {
114 multiplier: self.multiplier.abs(),
115 power: self.power,
116 }
117 }
118
119 fn is_nan(&self) -> bool {
120 self.multiplier.is_nan()
121 }
122
123 fn from_raw(value: f64) -> Self {
124 if value.is_infinite() {
125 if value.is_sign_negative() {
126 return Self::NEG_INFINITY;
127 } else {
128 return Self::INFINITY;
129 }
130 }
131 let (multiplier, power) = Self::split_value(value);
132 Self { multiplier, power }
133 }
134
135 fn nan() -> Self {
136 Self {
137 multiplier: <f64 as Float>::nan(),
138 power: 0,
139 }
140 }
141
142 fn from_exponential(multiplier: f64, power: i32) -> Self {
143 Self { multiplier, power }
144 }
145
146 fn min(self, other: Self) -> Self {
147 if self < other {
148 self
149 } else {
150 other
151 }
152 }
153
154 fn max(self, other: Self) -> Self {
155 if self > other {
156 self
157 } else {
158 other
159 }
160 }
161
162 fn is_close(&self, other: &Self, tolerance: &Self) -> bool {
163 (self.as_f64() - other.as_f64()).abs() <= tolerance.as_f64().abs()
164 }
165
166 fn optimize(&mut self) {
167 if self.multiplier.abs() < f64::EPSILON {
168 return;
169 }
170 let power_on_multiplier = self.multiplier.abs().log10().round() as i32;
171 self.multiplier /= 10_f64.powi(power_on_multiplier);
172 self.power += power_on_multiplier;
173 }
174
175 const INFINITY: Self = Self {
176 multiplier: f64::INFINITY,
177 power: 0,
178 };
179
180 const NEG_INFINITY: Self = Self {
181 multiplier: f64::NEG_INFINITY,
182 power: 0,
183 };
184 }
185
186 impl $crate::UnitforgeQuantity for $type {}
187
188 impl From<f64> for $type {
189 fn from(value: f64) -> Self {
190 if value.is_infinite() {
191 if value.is_sign_negative() {
192 return Self::NEG_INFINITY;
193 } else {
194 return Self::INFINITY;
195 }
196 }
197 let (multiplier, power) = Self::split_value(value);
198 Self { multiplier, power }
199 }
200 }
201
202 #[cfg(feature = "pyo3")]
203 #[pymethods]
204 impl $type {
205 fn __mul__(lhs: PyRef<Self>, rhs: Py<PyAny>) -> PyResult<Py<PyAny>> {
206 let py = lhs.py();
207 let rhs_ref = rhs.bind(py);
208 let lhs_ref = lhs.into_pyobject(py)?;
209 let rhs_quantity = Quantity::from_py_any(rhs_ref)
210 .map_err(|e| PyValueError::new_err(e.to_string()))?;
211 let lhs_quantity = Quantity::from_py_any(&lhs_ref)
212 .map_err(|e| PyValueError::new_err(e.to_string()))?;
213 match lhs_quantity * rhs_quantity {
214 Ok(value) => Ok(value.to_pyobject(py)?),
215 Err(_) => Err(PyValueError::new_err(
216 "Multiplication of given objects is not possible.",
217 )),
218 }
219 }
220
221 fn __truediv__(lhs: PyRef<Self>, rhs: Py<PyAny>) -> PyResult<Py<PyAny>> {
222 let py = lhs.py();
223 let rhs_ref = rhs.bind(py);
224 let lhs_ref = lhs.into_pyobject(py)?;
225 let rhs_quantity = Quantity::from_py_any(rhs_ref)
226 .map_err(|e| PyValueError::new_err(e.to_string()))?;
227 let lhs_quantity = Quantity::from_py_any(&lhs_ref)
228 .map_err(|e| PyValueError::new_err(e.to_string()))?;
229 match lhs_quantity / rhs_quantity {
230 Ok(value) => Ok(value.to_pyobject(py)?),
231 Err(_) => Err(PyValueError::new_err(
232 "Division of given objects is not possible.",
233 )),
234 }
235 }
236
237 fn __rmul__(&self, rhs: f64) -> PyResult<Self> {
238 Ok(*self * rhs)
239 }
240 }
241
242 impl fmt::Display for $type {
243 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
244 use std::collections::HashMap;
245 let mut groups: HashMap<String, Vec<String>> = HashMap::new();
246
247 for base_unit in $display_units {
248 let value = self.to(base_unit);
249 let rounded = if value.is_zero() {
250 value
251 } else {
252 let digits = value.abs().log10().ceil() as i32;
253 let rounding_multiplier = 10_f64.powi(3 - digits);
254 (value * rounding_multiplier).round() / rounding_multiplier
255 };
256
257 let value = format!("{}", rounded);
258 groups
259 .entry(value)
260 .or_default()
261 .push(base_unit.name().to_string());
262 }
263
264 let mut parts = Vec::new();
265 for (value, units) in groups {
266 let joined_units = units.join(", ");
267 parts.push(format!(
268 "{}{}{}",
269 value,
270 if joined_units.starts_with('°') {
271 ""
272 } else {
273 " "
274 },
275 joined_units
276 ));
277 }
278
279 write!(f, "{}", parts.join(", "))
280 }
281 }
282
283 impl Neg for $type {
284 type Output = Self;
285
286 fn neg(self) -> Self::Output {
287 Self {
288 multiplier: -self.multiplier,
289 power: self.power,
290 }
291 }
292 }
293
294 impl PartialEq<Self> for $type
295 where
296 $type: PhysicsQuantity,
297 {
298 fn eq(&self, other: &Self) -> bool {
299 self.is_close(
300 other,
301 &Self {
302 multiplier: self.multiplier,
303 power: self.power.clone() - 9,
304 },
305 )
306 }
307 }
308
309 impl PartialOrd for $type
310 where
311 $type: PhysicsQuantity,
312 {
313 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
314 self.as_f64().partial_cmp(&other.as_f64())
315 }
316 }
317
318 impl FromPrimitive for $type {
319 fn from_i64(n: i64) -> Option<Self> {
320 Some(Self::from_raw(n as f64))
321 }
322
323 fn from_u64(n: u64) -> Option<Self> {
324 Some(Self::from_raw(n as f64))
325 }
326
327 fn from_f64(n: f64) -> Option<Self> {
328 Some(Self::from_raw(n))
329 }
330 }
331
332 impl Add for $type {
333 type Output = Self;
334
335 fn add(self, other: Self) -> Self {
336 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);
339 let mut res = Self {
340 multiplier,
341 power: common_power,
342 };
343 res.optimize();
344 res
345 }
346 }
347
348 impl Sub for $type {
349 type Output = Self;
350
351 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);
355
356 let mut res = Self {
357 multiplier,
358 power: common_power,
359 };
360 res.optimize();
361 res
362 }
363 }
364
365 impl Div<f64> for $type {
366 type Output = Self;
367
368 fn div(self, rhs: f64) -> Self::Output {
369 let (rhs_multiplier, rhs_power) = Self::split_value(rhs);
370 Self {
371 multiplier: self.multiplier / rhs_multiplier,
372 power: self.power - rhs_power,
373 }
374 }
375 }
376
377 impl Mul<f64> for $type {
378 type Output = Self;
379
380 fn mul(self, rhs: f64) -> Self::Output {
381 let (rhs_multiplier, rhs_power) = Self::split_value(rhs);
382 Self {
383 multiplier: self.multiplier * rhs_multiplier,
384 power: self.power + rhs_power,
385 }
386 }
387 }
388
389 impl Mul<$type> for f64 {
390 type Output = $type;
391
392 fn mul(self, rhs: $type) -> Self::Output {
393 rhs * self
394 }
395 }
396
397 impl AddAssign for $type {
398 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);
402 self.power = common_power;
403 self.optimize();
404 }
405 }
406
407 impl SubAssign for $type {
408 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);
412 self.power = common_power;
413 self.optimize();
414 }
415 }
416
417 impl MulAssign<f64> for $type {
418 fn mul_assign(&mut self, rhs: f64) {
419 let (rhs_multiplier, rhs_power) = Self::split_value(rhs);
420 self.multiplier *= rhs_multiplier;
421 self.power += rhs_power;
422 }
423 }
424
425 impl DivAssign<f64> for $type {
426 fn div_assign(&mut self, rhs: f64) {
427 let (rhs_multiplier, rhs_power) = Self::split_value(rhs);
428 self.multiplier /= rhs_multiplier;
429 self.power -= rhs_power;
430 }
431 }
432
433 impl Mul<Vector3<f64>> for $type {
434 type Output = Vector3<$type>;
435
436 fn mul(self, rhs: Vector3<f64>) -> Self::Output {
437 Vector3::new([self * rhs[0], self * rhs[1], self * rhs[2]])
438 }
439 }
440
441 impl Mul<Matrix3<f64>> for $type {
442 type Output = Matrix3<$type>;
443
444 fn mul(self, rhs: Matrix3<f64>) -> Self::Output {
445 Matrix3::new([
446 [self * rhs[(0, 0)], self * rhs[(0, 1)], self * rhs[(0, 2)]],
447 [self * rhs[(1, 0)], self * rhs[(1, 1)], self * rhs[(1, 2)]],
448 [self * rhs[(2, 0)], self * rhs[(2, 1)], self * rhs[(2, 2)]],
449 ])
450 }
451 }
452
453 impl Zero for $type {
454 fn zero() -> Self {
455 Self {
456 multiplier: 0.0,
457 power: 0,
458 }
459 }
460
461 fn is_zero(&self) -> bool {
462 self.multiplier == 0.0
463 }
464 }
465
466 impl fmt::Debug for $type {
467 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
468 fmt::Display::fmt(self, f)
469 }
470 }
471
472 impl QuantityArray2<$unit> for Array2<$type> {
473 fn from_raw(raw: ArrayView2<f64>, unit: $unit) -> Self {
474 let mut res = Array2::zeros(raw.dim());
475 for i in 0..raw.dim().0 {
476 for j in 0..raw.dim().1 {
477 res[[i, j]] = <$type>::new(raw[[i, j]], unit);
478 }
479 }
480 res
481 }
482
483 fn to_raw(&self) -> Array2<f64> {
484 let mut res = Array2::zeros(self.dim());
485 for i in 0..self.dim().0 {
486 for j in 0..self.dim().1 {
487 res[[i, j]] = self[[i, j]].as_f64();
488 }
489 }
490 res
491 }
492
493 fn to(&self, unit: $unit) -> Array2<f64> {
494 let mut res = Array2::zeros(self.dim());
495 for i in 0..self.dim().0 {
496 for j in 0..self.dim().1 {
497 res[[i, j]] = self[[i, j]].to(unit);
498 }
499 }
500 res
501 }
502 }
503
504 impl QuantityArray1<$unit> for Array1<$type> {
505 fn from_raw(raw: ArrayView1<f64>, unit: $unit) -> Self {
506 let mut res = Array1::zeros(raw.dim());
507 for i in 0..raw.dim() {
508 res[i] = <$type>::new(raw[i], unit);
509 }
510 res
511 }
512
513 fn to_raw(&self) -> Array1<f64> {
514 let mut res = Array1::zeros(self.dim());
515 for i in 0..self.dim() {
516 res[i] = self[i].as_f64();
517 }
518 res
519 }
520
521 fn to(&self, unit: $unit) -> Array1<f64> {
522 let mut res = Array1::zeros(self.dim());
523 for i in 0..self.dim() {
524 res[i] = self[i].to(unit);
525 }
526 res
527 }
528 }
529
530 #[cfg(feature = "pyo3")]
531 #[pymethods]
532 impl $type {
533 #[classmethod]
534 #[pyo3(name = "zero")]
535 fn zero_py(_cls: &Bound<'_, PyType>) -> Self {
536 Self::zero()
537 }
538
539 #[new]
540 fn new_py(value: f64, unit: $unit) -> PyResult<Self> {
541 Ok(Self::new(value, unit))
542 }
543
544 fn close_abs(&self, other: PyRef<Self>, threshold: Self) -> bool {
545 (self.clone() - other.clone()).abs() <= threshold
546 }
547
548 fn close_rel(&self, other: PyRef<Self>, threshold: f64) -> bool {
549 let mean = (self.clone() + other.clone()) / 2.;
550 (self.clone() - other.clone()).abs() <= mean * threshold
551 }
552
553 fn __richcmp__(&self, other: PyRef<Self>, op: CompareOp) -> bool {
554 match op {
555 CompareOp::Lt => self.clone() < other.clone(),
556 CompareOp::Le => self.clone() <= other.clone(),
557 CompareOp::Eq => self.clone() == other.clone(),
558 CompareOp::Ne => self.clone() != other.clone(),
559 CompareOp::Gt => self.clone() > other.clone(),
560 CompareOp::Ge => self.clone() >= other.clone(),
561 }
562 }
563
564 fn __repr__(&self) -> PyResult<String> {
565 Ok(format!("{:?}", self))
566 }
567
568 fn __str__(&self) -> PyResult<String> {
569 Ok(format!("{:?}", self))
570 }
571
572 fn __add__(lhs: PyRef<Self>, rhs: PyRef<Self>) -> PyResult<Self> {
573 Ok(lhs.clone() + rhs.clone())
574 }
575
576 fn __neg__(lhs: PyRef<Self>) -> PyResult<Self> {
577 Ok(-lhs.clone())
578 }
579
580 fn __sub__(lhs: PyRef<Self>, rhs: PyRef<Self>) -> PyResult<Self> {
581 Ok(lhs.clone() - rhs.clone())
582 }
583
584 fn __abs__(&self) -> Self {
585 self.abs()
586 }
587
588 #[allow(clippy::wrong_self_convention)]
589 #[pyo3(name = "to")]
590 fn to_py(&self, unit: $unit) -> f64 {
591 self.to(unit)
592 }
593 }
594
595 impl $type {
596 pub fn optimal_unit(&self, choices: HashSet<$unit>) -> Option<$unit> {
597 let mut min_value = i32::MAX;
598 let mut best_unit = None;
599 for unit in choices {
600 let difference = (self.power - unit.base_per_x().1).abs();
601 if difference < min_value {
602 min_value = difference;
603 best_unit = Some(unit);
604 }
605 }
606 best_unit
607 }
608 }
609 };
610}
611
612#[macro_export]
613macro_rules! make_alias {
614 ($base_quantity:ty, $base_unit:ty, $alias_quantity:ident, $alias_unit:ident) => {
615 pub type $alias_unit = $base_unit;
616 pub type $alias_quantity = $base_quantity;
617 };
618}
619
620#[macro_export]
621macro_rules! impl_const {
622 ($type:ident, $name:ident, $multiplier:expr, $power:expr) => {
623 #[cfg(feature = "pyo3")]
624 #[pymethods]
625 impl $type {
626 #[staticmethod]
627 pub fn $name() -> Self {
628 Self {
629 multiplier: $multiplier,
630 power: $power,
631 }
632 }
633 }
634 #[cfg(not(feature = "pyo3"))]
635 impl $type {
636 pub fn $name() -> Self {
637 Self {
638 multiplier: $multiplier,
639 power: $power,
640 }
641 }
642 }
643 };
644}
645
646#[macro_export]
647macro_rules! impl_div_with_self_to_f64 {
648 ($lhs:ty) => {
649 impl Div<$lhs> for $lhs {
650 type Output = f64;
651
652 fn div(self, rhs: Self) -> Self::Output {
653 (self.multiplier / rhs.multiplier) * 10_f64.powi(self.power - rhs.power)
654 }
655 }
656 };
657}
658
659#[macro_export]
660macro_rules! impl_mul {
661 ($lhs:ty, $rhs:ty, $result:ty) => {
662 impl std::ops::Mul<$rhs> for $lhs {
663 type Output = $result;
664
665 fn mul(self, rhs: $rhs) -> Self::Output {
666 <$result>::from_exponential(
667 self.multiplier * rhs.multiplier,
668 self.power + rhs.power,
669 )
670 }
671 }
672
673 impl std::ops::Mul<$lhs> for $rhs {
674 type Output = $result;
675
676 fn mul(self, rhs: $lhs) -> Self::Output {
677 <$result>::from_exponential(
678 self.multiplier * rhs.multiplier,
679 self.power + rhs.power,
680 )
681 }
682 }
683
684 impl MulArray1<$rhs> for Array1<$lhs> {
685 type Output = Array1<$result>;
686
687 fn mul_array1(self, rhs: Array1<$rhs>) -> Array1<$result> {
688 self.into_iter()
689 .zip(rhs.into_iter())
690 .map(|(force, distance)| force * distance)
691 .collect()
692 }
693 }
694
695 impl MulArray2<$rhs> for Array2<$lhs> {
696 type Output = Array2<$result>;
697
698 fn mul_array2(self, rhs: Array2<$rhs>) -> Result<Array2<$result>, String> {
699 let mut results = Vec::new();
700
701 for (lhs_row, rhs_row) in self.outer_iter().zip(rhs.outer_iter()) {
702 let result_row: Array1<$result> = lhs_row
703 .iter()
704 .zip(rhs_row.iter())
705 .map(|(&lhs, &rhs)| lhs * rhs)
706 .collect();
707 results.push(result_row);
708 }
709
710 let nrows = results.len();
711 let ncols = if nrows > 0 { results[0].len() } else { 0 };
712 let data: Vec<$result> = results
713 .into_iter()
714 .flat_map(|r| {
715 let (raw_vec, _) = r.into_raw_vec_and_offset();
716 raw_vec
717 })
718 .collect();
719
720 Array2::from_shape_vec((nrows, ncols), data)
721 .map_err(|_| "Shape mismatch".to_string())
722 }
723 }
724
725 impl MulArray1<$lhs> for Array1<$rhs> {
726 type Output = Array1<$result>;
727
728 fn mul_array1(self, rhs: Array1<$lhs>) -> Array1<$result> {
729 self.into_iter()
730 .zip(rhs.into_iter())
731 .map(|(force, distance)| force * distance)
732 .collect()
733 }
734 }
735
736 impl MulArray2<$lhs> for Array2<$rhs> {
737 type Output = Array2<$result>;
738
739 fn mul_array2(self, rhs: Array2<$lhs>) -> Result<Array2<$result>, String> {
740 let mut results = Vec::new();
741
742 for (force_row, distance_row) in self.outer_iter().zip(rhs.outer_iter()) {
743 let result_row: Array1<$result> = force_row
744 .iter()
745 .zip(distance_row.iter())
746 .map(|(&force, &distance)| force * distance)
747 .collect();
748 results.push(result_row);
749 }
750
751 let nrows = results.len();
752 let ncols = if nrows > 0 { results[0].len() } else { 0 };
753 let data: Vec<$result> = results
754 .into_iter()
755 .flat_map(|r| {
756 let (raw_vec, _) = r.into_raw_vec_and_offset();
757 raw_vec
758 })
759 .collect();
760
761 Array2::from_shape_vec((nrows, ncols), data)
762 .map_err(|_| "Shape mismatch".to_string())
763 }
764 }
765 };
766}
767
768#[macro_export]
769macro_rules! impl_mul_with_self {
770 ($lhs:ty,$result:ty) => {
771 impl std::ops::Mul<$lhs> for $lhs {
772 type Output = $result;
773
774 fn mul(self, rhs: $lhs) -> Self::Output {
775 <$result>::from_exponential(
776 self.multiplier * rhs.multiplier,
777 self.power + rhs.power,
778 )
779 }
780 }
781
782 impl MulArray1<$lhs> for Array1<$lhs> {
783 type Output = Array1<$result>;
784
785 fn mul_array1(self, rhs: Array1<$lhs>) -> Array1<$result> {
786 self.into_iter()
787 .zip(rhs.into_iter())
788 .map(|(force, distance)| force * distance)
789 .collect()
790 }
791 }
792
793 impl MulArray2<$lhs> for Array2<$lhs> {
794 type Output = Array2<$result>;
795
796 fn mul_array2(self, rhs: Array2<$lhs>) -> Result<Array2<$result>, String> {
797 let mut results = Vec::new();
798
799 for (force_row, distance_row) in self.outer_iter().zip(rhs.outer_iter()) {
800 let result_row: Array1<$result> = force_row
801 .iter()
802 .zip(distance_row.iter())
803 .map(|(&force, &distance)| force * distance)
804 .collect();
805 results.push(result_row);
806 }
807
808 let nrows = results.len();
809 let ncols = if nrows > 0 { results[0].len() } else { 0 };
810 let data: Vec<$result> = results
811 .into_iter()
812 .flat_map(|r| {
813 let (raw_vec, _) = r.into_raw_vec_and_offset();
814 raw_vec
815 })
816 .collect();
817
818 Array2::from_shape_vec((nrows, ncols), data)
819 .map_err(|_| "Shape mismatch".to_string())
820 }
821 }
822 };
823}
824
825#[macro_export]
826macro_rules! impl_div {
827 ($lhs:ty, $rhs:ty, $result:ty) => {
828 impl std::ops::Div<$rhs> for $lhs {
829 type Output = $result;
830
831 fn div(self, rhs: $rhs) -> Self::Output {
832 <$result>::from_exponential(
833 self.multiplier / rhs.multiplier,
834 self.power - rhs.power,
835 )
836 }
837 }
838
839 impl DivArray1<$rhs> for Array1<$lhs> {
840 type Output = Array1<$result>;
841
842 fn div_array1(self, rhs: Array1<$rhs>) -> Array1<$result> {
843 self.into_iter()
844 .zip(rhs.into_iter())
845 .map(|(force, distance)| force / distance)
846 .collect()
847 }
848 }
849
850 impl DivArray2<$rhs> for Array2<$lhs> {
851 type Output = Array2<$result>;
852
853 fn div_array2(self, rhs: Array2<$rhs>) -> Result<Array2<$result>, String> {
854 let mut results = Vec::new();
855
856 for (force_row, distance_row) in self.outer_iter().zip(rhs.outer_iter()) {
857 let result_row: Array1<$result> = force_row
858 .iter()
859 .zip(distance_row.iter())
860 .map(|(&force, &distance)| force / distance)
861 .collect();
862 results.push(result_row);
863 }
864
865 let nrows = results.len();
866 let ncols = if nrows > 0 { results[0].len() } else { 0 };
867 let data: Vec<$result> = results
868 .into_iter()
869 .flat_map(|r| {
870 let (raw_vec, _) = r.into_raw_vec_and_offset();
871 raw_vec
872 })
873 .collect();
874
875 Array2::from_shape_vec((nrows, ncols), data)
876 .map_err(|_| "Shape mismatch".to_string())
877 }
878 }
879 };
880}
881
882#[macro_export]
883macro_rules! impl_sqrt {
884 ($lhs:ty, $res:ty) => {
885 impl Sqrt<$res> for $lhs {
886 fn sqrt(self) -> $res {
887 if self.power % 2 == 0 {
888 <$res>::from_exponential(self.multiplier.sqrt(), self.power / 2)
889 } else {
890 <$res>::from_exponential(
891 self.multiplier.sqrt() * 10_f64.sqrt(),
892 (self.power - 1) / 2,
893 )
894 }
895 }
896 }
897 #[cfg(feature = "pyo3")]
898 #[pymethods]
899 impl $lhs {
900 #[pyo3(name = "sqrt")]
901 fn sqrt_py(&self) -> $res {
902 self.sqrt()
903 }
904 }
905 };
906}
907
908#[macro_export]
909macro_rules! impl_mul_relation_with_self {
910 ($lhs:ty, $res:ty) => {
911 impl_mul_with_self!($lhs, $res);
912 impl_sqrt!($res, $lhs);
913 impl_div!($res, $lhs, $lhs);
914 };
915}
916
917#[macro_export]
918macro_rules! impl_mul_relation_with_other {
919 ($lhs:ty, $rhs:ty, $res:ty) => {
920 impl_mul!($lhs, $rhs, $res);
921 impl_div!($res, $lhs, $rhs);
922 impl_div!($res, $rhs, $lhs);
923 };
924}
925
926pub mod macros {
927 pub use crate::impl_const;
928 pub use crate::impl_div;
929 pub use crate::impl_div_with_self_to_f64;
930 pub use crate::impl_mul;
931 pub use crate::impl_mul_relation_with_other;
932 pub use crate::impl_mul_relation_with_self;
933 pub use crate::impl_mul_with_self;
934 pub use crate::impl_quantity;
935 pub use crate::impl_sqrt;
936 pub use crate::make_alias;
937}