// Copyright 2026 International Digital Economy Academy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

///|
/// Apply GSUB single substitution lookups to glyph array.
pub fn GsubTable::apply_single(
  self : GsubTable,
  glyphs : Array[UInt],
) -> Result[Bool, OtLayoutError] {
  let layout = self.layout
  self.apply_single_with_lookups(glyphs, layout.lookup_list.offsets[:])
}

///|
pub fn GsubTable::apply_single_with_lookups(
  self : GsubTable,
  glyphs : Array[UInt],
  lookup_offsets : ArrayView[Int],
  masks? : Array[UInt] = [],
  lookup_mask? : UInt = 0xFFFFFFFFU,
) -> Result[Bool, OtLayoutError] {
  if !masks.is_empty() && masks.length() != glyphs.length() {
    return Err(InvalidFormat)
  }
  let data = self.layout.data
  let mut changed = false
  for lookup_offset in lookup_offsets {
    let parsed = parse_lookup(data, lookup_offset, 7)
    match parsed {
      Err(err) => return Err(err)
      Ok((lookup_type, lookup_flag, mark_filtering_set, subtable_offsets)) => {
        if lookup_type != 1 {
          continue
        }
        for subtable_offset in subtable_offsets {
          let res = apply_single_subtable(
            data,
            subtable_offset,
            glyphs,
            lookup_flag,
            mark_filtering_set,
            None,
            masks=masks,
            lookup_mask=lookup_mask,
          )
          match res {
            Err(err) => return Err(err)
            Ok(did_change) => if did_change { changed = true }
          }
        }
      }
    }
  }
  Ok(changed)
}

///|
pub fn GsubTable::apply_with_lookups(
  self : GsubTable,
  glyphs : Array[UInt],
  clusters : Array[Int],
  lookup_offsets : ArrayView[Int],
  gdef? : GdefTable,
  masks? : Array[UInt] = [],
  lookup_mask? : UInt = 0xFFFFFFFFU,
) -> Result[(Array[UInt], Array[Int], Bool), OtLayoutError] {
  let result = match gdef {
    None =>
      self.apply_with_lookups_and_masks(
        glyphs,
        clusters,
        lookup_offsets,
        masks=masks,
        lookup_mask=lookup_mask,
      )
    Some(gdef) =>
      self.apply_with_lookups_and_masks(
        glyphs,
        clusters,
        lookup_offsets,
        gdef=gdef,
        masks=masks,
        lookup_mask=lookup_mask,
      )
  }
  match result {
    Err(err) => Err(err)
    Ok((next_glyphs, next_clusters, _, changed)) => {
      Ok((next_glyphs, next_clusters, changed))
    }
  }
}

///|
pub fn GsubTable::apply_with_lookups_and_masks(
  self : GsubTable,
  glyphs : Array[UInt],
  clusters : Array[Int],
  lookup_offsets : ArrayView[Int],
  gdef? : GdefTable,
  masks? : Array[UInt] = [],
  lookup_mask? : UInt = 0xFFFFFFFFU,
  alt_values? : Array[Int] = [],
) -> Result[(Array[UInt], Array[Int], Array[UInt], Bool), OtLayoutError] {
  if clusters.length() != glyphs.length() {
    return Err(InvalidFormat)
  }
  if !masks.is_empty() && masks.length() != glyphs.length() {
    return Err(InvalidFormat)
  }
  let data = self.layout.data
  let lookup_list_offsets = self.layout.lookup_list.offsets
  let mut current_glyphs = glyphs
  let mut current_clusters = clusters
  let mut current_masks = masks
  let mut changed = false
  let gdef = gdef
  for lookup_offset in lookup_offsets {
    let parsed = parse_lookup(data, lookup_offset, 7)
    match parsed {
      Err(err) => return Err(err)
      Ok((lookup_type, lookup_flag, mark_filtering_set, subtable_offsets)) => {
        if lookup_type == 1 {
          let did_change = match apply_single_subtable_list(
            data,
            subtable_offsets,
            current_glyphs,
            lookup_flag,
            mark_filtering_set,
            gdef,
            masks=current_masks,
            lookup_mask=lookup_mask,
          ) {
            Err(err) => return Err(err)
            Ok(value) => value
          }
          if did_change {
            changed = true
          }
        } else if lookup_type == 2 {
          let result = match apply_multiple_subtable_list(
            data,
            subtable_offsets,
            current_glyphs,
            current_clusters,
            lookup_flag,
            mark_filtering_set,
            gdef,
            masks=current_masks,
            lookup_mask=lookup_mask,
          ) {
            Err(err) => return Err(err)
            Ok(value) => value
          }
          let (next_glyphs, next_clusters, next_masks, did_change) = result
          current_glyphs = next_glyphs
          current_clusters = next_clusters
          current_masks = next_masks
          if did_change {
            changed = true
          }
        } else if lookup_type == 3 {
          let did_change = match apply_alternate_subtable_list(
            data,
            subtable_offsets,
            current_glyphs,
            lookup_flag,
            mark_filtering_set,
            gdef,
            masks=current_masks,
            lookup_mask=lookup_mask,
            alt_values=alt_values,
          ) {
            Err(err) => return Err(err)
            Ok(value) => value
          }
          if did_change {
            changed = true
          }
        } else if lookup_type == 4 {
          let result = match apply_ligature_subtable_list(
            data,
            subtable_offsets,
            current_glyphs,
            current_clusters,
            lookup_flag,
            mark_filtering_set,
            gdef,
            masks=current_masks,
            lookup_mask=lookup_mask,
          ) {
            Err(err) => return Err(err)
            Ok(value) => value
          }
          let (next_glyphs, next_clusters, next_masks, did_change) = result
          current_glyphs = next_glyphs
          current_clusters = next_clusters
          current_masks = next_masks
          if did_change {
            changed = true
          }
        } else if lookup_type == 5 {
          let did_change = match apply_context_subtable_list(
            data,
            subtable_offsets,
            current_glyphs,
            current_clusters,
            lookup_list_offsets,
            lookup_flag,
            mark_filtering_set,
            gdef,
            masks=current_masks,
            lookup_mask=lookup_mask,
          ) {
            Err(err) => return Err(err)
            Ok(value) => value
          }
          if did_change {
            changed = true
          }
        } else if lookup_type == 6 {
          let did_change = match apply_chain_subtable_list(
            data,
            subtable_offsets,
            current_glyphs,
            current_clusters,
            lookup_list_offsets,
            lookup_flag,
            mark_filtering_set,
            gdef,
            masks=current_masks,
            lookup_mask=lookup_mask,
          ) {
            Err(err) => return Err(err)
            Ok(value) => value
          }
          if did_change {
            changed = true
          }
        } else if lookup_type == 8 {
          let did_change = match apply_reverse_chain_subtable_list(
            data,
            subtable_offsets,
            current_glyphs,
            lookup_flag,
            mark_filtering_set,
            gdef,
            masks=current_masks,
            lookup_mask=lookup_mask,
          ) {
            Err(err) => return Err(err)
            Ok(value) => value
          }
          if did_change {
            changed = true
          }
        } else {
          continue
        }
      }
    }
  }
  Ok((current_glyphs, current_clusters, current_masks, changed))
}

///|
fn apply_single_subtable(
  data : BytesView,
  offset : Int,
  glyphs : Array[UInt],
  lookup_flag : Int,
  mark_filtering_set : Int?,
  gdef : GdefTable?,
  masks? : Array[UInt] = [],
  lookup_mask? : UInt = 0xFFFFFFFFU,
) -> Result[Bool, OtLayoutError] {
  let format = read_u16_int(data, offset)
  match format {
    Err(err) => Err(err)
    Ok(format) => {
      let mut changed = false
      for i in 0.. return Err(err)
          Ok(true) => continue
          Ok(false) => ()
        }
        let next = if format == 1 {
          single_substitute_format1(data, offset, glyph)
        } else if format == 2 {
          single_substitute_format2(data, offset, glyph)
        } else {
          Err(InvalidFormat)
        }
        match next {
          Err(err) => return Err(err)
          Ok(None) => ()
          Ok(Some(value)) => {
            if value != glyph {
              glyphs[i] = value
              changed = true
            }
          }
        }
      }
      Ok(changed)
    }
  }
}

///|
fn apply_single_subtable_list(
  data : BytesView,
  offsets : Array[Int],
  glyphs : Array[UInt],
  lookup_flag : Int,
  mark_filtering_set : Int?,
  gdef : GdefTable?,
  masks? : Array[UInt] = [],
  lookup_mask? : UInt = 0xFFFFFFFFU,
) -> Result[Bool, OtLayoutError] {
  let mut changed = false
  for offset in offsets {
    let result = apply_single_subtable(
      data,
      offset,
      glyphs,
      lookup_flag,
      mark_filtering_set,
      gdef,
      masks=masks,
      lookup_mask=lookup_mask,
    )
    match result {
      Err(err) => return Err(err)
      Ok(value) => if value { changed = true }
    }
  }
  Ok(changed)
}

///|
fn apply_ligature_subtable_list(
  data : BytesView,
  offsets : Array[Int],
  glyphs : Array[UInt],
  clusters : Array[Int],
  lookup_flag : Int,
  mark_filtering_set : Int?,
  gdef : GdefTable?,
  masks? : Array[UInt] = [],
  lookup_mask? : UInt = 0xFFFFFFFFU,
) -> Result[(Array[UInt], Array[Int], Array[UInt], Bool), OtLayoutError] {
  let new_glyphs : Array[UInt] = []
  let new_clusters : Array[Int] = []
  let new_masks : Array[UInt] = []
  let has_masks = !masks.is_empty()
  let skip = Array::make(glyphs.length(), false)
  let mut changed = false
  let len = glyphs.length()
  let mut i = 0
  while i < len {
    if skip[i] {
      i = i + 1
      continue
    }
    let glyph = glyphs[i]
    let ignored = should_ignore_glyph(
      glyph,
      lookup_flag,
      gdef,
      mark_filtering_set,
      masks=masks,
      index=i,
      lookup_mask=lookup_mask,
    )
    match ignored {
      Err(err) => return Err(err)
      Ok(true) => {
        new_glyphs.push(glyph)
        new_clusters.push(clusters[i])
        if has_masks {
          new_masks.push(masks[i])
        }
        i = i + 1
        continue
      }
      Ok(false) => ()
    }
    let match_result = find_ligature_match(
      data,
      offsets,
      glyphs,
      i,
      lookup_flag,
      mark_filtering_set,
      gdef,
      masks=masks,
      lookup_mask=lookup_mask,
    )
    match match_result {
      Err(err) => return Err(err)
      Ok(None) => {
        new_glyphs.push(glyphs[i])
        new_clusters.push(clusters[i])
        if has_masks {
          new_masks.push(masks[i])
        }
        i = i + 1
      }
      Ok(Some((lig_glyph, indices))) => {
        if indices.length() < 2 {
          return Err(InvalidFormat)
        }
        new_glyphs.push(lig_glyph)
        new_clusters.push(clusters[indices[0]])
        if has_masks {
          let mut lig_mask = 0U
          for idx in indices {
            lig_mask = lig_mask | masks[idx]
          }
          new_masks.push(lig_mask)
        }
        if lig_glyph != glyphs[indices[0]] {
          changed = true
        }
        for j in 1..= skip.length() {
            return Err(InvalidFormat)
          }
          skip[idx] = true
        }
        changed = true
        i = i + 1
      }
    }
  }
  Ok((new_glyphs, new_clusters, new_masks, changed))
}

