calc
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © kaigouthro

//@version=5

// @description Library for math functions. will expand over time.
library("calc")


// =============================================================================
// misc

// @function Split a large number into integer sized chunks
// @param _sumTotal     (int) Total numbert of items
// @param _divideBy     (int) Groups to make
// @param _forceMinimum (bool) force minimum number 1/group
// @param _haltOnError  (bool) force error if too few groups
// @returns int[] array of items per group
export  split( int _sumTotal,  int _divideBy, bool _forceMinimum = true, bool _haltOnError = false)=>
    varip _counts = array.new<int>(_divideBy)
    switch
        _sumTotal < _divideBy =>
            switch
                _haltOnError  => runtime.error('Size < Divisions')
                _forceMinimum => array.fill(_counts,1)
                =>               array.fill(_counts,1)
        _sumTotal % _divideBy == 0 =>
            for i = 0 to _divideBy - 1
                array.set(_counts,i,_sumTotal/_divideBy)
        =>
            _base  = _divideBy - (_sumTotal % _divideBy)
            _fl    = math.floor(_sumTotal/_divideBy)
            for i = 0 to _divideBy -1
                if ( i >= _base)
                    array.set(_counts, i , _fl + 1)
                else
                    array.set(_counts, i , _fl )
    _counts



// @function Absolute distance between any two float values, agnostic to +/-
// @param _value1 First value
// @param _value2 Second value
// @returns Absolute Positive Distance
export  gapSize(float _value1, float __value2) =>
    varip float _top = na
    varip float _bot = na
    varip float _out = na
    _top  := math.max(_value1,__value2)
    _bot  := math.min(_value1,__value2)
    _out  := switch
        _bot > 0 or _top < 0 => _top - _bot
        =>  _top +  0 - _bot

// test demo
_value1  = input.float(1    ,step=0.25)
// _value2  = input.float(-1.5 ,step=0.25)
// plot(gapSize(_value1,_value2))


// =============================================================================
// fractions (as of lib revision 5, very fast. using Richards algo.)

// @function Helper
greatestDenom(_numerator, _denominator)=>
    _greatestCommon = _numerator
    _den  = _denominator
    _swap = _den
    while _den != 0
        _greatestCommon %= _den
        _swap  := _den
        _den   := _greatestCommon
        _greatestCommon := _swap
    _greatestCommon

//function Simplify fraction to lowest form
// @param _num (int) Input fraction numerator
// @param _den (int) Input fraction dennominator
// @returns Simplified Fraction
export  simplifyFraction(int _num, int _den)=>
    common     = greatestDenom(_num, _den)
    _numLowest = int(_num / common)
    _denLowest = int(_den / common)
    [_numLowest,_denLowest]

// @function Convert Decimal to tuple fraction Output, 1/0 resolves to [1,1]
// note :   it is suboptimal, will be updating with a much faster algorithm
// @param  _input Decimal Input
// @param  _epsilon (OPTIONAL) to precision (10e-N)
// @param  _iterations Maximum iterations (OPTIONAL)
// @returns [num,den] Simplified Fraction (if negative, Numerator gets the sign)
export  toFraction(float _source, int _epsilon = 6, int _iterations = 20) =>
    switch
        _source == int(_source) => [int(_source),1]
        na (_source) => [0 , 1]
        =>
            _d = array.new<int>(_iterations+2,0)
            _num = math.abs(_source)
            array.set(_d,1,1)
            _z = _num, _n = 1
            _t = 1
            while _num and _t < _iterations and math.abs(_n/array.get(_d,_t) - _num) > math.pow(10, -_epsilon)
                _t += 1
                _z := 1/(_z - int(_z))
                array.set(_d,_t,array.get(_d,_t-1) * int(_z) + array.get(_d,_t-2))
                _n := int(_num * array.get(_d,_t) + 0.5)
            simplifyFraction(int(math.sign(_source)*_n),int(array.get(_d,_t)))

//@function Measure percent (0.##) of Distance between two points
// @param  signal    value to check
// @param  _startVal first value limit
// @param  _endVal   second value limit
export  percentOfDistance(float signal, float _startVal, float _endVal) =>
    varip _max = 0.,varip _min = 0.,varip _signal   = 0.
    varip _num = 0.,varip _den = 0.,varip _position = 0.
    _max      := math.max( _startVal   , _endVal   )
    _min      := math.min( _startVal   , _endVal   )
    _signal   := math.max( _min        ,  math.min ( _max, signal ) )
    _num      := gapSize ( _signal     , _startVal )
    _den      := gapSize ( _endVal     , _startVal )
    _position := switch
        _num == 0 or _den == 0 => 0
        => _num / _den

//@function Measure percent (0.##) of Distance between two points
// @param  signal    value to check
// @param  _startVal first value limit
// @param  _endVal   second value limit
// @returns [Num,Denom] fraction
export  fractionOfDistance(float signal, float _startVal, float _endVal) =>
    toFraction(percentOfDistance(signal,_startVal,_endVal))

//@function Power of 10 scale up
//@param   _src Input value
//@param   _pow Number of * 10 to perform
//@returns Value with decimal moved + right, - left
export  pow10 (float _src, int _pow = 2) =>
    switch _pow
        0 => _src
        =>_src * math.pow(10,math.max(1,_pow))

//@function Power of 10 to req to integer
//@param _src Input value) =>
//@returns Decimals required to reach integer from minimum tick. (useful for for non ohlc vals)
export  pow10chk(float _src) =>
    varip _p = 1, varip _v = float(na)
    _v := math.round_to_mintick(_src)
    while _v != int(_v)
        _p += 1
        _v *= 10

    _p

// @function Measure a source distance from a mid point as +/- 1.0 being he furhest distance the have been
// @param _src Input value
// @param _mid The mid point to Measure fron
export from_center(float _src, float _mid = 0 )    =>
    var float _limit  = 0
    _limit :=  math.max(_limit, gapSize(_src,_mid))
    percentOfDistance(gapSize(_src,_mid) , 0, _limit) * math.sign(_src-_mid)

