Find this useful? Enter your email to receive occasional updates for securing PHP code.

Signing you up...

Thank you for signing up!

PHP Decode

<?php /** * This is a PHP port of the Trader extension for PHP, which is a port of the T..

Decoded Output download

<?php

/**
 * This is a PHP port of the Trader extension for PHP, which is a port of the TA-LIB C code.
 *
 * This port is written in PHP and without any other requirements.
 * The goal is that this library can be used by those whom cannot install the PHP Trader extension.
 *
 * Below is the copyright information for TA-LIB found in the source code.
 */

/* TA-LIB Copyright (c) 1999-2007, Mario Fortier
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or
 * without modification, are permitted provided that the following
 * conditions are met:
 *
 * - Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in
 *   the documentation and/or other materials provided with the
 *   distribution.
 *
 * - Neither name of author nor the names of its contributors
 *   may be used to endorse or promote products derived from this
 *   software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

namespace LupeCode\phpTraderNative\TALib\Core;

use LupeCode\phpTraderNative\TALib\Classes\MoneyFlow;
use LupeCode\phpTraderNative\TALib\Enum\Compatibility;
use LupeCode\phpTraderNative\TALib\Enum\ReturnCode;
use LupeCode\phpTraderNative\TALib\Enum\UnstablePeriodFunctionID;

class MomentumIndicators extends Core
{
    public static function adx(int $startIdx, int $endIdx, array $inHigh, array $inLow, array $inClose, int $optInTimePeriod, int &$outBegIdx, int &$outNBElement, array &$outReal): int
    {
        if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) {
            return $RetCode;
        }
        if ($optInTimePeriod === PHP_INT_MIN) {
            $optInTimePeriod = 14;
        } elseif ($optInTimePeriod < 2 || $optInTimePeriod > 100000) {
            return ReturnCode::BadParam;
        }
        $lookbackTotal = 2 * $optInTimePeriod + static::$unstablePeriod[UnstablePeriodFunctionID::ADX] - 1;
        if ($startIdx < $lookbackTotal) {
            $startIdx = (int)$lookbackTotal;
        }
        if ($startIdx > $endIdx) {
            $outBegIdx = 0;
            $outNBElement = 0;

            return ReturnCode::Success;
        }
        $outBegIdx = $today = $startIdx;
        $prevMinusDM = 0.0;
        $prevPlusDM = 0.0;
        $prevTR = 0.0;
        $today = $startIdx - $lookbackTotal;
        $prevHigh = $inHigh[$today];
        $prevLow = $inLow[$today];
        $prevClose = $inClose[$today];
        $i = $optInTimePeriod - 1;
        while ($i-- > 0) {
            $today++;
            $tempReal = $inHigh[$today];
            $diffP = $tempReal - $prevHigh;
            $prevHigh = $tempReal;
            $tempReal = $inLow[$today];
            $diffM = $prevLow - $tempReal;
            $prevLow = $tempReal;
            if ($diffM > 0 && $diffP < $diffM) {
                $prevMinusDM += $diffM;
            } elseif ($diffP > 0 && $diffP > $diffM) {
                $prevPlusDM += $diffP;
            }
            {
                $tempReal = $prevHigh - $prevLow;
                $tempReal2 = abs($prevHigh - $prevClose);
                if ($tempReal2 > $tempReal) {
                    $tempReal = $tempReal2;
                }
                $tempReal2 = abs($prevLow - $prevClose);
                if ($tempReal2 > $tempReal) {
                    $tempReal = $tempReal2;
                }
            }
            $prevTR += $tempReal;
            $prevClose = $inClose[$today];
        }
        $sumDX = 0.0;
        $i = $optInTimePeriod;
        while ($i-- > 0) {
            $today++;
            $tempReal = $inHigh[$today];
            $diffP = $tempReal - $prevHigh;
            $prevHigh = $tempReal;
            $tempReal = $inLow[$today];
            $diffM = $prevLow - $tempReal;
            $prevLow = $tempReal;
            $prevMinusDM -= $prevMinusDM / $optInTimePeriod;
            $prevPlusDM -= $prevPlusDM / $optInTimePeriod;
            if ($diffM > 0 && $diffP < $diffM) {
                $prevMinusDM += $diffM;
            } elseif ($diffP > 0 && $diffP > $diffM) {
                $prevPlusDM += $diffP;
            }
            {
                $tempReal = $prevHigh - $prevLow;
                $tempReal2 = abs($prevHigh - $prevClose);
                if ($tempReal2 > $tempReal) {
                    $tempReal = $tempReal2;
                }
                $tempReal2 = abs($prevLow - $prevClose);
                if ($tempReal2 > $tempReal) {
                    $tempReal = $tempReal2;
                }
            }
            $prevTR = $prevTR - $prevTR / $optInTimePeriod + $tempReal;
            $prevClose = $inClose[$today];
            if (!(-0.00000001 < $prevTR && $prevTR < 0.00000001)) {
                $minusDI = 100.0 * ($prevMinusDM / $prevTR);
                $plusDI = 100.0 * ($prevPlusDM / $prevTR);
                $tempReal = $minusDI + $plusDI;
                if (!(-0.00000001 < $tempReal && $tempReal < 0.00000001)) {
                    $sumDX += 100.0 * (abs($minusDI - $plusDI) / $tempReal);
                }
            }
        }
        $prevADX = $sumDX / $optInTimePeriod;
        $i = static::$unstablePeriod[UnstablePeriodFunctionID::ADX];
        while ($i-- > 0) {
            $today++;
            $tempReal = $inHigh[$today];
            $diffP = $tempReal - $prevHigh;
            $prevHigh = $tempReal;
            $tempReal = $inLow[$today];
            $diffM = $prevLow - $tempReal;
            $prevLow = $tempReal;
            $prevMinusDM -= $prevMinusDM / $optInTimePeriod;
            $prevPlusDM -= $prevPlusDM / $optInTimePeriod;
            if ($diffM > 0 && $diffP < $diffM) {
                $prevMinusDM += $diffM;
            } elseif ($diffP > 0 && $diffP > $diffM) {
                $prevPlusDM += $diffP;
            }
            {
                $tempReal = $prevHigh - $prevLow;
                $tempReal2 = abs($prevHigh - $prevClose);
                if ($tempReal2 > $tempReal) {
                    $tempReal = $tempReal2;
                }
                $tempReal2 = abs($prevLow - $prevClose);
                if ($tempReal2 > $tempReal) {
                    $tempReal = $tempReal2;
                }
            }
            $prevTR = $prevTR - $prevTR / $optInTimePeriod + $tempReal;
            $prevClose = $inClose[$today];
            if (!(-0.00000001 < $prevTR && $prevTR < 0.00000001)) {
                $minusDI = 100.0 * ($prevMinusDM / $prevTR);
                $plusDI = 100.0 * ($prevPlusDM / $prevTR);
                $tempReal = $minusDI + $plusDI;
                if (!(-0.00000001 < $tempReal && $tempReal < 0.00000001)) {
                    $tempReal = 100.0 * (abs($minusDI - $plusDI) / $tempReal);
                    $prevADX = ($prevADX * ($optInTimePeriod - 1) + $tempReal) / $optInTimePeriod;
                }
            }
        }
        $outReal[0] = $prevADX;
        $outIdx = 1;
        while ($today < $endIdx) {
            $today++;
            $tempReal = $inHigh[$today];
            $diffP = $tempReal - $prevHigh;
            $prevHigh = $tempReal;
            $tempReal = $inLow[$today];
            $diffM = $prevLow - $tempReal;
            $prevLow = $tempReal;
            $prevMinusDM -= $prevMinusDM / $optInTimePeriod;
            $prevPlusDM -= $prevPlusDM / $optInTimePeriod;
            if ($diffM > 0 && $diffP < $diffM) {
                $prevMinusDM += $diffM;
            } elseif ($diffP > 0 && $diffP > $diffM) {
                $prevPlusDM += $diffP;
            }
            {
                $tempReal = $prevHigh - $prevLow;
                $tempReal2 = abs($prevHigh - $prevClose);
                if ($tempReal2 > $tempReal) {
                    $tempReal = $tempReal2;
                }
                $tempReal2 = abs($prevLow - $prevClose);
                if ($tempReal2 > $tempReal) {
                    $tempReal = $tempReal2;
                }
            }
            $prevTR = $prevTR - $prevTR / $optInTimePeriod + $tempReal;
            $prevClose = $inClose[$today];
            if (!(-0.00000001 < $prevTR && $prevTR < 0.00000001)) {
                $minusDI = 100.0 * ($prevMinusDM / $prevTR);
                $plusDI = 100.0 * ($prevPlusDM / $prevTR);
                $tempReal = $minusDI + $plusDI;
                if (!(-0.00000001 < $tempReal && $tempReal < 0.00000001)) {
                    $tempReal = 100.0 * (abs($minusDI - $plusDI) / $tempReal);
                    $prevADX = ($prevADX * ($optInTimePeriod - 1) + $tempReal) / $optInTimePeriod;
                }
            }
            $outReal[$outIdx++] = $prevADX;
        }
        $outNBElement = $outIdx;

        return ReturnCode::Success;
    }

    public static function adxr(int $startIdx, int $endIdx, array $inHigh, array $inLow, array $inClose, int $optInTimePeriod, int &$outBegIdx, int &$outNBElement, array &$outReal): int
    {
        if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) {
            return $RetCode;
        }
        if ($optInTimePeriod === PHP_INT_MIN) {
            $optInTimePeriod = 14;
        } elseif ($optInTimePeriod < 2 || $optInTimePeriod > 100000) {
            return ReturnCode::BadParam;
        }
        $adxrLookback = Lookback::adxrLookback($optInTimePeriod);
        if ($startIdx < $adxrLookback) {
            $startIdx = $adxrLookback;
        }
        if ($startIdx > $endIdx) {
            $outBegIdx = 0;
            $outNBElement = 0;

            return ReturnCode::Success;
        }
        $adx = static::double($endIdx - $startIdx + $optInTimePeriod);
        $ReturnCode = self::adx($startIdx - ($optInTimePeriod - 1), $endIdx, $inHigh, $inLow, $inClose, $optInTimePeriod, $outBegIdx, $outNBElement, $adx);
        if ($ReturnCode !== ReturnCode::Success) {
            return $ReturnCode;
        }
        $i = $optInTimePeriod - 1;
        $j = 0;
        $outIdx = 0;
        $nbElement = $endIdx - $startIdx + 2;
        while (--$nbElement > 0) {
            $outReal[$outIdx++] = ($adx[$i++] + $adx[$j++]) / 2.0;
        }
        $outBegIdx = $startIdx;
        $outNBElement = $outIdx;

        return ReturnCode::Success;
    }

    public static function apo(int $startIdx, int $endIdx, array $inReal, int $optInFastPeriod, int $optInSlowPeriod, int $optInMAType, int &$outBegIdx, int &$outNBElement, array &$outReal): int
    {
        if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) {
            return $RetCode;
        }
        if ($optInFastPeriod === PHP_INT_MIN) {
            $optInFastPeriod = 12;
        } elseif ($optInFastPeriod < 2 || $optInFastPeriod > 100000) {
            return ReturnCode::BadParam;
        }
        if ($optInSlowPeriod === PHP_INT_MIN) {
            $optInSlowPeriod = 26;
        } elseif ($optInSlowPeriod < 2 || $optInSlowPeriod > 100000) {
            return ReturnCode::BadParam;
        }
        $tempBuffer = static::double($endIdx - $startIdx + 1);

        return static::TA_INT_PO($startIdx, $endIdx, $inReal, $optInFastPeriod, $optInSlowPeriod, $optInMAType, $outBegIdx, $outNBElement, $outReal, $tempBuffer, false);
    }

    public static function aroon(int $startIdx, int $endIdx, array $inHigh, array $inLow, int $optInTimePeriod, int &$outBegIdx, int &$outNBElement, array &$outAroonDown, array &$outAroonUp): int
    {
        if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) {
            return $RetCode;
        }
        if ($optInTimePeriod === PHP_INT_MIN) {
            $optInTimePeriod = 14;
        } elseif ($optInTimePeriod < 2 || $optInTimePeriod > 100000) {
            return ReturnCode::BadParam;
        }
        if ($startIdx < $optInTimePeriod) {
            $startIdx = $optInTimePeriod;
        }
        if ($startIdx > $endIdx) {
            $outBegIdx = 0;
            $outNBElement = 0;

            return ReturnCode::Success;
        }
        $outIdx = 0;
        $today = $startIdx;
        $trailingIdx = $startIdx - $optInTimePeriod;
        $lowestIdx = -1;
        $highestIdx = -1;
        $lowest = 0.0;
        $highest = 0.0;
        $factor = 100.0 / (double)$optInTimePeriod;
        while ($today <= $endIdx) {
            $tmp = $inLow[$today];
            if ($lowestIdx < $trailingIdx) {
                $lowestIdx = $trailingIdx;
                $lowest = $inLow[$lowestIdx];
                $i = $lowestIdx;
                while (++$i <= $today) {
                    $tmp = $inLow[$i];
                    if ($tmp <= $lowest) {
                        $lowestIdx = $i;
                        $lowest = $tmp;
                    }
                }
            } elseif ($tmp <= $lowest) {
                $lowestIdx = $today;
                $lowest = $tmp;
            }
            $tmp = $inHigh[$today];
            if ($highestIdx < $trailingIdx) {
                $highestIdx = $trailingIdx;
                $highest = $inHigh[$highestIdx];
                $i = $highestIdx;
                while (++$i <= $today) {
                    $tmp = $inHigh[$i];
                    if ($tmp >= $highest) {
                        $highestIdx = $i;
                        $highest = $tmp;
                    }
                }
            } elseif ($tmp >= $highest) {
                $highestIdx = $today;
                $highest = $tmp;
            }
            $outAroonUp[$outIdx] = $factor * ($optInTimePeriod - ($today - $highestIdx));
            $outAroonDown[$outIdx] = $factor * ($optInTimePeriod - ($today - $lowestIdx));
            $outIdx++;
            $trailingIdx++;
            $today++;
        }
        $outBegIdx = $startIdx;
        $outNBElement = $outIdx;

        return ReturnCode::Success;
    }

    public static function aroonOsc(int $startIdx, int $endIdx, array $inHigh, array $inLow, int $optInTimePeriod, int &$outBegIdx, int &$outNBElement, array &$outReal): int
    {
        if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) {
            return $RetCode;
        }
        if ($optInTimePeriod === PHP_INT_MIN) {
            $optInTimePeriod = 14;
        } elseif ($optInTimePeriod < 2 || $optInTimePeriod > 100000) {
            return ReturnCode::BadParam;
        }
        if ($startIdx < $optInTimePeriod) {
            $startIdx = $optInTimePeriod;
        }
        if ($startIdx > $endIdx) {
            $outBegIdx = 0;
            $outNBElement = 0;

            return ReturnCode::Success;
        }
        $outIdx = 0;
        $today = $startIdx;
        $trailingIdx = $startIdx - $optInTimePeriod;
        $lowestIdx = -1;
        $highestIdx = -1;
        $lowest = 0.0;
        $highest = 0.0;
        $factor = 100.0 / (double)$optInTimePeriod;
        while ($today <= $endIdx) {
            $tmp = $inLow[$today];
            if ($lowestIdx < $trailingIdx) {
                $lowestIdx = $trailingIdx;
                $lowest = $inLow[$lowestIdx];
                $i = $lowestIdx;
                while (++$i <= $today) {
                    $tmp = $inLow[$i];
                    if ($tmp <= $lowest) {
                        $lowestIdx = $i;
                        $lowest = $tmp;
                    }
                }
            } elseif ($tmp <= $lowest) {
                $lowestIdx = $today;
                $lowest = $tmp;
            }
            $tmp = $inHigh[$today];
            if ($highestIdx < $trailingIdx) {
                $highestIdx = $trailingIdx;
                $highest = $inHigh[$highestIdx];
                $i = $highestIdx;
                while (++$i <= $today) {
                    $tmp = $inHigh[$i];
                    if ($tmp >= $highest) {
                        $highestIdx = $i;
                        $highest = $tmp;
                    }
                }
            } elseif ($tmp >= $highest) {
                $highestIdx = $today;
                $highest = $tmp;
            }
            $aroon = $factor * ($highestIdx - $lowestIdx);
            $outReal[$outIdx] = $aroon;
            $outIdx++;
            $trailingIdx++;
            $today++;
        }
        $outBegIdx = $startIdx;
        $outNBElement = $outIdx;

        return ReturnCode::Success;
    }

    public static function bop(int $startIdx, int $endIdx, array $inOpen, array $inHigh, array $inLow, array $inClose, int &$outBegIdx, int &$outNBElement, array &$outReal): int
    {
        if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) {
            return $RetCode;
        }
        $outIdx = 0;
        for ($i = $startIdx; $i <= $endIdx; $i++) {
            $tempReal = $inHigh[$i] - $inLow[$i];
            if ($tempReal < 0.00000001) {
                $outReal[$outIdx++] = 0.0;
            } else {
                $outReal[$outIdx++] = ($inClose[$i] - $inOpen[$i]) / $tempReal;
            }
        }
        $outNBElement = $outIdx;
        $outBegIdx = $startIdx;

        return ReturnCode::Success;
    }

    public static function cci(int $startIdx, int $endIdx, array $inHigh, array $inLow, array $inClose, int $optInTimePeriod, int &$outBegIdx, int &$outNBElement, array &$outReal): int
    {
        if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) {
            return $RetCode;
        }
        $circBuffer_Idx = 0;
        if ($optInTimePeriod === PHP_INT_MIN) {
            $optInTimePeriod = 14;
        } elseif ($optInTimePeriod < 2 || $optInTimePeriod > 100000) {
            return ReturnCode::BadParam;
        }
        $lookbackTotal = $optInTimePeriod - 1;
        if ($startIdx < $lookbackTotal) {
            $startIdx = $lookbackTotal;
        }
        if ($startIdx > $endIdx) {
            $outBegIdx = 0;
            $outNBElement = 0;

            return ReturnCode::Success;
        }
        {
            $circBuffer = static::double($optInTimePeriod);
            $maxIdx_circBuffer = $optInTimePeriod - 1;
        }
        $i = $startIdx - $lookbackTotal;
        while ($i < $startIdx) {
            $circBuffer[$circBuffer_Idx] = ($inHigh[$i] + $inLow[$i] + $inClose[$i]) / 3;
            $i++;
            {
                $circBuffer_Idx++;
                if ($circBuffer_Idx > $maxIdx_circBuffer) {
                    $circBuffer_Idx = 0;
                }
            }
        }
        $outIdx = 0;
        do {
            $lastValue = ($inHigh[$i] + $inLow[$i] + $inClose[$i]) / 3;
            $circBuffer[$circBuffer_Idx] = $lastValue;
            $theAverage = 0;
            for ($j = 0; $j < $optInTimePeriod; $j++) {
                $theAverage += $circBuffer[$j];
            }
            $theAverage /= $optInTimePeriod;
            $tempReal2 = 0;
            for ($j = 0; $j < $optInTimePeriod; $j++) {
                $tempReal2 += abs($circBuffer[$j] - $theAverage);
            }
            $tempReal = $lastValue - $theAverage;
            if ($tempReal !== 0.0 && $tempReal2 !== 0.0) {
                $outReal[$outIdx++] = $tempReal / (0.015 * ($tempReal2 / $optInTimePeriod));
            } else {
                $outReal[$outIdx++] = 0.0;
            }
            {
                $circBuffer_Idx++;
                if ($circBuffer_Idx > $maxIdx_circBuffer) {
                    $circBuffer_Idx = 0;
                }
            }
            $i++;
        } while ($i <= $endIdx);
        $outNBElement = $outIdx;
        $outBegIdx = $startIdx;

        return ReturnCode::Success;
    }

    public static function cmo(int $startIdx, int $endIdx, array $inReal, int $optInTimePeriod, int &$outBegIdx, int &$outNBElement, array &$outReal): int
    {
        if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) {
            return $RetCode;
        }
        if ($optInTimePeriod === PHP_INT_MIN) {
            $optInTimePeriod = 14;
        } elseif ($optInTimePeriod < 2 || $optInTimePeriod > 100000) {
            return ReturnCode::BadParam;
        }
        $outBegIdx = 0;
        $outNBElement = 0;
        $lookbackTotal = Lookback::cmoLookback($optInTimePeriod);
        if ($startIdx < $lookbackTotal) {
            $startIdx = $lookbackTotal;
        }
        if ($startIdx > $endIdx) {
            return ReturnCode::Success;
        }
        $outIdx = 0;
        $today = $startIdx - $lookbackTotal;
        $prevValue = $inReal[$today];
        $unstablePeriod = static::$unstablePeriod[UnstablePeriodFunctionID::CMO];
        if ($unstablePeriod === 0 && static::$compatibility === Compatibility::Metastock) {
            $savePrevValue = $prevValue;
            $prevGain = 0.0;
            $prevLoss = 0.0;
            for ($i = $optInTimePeriod; $i > 0; $i--) {
                $tempValue1 = $inReal[$today++];
                $tempValue2 = $tempValue1 - $prevValue;
                $prevValue = $tempValue1;
                if ($tempValue2 < 0) {
                    $prevLoss -= $tempValue2;
                } else {
                    $prevGain += $tempValue2;
                }
            }
            $tempValue1 = $prevLoss / $optInTimePeriod;
            $tempValue2 = $prevGain / $optInTimePeriod;
            $tempValue3 = $tempValue2 - $tempValue1;
            $tempValue4 = $tempValue1 + $tempValue2;
            if (!(-0.00000001 < $tempValue4 && $tempValue4 < 0.00000001)) {
                $outReal[$outIdx++] = 100 * ($tempValue3 / $tempValue4);
            } else {
                $outReal[$outIdx++] = 0.0;
            }
            if ($today > $endIdx) {
                $outBegIdx = $startIdx;
                $outNBElement = $outIdx;

                return ReturnCode::Success;
            }
            $today -= $optInTimePeriod;
            $prevValue = $savePrevValue;
        }
        $prevLoss = 0.0;
        $prevGain = 0.0;
        $today++;
        for ($i = $optInTimePeriod; $i > 0; $i--) {
            $tempValue1 = $inReal[$today++];
            $tempValue2 = $tempValue1 - $prevValue;
            $prevValue = $tempValue1;
            if ($tempValue2 < 0) {
                $prevLoss -= $tempValue2;
            } else {
                $prevGain += $tempValue2;
            }
        }
        $prevLoss /= $optInTimePeriod;
        $prevGain /= $optInTimePeriod;
        if ($today > $startIdx) {
            $tempValue1 = $prevGain + $prevLoss;
            if (!(-0.00000001 < $tempValue1 && $tempValue1 < 0.00000001)) {
                $outReal[$outIdx++] = 100.0 * (($prevGain - $prevLoss) / $tempValue1);
            } else {
                $outReal[$outIdx++] = 0.0;
            }
        } else {
            while ($today < $startIdx) {
                $tempValue1 = $inReal[$today];
                $tempValue2 = $tempValue1 - $prevValue;
                $prevValue = $tempValue1;
                $prevLoss *= $optInTimePeriod - 1;
                $prevGain *= $optInTimePeriod - 1;
                if ($tempValue2 < 0) {
                    $prevLoss -= $tempValue2;
                } else {
                    $prevGain += $tempValue2;
                }
                $prevLoss /= $optInTimePeriod;
                $prevGain /= $optInTimePeriod;
                $today++;
            }
        }
        while ($today <= $endIdx) {
            $tempValue1 = $inReal[$today++];
            $tempValue2 = $tempValue1 - $prevValue;
            $prevValue = $tempValue1;
            $prevLoss *= $optInTimePeriod - 1;
            $prevGain *= $optInTimePeriod - 1;
            if ($tempValue2 < 0) {
                $prevLoss -= $tempValue2;
            } else {
                $prevGain += $tempValue2;
            }
            $prevLoss /= $optInTimePeriod;
            $prevGain /= $optInTimePeriod;
            $tempValue1 = $prevGain + $prevLoss;
            if (!(-0.00000001 < $tempValue1 && $tempValue1 < 0.00000001)) {
                $outReal[$outIdx++] = 100.0 * (($prevGain - $prevLoss) / $tempValue1);
            } else {
                $outReal[$outIdx++] = 0.0;
            }
        }
        $outBegIdx = $startIdx;
        $outNBElement = $outIdx;

        return ReturnCode::Success;
    }

    public static function dx(int $startIdx, int $endIdx, array $inHigh, array $inLow, array $inClose, int $optInTimePeriod, int &$outBegIdx, int &$outNBElement, array &$outReal): int
    {
        if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) {
            return $RetCode;
        }
        if ($optInTimePeriod === PHP_INT_MIN) {
            $optInTimePeriod = 14;
        } elseif ($optInTimePeriod < 2 || $optInTimePeriod > 100000) {
            return ReturnCode::BadParam;
        }
        $lookbackTotal = $optInTimePeriod + static::$unstablePeriod[UnstablePeriodFunctionID::DX];
        if ($startIdx < $lookbackTotal) {
            $startIdx = $lookbackTotal;
        }
        if ($startIdx > $endIdx) {
            $outBegIdx = 0;
            $outNBElement = 0;

            return ReturnCode::Success;
        }
        $outIdx = 0;
        $outBegIdx = $today = $startIdx;
        $prevMinusDM = 0.0;
        $prevPlusDM = 0.0;
        $prevTR = 0.0;
        $today = $startIdx - $lookbackTotal;
        $prevHigh = $inHigh[$today];
        $prevLow = $inLow[$today];
        $prevClose = $inClose[$today];
        $i = $optInTimePeriod - 1;
        while ($i-- > 0) {
            $today++;
            $tempReal = $inHigh[$today];
            $diffP = $tempReal - $prevHigh;
            $prevHigh = $tempReal;
            $tempReal = $inLow[$today];
            $diffM = $prevLow - $tempReal;
            $prevLow = $tempReal;
            if ($diffM > 0 && $diffP < $diffM) {
                $prevMinusDM += $diffM;
            } elseif ($diffP > 0 && $diffP > $diffM) {
                $prevPlusDM += $diffP;
            }
            {
                $tempReal = $prevHigh - $prevLow;
                $tempReal2 = abs($prevHigh - $prevClose);
                if ($tempReal2 > $tempReal) {
                    $tempReal = $tempReal2;
                }
                $tempReal2 = abs($prevLow - $prevClose);
                if ($tempReal2 > $tempReal) {
                    $tempReal = $tempReal2;
                }
            }
            $prevTR += $tempReal;
            $prevClose = $inClose[$today];
        }
        $i = static::$unstablePeriod[UnstablePeriodFunctionID::DX] + 1;
        while ($i-- > 0) {
            $today++;
            $tempReal = $inHigh[$today];
            $diffP = $tempReal - $prevHigh;
            $prevHigh = $tempReal;
            $tempReal = $inLow[$today];
            $diffM = $prevLow - $tempReal;
            $prevLow = $tempReal;
            $prevMinusDM -= $prevMinusDM / $optInTimePeriod;
            $prevPlusDM -= $prevPlusDM / $optInTimePeriod;
            if ($diffM > 0 && $diffP < $diffM) {
                $prevMinusDM += $diffM;
            } elseif ($diffP > 0 && $diffP > $diffM) {
                $prevPlusDM += $diffP;
            }
            {
                $tempReal = $prevHigh - $prevLow;
                $tempReal2 = abs($prevHigh - $prevClose);
                if ($tempReal2 > $tempReal) {
                    $tempReal = $tempReal2;
                }
                $tempReal2 = abs($prevLow - $prevClose);
                if ($tempReal2 > $tempReal) {
                    $tempReal = $tempReal2;
                }
            }
            $prevTR = $prevTR - $prevTR / $optInTimePeriod + $tempReal;
            $prevClose = $inClose[$today];
        }
        if (!(-0.00000001 < $prevTR && $prevTR < 0.00000001)) {
            $minusDI = 100.0 * ($prevMinusDM / $prevTR);
            $plusDI = 100.0 * ($prevPlusDM / $prevTR);
            $tempReal = $minusDI + $plusDI;
            if (!(-0.00000001 < $tempReal && $tempReal < 0.00000001)) {
                $outReal[0] = 100.0 * (abs($minusDI - $plusDI) / $tempReal);
            } else {
                $outReal[0] = 0.0;
            }
        } else {
            $outReal[0] = 0.0;
        }
        $outIdx = 1;
        while ($today < $endIdx) {
            $today++;
            $tempReal = $inHigh[$today];
            $diffP = $tempReal - $prevHigh;
            $prevHigh = $tempReal;
            $tempReal = $inLow[$today];
            $diffM = $prevLow - $tempReal;
            $prevLow = $tempReal;
            $prevMinusDM -= $prevMinusDM / $optInTimePeriod;
            $prevPlusDM -= $prevPlusDM / $optInTimePeriod;
            if ($diffM > 0 && $diffP < $diffM) {
                $prevMinusDM += $diffM;
            } elseif ($diffP > 0 && $diffP > $diffM) {
                $prevPlusDM += $diffP;
            }
            {
                $tempReal = $prevHigh - $prevLow;
                $tempReal2 = abs($prevHigh - $prevClose);
                if ($tempReal2 > $tempReal) {
                    $tempReal = $tempReal2;
                }
                $tempReal2 = abs($prevLow - $prevClose);
                if ($tempReal2 > $tempReal) {
                    $tempReal = $tempReal2;
                }
            }
            $prevTR = $prevTR - $prevTR / $optInTimePeriod + $tempReal;
            $prevClose = $inClose[$today];
            if (!(-0.00000001 < $prevTR && $prevTR < 0.00000001)) {
                $minusDI = 100.0 * ($prevMinusDM / $prevTR);
                $plusDI = 100.0 * ($prevPlusDM / $prevTR);
                $tempReal = $minusDI + $plusDI;
                if (!(-0.00000001 < $tempReal && $tempReal < 0.00000001)) {
                    $outReal[$outIdx] = 100.0 * (abs($minusDI - $plusDI) / $tempReal);
                } else {
                    $outReal[$outIdx] = $outReal[$outIdx - 1];
                }
            } else {
                $outReal[$outIdx] = $outReal[$outIdx - 1];
            }
            $outIdx++;
        }
        $outNBElement = $outIdx;

        return ReturnCode::Success;
    }

    public static function imi(int $startIdx, int $endIdx, array $inOpen, array $inClose, int $optInTimePeriod, int &$outBegIdx, int &$outNBElement, array &$outReal): int
    {
        if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) {
            return $RetCode;
        }

        $lookback = Lookback::imiLookback($optInTimePeriod);

        if ($startIdx < $lookback) {
            $startIdx = $lookback;
        }

        if ($startIdx > $endIdx) {
            $outBegIdx = 0;
            $outNBElement = 0;

            return ReturnCode::Success;
        }

        $outIdx = 0;
        $outBegIdx = $startIdx;

        while ($startIdx <= $endIdx) {
            $upsum = 0.0;
            $downsum = 0.0;

            for ($i = $startIdx - $lookback; $i <= $startIdx; $i++) {
                $close = $inClose[$i];
                $open = $inOpen[$i];

                if ($close > $open) {
                    $upsum += ($close - $open);
                } else {
                    $downsum += ($open - $close);
                }
            }

            $outReal[$outIdx] = 100.0 * ($upsum / ($upsum + $downsum));

            $startIdx++;
            $outIdx++;
        }

        $outNBElement = $outIdx;

        return ReturnCode::Success;
    }

    public static function macd(
        int $startIdx,
        int $endIdx,
        array $inReal,
        int $optInFastPeriod,
        int $optInSlowPeriod,
        int $optInSignalPeriod,
        int &$outBegIdx,
        int &$outNBElement,
        array &$outMACD,
        array &$outMACDSignal,
        array &$outMACDHist
    ): int {
        if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) {
            return $RetCode;
        }
        if ($optInFastPeriod === PHP_INT_MIN) {
            $optInFastPeriod = 12;
        } elseif ($optInFastPeriod < 2 || $optInFastPeriod > 100000) {
            return ReturnCode::BadParam;
        }
        if ($optInSlowPeriod === PHP_INT_MIN) {
            $optInSlowPeriod = 26;
        } elseif ($optInSlowPeriod < 2 || $optInSlowPeriod > 100000) {
            return ReturnCode::BadParam;
        }
        if ($optInSignalPeriod === PHP_INT_MIN) {
            $optInSignalPeriod = 9;
        } elseif ($optInSignalPeriod < 1 || $optInSignalPeriod > 100000) {
            return ReturnCode::BadParam;
        }

        return static::TA_INT_MACD(
            $startIdx,
            $endIdx,
            $inReal,
            $optInFastPeriod,
            $optInSlowPeriod,
            $optInSignalPeriod,
            $outBegIdx,
            $outNBElement,
            $outMACD,
            $outMACDSignal,
            $outMACDHist
        );
    }

    public static function macdExt(
        int $startIdx,
        int $endIdx,
        array $inReal,
        int $optInFastPeriod,
        int $optInFastMAType,
        int $optInSlowPeriod,
        int $optInSlowMAType,
        int $optInSignalPeriod,
        int $optInSignalMAType,
        int &$outBegIdx,
        int &$outNBElement,
        array &$outMACD,
        array &$outMACDSignal,
        array &$outMACDHist
    ): int {
        if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) {
            return $RetCode;
        }
        $outBegIdx1 = 0;
        $outNbElement1 = 0;
        $outBegIdx2 = 0;
        $outNbElement2 = 0;
        if ($optInFastPeriod === PHP_INT_MIN) {
            $optInFastPeriod = 12;
        } elseif ($optInFastPeriod < 2 || $optInFastPeriod > 100000) {
            return ReturnCode::BadParam;
        }
        if ($optInSlowPeriod === PHP_INT_MIN) {
            $optInSlowPeriod = 26;
        } elseif ($optInSlowPeriod < 2 || $optInSlowPeriod > 100000) {
            return ReturnCode::BadParam;
        }
        if ($optInSignalPeriod === PHP_INT_MIN) {
            $optInSignalPeriod = 9;
        } elseif ($optInSignalPeriod < 1 || $optInSignalPeriod > 100000) {
            return ReturnCode::BadParam;
        }
        if ($optInSlowPeriod < $optInFastPeriod) {
            $tempInteger = $optInSlowPeriod;
            $optInSlowPeriod = $optInFastPeriod;
            $optInFastPeriod = $tempInteger;
            $tempMAType = $optInSlowMAType;
            $optInSlowMAType = $optInFastMAType;
            $optInFastMAType = $tempMAType;
        }
        $lookbackLargest = Lookback::movingAverageLookback($optInFastPeriod, $optInFastMAType);
        $tempInteger = Lookback::movingAverageLookback($optInSlowPeriod, $optInSlowMAType);
        if ($tempInteger > $lookbackLargest) {
            $lookbackLargest = $tempInteger;
        }
        $lookbackSignal = Lookback::movingAverageLookback($optInSignalPeriod, $optInSignalMAType);
        $lookbackTotal = $lookbackSignal + $lookbackLargest;
        if ($startIdx < $lookbackTotal) {
            $startIdx = $lookbackTotal;
        }
        if ($startIdx > $endIdx) {
            $outBegIdx = 0;
            $outNBElement = 0;

            return ReturnCode::Success;
        }
        $tempInteger = $endIdx - $startIdx + 1 + $lookbackSignal;
        $fastMABuffer = static::double($tempInteger);
        $slowMABuffer = static::double($tempInteger);
        $tempInteger = $startIdx - $lookbackSignal;
        $ReturnCode = OverlapStudies::movingAverage(
            $tempInteger,
            $endIdx,
            $inReal,
            $optInSlowPeriod,
            $optInSlowMAType,
            $outBegIdx1,
            $outNbElement1,
            $slowMABuffer
        );
        if ($ReturnCode !== ReturnCode::Success) {
            $outBegIdx = 0;
            $outNBElement = 0;

            return $ReturnCode;
        }
        $ReturnCode = OverlapStudies::movingAverage(
            $tempInteger,
            $endIdx,
            $inReal,
            $optInFastPeriod,
            $optInFastMAType,
            $outBegIdx2,
            $outNbElement2,
            $fastMABuffer
        );
        if ($ReturnCode !== ReturnCode::Success) {
            $outBegIdx = 0;
            $outNBElement = 0;

            return $ReturnCode;
        }
        if ($outBegIdx1 !== $tempInteger ||
            $outBegIdx2 !== $tempInteger ||
            $outNbElement1 !== $outNbElement2 ||
            $outNbElement1 !== $endIdx - $startIdx + 1 + $lookbackSignal) {
            $outBegIdx = 0;
            $outNBElement = 0;

            return ReturnCode::InternalError;
        }
        for ($i = 0; $i < $outNbElement1; $i++) {
            $fastMABuffer[$i] -= $slowMABuffer[$i];
        }
        //System::arraycopy($fastMABuffer, $lookbackSignal, $outMACD, 0, ($endIdx - $startIdx) + 1);
        $outMACD = \array_slice($fastMABuffer, $lookbackSignal, $endIdx - $startIdx + 1);
        $ReturnCode = OverlapStudies::movingAverage(
            0,
            $outNbElement1 - 1,
            $fastMABuffer,
            $optInSignalPeriod,
            $optInSignalMAType,
            $outBegIdx2,
            $outNbElement2,
            $outMACDSignal
        );
        if ($ReturnCode !== ReturnCode::Success) {
            $outBegIdx = 0;
            $outNBElement = 0;

            return $ReturnCode;
        }
        for ($i = 0; $i < $outNbElement2; $i++) {
            $outMACDHist[$i] = $outMACD[$i] - $outMACDSignal[$i];
        }
        $outBegIdx = $startIdx;
        $outNBElement = $outNbElement2;

        return ReturnCode::Success;
    }

    public static function macdFix(
        int $startIdx,
        int $endIdx,
        array $inReal,
        int $optInSignalPeriod,
        int &$outBegIdx,
        int &$outNBElement,
        array &$outMACD,
        array &$outMACDSignal,
        array &$outMACDHist
    ): int {
        if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) {
            return $RetCode;
        }
        if ($optInSignalPeriod === PHP_INT_MIN) {
            $optInSignalPeriod = 9;
        } elseif ($optInSignalPeriod < 1 || $optInSignalPeriod > 100000) {
            return ReturnCode::BadParam;
        }

        return static::TA_INT_MACD(
            $startIdx,
            $endIdx,
            $inReal,
            0,
            0,
            $optInSignalPeriod,
            $outBegIdx,
            $outNBElement,
            $outMACD,
            $outMACDSignal,
            $outMACDHist
        );
    }

    public static function mfi(
        int $startIdx,
        int $endIdx,
        array $inHigh,
        array $inLow,
        array $inClose,
        array $inVolume,
        int $optInTimePeriod,
        int &$outBegIdx,
        int &$outNBElement,
        array &$outReal
    ): int {
        if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) {
            return $RetCode;
        }
        $money_flow_Idx = 0;
        $maxIdx_money_flow = 50 - 1;
        if ($optInTimePeriod === PHP_INT_MIN) {
            $optInTimePeriod = 14;
        } elseif ($optInTimePeriod < 2 || $optInTimePeriod > 100000) {
            return ReturnCode::BadParam;
        }
        {
            $money_flow = \array_pad([], $optInTimePeriod, new MoneyFlow());
            for ($_money_flow_index = 0; $_money_flow_index < $optInTimePeriod; $_money_flow_index++) {
                $money_flow[$_money_flow_index] = new MoneyFlow();
            }
            $maxIdx_money_flow = $optInTimePeriod - 1;
        }
        $outBegIdx = 0;
        $outNBElement = 0;
        $lookbackTotal = $optInTimePeriod + static::$unstablePeriod[UnstablePeriodFunctionID::MFI];
        if ($startIdx < $lookbackTotal) {
            $startIdx = $lookbackTotal;
        }
        if ($startIdx > $endIdx) {
            return ReturnCode::Success;
        }
        $outIdx = 0;
        $today = $startIdx - $lookbackTotal;
        $prevValue = ($inHigh[$today] + $inLow[$today] + $inClose[$today]) / 3.0;
        $posSumMF = 0.0;
        $negSumMF = 0.0;
        $today++;
        for ($i = $optInTimePeriod; $i > 0; $i--) {
            $tempValue1 = ($inHigh[$today] + $inLow[$today] + $inClose[$today]) / 3.0;
            $tempValue2 = $tempValue1 - $prevValue;
            $prevValue = $tempValue1;
            $tempValue1 *= $inVolume[$today++];
            if ($tempValue2 < 0) {
                $money_flow[$money_flow_Idx]->negative = $tempValue1;
                $negSumMF += $tempValue1;
                $money_flow[$money_flow_Idx]->positive = 0.0;
            } elseif ($tempValue2 > 0) {
                $money_flow[$money_flow_Idx]->positive = $tempValue1;
                $posSumMF += $tempValue1;
                $money_flow[$money_flow_Idx]->negative = 0.0;
            } else {
                $money_flow[$money_flow_Idx]->positive = 0.0;
                $money_flow[$money_flow_Idx]->negative = 0.0;
            }
            {
                $money_flow_Idx++;
                if ($money_flow_Idx > $maxIdx_money_flow) {
                    $money_flow_Idx = 0;
                }
            }
        }
        if ($today > $startIdx) {
            $tempValue1 = $posSumMF + $negSumMF;
            if ($tempValue1 < 1.0) {
                $outReal[$outIdx++] = 0.0;
            } else {
                $outReal[$outIdx++] = 100.0 * ($posSumMF / $tempValue1);
            }
        } else {
            while ($today < $startIdx) {
                $posSumMF -= $money_flow[$money_flow_Idx]->positive;
                $negSumMF -= $money_flow[$money_flow_Idx]->negative;
                $tempValue1 = ($inHigh[$today] + $inLow[$today] + $inClose[$today]) / 3.0;
                $tempValue2 = $tempValue1 - $prevValue;
                $prevValue = $tempValue1;
                $tempValue1 *= $inVolume[$today++];
                if ($tempValue2 < 0) {
                    $money_flow[$money_flow_Idx]->negative = $tempValue1;
                    $negSumMF += $tempValue1;
                    $money_flow[$money_flow_Idx]->positive = 0.0;
                } elseif ($tempValue2 > 0) {
                    $money_flow[$money_flow_Idx]->positive = $tempValue1;
                    $posSumMF += $tempValue1;
                    $money_flow[$money_flow_Idx]->negative = 0.0;
                } else {
                    $money_flow[$money_flow_Idx]->positive = 0.0;
                    $money_flow[$money_flow_Idx]->negative = 0.0;
                }
                {
                    $money_flow_Idx++;
                    if ($money_flow_Idx > $maxIdx_money_flow) {
                        $money_flow_Idx = 0;
                    }
                }
            }
        }
        while ($today <= $endIdx) {
            $posSumMF -= $money_flow[$money_flow_Idx]->positive;
            $negSumMF -= $money_flow[$money_flow_Idx]->negative;
            $tempValue1 = ($inHigh[$today] + $inLow[$today] + $inClose[$today]) / 3.0;
            $tempValue2 = $tempValue1 - $prevValue;
            $prevValue = $tempValue1;
            $tempValue1 *= $inVolume[$today++];
            if ($tempValue2 < 0) {
                $money_flow[$money_flow_Idx]->negative = $tempValue1;
                $negSumMF += $tempValue1;
                $money_flow[$money_flow_Idx]->positive = 0.0;
            } elseif ($tempValue2 > 0) {
                $money_flow[$money_flow_Idx]->positive = $tempValue1;
                $posSumMF += $tempValue1;
                $money_flow[$money_flow_Idx]->negative = 0.0;
            } else {
                $money_flow[$money_flow_Idx]->positive = 0.0;
                $money_flow[$money_flow_Idx]->negative = 0.0;
            }
            $tempValue1 = $posSumMF + $negSumMF;
            if ($tempValue1 < 1.0) {
                $outReal[$outIdx++] = 0.0;
            } else {
                $outReal[$outIdx++] = 100.0 * ($posSumMF / $tempValue1);
            }
            {
                $money_flow_Idx++;
                if ($money_flow_Idx > $maxIdx_money_flow) {
                    $money_flow_Idx = 0;
                }
            }
        }
        $outBegIdx = $startIdx;
        $outNBElement = $outIdx;

        return ReturnCode::Success;
    }

    public static function minusDI(int $startIdx, int $endIdx, array $inHigh, array $inLow, array $inClose, int $optInTimePeriod, int &$outBegIdx, int &$outNBElement, array &$outReal): int
    {
        if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) {
            return $RetCode;
        }
        if ($optInTimePeriod === PHP_INT_MIN) {
            $optInTimePeriod = 14;
        } elseif ($optInTimePeriod < 1 || $optInTimePeriod > 100000) {
            return ReturnCode::BadParam;
        }
        if ($optInTimePeriod > 1) {
            $lookbackTotal = $optInTimePeriod + static::$unstablePeriod[UnstablePeriodFunctionID::MinusDI];
        } else {
            $lookbackTotal = 1;
        }
        if ($startIdx < $lookbackTotal) {
            $startIdx = $lookbackTotal;
        }
        if ($startIdx > $endIdx) {
            $outBegIdx = 0;
            $outNBElement = 0;

            return ReturnCode::Success;
        }
        $outIdx = 0;
        if ($optInTimePeriod <= 1) {
            $outBegIdx = $startIdx;
            $today = $startIdx - 1;
            $prevHigh = $inHigh[$today];
            $prevLow = $inLow[$today];
            $prevClose = $inClose[$today];
            while ($today < $endIdx) {
                $today++;
                $tempReal = $inHigh[$today];
                $diffP = $tempReal - $prevHigh;
                $prevHigh = $tempReal;
                $tempReal = $inLow[$today];
                $diffM = $prevLow - $tempReal;
                $prevLow = $tempReal;
                if ($diffM > 0 && $diffP < $diffM) {
                    {
                        $tempReal = $prevHigh - $prevLow;
                        $tempReal2 = abs($prevHigh - $prevClose);
                        if ($tempReal2 > $tempReal) {
                            $tempReal = $tempReal2;
                        }
                        $tempReal2 = abs($prevLow - $prevClose);
                        if ($tempReal2 > $tempReal) {
                            $tempReal = $tempReal2;
                        }
                    }
                    if (-0.00000001 < $tempReal && $tempReal < 0.00000001) {
                        $outReal[$outIdx++] = 0.0;
                    } else {
                        $outReal[$outIdx++] = $diffM / $tempReal;
                    }
                } else {
                    $outReal[$outIdx++] = 0.0;
                }
                $prevClose = $inClose[$today];
            }
            $outNBElement = $outIdx;

            return ReturnCode::Success;
        }
        $outBegIdx = $today = $startIdx;
        $prevMinusDM = 0.0;
        $prevTR = 0.0;
        $today = $startIdx - $lookbackTotal;
        $prevHigh = $inHigh[$today];
        $prevLow = $inLow[$today];
        $prevClose = $inClose[$today];
        $i = $optInTimePeriod - 1;
        while ($i-- > 0) {
            $today++;
            $tempReal = $inHigh[$today];
            $diffP = $tempReal - $prevHigh;
            $prevHigh = $tempReal;
            $tempReal = $inLow[$today];
            $diffM = $prevLow - $tempReal;
            $prevLow = $tempReal;
            if ($diffM > 0 && $diffP < $diffM) {
                $prevMinusDM += $diffM;
            }
            {
                $tempReal = $prevHigh - $prevLow;
                $tempReal2 = abs($prevHigh - $prevClose);
                if ($tempReal2 > $tempReal) {
                    $tempReal = $tempReal2;
                }
                $tempReal2 = abs($prevLow - $prevClose);
                if ($tempReal2 > $tempReal) {
                    $tempReal = $tempReal2;
                }
            }
            $prevTR += $tempReal;
            $prevClose = $inClose[$today];
        }
        $i = static::$unstablePeriod[UnstablePeriodFunctionID::MinusDI] + 1;
        while ($i-- !== 0) {
            $today++;
            $tempReal = $inHigh[$today];
            $diffP = $tempReal - $prevHigh;
            $prevHigh = $tempReal;
            $tempReal = $inLow[$today];
            $diffM = $prevLow - $tempReal;
            $prevLow = $tempReal;
            if ($diffM > 0 && $diffP < $diffM) {
                $prevMinusDM = $prevMinusDM - $prevMinusDM / $optInTimePeriod + $diffM;
            } else {
                $prevMinusDM -= $prevMinusDM / $optInTimePeriod;
            }
            {
                $tempReal = $prevHigh - $prevLow;
                $tempReal2 = abs($prevHigh - $prevClose);
                if ($tempReal2 > $tempReal) {
                    $tempReal = $tempReal2;
                }
                $tempReal2 = abs($prevLow - $prevClose);
                if ($tempReal2 > $tempReal) {
                    $tempReal = $tempReal2;
                }
            }
            $prevTR = $prevTR - $prevTR / $optInTimePeriod + $tempReal;
            $prevClose = $inClose[$today];
        }
        if (!(-0.00000001 < $prevTR && $prevTR < 0.00000001)) {
            $outReal[0] = 100.0 * ($prevMinusDM / $prevTR);
        } else {
            $outReal[0] = 0.0;
        }
        $outIdx = 1;
        while ($today < $endIdx) {
            $today++;
            $tempReal = $inHigh[$today];
            $diffP = $tempReal - $prevHigh;
            $prevHigh = $tempReal;
            $tempReal = $inLow[$today];
            $diffM = $prevLow - $tempReal;
            $prevLow = $tempReal;
            if ($diffM > 0 && $diffP < $diffM) {
                $prevMinusDM = $prevMinusDM - $prevMinusDM / $optInTimePeriod + $diffM;
            } else {
                $prevMinusDM -= $prevMinusDM / $optInTimePeriod;
            }
            {
                $tempReal = $prevHigh - $prevLow;
                $tempReal2 = abs($prevHigh - $prevClose);
                if ($tempReal2 > $tempReal) {
                    $tempReal = $tempReal2;
                }
                $tempReal2 = abs($prevLow - $prevClose);
                if ($tempReal2 > $tempReal) {
                    $tempReal = $tempReal2;
                }
            }
            $prevTR = $prevTR - $prevTR / $optInTimePeriod + $tempReal;
            $prevClose = $inClose[$today];
            if (!(-0.00000001 < $prevTR && $prevTR < 0.00000001)) {
                $outReal[$outIdx++] = 100.0 * ($prevMinusDM / $prevTR);
            } else {
                $outReal[$outIdx++] = 0.0;
            }
        }
        $outNBElement = $outIdx;

        return ReturnCode::Success;
    }

    public static function minusDM(int $startIdx, int $endIdx, array $inHigh, array $inLow, int $optInTimePeriod, int &$outBegIdx, int &$outNBElement, array &$outReal): int
    {
        if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) {
            return $RetCode;
        }
        if ($optInTimePeriod === PHP_INT_MIN) {
            $optInTimePeriod = 14;
        } elseif ($optInTimePeriod < 1 || $optInTimePeriod > 100000) {
            return ReturnCode::BadParam;
        }
        if ($optInTimePeriod > 1) {
            $lookbackTotal = $optInTimePeriod + static::$unstablePeriod[UnstablePeriodFunctionID::MinusDM] - 1;
        } else {
            $lookbackTotal = 1;
        }
        if ($startIdx < $lookbackTotal) {
            $startIdx = $lookbackTotal;
        }
        if ($startIdx > $endIdx) {
            $outBegIdx = 0;
            $outNBElement = 0;

            return ReturnCode::Success;
        }
        $outIdx = 0;
        $outBegIdx = $startIdx;
        if ($optInTimePeriod <= 1) {
            $today = $startIdx - 1;
            $prevHigh = $inHigh[$today];
            $prevLow = $inLow[$today];
            while ($today < $endIdx) {
                $today++;
                $tempReal = $inHigh[$today];
                $diffP = $tempReal - $prevHigh;
                $prevHigh = $tempReal;
                $tempReal = $inLow[$today];
                $diffM = $prevLow - $tempReal;
                $prevLow = $tempReal;
                if ($diffM > 0 && $diffP < $diffM) {
                    $outReal[$outIdx++] = $diffM;
                } else {
                    $outReal[$outIdx++] = 0;
                }
            }
            $outNBElement = $outIdx;

            return ReturnCode::Success;
        }
        $prevMinusDM = 0.0;
        $today = $startIdx - $lookbackTotal;
        $prevHigh = $inHigh[$today];
        $prevLow = $inLow[$today];
        $i = $optInTimePeriod - 1;
        while ($i-- > 0) {
            $today++;
            $tempReal = $inHigh[$today];
            $diffP = $tempReal - $prevHigh;
            $prevHigh = $tempReal;
            $tempReal = $inLow[$today];
            $diffM = $prevLow - $tempReal;
            $prevLow = $tempReal;
            if ($diffM > 0 && $diffP < $diffM) {
                $prevMinusDM += $diffM;
            }
        }
        $i = static::$unstablePeriod[UnstablePeriodFunctionID::MinusDM];
        while ($i-- > 0) {
            $today++;
            $tempReal = $inHigh[$today];
            $diffP = $tempReal - $prevHigh;
            $prevHigh = $tempReal;
            $tempReal = $inLow[$today];
            $diffM = $prevLow - $tempReal;
            $prevLow = $tempReal;
            if ($diffM > 0 && $diffP < $diffM) {
                $prevMinusDM = $prevMinusDM - $prevMinusDM / $optInTimePeriod + $diffM;
            } else {
                $prevMinusDM -= $prevMinusDM / $optInTimePeriod;
            }
        }
        $outReal[0] = $prevMinusDM;
        $outIdx = 1;
        while ($today < $endIdx) {
            $today++;
            $tempReal = $inHigh[$today];
            $diffP = $tempReal - $prevHigh;
            $prevHigh = $tempReal;
            $tempReal = $inLow[$today];
            $diffM = $prevLow - $tempReal;
            $prevLow = $tempReal;
            if ($diffM > 0 && $diffP < $diffM) {
                $prevMinusDM = $prevMinusDM - $prevMinusDM / $optInTimePeriod + $diffM;
            } else {
                $prevMinusDM -= $prevMinusDM / $optInTimePeriod;
            }
            $outReal[$outIdx++] = $prevMinusDM;
        }
        $outNBElement = $outIdx;

        return ReturnCode::Success;
    }

    public static function mom(int $startIdx, int $endIdx, array $inReal, int $optInTimePeriod, int &$outBegIdx, int &$outNBElement, array &$outReal): int
    {
        if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) {
            return $RetCode;
        }
        if ($optInTimePeriod === PHP_INT_MIN) {
            $optInTimePeriod = 10;
        } elseif ($optInTimePeriod < 1 || $optInTimePeriod > 100000) {
            return ReturnCode::BadParam;
        }
        if ($startIdx < $optInTimePeriod) {
            $startIdx = $optInTimePeriod;
        }
        if ($startIdx > $endIdx) {
            $outBegIdx = 0;
            $outNBElement = 0;

            return ReturnCode::Success;
        }
        $outIdx = 0;
        $inIdx = $startIdx;
        $trailingIdx = $startIdx - $optInTimePeriod;
        while ($inIdx <= $endIdx) {
            $outReal[$outIdx++] = $inReal[$inIdx++] - $inReal[$trailingIdx++];
        }
        $outNBElement = $outIdx;
        $outBegIdx = $startIdx;

        return ReturnCode::Success;
    }

    public static function plusDI(int $startIdx, int $endIdx, array $inHigh, array $inLow, array $inClose, int $optInTimePeriod, int &$outBegIdx, int &$outNBElement, array &$outReal): int
    {
        if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) {
            return $RetCode;
        }
        if ($optInTimePeriod === PHP_INT_MIN) {
            $optInTimePeriod = 14;
        } elseif ($optInTimePeriod < 1 || $optInTimePeriod > 100000) {
            return ReturnCode::BadParam;
        }
        if ($optInTimePeriod > 1) {
            $lookbackTotal = $optInTimePeriod + static::$unstablePeriod[UnstablePeriodFunctionID::PlusDI];
        } else {
            $lookbackTotal = 1;
        }
        if ($startIdx < $lookbackTotal) {
            $startIdx = $lookbackTotal;
        }
        if ($startIdx > $endIdx) {
            $outBegIdx = 0;
            $outNBElement = 0;

            return ReturnCode::Success;
        }
        $outIdx = 0;
        if ($optInTimePeriod <= 1) {
            $outBegIdx = $startIdx;
            $today = $startIdx - 1;
            $prevHigh = $inHigh[$today];
            $prevLow = $inLow[$today];
            $prevClose = $inClose[$today];
            while ($today < $endIdx) {
                $today++;
                $tempReal = $inHigh[$today];
                $diffP = $tempReal - $prevHigh;
                $prevHigh = $tempReal;
                $tempReal = $inLow[$today];
                $diffM = $prevLow - $tempReal;
                $prevLow = $tempReal;
                if ($diffP > 0 && $diffP > $diffM) {
                    {
                        $tempReal = $prevHigh - $prevLow;
                        $tempReal2 = abs($prevHigh - $prevClose);
                        if ($tempReal2 > $tempReal) {
                            $tempReal = $tempReal2;
                        }
                        $tempReal2 = abs($prevLow - $prevClose);
                        if ($tempReal2 > $tempReal) {
                            $tempReal = $tempReal2;
                        }
                    }
                    if (-0.00000001 < $tempReal && $tempReal < 0.00000001) {
                        $outReal[$outIdx++] = 0.0;
                    } else {
                        $outReal[$outIdx++] = $diffP / $tempReal;
                    }
                } else {
                    $outReal[$outIdx++] = 0.0;
                }
                $prevClose = $inClose[$today];
            }
            $outNBElement = $outIdx;

            return ReturnCode::Success;
        }
        $outBegIdx = $today = $startIdx;
        $prevPlusDM = 0.0;
        $prevTR = 0.0;
        $today = $startIdx - $lookbackTotal;
        $prevHigh = $inHigh[$today];
        $prevLow = $inLow[$today];
        $prevClose = $inClose[$today];
        $i = $optInTimePeriod - 1;
        while ($i-- > 0) {
            $today++;
            $tempReal = $inHigh[$today];
            $diffP = $tempReal - $prevHigh;
            $prevHigh = $tempReal;
            $tempReal = $inLow[$today];
            $diffM = $prevLow - $tempReal;
            $prevLow = $tempReal;
            if ($diffP > 0 && $diffP > $diffM) {
                $prevPlusDM += $diffP;
            }
            {
                $tempReal = $prevHigh - $prevLow;
                $tempReal2 = abs($prevHigh - $prevClose);
                if ($tempReal2 > $tempReal) {
                    $tempReal = $tempReal2;
                }
                $tempReal2 = abs($prevLow - $prevClose);
                if ($tempReal2 > $tempReal) {
                    $tempReal = $tempReal2;
                }
            }
            $prevTR += $tempReal;
            $prevClose = $inClose[$today];
        }
        $i = static::$unstablePeriod[UnstablePeriodFunctionID::PlusDI] + 1;
        while ($i-- > 0) {
            $today++;
            $tempReal = $inHigh[$today];
            $diffP = $tempReal - $prevHigh;
            $prevHigh = $tempReal;
            $tempReal = $inLow[$today];
            $diffM = $prevLow - $tempReal;
            $prevLow = $tempReal;
            if ($diffP > 0 && $diffP > $diffM) {
                $prevPlusDM = $prevPlusDM - $prevPlusDM / $optInTimePeriod + $diffP;
            } else {
                $prevPlusDM -= $prevPlusDM / $optInTimePeriod;
            }
            {
                $tempReal = $prevHigh - $prevLow;
                $tempReal2 = abs($prevHigh - $prevClose);
                if ($tempReal2 > $tempReal) {
                    $tempReal = $tempReal2;
                }
                $tempReal2 = abs($prevLow - $prevClose);
                if ($tempReal2 > $tempReal) {
                    $tempReal = $tempReal2;
                }
            }
            $prevTR = $prevTR - $prevTR / $optInTimePeriod + $tempReal;
            $prevClose = $inClose[$today];
        }
        if (!(-0.00000001 < $prevTR && $prevTR < 0.00000001)) {
            $outReal[0] = 100.0 * ($prevPlusDM / $prevTR);
        } else {
            $outReal[0] = 0.0;
        }
        $outIdx = 1;
        while ($today < $endIdx) {
            $today++;
            $tempReal = $inHigh[$today];
            $diffP = $tempReal - $prevHigh;
            $prevHigh = $tempReal;
            $tempReal = $inLow[$today];
            $diffM = $prevLow - $tempReal;
            $prevLow = $tempReal;
            if ($diffP > 0 && $diffP > $diffM) {
                $prevPlusDM = $prevPlusDM - $prevPlusDM / $optInTimePeriod + $diffP;
            } else {
                $prevPlusDM -= $prevPlusDM / $optInTimePeriod;
            }
            {
                $tempReal = $prevHigh - $prevLow;
                $tempReal2 = abs($prevHigh - $prevClose);
                if ($tempReal2 > $tempReal) {
                    $tempReal = $tempReal2;
                }
                $tempReal2 = abs($prevLow - $prevClose);
                if ($tempReal2 > $tempReal) {
                    $tempReal = $tempReal2;
                }
            }
            $prevTR = $prevTR - $prevTR / $optInTimePeriod + $tempReal;
            $prevClose = $inClose[$today];
            if (!(-0.00000001 < $prevTR && $prevTR < 0.00000001)) {
                $outReal[$outIdx++] = 100.0 * ($prevPlusDM / $prevTR);
            } else {
                $outReal[$outIdx++] = 0.0;
            }
        }
        $outNBElement = $outIdx;

        return ReturnCode::Success;
    }

    public static function plusDM(int $startIdx, int $endIdx, array $inHigh, array $inLow, int $optInTimePeriod, int &$outBegIdx, int &$outNBElement, array &$outReal): int
    {
        if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) {
            return $RetCode;
        }
        if ($optInTimePeriod === PHP_INT_MIN) {
            $optInTimePeriod = 14;
        } elseif ($optInTimePeriod < 1 || $optInTimePeriod > 100000) {
            return ReturnCode::BadParam;
        }
        if ($optInTimePeriod > 1) {
            $lookbackTotal = $optInTimePeriod + static::$unstablePeriod[UnstablePeriodFunctionID::PlusDM] - 1;
        } else {
            $lookbackTotal = 1;
        }
        if ($startIdx < $lookbackTotal) {
            $startIdx = $lookbackTotal;
        }
        if ($startIdx > $endIdx) {
            $outBegIdx = 0;
            $outNBElement = 0;

            return ReturnCode::Success;
        }
        $outIdx = 0;
        $outBegIdx = $startIdx;
        if ($optInTimePeriod <= 1) {
            $today = $startIdx - 1;
            $prevHigh = $inHigh[$today];
            $prevLow = $inLow[$today];
            while ($today < $endIdx) {
                $today++;
                $tempReal = $inHigh[$today];
                $diffP = $tempReal - $prevHigh;
                $prevHigh = $tempReal;
                $tempReal = $inLow[$today];
                $diffM = $prevLow - $tempReal;
                $prevLow = $tempReal;
                if ($diffP > 0 && $diffP > $diffM) {
                    $outReal[$outIdx++] = $diffP;
                } else {
                    $outReal[$outIdx++] = 0;
                }
            }
            $outNBElement = $outIdx;

            return ReturnCode::Success;
        }
        $prevPlusDM = 0.0;
        $today = $startIdx - $lookbackTotal;
        $prevHigh = $inHigh[$today];
        $prevLow = $inLow[$today];
        $i = $optInTimePeriod - 1;
        while ($i-- > 0) {
            $today++;
            $tempReal = $inHigh[$today];
            $diffP = $tempReal - $prevHigh;
            $prevHigh = $tempReal;
            $tempReal = $inLow[$today];
            $diffM = $prevLow - $tempReal;
            $prevLow = $tempReal;
            if ($diffP > 0 && $diffP > $diffM) {
                $prevPlusDM += $diffP;
            }
        }
        $i = static::$unstablePeriod[UnstablePeriodFunctionID::PlusDM];
        while ($i-- > 0) {
            $today++;
            $tempReal = $inHigh[$today];
            $diffP = $tempReal - $prevHigh;
            $prevHigh = $tempReal;
            $tempReal = $inLow[$today];
            $diffM = $prevLow - $tempReal;
            $prevLow = $tempReal;
            if ($diffP > 0 && $diffP > $diffM) {
                $prevPlusDM = $prevPlusDM - $prevPlusDM / $optInTimePeriod + $diffP;
            } else {
                $prevPlusDM -= $prevPlusDM / $optInTimePeriod;
            }
        }
        $outReal[0] = $prevPlusDM;
        $outIdx = 1;
        while ($today < $endIdx) {
            $today++;
            $tempReal = $inHigh[$today];
            $diffP = $tempReal - $prevHigh;
            $prevHigh = $tempReal;
            $tempReal = $inLow[$today];
            $diffM = $prevLow - $tempReal;
            $prevLow = $tempReal;
            if ($diffP > 0 && $diffP > $diffM) {
                $prevPlusDM = $prevPlusDM - $prevPlusDM / $optInTimePeriod + $diffP;
            } else {
                $prevPlusDM -= $prevPlusDM / $optInTimePeriod;
            }
            $outReal[$outIdx++] = $prevPlusDM;
        }
        $outNBElement = $outIdx;

        return ReturnCode::Success;
    }

    public static function ppo(int $startIdx, int $endIdx, array $inReal, int $optInFastPeriod, int $optInSlowPeriod, int $optInMAType, int &$outBegIdx, int &$outNBElement, array &$outReal): int
    {
        if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) {
            return $RetCode;
        }
        if ($optInFastPeriod === PHP_INT_MIN) {
            $optInFastPeriod = 12;
        } elseif ($optInFastPeriod < 2 || $optInFastPeriod > 100000) {
            return ReturnCode::BadParam;
        }
        if ($optInSlowPeriod === PHP_INT_MIN) {
            $optInSlowPeriod = 26;
        } elseif ($optInSlowPeriod < 2 || $optInSlowPeriod > 100000) {
            return ReturnCode::BadParam;
        }
        $tempBuffer = static::double($endIdx - $startIdx + 1);
        $one = 1;

        return static::TA_INT_PO(
            $startIdx,
            $endIdx,
            $inReal,
            $optInFastPeriod,
            $optInSlowPeriod,
            $optInMAType,
            $outBegIdx,
            $outNBElement,
            $outReal,
            $tempBuffer,
            $one
        );
    }

    public static function roc(int $startIdx, int $endIdx, array $inReal, int $optInTimePeriod, int &$outBegIdx, int &$outNBElement, array &$outReal): int
    {
        if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) {
            return $RetCode;
        }
        if ($optInTimePeriod === PHP_INT_MIN) {
            $optInTimePeriod = 10;
        } elseif ($optInTimePeriod < 1 || $optInTimePeriod > 100000) {
            return ReturnCode::BadParam;
        }
        if ($startIdx < $optInTimePeriod) {
            $startIdx = $optInTimePeriod;
        }
        if ($startIdx > $endIdx) {
            $outBegIdx = 0;
            $outNBElement = 0;

            return ReturnCode::Success;
        }
        $outIdx = 0;
        $inIdx = $startIdx;
        $trailingIdx = $startIdx - $optInTimePeriod;
        while ($inIdx <= $endIdx) {
            $tempReal = $inReal[$trailingIdx++];
            if ($tempReal !== 0.0) {
                $outReal[$outIdx++] = ($inReal[$inIdx] / $tempReal - 1.0) * 100.0;
            } else {
                $outReal[$outIdx++] = 0.0;
            }
            $inIdx++;
        }
        $outNBElement = $outIdx;
        $outBegIdx = $startIdx;

        return ReturnCode::Success;
    }

    public static function rocP(int $startIdx, int $endIdx, array $inReal, int $optInTimePeriod, int &$outBegIdx, int &$outNBElement, array &$outReal): int
    {
        if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) {
            return $RetCode;
        }
        if ($optInTimePeriod === PHP_INT_MIN) {
            $optInTimePeriod = 10;
        } elseif ($optInTimePeriod < 1 || $optInTimePeriod > 100000) {
            return ReturnCode::BadParam;
        }
        if ($startIdx < $optInTimePeriod) {
            $startIdx = $optInTimePeriod;
        }
        if ($startIdx > $endIdx) {
            $outBegIdx = 0;
            $outNBElement = 0;

            return ReturnCode::Success;
        }
        $outIdx = 0;
        $inIdx = $startIdx;
        $trailingIdx = $startIdx - $optInTimePeriod;
        while ($inIdx <= $endIdx) {
            $tempReal = $inReal[$trailingIdx++];
            if ($tempReal !== 0.0) {
                $outReal[$outIdx++] = ($inReal[$inIdx] - $tempReal) / $tempReal;
            } else {
                $outReal[$outIdx++] = 0.0;
            }
            $inIdx++;
        }
        $outNBElement = $outIdx;
        $outBegIdx = $startIdx;

        return ReturnCode::Success;
    }

    public static function rocR(int $startIdx, int $endIdx, array $inReal, int $optInTimePeriod, int &$outBegIdx, int &$outNBElement, array &$outReal): int
    {
        if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) {
            return $RetCode;
        }
        if ($optInTimePeriod === PHP_INT_MIN) {
            $optInTimePeriod = 10;
        } elseif ($optInTimePeriod < 1 || $optInTimePeriod > 100000) {
            return ReturnCode::BadParam;
        }
        if ($startIdx < $optInTimePeriod) {
            $startIdx = $optInTimePeriod;
        }
        if ($startIdx > $endIdx) {
            $outBegIdx = 0;
            $outNBElement = 0;

            return ReturnCode::Success;
        }
        $outIdx = 0;
        $inIdx = $startIdx;
        $trailingIdx = $startIdx - $optInTimePeriod;
        while ($inIdx <= $endIdx) {
            $tempReal = $inReal[$trailingIdx++];
            if ($tempReal !== 0.0) {
                $outReal[$outIdx++] = $inReal[$inIdx] / $tempReal;
            } else {
                $outReal[$outIdx++] = 0.0;
            }
            $inIdx++;
        }
        $outNBElement = $outIdx;
        $outBegIdx = $startIdx;

        return ReturnCode::Success;
    }

    public static function rocR100(int $startIdx, int $endIdx, array $inReal, int $optInTimePeriod, int &$outBegIdx, int &$outNBElement, array &$outReal): int
    {
        if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) {
            return $RetCode;
        }
        if ($optInTimePeriod === PHP_INT_MIN) {
            $optInTimePeriod = 10;
        } elseif ($optInTimePeriod < 1 || $optInTimePeriod > 100000) {
            return ReturnCode::BadParam;
        }
        if ($startIdx < $optInTimePeriod) {
            $startIdx = $optInTimePeriod;
        }
        if ($startIdx > $endIdx) {
            $outBegIdx = 0;
            $outNBElement = 0;

            return ReturnCode::Success;
        }
        $outIdx = 0;
        $inIdx = $startIdx;
        $trailingIdx = $startIdx - $optInTimePeriod;
        while ($inIdx <= $endIdx) {
            $tempReal = $inReal[$trailingIdx++];
            if ($tempReal !== 0.0) {
                $outReal[$outIdx++] = $inReal[$inIdx] / $tempReal * 100.0;
            } else {
                $outReal[$outIdx++] = 0.0;
            }
            $inIdx++;
        }
        $outNBElement = $outIdx;
        $outBegIdx = $startIdx;

        return ReturnCode::Success;
    }

    public static function rsi(int $startIdx, int $endIdx, array $inReal, int $optInTimePeriod, int &$outBegIdx, int &$outNBElement, array &$outReal): int
    {
        if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) {
            return $RetCode;
        }
        if ($optInTimePeriod === PHP_INT_MIN) {
            $optInTimePeriod = 14;
        } elseif ($optInTimePeriod < 2 || $optInTimePeriod > 100000) {
            return ReturnCode::BadParam;
        }
        $outBegIdx = 0;
        $outNBElement = 0;
        $lookbackTotal = Lookback::rsiLookback($optInTimePeriod);
        if ($startIdx < $lookbackTotal) {
            $startIdx = $lookbackTotal;
        }
        if ($startIdx > $endIdx) {
            return ReturnCode::Success;
        }
        $outIdx = 0;
        $today = $startIdx - $lookbackTotal;
        $prevValue = $inReal[$today];
        $unstablePeriod = static::$unstablePeriod[UnstablePeriodFunctionID::RSI];
        if ($unstablePeriod === 0 &&
            static::$compatibility === Compatibility::Metastock) {
            $savePrevValue = $prevValue;
            $prevGain = 0.0;
            $prevLoss = 0.0;
            for ($i = $optInTimePeriod; $i > 0; $i--) {
                $tempValue1 = $inReal[$today++];
                $tempValue2 = $tempValue1 - $prevValue;
                $prevValue = $tempValue1;
                if ($tempValue2 < 0) {
                    $prevLoss -= $tempValue2;
                } else {
                    $prevGain += $tempValue2;
                }
            }
            $tempValue1 = $prevLoss / $optInTimePeriod;
            $tempValue2 = $prevGain / $optInTimePeriod;
            $tempValue1 = $tempValue2 + $tempValue1;
            if (!(-0.00000001 < $tempValue1 && $tempValue1 < 0.00000001)) {
                $outReal[$outIdx++] = 100 * ($tempValue2 / $tempValue1);
            } else {
                $outReal[$outIdx++] = 0.0;
            }
            if ($today > $endIdx) {
                $outBegIdx = $startIdx;
                $outNBElement = $outIdx;

                return ReturnCode::Success;
            }
            $today -= $optInTimePeriod;
            $prevValue = $savePrevValue;
        }
        $prevLoss = 0.0;
        $prevGain = 0.0;
        $today++;
        for ($i = $optInTimePeriod; $i > 0; $i--) {
            $tempValue1 = $inReal[$today++];
            $tempValue2 = $tempValue1 - $prevValue;
            $prevValue = $tempValue1;
            if ($tempValue2 < 0) {
                $prevLoss -= $tempValue2;
            } else {
                $prevGain += $tempValue2;
            }
        }
        $prevLoss /= $optInTimePeriod;
        $prevGain /= $optInTimePeriod;
        if ($today > $startIdx) {
            $tempValue1 = $prevGain + $prevLoss;
            if (!(-0.00000001 < $tempValue1 && $tempValue1 < 0.00000001)) {
                $outReal[$outIdx++] = 100.0 * ($prevGain / $tempValue1);
            } else {
                $outReal[$outIdx++] = 0.0;
            }
        } else {
            while ($today < $startIdx) {
                $tempValue1 = $inReal[$today];
                $tempValue2 = $tempValue1 - $prevValue;
                $prevValue = $tempValue1;
                $prevLoss *= $optInTimePeriod - 1;
                $prevGain *= $optInTimePeriod - 1;
                if ($tempValue2 < 0) {
                    $prevLoss -= $tempValue2;
                } else {
                    $prevGain += $tempValue2;
                }
                $prevLoss /= $optInTimePeriod;
                $prevGain /= $optInTimePeriod;
                $today++;
            }
        }
        while ($today <= $endIdx) {
            $tempValue1 = $inReal[$today++];
            $tempValue2 = $tempValue1 - $prevValue;
            $prevValue = $tempValue1;
            $prevLoss *= $optInTimePeriod - 1;
            $prevGain *= $optInTimePeriod - 1;
            if ($tempValue2 < 0) {
                $prevLoss -= $tempValue2;
            } else {
                $prevGain += $tempValue2;
            }
            $prevLoss /= $optInTimePeriod;
            $prevGain /= $optInTimePeriod;
            $tempValue1 = $prevGain + $prevLoss;
            if (!(-0.00000001 < $tempValue1 && $tempValue1 < 0.00000001)) {
                $outReal[$outIdx++] = 100.0 * ($prevGain / $tempValue1);
            } else {
                $outReal[$outIdx++] = 0.0;
            }
        }
        $outBegIdx = $startIdx;
        $outNBElement = $outIdx;

        return ReturnCode::Success;
    }

    public static function stoch(
        int $startIdx,
        int $endIdx,
        array $inHigh,
        array $inLow,
        array $inClose,
        int $optInFastK_Period,
        int $optInSlowK_Period,
        int $optInSlowK_MAType,
        int $optInSlowD_Period,
        int $optInSlowD_MAType,
        int &$outBegIdx,
        int &$outNBElement,
        array &$outSlowK,
        array &$outSlowD
    ): int {
        if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) {
            return $RetCode;
        }
        if ($optInFastK_Period === PHP_INT_MIN) {
            $optInFastK_Period = 5;
        } elseif ($optInFastK_Period < 1 || $optInFastK_Period > 100000) {
            return ReturnCode::BadParam;
        }
        if ($optInSlowK_Period === PHP_INT_MIN) {
            $optInSlowK_Period = 3;
        } elseif ($optInSlowK_Period < 1 || $optInSlowK_Period > 100000) {
            return ReturnCode::BadParam;
        }
        if ($optInSlowD_Period === PHP_INT_MIN) {
            $optInSlowD_Period = 3;
        } elseif ($optInSlowD_Period < 1 || $optInSlowD_Period > 100000) {
            return ReturnCode::BadParam;
        }
        $lookbackK = $optInFastK_Period - 1;
        $lookbackKSlow = Lookback::movingAverageLookback($optInSlowK_Period, $optInSlowK_MAType);
        $lookbackDSlow = Lookback::movingAverageLookback($optInSlowD_Period, $optInSlowD_MAType);
        $lookbackTotal = $lookbackK + $lookbackDSlow + $lookbackKSlow;
        if ($startIdx < $lookbackTotal) {
            $startIdx = $lookbackTotal;
        }
        if ($startIdx > $endIdx) {
            $outBegIdx = 0;
            $outNBElement = 0;

            return ReturnCode::Success;
        }
        $outIdx = 0;
        $trailingIdx = $startIdx - $lookbackTotal;
        $today = $trailingIdx + $lookbackK;
        $lowestIdx = $highestIdx = -1;
        $diff = $highest = $lowest = 0.0;
        if ($outSlowK === $inHigh ||
            $outSlowK === $inLow ||
            $outSlowK === $inClose) {
            $tempBuffer = $outSlowK;
        } elseif ($outSlowD === $inHigh ||
                  $outSlowD === $inLow ||
                  $outSlowD === $inClose) {
            $tempBuffer = $outSlowD;
        } else {
            $tempBuffer = static::double($endIdx - $today + 1);
        }
        while ($today <= $endIdx) {
            $tmp = $inLow[$today];
            if ($lowestIdx < $trailingIdx) {
                $lowestIdx = $trailingIdx;
                $lowest = $inLow[$lowestIdx];
                $i = $lowestIdx;
                while (++$i <= $today) {
                    $tmp = $inLow[$i];
                    if ($tmp < $lowest) {
                        $lowestIdx = $i;
                        $lowest = $tmp;
                    }
                }
                $diff = ($highest - $lowest) / 100.0;
            } elseif ($tmp <= $lowest) {
                $lowestIdx = $today;
                $lowest = $tmp;
                $diff = ($highest - $lowest) / 100.0;
            }
            $tmp = $inHigh[$today];
            if ($highestIdx < $trailingIdx) {
                $highestIdx = $trailingIdx;
                $highest = $inHigh[$highestIdx];
                $i = $highestIdx;
                while (++$i <= $today) {
                    $tmp = $inHigh[$i];
                    if ($tmp > $highest) {
                        $highestIdx = $i;
                        $highest = $tmp;
                    }
                }
                $diff = ($highest - $lowest) / 100.0;
            } elseif ($tmp >= $highest) {
                $highestIdx = $today;
                $highest = $tmp;
                $diff = ($highest - $lowest) / 100.0;
            }
            if ($diff !== 0.0) {
                $tempBuffer[$outIdx++] = ($inClose[$today] - $lowest) / $diff;
            } else {
                $tempBuffer[$outIdx++] = 0.0;
            }
            $trailingIdx++;
            $today++;
        }
        $ReturnCode = OverlapStudies::movingAverage(
            0,
            $outIdx - 1,
            $tempBuffer,
            $optInSlowK_Period,
            $optInSlowK_MAType,
            $outBegIdx,
            $outNBElement,
            $tempBuffer
        );
        if ($ReturnCode !== ReturnCode::Success || $outNBElement === 0) {
            $outBegIdx = 0;
            $outNBElement = 0;

            return $ReturnCode;
        }
        $ReturnCode = OverlapStudies::movingAverage(
            0,
            $outNBElement - 1,
            $tempBuffer,
            $optInSlowD_Period,
            $optInSlowD_MAType,
            $outBegIdx,
            $outNBElement,
            $outSlowD
        );
        //System::arraycopy($tempBuffer, $lookbackDSlow, $outSlowK, 0, (int)$outNBElement);
        $outSlowK = \array_slice($tempBuffer, $lookbackDSlow, $outNBElement);
        if ($ReturnCode !== ReturnCode::Success) {
            $outBegIdx = 0;
            $outNBElement = 0;

            return $ReturnCode;
        }
        $outBegIdx = $startIdx;

        return ReturnCode::Success;
    }

    public static function stochF(
        int $startIdx,
        int $endIdx,
        array $inHigh,
        array $inLow,
        array $inClose,
        int $optInFastK_Period,
        int $optInFastD_Period,
        int $optInFastD_MAType,
        int &$outBegIdx,
        int &$outNBElement,
        array &$outFastK,
        array &$outFastD
    ): int {
        if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) {
            return $RetCode;
        }
        if ($optInFastK_Period === PHP_INT_MIN) {
            $optInFastK_Period = 5;
        } elseif ($optInFastK_Period < 1 || $optInFastK_Period > 100000) {
            return ReturnCode::BadParam;
        }
        if ($optInFastD_Period === PHP_INT_MIN) {
            $optInFastD_Period = 3;
        } elseif ($optInFastD_Period < 1 || $optInFastD_Period > 100000) {
            return ReturnCode::BadParam;
        }
        $lookbackK = $optInFastK_Period - 1;
        $lookbackFastD = Lookback::movingAverageLookback($optInFastD_Period, $optInFastD_MAType);
        $lookbackTotal = $lookbackK + $lookbackFastD;
        if ($startIdx < $lookbackTotal) {
            $startIdx = $lookbackTotal;
        }
        if ($startIdx > $endIdx) {
            $outBegIdx = 0;
            $outNBElement = 0;

            return ReturnCode::Success;
        }
        $outIdx = 0;
        $trailingIdx = $startIdx - $lookbackTotal;
        $today = $trailingIdx + $lookbackK;
        $lowestIdx = $highestIdx = -1;
        $diff = $highest = $lowest = 0.0;
        if ($outFastK === $inHigh ||
            $outFastK === $inLow ||
            $outFastK === $inClose) {
            $tempBuffer = $outFastK;
        } elseif ($outFastD === $inHigh ||
                  $outFastD === $inLow ||
                  $outFastD === $inClose) {
            $tempBuffer = $outFastD;
        } else {
            $tempBuffer = static::double($endIdx - $today + 1);
        }
        while ($today <= $endIdx) {
            $tmp = $inLow[$today];
            if ($lowestIdx < $trailingIdx) {
                $lowestIdx = $trailingIdx;
                $lowest = $inLow[$lowestIdx];
                $i = $lowestIdx;
                while (++$i <= $today) {
                    $tmp = $inLow[$i];
                    if ($tmp < $lowest) {
                        $lowestIdx = $i;
                        $lowest = $tmp;
                    }
                }
                $diff = ($highest - $lowest) / 100.0;
            } elseif ($tmp <= $lowest) {
                $lowestIdx = $today;
                $lowest = $tmp;
                $diff = ($highest - $lowest) / 100.0;
            }
            $tmp = $inHigh[$today];
            if ($highestIdx < $trailingIdx) {
                $highestIdx = $trailingIdx;
                $highest = $inHigh[$highestIdx];
                $i = $highestIdx;
                while (++$i <= $today) {
                    $tmp = $inHigh[$i];
                    if ($tmp > $highest) {
                        $highestIdx = $i;
                        $highest = $tmp;
                    }
                }
                $diff = ($highest - $lowest) / 100.0;
            } elseif ($tmp >= $highest) {
                $highestIdx = $today;
                $highest = $tmp;
                $diff = ($highest - $lowest) / 100.0;
            }
            if ($diff !== 0.0) {
                $tempBuffer[$outIdx++] = ($inClose[$today] - $lowest) / $diff;
            } else {
                $tempBuffer[$outIdx++] = 0.0;
            }
            $trailingIdx++;
            $today++;
        }
        $ReturnCode = OverlapStudies::movingAverage(
            0,
            $outIdx - 1,
            $tempBuffer,
            $optInFastD_Period,
            $optInFastD_MAType,
            $outBegIdx,
            $outNBElement,
            $outFastD
        );
        if ($ReturnCode !== ReturnCode::Success || $outNBElement === 0) {
            $outBegIdx = 0;
            $outNBElement = 0;

            return $ReturnCode;
        }
        //System::arraycopy($tempBuffer, $lookbackFastD, $outFastK, 0, (int)$outNBElement);
        $outFastK = \array_slice($tempBuffer, $lookbackFastD, $outNBElement);
        $outBegIdx = $startIdx;

        return ReturnCode::Success;
    }

    public static function stochRsi(
        int $startIdx,
        int $endIdx,
        array $inReal,
        int $optInTimePeriod,
        int $optInFastK_Period,
        int $optInFastD_Period,
        int $optInFastD_MAType,
        int &$outBegIdx,
        int &$outNBElement,
        array &$outFastK,
        array &$outFastD
    ): int {
        if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) {
            return $RetCode;
        }
        $outBegIdx1 = 0;
        $outBegIdx2 = 0;
        $outNbElement1 = 0;
        if ($optInTimePeriod === PHP_INT_MIN) {
            $optInTimePeriod = 14;
        } elseif ($optInTimePeriod < 2 || $optInTimePeriod > 100000) {
            return ReturnCode::BadParam;
        }
        if ($optInFastK_Period === PHP_INT_MIN) {
            $optInFastK_Period = 5;
        } elseif ($optInFastK_Period < 1 || $optInFastK_Period > 100000) {
            return ReturnCode::BadParam;
        }
        if ($optInFastD_Period === PHP_INT_MIN) {
            $optInFastD_Period = 3;
        } elseif ($optInFastD_Period < 1 || $optInFastD_Period > 100000) {
            return ReturnCode::BadParam;
        }
        $outBegIdx = 0;
        $outNBElement = 0;
        $lookbackSTOCHF = Lookback::stochFLookback($optInFastK_Period, $optInFastD_Period, $optInFastD_MAType);
        $lookbackTotal = Lookback::rsiLookback($optInTimePeriod) + $lookbackSTOCHF;
        if ($startIdx < $lookbackTotal) {
            $startIdx = $lookbackTotal;
        }
        if ($startIdx > $endIdx) {
            return ReturnCode::Success;
        }
        $outBegIdx = $startIdx;
        $tempArraySize = $endIdx - $startIdx + 1 + $lookbackSTOCHF;
        $tempRSIBuffer = static::double($tempArraySize);
        $ReturnCode = self::rsi(
            $startIdx - $lookbackSTOCHF,
            $endIdx,
            $inReal,
            $optInTimePeriod,
            $outBegIdx1,
            $outNbElement1,
            $tempRSIBuffer
        );
        if ($ReturnCode !== ReturnCode::Success || $outNbElement1 === 0) {
            $outBegIdx = 0;

            return $ReturnCode;
        }
        $ReturnCode = self::stochF(
            0,
            $tempArraySize - 1,
            $tempRSIBuffer,
            $tempRSIBuffer,
            $tempRSIBuffer,
            $optInFastK_Period,
            $optInFastD_Period,
            $optInFastD_MAType,
            $outBegIdx2,
            $outNBElement,
            $outFastK,
            $outFastD
        );
        if ($ReturnCode !== ReturnCode::Success || $outNBElement === 0) {
            $outBegIdx = 0;
            $outNBElement = 0;

            return $ReturnCode;
        }

        return ReturnCode::Success;
    }

    public static function trix(int $startIdx, int $endIdx, array $inReal, int $optInTimePeriod, int &$outBegIdx, int &$outNBElement, array &$outReal): int
    {
        if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) {
            return $RetCode;
        }
        $nbElement = 0;
        $begIdx = 0;
        if ($optInTimePeriod === PHP_INT_MIN) {
            $optInTimePeriod = 30;
        } elseif ($optInTimePeriod < 1 || $optInTimePeriod > 100000) {
            return ReturnCode::BadParam;
        }
        $emaLookback = Lookback::emaLookback($optInTimePeriod);
        $rocLookback = Lookback::rocRLookback(1);
        $totalLookback = $emaLookback * 3 + $rocLookback;
        if ($startIdx < $totalLookback) {
            $startIdx = (int)$totalLookback;
        }
        if ($startIdx > $endIdx) {
            $outNBElement = 0;
            $outBegIdx = 0;

            return ReturnCode::Success;
        }
        $outBegIdx = $startIdx;
        $nbElementToOutput = $endIdx - $startIdx + 1 + $totalLookback;
        $tempBuffer = static::double($nbElementToOutput);
        $k = 2.0 / (double)($optInTimePeriod + 1);
        $ReturnCode = static::TA_INT_EMA(
            $startIdx - $totalLookback,
            $endIdx,
            $inReal,
            $optInTimePeriod,
            $k,
            $begIdx,
            $nbElement,
            $tempBuffer
        );
        if ($ReturnCode !== ReturnCode::Success || $nbElement === 0) {
            $outNBElement = 0;
            $outBegIdx = 0;

            return $ReturnCode;
        }
        $nbElementToOutput--;
        $nbElementToOutput -= $emaLookback;
        $ReturnCode = static::TA_INT_EMA(
            0,
            $nbElementToOutput,
            $tempBuffer,
            $optInTimePeriod,
            $k,
            $begIdx,
            $nbElement,
            $tempBuffer
        );
        if ($ReturnCode !== ReturnCode::Success || $nbElement === 0) {
            $outNBElement = 0;
            $outBegIdx = 0;

            return $ReturnCode;
        }
        $nbElementToOutput -= $emaLookback;
        $ReturnCode = static::TA_INT_EMA(
            0,
            $nbElementToOutput,
            $tempBuffer,
            $optInTimePeriod,
            $k,
            $begIdx,
            $nbElement,
            $tempBuffer
        );
        if ($ReturnCode !== ReturnCode::Success || $nbElement === 0) {
            $outNBElement = 0;
            $outBegIdx = 0;

            return $ReturnCode;
        }
        $nbElementToOutput -= $emaLookback;
        $ReturnCode = self::roc(
            0,
            $nbElementToOutput,
            $tempBuffer,
            1,
            $begIdx,
            $outNBElement,
            $outReal
        );
        if ($ReturnCode !== ReturnCode::Success || $outNBElement === 0) {
            $outNBElement = 0;
            $outBegIdx = 0;

            return $ReturnCode;
        }

        return ReturnCode::Success;
    }

    public static function ultOsc(
        int $startIdx,
        int $endIdx,
        array $inHigh,
        array $inLow,
        array $inClose,
        int $optInTimePeriod1,
        int $optInTimePeriod2,
        int $optInTimePeriod3,
        int &$outBegIdx,
        int &$outNBElement,
        array &$outReal
    ): int {
        if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) {
            return $RetCode;
        }
        $usedFlag = \array_pad([], 3, 0);
        $periods = \array_pad([], 3, 0);
        $sortedPeriods = \array_pad([], 3, 0);
        if ($optInTimePeriod1 === PHP_INT_MIN) {
            $optInTimePeriod1 = 7;
        } elseif ($optInTimePeriod1 < 1 || $optInTimePeriod1 > 100000) {
            return ReturnCode::BadParam;
        }
        if ($optInTimePeriod2 === PHP_INT_MIN) {
            $optInTimePeriod2 = 14;
        } elseif ($optInTimePeriod2 < 1 || $optInTimePeriod2 > 100000) {
            return ReturnCode::BadParam;
        }
        if ($optInTimePeriod3 === PHP_INT_MIN) {
            $optInTimePeriod3 = 28;
        } elseif ($optInTimePeriod3 < 1 || $optInTimePeriod3 > 100000) {
            return ReturnCode::BadParam;
        }
        $outBegIdx = 0;
        $outNBElement = 0;

        $periods = [$optInTimePeriod1, $optInTimePeriod2, $optInTimePeriod3];
        sort($periods);
        [$optInTimePeriod1, $optInTimePeriod2, $optInTimePeriod3] = $periods;

        $lookbackTotal = Lookback::ultOscLookback(...$periods);
        if ($startIdx < $lookbackTotal) {
            $startIdx = $lookbackTotal;
        }
        if ($startIdx > $endIdx) {
            return ReturnCode::Success;
        }
        {
            $a1Total = 0;
            $b1Total = 0;
            for ($i = $startIdx - $optInTimePeriod1 + 1; $i < $startIdx; ++$i) {
                {
                    $tempLT = $inLow[$i];
                    $tempHT = $inHigh[$i];
                    $tempCY = $inClose[$i - 1];
                    $trueLow = min($tempLT, $tempCY);
                    $closeMinusTrueLow = $inClose[$i] - $trueLow;
                    $trueRange = $tempHT - $tempLT;
                    $tempDouble = abs($tempCY - $tempHT);
                    if ($tempDouble > $trueRange) {
                        $trueRange = $tempDouble;
                    }
                    $tempDouble = abs($tempCY - $tempLT);
                    if ($tempDouble > $trueRange) {
                        $trueRange = $tempDouble;
                    }
                }
                $a1Total += $closeMinusTrueLow;
                $b1Total += $trueRange;
            }
        }
        {
            $a2Total = 0;
            $b2Total = 0;
            for ($i = $startIdx - $optInTimePeriod2 + 1; $i < $startIdx; ++$i) {
                {
                    $tempLT = $inLow[$i];
                    $tempHT = $inHigh[$i];
                    $tempCY = $inClose[$i - 1];
                    $trueLow = min($tempLT, $tempCY);
                    $closeMinusTrueLow = $inClose[$i] - $trueLow;
                    $trueRange = $tempHT - $tempLT;
                    $tempDouble = abs($tempCY - $tempHT);
                    if ($tempDouble > $trueRange) {
                        $trueRange = $tempDouble;
                    }
                    $tempDouble = abs($tempCY - $tempLT);
                    if ($tempDouble > $trueRange) {
                        $trueRange = $tempDouble;
                    }
                }
                $a2Total += $closeMinusTrueLow;
                $b2Total += $trueRange;
            }
        }
        {
            $a3Total = 0;
            $b3Total = 0;
            for ($i = $startIdx - $optInTimePeriod3 + 1; $i < $startIdx; ++$i) {
                {
                    $tempLT = $inLow[$i];
                    $tempHT = $inHigh[$i];
                    $tempCY = $inClose[$i - 1];
                    $trueLow = min($tempLT, $tempCY);
                    $closeMinusTrueLow = $inClose[$i] - $trueLow;
                    $trueRange = $tempHT - $tempLT;
                    $tempDouble = abs($tempCY - $tempHT);
                    if ($tempDouble > $trueRange) {
                        $trueRange = $tempDouble;
                    }
                    $tempDouble = abs($tempCY - $tempLT);
                    if ($tempDouble > $trueRange) {
                        $trueRange = $tempDouble;
                    }
                }
                $a3Total += $closeMinusTrueLow;
                $b3Total += $trueRange;
            }
        }
        $today = $startIdx;
        $outIdx = 0;
        $trailingIdx1 = $today - $optInTimePeriod1 + 1;
        $trailingIdx2 = $today - $optInTimePeriod2 + 1;
        $trailingIdx3 = $today - $optInTimePeriod3 + 1;
        while ($today <= $endIdx) {
            {
                $tempLT = $inLow[$today];
                $tempHT = $inHigh[$today];
                $tempCY = $inClose[$today - 1];
                $trueLow = min($tempLT, $tempCY);
                $closeMinusTrueLow = $inClose[$today] - $trueLow;
                $trueRange = $tempHT - $tempLT;
                $tempDouble = abs($tempCY - $tempHT);
                if ($tempDouble > $trueRange) {
                    $trueRange = $tempDouble;
                }
                $tempDouble = abs($tempCY - $tempLT);
                if ($tempDouble > $trueRange) {
                    $trueRange = $tempDouble;
                }
            }
            $a1Total += $closeMinusTrueLow;
            $a2Total += $closeMinusTrueLow;
            $a3Total += $closeMinusTrueLow;
            $b1Total += $trueRange;
            $b2Total += $trueRange;
            $b3Total += $trueRange;
            $output = 0.0;
            if (!(-0.00000001 < $b1Total && $b1Total < 0.00000001)) {
                $output += 4.0 * ($a1Total / $b1Total);
            }
            if (!(-0.00000001 < $b2Total && $b2Total < 0.00000001)) {
                $output += 2.0 * ($a2Total / $b2Total);
            }
            if (!(-0.00000001 < $b3Total && $b3Total < 0.00000001)) {
                $output += $a3Total / $b3Total;
            }
            {
                $tempLT = $inLow[$trailingIdx1];
                $tempHT = $inHigh[$trailingIdx1];
                $tempCY = $inClose[$trailingIdx1 - 1];
                $trueLow = min($tempLT, $tempCY);
                $closeMinusTrueLow = $inClose[$trailingIdx1] - $trueLow;
                $trueRange = $tempHT - $tempLT;
                $tempDouble = abs($tempCY - $tempHT);
                if ($tempDouble > $trueRange) {
                    $trueRange = $tempDouble;
                }
                $tempDouble = abs($tempCY - $tempLT);
                if ($tempDouble > $trueRange) {
                    $trueRange = $tempDouble;
                }
            }
            $a1Total -= $closeMinusTrueLow;
            $b1Total -= $trueRange;
            {
                $tempLT = $inLow[$trailingIdx2];
                $tempHT = $inHigh[$trailingIdx2];
                $tempCY = $inClose[$trailingIdx2 - 1];
                $trueLow = min($tempLT, $tempCY);
                $closeMinusTrueLow = $inClose[$trailingIdx2] - $trueLow;
                $trueRange = $tempHT - $tempLT;
                $tempDouble = abs($tempCY - $tempHT);
                if ($tempDouble > $trueRange) {
                    $trueRange = $tempDouble;
                }
                $tempDouble = abs($tempCY - $tempLT);
                if ($tempDouble > $trueRange) {
                    $trueRange = $tempDouble;
                }
            }
            $a2Total -= $closeMinusTrueLow;
            $b2Total -= $trueRange;
            {
                $tempLT = $inLow[$trailingIdx3];
                $tempHT = $inHigh[$trailingIdx3];
                $tempCY = $inClose[$trailingIdx3 - 1];
                $trueLow = min($tempLT, $tempCY);
                $closeMinusTrueLow = $inClose[$trailingIdx3] - $trueLow;
                $trueRange = $tempHT - $tempLT;
                $tempDouble = abs($tempCY - $tempHT);
                if ($tempDouble > $trueRange) {
                    $trueRange = $tempDouble;
                }
                $tempDouble = abs($tempCY - $tempLT);
                if ($tempDouble > $trueRange) {
                    $trueRange = $tempDouble;
                }
            }
            $a3Total -= $closeMinusTrueLow;
            $b3Total -= $trueRange;
            $outReal[$outIdx] = 100.0 * ($output / 7.0);
            $outIdx++;
            $today++;
            $trailingIdx1++;
            $trailingIdx2++;
            $trailingIdx3++;
        }
        $outNBElement = $outIdx;
        $outBegIdx = $startIdx;

        return ReturnCode::Success;
    }

    public static function willR(int $startIdx, int $endIdx, array $inHigh, array $inLow, array $inClose, int $optInTimePeriod, int &$outBegIdx, int &$outNBElement, array &$outReal): int
    {
        if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) {
            return $RetCode;
        }
        if ($optInTimePeriod === PHP_INT_MIN) {
            $optInTimePeriod = 14;
        } elseif ($optInTimePeriod < 2 || $optInTimePeriod > 100000) {
            return ReturnCode::BadParam;
        }
        $nbInitialElementNeeded = $optInTimePeriod - 1;
        if ($startIdx < $nbInitialElementNeeded) {
            $startIdx = $nbInitialElementNeeded;
        }
        if ($startIdx > $endIdx) {
            $outBegIdx = 0;
            $outNBElement = 0;

            return ReturnCode::Success;
        }
        $diff = 0.0;
        $outIdx = 0;
        $today = $startIdx;
        $trailingIdx = $startIdx - $nbInitialElementNeeded;
        $lowestIdx = $highestIdx = -1;
        $diff = $highest = $lowest = 0.0;
        while ($today <= $endIdx) {
            $tmp = $inLow[$today];
            if ($lowestIdx < $trailingIdx) {
                $lowestIdx = $trailingIdx;
                $lowest = $inLow[$lowestIdx];
                $i = $lowestIdx;
                while (++$i <= $today) {
                    $tmp = $inLow[$i];
                    if ($tmp < $lowest) {
                        $lowestIdx = $i;
                        $lowest = $tmp;
                    }
                }
                $diff = ($highest - $lowest) / -100.0;
            } elseif ($tmp <= $lowest) {
                $lowestIdx = $today;
                $lowest = $tmp;
                $diff = ($highest - $lowest) / -100.0;
            }
            $tmp = $inHigh[$today];
            if ($highestIdx < $trailingIdx) {
                $highestIdx = $trailingIdx;
                $highest = $inHigh[$highestIdx];
                $i = $highestIdx;
                while (++$i <= $today) {
                    $tmp = $inHigh[$i];
                    if ($tmp > $highest) {
                        $highestIdx = $i;
                        $highest = $tmp;
                    }
                }
                $diff = ($highest - $lowest) / -100.0;
            } elseif ($tmp >= $highest) {
                $highestIdx = $today;
                $highest = $tmp;
                $diff = ($highest - $lowest) / -100.0;
            }
            if ($diff !== 0.0) {
                $outReal[$outIdx++] = ($highest - $inClose[$today]) / $diff;
            } else {
                $outReal[$outIdx++] = 0.0;
            }
            $trailingIdx++;
            $today++;
        }
        $outBegIdx = $startIdx;
        $outNBElement = $outIdx;

        return ReturnCode::Success;
    }
}
 ?>

Did this file decode correctly?

Original Code

<?php

/**
 * This is a PHP port of the Trader extension for PHP, which is a port of the TA-LIB C code.
 *
 * This port is written in PHP and without any other requirements.
 * The goal is that this library can be used by those whom cannot install the PHP Trader extension.
 *
 * Below is the copyright information for TA-LIB found in the source code.
 */

