# Path: lib/coloraide/util.py
# INF = float('inf')
# ACHROMATIC_THRESHOLD = 0.0005
# DEF_PREC = 5
# DEF_FIT_TOLERANCE = 0.000075
# DEF_ALPHA = 1.0
# DEF_MIX = 0.5
# DEF_HUE_ADJ = "shorter"
# DEF_INTERPOLATE = "oklab"
# DEF_FIT = "lch-chroma"
# DEF_DELTA_E = "76"
# ERR_MAP_MSG = """
#     To add or remove items from this mapping, please subclass the
#     'Color' object and replace the entire mapping by either copying
#     this mapping and then altering it or by creating an entirely
#     new mapping. Example:
# 
#     class MyNewClass(Color):
#         {name} = {{**Color.{name}, **my_override_map}}
# """
# YW = 203
# M1 = 2610 / 16384
# M2 = 2523 / 32
# C1 = 3424 / 4096
# C2 = 2413 / 128
# C3 = 2392 / 128
# def xy_to_xyz(xy: Vector, Y: float = 1) -> MutableVector:
# def xyz_to_uv(xyz: Vector) -> MutableVector:
# def uv_to_xy(uv: Vector) -> MutableVector:
# def xy_to_uv_1960(xy: Vector) -> MutableVector:
# def uv_1960_to_xy(uv: Vector) -> MutableVector:
# def xyz_to_xyY(xyz: Vector, white: Vector) -> MutableVector:
# def pq_st2084_inverse_eotf(
#     values: Vector,
#     c1: float = C1,
#     c2: float = C2,
#     c3: float = C3,
#     m1: float = M1,
#     m2: float = M2
# ) -> MutableVector:
# def pq_st2084_eotf(
#     values: Vector,
#     c1: float = C1,
#     c2: float = C2,
#     c3: float = C3,
#     m1: float = M1,
#     m2: float = M2
# ) -> MutableVector:
# def xyz_d65_to_absxyzd65(xyzd65: Vector) -> MutableVector:
# def absxyzd65_to_xyz_d65(absxyzd65: Vector) -> MutableVector:
# def npow(base: float, exp: float) -> float:
# def constrain_hue(hue: float) -> float:
# def is_number(value: Any) -> bool:
# def is_nan(value: float) -> bool:
# def no_nans(value: Vector, default: float = 0.0) -> MutableVector:
# def no_nan(value: float, default: float = 0.0) -> float:
# def cmp_coords(c1: Vector, c2: Vector) -> bool:
# def dot(a: Union[float, Vector, Matrix], b: Union[float, Vector, Matrix]) -> Union[float, MutableVector, MutableMatrix]:
# def multiply(
#     a: Union[float, Vector, Matrix],
#     b: Union[float, Vector, Matrix]
# ) -> Union[float, MutableVector, MutableMatrix]:
# def divide(
#     a: Union[float, Vector, Matrix],
#     b: Union[float, Vector, Matrix]
# ) -> Union[float, MutableVector, MutableMatrix]:
# def diag(v: Union[Vector, Matrix], k: int = 0) -> Union[MutableVector, MutableMatrix]:
# def inv(matrix: Matrix) -> MutableMatrix:
# def cbrt(n: float) -> float:
# def nth_root(n: float, p: float) -> float:
# def clamp(value: float, mn: Optional[float] = None, mx: Optional[float] = None) -> float:
# def fmt_float(f: float, p: int = 0) -> str:
# def fmt_percent(f: float, p: int = 0) -> str:
# def adjust_precision(f: float, p: int = 0) -> float:
# def round_half_up(n: float, scale: int = 0) -> float:
# def deprecated(message: str, stacklevel: int = 2) -> Callable[..., Any]:  # pragma: no cover
#     def _wrapper(func: Callable[..., Any]) -> Callable[..., Any]:
#         def _deprecated_func(*args: Any, **kwargs: Any) -> Any:
# def warn_deprecated(message: str, stacklevel: int = 2) -> None:  # pragma: no cover


import math
from operator import itemgetter
from typing import Any, Callable, cast
from ..util import Vector, MutableVector
"""Blend modes."""


def is_non_seperable(mode: Any) -> bool:
    """Check if blend mode is non-separable."""

    return mode in frozenset(['color', 'hue', 'saturation', 'luminosity'])


# -----------------------------------------
# Non-separable blending helper functions
# -----------------------------------------
def lum(rgb: Vector) -> float:
    """Get luminosity."""

    return 0.299 * rgb[0] + 0.587 * rgb[1] + 0.114 * rgb[2]



