# Path: jax/numerics.py
# def lerp_weight(x, xs):
#   """Linear interpolation weight from a sample at x to xs.
# 
#   Returns the linear interpolation weight of a "query point" at coordinate `x`
#   with respect to a "sample" at coordinate `xs`.
# 
#   The integer coordinates `x` are at pixel centers.
#   The floating point coordinates `xs` are at pixel edges.
#   (OpenGL convention).
# 
#   Args:
#     x: "Query" point position.
#     xs: "Sample" position.
# 
#   Returns:
#     - 1 when x = xs.
#     - 0 when |x - xs| > 1.
#   """
#   dx = x - xs
#   abs_dx = abs(dx)
#   return jnp.maximum(1.0 - abs_dx, 0.0)
# 
# Path: jax/numerics.py
# def smoothed_lerp_weight(x, xs, eps=1e-8):
#   """Smoothed version of `LerpWeight` with gradients more suitable for backprop.
# 
#   Let f(x, xs) = LerpWeight(x, xs)
#                = max(1 - |x - xs|, 0)
#                = max(1 - |dx|, 0)
# 
#   f is not smooth when:
#   - |dx| is close to 0. We smooth this by replacing |dx| with
#     SmoothedAbs(dx, eps) = sqrt(dx * dx + eps), which has derivative
#     dx / sqrt(dx * dx + eps).
#   - |dx| = 1. When smoothed, this happens when dx = sqrt(1 - eps). Like ReLU,
#     We just ignore this (in the implementation below, when the floats are
#     exactly equal, we choose the SmoothedAbsGrad path since it is more useful
#     than returning a 0 gradient).
# 
#   Args:
#     x: "Query" point position.
#     xs: "Sample" position.
#     eps: a small number.
# 
#   Returns:
#     max(1 - |dx|, 0) where |dx| is smoothed_abs(dx).
#   """
#   dx = x - xs
#   abs_dx = smoothed_abs(dx, eps)
#   return jnp.maximum(1.0 - abs_dx, 0.0)
# 
# Path: jax/numerics.py
# def smoothed_lerp_weight_grad(x, xs, eps=1e-8):
#   """Gradient of smoothed_lerp_weight."""
#   dx = x - xs
#   abs_dx = smoothed_abs(dx, eps)
#   grad = smoothed_abs_grad(dx, eps)
#   return jnp.where(abs_dx > 1, 0, grad)


import jax
import jax.numpy as jnp
from .numerics import lerp_weight
from .numerics import smoothed_lerp_weight
from .numerics import smoothed_lerp_weight_grad
    scale: spatial scaling (image pixels per grid cell)
    half_pad: how much padding is needed on either side of the image.
  """
  scale = image_extent / grid_extent
  half_scale = 0.5 * scale
  half_pad = jnp.ceil(half_scale).astype(jnp.int32)

  return scale, half_pad


def _compute_spatial_weights(image_extent, grid_extent):
  """Computes spatial weights given image and grid extents.

  Args:
    image_extent: Image extent along that axis.
    grid_extent: Grid extent along the corresponding axis.

  Returns:
    An (image_extent, grid_extent) array with the spatial weight for each
    spatial and grid position.
  """
  scale, half_pad = _compute_scale_pad(image_extent, grid_extent)
  image_extent_padded = image_extent + 2 * half_pad

  indices = jnp.arange(image_extent_padded) - half_pad
  grid_indices_float = (indices + 0.5) / scale

  gif, gi = jnp.meshgrid(
      grid_indices_float, jnp.arange(grid_extent), indexing='ij')


# Path: jax/numerics.py
# def lerp_weight(x, xs):
#   """Linear interpolation weight from a sample at x to xs.
# 
#   Returns the linear interpolation weight of a "query point" at coordinate `x`
#   with respect to a "sample" at coordinate `xs`.
# 
#   The integer coordinates `x` are at pixel centers.
#   The floating point coordinates `xs` are at pixel edges.
#   (OpenGL convention).
# 
#   Args:
#     x: "Query" point position.
#     xs: "Sample" position.
# 
#   Returns:
#     - 1 when x = xs.
#     - 0 when |x - xs| > 1.
#   """
#   dx = x - xs
#   abs_dx = abs(dx)
#   return jnp.maximum(1.0 - abs_dx, 0.0)
# 
# Path: jax/numerics.py
# def smoothed_lerp_weight(x, xs, eps=1e-8):
#   """Smoothed version of `LerpWeight` with gradients more suitable for backprop.
# 
#   Let f(x, xs) = LerpWeight(x, xs)
#                = max(1 - |x - xs|, 0)
#                = max(1 - |dx|, 0)
# 
#   f is not smooth when:
#   - |dx| is close to 0. We smooth this by replacing |dx| with
#     SmoothedAbs(dx, eps) = sqrt(dx * dx + eps), which has derivative
#     dx / sqrt(dx * dx + eps).
#   - |dx| = 1. When smoothed, this happens when dx = sqrt(1 - eps). Like ReLU,
#     We just ignore this (in the implementation below, when the floats are
#     exactly equal, we choose the SmoothedAbsGrad path since it is more useful
#     than returning a 0 gradient).
# 
#   Args:
#     x: "Query" point position.
#     xs: "Sample" position.
#     eps: a small number.
# 
#   Returns:
#     max(1 - |dx|, 0) where |dx| is smoothed_abs(dx).
#   """
#   dx = x - xs
#   abs_dx = smoothed_abs(dx, eps)
#   return jnp.maximum(1.0 - abs_dx, 0.0)
# 
# Path: jax/numerics.py
# def smoothed_lerp_weight_grad(x, xs, eps=1e-8):
#   """Gradient of smoothed_lerp_weight."""
#   dx = x - xs
#   abs_dx = smoothed_abs(dx, eps)
#   grad = smoothed_abs_grad(dx, eps)
#   return jnp.where(abs_dx > 1, 0, grad)


# Path: jax/bilateral_slice.py
import jax
import jax.numpy as jnp
from .numerics import lerp_weight
from .numerics import smoothed_lerp_weight
from .numerics import smoothed_lerp_weight_grad

    scale: spatial scaling (image pixels per grid cell)
    half_pad: how much padding is needed on either side of the image.
  """
  scale = image_extent / grid_extent
  half_scale = 0.5 * scale
  half_pad = jnp.ceil(half_scale).astype(jnp.int32)

  return scale, half_pad


def _compute_spatial_weights(image_extent, grid_extent):
  """Computes spatial weights given image and grid extents.

  Args:
    image_extent: Image extent along that axis.
    grid_extent: Grid extent along the corresponding axis.

  Returns:
    An (image_extent, grid_extent) array with the spatial weight for each
    spatial and grid position.
  """
  scale, half_pad = _compute_scale_pad(image_extent, grid_extent)
  image_extent_padded = image_extent + 2 * half_pad

  indices = jnp.arange(image_extent_padded) - half_pad
  grid_indices_float = (indices + 0.5) / scale

  gif, gi = jnp.meshgrid(
      grid_indices_float, jnp.arange(grid_extent), indexing='ij')


  weights = lerp_weight(gi + 0.5, gif)