/* TA-LIB Copyright (c) 1999-2025, 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.
 */

/* Important:
 *  This file is automatically generated by the utility ta_codegen.
 *  Any modifications will be lost on next execution of ta_codegen.
 *
 *  Modifications should instead be done with the "C" source file
 *  in ta-lib\src\ta_func
 */

/* List of contributors:
 *
 *  Initial  Name/description
 *  -------------------------------------------------------------------
 *  DM       Drew McCormack (http://www.trade-strategist.com)
 *  MF       Mario Fortier
 *
 * Change history:
 *
 *  MMDDYY BY   Description
 *  -------------------------------------------------------------------
 *  281206 DM   Initial Implementation
 *  010606 MF   Abstract local arrays. Detect divide by zero.
 */

// Import types from parent module
use super::*;

// Allow non-snake-case names to maintain TA-Lib API compatibility
#[allow(non_snake_case)]
#[allow(unused_variables)]
#[allow(dead_code)]
#[allow(unused_mut)]
#[allow(unused_assignments)]
impl Core {
    /// Lookback period for [`Core::ultosc`]: the number of leading input values consumed before the
    /// first output value can be produced.
    ///
    /// # Arguments
    ///
    /// * `optInTimePeriod1` — Bars for one averaging window (default 7, range 1..=100000)
    /// * `optInTimePeriod2` — Bars for another averaging window (default 14, range 1..=100000)
    /// * `optInTimePeriod3` — Bars for another averaging window (default 28, range 1..=100000)
    ///
    /// Returns `usize::MAX` when a parameter is out of range. Integer parameters accept `i32::MIN`
    /// to select their default value.
    #[inline]
    pub fn ultosc_lookback(&self, mut optInTimePeriod1: i32, mut optInTimePeriod2: i32, mut optInTimePeriod3: i32) -> usize {
        if ((optInTimePeriod1) as i32) == (i32::MIN) {
            optInTimePeriod1 = 7;
        } else if (((optInTimePeriod1) as i32) < 1) || (((optInTimePeriod1) as i32) > 100000) {
            return usize::MAX;
        }
        if ((optInTimePeriod2) as i32) == (i32::MIN) {
            optInTimePeriod2 = 14;
        } else if (((optInTimePeriod2) as i32) < 1) || (((optInTimePeriod2) as i32) > 100000) {
            return usize::MAX;
        }
        if ((optInTimePeriod3) as i32) == (i32::MIN) {
            optInTimePeriod3 = 28;
        } else if (((optInTimePeriod3) as i32) < 1) || (((optInTimePeriod3) as i32) > 100000) {
            return usize::MAX;
        }
        let mut maxPeriod: usize = 0_usize;
        // Lookback for the Ultimate Oscillator is the lookback of the SMA with the longest
        // time period, plus 1 for the True Range.
        maxPeriod = (((optInTimePeriod1).max(optInTimePeriod2)).max(optInTimePeriod3)) as usize;
        return (self.sma_lookback((maxPeriod) as i32) + 1) as usize;
    }
    /// Ultimate Oscillator: momentum indicator combining buying-pressure/true-range ratios over
    /// three time periods into one 0-100 weighted average. Blends short-, medium-, and long-term
    /// momentum to damp single-period noise. Ranges 0-100; conventionally >70 overbought, \<30
    /// oversold.
    ///
    /// # Formula
    ///
    /// ```text
    /// trueLow = min(low, prevClose);  BP = close - trueLow
    /// TR = max(high-low, |prevClose-high|, |prevClose-low|)
    /// avg_n = (sum BP over n bars) / (sum TR over n bars)
    /// ULTOSC = 100 * (4*avg_short + 2*avg_mid + avg_long) / 7
    /// ```
    ///
    /// # Notes
    ///
    /// * The three periods are sorted internally, so the 4/2/1 weighting always applies to the
    ///   shortest, middle, and longest period regardless of the order in which you pass them.
    ///
    /// # Arguments
    ///
    /// * `startIdx` — Start index of the requested calculation range.
    /// * `endIdx` — End index of the requested calculation range (inclusive).
    /// * `inHigh` — High prices per bar.
    /// * `inLow` — Low prices per bar.
    /// * `inClose` — Close prices per bar.
    /// * `optInTimePeriod1` — Bars for one averaging window (default 7, range 1..=100000)
    /// * `optInTimePeriod2` — Bars for another averaging window (default 14, range 1..=100000)
    /// * `optInTimePeriod3` — Bars for another averaging window (default 28, range 1..=100000)
    /// * `outBegIdx` — Set to the input index of the first output value.
    /// * `outNBElement` — Set to the number of output values written.
    /// * `outReal` — Ultimate Oscillator value.
    ///
    /// Integer parameters accept `i32::MIN` to select their default value.
    ///
    /// # Errors
    ///
    /// Returns [`RetCode::OutOfRangeStartIndex`] when `endIdx < startIdx`, and
    /// [`RetCode::BadParam`] when an optional parameter is outside its documented range.
    ///
    /// # Panics
    ///
    /// Input slices must cover `startIdx..=endIdx` and output slices must hold the number of values
    /// produced for that range: undersized slices panic or, for functions that forward to unchecked
    /// internals, cause undefined behavior. Sizing every output slice to the input length is always
    /// sufficient.
    ///
    /// # Examples
    ///
    /// ```
    /// use ta_lib::{Core, RetCode};
    ///
    /// let high: Vec<f64> = (0..252).map(|i| 101.0 + 10.0 * (0.1 * i as f64).sin()).collect();
    /// let low: Vec<f64> = (0..252).map(|i| 99.0 + 10.0 * (0.1 * i as f64).sin()).collect();
    /// let close: Vec<f64> = (0..252).map(|i| 100.0 + 10.0 * (0.1 * i as f64).sin()).collect();
    ///
    /// let core = Core::new();
    /// let mut out_beg = 0;
    /// let mut out_nb = 0;
    /// let mut out = vec![0.0; 252];
    ///
    /// let ret = core.ultosc(
    ///     0, high.len() - 1, &high, &low, &close, 7, 14, 28,
    ///     &mut out_beg, &mut out_nb, &mut out,
    /// );
    /// assert_eq!(ret, RetCode::Success);
    /// assert!(out_nb > 0);
    /// ```
    ///
    /// # See also
    ///
    /// [`Core::atr`] · [`Core::trange`] · [`Core::rsi`]
    ///
    /// Further reading: [ta-lib.org/functions/ultosc](https://ta-lib.org/functions/ultosc/)
    #[doc(alias = "UltimateOscillator")]
    #[doc(alias = "UO")]
    pub fn ultosc(
        &self,
        startIdx: usize,
        endIdx: usize,
        inHigh: &[f64],
        inLow: &[f64],
        inClose: &[f64],
        mut optInTimePeriod1: i32,
        mut optInTimePeriod2: i32,
        mut optInTimePeriod3: i32,
        outBegIdx: &mut usize,
        outNBElement: &mut usize,
        outReal: &mut [f64],
    ) -> RetCode {
        if endIdx < startIdx {
            return RetCode::OutOfRangeStartIndex;
        }
        if ((optInTimePeriod1) as i32) == (i32::MIN) {
            optInTimePeriod1 = 7;
        } else if (((optInTimePeriod1) as i32) < 1) || (((optInTimePeriod1) as i32) > 100000) {
            return RetCode::BadParam;
        }
        if ((optInTimePeriod2) as i32) == (i32::MIN) {
            optInTimePeriod2 = 14;
        } else if (((optInTimePeriod2) as i32) < 1) || (((optInTimePeriod2) as i32) > 100000) {
            return RetCode::BadParam;
        }
        if ((optInTimePeriod3) as i32) == (i32::MIN) {
            optInTimePeriod3 = 28;
        } else if (((optInTimePeriod3) as i32) < 1) || (((optInTimePeriod3) as i32) > 100000) {
            return RetCode::BadParam;
        }
        let mut startIdx = startIdx;
        let mut a1Total: f64 = 0.0_f64;
        let mut a2Total: f64 = 0.0_f64;
        let mut a3Total: f64 = 0.0_f64;
        let mut b1Total: f64 = 0.0_f64;
        let mut b2Total: f64 = 0.0_f64;
        let mut b3Total: f64 = 0.0_f64;
        let mut trueLow: f64 = 0.0_f64;
        let mut trueRange: f64 = 0.0_f64;
        let mut closeMinusTrueLow: f64 = 0.0_f64;
        let mut tempDouble: f64 = 0.0_f64;
        let mut output: f64 = 0.0_f64;
        let mut tempHT: f64 = 0.0_f64;
        let mut tempLT: f64 = 0.0_f64;
        let mut tempCY: f64 = 0.0_f64;
        let mut lookbackTotal: usize = 0_usize;
        let mut longestPeriod: usize = 0_usize;
        let mut longestIndex: usize = 0_usize;
        let mut i: usize = 0_usize;
        let mut j: usize = 0_usize;
        let mut today: usize = 0_usize;
        let mut outIdx: usize = 0_usize;
        let mut trailingIdx1: usize = 0_usize;
        let mut trailingIdx2: usize = 0_usize;
        let mut trailingIdx3: usize = 0_usize;
        let mut usedFlag: [i32; 3 as usize] = [0i32; 3 as usize];
        let mut periods: [i32; 3 as usize] = [0i32; 3 as usize];
        let mut sortedPeriods: [i32; 3 as usize] = [0i32; 3 as usize];
        (*outBegIdx) = 0;
        (*outNBElement) = 0;
        // Ensure that the time periods are ordered from shortest to longest.
        // Sort.
        periods[0] = optInTimePeriod1;
        periods[1] = optInTimePeriod2;
        periods[2] = optInTimePeriod3;
        usedFlag[0] = 0;
        usedFlag[1] = 0;
        usedFlag[2] = 0;
        // for( i = 0; i < 3; i += 1 )
        i = 0;
        while i < 3 {
            longestPeriod = 0;
            longestIndex = 0;
            // for( j = 0; j < 3; j += 1 )
            j = 0;
            while j < 3 {
                if usedFlag[j] == 0 && (periods[j]) as usize > longestPeriod {
                    longestPeriod = (periods[j]) as usize;
                    longestIndex = j;
                }
                j += 1;
            }
            usedFlag[longestIndex] = 1;
            sortedPeriods[i] = (longestPeriod) as i32;
            i += 1;
        }
        optInTimePeriod1 = sortedPeriods[2];
        optInTimePeriod2 = sortedPeriods[1];
        optInTimePeriod3 = sortedPeriods[0];
        // Adjust startIdx for lookback period.
        lookbackTotal = self.ultosc_lookback(optInTimePeriod1, optInTimePeriod2, optInTimePeriod3);
        if startIdx < lookbackTotal {
            startIdx = lookbackTotal;
        }
        // Make sure there is still something to evaluate.
        if startIdx > endIdx {
            return RetCode::Success;
        }
        // Prime running totals used in moving averages
        a1Total = 0.0;
        b1Total = 0.0;
        // for( i = startIdx - (optInTimePeriod1) as usize + 1; i < startIdx; i += 1 )
        i = startIdx - (optInTimePeriod1) as usize + 1;
        while i < startIdx {
            tempLT = inLow[i];
            tempHT = inHigh[i];
            tempCY = inClose[i - 1];
            trueLow = (tempLT).min(tempCY);
            closeMinusTrueLow = inClose[i] - trueLow;
            trueRange = tempHT - tempLT;
            tempDouble = (tempCY - tempHT).abs();
            if tempDouble > trueRange {
                trueRange = tempDouble;
            }
            tempDouble = (tempCY - tempLT).abs();
            if tempDouble > trueRange {
                trueRange = tempDouble;
            }
            a1Total += closeMinusTrueLow;
            b1Total += trueRange;
            i += 1;
        }
        a2Total = 0.0;
        b2Total = 0.0;
        // for( i = startIdx - (optInTimePeriod2) as usize + 1; i < startIdx; i += 1 )
        i = startIdx - (optInTimePeriod2) as usize + 1;
        while i < startIdx {
            tempLT = inLow[i];
            tempHT = inHigh[i];
            tempCY = inClose[i - 1];
            trueLow = (tempLT).min(tempCY);
            closeMinusTrueLow = inClose[i] - trueLow;
            trueRange = tempHT - tempLT;
            tempDouble = (tempCY - tempHT).abs();
            if tempDouble > trueRange {
                trueRange = tempDouble;
            }
            tempDouble = (tempCY - tempLT).abs();
            if tempDouble > trueRange {
                trueRange = tempDouble;
            }
            a2Total += closeMinusTrueLow;
            b2Total += trueRange;
            i += 1;
        }
        a3Total = 0.0;
        b3Total = 0.0;
        // for( i = startIdx - (optInTimePeriod3) as usize + 1; i < startIdx; i += 1 )
        i = startIdx - (optInTimePeriod3) as usize + 1;
        while i < startIdx {
            tempLT = inLow[i];
            tempHT = inHigh[i];
            tempCY = inClose[i - 1];
            trueLow = (tempLT).min(tempCY);
            closeMinusTrueLow = inClose[i] - trueLow;
            trueRange = tempHT - tempLT;
            tempDouble = (tempCY - tempHT).abs();
            if tempDouble > trueRange {
                trueRange = tempDouble;
            }
            tempDouble = (tempCY - tempLT).abs();
            if tempDouble > trueRange {
                trueRange = tempDouble;
            }
            a3Total += closeMinusTrueLow;
            b3Total += trueRange;
            i += 1;
        }
        // Calculate oscillator
        today = startIdx;
        outIdx = 0;
        trailingIdx1 = today - (optInTimePeriod1) as usize + 1;
        trailingIdx2 = today - (optInTimePeriod2) as usize + 1;
        trailingIdx3 = today - (optInTimePeriod3) as usize + 1;
        while today <= endIdx {
            // Add on today's terms
            tempLT = inLow[today];
            tempHT = inHigh[today];
            tempCY = inClose[today - 1];
            trueLow = (tempLT).min(tempCY);
            closeMinusTrueLow = inClose[today] - trueLow;
            trueRange = tempHT - tempLT;
            tempDouble = (tempCY - tempHT).abs();
            if tempDouble > trueRange {
                trueRange = tempDouble;
            }
            tempDouble = (tempCY - tempLT).abs();
            if tempDouble > trueRange {
                trueRange = tempDouble;
            }
            a1Total += closeMinusTrueLow;
            a2Total += closeMinusTrueLow;
            a3Total += closeMinusTrueLow;
            b1Total += trueRange;
            b2Total += trueRange;
            b3Total += trueRange;
            // Calculate the oscillator value for today
            output = 0.0;
            if !((b1Total).abs() < 1e-14) {
                output += 4.0 * (a1Total / b1Total);
            }
            if !((b2Total).abs() < 1e-14) {
                output += 2.0 * (a2Total / b2Total);
            }
            if !((b3Total).abs() < 1e-14) {
                output += a3Total / b3Total;
            }
            // Remove the trailing terms to prepare for next day
            tempLT = inLow[trailingIdx1];
            tempHT = inHigh[trailingIdx1];
            tempCY = inClose[trailingIdx1 - 1];
            trueLow = (tempLT).min(tempCY);
            closeMinusTrueLow = inClose[trailingIdx1] - trueLow;
            trueRange = tempHT - tempLT;
            tempDouble = (tempCY - tempHT).abs();
            if tempDouble > trueRange {
                trueRange = tempDouble;
            }
            tempDouble = (tempCY - tempLT).abs();
            if tempDouble > trueRange {
                trueRange = tempDouble;
            }
            a1Total -= closeMinusTrueLow;
            b1Total -= trueRange;
            tempLT = inLow[trailingIdx2];
            tempHT = inHigh[trailingIdx2];
            tempCY = inClose[trailingIdx2 - 1];
            trueLow = (tempLT).min(tempCY);
            closeMinusTrueLow = inClose[trailingIdx2] - trueLow;
            trueRange = tempHT - tempLT;
            tempDouble = (tempCY - tempHT).abs();
            if tempDouble > trueRange {
                trueRange = tempDouble;
            }
            tempDouble = (tempCY - tempLT).abs();
            if tempDouble > trueRange {
                trueRange = tempDouble;
            }
            a2Total -= closeMinusTrueLow;
            b2Total -= trueRange;
            tempLT = inLow[trailingIdx3];
            tempHT = inHigh[trailingIdx3];
            tempCY = inClose[trailingIdx3 - 1];
            trueLow = (tempLT).min(tempCY);
            closeMinusTrueLow = inClose[trailingIdx3] - trueLow;
            trueRange = tempHT - tempLT;
            tempDouble = (tempCY - tempHT).abs();
            if tempDouble > trueRange {
                trueRange = tempDouble;
            }
            tempDouble = (tempCY - tempLT).abs();
            if tempDouble > trueRange {
                trueRange = tempDouble;
            }
            a3Total -= closeMinusTrueLow;
            b3Total -= trueRange;
            // Last operation is to write the output. Must
            // be done after the trailing index have all been
            // taken care of because the caller is allowed
            // to have the input array to be also the output
            // array.
            outReal[outIdx] = 100.0 * (output / 7.0);
            // Increment indexes
            outIdx += 1;
            today += 1;
            trailingIdx1 += 1;
            trailingIdx2 += 1;
            trailingIdx3 += 1;
        }
        // All done. Indicate the output limits and return.
        (*outNBElement) = outIdx;
        (*outBegIdx) = startIdx;
        return RetCode::Success;
    }
    /// Unchecked variant of [`Core::ultosc`], used for internal cross-indicator calls.
    ///
    /// Skips parameter validation and uses unchecked indexing internally. Every argument must
    /// satisfy the constraints documented on [`Core::ultosc`]; an out-of-range parameter, an input
    /// slice not covering `startIdx..=endIdx`, or an undersized output slice may panic or cause
    /// undefined behavior. Prefer [`Core::ultosc`].
    #[inline]
    pub fn ultosc_unguarded(
        &self,
        mut startIdx: usize,
        endIdx: usize,
        inHigh: &[f64],
        inLow: &[f64],
        inClose: &[f64],
        mut optInTimePeriod1: i32,
        mut optInTimePeriod2: i32,
        mut optInTimePeriod3: i32,
        outBegIdx: &mut usize,
        outNBElement: &mut usize,
        outReal: &mut [f64],
    ) -> RetCode {
        let mut a1Total: f64 = 0.0_f64;
        let mut a2Total: f64 = 0.0_f64;
        let mut a3Total: f64 = 0.0_f64;
        let mut b1Total: f64 = 0.0_f64;
        let mut b2Total: f64 = 0.0_f64;
        let mut b3Total: f64 = 0.0_f64;
        let mut trueLow: f64 = 0.0_f64;
        let mut trueRange: f64 = 0.0_f64;
        let mut closeMinusTrueLow: f64 = 0.0_f64;
        let mut tempDouble: f64 = 0.0_f64;
        let mut output: f64 = 0.0_f64;
        let mut tempHT: f64 = 0.0_f64;
        let mut tempLT: f64 = 0.0_f64;
        let mut tempCY: f64 = 0.0_f64;
        let mut lookbackTotal: usize = 0_usize;
        let mut longestPeriod: usize = 0_usize;
        let mut longestIndex: usize = 0_usize;
        let mut i: usize = 0_usize;
        let mut j: usize = 0_usize;
        let mut today: usize = 0_usize;
        let mut outIdx: usize = 0_usize;
        let mut trailingIdx1: usize = 0_usize;
        let mut trailingIdx2: usize = 0_usize;
        let mut trailingIdx3: usize = 0_usize;
        let mut usedFlag: [i32; 3 as usize] = [0i32; 3 as usize];
        let mut periods: [i32; 3 as usize] = [0i32; 3 as usize];
        let mut sortedPeriods: [i32; 3 as usize] = [0i32; 3 as usize];
        unsafe {
        assert!(endIdx < inHigh.len());
        assert!(endIdx < inLow.len());
        assert!(endIdx < inClose.len());
        let _assertLb = self.ultosc_lookback(optInTimePeriod1, optInTimePeriod2, optInTimePeriod3);
        let _assertStart = if startIdx > _assertLb { startIdx } else { _assertLb };
        assert!(_assertStart > endIdx || endIdx - _assertStart < outReal.len());
        (*outBegIdx) = 0;
        (*outNBElement) = 0;
        *periods.as_mut_ptr().add(0) = optInTimePeriod1;
        *periods.as_mut_ptr().add(1) = optInTimePeriod2;
        *periods.as_mut_ptr().add(2) = optInTimePeriod3;
        *usedFlag.as_mut_ptr().add(0) = 0;
        *usedFlag.as_mut_ptr().add(1) = 0;
        *usedFlag.as_mut_ptr().add(2) = 0;
        // for( i = 0; i < 3; i += 1 )
        i = 0;
        while i < 3 {
            longestPeriod = 0;
            longestIndex = 0;
            // for( j = 0; j < 3; j += 1 )
            j = 0;
            while j < 3 {
                if *usedFlag.as_ptr().add(j) == 0 && (*periods.as_ptr().add(j)) as usize > longestPeriod {
                    longestPeriod = (*periods.as_ptr().add(j)) as usize;
                    longestIndex = j;
                }
                j += 1;
            }
            *usedFlag.as_mut_ptr().add(longestIndex) = 1;
            *sortedPeriods.as_mut_ptr().add(i) = (longestPeriod) as i32;
            i += 1;
        }
        optInTimePeriod1 = *sortedPeriods.as_ptr().add(2);
        optInTimePeriod2 = *sortedPeriods.as_ptr().add(1);
        optInTimePeriod3 = *sortedPeriods.as_ptr().add(0);
        lookbackTotal = self.ultosc_lookback(optInTimePeriod1, optInTimePeriod2, optInTimePeriod3);
        if startIdx < lookbackTotal {
            startIdx = lookbackTotal;
        }
        if startIdx > endIdx {
            return RetCode::Success;
        }
        a1Total = 0.0;
        b1Total = 0.0;
        // for( i = startIdx - (optInTimePeriod1) as usize + 1; i < startIdx; i += 1 )
        i = startIdx - (optInTimePeriod1) as usize + 1;
        while i < startIdx {
            tempLT = *inLow.as_ptr().add(i);
            tempHT = *inHigh.as_ptr().add(i);
            tempCY = *inClose.as_ptr().add(i - 1);
            trueLow = (tempLT).min(tempCY);
            closeMinusTrueLow = *inClose.as_ptr().add(i) - trueLow;
            trueRange = tempHT - tempLT;
            tempDouble = (tempCY - tempHT).abs();
            if tempDouble > trueRange {
                trueRange = tempDouble;
            }
            tempDouble = (tempCY - tempLT).abs();
            if tempDouble > trueRange {
                trueRange = tempDouble;
            }
            a1Total += closeMinusTrueLow;
            b1Total += trueRange;
            i += 1;
        }
        a2Total = 0.0;
        b2Total = 0.0;
        // for( i = startIdx - (optInTimePeriod2) as usize + 1; i < startIdx; i += 1 )
        i = startIdx - (optInTimePeriod2) as usize + 1;
        while i < startIdx {
            tempLT = *inLow.as_ptr().add(i);
            tempHT = *inHigh.as_ptr().add(i);
            tempCY = *inClose.as_ptr().add(i - 1);
            trueLow = (tempLT).min(tempCY);
            closeMinusTrueLow = *inClose.as_ptr().add(i) - trueLow;
            trueRange = tempHT - tempLT;
            tempDouble = (tempCY - tempHT).abs();
            if tempDouble > trueRange {
                trueRange = tempDouble;
            }
            tempDouble = (tempCY - tempLT).abs();
            if tempDouble > trueRange {
                trueRange = tempDouble;
            }
            a2Total += closeMinusTrueLow;
            b2Total += trueRange;
            i += 1;
        }
        a3Total = 0.0;
        b3Total = 0.0;
        // for( i = startIdx - (optInTimePeriod3) as usize + 1; i < startIdx; i += 1 )
        i = startIdx - (optInTimePeriod3) as usize + 1;
        while i < startIdx {
            tempLT = *inLow.as_ptr().add(i);
            tempHT = *inHigh.as_ptr().add(i);
            tempCY = *inClose.as_ptr().add(i - 1);
            trueLow = (tempLT).min(tempCY);
            closeMinusTrueLow = *inClose.as_ptr().add(i) - trueLow;
            trueRange = tempHT - tempLT;
            tempDouble = (tempCY - tempHT).abs();
            if tempDouble > trueRange {
                trueRange = tempDouble;
            }
            tempDouble = (tempCY - tempLT).abs();
            if tempDouble > trueRange {
                trueRange = tempDouble;
            }
            a3Total += closeMinusTrueLow;
            b3Total += trueRange;
            i += 1;
        }
        today = startIdx;
        outIdx = 0;
        trailingIdx1 = today - (optInTimePeriod1) as usize + 1;
        trailingIdx2 = today - (optInTimePeriod2) as usize + 1;
        trailingIdx3 = today - (optInTimePeriod3) as usize + 1;
        while today <= endIdx {
            tempLT = *inLow.as_ptr().add(today);
            tempHT = *inHigh.as_ptr().add(today);
            tempCY = *inClose.as_ptr().add(today - 1);
            trueLow = (tempLT).min(tempCY);
            closeMinusTrueLow = *inClose.as_ptr().add(today) - trueLow;
            trueRange = tempHT - tempLT;
            tempDouble = (tempCY - tempHT).abs();
            if tempDouble > trueRange {
                trueRange = tempDouble;
            }
            tempDouble = (tempCY - tempLT).abs();
            if tempDouble > trueRange {
                trueRange = tempDouble;
            }
            a1Total += closeMinusTrueLow;
            a2Total += closeMinusTrueLow;
            a3Total += closeMinusTrueLow;
            b1Total += trueRange;
            b2Total += trueRange;
            b3Total += trueRange;
            output = 0.0;
            if !((b1Total).abs() < 1e-14) {
                output += 4.0 * (a1Total / b1Total);
            }
            if !((b2Total).abs() < 1e-14) {
                output += 2.0 * (a2Total / b2Total);
            }
            if !((b3Total).abs() < 1e-14) {
                output += a3Total / b3Total;
            }
            tempLT = *inLow.as_ptr().add(trailingIdx1);
            tempHT = *inHigh.as_ptr().add(trailingIdx1);
            tempCY = *inClose.as_ptr().add(trailingIdx1 - 1);
            trueLow = (tempLT).min(tempCY);
            closeMinusTrueLow = *inClose.as_ptr().add(trailingIdx1) - trueLow;
            trueRange = tempHT - tempLT;
            tempDouble = (tempCY - tempHT).abs();
            if tempDouble > trueRange {
                trueRange = tempDouble;
            }
            tempDouble = (tempCY - tempLT).abs();
            if tempDouble > trueRange {
                trueRange = tempDouble;
            }
            a1Total -= closeMinusTrueLow;
            b1Total -= trueRange;
            tempLT = *inLow.as_ptr().add(trailingIdx2);
            tempHT = *inHigh.as_ptr().add(trailingIdx2);
            tempCY = *inClose.as_ptr().add(trailingIdx2 - 1);
            trueLow = (tempLT).min(tempCY);
            closeMinusTrueLow = *inClose.as_ptr().add(trailingIdx2) - trueLow;
            trueRange = tempHT - tempLT;
            tempDouble = (tempCY - tempHT).abs();
            if tempDouble > trueRange {
                trueRange = tempDouble;
            }
            tempDouble = (tempCY - tempLT).abs();
            if tempDouble > trueRange {
                trueRange = tempDouble;
            }
            a2Total -= closeMinusTrueLow;
            b2Total -= trueRange;
            tempLT = *inLow.as_ptr().add(trailingIdx3);
            tempHT = *inHigh.as_ptr().add(trailingIdx3);
            tempCY = *inClose.as_ptr().add(trailingIdx3 - 1);
            trueLow = (tempLT).min(tempCY);
            closeMinusTrueLow = *inClose.as_ptr().add(trailingIdx3) - trueLow;
            trueRange = tempHT - tempLT;
            tempDouble = (tempCY - tempHT).abs();
            if tempDouble > trueRange {
                trueRange = tempDouble;
            }
            tempDouble = (tempCY - tempLT).abs();
            if tempDouble > trueRange {
                trueRange = tempDouble;
            }
            a3Total -= closeMinusTrueLow;
            b3Total -= trueRange;
            *outReal.as_mut_ptr().add(outIdx) = 100.0 * (output / 7.0);
            outIdx += 1;
            today += 1;
            trailingIdx1 += 1;
            trailingIdx2 += 1;
            trailingIdx3 += 1;
        }
        (*outNBElement) = outIdx;
        (*outBegIdx) = startIdx;
        return RetCode::Success;
        } // unsafe
    }
}
/***************/
/* End of File */
/***************/
