/* 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.
*/

/* AUTO-GENERATED by ta_codegen — DO NOT EDIT.
* Source of truth: ta_codegen/input/<name>/  (regenerate: cd ta_codegen/generator && cargo run -- generate)
*/

#include <string.h>
#include <math.h>
#include "ta_func.h"
#include "ta_utility.h"
#include "ta_memory.h"
#include "ta_func_unguarded.h"

/* List of contributors:
 *
 *  Initial  Name/description
 *  -------------------------------------------------------------------
 *  MF       Mario Fortier
 *  JPP      JP Pienaar (j.pienaar@mci.co.za)
 *
 * Change history:
 *
 *  MMDDYY BY   Description
 *  -------------------------------------------------------------------
 *  112400 MF   Template creation.
 *  052603 MF   Adapt code to compile with .NET Managed C++
 *  080403 JPP  Fix #767653 for logic when swapping periods.
 */

TA_LIB_API int TA_MACD_Lookback( int optInFastPeriod, int optInSlowPeriod, int optInSignalPeriod )
{
   int tempInteger;
   if( (int)optInFastPeriod == (int)0x80000000 )
      optInFastPeriod = 12;
   else if( (int)optInFastPeriod < 2 || (int)optInFastPeriod > 100000 )
      return -1;
   if( (int)optInSlowPeriod == (int)0x80000000 )
      optInSlowPeriod = 26;
   else if( (int)optInSlowPeriod < 2 || (int)optInSlowPeriod > 100000 )
      return -1;
   if( (int)optInSignalPeriod == (int)0x80000000 )
      optInSignalPeriod = 9;
   else if( (int)optInSignalPeriod < 1 || (int)optInSignalPeriod > 100000 )
      return -1;
   /* The lookback is driven by the signal line output.
    *
    * (must also account for the initial data consume
    *  by the slow period).
    */
   /* Make sure slow is really slower than
    * the fast period! if not, swap...
    */
   if( optInSlowPeriod < optInFastPeriod )
   {
      /* swap */
      tempInteger = optInSlowPeriod;
      optInSlowPeriod = optInFastPeriod;
      optInFastPeriod = tempInteger;
   }
   return TA_EMA_Lookback(optInSlowPeriod) + TA_EMA_Lookback(optInSignalPeriod);
}