///|
fn apply_context_subtable_list(
  data : BytesView,
  offsets : Array[Int],
  glyphs : Array[UInt],
  clusters : Array[Int],
  lookup_list_offsets : Array[Int],
  lookup_flag : Int,
  mark_filtering_set : Int?,
  gdef : GdefTable?,
  masks? : Array[UInt] = [],
  lookup_mask? : UInt = 0xFFFFFFFFU,
) -> Result[Bool, OtLayoutError] {
  let mut changed = false
  for offset in offsets {
    let result = apply_context_subtable(
      data,
      offset,
      glyphs,
      clusters,
      lookup_list_offsets,
      lookup_flag,
      mark_filtering_set,
      gdef,
      masks=masks,
      lookup_mask=lookup_mask,
    )
    match result {
      Err(err) => return Err(err)
      Ok(value) => if value { changed = true }
    }
  }
  Ok(changed)
}

///|
fn apply_context_subtable(
  data : BytesView,
  offset : Int,
  glyphs : Array[UInt],
  clusters : Array[Int],
  lookup_list_offsets : Array[Int],
  lookup_flag : Int,
  mark_filtering_set : Int?,
  gdef : GdefTable?,
  masks? : Array[UInt] = [],
  lookup_mask? : UInt = 0xFFFFFFFFU,
) -> Result[Bool, OtLayoutError] {
  if offset < 0 || offset + 2 > data.length() {
    return Err(UnexpectedEof)
  }
  let format = read_u16_int(data, offset)
  match format {
    Err(err) => Err(err)
    Ok(format) => if format == 1 {
      apply_context_subtable_format1(
        data,
        offset,
        glyphs,
        clusters,
      lookup_list_offsets,
      lookup_flag,
      mark_filtering_set,
      gdef,
      masks=masks,
      lookup_mask=lookup_mask,
    )
  } else if format == 2 {
    apply_context_subtable_format2(
      data,
      offset,
      glyphs,
      clusters,
      lookup_list_offsets,
      lookup_flag,
      mark_filtering_set,
      gdef,
      masks=masks,
      lookup_mask=lookup_mask,
    )
  } else if format == 3 {
    apply_context_subtable_format3(
      data,
      offset,
      glyphs,
      clusters,
      lookup_list_offsets,
      lookup_flag,
      mark_filtering_set,
      gdef,
      masks=masks,
      lookup_mask=lookup_mask,
    )
    } else {
      Err(InvalidFormat)
    }
  }
}

///|
fn apply_context_subtable_format1(
  data : BytesView,
  offset : Int,
  glyphs : Array[UInt],
  clusters : Array[Int],
  lookup_list_offsets : Array[Int],
  lookup_flag : Int,
  mark_filtering_set : Int?,
  gdef : GdefTable?,
  masks? : Array[UInt] = [],
  lookup_mask? : UInt = 0xFFFFFFFFU,
) -> Result[Bool, OtLayoutError] {
  if offset < 0 || offset + 6 > data.length() {
    return Err(UnexpectedEof)
  }
  let coverage_offset = read_u16_int(data, offset + 2)
  let set_count = read_u16_int(data, offset + 4)
  match (coverage_offset, set_count) {
    (Err(err), _) => Err(err)
    (_, Err(err)) => Err(err)
    (Ok(coverage_offset), Ok(set_count)) => {
      if coverage_offset == 0 || set_count <= 0 {
        return Err(InvalidFormat)
      }
      let coverage_base = offset + coverage_offset
      let set_base = offset + 6
      if set_base < 0 || set_base + set_count * 2 > data.length() {
        return Err(UnexpectedEof)
      }
      let mut changed = false
      let mut i = 0
      while i < glyphs.length() {
        let glyph = glyphs[i]
        let ignored = should_ignore_glyph(
          glyph,
          lookup_flag,
          gdef,
          mark_filtering_set,
          masks=masks,
          index=i,
          lookup_mask=lookup_mask,
        )
        match ignored {
          Err(err) => return Err(err)
          Ok(true) => {
            i = i + 1
            continue
          }
          Ok(false) => ()
        }
        let index = coverage_index(data, coverage_base, glyph)
        match index {
          Err(err) => return Err(err)
          Ok(None) => ()
          Ok(Some(index)) => {
            if index < 0 || index >= set_count {
              return Err(InvalidFormat)
            }
            let rule_set_offset = read_u16_int(data, set_base + index * 2)
            match rule_set_offset {
              Err(err) => return Err(err)
              Ok(rule_set_offset) => {
                if rule_set_offset == 0 {
                  return Err(InvalidFormat)
                }
                let rule_set_base = offset + rule_set_offset
                if rule_set_base < 0 || rule_set_base + 2 > data.length() {
                  return Err(UnexpectedEof)
                }
                let rule_count = read_u16_int(data, rule_set_base)
                match rule_count {
                  Err(err) => return Err(err)
                  Ok(rule_count) => {
                    if rule_count <= 0 {
                      return Err(InvalidFormat)
                    }
                    let rule_list_base = rule_set_base + 2
                    if rule_list_base < 0 ||
                        rule_list_base + rule_count * 2 > data.length() {
                      return Err(UnexpectedEof)
                    }
                    let mut advance = i + 1
                    for r in 0.. return Err(err)
                        Ok(rule_offset) => {
                          if rule_offset == 0 {
                            return Err(InvalidFormat)
                          }
                          let rule_base = rule_set_base + rule_offset
                          let result = match apply_context_rule(
                            data,
                            rule_base,
                            glyphs,
                            clusters,
                            i,
                            lookup_list_offsets,
                            lookup_flag,
                            mark_filtering_set,
                            gdef,
                            masks=masks,
                            lookup_mask=lookup_mask,
                          ) {
                            Err(err) => return Err(err)
                            Ok(value) => value
                          }
                          if result.matched {
                            if result.changed {
                              changed = true
                            }
                            advance = result.next_index
                            if advance <= i {
                              advance = i + 1
                            }
                            if advance > glyphs.length() {
                              advance = glyphs.length()
                            }
                            break
                          }
                        }
                      }
                    }
                    i = advance
                    continue
                  }
                }
              }
            }
          }
        }
        i = i + 1
      }
      Ok(changed)
    }
  }
}

///|
fn apply_context_subtable_format2(
  data : BytesView,
  offset : Int,
  glyphs : Array[UInt],
  clusters : Array[Int],
  lookup_list_offsets : Array[Int],
  lookup_flag : Int,
  mark_filtering_set : Int?,
  gdef : GdefTable?,
  masks? : Array[UInt] = [],
  lookup_mask? : UInt = 0xFFFFFFFFU,
) -> Result[Bool, OtLayoutError] {
  if offset < 0 || offset + 8 > data.length() {
    return Err(UnexpectedEof)
  }
  let coverage_offset = read_u16_int(data, offset + 2)
  let class_def_offset = read_u16_int(data, offset + 4)
  let class_set_count = read_u16_int(data, offset + 6)
  match (coverage_offset, class_def_offset, class_set_count) {
    (Err(err), _, _) => Err(err)
    (_, Err(err), _) => Err(err)
    (_, _, Err(err)) => Err(err)
    (Ok(coverage_offset), Ok(class_def_offset), Ok(class_set_count)) => {
      if coverage_offset == 0 || class_def_offset == 0 || class_set_count <= 0 {
        return Ok(false)
      }
      let coverage_base = offset + coverage_offset
      let class_def_base = offset + class_def_offset
      let class_set_base = offset + 8
      if class_set_base < 0 ||
          class_set_base + class_set_count * 2 > data.length() {
        return Err(UnexpectedEof)
      }
      let mut changed = false
      let mut i = 0
      while i < glyphs.length() {
        let glyph = glyphs[i]
        let ignored = should_ignore_glyph(
          glyph,
          lookup_flag,
          gdef,
          mark_filtering_set,
          masks=masks,
          index=i,
          lookup_mask=lookup_mask,
        )
        match ignored {
          Err(err) => return Err(err)
          Ok(true) => {
            i = i + 1
            continue
          }
          Ok(false) => ()
        }
        let covered = coverage_index(data, coverage_base, glyph)
        match covered {
          Err(err) => return Err(err)
          Ok(None) => ()
          Ok(Some(_)) => {
            let class_value = class_def_value(data, class_def_base, glyph)
            match class_value {
              Err(InvalidFormat) => {
                i = i + 1
                continue
              }
              Err(err) => return Err(err)
              Ok(class_value) => {
                if class_value < 0 || class_value >= class_set_count {
                  i = i + 1
                  continue
                }
                let class_set_offset = read_u16_int(
                  data,
                  class_set_base + class_value * 2,
                )
                match class_set_offset {
                  Err(err) => return Err(err)
                  Ok(class_set_offset) => {
                    if class_set_offset == 0 {
                      i = i + 1
                      continue
                    }
                    let rule_set_base = offset + class_set_offset
                    if rule_set_base < 0 || rule_set_base + 2 > data.length() {
                      return Err(UnexpectedEof)
                    }
                    let rule_count = read_u16_int(data, rule_set_base)
                    match rule_count {
                      Err(err) => return Err(err)
                      Ok(rule_count) => {
                        if rule_count <= 0 {
                          i = i + 1
                          continue
                        }
                        let rule_list_base = rule_set_base + 2
                        if rule_list_base < 0 ||
                            rule_list_base + rule_count * 2 > data.length() {
                          return Err(UnexpectedEof)
                        }
                        let mut advance = i + 1
                        for r in 0.. return Err(err)
                            Ok(rule_offset) => {
                              if rule_offset == 0 {
                                continue
                              }
                              let rule_base = rule_set_base + rule_offset
                              let result = match apply_context_class_rule(
                                data,
                                rule_base,
                                glyphs,
                                clusters,
                                i,
                                class_def_base,
                                lookup_list_offsets,
                                lookup_flag,
                                mark_filtering_set,
                                gdef,
                                masks=masks,
                                lookup_mask=lookup_mask,
                              ) {
                                Err(err) => return Err(err)
                                Ok(value) => value
                              }
                              if result.matched {
                                if result.changed {
                                  changed = true
                                }
                                advance = result.next_index
                                if advance <= i {
                                  advance = i + 1
                                }
                                if advance > glyphs.length() {
                                  advance = glyphs.length()
                                }
                                break
                              }
                            }
                          }
                        }
                        i = advance
                        continue
                      }
                    }
                  }
                }
              }
            }
          }
        }
        i = i + 1
      }
      Ok(changed)
    }
  }
}

