///|
/// Unicode UAX #29 Grapheme Cluster Break (GCB) category.
/// Assigned to each code point and used for grapheme cluster boundary determination.
priv enum GCBCategory {
  Other
  CR
  LF
  Control
  Extend
  ZWJ
  Regional_Indicator
  Prepend
  SpacingMark
  L // Hangul Leading Jamo
  V // Hangul Vowel Jamo
  T // Hangul Trailing Jamo
  LV // Hangul LV Syllable
  LVT // Hangul LVT Syllable
  Extended_Pictographic
  InCB_Consonant
} derive(Eq)

///|
/// Returns the GCB category for a code point (returns Other if not in the table).
fn gcb_category(cp : Int) -> GCBCategory {
  // ASCII fast path: 0x20..0x7E are all Other (printable ASCII range).
  // This avoids binary search for the most common code points in typical text.
  if cp >= 0x20 && cp <= 0x7E {
    return Other
  }
  let mut lo = 0
  let mut hi = gcb_table.length() - 1
  while lo <= hi {
    let mid = (lo + hi) / 2
    let (start, end, cat) = gcb_table[mid]
    if cp < start {
      hi = mid - 1
    } else if cp > end {
      lo = mid + 1
    } else {
      return cat
    }
  }
  Other
}

///|
/// Returns whether the code point is InCB=Linker.
fn is_incb_linker(cp : Int) -> Bool {
  let mut lo = 0
  let mut hi = incb_linker_table.length() - 1
  while lo <= hi {
    let mid = (lo + hi) / 2
    let v = incb_linker_table[mid]
    if cp < v {
      hi = mid - 1
    } else if cp > v {
      lo = mid + 1
    } else {
      return true
    }
  }
  false
}

///|
/// Returns whether the code point is InCB=Extend.
fn is_incb_extend(cp : Int) -> Bool {
  let mut lo = 0
  let mut hi = incb_extend_table.length() - 1
  while lo <= hi {
    let mid = (lo + hi) / 2
    let (start, end) = incb_extend_table[mid]
    if cp < start {
      hi = mid - 1
    } else if cp > end {
      lo = mid + 1
    } else {
      return true
    }
  }
  false
}