Skip to main content

unitforge/
impl_macros.rs

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}