///|
fn apply_context_subtable_format3(
  data : BytesView,
  offset : Int,
  glyphs : Array[UInt],
  clusters : Array[Int],
  lookup_list_offsets : Array[Int],
  lookup_flag : Int,
  mark_filtering_set : Int?,
  gdef : GdefTable?,
  masks? : Array[UInt] = [],
  lookup_mask? : UInt = 0xFFFFFFFFU,
) -> Result[Bool, OtLayoutError] {
  if offset < 0 || offset + 6 > data.length() {
    return Err(UnexpectedEof)
  }
  let glyph_count = read_u16_int(data, offset + 2)
  let subst_count = read_u16_int(data, offset + 4)
  match (glyph_count, subst_count) {
    (Err(err), _) => Err(err)
    (_, Err(err)) => Err(err)
    (Ok(glyph_count), Ok(subst_count)) => {
      if glyph_count <= 0 || subst_count < 0 {
        return Ok(false)
      }
      let coverage_list_base = offset + 6
      if coverage_list_base < 0 ||
          coverage_list_base + glyph_count * 2 > data.length() {
        return Err(UnexpectedEof)
      }
      let record_base = coverage_list_base + glyph_count * 2
      let mut changed = false
      let mut i = 0
      while i < glyphs.length() {
        let input_indices = collect_forward_indices(
          glyphs[:],
          i,
          glyph_count,
          lookup_flag,
          gdef,
          mark_filtering_set,
          masks=masks,
          lookup_mask=lookup_mask,
        )
        let indices = match input_indices {
          Err(err) => return Err(err)
          Ok(None) => {
            i = i + 1
            continue
          }
          Ok(Some(indices)) => indices
        }
        let mut matched = true
        for j in 0.. return Err(err)
            Ok(cov_offset) => {
              if cov_offset == 0 {
                return Err(InvalidFormat)
              }
              let cov_base = offset + cov_offset
              let covered = coverage_index(data, cov_base, glyphs[indices[j]])
              match covered {
                Err(err) => return Err(err)
                Ok(None) => {
                  matched = false
                  break
                }
                Ok(Some(_)) => ()
              }
            }
          }
        }
        if matched {
          let len_before = glyphs.length()
          let applied = match apply_subst_lookup_records(
            data,
            record_base,
            subst_count,
            glyph_count,
            glyphs,
            clusters,
            i,
            lookup_list_offsets,
            Some(indices),
            lookup_flag,
            mark_filtering_set,
            gdef,
            masks=masks,
            lookup_mask=lookup_mask,
          ) {
            Err(err) => return Err(err)
            Ok(value) => value
          }
          if applied {
            changed = true
          }
          let delta = glyphs.length() - len_before
          let mut advance = indices[indices.length() - 1] + 1 + delta
          if advance <= i {
            advance = i + 1
          }
          if advance > glyphs.length() {
            advance = glyphs.length()
          }
          i = advance
          continue
        }
        i = i + 1
      }
      Ok(changed)
    }
  }
}

///|
priv struct ContextApplyResult {
  matched : Bool
  changed : Bool
  next_index : Int
}

///|
fn context_no_match(index : Int) -> ContextApplyResult {
  ContextApplyResult::{ matched: false, changed: false, next_index: index + 1 }
}

///|
fn apply_context_class_rule(
  data : BytesView,
  offset : Int,
  glyphs : Array[UInt],
  clusters : Array[Int],
  index : Int,
  class_def_base : Int,
  lookup_list_offsets : Array[Int],
  lookup_flag : Int,
  mark_filtering_set : Int?,
  gdef : GdefTable?,
  masks? : Array[UInt] = [],
  lookup_mask? : UInt = 0xFFFFFFFFU,
) -> Result[ContextApplyResult, OtLayoutError] {
  if offset < 0 || offset + 4 > data.length() {
    return Err(UnexpectedEof)
  }
  let glyph_count = read_u16_int(data, offset)
  let subst_count = read_u16_int(data, offset + 2)
  match (glyph_count, subst_count) {
    (Err(err), _) => Err(err)
    (_, Err(err)) => Err(err)
    (Ok(glyph_count), Ok(subst_count)) => {
      if glyph_count <= 0 || subst_count < 0 {
        return Err(InvalidFormat)
      }
      let input_indices = collect_forward_indices(
        glyphs[:],
        index,
        glyph_count,
        lookup_flag,
        gdef,
        mark_filtering_set,
        masks=masks,
        lookup_mask=lookup_mask,
      )
      let indices = match input_indices {
        Err(err) => return Err(err)
        Ok(None) => return Ok(context_no_match(index))
        Ok(Some(indices)) => indices
      }
      let class_list_base = offset + 4
      if class_list_base < 0 ||
          class_list_base + (glyph_count - 1) * 2 > data.length() {
        return Err(UnexpectedEof)
      }
      for i in 0..<(glyph_count - 1) {
        let expected = read_u16_int(data, class_list_base + i * 2)
        match expected {
          Err(err) => return Err(err)
          Ok(expected) => {
            let actual = class_def_value(
              data,
              class_def_base,
              glyphs[indices[i + 1]],
            )
            match actual {
              Err(InvalidFormat) => return Ok(context_no_match(index))
              Err(err) => return Err(err)
              Ok(actual) => if actual != expected {
                return Ok(context_no_match(index))
              }
            }
          }
        }
      }
      let record_base = class_list_base + (glyph_count - 1) * 2
      let len_before = glyphs.length()
      let changed = match apply_subst_lookup_records(
        data,
        record_base,
        subst_count,
        glyph_count,
        glyphs,
        clusters,
        index,
        lookup_list_offsets,
        Some(indices),
        lookup_flag,
        mark_filtering_set,
        gdef,
        masks=masks,
        lookup_mask=lookup_mask,
      ) {
        Err(InvalidFormat) => return Ok(context_no_match(index))
        Err(err) => return Err(err)
        Ok(value) => value
      }
      let delta = glyphs.length() - len_before
      let mut next_index = indices[indices.length() - 1] + 1 + delta
      if next_index <= index {
        next_index = index + 1
      }
      if next_index > glyphs.length() {
        next_index = glyphs.length()
      }
      Ok(ContextApplyResult::{ matched: true, changed, next_index })
    }
  }
}

///|
fn apply_subst_lookup_records(
  data : BytesView,
  record_base : Int,
  subst_count : Int,
  glyph_count : Int,
  glyphs : Array[UInt],
  clusters : Array[Int],
  index : Int,
  lookup_list_offsets : Array[Int],
  input_indices : Array[Int]?,
  input_lookup_flag : Int,
  input_mark_filtering_set : Int?,
  gdef : GdefTable?,
  masks? : Array[UInt] = [],
  lookup_mask? : UInt = 0xFFFFFFFFU,
) -> Result[Bool, OtLayoutError] {
  if subst_count < 0 {
    return Err(InvalidFormat)
  }
  if clusters.length() != glyphs.length() {
    return Err(InvalidFormat)
  }
  let has_masks = !masks.is_empty()
  if has_masks && masks.length() != glyphs.length() {
    return Err(InvalidFormat)
  }
  let mut indices = match input_indices {
    None => {
      let values : Array[Int] = []
      for i in 0.. {
      if values.length() < glyph_count {
        return Err(InvalidFormat)
      }
      values
    }
  }
  if record_base < 0 || record_base + subst_count * 4 > data.length() {
    return Err(UnexpectedEof)
  }
  let mut changed = false
  for i in 0.. return Err(err)
      (_, Err(err)) => return Err(err)
      (Ok(seq_index), Ok(lookup_index)) => {
        if seq_index < 0 || seq_index >= glyph_count {
          return Err(InvalidFormat)
        }
        if lookup_index < 0 || lookup_index >= lookup_list_offsets.length() {
          return Err(InvalidFormat)
        }
        let target = indices[seq_index]
        if target < 0 || target >= glyphs.length() {
          return Err(InvalidFormat)
        }
        let lookup_offset = lookup_list_offsets[lookup_index]
        let (did_change, delta) = match apply_lookup_at(
          data,
          lookup_offset,
          glyphs,
          clusters,
          target,
          gdef,
          masks=masks,
          lookup_mask=lookup_mask,
        ) {
          Err(err) => return Err(err)
          Ok(value) => value
        }
        if did_change {
          changed = true
        }
        if delta != 0 {
          let updated = collect_forward_indices(
            glyphs[:],
            indices[0],
            glyph_count,
            input_lookup_flag,
            gdef,
            input_mark_filtering_set,
            masks=masks,
            lookup_mask=lookup_mask,
          )
          match updated {
            Err(err) => return Err(err)
            Ok(None) => return Ok(changed)
            Ok(Some(values)) => indices = values
          }
        }
      }
    }
  }
  Ok(changed)
}

///|
fn apply_context_rule(
  data : BytesView,
  offset : Int,
  glyphs : Array[UInt],
  clusters : Array[Int],
  index : Int,
  lookup_list_offsets : Array[Int],
  lookup_flag : Int,
  mark_filtering_set : Int?,
  gdef : GdefTable?,
  masks? : Array[UInt] = [],
  lookup_mask? : UInt = 0xFFFFFFFFU,
) -> Result[ContextApplyResult, OtLayoutError] {
  if offset < 0 || offset + 4 > data.length() {
    return Err(UnexpectedEof)
  }
  let glyph_count = read_u16_int(data, offset)
  let subst_count = read_u16_int(data, offset + 2)
  match (glyph_count, subst_count) {
    (Err(err), _) => Err(err)
    (_, Err(err)) => Err(err)
    (Ok(glyph_count), Ok(subst_count)) => {
      if glyph_count <= 0 || subst_count < 0 {
        return Err(InvalidFormat)
      }
      let input_indices = collect_forward_indices(
        glyphs[:],
        index,
        glyph_count,
        lookup_flag,
        gdef,
        mark_filtering_set,
        masks=masks,
        lookup_mask=lookup_mask,
      )
      let indices = match input_indices {
        Err(err) => return Err(err)
        Ok(None) => return Ok(context_no_match(index))
        Ok(Some(indices)) => indices
      }
      let input_base = offset + 4
      if input_base < 0 || input_base + (glyph_count - 1) * 2 > data.length() {
        return Err(UnexpectedEof)
      }
      for i in 0..<(glyph_count - 1) {
        let expected = read_u16_int(data, input_base + i * 2)
        match expected {
          Err(err) => return Err(err)
          Ok(expected) => {
            let actual = glyphs[indices[i + 1]]
            if expected != u16_to_int(actual) {
              return Ok(context_no_match(index))
            }
          }
        }
      }
      let record_base = input_base + (glyph_count - 1) * 2
      let len_before = glyphs.length()
      let changed = match apply_subst_lookup_records(
        data,
        record_base,
        subst_count,
        glyph_count,
        glyphs,
        clusters,
        index,
        lookup_list_offsets,
        Some(indices),
        lookup_flag,
        mark_filtering_set,
        gdef,
        masks=masks,
        lookup_mask=lookup_mask,
      ) {
        Err(err) => return Err(err)
        Ok(value) => value
      }
      let delta = glyphs.length() - len_before
      let mut next_index = indices[indices.length() - 1] + 1 + delta
      if next_index <= index {
        next_index = index + 1
      }
      if next_index > glyphs.length() {
        next_index = glyphs.length()
      }
      Ok(ContextApplyResult::{ matched: true, changed, next_index })
    }
  }
}

