// SPDX-License-Identifier: BSD-3-Clause
// Copyright (C) 2026 Vallés Puig, Ramon

use qtty_core::*;

// Use Length as the test dimension.
type TestDim = Length;

#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
pub enum TestUnit {}
impl Unit for TestUnit {
    const RATIO: f64 = 1.0;
    type Dim = TestDim;
    const SYMBOL: &'static str = "tu";
}

#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
pub enum DoubleTestUnit {}
impl Unit for DoubleTestUnit {
    const RATIO: f64 = 2.0;
    type Dim = TestDim;
    const SYMBOL: &'static str = "dtu";
}

#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
pub enum HalfTestUnit {}
impl Unit for HalfTestUnit {
    const RATIO: f64 = 0.5;
    type Dim = TestDim;
    const SYMBOL: &'static str = "htu";
}

// Register test units for arithmetic.
qtty_core::impl_unit_arithmetic_pairs!(TestUnit, DoubleTestUnit, HalfTestUnit);

type TU = Quantity<TestUnit>;
type Dtu = Quantity<DoubleTestUnit>;

#[test]
fn quantity_new_and_value() {
    let q = TU::new(42.0);
    assert_eq!(q.value(), 42.0);
}

#[test]
fn quantity_nan_constant() {
    assert!(TU::NAN.value().is_nan());
}

#[test]
fn quantity_abs() {
    assert_eq!(TU::new(-5.0).abs().value(), 5.0);
    assert_eq!(TU::new(5.0).abs().value(), 5.0);
    assert_eq!(TU::new(0.0).abs().value(), 0.0);
}

#[test]
fn quantity_ceil() {
    assert_eq!(TU::new(3.2).ceil().value(), 4.0);
    assert_eq!(TU::new(-3.2).ceil().value(), -3.0);
    assert_eq!(TU::new(5.0).ceil().value(), 5.0);
}

#[test]
fn quantity_floor() {
    assert_eq!(TU::new(3.7).floor().value(), 3.0);
    assert_eq!(TU::new(-3.7).floor().value(), -4.0);
    assert_eq!(TU::new(5.0).floor().value(), 5.0);
}

#[test]
fn quantity_round() {
    assert_eq!(TU::new(3.5).round().value(), 4.0);
    assert_eq!(TU::new(3.4).round().value(), 3.0);
    assert_eq!(TU::new(-3.5).round().value(), -4.0);
}

#[test]
fn quantity_trunc() {
    assert_eq!(TU::new(3.9).trunc().value(), 3.0);
    assert_eq!(TU::new(-3.9).trunc().value(), -3.0);
}

#[test]
fn quantity_fract() {
    assert!((TU::new(3.75).fract().value() - 0.75).abs() < 1e-12);
    assert!((TU::new(-3.75).fract().value() + 0.75).abs() < 1e-12);
}

#[test]
fn quantity_from_f64() {
    let q: TU = 123.456.into();
    assert_eq!(q.value(), 123.456);
}

#[test]
fn quantity_has_scalar_layout() {
    assert_eq!(core::mem::size_of::<TU>(), core::mem::size_of::<f64>());
    assert_eq!(core::mem::align_of::<TU>(), core::mem::align_of::<f64>());
}

#[test]
fn quantity_conversion_to_same_unit() {
    let q = TU::new(10.0);
    let converted = q.to::<TestUnit>();
    assert_eq!(converted.value(), 10.0);
}

#[test]
fn exact_checked_lossy_same_ratio_preserves_large_integer() {
    let q = Quantity::<TestUnit, i64>::new(i64::MAX);
    let converted = q.checked_to_lossy::<TestUnit>().unwrap();
    assert_eq!(converted.value(), i64::MAX);
}

#[test]
fn quantity_conversion_to_different_unit() {
    let q = TU::new(10.0);
    let converted = q.to::<DoubleTestUnit>();
    assert!((converted.value() - 5.0).abs() < 1e-12);
}

