# Copilot Instructions for grid1d

## Project Overview
**grid1d** is a Rust library (Edition 2024, Nightly required) for mathematically robust 1D grids and interval partitions. It emphasizes compile-time type safety, generic scalar support via `num-valid`, and performance for numerical computing, finite element methods, and adaptive mesh refinement.

**Current version**: 0.2.0

## Build & Test Commands
```bash
cargo build                      # Nightly auto-configured via rust-toolchain.toml
cargo test                       # Run all tests
cargo test --features rug        # With arbitrary precision (needs libgmp-dev, libmpfr-dev, libmpc-dev)
cargo test --features backtrace  # Enable backtraces in errors
cargo bench                      # Criterion benchmarks → target/criterion/
cargo doc --open                 # Generate and view documentation
```

## Architecture

| Module | Purpose | Key Types |
|--------|---------|-----------|
| `lib.rs` | Grid types, core errors | `Grid1D`, `Grid1DUniform`, `Grid1DNonUniform`, `ErrorsGrid1D` |
| `operations.rs` | Grid operations module | Re-exports from `refinement` and `union` submodules |
| `operations/refinement.rs` | Grid refinement operations | `Grid1DRefinement`, `Grid1DUniformRefinement`, `Grid1DNonUniformRefinement` |
| `operations/union.rs` | Grid union operations | `Grid1DUnion`, `ErrorsGrid1DUnion` |
| `coords.rs` | Coordinate collections | `Coords1D`, `Coords1DInDomain`, `Coords1DInDomainOwned`, `Coords1DInDomainBorrowed`, `Coords1DInDomainArc`, `Coords1DInDomainRc`, `Coords1DInDomainBox`, `Coords1DInDomainCow` |
| `intervals.rs` | Type-safe interval system | `IntervalClosed`, `IntervalOpen`, 6 half-open/unbounded variants, `IntervalSingleton`, `Interval` enum, `IntervalFinitePositiveLength`, `IntervalFiniteLength` |
| `intervals/bounded.rs` | Bounded interval implementations | Internal implementation details |
| `intervals/unbounded.rs` | Unbounded interval implementations | Internal implementation details |
| `intervals/operations.rs` | Interval operations | `IntervalOperations`, intersection, union, hull operations |
| `intervals/conversions.rs` | Interval type conversions | Conversion traits between interval types |
| `bounds.rs` | Boundary marker types | `LowerBoundClosed`, `LowerBoundOpen`, `UpperBoundClosed`, `UpperBoundOpen`, `IntervalBound<RealType, Side, Type>`, `BoundSide`, `BoundType`, `Lower`, `Upper`, `Open`, `Closed` |
| `scalars.rs` | Domain-validated wrappers | `NumIntervals`, `IntervalId`, `PositiveNumPoints1D`, `PositiveSize`, `Size` |
| `traits.rs` | Core trait definitions | `HasDomain1D`, `HasCoords1D`, `IntervalPartition`, `BuildIntervalInPartition`, `TransformedPoints1D` |

**Note**: Grid operation types (`Grid1DRefinement`, `Grid1DUniformRefinement`, `Grid1DNonUniformRefinement`, `Grid1DUnion`, `ErrorsGrid1DUnion`) are re-exported at the crate root for ergonomic access.

### Core Traits Hierarchy
```
IntervalPartition ─┬─ HasCoords1D  (coordinate access: coords(), num_points())
                   └─ HasDomain1D  (domain access: domain())
                          └─ Domain1D: BuildIntervalInPartition

BuildIntervalInPartition
    └─ IntervalFinitePositiveLengthTrait

IntervalTrait      ─── Base trait for all interval types (contains_point, length, etc.)
IntervalFinitePositiveLengthTrait ─── For bounded intervals with positive measure (new(), try_new(), length(), mid_point())
Contains           ─── Point containment trait (contains_point)
```

## Critical Design Patterns

### 1. Type-Safe Intervals (Never boolean flags)
Boundary semantics are encoded in the type, not runtime values:
```rust
// ✅ Correct: type encodes boundary behavior
let closed = 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, +∞)

// ❌ Wrong: this API does not exist
// Interval::new(a, b, is_left_closed, is_right_closed)
```

**All 9 interval types:**
| Type | Notation | Use Case |
|------|----------|----------|
| `IntervalClosed` | [a, b] | Standard bounded domains |
| `IntervalOpen` | (a, b) | Open sets, strict inequalities |
| `IntervalLowerClosedUpperOpen` | [a, b) | Periodic domains, partitions |
| `IntervalLowerOpenUpperClosed` | (a, b] | Asymmetric boundaries |
| `IntervalLowerClosedUpperUnbounded` | [a, +∞) | Semi-infinite domains |
| `IntervalLowerOpenUpperUnbounded` | (a, +∞) | Strict lower, unbounded above |
| `IntervalLowerUnboundedUpperClosed` | (-∞, b] | Unbounded below |
| `IntervalLowerUnboundedUpperOpen` | (-∞, b) | Strictly bounded above |
| `IntervalSingleton` | {a} | Single point (degenerate) |

