/* 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
 *  -------------------------------------------------------------------
 *  DM       Drew McCormack (http://www.trade-strategist.com)
 *  MF       Mario Fortier
 *
 * Change history:
 *
 *  MMDDYY BY   Description
 *  -------------------------------------------------------------------
 *  281206 DM   Initial Implementation
 *  010606 MF   Abstract local arrays. Detect divide by zero.
 */

TA_LIB_API int TA_ULTOSC_Lookback( int optInTimePeriod1, int optInTimePeriod2, int optInTimePeriod3 )
{
   int maxPeriod;
   if( (int)optInTimePeriod1 == (int)0x80000000 )
      optInTimePeriod1 = 7;
   else if( (int)optInTimePeriod1 < 1 || (int)optInTimePeriod1 > 100000 )
      return -1;
   if( (int)optInTimePeriod2 == (int)0x80000000 )
      optInTimePeriod2 = 14;
   else if( (int)optInTimePeriod2 < 1 || (int)optInTimePeriod2 > 100000 )
      return -1;
   if( (int)optInTimePeriod3 == (int)0x80000000 )
      optInTimePeriod3 = 28;
   else if( (int)optInTimePeriod3 < 1 || (int)optInTimePeriod3 > 100000 )
      return -1;
   /* Lookback for the Ultimate Oscillator is the lookback of the SMA with the longest
    * time period, plus 1 for the True Range.
    */
   maxPeriod = fmax(fmax(optInTimePeriod1,optInTimePeriod2),optInTimePeriod3);
   return TA_SMA_Lookback(maxPeriod) + 1;
}

