/* 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++
 *  062704 MF     Fix limit case to avoid divid by zero (or by
 *                a value close to zero induce by the imprecision
 *                of floating points).
 *  070226 MF,CC  Allow period of 1: output is a copy of the input,
 *                consistent with TA_MA (issues #48, #59). The natural
 *                KAMA math at period=1 would be a fixed-alpha EMA
 *                (efficiency ratio is always 1), which would disagree
 *                with TA_MA's period-1 copy, so identity is explicit.
 */

// 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::kama`]: the number of leading input values consumed before the
    /// first output value can be produced.
    ///
    /// # Arguments
    ///
    /// * `optInTimePeriod` — Lookback window for the efficiency ratio (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 kama_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;
        }
        if optInTimePeriod == 1 {
            return (self.unstable_period[FuncUnstId::Kama as usize]) as usize;
        }
        return (optInTimePeriod + self.unstable_period[FuncUnstId::Kama as usize]) as usize;
    }
    /// Kaufman Adaptive Moving Average: an EMA whose smoothing factor adapts each bar to an
    /// efficiency ratio (directional move vs. total volatility). Reacts fast in trends and smooths
    /// in ranging markets. Flat KAMA = non-trending/ranging market. KAMA tracking price closely =
    /// efficient trend.
    ///
    /// # Formula
    ///
    /// ```text
    /// ER = |price[t] - price[t-period]| / sum(|price[i]-price[i-1]|, last period bars)
    /// SC = (ER*(2/3 - 2/31) + 2/31)^2
    /// KAMA[t] = KAMA[t-1] + SC*(price[t] - KAMA[t-1])
    /// ```
    ///
    /// # Notes
    ///
    /// * A period of 1 performs no smoothing: the output is a copy of the input, consistent with
    ///   `MA(period=1)` for every MAType. (The natural KAMA math at period 1 would degenerate to a
    ///   fixed-alpha EMA because the efficiency ratio is always 1, so the copy is made explicit.)
    ///   Allowed since 0.6.5.
    ///
    /// # Arguments
    ///
    /// * `startIdx` — Start index of the requested calculation range.
    /// * `endIdx` — End index of the requested calculation range (inclusive).
    /// * `inReal` — Source price series.
    /// * `optInTimePeriod` — Lookback window for the efficiency ratio (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` — Adaptive moving average 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.kama(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::mama`] · [`Core::ema`] · [`Core::ma`]
    ///
    /// Further reading: [ta-lib.org/functions/kama](https://ta-lib.org/functions/kama/)
    #[doc(alias = "KaufmanAdaptiveMovingAverage")]
    #[doc(alias = "KaufmansAdaptiveMovingAverage")]
    pub fn kama(
        &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 constMax: f64 = 0.0_f64;
        let mut constDiff: f64 = 0.0_f64;
        let mut tempReal: f64 = 0.0_f64;
        let mut tempReal2: f64 = 0.0_f64;
        let mut sumROC1: f64 = 0.0_f64;
        let mut periodROC: f64 = 0.0_f64;
        let mut prevKAMA: f64 = 0.0_f64;
        let mut i: usize = 0_usize;
        let mut today: usize = 0_usize;
        let mut outIdx: usize = 0_usize;
        let mut lookbackTotal: usize = 0_usize;
        let mut trailingIdx: usize = 0_usize;
        let mut trailingValue: f64 = 0.0_f64;
        constMax = 2.0 / (30.0 + 1.0);
        constDiff = 2.0 / (2.0 + 1.0) - constMax;
        // Default return values
        (*outBegIdx) = 0;
        (*outNBElement) = 0;
        // No smoothing at period of 1: the output is a copy of the input
        // (same convention as TA_MA for every MAType). The unstable period
        // still delays the first output for API consistency.
        if optInTimePeriod == 1 {
            lookbackTotal = (self.unstable_period[FuncUnstId::Kama as usize]) as usize;
            if startIdx < lookbackTotal {
                startIdx = lookbackTotal;
            }
            if startIdx > endIdx {
                return RetCode::Success;
            }
            (*outBegIdx) = startIdx;
            outIdx = 0;
            today = startIdx;
            while today <= endIdx {
                outReal[outIdx] = ((inReal[{ let _v = today; today += 1; _v }]) as f64);
                outIdx += 1;
            }
            (*outNBElement) = outIdx;
            return RetCode::Success;
        }
        // Identify the minimum number of price bar needed
        // to calculate at least one output.
        lookbackTotal = (optInTimePeriod + self.unstable_period[FuncUnstId::Kama as usize]) as usize;
        // Move up the start index if there is not
        // enough initial data.
        if startIdx < lookbackTotal {
            startIdx = lookbackTotal;
        }
        // Make sure there is still something to evaluate.
        if startIdx > endIdx {
            (*outBegIdx) = 0;
            (*outNBElement) = 0;
            return RetCode::Success;
        }
        // Initialize the variables by going through
        // the lookback period.
        sumROC1 = 0.0;
        today = startIdx - lookbackTotal;
        trailingIdx = today;
        i = (optInTimePeriod) as usize;
        while { let _v = i; i = i.wrapping_sub(1); _v } > 0 {
            tempReal = inReal[{ let _v = today; today += 1; _v }];
            tempReal -= inReal[today];
            sumROC1 += (tempReal).abs();
        }
        // At this point sumROC1 represent the
        // summation of the 1-day price difference
        // over the (optInTimePeriod-1)
        // Calculate the first KAMA
        // The yesterday price is used here as the previous KAMA.
        prevKAMA = inReal[today - 1];
        tempReal = inReal[today];
        tempReal2 = inReal[{ let _v = trailingIdx; trailingIdx += 1; _v }];
        periodROC = tempReal - tempReal2;
        // Save the trailing value. Do this because inReal
        // and outReal can be pointers to the same buffer.
        trailingValue = tempReal2;
        // Calculate the efficiency ratio
        if sumROC1 <= periodROC || (sumROC1).abs() < 1e-14 {
            tempReal = 1.0;
        } else {
            tempReal = (periodROC / sumROC1).abs();
        }
        // Calculate the smoothing constant
        tempReal = (tempReal as f64).mul_add(constDiff, constMax);
        tempReal *= tempReal;
        // Calculate the KAMA like an EMA, using the
        // smoothing constant as the adaptive factor.
        prevKAMA = (inReal[{ let _v = today; today += 1; _v }] - prevKAMA as f64).mul_add(tempReal, prevKAMA);
        // 'today' keep track of where the processing is within the
        // input.
        // Skip the unstable period. Do the whole processing
        // needed for KAMA, but do not write it in the output.
        while today <= startIdx {
            tempReal = inReal[today];
            tempReal2 = inReal[{ let _v = trailingIdx; trailingIdx += 1; _v }];
            periodROC = tempReal - tempReal2;
            // Adjust sumROC1:
            //  - Remove trailing ROC1
            //  - Add new ROC1
            sumROC1 -= (trailingValue - tempReal2).abs();
            sumROC1 += (tempReal - inReal[today - 1]).abs();
            // Save the trailing value. Do this because inReal
            // and outReal can be pointers to the same buffer.
            trailingValue = tempReal2;
            // Calculate the efficiency ratio
            if sumROC1 <= periodROC || (sumROC1).abs() < 1e-14 {
                tempReal = 1.0;
            } else {
                tempReal = (periodROC / sumROC1).abs();
            }
            // Calculate the smoothing constant
            tempReal = (tempReal as f64).mul_add(constDiff, constMax);
            tempReal *= tempReal;
            // Calculate the KAMA like an EMA, using the
            // smoothing constant as the adaptive factor.
            prevKAMA = (inReal[{ let _v = today; today += 1; _v }] - prevKAMA as f64).mul_add(tempReal, prevKAMA);
        }
        // Write the first value.
        outReal[0] = prevKAMA;
        outIdx = 1;
        (*outBegIdx) = today - 1;
        // Do the KAMA calculation for the requested range.
        while today <= endIdx {
            tempReal = inReal[today];
            tempReal2 = inReal[{ let _v = trailingIdx; trailingIdx += 1; _v }];
            periodROC = tempReal - tempReal2;
            // Adjust sumROC1:
            //  - Remove trailing ROC1
            //  - Add new ROC1
            sumROC1 -= (trailingValue - tempReal2).abs();
            sumROC1 += (tempReal - inReal[today - 1]).abs();
            // Save the trailing value. Do this because inReal
            // and outReal can be pointers to the same buffer.
            trailingValue = tempReal2;
            // Calculate the efficiency ratio
            if sumROC1 <= periodROC || (sumROC1).abs() < 1e-14 {
                tempReal = 1.0;
            } else {
                tempReal = (periodROC / sumROC1).abs();
            }
            // Calculate the smoothing constant
            tempReal = (tempReal as f64).mul_add(constDiff, constMax);
            tempReal *= tempReal;
            // Calculate the KAMA like an EMA, using the
            // smoothing constant as the adaptive factor.
            prevKAMA = (inReal[{ let _v = today; today += 1; _v }] - prevKAMA as f64).mul_add(tempReal, prevKAMA);
            outReal[outIdx] = prevKAMA;
            outIdx += 1;
        }
        (*outNBElement) = outIdx;
        return RetCode::Success;
    }
    /// Unchecked variant of [`Core::kama`], used for internal cross-indicator calls.
    ///
    /// Skips parameter validation and uses unchecked indexing internally. Every argument must
    /// satisfy the constraints documented on [`Core::kama`]; 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::kama`].
    #[inline]
    pub fn kama_unguarded(
        &self,
        mut startIdx: usize,
        endIdx: usize,
        inReal: &[f64],
        mut optInTimePeriod: i32,
        outBegIdx: &mut usize,
        outNBElement: &mut usize,
        outReal: &mut [f64],
    ) -> RetCode {
        let mut constMax: f64 = 0.0_f64;
        let mut constDiff: f64 = 0.0_f64;
        let mut tempReal: f64 = 0.0_f64;
        let mut tempReal2: f64 = 0.0_f64;
        let mut sumROC1: f64 = 0.0_f64;
        let mut periodROC: f64 = 0.0_f64;
        let mut prevKAMA: f64 = 0.0_f64;
        let mut i: usize = 0_usize;
        let mut today: usize = 0_usize;
        let mut outIdx: usize = 0_usize;
        let mut lookbackTotal: usize = 0_usize;
        let mut trailingIdx: usize = 0_usize;
        let mut trailingValue: f64 = 0.0_f64;
        unsafe {
        assert!(endIdx < inReal.len());
        let _assertLb = self.kama_lookback(optInTimePeriod);
        let _assertStart = if startIdx > _assertLb { startIdx } else { _assertLb };
        assert!(_assertStart > endIdx || endIdx - _assertStart < outReal.len());
        constMax = 2.0 / (30.0 + 1.0);
        constDiff = 2.0 / (2.0 + 1.0) - constMax;
        (*outBegIdx) = 0;
        (*outNBElement) = 0;
        if optInTimePeriod == 1 {
            lookbackTotal = (self.unstable_period[FuncUnstId::Kama as usize]) as usize;
            if startIdx < lookbackTotal {
                startIdx = lookbackTotal;
            }
            if startIdx > endIdx {
                return RetCode::Success;
            }
            (*outBegIdx) = startIdx;
            outIdx = 0;
            today = startIdx;
            while today <= endIdx {
                *outReal.as_mut_ptr().add(outIdx) = ((*inReal.as_ptr().add({ let _v = today; today += 1; _v })) as f64);
                outIdx += 1;
            }
            (*outNBElement) = outIdx;
            return RetCode::Success;
        }
        lookbackTotal = (optInTimePeriod + self.unstable_period[FuncUnstId::Kama as usize]) as usize;
        if startIdx < lookbackTotal {
            startIdx = lookbackTotal;
        }
        if startIdx > endIdx {
            (*outBegIdx) = 0;
            (*outNBElement) = 0;
            return RetCode::Success;
        }
        sumROC1 = 0.0;
        today = startIdx - lookbackTotal;
        trailingIdx = today;
        i = (optInTimePeriod) as usize;
        while { let _v = i; i = i.wrapping_sub(1); _v } > 0 {
            tempReal = *inReal.as_ptr().add({ let _v = today; today += 1; _v });
            tempReal -= *inReal.as_ptr().add(today);
            sumROC1 += (tempReal).abs();
        }
        prevKAMA = *inReal.as_ptr().add(today - 1);
        tempReal = *inReal.as_ptr().add(today);
        tempReal2 = *inReal.as_ptr().add({ let _v = trailingIdx; trailingIdx += 1; _v });
        periodROC = tempReal - tempReal2;
        trailingValue = tempReal2;
        if sumROC1 <= periodROC || (sumROC1).abs() < 1e-14 {
            tempReal = 1.0;
        } else {
            tempReal = (periodROC / sumROC1).abs();
        }
        tempReal = (tempReal as f64).mul_add(constDiff, constMax);
        tempReal *= tempReal;
        prevKAMA = (*inReal.as_ptr().add({ let _v = today; today += 1; _v }) - prevKAMA as f64).mul_add(tempReal, prevKAMA);
        while today <= startIdx {
            tempReal = *inReal.as_ptr().add(today);
            tempReal2 = *inReal.as_ptr().add({ let _v = trailingIdx; trailingIdx += 1; _v });
            periodROC = tempReal - tempReal2;
            sumROC1 -= (trailingValue - tempReal2).abs();
            sumROC1 += (tempReal - *inReal.as_ptr().add(today - 1)).abs();
            trailingValue = tempReal2;
            if sumROC1 <= periodROC || (sumROC1).abs() < 1e-14 {
                tempReal = 1.0;
            } else {
                tempReal = (periodROC / sumROC1).abs();
            }
            tempReal = (tempReal as f64).mul_add(constDiff, constMax);
            tempReal *= tempReal;
            prevKAMA = (*inReal.as_ptr().add({ let _v = today; today += 1; _v }) - prevKAMA as f64).mul_add(tempReal, prevKAMA);
        }
        *outReal.as_mut_ptr().add(0) = prevKAMA;
        outIdx = 1;
        (*outBegIdx) = today - 1;
        while today <= endIdx {
            tempReal = *inReal.as_ptr().add(today);
            tempReal2 = *inReal.as_ptr().add({ let _v = trailingIdx; trailingIdx += 1; _v });
            periodROC = tempReal - tempReal2;
            sumROC1 -= (trailingValue - tempReal2).abs();
            sumROC1 += (tempReal - *inReal.as_ptr().add(today - 1)).abs();
            trailingValue = tempReal2;
            if sumROC1 <= periodROC || (sumROC1).abs() < 1e-14 {
                tempReal = 1.0;
            } else {
                tempReal = (periodROC / sumROC1).abs();
            }
            tempReal = (tempReal as f64).mul_add(constDiff, constMax);
            tempReal *= tempReal;
            prevKAMA = (*inReal.as_ptr().add({ let _v = today; today += 1; _v }) - prevKAMA as f64).mul_add(tempReal, prevKAMA);
            *outReal.as_mut_ptr().add(outIdx) = prevKAMA;
            outIdx += 1;
        }
        (*outNBElement) = outIdx;
        return RetCode::Success;
        } // unsafe
    }
}
/***************/
/* End of File */
/***************/
