# 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
    guide: The guide image with shape (h, w).
    grid_shape: The grid shape, an array-like containing [gh, gw, gd, gc].

  Returns:
    An (image_extent, grid_extent) array with the spatial weight for each
    spatial and grid position.
  """
  guide_padded = _symmetric_pad_ij(guide, grid_shape)

  # Rescale `image` from [0, 1] to [0, grid_depth].
  # These are the floating point k coordinates of each sample.
  grid_depth = grid_shape[2]
  gk_float = guide_padded * grid_depth

  # Each sample with float value kf can splat onto locations:
  # k0 = floor(kf - 0.5)
  # k1 = ceil(kf - 0.5)
  #
  # The subtraction by 0.5 is necessary:
  # - Grid samples are located at half-integer coordinates:
  #   k = 0 places its sample at kf = 0.5.
  # - If kf = 1.4, the tent weight function is nonzero in the range [0.4, 1.4].
  #   Therefore, we need to splat to k0 = 0 and k1 = 1.
  # - If kf = 1.9, the tent weight function is nonzero in the range [0.9, 1.9].
  #   Therefore, we need to splat to k0 = 1 and k1 = 2.
  gk_floor = jnp.floor(gk_float - 0.5)
  gk_ceil = jnp.ceil(gk_float - 0.5)

  # Compute tent weights before clipping.
  wk_floor = smoothed_lerp_weight(gk_floor + 0.5, gk_float)

# 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

    guide: The guide image with shape (h, w).
    grid_shape: The grid shape, an array-like containing [gh, gw, gd, gc].

  Returns:
    An (image_extent, grid_extent) array with the spatial weight for each
    spatial and grid position.
  """
  guide_padded = _symmetric_pad_ij(guide, grid_shape)

  # Rescale `image` from [0, 1] to [0, grid_depth].
  # These are the floating point k coordinates of each sample.
  grid_depth = grid_shape[2]
  gk_float = guide_padded * grid_depth

  # Each sample with float value kf can splat onto locations:
  # k0 = floor(kf - 0.5)
  # k1 = ceil(kf - 0.5)
  #
  # The subtraction by 0.5 is necessary:
  # - Grid samples are located at half-integer coordinates:
  #   k = 0 places its sample at kf = 0.5.
  # - If kf = 1.4, the tent weight function is nonzero in the range [0.4, 1.4].
  #   Therefore, we need to splat to k0 = 0 and k1 = 1.
  # - If kf = 1.9, the tent weight function is nonzero in the range [0.9, 1.9].
  #   Therefore, we need to splat to k0 = 1 and k1 = 2.
  gk_floor = jnp.floor(gk_float - 0.5)
  gk_ceil = jnp.ceil(gk_float - 0.5)

  # Compute tent weights before clipping.
  wk_floor = smoothed_lerp_weight(gk_floor + 0.5, gk_float)

  wk_ceil = smoothed_lerp_weight(gk_ceil + 0.5, gk_float)