///|
/// Result of RANSAC Homography estimation.
pub(all) struct RansacHomographyResult {
  homography : Matrix3x3
  inliers : Array[Bool]
  inlier_count : Int
  reprojection_error : Double
} derive(Debug, Eq)

///|
/// Computes symmetric transfer error for a point correspondence under homography H and H^(-1).
pub fn symmetric_transfer_error(
  h : Matrix3x3,
  h_inv : Matrix3x3,
  src : Point2D,
  dst : Point2D,
) -> Double {
  let dst_est = h.transform_point2d(src)
  let src_est = h_inv.transform_point2d(dst)
  let err1 = dst.distance_sq(dst_est)
  let err2 = src.distance_sq(src_est)
  (err1 + err2) * 0.5
}

///|
/// Robust Homography estimation using RANSAC (Random Sample Consensus) algorithm.
pub fn find_homography_ransac(
  src_pts : Array[Point2D],
  dst_pts : Array[Point2D],
  max_iterations? : Int = 1000,
  inlier_threshold? : Double = 3.0,
  _confidence? : Double = 0.99,
  seed? : Int = 42,
) -> RansacHomographyResult? {
  let n = src_pts.length()
  if n < 4 || dst_pts.length() != n {
    return None
  }

  let thresh_sq = inlier_threshold * inlier_threshold
  let mut rng_state = seed.to_int64()
  let next_rand = fn() -> Int {
    rng_state = rng_state * 6364136223846793005L + 1442695040888963407L
    let val = if rng_state < 0L { -rng_state } else { rng_state }
    (val % n.to_int64()).to_int()
  }

  let mut best_inlier_count = 0
  let mut best_inliers = Array::make(n, false)
  let mut best_h = Matrix3x3::identity()
  let mut best_error = 1.0e30

  let mut iters = 0
  let max_iters = max_iterations

  while iters < max_iters {
    iters += 1

    // Randomly sample 4 unique indices
    let idxs = Array::make(4, 0)
    idxs[0] = next_rand()
    let mut count = 1
    while count < 4 {
      let r = next_rand()
      let mut duplicate = false
      for i in 0..
        match h.inverse() {
          Some(h_inv) => {
            let inliers = Array::make(n, false)
            let mut inlier_cnt = 0
            let mut total_err = 0.0

            for i in 0.. best_inlier_count ||
              (inlier_cnt == best_inlier_count && total_err < best_error) {
              best_inlier_count = inlier_cnt
              best_inliers = inliers
              best_h = h
              best_error = total_err
            }

            // Adaptive early termination if sufficiently high inlier ratio
            let inlier_ratio = inlier_cnt.to_double() / n.to_double()
            if inlier_ratio > 0.85 {
              break // Early convergence on high inlier consensus
            }
          }
          None => ()
        }
      None => ()
    }
  }

  if best_inlier_count >= 4 {
    // Re-estimate Homography on all inliers using DLT
    let inlier_src : Array[Point2D] = []
    let inlier_dst : Array[Point2D] = []
    for i in 0.. h
      None => best_h
    }

    let avg_err = if best_inlier_count > 0 {
      best_error / best_inlier_count.to_double()
    } else {
      0.0
    }

    Some({
      homography: final_h,
      inliers: best_inliers,
      inlier_count: best_inlier_count,
      reprojection_error: avg_err,
    })
  } else {
    None
  }
}