/* TA-LIB Copyright (c) 1999-2026, 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. */ /* AUTO-GENERATED by ta_codegen — DO NOT EDIT. * Source of truth: ta_codegen/input// (regenerate: cd ta_codegen/generator && cargo run -- generate) */ #include #include #include "ta_func.h" #include "ta_utility.h" #include "ta_memory.h" #include "ta_func_stream_private.h" /* List of contributors: * * Initial Name/description * ------------------------------------------------------------------- * MHL Matthew Lindblom * MF Mario Fortier * * Change history: * * MMDDYY BY Description * ------------------------------------------------------------------- * 120802 MF Template creation. * 032003 MHL Implementation of T3 * 040503 MF Adapt for compatibility with published code * for TradeStation and Metastock. * See "Smoothing Techniques For More Accurate Signals" * from Tim Tillson in Stock&Commodities V16:1 Page 33-37 * 052603 MF Adapt code to compile with .NET Managed C++ * 070226 MF,CC Allow period of 1: output is an exact copy of the * input, consistent with TA_MA (issues #48, #59). The * natural math is only near-identity at period=1: the * coefficients sum to 1 in real arithmetic but not in * floating point (~1e-14 drift), so the copy is explicit. * 100626 MF,CC Auto rule sized on the worst seed of the six stages, * against the largest output difference it causes (#492). * 100726 MF,CC Auto rule sized by measurement on price series (#492). */ TA_NOINLINE TA_LIB_API int TA_T3_Lookback( int optInTimePeriod, double optInVFactor ) { if( (int)optInTimePeriod == TA_INTEGER_DEFAULT ) optInTimePeriod = 5; else if( (int)optInTimePeriod < 1 || (int)optInTimePeriod > 100000 ) return -1; if( optInVFactor == TA_REAL_DEFAULT ) optInVFactor = 0.7; else if( !(optInVFactor >= 0e0 && optInVFactor <= 1e0) ) return -1; /* Sized by measurement on price series; it is not a bound. The six stages * share the pole (n-1)/(n+1) and are stepped together, so a seed can cancel * itself in the early outputs and show late: such a seed needs up to * 13*n bars at PREC_4 and 18.5*n at PREC_8. */ return 6 * (optInTimePeriod - 1) + TA_GLOBALS_UNSTABLE(TA_FUNC_UNST_T3,T3,(optInTimePeriod > 1) ? (11 * (4 + 4) * optInTimePeriod + 7) / 8 : 0,(optInTimePeriod > 1) ? (11 * (8 + 4) * optInTimePeriod + 7) / 8 : 0); } TA_LIB_API int TA_T3_DisplayShift( int optInTimePeriod, double optInVFactor, int outputIdx ) { if( TA_T3_Lookback( optInTimePeriod, optInVFactor ) < 0 ) return INT_MIN; if( outputIdx < 0 || outputIdx >= 1 ) return INT_MIN; return 0; } TA_FMA_MULTIVERSION TA_LIB_API TA_RetCode TA_T3( int startIdx, int endIdx, const double inReal[], int optInTimePeriod, double optInVFactor, int *outBegIdx, int *outNBElement, double outReal[] ) { int outIdx; int lookbackTotal; int today; int i; double k; double one_minus_k; double e1; double e2; double e3; double e4; double e5; double e6; double c1; double c2; double c3; double c4; double tempReal; if( (startIdx < 0) || (startIdx > TA_INDEX_MAX) ) return TA_OUT_OF_RANGE_START_INDEX; if( (endIdx < 0) || (endIdx > TA_INDEX_MAX) || (endIdx < startIdx) ) return TA_OUT_OF_RANGE_END_INDEX; if( (int)optInTimePeriod == TA_INTEGER_DEFAULT ) optInTimePeriod = 5; else if( (int)optInTimePeriod < 1 || (int)optInTimePeriod > 100000 ) return TA_BAD_PARAM; if( optInVFactor == TA_REAL_DEFAULT ) optInVFactor = 0.7; else if( !(optInVFactor >= 0e0 && optInVFactor <= 1e0) ) return TA_BAD_PARAM; if( !inReal ) return TA_BAD_PARAM; if( !outBegIdx || !outNBElement ) return TA_BAD_PARAM; if( !outReal ) return TA_BAD_PARAM; /* For an explanation of this function, please read: * * Magazine articles written by Tim Tillson * * Essentially, a T3 of time serie 't' is: * EMA1(x,Period) = EMA(x,Period) * EMA2(x,Period) = EMA(EMA1(x,Period),Period) * GD(x,Period,vFactor) = (EMA1(x,Period)*(1+vFactor)) - (EMA2(x,Period)*vFactor) * T3 = GD (GD ( GD(t, Period, vFactor), Period, vFactor), Period, vFactor); * * T3 offers a moving average with less lags then the * traditional EMA. * * Do not confuse a T3 with EMA3. Both are called "Triple EMA" * in the litterature. */ lookbackTotal = TA_T3_Lookback(optInTimePeriod,optInVFactor); if( startIdx <= lookbackTotal ) { startIdx = lookbackTotal; } /* Make sure there is still something to evaluate. */ if( startIdx > endIdx ) { *outNBElement= 0; *outBegIdx= 0; return TA_SUCCESS; } /* No smoothing at period of 1: the output is a copy of the input * (same convention as TA_MA for every MAType). Explicit because the * coefficients below sum to 1 only in real arithmetic; going through * the math would leave ~1e-14 floating-point drift on every value. */ if( optInTimePeriod == 1 ) { *outBegIdx= startIdx; outIdx = 0; today = startIdx; while( today <= endIdx ) { outReal[outIdx++] = inReal[today++]; } *outNBElement= outIdx; return TA_SUCCESS; } *outBegIdx= startIdx; today = startIdx - lookbackTotal; k = 2.0 / (optInTimePeriod + 1.0); one_minus_k = 1.0 - k; /* Initialize e1 */ tempReal = inReal[today++]; for( i = optInTimePeriod - 1; i > 0; i -= 1 ) { tempReal += inReal[today++]; } e1 = tempReal / optInTimePeriod; /* Initialize e2 */ tempReal = e1; for( i = optInTimePeriod - 1; i > 0; i -= 1 ) { e1 = fma(one_minus_k, e1, k * inReal[today++]); tempReal += e1; } e2 = tempReal / optInTimePeriod; /* Initialize e3 */ tempReal = e2; for( i = optInTimePeriod - 1; i > 0; i -= 1 ) { e1 = fma(one_minus_k, e1, k * inReal[today++]); e2 = fma(one_minus_k, e2, k * e1); tempReal += e2; } e3 = tempReal / optInTimePeriod; /* Initialize e4 */ tempReal = e3; for( i = optInTimePeriod - 1; i > 0; i -= 1 ) { e1 = fma(one_minus_k, e1, k * inReal[today++]); e2 = fma(one_minus_k, e2, k * e1); e3 = fma(one_minus_k, e3, k * e2); tempReal += e3; } e4 = tempReal / optInTimePeriod; /* Initialize e5 */ tempReal = e4; for( i = optInTimePeriod - 1; i > 0; i -= 1 ) { e1 = fma(one_minus_k, e1, k * inReal[today++]); e2 = fma(one_minus_k, e2, k * e1); e3 = fma(one_minus_k, e3, k * e2); e4 = fma(one_minus_k, e4, k * e3); tempReal += e4; } e5 = tempReal / optInTimePeriod; /* Initialize e6 */ tempReal = e5; for( i = optInTimePeriod - 1; i > 0; i -= 1 ) { e1 = fma(one_minus_k, e1, k * inReal[today++]); e2 = fma(one_minus_k, e2, k * e1); e3 = fma(one_minus_k, e3, k * e2); e4 = fma(one_minus_k, e4, k * e3); e5 = fma(one_minus_k, e5, k * e4); tempReal += e5; } e6 = tempReal / optInTimePeriod; /* Skip the unstable period */ while( today <= startIdx ) { /* Do the calculation but do not write the output */ e1 = fma(one_minus_k, e1, k * inReal[today++]); e2 = fma(one_minus_k, e2, k * e1); e3 = fma(one_minus_k, e3, k * e2); e4 = fma(one_minus_k, e4, k * e3); e5 = fma(one_minus_k, e5, k * e4); e6 = fma(one_minus_k, e6, k * e5); } /* Calculate the constants */ tempReal = optInVFactor * optInVFactor; c1 = -(tempReal * optInVFactor); c2 = 3.0 * (tempReal - c1); c3 = -6.0 * tempReal - 3.0 * (optInVFactor - c1); c4 = fma(3.0, tempReal, fma(3.0, optInVFactor, 1.0) - c1); /* Write the first output */ outIdx = 0; outReal[outIdx++] = fma(c4, e3, fma(c3, e4, fma(c1, e6, c2 * e5))); /* Calculate and output the remaining of the range. */ while( today <= endIdx ) { e1 = fma(one_minus_k, e1, k * inReal[today++]); e2 = fma(one_minus_k, e2, k * e1); e3 = fma(one_minus_k, e3, k * e2); e4 = fma(one_minus_k, e4, k * e3); e5 = fma(one_minus_k, e5, k * e4); e6 = fma(one_minus_k, e6, k * e5); outReal[outIdx++] = fma(c4, e3, fma(c3, e4, fma(c1, e6, c2 * e5))); } /* Indicates to the caller the number of output * successfully calculated. */ *outNBElement= outIdx; return TA_SUCCESS; } TA_FMA_MULTIVERSION TA_RetCode TA_S_T3( int startIdx, int endIdx, const float inReal[], int optInTimePeriod, double optInVFactor, int *outBegIdx, int *outNBElement, double outReal[] ) { int outIdx; int lookbackTotal; int today; int i; double k; double one_minus_k; double e1; double e2; double e3; double e4; double e5; double e6; double c1; double c2; double c3; double c4; double tempReal; if( (startIdx < 0) || (startIdx > TA_INDEX_MAX) ) return TA_OUT_OF_RANGE_START_INDEX; if( (endIdx < 0) || (endIdx > TA_INDEX_MAX) || (endIdx < startIdx) ) return TA_OUT_OF_RANGE_END_INDEX; if( (int)optInTimePeriod == TA_INTEGER_DEFAULT ) optInTimePeriod = 5; else if( (int)optInTimePeriod < 1 || (int)optInTimePeriod > 100000 ) return TA_BAD_PARAM; if( optInVFactor == TA_REAL_DEFAULT ) optInVFactor = 0.7; else if( !(optInVFactor >= 0e0 && optInVFactor <= 1e0) ) return TA_BAD_PARAM; if( !inReal ) return TA_BAD_PARAM; if( !outBegIdx || !outNBElement ) return TA_BAD_PARAM; if( !outReal ) return TA_BAD_PARAM; lookbackTotal = TA_T3_Lookback(optInTimePeriod,optInVFactor); if( startIdx <= lookbackTotal ) { startIdx = lookbackTotal; } if( startIdx > endIdx ) { *outNBElement= 0; *outBegIdx= 0; return TA_SUCCESS; } if( optInTimePeriod == 1 ) { *outBegIdx= startIdx; outIdx = 0; today = startIdx; while( today <= endIdx ) { outReal[outIdx++] = (double)inReal[today++]; } *outNBElement= outIdx; return TA_SUCCESS; } *outBegIdx= startIdx; today = startIdx - lookbackTotal; k = 2.0 / (optInTimePeriod + 1.0); one_minus_k = 1.0 - k; tempReal = (double)inReal[today++]; for( i = optInTimePeriod - 1; i > 0; i -= 1 ) { tempReal += (double)inReal[today++]; } e1 = tempReal / optInTimePeriod; tempReal = e1; for( i = optInTimePeriod - 1; i > 0; i -= 1 ) { e1 = fma(one_minus_k, e1, k * (double)inReal[today++]); tempReal += e1; } e2 = tempReal / optInTimePeriod; tempReal = e2; for( i = optInTimePeriod - 1; i > 0; i -= 1 ) { e1 = fma(one_minus_k, e1, k * (double)inReal[today++]); e2 = fma(one_minus_k, e2, k * e1); tempReal += e2; } e3 = tempReal / optInTimePeriod; tempReal = e3; for( i = optInTimePeriod - 1; i > 0; i -= 1 ) { e1 = fma(one_minus_k, e1, k * (double)inReal[today++]); e2 = fma(one_minus_k, e2, k * e1); e3 = fma(one_minus_k, e3, k * e2); tempReal += e3; } e4 = tempReal / optInTimePeriod; tempReal = e4; for( i = optInTimePeriod - 1; i > 0; i -= 1 ) { e1 = fma(one_minus_k, e1, k * (double)inReal[today++]); e2 = fma(one_minus_k, e2, k * e1); e3 = fma(one_minus_k, e3, k * e2); e4 = fma(one_minus_k, e4, k * e3); tempReal += e4; } e5 = tempReal / optInTimePeriod; tempReal = e5; for( i = optInTimePeriod - 1; i > 0; i -= 1 ) { e1 = fma(one_minus_k, e1, k * (double)inReal[today++]); e2 = fma(one_minus_k, e2, k * e1); e3 = fma(one_minus_k, e3, k * e2); e4 = fma(one_minus_k, e4, k * e3); e5 = fma(one_minus_k, e5, k * e4); tempReal += e5; } e6 = tempReal / optInTimePeriod; while( today <= startIdx ) { e1 = fma(one_minus_k, e1, k * (double)inReal[today++]); e2 = fma(one_minus_k, e2, k * e1); e3 = fma(one_minus_k, e3, k * e2); e4 = fma(one_minus_k, e4, k * e3); e5 = fma(one_minus_k, e5, k * e4); e6 = fma(one_minus_k, e6, k * e5); } tempReal = optInVFactor * optInVFactor; c1 = -(tempReal * optInVFactor); c2 = 3.0 * (tempReal - c1); c3 = -6.0 * tempReal - 3.0 * (optInVFactor - c1); c4 = fma(3.0, tempReal, fma(3.0, optInVFactor, 1.0) - c1); outIdx = 0; outReal[outIdx++] = fma(c4, e3, fma(c3, e4, fma(c1, e6, c2 * e5))); while( today <= endIdx ) { e1 = fma(one_minus_k, e1, k * (double)inReal[today++]); e2 = fma(one_minus_k, e2, k * e1); e3 = fma(one_minus_k, e3, k * e2); e4 = fma(one_minus_k, e4, k * e3); e5 = fma(one_minus_k, e5, k * e4); e6 = fma(one_minus_k, e6, k * e5); outReal[outIdx++] = fma(c4, e3, fma(c3, e4, fma(c1, e6, c2 * e5))); } *outNBElement= outIdx; return TA_SUCCESS; } /**** Streaming API *****/ struct TA_T3_Stream { /* The bars this handle has an output for (see TA_T3_OutRange). */ int outRangeBegIdx; int outRangeCount; /* The value(s) at the last bar the stream counted (see TA_T3_Value). */ double cur_outReal; int optInTimePeriod; double optInVFactor; double k; double one_minus_k; double pad_0; double e1; double e2; double e3; double e4; double e5; double e6; double pad_1; double c1; double c2; double c3; double c4; }; /* Private function, not in public API. */ static TA_FMA_STEP_INLINE void TA_T3_StepImpl( struct TA_T3_Stream *sp, double inReal, double *outReal ) { if( sp->optInTimePeriod == 1 ) { *outReal= inReal; sp->cur_outReal = *outReal; return; } sp->e1 = fma(sp->one_minus_k, sp->e1, sp->k * inReal); sp->e2 = fma(sp->one_minus_k, sp->e2, sp->k * sp->e1); sp->e3 = fma(sp->one_minus_k, sp->e3, sp->k * sp->e2); sp->e4 = fma(sp->one_minus_k, sp->e4, sp->k * sp->e3); sp->e5 = fma(sp->one_minus_k, sp->e5, sp->k * sp->e4); sp->e6 = fma(sp->one_minus_k, sp->e6, sp->k * sp->e5); *outReal= fma(sp->c4, sp->e3, fma(sp->c3, sp->e4, fma(sp->c1, sp->e6, sp->c2 * sp->e5))); sp->cur_outReal = *outReal; } static TA_FMA_STEP_INLINE TA_RetCode TA_T3_OpenImpl( struct TA_T3_Stream **stream, const double inReal[], int startIdx, int historyLen, int optInTimePeriod, double optInVFactor, int *outBegIdx, int *outNBElement, double outReal[], int outStride ) { struct TA_T3_Stream *sp; int endIdx; if( !stream ) return TA_BAD_PARAM; *stream = NULL; if( historyLen < 1 ) return TA_OUT_OF_RANGE_START_INDEX; if( historyLen > TA_INDEX_MAX + 1 ) return TA_OUT_OF_RANGE_END_INDEX; if( !inReal || !outReal ) return TA_BAD_PARAM; if( (int)optInTimePeriod == TA_INTEGER_DEFAULT ) optInTimePeriod = 5; else if( (int)optInTimePeriod < 1 || (int)optInTimePeriod > 100000 ) return TA_BAD_PARAM; if( optInVFactor == TA_REAL_DEFAULT ) optInVFactor = 0.7; else if( !(optInVFactor >= 0e0 && optInVFactor <= 1e0) ) return TA_BAD_PARAM; if( startIdx > historyLen - 1 ) { *outBegIdx = 0; *outNBElement = 0; return TA_INSUFFICIENT_HISTORY; } endIdx = historyLen - 1; if( optInTimePeriod == 1 ) { int fillLb = TA_T3_Lookback( optInTimePeriod, optInVFactor ); if( startIdx > fillLb ) fillLb = startIdx; if( historyLen < fillLb + 1 ) { *outBegIdx = 0; *outNBElement = 0; return TA_INSUFFICIENT_HISTORY; } sp = (struct TA_T3_Stream *)TA_Malloc( sizeof(*sp) ); if( !sp ) { return TA_ALLOC_ERR; } memset( sp, 0, sizeof(*sp) ); sp->optInTimePeriod = optInTimePeriod; sp->optInVFactor = optInVFactor; { int fillIdx; *outBegIdx = fillLb; *outNBElement = historyLen - fillLb; if( outStride ) { for( fillIdx = 0; fillIdx < historyLen - fillLb; fillIdx++ ) { outReal[fillIdx] = inReal[fillLb + fillIdx]; } } else { outReal[0] = inReal[historyLen - 1]; } } sp->outRangeBegIdx = *outBegIdx; sp->outRangeCount = *outNBElement; sp->cur_outReal = outReal[(*outNBElement - 1) * outStride]; *stream = sp; return TA_SUCCESS; } { int outIdx; int lookbackTotal; int today; int i; double k = 0.0; double one_minus_k = 0.0; double e1 = 0.0; double e2 = 0.0; double e3 = 0.0; double e4 = 0.0; double e5 = 0.0; double e6 = 0.0; double c1 = 0.0; double c2 = 0.0; double c3 = 0.0; double c4 = 0.0; double tempReal; /* For an explanation of this function, please read: * * Magazine articles written by Tim Tillson * * Essentially, a T3 of time serie 't' is: * EMA1(x,Period) = EMA(x,Period) * EMA2(x,Period) = EMA(EMA1(x,Period),Period) * GD(x,Period,vFactor) = (EMA1(x,Period)*(1+vFactor)) - (EMA2(x,Period)*vFactor) * T3 = GD (GD ( GD(t, Period, vFactor), Period, vFactor), Period, vFactor); * * T3 offers a moving average with less lags then the * traditional EMA. * * Do not confuse a T3 with EMA3. Both are called "Triple EMA" * in the litterature. */ lookbackTotal = TA_T3_Lookback(optInTimePeriod,optInVFactor); if( startIdx <= lookbackTotal ) { startIdx = lookbackTotal; } /* Make sure there is still something to evaluate. */ if( startIdx > endIdx ) { *outNBElement= 0; *outBegIdx= 0; return TA_INSUFFICIENT_HISTORY; } *outBegIdx= startIdx; today = startIdx - lookbackTotal; k = 2.0 / (optInTimePeriod + 1.0); one_minus_k = 1.0 - k; /* Initialize e1 */ tempReal = inReal[today++]; for( i = optInTimePeriod - 1; i > 0; i -= 1 ) { tempReal += inReal[today++]; } e1 = tempReal / optInTimePeriod; /* Initialize e2 */ tempReal = e1; for( i = optInTimePeriod - 1; i > 0; i -= 1 ) { e1 = fma(one_minus_k, e1, k * inReal[today++]); tempReal += e1; } e2 = tempReal / optInTimePeriod; /* Initialize e3 */ tempReal = e2; for( i = optInTimePeriod - 1; i > 0; i -= 1 ) { e1 = fma(one_minus_k, e1, k * inReal[today++]); e2 = fma(one_minus_k, e2, k * e1); tempReal += e2; } e3 = tempReal / optInTimePeriod; /* Initialize e4 */ tempReal = e3; for( i = optInTimePeriod - 1; i > 0; i -= 1 ) { e1 = fma(one_minus_k, e1, k * inReal[today++]); e2 = fma(one_minus_k, e2, k * e1); e3 = fma(one_minus_k, e3, k * e2); tempReal += e3; } e4 = tempReal / optInTimePeriod; /* Initialize e5 */ tempReal = e4; for( i = optInTimePeriod - 1; i > 0; i -= 1 ) { e1 = fma(one_minus_k, e1, k * inReal[today++]); e2 = fma(one_minus_k, e2, k * e1); e3 = fma(one_minus_k, e3, k * e2); e4 = fma(one_minus_k, e4, k * e3); tempReal += e4; } e5 = tempReal / optInTimePeriod; /* Initialize e6 */ tempReal = e5; for( i = optInTimePeriod - 1; i > 0; i -= 1 ) { e1 = fma(one_minus_k, e1, k * inReal[today++]); e2 = fma(one_minus_k, e2, k * e1); e3 = fma(one_minus_k, e3, k * e2); e4 = fma(one_minus_k, e4, k * e3); e5 = fma(one_minus_k, e5, k * e4); tempReal += e5; } e6 = tempReal / optInTimePeriod; /* Skip the unstable period */ while( today <= startIdx ) { /* Do the calculation but do not write the output */ e1 = fma(one_minus_k, e1, k * inReal[today++]); e2 = fma(one_minus_k, e2, k * e1); e3 = fma(one_minus_k, e3, k * e2); e4 = fma(one_minus_k, e4, k * e3); e5 = fma(one_minus_k, e5, k * e4); e6 = fma(one_minus_k, e6, k * e5); } /* Calculate the constants */ tempReal = optInVFactor * optInVFactor; c1 = -(tempReal * optInVFactor); c2 = 3.0 * (tempReal - c1); c3 = -6.0 * tempReal - 3.0 * (optInVFactor - c1); c4 = fma(3.0, tempReal, fma(3.0, optInVFactor, 1.0) - c1); /* Write the first output */ outIdx = 0; outReal[outIdx++ * outStride] = fma(c4, e3, fma(c3, e4, fma(c1, e6, c2 * e5))); /* Calculate and output the remaining of the range. */ while( today <= endIdx ) { e1 = fma(one_minus_k, e1, k * inReal[today++]); e2 = fma(one_minus_k, e2, k * e1); e3 = fma(one_minus_k, e3, k * e2); e4 = fma(one_minus_k, e4, k * e3); e5 = fma(one_minus_k, e5, k * e4); e6 = fma(one_minus_k, e6, k * e5); outReal[outIdx++ * outStride] = fma(c4, e3, fma(c3, e4, fma(c1, e6, c2 * e5))); } /* Indicates to the caller the number of output * successfully calculated. */ *outNBElement= outIdx; /* Capture the live batch state into the handle. */ sp = (struct TA_T3_Stream *)TA_Malloc( sizeof(*sp) ); if( !sp ) { return TA_ALLOC_ERR; } memset( sp, 0, sizeof(*sp) ); sp->optInTimePeriod = optInTimePeriod; sp->optInVFactor = optInVFactor; sp->k = k; sp->one_minus_k = one_minus_k; sp->e1 = e1; sp->e2 = e2; sp->e3 = e3; sp->e4 = e4; sp->e5 = e5; sp->e6 = e6; sp->c1 = c1; sp->c2 = c2; sp->c3 = c3; sp->c4 = c4; sp->outRangeBegIdx = *outBegIdx; sp->outRangeCount = *outNBElement; sp->cur_outReal = outReal[(*outNBElement - 1) * outStride]; *stream = sp; return TA_SUCCESS; } } TA_FMA_OPEN_CLONE static TA_RetCode TA_T3_OpenImplFma( struct TA_T3_Stream **stream, const double inReal[], int startIdx, int historyLen, int optInTimePeriod, double optInVFactor, int *outBegIdx, int *outNBElement, double outReal[], int outStride ) { return TA_T3_OpenImpl( stream, inReal, startIdx, historyLen, optInTimePeriod, optInVFactor, outBegIdx, outNBElement, outReal, outStride ); } TA_FMA_OPEN_PLAIN static TA_RetCode TA_T3_OpenImplPlain( struct TA_T3_Stream **stream, const double inReal[], int startIdx, int historyLen, int optInTimePeriod, double optInVFactor, int *outBegIdx, int *outNBElement, double outReal[], int outStride ) { return TA_T3_OpenImpl( stream, inReal, startIdx, historyLen, optInTimePeriod, optInVFactor, outBegIdx, outNBElement, outReal, outStride ); } TA_FMA_OPEN_CLONE static TA_RetCode TA_T3_OpenSinkFma( struct TA_T3_Stream **stream, const double inReal[], int startIdx, int historyLen, int optInTimePeriod, double optInVFactor, double *outReal ) { TA_RetCode retCode; int dummyBegIdx = 0; int dummyNBElement = 0; double sink_outReal = 0.0; retCode = TA_T3_OpenImpl( stream, inReal, startIdx, historyLen, optInTimePeriod, optInVFactor, &dummyBegIdx, &dummyNBElement, &sink_outReal, 0 ); if( retCode == TA_SUCCESS ) { *outReal = sink_outReal; } return retCode; } /* Private function, not in public API. */ TA_RetCode TA_T3_OpenInternal( struct TA_T3_Stream **stream, const double inReal[], int startIdx, int historyLen, int optInTimePeriod, double optInVFactor, double *outReal ) { TA_RetCode retCode; int dummyBegIdx = 0; int dummyNBElement = 0; double sink_outReal = 0.0; retCode = TA_FMA_AVAILABLE ? TA_T3_OpenImplFma( stream, inReal, startIdx, historyLen, optInTimePeriod, optInVFactor, &dummyBegIdx, &dummyNBElement, &sink_outReal, 0 ) : TA_T3_OpenImplPlain( stream, inReal, startIdx, historyLen, optInTimePeriod, optInVFactor, &dummyBegIdx, &dummyNBElement, &sink_outReal, 0 ); if( retCode == TA_SUCCESS ) { *outReal = sink_outReal; } return retCode; } TA_LIB_API TA_RetCode TA_T3_Open( TA_T3_Stream **stream, const double inReal[], int historyLen, int optInTimePeriod, double optInVFactor, double *outReal ) { if( !stream ) return TA_BAD_PARAM; *stream = NULL; if( historyLen < 1 ) return TA_OUT_OF_RANGE_START_INDEX; if( historyLen > TA_INDEX_MAX + 1 ) return TA_OUT_OF_RANGE_END_INDEX; if( !inReal || !outReal ) return TA_BAD_PARAM; return TA_FMA_AVAILABLE ? TA_T3_OpenSinkFma( stream, inReal, 0, historyLen, optInTimePeriod, optInVFactor, outReal ) : TA_T3_OpenInternal( stream, inReal, 0, historyLen, optInTimePeriod, optInVFactor, outReal ); } TA_LIB_API TA_RetCode TA_T3_OpenAndFill( TA_T3_Stream **stream, const double inReal[], int historyLen, int optInTimePeriod, double optInVFactor, int *outBegIdx, int *outNBElement, double outReal[] ) { if( !stream ) return TA_BAD_PARAM; *stream = NULL; if( historyLen < 1 ) return TA_OUT_OF_RANGE_START_INDEX; if( historyLen > TA_INDEX_MAX + 1 ) return TA_OUT_OF_RANGE_END_INDEX; if( !inReal || !outBegIdx || !outNBElement || !outReal ) return TA_BAD_PARAM; if( (const void *)outReal == (const void *)inReal ) return TA_BAD_PARAM; return TA_T3_OpenAndFillInternal( stream, inReal, 0, historyLen, optInTimePeriod, optInVFactor, outBegIdx, outNBElement, outReal ); } /* Private function, not in public API. */ TA_RetCode TA_T3_OpenAndFillInternal( struct TA_T3_Stream **stream, const double inReal[], int startIdx, int historyLen, int optInTimePeriod, double optInVFactor, int *outBegIdx, int *outNBElement, double outReal[] ) { return TA_FMA_AVAILABLE ? TA_T3_OpenImplFma( stream, inReal, startIdx, historyLen, optInTimePeriod, optInVFactor, outBegIdx, outNBElement, outReal, 1 ) : TA_T3_OpenImplPlain( stream, inReal, startIdx, historyLen, optInTimePeriod, optInVFactor, outBegIdx, outNBElement, outReal, 1 ); } TA_FMA_MULTIVERSION TA_LIB_API TA_RetCode TA_T3_Update( TA_T3_Stream *stream, double inReal, double *outReal ) { if( !stream ) return TA_BAD_PARAM; if( stream->outRangeBegIdx + stream->outRangeCount > TA_INDEX_MAX ) return TA_OUT_OF_RANGE_END_INDEX; if( !outReal ) return TA_BAD_PARAM; if( !TA_IS_FINITE( inReal ) ) return TA_BAD_PARAM; TA_T3_StepImpl( stream, inReal, outReal ); stream->outRangeCount++; return TA_SUCCESS; } TA_FMA_MULTIVERSION TA_LIB_API TA_RetCode TA_T3_Peek( const TA_T3_Stream *stream, double inReal, double *outReal ) { const struct TA_T3_Stream *sp = stream; double e1; double e2; double e3; double e4; double e5; double e6; if( !stream || !outReal ) return TA_BAD_PARAM; if( !TA_IS_FINITE( inReal ) ) return TA_BAD_PARAM; e1 = sp->e1; e2 = sp->e2; e3 = sp->e3; e4 = sp->e4; e5 = sp->e5; e6 = sp->e6; if( sp->optInTimePeriod == 1 ) { *outReal= inReal; return TA_SUCCESS; } e1 = fma(sp->one_minus_k, e1, sp->k * inReal); e2 = fma(sp->one_minus_k, e2, sp->k * e1); e3 = fma(sp->one_minus_k, e3, sp->k * e2); e4 = fma(sp->one_minus_k, e4, sp->k * e3); e5 = fma(sp->one_minus_k, e5, sp->k * e4); e6 = fma(sp->one_minus_k, e6, sp->k * e5); *outReal= fma(sp->c4, e3, fma(sp->c3, e4, fma(sp->c1, e6, sp->c2 * e5))); return TA_SUCCESS; } TA_LIB_API TA_RetCode TA_T3_Close( TA_T3_Stream *stream ) { if( stream ) TA_Free( stream ); return TA_SUCCESS; } /* Private function, not in public API. */ void TA_T3_StepTape( struct TA_T3_Stream *sp, const double tape[], int tapeBase, int tapeMask, double inReal, double *outReal ) { (void)tape; (void)tapeBase; (void)tapeMask; TA_T3_StepImpl( sp, inReal, outReal ); sp->outRangeCount++; } /* Private function, not in public API. */ void TA_T3_PeekTape( const struct TA_T3_Stream *sp, const double tape[], int tapeBase, int tapeMask, double inReal, double *outReal ) { (void)tape; (void)tapeBase; (void)tapeMask; (void)TA_T3_Peek( sp, inReal, outReal ); } /* Private function, not in public API. */ int TA_T3_TapeDetach( struct TA_T3_Stream *sp ) { (void)sp; return 0; } TA_LIB_API TA_RetCode TA_T3_Value( const TA_T3_Stream *stream, double *outReal ) { if( !stream || !outReal ) return TA_BAD_PARAM; *outReal = stream->cur_outReal; return TA_SUCCESS; } TA_LIB_API TA_RetCode TA_T3_OutRange( const TA_T3_Stream *stream, int *outBegIdx, int *outNBElement ) { if( !stream || !outBegIdx || !outNBElement ) return TA_BAD_PARAM; *outBegIdx = stream->outRangeBegIdx; *outNBElement = stream->outRangeCount; return TA_SUCCESS; } TA_LIB_API TA_RetCode TA_T3_Advance( TA_T3_Stream *stream ) { if( !stream ) return TA_BAD_PARAM; if( stream->outRangeBegIdx + stream->outRangeCount > TA_INDEX_MAX ) return TA_OUT_OF_RANGE_END_INDEX; stream->outRangeCount++; return TA_SUCCESS; } TA_LIB_API TA_RetCode TA_T3_Clone( const TA_T3_Stream *stream, TA_T3_Stream **clone ) { struct TA_T3_Stream *sp; if( !clone ) return TA_BAD_PARAM; *clone = NULL; if( !stream ) return TA_BAD_PARAM; sp = (struct TA_T3_Stream *)TA_Malloc( sizeof(*sp) ); if( !sp ) return TA_ALLOC_ERR; *sp = *stream; *clone = sp; return TA_SUCCESS; }