TA_LIB_API TA_RetCode TA_ULTOSC( int    startIdx,
                                 int    endIdx,
                                 const double inHigh[],
                                 const double inLow[],
                                 const double inClose[],
                                 int optInTimePeriod1,
                                 int optInTimePeriod2,
                                 int optInTimePeriod3,
                                 int          *outBegIdx,
                                 int          *outNBElement,
                                 double        outReal[] )
{
   double a1Total;
   double a2Total;
   double a3Total;
   double b1Total;
   double b2Total;
   double b3Total;
   double trueLow;
   double trueRange;
   double closeMinusTrueLow;
   double tempDouble;
   double output;
   double tempHT;
   double tempLT;
   double tempCY;
   int lookbackTotal;
   int longestPeriod;
   int longestIndex;
   int i;
   int j;
   int today;
   int outIdx;
   int trailingIdx1;
   int trailingIdx2;
   int trailingIdx3;
   int usedFlag[3];
   int periods[3];
   int sortedPeriods[3];

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

   if( !inHigh )
      return TA_BAD_PARAM;
   if( !inLow )
      return TA_BAD_PARAM;
   if( !inClose )
      return TA_BAD_PARAM;
   if( (int)optInTimePeriod1 == (int)0x80000000 )
      optInTimePeriod1 = 7;
   else if( (int)optInTimePeriod1 < 1 || (int)optInTimePeriod1 > 100000 )
      return TA_BAD_PARAM;
   if( (int)optInTimePeriod2 == (int)0x80000000 )
      optInTimePeriod2 = 14;
   else if( (int)optInTimePeriod2 < 1 || (int)optInTimePeriod2 > 100000 )
      return TA_BAD_PARAM;
   if( (int)optInTimePeriod3 == (int)0x80000000 )
      optInTimePeriod3 = 28;
   else if( (int)optInTimePeriod3 < 1 || (int)optInTimePeriod3 > 100000 )
      return TA_BAD_PARAM;
   if( !outReal )
      return TA_BAD_PARAM;

   *outBegIdx= 0;
   *outNBElement= 0;
   /* Ensure that the time periods are ordered from shortest to longest.
    * Sort.
    */
   periods[0] = optInTimePeriod1;
   periods[1] = optInTimePeriod2;
   periods[2] = optInTimePeriod3;
   usedFlag[0] = 0;
   usedFlag[1] = 0;
   usedFlag[2] = 0;
   for( i = 0; i < 3; i += 1 )
   {
      longestPeriod = 0;
      longestIndex = 0;
      for( j = 0; j < 3; j += 1 )
      {
         if( usedFlag[j] == 0 && periods[j] > longestPeriod )
         {
            longestPeriod = periods[j];
            longestIndex = j;
         }
      }
      usedFlag[longestIndex] = 1;
      sortedPeriods[i] = longestPeriod;
   }
   optInTimePeriod1 = sortedPeriods[2];
   optInTimePeriod2 = sortedPeriods[1];
   optInTimePeriod3 = sortedPeriods[0];
   /* Adjust startIdx for lookback period. */
   lookbackTotal = TA_ULTOSC_Lookback(optInTimePeriod1,optInTimePeriod2,optInTimePeriod3);
   if( startIdx < lookbackTotal )
   {
      startIdx = lookbackTotal;
   }
   /* Make sure there is still something to evaluate. */
   if( startIdx > endIdx )
   {
      return TA_SUCCESS;
   }
   /* Prime running totals used in moving averages */
   a1Total = 0;
   b1Total = 0;
   for( i = startIdx - optInTimePeriod1 + 1; i < startIdx; i += 1 )
   {
      tempLT = inLow[i];
      tempHT = inHigh[i];
      tempCY = inClose[i - 1];
      trueLow = fmin(tempLT,tempCY);
      closeMinusTrueLow = inClose[i] - trueLow;
      trueRange = tempHT - tempLT;
      tempDouble = fabs(tempCY - tempHT);
      if( tempDouble > trueRange )
      {
         trueRange = tempDouble;
      }
      tempDouble = fabs(tempCY - tempLT);
      if( tempDouble > trueRange )
      {
         trueRange = tempDouble;
      }
      a1Total += closeMinusTrueLow;
      b1Total += trueRange;
   }
   a2Total = 0;
   b2Total = 0;
   for( i = startIdx - optInTimePeriod2 + 1; i < startIdx; i += 1 )
   {
      tempLT = inLow[i];
      tempHT = inHigh[i];
      tempCY = inClose[i - 1];
      trueLow = fmin(tempLT,tempCY);
      closeMinusTrueLow = inClose[i] - trueLow;
      trueRange = tempHT - tempLT;
      tempDouble = fabs(tempCY - tempHT);
      if( tempDouble > trueRange )
      {
         trueRange = tempDouble;
      }
      tempDouble = fabs(tempCY - tempLT);
      if( tempDouble > trueRange )
      {
         trueRange = tempDouble;
      }
      a2Total += closeMinusTrueLow;
      b2Total += trueRange;
   }
   a3Total = 0;
   b3Total = 0;
   for( i = startIdx - optInTimePeriod3 + 1; i < startIdx; i += 1 )
   {
      tempLT = inLow[i];
      tempHT = inHigh[i];
      tempCY = inClose[i - 1];
      trueLow = fmin(tempLT,tempCY);
      closeMinusTrueLow = inClose[i] - trueLow;
      trueRange = tempHT - tempLT;
      tempDouble = fabs(tempCY - tempHT);
      if( tempDouble > trueRange )
      {
         trueRange = tempDouble;
      }
      tempDouble = fabs(tempCY - tempLT);
      if( tempDouble > trueRange )
      {
         trueRange = tempDouble;
      }
      a3Total += closeMinusTrueLow;
      b3Total += trueRange;
   }
   /* Calculate oscillator */
   today = startIdx;
   outIdx = 0;
   trailingIdx1 = today - optInTimePeriod1 + 1;
   trailingIdx2 = today - optInTimePeriod2 + 1;
   trailingIdx3 = today - optInTimePeriod3 + 1;
   while( today <= endIdx )
   {
      /* Add on today's terms */
      tempLT = inLow[today];
      tempHT = inHigh[today];
      tempCY = inClose[today - 1];
      trueLow = fmin(tempLT,tempCY);
      closeMinusTrueLow = inClose[today] - trueLow;
      trueRange = tempHT - tempLT;
      tempDouble = fabs(tempCY - tempHT);
      if( tempDouble > trueRange )
      {
         trueRange = tempDouble;
      }
      tempDouble = fabs(tempCY - tempLT);
      if( tempDouble > trueRange )
      {
         trueRange = tempDouble;
      }
      a1Total += closeMinusTrueLow;
      a2Total += closeMinusTrueLow;
      a3Total += closeMinusTrueLow;
      b1Total += trueRange;
      b2Total += trueRange;
      b3Total += trueRange;
      /* Calculate the oscillator value for today */
      output = 0.0;
      if( !TA_IS_ZERO(b1Total) )
      {
         output += 4.0 * (a1Total / b1Total);
      }
      if( !TA_IS_ZERO(b2Total) )
      {
         output += 2.0 * (a2Total / b2Total);
      }
      if( !TA_IS_ZERO(b3Total) )
      {
         output += a3Total / b3Total;
      }
      /* Remove the trailing terms to prepare for next day */
      tempLT = inLow[trailingIdx1];
      tempHT = inHigh[trailingIdx1];
      tempCY = inClose[trailingIdx1 - 1];
      trueLow = fmin(tempLT,tempCY);
      closeMinusTrueLow = inClose[trailingIdx1] - trueLow;
      trueRange = tempHT - tempLT;
      tempDouble = fabs(tempCY - tempHT);
      if( tempDouble > trueRange )
      {
         trueRange = tempDouble;
      }
      tempDouble = fabs(tempCY - tempLT);
      if( tempDouble > trueRange )
      {
         trueRange = tempDouble;
      }
      a1Total -= closeMinusTrueLow;
      b1Total -= trueRange;
      tempLT = inLow[trailingIdx2];
      tempHT = inHigh[trailingIdx2];
      tempCY = inClose[trailingIdx2 - 1];
      trueLow = fmin(tempLT,tempCY);
      closeMinusTrueLow = inClose[trailingIdx2] - trueLow;
      trueRange = tempHT - tempLT;
      tempDouble = fabs(tempCY - tempHT);
      if( tempDouble > trueRange )
      {
         trueRange = tempDouble;
      }
      tempDouble = fabs(tempCY - tempLT);
      if( tempDouble > trueRange )
      {
         trueRange = tempDouble;
      }
      a2Total -= closeMinusTrueLow;
      b2Total -= trueRange;
      tempLT = inLow[trailingIdx3];
      tempHT = inHigh[trailingIdx3];
      tempCY = inClose[trailingIdx3 - 1];
      trueLow = fmin(tempLT,tempCY);
      closeMinusTrueLow = inClose[trailingIdx3] - trueLow;
      trueRange = tempHT - tempLT;
      tempDouble = fabs(tempCY - tempHT);
      if( tempDouble > trueRange )
      {
         trueRange = tempDouble;
      }
      tempDouble = fabs(tempCY - tempLT);
      if( tempDouble > trueRange )
      {
         trueRange = tempDouble;
      }
      a3Total -= closeMinusTrueLow;
      b3Total -= trueRange;
      /* Last operation is to write the output. Must
       * be done after the trailing index have all been
       * taken care of because the caller is allowed
       * to have the input array to be also the output
       * array.
       */
      outReal[outIdx] = 100.0 * (output / 7.0);
      /* Increment indexes */
      outIdx += 1;
      today += 1;
      trailingIdx1 += 1;
      trailingIdx2 += 1;
      trailingIdx3 += 1;
   }
   /* All done. Indicate the output limits and return. */
   *outNBElement= outIdx;
   *outBegIdx= startIdx;
   return TA_SUCCESS;
}