### 2. Fallible (`try_new`) vs Infallible (`new`) Construction
```rust
use grid1d::intervals::*;  // Required for new()/try_new() trait methods

// try_new(): validated constraints, can fail
let n = NumIntervals::try_new(0);   // Err: must be ≥1
let interval = IntervalClosed::try_new(5.0, 3.0);  // Err: lower > upper

// new(): always valid, no constraints or pre-validated
let id = IntervalId::new(5);        // Always succeeds
let closed = IntervalClosed::new(0.0, 1.0);  // Panics if invalid (use in trusted contexts)
```

**Note**: `new()` and `try_new()` for interval types are defined on `IntervalFinitePositiveLengthTrait`.
Use `use grid1d::intervals::*` to bring these methods into scope.

### 3. Grid Construction & Selection
```rust
use grid1d::{Grid1D, IntervalPartition, intervals::*, scalars::NumIntervals};
use sorted_vec::partial::SortedSet;
use try_create::TryNew;

// Uniform grid: O(1) point location, best for regular discretizations
let grid = Grid1D::uniform(IntervalClosed::new(0.0, 1.0), NumIntervals::try_new(10).unwrap());

// Non-uniform grid: O(log n) point location, for adaptive/clustered meshes
// Domain is inferred from the first and last coordinates
let coords = SortedSet::from_unsorted(vec![0.0, 0.1, 0.5, 1.0]);
let grid = Grid1D::<IntervalClosed<f64>>::try_from_sorted(coords)?;
```

**Grid type selection:**
| Scenario | Grid Type | Why |
|----------|-----------|-----|
| Regular FDM mesh | `Grid1DUniform` | O(1) lookups, predictable timing |
| Boundary layers, adaptive FEM | `Grid1DNonUniform` | Custom point clustering |
| Multi-physics coupling | `Grid1DUnion` | Combines grids with bidirectional mapping |
| Mesh refinement | `Grid1DRefinement` | Parent-child interval tracking |

### 4. Scalar Types (import from `num-valid`, not `grid1d`)
```rust
use num_valid::{RealNative64StrictFiniteInDebug, RealScalar};
type Real = RealNative64StrictFiniteInDebug;  // Recommended default
```

| Use Case | Type | Performance | Validation |
|----------|------|-------------|------------|
| **Development (recommended)** | `RealNative64StrictFiniteInDebug` | ⚡⚡⚡ Same as f64 | Debug only |
| Max performance | `f64` | ⚡⚡⚡ Maximum | None |
| Safety-critical | `RealNative64StrictFinite` | ⚡⚡ ~5-10% overhead | Always |
| Arbitrary precision | `RealRugStrictFinite<N>` | ⚡ Variable | Always |

**Also import from `num-valid`:** `AbsoluteTolerance`, `RelativeTolerance`, `PositiveRealScalar`, `NonNegativeRealScalar`

### 5. Error Handling with `thiserror`
All error types are generic and use `thiserror`:
```rust
// Error types include backtrace for debugging
pub enum ErrorsGrid1D<Domain1D: IntervalFinitePositiveLengthTrait> {
    RequiredAtLeastTwoDistinctPoints { backtrace: Backtrace },
    ...
}
pub enum ErrorsIntervalConstruction<RealType: RealScalar> {
    LowerBoundGreaterOrEqualThanUpperBound { lower_bound: RealType, upper_bound: RealType, backtrace: Backtrace },
}
pub enum ErrorsGrid1DUnion<Domain1D: IntervalFinitePositiveLengthTrait> {
    DomainsMismatch { domain_grid_a: Domain1D, domain_grid_b: Domain1D, backtrace: Backtrace },
    ...
}
```

### 6. Code Generation with `duplicate`
The crate uses `duplicate::duplicate_item` macro extensively to reduce boilerplate for similar implementations across interval types:
```rust
#[duplicate_item(
    interval_type                  lower_bound_type upper_bound_type;
    [IntervalClosed]               [Closed]         [Closed];
    [IntervalOpen]                 [Open]           [Open];
    [IntervalLowerClosedUpperOpen] [Closed]         [Open];
    ...
)]
impl<RealType: RealScalar> IntervalTrait for interval_type<RealType> { ... }
```

## Common Operations

### Point Location & Interval Access
```rust
// Single point location
let id: IntervalId = grid.find_interval_id_of_point(&0.3);
let interval: SubIntervalInPartition<_> = grid.interval(&id);

// Iterate over all intervals
for (id, interval) in grid.iter_intervals() {
    println!("Interval {}: {:?}", id.as_ref(), interval);
}

// Interval length (type-safe wrapper)
let length: PositiveRealScalar<_> = grid.interval_length(&id);
```

### Grid Union (Multi-Physics Coupling)
```rust
let union = Grid1DUnion::try_new(&flow_grid, &chemistry_grid)?;

// Bidirectional mapping
for (unified_id, flow_id, chem_id) in union.iter_interval_mappings() {
    // Transfer data between physics
}

// Direct mapping access
let (original_a, original_b) = union.find_original_intervals(&refined_id);
```