///|
fn apply_lookup_at(
  data : BytesView,
  lookup_offset : Int,
  glyphs : Array[UInt],
  clusters : Array[Int],
  index : Int,
  gdef : GdefTable?,
  masks? : Array[UInt] = [],
  lookup_mask? : UInt = 0xFFFFFFFFU,
) -> Result[(Bool, Int), OtLayoutError] {
  let parsed = parse_lookup(data, lookup_offset, 7)
  match parsed {
    Err(err) => Err(err)
    Ok((lookup_type, lookup_flag, mark_filtering_set, subtable_offsets)) => {
      if lookup_type == 1 {
        let changed = match apply_single_at(
          data,
          subtable_offsets,
          glyphs,
          index,
          lookup_flag,
          mark_filtering_set,
          gdef,
          masks=masks,
          lookup_mask=lookup_mask,
        ) {
          Err(err) => return Err(err)
          Ok(value) => value
        }
        Ok((changed, 0))
      } else if lookup_type == 2 {
        apply_multiple_at(
          data,
          subtable_offsets,
          glyphs,
          clusters,
          index,
          lookup_flag,
          mark_filtering_set,
          gdef,
          masks=masks,
          lookup_mask=lookup_mask,
        )
      } else if lookup_type == 3 {
        let changed = match apply_alternate_at(
          data,
          subtable_offsets,
          glyphs,
          index,
          lookup_flag,
          mark_filtering_set,
          gdef,
          masks=masks,
          lookup_mask=lookup_mask,
        ) {
          Err(err) => return Err(err)
          Ok(value) => value
        }
        Ok((changed, 0))
      } else if lookup_type == 4 {
        apply_ligature_at(
          data,
          subtable_offsets,
          glyphs,
          clusters,
          index,
          lookup_flag,
          mark_filtering_set,
          gdef,
          masks=masks,
          lookup_mask=lookup_mask,
        )
      } else {
        Ok((false, 0))
      }
    }
  }
}

///|
fn apply_chain_subtable_list(
  data : BytesView,
  offsets : Array[Int],
  glyphs : Array[UInt],
  clusters : Array[Int],
  lookup_list_offsets : Array[Int],
  lookup_flag : Int,
  mark_filtering_set : Int?,
  gdef : GdefTable?,
  masks? : Array[UInt] = [],
  lookup_mask? : UInt = 0xFFFFFFFFU,
) -> Result[Bool, OtLayoutError] {
  let mut changed = false
  for offset in offsets {
    let result = apply_chain_subtable(
      data,
      offset,
      glyphs,
      clusters,
      lookup_list_offsets,
      lookup_flag,
      mark_filtering_set,
      gdef,
      masks=masks,
      lookup_mask=lookup_mask,
    )
    match result {
      Err(err) => return Err(err)
      Ok(value) => if value { changed = true }
    }
  }
  Ok(changed)
}

///|
fn apply_chain_subtable(
  data : BytesView,
  offset : Int,
  glyphs : Array[UInt],
  clusters : Array[Int],
  lookup_list_offsets : Array[Int],
  lookup_flag : Int,
  mark_filtering_set : Int?,
  gdef : GdefTable?,
  masks? : Array[UInt] = [],
  lookup_mask? : UInt = 0xFFFFFFFFU,
) -> Result[Bool, OtLayoutError] {
  if offset < 0 || offset + 2 > data.length() {
    return Err(UnexpectedEof)
  }
  let format = read_u16_int(data, offset)
  match format {
    Err(err) => Err(err)
    Ok(format) => if format == 1 {
      apply_chain_subtable_format1(
        data,
        offset,
        glyphs,
        clusters,
      lookup_list_offsets,
      lookup_flag,
      mark_filtering_set,
      gdef,
      masks=masks,
      lookup_mask=lookup_mask,
    )
  } else if format == 2 {
    apply_chain_subtable_format2(
      data,
      offset,
      glyphs,
      clusters,
      lookup_list_offsets,
      lookup_flag,
      mark_filtering_set,
      gdef,
      masks=masks,
      lookup_mask=lookup_mask,
    )
  } else if format == 3 {
    apply_chain_subtable_format3(
      data,
      offset,
      glyphs,
      clusters,
      lookup_list_offsets,
      lookup_flag,
      mark_filtering_set,
      gdef,
      masks=masks,
      lookup_mask=lookup_mask,
    )
    } else {
      Err(InvalidFormat)
    }
  }
}

