///|
/// GenomicRanges Advanced Demo - Tiling, Windows, and Set Operations
///
/// This demo shows how to:
/// 1. Tile genomic ranges into non-overlapping sub-ranges
/// 2. Generate sliding windows over ranges
/// 3. Tile an entire genome by chromosome
/// 4. Compute coverage in sliding windows
/// 5. Perform set operations (gaps, subtract)
/// 6. Create promoter regions
fn main {
  println("=== GenomicRanges Advanced: Tiling and Windowing Demo ===\n")

  // Step 1: Create GRanges
  println("Step 1: Creating GRanges...")
  let gr = @bio.granges(
    ["chr1", "chr1", "chr2"],
    [(100, 300), (500, 900), (200, 600)],
    [@bio.strand_plus(), @bio.strand_minus(), @bio.strand_star()],
  )
  println("  Original ranges: \{gr.seqnames.length()}")
  for i = 0; i < gr.seqnames.length(); i = i + 1 {
    println(
      "  \{gr.seqnames[i]}: \{gr.starts[i]}-\{gr.ends[i]} (\{@bio.strand_to_char(gr.strands[i])})",
    )
  }

  // Step 2: Tile by fixed width
  println("\nStep 2: Tiling by width=100...")
  let tiled = @bio.granges_tile(gr, width=100)
  println("  Tiled ranges: \{tiled.seqnames.length()}")
  for i = 0; i < tiled.seqnames.length(); i = i + 1 {
    println("  \{tiled.seqnames[i]}: \{tiled.starts[i]}-\{tiled.ends[i]}")
  }

  // Step 3: Tile by number
  println("\nStep 3: Tiling by n=3 (3 tiles per range)...")
  let tiled_n = @bio.granges_tile(gr, n=3)
  println("  Tiled ranges: \{tiled_n.seqnames.length()}")
  for i = 0; i < tiled_n.seqnames.length(); i = i + 1 {
    println("  \{tiled_n.seqnames[i]}: \{tiled_n.starts[i]}-\{tiled_n.ends[i]}")
  }

  // Step 4: Sliding windows
  println("\nStep 4: Sliding windows (width=100, step=50)...")
  let windows = @bio.granges_sliding_windows(gr, 100, 50)
  println("  Window count: \{windows.seqnames.length()}")
  for i = 0; i < windows.seqnames.length(); i = i + 1 {
    println("  \{windows.seqnames[i]}: \{windows.starts[i]}-\{windows.ends[i]}")
  }

  // Step 5: Tile entire genome
  println("\nStep 5: Tiling entire genome (width=500)...")
  let seqlengths = [("chr1", 2000), ("chr2", 1500)]
  let genome_tiles = @bio.tile_genome(seqlengths, 500)
  println("  Genome tiles: \{genome_tiles.seqnames.length()}")
  for i = 0; i < genome_tiles.seqnames.length(); i = i + 1 {
    println(
      "  \{genome_tiles.seqnames[i]}: \{genome_tiles.starts[i]}-\{genome_tiles.ends[i]}",
    )
  }

  // Step 6: Tile by ranges (exactly n tiles per range)
  println("\nStep 6: Tile by ranges (n=4 per range)...")
  let tile_by_r = @bio.tile_by_ranges(gr, 4)
  println("  Tiled ranges: \{tile_by_r.seqnames.length()}")
  for i = 0; i < tile_by_r.seqnames.length(); i = i + 1 {
    println(
      "  \{tile_by_r.seqnames[i]}: \{tile_by_r.starts[i]}-\{tile_by_r.ends[i]} (width=\{tile_by_r.widths[i]})",
    )
  }

  // Step 7: Coverage by window
  println("\nStep 7: Coverage by window (chr1, width=100, step=50)...")
  let coverage = @bio.coverage_by_window(gr, "chr1", 1000, 100, 50)
  println("  Windows: \{coverage.length()}")
  for entry in coverage {
    let (start, end, count) = entry
    println("  chr1: \{start}-\{end} -> coverage=\{count}")
  }

  // Step 8: Bin genome
  println("\nStep 8: Bin genome (width=200)...")
  let bins = @bio.bin_genome(gr, seqlengths, 200)
  println("  Bins: \{bins.length()}")
  for entry in bins {
    let (chrom, start, end, count, density) = entry
    if count > 0 {
      println(
        "  \{chrom}: \{start}-\{end} -> count=\{count}, density=\{density.to_string()}",
      )
    }
  }

  // Step 9: Promoters
  println(
    "\nStep 9: Creating promoter regions (upstream=200, downstream=50)...",
  )
  let promoters = @bio.granges_promoters(gr, 200, 50)
  println("  Promoter ranges: \{promoters.seqnames.length()}")
  for i = 0; i < promoters.seqnames.length(); i = i + 1 {
    println(
      "  \{promoters.seqnames[i]}: \{promoters.starts[i]}-\{promoters.ends[i]} (\{@bio.strand_to_char(promoters.strands[i])})",
    )
  }

  // Step 10: Gaps
  println("\nStep 10: Finding gaps in chr1 (1 to 1000)...")
  let gr_chr1 = @bio.granges(["chr1", "chr1"], [(100, 200), (400, 600)], [
    @bio.strand_plus(),
    @bio.strand_plus(),
  ])
  let gaps = @bio.granges_gaps(gr_chr1, start_limit=1, end_limit=1000)
  println("  Gap ranges: \{gaps.seqnames.length()}")
  for i = 0; i < gaps.seqnames.length(); i = i + 1 {
    println("  \{gaps.seqnames[i]}: \{gaps.starts[i]}-\{gaps.ends[i]}")
  }

  // Step 11: Subtract
  println("\nStep 11: Subtracting ranges...")
  let gr_main = @bio.granges(["chr1"], [(100, 500)], [@bio.strand_plus()])
  let gr_sub = @bio.granges(["chr1"], [(200, 300)], [@bio.strand_minus()])
  let subtracted = @bio.granges_subtract(gr_main, gr_sub)
  println("  After subtraction: \{subtracted.seqnames.length()} ranges")
  for i = 0; i < subtracted.seqnames.length(); i = i + 1 {
    println(
      "  \{subtracted.seqnames[i]}: \{subtracted.starts[i]}-\{subtracted.ends[i]}",
    )
  }

  // Step 12: Resize advanced
  println("\nStep 12: Resizing ranges (width=100, fix=center)...")
  let resized = @bio.granges_resize_advanced(gr, 100, "center")
  println("  Resized ranges: \{resized.seqnames.length()}")
  for i = 0; i < resized.seqnames.length(); i = i + 1 {
    println(
      "  \{resized.seqnames[i]}: \{resized.starts[i]}-\{resized.ends[i]} (width=\{resized.widths[i]})",
    )
  }

  println("\n=== GenomicRanges Advanced Demo Complete ===")
}