#![deny(rustdoc::broken_intra_doc_links)]

//! Mathematical bounds for 1D grid construction and interval arithmetic.
//!
//! This module provides strongly-typed boundary representations that encode inclusion/exclusion
//! semantics at the type level. These bounds are fundamental building blocks for constructing
//! mathematically correct intervals and grids where boundary behavior matters.
//!
//! ## Core Design Philosophy
//!
//! ### Type-Safe Boundary Semantics
//!
//! Rather than using raw scalar values with separate boolean flags, this module encodes
//! boundary behavior directly in the type system:
//!
//! ```rust
//! use grid1d::bounds::*;
//! use try_create::New;
//!
//! // These are different types that cannot be confused:
//! let inclusive_lower = LowerBoundClosed::new(0.0);  // [0
//! let exclusive_lower = LowerBoundOpen::new(0.0);    // (0
//! let inclusive_upper = UpperBoundClosed::new(1.0);  // 1]
//! let exclusive_upper = UpperBoundOpen::new(1.0);    // 1)
//!
//! // Compile-time prevention of boundary confusion:
//! // assert_eq!(inclusive_lower, exclusive_lower); // ← Compilation error!
//! ```
//!
//! ## Architecture Summary
//!
//! The bounds system consists of four key components:
//!
//! ### 1. Marker Traits and Types
//! - [`BoundSide`]: [`Lower`] vs [`Upper`] marker trait and types
//! - [`BoundType`]: [`Open`] vs [`Closed`] marker trait and types
//! - Enables compile-time distinction between boundary semantics
//!
//! ### 2. Core Bound Type
//! - [`IntervalBound<RealType, Side, Type>`]: Generic bound representation
//! - Phantom types encode boundary semantics in the type system
//! - Zero-cost abstraction over scalar values
//!
//! ### 3. Type Aliases
//! - [`LowerBoundOpen`], [`LowerBoundClosed`]: Lower boundary types
//! - [`UpperBoundOpen`], [`UpperBoundClosed`]: Upper boundary types
//! - Provide convenient, intention-revealing names
//!
//! ### 4. Conversion Traits
//! - [`BoundTypeConversion`]: Convert between open/closed bounds
//! - [`BoundSideConversion`]: Convert between lower/upper bounds
//! - Enable flexible bound manipulation while maintaining type safety
//!
//! ## Mathematical Foundation
//!
//! The bound types maintain strict mathematical ordering that reflects their semantic meaning:
//!
//! | Comparison | Mathematical Meaning | Code Example |
//! |------------|---------------------|--------------|
//! | `[a < (a` | Closed bound is "tighter" than open | `LowerBoundClosed::new(5.0) < LowerBoundOpen::new(5.0)` |
//! | `b) < b]` | Open bound is "smaller" than closed | `UpperBoundOpen::new(5.0) < UpperBoundClosed::new(5.0)` |
//!
//! This ordering ensures that interval intersections, unions, and containment checks
//! produce mathematically correct results.
//!
//! ## Generic Over Any [`num_valid::RealScalar`] Type
//!
//! All bound types work 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<N>` | ⚡ Precision-dependent | ✅ Always | Arbitrary precision needs (available from the [num-valid](https://crates.io/crates/num-valid) crate when compiled with `--features=rug`)|
//!
//! ## Performance Characteristics
//!
//! ### Zero-Cost Abstractions
//! - **Memory**: Each bound is a single scalar value (8 bytes for f64)
//! - **Operations**: All bound operations compile to direct scalar operations
//! - **Validation**: Debug-only by default, zero overhead in release builds
//!
//! ### Operation Complexity
//! | Operation | Time | Space | Notes |
//! |-----------|------|-------|-------|
//! | Construction | O(1) | O(1) | Debug validation only |
//! | Comparison | O(1) | O(1) | Direct scalar comparison |
//! | Value access | O(1) | O(1) | Zero-cost via `AsRef` |
//!
//! ## Use Cases and Applications
//!
//! ### Grid Construction
//! ```rust
//! use grid1d::{bounds::*, intervals::*};
//! use try_create::New;
//!
//! // Define grid boundaries with precise semantics using actual interval types
//! struct GridRange {
//!     start: LowerBoundClosed<f64>,  // Include the starting point
//!     end: UpperBoundOpen<f64>,      // Exclude the ending point
//! }
//!
//! impl GridRange {
//!     fn new(start: f64, end: f64) -> Self {
//!         Self {
//!             start: LowerBoundClosed::new(start),
//!             end: UpperBoundOpen::new(end),
//!         }
//!     }
//!     
//!     fn to_interval(&self) -> IntervalLowerClosedUpperOpen<f64> {
//!         IntervalLowerClosedUpperOpen::new(*self.start.as_ref(), *self.end.as_ref())
//!     }
//!     
//!     fn contains(&self, point: f64) -> bool {
//!         self.to_interval().contains_point(&point)
//!     }
//! }
//!
//! let range = GridRange::new(0.0, 10.0);  // [0, 10)
//! assert!(range.contains(0.0));   // Start included
//! assert!(!range.contains(10.0)); // End excluded
//! ```
//!
//! ### Advanced Usage Patterns
//!
//! #### Constraint Composition
//! ```rust
//! use grid1d::bounds::*;
//! use try_create::New;
//!
//! // Combine multiple constraints
//! struct ConstraintSystem {
//!     lower_limit: LowerBoundClosed<f64>,
//!     upper_limit: UpperBoundOpen<f64>,
//! }
//!
//! impl ConstraintSystem {
//!     fn new(min_value: f64, max_value: f64) -> Self {
//!         Self {
//!             lower_limit: LowerBoundClosed::new(min_value),
//!             upper_limit: UpperBoundOpen::new(max_value),
//!         }
//!     }
//!     
//!     fn is_feasible(&self, value: f64) -> bool {
//!         self.lower_limit.value_within_bound(&value) && self.upper_limit.value_within_bound(&value)
//!     }
//! }
//! ```
//!
//! #### Generic Bound Processing
//! ```rust
//! use grid1d::bounds::*;
//! use num_valid::RealScalar;
//!
//! fn process_bound<T, S, B>(bound: &IntervalBound<T, S, B>) -> String
//! where
//!     T: RealScalar + std::fmt::Display,
//!     S: BoundSide,
//!     B: BoundType,
//! {
//!     format!(
//!         "{} bound: {} ({})",
//!         if bound.is_lower_bound() { "Lower" } else { "Upper" },
//!         bound.as_ref(),
//!         if bound.is_closed() { "inclusive" } else { "exclusive" }
//!     )
//! }
//! ```
//!
//! ## Error Handling and Validation
//!
//! ### Debug-Mode Validation
//! ```rust,ignore
//! use grid1d::bounds::*;
//! use try_create::New;
//!
//! // Valid construction
//! let bound = LowerBoundClosed::new(42.0);
//! assert_eq!(bound.as_ref(), &42.0);
//!
//! // Invalid values panic in debug mode only
//! #[cfg(debug_assertions)]
//! {
//!     std::panic::catch_unwind(|| {
//!         LowerBoundClosed::new(f64::NAN); // Panics in debug
//!     }).expect_err("Should panic on NaN");
//! }
//!
//! // In release mode, validation is skipped for performance
//! #[cfg(not(debug_assertions))]
//! {
//!     let _invalid = LowerBoundClosed::new(f64::NAN); // No panic in release
//! }
//! ```
//!
//! ## Best Practices
//!
//! ### Type Selection Guidelines
//! - **[`LowerBoundClosed`]**: Use for `x ≥ a` constraints → [`IntervalLowerClosedUpperUnbounded`](crate::intervals::IntervalLowerClosedUpperUnbounded), [`IntervalClosed`](crate::intervals::IntervalClosed)
//! - **[`LowerBoundOpen`]**: Use for `x > a` constraints → [`IntervalLowerOpenUpperUnbounded`](crate::intervals::IntervalLowerOpenUpperUnbounded), [`IntervalOpen`](crate::intervals::IntervalOpen)
//! - **[`UpperBoundClosed`]**: Use for `x ≤ b` constraints → [`IntervalLowerUnboundedUpperClosed`](crate::intervals::IntervalLowerUnboundedUpperClosed), [`IntervalClosed`](crate::intervals::IntervalClosed)
//! - **[`UpperBoundOpen`]**: Use for `x < b` constraints → [`IntervalLowerUnboundedUpperOpen`](crate::intervals::IntervalLowerUnboundedUpperOpen), [`IntervalOpen`](crate::intervals::IntervalOpen)
//!
//! ### Recommended Patterns
//! ```rust
//! use grid1d::{bounds::*, intervals::*};
//! use num_valid::RealNative64StrictFiniteInDebug;
//! use try_create::{IntoInner, New, TryNew};
//!
//! // Define type aliases for your application
//! type Real = RealNative64StrictFiniteInDebug;
//! type SafeLowerBound = LowerBoundClosed<Real>;
//! type StrictUpperBound = UpperBoundOpen<Real>;
//!
//! // Pattern: Safe construction with validation
//! fn create_safe_bounds(start: f64, end: f64) ->
//!     Result<(SafeLowerBound, StrictUpperBound), <Real as TryNew>::Error> {
//!     let start_val = Real::try_new(start)?;
//!     let end_val = Real::try_new(end)?;
//!     Ok((SafeLowerBound::new(start_val), StrictUpperBound::new(end_val)))
//! }
//!
//! // Pattern: Convert bounds to intervals
//! fn bounds_to_interval(
//!     lower: SafeLowerBound,
//!     upper: StrictUpperBound
//! ) -> IntervalLowerClosedUpperOpen<Real> {
//!     IntervalLowerClosedUpperOpen::new(
//!         lower.into_inner(),
//!         upper.into_inner()
//!     )
//! }
//! ```
//!
//! ## Performance Guarantees
//!
//! ### Zero-Cost Abstractions
//! - **Memory Layout**: Each bound is exactly one scalar value (e.g., 8 bytes for `f64`)
//! - **Runtime Cost**: All operations compile to direct scalar operations
//! - **Validation**: Debug assertions only, zero overhead in release builds
//! - **Monomorphization**: Generic code specializes to concrete scalar types

use derive_more::AsRef;
use duplicate::duplicate_item;
use num_valid::RealScalar;
use serde::{Deserialize, Serialize};
use std::{cmp::Ordering, fmt::Debug};
use try_create::{IntoInner, New};

//------------------------------------------------------------------------------------------------
/// Trait for bound side markers (Lower/Upper)
///
/// This trait distinguishes between lower and upper bounds in interval arithmetic.
/// The key feature is the [`Opposite`](BoundSide::Opposite) associated type, which
/// enables type-safe conversions between lower and upper bounds without requiring
/// explicit type annotations.
///
/// # Associated Types
///
/// - [`Opposite`](BoundSide::Opposite): The opposite bound side type
///   - For `Lower`, `Opposite = Upper`
///   - For `Upper`, `Opposite = Lower`
///   - The constraint `BoundSide<Opposite = Self>` ensures bidirectional conversion
///
/// # Examples
///
/// ```rust
/// use grid1d::bounds::*;
///
/// // The associated type enables clean API without turbofish syntax
/// let lower = LowerBoundRuntime::new_open(5.0);
/// let upper = lower.flip_bound_side(); // Type automatically inferred as UpperBoundRuntime
///
/// // Works in both directions
/// let back_to_lower = upper.flip_bound_side(); // Type automatically inferred as LowerBoundRuntime
/// ```
pub trait BoundSide: Debug + Clone + PartialEq + Eq + Serialize + for<'a> Deserialize<'a> {
    /// The opposite bound side (Lower ↔ Upper)
    ///
    /// This associated type enables [`flip_bound_side()`](IntervalBoundRuntime::flip_bound_side)
    /// to automatically infer the return type without requiring turbofish syntax.
    ///
    /// # Type Relationships
    ///
    /// - `<Lower as BoundSide>::Opposite = Upper`
    /// - `<Upper as BoundSide>::Opposite = Lower`
    ///
    /// The constraint `BoundSide<Opposite = Self>` ensures that flipping twice
    /// returns to the original type: `Side::Opposite::Opposite = Side`
    type Opposite: BoundSide<Opposite = Self>;

    /// Returns true if this is an upper bound
    fn is_upper() -> bool;

    /// Returns true if this is a lower bound
    #[inline(always)]
    fn is_lower() -> bool {
        !Self::is_upper()
    }
}

/// Marker type for upper bounds
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Upper;

/// Marker type for lower bounds
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Lower;

impl BoundSide for Upper {
    type Opposite = Lower;

    #[inline(always)]
    fn is_upper() -> bool {
        true
    }
}

impl BoundSide for Lower {
    type Opposite = Upper;

    #[inline(always)]
    fn is_upper() -> bool {
        false
    }
}
//------------------------------------------------------------------------------------------------

//------------------------------------------------------------------------------------------------
/// Runtime-determined bound type that can be either open or closed.
///
/// [`IntervalBoundRuntime<RealType, Side>`] provides a unified enum for representing bounds
/// where the open/closed nature needs to be determined at runtime rather than
/// compile time. This is essential for dynamic interval construction, serialization,
/// configuration-driven applications, and algorithms that need to work with varying
/// boundary semantics.
///
/// ## Design Philosophy
///
/// ### Compile-Time vs Runtime Trade-offs
///
/// Unlike the compile-time typed bounds ([`LowerBoundOpen`], [`LowerBoundClosed`], etc.),
/// this enum moves the open/closed distinction from compile time to runtime:
///
/// | Aspect | Compile-Time Bounds | Runtime Bounds |
/// |--------|-------------------|----------------|
/// | **Type Safety** | ✅ Cannot mix open/closed at compile time | ❌ Open/closed confusion possible |
/// | **Performance** | ✅ Zero-cost abstractions, inlined | ⚡ Small pattern matching overhead |
/// | **Flexibility** | ❌ Fixed at compile time | ✅ Can change dynamically |
/// | **Serialization** | ❌ Complex (type information lost) | ✅ Natural enum serialization |
/// | **User Input** | ❌ Requires compile-time knowledge | ✅ Can handle dynamic input |
///
/// ### When to Use Runtime Bounds
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// // ✅ GOOD: Dynamic interval construction from user input
/// fn create_bound_from_config(value: f64, is_closed: bool) -> LowerBoundRuntime<f64> {
///     if is_closed {
///         IntervalBoundRuntime::Closed(LowerBoundClosed::new(value))
///     } else {
///         IntervalBoundRuntime::Open(LowerBoundOpen::new(value))
///     }
/// }
///
/// // ✅ GOOD: Serialization scenarios
/// let bound = create_bound_from_config(0.0, true);
/// let serialized = serde_json::to_string(&bound).unwrap();
/// let deserialized: LowerBoundRuntime<f64> = serde_json::from_str(&serialized).unwrap();
///
/// // ❌ AVOID: When bound type is known at compile time
/// fn bad_usage() -> LowerBoundRuntime<f64> {
///     // This should just be LowerBoundClosed::new(0.0)
///     IntervalBoundRuntime::Closed(LowerBoundClosed::new(0.0))
/// }
/// ```
///
/// ## Type Parameters
///
/// - **`RealType`**: Any scalar type implementing [`num_valid::RealScalar`]
/// - **`Side`**: Either [`Lower`] or [`Upper`] boundary side marker
///
/// ## Variants
///
/// ### [`IntervalBoundRuntime::Open`]
///
/// Contains an open bound that excludes the boundary value:
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// let open_lower = IntervalBoundRuntime::Open(LowerBoundOpen::new(5.0));
/// let open_upper = IntervalBoundRuntime::Open(UpperBoundOpen::new(10.0));
///
/// // Open bounds exclude their boundary values
/// assert!(!open_lower.value_within_bound(&5.0));   // 5 > 5 → false
/// assert!(!open_upper.value_within_bound(&10.0));  // 10 < 10 → false
///
/// // But include nearby values
/// assert!(open_lower.value_within_bound(&5.1));    // 5.1 > 5 → true
/// assert!(open_upper.value_within_bound(&9.9));    // 9.9 < 10 → true
/// ```
///
/// ### [`IntervalBoundRuntime::Closed`]
///
/// Contains a closed bound that includes the boundary value:
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// let closed_lower = IntervalBoundRuntime::Closed(LowerBoundClosed::new(5.0));
/// let closed_upper = IntervalBoundRuntime::Closed(UpperBoundClosed::new(10.0));
///
/// // Closed bounds include their boundary values
/// assert!(closed_lower.value_within_bound(&5.0));   // 5 ≥ 5 → true
/// assert!(closed_upper.value_within_bound(&10.0));  // 10 ≤ 10 → true
///
/// // And also include nearby values
/// assert!(closed_lower.value_within_bound(&5.1));   // 5.1 ≥ 5 → true
/// assert!(closed_upper.value_within_bound(&9.9));   // 9.9 ≤ 10 → true
/// ```
///
/// ## Type Aliases for Convenience
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// // Specific side aliases for cleaner code
/// type LowerBoundRuntime<T> = IntervalBoundRuntime<T, Lower>;
/// type UpperBoundRuntime<T> = IntervalBoundRuntime<T, Upper>;
///
/// // Usage examples
/// let lower: LowerBoundRuntime<f64> = IntervalBoundRuntime::Closed(LowerBoundClosed::new(0.0));
/// let upper: UpperBoundRuntime<f64> = IntervalBoundRuntime::Open(UpperBoundOpen::new(1.0));
/// ```
///
/// ## Core Operations
///
/// ### Boundary Constraint Testing
///
/// All runtime bounds implement [`BoundTypeChecks`] and provide constraint validation:
///
/// ```rust
/// use grid1d::bounds::*;
/// use num_valid::RealScalar;
/// use try_create::New;
///
/// fn test_constraint<T: RealScalar>(
///     bound: &IntervalBoundRuntime<T, Lower>,
///     test_value: &T
/// ) -> bool {
///     match bound {
///         IntervalBoundRuntime::Open(open_bound) => {
///             open_bound.value_within_bound(test_value)  // x > bound_value
///         }
///         IntervalBoundRuntime::Closed(closed_bound) => {
///             closed_bound.value_within_bound(test_value)  // x ≥ bound_value
///         }
///     }
/// }
///
/// let open_bound = IntervalBoundRuntime::Open(LowerBoundOpen::new(5.0));
/// let closed_bound = IntervalBoundRuntime::Closed(LowerBoundClosed::new(5.0));
///
/// assert!(!test_constraint(&open_bound, &5.0));    // 5 > 5 → false
/// assert!(test_constraint(&closed_bound, &5.0));   // 5 ≥ 5 → true
/// ```
///
/// ### Value Access and Manipulation
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::{New, IntoInner};
///
/// let bound = IntervalBoundRuntime::Closed(LowerBoundClosed::new(3.14));
///
/// // Access the underlying value
/// let value_ref: &f64 = bound.as_ref();
/// assert_eq!(*value_ref, 3.14);
///
/// // Extract the value (consuming the bound)
/// let value: f64 = bound.into_inner();
/// assert_eq!(value, 3.14);
/// ```
///
/// ### Type Information Queries
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// let open_bound = IntervalBoundRuntime::Open(LowerBoundOpen::new(1.0));
/// let closed_bound = IntervalBoundRuntime::Closed(UpperBoundClosed::new(2.0));
///
/// // Query boundary inclusion behavior
/// assert!(open_bound.is_open());
/// assert!(!open_bound.is_closed());
/// assert!(!open_bound.includes_boundary());
///
/// assert!(!closed_bound.is_open());
/// assert!(closed_bound.is_closed());
/// assert!(closed_bound.includes_boundary());
/// ```
///
/// ## Conversion Patterns
///
/// ### From Compile-Time to Runtime Bounds
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// // Automatic conversion via From trait
/// let compile_time_bound = LowerBoundClosed::new(5.0);
/// let runtime_bound: LowerBoundRuntime<f64> = compile_time_bound.into();
///
/// match runtime_bound {
///     IntervalBoundRuntime::Closed(bound) => {
///         println!("Converted to runtime closed bound: {}", bound.as_ref());
///     }
///     IntervalBoundRuntime::Open(_) => unreachable!(),
/// }
/// ```
///
/// ### Collection of Mixed Bound Types
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// // Store different bound types in the same collection
/// let bounds: Vec<LowerBoundRuntime<f64>> = vec![
///     LowerBoundClosed::new(0.0).into(),
///     LowerBoundOpen::new(1.0).into(),
///     LowerBoundClosed::new(2.0).into(),
///     LowerBoundOpen::new(3.0).into(),
/// ];
///
/// // Process them uniformly
/// for (i, bound) in bounds.iter().enumerate() {
///     let inclusion = if bound.is_closed() { "includes" } else { "excludes" };
///     println!("Bound {}: {} (value {})", i, inclusion, bound.as_ref());
/// }
/// ```
///
/// ## Mathematical Ordering
///
/// Runtime bounds maintain the same mathematical ordering as their compile-time counterparts:
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// let closed_lower = LowerBoundRuntime::Closed(LowerBoundClosed::new(5.0));
/// let open_lower = LowerBoundRuntime::Open(LowerBoundOpen::new(5.0));
///
/// // For lower bounds: closed is "tighter" than open
/// assert!(closed_lower < open_lower);  // [5 < (5
///
/// let open_upper = UpperBoundRuntime::Open(UpperBoundOpen::new(5.0));
/// let closed_upper = UpperBoundRuntime::Closed(UpperBoundClosed::new(5.0));
///
/// // For upper bounds: open is "tighter" than closed
/// assert!(open_upper < closed_upper);  // 5) < 5]
/// ```
///
/// ## Serialization Support
///
/// Runtime bounds have natural serialization support via `serde`:
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
/// use serde_json;
///
/// let bounds = vec![
///     LowerBoundRuntime::Closed(LowerBoundClosed::new(0.0)),
///     LowerBoundRuntime::Open(LowerBoundOpen::new(1.0)),
/// ];
///
/// // Serialize to JSON
/// let json = serde_json::to_string_pretty(&bounds).unwrap();
/// println!("Serialized bounds:\n{}", json);
///
/// // Deserialize back
/// let deserialized: Vec<LowerBoundRuntime<f64>> =
///     serde_json::from_str(&json).unwrap();
///
/// assert_eq!(bounds.len(), deserialized.len());
/// for (original, restored) in bounds.iter().zip(deserialized.iter()) {
///     assert_eq!(original.as_ref(), restored.as_ref());
///     assert_eq!(original.is_closed(), restored.is_closed());
/// }
/// ```
///
/// ## Advanced Usage Patterns
///
/// ### Dynamic Interval Construction
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// struct IntervalConfig {
///     lower_value: f64,
///     lower_closed: bool,
///     upper_value: f64,
///     upper_closed: bool,
/// }
///
/// fn create_dynamic_interval(config: IntervalConfig) -> (LowerBoundRuntime<f64>, UpperBoundRuntime<f64>) {
///     let lower = if config.lower_closed {
///         IntervalBoundRuntime::Closed(LowerBoundClosed::new(config.lower_value))
///     } else {
///         IntervalBoundRuntime::Open(LowerBoundOpen::new(config.lower_value))
///     };
///     
///     let upper = if config.upper_closed {
///         IntervalBoundRuntime::Closed(UpperBoundClosed::new(config.upper_value))
///     } else {
///         IntervalBoundRuntime::Open(UpperBoundOpen::new(config.upper_value))
///     };
///     
///     (lower, upper)
/// }
///
/// // Create different interval types from configuration
/// let closed_interval = create_dynamic_interval(IntervalConfig {
///     lower_value: 0.0, lower_closed: true,
///     upper_value: 1.0, upper_closed: true,
/// }); // Represents [0, 1]
///
/// let half_open_interval = create_dynamic_interval(IntervalConfig {
///     lower_value: 0.0, lower_closed: true,
///     upper_value: 1.0, upper_closed: false,
/// }); // Represents [0, 1)
/// ```
///
/// ### Generic Algorithm Implementation
///
/// ```rust
/// use grid1d::bounds::*;
/// use num_valid::RealScalar;
/// use try_create::New;
///
/// fn find_overlapping_bounds<T: RealScalar + std::fmt::Display>(
///     bounds: &[IntervalBoundRuntime<T, Lower>],
///     test_value: &T
/// ) -> Vec<usize> {
///     bounds.iter()
///         .enumerate()
///         .filter_map(|(i, bound)| {
///             let satisfies = match bound {
///                 IntervalBoundRuntime::Open(open_bound) => {
///                     open_bound.value_within_bound(test_value)
///                 }
///                 IntervalBoundRuntime::Closed(closed_bound) => {
///                     closed_bound.value_within_bound(test_value)
///                 }
///             };
///             if satisfies { Some(i) } else { None }
///         })
///         .collect()
/// }
///
/// let bounds = vec![
///     IntervalBoundRuntime::Closed(LowerBoundClosed::new(0.0)),  // [0 - includes 0.0
///     IntervalBoundRuntime::Open(LowerBoundOpen::new(0.0)),      // (0 - excludes 0.0
///     IntervalBoundRuntime::Closed(LowerBoundClosed::new(1.0)),  // [1 - excludes 0.0
/// ];
///
/// let overlapping = find_overlapping_bounds(&bounds, &0.0);
/// assert_eq!(overlapping, vec![0]); // Only the closed bound includes 0.0
/// ```
///
/// ## Performance Characteristics
///
/// ### Memory Layout
/// - **Size**: Same as underlying [`IntervalBound`] plus enum discriminant
/// - **Alignment**: Natural alignment of the largest variant
/// - **Overhead**: One byte discriminant plus potential padding
///
/// ### Operation Complexity
/// | Operation | Time | Notes |
/// |-----------|------|-------|
/// | Construction | O(1) | Simple enum wrapping |
/// | Pattern matching | O(1) | Compile-time branch prediction |
/// | Value access | O(1) | Direct field access |
/// | Constraint testing | O(1) | Inlined comparison operations |
/// | Serialization | O(1) | Direct enum serialization |
///
/// ### Performance vs Compile-Time Bounds
///
/// ```rust
/// use grid1d::bounds::*;
///
/// // Runtime bounds: small pattern matching overhead
/// fn check_runtime_bound(bound: &LowerBoundRuntime<f64>, value: f64) -> bool {
///     match bound {  // Small branching cost
///         IntervalBoundRuntime::Open(b) => value > *b.as_ref(),
///         IntervalBoundRuntime::Closed(b) => value >= *b.as_ref(),
///     }
/// }
///
/// // Compile-time bounds: zero overhead
/// fn check_compile_time_bound<T: BoundType>(bound: &LowerBound<f64, T>, value: f64) -> bool {
///     if T::is_open() {
///         value > *bound.as_ref()  // Optimized to direct comparison
///     } else {
///         value >= *bound.as_ref() // Optimized to direct comparison
///     }
/// }
/// ```
///
/// ## Best Practices
///
/// ### When to Use Runtime Bounds
/// - ✅ **User input**: Boundary types determined by configuration
/// - ✅ **Serialization**: Saving/loading interval data
/// - ✅ **Dynamic algorithms**: Boundary types vary during execution
/// - ✅ **Heterogeneous collections**: Storing different bound types together
///
/// ### When to Use Compile-Time Bounds
/// - ✅ **Known constraints**: Boundary types fixed at compile time
/// - ✅ **Performance critical**: Zero-overhead abstractions required
/// - ✅ **Type safety**: Prevent mixing incompatible boundary types
/// - ✅ **Generic algorithms**: Type-parameterized over boundary semantics
///
/// ### Conversion Strategy
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// // Pattern: Convert from runtime to compile-time when possible
/// fn extract_closed_bound(bound: LowerBoundRuntime<f64>) -> Option<LowerBoundClosed<f64>> {
///     match bound {
///         IntervalBoundRuntime::Closed(closed_bound) => Some(closed_bound),
///         IntervalBoundRuntime::Open(_) => None,
///     }
/// }
///
/// // Pattern: Use runtime bounds at API boundaries, compile-time internally
/// fn process_user_interval(
///     lower: LowerBoundRuntime<f64>,
///     upper: UpperBoundRuntime<f64>
/// ) -> String {
///     match (lower, upper) {
///         (IntervalBoundRuntime::Closed(l), IntervalBoundRuntime::Closed(u)) => {
///             process_closed_interval(l, u)  // Delegate to compile-time version
///         }
///         (IntervalBoundRuntime::Open(l), IntervalBoundRuntime::Open(u)) => {
///             process_open_interval(l, u)    // Delegate to compile-time version
///         }
///         // Handle mixed cases...
///         _ => "Mixed interval types".to_string(),
///     }
/// }
///
/// fn process_closed_interval(
///     lower: LowerBoundClosed<f64>,
///     upper: UpperBoundClosed<f64>
/// ) -> String {
///     format!("Closed interval: [{}, {}]", lower.as_ref(), upper.as_ref())
/// }
///
/// fn process_open_interval(
///     lower: LowerBoundOpen<f64>,
///     upper: UpperBoundOpen<f64>
/// ) -> String {
///     format!("Open interval: ({}, {})", lower.as_ref(), upper.as_ref())
/// }
/// ```
///
/// ## Integration with Grid1D
///
/// Runtime bounds integrate seamlessly with the broader grid1d ecosystem:
///
/// ```rust
/// use grid1d::{bounds::*, intervals::*};
/// use try_create::{IntoInner, New};
///
/// // Convert runtime bounds to actual intervals when needed
/// fn runtime_bounds_to_interval(
///     lower: LowerBoundRuntime<f64>,
///     upper: UpperBoundRuntime<f64>
/// ) -> Box<dyn std::fmt::Debug> {
///     match (lower, upper) {
///         (IntervalBoundRuntime::Closed(l), IntervalBoundRuntime::Closed(u)) => {
///             Box::new(IntervalClosed::new(l.into_inner(), u.into_inner()))
///         }
///         (IntervalBoundRuntime::Open(l), IntervalBoundRuntime::Open(u)) => {
///             Box::new(IntervalOpen::new(l.into_inner(), u.into_inner()))
///         }
///         (IntervalBoundRuntime::Closed(l), IntervalBoundRuntime::Open(u)) => {
///             Box::new(IntervalLowerClosedUpperOpen::new(l.into_inner(), u.into_inner()))
///         }
///         (IntervalBoundRuntime::Open(l), IntervalBoundRuntime::Closed(u)) => {
///             Box::new(IntervalLowerOpenUpperClosed::new(l.into_inner(), u.into_inner()))
///         }
///     }
/// }
/// ```
///
/// The [`IntervalBoundRuntime`] enum provides essential flexibility for dynamic interval
/// construction while maintaining mathematical correctness and integration with the
/// grid1d type system. Use it when runtime flexibility is needed, but prefer
/// compile-time bounds when the boundary semantics are known statically.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(bound(deserialize = "RealType: for<'a> Deserialize<'a>"))]
pub enum IntervalBoundRuntime<RealType: RealScalar, Side: BoundSide> {
    /// An open (exclusive) interval bound.
    Open(IntervalBound<RealType, Side, Open>),
    /// A closed (inclusive) interval bound.
    Closed(IntervalBound<RealType, Side, Closed>),
}

impl<RealType: RealScalar, Side: BoundSide> IntervalBoundRuntime<RealType, Side> {
    /// Create a new open (exclusive) runtime bound.
    ///
    /// This is a convenience constructor that creates an open bound variant without
    /// requiring explicit construction of the underlying [`IntervalBound`].
    ///
    /// # Mathematical Meaning
    ///
    /// - For **lower bounds**: Creates `x > value` constraint (excludes the boundary)
    /// - For **upper bounds**: Creates `x < value` constraint (excludes the boundary)
    ///
    /// # Examples
    ///
    /// ## Lower Bound
    ///
    /// ```rust
    /// use grid1d::bounds::*;
    ///
    /// let lower_open: LowerBoundRuntime<f64> = IntervalBoundRuntime::new_open(5.0);
    ///
    /// // Excludes the boundary value
    /// assert!(!lower_open.value_within_bound(&5.0));  // 5 > 5 → false
    /// assert!(lower_open.value_within_bound(&5.1));   // 5.1 > 5 → true
    /// assert!(!lower_open.value_within_bound(&4.9));  // 4.9 > 5 → false
    ///
    /// assert!(lower_open.is_open());
    /// assert!(lower_open.is_open_variant());
    /// ```
    ///
    /// ## Upper Bound
    ///
    /// ```rust
    /// use grid1d::bounds::*;
    ///
    /// let upper_open: UpperBoundRuntime<f64> = IntervalBoundRuntime::new_open(10.0);
    ///
    /// // Excludes the boundary value
    /// assert!(!upper_open.value_within_bound(&10.0));  // 10 < 10 → false
    /// assert!(upper_open.value_within_bound(&9.9));    // 9.9 < 10 → true
    /// assert!(!upper_open.value_within_bound(&10.1));  // 10.1 < 10 → false
    ///
    /// assert!(upper_open.is_open());
    /// assert!(upper_open.is_open_variant());
    /// ```
    ///
    /// ## Comparison with Manual Construction
    ///
    /// ```rust
    /// use grid1d::bounds::*;
    /// use try_create::New;
    ///
    /// // Using convenience constructor (cleaner)
    /// let bound1: LowerBoundRuntime<f64> = IntervalBoundRuntime::new_open(5.0);
    ///
    /// // Manual construction (more verbose)
    /// let bound2: LowerBoundRuntime<f64> =
    ///     IntervalBoundRuntime::Open(LowerBoundOpen::new(5.0));
    ///
    /// assert_eq!(bound1, bound2);
    /// ```
    ///
    /// # See Also
    ///
    /// - [`new_closed`](Self::new_closed) - Create a closed (inclusive) bound
    /// - [`is_open_variant`](Self::is_open_variant) - Check if bound is open
    pub fn new_open(value: RealType) -> Self {
        Self::Open(IntervalBound::<RealType, Side, Open>::new(value))
    }

    /// Create a new closed (inclusive) runtime bound.
    ///
    /// This is a convenience constructor that creates a closed bound variant without
    /// requiring explicit construction of the underlying [`IntervalBound`].
    ///
    /// # Mathematical Meaning
    ///
    /// - For **lower bounds**: Creates `x ≥ value` constraint (includes the boundary)
    /// - For **upper bounds**: Creates `x ≤ value` constraint (includes the boundary)
    ///
    /// # Examples
    ///
    /// ## Lower Bound
    ///
    /// ```rust
    /// use grid1d::bounds::*;
    ///
    /// let lower_closed: LowerBoundRuntime<f64> = IntervalBoundRuntime::new_closed(5.0);
    ///
    /// // Includes the boundary value
    /// assert!(lower_closed.value_within_bound(&5.0));  // 5 ≥ 5 → true
    /// assert!(lower_closed.value_within_bound(&5.1));  // 5.1 ≥ 5 → true
    /// assert!(!lower_closed.value_within_bound(&4.9)); // 4.9 ≥ 5 → false
    ///
    /// assert!(lower_closed.is_closed());
    /// assert!(lower_closed.is_closed_variant());
    /// ```
    ///
    /// ## Upper Bound
    ///
    /// ```rust
    /// use grid1d::bounds::*;
    ///
    /// let upper_closed: UpperBoundRuntime<f64> = IntervalBoundRuntime::new_closed(10.0);
    ///
    /// // Includes the boundary value
    /// assert!(upper_closed.value_within_bound(&10.0));  // 10 ≤ 10 → true
    /// assert!(upper_closed.value_within_bound(&9.9));   // 9.9 ≤ 10 → true
    /// assert!(!upper_closed.value_within_bound(&10.1)); // 10.1 ≤ 10 → false
    ///
    /// assert!(upper_closed.is_closed());
    /// assert!(upper_closed.is_closed_variant());
    /// ```
    ///
    /// ## Comparison with Manual Construction
    ///
    /// ```rust
    /// use grid1d::bounds::*;
    /// use try_create::New;
    ///
    /// // Using convenience constructor (cleaner)
    /// let bound1: LowerBoundRuntime<f64> = IntervalBoundRuntime::new_closed(5.0);
    ///
    /// // Manual construction (more verbose)
    /// let bound2: LowerBoundRuntime<f64> =
    ///     IntervalBoundRuntime::Closed(LowerBoundClosed::new(5.0));
    ///
    /// assert_eq!(bound1, bound2);
    /// ```
    ///
    /// # See Also
    ///
    /// - [`new_open`](Self::new_open) - Create an open (exclusive) bound
    /// - [`is_closed_variant`](Self::is_closed_variant) - Check if bound is closed
    pub fn new_closed(value: RealType) -> Self {
        Self::Closed(IntervalBound::<RealType, Side, Closed>::new(value))
    }

    /// Check if this runtime bound is a closed variant without consuming it.
    ///
    /// This method provides a non-consuming way to check the bound type, which is useful
    /// when you need to inspect the bound type without pattern matching or when you want
    /// to check the type multiple times.
    ///
    /// # Returns
    ///
    /// - `true` if the bound is [`IntervalBoundRuntime::Closed`]
    /// - `false` if the bound is [`IntervalBoundRuntime::Open`]
    ///
    /// # Performance
    ///
    /// This is a zero-cost check that compiles to a simple discriminant comparison.
    ///
    /// # Examples
    ///
    /// ## Basic Usage
    ///
    /// ```rust
    /// use grid1d::bounds::*;
    ///
    /// let closed_bound: LowerBoundRuntime<f64> = IntervalBoundRuntime::new_closed(5.0);
    /// let open_bound: LowerBoundRuntime<f64> = IntervalBoundRuntime::new_open(5.0);
    ///
    /// assert!(closed_bound.is_closed_variant());
    /// assert!(!open_bound.is_closed_variant());
    /// ```
    ///
    /// ## Conditional Logic
    ///
    /// ```rust
    /// use grid1d::bounds::*;
    ///
    /// fn describe_bound(bound: &LowerBoundRuntime<f64>) -> String {
    ///     let inclusion = if bound.is_closed_variant() {
    ///         "inclusive"
    ///     } else {
    ///         "exclusive"
    ///     };
    ///     format!("Lower bound at {} ({})", bound.as_ref(), inclusion)
    /// }
    ///
    /// let bound = IntervalBoundRuntime::new_closed(10.0);
    /// assert_eq!(describe_bound(&bound), "Lower bound at 10 (inclusive)");
    /// ```
    ///
    /// ## Difference from `is_closed()` Trait Method
    ///
    /// ```rust
    /// use grid1d::bounds::*;
    ///
    /// let bound: LowerBoundRuntime<f64> = IntervalBoundRuntime::new_closed(5.0);
    ///
    /// // Both return the same result
    /// assert_eq!(bound.is_closed_variant(), bound.is_closed());
    ///
    /// // is_closed_variant() is more explicit about checking the enum variant
    /// // is_closed() comes from the BoundTypeChecks trait
    /// ```
    ///
    /// # See Also
    ///
    /// - [`is_open_variant`](Self::is_open_variant) - Check if bound is open
    /// - [`is_closed`](BoundTypeChecks::is_closed) - Trait method for same check
    /// - [`new_closed`](Self::new_closed) - Create a closed bound
    pub fn is_closed_variant(&self) -> bool {
        matches!(self, Self::Closed(_))
    }

    /// Check if this runtime bound is an open variant without consuming it.
    ///
    /// This method provides a non-consuming way to check the bound type, which is useful
    /// when you need to inspect the bound type without pattern matching or when you want
    /// to check the type multiple times.
    ///
    /// # Returns
    ///
    /// - `true` if the bound is [`IntervalBoundRuntime::Open`]
    /// - `false` if the bound is [`IntervalBoundRuntime::Closed`]
    ///
    /// # Performance
    ///
    /// This is a zero-cost check that compiles to a simple discriminant comparison.
    ///
    /// # Examples
    ///
    /// ## Basic Usage
    ///
    /// ```rust
    /// use grid1d::bounds::*;
    ///
    /// let open_bound: LowerBoundRuntime<f64> = IntervalBoundRuntime::new_open(5.0);
    /// let closed_bound: LowerBoundRuntime<f64> = IntervalBoundRuntime::new_closed(5.0);
    ///
    /// assert!(open_bound.is_open_variant());
    /// assert!(!closed_bound.is_open_variant());
    /// ```
    ///
    /// ## Conditional Logic
    ///
    /// ```rust
    /// use grid1d::bounds::*;
    ///
    /// fn describe_constraint(bound: &UpperBoundRuntime<f64>) -> String {
    ///     let symbol = if bound.is_open_variant() { "<" } else { "≤" };
    ///     format!("x {} {}", symbol, bound.as_ref())
    /// }
    ///
    /// let bound = IntervalBoundRuntime::new_open(100.0);
    /// assert_eq!(describe_constraint(&bound), "x < 100");
    /// ```
    ///
    /// ## Collecting by Type
    ///
    /// ```rust
    /// use grid1d::bounds::*;
    ///
    /// let bounds = vec![
    ///     LowerBoundRuntime::new_open(1.0),
    ///     LowerBoundRuntime::new_closed(2.0),
    ///     LowerBoundRuntime::new_open(3.0),
    ///     LowerBoundRuntime::new_closed(4.0),
    /// ];
    ///
    /// let open_count = bounds.iter().filter(|b| b.is_open_variant()).count();
    /// let closed_count = bounds.iter().filter(|b| b.is_closed_variant()).count();
    ///
    /// assert_eq!(open_count, 2);
    /// assert_eq!(closed_count, 2);
    /// ```
    ///
    /// ## Difference from `is_open()` Trait Method
    ///
    /// ```rust
    /// use grid1d::bounds::*;
    ///
    /// let bound: UpperBoundRuntime<f64> = IntervalBoundRuntime::new_open(5.0);
    ///
    /// // Both return the same result
    /// assert_eq!(bound.is_open_variant(), bound.is_open());
    ///
    /// // is_open_variant() is more explicit about checking the enum variant
    /// // is_open() comes from the BoundTypeChecks trait
    /// ```
    ///
    /// # See Also
    ///
    /// - [`is_closed_variant`](Self::is_closed_variant) - Check if bound is closed
    /// - [`is_open`](BoundTypeChecks::is_open) - Trait method for same check
    /// - [`new_open`](Self::new_open) - Create an open bound
    pub fn is_open_variant(&self) -> bool {
        matches!(self, Self::Open(_))
    }

    /// Convert this bound to the opposite bound type, preserving the value.
    ///
    /// This method transforms an open bound to a closed bound with the same value, or vice versa.
    /// The side (lower/upper) is preserved - only the inclusion/exclusion semantics change.
    ///
    /// # Mathematical Meaning
    ///
    /// ## For Lower Bounds
    /// - **Open → Closed**: `(a` becomes `[a` — changes `x > a` to `x ≥ a`
    /// - **Closed → Open**: `[a` becomes `(a` — changes `x ≥ a` to `x > a`
    ///
    /// ## For Upper Bounds
    /// - **Open → Closed**: `b)` becomes `b]` — changes `x < b` to `x ≤ b`
    /// - **Closed → Open**: `b]` becomes `b)` — changes `x ≤ b` to `x < b`
    ///
    /// # Use Cases
    ///
    /// This method is particularly useful when:
    /// - Adjusting interval boundaries in set difference operations
    /// - Converting between interval types (e.g., `[a, b)` ↔ `(a, b]`)
    /// - Implementing boundary adjustments in computational geometry
    /// - Handling boundary conditions in numerical algorithms
    ///
    /// # Examples
    ///
    /// ## Lower Bound Conversion
    ///
    /// ```rust
    /// use grid1d::bounds::*;
    ///
    /// let open_lower: LowerBoundRuntime<f64> = IntervalBoundRuntime::new_open(5.0);
    ///
    /// // Check properties before conversion
    /// assert!(open_lower.is_open_variant());
    /// assert!(!open_lower.value_within_bound(&5.0));   // 5 > 5 → false
    ///
    /// // Convert (consumes original)
    /// let closed_lower = open_lower.flip_bound_type();
    ///
    /// // Value is preserved
    /// assert_eq!(*closed_lower.as_ref(), 5.0);
    ///
    /// // But semantics change
    /// assert!(closed_lower.is_closed_variant());
    /// assert!(closed_lower.value_within_bound(&5.0));  // 5 ≥ 5 → true
    /// ```
    ///
    /// ## Upper Bound Conversion
    ///
    /// ```rust
    /// use grid1d::bounds::*;
    ///
    /// let closed_upper: UpperBoundRuntime<f64> = IntervalBoundRuntime::new_closed(10.0);
    ///
    /// // Check properties before conversion
    /// assert!(closed_upper.is_closed_variant());
    /// assert!(closed_upper.value_within_bound(&10.0));  // 10 ≤ 10 → true
    ///
    /// // Convert (consumes original)
    /// let open_upper = closed_upper.flip_bound_type();
    ///
    /// // Value is preserved
    /// assert_eq!(*open_upper.as_ref(), 10.0);
    ///
    /// // But semantics change
    /// assert!(open_upper.is_open_variant());
    /// assert!(!open_upper.value_within_bound(&10.0));   // 10 < 10 → false
    /// ```
    ///
    /// ## Interval Boundary Adjustment
    ///
    /// ```rust
    /// use grid1d::bounds::*;
    ///
    /// // When computing interval difference A \ B, we often need to flip boundaries
    /// // If B has lower bound [b, we need to create upper bound b) for the left remainder
    /// let b_lower: LowerBoundRuntime<f64> = IntervalBoundRuntime::new_closed(3.0);  // [3
    /// let left_upper = b_lower.flip_bound_type();            // 3) — exclude what B includes
    ///
    /// // Similarly for upper bounds
    /// let b_upper: UpperBoundRuntime<f64> = IntervalBoundRuntime::new_open(7.0);    // 7)
    /// let right_lower = b_upper.flip_bound_type();          // [7 — include what B excludes
    ///
    /// assert!(left_upper.is_open_variant());
    /// assert!(right_lower.is_closed_variant());
    /// ```
    ///
    /// ## Round-Trip Consistency
    ///
    /// ```rust
    /// use grid1d::bounds::*;
    ///
    /// let original: LowerBoundRuntime<f64> = IntervalBoundRuntime::new_closed(2.5);
    ///
    /// // Save properties for comparison
    /// let original_value = *original.as_ref();
    /// let was_closed = original.is_closed_variant();
    ///
    /// // Double flip restores the type
    /// let flipped = original.flip_bound_type();
    /// let restored = flipped.flip_bound_type();
    ///
    /// // Value preserved through transformations
    /// assert_eq!(*restored.as_ref(), original_value);
    ///
    /// // Type restored
    /// assert_eq!(was_closed, restored.is_closed_variant());
    /// ```
    ///
    /// ## Dynamic Interval Adjustment
    ///
    /// ```rust
    /// use grid1d::bounds::*;
    ///
    /// fn adjust_boundary_for_exclusion(bound: LowerBoundRuntime<f64>) -> LowerBoundRuntime<f64> {
    ///     // Convert any lower bound to open (exclusive) form
    ///     if bound.is_closed_variant() {
    ///         bound.flip_bound_type()
    ///     } else {
    ///         bound
    ///     }
    /// }
    ///
    /// let closed = IntervalBoundRuntime::new_closed(1.0);
    /// let open = adjust_boundary_for_exclusion(closed);
    ///
    /// assert!(!open.value_within_bound(&1.0));  // Now excludes the boundary
    /// assert!(open.value_within_bound(&1.1));   // But includes values above
    /// ```
    ///
    /// # Performance
    ///
    /// This is an O(1) operation that:
    /// - Extracts the underlying scalar value (moving it)
    /// - Constructs a new enum variant
    /// - Has no allocations or clones
    ///
    /// For `Copy` types like `f64`, this is a single register move. For non-`Copy` types,
    /// it moves the value without cloning.
    ///
    /// If you need to preserve the original bound, clone it before calling this method:
    /// ```rust
    /// use grid1d::bounds::*;
    ///
    /// let original: LowerBoundRuntime<f64> = IntervalBoundRuntime::new_open(5.0);
    /// let flipped = original.clone().flip_bound_type();  // Keep original
    ///
    /// assert!(original.is_open_variant());
    /// assert!(flipped.is_closed_variant());
    /// ```
    ///
    /// # See Also
    ///
    /// - [`new_open`](Self::new_open) - Create an open bound
    /// - [`new_closed`](Self::new_closed) - Create a closed bound
    /// - [`is_open_variant`](Self::is_open_variant) - Check if bound is open
    /// - [`is_closed_variant`](Self::is_closed_variant) - Check if bound is closed
    #[inline(always)]
    pub fn flip_bound_type(self) -> Self {
        match self {
            Self::Open(bound) => Self::Closed(IntervalBound::new(bound.into_inner())),
            Self::Closed(bound) => Self::Open(IntervalBound::new(bound.into_inner())),
        }
    }

    /// Converts a bound to the opposite side while preserving the open/closed type.
    ///
    /// This function transforms an `IntervalBoundRuntime<RealType, Side>` to
    /// `IntervalBoundRuntime<RealType, Side::Opposite>`, changing `Lower` ↔ `Upper`
    /// while keeping `Open` ↔ `Open` and `Closed` ↔ `Closed`.
    ///
    /// The return type is determined by the [`BoundSide::Opposite`] associated type,
    /// so no turbofish syntax is needed - the type system automatically infers the
    /// correct opposite side based on the input.
    ///
    /// # Type Transformations
    ///
    /// | Input Type | Output Type |
    /// |------------|-------------|
    /// | `LowerBoundRuntime::Open(a)` | `UpperBoundRuntime::Open(a)` |
    /// | `LowerBoundRuntime::Closed(a)` | `UpperBoundRuntime::Closed(a)` |
    /// | `UpperBoundRuntime::Open(b)` | `LowerBoundRuntime::Open(b)` |
    /// | `UpperBoundRuntime::Closed(b)` | `LowerBoundRuntime::Closed(b)` |
    ///
    /// # Use Cases
    ///
    /// This function is primarily useful in interval set operations (like difference)
    /// where you need to take a bound from one interval and use it as the opposite
    /// side for another interval, while flipping the inclusion/exclusion semantics.
    ///
    /// # Examples
    ///
    /// ## Converting Lower to Upper Bounds
    ///
    /// ```rust
    /// use grid1d::bounds::*;
    ///
    /// // Convert lower bound to upper bound (both open)
    /// let lower_open = LowerBoundRuntime::new_open(5.0);
    /// let upper_open: UpperBoundRuntime<f64> = lower_open.flip_bound_side();
    /// assert!(upper_open.is_open_variant());
    /// assert_eq!(*upper_open.as_ref(), 5.0);
    ///
    /// // Convert lower bound to upper bound (both closed)
    /// let lower_closed = LowerBoundRuntime::new_closed(10.0);
    /// let upper_closed: UpperBoundRuntime<f64> = lower_closed.flip_bound_side();
    /// assert!(upper_closed.is_closed_variant());
    /// assert_eq!(*upper_closed.as_ref(), 10.0);
    /// ```
    ///
    /// ## Converting Upper to Lower Bounds
    ///
    /// ```rust
    /// use grid1d::bounds::*;
    ///
    /// // Convert upper bound to lower bound (both open)
    /// let upper_open = UpperBoundRuntime::new_open(20.0);
    /// let lower_open: LowerBoundRuntime<f64> = upper_open.flip_bound_side();
    /// assert!(lower_open.is_open_variant());
    /// assert_eq!(*lower_open.as_ref(), 20.0);
    ///
    /// // Convert upper bound to lower bound (both closed)
    /// let upper_closed = UpperBoundRuntime::new_closed(15.0);
    /// let lower_closed: LowerBoundRuntime<f64> = upper_closed.flip_bound_side();
    /// assert!(lower_closed.is_closed_variant());
    /// assert_eq!(*lower_closed.as_ref(), 15.0);
    /// ```
    ///
    /// ## Combining with Bound Type Flipping
    ///
    /// You can combine `flip_bound_side()` with `flip_bound_type()` to perform
    /// both transformations:
    ///
    /// ```rust
    /// use grid1d::bounds::*;
    ///
    /// // Start with a closed lower bound
    /// let lower_closed = LowerBoundRuntime::new_closed(3.0);
    ///
    /// // Convert to open upper bound (both transformations)
    /// let upper_open: UpperBoundRuntime<f64> = lower_closed
    ///     .flip_bound_side()  // Lower Closed -> Upper Closed
    ///     .flip_bound_type();  // Upper Closed -> Upper Open
    ///
    /// assert!(upper_open.is_open_variant());
    /// assert_eq!(*upper_open.as_ref(), 3.0);
    /// ```
    ///
    /// ## Use in Interval Difference Operations
    ///
    /// ```rust
    /// use grid1d::bounds::*;
    ///
    /// // When computing A \ B, if B has lower bound [5, we need upper bound )5
    /// let b_lower = LowerBoundRuntime::new_closed(5.0);
    ///
    /// // Convert and flip for the difference result
    /// let result_upper: UpperBoundRuntime<f64> = b_lower
    ///     .flip_bound_side()   // Lower -> Upper
    ///     .flip_bound_type();   // Closed -> Open
    ///
    /// assert!(result_upper.is_open_variant());
    /// assert_eq!(*result_upper.as_ref(), 5.0);
    /// // This creates the upper bound )5 from the lower bound [5
    /// ```
    ///
    /// # Performance
    ///
    /// - **Time Complexity**: O(1) - just moves the underlying value
    /// - **Memory**: Consumes `self`, no allocation or cloning required
    /// - **Inlining**: Marked `#[inline(always)]` for zero-cost abstraction
    ///
    /// # See Also
    ///
    /// - [`flip_bound_type`](Self::flip_bound_type) - Change Open ↔ Closed
    /// - [`new_open`](Self::new_open) - Create an open bound
    /// - [`new_closed`](Self::new_closed) - Create a closed bound
    #[inline(always)]
    pub fn flip_bound_side(self) -> IntervalBoundRuntime<RealType, Side::Opposite> {
        match self {
            Self::Open(bound) => IntervalBoundRuntime::Open(IntervalBound::new(bound.into_inner())),
            Self::Closed(bound) => {
                IntervalBoundRuntime::Closed(IntervalBound::new(bound.into_inner()))
            }
        }
    }

    /// Converts a bound to the opposite side AND flips the open/closed type.
    ///
    /// This is a convenience function that combines [`flip_bound_side`](Self::flip_bound_side)
    /// and [`flip_bound_type`](Self::flip_bound_type) in a single operation, performing
    /// both transformations atomically:
    /// - Changes `Lower` ↔ `Upper` (side transformation)
    /// - Changes `Open` ↔ `Closed` (type transformation)
    ///
    /// This is equivalent to calling `self.flip_bound_side().flip_bound_type()` but
    /// more concise and expresses the intent clearly.
    ///
    /// # Type Transformations
    ///
    /// | Input Type | Output Type |
    /// |------------|-------------|
    /// | `LowerBoundRuntime::Open(a)` | `UpperBoundRuntime::Closed(a)` |
    /// | `LowerBoundRuntime::Closed(a)` | `UpperBoundRuntime::Open(a)` |
    /// | `UpperBoundRuntime::Open(b)` | `LowerBoundRuntime::Closed(b)` |
    /// | `UpperBoundRuntime::Closed(b)` | `LowerBoundRuntime::Open(b)` |
    ///
    /// # Mathematical Motivation
    ///
    /// In interval arithmetic, particularly in set difference operations, when you
    /// exclude a portion of an interval, the complementary bound needs both transformations:
    ///
    /// - If the excluded interval has a **closed** lower bound `[a`, the remaining part
    ///   needs an **open** upper bound `)a` to avoid including the boundary
    /// - If the excluded interval has an **open** lower bound `(a`, the remaining part
    ///   needs a **closed** upper bound `]a` to include what was excluded
    ///
    /// This is precisely the transformation this function performs.
    ///
    /// # Use Cases
    ///
    /// This function is essential for interval difference operations (`A \ B`) where
    /// bounds from the subtracted interval need to become complementary bounds in the result.
    ///
    /// # Examples
    ///
    /// ## Basic Transformations
    ///
    /// ```rust
    /// use grid1d::bounds::*;
    ///
    /// // Lower Closed -> Upper Open
    /// let lower_closed = LowerBoundRuntime::new_closed(5.0);
    /// let upper_open: UpperBoundRuntime<f64> = lower_closed.flip_bound_side_and_type();
    /// assert!(upper_open.is_open_variant());
    /// assert_eq!(*upper_open.as_ref(), 5.0);
    ///
    /// // Lower Open -> Upper Closed
    /// let lower_open = LowerBoundRuntime::new_open(10.0);
    /// let upper_closed: UpperBoundRuntime<f64> = lower_open.flip_bound_side_and_type();
    /// assert!(upper_closed.is_closed_variant());
    /// assert_eq!(*upper_closed.as_ref(), 10.0);
    ///
    /// // Upper Closed -> Lower Open
    /// let upper_closed = UpperBoundRuntime::new_closed(15.0);
    /// let lower_open: LowerBoundRuntime<f64> = upper_closed.flip_bound_side_and_type();
    /// assert!(lower_open.is_open_variant());
    /// assert_eq!(*lower_open.as_ref(), 15.0);
    ///
    /// // Upper Open -> Lower Closed
    /// let upper_open = UpperBoundRuntime::new_open(20.0);
    /// let lower_closed: LowerBoundRuntime<f64> = upper_open.flip_bound_side_and_type();
    /// assert!(lower_closed.is_closed_variant());
    /// assert_eq!(*lower_closed.as_ref(), 20.0);
    /// ```
    ///
    /// ## Interval Difference: Why This Matters
    ///
    /// ```rust
    /// use grid1d::bounds::*;
    ///
    /// // Computing A \ B where:
    /// // A = [0, 10]
    /// // B = [5, 7]
    /// // Result should be [0, 5) ∪ (7, 10]
    ///
    /// // B's lower bound is [5 (closed), so left remainder needs )5 (open upper)
    /// let b_lower = LowerBoundRuntime::new_closed(5.0);
    /// let left_upper: UpperBoundRuntime<f64> = b_lower.flip_bound_side_and_type();
    /// assert!(left_upper.is_open_variant());
    /// assert_eq!(*left_upper.as_ref(), 5.0);
    /// // This creates [0, 5) - the left part of A \ B
    ///
    /// // B's upper bound is ]7 (closed), so right remainder needs (7 (open lower)
    /// let b_upper = UpperBoundRuntime::new_closed(7.0);
    /// let right_lower: LowerBoundRuntime<f64> = b_upper.flip_bound_side_and_type();
    /// assert!(right_lower.is_open_variant());
    /// assert_eq!(*right_lower.as_ref(), 7.0);
    /// // This creates (7, 10] - the right part of A \ B
    /// ```
    ///
    /// ## Comparison with Sequential Operations
    ///
    /// ```rust
    /// use grid1d::bounds::*;
    ///
    /// let bound = LowerBoundRuntime::new_closed(3.0);
    ///
    /// // These are equivalent:
    /// let result1: UpperBoundRuntime<f64> = bound.clone().flip_bound_side_and_type();
    /// let result2: UpperBoundRuntime<f64> = bound.flip_bound_side().flip_bound_type();
    ///
    /// assert_eq!(result1.is_open_variant(), result2.is_open_variant());
    /// assert_eq!(*result1.as_ref(), *result2.as_ref());
    /// ```
    ///
    /// ## Involutive Property
    ///
    /// Applying this transformation twice returns to the original state (modulo the value):
    ///
    /// ```rust
    /// use grid1d::bounds::*;
    ///
    /// let original = LowerBoundRuntime::new_closed(7.5);
    /// let was_closed = original.is_closed_variant();
    /// let original_value = *original.as_ref();
    ///
    /// // Apply transformation twice
    /// let transformed: UpperBoundRuntime<f64> = original.flip_bound_side_and_type();
    /// let back: LowerBoundRuntime<f64> = transformed.flip_bound_side_and_type();
    ///
    /// // Returns to original type and value
    /// assert_eq!(back.is_closed_variant(), was_closed);
    /// assert_eq!(*back.as_ref(), original_value);
    /// ```
    ///
    /// # Performance
    ///
    /// - **Time Complexity**: O(1) - single pattern match and value move
    /// - **Memory**: Consumes `self`, no allocation or cloning required
    /// - **Inlining**: Marked `#[inline(always)]` for zero-cost abstraction
    /// - **Optimization**: Compiler can optimize to a simple field swap
    ///
    /// # See Also
    ///
    /// - [`flip_bound_side`](Self::flip_bound_side) - Change only the side (Lower ↔ Upper)
    /// - [`flip_bound_type`](Self::flip_bound_type) - Change only the type (Open ↔ Closed)
    /// - [`new_open`](Self::new_open) - Create an open bound
    /// - [`new_closed`](Self::new_closed) - Create a closed bound
    #[inline(always)]
    pub fn flip_bound_side_and_type(self) -> IntervalBoundRuntime<RealType, Side::Opposite> {
        match self {
            Self::Open(bound) => {
                IntervalBoundRuntime::Closed(IntervalBound::new(bound.into_inner()))
            }
            Self::Closed(bound) => {
                IntervalBoundRuntime::Open(IntervalBound::new(bound.into_inner()))
            }
        }
    }
}

/// Runtime-determined lower bound that can be either open or closed.
///
/// [`LowerBoundRuntime<RealType>`] is a convenient type alias for [`IntervalBoundRuntime<RealType, Lower>`]
/// that specifically represents lower boundaries where the open/closed nature is determined at runtime.
/// This type is essential for dynamic interval construction, user configuration, serialization,
/// and algorithms that work with varying lower boundary semantics.
///
/// ## Type Definition
///
/// ```rust
/// use grid1d::bounds::*;
///
/// // These are equivalent:
/// type LowerBoundRuntime<T> = IntervalBoundRuntime<T, Lower>;
/// type ExplicitForm<T> = IntervalBoundRuntime<T, Lower>;
/// ```
///
/// ## Mathematical Properties
///
/// Lower bounds define minimum constraints for intervals and domains with runtime-determined inclusion:
///
/// | Variant | Mathematical Constraint | Notation | Value at Boundary |
/// |---------|------------------------|----------|-------------------|
/// | `Open(LowerBoundOpen)`   | `x > lower_value` | `(a` | **Excluded** |
/// | `Closed(LowerBoundClosed)` | `x ≥ lower_value` | `[a` | **Included** |
///
/// ## When to Use Lower Bound Runtime
///
/// ### ✅ **Recommended Use Cases**
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// // ✅ GOOD: User input with configuration
/// fn create_constraint_from_user_input(min_value: f64, inclusive: bool) -> LowerBoundRuntime<f64> {
///     if inclusive {
///         IntervalBoundRuntime::Closed(LowerBoundClosed::new(min_value))
///     } else {
///         IntervalBoundRuntime::Open(LowerBoundOpen::new(min_value))
///     }
/// }
///
/// // ✅ GOOD: Configuration-driven interval construction
/// struct DomainConfig {
///     start: f64,
///     include_start: bool,
///     end: f64,
///     include_end: bool,
/// }
///
/// fn build_dynamic_domain(config: DomainConfig) -> (LowerBoundRuntime<f64>, UpperBoundRuntime<f64>) {
///     let lower = create_constraint_from_user_input(config.start, config.include_start);
///     let upper = if config.include_end {
///         IntervalBoundRuntime::Closed(UpperBoundClosed::new(config.end))
///     } else {
///         IntervalBoundRuntime::Open(UpperBoundOpen::new(config.end))
///     };
///     (lower, upper)
/// }
/// ```
///
/// ### ❌ **Avoid When**
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// // ❌ AVOID: When boundary type is known at compile time
/// fn bad_usage() -> LowerBoundRuntime<f64> {
///     // This should just be LowerBoundClosed::new(0.0)
///     IntervalBoundRuntime::Closed(LowerBoundClosed::new(0.0))
/// }
///
/// // ✅ BETTER: Use compile-time types when semantics are fixed
/// fn good_usage() -> LowerBoundClosed<f64> {
///     LowerBoundClosed::new(0.0)
/// }
/// ```
///
/// ## Core Operations
///
/// ### Constraint Testing
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// let closed_lower = IntervalBoundRuntime::Closed(LowerBoundClosed::new(5.0));
/// let open_lower = IntervalBoundRuntime::Open(LowerBoundOpen::new(5.0));
///
/// // Test the boundary value itself
/// assert!(closed_lower.value_within_bound(&5.0));  // 5 ≥ 5 → true
/// assert!(!open_lower.value_within_bound(&5.0));   // 5 > 5 → false
///
/// // Test values above the boundary
/// assert!(closed_lower.value_within_bound(&5.1));  // 5.1 ≥ 5 → true
/// assert!(open_lower.value_within_bound(&5.1));    // 5.1 > 5 → true
///
/// // Test values below the boundary
/// assert!(!closed_lower.value_within_bound(&4.9)); // 4.9 ≥ 5 → false
/// assert!(!open_lower.value_within_bound(&4.9));   // 4.9 > 5 → false
/// ```
///
/// ### Type Information Queries
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// let closed_bound = IntervalBoundRuntime::Closed(LowerBoundClosed::new(1.0));
/// let open_bound = IntervalBoundRuntime::Open(LowerBoundOpen::new(1.0));
///
/// // Query boundary inclusion behavior
/// assert!(closed_bound.is_closed());
/// assert!(!closed_bound.is_open());
/// assert!(closed_bound.includes_boundary());
///
/// assert!(!open_bound.is_closed());
/// assert!(open_bound.is_open());
/// assert!(!open_bound.includes_boundary());
///
/// // All lower bounds share this property
/// assert!(closed_bound.is_lower_bound());
/// assert!(open_bound.is_lower_bound());
/// assert!(!closed_bound.is_upper_bound());
/// assert!(!open_bound.is_upper_bound());
/// ```
///
/// ### Value Access
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::{New, IntoInner};
///
/// let bound = IntervalBoundRuntime::Closed(LowerBoundClosed::new(3.14));
///
/// // Access the underlying value by reference
/// let value_ref: &f64 = bound.as_ref();
/// assert_eq!(*value_ref, 3.14);
///
/// // Extract the value (consuming the bound)
/// let value: f64 = bound.into_inner();
/// assert_eq!(value, 3.14);
/// ```
///
/// ## Mathematical Ordering
///
/// Lower bounds maintain mathematically correct ordering where "tighter" constraints are considered smaller:
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// let closed_lower = IntervalBoundRuntime::Closed(LowerBoundClosed::new(5.0));  // [5
/// let open_lower = IntervalBoundRuntime::Open(LowerBoundOpen::new(5.0));        // (5
///
/// // Closed bound is "tighter" (more restrictive) than open bound
/// assert!(closed_lower < open_lower);  // [5 < (5
///
/// // This ordering ensures correct interval operations:
/// // intersection([5, 10]) ∩ (5, 10]) = (5, 10] (the looser constraint wins)
/// ```
///
/// ## Practical Usage Patterns
///
/// ### Dynamic Interval Construction
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// struct IntervalBuilder {
///     lower_value: f64,
///     lower_inclusive: bool,
/// }
///
/// impl IntervalBuilder {
///     fn build_lower_bound(&self) -> LowerBoundRuntime<f64> {
///         if self.lower_inclusive {
///             IntervalBoundRuntime::Closed(LowerBoundClosed::new(self.lower_value))
///         } else {
///             IntervalBoundRuntime::Open(LowerBoundOpen::new(self.lower_value))
///         }
///     }
///     
///     fn set_constraint(&mut self, value: f64, inclusive: bool) {
///         self.lower_value = value;
///         self.lower_inclusive = inclusive;
///     }
/// }
///
/// let mut builder = IntervalBuilder { lower_value: 0.0, lower_inclusive: true };
/// let bound1 = builder.build_lower_bound(); // [0
///
/// builder.set_constraint(1.0, false);
/// let bound2 = builder.build_lower_bound(); // (1
///
/// assert!(bound1.is_closed());
/// assert!(bound2.is_open());
/// ```
///
/// ### Configuration-Driven Systems
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
/// use serde::{Deserialize, Serialize};
///
/// #[derive(Serialize, Deserialize)]
/// struct ConstraintConfig {
///     minimum_value: f64,
///     include_minimum: bool,
/// }
///
/// impl ConstraintConfig {
///     fn to_lower_bound(&self) -> LowerBoundRuntime<f64> {
///         if self.include_minimum {
///             IntervalBoundRuntime::Closed(LowerBoundClosed::new(self.minimum_value))
///         } else {
///             IntervalBoundRuntime::Open(LowerBoundOpen::new(self.minimum_value))
///         }
///     }
/// }
///
/// // Load from configuration file or user input
/// let config = ConstraintConfig {
///     minimum_value: 0.0,
///     include_minimum: true,
/// };
///
/// let lower_bound = config.to_lower_bound();
/// assert!(lower_bound.value_within_bound(&0.0)); // Includes minimum
/// ```
///
/// ### Collection Processing
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// // Store different lower bound types in the same collection
/// let constraints: Vec<LowerBoundRuntime<f64>> = vec![
///     IntervalBoundRuntime::Closed(LowerBoundClosed::new(0.0)),   // [0
///     IntervalBoundRuntime::Open(LowerBoundOpen::new(1.0)),       // (1
///     IntervalBoundRuntime::Closed(LowerBoundClosed::new(2.0)),   // [2
///     IntervalBoundRuntime::Open(LowerBoundOpen::new(3.0)),       // (3
/// ];
///
/// // Process them uniformly
/// for (i, constraint) in constraints.iter().enumerate() {
///     let inclusion = if constraint.is_closed() { "≥" } else { ">" };
///     println!("Constraint {}: x {} {}", i, inclusion, constraint.as_ref());
/// }
/// // Output:
/// // Constraint 0: x ≥ 0
/// // Constraint 1: x > 1
/// // Constraint 2: x ≥ 2
/// // Constraint 3: x > 3
///
/// // Find all constraints satisfied by a test value
/// let test_value = 2.0;
/// let satisfied: Vec<usize> = constraints.iter()
///     .enumerate()
///     .filter(|(_, constraint)| constraint.value_within_bound(&test_value))
///     .map(|(i, _)| i)
///     .collect();
///
/// assert_eq!(satisfied, vec![0, 1, 2]); // Only [0, (1 and [2 include 2.0
/// ```
///
/// ## Serialization Support
///
/// Runtime bounds have natural serialization support, making them ideal for persistent configuration:
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
/// use serde_json;
///
/// let bounds = vec![
///     IntervalBoundRuntime::Closed(LowerBoundClosed::new(0.0)),
///     IntervalBoundRuntime::Open(LowerBoundOpen::new(1.5)),
/// ];
///
/// // Serialize to JSON
/// let json = serde_json::to_string_pretty(&bounds).unwrap();
/// println!("Serialized lower bounds:\n{}", json);
///
/// // Deserialize back
/// let deserialized: Vec<LowerBoundRuntime<f64>> =
///     serde_json::from_str(&json).unwrap();
///
/// // Verify round-trip correctness
/// for (original, restored) in bounds.iter().zip(deserialized.iter()) {
///     assert_eq!(original.as_ref(), restored.as_ref());
///     assert_eq!(original.is_closed(), restored.is_closed());
///     assert_eq!(original.value_within_bound(&0.0), restored.value_within_bound(&0.0));
/// }
/// ```
///
/// ## Generic Algorithm Support
///
/// ```rust
/// use grid1d::bounds::*;
/// use num_valid::RealScalar;
/// use try_create::New;
///
/// fn find_minimum_satisfying_constraint<T: RealScalar + Copy>(
///     candidates: &[T],
///     constraint: &LowerBoundRuntime<T>
/// ) -> Option<T>
/// where
///     T: PartialOrd,
/// {
///     candidates.iter()
///         .filter(|&&value| constraint.value_within_bound(&value))
///         .min_by(|a, b| a.partial_cmp(b).unwrap())
///         .copied()
/// }
///
/// let values = vec![0.0, 0.5, 1.0, 1.5, 2.0];
///
/// let closed_constraint = IntervalBoundRuntime::Closed(LowerBoundClosed::new(1.0));
/// let open_constraint = IntervalBoundRuntime::Open(LowerBoundOpen::new(1.0));
///
/// assert_eq!(find_minimum_satisfying_constraint(&values, &closed_constraint), Some(1.0)); // x ≥ 1
/// assert_eq!(find_minimum_satisfying_constraint(&values, &open_constraint), Some(1.5));   // x > 1
/// ```
///
/// ## Performance vs Compile-Time Bounds
///
/// ### Runtime Performance
/// - **Memory**: Same as underlying bound plus small enum discriminant (~9 bytes for f64)
/// - **Operations**: Small pattern matching overhead (~1-2 CPU cycles)
/// - **Constraint Testing**: O(1) with inlined comparisons
///
/// ### Comparison with Compile-Time Bounds
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// // Runtime bound: small branching cost
/// fn check_runtime_constraint(bound: &LowerBoundRuntime<f64>, value: f64) -> bool {
///     match bound {  // Runtime dispatch (small cost)
///         IntervalBoundRuntime::Open(b) => value > *b.as_ref(),
///         IntervalBoundRuntime::Closed(b) => value >= *b.as_ref(),
///     }
/// }
///
/// // Compile-time bound: zero overhead
/// fn check_compile_time_constraint<T: BoundType>(bound: &LowerBound<f64, T>, value: f64) -> bool {
///     if T::is_open() {
///         value > *bound.as_ref()  // Direct comparison, no branching
///     } else {
///         value >= *bound.as_ref() // Direct comparison, no branching
///     }
/// }
/// ```
///
/// ## Integration with Grid1D
///
/// Runtime bounds integrate seamlessly with interval construction:
///
/// ```rust
/// use grid1d::{bounds::*, intervals::*};
/// use try_create::{IntoInner, New};
///
/// fn create_interval_from_runtime_bounds(
///     lower: LowerBoundRuntime<f64>,
///     upper: UpperBoundRuntime<f64>
/// ) -> Box<dyn std::fmt::Debug> {
///     match (lower, upper) {
///         (IntervalBoundRuntime::Closed(l), IntervalBoundRuntime::Closed(u)) => {
///             Box::new(IntervalClosed::new(l.into_inner(), u.into_inner()))
///         }
///         (IntervalBoundRuntime::Open(l), IntervalBoundRuntime::Open(u)) => {
///             Box::new(IntervalOpen::new(l.into_inner(), u.into_inner()))
///         }
///         (IntervalBoundRuntime::Closed(l), IntervalBoundRuntime::Open(u)) => {
///             Box::new(IntervalLowerClosedUpperOpen::new(l.into_inner(), u.into_inner()))
///         }
///         (IntervalBoundRuntime::Open(l), IntervalBoundRuntime::Closed(u)) => {
///             Box::new(IntervalLowerOpenUpperClosed::new(l.into_inner(), u.into_inner()))
///         }
///     }
/// }
///
/// // Example usage
/// let lower = IntervalBoundRuntime::Closed(LowerBoundClosed::new(0.0));
/// let upper = IntervalBoundRuntime::Open(UpperBoundOpen::new(1.0));
/// let interval = create_interval_from_runtime_bounds(lower, upper); // Creates [0, 1)
/// ```
///
/// ## Conversion Patterns
///
/// ### Extract Specific Bound Types
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// fn extract_closed_lower_bound(bound: LowerBoundRuntime<f64>) -> Option<LowerBoundClosed<f64>> {
///     match bound {
///         IntervalBoundRuntime::Closed(closed_bound) => Some(closed_bound),
///         IntervalBoundRuntime::Open(_) => None,
///     }
/// }
///
/// let runtime_bound = IntervalBoundRuntime::Closed(LowerBoundClosed::new(5.0));
/// let compile_time_bound = extract_closed_lower_bound(runtime_bound);
/// assert!(compile_time_bound.is_some());
/// ```
///
/// ### Convert to Compile-Time When Possible
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// // Pattern: Use runtime bounds at API boundaries, compile-time internally
/// fn process_constraint(constraint: LowerBoundRuntime<f64>) -> String {
///     match constraint {
///         IntervalBoundRuntime::Closed(closed_bound) => {
///             // Delegate to compile-time optimized function
///             process_closed_constraint(closed_bound)
///         }
///         IntervalBoundRuntime::Open(open_bound) => {
///             // Delegate to compile-time optimized function
///             process_open_constraint(open_bound)
///         }
///     }
/// }
///
/// fn process_closed_constraint(bound: LowerBoundClosed<f64>) -> String {
///     format!("Inclusive lower bound: x ≥ {}", bound.as_ref())
/// }
///
/// fn process_open_constraint(bound: LowerBoundOpen<f64>) -> String {
///     format!("Exclusive lower bound: x > {}", bound.as_ref())
/// }
/// ```
///
/// ## Best Practices
///
/// ### 1. **Use at API Boundaries**
/// ```rust
/// use grid1d::bounds::*;
///
/// // ✅ GOOD: Runtime bounds for user-facing APIs
/// pub fn create_domain_from_config(
///     lower: LowerBoundRuntime<f64>,
///     upper: UpperBoundRuntime<f64>
/// ) -> Result<Box<dyn std::fmt::Debug>, String> {
///     // Implementation...
///     Ok(Box::new(format!("Domain created")))
/// }
///
/// // ✅ GOOD: Compile-time bounds for internal algorithms
/// fn optimize_grid_spacing<T: BoundType>(
///     lower: &LowerBound<f64, T>
/// ) -> f64 {
///     // High-performance internal computation
///     *lower.as_ref() * 1.1
/// }
/// ```
///
/// ### 2. **Pattern Match Early**
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// // ✅ GOOD: Extract compile-time types early
/// fn efficient_processing(constraint: LowerBoundRuntime<f64>) -> f64 {
///     match constraint {
///         IntervalBoundRuntime::Closed(bound) => {
///             // Use compile-time optimized path
///             bound.as_ref() * 1.0  // Zero overhead
///         }
///         IntervalBoundRuntime::Open(bound) => {
///             // Use compile-time optimized path
///             bound.as_ref() + f64::EPSILON  // Zero overhead
///         }
///     }
/// }
/// ```
///
/// ### 3. **Validate Early**
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// fn create_constraint_with_validation(
///     value: f64,
///     inclusive: bool
/// ) -> Result<LowerBoundRuntime<f64>, String> {
///     if !value.is_finite() {
///         return Err(format!("Constraint value must be finite, got: {}", value));
///     }
///     
///     let bound = if inclusive {
///         IntervalBoundRuntime::Closed(LowerBoundClosed::new(value))
///     } else {
///         IntervalBoundRuntime::Open(LowerBoundOpen::new(value))
///     };
///     
///     Ok(bound)
/// }
/// ```
///
/// ## Mathematical Guarantees
///
/// The [`LowerBoundRuntime`] type maintains mathematical correctness by:
///
/// - **Constraint Semantics**: Properly implements `x ≥ a` (closed) vs `x > a` (open)
/// - **Ordering Consistency**: Maintains mathematical ordering where `[a < (a`
/// - **Type Safety**: Prevents mixing with upper bounds at compile time
/// - **Value Preservation**: Maintains exact scalar values without precision loss
/// - **Conversion Safety**: Provides safe extraction of compile-time types
///
/// Use [`LowerBoundRuntime`] when you need the flexibility of runtime boundary semantics
/// while maintaining mathematical correctness and integration with the grid1d ecosystem.
/// For maximum performance when boundary types are known at compile time, prefer the
/// direct types [`LowerBoundClosed`] and [`LowerBoundOpen`].
pub type LowerBoundRuntime<RealType> = IntervalBoundRuntime<RealType, Lower>;

/// Runtime-determined upper bound that can be either open or closed.
///
/// [`UpperBoundRuntime<RealType>`] is a convenient type alias for [`IntervalBoundRuntime<RealType, Upper>`]
/// that specifically represents upper boundaries where the open/closed nature is determined at runtime.
/// This type is essential for dynamic interval construction, user configuration, serialization,
/// and algorithms that work with varying upper boundary semantics.
///
/// ## Type Definition
///
/// ```rust
/// use grid1d::bounds::*;
///
/// // These are equivalent:
/// type UpperBoundRuntime<T> = IntervalBoundRuntime<T, Upper>;
/// type ExplicitForm<T> = IntervalBoundRuntime<T, Upper>;
/// ```
///
/// ## Mathematical Properties
///
/// Upper bounds define maximum constraints for intervals and domains with runtime-determined inclusion:
///
/// | Variant | Mathematical Constraint | Notation | Value at Boundary |
/// |---------|------------------------|----------|-------------------|
/// | `Open(UpperBoundOpen)`   | `x < upper_value` | `b)` | **Excluded** |
/// | `Closed(UpperBoundClosed)` | `x ≤ upper_value` | `b]` | **Included** |
///
/// ## When to Use Upper Bound Runtime
///
/// ### ✅ **Recommended Use Cases**
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// // ✅ GOOD: User input with configuration
/// fn create_upper_constraint_from_user_input(max_value: f64, inclusive: bool) -> UpperBoundRuntime<f64> {
///     if inclusive {
///         IntervalBoundRuntime::Closed(UpperBoundClosed::new(max_value))
///     } else {
///         IntervalBoundRuntime::Open(UpperBoundOpen::new(max_value))
///     }
/// }
///
/// // ✅ GOOD: Configuration-driven interval construction
/// struct RangeConfig {
///     start: f64,
///     include_start: bool,
///     end: f64,
///     include_end: bool,
/// }
///
/// fn build_dynamic_range(config: RangeConfig) -> (LowerBoundRuntime<f64>, UpperBoundRuntime<f64>) {
///     let lower = if config.include_start {
///         IntervalBoundRuntime::Closed(LowerBoundClosed::new(config.start))
///     } else {
///         IntervalBoundRuntime::Open(LowerBoundOpen::new(config.start))
///     };
///     
///     let upper = create_upper_constraint_from_user_input(config.end, config.include_end);
///     (lower, upper)
/// }
/// ```
///
/// ### ❌ **Avoid When**
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// // ❌ AVOID: When boundary type is known at compile time
/// fn bad_usage() -> UpperBoundRuntime<f64> {
///     // This should just be UpperBoundOpen::new(10.0)
///     IntervalBoundRuntime::Open(UpperBoundOpen::new(10.0))
/// }
///
/// // ✅ BETTER: Use compile-time types when semantics are fixed
/// fn good_usage() -> UpperBoundOpen<f64> {
///     UpperBoundOpen::new(10.0)
/// }
/// ```
///
/// ## Core Operations
///
/// ### Constraint Testing
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// let closed_upper = IntervalBoundRuntime::Closed(UpperBoundClosed::new(10.0));
/// let open_upper = IntervalBoundRuntime::Open(UpperBoundOpen::new(10.0));
///
/// // Test the boundary value itself
/// assert!(closed_upper.value_within_bound(&10.0));  // 10 ≤ 10 → true
/// assert!(!open_upper.value_within_bound(&10.0));   // 10 < 10 → false
///
/// // Test values below the boundary
/// assert!(closed_upper.value_within_bound(&9.5));   // 9.5 ≤ 10 → true
/// assert!(open_upper.value_within_bound(&9.5));     // 9.5 < 10 → true
///
/// // Test values above the boundary
/// assert!(!closed_upper.value_within_bound(&10.5)); // 10.5 ≤ 10 → false
/// assert!(!open_upper.value_within_bound(&10.5));   // 10.5 < 10 → false
/// ```
///
/// ### Type Information Queries
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// let closed_bound = IntervalBoundRuntime::Closed(UpperBoundClosed::new(5.0));
/// let open_bound = IntervalBoundRuntime::Open(UpperBoundOpen::new(5.0));
///
/// // Query boundary inclusion behavior
/// assert!(closed_bound.is_closed());
/// assert!(!closed_bound.is_open());
/// assert!(closed_bound.includes_boundary());
///
/// assert!(!open_bound.is_closed());
/// assert!(open_bound.is_open());
/// assert!(!open_bound.includes_boundary());
///
/// // All upper bounds share this property
/// assert!(closed_bound.is_upper_bound());
/// assert!(open_bound.is_upper_bound());
/// assert!(!closed_bound.is_lower_bound());
/// assert!(!open_bound.is_lower_bound());
/// ```
///
/// ### Value Access
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::{New, IntoInner};
///
/// let bound = IntervalBoundRuntime::Closed(UpperBoundClosed::new(2.718));
///
/// // Access the underlying value by reference
/// let value_ref: &f64 = bound.as_ref();
/// assert_eq!(*value_ref, 2.718);
///
/// // Extract the value (consuming the bound)
/// let value: f64 = bound.into_inner();
/// assert_eq!(value, 2.718);
/// ```
///
/// ## Mathematical Ordering
///
/// Upper bounds maintain mathematically correct ordering where "tighter" constraints are considered smaller:
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// let open_upper = IntervalBoundRuntime::Open(UpperBoundOpen::new(10.0));        // 10)
/// let closed_upper = IntervalBoundRuntime::Closed(UpperBoundClosed::new(10.0));  // 10]
///
/// // Open bound is "tighter" (more restrictive) than closed bound
/// assert!(open_upper < closed_upper);  // 10) < 10]
///
/// // This ordering ensures correct interval operations:
/// // intersection([0, 10)) ∩ [0, 10]) = [0, 10) (the tighter constraint wins)
/// ```
///
/// ## Practical Usage Patterns
///
/// ### Dynamic Range Construction
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// struct RangeBuilder {
///     upper_value: f64,
///     upper_inclusive: bool,
/// }
///
/// impl RangeBuilder {
///     fn build_upper_bound(&self) -> UpperBoundRuntime<f64> {
///         if self.upper_inclusive {
///             IntervalBoundRuntime::Closed(UpperBoundClosed::new(self.upper_value))
///         } else {
///             IntervalBoundRuntime::Open(UpperBoundOpen::new(self.upper_value))
///         }
///     }
///     
///     fn set_maximum(&mut self, value: f64, inclusive: bool) {
///         self.upper_value = value;
///         self.upper_inclusive = inclusive;
///     }
/// }
///
/// let mut builder = RangeBuilder { upper_value: 100.0, upper_inclusive: true };
/// let bound1 = builder.build_upper_bound(); // 100]
///
/// builder.set_maximum(50.0, false);
/// let bound2 = builder.build_upper_bound(); // 50)
///
/// assert!(bound1.is_closed());
/// assert!(bound2.is_open());
/// ```
///
/// ### Configuration-Driven Systems
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
/// use serde::{Deserialize, Serialize};
///
/// #[derive(Serialize, Deserialize)]
/// struct MaximumConfig {
///     maximum_value: f64,
///     include_maximum: bool,
/// }
///
/// impl MaximumConfig {
///     fn to_upper_bound(&self) -> UpperBoundRuntime<f64> {
///         if self.include_maximum {
///             IntervalBoundRuntime::Closed(UpperBoundClosed::new(self.maximum_value))
///         } else {
///             IntervalBoundRuntime::Open(UpperBoundOpen::new(self.maximum_value))
///         }
///     }
/// }
///
/// // Load from configuration file or user input
/// let config = MaximumConfig {
///     maximum_value: 100.0,
///     include_maximum: false,
/// };
///
/// let upper_bound = config.to_upper_bound();
/// assert!(!upper_bound.value_within_bound(&100.0)); // Excludes maximum
/// assert!(upper_bound.value_within_bound(&99.9));   // Includes values below
/// ```
///
/// ### Collection Processing
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// // Store different upper bound types in the same collection
/// let constraints: Vec<UpperBoundRuntime<f64>> = vec![
///     IntervalBoundRuntime::Closed(UpperBoundClosed::new(10.0)),  // 10]
///     IntervalBoundRuntime::Open(UpperBoundOpen::new(20.0)),      // 20)
///     IntervalBoundRuntime::Closed(UpperBoundClosed::new(30.0)),  // 30]
///     IntervalBoundRuntime::Open(UpperBoundOpen::new(40.0)),      // 40)
/// ];
///
/// // Process them uniformly
/// for (i, constraint) in constraints.iter().enumerate() {
///     let inclusion = if constraint.is_closed() { "≤" } else { "<" };
///     println!("Constraint {}: x {} {}", i, inclusion, constraint.as_ref());
/// }
/// // Output:
/// // Constraint 0: x ≤ 10
/// // Constraint 1: x < 20
/// // Constraint 2: x ≤ 30
/// // Constraint 3: x < 40
///
/// // Find all constraints satisfied by a test value
/// let test_value = 20.0;
/// let satisfied: Vec<usize> = constraints.iter()
///     .enumerate()
///     .filter(|(_, constraint)| constraint.value_within_bound(&test_value))
///     .map(|(i, _)| i)
///     .collect();
///
/// assert_eq!(satisfied, vec![2, 3]); // Only 30] and 40) include 20.0
/// ```
///
/// ## Serialization Support
///
/// Runtime bounds have natural serialization support, making them ideal for persistent configuration:
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
/// use serde_json;
///
/// let bounds = vec![
///     IntervalBoundRuntime::Closed(UpperBoundClosed::new(100.0)),
///     IntervalBoundRuntime::Open(UpperBoundOpen::new(50.0)),
/// ];
///
/// // Serialize to JSON
/// let json = serde_json::to_string_pretty(&bounds).unwrap();
/// println!("Serialized upper bounds:\n{}", json);
///
/// // Deserialize back
/// let deserialized: Vec<UpperBoundRuntime<f64>> =
///     serde_json::from_str(&json).unwrap();
///
/// // Verify round-trip correctness
/// for (original, restored) in bounds.iter().zip(deserialized.iter()) {
///     assert_eq!(original.as_ref(), restored.as_ref());
///     assert_eq!(original.is_closed(), restored.is_closed());
///     assert_eq!(original.value_within_bound(&50.0), restored.value_within_bound(&50.0));
/// }
/// ```
///
/// ## Generic Algorithm Support
///
/// ```rust
/// use grid1d::bounds::*;
/// use num_valid::RealScalar;
/// use try_create::New;
///
/// fn find_maximum_satisfying_constraint<T: RealScalar + Copy>(
///     candidates: &[T],
///     constraint: &UpperBoundRuntime<T>
/// ) -> Option<T>
/// where
///     T: PartialOrd,
/// {
///     candidates.iter()
///         .filter(|&&value| constraint.value_within_bound(&value))
///         .max_by(|a, b| a.partial_cmp(b).unwrap())
///         .copied()
/// }
///
/// let values = vec![8.0, 9.0, 10.0, 11.0, 12.0];
///
/// let closed_constraint = IntervalBoundRuntime::Closed(UpperBoundClosed::new(10.0));
/// let open_constraint = IntervalBoundRuntime::Open(UpperBoundOpen::new(10.0));
///
/// assert_eq!(find_maximum_satisfying_constraint(&values, &closed_constraint), Some(10.0)); // x ≤ 10
/// assert_eq!(find_maximum_satisfying_constraint(&values, &open_constraint), Some(9.0));    // x < 10
/// ```
///
/// ## Performance vs Compile-Time Bounds
///
/// ### Runtime Performance
/// - **Memory**: Same as underlying bound plus small enum discriminant (~9 bytes for f64)
/// - **Operations**: Small pattern matching overhead (~1-2 CPU cycles)
/// - **Constraint Testing**: O(1) with inlined comparisons
///
/// ### Comparison with Compile-Time Bounds
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// // Runtime bound: small branching cost
/// fn check_runtime_constraint(bound: &UpperBoundRuntime<f64>, value: f64) -> bool {
///     match bound {  // Runtime dispatch (small cost)
///         IntervalBoundRuntime::Open(b) => value < *b.as_ref(),
///         IntervalBoundRuntime::Closed(b) => value <= *b.as_ref(),
///     }
/// }
///
/// // Compile-time bound: zero overhead
/// fn check_compile_time_constraint<T: BoundType>(bound: &UpperBound<f64, T>, value: f64) -> bool {
///     if T::is_open() {
///         value < *bound.as_ref()  // Direct comparison, no branching
///     } else {
///         value <= *bound.as_ref() // Direct comparison, no branching
///     }
/// }
/// ```
///
/// ## Integration with Grid1D
///
/// Runtime bounds integrate seamlessly with interval construction:
///
/// ```rust
/// use grid1d::{bounds::*, intervals::*};
/// use try_create::{IntoInner, New};
///
/// fn create_interval_from_runtime_bounds(
///     lower: LowerBoundRuntime<f64>,
///     upper: UpperBoundRuntime<f64>
/// ) -> Box<dyn std::fmt::Debug> {
///     match (lower, upper) {
///         (IntervalBoundRuntime::Closed(l), IntervalBoundRuntime::Closed(u)) => {
///             Box::new(IntervalClosed::new(l.into_inner(), u.into_inner()))
///         }
///         (IntervalBoundRuntime::Open(l), IntervalBoundRuntime::Open(u)) => {
///             Box::new(IntervalOpen::new(l.into_inner(), u.into_inner()))
///         }
///         (IntervalBoundRuntime::Closed(l), IntervalBoundRuntime::Open(u)) => {
///             Box::new(IntervalLowerClosedUpperOpen::new(l.into_inner(), u.into_inner()))
///         }
///         (IntervalBoundRuntime::Open(l), IntervalBoundRuntime::Closed(u)) => {
///             Box::new(IntervalLowerOpenUpperClosed::new(l.into_inner(), u.into_inner()))
///         }
///     }
/// }
///
/// // Example usage
/// let lower = IntervalBoundRuntime::Open(LowerBoundOpen::new(0.0));
/// let upper = IntervalBoundRuntime::Closed(UpperBoundClosed::new(1.0));
/// let interval = create_interval_from_runtime_bounds(lower, upper); // Creates (0, 1]
/// ```
///
/// ## Conversion Patterns
///
/// ### Extract Specific Bound Types
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// fn extract_open_upper_bound(bound: UpperBoundRuntime<f64>) -> Option<UpperBoundOpen<f64>> {
///     match bound {
///         IntervalBoundRuntime::Open(open_bound) => Some(open_bound),
///         IntervalBoundRuntime::Closed(_) => None,
///     }
/// }
///
/// let runtime_bound = IntervalBoundRuntime::Open(UpperBoundOpen::new(10.0));
/// let compile_time_bound = extract_open_upper_bound(runtime_bound);
/// assert!(compile_time_bound.is_some());
/// ```
///
/// ### Convert to Compile-Time When Possible
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// // Pattern: Use runtime bounds at API boundaries, compile-time internally
/// fn process_upper_constraint(constraint: UpperBoundRuntime<f64>) -> String {
///     match constraint {
///         IntervalBoundRuntime::Closed(closed_bound) => {
///             // Delegate to compile-time optimized function
///             process_closed_upper_constraint(closed_bound)
///         }
///         IntervalBoundRuntime::Open(open_bound) => {
///             // Delegate to compile-time optimized function
///             process_open_upper_constraint(open_bound)
///         }
///     }
/// }
///
/// fn process_closed_upper_constraint(bound: UpperBoundClosed<f64>) -> String {
///     format!("Inclusive upper bound: x ≤ {}", bound.as_ref())
/// }
///
/// fn process_open_upper_constraint(bound: UpperBoundOpen<f64>) -> String {
///     format!("Exclusive upper bound: x < {}", bound.as_ref())
/// }
/// ```
///
/// ## Best Practices
///
/// ### 1. **Use at API Boundaries**
/// ```rust
/// use grid1d::bounds::*;
///
/// // ✅ GOOD: Runtime bounds for user-facing APIs
/// pub fn create_range_from_config(
///     lower: LowerBoundRuntime<f64>,
///     upper: UpperBoundRuntime<f64>
/// ) -> Result<Box<dyn std::fmt::Debug>, String> {
///     // Implementation...
///     Ok(Box::new(format!("Range created")))
/// }
///
/// // ✅ GOOD: Compile-time bounds for internal algorithms
/// fn optimize_upper_limit<T: BoundType>(
///     upper: &UpperBound<f64, T>
/// ) -> f64 {
///     // High-performance internal computation
///     *upper.as_ref() * 0.9
/// }
/// ```
///
/// ### 2. **Pattern Match Early**
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// // ✅ GOOD: Extract compile-time types early
/// fn efficient_upper_processing(constraint: UpperBoundRuntime<f64>) -> f64 {
///     match constraint {
///         IntervalBoundRuntime::Closed(bound) => {
///             // Use compile-time optimized path
///             *bound.as_ref()  // Zero overhead
///         }
///         IntervalBoundRuntime::Open(bound) => {
///             // Use compile-time optimized path
///             *bound.as_ref() - f64::EPSILON  // Zero overhead
///         }
///     }
/// }
/// ```
///
/// ### 3. **Validate Early**
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// fn create_upper_constraint_with_validation(
///     value: f64,
///     inclusive: bool
/// ) -> Result<UpperBoundRuntime<f64>, String> {
///     if !value.is_finite() {
///         return Err(format!("Upper bound value must be finite, got: {}", value));
///     }
///     
///     let bound = if inclusive {
///         IntervalBoundRuntime::Closed(UpperBoundClosed::new(value))
///     } else {
///         IntervalBoundRuntime::Open(UpperBoundOpen::new(value))
///     };
///     
///     Ok(bound)
/// }
/// ```
///
/// ## Array-Like Range Construction
///
/// Upper bounds work naturally with Rust-style range construction:
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// // Pattern: Rust-style ranges with runtime bounds
/// fn create_rust_like_range(start: f64, end: f64, end_inclusive: bool) -> (LowerBoundClosed<f64>, UpperBoundRuntime<f64>) {
///     let lower = LowerBoundClosed::new(start);  // Always include start like [start, ...)
///     let upper = if end_inclusive {
///         IntervalBoundRuntime::Closed(UpperBoundClosed::new(end))  // [start, end]
///     } else {
///         IntervalBoundRuntime::Open(UpperBoundOpen::new(end))      // [start, end)
///     };
///     (lower, upper)
/// }
///
/// let (lower, upper_inclusive) = create_rust_like_range(0.0, 10.0, true);   // [0, 10]
/// let (_, upper_exclusive) = create_rust_like_range(0.0, 10.0, false);      // [0, 10)
///
/// assert!(upper_inclusive.value_within_bound(&10.0));  // 10 ≤ 10 → true
/// assert!(!upper_exclusive.value_within_bound(&10.0)); // 10 < 10 → false
/// ```
///
/// ## Mathematical Guarantees
///
/// The [`UpperBoundRuntime`] type maintains mathematical correctness by:
///
/// - **Constraint Semantics**: Properly implements `x ≤ b` (closed) vs `x < b` (open)
/// - **Ordering Consistency**: Maintains mathematical ordering where `b) < b]`
/// - **Type Safety**: Prevents mixing with lower bounds at compile time
/// - **Value Preservation**: Maintains exact scalar values without precision loss
/// - **Conversion Safety**: Provides safe extraction of compile-time types
///
/// Use [`UpperBoundRuntime`] when you need the flexibility of runtime boundary semantics
/// while maintaining mathematical correctness and integration with the grid1d ecosystem.
/// For maximum performance when boundary types are known at compile time, prefer the
/// direct types [`UpperBoundClosed`] and [`UpperBoundOpen`].
pub type UpperBoundRuntime<RealType> = IntervalBoundRuntime<RealType, Upper>;

impl<RealType: RealScalar, Side: BoundSide> BoundTypeChecks
    for IntervalBoundRuntime<RealType, Side>
{
    #[inline(always)]
    fn includes_boundary(&self) -> bool {
        matches!(self, IntervalBoundRuntime::Closed(_))
    }
}

impl<RealType: RealScalar> BoundSideChecks for LowerBoundRuntime<RealType> {
    #[inline(always)]
    fn is_upper_bound(&self) -> bool {
        false
    }
}

impl<RealType: RealScalar> BoundSideChecks for UpperBoundRuntime<RealType> {
    #[inline(always)]
    fn is_upper_bound(&self) -> bool {
        true
    }
}

impl<RealType: RealScalar, Side: BoundSide> ValueWithinBound
    for IntervalBoundRuntime<RealType, Side>
where
    IntervalBound<RealType, Side, Open>: ValueWithinBound<RealType = RealType>,
    IntervalBound<RealType, Side, Closed>: ValueWithinBound<RealType = RealType>,
{
    type RealType = RealType;

    fn value_within_bound(&self, value: &RealType) -> bool {
        match self {
            IntervalBoundRuntime::Open(bound) => bound.value_within_bound(value),
            IntervalBoundRuntime::Closed(bound) => bound.value_within_bound(value),
        }
    }
}

impl<RealType: RealScalar, Side: BoundSide> AsRef<RealType>
    for IntervalBoundRuntime<RealType, Side>
{
    fn as_ref(&self) -> &RealType {
        match self {
            IntervalBoundRuntime::Open(bound) => bound.as_ref(),
            IntervalBoundRuntime::Closed(bound) => bound.as_ref(),
        }
    }
}

impl<RealType: RealScalar, Side: BoundSide> IntoInner for IntervalBoundRuntime<RealType, Side> {
    type InnerType = RealType;

    fn into_inner(self) -> RealType {
        match self {
            IntervalBoundRuntime::Open(bound) => bound.into_inner(),
            IntervalBoundRuntime::Closed(bound) => bound.into_inner(),
        }
    }
}

impl<RealType: RealScalar, Side: BoundSide> BoundChecks for IntervalBoundRuntime<RealType, Side> where
    Self: BoundSideChecks
{
}

#[duplicate_item(
    Type;
    [Open];
    [Closed];
)]
impl<RealType: RealScalar, Side: BoundSide> From<IntervalBound<RealType, Side, Type>>
    for IntervalBoundRuntime<RealType, Side>
{
    fn from(bound: IntervalBound<RealType, Side, Type>) -> Self {
        IntervalBoundRuntime::Type(bound)
    }
}

impl<RealType: RealScalar> PartialOrd for LowerBoundRuntime<RealType> {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        if self.as_ref() == other.as_ref() {
            // Same value, closed is "less than" open
            Some(match (self, other) {
                (LowerBoundRuntime::Closed(_), LowerBoundRuntime::Open(_)) => Ordering::Less,
                (LowerBoundRuntime::Open(_), LowerBoundRuntime::Closed(_)) => Ordering::Greater,
                _ => Ordering::Equal,
            })
        } else {
            // different values, compare normally
            self.as_ref().partial_cmp(other.as_ref())
        }
    }
}

impl<RealType: RealScalar> PartialOrd for UpperBoundRuntime<RealType> {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        if self.as_ref() == other.as_ref() {
            // Same value, closed is "less than" open
            Some(match (self, other) {
                (UpperBoundRuntime::Closed(_), UpperBoundRuntime::Open(_)) => Ordering::Greater,
                (UpperBoundRuntime::Open(_), UpperBoundRuntime::Closed(_)) => Ordering::Less,
                _ => Ordering::Equal,
            })
        } else {
            // different values, compare normally
            self.as_ref().partial_cmp(other.as_ref())
        }
    }
}
//------------------------------------------------------------------------------------------------

//------------------------------------------------------------------------------------------------
/// Compile-time boundary inclusion semantics for interval bounds.
///
/// [`BoundType`] is a marker trait that encodes whether a boundary includes or excludes
/// its boundary value at the type level. This enables compile-time verification of boundary
/// semantics and zero-cost abstractions for interval arithmetic operations.
///
/// ## Design Philosophy
///
/// ### Compile-Time Type Safety
///
/// Unlike runtime boundary checks, [`BoundType`] moves boundary inclusion semantics into
/// the type system, providing these advantages:
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// // Type system prevents mixing incompatible boundary types
/// let closed_bound = LowerBoundClosed::new(0.0);  // Type: LowerBound<f64, Closed>
/// let open_bound = LowerBoundOpen::new(0.0);      // Type: LowerBound<f64, Open>
///
/// // This would be a compile error:
/// // assert_eq!(closed_bound, open_bound); // ← Cannot compare different BoundTypes
/// ```
///
/// ### Zero-Cost Abstractions
///
/// All methods compile to static constants, providing zero runtime overhead:
///
/// ```rust
/// use grid1d::bounds::*;
///
/// fn check_boundary_inclusion<T: BoundType>() -> &'static str {
///     if T::is_closed() {
///         "Boundary included"  // Optimized to constant at compile time
///     } else {
///         "Boundary excluded"  // Optimized to constant at compile time
///     }
/// }
///
/// assert_eq!(check_boundary_inclusion::<Closed>(), "Boundary included");
/// assert_eq!(check_boundary_inclusion::<Open>(), "Boundary excluded");
/// ```
///
/// ## Mathematical Semantics
///
/// ### Closed Boundaries ([`Closed`])
///
/// Closed boundaries include the boundary value in their constraint:
///
/// | Bound Type | Mathematical Constraint | Notation | Boundary Value |
/// |------------|------------------------|----------|----------------|
/// | `LowerBound<T, Closed>` | `x ≥ bound_value` | `[a` | **Included** |
/// | `UpperBound<T, Closed>` | `x ≤ bound_value` | `b]` | **Included** |
///
/// ### Open Boundaries ([`Open`])
///
/// Open boundaries exclude the boundary value from their constraint:
///
/// | Bound Type | Mathematical Constraint | Notation | Boundary Value |
/// |------------|------------------------|----------|----------------|
/// | `LowerBound<T, Open>` | `x > bound_value` | `(a` | **Excluded** |
/// | `UpperBound<T, Open>` | `x < bound_value` | `b)` | **Excluded** |
///
/// ## Core Methods
///
/// ### [`BoundType::includes_boundary()`]
///
/// The fundamental function that determines boundary inclusion:
///
/// ```rust
/// use grid1d::bounds::*;
///
/// let closed_includes = Closed::includes_boundary(); // true
/// let open_includes = Open::includes_boundary();     // false
///
/// assert!(closed_includes);
/// assert!(!open_includes);
/// ```
///
/// ### [`BoundType::is_open()`] and [`BoundType::is_closed()`]
///
/// Convenience methods that provide semantic clarity:
///
/// ```rust
/// use grid1d::bounds::*;
///
/// // Clear semantic meaning
/// assert!(Closed::is_closed());
/// assert!(!Closed::is_open());
/// assert!(Open::is_open());
/// assert!(!Open::is_closed());
///
/// // These compile to constants and have zero runtime cost
/// ```
///
/// ## Generic Programming Applications
///
/// ### Type-Parameterized Algorithms
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// fn create_strict_bound<T: BoundType>(value: f64) -> LowerBound<f64, T> {
///     LowerBound::new(value)
/// }
///
/// // Create different bound types from the same function
/// let closed: LowerBoundClosed<f64> = create_strict_bound::<Closed>(5.0);
/// let open: LowerBoundOpen<f64> = create_strict_bound::<Open>(5.0);
///
/// assert!(closed.is_closed());
/// assert!(open.is_open());
/// ```
///
/// ### Compile-Time Interval Construction
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// fn create_interval<LowerType: BoundType, UpperType: BoundType>(
///     lower: f64,
///     upper: f64
/// ) -> (LowerBound<f64, LowerType>, UpperBound<f64, UpperType>) {
///     (LowerBound::new(lower), UpperBound::new(upper))
/// }
///
/// // Different interval types from the same constructor
/// let (l, u) = create_interval::<Closed, Open>(0.0, 1.0);  // [0, 1)
/// let (l2, u2) = create_interval::<Open, Closed>(0.0, 1.0); // (0, 1]
/// ```
///
/// ### Constraint Optimization
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// fn optimize_constraint<T: BoundType>(bound: &LowerBound<f64, T>, value: f64) -> bool {
///     // Compile-time optimization based on boundary type
///     if T::is_open() {
///         value > *bound.as_ref()  // Direct comparison, no branching
///     } else {
///         value >= *bound.as_ref() // Direct comparison, no branching
///     }
/// }
///
/// let closed_bound = LowerBoundClosed::new(5.0);
/// let open_bound = LowerBoundOpen::new(5.0);
///
/// assert!(optimize_constraint(&closed_bound, 5.0));   // 5 >= 5 → true
/// assert!(!optimize_constraint(&open_bound, 5.0));    // 5 > 5 → false
/// ```
///
/// ## Trait Bounds and Constraints
///
/// ### Required Trait Implementations
///
/// All [`BoundType`] implementors must support:
/// - **Debug**: For diagnostic output and error messages
/// - **Clone**: For copying boundary type information
/// - **PartialEq + Eq**: For type equality comparisons
/// - **Serialize + Deserialize**: For boundary type serialization
///
/// ### Implementation Requirements
///
/// Implementors must provide only [`BoundType::includes_boundary()`]:
///
/// ```rust
/// use grid1d::bounds::*;
/// use serde::{Deserialize, Serialize};
///
/// #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
/// struct CustomBoundType;
///
/// impl BoundType for CustomBoundType {
///     type Opposite = CustomBoundType;  // Self-opposite for demonstration
///
///     fn includes_boundary() -> bool {
///         true  // This is a closed boundary type
///     }
/// }
///
/// // All other methods are automatically provided
/// assert!(CustomBoundType::is_closed());
/// assert!(!CustomBoundType::is_open());
/// ```
///
/// ## Performance Characteristics
///
/// ### Compile-Time Optimization
/// - **Method calls**: All methods compile to constants at call sites
/// - **Memory**: No runtime storage, pure compile-time type information
/// - **Branching**: Type-based dispatch eliminates runtime branching
/// - **Inlining**: All operations are automatically inlined
///
/// ### Comparison with Runtime Checks
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// // Compile-time (zero cost)
/// fn compile_time_check<T: BoundType>(bound: &LowerBound<f64, T>, value: f64) -> bool {
///     if T::is_open() {
///         value > *bound.as_ref()  // No runtime branching
///     } else {
///         value >= *bound.as_ref() // No runtime branching
///     }
/// }
///
/// // Runtime (small cost)
/// fn runtime_check(bound: &LowerBoundRuntime<f64>, value: f64) -> bool {
///     if bound.is_open() {
///         value > *bound.as_ref()  // Runtime branch
///     } else {
///         value >= *bound.as_ref() // Runtime branch
///     }
/// }
/// ```
///
/// ## Integration with Grid1D
///
/// The [`BoundType`] trait integrates seamlessly with interval construction:
///
/// ```rust
/// use grid1d::{bounds::*, intervals::*};
/// use try_create::{IntoInner, New};
///
/// fn create_typed_interval<LT: BoundType, UT: BoundType>(
///     lower: LowerBound<f64, LT>,
///     upper: UpperBound<f64, UT>
/// ) -> String {
///     let left = if LT::is_closed() { "[" } else { "(" };
///     let right = if UT::is_closed() { "]" } else { ")" };
///     format!("{}{}, {}{}", left, lower.as_ref(), upper.as_ref(), right)
/// }
///
/// let closed_lower = LowerBoundClosed::new(0.0);
/// let open_upper = UpperBoundOpen::new(1.0);
/// assert_eq!(create_typed_interval(closed_lower, open_upper), "[0, 1)");
/// ```
///
/// ## Best Practices
///
/// ### 1. **Use Compile-Time Types When Possible**
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// // ✅ GOOD: Boundary type known at compile time
/// fn create_closed_constraint(value: f64) -> LowerBoundClosed<f64> {
///     LowerBoundClosed::new(value)
/// }
///
/// // ❌ AVOID: Using runtime types when compile-time is sufficient
/// fn create_runtime_constraint(value: f64) -> LowerBoundRuntime<f64> {
///     IntervalBoundRuntime::Closed(LowerBoundClosed::new(value))
/// }
/// ```
///
/// ### 2. **Leverage Type Parameters for Flexibility**
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// fn create_flexible_bound<T: BoundType>(value: f64) -> LowerBound<f64, T> {
///     LowerBound::new(value)
/// }
///
/// // Caller specifies the exact boundary type needed
/// let closed = create_flexible_bound::<Closed>(5.0);
/// let open = create_flexible_bound::<Open>(5.0);
/// ```
///
/// ### 3. **Use for Generic Algorithm Implementation**
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// fn boundary_constraint_message<S: BoundSide, T: BoundType>() -> String {
///     let side = if S::is_lower() { "lower" } else { "upper" };
///     let inclusion = if T::is_closed() { "inclusive" } else { "exclusive" };
///     format!("{} {} boundary", inclusion, side)
/// }
///
/// type ClosedLower = LowerBound<f64, Closed>;
/// type OpenUpper = UpperBound<f64, Open>;
///
/// assert_eq!(boundary_constraint_message::<Lower, Closed>(), "inclusive lower boundary");
/// assert_eq!(boundary_constraint_message::<Upper, Open>(), "exclusive upper boundary");
/// ```
///
/// ## Mathematical Guarantees
///
/// The [`BoundType`] trait maintains mathematical correctness by:
///
/// - **Semantic Consistency**: Methods accurately reflect mathematical boundary inclusion
/// - **Type Safety**: Prevents mixing incompatible boundary semantics at compile time
/// - **Optimization**: Enables compiler optimizations through compile-time constants
/// - **Composability**: Works seamlessly with other traits in the boundary system
///
/// Use [`BoundType`] when you need compile-time boundary semantics with zero runtime cost.
/// For runtime flexibility, use [`BoundTypeChecks`] with [`IntervalBoundRuntime`].
pub trait BoundType: Debug + Clone + PartialEq + Eq + Serialize + for<'a> Deserialize<'a> {
    /// The opposite bound type (Open ↔ Closed)
    ///
    /// This associated type enables [`flip_bound_type()`](IntervalBoundRuntime::flip_bound_type)
    /// to return the correct type at compile time without requiring turbofish syntax.
    ///
    /// # Type Relationships
    ///
    /// - `<Open as BoundType>::Opposite = Closed`
    /// - `<Closed as BoundType>::Opposite = Open`
    ///
    /// The constraint `BoundType<Opposite = Self>` ensures that flipping twice
    /// returns to the original type: `Type::Opposite::Opposite = Type`
    ///
    /// # Examples
    ///
    /// ```rust
    /// use grid1d::bounds::*;
    ///
    /// // Open::Opposite is Closed
    /// fn assert_open_opposite_is_closed<T>()
    /// where
    ///     T: BoundType<Opposite = Closed>,
    /// {
    ///     // This function only compiles if T::Opposite = Closed
    /// }
    /// assert_open_opposite_is_closed::<Open>();
    ///
    /// // Closed::Opposite is Open
    /// fn assert_closed_opposite_is_open<T>()
    /// where
    ///     T: BoundType<Opposite = Open>,
    /// {
    ///     // This function only compiles if T::Opposite = Open
    /// }
    /// assert_closed_opposite_is_open::<Closed>();
    ///
    /// // Bidirectional: Type::Opposite::Opposite = Type
    /// fn assert_bidirectional<T>()
    /// where
    ///     T: BoundType,
    ///     T::Opposite: BoundType<Opposite = T>,
    /// {
    ///     // Flipping twice returns to original type
    /// }
    /// assert_bidirectional::<Open>();
    /// assert_bidirectional::<Closed>();
    /// ```
    type Opposite: BoundType<Opposite = Self>;

    /// Returns true if the boundary value is included in the constraint.
    ///
    /// This is the fundamental method that determines boundary inclusion semantics
    /// at compile time. It must return a consistent value for the entire lifetime
    /// of the type.
    ///
    /// # Mathematical Semantics
    ///
    /// - Returns `true` for closed boundaries: boundary value is **included**
    /// - Returns `false` for open boundaries: boundary value is **excluded**
    ///
    /// # Examples
    ///
    /// ```rust
    /// use grid1d::bounds::*;
    ///
    /// // Compile-time constants
    /// assert!(Closed::includes_boundary());  // Closed boundaries include
    /// assert!(!Open::includes_boundary());   // Open boundaries exclude
    /// ```
    fn includes_boundary() -> bool;

    /// Returns true if the boundary value is excluded from the constraint.
    ///
    /// This method is automatically implemented as the logical negation of
    /// [`BoundType::includes_boundary()`], ensuring mathematical consistency.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use grid1d::bounds::*;
    ///
    /// assert!(Open::is_open());      // Open boundaries exclude
    /// assert!(!Closed::is_open());   // Closed boundaries include
    /// ```
    #[inline(always)]
    fn is_open() -> bool {
        !Self::includes_boundary()
    }

    /// Returns true if the boundary value is included in the constraint.
    ///
    /// This method is automatically implemented as an alias for
    /// [`BoundType::includes_boundary()`], providing semantic clarity.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use grid1d::bounds::*;
    ///
    /// assert!(Closed::is_closed());  // Closed boundaries include
    /// assert!(!Open::is_closed());   // Open boundaries exclude
    /// ```
    #[inline(always)]
    fn is_closed() -> bool {
        Self::includes_boundary()
    }
}

/// Marker type for open bounds that exclude their boundary values.
///
/// [`Open`] is a zero-sized type that implements [`BoundType`] to represent boundary
/// semantics where the boundary value is **excluded** from the constraint. This type
/// enables compile-time verification of open boundary semantics in interval arithmetic.
///
/// ## Mathematical Semantics
///
/// Open bounds exclude their boundary values from constraints:
///
/// | Bound Type | Mathematical Constraint | Notation | Boundary Value |
/// |------------|------------------------|----------|----------------|
/// | `LowerBound<T, Open>` | `x > bound_value` | `(a` | **Excluded** |
/// | `UpperBound<T, Open>` | `x < bound_value` | `b)` | **Excluded** |
///
/// ## Usage Examples
///
/// ### Basic Open Bound Construction
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// // Lower open bound: x > 5
/// let lower_open: LowerBoundOpen<f64> = LowerBound::new(5.0);
/// assert!(lower_open.is_open());
/// assert!(!lower_open.includes_boundary());
///
/// // Upper open bound: x < 10
/// let upper_open: UpperBoundOpen<f64> = UpperBound::new(10.0);
/// assert!(upper_open.is_open());
/// assert!(!upper_open.includes_boundary());
/// ```
///
/// ### Constraint Testing
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// let open_lower = LowerBoundOpen::new(5.0);  // x > 5
/// let open_upper = UpperBoundOpen::new(10.0); // x < 10
///
/// // Boundary values are excluded
/// assert!(!open_lower.value_within_bound(&5.0));   // 5 > 5 → false
/// assert!(!open_upper.value_within_bound(&10.0));  // 10 < 10 → false
///
/// // Interior values are included
/// assert!(open_lower.value_within_bound(&5.1));    // 5.1 > 5 → true
/// assert!(open_upper.value_within_bound(&9.9));    // 9.9 < 10 → true
/// ```
///
/// ## Type-Level Programming
///
/// ### Compile-Time Type Safety
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// fn create_open_interval<T>(lower: T, upper: T) -> (LowerBound<T, Open>, UpperBound<T, Open>)
/// where
///     T: num_valid::RealScalar,
/// {
///     (LowerBound::new(lower), UpperBound::new(upper))
/// }
///
/// let (lower, upper) = create_open_interval(0.0, 1.0); // (0, 1)
/// assert!(lower.is_open());
/// assert!(upper.is_open());
/// ```
///
/// ### Generic Algorithm Support
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// fn describe_bound<S: BoundSide, T: BoundType>() -> &'static str {
///     match (S::is_lower(), T::is_open()) {
///         (true, true) => "lower open bound (x > value)",
///         (true, false) => "lower closed bound (x ≥ value)",
///         (false, true) => "upper open bound (x < value)",
///         (false, false) => "upper closed bound (x ≤ value)",
///     }
/// }
///
/// assert_eq!(describe_bound::<Lower, Open>(), "lower open bound (x > value)");
/// assert_eq!(describe_bound::<Upper, Open>(), "upper open bound (x < value)");
/// ```
///
/// ## Mathematical Properties
///
/// ### Interval Notation
///
/// Open bounds use parentheses in mathematical interval notation:
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// // Mathematical: (0, 1) = {x ∈ ℝ : 0 < x < 1}
/// let lower = LowerBoundOpen::new(0.0);   // (0
/// let upper = UpperBoundOpen::new(1.0);   // 1)
/// ```
///
/// ### Ordering Semantics
///
/// Open bounds are "looser" than closed bounds with the same value:
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// let open_lower = LowerBoundOpen::new(5.0);    // (5
/// let closed_lower = LowerBoundClosed::new(5.0); // [5
///
/// // For lower bounds: closed < open (tighter constraint < looser constraint)
/// assert!(closed_lower < open_lower);
///
/// let open_upper = UpperBoundOpen::new(5.0);    // 5)
/// let closed_upper = UpperBoundClosed::new(5.0); // 5]
///
/// // For upper bounds: open < closed (tighter constraint < looser constraint)
/// assert!(open_upper < closed_upper);
/// ```
///
/// ## Integration with Intervals
///
/// ### Open Interval Construction
///
/// ```rust
/// use grid1d::{bounds::*, intervals::*};
/// use try_create::{IntoInner, New};
///
/// let lower = LowerBoundOpen::new(0.0);
/// let upper = UpperBoundOpen::new(1.0);
///
/// // Create open interval (0, 1)
/// let interval = IntervalOpen::new(lower.into_inner(), upper.into_inner());
/// assert!(!interval.contains_point(&0.0)); // Excludes endpoints
/// assert!(!interval.contains_point(&1.0)); // Excludes endpoints
/// assert!(interval.contains_point(&0.5));  // Includes interior
/// ```
///
/// ### Mixed Interval Types
///
/// ```rust
/// use grid1d::{bounds::*, intervals::*};
/// use try_create::{IntoInner, New};
///
/// let open_lower = LowerBoundOpen::new(0.0);
/// let closed_upper = UpperBoundClosed::new(1.0);
///
/// // Create half-open interval (0, 1]
/// let interval = IntervalLowerOpenUpperClosed::new(
///     open_lower.into_inner(),
///     closed_upper.into_inner()
/// );
/// assert!(!interval.contains_point(&0.0)); // Excludes lower
/// assert!(interval.contains_point(&1.0));  // Includes upper
/// ```
///
/// ## Performance Characteristics
///
/// ### Zero-Cost Abstractions
/// - **Size**: Zero bytes (empty struct)
/// - **Alignment**: No storage requirements
/// - **Operations**: All compile to constants
/// - **Type checking**: Pure compile-time verification
///
/// ### Optimization Benefits
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// fn optimized_constraint_check<T: BoundType>(bound: &LowerBound<f64, T>, value: f64) -> bool {
///     // This compiles to direct comparison with no runtime branching
///     if T::is_open() {
///         value > *bound.as_ref()  // Constant folded for Open
///     } else {
///         value >= *bound.as_ref() // Never executed for Open
///     }
/// }
///
/// let open_bound = LowerBoundOpen::new(5.0);
/// // Compiles to: value > 5.0 (no type checking overhead)
/// assert!(!optimized_constraint_check(&open_bound, 5.0));
/// ```
///
/// ## Trait Implementation
///
/// ### BoundType Implementation
///
/// ```rust
/// # use grid1d::bounds::*;
/// assert!(!Open::includes_boundary()); // Always false for open bounds
/// assert!(Open::is_open());            // Always true for open bounds
/// assert!(!Open::is_closed());         // Always false for open bounds
/// ```
///
/// ### Required Traits
///
/// The [`Open`] type implements all required traits for boundary type markers:
/// - **Debug**: For diagnostic output
/// - **Clone**: For copying (zero-cost)
/// - **PartialEq + Eq**: For type equality
/// - **Serialize + Deserialize**: For boundary serialization
///
/// ## Best Practices
///
/// ### When to Use Open Bounds
/// - ✅ **Strict inequalities**: When boundary values must be excluded
/// - ✅ **Mathematical limits**: Approaching but not reaching boundary values
/// - ✅ **Asymptotic behavior**: Modeling behaviors near but not at boundaries
/// - ✅ **Convergence criteria**: When exact boundary values indicate non-convergence
///
/// ### Example Applications
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// // Modeling asymptotic behavior
/// fn model_asymptote() -> UpperBoundOpen<f64> {
///     UpperBoundOpen::new(1.0) // x < 1, approaching but never reaching 1
/// }
///
/// // Strict positivity constraints
/// fn positive_domain() -> LowerBoundOpen<f64> {
///     LowerBoundOpen::new(0.0) // x > 0, strictly positive
/// }
///
/// // Convergence tolerance
/// fn convergence_bound(tolerance: f64) -> UpperBoundOpen<f64> {
///     UpperBoundOpen::new(tolerance) // error < tolerance
/// }
/// ```
///
/// ## Mathematical Guarantees
///
/// The [`Open`] type maintains mathematical correctness by:
///
/// - **Exclusion Semantics**: Always excludes boundary values from constraints
/// - **Ordering Consistency**: Maintains proper mathematical ordering with [`Closed`]
/// - **Type Safety**: Prevents accidental inclusion of boundary values at compile time
/// - **Zero-Cost**: Provides complete type safety with no runtime overhead
///
/// Use [`Open`] when you need strict inequality constraints with compile-time verification
/// and zero runtime cost. For runtime flexibility, use [`IntervalBoundRuntime::Open`].
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Open;

/// Marker type for closed bounds that include their boundary values.
///
/// [`Closed`] is a zero-sized type that implements [`BoundType`] to represent boundary
/// semantics where the boundary value is **included** in the constraint. This type
/// enables compile-time verification of closed boundary semantics in interval arithmetic.
///
/// ## Mathematical Semantics
///
/// Closed bounds include their boundary values in constraints:
///
/// | Bound Type | Mathematical Constraint | Notation | Boundary Value |
/// |------------|------------------------|----------|----------------|
/// | `LowerBound<T, Closed>` | `x ≥ bound_value` | `[a` | **Included** |
/// | `UpperBound<T, Closed>` | `x ≤ bound_value` | `b]` | **Included** |
///
/// ## Usage Examples
///
/// ### Basic Closed Bound Construction
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// // Lower closed bound: x ≥ 5
/// let lower_closed: LowerBoundClosed<f64> = LowerBound::new(5.0);
/// assert!(lower_closed.is_closed());
/// assert!(lower_closed.includes_boundary());
///
/// // Upper closed bound: x ≤ 10
/// let upper_closed: UpperBoundClosed<f64> = UpperBound::new(10.0);
/// assert!(upper_closed.is_closed());
/// assert!(upper_closed.includes_boundary());
/// ```
///
/// ### Constraint Testing
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// let closed_lower = LowerBoundClosed::new(5.0);  // x ≥ 5
/// let closed_upper = UpperBoundClosed::new(10.0); // x ≤ 10
///
/// // Boundary values are included
/// assert!(closed_lower.value_within_bound(&5.0));   // 5 ≥ 5 → true
/// assert!(closed_upper.value_within_bound(&10.0));  // 10 ≤ 10 → true
///
/// // Interior values are also included
/// assert!(closed_lower.value_within_bound(&5.1));   // 5.1 ≥ 5 → true
/// assert!(closed_upper.value_within_bound(&9.9));   // 9.9 ≤ 10 → true
///
/// // Values outside the bound are excluded
/// assert!(!closed_lower.value_within_bound(&4.9));  // 4.9 ≥ 5 → false
/// assert!(!closed_upper.value_within_bound(&10.1)); // 10.1 ≤ 10 → false
/// ```
///
/// ## Type-Level Programming
///
/// ### Compile-Time Type Safety
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// fn create_closed_interval<T>(lower: T, upper: T) -> (LowerBound<T, Closed>, UpperBound<T, Closed>)
/// where
///     T: num_valid::RealScalar,
/// {
///     (LowerBound::new(lower), UpperBound::new(upper))
/// }
///
/// let (lower, upper) = create_closed_interval(0.0, 1.0); // [0, 1]
/// assert!(lower.is_closed());
/// assert!(upper.is_closed());
/// ```
///
/// ### Generic Algorithm Support
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// fn bound_description<S: BoundSide, T: BoundType>() -> &'static str {
///     match (S::is_lower(), T::is_closed()) {
///         (true, true) => "lower closed bound (x ≥ value)",
///         (true, false) => "lower open bound (x > value)",
///         (false, true) => "upper closed bound (x ≤ value)",
///         (false, false) => "upper open bound (x < value)",
///     }
/// }
///
/// assert_eq!(bound_description::<Lower, Closed>(), "lower closed bound (x ≥ value)");
/// assert_eq!(bound_description::<Upper, Closed>(), "upper closed bound (x ≤ value)");
/// ```
///
/// ## Mathematical Properties
///
/// ### Interval Notation
///
/// Closed bounds use square brackets in mathematical interval notation:
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// // Mathematical: [0, 1] = {x ∈ ℝ : 0 ≤ x ≤ 1}
/// let lower = LowerBoundClosed::new(0.0);   // [0
/// let upper = UpperBoundClosed::new(1.0);   // 1]
/// ```
///
/// ### Ordering Semantics
///
/// Closed bounds are "tighter" than open bounds with the same value:
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// let closed_lower = LowerBoundClosed::new(5.0); // [5
/// let open_lower = LowerBoundOpen::new(5.0);     // (5
///
/// // For lower bounds: closed < open (tighter constraint < looser constraint)
/// assert!(closed_lower < open_lower);
///
/// let closed_upper = UpperBoundClosed::new(5.0); // 5]
/// let open_upper = UpperBoundOpen::new(5.0);     // 5)
///
/// // For upper bounds: open < closed (tighter constraint < looser constraint)
/// assert!(open_upper < closed_upper);
/// ```
///
/// ## Integration with Intervals
///
/// ### Closed Interval Construction
///
/// ```rust
/// use grid1d::{bounds::*, intervals::*};
/// use try_create::{IntoInner, New};
///
/// let lower = LowerBoundClosed::new(0.0);
/// let upper = UpperBoundClosed::new(1.0);
///
/// // Create closed interval [0, 1]
/// let interval = IntervalClosed::new(lower.into_inner(), upper.into_inner());
/// assert!(interval.contains_point(&0.0)); // Includes endpoints
/// assert!(interval.contains_point(&1.0)); // Includes endpoints
/// assert!(interval.contains_point(&0.5)); // Includes interior
/// ```
///
/// ### Mixed Interval Types
///
/// ```rust
/// use grid1d::{bounds::*, intervals::*};
/// use try_create::{IntoInner, New};
///
/// let closed_lower = LowerBoundClosed::new(0.0);
/// let open_upper = UpperBoundOpen::new(1.0);
///
/// // Create half-open interval [0, 1)
/// let interval = IntervalLowerClosedUpperOpen::new(
///     closed_lower.into_inner(),
///     open_upper.into_inner()
/// );
/// assert!(interval.contains_point(&0.0));  // Includes lower
/// assert!(!interval.contains_point(&1.0)); // Excludes upper
/// ```
///
/// ## Performance Characteristics
///
/// ### Zero-Cost Abstractions
/// - **Size**: Zero bytes (empty struct)
/// - **Alignment**: No storage requirements
/// - **Operations**: All compile to constants
/// - **Type checking**: Pure compile-time verification
///
/// ### Optimization Benefits
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// fn optimized_constraint_check<T: BoundType>(bound: &LowerBound<f64, T>, value: f64) -> bool {
///     // This compiles to direct comparison with no runtime branching
///     if T::is_closed() {
///         value >= *bound.as_ref() // Constant folded for Closed
///     } else {
///         value > *bound.as_ref()  // Never executed for Closed
///     }
/// }
///
/// let closed_bound = LowerBoundClosed::new(5.0);
/// // Compiles to: value >= 5.0 (no type checking overhead)
/// assert!(optimized_constraint_check(&closed_bound, 5.0));
/// ```
///
/// ## Trait Implementation
///
/// ### BoundType Implementation
///
/// ```rust
/// # use grid1d::bounds::*;
/// assert!(Closed::includes_boundary()); // Always true for closed bounds
/// assert!(!Closed::is_open());          // Always false for closed bounds
/// assert!(Closed::is_closed());         // Always true for closed bounds
/// ```
///
/// ### Required Traits
///
/// The [`Closed`] type implements all required traits for boundary type markers:
/// - **Debug**: For diagnostic output
/// - **Clone**: For copying (zero-cost)
/// - **PartialEq + Eq**: For type equality
/// - **Serialize + Deserialize**: For boundary serialization
///
/// ## Best Practices
///
/// ### When to Use Closed Bounds
/// - ✅ **Inclusive constraints**: When boundary values are valid/required
/// - ✅ **Physical boundaries**: Real-world limits that can be achieved
/// - ✅ **Optimization bounds**: When optimal values can occur at boundaries
/// - ✅ **Discrete domains**: When working with finite precision arithmetic
///
/// ### Example Applications
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// // Physical constraint (temperature can be exactly 0°C)
/// fn temperature_bound() -> LowerBoundClosed<f64> {
///     LowerBoundClosed::new(0.0) // T ≥ 0°C (absolute zero in Celsius)
/// }
///
/// // Probability constraint (probability can be exactly 1.0)
/// fn probability_upper_bound() -> UpperBoundClosed<f64> {
///     UpperBoundClosed::new(1.0) // p ≤ 1.0
/// }
/// ```
///
/// ## Common Use Cases
///
/// ### Standard Mathematical Intervals
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// // Unit interval [0, 1]
/// let unit_lower = LowerBoundClosed::new(0.0);
/// let unit_upper = UpperBoundClosed::new(1.0);
///
/// // Non-negative reals [0, +∞)
/// let non_negative = LowerBoundClosed::new(0.0);
///
/// // Bounded domain [-1, 1]
/// let symmetric_lower = LowerBoundClosed::new(-1.0);
/// let symmetric_upper = UpperBoundClosed::new(1.0);
/// ```
///
/// ## Mathematical Guarantees
///
/// The [`Closed`] type maintains mathematical correctness by:
///
/// - **Inclusion Semantics**: Always includes boundary values in constraints
/// - **Ordering Consistency**: Maintains proper mathematical ordering with [`Open`]
/// - **Type Safety**: Ensures boundary values are considered valid at compile time
/// - **Zero-Cost**: Provides complete type safety with no runtime overhead
///
/// Use [`Closed`] when you need inclusive constraint semantics with compile-time verification
/// and zero runtime cost. For runtime flexibility, use [`IntervalBoundRuntime::Closed`].
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Closed;

impl BoundType for Open {
    type Opposite = Closed;

    #[inline(always)]
    fn includes_boundary() -> bool {
        false
    }
}

impl BoundType for Closed {
    type Opposite = Open;

    #[inline(always)]
    fn includes_boundary() -> bool {
        true
    }
}
//------------------------------------------------------------------------------------------------

//------------------------------------------------------------------------------------------------
/// Runtime queries for boundary inclusion semantics (open vs closed bounds).
///
/// [`BoundTypeChecks`] provides a unified interface for determining whether a bound includes
/// or excludes its boundary value at runtime. This trait enables generic programming over
/// different boundary inclusion semantics while maintaining mathematical correctness and type safety.
///
/// ## Design Philosophy
///
/// ### Runtime Type Detection
///
/// Unlike the compile-time [`BoundType`] trait that provides static type information,
/// [`BoundTypeChecks`] operates on bound instances to determine their inclusion semantics:
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// fn analyze_bound_inclusion<B: BoundTypeChecks>(bound: &B) -> String {
///     if bound.is_closed() {
///         "This bound includes its boundary value".to_string()
///     } else {
///         "This bound excludes its boundary value".to_string()
///     }
/// }
///
/// let closed = LowerBoundClosed::new(0.0);
/// let open = UpperBoundOpen::new(10.0);
///
/// assert_eq!(analyze_bound_inclusion(&closed), "This bound includes its boundary value");
/// assert_eq!(analyze_bound_inclusion(&open), "This bound excludes its boundary value");
/// ```
///
/// ### Complement Relationship
///
/// The two derived methods in this trait are logical complements:
/// ```text
/// is_closed() == includes_boundary()
/// is_open() == !includes_boundary()
/// ```
///
/// This relationship is enforced by the default implementations of [`BoundTypeChecks::is_closed()`] and [`BoundTypeChecks::is_open()`],
/// ensuring mathematical consistency across all implementations.
///
/// ## Mathematical Semantics
///
/// ### Closed Bounds (Inclusive)
///
/// Closed bounds include their boundary values in the constraint:
///
/// | Bound Type | Mathematical Constraint | Notation | Meaning |
/// |------------|------------------------|----------|---------|
/// | [`LowerBoundClosed`] | `x ≥ bound_value` | `[a` | Includes minimum |
/// | [`UpperBoundClosed`] | `x ≤ bound_value` | `b]` | Includes maximum |
///
/// ### Open Bounds (Exclusive)
///
/// Open bounds exclude their boundary values from the constraint:
///
/// | Bound Type | Mathematical Constraint | Notation | Meaning |
/// |------------|------------------------|----------|---------|
/// | [`LowerBoundOpen`] | `x > bound_value` | `(a` | Excludes minimum |
/// | [`UpperBoundOpen`] | `x < bound_value` | `b)` | Excludes maximum |
///
/// ## Core Methods
///
/// ### [`BoundTypeChecks::includes_boundary()`]
///
/// The fundamental method that determines whether the boundary value is part of the constraint.
/// This is the only method implementors must provide:
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// let closed_lower = LowerBoundClosed::new(5.0);
/// let open_lower = LowerBoundOpen::new(5.0);
/// let closed_upper = UpperBoundClosed::new(10.0);
/// let open_upper = UpperBoundOpen::new(10.0);
///
/// // Closed bounds include their boundary
/// assert!(closed_lower.includes_boundary());
/// assert!(closed_upper.includes_boundary());
///
/// // Open bounds exclude their boundary
/// assert!(!open_lower.includes_boundary());
/// assert!(!open_upper.includes_boundary());
/// ```
///
/// ### [`BoundTypeChecks::is_closed()`]
///
/// Returns `true` if this bound includes its boundary value. This is a convenience method
/// that delegates to [`BoundTypeChecks::includes_boundary()`]:
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// assert!(LowerBoundClosed::new(0.0).is_closed());
/// assert!(!LowerBoundClosed::new(1.0).is_open());
///
/// assert!(UpperBoundClosed::new(2.0).is_closed());
/// assert!(!UpperBoundClosed::new(3.0).is_open());
///
/// assert!(!LowerBoundOpen::new(4.0).is_closed());
/// assert!(LowerBoundOpen::new(5.0).is_open());
///
/// assert!(!UpperBoundOpen::new(6.0).is_closed());
/// assert!(UpperBoundOpen::new(7.0).is_open());
/// ```
///
/// ### [`BoundTypeChecks::is_open()`]
///
/// Returns `true` if this bound excludes its boundary value. This is implemented as
/// the logical negation of [`BoundTypeChecks::includes_boundary()`]:
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// let open_lower = LowerBoundOpen::new(1.0);
/// let open_upper = UpperBoundOpen::new(2.0);
/// let closed_lower = LowerBoundClosed::new(1.0);
/// let closed_upper = UpperBoundClosed::new(2.0);
///
/// // Open bounds return true
/// assert!(open_lower.is_open());
/// assert!(open_upper.is_open());
/// assert!(!open_lower.is_closed());
/// assert!(!open_upper.is_closed());
///
/// // Closed bounds return false
/// assert!(!closed_lower.is_open());
/// assert!(!closed_upper.is_open());
/// assert!(closed_lower.is_closed());
/// assert!(closed_upper.is_closed());
/// ```
///
/// ## Generic Programming Applications
///
/// ### Constraint Analysis
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// fn analyze_constraint_strictness<B: BoundTypeChecks + AsRef<f64>>(bounds: &[B]) -> (usize, usize) {
///     let (strict, inclusive): (Vec<_>, Vec<_>) = bounds.iter()
///         .partition(|bound| bound.is_open());
///     (strict.len(), inclusive.len())
/// }
///
/// let mixed_lower_bounds: Vec<LowerBoundRuntime<f64>> = vec![
///     LowerBoundClosed::new(0.0).into(),   // Inclusive
///     LowerBoundOpen::new(1.0).into(),     // Strict
/// ];
/// let mixed_upper_bounds: Vec<UpperBoundRuntime<f64>> = vec![
///     UpperBoundClosed::new(10.0).into(),  // Inclusive
///     UpperBoundOpen::new(9.0).into(),     // Strict
///     UpperBoundOpen::new(8.0).into(),     // Strict
/// ];
///
/// let (strict_count, inclusive_count) = analyze_constraint_strictness(&mixed_lower_bounds);
/// assert_eq!(strict_count, 1);     // One open bounds
/// assert_eq!(inclusive_count, 1);  // One closed bounds
///
/// let (strict_count, inclusive_count) = analyze_constraint_strictness(&mixed_upper_bounds);
/// assert_eq!(strict_count, 2);     // Two open bounds
/// assert_eq!(inclusive_count, 1);  // One closed bounds
/// ```
///
/// ### Boundary Behavior Validation
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// fn validate_boundary_consistency<B>(bound: &B, test_value: f64) -> bool
/// where
///     B: BoundTypeChecks + ValueWithinBound<RealType = f64> + AsRef<f64>,
/// {
///     let boundary_value = *bound.as_ref();
///     let at_boundary = (test_value - boundary_value).abs() < f64::EPSILON;
///     
///     if at_boundary {
///         // At the boundary, constraint should match inclusion semantics
///         bound.value_within_bound(&test_value) == bound.includes_boundary()
///     } else {
///         // Away from boundary, inclusion type shouldn't matter for this test
///         true
///     }
/// }
///
/// let closed_bound = LowerBoundClosed::new(5.0);
/// let open_bound = LowerBoundOpen::new(5.0);
///
/// // Test boundary value behavior
/// assert!(validate_boundary_consistency(&closed_bound, 5.0)); // Should include 5.0
/// assert!(validate_boundary_consistency(&open_bound, 5.0));   // Should exclude 5.0
///
/// // Test non-boundary values
/// assert!(validate_boundary_consistency(&closed_bound, 6.0)); // Both should include 6.0
/// assert!(validate_boundary_consistency(&open_bound, 6.0));
/// ```
///
/// ### Dynamic Interval Construction
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// fn describe_interval_boundary<L, U>(lower: &L, upper: &U) -> String
/// where
///     L: BoundTypeChecks + AsRef<f64>,
///     U: BoundTypeChecks + AsRef<f64>,
/// {
///     let left_bracket = if lower.is_closed() { "[" } else { "(" };
///     let right_bracket = if upper.is_closed() { "]" } else { ")" };
///     
///     format!("{}{}, {}{}",
///         left_bracket, lower.as_ref(),
///         upper.as_ref(), right_bracket)
/// }
///
/// let bounds_combinations: Vec<(LowerBoundRuntime<f64>, UpperBoundRuntime<f64>)> = vec![
///     (LowerBoundClosed::new(0.0).into(), UpperBoundClosed::new(1.0).into()),   // [0, 1]
///     (LowerBoundOpen::new(0.0).into(), UpperBoundOpen::new(1.0).into()),       // (0, 1)
///     (LowerBoundClosed::new(0.0).into(), UpperBoundOpen::new(1.0).into()),     // [0, 1)
///     (LowerBoundOpen::new(0.0).into(), UpperBoundClosed::new(1.0).into()),     // (0, 1]
/// ];
///
/// let descriptions: Vec<String> = bounds_combinations.iter()
///     .map(|(l, u)| describe_interval_boundary(l, u))
///     .collect();
///
/// assert_eq!(descriptions, vec!["[0, 1]", "(0, 1)", "[0, 1)", "(0, 1]"]);
/// ```
///
/// ## Integration with Runtime Bounds
///
/// The trait works seamlessly with runtime bound types:
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// fn analyze_runtime_bound_types(
///     lower: &LowerBoundRuntime<f64>,
///     upper: &UpperBoundRuntime<f64>
/// ) -> String {
///     let lower_type = if lower.is_closed() { "closed" } else { "open" };
///     let upper_type = if upper.is_closed() { "closed" } else { "open" };
///     
///     format!("Lower: {} ({}), Upper: {} ({})",
///         lower.as_ref(), lower_type,
///         upper.as_ref(), upper_type)
/// }
///
/// let lower = IntervalBoundRuntime::Closed(LowerBoundClosed::new(0.0));
/// let upper = IntervalBoundRuntime::Open(UpperBoundOpen::new(1.0));
///
/// assert_eq!(
///     analyze_runtime_bound_types(&lower, &upper),
///     "Lower: 0 (closed), Upper: 1 (open)"
/// );
/// ```
///
/// ## Mathematical Invariant Verification
///
/// The trait enables runtime validation of mathematical properties:
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// fn verify_inclusion_consistency<B: BoundTypeChecks>(bound: &B) -> bool {
///     // Verify the complement relationship holds
///     bound.is_closed() == bound.includes_boundary() &&
///     bound.is_open() == !bound.includes_boundary()
/// }
///
/// fn verify_boundary_semantics<B>(bound: &B, boundary_value: f64) -> Result<(), String>
/// where
///     B: BoundTypeChecks + ValueWithinBound<RealType = f64> + AsRef<f64>,
/// {
///     if !verify_inclusion_consistency(bound) {
///         return Err("Bound inclusion consistency check failed".to_string());
///     }
///     
///     // The boundary value itself should only satisfy the constraint if the bound is closed
///     let boundary_satisfies = bound.value_within_bound(&boundary_value);
///     if boundary_satisfies != bound.includes_boundary() {
///         return Err("Boundary constraint behavior inconsistent with inclusion type".to_string());
///     }
///     
///     Ok(())
/// }
///
/// let closed_bound = LowerBoundClosed::new(5.0);
/// let open_bound = LowerBoundOpen::new(5.0);
///
/// assert!(verify_boundary_semantics(&closed_bound, 5.0).is_ok());
/// assert!(verify_boundary_semantics(&open_bound, 5.0).is_ok());
/// ```
///
/// ## Performance Characteristics
///
/// ### Runtime Complexity
/// - **`includes_boundary()`**: O(1) - typically compiles to a constant or simple type check
/// - **`is_closed()`**: O(1) - delegates to `includes_boundary()`
/// - **`is_open()`**: O(1) - logical negation of `includes_boundary()`
/// - **Memory**: No additional storage overhead beyond the underlying bound
///
/// ### Optimization Notes
/// - All methods are marked `#[inline(always)]` for zero-cost abstractions
/// - The complement relationship enables compiler optimizations
/// - For compile-time bounds, inclusion information is known statically and optimized away
/// - Runtime bounds have small pattern matching overhead but maintain correctness
///
/// ## Implementation Requirements
///
/// Implementors must provide:
/// 1. **`includes_boundary()`**: Core method that determines boundary inclusion semantics
/// 2. **Consistency**: The method must return consistent results for the lifetime of the object
/// 3. **Mathematical Correctness**: Return `true` for closed bounds, `false` for open bounds
///
/// ### Example Implementation
///
/// ```rust
/// use grid1d::bounds::*;
///
/// struct CustomBound {
///     value: f64,
///     is_inclusive: bool,
/// }
///
/// impl BoundTypeChecks for CustomBound {
///     fn includes_boundary(&self) -> bool {
///         self.is_inclusive
///     }
/// }
///
/// let inclusive = CustomBound { value: 5.0, is_inclusive: true };
/// let exclusive = CustomBound { value: 5.0, is_inclusive: false };
///
/// assert!(inclusive.is_closed());
/// assert!(!inclusive.is_open());
/// assert!(inclusive.includes_boundary());
///
/// assert!(!exclusive.is_closed());
/// assert!(exclusive.is_open());
/// assert!(!exclusive.includes_boundary());
/// ```
///
/// ## Mathematical Guarantees
///
/// The [`BoundTypeChecks`] trait maintains mathematical correctness by:
///
/// - **Complement Relationship**: Ensuring exactly one of `is_open()` or `is_closed()` is true
/// - **Consistency**: Boundary inclusion semantics remain constant for the object's lifetime
/// - **Type Safety**: Preventing confusion between inclusive and exclusive constraints
/// - **Constraint Semantics**: Properly reflecting the mathematical meaning of open vs closed bounds
///
/// Use [`BoundTypeChecks`] when you need to determine boundary inclusion behavior at runtime
/// while maintaining mathematical correctness and type safety. For compile-time inclusion
/// information, prefer the static [`BoundType`] trait and its associated types ([`Open`], [`Closed`]).
pub trait BoundTypeChecks {
    /// Returns whether the boundary value is included in the interval
    fn includes_boundary(&self) -> bool;

    /// Returns whether this bound is open (excludes the boundary value)
    #[inline(always)]
    fn is_open(&self) -> bool {
        !self.is_closed()
    }

    /// Returns whether this bound is closed (includes the boundary value)
    #[inline(always)]
    fn is_closed(&self) -> bool {
        self.includes_boundary()
    }
}

/// Runtime queries for boundary side semantics (upper vs lower bounds).
///
/// [`BoundSideChecks`] provides a unified interface for determining whether a bound represents
/// an upper or lower constraint at runtime. This trait enables generic programming over
/// different boundary sides while maintaining mathematical correctness and type safety.
///
/// ## Design Philosophy
///
/// ### Runtime Side Detection
///
/// Unlike the compile-time [`BoundSide`] trait that provides static type information,
/// [`BoundSideChecks`] operates on bound instances to determine their side semantics:
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// fn analyze_bound_side<B: BoundSideChecks>(bound: &B) -> String {
///     if bound.is_lower_bound() {
///         "This bound defines a minimum constraint".to_string()
///     } else {
///         "This bound defines a maximum constraint".to_string()
///     }
/// }
///
/// let lower = LowerBoundClosed::new(0.0);
/// let upper = UpperBoundOpen::new(10.0);
///
/// assert_eq!(analyze_bound_side(&lower), "This bound defines a minimum constraint");
/// assert_eq!(analyze_bound_side(&upper), "This bound defines a maximum constraint");
/// ```
///
/// ### Complement Relationship
///
/// The two methods in this trait are logical complements:
/// ```text
/// is_lower_bound() == !is_upper_bound()
/// is_upper_bound() == !is_lower_bound()
/// ```
///
/// This relationship is enforced by the default implementation of [`BoundSideChecks::is_lower_bound()`],
/// ensuring mathematical consistency across all implementations.
///
/// ## Mathematical Semantics
///
/// ### Lower Bounds (Minimum Constraints)
///
/// Lower bounds define minimum values for intervals and domains:
///
/// | Bound Type | Mathematical Constraint | Notation | Meaning |
/// |------------|------------------------|----------|---------|
/// | [`LowerBoundClosed`] | `x ≥ lower_value` | `[a` | Includes minimum |
/// | [`LowerBoundOpen`] | `x > lower_value` | `(a` | Excludes minimum |
///
/// ### Upper Bounds (Maximum Constraints)
///
/// Upper bounds define maximum values for intervals and domains:
///
/// | Bound Type | Mathematical Constraint | Notation | Meaning |
/// |------------|------------------------|----------|---------|
/// | [`UpperBoundClosed`] | `x ≤ upper_value` | `b]` | Includes maximum |
/// | [`UpperBoundOpen`] | `x < upper_value` | `b)` | Excludes maximum |
///
/// ## Core Methods
///
/// ### [`BoundSideChecks::is_upper_bound()`]
///
/// Returns `true` if this bound represents an upper (maximum) constraint:
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// let upper_closed = UpperBoundClosed::new(10.0);
/// let upper_open = UpperBoundOpen::new(10.0);
/// let lower_closed = LowerBoundClosed::new(0.0);
/// let lower_open = LowerBoundOpen::new(0.0);
///
/// // Upper bounds return true
/// assert!(upper_closed.is_upper_bound());
/// assert!(upper_open.is_upper_bound());
///
/// // Lower bounds return false
/// assert!(!lower_closed.is_upper_bound());
/// assert!(!lower_open.is_upper_bound());
/// ```
///
/// ### [`BoundSideChecks::is_lower_bound()`]
///
/// Returns `true` if this bound represents a lower (minimum) constraint:
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// let lower_closed = LowerBoundClosed::new(0.0);
/// let lower_open = LowerBoundOpen::new(0.0);
/// let upper_closed = UpperBoundClosed::new(10.0);
/// let upper_open = UpperBoundOpen::new(10.0);
///
/// // Lower bounds return true
/// assert!(lower_closed.is_lower_bound());
/// assert!(lower_open.is_lower_bound());
///
/// // Upper bounds return false
/// assert!(!upper_closed.is_lower_bound());
/// assert!(!upper_open.is_lower_bound());
/// ```
///
/// ## Generic Programming Applications
///
/// ### Bound Classification
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// let bounds: Vec<Box<dyn BoundSideChecks>> = vec![
///     Box::new(LowerBoundClosed::new(0.0)),
///     Box::new(UpperBoundOpen::new(10.0)),
///     Box::new(LowerBoundOpen::new(1.0)),
///     Box::new(UpperBoundClosed::new(9.0)),
/// ];
///
/// let (lower_bounds, upper_bounds): (Vec<_>, Vec<_>) =
///     bounds.iter().partition(|bound| bound.is_lower_bound());
/// assert_eq!(lower_bounds.len(), 2); // Two lower bounds
/// assert_eq!(upper_bounds.len(), 2); // Two upper bounds
/// ```
///
/// ### Constraint Validation
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// fn validate_constraint_consistency<BL, BU>(lower: &BL, upper: &BU) -> Result<(), String>
/// where
///     BL: BoundSideChecks + AsRef<f64>,
///     BU: BoundSideChecks + AsRef<f64>,
/// {
///     if !lower.is_lower_bound() {
///         return Err("First argument must be a lower bound".to_string());
///     }
///     if !upper.is_upper_bound() {
///         return Err("Second argument must be an upper bound".to_string());
///     }
///     if lower.as_ref() >= upper.as_ref() {
///         return Err("Lower bound must be less than upper bound".to_string());
///     }
///     Ok(())
/// }
///
/// let lower = LowerBoundClosed::new(0.0);
/// let upper = UpperBoundOpen::new(10.0);
/// let wrong_lower = UpperBoundClosed::new(5.0);
///
/// assert!(validate_constraint_consistency(&lower, &upper).is_ok());
/// assert!(validate_constraint_consistency(&wrong_lower, &upper).is_err());
/// ```
///
/// ### Dynamic Bound Processing
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// fn process_bound<B>(bound: &B) -> String
/// where
///     B: BoundChecks + AsRef<f64>,
/// {
///     let side = if bound.is_lower_bound() { "Lower" } else { "Upper" };
///     let inclusion = if bound.is_closed() { "inclusive" } else { "exclusive" };
///     let value = bound.as_ref();
///     
///     format!("{} bound: {} ({})", side, value, inclusion)
/// }
///
///
/// let lower_bounds: Vec<LowerBoundRuntime<f64>> = vec![
///     LowerBoundClosed::new(0.0).into(),
///     LowerBoundOpen::new(1.0).into(),
/// ];
/// let upper_bounds: Vec<UpperBoundRuntime<f64>> = vec![
///     UpperBoundOpen::new(10.0).into(),
///     UpperBoundClosed::new(9.0).into(),
/// ];
///
/// for bound in &lower_bounds {
///     println!("{}", process_bound(bound));
/// }
/// // Output:
/// // Lower bound: 0 (inclusive)
/// // Lower bound: 1 (exclusive)
/// for bound in &upper_bounds {
///     println!("{}", process_bound(bound));
/// }
/// // Output:
/// // Upper bound: 10 (exclusive)
/// // Upper bound: 9 (inclusive)
/// // Upper bound: 9 (inclusive)
/// ```
///
/// ## Integration with Runtime Bounds
///
/// The trait works seamlessly with runtime bound types:
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// fn analyze_runtime_bounds(
///     lower: &LowerBoundRuntime<f64>,
///     upper: &UpperBoundRuntime<f64>
/// ) -> String {
///     // These assertions are guaranteed by the type system
///     assert!(lower.is_lower_bound());
///     assert!(upper.is_upper_bound());
///     assert!(!lower.is_upper_bound());
///     assert!(!upper.is_lower_bound());
///     
///     format!(
///         "Interval from {} {} to {} {}",
///         if lower.is_closed() { "[" } else { "(" },
///         lower.as_ref(),
///         upper.as_ref(),
///         if upper.is_closed() { "]" } else { ")" }
///     )
/// }
///
/// let lower = IntervalBoundRuntime::Closed(LowerBoundClosed::new(0.0));
/// let upper = IntervalBoundRuntime::Open(UpperBoundOpen::new(1.0));
///
/// assert_eq!(analyze_runtime_bounds(&lower, &upper), "Interval from [ 0 to 1 )");
/// ```
///
/// ## Invariant Checking
///
/// The trait enables runtime validation of mathematical invariants:
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// fn verify_bound_side_consistency<B: BoundSideChecks>(bound: &B) -> bool {
///     // Verify the complement relationship holds
///     bound.is_lower_bound() == !bound.is_upper_bound()
/// }
///
/// fn verify_interval_bounds<L, U>(lower: &L, upper: &U) -> Result<(), String>
/// where
///     L: BoundSideChecks + AsRef<f64>,
///     U: BoundSideChecks + AsRef<f64>,
/// {
///     if !verify_bound_side_consistency(lower) {
///         return Err("Lower bound side consistency check failed".to_string());
///     }
///     if !verify_bound_side_consistency(upper) {
///         return Err("Upper bound side consistency check failed".to_string());
///     }
///     if !lower.is_lower_bound() {
///         return Err("First argument is not a lower bound".to_string());
///     }
///     if !upper.is_upper_bound() {
///         return Err("Second argument is not an upper bound".to_string());
///     }
///     Ok(())
/// }
///
/// let lower = LowerBoundClosed::new(0.0);
/// let upper = UpperBoundOpen::new(1.0);
///
/// assert!(verify_interval_bounds(&lower, &upper).is_ok());
/// ```
///
/// ## Performance Characteristics
///
/// ### Runtime Complexity
/// - **`is_upper_bound()`**: O(1) - typically compiles to a constant or simple field access
/// - **`is_lower_bound()`**: O(1) - implemented as logical negation of `is_upper_bound()`
/// - **Memory**: No additional storage overhead beyond the underlying bound
///
/// ### Optimization Notes
/// - All methods are marked `#[inline(always)]` for zero-cost abstractions
/// - The complement relationship enables compiler optimizations
/// - For compile-time bounds, side information is known statically and optimized away
///
/// ## Implementation Requirements
///
/// Implementors must provide:
/// 1. **`is_upper_bound()`**: Core method that determines the bound's side
/// 2. **Consistency**: The method must return consistent results for the lifetime of the object
/// 3. **Complement Semantics**: `is_lower_bound()` is automatically derived as `!is_upper_bound()`
///
/// ### Example Implementation
///
/// ```rust
/// use grid1d::bounds::*;
///
/// struct CustomBound {
///     value: f64,
///     is_upper: bool,
/// }
///
/// impl BoundSideChecks for CustomBound {
///     fn is_upper_bound(&self) -> bool {
///         self.is_upper
///     }
/// }
///
/// let lower = CustomBound { value: 0.0, is_upper: false };
/// let upper = CustomBound { value: 10.0, is_upper: true };
///
/// assert!(lower.is_lower_bound());
/// assert!(!lower.is_upper_bound());
/// assert!(upper.is_upper_bound());
/// assert!(!upper.is_lower_bound());
/// ```
///
/// ## Mathematical Guarantees
///
/// The [`BoundSideChecks`] trait maintains mathematical correctness by:
///
/// - **Complement Relationship**: Ensuring exactly one of `is_lower_bound()` or `is_upper_bound()` is true
/// - **Consistency**: Bound side semantics remain constant for the object's lifetime
/// - **Type Safety**: Preventing confusion between minimum and maximum constraints
/// - **Generic Compatibility**: Enabling type-safe generic programming over bound sides
///
/// Use [`BoundSideChecks`] when you need to determine boundary side semantics at runtime
/// while maintaining mathematical correctness and type safety. For compile-time side
/// information, prefer the static [`BoundSide`] trait and its associated types.
pub trait BoundSideChecks {
    /// Returns whether this is an upper bound
    fn is_upper_bound(&self) -> bool;

    /// Returns whether this is a lower bound
    #[inline(always)]
    fn is_lower_bound(&self) -> bool {
        !self.is_upper_bound()
    }
}

/// Unified runtime queries for both boundary side and type semantics.
///
/// [`BoundChecks`] is a marker trait that combines [`BoundSideChecks`] and [`BoundTypeChecks`]
/// to provide a unified interface for querying all boundary properties at runtime. This trait
/// enables comprehensive generic programming over bounds while maintaining mathematical correctness
/// and type safety.
///
/// ## Design Philosophy
///
/// ### Unified Boundary Interface
///
/// Rather than requiring separate trait bounds for side and type checking, [`BoundChecks`]
/// provides a single trait that encompasses all boundary query capabilities:
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// // Instead of requiring multiple trait bounds:
/// fn analyze_bound_verbose<B>(bound: &B) -> String
/// where
///     B: BoundSideChecks + BoundTypeChecks + AsRef<f64>,
/// {
///     // Implementation...
///     format!("Bound analysis")
/// }
///
/// // Use the unified BoundChecks trait:
/// fn analyze_bound_concise<B>(bound: &B) -> String
/// where
///     B: BoundChecks + AsRef<f64>,
/// {
///     let side = if bound.is_lower_bound() { "Lower" } else { "Upper" };
///     let inclusion = if bound.is_closed() { "closed" } else { "open" };
///     format!("{} {} bound: {}", side, inclusion, bound.as_ref())
/// }
///
/// let lower_closed = LowerBoundClosed::new(5.0);
/// let upper_open = UpperBoundOpen::new(10.0);
///
/// println!("{}", analyze_bound_concise(&lower_closed)); // "Lower closed bound: 5"
/// println!("{}", analyze_bound_concise(&upper_open));   // "Upper open bound: 10"
/// ```
///
/// ### Automatic Implementation
///
/// [`BoundChecks`] is automatically implemented for any type that implements both
/// [`BoundSideChecks`] and [`BoundTypeChecks`], requiring no additional implementation work:
///
/// ```rust
/// use grid1d::bounds::*;
///
/// // This automatically implements BoundChecks:
/// struct CustomBound {
///     value: f64,
///     is_upper: bool,
///     is_inclusive: bool,
/// }
///
/// impl BoundSideChecks for CustomBound {
///     fn is_upper_bound(&self) -> bool {
///         self.is_upper
///     }
/// }
///
/// impl BoundTypeChecks for CustomBound {
///     fn includes_boundary(&self) -> bool {
///         self.is_inclusive
///     }
/// }
///
/// // BoundChecks is now available automatically
/// let custom = CustomBound { value: 7.0, is_upper: false, is_inclusive: true };
/// assert!(custom.is_lower_bound()); // From BoundSideChecks
/// assert!(custom.is_closed());      // From BoundTypeChecks
/// ```
///
/// ## Available Methods
///
/// All methods from both constituent traits are available:
///
/// ### From [`BoundSideChecks`]
/// - [`is_upper_bound()`](BoundSideChecks::is_upper_bound) - Returns `true` for upper bounds
/// - [`is_lower_bound()`](BoundSideChecks::is_lower_bound) - Returns `true` for lower bounds
///
/// ### From [`BoundTypeChecks`]  
/// - [`includes_boundary()`](BoundTypeChecks::includes_boundary) - Returns `true` if boundary is included
/// - [`is_closed()`](BoundTypeChecks::is_closed) - Returns `true` for closed bounds
/// - [`is_open()`](BoundTypeChecks::is_open) - Returns `true` for open bounds
///
/// ## Generic Programming Applications
///
/// ### Interval Validation
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// fn validate_interval_bounds<L, U>(lower: &L, upper: &U) -> Result<(), String>
/// where
///     L: BoundChecks + AsRef<f64>,
///     U: BoundChecks + AsRef<f64>,
/// {
///     // Validate sides
///     if !lower.is_lower_bound() {
///         return Err("First argument must be a lower bound".to_string());
///     }
///     if !upper.is_upper_bound() {
///         return Err("Second argument must be an upper bound".to_string());
///     }
///     
///     // Validate ordering
///     if lower.as_ref() > upper.as_ref() {
///         return Err("Lower bound cannot exceed upper bound".to_string());
///     }
///     
///     // Check for degenerate cases
///     if lower.as_ref() == upper.as_ref() {
///         if lower.is_open() || upper.is_open() {
///             return Err("Empty interval: bounds equal but at least one is open".to_string());
///         }
///     }
///     
///     Ok(())
/// }
///
/// // Valid intervals
/// let lower_closed = LowerBoundClosed::new(0.0);
/// let upper_open = UpperBoundOpen::new(1.0);
/// assert!(validate_interval_bounds(&lower_closed, &upper_open).is_ok());
///
/// // Invalid: wrong sides
/// let upper_as_lower = UpperBoundClosed::new(0.0);
/// assert!(validate_interval_bounds(&upper_as_lower, &upper_open).is_err());
///
/// // Invalid: empty interval
/// let same_value_open = LowerBoundOpen::new(1.0);
/// let same_value_closed = UpperBoundClosed::new(1.0);
/// assert!(validate_interval_bounds(&same_value_open, &same_value_closed).is_err());
/// ```
///
/// ## Performance Characteristics
///
/// ### Runtime Complexity
/// - **All methods**: O(1) - direct delegation to constituent traits
/// - **Memory overhead**: Zero - marker trait with no additional storage
/// - **Generic dispatch**: Same as individual traits - optimizes to static dispatch when possible
///
/// ### Compilation Benefits
/// - **Trait bound simplification**: Reduces generic constraints from two traits to one
/// - **Code clarity**: More readable generic function signatures
/// - **Automatic availability**: No additional implementation required
///
/// ## Best Practices
///
/// ### 1. **Use for Generic Functions**
/// ```rust
/// use grid1d::bounds::*;
///
/// // ✅ GOOD: Use BoundChecks for comprehensive bound analysis
/// fn analyze_bound<B: BoundChecks + AsRef<f64>>(bound: &B) -> String {
///     // Can use all boundary query methods
///     format!("Analysis complete")
/// }
///
/// // ❌ AVOID: Separate trait bounds when you need both
/// fn analyze_bound_verbose<B>(bound: &B) -> String
/// where
///     B: BoundSideChecks + BoundTypeChecks + AsRef<f64>
/// {
///     format!("Analysis complete") // More verbose for same functionality
/// }
/// ```
///
/// ### 2. **Combine with Other Traits**
/// ```rust
/// use grid1d::bounds::*;
///
/// // Common pattern: BoundChecks + value access
/// fn process_bound<B>(bound: &B) -> String
/// where
///     B: BoundChecks + AsRef<f64>,
/// {
///     // Full bound analysis with value access
///     format!("Processed bound")
/// }
///
/// // For constraint checking
/// fn validate_constraint<B>(bound: &B, test_value: f64) -> bool
/// where
///     B: BoundChecks + ValueWithinBound<RealType = f64>,
/// {
///     bound.value_within_bound(&test_value)
/// }
/// ```
///
/// ### 3. **Leverage Automatic Implementation**
/// ```rust
/// use grid1d::bounds::*;
///
/// // ✅ GOOD: Implement constituent traits, get BoundChecks for free
/// struct MyBound {
///     value: f64,
///     properties: (bool, bool), // (is_upper, is_closed)
/// }
///
/// impl BoundSideChecks for MyBound {
///     fn is_upper_bound(&self) -> bool { self.properties.0 }
/// }
///
/// impl BoundTypeChecks for MyBound {
///     fn includes_boundary(&self) -> bool { self.properties.1 }
/// }
///
/// // BoundChecks is now automatically available
/// ```
///
/// ## Mathematical Guarantees
///
/// The [`BoundChecks`] trait maintains all mathematical guarantees from its constituent traits:
///
/// - **Side Semantics**: Exactly one of `is_lower_bound()` or `is_upper_bound()` returns `true`
/// - **Type Semantics**: Exactly one of `is_open()` or `is_closed()` returns `true`
/// - **Consistency**: All methods return consistent results for the object's lifetime
/// - **Logical Relationships**: Complement relationships are preserved (`is_open() == !is_closed()`)
///
/// Use [`BoundChecks`] when you need comprehensive runtime boundary analysis while maintaining
/// mathematical correctness and type safety. This trait provides the most convenient interface
/// for generic programming over bounds with complete boundary semantics access.
pub trait BoundChecks: BoundTypeChecks + BoundSideChecks {}
//------------------------------------------------------------------------------------------------

//------------------------------------------------------------------------------------------------
/// Core interval boundary representation with compile-time semantics encoding.
///
/// [`IntervalBound<RealType, Side, Type>`] is the fundamental building block for all interval
/// boundaries in the grid1d library. It combines a scalar value with compile-time type
/// information that encodes both the boundary side (lower/upper) and inclusion semantics
/// (open/closed), providing mathematical correctness and zero-cost abstractions.
///
/// ## Design Philosophy
///
/// ### Type-Safe Boundary Semantics
///
/// Unlike traditional approaches that use boolean flags or runtime enums to indicate
/// boundary properties, [`IntervalBound`] encodes all semantic information in the type system:
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// // Type system prevents mixing incompatible boundaries
/// let lower_closed: LowerBoundClosed<f64> = IntervalBound::new(0.0);
/// let upper_open: UpperBoundOpen<f64> = IntervalBound::new(1.0);
///
/// // This would be a compile error:
/// // assert_eq!(lower_closed, upper_open); // Cannot compare different boundary types
/// ```
///
/// ### Zero-Cost Abstractions
///
/// The phantom types `Side` and `Type` have no runtime representation, allowing
/// the compiler to optimize boundary operations to direct scalar comparisons:
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// let bound = LowerBoundClosed::new(5.0);
/// // This compiles to a single comparison instruction:
/// let satisfies = bound.value_within_bound(&6.0); // 6.0 >= 5.0
/// ```
///
/// ## Type Parameters
///
/// ### `RealType: RealScalar`
///
/// The scalar type representing the boundary value. Must implement [`num_valid::RealScalar`]
/// for mathematical operations and validation:
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::{New, TryNew};
/// use num_valid::{RealNative64StrictFiniteInDebug};
///
/// // Different scalar types supported
/// let f64_bound: LowerBoundClosed<f64> = IntervalBound::new(1.0);
/// let validated_bound: LowerBoundClosed<RealNative64StrictFiniteInDebug> =
///     IntervalBound::new(RealNative64StrictFiniteInDebug::try_new(1.0).unwrap());
/// ```
///
/// ### `Side: BoundSide`
///
/// Compile-time marker indicating whether this is a lower or upper boundary:
/// - [`Lower`]: Defines minimum constraints (`x ≥ value` or `x > value`)
/// - [`Upper`]: Defines maximum constraints (`x ≤ value` or `x < value`)
///
/// ### `Type: BoundType`
///
/// Compile-time marker indicating boundary inclusion semantics:
/// - [`Closed`]: Includes the boundary value in the constraint
/// - [`Open`]: Excludes the boundary value from the constraint
///
/// ## Mathematical Semantics
///
/// The combination of `Side` and `Type` parameters defines the mathematical constraint:
///
/// | Side | Type | Mathematical Constraint | Notation | Example |
/// |------|------|------------------------|----------|---------|
/// | [`Lower`] | [`Closed`] | `x ≥ bound_value` | `[a` | `x ≥ 5` |
/// | [`Lower`] | [`Open`] | `x > bound_value` | `(a` | `x > 5` |
/// | [`Upper`] | [`Closed`] | `x ≤ bound_value` | `b]` | `x ≤ 10` |
/// | [`Upper`] | [`Open`] | `x < bound_value` | `b)` | `x < 10` |
///
/// ## Core Operations
///
/// ### Construction and Value Access
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::{New, IntoInner};
///
/// // Create a boundary
/// let bound = LowerBoundClosed::new(3.14);
///
/// // Access the value by reference
/// let value_ref: &f64 = bound.as_ref();
/// assert_eq!(*value_ref, 3.14);
///
/// // Extract the value (consuming the bound)
/// let value: f64 = bound.into_inner();
/// assert_eq!(value, 3.14);
/// ```
///
/// ### Constraint Testing
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// let lower_closed = LowerBoundClosed::new(5.0);  // x ≥ 5
/// let lower_open = LowerBoundOpen::new(5.0);      // x > 5
/// let upper_closed = UpperBoundClosed::new(10.0); // x ≤ 10
/// let upper_open = UpperBoundOpen::new(10.0);     // x < 10
///
/// // Test boundary values
/// assert!(lower_closed.value_within_bound(&5.0));   // 5 ≥ 5 → true
/// assert!(!lower_open.value_within_bound(&5.0));    // 5 > 5 → false
/// assert!(upper_closed.value_within_bound(&10.0));  // 10 ≤ 10 → true
/// assert!(!upper_open.value_within_bound(&10.0));   // 10 < 10 → false
///
/// // Test interior values
/// assert!(lower_closed.value_within_bound(&7.0));   // 7 ≥ 5 → true
/// assert!(lower_open.value_within_bound(&7.0));     // 7 > 5 → true
/// assert!(upper_closed.value_within_bound(&8.0));   // 8 ≤ 10 → true
/// assert!(upper_open.value_within_bound(&8.0));     // 8 < 10 → true
/// ```
///
/// ### Type Information Queries
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// let bound = LowerBoundClosed::new(0.0);
///
/// // Boundary inclusion semantics
/// assert!(bound.is_closed());
/// assert!(!bound.is_open());
/// assert!(bound.includes_boundary());
///
/// // Boundary side semantics
/// assert!(bound.is_lower_bound());
/// assert!(!bound.is_upper_bound());
/// ```
///
/// ## Transformation Operations
///
/// ### Type Conversion
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// let closed_bound = LowerBoundClosed::new(5.0);
///
/// // Convert to different boundary type (same side, different inclusion)
/// let open_bound: LowerBoundOpen<f64> = closed_bound.clone().with_bound_type();
/// assert_eq!(open_bound.as_ref(), closed_bound.as_ref()); // Same value
/// assert!(open_bound.is_open());  // Different inclusion semantics
///
/// // Create new bound with different value (same types)
/// let new_bound = closed_bound.with_value(10.0);
/// assert_eq!(new_bound.as_ref(), &10.0);
/// assert!(new_bound.is_closed()); // Same inclusion semantics
/// ```
///
/// ## Mathematical Ordering
///
/// Bounds are ordered by their values, providing intuitive mathematical ordering:
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// let bound_1 = LowerBoundClosed::new(1.0);
/// let bound_2 = LowerBoundClosed::new(2.0);
/// let bound_3 = LowerBoundClosed::new(3.0);
///
/// assert!(bound_1 < bound_2);
/// assert!(bound_2 < bound_3);
/// assert!(bound_1 < bound_3);
///
/// // Ordering respects mathematical semantics
/// use std::cmp::Ordering;
/// let ordering = bound_1.partial_cmp(&bound_2);
/// assert_eq!(ordering, Some(Ordering::Less));
/// ```
///
/// ## Generic Programming Support
///
/// ### Type-Parameterized Functions
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// fn create_symmetric_bounds<T, S, U>(center: T, radius: T) -> (IntervalBound<T, S, U>, IntervalBound<T, S, U>)
/// where
///     T: num_valid::RealScalar + std::ops::Sub<Output = T> + std::ops::Add<Output = T>,
///     S: BoundSide,
///     U: BoundType,
/// {
///     (
///         IntervalBound::new(center.clone() - radius.clone()),
///         IntervalBound::new(center + radius),
///     )
/// }
///
/// let (lower, upper) = create_symmetric_bounds::<_, Lower, Closed>(5.0, 2.0);
/// assert_eq!(*lower.as_ref(), 3.0);
/// assert_eq!(*upper.as_ref(), 7.0);
/// ```
///
/// ### Constraint Analysis
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// fn analyze_bounds<B: BoundChecks + AsRef<f64>>(bounds: &[B]) -> (f64, f64, usize, usize) {
///     let values: Vec<f64> = bounds.iter().map(|b| *b.as_ref()).collect();
///     let min_value = values.iter().fold(f64::INFINITY, |a, &b| a.min(b));
///     let max_value = values.iter().fold(f64::NEG_INFINITY, |a, &b| a.max(b));
///     
///     let (open_count, closed_count) = bounds.iter()
///         .fold((0, 0), |(open, closed), bound| {
///             if bound.is_open() { (open + 1, closed) } else { (open, closed + 1) }
///         });
///     
///     (min_value, max_value, open_count, closed_count)
/// }
///
/// let bounds = vec![
///     LowerBoundClosed::new(1.0),
///     LowerBoundClosed::new(2.0),
///     LowerBoundClosed::new(3.0),
/// ];
///
/// let (min, max, open, closed) = analyze_bounds(&bounds);
/// assert_eq!(min, 1.0);
/// assert_eq!(max, 3.0);
/// assert_eq!(open, 0);
/// assert_eq!(closed, 3);
/// ```
///
/// ## Integration with Grid1D
///
/// ### Interval Construction
///
/// ```rust
/// use grid1d::{bounds::*, intervals::*};
/// use try_create::{IntoInner, New};
///
/// let lower = LowerBoundClosed::new(0.0);
/// let upper = UpperBoundOpen::new(1.0);
///
/// // Convert bounds to interval
/// let interval = IntervalLowerClosedUpperOpen::new(
///     lower.into_inner(),
///     upper.into_inner()
/// );
///
/// // Test interval properties
/// assert!(interval.contains_point(&0.0));  // Includes lower (closed)
/// assert!(!interval.contains_point(&1.0)); // Excludes upper (open)
/// assert!(interval.contains_point(&0.5));  // Interior point
/// ```
///
/// ### Domain Specification
///
/// ```rust
/// use grid1d::{bounds::*, Grid1D, intervals::*, scalars::NumIntervals, IntervalPartition};
/// use try_create::{New, IntoInner, TryNew};
///
/// // Define domain using bounds
/// let domain_start = LowerBoundClosed::new(0.0);
/// let domain_end = UpperBoundClosed::new(10.0);
///
/// // Create interval from bounds
/// let domain = IntervalClosed::new(
///     domain_start.into_inner(),
///     domain_end.into_inner()
/// );
///
/// // Use in grid construction
/// let grid = Grid1D::uniform(domain, NumIntervals::try_new(100).unwrap());
/// assert_eq!(grid.num_intervals().as_ref(), &100);
/// ```
///
/// ## Performance Characteristics
///
/// ### Memory Layout
/// - **Size**: Same as `RealType` (phantom types have zero size)
/// - **Alignment**: Same as `RealType`
/// - **Padding**: No additional padding beyond the scalar value
///
/// ### Runtime Performance
/// | Operation | Complexity | Notes |
/// |-----------|------------|-------|
/// | Construction | O(1) | Debug assertion only |
/// | Value access | O(1) | Direct field access |
/// | Constraint testing | O(1) | Single comparison operation |
/// | Type queries | O(1) | Compile-time constants |
/// | Conversion | O(1) | Pure type manipulation |
///
/// ## Error Handling and Validation
///
/// ### Debug-Mode Validation
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// // In debug builds, construction validates finite values
/// #[cfg(debug_assertions)]
/// {
///     // This would panic in debug mode:
///     // let invalid = LowerBoundClosed::new(f64::NAN);
/// }
///
/// // In release builds, validation is skipped for performance
/// #[cfg(not(debug_assertions))]
/// {
///     let bound = LowerBoundClosed::new(f64::INFINITY); // Allowed in release
/// }
/// ```
///
/// ### Safe Construction Patterns
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::{New, TryNew};
/// use num_valid::RealNative64StrictFiniteInDebug;
///
/// // Use validated scalar types for automatic validation
/// type SafeScalar = RealNative64StrictFiniteInDebug;
///
/// fn create_safe_bound(value: f64) -> Result<LowerBoundClosed<SafeScalar>, <SafeScalar as TryNew>::Error> {
///     let validated_value = SafeScalar::try_new(value)?;
///     Ok(LowerBoundClosed::new(validated_value))
/// }
///
/// // Safe construction with validation
/// let safe_bound = create_safe_bound(5.0).unwrap();
/// assert_eq!(safe_bound.as_ref().as_ref(), &5.0);
/// ```
///
/// ## Advanced Usage Patterns
///
/// ### Custom Bound Types
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// // Specialized bounds for specific domains
/// type PositiveLowerBound<T> = LowerBoundOpen<T>;  // x > 0
/// type UnitUpperBound<T> = UpperBoundClosed<T>;    // x ≤ 1
/// type ProbabilityBound<T> = LowerBoundClosed<T>;  // p ≥ 0
///
/// fn create_probability_domain() -> (ProbabilityBound<f64>, UnitUpperBound<f64>) {
///     (ProbabilityBound::new(0.0), UnitUpperBound::new(1.0))
/// }
///
/// let (prob_lower, prob_upper) = create_probability_domain();
/// assert!(prob_lower.value_within_bound(&0.0));  // Probability can be 0
/// assert!(prob_upper.value_within_bound(&1.0));  // Probability can be 1
/// ```
///
/// ### Bound Collections and Analysis
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// // Heterogeneous bound collection (same side and type)
/// let lower_bounds = vec![
///     LowerBoundClosed::new(0.0),
///     LowerBoundClosed::new(1.0),
///     LowerBoundClosed::new(2.0),
/// ];
///
/// // Find the most restrictive (maximum) lower bound
/// let max_lower = lower_bounds.iter().max_by(|a, b| a.partial_cmp(b).unwrap()).unwrap();
/// assert_eq!(max_lower.as_ref(), &2.0);
///
/// // Test which bounds a value satisfies
/// let test_value = 1.5;
/// let satisfied: Vec<_> = lower_bounds.iter()
///     .enumerate()
///     .filter(|(_, bound)| bound.value_within_bound(&test_value))
///     .map(|(i, _)| i)
///     .collect();
///
/// assert_eq!(satisfied, vec![0, 1]); // Satisfies bounds 0 and 1, not 2
/// ```
///
/// ## Best Practices
///
/// ### 1. **Choose Appropriate Scalar Types**
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
/// use num_valid::RealNative64StrictFiniteInDebug;
///
/// // ✅ GOOD: Use validated types for safety-critical code
/// type SafeBound = LowerBoundClosed<RealNative64StrictFiniteInDebug>;
///
/// // ✅ GOOD: Use f64 for performance-critical code with known valid input
/// type FastBound = LowerBoundClosed<f64>;
/// ```
///
/// ### 2. **Leverage Compile-Time Type Safety**
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// // ✅ GOOD: Use specific types when semantics are known
/// fn create_unit_interval() -> (LowerBoundClosed<f64>, UpperBoundClosed<f64>) {
///     (LowerBoundClosed::new(0.0), UpperBoundClosed::new(1.0))
/// }
///
/// // ❌ AVOID: Using runtime types when compile-time types suffice
/// fn create_runtime_unit_interval() -> (LowerBoundRuntime<f64>, UpperBoundRuntime<f64>) {
///     (
///         IntervalBoundRuntime::Closed(LowerBoundClosed::new(0.0)),
///         IntervalBoundRuntime::Closed(UpperBoundClosed::new(1.0))
///     )
/// }
/// ```
///
/// ### 3. **Use Type Aliases for Domain-Specific Bounds**
/// ```rust
/// use grid1d::bounds::*;
///
/// // ✅ GOOD: Clear semantic meaning
/// type TemperatureLowerBound = LowerBoundClosed<f64>;  // T ≥ 0 (Kelvin)
/// type ProbabilityUpperBound = UpperBoundClosed<f64>;  // p ≤ 1
/// type StrictPositiveBound = LowerBoundOpen<f64>;      // x > 0
/// ```
///
/// ## Mathematical Guarantees
///
/// The [`IntervalBound`] struct maintains mathematical correctness by:
///
/// - **Type Safety**: Prevents mixing incompatible boundary types at compile time
/// - **Semantic Consistency**: Boundary inclusion/exclusion semantics are enforced by types
/// - **Value Integrity**: Debug assertions ensure finite values (configurable via scalar types)
/// - **Zero-Cost Abstractions**: Full type safety with no runtime overhead
/// - **Mathematical Ordering**: Proper ordering semantics for boundary comparisons
///
/// [`IntervalBound`] forms the foundation of the grid1d boundary system, providing
/// type-safe, mathematically correct, and performant boundary representations for
/// interval arithmetic and domain specification.
#[derive(Debug, Clone, AsRef, PartialEq, Eq, Serialize, Deserialize)]
#[serde(bound(deserialize = "RealType: for<'a> Deserialize<'a>"))]
pub struct IntervalBound<RealType: RealScalar, Side: BoundSide, Type: BoundType> {
    #[as_ref]
    pub(crate) value: RealType,

    _phantom: std::marker::PhantomData<(Side, Type)>,
}

impl<RealType: RealScalar, Side: BoundSide, Type: BoundType> PartialOrd
    for IntervalBound<RealType, Side, Type>
{
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        self.value.partial_cmp(&other.value)
    }
}

impl<RealType: RealScalar, Side: BoundSide, Type: BoundType> New
    for IntervalBound<RealType, Side, Type>
{
    #[inline(always)]
    fn new(value: RealType) -> Self {
        debug_assert!(
            value.is_finite(),
            "Bound value must be finite, got: {:?}",
            value
        );

        Self {
            value,
            _phantom: std::marker::PhantomData,
        }
    }
}

impl<RealType: RealScalar, Side: BoundSide, Type: BoundType> BoundTypeChecks
    for IntervalBound<RealType, Side, Type>
{
    /// Returns whether the boundary value is included in the interval
    #[inline(always)]
    fn includes_boundary(&self) -> bool {
        Type::includes_boundary()
    }
}

impl<RealType: RealScalar, Side: BoundSide, Type: BoundType> BoundSideChecks
    for IntervalBound<RealType, Side, Type>
{
    /// Returns whether this is an upper bound
    #[inline(always)]
    fn is_upper_bound(&self) -> bool {
        Side::is_upper()
    }
}

impl<RealType: RealScalar, Side: BoundSide, Type: BoundType> BoundChecks
    for IntervalBound<RealType, Side, Type>
{
}

impl<RealType: RealScalar, Side: BoundSide, Type: BoundType> IntervalBound<RealType, Side, Type> {
    /// Convert this bound to a (possibly) different [`BoundType`] while keeping the same value
    #[inline(always)]
    pub fn with_bound_type<NewType: BoundType>(self) -> IntervalBound<RealType, Side, NewType> {
        IntervalBound {
            value: self.value,
            _phantom: std::marker::PhantomData,
        }
    }

    /// Create a new bound with the same type but different value
    #[inline(always)]
    pub fn with_value(&self, new_value: RealType) -> Self {
        Self::new(new_value)
    }

    /// Converts a bound to the opposite type (Open ↔ Closed) while preserving value and side.
    ///
    /// This function transforms an `IntervalBound<RealType, Side, Type>` to
    /// `IntervalBound<RealType, Side, Type::Opposite>`, changing `Open` ↔ `Closed`
    /// at compile-time while keeping the side (`Lower` or `Upper`) unchanged.
    ///
    /// The return type is automatically inferred from the [`BoundType::Opposite`] associated
    /// type, so no turbofish syntax is needed.
    ///
    /// # Type Transformations
    ///
    /// | Input Type | Output Type |
    /// |------------|-------------|
    /// | `IntervalBound<T, Side, Open>` | `IntervalBound<T, Side, Closed>` |
    /// | `IntervalBound<T, Side, Closed>` | `IntervalBound<T, Side, Open>` |
    ///
    /// # Use Cases
    ///
    /// - Adjusting interval boundaries in set operations
    /// - Converting between boundary inclusion/exclusion semantics
    /// - Implementing boundary transformations in computational geometry
    /// - Handling boundary conditions in numerical algorithms
    ///
    /// # Examples
    ///
    /// ## Basic Transformation
    ///
    /// ```rust
    /// use grid1d::bounds::*;
    /// use try_create::New;
    ///
    /// // Open to Closed
    /// let open: LowerBound<f64, Open> = IntervalBound::new(5.0);
    /// let closed: LowerBound<f64, Closed> = open.flip_bound_type();
    ///
    /// assert_eq!(*closed.as_ref(), 5.0);
    /// assert!(closed.includes_boundary());
    ///
    /// // Closed to Open
    /// let closed: UpperBound<f64, Closed> = IntervalBound::new(10.0);
    /// let open: UpperBound<f64, Open> = closed.flip_bound_type();
    ///
    /// assert_eq!(*open.as_ref(), 10.0);
    /// assert!(!open.includes_boundary());
    /// ```
    ///
    /// ## Round-Trip Property
    ///
    /// ```rust
    /// use grid1d::bounds::*;
    /// use try_create::New;
    ///
    /// let original: LowerBound<f64, Closed> = IntervalBound::new(2.5);
    /// let flipped: LowerBound<f64, Open> = original.flip_bound_type();
    /// let restored: LowerBound<f64, Closed> = flipped.flip_bound_type();
    ///
    /// assert_eq!(*restored.as_ref(), 2.5);
    /// assert!(restored.includes_boundary());
    /// ```
    ///
    /// ## Interval Boundary Adjustment
    ///
    /// ```rust
    /// use grid1d::bounds::*;
    /// use try_create::New;
    ///
    /// // When computing interval difference A \ B, we often need to flip boundaries
    /// let b_lower: LowerBound<f64, Closed> = IntervalBound::new(3.0);  // [3
    /// let left_upper: LowerBound<f64, Open> = b_lower.flip_bound_type(); // (3
    ///
    /// assert!(!left_upper.includes_boundary());
    /// assert_eq!(*left_upper.as_ref(), 3.0);
    /// ```
    ///
    /// # Performance
    ///
    /// - **Time Complexity**: O(1) - compile-time type transformation
    /// - **Memory**: Zero-cost abstraction - no runtime overhead
    /// - **Inlining**: Marked `#[inline(always)]` for optimal performance
    ///
    /// # See Also
    ///
    /// - [`flip_bound_side`](Self::flip_bound_side) - Change Lower ↔ Upper
    /// - [`flip_bound_side_and_type`](Self::flip_bound_side_and_type) - Both transformations
    /// - [`with_bound_type`](Self::with_bound_type) - Convert to arbitrary bound type
    #[inline(always)]
    pub fn flip_bound_type(self) -> IntervalBound<RealType, Side, Type::Opposite> {
        IntervalBound {
            value: self.value,
            _phantom: std::marker::PhantomData,
        }
    }

    /// Converts a bound to the opposite side while preserving the open/closed type.
    ///
    /// This function transforms an `IntervalBound<RealType, Side, Type>` to
    /// `IntervalBound<RealType, Side::Opposite, Type>`, changing `Lower` ↔ `Upper`
    /// at compile-time while keeping the open/closed type unchanged.
    ///
    /// The return type is automatically inferred from the [`BoundSide::Opposite`] associated
    /// type, so no turbofish syntax is needed.
    ///
    /// # Type Transformations
    ///
    /// | Input Type | Output Type |
    /// |------------|-------------|
    /// | `LowerBound<T, Type>` | `UpperBound<T, Type>` |
    /// | `UpperBound<T, Type>` | `LowerBound<T, Type>` |
    ///
    /// More specifically:
    ///
    /// | Input Type | Output Type |
    /// |------------|-------------|
    /// | `LowerBound<T, Open>` | `UpperBound<T, Open>` |
    /// | `LowerBound<T, Closed>` | `UpperBound<T, Closed>` |
    /// | `UpperBound<T, Open>` | `LowerBound<T, Open>` |
    /// | `UpperBound<T, Closed>` | `LowerBound<T, Closed>` |
    ///
    /// # Use Cases
    ///
    /// - Converting bounds between interval endpoints
    /// - Implementing interval set operations (difference, complement)
    /// - Transforming boundary constraints in optimization problems
    /// - Handling bound conversions in computational geometry
    ///
    /// # Examples
    ///
    /// ## Converting Lower to Upper Bounds
    ///
    /// ```rust
    /// use grid1d::bounds::*;
    /// use try_create::New;
    ///
    /// // Open: Lower to Upper
    /// let lower_open: LowerBound<f64, Open> = IntervalBound::new(5.0);
    /// let upper_open: UpperBound<f64, Open> = lower_open.flip_bound_side();
    /// assert_eq!(*upper_open.as_ref(), 5.0);
    /// assert!(!upper_open.includes_boundary());
    ///
    /// // Closed: Lower to Upper
    /// let lower_closed: LowerBound<f64, Closed> = IntervalBound::new(10.0);
    /// let upper_closed: UpperBound<f64, Closed> = lower_closed.flip_bound_side();
    /// assert_eq!(*upper_closed.as_ref(), 10.0);
    /// assert!(upper_closed.includes_boundary());
    /// ```
    ///
    /// ## Converting Upper to Lower Bounds
    ///
    /// ```rust
    /// use grid1d::bounds::*;
    /// use try_create::New;
    ///
    /// // Open: Upper to Lower
    /// let upper_open: UpperBound<f64, Open> = IntervalBound::new(20.0);
    /// let lower_open: LowerBound<f64, Open> = upper_open.flip_bound_side();
    /// assert_eq!(*lower_open.as_ref(), 20.0);
    /// assert!(!lower_open.includes_boundary());
    ///
    /// // Closed: Upper to Lower
    /// let upper_closed: UpperBound<f64, Closed> = IntervalBound::new(15.0);
    /// let lower_closed: LowerBound<f64, Closed> = upper_closed.flip_bound_side();
    /// assert_eq!(*lower_closed.as_ref(), 15.0);
    /// assert!(lower_closed.includes_boundary());
    /// ```
    ///
    /// ## Combining with Type Flipping
    ///
    /// ```rust
    /// use grid1d::bounds::*;
    /// use try_create::New;
    ///
    /// // Start with a closed lower bound
    /// let lower_closed: LowerBound<f64, Closed> = IntervalBound::new(3.0);
    ///
    /// // Convert to open upper bound (both transformations)
    /// let upper_open: UpperBound<f64, Open> = lower_closed
    ///     .flip_bound_side()  // Lower Closed -> Upper Closed
    ///     .flip_bound_type(); // Upper Closed -> Upper Open
    ///
    /// assert_eq!(*upper_open.as_ref(), 3.0);
    /// assert!(!upper_open.includes_boundary());
    /// ```
    ///
    /// ## Use in Interval Operations
    ///
    /// ```rust
    /// use grid1d::bounds::*;
    /// use try_create::New;
    ///
    /// // When computing A \ B, if B has lower bound [5, we need to use it as upper bound
    /// let b_lower: LowerBound<f64, Closed> = IntervalBound::new(5.0);
    ///
    /// // Convert to upper bound for the left remainder
    /// let result_upper: UpperBound<f64, Closed> = b_lower.flip_bound_side();
    ///
    /// assert!(result_upper.includes_boundary());
    /// assert_eq!(*result_upper.as_ref(), 5.0);
    /// ```
    ///
    /// # Performance
    ///
    /// - **Time Complexity**: O(1) - compile-time type transformation
    /// - **Memory**: Zero-cost abstraction - no runtime overhead
    /// - **Inlining**: Marked `#[inline(always)]` for optimal performance
    ///
    /// # See Also
    ///
    /// - [`flip_bound_type`](Self::flip_bound_type) - Change Open ↔ Closed
    /// - [`flip_bound_side_and_type`](Self::flip_bound_side_and_type) - Both transformations
    #[inline(always)]
    pub fn flip_bound_side(self) -> IntervalBound<RealType, Side::Opposite, Type> {
        IntervalBound {
            value: self.value,
            _phantom: std::marker::PhantomData,
        }
    }

    /// Converts a bound to the opposite side AND flips the open/closed type.
    ///
    /// This is a convenience function that combines [`flip_bound_side`](Self::flip_bound_side)
    /// and [`flip_bound_type`](Self::flip_bound_type) in a single operation, performing
    /// both transformations at compile-time:
    /// - Changes `Lower` ↔ `Upper` (side transformation)
    /// - Changes `Open` ↔ `Closed` (type transformation)
    ///
    /// This is equivalent to calling `self.flip_bound_side().flip_bound_type()` but
    /// more concise and expresses the intent clearly.
    ///
    /// # Type Transformations
    ///
    /// | Input Type | Output Type |
    /// |------------|-------------|
    /// | `LowerBound<T, Open>` | `UpperBound<T, Closed>` |
    /// | `LowerBound<T, Closed>` | `UpperBound<T, Open>` |
    /// | `UpperBound<T, Open>` | `LowerBound<T, Closed>` |
    /// | `UpperBound<T, Closed>` | `LowerBound<T, Open>` |
    ///
    /// # Mathematical Motivation
    ///
    /// In interval arithmetic, particularly in set difference operations, when you
    /// exclude a portion of an interval, the complementary bound needs both transformations:
    ///
    /// - If the excluded interval has a **closed** lower bound `[a`, the remaining part
    ///   needs an **open** upper bound `)a` to avoid including the boundary
    /// - If the excluded interval has an **open** lower bound `(a`, the remaining part
    ///   needs a **closed** upper bound `]a` to include what was excluded
    ///
    /// This is precisely the transformation this function performs.
    ///
    /// # Use Cases
    ///
    /// - Essential for interval difference operations (`A \ B`)
    /// - Computing interval complements
    /// - Boundary adjustment in set operations
    /// - Transforming constraints in optimization problems
    ///
    /// # Examples
    ///
    /// ## Basic Transformations
    ///
    /// ```rust
    /// use grid1d::bounds::*;
    /// use try_create::New;
    ///
    /// // Lower Closed -> Upper Open
    /// let lower_closed: LowerBound<f64, Closed> = IntervalBound::new(5.0);
    /// let upper_open: UpperBound<f64, Open> = lower_closed.flip_bound_side_and_type();
    /// assert_eq!(*upper_open.as_ref(), 5.0);
    /// assert!(!upper_open.includes_boundary());
    ///
    /// // Lower Open -> Upper Closed
    /// let lower_open: LowerBound<f64, Open> = IntervalBound::new(10.0);
    /// let upper_closed: UpperBound<f64, Closed> = lower_open.flip_bound_side_and_type();
    /// assert_eq!(*upper_closed.as_ref(), 10.0);
    /// assert!(upper_closed.includes_boundary());
    ///
    /// // Upper Closed -> Lower Open
    /// let upper_closed: UpperBound<f64, Closed> = IntervalBound::new(15.0);
    /// let lower_open: LowerBound<f64, Open> = upper_closed.flip_bound_side_and_type();
    /// assert_eq!(*lower_open.as_ref(), 15.0);
    /// assert!(!lower_open.includes_boundary());
    ///
    /// // Upper Open -> Lower Closed
    /// let upper_open: UpperBound<f64, Open> = IntervalBound::new(20.0);
    /// let lower_closed: LowerBound<f64, Closed> = upper_open.flip_bound_side_and_type();
    /// assert_eq!(*lower_closed.as_ref(), 20.0);
    /// assert!(lower_closed.includes_boundary());
    /// ```
    ///
    /// ## Interval Difference: Complete Example
    ///
    /// ```rust
    /// use grid1d::bounds::*;
    /// use try_create::New;
    ///
    /// // Computing A \ B where:
    /// // A = [0, 10]
    /// // B = [5, 7]
    /// // Result should be [0, 5) ∪ (7, 10]
    ///
    /// // B's lower bound is [5 (closed), so left remainder needs )5 (open upper)
    /// let b_lower: LowerBound<f64, Closed> = IntervalBound::new(5.0);
    /// let left_upper: UpperBound<f64, Open> = b_lower.flip_bound_side_and_type();
    /// assert_eq!(*left_upper.as_ref(), 5.0);
    /// assert!(!left_upper.includes_boundary());
    /// // This creates [0, 5) - the left part of A \ B
    ///
    /// // B's upper bound is ]7 (closed), so right remainder needs (7 (open lower)
    /// let b_upper: UpperBound<f64, Closed> = IntervalBound::new(7.0);
    /// let right_lower: LowerBound<f64, Open> = b_upper.flip_bound_side_and_type();
    /// assert_eq!(*right_lower.as_ref(), 7.0);
    /// assert!(!right_lower.includes_boundary());
    /// // This creates (7, 10] - the right part of A \ B
    /// ```
    ///
    /// ## Comparison with Sequential Operations
    ///
    /// ```rust
    /// use grid1d::bounds::*;
    /// use try_create::New;
    ///
    /// let bound: LowerBound<f64, Closed> = IntervalBound::new(3.0);
    ///
    /// // These are equivalent:
    /// let result1: UpperBound<f64, Open> = bound.clone().flip_bound_side_and_type();
    /// let result2: UpperBound<f64, Open> = bound.flip_bound_side().flip_bound_type();
    ///
    /// assert_eq!(*result1.as_ref(), *result2.as_ref());
    /// assert_eq!(result1.includes_boundary(), result2.includes_boundary());
    /// ```
    ///
    /// ## Involutive Property
    ///
    /// Applying this transformation twice returns to the original state:
    ///
    /// ```rust
    /// use grid1d::bounds::*;
    /// use try_create::New;
    ///
    /// let original: LowerBound<f64, Closed> = IntervalBound::new(7.5);
    /// let original_value = *original.as_ref();
    /// let includes = original.includes_boundary();
    ///
    /// // Apply transformation twice
    /// let transformed: UpperBound<f64, Open> = original.flip_bound_side_and_type();
    /// let back: LowerBound<f64, Closed> = transformed.flip_bound_side_and_type();
    ///
    /// // Returns to original type and value
    /// assert_eq!(*back.as_ref(), original_value);
    /// assert_eq!(back.includes_boundary(), includes);
    /// ```
    ///
    /// # Performance
    ///
    /// - **Time Complexity**: O(1) - compile-time type transformation
    /// - **Memory**: Zero-cost abstraction - no runtime overhead
    /// - **Inlining**: Marked `#[inline(always)]` for optimal performance
    /// - **Optimization**: Compiler optimizes to a simple value move
    ///
    /// # See Also
    ///
    /// - [`flip_bound_side`](Self::flip_bound_side) - Change only the side (Lower ↔ Upper)
    /// - [`flip_bound_type`](Self::flip_bound_type) - Change only the type (Open ↔ Closed)
    #[inline(always)]
    pub fn flip_bound_side_and_type(
        self,
    ) -> IntervalBound<RealType, Side::Opposite, Type::Opposite> {
        IntervalBound {
            value: self.value,
            _phantom: std::marker::PhantomData,
        }
    }
}

impl<RealType: RealScalar, Side: BoundSide, Type: BoundType> IntoInner
    for IntervalBound<RealType, Side, Type>
{
    type InnerType = RealType;

    #[inline(always)]
    fn into_inner(self) -> Self::InnerType {
        self.value
    }
}

/// Trait for testing if a value falls within the constraint defined by a bound
pub trait ValueWithinBound: Sized {
    /// The scalar type used for the bound value.
    type RealType: RealScalar;

    /// Test if a value falls within the bound's constraint
    ///
    /// Returns `true` if the value satisfies the bound's constraint:
    /// - For [`LowerBoundOpen`]: `value > bound_value`
    /// - For [`LowerBoundClosed`]: `value >= bound_value`  
    /// - For [`UpperBoundOpen`]: `value < bound_value`
    /// - For [`UpperBoundClosed`]: `value <= bound_value`
    fn value_within_bound(&self, x: &Self::RealType) -> bool;
}

#[duplicate_item(
    BoundSide BoundType ordering_func;
    [Lower]   [Open]    [gt]; // x > value
    [Lower]   [Closed]  [ge]; // x >= value
    [Upper]   [Open]    [lt]; // x < value
    [Upper]   [Closed]  [le]; // x <= value
)]
impl<RealType: RealScalar> ValueWithinBound for IntervalBound<RealType, BoundSide, BoundType> {
    type RealType = RealType;

    #[inline(always)]
    fn value_within_bound(&self, x: &Self::RealType) -> bool {
        PartialOrd::ordering_func(x, &self.value)
    }
}
//------------------------------------------------------------------------------------------------

//------------------------------------------------------------------------------------------------
/// Generic lower bound type alias that parameterizes over boundary inclusion semantics.
///
/// [`LowerBound<RealType, BoundType>`] is a convenience type alias for [`IntervalBound<RealType, Lower, BoundType>`]
/// that simplifies the specification of lower boundaries while maintaining full type safety.
/// This alias enables more concise generic programming over different lower boundary types.
///
/// ## Type Parameters
///
/// - **`RealType`**: Any scalar type implementing [`num_valid::RealScalar`]
/// - **`BoundType`**: Either [`Open`] (exclusive) or [`Closed`] (inclusive) boundary semantics
///
/// ## Mathematical Properties
///
/// Lower bounds define the minimum constraint for intervals and domains:
///
/// | BoundType | Mathematical Constraint | Notation | Semantics |
/// |-----------|------------------------|----------|-----------|
/// | [`Open`]  | `x > lower_value`      | `(a`     | Excludes boundary value |
/// | [`Closed`]| `x ≥ lower_value`      | `[a`     | Includes boundary value |
///
/// ## Generic Programming Benefits
///
/// This type alias enables writing generic functions that work with any lower bound type:
///
/// ```rust
/// use grid1d::bounds::*;
/// use num_valid::RealScalar;
/// use try_create::New;
///
/// fn analyze_lower_bound<T, B>(bound: &LowerBound<T, B>) -> String
/// where
///     T: RealScalar + std::fmt::Display,
///     B: BoundType,
/// {
///     format!(
///         "Lower bound: {} ({})",
///         bound.as_ref(),
///         if bound.is_closed() { "inclusive" } else { "exclusive" }
///     )
/// }
///
/// // Works with both open and closed lower bounds
/// let closed_bound = LowerBoundClosed::new(5.0);
/// let open_bound = LowerBoundOpen::new(5.0);
///
/// println!("{}", analyze_lower_bound(&closed_bound)); // "Lower bound: 5 (inclusive)"
/// println!("{}", analyze_lower_bound(&open_bound));   // "Lower bound: 5 (exclusive)"
/// ```
///
/// ## Concrete Type Instantiations
///
/// The most commonly used concrete types are:
///
/// ```rust
/// use grid1d::bounds::*;
///
/// // These are equivalent type specifications:
/// type LowerBoundClosedF64 = LowerBound<f64, Closed>;  // Using alias
/// type LowerBoundClosedF64Alt = LowerBoundClosed<f64>;  // Direct alias
///
/// type LowerBoundOpenF64 = LowerBound<f64, Open>;      // Using alias
/// type LowerBoundOpenF64Alt = LowerBoundOpen<f64>;     // Direct alias
/// ```
///
/// ## Usage in Generic Interval Construction
///
/// This type alias is particularly useful when building generic interval types:
///
/// ```rust
/// use grid1d::bounds::*;
/// use num_valid::RealScalar;
///
/// // Generic interval with parameterized lower bound type
/// struct GenericInterval<T, LowerType, UpperType>
/// where
///     T: RealScalar,
///     LowerType: BoundType,
///     UpperType: BoundType,
/// {
///     lower: LowerBound<T, LowerType>,
///     upper: UpperBound<T, UpperType>,
/// }
///
/// // This enables flexible interval construction while maintaining type safety
/// type ClosedInterval<T> = GenericInterval<T, Closed, Closed>;     // [a, b]
/// type OpenInterval<T> = GenericInterval<T, Open, Open>;           // (a, b)
/// type HalfOpenInterval<T> = GenericInterval<T, Closed, Open>;     // [a, b)
/// ```
///
/// ## Trait Implementations
///
/// All [`IntervalBound`] functionality is available through this alias:
///
/// - **[`ValueWithinBound`]**: Test if values satisfy the bound constraint
/// - **[`BoundTypeConversion`]**: Convert between open and closed semantics
/// - **[`BoundSideConversion`]**: Convert to upper bounds (maintaining boundary type)
/// - **[`AsRef<RealType>`]**: Zero-cost access to the underlying scalar value
/// - **[`IntoInner`]**: Extract the underlying scalar value
///
/// ## Performance Characteristics
///
/// - **Zero Runtime Cost**: Type alias disappears completely at compile time
/// - **Memory Layout**: Identical to the underlying scalar type (e.g., 8 bytes for `f64`)
/// - **Generic Specialization**: Compiler generates optimized code for each concrete instantiation
/// - **Inline Optimization**: All operations inline to direct scalar operations
pub type LowerBound<RealType, BoundType> = IntervalBound<RealType, Lower, BoundType>;

/// Generic upper bound type alias that parameterizes over boundary inclusion semantics.
///
/// [`UpperBound<RealType, BoundType>`] is a convenience type alias for [`IntervalBound<RealType, Upper, BoundType>`]
/// that simplifies the specification of upper boundaries while maintaining full type safety.
/// This alias enables more concise generic programming over different upper boundary types.
///
/// ## Type Parameters
///
/// - **`RealType`**: Any scalar type implementing [`num_valid::RealScalar`]
/// - **`BoundType`**: Either [`Open`] (exclusive) or [`Closed`] (inclusive) boundary semantics
///
/// ## Mathematical Properties
///
/// Upper bounds define the maximum constraint for intervals and domains:
///
/// | BoundType | Mathematical Constraint | Notation | Semantics |
/// |-----------|------------------------|----------|-----------|
/// | [`Open`]  | `x < upper_value`      | `b)`     | Excludes boundary value |
/// | [`Closed`]| `x ≤ upper_value`      | `b]`     | Includes boundary value |
///
/// ## Generic Programming Benefits
///
/// This type alias enables writing generic functions that work with any upper bound type:
///
/// ```rust
/// use grid1d::bounds::*;
/// use num_valid::RealScalar;
/// use try_create::New;
///
/// fn analyze_upper_bound<T, B>(bound: &UpperBound<T, B>) -> String
/// where
///     T: RealScalar + std::fmt::Display,
///     B: BoundType,
/// {
///     format!(
///         "Upper bound: {} ({})",
///         bound.as_ref(),
///         if bound.is_closed() { "inclusive" } else { "exclusive" }
///     )
/// }
///
/// // Works with both open and closed upper bounds
/// let closed_bound = UpperBoundClosed::new(10.0);
/// let open_bound = UpperBoundOpen::new(10.0);
///
/// println!("{}", analyze_upper_bound(&closed_bound)); // "Upper bound: 10 (inclusive)"
/// println!("{}", analyze_upper_bound(&open_bound));   // "Upper bound: 10 (exclusive)"
/// ```
///
/// ## Concrete Type Instantiations
///
/// The most commonly used concrete types are:
///
/// ```rust
/// use grid1d::bounds::*;
///
/// // These are equivalent type specifications:
/// type UpperBoundClosedF64 = UpperBound<f64, Closed>;  // Using alias
/// type UpperBoundClosedF64Alt = UpperBoundClosed<f64>; // Direct alias
///
/// type UpperBoundOpenF64 = UpperBound<f64, Open>;      // Using alias
/// type UpperBoundOpenF64Alt = UpperBoundOpen<f64>;     // Direct alias
/// ```
///
/// ## Mathematical Ordering Semantics
///
/// Upper bounds follow mathematically correct ordering where "tighter" bounds are considered smaller:
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// let open_upper = UpperBoundOpen::new(5.0);    // 5)
/// let closed_upper = UpperBoundClosed::new(5.0); // 5]
///
/// // Open bound is "tighter" (excludes more) than closed bound
/// assert!(open_upper < closed_upper);  // 5) < 5] → true
///
/// // This ordering ensures correct interval intersection behavior:
/// // intersection([0, 5)) ∩ [0, 5]) = [0, 5) (the tighter constraint wins)
/// ```
///
/// ## Constraint Validation
///
/// Upper bounds provide constraint checking through the [`ValueWithinBound`] trait:
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// let open_upper = UpperBoundOpen::new(10.0);
/// let closed_upper = UpperBoundClosed::new(10.0);
///
/// // Test boundary value behavior
/// assert!(!open_upper.value_within_bound(&10.0));   // 10 < 10 → false
/// assert!(closed_upper.value_within_bound(&10.0));  // 10 ≤ 10 → true
///
/// // Test interior values
/// assert!(open_upper.value_within_bound(&9.5));     // 9.5 < 10 → true
/// assert!(closed_upper.value_within_bound(&9.5));   // 9.5 ≤ 10 → true
/// ```
///
/// ## Trait Implementations
///
/// All [`IntervalBound`] functionality is available through this alias:
///
/// - **[`ValueWithinBound`]**: Test if values satisfy the bound constraint
/// - **[`BoundTypeConversion`]**: Convert between open and closed semantics
/// - **[`BoundSideConversion`]**: Convert to lower bounds (maintaining boundary type)
/// - **[`AsRef<RealType>`]**: Zero-cost access to the underlying scalar value
/// - **[`IntoInner`]**: Extract the underlying scalar value
///
/// ## Performance Characteristics
///
/// - **Zero Runtime Cost**: Type alias disappears completely at compile time
/// - **Memory Layout**: Identical to the underlying scalar type (e.g., 8 bytes for `f64`)
/// - **Generic Specialization**: Compiler generates optimized code for each concrete instantiation
/// - **Inline Optimization**: All operations inline to direct scalar operations
pub type UpperBound<RealType, BoundType> = IntervalBound<RealType, Upper, BoundType>;
//------------------------------------------------------------------------------------------------

//------------------------------------------------------------------------------------------------
/// An open lower bound where the boundary value is excluded from the interval.
///
/// Represents the lower boundary of mathematical objects like intervals `(a, b)` and grids
/// where the starting point `a` is approached but not included. This enforces the strict
/// inequality `x > a` for any point `x` within the bounded region.
///
/// ## Mathematical Properties
///
/// - **Notation**: `(a` in interval notation
/// - **Semantics**: Excludes the boundary value
/// - **Containment**: Point `x` satisfies `x > a`
/// - **Ordering**: `LowerBoundClosed::new(a) < LowerBoundOpen::new(a)` (closed is "tighter")
///
/// ## When to Use
///
/// - **Strict constraints**: Modeling `x > threshold` relationships
/// - **Asymptotic domains**: Functions with singularities at the boundary
/// - **Grid partitioning**: Half-open ranges like `(0, 1]`, `(1, 2]`, etc.
/// - **Limit approaches**: Domains where the boundary is approached but never reached
///
/// ## Compatible Interval Types
///
/// This bound type is used in constructing:
/// - [`IntervalOpen`](crate::intervals::IntervalOpen): `(a, b)` - both bounds open
/// - [`IntervalLowerOpenUpperClosed`](crate::intervals::IntervalLowerOpenUpperClosed): `(a, b]` - lower open, upper closed
/// - [`IntervalLowerOpenUpperUnbounded`](crate::intervals::IntervalLowerOpenUpperUnbounded): `(a, +∞)` - extends infinitely upward
///
/// ## Examples
///
/// ```rust
/// use grid1d::{bounds::*, intervals::*};
/// use try_create::New;
///
/// let bound = LowerBoundOpen::new(0.0);  // (0
///
/// // Used in strict inequality contexts
/// let test_point = 0.0;
/// let is_inside = test_point > *bound.as_ref(); // false (0 > 0 → false)
///
/// // Construct intervals using this bound type
/// let open_interval = IntervalOpen::new(0.0, 1.0);              // (0, 1)
/// let half_open = IntervalLowerOpenUpperClosed::new(0.0, 1.0);  // (0, 1]
/// let unbounded = IntervalLowerOpenUpperUnbounded::new(0.0);    // (0, +∞)
///
/// // All exclude the lower bound value
/// assert!(!open_interval.contains_point(&0.0));
/// assert!(!half_open.contains_point(&0.0));
/// assert!(!unbounded.contains_point(&0.0));
/// ```
pub type LowerBoundOpen<RealType> = LowerBound<RealType, Open>;

/// A closed lower bound where the boundary value is included in the interval.
///
/// Represents the lower boundary of mathematical objects like intervals `[a, b]` and grids
/// where the starting point `a` is part of the bounded region. This enforces the inclusive
/// inequality `x ≥ a` for any point `x` within the bounded region.
///
/// ## Mathematical Properties
///
/// - **Notation**: `[a` in interval notation
/// - **Semantics**: Includes the boundary value
/// - **Containment**: Point `x` satisfies `x ≥ a`
/// - **Ordering**: `LowerBoundClosed::new(a) < LowerBoundOpen::new(a)` (closed is "tighter")
///
/// ## When to Use
///
/// - **Inclusive constraints**: Modeling `x ≥ minimum` relationships
/// - **Physical boundaries**: Achievable minimum values (e.g., temperature ≥ 0K)
/// - **Standard intervals**: Closed intervals `[a, b]` for integration, optimization
/// - **Array-like indexing**: When the starting index should be included
///
/// ## Compatible Interval Types
///
/// This bound type is used in constructing:
/// - [`IntervalClosed`](crate::intervals::IntervalClosed): `[a, b]` - both bounds closed
/// - [`IntervalLowerClosedUpperOpen`](crate::intervals::IntervalLowerClosedUpperOpen): `[a, b)` - lower closed, upper open
/// - [`IntervalLowerClosedUpperUnbounded`](crate::intervals::IntervalLowerClosedUpperUnbounded): `[a, +∞)` - extends infinitely upward
///
/// ## Examples
///
/// ```rust
/// use grid1d::{bounds::*, intervals::*};
/// use try_create::New;
///
/// let bound = LowerBoundClosed::new(0.0);  // [0
///
/// // Used in inclusive constraint contexts
/// let test_point = 0.0;
/// let is_inside = test_point >= *bound.as_ref(); // true (0 ≥ 0 → true)
///
/// // Construct intervals using this bound type
/// let closed_interval = IntervalClosed::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 include the lower bound value
/// assert!(closed_interval.contains_point(&0.0));
/// assert!(half_open.contains_point(&0.0));
/// assert!(unbounded.contains_point(&0.0));
/// ```
pub type LowerBoundClosed<RealType> = LowerBound<RealType, Closed>;

/// An open upper bound where the boundary value is excluded from the interval.
///
/// Represents the upper boundary of mathematical objects like intervals `[a, b)` and grids
/// where the ending point `b` is approached but not included. This enforces the strict
/// inequality `x < b` for any point `x` within the bounded region.
///
/// ## Mathematical Properties
///
/// - **Notation**: `b)` in interval notation
/// - **Semantics**: Excludes the boundary value  
/// - **Containment**: Point `x` satisfies `x < b`
/// - **Ordering**: `UpperBoundOpen::new(b) < UpperBoundClosed::new(b)` (open is "smaller")
///
/// ## When to Use
///
/// - **Strict constraints**: Modeling `x < threshold` relationships
/// - **Grid partitioning**: Half-open ranges like `[0, 1)`, `[1, 2)`, etc.
/// - **Asymptotic limits**: Functions approaching a limit from below
/// - **Array-like indexing**: When the ending index should be excluded (like Rust ranges)
///
/// ## Compatible Interval Types
///
/// This bound type is used in constructing:
/// - [`IntervalOpen`](crate::intervals::IntervalOpen): `(a, b)` - both bounds open
/// - [`IntervalLowerClosedUpperOpen`](crate::intervals::IntervalLowerClosedUpperOpen): `[a, b)` - lower closed, upper open
/// - [`IntervalLowerUnboundedUpperOpen`](crate::intervals::IntervalLowerUnboundedUpperOpen): `(-∞, b)` - extends infinitely downward
///
/// ## Examples
///
/// ```rust
/// use grid1d::{bounds::*, intervals::*};
/// use try_create::New;
///
/// let bound = UpperBoundOpen::new(1.0);  // 1)
///
/// // Used in strict constraint contexts
/// let test_point = 1.0;
/// let is_inside = test_point < *bound.as_ref(); // false (1 < 1 → false)
///
/// // Construct intervals using this bound type - perfect for Rust-style ranges
/// let open_interval = IntervalOpen::new(0.0, 1.0);                      // (0, 1)
/// let half_open = IntervalLowerClosedUpperOpen::new(0.0, 1.0);          // [0, 1)
/// let unbounded = IntervalLowerUnboundedUpperOpen::new(1.0);            // (-∞, 1)
///
/// // All exclude the upper bound value
/// assert!(!open_interval.contains_point(&1.0));
/// assert!(!half_open.contains_point(&1.0));
/// assert!(!unbounded.contains_point(&1.0));
/// ```
pub type UpperBoundOpen<RealType> = UpperBound<RealType, Open>;

/// A closed upper bound where the boundary value is included in the interval.
///
/// Represents the upper boundary of mathematical objects like intervals `[a, b]` and grids
/// where the ending point `b` is part of the bounded region. This enforces the inclusive
/// inequality `x ≤ b` for any point `x` within the bounded region.
///
/// ## Mathematical Properties
///
/// - **Notation**: `b]` in interval notation
/// - **Semantics**: Includes the boundary value
/// - **Containment**: Point `x` satisfies `x ≤ b`
/// - **Ordering**: `UpperBoundOpen::new(b) < UpperBoundClosed::new(b)` (open is "smaller")
///
/// ## When to Use
///
/// - **Inclusive constraints**: Modeling `x ≤ maximum` relationships
/// - **Physical boundaries**: Achievable maximum values (e.g., temperature ≤ 100°C)
/// - **Standard intervals**: Closed intervals `[a, b]` for integration, analysis
/// - **Complete coverage**: When the ending boundary should be part of the domain
///
/// ## Compatible Interval Types
///
/// This bound type is used in constructing:
/// - [`IntervalClosed`](crate::intervals::IntervalClosed): `[a, b]` - both bounds closed
/// - [`IntervalLowerOpenUpperClosed`](crate::intervals::IntervalLowerOpenUpperClosed): `(a, b]` - lower open, upper closed
/// - [`IntervalLowerUnboundedUpperClosed`](crate::intervals::IntervalLowerUnboundedUpperClosed): `(-∞, b]` - extends infinitely downward
///
/// ## Examples
///
/// ```rust
/// use grid1d::{bounds::*, intervals::*};
/// use try_create::New;
///
/// let bound = UpperBoundClosed::new(1.0);  // 1]
///
/// // Used in inclusive constraint contexts
/// let test_point = 1.0;
/// let is_inside = test_point <= *bound.as_ref(); // true (1 ≤ 1 → true)
///
/// // Construct intervals using this bound type
/// let closed_interval = IntervalClosed::new(0.0, 1.0);                   // [0, 1]
/// let half_open = IntervalLowerOpenUpperClosed::new(0.0, 1.0);           // (0, 1]
/// let unbounded = IntervalLowerUnboundedUpperClosed::new(1.0);           // (-∞, 1]
///
/// // All include the upper bound value
/// assert!(closed_interval.contains_point(&1.0));
/// assert!(half_open.contains_point(&1.0));
/// assert!(unbounded.contains_point(&1.0));
/// ```
pub type UpperBoundClosed<RealType> = UpperBound<RealType, Closed>;

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

//------------------------------------------------------------------------------------------------
/// Convert between open and closed bounds while preserving boundary side and value.
///
/// [`BoundTypeConversion`] provides a unified interface for converting interval bounds
/// between open and closed inclusion semantics while maintaining the same boundary side
/// (lower/upper) and scalar value. This trait enables flexible boundary inclusion manipulation
/// in interval arithmetic and constraint specification operations.
///
/// ## Design Philosophy
///
/// ### Inclusion Semantic Transformation
///
/// Unlike side-changing transformations, [`BoundTypeConversion`] preserves the boundary
/// side and value while changing only the inclusion behavior:
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// let closed_lower = LowerBoundClosed::new(5.0);    // [5 (lower, closed)
/// let open_lower = closed_lower.into_open();        // (5 (lower, open)
///
/// // Side and value preserved, inclusion changed
/// assert_eq!(open_lower.as_ref(), &5.0);           // Same value
/// assert!(open_lower.is_lower_bound());            // Same side
/// assert!(open_lower.is_open());                   // Different inclusion
/// ```
///
/// ### Type-Safe Inclusion Conversions
///
/// All conversions are compile-time verified and zero-cost:
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// let open_upper = UpperBoundOpen::new(10.0);      // 10)
/// let closed_upper = open_upper.into_closed();     // 10]
///
/// // Type system ensures consistency
/// let _: UpperBoundClosed<f64> = closed_upper;     // Compile-time verification
/// ```
///
/// ## Mathematical Semantics
///
/// ### Open to Closed Conversion
///
/// Converting an open bound to a closed bound changes the inclusion behavior
/// while preserving the boundary value and side:
///
/// | Original Open Bound | Mathematical Constraint | Converted Closed Bound | New Constraint |
/// |-------------------|------------------------|---------------------|----------------|
/// | `LowerBoundOpen` | `x > value` | `LowerBoundClosed` | `x ≥ value` |
/// | `UpperBoundOpen` | `x < value` | `UpperBoundClosed` | `x ≤ value` |
///
/// ### Closed to Open Conversion
///
/// Converting a closed bound to an open bound changes the inclusion behavior
/// while preserving the boundary value and side:
///
/// | Original Closed Bound | Mathematical Constraint | Converted Open Bound | New Constraint |
/// |---------------------|------------------------|-------------------|----------------|
/// | `LowerBoundClosed` | `x ≥ value` | `LowerBoundOpen` | `x > value` |
/// | `UpperBoundClosed` | `x ≤ value` | `UpperBoundOpen` | `x < value` |
///
/// ## Core Methods
///
/// ### [`BoundTypeConversion::into_open()`]
///
/// Converts any bound to an open bound with the same value and side:
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// // Closed bounds to open bounds
/// let lower_closed = LowerBoundClosed::new(0.0);    // x ≥ 0
/// let lower_open = lower_closed.into_open();         // x > 0
/// assert_eq!(lower_open.as_ref(), &0.0);
/// assert!(lower_open.is_open());
/// assert!(lower_open.is_lower_bound());
///
/// let upper_closed = UpperBoundClosed::new(100.0);  // x ≤ 100
/// let upper_open = upper_closed.into_open();         // x < 100
/// assert_eq!(upper_open.as_ref(), &100.0);
/// assert!(upper_open.is_open());
/// assert!(upper_open.is_upper_bound());
///
/// // Open bounds (identity conversion)
/// let existing_open = LowerBoundOpen::new(25.0);
/// let same_open = existing_open.into_open();         // No change
/// assert_eq!(same_open.as_ref(), &25.0);
/// ```
///
/// ### [`BoundTypeConversion::into_closed()`]
///
/// Converts any bound to a closed bound with the same value and side:
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// // Open bounds to closed bounds
/// let lower_open = LowerBoundOpen::new(5.0);        // x > 5
/// let lower_closed = lower_open.into_closed();       // x ≥ 5
/// assert_eq!(lower_closed.as_ref(), &5.0);
/// assert!(lower_closed.is_closed());
/// assert!(lower_closed.is_lower_bound());
///
/// let upper_open = UpperBoundOpen::new(50.0);       // x < 50
/// let upper_closed = upper_open.into_closed();       // x ≤ 50
/// assert_eq!(upper_closed.as_ref(), &50.0);
/// assert!(upper_closed.is_closed());
/// assert!(upper_closed.is_upper_bound());
///
/// // Closed bounds (identity conversion)
/// let existing_closed = UpperBoundClosed::new(10.0);
/// let same_closed = existing_closed.into_closed();   // No change
/// assert_eq!(same_closed.as_ref(), &10.0);
/// ```
///
/// ## Practical Usage Patterns
///
/// ### Constraint Tightening and Loosening
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// // Tighten constraints (closed → open)
/// fn make_constraint_stricter<S: BoundSide>(
///     loose_bound: IntervalBound<f64, S, Closed>
/// ) -> IntervalBound<f64, S, Open> {
///     loose_bound.into_open()
/// }
///
/// let loose_lower = LowerBoundClosed::new(0.0);     // x ≥ 0 (includes 0)
/// let strict_lower = make_constraint_stricter(loose_lower.clone()); // x > 0 (excludes 0)
///
/// assert!(loose_lower.value_within_bound(&0.0));    // 0 ≥ 0 → true
/// assert!(!strict_lower.value_within_bound(&0.0));  // 0 > 0 → false
///
/// // Loosen constraints (open → closed)
/// fn make_constraint_more_permissive<S: BoundSide>(
///     strict_bound: IntervalBound<f64, S, Open>
/// ) -> IntervalBound<f64, S, Closed> {
///     strict_bound.into_closed()
/// }
///
/// let strict_upper = UpperBoundOpen::new(10.0);     // x < 10 (excludes 10)
/// let loose_upper = make_constraint_more_permissive(strict_upper.clone()); // x ≤ 10 (includes 10)
///
/// assert!(!strict_upper.value_within_bound(&10.0)); // 10 < 10 → false
/// assert!(loose_upper.value_within_bound(&10.0));   // 10 ≤ 10 → true
/// ```
///
/// ### Dynamic Inclusion Policy
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// // Adjust inclusion based on numerical context
/// fn create_robust_bound(
///     value: f64,
///     include_boundary: bool
/// ) -> (LowerBoundRuntime<f64>, UpperBoundRuntime<f64>) {
///     let lower_closed = LowerBoundClosed::new(value);
///     let upper_closed = UpperBoundClosed::new(value);
///     
///     if include_boundary {
///         (
///             IntervalBoundRuntime::Closed(lower_closed),
///             IntervalBoundRuntime::Closed(upper_closed)
///         )
///     } else {
///         (
///             IntervalBoundRuntime::Open(lower_closed.into_open()),
///             IntervalBoundRuntime::Open(upper_closed.into_open())
///         )
///     }
/// }
///
/// let (inclusive_lower, inclusive_upper) = create_robust_bound(0.0, true);
/// let (exclusive_lower, exclusive_upper) = create_robust_bound(0.0, false);
///
/// assert!(inclusive_lower.includes_boundary());
/// assert!(!exclusive_lower.includes_boundary());
/// ```
///
/// ### Interval Boundary Adjustment
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// // Adjust interval boundaries for different mathematical contexts
/// fn adjust_interval_strictness<T: BoundType>(
///     lower: LowerBound<f64, T>,
///     upper: UpperBound<f64, T>,
///     make_strict: bool
/// ) -> (LowerBoundRuntime<f64>, UpperBoundRuntime<f64>)
/// where
///     LowerBound<f64, T>: BoundTypeConversion<f64, Lower>,
///     UpperBound<f64, T>: BoundTypeConversion<f64, Upper>,
/// {
///     if make_strict {
///         (
///             IntervalBoundRuntime::Open(lower.into_open()),
///             IntervalBoundRuntime::Open(upper.into_open())
///         )
///     } else {
///         (
///             IntervalBoundRuntime::Closed(lower.into_closed()),
///             IntervalBoundRuntime::Closed(upper.into_closed())
///         )
///     }
/// }
///
/// let lower = LowerBoundClosed::new(0.0);
/// let upper = UpperBoundClosed::new(1.0);
///
/// let (strict_lower, strict_upper) = adjust_interval_strictness(lower.clone(), upper.clone(), true);
/// let (loose_lower, loose_upper) = adjust_interval_strictness(lower, upper, false);
///
/// assert!(strict_lower.is_open());   // Strict: (0, 1)
/// assert!(loose_lower.is_closed());  // Loose: [0, 1]
/// ```
///
/// ## Integration with Grid1D
///
/// ### Constraint System Flexibility
///
/// ```rust
/// use grid1d::bounds::*;
/// use num_valid::RealScalar;
/// use try_create::New;
///
/// struct FlexibleConstraint<T: RealScalar> {
///     lower: LowerBoundRuntime<T>,
///     upper: UpperBoundRuntime<T>,
/// }
///
/// impl<T: RealScalar> FlexibleConstraint<T> {
///     fn make_inclusive(self) -> Self {
///         let lower = match self.lower {
///             IntervalBoundRuntime::Open(open) => IntervalBoundRuntime::Closed(open.into_closed()),
///             closed => closed,
///         };
///         let upper = match self.upper {
///             IntervalBoundRuntime::Open(open) => IntervalBoundRuntime::Closed(open.into_closed()),
///             closed => closed,
///         };
///         Self { lower, upper }
///     }
///     
///     fn make_exclusive(self) -> Self {
///         let lower = match self.lower {
///             IntervalBoundRuntime::Closed(closed) => IntervalBoundRuntime::Open(closed.into_open()),
///             open => open,
///         };
///         let upper = match self.upper {
///             IntervalBoundRuntime::Closed(closed) => IntervalBoundRuntime::Open(closed.into_open()),
///             open => open,
///         };
///         Self { lower, upper }
///     }
/// }
/// ```
///
/// ### Numerical Robustness
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// // Handle numerical edge cases with inclusion adjustment
/// fn create_numerically_robust_bounds(
///     value: f64,
///     tolerance: f64
/// ) -> (LowerBoundRuntime<f64>, UpperBoundRuntime<f64>) {
///     let is_zero = value.abs() < tolerance;
///     
///     let lower = LowerBoundClosed::new(value);
///     let upper = UpperBoundClosed::new(value);
///     
///     if is_zero {
///         // For zero, use inclusive bounds to avoid numerical issues
///         (
///             IntervalBoundRuntime::Closed(lower),
///             IntervalBoundRuntime::Closed(upper)
///         )
///     } else {
///         // For non-zero, use exclusive bounds for strict inequalities
///         (
///             IntervalBoundRuntime::Open(lower.into_open()),
///             IntervalBoundRuntime::Open(upper.into_open())
///         )
///     }
/// }
///
/// let (zero_lower, zero_upper) = create_numerically_robust_bounds(0.0, 1e-10);
/// let (nonzero_lower, nonzero_upper) = create_numerically_robust_bounds(1.0, 1e-10);
///
/// assert!(zero_lower.is_closed());    // Inclusive for zero
/// assert!(nonzero_lower.is_open());   // Exclusive for non-zero
/// ```
///
/// ## Performance Characteristics
///
/// ### Zero-Cost Abstractions
/// - **Memory**: No additional storage overhead (phantom type manipulation only)
/// - **Conversion**: Direct field copying with compile-time type transformation
/// - **Runtime**: All conversions compile to simple memory moves
///
/// ### Optimization Benefits
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// // This entire conversion compiles to a no-op
/// fn efficient_inclusion_change(bound: LowerBoundOpen<f64>) -> LowerBoundClosed<f64> {
///     bound.into_closed()  // Zero runtime cost
/// }
///
/// let open = LowerBoundOpen::new(42.0);
/// let closed = efficient_inclusion_change(open);  // Optimized to direct assignment
/// assert_eq!(closed.as_ref(), &42.0);
/// ```
///
/// ## Advanced Usage Patterns
///
/// ### Constraint Relaxation Systems
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// // Progressive constraint relaxation for optimization
/// struct RelaxationSequence {
///     original_lower: LowerBoundClosed<f64>,
///     original_upper: UpperBoundClosed<f64>,
/// }
///
/// impl RelaxationSequence {
///     fn relax_step(&self, step: u32) -> (LowerBoundRuntime<f64>, UpperBoundRuntime<f64>) {
///         match step {
///             0 => (
///                 // Most restrictive: use open bounds
///                 IntervalBoundRuntime::Open(self.original_lower.clone().into_open()),
///                 IntervalBoundRuntime::Open(self.original_upper.clone().into_open())
///             ),
///             1 => (
///                 // Medium: mixed bounds
///                 IntervalBoundRuntime::Closed(self.original_lower.clone()),
///                 IntervalBoundRuntime::Open(self.original_upper.clone().into_open())
///             ),
///             _ => (
///                 // Most permissive: use closed bounds
///                 IntervalBoundRuntime::Closed(self.original_lower.clone()),
///                 IntervalBoundRuntime::Closed(self.original_upper.clone())
///             ),
///         }
///     }
/// }
/// ```
///
/// ### Interval Union and Intersection
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// // Combine bounds with appropriate inclusion semantics
/// fn combine_bounds_conservatively<S: BoundSide>(
///     bound1: IntervalBound<f64, S, Open>,
///     bound2: IntervalBound<f64, S, Closed>
/// ) -> IntervalBound<f64, S, Closed> {
///     // When combining bounds, choose more permissive inclusion
///     if bound1.as_ref() == bound2.as_ref() {
///         bound2  // Closed is more permissive
///     } else {
///         bound1.into_closed()  // Convert to consistent type
///     }
/// }
///
/// let open_bound = LowerBoundOpen::new(5.0);    // x > 5
/// let closed_bound = LowerBoundClosed::new(5.0); // x ≥ 5
/// let combined = combine_bounds_conservatively(open_bound, closed_bound);
///
/// assert!(combined.is_closed());  // More permissive inclusion chosen
/// assert!(combined.value_within_bound(&5.0));  // Includes boundary
/// ```
///
/// ### Generic Bound Processing
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// // Process bounds with configurable inclusion policy
/// fn process_bounds_with_policy<S: BoundSide, T: BoundType>(
///     bounds: Vec<IntervalBound<f64, S, T>>,
///     use_inclusive: bool
/// ) -> Vec<IntervalBound<f64, S, Closed>>
/// where
///     IntervalBound<f64, S, T>: BoundTypeConversion<f64, S>,
/// {
///     bounds.into_iter()
///         .map(|bound| {
///             if use_inclusive {
///                 bound.into_closed()
///             } else {
///                 bound.into_open().into_closed()  // Force consistency
///             }
///         })
///         .collect()
/// }
/// ```
///
/// ## Type System Guarantees
///
/// ### Compile-Time Verification
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// let closed_lower = LowerBoundClosed::new(1.0);
/// let open_lower = closed_lower.into_open();
///
/// // Type system ensures correctness
/// let _: LowerBoundOpen<f64> = open_lower;        // ✅ Correct type
/// // let _: UpperBoundOpen<f64> = open_lower;     // ❌ Compile error
/// ```
///
/// ### Mathematical Consistency
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// // Value and side properties are preserved
/// fn verify_conversion_consistency<S: BoundSide>(value: f64)
/// where
///     IntervalBound<f64, S, Open>: BoundTypeConversion<f64, S>,
/// {
///     let open = IntervalBound::<f64, S, Open>::new(value);
///     let closed = open.clone().into_closed();
///     let back_to_open = closed.into_open();
///     
///     // Value preservation
///     assert_eq!(open.as_ref(), back_to_open.as_ref());
///     
///     // Side preservation
///     assert_eq!(S::is_lower(), S::is_lower());  // Tautology showing consistency
/// }
/// ```
///
/// ## Best Practices
///
/// ### 1. **Use for Semantic Clarity**
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// // ✅ GOOD: Clear intention for inclusion change
/// fn make_strict_positive_constraint(bound: LowerBoundClosed<f64>) -> LowerBoundOpen<f64> {
///     bound.into_open()  // Clearly excludes zero
/// }
///
/// // ❌ AVOID: Unnecessary conversions
/// fn pointless_conversion(bound: LowerBoundOpen<f64>) -> LowerBoundOpen<f64> {
///     bound.into_closed().into_open()  // Wastes operations
/// }
/// ```
///
/// ### 2. **Combine with Other Transformations**
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// // ✅ GOOD: Chain operations for complex transformations
/// fn create_opposite_strict_bound(
///     original: LowerBoundClosed<f64>
/// ) -> UpperBoundOpen<f64> {
///     original.into_open()           // Make strict
///             .into_upper()          // Change side
/// }
/// ```
///
/// ### 3. **Consider Mathematical Implications**
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// // ✅ GOOD: Understand the mathematical meaning
/// fn relax_constraint_safely(strict_bound: LowerBoundOpen<f64>) -> LowerBoundClosed<f64> {
///     // Converting x > a to x ≥ a (more permissive)
///     strict_bound.into_closed()
/// }
///
/// fn tighten_constraint_carefully(loose_bound: LowerBoundClosed<f64>) -> LowerBoundOpen<f64> {
///     // Converting x ≥ a to x > a (more restrictive)
///     loose_bound.into_open()
/// }
/// ```
///
/// ## Mathematical Guarantees
///
/// The [`BoundTypeConversion`] trait maintains mathematical correctness by:
///
/// - **Value Preservation**: Boundary values remain unchanged during conversion
/// - **Side Preservation**: Lower/upper semantics are maintained
/// - **Type Safety**: Prevents invalid inclusion combinations at compile time
/// - **Reversibility**: Conversions are mathematically reversible
/// - **Zero-Cost**: Provides semantic transformations without runtime overhead
///
/// ## Relationship with `flip_bound_type()`
///
/// This trait provides a more ergonomic API compared to the lower-level
/// [`IntervalBound::flip_bound_type()`](IntervalBound::flip_bound_type) method:
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// let bound_open = LowerBoundOpen::new(5.0);
///
/// // BoundTypeConversion: Target-oriented API
/// let closed = bound_open.into_closed();  // "I want Closed"
///
/// // flip_bound_type(): Transformation-oriented API  
/// let also_closed = LowerBoundOpen::new(5.0).flip_bound_type();  // "Toggle type"
/// ```
///
/// **Use `into_open()`/`into_closed()` when**: You want to ensure a specific bound type
/// regardless of the current type (idempotent operations).
///
/// **Use `flip_bound_type()` when**: You explicitly want to toggle between Open/Closed
/// and the current type matters for your logic.
///
/// Note: The implementations of `into_open()` and `into_closed()` internally use
/// `flip_bound_type()` when conversion is needed, ensuring code reuse and consistency.
///
/// Use [`BoundTypeConversion`] when you need to change boundary inclusion semantics while
/// maintaining boundary values and sides. This trait is essential for constraint adjustment,
/// numerical robustness, and flexible interval specification systems.
pub trait BoundTypeConversion<RealType: RealScalar, Side: BoundSide>: Sized {
    /// Convert this bound into an open bound with the same side and value.
    ///
    /// This method transforms any bound into an open bound while preserving the scalar
    /// value and boundary side (lower/upper). The conversion changes only the inclusion
    /// behavior, making the constraint more restrictive by excluding the boundary value.
    ///
    /// # Mathematical Semantics
    ///
    /// - **For closed bounds**: Changes inclusion to exclusion (`≥/≤` becomes `>//<`)
    /// - **For open bounds**: Returns the same bound unchanged (identity conversion)
    ///
    /// # Examples
    ///
    /// ```rust
    /// use grid1d::bounds::*;
    /// use try_create::New;
    ///
    /// // Closed to open conversion
    /// let lower_closed = LowerBoundClosed::new(5.0);     // x ≥ 5
    /// let lower_open = lower_closed.into_open();          // x > 5
    /// assert!(lower_open.is_open());
    /// assert!(lower_open.is_lower_bound());
    /// assert_eq!(lower_open.as_ref(), &5.0);
    ///
    /// // Open bound identity conversion
    /// let existing_open = UpperBoundOpen::new(10.0);     // x < 10
    /// let same_open = existing_open.into_open();          // x < 10 (unchanged)
    /// assert!(same_open.is_open());
    /// assert!(same_open.is_upper_bound());
    /// ```
    fn into_open(self) -> IntervalBound<RealType, Side, Open>;

    /// Convert this bound into a closed bound with the same side and value.
    ///
    /// This method transforms any bound into a closed bound while preserving the scalar
    /// value and boundary side (lower/upper). The conversion changes only the inclusion
    /// behavior, making the constraint more permissive by including the boundary value.
    ///
    /// # Mathematical Semantics
    ///
    /// - **For open bounds**: Changes exclusion to inclusion (`>//<` becomes `≥/≤`)
    /// - **For closed bounds**: Returns the same bound unchanged (identity conversion)
    ///
    /// # Examples
    ///
    /// ```rust
    /// use grid1d::bounds::*;
    /// use try_create::New;
    ///
    /// // Open to closed conversion
    /// let upper_open = UpperBoundOpen::new(100.0);       // x < 100
    /// let upper_closed = upper_open.into_closed();        // x ≤ 100
    /// assert!(upper_closed.is_closed());
    /// assert!(upper_closed.is_upper_bound());
    /// assert_eq!(upper_closed.as_ref(), &100.0);
    ///
    /// // Closed bound identity conversion
    /// let existing_closed = LowerBoundClosed::new(0.0);  // x ≥ 0
    /// let same_closed = existing_closed.into_closed();    // x ≥ 0 (unchanged)
    /// assert!(same_closed.is_closed());
    /// assert!(same_closed.is_lower_bound());
    /// ```
    fn into_closed(self) -> IntervalBound<RealType, Side, Closed>;
}

impl<RealType: RealScalar, Side: BoundSide> BoundTypeConversion<RealType, Side>
    for IntervalBound<RealType, Side, Open>
{
    #[inline(always)]
    fn into_open(self) -> IntervalBound<RealType, Side, Open> {
        self
    }

    #[inline(always)]
    fn into_closed(self) -> IntervalBound<RealType, Side, Closed> {
        // Implemented in terms of flip_bound_type() to avoid code duplication
        self.flip_bound_type()
    }
}

impl<RealType: RealScalar, Side: BoundSide> BoundTypeConversion<RealType, Side>
    for IntervalBound<RealType, Side, Closed>
{
    #[inline(always)]
    fn into_open(self) -> IntervalBound<RealType, Side, Open> {
        // Implemented in terms of flip_bound_type() to avoid code duplication
        self.flip_bound_type()
    }

    #[inline(always)]
    fn into_closed(self) -> IntervalBound<RealType, Side, Closed> {
        self
    }
}

/// Convert between lower and upper bounds while preserving boundary type and value.
///
/// [`BoundSideConversion`] provides a unified interface for converting interval bounds
/// between lower and upper sides while maintaining the same inclusion semantics (open/closed)
/// and scalar value. This trait enables flexible bound manipulation in interval arithmetic
/// and domain specification operations.
///
/// ## Design Philosophy
///
/// ### Semantic Preservation
///
/// Unlike value-changing transformations, [`BoundSideConversion`] preserves all properties
/// except the boundary side:
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// let lower_closed = LowerBoundClosed::new(5.0);    // [5 (lower, closed)
/// let upper_closed = lower_closed.into_upper();     // 5] (upper, closed)
///
/// // Value and inclusion semantics preserved
/// assert_eq!(upper_closed.as_ref(), &5.0);         // Same value
/// assert!(upper_closed.is_closed());               // Same inclusion
/// assert!(upper_closed.is_upper_bound());          // Different side
/// ```
///
/// ### Type-Safe Conversions
///
/// All conversions are compile-time verified and zero-cost:
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// let open_lower = LowerBoundOpen::new(10.0);      // (10
/// let open_upper = open_lower.into_upper();        // 10)
///
/// // Type system ensures consistency
/// let _: UpperBoundOpen<f64> = open_upper;         // Compile-time verification
/// ```
///
/// ## Mathematical Semantics
///
/// ### Lower to Upper Conversion
///
/// Converting a lower bound to an upper bound changes the constraint direction
/// while preserving the boundary value and inclusion:
///
/// | Original Lower Bound | Mathematical Constraint | Converted Upper Bound | New Constraint |
/// |---------------------|------------------------|---------------------|----------------|
/// | `LowerBoundClosed` | `x ≥ value` | `UpperBoundClosed` | `x ≤ value` |
/// | `LowerBoundOpen` | `x > value` | `UpperBoundOpen` | `x < value` |
///
/// ### Upper to Lower Conversion
///
/// Converting an upper bound to a lower bound changes the constraint direction
/// while preserving the boundary value and inclusion:
///
/// | Original Upper Bound | Mathematical Constraint | Converted Lower Bound | New Constraint |
/// |---------------------|------------------------|---------------------|----------------|
/// | `UpperBoundClosed` | `x ≤ value` | `LowerBoundClosed` | `x ≥ value` |
/// | `UpperBoundOpen` | `x < value` | `LowerBoundOpen` | `x > value` |
///
/// ## Core Methods
///
/// ### [`BoundSideConversion::into_lower()`]
///
/// Converts any bound to a lower bound with the same value and inclusion semantics:
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// // Upper bounds to lower bounds
/// let upper_closed = UpperBoundClosed::new(100.0);  // x ≤ 100
/// let lower_closed = upper_closed.into_lower();      // x ≥ 100
/// assert_eq!(lower_closed.as_ref(), &100.0);
/// assert!(lower_closed.is_closed());
/// assert!(lower_closed.is_lower_bound());
///
/// let upper_open = UpperBoundOpen::new(50.0);       // x < 50
/// let lower_open = upper_open.into_lower();          // x > 50
/// assert_eq!(lower_open.as_ref(), &50.0);
/// assert!(lower_open.is_open());
/// assert!(lower_open.is_lower_bound());
///
/// // Lower bounds (identity conversion)
/// let existing_lower = LowerBoundClosed::new(25.0);
/// let same_lower = existing_lower.into_lower();      // No change
/// assert_eq!(same_lower.as_ref(), &25.0);
/// ```
///
/// ### [`BoundSideConversion::into_upper()`]
///
/// Converts any bound to an upper bound with the same value and inclusion semantics:
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// // Lower bounds to upper bounds
/// let lower_closed = LowerBoundClosed::new(0.0);    // x ≥ 0
/// let upper_closed = lower_closed.into_upper();      // x ≤ 0
/// assert_eq!(upper_closed.as_ref(), &0.0);
/// assert!(upper_closed.is_closed());
/// assert!(upper_closed.is_upper_bound());
///
/// let lower_open = LowerBoundOpen::new(-5.0);       // x > -5
/// let upper_open = lower_open.into_upper();          // x < -5
/// assert_eq!(upper_open.as_ref(), &-5.0);
/// assert!(upper_open.is_open());
/// assert!(upper_open.is_upper_bound());
///
/// // Upper bounds (identity conversion)
/// let existing_upper = UpperBoundOpen::new(10.0);
/// let same_upper = existing_upper.into_upper();      // No change
/// assert_eq!(same_upper.as_ref(), &10.0);
/// ```
///
/// ## Practical Usage Patterns
///
/// ### Interval Construction and Manipulation
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// // Create symmetric intervals from a single bound
/// fn create_symmetric_interval<T: BoundType>(
///     center: f64,
///     radius: f64
/// ) -> (LowerBound<f64, T>, UpperBound<f64, T>) {
///     let lower_bound = LowerBound::<f64, T>::new(center - radius);
///     let upper_bound = lower_bound.clone().into_upper().with_value(center + radius);
///     (lower_bound, upper_bound)
/// }
///
/// let (lower, upper) = create_symmetric_interval::<Closed>(5.0, 2.0);
/// assert_eq!(lower.as_ref(), &3.0);  // [3
/// assert_eq!(upper.as_ref(), &7.0);  // 7]
/// assert!(lower.is_closed());
/// assert!(upper.is_closed());
/// ```
///
/// ### Domain Transformations
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// // Transform constraint direction for mathematical operations
/// fn create_complementary_bounds(
///     original: LowerBoundClosed<f64>
/// ) -> (LowerBoundClosed<f64>, UpperBoundClosed<f64>) {
///     let complementary_upper = original.clone().into_upper();
///     (original, complementary_upper)
/// }
///
/// let lower = LowerBoundClosed::new(0.0);           // x ≥ 0
/// let (same_lower, upper) = create_complementary_bounds(lower);
///
/// // Now we have both x ≥ 0 and x ≤ 0, useful for equality constraints
/// assert!(same_lower.value_within_bound(&0.0));     // 0 ≥ 0 → true
/// assert!(upper.value_within_bound(&0.0));          // 0 ≤ 0 → true
/// assert!(!same_lower.value_within_bound(&-1.0));   // -1 ≥ 0 → false
/// assert!(!upper.value_within_bound(&1.0));         // 1 ≤ 0 → false
/// ```
///
/// ### Constraint Reversal
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// // Reverse constraint direction for optimization problems
/// fn reverse_constraint_direction<T: BoundType>(
///     lower: LowerBound<f64, T>
/// ) -> UpperBound<f64, T> {
///     lower.into_upper()
/// }
///
/// let min_constraint = LowerBoundOpen::new(0.0);    // x > 0 (minimum)
/// let max_constraint = reverse_constraint_direction(min_constraint);  // x < 0 (maximum)
///
/// // Useful for converting "at least" to "at most" constraints
/// assert!(max_constraint.is_upper_bound());
/// assert!(max_constraint.is_open());
/// assert_eq!(max_constraint.as_ref(), &0.0);
/// ```
///
/// ## Integration with Grid1D
///
/// ### Interval Boundary Specification
///
/// ```rust
/// use grid1d::{bounds::*, intervals::*};
/// use try_create::{New, IntoInner};
///
/// // Create intervals using bound conversion
/// fn create_interval_from_single_bound<T: BoundType>(
///     bound_value: f64,
///     width: f64
/// ) -> String
/// where
///     LowerBound<f64, T>: BoundSideConversion<f64, T>,
/// {
///     let lower = LowerBound::<f64, T>::new(bound_value);
///     let upper = lower.clone().into_upper().with_value(bound_value + width);
///     
///     let left = if T::is_closed() { "[" } else { "(" };
///     let right = if T::is_closed() { "]" } else { ")" };
///     format!("{}{}, {}{}", left, lower.as_ref(), upper.as_ref(), right)
/// }
///
/// let closed_interval = create_interval_from_single_bound::<Closed>(0.0, 1.0);
/// assert_eq!(closed_interval, "[0, 1]");
///
/// let open_interval = create_interval_from_single_bound::<Open>(0.0, 1.0);
/// assert_eq!(open_interval, "(0, 1)");
/// ```
///
/// ### Domain Analysis and Transformation
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// // Analyze and transform domain boundaries
/// fn analyze_domain_bounds<B>(bounds: &[B]) -> (f64, f64, usize, usize)
/// where
///     B: BoundSideConversion<f64, Closed> + AsRef<f64> + Clone,
/// {
///     let values: Vec<f64> = bounds.iter().map(|b| *b.as_ref()).collect();
///     let min_val = values.iter().fold(f64::INFINITY, |a, &b| a.min(b));
///     let max_val = values.iter().fold(f64::NEG_INFINITY, |a, &b| a.max(b));
///     
///     // Convert all to lower bounds for uniform analysis
///     let lower_bounds: Vec<_> = bounds.iter()
///         .map(|b| b.clone().into_lower())
///         .collect();
///     
///     let active_lower = lower_bounds.iter()
///         .filter(|b| b.value_within_bound(&min_val))
///         .count();
///     
///     let total_bounds = bounds.len();
///     (min_val, max_val, active_lower, total_bounds - active_lower)
/// }
/// ```
///
/// ## Performance Characteristics
///
/// ### Zero-Cost Abstractions
/// - **Memory**: No additional storage overhead (phantom type manipulation only)
/// - **Conversion**: Direct field copying with compile-time type transformation
/// - **Runtime**: All conversions compile to simple memory moves
///
/// ### Optimization Benefits
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// // This entire conversion compiles to a no-op
/// fn efficient_conversion(bound: LowerBoundClosed<f64>) -> UpperBoundClosed<f64> {
///     bound.into_upper()  // Zero runtime cost
/// }
///
/// let lower = LowerBoundClosed::new(42.0);
/// let upper = efficient_conversion(lower);  // Optimized to direct assignment
/// assert_eq!(upper.as_ref(), &42.0);
/// ```
///
/// ## Type System Guarantees
///
/// ### Compile-Time Verification
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// let open_lower = LowerBoundOpen::new(1.0);
/// let open_upper = open_lower.into_upper();
///
/// // Type system ensures correctness
/// let _: UpperBoundOpen<f64> = open_upper;        // ✅ Correct type
/// // let _: UpperBoundClosed<f64> = open_upper;   // ❌ Compile error
/// ```
///
/// ### Mathematical Consistency
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// // Value and inclusion properties are preserved
/// fn verify_conversion_consistency<T: BoundType>(value: f64)
/// where
///     LowerBound<f64, T>: BoundSideConversion<f64, T>,
/// {
///     let lower = LowerBound::<f64, T>::new(value);
///     let upper = lower.clone().into_upper();
///     let back_to_lower = upper.into_lower();
///     
///     // Value preservation
///     assert_eq!(lower.as_ref(), back_to_lower.as_ref());
///     
///     // Inclusion preservation
///     assert_eq!(lower.is_closed(), back_to_lower.is_closed());
///     assert_eq!(lower.is_open(), back_to_lower.is_open());
/// }
/// ```
///
/// ## Advanced Usage Patterns
///
/// ### Constraint System Building
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// struct ConstraintSystem {
///     lower_bounds: Vec<LowerBoundRuntime<f64>>,
///     upper_bounds: Vec<UpperBoundRuntime<f64>>,
/// }
///
/// impl ConstraintSystem {
///     fn add_lower_bound<B>(mut self, bound: B) -> Self
///     where
///         B: BoundSideConversion<f64, Closed> + Into<LowerBoundRuntime<f64>>,
///     {
///         self.lower_bounds.push(bound.into_lower().into());
///         self
///     }
///     
///     fn add_upper_bound<B>(mut self, bound: B) -> Self
///     where
///         B: BoundSideConversion<f64, Closed> + Into<UpperBoundRuntime<f64>>,
///     {
///         self.upper_bounds.push(bound.into_upper().into());
///         self
///     }
/// }
/// ```
///
/// ### Symmetric Domain Construction
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// // Build symmetric domains from a single specification
/// fn create_symmetric_domain<T: BoundType>(
///     center: f64,
///     half_width: f64
/// ) -> (LowerBound<f64, T>, UpperBound<f64, T>)
/// where
///     LowerBound<f64, T>: BoundSideConversion<f64, T>,
/// {
///     let lower = LowerBound::<f64, T>::new(center - half_width);
///     let upper = lower.clone().into_upper().with_value(center + half_width);
///     (lower, upper)
/// }
///
/// let (lower, upper) = create_symmetric_domain::<Closed>(0.0, 5.0);
/// assert_eq!(lower.as_ref(), &-5.0);  // [-5
/// assert_eq!(upper.as_ref(), &5.0);   // 5]
/// ```
///
/// ## Best Practices
///
/// ### 1. **Use for Semantic Transformations**
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// // ✅ GOOD: Converting constraint direction
/// fn convert_minimum_to_maximum(min_bound: LowerBoundClosed<f64>) -> UpperBoundClosed<f64> {
///     min_bound.into_upper()  // Clear semantic transformation
/// }
///
/// // ❌ AVOID: Using when side doesn't need to change
/// fn unnecessary_conversion(bound: LowerBoundClosed<f64>) -> LowerBoundClosed<f64> {
///     bound.into_upper().into_lower()  // Pointless round-trip
/// }
/// ```
///
/// ### 2. **Leverage Type System for Safety**
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// // ✅ GOOD: Type-guided API design
/// fn build_interval<T: BoundType>(
///     lower_val: f64,
///     upper_val: f64
/// ) -> (LowerBound<f64, T>, UpperBound<f64, T>)
/// where
///     LowerBound<f64, T>: BoundSideConversion<f64, T>,
/// {
///     let lower = LowerBound::<f64, T>::new(lower_val);
///     let upper = lower.clone().into_upper().with_value(upper_val);
///     (lower, upper)
/// }
/// ```
///
/// ### 3. **Combine with Other Bound Operations**
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// // ✅ GOOD: Chain operations for complex transformations
/// fn create_flipped_bound<T: BoundType, U: BoundType>(
///     original: LowerBound<f64, T>
/// ) -> UpperBound<f64, U>
/// where
///     LowerBound<f64, T>: BoundSideConversion<f64, T>,
///     IntervalBound<f64, Upper, T>: BoundTypeConversion<f64, Upper>,
/// {
///     original.into_upper().with_bound_type::<U>()
/// }
/// ```
///
/// ## Mathematical Guarantees
///
/// The [`BoundSideConversion`] trait maintains mathematical correctness by:
///
/// - **Value Preservation**: Boundary values remain unchanged during conversion
/// - **Inclusion Preservation**: Open/closed semantics are maintained
/// - **Type Safety**: Prevents invalid bound combinations at compile time
/// - **Reversibility**: Conversions are mathematically reversible
/// - **Zero-Cost**: Provides semantic transformations without runtime overhead
///
/// ## Relationship with `flip_bound_side()`
///
/// This trait provides a more ergonomic API compared to the lower-level
/// [`IntervalBound::flip_bound_side()`](IntervalBound::flip_bound_side) method:
///
/// ```rust
/// use grid1d::bounds::*;
/// use try_create::New;
///
/// let lower = LowerBoundClosed::new(5.0);
///
/// // BoundSideConversion: Target-oriented API
/// let upper = lower.into_upper();  // "I want Upper"
///
/// // flip_bound_side(): Transformation-oriented API  
/// let also_upper = LowerBoundClosed::new(5.0).flip_bound_side();  // "Toggle side"
/// ```
///
/// **Use `into_lower()`/`into_upper()` when**: You want to ensure a specific bound side
/// regardless of the current side (idempotent operations).
///
/// **Use `flip_bound_side()` when**: You explicitly want to toggle between Lower/Upper
/// and the current side matters for your logic.
///
/// Note: The implementations of `into_lower()` and `into_upper()` internally use
/// `flip_bound_side()` when conversion is needed, ensuring code reuse and consistency.
///
/// Use [`BoundSideConversion`] when you need to change constraint direction while
/// maintaining boundary values and inclusion semantics. This trait is essential for
/// interval manipulation, domain transformation, and constraint system construction.
pub trait BoundSideConversion<RealType: RealScalar, Type: BoundType>: Sized {
    /// Convert this bound into a lower bound with the same value and inclusion semantics.
    ///
    /// This method transforms any bound into a lower bound while preserving the scalar
    /// value and boundary inclusion behavior (open/closed). The conversion changes only
    /// the constraint direction.
    ///
    /// # Mathematical Semantics
    ///
    /// - **For upper bounds**: Changes `x ≤/< value` to `x ≥/> value`
    /// - **For lower bounds**: Returns the same bound unchanged (identity conversion)
    ///
    /// # Examples
    ///
    /// ```rust
    /// use grid1d::bounds::*;
    /// use try_create::New;
    ///
    /// // Upper to lower conversion
    /// let upper_closed = UpperBoundClosed::new(10.0);   // x ≤ 10
    /// let lower_closed = upper_closed.into_lower();      // x ≥ 10
    /// assert!(lower_closed.is_lower_bound());
    /// assert!(lower_closed.is_closed());
    /// assert_eq!(lower_closed.as_ref(), &10.0);
    ///
    /// // Lower bound identity conversion
    /// let existing_lower = LowerBoundOpen::new(5.0);     // x > 5
    /// let same_lower = existing_lower.into_lower();       // x > 5 (unchanged)
    /// assert!(same_lower.is_lower_bound());
    /// assert!(same_lower.is_open());
    /// ```
    fn into_lower(self) -> IntervalBound<RealType, Lower, Type>;

    /// Convert this bound into an upper bound with the same value and inclusion semantics.
    ///
    /// This method transforms any bound into an upper bound while preserving the scalar
    /// value and boundary inclusion behavior (open/closed). The conversion changes only
    /// the constraint direction.
    ///
    /// # Mathematical Semantics
    ///
    /// - **For lower bounds**: Changes `x ≥/> value` to `x ≤/< value`
    /// - **For upper bounds**: Returns the same bound unchanged (identity conversion)
    ///
    /// # Examples
    ///
    /// ```rust
    /// use grid1d::bounds::*;
    /// use try_create::New;
    ///
    /// // Lower to upper conversion
    /// let lower_open = LowerBoundOpen::new(0.0);         // x > 0
    /// let upper_open = lower_open.into_upper();           // x < 0
    /// assert!(upper_open.is_upper_bound());
    /// assert!(upper_open.is_open());
    /// assert_eq!(upper_open.as_ref(), &0.0);
    ///
    /// // Upper bound identity conversion
    /// let existing_upper = UpperBoundClosed::new(100.0); // x ≤ 100
    /// let same_upper = existing_upper.into_upper();       // x ≤ 100 (unchanged)
    /// assert!(same_upper.is_upper_bound());
    /// assert!(same_upper.is_closed());
    /// ```
    fn into_upper(self) -> IntervalBound<RealType, Upper, Type>;
}

impl<RealType: RealScalar, Type: BoundType> BoundSideConversion<RealType, Type>
    for IntervalBound<RealType, Lower, Type>
{
    #[inline(always)]
    fn into_lower(self) -> IntervalBound<RealType, Lower, Type> {
        self
    }

    #[inline(always)]
    fn into_upper(self) -> IntervalBound<RealType, Upper, Type> {
        // Implemented in terms of flip_bound_side() to avoid code duplication
        self.flip_bound_side()
    }
}

impl<RealType: RealScalar, Type: BoundType> BoundSideConversion<RealType, Type>
    for IntervalBound<RealType, Upper, Type>
{
    #[inline(always)]
    fn into_lower(self) -> IntervalBound<RealType, Lower, Type> {
        // Implemented in terms of flip_bound_side() to avoid code duplication
        self.flip_bound_side()
    }

    #[inline(always)]
    fn into_upper(self) -> IntervalBound<RealType, Upper, Type> {
        self
    }
}
//------------------------------------------------------------------------------------------------

#[duplicate_item(
    bound_lhs                    bound_rhs;
    [LowerBoundOpen<RealType>]   [LowerBoundClosed<RealType>];
    [LowerBoundClosed<RealType>] [LowerBoundOpen<RealType>] ;
    [UpperBoundOpen<RealType>]   [UpperBoundClosed<RealType>];
    [UpperBoundClosed<RealType>] [UpperBoundOpen<RealType>];
)]
impl<RealType: RealScalar> PartialEq<bound_rhs> for bound_lhs {
    #[inline(always)]
    fn eq(&self, _other: &bound_rhs) -> bool {
        false // Different bound types are never equal
    }
}

#[duplicate_item(
    bound_lhs                    bound_rhs;
    [LowerBoundClosed<RealType>] [LowerBoundOpen<RealType>];
    [UpperBoundOpen<RealType>]   [UpperBoundClosed<RealType>];
)]
impl<RealType: RealScalar> PartialOrd<bound_rhs> for bound_lhs {
    #[inline(always)]
    fn partial_cmp(&self, other: &bound_rhs) -> Option<Ordering> {
        match self.value.partial_cmp(&other.value) {
            Some(Ordering::Equal) => {
                // When values are equal, the "tighter" bound is considered smaller.
                // For lower bounds: [a is tighter than (a, so [a < (a.
                // For upper bounds: b) is tighter than b], so b) < b].
                Some(Ordering::Less)
            }
            other_ordering => other_ordering,
        }
    }
}

#[duplicate_item(
    bound_lhs                    bound_rhs;
    [UpperBoundClosed<RealType>] [UpperBoundOpen<RealType>];
    [LowerBoundOpen<RealType>]   [LowerBoundClosed<RealType>];
)]
impl<RealType: RealScalar> PartialOrd<bound_rhs> for bound_lhs {
    #[inline(always)]
    fn partial_cmp(&self, other: &bound_rhs) -> Option<Ordering> {
        match self.value.partial_cmp(&other.value) {
            Some(Ordering::Equal) => {
                // When values are equal, the "looser" bound is considered greater.
                // For lower bounds: (a is looser than [a, so (a > [a.
                // For upper bounds: b] is looser than b), so b] > b).
                Some(Ordering::Greater)
            }
            other_ordering => other_ordering,
        }
    }
}
//------------------------------------------------------------------------------------------------

//------------------------------------------------------------------------------------------------
#[duplicate_item(
        func_name bound_type;
        [min_lower_bound] [LowerBoundRuntime];
        [min_upper_bound] [UpperBoundRuntime];
    )]
#[allow(dead_code)]
#[inline(always)]
pub(crate) fn func_name<RealType: RealScalar>(
    a: bound_type<RealType>,
    b: bound_type<RealType>,
) -> bound_type<RealType> {
    if a < b { a } else { b }
}

#[duplicate_item(
        func_name bound_type;
        [max_lower_bound] [LowerBoundRuntime];
        [max_upper_bound] [UpperBoundRuntime];
    )]
#[inline(always)]
pub(crate) fn func_name<RealType: RealScalar>(
    a: bound_type<RealType>,
    b: bound_type<RealType>,
) -> bound_type<RealType> {
    if a > b { a } else { b }
}

pub(crate) fn min_lower_bound_maybe_unbounded<RealType: RealScalar>(
    a: Option<LowerBoundRuntime<RealType>>,
    b: Option<LowerBoundRuntime<RealType>>,
) -> Option<LowerBoundRuntime<RealType>> {
    match (a, b) {
        (None, _) | (_, None) => None,
        (Some(a), Some(b)) => {
            if a < b {
                Some(a)
            } else {
                Some(b)
            }
        }
    }
}

pub(crate) fn max_upper_bound_maybe_unbounded<RealType: RealScalar>(
    a: Option<UpperBoundRuntime<RealType>>,
    b: Option<UpperBoundRuntime<RealType>>,
) -> Option<UpperBoundRuntime<RealType>> {
    match (a, b) {
        (None, _) | (_, None) => None,
        (Some(a), Some(b)) => {
            if a > b {
                Some(a)
            } else {
                Some(b)
            }
        }
    }
}

/*
#[duplicate_item(
        func_name bound_type;
        [min_lower_bound_ref] [LowerBoundRuntime];
        [min_upper_bound_ref] [UpperBoundRuntime];
    )]
#[allow(dead_code)]
#[inline(always)]
pub(crate) fn func_name<'a, RealType: RealScalar>(
    a: &'a bound_type<RealType>,
    b: &'a bound_type<RealType>,
) -> &'a bound_type<RealType> {
    if a < b { a } else { b }
}

#[duplicate_item(
        func_name bound_type;
        [max_lower_bound_ref] [LowerBoundRuntime];
        [max_upper_bound_ref] [UpperBoundRuntime];
    )]
#[inline(always)]
pub(crate) fn func_name<'a, RealType: RealScalar>(
    a: &'a bound_type<RealType>,
    b: &'a bound_type<RealType>,
) -> &'a bound_type<RealType> {
    if a > b { a } else { b }
}
*/
//------------------------------------------------------------------------------------------------

//------------------------------------------------------------------------------------------------
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn bound_constraint_satisfaction() {
        let lower_closed = LowerBoundClosed::new(5.0);
        let lower_open = LowerBoundOpen::new(5.0);
        let upper_closed = UpperBoundClosed::new(10.0);
        let upper_open = UpperBoundOpen::new(10.0);

        // Test constraint satisfaction
        assert!(lower_closed.value_within_bound(&5.0)); // x >= 5
        assert!(!lower_open.value_within_bound(&5.0)); // x > 5
        assert!(upper_closed.value_within_bound(&10.0)); // x <= 10
        assert!(!upper_open.value_within_bound(&10.0)); // x < 10

        assert!(lower_closed.value_within_bound(&6.0));
        assert!(lower_open.value_within_bound(&6.0));
        assert!(upper_closed.value_within_bound(&9.0));
        assert!(upper_open.value_within_bound(&9.0));
    }

    #[test]
    fn bound_type_queries() {
        let lower_closed = LowerBoundClosed::new(1.0);
        let upper_open = UpperBoundOpen::new(1.0);

        assert!(lower_closed.is_lower_bound());
        assert!(!lower_closed.is_upper_bound());
        assert!(lower_closed.is_closed());
        assert!(!lower_closed.is_open());

        assert!(!upper_open.is_lower_bound());
        assert!(upper_open.is_upper_bound());
        assert!(!upper_open.is_closed());
        assert!(upper_open.is_open());
    }

    #[test]
    fn bound_conversions() {
        let lower_open_bound = LowerBoundOpen::new(5.0);
        let lower_closed_bound = LowerBoundClosed::new(5.0);
        let upper_open_bound = UpperBoundOpen::new(5.0);
        let upper_closed_bound = UpperBoundClosed::new(5.0);

        assert_eq!(lower_open_bound.clone().into_open(), lower_open_bound);
        assert_eq!(lower_open_bound.clone().into_closed(), lower_closed_bound);
        assert_eq!(lower_open_bound.clone().into_lower(), lower_open_bound);
        assert_eq!(lower_open_bound.clone().into_upper(), upper_open_bound);

        assert_eq!(lower_closed_bound.clone().into_open(), lower_open_bound);
        assert_eq!(lower_closed_bound.clone().into_closed(), lower_closed_bound);
        assert_eq!(lower_closed_bound.clone().into_lower(), lower_closed_bound);
        assert_eq!(lower_closed_bound.clone().into_upper(), upper_closed_bound);

        assert_eq!(upper_open_bound.clone().into_open(), upper_open_bound);
        assert_eq!(upper_open_bound.clone().into_closed(), upper_closed_bound);
        assert_eq!(upper_open_bound.clone().into_lower(), lower_open_bound);
        assert_eq!(upper_open_bound.clone().into_upper(), upper_open_bound);

        assert_eq!(upper_closed_bound.clone().into_open(), upper_open_bound);
        assert_eq!(upper_closed_bound.clone().into_closed(), upper_closed_bound);
        assert_eq!(upper_closed_bound.clone().into_lower(), lower_closed_bound);
        assert_eq!(upper_closed_bound.clone().into_upper(), upper_closed_bound);
    }

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

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

    #[test]
    fn bounds_into_inner() {
        let open = LowerBoundOpen::new(1.0);
        let closed = LowerBoundClosed::new(2.0);
        let up_open = UpperBoundOpen::new(3.0);
        let up_closed = UpperBoundClosed::new(4.0);
        assert_eq!(open.into_inner(), 1.0);
        assert_eq!(closed.into_inner(), 2.0);
        assert_eq!(up_open.into_inner(), 3.0);
        assert_eq!(up_closed.into_inner(), 4.0);
    }

    #[test]
    fn bounds_new_and_try_new() {
        let open = LowerBoundOpen::new(1.0);
        let closed = LowerBoundClosed::new(2.0);
        let up_open = UpperBoundOpen::new(3.0);
        let up_closed = UpperBoundClosed::new(4.0);
        assert_eq!(open.as_ref(), &1.0);
        assert_eq!(closed.as_ref(), &2.0);
        assert_eq!(up_open.as_ref(), &3.0);
        assert_eq!(up_closed.as_ref(), &4.0);
    }

    #[test]
    fn bounds_ordering_01() {
        let lower_closed_0 = LowerBoundClosed::new(0.0);
        assert_eq!(lower_closed_0.clone(), lower_closed_0);
        assert!(lower_closed_0 == lower_closed_0);
        assert!(!(lower_closed_0 != lower_closed_0));
        more_asserts::assert_ge!(lower_closed_0, lower_closed_0);
        assert!(lower_closed_0 <= lower_closed_0);
        more_asserts::assert_le!(lower_closed_0, lower_closed_0);
        assert!(lower_closed_0 >= lower_closed_0);
        more_asserts::assert_le!(lower_closed_0, lower_closed_0);
        more_asserts::assert_ge!(lower_closed_0, lower_closed_0);

        let lower_closed_1 = LowerBoundClosed::new(1.0);
        assert_ne!(lower_closed_0, lower_closed_1);
        more_asserts::assert_lt!(lower_closed_0, lower_closed_1);
        more_asserts::assert_le!(lower_closed_0, lower_closed_1);
        more_asserts::assert_gt!(lower_closed_1, lower_closed_0);
        more_asserts::assert_ge!(lower_closed_1, lower_closed_0);

        let lower_open_0 = LowerBoundOpen::new(0.0);
        assert_eq!(lower_open_0.clone(), lower_open_0);
        assert!(lower_open_0 == lower_open_0);
        assert!(!(lower_open_0 != lower_open_0));
        more_asserts::assert_ge!(lower_open_0, lower_open_0);
        assert!(lower_open_0 <= lower_open_0);
        more_asserts::assert_le!(lower_open_0, lower_open_0);
        assert!(lower_open_0 >= lower_open_0);
        more_asserts::assert_le!(lower_open_0, lower_open_0);
        more_asserts::assert_ge!(lower_open_0, lower_open_0);

        let lower_open_1 = LowerBoundOpen::new(1.0);
        assert_ne!(lower_open_0, lower_open_1);
        more_asserts::assert_lt!(lower_open_0, lower_open_1);
        more_asserts::assert_le!(lower_open_0, lower_open_1);
        more_asserts::assert_gt!(lower_open_1, lower_open_0);
        more_asserts::assert_ge!(lower_open_1, lower_open_0);

        let upper_closed_0 = UpperBoundClosed::new(0.0);
        assert_eq!(upper_closed_0.clone(), upper_closed_0);
        assert!(upper_closed_0 == upper_closed_0);
        assert!(!(upper_closed_0 != upper_closed_0));
        more_asserts::assert_ge!(upper_closed_0, upper_closed_0);
        assert!(upper_closed_0 <= upper_closed_0);
        more_asserts::assert_le!(upper_closed_0, upper_closed_0);
        assert!(upper_closed_0 >= upper_closed_0);
        more_asserts::assert_le!(upper_closed_0, upper_closed_0);
        more_asserts::assert_ge!(upper_closed_0, upper_closed_0);

        let upper_closed_1 = UpperBoundClosed::new(1.0);
        assert_ne!(upper_closed_0, upper_closed_1);
        more_asserts::assert_lt!(upper_closed_0, upper_closed_1);
        more_asserts::assert_le!(upper_closed_0, upper_closed_1);
        more_asserts::assert_gt!(upper_closed_1, upper_closed_0);
        more_asserts::assert_ge!(upper_closed_1, upper_closed_0);

        let upper_open_0 = UpperBoundOpen::new(0.0);
        assert_eq!(upper_open_0.clone(), upper_open_0);
        assert!(upper_open_0 == upper_open_0);
        assert!(!(upper_open_0 != upper_open_0));
        more_asserts::assert_ge!(upper_open_0, upper_open_0);
        assert!(upper_open_0 <= upper_open_0);
        more_asserts::assert_le!(upper_open_0, upper_open_0);
        assert!(upper_open_0 >= upper_open_0);
        more_asserts::assert_le!(upper_open_0, upper_open_0);
        more_asserts::assert_ge!(upper_open_0, upper_open_0);

        let upper_open_1 = UpperBoundOpen::new(1.0);
        assert_ne!(upper_open_0, upper_open_1);
        more_asserts::assert_lt!(upper_open_0, upper_open_1);
        more_asserts::assert_le!(upper_open_0, upper_open_1);
        more_asserts::assert_gt!(upper_open_1, upper_open_0);
        more_asserts::assert_ge!(upper_open_1, upper_open_0);
    }

    #[test]
    fn bounds_ordering_02() {
        let lower_closed = LowerBoundClosed::new(0.0);
        let lower_open = LowerBoundOpen::new(0.0);
        let upper_closed = UpperBoundClosed::new(0.0);
        let upper_open = UpperBoundOpen::new(0.0);

        {
            // lower closed - lower open
            assert_ne!(lower_closed, lower_open);
            more_asserts::assert_lt!(lower_closed, lower_open);
            more_asserts::assert_le!(lower_closed, lower_open);
        }

        {
            // lower open - lower closed
            assert_ne!(lower_open, lower_closed);
            more_asserts::assert_ge!(lower_open, lower_closed);
            more_asserts::assert_gt!(lower_open, lower_closed);
        }

        {
            // upper closed - upper open
            assert_ne!(upper_closed, upper_open);
            more_asserts::assert_gt!(upper_closed, upper_open);
            more_asserts::assert_ge!(upper_closed, upper_open);
        }

        {
            // upper open - upper closed
            assert_ne!(upper_open, upper_closed);
            more_asserts::assert_lt!(upper_open, upper_closed);
            more_asserts::assert_le!(upper_open, upper_closed);
        }
    }

    #[test]
    fn bound_utility_methods() {
        let lower_closed = LowerBoundClosed::new(5.0);

        // Test with_value method
        let new_lower = lower_closed.with_value(7.0);
        assert_eq!(new_lower.as_ref(), &7.0);
        assert!(new_lower.is_closed());
        assert!(new_lower.is_lower_bound());

        // Test with_bound_type method
        let open_version = lower_closed.clone().with_bound_type::<Open>();
        assert_eq!(open_version.as_ref(), lower_closed.as_ref());
        assert!(open_version.is_open());
        assert!(open_version.is_lower_bound());
    }

    mod interval_bound_runtime_comparisons {
        use super::*;
        use try_create::TryNew;

        type Real = f64;

        /// Test PartialOrd implementation for LowerBoundRuntime
        #[test]
        fn test_interval_lower_bound_comparison() {
            // Create test bounds
            let lower_bound_closed_5 =
                LowerBoundRuntime::Closed(LowerBoundClosed::new(Real::try_new(5.0).unwrap()));
            let lower_bound_open_5 =
                LowerBoundRuntime::Open(LowerBoundOpen::new(Real::try_new(5.0).unwrap()));
            let lower_bound_closed_3 =
                LowerBoundRuntime::Closed(LowerBoundClosed::new(Real::try_new(3.0).unwrap()));
            let lower_bound_open_3 =
                LowerBoundRuntime::Open(LowerBoundOpen::new(Real::try_new(3.0).unwrap()));
            let lower_bound_closed_7 =
                LowerBoundRuntime::Closed(LowerBoundClosed::new(Real::try_new(7.0).unwrap()));

            // Test basic ordering by value
            assert_eq!(
                lower_bound_closed_3.partial_cmp(&lower_bound_closed_5),
                Some(Ordering::Less)
            );
            assert_eq!(
                lower_bound_closed_5.partial_cmp(&lower_bound_closed_3),
                Some(Ordering::Greater)
            );
            assert_eq!(
                lower_bound_closed_5.partial_cmp(&lower_bound_closed_5),
                Some(Ordering::Equal)
            );

            // Test ordering between closed and open bounds with same value
            // For lower bounds: closed < open at same value (closed is more restrictive for lower)
            assert_eq!(
                lower_bound_closed_5.partial_cmp(&lower_bound_open_5),
                Some(Ordering::Less)
            );
            assert_eq!(
                lower_bound_open_5.partial_cmp(&lower_bound_closed_5),
                Some(Ordering::Greater)
            );

            // Test mixed value and boundary type comparisons
            assert_eq!(
                lower_bound_closed_3.partial_cmp(&lower_bound_open_5),
                Some(Ordering::Less)
            );
            assert_eq!(
                lower_bound_open_3.partial_cmp(&lower_bound_closed_5),
                Some(Ordering::Less)
            );
            assert_eq!(
                lower_bound_closed_5.partial_cmp(&lower_bound_open_3),
                Some(Ordering::Greater)
            );

            // Test transitivity
            assert_eq!(
                lower_bound_closed_3.partial_cmp(&lower_bound_closed_7),
                Some(Ordering::Less)
            );
            assert_eq!(
                lower_bound_open_3.partial_cmp(&lower_bound_closed_7),
                Some(Ordering::Less)
            );
        }

        /// Test PartialOrd implementation for UpperBoundRuntime
        #[test]
        fn test_interval_upper_bound_comparison() {
            // Create test bounds
            let upper_bound_closed_5 =
                UpperBoundRuntime::Closed(UpperBoundClosed::new(Real::try_new(5.0).unwrap()));
            let upper_bound_open_5 =
                UpperBoundRuntime::Open(UpperBoundOpen::new(Real::try_new(5.0).unwrap()));
            let upper_bound_closed_3 =
                UpperBoundRuntime::Closed(UpperBoundClosed::new(Real::try_new(3.0).unwrap()));
            let upper_bound_open_3 =
                UpperBoundRuntime::Open(UpperBoundOpen::new(Real::try_new(3.0).unwrap()));
            let upper_bound_closed_7 =
                UpperBoundRuntime::Closed(UpperBoundClosed::new(Real::try_new(7.0).unwrap()));

            // Test basic ordering by value
            assert_eq!(
                upper_bound_closed_3.partial_cmp(&upper_bound_closed_5),
                Some(Ordering::Less)
            );
            assert_eq!(
                upper_bound_closed_5.partial_cmp(&upper_bound_closed_3),
                Some(Ordering::Greater)
            );
            assert_eq!(
                upper_bound_closed_5.partial_cmp(&upper_bound_closed_5),
                Some(Ordering::Equal)
            );

            // Test ordering between closed and open bounds with same value
            // For upper bounds: open < closed at same value (open is more restrictive for upper)
            assert_eq!(
                upper_bound_open_5.partial_cmp(&upper_bound_closed_5),
                Some(Ordering::Less)
            );
            assert_eq!(
                upper_bound_closed_5.partial_cmp(&upper_bound_open_5),
                Some(Ordering::Greater)
            );

            // Test mixed value and boundary type comparisons
            assert_eq!(
                upper_bound_closed_3.partial_cmp(&upper_bound_open_5),
                Some(Ordering::Less)
            );
            assert_eq!(
                upper_bound_open_3.partial_cmp(&upper_bound_closed_5),
                Some(Ordering::Less)
            );
            assert_eq!(
                upper_bound_closed_5.partial_cmp(&upper_bound_open_3),
                Some(Ordering::Greater)
            );

            // Test transitivity
            assert_eq!(
                upper_bound_closed_3.partial_cmp(&upper_bound_closed_7),
                Some(Ordering::Less)
            );
            assert_eq!(
                upper_bound_open_3.partial_cmp(&upper_bound_closed_7),
                Some(Ordering::Less)
            );
        }

        /// Test boundary type semantics for lower bounds
        #[test]
        fn test_lower_bound_semantics() {
            let closed_5 =
                LowerBoundRuntime::Closed(LowerBoundClosed::new(Real::try_new(5.0).unwrap()));
            let open_5 = LowerBoundRuntime::Open(LowerBoundOpen::new(Real::try_new(5.0).unwrap()));

            // For lower bounds, closed is more restrictive (smaller) than open at same value
            // This is because [5,b) contains 5, while (5,b) does not
            more_asserts::assert_lt!(closed_5, open_5);
            more_asserts::assert_ge!(open_5, closed_5);
            assert_ne!(closed_5, open_5);
        }

        /// Test boundary type semantics for upper bounds
        #[test]
        fn test_upper_bound_semantics() {
            let closed_5 =
                UpperBoundRuntime::Closed(UpperBoundClosed::new(Real::try_new(5.0).unwrap()));
            let open_5 = UpperBoundRuntime::Open(UpperBoundOpen::new(Real::try_new(5.0).unwrap()));

            // For upper bounds, open is more restrictive (smaller) than closed at same value
            // This is because [a,5) excludes 5, while [a,5] includes 5
            more_asserts::assert_lt!(open_5, closed_5);
            more_asserts::assert_ge!(closed_5, open_5);
            assert_ne!(open_5, closed_5);
        }

        /// Test consistency with mathematical interval ordering
        #[test]
        fn test_mathematical_consistency() {
            // Create bounds that would represent valid intervals
            let lower_closed_1 =
                LowerBoundRuntime::Closed(LowerBoundClosed::new(Real::try_new(1.0).unwrap()));
            let lower_open_1 =
                LowerBoundRuntime::Open(LowerBoundOpen::new(Real::try_new(1.0).unwrap()));
            let upper_open_2 =
                UpperBoundRuntime::Open(UpperBoundOpen::new(Real::try_new(2.0).unwrap()));
            let upper_closed_2 =
                UpperBoundRuntime::Closed(UpperBoundClosed::new(Real::try_new(2.0).unwrap()));

            // Mathematical consistency checks:
            // [1,2) should have closed lower bound and open upper bound
            // (1,2] should have open lower bound and closed upper bound
            // [1,2] should have closed bounds
            // (1,2) should have open bounds

            // Verify ordering is consistent with interval containment
            // [1,x] contains more points than (1,x] at the same x
            assert!(lower_closed_1 < lower_open_1);

            // [x,2) contains fewer points than [x,2] at the same x
            assert!(upper_open_2 < upper_closed_2);
        }

        /// Test reflexivity property
        #[test]
        fn test_reflexivity() {
            let lower_closed =
                LowerBoundRuntime::Closed(LowerBoundClosed::new(Real::try_new(5.0).unwrap()));
            let lower_open =
                LowerBoundRuntime::Open(LowerBoundOpen::new(Real::try_new(5.0).unwrap()));
            let upper_closed =
                UpperBoundRuntime::Closed(UpperBoundClosed::new(Real::try_new(5.0).unwrap()));
            let upper_open =
                UpperBoundRuntime::Open(UpperBoundOpen::new(Real::try_new(5.0).unwrap()));

            // Reflexivity: x.partial_cmp(&x) == Some(Ordering::Equal)
            assert_eq!(
                lower_closed.partial_cmp(&lower_closed),
                Some(Ordering::Equal)
            );
            assert_eq!(lower_open.partial_cmp(&lower_open), Some(Ordering::Equal));
            assert_eq!(
                upper_closed.partial_cmp(&upper_closed),
                Some(Ordering::Equal)
            );
            assert_eq!(upper_open.partial_cmp(&upper_open), Some(Ordering::Equal));
        }

        /// Test antisymmetry property
        #[test]
        fn test_antisymmetry() {
            let lower_closed_3 =
                LowerBoundRuntime::Closed(LowerBoundClosed::new(Real::try_new(3.0).unwrap()));
            let lower_closed_5 =
                LowerBoundRuntime::Closed(LowerBoundClosed::new(Real::try_new(5.0).unwrap()));

            // Antisymmetry: if x <= y and y <= x, then x == y
            if lower_closed_3.partial_cmp(&lower_closed_5) == Some(Ordering::Less) {
                assert_eq!(
                    lower_closed_5.partial_cmp(&lower_closed_3),
                    Some(Ordering::Greater)
                );
            }
        }

        /// Test transitivity property
        #[test]
        fn test_transitivity() {
            let lower_closed_1 =
                LowerBoundRuntime::Closed(LowerBoundClosed::new(Real::try_new(1.0).unwrap()));
            let lower_open_3 =
                LowerBoundRuntime::Open(LowerBoundOpen::new(Real::try_new(3.0).unwrap()));
            let lower_closed_5 =
                LowerBoundRuntime::Closed(LowerBoundClosed::new(Real::try_new(5.0).unwrap()));

            // Transitivity: if x <= y and y <= z, then x <= z
            let ord_1_3 = lower_closed_1.partial_cmp(&lower_open_3);
            let ord_3_5 = lower_open_3.partial_cmp(&lower_closed_5);
            let ord_1_5 = lower_closed_1.partial_cmp(&lower_closed_5);

            if ord_1_3 == Some(Ordering::Less) && ord_3_5 == Some(Ordering::Less) {
                assert_eq!(ord_1_5, Some(Ordering::Less));
            }
        }

        /// Test edge cases with same values but different boundary types
        #[test]
        fn test_edge_cases_same_values() {
            let value = Real::try_new(42.0).unwrap();

            let lower_closed = LowerBoundRuntime::Closed(LowerBoundClosed::new(value));
            let lower_open = LowerBoundRuntime::Open(LowerBoundOpen::new(value));
            let upper_closed = UpperBoundRuntime::Closed(UpperBoundClosed::new(value));
            let upper_open = UpperBoundRuntime::Open(UpperBoundOpen::new(value));

            // Test that boundary type matters even with same value
            assert_ne!(lower_closed.partial_cmp(&lower_open), Some(Ordering::Equal));
            assert_ne!(upper_closed.partial_cmp(&upper_open), Some(Ordering::Equal));

            // Verify specific ordering relationships
            assert_eq!(lower_closed.partial_cmp(&lower_open), Some(Ordering::Less));
            assert_eq!(upper_open.partial_cmp(&upper_closed), Some(Ordering::Less));
        }

        /// Test with extreme values
        #[test]
        fn test_extreme_values() {
            let min_val = Real::try_new(f64::MIN).unwrap();
            let max_val = Real::try_new(f64::MAX).unwrap();

            let lower_min_closed = LowerBoundRuntime::Closed(LowerBoundClosed::new(min_val));
            let lower_max_open = LowerBoundRuntime::Open(LowerBoundOpen::new(max_val));
            let upper_min_open = UpperBoundRuntime::Open(UpperBoundOpen::new(min_val));
            let upper_max_closed = UpperBoundRuntime::Closed(UpperBoundClosed::new(max_val));

            // Test that extreme values still follow ordering rules
            assert_eq!(
                lower_min_closed.partial_cmp(&lower_max_open),
                Some(Ordering::Less)
            );
            assert_eq!(
                upper_min_open.partial_cmp(&upper_max_closed),
                Some(Ordering::Less)
            );
        }

        /// Test helper functions min_lower_bound and max_lower_bound
        #[test]
        fn test_min_max_lower_bound() {
            let lower_closed_3 =
                LowerBoundRuntime::Closed(LowerBoundClosed::new(Real::try_new(3.0).unwrap()));
            let lower_open_5 =
                LowerBoundRuntime::Open(LowerBoundOpen::new(Real::try_new(5.0).unwrap()));

            // Test min_lower_bound
            let min_result = min_lower_bound(lower_closed_3.clone(), lower_open_5.clone());
            assert_eq!(min_result, lower_closed_3);

            // Test max_lower_bound
            let max_result = max_lower_bound(lower_closed_3.clone(), lower_open_5.clone());
            assert_eq!(max_result, lower_open_5);
        }

        /// Test helper functions min_upper_bound and max_upper_bound
        #[test]
        fn test_min_max_upper_bound() {
            let upper_open_3 =
                UpperBoundRuntime::Open(UpperBoundOpen::new(Real::try_new(3.0).unwrap()));
            let upper_closed_5 =
                UpperBoundRuntime::Closed(UpperBoundClosed::new(Real::try_new(5.0).unwrap()));

            // Test min_upper_bound
            let min_result = min_upper_bound(upper_open_3.clone(), upper_closed_5.clone());
            assert_eq!(min_result, upper_open_3);

            // Test max_upper_bound
            let max_result = max_upper_bound(upper_open_3.clone(), upper_closed_5.clone());
            assert_eq!(max_result, upper_closed_5);
        }
    }

    mod interval_lower_bound {
        use super::*;
        use crate::bounds::{LowerBoundClosed, LowerBoundOpen};
        use std::cmp::Ordering;

        #[test]
        fn test_interval_lower_bound_equality() {
            let closed_1 = LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0));
            let closed_2 = LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0));
            let closed_3 = LowerBoundRuntime::Closed(LowerBoundClosed::new(2.0));

            let open_1 = LowerBoundRuntime::Open(LowerBoundOpen::new(1.0));
            let open_2 = LowerBoundRuntime::Open(LowerBoundOpen::new(1.0));

            // Test equality for same variant and value
            assert_eq!(closed_1, closed_2);
            assert_eq!(open_1, open_2);

            // Test inequality for different values
            assert_ne!(closed_1, closed_3);

            // Test inequality for different variants, same value
            assert_ne!(closed_1, open_1);
        }

        mod interval_upper_bound {
            use super::*;
            use crate::bounds::{UpperBoundClosed, UpperBoundOpen};
            use std::cmp::Ordering;

            #[test]
            fn test_interval_upper_bound_equality() {
                let closed_1 = UpperBoundRuntime::Closed(UpperBoundClosed::new(1.0));
                let closed_2 = UpperBoundRuntime::Closed(UpperBoundClosed::new(1.0));
                let closed_3 = UpperBoundRuntime::Closed(UpperBoundClosed::new(2.0));

                let open_1 = UpperBoundRuntime::Open(UpperBoundOpen::new(1.0));
                let open_2 = UpperBoundRuntime::Open(UpperBoundOpen::new(1.0));

                // Test equality for same variant and value
                assert_eq!(closed_1, closed_2);
                assert_eq!(open_1, open_2);

                // Test inequality for different values
                assert_ne!(closed_1, closed_3);

                // Test inequality for different variants, same value
                assert_ne!(closed_1, open_1);
            }

            #[test]
            fn test_interval_upper_bound_partial_ord() {
                let closed_1 = UpperBoundRuntime::Closed(UpperBoundClosed::new(1.0));
                let closed_2 = UpperBoundRuntime::Closed(UpperBoundClosed::new(2.0));
                let open_1 = UpperBoundRuntime::Open(UpperBoundOpen::new(1.0));
                let open_2 = UpperBoundRuntime::Open(UpperBoundOpen::new(2.0));

                // Test closed vs closed
                assert_eq!(closed_1.partial_cmp(&closed_1), Some(Ordering::Equal));
                assert_eq!(closed_1.partial_cmp(&closed_2), Some(Ordering::Less));
                assert_eq!(closed_2.partial_cmp(&closed_1), Some(Ordering::Greater));

                // Test open vs open
                assert_eq!(open_1.partial_cmp(&open_1), Some(Ordering::Equal));
                assert_eq!(open_1.partial_cmp(&open_2), Some(Ordering::Less));
                assert_eq!(open_2.partial_cmp(&open_1), Some(Ordering::Greater));

                // Test open vs closed with same value (open < closed for upper bounds)
                assert_eq!(open_1.partial_cmp(&closed_1), Some(Ordering::Less));
                assert_eq!(closed_1.partial_cmp(&open_1), Some(Ordering::Greater));

                // Test open vs closed with different values
                assert_eq!(open_1.partial_cmp(&closed_2), Some(Ordering::Less));
                assert_eq!(closed_1.partial_cmp(&open_2), Some(Ordering::Less));
            }

            #[test]
            fn test_interval_upper_bound_ordering_consistency() {
                let closed_1 = UpperBoundRuntime::Closed(UpperBoundClosed::new(1.0));
                let open_1 = UpperBoundRuntime::Open(UpperBoundOpen::new(1.0));
                let closed_2 = UpperBoundRuntime::Closed(UpperBoundClosed::new(2.0));
                let open_2 = UpperBoundRuntime::Open(UpperBoundOpen::new(2.0));

                // Create sorted list based on expected ordering
                // For upper bounds: smaller values < larger values, and open < closed for same value
                let bounds = vec![
                    open_1.clone(),
                    closed_1.clone(),
                    open_2.clone(),
                    closed_2.clone(),
                ];

                // Test that the ordering is consistent
                for i in 0..bounds.len() {
                    for j in i + 1..bounds.len() {
                        assert!(bounds[i] < bounds[j],);
                        assert!(bounds[j] > bounds[i],);
                    }
                }

                // Test sorting
                let mut shuffled = bounds.clone();
                shuffled.reverse();
                shuffled.sort_by(|a, b| a.partial_cmp(b).unwrap());
                assert_eq!(shuffled, bounds);
            }

            #[test]
            fn test_interval_upper_bound_edge_cases() {
                // Test with special floating point values
                let closed_zero = UpperBoundRuntime::Closed(UpperBoundClosed::new(0.0));
                let closed_neg_zero = UpperBoundRuntime::Closed(UpperBoundClosed::new(-0.0));
                let open_zero = UpperBoundRuntime::Open(UpperBoundOpen::new(0.0));

                // 0.0 and -0.0 should be equal
                assert_eq!(closed_zero, closed_neg_zero);

                // But closed and open should be different even with same value
                assert_ne!(closed_zero, open_zero);
                assert!(open_zero < closed_zero); // open < closed for upper bounds

                // Test with very small differences
                let closed_small = UpperBoundRuntime::Closed(UpperBoundClosed::new(1e-15));
                let open_small = UpperBoundRuntime::Open(UpperBoundOpen::new(1e-15));
                assert!(open_small < closed_small);
            }

            #[test]
            fn test_interval_upper_bound_transitivity() {
                let open_1 = UpperBoundRuntime::Open(UpperBoundOpen::new(1.0));
                let closed_1 = UpperBoundRuntime::Closed(UpperBoundClosed::new(1.0));
                let closed_2 = UpperBoundRuntime::Closed(UpperBoundClosed::new(2.0));

                // Test transitivity: if a < b and b < c, then a < c
                assert!(open_1 < closed_1);
                assert!(closed_1 < closed_2);
                assert!(open_1 < closed_2); // transitivity
            }

            #[test]
            fn test_interval_upper_bound_reflexivity() {
                let closed = UpperBoundRuntime::Closed(UpperBoundClosed::new(1.0));
                let open = UpperBoundRuntime::Open(UpperBoundOpen::new(1.0));

                // Test reflexivity: a == a
                assert_eq!(closed.partial_cmp(&closed), Some(Ordering::Equal));
                assert_eq!(open.partial_cmp(&open), Some(Ordering::Equal));
            }

            #[test]
            fn test_interval_upper_bound_antisymmetry() {
                let closed_1 = UpperBoundRuntime::Closed(UpperBoundClosed::new(1.0));
                let closed_2 = UpperBoundRuntime::Closed(UpperBoundClosed::new(2.0));
                let open_1 = UpperBoundRuntime::Open(UpperBoundOpen::new(1.0));

                // Test antisymmetry: if a <= b and b <= a, then a == b
                // (This should only happen when a == b)
                assert_eq!(closed_1.partial_cmp(&closed_1), Some(Ordering::Equal));

                // When a < b, then b > a (not equal)
                assert_eq!(closed_1.partial_cmp(&closed_2), Some(Ordering::Less));
                assert_eq!(closed_2.partial_cmp(&closed_1), Some(Ordering::Greater));

                assert_eq!(open_1.partial_cmp(&closed_1), Some(Ordering::Less));
                assert_eq!(closed_1.partial_cmp(&open_1), Some(Ordering::Greater));
            }

            #[test]
            fn test_min_upper_bound() {
                let closed_1 = UpperBoundRuntime::Closed(UpperBoundClosed::new(1.0));
                let open_1 = UpperBoundRuntime::Open(UpperBoundOpen::new(1.0));
                let closed_2 = UpperBoundRuntime::Closed(UpperBoundClosed::new(2.0));

                // min with same value, different types should be open (smaller for upper bounds)
                assert_eq!(min_upper_bound(closed_1.clone(), open_1.clone()), open_1);
                assert_eq!(min_upper_bound(open_1.clone(), closed_1.clone()), open_1);

                // min with different values
                assert_eq!(
                    min_upper_bound(closed_1.clone(), closed_2.clone()),
                    closed_1
                );
                assert_eq!(
                    min_upper_bound(closed_2.clone(), closed_1.clone()),
                    closed_1
                );

                // min with same bound should return that bound
                assert_eq!(
                    min_upper_bound(closed_1.clone(), closed_1.clone()),
                    closed_1
                );
            }

            #[test]
            fn test_max_upper_bound() {
                let closed_1 = UpperBoundRuntime::Closed(UpperBoundClosed::new(1.0));
                let open_1 = UpperBoundRuntime::Open(UpperBoundOpen::new(1.0));
                let closed_2 = UpperBoundRuntime::Closed(UpperBoundClosed::new(2.0));

                // max with same value, different types should be closed (larger for upper bounds)
                assert_eq!(max_upper_bound(closed_1.clone(), open_1.clone()), closed_1);
                assert_eq!(max_upper_bound(open_1.clone(), closed_1.clone()), closed_1);

                // max with different values
                assert_eq!(
                    max_upper_bound(closed_1.clone(), closed_2.clone()),
                    closed_2
                );
                assert_eq!(
                    max_upper_bound(closed_2.clone(), closed_1.clone()),
                    closed_2
                );

                // max with same bound should return that bound
                assert_eq!(
                    max_upper_bound(closed_1.clone(), closed_1.clone()),
                    closed_1
                );
            }

            #[test]
            fn test_interval_upper_bound_with_negative_values() {
                let closed_neg = UpperBoundRuntime::Closed(UpperBoundClosed::new(-5.0));
                let open_neg = UpperBoundRuntime::Open(UpperBoundOpen::new(-5.0));
                let closed_pos = UpperBoundRuntime::Closed(UpperBoundClosed::new(5.0));

                // Negative values should still follow the ordering rules
                assert!(open_neg < closed_neg);
                assert!(closed_neg < closed_pos);

                // More negative values should be smaller
                let closed_more_neg = UpperBoundRuntime::Closed(UpperBoundClosed::new(-10.0));
                assert!(closed_more_neg < closed_neg);
            }

            #[test]
            fn test_interval_upper_bound_with_large_values() {
                let closed_large = UpperBoundRuntime::Closed(UpperBoundClosed::new(1e6));
                let open_large = UpperBoundRuntime::Open(UpperBoundOpen::new(1e6));
                let closed_larger = UpperBoundRuntime::Closed(UpperBoundClosed::new(1e9));

                // Large values should still follow ordering
                assert!(open_large < closed_large);
                assert!(closed_large < closed_larger);

                // Should be consistent with regular values
                let closed_small = UpperBoundRuntime::Closed(UpperBoundClosed::new(1.0));
                assert!(closed_small < closed_large);
            }

            #[test]
            fn test_interval_upper_bound_boundary_semantics() {
                // Test the key difference between upper and lower bounds:
                // For upper bounds, open < closed (opposite of lower bounds)

                let open_5 = UpperBoundRuntime::Open(UpperBoundOpen::new(5.0));
                let closed_5 = UpperBoundRuntime::Closed(UpperBoundClosed::new(5.0));

                // For upper bounds: open is more restrictive (smaller) than closed
                assert!(open_5 < closed_5);
                assert!(closed_5 > open_5);

                // This reflects the fact that (-∞, 5) ⊂ (-∞, 5]
                // The open upper bound excludes the endpoint, making it "smaller"
            }
        }

        #[test]
        fn test_interval_lower_bound_partial_ord() {
            let closed_1 = LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0));
            let closed_2 = LowerBoundRuntime::Closed(LowerBoundClosed::new(2.0));
            let open_1 = LowerBoundRuntime::Open(LowerBoundOpen::new(1.0));
            let open_2 = LowerBoundRuntime::Open(LowerBoundOpen::new(2.0));

            // Test closed vs closed
            assert_eq!(closed_1.partial_cmp(&closed_1), Some(Ordering::Equal));
            assert_eq!(closed_1.partial_cmp(&closed_2), Some(Ordering::Less));
            assert_eq!(closed_2.partial_cmp(&closed_1), Some(Ordering::Greater));

            // Test open vs open
            assert_eq!(open_1.partial_cmp(&open_1), Some(Ordering::Equal));
            assert_eq!(open_1.partial_cmp(&open_2), Some(Ordering::Less));
            assert_eq!(open_2.partial_cmp(&open_1), Some(Ordering::Greater));

            // Test closed vs open with same value (closed < open)
            assert_eq!(closed_1.partial_cmp(&open_1), Some(Ordering::Less));
            assert_eq!(open_1.partial_cmp(&closed_1), Some(Ordering::Greater));

            // Test closed vs open with different values
            assert_eq!(closed_1.partial_cmp(&open_2), Some(Ordering::Less));
            assert_eq!(open_1.partial_cmp(&closed_2), Some(Ordering::Less));
        }

        #[test]
        fn test_interval_lower_bound_ordering_consistency() {
            let closed_0 = LowerBoundRuntime::Closed(LowerBoundClosed::new(0.0));
            let closed_1 = LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0));
            let open_0 = LowerBoundRuntime::Open(LowerBoundOpen::new(0.0));
            let open_1 = LowerBoundRuntime::Open(LowerBoundOpen::new(1.0));

            // Create sorted list based on expected ordering
            let bounds = vec![
                closed_0.clone(),
                open_0.clone(),
                closed_1.clone(),
                open_1.clone(),
            ];

            // Test that the ordering is consistent
            for i in 0..bounds.len() {
                for j in i + 1..bounds.len() {
                    assert!(bounds[i] < bounds[j],);
                    assert!(bounds[j] > bounds[i],);
                }
            }

            // Test sorting
            let mut shuffled = bounds.clone();
            shuffled.reverse();
            shuffled.sort_by(|a, b| a.partial_cmp(b).unwrap());
            assert_eq!(shuffled, bounds);
        }

        #[test]
        fn test_interval_lower_bound_edge_cases() {
            // Test with special floating point values
            let closed_zero = LowerBoundRuntime::Closed(LowerBoundClosed::new(0.0));
            let closed_neg_zero = LowerBoundRuntime::Closed(LowerBoundClosed::new(-0.0));
            let open_zero = LowerBoundRuntime::Open(LowerBoundOpen::new(0.0));

            // 0.0 and -0.0 should be equal
            assert_eq!(closed_zero, closed_neg_zero);

            // But closed and open should be different even with same value
            assert_ne!(closed_zero, open_zero);
            assert!(closed_zero < open_zero);

            // Test with very small differences
            let closed_small = LowerBoundRuntime::Closed(LowerBoundClosed::new(1e-15));
            let open_small = LowerBoundRuntime::Open(LowerBoundOpen::new(1e-15));
            assert!(closed_small < open_small);
        }

        #[test]
        fn test_interval_lower_bound_transitivity() {
            let closed_1 = LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0));
            let open_1 = LowerBoundRuntime::Open(LowerBoundOpen::new(1.0));
            let closed_2 = LowerBoundRuntime::Closed(LowerBoundClosed::new(2.0));

            // Test transitivity: if a < b and b < c, then a < c
            assert!(closed_1 < open_1);

            assert!(open_1 < closed_2);
            assert!(closed_1 < closed_2); // transitivity
        }

        #[test]
        fn test_interval_lower_bound_reflexivity() {
            let closed = LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0));
            let open = LowerBoundRuntime::Open(LowerBoundOpen::new(1.0));

            // Test reflexivity: a == a
            assert_eq!(closed.partial_cmp(&closed), Some(Ordering::Equal));
            assert_eq!(open.partial_cmp(&open), Some(Ordering::Equal));
        }

        #[test]
        fn test_interval_lower_bound_antisymmetry() {
            let closed_1 = LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0));
            let closed_2 = LowerBoundRuntime::Closed(LowerBoundClosed::new(2.0));
            let open_1 = LowerBoundRuntime::Open(LowerBoundOpen::new(1.0));

            // Test antisymmetry: if a <= b and b <= a, then a == b
            // (This should only happen when a == b)
            assert_eq!(closed_1.partial_cmp(&closed_1), Some(Ordering::Equal));

            // When a < b, then b > a (not equal)
            assert_eq!(closed_1.partial_cmp(&closed_2), Some(Ordering::Less));
            assert_eq!(closed_2.partial_cmp(&closed_1), Some(Ordering::Greater));

            assert_eq!(closed_1.partial_cmp(&open_1), Some(Ordering::Less));
            assert_eq!(open_1.partial_cmp(&closed_1), Some(Ordering::Greater));
        }

        #[test]
        fn test_min_lower_bound() {
            let closed_1 = LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0));
            let open_1 = LowerBoundRuntime::Open(LowerBoundOpen::new(1.0));
            let closed_2 = LowerBoundRuntime::Closed(LowerBoundClosed::new(2.0));

            // min with same value, different types should be closed (smaller)
            assert_eq!(min_lower_bound(closed_1.clone(), open_1.clone()), closed_1);
            assert_eq!(min_lower_bound(open_1.clone(), closed_1.clone()), closed_1);

            // min with different values
            assert_eq!(
                min_lower_bound(closed_1.clone(), closed_2.clone()),
                closed_1
            );
            assert_eq!(
                min_lower_bound(closed_2.clone(), closed_1.clone()),
                closed_1
            );

            // min with same bound should return that bound
            assert_eq!(
                min_lower_bound(closed_1.clone(), closed_1.clone()),
                closed_1
            );
        }

        #[test]
        fn test_max_lower_bound() {
            let closed_1 = LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0));
            let open_1 = LowerBoundRuntime::Open(LowerBoundOpen::new(1.0));
            let closed_2 = LowerBoundRuntime::Closed(LowerBoundClosed::new(2.0));

            // max with same value, different types should be open (larger)
            assert_eq!(max_lower_bound(closed_1.clone(), open_1.clone()), open_1);
            assert_eq!(max_lower_bound(open_1.clone(), closed_1.clone()), open_1);

            // max with different values
            assert_eq!(
                max_lower_bound(closed_1.clone(), closed_2.clone()),
                closed_2
            );
            assert_eq!(
                max_lower_bound(closed_2.clone(), closed_1.clone()),
                closed_2
            );

            // max with same bound should return that bound
            assert_eq!(
                max_lower_bound(closed_1.clone(), closed_1.clone()),
                closed_1
            );
        }

        #[test]
        fn test_interval_lower_bound_with_negative_values() {
            let closed_neg = LowerBoundRuntime::Closed(LowerBoundClosed::new(-5.0));
            let open_neg = LowerBoundRuntime::Open(LowerBoundOpen::new(-5.0));
            let closed_pos = LowerBoundRuntime::Closed(LowerBoundClosed::new(5.0));

            // Negative values should still follow the ordering rules
            assert!(closed_neg < open_neg);
            assert!(open_neg < closed_pos);

            // More negative values should be smaller
            let closed_more_neg = LowerBoundRuntime::Closed(LowerBoundClosed::new(-10.0));
            assert!(closed_more_neg < closed_neg);
        }

        #[test]
        fn test_interval_lower_bound_with_large_values() {
            let closed_large = LowerBoundRuntime::Closed(LowerBoundClosed::new(1e6));
            let open_large = LowerBoundRuntime::Open(LowerBoundOpen::new(1e6));
            let closed_larger = LowerBoundRuntime::Closed(LowerBoundClosed::new(1e9));

            // Large values should still follow ordering
            assert!(closed_large < open_large);
            assert!(open_large < closed_larger);

            // Should be consistent with regular values
            let closed_small = LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0));
            assert!(closed_small < closed_large);
        }
    }

    mod get_interval_bound_trait {
        use super::*;
        use crate::intervals::*;

        #[test]
        fn get_interval_bound_closed() {
            let interval = IntervalClosed::new(1.0, 2.0);

            let lower = interval.lower_bound_runtime().unwrap();
            assert_eq!(lower, LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0)));

            let upper = interval.upper_bound_runtime().unwrap();
            assert_eq!(upper, UpperBoundRuntime::Closed(UpperBoundClosed::new(2.0)));
        }

        #[test]
        fn get_interval_bound_open() {
            let interval = IntervalOpen::new(1.0, 2.0);

            let lower = interval.lower_bound_runtime().unwrap();
            assert_eq!(lower, LowerBoundRuntime::Open(LowerBoundOpen::new(1.0)));

            let upper = interval.upper_bound_runtime().unwrap();
            assert_eq!(upper, UpperBoundRuntime::Open(UpperBoundOpen::new(2.0)));
        }

        #[test]
        fn get_interval_bound_left_open_upper_closed() {
            let interval = IntervalLowerOpenUpperClosed::new(1.0, 2.0);

            let lower = interval.lower_bound_runtime().unwrap();
            assert_eq!(lower, LowerBoundRuntime::Open(LowerBoundOpen::new(1.0)));

            let upper = interval.upper_bound_runtime().unwrap();
            assert_eq!(upper, UpperBoundRuntime::Closed(UpperBoundClosed::new(2.0)));
        }

        #[test]
        fn get_interval_bound_left_closed_upper_open() {
            let interval = IntervalLowerClosedUpperOpen::new(1.0, 2.0);

            let lower = interval.lower_bound_runtime().unwrap();
            assert_eq!(lower, LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0)));

            let upper = interval.upper_bound_runtime().unwrap();
            assert_eq!(upper, UpperBoundRuntime::Open(UpperBoundOpen::new(2.0)));
        }

        #[test]
        fn get_interval_bound_lower_closed_upper_unbounded() {
            let interval = IntervalLowerClosedUpperUnbounded::new(1.0);

            let lower = interval.lower_bound_runtime().unwrap();
            assert_eq!(lower, LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0)));

            let upper = interval.upper_bound_runtime();
            assert_eq!(upper, None);
        }

        #[test]
        fn get_interval_bound_lower_open_upper_unbounded() {
            let interval = IntervalLowerOpenUpperUnbounded::new(1.0);

            let lower = interval.lower_bound_runtime().unwrap();
            assert_eq!(lower, LowerBoundRuntime::Open(LowerBoundOpen::new(1.0)));

            let upper = interval.upper_bound_runtime();
            assert_eq!(upper, None);
        }

        #[test]
        fn get_interval_bound_lower_unbounded_upper_closed() {
            let interval = IntervalLowerUnboundedUpperClosed::new(2.0);

            let lower = interval.lower_bound_runtime();
            assert_eq!(lower, None);

            let upper = interval.upper_bound_runtime().unwrap();
            assert_eq!(upper, UpperBoundRuntime::Closed(UpperBoundClosed::new(2.0)));
        }

        #[test]
        fn get_interval_bound_lower_unbounded_upper_open() {
            let interval = IntervalLowerUnboundedUpperOpen::new(2.0);

            let lower = interval.lower_bound_runtime();
            assert_eq!(lower, None);

            let upper = interval.upper_bound_runtime().unwrap();
            assert_eq!(upper, UpperBoundRuntime::Open(UpperBoundOpen::new(2.0)));
        }

        #[test]
        fn get_interval_bound_singleton() {
            let interval = IntervalSingleton::new(1.5);

            let lower = interval.lower_bound_runtime().unwrap();
            assert_eq!(lower, LowerBoundRuntime::Closed(LowerBoundClosed::new(1.5)));

            let upper = interval.upper_bound_runtime().unwrap();
            assert_eq!(upper, UpperBoundRuntime::Closed(UpperBoundClosed::new(1.5)));
        }

        #[test]
        fn get_interval_bound_finite_length() {
            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);

            let lower = finite.lower_bound_runtime().unwrap();
            let upper = finite.upper_bound_runtime().unwrap();
            assert_eq!(lower, LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0)));
            assert_eq!(upper, UpperBoundRuntime::Closed(UpperBoundClosed::new(2.0)));

            let lower = zero.lower_bound_runtime().unwrap();
            let upper = zero.upper_bound_runtime().unwrap();
            assert_eq!(lower, LowerBoundRuntime::Closed(LowerBoundClosed::new(1.5)));
            assert_eq!(upper, UpperBoundRuntime::Closed(UpperBoundClosed::new(1.5)));
        }

        #[test]
        fn get_interval_bound_finite_positive_length() {
            use crate::intervals::IntervalFinitePositiveLengthTrait;

            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);

            let (lower, upper) = (c.lower_bound_runtime(), c.upper_bound_runtime());
            assert_eq!(
                lower.unwrap(),
                LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0))
            );
            assert_eq!(
                upper.unwrap(),
                UpperBoundRuntime::Closed(UpperBoundClosed::new(2.0))
            );
            assert_eq!(c.clone().into_bounds_pair(), (1., 2.));

            let (lower, upper) = (o.lower_bound_runtime(), o.upper_bound_runtime());
            assert_eq!(
                lower.unwrap(),
                LowerBoundRuntime::Open(LowerBoundOpen::new(1.0))
            );
            assert_eq!(
                upper.unwrap(),
                UpperBoundRuntime::Open(UpperBoundOpen::new(2.0))
            );
            assert_eq!(o.clone().into_bounds_pair(), (1., 2.));

            let (lower, upper) = (l.lower_bound_runtime(), l.upper_bound_runtime());
            assert_eq!(
                lower.unwrap(),
                LowerBoundRuntime::Open(LowerBoundOpen::new(1.0))
            );
            assert_eq!(
                upper.unwrap(),
                UpperBoundRuntime::Closed(UpperBoundClosed::new(2.0))
            );
            assert_eq!(l.clone().into_bounds_pair(), (1., 2.));

            let (lower, upper) = (r.lower_bound_runtime(), r.upper_bound_runtime());
            assert_eq!(
                lower.unwrap(),
                LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0))
            );
            assert_eq!(
                upper.unwrap(),
                UpperBoundRuntime::Open(UpperBoundOpen::new(2.0))
            );
            assert_eq!(r.clone().into_bounds_pair(), (1., 2.));
        }

        #[test]
        fn get_interval_bound_infinite_length() {
            let closed = IntervalLowerClosedUpperUnbounded::new(1.0);
            let open = IntervalLowerOpenUpperUnbounded::new(2.0);
            let right_closed = IntervalLowerUnboundedUpperClosed::new(3.0);
            let right_open = IntervalLowerUnboundedUpperOpen::new(4.0);
            let unbounded = IntervalLowerUnboundedUpperUnbounded::<f64>::new();

            let c = IntervalInfiniteLength::LowerClosedUpperUnbounded(closed);
            let o = IntervalInfiniteLength::LowerOpenUpperUnbounded(open);
            let rc = IntervalInfiniteLength::LowerUnboundedUpperClosed(right_closed);
            let ro = IntervalInfiniteLength::LowerUnboundedUpperOpen(right_open);
            let u = IntervalInfiniteLength::LowerUnboundedUpperUnbounded(unbounded);

            let (lower, upper) = (c.lower_bound_runtime(), c.upper_bound_runtime());
            assert_eq!(
                lower.unwrap(),
                LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0))
            );
            assert_eq!(upper, None);

            let (lower, upper) = (o.lower_bound_runtime(), o.upper_bound_runtime());
            assert_eq!(
                lower.unwrap(),
                LowerBoundRuntime::Open(LowerBoundOpen::new(2.0))
            );
            assert_eq!(upper, None);

            let (lower, upper) = (rc.lower_bound_runtime(), rc.upper_bound_runtime());
            assert_eq!(lower, None);
            assert_eq!(
                upper.unwrap(),
                UpperBoundRuntime::Closed(UpperBoundClosed::new(3.0))
            );

            let (lower, upper) = (ro.lower_bound_runtime(), ro.upper_bound_runtime());
            assert_eq!(lower, None);
            assert_eq!(
                upper.unwrap(),
                UpperBoundRuntime::Open(UpperBoundOpen::new(4.0))
            );

            let (lower, upper) = (u.lower_bound_runtime(), u.upper_bound_runtime());
            assert_eq!(lower, None);
            assert_eq!(upper, None);
        }

        #[test]
        fn get_interval_bound_enum_wrappers() {
            let closed = IntervalClosed::new(1.0, 2.0);
            let finite =
                IntervalFiniteLength::PositiveLength(IntervalFinitePositiveLength::Closed(closed));
            let inf = IntervalInfiniteLength::LowerUnboundedUpperUnbounded(
                IntervalLowerUnboundedUpperUnbounded::<f64>::new(),
            );
            let interval = Interval::FiniteLength(finite.clone());
            let interval_inf = Interval::InfiniteLength(inf.clone());

            let (lower, upper) = (
                interval.lower_bound_runtime(),
                interval.upper_bound_runtime(),
            );
            assert_eq!(
                lower.unwrap(),
                LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0))
            );
            assert_eq!(
                upper.unwrap(),
                UpperBoundRuntime::Closed(UpperBoundClosed::new(2.0))
            );

            let (lower, upper) = (
                interval_inf.lower_bound_runtime(),
                interval_inf.upper_bound_runtime(),
            );
            assert_eq!(lower, None);
            assert_eq!(upper, None);
        }

        #[test]
        fn get_interval_bound_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);

            let (lower, upper) = (s1.lower_bound_runtime(), s1.upper_bound_runtime());
            assert_eq!(
                lower,
                Some(LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0)))
            );
            assert_eq!(
                upper,
                Some(UpperBoundRuntime::Closed(UpperBoundClosed::new(2.0)))
            );

            let (lower, upper) = (s2.lower_bound_runtime(), s2.upper_bound_runtime());
            assert_eq!(
                lower,
                Some(LowerBoundRuntime::Open(LowerBoundOpen::new(2.0)))
            );
            assert_eq!(
                upper,
                Some(UpperBoundRuntime::Closed(UpperBoundClosed::new(3.0)))
            );
        }
    }

    mod partial_ord {
        use super::*;

        #[test]
        fn partial_ord_lower_bound_structs() {
            let closed1 = LowerBoundClosed::new(1.0);
            let closed2 = LowerBoundClosed::new(2.0);
            let open1 = LowerBoundOpen::new(1.0);
            let open2 = LowerBoundOpen::new(2.0);

            assert!(closed1 < closed2);
            assert!(open1 < open2);
            assert!(closed1 < open2);
            assert!(open1 < closed2);
            assert!(closed1 < open1); // closed < open if same value
            assert!(open2 > closed1);
            assert!(closed1 <= closed1);
            assert!(open2 >= open2);
            assert!(!(closed1 == open1));
        }

        #[test]
        fn partial_ord_upper_bound_structs() {
            let closed1 = UpperBoundClosed::new(1.0);
            let closed2 = UpperBoundClosed::new(2.0);
            let open1 = UpperBoundOpen::new(1.0);
            let open2 = UpperBoundOpen::new(2.0);

            assert!(closed1 < closed2);
            assert!(open1 < open2);
            assert!(closed1 < open2);
            assert!(open1 < closed2);
            assert!(open1 < closed1); // open < closed if same value
            assert!(closed2 > open1);
            assert!(closed1 <= closed1);
            assert!(open2 >= open2);
            assert!(!(closed1 == open1));
        }

        #[test]
        fn partial_ord_interval_lower_bound_enum() {
            let closed = LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0));
            let open = LowerBoundRuntime::Open(LowerBoundOpen::new(2.0));

            assert_eq!(closed.partial_cmp(&open), Some(std::cmp::Ordering::Less));
            assert_eq!(open.partial_cmp(&closed), Some(std::cmp::Ordering::Greater));
            assert_eq!(closed.partial_cmp(&closed), Some(std::cmp::Ordering::Equal));
            assert_eq!(open.partial_cmp(&open), Some(std::cmp::Ordering::Equal));
        }

        #[test]
        fn partial_ord_interval_upper_bound_enum() {
            let closed = UpperBoundRuntime::Closed(UpperBoundClosed::new(1.0));
            let open = UpperBoundRuntime::Open(UpperBoundOpen::new(2.0));

            assert_eq!(closed.partial_cmp(&open), Some(std::cmp::Ordering::Less));
            assert_eq!(open.partial_cmp(&closed), Some(std::cmp::Ordering::Greater));
            assert_eq!(closed.partial_cmp(&closed), Some(std::cmp::Ordering::Equal));
            assert_eq!(open.partial_cmp(&open), Some(std::cmp::Ordering::Equal));
        }

        #[test]
        fn partial_ord_interval_lower_bound_enum_same_value() {
            let closed = LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0));
            let open = LowerBoundRuntime::Open(LowerBoundOpen::new(1.0));
            assert_eq!(closed.partial_cmp(&open), Some(std::cmp::Ordering::Less));
            assert_eq!(open.partial_cmp(&closed), Some(std::cmp::Ordering::Greater));
        }

        #[test]
        fn partial_ord_interval_upper_bound_enum_same_value() {
            let closed = UpperBoundRuntime::Closed(UpperBoundClosed::new(1.0));
            let open = UpperBoundRuntime::Open(UpperBoundOpen::new(1.0));
            assert_eq!(open.partial_cmp(&closed), Some(std::cmp::Ordering::Less));
            assert_eq!(closed.partial_cmp(&open), Some(std::cmp::Ordering::Greater));
        }
    }

    mod test_interval_bound_comparisons {
        use super::*;
        use try_create::TryNew;

        type Real = f64;

        /// Test PartialOrd implementation for LowerBoundRuntime
        #[test]
        fn test_interval_lower_bound_comparison() {
            // Create test bounds
            let lower_bound_closed_5 =
                LowerBoundRuntime::Closed(LowerBoundClosed::new(Real::try_new(5.0).unwrap()));
            let lower_bound_open_5 =
                LowerBoundRuntime::Open(LowerBoundOpen::new(Real::try_new(5.0).unwrap()));
            let lower_bound_closed_3 =
                LowerBoundRuntime::Closed(LowerBoundClosed::new(Real::try_new(3.0).unwrap()));
            let lower_bound_open_3 =
                LowerBoundRuntime::Open(LowerBoundOpen::new(Real::try_new(3.0).unwrap()));
            let lower_bound_closed_7 =
                LowerBoundRuntime::Closed(LowerBoundClosed::new(Real::try_new(7.0).unwrap()));

            // Test basic ordering by value
            assert_eq!(
                lower_bound_closed_3.partial_cmp(&lower_bound_closed_5),
                Some(Ordering::Less)
            );
            assert_eq!(
                lower_bound_closed_5.partial_cmp(&lower_bound_closed_3),
                Some(Ordering::Greater)
            );
            assert_eq!(
                lower_bound_closed_5.partial_cmp(&lower_bound_closed_5),
                Some(Ordering::Equal)
            );

            // Test ordering between closed and open bounds with same value
            // For lower bounds: closed < open at same value (closed is more restrictive for lower)
            assert_eq!(
                lower_bound_closed_5.partial_cmp(&lower_bound_open_5),
                Some(Ordering::Less)
            );
            assert_eq!(
                lower_bound_open_5.partial_cmp(&lower_bound_closed_5),
                Some(Ordering::Greater)
            );

            // Test mixed value and boundary type comparisons
            assert_eq!(
                lower_bound_closed_3.partial_cmp(&lower_bound_open_5),
                Some(Ordering::Less)
            );
            assert_eq!(
                lower_bound_open_3.partial_cmp(&lower_bound_closed_5),
                Some(Ordering::Less)
            );
            assert_eq!(
                lower_bound_closed_5.partial_cmp(&lower_bound_open_3),
                Some(Ordering::Greater)
            );

            // Test transitivity
            assert_eq!(
                lower_bound_closed_3.partial_cmp(&lower_bound_closed_7),
                Some(Ordering::Less)
            );
            assert_eq!(
                lower_bound_open_3.partial_cmp(&lower_bound_closed_7),
                Some(Ordering::Less)
            );
        }

        /// Test PartialOrd implementation for UpperBoundRuntime
        #[test]
        fn test_interval_upper_bound_comparison() {
            // Create test bounds
            let upper_bound_closed_5 =
                UpperBoundRuntime::Closed(UpperBoundClosed::new(Real::try_new(5.0).unwrap()));
            let upper_bound_open_5 =
                UpperBoundRuntime::Open(UpperBoundOpen::new(Real::try_new(5.0).unwrap()));
            let upper_bound_closed_3 =
                UpperBoundRuntime::Closed(UpperBoundClosed::new(Real::try_new(3.0).unwrap()));
            let upper_bound_open_3 =
                UpperBoundRuntime::Open(UpperBoundOpen::new(Real::try_new(3.0).unwrap()));
            let upper_bound_closed_7 =
                UpperBoundRuntime::Closed(UpperBoundClosed::new(Real::try_new(7.0).unwrap()));

            // Test basic ordering by value
            assert_eq!(
                upper_bound_closed_3.partial_cmp(&upper_bound_closed_5),
                Some(Ordering::Less)
            );
            assert_eq!(
                upper_bound_closed_5.partial_cmp(&upper_bound_closed_3),
                Some(Ordering::Greater)
            );
            assert_eq!(
                upper_bound_closed_5.partial_cmp(&upper_bound_closed_5),
                Some(Ordering::Equal)
            );

            // Test ordering between closed and open bounds with same value
            // For upper bounds: open < closed at same value (open is more restrictive for upper)
            assert_eq!(
                upper_bound_open_5.partial_cmp(&upper_bound_closed_5),
                Some(Ordering::Less)
            );
            assert_eq!(
                upper_bound_closed_5.partial_cmp(&upper_bound_open_5),
                Some(Ordering::Greater)
            );

            // Test mixed value and boundary type comparisons
            assert_eq!(
                upper_bound_closed_3.partial_cmp(&upper_bound_open_5),
                Some(Ordering::Less)
            );
            assert_eq!(
                upper_bound_open_3.partial_cmp(&upper_bound_closed_5),
                Some(Ordering::Less)
            );
            assert_eq!(
                upper_bound_closed_5.partial_cmp(&upper_bound_open_3),
                Some(Ordering::Greater)
            );

            // Test transitivity
            assert_eq!(
                upper_bound_closed_3.partial_cmp(&upper_bound_closed_7),
                Some(Ordering::Less)
            );
            assert_eq!(
                upper_bound_open_3.partial_cmp(&upper_bound_closed_7),
                Some(Ordering::Less)
            );
        }

        /// Test boundary type semantics for lower bounds
        #[test]
        fn test_lower_bound_semantics() {
            let closed_5 =
                LowerBoundRuntime::Closed(LowerBoundClosed::new(Real::try_new(5.0).unwrap()));
            let open_5 = LowerBoundRuntime::Open(LowerBoundOpen::new(Real::try_new(5.0).unwrap()));

            // For lower bounds, closed is more restrictive (smaller) than open at same value
            // This is because [5,b) contains 5, while (5,b) does not
            more_asserts::assert_lt!(closed_5, open_5);
            more_asserts::assert_ge!(open_5, closed_5);
            assert_ne!(closed_5, open_5);
        }

        /// Test boundary type semantics for upper bounds
        #[test]
        fn test_upper_bound_semantics() {
            let closed_5 =
                UpperBoundRuntime::Closed(UpperBoundClosed::new(Real::try_new(5.0).unwrap()));
            let open_5 = UpperBoundRuntime::Open(UpperBoundOpen::new(Real::try_new(5.0).unwrap()));

            // For upper bounds, open is more restrictive (smaller) than closed at same value
            // This is because [a,5) excludes 5, while [a,5] includes 5
            more_asserts::assert_lt!(open_5, closed_5);
            more_asserts::assert_ge!(closed_5, open_5);
            assert_ne!(open_5, closed_5);
        }

        /// Test consistency with mathematical interval ordering
        #[test]
        fn test_mathematical_consistency() {
            // Create bounds that would represent valid intervals
            let lower_closed_1 =
                LowerBoundRuntime::Closed(LowerBoundClosed::new(Real::try_new(1.0).unwrap()));
            let lower_open_1 =
                LowerBoundRuntime::Open(LowerBoundOpen::new(Real::try_new(1.0).unwrap()));
            let upper_open_2 =
                UpperBoundRuntime::Open(UpperBoundOpen::new(Real::try_new(2.0).unwrap()));
            let upper_closed_2 =
                UpperBoundRuntime::Closed(UpperBoundClosed::new(Real::try_new(2.0).unwrap()));

            // Mathematical consistency checks:
            // [1,2) should have closed lower bound and open upper bound
            // (1,2] should have open lower bound and closed upper bound
            // [1,2] should have closed bounds
            // (1,2) should have open bounds

            // Verify ordering is consistent with interval containment
            // [1,x] contains more points than (1,x] at the same x
            assert!(lower_closed_1 < lower_open_1);

            // [x,2) contains fewer points than [x,2] at the same x
            assert!(upper_open_2 < upper_closed_2);
        }

        /// Test reflexivity property
        #[test]
        fn test_reflexivity() {
            let lower_closed =
                LowerBoundRuntime::Closed(LowerBoundClosed::new(Real::try_new(5.0).unwrap()));
            let lower_open =
                LowerBoundRuntime::Open(LowerBoundOpen::new(Real::try_new(5.0).unwrap()));
            let upper_closed =
                UpperBoundRuntime::Closed(UpperBoundClosed::new(Real::try_new(5.0).unwrap()));
            let upper_open =
                UpperBoundRuntime::Open(UpperBoundOpen::new(Real::try_new(5.0).unwrap()));

            // Reflexivity: x.partial_cmp(&x) == Some(Ordering::Equal)
            assert_eq!(
                lower_closed.partial_cmp(&lower_closed),
                Some(Ordering::Equal)
            );
            assert_eq!(lower_open.partial_cmp(&lower_open), Some(Ordering::Equal));
            assert_eq!(
                upper_closed.partial_cmp(&upper_closed),
                Some(Ordering::Equal)
            );
            assert_eq!(upper_open.partial_cmp(&upper_open), Some(Ordering::Equal));
        }

        /// Test antisymmetry property
        #[test]
        fn test_antisymmetry() {
            let lower_closed_3 =
                LowerBoundRuntime::Closed(LowerBoundClosed::new(Real::try_new(3.0).unwrap()));
            let lower_closed_5 =
                LowerBoundRuntime::Closed(LowerBoundClosed::new(Real::try_new(5.0).unwrap()));

            // Antisymmetry: if x <= y and y <= x, then x == y
            if lower_closed_3.partial_cmp(&lower_closed_5) == Some(Ordering::Less) {
                assert_eq!(
                    lower_closed_5.partial_cmp(&lower_closed_3),
                    Some(Ordering::Greater)
                );
            }
        }

        /// Test transitivity property
        #[test]
        fn test_transitivity() {
            let lower_closed_1 =
                LowerBoundRuntime::Closed(LowerBoundClosed::new(Real::try_new(1.0).unwrap()));
            let lower_open_3 =
                LowerBoundRuntime::Open(LowerBoundOpen::new(Real::try_new(3.0).unwrap()));
            let lower_closed_5 =
                LowerBoundRuntime::Closed(LowerBoundClosed::new(Real::try_new(5.0).unwrap()));

            // Transitivity: if x <= y and y <= z, then x <= z
            let ord_1_3 = lower_closed_1.partial_cmp(&lower_open_3);
            let ord_3_5 = lower_open_3.partial_cmp(&lower_closed_5);
            let ord_1_5 = lower_closed_1.partial_cmp(&lower_closed_5);

            if ord_1_3 == Some(Ordering::Less) && ord_3_5 == Some(Ordering::Less) {
                assert_eq!(ord_1_5, Some(Ordering::Less));
            }
        }

        /// Test edge cases with same values but different boundary types
        #[test]
        fn test_edge_cases_same_values() {
            let value = Real::try_new(42.0).unwrap();

            let lower_closed = LowerBoundRuntime::Closed(LowerBoundClosed::new(value));
            let lower_open = LowerBoundRuntime::Open(LowerBoundOpen::new(value));
            let upper_closed = UpperBoundRuntime::Closed(UpperBoundClosed::new(value));
            let upper_open = UpperBoundRuntime::Open(UpperBoundOpen::new(value));

            // Test that boundary type matters even with same value
            assert_ne!(lower_closed.partial_cmp(&lower_open), Some(Ordering::Equal));
            assert_ne!(upper_closed.partial_cmp(&upper_open), Some(Ordering::Equal));

            // Verify specific ordering relationships
            assert_eq!(lower_closed.partial_cmp(&lower_open), Some(Ordering::Less));
            assert_eq!(upper_open.partial_cmp(&upper_closed), Some(Ordering::Less));
        }

        /// Test with extreme values
        #[test]
        fn test_extreme_values() {
            let min_val = Real::try_new(f64::MIN).unwrap();
            let max_val = Real::try_new(f64::MAX).unwrap();

            let lower_min_closed = LowerBoundRuntime::Closed(LowerBoundClosed::new(min_val));
            let lower_max_open = LowerBoundRuntime::Open(LowerBoundOpen::new(max_val));
            let upper_min_open = UpperBoundRuntime::Open(UpperBoundOpen::new(min_val));
            let upper_max_closed = UpperBoundRuntime::Closed(UpperBoundClosed::new(max_val));

            // Test that extreme values still follow ordering rules
            assert_eq!(
                lower_min_closed.partial_cmp(&lower_max_open),
                Some(Ordering::Less)
            );
            assert_eq!(
                upper_min_open.partial_cmp(&upper_max_closed),
                Some(Ordering::Less)
            );
        }

        /// Test helper functions min_lower_bound and max_lower_bound
        #[test]
        fn test_min_max_lower_bound() {
            let lower_closed_3 =
                LowerBoundRuntime::Closed(LowerBoundClosed::new(Real::try_new(3.0).unwrap()));
            let lower_open_5 =
                LowerBoundRuntime::Open(LowerBoundOpen::new(Real::try_new(5.0).unwrap()));

            // Test min_lower_bound
            let min_result = min_lower_bound(lower_closed_3.clone(), lower_open_5.clone());
            assert_eq!(min_result, lower_closed_3);

            // Test max_lower_bound
            let max_result = max_lower_bound(lower_closed_3.clone(), lower_open_5.clone());
            assert_eq!(max_result, lower_open_5);
        }

        /// Test helper functions min_upper_bound and max_upper_bound
        #[test]
        fn test_min_max_upper_bound() {
            let upper_open_3 =
                UpperBoundRuntime::Open(UpperBoundOpen::new(Real::try_new(3.0).unwrap()));
            let upper_closed_5 =
                UpperBoundRuntime::Closed(UpperBoundClosed::new(Real::try_new(5.0).unwrap()));

            // Test min_upper_bound
            let min_result = min_upper_bound(upper_open_3.clone(), upper_closed_5.clone());
            assert_eq!(min_result, upper_open_3);

            // Test max_upper_bound
            let max_result = max_upper_bound(upper_open_3.clone(), upper_closed_5.clone());
            assert_eq!(max_result, upper_closed_5);
        }
    }

    mod bounds_runtime {
        use super::*;

        mod lower_bound {
            use super::*;
            use crate::bounds::{LowerBoundClosed, LowerBoundOpen};
            use std::cmp::Ordering;

            #[test]
            fn test_interval_lower_bound_equality() {
                let closed_1 = LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0));
                let closed_2 = LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0));
                let closed_3 = LowerBoundRuntime::Closed(LowerBoundClosed::new(2.0));

                let open_1 = LowerBoundRuntime::Open(LowerBoundOpen::new(1.0));
                let open_2 = LowerBoundRuntime::Open(LowerBoundOpen::new(1.0));

                // Test equality for same variant and value
                assert_eq!(closed_1, closed_2);
                assert_eq!(open_1, open_2);

                // Test inequality for different values
                assert_ne!(closed_1, closed_3);

                // Test inequality for different variants, same value
                assert_ne!(closed_1, open_1);
            }

            mod interval_upper_bound {
                use super::*;
                use crate::bounds::{UpperBoundClosed, UpperBoundOpen};
                use std::cmp::Ordering;

                #[test]
                fn test_interval_upper_bound_equality() {
                    let closed_1 = UpperBoundRuntime::Closed(UpperBoundClosed::new(1.0));
                    let closed_2 = UpperBoundRuntime::Closed(UpperBoundClosed::new(1.0));
                    let closed_3 = UpperBoundRuntime::Closed(UpperBoundClosed::new(2.0));

                    let open_1 = UpperBoundRuntime::Open(UpperBoundOpen::new(1.0));
                    let open_2 = UpperBoundRuntime::Open(UpperBoundOpen::new(1.0));

                    // Test equality for same variant and value
                    assert_eq!(closed_1, closed_2);
                    assert_eq!(open_1, open_2);

                    // Test inequality for different values
                    assert_ne!(closed_1, closed_3);

                    // Test inequality for different variants, same value
                    assert_ne!(closed_1, open_1);
                }

                #[test]
                fn test_interval_upper_bound_partial_ord() {
                    let closed_1 = UpperBoundRuntime::Closed(UpperBoundClosed::new(1.0));
                    let closed_2 = UpperBoundRuntime::Closed(UpperBoundClosed::new(2.0));
                    let open_1 = UpperBoundRuntime::Open(UpperBoundOpen::new(1.0));
                    let open_2 = UpperBoundRuntime::Open(UpperBoundOpen::new(2.0));

                    // Test closed vs closed
                    assert_eq!(closed_1.partial_cmp(&closed_1), Some(Ordering::Equal));
                    assert_eq!(closed_1.partial_cmp(&closed_2), Some(Ordering::Less));
                    assert_eq!(closed_2.partial_cmp(&closed_1), Some(Ordering::Greater));

                    // Test open vs open
                    assert_eq!(open_1.partial_cmp(&open_1), Some(Ordering::Equal));
                    assert_eq!(open_1.partial_cmp(&open_2), Some(Ordering::Less));
                    assert_eq!(open_2.partial_cmp(&open_1), Some(Ordering::Greater));

                    // Test open vs closed with same value (open < closed for upper bounds)
                    assert_eq!(open_1.partial_cmp(&closed_1), Some(Ordering::Less));
                    assert_eq!(closed_1.partial_cmp(&open_1), Some(Ordering::Greater));

                    // Test open vs closed with different values
                    assert_eq!(open_1.partial_cmp(&closed_2), Some(Ordering::Less));
                    assert_eq!(closed_1.partial_cmp(&open_2), Some(Ordering::Less));
                }

                #[test]
                fn test_interval_upper_bound_ordering_consistency() {
                    let closed_1 = UpperBoundRuntime::Closed(UpperBoundClosed::new(1.0));
                    let open_1 = UpperBoundRuntime::Open(UpperBoundOpen::new(1.0));
                    let closed_2 = UpperBoundRuntime::Closed(UpperBoundClosed::new(2.0));
                    let open_2 = UpperBoundRuntime::Open(UpperBoundOpen::new(2.0));

                    // Create sorted list based on expected ordering
                    // For upper bounds: smaller values < larger values, and open < closed for same value
                    let bounds = vec![
                        open_1.clone(),
                        closed_1.clone(),
                        open_2.clone(),
                        closed_2.clone(),
                    ];

                    // Test that the ordering is consistent
                    for i in 0..bounds.len() {
                        for j in i + 1..bounds.len() {
                            assert!(bounds[i] < bounds[j],);
                            assert!(bounds[j] > bounds[i],);
                        }
                    }

                    // Test sorting
                    let mut shuffled = bounds.clone();
                    shuffled.reverse();
                    shuffled.sort_by(|a, b| a.partial_cmp(b).unwrap());
                    assert_eq!(shuffled, bounds);
                }

                #[test]
                fn test_interval_upper_bound_edge_cases() {
                    // Test with special floating point values
                    let closed_zero = UpperBoundRuntime::Closed(UpperBoundClosed::new(0.0));
                    let closed_neg_zero = UpperBoundRuntime::Closed(UpperBoundClosed::new(-0.0));
                    let open_zero = UpperBoundRuntime::Open(UpperBoundOpen::new(0.0));

                    // 0.0 and -0.0 should be equal
                    assert_eq!(closed_zero, closed_neg_zero);

                    // But closed and open should be different even with same value
                    assert_ne!(closed_zero, open_zero);
                    assert!(open_zero < closed_zero); // open < closed for upper bounds

                    // Test with very small differences
                    let closed_small = UpperBoundRuntime::Closed(UpperBoundClosed::new(1e-15));
                    let open_small = UpperBoundRuntime::Open(UpperBoundOpen::new(1e-15));
                    assert!(open_small < closed_small);
                }

                #[test]
                fn test_interval_upper_bound_transitivity() {
                    let open_1 = UpperBoundRuntime::Open(UpperBoundOpen::new(1.0));
                    let closed_1 = UpperBoundRuntime::Closed(UpperBoundClosed::new(1.0));
                    let closed_2 = UpperBoundRuntime::Closed(UpperBoundClosed::new(2.0));

                    // Test transitivity: if a < b and b < c, then a < c
                    assert!(open_1 < closed_1);
                    assert!(closed_1 < closed_2);
                    assert!(open_1 < closed_2); // transitivity
                }

                #[test]
                fn test_interval_upper_bound_reflexivity() {
                    let closed = UpperBoundRuntime::Closed(UpperBoundClosed::new(1.0));
                    let open = UpperBoundRuntime::Open(UpperBoundOpen::new(1.0));

                    // Test reflexivity: a == a
                    assert_eq!(closed.partial_cmp(&closed), Some(Ordering::Equal));
                    assert_eq!(open.partial_cmp(&open), Some(Ordering::Equal));
                }

                #[test]
                fn test_interval_upper_bound_antisymmetry() {
                    let closed_1 = UpperBoundRuntime::Closed(UpperBoundClosed::new(1.0));
                    let closed_2 = UpperBoundRuntime::Closed(UpperBoundClosed::new(2.0));
                    let open_1 = UpperBoundRuntime::Open(UpperBoundOpen::new(1.0));

                    // Test antisymmetry: if a <= b and b <= a, then a == b
                    // (This should only happen when a == b)
                    assert_eq!(closed_1.partial_cmp(&closed_1), Some(Ordering::Equal));

                    // When a < b, then b > a (not equal)
                    assert_eq!(closed_1.partial_cmp(&closed_2), Some(Ordering::Less));
                    assert_eq!(closed_2.partial_cmp(&closed_1), Some(Ordering::Greater));

                    assert_eq!(open_1.partial_cmp(&closed_1), Some(Ordering::Less));
                    assert_eq!(closed_1.partial_cmp(&open_1), Some(Ordering::Greater));
                }

                #[test]
                fn test_min_upper_bound() {
                    let closed_1 = UpperBoundRuntime::Closed(UpperBoundClosed::new(1.0));
                    let open_1 = UpperBoundRuntime::Open(UpperBoundOpen::new(1.0));
                    let closed_2 = UpperBoundRuntime::Closed(UpperBoundClosed::new(2.0));

                    // min with same value, different types should be open (smaller for upper bounds)
                    assert_eq!(min_upper_bound(closed_1.clone(), open_1.clone()), open_1);
                    assert_eq!(min_upper_bound(open_1.clone(), closed_1.clone()), open_1);

                    // min with different values
                    assert_eq!(
                        min_upper_bound(closed_1.clone(), closed_2.clone()),
                        closed_1
                    );
                    assert_eq!(
                        min_upper_bound(closed_2.clone(), closed_1.clone()),
                        closed_1
                    );

                    // min with same bound should return that bound
                    assert_eq!(
                        min_upper_bound(closed_1.clone(), closed_1.clone()),
                        closed_1
                    );
                }

                #[test]
                fn test_max_upper_bound() {
                    let closed_1 = UpperBoundRuntime::Closed(UpperBoundClosed::new(1.0));
                    let open_1 = UpperBoundRuntime::Open(UpperBoundOpen::new(1.0));
                    let closed_2 = UpperBoundRuntime::Closed(UpperBoundClosed::new(2.0));

                    // max with same value, different types should be closed (larger for upper bounds)
                    assert_eq!(max_upper_bound(closed_1.clone(), open_1.clone()), closed_1);
                    assert_eq!(max_upper_bound(open_1.clone(), closed_1.clone()), closed_1);

                    // max with different values
                    assert_eq!(
                        max_upper_bound(closed_1.clone(), closed_2.clone()),
                        closed_2
                    );
                    assert_eq!(
                        max_upper_bound(closed_2.clone(), closed_1.clone()),
                        closed_2
                    );

                    // max with same bound should return that bound
                    assert_eq!(
                        max_upper_bound(closed_1.clone(), closed_1.clone()),
                        closed_1
                    );
                }

                #[test]
                fn test_interval_upper_bound_with_negative_values() {
                    let closed_neg = UpperBoundRuntime::Closed(UpperBoundClosed::new(-5.0));
                    let open_neg = UpperBoundRuntime::Open(UpperBoundOpen::new(-5.0));
                    let closed_pos = UpperBoundRuntime::Closed(UpperBoundClosed::new(5.0));

                    // Negative values should still follow the ordering rules
                    assert!(open_neg < closed_neg);
                    assert!(closed_neg < closed_pos);

                    // More negative values should be smaller
                    let closed_more_neg = UpperBoundRuntime::Closed(UpperBoundClosed::new(-10.0));
                    assert!(closed_more_neg < closed_neg);
                }

                #[test]
                fn test_interval_upper_bound_with_large_values() {
                    let closed_large = UpperBoundRuntime::Closed(UpperBoundClosed::new(1e6));
                    let open_large = UpperBoundRuntime::Open(UpperBoundOpen::new(1e6));
                    let closed_larger = UpperBoundRuntime::Closed(UpperBoundClosed::new(1e9));

                    // Large values should still follow ordering
                    assert!(open_large < closed_large);
                    assert!(closed_large < closed_larger);

                    // Should be consistent with regular values
                    let closed_small = UpperBoundRuntime::Closed(UpperBoundClosed::new(1.0));
                    assert!(closed_small < closed_large);
                }

                #[test]
                fn test_interval_upper_bound_boundary_semantics() {
                    // Test the key difference between upper and lower bounds:
                    // For upper bounds, open < closed (opposite of lower bounds)

                    let open_5 = UpperBoundRuntime::Open(UpperBoundOpen::new(5.0));
                    let closed_5 = UpperBoundRuntime::Closed(UpperBoundClosed::new(5.0));

                    // For upper bounds: open is more restrictive (smaller) than closed
                    assert!(open_5 < closed_5);
                    assert!(closed_5 > open_5);

                    // This reflects the fact that (-∞, 5) ⊂ (-∞, 5]
                    // The open upper bound excludes the endpoint, making it "smaller"
                }
            }

            #[test]
            fn test_interval_lower_bound_partial_ord() {
                let closed_1 = LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0));
                let closed_2 = LowerBoundRuntime::Closed(LowerBoundClosed::new(2.0));
                let open_1 = LowerBoundRuntime::Open(LowerBoundOpen::new(1.0));
                let open_2 = LowerBoundRuntime::Open(LowerBoundOpen::new(2.0));

                // Test closed vs closed
                assert_eq!(closed_1.partial_cmp(&closed_1), Some(Ordering::Equal));
                assert_eq!(closed_1.partial_cmp(&closed_2), Some(Ordering::Less));
                assert_eq!(closed_2.partial_cmp(&closed_1), Some(Ordering::Greater));

                // Test open vs open
                assert_eq!(open_1.partial_cmp(&open_1), Some(Ordering::Equal));
                assert_eq!(open_1.partial_cmp(&open_2), Some(Ordering::Less));
                assert_eq!(open_2.partial_cmp(&open_1), Some(Ordering::Greater));

                // Test closed vs open with same value (closed < open)
                assert_eq!(closed_1.partial_cmp(&open_1), Some(Ordering::Less));
                assert_eq!(open_1.partial_cmp(&closed_1), Some(Ordering::Greater));

                // Test closed vs open with different values
                assert_eq!(closed_1.partial_cmp(&open_2), Some(Ordering::Less));
                assert_eq!(open_1.partial_cmp(&closed_2), Some(Ordering::Less));
            }

            #[test]
            fn test_interval_lower_bound_ordering_consistency() {
                let closed_0 = LowerBoundRuntime::Closed(LowerBoundClosed::new(0.0));
                let closed_1 = LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0));
                let open_0 = LowerBoundRuntime::Open(LowerBoundOpen::new(0.0));
                let open_1 = LowerBoundRuntime::Open(LowerBoundOpen::new(1.0));

                // Create sorted list based on expected ordering
                let bounds = vec![
                    closed_0.clone(),
                    open_0.clone(),
                    closed_1.clone(),
                    open_1.clone(),
                ];

                // Test that the ordering is consistent
                for i in 0..bounds.len() {
                    for j in i + 1..bounds.len() {
                        assert!(bounds[i] < bounds[j],);
                        assert!(bounds[j] > bounds[i],);
                    }
                }

                // Test sorting
                let mut shuffled = bounds.clone();
                shuffled.reverse();
                shuffled.sort_by(|a, b| a.partial_cmp(b).unwrap());
                assert_eq!(shuffled, bounds);
            }

            #[test]
            fn test_interval_lower_bound_edge_cases() {
                // Test with special floating point values
                let closed_zero = LowerBoundRuntime::Closed(LowerBoundClosed::new(0.0));
                let closed_neg_zero = LowerBoundRuntime::Closed(LowerBoundClosed::new(-0.0));
                let open_zero = LowerBoundRuntime::Open(LowerBoundOpen::new(0.0));

                // 0.0 and -0.0 should be equal
                assert_eq!(closed_zero, closed_neg_zero);

                // But closed and open should be different even with same value
                assert_ne!(closed_zero, open_zero);
                assert!(closed_zero < open_zero);

                // Test with very small differences
                let closed_small = LowerBoundRuntime::Closed(LowerBoundClosed::new(1e-15));
                let open_small = LowerBoundRuntime::Open(LowerBoundOpen::new(1e-15));
                assert!(closed_small < open_small);
            }

            #[test]
            fn test_interval_lower_bound_transitivity() {
                let closed_1 = LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0));
                let open_1 = LowerBoundRuntime::Open(LowerBoundOpen::new(1.0));
                let closed_2 = LowerBoundRuntime::Closed(LowerBoundClosed::new(2.0));

                // Test transitivity: if a < b and b < c, then a < c
                assert!(closed_1 < open_1);

                assert!(open_1 < closed_2);
                assert!(closed_1 < closed_2); // transitivity
            }

            #[test]
            fn test_interval_lower_bound_reflexivity() {
                let closed = LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0));
                let open = LowerBoundRuntime::Open(LowerBoundOpen::new(1.0));

                // Test reflexivity: a == a
                assert_eq!(closed.partial_cmp(&closed), Some(Ordering::Equal));
                assert_eq!(open.partial_cmp(&open), Some(Ordering::Equal));
            }

            #[test]
            fn test_interval_lower_bound_antisymmetry() {
                let closed_1 = LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0));
                let closed_2 = LowerBoundRuntime::Closed(LowerBoundClosed::new(2.0));
                let open_1 = LowerBoundRuntime::Open(LowerBoundOpen::new(1.0));

                // Test antisymmetry: if a <= b and b <= a, then a == b
                // (This should only happen when a == b)
                assert_eq!(closed_1.partial_cmp(&closed_1), Some(Ordering::Equal));

                // When a < b, then b > a (not equal)
                assert_eq!(closed_1.partial_cmp(&closed_2), Some(Ordering::Less));
                assert_eq!(closed_2.partial_cmp(&closed_1), Some(Ordering::Greater));

                assert_eq!(closed_1.partial_cmp(&open_1), Some(Ordering::Less));
                assert_eq!(open_1.partial_cmp(&closed_1), Some(Ordering::Greater));
            }

            #[test]
            fn test_min_lower_bound() {
                let closed_1 = LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0));
                let open_1 = LowerBoundRuntime::Open(LowerBoundOpen::new(1.0));
                let closed_2 = LowerBoundRuntime::Closed(LowerBoundClosed::new(2.0));

                // min with same value, different types should be closed (smaller)
                assert_eq!(min_lower_bound(closed_1.clone(), open_1.clone()), closed_1);
                assert_eq!(min_lower_bound(open_1.clone(), closed_1.clone()), closed_1);

                // min with different values
                assert_eq!(
                    min_lower_bound(closed_1.clone(), closed_2.clone()),
                    closed_1
                );
                assert_eq!(
                    min_lower_bound(closed_2.clone(), closed_1.clone()),
                    closed_1
                );

                // min with same bound should return that bound
                assert_eq!(
                    min_lower_bound(closed_1.clone(), closed_1.clone()),
                    closed_1
                );
            }

            #[test]
            fn test_max_lower_bound() {
                let closed_1 = LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0));
                let open_1 = LowerBoundRuntime::Open(LowerBoundOpen::new(1.0));
                let closed_2 = LowerBoundRuntime::Closed(LowerBoundClosed::new(2.0));

                // max with same value, different types should be open (larger)
                assert_eq!(max_lower_bound(closed_1.clone(), open_1.clone()), open_1);
                assert_eq!(max_lower_bound(open_1.clone(), closed_1.clone()), open_1);

                // max with different values
                assert_eq!(
                    max_lower_bound(closed_1.clone(), closed_2.clone()),
                    closed_2
                );
                assert_eq!(
                    max_lower_bound(closed_2.clone(), closed_1.clone()),
                    closed_2
                );

                // max with same bound should return that bound
                assert_eq!(
                    max_lower_bound(closed_1.clone(), closed_1.clone()),
                    closed_1
                );
            }

            #[test]
            fn test_interval_lower_bound_with_negative_values() {
                let closed_neg = LowerBoundRuntime::Closed(LowerBoundClosed::new(-5.0));
                let open_neg = LowerBoundRuntime::Open(LowerBoundOpen::new(-5.0));
                let closed_pos = LowerBoundRuntime::Closed(LowerBoundClosed::new(5.0));

                // Negative values should still follow the ordering rules
                assert!(closed_neg < open_neg);
                assert!(open_neg < closed_pos);

                // More negative values should be smaller
                let closed_more_neg = LowerBoundRuntime::Closed(LowerBoundClosed::new(-10.0));
                assert!(closed_more_neg < closed_neg);
            }

            #[test]
            fn test_interval_lower_bound_with_large_values() {
                let closed_large = LowerBoundRuntime::Closed(LowerBoundClosed::new(1e6));
                let open_large = LowerBoundRuntime::Open(LowerBoundOpen::new(1e6));
                let closed_larger = LowerBoundRuntime::Closed(LowerBoundClosed::new(1e9));

                // Large values should still follow ordering
                assert!(closed_large < open_large);
                assert!(open_large < closed_larger);

                // Should be consistent with regular values
                let closed_small = LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0));
                assert!(closed_small < closed_large);
            }
        }

        mod interval_bounds_runtime {
            use super::*;
            use crate::intervals::*;

            #[test]
            fn get_interval_bound_closed() {
                let interval = IntervalClosed::new(1.0, 2.0);

                let lower = interval.lower_bound_runtime().unwrap();
                assert_eq!(lower, LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0)));

                let upper = interval.upper_bound_runtime().unwrap();
                assert_eq!(upper, UpperBoundRuntime::Closed(UpperBoundClosed::new(2.0)));
            }

            #[test]
            fn get_interval_bound_open() {
                let interval = IntervalOpen::new(1.0, 2.0);

                let lower = interval.lower_bound_runtime().unwrap();
                assert_eq!(lower, LowerBoundRuntime::Open(LowerBoundOpen::new(1.0)));

                let upper = interval.upper_bound_runtime().unwrap();
                assert_eq!(upper, UpperBoundRuntime::Open(UpperBoundOpen::new(2.0)));
            }

            #[test]
            fn get_interval_bound_left_open_upper_closed() {
                let interval = IntervalLowerOpenUpperClosed::new(1.0, 2.0);

                let lower = interval.lower_bound_runtime().unwrap();
                assert_eq!(lower, LowerBoundRuntime::Open(LowerBoundOpen::new(1.0)));

                let upper = interval.upper_bound_runtime().unwrap();
                assert_eq!(upper, UpperBoundRuntime::Closed(UpperBoundClosed::new(2.0)));
            }

            #[test]
            fn get_interval_bound_left_closed_upper_open() {
                let interval = IntervalLowerClosedUpperOpen::new(1.0, 2.0);

                let lower = interval.lower_bound_runtime().unwrap();
                assert_eq!(lower, LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0)));

                let upper = interval.upper_bound_runtime().unwrap();
                assert_eq!(upper, UpperBoundRuntime::Open(UpperBoundOpen::new(2.0)));
            }

            #[test]
            fn get_interval_bound_lower_closed_upper_unbounded() {
                let interval = IntervalLowerClosedUpperUnbounded::new(1.0);

                let lower = interval.lower_bound_runtime().unwrap();
                assert_eq!(lower, LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0)));

                let upper = interval.upper_bound_runtime();
                assert_eq!(upper, None);
            }

            #[test]
            fn get_interval_bound_lower_open_upper_unbounded() {
                let interval = IntervalLowerOpenUpperUnbounded::new(1.0);

                let lower = interval.lower_bound_runtime().unwrap();
                assert_eq!(lower, LowerBoundRuntime::Open(LowerBoundOpen::new(1.0)));

                let upper = interval.upper_bound_runtime();
                assert_eq!(upper, None);
            }

            #[test]
            fn get_interval_bound_lower_unbounded_upper_closed() {
                let interval = IntervalLowerUnboundedUpperClosed::new(2.0);

                let lower = interval.lower_bound_runtime();
                assert_eq!(lower, None);

                let upper = interval.upper_bound_runtime().unwrap();
                assert_eq!(upper, UpperBoundRuntime::Closed(UpperBoundClosed::new(2.0)));
            }

            #[test]
            fn get_interval_bound_lower_unbounded_upper_open() {
                let interval = IntervalLowerUnboundedUpperOpen::new(2.0);

                let lower = interval.lower_bound_runtime();
                assert_eq!(lower, None);

                let upper = interval.upper_bound_runtime().unwrap();
                assert_eq!(upper, UpperBoundRuntime::Open(UpperBoundOpen::new(2.0)));
            }

            #[test]
            fn get_interval_bound_singleton() {
                let interval = IntervalSingleton::new(1.5);

                let lower = interval.lower_bound_runtime().unwrap();
                assert_eq!(lower, LowerBoundRuntime::Closed(LowerBoundClosed::new(1.5)));

                let upper = interval.upper_bound_runtime().unwrap();
                assert_eq!(upper, UpperBoundRuntime::Closed(UpperBoundClosed::new(1.5)));
            }

            #[test]
            fn get_interval_bound_finite_length() {
                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);

                let lower = finite.lower_bound_runtime().unwrap();
                let upper = finite.upper_bound_runtime().unwrap();
                assert_eq!(lower, LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0)));
                assert_eq!(upper, UpperBoundRuntime::Closed(UpperBoundClosed::new(2.0)));

                let lower = zero.lower_bound_runtime().unwrap();
                let upper = zero.upper_bound_runtime().unwrap();
                assert_eq!(lower, LowerBoundRuntime::Closed(LowerBoundClosed::new(1.5)));
                assert_eq!(upper, UpperBoundRuntime::Closed(UpperBoundClosed::new(1.5)));
            }

            #[test]
            fn get_interval_bound_finite_positive_length() {
                use crate::intervals::IntervalFinitePositiveLengthTrait;

                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);

                let (lower, upper) = (
                    c.lower_bound_runtime().unwrap(),
                    c.upper_bound_runtime().unwrap(),
                );
                assert_eq!(lower, LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0)));
                assert_eq!(upper, UpperBoundRuntime::Closed(UpperBoundClosed::new(2.0)));
                assert_eq!(c.clone().into_bounds_pair(), (1., 2.));

                let (lower, upper) = (
                    o.lower_bound_runtime().unwrap(),
                    o.upper_bound_runtime().unwrap(),
                );
                assert_eq!(lower, LowerBoundRuntime::Open(LowerBoundOpen::new(1.0)));
                assert_eq!(upper, UpperBoundRuntime::Open(UpperBoundOpen::new(2.0)));
                assert_eq!(o.clone().into_bounds_pair(), (1., 2.));

                let (lower, upper) = (
                    l.lower_bound_runtime().unwrap(),
                    l.upper_bound_runtime().unwrap(),
                );
                assert_eq!(lower, LowerBoundRuntime::Open(LowerBoundOpen::new(1.0)));
                assert_eq!(upper, UpperBoundRuntime::Closed(UpperBoundClosed::new(2.0)));
                assert_eq!(l.clone().into_bounds_pair(), (1., 2.));

                let (lower, upper) = (
                    r.lower_bound_runtime().unwrap(),
                    r.upper_bound_runtime().unwrap(),
                );
                assert_eq!(lower, LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0)));
                assert_eq!(upper, UpperBoundRuntime::Open(UpperBoundOpen::new(2.0)));
                assert_eq!(r.clone().into_bounds_pair(), (1., 2.));
            }

            #[test]
            fn get_interval_bound_infinite_length() {
                let closed = IntervalLowerClosedUpperUnbounded::new(1.0);
                let open = IntervalLowerOpenUpperUnbounded::new(2.0);
                let right_closed = IntervalLowerUnboundedUpperClosed::new(3.0);
                let right_open = IntervalLowerUnboundedUpperOpen::new(4.0);
                let unbounded = IntervalLowerUnboundedUpperUnbounded::<f64>::new();

                let c = IntervalInfiniteLength::LowerClosedUpperUnbounded(closed);
                let o = IntervalInfiniteLength::LowerOpenUpperUnbounded(open);
                let rc = IntervalInfiniteLength::LowerUnboundedUpperClosed(right_closed);
                let ro = IntervalInfiniteLength::LowerUnboundedUpperOpen(right_open);
                let u = IntervalInfiniteLength::LowerUnboundedUpperUnbounded(unbounded);

                let (lower, upper) = (c.lower_bound_runtime(), c.upper_bound_runtime());
                assert_eq!(
                    lower.unwrap(),
                    LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0))
                );
                assert_eq!(upper, None);

                let (lower, upper) = (o.lower_bound_runtime(), o.upper_bound_runtime());
                assert_eq!(
                    lower.unwrap(),
                    LowerBoundRuntime::Open(LowerBoundOpen::new(2.0))
                );
                assert_eq!(upper, None);

                let (lower, upper) = (rc.lower_bound_runtime(), rc.upper_bound_runtime());
                assert_eq!(lower, None);
                assert_eq!(
                    upper.unwrap(),
                    UpperBoundRuntime::Closed(UpperBoundClosed::new(3.0))
                );

                let (lower, upper) = (ro.lower_bound_runtime(), ro.upper_bound_runtime());
                assert_eq!(lower, None);
                assert_eq!(
                    upper.unwrap(),
                    UpperBoundRuntime::Open(UpperBoundOpen::new(4.0))
                );

                let (lower, upper) = (u.lower_bound_runtime(), u.upper_bound_runtime());
                assert_eq!(lower, None);
                assert_eq!(upper, None);
            }

            #[test]
            fn get_interval_bound_enum_wrappers() {
                let closed = IntervalClosed::new(1.0, 2.0);
                let finite = IntervalFiniteLength::PositiveLength(
                    IntervalFinitePositiveLength::Closed(closed),
                );
                let inf = IntervalInfiniteLength::LowerUnboundedUpperUnbounded(
                    IntervalLowerUnboundedUpperUnbounded::<f64>::new(),
                );
                let interval = Interval::FiniteLength(finite.clone());
                let interval_inf = Interval::InfiniteLength(inf.clone());

                let (lower, upper) = (
                    interval.lower_bound_runtime(),
                    interval.upper_bound_runtime(),
                );
                assert_eq!(
                    lower.unwrap(),
                    LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0))
                );
                assert_eq!(
                    upper.unwrap(),
                    UpperBoundRuntime::Closed(UpperBoundClosed::new(2.0))
                );

                let (lower, upper) = (
                    interval_inf.lower_bound_runtime(),
                    interval_inf.upper_bound_runtime(),
                );
                assert_eq!(lower, None);
                assert_eq!(upper, None);
            }

            #[test]
            fn get_interval_bound_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);

                let (lower, upper) = (s1.lower_bound_runtime(), s1.upper_bound_runtime());
                assert_eq!(
                    lower,
                    Some(LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0)))
                );
                assert_eq!(
                    upper,
                    Some(UpperBoundRuntime::Closed(UpperBoundClosed::new(2.0)))
                );

                let (lower, upper) = (s2.lower_bound_runtime(), s2.upper_bound_runtime());
                assert_eq!(
                    lower,
                    Some(LowerBoundRuntime::Open(LowerBoundOpen::new(2.0)))
                );
                assert_eq!(
                    upper,
                    Some(UpperBoundRuntime::Closed(UpperBoundClosed::new(3.0)))
                );
            }
        }

        mod partial_ord {
            use super::*;

            #[test]
            fn partial_ord_lower_bound_structs() {
                let closed1 = LowerBoundClosed::new(1.0);
                let closed2 = LowerBoundClosed::new(2.0);
                let open1 = LowerBoundOpen::new(1.0);
                let open2 = LowerBoundOpen::new(2.0);

                assert!(closed1 < closed2);
                assert!(open1 < open2);
                assert!(closed1 < open2);
                assert!(open1 < closed2);
                assert!(closed1 < open1); // closed < open if same value
                assert!(open2 > closed1);
                assert!(closed1 <= closed1);
                assert!(open2 >= open2);
                assert!(!(closed1 == open1));
            }

            #[test]
            fn partial_ord_upper_bound_structs() {
                let closed1 = UpperBoundClosed::new(1.0);
                let closed2 = UpperBoundClosed::new(2.0);
                let open1 = UpperBoundOpen::new(1.0);
                let open2 = UpperBoundOpen::new(2.0);

                assert!(closed1 < closed2);
                assert!(open1 < open2);
                assert!(closed1 < open2);
                assert!(open1 < closed2);
                assert!(open1 < closed1); // open < closed if same value
                assert!(closed2 > open1);
                assert!(closed1 <= closed1);
                assert!(open2 >= open2);
                assert!(!(closed1 == open1));
            }

            #[test]
            fn partial_ord_interval_lower_bound_enum() {
                let closed = LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0));
                let open = LowerBoundRuntime::Open(LowerBoundOpen::new(2.0));

                assert_eq!(closed.partial_cmp(&open), Some(std::cmp::Ordering::Less));
                assert_eq!(open.partial_cmp(&closed), Some(std::cmp::Ordering::Greater));
                assert_eq!(closed.partial_cmp(&closed), Some(std::cmp::Ordering::Equal));
                assert_eq!(open.partial_cmp(&open), Some(std::cmp::Ordering::Equal));
            }

            #[test]
            fn partial_ord_interval_upper_bound_enum() {
                let closed = UpperBoundRuntime::Closed(UpperBoundClosed::new(1.0));
                let open = UpperBoundRuntime::Open(UpperBoundOpen::new(2.0));

                assert_eq!(closed.partial_cmp(&open), Some(std::cmp::Ordering::Less));
                assert_eq!(open.partial_cmp(&closed), Some(std::cmp::Ordering::Greater));
                assert_eq!(closed.partial_cmp(&closed), Some(std::cmp::Ordering::Equal));
                assert_eq!(open.partial_cmp(&open), Some(std::cmp::Ordering::Equal));
            }

            #[test]
            fn partial_ord_interval_lower_bound_enum_same_value() {
                let closed = LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0));
                let open = LowerBoundRuntime::Open(LowerBoundOpen::new(1.0));
                assert_eq!(closed.partial_cmp(&open), Some(std::cmp::Ordering::Less));
                assert_eq!(open.partial_cmp(&closed), Some(std::cmp::Ordering::Greater));
            }

            #[test]
            fn partial_ord_interval_upper_bound_enum_same_value() {
                let closed = UpperBoundRuntime::Closed(UpperBoundClosed::new(1.0));
                let open = UpperBoundRuntime::Open(UpperBoundOpen::new(1.0));
                assert_eq!(open.partial_cmp(&closed), Some(std::cmp::Ordering::Less));
                assert_eq!(closed.partial_cmp(&open), Some(std::cmp::Ordering::Greater));
            }
        }

        mod bounds_comparisons {
            use super::*;
            use try_create::TryNew;

            type Real = f64;

            /// Test PartialOrd implementation for LowerBoundRuntime
            #[test]
            fn test_interval_lower_bound_comparison() {
                // Create test bounds
                let lower_bound_closed_5 =
                    LowerBoundRuntime::Closed(LowerBoundClosed::new(Real::try_new(5.0).unwrap()));
                let lower_bound_open_5 =
                    LowerBoundRuntime::Open(LowerBoundOpen::new(Real::try_new(5.0).unwrap()));
                let lower_bound_closed_3 =
                    LowerBoundRuntime::Closed(LowerBoundClosed::new(Real::try_new(3.0).unwrap()));
                let lower_bound_open_3 =
                    LowerBoundRuntime::Open(LowerBoundOpen::new(Real::try_new(3.0).unwrap()));
                let lower_bound_closed_7 =
                    LowerBoundRuntime::Closed(LowerBoundClosed::new(Real::try_new(7.0).unwrap()));

                // Test basic ordering by value
                assert_eq!(
                    lower_bound_closed_3.partial_cmp(&lower_bound_closed_5),
                    Some(Ordering::Less)
                );
                assert_eq!(
                    lower_bound_closed_5.partial_cmp(&lower_bound_closed_3),
                    Some(Ordering::Greater)
                );
                assert_eq!(
                    lower_bound_closed_5.partial_cmp(&lower_bound_closed_5),
                    Some(Ordering::Equal)
                );

                // Test ordering between closed and open bounds with same value
                // For lower bounds: closed < open at same value (closed is more restrictive for lower)
                assert_eq!(
                    lower_bound_closed_5.partial_cmp(&lower_bound_open_5),
                    Some(Ordering::Less)
                );
                assert_eq!(
                    lower_bound_open_5.partial_cmp(&lower_bound_closed_5),
                    Some(Ordering::Greater)
                );

                // Test mixed value and boundary type comparisons
                assert_eq!(
                    lower_bound_closed_3.partial_cmp(&lower_bound_open_5),
                    Some(Ordering::Less)
                );
                assert_eq!(
                    lower_bound_open_3.partial_cmp(&lower_bound_closed_5),
                    Some(Ordering::Less)
                );
                assert_eq!(
                    lower_bound_closed_5.partial_cmp(&lower_bound_open_3),
                    Some(Ordering::Greater)
                );

                // Test transitivity
                assert_eq!(
                    lower_bound_closed_3.partial_cmp(&lower_bound_closed_7),
                    Some(Ordering::Less)
                );
                assert_eq!(
                    lower_bound_open_3.partial_cmp(&lower_bound_closed_7),
                    Some(Ordering::Less)
                );
            }

            /// Test PartialOrd implementation for UpperBoundRuntime
            #[test]
            fn test_interval_upper_bound_comparison() {
                // Create test bounds
                let upper_bound_closed_5 =
                    UpperBoundRuntime::Closed(UpperBoundClosed::new(Real::try_new(5.0).unwrap()));
                let upper_bound_open_5 =
                    UpperBoundRuntime::Open(UpperBoundOpen::new(Real::try_new(5.0).unwrap()));
                let upper_bound_closed_3 =
                    UpperBoundRuntime::Closed(UpperBoundClosed::new(Real::try_new(3.0).unwrap()));
                let upper_bound_open_3 =
                    UpperBoundRuntime::Open(UpperBoundOpen::new(Real::try_new(3.0).unwrap()));
                let upper_bound_closed_7 =
                    UpperBoundRuntime::Closed(UpperBoundClosed::new(Real::try_new(7.0).unwrap()));

                // Test basic ordering by value
                assert_eq!(
                    upper_bound_closed_3.partial_cmp(&upper_bound_closed_5),
                    Some(Ordering::Less)
                );
                assert_eq!(
                    upper_bound_closed_5.partial_cmp(&upper_bound_closed_3),
                    Some(Ordering::Greater)
                );
                assert_eq!(
                    upper_bound_closed_5.partial_cmp(&upper_bound_closed_5),
                    Some(Ordering::Equal)
                );

                // Test ordering between closed and open bounds with same value
                // For upper bounds: open < closed at same value (open is more restrictive for upper)
                assert_eq!(
                    upper_bound_open_5.partial_cmp(&upper_bound_closed_5),
                    Some(Ordering::Less)
                );
                assert_eq!(
                    upper_bound_closed_5.partial_cmp(&upper_bound_open_5),
                    Some(Ordering::Greater)
                );

                // Test mixed value and boundary type comparisons
                assert_eq!(
                    upper_bound_closed_3.partial_cmp(&upper_bound_open_5),
                    Some(Ordering::Less)
                );
                assert_eq!(
                    upper_bound_open_3.partial_cmp(&upper_bound_closed_5),
                    Some(Ordering::Less)
                );
                assert_eq!(
                    upper_bound_closed_5.partial_cmp(&upper_bound_open_3),
                    Some(Ordering::Greater)
                );

                // Test transitivity
                assert_eq!(
                    upper_bound_closed_3.partial_cmp(&upper_bound_closed_7),
                    Some(Ordering::Less)
                );
                assert_eq!(
                    upper_bound_open_3.partial_cmp(&upper_bound_closed_7),
                    Some(Ordering::Less)
                );
            }

            /// Test boundary type semantics for lower bounds
            #[test]
            fn test_lower_bound_semantics() {
                let closed_5 =
                    LowerBoundRuntime::Closed(LowerBoundClosed::new(Real::try_new(5.0).unwrap()));
                let open_5 =
                    LowerBoundRuntime::Open(LowerBoundOpen::new(Real::try_new(5.0).unwrap()));

                // For lower bounds, closed is more restrictive (smaller) than open at same value
                // This is because [5,b) contains 5, while (5,b) does not
                more_asserts::assert_lt!(closed_5, open_5);
                more_asserts::assert_ge!(open_5, closed_5);
                assert_ne!(closed_5, open_5);
            }

            /// Test boundary type semantics for upper bounds
            #[test]
            fn test_upper_bound_semantics() {
                let closed_5 =
                    UpperBoundRuntime::Closed(UpperBoundClosed::new(Real::try_new(5.0).unwrap()));
                let open_5 =
                    UpperBoundRuntime::Open(UpperBoundOpen::new(Real::try_new(5.0).unwrap()));

                // For upper bounds, open is more restrictive (smaller) than closed at same value
                // This is because [a,5) excludes 5, while [a,5] includes 5
                more_asserts::assert_lt!(open_5, closed_5);
                more_asserts::assert_ge!(closed_5, open_5);
                assert_ne!(open_5, closed_5);
            }

            /// Test consistency with mathematical interval ordering
            #[test]
            fn test_mathematical_consistency() {
                // Create bounds that would represent valid intervals
                let lower_closed_1 =
                    LowerBoundRuntime::Closed(LowerBoundClosed::new(Real::try_new(1.0).unwrap()));
                let lower_open_1 =
                    LowerBoundRuntime::Open(LowerBoundOpen::new(Real::try_new(1.0).unwrap()));
                let upper_open_2 =
                    UpperBoundRuntime::Open(UpperBoundOpen::new(Real::try_new(2.0).unwrap()));
                let upper_closed_2 =
                    UpperBoundRuntime::Closed(UpperBoundClosed::new(Real::try_new(2.0).unwrap()));

                // Mathematical consistency checks:
                // [1,2) should have closed lower bound and open upper bound
                // (1,2] should have open lower bound and closed upper bound
                // [1,2] should have closed bounds
                // (1,2) should have open bounds

                // Verify ordering is consistent with interval containment
                // [1,x] contains more points than (1,x] at the same x
                assert!(lower_closed_1 < lower_open_1);

                // [x,2) contains fewer points than [x,2] at the same x
                assert!(upper_open_2 < upper_closed_2);
            }

            /// Test reflexivity property
            #[test]
            fn test_reflexivity() {
                let lower_closed =
                    LowerBoundRuntime::Closed(LowerBoundClosed::new(Real::try_new(5.0).unwrap()));
                let lower_open =
                    LowerBoundRuntime::Open(LowerBoundOpen::new(Real::try_new(5.0).unwrap()));
                let upper_closed =
                    UpperBoundRuntime::Closed(UpperBoundClosed::new(Real::try_new(5.0).unwrap()));
                let upper_open =
                    UpperBoundRuntime::Open(UpperBoundOpen::new(Real::try_new(5.0).unwrap()));

                // Reflexivity: x.partial_cmp(&x) == Some(Ordering::Equal)
                assert_eq!(
                    lower_closed.partial_cmp(&lower_closed),
                    Some(Ordering::Equal)
                );
                assert_eq!(lower_open.partial_cmp(&lower_open), Some(Ordering::Equal));
                assert_eq!(
                    upper_closed.partial_cmp(&upper_closed),
                    Some(Ordering::Equal)
                );
                assert_eq!(upper_open.partial_cmp(&upper_open), Some(Ordering::Equal));
            }

            /// Test antisymmetry property
            #[test]
            fn test_antisymmetry() {
                let lower_closed_3 =
                    LowerBoundRuntime::Closed(LowerBoundClosed::new(Real::try_new(3.0).unwrap()));
                let lower_closed_5 =
                    LowerBoundRuntime::Closed(LowerBoundClosed::new(Real::try_new(5.0).unwrap()));

                // Antisymmetry: if x <= y and y <= x, then x == y
                if lower_closed_3.partial_cmp(&lower_closed_5) == Some(Ordering::Less) {
                    assert_eq!(
                        lower_closed_5.partial_cmp(&lower_closed_3),
                        Some(Ordering::Greater)
                    );
                }
            }

            /// Test transitivity property
            #[test]
            fn test_transitivity() {
                let lower_closed_1 =
                    LowerBoundRuntime::Closed(LowerBoundClosed::new(Real::try_new(1.0).unwrap()));
                let lower_open_3 =
                    LowerBoundRuntime::Open(LowerBoundOpen::new(Real::try_new(3.0).unwrap()));
                let lower_closed_5 =
                    LowerBoundRuntime::Closed(LowerBoundClosed::new(Real::try_new(5.0).unwrap()));

                // Transitivity: if x <= y and y <= z, then x <= z
                let ord_1_3 = lower_closed_1.partial_cmp(&lower_open_3);
                let ord_3_5 = lower_open_3.partial_cmp(&lower_closed_5);
                let ord_1_5 = lower_closed_1.partial_cmp(&lower_closed_5);

                if ord_1_3 == Some(Ordering::Less) && ord_3_5 == Some(Ordering::Less) {
                    assert_eq!(ord_1_5, Some(Ordering::Less));
                }
            }

            /// Test edge cases with same values but different boundary types
            #[test]
            fn test_edge_cases_same_values() {
                let value = Real::try_new(42.0).unwrap();

                let lower_closed = LowerBoundRuntime::Closed(LowerBoundClosed::new(value));
                let lower_open = LowerBoundRuntime::Open(LowerBoundOpen::new(value));
                let upper_closed = UpperBoundRuntime::Closed(UpperBoundClosed::new(value));
                let upper_open = UpperBoundRuntime::Open(UpperBoundOpen::new(value));

                // Test that boundary type matters even with same value
                assert_ne!(lower_closed.partial_cmp(&lower_open), Some(Ordering::Equal));
                assert_ne!(upper_closed.partial_cmp(&upper_open), Some(Ordering::Equal));

                // Verify specific ordering relationships
                assert_eq!(lower_closed.partial_cmp(&lower_open), Some(Ordering::Less));
                assert_eq!(upper_open.partial_cmp(&upper_closed), Some(Ordering::Less));
            }

            /// Test with extreme values
            #[test]
            fn test_extreme_values() {
                let min_val = Real::try_new(f64::MIN).unwrap();
                let max_val = Real::try_new(f64::MAX).unwrap();

                let lower_min_closed = LowerBoundRuntime::Closed(LowerBoundClosed::new(min_val));
                let lower_max_open = LowerBoundRuntime::Open(LowerBoundOpen::new(max_val));
                let upper_min_open = UpperBoundRuntime::Open(UpperBoundOpen::new(min_val));
                let upper_max_closed = UpperBoundRuntime::Closed(UpperBoundClosed::new(max_val));

                // Test that extreme values still follow ordering rules
                assert_eq!(
                    lower_min_closed.partial_cmp(&lower_max_open),
                    Some(Ordering::Less)
                );
                assert_eq!(
                    upper_min_open.partial_cmp(&upper_max_closed),
                    Some(Ordering::Less)
                );
            }

            /// Test helper functions min_lower_bound and max_lower_bound
            #[test]
            fn test_min_max_lower_bound() {
                let lower_closed_3 =
                    LowerBoundRuntime::Closed(LowerBoundClosed::new(Real::try_new(3.0).unwrap()));
                let lower_open_5 =
                    LowerBoundRuntime::Open(LowerBoundOpen::new(Real::try_new(5.0).unwrap()));

                // Test min_lower_bound
                let min_result = min_lower_bound(lower_closed_3.clone(), lower_open_5.clone());
                assert_eq!(min_result, lower_closed_3);

                // Test max_lower_bound
                let max_result = max_lower_bound(lower_closed_3.clone(), lower_open_5.clone());
                assert_eq!(max_result, lower_open_5);
            }

            /// Test helper functions min_upper_bound and max_upper_bound
            #[test]
            fn test_min_max_upper_bound() {
                let upper_open_3 =
                    UpperBoundRuntime::Open(UpperBoundOpen::new(Real::try_new(3.0).unwrap()));
                let upper_closed_5 =
                    UpperBoundRuntime::Closed(UpperBoundClosed::new(Real::try_new(5.0).unwrap()));

                // Test min_upper_bound
                let min_result = min_upper_bound(upper_open_3.clone(), upper_closed_5.clone());
                assert_eq!(min_result, upper_open_3);

                // Test max_upper_bound
                let max_result = max_upper_bound(upper_open_3.clone(), upper_closed_5.clone());
                assert_eq!(max_result, upper_closed_5);
            }

            /// Test new_open and new_closed constructors for LowerBoundRuntime
            #[test]
            fn test_lower_bound_runtime_constructors() {
                let value = Real::try_new(5.0).unwrap();

                // Test new_open
                let open_bound = LowerBoundRuntime::new_open(value);
                assert!(open_bound.is_open());
                assert!(open_bound.is_open_variant());
                assert!(!open_bound.is_closed());
                assert!(!open_bound.is_closed_variant());
                assert_eq!(open_bound.as_ref(), &value);

                // Test new_closed
                let closed_bound = LowerBoundRuntime::new_closed(value);
                assert!(closed_bound.is_closed());
                assert!(closed_bound.is_closed_variant());
                assert!(!closed_bound.is_open());
                assert!(!closed_bound.is_open_variant());
                assert_eq!(closed_bound.as_ref(), &value);

                // Test constraint semantics for open bound
                assert!(!open_bound.value_within_bound(&value)); // 5 > 5 → false
                assert!(open_bound.value_within_bound(&Real::try_new(5.1).unwrap())); // 5.1 > 5 → true
                assert!(!open_bound.value_within_bound(&Real::try_new(4.9).unwrap())); // 4.9 > 5 → false

                // Test constraint semantics for closed bound
                assert!(closed_bound.value_within_bound(&value)); // 5 ≥ 5 → true
                assert!(closed_bound.value_within_bound(&Real::try_new(5.1).unwrap())); // 5.1 ≥ 5 → true
                assert!(!closed_bound.value_within_bound(&Real::try_new(4.9).unwrap())); // 4.9 ≥ 5 → false
            }

            /// Test new_open and new_closed constructors for UpperBoundRuntime
            #[test]
            fn test_upper_bound_runtime_constructors() {
                let value = Real::try_new(10.0).unwrap();

                // Test new_open
                let open_bound = UpperBoundRuntime::new_open(value);
                assert!(open_bound.is_open());
                assert!(open_bound.is_open_variant());
                assert!(!open_bound.is_closed());
                assert!(!open_bound.is_closed_variant());
                assert_eq!(open_bound.as_ref(), &value);

                // Test new_closed
                let closed_bound = UpperBoundRuntime::new_closed(value);
                assert!(closed_bound.is_closed());
                assert!(closed_bound.is_closed_variant());
                assert!(!closed_bound.is_open());
                assert!(!closed_bound.is_open_variant());
                assert_eq!(closed_bound.as_ref(), &value);

                // Test constraint semantics for open bound
                assert!(!open_bound.value_within_bound(&value)); // 10 < 10 → false
                assert!(open_bound.value_within_bound(&Real::try_new(9.9).unwrap())); // 9.9 < 10 → true
                assert!(!open_bound.value_within_bound(&Real::try_new(10.1).unwrap())); // 10.1 < 10 → false

                // Test constraint semantics for closed bound
                assert!(closed_bound.value_within_bound(&value)); // 10 ≤ 10 → true
                assert!(closed_bound.value_within_bound(&Real::try_new(9.9).unwrap())); // 9.9 ≤ 10 → true
                assert!(!closed_bound.value_within_bound(&Real::try_new(10.1).unwrap())); // 10.1 ≤ 10 → false
            }

            /// Test equivalence between convenience constructors and manual construction
            #[test]
            fn test_constructor_equivalence() {
                let value = Real::try_new(7.5).unwrap();

                // Lower bounds
                let lower_open_convenience = LowerBoundRuntime::new_open(value);
                let lower_open_manual = LowerBoundRuntime::Open(LowerBoundOpen::new(value));
                assert_eq!(lower_open_convenience, lower_open_manual);

                let lower_closed_convenience = LowerBoundRuntime::new_closed(value);
                let lower_closed_manual = LowerBoundRuntime::Closed(LowerBoundClosed::new(value));
                assert_eq!(lower_closed_convenience, lower_closed_manual);

                // Upper bounds
                let upper_open_convenience = UpperBoundRuntime::new_open(value);
                let upper_open_manual = UpperBoundRuntime::Open(UpperBoundOpen::new(value));
                assert_eq!(upper_open_convenience, upper_open_manual);

                let upper_closed_convenience = UpperBoundRuntime::new_closed(value);
                let upper_closed_manual = UpperBoundRuntime::Closed(UpperBoundClosed::new(value));
                assert_eq!(upper_closed_convenience, upper_closed_manual);
            }

            /// Test is_open_variant and is_closed_variant consistency
            #[test]
            fn test_variant_check_consistency() {
                let value = Real::try_new(3.5).unwrap();

                // Lower bounds
                let lower_open = LowerBoundRuntime::new_open(value);
                let lower_closed = LowerBoundRuntime::new_closed(value);

                // Verify is_open_variant matches is_open() from trait
                assert_eq!(lower_open.is_open_variant(), lower_open.is_open());
                assert_eq!(lower_closed.is_open_variant(), lower_closed.is_open());

                // Verify is_closed_variant matches is_closed() from trait
                assert_eq!(lower_open.is_closed_variant(), lower_open.is_closed());
                assert_eq!(lower_closed.is_closed_variant(), lower_closed.is_closed());

                // Verify complementary relationship
                assert_eq!(
                    lower_open.is_open_variant(),
                    !lower_open.is_closed_variant()
                );
                assert_eq!(
                    lower_closed.is_open_variant(),
                    !lower_closed.is_closed_variant()
                );

                // Upper bounds
                let upper_open = UpperBoundRuntime::new_open(value);
                let upper_closed = UpperBoundRuntime::new_closed(value);

                assert_eq!(upper_open.is_open_variant(), upper_open.is_open());
                assert_eq!(upper_closed.is_open_variant(), upper_closed.is_open());
                assert_eq!(upper_open.is_closed_variant(), upper_open.is_closed());
                assert_eq!(upper_closed.is_closed_variant(), upper_closed.is_closed());
                assert_eq!(
                    upper_open.is_open_variant(),
                    !upper_open.is_closed_variant()
                );
                assert_eq!(
                    upper_closed.is_open_variant(),
                    !upper_closed.is_closed_variant()
                );
            }

            /// Test variant checks for filtering and categorization
            #[test]
            fn test_variant_checks_for_filtering() {
                let bounds = [
                    LowerBoundRuntime::new_open(Real::try_new(1.0).unwrap()),
                    LowerBoundRuntime::new_closed(Real::try_new(2.0).unwrap()),
                    LowerBoundRuntime::new_open(Real::try_new(3.0).unwrap()),
                    LowerBoundRuntime::new_closed(Real::try_new(4.0).unwrap()),
                    LowerBoundRuntime::new_open(Real::try_new(5.0).unwrap()),
                ];

                // Filter by variant type
                let open_bounds: Vec<_> = bounds.iter().filter(|b| b.is_open_variant()).collect();
                let closed_bounds: Vec<_> =
                    bounds.iter().filter(|b| b.is_closed_variant()).collect();

                assert_eq!(open_bounds.len(), 3);
                assert_eq!(closed_bounds.len(), 2);

                // Verify all categorized correctly
                for bound in &open_bounds {
                    assert!(bound.is_open_variant());
                    assert!(!bound.is_closed_variant());
                }
                for bound in &closed_bounds {
                    assert!(bound.is_closed_variant());
                    assert!(!bound.is_open_variant());
                }
            }

            /// Test variant checks are non-consuming
            #[test]
            fn test_variant_checks_non_consuming() {
                let bound = LowerBoundRuntime::new_open(Real::try_new(42.0).unwrap());

                // Can check multiple times without consuming
                assert!(bound.is_open_variant());
                assert!(bound.is_open_variant());
                assert!(!bound.is_closed_variant());
                assert!(!bound.is_closed_variant());

                // Can still use the bound after checks
                assert!(bound.value_within_bound(&Real::try_new(50.0).unwrap()));
                assert_eq!(bound.as_ref(), &Real::try_new(42.0).unwrap());
            }

            /// Test constructors with different scalar types
            #[test]
            fn test_constructors_with_different_scalars() {
                // Test with f64
                let f64_open = LowerBoundRuntime::new_open(5.0_f64);
                let f64_closed = LowerBoundRuntime::new_closed(5.0_f64);
                assert!(f64_open.is_open_variant());
                assert!(f64_closed.is_closed_variant());

                // Test with validated scalar
                let validated_value = Real::try_new(10.0).unwrap();
                let validated_open = UpperBoundRuntime::new_open(validated_value);
                let validated_closed = UpperBoundRuntime::new_closed(validated_value);
                assert!(validated_open.is_open_variant());
                assert!(validated_closed.is_closed_variant());
            }

            /// Test constructors in dynamic interval creation scenarios
            #[test]
            fn test_constructors_dynamic_interval_creation() {
                let create_bound_from_config =
                    |value: f64, inclusive: bool| -> LowerBoundRuntime<Real> {
                        let real_value = Real::try_new(value).unwrap();
                        if inclusive {
                            LowerBoundRuntime::new_closed(real_value)
                        } else {
                            LowerBoundRuntime::new_open(real_value)
                        }
                    };

                // Test inclusive bound
                let inclusive_bound = create_bound_from_config(0.0, true);
                assert!(inclusive_bound.is_closed_variant());
                assert!(inclusive_bound.value_within_bound(&Real::try_new(0.0).unwrap()));

                // Test exclusive bound
                let exclusive_bound = create_bound_from_config(0.0, false);
                assert!(exclusive_bound.is_open_variant());
                assert!(!exclusive_bound.value_within_bound(&Real::try_new(0.0).unwrap()));
            }

            /// Test that constructors maintain proper ordering relationships
            #[test]
            fn test_constructor_ordering() {
                let value = Real::try_new(5.0).unwrap();

                // For lower bounds: closed < open at same value
                let lower_closed = LowerBoundRuntime::new_closed(value);
                let lower_open = LowerBoundRuntime::new_open(value);
                assert!(lower_closed < lower_open);

                // For upper bounds: open < closed at same value
                let upper_open = UpperBoundRuntime::new_open(value);
                let upper_closed = UpperBoundRuntime::new_closed(value);
                assert!(upper_open < upper_closed);
            }
        }

        /// Tests for new helper functions for hull computation with optional bounds
        mod maybe_unbounded_helpers {
            use super::*;
            use num_valid::RealNative64StrictFiniteInDebug;
            use try_create::TryNew;

            type Real = RealNative64StrictFiniteInDebug;

            /// Test min_lower_bound_maybe_unbounded with both bounds present (closed wins)
            #[test]
            fn test_min_lower_bound_both_present_closed_wins() {
                let value = Real::try_new(5.0).unwrap();
                let closed = LowerBoundRuntime::new_closed(value);
                let open = LowerBoundRuntime::new_open(value);

                let result = min_lower_bound_maybe_unbounded(Some(closed), Some(open));

                assert!(result.is_some());
                assert!(
                    result.unwrap().is_closed_variant(),
                    "Closed should win over open at same value"
                );
            }

            /// Test min_lower_bound_maybe_unbounded with different values
            #[test]
            fn test_min_lower_bound_different_values() {
                let value1 = Real::try_new(3.0).unwrap();
                let value2 = Real::try_new(7.0).unwrap();
                let bound1 = LowerBoundRuntime::new_closed(value1);
                let bound2 = LowerBoundRuntime::new_open(value2);

                let result = min_lower_bound_maybe_unbounded(Some(bound1.clone()), Some(bound2));

                assert!(result.is_some());
                let result_bound = result.unwrap();
                assert_eq!(result_bound.as_ref(), &3.0, "Should select minimum value");
                assert!(result_bound.is_closed_variant());
            }

            /// Test min_lower_bound_maybe_unbounded with first None (unbounded wins)
            #[test]
            fn test_min_lower_bound_first_none() {
                let value = Real::try_new(5.0).unwrap();
                let bound = LowerBoundRuntime::new_closed(value);

                let result = min_lower_bound_maybe_unbounded(None, Some(bound));

                assert!(result.is_none(), "None (unbounded) should win");
            }

            /// Test min_lower_bound_maybe_unbounded with second None (unbounded wins)
            #[test]
            fn test_min_lower_bound_second_none() {
                let value = Real::try_new(5.0).unwrap();
                let bound = LowerBoundRuntime::new_closed(value);

                let result = min_lower_bound_maybe_unbounded(Some(bound), None);

                assert!(result.is_none(), "None (unbounded) should win");
            }

            /// Test min_lower_bound_maybe_unbounded with both None
            #[test]
            fn test_min_lower_bound_both_none() {
                let result = min_lower_bound_maybe_unbounded::<Real>(None, None);

                assert!(result.is_none());
            }

            /// Test max_upper_bound_maybe_unbounded with both bounds present (closed wins)
            #[test]
            fn test_max_upper_bound_both_present_closed_wins() {
                let value = Real::try_new(5.0).unwrap();
                let closed = UpperBoundRuntime::new_closed(value);
                let open = UpperBoundRuntime::new_open(value);

                let result =
                    max_upper_bound_maybe_unbounded(Some(closed.clone()), Some(open.clone()));

                assert!(result.is_some());
                assert!(
                    result.unwrap().is_closed_variant(),
                    "Closed should win over open at same value"
                );
            }

            /// Test max_upper_bound_maybe_unbounded with different values
            #[test]
            fn test_max_upper_bound_different_values() {
                let value1 = Real::try_new(3.0).unwrap();
                let value2 = Real::try_new(7.0).unwrap();
                let bound1 = UpperBoundRuntime::new_open(value1);
                let bound2 = UpperBoundRuntime::new_closed(value2);

                let result = max_upper_bound_maybe_unbounded(Some(bound1), Some(bound2.clone()));

                assert!(result.is_some());
                let result_bound = result.unwrap();
                assert_eq!(result_bound.as_ref(), &7.0, "Should select maximum value");
                assert!(result_bound.is_closed_variant());
            }

            /// Test max_upper_bound_maybe_unbounded with first None (unbounded wins)
            #[test]
            fn test_max_upper_bound_first_none() {
                let value = Real::try_new(5.0).unwrap();
                let bound = UpperBoundRuntime::new_closed(value);

                let result = max_upper_bound_maybe_unbounded(None, Some(bound));

                assert!(result.is_none(), "None (unbounded) should win");
            }

            /// Test max_upper_bound_maybe_unbounded with second None (unbounded wins)
            #[test]
            fn test_max_upper_bound_second_none() {
                let value = Real::try_new(5.0).unwrap();
                let bound = UpperBoundRuntime::new_closed(value);

                let result = max_upper_bound_maybe_unbounded(Some(bound), None);

                assert!(result.is_none(), "None (unbounded) should win");
            }

            /// Test max_upper_bound_maybe_unbounded with both None
            #[test]
            fn test_max_upper_bound_both_none() {
                let result = max_upper_bound_maybe_unbounded::<Real>(None, None);

                assert!(result.is_none());
            }

            /// Test symmetry: swapping arguments doesn't change result
            #[test]
            fn test_min_lower_bound_symmetry() {
                let value1 = Real::try_new(3.0).unwrap();
                let value2 = Real::try_new(7.0).unwrap();
                let bound1 = LowerBoundRuntime::new_closed(value1);
                let bound2 = LowerBoundRuntime::new_open(value2);

                let result1 =
                    min_lower_bound_maybe_unbounded(Some(bound1.clone()), Some(bound2.clone()));
                let result2 = min_lower_bound_maybe_unbounded(Some(bound2), Some(bound1));

                assert_eq!(result1, result2, "Operation should be commutative");
            }

            /// Test symmetry for upper bounds
            #[test]
            fn test_max_upper_bound_symmetry() {
                let value1 = Real::try_new(3.0).unwrap();
                let value2 = Real::try_new(7.0).unwrap();
                let bound1 = UpperBoundRuntime::new_closed(value1);
                let bound2 = UpperBoundRuntime::new_open(value2);

                let result1 =
                    max_upper_bound_maybe_unbounded(Some(bound1.clone()), Some(bound2.clone()));
                let result2 = max_upper_bound_maybe_unbounded(Some(bound2), Some(bound1));

                assert_eq!(result1, result2, "Operation should be commutative");
            }

            /// Test interaction with min_lower_bound (existing function)
            #[test]
            fn test_consistency_with_existing_min_lower_bound() {
                let value1 = Real::try_new(3.0).unwrap();
                let value2 = Real::try_new(7.0).unwrap();
                let bound1 = LowerBoundRuntime::new_closed(value1);
                let bound2 = LowerBoundRuntime::new_open(value2);

                // Using the new maybe_unbounded function with Some
                let result_maybe =
                    min_lower_bound_maybe_unbounded(Some(bound1.clone()), Some(bound2.clone()))
                        .unwrap();

                // Using the existing function
                let result_direct = min_lower_bound(bound1, bound2);

                assert_eq!(
                    result_maybe, result_direct,
                    "Should be consistent with existing function"
                );
            }

            /// Test interaction with max_upper_bound (existing function)
            #[test]
            fn test_consistency_with_existing_max_upper_bound() {
                let value1 = Real::try_new(3.0).unwrap();
                let value2 = Real::try_new(7.0).unwrap();
                let bound1 = UpperBoundRuntime::new_closed(value1);
                let bound2 = UpperBoundRuntime::new_open(value2);

                // Using the new maybe_unbounded function with Some
                let result_maybe =
                    max_upper_bound_maybe_unbounded(Some(bound1.clone()), Some(bound2.clone()))
                        .unwrap();

                // Using the existing function
                let result_direct = max_upper_bound(bound1, bound2);

                assert_eq!(
                    result_maybe, result_direct,
                    "Should be consistent with existing function"
                );
            }

            /// Test with multiple calls (associativity)
            #[test]
            fn test_min_lower_bound_associativity() {
                let v1 = Real::try_new(1.0).unwrap();
                let v2 = Real::try_new(2.0).unwrap();
                let v3 = Real::try_new(3.0).unwrap();
                let b1 = LowerBoundRuntime::new_closed(v1);
                let b2 = LowerBoundRuntime::new_open(v2);
                let b3 = LowerBoundRuntime::new_closed(v3);

                // (b1 min b2) min b3
                let temp = min_lower_bound_maybe_unbounded(Some(b1.clone()), Some(b2.clone()));
                let result1 = min_lower_bound_maybe_unbounded(temp, Some(b3.clone()));

                // b1 min (b2 min b3)
                let temp = min_lower_bound_maybe_unbounded(Some(b2), Some(b3));
                let result2 = min_lower_bound_maybe_unbounded(Some(b1), temp);

                assert_eq!(result1, result2, "Operation should be associative");
            }
        }
    }

    // Tests for IntervalBoundRuntime::flip_bound_type() and flip_bound_side()
    mod interval_bound_runtime_transformations {
        use super::*;

        /// Test flip_bound_type() on LowerBoundRuntime variants
        #[test]
        fn flip_bound_type_lower_open_to_closed() {
            let lower_open = LowerBoundRuntime::new_open(5.0);
            let lower_closed = lower_open.flip_bound_type();

            assert!(lower_closed.is_closed_variant());
            assert!(!lower_closed.is_open_variant());
            assert_eq!(*lower_closed.as_ref(), 5.0);
        }

        #[test]
        fn flip_bound_type_lower_closed_to_open() {
            let lower_closed = LowerBoundRuntime::new_closed(10.0);
            let lower_open = lower_closed.flip_bound_type();

            assert!(lower_open.is_open_variant());
            assert!(!lower_open.is_closed_variant());
            assert_eq!(*lower_open.as_ref(), 10.0);
        }

        /// Test flip_bound_type() on UpperBoundRuntime variants
        #[test]
        fn flip_bound_type_upper_open_to_closed() {
            let upper_open = UpperBoundRuntime::new_open(20.0);
            let upper_closed = upper_open.flip_bound_type();

            assert!(upper_closed.is_closed_variant());
            assert!(!upper_closed.is_open_variant());
            assert_eq!(*upper_closed.as_ref(), 20.0);
        }

        #[test]
        fn flip_bound_type_upper_closed_to_open() {
            let upper_closed = UpperBoundRuntime::new_closed(15.0);
            let upper_open = upper_closed.flip_bound_type();

            assert!(upper_open.is_open_variant());
            assert!(!upper_open.is_closed_variant());
            assert_eq!(*upper_open.as_ref(), 15.0);
        }

        /// Test that flipping bound type twice returns to original
        #[test]
        fn flip_bound_type_idempotent() {
            // Lower bounds
            let lower_open_original = LowerBoundRuntime::new_open(3.0);
            let lower_open_roundtrip = lower_open_original
                .clone()
                .flip_bound_type()
                .flip_bound_type();
            assert_eq!(lower_open_original, lower_open_roundtrip);

            let lower_closed_original = LowerBoundRuntime::new_closed(7.0);
            let lower_closed_roundtrip = lower_closed_original
                .clone()
                .flip_bound_type()
                .flip_bound_type();
            assert_eq!(lower_closed_original, lower_closed_roundtrip);

            // Upper bounds
            let upper_open_original = UpperBoundRuntime::new_open(12.0);
            let upper_open_roundtrip = upper_open_original
                .clone()
                .flip_bound_type()
                .flip_bound_type();
            assert_eq!(upper_open_original, upper_open_roundtrip);

            let upper_closed_original = UpperBoundRuntime::new_closed(18.0);
            let upper_closed_roundtrip = upper_closed_original
                .clone()
                .flip_bound_type()
                .flip_bound_type();
            assert_eq!(upper_closed_original, upper_closed_roundtrip);
        }

        /// Test flip_bound_side() from lower to upper bounds
        #[test]
        fn flip_bound_side_lower_open_to_upper_open() {
            let lower_open = LowerBoundRuntime::new_open(5.0);
            let upper_open: UpperBoundRuntime<f64> = lower_open.flip_bound_side();

            assert!(upper_open.is_open_variant());
            assert_eq!(*upper_open.as_ref(), 5.0);
        }

        #[test]
        fn flip_bound_side_lower_closed_to_upper_closed() {
            let lower_closed = LowerBoundRuntime::new_closed(10.0);
            let upper_closed: UpperBoundRuntime<f64> = lower_closed.flip_bound_side();

            assert!(upper_closed.is_closed_variant());
            assert_eq!(*upper_closed.as_ref(), 10.0);
        }

        /// Test flip_bound_side() from upper to lower bounds
        #[test]
        fn flip_bound_side_upper_open_to_lower_open() {
            let upper_open = UpperBoundRuntime::new_open(20.0);
            let lower_open: LowerBoundRuntime<f64> = upper_open.flip_bound_side();

            assert!(lower_open.is_open_variant());
            assert_eq!(*lower_open.as_ref(), 20.0);
        }

        #[test]
        fn flip_bound_side_upper_closed_to_lower_closed() {
            let upper_closed = UpperBoundRuntime::new_closed(15.0);
            let lower_closed: LowerBoundRuntime<f64> = upper_closed.flip_bound_side();

            assert!(lower_closed.is_closed_variant());
            assert_eq!(*lower_closed.as_ref(), 15.0);
        }

        /// Test that flipping bound side twice returns to original side
        #[test]
        fn flip_bound_side_roundtrip() {
            // Lower to upper to lower
            let lower_open_original = LowerBoundRuntime::new_open(3.0);
            let upper_open: UpperBoundRuntime<f64> = lower_open_original.clone().flip_bound_side();
            let lower_open_back: LowerBoundRuntime<f64> = upper_open.flip_bound_side();
            assert_eq!(lower_open_original, lower_open_back);

            let lower_closed_original = LowerBoundRuntime::new_closed(7.0);
            let upper_closed: UpperBoundRuntime<f64> =
                lower_closed_original.clone().flip_bound_side();
            let lower_closed_back: LowerBoundRuntime<f64> = upper_closed.flip_bound_side();
            assert_eq!(lower_closed_original, lower_closed_back);

            // Upper to lower to upper
            let upper_open_original = UpperBoundRuntime::new_open(12.0);
            let lower_open_temp: LowerBoundRuntime<f64> =
                upper_open_original.clone().flip_bound_side();
            let upper_open_back: UpperBoundRuntime<f64> = lower_open_temp.flip_bound_side();
            assert_eq!(upper_open_original, upper_open_back);

            let upper_closed_original = UpperBoundRuntime::new_closed(18.0);
            let lower_closed_temp: LowerBoundRuntime<f64> =
                upper_closed_original.clone().flip_bound_side();
            let upper_closed_back: UpperBoundRuntime<f64> = lower_closed_temp.flip_bound_side();
            assert_eq!(upper_closed_original, upper_closed_back);
        }

        /// Test combining flip_bound_side() and flip_bound_type()
        #[test]
        fn combined_flip_lower_closed_to_upper_open() {
            let lower_closed = LowerBoundRuntime::new_closed(3.0);
            let upper_open: UpperBoundRuntime<f64> = lower_closed
                .flip_bound_side() // Lower Closed -> Upper Closed
                .flip_bound_type(); // Upper Closed -> Upper Open

            assert!(upper_open.is_open_variant());
            assert_eq!(*upper_open.as_ref(), 3.0);
        }

        #[test]
        fn combined_flip_lower_open_to_upper_closed() {
            let lower_open = LowerBoundRuntime::new_open(8.0);
            let upper_closed: UpperBoundRuntime<f64> = lower_open
                .flip_bound_side() // Lower Open -> Upper Open
                .flip_bound_type(); // Upper Open -> Upper Closed

            assert!(upper_closed.is_closed_variant());
            assert_eq!(*upper_closed.as_ref(), 8.0);
        }

        #[test]
        fn combined_flip_upper_closed_to_lower_open() {
            let upper_closed = UpperBoundRuntime::new_closed(15.0);
            let lower_open: LowerBoundRuntime<f64> = upper_closed
                .flip_bound_side() // Upper Closed -> Lower Closed
                .flip_bound_type(); // Lower Closed -> Lower Open

            assert!(lower_open.is_open_variant());
            assert_eq!(*lower_open.as_ref(), 15.0);
        }

        #[test]
        fn combined_flip_upper_open_to_lower_closed() {
            let upper_open = UpperBoundRuntime::new_open(22.0);
            let lower_closed: LowerBoundRuntime<f64> = upper_open
                .flip_bound_side() // Upper Open -> Lower Open
                .flip_bound_type(); // Lower Open -> Lower Closed

            assert!(lower_closed.is_closed_variant());
            assert_eq!(*lower_closed.as_ref(), 22.0);
        }

        /// Test flip_bound_type() preserves value with negative numbers
        #[test]
        fn flip_bound_type_negative_values() {
            let lower_open = LowerBoundRuntime::new_open(-5.0);
            let lower_closed = lower_open.flip_bound_type();
            assert_eq!(*lower_closed.as_ref(), -5.0);
            assert!(lower_closed.is_closed_variant());

            let upper_closed = UpperBoundRuntime::new_closed(-10.0);
            let upper_open = upper_closed.flip_bound_type();
            assert_eq!(*upper_open.as_ref(), -10.0);
            assert!(upper_open.is_open_variant());
        }

        /// Test flip_bound_side() preserves value with negative numbers
        #[test]
        fn flip_bound_side_negative_values() {
            let lower_open = LowerBoundRuntime::new_open(-3.0);
            let upper_open: UpperBoundRuntime<f64> = lower_open.flip_bound_side();
            assert_eq!(*upper_open.as_ref(), -3.0);
            assert!(upper_open.is_open_variant());

            let upper_closed = UpperBoundRuntime::new_closed(-7.0);
            let lower_closed: LowerBoundRuntime<f64> = upper_closed.flip_bound_side();
            assert_eq!(*lower_closed.as_ref(), -7.0);
            assert!(lower_closed.is_closed_variant());
        }

        /// Test with zero values
        #[test]
        fn transformations_with_zero() {
            let lower_zero_open = LowerBoundRuntime::new_open(0.0);
            let lower_zero_closed = lower_zero_open.flip_bound_type();
            assert_eq!(*lower_zero_closed.as_ref(), 0.0);
            assert!(lower_zero_closed.is_closed_variant());

            let upper_zero_closed = UpperBoundRuntime::new_closed(0.0);
            let lower_zero_from_upper: LowerBoundRuntime<f64> = upper_zero_closed.flip_bound_side();
            assert_eq!(*lower_zero_from_upper.as_ref(), 0.0);
            assert!(lower_zero_from_upper.is_closed_variant());
        }

        /// Test with very small positive values (edge case)
        #[test]
        fn transformations_with_small_values() {
            let tiny_value = 1e-100;

            let lower_open = LowerBoundRuntime::new_open(tiny_value);
            let lower_closed = lower_open.flip_bound_type();
            assert_eq!(*lower_closed.as_ref(), tiny_value);

            let upper_open = UpperBoundRuntime::new_open(tiny_value);
            let lower_open_from_upper: LowerBoundRuntime<f64> = upper_open.flip_bound_side();
            assert_eq!(*lower_open_from_upper.as_ref(), tiny_value);
        }

        /// Test with very large values
        #[test]
        fn transformations_with_large_values() {
            let large_value = 1e100;

            let upper_closed = UpperBoundRuntime::new_closed(large_value);
            let upper_open = upper_closed.flip_bound_type();
            assert_eq!(*upper_open.as_ref(), large_value);
            assert!(upper_open.is_open_variant());

            let lower_closed = LowerBoundRuntime::new_closed(large_value);
            let upper_closed_from_lower: UpperBoundRuntime<f64> = lower_closed.flip_bound_side();
            assert_eq!(*upper_closed_from_lower.as_ref(), large_value);
            assert!(upper_closed_from_lower.is_closed_variant());
        }

        /// Test commutativity: order of flip_bound_side and flip_bound_type shouldn't matter
        #[test]
        fn flip_operations_commute() {
            let original = LowerBoundRuntime::new_closed(5.0);

            // Side first, then type
            let result1: UpperBoundRuntime<f64> = original
                .clone()
                .flip_bound_side() // Lower Closed -> Upper Closed
                .flip_bound_type(); // Upper Closed -> Upper Open

            // Type first, then side
            let result2: UpperBoundRuntime<f64> = original
                .flip_bound_type() // Lower Closed -> Lower Open
                .flip_bound_side(); // Lower Open -> Upper Open

            assert_eq!(result1, result2);
            assert!(result1.is_open_variant());
            assert_eq!(*result1.as_ref(), 5.0);
        }

        /// Test that flip_bound_side preserves the type (open/closed)
        #[test]
        fn flip_bound_side_preserves_type() {
            // Open bounds stay open
            let lower_open = LowerBoundRuntime::new_open(5.0);
            let upper_open: UpperBoundRuntime<f64> = lower_open.flip_bound_side();
            assert!(upper_open.is_open_variant());

            let upper_open2 = UpperBoundRuntime::new_open(10.0);
            let lower_open2: LowerBoundRuntime<f64> = upper_open2.flip_bound_side();
            assert!(lower_open2.is_open_variant());

            // Closed bounds stay closed
            let lower_closed = LowerBoundRuntime::new_closed(15.0);
            let upper_closed: UpperBoundRuntime<f64> = lower_closed.flip_bound_side();
            assert!(upper_closed.is_closed_variant());

            let upper_closed2 = UpperBoundRuntime::new_closed(20.0);
            let lower_closed2: LowerBoundRuntime<f64> = upper_closed2.flip_bound_side();
            assert!(lower_closed2.is_closed_variant());
        }

        /// Test that flip_bound_type preserves the side (lower/upper)
        #[test]
        fn flip_bound_type_preserves_side() {
            // Lower bounds stay lower (test by trying to use as lower bounds)
            let lower_open = LowerBoundRuntime::new_open(5.0);
            let lower_closed = lower_open.flip_bound_type();
            // If this compiles and runs, the type is still LowerBoundRuntime
            assert_eq!(*lower_closed.as_ref(), 5.0);

            // Upper bounds stay upper
            let upper_closed = UpperBoundRuntime::new_closed(10.0);
            let upper_open = upper_closed.flip_bound_type();
            // If this compiles and runs, the type is still UpperBoundRuntime
            assert_eq!(*upper_open.as_ref(), 10.0);
        }

        /// Test use case: interval difference computation
        /// When computing A \ B, we need to flip bounds appropriately
        #[test]
        fn interval_difference_use_case() {
            // A = [0, 10], B = [5, 7]
            // Result should be [0, 5) ∪ (7, 10]

            // B's lower bound [5 needs to become upper bound )5 for left part
            let b_lower = LowerBoundRuntime::new_closed(5.0);
            let left_upper: UpperBoundRuntime<f64> = b_lower
                .flip_bound_side() // Lower Closed -> Upper Closed
                .flip_bound_type(); // Upper Closed -> Upper Open

            assert!(left_upper.is_open_variant());
            assert_eq!(*left_upper.as_ref(), 5.0);
            // This creates the upper bound )5 for interval [0, 5)

            // B's upper bound ]7 needs to become lower bound (7 for right part
            let b_upper = UpperBoundRuntime::new_closed(7.0);
            let right_lower: LowerBoundRuntime<f64> = b_upper
                .flip_bound_side() // Upper Closed -> Lower Closed
                .flip_bound_type(); // Lower Closed -> Lower Open

            assert!(right_lower.is_open_variant());
            assert_eq!(*right_lower.as_ref(), 7.0);
            // This creates the lower bound (7 for interval (7, 10]
        }

        /// Test with validated scalar types
        #[test]
        fn transformations_with_validated_types() {
            use num_valid::RealNative64StrictFiniteInDebug;
            use try_create::TryNew;

            let value = RealNative64StrictFiniteInDebug::try_new(5.0).unwrap();

            let lower_open = LowerBoundRuntime::new_open(value);
            let lower_closed = lower_open.flip_bound_type();
            assert!(lower_closed.is_closed_variant());
            assert_eq!(*lower_closed.as_ref(), value);

            let upper_closed = UpperBoundRuntime::new_closed(value);
            let lower_closed_from_upper: LowerBoundRuntime<_> = upper_closed.flip_bound_side();
            assert!(lower_closed_from_upper.is_closed_variant());
            assert_eq!(*lower_closed_from_upper.as_ref(), value);
        }

        /// Test multiple sequential transformations
        #[test]
        fn multiple_sequential_transformations() {
            let original = LowerBoundRuntime::new_open(10.0);

            // Apply transformations in sequence
            let step1 = original.flip_bound_type(); // Lower Open -> Lower Closed
            let step2: UpperBoundRuntime<f64> = step1.flip_bound_side(); // Lower Closed -> Upper Closed
            let step3 = step2.flip_bound_type(); // Upper Closed -> Upper Open
            let step4: LowerBoundRuntime<f64> = step3.flip_bound_side(); // Upper Open -> Lower Open

            // Should return to same type as original
            assert!(step4.is_open_variant());
            assert_eq!(*step4.as_ref(), 10.0);
        }
    }
}
//------------------------------------------------------------------------------------------------