///|
fn apply_chain_subtable_format1(
  data : BytesView,
  offset : Int,
  glyphs : Array[UInt],
  clusters : Array[Int],
  lookup_list_offsets : Array[Int],
  lookup_flag : Int,
  mark_filtering_set : Int?,
  gdef : GdefTable?,
  masks? : Array[UInt] = [],
  lookup_mask? : UInt = 0xFFFFFFFFU,
) -> Result[Bool, OtLayoutError] {
  if offset < 0 || offset + 6 > data.length() {
    return Err(UnexpectedEof)
  }
  let coverage_offset = read_u16_int(data, offset + 2)
  let set_count = read_u16_int(data, offset + 4)
  match (coverage_offset, set_count) {
    (Err(err), _) => Err(err)
    (_, Err(err)) => Err(err)
    (Ok(coverage_offset), Ok(set_count)) => {
      if coverage_offset == 0 || set_count <= 0 {
        return Err(InvalidFormat)
      }
      let coverage_base = offset + coverage_offset
      let set_base = offset + 6
      if set_base < 0 || set_base + set_count * 2 > data.length() {
        return Err(UnexpectedEof)
      }
      let mut changed = false
      let mut i = 0
      while i < glyphs.length() {
        let glyph = glyphs[i]
        let ignored = should_ignore_glyph(
          glyph,
          lookup_flag,
          gdef,
          mark_filtering_set,
          masks=masks,
          index=i,
          lookup_mask=lookup_mask,
        )
        match ignored {
          Err(err) => return Err(err)
          Ok(true) => {
            i = i + 1
            continue
          }
          Ok(false) => ()
        }
        let index = coverage_index(data, coverage_base, glyph)
        match index {
          Err(err) => return Err(err)
          Ok(None) => ()
          Ok(Some(index)) => {
            if index < 0 || index >= set_count {
              return Err(InvalidFormat)
            }
            let rule_set_offset = read_u16_int(data, set_base + index * 2)
            match rule_set_offset {
              Err(err) => return Err(err)
              Ok(rule_set_offset) => {
                if rule_set_offset == 0 {
                  return Err(InvalidFormat)
                }
                let rule_set_base = offset + rule_set_offset
                if rule_set_base < 0 || rule_set_base + 2 > data.length() {
                  return Err(UnexpectedEof)
                }
                let rule_count = read_u16_int(data, rule_set_base)
                match rule_count {
                  Err(err) => return Err(err)
                  Ok(rule_count) => {
                    if rule_count <= 0 {
                      return Err(InvalidFormat)
                    }
                    let rule_list_base = rule_set_base + 2
                    if rule_list_base < 0 ||
                        rule_list_base + rule_count * 2 > data.length() {
                      return Err(UnexpectedEof)
                    }
                    for r in 0.. return Err(err)
                        Ok(rule_offset) => {
                          if rule_offset == 0 {
                            return Err(InvalidFormat)
                          }
                          let rule_base = rule_set_base + rule_offset
                          let applied = match apply_chain_rule(
                            data,
                            rule_base,
                            glyphs,
                            clusters,
                            i,
                            lookup_list_offsets,
                            lookup_flag,
                            mark_filtering_set,
                            gdef,
                            masks=masks,
                            lookup_mask=lookup_mask,
                          ) {
                            Err(err) => return Err(err)
                            Ok(value) => value
                          }
                          if applied {
                            changed = true
                            break
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
        i = i + 1
      }
      Ok(changed)
    }
  }
}

///|
fn apply_chain_subtable_format2(
  data : BytesView,
  offset : Int,
  glyphs : Array[UInt],
  clusters : Array[Int],
  lookup_list_offsets : Array[Int],
  lookup_flag : Int,
  mark_filtering_set : Int?,
  gdef : GdefTable?,
  masks? : Array[UInt] = [],
  lookup_mask? : UInt = 0xFFFFFFFFU,
) -> Result[Bool, OtLayoutError] {
  if offset < 0 || offset + 12 > data.length() {
    return Err(UnexpectedEof)
  }
  let coverage_offset = read_u16_int(data, offset + 2)
  let backtrack_class_def_offset = read_u16_int(data, offset + 4)
  let input_class_def_offset = read_u16_int(data, offset + 6)
  let lookahead_class_def_offset = read_u16_int(data, offset + 8)
  let class_set_count = read_u16_int(data, offset + 10)
  match (
    coverage_offset,
    backtrack_class_def_offset,
    input_class_def_offset,
    lookahead_class_def_offset,
    class_set_count,
  ) {
    (Err(err), _, _, _, _) => Err(err)
    (_, Err(err), _, _, _) => Err(err)
    (_, _, Err(err), _, _) => Err(err)
    (_, _, _, Err(err), _) => Err(err)
    (_, _, _, _, Err(err)) => Err(err)
    (
      Ok(coverage_offset),
      Ok(backtrack_class_def_offset),
      Ok(input_class_def_offset),
      Ok(lookahead_class_def_offset),
      Ok(class_set_count),
    ) => {
      if coverage_offset == 0 || backtrack_class_def_offset == 0 ||
          input_class_def_offset == 0 || lookahead_class_def_offset == 0 {
        return Ok(false)
      }
      if class_set_count <= 0 {
        return Ok(false)
      }
      let coverage_base = offset + coverage_offset
      let backtrack_class_def_base = offset + backtrack_class_def_offset
      let input_class_def_base = offset + input_class_def_offset
      let lookahead_class_def_base = offset + lookahead_class_def_offset
      let class_set_base = offset + 12
      if class_set_base < 0 ||
          class_set_base + class_set_count * 2 > data.length() {
        return Err(UnexpectedEof)
      }
      let mut changed = false
      let mut i = 0
      while i < glyphs.length() {
        let glyph = glyphs[i]
        let ignored = should_ignore_glyph(
          glyph,
          lookup_flag,
          gdef,
          mark_filtering_set,
          masks=masks,
          index=i,
          lookup_mask=lookup_mask,
        )
        match ignored {
          Err(err) => return Err(err)
          Ok(true) => {
            i = i + 1
            continue
          }
          Ok(false) => ()
        }
        let covered = coverage_index(data, coverage_base, glyph)
        match covered {
          Err(err) => return Err(err)
          Ok(None) => ()
          Ok(Some(_)) => {
            let class_value = class_def_value(data, input_class_def_base, glyph)
            match class_value {
              Err(err) => return Err(err)
              Ok(class_value) => {
                if class_value < 0 || class_value >= class_set_count {
                  i = i + 1
                  continue
                }
                let class_set_offset = read_u16_int(
                  data,
                  class_set_base + class_value * 2,
                )
                match class_set_offset {
                  Err(err) => return Err(err)
                  Ok(class_set_offset) => {
                    if class_set_offset == 0 {
                      i = i + 1
                      continue
                    }
                    let rule_set_base = offset + class_set_offset
                    if rule_set_base < 0 || rule_set_base + 2 > data.length() {
                      return Err(UnexpectedEof)
                    }
                    let rule_count = read_u16_int(data, rule_set_base)
                    match rule_count {
                      Err(err) => return Err(err)
                      Ok(rule_count) => {
                        if rule_count <= 0 {
                          i = i + 1
                          continue
                        }
                        let rule_list_base = rule_set_base + 2
                        if rule_list_base < 0 ||
                            rule_list_base + rule_count * 2 > data.length() {
                          return Err(UnexpectedEof)
                        }
                        for r in 0.. return Err(err)
                            Ok(rule_offset) => {
                              if rule_offset == 0 {
                                continue
                              }
                              let rule_base = rule_set_base + rule_offset
                              let applied = match apply_chain_class_rule(
                                data,
                                rule_base,
                                glyphs,
                                clusters,
                                i,
                                backtrack_class_def_base,
                                input_class_def_base,
                                lookahead_class_def_base,
                                lookup_list_offsets,
                                lookup_flag,
                                mark_filtering_set,
                                gdef,
                                masks=masks,
                                lookup_mask=lookup_mask,
                              ) {
                                Err(InvalidFormat) => false
                                Err(err) => return Err(err)
                                Ok(value) => value
                              }
                              if applied {
                                changed = true
                                break
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
        i = i + 1
      }
      Ok(changed)
    }
  }
}

///|
fn apply_chain_subtable_format3(
  data : BytesView,
  offset : Int,
  glyphs : Array[UInt],
  clusters : Array[Int],
  lookup_list_offsets : Array[Int],
  lookup_flag : Int,
  mark_filtering_set : Int?,
  gdef : GdefTable?,
  masks? : Array[UInt] = [],
  lookup_mask? : UInt = 0xFFFFFFFFU,
) -> Result[Bool, OtLayoutError] {
  if offset < 0 || offset + 4 > data.length() {
    return Err(UnexpectedEof)
  }
  let backtrack_count = read_u16_int(data, offset + 2)
  match backtrack_count {
    Err(err) => Err(err)
    Ok(backtrack_count) => {
      if backtrack_count < 0 {
        return Err(InvalidFormat)
      }
      let mut cursor = offset + 4
      if cursor < 0 || cursor + backtrack_count * 2 > data.length() {
        return Err(UnexpectedEof)
      }
      let backtrack_bases : Array[Int] = []
      for i in 0.. return Err(err)
          Ok(cov_offset) => {
            if cov_offset == 0 {
              return Err(InvalidFormat)
            }
            let base = offset + cov_offset
            if base < 0 || base >= data.length() {
              return Err(UnexpectedEof)
            }
            backtrack_bases.push(base)
          }
        }
      }
      cursor = cursor + backtrack_count * 2
      if cursor < 0 || cursor + 2 > data.length() {
        return Err(UnexpectedEof)
      }
      let input_count = read_u16_int(data, cursor)
      match input_count {
        Err(err) => Err(err)
        Ok(input_count) => {
          cursor = cursor + 2
          if input_count <= 0 {
            return Err(InvalidFormat)
          }
          if cursor < 0 || cursor + input_count * 2 > data.length() {
            return Err(UnexpectedEof)
          }
          let input_bases : Array[Int] = []
          for i in 0.. return Err(err)
              Ok(cov_offset) => {
                if cov_offset == 0 {
                  return Err(InvalidFormat)
                }
                let base = offset + cov_offset
                if base < 0 || base >= data.length() {
                  return Err(UnexpectedEof)
                }
                input_bases.push(base)
              }
            }
          }
          cursor = cursor + input_count * 2
          if cursor < 0 || cursor + 2 > data.length() {
            return Err(UnexpectedEof)
          }
          let lookahead_count = read_u16_int(data, cursor)
          match lookahead_count {
            Err(err) => Err(err)
            Ok(lookahead_count) => {
              cursor = cursor + 2
              if lookahead_count < 0 {
                return Err(InvalidFormat)
              }
              if cursor < 0 || cursor + lookahead_count * 2 > data.length() {
                return Err(UnexpectedEof)
              }
              let lookahead_bases : Array[Int] = []
              for i in 0.. return Err(err)
                  Ok(cov_offset) => {
                    if cov_offset == 0 {
                      return Err(InvalidFormat)
                    }
                    let base = offset + cov_offset
                    if base < 0 || base >= data.length() {
                      return Err(UnexpectedEof)
                    }
                    lookahead_bases.push(base)
                  }
                }
              }
              cursor = cursor + lookahead_count * 2
              if cursor < 0 || cursor + 2 > data.length() {
                return Err(UnexpectedEof)
              }
              let subst_count = read_u16_int(data, cursor)
              match subst_count {
                Err(err) => Err(err)
                Ok(subst_count) => {
                  cursor = cursor + 2
                  if subst_count < 0 {
                    return Err(InvalidFormat)
                  }
                  let record_base = cursor
                  let mut changed = false
                  let mut i = 0
                  while i < glyphs.length() {
                    let input_indices = collect_forward_indices(
                      glyphs[:],
                      i,
                      input_count,
                      lookup_flag,
                      gdef,
                      mark_filtering_set,
                      masks=masks,
                      lookup_mask=lookup_mask,
                    )
                    let indices = match input_indices {
                      Err(err) => return Err(err)
                      Ok(None) => {
                        i = i + 1
                        continue
                      }
                      Ok(Some(indices)) => indices
                    }
                    let backtrack_indices = collect_backward_indices(
                      glyphs[:],
                      indices[0],
                      backtrack_bases.length(),
                      lookup_flag,
                      gdef,
                      mark_filtering_set,
                      masks=masks,
                      lookup_mask=lookup_mask,
                    )
                    let backtrack = match backtrack_indices {
                      Err(err) => return Err(err)
                      Ok(None) => {
                        i = i + 1
                        continue
                      }
                      Ok(Some(values)) => values
                    }
                    let lookahead = if lookahead_bases.is_empty() {
                      Ok(Some([]))
                    } else {
                      collect_forward_indices(
                        glyphs[:],
                        indices[input_count - 1] + 1,
                        lookahead_bases.length(),
                        lookup_flag,
                        gdef,
                        mark_filtering_set,
                        masks=masks,
                        lookup_mask=lookup_mask,
                        allow_ignored_start=true,
                      )
                    }
                    let lookahead = match lookahead {
                      Err(err) => return Err(err)
                      Ok(None) => {
                        i = i + 1
                        continue
                      }
                      Ok(Some(values)) => values
                    }
                    let mut matched = true
                    for j in 0.. return Err(err)
                        Ok(None) => {
                          matched = false
                          break
                        }
                        Ok(Some(_)) => ()
                      }
                    }
                    if matched {
                      for j in 0.. return Err(err)
                          Ok(None) => {
                            matched = false
                            break
                          }
                          Ok(Some(_)) => ()
                        }
                      }
                    }
                    if matched {
                      for j in 0.. return Err(err)
                          Ok(None) => {
                            matched = false
                            break
                          }
                          Ok(Some(_)) => ()
                        }
                      }
                    }
                    if matched {
                      let applied = match apply_subst_lookup_records(
                        data,
                        record_base,
                        subst_count,
                        input_count,
                        glyphs,
                        clusters,
                        indices[0],
                        lookup_list_offsets,
                        Some(indices),
                        lookup_flag,
                        mark_filtering_set,
                        gdef,
                        masks=masks,
                        lookup_mask=lookup_mask,
                      ) {
                        Err(err) => return Err(err)
                        Ok(value) => value
                      }
                      if applied {
                        changed = true
                      }
                    }
                    i = i + 1
                  }
                  Ok(changed)
                }
              }
            }
          }
        }
      }
    }
  }
}

///|
fn apply_chain_class_rule(
  data : BytesView,
  offset : Int,
  glyphs : Array[UInt],
  clusters : Array[Int],
  index : Int,
  backtrack_class_def_base : Int,
  input_class_def_base : Int,
  lookahead_class_def_base : Int,
  lookup_list_offsets : Array[Int],
  lookup_flag : Int,
  mark_filtering_set : Int?,
  gdef : GdefTable?,
  masks? : Array[UInt] = [],
  lookup_mask? : UInt = 0xFFFFFFFFU,
) -> Result[Bool, OtLayoutError] {
  if offset < 0 || offset + 2 > data.length() {
    return Err(UnexpectedEof)
  }
  let backtrack_count = read_u16_int(data, offset)
  match backtrack_count {
    Err(err) => Err(err)
    Ok(backtrack_count) => {
      if backtrack_count < 0 {
        return Err(InvalidFormat)
      }
      let mut cursor = offset + 2
      if cursor < 0 || cursor + backtrack_count * 2 > data.length() {
        return Err(UnexpectedEof)
      }
      let backtrack : Array[Int] = []
      for _ in 0.. return Err(err)
          Ok(value) => backtrack.push(value)
        }
      }
      if cursor < 0 || cursor + 2 > data.length() {
        return Err(UnexpectedEof)
      }
      let input_count = read_u16_int(data, cursor)
      match input_count {
        Err(err) => Err(err)
        Ok(input_count) => {
          cursor = cursor + 2
          if input_count <= 0 {
            return Err(InvalidFormat)
          }
          if index + input_count > glyphs.length() {
            return Ok(false)
          }
          if cursor < 0 || cursor + (input_count - 1) * 2 > data.length() {
            return Err(UnexpectedEof)
          }
          let input : Array[Int] = []
          for _ in 0..<(input_count - 1) {
            let value = read_u16_int(data, cursor)
            cursor = cursor + 2
            match value {
              Err(err) => return Err(err)
              Ok(value) => input.push(value)
            }
          }
          if cursor < 0 || cursor + 2 > data.length() {
            return Err(UnexpectedEof)
          }
          let lookahead_count = read_u16_int(data, cursor)
          match lookahead_count {
            Err(err) => Err(err)
            Ok(lookahead_count) => {
              cursor = cursor + 2
              if lookahead_count < 0 {
                return Err(InvalidFormat)
              }
              if cursor < 0 || cursor + lookahead_count * 2 > data.length() {
                return Err(UnexpectedEof)
              }
              let lookahead : Array[Int] = []
              for _ in 0.. return Err(err)
                  Ok(value) => lookahead.push(value)
                }
              }
              if cursor < 0 || cursor + 2 > data.length() {
                return Err(UnexpectedEof)
              }
              let subst_count = read_u16_int(data, cursor)
              match subst_count {
                Err(err) => Err(err)
                Ok(subst_count) => {
                  cursor = cursor + 2
                  if subst_count < 0 {
                    return Err(InvalidFormat)
                  }
                  let input_indices = collect_forward_indices(
                    glyphs[:],
                    index,
                    input_count,
                    lookup_flag,
                    gdef,
                    mark_filtering_set,
                    masks=masks,
                    lookup_mask=lookup_mask,
                  )
                  let indices = match input_indices {
                    Err(err) => return Err(err)
                    Ok(None) => return Ok(false)
                    Ok(Some(indices)) => indices
                  }
                  let backtrack_indices = collect_backward_indices(
                    glyphs[:],
                    indices[0],
                    backtrack.length(),
                    lookup_flag,
                    gdef,
                    mark_filtering_set,
                    masks=masks,
                    lookup_mask=lookup_mask,
                  )
                  let backtrack_indices = match backtrack_indices {
                    Err(err) => return Err(err)
                    Ok(None) => return Ok(false)
                    Ok(Some(values)) => values
                  }
                  let lookahead_indices = if lookahead.is_empty() {
                    Ok(Some([]))
                  } else {
                    collect_forward_indices(
                      glyphs[:],
                      indices[input_count - 1] + 1,
                      lookahead.length(),
                      lookup_flag,
                      gdef,
                      mark_filtering_set,
                      masks=masks,
                      lookup_mask=lookup_mask,
                      allow_ignored_start=true,
                    )
                  }
                  let lookahead_indices = match lookahead_indices {
                    Err(err) => return Err(err)
                    Ok(None) => return Ok(false)
                    Ok(Some(values)) => values
                  }
                  for j in 0.. return Err(err)
                      Ok(actual) => if actual != expected { return Ok(false) }
                    }
                  }
                  for j in 0.. return Err(err)
                      Ok(actual) => if actual != expected { return Ok(false) }
                    }
                  }
                  for j in 0.. return Err(err)
                      Ok(actual) => if actual != expected { return Ok(false) }
                    }
                  }
                  apply_subst_lookup_records(
                    data,
                    cursor,
                    subst_count,
                    input_count,
                    glyphs,
                    clusters,
                    indices[0],
                    lookup_list_offsets,
                    Some(indices),
                    lookup_flag,
                    mark_filtering_set,
                    gdef,
                    masks=masks,
                    lookup_mask=lookup_mask,
                  )
                }
              }
            }
          }
        }
      }
    }
  }
}

