## Coverage Report

#### Created: 2026-01-25 15:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)

    /home/noah/src/trueno/src/vector/ops/arithmetic.rs

    Line
    Count
    Source
    1
    //! Arithmetic operations for Vector<f32>
    2
    //!
    3
    //! This module provides element-wise arithmetic operations:
    4
    //! - Basic: `add`, `sub`, `mul`, `div`
    5
    //! - Scalar: `scale`
    6
    //! - Fused: `fma` (fused multiply-add)
    7
    8
    #[cfg(target_arch = "x86_64")]
    9
    use crate::backends::avx2::Avx2Backend;
    10
    #[cfg(any(target_arch = "aarch64", target_arch = "arm"))]
    11
    use crate::backends::neon::NeonBackend;
    12
    use crate::backends::scalar::ScalarBackend;
    13
    #[cfg(target_arch = "x86_64")]
    14
    use crate::backends::sse2::Sse2Backend;
    15
    #[cfg(target_arch = "wasm32")]
    16
    use crate::backends::wasm::WasmBackend;
    17
    use crate::backends::VectorBackend;
    18
    use crate::vector::Vector;
    19
    use crate::{dispatch_binary_op, Backend, Result, TruenoError};
    20
    21
    impl Vector<f32> {
    22
        /// Element-wise addition
    23
        ///
    24
        /// # Performance
    25
        ///
    26
        /// Auto-selects the best available backend:
    27
        /// - **AVX2**: ~4x faster than scalar for 1K+ elements
    28
        /// - **GPU**: ~50x faster than scalar for 10M+ elements
    29
        ///
    30
        /// # Examples
    31
        ///
    32
        /// ```
    33
        /// use trueno::Vector;
    34
        ///
    35
        /// let a = Vector::from_slice(&[1.0, 2.0, 3.0]);
    36
        /// let b = Vector::from_slice(&[4.0, 5.0, 6.0]);
    37
        /// let result = a.add(&b)?;
    38
        ///
    39
        /// assert_eq!(result.as_slice(), &[5.0, 7.0, 9.0]);
    40
        /// # Ok::<(), trueno::TruenoError>(())
    41
        /// ```
    42
        ///
    43
        /// # Errors
    44
        ///
    45
        /// Returns [`TruenoError::SizeMismatch`] if vectors have different lengths.
    46
    15
        pub fn add(&self, other: &Self) -> Result<Self> {
    47
    15
            if self.len() != other.len() {
    48
    0
                return Err(TruenoError::SizeMismatch {
    49
    0
                    expected: self.len(),
    50
    0
                    actual: other.len(),
    51
    0
                });
    52
    15
            }
    53
    54
    15
            let mut result = vec![0.0; self.len()];
    55
    56
            // Use parallel processing for large arrays
    57
            #[cfg(feature = "parallel")]
    58
            {
    59
                const PARALLEL_THRESHOLD: usize = 100_000; // Threshold for element-wise ops
    60
                const CHUNK_SIZE: usize = 65536; // 64K elements = 256KB, cache-friendly
    61
    62
                if self.len() >= PARALLEL_THRESHOLD {
    63
                    use rayon::prelude::*;
    64
    65
                    self.data
    66
                        .par_chunks(CHUNK_SIZE)
    67
                        .zip(other.data.par_chunks(CHUNK_SIZE))
    68
                        .zip(result.par_chunks_mut(CHUNK_SIZE))
    69
                        .for_each(|((chunk_a, chunk_b), chunk_out)| {
    70
                            dispatch_binary_op!(self.backend, add, chunk_a, chunk_b, chunk_out);
    71
                        });
    72
    73
                    return Ok(Self {
    74
                        data: result,
    75
                        backend: self.backend,
    76
                    });
    77
                }
    78
            }
    79
    80
    15
            dispatch_binary_op!0(self.backend, add, &self.data0, &other.data0, &mut result0);
    81
    82
    15
            Ok(Self {
    83
    15
                data: result,
    84
    15
                backend: self.backend,
    85
    15
            })
    86
    15
        }
    87
    88
        /// Element-wise subtraction
    89
        ///
    90
        /// # Performance
    91
        ///
    92
        /// Auto-selects the best available backend:
    93
        /// - **AVX2**: ~4x faster than scalar for 1K+ elements
    94
        /// - **GPU**: ~50x faster than scalar for 10M+ elements
    95
        ///
    96
        /// # Examples
    97
        ///
    98
        /// ```
    99
        /// use trueno::Vector;
    100
        ///
    101
        /// let a = Vector::from_slice(&[5.0, 7.0, 9.0]);
    102
        /// let b = Vector::from_slice(&[1.0, 2.0, 3.0]);
    103
        /// let result = a.sub(&b)?;
    104
        ///
    105
        /// assert_eq!(result.as_slice(), &[4.0, 5.0, 6.0]);
    106
        /// # Ok::<(), trueno::TruenoError>(())
    107
        /// ```
    108
        ///
    109
        /// # Errors
    110
        ///
    111
        /// Returns [`TruenoError::SizeMismatch`] if vectors have different lengths.
    112
    15
        pub fn sub(&self, other: &Self) -> Result<Self> {
    113
    15
            if self.len() != other.len() {
    114
    0
                return Err(TruenoError::SizeMismatch {
    115
    0
                    expected: self.len(),
    116
    0
                    actual: other.len(),
    117
    0
                });
    118
    15
            }
    119
    120
    15
            let mut result = vec![0.0; self.len()];
    121
    122
            // Use parallel processing for large arrays
    123
            #[cfg(feature = "parallel")]
    124
            {
    125
                const PARALLEL_THRESHOLD: usize = 100_000;
    126
                const CHUNK_SIZE: usize = 65536;
    127
    128
                if self.len() >= PARALLEL_THRESHOLD {
    129
                    use rayon::prelude::*;
    130
    131
                    self.data
    132
                        .par_chunks(CHUNK_SIZE)
    133
                        .zip(other.data.par_chunks(CHUNK_SIZE))
    134
                        .zip(result.par_chunks_mut(CHUNK_SIZE))
    135
                        .for_each(|((chunk_a, chunk_b), chunk_out)| {
    136
                            dispatch_binary_op!(self.backend, sub, chunk_a, chunk_b, chunk_out);
    137
                        });
    138
    139
                    return Ok(Self {
    140
                        data: result,
    141
                        backend: self.backend,
    142
                    });
    143
                }
    144
            }
    145
    146
    15
            dispatch_binary_op!0(self.backend, sub, &self.data0, &other.data0, &mut result0);
    147
    148
    15
            Ok(Self {
    149
    15
                data: result,
    150
    15
                backend: self.backend,
    151
    15
            })
    152
    15
        }
    153
    154
        /// Element-wise multiplication
    155
        ///
    156
        /// # Examples
    157
        ///
    158
        /// ```
    159
        /// use trueno::Vector;
    160
        ///
    161
        /// let a = Vector::from_slice(&[2.0, 3.0, 4.0]);
    162
        /// let b = Vector::from_slice(&[5.0, 6.0, 7.0]);
    163
        /// let result = a.mul(&b)?;
    164
        ///
    165
        /// assert_eq!(result.as_slice(), &[10.0, 18.0, 28.0]);
    166
        /// # Ok::<(), trueno::TruenoError>(())
    167
        /// ```
    168
    61
        pub fn mul(&self, other: &Self) -> Result<Self> {
    169
    61
            if self.len() != other.len() {
    170
    0
                return Err(TruenoError::SizeMismatch {
    171
    0
                    expected: self.len(),
    172
    0
                    actual: other.len(),
    173
    0
                });
    174
    61
            }
    175
    176
    61
            let mut result = vec![0.0; self.len()];
    177
    178
            // Use parallel processing for large arrays
    179
            #[cfg(feature = "parallel")]
    180
            {
    181
                const PARALLEL_THRESHOLD: usize = 100_000;
    182
                const CHUNK_SIZE: usize = 65536;
    183
    184
                if self.len() >= PARALLEL_THRESHOLD {
    185
                    use rayon::prelude::*;
    186
    187
                    self.data
    188
                        .par_chunks(CHUNK_SIZE)
    189
                        .zip(other.data.par_chunks(CHUNK_SIZE))
    190
                        .zip(result.par_chunks_mut(CHUNK_SIZE))
    191
                        .for_each(|((chunk_a, chunk_b), chunk_out)| {
    192
                            dispatch_binary_op!(self.backend, mul, chunk_a, chunk_b, chunk_out);
    193
                        });
    194
    195
                    return Ok(Self {
    196
                        data: result,
    197
                        backend: self.backend,
    198
                    });
    199
                }
    200
            }
    201
    202
    61
            dispatch_binary_op!0(self.backend, mul, &self.data0, &other.data0, &mut result0);
    203
    204
    61
            Ok(Self {
    205
    61
                data: result,
    206
    61
                backend: self.backend,
    207
    61
            })
    208
    61
        }
    209
    210
        /// Element-wise division
    211
        ///
    212
        /// # Examples
    213
        ///
    214
        /// ```
    215
        /// use trueno::Vector;
    216
        ///
    217
        /// let a = Vector::from_slice(&[10.0, 20.0, 30.0]);
    218
        /// let b = Vector::from_slice(&[2.0, 4.0, 5.0]);
    219
        /// let result = a.div(&b)?;
    220
        ///
    221
        /// assert_eq!(result.as_slice(), &[5.0, 5.0, 6.0]);
    222
        /// # Ok::<(), trueno::TruenoError>(())
    223
        /// ```
    224
    0
        pub fn div(&self, other: &Self) -> Result<Self> {
    225
    0
            if self.len() != other.len() {
    226
    0
                return Err(TruenoError::SizeMismatch {
    227
    0
                    expected: self.len(),
    228
    0
                    actual: other.len(),
    229
    0
                });
    230
    0
            }
    231
    232
    0
            let mut result = vec![0.0; self.len()];
    233
    234
            // Use parallel processing for large arrays
    235
            #[cfg(feature = "parallel")]
    236
            {
    237
                const PARALLEL_THRESHOLD: usize = 100_000;
    238
                const CHUNK_SIZE: usize = 65536;
    239
    240
                if self.len() >= PARALLEL_THRESHOLD {
    241
                    use rayon::prelude::*;
    242
    243
                    self.data
    244
                        .par_chunks(CHUNK_SIZE)
    245
                        .zip(other.data.par_chunks(CHUNK_SIZE))
    246
                        .zip(result.par_chunks_mut(CHUNK_SIZE))
    247
                        .for_each(|((chunk_a, chunk_b), chunk_out)| {
    248
                            dispatch_binary_op!(self.backend, div, chunk_a, chunk_b, chunk_out);
    249
                        });
    250
    251
                    return Ok(Self {
    252
                        data: result,
    253
                        backend: self.backend,
    254
                    });
    255
                }
    256
            }
    257
    258
    0
            dispatch_binary_op!(self.backend, div, &self.data, &other.data, &mut result);
    259
    260
    0
            Ok(Self {
    261
    0
                data: result,
    262
    0
                backend: self.backend,
    263
    0
            })
    264
    0
        }
    265
    266
        /// Scalar multiplication (scale all elements by a scalar value)
    267
        ///
    268
        /// Returns a new vector where each element is multiplied by the scalar.
    269
        ///
    270
        /// # Examples
    271
        ///
    272
        /// ```
    273
        /// use trueno::Vector;
    274
        ///
    275
        /// let v = Vector::from_slice(&[1.0, 2.0, 3.0, 4.0]);
    276
        /// let result = v.scale(2.0)?;
    277
        ///
    278
        /// assert_eq!(result.as_slice(), &[2.0, 4.0, 6.0, 8.0]);
    279
        /// # Ok::<(), trueno::TruenoError>(())
    280
        /// ```
    281
        ///
    282
        /// # Scaling by Zero
    283
        ///
    284
        /// ```
    285
        /// use trueno::Vector;
    286
        ///
    287
        /// let v = Vector::from_slice(&[1.0, 2.0, 3.0]);
    288
        /// let result = v.scale(0.0)?;
    289
        /// assert_eq!(result.as_slice(), &[0.0, 0.0, 0.0]);
    290
        /// # Ok::<(), trueno::TruenoError>(())
    291
        /// ```
    292
        ///
    293
        /// # Negative Scaling
    294
        ///
    295
        /// ```
    296
        /// use trueno::Vector;
    297
        ///
    298
        /// let v = Vector::from_slice(&[1.0, -2.0, 3.0]);
    299
        /// let result = v.scale(-2.0)?;
    300
        /// assert_eq!(result.as_slice(), &[-2.0, 4.0, -6.0]);
    301
        /// # Ok::<(), trueno::TruenoError>(())
    302
        /// ```
    303
    1
        pub fn scale(&self, scalar: f32) -> Result<Vector<f32>> {
    304
    1
            let mut result_data = vec![0.0; self.len()];
    305
    306
    1
            if !self.data.is_empty() {
    307
                // SAFETY: Unsafe block delegates to backend implementation which maintains safety invariants
    308
                unsafe {
    309
    1
                    match self.backend {
    310
    0
                        Backend::Scalar => ScalarBackend::scale(&self.data, scalar, &mut result_data),
    311
                        #[cfg(target_arch = "x86_64")]
    312
                        Backend::SSE2 | Backend::AVX => {
    313
    0
                            Sse2Backend::scale(&self.data, scalar, &mut result_data)
    314
                        }
    315
                        #[cfg(target_arch = "x86_64")]
    316
                        Backend::AVX2 | Backend::AVX512 => {
    317
    1
                            Avx2Backend::scale(&self.data, scalar, &mut result_data)
    318
                        }
    319
                        #[cfg(any(target_arch = "aarch64", target_arch = "arm"))]
    320
                        Backend::NEON => NeonBackend::scale(&self.data, scalar, &mut result_data),
    321
                        #[cfg(target_arch = "wasm32")]
    322
                        Backend::WasmSIMD => WasmBackend::scale(&self.data, scalar, &mut result_data),
    323
    0
                        Backend::GPU => return Err(TruenoError::UnsupportedBackend(Backend::GPU)),
    324
                        Backend::Auto => {
    325
                            // Auto should have been resolved at creation time
    326
    0
                            return Err(TruenoError::UnsupportedBackend(Backend::Auto));
    327
                        }
    328
                        #[allow(unreachable_patterns)]
    329
    0
                        _ => ScalarBackend::scale(&self.data, scalar, &mut result_data),
    330
                    }
    331
                }
    332
    0
            }
    333
    334
    1
            Ok(Vector {
    335
    1
                data: result_data,
    336
    1
                backend: self.backend,
    337
    1
            })
    338
    1
        }
    339
    340
        /// Fused multiply-add: result\[i\] = self\[i\] * b\[i\] + c\[i\]
    341
        ///
    342
        /// Computes element-wise fused multiply-add operation. On hardware with FMA support
    343
        /// (AVX2, NEON), this is a single instruction with better performance and numerical
    344
        /// accuracy (no intermediate rounding). On platforms without FMA (SSE2, WASM), uses
    345
        /// separate multiply and add operations.
    346
        ///
    347
        /// # Arguments
    348
        ///
    349
        /// * `b` - The second vector to multiply with
    350
        /// * `c` - The vector to add to the product
    351
        ///
    352
        /// # Returns
    353
        ///
    354
        /// A new vector where each element is `self\[i\] * b\[i\] + c\[i\]`
    355
        ///
    356
        /// # Errors
    357
        ///
    358
        /// Returns `SizeMismatch` if vector lengths don't match
    359
        ///
    360
        /// # Examples
    361
        ///
    362
        /// ```
    363
        /// use trueno::Vector;
    364
        ///
    365
        /// let a = Vector::from_slice(&[2.0, 3.0, 4.0]);
    366
        /// let b = Vector::from_slice(&[5.0, 6.0, 7.0]);
    367
        /// let c = Vector::from_slice(&[1.0, 2.0, 3.0]);
    368
        /// let result = a.fma(&b, &c)?;
    369
        /// assert_eq!(result.as_slice(), &[11.0, 20.0, 31.0]);  // [2*5+1, 3*6+2, 4*7+3]
    370
        /// # Ok::<(), trueno::TruenoError>(())
    371
        /// ```
    372
        ///
    373
        /// # Use Cases
    374
        ///
    375
        /// - Neural networks: matrix multiplication, backpropagation
    376
        /// - Scientific computing: polynomial evaluation, numerical integration
    377
        /// - Graphics: transformation matrices, shader computations
    378
        /// - Physics simulations: force calculations, particle systems
    379
    0
        pub fn fma(&self, b: &Vector<f32>, c: &Vector<f32>) -> Result<Vector<f32>> {
    380
    0
            if self.len() != b.len() {
    381
    0
                return Err(TruenoError::SizeMismatch {
    382
    0
                    expected: self.len(),
    383
    0
                    actual: b.len(),
    384
    0
                });
    385
    0
            }
    386
    0
            if self.len() != c.len() {
    387
    0
                return Err(TruenoError::SizeMismatch {
    388
    0
                    expected: self.len(),
    389
    0
                    actual: c.len(),
    390
    0
                });
    391
    0
            }
    392
    393
    0
            let mut result_data = vec![0.0; self.len()];
    394
    395
    0
            if !self.data.is_empty() {
    396
                // SAFETY: Unsafe block delegates to backend implementation which maintains safety invariants
    397
                unsafe {
    398
    0
                    match self.backend {
    399
                        Backend::Scalar => {
    400
    0
                            ScalarBackend::fma(&self.data, &b.data, &c.data, &mut result_data)
    401
                        }
    402
                        #[cfg(target_arch = "x86_64")]
    403
                        Backend::SSE2 | Backend::AVX => {
    404
    0
                            Sse2Backend::fma(&self.data, &b.data, &c.data, &mut result_data)
    405
                        }
    406
                        #[cfg(target_arch = "x86_64")]
    407
                        Backend::AVX2 | Backend::AVX512 => {
    408
    0
                            Avx2Backend::fma(&self.data, &b.data, &c.data, &mut result_data)
    409
                        }
    410
                        #[cfg(any(target_arch = "aarch64", target_arch = "arm"))]
    411
                        Backend::NEON => {
    412
                            NeonBackend::fma(&self.data, &b.data, &c.data, &mut result_data)
    413
                        }
    414
                        #[cfg(target_arch = "wasm32")]
    415
                        Backend::WasmSIMD => {
    416
                            WasmBackend::fma(&self.data, &b.data, &c.data, &mut result_data)
    417
                        }
    418
    0
                        Backend::GPU => return Err(TruenoError::UnsupportedBackend(Backend::GPU)),
    419
                        Backend::Auto => {
    420
    0
                            return Err(TruenoError::UnsupportedBackend(Backend::Auto));
    421
                        }
    422
                        #[allow(unreachable_patterns)]
    423
    0
                        _ => ScalarBackend::fma(&self.data, &b.data, &c.data, &mut result_data),
    424
                    }
    425
                }
    426
    0
            }
    427
    428
    0
            Ok(Vector {
    429
    0
                data: result_data,
    430
    0
                backend: self.backend,
    431
    0
            })
    432
    0
        }
    433
    }
    434
    435
    #[cfg(test)]
    436
    mod tests {
    437
        use super::*;
    438
    439
        // ===== Add Tests =====
    440
    441
        #[test]
    442
        fn test_add_basic() {
    443
            let a = Vector::from_slice(&[1.0, 2.0, 3.0]);
    444
            let b = Vector::from_slice(&[4.0, 5.0, 6.0]);
    445
            let result = a.add(&b).unwrap();
    446
            assert_eq!(result.as_slice(), &[5.0, 7.0, 9.0]);
    447
        }
    448
    449
        #[test]
    450
        fn test_add_size_mismatch() {
    451
            let a = Vector::from_slice(&[1.0, 2.0]);
    452
            let b = Vector::from_slice(&[1.0, 2.0, 3.0]);
    453
            let result = a.add(&b);
    454
            assert!(result.is_err());
    455
            match result {
    456
                Err(TruenoError::SizeMismatch { expected, actual }) => {
    457
                    assert_eq!(expected, 2);
    458
                    assert_eq!(actual, 3);
    459
                }
    460
                _ => panic!("Expected SizeMismatch error"),
    461
            }
    462
        }
    463
    464
        #[test]
    465
        fn test_add_empty() {
    466
            let a = Vector::from_slice(&[]);
    467
            let b = Vector::from_slice(&[]);
    468
            let result = a.add(&b).unwrap();
    469
            assert!(result.as_slice().is_empty());
    470
        }
    471
    472
        #[test]
    473
        fn test_add_single_element() {
    474
            let a = Vector::from_slice(&[1.5]);
    475
            let b = Vector::from_slice(&[2.5]);
    476
            let result = a.add(&b).unwrap();
    477
            assert!((result.as_slice()[0] - 4.0).abs() < 1e-6);
    478
        }
    479
    480
        #[test]
    481
        fn test_add_negatives() {
    482
            let a = Vector::from_slice(&[-1.0, -2.0, -3.0]);
    483
            let b = Vector::from_slice(&[1.0, 2.0, 3.0]);
    484
            let result = a.add(&b).unwrap();
    485
            assert_eq!(result.as_slice(), &[0.0, 0.0, 0.0]);
    486
        }
    487
    488
        #[test]
    489
        fn test_add_large_array() {
    490
            let n = 10000;
    491
            let a = Vector::from_slice(&vec![1.0; n]);
    492
            let b = Vector::from_slice(&vec![2.0; n]);
    493
            let result = a.add(&b).unwrap();
    494
            for val in result.as_slice() {
    495
                assert!((val - 3.0).abs() < 1e-6);
    496
            }
    497
        }
    498
    499
        // ===== Sub Tests =====
    500
    501
        #[test]
    502
        fn test_sub_basic() {
    503
            let a = Vector::from_slice(&[5.0, 7.0, 9.0]);
    504
            let b = Vector::from_slice(&[1.0, 2.0, 3.0]);
    505
            let result = a.sub(&b).unwrap();
    506
            assert_eq!(result.as_slice(), &[4.0, 5.0, 6.0]);
    507
        }
    508
    509
        #[test]
    510
        fn test_sub_size_mismatch() {
    511
            let a = Vector::from_slice(&[1.0, 2.0, 3.0]);
    512
            let b = Vector::from_slice(&[1.0]);
    513
            let result = a.sub(&b);
    514
            assert!(result.is_err());
    515
        }
    516
    517
        #[test]
    518
        fn test_sub_empty() {
    519
            let a = Vector::from_slice(&[]);
    520
            let b = Vector::from_slice(&[]);
    521
            let result = a.sub(&b).unwrap();
    522
            assert!(result.as_slice().is_empty());
    523
        }
    524
    525
        #[test]
    526
        fn test_sub_self() {
    527
            let a = Vector::from_slice(&[1.0, 2.0, 3.0, 4.0]);
    528
            let result = a.sub(&a).unwrap();
    529
            for val in result.as_slice() {
    530
                assert!((val - 0.0).abs() < 1e-6);
    531
            }
    532
        }
    533
    534
        // ===== Mul Tests =====
    535
    536
        #[test]
    537
        fn test_mul_basic() {
    538
            let a = Vector::from_slice(&[1.0, 2.0, 3.0]);
    539
            let b = Vector::from_slice(&[2.0, 3.0, 4.0]);
    540
            let result = a.mul(&b).unwrap();
    541
            assert_eq!(result.as_slice(), &[2.0, 6.0, 12.0]);
    542
        }
    543
    544
        #[test]
    545
        fn test_mul_size_mismatch() {
    546
            let a = Vector::from_slice(&[1.0]);
    547
            let b = Vector::from_slice(&[1.0, 2.0]);
    548
            let result = a.mul(&b);
    549
            assert!(result.is_err());
    550
        }
    551
    552
        #[test]
    553
        fn test_mul_empty() {
    554
            let a = Vector::from_slice(&[]);
    555
            let b = Vector::from_slice(&[]);
    556
            let result = a.mul(&b).unwrap();
    557
            assert!(result.as_slice().is_empty());
    558
        }
    559
    560
        #[test]
    561
        fn test_mul_by_zero() {
    562
            let a = Vector::from_slice(&[1.0, 2.0, 3.0]);
    563
            let b = Vector::from_slice(&[0.0, 0.0, 0.0]);
    564
            let result = a.mul(&b).unwrap();
    565
            assert_eq!(result.as_slice(), &[0.0, 0.0, 0.0]);
    566
        }
    567
    568
        #[test]
    569
        fn test_mul_by_one() {
    570
            let a = Vector::from_slice(&[5.0, 10.0, 15.0]);
    571
            let b = Vector::from_slice(&[1.0, 1.0, 1.0]);
    572
            let result = a.mul(&b).unwrap();
    573
            assert_eq!(result.as_slice(), &[5.0, 10.0, 15.0]);
    574
        }
    575
    576
        // ===== Div Tests =====
    577
    578
        #[test]
    579
        fn test_div_basic() {
    580
            let a = Vector::from_slice(&[4.0, 6.0, 8.0]);
    581
            let b = Vector::from_slice(&[2.0, 2.0, 2.0]);
    582
            let result = a.div(&b).unwrap();
    583
            assert_eq!(result.as_slice(), &[2.0, 3.0, 4.0]);
    584
        }
    585
    586
        #[test]
    587
        fn test_div_size_mismatch() {
    588
            let a = Vector::from_slice(&[1.0, 2.0]);
    589
            let b = Vector::from_slice(&[1.0, 2.0, 3.0, 4.0]);
    590
            let result = a.div(&b);
    591
            assert!(result.is_err());
    592
        }
    593
    594
        #[test]
    595
        fn test_div_empty() {
    596
            let a = Vector::from_slice(&[]);
    597
            let b = Vector::from_slice(&[]);
    598
            let result = a.div(&b).unwrap();
    599
            assert!(result.as_slice().is_empty());
    600
        }
    601
    602
        #[test]
    603
        fn test_div_by_one() {
    604
            let a = Vector::from_slice(&[5.0, 10.0, 15.0]);
    605
            let b = Vector::from_slice(&[1.0, 1.0, 1.0]);
    606
            let result = a.div(&b).unwrap();
    607
            assert_eq!(result.as_slice(), &[5.0, 10.0, 15.0]);
    608
        }
    609
    610
        #[test]
    611
        fn test_div_by_zero_produces_inf() {
    612
            let a = Vector::from_slice(&[1.0, 2.0]);
    613
            let b = Vector::from_slice(&[0.0, 0.0]);
    614
            let result = a.div(&b).unwrap();
    615
            assert!(result.as_slice()[0].is_infinite());
    616
            assert!(result.as_slice()[1].is_infinite());
    617
        }
    618
    619
        // ===== Scale Tests =====
    620
    621
        #[test]
    622
        fn test_scale_basic() {
    623
            let a = Vector::from_slice(&[1.0, 2.0, 3.0]);
    624
            let result = a.scale(2.0).unwrap();
    625
            assert_eq!(result.as_slice(), &[2.0, 4.0, 6.0]);
    626
        }
    627
    628
        #[test]
    629
        fn test_scale_empty() {
    630
            let a = Vector::from_slice(&[]);
    631
            let result = a.scale(5.0).unwrap();
    632
            assert!(result.as_slice().is_empty());
    633
        }
    634
    635
        #[test]
    636
        fn test_scale_by_zero() {
    637
            let a = Vector::from_slice(&[1.0, 2.0, 3.0]);
    638
            let result = a.scale(0.0).unwrap();
    639
            assert_eq!(result.as_slice(), &[0.0, 0.0, 0.0]);
    640
        }
    641
    642
        #[test]
    643
        fn test_scale_by_one() {
    644
            let a = Vector::from_slice(&[5.0, 10.0, 15.0]);
    645
            let result = a.scale(1.0).unwrap();
    646
            assert_eq!(result.as_slice(), &[5.0, 10.0, 15.0]);
    647
        }
    648
    649
        #[test]
    650
        fn test_scale_negative() {
    651
            let a = Vector::from_slice(&[1.0, -2.0, 3.0]);
    652
            let result = a.scale(-1.0).unwrap();
    653
            assert_eq!(result.as_slice(), &[-1.0, 2.0, -3.0]);
    654
        }
    655
    656
        // ===== FMA Tests =====
    657
    658
        #[test]
    659
        fn test_fma_basic() {
    660
            let a = Vector::from_slice(&[1.0, 2.0, 3.0]);
    661
            let b = Vector::from_slice(&[2.0, 2.0, 2.0]);
    662
            let c = Vector::from_slice(&[1.0, 1.0, 1.0]);
    663
            // a * b + c = [2+1, 4+1, 6+1] = [3, 5, 7]
    664
            let result = a.fma(&b, &c).unwrap();
    665
            assert_eq!(result.as_slice(), &[3.0, 5.0, 7.0]);
    666
        }
    667
    668
        #[test]
    669
        fn test_fma_size_mismatch_b() {
    670
            let a = Vector::from_slice(&[1.0, 2.0, 3.0]);
    671
            let b = Vector::from_slice(&[2.0]);
    672
            let c = Vector::from_slice(&[1.0, 1.0, 1.0]);
    673
            let result = a.fma(&b, &c);
    674
            assert!(result.is_err());
    675
        }
    676
    677
        #[test]
    678
        fn test_fma_size_mismatch_c() {
    679
            let a = Vector::from_slice(&[1.0, 2.0, 3.0]);
    680
            let b = Vector::from_slice(&[2.0, 2.0, 2.0]);
    681
            let c = Vector::from_slice(&[1.0]);
    682
            let result = a.fma(&b, &c);
    683
            assert!(result.is_err());
    684
        }
    685
    686
        #[test]
    687
        fn test_fma_empty() {
    688
            let a = Vector::from_slice(&[]);
    689
            let b = Vector::from_slice(&[]);
    690
            let c = Vector::from_slice(&[]);
    691
            let result = a.fma(&b, &c).unwrap();
    692
            assert!(result.as_slice().is_empty());
    693
        }
    694
    695
        #[test]
    696
        fn test_fma_multiply_by_zero() {
    697
            let a = Vector::from_slice(&[5.0, 10.0, 15.0]);
    698
            let b = Vector::from_slice(&[0.0, 0.0, 0.0]);
    699
            let c = Vector::from_slice(&[1.0, 2.0, 3.0]);
    700
            // a * 0 + c = c
    701
            let result = a.fma(&b, &c).unwrap();
    702
            assert_eq!(result.as_slice(), &[1.0, 2.0, 3.0]);
    703
        }
    704
    705
        #[test]
    706
        fn test_fma_add_zero() {
    707
            let a = Vector::from_slice(&[2.0, 3.0, 4.0]);
    708
            let b = Vector::from_slice(&[3.0, 2.0, 1.0]);
    709
            let c = Vector::from_slice(&[0.0, 0.0, 0.0]);
    710
            // a * b + 0 = a * b
    711
            let result = a.fma(&b, &c).unwrap();
    712
            assert_eq!(result.as_slice(), &[6.0, 6.0, 4.0]);
    713
        }
    714
    715
        // ===== Backend Tests =====
    716
    717
        #[test]
    718
        fn test_add_scalar_backend() {
    719
            let a = Vector::from_slice_with_backend(&[1.0, 2.0, 3.0], Backend::Scalar);
    720
            let b = Vector::from_slice_with_backend(&[4.0, 5.0, 6.0], Backend::Scalar);
    721
            let result = a.add(&b).unwrap();
    722
            assert_eq!(result.as_slice(), &[5.0, 7.0, 9.0]);
    723
        }
    724
    725
        #[test]
    726
        #[cfg(target_arch = "x86_64")]
    727
        fn test_add_sse2_backend() {
    728
            let a = Vector::from_slice_with_backend(&[1.0, 2.0, 3.0, 4.0], Backend::SSE2);
    729
            let b = Vector::from_slice_with_backend(&[4.0, 5.0, 6.0, 7.0], Backend::SSE2);
    730
            let result = a.add(&b).unwrap();
    731
            assert_eq!(result.as_slice(), &[5.0, 7.0, 9.0, 11.0]);
    732
        }
    733
    734
        #[test]
    735
        #[cfg(target_arch = "x86_64")]
    736
        fn test_add_avx2_backend() {
    737
            if !std::arch::is_x86_feature_detected!("avx2") {
    738
                return; // Skip if AVX2 not available
    739
            }
    740
            let data: Vec<f32> = vec![1.0; 16];
    741
            let a = Vector::from_slice_with_backend(&data, Backend::AVX2);
    742
            let b_data: Vec<f32> = vec![2.0; 16];
    743
            let b = Vector::from_slice_with_backend(&b_data, Backend::AVX2);
    744
            let result = a.add(&b).unwrap();
    745
            for &val in result.as_slice() {
    746
                assert!((val - 3.0).abs() < 1e-6);
    747
            }
    748
        }
    749
    750
        // ===== Edge Cases =====
    751
    752
        #[test]
    753
        fn test_add_non_aligned_size() {
    754
            // Test with sizes that don't align to SIMD register widths
    755
            let a = Vector::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0]); // 7 elements
    756
            let b = Vector::from_slice(&[1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]);
    757
            let result = a.add(&b).unwrap();
    758
            assert_eq!(result.as_slice(), &[2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0]);
    759
        }
    760
    761
        #[test]
    762
        fn test_mul_preserves_sign() {
    763
            let a = Vector::from_slice(&[2.0, -2.0, 2.0, -2.0]);
    764
            let b = Vector::from_slice(&[3.0, 3.0, -3.0, -3.0]);
    765
            let result = a.mul(&b).unwrap();
    766
            assert_eq!(result.as_slice(), &[6.0, -6.0, -6.0, 6.0]);
    767
        }
    768
    769
        #[test]
    770
        fn test_operations_with_special_floats() {
    771
            let a = Vector::from_slice(&[f32::INFINITY, f32::NEG_INFINITY, 0.0]);
    772
            let b = Vector::from_slice(&[1.0, 1.0, 1.0]);
    773
            let result = a.add(&b).unwrap();
    774
            assert!(result.as_slice()[0].is_infinite());
    775
            assert!(result.as_slice()[1].is_infinite());
    776
            assert!((result.as_slice()[2] - 1.0).abs() < 1e-6);
    777
        }
    778
    }