/* 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
 *  -------------------------------------------------------------------
 *  MF       Mario Fortier
 *
 *
 * Change history:
 *
 *  MMDDYY BY   Description
 *  -------------------------------------------------------------------
 *  120802 MF   Template creation.
 *  052603 MF   Adapt code to compile with .NET Managed C++
 */

// 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::adosc`]: the number of leading input values consumed before the
    /// first output value can be produced.
    ///
    /// # Arguments
    ///
    /// * `optInFastPeriod` — Period of the fast A/D EMA (default 3, range 2..=100000)
    /// * `optInSlowPeriod` — Period of the slow A/D EMA (default 10, range 2..=100000)
    ///
    /// Returns `usize::MAX` when a parameter is out of range. Integer parameters accept `i32::MIN`
    /// to select their default value.
    #[inline]
    pub fn adosc_lookback(&self, mut optInFastPeriod: i32, mut optInSlowPeriod: i32) -> usize {
        if ((optInFastPeriod) as i32) == (i32::MIN) {
            optInFastPeriod = 3;
        } else if (((optInFastPeriod) as i32) < 2) || (((optInFastPeriod) as i32) > 100000) {
            return usize::MAX;
        }
        if ((optInSlowPeriod) as i32) == (i32::MIN) {
            optInSlowPeriod = 10;
        } else if (((optInSlowPeriod) as i32) < 2) || (((optInSlowPeriod) as i32) > 100000) {
            return usize::MAX;
        }
        let mut slowestPeriod: usize = 0_usize;
        // Use the slowest EMA period to evaluate the total lookback.
        if optInFastPeriod < optInSlowPeriod {
            slowestPeriod = (optInSlowPeriod) as usize;
        } else {
            slowestPeriod = (optInFastPeriod) as usize;
        }
        // Adjust startIdx to account for the lookback period.
        return self.ema_lookback((slowestPeriod) as i32);
    }
    /// Chaikin A/D Oscillator: the difference between a fast and a slow EMA of the
    /// Accumulation/Distribution line. Highlights momentum in accumulation/distribution volume
    /// flow. Positive/rising suggests accumulation; negative/falling suggests distribution.
    ///
    /// # Formula
    ///
    /// ```text
    /// ad += ((close-low)-(high-close))/(high-low) * volume   (only when high>low)
    /// fastEMA = fastk*ad + (1-fastk)*fastEMA,  fastk = 2/(optInFastPeriod+1)
    /// slowEMA = slowk*ad + (1-slowk)*slowEMA,  slowk = 2/(optInSlowPeriod+1)
    /// ADOSC = fastEMA - slowEMA
    /// ```
    ///
    /// # 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.
    /// * `inVolume` — Volume per bar.
    /// * `optInFastPeriod` — Period of the fast A/D EMA (default 3, range 2..=100000)
    /// * `optInSlowPeriod` — Period of the slow A/D EMA (default 10, range 2..=100000)
    /// * `outBegIdx` — Set to the input index of the first output value.
    /// * `outNBElement` — Set to the number of output values written.
    /// * `outReal` — Fast-EMA minus slow-EMA of the A/D line.
    ///
    /// 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 volume: Vec<f64> = (0..252).map(|i| 10_000.0 + 100.0 * i as f64).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.adosc(
    ///     0, high.len() - 1, &high, &low, &close, &volume, 3, 10,
    ///     &mut out_beg, &mut out_nb, &mut out,
    /// );
    /// assert_eq!(ret, RetCode::Success);
    /// assert!(out_nb > 0);
    /// ```
    ///
    /// # See also
    ///
    /// [`Core::ad`] · [`Core::ema`]
    ///
    /// # References
    ///
    /// * Marc Chaikin
    ///
    /// Further reading: [ta-lib.org/functions/adosc](https://ta-lib.org/functions/adosc/)
    #[doc(alias = "ChaikinADOscillator")]
    #[doc(alias = "ChaikinOscillator")]
    pub fn adosc(
        &self,
        startIdx: usize,
        endIdx: usize,
        inHigh: &[f64],
        inLow: &[f64],
        inClose: &[f64],
        inVolume: &[f64],
        mut optInFastPeriod: i32,
        mut optInSlowPeriod: i32,
        outBegIdx: &mut usize,
        outNBElement: &mut usize,
        outReal: &mut [f64],
    ) -> RetCode {
        if endIdx < startIdx {
            return RetCode::OutOfRangeStartIndex;
        }
        if ((optInFastPeriod) as i32) == (i32::MIN) {
            optInFastPeriod = 3;
        } else if (((optInFastPeriod) as i32) < 2) || (((optInFastPeriod) as i32) > 100000) {
            return RetCode::BadParam;
        }
        if ((optInSlowPeriod) as i32) == (i32::MIN) {
            optInSlowPeriod = 10;
        } else if (((optInSlowPeriod) as i32) < 2) || (((optInSlowPeriod) as i32) > 100000) {
            return RetCode::BadParam;
        }
        let mut startIdx = startIdx;
        let mut today: usize = 0_usize;
        let mut outIdx: usize = 0_usize;
        let mut lookbackTotal: usize = 0_usize;
        let mut slowestPeriod: usize = 0_usize;
        let mut high: f64 = 0.0_f64;
        let mut low: f64 = 0.0_f64;
        let mut close: f64 = 0.0_f64;
        let mut tmp: f64 = 0.0_f64;
        let mut slowEMA: f64 = 0.0_f64;
        let mut slowk: f64 = 0.0_f64;
        let mut one_minus_slowk: f64 = 0.0_f64;
        let mut fastEMA: f64 = 0.0_f64;
        let mut fastk: f64 = 0.0_f64;
        let mut one_minus_fastk: f64 = 0.0_f64;
        let mut ad: f64 = 0.0_f64;
        // Implementation Note:
        //     The fastEMA varaible is not neceseraly the
        //     fastest EMA.
        //     In the same way, slowEMA is not neceseraly the
        //     slowest EMA.
        //
        //     The ADOSC is always the (fastEMA - slowEMA) regardless
        //     of the period specified. In other word:
        //
        //     ADOSC(3,10) = EMA(3,AD) - EMA(10,AD)
        //
        //        while
        //
        //     ADOSC(10,3) = EMA(10,AD)- EMA(3,AD)
        //
        //     In the first case the EMA(3) is truly a faster EMA,
        //     while in the second case, the EMA(10) is still call
        //     fastEMA in the algorithm, even if it is in fact slower.
        //
        //     This gives more flexibility to the user if they want to
        //     experiment with unusual parameter settings.
        // Identify the slowest period.
        // This infomration is used soleley to bootstrap
        // the algorithm (skip the lookback period).
        if optInFastPeriod < optInSlowPeriod {
            slowestPeriod = (optInSlowPeriod) as usize;
        } else {
            slowestPeriod = (optInFastPeriod) as usize;
        }
        // Adjust startIdx to account for the lookback period.
        lookbackTotal = self.ema_lookback((slowestPeriod) as i32);
        if startIdx < lookbackTotal {
            startIdx = lookbackTotal;
        }
        // Make sure there is still something to evaluate.
        if startIdx > endIdx {
            (*outBegIdx) = 0;
            (*outNBElement) = 0;
            return RetCode::Success;
        }
        (*outBegIdx) = startIdx;
        today = startIdx - lookbackTotal;
        // The following variables are used to
        // calculate the "ad".
        ad = 0.0;
        // Constants for EMA
        fastk = 2.0 / ((optInFastPeriod as f64) + 1.0);
        one_minus_fastk = 1.0 - fastk;
        slowk = 2.0 / ((optInSlowPeriod as f64) + 1.0);
        one_minus_slowk = 1.0 - slowk;
        // Initialize the two EMA
        //
        // Use the same range of initialization inputs for
        // both EMA and simply seed with the first A/D value.
        //
        // Note: Metastock do the same.
        high = inHigh[today];
        low = inLow[today];
        tmp = high - low;
        close = inClose[today];
        if tmp > 0.0 {
            ad += (close - low - (high - close)) / tmp * (inVolume[today] as f64);
        }
        today += 1;
        fastEMA = ad;
        slowEMA = ad;
        // Initialize the EMA and skip the unstable period.
        while today < startIdx {
            high = inHigh[today];
            low = inLow[today];
            tmp = high - low;
            close = inClose[today];
            if tmp > 0.0 {
                ad += (close - low - (high - close)) / tmp * (inVolume[today] as f64);
            }
            today += 1;
            fastEMA = (fastk as f64).mul_add(ad, one_minus_fastk * fastEMA);
            slowEMA = (slowk as f64).mul_add(ad, one_minus_slowk * slowEMA);
        }
        // Perform the calculation for the requested range
        outIdx = 0;
        while today <= endIdx {
            high = inHigh[today];
            low = inLow[today];
            tmp = high - low;
            close = inClose[today];
            if tmp > 0.0 {
                ad += (close - low - (high - close)) / tmp * (inVolume[today] as f64);
            }
            today += 1;
            fastEMA = (fastk as f64).mul_add(ad, one_minus_fastk * fastEMA);
            slowEMA = (slowk as f64).mul_add(ad, one_minus_slowk * slowEMA);
            outReal[outIdx] = fastEMA - slowEMA;
            outIdx += 1;
        }
        (*outNBElement) = outIdx;
        return RetCode::Success;
    }
    /// Unchecked variant of [`Core::adosc`], used for internal cross-indicator calls.
    ///
    /// Skips parameter validation and uses unchecked indexing internally. Every argument must
    /// satisfy the constraints documented on [`Core::adosc`]; 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::adosc`].
    #[inline]
    pub fn adosc_unguarded(
        &self,
        mut startIdx: usize,
        endIdx: usize,
        inHigh: &[f64],
        inLow: &[f64],
        inClose: &[f64],
        inVolume: &[f64],
        mut optInFastPeriod: i32,
        mut optInSlowPeriod: i32,
        outBegIdx: &mut usize,
        outNBElement: &mut usize,
        outReal: &mut [f64],
    ) -> RetCode {
        let mut today: usize = 0_usize;
        let mut outIdx: usize = 0_usize;
        let mut lookbackTotal: usize = 0_usize;
        let mut slowestPeriod: usize = 0_usize;
        let mut high: f64 = 0.0_f64;
        let mut low: f64 = 0.0_f64;
        let mut close: f64 = 0.0_f64;
        let mut tmp: f64 = 0.0_f64;
        let mut slowEMA: f64 = 0.0_f64;
        let mut slowk: f64 = 0.0_f64;
        let mut one_minus_slowk: f64 = 0.0_f64;
        let mut fastEMA: f64 = 0.0_f64;
        let mut fastk: f64 = 0.0_f64;
        let mut one_minus_fastk: f64 = 0.0_f64;
        let mut ad: f64 = 0.0_f64;
        unsafe {
        assert!(endIdx < inHigh.len());
        assert!(endIdx < inLow.len());
        assert!(endIdx < inClose.len());
        assert!(endIdx < inVolume.len());
        let _assertLb = self.adosc_lookback(optInFastPeriod, optInSlowPeriod);
        let _assertStart = if startIdx > _assertLb { startIdx } else { _assertLb };
        assert!(_assertStart > endIdx || endIdx - _assertStart < outReal.len());
        if optInFastPeriod < optInSlowPeriod {
            slowestPeriod = (optInSlowPeriod) as usize;
        } else {
            slowestPeriod = (optInFastPeriod) as usize;
        }
        lookbackTotal = self.ema_lookback((slowestPeriod) as i32);
        if startIdx < lookbackTotal {
            startIdx = lookbackTotal;
        }
        if startIdx > endIdx {
            (*outBegIdx) = 0;
            (*outNBElement) = 0;
            return RetCode::Success;
        }
        (*outBegIdx) = startIdx;
        today = startIdx - lookbackTotal;
        ad = 0.0;
        fastk = 2.0 / ((optInFastPeriod as f64) + 1.0);
        one_minus_fastk = 1.0 - fastk;
        slowk = 2.0 / ((optInSlowPeriod as f64) + 1.0);
        one_minus_slowk = 1.0 - slowk;
        high = *inHigh.as_ptr().add(today);
        low = *inLow.as_ptr().add(today);
        tmp = high - low;
        close = *inClose.as_ptr().add(today);
        if tmp > 0.0 {
            ad += (close - low - (high - close)) / tmp * (*inVolume.as_ptr().add(today) as f64);
        }
        today += 1;
        fastEMA = ad;
        slowEMA = ad;
        while today < startIdx {
            high = *inHigh.as_ptr().add(today);
            low = *inLow.as_ptr().add(today);
            tmp = high - low;
            close = *inClose.as_ptr().add(today);
            if tmp > 0.0 {
                ad += (close - low - (high - close)) / tmp * (*inVolume.as_ptr().add(today) as f64);
            }
            today += 1;
            fastEMA = (fastk as f64).mul_add(ad, one_minus_fastk * fastEMA);
            slowEMA = (slowk as f64).mul_add(ad, one_minus_slowk * slowEMA);
        }
        outIdx = 0;
        while today <= endIdx {
            high = *inHigh.as_ptr().add(today);
            low = *inLow.as_ptr().add(today);
            tmp = high - low;
            close = *inClose.as_ptr().add(today);
            if tmp > 0.0 {
                ad += (close - low - (high - close)) / tmp * (*inVolume.as_ptr().add(today) as f64);
            }
            today += 1;
            fastEMA = (fastk as f64).mul_add(ad, one_minus_fastk * fastEMA);
            slowEMA = (slowk as f64).mul_add(ad, one_minus_slowk * slowEMA);
            *outReal.as_mut_ptr().add(outIdx) = fastEMA - slowEMA;
            outIdx += 1;
        }
        (*outNBElement) = outIdx;
        return RetCode::Success;
        } // unsafe
    }
}
/***************/
/* End of File */
/***************/