/* TA-LIB Copyright (c) 1999-2007, Mario Fortier
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or
 * without modification, are permitted provided that the following
 * conditions are met:
 *
 * - Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in
 *   the documentation and/or other materials provided with the
 *   distribution.
 *
 * - Neither name of author nor the names of its contributors
 *   may be used to endorse or promote products derived from this
 *   software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

namespace LupeCode\phpTraderNative\TALib\Core;

use LupeCode\phpTraderNative\TALib\Classes\MoneyFlow;
use LupeCode\phpTraderNative\TALib\Enum\Compatibility;
use LupeCode\phpTraderNative\TALib\Enum\ReturnCode;
use LupeCode\phpTraderNative\TALib\Enum\UnstablePeriodFunctionID;

class MomentumIndicators extends Core
{
    public static function adx(int $startIdx, int $endIdx, array $inHigh, array $inLow, array $inClose, int $optInTimePeriod, int &$outBegIdx, int &$outNBElement, array &$outReal): int
    {
        if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) {
            return $RetCode;
        }
        if ($optInTimePeriod === PHP_INT_MIN) {
            $optInTimePeriod = 14;
        } elseif ($optInTimePeriod < 2 || $optInTimePeriod > 100000) {
            return ReturnCode::BadParam;
        }
        $lookbackTotal = 2 * $optInTimePeriod + static::$unstablePeriod[UnstablePeriodFunctionID::ADX] - 1;
        if ($startIdx < $lookbackTotal) {
            $startIdx = (int)$lookbackTotal;
        }
        if ($startIdx > $endIdx) {
            $outBegIdx = 0;
            $outNBElement = 0;

            return ReturnCode::Success;
        }
        $outBegIdx = $today = $startIdx;
        $prevMinusDM = 0.0;
        $prevPlusDM = 0.0;
        $prevTR = 0.0;
        $today = $startIdx - $lookbackTotal;
        $prevHigh = $inHigh[$today];
        $prevLow = $inLow[$today];
        $prevClose = $inClose[$today];
        $i = $optInTimePeriod - 1;
        while ($i-- > 0) {
            $today++;
            $tempReal = $inHigh[$today];
            $diffP = $tempReal - $prevHigh;
            $prevHigh = $tempReal;
            $tempReal = $inLow[$today];
            $diffM = $prevLow - $tempReal;
            $prevLow = $tempReal;
            if ($diffM > 0 && $diffP < $diffM) {
                $prevMinusDM += $diffM;
            } elseif ($diffP > 0 && $diffP > $diffM) {
                $prevPlusDM += $diffP;
            }
            {
                $tempReal = $prevHigh - $prevLow;
                $tempReal2 = abs($prevHigh - $prevClose);
                if ($tempReal2 > $tempReal) {
                    $tempReal = $tempReal2;
                }
                $tempReal2 = abs($prevLow - $prevClose);
                if ($tempReal2 > $tempReal) {
                    $tempReal = $tempReal2;
                }
            }
            $prevTR += $tempReal;
            $prevClose = $inClose[$today];
        }
        $sumDX = 0.0;
        $i = $optInTimePeriod;
        while ($i-- > 0) {
            $today++;
            $tempReal = $inHigh[$today];
            $diffP = $tempReal - $prevHigh;
            $prevHigh = $tempReal;
            $tempReal = $inLow[$today];
            $diffM = $prevLow - $tempReal;
            $prevLow = $tempReal;
            $prevMinusDM -= $prevMinusDM / $optInTimePeriod;
            $prevPlusDM -= $prevPlusDM / $optInTimePeriod;
            if ($diffM > 0 && $diffP < $diffM) {
                $prevMinusDM += $diffM;
            } elseif ($diffP > 0 && $diffP > $diffM) {
                $prevPlusDM += $diffP;
            }
            {
                $tempReal = $prevHigh - $prevLow;
                $tempReal2 = abs($prevHigh - $prevClose);
                if ($tempReal2 > $tempReal) {
                    $tempReal = $tempReal2;
                }
                $tempReal2 = abs($prevLow - $prevClose);
                if ($tempReal2 > $tempReal) {
                    $tempReal = $tempReal2;
                }
            }
            $prevTR = $prevTR - $prevTR / $optInTimePeriod + $tempReal;
            $prevClose = $inClose[$today];
            if (!(-0.00000001 < $prevTR && $prevTR < 0.00000001)) {
                $minusDI = 100.0 * ($prevMinusDM / $prevTR);
                $plusDI = 100.0 * ($prevPlusDM / $prevTR);
                $tempReal = $minusDI + $plusDI;
                if (!(-0.00000001 < $tempReal && $tempReal < 0.00000001)) {
                    $sumDX += 100.0 * (abs($minusDI - $plusDI) / $tempReal);
                }
            }
        }
        $prevADX = $sumDX / $optInTimePeriod;
        $i = static::$unstablePeriod[UnstablePeriodFunctionID::ADX];
        while ($i-- > 0) {
            $today++;
            $tempReal = $inHigh[$today];
            $diffP = $tempReal - $prevHigh;
            $prevHigh = $tempReal;
            $tempReal = $inLow[$today];
            $diffM = $prevLow - $tempReal;
            $prevLow = $tempReal;
            $prevMinusDM -= $prevMinusDM / $optInTimePeriod;
            $prevPlusDM -= $prevPlusDM / $optInTimePeriod;
            if ($diffM > 0 && $diffP < $diffM) {
                $prevMinusDM += $diffM;
            } elseif ($diffP > 0 && $diffP > $diffM) {
                $prevPlusDM += $diffP;
            }
            {
                $tempReal = $prevHigh - $prevLow;
                $tempReal2 = abs($prevHigh - $prevClose);
                if ($tempReal2 > $tempReal) {
                    $tempReal = $tempReal2;
                }
                $tempReal2 = abs($prevLow - $prevClose);
                if ($tempReal2 > $tempReal) {
                    $tempReal = $tempReal2;
                }
            }
            $prevTR = $prevTR - $prevTR / $optInTimePeriod + $tempReal;
            $prevClose = $inClose[$today];
            if (!(-0.00000001 < $prevTR && $prevTR < 0.00000001)) {
                $minusDI = 100.0 * ($prevMinusDM / $prevTR);
                $plusDI = 100.0 * ($prevPlusDM / $prevTR);
                $tempReal = $minusDI + $plusDI;
                if (!(-0.00000001 < $tempReal && $tempReal < 0.00000001)) {
                    $tempReal = 100.0 * (abs($minusDI - $plusDI) / $tempReal);
                    $prevADX = ($prevADX * ($optInTimePeriod - 1) + $tempReal) / $optInTimePeriod;
                }
            }
        }
        $outReal[0] = $prevADX;
        $outIdx = 1;
        while ($today < $endIdx) {
            $today++;
            $tempReal = $inHigh[$today];
            $diffP = $tempReal - $prevHigh;
            $prevHigh = $tempReal;
            $tempReal = $inLow[$today];
            $diffM = $prevLow - $tempReal;
            $prevLow = $tempReal;
            $prevMinusDM -= $prevMinusDM / $optInTimePeriod;
            $prevPlusDM -= $prevPlusDM / $optInTimePeriod;
            if ($diffM > 0 && $diffP < $diffM) {
                $prevMinusDM += $diffM;
            } elseif ($diffP > 0 && $diffP > $diffM) {
                $prevPlusDM += $diffP;
            }
            {
                $tempReal = $prevHigh - $prevLow;
                $tempReal2 = abs($prevHigh - $prevClose);
                if ($tempReal2 > $tempReal) {
                    $tempReal = $tempReal2;
                }
                $tempReal2 = abs($prevLow - $prevClose);
                if ($tempReal2 > $tempReal) {
                    $tempReal = $tempReal2;
                }
            }
            $prevTR = $prevTR - $prevTR / $optInTimePeriod + $tempReal;
            $prevClose = $inClose[$today];
            if (!(-0.00000001 < $prevTR && $prevTR < 0.00000001)) {
                $minusDI = 100.0 * ($prevMinusDM / $prevTR);
                $plusDI = 100.0 * ($prevPlusDM / $prevTR);
                $tempReal = $minusDI + $plusDI;
                if (!(-0.00000001 < $tempReal && $tempReal < 0.00000001)) {
                    $tempReal = 100.0 * (abs($minusDI - $plusDI) / $tempReal);
                    $prevADX = ($prevADX * ($optInTimePeriod - 1) + $tempReal) / $optInTimePeriod;
                }
            }
            $outReal[$outIdx++] = $prevADX;
        }
        $outNBElement = $outIdx;

        return ReturnCode::Success;
    }

    public static function adxr(int $startIdx, int $endIdx, array $inHigh, array $inLow, array $inClose, int $optInTimePeriod, int &$outBegIdx, int &$outNBElement, array &$outReal): int
    {
        if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) {
            return $RetCode;
        }
        if ($optInTimePeriod === PHP_INT_MIN) {
            $optInTimePeriod = 14;
        } elseif ($optInTimePeriod < 2 || $optInTimePeriod > 100000) {
            return ReturnCode::BadParam;
        }
        $adxrLookback = Lookback::adxrLookback($optInTimePeriod);
        if ($startIdx < $adxrLookback) {
            $startIdx = $adxrLookback;
        }
        if ($startIdx > $endIdx) {
            $outBegIdx = 0;
            $outNBElement = 0;

            return ReturnCode::Success;
        }
        $adx = static::double($endIdx - $startIdx + $optInTimePeriod);
        $ReturnCode = self::adx($startIdx - ($optInTimePeriod - 1), $endIdx, $inHigh, $inLow, $inClose, $optInTimePeriod, $outBegIdx, $outNBElement, $adx);
        if ($ReturnCode !== ReturnCode::Success) {
            return $ReturnCode;
        }
        $i = $optInTimePeriod - 1;
        $j = 0;
        $outIdx = 0;
        $nbElement = $endIdx - $startIdx + 2;
        while (--$nbElement > 0) {
            $outReal[$outIdx++] = ($adx[$i++] + $adx[$j++]) / 2.0;
        }
        $outBegIdx = $startIdx;
        $outNBElement = $outIdx;

        return ReturnCode::Success;
    }

    public static function apo(int $startIdx, int $endIdx, array $inReal, int $optInFastPeriod, int $optInSlowPeriod, int $optInMAType, int &$outBegIdx, int &$outNBElement, array &$outReal): int
    {
        if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) {
            return $RetCode;
        }
        if ($optInFastPeriod === PHP_INT_MIN) {
            $optInFastPeriod = 12;
        } elseif ($optInFastPeriod < 2 || $optInFastPeriod > 100000) {
            return ReturnCode::BadParam;
        }
        if ($optInSlowPeriod === PHP_INT_MIN) {
            $optInSlowPeriod = 26;
        } elseif ($optInSlowPeriod < 2 || $optInSlowPeriod > 100000) {
            return ReturnCode::BadParam;
        }
        $tempBuffer = static::double($endIdx - $startIdx + 1);

        return static::TA_INT_PO($startIdx, $endIdx, $inReal, $optInFastPeriod, $optInSlowPeriod, $optInMAType, $outBegIdx, $outNBElement, $outReal, $tempBuffer, false);
    }

    public static function aroon(int $startIdx, int $endIdx, array $inHigh, array $inLow, int $optInTimePeriod, int &$outBegIdx, int &$outNBElement, array &$outAroonDown, array &$outAroonUp): int
    {
        if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) {
            return $RetCode;
        }
        if ($optInTimePeriod === PHP_INT_MIN) {
            $optInTimePeriod = 14;
        } elseif ($optInTimePeriod < 2 || $optInTimePeriod > 100000) {
            return ReturnCode::BadParam;
        }
        if ($startIdx < $optInTimePeriod) {
            $startIdx = $optInTimePeriod;
        }
        if ($startIdx > $endIdx) {
            $outBegIdx = 0;
            $outNBElement = 0;

            return ReturnCode::Success;
        }
        $outIdx = 0;
        $today = $startIdx;
        $trailingIdx = $startIdx - $optInTimePeriod;
        $lowestIdx = -1;
        $highestIdx = -1;
        $lowest = 0.0;
        $highest = 0.0;
        $factor = 100.0 / (double)$optInTimePeriod;
        while ($today <= $endIdx) {
            $tmp = $inLow[$today];
            if ($lowestIdx < $trailingIdx) {
                $lowestIdx = $trailingIdx;
                $lowest = $inLow[$lowestIdx];
                $i = $lowestIdx;
                while (++$i <= $today) {
                    $tmp = $inLow[$i];
                    if ($tmp <= $lowest) {
                        $lowestIdx = $i;
                        $lowest = $tmp;
                    }
                }
            } elseif ($tmp <= $lowest) {
                $lowestIdx = $today;
                $lowest = $tmp;
            }
            $tmp = $inHigh[$today];
            if ($highestIdx < $trailingIdx) {
                $highestIdx = $trailingIdx;
                $highest = $inHigh[$highestIdx];
                $i = $highestIdx;
                while (++$i <= $today) {
                    $tmp = $inHigh[$i];
                    if ($tmp >= $highest) {
                        $highestIdx = $i;
                        $highest = $tmp;
                    }
                }
            } elseif ($tmp >= $highest) {
                $highestIdx = $today;
                $highest = $tmp;
            }
            $outAroonUp[$outIdx] = $factor * ($optInTimePeriod - ($today - $highestIdx));
            $outAroonDown[$outIdx] = $factor * ($optInTimePeriod - ($today - $lowestIdx));
            $outIdx++;
            $trailingIdx++;
            $today++;
        }
        $outBegIdx = $startIdx;
        $outNBElement = $outIdx;

        return ReturnCode::Success;
    }

    public static function aroonOsc(int $startIdx, int $endIdx, array $inHigh, array $inLow, int $optInTimePeriod, int &$outBegIdx, int &$outNBElement, array &$outReal): int
    {
        if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) {
            return $RetCode;
        }
        if ($optInTimePeriod === PHP_INT_MIN) {
            $optInTimePeriod = 14;
        } elseif ($optInTimePeriod < 2 || $optInTimePeriod > 100000) {
            return ReturnCode::BadParam;
        }
        if ($startIdx < $optInTimePeriod) {
            $startIdx = $optInTimePeriod;
        }
        if ($startIdx > $endIdx) {
            $outBegIdx = 0;
            $outNBElement = 0;

            return ReturnCode::Success;
        }
        $outIdx = 0;
        $today = $startIdx;
        $trailingIdx = $startIdx - $optInTimePeriod;
        $lowestIdx = -1;
        $highestIdx = -1;
        $lowest = 0.0;
        $highest = 0.0;
        $factor = 100.0 / (double)$optInTimePeriod;
        while ($today <= $endIdx) {
            $tmp = $inLow[$today];
            if ($lowestIdx < $trailingIdx) {
                $lowestIdx = $trailingIdx;
                $lowest = $inLow[$lowestIdx];
                $i = $lowestIdx;
                while (++$i <= $today) {
                    $tmp = $inLow[$i];
                    if ($tmp <= $lowest) {
                        $lowestIdx = $i;
                        $lowest = $tmp;
                    }
                }
            } elseif ($tmp <= $lowest) {
                $lowestIdx = $today;
                $lowest = $tmp;
            }
            $tmp = $inHigh[$today];
            if ($highestIdx < $trailingIdx) {
                $highestIdx = $trailingIdx;
                $highest = $inHigh[$highestIdx];
                $i = $highestIdx;
                while (++$i <= $today) {
                    $tmp = $inHigh[$i];
                    if ($tmp >= $highest) {
                        $highestIdx = $i;
                        $highest = $tmp;
                    }
                }
            } elseif ($tmp >= $highest) {
                $highestIdx = $today;
                $highest = $tmp;
            }
            $aroon = $factor * ($highestIdx - $lowestIdx);
            $outReal[$outIdx] = $aroon;
            $outIdx++;
            $trailingIdx++;
            $today++;
        }
        $outBegIdx = $startIdx;
        $outNBElement = $outIdx;

        return ReturnCode::Success;
    }

    public static function bop(int $startIdx, int $endIdx, array $inOpen, array $inHigh, array $inLow, array $inClose, int &$outBegIdx, int &$outNBElement, array &$outReal): int
    {
        if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) {
            return $RetCode;
        }
        $outIdx = 0;
        for ($i = $startIdx; $i <= $endIdx; $i++) {
            $tempReal = $inHigh[$i] - $inLow[$i];
            if ($tempReal < 0.00000001) {
                $outReal[$outIdx++] = 0.0;
            } else {
                $outReal[$outIdx++] = ($inClose[$i] - $inOpen[$i]) / $tempReal;
            }
        }
        $outNBElement = $outIdx;
        $outBegIdx = $startIdx;

        return ReturnCode::Success;
    }

    public static function cci(int $startIdx, int $endIdx, array $inHigh, array $inLow, array $inClose, int $optInTimePeriod, int &$outBegIdx, int &$outNBElement, array &$outReal): int
    {
        if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) {
            return $RetCode;
        }
        $circBuffer_Idx = 0;
        if ($optInTimePeriod === PHP_INT_MIN) {
            $optInTimePeriod = 14;
        } elseif ($optInTimePeriod < 2 || $optInTimePeriod > 100000) {
            return ReturnCode::BadParam;
        }
        $lookbackTotal = $optInTimePeriod - 1;
        if ($startIdx < $lookbackTotal) {
            $startIdx = $lookbackTotal;
        }
        if ($startIdx > $endIdx) {
            $outBegIdx = 0;
            $outNBElement = 0;

            return ReturnCode::Success;
        }
        {
            $circBuffer = static::double($optInTimePeriod);
            $maxIdx_circBuffer = $optInTimePeriod - 1;
        }
        $i = $startIdx - $lookbackTotal;
        while ($i < $startIdx) {
            $circBuffer[$circBuffer_Idx] = ($inHigh[$i] + $inLow[$i] + $inClose[$i]) / 3;
            $i++;
            {
                $circBuffer_Idx++;
                if ($circBuffer_Idx > $maxIdx_circBuffer) {
                    $circBuffer_Idx = 0;
                }
            }
        }
        $outIdx = 0;
        do {
            $lastValue = ($inHigh[$i] + $inLow[$i] + $inClose[$i]) / 3;
            $circBuffer[$circBuffer_Idx] = $lastValue;
            $theAverage = 0;
            for ($j = 0; $j < $optInTimePeriod; $j++) {
                $theAverage += $circBuffer[$j];
            }
            $theAverage /= $optInTimePeriod;
            $tempReal2 = 0;
            for ($j = 0; $j < $optInTimePeriod; $j++) {
                $tempReal2 += abs($circBuffer[$j] - $theAverage);
            }
            $tempReal = $lastValue - $theAverage;
            if ($tempReal !== 0.0 && $tempReal2 !== 0.0) {
                $outReal[$outIdx++] = $tempReal / (0.015 * ($tempReal2 / $optInTimePeriod));
            } else {
                $outReal[$outIdx++] = 0.0;
            }
            {
                $circBuffer_Idx++;
                if ($circBuffer_Idx > $maxIdx_circBuffer) {
                    $circBuffer_Idx = 0;
                }
            }
            $i++;
        } while ($i <= $endIdx);
        $outNBElement = $outIdx;
        $outBegIdx = $startIdx;

        return ReturnCode::Success;
    }

    public static function cmo(int $startIdx, int $endIdx, array $inReal, int $optInTimePeriod, int &$outBegIdx, int &$outNBElement, array &$outReal): int
    {
        if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) {
            return $RetCode;
        }
        if ($optInTimePeriod === PHP_INT_MIN) {
            $optInTimePeriod = 14;
        } elseif ($optInTimePeriod < 2 || $optInTimePeriod > 100000) {
            return ReturnCode::BadParam;
        }
        $outBegIdx = 0;
        $outNBElement = 0;
        $lookbackTotal = Lookback::cmoLookback($optInTimePeriod);
        if ($startIdx < $lookbackTotal) {
            $startIdx = $lookbackTotal;
        }
        if ($startIdx > $endIdx) {
            return ReturnCode::Success;
        }
        $outIdx = 0;
        $today = $startIdx - $lookbackTotal;
        $prevValue = $inReal[$today];
        $unstablePeriod = static::$unstablePeriod[UnstablePeriodFunctionID::CMO];
        if ($unstablePeriod === 0 && static::$compatibility === Compatibility::Metastock) {
            $savePrevValue = $prevValue;
            $prevGain = 0.0;
            $prevLoss = 0.0;
            for ($i = $optInTimePeriod; $i > 0; $i--) {
                $tempValue1 = $inReal[$today++];
                $tempValue2 = $tempValue1 - $prevValue;
                $prevValue = $tempValue1;
                if ($tempValue2 < 0) {
                    $prevLoss -= $tempValue2;
                } else {
                    $prevGain += $tempValue2;
                }
            }
            $tempValue1 = $prevLoss / $optInTimePeriod;
            $tempValue2 = $prevGain / $optInTimePeriod;
            $tempValue3 = $tempValue2 - $tempValue1;
            $tempValue4 = $tempValue1 + $tempValue2;
            if (!(-0.00000001 < $tempValue4 && $tempValue4 < 0.00000001)) {
                $outReal[$outIdx++] = 100 * ($tempValue3 / $tempValue4);
            } else {
                $outReal[$outIdx++] = 0.0;
            }
            if ($today > $endIdx) {
                $outBegIdx = $startIdx;
                $outNBElement = $outIdx;

                return ReturnCode::Success;
            }
            $today -= $optInTimePeriod;
            $prevValue = $savePrevValue;
        }
        $prevLoss = 0.0;
        $prevGain = 0.0;
        $today++;
        for ($i = $optInTimePeriod; $i > 0; $i--) {
            $tempValue1 = $inReal[$today++];
            $tempValue2 = $tempValue1 - $prevValue;
            $prevValue = $tempValue1;
            if ($tempValue2 < 0) {
                $prevLoss -= $tempValue2;
            } else {
                $prevGain += $tempValue2;
            }
        }
        $prevLoss /= $optInTimePeriod;
        $prevGain /= $optInTimePeriod;
        if ($today > $startIdx) {
            $tempValue1 = $prevGain + $prevLoss;
            if (!(-0.00000001 < $tempValue1 && $tempValue1 < 0.00000001)) {
                $outReal[$outIdx++] = 100.0 * (($prevGain - $prevLoss) / $tempValue1);
            } else {
                $outReal[$outIdx++] = 0.0;
            }
        } else {
            while ($today < $startIdx) {
                $tempValue1 = $inReal[$today];
                $tempValue2 = $tempValue1 - $prevValue;
                $prevValue = $tempValue1;
                $prevLoss *= $optInTimePeriod - 1;
                $prevGain *= $optInTimePeriod - 1;
                if ($tempValue2 < 0) {
                    $prevLoss -= $tempValue2;
                } else {
                    $prevGain += $tempValue2;
                }
                $prevLoss /= $optInTimePeriod;
                $prevGain /= $optInTimePeriod;
                $today++;
            }
        }
        while ($today <= $endIdx) {
            $tempValue1 = $inReal[$today++];
            $tempValue2 = $tempValue1 - $prevValue;
            $prevValue = $tempValue1;
            $prevLoss *= $optInTimePeriod - 1;
            $prevGain *= $optInTimePeriod - 1;
            if ($tempValue2 < 0) {
                $prevLoss -= $tempValue2;
            } else {
                $prevGain += $tempValue2;
            }
            $prevLoss /= $optInTimePeriod;
            $prevGain /= $optInTimePeriod;
            $tempValue1 = $prevGain + $prevLoss;
            if (!(-0.00000001 < $tempValue1 && $tempValue1 < 0.00000001)) {
                $outReal[$outIdx++] = 100.0 * (($prevGain - $prevLoss) / $tempValue1);
            } else {
                $outReal[$outIdx++] = 0.0;
            }
        }
        $outBegIdx = $startIdx;
        $outNBElement = $outIdx;

        return ReturnCode::Success;
    }

    public static function dx(int $startIdx, int $endIdx, array $inHigh, array $inLow, array $inClose, int $optInTimePeriod, int &$outBegIdx, int &$outNBElement, array &$outReal): int
    {
        if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) {
            return $RetCode;
        }
        if ($optInTimePeriod === PHP_INT_MIN) {
            $optInTimePeriod = 14;
        } elseif ($optInTimePeriod < 2 || $optInTimePeriod > 100000) {
            return ReturnCode::BadParam;
        }
        $lookbackTotal = $optInTimePeriod + static::$unstablePeriod[UnstablePeriodFunctionID::DX];
        if ($startIdx < $lookbackTotal) {
            $startIdx = $lookbackTotal;
        }
        if ($startIdx > $endIdx) {
            $outBegIdx = 0;
            $outNBElement = 0;

            return ReturnCode::Success;
        }
        $outIdx = 0;
        $outBegIdx = $today = $startIdx;
        $prevMinusDM = 0.0;
        $prevPlusDM = 0.0;
        $prevTR = 0.0;
        $today = $startIdx - $lookbackTotal;
        $prevHigh = $inHigh[$today];
        $prevLow = $inLow[$today];
        $prevClose = $inClose[$today];
        $i = $optInTimePeriod - 1;
        while ($i-- > 0) {
            $today++;
            $tempReal = $inHigh[$today];
            $diffP = $tempReal - $prevHigh;
            $prevHigh = $tempReal;
            $tempReal = $inLow[$today];
            $diffM = $prevLow - $tempReal;
            $prevLow = $tempReal;
            if ($diffM > 0 && $diffP < $diffM) {
                $prevMinusDM += $diffM;
            } elseif ($diffP > 0 && $diffP > $diffM) {
                $prevPlusDM += $diffP;
            }
            {
                $tempReal = $prevHigh - $prevLow;
                $tempReal2 = abs($prevHigh - $prevClose);
                if ($tempReal2 > $tempReal) {
                    $tempReal = $tempReal2;
                }
                $tempReal2 = abs($prevLow - $prevClose);
                if ($tempReal2 > $tempReal) {
                    $tempReal = $tempReal2;
                }
            }
            $prevTR += $tempReal;
            $prevClose = $inClose[$today];
        }
        $i = static::$unstablePeriod[UnstablePeriodFunctionID::DX] + 1;
        while ($i-- > 0) {
            $today++;
            $tempReal = $inHigh[$today];
            $diffP = $tempReal - $prevHigh;
            $prevHigh = $tempReal;
            $tempReal = $inLow[$today];
            $diffM = $prevLow - $tempReal;
            $prevLow = $tempReal;
            $prevMinusDM -= $prevMinusDM / $optInTimePeriod;
            $prevPlusDM -= $prevPlusDM / $optInTimePeriod;
            if ($diffM > 0 && $diffP < $diffM) {
                $prevMinusDM += $diffM;
            } elseif ($diffP > 0 && $diffP > $diffM) {
                $prevPlusDM += $diffP;
            }
            {
                $tempReal = $prevHigh - $prevLow;
                $tempReal2 = abs($prevHigh - $prevClose);
                if ($tempReal2 > $tempReal) {
                    $tempReal = $tempReal2;
                }
                $tempReal2 = abs($prevLow - $prevClose);
                if ($tempReal2 > $tempReal) {
                    $tempReal = $tempReal2;
                }
            }
            $prevTR = $prevTR - $prevTR / $optInTimePeriod + $tempReal;
            $prevClose = $inClose[$today];
        }
        if (!(-0.00000001 < $prevTR && $prevTR < 0.00000001)) {
            $minusDI = 100.0 * ($prevMinusDM / $prevTR);
            $plusDI = 100.0 * ($prevPlusDM / $prevTR);
            $tempReal = $minusDI + $plusDI;
            if (!(-0.00000001 < $tempReal && $tempReal < 0.00000001)) {
                $outReal[0] = 100.0 * (abs($minusDI - $plusDI) / $tempReal);
            } else {
                $outReal[0] = 0.0;
            }
        } else {
            $outReal[0] = 0.0;
        }
        $outIdx = 1;
        while ($today < $endIdx) {
            $today++;
            $tempReal = $inHigh[$today];
            $diffP = $tempReal - $prevHigh;
            $prevHigh = $tempReal;
            $tempReal = $inLow[$today];
            $diffM = $prevLow - $tempReal;
            $prevLow = $tempReal;
            $prevMinusDM -= $prevMinusDM / $optInTimePeriod;
            $prevPlusDM -= $prevPlusDM / $optInTimePeriod;
            if ($diffM > 0 && $diffP < $diffM) {
                $prevMinusDM += $diffM;
            } elseif ($diffP > 0 && $diffP > $diffM) {
                $prevPlusDM += $diffP;
            }
            {
                $tempReal = $prevHigh - $prevLow;
                $tempReal2 = abs($prevHigh - $prevClose);
                if ($tempReal2 > $tempReal) {
                    $tempReal = $tempReal2;
                }
                $tempReal2 = abs($prevLow - $prevClose);
                if ($tempReal2 > $tempReal) {
                    $tempReal = $tempReal2;
                }
            }
            $prevTR = $prevTR - $prevTR / $optInTimePeriod + $tempReal;
            $prevClose = $inClose[$today];
            if (!(-0.00000001 < $prevTR && $prevTR < 0.00000001)) {
                $minusDI = 100.0 * ($prevMinusDM / $prevTR);
                $plusDI = 100.0 * ($prevPlusDM / $prevTR);
                $tempReal = $minusDI + $plusDI;
                if (!(-0.00000001 < $tempReal && $tempReal < 0.00000001)) {
                    $outReal[$outIdx] = 100.0 * (abs($minusDI - $plusDI) / $tempReal);
                } else {
                    $outReal[$outIdx] = $outReal[$outIdx - 1];
                }
            } else {
                $outReal[$outIdx] = $outReal[$outIdx - 1];
            }
            $outIdx++;
        }
        $outNBElement = $outIdx;

        return ReturnCode::Success;
    }

    public static function imi(int $startIdx, int $endIdx, array $inOpen, array $inClose, int $optInTimePeriod, int &$outBegIdx, int &$outNBElement, array &$outReal): int
    {
        if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) {
            return $RetCode;
        }

        $lookback = Lookback::imiLookback($optInTimePeriod);

        if ($startIdx < $lookback) {
            $startIdx = $lookback;
        }

        if ($startIdx > $endIdx) {
            $outBegIdx = 0;
            $outNBElement = 0;

            return ReturnCode::Success;
        }

        $outIdx = 0;
        $outBegIdx = $startIdx;

        while ($startIdx <= $endIdx) {
            $upsum = 0.0;
            $downsum = 0.0;

            for ($i = $startIdx - $lookback; $i <= $startIdx; $i++) {
                $close = $inClose[$i];
                $open = $inOpen[$i];

                if ($close > $open) {
                    $upsum += ($close - $open);
                } else {
                    $downsum += ($open - $close);
                }
            }

            $outReal[$outIdx] = 100.0 * ($upsum / ($upsum + $downsum));

            $startIdx++;
            $outIdx++;
        }

        $outNBElement = $outIdx;

        return ReturnCode::Success;
    }

    public static function macd(
        int $startIdx,
        int $endIdx,
        array $inReal,
        int $optInFastPeriod,
        int $optInSlowPeriod,
        int $optInSignalPeriod,
        int &$outBegIdx,
        int &$outNBElement,
        array &$outMACD,
        array &$outMACDSignal,
        array &$outMACDHist
    ): int {
        if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) {
            return $RetCode;
        }
        if ($optInFastPeriod === PHP_INT_MIN) {
            $optInFastPeriod = 12;
        } elseif ($optInFastPeriod < 2 || $optInFastPeriod > 100000) {
            return ReturnCode::BadParam;
        }
        if ($optInSlowPeriod === PHP_INT_MIN) {
            $optInSlowPeriod = 26;
        } elseif ($optInSlowPeriod < 2 || $optInSlowPeriod > 100000) {
            return ReturnCode::BadParam;
        }
        if ($optInSignalPeriod === PHP_INT_MIN) {
            $optInSignalPeriod = 9;
        } elseif ($optInSignalPeriod < 1 || $optInSignalPeriod > 100000) {
            return ReturnCode::BadParam;
        }

        return static::TA_INT_MACD(
            $startIdx,
            $endIdx,
            $inReal,
            $optInFastPeriod,
            $optInSlowPeriod,
            $optInSignalPeriod,
            $outBegIdx,
            $outNBElement,
            $outMACD,
            $outMACDSignal,
            $outMACDHist
        );
    }

    public static function macdExt(
        int $startIdx,
        int $endIdx,
        array $inReal,
        int $optInFastPeriod,
        int $optInFastMAType,
        int $optInSlowPeriod,
        int $optInSlowMAType,
        int $optInSignalPeriod,
        int $optInSignalMAType,
        int &$outBegIdx,
        int &$outNBElement,
        array &$outMACD,
        array &$outMACDSignal,
        array &$outMACDHist
    ): int {
        if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) {
            return $RetCode;
        }
        $outBegIdx1 = 0;
        $outNbElement1 = 0;
        $outBegIdx2 = 0;
        $outNbElement2 = 0;
        if ($optInFastPeriod === PHP_INT_MIN) {
            $optInFastPeriod = 12;
        } elseif ($optInFastPeriod < 2 || $optInFastPeriod > 100000) {
            return ReturnCode::BadParam;
        }
        if ($optInSlowPeriod === PHP_INT_MIN) {
            $optInSlowPeriod = 26;
        } elseif ($optInSlowPeriod < 2 || $optInSlowPeriod > 100000) {
            return ReturnCode::BadParam;
        }
        if ($optInSignalPeriod === PHP_INT_MIN) {
            $optInSignalPeriod = 9;
        } elseif ($optInSignalPeriod < 1 || $optInSignalPeriod > 100000) {
            return ReturnCode::BadParam;
        }
        if ($optInSlowPeriod < $optInFastPeriod) {
            $tempInteger = $optInSlowPeriod;
            $optInSlowPeriod = $optInFastPeriod;
            $optInFastPeriod = $tempInteger;
            $tempMAType = $optInSlowMAType;
            $optInSlowMAType = $optInFastMAType;
            $optInFastMAType = $tempMAType;
        }
        $lookbackLargest = Lookback::movingAverageLookback($optInFastPeriod, $optInFastMAType);
        $tempInteger = Lookback::movingAverageLookback($optInSlowPeriod, $optInSlowMAType);
        if ($tempInteger > $lookbackLargest) {
            $lookbackLargest = $tempInteger;
        }
        $lookbackSignal = Lookback::movingAverageLookback($optInSignalPeriod, $optInSignalMAType);
        $lookbackTotal = $lookbackSignal + $lookbackLargest;
        if ($startIdx < $lookbackTotal) {
            $startIdx = $lookbackTotal;
        }
        if ($startIdx > $endIdx) {
            $outBegIdx = 0;
            $outNBElement = 0;

            return ReturnCode::Success;
        }
        $tempInteger = $endIdx - $startIdx + 1 + $lookbackSignal;
        $fastMABuffer = static::double($tempInteger);
        $slowMABuffer = static::double($tempInteger);
        $tempInteger = $startIdx - $lookbackSignal;
        $ReturnCode = OverlapStudies::movingAverage(
            $tempInteger,
            $endIdx,
            $inReal,
            $optInSlowPeriod,
            $optInSlowMAType,
            $outBegIdx1,
            $outNbElement1,
            $slowMABuffer
        );
        if ($ReturnCode !== ReturnCode::Success) {
            $outBegIdx = 0;
            $outNBElement = 0;

            return $ReturnCode;
        }
        $ReturnCode = OverlapStudies::movingAverage(
            $tempInteger,
            $endIdx,
            $inReal,
            $optInFastPeriod,
            $optInFastMAType,
            $outBegIdx2,
            $outNbElement2,
            $fastMABuffer
        );
        if ($ReturnCode !== ReturnCode::Success) {
            $outBegIdx = 0;
            $outNBElement = 0;

            return $ReturnCode;
        }
        if ($outBegIdx1 !== $tempInteger ||
            $outBegIdx2 !== $tempInteger ||
            $outNbElement1 !== $outNbElement2 ||
            $outNbElement1 !== $endIdx - $startIdx + 1 + $lookbackSignal) {
            $outBegIdx = 0;
            $outNBElement = 0;

            return ReturnCode::InternalError;
        }
        for ($i = 0; $i < $outNbElement1; $i++) {
            $fastMABuffer[$i] -= $slowMABuffer[$i];
        }
        //System::arraycopy($fastMABuffer, $lookbackSignal, $outMACD, 0, ($endIdx - $startIdx) + 1);
        $outMACD = \array_slice($fastMABuffer, $lookbackSignal, $endIdx - $startIdx + 1);
        $ReturnCode = OverlapStudies::movingAverage(
            0,
            $outNbElement1 - 1,
            $fastMABuffer,
            $optInSignalPeriod,
            $optInSignalMAType,
            $outBegIdx2,
            $outNbElement2,
            $outMACDSignal
        );
        if ($ReturnCode !== ReturnCode::Success) {
            $outBegIdx = 0;
            $outNBElement = 0;

            return $ReturnCode;
        }
        for ($i = 0; $i < $outNbElement2; $i++) {
            $outMACDHist[$i] = $outMACD[$i] - $outMACDSignal[$i];
        }
        $outBegIdx = $startIdx;
        $outNBElement = $outNbElement2;

        return ReturnCode::Success;
    }

    public static function macdFix(
        int $startIdx,
        int $endIdx,
        array $inReal,
        int $optInSignalPeriod,
        int &$outBegIdx,
        int &$outNBElement,
        array &$outMACD,
        array &$outMACDSignal,
        array &$outMACDHist
    ): int {
        if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) {
            return $RetCode;
        }
        if ($optInSignalPeriod === PHP_INT_MIN) {
            $optInSignalPeriod = 9;
        } elseif ($optInSignalPeriod < 1 || $optInSignalPeriod > 100000) {
            return ReturnCode::BadParam;
        }

        return static::TA_INT_MACD(
            $startIdx,
            $endIdx,
            $inReal,
            0,
            0,
            $optInSignalPeriod,
            $outBegIdx,
            $outNBElement,
            $outMACD,
            $outMACDSignal,
            $outMACDHist
        );
    }

    public static function mfi(
        int $startIdx,
        int $endIdx,
        array $inHigh,
        array $inLow,
        array $inClose,
        array $inVolume,
        int $optInTimePeriod,
        int &$outBegIdx,
        int &$outNBElement,
        array &$outReal
    ): int {
        if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) {
            return $RetCode;
        }
        $money_flow_Idx = 0;
        $maxIdx_money_flow = 50 - 1;
        if ($optInTimePeriod === PHP_INT_MIN) {
            $optInTimePeriod = 14;
        } elseif ($optInTimePeriod < 2 || $optInTimePeriod > 100000) {
            return ReturnCode::BadParam;
        }
        {
            $money_flow = \array_pad([], $optInTimePeriod, new MoneyFlow());
            for ($_money_flow_index = 0; $_money_flow_index < $optInTimePeriod; $_money_flow_index++) {
                $money_flow[$_money_flow_index] = new MoneyFlow();
            }
            $maxIdx_money_flow = $optInTimePeriod - 1;
        }
        $outBegIdx = 0;
        $outNBElement = 0;
        $lookbackTotal = $optInTimePeriod + static::$unstablePeriod[UnstablePeriodFunctionID::MFI];
        if ($startIdx < $lookbackTotal) {
            $startIdx = $lookbackTotal;
        }
        if ($startIdx > $endIdx) {
            return ReturnCode::Success;
        }
        $outIdx = 0;
        $today = $startIdx - $lookbackTotal;
        $prevValue = ($inHigh[$today] + $inLow[$today] + $inClose[$today]) / 3.0;
        $posSumMF = 0.0;
        $negSumMF = 0.0;
        $today++;
        for ($i = $optInTimePeriod; $i > 0; $i--) {
            $tempValue1 = ($inHigh[$today] + $inLow[$today] + $inClose[$today]) / 3.0;
            $tempValue2 = $tempValue1 - $prevValue;
            $prevValue = $tempValue1;
            $tempValue1 *= $inVolume[$today++];
            if ($tempValue2 < 0) {
                $money_flow[$money_flow_Idx]->negative = $tempValue1;
                $negSumMF += $tempValue1;
                $money_flow[$money_flow_Idx]->positive = 0.0;
            } elseif ($tempValue2 > 0) {
                $money_flow[$money_flow_Idx]->positive = $tempValue1;
                $posSumMF += $tempValue1;
                $money_flow[$money_flow_Idx]->negative = 0.0;
            } else {
                $money_flow[$money_flow_Idx]->positive = 0.0;
                $money_flow[$money_flow_Idx]->negative = 0.0;
            }
            {
                $money_flow_Idx++;
                if ($money_flow_Idx > $maxIdx_money_flow) {
                    $money_flow_Idx = 0;
                }
            }
        }
        if ($today > $startIdx) {
            $tempValue1 = $posSumMF + $negSumMF;
            if ($tempValue1 < 1.0) {
                $outReal[$outIdx++] = 0.0;
            } else {
                $outReal[$outIdx++] = 100.0 * ($posSumMF / $tempValue1);
            }
        } else {
            while ($today < $startIdx) {
                $posSumMF -= $money_flow[$money_flow_Idx]->positive;
                $negSumMF -= $money_flow[$money_flow_Idx]->negative;
                $tempValue1 = ($inHigh[$today] + $inLow[$today] + $inClose[$today]) / 3.0;
                $tempValue2 = $tempValue1 - $prevValue;
                $prevValue = $tempValue1;
                $tempValue1 *= $inVolume[$today++];
                if ($tempValue2 < 0) {
                    $money_flow[$money_flow_Idx]->negative = $tempValue1;
                    $negSumMF += $tempValue1;
                    $money_flow[$money_flow_Idx]->positive = 0.0;
                } elseif ($tempValue2 > 0) {
                    $money_flow[$money_flow_Idx]->positive = $tempValue1;
                    $posSumMF += $tempValue1;
                    $money_flow[$money_flow_Idx]->negative = 0.0;
                } else {
                    $money_flow[$money_flow_Idx]->positive = 0.0;
                    $money_flow[$money_flow_Idx]->negative = 0.0;
                }
                {
                    $money_flow_Idx++;
                    if ($money_flow_Idx > $maxIdx_money_flow) {
                        $money_flow_Idx = 0;
                    }
                }
            }
        }
        while ($today <= $endIdx) {
            $posSumMF -= $money_flow[$money_flow_Idx]->positive;
            $negSumMF -= $money_flow[$money_flow_Idx]->negative;
            $tempValue1 = ($inHigh[$today] + $inLow[$today] + $inClose[$today]) / 3.0;
            $tempValue2 = $tempValue1 - $prevValue;
            $prevValue = $tempValue1;
            $tempValue1 *= $inVolume[$today++];
            if ($tempValue2 < 0) {
                $money_flow[$money_flow_Idx]->negative = $tempValue1;
                $negSumMF += $tempValue1;
                $money_flow[$money_flow_Idx]->positive = 0.0;
            } elseif ($tempValue2 > 0) {
                $money_flow[$money_flow_Idx]->positive = $tempValue1;
                $posSumMF += $tempValue1;
                $money_flow[$money_flow_Idx]->negative = 0.0;
            } else {
                $money_flow[$money_flow_Idx]->positive = 0.0;
                $money_flow[$money_flow_Idx]->negative = 0.0;
            }
            $tempValue1 = $posSumMF + $negSumMF;
            if ($tempValue1 < 1.0) {
                $outReal[$outIdx++] = 0.0;
            } else {
                $outReal[$outIdx++] = 100.0 * ($posSumMF / $tempValue1);
            }
            {
                $money_flow_Idx++;
                if ($money_flow_Idx > $maxIdx_money_flow) {
                    $money_flow_Idx = 0;
                }
            }
        }
        $outBegIdx = $startIdx;
        $outNBElement = $outIdx;

        return ReturnCode::Success;
    }

    public static function minusDI(int $startIdx, int $endIdx, array $inHigh, array $inLow, array $inClose, int $optInTimePeriod, int &$outBegIdx, int &$outNBElement, array &$outReal): int
    {
        if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) {
            return $RetCode;
        }
        if ($optInTimePeriod === PHP_INT_MIN) {
            $optInTimePeriod = 14;
        } elseif ($optInTimePeriod < 1 || $optInTimePeriod > 100000) {
            return ReturnCode::BadParam;
        }
        if ($optInTimePeriod > 1) {
            $lookbackTotal = $optInTimePeriod + static::$unstablePeriod[UnstablePeriodFunctionID::MinusDI];
        } else {
            $lookbackTotal = 1;
        }
        if ($startIdx < $lookbackTotal) {
            $startIdx = $lookbackTotal;
        }
        if ($startIdx > $endIdx) {
            $outBegIdx = 0;
            $outNBElement = 0;

            return ReturnCode::Success;
        }
        $outIdx = 0;
        if ($optInTimePeriod <= 1) {
            $outBegIdx = $startIdx;
            $today = $startIdx - 1;
            $prevHigh = $inHigh[$today];
            $prevLow = $inLow[$today];
            $prevClose = $inClose[$today];
            while ($today < $endIdx) {
                $today++;
                $tempReal = $inHigh[$today];
                $diffP = $tempReal - $prevHigh;
                $prevHigh = $tempReal;
                $tempReal = $inLow[$today];
                $diffM = $prevLow - $tempReal;
                $prevLow = $tempReal;
                if ($diffM > 0 && $diffP < $diffM) {
                    {
                        $tempReal = $prevHigh - $prevLow;
                        $tempReal2 = abs($prevHigh - $prevClose);
                        if ($tempReal2 > $tempReal) {
                            $tempReal = $tempReal2;
                        }
                        $tempReal2 = abs($prevLow - $prevClose);
                        if ($tempReal2 > $tempReal) {
                            $tempReal = $tempReal2;
                        }
                    }
                    if (-0.00000001 < $tempReal && $tempReal < 0.00000001) {
                        $outReal[$outIdx++] = 0.0;
                    } else {
                        $outReal[$outIdx++] = $diffM / $tempReal;
                    }
                } else {
                    $outReal[$outIdx++] = 0.0;
                }
                $prevClose = $inClose[$today];
            }
            $outNBElement = $outIdx;

            return ReturnCode::Success;
        }
        $outBegIdx = $today = $startIdx;
        $prevMinusDM = 0.0;
        $prevTR = 0.0;
        $today = $startIdx - $lookbackTotal;
        $prevHigh = $inHigh[$today];
        $prevLow = $inLow[$today];
        $prevClose = $inClose[$today];
        $i = $optInTimePeriod - 1;
        while ($i-- > 0) {
            $today++;
            $tempReal = $inHigh[$today];
            $diffP = $tempReal - $prevHigh;
            $prevHigh = $tempReal;
            $tempReal = $inLow[$today];
            $diffM = $prevLow - $tempReal;
            $prevLow = $tempReal;
            if ($diffM > 0 && $diffP < $diffM) {
                $prevMinusDM += $diffM;
            }
            {
                $tempReal = $prevHigh - $prevLow;
                $tempReal2 = abs($prevHigh - $prevClose);
                if ($tempReal2 > $tempReal) {
                    $tempReal = $tempReal2;
                }
                $tempReal2 = abs($prevLow - $prevClose);
                if ($tempReal2 > $tempReal) {
                    $tempReal = $tempReal2;
                }
            }
            $prevTR += $tempReal;
            $prevClose = $inClose[$today];
        }
        $i = static::$unstablePeriod[UnstablePeriodFunctionID::MinusDI] + 1;
        while ($i-- !== 0) {
            $today++;
            $tempReal = $inHigh[$today];
            $diffP = $tempReal - $prevHigh;
            $prevHigh = $tempReal;
            $tempReal = $inLow[$today];
            $diffM = $prevLow - $tempReal;
            $prevLow = $tempReal;
            if ($diffM > 0 && $diffP < $diffM) {
                $prevMinusDM = $prevMinusDM - $prevMinusDM / $optInTimePeriod + $diffM;
            } else {
                $prevMinusDM -= $prevMinusDM / $optInTimePeriod;
            }
            {
                $tempReal = $prevHigh - $prevLow;
                $tempReal2 = abs($prevHigh - $prevClose);
                if ($tempReal2 > $tempReal) {
                    $tempReal = $tempReal2;
                }
                $tempReal2 = abs($prevLow - $prevClose);
                if ($tempReal2 > $tempReal) {
                    $tempReal = $tempReal2;
                }
            }
            $prevTR = $prevTR - $prevTR / $optInTimePeriod + $tempReal;
            $prevClose = $inClose[$today];
        }
        if (!(-0.00000001 < $prevTR && $prevTR < 0.00000001)) {
            $outReal[0] = 100.0 * ($prevMinusDM / $prevTR);
        } else {
            $outReal[0] = 0.0;
        }
        $outIdx = 1;
        while ($today < $endIdx) {
            $today++;
            $tempReal = $inHigh[$today];
            $diffP = $tempReal - $prevHigh;
            $prevHigh = $tempReal;
            $tempReal = $inLow[$today];
            $diffM = $prevLow - $tempReal;
            $prevLow = $tempReal;
            if ($diffM > 0 && $diffP < $diffM) {
                $prevMinusDM = $prevMinusDM - $prevMinusDM / $optInTimePeriod + $diffM;
            } else {
                $prevMinusDM -= $prevMinusDM / $optInTimePeriod;
            }
            {
                $tempReal = $prevHigh - $prevLow;
                $tempReal2 = abs($prevHigh - $prevClose);
                if ($tempReal2 > $tempReal) {
                    $tempReal = $tempReal2;
                }
                $tempReal2 = abs($prevLow - $prevClose);
                if ($tempReal2 > $tempReal) {
                    $tempReal = $tempReal2;
                }
            }
            $prevTR = $prevTR - $prevTR / $optInTimePeriod + $tempReal;
            $prevClose = $inClose[$today];
            if (!(-0.00000001 < $prevTR && $prevTR < 0.00000001)) {
                $outReal[$outIdx++] = 100.0 * ($prevMinusDM / $prevTR);
            } else {
                $outReal[$outIdx++] = 0.0;
            }
        }
        $outNBElement = $outIdx;

        return ReturnCode::Success;
    }

    public static function minusDM(int $startIdx, int $endIdx, array $inHigh, array $inLow, int $optInTimePeriod, int &$outBegIdx, int &$outNBElement, array &$outReal): int
    {
        if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) {
            return $RetCode;
        }
        if ($optInTimePeriod === PHP_INT_MIN) {
            $optInTimePeriod = 14;
        } elseif ($optInTimePeriod < 1 || $optInTimePeriod > 100000) {
            return ReturnCode::BadParam;
        }
        if ($optInTimePeriod > 1) {
            $lookbackTotal = $optInTimePeriod + static::$unstablePeriod[UnstablePeriodFunctionID::MinusDM] - 1;
        } else {
            $lookbackTotal = 1;
        }
        if ($startIdx < $lookbackTotal) {
            $startIdx = $lookbackTotal;
        }
        if ($startIdx > $endIdx) {
            $outBegIdx = 0;
            $outNBElement = 0;

            return ReturnCode::Success;
        }
        $outIdx = 0;
        $outBegIdx = $startIdx;
        if ($optInTimePeriod <= 1) {
            $today = $startIdx - 1;
            $prevHigh = $inHigh[$today];
            $prevLow = $inLow[$today];
            while ($today < $endIdx) {
                $today++;
                $tempReal = $inHigh[$today];
                $diffP = $tempReal - $prevHigh;
                $prevHigh = $tempReal;
                $tempReal = $inLow[$today];
                $diffM = $prevLow - $tempReal;
                $prevLow = $tempReal;
                if ($diffM > 0 && $diffP < $diffM) {
                    $outReal[$outIdx++] = $diffM;
                } else {
                    $outReal[$outIdx++] = 0;
                }
            }
            $outNBElement = $outIdx;

            return ReturnCode::Success;
        }
        $prevMinusDM = 0.0;
        $today = $startIdx - $lookbackTotal;
        $prevHigh = $inHigh[$today];
        $prevLow = $inLow[$today];
        $i = $optInTimePeriod - 1;
        while ($i-- > 0) {
            $today++;
            $tempReal = $inHigh[$today];
            $diffP = $tempReal - $prevHigh;
            $prevHigh = $tempReal;
            $tempReal = $inLow[$today];
            $diffM = $prevLow - $tempReal;
            $prevLow = $tempReal;
            if ($diffM > 0 && $diffP < $diffM) {
                $prevMinusDM += $diffM;
            }
        }
        $i = static::$unstablePeriod[UnstablePeriodFunctionID::MinusDM];
        while ($i-- > 0) {
            $today++;
            $tempReal = $inHigh[$today];
            $diffP = $tempReal - $prevHigh;
            $prevHigh = $tempReal;
            $tempReal = $inLow[$today];
            $diffM = $prevLow - $tempReal;
            $prevLow = $tempReal;
            if ($diffM > 0 && $diffP < $diffM) {
                $prevMinusDM = $prevMinusDM - $prevMinusDM / $optInTimePeriod + $diffM;
            } else {
                $prevMinusDM -= $prevMinusDM / $optInTimePeriod;
            }
        }
        $outReal[0] = $prevMinusDM;
        $outIdx = 1;
        while ($today < $endIdx) {
            $today++;
            $tempReal = $inHigh[$today];
            $diffP = $tempReal - $prevHigh;
            $prevHigh = $tempReal;
            $tempReal = $inLow[$today];
            $diffM = $prevLow - $tempReal;
            $prevLow = $tempReal;
            if ($diffM > 0 && $diffP < $diffM) {
                $prevMinusDM = $prevMinusDM - $prevMinusDM / $optInTimePeriod + $diffM;
            } else {
                $prevMinusDM -= $prevMinusDM / $optInTimePeriod;
            }
            $outReal[$outIdx++] = $prevMinusDM;
        }
        $outNBElement = $outIdx;

        return ReturnCode::Success;
    }

    public static function mom(int $startIdx, int $endIdx, array $inReal, int $optInTimePeriod, int &$outBegIdx, int &$outNBElement, array &$outReal): int
    {
        if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) {
            return $RetCode;
        }
        if ($optInTimePeriod === PHP_INT_MIN) {
            $optInTimePeriod = 10;
        } elseif ($optInTimePeriod < 1 || $optInTimePeriod > 100000) {
            return ReturnCode::BadParam;
        }
        if ($startIdx < $optInTimePeriod) {
            $startIdx = $optInTimePeriod;
        }
        if ($startIdx > $endIdx) {
            $outBegIdx = 0;
            $outNBElement = 0;

            return ReturnCode::Success;
        }
        $outIdx = 0;
        $inIdx = $startIdx;
        $trailingIdx = $startIdx - $optInTimePeriod;
        while ($inIdx <= $endIdx) {
            $outReal[$outIdx++] = $inReal[$inIdx++] - $inReal[$trailingIdx++];
        }
        $outNBElement = $outIdx;
        $outBegIdx = $startIdx;

        return ReturnCode::Success;
    }

    public static function plusDI(int $startIdx, int $endIdx, array $inHigh, array $inLow, array $inClose, int $optInTimePeriod, int &$outBegIdx, int &$outNBElement, array &$outReal): int
    {
        if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) {
            return $RetCode;
        }
        if ($optInTimePeriod === PHP_INT_MIN) {
            $optInTimePeriod = 14;
        } elseif ($optInTimePeriod < 1 || $optInTimePeriod > 100000) {
            return ReturnCode::BadParam;
        }
        if ($optInTimePeriod > 1) {
            $lookbackTotal = $optInTimePeriod + static::$unstablePeriod[UnstablePeriodFunctionID::PlusDI];
        } else {
            $lookbackTotal = 1;
        }
        if ($startIdx < $lookbackTotal) {
            $startIdx = $lookbackTotal;
        }
        if ($startIdx > $endIdx) {
            $outBegIdx = 0;
            $outNBElement = 0;

            return ReturnCode::Success;
        }
        $outIdx = 0;
        if ($optInTimePeriod <= 1) {
            $outBegIdx = $startIdx;
            $today = $startIdx - 1;
            $prevHigh = $inHigh[$today];
            $prevLow = $inLow[$today];
            $prevClose = $inClose[$today];
            while ($today < $endIdx) {
                $today++;
                $tempReal = $inHigh[$today];
                $diffP = $tempReal - $prevHigh;
                $prevHigh = $tempReal;
                $tempReal = $inLow[$today];
                $diffM = $prevLow - $tempReal;
                $prevLow = $tempReal;
                if ($diffP > 0 && $diffP > $diffM) {
                    {
                        $tempReal = $prevHigh - $prevLow;
                        $tempReal2 = abs($prevHigh - $prevClose);
                        if ($tempReal2 > $tempReal) {
                            $tempReal = $tempReal2;
                        }
                        $tempReal2 = abs($prevLow - $prevClose);
                        if ($tempReal2 > $tempReal) {
                            $tempReal = $tempReal2;
                        }
                    }
                    if (-0.00000001 < $tempReal && $tempReal < 0.00000001) {
                        $outReal[$outIdx++] = 0.0;
                    } else {
                        $outReal[$outIdx++] = $diffP / $tempReal;
                    }
                } else {
                    $outReal[$outIdx++] = 0.0;
                }
                $prevClose = $inClose[$today];
            }
            $outNBElement = $outIdx;

            return ReturnCode::Success;
        }
        $outBegIdx = $today = $startIdx;
        $prevPlusDM = 0.0;
        $prevTR = 0.0;
        $today = $startIdx - $lookbackTotal;
        $prevHigh = $inHigh[$today];
        $prevLow = $inLow[$today];
        $prevClose = $inClose[$today];
        $i = $optInTimePeriod - 1;
        while ($i-- > 0) {
            $today++;
            $tempReal = $inHigh[$today];
            $diffP = $tempReal - $prevHigh;
            $prevHigh = $tempReal;
            $tempReal = $inLow[$today];
            $diffM = $prevLow - $tempReal;
            $prevLow = $tempReal;
            if ($diffP > 0 && $diffP > $diffM) {
                $prevPlusDM += $diffP;
            }
            {
                $tempReal = $prevHigh - $prevLow;
                $tempReal2 = abs($prevHigh - $prevClose);
                if ($tempReal2 > $tempReal) {
                    $tempReal = $tempReal2;
                }
                $tempReal2 = abs($prevLow - $prevClose);
                if ($tempReal2 > $tempReal) {
                    $tempReal = $tempReal2;
                }
            }
            $prevTR += $tempReal;
            $prevClose = $inClose[$today];
        }
        $i = static::$unstablePeriod[UnstablePeriodFunctionID::PlusDI] + 1;
        while ($i-- > 0) {
            $today++;
            $tempReal = $inHigh[$today];
            $diffP = $tempReal - $prevHigh;
            $prevHigh = $tempReal;
            $tempReal = $inLow[$today];
            $diffM = $prevLow - $tempReal;
            $prevLow = $tempReal;
            if ($diffP > 0 && $diffP > $diffM) {
                $prevPlusDM = $prevPlusDM - $prevPlusDM / $optInTimePeriod + $diffP;
            } else {
                $prevPlusDM -= $prevPlusDM / $optInTimePeriod;
            }
            {
                $tempReal = $prevHigh - $prevLow;
                $tempReal2 = abs($prevHigh - $prevClose);
                if ($tempReal2 > $tempReal) {
                    $tempReal = $tempReal2;
                }
                $tempReal2 = abs($prevLow - $prevClose);
                if ($tempReal2 > $tempReal) {
                    $tempReal = $tempReal2;
                }
            }
            $prevTR = $prevTR - $prevTR / $optInTimePeriod + $tempReal;
            $prevClose = $inClose[$today];
        }
        if (!(-0.00000001 < $prevTR && $prevTR < 0.00000001)) {
            $outReal[0] = 100.0 * ($prevPlusDM / $prevTR);
        } else {
            $outReal[0] = 0.0;
        }
        $outIdx = 1;
        while ($today < $endIdx) {
            $today++;
            $tempReal = $inHigh[$today];
            $diffP = $tempReal - $prevHigh;
            $prevHigh = $tempReal;
            $tempReal = $inLow[$today];
            $diffM = $prevLow - $tempReal;
            $prevLow = $tempReal;
            if ($diffP > 0 && $diffP > $diffM) {
                $prevPlusDM = $prevPlusDM - $prevPlusDM / $optInTimePeriod + $diffP;
            } else {
                $prevPlusDM -= $prevPlusDM / $optInTimePeriod;
            }
            {
                $tempReal = $prevHigh - $prevLow;
                $tempReal2 = abs($prevHigh - $prevClose);
                if ($tempReal2 > $tempReal) {
                    $tempReal = $tempReal2;
                }
                $tempReal2 = abs($prevLow - $prevClose);
                if ($tempReal2 > $tempReal) {
                    $tempReal = $tempReal2;
                }
            }
            $prevTR = $prevTR - $prevTR / $optInTimePeriod + $tempReal;
            $prevClose = $inClose[$today];
            if (!(-0.00000001 < $prevTR && $prevTR < 0.00000001)) {
                $outReal[$outIdx++] = 100.0 * ($prevPlusDM / $prevTR);
            } else {
                $outReal[$outIdx++] = 0.0;
            }
        }
        $outNBElement = $outIdx;

        return ReturnCode::Success;
    }

    public static function plusDM(int $startIdx, int $endIdx, array $inHigh, array $inLow, int $optInTimePeriod, int &$outBegIdx, int &$outNBElement, array &$outReal): int
    {
        if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) {
            return $RetCode;
        }
        if ($optInTimePeriod === PHP_INT_MIN) {
            $optInTimePeriod = 14;
        } elseif ($optInTimePeriod < 1 || $optInTimePeriod > 100000) {
            return ReturnCode::BadParam;
        }
        if ($optInTimePeriod > 1) {
            $lookbackTotal = $optInTimePeriod + static::$unstablePeriod[UnstablePeriodFunctionID::PlusDM] - 1;
        } else {
            $lookbackTotal = 1;
        }
        if ($startIdx < $lookbackTotal) {
            $startIdx = $lookbackTotal;
        }
        if ($startIdx > $endIdx) {
            $outBegIdx = 0;
            $outNBElement = 0;

            return ReturnCode::Success;
        }
        $outIdx = 0;
        $outBegIdx = $startIdx;
        if ($optInTimePeriod <= 1) {
            $today = $startIdx - 1;
            $prevHigh = $inHigh[$today];
            $prevLow = $inLow[$today];
            while ($today < $endIdx) {
                $today++;
                $tempReal = $inHigh[$today];
                $diffP = $tempReal - $prevHigh;
                $prevHigh = $tempReal;
                $tempReal = $inLow[$today];
                $diffM = $prevLow - $tempReal;
                $prevLow = $tempReal;
                if ($diffP > 0 && $diffP > $diffM) {
                    $outReal[$outIdx++] = $diffP;
                } else {
                    $outReal[$outIdx++] = 0;
                }
            }
            $outNBElement = $outIdx;

            return ReturnCode::Success;
        }
        $prevPlusDM = 0.0;
        $today = $startIdx - $lookbackTotal;
        $prevHigh = $inHigh[$today];
        $prevLow = $inLow[$today];
        $i = $optInTimePeriod - 1;
        while ($i-- > 0) {
            $today++;
            $tempReal = $inHigh[$today];
            $diffP = $tempReal - $prevHigh;
            $prevHigh = $tempReal;
            $tempReal = $inLow[$today];
            $diffM = $prevLow - $tempReal;
            $prevLow = $tempReal;
            if ($diffP > 0 && $diffP > $diffM) {
                $prevPlusDM += $diffP;
            }
        }
        $i = static::$unstablePeriod[UnstablePeriodFunctionID::PlusDM];
        while ($i-- > 0) {
            $today++;
            $tempReal = $inHigh[$today];
            $diffP = $tempReal - $prevHigh;
            $prevHigh = $tempReal;
            $tempReal = $inLow[$today];
            $diffM = $prevLow - $tempReal;
            $prevLow = $tempReal;
            if ($diffP > 0 && $diffP > $diffM) {
                $prevPlusDM = $prevPlusDM - $prevPlusDM / $optInTimePeriod + $diffP;
            } else {
                $prevPlusDM -= $prevPlusDM / $optInTimePeriod;
            }
        }
        $outReal[0] = $prevPlusDM;
        $outIdx = 1;
        while ($today < $endIdx) {
            $today++;
            $tempReal = $inHigh[$today];
            $diffP = $tempReal - $prevHigh;
            $prevHigh = $tempReal;
            $tempReal = $inLow[$today];
            $diffM = $prevLow - $tempReal;
            $prevLow = $tempReal;
            if ($diffP > 0 && $diffP > $diffM) {
                $prevPlusDM = $prevPlusDM - $prevPlusDM / $optInTimePeriod + $diffP;
            } else {
                $prevPlusDM -= $prevPlusDM / $optInTimePeriod;
            }
            $outReal[$outIdx++] = $prevPlusDM;
        }
        $outNBElement = $outIdx;

        return ReturnCode::Success;
    }

    public static function ppo(int $startIdx, int $endIdx, array $inReal, int $optInFastPeriod, int $optInSlowPeriod, int $optInMAType, int &$outBegIdx, int &$outNBElement, array &$outReal): int
    {
        if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) {
            return $RetCode;
        }
        if ($optInFastPeriod === PHP_INT_MIN) {
            $optInFastPeriod = 12;
        } elseif ($optInFastPeriod < 2 || $optInFastPeriod > 100000) {
            return ReturnCode::BadParam;
        }
        if ($optInSlowPeriod === PHP_INT_MIN) {
            $optInSlowPeriod = 26;
        } elseif ($optInSlowPeriod < 2 || $optInSlowPeriod > 100000) {
            return ReturnCode::BadParam;
        }
        $tempBuffer = static::double($endIdx - $startIdx + 1);
        $one = 1;

        return static::TA_INT_PO(
            $startIdx,
            $endIdx,
            $inReal,
            $optInFastPeriod,
            $optInSlowPeriod,
            $optInMAType,
            $outBegIdx,
            $outNBElement,
            $outReal,
            $tempBuffer,
            $one
        );
    }

    public static function roc(int $startIdx, int $endIdx, array $inReal, int $optInTimePeriod, int &$outBegIdx, int &$outNBElement, array &$outReal): int
    {
        if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) {
            return $RetCode;
        }
        if ($optInTimePeriod === PHP_INT_MIN) {
            $optInTimePeriod = 10;
        } elseif ($optInTimePeriod < 1 || $optInTimePeriod > 100000) {
            return ReturnCode::BadParam;
        }
        if ($startIdx < $optInTimePeriod) {
            $startIdx = $optInTimePeriod;
        }
        if ($startIdx > $endIdx) {
            $outBegIdx = 0;
            $outNBElement = 0;

            return ReturnCode::Success;
        }
        $outIdx = 0;
        $inIdx = $startIdx;
        $trailingIdx = $startIdx - $optInTimePeriod;
        while ($inIdx <= $endIdx) {
            $tempReal = $inReal[$trailingIdx++];
            if ($tempReal !== 0.0) {
                $outReal[$outIdx++] = ($inReal[$inIdx] / $tempReal - 1.0) * 100.0;
            } else {
                $outReal[$outIdx++] = 0.0;
            }
            $inIdx++;
        }
        $outNBElement = $outIdx;
        $outBegIdx = $startIdx;

        return ReturnCode::Success;
    }

    public static function rocP(int $startIdx, int $endIdx, array $inReal, int $optInTimePeriod, int &$outBegIdx, int &$outNBElement, array &$outReal): int
    {
        if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) {
            return $RetCode;
        }
        if ($optInTimePeriod === PHP_INT_MIN) {
            $optInTimePeriod = 10;
        } elseif ($optInTimePeriod < 1 || $optInTimePeriod > 100000) {
            return ReturnCode::BadParam;
        }
        if ($startIdx < $optInTimePeriod) {
            $startIdx = $optInTimePeriod;
        }
        if ($startIdx > $endIdx) {
            $outBegIdx = 0;
            $outNBElement = 0;

            return ReturnCode::Success;
        }
        $outIdx = 0;
        $inIdx = $startIdx;
        $trailingIdx = $startIdx - $optInTimePeriod;
        while ($inIdx <= $endIdx) {
            $tempReal = $inReal[$trailingIdx++];
            if ($tempReal !== 0.0) {
                $outReal[$outIdx++] = ($inReal[$inIdx] - $tempReal) / $tempReal;
            } else {
                $outReal[$outIdx++] = 0.0;
            }
            $inIdx++;
        }
        $outNBElement = $outIdx;
        $outBegIdx = $startIdx;

        return ReturnCode::Success;
    }

    public static function rocR(int $startIdx, int $endIdx, array $inReal, int $optInTimePeriod, int &$outBegIdx, int &$outNBElement, array &$outReal): int
    {
        if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) {
            return $RetCode;
        }
        if ($optInTimePeriod === PHP_INT_MIN) {
            $optInTimePeriod = 10;
        } elseif ($optInTimePeriod < 1 || $optInTimePeriod > 100000) {
            return ReturnCode::BadParam;
        }
        if ($startIdx < $optInTimePeriod) {
            $startIdx = $optInTimePeriod;
        }
        if ($startIdx > $endIdx) {
            $outBegIdx = 0;
            $outNBElement = 0;

            return ReturnCode::Success;
        }
        $outIdx = 0;
        $inIdx = $startIdx;
        $trailingIdx = $startIdx - $optInTimePeriod;
        while ($inIdx <= $endIdx) {
            $tempReal = $inReal[$trailingIdx++];
            if ($tempReal !== 0.0) {
                $outReal[$outIdx++] = $inReal[$inIdx] / $tempReal;
            } else {
                $outReal[$outIdx++] = 0.0;
            }
            $inIdx++;
        }
        $outNBElement = $outIdx;
        $outBegIdx = $startIdx;

        return ReturnCode::Success;
    }

    public static function rocR100(int $startIdx, int $endIdx, array $inReal, int $optInTimePeriod, int &$outBegIdx, int &$outNBElement, array &$outReal): int
    {
        if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) {
            return $RetCode;
        }
        if ($optInTimePeriod === PHP_INT_MIN) {
            $optInTimePeriod = 10;
        } elseif ($optInTimePeriod < 1 || $optInTimePeriod > 100000) {
            return ReturnCode::BadParam;
        }
        if ($startIdx < $optInTimePeriod) {
            $startIdx = $optInTimePeriod;
        }
        if ($startIdx > $endIdx) {
            $outBegIdx = 0;
            $outNBElement = 0;

            return ReturnCode::Success;
        }
        $outIdx = 0;
        $inIdx = $startIdx;
        $trailingIdx = $startIdx - $optInTimePeriod;
        while ($inIdx <= $endIdx) {
            $tempReal = $inReal[$trailingIdx++];
            if ($tempReal !== 0.0) {
                $outReal[$outIdx++] = $inReal[$inIdx] / $tempReal * 100.0;
            } else {
                $outReal[$outIdx++] = 0.0;
            }
            $inIdx++;
        }
        $outNBElement = $outIdx;
        $outBegIdx = $startIdx;

        return ReturnCode::Success;
    }

    public static function rsi(int $startIdx, int $endIdx, array $inReal, int $optInTimePeriod, int &$outBegIdx, int &$outNBElement, array &$outReal): int
    {
        if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) {
            return $RetCode;
        }
        if ($optInTimePeriod === PHP_INT_MIN) {
            $optInTimePeriod = 14;
        } elseif ($optInTimePeriod < 2 || $optInTimePeriod > 100000) {
            return ReturnCode::BadParam;
        }
        $outBegIdx = 0;
        $outNBElement = 0;
        $lookbackTotal = Lookback::rsiLookback($optInTimePeriod);
        if ($startIdx < $lookbackTotal) {
            $startIdx = $lookbackTotal;
        }
        if ($startIdx > $endIdx) {
            return ReturnCode::Success;
        }
        $outIdx = 0;
        $today = $startIdx - $lookbackTotal;
        $prevValue = $inReal[$today];
        $unstablePeriod = static::$unstablePeriod[UnstablePeriodFunctionID::RSI];
        if ($unstablePeriod === 0 &&
            static::$compatibility === Compatibility::Metastock) {
            $savePrevValue = $prevValue;
            $prevGain = 0.0;
            $prevLoss = 0.0;
            for ($i = $optInTimePeriod; $i > 0; $i--) {
                $tempValue1 = $inReal[$today++];
                $tempValue2 = $tempValue1 - $prevValue;
                $prevValue = $tempValue1;
                if ($tempValue2 < 0) {
                    $prevLoss -= $tempValue2;
                } else {
                    $prevGain += $tempValue2;
                }
            }
            $tempValue1 = $prevLoss / $optInTimePeriod;
            $tempValue2 = $prevGain / $optInTimePeriod;
            $tempValue1 = $tempValue2 + $tempValue1;
            if (!(-0.00000001 < $tempValue1 && $tempValue1 < 0.00000001)) {
                $outReal[$outIdx++] = 100 * ($tempValue2 / $tempValue1);
            } else {
                $outReal[$outIdx++] = 0.0;
            }
            if ($today > $endIdx) {
                $outBegIdx = $startIdx;
                $outNBElement = $outIdx;

                return ReturnCode::Success;
            }
            $today -= $optInTimePeriod;
            $prevValue = $savePrevValue;
        }
        $prevLoss = 0.0;
        $prevGain = 0.0;
        $today++;
        for ($i = $optInTimePeriod; $i > 0; $i--) {
            $tempValue1 = $inReal[$today++];
            $tempValue2 = $tempValue1 - $prevValue;
            $prevValue = $tempValue1;
            if ($tempValue2 < 0) {
                $prevLoss -= $tempValue2;
            } else {
                $prevGain += $tempValue2;
            }
        }
        $prevLoss /= $optInTimePeriod;
        $prevGain /= $optInTimePeriod;
        if ($today > $startIdx) {
            $tempValue1 = $prevGain + $prevLoss;
            if (!(-0.00000001 < $tempValue1 && $tempValue1 < 0.00000001)) {
                $outReal[$outIdx++] = 100.0 * ($prevGain / $tempValue1);
            } else {
                $outReal[$outIdx++] = 0.0;
            }
        } else {
            while ($today < $startIdx) {
                $tempValue1 = $inReal[$today];
                $tempValue2 = $tempValue1 - $prevValue;
                $prevValue = $tempValue1;
                $prevLoss *= $optInTimePeriod - 1;
                $prevGain *= $optInTimePeriod - 1;
                if ($tempValue2 < 0) {
                    $prevLoss -= $tempValue2;
                } else {
                    $prevGain += $tempValue2;
                }
                $prevLoss /= $optInTimePeriod;
                $prevGain /= $optInTimePeriod;
                $today++;
            }
        }
        while ($today <= $endIdx) {
            $tempValue1 = $inReal[$today++];
            $tempValue2 = $tempValue1 - $prevValue;
            $prevValue = $tempValue1;
            $prevLoss *= $optInTimePeriod - 1;
            $prevGain *= $optInTimePeriod - 1;
            if ($tempValue2 < 0) {
                $prevLoss -= $tempValue2;
            } else {
                $prevGain += $tempValue2;
            }
            $prevLoss /= $optInTimePeriod;
            $prevGain /= $optInTimePeriod;
            $tempValue1 = $prevGain + $prevLoss;
            if (!(-0.00000001 < $tempValue1 && $tempValue1 < 0.00000001)) {
                $outReal[$outIdx++] = 100.0 * ($prevGain / $tempValue1);
            } else {
                $outReal[$outIdx++] = 0.0;
            }
        }
        $outBegIdx = $startIdx;
        $outNBElement = $outIdx;

        return ReturnCode::Success;
    }

    public static function stoch(
        int $startIdx,
        int $endIdx,
        array $inHigh,
        array $inLow,
        array $inClose,
        int $optInFastK_Period,
        int $optInSlowK_Period,
        int $optInSlowK_MAType,
        int $optInSlowD_Period,
        int $optInSlowD_MAType,
        int &$outBegIdx,
        int &$outNBElement,
        array &$outSlowK,
        array &$outSlowD
    ): int {
        if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) {
            return $RetCode;
        }
        if ($optInFastK_Period === PHP_INT_MIN) {
            $optInFastK_Period = 5;
        } elseif ($optInFastK_Period < 1 || $optInFastK_Period > 100000) {
            return ReturnCode::BadParam;
        }
        if ($optInSlowK_Period === PHP_INT_MIN) {
            $optInSlowK_Period = 3;
        } elseif ($optInSlowK_Period < 1 || $optInSlowK_Period > 100000) {
            return ReturnCode::BadParam;
        }
        if ($optInSlowD_Period === PHP_INT_MIN) {
            $optInSlowD_Period = 3;
        } elseif ($optInSlowD_Period < 1 || $optInSlowD_Period > 100000) {
            return ReturnCode::BadParam;
        }
        $lookbackK = $optInFastK_Period - 1;
        $lookbackKSlow = Lookback::movingAverageLookback($optInSlowK_Period, $optInSlowK_MAType);
        $lookbackDSlow = Lookback::movingAverageLookback($optInSlowD_Period, $optInSlowD_MAType);
        $lookbackTotal = $lookbackK + $lookbackDSlow + $lookbackKSlow;
        if ($startIdx < $lookbackTotal) {
            $startIdx = $lookbackTotal;
        }
        if ($startIdx > $endIdx) {
            $outBegIdx = 0;
            $outNBElement = 0;

            return ReturnCode::Success;
        }
        $outIdx = 0;
        $trailingIdx = $startIdx - $lookbackTotal;
        $today = $trailingIdx + $lookbackK;
        $lowestIdx = $highestIdx = -1;
        $diff = $highest = $lowest = 0.0;
        if ($outSlowK === $inHigh ||
            $outSlowK === $inLow ||
            $outSlowK === $inClose) {
            $tempBuffer = $outSlowK;
        } elseif ($outSlowD === $inHigh ||
                  $outSlowD === $inLow ||
                  $outSlowD === $inClose) {
            $tempBuffer = $outSlowD;
        } else {
            $tempBuffer = static::double($endIdx - $today + 1);
        }
        while ($today <= $endIdx) {
            $tmp = $inLow[$today];
            if ($lowestIdx < $trailingIdx) {
                $lowestIdx = $trailingIdx;
                $lowest = $inLow[$lowestIdx];
                $i = $lowestIdx;
                while (++$i <= $today) {
                    $tmp = $inLow[$i];
                    if ($tmp < $lowest) {
                        $lowestIdx = $i;
                        $lowest = $tmp;
                    }
                }
                $diff = ($highest - $lowest) / 100.0;
            } elseif ($tmp <= $lowest) {
                $lowestIdx = $today;
                $lowest = $tmp;
                $diff = ($highest - $lowest) / 100.0;
            }
            $tmp = $inHigh[$today];
            if ($highestIdx < $trailingIdx) {
                $highestIdx = $trailingIdx;
                $highest = $inHigh[$highestIdx];
                $i = $highestIdx;
                while (++$i <= $today) {
                    $tmp = $inHigh[$i];
                    if ($tmp > $highest) {
                        $highestIdx = $i;
                        $highest = $tmp;
                    }
                }
                $diff = ($highest - $lowest) / 100.0;
            } elseif ($tmp >= $highest) {
                $highestIdx = $today;
                $highest = $tmp;
                $diff = ($highest - $lowest) / 100.0;
            }
            if ($diff !== 0.0) {
                $tempBuffer[$outIdx++] = ($inClose[$today] - $lowest) / $diff;
            } else {
                $tempBuffer[$outIdx++] = 0.0;
            }
            $trailingIdx++;
            $today++;
        }
        $ReturnCode = OverlapStudies::movingAverage(
            0,
            $outIdx - 1,
            $tempBuffer,
            $optInSlowK_Period,
            $optInSlowK_MAType,
            $outBegIdx,
            $outNBElement,
            $tempBuffer
        );
        if ($ReturnCode !== ReturnCode::Success || $outNBElement === 0) {
            $outBegIdx = 0;
            $outNBElement = 0;

            return $ReturnCode;
        }
        $ReturnCode = OverlapStudies::movingAverage(
            0,
            $outNBElement - 1,
            $tempBuffer,
            $optInSlowD_Period,
            $optInSlowD_MAType,
            $outBegIdx,
            $outNBElement,
            $outSlowD
        );
        //System::arraycopy($tempBuffer, $lookbackDSlow, $outSlowK, 0, (int)$outNBElement);
        $outSlowK = \array_slice($tempBuffer, $lookbackDSlow, $outNBElement);
        if ($ReturnCode !== ReturnCode::Success) {
            $outBegIdx = 0;
            $outNBElement = 0;

            return $ReturnCode;
        }
        $outBegIdx = $startIdx;

        return ReturnCode::Success;
    }

    public static function stochF(
        int $startIdx,
        int $endIdx,
        array $inHigh,
        array $inLow,
        array $inClose,
        int $optInFastK_Period,
        int $optInFastD_Period,
        int $optInFastD_MAType,
        int &$outBegIdx,
        int &$outNBElement,
        array &$outFastK,
        array &$outFastD
    ): int {
        if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) {
            return $RetCode;
        }
        if ($optInFastK_Period === PHP_INT_MIN) {
            $optInFastK_Period = 5;
        } elseif ($optInFastK_Period < 1 || $optInFastK_Period > 100000) {
            return ReturnCode::BadParam;
        }
        if ($optInFastD_Period === PHP_INT_MIN) {
            $optInFastD_Period = 3;
        } elseif ($optInFastD_Period < 1 || $optInFastD_Period > 100000) {
            return ReturnCode::BadParam;
        }
        $lookbackK = $optInFastK_Period - 1;
        $lookbackFastD = Lookback::movingAverageLookback($optInFastD_Period, $optInFastD_MAType);
        $lookbackTotal = $lookbackK + $lookbackFastD;
        if ($startIdx < $lookbackTotal) {
            $startIdx = $lookbackTotal;
        }
        if ($startIdx > $endIdx) {
            $outBegIdx = 0;
            $outNBElement = 0;

            return ReturnCode::Success;
        }
        $outIdx = 0;
        $trailingIdx = $startIdx - $lookbackTotal;
        $today = $trailingIdx + $lookbackK;
        $lowestIdx = $highestIdx = -1;
        $diff = $highest = $lowest = 0.0;
        if ($outFastK === $inHigh ||
            $outFastK === $inLow ||
            $outFastK === $inClose) {
            $tempBuffer = $outFastK;
        } elseif ($outFastD === $inHigh ||
                  $outFastD === $inLow ||
                  $outFastD === $inClose) {
            $tempBuffer = $outFastD;
        } else {
            $tempBuffer = static::double($endIdx - $today + 1);
        }
        while ($today <= $endIdx) {
            $tmp = $inLow[$today];
            if ($lowestIdx < $trailingIdx) {
                $lowestIdx = $trailingIdx;
                $lowest = $inLow[$lowestIdx];
                $i = $lowestIdx;
                while (++$i <= $today) {
                    $tmp = $inLow[$i];
                    if ($tmp < $lowest) {
                        $lowestIdx = $i;
                        $lowest = $tmp;
                    }
                }
                $diff = ($highest - $lowest) / 100.0;
            } elseif ($tmp <= $lowest) {
                $lowestIdx = $today;
                $lowest = $tmp;
                $diff = ($highest - $lowest) / 100.0;
            }
            $tmp = $inHigh[$today];
            if ($highestIdx < $trailingIdx) {
                $highestIdx = $trailingIdx;
                $highest = $inHigh[$highestIdx];
                $i = $highestIdx;
                while (++$i <= $today) {
                    $tmp = $inHigh[$i];
                    if ($tmp > $highest) {
                        $highestIdx = $i;
                        $highest = $tmp;
                    }
                }
                $diff = ($highest - $lowest) / 100.0;
            } elseif ($tmp >= $highest) {
                $highestIdx = $today;
                $highest = $tmp;
                $diff = ($highest - $lowest) / 100.0;
            }
            if ($diff !== 0.0) {
                $tempBuffer[$outIdx++] = ($inClose[$today] - $lowest) / $diff;
            } else {
                $tempBuffer[$outIdx++] = 0.0;
            }
            $trailingIdx++;
            $today++;
        }
        $ReturnCode = OverlapStudies::movingAverage(
            0,
            $outIdx - 1,
            $tempBuffer,
            $optInFastD_Period,
            $optInFastD_MAType,
            $outBegIdx,
            $outNBElement,
            $outFastD
        );
        if ($ReturnCode !== ReturnCode::Success || $outNBElement === 0) {
            $outBegIdx = 0;
            $outNBElement = 0;

            return $ReturnCode;
        }
        //System::arraycopy($tempBuffer, $lookbackFastD, $outFastK, 0, (int)$outNBElement);
        $outFastK = \array_slice($tempBuffer, $lookbackFastD, $outNBElement);
        $outBegIdx = $startIdx;

        return ReturnCode::Success;
    }

    public static function stochRsi(
        int $startIdx,
        int $endIdx,
        array $inReal,
        int $optInTimePeriod,
        int $optInFastK_Period,
        int $optInFastD_Period,
        int $optInFastD_MAType,
        int &$outBegIdx,
        int &$outNBElement,
        array &$outFastK,
        array &$outFastD
    ): int {
        if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) {
            return $RetCode;
        }
        $outBegIdx1 = 0;
        $outBegIdx2 = 0;
        $outNbElement1 = 0;
        if ($optInTimePeriod === PHP_INT_MIN) {
            $optInTimePeriod = 14;
        } elseif ($optInTimePeriod < 2 || $optInTimePeriod > 100000) {
            return ReturnCode::BadParam;
        }
        if ($optInFastK_Period === PHP_INT_MIN) {
            $optInFastK_Period = 5;
        } elseif ($optInFastK_Period < 1 || $optInFastK_Period > 100000) {
            return ReturnCode::BadParam;
        }
        if ($optInFastD_Period === PHP_INT_MIN) {
            $optInFastD_Period = 3;
        } elseif ($optInFastD_Period < 1 || $optInFastD_Period > 100000) {
            return ReturnCode::BadParam;
        }
        $outBegIdx = 0;
        $outNBElement = 0;
        $lookbackSTOCHF = Lookback::stochFLookback($optInFastK_Period, $optInFastD_Period, $optInFastD_MAType);
        $lookbackTotal = Lookback::rsiLookback($optInTimePeriod) + $lookbackSTOCHF;
        if ($startIdx < $lookbackTotal) {
            $startIdx = $lookbackTotal;
        }
        if ($startIdx > $endIdx) {
            return ReturnCode::Success;
        }
        $outBegIdx = $startIdx;
        $tempArraySize = $endIdx - $startIdx + 1 + $lookbackSTOCHF;
        $tempRSIBuffer = static::double($tempArraySize);
        $ReturnCode = self::rsi(
            $startIdx - $lookbackSTOCHF,
            $endIdx,
            $inReal,
            $optInTimePeriod,
            $outBegIdx1,
            $outNbElement1,
            $tempRSIBuffer
        );
        if ($ReturnCode !== ReturnCode::Success || $outNbElement1 === 0) {
            $outBegIdx = 0;

            return $ReturnCode;
        }
        $ReturnCode = self::stochF(
            0,
            $tempArraySize - 1,
            $tempRSIBuffer,
            $tempRSIBuffer,
            $tempRSIBuffer,
            $optInFastK_Period,
            $optInFastD_Period,
            $optInFastD_MAType,
            $outBegIdx2,
            $outNBElement,
            $outFastK,
            $outFastD
        );
        if ($ReturnCode !== ReturnCode::Success || $outNBElement === 0) {
            $outBegIdx = 0;
            $outNBElement = 0;

            return $ReturnCode;
        }

        return ReturnCode::Success;
    }

    public static function trix(int $startIdx, int $endIdx, array $inReal, int $optInTimePeriod, int &$outBegIdx, int &$outNBElement, array &$outReal): int
    {
        if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) {
            return $RetCode;
        }
        $nbElement = 0;
        $begIdx = 0;
        if ($optInTimePeriod === PHP_INT_MIN) {
            $optInTimePeriod = 30;
        } elseif ($optInTimePeriod < 1 || $optInTimePeriod > 100000) {
            return ReturnCode::BadParam;
        }
        $emaLookback = Lookback::emaLookback($optInTimePeriod);
        $rocLookback = Lookback::rocRLookback(1);
        $totalLookback = $emaLookback * 3 + $rocLookback;
        if ($startIdx < $totalLookback) {
            $startIdx = (int)$totalLookback;
        }
        if ($startIdx > $endIdx) {
            $outNBElement = 0;
            $outBegIdx = 0;

            return ReturnCode::Success;
        }
        $outBegIdx = $startIdx;
        $nbElementToOutput = $endIdx - $startIdx + 1 + $totalLookback;
        $tempBuffer = static::double($nbElementToOutput);
        $k = 2.0 / (double)($optInTimePeriod + 1);
        $ReturnCode = static::TA_INT_EMA(
            $startIdx - $totalLookback,
            $endIdx,
            $inReal,
            $optInTimePeriod,
            $k,
            $begIdx,
            $nbElement,
            $tempBuffer
        );
        if ($ReturnCode !== ReturnCode::Success || $nbElement === 0) {
            $outNBElement = 0;
            $outBegIdx = 0;

            return $ReturnCode;
        }
        $nbElementToOutput--;
        $nbElementToOutput -= $emaLookback;
        $ReturnCode = static::TA_INT_EMA(
            0,
            $nbElementToOutput,
            $tempBuffer,
            $optInTimePeriod,
            $k,
            $begIdx,
            $nbElement,
            $tempBuffer
        );
        if ($ReturnCode !== ReturnCode::Success || $nbElement === 0) {
            $outNBElement = 0;
            $outBegIdx = 0;

            return $ReturnCode;
        }
        $nbElementToOutput -= $emaLookback;
        $ReturnCode = static::TA_INT_EMA(
            0,
            $nbElementToOutput,
            $tempBuffer,
            $optInTimePeriod,
            $k,
            $begIdx,
            $nbElement,
            $tempBuffer
        );
        if ($ReturnCode !== ReturnCode::Success || $nbElement === 0) {
            $outNBElement = 0;
            $outBegIdx = 0;

            return $ReturnCode;
        }
        $nbElementToOutput -= $emaLookback;
        $ReturnCode = self::roc(
            0,
            $nbElementToOutput,
            $tempBuffer,
            1,
            $begIdx,
            $outNBElement,
            $outReal
        );
        if ($ReturnCode !== ReturnCode::Success || $outNBElement === 0) {
            $outNBElement = 0;
            $outBegIdx = 0;

            return $ReturnCode;
        }

        return ReturnCode::Success;
    }

    public static function ultOsc(
        int $startIdx,
        int $endIdx,
        array $inHigh,
        array $inLow,
        array $inClose,
        int $optInTimePeriod1,
        int $optInTimePeriod2,
        int $optInTimePeriod3,
        int &$outBegIdx,
        int &$outNBElement,
        array &$outReal
    ): int {
        if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) {
            return $RetCode;
        }
        $usedFlag = \array_pad([], 3, 0);
        $periods = \array_pad([], 3, 0);
        $sortedPeriods = \array_pad([], 3, 0);
        if ($optInTimePeriod1 === PHP_INT_MIN) {
            $optInTimePeriod1 = 7;
        } elseif ($optInTimePeriod1 < 1 || $optInTimePeriod1 > 100000) {
            return ReturnCode::BadParam;
        }
        if ($optInTimePeriod2 === PHP_INT_MIN) {
            $optInTimePeriod2 = 14;
        } elseif ($optInTimePeriod2 < 1 || $optInTimePeriod2 > 100000) {
            return ReturnCode::BadParam;
        }
        if ($optInTimePeriod3 === PHP_INT_MIN) {
            $optInTimePeriod3 = 28;
        } elseif ($optInTimePeriod3 < 1 || $optInTimePeriod3 > 100000) {
            return ReturnCode::BadParam;
        }
        $outBegIdx = 0;
        $outNBElement = 0;

        $periods = [$optInTimePeriod1, $optInTimePeriod2, $optInTimePeriod3];
        sort($periods);
        [$optInTimePeriod1, $optInTimePeriod2, $optInTimePeriod3] = $periods;

        $lookbackTotal = Lookback::ultOscLookback(...$periods);
        if ($startIdx < $lookbackTotal) {
            $startIdx = $lookbackTotal;
        }
        if ($startIdx > $endIdx) {
            return ReturnCode::Success;
        }
        {
            $a1Total = 0;
            $b1Total = 0;
            for ($i = $startIdx - $optInTimePeriod1 + 1; $i < $startIdx; ++$i) {
                {
                    $tempLT = $inLow[$i];
                    $tempHT = $inHigh[$i];
                    $tempCY = $inClose[$i - 1];
                    $trueLow = min($tempLT, $tempCY);
                    $closeMinusTrueLow = $inClose[$i] - $trueLow;
                    $trueRange = $tempHT - $tempLT;
                    $tempDouble = abs($tempCY - $tempHT);
                    if ($tempDouble > $trueRange) {
                        $trueRange = $tempDouble;
                    }
                    $tempDouble = abs($tempCY - $tempLT);
                    if ($tempDouble > $trueRange) {
                        $trueRange = $tempDouble;
                    }
                }
                $a1Total += $closeMinusTrueLow;
                $b1Total += $trueRange;
            }
        }
        {
            $a2Total = 0;
            $b2Total = 0;
            for ($i = $startIdx - $optInTimePeriod2 + 1; $i < $startIdx; ++$i) {
                {
                    $tempLT = $inLow[$i];
                    $tempHT = $inHigh[$i];
                    $tempCY = $inClose[$i - 1];
                    $trueLow = min($tempLT, $tempCY);
                    $closeMinusTrueLow = $inClose[$i] - $trueLow;
                    $trueRange = $tempHT - $tempLT;
                    $tempDouble = abs($tempCY - $tempHT);
                    if ($tempDouble > $trueRange) {
                        $trueRange = $tempDouble;
                    }
                    $tempDouble = abs($tempCY - $tempLT);
                    if ($tempDouble > $trueRange) {
                        $trueRange = $tempDouble;
                    }
                }
                $a2Total += $closeMinusTrueLow;
                $b2Total += $trueRange;
            }
        }
        {
            $a3Total = 0;
            $b3Total = 0;
            for ($i = $startIdx - $optInTimePeriod3 + 1; $i < $startIdx; ++$i) {
                {
                    $tempLT = $inLow[$i];
                    $tempHT = $inHigh[$i];
                    $tempCY = $inClose[$i - 1];
                    $trueLow = min($tempLT, $tempCY);
                    $closeMinusTrueLow = $inClose[$i] - $trueLow;
                    $trueRange = $tempHT - $tempLT;
                    $tempDouble = abs($tempCY - $tempHT);
                    if ($tempDouble > $trueRange) {
                        $trueRange = $tempDouble;
                    }
                    $tempDouble = abs($tempCY - $tempLT);
                    if ($tempDouble > $trueRange) {
                        $trueRange = $tempDouble;
                    }
                }
                $a3Total += $closeMinusTrueLow;
                $b3Total += $trueRange;
            }
        }
        $today = $startIdx;
        $outIdx = 0;
        $trailingIdx1 = $today - $optInTimePeriod1 + 1;
        $trailingIdx2 = $today - $optInTimePeriod2 + 1;
        $trailingIdx3 = $today - $optInTimePeriod3 + 1;
        while ($today <= $endIdx) {
            {
                $tempLT = $inLow[$today];
                $tempHT = $inHigh[$today];
                $tempCY = $inClose[$today - 1];
                $trueLow = min($tempLT, $tempCY);
                $closeMinusTrueLow = $inClose[$today] - $trueLow;
                $trueRange = $tempHT - $tempLT;
                $tempDouble = abs($tempCY - $tempHT);
                if ($tempDouble > $trueRange) {
                    $trueRange = $tempDouble;
                }
                $tempDouble = abs($tempCY - $tempLT);
                if ($tempDouble > $trueRange) {
                    $trueRange = $tempDouble;
                }
            }
            $a1Total += $closeMinusTrueLow;
            $a2Total += $closeMinusTrueLow;
            $a3Total += $closeMinusTrueLow;
            $b1Total += $trueRange;
            $b2Total += $trueRange;
            $b3Total += $trueRange;
            $output = 0.0;
            if (!(-0.00000001 < $b1Total && $b1Total < 0.00000001)) {
                $output += 4.0 * ($a1Total / $b1Total);
            }
            if (!(-0.00000001 < $b2Total && $b2Total < 0.00000001)) {
                $output += 2.0 * ($a2Total / $b2Total);
            }
            if (!(-0.00000001 < $b3Total && $b3Total < 0.00000001)) {
                $output += $a3Total / $b3Total;
            }
            {
                $tempLT = $inLow[$trailingIdx1];
                $tempHT = $inHigh[$trailingIdx1];
                $tempCY = $inClose[$trailingIdx1 - 1];
                $trueLow = min($tempLT, $tempCY);
                $closeMinusTrueLow = $inClose[$trailingIdx1] - $trueLow;
                $trueRange = $tempHT - $tempLT;
                $tempDouble = abs($tempCY - $tempHT);
                if ($tempDouble > $trueRange) {
                    $trueRange = $tempDouble;
                }
                $tempDouble = abs($tempCY - $tempLT);
                if ($tempDouble > $trueRange) {
                    $trueRange = $tempDouble;
                }
            }
            $a1Total -= $closeMinusTrueLow;
            $b1Total -= $trueRange;
            {
                $tempLT = $inLow[$trailingIdx2];
                $tempHT = $inHigh[$trailingIdx2];
                $tempCY = $inClose[$trailingIdx2 - 1];
                $trueLow = min($tempLT, $tempCY);
                $closeMinusTrueLow = $inClose[$trailingIdx2] - $trueLow;
                $trueRange = $tempHT - $tempLT;
                $tempDouble = abs($tempCY - $tempHT);
                if ($tempDouble > $trueRange) {
                    $trueRange = $tempDouble;
                }
                $tempDouble = abs($tempCY - $tempLT);
                if ($tempDouble > $trueRange) {
                    $trueRange = $tempDouble;
                }
            }
            $a2Total -= $closeMinusTrueLow;
            $b2Total -= $trueRange;
            {
                $tempLT = $inLow[$trailingIdx3];
                $tempHT = $inHigh[$trailingIdx3];
                $tempCY = $inClose[$trailingIdx3 - 1];
                $trueLow = min($tempLT, $tempCY);
                $closeMinusTrueLow = $inClose[$trailingIdx3] - $trueLow;
                $trueRange = $tempHT - $tempLT;
                $tempDouble = abs($tempCY - $tempHT);
                if ($tempDouble > $trueRange) {
                    $trueRange = $tempDouble;
                }
                $tempDouble = abs($tempCY - $tempLT);
                if ($tempDouble > $trueRange) {
                    $trueRange = $tempDouble;
                }
            }
            $a3Total -= $closeMinusTrueLow;
            $b3Total -= $trueRange;
            $outReal[$outIdx] = 100.0 * ($output / 7.0);
            $outIdx++;
            $today++;
            $trailingIdx1++;
            $trailingIdx2++;
            $trailingIdx3++;
        }
        $outNBElement = $outIdx;
        $outBegIdx = $startIdx;

        return ReturnCode::Success;
    }

    public static function willR(int $startIdx, int $endIdx, array $inHigh, array $inLow, array $inClose, int $optInTimePeriod, int &$outBegIdx, int &$outNBElement, array &$outReal): int
    {
        if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) {
            return $RetCode;
        }
        if ($optInTimePeriod === PHP_INT_MIN) {
            $optInTimePeriod = 14;
        } elseif ($optInTimePeriod < 2 || $optInTimePeriod > 100000) {
            return ReturnCode::BadParam;
        }
        $nbInitialElementNeeded = $optInTimePeriod - 1;
        if ($startIdx < $nbInitialElementNeeded) {
            $startIdx = $nbInitialElementNeeded;
        }
        if ($startIdx > $endIdx) {
            $outBegIdx = 0;
            $outNBElement = 0;

            return ReturnCode::Success;
        }
        $diff = 0.0;
        $outIdx = 0;
        $today = $startIdx;
        $trailingIdx = $startIdx - $nbInitialElementNeeded;
        $lowestIdx = $highestIdx = -1;
        $diff = $highest = $lowest = 0.0;
        while ($today <= $endIdx) {
            $tmp = $inLow[$today];
            if ($lowestIdx < $trailingIdx) {
                $lowestIdx = $trailingIdx;
                $lowest = $inLow[$lowestIdx];
                $i = $lowestIdx;
                while (++$i <= $today) {
                    $tmp = $inLow[$i];
                    if ($tmp < $lowest) {
                        $lowestIdx = $i;
                        $lowest = $tmp;
                    }
                }
                $diff = ($highest - $lowest) / -100.0;
            } elseif ($tmp <= $lowest) {
                $lowestIdx = $today;
                $lowest = $tmp;
                $diff = ($highest - $lowest) / -100.0;
            }
            $tmp = $inHigh[$today];
            if ($highestIdx < $trailingIdx) {
                $highestIdx = $trailingIdx;
                $highest = $inHigh[$highestIdx];
                $i = $highestIdx;
                while (++$i <= $today) {
                    $tmp = $inHigh[$i];
                    if ($tmp > $highest) {
                        $highestIdx = $i;
                        $highest = $tmp;
                    }
                }
                $diff = ($highest - $lowest) / -100.0;
            } elseif ($tmp >= $highest) {
                $highestIdx = $today;
                $highest = $tmp;
                $diff = ($highest - $lowest) / -100.0;
            }
            if ($diff !== 0.0) {
                $outReal[$outIdx++] = ($highest - $inClose[$today]) / $diff;
            } else {
                $outReal[$outIdx++] = 0.0;
            }
            $trailingIdx++;
            $today++;
        }
        $outBegIdx = $startIdx;
        $outNBElement = $outIdx;

        return ReturnCode::Success;
    }
}

Function Calls

None

Variables

None

Stats

MD5 7d63b8ca7a5dc058ad8ab270f943d972
Eval Count 0
Decode Time 146 ms