///|
fn transform_safe_float(value : Double) -> Double {
  if value.is_nan() ||
    value.is_inf() ||
    value == 0.0 ||
    value.abs() < 2.2250738585072014e-308 {
    0.0
  } else {
    value
  }
}

///|
/// Decomposes this matrix into scale, aspect, rotation, shear, and translation
/// components.
///
/// Non-finite and underflowed intermediate values are normalized to zero so the
/// result is stable for malformed or degenerate matrices.
pub fn TransformMatrix::decompose(
  self : TransformMatrix,
) -> TransformDecomposition {
  let determinant = self.a * self.d - self.c * self.b
  let moda = (self.a * self.a + self.b * self.b).sqrt()
  let modb = (self.c * self.c + self.d * self.d).sqrt()
  let dot = self.a * self.c + self.b * self.d
  let scale = determinant / moda
  let aspect = if scale.abs() == 0.0 { 1.0 } else { moda / scale.abs() }
  let rotation = @math.atan2(self.b, self.a)
  let shear = if moda * modb == 0.0 {
    0.0
  } else {
    @math.PI / 2.0 - @math.acos(dot / (moda * modb))
  }
  {
    scale: transform_safe_float(scale),
    aspect: transform_safe_float(aspect),
    rotation: transform_safe_float(rotation),
    shear: transform_safe_float(shear),
    translate_x: transform_safe_float(self.e),
    translate_y: transform_safe_float(self.f),
  }
}

///|
/// Decomposes a matrix into scale, aspect, rotation, shear, and translation.
pub fn transform_decompose(matrix : TransformMatrix) -> TransformDecomposition {
  matrix.decompose()
}

///|
/// Recomposes a matrix from decomposed transform components.
pub fn transform_recompose(parts : TransformDecomposition) -> TransformMatrix {
  let scale_aspect_shear = {
    ..transform_identity_matrix,
    a: parts.scale.abs() * parts.aspect,
    c: parts.scale * @math.tan(parts.shear),
    d: parts.scale,
  }
  transform_matrix_compose(
    transform_translate(parts.translate_x, parts.translate_y),
    transform_matrix_compose(
      transform_rotate(point2(0.0, 0.0), parts.rotation),
      scale_aspect_shear,
    ),
  )
}

///|
/// Recomposes a matrix from individual decomposition fields.
pub fn transform_recompose_components(
  scale : Double,
  aspect : Double,
  rotation : Double,
  shear : Double,
  translate_x : Double,
  translate_y : Double,
) -> TransformMatrix {
  transform_recompose({
    scale,
    aspect,
    rotation,
    shear,
    translate_x,
    translate_y,
  })
}