TA_LIB_API TA_RetCode TA_ULTOSC_Unguarded( int    startIdx,
                                           int    endIdx,
                                           const double inHigh[],
                                           const double inLow[],
                                           const double inClose[],
                                           int optInTimePeriod1,
                                           int optInTimePeriod2,
                                           int optInTimePeriod3,
                                           int          *outBegIdx,
                                           int          *outNBElement,
                                           double        outReal[] )
{
   double a1Total;
   double a2Total;
   double a3Total;
   double b1Total;
   double b2Total;
   double b3Total;
   double trueLow;
   double trueRange;
   double closeMinusTrueLow;
   double tempDouble;
   double output;
   double tempHT;
   double tempLT;
   double tempCY;
   int lookbackTotal;
   int longestPeriod;
   int longestIndex;
   int i;
   int j;
   int today;
   int outIdx;
   int trailingIdx1;
   int trailingIdx2;
   int trailingIdx3;
   int usedFlag[3];
   int periods[3];
   int sortedPeriods[3];

   *outBegIdx= 0;
   *outNBElement= 0;
   periods[0] = optInTimePeriod1;
   periods[1] = optInTimePeriod2;
   periods[2] = optInTimePeriod3;
   usedFlag[0] = 0;
   usedFlag[1] = 0;
   usedFlag[2] = 0;
   for( i = 0; i < 3; i += 1 )
   {
      longestPeriod = 0;
      longestIndex = 0;
      for( j = 0; j < 3; j += 1 )
      {
         if( usedFlag[j] == 0 && periods[j] > longestPeriod )
         {
            longestPeriod = periods[j];
            longestIndex = j;
         }
      }
      usedFlag[longestIndex] = 1;
      sortedPeriods[i] = longestPeriod;
   }
   optInTimePeriod1 = sortedPeriods[2];
   optInTimePeriod2 = sortedPeriods[1];
   optInTimePeriod3 = sortedPeriods[0];
   lookbackTotal = TA_ULTOSC_Lookback(optInTimePeriod1,optInTimePeriod2,optInTimePeriod3);
   if( startIdx < lookbackTotal )
   {
      startIdx = lookbackTotal;
   }
   if( startIdx > endIdx )
   {
      return TA_SUCCESS;
   }
   a1Total = 0;
   b1Total = 0;
   for( i = startIdx - optInTimePeriod1 + 1; i < startIdx; i += 1 )
   {
      tempLT = inLow[i];
      tempHT = inHigh[i];
      tempCY = inClose[i - 1];
      trueLow = fmin(tempLT,tempCY);
      closeMinusTrueLow = inClose[i] - trueLow;
      trueRange = tempHT - tempLT;
      tempDouble = fabs(tempCY - tempHT);
      if( tempDouble > trueRange )
      {
         trueRange = tempDouble;
      }
      tempDouble = fabs(tempCY - tempLT);
      if( tempDouble > trueRange )
      {
         trueRange = tempDouble;
      }
      a1Total += closeMinusTrueLow;
      b1Total += trueRange;
   }
   a2Total = 0;
   b2Total = 0;
   for( i = startIdx - optInTimePeriod2 + 1; i < startIdx; i += 1 )
   {
      tempLT = inLow[i];
      tempHT = inHigh[i];
      tempCY = inClose[i - 1];
      trueLow = fmin(tempLT,tempCY);
      closeMinusTrueLow = inClose[i] - trueLow;
      trueRange = tempHT - tempLT;
      tempDouble = fabs(tempCY - tempHT);
      if( tempDouble > trueRange )
      {
         trueRange = tempDouble;
      }
      tempDouble = fabs(tempCY - tempLT);
      if( tempDouble > trueRange )
      {
         trueRange = tempDouble;
      }
      a2Total += closeMinusTrueLow;
      b2Total += trueRange;
   }
   a3Total = 0;
   b3Total = 0;
   for( i = startIdx - optInTimePeriod3 + 1; i < startIdx; i += 1 )
   {
      tempLT = inLow[i];
      tempHT = inHigh[i];
      tempCY = inClose[i - 1];
      trueLow = fmin(tempLT,tempCY);
      closeMinusTrueLow = inClose[i] - trueLow;
      trueRange = tempHT - tempLT;
      tempDouble = fabs(tempCY - tempHT);
      if( tempDouble > trueRange )
      {
         trueRange = tempDouble;
      }
      tempDouble = fabs(tempCY - tempLT);
      if( tempDouble > trueRange )
      {
         trueRange = tempDouble;
      }
      a3Total += closeMinusTrueLow;
      b3Total += trueRange;
   }
   today = startIdx;
   outIdx = 0;
   trailingIdx1 = today - optInTimePeriod1 + 1;
   trailingIdx2 = today - optInTimePeriod2 + 1;
   trailingIdx3 = today - optInTimePeriod3 + 1;
   while( today <= endIdx )
   {
      tempLT = inLow[today];
      tempHT = inHigh[today];
      tempCY = inClose[today - 1];
      trueLow = fmin(tempLT,tempCY);
      closeMinusTrueLow = inClose[today] - trueLow;
      trueRange = tempHT - tempLT;
      tempDouble = fabs(tempCY - tempHT);
      if( tempDouble > trueRange )
      {
         trueRange = tempDouble;
      }
      tempDouble = fabs(tempCY - tempLT);
      if( tempDouble > trueRange )
      {
         trueRange = tempDouble;
      }
      a1Total += closeMinusTrueLow;
      a2Total += closeMinusTrueLow;
      a3Total += closeMinusTrueLow;
      b1Total += trueRange;
      b2Total += trueRange;
      b3Total += trueRange;
      output = 0.0;
      if( !TA_IS_ZERO(b1Total) )
      {
         output += 4.0 * (a1Total / b1Total);
      }
      if( !TA_IS_ZERO(b2Total) )
      {
         output += 2.0 * (a2Total / b2Total);
      }
      if( !TA_IS_ZERO(b3Total) )
      {
         output += a3Total / b3Total;
      }
      tempLT = inLow[trailingIdx1];
      tempHT = inHigh[trailingIdx1];
      tempCY = inClose[trailingIdx1 - 1];
      trueLow = fmin(tempLT,tempCY);
      closeMinusTrueLow = inClose[trailingIdx1] - trueLow;
      trueRange = tempHT - tempLT;
      tempDouble = fabs(tempCY - tempHT);
      if( tempDouble > trueRange )
      {
         trueRange = tempDouble;
      }
      tempDouble = fabs(tempCY - tempLT);
      if( tempDouble > trueRange )
      {
         trueRange = tempDouble;
      }
      a1Total -= closeMinusTrueLow;
      b1Total -= trueRange;
      tempLT = inLow[trailingIdx2];
      tempHT = inHigh[trailingIdx2];
      tempCY = inClose[trailingIdx2 - 1];
      trueLow = fmin(tempLT,tempCY);
      closeMinusTrueLow = inClose[trailingIdx2] - trueLow;
      trueRange = tempHT - tempLT;
      tempDouble = fabs(tempCY - tempHT);
      if( tempDouble > trueRange )
      {
         trueRange = tempDouble;
      }
      tempDouble = fabs(tempCY - tempLT);
      if( tempDouble > trueRange )
      {
         trueRange = tempDouble;
      }
      a2Total -= closeMinusTrueLow;
      b2Total -= trueRange;
      tempLT = inLow[trailingIdx3];
      tempHT = inHigh[trailingIdx3];
      tempCY = inClose[trailingIdx3 - 1];
      trueLow = fmin(tempLT,tempCY);
      closeMinusTrueLow = inClose[trailingIdx3] - trueLow;
      trueRange = tempHT - tempLT;
      tempDouble = fabs(tempCY - tempHT);
      if( tempDouble > trueRange )
      {
         trueRange = tempDouble;
      }
      tempDouble = fabs(tempCY - tempLT);
      if( tempDouble > trueRange )
      {
         trueRange = tempDouble;
      }
      a3Total -= closeMinusTrueLow;
      b3Total -= trueRange;
      outReal[outIdx] = 100.0 * (output / 7.0);
      outIdx += 1;
      today += 1;
      trailingIdx1 += 1;
      trailingIdx2 += 1;
      trailingIdx3 += 1;
   }
   *outNBElement= outIdx;
   *outBegIdx= startIdx;
   return TA_SUCCESS;
}

