/* 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
 *  -------------------------------------------------------------------
 *  MW       Michael Williamson
 *
 *
 * Change history:
 *
 *  MMDDYY BY   Description
 *  -------------------------------------------------------------------
 *  122006 MW   Initial Version
 */

// 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::beta`]: the number of leading input values consumed before the
    /// first output value can be produced.
    ///
    /// # Arguments
    ///
    /// * `optInTimePeriod` — Rolling window length (number of returns) for the regression sums
    ///   (default 5, 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 beta_lookback(&self, mut optInTimePeriod: i32) -> usize {
        if ((optInTimePeriod) as i32) == (i32::MIN) {
            optInTimePeriod = 5;
        } else if (((optInTimePeriod) as i32) < 1) || (((optInTimePeriod) as i32) > 100000) {
            return usize::MAX;
        }
        return (optInTimePeriod) as usize;
    }
    /// Beta: the slope of a least-squares linear regression of one series' percentage returns (y,
    /// from inReal1) against another's (x, from inReal0) over a rolling window. Measures how much a
    /// security moves relative to a market index. Beta = 1 moves with the index; \< 1 less
    /// volatile, > 1 more volatile.
    ///
    /// # Formula
    ///
    /// ```text
    /// Per-bar returns: $x_i=(p^0_i-p^0_{i-1})/p^0_{i-1}$ from inReal0, $y_i=(p^1_i-p^1_{i-1})/p^1_{i-1}$ from inReal1. With $n$=period over the window: $\beta = \dfrac{n\,S_{xy}-S_x S_y}{n\,S_{xx}-S_x^2}$, where $S_{xx}=\sum x^2,\ S_{xy}=\sum xy,\ S_x=\sum x,\ S_y=\sum y$.
    /// ```
    ///
    /// # Arguments
    ///
    /// * `startIdx` — Start index of the requested calculation range.
    /// * `endIdx` — End index of the requested calculation range (inclusive).
    /// * `inReal0` — Series whose returns are the regression x (market/index)
    /// * `inReal1` — Series whose returns are the regression y (security)
    /// * `optInTimePeriod` — Rolling window length (number of returns) for the regression sums
    ///   (default 5, range 1..=100000)
    /// * `outBegIdx` — Set to the input index of the first output value.
    /// * `outNBElement` — Set to the number of output values written.
    /// * `outReal` — Beta: regression slope of inReal1-returns on inReal0-returns.
    ///
    /// 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 data0: Vec<f64> = (0..252).map(|i| 100.0 + 10.0 * (0.1 * i as f64).sin()).collect();
    /// let data1: Vec<f64> = (0..252).map(|i| 100.0 + 10.0 * (0.1 * i as f64 + 0.7).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.beta(
    ///     0, data0.len() - 1, &data0, &data1, 5,
    ///     &mut out_beg, &mut out_nb, &mut out,
    /// );
    /// assert_eq!(ret, RetCode::Success);
    /// assert!(out_nb > 0);
    /// ```
    ///
    /// # See also
    ///
    /// [`Core::correl`] · [`Core::linearreg_slope`] · [`Core::var`] · [`Core::stddev`]
    ///
    /// Further reading: [ta-lib.org/functions/beta](https://ta-lib.org/functions/beta/)
    #[doc(alias = "Betacoefficient")]
    pub fn beta(
        &self,
        startIdx: usize,
        endIdx: usize,
        inReal0: &[f64],
        inReal1: &[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 = 5;
        } else if (((optInTimePeriod) as i32) < 1) || (((optInTimePeriod) as i32) > 100000) {
            return RetCode::BadParam;
        }
        let mut startIdx = startIdx;
        let mut S_xx: f64 = 0.0_f64;
        let mut S_xy: f64 = 0.0_f64;
        let mut S_x: f64 = 0.0_f64;
        let mut S_y: f64 = 0.0_f64;
        let mut last_price_x: f64 = 0.0_f64;
        let mut last_price_y: f64 = 0.0_f64;
        let mut trailing_last_price_x: f64 = 0.0_f64;
        let mut trailing_last_price_y: f64 = 0.0_f64;
        let mut tmp_real: f64 = 0.0_f64;
        let mut x: f64 = 0.0_f64;
        let mut y: f64 = 0.0_f64;
        let mut n: f64 = 0.0_f64;
        let mut i: usize = 0_usize;
        let mut outIdx: usize = 0_usize;
        let mut trailingIdx: usize = 0_usize;
        let mut nbInitialElementNeeded: usize = 0_usize;
        S_xx = 0.0;
        S_xy = 0.0;
        S_x = 0.0;
        S_y = 0.0;
        last_price_x = 0.0;
        last_price_y = 0.0;
        trailing_last_price_x = 0.0;
        trailing_last_price_y = 0.0;
        tmp_real = 0.0;
        // sum of x * x
        // sum of x * y
        // sum of x
        // sum of y
        // the last price read from inReal0
        // the last price read from inReal1
        // same as last_price_x except used to remove elements from the trailing summation
        // same as last_price_y except used to remove elements from the trailing summation
        // temporary variable
        // the 'x' value, which is the last change between values in inReal0
        // the 'y' value, which is the last change between values in inReal1
        // DESCRIPTION OF ALGORITHM:
        //   The Beta 'algorithm' is a measure of a stocks volatility vs from index. The stock prices
        //   are given in inReal0 and the index prices are give in inReal1. The size of these vectors
        //   should be equal. The algorithm is to calculate the change between prices in both vectors
        //   and then 'plot' these changes are points in the Euclidean plane. The x value of the point
        //   is market return and the y value is the security return. The beta value is the slope of a
        //   linear regression through these points. A beta of 1 is simple the line y=x, so the stock
        //   varies percisely with the market. A beta of less than one means the stock varies less than
        //   the market and a beta of more than one means the stock varies more than market. A related
        //   value is the Alpha value (see TA_ALPHA) which is the Y-intercept of the same linear regression.
        // Validate the calculation method type and
        // identify the minimum number of input
        // consume before the first value is output..
        nbInitialElementNeeded = (optInTimePeriod) as usize;
        // Move up the start index if there is not
        // enough initial data.
        if startIdx < nbInitialElementNeeded {
            startIdx = nbInitialElementNeeded;
        }
        // Make sure there is still something to evaluate.
        if startIdx > endIdx {
            (*outBegIdx) = 0;
            (*outNBElement) = 0;
            return RetCode::Success;
        }
        // Consume first input.
        trailingIdx = startIdx - nbInitialElementNeeded;
        trailing_last_price_x = inReal0[trailingIdx];
        last_price_x = trailing_last_price_x;
        trailing_last_price_y = inReal1[trailingIdx];
        last_price_y = trailing_last_price_y;
        // Process remaining of lookback until ready to output the first value.
        i = { trailingIdx += 1; trailingIdx };
        while i < startIdx {
            tmp_real = inReal0[i];
            if !((last_price_x).abs() < 1e-14) {
                x = (tmp_real - last_price_x) / last_price_x;
            } else {
                x = 0.0;
            }
            last_price_x = tmp_real;
            tmp_real = inReal1[{ let _v = i; i += 1; _v }];
            if !((last_price_y).abs() < 1e-14) {
                y = (tmp_real - last_price_y) / last_price_y;
            } else {
                y = 0.0;
            }
            last_price_y = tmp_real;
            S_xx += x * x;
            S_xy += x * y;
            S_x += x;
            S_y += y;
        }
        outIdx = 0;
        // First output always start at index zero
        n = optInTimePeriod as f64;
        loop {
            tmp_real = inReal0[i];
            if !((last_price_x).abs() < 1e-14) {
                x = (tmp_real - last_price_x) / last_price_x;
            } else {
                x = 0.0;
            }
            last_price_x = tmp_real;
            tmp_real = inReal1[{ let _v = i; i += 1; _v }];
            if !((last_price_y).abs() < 1e-14) {
                y = (tmp_real - last_price_y) / last_price_y;
            } else {
                y = 0.0;
            }
            last_price_y = tmp_real;
            S_xx += x * x;
            S_xy += x * y;
            S_x += x;
            S_y += y;
            // Always read the trailing before writing the output because the input and output
            // buffer can be the same.
            tmp_real = inReal0[trailingIdx];
            if !((trailing_last_price_x).abs() < 1e-14) {
                x = (tmp_real - trailing_last_price_x) / trailing_last_price_x;
            } else {
                x = 0.0;
            }
            trailing_last_price_x = tmp_real;
            tmp_real = inReal1[{ let _v = trailingIdx; trailingIdx += 1; _v }];
            if !((trailing_last_price_y).abs() < 1e-14) {
                y = (tmp_real - trailing_last_price_y) / trailing_last_price_y;
            } else {
                y = 0.0;
            }
            trailing_last_price_y = tmp_real;
            // Write the output
            tmp_real = n * S_xx - S_x * S_x;
            if !((tmp_real).abs() < 1e-14) {
                outReal[outIdx] = (n * S_xy - S_x * S_y) / tmp_real;
                outIdx += 1;
            } else {
                outReal[outIdx] = 0.0;
                outIdx += 1;
            }
            // Remove the calculation starting with the trailingIdx.
            S_xx -= x * x;
            S_xy -= x * y;
            S_x -= x;
            S_y -= y;
            if !(i <= endIdx) { break; }
        }
        // All done. Indicate the output limits and return.
        (*outNBElement) = outIdx;
        (*outBegIdx) = startIdx;
        return RetCode::Success;
    }
    /// Unchecked variant of [`Core::beta`], used for internal cross-indicator calls.
    ///
    /// Skips parameter validation and uses unchecked indexing internally. Every argument must
    /// satisfy the constraints documented on [`Core::beta`]; 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::beta`].
    #[inline]
    pub fn beta_unguarded(
        &self,
        mut startIdx: usize,
        endIdx: usize,
        inReal0: &[f64],
        inReal1: &[f64],
        mut optInTimePeriod: i32,
        outBegIdx: &mut usize,
        outNBElement: &mut usize,
        outReal: &mut [f64],
    ) -> RetCode {
        let mut S_xx: f64 = 0.0_f64;
        let mut S_xy: f64 = 0.0_f64;
        let mut S_x: f64 = 0.0_f64;
        let mut S_y: f64 = 0.0_f64;
        let mut last_price_x: f64 = 0.0_f64;
        let mut last_price_y: f64 = 0.0_f64;
        let mut trailing_last_price_x: f64 = 0.0_f64;
        let mut trailing_last_price_y: f64 = 0.0_f64;
        let mut tmp_real: f64 = 0.0_f64;
        let mut x: f64 = 0.0_f64;
        let mut y: f64 = 0.0_f64;
        let mut n: f64 = 0.0_f64;
        let mut i: usize = 0_usize;
        let mut outIdx: usize = 0_usize;
        let mut trailingIdx: usize = 0_usize;
        let mut nbInitialElementNeeded: usize = 0_usize;
        unsafe {
        assert!(endIdx < inReal0.len());
        assert!(endIdx < inReal1.len());
        let _assertLb = self.beta_lookback(optInTimePeriod);
        let _assertStart = if startIdx > _assertLb { startIdx } else { _assertLb };
        assert!(_assertStart > endIdx || endIdx - _assertStart < outReal.len());
        S_xx = 0.0;
        S_xy = 0.0;
        S_x = 0.0;
        S_y = 0.0;
        last_price_x = 0.0;
        last_price_y = 0.0;
        trailing_last_price_x = 0.0;
        trailing_last_price_y = 0.0;
        tmp_real = 0.0;
        nbInitialElementNeeded = (optInTimePeriod) as usize;
        if startIdx < nbInitialElementNeeded {
            startIdx = nbInitialElementNeeded;
        }
        if startIdx > endIdx {
            (*outBegIdx) = 0;
            (*outNBElement) = 0;
            return RetCode::Success;
        }
        trailingIdx = startIdx - nbInitialElementNeeded;
        trailing_last_price_x = *inReal0.as_ptr().add(trailingIdx);
        last_price_x = trailing_last_price_x;
        trailing_last_price_y = *inReal1.as_ptr().add(trailingIdx);
        last_price_y = trailing_last_price_y;
        i = { trailingIdx += 1; trailingIdx };
        while i < startIdx {
            tmp_real = *inReal0.as_ptr().add(i);
            if !((last_price_x).abs() < 1e-14) {
                x = (tmp_real - last_price_x) / last_price_x;
            } else {
                x = 0.0;
            }
            last_price_x = tmp_real;
            tmp_real = *inReal1.as_ptr().add({ let _v = i; i += 1; _v });
            if !((last_price_y).abs() < 1e-14) {
                y = (tmp_real - last_price_y) / last_price_y;
            } else {
                y = 0.0;
            }
            last_price_y = tmp_real;
            S_xx += x * x;
            S_xy += x * y;
            S_x += x;
            S_y += y;
        }
        outIdx = 0;
        n = optInTimePeriod as f64;
        loop {
            tmp_real = *inReal0.as_ptr().add(i);
            if !((last_price_x).abs() < 1e-14) {
                x = (tmp_real - last_price_x) / last_price_x;
            } else {
                x = 0.0;
            }
            last_price_x = tmp_real;
            tmp_real = *inReal1.as_ptr().add({ let _v = i; i += 1; _v });
            if !((last_price_y).abs() < 1e-14) {
                y = (tmp_real - last_price_y) / last_price_y;
            } else {
                y = 0.0;
            }
            last_price_y = tmp_real;
            S_xx += x * x;
            S_xy += x * y;
            S_x += x;
            S_y += y;
            tmp_real = *inReal0.as_ptr().add(trailingIdx);
            if !((trailing_last_price_x).abs() < 1e-14) {
                x = (tmp_real - trailing_last_price_x) / trailing_last_price_x;
            } else {
                x = 0.0;
            }
            trailing_last_price_x = tmp_real;
            tmp_real = *inReal1.as_ptr().add({ let _v = trailingIdx; trailingIdx += 1; _v });
            if !((trailing_last_price_y).abs() < 1e-14) {
                y = (tmp_real - trailing_last_price_y) / trailing_last_price_y;
            } else {
                y = 0.0;
            }
            trailing_last_price_y = tmp_real;
            tmp_real = n * S_xx - S_x * S_x;
            if !((tmp_real).abs() < 1e-14) {
                *outReal.as_mut_ptr().add(outIdx) = (n * S_xy - S_x * S_y) / tmp_real;
                outIdx += 1;
            } else {
                *outReal.as_mut_ptr().add(outIdx) = 0.0;
                outIdx += 1;
            }
            S_xx -= x * x;
            S_xy -= x * y;
            S_x -= x;
            S_y -= y;
            if !(i <= endIdx) { break; }
        }
        (*outNBElement) = outIdx;
        (*outBegIdx) = startIdx;
        return RetCode::Success;
        } // unsafe
    }
}
/***************/
/* End of File */
/***************/