#[test]
fn quantity_conversion_roundtrip() {
    let original = TU::new(100.0);
    let converted = original.to::<DoubleTestUnit>();
    let back = converted.to::<TestUnit>();
    assert!((back.value() - original.value()).abs() < 1e-12);
}

#[test]
fn const_add() {
    let a = TU::new(3.0);
    let b = TU::new(7.0);
    assert_eq!(a.const_add(b).value(), 10.0);
}

#[test]
fn const_sub() {
    let a = TU::new(10.0);
    let b = TU::new(3.0);
    assert_eq!(a.const_sub(b).value(), 7.0);
}

#[test]
fn const_mul() {
    let a = TU::new(4.0);
    let b = 5.0;
    assert_eq!(a.const_mul(b).value(), 20.0);
}

#[test]
fn const_div() {
    let a = TU::new(20.0);
    let b = 4.0;
    assert_eq!(a.const_div(b).value(), 5.0);
}

#[test]
fn const_min() {
    let a = TU::new(5.0);
    let b = TU::new(3.0);
    assert_eq!(a.min(b).value(), 3.0);
    assert_eq!(b.min(a).value(), 3.0);
}

#[test]
fn quantity_clamp_with_valid_bounds() {
    let q = TU::new(7.5);
    assert_eq!(q.clamp(TU::new(0.0), TU::new(10.0)).value(), 7.5);
    assert_eq!(q.clamp(TU::new(8.0), TU::new(10.0)).value(), 8.0);
    assert_eq!(q.clamp(TU::new(0.0), TU::new(7.0)).value(), 7.0);
}

#[cfg(debug_assertions)]
#[test]
#[should_panic(expected = "Quantity::clamp requires min_val <= max_val")]
fn quantity_clamp_panics_on_inverted_bounds_in_debug() {
    let _ = TU::new(5.0).clamp(TU::new(10.0), TU::new(0.0));
}

#[test]
fn operator_add() {
    let a = TU::new(3.0);
    let b = TU::new(7.0);
    assert_eq!((a + b).value(), 10.0);
}

#[test]
fn operator_sub() {
    let a = TU::new(10.0);
    let b = TU::new(3.0);
    assert_eq!((a - b).value(), 7.0);
}

#[test]
fn operator_mul_by_f64() {
    let q = TU::new(5.0);
    assert_eq!((q * 3.0).value(), 15.0);
    assert_eq!((3.0 * q).value(), 15.0);
}

#[test]
fn operator_div_by_f64() {
    let q = TU::new(15.0);
    assert_eq!((q / 3.0).value(), 5.0);
}

#[test]
fn operator_neg() {
    let q = TU::new(5.0);
    assert_eq!((-q).value(), -5.0);
    assert_eq!((-(-q)).value(), 5.0);
}

#[test]
fn operator_rem() {
    let q = TU::new(10.0);
    assert_eq!((q % 3.0).value(), 1.0);
}

#[test]
fn operator_add_assign() {
    let mut q = TU::new(5.0);
    q += TU::new(3.0);
    assert_eq!(q.value(), 8.0);
}

#[test]
fn operator_sub_assign() {
    let mut q = TU::new(10.0);
    q -= TU::new(3.0);
    assert_eq!(q.value(), 7.0);
}

#[test]
fn iterator_sum_quantity_owned() {
    let values = vec![TU::new(1.0), TU::new(2.5), TU::new(3.5)];
    let total: TU = values.into_iter().sum();
    assert!((total.value() - 7.0).abs() < 1e-12);
}

#[test]
fn iterator_sum_quantity_borrowed() {
    let values = [TU::new(1.0), TU::new(2.5), TU::new(3.5)];
    let total: TU = values.iter().sum();
    assert!((total.value() - 7.0).abs() < 1e-12);
}

#[test]
fn operator_div_assign() {
    let mut q = TU::new(20.0);
    q /= 4.0;
    assert_eq!(q.value(), 5.0);
}