# Path: lib/coloraide/util.py
# INF = float('inf')
# ACHROMATIC_THRESHOLD = 0.0005
# DEF_PREC = 5
# DEF_FIT_TOLERANCE = 0.000075
# DEF_ALPHA = 1.0
# DEF_MIX = 0.5
# DEF_HUE_ADJ = "shorter"
# DEF_INTERPOLATE = "oklab"
# DEF_FIT = "lch-chroma"
# DEF_DELTA_E = "76"
# ERR_MAP_MSG = """
#     To add or remove items from this mapping, please subclass the
#     'Color' object and replace the entire mapping by either copying
#     this mapping and then altering it or by creating an entirely
#     new mapping. Example:
# 
#     class MyNewClass(Color):
#         {name} = {{**Color.{name}, **my_override_map}}
# """
# YW = 203
# M1 = 2610 / 16384
# M2 = 2523 / 32
# C1 = 3424 / 4096
# C2 = 2413 / 128
# C3 = 2392 / 128
# def xy_to_xyz(xy: Vector, Y: float = 1) -> MutableVector:
# def xyz_to_uv(xyz: Vector) -> MutableVector:
# def uv_to_xy(uv: Vector) -> MutableVector:
# def xy_to_uv_1960(xy: Vector) -> MutableVector:
# def uv_1960_to_xy(uv: Vector) -> MutableVector:
# def xyz_to_xyY(xyz: Vector, white: Vector) -> MutableVector:
# def pq_st2084_inverse_eotf(
#     values: Vector,
#     c1: float = C1,
#     c2: float = C2,
#     c3: float = C3,
#     m1: float = M1,
#     m2: float = M2
# ) -> MutableVector:
# def pq_st2084_eotf(
#     values: Vector,
#     c1: float = C1,
#     c2: float = C2,
#     c3: float = C3,
#     m1: float = M1,
#     m2: float = M2
# ) -> MutableVector:
# def xyz_d65_to_absxyzd65(xyzd65: Vector) -> MutableVector:
# def absxyzd65_to_xyz_d65(absxyzd65: Vector) -> MutableVector:
# def npow(base: float, exp: float) -> float:
# def constrain_hue(hue: float) -> float:
# def is_number(value: Any) -> bool:
# def is_nan(value: float) -> bool:
# def no_nans(value: Vector, default: float = 0.0) -> MutableVector:
# def no_nan(value: float, default: float = 0.0) -> float:
# def cmp_coords(c1: Vector, c2: Vector) -> bool:
# def dot(a: Union[float, Vector, Matrix], b: Union[float, Vector, Matrix]) -> Union[float, MutableVector, MutableMatrix]:
# def multiply(
#     a: Union[float, Vector, Matrix],
#     b: Union[float, Vector, Matrix]
# ) -> Union[float, MutableVector, MutableMatrix]:
# def divide(
#     a: Union[float, Vector, Matrix],
#     b: Union[float, Vector, Matrix]
# ) -> Union[float, MutableVector, MutableMatrix]:
# def diag(v: Union[Vector, Matrix], k: int = 0) -> Union[MutableVector, MutableMatrix]:
# def inv(matrix: Matrix) -> MutableMatrix:
# def cbrt(n: float) -> float:
# def nth_root(n: float, p: float) -> float:
# def clamp(value: float, mn: Optional[float] = None, mx: Optional[float] = None) -> float:
# def fmt_float(f: float, p: int = 0) -> str:
# def fmt_percent(f: float, p: int = 0) -> str:
# def adjust_precision(f: float, p: int = 0) -> float:
# def round_half_up(n: float, scale: int = 0) -> float:
# def deprecated(message: str, stacklevel: int = 2) -> Callable[..., Any]:  # pragma: no cover
#     def _wrapper(func: Callable[..., Any]) -> Callable[..., Any]:
#         def _deprecated_func(*args: Any, **kwargs: Any) -> Any:
# def warn_deprecated(message: str, stacklevel: int = 2) -> None:  # pragma: no cover


# Path: lib/coloraide/compositing/blend_modes.py
import math
from operator import itemgetter
from typing import Any, Callable, cast
from ..util import Vector, MutableVector

"""Blend modes."""


def is_non_seperable(mode: Any) -> bool:
    """Check if blend mode is non-separable."""

    return mode in frozenset(['color', 'hue', 'saturation', 'luminosity'])


# -----------------------------------------
# Non-separable blending helper functions
# -----------------------------------------
def lum(rgb: Vector) -> float:
    """Get luminosity."""

    return 0.299 * rgb[0] + 0.587 * rgb[1] + 0.114 * rgb[2]



def clip_color(rgb: MutableVector) -> MutableVector: