# This file was auto generated by Styx.
# Do not edit this file directly.

import typing
import pathlib
from styxdefs import *

METRIC_MATH_METADATA = Metadata(
    id="c3f61d2cfe1af355592b15dccd2cddda61e44ce6.workbench",
    name="metric-math",
    package="workbench",
    container_image_tag="nx10x/workbench:2.1.0",
)


_MetricMathVarParamsDictNoTag = typing.TypedDict('_MetricMathVarParamsDictNoTag', {
    "name": str,
    "metric": InputPathType,
    "column": typing.NotRequired[str | None],
    "repeat": bool,
})
MetricMathVarParamsDictTagged = typing.TypedDict('MetricMathVarParamsDictTagged', {
    "@type": typing.Literal["var"],
    "name": str,
    "metric": InputPathType,
    "column": typing.NotRequired[str | None],
    "repeat": bool,
})
MetricMathVarParamsDict = _MetricMathVarParamsDictNoTag | MetricMathVarParamsDictTagged


_MetricMathParamsDictNoTag = typing.TypedDict('_MetricMathParamsDictNoTag', {
    "expression": str,
    "metric-out": str,
    "var": typing.NotRequired[list[MetricMathVarParamsDict] | None],
    "replace": typing.NotRequired[float | None],
})
MetricMathParamsDictTagged = typing.TypedDict('MetricMathParamsDictTagged', {
    "@type": typing.Literal["workbench/metric-math"],
    "expression": str,
    "metric-out": str,
    "var": typing.NotRequired[list[MetricMathVarParamsDict] | None],
    "replace": typing.NotRequired[float | None],
})
MetricMathParamsDict = _MetricMathParamsDictNoTag | MetricMathParamsDictTagged


def metric_math_var(
    name: str,
    metric: InputPathType,
    column: str | None = None,
    repeat: bool = False,
) -> MetricMathVarParamsDictTagged:
    """
    Build parameters.
    
    Args:
        name: the name of the variable, as used in the expression.
        metric: the metric file to use as this variable.
        column: select a single column\
            \
            the column number or name.
        repeat: reuse a single column for each column of calculation.
    Returns:
        Parameter dictionary
    """
    params = {
        "@type": "var",
        "name": name,
        "metric": metric,
        "repeat": repeat,
    }
    if column is not None:
        params["column"] = column
    return params


def metric_math_var_validate(
    params: typing.Any,
) -> None:
    """
    Validate parameters. Throws an error if `params` is not a valid
    `MetricMathVarParamsDict` object.
    
    Args:
        params: The parameters object to validate.
    """
    if params is None or not isinstance(params, dict):
        raise StyxValidationError(f'Params object has the wrong type \'{type(params)}\'')
    if params.get("name", None) is None:
        raise StyxValidationError("`name` must not be None")
    if not isinstance(params["name"], str):
        raise StyxValidationError(f'`name` has the wrong type: Received `{type(params.get("name", None))}` expected `str`')
    if params.get("metric", None) is None:
        raise StyxValidationError("`metric` must not be None")
    if not isinstance(params["metric"], (pathlib.Path, str)):
        raise StyxValidationError(f'`metric` has the wrong type: Received `{type(params.get("metric", None))}` expected `InputPathType`')
    if params.get("column", None) is not None:
        if not isinstance(params["column"], str):
            raise StyxValidationError(f'`column` has the wrong type: Received `{type(params.get("column", None))}` expected `str | None`')
    if params.get("repeat", False) is None:
        raise StyxValidationError("`repeat` must not be None")
    if not isinstance(params["repeat"], bool):
        raise StyxValidationError(f'`repeat` has the wrong type: Received `{type(params.get("repeat", False))}` expected `bool`')


def metric_math_var_cargs(
    params: MetricMathVarParamsDict,
    execution: Execution,
) -> list[str]:
    """
    Build command-line arguments from parameters.
    
    Args:
        params: The parameters.
        execution: The execution object for resolving input paths.
    Returns:
        Command-line arguments.
    """
    cargs = []
    cargs.extend([
        "-var",
        params.get("name", None),
        execution.input_file(params.get("metric", None))
    ])
    if params.get("column", None) is not None:
        cargs.extend([
            "-column",
            params.get("column", None)
        ])
    if params.get("repeat", False):
        cargs.append("-repeat")
    return cargs


class MetricMathOutputs(typing.NamedTuple):
    """
    Output object returned when calling `MetricMathParamsDict(...)`.
    """
    root: OutputPathType
    """Output root folder. This is the root folder for all outputs."""
    metric_out: OutputPathType
    """the output metric"""


