

@function __math-pow($number, $exp) {
    @if (round($exp) != $exp) {
      @return __math-exp($exp * __math-ln($number));
    }
    
    // Traditional method for integers
    $value: 1;
    
    @if $exp > 0 {
        @for $i from 1 through $exp {
           $value: $value * $number;
        }
    }
    @else if $exp < 0 {
        @for $i from 1 through -$exp {
            $value: $value / $number;
        }
    }

    @return $value;
}


@function __math-factorial($value) {
  @if $value == 0 {
    @return 1;
  }
  
  $result: 1;
  
  @for $index from 1 through $value {
    $result: $result * $index;
  }
  
  @return $result;
}


@function __math-summation($iteratee, $input, $initial: 0, $limit: 100) {
  $sum: 0;
  
  @for $index from $initial to $limit {
    $sum: $sum + call($iteratee, $input, $index);
  }
  
  @return $sum;
}


@function __math-exp-maclaurin($x, $n) {
  $result: __math-pow($x, $n) / __math-factorial($n);
  @return $result;
}


@function __math-exp($value) {
  $result: __math-summation(__math-exp-maclaurin, $value, 0, 100);
  @return $result;
}


@function __math-ln-maclaurin($x, $n) {
  $result: (__math-pow(-1, $n + 1) / $n) * (__math-pow($x - 1, $n));
  @return $result;
}


@function __math-ln($value) {
  $ten-exp: 1;
  $ln-ten: 2.30258509;
  
  @while ($value > __math-pow(10, $ten-exp)) {
    $ten-exp: $ten-exp + 1;
  }
  
  $value: $value / __math-pow(10, $ten-exp);

  $result: __math-summation(__math-ln-maclaurin, $value, 1, 100);
  
  @return $result + $ten-exp * $ln-ten;
}


@function __number-to-fixed($value, $digits: null) {
    @if $digits == null {
        @return round($value);
    }

    @return round($value * __math-pow(10, $digits)) / __math-pow(10, $digits);
}


@function __unit($value, $args...) {
    $units: __const('UNITS');

    @if __is-number($value) {
        @return unit($value);
    } @else if __is-string($value) {
        @each $string-unit, $number-unit in $units {
            @if __ends-with($value, $string-unit) {
                @return $number-unit;
            }
        }
    }

    @return 1;
}

@function __unit-group-predicate($unit, $group, $args...) {
  @return __includes($group, $unit);
}
@function __unit-group($value, $args...) {
    @return __find-key(__const('UNIT_GROUPS'), __partial(__unit-group-predicate, $value));
}


@function __parse-float($string, $radix: 10) {
    @if type-of($string) != 'string' {
        @return $string;
    }

    @if __is-falsey($string) {
        @return 0;
    }

    $string: __trim($string);
    $result: 0;
    $exponent: 0;
    $decimal: false;
    $multiplier: if(__starts-with($string, '-'), -1, 1);

    $char-min: __get-char-code(0);
    $char-max: $char-min + 9;
    $char-decimal: __get-char-code('.');
    
    @each $char-code in __map($string, '__get-char-code') {
        @if $char-code >= $char-min and $char-code <= $char-max {
            $result: ($radix * $result) + ($char-code - $char-min);

            @if $decimal {
                $exponent: $exponent + 1;
            }
        } @else if $char-code == $char-decimal {
            $decimal: true;
        }
    }

    $unit: __unit($string);

    @return $multiplier * $result / __math-pow($radix, $exponent) * $unit;
}


@function _parse-float($args...) {
    @return call(get-function('__parse-float'), $args...);
}


@function _to-number($args...) {
    @return call(get-function('__parse-float'), $args...);
}


@function _pow($args...) {
    @return call(get-function('__math-pow'), $args...);
}