///|
fn apply_chain_rule(
  data : BytesView,
  offset : Int,
  glyphs : Array[UInt],
  clusters : Array[Int],
  index : Int,
  lookup_list_offsets : Array[Int],
  lookup_flag : Int,
  mark_filtering_set : Int?,
  gdef : GdefTable?,
  masks? : Array[UInt] = [],
  lookup_mask? : UInt = 0xFFFFFFFFU,
) -> Result[Bool, OtLayoutError] {
  if offset < 0 || offset + 2 > data.length() {
    return Err(UnexpectedEof)
  }
  let backtrack_count = read_u16_int(data, offset)
  match backtrack_count {
    Err(err) => Err(err)
    Ok(backtrack_count) => {
      if backtrack_count < 0 {
        return Err(InvalidFormat)
      }
      let mut cursor = offset + 2
      if cursor < 0 || cursor + backtrack_count * 2 > data.length() {
        return Err(UnexpectedEof)
      }
      let backtrack : Array[Int] = []
      for _ in 0.. return Err(err)
          Ok(value) => backtrack.push(value)
        }
      }
      if cursor < 0 || cursor + 2 > data.length() {
        return Err(UnexpectedEof)
      }
      let input_count = read_u16_int(data, cursor)
      match input_count {
        Err(err) => Err(err)
        Ok(input_count) => {
          cursor = cursor + 2
          if input_count <= 0 {
            return Err(InvalidFormat)
          }
          if index + input_count > glyphs.length() {
            return Ok(false)
          }
          if cursor < 0 || cursor + (input_count - 1) * 2 > data.length() {
            return Err(UnexpectedEof)
          }
          let input : Array[Int] = []
          for _ in 0..<(input_count - 1) {
            let value = read_u16_int(data, cursor)
            cursor = cursor + 2
            match value {
              Err(err) => return Err(err)
              Ok(value) => input.push(value)
            }
          }
          if cursor < 0 || cursor + 2 > data.length() {
            return Err(UnexpectedEof)
          }
          let lookahead_count = read_u16_int(data, cursor)
          match lookahead_count {
            Err(err) => Err(err)
            Ok(lookahead_count) => {
              cursor = cursor + 2
              if lookahead_count < 0 {
                return Err(InvalidFormat)
              }
              if cursor < 0 || cursor + lookahead_count * 2 > data.length() {
                return Err(UnexpectedEof)
              }
              let lookahead : Array[Int] = []
              for _ in 0.. return Err(err)
                  Ok(value) => lookahead.push(value)
                }
              }
              if cursor < 0 || cursor + 2 > data.length() {
                return Err(UnexpectedEof)
              }
              let subst_count = read_u16_int(data, cursor)
              match subst_count {
                Err(err) => Err(err)
                Ok(subst_count) => {
                  cursor = cursor + 2
                  if subst_count < 0 {
                    return Err(InvalidFormat)
                  }
                  if cursor < 0 || cursor + subst_count * 4 > data.length() {
                    return Err(UnexpectedEof)
                  }
                  let input_indices = collect_forward_indices(
                    glyphs[:],
                    index,
                    input_count,
                    lookup_flag,
                    gdef,
                    mark_filtering_set,
                    masks=masks,
                    lookup_mask=lookup_mask,
                  )
                  let indices = match input_indices {
                    Err(err) => return Err(err)
                    Ok(None) => return Ok(false)
                    Ok(Some(indices)) => indices
                  }
                  let backtrack_indices = collect_backward_indices(
                    glyphs[:],
                    indices[0],
                    backtrack.length(),
                    lookup_flag,
                    gdef,
                    mark_filtering_set,
                    masks=masks,
                    lookup_mask=lookup_mask,
                  )
                  let backtrack_indices = match backtrack_indices {
                    Err(err) => return Err(err)
                    Ok(None) => return Ok(false)
                    Ok(Some(values)) => values
                  }
                  let lookahead_indices = if lookahead.is_empty() {
                    Ok(Some([]))
                  } else {
                    collect_forward_indices(
                      glyphs[:],
                      indices[input_count - 1] + 1,
                      lookahead.length(),
                      lookup_flag,
                      gdef,
                      mark_filtering_set,
                      masks=masks,
                      lookup_mask=lookup_mask,
                      allow_ignored_start=true,
                    )
                  }
                  let lookahead_indices = match lookahead_indices {
                    Err(err) => return Err(err)
                    Ok(None) => return Ok(false)
                    Ok(Some(values)) => values
                  }
                  for j in 0.. Result[Bool, OtLayoutError] {
  if index < 0 || index >= glyphs.length() {
    return Err(InvalidFormat)
  }
  let ignored = should_ignore_glyph(
    glyphs[index],
    lookup_flag,
    gdef,
    mark_filtering_set,
    masks=masks,
    index=index,
    lookup_mask=lookup_mask,
  )
  match ignored {
    Err(err) => return Err(err)
    Ok(true) => return Ok(false)
    Ok(false) => ()
  }
  let mut changed = false
  let mut glyph = glyphs[index]
  for offset in offsets {
    let format = read_u16_int(data, offset)
    match format {
      Err(err) => return Err(err)
      Ok(format) => {
        let next = if format == 1 {
          single_substitute_format1(data, offset, glyph)
        } else if format == 2 {
          single_substitute_format2(data, offset, glyph)
        } else {
          Err(InvalidFormat)
        }
        match next {
          Err(err) => return Err(err)
          Ok(None) => ()
          Ok(Some(value)) => {
            if value != glyph {
              glyph = value
              glyphs[index] = value
              changed = true
            }
          }
        }
      }
    }
  }
  Ok(changed)
}

///|
fn apply_alternate_at(
  data : BytesView,
  offsets : Array[Int],
  glyphs : Array[UInt],
  index : Int,
  lookup_flag : Int,
  mark_filtering_set : Int?,
  gdef : GdefTable?,
  masks? : Array[UInt] = [],
  lookup_mask? : UInt = 0xFFFFFFFFU,
) -> Result[Bool, OtLayoutError] {
  if index < 0 || index >= glyphs.length() {
    return Err(InvalidFormat)
  }
  let ignored = should_ignore_glyph(
    glyphs[index],
    lookup_flag,
    gdef,
    mark_filtering_set,
    masks=masks,
    index=index,
    lookup_mask=lookup_mask,
  )
  match ignored {
    Err(err) => return Err(err)
    Ok(true) => return Ok(false)
    Ok(false) => ()
  }
  let mut changed = false
  let mut glyph = glyphs[index]
  for offset in offsets {
    let next = alternate_substitute_format1(data, offset, glyph)
    match next {
      Err(err) => return Err(err)
      Ok(None) => ()
      Ok(Some(value)) => {
        if value != glyph {
          glyph = value
          glyphs[index] = value
          changed = true
        }
      }
    }
  }
  Ok(changed)
}

fn apply_multiple_at(
  data : BytesView,
  offsets : Array[Int],
  glyphs : Array[UInt],
  clusters : Array[Int],
  index : Int,
  lookup_flag : Int,
  mark_filtering_set : Int?,
  gdef : GdefTable?,
  masks? : Array[UInt] = [],
  lookup_mask? : UInt = 0xFFFFFFFFU,
) -> Result[(Bool, Int), OtLayoutError] {
  if index < 0 || index >= glyphs.length() {
    return Err(InvalidFormat)
  }
  if clusters.length() != glyphs.length() {
    return Err(InvalidFormat)
  }
  let has_masks = !masks.is_empty()
  if has_masks && masks.length() != glyphs.length() {
    return Err(InvalidFormat)
  }
  let ignored = should_ignore_glyph(
    glyphs[index],
    lookup_flag,
    gdef,
    mark_filtering_set,
    masks=masks,
    index=index,
    lookup_mask=lookup_mask,
  )
  match ignored {
    Err(err) => return Err(err)
    Ok(true) => return Ok((false, 0))
    Ok(false) => ()
  }
  let glyph = glyphs[index]
  let result = find_multiple_substitution(data, offsets, glyph)
  match result {
    Err(err) => Err(err)
    Ok(None) => Ok((false, 0))
    Ok(Some(sequence)) => {
      if sequence.is_empty() {
        return Err(InvalidFormat)
      }
      if sequence.length() == 1 {
        let value = sequence[0]
        if value != glyph {
          glyphs[index] = value
          return Ok((true, 0))
        }
        return Ok((false, 0))
      }
      let cluster = clusters[index]
      let mask_value = if has_masks { masks[index] } else { 0U }
      glyphs[index] = sequence[0]
      for i in 1.. Result[(Bool, Int), OtLayoutError] {
  if index < 0 || index >= glyphs.length() {
    return Err(InvalidFormat)
  }
  if clusters.length() != glyphs.length() {
    return Err(InvalidFormat)
  }
  let has_masks = !masks.is_empty()
  if has_masks && masks.length() != glyphs.length() {
    return Err(InvalidFormat)
  }
  let ignored = should_ignore_glyph(
    glyphs[index],
    lookup_flag,
    gdef,
    mark_filtering_set,
    masks=masks,
    index=index,
    lookup_mask=lookup_mask,
  )
  match ignored {
    Err(err) => return Err(err)
    Ok(true) => return Ok((false, 0))
    Ok(false) => ()
  }
  let result = find_ligature_match(
    data,
    offsets,
    glyphs,
    index,
    lookup_flag,
    mark_filtering_set,
    gdef,
    masks=masks,
    lookup_mask=lookup_mask,
  )
  match result {
    Err(err) => Err(err)
    Ok(None) => Ok((false, 0))
    Ok(Some((lig_glyph, indices))) => {
      if indices.length() < 2 {
        return Err(InvalidFormat)
      }
      let first = indices[0]
      if first < 0 || first >= glyphs.length() {
        return Err(InvalidFormat)
      }
      glyphs[first] = lig_glyph
      if has_masks {
        let mut lig_mask = 0U
        for idx in indices {
          lig_mask = lig_mask | masks[idx]
        }
        masks[first] = lig_mask
      }
      let mut j = indices.length() - 1
      while j > 0 {
        let remove_index = indices[j]
        if remove_index < 0 || remove_index >= glyphs.length() {
          return Err(InvalidFormat)
        }
        ignore(glyphs.remove(remove_index))
        ignore(clusters.remove(remove_index))
        if has_masks {
          ignore(masks.remove(remove_index))
        }
        j = j - 1
      }
      Ok((true, 1 - indices.length()))
    }
  }
}

fn apply_reverse_chain_subtable_list(
  data : BytesView,
  offsets : Array[Int],
  glyphs : Array[UInt],
  lookup_flag : Int,
  mark_filtering_set : Int?,
  gdef : GdefTable?,
  masks? : Array[UInt] = [],
  lookup_mask? : UInt = 0xFFFFFFFFU,
) -> Result[Bool, OtLayoutError] {
  let mut changed = false
  for offset in offsets {
    let result = apply_reverse_chain_subtable(
      data,
      offset,
      glyphs,
      lookup_flag,
      mark_filtering_set,
      gdef,
      masks=masks,
      lookup_mask=lookup_mask,
    )
    match result {
      Err(err) => return Err(err)
      Ok(value) => if value { changed = true }
    }
  }
  Ok(changed)
}

///|
fn apply_reverse_chain_subtable(
  data : BytesView,
  offset : Int,
  glyphs : Array[UInt],
  lookup_flag : Int,
  mark_filtering_set : Int?,
  gdef : GdefTable?,
  masks? : Array[UInt] = [],
  lookup_mask? : UInt = 0xFFFFFFFFU,
) -> Result[Bool, OtLayoutError] {
  if glyphs.is_empty() {
    return Ok(false)
  }
  if offset < 0 || offset + 10 > data.length() {
    return Err(UnexpectedEof)
  }
  let format = read_u16_int(data, offset)
  let coverage_offset = read_u16_int(data, offset + 2)
  let backtrack_count = read_u16_int(data, offset + 4)
  match (format, coverage_offset, backtrack_count) {
    (Err(err), _, _) => Err(err)
    (_, Err(err), _) => Err(err)
    (_, _, Err(err)) => Err(err)
    (Ok(format), Ok(coverage_offset), Ok(backtrack_count)) => {
      if format != 1 {
        return Err(InvalidFormat)
      }
      if coverage_offset == 0 || backtrack_count < 0 {
        return Err(InvalidFormat)
      }
      let mut cursor = offset + 6
      let backtrack_bases : Array[Int] = []
      if cursor < 0 || cursor + backtrack_count * 2 > data.length() {
        return Err(UnexpectedEof)
      }
      for _ in 0.. return Err(err)
          Ok(sub_offset) => {
            if sub_offset == 0 {
              return Err(InvalidFormat)
            }
            let base = offset + sub_offset
            if base < 0 || base >= data.length() {
              return Err(UnexpectedEof)
            }
            backtrack_bases.push(base)
          }
        }
      }
      if cursor < 0 || cursor + 2 > data.length() {
        return Err(UnexpectedEof)
      }
      let lookahead_count = read_u16_int(data, cursor)
      match lookahead_count {
        Err(err) => return Err(err)
        Ok(lookahead_count) => {
          cursor = cursor + 2
          if lookahead_count < 0 {
            return Err(InvalidFormat)
          }
          if cursor < 0 || cursor + lookahead_count * 2 > data.length() {
            return Err(UnexpectedEof)
          }
          let lookahead_bases : Array[Int] = []
          for _ in 0.. return Err(err)
              Ok(sub_offset) => {
                if sub_offset == 0 {
                  return Err(InvalidFormat)
                }
                let base = offset + sub_offset
                if base < 0 || base >= data.length() {
                  return Err(UnexpectedEof)
                }
                lookahead_bases.push(base)
              }
            }
          }
          if cursor < 0 || cursor + 2 > data.length() {
            return Err(UnexpectedEof)
          }
          let glyph_count = read_u16_int(data, cursor)
          match glyph_count {
            Err(err) => Err(err)
            Ok(glyph_count) => {
              cursor = cursor + 2
              if glyph_count <= 0 {
                return Err(InvalidFormat)
              }
              if cursor < 0 || cursor + glyph_count * 2 > data.length() {
                return Err(UnexpectedEof)
              }
              let substitutes : Array[UInt] = []
              for _ in 0.. return Err(err)
                  Ok(value) => substitutes.push(value.reinterpret_as_uint())
                }
              }
              let coverage_base = offset + coverage_offset
              let original : Array[UInt] = []
              for value in glyphs {
                original.push(value)
              }
              let mut changed = false
              let mut i = glyphs.length() - 1
              while i >= 0 {
                let input_indices = collect_forward_indices(
                  original[:],
                  i,
                  1,
                  lookup_flag,
                  gdef,
                  mark_filtering_set,
                  masks=masks,
                  lookup_mask=lookup_mask,
                )
                let indices = match input_indices {
                  Err(err) => return Err(err)
                  Ok(None) => {
                    if i == 0 {
                      break
                    }
                    i = i - 1
                    continue
                  }
                  Ok(Some(indices)) => indices
                }
                let input_index = indices[0]
                let glyph = original[input_index]
                let index = coverage_index(data, coverage_base, glyph)
                match index {
                  Err(err) => return Err(err)
                  Ok(None) => ()
                  Ok(Some(index)) => {
                    if index < 0 || index >= glyph_count {
                      return Err(InvalidFormat)
                    }
                    let mut match_ok = true
                    if backtrack_count > 0 {
                      let backtrack_indices = collect_backward_indices(
                        original[:],
                        input_index,
                        backtrack_count,
                        lookup_flag,
                        gdef,
                        mark_filtering_set,
                        masks=masks,
                        lookup_mask=lookup_mask,
                      )
                      let backtrack = match backtrack_indices {
                        Err(err) => return Err(err)
                        Ok(None) => {
                          match_ok = false
                          []
                        }
                        Ok(Some(values)) => values
                      }
                      if match_ok {
                        for j in 0.. return Err(err)
                            Ok(None) => {
                              match_ok = false
                              break
                            }
                            Ok(Some(_)) => ()
                          }
                        }
                      }
                    }
                    if match_ok && lookahead_count > 0 {
                      let lookahead_indices = collect_forward_indices(
                        original[:],
                        input_index + 1,
                        lookahead_count,
                        lookup_flag,
                        gdef,
                        mark_filtering_set,
                        masks=masks,
                        lookup_mask=lookup_mask,
                      )
                      let lookahead = match lookahead_indices {
                        Err(err) => return Err(err)
                        Ok(None) => {
                          match_ok = false
                          []
                        }
                        Ok(Some(values)) => values
                      }
                      if match_ok {
                        for j in 0.. return Err(err)
                            Ok(None) => {
                              match_ok = false
                              break
                            }
                            Ok(Some(_)) => ()
                          }
                        }
                      }
                    }
                    if match_ok {
                      let sub = substitutes[index]
                      if sub != glyphs[input_index] {
                        glyphs[input_index] = sub
                        changed = true
                      }
                    }
                  }
                }
                if i == 0 {
                  break
                }
                i = i - 1
              }
              Ok(changed)
            }
          }
        }
      }
    }
  }
}