#[test]
fn partial_eq_same_unit() {
    let q = TU::new(5.0);
    assert!(q == TU::new(5.0));
    assert!(!(q == TU::new(4.0)));
}

#[test]
fn division_same_unit_gives_raw_scalar() {
    let a = TU::new(100.0);
    let b = TU::new(20.0);
    let ratio: f64 = a / b;
    assert!((ratio - 5.0).abs() < 1e-12);
}

#[test]
fn division_creates_per_type() {
    let num = TU::new(100.0);
    let den = Dtu::new(20.0);
    let ratio: Quantity<Per<TestUnit, DoubleTestUnit>> = num / den;
    assert!((ratio.value() - 5.0).abs() < 1e-12);
}

#[test]
fn per_ratio_conversion() {
    let v1: Quantity<Per<DoubleTestUnit, TestUnit>> = Quantity::new(10.0);
    let v2: Quantity<Per<TestUnit, TestUnit>> = v1.to();
    assert!((v2.value() - 20.0).abs() < 1e-12);
}

#[test]
fn per_multiplication_recovers_numerator() {
    let rate: Quantity<Per<TestUnit, DoubleTestUnit>> = Quantity::new(5.0);
    let time = Dtu::new(4.0);
    // UnitMul: Per<TU, DTU> * DTU → TU (recovery impl)
    let result: TU = rate * time;
    assert!((result.value() - 20.0).abs() < 1e-12);
}

#[test]
fn per_multiplication_commutative() {
    let rate: Quantity<Per<TestUnit, DoubleTestUnit>> = Quantity::new(5.0);
    let time = Dtu::new(4.0);
    let result1: TU = rate * time;
    let result2: TU = time * rate;
    assert!((result1.value() - result2.value()).abs() < 1e-12);
}

#[test]
fn dimensionless_asin_angle() {
    use qtty_core::units::angular::Radian;
    // Per<TestUnit, DoubleTestUnit> is Dimensionless → asin_angle available
    let ratio: Quantity<Per<TestUnit, DoubleTestUnit>> = Quantity::new(0.5);
    let result: Quantity<Radian> = ratio.asin_angle();
    assert!((result.value() - 0.5_f64.asin()).abs() < 1e-12);
}

#[test]
fn same_unit_ratio_is_raw_scalar() {
    // Same-unit division now returns the raw f64 scalar.
    let ratio: f64 = TU::new(1.0) / TU::new(2.0);
    assert!((ratio - 0.5).abs() < 1e-12);
}

#[test]
fn dimensionless_asin_angle_boundary_values() {
    let one: Quantity<Per<TestUnit, DoubleTestUnit>> = Quantity::new(1.0);
    assert!((one.asin_angle().value() - core::f64::consts::FRAC_PI_2).abs() < 1e-12);

    let neg_one: Quantity<Per<TestUnit, DoubleTestUnit>> = Quantity::new(-1.0);
    assert!((neg_one.asin_angle().value() - (-core::f64::consts::FRAC_PI_2)).abs() < 1e-12);

    let zero: Quantity<Per<TestUnit, DoubleTestUnit>> = Quantity::new(0.0);
    assert!((zero.asin_angle().value() - 0.0).abs() < 1e-12);
}

#[test]
fn asin_angle_to_degrees() {
    use qtty_core::units::angular::{Degree, Radian};

    let ratio: Quantity<Per<TestUnit, DoubleTestUnit>> = Quantity::new(0.5);
    let angle: Quantity<Radian> = ratio.asin_angle();
    let deg: Quantity<Degree> = angle.to();

    assert!((deg.value() - 30.0).abs() < 1e-10);
}

#[test]
fn asin_angle_sin_roundtrip() {
    let ratio: Quantity<Per<TestUnit, DoubleTestUnit>> = Quantity::new(0.75);
    let angle = ratio.asin_angle();

    assert!((angle.sin() - 0.75).abs() < 1e-12);
}

#[test]
fn display_simple_quantity() {
    let q = TU::new(42.5);
    let s = format!("{} {}", q.value(), TestUnit::SYMBOL);
    assert_eq!(s, "42.5 tu");
}

#[test]
fn display_per_quantity() {
    let q: Quantity<Per<TestUnit, DoubleTestUnit>> = Quantity::new(2.5);
    let s = format!("{}", q);
    assert_eq!(s, "2.5 tu/dtu");
}

#[test]
fn display_negative_value() {
    let q = TU::new(-99.9);
    let s = format!("{} {}", q.value(), TestUnit::SYMBOL);
    assert_eq!(s, "-99.9 tu");
}

#[test]
fn edge_case_zero() {
    let zero = TU::new(0.0);
    assert_eq!(zero.value(), 0.0);
    assert_eq!((-zero).value(), 0.0);
    assert_eq!(zero.abs().value(), 0.0);
}

#[test]
fn edge_case_negative_values() {
    let neg = TU::new(-10.0);
    let pos = TU::new(5.0);

    assert_eq!((neg + pos).value(), -5.0);
    assert_eq!((neg - pos).value(), -15.0);
    assert_eq!((neg * 2.0).value(), -20.0);
    assert_eq!(neg.abs().value(), 10.0);
}

#[test]
fn edge_case_large_values() {
    let large = TU::new(1e100);
    let small = TU::new(1e-100);
    assert_eq!(large.value(), 1e100);
    assert_eq!(small.value(), 1e-100);
}

#[test]
fn edge_case_infinity() {
    let inf = TU::new(f64::INFINITY);
    let neg_inf = TU::new(f64::NEG_INFINITY);

    assert!(inf.value().is_infinite());
    assert!(neg_inf.value().is_infinite());
    assert_eq!(inf.value().signum(), 1.0);
    assert_eq!(neg_inf.value().signum(), -1.0);
}

#[test]
fn mean_positive_infinity_stays_infinite() {
    let inf = TU::INFINITY;
    assert!(inf.mean(inf).value().is_infinite());
    assert!(inf.mean(inf).value() > 0.0);
}

#[test]
fn mean_negative_infinity_stays_infinite() {
    let neg_inf = TU::NEG_INFINITY;
    assert!(neg_inf.mean(neg_inf).value().is_infinite());
    assert!(neg_inf.mean(neg_inf).value() < 0.0);
}

#[test]
fn mean_finite_values_unaffected() {
    assert_eq!(TU::new(10.0).mean(TU::new(14.0)).value(), 12.0);
    assert_eq!(
        TU::new(i64::MAX as f64)
            .mean(TU::new(i64::MAX as f64))
            .value(),
        i64::MAX as f64
    );
}

#[test]
fn mean_pos_infinity_with_finite_stays_infinite() {
    let inf = TU::INFINITY;
    let fin = TU::new(1.0);

    assert!(inf.mean(fin).value().is_infinite());
    assert!(inf.mean(fin).value() > 0.0);
    assert!(fin.mean(inf).value().is_infinite());
    assert!(fin.mean(inf).value() > 0.0);
}

#[test]
fn mean_neg_infinity_with_finite_stays_infinite() {
    let neg_inf = TU::NEG_INFINITY;
    let fin = TU::new(-1.0);

    assert!(neg_inf.mean(fin).value().is_infinite());
    assert!(neg_inf.mean(fin).value() < 0.0);
    assert!(fin.mean(neg_inf).value().is_infinite());
    assert!(fin.mean(neg_inf).value() < 0.0);
}

// ─────────────────────────────────────────────────────────────────────────
// Additional method coverage tests
// ─────────────────────────────────────────────────────────────────────────

#[test]
fn test_is_nan() {
    let nan = TU::new(f64::NAN);
    let normal = TU::new(42.0);
    assert!(nan.is_nan());
    assert!(!normal.is_nan());
}

#[test]
fn test_is_infinite() {
    let inf = TU::new(f64::INFINITY);
    let neg_inf = TU::new(f64::NEG_INFINITY);
    let normal = TU::new(42.0);
    assert!(inf.is_infinite());
    assert!(neg_inf.is_infinite());
    assert!(!normal.is_infinite());
}

#[test]
fn test_is_finite() {
    let normal = TU::new(42.0);
    let inf = TU::new(f64::INFINITY);
    let nan = TU::new(f64::NAN);
    assert!(normal.is_finite());
    assert!(!inf.is_finite());
    assert!(!nan.is_finite());
}

#[test]
fn test_signum() {
    let pos = TU::new(42.0);
    let neg = TU::new(-42.0);
    assert_eq!(pos.signum(), 1.0);
    assert_eq!(neg.signum(), -1.0);
}

#[test]
fn test_scalar_sqrt() {
    let q = TU::new(16.0);
    let sqrt_val = q.scalar_sqrt();
    assert!((sqrt_val - 4.0).abs() < 1e-12);
}

#[test]
fn test_cast() {
    let q = TU::new(42.5);
    let q_f32: Quantity<TestUnit, f32> = q.cast();
    assert!((q_f32.value() - 42.5).abs() < 0.01);
}

#[test]
fn test_max() {
    let a = TU::new(5.0);
    let b = TU::new(10.0);
    assert_eq!(a.max(b).value(), 10.0);
    assert_eq!(b.max(a).value(), 10.0);
}

#[test]
fn test_mean() {
    let a = TU::new(10.0);
    let b = TU::new(14.0);
    assert_eq!(a.mean(b).value(), 12.0);
    assert_eq!(b.mean(a).value(), 12.0);
}

#[test]
fn test_acos_angle() {
    use qtty_core::units::angular::Radian;
    let ratio: Quantity<Per<TestUnit, DoubleTestUnit>> = Quantity::new(0.5);
    let result: Quantity<Radian> = ratio.acos_angle();
    assert!((result.value() - 0.5_f64.acos()).abs() < 1e-12);
}

#[test]
fn test_atan_angle() {
    use qtty_core::units::angular::Radian;
    let ratio: Quantity<Per<TestUnit, DoubleTestUnit>> = Quantity::new(1.0);
    let result: Quantity<Radian> = ratio.atan_angle();
    assert!((result.value() - core::f64::consts::FRAC_PI_4).abs() < 1e-12);
}

#[test]
fn test_to_lossy() {
    use qtty_core::units::length::{Kilometer, Meter};
    let km: Quantity<Kilometer, i32> = Quantity::new(5);
    let m: Quantity<Meter, i32> = km.to_lossy();
    assert_eq!(m.value(), 5000);
}

#[test]
fn test_value_ref() {
    let q = TU::new(42.5);
    assert_eq!(*q.value_ref(), 42.5);
}

#[test]
fn test_zero_one() {
    let zero = TU::zero();
    let one = TU::one();
    assert_eq!(zero.value(), 0.0);
    assert_eq!(one.value(), 1.0);
}

#[test]
fn test_min_const() {
    let a = TU::new(3.0);
    let b = TU::new(7.0);
    assert_eq!(a.min_const(b).value(), 3.0);
}

#[test]
fn test_min_const_else_branch() {
    let a = TU::new(7.0);
    let b = TU::new(3.0);
    assert_eq!(a.min_const(b).value(), 3.0);
}

#[test]
fn test_max_const() {
    let a = TU::new(3.0);
    let b = TU::new(7.0);
    assert_eq!(a.max_const(b).value(), 7.0);
}

#[test]
fn test_max_const_then_branch() {
    let a = TU::new(7.0);
    let b = TU::new(3.0);
    assert_eq!(a.max_const(b).value(), 7.0);
}

