// Test file demonstrating the generalized IntervalHull trait

use grid1d::intervals::*;
use try_create::New;

fn main() {
    println!("=== Testing Generalized IntervalHull Trait ===\n");

    // Test 1: Hull of two bounded intervals
    println!("Test 1: Hull of bounded intervals");
    let closed1 = IntervalClosed::new(0.0, 2.0);
    let closed2 = IntervalClosed::new(1.0, 3.0);
    let hull1 = closed1.hull(&closed2);
    println!("  [0, 2] ∪ [1, 3] = {:?}", hull1);

    // Test 2: Hull of bounded with unbounded (lower bounded, upper unbounded)
    println!("\nTest 2: Hull of bounded with half-unbounded");
    let bounded = IntervalClosed::new(0.0, 2.0);
    let unbounded = IntervalLowerClosedUpperUnbounded::new(1.0);
    let hull2 = bounded.hull(&unbounded);
    println!("  [0, 2] ∪ [1, +∞) = {:?}", hull2);

    // Test 3: Hull of two half-unbounded intervals (lower bounded)
    println!("\nTest 3: Hull of two half-unbounded intervals (lower bounded)");
    let unbounded1 = IntervalLowerClosedUpperUnbounded::new(0.0);
    let unbounded2 = IntervalLowerOpenUpperUnbounded::new(5.0);
    let hull3 = unbounded1.hull(&unbounded2);
    println!("  [0, +∞) ∪ (5, +∞) = {:?}", hull3);

    // Test 4: Hull of bounded with upper unbounded
    println!("\nTest 4: Hull of bounded with upper unbounded");
    let bounded = IntervalClosed::new(-2.0, 1.0);
    let upper_unbounded = IntervalLowerUnboundedUpperClosed::new(0.0);
    let hull4 = bounded.hull(&upper_unbounded);
    println!("  [-2, 1] ∪ (-∞, 0] = {:?}", hull4);

    // Test 5: Hull of two upper unbounded intervals
    println!("\nTest 5: Hull of two upper unbounded intervals");
    let upper_unbounded1 = IntervalLowerUnboundedUpperClosed::new(2.0);
    let upper_unbounded2 = IntervalLowerUnboundedUpperOpen::new(-1.0);
    let hull5 = upper_unbounded1.hull(&upper_unbounded2);
    println!("  (-∞, 2] ∪ (-∞, -1) = {:?}", hull5);

    // Test 6: Hull of fully unbounded intervals
    println!("\nTest 6: Hull with fully unbounded interval");
    let bounded = IntervalClosed::new(0.0, 10.0);
    let fully_unbounded = IntervalLowerUnboundedUpperUnbounded::new();
    let hull6 = bounded.hull(&fully_unbounded);
    println!("  [0, 10] ∪ (-∞, +∞) = {:?}", hull6);

    // Test 7: Using the Interval enum directly
    println!("\nTest 7: Using Interval enum");
    let interval1: Interval<f64> = Interval::from(IntervalClosed::new(1.0, 3.0));
    let interval2: Interval<f64> = Interval::from(IntervalLowerClosedUpperUnbounded::new(2.0));
    let hull7 = interval1.hull(&interval2);
    println!("  Interval([1, 3]) ∪ Interval([2, +∞)) = {:?}", hull7);

    // Test 8: Mixed boundary types
    println!("\nTest 8: Mixed boundary types (open/closed)");
    let open = IntervalOpen::new(0.0, 2.0);
    let closed = IntervalClosed::new(1.0, 3.0);
    let hull8 = open.hull(&closed);
    println!("  (0, 2) ∪ [1, 3] = {:?}", hull8);

    // Test 9: Singleton with bounded interval
    println!("\nTest 9: Singleton with bounded interval");
    let singleton = IntervalSingleton::new(1.5);
    let closed = IntervalClosed::new(0.0, 1.0);
    let hull9 = singleton.hull(&closed);
    println!("  {{1.5}} ∪ [0, 1] = {:?}", hull9);

    // Test 10: Singleton with unbounded interval
    println!("\nTest 10: Singleton with unbounded interval");
    let singleton = IntervalSingleton::new(5.0);
    let unbounded = IntervalLowerClosedUpperUnbounded::new(0.0);
    let hull10 = singleton.hull(&unbounded);
    println!("  {{5}} ∪ [0, +∞) = {:?}", hull10);

    println!("\n=== All tests completed successfully! ===");
}