TA_LIB_API TA_RetCode TA_MACD( int    startIdx,
                               int    endIdx,
                               const double inReal[],
                               int optInFastPeriod,
                               int optInSlowPeriod,
                               int optInSignalPeriod,
                               int          *outBegIdx,
                               int          *outNBElement,
                               double        outMACD[],
                               double        outMACDSignal[],
                               double        outMACDHist[] )
{
   double *slowEMABuffer;
   double *fastEMABuffer;
   TA_RetCode retCode;
   int tempInteger;
   int outBegIdx1;
   int outNbElement1;
   int outBegIdx2;
   int outNbElement2;
   double slowK;
   double fastK;
   double signalK;
   int lookbackTotal;
   int lookbackSignal;
   int useFixedK;
   int i;

   if( startIdx < 0 )
      return TA_OUT_OF_RANGE_START_INDEX;
   if( (endIdx < 0) || (endIdx < startIdx) )
      return TA_OUT_OF_RANGE_END_INDEX;

   if( !inReal )
      return TA_BAD_PARAM;
   if( (int)optInFastPeriod == (int)0x80000000 )
      optInFastPeriod = 12;
   else if( (int)optInFastPeriod < 2 || (int)optInFastPeriod > 100000 )
      return TA_BAD_PARAM;
   if( (int)optInSlowPeriod == (int)0x80000000 )
      optInSlowPeriod = 26;
   else if( (int)optInSlowPeriod < 2 || (int)optInSlowPeriod > 100000 )
      return TA_BAD_PARAM;
   if( (int)optInSignalPeriod == (int)0x80000000 )
      optInSignalPeriod = 9;
   else if( (int)optInSignalPeriod < 1 || (int)optInSignalPeriod > 100000 )
      return TA_BAD_PARAM;
   if( !outMACD )
      return TA_BAD_PARAM;
   if( !outMACDSignal )
      return TA_BAD_PARAM;
   if( !outMACDHist )
      return TA_BAD_PARAM;

   /* !!! A lot of speed optimization could be done
    * !!! with this function.
    * !!!
    * !!! A better approach would be to use ema
    * !!! just to get the seeding values for the
    * !!! fast and slow EMA. Then process the difference
    * !!! in an allocated buffer until enough data is
    * !!! available for the first signal value.
    * !!! From that point all the processing can
    * !!! be done in a tight loop.
    * !!!
    * !!! That approach will have the following
    * !!! advantage:
    * !!!   1) One mem allocation needed instead of two.
    * !!!   2) The mem allocation size will be only the
    * !!!      signal lookback period instead of the
    * !!!      whole range of data.
    * !!!   3) Processing will be done in a tight loop.
    * !!!      allowing to avoid a lot of memory store-load
    * !!!      operation.
    * !!!   4) The memcpy at the end will be eliminated!
    * !!!
    * !!! If only I had time....
    */
   /* Make sure slow is really slower than
    * the fast period! if not, swap...
    */
   if( optInSlowPeriod < optInFastPeriod )
   {
      /* swap */
      tempInteger = optInSlowPeriod;
      optInSlowPeriod = optInFastPeriod;
      optInFastPeriod = tempInteger;
   }
   /* Catch special case for fix 26/12 MACD.
    * Use hardcoded k values matching the original algorithm.
    */
   useFixedK = 0;
   if( optInSlowPeriod == 0 )
   {
      optInSlowPeriod = 26;
      /* Fix 26 */
      slowK = 0.075;
      useFixedK = 1;
   } else 
   {
      slowK = 2.0 / (double)(optInSlowPeriod + 1);
   }
   if( optInFastPeriod == 0 )
   {
      optInFastPeriod = 12;
      /* Fix 12 */
      fastK = 0.15;
      useFixedK = 1;
   } else 
   {
      fastK = 2.0 / (double)(optInFastPeriod + 1);
   }
   signalK = 2.0 / (double)(optInSignalPeriod + 1);
   lookbackSignal = TA_EMA_Lookback(optInSignalPeriod);
   /* Move up the start index if there is not
    * enough initial data.
    */
   lookbackTotal = lookbackSignal;
   lookbackTotal += TA_EMA_Lookback(optInSlowPeriod);
   if( startIdx < lookbackTotal )
   {
      startIdx = lookbackTotal;
   }
   /* Make sure there is still something to evaluate. */
   if( startIdx > endIdx )
   {
      *outBegIdx= 0;
      *outNBElement= 0;
      return TA_SUCCESS;
   }
   /* Allocate intermediate buffer for fast/slow EMA. */
   tempInteger = endIdx - startIdx + 1 + lookbackSignal;
   fastEMABuffer = malloc(tempInteger * sizeof(double));
   if( !fastEMABuffer )
   {
      *outBegIdx= 0;
      *outNBElement= 0;
      return TA_ALLOC_ERR;
   }
   slowEMABuffer = malloc(tempInteger * sizeof(double));
   if( !slowEMABuffer )
   {
      *outBegIdx= 0;
      *outNBElement= 0;
      free(fastEMABuffer);
      return TA_ALLOC_ERR;
   }
   /* Calculate the slow EMA.
    *
    * Move back the startIdx to get enough data
    * for the signal period. That way, once the
    * signal calculation is done, all the output
    * will start at the requested 'startIdx'.
    */
   tempInteger = startIdx - lookbackSignal;
   /* Use ema_private when hardcoded k is needed (MACDFIX path).
    * Use ema() for the normal path — codegen handles double/float routing.
    */
   if( useFixedK )
   {
      retCode = TA_EMA_Private(tempInteger,endIdx,inReal,optInSlowPeriod,slowK,&outBegIdx1,&outNbElement1,slowEMABuffer);
   } else 
   {
      retCode = TA_EMA_Unguarded(tempInteger,endIdx,inReal,optInSlowPeriod,&outBegIdx1,&outNbElement1,slowEMABuffer);
   }
   if( retCode != TA_SUCCESS )
   {
      *outBegIdx= 0;
      *outNBElement= 0;
      free(fastEMABuffer);
      free(slowEMABuffer);
      return retCode;
   }
   /* Calculate the fast EMA. */
   if( useFixedK )
   {
      retCode = TA_EMA_Private(tempInteger,endIdx,inReal,optInFastPeriod,fastK,&outBegIdx2,&outNbElement2,fastEMABuffer);
   } else 
   {
      retCode = TA_EMA_Unguarded(tempInteger,endIdx,inReal,optInFastPeriod,&outBegIdx2,&outNbElement2,fastEMABuffer);
   }
   if( retCode != TA_SUCCESS )
   {
      *outBegIdx= 0;
      *outNBElement= 0;
      free(fastEMABuffer);
      free(slowEMABuffer);
      return retCode;
   }
   /* Parano tests. Will be removed eventually. */
   if( outBegIdx1 != tempInteger || outBegIdx2 != tempInteger || outNbElement1 != outNbElement2 || outNbElement1 != endIdx - startIdx + 1 + lookbackSignal )
   {
      *outBegIdx= 0;
      *outNBElement= 0;
      free(fastEMABuffer);
      free(slowEMABuffer);
      return TA_BAD_PARAM;
   }
   /* Calculate (fast EMA) - (slow EMA). */
   for( i = 0; i < outNbElement1; i += 1 )
   {
      fastEMABuffer[i] = fastEMABuffer[i] - slowEMABuffer[i];
   }
   /* Copy the result into the output for the caller. */
   memcpy(outMACD,&fastEMABuffer[lookbackSignal],(endIdx - startIdx + 1) * sizeof(double));
   /* Calculate the signal/trigger line (on double buffer, use ema_private). */
   retCode = TA_EMA_Private(0,outNbElement1 - 1,fastEMABuffer,optInSignalPeriod,signalK,&outBegIdx2,&outNbElement2,outMACDSignal);
   free(fastEMABuffer);
   free(slowEMABuffer);
   if( retCode != TA_SUCCESS )
   {
      *outBegIdx= 0;
      *outNBElement= 0;
      return retCode;
   }
   /* Calculate the histogram. */
   for( i = 0; i < outNbElement2; i += 1 )
   {
      outMACDHist[i] = outMACD[i] - outMACDSignal[i];
   }
   /* All done! Indicate the output limits and return success. */
   *outBegIdx= startIdx;
   *outNBElement= outNbElement2;
   return TA_SUCCESS;
}

