unitforge/
impl_macros.rs

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