# Path: dataflow/illumination_integration/helper.py
# def getBilinearFromUv(env_map: tf.Tensor, uvs: tf.Tensor) -> tf.Tensor:
#     u = uvs[..., 0]
#     v = uvs[..., 1]
# 
#     # vFlipped = 1 - v
# 
#     # u corresponds to width and v to heights
#     u_reshaped = tf.reshape(u, (-1, tf.math.reduce_prod(u.shape[1:])))
#     v_reshaped = tf.reshape(v, (-1, tf.math.reduce_prod(v.shape[1:])))
# 
#     uvs = tf.stack(
#         [u_reshaped * (env_map.shape[2] - 1), v_reshaped * (env_map.shape[1] - 1)],
#         axis=-1,
#     )
# 
#     return tfa.image.interpolate_bilinear(env_map, uvs, indexing="xy")
# 
# Path: nn_utils/math_utils.py
# def shape_to_uv(height: int, width: int) -> tf.Tensor:
#     # UV
#     # 0,0              1,0
#     # 0,1              1,1
#     us, vs = tf.meshgrid(
#         tf.linspace(
#             0.0 + 0.5 / tf.cast(width, tf.float32),
#             1.0 - 0.5 / tf.cast(width, tf.float32),
#             width,
#         ),
#         tf.linspace(
#             0.0 + 0.5 / tf.cast(height, tf.float32),
#             1.0 - 0.5 / tf.cast(height, tf.float32),
#             height,
#         ),
#     )  # Use pixel centers
#     return tf.cast(tf.stack([us, vs], -1), tf.float32)
# 
# def uv_to_direction(uvs: tf.Tensor) -> tf.Tensor:
#     spherical = uv_to_spherical(uvs)
#     theta = spherical[..., 0:1]
#     phi = spherical[..., 1:2]
#     return spherical_to_cartesian(theta, phi)


from typing import List, Union
from dataflow.illumination_integration.helper import (
    getBilinearFromUv,
)
from nn_utils.math_utils import shape_to_uv, uv_to_direction
import numpy as np
import tensorflow as tf
import nn_utils.math_utils as math_utils

    return (
        data_levels,
        *ret,
    )


@tf.function
def full_map_samples(num_roughness_steps: int, data_levels: List[tf.Tensor]):
    # Setup random roughnesses and get all values
    full_uvs = tf.reshape(shape_to_uv(*data_levels[0].shape[:-1]), (-1, 2))

    roughness_steps = np.linspace(0.0, 1.0, num_roughness_steps, dtype=np.float32)[
        :, None
    ]  # Add a dimension

    # Store the roughness steps
    all_samples = tf.TensorArray(
        tf.float32, size=num_roughness_steps, clear_after_read=True
    )

    for i, r in enumerate(roughness_steps):  # The dimension is removed in the for loop
        r = math_utils.repeat(
            r[:, None], full_uvs.shape[0], 0
        )  # Add a batch dimension back

        samples = random_uv_roughness_access(data_levels, full_uvs, r)
        all_samples = all_samples.write(i, samples)  # Write the sample

    ret = (

# Path: dataflow/illumination_integration/helper.py
# def getBilinearFromUv(env_map: tf.Tensor, uvs: tf.Tensor) -> tf.Tensor:
#     u = uvs[..., 0]
#     v = uvs[..., 1]
# 
#     # vFlipped = 1 - v
# 
#     # u corresponds to width and v to heights
#     u_reshaped = tf.reshape(u, (-1, tf.math.reduce_prod(u.shape[1:])))
#     v_reshaped = tf.reshape(v, (-1, tf.math.reduce_prod(v.shape[1:])))
# 
#     uvs = tf.stack(
#         [u_reshaped * (env_map.shape[2] - 1), v_reshaped * (env_map.shape[1] - 1)],
#         axis=-1,
#     )
# 
#     return tfa.image.interpolate_bilinear(env_map, uvs, indexing="xy")
# 
# Path: nn_utils/math_utils.py
# def shape_to_uv(height: int, width: int) -> tf.Tensor:
#     # UV
#     # 0,0              1,0
#     # 0,1              1,1
#     us, vs = tf.meshgrid(
#         tf.linspace(
#             0.0 + 0.5 / tf.cast(width, tf.float32),
#             1.0 - 0.5 / tf.cast(width, tf.float32),
#             width,
#         ),
#         tf.linspace(
#             0.0 + 0.5 / tf.cast(height, tf.float32),
#             1.0 - 0.5 / tf.cast(height, tf.float32),
#             height,
#         ),
#     )  # Use pixel centers
#     return tf.cast(tf.stack([us, vs], -1), tf.float32)
# 
# def uv_to_direction(uvs: tf.Tensor) -> tf.Tensor:
#     spherical = uv_to_spherical(uvs)
#     theta = spherical[..., 0:1]
#     phi = spherical[..., 1:2]
#     return spherical_to_cartesian(theta, phi)


# Path: dataflow/illumination_integration/dataflow.py
from typing import List, Union
from dataflow.illumination_integration.helper import (
    getBilinearFromUv,
)
from nn_utils.math_utils import shape_to_uv, uv_to_direction
import numpy as np
import tensorflow as tf
import nn_utils.math_utils as math_utils


    return (
        data_levels,
        *ret,
    )


@tf.function
def full_map_samples(num_roughness_steps: int, data_levels: List[tf.Tensor]):
    # Setup random roughnesses and get all values
    full_uvs = tf.reshape(shape_to_uv(*data_levels[0].shape[:-1]), (-1, 2))

    roughness_steps = np.linspace(0.0, 1.0, num_roughness_steps, dtype=np.float32)[
        :, None
    ]  # Add a dimension

    # Store the roughness steps
    all_samples = tf.TensorArray(
        tf.float32, size=num_roughness_steps, clear_after_read=True
    )

    for i, r in enumerate(roughness_steps):  # The dimension is removed in the for loop
        r = math_utils.repeat(
            r[:, None], full_uvs.shape[0], 0
        )  # Add a batch dimension back

        samples = random_uv_roughness_access(data_levels, full_uvs, r)
        all_samples = all_samples.write(i, samples)  # Write the sample

    ret = (

        uv_to_direction(full_uvs),