///|
// Keep source encounter order in the output, using membership hashing only for
// large joins. Published source arrays remain immutable, including subset joins.
fn local_graph_join_indexed_sources(
  lhs : Array[Int32],
  rhs : Array[Int32],
  membership_builds~ : Array[Int32]? = None,
) -> Array[Int32] {
  if rhs.is_empty() {
    return lhs
  }
  let mut start = 0
  while start < lhs.length() && lhs[start] != rhs[0] {
    start += 1
  }
  if rhs.length() <= lhs.length() - start {
    let mut contiguous = true
    for index = 1; index < rhs.length(); index = index + 1 {
      if lhs[start + index] != rhs[index] {
        contiguous = false
        break
      }
    }
    if contiguous {
      return lhs
    }
  }
  // Keep the original allocation-free subset path for arbitrary source order.
  if rhs.all(fn(source) { local_graph_array_contains(lhs, source) }) {
    return lhs
  }
  match membership_builds {
    Some(count) => count[0] += 1
    None => ()
  }
  let present : @set.Set[Int32] = @set.Set([])
  for source in lhs {
    present.add(source)
  }
  let mut merged : Array[Int32]? = None
  for source in rhs {
    if !present.contains(source) {
      present.add(source)
      let output = match merged {
        Some(output) => output
        None => {
          let output = lhs.copy()
          merged = Some(output)
          output
        }
      }
      output.push(source)
    }
  }
  match merged {
    Some(output) => output
    None => lhs
  }
}