## 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/transcendental.rs

    Line
    Count
    Source
    1
    //! Transcendental mathematical functions for Vector<f32>
    2
    //!
    3
    //! This module provides element-wise transcendental functions including:
    4
    //! - Exponentials: `exp`, `ln`, `log2`, `log10`
    5
    //! - Trigonometric: `sin`, `cos`, `tan`, `asin`, `acos`, `atan`
    6
    //! - Hyperbolic: `sinh`, `cosh`, `tanh`, `asinh`, `acosh`, `atanh`
    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_unary_op, Backend, Result, TruenoError};
    20
    21
    impl Vector<f32> {
    22
        /// Element-wise exponential: result\[i\] = e^x\[i\]
    23
        ///
    24
        /// Computes the natural exponential (e^x) for each element.
    25
        /// Uses Rust's optimized f32::exp() method.
    26
        ///
    27
        /// # Examples
    28
        ///
    29
        /// ```
    30
        /// use trueno::Vector;
    31
        ///
    32
        /// let v = Vector::from_slice(&[0.0, 1.0, 2.0]);
    33
        /// let result = v.exp().unwrap();
    34
        /// // result ≈ [1.0, 2.718, 7.389]
    35
        /// ```
    36
        ///
    37
        /// # Special Cases
    38
        ///
    39
        /// - `exp(0.0)` returns 1.0
    40
        /// - `exp(1.0)` returns e ≈ 2.71828
    41
        /// - `exp(-∞)` returns 0.0
    42
        /// - `exp(+∞)` returns +∞
    43
        ///
    44
        /// # Applications
    45
        ///
    46
        /// - Machine learning: Softmax activation, sigmoid, exponential loss
    47
        /// - Statistics: Exponential distribution, log-normal distribution
    48
        /// - Physics: Radioactive decay, population growth models
    49
        /// - Signal processing: Exponential smoothing, envelope detection
    50
        /// - Numerical methods: Solving differential equations
    51
    0
        pub fn exp(&self) -> Result<Vector<f32>> {
    52
    0
            let mut result_data = vec![0.0; self.len()];
    53
    54
    0
            if !self.data.is_empty() {
    55
                // Use parallel processing for large arrays
    56
                #[cfg(feature = "parallel")]
    57
                {
    58
                    const PARALLEL_THRESHOLD: usize = 100_000;
    59
                    const CHUNK_SIZE: usize = 65536;
    60
    61
                    if self.len() >= PARALLEL_THRESHOLD {
    62
                        use rayon::prelude::*;
    63
    64
                        self.data
    65
                            .par_chunks(CHUNK_SIZE)
    66
                            .zip(result_data.par_chunks_mut(CHUNK_SIZE))
    67
                            .for_each(|(chunk_in, chunk_out)| {
    68
                                // SAFETY: Unsafe block delegates to backend implementation which maintains safety invariants
    69
                                unsafe {
    70
                                    match self.backend {
    71
                                        Backend::Scalar => ScalarBackend::exp(chunk_in, chunk_out),
    72
                                        #[cfg(target_arch = "x86_64")]
    73
                                        Backend::SSE2 | Backend::AVX => {
    74
                                            Sse2Backend::exp(chunk_in, chunk_out)
    75
                                        }
    76
                                        #[cfg(target_arch = "x86_64")]
    77
                                        Backend::AVX2 | Backend::AVX512 => {
    78
                                            Avx2Backend::exp(chunk_in, chunk_out)
    79
                                        }
    80
                                        #[cfg(any(target_arch = "aarch64", target_arch = "arm"))]
    81
                                        Backend::NEON => NeonBackend::exp(chunk_in, chunk_out),
    82
                                        #[cfg(target_arch = "wasm32")]
    83
                                        Backend::WasmSIMD => WasmBackend::exp(chunk_in, chunk_out),
    84
                                        Backend::GPU => ScalarBackend::exp(chunk_in, chunk_out),
    85
                                        Backend::Auto => ScalarBackend::exp(chunk_in, chunk_out),
    86
                                        #[allow(unreachable_patterns)]
    87
                                        _ => ScalarBackend::exp(chunk_in, chunk_out),
    88
                                    }
    89
                                }
    90
                            });
    91
    92
                        return Ok(Vector {
    93
                            data: result_data,
    94
                            backend: self.backend,
    95
                        });
    96
                    }
    97
                }
    98
    99
                // SAFETY: Unsafe block delegates to backend implementation which maintains safety invariants
    100
                unsafe {
    101
    0
                    match self.backend {
    102
    0
                        Backend::Scalar => ScalarBackend::exp(&self.data, &mut result_data),
    103
                        #[cfg(target_arch = "x86_64")]
    104
    0
                        Backend::SSE2 | Backend::AVX => Sse2Backend::exp(&self.data, &mut result_data),
    105
                        #[cfg(target_arch = "x86_64")]
    106
                        Backend::AVX2 | Backend::AVX512 => {
    107
    0
                            Avx2Backend::exp(&self.data, &mut result_data)
    108
                        }
    109
                        #[cfg(any(target_arch = "aarch64", target_arch = "arm"))]
    110
                        Backend::NEON => NeonBackend::exp(&self.data, &mut result_data),
    111
                        #[cfg(target_arch = "wasm32")]
    112
                        Backend::WasmSIMD => WasmBackend::exp(&self.data, &mut result_data),
    113
    0
                        Backend::GPU => return Err(TruenoError::UnsupportedBackend(Backend::GPU)),
    114
                        Backend::Auto => {
    115
                            // Auto should have been resolved at creation time
    116
    0
                            return Err(TruenoError::UnsupportedBackend(Backend::Auto));
    117
                        }
    118
                        #[allow(unreachable_patterns)]
    119
    0
                        _ => ScalarBackend::exp(&self.data, &mut result_data),
    120
                    }
    121
                }
    122
    0
            }
    123
    124
    0
            Ok(Vector {
    125
    0
                data: result_data,
    126
    0
                backend: self.backend,
    127
    0
            })
    128
    0
        }
    129
    130
        /// Element-wise natural logarithm: result\[i\] = ln(x\[i\])
    131
        ///
    132
        /// Computes the natural logarithm (base e) for each element.
    133
        /// Uses Rust's optimized f32::ln() method.
    134
        ///
    135
        /// # Examples
    136
        ///
    137
        /// ```
    138
        /// use trueno::Vector;
    139
        ///
    140
        /// let v = Vector::from_slice(&[1.0, std::f32::consts::E, std::f32::consts::E.powi(2)]);
    141
        /// let result = v.ln().unwrap();
    142
        /// // result ≈ [0.0, 1.0, 2.0]
    143
        /// ```
    144
        ///
    145
        /// # Special Cases
    146
        ///
    147
        /// - `ln(1.0)` returns 0.0
    148
        /// - `ln(e)` returns 1.0
    149
        /// - `ln(x)` for x ≤ 0 returns NaN
    150
        /// - `ln(0.0)` returns -∞
    151
        /// - `ln(+∞)` returns +∞
    152
        ///
    153
        /// # Applications
    154
        ///
    155
        /// - Machine learning: Log loss, log-likelihood, softmax normalization
    156
        /// - Statistics: Log-normal distribution, log transformation for skewed data
    157
        /// - Information theory: Entropy calculation, mutual information
    158
        /// - Economics: Log returns, elasticity calculations
    159
        /// - Signal processing: Decibel conversion, log-frequency analysis
    160
    0
        pub fn ln(&self) -> Result<Vector<f32>> {
    161
    0
            let mut result_data = vec![0.0; self.len()];
    162
    163
    0
            if !self.data.is_empty() {
    164
    0
                dispatch_unary_op!(self.backend, ln, &self.data, &mut result_data);
    165
    0
            }
    166
    167
    0
            Ok(Vector {
    168
    0
                data: result_data,
    169
    0
                backend: self.backend,
    170
    0
            })
    171
    0
        }
    172
    173
        /// Element-wise base-2 logarithm: result\[i\] = log₂(x\[i\])
    174
        ///
    175
        /// Computes the base-2 logarithm for each element.
    176
        /// Uses Rust's optimized f32::log2() method.
    177
        ///
    178
        /// # Examples
    179
        ///
    180
        /// ```
    181
        /// use trueno::Vector;
    182
        ///
    183
        /// let v = Vector::from_slice(&[1.0, 2.0, 4.0, 8.0]);
    184
        /// let result = v.log2().unwrap();
    185
        /// // result ≈ [0.0, 1.0, 2.0, 3.0]
    186
        /// ```
    187
        ///
    188
        /// # Special Cases
    189
        ///
    190
        /// - `log2(1.0)` returns 0.0
    191
        /// - `log2(2.0)` returns 1.0
    192
        /// - `log2(x)` for x ≤ 0 returns NaN
    193
        /// - `log2(0.0)` returns -∞
    194
        /// - `log2(+∞)` returns +∞
    195
        ///
    196
        /// # Applications
    197
        ///
    198
        /// - Information theory: Entropy in bits, mutual information
    199
        /// - Computer science: Bit manipulation, binary search complexity
    200
        /// - Audio: Octave calculations, pitch detection
    201
        /// - Data compression: Huffman coding, arithmetic coding
    202
    0
        pub fn log2(&self) -> Result<Vector<f32>> {
    203
    0
            let mut result_data = vec![0.0; self.len()];
    204
    205
    0
            if !self.data.is_empty() {
    206
    0
                dispatch_unary_op!(self.backend, log2, &self.data, &mut result_data);
    207
    0
            }
    208
    209
    0
            Ok(Vector {
    210
    0
                data: result_data,
    211
    0
                backend: self.backend,
    212
    0
            })
    213
    0
        }
    214
    215
        /// Element-wise base-10 logarithm: result\[i\] = log₁₀(x\[i\])
    216
        ///
    217
        /// Computes the base-10 (common) logarithm for each element.
    218
        /// Uses Rust's optimized f32::log10() method.
    219
        ///
    220
        /// # Examples
    221
        ///
    222
        /// ```
    223
        /// use trueno::Vector;
    224
        ///
    225
        /// let v = Vector::from_slice(&[1.0, 10.0, 100.0, 1000.0]);
    226
        /// let result = v.log10().unwrap();
    227
        /// // result ≈ [0.0, 1.0, 2.0, 3.0]
    228
        /// ```
    229
        ///
    230
        /// # Special Cases
    231
        ///
    232
        /// - `log10(1.0)` returns 0.0
    233
        /// - `log10(10.0)` returns 1.0
    234
        /// - `log10(x)` for x ≤ 0 returns NaN
    235
        /// - `log10(0.0)` returns -∞
    236
        /// - `log10(+∞)` returns +∞
    237
        ///
    238
        /// # Applications
    239
        ///
    240
        /// - Audio: Decibel calculations (dB = 20 * log10(amplitude))
    241
        /// - Chemistry: pH calculations (-log10(H+ concentration))
    242
        /// - Seismology: Richter scale
    243
        /// - Scientific notation: Order of magnitude calculations
    244
    0
        pub fn log10(&self) -> Result<Vector<f32>> {
    245
    0
            let mut result_data = vec![0.0; self.len()];
    246
    247
    0
            if !self.data.is_empty() {
    248
    0
                dispatch_unary_op!(self.backend, log10, &self.data, &mut result_data);
    249
    0
            }
    250
    251
    0
            Ok(Vector {
    252
    0
                data: result_data,
    253
    0
                backend: self.backend,
    254
    0
            })
    255
    0
        }
    256
    257
        /// Element-wise sine: result\[i\] = sin(x\[i\])
    258
        ///
    259
        /// Computes the sine for each element (input in radians).
    260
        /// Uses Rust's optimized f32::sin() method.
    261
        ///
    262
        /// # Examples
    263
        ///
    264
        /// ```
    265
        /// use trueno::Vector;
    266
        /// use std::f32::consts::PI;
    267
        ///
    268
        /// let v = Vector::from_slice(&[0.0, PI / 2.0, PI]);
    269
        /// let result = v.sin().unwrap();
    270
        /// // result ≈ [0.0, 1.0, 0.0]
    271
        /// ```
    272
        ///
    273
        /// # Special Cases
    274
        ///
    275
        /// - `sin(0)` returns 0.0
    276
        /// - `sin(π/2)` returns 1.0
    277
        /// - `sin(π)` returns 0.0 (approximately)
    278
        /// - `sin(-x)` returns -sin(x) (odd function)
    279
        /// - Periodic with period 2π: sin(x + 2π) = sin(x)
    280
        ///
    281
        /// # Applications
    282
        ///
    283
        /// - Signal processing: Waveform generation, oscillators, modulation
    284
        /// - Physics: Harmonic motion, wave propagation, pendulums
    285
        /// - Audio: Synthesizers, tone generation, effects processing
    286
        /// - Graphics: Animation, rotation transformations, procedural generation
    287
        /// - Fourier analysis: Frequency decomposition, spectral analysis
    288
    0
        pub fn sin(&self) -> Result<Vector<f32>> {
    289
    0
            let mut result_data = vec![0.0; self.len()];
    290
    291
    0
            if !self.data.is_empty() {
    292
    0
                dispatch_unary_op!(self.backend, sin, &self.data, &mut result_data);
    293
    0
            }
    294
    295
    0
            Ok(Vector {
    296
    0
                data: result_data,
    297
    0
                backend: self.backend,
    298
    0
            })
    299
    0
        }
    300
    301
        /// Element-wise cosine: result\[i\] = cos(x\[i\])
    302
        ///
    303
        /// Computes the cosine for each element (input in radians).
    304
        /// Uses Rust's optimized f32::cos() method.
    305
        ///
    306
        /// # Examples
    307
        ///
    308
        /// ```
    309
        /// use trueno::Vector;
    310
        /// use std::f32::consts::PI;
    311
        ///
    312
        /// let v = Vector::from_slice(&[0.0, PI / 2.0, PI]);
    313
        /// let result = v.cos().unwrap();
    314
        /// // result ≈ [1.0, 0.0, -1.0]
    315
        /// ```
    316
        ///
    317
        /// # Special Cases
    318
        ///
    319
        /// - `cos(0)` returns 1.0
    320
        /// - `cos(π/2)` returns 0.0 (approximately)
    321
        /// - `cos(π)` returns -1.0
    322
        /// - `cos(-x)` returns cos(x) (even function)
    323
        /// - Periodic with period 2π: cos(x + 2π) = cos(x)
    324
        /// - Relation to sine: cos(x) = sin(x + π/2)
    325
        ///
    326
        /// # Applications
    327
        ///
    328
        /// - Signal processing: Phase-shifted waveforms, I/Q modulation, quadrature signals
    329
        /// - Physics: Projectile motion, wave interference, damped oscillations
    330
        /// - Graphics: Rotation matrices, camera transforms, circular motion
    331
        /// - Audio: Stereo panning, spatial audio, frequency synthesis
    332
        /// - Engineering: Control systems, frequency response, AC circuits
    333
    0
        pub fn cos(&self) -> Result<Vector<f32>> {
    334
    0
            let mut result_data = vec![0.0; self.len()];
    335
    336
    0
            if !self.data.is_empty() {
    337
    0
                dispatch_unary_op!(self.backend, cos, &self.data, &mut result_data);
    338
    0
            }
    339
    340
    0
            Ok(Vector {
    341
    0
                data: result_data,
    342
    0
                backend: self.backend,
    343
    0
            })
    344
    0
        }
    345
    346
        /// Computes element-wise tangent (tan) of the vector.
    347
        ///
    348
        /// Returns a new vector where each element is the tangent of the corresponding input element.
    349
        /// tan(x) = sin(x) / cos(x)
    350
        ///
    351
        /// # Returns
    352
        /// - `Ok(Vector<f32>)`: New vector with tan(x) for each element
    353
        ///
    354
        /// # Properties
    355
        /// - Odd function: tan(-x) = -tan(x)
    356
        /// - Period: 2π (not π, despite common misconception)
    357
        /// - Undefined at x = π/2 + nπ (where n is any integer)
    358
        /// - tan(x) = sin(x) / cos(x)
    359
        /// - Range: (-∞, +∞)
    360
        ///
    361
        /// # Performance
    362
        /// - Iterator map pattern for cache efficiency
    363
        /// - Leverages Rust's optimized f32::tan()
    364
        /// - Auto-vectorized by LLVM on supporting platforms
    365
        ///
    366
        /// # Examples
    367
        /// ```
    368
        /// use trueno::Vector;
    369
        /// use std::f32::consts::PI;
    370
        ///
    371
        /// let angles = Vector::from_slice(&[0.0, PI / 4.0, -PI / 4.0]);
    372
        /// let result = angles.tan().unwrap();
    373
        /// // Result: [0.0, 1.0, -1.0] (approximately)
    374
        /// ```
    375
        ///
    376
        /// # Use Cases
    377
        /// - Trigonometry: Slope calculations, angle relationships
    378
        /// - Signal processing: Phase analysis, modulation
    379
        /// - Physics: Projectile trajectories, optics (Snell's law angles)
    380
        /// - Graphics: Perspective projection, field of view calculations
    381
        /// - Engineering: Slope gradients, tangent lines to curves
    382
    0
        pub fn tan(&self) -> Result<Vector<f32>> {
    383
    0
            let mut result_data = vec![0.0; self.len()];
    384
    385
    0
            if !self.data.is_empty() {
    386
    0
                dispatch_unary_op!(self.backend, tan, &self.data, &mut result_data);
    387
    0
            }
    388
    389
    0
            Ok(Vector {
    390
    0
                data: result_data,
    391
    0
                backend: self.backend,
    392
    0
            })
    393
    0
        }
    394
    395
        /// Computes element-wise arcsine (asin/sin⁻¹) of the vector.
    396
        ///
    397
        /// Returns a new vector where each element is the inverse sine of the corresponding input element.
    398
        /// This is the inverse function of sin: if y = sin(x), then x = asin(y).
    399
        ///
    400
        /// # Returns
    401
        /// - `Ok(Vector<f32>)`: New vector with asin(x) for each element
    402
        ///
    403
        /// # Properties
    404
        /// - Domain: [-1, 1] (inputs outside this range produce NaN)
    405
        /// - Range: [-π/2, π/2]
    406
        /// - Odd function: asin(-x) = -asin(x)
    407
        /// - Inverse relation: asin(sin(x)) = x for x ∈ [-π/2, π/2]
    408
        /// - asin(0) = 0
    409
        /// - asin(1) = π/2
    410
        /// - asin(-1) = -π/2
    411
        ///
    412
        /// # Performance
    413
        /// - Iterator map pattern for cache efficiency
    414
        /// - Leverages Rust's optimized f32::asin()
    415
        /// - Auto-vectorized by LLVM on supporting platforms
    416
        ///
    417
        /// # Examples
    418
        /// ```
    419
        /// use trueno::Vector;
    420
        /// use std::f32::consts::PI;
    421
        ///
    422
        /// let values = Vector::from_slice(&[0.0, 0.5, 1.0]);
    423
        /// let result = values.asin().unwrap();
    424
        /// // Result: [0.0, π/6, π/2] (approximately)
    425
        /// ```
    426
        ///
    427
        /// # Use Cases
    428
        /// - Physics: Calculating angles from sine values in mechanics, optics
    429
        /// - Signal processing: Phase recovery, demodulation
    430
        /// - Graphics: Inverse transformations, angle calculations
    431
        /// - Navigation: GPS calculations, spherical trigonometry
    432
        /// - Control systems: Inverse kinematics, servo positioning
    433
    0
        pub fn asin(&self) -> Result<Vector<f32>> {
    434
    0
            let asin_data: Vec<f32> = self.data.iter().map(|x| x.asin()).collect();
    435
    0
            Ok(Vector {
    436
    0
                data: asin_data,
    437
    0
                backend: self.backend,
    438
    0
            })
    439
    0
        }
    440
    441
        /// Computes element-wise arccosine (acos/cos⁻¹) of the vector.
    442
        ///
    443
        /// Returns a new vector where each element is the inverse cosine of the corresponding input element.
    444
        /// This is the inverse function of cos: if y = cos(x), then x = acos(y).
    445
        ///
    446
        /// # Returns
    447
        /// - `Ok(Vector<f32>)`: New vector with acos(x) for each element
    448
        ///
    449
        /// # Properties
    450
        /// - Domain: [-1, 1] (inputs outside this range produce NaN)
    451
        /// - Range: [0, π]
    452
        /// - Symmetry: acos(-x) = π - acos(x)
    453
        /// - Inverse relation: acos(cos(x)) = x for x ∈ [0, π]
    454
        /// - acos(0) = π/2
    455
        /// - acos(1) = 0
    456
        /// - acos(-1) = π
    457
        ///
    458
        /// # Performance
    459
        /// - Iterator map pattern for cache efficiency
    460
        /// - Leverages Rust's optimized f32::acos()
    461
        /// - Auto-vectorized by LLVM on supporting platforms
    462
        ///
    463
        /// # Examples
    464
        /// ```
    465
        /// use trueno::Vector;
    466
        /// use std::f32::consts::PI;
    467
        ///
    468
        /// let values = Vector::from_slice(&[0.0, 0.5, 1.0]);
    469
        /// let result = values.acos().unwrap();
    470
        /// // Result: [π/2, π/3, 0.0] (approximately)
    471
        /// ```
    472
        ///
    473
        /// # Use Cases
    474
        /// - Physics: Angle calculations in mechanics, optics, reflections
    475
        /// - Signal processing: Phase analysis, correlation functions
    476
        /// - Graphics: View angle calculations, lighting models
    477
        /// - Navigation: Bearing calculations, great circle distances
    478
        /// - Robotics: Joint angle solving, orientation calculations
    479
    0
        pub fn acos(&self) -> Result<Vector<f32>> {
    480
    0
            let acos_data: Vec<f32> = self.data.iter().map(|x| x.acos()).collect();
    481
    0
            Ok(Vector {
    482
    0
                data: acos_data,
    483
    0
                backend: self.backend,
    484
    0
            })
    485
    0
        }
    486
    487
        /// Computes element-wise arctangent (atan/tan⁻¹) of the vector.
    488
        ///
    489
        /// Returns a new vector where each element is the inverse tangent of the corresponding input element.
    490
        /// This is the inverse function of tan: if y = tan(x), then x = atan(y).
    491
        ///
    492
        /// # Returns
    493
        /// - `Ok(Vector<f32>)`: New vector with atan(x) for each element
    494
        ///
    495
        /// # Properties
    496
        /// - Domain: All real numbers (-∞, +∞)
    497
        /// - Range: (-π/2, π/2)
    498
        /// - Odd function: atan(-x) = -atan(x)
    499
        /// - Inverse relation: atan(tan(x)) = x for x ∈ (-π/2, π/2)
    500
        /// - atan(0) = 0
    501
        /// - atan(1) = π/4
    502
        /// - atan(-1) = -π/4
    503
        /// - lim(x→∞) atan(x) = π/2
    504
        /// - lim(x→-∞) atan(x) = -π/2
    505
        ///
    506
        /// # Performance
    507
        /// - Iterator map pattern for cache efficiency
    508
        /// - Leverages Rust's optimized f32::atan()
    509
        /// - Auto-vectorized by LLVM on supporting platforms
    510
        ///
    511
        /// # Examples
    512
        /// ```
    513
        /// use trueno::Vector;
    514
        /// use std::f32::consts::PI;
    515
        ///
    516
        /// let values = Vector::from_slice(&[0.0, 1.0, -1.0]);
    517
        /// let result = values.atan().unwrap();
    518
        /// // Result: [0.0, π/4, -π/4] (approximately)
    519
        /// ```
    520
        ///
    521
        /// # Use Cases
    522
        /// - Physics: Angle calculations from slopes, velocity components
    523
        /// - Signal processing: Phase unwrapping, FM demodulation
    524
        /// - Graphics: Rotation calculations, camera orientation
    525
        /// - Robotics: Inverse kinematics, steering angles
    526
        /// - Navigation: Heading calculations from coordinates
    527
    0
        pub fn atan(&self) -> Result<Vector<f32>> {
    528
    0
            let atan_data: Vec<f32> = self.data.iter().map(|x| x.atan()).collect();
    529
    0
            Ok(Vector {
    530
    0
                data: atan_data,
    531
    0
                backend: self.backend,
    532
    0
            })
    533
    0
        }
    534
    535
        /// Computes the hyperbolic sine (sinh) of each element.
    536
        ///
    537
        /// # Mathematical Definition
    538
        ///
    539
        /// sinh(x) = (e^x - e^(-x)) / 2
    540
        ///
    541
        /// # Properties
    542
        ///
    543
        /// - Domain: (-∞, +∞)
    544
        /// - Range: (-∞, +∞)
    545
        /// - Odd function: sinh(-x) = -sinh(x)
    546
        /// - sinh(0) = 0
    547
        ///
    548
        /// # Examples
    549
        ///
    550
        /// ```
    551
        /// use trueno::Vector;
    552
        ///
    553
        /// let v = Vector::from_slice(&[0.0, 1.0, -1.0]);
    554
        /// let result = v.sinh().unwrap();
    555
        /// assert!((result.as_slice()[0] - 0.0).abs() < 1e-5);
    556
        /// ```
    557
    0
        pub fn sinh(&self) -> Result<Vector<f32>> {
    558
    0
            let sinh_data: Vec<f32> = self.data.iter().map(|x| x.sinh()).collect();
    559
    0
            Ok(Vector {
    560
    0
                data: sinh_data,
    561
    0
                backend: self.backend,
    562
    0
            })
    563
    0
        }
    564
    565
        /// Computes the hyperbolic cosine (cosh) of each element.
    566
        ///
    567
        /// # Mathematical Definition
    568
        ///
    569
        /// cosh(x) = (e^x + e^(-x)) / 2
    570
        ///
    571
        /// # Properties
    572
        ///
    573
        /// - Domain: (-∞, +∞)
    574
        /// - Range: [1, +∞)
    575
        /// - Even function: cosh(-x) = cosh(x)
    576
        /// - cosh(0) = 1
    577
        /// - Always positive: cosh(x) ≥ 1 for all x
    578
        ///
    579
        /// # Examples
    580
        ///
    581
        /// ```
    582
        /// use trueno::Vector;
    583
        ///
    584
        /// let v = Vector::from_slice(&[0.0, 1.0, -1.0]);
    585
        /// let result = v.cosh().unwrap();
    586
        /// assert!((result.as_slice()[0] - 1.0).abs() < 1e-5);
    587
        /// ```
    588
    0
        pub fn cosh(&self) -> Result<Vector<f32>> {
    589
    0
            let cosh_data: Vec<f32> = self.data.iter().map(|x| x.cosh()).collect();
    590
    0
            Ok(Vector {
    591
    0
                data: cosh_data,
    592
    0
                backend: self.backend,
    593
    0
            })
    594
    0
        }
    595
    596
        /// Computes the hyperbolic tangent (tanh) of each element.
    597
        ///
    598
        /// # Mathematical Definition
    599
        ///
    600
        /// tanh(x) = sinh(x) / cosh(x) = (e^x - e^(-x)) / (e^x + e^(-x))
    601
        ///
    602
        /// # Properties
    603
        ///
    604
        /// - Domain: (-∞, +∞)
    605
        /// - Range: (-1, 1)
    606
        /// - Odd function: tanh(-x) = -tanh(x)
    607
        /// - tanh(0) = 0
    608
        /// - Bounded: -1 < tanh(x) < 1 for all x
    609
        /// - Commonly used as activation function in neural networks
    610
        ///
    611
        /// # Examples
    612
        ///
    613
        /// ```
    614
        /// use trueno::Vector;
    615
        ///
    616
        /// let v = Vector::from_slice(&[0.0, 1.0, -1.0]);
    617
        /// let result = v.tanh().unwrap();
    618
        /// assert!((result.as_slice()[0] - 0.0).abs() < 1e-5);
    619
        /// // All values are in range (-1, 1)
    620
        /// assert!(result.as_slice().iter().all(|&x| x > -1.0 && x < 1.0));
    621
        /// ```
    622
    0
        pub fn tanh(&self) -> Result<Vector<f32>> {
    623
    0
            if self.data.is_empty() {
    624
    0
                return Err(TruenoError::EmptyVector);
    625
    0
            }
    626
    627
            // OpComplexity::Low - GPU threshold: >100K elements
    628
            #[cfg(all(feature = "gpu", not(target_arch = "wasm32")))]
    629
            const GPU_THRESHOLD: usize = usize::MAX; // GPU DISABLED - 2-800x slower, see docs/performance-analysis.md
    630
    631
            // Try GPU first for large vectors
    632
            #[cfg(all(feature = "gpu", not(target_arch = "wasm32")))]
    633
            {
    634
    0
                if self.data.len() >= GPU_THRESHOLD {
    635
                    use crate::backends::gpu::GpuDevice;
    636
    0
                    if GpuDevice::is_available() {
    637
    0
                        let gpu = GpuDevice::new().map_err(TruenoError::InvalidInput)?;
    638
    0
                        let mut result = vec![0.0; self.data.len()];
    639
    0
                        if gpu.tanh(&self.data, &mut result).is_ok() {
    640
    0
                            return Ok(Vector::from_vec(result));
    641
    0
                        }
    642
    0
                    }
    643
    0
                }
    644
            }
    645
    646
    0
            let mut result = vec![0.0; self.len()];
    647
    648
            // Dispatch to appropriate SIMD backend
    649
            // SAFETY: Unsafe block delegates to backend implementation which maintains safety invariants
    650
            unsafe {
    651
    0
                match self.backend {
    652
    0
                    Backend::Scalar => {
    653
    0
                        ScalarBackend::tanh(&self.data, &mut result);
    654
    0
                    }
    655
                    #[cfg(target_arch = "x86_64")]
    656
    0
                    Backend::SSE2 | Backend::AVX => {
    657
    0
                        Sse2Backend::tanh(&self.data, &mut result);
    658
    0
                    }
    659
                    #[cfg(target_arch = "x86_64")]
    660
    0
                    Backend::AVX2 | Backend::AVX512 => {
    661
    0
                        Avx2Backend::tanh(&self.data, &mut result);
    662
    0
                    }
    663
                    #[cfg(not(target_arch = "x86_64"))]
    664
                    Backend::SSE2 | Backend::AVX | Backend::AVX2 | Backend::AVX512 => {
    665
                        ScalarBackend::tanh(&self.data, &mut result);
    666
                    }
    667
                    #[cfg(any(target_arch = "aarch64", target_arch = "arm"))]
    668
                    Backend::NEON => {
    669
                        NeonBackend::tanh(&self.data, &mut result);
    670
                    }
    671
                    #[cfg(not(any(target_arch = "aarch64", target_arch = "arm")))]
    672
    0
                    Backend::NEON => {
    673
    0
                        ScalarBackend::tanh(&self.data, &mut result);
    674
    0
                    }
    675
                    #[cfg(target_arch = "wasm32")]
    676
                    Backend::WasmSIMD => {
    677
                        WasmBackend::tanh(&self.data, &mut result);
    678
                    }
    679
                    #[cfg(not(target_arch = "wasm32"))]
    680
    0
                    Backend::WasmSIMD => {
    681
    0
                        ScalarBackend::tanh(&self.data, &mut result);
    682
    0
                    }
    683
                    Backend::GPU | Backend::Auto => {
    684
                        // Auto should have been resolved at Vector creation
    685
                        // GPU falls back to best available SIMD
    686
                        #[cfg(target_arch = "x86_64")]
    687
                        {
    688
    0
                            if is_x86_feature_detected!("avx2") {
    689
    0
                                Avx2Backend::tanh(&self.data, &mut result);
    690
    0
                            } else {
    691
    0
                                Sse2Backend::tanh(&self.data, &mut result);
    692
    0
                            }
    693
                        }
    694
                        #[cfg(not(target_arch = "x86_64"))]
    695
                        {
    696
                            ScalarBackend::tanh(&self.data, &mut result);
    697
                        }
    698
                    }
    699
                }
    700
            }
    701
    702
    0
            Ok(Vector {
    703
    0
                data: result,
    704
    0
                backend: self.backend,
    705
    0
            })
    706
    0
        }
    707
    708
        /// Computes the inverse hyperbolic sine (asinh) of each element.
    709
        ///
    710
        /// # Mathematical Definition
    711
        ///
    712
        /// asinh(x) = ln(x + sqrt(x² + 1))
    713
        ///
    714
        /// # Properties
    715
        ///
    716
        /// - Domain: (-∞, +∞)
    717
        /// - Range: (-∞, +∞)
    718
        /// - Odd function: asinh(-x) = -asinh(x)
    719
        /// - asinh(0) = 0
    720
        /// - Inverse of sinh: asinh(sinh(x)) = x
    721
        ///
    722
        /// # Examples
    723
        ///
    724
        /// ```
    725
        /// use trueno::Vector;
    726
        ///
    727
        /// let v = Vector::from_slice(&[0.0, 1.0, -1.0]);
    728
        /// let result = v.asinh().unwrap();
    729
        /// assert!((result.as_slice()[0] - 0.0).abs() < 1e-5);
    730
        /// ```
    731
    0
        pub fn asinh(&self) -> Result<Vector<f32>> {
    732
    0
            let asinh_data: Vec<f32> = self.data.iter().map(|x| x.asinh()).collect();
    733
    0
            Ok(Vector {
    734
    0
                data: asinh_data,
    735
    0
                backend: self.backend,
    736
    0
            })
    737
    0
        }
    738
    739
        /// Computes the inverse hyperbolic cosine (acosh) of each element.
    740
        ///
    741
        /// # Mathematical Definition
    742
        ///
    743
        /// acosh(x) = ln(x + sqrt(x² - 1))
    744
        ///
    745
        /// # Properties
    746
        ///
    747
        /// - Domain: [1, +∞)
    748
        /// - Range: [0, +∞)
    749
        /// - acosh(1) = 0
    750
        /// - Inverse of cosh: acosh(cosh(x)) = x for x >= 0
    751
        ///
    752
        /// # Examples
    753
        ///
    754
        /// ```
    755
        /// use trueno::Vector;
    756
        ///
    757
        /// let v = Vector::from_slice(&[1.0, 2.0, 3.0]);
    758
        /// let result = v.acosh().unwrap();
    759
        /// assert!((result.as_slice()[0] - 0.0).abs() < 1e-5);
    760
        /// ```
    761
    0
        pub fn acosh(&self) -> Result<Vector<f32>> {
    762
    0
            let acosh_data: Vec<f32> = self.data.iter().map(|x| x.acosh()).collect();
    763
    0
            Ok(Vector {
    764
    0
                data: acosh_data,
    765
    0
                backend: self.backend,
    766
    0
            })
    767
    0
        }
    768
    769
        /// Computes the inverse hyperbolic tangent (atanh) of each element.
    770
        ///
    771
        /// Domain: (-1, 1)
    772
        /// Range: (-∞, +∞)
    773
        ///
    774
        /// # Examples
    775
        ///
    776
        /// ```
    777
        /// use trueno::Vector;
    778
        ///
    779
        /// let v = Vector::from_slice(&[0.0, 0.5, -0.5]);
    780
        /// let result = v.atanh().unwrap();
    781
        /// // atanh(0) = 0, atanh(0.5) ≈ 0.549, atanh(-0.5) ≈ -0.549
    782
        /// ```
    783
    0
        pub fn atanh(&self) -> Result<Vector<f32>> {
    784
    0
            let atanh_data: Vec<f32> = self.data.iter().map(|x| x.atanh()).collect();
    785
    0
            Ok(Vector {
    786
    0
                data: atanh_data,
    787
    0
                backend: self.backend,
    788
    0
            })
    789
    0
        }
    790
    }
    791
    792
    #[cfg(test)]
    793
    mod tests {
    794
        use super::*;
    795
    796
        // ========== Exponential Functions ==========
    797
    798
        #[test]
    799
        fn test_exp_basic() {
    800
            let v = Vector::from_slice(&[0.0, 1.0, 2.0]);
    801
            let result = v.exp().unwrap();
    802
            assert!((result.as_slice()[0] - 1.0).abs() < 1e-6); // e^0 = 1
    803
            assert!((result.as_slice()[1] - std::f32::consts::E).abs() < 1e-5); // e^1 = e
    804
            assert!((result.as_slice()[2] - std::f32::consts::E.powi(2)).abs() < 1e-4);
    805
        }
    806
    807
        #[test]
    808
        fn test_exp_empty() {
    809
            let v = Vector::<f32>::from_slice(&[]);
    810
            let result = v.exp().unwrap();
    811
            assert!(result.is_empty());
    812
        }
    813
    814
        #[test]
    815
        fn test_exp_negative() {
    816
            let v = Vector::from_slice(&[-1.0, -2.0]);
    817
            let result = v.exp().unwrap();
    818
            assert!((result.as_slice()[0] - 1.0 / std::f32::consts::E).abs() < 1e-5);
    819
        }
    820
    821
        #[test]
    822
        fn test_ln_basic() {
    823
            let v = Vector::from_slice(&[1.0, std::f32::consts::E, std::f32::consts::E.powi(2)]);
    824
            let result = v.ln().unwrap();
    825
            assert!((result.as_slice()[0] - 0.0).abs() < 1e-6);
    826
            assert!((result.as_slice()[1] - 1.0).abs() < 1e-5);
    827
            assert!((result.as_slice()[2] - 2.0).abs() < 1e-5);
    828
        }
    829
    830
        #[test]
    831
        fn test_ln_empty() {
    832
            let v = Vector::<f32>::from_slice(&[]);
    833
            let result = v.ln().unwrap();
    834
            assert!(result.is_empty());
    835
        }
    836
    837
        #[test]
    838
        fn test_log2_basic() {
    839
            let v = Vector::from_slice(&[1.0, 2.0, 4.0, 8.0]);
    840
            let result = v.log2().unwrap();
    841
            assert!((result.as_slice()[0] - 0.0).abs() < 1e-6);
    842
            assert!((result.as_slice()[1] - 1.0).abs() < 1e-6);
    843
            assert!((result.as_slice()[2] - 2.0).abs() < 1e-6);
    844
            assert!((result.as_slice()[3] - 3.0).abs() < 1e-6);
    845
        }
    846
    847
        #[test]
    848
        fn test_log2_empty() {
    849
            let v = Vector::<f32>::from_slice(&[]);
    850
            let result = v.log2().unwrap();
    851
            assert!(result.is_empty());
    852
        }
    853
    854
        #[test]
    855
        fn test_log10_basic() {
    856
            let v = Vector::from_slice(&[1.0, 10.0, 100.0, 1000.0]);
    857
            let result = v.log10().unwrap();
    858
            assert!((result.as_slice()[0] - 0.0).abs() < 1e-6);
    859
            assert!((result.as_slice()[1] - 1.0).abs() < 1e-5);
    860
            assert!((result.as_slice()[2] - 2.0).abs() < 1e-5);
    861
            assert!((result.as_slice()[3] - 3.0).abs() < 1e-4);
    862
        }
    863
    864
        #[test]
    865
        fn test_log10_empty() {
    866
            let v = Vector::<f32>::from_slice(&[]);
    867
            let result = v.log10().unwrap();
    868
            assert!(result.is_empty());
    869
        }
    870
    871
        // ========== Trigonometric Functions ==========
    872
    873
        #[test]
    874
        fn test_sin_basic() {
    875
            let v = Vector::from_slice(&[0.0, std::f32::consts::PI / 2.0, std::f32::consts::PI]);
    876
            let result = v.sin().unwrap();
    877
            assert!((result.as_slice()[0] - 0.0).abs() < 1e-6);
    878
            assert!((result.as_slice()[1] - 1.0).abs() < 1e-6);
    879
            assert!((result.as_slice()[2] - 0.0).abs() < 1e-5);
    880
        }
    881
    882
        #[test]
    883
        fn test_sin_empty() {
    884
            let v = Vector::<f32>::from_slice(&[]);
    885
            let result = v.sin().unwrap();
    886
            assert!(result.is_empty());
    887
        }
    888
    889
        #[test]
    890
        fn test_cos_basic() {
    891
            let v = Vector::from_slice(&[0.0, std::f32::consts::PI / 2.0, std::f32::consts::PI]);
    892
            let result = v.cos().unwrap();
    893
            assert!((result.as_slice()[0] - 1.0).abs() < 1e-6);
    894
            assert!((result.as_slice()[1] - 0.0).abs() < 1e-6);
    895
            assert!((result.as_slice()[2] - (-1.0)).abs() < 1e-5);
    896
        }
    897
    898
        #[test]
    899
        fn test_cos_empty() {
    900
            let v = Vector::<f32>::from_slice(&[]);
    901
            let result = v.cos().unwrap();
    902
            assert!(result.is_empty());
    903
        }
    904
    905
        #[test]
    906
        fn test_tan_basic() {
    907
            let v = Vector::from_slice(&[0.0, std::f32::consts::PI / 4.0]);
    908
            let result = v.tan().unwrap();
    909
            assert!((result.as_slice()[0] - 0.0).abs() < 1e-6);
    910
            assert!((result.as_slice()[1] - 1.0).abs() < 1e-5);
    911
        }
    912
    913
        #[test]
    914
        fn test_tan_empty() {
    915
            let v = Vector::<f32>::from_slice(&[]);
    916
            let result = v.tan().unwrap();
    917
            assert!(result.is_empty());
    918
        }
    919
    920
        #[test]
    921
        fn test_asin_basic() {
    922
            let v = Vector::from_slice(&[0.0, 0.5, 1.0]);
    923
            let result = v.asin().unwrap();
    924
            assert!((result.as_slice()[0] - 0.0).abs() < 1e-6);
    925
            assert!((result.as_slice()[1] - 0.5236).abs() < 1e-3); // ~π/6
    926
            assert!((result.as_slice()[2] - std::f32::consts::FRAC_PI_2).abs() < 1e-5);
    927
        }
    928
    929
        #[test]
    930
        fn test_asin_empty() {
    931
            let v = Vector::<f32>::from_slice(&[]);
    932
            let result = v.asin().unwrap();
    933
            assert!(result.is_empty());
    934
        }
    935
    936
        #[test]
    937
        fn test_acos_basic() {
    938
            let v = Vector::from_slice(&[1.0, 0.5, 0.0]);
    939
            let result = v.acos().unwrap();
    940
            assert!((result.as_slice()[0] - 0.0).abs() < 1e-6);
    941
            assert!((result.as_slice()[1] - 1.0472).abs() < 1e-3); // ~π/3
    942
            assert!((result.as_slice()[2] - std::f32::consts::FRAC_PI_2).abs() < 1e-5);
    943
        }
    944
    945
        #[test]
    946
        fn test_acos_empty() {
    947
            let v = Vector::<f32>::from_slice(&[]);
    948
            let result = v.acos().unwrap();
    949
            assert!(result.is_empty());
    950
        }
    951
    952
        #[test]
    953
        fn test_atan_basic() {
    954
            let v = Vector::from_slice(&[0.0, 1.0, -1.0]);
    955
            let result = v.atan().unwrap();
    956
            assert!((result.as_slice()[0] - 0.0).abs() < 1e-6);
    957
            assert!((result.as_slice()[1] - std::f32::consts::FRAC_PI_4).abs() < 1e-5);
    958
            assert!((result.as_slice()[2] - (-std::f32::consts::FRAC_PI_4)).abs() < 1e-5);
    959
        }
    960
    961
        #[test]
    962
        fn test_atan_empty() {
    963
            let v = Vector::<f32>::from_slice(&[]);
    964
            let result = v.atan().unwrap();
    965
            assert!(result.is_empty());
    966
        }
    967
    968
        // ========== Hyperbolic Functions ==========
    969
    970
        #[test]
    971
        fn test_sinh_basic() {
    972
            let v = Vector::from_slice(&[0.0, 1.0, -1.0]);
    973
            let result = v.sinh().unwrap();
    974
            assert!((result.as_slice()[0] - 0.0).abs() < 1e-6);
    975
            assert!((result.as_slice()[1] - 1.1752).abs() < 1e-3);
    976
            assert!((result.as_slice()[2] - (-1.1752)).abs() < 1e-3);
    977
        }
    978
    979
        #[test]
    980
        fn test_sinh_empty() {
    981
            let v = Vector::<f32>::from_slice(&[]);
    982
            let result = v.sinh().unwrap();
    983
            assert!(result.is_empty());
    984
        }
    985
    986
        #[test]
    987
        fn test_cosh_basic() {
    988
            let v = Vector::from_slice(&[0.0, 1.0, -1.0]);
    989
            let result = v.cosh().unwrap();
    990
            assert!((result.as_slice()[0] - 1.0).abs() < 1e-6);
    991
            assert!((result.as_slice()[1] - 1.5431).abs() < 1e-3);
    992
            assert!((result.as_slice()[2] - 1.5431).abs() < 1e-3); // cosh is even
    993
        }
    994
    995
        #[test]
    996
        fn test_cosh_empty() {
    997
            let v = Vector::<f32>::from_slice(&[]);
    998
            let result = v.cosh().unwrap();
    999
            assert!(result.is_empty());
    1000
        }
    1001
    1002
        #[test]
    1003
        fn test_tanh_basic() {
    1004
            let v = Vector::from_slice(&[0.0, 1.0, -1.0]);
    1005
            let result = v.tanh().unwrap();
    1006
            assert!((result.as_slice()[0] - 0.0).abs() < 1e-6);
    1007
            assert!((result.as_slice()[1] - 0.7616).abs() < 1e-3);
    1008
            assert!((result.as_slice()[2] - (-0.7616)).abs() < 1e-3);
    1009
        }
    1010
    1011
        #[test]
    1012
        fn test_tanh_empty() {
    1013
            let v = Vector::<f32>::from_slice(&[]);
    1014
            // tanh returns EmptyVector error for empty input
    1015
            assert!(matches!(v.tanh(), Err(TruenoError::EmptyVector)));
    1016
        }
    1017
    1018
        #[test]
    1019
        fn test_asinh_basic() {
    1020
            let v = Vector::from_slice(&[0.0, 1.0, -1.0]);
    1021
            let result = v.asinh().unwrap();
    1022
            assert!((result.as_slice()[0] - 0.0).abs() < 1e-6);
    1023
            assert!((result.as_slice()[1] - 0.8814).abs() < 1e-3);
    1024
            assert!((result.as_slice()[2] - (-0.8814)).abs() < 1e-3);
    1025
        }
    1026
    1027
        #[test]
    1028
        fn test_asinh_empty() {
    1029
            let v = Vector::<f32>::from_slice(&[]);
    1030
            let result = v.asinh().unwrap();
    1031
            assert!(result.is_empty());
    1032
        }
    1033
    1034
        #[test]
    1035
        fn test_acosh_basic() {
    1036
            let v = Vector::from_slice(&[1.0, 2.0, 3.0]);
    1037
            let result = v.acosh().unwrap();
    1038
            assert!((result.as_slice()[0] - 0.0).abs() < 1e-6);
    1039
            assert!((result.as_slice()[1] - 1.3170).abs() < 1e-3);
    1040
            assert!((result.as_slice()[2] - 1.7627).abs() < 1e-3);
    1041
        }
    1042
    1043
        #[test]
    1044
        fn test_acosh_empty() {
    1045
            let v = Vector::<f32>::from_slice(&[]);
    1046
            let result = v.acosh().unwrap();
    1047
            assert!(result.is_empty());
    1048
        }
    1049
    1050
        #[test]
    1051
        fn test_atanh_basic() {
    1052
            let v = Vector::from_slice(&[0.0, 0.5, -0.5]);
    1053
            let result = v.atanh().unwrap();
    1054
            assert!((result.as_slice()[0] - 0.0).abs() < 1e-6);
    1055
            assert!((result.as_slice()[1] - 0.5493).abs() < 1e-3);
    1056
            assert!((result.as_slice()[2] - (-0.5493)).abs() < 1e-3);
    1057
        }
    1058
    1059
        #[test]
    1060
        fn test_atanh_empty() {
    1061
            let v = Vector::<f32>::from_slice(&[]);
    1062
            let result = v.atanh().unwrap();
    1063
            assert!(result.is_empty());
    1064
        }
    1065
    1066
        // ========== Backend-specific Tests ==========
    1067
    1068
        #[test]
    1069
        fn test_exp_scalar_backend() {
    1070
            let v = Vector::from_slice_with_backend(&[0.0, 1.0, 2.0], Backend::Scalar);
    1071
            let result = v.exp().unwrap();
    1072
            assert!((result.as_slice()[0] - 1.0).abs() < 1e-6);
    1073
        }
    1074
    1075
        #[test]
    1076
        #[cfg(target_arch = "x86_64")]
    1077
        fn test_exp_sse2_backend() {
    1078
            let v = Vector::from_slice_with_backend(&[0.0, 1.0, 2.0, 3.0], Backend::SSE2);
    1079
            let result = v.exp().unwrap();
    1080
            assert!((result.as_slice()[0] - 1.0).abs() < 1e-6);
    1081
        }
    1082
    1083
        #[test]
    1084
        #[cfg(target_arch = "x86_64")]
    1085
        fn test_exp_avx2_backend() {
    1086
            if !is_x86_feature_detected!("avx2") {
    1087
                return;
    1088
            }
    1089
            let v = Vector::from_slice_with_backend(&[0.0; 16], Backend::AVX2);
    1090
            let result = v.exp().unwrap();
    1091
            for val in result.as_slice() {
    1092
                assert!((val - 1.0).abs() < 1e-6);
    1093
            }
    1094
        }
    1095
    1096
        #[test]
    1097
        fn test_sin_scalar_backend() {
    1098
            let v = Vector::from_slice_with_backend(&[0.0, std::f32::consts::FRAC_PI_2], Backend::Scalar);
    1099
            let result = v.sin().unwrap();
    1100
            assert!((result.as_slice()[0] - 0.0).abs() < 1e-6);
    1101
            assert!((result.as_slice()[1] - 1.0).abs() < 1e-6);
    1102
        }
    1103
    1104
        #[test]
    1105
        fn test_cos_scalar_backend() {
    1106
            let v = Vector::from_slice_with_backend(&[0.0, std::f32::consts::PI], Backend::Scalar);
    1107
            let result = v.cos().unwrap();
    1108
            assert!((result.as_slice()[0] - 1.0).abs() < 1e-6);
    1109
            assert!((result.as_slice()[1] - (-1.0)).abs() < 1e-5);
    1110
        }
    1111
    1112
        // ========== Large Array Tests ==========
    1113
    1114
        #[test]
    1115
        fn test_exp_large() {
    1116
            let v = Vector::from_slice(&[1.0; 1000]);
    1117
            let result = v.exp().unwrap();
    1118
            assert_eq!(result.len(), 1000);
    1119
            for val in result.as_slice() {
    1120
                assert!((val - std::f32::consts::E).abs() < 1e-5);
    1121
            }
    1122
        }
    1123
    1124
        #[test]
    1125
        fn test_sin_large() {
    1126
            let v = Vector::from_slice(&[0.0; 1000]);
    1127
            let result = v.sin().unwrap();
    1128
            assert_eq!(result.len(), 1000);
    1129
            for val in result.as_slice() {
    1130
                assert!((val - 0.0).abs() < 1e-6);
    1131
            }
    1132
        }
    1133
    1134
        // ========== Inverse Relationship Tests ==========
    1135
    1136
        #[test]
    1137
        fn test_exp_ln_inverse() {
    1138
            let v = Vector::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0]);
    1139
            let exp_result = v.exp().unwrap();
    1140
            let roundtrip = exp_result.ln().unwrap();
    1141
            for (orig, rt) in v.as_slice().iter().zip(roundtrip.as_slice()) {
    1142
                assert!((orig - rt).abs() < 1e-5);
    1143
            }
    1144
        }
    1145
    1146
        #[test]
    1147
        fn test_sin_asin_inverse() {
    1148
            let v = Vector::from_slice(&[0.0, 0.3, 0.5, 0.7]);
    1149
            let sin_result = v.sin().unwrap();
    1150
            let roundtrip = sin_result.asin().unwrap();
    1151
            for (orig, rt) in v.as_slice().iter().zip(roundtrip.as_slice()) {
    1152
                assert!((orig - rt).abs() < 1e-5);
    1153
            }
    1154
        }
    1155
    1156
        #[test]
    1157
        fn test_cos_acos_inverse() {
    1158
            let v = Vector::from_slice(&[0.0, 0.3, 0.5, 0.7]);
    1159
            let cos_result = v.cos().unwrap();
    1160
            let roundtrip = cos_result.acos().unwrap();
    1161
            for (orig, rt) in v.as_slice().iter().zip(roundtrip.as_slice()) {
    1162
                assert!((orig - rt).abs() < 1e-5);
    1163
            }
    1164
        }
    1165
    1166
        #[test]
    1167
        fn test_sinh_asinh_inverse() {
    1168
            let v = Vector::from_slice(&[0.0, 1.0, 2.0, -1.0, -2.0]);
    1169
            let sinh_result = v.sinh().unwrap();
    1170
            let roundtrip = sinh_result.asinh().unwrap();
    1171
            for (orig, rt) in v.as_slice().iter().zip(roundtrip.as_slice()) {
    1172
                assert!((orig - rt).abs() < 1e-4);
    1173
            }
    1174
        }
    1175
    1176
        #[test]
    1177
        fn test_tanh_atanh_inverse() {
    1178
            let v = Vector::from_slice(&[0.0, 0.3, 0.5, -0.3, -0.5]);
    1179
            let tanh_result = v.tanh().unwrap();
    1180
            let roundtrip = tanh_result.atanh().unwrap();
    1181
            for (orig, rt) in v.as_slice().iter().zip(roundtrip.as_slice()) {
    1182
                assert!((orig - rt).abs() < 1e-4);
    1183
            }
    1184
        }
    1185
    }