TA_RetCode TA_S_ULTOSC( int    startIdx,
                        int    endIdx,
                        const float inHigh[],
                        const float inLow[],
                        const float inClose[],
                        int optInTimePeriod1,
                        int optInTimePeriod2,
                        int optInTimePeriod3,
                        int          *outBegIdx,
                        int          *outNBElement,
                        double        outReal[] )
{
   double a1Total;
   double a2Total;
   double a3Total;
   double b1Total;
   double b2Total;
   double b3Total;
   double trueLow;
   double trueRange;
   double closeMinusTrueLow;
   double tempDouble;
   double output;
   double tempHT;
   double tempLT;
   double tempCY;
   int lookbackTotal;
   int longestPeriod;
   int longestIndex;
   int i;
   int j;
   int today;
   int outIdx;
   int trailingIdx1;
   int trailingIdx2;
   int trailingIdx3;
   int usedFlag[3];
   int periods[3];
   int sortedPeriods[3];

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

   if( !inHigh )
      return TA_BAD_PARAM;
   if( !inLow )
      return TA_BAD_PARAM;
   if( !inClose )
      return TA_BAD_PARAM;
   if( (int)optInTimePeriod1 == (int)0x80000000 )
      optInTimePeriod1 = 7;
   else if( (int)optInTimePeriod1 < 1 || (int)optInTimePeriod1 > 100000 )
      return TA_BAD_PARAM;
   if( (int)optInTimePeriod2 == (int)0x80000000 )
      optInTimePeriod2 = 14;
   else if( (int)optInTimePeriod2 < 1 || (int)optInTimePeriod2 > 100000 )
      return TA_BAD_PARAM;
   if( (int)optInTimePeriod3 == (int)0x80000000 )
      optInTimePeriod3 = 28;
   else if( (int)optInTimePeriod3 < 1 || (int)optInTimePeriod3 > 100000 )
      return TA_BAD_PARAM;
   if( !outReal )
      return TA_BAD_PARAM;

   *outBegIdx= 0;
   *outNBElement= 0;
   periods[0] = optInTimePeriod1;
   periods[1] = optInTimePeriod2;
   periods[2] = optInTimePeriod3;
   usedFlag[0] = 0;
   usedFlag[1] = 0;
   usedFlag[2] = 0;
   for( i = 0; i < 3; i += 1 )
   {
      longestPeriod = 0;
      longestIndex = 0;
      for( j = 0; j < 3; j += 1 )
      {
         if( usedFlag[j] == 0 && periods[j] > longestPeriod )
         {
            longestPeriod = periods[j];
            longestIndex = j;
         }
      }
      usedFlag[longestIndex] = 1;
      sortedPeriods[i] = longestPeriod;
   }
   optInTimePeriod1 = sortedPeriods[2];
   optInTimePeriod2 = sortedPeriods[1];
   optInTimePeriod3 = sortedPeriods[0];
   lookbackTotal = TA_ULTOSC_Lookback(optInTimePeriod1,optInTimePeriod2,optInTimePeriod3);
   if( startIdx < lookbackTotal )
   {
      startIdx = lookbackTotal;
   }
   if( startIdx > endIdx )
   {
      return TA_SUCCESS;
   }
   a1Total = 0;
   b1Total = 0;
   for( i = startIdx - optInTimePeriod1 + 1; i < startIdx; i += 1 )
   {
      tempLT = inLow[i];
      tempHT = inHigh[i];
      tempCY = inClose[i - 1];
      trueLow = fmin(tempLT,tempCY);
      closeMinusTrueLow = inClose[i] - trueLow;
      trueRange = tempHT - tempLT;
      tempDouble = fabs(tempCY - tempHT);
      if( tempDouble > trueRange )
      {
         trueRange = tempDouble;
      }
      tempDouble = fabs(tempCY - tempLT);
      if( tempDouble > trueRange )
      {
         trueRange = tempDouble;
      }
      a1Total += closeMinusTrueLow;
      b1Total += trueRange;
   }
   a2Total = 0;
   b2Total = 0;
   for( i = startIdx - optInTimePeriod2 + 1; i < startIdx; i += 1 )
   {
      tempLT = inLow[i];
      tempHT = inHigh[i];
      tempCY = inClose[i - 1];
      trueLow = fmin(tempLT,tempCY);
      closeMinusTrueLow = inClose[i] - trueLow;
      trueRange = tempHT - tempLT;
      tempDouble = fabs(tempCY - tempHT);
      if( tempDouble > trueRange )
      {
         trueRange = tempDouble;
      }
      tempDouble = fabs(tempCY - tempLT);
      if( tempDouble > trueRange )
      {
         trueRange = tempDouble;
      }
      a2Total += closeMinusTrueLow;
      b2Total += trueRange;
   }
   a3Total = 0;
   b3Total = 0;
   for( i = startIdx - optInTimePeriod3 + 1; i < startIdx; i += 1 )
   {
      tempLT = inLow[i];
      tempHT = inHigh[i];
      tempCY = inClose[i - 1];
      trueLow = fmin(tempLT,tempCY);
      closeMinusTrueLow = inClose[i] - trueLow;
      trueRange = tempHT - tempLT;
      tempDouble = fabs(tempCY - tempHT);
      if( tempDouble > trueRange )
      {
         trueRange = tempDouble;
      }
      tempDouble = fabs(tempCY - tempLT);
      if( tempDouble > trueRange )
      {
         trueRange = tempDouble;
      }
      a3Total += closeMinusTrueLow;
      b3Total += trueRange;
   }
   today = startIdx;
   outIdx = 0;
   trailingIdx1 = today - optInTimePeriod1 + 1;
   trailingIdx2 = today - optInTimePeriod2 + 1;
   trailingIdx3 = today - optInTimePeriod3 + 1;
   while( today <= endIdx )
   {
      tempLT = inLow[today];
      tempHT = inHigh[today];
      tempCY = inClose[today - 1];
      trueLow = fmin(tempLT,tempCY);
      closeMinusTrueLow = inClose[today] - trueLow;
      trueRange = tempHT - tempLT;
      tempDouble = fabs(tempCY - tempHT);
      if( tempDouble > trueRange )
      {
         trueRange = tempDouble;
      }
      tempDouble = fabs(tempCY - tempLT);
      if( tempDouble > trueRange )
      {
         trueRange = tempDouble;
      }
      a1Total += closeMinusTrueLow;
      a2Total += closeMinusTrueLow;
      a3Total += closeMinusTrueLow;
      b1Total += trueRange;
      b2Total += trueRange;
      b3Total += trueRange;
      output = 0.0;
      if( !TA_IS_ZERO(b1Total) )
      {
         output += 4.0 * (a1Total / b1Total);
      }
      if( !TA_IS_ZERO(b2Total) )
      {
         output += 2.0 * (a2Total / b2Total);
      }
      if( !TA_IS_ZERO(b3Total) )
      {
         output += a3Total / b3Total;
      }
      tempLT = inLow[trailingIdx1];
      tempHT = inHigh[trailingIdx1];
      tempCY = inClose[trailingIdx1 - 1];
      trueLow = fmin(tempLT,tempCY);
      closeMinusTrueLow = inClose[trailingIdx1] - trueLow;
      trueRange = tempHT - tempLT;
      tempDouble = fabs(tempCY - tempHT);
      if( tempDouble > trueRange )
      {
         trueRange = tempDouble;
      }
      tempDouble = fabs(tempCY - tempLT);
      if( tempDouble > trueRange )
      {
         trueRange = tempDouble;
      }
      a1Total -= closeMinusTrueLow;
      b1Total -= trueRange;
      tempLT = inLow[trailingIdx2];
      tempHT = inHigh[trailingIdx2];
      tempCY = inClose[trailingIdx2 - 1];
      trueLow = fmin(tempLT,tempCY);
      closeMinusTrueLow = inClose[trailingIdx2] - trueLow;
      trueRange = tempHT - tempLT;
      tempDouble = fabs(tempCY - tempHT);
      if( tempDouble > trueRange )
      {
         trueRange = tempDouble;
      }
      tempDouble = fabs(tempCY - tempLT);
      if( tempDouble > trueRange )
      {
         trueRange = tempDouble;
      }
      a2Total -= closeMinusTrueLow;
      b2Total -= trueRange;
      tempLT = inLow[trailingIdx3];
      tempHT = inHigh[trailingIdx3];
      tempCY = inClose[trailingIdx3 - 1];
      trueLow = fmin(tempLT,tempCY);
      closeMinusTrueLow = inClose[trailingIdx3] - trueLow;
      trueRange = tempHT - tempLT;
      tempDouble = fabs(tempCY - tempHT);
      if( tempDouble > trueRange )
      {
         trueRange = tempDouble;
      }
      tempDouble = fabs(tempCY - tempLT);
      if( tempDouble > trueRange )
      {
         trueRange = tempDouble;
      }
      a3Total -= closeMinusTrueLow;
      b3Total -= trueRange;
      outReal[outIdx] = 100.0 * (output / 7.0);
      outIdx += 1;
      today += 1;
      trailingIdx1 += 1;
      trailingIdx2 += 1;
      trailingIdx3 += 1;
   }
   *outNBElement= outIdx;
   *outBegIdx= startIdx;
   return TA_SUCCESS;
}