TA_LIB_API TA_RetCode TA_MACD_Unguarded( int    startIdx,
                                         int    endIdx,
                                         const double inReal[],
                                         int optInFastPeriod,
                                         int optInSlowPeriod,
                                         int optInSignalPeriod,
                                         int          *outBegIdx,
                                         int          *outNBElement,
                                         double        outMACD[],
                                         double        outMACDSignal[],
                                         double        outMACDHist[] )
{
   double *slowEMABuffer;
   double *fastEMABuffer;
   TA_RetCode retCode;
   int tempInteger;
   int outBegIdx1;
   int outNbElement1;
   int outBegIdx2;
   int outNbElement2;
   double slowK;
   double fastK;
   double signalK;
   int lookbackTotal;
   int lookbackSignal;
   int useFixedK;
   int i;

   if( optInSlowPeriod < optInFastPeriod )
   {
      tempInteger = optInSlowPeriod;
      optInSlowPeriod = optInFastPeriod;
      optInFastPeriod = tempInteger;
   }
   useFixedK = 0;
   if( optInSlowPeriod == 0 )
   {
      optInSlowPeriod = 26;
      slowK = 0.075;
      useFixedK = 1;
   } else 
   {
      slowK = 2.0 / (double)(optInSlowPeriod + 1);
   }
   if( optInFastPeriod == 0 )
   {
      optInFastPeriod = 12;
      fastK = 0.15;
      useFixedK = 1;
   } else 
   {
      fastK = 2.0 / (double)(optInFastPeriod + 1);
   }
   signalK = 2.0 / (double)(optInSignalPeriod + 1);
   lookbackSignal = TA_EMA_Lookback(optInSignalPeriod);
   lookbackTotal = lookbackSignal;
   lookbackTotal += TA_EMA_Lookback(optInSlowPeriod);
   if( startIdx < lookbackTotal )
   {
      startIdx = lookbackTotal;
   }
   if( startIdx > endIdx )
   {
      *outBegIdx= 0;
      *outNBElement= 0;
      return TA_SUCCESS;
   }
   tempInteger = endIdx - startIdx + 1 + lookbackSignal;
   fastEMABuffer = malloc(tempInteger * sizeof(double));
   if( !fastEMABuffer )
   {
      *outBegIdx= 0;
      *outNBElement= 0;
      return TA_ALLOC_ERR;
   }
   slowEMABuffer = malloc(tempInteger * sizeof(double));
   if( !slowEMABuffer )
   {
      *outBegIdx= 0;
      *outNBElement= 0;
      free(fastEMABuffer);
      return TA_ALLOC_ERR;
   }
   tempInteger = startIdx - lookbackSignal;
   if( useFixedK )
   {
      retCode = TA_EMA_Private(tempInteger,endIdx,inReal,optInSlowPeriod,slowK,&outBegIdx1,&outNbElement1,slowEMABuffer);
   } else 
   {
      retCode = TA_EMA_Unguarded(tempInteger,endIdx,inReal,optInSlowPeriod,&outBegIdx1,&outNbElement1,slowEMABuffer);
   }
   if( retCode != TA_SUCCESS )
   {
      *outBegIdx= 0;
      *outNBElement= 0;
      free(fastEMABuffer);
      free(slowEMABuffer);
      return retCode;
   }
   if( useFixedK )
   {
      retCode = TA_EMA_Private(tempInteger,endIdx,inReal,optInFastPeriod,fastK,&outBegIdx2,&outNbElement2,fastEMABuffer);
   } else 
   {
      retCode = TA_EMA_Unguarded(tempInteger,endIdx,inReal,optInFastPeriod,&outBegIdx2,&outNbElement2,fastEMABuffer);
   }
   if( retCode != TA_SUCCESS )
   {
      *outBegIdx= 0;
      *outNBElement= 0;
      free(fastEMABuffer);
      free(slowEMABuffer);
      return retCode;
   }
   if( outBegIdx1 != tempInteger || outBegIdx2 != tempInteger || outNbElement1 != outNbElement2 || outNbElement1 != endIdx - startIdx + 1 + lookbackSignal )
   {
      *outBegIdx= 0;
      *outNBElement= 0;
      free(fastEMABuffer);
      free(slowEMABuffer);
      return TA_BAD_PARAM;
   }
   for( i = 0; i < outNbElement1; i += 1 )
   {
      fastEMABuffer[i] = fastEMABuffer[i] - slowEMABuffer[i];
   }
   memcpy(outMACD,&fastEMABuffer[lookbackSignal],(endIdx - startIdx + 1) * sizeof(double));
   retCode = TA_EMA_Private(0,outNbElement1 - 1,fastEMABuffer,optInSignalPeriod,signalK,&outBegIdx2,&outNbElement2,outMACDSignal);
   free(fastEMABuffer);
   free(slowEMABuffer);
   if( retCode != TA_SUCCESS )
   {
      *outBegIdx= 0;
      *outNBElement= 0;
      return retCode;
   }
   for( i = 0; i < outNbElement2; i += 1 )
   {
      outMACDHist[i] = outMACD[i] - outMACDSignal[i];
   }
   *outBegIdx= startIdx;
   *outNBElement= outNbElement2;
   return TA_SUCCESS;
}

