avx_arrow/
scientific.rs

1//! Scientific types for physics and astrophysics
2
3use std::ops::{Add, Mul, Sub};
4
5/// Quaternion for 4D rotations
6///
7/// Used in physics simulations, spacecraft orientation, and 3D graphics.
8/// Components: w (scalar), x, y, z (vector)
9#[derive(Debug, Clone, Copy, PartialEq)]
10pub struct Quaternion {
11    /// Scalar component (real part)
12    pub w: f64,
13    /// X component (i)
14    pub x: f64,
15    /// Y component (j)
16    pub y: f64,
17    /// Z component (k)
18    pub z: f64,
19}
20
21impl Quaternion {
22    /// Create a new quaternion
23    pub fn new(w: f64, x: f64, y: f64, z: f64) -> Self {
24        Self { w, x, y, z }
25    }
26
27    /// Create identity quaternion (no rotation)
28    pub fn identity() -> Self {
29        Self::new(1.0, 0.0, 0.0, 0.0)
30    }
31
32    /// Create quaternion from axis and angle
33    pub fn from_axis_angle(axis: [f64; 3], angle: f64) -> Self {
34        let half_angle = angle / 2.0;
35        let sin_half = half_angle.sin();
36        let cos_half = half_angle.cos();
37
38        // Normalize axis
39        let len = (axis[0] * axis[0] + axis[1] * axis[1] + axis[2] * axis[2]).sqrt();
40        let norm_axis = [axis[0] / len, axis[1] / len, axis[2] / len];
41
42        Self {
43            w: cos_half,
44            x: norm_axis[0] * sin_half,
45            y: norm_axis[1] * sin_half,
46            z: norm_axis[2] * sin_half,
47        }
48    }
49
50    /// Get the magnitude (norm) of the quaternion
51    pub fn magnitude(&self) -> f64 {
52        (self.w * self.w + self.x * self.x + self.y * self.y + self.z * self.z).sqrt()
53    }
54
55    /// Normalize the quaternion (magnitude = 1)
56    pub fn normalize(&self) -> Self {
57        let mag = self.magnitude();
58        if mag > 0.0 {
59            Self {
60                w: self.w / mag,
61                x: self.x / mag,
62                y: self.y / mag,
63                z: self.z / mag,
64            }
65        } else {
66            Self::identity()
67        }
68    }
69
70    /// Get the conjugate (inverse rotation)
71    pub fn conjugate(&self) -> Self {
72        Self {
73            w: self.w,
74            x: -self.x,
75            y: -self.y,
76            z: -self.z,
77        }
78    }
79
80    /// Get the inverse
81    pub fn inverse(&self) -> Self {
82        let mag_sq = self.w * self.w + self.x * self.x + self.y * self.y + self.z * self.z;
83        if mag_sq > 0.0 {
84            let conj = self.conjugate();
85            Self {
86                w: conj.w / mag_sq,
87                x: conj.x / mag_sq,
88                y: conj.y / mag_sq,
89                z: conj.z / mag_sq,
90            }
91        } else {
92            Self::identity()
93        }
94    }
95
96    /// Rotate a 3D vector by this quaternion
97    pub fn rotate_vector(&self, v: [f64; 3]) -> [f64; 3] {
98        let q = self.normalize();
99        let v_quat = Quaternion::new(0.0, v[0], v[1], v[2]);
100        let result = q * v_quat * q.conjugate();
101        [result.x, result.y, result.z]
102    }
103}
104
105impl Mul for Quaternion {
106    type Output = Self;
107
108    fn mul(self, other: Self) -> Self {
109        Self {
110            w: self.w * other.w - self.x * other.x - self.y * other.y - self.z * other.z,
111            x: self.w * other.x + self.x * other.w + self.y * other.z - self.z * other.y,
112            y: self.w * other.y - self.x * other.z + self.y * other.w + self.z * other.x,
113            z: self.w * other.z + self.x * other.y - self.y * other.x + self.z * other.w,
114        }
115    }
116}
117
118impl Add for Quaternion {
119    type Output = Self;
120
121    fn add(self, other: Self) -> Self {
122        Self {
123            w: self.w + other.w,
124            x: self.x + other.x,
125            y: self.y + other.y,
126            z: self.z + other.z,
127        }
128    }
129}
130
131impl Sub for Quaternion {
132    type Output = Self;
133
134    fn sub(self, other: Self) -> Self {
135        Self {
136            w: self.w - other.w,
137            x: self.x - other.x,
138            y: self.y - other.y,
139            z: self.z - other.z,
140        }
141    }
142}
143
144/// Complex number (for FFT, quantum mechanics)
145#[derive(Debug, Clone, Copy, PartialEq)]
146pub struct Complex64 {
147    /// Real part
148    pub re: f64,
149    /// Imaginary part
150    pub im: f64,
151}
152
153impl Complex64 {
154    /// Create a new complex number
155    pub fn new(re: f64, im: f64) -> Self {
156        Self { re, im }
157    }
158
159    /// Create from polar coordinates (magnitude, phase)
160    pub fn from_polar(r: f64, theta: f64) -> Self {
161        Self {
162            re: r * theta.cos(),
163            im: r * theta.sin(),
164        }
165    }
166
167    /// Get magnitude
168    pub fn magnitude(&self) -> f64 {
169        (self.re * self.re + self.im * self.im).sqrt()
170    }
171
172    /// Get phase (angle)
173    pub fn phase(&self) -> f64 {
174        self.im.atan2(self.re)
175    }
176
177    /// Get complex conjugate
178    pub fn conjugate(&self) -> Self {
179        Self {
180            re: self.re,
181            im: -self.im,
182        }
183    }
184}
185
186impl Mul for Complex64 {
187    type Output = Self;
188
189    fn mul(self, other: Self) -> Self {
190        Self {
191            re: self.re * other.re - self.im * other.im,
192            im: self.re * other.im + self.im * other.re,
193        }
194    }
195}
196
197impl Add for Complex64 {
198    type Output = Self;
199
200    fn add(self, other: Self) -> Self {
201        Self {
202            re: self.re + other.re,
203            im: self.im + other.im,
204        }
205    }
206}
207
208/// Tensor4D for General Relativity (4x4 spacetime tensor)
209#[derive(Debug, Clone, Copy, PartialEq)]
210pub struct Tensor4D {
211    /// 4x4 matrix components [row][col]
212    pub components: [[f64; 4]; 4],
213}
214
215impl Tensor4D {
216    /// Create a new tensor with all zeros
217    pub fn zeros() -> Self {
218        Self {
219            components: [[0.0; 4]; 4],
220        }
221    }
222
223    /// Create identity tensor
224    pub fn identity() -> Self {
225        let mut tensor = Self::zeros();
226        for i in 0..4 {
227            tensor.components[i][i] = 1.0;
228        }
229        tensor
230    }
231
232    /// Create Minkowski metric (flat spacetime)
233    pub fn minkowski() -> Self {
234        let mut tensor = Self::zeros();
235        tensor.components[0][0] = -1.0; // Time component (signature -+++)
236        tensor.components[1][1] = 1.0;
237        tensor.components[2][2] = 1.0;
238        tensor.components[3][3] = 1.0;
239        tensor
240    }
241
242    /// Create Schwarzschild metric (black hole)
243    pub fn schwarzschild_metric(mass: f64, r: f64) -> Self {
244        let rs = 2.0 * mass; // Schwarzschild radius (G=c=1)
245        let mut tensor = Self::zeros();
246
247        if r > rs {
248            tensor.components[0][0] = -(1.0 - rs / r); // g_tt
249            tensor.components[1][1] = 1.0 / (1.0 - rs / r); // g_rr
250            tensor.components[2][2] = r * r; // g_θθ
251            tensor.components[3][3] = r * r * r.sin().powi(2); // g_φφ (assuming θ=π/2)
252        }
253
254        tensor
255    }
256
257    /// Get component at (row, col)
258    pub fn get(&self, row: usize, col: usize) -> f64 {
259        self.components[row][col]
260    }
261
262    /// Set component at (row, col)
263    pub fn set(&mut self, row: usize, col: usize, value: f64) {
264        self.components[row][col] = value;
265    }
266
267    /// Calculate determinant (for volume elements)
268    pub fn determinant(&self) -> f64 {
269        // Simplified 4x4 determinant calculation
270        // Full implementation would use cofactor expansion
271        let m = &self.components;
272
273        // Using first row expansion
274        let mut det = 0.0;
275        for j in 0..4 {
276            let sign = if j % 2 == 0 { 1.0 } else { -1.0 };
277            det += sign * m[0][j] * self.minor_3x3(0, j);
278        }
279        det
280    }
281
282    fn minor_3x3(&self, skip_row: usize, skip_col: usize) -> f64 {
283        let mut m3x3 = [[0.0; 3]; 3];
284        let mut i3 = 0;
285        for i in 0..4 {
286            if i == skip_row {
287                continue;
288            }
289            let mut j3 = 0;
290            for j in 0..4 {
291                if j == skip_col {
292                    continue;
293                }
294                m3x3[i3][j3] = self.components[i][j];
295                j3 += 1;
296            }
297            i3 += 1;
298        }
299
300        // 3x3 determinant
301        m3x3[0][0] * (m3x3[1][1] * m3x3[2][2] - m3x3[1][2] * m3x3[2][1])
302            - m3x3[0][1] * (m3x3[1][0] * m3x3[2][2] - m3x3[1][2] * m3x3[2][0])
303            + m3x3[0][2] * (m3x3[1][0] * m3x3[2][1] - m3x3[1][1] * m3x3[2][0])
304    }
305}
306
307/// Spinor for particle physics (Dirac spinor)
308#[derive(Debug, Clone, Copy, PartialEq)]
309pub struct Spinor {
310    /// Upper component (spin up)
311    pub up: Complex64,
312    /// Lower component (spin down)
313    pub down: Complex64,
314}
315
316impl Spinor {
317    /// Create a new spinor
318    pub fn new(up: Complex64, down: Complex64) -> Self {
319        Self { up, down }
320    }
321
322    /// Create spin-up state
323    pub fn spin_up() -> Self {
324        Self {
325            up: Complex64::new(1.0, 0.0),
326            down: Complex64::new(0.0, 0.0),
327        }
328    }
329
330    /// Create spin-down state
331    pub fn spin_down() -> Self {
332        Self {
333            up: Complex64::new(0.0, 0.0),
334            down: Complex64::new(1.0, 0.0),
335        }
336    }
337
338    /// Get norm (probability amplitude)
339    pub fn norm(&self) -> f64 {
340        (self.up.magnitude().powi(2) + self.down.magnitude().powi(2)).sqrt()
341    }
342
343    /// Normalize spinor
344    pub fn normalize(&self) -> Self {
345        let n = self.norm();
346        if n > 0.0 {
347            Self {
348                up: Complex64::new(self.up.re / n, self.up.im / n),
349                down: Complex64::new(self.down.re / n, self.down.im / n),
350            }
351        } else {
352            Self::spin_up()
353        }
354    }
355}
356
357// ==================== SCIENTIFIC ARRAYS ====================
358
359/// Quaternion array for columnar storage
360#[derive(Debug, Clone)]
361pub struct QuaternionArray {
362    data: Vec<Quaternion>,
363}
364
365impl QuaternionArray {
366    /// Create new QuaternionArray
367    pub fn new(data: Vec<Quaternion>) -> Self {
368        Self { data }
369    }
370
371    /// Create from iterator
372    pub fn from_iter<I: IntoIterator<Item = Quaternion>>(iter: I) -> Self {
373        Self {
374            data: iter.into_iter().collect(),
375        }
376    }
377
378    /// Get value at index
379    pub fn value(&self, index: usize) -> Option<Quaternion> {
380        self.data.get(index).copied()
381    }
382
383    /// Get all values
384    pub fn values(&self) -> &[Quaternion] {
385        &self.data
386    }
387
388    /// Get length
389    pub fn len(&self) -> usize {
390        self.data.len()
391    }
392
393    /// Check if empty
394    pub fn is_empty(&self) -> bool {
395        self.data.is_empty()
396    }
397
398    /// Element-wise multiplication
399    pub fn multiply(&self, other: &Self) -> Option<Self> {
400        if self.len() != other.len() {
401            return None;
402        }
403
404        let result: Vec<Quaternion> = self
405            .data
406            .iter()
407            .zip(other.data.iter())
408            .map(|(a, b)| *a * *b)
409            .collect();
410
411        Some(Self::new(result))
412    }
413
414    /// Normalize all quaternions
415    pub fn normalize(&self) -> Self {
416        let result: Vec<Quaternion> = self.data.iter().map(|q| q.normalize()).collect();
417        Self::new(result)
418    }
419
420    /// Conjugate all quaternions
421    pub fn conjugate(&self) -> Self {
422        let result: Vec<Quaternion> = self.data.iter().map(|q| q.conjugate()).collect();
423        Self::new(result)
424    }
425
426    /// SLERP (Spherical Linear Interpolation) between two quaternion arrays
427    pub fn slerp(&self, other: &Self, t: f64) -> Option<Self> {
428        if self.len() != other.len() {
429            return None;
430        }
431
432        let result: Vec<Quaternion> = self
433            .data
434            .iter()
435            .zip(other.data.iter())
436            .map(|(a, b)| {
437                // Simple SLERP implementation
438                let dot = a.w * b.w + a.x * b.x + a.y * b.y + a.z * b.z;
439                let theta = dot.acos();
440                let sin_theta = theta.sin();
441
442                if sin_theta.abs() < 1e-10 {
443                    *a // Quaternions are very close
444                } else {
445                    let w1 = ((1.0 - t) * theta).sin() / sin_theta;
446                    let w2 = (t * theta).sin() / sin_theta;
447
448                    Quaternion::new(
449                        a.w * w1 + b.w * w2,
450                        a.x * w1 + b.x * w2,
451                        a.y * w1 + b.y * w2,
452                        a.z * w1 + b.z * w2,
453                    )
454                }
455            })
456            .collect();
457
458        Some(Self::new(result))
459    }
460}
461
462/// Complex number array for columnar storage
463#[derive(Debug, Clone)]
464pub struct ComplexArray {
465    data: Vec<Complex64>,
466}
467
468impl ComplexArray {
469    /// Create new ComplexArray
470    pub fn new(data: Vec<Complex64>) -> Self {
471        Self { data }
472    }
473
474    /// Create from iterator
475    pub fn from_iter<I: IntoIterator<Item = Complex64>>(iter: I) -> Self {
476        Self {
477            data: iter.into_iter().collect(),
478        }
479    }
480
481    /// Get value at index
482    pub fn value(&self, index: usize) -> Option<Complex64> {
483        self.data.get(index).copied()
484    }
485
486    /// Get all values
487    pub fn values(&self) -> &[Complex64] {
488        &self.data
489    }
490
491    /// Get length
492    pub fn len(&self) -> usize {
493        self.data.len()
494    }
495
496    /// Check if empty
497    pub fn is_empty(&self) -> bool {
498        self.data.is_empty()
499    }
500
501    /// Element-wise multiplication
502    pub fn multiply(&self, other: &Self) -> Option<Self> {
503        if self.len() != other.len() {
504            return None;
505        }
506
507        let result: Vec<Complex64> = self
508            .data
509            .iter()
510            .zip(other.data.iter())
511            .map(|(a, b)| *a * *b)
512            .collect();
513
514        Some(Self::new(result))
515    }
516
517    /// Element-wise addition
518    pub fn add(&self, other: &Self) -> Option<Self> {
519        if self.len() != other.len() {
520            return None;
521        }
522
523        let result: Vec<Complex64> = self
524            .data
525            .iter()
526            .zip(other.data.iter())
527            .map(|(a, b)| *a + *b)
528            .collect();
529
530        Some(Self::new(result))
531    }
532
533    /// Get magnitude of all elements
534    pub fn magnitude(&self) -> Vec<f64> {
535        self.data.iter().map(|c| c.magnitude()).collect()
536    }
537
538    /// Get phase of all elements
539    pub fn phase(&self) -> Vec<f64> {
540        self.data.iter().map(|c| c.phase()).collect()
541    }
542
543    /// Conjugate all elements
544    pub fn conjugate(&self) -> Self {
545        let result: Vec<Complex64> = self.data.iter().map(|c| c.conjugate()).collect();
546        Self::new(result)
547    }
548}
549
550/// Tensor4D array for columnar storage
551#[derive(Debug, Clone)]
552pub struct Tensor4DArray {
553    data: Vec<Tensor4D>,
554}
555
556impl Tensor4DArray {
557    /// Create new Tensor4DArray
558    pub fn new(data: Vec<Tensor4D>) -> Self {
559        Self { data }
560    }
561
562    /// Create from iterator
563    pub fn from_iter<I: IntoIterator<Item = Tensor4D>>(iter: I) -> Self {
564        Self {
565            data: iter.into_iter().collect(),
566        }
567    }
568
569    /// Get value at index
570    pub fn value(&self, index: usize) -> Option<Tensor4D> {
571        self.data.get(index).copied()
572    }
573
574    /// Get all values
575    pub fn values(&self) -> &[Tensor4D] {
576        &self.data
577    }
578
579    /// Get length
580    pub fn len(&self) -> usize {
581        self.data.len()
582    }
583
584    /// Check if empty
585    pub fn is_empty(&self) -> bool {
586        self.data.is_empty()
587    }
588}
589
590/// Spinor array for columnar storage
591#[derive(Debug, Clone)]
592pub struct SpinorArray {
593    data: Vec<Spinor>,
594}
595
596impl SpinorArray {
597    /// Create new SpinorArray
598    pub fn new(data: Vec<Spinor>) -> Self {
599        Self { data }
600    }
601
602    /// Create from iterator
603    pub fn from_iter<I: IntoIterator<Item = Spinor>>(iter: I) -> Self {
604        Self {
605            data: iter.into_iter().collect(),
606        }
607    }
608
609    /// Get value at index
610    pub fn value(&self, index: usize) -> Option<Spinor> {
611        self.data.get(index).copied()
612    }
613
614    /// Get all values
615    pub fn values(&self) -> &[Spinor] {
616        &self.data
617    }
618
619    /// Get length
620    pub fn len(&self) -> usize {
621        self.data.len()
622    }
623
624    /// Check if empty
625    pub fn is_empty(&self) -> bool {
626        self.data.is_empty()
627    }
628
629    /// Normalize all spinors
630    pub fn normalize(&self) -> Self {
631        let result: Vec<Spinor> = self.data.iter().map(|s| s.normalize()).collect();
632        Self::new(result)
633    }
634}
635
636#[cfg(test)]
637mod tests {
638    use super::*;
639
640    #[test]
641    fn test_quaternion_identity() {
642        let q = Quaternion::identity();
643        assert_eq!(q.w, 1.0);
644        assert_eq!(q.x, 0.0);
645        assert_eq!(q.magnitude(), 1.0);
646    }
647
648    #[test]
649    fn test_quaternion_multiplication() {
650        let q1 = Quaternion::new(1.0, 0.0, 0.0, 0.0);
651        let q2 = Quaternion::new(0.0, 1.0, 0.0, 0.0);
652        let result = q1 * q2;
653        assert_eq!(result.x, 1.0);
654    }
655
656    #[test]
657    fn test_quaternion_normalize() {
658        let q = Quaternion::new(2.0, 0.0, 0.0, 0.0);
659        let normalized = q.normalize();
660        assert!((normalized.magnitude() - 1.0).abs() < 1e-10);
661    }
662
663    #[test]
664    fn test_complex_magnitude() {
665        let c = Complex64::new(3.0, 4.0);
666        assert_eq!(c.magnitude(), 5.0);
667    }
668
669    #[test]
670    fn test_complex_multiplication() {
671        let c1 = Complex64::new(1.0, 2.0);
672        let c2 = Complex64::new(3.0, 4.0);
673        let result = c1 * c2;
674        assert_eq!(result.re, -5.0);
675        assert_eq!(result.im, 10.0);
676    }
677
678    #[test]
679    fn test_tensor4d_minkowski() {
680        let metric = Tensor4D::minkowski();
681        assert_eq!(metric.get(0, 0), -1.0);
682        assert_eq!(metric.get(1, 1), 1.0);
683        assert_eq!(metric.get(2, 2), 1.0);
684        assert_eq!(metric.get(3, 3), 1.0);
685    }
686
687    #[test]
688    fn test_tensor4d_schwarzschild() {
689        let metric = Tensor4D::schwarzschild_metric(1.0, 10.0);
690        assert!(metric.get(0, 0) < 0.0); // Time component negative
691        assert!(metric.get(1, 1) > 0.0); // Spatial component positive
692    }
693
694    #[test]
695    fn test_spinor_creation() {
696        let spinor = Spinor::spin_up();
697        assert_eq!(spinor.up.re, 1.0);
698        assert_eq!(spinor.down.re, 0.0);
699        assert!((spinor.norm() - 1.0).abs() < 1e-10);
700    }
701
702    #[test]
703    fn test_spinor_normalize() {
704        let spinor = Spinor::new(
705            Complex64::new(2.0, 0.0),
706            Complex64::new(2.0, 0.0),
707        );
708        let normalized = spinor.normalize();
709        assert!((normalized.norm() - 1.0).abs() < 1e-10);
710    }
711
712    // ==================== ARRAY TESTS ====================
713
714    #[test]
715    fn test_quaternion_array_creation() {
716        let q1 = Quaternion::identity();
717        let q2 = Quaternion::new(0.707, 0.707, 0.0, 0.0);
718        let array = QuaternionArray::new(vec![q1, q2]);
719
720        assert_eq!(array.len(), 2);
721        assert_eq!(array.value(0), Some(q1));
722        assert_eq!(array.value(1), Some(q2));
723    }
724
725    #[test]
726    fn test_quaternion_array_multiply() {
727        let q1 = Quaternion::identity();
728        let q2 = Quaternion::new(0.0, 1.0, 0.0, 0.0);
729        let array1 = QuaternionArray::new(vec![q1, q1]);
730        let array2 = QuaternionArray::new(vec![q2, q2]);
731
732        let result = array1.multiply(&array2).unwrap();
733        assert_eq!(result.len(), 2);
734        assert_eq!(result.value(0).unwrap().x, 1.0);
735    }
736
737    #[test]
738    fn test_quaternion_array_normalize() {
739        let q1 = Quaternion::new(2.0, 0.0, 0.0, 0.0);
740        let q2 = Quaternion::new(0.0, 3.0, 0.0, 0.0);
741        let array = QuaternionArray::new(vec![q1, q2]);
742
743        let normalized = array.normalize();
744        assert!((normalized.value(0).unwrap().magnitude() - 1.0).abs() < 1e-10);
745        assert!((normalized.value(1).unwrap().magnitude() - 1.0).abs() < 1e-10);
746    }
747
748    #[test]
749    fn test_quaternion_array_slerp() {
750        let q1 = Quaternion::identity();
751        let q2 = Quaternion::from_axis_angle([0.0, 0.0, 1.0], std::f64::consts::PI);
752        let array1 = QuaternionArray::new(vec![q1, q1]);
753        let array2 = QuaternionArray::new(vec![q2, q2]);
754
755        let result = array1.slerp(&array2, 0.5).unwrap();
756        assert_eq!(result.len(), 2);
757        assert!((result.value(0).unwrap().magnitude() - 1.0).abs() < 1e-10);
758    }
759
760    #[test]
761    fn test_complex_array_creation() {
762        let c1 = Complex64::new(1.0, 2.0);
763        let c2 = Complex64::new(3.0, 4.0);
764        let array = ComplexArray::new(vec![c1, c2]);
765
766        assert_eq!(array.len(), 2);
767        assert_eq!(array.value(0), Some(c1));
768        assert_eq!(array.value(1), Some(c2));
769    }
770
771    #[test]
772    fn test_complex_array_multiply() {
773        let c1 = Complex64::new(1.0, 2.0);
774        let c2 = Complex64::new(3.0, 4.0);
775        let array1 = ComplexArray::new(vec![c1, c1]);
776        let array2 = ComplexArray::new(vec![c2, c2]);
777
778        let result = array1.multiply(&array2).unwrap();
779        assert_eq!(result.len(), 2);
780        assert_eq!(result.value(0).unwrap().re, -5.0);
781        assert_eq!(result.value(0).unwrap().im, 10.0);
782    }
783
784    #[test]
785    fn test_complex_array_add() {
786        let c1 = Complex64::new(1.0, 2.0);
787        let c2 = Complex64::new(3.0, 4.0);
788        let array1 = ComplexArray::new(vec![c1, c1]);
789        let array2 = ComplexArray::new(vec![c2, c2]);
790
791        let result = array1.add(&array2).unwrap();
792        assert_eq!(result.len(), 2);
793        assert_eq!(result.value(0).unwrap().re, 4.0);
794        assert_eq!(result.value(0).unwrap().im, 6.0);
795    }
796
797    #[test]
798    fn test_complex_array_magnitude() {
799        let c1 = Complex64::new(3.0, 4.0);
800        let c2 = Complex64::new(5.0, 12.0);
801        let array = ComplexArray::new(vec![c1, c2]);
802
803        let magnitudes = array.magnitude();
804        assert_eq!(magnitudes[0], 5.0);
805        assert_eq!(magnitudes[1], 13.0);
806    }
807
808    #[test]
809    fn test_complex_array_phase() {
810        let c1 = Complex64::new(1.0, 1.0);
811        let array = ComplexArray::new(vec![c1]);
812
813        let phases = array.phase();
814        assert!((phases[0] - std::f64::consts::PI / 4.0).abs() < 1e-10);
815    }
816
817    #[test]
818    fn test_complex_array_conjugate() {
819        let c1 = Complex64::new(1.0, 2.0);
820        let c2 = Complex64::new(3.0, 4.0);
821        let array = ComplexArray::new(vec![c1, c2]);
822
823        let conj = array.conjugate();
824        assert_eq!(conj.value(0).unwrap().re, 1.0);
825        assert_eq!(conj.value(0).unwrap().im, -2.0);
826        assert_eq!(conj.value(1).unwrap().re, 3.0);
827        assert_eq!(conj.value(1).unwrap().im, -4.0);
828    }
829
830    #[test]
831    fn test_tensor4d_array_creation() {
832        let t1 = Tensor4D::minkowski();
833        let t2 = Tensor4D::zeros();
834        let array = Tensor4DArray::new(vec![t1, t2]);
835
836        assert_eq!(array.len(), 2);
837        assert_eq!(array.value(0), Some(t1));
838        assert_eq!(array.value(1), Some(t2));
839    }
840
841    #[test]
842    fn test_spinor_array_creation() {
843        let s1 = Spinor::spin_up();
844        let s2 = Spinor::spin_down();
845        let array = SpinorArray::new(vec![s1, s2]);
846
847        assert_eq!(array.len(), 2);
848        assert_eq!(array.value(0), Some(s1));
849        assert_eq!(array.value(1), Some(s2));
850    }
851
852    #[test]
853    fn test_spinor_array_normalize() {
854        let s1 = Spinor::new(Complex64::new(2.0, 0.0), Complex64::new(0.0, 0.0));
855        let s2 = Spinor::new(Complex64::new(0.0, 0.0), Complex64::new(3.0, 0.0));
856        let array = SpinorArray::new(vec![s1, s2]);
857
858        let normalized = array.normalize();
859        assert!((normalized.value(0).unwrap().norm() - 1.0).abs() < 1e-10);
860        assert!((normalized.value(1).unwrap().norm() - 1.0).abs() < 1e-10);
861    }
862}
863
864
865
866
867