def metric_math_params(
    expression: str,
    metric_out: str,
    var: list[MetricMathVarParamsDict] | None = None,
    replace: float | None = None,
) -> MetricMathParamsDictTagged:
    """
    Build parameters.
    
    Args:
        expression: the expression to evaluate, in quotes.
        metric_out: the output metric.
        var: a metric to use as a variable.
        replace: replace NaN results with a value\
            \
            value to replace NaN with.
    Returns:
        Parameter dictionary
    """
    params = {
        "@type": "workbench/metric-math",
        "expression": expression,
        "metric-out": metric_out,
    }
    if var is not None:
        params["var"] = var
    if replace is not None:
        params["replace"] = replace
    return params


def metric_math_validate(
    params: typing.Any,
) -> None:
    """
    Validate parameters. Throws an error if `params` is not a valid
    `MetricMathParamsDict` object.
    
    Args:
        params: The parameters object to validate.
    """
    if params is None or not isinstance(params, dict):
        raise StyxValidationError(f'Params object has the wrong type \'{type(params)}\'')
    if params.get("expression", None) is None:
        raise StyxValidationError("`expression` must not be None")
    if not isinstance(params["expression"], str):
        raise StyxValidationError(f'`expression` has the wrong type: Received `{type(params.get("expression", None))}` expected `str`')
    if params.get("metric-out", None) is None:
        raise StyxValidationError("`metric-out` must not be None")
    if not isinstance(params["metric-out"], str):
        raise StyxValidationError(f'`metric-out` has the wrong type: Received `{type(params.get("metric-out", None))}` expected `str`')
    if params.get("var", None) is not None:
        if not isinstance(params["var"], list):
            raise StyxValidationError(f'`var` has the wrong type: Received `{type(params.get("var", None))}` expected `list[MetricMathVarParamsDict] | None`')
        for e in params["var"]:
            metric_math_var_validate(e)
    if params.get("replace", None) is not None:
        if not isinstance(params["replace"], (float, int)):
            raise StyxValidationError(f'`replace` has the wrong type: Received `{type(params.get("replace", None))}` expected `float | None`')


def metric_math_cargs(
    params: MetricMathParamsDict,
    execution: Execution,
) -> list[str]:
    """
    Build command-line arguments from parameters.
    
    Args:
        params: The parameters.
        execution: The execution object for resolving input paths.
    Returns:
        Command-line arguments.
    """
    cargs = []
    cargs.extend([
        "wb_command",
        "-metric-math"
    ])
    cargs.append(params.get("expression", None))
    cargs.append(params.get("metric-out", None))
    if params.get("var", None) is not None:
        cargs.extend([a for c in [metric_math_var_cargs(s, execution) for s in params.get("var", None)] for a in c])
    if params.get("replace", None) is not None:
        cargs.extend([
            "-fixnan",
            str(params.get("replace", None))
        ])
    return cargs


def metric_math_outputs(
    params: MetricMathParamsDict,
    execution: Execution,
) -> MetricMathOutputs:
    """
    Build outputs object containing output file paths and possibly stdout/stderr.
    
    Args:
        params: The parameters.
        execution: The execution object for resolving input paths.
    Returns:
        Outputs object.
    """
    ret = MetricMathOutputs(
        root=execution.output_file("."),
        metric_out=execution.output_file(params.get("metric-out", None)),
    )
    return ret