#[test]
fn test_to_const() {
    let q = TU::new(10.0);
    let converted: Dtu = q.to_const();
    assert!((converted.value() - 5.0).abs() < 1e-12);
}

// ─────────────────────────────────────────────────────────────────────────
// Comparison operator tests
// ─────────────────────────────────────────────────────────────────────────

#[test]
fn partial_ord_same_unit() {
    let a = TU::new(3.0);
    let b = TU::new(7.0);
    assert!(a < b);
    assert!(a <= b);
    assert!(b > a);
    assert!(b >= a);
    assert!(a <= a);
    assert!(a >= a);
}

#[test]
fn partial_ord_same_unit_more() {
    let q = TU::new(5.0);
    assert!(q > TU::new(3.0));
    assert!(q >= TU::new(5.0));
    assert!(q < TU::new(10.0));
    assert!(q <= TU::new(5.0));
}

#[test]
fn partial_ord_nan_returns_none() {
    let nan = TU::new(f64::NAN);
    let normal = TU::new(5.0);
    // NaN comparisons are unordered
    assert!(nan.partial_cmp(&normal).is_none());
    assert!(!(nan == normal));
}

#[test]
fn eq_ord_integers() {
    use core::cmp::Ordering;
    let a: Quantity<TestUnit, i32> = Quantity::new(3);
    let b: Quantity<TestUnit, i32> = Quantity::new(7);
    let c: Quantity<TestUnit, i32> = Quantity::new(3);

    // Eq
    assert_eq!(a, c);
    assert_ne!(a, b);

    // Ord
    assert_eq!(a.cmp(&b), Ordering::Less);
    assert_eq!(b.cmp(&a), Ordering::Greater);
    assert_eq!(a.cmp(&c), Ordering::Equal);
}

#[test]
fn ord_integers_sort() {
    let mut quantities: Vec<Quantity<TestUnit, i32>> = vec![
        Quantity::new(5),
        Quantity::new(1),
        Quantity::new(3),
        Quantity::new(2),
        Quantity::new(4),
    ];
    quantities.sort();
    let values: Vec<i32> = quantities.iter().map(|q| q.value()).collect();
    assert_eq!(values, vec![1, 2, 3, 4, 5]);
}

#[test]
fn ord_integers_min_max() {
    let a: Quantity<TestUnit, i32> = Quantity::new(3);
    let b: Quantity<TestUnit, i32> = Quantity::new(7);
    assert_eq!(a.min(b).value(), 3);
    assert_eq!(a.max(b).value(), 7);
    assert_eq!(a.mean(b).value(), 5);
}

#[test]
fn ord_integers_btreemap() {
    use std::collections::BTreeMap;
    let mut map: BTreeMap<Quantity<TestUnit, i32>, &str> = BTreeMap::new();
    map.insert(Quantity::new(3), "three");
    map.insert(Quantity::new(1), "one");
    map.insert(Quantity::new(2), "two");
    let keys: Vec<i32> = map.keys().map(|q| q.value()).collect();
    assert_eq!(keys, vec![1, 2, 3]);
}

// ─────────────────────────────────────────────────────────────────────────
// Cross-unit comparison tests
// ─────────────────────────────────────────────────────────────────────────

#[test]
fn cross_unit_eq_method() {
    // DoubleTestUnit has ratio 2.0, so 5 dtu = 10 tu
    let dtu = Dtu::new(5.0);
    let tu = TU::new(10.0);
    assert!(dtu.eq_unit(&tu));
    assert!(tu.eq_unit(&dtu));
}

#[test]
fn cross_unit_cmp_method() {
    use core::cmp::Ordering;
    let dtu = Dtu::new(5.0); // = 10 tu
    let tu_less = TU::new(3.0);
    let tu_more = TU::new(20.0);
    let tu_eq = TU::new(10.0);

    assert_eq!(dtu.cmp_unit(&tu_less), Some(Ordering::Greater));
    assert_eq!(dtu.cmp_unit(&tu_more), Some(Ordering::Less));
    assert_eq!(dtu.cmp_unit(&tu_eq), Some(Ordering::Equal));
}

