"""Geolocation math utilities.

This module provides the mathematical building blocks for geolocation:

- ``whitened_least_squares``: Generic weighted nonlinear least squares solver
  with Cholesky whitening. Accepts any residual/Jacobian function and full
  measurement covariance matrix.

- ``predicted_covariance``: Analytical prediction of solution covariance from
  a Jacobian and measurement covariance (GDOP). No solver iteration needed --
  just geometry and noise specs.

- ``default_initial_guess``: Projects collector centroid onto Earth surface
  for use as an initial guess in iterative solvers.

For observable-specific locators (tdoa_locator, aoa_locator, etc.), see
gri_geosim.geolocation, which wraps these building blocks with measurement-type
specific residual/Jacobian construction and input validation.

Example: Predict TDOA geolocation accuracy analytically::

    from gri_utils.observables import tdoa_with_gradient
    from gri_utils.geolocation import predicted_covariance
    import numpy as np

    # Evaluate Jacobian at candidate geometry
    target = np.array([6378137.0, 100.0, 100.0])
    c1 = np.array([[6378137.0, 1000.0, 0.0],
                   [6378137.0, 0.0, 1000.0],
                   [6378137.0, -1000.0, 0.0]])
    c2 = np.array([[6378137.0, -1000.0, 0.0],
                   [6378137.0, 0.0, -1000.0],
                   [6378137.0, 1000.0, 0.0]])

    jacobian = np.empty((3, 3))
    for i in range(3):
        _, jacobian[i] = tdoa_with_gradient(target, c1[i], c2[i])

    # 10 ns timing uncertainty, independent measurements
    R = np.diag(np.full(3, (10e-9)**2))

    # Predicted position covariance (3x3, ECEF)
    cov = predicted_covariance(jacobian, R)
    print(f"Position stdev: {np.sqrt(np.diag(cov))} meters")

Example: Solve with full covariance (correlated measurements)::

    from gri_utils.geolocation import whitened_least_squares
    import numpy as np

    # User-defined residual and Jacobian function
    def my_residual_and_jacobian(x):
        # ... compute residuals and Jacobian at x ...
        return residual, jacobian

    # Full measurement covariance with off-diagonal correlation
    R = np.array([[1e-18, 0.5e-18, 0.0],
                  [0.5e-18, 1e-18, 0.0],
                  [0.0, 0.0, 1e-18]])

    solution, sol_cov, chi_sq = whitened_least_squares(
        my_residual_and_jacobian, x0, R
    )
"""

from .default_initial_guess import default_initial_guess
from .predicted_covariance import predicted_covariance
from .whitened_least_squares import whitened_least_squares

__all__ = [
    "default_initial_guess",
    "predicted_covariance",
    "whitened_least_squares",
]