TA_RetCode TA_S_MACD( int    startIdx,
                      int    endIdx,
                      const float inReal[],
                      int optInFastPeriod,
                      int optInSlowPeriod,
                      int optInSignalPeriod,
                      int          *outBegIdx,
                      int          *outNBElement,
                      double        outMACD[],
                      double        outMACDSignal[],
                      double        outMACDHist[] )
{
   double *slowEMABuffer;
   double *fastEMABuffer;
   TA_RetCode retCode;
   int tempInteger;
   int outBegIdx1;
   int outNbElement1;
   int outBegIdx2;
   int outNbElement2;
   double slowK;
   double fastK;
   double signalK;
   int lookbackTotal;
   int lookbackSignal;
   int useFixedK;
   int i;

   if( startIdx < 0 )
      return TA_OUT_OF_RANGE_START_INDEX;
   if( (endIdx < 0) || (endIdx < startIdx) )
      return TA_OUT_OF_RANGE_END_INDEX;

   if( !inReal )
      return TA_BAD_PARAM;
   if( (int)optInFastPeriod == (int)0x80000000 )
      optInFastPeriod = 12;
   else if( (int)optInFastPeriod < 2 || (int)optInFastPeriod > 100000 )
      return TA_BAD_PARAM;
   if( (int)optInSlowPeriod == (int)0x80000000 )
      optInSlowPeriod = 26;
   else if( (int)optInSlowPeriod < 2 || (int)optInSlowPeriod > 100000 )
      return TA_BAD_PARAM;
   if( (int)optInSignalPeriod == (int)0x80000000 )
      optInSignalPeriod = 9;
   else if( (int)optInSignalPeriod < 1 || (int)optInSignalPeriod > 100000 )
      return TA_BAD_PARAM;
   if( !outMACD )
      return TA_BAD_PARAM;
   if( !outMACDSignal )
      return TA_BAD_PARAM;
   if( !outMACDHist )
      return TA_BAD_PARAM;

   if( optInSlowPeriod < optInFastPeriod )
   {
      tempInteger = optInSlowPeriod;
      optInSlowPeriod = optInFastPeriod;
      optInFastPeriod = tempInteger;
   }
   useFixedK = 0;
   if( optInSlowPeriod == 0 )
   {
      optInSlowPeriod = 26;
      slowK = 0.075;
      useFixedK = 1;
   } else 
   {
      slowK = 2.0 / (double)(optInSlowPeriod + 1);
   }
   if( optInFastPeriod == 0 )
   {
      optInFastPeriod = 12;
      fastK = 0.15;
      useFixedK = 1;
   } else 
   {
      fastK = 2.0 / (double)(optInFastPeriod + 1);
   }
   signalK = 2.0 / (double)(optInSignalPeriod + 1);
   lookbackSignal = TA_EMA_Lookback(optInSignalPeriod);
   lookbackTotal = lookbackSignal;
   lookbackTotal += TA_EMA_Lookback(optInSlowPeriod);
   if( startIdx < lookbackTotal )
   {
      startIdx = lookbackTotal;
   }
   if( startIdx > endIdx )
   {
      *outBegIdx= 0;
      *outNBElement= 0;
      return TA_SUCCESS;
   }
   tempInteger = endIdx - startIdx + 1 + lookbackSignal;
   fastEMABuffer = malloc(tempInteger * sizeof(double));
   if( !fastEMABuffer )
   {
      *outBegIdx= 0;
      *outNBElement= 0;
      return TA_ALLOC_ERR;
   }
   slowEMABuffer = malloc(tempInteger * sizeof(double));
   if( !slowEMABuffer )
   {
      *outBegIdx= 0;
      *outNBElement= 0;
      free(fastEMABuffer);
      return TA_ALLOC_ERR;
   }
   tempInteger = startIdx - lookbackSignal;
   if( useFixedK )
   {
      retCode = TA_S_EMA_Private(tempInteger,endIdx,inReal,optInSlowPeriod,slowK,&outBegIdx1,&outNbElement1,slowEMABuffer);
   } else 
   {
      retCode = TA_S_EMA_Unguarded(tempInteger,endIdx,inReal,optInSlowPeriod,&outBegIdx1,&outNbElement1,slowEMABuffer);
   }
   if( retCode != TA_SUCCESS )
   {
      *outBegIdx= 0;
      *outNBElement= 0;
      free(fastEMABuffer);
      free(slowEMABuffer);
      return retCode;
   }
   if( useFixedK )
   {
      retCode = TA_S_EMA_Private(tempInteger,endIdx,inReal,optInFastPeriod,fastK,&outBegIdx2,&outNbElement2,fastEMABuffer);
   } else 
   {
      retCode = TA_S_EMA_Unguarded(tempInteger,endIdx,inReal,optInFastPeriod,&outBegIdx2,&outNbElement2,fastEMABuffer);
   }
   if( retCode != TA_SUCCESS )
   {
      *outBegIdx= 0;
      *outNBElement= 0;
      free(fastEMABuffer);
      free(slowEMABuffer);
      return retCode;
   }
   if( outBegIdx1 != tempInteger || outBegIdx2 != tempInteger || outNbElement1 != outNbElement2 || outNbElement1 != endIdx - startIdx + 1 + lookbackSignal )
   {
      *outBegIdx= 0;
      *outNBElement= 0;
      free(fastEMABuffer);
      free(slowEMABuffer);
      return TA_BAD_PARAM;
   }
   for( i = 0; i < outNbElement1; i += 1 )
   {
      fastEMABuffer[i] = fastEMABuffer[i] - slowEMABuffer[i];
   }
   memcpy(outMACD,&fastEMABuffer[lookbackSignal],(endIdx - startIdx + 1) * sizeof(double));
   retCode = TA_EMA_Private(0,outNbElement1 - 1,fastEMABuffer,optInSignalPeriod,signalK,&outBegIdx2,&outNbElement2,outMACDSignal);
   free(fastEMABuffer);
   free(slowEMABuffer);
   if( retCode != TA_SUCCESS )
   {
      *outBegIdx= 0;
      *outNBElement= 0;
      return retCode;
   }
   for( i = 0; i < outNbElement2; i += 1 )
   {
      outMACDHist[i] = outMACD[i] - outMACDSignal[i];
   }
   *outBegIdx= startIdx;
   *outNBElement= outNbElement2;
   return TA_SUCCESS;
}