#[test]
#[cfg(feature = "cross-unit-ops")]
fn cross_unit_operators_via_macro() {
    use qtty_core::units::length::{Kilometer, Meter};

    let km: Quantity<Kilometer> = Quantity::new(1.0);
    let m: Quantity<Meter> = Quantity::new(500.0);

    // 1 km > 500 m
    assert!(km > m);
    assert!(km >= m);
    assert!(m < km);
    assert!(m <= km);
    assert!(km != m);

    // 1 km == 1000 m
    let m_eq: Quantity<Meter> = Quantity::new(1000.0);
    assert!(km == m_eq);
    assert!(km >= m_eq);
    assert!(km <= m_eq);
    // Also test the reverse direction (rest == first) to cover all eq branches
    assert!(m_eq == km);
    assert!(m_eq >= km);
    assert!(m_eq <= km);
}

#[test]
#[cfg(feature = "cross-unit-ops")]
fn cross_unit_operators_f32() {
    use qtty_core::units::length::{Kilometer, Meter};

    let km: Quantity<Kilometer, f32> = Quantity::new(2.0_f32);
    let m: Quantity<Meter, f32> = Quantity::new(1500.0_f32);

    assert!(km > m);
    assert!(m < km);
}

#[test]
#[cfg(feature = "cross-unit-ops")]
fn cross_unit_nan_comparison() {
    use qtty_core::units::length::{Kilometer, Meter};

    let km_nan: Quantity<Kilometer> = Quantity::new(f64::NAN);
    let m: Quantity<Meter> = Quantity::new(1000.0);

    assert!(!(km_nan == m));
    assert!(km_nan.partial_cmp(&m).is_none());
}

// ─────────────────────────────────────────────────────────────────────────────
// tests: Ratio arithmetic, exp/ln roundtrip, powi/powf, ratio_to
// ─────────────────────────────────────────────────────────────────────────────

#[test]
fn ratio_basic_arithmetic() {
    use qtty_core::units::dimensionless::Ratios;

    let a = Ratios::new(3.0);
    let b = Ratios::new(2.0);

    assert_eq!((a + b).value(), 5.0);
    assert_eq!((a - b).value(), 1.0);
    // scalar multiplication
    assert_eq!((a * 2.0).value(), 6.0);
    // same-unit division returns raw scalar
    let raw: f64 = a / b;
    assert!((raw - 1.5).abs() < 1e-12);
}

#[test]
#[cfg(feature = "std")]
fn ratio_exp_ln_roundtrip() {
    use qtty_core::units::dimensionless::Ratios;

    let x = Ratios::new(1.5);
    let roundtrip = x.exp().ln();
    assert!((roundtrip.value() - 1.5).abs() < 1e-12);
}

#[test]
#[cfg(feature = "std")]
fn ratio_powi() {
    use qtty_core::units::dimensionless::Ratios;

    let x = Ratios::new(2.0);
    let result = x.powi(3);
    assert!((result.value() - 8.0).abs() < 1e-12);
}

#[test]
#[cfg(feature = "std")]
fn ratio_powf() {
    use qtty_core::units::dimensionless::{Ratio, Ratios};
    use qtty_core::Quantity;

    let base = Ratios::new(4.0);
    let exp = Quantity::<Ratio>::new(0.5);
    let result = base.powf(exp);
    // 4^0.5 == 2
    assert!((result.value() - 2.0).abs() < 1e-12);
}

#[test]
#[cfg(feature = "std")]
fn ratio_to_same_unit_division() {
    use qtty_core::units::dimensionless::Ratios;
    use qtty_core::units::length::Meters;

    let a = Meters::new(10.0);
    let b = Meters::new(4.0);
    let r: Ratios = a.ratio_to(b);
    assert!((r.value() - 2.5).abs() < 1e-12);
}