def metric_math_execute(
    params: MetricMathParamsDict,
    runner: Runner | None = None,
) -> MetricMathOutputs:
    """
    EVALUATE EXPRESSION ON METRIC FILES.
    
    This command evaluates <expression> at each surface vertex independently.
    There must be at least one -var option (to get the structure, number of
    vertices, and number of columns from), even if the <name> specified in it
    isn't used in <expression>. All metrics must have the same number of
    vertices. Filenames are not valid in <expression>, use a variable name and a
    -var option with matching <name> to specify an input file. If the -column
    option is given to any -var option, only one column is used from that file.
    If -repeat is specified, the file must either have only one column, or have
    the -column option specified. All files that don't use -repeat must have the
    same number of columns requested to be used. The format of <expression> is
    as follows:
    
    Expressions consist of constants, variables, operators, parentheses, and
    functions, in infix notation, such as 'exp(-x + 3) * scale'. Variables are
    strings of any length, using the characters a-z, A-Z, 0-9, and _, but may
    not take the name of a named constant. Currently, there is only one named
    constant, PI. The operators are +, -, *, /, ^, >, <, >=, <=, ==, !=, !, &&,
    ||. These behave as in C, except that ^ is exponentiation, i.e. pow(x, y),
    and takes higher precedence than other binary operators (also, '-3^-4^-5'
    means '-(3^(-(4^-5)))'). The <=, >=, ==, and != operators are given a small
    amount of wiggle room, equal to one millionth of the smaller of the absolute
    values of the values being compared.
    
    Comparison and logical operators return 0 or 1, you can do masking with
    expressions like 'x * (mask > 0)'. For all logical operators, an input is
    considered true iff it is greater than 0. The expression '0 < x < 5' is not
    syntactically wrong, but it will NOT do what is desired, because it is
    evaluated left to right, i.e. '((0 < x) < 5)', which will always return 1,
    as both possible results of a comparison are less than 5. A warning is
    generated if an expression of this type is detected. Use something like 'x >
    0 && x < 5' to get the desired behavior.
    
    Whitespace between elements is ignored, ' sin ( 2 * x ) ' is equivalent to
    'sin(2*x)', but 's in(2*x)' is an error. Implied multiplication is not
    allowed, the expression '2x' will be parsed as a variable. Parentheses are
    (), do not use [] or {}. Functions require parentheses, the expression 'sin
    x' is an error.
    
    The following functions are supported:
    
    sin: 1 argument, the sine of the argument (units are radians)
    cos: 1 argument, the cosine of the argument (units are radians)
    tan: 1 argument, the tangent of the argument (units are radians)
    asin: 1 argument, the inverse of sine of the argument, in radians
    acos: 1 argument, the inverse of cosine of the argument, in radians
    atan: 1 argument, the inverse of tangent of the argument, in radians
    atan2: 2 arguments, atan2(y, x) returns the inverse of tangent of (y/x), in
    radians, determining quadrant by the sign of both arguments
    sinh: 1 argument, the hyperbolic sine of the argument
    cosh: 1 argument, the hyperbolic cosine of the argument
    tanh: 1 argument, the hyperbolic tangent of the argument
    asinh: 1 argument, the inverse hyperbolic sine of the argument
    acosh: 1 argument, the inverse hyperbolic cosine of the argument
    atanh: 1 argument, the inverse hyperbolic tangent of the argument
    sinc: 1 argument, sinc(0) = 1, sin(x) / x otherwise
    ln: 1 argument, the natural logarithm of the argument
    exp: 1 argument, the constant e raised to the power of the argument
    log: 1 argument, the base 10 logarithm of the argument
    log2: 1 argument, the base 2 logarithm of the argument
    sqrt: 1 argument, the square root of the argument
    abs: 1 argument, the absolute value of the argument
    floor: 1 argument, the largest integer not greater than the argument
    round: 1 argument, the nearest integer, with ties rounded away from zero
    ceil: 1 argument, the smallest integer not less than the argument
    min: 2 arguments, min(x, y) returns y if (x > y), x otherwise
    max: 2 arguments, max(x, y) returns y if (x < y), x otherwise
    mod: 2 arguments, mod(x, y) = x - y * floor(x / y), or 0 if y == 0
    clamp: 3 arguments, clamp(x, low, high) = min(max(x, low), high)
    .
    
    Args:
        params: The parameters.
        runner: Command runner.
    Returns:
        NamedTuple of outputs (described in `MetricMathOutputs`).
    """
    metric_math_validate(params)
    runner = runner or get_global_runner()
    execution = runner.start_execution(METRIC_MATH_METADATA)
    params = execution.params(params)
    cargs = metric_math_cargs(params, execution)
    ret = metric_math_outputs(params, execution)
    execution.run(cargs)
    return ret


