/* 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
 *  CR       Chris (crokusek@hotmail.com)
 *
 * Change history:
 *
 *  MMDDYY BY   Description
 *  -------------------------------------------------------------------
 *  010503 MF   Initial Coding
 *  031703 MF   Fix #701060. Correct logic when using a range with
 *              startIdx/endIdx. Thanks to Chris for reporting this.
 *  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::trima`]: the number of leading input values consumed before the
    /// first output value can be produced.
    ///
    /// # Arguments
    ///
    /// * `optInTimePeriod` — Number of bars in the averaging window (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 trima_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;
        }
        return (optInTimePeriod - 1) as usize;
    }
    /// Triangular Moving Average: a double-smoothed moving average that weights prices toward the
    /// middle of the window most heavily. Equivalent to an SMA of an SMA, computed here via an
    /// incremental triangular-weighted running numerator.
    ///
    /// # Formula
    ///
    /// ```text
    /// Weights rise then fall (4-period: (1a+2b+2c+1d)/6; 5-period: (1a+2b+3c+2d+1e)/9). With n = period>>1: odd divides by (n+1)^2, even by n(n+1). Equivalent to odd: SMA(SMA(x,(period+1)/2),(period+1)/2); even: SMA(SMA(x,period/2),period/2+1).
    /// ```
    ///
    /// # Notes
    ///
    /// * Follows the generally accepted (Metastock) definition rather than the TradeStation
    ///   variant.
    /// * 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 price series.
    /// * `optInTimePeriod` — Number of bars in the averaging window (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` — Triangular moving average.
    ///
    /// 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.trima(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::sma`] · [`Core::wma`] · [`Core::ma`]
    ///
    /// Further reading: [ta-lib.org/functions/trima](https://ta-lib.org/functions/trima/)
    #[doc(alias = "TriangularMovingAverage")]
    pub fn trima(
        &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 lookbackTotal: usize = 0_usize;
        let mut numerator: f64 = 0.0_f64;
        let mut numeratorSub: f64 = 0.0_f64;
        let mut numeratorAdd: f64 = 0.0_f64;
        let mut i: usize = 0_usize;
        let mut outIdx: usize = 0_usize;
        let mut todayIdx: usize = 0_usize;
        let mut trailingIdx: usize = 0_usize;
        let mut middleIdx: usize = 0_usize;
        let mut factor: f64 = 0.0_f64;
        let mut tempReal: f64 = 0.0_f64;
        // Identify the minimum number of price bar needed
        // to calculate at least one output.
        lookbackTotal = (optInTimePeriod - 1) 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;
        }
        // TRIMA Description
        // =================
        // The triangular MA is a weighted moving average. Instead of the
        // TA_WMA who put more weigth on the latest price bar, the triangular
        // put more weigth on the data in the middle of the specified period.
        //
        // Examples:
        //   For TimeSerie={a,b,c,d,e,f...} ('a' is the older price)
        //
        //   1st value for TRIMA 4-Period is:  ((1*a)+(2*b)+(2*c)+(1*d)) / 6
        //   2nd value for TRIMA 4-Period is:  ((1*b)+(2*c)+(2*d)+(1*e)) / 6
        //
        //   1st value for TRIMA 5-Period is:  ((1*a)+(2*b)+(3*c)+(2*d)+(1*e)) / 9
        //   2nd value for TRIMA 5-Period is:  ((1*b)+(2*c)+(3*d)+(2*e)+(1*f)) / 9
        //
        // Generally Accepted Implementation
        // ==================================
        // Using algebra, it can be demonstrated that the TRIMA is equivalent to
        // doing a SMA of a SMA. The following explain the rules:
        //
        //  (1) When the period is even, TRIMA(x,period)=SMA(SMA(x,period/2),(period/2)+1)
        //  (2) When the period is odd,  TRIMA(x,period)=SMA(SMA(x,(period+1)/2),(period+1)/2)
        //
        // In other word:
        //  (1) A period of 4 becomes TRIMA(x,4) = SMA( SMA( x, 2), 3 )
        //  (2) A period of 5 becomes TRIMA(x,5) = SMA( SMA( x, 3), 3 )
        //
        // The SMA of a SMA is the algorithm generaly found in books.
        //
        // Tradestation Implementation
        // ===========================
        // Tradestation deviate from the generally accepted implementation by
        // making the TRIMA to be as follow:
        //    TRIMA(x,period) = SMA( SMA( x, (int)(period/2)+1), (int)(period/2)+1 );
        // This formula is done regardless if the period is even or odd.
        //
        // In other word:
        //  (1) A period of 4 becomes TRIMA(x,4) = SMA( SMA( x, 3), 3 )
        //  (2) A period of 5 becomes TRIMA(x,5) = SMA( SMA( x, 3), 3 )
        //  (3) A period of 6 becomes TRIMA(x,5) = SMA( SMA( x, 4), 4 )
        //  (4) A period of 7 becomes TRIMA(x,5) = SMA( SMA( x, 4), 4 )
        //
        // It is not clear to me if the Tradestation approach is a bug or a deliberate
        // decision to do things differently.
        //
        // Metastock Implementation
        // ========================
        // Output is the same as the generally accepted implementation.
        //
        // TA-Lib Implementation
        // =====================
        // Output is also the same as the generally accepted implementation.
        //
        // For speed optimization and avoid memory allocation, TA-Lib use
        // a better algorithm than the usual SMA of a SMA.
        //
        // The calculation from one TRIMA value to the next is done by doing 4
        // little adjustment (the following show a TRIMA 4-period):
        //
        // TRIMA at time 'd': ((1*a)+(2*b)+(2*c)+(1*d)) / 6
        // TRIMA at time 'e': ((1*b)+(2*c)+(2*d)+(1*e)) / 6
        //
        // To go from TRIMA 'd' to 'e', the following is done:
        //       1) 'a' and 'b' are substract from the numerator.
        //       2) 'd' is added to the numerator.
        //       3) 'e' is added to the numerator.
        //       4) Calculate TRIMA by doing numerator / 6
        //       5) Repeat sequence for next output
        //
        // These operations are the same steps done by TA-LIB:
        //       1) is done by numeratorSub
        //       2) is done by numeratorAdd.
        //       3) is obtain from the latest input
        //       4) Calculate and write TRIMA in the output
        //       5) Repeat for next output.
        //
        // Of course, numerotrAdd and numeratorSub needs to be
        // adjusted for each iteration.
        //
        // The update of numeratorSub needs values from the input at
        // the trailingIdx and middleIdx position.
        //
        // The update of numeratorAdd needs values from the input at
        // the middleIdx and todayIdx.
        outIdx = 0;
        if optInTimePeriod % 2 == 1 {
            // Logic for Odd period
            // Calculate the factor which is 1 divided by the
            // sumation of the weight.
            //
            // The sum of the weight is calculated as follow:
            //
            // The simple sumation serie 1+2+3... n can be
            // express as n(n+1)/2
            //
            // From this logic, a "triangular" sumation formula
            // can be found depending if the period is odd or even.
            //
            // Odd Period Formula:
            //  period = 5 and with n=(int)(period/2)
            //  the formula for a "triangular" serie is:
            //    1+2+3+2+1 = (n*(n+1))+n+1
            //              = (n+1)*(n+1)
            //              = 3 * 3 = 9
            //
            // Even period Formula:
            //   period = 6 and with n=(int)(period/2)
            //   the formula for a "triangular" serie is:
            //    1+2+3+3+2+1 = n*(n+1)
            //                = 3 * 4 = 12
            // Note: entirely done with int and becomes double only
            //       on assignement to the factor variable.
            i = (optInTimePeriod >> 1) as usize;
            factor = (((i + 1) * (i + 1)) as f64);
            factor = 1.0 / factor;
            // Initialize all the variable before
            // starting to iterate for each output.
            trailingIdx = startIdx - lookbackTotal;
            middleIdx = trailingIdx + i;
            todayIdx = middleIdx + i;
            numerator = 0.0;
            numeratorSub = 0.0;
            // for( i = middleIdx; i >= trailingIdx; i -= 1 )
            i = middleIdx;
            loop {
                tempReal = inReal[i];
                numeratorSub += tempReal;
                numerator += numeratorSub;
                if i == trailingIdx { break; }
                i -= 1;
            }
            numeratorAdd = 0.0;
            middleIdx += 1;
            for i in (middleIdx as usize)..(todayIdx as usize) + 1 {
                tempReal = inReal[i];
                numeratorAdd += tempReal;
                numerator += numeratorAdd;
            }
            i = (todayIdx as usize) + 1;
            // Write the first output
            outIdx = 0;
            tempReal = inReal[{ let _v = trailingIdx; trailingIdx += 1; _v }];
            outReal[outIdx] = numerator * factor;
            outIdx += 1;
            todayIdx += 1;
            // Note: The value at the trailingIdx was saved
            //       in tempReal to account for the case where
            //       outReal and inReal are ptr on the same
            //       buffer.
            // Iterate for remaining output
            while todayIdx <= endIdx {
                // Step (1)
                numerator -= numeratorSub;
                numeratorSub -= tempReal;
                tempReal = inReal[{ let _v = middleIdx; middleIdx += 1; _v }];
                numeratorSub += tempReal;
                // Step (2)
                numerator += numeratorAdd;
                numeratorAdd -= tempReal;
                tempReal = inReal[{ let _v = todayIdx; todayIdx += 1; _v }];
                numeratorAdd += tempReal;
                // Step (3)
                numerator += tempReal;
                // Step (4)
                tempReal = inReal[{ let _v = trailingIdx; trailingIdx += 1; _v }];
                outReal[outIdx] = numerator * factor;
                outIdx += 1;
            }
        } else {
            // Even logic.
            //
            // Very similar to the odd logic, except:
            //  - calculation of the factor is different.
            //  - the coverage of the numeratorSub and numeratorAdd is
            //    slightly different.
            //  - Adjustment of numeratorAdd is different. See Step (2).
            i = (optInTimePeriod >> 1) as usize;
            factor = ((i * (i + 1)) as f64);
            factor = 1.0 / factor;
            // Initialize all the variable before
            // starting to iterate for each output.
            trailingIdx = startIdx - lookbackTotal;
            middleIdx = trailingIdx + i - 1;
            todayIdx = middleIdx + i;
            numerator = 0.0;
            numeratorSub = 0.0;
            // for( i = middleIdx; i >= trailingIdx; i -= 1 )
            i = middleIdx;
            loop {
                tempReal = inReal[i];
                numeratorSub += tempReal;
                numerator += numeratorSub;
                if i == trailingIdx { break; }
                i -= 1;
            }
            numeratorAdd = 0.0;
            middleIdx += 1;
            for i in (middleIdx as usize)..(todayIdx as usize) + 1 {
                tempReal = inReal[i];
                numeratorAdd += tempReal;
                numerator += numeratorAdd;
            }
            i = (todayIdx as usize) + 1;
            // Write the first output
            outIdx = 0;
            tempReal = inReal[{ let _v = trailingIdx; trailingIdx += 1; _v }];
            outReal[outIdx] = numerator * factor;
            outIdx += 1;
            todayIdx += 1;
            // Note: The value at the trailingIdx was saved
            //       in tempReal to account for the case where
            //       outReal and inReal are ptr on the same
            //       buffer.
            // Iterate for remaining output
            while todayIdx <= endIdx {
                // Step (1)
                numerator -= numeratorSub;
                numeratorSub -= tempReal;
                tempReal = inReal[{ let _v = middleIdx; middleIdx += 1; _v }];
                numeratorSub += tempReal;
                // Step (2)
                numeratorAdd -= tempReal;
                numerator += numeratorAdd;
                tempReal = inReal[{ let _v = todayIdx; todayIdx += 1; _v }];
                numeratorAdd += tempReal;
                // Step (3)
                numerator += tempReal;
                // Step (4)
                tempReal = inReal[{ let _v = trailingIdx; trailingIdx += 1; _v }];
                outReal[outIdx] = numerator * factor;
                outIdx += 1;
            }
        }
        (*outNBElement) = outIdx;
        (*outBegIdx) = startIdx;
        return RetCode::Success;
    }
    /// Unchecked variant of [`Core::trima`], used for internal cross-indicator calls.
    ///
    /// Skips parameter validation and uses unchecked indexing internally. Every argument must
    /// satisfy the constraints documented on [`Core::trima`]; 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::trima`].
    #[inline]
    pub fn trima_unguarded(
        &self,
        mut startIdx: usize,
        endIdx: usize,
        inReal: &[f64],
        mut optInTimePeriod: i32,
        outBegIdx: &mut usize,
        outNBElement: &mut usize,
        outReal: &mut [f64],
    ) -> RetCode {
        let mut lookbackTotal: usize = 0_usize;
        let mut numerator: f64 = 0.0_f64;
        let mut numeratorSub: f64 = 0.0_f64;
        let mut numeratorAdd: f64 = 0.0_f64;
        let mut i: usize = 0_usize;
        let mut outIdx: usize = 0_usize;
        let mut todayIdx: usize = 0_usize;
        let mut trailingIdx: usize = 0_usize;
        let mut middleIdx: usize = 0_usize;
        let mut factor: f64 = 0.0_f64;
        let mut tempReal: f64 = 0.0_f64;
        unsafe {
        assert!(endIdx < inReal.len());
        let _assertLb = self.trima_lookback(optInTimePeriod);
        let _assertStart = if startIdx > _assertLb { startIdx } else { _assertLb };
        assert!(_assertStart > endIdx || endIdx - _assertStart < outReal.len());
        lookbackTotal = (optInTimePeriod - 1) as usize;
        if startIdx < lookbackTotal {
            startIdx = lookbackTotal;
        }
        if startIdx > endIdx {
            (*outBegIdx) = 0;
            (*outNBElement) = 0;
            return RetCode::Success;
        }
        outIdx = 0;
        if optInTimePeriod % 2 == 1 {
            i = (optInTimePeriod >> 1) as usize;
            factor = (((i + 1) * (i + 1)) as f64);
            factor = 1.0 / factor;
            trailingIdx = startIdx - lookbackTotal;
            middleIdx = trailingIdx + i;
            todayIdx = middleIdx + i;
            numerator = 0.0;
            numeratorSub = 0.0;
            // for( i = middleIdx; i >= trailingIdx; i -= 1 )
            i = middleIdx;
            loop {
                tempReal = *inReal.as_ptr().add(i);
                numeratorSub += tempReal;
                numerator += numeratorSub;
                if i == trailingIdx { break; }
                i -= 1;
            }
            numeratorAdd = 0.0;
            middleIdx += 1;
            for i in (middleIdx as usize)..(todayIdx as usize) + 1 {
                tempReal = *inReal.as_ptr().add(i);
                numeratorAdd += tempReal;
                numerator += numeratorAdd;
            }
            i = (todayIdx as usize) + 1;
            outIdx = 0;
            tempReal = *inReal.as_ptr().add({ let _v = trailingIdx; trailingIdx += 1; _v });
            *outReal.as_mut_ptr().add(outIdx) = numerator * factor;
            outIdx += 1;
            todayIdx += 1;
            while todayIdx <= endIdx {
                numerator -= numeratorSub;
                numeratorSub -= tempReal;
                tempReal = *inReal.as_ptr().add({ let _v = middleIdx; middleIdx += 1; _v });
                numeratorSub += tempReal;
                numerator += numeratorAdd;
                numeratorAdd -= tempReal;
                tempReal = *inReal.as_ptr().add({ let _v = todayIdx; todayIdx += 1; _v });
                numeratorAdd += tempReal;
                numerator += tempReal;
                tempReal = *inReal.as_ptr().add({ let _v = trailingIdx; trailingIdx += 1; _v });
                *outReal.as_mut_ptr().add(outIdx) = numerator * factor;
                outIdx += 1;
            }
        } else {
            i = (optInTimePeriod >> 1) as usize;
            factor = ((i * (i + 1)) as f64);
            factor = 1.0 / factor;
            trailingIdx = startIdx - lookbackTotal;
            middleIdx = trailingIdx + i - 1;
            todayIdx = middleIdx + i;
            numerator = 0.0;
            numeratorSub = 0.0;
            // for( i = middleIdx; i >= trailingIdx; i -= 1 )
            i = middleIdx;
            loop {
                tempReal = *inReal.as_ptr().add(i);
                numeratorSub += tempReal;
                numerator += numeratorSub;
                if i == trailingIdx { break; }
                i -= 1;
            }
            numeratorAdd = 0.0;
            middleIdx += 1;
            for i in (middleIdx as usize)..(todayIdx as usize) + 1 {
                tempReal = *inReal.as_ptr().add(i);
                numeratorAdd += tempReal;
                numerator += numeratorAdd;
            }
            i = (todayIdx as usize) + 1;
            outIdx = 0;
            tempReal = *inReal.as_ptr().add({ let _v = trailingIdx; trailingIdx += 1; _v });
            *outReal.as_mut_ptr().add(outIdx) = numerator * factor;
            outIdx += 1;
            todayIdx += 1;
            while todayIdx <= endIdx {
                numerator -= numeratorSub;
                numeratorSub -= tempReal;
                tempReal = *inReal.as_ptr().add({ let _v = middleIdx; middleIdx += 1; _v });
                numeratorSub += tempReal;
                numeratorAdd -= tempReal;
                numerator += numeratorAdd;
                tempReal = *inReal.as_ptr().add({ let _v = todayIdx; todayIdx += 1; _v });
                numeratorAdd += tempReal;
                numerator += tempReal;
                tempReal = *inReal.as_ptr().add({ let _v = trailingIdx; trailingIdx += 1; _v });
                *outReal.as_mut_ptr().add(outIdx) = numerator * factor;
                outIdx += 1;
            }
        }
        (*outNBElement) = outIdx;
        (*outBegIdx) = startIdx;
        return RetCode::Success;
        } // unsafe
    }
}
/***************/
/* End of File */
/***************/