### Grid Refinement (AMR)
```rust
use grid1d::{Grid1D, IntervalPartition, intervals::*, scalars::{NumIntervals, PositiveNumPoints1D, IntervalId}};
use try_create::TryNew;
use std::collections::BTreeMap;

let grid = Grid1D::uniform(IntervalClosed::new(0.0, 1.0), NumIntervals::try_new(4).unwrap());

// Uniform refinement: each interval gets 1 extra point (2 sub-intervals)
let refinement = grid.refine_uniform(&PositiveNumPoints1D::try_new(1).unwrap());

// Track parent-child relationships
for (refined_id, original_id) in refinement.iter_refined_with_mapping() {
    println!("Refined {} came from original {}", refined_id.as_ref(), original_id.as_ref());
}

// Transfer data from coarse to fine
let coarse_data = vec![1.0, 2.0, 3.0, 4.0];
let fine_data = refinement.transfer_interval_data(&coarse_data);

// Selective refinement with BTreeMap
let selective_plan = BTreeMap::from([
    (IntervalId::new(1), PositiveNumPoints1D::try_new(2).unwrap()), // 3 sub-intervals
    (IntervalId::new(3), PositiveNumPoints1D::try_new(1).unwrap()), // 2 sub-intervals
]);
let selective_refinement = grid.refine(&selective_plan);
```

### Coordinates with Ownership Flexibility
```rust
use grid1d::coords::{Coords1DInDomain, Coords1DInDomainOwned, Coords1DInDomainBorrowed, Coords1DInDomainArc};

// Owned, borrowed, Arc, or Rc storage
let owned: Coords1DInDomainOwned<IntervalClosed<f64>> = Coords1DInDomain::try_new(coords, domain)?;
let borrowed: Coords1DInDomainBorrowed<_> = Coords1DInDomain::try_new(&coords, domain)?;
let arc_shared: Coords1DInDomainArc<_> = Coords1DInDomain::try_new(Arc::new(coords), domain)?;
```

## Key Pitfalls to Avoid

1. **Heterogeneous intervals in collections**: Use `Interval<T>` enum wrapper, not mixed concrete types
2. **Raw `usize` for grid quantities**: Always use `NumIntervals`, `IntervalId`, `PositiveNumPoints1D`
3. **Assuming uniform spacing**: Don't assume `interval_length()` is constant for generic `IntervalPartition`
4. **Importing from wrong crate**: Scalar types (`RealScalar`, tolerances) come from `num-valid`
5. **Ignoring boundary semantics**: Open bounds exclude endpoints—critical for partitions and containment checks
6. **Using `new()` with untrusted input**: Use `try_new()` for user input; `new()` panics on invalid data
7. **Coords types are in `coords` module**: Import `Coords1D`, `Coords1DInDomain*` from `grid1d::coords`
8. **Missing trait import for interval construction**: Use `use grid1d::intervals::*` to access `new()`/`try_new()` on interval types (they're trait methods on `IntervalFinitePositiveLengthTrait`)

## Codebase Conventions

### Derive Macros (standard pattern)
```rust
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]  // Basic types
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Getters)]  // With getters
#[derive(Debug, Clone, Into, Getters, Serialize, Deserialize)]  // With Into conversion
```

### Serde Bounds for Generics
```rust
#[derive(Serialize, Deserialize)]
#[serde(bound(deserialize = "RealType: for<'a> Deserialize<'a>"))]
pub struct IntervalBounded<RealType: RealScalar, ...> { ... }
```

### Accessor Methods
- Use `getset` crate: `#[derive(Getters)]` with `#[getset(get = "pub")]`
- Return references for complex types, copies for primitives
- Use `.as_ref()` to access inner value of newtype wrappers

### Test Organization
```rust
#[cfg(test)]
mod tests {
    use super::*;
    
    mod native64_strict_finite {  // Group by scalar type
        type Real = RealNative64StrictFinite;
        
        mod coords1d {  // Group by functionality
            #[test]
            fn test_coords1d_creation() { ... }
        }
    }
    
    #[cfg(feature = "rug")]
    mod rug_tests { ... }  // Feature-gated tests
}
```

### Documentation
- Every public item has doc comments with examples
- Examples are runnable (`cargo test` runs doc tests)
- Mathematical properties documented in `/// ## Mathematical Properties` sections
- Performance characteristics in tables

## Project Resources
- `README.md`: Quick start, feature overview, badges
- `TUTORIAL.md`: Step-by-step examples from basic to advanced
- `DECISION_GUIDE.md`: Decision trees for grid/scalar/interval selection
- `examples/`: Standalone programs (`coords1d_serialization.rs`, `generic_coords_in_domain.rs`, `interval_hull_generalized.rs`)
- `benches/`: Criterion benchmarks (`grid1d_benchmarks.rs`, `interval_benchmarks.rs`)