#![deny(rustdoc::broken_intra_doc_links)]

//! Comprehensive type-safe system for mathematical intervals with generic scalar support.
//!
//! This module provides a complete interval arithmetic system that supports all standard interval types
//! and operations while ensuring correctness through Rust's type system and extensive validation.
//! The design is fully generic over any type implementing [`num_valid::RealScalar`], providing
//! unprecedented flexibility and safety.
//!
//! ## Generic Design with [`num_valid::RealScalar`]
//!
//! **All interval types are generic over any scalar type implementing [`num_valid::RealScalar`].**
//! This enables the same interval algorithms to work seamlessly across different scalar types
//! with varying performance and safety characteristics.
//!
//! ### Supported Scalar Types
//!
//! | Scalar Type | Performance | Validation | Best For |
//! |-------------|-------------|------------|----------|
//! | [`f64`] | ⚡⚡⚡ Maximum | ❌ None | Trusted input, absolute maximum speed |
//! | [`RealNative64StrictFiniteInDebug`](num_valid::RealNative64StrictFiniteInDebug) | ⚡⚡⚡ **Same as f64** | ✅ **Debug only** | **Recommended for most applications** |
//! | [`RealNative64StrictFinite`](num_valid::RealNative64StrictFinite) | ⚡⚡ Small overhead | ✅ Always | Safety-critical applications |
//! | `RealRugStrictFinite` | ⚡ Precision-dependent | ✅ Always | Arbitrary precision needs (available from the [num-valid](https://crates.io/crates/num-valid) crate when compiled with `--features=rug`)|
//!
//! ### Benefits of Generic Design
//!
//! 1. **Write Once, Use Everywhere**: Same algorithms work with any scalar type
//! 2. **Zero-Cost Safety**: [`RealNative64StrictFiniteInDebug`](num_valid::RealNative64StrictFiniteInDebug) gives f64 performance with debug validation
//! 3. **Safety by Choice**: Use validated types to eliminate floating-point edge cases
//! 4. **Precision by Demand**: Switch from f64 to arbitrary precision without code changes
//! 5. **Development Safety**: Catch NaN/∞ errors during development without release performance penalty
//!
//! ## Architecture Overview
//!
//! The module is built around a four-tier hierarchy:
//!
//! 1. **Bound Types**: [`LowerBoundOpen`](crate::bounds::LowerBoundOpen), [`LowerBoundClosed`](crate::bounds::LowerBoundClosed), [`UpperBoundOpen`](crate::bounds::UpperBoundOpen), [`UpperBoundClosed`](crate::bounds::UpperBoundClosed)
//!    - Strongly-typed representations of interval boundaries
//!    - Prevent mixing of open and closed bounds incorrectly
//!    - Include validation and comparison logic
//!
//! 2. **Concrete Interval Types**: All mathematical interval variants
//!    - [`IntervalClosed`]: `[a, b]` - both bounds finite and included
//!    - [`IntervalOpen`]: `(a, b)` - both bounds finite and excluded
//!    - [`IntervalLowerClosedUpperOpen`]: `[a, b)` - left closed, right open
//!    - [`IntervalLowerOpenUpperClosed`]: `(a, b]` - left open, right closed
//!    - [`IntervalLowerClosedUpperUnbounded`]: `[a, +∞)` - bounded below, unbounded above
//!    - [`IntervalLowerOpenUpperUnbounded`]: `(a, +∞)` - strict lower bound, unbounded above
//!    - [`IntervalLowerUnboundedUpperClosed`]: `(-∞, b]` - unbounded below, bounded above
//!    - [`IntervalLowerUnboundedUpperOpen`]: `(-∞, b)` - unbounded below, strict upper bound
//!    - [`IntervalSingleton`]: `{a}` - single point intervals
//!
//! 3. **Interval Categories**: Enum wrappers grouping related interval types
//!    - [`IntervalFinitePositiveLength`]: All bounded intervals with positive length
//!    - [`IntervalInfiniteLength`]: All unbounded intervals
//!    - Provide unified interfaces for similar intervals
//!
//! 4. **Universal Interface**: [`Interval`] enum and [`IntervalTrait`]
//!    - Top-level abstraction for all interval types
//!    - Enables fully generic interval programming
//!    - Provides conversion and interop between all types
//!
//! ## Design Philosophy
//!
//! ### Type Safety First
//! - Impossible to create invalid intervals (e.g., `[2., 1.]`)
//! - Boundary types prevent confusion between open/closed bounds
//! - Compile-time guarantees about interval properties
//!
//! ### Zero-Cost Abstractions
//! - Newtypes have zero runtime cost
//! - Generic code specializes to concrete types
//! - Debug assertions provide safety without release overhead
//!
//! ### Mathematical Correctness
//! - Proper handling of boundary cases
//! - Consistent intersection and containment logic
//! - Support for both discrete and continuous domains
//!
//! ## Quick Start Guide
//!
//! ### Recommended Approach for Most Applications
//!
//! ```rust
//! use grid1d::intervals::*;
//! use num_valid::RealNative64StrictFiniteInDebug;
//! use try_create::TryNew;
//!
//! // Define your optimal type alias
//! type Real = RealNative64StrictFiniteInDebug;
//!
//! // This gives you f64 performance with debug-time safety
//! let interval = IntervalClosed::new(
//!     Real::try_new(0.0).unwrap(),
//!     Real::try_new(1.0).unwrap()
//! );
//!
//! // All operations compile to the same optimized assembly as raw f64
//! assert_eq!(interval.length().as_ref(), &1.0);
//! assert!(interval.contains_point(&Real::try_new(0.5).unwrap()));
//! ```
//!
//! ### Different Scalar Types for Different Needs
//!
//! ```rust
//! use grid1d::intervals::*;
//! use num_valid::{RealNative64StrictFiniteInDebug, RealNative64StrictFinite};
//! use try_create::TryNew;
//!
//! // Raw f64 for absolute maximum performance (use with caution)
//! let raw_interval = IntervalClosed::new(0.0_f64, 1.0_f64);
//!
//! // Performance-optimal with debug safety (recommended)
//! let optimal_interval = IntervalClosed::new(
//!     RealNative64StrictFiniteInDebug::try_new(0.0).unwrap(),
//!     RealNative64StrictFiniteInDebug::try_new(1.0).unwrap()
//! );
//!
//! // Always validated for safety-critical applications
//! let safe_interval = IntervalClosed::new(
//!     RealNative64StrictFinite::try_new(0.0).unwrap(),
//!     RealNative64StrictFinite::try_new(1.0).unwrap()
//! );
//!
//! // All have identical APIs and mathematical behavior
//! assert_eq!(raw_interval.length().as_ref(), &1.0);
//! assert_eq!(optimal_interval.length().as_ref(), &1.0);
//! assert_eq!(safe_interval.length().as_ref(), &1.0);
//! ```
//!
//! ### Generic Programming
//!
//! ```rust
//! use grid1d::intervals::*;
//! use num_valid::RealScalar;
//!
//! // This function works with ANY scalar type implementing RealScalar
//! fn analyze_interval<I, T>(interval: &I) -> String
//! where
//!     I: IntervalTrait<RealType = T>,
//!     T: RealScalar + std::fmt::Display,
//! {
//!     if interval.contains_point(&T::zero()) {
//!         format!("Contains zero")
//!     } else {
//!         format!("Does not contain zero")
//!     }
//! }
//!
//! // Works with any interval type and scalar type
//! let closed = IntervalClosed::new(-1.0, 1.0);
//! let open = IntervalOpen::new(0.0, 2.0);
//! let unbounded = IntervalLowerClosedUpperUnbounded::new(0.0);
//!
//! assert_eq!(analyze_interval(&closed), "Contains zero");
//! assert_eq!(analyze_interval(&open), "Does not contain zero");
//! assert_eq!(analyze_interval(&unbounded), "Contains zero");
//! ```
//!
//! ## Advanced Features
//!
//! ### Type Conversions
//! ```rust
//! use grid1d::intervals::*;
//!
//! let closed = IntervalClosed::new(0.0, 1.0);
//! let general: Interval<f64> = closed.into();
//! let back: IntervalClosed<f64> = general.try_into().unwrap();
//! ```
//!
//! ### Intersection and Set Operations
//! ```rust
//! use grid1d::intervals::*;
//!
//! let a = IntervalClosed::new(0.0, 2.0);    // [0, 2]
//! let b = IntervalClosed::new(1.0, 3.0);    // [1, 3]
//!
//! if let Some(intersection) = a.intersection(&b) {
//!     // Result: [1, 2]
//!     println!("Intersection: {:?}", intersection);
//! }
//! ```
//!
//! ## Performance Notes
//!
//! - All bound checks are compile-time in release builds
//! - Generic code specializes to zero-cost concrete operations
//! - Memory layout is optimal (single word for most interval types)
//! - No heap allocations in normal operation
//!
//! ## Error Handling
//!
//! - `IntervalError` for unified error handling
//! - Specific error types for different failure modes
//! - Backtrace support for debugging
//! - Both fallible (`try_new`) and panicking (`new`) constructors

use crate::{
    BuildIntervalInPartition,
    bounds::{
        BoundType, Closed, IntervalBoundRuntime, LowerBound, LowerBoundRuntime, Open, UpperBound,
        UpperBoundRuntime, ValueWithinBound, max_upper_bound, max_upper_bound_maybe_unbounded,
        min_lower_bound, min_lower_bound_maybe_unbounded,
    },
};
use duplicate::duplicate_item;
use num_valid::RealScalar;
use serde::{Deserialize, Serialize};
use std::{backtrace::Backtrace, fmt::Debug};
use thiserror::Error;

pub mod bounded;
pub mod conversions;
pub mod operations;
pub mod unbounded;

pub use bounded::{
    IntervalBounded, IntervalClosed, IntervalFiniteLength, IntervalFinitePositiveLength,
    IntervalFinitePositiveLengthTrait, IntervalLowerClosedUpperOpen, IntervalLowerOpenUpperClosed,
    IntervalOpen, IntervalSingleton, SubIntervalInPartition,
};
pub use operations::{IntervalDifference, IntervalOperations, IntervalUnion};
pub use unbounded::{
    IntervalInfiniteLength, IntervalLowerClosedUpperUnbounded, IntervalLowerOpenUpperUnbounded,
    IntervalLowerUnboundedUpperClosed, IntervalLowerUnboundedUpperOpen,
    IntervalLowerUnboundedUpperUnbounded,
};

//------------------------------------------------------------------------------------------------
/// Core trait for point containment testing in mathematical intervals.
///
/// The [`Contains`] trait provides the fundamental operation for testing whether a specific
/// point belongs to an interval. This trait forms the foundation for all point-based queries
/// and is complemented by the [`contains_interval`](Contains::contains_interval) method
/// for interval-to-interval containment testing.
///
/// ## Mathematical Foundation
///
/// Point containment is the most basic operation in interval mathematics. For any interval `I`
/// and point `x`, the containment test `x ∈ I` determines whether the point lies within the
/// interval according to its boundary conditions:
///
/// - **Closed intervals** `[a,b]`: `x ∈ [a,b] ⟺ a ≤ x ≤ b`
/// - **Open intervals** `(a,b)`: `x ∈ (a,b) ⟺ a < x < b`
/// - **Half-open intervals** `[a,b)`: `x ∈ [a,b) ⟺ a ≤ x < b`
/// - **Half-open intervals** `(a,b]`: `x ∈ (a,b] ⟺ a < x ≤ b`
/// - **Singleton intervals** `{a}`: `x ∈ {a} ⟺ x = a`
/// - **Unbounded intervals**: Various combinations with infinite extents
///
/// ## Design Philosophy
///
/// The [`Contains`] trait is intentionally minimal and focused:
///
/// - **Single Responsibility**: Only handles point containment, not interval containment
/// - **Generic Design**: Works with any scalar type implementing [`RealScalar`]
/// - **Performance First**: Designed for O(1) operations with minimal overhead
/// - **Type Safety**: Prevents containment queries with incompatible types
/// - **Mathematical Correctness**: Respects boundary semantics exactly
///
/// ## Relationship with Interval Containment
///
/// Point containment ([`contains_point`](Contains::contains_point)) and interval containment
/// ([`contains_interval`](Contains::contains_interval)) are related but distinct operations:
///
/// ```text
/// Point Containment:     x ∈ I     (scalar ∈ interval)
/// Interval Containment:  A ⊆ B     (interval ⊆ interval)
/// ```
///
/// ### Key Differences
///
/// | Aspect | Point Containment | Interval Containment |
/// |--------|-------------------|---------------------|
/// | **Input** | Single scalar value | Another interval |
/// | **Output** | `bool` | `bool` |
/// | **Complexity** | O(1) - at most 2 comparisons | O(1) - boundary logic |
/// | **Use Cases** | Point queries, evaluation domains | Set operations, subset testing |
/// | **Mathematical Basis** | Element membership `x ∈ I` | Subset relation `A ⊆ B` |
///
/// ### Mathematical Relationship
///
/// Interval containment can be expressed in terms of point containment:
/// ```text
/// A ⊆ B ⟺ ∀x ∈ A: x ∈ B
/// ```
/// However, for efficiency, interval containment is implemented by comparing boundaries
/// rather than testing all points.
///
/// ## Core Operation
///
/// ### [`contains_point`](Contains::contains_point)
/// Tests whether a specific point belongs to the interval.
///
/// **Signature**: `fn contains_point(&self, point: &RealType) -> bool`
///
/// **Behavior**:
/// - Returns `true` if the point is within the interval
/// - Returns `false` if the point is outside the interval
/// - Respects boundary inclusion/exclusion semantics
/// - Always O(1) complexity regardless of interval type
///
/// ## Usage Examples
///
/// ### Basic Point Containment
/// ```rust
/// use grid1d::intervals::*;
/// use num_valid::RealNative64StrictFiniteInDebug;
/// use try_create::{New, TryNew};
///
/// type Real = RealNative64StrictFiniteInDebug;
///
/// let closed = IntervalClosed::new(
///     Real::try_new(0.0).unwrap(),
///     Real::try_new(1.0).unwrap()
/// );
/// let open = IntervalOpen::new(
///     Real::try_new(0.0).unwrap(),
///     Real::try_new(1.0).unwrap()
/// );
/// let singleton = IntervalSingleton::new(Real::try_new(0.5).unwrap());
///
/// // Closed interval includes boundaries
/// assert!(closed.contains_point(&Real::try_new(0.0).unwrap()));   // Lower bound
/// assert!(closed.contains_point(&Real::try_new(0.5).unwrap()));   // Interior
/// assert!(closed.contains_point(&Real::try_new(1.0).unwrap()));   // Upper bound
///
/// // Open interval excludes boundaries
/// assert!(!open.contains_point(&Real::try_new(0.0).unwrap()));    // Lower bound excluded
/// assert!(open.contains_point(&Real::try_new(0.5).unwrap()));     // Interior
/// assert!(!open.contains_point(&Real::try_new(1.0).unwrap()));    // Upper bound excluded
///
/// // Singleton contains only exact match
/// assert!(singleton.contains_point(&Real::try_new(0.5).unwrap())); // Exact match
/// assert!(!singleton.contains_point(&Real::try_new(0.4).unwrap())); // Any other value
/// ```
///
/// ### Comparison with Interval Containment
/// ```rust
/// use grid1d::intervals::*;
/// use try_create::New;
///
/// let outer = IntervalClosed::new(0.0, 2.0);    // [0, 2]
/// let inner = IntervalClosed::new(0.5, 1.5);    // [0.5, 1.5]
/// let point_interval = IntervalSingleton::new(1.0); // {1}
///
/// // Point containment: test specific values
/// assert!(outer.contains_point(&1.0));          // Point 1.0 ∈ [0, 2] ✓
/// assert!(inner.contains_point(&1.0));          // Point 1.0 ∈ [0.5, 1.5] ✓
///
/// // Interval containment: test subset relationships
/// assert!(outer.contains_interval(&inner));      // [0, 2] ⊇ [0.5, 1.5] ✓
/// assert!(outer.contains_interval(&point_interval)); // [0, 2] ⊇ {1} ✓
/// assert!(inner.contains_interval(&point_interval)); // [0.5, 1.5] ⊇ {1} ✓
/// assert!(!inner.contains_interval(&outer));     // [0.5, 1.5] ⊉ [0, 2] ✗
/// ```
///
/// ### Generic Programming with Point Containment
/// ```rust
/// use grid1d::intervals::*;
/// use num_valid::RealScalar;
///
/// /// Count how many test points are contained in an interval
/// fn count_contained_points<I, T>(interval: &I, test_points: &[T]) -> usize
/// where
///     I: Contains<RealType = T>,
///     T: RealScalar,
/// {
///     test_points.iter()
///         .filter(|&point| interval.contains_point(point))
///         .count()
/// }
///
/// /// Filter points to only those contained in an interval
/// fn filter_points_in_interval<I, T>(interval: &I, points: Vec<T>) -> Vec<T>
/// where
///     I: Contains<RealType = T>,
///     T: RealScalar,
/// {
///     points.into_iter()
///         .filter(|point| interval.contains_point(point))
///         .collect()
/// }
///
/// let interval = IntervalClosed::new(-1.0, 1.0);
/// let test_points = vec![-2.0, -1.0, -0.5, 0.0, 0.5, 1.0, 2.0];
///
/// let count = count_contained_points(&interval, &test_points);
/// assert_eq!(count, 5); // -1.0, -0.5, 0.0, 0.5, 1.0
///
/// let filtered = filter_points_in_interval(&interval, test_points);
/// assert_eq!(filtered, vec![-1.0, -0.5, 0.0, 0.5, 1.0]);
/// ```
///
/// ## Performance Characteristics
///
/// ### Time Complexity
/// - **All interval types**: O(1) regardless of interval size or type
/// - **Bounded intervals**: At most 2 comparisons (lower ≤ x ≤ upper)
/// - **Semi-bounded intervals**: 1 comparison (x ≥ lower or x ≤ upper)
/// - **Unbounded intervals**: Constant time (always true)
/// - **Singletons**: 1 equality comparison (x == value)
///
/// ### Space Complexity
/// - **Memory usage**: O(1) - no additional allocation
/// - **Stack usage**: Minimal - simple comparison operations
/// - **Cache efficiency**: Excellent - operates on interval bounds directly
///
/// ### Scalar Type Performance Impact
/// ```rust
/// use grid1d::intervals::*;
/// use num_valid::{RealNative64StrictFiniteInDebug, RealNative64StrictFinite, RealScalar};
///
/// // All these have nearly identical performance for point containment:
/// type FastInterval = IntervalClosed<f64>;
/// type OptimalInterval = IntervalClosed<RealNative64StrictFiniteInDebug>;
/// type SafeInterval = IntervalClosed<RealNative64StrictFinite>;
///
/// fn benchmark_point_containment<T: RealScalar>(
///     interval: &IntervalClosed<T>,
///     test_point: &T
/// ) -> bool {
///     // This compiles to identical assembly for f64 and RealNative64StrictFiniteInDebug
///     // Slight overhead for RealNative64StrictFinite due to validation
///     interval.contains_point(test_point)
/// }
/// ```
///
/// ## Boundary Semantics and Edge Cases
///
/// ### Boundary Inclusion Rules
/// ```rust
/// use grid1d::intervals::*;
/// use num_valid::RealScalar;
///
/// // Demonstrate different boundary behaviors
/// let closed = IntervalClosed::new(0.0, 1.0);           // [0, 1]
/// let open = IntervalOpen::new(0.0, 1.0);               // (0, 1)
/// let left_open = IntervalLowerOpenUpperClosed::new(0.0, 1.0);  // (0, 1]
/// let right_open = IntervalLowerClosedUpperOpen::new(0.0, 1.0); // [0, 1)
///
/// let lower_bound = 0.0;
/// let upper_bound = 1.0;
/// let interior = 0.5;
///
/// // Lower boundary behavior
/// assert!(closed.contains_point(&lower_bound));      // [0,1] includes 0
/// assert!(!open.contains_point(&lower_bound));       // (0,1) excludes 0
/// assert!(!left_open.contains_point(&lower_bound));  // (0,1] excludes 0
/// assert!(right_open.contains_point(&lower_bound));  // [0,1) includes 0
///
/// // Upper boundary behavior
/// assert!(closed.contains_point(&upper_bound));      // [0,1] includes 1
/// assert!(!open.contains_point(&upper_bound));       // (0,1) excludes 1
/// assert!(left_open.contains_point(&upper_bound));   // (0,1] includes 1
/// assert!(!right_open.contains_point(&upper_bound)); // [0,1) excludes 1
///
/// // Interior points (always included for positive-length intervals)
/// assert!(closed.contains_point(&interior));
/// assert!(open.contains_point(&interior));
/// assert!(left_open.contains_point(&interior));
/// assert!(right_open.contains_point(&interior));
/// ```
///
/// ### Floating-Point Precision Considerations
/// ```rust
/// use grid1d::intervals::*;
///
/// let interval = IntervalClosed::new(0.0, 1.0);
///
/// // Exact boundary values
/// assert!(interval.contains_point(&0.0));
/// assert!(interval.contains_point(&1.0));
///
/// // Values very close to boundaries
/// assert!(interval.contains_point(&f64::EPSILON));           // Just above 0
/// assert!(interval.contains_point(&(1.0 - f64::EPSILON)));   // Just below 1
/// assert!(!interval.contains_point(&-f64::EPSILON));         // Just below 0
/// assert!(!interval.contains_point(&(1.0 + f64::EPSILON)));  // Just above 1
///
/// // For very small intervals, be aware of precision limits
/// let tiny = IntervalClosed::new(1.0, 1.0 + f64::EPSILON);
/// assert!(tiny.contains_point(&1.0));
/// assert!(tiny.contains_point(&(1.0 + f64::EPSILON)));
/// assert_eq!(tiny.length().into_inner(), f64::EPSILON);
/// ```
///
/// ## Implementation Requirements
///
/// Types implementing [`Contains`] must guarantee:
///
/// ### Mathematical Correctness
/// ```rust
/// use grid1d::intervals::*;
/// use num_valid::RealScalar;
///
/// // These properties must hold for all implementations:
/// fn verify_contains_properties<I, T>(interval: &I, point: &T)
/// where
///     I: Contains<RealType = T>,
///     T: RealScalar,
/// {
///     let result = interval.contains_point(point);
///     
///     // Consistency: repeated calls must give same result
///     assert_eq!(result, interval.contains_point(point));
///     
///     // The result should be deterministic for the same inputs
///     assert_eq!(result, interval.contains_point(&point.clone()));
/// }
/// ```
///
/// ### Performance Requirements
/// - **O(1) time complexity**: All implementations must be constant time
/// - **No allocation**: Point containment should not allocate memory
/// - **Minimal computation**: Use the most efficient boundary checks possible
///
/// ### Type Safety Requirements
/// - **Scalar type consistency**: Point type must match interval's scalar type
/// - **Reference semantics**: Accept points by reference to avoid unnecessary copies
/// - **Generic compatibility**: Work with any [`RealScalar`] implementation
///
/// ## Advanced Usage Patterns
///
/// ### Predicate Functions and Filtering
/// ```rust
/// use grid1d::intervals::*;
/// use num_valid::RealScalar;
///
/// /// Create a predicate function from an interval
/// fn interval_predicate<I, T>(interval: I) -> impl Fn(&T) -> bool
/// where
///     I: Contains<RealType = T>,
///     T: RealScalar,
/// {
///     move |point: &T| interval.contains_point(point)
/// }
///
/// /// Filter a dataset based on multiple interval constraints
/// fn filter_by_intervals<T>(
///     data: Vec<T>,
///     constraints: &[impl Contains<RealType = T>]
/// ) -> Vec<T>
/// where
///     T: RealScalar,
/// {
///     data.into_iter()
///         .filter(|point| {
///             constraints.iter().all(|interval| interval.contains_point(point))
///         })
///         .collect()
/// }
/// ```
///
/// ### Statistical Analysis
/// ```rust
/// use grid1d::intervals::*;
///
/// /// Compute statistics for points within an interval
/// fn interval_statistics<I>(
///     interval: &I,
///     data: &[f64]
/// ) -> Option<(f64, f64, usize)>
/// where
///     I: Contains<RealType = f64>,
/// {
///     let contained_points: Vec<f64> = data.iter()
///         .filter(|&x| interval.contains_point(x))
///         .cloned()
///         .collect();
///         
///     if contained_points.is_empty() {
///         return None;
///     }
///     
///     let count = contained_points.len();
///     let sum: f64 = contained_points.iter().sum();
///     let mean = sum / count as f64;
///     let variance = contained_points.iter()
///         .map(|x| (x - mean).powi(2))
///         .sum::<f64>() / count as f64;
///     
///     Some((mean, variance, count))
/// }
///
/// let interval = IntervalClosed::new(-1.0, 1.0);
/// let data = vec![-2.0, -0.5, 0.0, 0.5, 2.0];
///
/// if let Some((mean, variance, count)) = interval_statistics(&interval, &data) {
///     println!("Points in interval: count={}, mean={}, variance={}", count, mean, variance);
/// }
/// ```
///
/// ## Common Pitfalls and Best Practices
///
/// ### Avoiding Common Mistakes
/// ```rust
/// use grid1d::intervals::*;
///
/// // WRONG: Assuming all intervals include their "endpoints"
/// fn naive_endpoint_test<I>(interval: &I) -> bool
/// where
///     I: Contains<RealType = f64> + GetLowerBoundValue<LowerBoundValue = f64> + GetUpperBoundValue<UpperBoundValue = f64>,
/// {
///     let lower = *interval.lower_bound_value();
///     let upper = *interval.upper_bound_value();
///     
///     // This fails for open intervals!
///     interval.contains_point(&lower) && interval.contains_point(&upper)
/// }
///
/// // CORRECT: Test boundary inclusion explicitly
/// fn proper_boundary_test<I>(interval: &I) -> (bool, bool)
/// where
///     I: Contains<RealType = f64> + GetLowerBoundValue<LowerBoundValue = f64> + GetUpperBoundValue<UpperBoundValue = f64>,
/// {
///     let lower = *interval.lower_bound_value();
///     let upper = *interval.upper_bound_value();
///     
///     (interval.contains_point(&lower), interval.contains_point(&upper))
/// }
///
/// let closed = IntervalClosed::new(0.0, 1.0);
/// let open = IntervalOpen::new(0.0, 1.0);
///
/// assert!(naive_endpoint_test(&closed));     // Works for closed
/// assert!(!naive_endpoint_test(&open));      // Fails for open (correct behavior)
///
/// assert_eq!(proper_boundary_test(&closed), (true, true));   // Both included
/// assert_eq!(proper_boundary_test(&open), (false, false));   // Both excluded
/// ```
///
/// ### Performance Best Practices
/// ```rust
/// use grid1d::intervals::*;
///
/// // GOOD: Reuse intervals for multiple point tests
/// fn efficient_batch_testing(interval: &IntervalClosed<f64>, points: &[f64]) -> Vec<bool> {
///     points.iter()
///         .map(|point| interval.contains_point(point))
///         .collect()
/// }
///
/// // AVOID: Creating intervals repeatedly for the same bounds
/// fn inefficient_testing(lower: f64, upper: f64, points: &[f64]) -> Vec<bool> {
///     points.iter()
///         .map(|point| {
///             let interval = IntervalClosed::new(lower, upper); // Wasteful!
///             interval.contains_point(point)
///         })
///         .collect()
/// }
/// ```
///
/// ## Mathematical Properties and Guarantees
///
/// The [`Contains`] trait implementations maintain these essential properties:
///
/// ### Consistency Properties
/// - **Deterministic**: Same input always produces same output
/// - **Reflexive**: For any point in the interval, containment is stable
/// - **Boundary Correct**: Respects mathematical interval notation exactly
///
/// ### Performance Properties  
/// - **Constant Time**: O(1) for all interval types and point queries
/// - **Memory Efficient**: No allocation, minimal stack usage
/// - **Cache Friendly**: Operations on compact data structures
///
/// ### Type Safety Properties
/// - **Scalar Consistency**: Point and interval scalar types must match
/// - **Generic Correctness**: Works with any [`RealScalar`] implementation
/// - **Reference Safety**: Accepts points by reference safely
///
/// These guarantees are preserved across all interval types and scalar types,
/// ensuring that point containment testing is both mathematically correct and
/// computationally efficient throughout the library.
pub trait Contains: IntervalBoundsRuntime + Clone + PartialEq {
    /// Tests whether a point is contained within this interval.
    ///
    /// This is the fundamental operation that all interval types must implement.
    /// The behavior depends on the specific interval type and its boundary conditions.
    ///
    /// # Boundary Behavior
    ///
    /// - **Closed intervals** `[a, b]`: Include both endpoints
    /// - **Open intervals** `(a, b)`: Exclude both endpoints  
    /// - **Half-open intervals**: Include one endpoint, exclude the other
    /// - **Unbounded intervals**: Have infinite extent in one or both directions
    /// - **Singleton intervals**: Contain exactly one point
    ///
    /// # Examples
    ///
    /// ```rust
    /// use grid1d::intervals::*;
    /// use try_create::New;
    ///
    /// let closed = IntervalClosed::new(0.0, 1.0);
    /// assert!(closed.contains_point(&0.0));    // Endpoints included
    /// assert!(closed.contains_point(&1.0));    // Endpoints included
    /// assert!(closed.contains_point(&0.5));    // Interior point
    ///
    /// let open = IntervalOpen::new(0.0, 1.0);
    /// assert!(!open.contains_point(&0.0));     // Endpoints excluded
    /// assert!(!open.contains_point(&1.0));     // Endpoints excluded
    /// assert!(open.contains_point(&0.5));      // Interior point
    ///
    /// let half_open = IntervalLowerOpenUpperClosed::new(0.0, 1.0);
    /// assert!(!half_open.contains_point(&0.0)); // Lower bound excluded
    /// assert!(half_open.contains_point(&1.0));  // Upper bound included
    ///
    /// let singleton = IntervalSingleton::new(42.0);
    /// assert!(singleton.contains_point(&42.0));  // Exact match
    /// assert!(!singleton.contains_point(&41.0)); // No match
    /// ```
    ///
    /// # Performance
    ///
    /// This operation is O(1) for all interval types, involving at most two comparisons.
    fn contains_point(&self, x: &Self::RealType) -> bool;

    /// Tests whether this interval completely contains another interval.
    ///
    /// Returns `true` if and only if every point in `other` is also contained in `self`.
    /// This includes proper handling of boundary conditions - for example, the closed
    /// interval `[0., 1.]` contains the open interval `(0., 1.)`, but not vice versa.
    ///
    /// # Mathematical Definition
    ///
    /// For intervals A and B, A contains B (A ⊇ B) if and only if:
    /// - The lower bound of A is ≤ the lower bound of B (with proper boundary handling)
    /// - The upper bound of A is ≥ the upper bound of B (with proper boundary handling)
    ///
    /// # Boundary Rules
    ///
    /// - Closed bounds can contain open bounds at the same value
    /// - Open bounds cannot contain closed bounds at the same value
    /// - Unbounded sides always contain any bounded side
    ///
    /// # Examples
    ///
    /// ```rust
    /// use grid1d::intervals::*;
    ///
    /// let outer = IntervalClosed::new(0.0, 2.0);       // [0, 2]
    /// let inner = IntervalOpen::new(0.5, 1.5);         // (0.5, 1.5)
    /// let boundary = IntervalClosed::new(0.0, 1.0);    // [0, 1]
    /// let open_boundary = IntervalOpen::new(0.0, 1.0); // (0, 1)
    ///
    /// assert!(outer.contains_interval(&inner));      // [0, 2] ⊇ (0.5, 1.5) ✓
    /// assert!(outer.contains_interval(&boundary));   // [0, 2] ⊇ [0, 1] ✓
    /// assert!(boundary.contains_interval(&open_boundary)); // [0, 1] ⊇ (0, 1) ✓
    /// assert!(!open_boundary.contains_interval(&boundary)); // (0, 1) ⊉ [0, 1] ✗
    /// ```
    ///
    /// # Performance
    ///
    /// This operation is O(1) and involves only bound comparisons.
    fn contains_interval<IntervalType: IntervalTrait<RealType = Self::RealType>>(
        &self,
        other: &IntervalType,
    ) -> bool {
        let lower_bound_b_is_contained =
            match (self.lower_bound_runtime(), other.lower_bound_runtime()) {
                // If the lower bound of `self` is unbounded, it will contain any other lower bound
                (None, _) => true,

                // If the lower bound of `self` is not unbounded while the lower bound of `other` is unbounded, `self` cannot contain `other`
                (_, None) => false,

                (Some(lower_bound_a), Some(lower_bound_b)) => lower_bound_a <= lower_bound_b,
            };

        let upper_bound_b_is_contained =
            match (self.upper_bound_runtime(), other.upper_bound_runtime()) {
                // If the upper bound of `self` is unbounded, it will contain any other upper bound
                (None, _) => true,

                // If the upper bound of `self` is not unbounded while the upper bound of `other` is unbounded, `self` cannot contain `other`
                (_, None) => false,

                (Some(upper_bound_a), Some(upper_bound_b)) => upper_bound_b <= upper_bound_a,
            };

        lower_bound_b_is_contained && upper_bound_b_is_contained
    }
}
//------------------------------------------------------------------------------------------------

//------------------------------------------------------------------------------------------------
/// Fundamental trait for runtime access to interval boundary information with dynamic bound type handling.
///
/// [`IntervalBoundsRuntime`] provides a unified interface for accessing interval boundaries where
/// the open/closed nature of bounds is determined at runtime rather than compile time. This trait
/// serves as the foundation for all interval types in the grid1d library, enabling dynamic boundary
/// introspection, cross-type operations, and flexible interval manipulation.
///
/// ## Design Philosophy
///
/// ### Runtime vs Compile-Time Boundary Access
///
/// Unlike compile-time bound access where boundary types are encoded in the type system,
/// [`IntervalBoundsRuntime`] provides dynamic access to boundary information:
///
/// ```rust
/// use grid1d::{bounds::*, intervals::*};
/// use try_create::New;
///
/// // Different interval types with different boundary semantics
/// let closed = IntervalClosed::new(0.0, 1.0);           // [0, 1]
/// let open = IntervalOpen::new(0.0, 1.0);               // (0, 1)
/// let half_open = IntervalLowerClosedUpperOpen::new(0.0, 1.0); // [0, 1)
/// let unbounded = IntervalLowerClosedUpperUnbounded::new(0.0); // [0, ∞)
///
/// // All can be accessed uniformly through the trait
/// fn inspect_bounds<I: IntervalBoundsRuntime>(
///     interval: &I
/// ) -> (Option<LowerBoundRuntime<I::RealType>>, Option<UpperBoundRuntime<I::RealType>>) {
///     (interval.lower_bound_runtime(), interval.upper_bound_runtime())
/// }
///
/// let (lower, upper) = inspect_bounds(&closed);
/// // lower: Some(LowerBoundRuntime::Closed(...))
/// // upper: Some(UpperBoundRuntime::Closed(...))
///
/// let (lower, upper) = inspect_bounds(&unbounded);
/// // lower: Some(LowerBoundRuntime::Closed(...))
/// // upper: None (unbounded above)
/// ```
///
/// ### Unified Boundary Operations
///
/// The trait enables unified operations across different interval types:
///
/// | Operation | Purpose | Returns |
/// |-----------|---------|---------|
/// | [`lower_bound_runtime`](crate::intervals::IntervalBoundsRuntime::lower_bound_runtime) | Access lower boundary with type info | `Option<LowerBoundRuntime<T>>` |
/// | [`upper_bound_runtime`](crate::intervals::IntervalBoundsRuntime::upper_bound_runtime) | Access upper boundary with type info | `Option<UpperBoundRuntime<T>>` |
/// | [`max_lower_bound`](crate::intervals::IntervalBoundsRuntime::max_lower_bound) | Find most restrictive lower bound | `Option<LowerBoundRuntime<T>>` |
/// | [`min_upper_bound`](crate::intervals::IntervalBoundsRuntime::min_upper_bound) | Find most restrictive upper bound | `Option<UpperBoundRuntime<T>>` |
///
/// ## Associated Types
///
/// ### `RealType: RealScalar`
/// The scalar type used for boundary values and computations. Must implement [`num_valid::RealScalar`]
/// to ensure mathematical correctness and provide validation capabilities.
///
/// ## Core Operations
///
/// ### Boundary Access Methods
///
/// #### [`lower_bound_runtime`](crate::intervals::IntervalBoundsRuntime::lower_bound_runtime)
/// Returns the lower boundary information with runtime type encoding:
///
/// ```rust
/// use grid1d::{bounds::*, intervals::*};
/// use try_create::New;
///
/// let closed_interval = IntervalClosed::new(1.0, 5.0);
/// let unbounded_interval = IntervalLowerUnboundedUpperClosed::new(5.0);
///
/// // Bounded interval returns boundary info
/// if let Some(lower) = closed_interval.lower_bound_runtime() {
///     match lower {
///         LowerBoundRuntime::Closed(bound) => {
///             println!("Lower bound [{}:", bound.as_ref()); // "[1.0:"
///         }
///         LowerBoundRuntime::Open(bound) => {
///             println!("Lower bound ({}:", bound.as_ref());
///         }
///     }
/// }
///
/// // Unbounded interval returns None
/// assert_eq!(unbounded_interval.lower_bound_runtime(), None);
/// ```
///
/// **Returns:**
/// - `Some(LowerBoundRuntime::Closed(_))` for intervals with closed lower bounds
/// - `Some(LowerBoundRuntime::Open(_))` for intervals with open lower bounds  
/// - `None` for intervals unbounded below (extending to -∞)
///
/// #### [`upper_bound_runtime`](crate::intervals::IntervalBoundsRuntime::upper_bound_runtime)
/// Returns the upper boundary information with runtime type encoding:
///
/// ```rust
/// use grid1d::{bounds::*, intervals::*};
/// use try_create::New;
///
/// let closed_interval = IntervalClosed::new(1.0, 5.0);
/// let unbounded_interval = IntervalLowerClosedUpperUnbounded::new(1.0);
///
/// // Bounded interval returns boundary info
/// if let Some(upper) = closed_interval.upper_bound_runtime() {
///     match upper {
///         UpperBoundRuntime::Closed(bound) => {
///             println!(":{}]", bound.as_ref()); // ":5.0]"
///         }
///         UpperBoundRuntime::Open(bound) => {
///             println!(":{})", bound.as_ref());
///         }
///     }
/// }
///
/// // Unbounded interval returns None
/// assert_eq!(unbounded_interval.upper_bound_runtime(), None);
/// ```
///
/// **Returns:**
/// - `Some(UpperBoundRuntime::Closed(_))` for intervals with closed upper bounds
/// - `Some(UpperBoundRuntime::Open(_))` for intervals with open upper bounds
/// - `None` for intervals unbounded above (extending to +∞)
///
/// ### Boundary Comparison Operations
///
/// #### [`max_lower_bound`](crate::intervals::IntervalBoundsRuntime::max_lower_bound)
/// Finds the most restrictive (maximum) lower bound between two intervals:
///
/// ```rust
/// use grid1d::{bounds::*, intervals::*};
/// use try_create::New;
///
/// let interval_a = IntervalClosed::new(0.0, 10.0);      // [0, 10]
/// let interval_b = IntervalLowerClosedUpperOpen::new(2.0, 8.0); // [2, 8)
///
/// // The most restrictive lower bound is [2
/// let max_lower = interval_a.max_lower_bound(&interval_b);
/// if let Some(LowerBoundRuntime::Closed(bound)) = max_lower {
///     assert_eq!(*bound.as_ref(), 2.0);
/// }
///
/// // With unbounded intervals
/// let unbounded = IntervalLowerUnboundedUpperClosed::new(5.0); // (-∞, 5]
/// let max_with_unbounded = interval_a.max_lower_bound(&unbounded);
/// // Returns [0 since unbounded has no lower bound
/// if let Some(LowerBoundRuntime::Closed(bound)) = max_with_unbounded {
///     assert_eq!(*bound.as_ref(), 0.0);
/// }
/// ```
///
/// **Behavior:**
/// - Returns the higher (more restrictive) of the two lower bounds
/// - If one interval is unbounded below, returns the other's lower bound
/// - If both are unbounded below, returns `None`
/// - Properly handles open vs closed boundary precedence
///
/// #### [`min_upper_bound`](crate::intervals::IntervalBoundsRuntime::min_upper_bound)
/// Finds the most restrictive (minimum) upper bound between two intervals:
///
/// ```rust
/// use grid1d::{bounds::*, intervals::*};
/// use try_create::New;
///
/// let interval_a = IntervalClosed::new(0.0, 10.0);     // [0, 10]
/// let interval_b = IntervalOpen::new(2.0, 8.0);        // (2, 8)
///
/// // The most restrictive upper bound is 8)
/// let min_upper = interval_a.min_upper_bound(&interval_b);
/// if let Some(UpperBoundRuntime::Open(bound)) = min_upper {
///     assert_eq!(*bound.as_ref(), 8.0);
/// }
///
/// // With unbounded intervals
/// let unbounded = IntervalLowerClosedUpperUnbounded::new(0.0); // [0, +∞)
/// let min_with_unbounded = interval_a.min_upper_bound(&unbounded);
/// // Returns 10] since unbounded has no upper bound
/// if let Some(UpperBoundRuntime::Closed(bound)) = min_with_unbounded {
///     assert_eq!(*bound.as_ref(), 10.0);
/// }
/// ```
///
/// **Behavior:**
/// - Returns the lower (more restrictive) of the two upper bounds
/// - If one interval is unbounded above, returns the other's upper bound
/// - If both are unbounded above, returns `None`
/// - Properly handles open vs closed boundary precedence
///
/// ## Implementation Patterns
///
/// ### Finite Intervals
/// All finite intervals (closed, open, half-open) return `Some(_)` for both bounds:
///
/// ```rust,ignore
/// impl<RealType: RealScalar> IntervalBoundsRuntime for IntervalClosed<RealType> {
///     type RealType = RealType;
///
///     fn lower_bound_runtime(&self) -> Option<LowerBoundRuntime<RealType>> {
///         Some(IntervalBoundRuntime::Closed(self.lower_bound().clone()))
///     }
///
///     fn upper_bound_runtime(&self) -> Option<UpperBoundRuntime<RealType>> {
///         Some(IntervalBoundRuntime::Closed(self.upper_bound().clone()))
///     }
/// }
/// ```
///
/// ### Semi-Bounded Intervals
/// Semi-bounded intervals return `Some(_)` for the finite bound and `None` for the infinite bound:
///
/// ```rust,ignore
/// impl<RealType: RealScalar> IntervalBoundsRuntime for IntervalLowerClosedUpperUnbounded<RealType> {
///     type RealType = RealType;
///
///     fn lower_bound_runtime(&self) -> Option<LowerBoundRuntime<RealType>> {
///         Some(IntervalBoundRuntime::Closed(self.lower_bound().clone()))
///     }
///
///     fn upper_bound_runtime(&self) -> Option<UpperBoundRuntime<RealType>> {
///         None  // Unbounded above
///     }
/// }
/// ```
///
/// ### Unbounded Intervals
/// Fully unbounded intervals return `None` for both bounds:
///
/// ```rust,ignore
/// impl<RealType: RealScalar> IntervalBoundsRuntime for IntervalLowerUnboundedUpperUnbounded<RealType> {
///     type RealType = RealType;
///
///     fn lower_bound_runtime(&self) -> Option<LowerBoundRuntime<RealType>> {
///         None  // Unbounded below
///     }
///
///     fn upper_bound_runtime(&self) -> Option<UpperBoundRuntime<RealType>> {
///         None  // Unbounded above
///     }
/// }
/// ```
///
/// ### Singleton Intervals
/// Singleton intervals return the same value for both bounds with closed semantics:
///
/// ```rust,ignore
/// impl<RealType: RealScalar> IntervalBoundsRuntime for IntervalSingleton<RealType> {
///     type RealType = RealType;
///
///     fn lower_bound_runtime(&self) -> Option<LowerBoundRuntime<RealType>> {
///         Some(IntervalBoundRuntime::Closed(LowerBoundClosed::new(self.value().clone())))
///     }
///
///     fn upper_bound_runtime(&self) -> Option<UpperBoundRuntime<RealType>> {
///         Some(IntervalBoundRuntime::Closed(UpperBoundClosed::new(self.value().clone())))
///     }
/// }
/// ```
///
/// ## Advanced Usage Patterns
///
/// ### Generic Intersection Algorithm
///
/// ```rust
/// use grid1d::{bounds::*, intervals::*};
/// use num_valid::RealScalar;
///
/// /// Generic intersection algorithm using runtime bounds
/// fn intersection_bounds<I1, I2, T>(
///     a: &I1,
///     b: &I2,
/// ) -> Option<(LowerBoundRuntime<T>, UpperBoundRuntime<T>)>
/// where
///     I1: IntervalBoundsRuntime<RealType = T>,
///     I2: IntervalBoundsRuntime<RealType = T>,
///     T: RealScalar,
/// {
///     // Find most restrictive bounds
///     let lower = a.max_lower_bound(b)?;
///     let upper = a.min_upper_bound(b)?;
///     
///     // Check if intersection is valid (lower ≤ upper)
///     let lower_val = lower.as_ref();
///     let upper_val = upper.as_ref();
///     
///     if lower_val <= upper_val {
///         // Additional boundary compatibility check for exact equality
///         if lower_val == upper_val {
///             match (&lower, &upper) {
///                 (LowerBoundRuntime::Closed(_), UpperBoundRuntime::Closed(_)) => {
///                     Some((lower, upper)) // [a, a] is valid (singleton)
///                 }
///                 _ => None, // (a, a], [a, a), or (a, a) are empty
///             }
///         } else {
///             Some((lower, upper))
///         }
///     } else {
///         None // Disjoint intervals
///     }
/// }
///
/// let a = IntervalClosed::new(0.0, 2.0);    // [0, 2]
/// let b = IntervalOpen::new(1.0, 3.0);      // (1, 3)
///
/// if let Some((lower, upper)) = intersection_bounds(&a, &b) {
///     println!("Intersection exists:");
///     match lower {
///         LowerBoundRuntime::Open(bound) => println!("  Lower: ({}", bound.as_ref()),
///         LowerBoundRuntime::Closed(bound) => println!("  Lower: [{}",bound.as_ref()),
///     }
///     match upper {
///         UpperBoundRuntime::Open(bound) => println!("  Upper: {})", bound.as_ref()),
///         UpperBoundRuntime::Closed(bound) => println!("  Upper: {}]", bound.as_ref()),
///     }
/// }
/// // Output: Intersection is (1, 2]
/// ```
///
/// ### Interval Classification
///
/// ```rust
/// use grid1d::{bounds::*, intervals::*};
/// use num_valid::RealScalar;
///
/// #[derive(Debug, PartialEq)]
/// enum IntervalType {
///     Finite { closed_lower: bool, closed_upper: bool },
///     LowerBounded { closed: bool },
///     UpperBounded { closed: bool },
///     Unbounded,
///     Singleton,
/// }
///
/// /// Classify any interval based on its runtime bounds
/// fn classify_interval<I, T>(interval: &I) -> IntervalType
/// where
///     I: IntervalBoundsRuntime<RealType = T>,
///     T: RealScalar,
/// {
///     let lower = interval.lower_bound_runtime();
///     let upper = interval.upper_bound_runtime();
///     
///     match (lower, upper) {
///         (Some(lower_bound), Some(upper_bound)) => {
///             // Check if it's a singleton
///             let lower_val = lower_bound.as_ref();
///             let upper_val = upper_bound.as_ref();
///             
///             if lower_val == upper_val &&
///                matches!((&lower_bound, &upper_bound),
///                        (LowerBoundRuntime::Closed(_), UpperBoundRuntime::Closed(_))) {
///                 IntervalType::Singleton
///             } else {
///                 IntervalType::Finite {
///                     closed_lower: matches!(lower_bound, LowerBoundRuntime::Closed(_)),
///                     closed_upper: matches!(upper_bound, UpperBoundRuntime::Closed(_)),
///                 }
///             }
///         }
///         (Some(lower_bound), None) => {
///             IntervalType::LowerBounded {
///                 closed: matches!(lower_bound, LowerBoundRuntime::Closed(_)),
///             }
///         }
///         (None, Some(upper_bound)) => {
///             IntervalType::UpperBounded {
///                 closed: matches!(upper_bound, UpperBoundRuntime::Closed(_)),
///             }
///         }
///         (None, None) => IntervalType::Unbounded,
///     }
/// }
/// ```
///
/// ## Mathematical Properties
///
/// ### Boundary Ordering
/// The trait respects mathematical ordering of boundaries:
///
/// ```rust
/// use grid1d::{bounds::*, intervals::*};
///
/// // Lower bounds: more restrictive (higher) values dominate
/// let a = IntervalClosed::new(0.0, 5.0);    // [0, 5]
/// let b = IntervalClosed::new(2.0, 3.0);    // [2, 3]
///
/// let max_lower = a.max_lower_bound(&b);
/// // Returns [2 (more restrictive)
///
/// // Upper bounds: more restrictive (lower) values dominate  
/// let min_upper = a.min_upper_bound(&b);
/// // Returns 3] (more restrictive)
/// ```
///
/// ### Boundary Type Precedence
/// When bounds have the same value, the more restrictive type wins:
///
/// ```rust
/// use grid1d::{bounds::*, intervals::*};
///
/// let closed_interval = IntervalClosed::new(1.0, 2.0);        // [1, 2]
/// let half_open_interval = IntervalLowerOpenUpperClosed::new(1.0, 2.0); // (1, 2]
///
/// // For lower bounds with same value: closed ([) is more restrictive than open (()
/// let max_lower = closed_interval.max_lower_bound(&half_open_interval);
/// // Returns [1 (closed bound wins)
/// ```
///
/// ## Performance Characteristics
///
/// ### Memory Layout
/// - **Associated Type**: `RealType` adds no runtime overhead
/// - **Boundary Access**: Methods return owned [`IntervalBoundRuntime`] values
/// - **Comparison Operations**: Default implementations with optimal algorithms
///
/// ### Time Complexity
/// | Operation | Complexity | Notes |
/// |-----------|------------|-------|
/// | [`lower_bound_runtime`](crate::intervals::IntervalBoundsRuntime::lower_bound_runtime) | O(1) | Direct field access and enum construction |
/// | [`upper_bound_runtime`](crate::intervals::IntervalBoundsRuntime::upper_bound_runtime) | O(1) | Direct field access and enum construction |
/// | [`max_lower_bound`](crate::intervals::IntervalBoundsRuntime::max_lower_bound) | O(1) | Pattern matching and comparison |
/// | [`min_upper_bound`](crate::intervals::IntervalBoundsRuntime::min_upper_bound) | O(1) | Pattern matching and comparison |
///
/// ### Space Complexity
/// - **Return Values**: Owned [`IntervalBoundRuntime`] values (small enum + scalar)
/// - **No Allocation**: All operations work on stack-allocated data
/// - **Generic Efficiency**: Monomorphization ensures optimal code generation
///
/// ## Integration with Grid1D Architecture
///
/// ### Cross-Type Operations
/// The trait enables operations between different interval types:
///
/// ```rust
/// use grid1d::{bounds::*, intervals::*};
/// use num_valid::RealScalar;
///
/// fn can_intervals_intersect<I1, I2, T>(a: &I1, b: &I2) -> bool
/// where
///     I1: IntervalBoundsRuntime<RealType = T>,
///     I2: IntervalBoundsRuntime<RealType = T>,
///     T: RealScalar,
/// {
///     // Use runtime boundary information to check intersection possibility
///     let max_lower = a.max_lower_bound(b);
///     let min_upper = a.min_upper_bound(b);
///     
///     match (max_lower, min_upper) {
///         (Some(lower), Some(upper)) => {
///             // Extract values for comparison
///             let lower_val = lower.as_ref();
///             let upper_val = upper.as_ref();
///             
///             lower_val <= upper_val // Non-empty intersection possible
///         }
///         _ => true, // At least one interval is unbounded
///     }
/// }
///
/// // Works across all interval types
/// let closed = IntervalClosed::new(0.0, 2.0);
/// let open = IntervalOpen::new(1.0, 3.0);
/// let unbounded = IntervalLowerClosedUpperUnbounded::new(-1.0);
///
/// assert!(can_intervals_intersect(&closed, &open));       // [0,2] ∩ (1,3) ≠ ∅
/// assert!(can_intervals_intersect(&closed, &unbounded));  // [0,2] ∩ [-1,∞) ≠ ∅
/// ```
///
/// ## Best Practices
///
/// ### When to Use Runtime Boundary Access
/// - ✅ **Dynamic interval analysis**: When interval types vary at runtime
/// - ✅ **Generic algorithms**: Operations that work across different interval types  
/// - ✅ **Intersection computations**: Finding overlaps between arbitrary intervals
/// - ✅ **Serialization/deserialization**: Converting intervals to/from external formats
/// - ✅ **User interfaces**: Displaying interval information to users
///
/// ### Type Safety Guidelines
///
/// ```rust
/// use grid1d::{bounds::*, intervals::*};
/// use num_valid::RealScalar;
///
/// // RECOMMENDED: Use associated types for consistency
/// fn process_interval_bounds<I>(interval: &I) -> String
/// where
///     I: IntervalBoundsRuntime,
///     I::RealType: std::fmt::Display,
/// {
///     let lower = interval.lower_bound_runtime();
///     let upper = interval.upper_bound_runtime();
///     // Process using I::RealType consistently
///     format!("Interval bounds processed")
/// }
/// ```
///
/// ## Error Handling and Edge Cases
///
/// ### Boundary Validation
/// Runtime boundary access preserves the validation guarantees of the underlying scalar types:
///
/// ```rust
/// use grid1d::{bounds::*, intervals::*};
/// use num_valid::RealNative64StrictFiniteInDebug;
/// use try_create::TryNew;
///
/// type SafeReal = RealNative64StrictFiniteInDebug;
///
/// // Validation happens at interval construction, not boundary access
/// let safe_interval = IntervalClosed::new(
///     SafeReal::try_new(0.0).unwrap(),
///     SafeReal::try_new(1.0).unwrap()
/// );
///
/// // Boundary access is always safe after valid construction
/// let lower = safe_interval.lower_bound_runtime();
/// // lower is guaranteed to contain valid RealNative64StrictFiniteInDebug values
/// ```
///
/// ### Degenerate Cases
/// The trait handles degenerate interval cases correctly:
///
/// ```rust
/// use grid1d::{bounds::*, intervals::*};
/// use try_create::New;
///
/// // Singleton interval
/// let singleton = IntervalSingleton::new(5.0);
/// let lower = singleton.lower_bound_runtime().unwrap();
/// let upper = singleton.upper_bound_runtime().unwrap();
///
/// // Both bounds point to the same value with closed semantics
/// match (lower, upper) {
///     (LowerBoundRuntime::Closed(l), UpperBoundRuntime::Closed(u)) => {
///         assert_eq!(l.as_ref(), u.as_ref());
///         assert_eq!(*l.as_ref(), 5.0);
///     }
///     _ => panic!("Singleton should have closed bounds"),
/// }
/// ```
///
/// The [`IntervalBoundsRuntime`] trait provides the essential foundation for dynamic interval
/// boundary access and manipulation in the grid1d library. It enables flexible, type-safe
/// operations across all interval types while maintaining mathematical correctness and optimal
/// performance through Rust's zero-cost abstractions.
pub trait IntervalBoundsRuntime: Debug {
    /// The scalar type used for interval bounds.
    ///
    /// This associated type must implement [`RealScalar`], ensuring it supports
    /// all necessary mathematical operations and comparisons.
    type RealType: RealScalar;

    /// Returns the lower boundary value if the interval is bounded below.
    ///
    /// # Returns
    /// - `Some(&value)` if the interval has a finite lower bound
    /// - `None` if the interval extends to negative infinity
    ///
    /// # Examples
    /// ```rust
    /// use grid1d::{bounds::*, intervals::*};
    /// use try_create::New;
    ///
    /// let bounded = IntervalClosed::new(1.0, 5.0);
    /// let unbounded = IntervalLowerUnboundedUpperClosed::new(5.0);
    ///
    /// assert_eq!(bounded.lower_bound_runtime(), Some(LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0))));
    /// assert_eq!(unbounded.lower_bound_runtime(), None);
    /// ```
    fn lower_bound_runtime(&self) -> Option<LowerBoundRuntime<Self::RealType>>;

    /// Returns the upper boundary value if the interval is bounded above.
    ///
    /// # Returns
    /// - `Some(&value)` if the interval has a finite upper bound
    /// - `None` if the interval extends to positive infinity
    ///
    /// # Examples
    /// ```rust
    /// use grid1d::{bounds::*, intervals::*};
    /// use try_create::New;
    ///
    /// let bounded = IntervalClosed::new(1.0, 5.0);
    /// let unbounded = IntervalLowerClosedUpperUnbounded::new(1.0);
    ///
    /// assert_eq!(bounded.upper_bound_runtime(), Some(UpperBoundRuntime::Closed(UpperBoundClosed::new(5.0))));
    /// assert_eq!(unbounded.upper_bound_runtime(), None);
    /// ```
    fn upper_bound_runtime(&self) -> Option<UpperBoundRuntime<Self::RealType>>;

    /// Finds the most restrictive (maximum) lower bound between two intervals.
    ///
    /// This method compares the lower bounds of two intervals and returns the one that
    /// is more restrictive (higher value). This is essential for intersection operations
    /// where the resulting interval's lower bound must satisfy both input intervals.
    ///
    /// # Mathematical Behavior
    /// - Returns the higher (more restrictive) of the two lower bounds
    /// - If one interval is unbounded below, returns the other's lower bound
    /// - If both are unbounded below, returns `None`
    /// - Properly handles open vs closed boundary precedence (closed is more restrictive)
    ///
    /// # Type Parameters
    /// - `Other`: Another interval type implementing `IntervalBoundsRuntime` with the same scalar type
    ///
    /// # Returns
    /// - `Some(LowerBoundRuntime)`: The most restrictive lower bound
    /// - `None`: If both intervals are unbounded below
    ///
    /// # Examples
    /// ```rust
    /// use grid1d::{bounds::*, intervals::*};
    /// use try_create::New;
    ///
    /// let interval_a = IntervalClosed::new(0.0, 10.0);      // [0, 10]
    /// let interval_b = IntervalLowerClosedUpperOpen::new(2.0, 8.0); // [2, 8)
    ///
    /// // The most restrictive lower bound is [2
    /// let max_lower = interval_a.max_lower_bound(&interval_b);
    /// if let Some(LowerBoundRuntime::Closed(bound)) = max_lower {
    ///     assert_eq!(*bound.as_ref(), 2.0);
    /// }
    ///
    /// // With unbounded intervals
    /// let unbounded = IntervalLowerUnboundedUpperClosed::new(5.0); // (-∞, 5]
    /// let max_with_unbounded = interval_a.max_lower_bound(&unbounded);
    /// // Returns [0 since unbounded has no lower bound
    /// if let Some(LowerBoundRuntime::Closed(bound)) = max_with_unbounded {
    ///     assert_eq!(*bound.as_ref(), 0.0);
    /// }
    /// ```
    fn max_lower_bound<Other>(&self, other: &Other) -> Option<LowerBoundRuntime<Self::RealType>>
    where
        Other: IntervalBoundsRuntime<RealType = Self::RealType>,
    {
        let lb_self = self.lower_bound_runtime();
        let lb_other = other.lower_bound_runtime();
        match (lb_self, lb_other) {
            (None, lb_other) => lb_other,
            (lb_self, None) => lb_self,
            (Some(lb_self), Some(lb_other)) => {
                Some(crate::bounds::max_lower_bound(lb_self, lb_other))
            }
        }
    }

    /// Finds the most restrictive (minimum) upper bound between two intervals.
    ///
    /// This method compares the upper bounds of two intervals and returns the one that
    /// is more restrictive (lower value). This is essential for intersection operations
    /// where the resulting interval's upper bound must satisfy both input intervals.
    ///
    /// # Mathematical Behavior
    /// - Returns the lower (more restrictive) of the two upper bounds
    /// - If one interval is unbounded above, returns the other's upper bound
    /// - If both are unbounded above, returns `None`
    /// - Properly handles open vs closed boundary precedence (open is more restrictive)
    ///
    /// # Type Parameters
    /// - `Other`: Another interval type implementing `IntervalBoundsRuntime` with the same scalar type
    ///
    /// # Returns
    /// - `Some(UpperBoundRuntime)`: The most restrictive upper bound
    /// - `None`: If both intervals are unbounded above
    ///
    /// # Examples
    /// ```rust
    /// use grid1d::{bounds::*, intervals::*};
    /// use try_create::New;
    ///
    /// let interval_a = IntervalClosed::new(0.0, 10.0);     // [0, 10]
    /// let interval_b = IntervalOpen::new(2.0, 8.0);        // (2, 8)
    ///
    /// // The most restrictive upper bound is 8)
    /// let min_upper = interval_a.min_upper_bound(&interval_b);
    /// if let Some(UpperBoundRuntime::Open(bound)) = min_upper {
    ///     assert_eq!(*bound.as_ref(), 8.0);
    /// }
    ///
    /// // With unbounded intervals
    /// let unbounded = IntervalLowerClosedUpperUnbounded::new(0.0); // [0, +∞)
    /// let min_with_unbounded = interval_a.min_upper_bound(&unbounded);
    /// // Returns 10] since unbounded has no upper bound
    /// if let Some(UpperBoundRuntime::Closed(bound)) = min_with_unbounded {
    ///     assert_eq!(*bound.as_ref(), 10.0);
    /// }
    /// ```
    fn min_upper_bound<Other>(&self, other: &Other) -> Option<UpperBoundRuntime<Self::RealType>>
    where
        Other: IntervalBoundsRuntime<RealType = Self::RealType>,
    {
        let ub_self = self.upper_bound_runtime();
        let ub_other = other.upper_bound_runtime();
        match (ub_self, ub_other) {
            (None, ub_other) => ub_other,
            (ub_self, None) => ub_self,
            (Some(ub_self), Some(ub_other)) => {
                Some(crate::bounds::min_upper_bound(ub_self, ub_other))
            }
        }
    }
}
//------------------------------------------------------------------------------------------------

//------------------------------------------------------------------------------------------------
/// Universal interface for all interval types providing common operations.
///
/// This trait provides a unified API for working with intervals regardless of their
/// specific type (closed, open, bounded, unbounded, etc.) and scalar type. It's the
/// foundation for generic interval programming in this library.
///
/// ## Core Operations
///
/// All intervals support three fundamental operations:
///
/// 1. **Point Containment**: [`contains_point`](Contains::contains_point)
///    - Tests if a single value lies within the interval
///    - Respects boundary conditions (open vs closed)
///    - O(1) operation for all interval types
///
/// 2. **Interval Containment**: [`contains_interval`](Contains::contains_interval)
///    - Tests if one interval is completely contained within another
///    - Handles complex boundary interactions correctly
///    - Supports cross-type containment testing
///
/// 3. **Intersection**: [`intersection`](IntervalOperations::intersection)
///    - Computes the overlap between two intervals
///    - Returns `None` for disjoint intervals
///    - Produces the most specific interval type possible
///
/// ## Mathematical Properties
///
/// The trait implementations guarantee these mathematical properties:
///
/// - **Reflexivity**: Every interval contains itself
/// - **Transitivity**: If A ⊆ B and B ⊆ C, then A ⊆ C
/// - **Commutativity**: A ∩ B = B ∩ A
/// - **Associativity**: (A ∩ B) ∩ C = A ∩ (B ∩ C)
/// - **Monotonicity**: If A ⊆ B, then A ∩ C ⊆ B ∩ C
///
/// ## Performance Characteristics by Scalar Type
///
/// | Operation | [`f64`] | [`RealNative64StrictFiniteInDebug`](num_valid::RealNative64StrictFiniteInDebug) | [`RealNative64StrictFinite`](num_valid::RealNative64StrictFinite) |
/// |-----------|---------|-----------------------------------|----------------------------|
/// | **Point queries** | O(1) raw | **O(1) identical** | O(1) + validation |
/// | **Containment** | O(1) comparisons | **O(1) identical** | O(1) + validation |
/// | **Intersections** | O(1) arithmetic | **O(1) identical** | O(1) + validation |
/// | **Memory usage** | 8-16 bytes | **8-16 bytes** | 8-16 bytes + metadata |
///
/// ## Generic Programming Examples
///
/// ### Interval Analysis Function
/// ```rust
/// use grid1d::intervals::*;
/// use num_valid::RealScalar;
///
/// fn analyze_interval<I, T>(interval: &I) -> String
/// where
///     I: IntervalTrait<RealType = T>,
///     T: RealScalar + std::fmt::Display,
/// {
///     if interval.contains_point(&T::zero()) {
///         "Contains zero".to_string()
///     } else {
///         "Does not contain zero".to_string()
///     }
/// }
///
/// // Works with any interval type
/// let closed = IntervalClosed::new(-1.0, 1.0);
/// let open = IntervalOpen::new(0.0, 2.0);
/// let unbounded = IntervalLowerClosedUpperUnbounded::new(0.0);
///
/// assert_eq!(analyze_interval(&closed), "Contains zero");
/// assert_eq!(analyze_interval(&open), "Does not contain zero");
/// assert_eq!(analyze_interval(&unbounded), "Contains zero");
/// ```
///
/// ### Generic Intersection Function
/// ```rust
/// use grid1d::intervals::*;
///
/// fn safe_intersection<I1, I2>(a: &I1, b: &I2) -> Option<Interval<f64>>
/// where
///     I1: IntervalTrait<RealType = f64>,
///     I2: IntervalTrait<RealType = f64>,
/// {
///     a.intersection(b)
/// }
///
/// let closed = IntervalClosed::new(0.0, 2.0);
/// let open = IntervalOpen::new(1.0, 3.0);
///
/// if let Some(result) = safe_intersection(&closed, &open) {
///     println!("Intersection found: {:?}", result);
/// }
/// ```
///
/// ## Conversion to General Interval Type
///
/// All concrete types implementing `IntervalTrait` can be converted to the general
/// [`Interval`] enum via the [`Into`]/[`From`] traits:
///
/// ```rust
/// use grid1d::intervals::*;
///
/// // All concrete interval types convert to Interval<T>
/// let closed = IntervalClosed::new(0.0, 1.0);
/// let general: Interval<f64> = closed.into();
///
/// let open = IntervalOpen::new(0.0, 1.0);
/// let general2 = Interval::from(open);
///
/// let half_open = IntervalLowerClosedUpperOpen::new(0.0, 1.0);
/// let general3: Interval<f64> = half_open.into();
/// ```
///
/// This conversion is available for all interval types in the library:
/// - All bounded intervals: [`IntervalClosed`], [`IntervalOpen`], [`IntervalLowerClosedUpperOpen`], [`IntervalLowerOpenUpperClosed`]
/// - All unbounded intervals: [`IntervalLowerClosedUpperUnbounded`], [`IntervalLowerOpenUpperUnbounded`],
///   [`IntervalLowerUnboundedUpperClosed`], [`IntervalLowerUnboundedUpperOpen`], [`IntervalLowerUnboundedUpperUnbounded`]
/// - Singleton intervals: [`IntervalSingleton`]
/// - Enum variants: [`IntervalFinitePositiveLength`], [`IntervalFiniteLength`], [`IntervalInfiniteLength`]
pub trait IntervalTrait: Serialize + for<'a> Deserialize<'a> + IntervalOperations {
    /// Convert this interval into the general [`Interval`] enum by consuming it.
    ///
    /// This method provides an explicit, ergonomic way to convert any interval type
    /// to the most general `Interval<RealType>` enum, which can represent all possible
    /// interval types in a unified way. The conversion consumes `self` and is zero-cost.
    ///
    /// # Naming Convention
    ///
    /// This method follows Rust naming conventions:
    /// - `into_*`: Consumes `self`, zero-cost conversion (this method)
    /// - `to_*`: May clone or allocate, potentially expensive
    /// - `as_*`: Borrows `self`, returns a reference
    ///
    /// # Examples
    ///
    /// ```rust
    /// use grid1d::intervals::*;
    ///
    /// let closed = IntervalClosed::new(0.0, 1.0);
    /// let general = closed.into_interval();
    /// assert!(matches!(general, Interval::FiniteLength(_)));
    ///
    /// let unbounded = IntervalLowerClosedUpperUnbounded::new(0.0);
    /// let general2 = unbounded.into_interval();
    /// assert!(matches!(general2, Interval::InfiniteLength(_)));
    /// ```
    ///
    /// # Type Flexibility
    ///
    /// This method is particularly useful when working with heterogeneous collections
    /// of intervals or when you need to return different interval types from a function:
    ///
    /// ```rust
    /// use grid1d::intervals::*;
    ///
    /// fn make_interval(bounded: bool) -> Interval<f64> {
    ///     if bounded {
    ///         IntervalClosed::new(0.0, 1.0).into_interval()
    ///     } else {
    ///         IntervalLowerClosedUpperUnbounded::new(0.0).into_interval()
    ///     }
    /// }
    /// ```
    ///
    /// # Working with Generic IntervalBounded
    ///
    /// The conversion also works with the generic `IntervalBounded<T, L, U>` type:
    ///
    /// ```rust
    /// use grid1d::{
    ///     bounds::{Closed, Open},
    ///     intervals::{*, bounded::*},
    /// };
    ///
    /// // Generic IntervalBounded can be converted to Interval
    /// let bounded: IntervalBounded<f64, Closed, Open> = IntervalBounded::new(0.0, 1.0);
    /// let general: Interval<f64> = bounded.into_interval();
    ///
    /// // This works because IntervalClosed, IntervalOpen, etc. are just type aliases
    /// // for IntervalBounded with specific bound type parameters
    /// ```
    fn into_interval(self) -> Interval<Self::RealType>
    where
        Self: Sized + Into<Interval<Self::RealType>>,
    {
        self.into()
    }
}

//------------------------------------------------------------------------------------------------

//------------------------------------------------------------------------------------------------
/// Trait for computing the **convex hull** (interval hull) between two intervals.
///
/// The convex hull is the smallest interval that contains both input intervals,
/// preserving the most **inclusive boundary semantics**.
///
/// ## Mathematical Definition
///
/// For two intervals `A` and `B`, their convex hull `hull(A, B)` is defined as:
///
/// ```text
/// hull(A, B) = [min(inf A, inf B), max(sup A, sup B)]
/// ```
///
/// where the boundary types follow the **most inclusive principle**:
/// - **Lower bound**: Take the minimum value with the **most inclusive** boundary type
/// - **Upper bound**: Take the maximum value with the **most inclusive** boundary type
/// - **Inclusion hierarchy**: `Closed ⊃ Open` (closed is more inclusive than open)
///
/// ### Boundary Inclusion Rules
///
/// When computing the hull, boundary types are resolved according to inclusion precedence:
///
/// | Input Bounds | Result Bound | Rationale |
/// |--------------|--------------|-----------|
/// | `Closed` + `Closed` | `Closed` | Both include boundary → include in hull |
/// | `Closed` + `Open` | `Closed` | Closed is more inclusive → include in hull |
/// | `Open` + `Open` | `Open` | Both exclude boundary → exclude in hull |
///
/// **Example**: For intervals `[0, 1]` and `(0, 2)`:
/// - Lower bound: Both have value 0, but `[` (closed) is more inclusive than `(` (open) → result uses `[`
/// - Upper bound: Values 1 vs 2, take max(1,2)=2 with bound type from `(0, 2)` → result uses `)`
/// - Final hull: `[0, 2)`
///
/// This ensures that the hull contains **exactly** the union of all points contained
/// in either input interval, with no points added or removed incorrectly.
///
/// ## Core Operation
///
/// ### [`hull`](IntervalHull::hull) Method
///
/// Computes the convex hull between `self` and another interval of potentially different type.
///
/// **Signature**: `fn hull(&self, other: &Other) -> Self::Output`
///
/// **Parameters**:
/// - `other`: Another finite positive-length interval (may be different type)
///
/// **Returns**: An interval containing both input intervals with correct boundary semantics
///
/// ## Generic Design and Type Safety
///
/// The trait is designed for maximum flexibility while maintaining type safety:
/// - **Generic over interval types**: Works between any interval implementing the trait [`IntervalTrait`]
/// - **Scalar type consistency**: Both intervals must use the same `RealType`
/// - **Type-safe output**: Result type depends on input boundary combinations
/// - **Compile-time verification**: Invalid combinations are caught at compile time
///
/// ## Mathematical Properties
///
/// All implementations guarantee these fundamental mathematical properties:
///
/// ### 1. **Containment Property**
/// ```text
/// A ⊆ hull(A, B) and B ⊆ hull(A, B)
/// ```
/// The hull always contains both input intervals completely.
///
/// ### 2. **Minimality Property**
/// ```text
/// If A ⊆ C and B ⊆ C, then hull(A, B) ⊆ C
/// ```
/// The hull is the smallest interval containing both inputs.
///
/// ### 3. **Commutativity Property**
/// ```text
/// hull(A, B) = hull(B, A)
/// ```
/// Order of operands doesn't affect the result.
///
/// ### 4. **Idempotence Property**
/// ```text
/// hull(A, A) = A (for compatible types)
/// ```
/// Hull of an interval with itself is the interval.
///
/// ### 5. **Associativity Property**
/// ```text
/// hull(hull(A, B), C) = hull(A, hull(B, C))
/// ```
/// Grouping doesn't affect the final result.
///
/// ## Comprehensive Usage Examples
///
/// ### Basic Hull Operations
/// ```rust
/// use grid1d::intervals::*;
/// use num_valid::RealNative64StrictFiniteInDebug;
/// use try_create::TryNew;
///
/// type Real = RealNative64StrictFiniteInDebug;
///
/// // Same boundary types → preserve type
/// let closed1 = IntervalClosed::new(
///     Real::try_new(1.0).unwrap(),
///     Real::try_new(3.0).unwrap()
/// );
/// let closed2 = IntervalClosed::new(
///     Real::try_new(2.0).unwrap(),
///     Real::try_new(5.0).unwrap()
/// );
///
/// let hull = closed1.hull(&closed2);
/// // Result: [1.0, 5.0] (closed)
/// assert_eq!(hull.lower_bound_value(), &1.0);
/// assert_eq!(hull.upper_bound_value(), &5.0);
/// ```
///
/// ### Cross-Type Hull Operations
/// ```rust
/// use grid1d::intervals::*;
/// use try_create::TryNew;
///
/// let closed = IntervalClosed::new(1.0, 3.0);      // [1, 3]
/// let open = IntervalOpen::new(2.0, 5.0);          // (2, 5)
///
/// let hull = closed.hull(&open);
/// // Result: [1.0, 5.0) - closed lower wins, open upper wins  
/// match hull {
///     IntervalFinitePositiveLength::LowerClosedUpperOpen(result) => {
///         assert_eq!(*result.lower_bound_value(), 1.0);
///         assert_eq!(*result.upper_bound_value(), 5.0);
///     }
///     _ => unreachable!(),
/// }
/// ```
///
/// ### Complex Boundary Combinations
/// ```rust
/// use grid1d::intervals::*;
///
/// let left_closed = IntervalLowerClosedUpperOpen::new(1.0, 3.0);   // [1, 3)
/// let right_closed = IntervalLowerOpenUpperClosed::new(2.0, 5.0);  // (2, 5]
///
/// let hull = left_closed.hull(&right_closed);
/// // Result: [1.0, 5.0] - closed lower wins, closed upper wins
/// match hull {
///     IntervalFinitePositiveLength::Closed(result) => {
///         assert_eq!(*result.lower_bound_value(), 1.0);
///         assert_eq!(*result.upper_bound_value(), 5.0);
///     }
///     _ => unreachable!(),
/// }
/// ```
///
/// ### Disjoint Intervals
/// ```rust
/// use grid1d::intervals::*;
///
/// let left = IntervalClosed::new(1.0, 2.0);    // [1, 2]
/// let right = IntervalClosed::new(4.0, 5.0);   // [4, 5]
///
/// let hull = left.hull(&right);
/// // Result: [1.0, 5.0] - spans both intervals and the gap
/// assert_eq!(*hull.lower_bound_value(), 1.0);
/// assert_eq!(*hull.upper_bound_value(), 5.0);
/// assert_eq!(*hull.length().as_ref(), 4.0);  // Includes the gap
/// ```
///
/// ### Containment Relationships
/// ```rust
/// use grid1d::intervals::*;
///
/// let outer = IntervalClosed::new(0.0, 10.0);  // [0, 10]
/// let inner = IntervalClosed::new(3.0, 7.0);   // [3, 7]
///
/// let hull = outer.hull(&inner);
/// // Result: [0.0, 10.0] - same as outer interval
/// assert_eq!(*hull.lower_bound_value(), 0.0);
/// assert_eq!(*hull.upper_bound_value(), 10.0);
/// ```
///
/// ## Advanced Usage Patterns
///
/// ### Multi-Interval Hull
/// ```rust
/// use grid1d::intervals::*;
///
/// /// Compute hull of multiple intervals using fold
/// fn multi_hull(intervals: Vec<IntervalClosed<f64>>) -> Option<IntervalFinitePositiveLength<f64>> {
///     let mut iter = intervals.into_iter();
///     let first = iter.next()?;
///     Some(iter.fold(first.into(), |acc: IntervalFinitePositiveLength<f64>, interval| {
///         acc.hull(&interval)
///     }))
/// }
///
/// let intervals = vec![
///     IntervalClosed::new(1.0, 2.0),
///     IntervalClosed::new(3.0, 4.0),
///     IntervalClosed::new(5.0, 6.0),
/// ];
///
/// if let Some(hull) = multi_hull(intervals) {
///     // Result: [1.0, 6.0]
///     assert_eq!(*hull.lower_bound_value(), 1.0);
///     assert_eq!(*hull.upper_bound_value(), 6.0);
/// }
/// ```
///
/// ### Boundary-Aware Hull Analysis
/// ```rust
/// use grid1d::intervals::*;
///
/// /// Analyze how boundary types affect hull computation
/// let closed = IntervalClosed::new(0.0, 1.0);           // [0, 1]
/// let open = IntervalOpen::new(0.0, 1.0);               // (0, 1)
/// let left_open = IntervalLowerOpenUpperClosed::new(0.0, 1.0);  // (0, 1]
/// let right_open = IntervalLowerClosedUpperOpen::new(0.0, 1.0); // [0, 1)
///
/// // Same bounds, different boundary types
/// let hull_closed_open = closed.hull(&open);
/// let hull_left_right = left_open.hull(&right_open);
///
/// // Both should result in closed intervals due to boundary inclusion rules
/// // (closed boundaries win over open boundaries at same values)
/// match (hull_closed_open, hull_left_right) {
///     (
///         IntervalFinitePositiveLength::Closed(h1),
///         IntervalFinitePositiveLength::Closed(h2)
///     ) => {
///         assert_eq!(h1.lower_bound_value(), h2.lower_bound_value());
///         assert_eq!(h1.upper_bound_value(), h2.upper_bound_value());
///     }
///     _ => unreachable!("Expected closed intervals"),
/// }
/// ```
///
/// ## Implementation Strategy
///
/// The trait provides implementations for all combinations of finite positive-length intervals:
///
/// ### Homogeneous Implementations
/// - [`IntervalClosed`] with [`IntervalClosed`] → [`IntervalClosed`]
/// - [`IntervalOpen`] with [`IntervalOpen`] → [`IntervalOpen`]
/// - [`IntervalLowerClosedUpperOpen`] with [`IntervalLowerClosedUpperOpen`] → [`IntervalLowerClosedUpperOpen`]
/// - [`IntervalLowerOpenUpperClosed`] with [`IntervalLowerOpenUpperClosed`] → [`IntervalLowerOpenUpperClosed`]
///
/// ### Heterogeneous Implementations
/// All combinations between different interval types, following boundary inclusion rules:
/// - [`IntervalClosed`] with [`IntervalOpen`] → Result depends on boundary positions (e.g., [`IntervalLowerClosedUpperOpen`])
/// - [`IntervalClosed`] with [`IntervalLowerClosedUpperOpen`] → Result depends on boundary positions  
/// - [`IntervalClosed`] with [`IntervalLowerOpenUpperClosed`] → Result depends on boundary positions
/// - And all symmetric variants...
///
/// ### Enum Implementation
/// - [`IntervalFinitePositiveLength`] with [`IntervalFinitePositiveLength`] → [`IntervalFinitePositiveLength`]
///
/// ## Performance Characteristics
///
/// ### Time Complexity
/// - **All operations**: O(1) - constant time regardless of interval types
/// - **Boundary analysis**: O(1) - simple comparisons and type matching
/// - **Result construction**: O(1) - direct value assignment
///
/// ### Space Complexity
/// - **Memory usage**: O(1) - no additional allocation beyond result interval
/// - **Stack usage**: Minimal - simple function calls with value parameters
/// - **Cache efficiency**: Excellent - operates on compact interval data
///
/// ### Scalar Type Performance Impact
/// ```rust
/// use grid1d::intervals::*;
/// use num_valid::{RealNative64StrictFiniteInDebug, RealNative64StrictFinite};
///
/// // Performance comparison across scalar types
/// type FastInterval = IntervalClosed<f64>;
/// type OptimalInterval = IntervalClosed<RealNative64StrictFiniteInDebug>;
/// type SafeInterval = IntervalClosed<RealNative64StrictFinite>;
///
/// // All have nearly identical hull performance:
/// // - f64: Maximum speed, no validation overhead
/// // - RealNative64StrictFiniteInDebug: Same speed as f64 in release builds
/// // - RealNative64StrictFinite: Small overhead for validation
/// ```
///
/// ## Error Handling and Edge Cases
///
/// ### Compile-Time Safety
/// ```rust
/// use grid1d::intervals::*;
///
/// // This won't compile - scalar type mismatch:
/// // let interval_f64 = IntervalClosed::new(1.0_f64, 2.0_f64);
/// // let interval_f32 = IntervalClosed::new(1.0_f32, 2.0_f32);
/// // let hull = interval_f64.hull(&interval_f32); // ERROR!
/// ```
///
/// ### Precision Considerations
/// ```rust
/// use grid1d::intervals::*;
///
/// // Very small intervals
/// let tiny1 = IntervalClosed::new(0.0, f64::EPSILON);
/// let tiny2 = IntervalClosed::new(f64::EPSILON, 2.0 * f64::EPSILON);
///
/// let hull = tiny1.hull(&tiny2);
/// // Result: [0.0, 2ε] - mathematically correct even for tiny intervals
/// ```
///
/// ## Integration with Other Traits
///
/// ### Containment Verification
/// ```rust
/// use grid1d::intervals::*;
///
/// /// Verify that hull satisfies containment property
/// fn verify_hull_containment<I1, I2>(a: &I1, b: &I2) -> bool
/// where
///     I1: IntervalHull<I2> + Clone,
///     I2: IntervalFinitePositiveLengthTrait<RealType = I1::RealType> + Clone,
///     I1::Output: Contains<RealType = I1::RealType>,
///     Interval<I1::RealType>: From<I1> + From<I2> + From<I1::Output>,
/// {
///     let hull = a.hull(b);
///     let hull_general: Interval<I1::RealType> = hull.into();
///     let a_general: Interval<I1::RealType> = a.clone().into();
///     let b_general: Interval<I1::RealType> = b.clone().into();
///     
///     hull_general.contains_interval(&a_general) &&
///     hull_general.contains_interval(&b_general)
/// }
/// ```
///
/// ### Set Operations Integration
/// ```rust
/// use grid1d::intervals::*;
///
/// /// Compare hull with union operation
/// fn hull_vs_union_comparison() {
///     let a = IntervalClosed::new(1.0, 3.0);
///     let b = IntervalClosed::new(2.0, 5.0);
///     
///     // Hull always produces a single connected interval
///     let hull = a.hull(&b);
///     
///     // Union might produce disjoint intervals (but not in this case)
///     let union = a.union(&b);
///     
///     match union {
///         IntervalUnion::SingleConnected(connected) => {
///             // When union is connected, it should match the hull
///             // (modulo boundary type differences)
///         }
///         IntervalUnion::TwoDisjoint { .. } => {
///             // Hull always connects disjoint intervals
///             // This is where hull differs from union
///         }
///     }
/// }
/// ```
///
/// ## Mathematical Foundations and Proofs
///
/// ### Correctness Theorem
/// For any intervals `A` and `B` with finite positive length:
/// ```text
/// hull(A, B) = [min(inf A, inf B), max(sup A, sup B)]
/// ```
/// where boundary inclusion follows the most inclusive principle.
///
/// ### Proof Sketch of Containment Property
/// 1. **Lower bound**: `min(inf A, inf B) ≤ inf A` and `min(inf A, inf B) ≤ inf B`
/// 2. **Upper bound**: `max(sup A, sup B) ≥ sup A` and `max(sup A, sup B) ≥ sup B`
/// 3. **Boundary inclusion**: Most inclusive boundaries ensure all points in A ∪ B are contained
/// 4. **Therefore**: `A ⊆ hull(A, B)` and `B ⊆ hull(A, B)` ∎
///
/// ### Proof Sketch of Minimality Property
/// 1. **Assumption**: Interval C contains both A and B
/// 2. **Lower bound**: `inf C ≤ inf A` and `inf C ≤ inf B` ⟹ `inf C ≤ min(inf A, inf B)`
/// 3. **Upper bound**: `sup C ≥ sup A` and `sup C ≥ sup B` ⟹ `sup C ≥ max(sup A, sup B)`
/// 4. **Therefore**: `hull(A, B) ⊆ C` ∎
///
/// ## Best Practices and Recommendations
///
/// ### When to Use [`IntervalHull`]
/// - **Bounding box computation**: Find minimal interval containing multiple regions
/// - **Range analysis**: Determine combined range of multiple functions
/// - **Geometric algorithms**: Convex hull computation in 1D space
/// - **Data analysis**: Find overall range of multiple datasets
/// - **Optimization**: Combine constraint domains
///
/// ### Avoiding Common Pitfalls
/// ```rust
/// use grid1d::intervals::*;
///
/// // WRONG: Assuming hull preserves original boundary types
/// fn incorrect_assumption() {
///     let closed = IntervalClosed::new(1.0, 3.0);
///     let open = IntervalOpen::new(2.0, 5.0);
///     let hull = closed.hull(&open);
///     
///     // DON'T assume the result is closed just because one input was closed
///     // The result depends on boundary inclusion rules
/// }
///
/// // CORRECT: Handle all possible result types
/// fn correct_handling() {
///     let closed = IntervalClosed::new(1.0, 3.0);
///     let open = IntervalOpen::new(2.0, 5.0);
///     let hull = closed.hull(&open);
///     
///     match hull {
///         IntervalFinitePositiveLength::Closed(_) => { /* Handle closed */ }
///         IntervalFinitePositiveLength::Open(_) => { /* Handle open */ }
///         IntervalFinitePositiveLength::LowerClosedUpperOpen(_) => { /* Handle half-open */ }
///         IntervalFinitePositiveLength::LowerOpenUpperClosed(_) => { /* Handle half-open */ }
///     }
/// }
/// ```
///
/// The [`IntervalHull`] trait is fundamental for interval arithmetic and computational geometry
/// applications, providing mathematically sound and computationally efficient convex hull
/// operations while maintaining the library's strong type safety guarantees.
///
/// ## Support for All Interval Types
///
/// This trait is **fully generalized** to compute the convex hull between **any two intervals** implementing [`IntervalTrait`], including:
/// - **Bounded intervals**: [`IntervalClosed`], [`IntervalOpen`], [`IntervalLowerClosedUpperOpen`], [`IntervalLowerOpenUpperClosed`]
/// - **Half-unbounded intervals**: [`IntervalLowerClosedUpperUnbounded`], [`IntervalLowerOpenUpperUnbounded`], [`IntervalLowerUnboundedUpperClosed`], [`IntervalLowerUnboundedUpperOpen`]
/// - **Fully unbounded**: [`IntervalLowerUnboundedUpperUnbounded`]
/// - **Singleton intervals**: [`IntervalSingleton`]
/// - **Enum types**: [`Interval`], [`IntervalFiniteLength`], [`IntervalInfiniteLength`], [`IntervalFinitePositiveLength`]
///
/// ### Hull Rules for Different Interval Types
///
/// The hull operation follows a fundamental principle:
///
/// **If ANY interval is unbounded in a direction, the hull is unbounded in that direction.**
///
/// Specifically:
/// - **Both bounded**: Returns smallest bounded interval containing both (specific bounded type)
/// - **One or both unbounded above**: Returns interval unbounded above with minimum lower bound
/// - **One or both unbounded below**: Returns interval unbounded below with maximum upper bound
/// - **Both fully unbounded**: Returns the universal interval `(-∞, +∞)`
///
/// ### Examples with Unbounded Intervals
///
/// ```rust
/// use grid1d::intervals::*;
///
/// // Case 1: Both bounded → bounded result
/// let bounded1 = IntervalClosed::new(0.0, 2.0);  // [0, 2]
/// let bounded2 = IntervalClosed::new(1.0, 3.0);  // [1, 3]
/// let hull = bounded1.hull(&bounded2);           // [0, 3]
///
/// // Case 2: Bounded with half-unbounded → unbounded result
/// let bounded = IntervalClosed::new(0.0, 2.0);              // [0, 2]
/// let unbounded = IntervalLowerClosedUpperUnbounded::new(1.0);  // [1, +∞)
/// let hull = bounded.hull(&unbounded);                       // [0, +∞)
/// // Explanation: Takes min lower bound (0) and unbounded upper
///
/// // Case 3: Bounded with opposite-direction unbounded → fully unbounded
/// let bounded = IntervalClosed::new(-2.0, 1.0);              // [-2, 1]
/// let upper_unbounded = IntervalLowerUnboundedUpperClosed::new(0.0);  // (-∞, 0]
/// let hull = bounded.hull(&upper_unbounded);                  // (-∞, 1]
/// // Explanation: Unbounded below, max upper bound (1)
///
/// // Case 4: Two half-unbounded in same direction → half-unbounded result
/// let unbounded1 = IntervalLowerClosedUpperUnbounded::new(0.0);  // [0, +∞)
/// let unbounded2 = IntervalLowerClosedUpperUnbounded::new(5.0);  // [5, +∞)
/// let hull = unbounded1.hull(&unbounded2);                       // [0, +∞)
/// // Explanation: Min lower bound (0), both unbounded above
///
/// // Case 5: Mixed boundary types with unbounded
/// let open = IntervalOpen::new(0.0, 2.0);                        // (0, 2)
/// let unbounded = IntervalLowerClosedUpperUnbounded::new(1.5);   // [1.5, +∞)
/// let hull = open.hull(&unbounded);                              // (0, +∞)
/// // Explanation: Open lower wins (more restrictive), unbounded upper
///
/// // Case 6: Fully unbounded with any interval → fully unbounded
/// let bounded = IntervalClosed::new(0.0, 10.0);     // [0, 10]
/// let fully_unbounded = IntervalLowerUnboundedUpperUnbounded::new();  // (-∞, +∞)
/// let hull = bounded.hull(&fully_unbounded);        // (-∞, +∞)
/// ```
///
/// ### Return Type Strategy
///
/// The trait uses different output types depending on the input combinations:
///
/// - **Bounded-to-bounded**: Returns [`IntervalFinitePositiveLength<RealType>`] to preserve type specificity and performance
/// - **Bounded-to-unbounded**: Returns [`Interval<RealType>`] (universal enum) to accommodate any result type
/// - **Unbounded-to-bounded**: Returns [`Interval<RealType>`]
/// - **Unbounded-to-unbounded**: Returns [`Interval<RealType>`]
/// - **Using enum types**: Always returns [`Interval<RealType>`]
///
/// This design provides optimal performance for bounded intervals while supporting full generality.
pub trait IntervalHull<Other: IntervalTrait<RealType = Self::RealType>>: IntervalTrait {
    /// The type of interval returned by the hull operation.
    ///
    /// This associated type ensures type safety while allowing different implementations
    /// to return the most appropriate interval type based on their boundary semantics.
    /// The output type must implement [`IntervalTrait`] and use the same scalar type
    /// as the input intervals.
    ///
    /// For generalized hulls, the output is typically the universal [`Interval<RealType>`]
    /// enum which can represent any interval type (bounded, unbounded, etc.).
    type Output: IntervalTrait<RealType = Self::RealType>;

    /// Computes the convex hull (interval hull) between this interval and another.
    ///
    /// The convex hull is the smallest interval that contains both input intervals,
    /// following the **most inclusive boundary principle**:
    /// - Takes the minimum of lower bounds with most inclusive boundary type
    /// - Takes the maximum of upper bounds with most inclusive boundary type
    /// - Open boundaries are less inclusive than closed boundaries
    /// - Handles unbounded intervals correctly
    ///
    /// ## Mathematical Guarantee
    ///
    /// The result `H = self.hull(other)` satisfies:
    /// - **Containment**: `self ⊆ H` and `other ⊆ H`
    /// - **Minimality**: If `self ⊆ I` and `other ⊆ I`, then `H ⊆ I`
    /// - **Commutativity**: `self.hull(other) = other.hull(self)`
    ///
    /// ## Parameters
    ///
    /// - `other`: Another interval implementing [`IntervalTrait`] with the same scalar type
    ///
    /// ## Returns
    ///
    /// An interval of type [`Self::Output`] that represents the convex hull.
    /// The exact type depends on the boundary types and boundedness of the input intervals.
    ///
    /// ## Examples
    ///
    /// ```rust
    /// use grid1d::intervals::*;
    ///
    /// // Same types → preserve type when possible
    /// let a = IntervalClosed::new(1.0, 3.0);  // [1, 3]
    /// let b = IntervalClosed::new(2.0, 5.0);  // [2, 5]
    /// let hull = a.hull(&b);                  // [1, 5]
    ///
    /// // Different types → follow inclusion rules
    /// let closed = IntervalClosed::new(1.0, 3.0);  // [1, 3]
    /// let open = IntervalOpen::new(2.0, 5.0);      // (2, 5)
    /// let hull = closed.hull(&open);               // [1, 5) - closed lower wins, open upper wins
    ///
    /// // With unbounded intervals
    /// let bounded = IntervalClosed::new(0.0, 2.0);
    /// let unbounded = IntervalLowerClosedUpperUnbounded::new(1.0);
    /// let hull = bounded.hull(&unbounded);  // [0, +∞)
    /// ```
    fn hull(&self, other: &Other) -> Self::Output;
}

/// Helper function to compute the convex hull of two general intervals.
///
/// This function handles all interval types including bounded and unbounded intervals.
/// It uses runtime bound information to compute the hull following these rules:
///
/// - **Both bounds finite**: Returns bounded interval with min lower, max upper
/// - **One or both unbounded**: Returns appropriate unbounded interval
/// - **Boundary types**: "Closed wins over open" for equal values
///
/// # Parameters
///
/// - `interval1`: First interval implementing [`IntervalBoundsRuntime`]
/// - `interval2`: Second interval implementing [`IntervalBoundsRuntime`]
///
/// # Returns
///
/// An `Interval<RealType>` that can represent any interval type (bounded or unbounded).
#[inline]
fn compute_hull_general<RealType: RealScalar, I1, I2>(
    interval1: &I1,
    interval2: &I2,
) -> Interval<RealType>
where
    I1: IntervalBoundsRuntime<RealType = RealType>,
    I2: IntervalBoundsRuntime<RealType = RealType>,
{
    // Get bounds as owned values first to avoid lifetime issues
    let lower1 = interval1.lower_bound_runtime();
    let upper1 = interval1.upper_bound_runtime();
    let lower2 = interval2.lower_bound_runtime();
    let upper2 = interval2.upper_bound_runtime();

    let lower = min_lower_bound_maybe_unbounded(lower1, lower2);
    let upper = max_upper_bound_maybe_unbounded(upper1, upper2);

    Interval::try_new(lower, upper).unwrap_or_else(move |err| {
        panic!(
            "Hull computation produced invalid interval bounds: {err}. This indicates a bug in bound selection logic."
        )
    })
}

/// Helper function to compute the convex hull of two bounded intervals.
///
/// This function simplifies hull computation by using `min`/`max` on the bound objects directly,
/// leveraging the existing `PartialOrd` implementation. The "closed wins over open" logic
/// for equal values is handled by comparing bound values and checking `includes_boundary()`.
///
/// # Returns
///
/// An `IntervalFinitePositiveLength` with the appropriate bound combination.
#[inline]
fn compute_hull_bounded_intervals<RealType: RealScalar, I1, I2>(
    interval1: &I1,
    interval2: &I2,
) -> IntervalFinitePositiveLength<RealType>
where
    I1: IntervalFinitePositiveLengthTrait<RealType = RealType>,
    I2: IntervalFinitePositiveLengthTrait<RealType = RealType>,
{
    let lower1 = interval1.lower_bound_runtime().unwrap();
    let upper1 = interval1.upper_bound_runtime().unwrap();
    let lower2 = interval2.lower_bound_runtime().unwrap();
    let upper2 = interval2.upper_bound_runtime().unwrap();

    let lower = min_lower_bound(lower1, lower2);
    let upper = max_upper_bound(upper1, upper2);

    IntervalFinitePositiveLength::try_new(lower, upper)
        .expect("Failed to create the interval hull!")
}

// Specific implementations for bounded-to-bounded hulls (preserve bounded return type)
// These are more specific than the blanket implementations below, so Rust will prefer them
#[duplicate_item(
    T Other;
    [IntervalClosed] [IntervalClosed];
    [IntervalClosed] [IntervalOpen];
    [IntervalClosed] [IntervalLowerClosedUpperOpen];
    [IntervalClosed] [IntervalLowerOpenUpperClosed];
    [IntervalOpen] [IntervalClosed];
    [IntervalOpen] [IntervalOpen];
    [IntervalOpen] [IntervalLowerClosedUpperOpen];
    [IntervalOpen] [IntervalLowerOpenUpperClosed];
    [IntervalLowerClosedUpperOpen] [IntervalClosed];
    [IntervalLowerClosedUpperOpen] [IntervalOpen];
    [IntervalLowerClosedUpperOpen] [IntervalLowerClosedUpperOpen];
    [IntervalLowerClosedUpperOpen] [IntervalLowerOpenUpperClosed];
    [IntervalLowerOpenUpperClosed] [IntervalClosed];
    [IntervalLowerOpenUpperClosed] [IntervalOpen];
    [IntervalLowerOpenUpperClosed] [IntervalLowerClosedUpperOpen];
    [IntervalLowerOpenUpperClosed] [IntervalLowerOpenUpperClosed];
)]
impl<RealType: RealScalar> IntervalHull<Other<RealType>> for T<RealType> {
    type Output = IntervalFinitePositiveLength<RealType>;

    fn hull(&self, other: &Other<RealType>) -> Self::Output {
        compute_hull_bounded_intervals(self, other)
    }
}

// General implementation for all IntervalTrait types
impl<RealType: RealScalar, Other: IntervalTrait<RealType = RealType>> IntervalHull<Other>
    for Interval<RealType>
{
    type Output = Interval<RealType>;

    fn hull(&self, other: &Other) -> Self::Output {
        compute_hull_general(self, other)
    }
}

// Implementations for bounded interval types with unbounded intervals
// This allows IntervalClosed, IntervalOpen, etc. to hull with unbounded intervals
#[duplicate_item(
    T Other;
    [IntervalClosed] [IntervalLowerClosedUpperUnbounded];
    [IntervalClosed] [IntervalLowerOpenUpperUnbounded];
    [IntervalClosed] [IntervalLowerUnboundedUpperClosed];
    [IntervalClosed] [IntervalLowerUnboundedUpperOpen];
    [IntervalClosed] [IntervalLowerUnboundedUpperUnbounded];
    [IntervalOpen] [IntervalLowerClosedUpperUnbounded];
    [IntervalOpen] [IntervalLowerOpenUpperUnbounded];
    [IntervalOpen] [IntervalLowerUnboundedUpperClosed];
    [IntervalOpen] [IntervalLowerUnboundedUpperOpen];
    [IntervalOpen] [IntervalLowerUnboundedUpperUnbounded];
    [IntervalLowerClosedUpperOpen] [IntervalLowerClosedUpperUnbounded];
    [IntervalLowerClosedUpperOpen] [IntervalLowerOpenUpperUnbounded];
    [IntervalLowerClosedUpperOpen] [IntervalLowerUnboundedUpperClosed];
    [IntervalLowerClosedUpperOpen] [IntervalLowerUnboundedUpperOpen];
    [IntervalLowerClosedUpperOpen] [IntervalLowerUnboundedUpperUnbounded];
    [IntervalLowerOpenUpperClosed] [IntervalLowerClosedUpperUnbounded];
    [IntervalLowerOpenUpperClosed] [IntervalLowerOpenUpperUnbounded];
    [IntervalLowerOpenUpperClosed] [IntervalLowerUnboundedUpperClosed];
    [IntervalLowerOpenUpperClosed] [IntervalLowerUnboundedUpperOpen];
    [IntervalLowerOpenUpperClosed] [IntervalLowerUnboundedUpperUnbounded];
)]
impl<RealType: RealScalar> IntervalHull<Other<RealType>> for T<RealType> {
    type Output = Interval<RealType>;

    fn hull(&self, other: &Other<RealType>) -> Self::Output {
        compute_hull_general(self, other)
    }
}

#[duplicate_item(
    T Other;
    [IntervalLowerClosedUpperUnbounded] [IntervalClosed];
    [IntervalLowerClosedUpperUnbounded] [IntervalOpen];
    [IntervalLowerClosedUpperUnbounded] [IntervalLowerClosedUpperOpen];
    [IntervalLowerClosedUpperUnbounded] [IntervalLowerOpenUpperClosed];
    [IntervalLowerClosedUpperUnbounded] [IntervalLowerClosedUpperUnbounded];
    [IntervalLowerClosedUpperUnbounded] [IntervalLowerOpenUpperUnbounded];
    [IntervalLowerClosedUpperUnbounded] [IntervalLowerUnboundedUpperClosed];
    [IntervalLowerClosedUpperUnbounded] [IntervalLowerUnboundedUpperOpen];
    [IntervalLowerClosedUpperUnbounded] [IntervalLowerUnboundedUpperUnbounded];
    [IntervalLowerOpenUpperUnbounded] [IntervalClosed];
    [IntervalLowerOpenUpperUnbounded] [IntervalOpen];
    [IntervalLowerOpenUpperUnbounded] [IntervalLowerClosedUpperOpen];
    [IntervalLowerOpenUpperUnbounded] [IntervalLowerOpenUpperClosed];
    [IntervalLowerOpenUpperUnbounded] [IntervalLowerClosedUpperUnbounded];
    [IntervalLowerOpenUpperUnbounded] [IntervalLowerOpenUpperUnbounded];
    [IntervalLowerOpenUpperUnbounded] [IntervalLowerUnboundedUpperClosed];
    [IntervalLowerOpenUpperUnbounded] [IntervalLowerUnboundedUpperOpen];
    [IntervalLowerOpenUpperUnbounded] [IntervalLowerUnboundedUpperUnbounded];
    [IntervalLowerUnboundedUpperClosed] [IntervalClosed];
    [IntervalLowerUnboundedUpperClosed] [IntervalOpen];
    [IntervalLowerUnboundedUpperClosed] [IntervalLowerClosedUpperOpen];
    [IntervalLowerUnboundedUpperClosed] [IntervalLowerOpenUpperClosed];
    [IntervalLowerUnboundedUpperClosed] [IntervalLowerClosedUpperUnbounded];
    [IntervalLowerUnboundedUpperClosed] [IntervalLowerOpenUpperUnbounded];
    [IntervalLowerUnboundedUpperClosed] [IntervalLowerUnboundedUpperClosed];
    [IntervalLowerUnboundedUpperClosed] [IntervalLowerUnboundedUpperOpen];
    [IntervalLowerUnboundedUpperClosed] [IntervalLowerUnboundedUpperUnbounded];
    [IntervalLowerUnboundedUpperOpen] [IntervalClosed];
    [IntervalLowerUnboundedUpperOpen] [IntervalOpen];
    [IntervalLowerUnboundedUpperOpen] [IntervalLowerClosedUpperOpen];
    [IntervalLowerUnboundedUpperOpen] [IntervalLowerOpenUpperClosed];
    [IntervalLowerUnboundedUpperOpen] [IntervalLowerClosedUpperUnbounded];
    [IntervalLowerUnboundedUpperOpen] [IntervalLowerOpenUpperUnbounded];
    [IntervalLowerUnboundedUpperOpen] [IntervalLowerUnboundedUpperClosed];
    [IntervalLowerUnboundedUpperOpen] [IntervalLowerUnboundedUpperOpen];
    [IntervalLowerUnboundedUpperOpen] [IntervalLowerUnboundedUpperUnbounded];
    [IntervalLowerUnboundedUpperUnbounded] [IntervalClosed];
    [IntervalLowerUnboundedUpperUnbounded] [IntervalOpen];
    [IntervalLowerUnboundedUpperUnbounded] [IntervalLowerClosedUpperOpen];
    [IntervalLowerUnboundedUpperUnbounded] [IntervalLowerOpenUpperClosed];
    [IntervalLowerUnboundedUpperUnbounded] [IntervalLowerClosedUpperUnbounded];
    [IntervalLowerUnboundedUpperUnbounded] [IntervalLowerOpenUpperUnbounded];
    [IntervalLowerUnboundedUpperUnbounded] [IntervalLowerUnboundedUpperClosed];
    [IntervalLowerUnboundedUpperUnbounded] [IntervalLowerUnboundedUpperOpen];
    [IntervalLowerUnboundedUpperUnbounded] [IntervalLowerUnboundedUpperUnbounded];
)]
impl<RealType: RealScalar> IntervalHull<Other<RealType>> for T<RealType> {
    type Output = Interval<RealType>;

    fn hull(&self, other: &Other<RealType>) -> Self::Output {
        compute_hull_general(self, other)
    }
}

#[duplicate_item(
    I;
    [IntervalSingleton];
    [IntervalFiniteLength];
    [IntervalInfiniteLength];
)]
impl<RealType: RealScalar, Other: IntervalTrait<RealType = RealType>> IntervalHull<Other>
    for I<RealType>
where
    Other: IntervalTrait<RealType = RealType>,
{
    type Output = Interval<RealType>;

    fn hull(&self, other: &Other) -> Self::Output {
        compute_hull_general(self, other)
    }
}
//------------------------------------------------------------------------------------------------

//------------------------------------------------------------------------------------------------
/// Common interface for intervals that have a finite lower bound.
///
/// The [`GetLowerBoundValue`] trait provides unified access to the lower bound of intervals
/// that have a finite lower boundary. This enables generic programming over intervals
/// with lower bounds while maintaining type safety and performance.
///
/// ## Implementing Types
///
/// All interval types with finite lower bounds implement this trait:
///
/// ### Bounded Intervals
/// - [`IntervalClosed`]: `[a, b]`, [`IntervalOpen`]: `(a, b)`
/// - [`IntervalLowerOpenUpperClosed`]: `(a, b]`, [`IntervalLowerClosedUpperOpen`]: `[a, b)`
///
/// ### Lower-Bounded, Upper-Unbounded Intervals
/// - [`IntervalLowerClosedUpperUnbounded`]: `[a, +∞)`
/// - [`IntervalLowerOpenUpperUnbounded`]: `(a, +∞)`
///
/// ## Core Operation
///
/// - [`lower_bound_value`](GetLowerBoundValue::lower_bound_value): Returns a reference to the lower bound value
///
/// This method provides **read-only access** with **zero computational cost**.
///
/// ## Usage Examples
///
/// ### Basic Lower Bound Access
/// ```rust
/// use grid1d::intervals::*;
/// use num_valid::RealNative64StrictFiniteInDebug;
/// use try_create::TryNew;
///
/// type Real = RealNative64StrictFiniteInDebug;
///
/// let closed = IntervalClosed::new(
///     Real::try_new(-2.0).unwrap(),
///     Real::try_new(5.0).unwrap()
/// );
/// let unbounded = IntervalLowerClosedUpperUnbounded::new(
///     Real::try_new(0.0).unwrap()
/// );
///
/// assert_eq!(closed.lower_bound_value(), &-2.0);
/// assert_eq!(unbounded.lower_bound_value(), &0.0);
/// ```
///
/// ### Generic Programming Over Lower-Bounded Intervals
/// ```rust
/// use grid1d::intervals::*;
/// use num_valid::RealScalar;
///
/// fn analyze_lower_bound<I, T>(interval: &I) -> String
/// where
///     I: GetLowerBoundValue<LowerBoundValue = T>,
///     T: RealScalar + std::fmt::Display,
/// {
///     format!("Lower bound: {}", interval.lower_bound_value())
/// }
///
/// let closed = IntervalClosed::new(-1.0, 1.0);
/// let unbounded = IntervalLowerOpenUpperUnbounded::new(5.0);
///
/// println!("{}", analyze_lower_bound(&closed));      // "Lower bound: -1"
/// println!("{}", analyze_lower_bound(&unbounded));   // "Lower bound: 5"
/// ```
///
/// ### Constraint Processing
/// ```rust
/// use grid1d::intervals::*;
/// use num_valid::RealScalar;
///
/// fn find_minimum_lower_bound<I, T>(intervals: &[I]) -> Option<T>
/// where
///     I: GetLowerBoundValue<LowerBoundValue = T>,
///     T: RealScalar + Clone + PartialOrd,
/// {
///     intervals.iter()
///         .map(|interval| interval.lower_bound_value())
///         .min_by(|a, b| a.partial_cmp(b).unwrap())
///         .cloned()
/// }
///
/// let intervals = vec![
///     IntervalClosed::new(1.0, 5.0),
///     IntervalClosed::new(-2.0, 3.0),
///     IntervalClosed::new(0.0, 4.0),
/// ];
///
/// let min_lower = find_minimum_lower_bound(&intervals).unwrap();
/// assert_eq!(min_lower, -2.0);
/// ```
pub trait GetLowerBoundValue: Debug {
    /// The scalar type used for the lower bound value.
    type LowerBoundValue: RealScalar;

    /// Return the lower bound of the interval.
    ///
    /// This method provides read-only access to the interval's lower bound value.
    /// The returned reference is guaranteed to be valid for the lifetime of the interval.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use grid1d::intervals::*;
    ///
    /// let interval = IntervalClosed::new(1.0, 3.0);
    /// assert_eq!(interval.lower_bound_value(), &1.0);
    ///
    /// let lower_closed_interval = IntervalLowerClosedUpperUnbounded::new(5.0);
    /// let lower_open_interval = IntervalLowerOpenUpperUnbounded::new(10.0);
    ///
    /// assert_eq!(lower_closed_interval.lower_bound_value(), &5.0);
    /// assert_eq!(lower_open_interval.lower_bound_value(), &10.0);
    ///
    /// // Both intervals extend infinitely to the right
    /// assert!(lower_closed_interval.contains_point(&1000.0));
    /// assert!(lower_open_interval.contains_point(&1000.0));
    ///
    /// // But they differ in their treatment of the lower bound
    /// assert!(lower_closed_interval.contains_point(&5.0));   // [5, +∞) includes 5
    /// assert!(!lower_open_interval.contains_point(&10.0));   // (10, +∞) excludes 10
    /// ```
    ///
    /// # Performance
    ///
    /// This operation is O(1) and typically compiles to a simple memory access.
    /// There is no computational overhead regardless of the scalar type used.
    fn lower_bound_value(&self) -> &Self::LowerBoundValue;

    /// Check if the lower bound is closed (included in the interval).
    ///
    /// This method returns `true` if the lower bound value is part of the interval,
    /// and `false` if it is excluded. This corresponds to the mathematical distinction
    /// between closed bounds `[a` and open bounds `(a`.
    ///
    /// ## Mathematical Interpretation
    ///
    /// - **Closed lower bound** (`true`): The interval includes its lower bound value
    ///   - Mathematical notation: `[a, ...` where `x ≥ a`
    ///   - The boundary point `a` is contained in the interval
    ///
    /// - **Open lower bound** (`false`): The interval excludes its lower bound value
    ///   - Mathematical notation: `(a, ...` where `x > a`
    ///   - The boundary point `a` is not contained in the interval
    ///
    /// ## Examples
    ///
    /// ```rust
    /// use grid1d::intervals::*;
    /// use num_valid::RealNative64StrictFiniteInDebug;
    /// use try_create::TryNew;
    ///
    /// type Real = RealNative64StrictFiniteInDebug;
    ///
    /// let closed = IntervalClosed::new(
    ///     Real::try_new(0.0).unwrap(),
    ///     Real::try_new(1.0).unwrap()
    /// );
    /// assert!(closed.is_lower_bound_closed());      // [0, 1] has closed lower bound
    ///
    /// let open = IntervalOpen::new(
    ///     Real::try_new(0.0).unwrap(),
    ///     Real::try_new(1.0).unwrap()
    /// );
    /// assert!(!open.is_lower_bound_closed());       // (0, 1) has open lower bound
    ///
    /// let half_open = IntervalLowerClosedUpperOpen::new(
    ///     Real::try_new(0.0).unwrap(),
    ///     Real::try_new(1.0).unwrap()
    /// );
    /// assert!(half_open.is_lower_bound_closed());   // [0, 1) has closed lower bound
    ///
    /// let other_half = IntervalLowerOpenUpperClosed::new(
    ///     Real::try_new(0.0).unwrap(),
    ///     Real::try_new(1.0).unwrap()
    /// );
    /// assert!(!other_half.is_lower_bound_closed()); // (0, 1] has open lower bound
    /// ```
    ///
    /// ## Relationship with Point Containment
    ///
    /// The lower bound closure directly affects point containment at the boundary:
    ///
    /// ```rust
    /// use grid1d::intervals::*;
    ///
    /// let closed = IntervalClosed::new(0.0, 1.0);
    /// let open = IntervalOpen::new(0.0, 1.0);
    ///
    /// // Closed lower bound contains the boundary value
    /// assert!(closed.is_lower_bound_closed());
    /// assert!(closed.contains_point(&0.0));        // Lower bound included
    ///
    /// // Open lower bound excludes the boundary value
    /// assert!(!open.is_lower_bound_closed());
    /// assert!(!open.contains_point(&0.0));         // Lower bound excluded
    /// ```
    ///
    /// ## Generic Programming Applications
    ///
    /// ```rust
    /// use grid1d::intervals::*;
    /// use num_valid::RealScalar;
    ///
    /// fn analyze_lower_boundary<I, T>(interval: &I) -> String
    /// where
    ///     I: GetLowerBoundValue<LowerBoundValue = T>,
    ///     T: RealScalar + std::fmt::Display,
    /// {
    ///     let bound_value = interval.lower_bound_value();
    ///     let inclusion = if interval.is_lower_bound_closed() {
    ///         "included"
    ///     } else {
    ///         "excluded"
    ///     };
    ///     
    ///     format!("Lower bound {} is {}", bound_value, inclusion)
    /// }
    ///
    /// let closed = IntervalClosed::new(-1.0, 1.0);
    /// let open = IntervalLowerOpenUpperClosed::new(-1.0, 1.0);
    ///
    /// assert_eq!(analyze_lower_boundary(&closed), "Lower bound -1 is included");
    /// assert_eq!(analyze_lower_boundary(&open), "Lower bound -1 is excluded");
    /// ```
    ///
    /// ## Performance
    ///
    /// This operation is O(1) and typically compiles to a simple constant or type-based
    /// determination. There is no computational overhead regardless of the scalar type used.
    fn is_lower_bound_closed(&self) -> bool;

    /// Check if the lower bound is open (excluded from the interval).
    ///
    /// This method returns `true` if the lower bound value is excluded from the interval,
    /// and `false` if it is included. This is the logical complement of [`is_lower_bound_closed()`](GetLowerBoundValue::is_lower_bound_closed).
    ///
    /// ## Mathematical Interpretation
    ///
    /// - **Open lower bound** (`true`): The interval excludes its lower bound value
    ///   - Mathematical notation: `(a, ...` where `x > a`
    ///   - The boundary point `a` is not contained in the interval
    ///
    /// - **Closed lower bound** (`false`): The interval includes its lower bound value
    ///   - Mathematical notation: `[a, ...` where `x ≥ a`
    ///   - The boundary point `a` is contained in the interval
    ///
    /// ## Relationship with `is_lower_bound_closed()`
    ///
    /// This method is defined as the logical complement of [`is_lower_bound_closed()`](GetLowerBoundValue::is_lower_bound_closed):
    /// ```text
    /// is_lower_bound_open() == !is_lower_bound_closed()
    /// ```
    ///
    /// ## Examples
    ///
    /// ```rust
    /// use grid1d::intervals::*;
    /// use num_valid::RealNative64StrictFiniteInDebug;
    /// use try_create::TryNew;
    ///
    /// type Real = RealNative64StrictFiniteInDebug;
    ///
    /// let open = IntervalOpen::new(
    ///     Real::try_new(0.0).unwrap(),
    ///     Real::try_new(1.0).unwrap()
    /// );
    /// assert!(open.is_lower_bound_open());          // (0, 1) has open lower bound
    /// assert!(!open.is_lower_bound_closed());       // Complement relationship
    ///
    /// let closed = IntervalClosed::new(
    ///     Real::try_new(0.0).unwrap(),
    ///     Real::try_new(1.0).unwrap()
    /// );
    /// assert!(!closed.is_lower_bound_open());       // [0, 1] has closed lower bound
    /// assert!(closed.is_lower_bound_closed());      // Complement relationship
    ///
    /// let half_open = IntervalLowerOpenUpperClosed::new(
    ///     Real::try_new(0.0).unwrap(),
    ///     Real::try_new(1.0).unwrap()
    /// );
    /// assert!(half_open.is_lower_bound_open());     // (0, 1] has open lower bound
    /// assert!(!half_open.is_lower_bound_closed());  // Complement relationship
    /// ```
    ///
    /// ## Usage in Boundary Analysis
    ///
    /// ```rust
    /// use grid1d::intervals::*;
    /// use num_valid::RealScalar;
    ///
    /// fn check_boundary_types<I, T>(interval: &I) -> (bool, bool)
    /// where
    ///     I: GetLowerBoundValue<LowerBoundValue = T>,
    ///     T: RealScalar,
    /// {
    ///     let lower_open = interval.is_lower_bound_open();
    ///     let lower_closed = interval.is_lower_bound_closed();
    ///     
    ///     // These should always be complements
    ///     assert_eq!(lower_open, !lower_closed);
    ///     
    ///     (lower_open, lower_closed)
    /// }
    ///
    /// let open_interval = IntervalOpen::new(0.0, 1.0);
    /// let (is_open, is_closed) = check_boundary_types(&open_interval);
    /// assert!(is_open && !is_closed);
    ///
    /// let closed_interval = IntervalClosed::new(0.0, 1.0);
    /// let (is_open, is_closed) = check_boundary_types(&closed_interval);
    /// assert!(!is_open && is_closed);
    /// ```
    ///
    /// ## Strict Inequality Testing
    ///
    /// ```rust
    /// use grid1d::intervals::*;
    /// use num_valid::RealScalar;
    ///
    /// fn requires_strict_lower_bound<I, T>(interval: &I) -> bool
    /// where
    ///     I: GetLowerBoundValue<LowerBoundValue = T>,
    ///     T: RealScalar,
    /// {
    ///     // Check if the interval enforces x > lower_bound (strict inequality)
    ///     interval.is_lower_bound_open()
    /// }
    ///
    /// let strict_interval = IntervalOpen::new(0.0, 1.0);           // (0, 1) requires x > 0
    /// let inclusive_interval = IntervalClosed::new(0.0, 1.0);      // [0, 1] allows x ≥ 0
    ///
    /// assert!(requires_strict_lower_bound(&strict_interval));      // true: x > 0 required
    /// assert!(!requires_strict_lower_bound(&inclusive_interval));  // false: x ≥ 0 allowed
    /// ```
    ///
    /// ## Performance
    ///
    /// This operation is O(1) and typically compiles to a simple constant or type-based
    /// determination. Since it's implemented as the logical complement of
    /// [`is_lower_bound_closed()`](GetLowerBoundValue::is_lower_bound_closed), it has identical performance characteristics.
    #[inline(always)]
    fn is_lower_bound_open(&self) -> bool {
        !self.is_lower_bound_closed()
    }
}

/// Common interface for intervals that have a finite upper bound.
///
/// The [`GetUpperBoundValue`] trait provides unified access to the upper bound of intervals
/// that have a finite upper boundary. This enables generic programming over intervals
/// with upper bounds while maintaining type safety and performance.
///
/// ## Implementing Types
///
/// All interval types with finite upper bounds implement this trait:
///
/// ### Bounded Intervals
/// - [`IntervalClosed`]: `[a, b]`, [`IntervalOpen`]: `(a, b)`
/// - [`IntervalLowerOpenUpperClosed`]: `(a, b]`, [`IntervalLowerClosedUpperOpen`]: `[a, b)`
///
/// ### Upper-Bounded, Lower-Unbounded Intervals
/// - [`IntervalLowerUnboundedUpperClosed`]: `(-∞, b]`
/// - [`IntervalLowerUnboundedUpperOpen`]: `(-∞, b)`
///
/// ## Core Operation
///
/// - [`upper_bound_value`](GetUpperBoundValue::upper_bound_value): Returns a reference to the upper bound value
///
/// This method provides **read-only access** with **zero computational cost**.
///
/// ## Usage Examples
///
/// ### Basic Upper Bound Access
/// ```rust
/// use grid1d::intervals::*;
/// use num_valid::RealNative64StrictFiniteInDebug;
/// use try_create::TryNew;
///
/// type Real = RealNative64StrictFiniteInDebug;
///
/// let closed = IntervalClosed::new(
///     Real::try_new(-2.0).unwrap(),
///     Real::try_new(5.0).unwrap()
/// );
/// let unbounded = IntervalLowerUnboundedUpperClosed::new(
///     Real::try_new(10.0).unwrap()
/// );
///
/// assert_eq!(closed.upper_bound_value(), &5.0);
/// assert_eq!(unbounded.upper_bound_value(), &10.0);
/// ```
///
/// ### Generic Programming Over Upper-Bounded Intervals
/// ```rust
/// use grid1d::intervals::*;
/// use num_valid::RealScalar;
///
/// fn analyze_upper_bound<I, T>(interval: &I) -> String
/// where
///     I: GetUpperBoundValue<UpperBoundValue = T>,
///     T: RealScalar + std::fmt::Display,
/// {
///     format!("Upper bound: {}", interval.upper_bound_value())
/// }
///
/// let closed = IntervalClosed::new(-1.0, 1.0);
/// let unbounded = IntervalLowerUnboundedUpperClosed::new(5.0);
///
/// println!("{}", analyze_upper_bound(&closed));      // "Upper bound: 1"
/// println!("{}", analyze_upper_bound(&unbounded));   // "Upper bound: 5"
/// ```
pub trait GetUpperBoundValue: Debug {
    /// The scalar type used for the upper bound value.
    ///
    /// This type must satisfy the [`RealScalar`] trait, ensuring it represents
    /// a valid real number suitable for interval arithmetic.
    type UpperBoundValue: RealScalar;

    /// Return the upper bound of the interval.
    ///
    /// This method provides read-only access to the interval's upper bound value.
    /// The returned reference is guaranteed to be valid for the lifetime of the interval.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use grid1d::intervals::*;
    ///
    /// let interval = IntervalClosed::new(1.0, 3.0);
    /// assert_eq!(interval.upper_bound_value(), &3.0);
    ///
    /// let upper_closed_interval = IntervalLowerUnboundedUpperClosed::new(15.0);
    /// let upper_open_interval = IntervalLowerUnboundedUpperOpen::new(20.0);
    /// assert_eq!(upper_closed_interval.upper_bound_value(), &15.0);
    /// assert_eq!(upper_open_interval.upper_bound_value(), &20.0);
    ///
    /// // Both intervals extend infinitely to the left
    /// assert!(upper_closed_interval.contains_point(&-1000.0));
    /// assert!(upper_open_interval.contains_point(&-1000.0));
    ///
    /// // But they differ in their treatment of the upper bound
    /// assert!(upper_closed_interval.contains_point(&15.0));   // (-∞, 15] includes 15
    /// assert!(!upper_open_interval.contains_point(&20.0));    // (-∞, 20) excludes 20
    /// ```
    ///
    /// # Performance
    ///
    /// This operation is O(1) and typically compiles to a simple memory access.
    /// There is no computational overhead regardless of the scalar type used.
    fn upper_bound_value(&self) -> &Self::UpperBoundValue;

    /// Check if the upper bound of the interval is closed (inclusive).
    ///
    /// Returns `true` if the upper boundary point is included in the interval,
    /// `false` if it is excluded. This determines whether the interval uses
    /// a `≤` (closed) or `<` (open) relation at the upper endpoint.
    ///
    /// # Boundary Semantics
    ///
    /// The upper bound closure affects:
    /// - **Point containment**: Whether `upper_bound_value` is in the interval
    /// - **Mathematical notation**: Closed uses `]`, open uses `)`
    /// - **Interval arithmetic**: Affects intersection and union operations
    ///
    /// ## Interval Type Behavior
    ///
    /// | Interval Type                    | Notation    | Returns |
    /// |----------------------------------|-------------|---------|
    /// | `IntervalClosed`                 | `[a, b]`    | `true`  |
    /// | `IntervalOpen`                   | `(a, b)`    | `false` |
    /// | `IntervalLowerClosedUpperOpen`   | `[a, b)`    | `false` |
    /// | `IntervalLowerOpenUpperClosed`   | `(a, b]`    | `true`  |
    /// | `IntervalLowerUnboundedUpperClosed` | `(-∞, b]` | `true`  |
    /// | `IntervalLowerUnboundedUpperOpen`   | `(-∞, b)` | `false` |
    /// | `IntervalSingleton`              | `{a}`       | `true`  |
    ///
    /// # Examples
    ///
    /// ```rust
    /// use grid1d::intervals::*;
    ///
    /// // Closed upper bound - includes the endpoint
    /// let closed = IntervalClosed::new(0.0, 10.0);
    /// assert!(closed.is_upper_bound_closed());
    /// assert!(closed.contains_point(&10.0));  // 10 is in [0, 10]
    ///
    /// // Open upper bound - excludes the endpoint
    /// let half_open = IntervalLowerClosedUpperOpen::new(0.0, 10.0);
    /// assert!(!half_open.is_upper_bound_closed());
    /// assert!(!half_open.contains_point(&10.0));  // 10 is not in [0, 10)
    ///
    /// // Unbounded intervals
    /// let upper_closed = IntervalLowerUnboundedUpperClosed::new(5.0);
    /// assert!(upper_closed.is_upper_bound_closed());  // (-∞, 5] includes 5
    /// assert!(upper_closed.contains_point(&5.0));
    ///
    /// let upper_open = IntervalLowerUnboundedUpperOpen::new(5.0);
    /// assert!(!upper_open.is_upper_bound_closed());   // (-∞, 5) excludes 5
    /// assert!(!upper_open.contains_point(&5.0));
    /// ```
    ///
    /// # Generic Programming
    ///
    /// ```rust
    /// use grid1d::intervals::*;
    /// use num_valid::RealScalar;
    ///
    /// fn check_endpoint_inclusion<I>(interval: &I, point: &I::RealType) -> bool
    /// where
    ///     I: GetUpperBoundValue + IntervalTrait,
    ///     I::UpperBoundValue: PartialEq<I::RealType>,
    /// {
    ///     if interval.upper_bound_value() == point {
    ///         interval.is_upper_bound_closed()
    ///     } else {
    ///         interval.contains_point(point)
    ///     }
    /// }
    ///
    /// let closed = IntervalClosed::new(0.0, 10.0);
    /// let open = IntervalOpen::new(0.0, 10.0);
    ///
    /// assert!(check_endpoint_inclusion(&closed, &10.0));   // Closed includes endpoint
    /// assert!(!check_endpoint_inclusion(&open, &10.0));    // Open excludes endpoint
    /// ```
    ///
    /// # See Also
    ///
    /// - [`is_upper_bound_open`](GetUpperBoundValue::is_upper_bound_open) - Logical complement
    /// - [`is_lower_bound_closed`](GetLowerBoundValue::is_lower_bound_closed) - Lower bound closure
    /// - [`upper_bound_value`](GetUpperBoundValue::upper_bound_value) - Get the upper bound value
    fn is_upper_bound_closed(&self) -> bool;

    /// Check if the upper bound of the interval is open (exclusive).
    ///
    /// Returns `true` if the upper boundary point is excluded from the interval,
    /// `false` if it is included. This is the logical complement of
    /// [`is_upper_bound_closed`](GetUpperBoundValue::is_upper_bound_closed).
    ///
    /// # Boundary Semantics
    ///
    /// An open upper bound means:
    /// - The interval uses a strict `<` inequality at the upper endpoint
    /// - The upper bound value itself is **not** contained in the interval
    /// - Mathematical notation uses `)` instead of `]`
    /// - The interval extends arbitrarily close to, but never reaches, the upper bound
    ///
    /// ## Interval Type Behavior
    ///
    /// | Interval Type                    | Notation    | Returns |
    /// |----------------------------------|-------------|---------|
    /// | `IntervalClosed`                 | `[a, b]`    | `false` |
    /// | `IntervalOpen`                   | `(a, b)`    | `true`  |
    /// | `IntervalLowerClosedUpperOpen`   | `[a, b)`    | `true`  |
    /// | `IntervalLowerOpenUpperClosed`   | `(a, b]`    | `false` |
    /// | `IntervalLowerUnboundedUpperClosed` | `(-∞, b]` | `false` |
    /// | `IntervalLowerUnboundedUpperOpen`   | `(-∞, b)` | `true`  |
    /// | `IntervalSingleton`              | `{a}`       | `false` |
    ///
    /// # Examples
    ///
    /// ```rust
    /// use grid1d::intervals::*;
    ///
    /// // Half-open interval [0, 10) - open upper bound
    /// let half_open = IntervalLowerClosedUpperOpen::new(0.0, 10.0);
    /// assert!(half_open.is_upper_bound_open());
    /// assert!(!half_open.contains_point(&10.0));  // 10 excluded
    /// assert!(half_open.contains_point(&9.999));  // Points arbitrarily close included
    ///
    /// // Fully open interval (0, 10) - open upper bound
    /// let open = IntervalOpen::new(0.0, 10.0);
    /// assert!(open.is_upper_bound_open());
    /// assert!(!open.contains_point(&10.0));
    ///
    /// // Closed interval [0, 10] - NOT open upper bound
    /// let closed = IntervalClosed::new(0.0, 10.0);
    /// assert!(!closed.is_upper_bound_open());
    /// assert!(closed.contains_point(&10.0));  // 10 included
    ///
    /// // Unbounded open interval (-∞, 5)
    /// let unbounded = IntervalLowerUnboundedUpperOpen::new(5.0);
    /// assert!(unbounded.is_upper_bound_open());
    /// assert!(!unbounded.contains_point(&5.0));
    /// ```
    ///
    /// # Complement Relationship
    ///
    /// This method is the logical complement of `is_upper_bound_closed()`:
    ///
    /// ```rust
    /// use grid1d::intervals::*;
    ///
    /// // Test complement relationship for each interval type
    /// let closed = IntervalClosed::new(0.0, 1.0);
    /// assert_eq!(closed.is_upper_bound_open(), !closed.is_upper_bound_closed());
    ///
    /// let open = IntervalOpen::new(0.0, 1.0);
    /// assert_eq!(open.is_upper_bound_open(), !open.is_upper_bound_closed());
    ///
    /// let half_open1 = IntervalLowerClosedUpperOpen::new(0.0, 1.0);
    /// assert_eq!(half_open1.is_upper_bound_open(), !half_open1.is_upper_bound_closed());
    ///
    /// let half_open2 = IntervalLowerOpenUpperClosed::new(0.0, 1.0);
    /// assert_eq!(half_open2.is_upper_bound_open(), !half_open2.is_upper_bound_closed());
    /// ```
    ///
    /// # Implementation Note
    ///
    /// This method has a default implementation as `!self.is_upper_bound_closed()`.
    /// Types implementing [`GetUpperBoundValue`] only need to implement
    /// `is_upper_bound_closed()`, and this method is automatically derived.
    ///
    /// # See Also
    ///
    /// - [`is_upper_bound_closed`](GetUpperBoundValue::is_upper_bound_closed) - Logical complement
    /// - [`is_lower_bound_open`](GetLowerBoundValue::is_lower_bound_open) - Lower bound openness
    /// - [`upper_bound_value`](GetUpperBoundValue::upper_bound_value) - Get the upper bound value
    #[inline(always)]
    fn is_upper_bound_open(&self) -> bool {
        !self.is_upper_bound_closed()
    }
}
//------------------------------------------------------------------------------------------------

//------------------------------------------------------------------------------------------------
/// Comprehensive error types for interval construction failures.
///
/// The [`ErrorsIntervalConstruction`] enum represents all possible errors that can occur
/// when attempting to construct intervals with finite bounds. This enum provides detailed
/// error information including the problematic values and automatic backtrace capture
/// for debugging purposes.
///
/// ## Design Philosophy
///
/// The error enum is designed to:
/// - **Provide Context**: Include the exact values that caused the construction failure
/// - **Enable Debugging**: Automatically capture backtraces for error diagnosis  
/// - **Maintain Type Safety**: Work generically with any `RealScalar` type
/// - **Support Validation**: Allow upstream code to validate inputs before construction
///
/// ## Generic Over Any [`num_valid::RealScalar`] Type
///
/// **The [`ErrorsIntervalConstruction`] enum works seamlessly with any scalar type implementing [`num_valid::RealScalar`]:**
///
/// | Scalar Type | Error Display | Debugging | Best For |
/// |-------------|---------------|-----------|----------|
/// | [`f64`] | ⚡⚡⚡ Fast | ✅ Full backtraces | Quick prototyping |
/// | [`RealNative64StrictFiniteInDebug`](num_valid::RealNative64StrictFiniteInDebug) | ⚡⚡⚡ **Same as f64** | ✅ **Full backtraces** | **Recommended for most uses** |
/// | [`RealNative64StrictFinite`](num_valid::RealNative64StrictFinite) | ⚡⚡ Small overhead | ✅ Full backtraces | Production systems |
/// | `RealRugStrictFinite` | ⚡ Precision-dependent | ✅ Full backtraces | Arbitrary precision needs (available from the [num-valid](https://crates.io/crates/num-valid) crate when compiled with `--features=rug`)|
///
/// ## Error Variants
///
/// ### [`ErrorsIntervalConstruction::LowerBoundGreaterOrEqualThanUpperBound`]
/// **The fundamental interval validity error**
///
/// This error occurs when attempting to construct an interval where the lower bound
/// is greater than or equal to the upper bound, violating the basic requirement that
/// intervals must have positive length (lower_bound < upper_bound).
///
/// #### When This Error Occurs
/// - **Inverted bounds**: `IntervalClosed::try_new(5.0, 3.0)` → lower > upper
/// - **Equal bounds**: `IntervalOpen::try_new(2.0, 2.0)` → zero length interval
///
/// #### Error Information Provided
/// - **`lower_bound`**: The lower bound value that was provided
/// - **`upper_bound`**: The upper bound value that was provided  
/// - **`backtrace`**: Automatic capture of the call stack for debugging
///
/// ## Usage Examples
///
/// ### Basic Error Handling
/// ```rust
/// use grid1d::intervals::*;
/// use num_valid::RealNative64StrictFiniteInDebug;
/// use try_create::TryNew;
///
/// // Use the recommended performance-optimal type
/// type Real = RealNative64StrictFiniteInDebug;
///
/// // Attempt to create an invalid interval
/// let result = IntervalClosed::try_new(
///     Real::try_new(10.0).unwrap(),
///     Real::try_new(5.0).unwrap()
/// );
///
/// match result {
///     Ok(interval) => {
///         println!("Successfully created interval: {:?}", interval);
///     }
///     Err(error) => {
///         println!("Failed to create interval: {}", error);
///         // Error message: "The lower bound (10) must be < than the upper bound (5)."
///     }
/// }
/// ```
///
/// ### Detailed Error Analysis
/// ```rust
/// use grid1d::intervals::*;
/// use std::error::Error;
///
/// fn analyze_construction_error(lower: f64, upper: f64) {
///     match IntervalClosed::try_new(lower, upper) {
///         Ok(_) => println!("Interval [{}, {}] created successfully", lower, upper),
///         Err(error) => {
///             match error {
///                 ErrorsIntervalConstruction::LowerBoundGreaterOrEqualThanUpperBound {
///                     lower_bound,
///                     upper_bound,
///                     backtrace: _,
///                 } => {
///                     println!("Invalid interval bounds detected:");
///                     println!("  Lower bound: {}", lower_bound);
///                     println!("  Upper bound: {}", upper_bound);
///                     println!("  Problem: Lower bound must be strictly less than upper bound");
///                     
///                     if lower_bound == upper_bound {
///                         println!("  Note: Zero-length intervals are not supported");
///                         println!("        Consider using IntervalSingleton for single points");
///                     } else {
///                         println!("  Note: Bounds appear to be inverted");
///                         println!("        Consider swapping the values");
///                     }
///                 }
///             }
///             
///             // Access the full error chain
///             let mut source = error.source();
///             while let Some(err) = source {
///                 println!("Caused by: {}", err);
///                 source = err.source();
///             }
///         }
///     }
/// }
///
/// // Test various invalid scenarios
/// analyze_construction_error(5.0, 3.0);   // Inverted bounds
/// analyze_construction_error(2.0, 2.0);   // Equal bounds (zero length)
/// analyze_construction_error(1.0, 0.9);   // Small inversion
/// ```
///
/// ### Error Recovery and Validation
/// ```rust
/// use grid1d::intervals::*;
/// use std::cmp::Ordering;
///
/// /// Safely create an interval with automatic bound correction
/// fn create_interval_with_correction(
///     bound1: f64,
///     bound2: f64
/// ) -> Result<IntervalClosed<f64>, String> {
///     // First, try to create interval as provided
///     match IntervalClosed::try_new(bound1, bound2) {
///         Ok(interval) => Ok(interval),
///         Err(ErrorsIntervalConstruction::LowerBoundGreaterOrEqualThanUpperBound {
///             lower_bound,
///             upper_bound,
///             ..
///         }) => {
///             match lower_bound.partial_cmp(&upper_bound).unwrap() {
///                 Ordering::Greater => {
///                     // Bounds are inverted - swap them
///                     println!("Swapping inverted bounds: {} <-> {}", lower_bound, upper_bound);
///                     IntervalClosed::try_new(upper_bound, lower_bound)
///                         .map_err(|e| format!("Even after swapping: {}", e))
///                 }
///                 Ordering::Equal => {
///                     // Zero-length interval - cannot be corrected
///                     Err(format!(
///                         "Cannot create positive-length interval: both bounds are {} \
///                          (consider using IntervalSingleton for single points)",
///                         lower_bound
///                     ))
///                 }
///                 Ordering::Less => {
///                     // This shouldn't happen, but handle it
///                     Err(format!("Unexpected error condition: {} < {} but construction failed",
///                                lower_bound, upper_bound))
///                 }
///             }
///         }
///     }
/// }
///
/// // Test the correction function
/// assert!(create_interval_with_correction(0.0, 1.0).is_ok());   // Valid input
/// assert!(create_interval_with_correction(1.0, 0.0).is_ok());   // Auto-corrected
/// assert!(create_interval_with_correction(5.0, 5.0).is_err());  // Cannot correct
///
/// println!("{:?}", create_interval_with_correction(10.0, 2.0)); // Shows corrected interval
/// ```
///
/// ### Integration with Result Chains
/// ```rust
/// use grid1d::intervals::*;
/// use std::fmt;
///
/// /// Custom error type that can wrap interval construction errors
/// #[derive(Debug)]
/// enum DomainError {
///     InvalidInterval(ErrorsIntervalConstruction<f64>),
///     InvalidInput(String),
///     ComputationError(String),
/// }
///
/// impl fmt::Display for DomainError {
///     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
///         match self {
///             DomainError::InvalidInterval(err) => write!(f, "Domain interval error: {}", err),
///             DomainError::InvalidInput(msg) => write!(f, "Invalid input: {}", msg),
///             DomainError::ComputationError(msg) => write!(f, "Computation error: {}", msg),
///         }
///     }
/// }
///
/// impl std::error::Error for DomainError {
///     fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
///         match self {
///             DomainError::InvalidInterval(err) => Some(err),
///             _ => None,
///         }
///     }
/// }
///
/// impl From<ErrorsIntervalConstruction<f64>> for DomainError {
///     fn from(err: ErrorsIntervalConstruction<f64>) -> Self {
///         DomainError::InvalidInterval(err)
///     }
/// }
///
/// /// Function that uses intervals in a larger computation
/// fn process_data_over_domain(
///     data: &[f64],
///     domain_start: f64,
///     domain_end: f64
/// ) -> Result<f64, DomainError> {
///     if data.is_empty() {
///         return Err(DomainError::InvalidInput("Data cannot be empty".to_string()));
///     }
///     
///     // This automatically converts ErrorsIntervalConstruction to DomainError
///     let domain = IntervalClosed::try_new(domain_start, domain_end)?;
///     
///     // Filter data points within domain
///     let filtered_data: Vec<f64> = data.iter()
///         .filter(|&&x| domain.contains_point(&x))
///         .cloned()
///         .collect();
///     
///     if filtered_data.is_empty() {
///         return Err(DomainError::ComputationError(
///             "No data points fall within the specified domain".to_string()
///         ));
///     }
///     
///     // Compute average
///     Ok(filtered_data.iter().sum::<f64>() / filtered_data.len() as f64)
/// }
///
/// // Usage examples
/// let data = vec![1.0, 2.0, 3.0, 4.0, 5.0];
///
/// match process_data_over_domain(&data, 2.0, 4.0) {
///     Ok(average) => println!("Average over domain [2, 4]: {}", average),
///     Err(error) => {
///         println!("Processing failed: {}", error);
///         
///         // Handle specific error types
///         match error {
///             DomainError::InvalidInterval(_) => {
///                 println!("The domain specification was invalid");
///             }
///             DomainError::InvalidInput(_) => {
///                 println!("The input data was invalid");
///             }
///             DomainError::ComputationError(_) => {
///                 println!("The computation could not be completed");
///             }
///         }
///     }
/// }
/// ```
///
/// ## Backtrace Support and Debugging
///
/// ### Automatic Backtrace Capture
/// ```rust
/// use grid1d::intervals::*;
/// use std::backtrace::Backtrace;
///
/// /// Demonstrate backtrace functionality
/// fn demonstrate_backtrace_capture() {
///     let result = IntervalClosed::try_new(10.0, 5.0);
///     
///     if let Err(ErrorsIntervalConstruction::LowerBoundGreaterOrEqualThanUpperBound {
///         lower_bound,
///         upper_bound,
///         backtrace,
///     }) = result {
///         println!("Construction failed:");
///         println!("  Lower: {}", lower_bound);
///         println!("  Upper: {}", upper_bound);
///         println!("  Backtrace: {}", backtrace);
///     }
/// }
///
/// // The backtrace will show the complete call stack leading to the error,
/// // making it easy to trace back through complex computation chains
/// ```
///
/// ### Debug Mode vs Release Mode Behavior
/// ```rust
/// use grid1d::intervals::*;
///
/// /// Show different behavior in debug vs release modes
/// fn show_debug_vs_release_behavior() {
///     // In debug mode: try_new returns Err, new() panics
///     #[cfg(debug_assertions)]
///     {
///         // This returns an error with full backtrace
///         let result = IntervalClosed::try_new(5.0, 3.0);
///         assert!(result.is_err());
///         
///         // This would panic with detailed message and backtrace
///         // let interval = IntervalClosed::new(5.0, 3.0); // PANIC!
///     }
///     
///     // In release mode: try_new returns Err, new() constructs without validation
///     #[cfg(not(debug_assertions))]
///     {
///         // This still returns an error
///         let result = IntervalClosed::try_new(5.0, 3.0);
///         assert!(result.is_err());
///         
///         // This constructs an invalid interval (use with extreme caution!)
///         let interval = IntervalClosed::new(5.0, 3.0);
///         // The interval may behave unexpectedly in subsequent operations
///     }
/// }
/// ```
///
/// ## Performance Characteristics
///
/// ### Error Construction Overhead
/// | Component | Cost | Notes |
/// |-----------|------|-------|
/// | **Value storage** | O(1) | Direct copy of scalar values |
/// | **Backtrace capture** | O(call stack depth) | Automatic on error construction |
/// | **Error formatting** | O(1) | Lazy evaluation via Display trait |
/// | **Memory usage** | Small | Two scalars + backtrace |
///
/// ### Backtrace Performance
/// ```rust
/// use grid1d::intervals::*;
/// use std::time::Instant;
///
/// /// Measure the overhead of error construction with backtraces
/// fn measure_error_overhead() {
///     let start = Instant::now();
///     
///     // Measure cost of successful construction
///     for _ in 0..10000 {
///         let _ = IntervalClosed::try_new(0.0, 1.0).unwrap();
///     }
///     let success_time = start.elapsed();
///     
///     let start = Instant::now();
///     
///     // Measure cost of failed construction (includes backtrace capture)
///     for _ in 0..10000 {
///         let _ = IntervalClosed::try_new(1.0, 0.0).unwrap_err();
///     }
///     let error_time = start.elapsed();
///     
///     println!("Success time: {:?}", success_time);
///     println!("Error time: {:?}", error_time);
///     println!("Error overhead: {:?}", error_time - success_time);
///     
///     // Note: Error construction is significantly more expensive due to backtrace capture
///     // This is intentional for debugging purposes
/// }
/// ```
///
/// ## Error Message Formatting
///
/// ### Default Display Implementation
/// The `Display` implementation provides human-readable error messages:
///
/// ```text
/// The lower bound (10) must be < than the upper bound (5).
/// ```
///
/// ### Debug Implementation
/// The `Debug` implementation shows the full error structure:
///
/// ```rust
/// use grid1d::intervals::*;
///
/// let error = IntervalClosed::try_new(10.0, 5.0).unwrap_err();
/// println!("{:#?}", error);
/// // Output shows:
/// // ErrorsIntervalConstruction::LowerBoundGreaterOrEqualThanUpperBound {
/// //     lower_bound: 10.0,
/// //     upper_bound: 5.0,
/// //     backtrace: Backtrace { ... }
/// // }
/// ```
///
/// ## Integration with Error Handling Libraries
///
/// ### With `thiserror`
/// ```rust
/// use grid1d::intervals::*;
/// use thiserror::Error;
///
/// #[derive(Error, Debug)]
/// enum ProcessingError {
///     #[error("Domain specification error")]
///     Domain(#[from] ErrorsIntervalConstruction<f64>),
///     
///     #[error("Computation failed: {message}")]
///     Computation { message: String },
/// }
///
/// fn process_with_thiserror(lower: f64, upper: f64) -> Result<f64, ProcessingError> {
///     let interval = IntervalClosed::try_new(lower, upper)?; // Auto-conversion
///     
///     if interval.length().into_inner() > 1000.0 {
///         return Err(ProcessingError::Computation {
///             message: "Domain too large for processing".to_string(),
///         });
///     }
///     
///     Ok(interval.mid_point())
/// }
/// ```
///
/// ## Common Usage Patterns
///
/// ### Pre-validation Pattern
/// ```rust
/// use grid1d::intervals::*;
/// use std::cmp::Ordering;
///
/// /// Validate bounds before attempting construction
/// fn validated_interval_construction(
///     lower: f64,
///     upper: f64
/// ) -> Result<IntervalClosed<f64>, String> {
///     // Pre-validate to provide custom error messages
///     if !lower.is_finite() {
///         return Err(format!("Lower bound {} is not finite", lower));
///     }
///     if !upper.is_finite() {
///         return Err(format!("Upper bound {} is not finite", upper));
///     }
///     
///     match lower.partial_cmp(&upper) {
///         Some(Ordering::Less) => {
///             // Valid bounds - construction should succeed
///             IntervalClosed::try_new(lower, upper)
///                 .map_err(|e| format!("Unexpected construction failure: {}", e))
///         }
///         Some(Ordering::Equal) => {
///             Err(format!(
///                 "Cannot create positive-length interval: both bounds are {} \
///                  (use IntervalSingleton for single points)",
///                 lower
///             ))
///         }
///         Some(Ordering::Greater) => {
///             Err(format!(
///                 "Lower bound {} must be less than upper bound {}",
///                 lower, upper
///             ))
///         }
///         None => {
///             Err(format!(
///                 "Cannot compare bounds: lower={}, upper={} (contains NaN?)",
///                 lower, upper
///             ))
///         }
///     }
/// }
/// ```
///
/// ### Fallback Construction Pattern
/// ```rust
/// use grid1d::intervals::*;
/// use try_create::New;
///
/// /// Attempt interval construction with multiple fallback strategies
/// fn robust_interval_construction(
///     lower: f64,
///     upper: f64
/// ) -> Result<Interval<f64>, String> {
///     // Strategy 1: Try direct construction
///     if let Ok(interval) = IntervalClosed::try_new(lower, upper) {
///         return Ok(interval.into());
///     }
///     
///     // Strategy 2: Try with swapped bounds
///     if let Ok(interval) = IntervalClosed::try_new(upper, lower) {
///         println!("Warning: Swapped bounds to create [{}, {}]", upper, lower);
///         return Ok(interval.into());
///     }
///     
///     // Strategy 3: Check for singleton case
///     if (lower - upper).abs() < f64::EPSILON {
///         println!("Warning: Creating singleton at {} instead of zero-length interval", lower);
///         return Ok(IntervalSingleton::new(lower).into());
///     }
///     
///     // Strategy 4: Create minimal valid interval
///     if lower.is_finite() && upper.is_finite() {
///         let center = (lower + upper) / 2.0;
///         let epsilon = f64::EPSILON * center.abs().max(1.0);
///         println!("Warning: Creating minimal interval around {}", center);
///         return Ok(IntervalClosed::new(center - epsilon, center + epsilon).into());
///     }
///     
///     Err(format!("Cannot create any valid interval from bounds: {}, {}", lower, upper))
/// }
/// ```
///
/// ## Best Practices and Recommendations
///
/// ### When to Use `try_new` vs `new`
/// - **Use `try_new`**: When input validation is needed or bounds come from external sources
/// - **Use `new`**: Only when bounds are guaranteed valid (e.g., from prior successful computations)
/// - **Debug safety**: `new` panics in debug mode with invalid inputs, constructs unchecked in release
///
/// ### Error Handling Strategies
/// ```rust
/// use grid1d::intervals::*;
///
/// // GOOD: Handle errors explicitly
/// fn good_error_handling(lower: f64, upper: f64) -> Option<IntervalClosed<f64>> {
///     match IntervalClosed::try_new(lower, upper) {
///         Ok(interval) => Some(interval),
///         Err(error) => {
///             eprintln!("Cannot create interval [{}, {}]: {}", lower, upper, error);
///             None
///         }
///     }
/// }
///
/// // BAD: Ignore error details
/// fn bad_error_handling(lower: f64, upper: f64) -> Option<IntervalClosed<f64>> {
///     IntervalClosed::try_new(lower, upper).ok() // Loses error context
/// }
///
/// // BETTER: Propagate with context
/// fn better_error_handling(lower: f64, upper: f64) -> Result<IntervalClosed<f64>, String> {
///     IntervalClosed::try_new(lower, upper)
///         .map_err(|e| format!("Domain [{}, {}] is invalid: {}", lower, upper, e))
/// }
/// ```
///
/// ## Mathematical Correctness and Validation
///
/// The [`ErrorsIntervalConstruction`] enum ensures mathematical correctness by:
///
/// - **Enforcing positive length**: Prevents creation of invalid intervals where `lower ≥ upper`
/// - **Maintaining invariants**: Ensures all successfully constructed intervals satisfy `lower < upper`
/// - **Providing diagnostics**: Includes exact values that caused failures for debugging
/// - **Supporting debugging**: Automatic backtrace capture helps trace complex error chains
/// - **Type safety**: Works generically with any `RealScalar` type while preserving validation
///
/// These guarantees ensure that all intervals created through the safe construction methods
/// (`try_new`) are mathematically valid and suitable for subsequent interval operations.
#[derive(Debug, Error)]
pub enum ErrorsIntervalConstruction<RealType: RealScalar> {
    /// This error occurs when attempting to construct an interval where the lower bound
    /// is greater than or equal to the upper bound, violating the basic requirement that
    /// intervals must have positive length (lower_bound < upper_bound).
    ///
    /// #### When This Error Occurs
    /// - **Inverted bounds**: `IntervalClosed::try_new(5.0, 3.0)` → lower > upper
    /// - **Equal bounds**: `IntervalOpen::try_new(2.0, 2.0)` → zero length interval
    ///
    /// #### Error Information Provided
    /// - **`lower_bound`**: The lower bound value that was provided
    /// - **`upper_bound`**: The upper bound value that was provided  
    /// - **`backtrace`**: Automatic capture of the call stack for debugging
    #[error("The lower bound ({lower_bound}) must be < than the upper bound ({upper_bound}).")]
    LowerBoundGreaterOrEqualThanUpperBound {
        /// The lower bound value that was provided.
        lower_bound: RealType,

        /// The upper bound value that was provided.
        upper_bound: RealType,

        /// Automatic capture of the call stack for debugging
        backtrace: Backtrace,
    },
}
//------------------------------------------------------------------------------------------------

//--------------------------------------------------------------------------------------------------------------
///  Universal container for all interval types in the library.
///
/// The [`Interval`] enum serves as the top-level abstraction that can represent any interval
/// type supported by this library. It acts as a unified interface for interval operations
/// while maintaining type safety and mathematical correctness across all interval categories.
///
/// ## Design Philosophy
///
/// The [`Interval`] enum represents the apex of the library's four-tier hierarchy:
/// 1. **Bound Types** → 2. **Concrete Intervals** → 3. **Interval Categories** → 4. **[`Interval`] (Universal Interface)**
///
/// This design provides:
/// - **Maximum Flexibility**: Work with any interval type through a single interface
/// - **Type Preservation**: Convert back to specific types when needed
/// - **Mathematical Completeness**: Support all interval operations and combinations
/// - **Performance**: Zero-cost abstractions that compile to efficient code
///
/// ## Generic Over Any `RealScalar` Type
///
/// **The [`Interval`] enum works seamlessly with any scalar type implementing [`num_valid::RealScalar`]:**
///
/// | Scalar Type | Performance | Validation | Best For |
/// |-------------|-------------|------------|----------|
/// | [`f64`] | ⚡⚡⚡ Maximum | ❌ None | Trusted input, maximum speed |
/// | [`RealNative64StrictFiniteInDebug`](num_valid::RealNative64StrictFiniteInDebug) | ⚡⚡⚡ **Same as f64** | ✅ **Debug only** | **Recommended for most uses** |
/// | [`RealNative64StrictFinite`](num_valid::RealNative64StrictFinite) | ⚡⚡ Small overhead | ✅ Always | Safety-critical applications |
/// | `RealRugStrictFinite` | ⚡ Precision-dependent | ✅ Always | Arbitrary precision needs (available from the [num-valid](https://crates.io/crates/num-valid) crate when compiled with `--features=rug`) |
///
/// ## Interval Categories
///
/// The enum organizes intervals into two fundamental categories based on their mathematical properties:
///
/// ### [`Interval::FiniteLength`] - Bounded Intervals
/// Contains all intervals with finite extent:
/// - **Positive Length**: `[a,b]`, `(a,b)`, `[a,b)`, `(a,b]` where `a < b`
/// - **Zero Length**: `[a]` (singleton intervals)
///
/// ### [`Interval::InfiniteLength`] - Unbounded Intervals  
/// Contains all intervals with infinite extent:
/// - **Fully Unbounded**: `(-∞, +∞)`
/// - **Semi-bounded**: `[a, +∞)`, `(a, +∞)`, `(-∞, b]`, `(-∞, b)`
///
/// ## Construction Patterns
///
/// ### From Concrete Interval Types (Recommended)
/// ```rust
/// use grid1d::intervals::*;
/// use num_valid::RealNative64StrictFiniteInDebug;
/// use try_create::TryNew;
///
/// // Use the recommended performance-optimal type
/// type Real = RealNative64StrictFiniteInDebug;
///
/// // Create specific interval types, then convert
/// let closed = IntervalClosed::new(
///     Real::try_new(0.0).unwrap(),
///     Real::try_new(1.0).unwrap()
/// );
/// let interval: Interval<Real> = closed.into();
///
/// let unbounded = IntervalLowerUnboundedUpperUnbounded::<Real>::new();
/// let interval: Interval<Real> = unbounded.into();
/// ```
///
/// ### Direct Construction (Advanced)
/// ```rust
/// use grid1d::intervals::*;
/// use num::{Zero, One};
/// use num_valid::{RealNative64StrictFinite};
///
/// // use the safe real type
/// type Real = RealNative64StrictFinite;
///
/// // Build from category enums
/// let finite = IntervalFiniteLength::PositiveLength(
///     IntervalFinitePositiveLength::Closed(
///         IntervalClosed::new(Real::zero(), Real::one())
///     )
/// );
/// let interval = Interval::FiniteLength(finite);
///
/// let infinite = IntervalInfiniteLength::LowerUnboundedUpperUnbounded(
///     IntervalLowerUnboundedUpperUnbounded::<Real>::new());
/// let interval = Interval::InfiniteLength(infinite);
/// ```
///
/// ## Core Operations
///
/// All [`Interval`] instances support the complete [`IntervalTrait`] interface:
///
/// ### Point Containment Testing
/// ```rust
/// use grid1d::intervals::*;
///
/// let interval = Interval::from(IntervalClosed::new(-1.0, 1.0));
///
/// assert!(interval.contains_point(&0.0));    // Interior point
/// assert!(interval.contains_point(&-1.0));   // Boundary (included)
/// assert!(interval.contains_point(&1.0));    // Boundary (included)
/// assert!(!interval.contains_point(&2.0));   // Outside interval
/// ```
///
/// ### Interval Containment Testing
/// ```rust
/// use grid1d::intervals::*;
///
/// let outer = Interval::from(IntervalClosed::new(0.0, 2.0));   // [0, 2]
/// let inner = Interval::from(IntervalOpen::new(0.5, 1.5));     // (0.5, 1.5)
///
/// assert!(outer.contains_interval(&inner));   // [0, 2] contains (0.5, 1.5)
/// assert!(!inner.contains_interval(&outer));  // (0.5, 1.5) doesn't contain [0, 2]
/// ```
///
/// ### Intersection Operations
/// ```rust
/// use grid1d::intervals::*;
///
/// let a = Interval::from(IntervalClosed::new(0.0, 2.0));
/// let b = Interval::from(IntervalClosed::new(1.0, 3.0));
///
/// if let Some(intersection) = a.intersection(&b) {
///     // Result: [1.0, 2.0] as an Interval enum
///     println!("Intersection found: {:?}", intersection);
/// }
///
/// // Test for disjoint intervals
/// let c = Interval::from(IntervalClosed::new(5.0, 6.0));
/// assert!(a.intersection(&c).is_none());  // No overlap
/// ```
///
/// ## Type Conversion and Recovery
///
/// ### Converting Back to Specific Types
/// ```rust
/// use grid1d::intervals::*;
///
/// let interval = Interval::from(IntervalClosed::new(0.0, 1.0));
///
/// // Try to recover the original type
/// match IntervalClosed::try_from(interval.clone()) {
///     Ok(closed) => println!("Recovered closed interval: {:?}", closed),
///     Err(e) => println!("Not a closed interval: {}", e),
/// }
///
/// // Convert to category types
/// match IntervalFinitePositiveLength::try_from(interval.clone()) {
///     Ok(finite) => println!("Finite positive length interval"),
///     Err(e) => println!("Not finite positive length: {}", e),
/// }
/// ```
///
/// ### Pattern Matching for Type Inspection
/// ```rust
/// use grid1d::intervals::*;
///
/// fn analyze_interval(interval: &Interval<f64>) -> String {
///     match interval {
///         Interval::FiniteLength(finite) => match finite {
///             IntervalFiniteLength::PositiveLength(positive) => {
///                 format!("Finite interval with length: {}", positive.length())
///             }
///             IntervalFiniteLength::ZeroLength(singleton) => {
///                 format!("Singleton at point: {}", singleton.value())
///             }
///         },
///         Interval::InfiniteLength(infinite) => match infinite {
///             IntervalInfiniteLength::LowerUnboundedUpperUnbounded(_) =>
///                 "Fully unbounded interval (-∞, +∞)".to_string(),
///             IntervalInfiniteLength::LowerClosedUpperUnbounded(semi) =>
///                 format!("Semi-bounded [{}, +∞)", semi.lower_bound_value()),
///             IntervalInfiniteLength::LowerOpenUpperUnbounded(semi) =>
///                 format!("Semi-bounded ({}, +∞)", semi.lower_bound_value()),
///             IntervalInfiniteLength::LowerUnboundedUpperClosed(semi) =>
///                 format!("Semi-bounded (-∞, {}]", semi.upper_bound_value()),
///             IntervalInfiniteLength::LowerUnboundedUpperOpen(semi) =>
///                 format!("Semi-bounded (-∞, {})", semi.upper_bound_value()),
///         }
///     }
/// }
/// ```
///
/// ## Generic Programming with Intervals
///
/// ### Universal Interval Functions
/// ```rust
/// use grid1d::intervals::*;
/// use num_valid::RealScalar;
///
/// /// Count points with integer coordinate in an interval (works with any interval type)
/// fn count_integer_points<T>(interval: &Interval<T>) -> usize
/// where
///     T: RealScalar,
/// {
///     // Implementation works regardless of interval type
///     let mut count = 0;
///     for i in -100..=100 {
///         if interval.contains_point(&T::try_from_f64(i as f64).unwrap()) {
///             count += 1;
///         }
///     }
///     count
/// }
///
/// // Works with any interval type or scalar type
/// let closed = Interval::from(IntervalClosed::new(-2.0, 2.0));
/// let open = Interval::from(IntervalOpen::new(-2.0, 2.0));
/// let unbounded = Interval::from(IntervalLowerUnboundedUpperUnbounded::<f64>::new());
///
/// assert_eq!(count_integer_points(&closed), 5);   // -2, -1, 0, 1, 2
/// assert_eq!(count_integer_points(&open), 3);     // -1, 0, 1
/// assert_eq!(count_integer_points(&unbounded), 201); // All integers in range
/// ```
///
/// ### Cross-Type Interval Operations
/// ```rust
/// use grid1d::intervals::*;
/// use num_valid::RealScalar;
/// use try_create::New;
///
/// /// Find intersection of any two intervals
/// fn safe_intersect<RealType, I1, I2>(a: I1, b: I2) -> Option<Interval<RealType>>
/// where
///     RealType: RealScalar,
///     I1: Into<Interval<RealType>>,
///     I2: Into<Interval<RealType>>,
/// {
///     let interval_a = a.into();
///     let interval_b = b.into();
///     interval_a.intersection(&interval_b)
/// }
///
/// // Mix different concrete types seamlessly
/// let closed = IntervalClosed::new(0.0, 2.0);
/// let open = IntervalOpen::new(1.0, 3.0);
/// let unbounded = IntervalLowerClosedUpperUnbounded::new(1.5);
///
/// let intersection1 = safe_intersect(closed, open.clone()).unwrap(); // (1.0, 2.0]
/// let expected_intersection1 = IntervalLowerOpenUpperClosed::new(1., 2.);
/// assert!(matches!(intersection1, Interval::FiniteLength(
///     IntervalFiniteLength::PositiveLength(
///         IntervalFinitePositiveLength::LowerOpenUpperClosed{..}))));
/// assert_eq!(expected_intersection1, intersection1.try_into().unwrap());
///
/// let intersection2 = safe_intersect(open, unbounded).unwrap(); // [1.5, 3.0)
/// let expected_intersection2 = IntervalLowerClosedUpperOpen::new(1.5, 3.);
/// assert!(matches!(intersection2, Interval::FiniteLength(
///     IntervalFiniteLength::PositiveLength(
///         IntervalFinitePositiveLength::LowerClosedUpperOpen{..}))));
/// assert_eq!(expected_intersection2, intersection2.try_into().unwrap());
/// ```
///
/// ## Performance Characteristics
///
/// ### Memory Layout
/// - **Size**: Typically 24-32 bytes (depends on scalar type and interval category)
/// - **Alignment**: Natural alignment of the scalar type
/// - **Stack Allocation**: No heap allocation for the enum itself
///
/// ### Operation Complexity
/// | Operation | Time Complexity | Notes |
/// |-----------|----------------|-------|
/// | **Point queries** | O(1) | At most 2 comparisons |
/// | **Containment tests** | O(1) | Boundary logic only |
/// | **Intersections** | O(1) | Pure computation, may allocate result |
/// | **Conversions** | O(1) | Zero-cost type transformations |
/// | **Pattern matching** | O(1) | Compile-time optimized |
///
/// ### Scalar Type Performance Impact
/// ```rust
/// use grid1d::intervals::*;
/// use num_valid::{RealNative64StrictFiniteInDebug, RealNative64StrictFinite, RealScalar};
///
/// // These have identical performance in release builds:
/// type FastInterval = Interval<f64>;  // Raw f64
/// type OptimalInterval = Interval<RealNative64StrictFiniteInDebug>;  // Validated in debug only
///
/// // This has small validation overhead:
/// type SafeInterval = Interval<RealNative64StrictFinite>;  // Always validated
///
/// // Performance test function (generic over scalar type)
/// fn benchmark_operations<T: RealScalar + Clone>(
///     interval1: &Interval<T>,
///     interval2: &Interval<T>
/// ) {
///     // All operations compile to identical assembly for f64 and RealNative64StrictFiniteInDebug
///     let _contains = interval1.contains_interval(interval2);
///     let _intersection = interval1.intersection(interval2);
///     // ... other operations
/// }
/// ```
///
/// ## Error Handling and Edge Cases
///
/// ### Conversion Errors
/// ```rust
/// use grid1d::intervals::*;
/// use try_create::New;
///
/// let singleton = Interval::from(IntervalSingleton::new(42.0));
///
/// // This will fail because singleton is not a positive-length interval
/// match IntervalFinitePositiveLength::try_from(singleton) {
///     Ok(_) => unreachable!(),
///     Err(e) => println!("Expected error: {}", e),  // "not a positive length interval"
/// }
/// ```
///
/// ### Mathematical Edge Cases
/// ```rust
/// use grid1d::intervals::*;
///
/// // Intersection at single point creates singleton
/// let a = Interval::from(IntervalClosed::new(0.0, 1.0));
/// let b = Interval::from(IntervalClosed::new(1.0, 2.0));
///
/// if let Some(intersection) = a.intersection(&b) {
///     // Result is a singleton interval at point 1.0
///     match intersection {
///         Interval::FiniteLength(IntervalFiniteLength::ZeroLength(singleton)) => {
///             assert_eq!(singleton.value(), &1.0);
///         }
///         _ => panic!("Expected singleton intersection"),
///     }
/// }
///
/// // Disjoint intervals have no intersection
/// let c = Interval::from(IntervalOpen::new(0.0, 1.0));
/// let d = Interval::from(IntervalOpen::new(1.0, 2.0));
/// assert!(c.intersection(&d).is_none());
/// ```
///
/// ## Integration with the Library Ecosystem
///
/// ### With Integration Algorithms
/// ```rust
/// use grid1d::intervals::*;
///
/// // Integration functions accept any interval through this enum
/// fn integrate_over_any_interval(
///     f: impl Fn(f64) -> f64,
///     domain: Interval<f64>
/// ) -> Result<f64, &'static str> {
///     match domain {
///         Interval::FiniteLength(finite) => {
///             match finite {
///                 IntervalFiniteLength::PositiveLength(positive) => {
///                     // Use standard integration techniques
///                     Ok(0.0) // placeholder
///                 }
///                 IntervalFiniteLength::ZeroLength(_) => Ok(0.0), // Zero integral
///             }
///         }
///         Interval::InfiniteLength(_) => {
///             Err("Infinite intervals require special handling")
///         }
///     }
/// }
/// ```
///
/// ### With Function Evaluation
/// ```rust
/// use grid1d::intervals::*;
///
/// // Function domains can be any interval type
/// struct MathFunction {
///     domain: Interval<f64>,
///     // ... function definition
/// }
///
/// impl MathFunction {
///     fn evaluate(&self, x: f64) -> Option<f64> {
///         if self.domain.contains_point(&x) {
///             Some(x * x)  // Example function: f(x) = x²
///         } else {
///             None  // Out of domain
///         }
///     }
/// }
/// ```
///
/// ## Best Practices and Recommendations
///
/// ### When to Use [`Interval`]
/// - **API Boundaries**: Accept any interval type in public APIs
/// - **Storage**: Store intervals when the specific type may vary
/// - **Algorithm Results**: Return intervals when the result type depends on input
/// - **Generic Programming**: Write interval algorithms that work with any type
///
/// ### When to Use Specific Types
/// - **Performance Critical Code**: Use concrete types like [`IntervalClosed`] when type is known
/// - **Type Safety**: Use specific types to enforce interval properties at compile time
/// - **Domain Modeling**: Use concrete types when the interval type has semantic meaning
///
/// ### Recommended Patterns
/// ```rust
/// use grid1d::intervals::*;
/// use num_valid::RealNative64StrictFiniteInDebug;
/// use try_create::TryNew;
///
/// // Define type aliases for your application
/// type Real = RealNative64StrictFiniteInDebug;
/// type GeneralInterval = Interval<Real>;
/// type ClosedInterval = IntervalClosed<Real>;
///
/// // Accept general intervals in APIs, work with specific types internally
/// fn process_interval(input: impl Into<GeneralInterval>) -> Result<ClosedInterval, String> {
///     let general = input.into();
///     
///     // Try to convert to the specific type you need
///     ClosedInterval::try_from(general)
///         .map_err(|e| format!("Expected closed interval: {}", e))
/// }
///
/// // This works with any interval type
/// let closed = IntervalClosed::new(
///     Real::try_new(0.0).unwrap(),
///     Real::try_new(1.0).unwrap());
/// let result = process_interval(closed).unwrap();
/// ```
///
/// ## Mathematical Properties Preserved
///
/// The [`Interval`] enum maintains all mathematical properties across conversions:
/// - **Reflexivity**: `interval.contains_interval(&interval)` is always `true`
/// - **Transitivity**: If A ⊆ B and B ⊆ C, then A ⊆ C
/// - **Commutativity**: `a.intersection(&b) == b.intersection(&a)`
/// - **Associativity**: `(a ∩ b) ∩ c == a ∩ (b ∩ c)`
/// - **Monotonicity**: If A ⊆ B, then A ∩ C ⊆ B ∩ C
///
/// These properties are guaranteed by the underlying [`IntervalTrait`] implementation
/// and are preserved regardless of which concrete interval types are involved.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(bound(deserialize = "RealType: for<'a> Deserialize<'a>"))]
pub enum Interval<RealType: RealScalar> {
    /// An interval of ***finite length***.
    ///
    /// This variant encompasses all bounded intervals, including:
    /// - **Positive length intervals**: `[a,b]`, `(a,b)`, `[a,b)`, `(a,b]` where `a < b`
    /// - **Zero length intervals**: `[a]` (singleton intervals)
    ///
    /// These intervals have well-defined bounds and finite measure.
    FiniteLength(IntervalFiniteLength<RealType>),

    /// An interval of ***infinite length***.
    ///
    /// This variant encompasses all unbounded intervals, including:
    /// - **Fully unbounded**: `(-∞, +∞)` (the entire real line)
    /// - **Semi-bounded**: `[a, +∞)`, `(a, +∞)`, `(-∞, b]`, `(-∞, b)`
    ///
    /// These intervals extend infinitely in one or both directions.
    InfiniteLength(IntervalInfiniteLength<RealType>),
}

impl<RealType: RealScalar> Interval<RealType> {
    /// Constructs an [`Interval`] from optional runtime-typed bounds.
    ///
    /// This is the universal interval constructor that handles all possible bound combinations,
    /// automatically selecting the appropriate concrete interval type based on the bounds provided.
    /// It delegates to specialized constructors to ensure proper validation and type selection.
    ///
    /// # Arguments
    ///
    /// * `lower_bound` - Optional lower bound with runtime type information (open/closed)
    /// * `upper_bound` - Optional upper bound with runtime type information (open/closed)
    ///
    /// # Bound Combination Logic
    ///
    /// | Lower Bound | Upper Bound | Result |
    /// |-------------|-------------|--------|
    /// | `None` | `None` | `(-∞, +∞)` - Fully unbounded interval |
    /// | `Some(_)` | `None` | `[a, +∞)` or `(a, +∞)` - Lower bounded only |
    /// | `None` | `Some(_)` | `(-∞, b]` or `(-∞, b)` - Upper bounded only |
    /// | `Some(_)` | `Some(_)` | Bounded interval (delegates to `IntervalFiniteLength::try_new`) |
    ///
    /// # Returns
    ///
    /// * `Ok(Interval)` - Successfully constructed interval of the appropriate type
    /// * `Err(ErrorsIntervalConstruction)` - If bounds violate interval constraints (e.g., lower ≥ upper)
    ///
    /// # Examples
    ///
    /// ```ignore
    /// // Note: This is a private function used internally by the Interval implementation
    /// use grid1d::intervals::*;
    /// use grid1d::bounds::*;
    /// use try_create::New;
    ///
    /// // Fully unbounded
    /// let unbounded = Interval::try_new(None, None).unwrap();
    ///
    /// // Lower bounded with closed bound
    /// let lower_closed = LowerBoundClosed::new(0.0);
    /// let semi_bounded = Interval::try_new(Some(lower_closed.into()), None).unwrap();
    ///
    /// // Fully bounded
    /// let lower = LowerBoundClosed::new(0.0);
    /// let upper = UpperBoundOpen::new(1.0);
    /// let bounded = Interval::try_new(Some(lower.into()), Some(upper.into())).unwrap();
    /// ```
    ///
    /// # Design Rationale
    ///
    /// This function serves as the universal entry point for interval construction when bounds
    /// are only known at runtime. It ensures:
    /// - **Type safety**: Correct interval variant selected automatically
    /// - **Validation**: All constraints checked by delegated constructors
    /// - **Consistency**: Single source of truth for interval type selection
    pub fn try_new(
        lower_bound: Option<LowerBoundRuntime<RealType>>,
        upper_bound: Option<UpperBoundRuntime<RealType>>,
    ) -> Result<Self, ErrorsIntervalConstruction<RealType>> {
        match (lower_bound, upper_bound) {
            (None, None) => Ok(Interval::InfiniteLength(
                IntervalLowerUnboundedUpperUnbounded::new().into(),
            )),
            (Some(lower_bound), None) => {
                Ok(IntervalInfiniteLength::new_upper_unbounded(lower_bound).into())
            }
            (None, Some(upper_bound)) => {
                Ok(IntervalInfiniteLength::new_lower_unbounded(upper_bound).into())
            }
            (Some(lower_bound), Some(upper_bound)) => {
                Ok(IntervalFiniteLength::try_new(lower_bound, upper_bound)?.into())
            }
        }
    }
}

impl<RealType: RealScalar> IntervalOperations for Interval<RealType> {}

impl<RealType: RealScalar> IntervalBoundsRuntime for Interval<RealType> {
    type RealType = RealType;

    fn lower_bound_runtime(&self) -> Option<LowerBoundRuntime<RealType>> {
        match self {
            Interval::FiniteLength(interval) => interval.lower_bound_runtime(),
            Interval::InfiniteLength(interval) => interval.lower_bound_runtime(),
        }
    }

    fn upper_bound_runtime(&self) -> Option<UpperBoundRuntime<RealType>> {
        match self {
            Interval::FiniteLength(interval) => interval.upper_bound_runtime(),
            Interval::InfiniteLength(interval) => interval.upper_bound_runtime(),
        }
    }
}

impl<RealType: RealScalar> Contains for Interval<RealType> {
    /// Returns `true` if the 1D point/coordinate `x` is contained in `self`.
    #[inline(always)]
    fn contains_point(&self, x: &RealType) -> bool {
        match self {
            Interval::FiniteLength(interval) => interval.contains_point(x),
            Interval::InfiniteLength(interval) => interval.contains_point(x),
        }
    }
}

impl<RealType: RealScalar> IntervalTrait for Interval<RealType> {}

//------------------------------------------------------------------------------------------------

//------------------------------------------------------------------------------------------------
///  Comprehensive error handling for interval type conversion operations.
///
/// The [`ErrorsIntervalConversion`] enum provides detailed error information when attempting
/// to convert between different interval types fails. Each variant captures the specific
/// reason for conversion failure along with the problematic interval and a backtrace
/// for debugging purposes.
///
/// ## Design Philosophy
///
/// This error type follows the principle of **precise error reporting** - each variant
/// corresponds to a specific conversion failure mode, making it easy to:
/// - Understand exactly why a conversion failed
/// - Handle different error cases appropriately
/// - Debug conversion issues with full context
/// - Provide meaningful error messages to users
///
/// ## Generic Over Any [`num_valid::RealScalar`] Type
///
/// **The [`ErrorsIntervalConversion`] enum works with any scalar type implementing [`num_valid::RealScalar`]:**
///
/// | Scalar Type | Error Handling | Best For |
/// |-------------|----------------|----------|
/// | [`f64`] | Full error context | Production systems with raw f64 |
/// | [`RealNative64StrictFiniteInDebug`](num_valid::RealNative64StrictFiniteInDebug) | **Recommended** | Development and testing |
/// | [`RealNative64StrictFinite`](num_valid::RealNative64StrictFinite) | Validated errors | Safety-critical applications |
/// | `RealRugStrictFinite` | High-precision errors | Arbitrary precision computations (available from the [num-valid](https://crates.io/crates/num-valid) crate when compiled with `--features=rug`) |
///
/// ## Error Categories
///
/// The variants can be grouped into logical categories based on the type of conversion failure:
///
/// ### Length-Based Conversion Errors
/// - [`ErrorsIntervalConversion::NotIntervalPositiveLength`] - Attempted to convert to positive-length interval
/// - [`ErrorsIntervalConversion::NotIntervalInfiniteLength`] - Attempted to convert to infinite-length interval  
/// - [`ErrorsIntervalConversion::NotIntervalFiniteLength`] - Attempted to convert to finite-length interval
///
/// ### Boundary-Based Conversion Errors
/// - [`ErrorsIntervalConversion::NotIntervalClosed`] - Attempted to convert to closed interval `[a,b]`
/// - [`ErrorsIntervalConversion::NotIntervalOpen`] - Attempted to convert to open interval `(a,b)`
/// - [`ErrorsIntervalConversion::NotIntervalLowerOpenUpperClosed`] - Attempted to convert to `(a,b]`
/// - [`ErrorsIntervalConversion::NotIntervalLowerClosedUpperOpen`] - Attempted to convert to `[a,b)`
///
/// ### Boundedness-Based Conversion Errors
/// - [`ErrorsIntervalConversion::NotIntervalLowerUnboundedUpperClosed`] - Attempted to convert to `(-∞,b]`
/// - [`ErrorsIntervalConversion::NotIntervalLowerUnboundedUpperOpen`] - Attempted to convert to `(-∞,b)`
/// - [`ErrorsIntervalConversion::NotIntervalLowerClosedUpperUnbounded`] - Attempted to convert to `[a,+∞)`
/// - [`ErrorsIntervalConversion::NotIntervalLowerOpenUpperUnbounded`] - Attempted to convert to `(a,+∞)`
///
/// ### Special Case Conversion Errors
/// - [`ErrorsIntervalConversion::NotIntervalSingleton`] - Attempted to convert to singleton interval `[a]`
///
/// ## Usage Examples
///
/// ### Basic Error Handling
/// ```rust
/// use grid1d::intervals::*;
/// use try_create::New;
///
/// // Create a finite interval
/// let finite = Interval::from(IntervalClosed::new(0.0, 1.0));
///
/// // Try to convert to infinite length (will fail)
/// match IntervalInfiniteLength::try_from(finite) {
///     Ok(infinite) => {
///         println!("Conversion succeeded: {:?}", infinite);
///     }
///     Err(ErrorsIntervalConversion::NotIntervalInfiniteLength { interval, .. }) => {
///         println!("Cannot convert finite interval to infinite: {:?}", interval);
///     }
///     Err(other) => {
///         println!("Unexpected error: {}", other);
///     }
/// }
/// ```
///
/// ### Handling Specific Conversion Failures
/// ```rust
/// use grid1d::intervals::*;
/// use try_create::New;
///
/// fn safe_convert_to_closed(interval: Interval<f64>) -> Result<IntervalClosed<f64>, String> {
///     match IntervalClosed::try_from(interval) {
///         Ok(closed) => Ok(closed),
///         Err(ErrorsIntervalConversion::NotIntervalClosed { interval, .. }) => {
///             Err(format!("Expected closed interval, got: {:?}", interval))
///         }
///         Err(ErrorsIntervalConversion::NotIntervalPositiveLength { interval, .. }) => {
///             Err(format!("Expected positive-length interval, got: {:?}", interval))
///         }
///         Err(ErrorsIntervalConversion::NotIntervalFiniteLength { interval, .. }) => {
///             Err(format!("Expected finite interval, got infinite: {:?}", interval))
///         }
///         Err(other) => {
///             Err(format!("Unexpected conversion error: {}", other))
///         }
///     }
/// }
///
/// // Test with different interval types
/// let closed = Interval::from(IntervalClosed::new(0.0, 1.0));
/// let open = Interval::from(IntervalOpen::new(0.0, 1.0));
/// let singleton = Interval::from(IntervalSingleton::new(0.5));
/// let infinite = Interval::from(IntervalInfiniteLength::LowerUnboundedUpperUnbounded(
///     IntervalLowerUnboundedUpperUnbounded::<f64>::new()));
///
/// assert!(safe_convert_to_closed(closed).is_ok());
/// assert!(safe_convert_to_closed(open).is_err());
/// assert!(safe_convert_to_closed(singleton).is_err());
/// assert!(safe_convert_to_closed(infinite).is_err());
/// ```
///
/// ### Error Analysis and Debugging
/// ```rust
/// use grid1d::intervals::*;
/// use std::error::Error;
///
/// fn analyze_conversion_error(error: &ErrorsIntervalConversion<f64>) {
///     println!("Conversion Error Analysis:");
///     println!("Error: {}", error);
///     
///     // Access the problematic interval
///     let interval = match error {
///         ErrorsIntervalConversion::NotIntervalPositiveLength { interval, .. } => interval,
///         ErrorsIntervalConversion::NotIntervalInfiniteLength { interval, .. } => interval,
///         ErrorsIntervalConversion::NotIntervalFiniteLength { interval, .. } => interval,
///         ErrorsIntervalConversion::NotIntervalClosed { interval, .. } => interval,
///         ErrorsIntervalConversion::NotIntervalOpen { interval, .. } => interval,
///         ErrorsIntervalConversion::NotIntervalLowerOpenUpperClosed { interval, .. } => interval,
///         ErrorsIntervalConversion::NotIntervalLowerClosedUpperOpen { interval, .. } => interval,
///         ErrorsIntervalConversion::NotIntervalLowerUnboundedUpperClosed { interval, .. } => interval,
///         ErrorsIntervalConversion::NotIntervalLowerUnboundedUpperOpen { interval, .. } => interval,
///         ErrorsIntervalConversion::NotIntervalLowerClosedUpperUnbounded { interval, .. } => interval,
///         ErrorsIntervalConversion::NotIntervalLowerOpenUpperUnbounded { interval, .. } => interval,
///         ErrorsIntervalConversion::NotIntervalSingleton { interval, .. } => interval,
///     };
///     
///     println!("Problematic interval: {:?}", interval);
///     
///     // Print the backtrace for debugging
///     if let Some(backtrace) = error.source() {
///         println!("Backtrace available: {}", backtrace);
///     }
/// }
/// ```
///
/// ## Error Recovery Patterns
///
/// ### Graceful Fallback Strategies
/// ```rust
/// use grid1d::intervals::*;
///
/// /// Attempt to convert to the most specific interval type possible
/// fn convert_to_most_specific(interval: Interval<f64>) -> Box<dyn std::fmt::Debug> {
///     // Try most specific types first, fall back to more general types
///     
///     // Try singleton first
///     if let Ok(singleton) = IntervalSingleton::try_from(interval.clone()) {
///         return Box::new(singleton);
///     }
///     
///     // Try specific positive-length interval types
///     if let Ok(closed) = IntervalClosed::try_from(interval.clone()) {
///         return Box::new(closed);
///     }
///     
///     if let Ok(open) = IntervalOpen::try_from(interval.clone()) {
///         return Box::new(open);
///     }
///     
///     if let Ok(half_open) = IntervalLowerOpenUpperClosed::try_from(interval.clone()) {
///         return Box::new(half_open);
///     }
///     
///     if let Ok(half_open) = IntervalLowerClosedUpperOpen::try_from(interval.clone()) {
///         return Box::new(half_open);
///     }
///     
///     // Try infinite interval types
///     if let Ok(infinite) = IntervalInfiniteLength::try_from(interval.clone()) {
///         return Box::new(infinite);
///     }
///     
///     // Fallback to the original interval
///     Box::new(interval)
/// }
/// ```
///
/// ### Error-Tolerant Conversion Chains
/// ```rust
/// use grid1d::intervals::*;
///
/// /// Convert to closed interval or return a suitable alternative
/// fn convert_to_closed_or_alternative(
///     interval: Interval<f64>
/// ) -> Result<IntervalClosed<f64>, (Interval<f64>, String)> {
///     match IntervalClosed::try_from(interval.clone()) {
///         Ok(closed) => Ok(closed),
///         Err(ErrorsIntervalConversion::NotIntervalClosed { .. }) => {
///             // Try to convert open to closed by using the same bounds
///             if let Ok(open) = IntervalOpen::try_from(interval.clone()) {
///                 let (lower, upper) = open.into_bounds_pair();
///                 return Ok(IntervalClosed::new(lower, upper));
///             }
///             Err((interval, "Cannot convert to closed interval".to_string()))
///         }
///         Err(ErrorsIntervalConversion::NotIntervalPositiveLength { .. }) => {
///             Err((interval, "Interval does not have positive length".to_string()))
///         }
///         Err(ErrorsIntervalConversion::NotIntervalFiniteLength { .. }) => {
///             Err((interval, "Interval is not finite".to_string()))
///         }
///         Err(other) => {
///             Err((interval, format!("Unexpected error: {}", other)))
///         }
///     }
/// }
/// ```
///
/// ## Advanced Error Handling
///
/// ### Custom Error Types with Context
/// ```rust
/// use grid1d::intervals::*;
/// use num_valid::RealScalar;
/// use std::fmt;
///
/// #[derive(Debug)]
/// pub enum IntervalOperationError<T: RealScalar> {
///     ConversionFailed {
///         operation: String,
///         source: ErrorsIntervalConversion<T>,
///     },
///     InvalidOperation {
///         reason: String,
///     },
/// }
///
/// impl<T: RealScalar> fmt::Display for IntervalOperationError<T>
/// {
///     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
///         match self {
///             IntervalOperationError::ConversionFailed { operation, source } => {
///                 write!(f, "Failed to {} due to: {}", operation, source)
///             }
///             IntervalOperationError::InvalidOperation { reason } => {
///                 write!(f, "Invalid operation: {}", reason)
///             }
///         }
///     }
/// }
///
/// impl<T: RealScalar> std::error::Error for IntervalOperationError<T>
/// {
///     fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
///         match self {
///             IntervalOperationError::ConversionFailed { source, .. } => Some(source),
///             _ => None,
///         }
///     }
/// }
///
/// fn safe_interval_operation(
///     interval: Interval<f64>
/// ) -> Result<f64, IntervalOperationError<f64>> {
///     let closed = IntervalClosed::try_from(interval)
///         .map_err(|e| IntervalOperationError::ConversionFailed {
///             operation: "convert to closed interval".to_string(),
///             source: e,
///         })?;
///     
///     Ok(closed.length().into_inner())
/// }
/// ```
///
/// ### Error Aggregation for Batch Operations
/// ```rust
/// use grid1d::intervals::*;
/// use num_valid::RealScalar;
/// use try_create::New;
///
/// /// Results of batch conversion operations
/// #[derive(Debug)]
/// pub struct BatchConversionResult<T: RealScalar> {
///     pub successful: Vec<IntervalClosed<T>>,
///     pub failed: Vec<(Interval<T>, ErrorsIntervalConversion<T>)>,
/// }
///
/// /// Convert multiple intervals to closed intervals, collecting both successes and failures
/// fn batch_convert_to_closed(
///     intervals: Vec<Interval<f64>>
/// ) -> BatchConversionResult<f64> {
///     let mut successful = Vec::new();
///     let mut failed = Vec::new();
///     
///     for interval in intervals {
///         match IntervalClosed::try_from(interval.clone()) {
///             Ok(closed) => successful.push(closed),
///             Err(error) => failed.push((interval, error)),
///         }
///     }
///     
///     BatchConversionResult { successful, failed }
/// }
///
/// // Usage example
/// let intervals = vec![
///     Interval::from(IntervalClosed::new(0.0, 1.0)),
///     Interval::from(IntervalOpen::new(1.0, 2.0)),
///     Interval::from(IntervalSingleton::new(2.5)),
///     Interval::from(IntervalInfiniteLength::LowerUnboundedUpperUnbounded(
///         IntervalLowerUnboundedUpperUnbounded::new(),
///     )),
/// ];
///
/// let result = batch_convert_to_closed(intervals);
/// println!("Successful conversions: {}", result.successful.len());
/// println!("Failed conversions: {}", result.failed.len());
///
/// for (interval, error) in result.failed {
///     println!("Failed to convert {:?}: {}", interval, error);
/// }
/// ```
///
/// ## Performance Considerations
///
/// ### Error Creation Cost
/// - **Backtrace capture**: Each error includes a backtrace for debugging
/// - **Interval cloning**: The problematic interval is stored in the error
/// - **Memory overhead**: Errors contain full context information
///
/// ### Optimization Strategies
/// ```rust
/// use grid1d::intervals::*;
///
/// // For performance-critical code, check types before conversion
/// fn fast_convert_if_closed(interval: &Interval<f64>) -> Option<(f64, f64)> {
///     match interval {
///         Interval::FiniteLength(IntervalFiniteLength::PositiveLength(
///             IntervalFinitePositiveLength::Closed(closed)
///         )) => {
///             Some((*closed.lower_bound_value(), *closed.upper_bound_value()))
///         }
///         _ => None, // Avoid expensive conversion attempt
///     }
/// }
///
/// // Use pattern matching to avoid conversion errors
/// fn extract_bounds_without_conversion(interval: &Interval<f64>) -> Option<(f64, f64)> {
///     match interval {
///         Interval::FiniteLength(finite) => match finite {
///             IntervalFiniteLength::PositiveLength(positive) => {
///                 Some((*positive.lower_bound_value(), *positive.upper_bound_value()))
///             }
///             IntervalFiniteLength::ZeroLength(singleton) => {
///                 let value = *singleton.value();
///                 Some((value, value))
///             }
///         },
///         Interval::InfiniteLength(_) => None, // Cannot extract finite bounds
///     }
/// }
/// ```
///
/// ## Integration with Error Handling Libraries
///
/// ### With `thiserror`
/// ```rust
/// use grid1d::intervals::*;
/// use num_valid::RealScalar;
/// use thiserror::Error;
///
/// #[derive(Error, Debug)]
/// pub enum ProcessingError<T: RealScalar> {
///     #[error("Interval conversion failed")]
///     ConversionError(#[from] ErrorsIntervalConversion<T>),
///     
///     #[error("Invalid operation: {message}")]
///     InvalidOperation { message: String },
/// }
///
/// fn process_with_thiserror(interval: Interval<f64>) -> Result<f64, ProcessingError<f64>> {
///     let closed = IntervalClosed::try_from(interval)?;
///     Ok(closed.length().into_inner())
/// }
/// ```
///
/// ## Best Practices and Recommendations
///
/// ### When to Use Different Conversion Strategies
/// - **Direct conversion with `try_from`**: When you expect a specific type and want detailed error information
/// - **Pattern matching**: When you want to handle multiple types without conversion overhead  
/// - **Fallback chains**: When you can accept multiple interval types in order of preference
/// - **Batch operations**: When processing many intervals and want to collect all errors
///
/// ### Error Message Guidelines
/// ```rust
/// use grid1d::intervals::*;
///
/// /// Provide user-friendly error messages
/// fn user_friendly_conversion_error(error: &ErrorsIntervalConversion<f64>) -> String {
///     match error {
///         ErrorsIntervalConversion::NotIntervalPositiveLength { .. } => {
///             "This operation requires an interval with positive length (not a single point)".to_string()
///         }
///         ErrorsIntervalConversion::NotIntervalFiniteLength { .. } => {
///             "This operation requires a finite interval (not unbounded)".to_string()
///         }
///         ErrorsIntervalConversion::NotIntervalClosed { .. } => {
///             "This operation requires a closed interval [a,b] (both endpoints included)".to_string()
///         }
///         ErrorsIntervalConversion::NotIntervalSingleton { .. } => {
///             "This operation requires a single point interval [a]".to_string()
///         }
///         _ => format!("Interval conversion failed: {}", error),
///     }
/// }
/// ```
///
/// ### Debugging and Diagnostics
/// ```rust
/// use grid1d::intervals::*;
///
/// /// Comprehensive error diagnostics
/// fn diagnose_conversion_failure(error: &ErrorsIntervalConversion<f64>) {
///     println!("=== Interval Conversion Failure Diagnosis ===");
///     println!("Error: {}", error);
///     
///     let (interval, expected_type) = match error {
///         ErrorsIntervalConversion::NotIntervalPositiveLength { interval, .. } => {
///             (interval, "positive-length interval")
///         }
///         ErrorsIntervalConversion::NotIntervalClosed { interval, .. } => {
///             (interval, "closed interval [a,b]")
///         }
///         ErrorsIntervalConversion::NotIntervalSingleton { interval, .. } => {
///             (interval, "singleton interval [a]")
///         }
///         // ... handle other variants
///         _ => return,
///     };
///     
///     println!("Expected: {}", expected_type);
///     println!("Actual interval: {:?}", interval);
///     
///     // Analyze what type the interval actually is
///     match interval {
///         Interval::FiniteLength(finite) => match finite {
///             IntervalFiniteLength::PositiveLength(positive) => {
///                 println!("Actual type: positive-length finite interval");
///                 println!("Length: {}", positive.length());
///             }
///             IntervalFiniteLength::ZeroLength(singleton) => {
///                 println!("Actual type: singleton interval");
///                 println!("Value: {}", singleton.value());
///             }
///         },
///         Interval::InfiniteLength(infinite) => {
///             println!("Actual type: infinite-length interval");
///             match infinite {
///                 IntervalInfiniteLength::LowerUnboundedUpperUnbounded(_) => println!("Subtype: fully unbounded"),
///                 _ => println!("Subtype: semi-bounded"),
///             }
///         }
///     }
/// }
/// ```
///
/// ## Mathematical Correctness Guarantees
///
/// The [`ErrorsIntervalConversion`] enum maintains these correctness properties:
///
/// - **Complete error coverage**: Every possible conversion failure has a corresponding variant
/// - **Error specificity**: Each variant corresponds to exactly one type of conversion failure
/// - **Context preservation**: Errors include the problematic interval for analysis
/// - **Backtrace support**: Full call stack information for debugging
/// - **Type safety**: Errors are parameterized by the scalar type for consistency
///
/// These properties ensure that conversion failures can be properly diagnosed, handled,
/// and recovered from in a type-safe and mathematically sound manner.
#[derive(Debug, Error)]
pub enum ErrorsIntervalConversion<RealType: RealScalar> {
    /// Error indicating the interval does not have positive length.
    #[error("the interval is not of a type that model a positive length interval.")]
    NotIntervalPositiveLength {
        /// The interval that failed the conversion.
        interval: Interval<RealType>,
        /// Captured backtrace for debugging.
        backtrace: Backtrace,
    },

    /// Error indicating the interval does not have infinite length.
    #[error("the interval is not of a type that model an infinite length interval.")]
    NotIntervalInfiniteLength {
        /// The interval that failed the conversion.
        interval: Interval<RealType>,
        /// Captured backtrace for debugging.
        backtrace: Backtrace,
    },

    /// Error indicating the interval does not have finite length.
    #[error("the interval is not of a type that model an finite length interval.")]
    NotIntervalFiniteLength {
        /// The interval that failed the conversion.
        interval: Interval<RealType>,
        /// Captured backtrace for debugging.
        backtrace: Backtrace,
    },

    /// Error indicating the interval is not closed on both ends.
    #[error("the interval is not closed.")]
    NotIntervalClosed {
        /// The interval that failed the conversion.
        interval: Interval<RealType>,
        /// Captured backtrace for debugging.
        backtrace: Backtrace,
    },

    /// Error indicating the interval is not open on both ends.
    #[error("the interval is not open.")]
    NotIntervalOpen {
        /// The interval that failed the conversion.
        interval: Interval<RealType>,
        /// Captured backtrace for debugging.
        backtrace: Backtrace,
    },

    /// Error indicating the interval is not lower-open and upper-closed.
    #[error("the interval is not lower-opem and upper-closed.")]
    NotIntervalLowerOpenUpperClosed {
        /// The interval that failed the conversion.
        interval: Interval<RealType>,
        /// Captured backtrace for debugging.
        backtrace: Backtrace,
    },

    /// Error indicating the interval is not lower-closed and upper-open.
    #[error("the interval is not lower-closed and upper-open.")]
    NotIntervalLowerClosedUpperOpen {
        /// The interval that failed the conversion.
        interval: Interval<RealType>,
        /// Captured backtrace for debugging.
        backtrace: Backtrace,
    },

    /// Error indicating the interval is not lower-unbounded and upper-closed.
    #[error("the interval is not lower-unbounded and upper-closed.")]
    NotIntervalLowerUnboundedUpperClosed {
        /// The interval that failed the conversion.
        interval: Interval<RealType>,
        /// Captured backtrace for debugging.
        backtrace: Backtrace,
    },

    /// Error indicating the interval is not lower-unbounded and upper-open.
    #[error("the interval is not lower-unbounded and upper-open.")]
    NotIntervalLowerUnboundedUpperOpen {
        /// The interval that failed the conversion.
        interval: Interval<RealType>,
        /// Captured backtrace for debugging.
        backtrace: Backtrace,
    },

    /// Error indicating the interval is not lower-closed and upper-unbounded.
    #[error("the interval is not lower-closed and upper-unbounded.")]
    NotIntervalLowerClosedUpperUnbounded {
        /// The interval that failed the conversion.
        interval: Interval<RealType>,
        /// Captured backtrace for debugging.
        backtrace: Backtrace,
    },

    /// Error indicating the interval is not lower-open and upper-unbounded.
    #[error("the interval is not lower-open and upper-unbounded.")]
    NotIntervalLowerOpenUpperUnbounded {
        /// The interval that failed the conversion.
        interval: Interval<RealType>,
        /// Captured backtrace for debugging.
        backtrace: Backtrace,
    },

    /// Error indicating the interval is not a singleton (zero-length interval).
    #[error("the interval is not a singleton.")]
    NotIntervalSingleton {
        /// The interval that failed the conversion.
        interval: Interval<RealType>,
        /// Captured backtrace for debugging.
        backtrace: Backtrace,
    },
}
//------------------------------------------------------------------------------------------------

//------------------------------------------------------------------------------------------------
#[cfg(test)]
mod tests {
    use super::*;
    use crate::{bounds::*, intervals::bounded::*};
    use num::One;
    use try_create::New;

    /// Tests for the `into_interval()` method that converts all interval types to `Interval<T>`
    mod into_interval_conversions {
        use super::*;

        #[test]
        fn to_interval_closed() {
            let closed = IntervalClosed::new(0.0, 1.0);
            let general = closed.into_interval();
            assert!(matches!(
                general,
                Interval::FiniteLength(IntervalFiniteLength::PositiveLength(
                    IntervalFinitePositiveLength::Closed(_)
                ))
            ));
        }

        #[test]
        fn to_interval_open() {
            let open = IntervalOpen::new(0.0, 1.0);
            let general = open.into_interval();
            assert!(matches!(
                general,
                Interval::FiniteLength(IntervalFiniteLength::PositiveLength(
                    IntervalFinitePositiveLength::Open(_)
                ))
            ));
        }

        #[test]
        fn to_interval_lower_closed_upper_open() {
            let half_open = IntervalLowerClosedUpperOpen::new(0.0, 1.0);
            let general = half_open.into_interval();
            assert!(matches!(
                general,
                Interval::FiniteLength(IntervalFiniteLength::PositiveLength(
                    IntervalFinitePositiveLength::LowerClosedUpperOpen(_)
                ))
            ));
        }

        #[test]
        fn to_interval_lower_open_upper_closed() {
            let half_open = IntervalLowerOpenUpperClosed::new(0.0, 1.0);
            let general = half_open.into_interval();
            assert!(matches!(
                general,
                Interval::FiniteLength(IntervalFiniteLength::PositiveLength(
                    IntervalFinitePositiveLength::LowerOpenUpperClosed(_)
                ))
            ));
        }

        #[test]
        fn to_interval_singleton() {
            let singleton = IntervalSingleton::new(5.0);
            let general = singleton.into_interval();
            assert!(matches!(
                general,
                Interval::FiniteLength(IntervalFiniteLength::ZeroLength(_))
            ));
        }

        #[test]
        fn to_interval_lower_closed_upper_unbounded() {
            let unbounded = IntervalLowerClosedUpperUnbounded::new(0.0);
            let general = unbounded.into_interval();
            assert!(matches!(
                general,
                Interval::InfiniteLength(IntervalInfiniteLength::LowerClosedUpperUnbounded(_))
            ));
        }

        #[test]
        fn to_interval_lower_open_upper_unbounded() {
            let unbounded = IntervalLowerOpenUpperUnbounded::new(0.0);
            let general = unbounded.into_interval();
            assert!(matches!(
                general,
                Interval::InfiniteLength(IntervalInfiniteLength::LowerOpenUpperUnbounded(_))
            ));
        }

        #[test]
        fn to_interval_lower_unbounded_upper_closed() {
            let unbounded = IntervalLowerUnboundedUpperClosed::new(1.0);
            let general = unbounded.into_interval();
            assert!(matches!(
                general,
                Interval::InfiniteLength(IntervalInfiniteLength::LowerUnboundedUpperClosed(_))
            ));
        }

        #[test]
        fn to_interval_lower_unbounded_upper_open() {
            let unbounded = IntervalLowerUnboundedUpperOpen::new(1.0);
            let general = unbounded.into_interval();
            assert!(matches!(
                general,
                Interval::InfiniteLength(IntervalInfiniteLength::LowerUnboundedUpperOpen(_))
            ));
        }

        #[test]
        fn to_interval_fully_unbounded() {
            let unbounded = IntervalLowerUnboundedUpperUnbounded::<f64>::new();
            let general = unbounded.into_interval();
            assert!(matches!(
                general,
                Interval::InfiniteLength(IntervalInfiniteLength::LowerUnboundedUpperUnbounded(_))
            ));
        }

        #[test]
        fn to_interval_enum_finite_positive_length() {
            let closed = IntervalClosed::new(0.0, 1.0);
            let enum_interval = IntervalFinitePositiveLength::Closed(closed);
            let general = enum_interval.into_interval();
            assert!(matches!(
                general,
                Interval::FiniteLength(IntervalFiniteLength::PositiveLength(_))
            ));
        }

        #[test]
        fn to_interval_enum_finite_length() {
            let singleton = IntervalSingleton::new(5.0);
            let enum_interval = IntervalFiniteLength::ZeroLength(singleton);
            let general = enum_interval.into_interval();
            assert!(matches!(general, Interval::FiniteLength(_)));
        }

        #[test]
        fn to_interval_enum_infinite_length() {
            let unbounded = IntervalLowerClosedUpperUnbounded::new(0.0);
            let enum_interval = IntervalInfiniteLength::LowerClosedUpperUnbounded(unbounded);
            let general = enum_interval.into_interval();
            assert!(matches!(general, Interval::InfiniteLength(_)));
        }

        #[test]
        fn to_interval_preserves_values() {
            let closed = IntervalClosed::new(2.5, 7.5);
            let general = closed.into_interval();

            // Extract the values and verify they're preserved using runtime bounds
            if let Some(LowerBoundRuntime::Closed(bound)) = general.lower_bound_runtime() {
                assert_eq!(bound.value, 2.5);
            } else {
                panic!("Expected closed lower bound");
            }
            if let Some(UpperBoundRuntime::Closed(bound)) = general.upper_bound_runtime() {
                assert_eq!(bound.value, 7.5);
            } else {
                panic!("Expected closed upper bound");
            }
        }

        #[test]
        fn to_interval_preserves_boundary_semantics() {
            let closed = IntervalClosed::new(0.0, 1.0);
            let general_closed = closed.into_interval();
            assert!(matches!(
                general_closed.lower_bound_runtime(),
                Some(LowerBoundRuntime::Closed(_))
            ));
            assert!(matches!(
                general_closed.upper_bound_runtime(),
                Some(UpperBoundRuntime::Closed(_))
            ));

            let open = IntervalOpen::new(0.0, 1.0);
            let general_open = open.into_interval();
            assert!(matches!(
                general_open.lower_bound_runtime(),
                Some(LowerBoundRuntime::Open(_))
            ));
            assert!(matches!(
                general_open.upper_bound_runtime(),
                Some(UpperBoundRuntime::Open(_))
            ));
        }

        #[test]
        fn to_interval_in_generic_function() {
            fn convert_any_interval<I>(interval: I) -> Interval<f64>
            where
                I: IntervalTrait<RealType = f64>,
                I: Into<Interval<f64>>,
            {
                interval.into_interval()
            }

            let closed = IntervalClosed::new(0.0, 1.0);
            let result = convert_any_interval(closed);
            assert!(matches!(result, Interval::FiniteLength(_)));

            let unbounded = IntervalLowerClosedUpperUnbounded::new(0.0);
            let result2 = convert_any_interval(unbounded);
            assert!(matches!(result2, Interval::InfiniteLength(_)));
        }

        /// Test that the generic IntervalBounded type can be converted to Interval
        /// This works because the type aliases (IntervalClosed, etc.) ARE IntervalBounded
        /// with specific type parameters, so Into is already implemented!
        #[test]
        fn generic_interval_bounded_into_interval() {
            use crate::bounds::{Closed, Open};

            // Test IntervalBounded<_, Closed, Closed> (same as IntervalClosed)
            let closed_bounded: IntervalBounded<f64, Closed, Closed> =
                IntervalBounded::new(0.0, 1.0);
            let interval1: Interval<f64> = closed_bounded.into();
            assert!(matches!(
                interval1,
                Interval::FiniteLength(IntervalFiniteLength::PositiveLength(
                    IntervalFinitePositiveLength::Closed(_)
                ))
            ));

            // Test IntervalBounded<_, Open, Open> (same as IntervalOpen)
            let open_bounded: IntervalBounded<f64, Open, Open> = IntervalBounded::new(0.0, 1.0);
            let interval2: Interval<f64> = open_bounded.into();
            assert!(matches!(
                interval2,
                Interval::FiniteLength(IntervalFiniteLength::PositiveLength(
                    IntervalFinitePositiveLength::Open(_)
                ))
            ));

            // Test IntervalBounded<_, Closed, Open> (same as IntervalLowerClosedUpperOpen)
            let half_open_bounded: IntervalBounded<f64, Closed, Open> =
                IntervalBounded::new(0.0, 1.0);
            let interval3: Interval<f64> = half_open_bounded.into();
            assert!(matches!(
                interval3,
                Interval::FiniteLength(IntervalFiniteLength::PositiveLength(
                    IntervalFinitePositiveLength::LowerClosedUpperOpen(_)
                ))
            ));

            // Test IntervalBounded<_, Open, Closed> (same as IntervalLowerOpenUpperClosed)
            let other_half_open: IntervalBounded<f64, Open, Closed> =
                IntervalBounded::new(0.0, 1.0);
            let interval4: Interval<f64> = other_half_open.into();
            assert!(matches!(
                interval4,
                Interval::FiniteLength(IntervalFiniteLength::PositiveLength(
                    IntervalFinitePositiveLength::LowerOpenUpperClosed(_)
                ))
            ));
        }

        /// Test that generic functions can work with IntervalBounded directly
        /// This test verifies that Into<Interval> works with type parameters
        #[test]
        fn generic_function_with_interval_bounded() {
            use crate::bounds::{Closed, Open};

            fn convert_generic_bounded<L: BoundType, U: BoundType>(
                interval: IntervalBounded<f64, L, U>,
            ) -> Interval<f64>
            where
                IntervalBounded<f64, L, U>: Into<Interval<f64>>,
            {
                interval.into()
            }

            let closed = IntervalBounded::<f64, Closed, Closed>::new(0.0, 1.0);
            let result = convert_generic_bounded(closed);
            assert!(matches!(result, Interval::FiniteLength(_)));

            let open = IntervalBounded::<f64, Open, Open>::new(0.0, 1.0);
            let result2 = convert_generic_bounded(open);
            assert!(matches!(result2, Interval::FiniteLength(_)));
        }

        /// Test that proves Into is NOT implemented for arbitrary generic L/U types
        /// This test SHOULD fail to compile if you remove the where clause
        #[test]
        fn generic_requires_explicit_bound() {
            use crate::bounds::Closed;

            // This function requires the explicit where clause
            // Without it, Rust can't prove that Into is implemented
            fn convert_with_bound<L: BoundType, U: BoundType>(
                interval: IntervalBounded<f64, L, U>,
            ) -> Interval<f64>
            where
                IntervalBounded<f64, L, U>: Into<Interval<f64>>,
            {
                interval.into()
            }

            // However, when we call it with concrete types, it works
            // because the compiler can monomorphize and check the Into impl exists
            let closed = IntervalBounded::<f64, Closed, Closed>::new(0.0, 1.0);
            let _ = convert_with_bound(closed);

            // The key insight: Into is implemented for the 4 specific combinations
            // (Closed,Closed), (Open,Open), (Closed,Open), (Open,Closed)
            // NOT for arbitrary (L,U) where L,U: BoundType
        }

        /// Test that into_interval() works with IntervalBounded
        #[test]
        fn interval_bounded_into_interval_method() {
            use crate::bounds::{Closed, Open};

            let bounded: IntervalBounded<f64, Closed, Open> = IntervalBounded::new(0.0, 1.0);
            let interval = bounded.into_interval();
            assert!(matches!(
                interval,
                Interval::FiniteLength(IntervalFiniteLength::PositiveLength(
                    IntervalFinitePositiveLength::LowerClosedUpperOpen(_)
                ))
            ));
        }
    }

    mod lower_bound_value {
        use super::*;

        #[test]
        fn lower_bound_value_interval_closed() {
            let interval = IntervalClosed::new(1.0, 2.0);
            assert_eq!(interval.lower_bound_value(), &1.0);
        }

        #[test]
        fn lower_bound_value_interval_open() {
            let interval = IntervalOpen::new(3.0, 4.0);
            assert_eq!(interval.lower_bound_value(), &3.0);
        }

        #[test]
        fn lower_bound_value_interval_lower_open_upper_closed() {
            let interval = IntervalLowerOpenUpperClosed::new(5.0, 6.0);
            assert_eq!(interval.lower_bound_value(), &5.0);
        }

        #[test]
        fn lower_bound_value_interval_lower_closed_upper_open() {
            let interval = IntervalLowerClosedUpperOpen::new(7.0, 8.0);
            assert_eq!(interval.lower_bound_value(), &7.0);
        }

        #[test]
        fn lower_bound_value_interval_finite_positive_length() {
            let closed = IntervalClosed::new(1.0, 2.0);
            let open = IntervalOpen::new(3.0, 4.0);
            let left = IntervalLowerOpenUpperClosed::new(5.0, 6.0);
            let right = IntervalLowerClosedUpperOpen::new(7.0, 8.0);

            let c = IntervalFinitePositiveLength::Closed(closed);
            let o = IntervalFinitePositiveLength::Open(open);
            let l = IntervalFinitePositiveLength::LowerOpenUpperClosed(left);
            let r = IntervalFinitePositiveLength::LowerClosedUpperOpen(right);

            assert_eq!(c.lower_bound_value(), &1.0);
            assert_eq!(o.lower_bound_value(), &3.0);
            assert_eq!(l.lower_bound_value(), &5.0);
            assert_eq!(r.lower_bound_value(), &7.0);
        }

        #[test]
        fn lower_bound_value_subinterval_in_partition() {
            let closed = IntervalClosed::new(1.0, 2.0);
            let left = IntervalLowerOpenUpperClosed::new(2.0, 3.0);
            let s1 = SubIntervalInPartition::<IntervalClosed<f64>>::First(closed);
            let s2 = SubIntervalInPartition::<IntervalClosed<f64>>::Last(left);

            assert_eq!(s1.lower_bound_value(), &1.0);
            assert_eq!(s2.lower_bound_value(), &2.0);
        }

        #[test]
        fn lower_bound_value_interval_lower_closed_upper_unbounded() {
            let interval = IntervalLowerClosedUpperUnbounded::new(1.0);
            assert_eq!(interval.lower_bound_value(), &1.0);
        }

        #[test]
        fn lower_bound_value_interval_lower_open_upper_unbounded() {
            let interval = IntervalLowerOpenUpperUnbounded::new(2.0);
            assert_eq!(interval.lower_bound_value(), &2.0);
        }
    }

    mod upper_bound_value {
        use super::*;

        #[test]
        fn upper_bound_value_interval_closed() {
            let interval = IntervalClosed::new(1.0, 2.0);
            assert_eq!(interval.upper_bound_value(), &2.0);
        }

        #[test]
        fn upper_bound_value_interval_open() {
            let interval = IntervalOpen::new(3.0, 4.0);
            assert_eq!(interval.upper_bound_value(), &4.0);
        }

        #[test]
        fn upper_bound_value_interval_lower_open_upper_closed() {
            let interval = IntervalLowerOpenUpperClosed::new(5.0, 6.0);
            assert_eq!(interval.upper_bound_value(), &6.0);
        }

        #[test]
        fn upper_bound_value_interval_lower_closed_upper_open() {
            let interval = IntervalLowerClosedUpperOpen::new(7.0, 8.0);
            assert_eq!(interval.upper_bound_value(), &8.0);
        }

        #[test]
        fn upper_bound_value_interval_finite_positive_length() {
            let closed = IntervalClosed::new(1.0, 2.0);
            let open = IntervalOpen::new(3.0, 4.0);
            let left = IntervalLowerOpenUpperClosed::new(5.0, 6.0);
            let right = IntervalLowerClosedUpperOpen::new(7.0, 8.0);

            let c = IntervalFinitePositiveLength::Closed(closed);
            let o = IntervalFinitePositiveLength::Open(open);
            let l = IntervalFinitePositiveLength::LowerOpenUpperClosed(left);
            let r = IntervalFinitePositiveLength::LowerClosedUpperOpen(right);

            assert_eq!(c.upper_bound_value(), &2.0);
            assert_eq!(o.upper_bound_value(), &4.0);
            assert_eq!(l.upper_bound_value(), &6.0);
            assert_eq!(r.upper_bound_value(), &8.0);
        }

        #[test]
        fn upper_bound_value_subinterval_in_partition() {
            let closed = IntervalClosed::new(1.0, 2.0);
            let left = IntervalLowerOpenUpperClosed::new(2.0, 3.0);
            let s1 = SubIntervalInPartition::<IntervalClosed<f64>>::First(closed);
            let s2 = SubIntervalInPartition::<IntervalClosed<f64>>::Last(left);

            assert_eq!(s1.upper_bound_value(), &2.0);
            assert_eq!(s2.upper_bound_value(), &3.0);
        }

        #[test]
        fn upper_bound_value_interval_lower_unbounded_upper_closed() {
            let interval = IntervalLowerUnboundedUpperClosed::new(1.0);
            assert_eq!(interval.upper_bound_value(), &1.0);
        }

        #[test]
        fn upper_bound_value_interval_lower_unbounded_upper_open() {
            let interval = IntervalLowerUnboundedUpperOpen::new(2.0);
            assert_eq!(interval.upper_bound_value(), &2.0);
        }
    }

    mod into_bounds_pair {
        use super::*;

        #[test]
        fn into_bounds_pair_interval_closed() {
            let interval = IntervalClosed::new(1.0, 2.0);
            let (a, b) = interval.clone().into_bounds_pair();
            assert_eq!(a, 1.0);
            assert_eq!(b, 2.0);
            assert_eq!(interval.lower_bound_value(), &1.0);
            assert_eq!(interval.upper_bound_value(), &2.0);
        }

        #[test]
        fn into_bounds_pair_interval_open() {
            let interval = IntervalOpen::new(3.0, 4.0);
            let (a, b) = interval.clone().into_bounds_pair();
            assert_eq!(a, 3.0);
            assert_eq!(b, 4.0);
            assert_eq!(interval.lower_bound_value(), &3.0);
            assert_eq!(interval.upper_bound_value(), &4.0);
        }

        #[test]
        fn into_bounds_pair_interval_lower_open_upper_closed() {
            let interval = IntervalLowerOpenUpperClosed::new(5.0, 6.0);
            let (a, b) = interval.clone().into_bounds_pair();
            assert_eq!(a, 5.0);
            assert_eq!(b, 6.0);
            assert_eq!(interval.lower_bound_value(), &5.0);
            assert_eq!(interval.upper_bound_value(), &6.0);
        }

        #[test]
        fn into_bounds_pair_interval_lower_closed_upper_open() {
            let interval = IntervalLowerClosedUpperOpen::new(7.0, 8.0);
            let (a, b) = interval.clone().into_bounds_pair();
            assert_eq!(a, 7.0);
            assert_eq!(b, 8.0);
            assert_eq!(interval.lower_bound_value(), &7.0);
            assert_eq!(interval.upper_bound_value(), &8.0);
        }

        #[test]
        fn into_bounds_pair_interval_finite_positive_length_01() {
            let closed = IntervalClosed::new(1.0, 2.0);
            let open = IntervalOpen::new(3.0, 4.0);
            let left = IntervalLowerOpenUpperClosed::new(5.0, 6.0);
            let right = IntervalLowerClosedUpperOpen::new(7.0, 8.0);

            let c = IntervalFinitePositiveLength::Closed(closed);
            let o = IntervalFinitePositiveLength::Open(open);
            let l = IntervalFinitePositiveLength::LowerOpenUpperClosed(left);
            let r = IntervalFinitePositiveLength::LowerClosedUpperOpen(right);

            assert_eq!(c.clone().into_bounds_pair(), (1.0, 2.0));
            assert_eq!(o.clone().into_bounds_pair(), (3.0, 4.0));
            assert_eq!(l.clone().into_bounds_pair(), (5.0, 6.0));
            assert_eq!(r.clone().into_bounds_pair(), (7.0, 8.0));
        }

        #[test]
        fn into_bounds_pair_interval_finite_positive_length_02() {
            let closed = IntervalClosed::new(1.0, 2.0);
            let open = IntervalOpen::new(1.0, 2.0);
            let left = IntervalLowerOpenUpperClosed::new(1.0, 2.0);
            let right = IntervalLowerClosedUpperOpen::new(1.0, 2.0);
            let c = IntervalFinitePositiveLength::Closed(closed);
            let o = IntervalFinitePositiveLength::Open(open);
            let l = IntervalFinitePositiveLength::LowerOpenUpperClosed(left);
            let r = IntervalFinitePositiveLength::LowerClosedUpperOpen(right);
            assert_eq!(c.into_bounds_pair(), (1.0, 2.0));
            assert_eq!(o.into_bounds_pair(), (1.0, 2.0));
            assert_eq!(l.into_bounds_pair(), (1.0, 2.0));
            assert_eq!(r.into_bounds_pair(), (1.0, 2.0));
        }

        #[test]
        fn into_bounds_pair_subinterval_in_partition() {
            let closed = IntervalClosed::new(1.0, 2.0);
            let left = IntervalLowerOpenUpperClosed::new(2.0, 3.0);
            let s1 = SubIntervalInPartition::<IntervalClosed<f64>>::First(closed);
            let s2 = SubIntervalInPartition::<IntervalClosed<f64>>::Last(left);

            assert_eq!(s1.clone().into_bounds_pair(), (1.0, 2.0));
            assert_eq!(s2.clone().into_bounds_pair(), (2.0, 3.0));
        }

        #[test]
        fn into_inner_interval_singleton() {
            let singleton = IntervalSingleton::new(42.0);
            let value = singleton.clone().into_inner();
            assert_eq!(value, 42.0);
            assert_eq!(singleton.value(), &42.0);
        }
    }

    mod length {
        use super::*;

        #[test]
        fn length_interval_closed() {
            let interval = IntervalClosed::new(1.0, 3.0);
            assert_eq!(interval.length().into_inner(), 2.0);
        }

        #[test]
        fn length_interval_open() {
            let interval = IntervalOpen::new(2.0, 5.0);
            assert_eq!(interval.length().into_inner(), 3.0);
        }

        #[test]
        fn length_interval_lower_open_upper_closed() {
            let interval = IntervalLowerOpenUpperClosed::new(0.0, 4.0);
            assert_eq!(interval.length().into_inner(), 4.0);
        }

        #[test]
        fn length_interval_lower_closed_upper_open() {
            let interval = IntervalLowerClosedUpperOpen::new(-1.0, 1.0);
            assert_eq!(interval.length().into_inner(), 2.0);
        }

        #[test]
        fn length_interval_finite_positive_length() {
            let closed = IntervalClosed::new(1.0, 2.0);
            let open = IntervalOpen::new(3.0, 6.0);
            let left = IntervalLowerOpenUpperClosed::new(5.0, 8.0);
            let right = IntervalLowerClosedUpperOpen::new(7.0, 9.0);

            let c = IntervalFinitePositiveLength::Closed(closed);
            let o = IntervalFinitePositiveLength::Open(open);
            let l = IntervalFinitePositiveLength::LowerOpenUpperClosed(left);
            let r = IntervalFinitePositiveLength::LowerClosedUpperOpen(right);

            assert_eq!(c.length().into_inner(), 1.0);
            assert_eq!(o.length().into_inner(), 3.0);
            assert_eq!(l.length().into_inner(), 3.0);
            assert_eq!(r.length().into_inner(), 2.0);
        }

        #[test]
        fn length_subinterval_in_partition() {
            let closed = IntervalClosed::new(1.0, 2.5);
            let left = IntervalLowerOpenUpperClosed::new(2.5, 5.0);
            let s1 = SubIntervalInPartition::<IntervalClosed<f64>>::First(closed);
            let s2 = SubIntervalInPartition::<IntervalClosed<f64>>::Last(left);

            assert_eq!(s1.length().into_inner(), 1.5);
            assert_eq!(s2.length().into_inner(), 2.5);
        }
    }

    mod mid_point {
        use super::*;

        #[test]
        fn mid_point_interval_closed() {
            let interval = IntervalClosed::new(1.0, 3.0);
            assert_eq!(interval.mid_point(), 2.0);
        }

        #[test]
        fn mid_point_interval_open() {
            let interval = IntervalOpen::new(2.0, 6.0);
            assert_eq!(interval.mid_point(), 4.0);
        }

        #[test]
        fn mid_point_interval_lower_open_upper_closed() {
            let interval = IntervalLowerOpenUpperClosed::new(0.0, 4.0);
            assert_eq!(interval.mid_point(), 2.0);
        }

        #[test]
        fn mid_point_interval_lower_closed_upper_open() {
            let interval = IntervalLowerClosedUpperOpen::new(-1.0, 1.0);
            assert_eq!(interval.mid_point(), 0.0);
        }

        #[test]
        fn mid_point_interval_finite_positive_length() {
            let closed = IntervalClosed::new(1.0, 3.0);
            let open = IntervalOpen::new(2.0, 6.0);
            let left = IntervalLowerOpenUpperClosed::new(0.0, 4.0);
            let right = IntervalLowerClosedUpperOpen::new(-1.0, 1.0);

            let c = IntervalFinitePositiveLength::Closed(closed);
            let o = IntervalFinitePositiveLength::Open(open);
            let l = IntervalFinitePositiveLength::LowerOpenUpperClosed(left);
            let r = IntervalFinitePositiveLength::LowerClosedUpperOpen(right);

            assert_eq!(c.mid_point(), 2.0);
            assert_eq!(o.mid_point(), 4.0);
            assert_eq!(l.mid_point(), 2.0);
            assert_eq!(r.mid_point(), 0.0);
        }

        #[test]
        fn mid_point_subinterval_in_partition() {
            let closed = IntervalClosed::new(1.0, 3.0);
            let left = IntervalLowerOpenUpperClosed::new(3.0, 7.0);
            let s1 = SubIntervalInPartition::<IntervalClosed<f64>>::First(closed);
            let s2 = SubIntervalInPartition::<IntervalClosed<f64>>::Last(left);

            assert_eq!(s1.mid_point(), 2.0);
            assert_eq!(s2.mid_point(), 5.0);
        }
    }

    mod partial_ord {
        use super::*;

        #[test]
        fn partial_ord_lower_bound_closed() {
            let a = LowerBoundClosed::new(1.0);
            let b = LowerBoundClosed::new(2.0);
            assert!(a < b);
            assert!(b > a);
            assert!(a <= b);
            assert!(b >= a);
            assert!(a == a);
            assert!(b == b);
        }

        #[test]
        fn partial_ord_lower_bound_open() {
            let a = LowerBoundOpen::new(1.0);
            let b = LowerBoundOpen::new(2.0);
            assert!(a < b);
            assert!(b > a);
            assert!(a <= b);
            assert!(b >= a);
            assert!(a == a);
            assert!(b == b);
        }

        #[test]
        fn partial_ord_upper_bound_closed() {
            let a = UpperBoundClosed::new(1.0);
            let b = UpperBoundClosed::new(2.0);
            assert!(a < b);
            assert!(b > a);
            assert!(a <= b);
            assert!(b >= a);
            assert!(a == a);
            assert!(b == b);
        }

        #[test]
        fn partial_ord_upper_bound_open() {
            let a = UpperBoundOpen::new(1.0);
            let b = UpperBoundOpen::new(2.0);
            assert!(a < b);
            assert!(b > a);
            assert!(a <= b);
            assert!(b >= a);
            assert!(a == a);
            assert!(b == b);
        }

        #[test]
        fn partial_ord_lower_bound_cross_types() {
            let closed = LowerBoundClosed::new(1.0);
            let open = LowerBoundOpen::new(2.0);
            assert!(closed < open);
            assert!(open > closed);
            assert!(closed <= open);
            assert!(open >= closed);
            assert!(!(closed == open));
        }

        #[test]
        fn partial_ord_upper_bound_cross_types() {
            let closed = UpperBoundClosed::new(1.0);
            let open = UpperBoundOpen::new(2.0);
            assert!(closed < open);
            assert!(open > closed);
            assert!(closed <= open);
            assert!(open >= closed);
            assert!(!(closed == open));
        }

        #[test]
        fn partial_ord_lower_bound_open_closed_same_value() {
            let closed = LowerBoundClosed::new(1.0);
            let open = LowerBoundOpen::new(1.0);
            assert!(closed < open);
            assert!(open > closed);
            assert!(closed <= open);
            assert!(open >= closed);
            assert!(!(closed == open));
        }

        #[test]
        fn partial_ord_upper_bound_open_closed_same_value() {
            let closed = UpperBoundClosed::new(1.0);
            let open = UpperBoundOpen::new(1.0);
            assert!(open < closed);
            assert!(closed > open);
            assert!(open <= closed);
            assert!(closed >= open);
            assert!(!(closed == open));
        }
    }

    mod contains_on_same_interval_type {
        use super::*;

        #[test]
        fn contains_interval_closed() {
            let outer = IntervalClosed::new(0.0, 2.0);
            let inner = IntervalClosed::new(0.5, 1.5);
            assert!(outer.contains_interval(&inner));
            assert!(!inner.contains_interval(&outer));
        }

        #[test]
        fn contains_interval_open() {
            let outer = IntervalOpen::new(0.0, 2.0);
            let inner = IntervalOpen::new(0.5, 1.5);
            assert!(outer.contains_interval(&inner));
            assert!(!inner.contains_interval(&outer));
        }

        #[test]
        fn contains_interval_lower_open_upper_closed() {
            let outer = IntervalLowerOpenUpperClosed::new(0.0, 2.0);
            let inner = IntervalLowerOpenUpperClosed::new(0.5, 1.5);
            assert!(outer.contains_interval(&inner));
            assert!(!inner.contains_interval(&outer));
        }

        #[test]
        fn contains_interval_lower_closed_upper_open() {
            let outer = IntervalLowerClosedUpperOpen::new(0.0, 2.0);
            let inner = IntervalLowerClosedUpperOpen::new(0.5, 1.5);
            assert!(outer.contains_interval(&inner));
            assert!(!inner.contains_interval(&outer));
        }

        #[test]
        fn contains_interval_singleton() {
            let outer = IntervalClosed::new(1.0, 1.1);
            let singleton = IntervalSingleton::new(1.0);
            assert!(outer.contains_interval(&singleton));
            assert!(singleton.contains_interval(&singleton));
            let other = IntervalSingleton::new(2.0);
            assert!(!singleton.contains_interval(&other));
        }

        #[test]
        fn contains_interval_finite_positive_length() {
            let outer = IntervalFinitePositiveLength::Closed(IntervalClosed::new(0.0, 2.0));
            let inner = IntervalFinitePositiveLength::Closed(IntervalClosed::new(0.5, 1.5));
            assert!(outer.contains_interval(&inner));
            assert!(!inner.contains_interval(&outer));
        }

        #[test]
        fn contains_interval_finite_length() {
            let outer = IntervalFiniteLength::PositiveLength(IntervalFinitePositiveLength::Closed(
                IntervalClosed::new(0.0, 2.0),
            ));
            let inner = IntervalFiniteLength::PositiveLength(IntervalFinitePositiveLength::Closed(
                IntervalClosed::new(0.5, 1.5),
            ));
            assert!(outer.contains_interval(&inner));
            assert!(!inner.contains_interval(&outer));
        }

        #[test]
        fn contains_interval_infinite_length() {
            let outer = IntervalInfiniteLength::LowerUnboundedUpperUnbounded(
                IntervalLowerUnboundedUpperUnbounded::new(),
            );
            let inner = IntervalInfiniteLength::LowerClosedUpperUnbounded(
                IntervalLowerClosedUpperUnbounded::new(0.0),
            );
            assert!(outer.contains_interval(&inner));
            assert!(!inner.contains_interval(&outer));
        }

        #[test]
        fn contains_interval_enum() {
            let outer = Interval::FiniteLength(IntervalFiniteLength::PositiveLength(
                IntervalFinitePositiveLength::Closed(IntervalClosed::new(0.0, 2.0)),
            ));
            let inner = Interval::FiniteLength(IntervalFiniteLength::PositiveLength(
                IntervalFinitePositiveLength::Closed(IntervalClosed::new(0.5, 1.5)),
            ));
            assert!(outer.contains_interval(&inner));
            assert!(!inner.contains_interval(&outer));
        }

        #[test]
        fn contains_subinterval_in_partition() {
            let outer =
                SubIntervalInPartition::<IntervalClosed<f64>>::First(IntervalClosed::new(0.0, 2.0));
            let inner =
                SubIntervalInPartition::<IntervalClosed<f64>>::First(IntervalClosed::new(0.5, 1.5));
            assert!(outer.contains_interval(&inner));
            assert!(!inner.contains_interval(&outer));

            let outer = SubIntervalInPartition::<IntervalClosed<f64>>::Last(
                IntervalLowerOpenUpperClosed::new(0.0, 2.0),
            );
            let inner = SubIntervalInPartition::<IntervalClosed<f64>>::Last(
                IntervalLowerOpenUpperClosed::new(0.5, 1.5),
            );
            assert!(outer.contains_interval(&inner));
            assert!(!inner.contains_interval(&outer));
        }
    }

    mod contains_on_intervals_combinations {
        use super::*;

        #[test]
        fn contains_closed_open() {
            let closed = IntervalClosed::new(0.0, 3.0);
            let open = IntervalOpen::new(0.5, 2.5);
            assert!(closed.contains_interval(&open));
            assert!(!open.contains_interval(&closed));
        }

        #[test]
        fn contains_closed_closed() {
            let i1 = IntervalClosed::new(0.0, 2.0);
            let i2 = IntervalClosed::new(0.5, 1.5);
            assert!(i1.contains_interval(&i2));
            assert!(!i2.contains_interval(&i1));
        }

        #[test]
        fn contains_closed_left_half_open() {
            let closed = IntervalClosed::new(0.0, 3.0);
            let left = IntervalLowerOpenUpperClosed::new(0.5, 3.0);
            assert!(closed.contains_interval(&left));
            assert!(!left.contains_interval(&closed));
        }

        #[test]
        fn contains_closed_right_half_open() {
            let closed = IntervalClosed::new(0.0, 3.0);
            let right = IntervalLowerClosedUpperOpen::new(0.0, 2.5);
            assert!(closed.contains_interval(&right));
            assert!(!right.contains_interval(&closed));
        }

        #[test]
        fn contains_open_left_half_open() {
            let open = IntervalOpen::new(0.0, 3.0);
            let left = IntervalLowerOpenUpperClosed::new(0.5, 2.5);
            assert!(open.contains_interval(&left));
            assert!(!left.contains_interval(&open));
        }

        #[test]
        fn contains_open_right_half_open() {
            let open = IntervalOpen::new(0.0, 3.0);
            let right = IntervalLowerClosedUpperOpen::new(0.5, 3.0);
            assert!(open.contains_interval(&right));
            assert!(!right.contains_interval(&open));
        }

        #[test]
        fn contains_left_half_open_right_half_open() {
            let left = IntervalLowerOpenUpperClosed::new(0.0, 3.0);
            let right = IntervalLowerClosedUpperOpen::new(0.5, 3.0);
            assert!(left.contains_interval(&right));
            assert!(!right.contains_interval(&left));
        }

        #[test]
        fn contains_closed_singleton() {
            let closed = IntervalClosed::new(0.0, 2.0);
            let singleton = IntervalSingleton::new(1.0);
            assert!(closed.contains_interval(&singleton));
            assert!(!singleton.contains_interval(&closed));
        }

        #[test]
        fn contains_open_singleton() {
            let open = IntervalOpen::new(0.0, 2.0);
            let singleton = IntervalSingleton::new(1.0);
            assert!(open.contains_interval(&singleton));
            assert!(!singleton.contains_interval(&open));
            let singleton_out = IntervalSingleton::new(0.0);
            assert!(!open.contains_interval(&singleton_out));
        }

        #[test]
        fn contains_finite_positive_length_finite_length() {
            let closed = IntervalFinitePositiveLength::Closed(IntervalClosed::new(0.0, 2.0));
            let finite = IntervalFiniteLength::PositiveLength(
                IntervalFinitePositiveLength::Closed(IntervalClosed::new(0.5, 1.5)),
            );
            assert!(closed.contains_interval(&finite));
            assert!(!finite.contains_interval(&closed));
        }

        #[test]
        fn contains_finite_length_singleton() {
            let finite = IntervalFiniteLength::PositiveLength(
                IntervalFinitePositiveLength::Closed(IntervalClosed::new(0.0, 2.0)),
            );
            let singleton = IntervalFiniteLength::ZeroLength(IntervalSingleton::new(1.0));
            assert!(finite.contains_interval(&singleton));
            assert!(!singleton.contains_interval(&finite));
        }

        #[test]
        fn contains_infinite_length_finite_length() {
            let inf = IntervalInfiniteLength::LowerUnboundedUpperUnbounded(
                IntervalLowerUnboundedUpperUnbounded::new(),
            );
            let finite = IntervalFiniteLength::PositiveLength(
                IntervalFinitePositiveLength::Closed(IntervalClosed::new(-100.0, 100.0)),
            );
            assert!(inf.contains_interval(&finite));
            assert!(!finite.contains_interval(&inf));
        }

        #[test]
        fn contains_enum_variants() {
            let closed = Interval::FiniteLength(IntervalFiniteLength::PositiveLength(
                IntervalFinitePositiveLength::Closed(IntervalClosed::new(0.0, 2.0)),
            ));
            let open = Interval::FiniteLength(IntervalFiniteLength::PositiveLength(
                IntervalFinitePositiveLength::Open(IntervalOpen::new(0.5, 1.5)),
            ));
            assert!(closed.contains_interval(&open));
            assert!(!open.contains_interval(&closed));

            let inf =
                Interval::InfiniteLength(IntervalInfiniteLength::LowerUnboundedUpperUnbounded(
                    IntervalLowerUnboundedUpperUnbounded::new(),
                ));
            assert!(inf.contains_interval(&closed));
            assert!(!closed.contains_interval(&inf));
        }

        #[test]
        fn contains_subinterval_first_not_first() {
            let first =
                SubIntervalInPartition::<IntervalClosed<f64>>::First(IntervalClosed::new(0.0, 2.0));
            let not_first = SubIntervalInPartition::<IntervalClosed<f64>>::Last(
                IntervalLowerOpenUpperClosed::new(0.0, 2.0),
            );
            assert!(first.contains_interval(&not_first));
            assert!(!not_first.contains_interval(&first));
        }

        #[test]
        fn contains_subinterval_enum() {
            let s1 =
                SubIntervalInPartition::<IntervalClosed<f64>>::First(IntervalClosed::new(0.0, 2.0));
            let s2 =
                SubIntervalInPartition::<IntervalClosed<f64>>::First(IntervalClosed::new(0.5, 1.5));
            assert!(s1.contains_interval(&s2));
            assert!(!s2.contains_interval(&s1));
            let s3 = SubIntervalInPartition::<IntervalClosed<f64>>::Last(
                IntervalLowerOpenUpperClosed::new(0.5, 2.0),
            );
            assert!(s1.contains_interval(&s3));
            assert!(!s3.contains_interval(&s1));
        }
    }

    mod contains_point {
        use super::*;

        #[test]
        fn interval_closed_contains_point() {
            let interval = IntervalClosed::new(1.0, 2.0);
            assert!(interval.contains_point(&1.0));
            assert!(interval.contains_point(&2.0));
            assert!(interval.contains_point(&1.5));
            assert!(!interval.contains_point(&0.9));
            assert!(!interval.contains_point(&2.1));
        }

        #[test]
        fn interval_open_contains_point() {
            let interval = IntervalOpen::new(1.0, 2.0);
            assert!(!interval.contains_point(&1.0));
            assert!(!interval.contains_point(&2.0));
            assert!(interval.contains_point(&1.5));
        }

        #[test]
        fn interval_lower_open_upper_closed_contains_point() {
            let interval = IntervalLowerOpenUpperClosed::new(1.0, 2.0);
            assert!(!interval.contains_point(&1.0));
            assert!(interval.contains_point(&2.0));
            assert!(interval.contains_point(&1.5));
        }

        #[test]
        fn interval_lower_closed_upper_open_contains_point() {
            let interval = IntervalLowerClosedUpperOpen::new(1.0, 2.0);
            assert!(interval.contains_point(&1.0));
            assert!(!interval.contains_point(&2.0));
            assert!(interval.contains_point(&1.5));
        }

        #[test]
        fn interval_singleton_contains_point() {
            let interval = IntervalSingleton::new(1.0);
            assert!(interval.contains_point(&1.0));
            assert!(!interval.contains_point(&0.0));
        }

        #[test]
        fn interval_lower_closed_upper_unbounded_contains_point() {
            let interval = IntervalLowerClosedUpperUnbounded::new(1.0);
            assert!(interval.contains_point(&1.0));
            assert!(interval.contains_point(&100.0));
            assert!(!interval.contains_point(&0.9));
        }

        #[test]
        fn interval_lower_open_upper_unbounded_contains_point() {
            let interval = IntervalLowerOpenUpperUnbounded::new(1.0);
            assert!(!interval.contains_point(&1.0));
            assert!(interval.contains_point(&1.00001));

            let interval = IntervalInfiniteLength::LowerOpenUpperUnbounded(interval);
            assert!(!interval.contains_point(&1.0));
            assert!(interval.contains_point(&1.00001));
        }

        #[test]
        fn interval_lower_unbounded_upper_closed_contains_point() {
            let interval = IntervalLowerUnboundedUpperClosed::new(2.0);
            assert!(interval.contains_point(&2.0));
            assert!(interval.contains_point(&-100.0));
            assert!(!interval.contains_point(&2.1));
        }

        #[test]
        fn interval_lower_unbounded_upper_open_contains_point() {
            let interval = IntervalLowerUnboundedUpperOpen::new(2.0);
            assert!(!interval.contains_point(&2.0));
            assert!(interval.contains_point(&-100.0));
            assert!(interval.contains_point(&1.99999));

            let interval = IntervalInfiniteLength::LowerUnboundedUpperOpen(interval);
            assert!(!interval.contains_point(&2.0));
            assert!(interval.contains_point(&-100.0));
            assert!(interval.contains_point(&1.99999));
        }

        #[test]
        fn interval_finite_positive_length_trait() {
            let interval = IntervalClosed::new(1.0, 2.0);
            assert_eq!(interval.length().into_inner(), 1.0);
            assert_eq!(interval.mid_point(), 1.5);
            assert!(!interval.is_symmetric());

            let interval = IntervalClosed::new(-1.0, 1.0);
            assert!(interval.is_symmetric());
        }

        #[test]
        fn subinterval_in_partition_contains_point() {
            let closed = IntervalClosed::new(0.0, 1.0);
            let not_first = IntervalLowerOpenUpperClosed::new(1.0, 2.0);
            let s1 = SubIntervalInPartition::<IntervalClosed<f64>>::First(closed);
            let s2 = SubIntervalInPartition::<IntervalClosed<f64>>::Last(not_first);

            assert!(s1.contains_point(&0.0));
            assert!(s1.contains_point(&1.0));
            assert!(!s1.contains_point(&-1.0));
            assert!(s2.contains_point(&2.0));
            assert!(!s2.contains_point(&1.0));
        }

        #[test]
        fn interval_finite_length_contains_point() {
            let closed = IntervalClosed::new(1.0, 2.0);
            let singleton = IntervalSingleton::new(1.5);
            let finite =
                IntervalFiniteLength::PositiveLength(IntervalFinitePositiveLength::Closed(closed));
            let zero = IntervalFiniteLength::ZeroLength(singleton);
            assert!(finite.contains_point(&1.5));
            assert!(zero.contains_point(&1.5));
            assert!(!zero.contains_point(&2.0));
        }

        #[test]
        fn interval_enum_contains_point() {
            let closed = IntervalClosed::new(1.0, 2.0);
            let finite = Interval::FiniteLength(IntervalFiniteLength::PositiveLength(
                IntervalFinitePositiveLength::Closed(closed),
            ));
            let inf =
                Interval::InfiniteLength(IntervalInfiniteLength::LowerUnboundedUpperUnbounded(
                    IntervalLowerUnboundedUpperUnbounded::new(),
                ));
            assert!(finite.contains_point(&1.5));
            assert!(inf.contains_point(&1000.0));
        }
    }

    mod tests_has_lower_bound {
        use super::*;
        use num_valid::RealNative64StrictFiniteInDebug;
        use try_create::TryNew;

        type Real = RealNative64StrictFiniteInDebug;

        #[test]
        fn test_is_lower_bound_closed_for_closed_intervals() {
            // Test IntervalClosed [a, b] - should have closed lower bound
            let closed_interval =
                IntervalClosed::new(Real::try_new(-2.0).unwrap(), Real::try_new(3.0).unwrap());
            assert!(closed_interval.is_lower_bound_closed());
            assert!(!closed_interval.is_lower_bound_open());

            // Test with zero bounds
            let zero_interval =
                IntervalClosed::new(Real::try_new(0.0).unwrap(), Real::try_new(1.0).unwrap());
            assert!(zero_interval.is_lower_bound_closed());
            assert!(!zero_interval.is_lower_bound_open());

            // Test with negative bounds
            let negative_interval =
                IntervalClosed::new(Real::try_new(-5.0).unwrap(), Real::try_new(-1.0).unwrap());
            assert!(negative_interval.is_lower_bound_closed());
            assert!(!negative_interval.is_lower_bound_open());
        }

        #[test]
        fn test_is_lower_bound_closed_for_open_intervals() {
            // Test IntervalOpen (a, b) - should have open lower bound
            let open_interval =
                IntervalOpen::new(Real::try_new(-1.0).unwrap(), Real::try_new(1.0).unwrap());
            assert!(!open_interval.is_lower_bound_closed());
            assert!(open_interval.is_lower_bound_open());

            // Test with zero bounds
            let zero_interval =
                IntervalOpen::new(Real::try_new(0.0).unwrap(), Real::try_new(2.0).unwrap());
            assert!(!zero_interval.is_lower_bound_closed());
            assert!(zero_interval.is_lower_bound_open());

            // Test with positive bounds
            let positive_interval =
                IntervalOpen::new(Real::try_new(1.0).unwrap(), Real::try_new(5.0).unwrap());
            assert!(!positive_interval.is_lower_bound_closed());
            assert!(positive_interval.is_lower_bound_open());
        }

        #[test]
        fn test_is_lower_bound_closed_for_half_open_intervals() {
            // Test IntervalLowerClosedUpperOpen [a, b) - should have closed lower bound
            let left_closed = IntervalLowerClosedUpperOpen::new(
                Real::try_new(2.0).unwrap(),
                Real::try_new(7.0).unwrap(),
            );
            assert!(left_closed.is_lower_bound_closed());
            assert!(!left_closed.is_lower_bound_open());

            // Test IntervalLowerOpenUpperClosed (a, b] - should have open lower bound
            let left_open = IntervalLowerOpenUpperClosed::new(
                Real::try_new(1.0).unwrap(),
                Real::try_new(4.0).unwrap(),
            );
            assert!(!left_open.is_lower_bound_closed());
            assert!(left_open.is_lower_bound_open());
        }

        #[test]
        fn test_is_lower_bound_closed_for_unbounded_intervals() {
            // Test IntervalLowerClosedUpperUnbounded [a, +∞) - should have closed lower bound
            let lower_closed_unbounded =
                IntervalLowerClosedUpperUnbounded::new(Real::try_new(0.0).unwrap());
            assert!(lower_closed_unbounded.is_lower_bound_closed());
            assert!(!lower_closed_unbounded.is_lower_bound_open());

            // Test IntervalLowerOpenUpperUnbounded (a, +∞) - should have open lower bound
            let lower_open_unbounded =
                IntervalLowerOpenUpperUnbounded::new(Real::try_new(5.0).unwrap());
            assert!(!lower_open_unbounded.is_lower_bound_closed());
            assert!(lower_open_unbounded.is_lower_bound_open());

            // Test with negative lower bounds
            let negative_unbounded =
                IntervalLowerClosedUpperUnbounded::new(Real::try_new(-10.0).unwrap());
            assert!(negative_unbounded.is_lower_bound_closed());
            assert!(!negative_unbounded.is_lower_bound_open());
        }

        #[test]
        fn test_is_lower_bound_open_complement_property() {
            // Test that is_lower_bound_open() is always the logical complement of is_lower_bound_closed()

            let test_intervals: Vec<Box<dyn GetLowerBoundValue<LowerBoundValue = Real>>> = vec![
                Box::new(IntervalClosed::new(
                    Real::try_new(0.0).unwrap(),
                    Real::try_new(1.0).unwrap(),
                )),
                Box::new(IntervalOpen::new(
                    Real::try_new(0.0).unwrap(),
                    Real::try_new(1.0).unwrap(),
                )),
                Box::new(IntervalLowerClosedUpperOpen::new(
                    Real::try_new(0.0).unwrap(),
                    Real::try_new(1.0).unwrap(),
                )),
                Box::new(IntervalLowerOpenUpperClosed::new(
                    Real::try_new(0.0).unwrap(),
                    Real::try_new(1.0).unwrap(),
                )),
                Box::new(IntervalLowerClosedUpperUnbounded::new(
                    Real::try_new(0.0).unwrap(),
                )),
                Box::new(IntervalLowerOpenUpperUnbounded::new(
                    Real::try_new(0.0).unwrap(),
                )),
            ];

            for interval in test_intervals {
                let is_closed = interval.is_lower_bound_closed();
                let is_open = interval.is_lower_bound_open();

                // The methods should be logical complements
                assert_eq!(
                    is_open, !is_closed,
                    "is_lower_bound_open() should be the complement of is_lower_bound_closed() for interval: {:?}",
                    interval
                );

                // Exactly one should be true
                assert!(
                    is_closed ^ is_open,
                    "Exactly one of is_lower_bound_closed() or is_lower_bound_open() should be true for interval: {:?}",
                    interval
                );
            }
        }

        #[test]
        fn test_boundary_consistency_with_point_containment() {
            // Test that boundary closure/openness is consistent with point containment

            let closed_interval =
                IntervalClosed::new(Real::try_new(1.0).unwrap(), Real::try_new(3.0).unwrap());
            assert!(closed_interval.is_lower_bound_closed());
            assert!(closed_interval.contains_point(closed_interval.lower_bound_value()));

            let open_interval =
                IntervalOpen::new(Real::try_new(1.0).unwrap(), Real::try_new(3.0).unwrap());
            assert!(open_interval.is_lower_bound_open());
            assert!(!open_interval.contains_point(open_interval.lower_bound_value()));

            let left_closed = IntervalLowerClosedUpperOpen::new(
                Real::try_new(2.0).unwrap(),
                Real::try_new(5.0).unwrap(),
            );
            assert!(left_closed.is_lower_bound_closed());
            assert!(left_closed.contains_point(left_closed.lower_bound_value()));

            let left_open = IntervalLowerOpenUpperClosed::new(
                Real::try_new(2.0).unwrap(),
                Real::try_new(5.0).unwrap(),
            );
            assert!(left_open.is_lower_bound_open());
            assert!(!left_open.contains_point(left_open.lower_bound_value()));
        }

        #[test]
        fn test_with_different_scalar_types() {
            // Test with raw f64
            let f64_closed = IntervalClosed::new(0.0_f64, 1.0_f64);
            assert!(f64_closed.is_lower_bound_closed());
            assert!(!f64_closed.is_lower_bound_open());

            let f64_open = IntervalOpen::new(0.0_f64, 1.0_f64);
            assert!(!f64_open.is_lower_bound_closed());
            assert!(f64_open.is_lower_bound_open());

            // Test with validated types
            use num_valid::RealNative64StrictFinite;
            type SafeReal = RealNative64StrictFinite;

            let safe_closed = IntervalClosed::new(
                SafeReal::try_new(0.0).unwrap(),
                SafeReal::try_new(1.0).unwrap(),
            );
            assert!(safe_closed.is_lower_bound_closed());
            assert!(!safe_closed.is_lower_bound_open());

            let safe_open = IntervalOpen::new(
                SafeReal::try_new(0.0).unwrap(),
                SafeReal::try_new(1.0).unwrap(),
            );
            assert!(!safe_open.is_lower_bound_closed());
            assert!(safe_open.is_lower_bound_open());
        }

        #[test]
        fn test_boundary_behavior_at_limits() {
            // Test with very small intervals
            let tiny_closed = IntervalClosed::new(
                Real::try_new(1.0).unwrap(),
                Real::try_new(1.0 + f64::EPSILON).unwrap(),
            );
            assert!(tiny_closed.is_lower_bound_closed());
            assert!(!tiny_closed.is_lower_bound_open());

            let tiny_open = IntervalOpen::new(
                Real::try_new(1.0).unwrap(),
                Real::try_new(1.0 + f64::EPSILON).unwrap(),
            );
            assert!(!tiny_open.is_lower_bound_closed());
            assert!(tiny_open.is_lower_bound_open());

            // Test with extreme values
            let extreme_closed =
                IntervalLowerClosedUpperUnbounded::new(Real::try_new(f64::MIN).unwrap());
            assert!(extreme_closed.is_lower_bound_closed());
            assert!(!extreme_closed.is_lower_bound_open());

            let extreme_open =
                IntervalLowerOpenUpperUnbounded::new(Real::try_new(f64::MAX / 2.0).unwrap());
            assert!(!extreme_open.is_lower_bound_closed());
            assert!(extreme_open.is_lower_bound_open());
        }

        #[test]
        fn test_generic_programming_pattern() {
            // Test using the methods in generic functions
            fn analyze_lower_boundary<I, T>(interval: &I) -> (bool, bool)
            where
                I: GetLowerBoundValue<LowerBoundValue = T>,
                T: Clone,
            {
                (
                    interval.is_lower_bound_closed(),
                    interval.is_lower_bound_open(),
                )
            }

            let closed =
                IntervalClosed::new(Real::try_new(0.0).unwrap(), Real::try_new(1.0).unwrap());
            let open = IntervalOpen::new(Real::try_new(0.0).unwrap(), Real::try_new(1.0).unwrap());
            let left_closed = IntervalLowerClosedUpperOpen::new(
                Real::try_new(0.0).unwrap(),
                Real::try_new(1.0).unwrap(),
            );
            let left_open = IntervalLowerOpenUpperClosed::new(
                Real::try_new(0.0).unwrap(),
                Real::try_new(1.0).unwrap(),
            );

            assert_eq!(analyze_lower_boundary(&closed), (true, false));
            assert_eq!(analyze_lower_boundary(&open), (false, true));
            assert_eq!(analyze_lower_boundary(&left_closed), (true, false));
            assert_eq!(analyze_lower_boundary(&left_open), (false, true));
        }

        #[test]
        fn test_finite_positive_length_intervals() {
            // Test all variants of IntervalFinitePositiveLength
            let closed = IntervalFinitePositiveLength::Closed(IntervalClosed::new(
                Real::try_new(0.0).unwrap(),
                Real::try_new(1.0).unwrap(),
            ));
            assert!(closed.is_lower_bound_closed());
            assert!(!closed.is_lower_bound_open());

            let open = IntervalFinitePositiveLength::Open(IntervalOpen::new(
                Real::try_new(0.0).unwrap(),
                Real::try_new(1.0).unwrap(),
            ));
            assert!(!open.is_lower_bound_closed());
            assert!(open.is_lower_bound_open());

            let left_open_upper_closed = IntervalFinitePositiveLength::LowerOpenUpperClosed(
                IntervalLowerOpenUpperClosed::new(
                    Real::try_new(0.0).unwrap(),
                    Real::try_new(1.0).unwrap(),
                ),
            );
            assert!(!left_open_upper_closed.is_lower_bound_closed());
            assert!(left_open_upper_closed.is_lower_bound_open());

            let left_closed_upper_open = IntervalFinitePositiveLength::LowerClosedUpperOpen(
                IntervalLowerClosedUpperOpen::new(
                    Real::try_new(0.0).unwrap(),
                    Real::try_new(1.0).unwrap(),
                ),
            );
            assert!(left_closed_upper_open.is_lower_bound_closed());
            assert!(!left_closed_upper_open.is_lower_bound_open());
        }

        #[test]
        fn test_edge_case_zero_bounds() {
            // Test intervals with zero as lower bound
            let zero_closed =
                IntervalClosed::new(Real::try_new(0.0).unwrap(), Real::try_new(1.0).unwrap());
            assert!(zero_closed.is_lower_bound_closed());
            assert!(!zero_closed.is_lower_bound_open());
            assert!(zero_closed.contains_point(&Real::try_new(0.0).unwrap()));

            let zero_open =
                IntervalOpen::new(Real::try_new(0.0).unwrap(), Real::try_new(1.0).unwrap());
            assert!(!zero_open.is_lower_bound_closed());
            assert!(zero_open.is_lower_bound_open());
            assert!(!zero_open.contains_point(&Real::try_new(0.0).unwrap()));
        }

        #[test]
        fn test_documentation_examples() {
            // Test examples from the documentation

            // Closed interval includes lower bound
            let closed =
                IntervalClosed::new(Real::try_new(0.0).unwrap(), Real::try_new(1.0).unwrap());
            assert!(closed.is_lower_bound_closed()); // [0, 1] has closed lower bound
            assert!(!closed.is_lower_bound_open()); // Complement

            // Open interval excludes lower bound
            let open = IntervalOpen::new(Real::try_new(0.0).unwrap(), Real::try_new(1.0).unwrap());
            assert!(!open.is_lower_bound_closed()); // (0, 1) has open lower bound
            assert!(open.is_lower_bound_open()); // [0, 1] has closed lower bound

            // Half-open intervals
            let half_open_left_closed = IntervalLowerClosedUpperOpen::new(
                Real::try_new(0.0).unwrap(),
                Real::try_new(1.0).unwrap(),
            );
            assert!(half_open_left_closed.is_lower_bound_closed()); // [0, 1) has closed lower bound
            assert!(!half_open_left_closed.is_lower_bound_open()); // Complement

            let half_open_left_open = IntervalLowerOpenUpperClosed::new(
                Real::try_new(0.0).unwrap(),
                Real::try_new(1.0).unwrap(),
            );
            assert!(!half_open_left_open.is_lower_bound_closed()); // (0, 1] has open lower bound
            assert!(half_open_left_open.is_lower_bound_open()); // Complement
        }
    }

    #[test]
    fn test_interval_conversion_errors() {
        let singleton = IntervalSingleton::new(1.0);
        let finite = IntervalFiniteLength::ZeroLength(singleton);
        let interval = Interval::FiniteLength(finite);

        let result = IntervalFinitePositiveLength::try_from(interval.clone());
        assert!(matches!(
            result,
            Err(ErrorsIntervalConversion::NotIntervalPositiveLength {
                interval: Interval::FiniteLength(IntervalFiniteLength::ZeroLength(..)),
                ..
            })
        ));

        let result = IntervalClosed::try_from(interval.clone());
        assert!(matches!(
            result,
            Err(ErrorsIntervalConversion::NotIntervalPositiveLength {
                interval: Interval::FiniteLength(IntervalFiniteLength::ZeroLength(..)),
                ..
            })
        ));

        let inf =
            Interval::<f64>::InfiniteLength(IntervalInfiniteLength::LowerUnboundedUpperUnbounded(
                IntervalLowerUnboundedUpperUnbounded::new(),
            ));
        let result = IntervalFinitePositiveLength::try_from(inf.clone());
        assert!(matches!(
            result,
            Err(ErrorsIntervalConversion::NotIntervalPositiveLength {
                interval: Interval::InfiniteLength(
                    IntervalInfiniteLength::LowerUnboundedUpperUnbounded(_)
                ),
                ..
            })
        ));

        let result = IntervalClosed::try_from(inf.clone());
        assert!(matches!(
            result,
            Err(ErrorsIntervalConversion::NotIntervalPositiveLength {
                interval: Interval::InfiniteLength(
                    IntervalInfiniteLength::LowerUnboundedUpperUnbounded(_)
                ),
                ..
            })
        ));
    }

    mod constructors {
        use super::*;

        #[test]
        #[should_panic(expected = "Invalid interval bounds for the closed interval!")]
        #[cfg(debug_assertions)]
        fn interval_closed_new_panics_on_invalid_bounds() {
            let _ = IntervalClosed::new(2.0, 1.0);
        }

        #[test]
        fn open() {
            let lower_bound = 0.0;
            let upper_bound = 1.0;
            let interval = IntervalOpen::new(lower_bound, upper_bound);
            assert_eq!(interval.lower_bound_value(), &0.);
            assert_eq!(interval.upper_bound_value(), &1.);
            assert_eq!(interval.length().into_inner(), 1.);
            assert_eq!(interval.mid_point(), 0.5);
            assert!(interval.contains_point(&0.5));

            assert_eq!(
                interval.clone().interior(),
                IntervalOpen::new(lower_bound, upper_bound)
            );
            assert_eq!(
                interval.clone().closure(),
                IntervalClosed::new(lower_bound, upper_bound)
            );

            let (lb, ub) = interval.into_bounds_pair();
            assert_eq!(lb, lower_bound);
            assert_eq!(ub, upper_bound);
        }

        #[test]
        fn closed_01() {
            let lower_bound = 0.0;
            let upper_bound = 1.0;
            let interval = IntervalClosed::new(lower_bound, upper_bound);
            assert_eq!(interval.lower_bound_value(), &0.);
            assert_eq!(interval.upper_bound_value(), &1.);
            assert_eq!(interval.length().into_inner(), 1.);
            assert_eq!(interval.mid_point(), 0.5);

            let x = 0.5;
            assert!(interval.contains_point(&x));

            assert_eq!(
                interval.clone().interior(),
                IntervalOpen::new(lower_bound, upper_bound)
            );
            assert_eq!(
                interval.clone().closure(),
                IntervalClosed::new(lower_bound, upper_bound)
            );

            let (lb, ub) = interval.into_bounds_pair();
            assert_eq!(lb, lower_bound);
            assert_eq!(ub, upper_bound);
        }

        #[test]
        #[should_panic]
        #[cfg(debug_assertions)]
        fn closed_02() {
            let lower_bound = 1.0;
            let upper_bound = 0.0;
            let _interval = IntervalClosed::new(lower_bound, upper_bound);
        }

        #[test]
        pub fn left_half_open() {
            let lower_bound = 0.0;
            let upper_bound = 1.0;
            let interval = IntervalLowerOpenUpperClosed::new(lower_bound, upper_bound);
            assert_eq!(interval.lower_bound_value(), &0.);
            assert_eq!(interval.upper_bound_value(), &1.);
            assert_eq!(interval.length().into_inner(), 1.);
            assert_eq!(interval.mid_point(), 0.5);

            let x = 0.5;
            assert!(interval.contains_point(&x));

            assert_eq!(
                interval.clone().interior(),
                IntervalOpen::new(lower_bound, upper_bound)
            );
            assert_eq!(
                interval.clone().closure(),
                IntervalClosed::new(lower_bound, upper_bound)
            );

            let (lb, ub) = interval.into_bounds_pair();
            assert_eq!(lb, lower_bound);
            assert_eq!(ub, upper_bound);
        }

        #[test]
        pub fn right_half_open() {
            let lower_bound = 0.0;
            let upper_bound = 1.0;
            let interval = IntervalLowerClosedUpperOpen::new(lower_bound, upper_bound);
            assert_eq!(interval.lower_bound_value(), &0.);
            assert_eq!(interval.upper_bound_value(), &1.);
            assert_eq!(interval.length().into_inner(), 1.);
            assert_eq!(interval.mid_point(), 0.5);

            let x = 0.5;
            assert!(interval.contains_point(&x));

            assert_eq!(
                interval.clone().interior(),
                IntervalOpen::new(lower_bound, upper_bound)
            );
            assert_eq!(
                interval.clone().closure(),
                IntervalClosed::new(lower_bound, upper_bound)
            );

            let (lb, ub) = interval.into_bounds_pair();
            assert_eq!(lb, lower_bound);
            assert_eq!(ub, upper_bound);
        }

        #[test]
        fn unbounded_open_left() {
            let lower_bound = 0.0;
            let interval = IntervalLowerOpenUpperUnbounded::new(lower_bound);
            assert_eq!(interval.lower_bound_value(), &0.);

            let x = 0.5;
            assert!(interval.contains_point(&x));
        }

        #[test]
        fn unbounded_open_right() {
            let upper_bound = 0.0;
            let interval = IntervalLowerUnboundedUpperOpen::new(upper_bound);
            assert_eq!(interval.upper_bound_value(), &0.);

            let x = -0.5;
            assert!(interval.contains_point(&x));
        }

        #[test]
        fn unbounded_closed_left() {
            let lower_bound = 0.;
            let interval = IntervalLowerClosedUpperUnbounded::new(lower_bound);
            assert_eq!(interval.lower_bound_value(), &0.);

            let x = 0.5;
            assert!(
                interval.contains_point(&x),
                "The interval {interval:?} does not contain the point {x}"
            );
        }

        #[test]
        fn unbounded_closed_right() {
            let upper_bound = 0.;
            let interval = IntervalLowerUnboundedUpperClosed::new(upper_bound);
            assert_eq!(interval.upper_bound_value(), &0.);

            let x = -0.5;
            assert!(interval.contains_point(&x));
        }

        #[test]
        fn singleton() {
            let value = 0.;
            let interval = IntervalSingleton::new(value);
            assert_eq!(interval.value(), &0.);

            let x = 0.;
            assert!(interval.contains_point(&x));
        }

        #[test]
        #[cfg(debug_assertions)]
        #[should_panic]
        fn interval_singleton_try_new_invalid() {
            let nan = f64::NAN;
            let _ = IntervalSingleton::new(nan);
        }

        #[test]
        fn interval_closed_try_new_invalid() {
            let res = IntervalClosed::try_new(2.0, 1.0);
            assert!(res.is_err());
        }

        #[test]
        fn interval_open_try_new_invalid() {
            let res = IntervalOpen::try_new(2.0, 1.0);
            assert!(res.is_err());
        }

        #[test]
        fn interval_lower_open_upper_closed_try_new_invalid() {
            let res = IntervalLowerOpenUpperClosed::try_new(2.0, 1.0);
            assert!(res.is_err());
        }

        #[test]
        fn interval_lower_closed_upper_open_try_new_invalid() {
            let res = IntervalLowerClosedUpperOpen::try_new(2.0, 1.0);
            assert!(res.is_err());
        }

        #[test]
        #[cfg(debug_assertions)]
        #[should_panic]
        fn interval_lower_closed_upper_unbounded_try_new_invalid() {
            let nan = f64::NAN;
            let _ = IntervalLowerClosedUpperUnbounded::new(nan);
        }

        #[test]
        #[cfg(debug_assertions)]
        #[should_panic]
        fn interval_lower_open_upper_unbounded_try_new_invalid() {
            let nan = f64::NAN;
            let _ = IntervalLowerOpenUpperUnbounded::new(nan);
        }

        #[test]
        #[cfg(debug_assertions)]
        #[should_panic]
        fn interval_lower_unbounded_upper_closed_try_new_invalid() {
            let nan = f64::NAN;
            let _ = IntervalLowerUnboundedUpperClosed::new(nan);
        }

        #[test]
        #[cfg(debug_assertions)]
        #[should_panic]
        fn interval_lower_unbounded_upper_open_try_new_invalid() {
            let nan = f64::NAN;
            let _ = IntervalLowerUnboundedUpperOpen::new(nan);
        }

        // Tests for from_bounds and try_from_bounds methods

        /// Test IntervalClosed::from_bounds with valid bounds
        #[test]
        fn from_bounds_closed_valid() {
            use crate::bounds::{LowerBoundClosed, UpperBoundClosed};

            let lower = LowerBoundClosed::new(0.0);
            let upper = UpperBoundClosed::new(5.0);

            let interval = IntervalClosed::from_bounds(lower, upper);

            assert_eq!(interval.lower_bound_value(), &0.0);
            assert_eq!(interval.upper_bound_value(), &5.0);
            assert!(interval.contains_point(&0.0));
            assert!(interval.contains_point(&5.0));
            assert!(interval.contains_point(&2.5));
        }

        /// Test IntervalOpen::from_bounds with valid bounds
        #[test]
        fn from_bounds_open_valid() {
            use crate::bounds::{LowerBoundOpen, UpperBoundOpen};

            let lower = LowerBoundOpen::new(0.0);
            let upper = UpperBoundOpen::new(5.0);

            let interval = IntervalOpen::from_bounds(lower, upper);

            assert_eq!(interval.lower_bound_value(), &0.0);
            assert_eq!(interval.upper_bound_value(), &5.0);
            assert!(!interval.contains_point(&0.0));
            assert!(!interval.contains_point(&5.0));
            assert!(interval.contains_point(&2.5));
        }

        /// Test IntervalLowerClosedUpperOpen::from_bounds with valid bounds
        #[test]
        fn from_bounds_half_open_valid() {
            use crate::bounds::{LowerBoundClosed, UpperBoundOpen};

            let lower = LowerBoundClosed::new(1.0);
            let upper = UpperBoundOpen::new(10.0);

            let interval = IntervalLowerClosedUpperOpen::from_bounds(lower, upper);

            assert_eq!(interval.lower_bound_value(), &1.0);
            assert_eq!(interval.upper_bound_value(), &10.0);
            assert!(interval.contains_point(&1.0));
            assert!(!interval.contains_point(&10.0));
            assert!(interval.contains_point(&5.0));
        }

        /// Test IntervalLowerOpenUpperClosed::from_bounds with valid bounds
        #[test]
        fn from_bounds_lower_open_upper_closed_valid() {
            use crate::bounds::{LowerBoundOpen, UpperBoundClosed};

            let lower = LowerBoundOpen::new(-5.0);
            let upper = UpperBoundClosed::new(5.0);

            let interval = IntervalLowerOpenUpperClosed::from_bounds(lower, upper);

            assert_eq!(interval.lower_bound_value(), &-5.0);
            assert_eq!(interval.upper_bound_value(), &5.0);
            assert!(!interval.contains_point(&-5.0));
            assert!(interval.contains_point(&5.0));
            assert!(interval.contains_point(&0.0));
        }

        /// Test try_from_bounds with valid bounds returns Ok
        #[test]
        fn try_from_bounds_closed_valid() {
            use crate::bounds::{LowerBoundClosed, UpperBoundClosed};

            let lower = LowerBoundClosed::new(0.0);
            let upper = UpperBoundClosed::new(10.0);

            let result = IntervalClosed::try_from_bounds(lower, upper);

            assert!(result.is_ok());
            let interval = result.unwrap();
            assert_eq!(interval.lower_bound_value(), &0.0);
            assert_eq!(interval.upper_bound_value(), &10.0);
        }

        /// Test try_from_bounds with invalid bounds (lower >= upper) returns Err
        #[test]
        fn try_from_bounds_closed_invalid_lower_greater() {
            use crate::bounds::{LowerBoundClosed, UpperBoundClosed};

            let lower = LowerBoundClosed::new(10.0);
            let upper = UpperBoundClosed::new(5.0);

            let result = IntervalClosed::try_from_bounds(lower, upper);

            assert!(result.is_err());
            match result {
                Err(ErrorsIntervalConstruction::LowerBoundGreaterOrEqualThanUpperBound {
                    lower_bound,
                    upper_bound,
                    ..
                }) => {
                    assert_eq!(lower_bound, 10.0);
                    assert_eq!(upper_bound, 5.0);
                }
                _ => panic!("Expected LowerBoundGreaterOrEqualThanUpperBound error"),
            }
        }

        /// Test try_from_bounds with equal bounds returns Err (degenerate interval)
        #[test]
        fn try_from_bounds_open_invalid_equal_bounds() {
            use crate::bounds::{LowerBoundOpen, UpperBoundOpen};

            let lower = LowerBoundOpen::new(5.0);
            let upper = UpperBoundOpen::new(5.0);

            let result = IntervalOpen::try_from_bounds(lower, upper);

            assert!(result.is_err());
            match result {
                Err(ErrorsIntervalConstruction::LowerBoundGreaterOrEqualThanUpperBound {
                    lower_bound,
                    upper_bound,
                    ..
                }) => {
                    assert_eq!(lower_bound, 5.0);
                    assert_eq!(upper_bound, 5.0);
                }
                _ => panic!("Expected LowerBoundGreaterOrEqualThanUpperBound error"),
            }
        }

        /// Test from_bounds with different boundary types
        #[test]
        fn from_bounds_mixed_types() {
            use crate::bounds::{
                LowerBoundClosed, LowerBoundOpen, UpperBoundClosed, UpperBoundOpen,
            };

            // [0, 10)
            let interval1 = IntervalLowerClosedUpperOpen::from_bounds(
                LowerBoundClosed::new(0.0),
                UpperBoundOpen::new(10.0),
            );
            assert!(interval1.contains_point(&0.0));
            assert!(!interval1.contains_point(&10.0));

            // (0, 10]
            let interval2 = IntervalLowerOpenUpperClosed::from_bounds(
                LowerBoundOpen::new(0.0),
                UpperBoundClosed::new(10.0),
            );
            assert!(!interval2.contains_point(&0.0));
            assert!(interval2.contains_point(&10.0));
        }

        /// Test from_bounds panics in debug mode with invalid bounds
        #[test]
        #[cfg(debug_assertions)]
        #[should_panic(expected = "Invalid interval bounds")]
        fn from_bounds_closed_panics_on_invalid() {
            use crate::bounds::{LowerBoundClosed, UpperBoundClosed};

            let lower = LowerBoundClosed::new(10.0);
            let upper = UpperBoundClosed::new(5.0);

            let _ = IntervalClosed::from_bounds(lower, upper);
        }

        /// Test from_bounds with very small valid interval
        #[test]
        fn from_bounds_very_small_interval() {
            use crate::bounds::{LowerBoundClosed, UpperBoundClosed};

            let lower = LowerBoundClosed::new(0.0);
            let upper = UpperBoundClosed::new(1e-10);

            let interval = IntervalClosed::from_bounds(lower, upper);

            assert_eq!(interval.lower_bound_value(), &0.0);
            assert_eq!(interval.upper_bound_value(), &1e-10);
            assert!(interval.contains_point(&5e-11));
        }

        /// Test from_bounds with negative bounds
        #[test]
        fn from_bounds_negative_bounds() {
            use crate::bounds::{LowerBoundOpen, UpperBoundOpen};

            let lower = LowerBoundOpen::new(-100.0);
            let upper = UpperBoundOpen::new(-10.0);

            let interval = IntervalOpen::from_bounds(lower, upper);

            assert_eq!(interval.lower_bound_value(), &-100.0);
            assert_eq!(interval.upper_bound_value(), &-10.0);
            assert!(interval.contains_point(&-50.0));
            assert!(!interval.contains_point(&-100.0));
            assert!(!interval.contains_point(&-10.0));
        }

        /// Test try_from_bounds with all interval types
        #[test]
        fn try_from_bounds_all_types() {
            use crate::bounds::{
                LowerBoundClosed, LowerBoundOpen, UpperBoundClosed, UpperBoundOpen,
            };

            // Test all 4 combinations
            let closed = IntervalClosed::try_from_bounds(
                LowerBoundClosed::new(0.0),
                UpperBoundClosed::new(1.0),
            );
            assert!(closed.is_ok());

            let open =
                IntervalOpen::try_from_bounds(LowerBoundOpen::new(0.0), UpperBoundOpen::new(1.0));
            assert!(open.is_ok());

            let half_open1 = IntervalLowerClosedUpperOpen::try_from_bounds(
                LowerBoundClosed::new(0.0),
                UpperBoundOpen::new(1.0),
            );
            assert!(half_open1.is_ok());

            let half_open2 = IntervalLowerOpenUpperClosed::try_from_bounds(
                LowerBoundOpen::new(0.0),
                UpperBoundClosed::new(1.0),
            );
            assert!(half_open2.is_ok());
        }
    }

    mod point_containment {
        use super::*;

        /// Test IntervalLowerClosedUpperOpen::contains_point() with lower bound (included)
        #[test]
        fn contains_point_lower_closed_upper_open_at_lower_bound() {
            let interval = IntervalLowerClosedUpperOpen::new(0.0, 10.0);

            // Lower bound should be included (closed)
            assert!(
                interval.contains_point(&0.0),
                "Lower bound 0.0 should be included in [0, 10)"
            );
        }

        /// Test IntervalLowerClosedUpperOpen::contains_point() with upper bound (excluded)
        #[test]
        fn contains_point_lower_closed_upper_open_at_upper_bound() {
            let interval = IntervalLowerClosedUpperOpen::new(0.0, 10.0);

            // Upper bound should be excluded (open)
            assert!(
                !interval.contains_point(&10.0),
                "Upper bound 10.0 should be excluded from [0, 10)"
            );

            let interval = IntervalFinitePositiveLength::LowerClosedUpperOpen(interval);
            assert!(
                !interval.contains_point(&10.0),
                "Upper bound 10.0 should be excluded from [0, 10)"
            );
        }

        /// Test IntervalLowerClosedUpperOpen::contains_point() with interior point
        #[test]
        fn contains_point_lower_closed_upper_open_interior() {
            let interval = IntervalLowerClosedUpperOpen::new(0.0, 10.0);

            // Interior points should be included
            assert!(
                interval.contains_point(&5.0),
                "Interior point 5.0 should be included in [0, 10)"
            );
            assert!(
                interval.contains_point(&0.1),
                "Point 0.1 just above lower bound should be included"
            );
            assert!(
                interval.contains_point(&9.9),
                "Point 9.9 just below upper bound should be included"
            );
        }

        /// Test IntervalLowerClosedUpperOpen::contains_point() with point below lower bound
        #[test]
        fn contains_point_lower_closed_upper_open_below_lower() {
            let interval = IntervalLowerClosedUpperOpen::new(5.0, 15.0);

            // Points below lower bound should be excluded
            assert!(
                !interval.contains_point(&4.9),
                "Point 4.9 below lower bound should be excluded from [5, 15)"
            );
            assert!(
                !interval.contains_point(&0.0),
                "Point 0.0 well below lower bound should be excluded"
            );
        }

        /// Test IntervalLowerClosedUpperOpen::contains_point() with point above upper bound
        #[test]
        fn contains_point_lower_closed_upper_open_above_upper() {
            let interval = IntervalLowerClosedUpperOpen::new(5.0, 15.0);

            // Points above upper bound should be excluded
            assert!(
                !interval.contains_point(&15.1),
                "Point 15.1 above upper bound should be excluded from [5, 15)"
            );
            assert!(
                !interval.contains_point(&20.0),
                "Point 20.0 well above upper bound should be excluded"
            );
        }

        /// Test IntervalLowerClosedUpperOpen::contains_point() with negative bounds
        #[test]
        fn contains_point_lower_closed_upper_open_negative_bounds() {
            let interval = IntervalLowerClosedUpperOpen::new(-10.0, -5.0);

            // Test with negative interval
            assert!(
                interval.contains_point(&-10.0),
                "Lower bound -10.0 should be included in [-10, -5)"
            );
            assert!(
                !interval.contains_point(&-5.0),
                "Upper bound -5.0 should be excluded from [-10, -5)"
            );
            assert!(
                interval.contains_point(&-7.5),
                "Interior point -7.5 should be included"
            );
            assert!(
                !interval.contains_point(&-4.0),
                "Point -4.0 above upper bound should be excluded"
            );
        }

        /// Test IntervalLowerClosedUpperOpen::contains_point() with very small interval
        #[test]
        fn contains_point_lower_closed_upper_open_tiny_interval() {
            let interval = IntervalLowerClosedUpperOpen::new(0.0, 1e-10);

            // Test with very small interval
            assert!(
                interval.contains_point(&0.0),
                "Lower bound 0.0 should be included"
            );
            assert!(
                !interval.contains_point(&1e-10),
                "Upper bound 1e-10 should be excluded"
            );
            assert!(
                interval.contains_point(&5e-11),
                "Interior point 5e-11 should be included"
            );
        }

        /// Test IntervalLowerClosedUpperOpen::contains_point() with boundary precision
        #[test]
        fn contains_point_lower_closed_upper_open_boundary_precision() {
            let interval = IntervalLowerClosedUpperOpen::new(0.0, 1.0);

            // Test points very close to boundaries
            assert!(
                interval.contains_point(&0.0),
                "Exact lower bound should be included"
            );
            assert!(
                interval.contains_point(&f64::EPSILON),
                "Point just above lower bound should be included"
            );
            assert!(
                !interval.contains_point(&1.0),
                "Exact upper bound should be excluded"
            );
            assert!(
                interval.contains_point(&(1.0 - f64::EPSILON)),
                "Point just below upper bound should be included"
            );
        }

        /// Test IntervalLowerClosedUpperOpen::contains_point() comparison with IntervalClosed
        #[test]
        fn contains_point_lower_closed_upper_open_vs_closed() {
            let half_open = IntervalLowerClosedUpperOpen::new(0.0, 10.0);
            let closed = IntervalClosed::new(0.0, 10.0);

            // Both include lower bound
            assert_eq!(
                half_open.contains_point(&0.0),
                closed.contains_point(&0.0),
                "Both should include lower bound"
            );

            // Differ at upper bound
            assert!(
                !half_open.contains_point(&10.0),
                "Half-open should exclude upper bound"
            );
            assert!(
                closed.contains_point(&10.0),
                "Closed should include upper bound"
            );

            // Both include interior
            assert_eq!(
                half_open.contains_point(&5.0),
                closed.contains_point(&5.0),
                "Both should include interior points"
            );
        }

        /// Test IntervalLowerClosedUpperOpen::contains_point() comparison with IntervalOpen
        #[test]
        fn contains_point_lower_closed_upper_open_vs_open() {
            let half_open = IntervalLowerClosedUpperOpen::new(0.0, 10.0);
            let open = IntervalOpen::new(0.0, 10.0);

            // Differ at lower bound
            assert!(
                half_open.contains_point(&0.0),
                "Half-open should include lower bound"
            );
            assert!(
                !open.contains_point(&0.0),
                "Open should exclude lower bound"
            );

            // Both exclude upper bound
            assert_eq!(
                half_open.contains_point(&10.0),
                open.contains_point(&10.0),
                "Both should exclude upper bound"
            );

            // Both include interior
            assert_eq!(
                half_open.contains_point(&5.0),
                open.contains_point(&5.0),
                "Both should include interior points"
            );
        }

        /// Test IntervalLowerClosedUpperOpen::contains_point() with multiple test points
        #[test]
        fn contains_point_lower_closed_upper_open_multiple_points() {
            let interval = IntervalLowerClosedUpperOpen::new(2.0, 8.0);

            // Test array of points
            let test_points = vec![
                (1.0, false),  // Below
                (2.0, true),   // Lower bound (included)
                (3.0, true),   // Interior
                (5.0, true),   // Interior
                (7.999, true), // Just below upper
                (8.0, false),  // Upper bound (excluded)
                (9.0, false),  // Above
            ];

            for (point, expected) in test_points {
                assert_eq!(
                    interval.contains_point(&point),
                    expected,
                    "Point {} containment in [2, 8) should be {}",
                    point,
                    expected
                );
            }
        }

        /// Test IntervalLowerClosedUpperOpen::contains_point() with unit interval
        #[test]
        fn contains_point_lower_closed_upper_open_unit_interval() {
            let interval = IntervalLowerClosedUpperOpen::new(0.0, 1.0);

            // Standard unit interval [0, 1)
            assert!(interval.contains_point(&0.0), "[0, 1) should include 0.0");
            assert!(interval.contains_point(&0.5), "[0, 1) should include 0.5");
            assert!(!interval.contains_point(&1.0), "[0, 1) should exclude 1.0");
            assert!(
                !interval.contains_point(&-0.1),
                "[0, 1) should exclude -0.1"
            );
            assert!(!interval.contains_point(&1.1), "[0, 1) should exclude 1.1");
        }
    }

    mod sub_interval_in_partition {
        use super::*;
        use num_valid::{RealNative64StrictFiniteInDebug, scalars::PositiveRealScalar};
        use try_create::TryNew;

        type Real = RealNative64StrictFiniteInDebug;

        type ThreeSubIntervalsInPartition = (
            SubIntervalInPartition<IntervalClosed<Real>>,
            SubIntervalInPartition<IntervalClosed<Real>>,
            SubIntervalInPartition<IntervalClosed<Real>>,
        );

        // Helper function to create test intervals
        fn create_test_intervals() -> ThreeSubIntervalsInPartition {
            let first = SubIntervalInPartition::First(IntervalClosed::new(
                Real::try_new(0.0).unwrap(),
                Real::try_new(1.0).unwrap(),
            ));

            let interior = SubIntervalInPartition::Middle(IntervalLowerOpenUpperClosed::new(
                Real::try_new(1.0).unwrap(),
                Real::try_new(2.0).unwrap(),
            ));

            let last = SubIntervalInPartition::Last(IntervalLowerOpenUpperClosed::new(
                Real::try_new(2.0).unwrap(),
                Real::try_new(3.0).unwrap(),
            ));

            (first, interior, last)
        }

        #[test]
        fn test_construction_and_basic_properties() {
            let (first, interior, last) = create_test_intervals();

            // Test that all intervals are constructed correctly
            assert_eq!(first.lower_bound_value(), &Real::try_new(0.0).unwrap());
            assert_eq!(first.upper_bound_value(), &Real::try_new(1.0).unwrap());

            assert_eq!(interior.lower_bound_value(), &Real::try_new(1.0).unwrap());
            assert_eq!(interior.upper_bound_value(), &Real::try_new(2.0).unwrap());

            assert_eq!(last.lower_bound_value(), &Real::try_new(2.0).unwrap());
            assert_eq!(last.upper_bound_value(), &Real::try_new(3.0).unwrap());
        }

        #[test]
        fn test_point_containment() {
            let (first, interior, last) = create_test_intervals();

            // Test first sub-interval [0, 1] - closed on both sides
            assert!(first.contains_point(&Real::try_new(0.0).unwrap())); // Lower bound included
            assert!(first.contains_point(&Real::try_new(0.5).unwrap())); // Interior point
            assert!(first.contains_point(&Real::try_new(1.0).unwrap())); // Upper bound included
            assert!(!first.contains_point(&Real::try_new(-0.1).unwrap())); // Outside interval
            assert!(!first.contains_point(&Real::try_new(1.1).unwrap())); // Outside interval

            // Test interior sub-interval (1, 2] - open on left, closed on right
            assert!(!interior.contains_point(&Real::try_new(1.0).unwrap())); // Lower bound excluded
            assert!(interior.contains_point(&Real::try_new(1.5).unwrap())); // Interior point
            assert!(interior.contains_point(&Real::try_new(2.0).unwrap())); // Upper bound included
            assert!(!interior.contains_point(&Real::try_new(0.9).unwrap())); // Outside interval
            assert!(!interior.contains_point(&Real::try_new(2.1).unwrap())); // Outside interval

            // Test last sub-interval (2, 3] - open on left, closed on right
            assert!(!last.contains_point(&Real::try_new(2.0).unwrap())); // Lower bound excluded
            assert!(last.contains_point(&Real::try_new(2.5).unwrap())); // Interior point
            assert!(last.contains_point(&Real::try_new(3.0).unwrap())); // Upper bound included
            assert!(!last.contains_point(&Real::try_new(1.9).unwrap())); // Outside interval
            assert!(!last.contains_point(&Real::try_new(3.1).unwrap())); // Outside interval
        }

        #[test]
        fn test_interval_containment() {
            let (first, interior, last) = create_test_intervals();

            // Test self-containment
            assert!(first.contains_interval(&first));
            assert!(interior.contains_interval(&interior));
            assert!(last.contains_interval(&last));

            // Test cross-containment (should all be false for proper partition)
            assert!(!first.contains_interval(&interior));
            assert!(!first.contains_interval(&last));
            assert!(!interior.contains_interval(&first));
            assert!(!interior.contains_interval(&last));
            assert!(!last.contains_interval(&first));
            assert!(!last.contains_interval(&interior));

            // Test containment of smaller intervals
            let small_in_first = SubIntervalInPartition::<IntervalClosed<Real>>::First(
                IntervalClosed::new(Real::try_new(0.2).unwrap(), Real::try_new(0.8).unwrap()),
            );
            assert!(first.contains_interval(&small_in_first));
            assert!(!interior.contains_interval(&small_in_first));
            assert!(!last.contains_interval(&small_in_first));
        }

        #[test]
        fn test_length_calculation() {
            let (first, interior, last) = create_test_intervals();

            let length_one = PositiveRealScalar::try_new(Real::one()).unwrap();

            // All intervals should have length 1.0
            assert_eq!(first.length(), length_one.clone());
            assert_eq!(interior.length(), length_one.clone());
            assert_eq!(last.length(), length_one);
        }

        #[test]
        fn test_midpoint_calculation() {
            let (first, interior, last) = create_test_intervals();

            // Test midpoints
            assert_eq!(first.mid_point(), Real::try_new(0.5).unwrap());
            assert_eq!(interior.mid_point(), Real::try_new(1.5).unwrap());
            assert_eq!(last.mid_point(), Real::try_new(2.5).unwrap());
        }

        #[test]
        fn test_bounds_pair_extraction() {
            let (first, interior, last) = create_test_intervals();

            // Test bounds extraction
            assert_eq!(
                first.clone().into_bounds_pair(),
                (Real::try_new(0.0).unwrap(), Real::try_new(1.0).unwrap())
            );
            assert_eq!(
                interior.clone().into_bounds_pair(),
                (Real::try_new(1.0).unwrap(), Real::try_new(2.0).unwrap())
            );
            assert_eq!(
                last.clone().into_bounds_pair(),
                (Real::try_new(2.0).unwrap(), Real::try_new(3.0).unwrap())
            );
        }

        #[test]
        fn test_symmetry() {
            let (first, interior, last) = create_test_intervals();

            // Test symmetry around origin (none should be symmetric)
            assert!(!first.is_symmetric());
            assert!(!interior.is_symmetric());
            assert!(!last.is_symmetric());

            // Create a symmetric interval for testing
            let symmetric = SubIntervalInPartition::<IntervalClosed<Real>>::Last(
                IntervalLowerOpenUpperClosed::new(
                    Real::try_new(-1.0).unwrap(),
                    Real::try_new(1.0).unwrap(),
                ),
            );
            assert!(symmetric.is_symmetric());
        }

        #[test]
        fn test_intersection_operations() {
            let (first, interior, last) = create_test_intervals();

            // Test intersections between adjacent intervals (should be singletons or empty)
            let intersection_first_interior = first.intersection(&interior);
            match intersection_first_interior {
                Some(Interval::FiniteLength(IntervalFiniteLength::ZeroLength(singleton))) => {
                    assert_eq!(singleton.value(), &Real::try_new(1.0).unwrap());
                }
                None => {
                    // This is also acceptable for open boundaries
                }
                _ => panic!("Expected singleton or no intersection between [0,1) and [1,2)"),
            }

            let intersection_interior_last = interior.intersection(&last);
            match intersection_interior_last {
                Some(Interval::FiniteLength(IntervalFiniteLength::ZeroLength(singleton))) => {
                    assert_eq!(singleton.value(), &Real::try_new(2.0).unwrap());
                }
                None => {
                    // This is also acceptable depending on implementation
                }
                _ => panic!("Expected singleton or no intersection between [1,2) and [2,3]"),
            }

            // Test intersection of non-adjacent intervals (should be empty)
            assert!(first.intersection(&last).is_none());

            // Test self-intersection (should return equivalent interval)
            let self_intersection = first.intersection(&first).unwrap();
            match self_intersection {
                Interval::FiniteLength(IntervalFiniteLength::PositiveLength(positive)) => {
                    assert_eq!(positive.length().into_inner(), Real::try_new(1.0).unwrap());
                    assert_eq!(positive.lower_bound_value(), &Real::try_new(0.0).unwrap());
                    assert_eq!(positive.upper_bound_value(), &Real::try_new(1.0).unwrap());
                }
                _ => panic!("Self-intersection should return equivalent positive-length interval"),
            }
        }

        #[test]
        fn test_boundary_semantics() {
            let (first, interior, last) = create_test_intervals();

            // Verify boundary inclusion semantics for proper partition
            // First interval: [0, 1] - includes 0, includes 1
            assert!(first.contains_point(&Real::try_new(0.0).unwrap()));
            assert!(first.contains_point(&Real::try_new(1.0).unwrap()));

            // Interior interval: (1, 2] - excludes 1, includes 2
            assert!(!interior.contains_point(&Real::try_new(1.0).unwrap()));
            assert!(interior.contains_point(&Real::try_new(2.0).unwrap()));

            // Last interval: (2, 3] - excludes 2, includes 3
            assert!(!last.contains_point(&Real::try_new(2.0).unwrap()));
            assert!(last.contains_point(&Real::try_new(3.0).unwrap()));
        }

        #[test]
        fn test_partition_continuity() {
            let (first, interior, last) = create_test_intervals();

            // Test that the partition forms a continuous coverage
            // Upper bound of first should equal lower bound of interior
            assert_eq!(first.upper_bound_value(), interior.lower_bound_value());

            // Upper bound of interior should equal lower bound of last
            assert_eq!(interior.upper_bound_value(), last.lower_bound_value());

            // Test that every point in [0, 3] is covered by exactly one sub-interval
            let test_points = [0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0];

            for &point in &test_points {
                let test_point = Real::try_new(point).unwrap();
                let containing_intervals = [&first, &interior, &last]
                    .iter()
                    .filter(|interval| interval.contains_point(&test_point))
                    .count();

                assert_eq!(
                    containing_intervals, 1,
                    "Point {} should be contained in exactly one sub-interval",
                    point
                );
            }
        }

        #[test]
        fn test_equality_and_debug() {
            let first1 = SubIntervalInPartition::<IntervalLowerClosedUpperOpen<Real>>::First(
                IntervalClosed::new(Real::try_new(0.0).unwrap(), Real::try_new(1.0).unwrap()),
            );

            let first2 = SubIntervalInPartition::<IntervalLowerClosedUpperOpen<Real>>::First(
                IntervalClosed::new(Real::try_new(0.0).unwrap(), Real::try_new(1.0).unwrap()),
            );

            let different = SubIntervalInPartition::<IntervalLowerClosedUpperOpen<Real>>::First(
                IntervalClosed::new(Real::try_new(0.0).unwrap(), Real::try_new(2.0).unwrap()),
            );

            // Test equality
            assert_eq!(first1, first2);
            assert_ne!(first1, different);

            // Test debug formatting (should not panic)
            let debug_str = format!("{:?}", first1);
            assert!(debug_str.contains("First"));
        }

        #[test]
        fn test_clone() {
            let (first, interior, last) = create_test_intervals();

            // Test cloning
            let first_clone = first.clone();
            let interior_clone = interior.clone();
            let last_clone = last.clone();

            assert_eq!(first, first_clone);
            assert_eq!(interior, interior_clone);
            assert_eq!(last, last_clone);

            // Verify clones are independent
            assert_eq!(first_clone.length(), first.length());
            assert_eq!(interior_clone.mid_point(), interior.mid_point());
            assert_eq!(last_clone.into_bounds_pair(), last.into_bounds_pair());
        }

        #[test]
        fn test_with_different_scalar_types() {
            // Test with f64
            let first_f64 = SubIntervalInPartition::<IntervalLowerClosedUpperOpen<f64>>::First(
                IntervalClosed::new(0.0_f64, 1.0_f64),
            );
            assert!(first_f64.contains_point(&0.5_f64));
            assert!(first_f64.contains_point(&1.0_f64));

            // Test with validated real types
            use num_valid::RealNative64StrictFinite;
            let first_validated = SubIntervalInPartition::<
                IntervalLowerClosedUpperOpen<RealNative64StrictFinite>,
            >::First(IntervalClosed::new(
                RealNative64StrictFinite::try_new(0.0).unwrap(),
                RealNative64StrictFinite::try_new(1.0).unwrap(),
            ));
            assert!(
                first_validated.contains_point(&RealNative64StrictFinite::try_new(0.5).unwrap())
            );
            assert!(
                first_validated.contains_point(&RealNative64StrictFinite::try_new(1.0).unwrap())
            );
        }

        #[test]
        fn test_conversion_to_general_intervals() {
            let (first, interior, last) = create_test_intervals();

            // Convert to general Interval enum
            let first_general: Interval<Real> = first.into();
            let interior_general: Interval<Real> = interior.into();
            let last_general: Interval<Real> = last.into();

            // Verify they maintain the same containment behavior
            assert!(first_general.contains_point(&Real::try_new(0.0).unwrap()));
            assert!(first_general.contains_point(&Real::try_new(1.0).unwrap()));

            assert!(!interior_general.contains_point(&Real::try_new(1.0).unwrap()));
            assert!(interior_general.contains_point(&Real::try_new(2.0).unwrap()));

            assert!(!last_general.contains_point(&Real::try_new(2.0).unwrap()));
            assert!(last_general.contains_point(&Real::try_new(3.0).unwrap()));
        }

        #[test]
        fn test_interior_and_closure_operations() {
            let (first, middle, last) = create_test_intervals();

            // Test interior operation (should convert to open intervals)
            let first_interior: IntervalOpen<_> = first.clone().interior();
            let _middle_interior: IntervalOpen<_> = middle.clone().interior();
            let _last_interior: IntervalOpen<_> = last.clone().interior();

            assert!(!first_interior.contains_point(&Real::try_new(0.0).unwrap()));
            assert!(!first_interior.contains_point(&Real::try_new(1.0).unwrap()));
            assert!(first_interior.contains_point(&Real::try_new(0.5).unwrap()));

            // Test closure operation (should convert to closed intervals)
            let first_closure: IntervalClosed<_> = first.clone().closure();
            let _middle_closure: IntervalClosed<_> = middle.clone().closure();
            let _last_closure: IntervalClosed<_> = last.clone().closure();

            assert!(first_closure.contains_point(&Real::try_new(0.0).unwrap()));
            assert!(first_closure.contains_point(&Real::try_new(1.0).unwrap()));
            assert!(first_closure.contains_point(&Real::try_new(0.5).unwrap()));
        }

        #[test]
        fn test_edge_cases() {
            // Test with very small intervals
            let tiny_first = SubIntervalInPartition::<IntervalLowerClosedUpperOpen<Real>>::First(
                IntervalClosed::new(
                    Real::try_new(0.0).unwrap(),
                    Real::try_new(f64::EPSILON).unwrap(),
                ),
            );

            assert!(tiny_first.length().into_inner() > Real::try_new(0.0).unwrap());
            assert!(tiny_first.contains_point(&Real::try_new(0.0).unwrap()));
            assert!(tiny_first.contains_point(&Real::try_new(f64::EPSILON).unwrap()));
            assert!(!tiny_first.contains_point(&Real::try_new(1.001 * f64::EPSILON).unwrap()));

            // Test with negative values
            let negative_last = SubIntervalInPartition::<IntervalLowerClosedUpperOpen<Real>>::Last(
                IntervalOpen::new(Real::try_new(-2.0).unwrap(), Real::try_new(-1.0).unwrap()),
            );

            assert!(negative_last.contains_point(&Real::try_new(-1.5).unwrap()));
            assert!(!negative_last.contains_point(&Real::try_new(-2.0).unwrap()));
            assert!(!negative_last.contains_point(&Real::try_new(-1.0).unwrap()));
            assert!(!negative_last.contains_point(&Real::try_new(0.0).unwrap()));
        }

        #[test]
        fn test_only_interval_in_partition() {
            let closed =
                IntervalClosed::new(Real::try_new(0.).unwrap(), Real::try_new(1.).unwrap());

            let only = SubIntervalInPartition::<IntervalClosed<Real>>::Only(closed.clone());
            assert_eq!(only.lower_bound_value(), &Real::try_new(0.0).unwrap());
            assert_eq!(only.upper_bound_value(), &Real::try_new(1.0).unwrap());

            let closed: Interval<_> = closed.into();
            let only: Interval<_> = only.into();
            assert_eq!(closed, only);
        }

        // Tests for boundary closure predicates

        /// Test is_lower_bound_closed() for SubIntervalInPartition::Only with closed domain
        #[test]
        fn is_lower_bound_closed_only_closed() {
            // Create a single interval [0, 10] as the only interval
            let domain = IntervalClosed::new(0.0, 10.0);
            let sub_interval: SubIntervalInPartition<IntervalClosed<f64>> =
                SubIntervalInPartition::Only(domain);

            // Closed interval should have closed lower bound
            assert!(
                sub_interval.is_lower_bound_closed(),
                "Only interval with closed domain should have closed lower bound"
            );
        }

        /// Test is_lower_bound_closed() for SubIntervalInPartition::Only with open domain
        #[test]
        fn is_lower_bound_closed_only_open() {
            // Create a single interval (0, 10) as the only interval
            let domain = IntervalOpen::new(0.0, 10.0);
            let sub_interval: SubIntervalInPartition<IntervalOpen<f64>> =
                SubIntervalInPartition::Only(domain);

            // Open interval should have open lower bound
            assert!(
                !sub_interval.is_lower_bound_closed(),
                "Only interval with open domain should have open lower bound"
            );
        }

        /// Test is_lower_bound_closed() for SubIntervalInPartition::Only with half-open domain
        #[test]
        fn is_lower_bound_closed_only_half_open() {
            // Create a single interval [0, 10) as the only interval
            let domain = IntervalLowerClosedUpperOpen::new(0.0, 10.0);
            let sub_interval: SubIntervalInPartition<IntervalLowerClosedUpperOpen<f64>> =
                SubIntervalInPartition::Only(domain);

            // Half-open [a, b) should have closed lower bound
            assert!(
                sub_interval.is_lower_bound_closed(),
                "Only interval [a, b) should have closed lower bound"
            );
        }

        /// Test is_lower_bound_closed() for SubIntervalInPartition::First with closed domain
        #[test]
        fn is_lower_bound_closed_first_closed() {
            // For closed domain [0, 10], the first interval would be [0, x]
            let first_interval = IntervalClosed::new(0.0, 5.0);
            let sub_interval: SubIntervalInPartition<IntervalClosed<f64>> =
                SubIntervalInPartition::First(first_interval);

            assert!(
                sub_interval.is_lower_bound_closed(),
                "First interval from closed domain should have closed lower bound"
            );
        }

        /// Test is_lower_bound_closed() for SubIntervalInPartition::First with open domain
        #[test]
        fn is_lower_bound_closed_first_open() {
            // For open domain (0, 10), the first interval would be (0, x]
            let first_interval = IntervalLowerOpenUpperClosed::new(0.0, 5.0);
            let sub_interval: SubIntervalInPartition<IntervalOpen<f64>> =
                SubIntervalInPartition::First(first_interval);

            assert!(
                !sub_interval.is_lower_bound_closed(),
                "First interval from open domain should have open lower bound"
            );
        }

        /// Test is_lower_bound_closed() for SubIntervalInPartition::First with half-open domain
        #[test]
        fn is_lower_bound_closed_first_half_open() {
            // For half-open domain [0, 10), the first interval would be [0, x]
            let first_interval = IntervalClosed::new(0.0, 5.0);
            let sub_interval: SubIntervalInPartition<IntervalLowerClosedUpperOpen<f64>> =
                SubIntervalInPartition::First(first_interval);

            assert!(
                sub_interval.is_lower_bound_closed(),
                "First interval from [a, b) domain should have closed lower bound"
            );
        }

        /// Test is_lower_bound_closed() for SubIntervalInPartition::Middle
        #[test]
        fn is_lower_bound_closed_middle() {
            // Middle intervals are always (a, b] - open lower bound
            let middle_interval = IntervalLowerOpenUpperClosed::new(5.0, 7.5);
            let sub_interval: SubIntervalInPartition<IntervalClosed<f64>> =
                SubIntervalInPartition::Middle(middle_interval);

            assert!(
                !sub_interval.is_lower_bound_closed(),
                "Middle interval (a, b] should always have open lower bound"
            );
        }

        /// Test is_lower_bound_closed() for SubIntervalInPartition::Last with closed domain
        #[test]
        fn is_lower_bound_closed_last_closed() {
            // For closed domain [0, 10], the last interval would be (x, 10]
            let last_interval = IntervalLowerOpenUpperClosed::new(7.5, 10.0);
            let sub_interval: SubIntervalInPartition<IntervalClosed<f64>> =
                SubIntervalInPartition::Last(last_interval);

            assert!(
                !sub_interval.is_lower_bound_closed(),
                "Last interval from closed domain should have open lower bound (continuation from middle)"
            );
        }

        /// Test is_lower_bound_closed() for SubIntervalInPartition::Last with open domain
        #[test]
        fn is_lower_bound_closed_last_open() {
            // For open domain (0, 10), the last interval would be (x, 10)
            let last_interval = IntervalOpen::new(7.5, 10.0);
            let sub_interval: SubIntervalInPartition<IntervalOpen<f64>> =
                SubIntervalInPartition::Last(last_interval);

            assert!(
                !sub_interval.is_lower_bound_closed(),
                "Last interval from open domain should have open lower bound"
            );
        }

        /// Test is_lower_bound_closed() for SubIntervalInPartition::Last with half-open domain
        #[test]
        fn is_lower_bound_closed_last_half_open() {
            // For half-open domain [0, 10), the last interval would be (x, 10)
            let last_interval = IntervalOpen::new(7.5, 10.0);
            let sub_interval: SubIntervalInPartition<IntervalLowerClosedUpperOpen<f64>> =
                SubIntervalInPartition::Last(last_interval);

            assert!(
                !sub_interval.is_lower_bound_closed(),
                "Last interval from [a, b) domain should have open lower bound"
            );
        }

        /// Test is_lower_bound_closed() consistency with lower_bound_value()
        #[test]
        fn is_lower_bound_closed_consistency_with_value() {
            // Test that is_lower_bound_closed() is consistent with actual bound behavior
            let closed_interval = IntervalClosed::new(5.0, 10.0);
            let sub_interval: SubIntervalInPartition<IntervalClosed<f64>> =
                SubIntervalInPartition::Only(closed_interval);

            // Get the lower bound value
            let lower_value = sub_interval.lower_bound_value();
            assert_eq!(lower_value, &5.0);

            // Check that it's closed
            assert!(sub_interval.is_lower_bound_closed());

            // Verify containment at the lower bound
            assert!(
                sub_interval.contains_point(&5.0),
                "Closed lower bound should contain the boundary point"
            );
        }

        /// Test is_lower_bound_closed() with all four variants
        #[test]
        fn is_lower_bound_closed_all_variants() {
            // Only - closed
            let only_closed: SubIntervalInPartition<IntervalClosed<f64>> =
                SubIntervalInPartition::Only(IntervalClosed::new(0.0, 10.0));
            assert!(only_closed.is_lower_bound_closed());

            // First - closed (from closed domain)
            let first_closed: SubIntervalInPartition<IntervalClosed<f64>> =
                SubIntervalInPartition::First(IntervalClosed::new(0.0, 3.0));
            assert!(first_closed.is_lower_bound_closed());

            // Middle - always open
            let middle: SubIntervalInPartition<IntervalClosed<f64>> =
                SubIntervalInPartition::Middle(IntervalLowerOpenUpperClosed::new(3.0, 7.0));
            assert!(!middle.is_lower_bound_closed());

            // Last - open (continuation from middle)
            let last_open: SubIntervalInPartition<IntervalClosed<f64>> =
                SubIntervalInPartition::Last(IntervalLowerOpenUpperClosed::new(7.0, 10.0));
            assert!(!last_open.is_lower_bound_closed());
        }

        /// Test is_lower_bound_closed() with negative values
        #[test]
        fn is_lower_bound_closed_negative_values() {
            // Test with negative bounds
            let closed_neg: SubIntervalInPartition<IntervalClosed<f64>> =
                SubIntervalInPartition::Only(IntervalClosed::new(-10.0, -5.0));
            assert!(
                closed_neg.is_lower_bound_closed(),
                "Closed interval with negative bounds should have closed lower bound"
            );

            let middle_neg: SubIntervalInPartition<IntervalClosed<f64>> =
                SubIntervalInPartition::Middle(IntervalLowerOpenUpperClosed::new(-5.0, 0.0));
            assert!(
                !middle_neg.is_lower_bound_closed(),
                "Middle interval with negative bounds should have open lower bound"
            );
        }

        // Tests for is_upper_bound_closed()

        /// Test is_upper_bound_closed() for SubIntervalInPartition::Only with closed domain
        #[test]
        fn is_upper_bound_closed_only_closed() {
            // Create a single interval [0, 10] as the only interval
            let domain = IntervalClosed::new(0.0, 10.0);
            let sub_interval: SubIntervalInPartition<IntervalClosed<f64>> =
                SubIntervalInPartition::Only(domain);

            // Closed interval should have closed upper bound
            assert!(
                sub_interval.is_upper_bound_closed(),
                "Only interval with closed domain should have closed upper bound"
            );
        }

        /// Test is_upper_bound_closed() for SubIntervalInPartition::Only with open domain
        #[test]
        fn is_upper_bound_closed_only_open() {
            // Create a single interval (0, 10) as the only interval
            let domain = IntervalOpen::new(0.0, 10.0);
            let sub_interval: SubIntervalInPartition<IntervalOpen<f64>> =
                SubIntervalInPartition::Only(domain);

            // Open interval should have open upper bound
            assert!(
                !sub_interval.is_upper_bound_closed(),
                "Only interval with open domain should have open upper bound"
            );
        }

        /// Test is_upper_bound_closed() for SubIntervalInPartition::Only with half-open domain
        #[test]
        fn is_upper_bound_closed_only_half_open() {
            // Create a single interval [0, 10) as the only interval
            let domain = IntervalLowerClosedUpperOpen::new(0.0, 10.0);
            let sub_interval: SubIntervalInPartition<IntervalLowerClosedUpperOpen<f64>> =
                SubIntervalInPartition::Only(domain);

            // Half-open [a, b) should have open upper bound
            assert!(
                !sub_interval.is_upper_bound_closed(),
                "Only interval [a, b) should have open upper bound"
            );
        }

        /// Test is_upper_bound_closed() for SubIntervalInPartition::First with closed domain
        #[test]
        fn is_upper_bound_closed_first_closed() {
            // For closed domain [0, 10], the first interval would be [0, x]
            let first_interval = IntervalClosed::new(0.0, 5.0);
            let sub_interval: SubIntervalInPartition<IntervalClosed<f64>> =
                SubIntervalInPartition::First(first_interval);

            assert!(
                sub_interval.is_upper_bound_closed(),
                "First interval from closed domain should have closed upper bound"
            );
        }

        /// Test is_upper_bound_closed() for SubIntervalInPartition::First with open domain
        #[test]
        fn is_upper_bound_closed_first_open() {
            // For open domain (0, 10), the first interval would be (0, x]
            let first_interval = IntervalLowerOpenUpperClosed::new(0.0, 5.0);
            let sub_interval: SubIntervalInPartition<IntervalOpen<f64>> =
                SubIntervalInPartition::First(first_interval);

            assert!(
                sub_interval.is_upper_bound_closed(),
                "First interval from open domain should have closed upper bound (gap-free partition)"
            );
        }

        /// Test is_upper_bound_closed() for SubIntervalInPartition::First with half-open domain
        #[test]
        fn is_upper_bound_closed_first_half_open() {
            // For half-open domain [0, 10), the first interval would be [0, x]
            let first_interval = IntervalClosed::new(0.0, 5.0);
            let sub_interval: SubIntervalInPartition<IntervalLowerClosedUpperOpen<f64>> =
                SubIntervalInPartition::First(first_interval);

            assert!(
                sub_interval.is_upper_bound_closed(),
                "First interval from [a, b) domain should have closed upper bound"
            );
        }

        /// Test is_upper_bound_closed() for SubIntervalInPartition::Middle
        #[test]
        fn is_upper_bound_closed_middle() {
            // Middle intervals are always (a, b] - closed upper bound
            let middle_interval = IntervalLowerOpenUpperClosed::new(5.0, 7.5);
            let sub_interval: SubIntervalInPartition<IntervalClosed<f64>> =
                SubIntervalInPartition::Middle(middle_interval);

            assert!(
                sub_interval.is_upper_bound_closed(),
                "Middle interval (a, b] should always have closed upper bound"
            );
        }

        /// Test is_upper_bound_closed() for SubIntervalInPartition::Last with closed domain
        #[test]
        fn is_upper_bound_closed_last_closed() {
            // For closed domain [0, 10], the last interval would be (x, 10]
            let last_interval = IntervalLowerOpenUpperClosed::new(7.5, 10.0);
            let sub_interval: SubIntervalInPartition<IntervalClosed<f64>> =
                SubIntervalInPartition::Last(last_interval);

            assert!(
                sub_interval.is_upper_bound_closed(),
                "Last interval from closed domain should have closed upper bound"
            );
        }

        /// Test is_upper_bound_closed() for SubIntervalInPartition::Last with open domain
        #[test]
        fn is_upper_bound_closed_last_open() {
            // For open domain (0, 10), the last interval would be (x, 10)
            let last_interval = IntervalOpen::new(7.5, 10.0);
            let sub_interval: SubIntervalInPartition<IntervalOpen<f64>> =
                SubIntervalInPartition::Last(last_interval);

            assert!(
                !sub_interval.is_upper_bound_closed(),
                "Last interval from open domain should have open upper bound"
            );
        }

        /// Test is_upper_bound_closed() for SubIntervalInPartition::Last with half-open domain
        #[test]
        fn is_upper_bound_closed_last_half_open() {
            // For half-open domain [0, 10), the last interval would be (x, 10)
            let last_interval = IntervalOpen::new(7.5, 10.0);
            let sub_interval: SubIntervalInPartition<IntervalLowerClosedUpperOpen<f64>> =
                SubIntervalInPartition::Last(last_interval);

            assert!(
                !sub_interval.is_upper_bound_closed(),
                "Last interval from [a, b) domain should have open upper bound"
            );
        }

        /// Test is_upper_bound_closed() consistency with upper_bound_value()
        #[test]
        fn is_upper_bound_closed_consistency_with_value() {
            // Test that is_upper_bound_closed() is consistent with actual bound behavior
            let closed_interval = IntervalClosed::new(5.0, 10.0);
            let sub_interval: SubIntervalInPartition<IntervalClosed<f64>> =
                SubIntervalInPartition::Only(closed_interval);

            // Get the upper bound value
            let upper_value = sub_interval.upper_bound_value();
            assert_eq!(upper_value, &10.0);

            // Check that it's closed
            assert!(sub_interval.is_upper_bound_closed());

            // Verify containment at the upper bound
            assert!(
                sub_interval.contains_point(&10.0),
                "Closed upper bound should contain the boundary point"
            );
        }

        /// Test is_upper_bound_closed() with all four variants
        #[test]
        fn is_upper_bound_closed_all_variants() {
            // Only - closed
            let only_closed: SubIntervalInPartition<IntervalClosed<f64>> =
                SubIntervalInPartition::Only(IntervalClosed::new(0.0, 10.0));
            assert!(only_closed.is_upper_bound_closed());

            // First - closed (gap-free partition)
            let first_closed: SubIntervalInPartition<IntervalClosed<f64>> =
                SubIntervalInPartition::First(IntervalClosed::new(0.0, 3.0));
            assert!(first_closed.is_upper_bound_closed());

            // Middle - always closed
            let middle: SubIntervalInPartition<IntervalClosed<f64>> =
                SubIntervalInPartition::Middle(IntervalLowerOpenUpperClosed::new(3.0, 7.0));
            assert!(middle.is_upper_bound_closed());

            // Last - closed (from closed domain)
            let last_closed: SubIntervalInPartition<IntervalClosed<f64>> =
                SubIntervalInPartition::Last(IntervalLowerOpenUpperClosed::new(7.0, 10.0));
            assert!(last_closed.is_upper_bound_closed());
        }

        /// Test is_upper_bound_closed() with negative values
        #[test]
        fn is_upper_bound_closed_negative_values() {
            // Test with negative bounds
            let closed_neg: SubIntervalInPartition<IntervalClosed<f64>> =
                SubIntervalInPartition::Only(IntervalClosed::new(-10.0, -5.0));
            assert!(
                closed_neg.is_upper_bound_closed(),
                "Closed interval with negative bounds should have closed upper bound"
            );

            let middle_neg: SubIntervalInPartition<IntervalClosed<f64>> =
                SubIntervalInPartition::Middle(IntervalLowerOpenUpperClosed::new(-5.0, 0.0));
            assert!(
                middle_neg.is_upper_bound_closed(),
                "Middle interval (a, b] with negative bounds should have closed upper bound"
            );
        }

        // Tests for is_upper_bound_open()

        /// Test is_upper_bound_open() for SubIntervalInPartition::Only with closed domain
        #[test]
        fn is_upper_bound_open_only_closed() {
            // Create a single interval [0, 10] as the only interval
            let domain = IntervalClosed::new(0.0, 10.0);
            let sub_interval: SubIntervalInPartition<IntervalClosed<f64>> =
                SubIntervalInPartition::Only(domain);

            // Closed interval should NOT have open upper bound
            assert!(
                !sub_interval.is_upper_bound_open(),
                "Only interval with closed domain should not have open upper bound"
            );
        }

        /// Test is_upper_bound_open() for SubIntervalInPartition::Only with open domain
        #[test]
        fn is_upper_bound_open_only_open() {
            // Create a single interval (0, 10) as the only interval
            let domain = IntervalOpen::new(0.0, 10.0);
            let sub_interval: SubIntervalInPartition<IntervalOpen<f64>> =
                SubIntervalInPartition::Only(domain);

            // Open interval should have open upper bound
            assert!(
                sub_interval.is_upper_bound_open(),
                "Only interval with open domain should have open upper bound"
            );
        }

        /// Test is_upper_bound_open() for SubIntervalInPartition::Only with half-open domain
        #[test]
        fn is_upper_bound_open_only_half_open() {
            // Create a single interval [0, 10) as the only interval
            let domain = IntervalLowerClosedUpperOpen::new(0.0, 10.0);
            let sub_interval: SubIntervalInPartition<IntervalLowerClosedUpperOpen<f64>> =
                SubIntervalInPartition::Only(domain);

            // Half-open [a, b) should have open upper bound
            assert!(
                sub_interval.is_upper_bound_open(),
                "Only interval [a, b) should have open upper bound"
            );
        }

        /// Test is_upper_bound_open() for SubIntervalInPartition::First
        #[test]
        fn is_upper_bound_open_first() {
            // First intervals in gap-free partitions have closed upper bounds
            let first_interval = IntervalClosed::new(0.0, 5.0);
            let sub_interval: SubIntervalInPartition<IntervalClosed<f64>> =
                SubIntervalInPartition::First(first_interval);

            assert!(
                !sub_interval.is_upper_bound_open(),
                "First interval should have closed (not open) upper bound"
            );
        }

        /// Test is_upper_bound_open() for SubIntervalInPartition::Middle
        #[test]
        fn is_upper_bound_open_middle() {
            // Middle intervals are always (a, b] - closed upper bound
            let middle_interval = IntervalLowerOpenUpperClosed::new(5.0, 7.5);
            let sub_interval: SubIntervalInPartition<IntervalClosed<f64>> =
                SubIntervalInPartition::Middle(middle_interval);

            assert!(
                !sub_interval.is_upper_bound_open(),
                "Middle interval (a, b] should NOT have open upper bound"
            );
        }

        /// Test is_upper_bound_open() for SubIntervalInPartition::Last with closed domain
        #[test]
        fn is_upper_bound_open_last_closed() {
            // For closed domain [0, 10], the last interval would be (x, 10]
            let last_interval = IntervalLowerOpenUpperClosed::new(7.5, 10.0);
            let sub_interval: SubIntervalInPartition<IntervalClosed<f64>> =
                SubIntervalInPartition::Last(last_interval);

            assert!(
                !sub_interval.is_upper_bound_open(),
                "Last interval from closed domain should have closed upper bound"
            );
        }

        /// Test is_upper_bound_open() for SubIntervalInPartition::Last with open domain
        #[test]
        fn is_upper_bound_open_last_open() {
            // For open domain (0, 10), the last interval would be (x, 10)
            let last_interval = IntervalOpen::new(7.5, 10.0);
            let sub_interval: SubIntervalInPartition<IntervalOpen<f64>> =
                SubIntervalInPartition::Last(last_interval);

            assert!(
                sub_interval.is_upper_bound_open(),
                "Last interval from open domain should have open upper bound"
            );
        }

        /// Test is_upper_bound_open() for SubIntervalInPartition::Last with half-open domain
        #[test]
        fn is_upper_bound_open_last_half_open() {
            // For half-open domain [0, 10), the last interval would be (x, 10)
            let last_interval = IntervalOpen::new(7.5, 10.0);
            let sub_interval: SubIntervalInPartition<IntervalLowerClosedUpperOpen<f64>> =
                SubIntervalInPartition::Last(last_interval);

            assert!(
                sub_interval.is_upper_bound_open(),
                "Last interval from [a, b) domain should have open upper bound"
            );
        }

        /// Test is_upper_bound_open() complement relationship with is_upper_bound_closed()
        #[test]
        fn is_upper_bound_open_complement_relationship() {
            // Test that is_upper_bound_open() == !is_upper_bound_closed()

            // Closed domain
            let closed: SubIntervalInPartition<IntervalClosed<f64>> =
                SubIntervalInPartition::Only(IntervalClosed::new(0.0, 10.0));
            assert_eq!(
                closed.is_upper_bound_open(),
                !closed.is_upper_bound_closed(),
                "is_upper_bound_open should be complement of is_upper_bound_closed"
            );

            // Open domain
            let open: SubIntervalInPartition<IntervalOpen<f64>> =
                SubIntervalInPartition::Only(IntervalOpen::new(0.0, 10.0));
            assert_eq!(
                open.is_upper_bound_open(),
                !open.is_upper_bound_closed(),
                "Complement relationship should hold for open domain"
            );

            // Middle variant
            let middle: SubIntervalInPartition<IntervalClosed<f64>> =
                SubIntervalInPartition::Middle(IntervalLowerOpenUpperClosed::new(3.0, 7.0));
            assert_eq!(
                middle.is_upper_bound_open(),
                !middle.is_upper_bound_closed(),
                "Complement relationship should hold for middle variant"
            );
        }

        /// Test is_upper_bound_open() consistency with contains_point()
        #[test]
        fn is_upper_bound_open_consistency_with_containment() {
            // Open upper bound should exclude the boundary point
            let half_open: SubIntervalInPartition<IntervalLowerClosedUpperOpen<f64>> =
                SubIntervalInPartition::Only(IntervalLowerClosedUpperOpen::new(0.0, 10.0));

            assert!(half_open.is_upper_bound_open());
            assert!(
                !half_open.contains_point(&10.0),
                "Open upper bound should exclude the boundary point"
            );

            // Closed upper bound should include the boundary point
            let closed: SubIntervalInPartition<IntervalClosed<f64>> =
                SubIntervalInPartition::Only(IntervalClosed::new(0.0, 10.0));

            assert!(!closed.is_upper_bound_open());
            assert!(
                closed.contains_point(&10.0),
                "Closed upper bound should include the boundary point"
            );
        }

        /// Test is_upper_bound_open() with all four variants
        #[test]
        fn is_upper_bound_open_all_variants() {
            // Only - half-open (open upper)
            let only_half_open: SubIntervalInPartition<IntervalLowerClosedUpperOpen<f64>> =
                SubIntervalInPartition::Only(IntervalLowerClosedUpperOpen::new(0.0, 10.0));
            assert!(only_half_open.is_upper_bound_open());

            // First - closed upper (not open)
            let first: SubIntervalInPartition<IntervalClosed<f64>> =
                SubIntervalInPartition::First(IntervalClosed::new(0.0, 3.0));
            assert!(!first.is_upper_bound_open());

            // Middle - closed upper (not open)
            let middle: SubIntervalInPartition<IntervalClosed<f64>> =
                SubIntervalInPartition::Middle(IntervalLowerOpenUpperClosed::new(3.0, 7.0));
            assert!(!middle.is_upper_bound_open());

            // Last - open upper (from open domain)
            let last_open: SubIntervalInPartition<IntervalOpen<f64>> =
                SubIntervalInPartition::Last(IntervalOpen::new(7.0, 10.0));
            assert!(last_open.is_upper_bound_open());
        }

        /// Test is_upper_bound_open() with negative values
        #[test]
        fn is_upper_bound_open_negative_values() {
            // Test with negative bounds - half-open
            let half_open_neg: SubIntervalInPartition<IntervalLowerClosedUpperOpen<f64>> =
                SubIntervalInPartition::Only(IntervalLowerClosedUpperOpen::new(-10.0, -5.0));
            assert!(
                half_open_neg.is_upper_bound_open(),
                "Half-open interval with negative bounds should have open upper bound"
            );

            // Test with negative bounds - closed
            let closed_neg: SubIntervalInPartition<IntervalClosed<f64>> =
                SubIntervalInPartition::Only(IntervalClosed::new(-10.0, -5.0));
            assert!(
                !closed_neg.is_upper_bound_open(),
                "Closed interval with negative bounds should not have open upper bound"
            );
        }
    }

    mod test_interval_lower_unbounded_upper_unbounded {
        use super::*;
        use num_valid::{RealNative64StrictFinite, RealNative64StrictFiniteInDebug};
        use try_create::TryNew;

        // Type aliases for testing
        type Real = RealNative64StrictFiniteInDebug;
        type SafeReal = RealNative64StrictFinite;

        #[test]
        fn test_construction() {
            // Test with f64
            let universal_f64 = IntervalLowerUnboundedUpperUnbounded::<f64>::new();

            // Test with validated types
            let universal_real = IntervalLowerUnboundedUpperUnbounded::<Real>::new();
            let universal_safe = IntervalLowerUnboundedUpperUnbounded::<SafeReal>::new();

            // All should be created successfully (no bounds to validate)
            assert_eq!(universal_f64, IntervalLowerUnboundedUpperUnbounded::new());
            assert_eq!(universal_real, IntervalLowerUnboundedUpperUnbounded::new());
            assert_eq!(universal_safe, IntervalLowerUnboundedUpperUnbounded::new());
        }

        #[test]
        fn test_default() {
            let universal = IntervalLowerUnboundedUpperUnbounded::<f64>::default();
            let expected = IntervalLowerUnboundedUpperUnbounded::<f64>::new();
            assert_eq!(universal, expected);
        }

        #[test]
        #[allow(clippy::clone_on_copy)]
        fn test_clone_and_equality() {
            let universal1 = IntervalLowerUnboundedUpperUnbounded::<f64>::new();
            let universal2 = universal1.clone();

            assert_eq!(universal1, universal2);
            assert_eq!(universal1, universal1); // Reflexivity
        }

        #[test]
        fn test_contains_point_always_true() {
            let universal = IntervalLowerUnboundedUpperUnbounded::<f64>::new();

            // Test various points - all should be contained
            assert!(universal.contains_point(&0.0));
            assert!(universal.contains_point(&1.0));
            assert!(universal.contains_point(&-1.0));
            assert!(universal.contains_point(&1000.0));
            assert!(universal.contains_point(&-1000.0));
            assert!(universal.contains_point(&std::f64::consts::PI));
            assert!(universal.contains_point(&std::f64::consts::E));
            assert!(universal.contains_point(&f64::MIN));
            assert!(universal.contains_point(&f64::MAX));
            assert!(universal.contains_point(&f64::EPSILON));
            assert!(universal.contains_point(&-f64::EPSILON));

            // Test edge cases
            assert!(universal.contains_point(&0.0));
            assert!(universal.contains_point(&1e-100));
            assert!(universal.contains_point(&-1e-100));
            assert!(universal.contains_point(&1e100));
            assert!(universal.contains_point(&-1e100));
        }

        #[test]
        fn test_contains_point_with_validated_types() {
            let universal = IntervalLowerUnboundedUpperUnbounded::<Real>::new();

            let zero = Real::try_new(0.0).unwrap();
            let positive = Real::try_new(42.0).unwrap();
            let negative = Real::try_new(-17.5).unwrap();

            assert!(universal.contains_point(&zero));
            assert!(universal.contains_point(&positive));
            assert!(universal.contains_point(&negative));
        }

        #[test]
        fn test_contains_any_interval() {
            let universal = IntervalLowerUnboundedUpperUnbounded::<f64>::new();

            // Test containment of finite intervals
            let closed = IntervalClosed::new(0.0, 1.0);
            let open = IntervalOpen::new(-1.0, 1.0);
            let half_open_left = IntervalLowerOpenUpperClosed::new(0.0, 1.0);
            let half_open_right = IntervalLowerClosedUpperOpen::new(0.0, 1.0);
            let singleton = IntervalSingleton::new(42.0);

            assert!(universal.contains_interval(&closed));
            assert!(universal.contains_interval(&open));
            assert!(universal.contains_interval(&half_open_left));
            assert!(universal.contains_interval(&half_open_right));
            assert!(universal.contains_interval(&singleton));

            // Test containment of infinite intervals
            let lower_bounded = IntervalLowerClosedUpperUnbounded::new(0.0);
            let upper_bounded = IntervalLowerUnboundedUpperClosed::new(100.0);
            let lower_open_unbounded = IntervalLowerOpenUpperUnbounded::new(1.0);
            let upper_open_unbounded = IntervalLowerUnboundedUpperOpen::new(10.0);

            assert!(universal.contains_interval(&lower_bounded));
            assert!(universal.contains_interval(&upper_bounded));
            assert!(universal.contains_interval(&lower_open_unbounded));
            assert!(universal.contains_interval(&upper_open_unbounded));

            // Test containment of itself
            let universal2 = IntervalLowerUnboundedUpperUnbounded::<f64>::new();
            assert!(universal.contains_interval(&universal2));
        }

        #[test]
        fn test_no_other_interval_contains_universal() {
            let universal = IntervalLowerUnboundedUpperUnbounded::<f64>::new();

            // Finite intervals cannot contain universal
            let closed = IntervalClosed::new(-1000.0, 1000.0);
            let open = IntervalOpen::new(-1e6, 1e6);
            let singleton = IntervalSingleton::new(0.0);

            assert!(!closed.contains_interval(&universal));
            assert!(!open.contains_interval(&universal));
            assert!(!singleton.contains_interval(&universal));

            // Semi-infinite intervals cannot contain universal
            let lower_bounded = IntervalLowerClosedUpperUnbounded::new(-1000.0);
            let upper_bounded = IntervalLowerUnboundedUpperClosed::new(1000.0);

            assert!(!lower_bounded.contains_interval(&universal));
            assert!(!upper_bounded.contains_interval(&universal));
        }

        #[test]
        fn test_intersection_identity_property() {
            let universal = IntervalLowerUnboundedUpperUnbounded::<f64>::new();

            // Universal ∩ Any = Any (identity property)
            let closed = IntervalClosed::new(0.0, 1.0);
            let open = IntervalOpen::new(-1.0, 1.0);
            let singleton = IntervalSingleton::new(0.5);

            // Test intersection with finite intervals
            if let Some(intersection) = universal.intersection(&closed) {
                // Should be equivalent to the original closed interval
                match intersection {
                    Interval::FiniteLength(IntervalFiniteLength::PositiveLength(positive)) => {
                        assert_eq!(positive.length().into_inner(), 1.0);
                        assert_eq!(positive.lower_bound_value(), &0.0);
                        assert_eq!(positive.upper_bound_value(), &1.0);
                    }
                    _ => panic!("Expected finite positive length interval"),
                }
            } else {
                panic!("Universal intersection should always exist");
            }

            if let Some(intersection) = universal.intersection(&open) {
                match intersection {
                    Interval::FiniteLength(IntervalFiniteLength::PositiveLength(
                        IntervalFinitePositiveLength::Open(recovered_open),
                    )) => {
                        assert_eq!(recovered_open.lower_bound_value(), &-1.0);
                        assert_eq!(recovered_open.upper_bound_value(), &1.0);
                    }
                    _ => panic!("Expected open interval"),
                }
            } else {
                panic!("Universal intersection should always exist");
            }

            if let Some(intersection) = universal.intersection(&singleton) {
                match intersection {
                    Interval::FiniteLength(IntervalFiniteLength::ZeroLength(
                        recovered_singleton,
                    )) => {
                        assert_eq!(recovered_singleton.value(), &0.5);
                    }
                    _ => panic!("Expected singleton interval"),
                }
            } else {
                panic!("Universal intersection should always exist");
            }
        }

        #[test]
        fn test_intersection_with_infinite_intervals() {
            let universal = IntervalLowerUnboundedUpperUnbounded::<f64>::new();

            // Test with semi-infinite intervals
            let lower_bounded = IntervalLowerClosedUpperUnbounded::new(5.0);
            let upper_bounded = IntervalLowerUnboundedUpperClosed::new(10.0);

            if let Some(intersection) = universal.intersection(&lower_bounded) {
                match intersection {
                    Interval::InfiniteLength(
                        IntervalInfiniteLength::LowerClosedUpperUnbounded(result),
                    ) => {
                        assert_eq!(result.lower_bound_value(), &5.0);
                    }
                    _ => panic!("Expected lower bounded infinite interval"),
                }
            }

            if let Some(intersection) = universal.intersection(&upper_bounded) {
                match intersection {
                    Interval::InfiniteLength(
                        IntervalInfiniteLength::LowerUnboundedUpperClosed(result),
                    ) => {
                        assert_eq!(result.upper_bound_value(), &10.0);
                    }
                    _ => panic!("Expected upper bounded infinite interval"),
                }
            }
        }

        #[test]
        fn test_intersection_with_self() {
            let universal1 = IntervalLowerUnboundedUpperUnbounded::<f64>::new();
            let universal2 = IntervalLowerUnboundedUpperUnbounded::<f64>::new();

            if let Some(intersection) = universal1.intersection(&universal2) {
                match intersection {
                    Interval::InfiniteLength(
                        IntervalInfiniteLength::LowerUnboundedUpperUnbounded(_),
                    ) => {
                        // Correct - intersection of universal with itself is universal
                    }
                    _ => panic!("Expected universal interval"),
                }
            } else {
                panic!("Universal should intersect with itself");
            }
        }

        #[test]
        fn test_intersection_commutativity() {
            let universal = IntervalLowerUnboundedUpperUnbounded::<f64>::new();
            let finite = IntervalClosed::new(1.0, 2.0);

            let intersection1 = universal.intersection(&finite);
            let intersection2 = finite.intersection(&universal);

            assert_eq!(intersection1, intersection2);

            // Test with infinite intervals
            let semi_infinite = IntervalLowerClosedUpperUnbounded::new(0.0);
            let intersection3 = universal.intersection(&semi_infinite);
            let intersection4 = semi_infinite.intersection(&universal);

            assert_eq!(intersection3, intersection4);
        }

        #[test]
        fn test_universal_as_identity_element() {
            let universal = IntervalLowerUnboundedUpperUnbounded::<f64>::new();

            let test_intervals = vec![
                Interval::from(IntervalClosed::new(0.0, 1.0)),
                Interval::from(IntervalOpen::new(-1.0, 1.0)),
                Interval::from(IntervalSingleton::new(0.5)),
                Interval::from(IntervalLowerClosedUpperUnbounded::new(0.0)),
                Interval::from(IntervalLowerUnboundedUpperClosed::new(10.0)),
            ];

            for interval in test_intervals {
                let intersection = universal.intersection(&interval).unwrap();

                // The intersection should contain the same points as the original interval
                for test_point in [-100.0, -1.0, 0.0, 0.5, 1.0, 10.0, 100.0] {
                    assert_eq!(
                        interval.contains_point(&test_point),
                        intersection.contains_point(&test_point),
                        "Point containment should be identical for point {}",
                        test_point
                    );
                }
            }
        }

        #[test]
        fn test_memory_efficiency() {
            let universal = IntervalLowerUnboundedUpperUnbounded::<f64>::new();

            // Universal interval should have minimal memory footprint
            // (This is more of a documentation test - actual size depends on implementation)
            let size = std::mem::size_of_val(&universal);
            assert!(
                size <= 8,
                "Universal interval should be very memory efficient, got {} bytes",
                size
            );
        }

        #[test]
        fn test_debug_and_display() {
            let universal = IntervalLowerUnboundedUpperUnbounded::<f64>::new();

            // Test Debug implementation
            let debug_str = format!("{:?}", universal);
            assert!(debug_str.contains("IntervalLowerUnboundedUpperUnbounded"));

            // Test that it doesn't panic
            println!("Debug: {:?}", universal);
        }

        #[test]
        fn test_serialization() {
            let universal = IntervalLowerUnboundedUpperUnbounded::<f64>::new();

            // Test that serialization traits are implemented (compilation test)
            let _serialized = serde_json::to_string(&universal);
            // Note: Actual serialization testing would require enabling serde features
        }

        #[test]
        fn test_mathematical_properties() {
            let universal = IntervalLowerUnboundedUpperUnbounded::<f64>::new();

            // Test that universal interval has the expected mathematical properties

            // 1. Contains every real number (tested above)
            // 2. Contains every other interval (tested above)
            // 3. Is its own intersection with any interval
            // 4. Union with any interval is itself

            let finite = IntervalClosed::new(0.0, 1.0);

            // Note: union method would need to be implemented to test this
            // For now, we test that intersection behaves correctly
            let intersection = universal.intersection(&finite).unwrap();

            // Verify that all points in finite are in intersection
            for test_point in [0.0, 0.5, 1.0] {
                assert!(finite.contains_point(&test_point));
                assert!(intersection.contains_point(&test_point));
            }

            // Verify that points outside finite are not in intersection
            for test_point in [-1.0, 2.0] {
                assert!(!finite.contains_point(&test_point));
                assert!(!intersection.contains_point(&test_point));
            }
        }

        #[test]
        fn test_with_different_scalar_types() {
            // Test that the universal interval works with different scalar types
            let universal_f64 = IntervalLowerUnboundedUpperUnbounded::<f64>::new();
            let universal_real = IntervalLowerUnboundedUpperUnbounded::<Real>::new();
            let universal_safe = IntervalLowerUnboundedUpperUnbounded::<SafeReal>::new();

            // All should contain their respective zero values
            assert!(universal_f64.contains_point(&0.0_f64));
            assert!(universal_real.contains_point(&Real::try_new(0.0).unwrap()));
            assert!(universal_safe.contains_point(&SafeReal::try_new(0.0).unwrap()));

            // Test containment with different finite intervals
            let finite_f64 = IntervalClosed::new(0.0_f64, 1.0_f64);
            let finite_real =
                IntervalClosed::new(Real::try_new(0.0).unwrap(), Real::try_new(1.0).unwrap());

            assert!(universal_f64.contains_interval(&finite_f64));
            assert!(universal_real.contains_interval(&finite_real));
        }

        #[test]
        fn test_edge_cases_and_boundary_conditions() {
            let universal = IntervalLowerUnboundedUpperUnbounded::<f64>::new();

            // Test with extreme floating-point values
            assert!(universal.contains_point(&f64::MIN));
            assert!(universal.contains_point(&f64::MAX));
            assert!(universal.contains_point(&f64::EPSILON));
            assert!(universal.contains_point(&f64::MIN_POSITIVE));

            // Test with zero and near-zero values
            assert!(universal.contains_point(&0.0));
            assert!(universal.contains_point(&-0.0));
            assert!(universal.contains_point(&1e-300));
            assert!(universal.contains_point(&-1e-300));

            // Test with very large values
            assert!(universal.contains_point(&1e100));
            assert!(universal.contains_point(&-1e100));
        }

        #[test]
        fn test_comparison_with_other_intervals() {
            let universal = IntervalLowerUnboundedUpperUnbounded::<f64>::new();

            // Universal interval should be equal to itself
            let universal2 = IntervalLowerUnboundedUpperUnbounded::<f64>::new();
            assert_eq!(universal, universal2);

            // Different scalar types should not be directly comparable
            // (This is enforced by the type system)
        }
    }

    /*
    #[cfg(test)]
    mod test_conversion_finite_positive_to_finite {
        use super::*;
        use num_valid::RealNative64StrictFiniteInDebug;
        use try_create::TryNew;

        type Real = RealNative64StrictFiniteInDebug;

        #[test]
        fn test_closed_interval_conversion() {
            let closed = IntervalFinitePositiveLength::Closed(IntervalClosed::new(
                Real::try_new(-1.0).unwrap(),
                Real::try_new(2.0).unwrap(),
            ));

            let finite: IntervalFiniteLength<Real> = closed.clone().into();

            match finite {
                IntervalFiniteLength::PositiveLength(positive) => {
                    assert_eq!(positive, closed);
                    assert_eq!(positive.length().as_ref(), &3.0);
                    assert_eq!(positive.mid_point().as_ref(), &0.5);
                }
                IntervalFiniteLength::ZeroLength(_) => {
                    panic!("Expected positive length, got zero length");
                }
            }
        }

        #[test]
        fn test_open_interval_conversion() {
            let open = IntervalFinitePositiveLength::Open(IntervalOpen::new(
                Real::try_new(0.0).unwrap(),
                Real::try_new(1.0).unwrap(),
            ));

            let finite: IntervalFiniteLength<Real> = open.clone().into();

            match finite {
                IntervalFiniteLength::PositiveLength(positive) => {
                    assert_eq!(positive, open);
                    assert_eq!(positive.length().as_ref(), &1.0);
                    assert_eq!(positive.mid_point().as_ref(), &0.5);
                    // Test boundary behavior
                    assert!(!positive.contains_point(&Real::try_new(0.0).unwrap()));
                    assert!(!positive.contains_point(&Real::try_new(1.0).unwrap()));
                    assert!(positive.contains_point(&Real::try_new(0.5).unwrap()));
                }
                IntervalFiniteLength::ZeroLength(_) => {
                    panic!("Expected positive length, got zero length");
                }
            }
        }

        #[test]
        fn test_lower_open_upper_closed_conversion() {
            let half_open = IntervalFinitePositiveLength::LowerOpenUpperClosed(
                IntervalLowerOpenUpperClosed::new(
                    Real::try_new(-2.0).unwrap(),
                    Real::try_new(3.0).unwrap(),
                ),
            );

            let finite: IntervalFiniteLength<Real> = half_open.clone().into();

            match finite {
                IntervalFiniteLength::PositiveLength(positive) => {
                    assert_eq!(positive, half_open);
                    assert_eq!(positive.length().as_ref(), &5.0);
                    assert_eq!(positive.mid_point().as_ref(), &0.5);
                    // Test boundary behavior: (a, b]
                    assert!(!positive.contains_point(&Real::try_new(-2.0).unwrap())); // Lower excluded
                    assert!(positive.contains_point(&Real::try_new(3.0).unwrap())); // Upper included
                    assert!(positive.contains_point(&Real::try_new(0.0).unwrap())); // Interior
                }
                IntervalFiniteLength::ZeroLength(_) => {
                    panic!("Expected positive length, got zero length");
                }
            }
        }

        #[test]
        fn test_lower_closed_upper_open_conversion() {
            let half_open = IntervalFinitePositiveLength::LowerClosedUpperOpen(
                IntervalLowerClosedUpperOpen::new(
                    Real::try_new(1.0).unwrap(),
                    Real::try_new(4.0).unwrap(),
                ),
            );

            let finite: IntervalFiniteLength<Real> = half_open.clone().into();

            match finite {
                IntervalFiniteLength::PositiveLength(positive) => {
                    assert_eq!(positive, half_open);
                    assert_eq!(positive.length().as_ref(), &3.0);
                    assert_eq!(positive.mid_point().as_ref(), &2.5);
                    // Test boundary behavior: [a, b)
                    assert!(positive.contains_point(&Real::try_new(1.0).unwrap())); // Lower included
                    assert!(!positive.contains_point(&Real::try_new(4.0).unwrap())); // Upper excluded
                    assert!(positive.contains_point(&Real::try_new(2.5).unwrap())); // Interior
                }
                IntervalFiniteLength::ZeroLength(_) => {
                    panic!("Expected positive length, got zero length");
                }
            }
        }

        #[test]
        fn test_conversion_preserves_mathematical_properties() {
            let original = IntervalFinitePositiveLength::Closed(IntervalClosed::new(
                Real::try_new(-5.0).unwrap(),
                Real::try_new(5.0).unwrap(),
            ));

            let converted: IntervalFiniteLength<Real> = original.clone().into();

            // Test that mathematical properties are preserved
            assert_eq!(original.length(), converted.length());
            assert_eq!(original.mid_point(), converted.mid_point());
            assert_eq!(original.is_symmetric(), converted.is_symmetric());
            assert_eq!(original.lower_bound_value(), converted.lower_bound_value());
            assert_eq!(original.upper_bound_value(), converted.upper_bound_value());

            // Test containment behavior is preserved
            let test_points = vec![
                Real::try_new(-6.0).unwrap(), // Outside left
                Real::try_new(-5.0).unwrap(), // Left boundary
                Real::try_new(0.0).unwrap(),  // Center
                Real::try_new(5.0).unwrap(),  // Right boundary
                Real::try_new(6.0).unwrap(),  // Outside right
            ];

            for point in test_points {
                assert_eq!(
                    original.contains_point(&point),
                    converted.contains_point(&point),
                    "Containment should be preserved for point: {}",
                    point
                );
            }
        }

        #[test]
        fn test_conversion_with_different_scalar_types() {
            // Test with raw f64
            let original_f64 = IntervalFinitePositiveLength::Closed(IntervalClosed::new(0.0, 1.0));
            let converted_f64: IntervalFiniteLength<f64> = original_f64.into();

            match converted_f64 {
                IntervalFiniteLength::PositiveLength(positive) => {
                    assert_eq!(positive.length().into_inner(), 1.0);
                }
                _ => panic!("Expected positive length"),
            }

            // Test with validated real type
            use num_valid::RealNative64StrictFinite;

            let original_validated = IntervalFinitePositiveLength::Open(IntervalOpen::new(
                RealNative64StrictFinite::try_new(0.0).unwrap(),
                RealNative64StrictFinite::try_new(2.0).unwrap(),
            ));
            let converted_validated: IntervalFiniteLength<RealNative64StrictFinite> =
                original_validated.into();

            match converted_validated {
                IntervalFiniteLength::PositiveLength(positive) => {
                    assert_eq!(positive.length().as_ref(), &2.0);
                }
                _ => panic!("Expected positive length"),
            }
        }

        #[test]
        fn test_conversion_chain_compatibility() {
            let original = IntervalFinitePositiveLength::Closed(IntervalClosed::new(
                Real::try_new(0.0).unwrap(),
                Real::try_new(1.0).unwrap(),
            ));

            // Convert to finite length
            let finite: IntervalFiniteLength<Real> = original.clone().into();

            // Convert to general interval
            let general: Interval<Real> = finite.clone().into();

            // Convert back to finite length
            let back_to_finite = IntervalFiniteLength::try_from(general).unwrap();

            // Should be identical
            assert_eq!(finite, back_to_finite);

            // Extract the positive length part and compare
            match back_to_finite {
                IntervalFiniteLength::PositiveLength(positive) => {
                    assert_eq!(positive, original);
                }
                _ => panic!("Expected positive length after round-trip conversion"),
            }
        }

        #[test]
        fn test_conversion_with_intersection_results() {
            let a = IntervalFinitePositiveLength::Closed(IntervalClosed::new(
                Real::try_new(0.0).unwrap(),
                Real::try_new(2.0).unwrap(),
            ));
            let b = IntervalFinitePositiveLength::Closed(IntervalClosed::new(
                Real::try_new(1.0).unwrap(),
                Real::try_new(3.0).unwrap(),
            ));

            let intersection = a.intersection(&b).unwrap();

            // The intersection should be convertible to finite length
            let finite_intersection = IntervalFiniteLength::try_from(intersection).unwrap();

            match finite_intersection {
                IntervalFiniteLength::PositiveLength(positive) => {
                    assert_eq!(positive.length().as_ref(), &1.0); // [1, 2] has length 1
                    assert_eq!(positive.lower_bound_value(), &1.0);
                    assert_eq!(positive.upper_bound_value(), &2.0);
                }
                IntervalFiniteLength::ZeroLength(_) => {
                    panic!("Expected positive length intersection");
                }
            }
        }

        #[test]
        fn test_conversion_preserves_interval_trait_operations() {
            let original = IntervalFinitePositiveLength::LowerOpenUpperClosed(
                IntervalLowerOpenUpperClosed::new(
                    Real::try_new(-1.0).unwrap(),
                    Real::try_new(1.0).unwrap(),
                ),
            );

            let converted: IntervalFiniteLength<Real> = original.clone().into();

            // Test that IntervalTrait operations work consistently
            let test_interval =
                IntervalClosed::new(Real::try_new(0.0).unwrap(), Real::try_new(0.5).unwrap());

            assert_eq!(
                original.contains_interval(&test_interval),
                converted.contains_interval(&test_interval)
            );

            let intersection_original = original.intersection(&test_interval);
            let intersection_converted = converted.intersection(&test_interval);

            assert_eq!(intersection_original, intersection_converted);
        }

        #[test]
        fn test_very_small_intervals() {
            // Test with very small but positive-length intervals
            let tiny = IntervalFinitePositiveLength::Closed(IntervalClosed::new(
                Real::try_new(1.0).unwrap(),
                Real::try_new(1.0 + f64::EPSILON).unwrap(),
            ));

            let finite: IntervalFiniteLength<Real> = tiny.clone().into();

            match finite {
                IntervalFiniteLength::PositiveLength(positive) => {
                    assert!(positive.length().as_ref() > &0.0);
                    assert!(positive.length().as_ref() < &1e-15);
                    assert_eq!(positive, tiny);
                }
                IntervalFiniteLength::ZeroLength(_) => {
                    panic!("Expected positive length for tiny interval");
                }
            }
        }

        #[test]
        fn test_conversion_is_zero_cost() {
            // This test verifies that the conversion is indeed zero-cost
            // by checking that the converted interval has the same properties
            let original = IntervalFinitePositiveLength::Open(IntervalOpen::new(
                Real::try_new(2.0).unwrap(),
                Real::try_new(8.0).unwrap(),
            ));

            let original_ptr = &original as *const _;
            let converted: IntervalFiniteLength<Real> = original.into();

            match &converted {
                IntervalFiniteLength::PositiveLength(positive) => {
                    // The data should be the same (though we can't directly compare pointers
                    // due to the enum wrapper, we can verify the values are identical)
                    assert_eq!(positive.length().as_ref(), &6.0);
                    assert_eq!(positive.lower_bound_value(), &2.0);
                    assert_eq!(positive.upper_bound_value(), &8.0);
                }
                _ => panic!("Expected positive length"),
            }
        }
    }
    */

    mod interval_hull {
        use super::*;
        use num_valid::RealNative64StrictFiniteInDebug;
        use try_create::TryNew;

        type Real = RealNative64StrictFiniteInDebug;

        /// Test hull between two intervals of the same type (closed with closed)
        #[test]
        fn hull_closed_with_closed() {
            let interval1 =
                IntervalClosed::new(Real::try_new(1.0).unwrap(), Real::try_new(3.0).unwrap());
            let interval2 =
                IntervalClosed::new(Real::try_new(2.0).unwrap(), Real::try_new(5.0).unwrap());

            let hull = interval1.hull(&interval2);

            // Hull should be [1.0, 5.0] (closed)
            assert_eq!(hull.lower_bound_value(), &1.0);
            assert_eq!(hull.upper_bound_value(), &5.0);
        }

        /// Test hull between two intervals of the same type (open with open)
        #[test]
        fn hull_open_with_open() {
            let interval1 =
                IntervalOpen::new(Real::try_new(1.0).unwrap(), Real::try_new(3.0).unwrap());
            let interval2 =
                IntervalOpen::new(Real::try_new(2.0).unwrap(), Real::try_new(5.0).unwrap());

            let hull = interval1.hull(&interval2);

            // Hull should be (1.0, 5.0) (open)
            assert_eq!(hull.lower_bound_value(), &1.0);
            assert_eq!(hull.upper_bound_value(), &5.0);
        }

        /// Test hull between closed and open intervals
        #[test]
        fn hull_closed_with_open() {
            let closed =
                IntervalClosed::new(Real::try_new(1.0).unwrap(), Real::try_new(3.0).unwrap());
            let open = IntervalOpen::new(Real::try_new(2.0).unwrap(), Real::try_new(5.0).unwrap());

            let hull = closed.hull(&open);

            // Hull should be [1.0, 5.0)
            match hull {
                IntervalFinitePositiveLength::LowerClosedUpperOpen(result) => {
                    assert_eq!(result.lower_bound_value(), &1.0);
                    assert_eq!(result.upper_bound_value(), &5.0);
                }
                _ => panic!("Expected LowerClosedUpperOpen interval"),
            }
        }

        /// Test hull between closed and half-open intervals
        #[test]
        fn hull_closed_with_lower_closed_upper_open() {
            let closed =
                IntervalClosed::new(Real::try_new(1.0).unwrap(), Real::try_new(3.0).unwrap());
            let half_open = IntervalLowerClosedUpperOpen::new(
                Real::try_new(2.0).unwrap(),
                Real::try_new(5.0).unwrap(),
            );

            let hull = closed.hull(&half_open);

            // Hull should be [1.0, 5.0) - most inclusive lower (closed), most inclusive upper (open)
            match hull {
                IntervalFinitePositiveLength::LowerClosedUpperOpen(result) => {
                    assert_eq!(result.lower_bound_value(), &1.0);
                    assert_eq!(result.upper_bound_value(), &5.0);
                }
                _ => panic!("Expected LowerClosedUpperOpen interval"),
            }
        }

        /// Test hull between closed and other half-open intervals
        #[test]
        fn hull_closed_with_lower_open_upper_closed() {
            let closed =
                IntervalClosed::new(Real::try_new(1.0).unwrap(), Real::try_new(3.0).unwrap());
            let half_open = IntervalLowerOpenUpperClosed::new(
                Real::try_new(2.0).unwrap(),
                Real::try_new(5.0).unwrap(),
            );

            let hull = closed.hull(&half_open);

            // Hull should be (1.0, 5.0] - most inclusive lower (open), most inclusive upper (closed)
            match hull {
                IntervalFinitePositiveLength::Closed(result) => {
                    assert_eq!(result.lower_bound_value(), &1.0);
                    assert_eq!(result.upper_bound_value(), &5.0);
                }
                _ => panic!("Expected Closed interval"),
            }
        }

        /// Test hull between open and half-open intervals
        #[test]
        fn hull_open_with_lower_closed_upper_open() {
            let open = IntervalOpen::new(Real::try_new(1.0).unwrap(), Real::try_new(3.0).unwrap());
            let half_open = IntervalLowerClosedUpperOpen::new(
                Real::try_new(2.0).unwrap(),
                Real::try_new(5.0).unwrap(),
            );

            let hull = open.hull(&half_open);

            // Hull should be (1.0, 5.0) - open wins on both sides
            match hull {
                IntervalFinitePositiveLength::Open(result) => {
                    assert_eq!(result.lower_bound_value(), &1.0);
                    assert_eq!(result.upper_bound_value(), &5.0);
                }
                _ => panic!("Expected open interval"),
            }
        }

        /// Test hull between open and other half-open intervals
        #[test]
        fn hull_open_with_lower_open_upper_closed() {
            let open = IntervalOpen::new(Real::try_new(1.0).unwrap(), Real::try_new(3.0).unwrap());
            let half_open = IntervalLowerOpenUpperClosed::new(
                Real::try_new(2.0).unwrap(),
                Real::try_new(5.0).unwrap(),
            );

            let hull = open.hull(&half_open);

            // Hull should be (1.0, 5.0]
            match hull {
                IntervalFinitePositiveLength::LowerOpenUpperClosed(result) => {
                    assert_eq!(result.lower_bound_value(), &1.0);
                    assert_eq!(result.upper_bound_value(), &5.0);
                }
                _ => panic!("Expected LowerOpenUpperClosed interval"),
            }
        }

        /// Test hull between the two half-open intervals
        #[test]
        fn hull_lower_closed_upper_open_with_lower_open_upper_closed() {
            let interval1 = IntervalLowerClosedUpperOpen::new(
                Real::try_new(1.0).unwrap(),
                Real::try_new(3.0).unwrap(),
            );
            let interval2 = IntervalLowerOpenUpperClosed::new(
                Real::try_new(2.0).unwrap(),
                Real::try_new(5.0).unwrap(),
            );

            let hull = interval1.hull(&interval2);

            // Hull should be [1.0, 5.0]
            match hull {
                IntervalFinitePositiveLength::Closed(result) => {
                    assert_eq!(result.lower_bound_value(), &1.0);
                    assert_eq!(result.upper_bound_value(), &5.0);
                }
                _ => panic!("Expected open interval"),
            }
        }

        /// Test hull with disjoint intervals
        #[test]
        fn hull_disjoint_intervals() {
            let interval1 =
                IntervalClosed::new(Real::try_new(1.0).unwrap(), Real::try_new(2.0).unwrap());
            let interval2 =
                IntervalClosed::new(Real::try_new(4.0).unwrap(), Real::try_new(5.0).unwrap());

            let hull = interval1.hull(&interval2);

            // Hull should be [1.0, 5.0] (covers both intervals and the gap)
            assert_eq!(hull.lower_bound_value(), &1.0);
            assert_eq!(hull.upper_bound_value(), &5.0);
            assert_eq!(hull.length().as_ref(), &4.0); // 5.0 - 1.0
        }

        /// Test hull with overlapping intervals
        #[test]
        fn hull_overlapping_intervals() {
            let interval1 =
                IntervalClosed::new(Real::try_new(1.0).unwrap(), Real::try_new(4.0).unwrap());
            let interval2 =
                IntervalClosed::new(Real::try_new(2.0).unwrap(), Real::try_new(5.0).unwrap());

            let hull = interval1.hull(&interval2);

            // Hull should be [1.0, 5.0]
            assert_eq!(hull.lower_bound_value(), &1.0);
            assert_eq!(hull.upper_bound_value(), &5.0);
        }

        /// Test hull with one interval contained in another
        #[test]
        fn hull_contained_intervals() {
            let outer =
                IntervalClosed::new(Real::try_new(1.0).unwrap(), Real::try_new(5.0).unwrap());
            let inner =
                IntervalClosed::new(Real::try_new(2.0).unwrap(), Real::try_new(3.0).unwrap());

            let hull = outer.hull(&inner);

            // Hull should be the outer interval [1.0, 5.0]
            assert_eq!(hull.lower_bound_value(), &1.0);
            assert_eq!(hull.upper_bound_value(), &5.0);
        }

        /// Test hull with identical intervals
        #[test]
        fn hull_identical_intervals() {
            let interval1 =
                IntervalClosed::new(Real::try_new(2.0).unwrap(), Real::try_new(4.0).unwrap());
            let interval2 =
                IntervalClosed::new(Real::try_new(2.0).unwrap(), Real::try_new(4.0).unwrap());

            let hull = interval1.hull(&interval2);

            // Hull should be identical to the original intervals
            assert_eq!(hull.lower_bound_value(), &2.0);
            assert_eq!(hull.upper_bound_value(), &4.0);
        }

        /// Test hull with boundary cases (same bounds, different types)
        #[test]
        fn hull_same_bounds_different_types() {
            let closed =
                IntervalClosed::new(Real::try_new(1.0).unwrap(), Real::try_new(3.0).unwrap());
            let open = IntervalOpen::new(Real::try_new(1.0).unwrap(), Real::try_new(3.0).unwrap());

            let hull = closed.hull(&open);

            // Hull should be [1.0, 3.0]
            match hull {
                IntervalFinitePositiveLength::Closed(result) => {
                    assert_eq!(result.lower_bound_value(), &1.0);
                    assert_eq!(result.upper_bound_value(), &3.0);
                }
                _ => panic!("Expected Closed interval"),
            }
        }

        /// Test hull using the enum variant
        #[test]
        fn hull_with_enum_variant() {
            let interval1 = IntervalFinitePositiveLength::Closed(IntervalClosed::new(
                Real::try_new(1.0).unwrap(),
                Real::try_new(3.0).unwrap(),
            ));
            let interval2 = IntervalFinitePositiveLength::Open(IntervalOpen::new(
                Real::try_new(2.0).unwrap(),
                Real::try_new(5.0).unwrap(),
            ));

            let hull = interval1.hull(&interval2);

            // Hull should be [1.0, 5.0)
            match hull {
                IntervalFinitePositiveLength::LowerClosedUpperOpen(result) => {
                    assert_eq!(result.lower_bound_value(), &1.0);
                    assert_eq!(result.upper_bound_value(), &5.0);
                }
                _ => panic!("Expected LowerClosedUpperOpen interval"),
            }
        }

        /// Test symmetry property: hull(A, B) should equal hull(B, A)
        #[test]
        fn hull_symmetry_property() {
            let interval1 =
                IntervalClosed::new(Real::try_new(1.0).unwrap(), Real::try_new(3.0).unwrap());
            let interval2 =
                IntervalOpen::new(Real::try_new(2.0).unwrap(), Real::try_new(5.0).unwrap());

            let hull1 = interval1.hull(&interval2);
            let hull2 = interval2.hull(&interval1);

            // Both hulls should be identical
            assert_eq!(hull1, hull2);
        }

        /// Test hull mathematical property: both intervals should be contained in the hull
        #[test]
        fn hull_containment_property() {
            let interval1 =
                IntervalClosed::new(Real::try_new(1.0).unwrap(), Real::try_new(3.0).unwrap());
            let interval2 = IntervalLowerOpenUpperClosed::new(
                Real::try_new(2.5).unwrap(),
                Real::try_new(4.0).unwrap(),
            );

            let hull = interval1.hull(&interval2);

            // Convert hull to the universal Interval enum for containment testing
            let hull_interval: Interval<Real> = match hull {
                IntervalFinitePositiveLength::Closed(h) => Interval::from(h),
                _ => panic!("Expected Closed interval"),
            };

            let interval1_general: Interval<Real> = Interval::from(interval1);
            let interval2_general: Interval<Real> = Interval::from(interval2);

            // The hull should contain both original intervals
            assert!(hull_interval.contains_interval(&interval1_general));
            assert!(hull_interval.contains_interval(&interval2_general));
        }

        /// Test hull with various scalar types (f64)
        #[test]
        fn hull_with_f64() {
            let interval1 = IntervalClosed::new(1.0_f64, 3.0_f64);
            let interval2 = IntervalOpen::new(2.0_f64, 5.0_f64);

            let hull = interval1.hull(&interval2);

            match hull {
                IntervalFinitePositiveLength::LowerClosedUpperOpen(h) => {
                    assert_eq!(*h.lower_bound_value(), 1.0);
                    assert_eq!(*h.upper_bound_value(), 5.0);
                }
                _ => panic!("Expected LowerClosedUpperOpen interval"),
            }
        }

        /// Test edge case: very small intervals
        #[test]
        fn hull_small_intervals() {
            let epsilon = Real::try_new(f64::EPSILON).unwrap();
            let zero = Real::try_new(0.0).unwrap();
            let two_epsilon = Real::try_new(2.0 * f64::EPSILON).unwrap();

            let interval1 = IntervalClosed::new(zero, epsilon);
            let interval2 = IntervalClosed::new(epsilon, two_epsilon);

            let hull = interval1.hull(&interval2);

            assert_eq!(hull.lower_bound_value(), &zero);
            assert_eq!(hull.upper_bound_value(), &two_epsilon);
        }

        /// Test reflexivity: hull(A, A) should equal A (for compatible types)
        #[test]
        fn hull_reflexivity() {
            let interval =
                IntervalClosed::new(Real::try_new(2.0).unwrap(), Real::try_new(4.0).unwrap());

            let hull = interval.hull(&interval);

            assert_eq!(hull.lower_bound_value(), &2.0);
            assert_eq!(hull.upper_bound_value(), &4.0);
            assert_eq!(hull.length().as_ref(), &2.0);
        }

        // Tests for generalized IntervalHull functionality with unbounded intervals

        /// Test hull of bounded with half-unbounded (lower bounded, upper unbounded)
        #[test]
        fn hull_bounded_with_lower_bounded_upper_unbounded() {
            let bounded =
                IntervalClosed::new(Real::try_new(0.0).unwrap(), Real::try_new(2.0).unwrap());
            let unbounded = IntervalLowerClosedUpperUnbounded::new(Real::try_new(1.0).unwrap());

            let hull = bounded.hull(&unbounded);

            // Hull should be [0, +∞)
            match hull {
                Interval::InfiniteLength(IntervalInfiniteLength::LowerClosedUpperUnbounded(
                    result,
                )) => {
                    assert_eq!(result.lower_bound_value(), &0.0);
                }
                _ => panic!("Expected LowerClosedUpperUnbounded interval"),
            }
        }

        /// Test hull of two half-unbounded intervals (both lower bounded)
        #[test]
        fn hull_two_lower_bounded_upper_unbounded() {
            let unbounded1 = IntervalLowerClosedUpperUnbounded::new(Real::try_new(0.0).unwrap());
            let unbounded2 = IntervalLowerOpenUpperUnbounded::new(Real::try_new(5.0).unwrap());

            let hull = unbounded1.hull(&unbounded2);

            // Hull should be [0, +∞) - closed wins over open
            match hull {
                Interval::InfiniteLength(IntervalInfiniteLength::LowerClosedUpperUnbounded(
                    result,
                )) => {
                    assert_eq!(result.lower_bound_value(), &0.0);
                }
                _ => panic!("Expected LowerClosedUpperUnbounded interval"),
            }
        }

        /// Test hull of bounded with upper unbounded
        #[test]
        fn hull_bounded_with_lower_unbounded_upper_bounded() {
            let bounded =
                IntervalClosed::new(Real::try_new(-2.0).unwrap(), Real::try_new(1.0).unwrap());
            let upper_unbounded =
                IntervalLowerUnboundedUpperClosed::new(Real::try_new(0.0).unwrap());

            let hull = bounded.hull(&upper_unbounded);

            // Hull should be (-∞, 1]
            match hull {
                Interval::InfiniteLength(IntervalInfiniteLength::LowerUnboundedUpperClosed(
                    result,
                )) => {
                    assert_eq!(result.upper_bound_value(), &1.0);
                }
                _ => panic!("Expected LowerUnboundedUpperClosed interval"),
            }
        }

        /// Test hull of two upper unbounded intervals
        #[test]
        fn hull_two_lower_unbounded_upper_bounded() {
            let upper_unbounded1 =
                IntervalLowerUnboundedUpperClosed::new(Real::try_new(2.0).unwrap());
            let upper_unbounded2 =
                IntervalLowerUnboundedUpperOpen::new(Real::try_new(-1.0).unwrap());

            let hull = upper_unbounded1.hull(&upper_unbounded2);

            // Hull should be (-∞, 2] - maximum upper bound, closed wins
            match hull {
                Interval::InfiniteLength(IntervalInfiniteLength::LowerUnboundedUpperClosed(
                    result,
                )) => {
                    assert_eq!(result.upper_bound_value(), &2.0);
                }
                _ => panic!("Expected LowerUnboundedUpperClosed interval"),
            }
        }

        /// Test hull with fully unbounded interval
        #[test]
        fn hull_bounded_with_fully_unbounded() {
            let bounded =
                IntervalClosed::new(Real::try_new(0.0).unwrap(), Real::try_new(10.0).unwrap());
            let fully_unbounded = IntervalLowerUnboundedUpperUnbounded::<Real>::new();

            let hull = bounded.hull(&fully_unbounded);

            // Hull should be (-∞, +∞)
            match hull {
                Interval::InfiniteLength(IntervalInfiniteLength::LowerUnboundedUpperUnbounded(
                    _,
                )) => {
                    // Success - fully unbounded
                }
                _ => panic!("Expected LowerUnboundedUpperUnbounded interval"),
            }
        }

        /// Test using the Interval enum directly
        #[test]
        fn hull_using_interval_enum() {
            let interval1: Interval<Real> = Interval::from(IntervalClosed::new(
                Real::try_new(1.0).unwrap(),
                Real::try_new(3.0).unwrap(),
            ));
            let interval2: Interval<Real> = Interval::from(IntervalLowerClosedUpperUnbounded::new(
                Real::try_new(2.0).unwrap(),
            ));

            let hull = interval1.hull(&interval2);

            // Hull should be [1, +∞)
            match hull {
                Interval::InfiniteLength(IntervalInfiniteLength::LowerClosedUpperUnbounded(
                    result,
                )) => {
                    assert_eq!(result.lower_bound_value(), &1.0);
                }
                _ => panic!("Expected LowerClosedUpperUnbounded interval"),
            }
        }

        /// Test hull with mixed boundary types and unbounded intervals
        #[test]
        fn hull_open_bounded_with_closed_unbounded() {
            let open = IntervalOpen::new(Real::try_new(0.0).unwrap(), Real::try_new(2.0).unwrap());
            let unbounded = IntervalLowerClosedUpperUnbounded::new(Real::try_new(1.5).unwrap());

            let hull = open.hull(&unbounded);

            // Hull should be (0, +∞) - open lower bound wins
            match hull {
                Interval::InfiniteLength(IntervalInfiniteLength::LowerOpenUpperUnbounded(
                    result,
                )) => {
                    assert_eq!(result.lower_bound_value(), &0.0);
                }
                _ => panic!("Expected LowerOpenUpperUnbounded interval"),
            }
        }

        /// Test hull with singleton and bounded interval
        #[test]
        fn hull_singleton_with_bounded() {
            let singleton = IntervalSingleton::new(Real::try_new(1.5).unwrap());
            let closed =
                IntervalClosed::new(Real::try_new(0.0).unwrap(), Real::try_new(1.0).unwrap());

            let hull = singleton.hull(&closed);

            // Hull should be [0, 1.5]
            match hull {
                Interval::FiniteLength(IntervalFiniteLength::PositiveLength(
                    IntervalFinitePositiveLength::Closed(result),
                )) => {
                    assert_eq!(result.lower_bound_value(), &0.0);
                    assert_eq!(result.upper_bound_value(), &1.5);
                }
                _ => panic!("Expected closed bounded interval"),
            }
        }

        /// Test hull with singleton and unbounded interval
        #[test]
        fn hull_singleton_with_unbounded() {
            let singleton = IntervalSingleton::new(Real::try_new(5.0).unwrap());
            let unbounded = IntervalLowerClosedUpperUnbounded::new(Real::try_new(0.0).unwrap());

            let hull = singleton.hull(&unbounded);

            // Hull should be [0, +∞)
            match hull {
                Interval::InfiniteLength(IntervalInfiniteLength::LowerClosedUpperUnbounded(
                    result,
                )) => {
                    assert_eq!(result.lower_bound_value(), &0.0);
                }
                _ => panic!("Expected LowerClosedUpperUnbounded interval"),
            }
        }

        /// Test hull of two fully unbounded intervals
        #[test]
        fn hull_two_fully_unbounded() {
            let unbounded1 = IntervalLowerUnboundedUpperUnbounded::<Real>::new();
            let unbounded2 = IntervalLowerUnboundedUpperUnbounded::<Real>::new();

            let hull = unbounded1.hull(&unbounded2);

            // Hull should be (-∞, +∞)
            match hull {
                Interval::InfiniteLength(IntervalInfiniteLength::LowerUnboundedUpperUnbounded(
                    _,
                )) => {
                    // Success - fully unbounded
                }
                _ => panic!("Expected LowerUnboundedUpperUnbounded interval"),
            }
        }

        /// Test hull of bounded with open lower unbounded (a, +∞)
        #[test]
        fn hull_bounded_with_lower_open_upper_unbounded() {
            let bounded =
                IntervalClosed::new(Real::try_new(0.0).unwrap(), Real::try_new(5.0).unwrap());
            let unbounded = IntervalLowerOpenUpperUnbounded::new(Real::try_new(3.0).unwrap());

            let hull = bounded.hull(&unbounded);

            // Hull should be [0, +∞) - bounded extends below unbounded
            match hull {
                Interval::InfiniteLength(IntervalInfiniteLength::LowerClosedUpperUnbounded(
                    result,
                )) => {
                    assert_eq!(result.lower_bound_value(), &0.0);
                }
                _ => panic!("Expected LowerClosedUpperUnbounded interval"),
            }
        }

        /// Test hull of bounded with open upper unbounded (-∞, a)
        #[test]
        fn hull_bounded_with_lower_unbounded_upper_open() {
            let bounded =
                IntervalClosed::new(Real::try_new(-5.0).unwrap(), Real::try_new(0.0).unwrap());
            let unbounded = IntervalLowerUnboundedUpperOpen::new(Real::try_new(-2.0).unwrap());

            let hull = bounded.hull(&unbounded);

            // Hull should be (-∞, 0] - bounded extends above unbounded
            match hull {
                Interval::InfiniteLength(IntervalInfiniteLength::LowerUnboundedUpperClosed(
                    result,
                )) => {
                    assert_eq!(result.upper_bound_value(), &0.0);
                }
                _ => panic!("Expected LowerUnboundedUpperClosed interval"),
            }
        }

        /// Test hull of opposite-direction unbounded: [a, +∞) ∪ (-∞, b]
        #[test]
        fn hull_opposite_unbounded_closed_disjoint() {
            let lower_unbounded =
                IntervalLowerUnboundedUpperClosed::new(Real::try_new(-5.0).unwrap());
            let upper_unbounded =
                IntervalLowerClosedUpperUnbounded::new(Real::try_new(10.0).unwrap());

            let hull = lower_unbounded.hull(&upper_unbounded);

            // Hull should be (-∞, +∞) - covers entire real line
            match hull {
                Interval::InfiniteLength(IntervalInfiniteLength::LowerUnboundedUpperUnbounded(
                    _,
                )) => {
                    // Success - fully unbounded
                }
                _ => panic!("Expected LowerUnboundedUpperUnbounded interval"),
            }
        }

        /// Test hull of opposite-direction unbounded: [a, +∞) ∪ (-∞, b) with gap
        #[test]
        fn hull_opposite_unbounded_mixed_boundaries_gap() {
            let lower_unbounded = IntervalLowerUnboundedUpperOpen::new(Real::try_new(0.0).unwrap());
            let upper_unbounded =
                IntervalLowerClosedUpperUnbounded::new(Real::try_new(5.0).unwrap());

            let hull = lower_unbounded.hull(&upper_unbounded);

            // Hull should be (-∞, +∞) - covers entire real line despite gap
            match hull {
                Interval::InfiniteLength(IntervalInfiniteLength::LowerUnboundedUpperUnbounded(
                    _,
                )) => {
                    // Success - fully unbounded
                }
                _ => panic!("Expected LowerUnboundedUpperUnbounded interval"),
            }
        }

        /// Test hull of opposite-direction unbounded: (a, +∞) ∪ (-∞, b] overlapping
        #[test]
        fn hull_opposite_unbounded_overlapping() {
            let lower_unbounded =
                IntervalLowerUnboundedUpperClosed::new(Real::try_new(10.0).unwrap());
            let upper_unbounded = IntervalLowerOpenUpperUnbounded::new(Real::try_new(5.0).unwrap());

            let hull = lower_unbounded.hull(&upper_unbounded);

            // Hull should be (-∞, +∞) - intervals overlap
            match hull {
                Interval::InfiniteLength(IntervalInfiniteLength::LowerUnboundedUpperUnbounded(
                    _,
                )) => {
                    // Success - fully unbounded
                }
                _ => panic!("Expected LowerUnboundedUpperUnbounded interval"),
            }
        }

        /// Test hull of opposite-direction unbounded: (a, +∞) ∪ (-∞, b)
        #[test]
        fn hull_opposite_unbounded_both_open() {
            let lower_unbounded = IntervalLowerUnboundedUpperOpen::new(Real::try_new(3.0).unwrap());
            let upper_unbounded =
                IntervalLowerOpenUpperUnbounded::new(Real::try_new(-2.0).unwrap());

            let hull = lower_unbounded.hull(&upper_unbounded);

            // Hull should be (-∞, +∞)
            match hull {
                Interval::InfiniteLength(IntervalInfiniteLength::LowerUnboundedUpperUnbounded(
                    _,
                )) => {
                    // Success - fully unbounded
                }
                _ => panic!("Expected LowerUnboundedUpperUnbounded interval"),
            }
        }

        /// Test hull: (a, +∞) ∪ (b, +∞) with different bounds
        #[test]
        fn hull_two_open_lower_unbounded() {
            let unbounded1 = IntervalLowerOpenUpperUnbounded::new(Real::try_new(5.0).unwrap());
            let unbounded2 = IntervalLowerOpenUpperUnbounded::new(Real::try_new(2.0).unwrap());

            let hull = unbounded1.hull(&unbounded2);

            // Hull should be (2, +∞) - minimum lower bound
            match hull {
                Interval::InfiniteLength(IntervalInfiniteLength::LowerOpenUpperUnbounded(
                    result,
                )) => {
                    assert_eq!(result.lower_bound_value(), &2.0);
                }
                _ => panic!("Expected LowerOpenUpperUnbounded interval"),
            }
        }

        /// Test hull: (-∞, a) ∪ (-∞, b) with different bounds
        #[test]
        fn hull_two_open_upper_unbounded() {
            let unbounded1 = IntervalLowerUnboundedUpperOpen::new(Real::try_new(3.0).unwrap());
            let unbounded2 = IntervalLowerUnboundedUpperOpen::new(Real::try_new(7.0).unwrap());

            let hull = unbounded1.hull(&unbounded2);

            // Hull should be (-∞, 7) - maximum upper bound
            match hull {
                Interval::InfiniteLength(IntervalInfiniteLength::LowerUnboundedUpperOpen(
                    result,
                )) => {
                    assert_eq!(result.upper_bound_value(), &7.0);
                }
                _ => panic!("Expected LowerUnboundedUpperOpen interval"),
            }
        }

        /// Test hull: mixed open/closed at same bound [a, +∞) ∪ (a, +∞)
        #[test]
        fn hull_same_bound_closed_vs_open_lower_unbounded() {
            let closed_unbounded =
                IntervalLowerClosedUpperUnbounded::new(Real::try_new(5.0).unwrap());
            let open_unbounded = IntervalLowerOpenUpperUnbounded::new(Real::try_new(5.0).unwrap());

            let hull = closed_unbounded.hull(&open_unbounded);

            // Hull should be [5, +∞) - closed wins
            match hull {
                Interval::InfiniteLength(IntervalInfiniteLength::LowerClosedUpperUnbounded(
                    result,
                )) => {
                    assert_eq!(result.lower_bound_value(), &5.0);
                }
                _ => panic!("Expected LowerClosedUpperUnbounded interval"),
            }
        }

        /// Test hull: mixed open/closed at same bound (-∞, a] ∪ (-∞, a)
        #[test]
        fn hull_same_bound_closed_vs_open_upper_unbounded() {
            let closed_unbounded =
                IntervalLowerUnboundedUpperClosed::new(Real::try_new(3.0).unwrap());
            let open_unbounded = IntervalLowerUnboundedUpperOpen::new(Real::try_new(3.0).unwrap());

            let hull = closed_unbounded.hull(&open_unbounded);

            // Hull should be (-∞, 3] - closed wins
            match hull {
                Interval::InfiniteLength(IntervalInfiniteLength::LowerUnboundedUpperClosed(
                    result,
                )) => {
                    assert_eq!(result.upper_bound_value(), &3.0);
                }
                _ => panic!("Expected LowerUnboundedUpperClosed interval"),
            }
        }

        // Reverse tests - testing commutativity with different interval types

        /// Test hull commutativity: [a, +∞) ∪ [bounded] (reverse of hull_bounded_with_lower_bounded_upper_unbounded)
        #[test]
        fn hull_lower_bounded_upper_unbounded_with_bounded() {
            let unbounded = IntervalLowerClosedUpperUnbounded::new(Real::try_new(1.0).unwrap());
            let bounded =
                IntervalClosed::new(Real::try_new(0.0).unwrap(), Real::try_new(2.0).unwrap());

            let hull = unbounded.hull(&bounded);

            // Hull should be [0, +∞)
            match hull {
                Interval::InfiniteLength(IntervalInfiniteLength::LowerClosedUpperUnbounded(
                    result,
                )) => {
                    assert_eq!(result.lower_bound_value(), &0.0);
                }
                _ => panic!("Expected LowerClosedUpperUnbounded interval"),
            }
        }

        /// Test hull commutativity: (-∞, a] ∪ [bounded] (reverse of hull_bounded_with_lower_unbounded_upper_bounded)
        #[test]
        fn hull_lower_unbounded_upper_bounded_with_bounded() {
            let upper_unbounded =
                IntervalLowerUnboundedUpperClosed::new(Real::try_new(0.0).unwrap());
            let bounded =
                IntervalClosed::new(Real::try_new(-2.0).unwrap(), Real::try_new(1.0).unwrap());

            let hull = upper_unbounded.hull(&bounded);

            // Hull should be (-∞, 1]
            match hull {
                Interval::InfiniteLength(IntervalInfiniteLength::LowerUnboundedUpperClosed(
                    result,
                )) => {
                    assert_eq!(result.upper_bound_value(), &1.0);
                }
                _ => panic!("Expected LowerUnboundedUpperClosed interval"),
            }
        }

        /// Test hull commutativity: (-∞, +∞) ∪ [bounded] (reverse of hull_bounded_with_fully_unbounded)
        #[test]
        fn hull_fully_unbounded_with_bounded() {
            let fully_unbounded = IntervalLowerUnboundedUpperUnbounded::<Real>::new();
            let bounded =
                IntervalClosed::new(Real::try_new(0.0).unwrap(), Real::try_new(10.0).unwrap());

            let hull = fully_unbounded.hull(&bounded);

            // Hull should be (-∞, +∞)
            match hull {
                Interval::InfiniteLength(IntervalInfiniteLength::LowerUnboundedUpperUnbounded(
                    _,
                )) => {
                    // Success - fully unbounded
                }
                _ => panic!("Expected LowerUnboundedUpperUnbounded interval"),
            }
        }

        /// Test hull commutativity: [a, +∞) ∪ (open) (reverse of hull_open_bounded_with_closed_unbounded)
        #[test]
        fn hull_closed_unbounded_with_open_bounded() {
            let unbounded = IntervalLowerClosedUpperUnbounded::new(Real::try_new(1.5).unwrap());
            let open = IntervalOpen::new(Real::try_new(0.0).unwrap(), Real::try_new(2.0).unwrap());

            let hull = unbounded.hull(&open);

            // Hull should be (0, +∞) - open lower bound wins
            match hull {
                Interval::InfiniteLength(IntervalInfiniteLength::LowerOpenUpperUnbounded(
                    result,
                )) => {
                    assert_eq!(result.lower_bound_value(), &0.0);
                }
                _ => panic!("Expected LowerOpenUpperUnbounded interval"),
            }
        }

        /// Test hull commutativity: (a, +∞) ∪ [bounded] (reverse of hull_bounded_with_lower_open_upper_unbounded)
        #[test]
        fn hull_lower_open_upper_unbounded_with_bounded() {
            let unbounded = IntervalLowerOpenUpperUnbounded::new(Real::try_new(3.0).unwrap());
            let bounded =
                IntervalClosed::new(Real::try_new(0.0).unwrap(), Real::try_new(5.0).unwrap());

            let hull = unbounded.hull(&bounded);

            // Hull should be [0, +∞) - bounded extends below unbounded
            match hull {
                Interval::InfiniteLength(IntervalInfiniteLength::LowerClosedUpperUnbounded(
                    result,
                )) => {
                    assert_eq!(result.lower_bound_value(), &0.0);
                }
                _ => panic!("Expected LowerClosedUpperUnbounded interval"),
            }
        }

        /// Test hull commutativity: (-∞, a) ∪ [bounded] (reverse of hull_bounded_with_lower_unbounded_upper_open)
        #[test]
        fn hull_lower_unbounded_upper_open_with_bounded() {
            let unbounded = IntervalLowerUnboundedUpperOpen::new(Real::try_new(-2.0).unwrap());
            let bounded =
                IntervalClosed::new(Real::try_new(-5.0).unwrap(), Real::try_new(0.0).unwrap());

            let hull = unbounded.hull(&bounded);

            // Hull should be (-∞, 0] - bounded extends above unbounded
            match hull {
                Interval::InfiniteLength(IntervalInfiniteLength::LowerUnboundedUpperClosed(
                    result,
                )) => {
                    assert_eq!(result.upper_bound_value(), &0.0);
                }
                _ => panic!("Expected LowerUnboundedUpperClosed interval"),
            }
        }

        /// Test hull commutativity: [a, +∞) ∪ (-∞, b] (reverse of hull_opposite_unbounded_closed_disjoint)
        #[test]
        fn hull_opposite_unbounded_closed_disjoint_reverse() {
            let upper_unbounded =
                IntervalLowerClosedUpperUnbounded::new(Real::try_new(10.0).unwrap());
            let lower_unbounded =
                IntervalLowerUnboundedUpperClosed::new(Real::try_new(-5.0).unwrap());

            let hull = upper_unbounded.hull(&lower_unbounded);

            // Hull should be (-∞, +∞) - covers entire real line
            match hull {
                Interval::InfiniteLength(IntervalInfiniteLength::LowerUnboundedUpperUnbounded(
                    _,
                )) => {
                    // Success - fully unbounded
                }
                _ => panic!("Expected LowerUnboundedUpperUnbounded interval"),
            }
        }

        /// Test hull commutativity: [a, +∞) ∪ (-∞, b) (reverse of hull_opposite_unbounded_mixed_boundaries_gap)
        #[test]
        fn hull_opposite_unbounded_mixed_boundaries_gap_reverse() {
            let upper_unbounded =
                IntervalLowerClosedUpperUnbounded::new(Real::try_new(5.0).unwrap());
            let lower_unbounded = IntervalLowerUnboundedUpperOpen::new(Real::try_new(0.0).unwrap());

            let hull = upper_unbounded.hull(&lower_unbounded);

            // Hull should be (-∞, +∞) - covers entire real line despite gap
            match hull {
                Interval::InfiniteLength(IntervalInfiniteLength::LowerUnboundedUpperUnbounded(
                    _,
                )) => {
                    // Success - fully unbounded
                }
                _ => panic!("Expected LowerUnboundedUpperUnbounded interval"),
            }
        }

        /// Test hull commutativity: (a, +∞) ∪ (-∞, b] (reverse of hull_opposite_unbounded_overlapping)
        #[test]
        fn hull_opposite_unbounded_overlapping_reverse() {
            let upper_unbounded = IntervalLowerOpenUpperUnbounded::new(Real::try_new(5.0).unwrap());
            let lower_unbounded =
                IntervalLowerUnboundedUpperClosed::new(Real::try_new(10.0).unwrap());

            let hull = upper_unbounded.hull(&lower_unbounded);

            // Hull should be (-∞, +∞) - intervals overlap
            match hull {
                Interval::InfiniteLength(IntervalInfiniteLength::LowerUnboundedUpperUnbounded(
                    _,
                )) => {
                    // Success - fully unbounded
                }
                _ => panic!("Expected LowerUnboundedUpperUnbounded interval"),
            }
        }

        /// Test hull commutativity: (-2, +∞) ∪ (-∞, 3) (reverse of hull_opposite_unbounded_both_open)
        #[test]
        fn hull_opposite_unbounded_both_open_reverse() {
            let upper_unbounded =
                IntervalLowerOpenUpperUnbounded::new(Real::try_new(-2.0).unwrap());
            let lower_unbounded = IntervalLowerUnboundedUpperOpen::new(Real::try_new(3.0).unwrap());

            let hull = upper_unbounded.hull(&lower_unbounded);

            // Hull should be (-∞, +∞)
            match hull {
                Interval::InfiniteLength(IntervalInfiniteLength::LowerUnboundedUpperUnbounded(
                    _,
                )) => {
                    // Success - fully unbounded
                }
                _ => panic!("Expected LowerUnboundedUpperUnbounded interval"),
            }
        }

        // Tests for hull with IntervalFiniteLength and IntervalFinitePositiveLength as self

        /// Test hull with IntervalFiniteLength::PositiveLength (closed) as self
        #[test]
        fn hull_finite_positive_closed_with_bounded() {
            let interval1: Interval<Real> = Interval::FiniteLength(
                IntervalFiniteLength::PositiveLength(IntervalFinitePositiveLength::Closed(
                    IntervalClosed::new(Real::try_new(0.0).unwrap(), Real::try_new(2.0).unwrap()),
                )),
            );
            let interval2: Interval<Real> = Interval::from(IntervalClosed::new(
                Real::try_new(1.0).unwrap(),
                Real::try_new(3.0).unwrap(),
            ));

            let hull = interval1.hull(&interval2);

            // Hull should be [0, 3]
            match hull {
                Interval::FiniteLength(IntervalFiniteLength::PositiveLength(
                    IntervalFinitePositiveLength::Closed(result),
                )) => {
                    assert_eq!(result.lower_bound_value(), &0.0);
                    assert_eq!(result.upper_bound_value(), &3.0);
                }
                _ => panic!("Expected closed bounded interval"),
            }
        }

        /// Test hull with IntervalFiniteLength::PositiveLength (open) as self
        #[test]
        fn hull_finite_positive_open_with_closed() {
            let interval1: Interval<Real> = Interval::FiniteLength(
                IntervalFiniteLength::PositiveLength(IntervalFinitePositiveLength::Open(
                    IntervalOpen::new(Real::try_new(0.0).unwrap(), Real::try_new(2.0).unwrap()),
                )),
            );
            let interval2: Interval<Real> = Interval::from(IntervalClosed::new(
                Real::try_new(1.0).unwrap(),
                Real::try_new(3.0).unwrap(),
            ));

            let hull = interval1.hull(&interval2);

            // Hull should be (0, 3] - open lower, closed upper
            match hull {
                Interval::FiniteLength(IntervalFiniteLength::PositiveLength(
                    IntervalFinitePositiveLength::LowerOpenUpperClosed(result),
                )) => {
                    assert_eq!(result.lower_bound_value(), &0.0);
                    assert_eq!(result.upper_bound_value(), &3.0);
                }
                _ => panic!("Expected lower open, upper closed interval"),
            }
        }

        /// Test hull with IntervalFiniteLength::PositiveLength (half-open) as self
        #[test]
        fn hull_finite_positive_half_open_with_open() {
            let interval1: Interval<Real> =
                Interval::FiniteLength(IntervalFiniteLength::PositiveLength(
                    IntervalFinitePositiveLength::LowerClosedUpperOpen(
                        IntervalLowerClosedUpperOpen::new(
                            Real::try_new(0.0).unwrap(),
                            Real::try_new(2.0).unwrap(),
                        ),
                    ),
                ));
            let interval2: Interval<Real> = Interval::from(IntervalOpen::new(
                Real::try_new(-1.0).unwrap(),
                Real::try_new(1.0).unwrap(),
            ));

            let hull = interval1.hull(&interval2);

            // Hull should be (-1, 2) - both open
            match hull {
                Interval::FiniteLength(IntervalFiniteLength::PositiveLength(
                    IntervalFinitePositiveLength::Open(result),
                )) => {
                    assert_eq!(result.lower_bound_value(), &-1.0);
                    assert_eq!(result.upper_bound_value(), &2.0);
                }
                _ => panic!("Expected open interval"),
            }
        }

        /// Test hull with IntervalFiniteLength::ZeroLength (singleton) as self
        #[test]
        fn hull_finite_zero_length_with_bounded() {
            let interval1: Interval<Real> =
                Interval::FiniteLength(IntervalFiniteLength::ZeroLength(IntervalSingleton::new(
                    Real::try_new(5.0).unwrap(),
                )));
            let interval2: Interval<Real> = Interval::from(IntervalClosed::new(
                Real::try_new(0.0).unwrap(),
                Real::try_new(3.0).unwrap(),
            ));

            let hull = interval1.hull(&interval2);

            // Hull should be [0, 5]
            match hull {
                Interval::FiniteLength(IntervalFiniteLength::PositiveLength(
                    IntervalFinitePositiveLength::Closed(result),
                )) => {
                    assert_eq!(result.lower_bound_value(), &0.0);
                    assert_eq!(result.upper_bound_value(), &5.0);
                }
                _ => panic!("Expected closed bounded interval"),
            }
        }

        /// Test hull with IntervalFiniteLength as self and unbounded interval
        #[test]
        fn hull_finite_length_with_unbounded() {
            let interval1: Interval<Real> = Interval::FiniteLength(
                IntervalFiniteLength::PositiveLength(IntervalFinitePositiveLength::Closed(
                    IntervalClosed::new(Real::try_new(0.0).unwrap(), Real::try_new(5.0).unwrap()),
                )),
            );
            let interval2: Interval<Real> = Interval::from(IntervalLowerClosedUpperUnbounded::new(
                Real::try_new(3.0).unwrap(),
            ));

            let hull = interval1.hull(&interval2);

            // Hull should be [0, +∞)
            match hull {
                Interval::InfiniteLength(IntervalInfiniteLength::LowerClosedUpperUnbounded(
                    result,
                )) => {
                    assert_eq!(result.lower_bound_value(), &0.0);
                }
                _ => panic!("Expected lower closed, upper unbounded interval"),
            }
        }

        /// Test hull with IntervalFinitePositiveLength directly constructed
        #[test]
        fn hull_finite_positive_length_disjoint() {
            let interval1: Interval<Real> = Interval::FiniteLength(
                IntervalFiniteLength::PositiveLength(IntervalFinitePositiveLength::Closed(
                    IntervalClosed::new(Real::try_new(0.0).unwrap(), Real::try_new(2.0).unwrap()),
                )),
            );
            let interval2: Interval<Real> = Interval::FiniteLength(
                IntervalFiniteLength::PositiveLength(IntervalFinitePositiveLength::Closed(
                    IntervalClosed::new(Real::try_new(5.0).unwrap(), Real::try_new(7.0).unwrap()),
                )),
            );

            let hull = interval1.hull(&interval2);

            // Hull should be [0, 7] spanning the gap
            match hull {
                Interval::FiniteLength(IntervalFiniteLength::PositiveLength(
                    IntervalFinitePositiveLength::Closed(result),
                )) => {
                    assert_eq!(result.lower_bound_value(), &0.0);
                    assert_eq!(result.upper_bound_value(), &7.0);
                }
                _ => panic!("Expected closed bounded interval"),
            }
        }

        /// Test hull with both IntervalFiniteLength::ZeroLength (two singletons)
        #[test]
        fn hull_two_finite_zero_length() {
            let interval1: Interval<Real> =
                Interval::FiniteLength(IntervalFiniteLength::ZeroLength(IntervalSingleton::new(
                    Real::try_new(2.0).unwrap(),
                )));
            let interval2: Interval<Real> =
                Interval::FiniteLength(IntervalFiniteLength::ZeroLength(IntervalSingleton::new(
                    Real::try_new(5.0).unwrap(),
                )));

            let hull = interval1.hull(&interval2);

            // Hull should be [2, 5]
            match hull {
                Interval::FiniteLength(IntervalFiniteLength::PositiveLength(
                    IntervalFinitePositiveLength::Closed(result),
                )) => {
                    assert_eq!(result.lower_bound_value(), &2.0);
                    assert_eq!(result.upper_bound_value(), &5.0);
                }
                _ => panic!("Expected closed bounded interval"),
            }
        }

        /// Test hull with IntervalFiniteLength and fully unbounded
        #[test]
        fn hull_finite_length_with_fully_unbounded() {
            let interval1: Interval<Real> = Interval::FiniteLength(
                IntervalFiniteLength::PositiveLength(IntervalFinitePositiveLength::Open(
                    IntervalOpen::new(Real::try_new(0.0).unwrap(), Real::try_new(1.0).unwrap()),
                )),
            );
            let interval2: Interval<Real> =
                Interval::from(IntervalLowerUnboundedUpperUnbounded::<Real>::new());

            let hull = interval1.hull(&interval2);

            // Hull should be (-∞, +∞)
            match hull {
                Interval::InfiniteLength(IntervalInfiniteLength::LowerUnboundedUpperUnbounded(
                    _,
                )) => {
                    // Success - fully unbounded
                }
                _ => panic!("Expected fully unbounded interval"),
            }
        }

        /// Test hull with mixed IntervalFinitePositiveLength variants
        #[test]
        fn hull_finite_positive_mixed_boundaries() {
            let interval1 = IntervalFiniteLength::PositiveLength(
                IntervalFinitePositiveLength::LowerClosedUpperOpen(
                    IntervalLowerClosedUpperOpen::new(
                        Real::try_new(0.0).unwrap(),
                        Real::try_new(3.0).unwrap(),
                    ),
                ),
            );
            let interval2: Interval<Real> =
                Interval::FiniteLength(IntervalFiniteLength::PositiveLength(
                    IntervalFinitePositiveLength::LowerOpenUpperClosed(
                        IntervalLowerOpenUpperClosed::new(
                            Real::try_new(2.0).unwrap(),
                            Real::try_new(5.0).unwrap(),
                        ),
                    ),
                ));

            let hull = interval1.hull(&interval2);

            // Hull should be [0, 5] - closed on both ends
            match hull {
                Interval::FiniteLength(IntervalFiniteLength::PositiveLength(
                    IntervalFinitePositiveLength::Closed(result),
                )) => {
                    assert_eq!(result.lower_bound_value(), &0.0);
                    assert_eq!(result.upper_bound_value(), &5.0);
                }
                _ => panic!("Expected closed bounded interval"),
            }
        }

        // Tests for hull with IntervalInfiniteLength as self

        /// Test hull with IntervalInfiniteLength::LowerClosedUpperUnbounded as self
        #[test]
        fn hull_infinite_lower_closed_upper_unbounded_with_bounded() {
            let interval1 = IntervalInfiniteLength::LowerClosedUpperUnbounded(
                IntervalLowerClosedUpperUnbounded::new(Real::try_new(5.0).unwrap()),
            );
            let interval2: Interval<Real> = Interval::from(IntervalClosed::new(
                Real::try_new(0.0).unwrap(),
                Real::try_new(3.0).unwrap(),
            ));

            let hull = interval1.hull(&interval2);

            // Hull should be [0, +∞)
            match hull {
                Interval::InfiniteLength(IntervalInfiniteLength::LowerClosedUpperUnbounded(
                    result,
                )) => {
                    assert_eq!(result.lower_bound_value(), &0.0);
                }
                _ => panic!("Expected LowerClosedUpperUnbounded interval"),
            }
        }

        /// Test hull with IntervalInfiniteLength::LowerOpenUpperUnbounded as self
        #[test]
        fn hull_infinite_lower_open_upper_unbounded_with_bounded() {
            let interval1 = IntervalInfiniteLength::LowerOpenUpperUnbounded(
                IntervalLowerOpenUpperUnbounded::new(Real::try_new(2.0).unwrap()),
            );
            let interval2: Interval<Real> = Interval::from(IntervalClosed::new(
                Real::try_new(0.0).unwrap(),
                Real::try_new(5.0).unwrap(),
            ));

            let hull = interval1.hull(&interval2);

            // Hull should be [0, +∞) - closed lower wins
            match hull {
                Interval::InfiniteLength(IntervalInfiniteLength::LowerClosedUpperUnbounded(
                    result,
                )) => {
                    assert_eq!(result.lower_bound_value(), &0.0);
                }
                _ => panic!("Expected LowerClosedUpperUnbounded interval"),
            }
        }

        /// Test hull with IntervalInfiniteLength::LowerUnboundedUpperClosed as self
        #[test]
        fn hull_infinite_lower_unbounded_upper_closed_with_bounded() {
            let interval1: Interval<Real> =
                Interval::InfiniteLength(IntervalInfiniteLength::LowerUnboundedUpperClosed(
                    IntervalLowerUnboundedUpperClosed::new(Real::try_new(0.0).unwrap()),
                ));
            let interval2: Interval<Real> = Interval::from(IntervalClosed::new(
                Real::try_new(2.0).unwrap(),
                Real::try_new(5.0).unwrap(),
            ));

            let hull = interval1.hull(&interval2);

            // Hull should be (-∞, 5]
            match hull {
                Interval::InfiniteLength(IntervalInfiniteLength::LowerUnboundedUpperClosed(
                    result,
                )) => {
                    assert_eq!(result.upper_bound_value(), &5.0);
                }
                _ => panic!("Expected LowerUnboundedUpperClosed interval"),
            }
        }

        /// Test hull with IntervalInfiniteLength::LowerUnboundedUpperOpen as self
        #[test]
        fn hull_infinite_lower_unbounded_upper_open_with_bounded() {
            let interval1: Interval<Real> =
                Interval::InfiniteLength(IntervalInfiniteLength::LowerUnboundedUpperOpen(
                    IntervalLowerUnboundedUpperOpen::new(Real::try_new(3.0).unwrap()),
                ));
            let interval2: Interval<Real> = Interval::from(IntervalOpen::new(
                Real::try_new(-2.0).unwrap(),
                Real::try_new(10.0).unwrap(),
            ));

            let hull = interval1.hull(&interval2);

            // Hull should be (-∞, 10) - open upper wins
            match hull {
                Interval::InfiniteLength(IntervalInfiniteLength::LowerUnboundedUpperOpen(
                    result,
                )) => {
                    assert_eq!(result.upper_bound_value(), &10.0);
                }
                _ => panic!("Expected LowerUnboundedUpperOpen interval"),
            }
        }

        /// Test hull with IntervalInfiniteLength::LowerUnboundedUpperUnbounded as self
        #[test]
        fn hull_infinite_fully_unbounded_with_bounded() {
            let interval1: Interval<Real> =
                Interval::InfiniteLength(IntervalInfiniteLength::LowerUnboundedUpperUnbounded(
                    IntervalLowerUnboundedUpperUnbounded::<Real>::new(),
                ));
            let interval2: Interval<Real> = Interval::from(IntervalClosed::new(
                Real::try_new(0.0).unwrap(),
                Real::try_new(10.0).unwrap(),
            ));

            let hull = interval1.hull(&interval2);

            // Hull should be (-∞, +∞)
            match hull {
                Interval::InfiniteLength(IntervalInfiniteLength::LowerUnboundedUpperUnbounded(
                    _,
                )) => {
                    // Success - fully unbounded
                }
                _ => panic!("Expected LowerUnboundedUpperUnbounded interval"),
            }
        }

        /// Test hull between two IntervalInfiniteLength with opposite directions
        #[test]
        fn hull_infinite_opposite_directions() {
            let interval1: Interval<Real> =
                Interval::InfiniteLength(IntervalInfiniteLength::LowerClosedUpperUnbounded(
                    IntervalLowerClosedUpperUnbounded::new(Real::try_new(5.0).unwrap()),
                ));
            let interval2: Interval<Real> =
                Interval::InfiniteLength(IntervalInfiniteLength::LowerUnboundedUpperClosed(
                    IntervalLowerUnboundedUpperClosed::new(Real::try_new(0.0).unwrap()),
                ));

            let hull = interval1.hull(&interval2);

            // Hull should be (-∞, +∞)
            match hull {
                Interval::InfiniteLength(IntervalInfiniteLength::LowerUnboundedUpperUnbounded(
                    _,
                )) => {
                    // Success - fully unbounded
                }
                _ => panic!("Expected LowerUnboundedUpperUnbounded interval"),
            }
        }

        /// Test hull between IntervalInfiniteLength and IntervalFiniteLength::ZeroLength
        #[test]
        fn hull_infinite_with_finite_zero_length() {
            let interval1: Interval<Real> =
                Interval::InfiniteLength(IntervalInfiniteLength::LowerClosedUpperUnbounded(
                    IntervalLowerClosedUpperUnbounded::new(Real::try_new(5.0).unwrap()),
                ));
            let interval2: Interval<Real> =
                Interval::FiniteLength(IntervalFiniteLength::ZeroLength(IntervalSingleton::new(
                    Real::try_new(2.0).unwrap(),
                )));

            let hull = interval1.hull(&interval2);

            // Hull should be [2, +∞)
            match hull {
                Interval::InfiniteLength(IntervalInfiniteLength::LowerClosedUpperUnbounded(
                    result,
                )) => {
                    assert_eq!(result.lower_bound_value(), &2.0);
                }
                _ => panic!("Expected LowerClosedUpperUnbounded interval"),
            }
        }

        /// Test hull between two IntervalInfiniteLength with same direction (open vs closed)
        #[test]
        fn hull_infinite_same_direction_open_vs_closed() {
            let interval1: Interval<Real> =
                Interval::InfiniteLength(IntervalInfiniteLength::LowerClosedUpperUnbounded(
                    IntervalLowerClosedUpperUnbounded::new(Real::try_new(5.0).unwrap()),
                ));
            let interval2: Interval<Real> =
                Interval::InfiniteLength(IntervalInfiniteLength::LowerOpenUpperUnbounded(
                    IntervalLowerOpenUpperUnbounded::new(Real::try_new(5.0).unwrap()),
                ));

            let hull = interval1.hull(&interval2);

            // Hull should be [5, +∞) - closed wins
            match hull {
                Interval::InfiniteLength(IntervalInfiniteLength::LowerClosedUpperUnbounded(
                    result,
                )) => {
                    assert_eq!(result.lower_bound_value(), &5.0);
                }
                _ => panic!("Expected LowerClosedUpperUnbounded interval"),
            }
        }

        /// Test hull between IntervalInfiniteLength with disjoint regions
        #[test]
        fn hull_infinite_disjoint_regions() {
            let interval1: Interval<Real> =
                Interval::InfiniteLength(IntervalInfiniteLength::LowerClosedUpperUnbounded(
                    IntervalLowerClosedUpperUnbounded::new(Real::try_new(10.0).unwrap()),
                ));
            let interval2: Interval<Real> =
                Interval::InfiniteLength(IntervalInfiniteLength::LowerUnboundedUpperOpen(
                    IntervalLowerUnboundedUpperOpen::new(Real::try_new(5.0).unwrap()),
                ));

            let hull = interval1.hull(&interval2);

            // Hull should be (-∞, +∞) - spans the gap
            match hull {
                Interval::InfiniteLength(IntervalInfiniteLength::LowerUnboundedUpperUnbounded(
                    _,
                )) => {
                    // Success - fully unbounded
                }
                _ => panic!("Expected LowerUnboundedUpperUnbounded interval"),
            }
        }

        /// Test hull with IntervalInfiniteLength and unbounded in same direction
        #[test]
        fn hull_infinite_same_direction_different_bounds() {
            let interval1: Interval<Real> =
                Interval::InfiniteLength(IntervalInfiniteLength::LowerUnboundedUpperClosed(
                    IntervalLowerUnboundedUpperClosed::new(Real::try_new(10.0).unwrap()),
                ));
            let interval2: Interval<Real> =
                Interval::InfiniteLength(IntervalInfiniteLength::LowerUnboundedUpperClosed(
                    IntervalLowerUnboundedUpperClosed::new(Real::try_new(5.0).unwrap()),
                ));

            let hull = interval1.hull(&interval2);

            // Hull should be (-∞, 10] - maximum upper bound
            match hull {
                Interval::InfiniteLength(IntervalInfiniteLength::LowerUnboundedUpperClosed(
                    result,
                )) => {
                    assert_eq!(result.upper_bound_value(), &10.0);
                }
                _ => panic!("Expected LowerUnboundedUpperClosed interval"),
            }
        }
    }

    /// Tests for interval type conversions using TryFrom
    mod try_from_conversions {
        use super::*;
        use num_valid::RealNative64StrictFiniteInDebug;
        use try_create::TryNew;

        type Real = RealNative64StrictFiniteInDebug;

        // Tests for Interval -> IntervalFiniteLength

        /// Test successful conversion from Interval to IntervalFiniteLength (positive length)
        #[test]
        fn try_from_interval_to_finite_length_positive() {
            let interval: Interval<Real> = Interval::from(IntervalClosed::new(
                Real::try_new(0.0).unwrap(),
                Real::try_new(5.0).unwrap(),
            ));

            let result = IntervalFiniteLength::try_from(interval);
            assert!(result.is_ok());

            let finite = result.unwrap();
            match finite {
                IntervalFiniteLength::PositiveLength(positive) => {
                    assert_eq!(positive.lower_bound_value(), &Real::try_new(0.0).unwrap());
                    assert_eq!(positive.upper_bound_value(), &Real::try_new(5.0).unwrap());
                }
                _ => panic!("Expected PositiveLength variant"),
            }
        }

        /// Test successful conversion from Interval to IntervalFiniteLength (zero length)
        #[test]
        fn try_from_interval_to_finite_length_zero() {
            let interval: Interval<Real> =
                Interval::from(IntervalSingleton::new(Real::try_new(3.0).unwrap()));

            let result = IntervalFiniteLength::try_from(interval);
            assert!(result.is_ok());

            let finite = result.unwrap();
            match finite {
                IntervalFiniteLength::ZeroLength(singleton) => {
                    assert_eq!(singleton.value(), &Real::try_new(3.0).unwrap());
                }
                _ => panic!("Expected ZeroLength variant"),
            }
        }

        /// Test failed conversion from infinite length Interval to IntervalFiniteLength
        #[test]
        fn try_from_interval_to_finite_length_fails_for_infinite() {
            let interval: Interval<Real> =
                Interval::InfiniteLength(IntervalInfiniteLength::LowerClosedUpperUnbounded(
                    IntervalLowerClosedUpperUnbounded::new(Real::try_new(0.0).unwrap()),
                ));

            let result = IntervalFiniteLength::<Real>::try_from(interval);
            assert!(result.is_err());

            match result.unwrap_err() {
                ErrorsIntervalConversion::NotIntervalFiniteLength { .. } => {
                    // Expected error
                }
                _ => panic!("Expected NotIntervalFiniteLength error"),
            }
        }

        // Tests for Interval -> IntervalFinitePositiveLength

        /// Test successful conversion from Interval to IntervalFinitePositiveLength (closed)
        #[test]
        fn try_from_interval_to_finite_positive_closed() {
            let interval: Interval<Real> = Interval::from(IntervalClosed::new(
                Real::try_new(1.0).unwrap(),
                Real::try_new(10.0).unwrap(),
            ));

            let result = IntervalFinitePositiveLength::try_from(interval);
            assert!(result.is_ok());

            let positive = result.unwrap();
            match positive {
                IntervalFinitePositiveLength::Closed(_) => {
                    assert_eq!(positive.lower_bound_value(), &Real::try_new(1.0).unwrap());
                    assert_eq!(positive.upper_bound_value(), &Real::try_new(10.0).unwrap());
                }
                _ => panic!("Expected Closed variant"),
            }
        }

        /// Test successful conversion from Interval to IntervalFinitePositiveLength (open)
        #[test]
        fn try_from_interval_to_finite_positive_open() {
            let interval: Interval<Real> = Interval::from(IntervalOpen::new(
                Real::try_new(0.0).unwrap(),
                Real::try_new(5.0).unwrap(),
            ));

            let result = IntervalFinitePositiveLength::try_from(interval);
            assert!(result.is_ok());

            let positive = result.unwrap();
            match positive {
                IntervalFinitePositiveLength::Open(_) => {}
                _ => panic!("Expected Open variant"),
            }
        }

        /// Test failed conversion from singleton to IntervalFinitePositiveLength
        #[test]
        fn try_from_interval_to_finite_positive_fails_for_singleton() {
            let interval: Interval<Real> =
                Interval::from(IntervalSingleton::new(Real::try_new(5.0).unwrap()));

            let result = IntervalFinitePositiveLength::<Real>::try_from(interval);
            assert!(result.is_err());

            match result.unwrap_err() {
                ErrorsIntervalConversion::NotIntervalPositiveLength { .. } => {
                    // Expected error
                }
                _ => panic!("Expected NotIntervalPositiveLength error"),
            }
        }

        /// Test failed conversion from infinite interval to IntervalFinitePositiveLength
        #[test]
        fn try_from_interval_to_finite_positive_fails_for_infinite() {
            let interval: Interval<Real> =
                Interval::InfiniteLength(IntervalInfiniteLength::LowerUnboundedUpperClosed(
                    IntervalLowerUnboundedUpperClosed::new(Real::try_new(10.0).unwrap()),
                ));

            let result = IntervalFinitePositiveLength::<Real>::try_from(interval);
            assert!(result.is_err());
        }

        // Tests for Interval -> IntervalInfiniteLength

        /// Test successful conversion from Interval to IntervalInfiniteLength (lower unbounded)
        #[test]
        fn try_from_interval_to_infinite_length_lower_unbounded() {
            let interval: Interval<Real> =
                Interval::InfiniteLength(IntervalInfiniteLength::LowerUnboundedUpperClosed(
                    IntervalLowerUnboundedUpperClosed::new(Real::try_new(5.0).unwrap()),
                ));

            let result = IntervalInfiniteLength::try_from(interval);
            assert!(result.is_ok());

            let infinite = result.unwrap();
            match infinite {
                IntervalInfiniteLength::LowerUnboundedUpperClosed(interval) => {
                    assert_eq!(interval.upper_bound_value(), &Real::try_new(5.0).unwrap());
                }
                _ => panic!("Expected LowerUnboundedUpperClosed variant"),
            }
        }

        /// Test successful conversion from Interval to IntervalInfiniteLength (upper unbounded)
        #[test]
        fn try_from_interval_to_infinite_length_upper_unbounded() {
            let interval: Interval<Real> =
                Interval::InfiniteLength(IntervalInfiniteLength::LowerClosedUpperUnbounded(
                    IntervalLowerClosedUpperUnbounded::new(Real::try_new(0.0).unwrap()),
                ));

            let result = IntervalInfiniteLength::try_from(interval);
            assert!(result.is_ok());

            let infinite = result.unwrap();
            match infinite {
                IntervalInfiniteLength::LowerClosedUpperUnbounded(interval) => {
                    assert_eq!(interval.lower_bound_value(), &Real::try_new(0.0).unwrap());
                }
                _ => panic!("Expected LowerClosedUpperUnbounded variant"),
            }
        }

        /// Test failed conversion from finite interval to IntervalInfiniteLength
        #[test]
        fn try_from_interval_to_infinite_fails_for_finite() {
            let interval: Interval<Real> = Interval::from(IntervalClosed::new(
                Real::try_new(0.0).unwrap(),
                Real::try_new(10.0).unwrap(),
            ));

            let result = IntervalInfiniteLength::<Real>::try_from(interval);
            assert!(result.is_err());

            match result.unwrap_err() {
                ErrorsIntervalConversion::NotIntervalInfiniteLength { .. } => {
                    // Expected error
                }
                _ => panic!("Expected NotIntervalInfiniteLength error"),
            }
        }

        // Tests for Interval -> specific bounded interval types (IntervalClosed, IntervalOpen, etc.)

        /// Test successful conversion from Interval to IntervalClosed
        #[test]
        fn try_from_interval_to_closed() {
            let interval: Interval<Real> = Interval::from(IntervalClosed::new(
                Real::try_new(2.0).unwrap(),
                Real::try_new(8.0).unwrap(),
            ));

            let result = IntervalClosed::<Real>::try_from(interval);
            assert!(result.is_ok());

            let closed = result.unwrap();
            assert_eq!(closed.lower_bound_value(), &Real::try_new(2.0).unwrap());
            assert_eq!(closed.upper_bound_value(), &Real::try_new(8.0).unwrap());
        }

        /// Test failed conversion from Interval to IntervalClosed (wrong type)
        #[test]
        fn try_from_interval_to_closed_fails_for_open() {
            let interval: Interval<Real> = Interval::from(IntervalOpen::new(
                Real::try_new(2.0).unwrap(),
                Real::try_new(8.0).unwrap(),
            ));

            let result = IntervalClosed::<Real>::try_from(interval);
            assert!(result.is_err());

            match result.unwrap_err() {
                ErrorsIntervalConversion::NotIntervalClosed { .. } => {
                    // Expected error
                }
                _ => panic!("Expected NotIntervalClosed error"),
            }
        }

        /// Test failed conversion from Interval to IntervalClosed (singleton)
        #[test]
        fn try_from_interval_to_closed_fails_for_singleton() {
            let interval: Interval<Real> =
                Interval::from(IntervalSingleton::new(Real::try_new(5.0).unwrap()));

            let result = IntervalClosed::<Real>::try_from(interval);
            assert!(result.is_err());
        }

        /// Test successful conversion from Interval to IntervalOpen
        #[test]
        fn try_from_interval_to_open() {
            let interval: Interval<Real> = Interval::from(IntervalOpen::new(
                Real::try_new(1.0).unwrap(),
                Real::try_new(9.0).unwrap(),
            ));

            let result = IntervalOpen::<Real>::try_from(interval);
            assert!(result.is_ok());

            let open = result.unwrap();
            assert_eq!(open.lower_bound_value(), &Real::try_new(1.0).unwrap());
            assert_eq!(open.upper_bound_value(), &Real::try_new(9.0).unwrap());
        }

        /// Test failed conversion from Interval to IntervalOpen (wrong type)
        #[test]
        fn try_from_interval_to_open_fails_for_closed() {
            let interval: Interval<Real> = Interval::from(IntervalClosed::new(
                Real::try_new(1.0).unwrap(),
                Real::try_new(9.0).unwrap(),
            ));

            let result = IntervalOpen::<Real>::try_from(interval);
            assert!(result.is_err());

            match result.unwrap_err() {
                ErrorsIntervalConversion::NotIntervalOpen { .. } => {
                    // Expected error
                }
                _ => panic!("Expected NotIntervalOpen error"),
            }
        }

        /// Test successful conversion from Interval to IntervalLowerOpenUpperClosed
        #[test]
        fn try_from_interval_to_lower_open_upper_closed() {
            let interval: Interval<Real> = Interval::from(IntervalLowerOpenUpperClosed::new(
                Real::try_new(0.0).unwrap(),
                Real::try_new(5.0).unwrap(),
            ));

            let result = IntervalLowerOpenUpperClosed::<Real>::try_from(interval);
            assert!(result.is_ok());

            let half_open = result.unwrap();
            assert!(!half_open.is_lower_bound_closed());
            assert!(half_open.is_upper_bound_closed());
        }

        /// Test failed conversion from Interval to IntervalLowerOpenUpperClosed
        #[test]
        fn try_from_interval_to_lower_open_upper_closed_fails() {
            let interval: Interval<Real> = Interval::from(IntervalLowerClosedUpperOpen::new(
                Real::try_new(0.0).unwrap(),
                Real::try_new(5.0).unwrap(),
            ));

            let result = IntervalLowerOpenUpperClosed::<Real>::try_from(interval);
            assert!(result.is_err());

            match result.unwrap_err() {
                ErrorsIntervalConversion::NotIntervalLowerOpenUpperClosed { .. } => {
                    // Expected error
                }
                _ => panic!("Expected NotIntervalLowerOpenUpperClosed error"),
            }
        }

        /// Test successful conversion from Interval to IntervalLowerClosedUpperOpen
        #[test]
        fn try_from_interval_to_lower_closed_upper_open() {
            let interval: Interval<Real> = Interval::from(IntervalLowerClosedUpperOpen::new(
                Real::try_new(1.0).unwrap(),
                Real::try_new(10.0).unwrap(),
            ));

            let result = IntervalLowerClosedUpperOpen::<Real>::try_from(interval);
            assert!(result.is_ok());

            let half_open = result.unwrap();
            assert!(half_open.is_lower_bound_closed());
            assert!(!half_open.is_upper_bound_closed());
        }

        /// Test failed conversion from Interval to IntervalLowerClosedUpperOpen
        #[test]
        fn try_from_interval_to_lower_closed_upper_open_fails() {
            let interval: Interval<Real> = Interval::from(IntervalLowerOpenUpperClosed::new(
                Real::try_new(1.0).unwrap(),
                Real::try_new(10.0).unwrap(),
            ));

            let result = IntervalLowerClosedUpperOpen::<Real>::try_from(interval);
            assert!(result.is_err());

            match result.unwrap_err() {
                ErrorsIntervalConversion::NotIntervalLowerClosedUpperOpen { .. } => {
                    // Expected error
                }
                _ => panic!("Expected NotIntervalLowerClosedUpperOpen error"),
            }
        }

        // Tests for IntervalFinitePositiveLength -> specific bounded types

        /// Test successful conversion from IntervalFinitePositiveLength to IntervalClosed
        #[test]
        fn try_from_finite_positive_to_closed() {
            let positive_length = IntervalFinitePositiveLength::Closed(IntervalClosed::new(
                Real::try_new(0.0).unwrap(),
                Real::try_new(5.0).unwrap(),
            ));

            let result = IntervalClosed::<Real>::try_from(positive_length);
            assert!(result.is_ok());
        }

        /// Test failed conversion from IntervalFinitePositiveLength to IntervalClosed
        #[test]
        fn try_from_finite_positive_to_closed_fails() {
            let positive_length = IntervalFinitePositiveLength::Open(IntervalOpen::new(
                Real::try_new(0.0).unwrap(),
                Real::try_new(5.0).unwrap(),
            ));

            let result = IntervalClosed::<Real>::try_from(positive_length);
            assert!(result.is_err());
        }

        /// Test successful conversion from IntervalFinitePositiveLength to IntervalOpen
        #[test]
        fn try_from_finite_positive_to_open() {
            let positive_length = IntervalFinitePositiveLength::Open(IntervalOpen::new(
                Real::try_new(1.0).unwrap(),
                Real::try_new(9.0).unwrap(),
            ));

            let result = IntervalOpen::<Real>::try_from(positive_length);
            assert!(result.is_ok());
        }

        /// Test successful conversion from IntervalFinitePositiveLength to IntervalLowerOpenUpperClosed
        #[test]
        fn try_from_finite_positive_to_lower_open_upper_closed() {
            let positive_length = IntervalFinitePositiveLength::LowerOpenUpperClosed(
                IntervalLowerOpenUpperClosed::new(
                    Real::try_new(2.0).unwrap(),
                    Real::try_new(7.0).unwrap(),
                ),
            );

            let result = IntervalLowerOpenUpperClosed::<Real>::try_from(positive_length);
            assert!(result.is_ok());
        }

        /// Test successful conversion from IntervalFinitePositiveLength to IntervalLowerClosedUpperOpen
        #[test]
        fn try_from_finite_positive_to_lower_closed_upper_open() {
            let positive_length = IntervalFinitePositiveLength::LowerClosedUpperOpen(
                IntervalLowerClosedUpperOpen::new(
                    Real::try_new(3.0).unwrap(),
                    Real::try_new(8.0).unwrap(),
                ),
            );

            let result = IntervalLowerClosedUpperOpen::<Real>::try_from(positive_length);
            assert!(result.is_ok());
        }

        // Tests for Interval -> IntervalSingleton

        /// Test successful conversion from Interval to IntervalSingleton
        #[test]
        fn try_from_interval_to_singleton() {
            let interval: Interval<Real> =
                Interval::from(IntervalSingleton::new(Real::try_new(7.0).unwrap()));

            let result = IntervalSingleton::<Real>::try_from(interval);
            assert!(result.is_ok());

            let singleton = result.unwrap();
            assert_eq!(singleton.value(), &Real::try_new(7.0).unwrap());
        }

        /// Test failed conversion from positive length Interval to IntervalSingleton
        #[test]
        fn try_from_interval_to_singleton_fails_for_positive() {
            let interval: Interval<Real> = Interval::from(IntervalClosed::new(
                Real::try_new(0.0).unwrap(),
                Real::try_new(5.0).unwrap(),
            ));

            let result = IntervalSingleton::<Real>::try_from(interval);
            assert!(result.is_err());

            match result.unwrap_err() {
                ErrorsIntervalConversion::NotIntervalSingleton { .. } => {
                    // Expected error
                }
                _ => panic!("Expected NotIntervalSingleton error"),
            }
        }

        /// Test failed conversion from infinite Interval to IntervalSingleton
        #[test]
        fn try_from_interval_to_singleton_fails_for_infinite() {
            let interval: Interval<Real> =
                Interval::InfiniteLength(IntervalInfiniteLength::LowerUnboundedUpperUnbounded(
                    IntervalLowerUnboundedUpperUnbounded::new(),
                ));

            let result = IntervalSingleton::<Real>::try_from(interval);
            assert!(result.is_err());
        }

        // Tests for Interval -> specific unbounded interval types

        /// Test successful conversion from Interval to IntervalLowerUnboundedUpperClosed
        #[test]
        fn try_from_interval_to_lower_unbounded_upper_closed() {
            let interval: Interval<Real> =
                Interval::InfiniteLength(IntervalInfiniteLength::LowerUnboundedUpperClosed(
                    IntervalLowerUnboundedUpperClosed::new(Real::try_new(10.0).unwrap()),
                ));

            let result = IntervalLowerUnboundedUpperClosed::<Real>::try_from(interval);
            assert!(result.is_ok());

            let unbounded = result.unwrap();
            assert_eq!(unbounded.upper_bound_value(), &Real::try_new(10.0).unwrap());
        }

        /// Test failed conversion from Interval to IntervalLowerUnboundedUpperClosed
        #[test]
        fn try_from_interval_to_lower_unbounded_upper_closed_fails() {
            let interval: Interval<Real> =
                Interval::InfiniteLength(IntervalInfiniteLength::LowerUnboundedUpperOpen(
                    IntervalLowerUnboundedUpperOpen::new(Real::try_new(10.0).unwrap()),
                ));

            let result = IntervalLowerUnboundedUpperClosed::<Real>::try_from(interval);
            assert!(result.is_err());

            match result.unwrap_err() {
                ErrorsIntervalConversion::NotIntervalLowerUnboundedUpperClosed { .. } => {
                    // Expected error
                }
                _ => panic!("Expected NotIntervalLowerUnboundedUpperClosed error"),
            }
        }

        /// Test successful conversion from Interval to IntervalLowerUnboundedUpperOpen
        #[test]
        fn try_from_interval_to_lower_unbounded_upper_open() {
            let interval: Interval<Real> =
                Interval::InfiniteLength(IntervalInfiniteLength::LowerUnboundedUpperOpen(
                    IntervalLowerUnboundedUpperOpen::new(Real::try_new(5.0).unwrap()),
                ));

            let result = IntervalLowerUnboundedUpperOpen::<Real>::try_from(interval);
            assert!(result.is_ok());

            let unbounded = result.unwrap();
            assert_eq!(unbounded.upper_bound_value(), &Real::try_new(5.0).unwrap());
        }

        /// Test failed conversion from Interval to IntervalLowerUnboundedUpperOpen
        #[test]
        fn try_from_interval_to_lower_unbounded_upper_open_fails() {
            let interval: Interval<Real> = Interval::from(IntervalClosed::new(
                Real::try_new(0.0).unwrap(),
                Real::try_new(5.0).unwrap(),
            ));

            let result = IntervalLowerUnboundedUpperOpen::<Real>::try_from(interval);
            assert!(result.is_err());
        }

        /// Test successful conversion from Interval to IntervalLowerClosedUpperUnbounded
        #[test]
        fn try_from_interval_to_lower_closed_upper_unbounded() {
            let interval: Interval<Real> =
                Interval::InfiniteLength(IntervalInfiniteLength::LowerClosedUpperUnbounded(
                    IntervalLowerClosedUpperUnbounded::new(Real::try_new(0.0).unwrap()),
                ));

            let result = IntervalLowerClosedUpperUnbounded::<Real>::try_from(interval);
            assert!(result.is_ok());

            let unbounded = result.unwrap();
            assert_eq!(unbounded.lower_bound_value(), &Real::try_new(0.0).unwrap());
        }

        /// Test failed conversion from Interval to IntervalLowerClosedUpperUnbounded
        #[test]
        fn try_from_interval_to_lower_closed_upper_unbounded_fails() {
            let interval: Interval<Real> =
                Interval::InfiniteLength(IntervalInfiniteLength::LowerOpenUpperUnbounded(
                    IntervalLowerOpenUpperUnbounded::new(Real::try_new(0.0).unwrap()),
                ));

            let result = IntervalLowerClosedUpperUnbounded::<Real>::try_from(interval);
            assert!(result.is_err());

            match result.unwrap_err() {
                ErrorsIntervalConversion::NotIntervalLowerClosedUpperUnbounded { .. } => {
                    // Expected error
                }
                _ => panic!("Expected NotIntervalLowerClosedUpperUnbounded error"),
            }
        }

        /// Test successful conversion from Interval to IntervalLowerOpenUpperUnbounded
        #[test]
        fn try_from_interval_to_lower_open_upper_unbounded() {
            let interval: Interval<Real> =
                Interval::InfiniteLength(IntervalInfiniteLength::LowerOpenUpperUnbounded(
                    IntervalLowerOpenUpperUnbounded::new(Real::try_new(3.0).unwrap()),
                ));

            let result = IntervalLowerOpenUpperUnbounded::<Real>::try_from(interval);
            assert!(result.is_ok());

            let unbounded = result.unwrap();
            assert_eq!(unbounded.lower_bound_value(), &Real::try_new(3.0).unwrap());
        }

        /// Test failed conversion from Interval to IntervalLowerOpenUpperUnbounded
        #[test]
        fn try_from_interval_to_lower_open_upper_unbounded_fails() {
            let interval: Interval<Real> = Interval::from(IntervalOpen::new(
                Real::try_new(3.0).unwrap(),
                Real::try_new(10.0).unwrap(),
            ));

            let result = IntervalLowerOpenUpperUnbounded::<Real>::try_from(interval);
            assert!(result.is_err());

            // First error will be NotIntervalInfiniteLength since IntervalOpen is finite
            match result.unwrap_err() {
                ErrorsIntervalConversion::NotIntervalInfiniteLength { .. } => {
                    // Expected error - finite interval cannot convert to unbounded
                }
                ErrorsIntervalConversion::NotIntervalLowerOpenUpperUnbounded { .. } => {
                    // Also acceptable - wrong unbounded type
                }
                _ => panic!(
                    "Expected NotIntervalInfiniteLength or NotIntervalLowerOpenUpperUnbounded error"
                ),
            }
        }

        // Comprehensive round-trip tests

        /// Test round-trip conversion: IntervalClosed -> Interval -> IntervalClosed
        #[test]
        fn round_trip_closed() {
            let original =
                IntervalClosed::new(Real::try_new(1.0).unwrap(), Real::try_new(5.0).unwrap());

            let interval: Interval<Real> = original.clone().into();
            let result = IntervalClosed::<Real>::try_from(interval).unwrap();

            assert_eq!(original, result);
        }

        /// Test round-trip conversion: IntervalOpen -> Interval -> IntervalOpen
        #[test]
        fn round_trip_open() {
            let original =
                IntervalOpen::new(Real::try_new(2.0).unwrap(), Real::try_new(8.0).unwrap());

            let interval: Interval<Real> = original.clone().into();
            let result = IntervalOpen::<Real>::try_from(interval).unwrap();

            assert_eq!(original, result);
        }

        /// Test round-trip conversion: IntervalSingleton -> Interval -> IntervalSingleton
        #[test]
        fn round_trip_singleton() {
            let original = IntervalSingleton::new(Real::try_new(4.0).unwrap());

            let interval: Interval<Real> = original.clone().into();
            let result = IntervalSingleton::<Real>::try_from(interval).unwrap();

            assert_eq!(original.value(), result.value());
        }

        /// Test round-trip conversion: unbounded interval
        #[test]
        fn round_trip_lower_unbounded_upper_closed() {
            let original = IntervalLowerUnboundedUpperClosed::new(Real::try_new(10.0).unwrap());

            let interval: Interval<Real> = Interval::from(original.clone());
            let result = IntervalLowerUnboundedUpperClosed::<Real>::try_from(interval).unwrap();

            assert_eq!(original.upper_bound_value(), result.upper_bound_value());
        }

        // Error message validation tests

        /// Test that error contains original interval information
        #[test]
        fn error_contains_interval_info() {
            let interval: Interval<Real> = Interval::from(IntervalOpen::new(
                Real::try_new(0.0).unwrap(),
                Real::try_new(5.0).unwrap(),
            ));

            let result = IntervalClosed::<Real>::try_from(interval.clone());
            assert!(result.is_err());

            let error = result.unwrap_err();
            let error_string = format!("{:?}", error);
            assert!(error_string.contains("NotIntervalClosed"));
        }

        /// Test chained conversions
        #[test]
        fn chained_conversion_interval_to_finite_to_closed() {
            let interval: Interval<Real> = Interval::from(IntervalClosed::new(
                Real::try_new(1.0).unwrap(),
                Real::try_new(10.0).unwrap(),
            ));

            // First convert to finite length
            let finite = IntervalFiniteLength::try_from(interval).unwrap();

            // Then extract as Interval again
            let interval2: Interval<Real> = finite.into();

            // Finally convert to closed
            let closed = IntervalClosed::<Real>::try_from(interval2).unwrap();
            assert_eq!(closed.lower_bound_value(), &Real::try_new(1.0).unwrap());
            assert_eq!(closed.upper_bound_value(), &Real::try_new(10.0).unwrap());
        }
    }

    /// Tests for new helper functions and factory methods added during refactoring
    mod new_functions {
        use super::*;

        /// Test IntervalInfiniteLength::new_lower_unbounded with closed upper bound
        #[test]
        fn test_new_lower_unbounded_closed() {
            use crate::bounds::{UpperBoundClosed, UpperBoundRuntime};

            let upper = UpperBoundClosed::new(10.0);
            let upper_runtime: UpperBoundRuntime<f64> = upper.into();

            let interval = IntervalInfiniteLength::new_lower_unbounded(upper_runtime);

            match interval {
                IntervalInfiniteLength::LowerUnboundedUpperClosed(result) => {
                    assert_eq!(result.upper_bound_value(), &10.0);
                }
                _ => panic!("Expected LowerUnboundedUpperClosed variant"),
            }
        }

        /// Test IntervalInfiniteLength::new_lower_unbounded with open upper bound
        #[test]
        fn test_new_lower_unbounded_open() {
            use crate::bounds::{UpperBoundOpen, UpperBoundRuntime};

            let upper = UpperBoundOpen::new(10.0);
            let upper_runtime: UpperBoundRuntime<f64> = upper.into();

            let interval = IntervalInfiniteLength::new_lower_unbounded(upper_runtime);

            match interval {
                IntervalInfiniteLength::LowerUnboundedUpperOpen(result) => {
                    assert_eq!(result.upper_bound_value(), &10.0);
                }
                _ => panic!("Expected LowerUnboundedUpperOpen variant"),
            }
        }

        /// Test IntervalInfiniteLength::new_upper_unbounded with closed lower bound
        #[test]
        fn test_new_upper_unbounded_closed() {
            use crate::bounds::{LowerBoundClosed, LowerBoundRuntime};

            let lower = LowerBoundClosed::new(0.0);
            let lower_runtime: LowerBoundRuntime<f64> = lower.into();

            let interval = IntervalInfiniteLength::new_upper_unbounded(lower_runtime);

            match interval {
                IntervalInfiniteLength::LowerClosedUpperUnbounded(result) => {
                    assert_eq!(result.lower_bound_value(), &0.0);
                }
                _ => panic!("Expected LowerClosedUpperUnbounded variant"),
            }
        }

        /// Test IntervalInfiniteLength::new_upper_unbounded with open lower bound
        #[test]
        fn test_new_upper_unbounded_open() {
            use crate::bounds::{LowerBoundOpen, LowerBoundRuntime};

            let lower = LowerBoundOpen::new(0.0);
            let lower_runtime: LowerBoundRuntime<f64> = lower.into();

            let interval = IntervalInfiniteLength::new_upper_unbounded(lower_runtime);

            match interval {
                IntervalInfiniteLength::LowerOpenUpperUnbounded(result) => {
                    assert_eq!(result.lower_bound_value(), &0.0);
                }
                _ => panic!("Expected LowerOpenUpperUnbounded variant"),
            }
        }

        /// Test Interval::try_new with both bounds None (fully unbounded)
        #[test]
        fn test_interval_try_new_fully_unbounded() {
            let interval = Interval::<f64>::try_new(None, None).unwrap();

            match interval {
                Interval::InfiniteLength(IntervalInfiniteLength::LowerUnboundedUpperUnbounded(
                    _,
                )) => {
                    // Success - correct variant
                }
                _ => panic!("Expected fully unbounded interval"),
            }
        }

        /// Test Interval::try_new with lower bound only (closed)
        #[test]
        fn test_interval_try_new_lower_bounded_closed() {
            use crate::bounds::{LowerBoundClosed, LowerBoundRuntime};

            let lower = LowerBoundClosed::new(0.0);
            let lower_runtime: LowerBoundRuntime<f64> = lower.into();

            let interval = Interval::try_new(Some(lower_runtime), None).unwrap();

            match interval {
                Interval::InfiniteLength(IntervalInfiniteLength::LowerClosedUpperUnbounded(
                    result,
                )) => {
                    assert_eq!(result.lower_bound_value(), &0.0);
                }
                _ => panic!("Expected LowerClosedUpperUnbounded interval"),
            }
        }

        /// Test Interval::try_new with lower bound only (open)
        #[test]
        fn test_interval_try_new_lower_bounded_open() {
            use crate::bounds::{LowerBoundOpen, LowerBoundRuntime};

            let lower = LowerBoundOpen::new(0.0);
            let lower_runtime: LowerBoundRuntime<f64> = lower.into();

            let interval = Interval::try_new(Some(lower_runtime), None).unwrap();

            match interval {
                Interval::InfiniteLength(IntervalInfiniteLength::LowerOpenUpperUnbounded(
                    result,
                )) => {
                    assert_eq!(result.lower_bound_value(), &0.0);
                }
                _ => panic!("Expected LowerOpenUpperUnbounded interval"),
            }
        }

        /// Test Interval::try_new with upper bound only (closed)
        #[test]
        fn test_interval_try_new_upper_bounded_closed() {
            use crate::bounds::{UpperBoundClosed, UpperBoundRuntime};

            let upper = UpperBoundClosed::new(10.0);
            let upper_runtime: UpperBoundRuntime<f64> = upper.into();

            let interval = Interval::try_new(None, Some(upper_runtime)).unwrap();

            match interval {
                Interval::InfiniteLength(IntervalInfiniteLength::LowerUnboundedUpperClosed(
                    result,
                )) => {
                    assert_eq!(result.upper_bound_value(), &10.0);
                }
                _ => panic!("Expected LowerUnboundedUpperClosed interval"),
            }
        }

        /// Test Interval::try_new with upper bound only (open)
        #[test]
        fn test_interval_try_new_upper_bounded_open() {
            use crate::bounds::{UpperBoundOpen, UpperBoundRuntime};

            let upper = UpperBoundOpen::new(10.0);
            let upper_runtime: UpperBoundRuntime<f64> = upper.into();

            let interval = Interval::try_new(None, Some(upper_runtime)).unwrap();

            match interval {
                Interval::InfiniteLength(IntervalInfiniteLength::LowerUnboundedUpperOpen(
                    result,
                )) => {
                    assert_eq!(result.upper_bound_value(), &10.0);
                }
                _ => panic!("Expected LowerUnboundedUpperOpen interval"),
            }
        }

        /// Test Interval::try_new with both bounds (closed-closed, creates singleton)
        #[test]
        fn test_interval_try_new_singleton() {
            use crate::bounds::{
                LowerBoundClosed, LowerBoundRuntime, UpperBoundClosed, UpperBoundRuntime,
            };

            let value = 5.0;
            let lower = LowerBoundClosed::new(value);
            let upper = UpperBoundClosed::new(value);
            let lower_runtime: LowerBoundRuntime<f64> = lower.into();
            let upper_runtime: UpperBoundRuntime<f64> = upper.into();

            let interval = Interval::try_new(Some(lower_runtime), Some(upper_runtime)).unwrap();

            match interval {
                Interval::FiniteLength(IntervalFiniteLength::ZeroLength(result)) => {
                    assert_eq!(result.value(), &value);
                }
                _ => panic!("Expected singleton interval"),
            }
        }

        /// Test Interval::try_new with both bounds (closed-closed, different values)
        #[test]
        fn test_interval_try_new_closed_interval() {
            use crate::bounds::{
                LowerBoundClosed, LowerBoundRuntime, UpperBoundClosed, UpperBoundRuntime,
            };

            let lower = LowerBoundClosed::new(0.0);
            let upper = UpperBoundClosed::new(10.0);
            let lower_runtime: LowerBoundRuntime<f64> = lower.into();
            let upper_runtime: UpperBoundRuntime<f64> = upper.into();

            let interval = Interval::try_new(Some(lower_runtime), Some(upper_runtime)).unwrap();

            match interval {
                Interval::FiniteLength(IntervalFiniteLength::PositiveLength(
                    IntervalFinitePositiveLength::Closed(result),
                )) => {
                    assert_eq!(result.lower_bound_value(), &0.0);
                    assert_eq!(result.upper_bound_value(), &10.0);
                }
                _ => panic!("Expected closed interval"),
            }
        }

        /// Test Interval::try_new with both bounds (closed-open)
        #[test]
        fn test_interval_try_new_half_open() {
            use crate::bounds::{
                LowerBoundClosed, LowerBoundRuntime, UpperBoundOpen, UpperBoundRuntime,
            };

            let lower = LowerBoundClosed::new(0.0);
            let upper = UpperBoundOpen::new(10.0);
            let lower_runtime: LowerBoundRuntime<f64> = lower.into();
            let upper_runtime: UpperBoundRuntime<f64> = upper.into();

            let interval = Interval::try_new(Some(lower_runtime), Some(upper_runtime)).unwrap();

            match interval {
                Interval::FiniteLength(IntervalFiniteLength::PositiveLength(
                    IntervalFinitePositiveLength::LowerClosedUpperOpen(result),
                )) => {
                    assert_eq!(result.lower_bound_value(), &0.0);
                    assert_eq!(result.upper_bound_value(), &10.0);
                }
                _ => panic!("Expected half-open interval [a, b)"),
            }
        }

        /// Test Interval::try_new with invalid bounds (lower > upper)
        #[test]
        fn test_interval_try_new_invalid_bounds() {
            use crate::bounds::{
                LowerBoundClosed, LowerBoundRuntime, UpperBoundClosed, UpperBoundRuntime,
            };

            let lower = LowerBoundClosed::new(10.0);
            let upper = UpperBoundClosed::new(0.0);
            let lower_runtime: LowerBoundRuntime<f64> = lower.into();
            let upper_runtime: UpperBoundRuntime<f64> = upper.into();

            let result = Interval::try_new(Some(lower_runtime), Some(upper_runtime));

            assert!(result.is_err(), "Should fail when lower > upper");
        }

        /// Test IntervalFiniteLength::try_new with all four bound combinations
        #[test]
        fn test_interval_finite_length_try_new_all_combinations() {
            use crate::bounds::{
                LowerBoundClosed, LowerBoundOpen, UpperBoundClosed, UpperBoundOpen,
            };

            // Closed-Closed
            let lower = LowerBoundClosed::new(0.0);
            let upper = UpperBoundClosed::new(10.0);
            let interval = IntervalFiniteLength::try_new(lower.into(), upper.into()).unwrap();
            match interval {
                IntervalFiniteLength::PositiveLength(IntervalFinitePositiveLength::Closed(_)) => {}
                _ => panic!("Expected closed interval"),
            }

            // Closed-Open
            let lower = LowerBoundClosed::new(0.0);
            let upper = UpperBoundOpen::new(10.0);
            let interval = IntervalFiniteLength::try_new(lower.into(), upper.into()).unwrap();
            match interval {
                IntervalFiniteLength::PositiveLength(
                    IntervalFinitePositiveLength::LowerClosedUpperOpen(_),
                ) => {}
                _ => panic!("Expected [a, b) interval"),
            }

            // Open-Closed
            let lower = LowerBoundOpen::new(0.0);
            let upper = UpperBoundClosed::new(10.0);
            let interval = IntervalFiniteLength::try_new(lower.into(), upper.into()).unwrap();
            match interval {
                IntervalFiniteLength::PositiveLength(
                    IntervalFinitePositiveLength::LowerOpenUpperClosed(_),
                ) => {}
                _ => panic!("Expected (a, b] interval"),
            }

            // Open-Open
            let lower = LowerBoundOpen::new(0.0);
            let upper = UpperBoundOpen::new(10.0);
            let interval = IntervalFiniteLength::try_new(lower.into(), upper.into()).unwrap();
            match interval {
                IntervalFiniteLength::PositiveLength(IntervalFinitePositiveLength::Open(_)) => {}
                _ => panic!("Expected open interval"),
            }
        }

        /// Test IntervalFiniteLength::try_new creates singleton for equal closed bounds
        #[test]
        fn test_interval_finite_length_singleton_creation() {
            use crate::bounds::{LowerBoundClosed, UpperBoundClosed};

            let value = 5.0;
            let lower = LowerBoundClosed::new(value);
            let upper = UpperBoundClosed::new(value);

            let interval = IntervalFiniteLength::try_new(lower.into(), upper.into()).unwrap();

            match interval {
                IntervalFiniteLength::ZeroLength(result) => {
                    assert_eq!(result.value(), &value);
                }
                _ => panic!("Expected singleton interval"),
            }
        }

        /// Test IntervalFinitePositiveLength::try_new with all four bound combinations
        #[test]
        fn test_interval_finite_positive_length_try_new() {
            use crate::bounds::{
                LowerBoundClosed, LowerBoundOpen, UpperBoundClosed, UpperBoundOpen,
            };

            // Closed-Closed (does NOT check for singleton)
            let lower = LowerBoundClosed::new(0.0);
            let upper = UpperBoundClosed::new(10.0);
            let interval =
                IntervalFinitePositiveLength::try_new(lower.into(), upper.into()).unwrap();
            match interval {
                IntervalFinitePositiveLength::Closed(result) => {
                    assert_eq!(result.lower_bound_value(), &0.0);
                    assert_eq!(result.upper_bound_value(), &10.0);
                }
                _ => panic!("Expected closed interval"),
            }

            // Closed-Open
            let lower = LowerBoundClosed::new(0.0);
            let upper = UpperBoundOpen::new(10.0);
            let interval =
                IntervalFinitePositiveLength::try_new(lower.into(), upper.into()).unwrap();
            match interval {
                IntervalFinitePositiveLength::LowerClosedUpperOpen(_) => {}
                _ => panic!("Expected [a, b) interval"),
            }

            // Open-Closed
            let lower = LowerBoundOpen::new(0.0);
            let upper = UpperBoundClosed::new(10.0);
            let interval =
                IntervalFinitePositiveLength::try_new(lower.into(), upper.into()).unwrap();
            match interval {
                IntervalFinitePositiveLength::LowerOpenUpperClosed(_) => {}
                _ => panic!("Expected (a, b] interval"),
            }

            // Open-Open
            let lower = LowerBoundOpen::new(0.0);
            let upper = UpperBoundOpen::new(10.0);
            let interval =
                IntervalFinitePositiveLength::try_new(lower.into(), upper.into()).unwrap();
            match interval {
                IntervalFinitePositiveLength::Open(_) => {}
                _ => panic!("Expected open interval"),
            }
        }

        /// Test that IntervalFinitePositiveLength::try_new fails with invalid bounds
        #[test]
        fn test_interval_finite_positive_length_invalid() {
            use crate::bounds::{LowerBoundClosed, UpperBoundClosed};

            let lower = LowerBoundClosed::new(10.0);
            let upper = UpperBoundClosed::new(5.0);

            let result = IntervalFinitePositiveLength::try_new(lower.into(), upper.into());

            assert!(result.is_err(), "Should fail when lower > upper");
        }
    }

    mod interval_finite_positive_length_bounds {
        use super::*;

        // Tests for is_upper_bound_closed() on IntervalFinitePositiveLength

        /// Test is_upper_bound_closed() for IntervalFinitePositiveLength::Closed variant
        #[test]
        fn is_upper_bound_closed_variant_closed() {
            let interval = IntervalFinitePositiveLength::Closed(IntervalClosed::new(0.0, 10.0));
            assert!(
                interval.is_upper_bound_closed(),
                "Closed variant should have closed upper bound"
            );
            assert!(!interval.is_upper_bound_open());
        }

        /// Test is_upper_bound_closed() for IntervalFinitePositiveLength::Open variant
        #[test]
        fn is_upper_bound_closed_variant_open() {
            let interval = IntervalFinitePositiveLength::Open(IntervalOpen::new(0.0, 10.0));
            assert!(
                !interval.is_upper_bound_closed(),
                "Open variant should have open upper bound"
            );
            assert!(interval.is_upper_bound_open());
        }

        /// Test is_upper_bound_closed() for IntervalFinitePositiveLength::LowerClosedUpperOpen variant
        #[test]
        fn is_upper_bound_closed_variant_lower_closed_upper_open() {
            let interval = IntervalFinitePositiveLength::LowerClosedUpperOpen(
                IntervalLowerClosedUpperOpen::new(0.0, 10.0),
            );
            assert!(
                !interval.is_upper_bound_closed(),
                "LowerClosedUpperOpen [a, b) should have open upper bound"
            );
            assert!(interval.is_upper_bound_open());
        }

        /// Test is_upper_bound_closed() for IntervalFinitePositiveLength::LowerOpenUpperClosed variant
        #[test]
        fn is_upper_bound_closed_variant_lower_open_upper_closed() {
            let interval = IntervalFinitePositiveLength::LowerOpenUpperClosed(
                IntervalLowerOpenUpperClosed::new(0.0, 10.0),
            );
            assert!(
                interval.is_upper_bound_closed(),
                "LowerOpenUpperClosed (a, b] should have closed upper bound"
            );
            assert!(!interval.is_upper_bound_open());
        }

        /// Test is_upper_bound_open() complement relationship for all variants
        #[test]
        fn is_upper_bound_open_complement_all_variants() {
            let closed = IntervalFinitePositiveLength::Closed(IntervalClosed::new(0.0, 10.0));
            assert_eq!(
                closed.is_upper_bound_open(),
                !closed.is_upper_bound_closed(),
                "Complement relationship must hold for Closed"
            );

            let open = IntervalFinitePositiveLength::Open(IntervalOpen::new(0.0, 10.0));
            assert_eq!(
                open.is_upper_bound_open(),
                !open.is_upper_bound_closed(),
                "Complement relationship must hold for Open"
            );

            let half_open1 = IntervalFinitePositiveLength::LowerClosedUpperOpen(
                IntervalLowerClosedUpperOpen::new(0.0, 10.0),
            );
            assert_eq!(
                half_open1.is_upper_bound_open(),
                !half_open1.is_upper_bound_closed(),
                "Complement relationship must hold for LowerClosedUpperOpen"
            );

            let half_open2 = IntervalFinitePositiveLength::LowerOpenUpperClosed(
                IntervalLowerOpenUpperClosed::new(0.0, 10.0),
            );
            assert_eq!(
                half_open2.is_upper_bound_open(),
                !half_open2.is_upper_bound_closed(),
                "Complement relationship must hold for LowerOpenUpperClosed"
            );
        }

        /// Test is_upper_bound_closed() with negative bounds
        #[test]
        fn is_upper_bound_closed_negative_values() {
            let closed = IntervalFinitePositiveLength::Closed(IntervalClosed::new(-10.0, -5.0));
            assert!(
                closed.is_upper_bound_closed(),
                "Closed interval with negative bounds should have closed upper bound"
            );

            let open = IntervalFinitePositiveLength::Open(IntervalOpen::new(-10.0, -5.0));
            assert!(
                !open.is_upper_bound_closed(),
                "Open interval with negative bounds should have open upper bound"
            );
        }

        /// Test is_upper_bound_closed() with very small intervals
        #[test]
        fn is_upper_bound_closed_small_intervals() {
            let epsilon = 1e-10;
            let closed = IntervalFinitePositiveLength::Closed(IntervalClosed::new(0.0, epsilon));
            assert!(
                closed.is_upper_bound_closed(),
                "Small closed interval should have closed upper bound"
            );

            let half_open = IntervalFinitePositiveLength::LowerClosedUpperOpen(
                IntervalLowerClosedUpperOpen::new(0.0, epsilon),
            );
            assert!(
                !half_open.is_upper_bound_closed(),
                "Small half-open interval should have open upper bound"
            );
        }

        /// Test consistency between is_upper_bound_closed() and upper_bound_value()
        #[test]
        fn is_upper_bound_closed_consistency_with_value() {
            let closed = IntervalFinitePositiveLength::Closed(IntervalClosed::new(0.0, 10.0));
            assert_eq!(closed.upper_bound_value(), &10.0);
            assert!(closed.is_upper_bound_closed());

            let open = IntervalFinitePositiveLength::Open(IntervalOpen::new(0.0, 10.0));
            assert_eq!(open.upper_bound_value(), &10.0);
            assert!(!open.is_upper_bound_closed());
        }

        // Tests for is_lower_bound_closed() on IntervalFinitePositiveLength

        /// Test is_lower_bound_closed() for IntervalFinitePositiveLength::Closed variant
        #[test]
        fn is_lower_bound_closed_variant_closed() {
            let interval = IntervalFinitePositiveLength::Closed(IntervalClosed::new(0.0, 10.0));
            assert!(
                interval.is_lower_bound_closed(),
                "Closed variant should have closed lower bound"
            );
            assert!(!interval.is_lower_bound_open());
        }

        /// Test is_lower_bound_closed() for IntervalFinitePositiveLength::Open variant
        #[test]
        fn is_lower_bound_closed_variant_open() {
            let interval = IntervalFinitePositiveLength::Open(IntervalOpen::new(0.0, 10.0));
            assert!(
                !interval.is_lower_bound_closed(),
                "Open variant should have open lower bound"
            );
            assert!(interval.is_lower_bound_open());
        }

        /// Test is_lower_bound_closed() for IntervalFinitePositiveLength::LowerClosedUpperOpen variant
        #[test]
        fn is_lower_bound_closed_variant_lower_closed_upper_open() {
            let interval = IntervalFinitePositiveLength::LowerClosedUpperOpen(
                IntervalLowerClosedUpperOpen::new(0.0, 10.0),
            );
            assert!(
                interval.is_lower_bound_closed(),
                "LowerClosedUpperOpen [a, b) should have closed lower bound"
            );
            assert!(!interval.is_lower_bound_open());
        }

        /// Test is_lower_bound_closed() for IntervalFinitePositiveLength::LowerOpenUpperClosed variant
        #[test]
        fn is_lower_bound_closed_variant_lower_open_upper_closed() {
            let interval = IntervalFinitePositiveLength::LowerOpenUpperClosed(
                IntervalLowerOpenUpperClosed::new(0.0, 10.0),
            );
            assert!(
                !interval.is_lower_bound_closed(),
                "LowerOpenUpperClosed (a, b] should have open lower bound"
            );
            assert!(interval.is_lower_bound_open());
        }

        /// Test both bounds together for all variants
        #[test]
        fn both_bounds_all_variants() {
            // Closed: [a, b]
            let closed = IntervalFinitePositiveLength::Closed(IntervalClosed::new(0.0, 10.0));
            assert!(closed.is_lower_bound_closed() && closed.is_upper_bound_closed());
            assert!(!closed.is_lower_bound_open() && !closed.is_upper_bound_open());

            // Open: (a, b)
            let open = IntervalFinitePositiveLength::Open(IntervalOpen::new(0.0, 10.0));
            assert!(!open.is_lower_bound_closed() && !open.is_upper_bound_closed());
            assert!(open.is_lower_bound_open() && open.is_upper_bound_open());

            // LowerClosedUpperOpen: [a, b)
            let lc_uo = IntervalFinitePositiveLength::LowerClosedUpperOpen(
                IntervalLowerClosedUpperOpen::new(0.0, 10.0),
            );
            assert!(lc_uo.is_lower_bound_closed() && !lc_uo.is_upper_bound_closed());
            assert!(!lc_uo.is_lower_bound_open() && lc_uo.is_upper_bound_open());

            // LowerOpenUpperClosed: (a, b]
            let lo_uc = IntervalFinitePositiveLength::LowerOpenUpperClosed(
                IntervalLowerOpenUpperClosed::new(0.0, 10.0),
            );
            assert!(!lo_uc.is_lower_bound_closed() && lo_uc.is_upper_bound_closed());
            assert!(lo_uc.is_lower_bound_open() && !lo_uc.is_upper_bound_open());
        }

        /// Test bound predicates with validated scalar types
        #[test]
        fn bound_predicates_with_validated_types() {
            use num_valid::RealNative64StrictFiniteInDebug;
            use try_create::TryNew;

            type Real = RealNative64StrictFiniteInDebug;

            let closed = IntervalFinitePositiveLength::Closed(IntervalClosed::new(
                Real::try_new(0.0).unwrap(),
                Real::try_new(10.0).unwrap(),
            ));
            assert!(closed.is_upper_bound_closed());
            assert!(closed.is_lower_bound_closed());

            let open = IntervalFinitePositiveLength::Open(IntervalOpen::new(
                Real::try_new(0.0).unwrap(),
                Real::try_new(10.0).unwrap(),
            ));
            assert!(open.is_upper_bound_open());
            assert!(open.is_lower_bound_open());
        }

        // Tests for clamp() on IntervalFinitePositiveLength

        /// Test clamp() with value below the interval
        #[test]
        fn clamp_value_below_interval() {
            let interval = IntervalFinitePositiveLength::Closed(IntervalClosed::new(0.0, 10.0));

            // Values below lower bound should clamp to lower bound
            assert_eq!(interval.clamp(-5.0), 0.0);
            assert_eq!(interval.clamp(-1.0), 0.0);
            assert_eq!(interval.clamp(-0.1), 0.0);
        }

        /// Test clamp() with value above the interval
        #[test]
        fn clamp_value_above_interval() {
            let interval = IntervalFinitePositiveLength::Closed(IntervalClosed::new(0.0, 10.0));

            // Values above upper bound should clamp to upper bound
            assert_eq!(interval.clamp(15.0), 10.0);
            assert_eq!(interval.clamp(11.0), 10.0);
            assert_eq!(interval.clamp(10.1), 10.0);
        }

        /// Test clamp() with value inside the interval
        #[test]
        fn clamp_value_inside_interval() {
            let interval = IntervalFinitePositiveLength::Closed(IntervalClosed::new(0.0, 10.0));

            // Values inside interval should remain unchanged
            assert_eq!(interval.clamp(5.0), 5.0);
            assert_eq!(interval.clamp(2.5), 2.5);
            assert_eq!(interval.clamp(7.5), 7.5);
            assert_eq!(interval.clamp(0.1), 0.1);
            assert_eq!(interval.clamp(9.9), 9.9);
        }

        /// Test clamp() at boundary values
        #[test]
        fn clamp_at_boundaries() {
            let interval = IntervalFinitePositiveLength::Closed(IntervalClosed::new(0.0, 10.0));

            // Values exactly at boundaries
            assert_eq!(interval.clamp(0.0), 0.0); // Lower boundary
            assert_eq!(interval.clamp(10.0), 10.0); // Upper boundary
        }

        /// Test clamp() with all variants of IntervalFinitePositiveLength
        #[test]
        fn clamp_all_variants() {
            // Closed variant
            let closed = IntervalFinitePositiveLength::Closed(IntervalClosed::new(5.0, 15.0));
            assert_eq!(closed.clamp(3.0), 5.0); // Below
            assert_eq!(closed.clamp(10.0), 10.0); // Inside
            assert_eq!(closed.clamp(20.0), 15.0); // Above

            // Open variant
            let open = IntervalFinitePositiveLength::Open(IntervalOpen::new(5.0, 15.0));
            assert_eq!(open.clamp(3.0), 5.0); // Below
            assert_eq!(open.clamp(10.0), 10.0); // Inside
            assert_eq!(open.clamp(20.0), 15.0); // Above

            // LowerClosedUpperOpen variant
            let lc_uo = IntervalFinitePositiveLength::LowerClosedUpperOpen(
                IntervalLowerClosedUpperOpen::new(5.0, 15.0),
            );
            assert_eq!(lc_uo.clamp(3.0), 5.0); // Below
            assert_eq!(lc_uo.clamp(10.0), 10.0); // Inside
            assert_eq!(lc_uo.clamp(20.0), 15.0); // Above

            // LowerOpenUpperClosed variant
            let lo_uc = IntervalFinitePositiveLength::LowerOpenUpperClosed(
                IntervalLowerOpenUpperClosed::new(5.0, 15.0),
            );
            assert_eq!(lo_uc.clamp(3.0), 5.0); // Below
            assert_eq!(lo_uc.clamp(10.0), 10.0); // Inside
            assert_eq!(lo_uc.clamp(20.0), 15.0); // Above
        }

        /// Test clamp() with negative bounds
        #[test]
        fn clamp_negative_bounds() {
            let interval = IntervalFinitePositiveLength::Closed(IntervalClosed::new(-10.0, -5.0));

            assert_eq!(interval.clamp(-15.0), -10.0); // Below lower
            assert_eq!(interval.clamp(-7.5), -7.5); // Inside
            assert_eq!(interval.clamp(-3.0), -5.0); // Above upper
            assert_eq!(interval.clamp(-10.0), -10.0); // At lower bound
            assert_eq!(interval.clamp(-5.0), -5.0); // At upper bound
        }

        /// Test clamp() with interval crossing zero
        #[test]
        fn clamp_interval_crossing_zero() {
            let interval = IntervalFinitePositiveLength::Closed(IntervalClosed::new(-5.0, 5.0));

            assert_eq!(interval.clamp(-10.0), -5.0); // Below
            assert_eq!(interval.clamp(-2.0), -2.0); // Inside negative
            assert_eq!(interval.clamp(0.0), 0.0); // At zero
            assert_eq!(interval.clamp(2.0), 2.0); // Inside positive
            assert_eq!(interval.clamp(10.0), 5.0); // Above
        }

        /// Test clamp() with very small intervals
        #[test]
        fn clamp_small_interval() {
            let epsilon = 1e-10;
            let interval = IntervalFinitePositiveLength::Closed(IntervalClosed::new(0.0, epsilon));

            assert_eq!(interval.clamp(-1.0), 0.0); // Below
            assert_eq!(interval.clamp(epsilon / 2.0), epsilon / 2.0); // Inside
            assert_eq!(interval.clamp(1.0), epsilon); // Above
        }

        /// Test clamp() with very large values
        #[test]
        fn clamp_large_values() {
            let interval = IntervalFinitePositiveLength::Closed(IntervalClosed::new(0.0, 1000.0));

            assert_eq!(interval.clamp(-1e6), 0.0); // Very far below
            assert_eq!(interval.clamp(1e6), 1000.0); // Very far above
            assert_eq!(interval.clamp(500.0), 500.0); // Inside
        }

        /// Test clamp() idempotency - clamping twice should give same result
        #[test]
        fn clamp_idempotency() {
            let interval = IntervalFinitePositiveLength::Closed(IntervalClosed::new(0.0, 10.0));

            // Clamping a clamped value should give the same result
            let value = 15.0;
            let clamped_once = interval.clamp(value);
            let clamped_twice = interval.clamp(clamped_once);
            assert_eq!(clamped_once, clamped_twice);

            // This should hold for any value
            for test_value in [-5.0, 0.0, 5.0, 10.0, 15.0] {
                let once = interval.clamp(test_value);
                let twice = interval.clamp(once);
                assert_eq!(once, twice, "Idempotency failed for value {}", test_value);
            }
        }

        /// Test clamp() ensures result is always in valid range
        #[test]
        fn clamp_result_in_range() {
            let interval = IntervalFinitePositiveLength::Closed(IntervalClosed::new(0.0, 10.0));

            // For any input, output should be in [lower_bound, upper_bound]
            for test_value in [-100.0, -10.0, -1.0, 0.0, 5.0, 10.0, 15.0, 100.0] {
                let clamped = interval.clamp(test_value);
                assert!(
                    clamped >= *interval.lower_bound_value(),
                    "Clamped value {} is below lower bound {}",
                    clamped,
                    interval.lower_bound_value()
                );
                assert!(
                    clamped <= *interval.upper_bound_value(),
                    "Clamped value {} is above upper bound {}",
                    clamped,
                    interval.upper_bound_value()
                );
            }
        }

        /// Test clamp() with validated scalar types
        #[test]
        fn clamp_with_validated_types() {
            use num_valid::RealNative64StrictFiniteInDebug;
            use try_create::TryNew;

            type Real = RealNative64StrictFiniteInDebug;

            let interval = IntervalFinitePositiveLength::Closed(IntervalClosed::new(
                Real::try_new(0.0).unwrap(),
                Real::try_new(10.0).unwrap(),
            ));

            // Below range
            let below = Real::try_new(-5.0).unwrap();
            assert_eq!(interval.clamp(below), Real::try_new(0.0).unwrap());

            // Inside range
            let inside = Real::try_new(5.0).unwrap();
            assert_eq!(interval.clamp(inside), Real::try_new(5.0).unwrap());

            // Above range
            let above = Real::try_new(15.0).unwrap();
            assert_eq!(interval.clamp(above), Real::try_new(10.0).unwrap());
        }

        /// Test clamp() preserves the original interval bounds
        #[test]
        fn clamp_preserves_interval() {
            let interval = IntervalFinitePositiveLength::Closed(IntervalClosed::new(0.0, 10.0));

            // Store original bounds
            let original_lower = *interval.lower_bound_value();
            let original_upper = *interval.upper_bound_value();

            // Perform several clamp operations
            let _ = interval.clamp(-5.0);
            let _ = interval.clamp(5.0);
            let _ = interval.clamp(15.0);

            // Bounds should remain unchanged
            assert_eq!(*interval.lower_bound_value(), original_lower);
            assert_eq!(*interval.upper_bound_value(), original_upper);
        }

        /// Test clamp() with floating point edge cases
        #[test]
        fn clamp_floating_point_edge_cases() {
            let interval = IntervalFinitePositiveLength::Closed(IntervalClosed::new(0.0, 10.0));

            // Test with very small positive and negative values
            assert_eq!(interval.clamp(f64::EPSILON), f64::EPSILON);
            assert_eq!(interval.clamp(-f64::EPSILON), 0.0);

            // Test with values very close to boundaries
            let just_below_upper = 10.0 - f64::EPSILON;
            assert_eq!(interval.clamp(just_below_upper), just_below_upper);

            let just_above_lower = 0.0 + f64::EPSILON;
            assert_eq!(interval.clamp(just_above_lower), just_above_lower);
        }

        /// Test clamp() on very small interval
        #[test]
        fn clamp_on_very_small_interval() {
            // Create a very small but positive length interval
            // Use a difference large enough to be representable in f64
            let interval = IntervalFinitePositiveLength::Closed(IntervalClosed::new(5.0, 5.001));

            assert_eq!(interval.clamp(0.0), 5.0); // Below
            assert_eq!(interval.clamp(10.0), 5.001); // Above

            // Middle value should be clamped to itself (it's inside)
            let mid = 5.0005;
            let clamped = interval.clamp(mid);
            assert_eq!(clamped, mid);
        }
    }
}
//------------------------------------------------------------------------------------------------