TA_RetCode TA_S_ULTOSC_Unguarded( int    startIdx,
                                  int    endIdx,
                                  const float inHigh[],
                                  const float inLow[],
                                  const float inClose[],
                                  int optInTimePeriod1,
                                  int optInTimePeriod2,
                                  int optInTimePeriod3,
                                  int          *outBegIdx,
                                  int          *outNBElement,
                                  double        outReal[] )
{
   double a1Total;
   double a2Total;
   double a3Total;
   double b1Total;
   double b2Total;
   double b3Total;
   double trueLow;
   double trueRange;
   double closeMinusTrueLow;
   double tempDouble;
   double output;
   double tempHT;
   double tempLT;
   double tempCY;
   int lookbackTotal;
   int longestPeriod;
   int longestIndex;
   int i;
   int j;
   int today;
   int outIdx;
   int trailingIdx1;
   int trailingIdx2;
   int trailingIdx3;
   int usedFlag[3];
   int periods[3];
   int sortedPeriods[3];

   *outBegIdx= 0;
   *outNBElement= 0;
   periods[0] = optInTimePeriod1;
   periods[1] = optInTimePeriod2;
   periods[2] = optInTimePeriod3;
   usedFlag[0] = 0;
   usedFlag[1] = 0;
   usedFlag[2] = 0;
   for( i = 0; i < 3; i += 1 )
   {
      longestPeriod = 0;
      longestIndex = 0;
      for( j = 0; j < 3; j += 1 )
      {
         if( usedFlag[j] == 0 && periods[j] > longestPeriod )
         {
            longestPeriod = periods[j];
            longestIndex = j;
         }
      }
      usedFlag[longestIndex] = 1;
      sortedPeriods[i] = longestPeriod;
   }
   optInTimePeriod1 = sortedPeriods[2];
   optInTimePeriod2 = sortedPeriods[1];
   optInTimePeriod3 = sortedPeriods[0];
   lookbackTotal = TA_ULTOSC_Lookback(optInTimePeriod1,optInTimePeriod2,optInTimePeriod3);
   if( startIdx < lookbackTotal )
   {
      startIdx = lookbackTotal;
   }
   if( startIdx > endIdx )
   {
      return TA_SUCCESS;
   }
   a1Total = 0;
   b1Total = 0;
   for( i = startIdx - optInTimePeriod1 + 1; i < startIdx; i += 1 )
   {
      tempLT = inLow[i];
      tempHT = inHigh[i];
      tempCY = inClose[i - 1];
      trueLow = fmin(tempLT,tempCY);
      closeMinusTrueLow = inClose[i] - trueLow;
      trueRange = tempHT - tempLT;
      tempDouble = fabs(tempCY - tempHT);
      if( tempDouble > trueRange )
      {
         trueRange = tempDouble;
      }
      tempDouble = fabs(tempCY - tempLT);
      if( tempDouble > trueRange )
      {
         trueRange = tempDouble;
      }
      a1Total += closeMinusTrueLow;
      b1Total += trueRange;
   }
   a2Total = 0;
   b2Total = 0;
   for( i = startIdx - optInTimePeriod2 + 1; i < startIdx; i += 1 )
   {
      tempLT = inLow[i];
      tempHT = inHigh[i];
      tempCY = inClose[i - 1];
      trueLow = fmin(tempLT,tempCY);
      closeMinusTrueLow = inClose[i] - trueLow;
      trueRange = tempHT - tempLT;
      tempDouble = fabs(tempCY - tempHT);
      if( tempDouble > trueRange )
      {
         trueRange = tempDouble;
      }
      tempDouble = fabs(tempCY - tempLT);
      if( tempDouble > trueRange )
      {
         trueRange = tempDouble;
      }
      a2Total += closeMinusTrueLow;
      b2Total += trueRange;
   }
   a3Total = 0;
   b3Total = 0;
   for( i = startIdx - optInTimePeriod3 + 1; i < startIdx; i += 1 )
   {
      tempLT = inLow[i];
      tempHT = inHigh[i];
      tempCY = inClose[i - 1];
      trueLow = fmin(tempLT,tempCY);
      closeMinusTrueLow = inClose[i] - trueLow;
      trueRange = tempHT - tempLT;
      tempDouble = fabs(tempCY - tempHT);
      if( tempDouble > trueRange )
      {
         trueRange = tempDouble;
      }
      tempDouble = fabs(tempCY - tempLT);
      if( tempDouble > trueRange )
      {
         trueRange = tempDouble;
      }
      a3Total += closeMinusTrueLow;
      b3Total += trueRange;
   }
   today = startIdx;
   outIdx = 0;
   trailingIdx1 = today - optInTimePeriod1 + 1;
   trailingIdx2 = today - optInTimePeriod2 + 1;
   trailingIdx3 = today - optInTimePeriod3 + 1;
   while( today <= endIdx )
   {
      tempLT = inLow[today];
      tempHT = inHigh[today];
      tempCY = inClose[today - 1];
      trueLow = fmin(tempLT,tempCY);
      closeMinusTrueLow = inClose[today] - trueLow;
      trueRange = tempHT - tempLT;
      tempDouble = fabs(tempCY - tempHT);
      if( tempDouble > trueRange )
      {
         trueRange = tempDouble;
      }
      tempDouble = fabs(tempCY - tempLT);
      if( tempDouble > trueRange )
      {
         trueRange = tempDouble;
      }
      a1Total += closeMinusTrueLow;
      a2Total += closeMinusTrueLow;
      a3Total += closeMinusTrueLow;
      b1Total += trueRange;
      b2Total += trueRange;
      b3Total += trueRange;
      output = 0.0;
      if( !TA_IS_ZERO(b1Total) )
      {
         output += 4.0 * (a1Total / b1Total);
      }
      if( !TA_IS_ZERO(b2Total) )
      {
         output += 2.0 * (a2Total / b2Total);
      }
      if( !TA_IS_ZERO(b3Total) )
      {
         output += a3Total / b3Total;
      }
      tempLT = inLow[trailingIdx1];
      tempHT = inHigh[trailingIdx1];
      tempCY = inClose[trailingIdx1 - 1];
      trueLow = fmin(tempLT,tempCY);
      closeMinusTrueLow = inClose[trailingIdx1] - trueLow;
      trueRange = tempHT - tempLT;
      tempDouble = fabs(tempCY - tempHT);
      if( tempDouble > trueRange )
      {
         trueRange = tempDouble;
      }
      tempDouble = fabs(tempCY - tempLT);
      if( tempDouble > trueRange )
      {
         trueRange = tempDouble;
      }
      a1Total -= closeMinusTrueLow;
      b1Total -= trueRange;
      tempLT = inLow[trailingIdx2];
      tempHT = inHigh[trailingIdx2];
      tempCY = inClose[trailingIdx2 - 1];
      trueLow = fmin(tempLT,tempCY);
      closeMinusTrueLow = inClose[trailingIdx2] - trueLow;
      trueRange = tempHT - tempLT;
      tempDouble = fabs(tempCY - tempHT);
      if( tempDouble > trueRange )
      {
         trueRange = tempDouble;
      }
      tempDouble = fabs(tempCY - tempLT);
      if( tempDouble > trueRange )
      {
         trueRange = tempDouble;
      }
      a2Total -= closeMinusTrueLow;
      b2Total -= trueRange;
      tempLT = inLow[trailingIdx3];
      tempHT = inHigh[trailingIdx3];
      tempCY = inClose[trailingIdx3 - 1];
      trueLow = fmin(tempLT,tempCY);
      closeMinusTrueLow = inClose[trailingIdx3] - trueLow;
      trueRange = tempHT - tempLT;
      tempDouble = fabs(tempCY - tempHT);
      if( tempDouble > trueRange )
      {
         trueRange = tempDouble;
      }
      tempDouble = fabs(tempCY - tempLT);
      if( tempDouble > trueRange )
      {
         trueRange = tempDouble;
      }
      a3Total -= closeMinusTrueLow;
      b3Total -= trueRange;
      outReal[outIdx] = 100.0 * (output / 7.0);
      outIdx += 1;
      today += 1;
      trailingIdx1 += 1;
      trailingIdx2 += 1;
      trailingIdx3 += 1;
   }
   *outNBElement= outIdx;
   *outBegIdx= startIdx;
   return TA_SUCCESS;
}

