/* 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
 *  -------------------------------------------------------------------
 *  010102 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::dema`]: the number of leading input values consumed before the
    /// first output value can be produced.
    ///
    /// # Arguments
    ///
    /// * `optInTimePeriod` — Smoothing period for both EMA passes (default 30, 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 dema_lookback(&self, mut optInTimePeriod: i32) -> usize {
        if ((optInTimePeriod) as i32) == (i32::MIN) {
            optInTimePeriod = 30;
        } else if (((optInTimePeriod) as i32) < 1) || (((optInTimePeriod) as i32) > 100000) {
            return usize::MAX;
        }
        // Get lookback for one EMA.
        // Multiply by two (because double smoothing).
        return (self.ema_lookback(optInTimePeriod) * 2) as usize;
    }
    /// Double Exponential Moving Average: an EMA combined with an EMA-of-EMA to reduce lag versus a
    /// plain EMA. Overlap Studies overlay on price.
    ///
    /// # Formula
    ///
    /// ```text
    /// EMA1 = EMA(inReal, period); EMA2 = EMA(EMA1, period); DEMA = 2*EMA1 - EMA2
    /// ```
    ///
    /// # Notes
    ///
    /// * A period of 1 performs no smoothing: the output is a copy of the input. Allowed since
    ///   0.6.5 (issues #48/#59).
    ///
    /// # Arguments
    ///
    /// * `startIdx` — Start index of the requested calculation range.
    /// * `endIdx` — End index of the requested calculation range (inclusive).
    /// * `inReal` — Source series (typically price)
    /// * `optInTimePeriod` — Smoothing period for both EMA passes (default 30, range 1..=100000)
    /// * `outBegIdx` — Set to the input index of the first output value.
    /// * `outNBElement` — Set to the number of output values written.
    /// * `outReal` — DEMA 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 data: 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.dema(0, data.len() - 1, &data, 30, &mut out_beg, &mut out_nb, &mut out);
    /// assert_eq!(ret, RetCode::Success);
    /// assert!(out_nb > 0);
    /// ```
    ///
    /// # See also
    ///
    /// [`Core::ema`] · [`Core::tema`] · [`Core::ma`]
    ///
    /// Further reading: [ta-lib.org/functions/dema](https://ta-lib.org/functions/dema/)
    #[doc(alias = "DoubleExponentialMovingAverage")]
    pub fn dema(
        &self,
        startIdx: usize,
        endIdx: usize,
        inReal: &[f64],
        mut optInTimePeriod: i32,
        outBegIdx: &mut usize,
        outNBElement: &mut usize,
        outReal: &mut [f64],
    ) -> RetCode {
        if endIdx < startIdx {
            return RetCode::OutOfRangeStartIndex;
        }
        if ((optInTimePeriod) as i32) == (i32::MIN) {
            optInTimePeriod = 30;
        } else if (((optInTimePeriod) as i32) < 1) || (((optInTimePeriod) as i32) > 100000) {
            return RetCode::BadParam;
        }
        let mut startIdx = startIdx;
        let mut firstEMA: Vec<f64> = Vec::new();
        let mut secondEMA: Vec<f64> = Vec::new();
        let mut firstEMABegIdx: usize = 0_usize;
        let mut firstEMANbElement: usize = 0_usize;
        let mut secondEMABegIdx: usize = 0_usize;
        let mut secondEMANbElement: usize = 0_usize;
        let mut tempInt: usize = 0_usize;
        let mut outIdx: usize = 0_usize;
        let mut firstEMAIdx: usize = 0_usize;
        let mut lookbackTotal: usize = 0_usize;
        let mut lookbackEMA: usize = 0_usize;
        let mut retCode: RetCode = RetCode::Success;
        // For an explanation of this function, please read
        //
        // Stocks & Commodities V. 12:1 (11-19):
        //   Smoothing Data With Faster Moving Averages
        // Stocks & Commodities V. 12:2 (72-80):
        //   Smoothing Data With Less Lag
        //
        // Both magazine articles written by Patrick G. Mulloy
        //
        // Essentially, a DEMA of time serie 't' is:
        //   EMA2 = EMA(EMA(t,period),period)
        //   DEMA = 2*EMA(t,period)- EMA2
        //
        // DEMA offers a moving average with less lags then the
        // traditional EMA.
        //
        // Do not confuse a DEMA with the EMA2. Both are called
        // "Double EMA" in the litterature, but EMA2 is a simple
        // EMA of an EMA, while DEMA is a compostie of a single
        // EMA with EMA2.
        //
        // TEMA is very similar (and from the same author).
        // Will change only on success.
        (*outNBElement) = 0;
        (*outBegIdx) = 0;
        // Adjust startIdx to account for the lookback period.
        lookbackEMA = self.ema_lookback(optInTimePeriod);
        lookbackTotal = lookbackEMA * 2;
        if startIdx < lookbackTotal {
            startIdx = lookbackTotal;
        }
        // Make sure there is still something to evaluate.
        if startIdx > endIdx {
            return RetCode::Success;
        }
        // Allocate a temporary buffer for the firstEMA.
        //
        // When possible, re-use the outputBuffer for temp
        // calculation.
        if inReal.as_ptr() == outReal.as_ptr() {
            firstEMA = outReal.to_vec();
        } else {
            tempInt = lookbackTotal + (endIdx - startIdx) + 1;
            firstEMA = vec![0.0_f64; (tempInt * 1) as usize];
        }
        // Calculate the first EMA
        retCode = self.ema_unguarded(startIdx - lookbackEMA, endIdx, inReal, optInTimePeriod, &mut firstEMABegIdx, &mut firstEMANbElement, &mut firstEMA[..]);
        // Verify for failure or if not enough data after
        // calculating the first EMA.
        if retCode != RetCode::Success || firstEMANbElement == 0 {
            if firstEMA.as_ptr() != outReal.as_ptr() {
            }
            return retCode;
        }
        // Allocate a temporary buffer for storing the EMA of the EMA.
        secondEMA = vec![0.0_f64; (firstEMANbElement * 1) as usize];
        retCode = self.ema_unguarded(0, firstEMANbElement - 1, &firstEMA, optInTimePeriod, &mut secondEMABegIdx, &mut secondEMANbElement, &mut secondEMA[..]);
        // Return empty output on failure or if not enough data after
        // calculating the second EMA.
        if retCode != RetCode::Success || secondEMANbElement == 0 {
            if firstEMA.as_ptr() != outReal.as_ptr() {
            }
            return retCode;
        }
        // Iterate through the second EMA and write the DEMA into
        // the output.
        firstEMAIdx = secondEMABegIdx;
        outIdx = 0;
        while outIdx < secondEMANbElement {
            outReal[outIdx] = ((2.0 * firstEMA[{ let _v = firstEMAIdx; firstEMAIdx += 1; _v }] - secondEMA[outIdx]) as f64);
            outIdx += 1;
        }
        if firstEMA.as_ptr() != outReal.as_ptr() {
        }
        // Succeed. Indicate where the output starts relative to
        // the caller input.
        (*outBegIdx) = firstEMABegIdx + secondEMABegIdx;
        (*outNBElement) = outIdx;
        return RetCode::Success;
    }
    /// Unchecked variant of [`Core::dema`], used for internal cross-indicator calls.
    ///
    /// Skips parameter validation and uses unchecked indexing internally. Every argument must
    /// satisfy the constraints documented on [`Core::dema`]; 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::dema`].
    #[inline]
    pub fn dema_unguarded(
        &self,
        mut startIdx: usize,
        endIdx: usize,
        inReal: &[f64],
        mut optInTimePeriod: i32,
        outBegIdx: &mut usize,
        outNBElement: &mut usize,
        outReal: &mut [f64],
    ) -> RetCode {
        let mut firstEMA: Vec<f64> = Vec::new();
        let mut secondEMA: Vec<f64> = Vec::new();
        let mut firstEMABegIdx: usize = 0_usize;
        let mut firstEMANbElement: usize = 0_usize;
        let mut secondEMABegIdx: usize = 0_usize;
        let mut secondEMANbElement: usize = 0_usize;
        let mut tempInt: usize = 0_usize;
        let mut outIdx: usize = 0_usize;
        let mut firstEMAIdx: usize = 0_usize;
        let mut lookbackTotal: usize = 0_usize;
        let mut lookbackEMA: usize = 0_usize;
        let mut retCode: RetCode = RetCode::Success;
        unsafe {
        assert!(endIdx < inReal.len());
        let _assertLb = self.dema_lookback(optInTimePeriod);
        let _assertStart = if startIdx > _assertLb { startIdx } else { _assertLb };
        assert!(_assertStart > endIdx || endIdx - _assertStart < outReal.len());
        (*outNBElement) = 0;
        (*outBegIdx) = 0;
        lookbackEMA = self.ema_lookback(optInTimePeriod);
        lookbackTotal = lookbackEMA * 2;
        if startIdx < lookbackTotal {
            startIdx = lookbackTotal;
        }
        if startIdx > endIdx {
            return RetCode::Success;
        }
        if inReal.as_ptr() == outReal.as_ptr() {
            firstEMA = outReal.to_vec();
        } else {
            tempInt = lookbackTotal + (endIdx - startIdx) + 1;
            firstEMA = vec![0.0_f64; (tempInt * 1) as usize];
        }
        retCode = self.ema_unguarded(startIdx - lookbackEMA, endIdx, inReal, optInTimePeriod, &mut firstEMABegIdx, &mut firstEMANbElement, &mut firstEMA[..]);
        if retCode != RetCode::Success || firstEMANbElement == 0 {
            if firstEMA.as_ptr() != outReal.as_ptr() {
            }
            return retCode;
        }
        secondEMA = vec![0.0_f64; (firstEMANbElement * 1) as usize];
        retCode = self.ema_unguarded(0, firstEMANbElement - 1, &firstEMA, optInTimePeriod, &mut secondEMABegIdx, &mut secondEMANbElement, &mut secondEMA[..]);
        if retCode != RetCode::Success || secondEMANbElement == 0 {
            if firstEMA.as_ptr() != outReal.as_ptr() {
            }
            return retCode;
        }
        firstEMAIdx = secondEMABegIdx;
        outIdx = 0;
        while outIdx < secondEMANbElement {
            *outReal.as_mut_ptr().add(outIdx) = ((2.0 * *firstEMA.as_ptr().add({ let _v = firstEMAIdx; firstEMAIdx += 1; _v }) - *secondEMA.as_ptr().add(outIdx)) as f64);
            outIdx += 1;
        }
        if firstEMA.as_ptr() != outReal.as_ptr() {
        }
        (*outBegIdx) = firstEMABegIdx + secondEMABegIdx;
        (*outNBElement) = outIdx;
        return RetCode::Success;
        } // unsafe
    }
}
/***************/
/* End of File */
/***************/
