@use 'sass:list';
@use 'sass:meta';
////
/// @group utils.math
////

@use 'sass:math' as math;

/// Calculate a number raised to the power of the provided exponent. (Wrapper for math.pow)
/// @param {*} $base The base number
/// @param {*} $exp The exponent used to raise the base
/// @example pow(2, 3)

@function pow($base, $exp) {
    @return math.pow($base, $exp);
}

/// Calculates the nth root of a number using iteration.
/// @param {*} $value
/// @param {*} $n

@function nth-root($value, $n) {
    $precision: 1e-10;
    $previous: 0;
    $current: $value;

    @while math.abs($current - $previous) > $precision {
        $previous: $current;
        $current: divide(1, $n) * (($n - 1) * $previous + divide($value, pow($previous, $n - 1)));
    }

    @return $current;
}

/// Round a number using the specified number of decimals.
/// @param {number} $n The number to be rounded
/// @param {number} $dec [2] The number of decimals in the output
///
/// @example $val: round-d(3.33333333, 2) // => 3.33

@function round-d($n, $dec: 2) {
    $p: pow(10, $dec);
    $r: math.round($n * $p);
    @return divide(math.round($n * $p), $p);
}

/// Round a number with the specified fraction (e.g. 4 will produce 1.0, 1.25, 1.5, etc.)
/// @param {number} $n The number to be rounded
/// @param {number} $frac [8] The denominator of the fractional portion of the result
///
/// @example  $val: round-f(3.3333, 2) // => 3.5

@function round-f($n, $frac: 8) {
    @if ($frac) {
        @return divide(math.round($n * $frac), $frac);
    } @else {
        @return math.round($n);
    }
}

/// Interpolate between two values.
///
/// @param {number} $prog The progress between the two values, provided as a value between 0 and 1.
///
/// @param {number} $range The range of interpolation. This can be provided as a single value which
/// represents the upper range from 0 or as two values which represent the lower and upper range.
///
/// @param {string} $ease A reference to an easing function. See the ease function for details
///
/// @example
/// intp(0.5, 4) // => 2
///
/// @example
/// intp(0.5, (1, 3) // => 2

@function intp($prog, $range, $ease: false, $round: false) {
    $from: 0;
    $to: $range;

    @if (meta.type-of($to) == 'list') {
        $from: list.nth($to, 1);
        $to: list.nth($to, 2);
    }

    @if ($ease) {
        $prog: ease($prog, $ease);
    }

    $result: $from + ($prog * ($to - $from));

    @if ($round) {
        @return round-f($result, $round);
    }
    @return $result;
}

// Easing functions based on these normalized versions of Penner:
// https://github.com/CharlotteGore/functional-easing/blob/master/penner-easing.js

/// Interpolate a value between 0 and 1 using the easing function with the given name
/// @param {number} $t The input value between 0 and 1
/// @param {String} $easing The name of the easing function that should be used for interpolation
/// @return {number} The eased equivalent of the input (also between 0 and 1).
///
/// @example  ease(0.5, 'out-quad') // => 0.75

@function ease($t, $easing: 'out-quad') {
    @if $easing == 'in-quad' {
        @return ease-in-quad($t);
    }
    @if $easing == 'out-quad' {
        @return ease-out-quad($t);
    }
    @if $easing == 'in-quart' {
        @return ease-in-quart($t);
    }
    @if $easing == 'out-quart' {
        @return ease-out-quart($t);
    }
    @if $easing == 'in-cubic' {
        @return ease-in-cubic($t);
    }
    @if $easing == 'out-cubic' {
        @return ease-out-cubic($t);
    }
    @return $t;
}

/// Interpolate a value between 0 and 1 using the Penner ease-in quad function.
/// @param {number} $t The input value between 0 and 1
/// @return {number} The eased equivalent of the input (also between 0 and 1).
///
/// @example  ease-in-quad(0.5) // => 0.25

@function ease-in-quad($t) {
    @return $t * $t;
}

/// Interpolate a value between 0 and 1 using the Penner ease-out quad function.
/// @param {number} $t The input value between 0 and 1
/// @return {number} The eased equivalent of the input (also between 0 and 1).
///
/// @example  ease-out-quad(0.25) // => 0.4375;

@function ease-out-quad($t) {
    @return -1 * $t * ($t - 2);
}

/// Interpolate a value between 0 and 1 using the Penner ease-in cubic function.
/// @param {number} $t The input value between 0 and 1
/// @return {number} The eased equivalent of the input (also between 0 and 1).
///
/// @example  ease-in-cubic(0.5) // => 0.125;

@function ease-in-cubic($t) {
    @return $t * $t * $t;
}

/// Interpolate a value between 0 and 1 using the Penner ease-out cubic function.
/// @param {number} $t The input value between 0 and 1
/// @return {number} The eased equivalent of the input (also between 0 and 1).
///
/// @example  ease-out-cubic(0.25) // => 0.578125

@function ease-out-cubic($t) {
    $t: $t - 1;
    @return $t * $t * $t + 1;
}

/// Interpolate a value between 0 and 1 using the Penner ease-in quart function.
/// @param {number} $t The input value between 0 and 1
/// @return {number} The eased equivalent of the input (also between 0 and 1).
///
/// @example  ease-in-quart(0.3) // => 0.0081

@function ease-in-quart($t) {
    @return $t * $t * $t * $t;
}

/// Interpolate a value between 0 and 1 using the Penner ease-out quart function.
/// @param {number} $t The input value between 0 and 1
/// @return {number} The eased equivalent of the input (also between 0 and 1).
///
/// @example  ease-out-quart(0.5) // => 0.9375

@function ease-out-quart($t) {
    $t: $t - 1;
    @return -1 * ($t * $t * $t * $t - 1);
}

/// Clamp a number (keep it within the confines of min/max). Note: the weird
/// name is to differentiate from the clamp() css function.
///
/// @param {*} $number The number to be clampled
/// @param {*} $min Min allowed value
/// @param {*} $max Max allowed value
///
/// @example clamp-number(4, 3, 10) // => 4 is above the min

@function clamp-number($number, $min, $max) {
    @return min(max($number, $min), $max);
}

/// Provides a wrapper for the standard sass divide function (math.div) to make imports a little
/// simpler. Returns the result of dividing $num1 by $num2.
///
/// @param {number} $num1 The first number in the division operation (the numerator)
/// @param {number} $num2 The second number in the division operation (the denominator)
///
/// @example  divide(10, 2) // => 5

@function divide($num1, $num2) {
    @return math.div($num1, $num2);
}

/// Convience function that divides two numbers and the multiples the result by 100%
///
/// @param {number} $num1 The first number in the division operation (the numerator)
/// @param {number} $num2 The first number in the division operation (the numerator)
/// @param {number} $round The number of digits to use when rounding the output
///
/// @example  percent(10, 3, 2) // => 333.33%

@function percent($num1, $num2, $round: 3) {
    @return round-d(divide($num1, $num2) * 100%, $round);
}