TA_RetCode TA_S_MACD_Unguarded( int    startIdx,
                                int    endIdx,
                                const float inReal[],
                                int optInFastPeriod,
                                int optInSlowPeriod,
                                int optInSignalPeriod,
                                int          *outBegIdx,
                                int          *outNBElement,
                                double        outMACD[],
                                double        outMACDSignal[],
                                double        outMACDHist[] )
{
   double *slowEMABuffer;
   double *fastEMABuffer;
   TA_RetCode retCode;
   int tempInteger;
   int outBegIdx1;
   int outNbElement1;
   int outBegIdx2;
   int outNbElement2;
   double slowK;
   double fastK;
   double signalK;
   int lookbackTotal;
   int lookbackSignal;
   int useFixedK;
   int i;

   if( optInSlowPeriod < optInFastPeriod )
   {
      tempInteger = optInSlowPeriod;
      optInSlowPeriod = optInFastPeriod;
      optInFastPeriod = tempInteger;
   }
   useFixedK = 0;
   if( optInSlowPeriod == 0 )
   {
      optInSlowPeriod = 26;
      slowK = 0.075;
      useFixedK = 1;
   } else 
   {
      slowK = 2.0 / (double)(optInSlowPeriod + 1);
   }
   if( optInFastPeriod == 0 )
   {
      optInFastPeriod = 12;
      fastK = 0.15;
      useFixedK = 1;
   } else 
   {
      fastK = 2.0 / (double)(optInFastPeriod + 1);
   }
   signalK = 2.0 / (double)(optInSignalPeriod + 1);
   lookbackSignal = TA_EMA_Lookback(optInSignalPeriod);
   lookbackTotal = lookbackSignal;
   lookbackTotal += TA_EMA_Lookback(optInSlowPeriod);
   if( startIdx < lookbackTotal )
   {
      startIdx = lookbackTotal;
   }
   if( startIdx > endIdx )
   {
      *outBegIdx= 0;
      *outNBElement= 0;
      return TA_SUCCESS;
   }
   tempInteger = endIdx - startIdx + 1 + lookbackSignal;
   fastEMABuffer = malloc(tempInteger * sizeof(double));
   if( !fastEMABuffer )
   {
      *outBegIdx= 0;
      *outNBElement= 0;
      return TA_ALLOC_ERR;
   }
   slowEMABuffer = malloc(tempInteger * sizeof(double));
   if( !slowEMABuffer )
   {
      *outBegIdx= 0;
      *outNBElement= 0;
      free(fastEMABuffer);
      return TA_ALLOC_ERR;
   }
   tempInteger = startIdx - lookbackSignal;
   if( useFixedK )
   {
      retCode = TA_S_EMA_Private(tempInteger,endIdx,inReal,optInSlowPeriod,slowK,&outBegIdx1,&outNbElement1,slowEMABuffer);
   } else 
   {
      retCode = TA_S_EMA_Unguarded(tempInteger,endIdx,inReal,optInSlowPeriod,&outBegIdx1,&outNbElement1,slowEMABuffer);
   }
   if( retCode != TA_SUCCESS )
   {
      *outBegIdx= 0;
      *outNBElement= 0;
      free(fastEMABuffer);
      free(slowEMABuffer);
      return retCode;
   }
   if( useFixedK )
   {
      retCode = TA_S_EMA_Private(tempInteger,endIdx,inReal,optInFastPeriod,fastK,&outBegIdx2,&outNbElement2,fastEMABuffer);
   } else 
   {
      retCode = TA_S_EMA_Unguarded(tempInteger,endIdx,inReal,optInFastPeriod,&outBegIdx2,&outNbElement2,fastEMABuffer);
   }
   if( retCode != TA_SUCCESS )
   {
      *outBegIdx= 0;
      *outNBElement= 0;
      free(fastEMABuffer);
      free(slowEMABuffer);
      return retCode;
   }
   if( outBegIdx1 != tempInteger || outBegIdx2 != tempInteger || outNbElement1 != outNbElement2 || outNbElement1 != endIdx - startIdx + 1 + lookbackSignal )
   {
      *outBegIdx= 0;
      *outNBElement= 0;
      free(fastEMABuffer);
      free(slowEMABuffer);
      return TA_BAD_PARAM;
   }
   for( i = 0; i < outNbElement1; i += 1 )
   {
      fastEMABuffer[i] = fastEMABuffer[i] - slowEMABuffer[i];
   }
   memcpy(outMACD,&fastEMABuffer[lookbackSignal],(endIdx - startIdx + 1) * sizeof(double));
   retCode = TA_EMA_Private(0,outNbElement1 - 1,fastEMABuffer,optInSignalPeriod,signalK,&outBegIdx2,&outNbElement2,outMACDSignal);
   free(fastEMABuffer);
   free(slowEMABuffer);
   if( retCode != TA_SUCCESS )
   {
      *outBegIdx= 0;
      *outNBElement= 0;
      return retCode;
   }
   for( i = 0; i < outNbElement2; i += 1 )
   {
      outMACDHist[i] = outMACD[i] - outMACDSignal[i];
   }
   *outBegIdx= startIdx;
   *outNBElement= outNbElement2;
   return TA_SUCCESS;
}