def metric_math(
    expression: str,
    metric_out: str,
    var: list[MetricMathVarParamsDict] | None = None,
    replace: float | None = None,
    runner: Runner | None = None,
) -> MetricMathOutputs:
    """
    EVALUATE EXPRESSION ON METRIC FILES.
    
    This command evaluates <expression> at each surface vertex independently.
    There must be at least one -var option (to get the structure, number of
    vertices, and number of columns from), even if the <name> specified in it
    isn't used in <expression>. All metrics must have the same number of
    vertices. Filenames are not valid in <expression>, use a variable name and a
    -var option with matching <name> to specify an input file. If the -column
    option is given to any -var option, only one column is used from that file.
    If -repeat is specified, the file must either have only one column, or have
    the -column option specified. All files that don't use -repeat must have the
    same number of columns requested to be used. The format of <expression> is
    as follows:
    
    Expressions consist of constants, variables, operators, parentheses, and
    functions, in infix notation, such as 'exp(-x + 3) * scale'. Variables are
    strings of any length, using the characters a-z, A-Z, 0-9, and _, but may
    not take the name of a named constant. Currently, there is only one named
    constant, PI. The operators are +, -, *, /, ^, >, <, >=, <=, ==, !=, !, &&,
    ||. These behave as in C, except that ^ is exponentiation, i.e. pow(x, y),
    and takes higher precedence than other binary operators (also, '-3^-4^-5'
    means '-(3^(-(4^-5)))'). The <=, >=, ==, and != operators are given a small
    amount of wiggle room, equal to one millionth of the smaller of the absolute
    values of the values being compared.
    
    Comparison and logical operators return 0 or 1, you can do masking with
    expressions like 'x * (mask > 0)'. For all logical operators, an input is
    considered true iff it is greater than 0. The expression '0 < x < 5' is not
    syntactically wrong, but it will NOT do what is desired, because it is
    evaluated left to right, i.e. '((0 < x) < 5)', which will always return 1,
    as both possible results of a comparison are less than 5. A warning is
    generated if an expression of this type is detected. Use something like 'x >
    0 && x < 5' to get the desired behavior.
    
    Whitespace between elements is ignored, ' sin ( 2 * x ) ' is equivalent to
    'sin(2*x)', but 's in(2*x)' is an error. Implied multiplication is not
    allowed, the expression '2x' will be parsed as a variable. Parentheses are
    (), do not use [] or {}. Functions require parentheses, the expression 'sin
    x' is an error.
    
    The following functions are supported:
    
    sin: 1 argument, the sine of the argument (units are radians)
    cos: 1 argument, the cosine of the argument (units are radians)
    tan: 1 argument, the tangent of the argument (units are radians)
    asin: 1 argument, the inverse of sine of the argument, in radians
    acos: 1 argument, the inverse of cosine of the argument, in radians
    atan: 1 argument, the inverse of tangent of the argument, in radians
    atan2: 2 arguments, atan2(y, x) returns the inverse of tangent of (y/x), in
    radians, determining quadrant by the sign of both arguments
    sinh: 1 argument, the hyperbolic sine of the argument
    cosh: 1 argument, the hyperbolic cosine of the argument
    tanh: 1 argument, the hyperbolic tangent of the argument
    asinh: 1 argument, the inverse hyperbolic sine of the argument
    acosh: 1 argument, the inverse hyperbolic cosine of the argument
    atanh: 1 argument, the inverse hyperbolic tangent of the argument
    sinc: 1 argument, sinc(0) = 1, sin(x) / x otherwise
    ln: 1 argument, the natural logarithm of the argument
    exp: 1 argument, the constant e raised to the power of the argument
    log: 1 argument, the base 10 logarithm of the argument
    log2: 1 argument, the base 2 logarithm of the argument
    sqrt: 1 argument, the square root of the argument
    abs: 1 argument, the absolute value of the argument
    floor: 1 argument, the largest integer not greater than the argument
    round: 1 argument, the nearest integer, with ties rounded away from zero
    ceil: 1 argument, the smallest integer not less than the argument
    min: 2 arguments, min(x, y) returns y if (x > y), x otherwise
    max: 2 arguments, max(x, y) returns y if (x < y), x otherwise
    mod: 2 arguments, mod(x, y) = x - y * floor(x / y), or 0 if y == 0
    clamp: 3 arguments, clamp(x, low, high) = min(max(x, low), high)
    .
    
    Args:
        expression: the expression to evaluate, in quotes.
        metric_out: the output metric.
        var: a metric to use as a variable.
        replace: replace NaN results with a value\
            \
            value to replace NaN with.
        runner: Command runner.
    Returns:
        NamedTuple of outputs (described in `MetricMathOutputs`).
    """
    params = metric_math_params(
        expression=expression,
        metric_out=metric_out,
        var=var,
        replace=replace,
    )
    return metric_math_execute(params, runner)


__all__ = [
    "METRIC_MATH_METADATA",
    "MetricMathOutputs",
    "MetricMathParamsDict",
    "MetricMathParamsDictTagged",
    "MetricMathVarParamsDict",
    "MetricMathVarParamsDictTagged",
    "metric_math",
    "metric_math_execute",
    "metric_math_params",
    "metric_math_var",
]