///|
fn apply_multiple_subtable_list(
  data : BytesView,
  offsets : Array[Int],
  glyphs : Array[UInt],
  clusters : Array[Int],
  lookup_flag : Int,
  mark_filtering_set : Int?,
  gdef : GdefTable?,
  masks? : Array[UInt] = [],
  lookup_mask? : UInt = 0xFFFFFFFFU,
) -> Result[(Array[UInt], Array[Int], Array[UInt], Bool), OtLayoutError] {
  if clusters.length() != glyphs.length() {
    return Err(InvalidFormat)
  }
  let new_glyphs : Array[UInt] = []
  let new_clusters : Array[Int] = []
  let new_masks : Array[UInt] = []
  let has_masks = !masks.is_empty()
  let mut changed = false
  for i in 0.. return Err(err)
      Ok(true) => {
        new_glyphs.push(glyph)
        new_clusters.push(clusters[i])
        if has_masks {
          new_masks.push(masks[i])
        }
        continue
      }
      Ok(false) => ()
    }
    let sub_result = find_multiple_substitution(data, offsets, glyph)
    match sub_result {
      Err(err) => return Err(err)
      Ok(None) => {
        new_glyphs.push(glyph)
        new_clusters.push(clusters[i])
        if has_masks {
          new_masks.push(masks[i])
        }
      }
      Ok(Some(sequence)) => {
        if sequence.is_empty() {
          return Err(InvalidFormat)
        }
        for value in sequence {
          new_glyphs.push(value)
          new_clusters.push(clusters[i])
          if has_masks {
            new_masks.push(masks[i])
          }
        }
        if sequence.length() != 1 || sequence[0] != glyph {
          changed = true
        }
      }
    }
  }
  Ok((new_glyphs, new_clusters, new_masks, changed))
}

///|
fn apply_alternate_subtable_list(
  data : BytesView,
  offsets : Array[Int],
  glyphs : Array[UInt],
  lookup_flag : Int,
  mark_filtering_set : Int?,
  gdef : GdefTable?,
  masks? : Array[UInt] = [],
  lookup_mask? : UInt = 0xFFFFFFFFU,
  alt_values? : Array[Int] = [],
) -> Result[Bool, OtLayoutError] {
  let mut changed = false
  for offset in offsets {
    let result = apply_alternate_subtable(
      data,
      offset,
      glyphs,
      lookup_flag,
      mark_filtering_set,
      gdef,
      masks=masks,
      lookup_mask=lookup_mask,
      alt_values=alt_values,
    )
    match result {
      Err(err) => return Err(err)
      Ok(value) => if value { changed = true }
    }
  }
  Ok(changed)
}

///|
fn apply_alternate_subtable(
  data : BytesView,
  offset : Int,
  glyphs : Array[UInt],
  lookup_flag : Int,
  mark_filtering_set : Int?,
  gdef : GdefTable?,
  masks? : Array[UInt] = [],
  lookup_mask? : UInt = 0xFFFFFFFFU,
  alt_values? : Array[Int] = [],
) -> Result[Bool, OtLayoutError] {
  let mut changed = false
  let has_alt_values = !alt_values.is_empty()
  for i in 0.. return Err(err)
      Ok(true) => continue
      Ok(false) => ()
    }
    let alt_value = if has_alt_values && i < alt_values.length() {
      alt_values[i]
    } else {
      1
    }
    if alt_value <= 0 {
      continue
    }
    let next = alternate_substitute_format1(data, offset, glyph, alt_index=alt_value)
    match next {
      Err(err) => return Err(err)
      Ok(None) => ()
      Ok(Some(value)) => {
        if value != glyph {
          glyphs[i] = value
          changed = true
        }
      }
    }
  }
  Ok(changed)
}

