///|
/// Birth-time representative face chains for H2 classes in a 3D volume.
/// They are algebraic cycles, not a segmentation or a shortest enclosing shell.
pub fn volume_h2_surfaces_json(analysis : Analysis) -> Json raise TopologyError {
  if analysis.filtration.kind != "cubical_volume" ||
    analysis.homology_dimension < 2 {
    raise TopologyError("H2 surfaces require a three-dimensional volume")
  }
  let result : Array[Json] = []
  for index, interval in analysis.intervals {
    if interval.dimension == 2 {
      let faces : Array[Json] = []
      let simplified = simplify_representative(analysis, index)
      for cell_index in simplified {
        let cell = analysis.filtration.cells[cell_index]
        if cell.dimension != 2 || cell.vertices.length() != 4 {
          raise TopologyError("invalid H2 representative face")
        }
        let corners : Array[Json] = []
        for vertex in cell.vertices {
          corners.push(analysis.filtration.positions[vertex].to_json())
        }
        faces.push({
          "cell_index": cell_index,
          "vertices": cell.vertices,
          "corners": corners,
          "value": cell.value,
        })
      }
      result.push({
        "interval_index": index,
        "birth": interval.birth,
        "death": match interval.death {
          Some(value) => value.to_json()
          None => Json::null()
        },
        "face_count": faces.length(),
        "original_face_count": interval.representative.length(),
        "faces": faces,
      })
    }
  }
  {
    "schema_version": 1,
    "kind": "volume_h2_representatives",
    "cutoff": analysis.filtration.cutoff,
    "surfaces": result,
    "interpretation": "birth-time GF(2) representative face cycles; not a shortest shell or proof of a unique physical cavity",
  }
}

///|
/// Transform H2 representative face corners with a NIfTI voxel-to-world
/// affine. Topology remains on the original voxel grid.
pub fn volume_h2_world_surfaces_json(
  analysis : Analysis,
  affine : Array[Array[Double]],
) -> Json raise TopologyError {
  if analysis.filtration.kind != "cubical_volume" || affine.length() != 3 {
    raise TopologyError("world surfaces require volume and a 3x4 affine")
  }
  for row in affine {
    if row.length() != 4 {
      raise TopologyError("world surfaces require a 3x4 affine")
    }
    for value in row {
      if !finite(value) {
        raise TopologyError("world surface affine must be finite")
      }
    }
  }
  let surfaces : Array[Json] = []
  for index, interval in analysis.intervals {
    if interval.dimension == 2 {
      let faces : Array[Json] = []
      for cell_index in simplify_representative(analysis, index) {
        let corners : Array[Json] = []
        for vertex in analysis.filtration.cells[cell_index].vertices {
          let p = analysis.filtration.positions[vertex]
          let x = p[0]
          let y = -p[1]
          let z = p[2]
          corners.push(
            Array::makei(3, fn(axis) {
              affine[axis][0] * x +
              affine[axis][1] * y +
              affine[axis][2] * z +
              affine[axis][3]
            }).to_json(),
          )
        }
        faces.push({ "cell_index": cell_index, "world_corners": corners })
      }
      surfaces.push({
        "interval_index": index,
        "birth": interval.birth,
        "death": match interval.death {
          Some(value) => value.to_json()
          None => Json::null()
        },
        "faces": faces,
      })
    }
  }
  {
    "schema_version": 1,
    "kind": "volume_h2_world_surfaces",
    "affine_voxel_xyz_to_world": affine,
    "surfaces": surfaces,
    "interpretation": "birth-time representative faces mapped through NIfTI affine; no spatial resampling performed",
  }
}