///|
fn alternate_substitute_format1(
  data : BytesView,
  offset : Int,
  glyph : UInt,
  alt_index? : Int = 1,
) -> Result[UInt?, OtLayoutError] {
  if glyph > 0xffffU {
    return Ok(None)
  }
  if offset < 0 || offset + 6 > data.length() {
    return Err(UnexpectedEof)
  }
  let format = read_u16_int(data, offset)
  let coverage_offset = read_u16_int(data, offset + 2)
  let set_count = read_u16_int(data, offset + 4)
  match (format, coverage_offset, set_count) {
    (Err(err), _, _) => Err(err)
    (_, Err(err), _) => Err(err)
    (_, _, Err(err)) => Err(err)
    (Ok(format), Ok(coverage_offset), Ok(set_count)) => {
      if format != 1 {
        return Err(InvalidFormat)
      }
      if coverage_offset == 0 || set_count <= 0 {
        return Err(InvalidFormat)
      }
      let list_base = offset + 6
      if list_base < 0 || list_base + set_count * 2 > data.length() {
        return Err(UnexpectedEof)
      }
      let coverage_base = offset + coverage_offset
      let index = coverage_index(data, coverage_base, glyph)
      match index {
        Err(err) => Err(err)
        Ok(None) => Ok(None)
        Ok(Some(index)) => {
          if index < 0 || index >= set_count {
            return Err(InvalidFormat)
          }
          let set_offset = read_u16_int(data, list_base + index * 2)
          match set_offset {
            Err(err) => Err(err)
            Ok(set_offset) => {
              if set_offset == 0 {
                return Err(InvalidFormat)
              }
              let set_base = offset + set_offset
              if set_base < 0 || set_base + 2 > data.length() {
                return Err(UnexpectedEof)
              }
              let glyph_count = read_u16_int(data, set_base)
              match glyph_count {
                Err(err) => Err(err)
                Ok(glyph_count) => {
                  if glyph_count <= 0 {
                    return Err(InvalidFormat)
                  }
                  let alt_base = set_base + 2
                  if alt_base < 0 || alt_base + glyph_count * 2 > data.length() {
                    return Err(UnexpectedEof)
                  }
                  let chosen =
                    if alt_index <= 1 {
                      1
                    } else if alt_index > glyph_count {
                      glyph_count
                    } else {
                      alt_index
                    }
                  let chosen_offset = alt_base + (chosen - 1) * 2
                  let chosen_value = read_u16_int(data, chosen_offset)
                  match chosen_value {
                    Err(err) => Err(err)
                    Ok(value) => Ok(Some(value.reinterpret_as_uint()))
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

///|
fn find_multiple_substitution(
  data : BytesView,
  offsets : Array[Int],
  glyph : UInt,
) -> Result[Array[UInt]?, OtLayoutError] {
  for offset in offsets {
    let result = multiple_substitute_format1(data, offset, glyph)
    match result {
      Err(err) => return Err(err)
      Ok(Some(value)) => return Ok(Some(value))
      Ok(None) => ()
    }
  }
  Ok(None)
}

///|
fn multiple_substitute_format1(
  data : BytesView,
  offset : Int,
  glyph : UInt,
) -> Result[Array[UInt]?, OtLayoutError] {
  if glyph > 0xffffU {
    return Ok(None)
  }
  if offset < 0 || offset + 6 > data.length() {
    return Err(UnexpectedEof)
  }
  let format = read_u16_int(data, offset)
  let coverage_offset = read_u16_int(data, offset + 2)
  let sequence_count = read_u16_int(data, offset + 4)
  match (format, coverage_offset, sequence_count) {
    (Err(err), _, _) => Err(err)
    (_, Err(err), _) => Err(err)
    (_, _, Err(err)) => Err(err)
    (Ok(format), Ok(coverage_offset), Ok(sequence_count)) => {
      if format != 1 {
        return Err(InvalidFormat)
      }
      if coverage_offset == 0 || sequence_count <= 0 {
        return Err(InvalidFormat)
      }
      let coverage_base = offset + coverage_offset
      let index = coverage_index(data, coverage_base, glyph)
      match index {
        Err(err) => Err(err)
        Ok(None) => Ok(None)
        Ok(Some(index)) => {
          if index < 0 || index >= sequence_count {
            return Err(InvalidFormat)
          }
          let sequence_offset = read_u16_int(data, offset + 6 + index * 2)
          match sequence_offset {
            Err(err) => Err(err)
            Ok(sequence_offset) => {
              if sequence_offset == 0 {
                return Err(InvalidFormat)
              }
              let sequence_base = offset + sequence_offset
              if sequence_base < 0 || sequence_base + 2 > data.length() {
                return Err(UnexpectedEof)
              }
              let glyph_count = read_u16_int(data, sequence_base)
              match glyph_count {
                Err(err) => Err(err)
                Ok(glyph_count) => {
                  if glyph_count <= 0 {
                    return Err(InvalidFormat)
                  }
                  let list_base = sequence_base + 2
                  if list_base < 0 || list_base + glyph_count * 2 > data.length() {
                    return Err(UnexpectedEof)
                  }
                  let sequence : Array[UInt] = []
                  for i in 0.. return Err(err)
                      Ok(value) => sequence.push(value.reinterpret_as_uint())
                    }
                  }
                  Ok(Some(sequence))
                }
              }
            }
          }
        }
      }
    }
  }
}

///|
fn find_ligature_match(
  data : BytesView,
  offsets : Array[Int],
  glyphs : Array[UInt],
  index : Int,
  lookup_flag : Int,
  mark_filtering_set : Int?,
  gdef : GdefTable?,
  masks? : Array[UInt] = [],
  lookup_mask? : UInt = 0xFFFFFFFFU,
) -> Result[(UInt, Array[Int])?, OtLayoutError] {
  for offset in offsets {
    let result = ligature_match_subtable(
      data,
      offset,
      glyphs,
      index,
      lookup_flag,
      mark_filtering_set,
      gdef,
      masks=masks,
      lookup_mask=lookup_mask,
    )
    match result {
      Err(err) => return Err(err)
      Ok(Some(value)) => return Ok(Some(value))
      Ok(None) => ()
    }
  }
  Ok(None)
}

///|
fn ligature_match_subtable(
  data : BytesView,
  offset : Int,
  glyphs : Array[UInt],
  index : Int,
  lookup_flag : Int,
  mark_filtering_set : Int?,
  gdef : GdefTable?,
  masks? : Array[UInt] = [],
  lookup_mask? : UInt = 0xFFFFFFFFU,
) -> Result[(UInt, Array[Int])?, OtLayoutError] {
  if index < 0 || index >= glyphs.length() {
    return Ok(None)
  }
  let first = glyphs[index]
  let ignored = should_ignore_glyph(
    first,
    lookup_flag,
    gdef,
    mark_filtering_set,
    masks=masks,
    index=index,
    lookup_mask=lookup_mask,
  )
  match ignored {
    Err(err) => return Err(err)
    Ok(true) => return Ok(None)
    Ok(false) => ()
  }
  if first > 0xffffU {
    return Ok(None)
  }
  if offset < 0 || offset + 6 > data.length() {
    return Err(UnexpectedEof)
  }
  let format = read_u16_int(data, offset)
  let coverage_offset = read_u16_int(data, offset + 2)
  let lig_set_count = read_u16_int(data, offset + 4)
  match (format, coverage_offset, lig_set_count) {
    (Err(err), _, _) => Err(err)
    (_, Err(err), _) => Err(err)
    (_, _, Err(err)) => Err(err)
    (Ok(format), Ok(coverage_offset), Ok(lig_set_count)) => {
      if format != 1 {
        return Err(InvalidFormat)
      }
      let coverage_base = offset + coverage_offset
      let coverage = coverage_index(data, coverage_base, first)
      match coverage {
        Err(err) => Err(err)
        Ok(None) => Ok(None)
        Ok(Some(coverage_index)) => {
          if coverage_index < 0 || coverage_index >= lig_set_count {
            return Err(InvalidFormat)
          }
          let set_offset = read_u16_int(
            data,
            offset + 6 + coverage_index * 2,
          )
          match set_offset {
            Err(err) => Err(err)
            Ok(set_offset) => {
              if set_offset == 0 {
                return Err(InvalidFormat)
              }
              let set_base = offset + set_offset
              if set_base < 0 || set_base + 2 > data.length() {
                return Err(UnexpectedEof)
              }
              let lig_count = read_u16_int(data, set_base)
              match lig_count {
                Err(err) => Err(err)
                Ok(lig_count) => {
                  let list_base = set_base + 2
                  if list_base < 0 || list_base + lig_count * 2 > data.length() {
                    return Err(UnexpectedEof)
                  }
                  for i in 0.. return Err(err)
                      Ok(lig_offset) => {
                        if lig_offset == 0 {
                          return Err(InvalidFormat)
                        }
                        let lig_base = set_base + lig_offset
                        if lig_base < 0 || lig_base + 4 > data.length() {
                          return Err(UnexpectedEof)
                        }
                        let lig_glyph = read_u16_int(data, lig_base)
                        let comp_count = read_u16_int(data, lig_base + 2)
                        match (lig_glyph, comp_count) {
                          (Err(err), _) => return Err(err)
                          (_, Err(err)) => return Err(err)
                          (Ok(lig_glyph), Ok(comp_count)) => {
                            if comp_count < 2 {
                              return Err(InvalidFormat)
                            }
                            let comp_base = lig_base + 4
                            let comp_len = (comp_count - 1) * 2
                            if comp_base < 0 || comp_base + comp_len > data.length() {
                              return Err(UnexpectedEof)
                            }
                            let input_indices = collect_forward_indices(
                              glyphs[:],
                              index,
                              comp_count,
                              lookup_flag,
                              gdef,
                              mark_filtering_set,
                              masks=masks,
                              lookup_mask=lookup_mask,
                            )
                            let indices = match input_indices {
                              Err(err) => return Err(err)
                              Ok(None) => continue
                              Ok(Some(indices)) => indices
                            }
                            let mut matches = true
                            for j in 0..<(comp_count - 1) {
                              let component = read_u16_int(data, comp_base + j * 2)
                              match component {
                                Err(err) => return Err(err)
                                Ok(component) => {
                                  let glyph = glyphs[indices[j + 1]]
                                  if glyph > 0xffffU {
                                    matches = false
                                  } else if u16_to_int(glyph) != component {
                                    matches = false
                                  }
                                }
                              }
                              if !matches {
                                break
                              }
                            }
                            if matches {
                              return Ok(
                                Some((lig_glyph.reinterpret_as_uint(), indices)),
                              )
                            }
                          }
                        }
                      }
                    }
                  }
                  Ok(None)
                }
              }
            }
          }
        }
      }
    }
  }
}

///|
fn single_substitute_format1(
  data : BytesView,
  offset : Int,
  glyph : UInt,
) -> Result[UInt?, OtLayoutError] {
  if offset < 0 || offset + 6 > data.length() {
    return Err(UnexpectedEof)
  }
  let coverage_offset = read_u16_int(data, offset + 2)
  let delta = read_i16(data, offset + 4)
  match (coverage_offset, delta) {
    (Err(err), _) => Err(err)
    (_, Err(err)) => Err(err)
    (Ok(coverage_offset), Ok(delta)) => {
      let coverage_base = offset + coverage_offset
      let index = coverage_index(data, coverage_base, glyph)
      match index {
        Err(err) => Err(err)
        Ok(None) => Ok(None)
        Ok(Some(_)) => {
          let base = u16_to_int(glyph)
          let next = (base + delta) & 0xffff
          Ok(Some(next.reinterpret_as_uint()))
        }
      }
    }
  }
}

///|
fn single_substitute_format2(
  data : BytesView,
  offset : Int,
  glyph : UInt,
) -> Result[UInt?, OtLayoutError] {
  if offset < 0 || offset + 8 > data.length() {
    return Err(UnexpectedEof)
  }
  let coverage_offset = read_u16_int(data, offset + 2)
  let glyph_count = read_u16_int(data, offset + 4)
  match (coverage_offset, glyph_count) {
    (Err(err), _) => Err(err)
    (_, Err(err)) => Err(err)
    (Ok(coverage_offset), Ok(glyph_count)) => {
      let coverage_base = offset + coverage_offset
      let index = coverage_index(data, coverage_base, glyph)
      match index {
        Err(err) => Err(err)
        Ok(None) => Ok(None)
        Ok(Some(index)) => {
          if index < 0 || index >= glyph_count {
            return Err(InvalidFormat)
          }
          let list_base = offset + 6
          let list_offset = list_base + index * 2
          let substitute = read_u16_int(data, list_offset)
          match substitute {
            Err(err) => Err(err)
            Ok(substitute) => Ok(Some(substitute.reinterpret_as_uint()))
          }
        }
      }
    }
  }
}