#ifndef TF_INDICATORS_REGIME_MATH_MQH
#define TF_INDICATORS_REGIME_MATH_MQH

enum TF_REGIME
  {
   TF_REGIME_TREND=1,
   TF_REGIME_RANGE=2,
   TF_REGIME_TRANSITION=3,
   TF_REGIME_SHOCK=4
  };

double TF_EfficiencyRatio(const double &close[],const int index,const int period)
  {
   double denominator=0.0;
   for(int j=0;j<period;j++) denominator+=MathAbs(close[index+j]-close[index+j+1]);
   if(denominator<=DBL_EPSILON) return(0.0);
   return(MathAbs(close[index]-close[index+period])/denominator);
  }

double TF_SignEntropy(const double &close[],const int index,const int period)
  {
   int positive=0,negative=0,zero=0;
   for(int j=0;j<period;j++)
     {
      double change=close[index+j]-close[index+j+1];
      double tolerance=DBL_EPSILON*MathMax(1.0,MathAbs(close[index+j+1]));
      if(change>tolerance) positive++;
      else if(change<-tolerance) negative++;
      else zero++;
     }
   double entropy=0.0;
   int counts[3];
   counts[0]=positive; counts[1]=negative; counts[2]=zero;
   for(int k=0;k<3;k++)
     {
      if(counts[k]==0) continue;
      double p=(double)counts[k]/period;
      entropy-=p*MathLog(p);
     }
   return(entropy/MathLog(3.0));
  }

double TF_RealizedVolatility(const double &close[],const int index,const int period)
  {
   double returns[];
   ArrayResize(returns,period);
   for(int j=0;j<period;j++)
     {
      double older=close[index+j+1];
      returns[j]=(older>0.0 && close[index+j]>0.0) ? MathLog(close[index+j]/older) : 0.0;
     }
   return(TF_SampleStdDev(returns));
  }

double TF_VolatilityRank(const double &close[],const int index)
  {
   double current=TF_RealizedVolatility(close,index,20);
   int less=0,equal=0;
   for(int j=1;j<=252;j++)
     {
      double historical=TF_RealizedVolatility(close,index+j,20);
      double tolerance=1.0e-12*MathMax(1.0,MathMax(current,historical));
      if(historical<current-tolerance) less++;
      else if(MathAbs(historical-current)<=tolerance) equal++;
     }
   return(100.0*((double)less+0.5*equal)/252.0);
  }

double TF_TrueRange(const double &high[],const double &low[],const double &close[],const int index)
  {
   double previous=close[index+1];
   return(MathMax(high[index]-low[index],MathMax(MathAbs(high[index]-previous),MathAbs(low[index]-previous))));
  }

double TF_RangeExpansion(const double &high[],const double &low[],const double &close[],const int index)
  {
   double recent[],baseline[];
   ArrayResize(recent,5);
   ArrayResize(baseline,60);
   for(int i=0;i<5;i++) recent[i]=TF_TrueRange(high,low,close,index+i);
   for(int i=0;i<60;i++) baseline[i]=TF_TrueRange(high,low,close,index+5+i);
   double denominator=TF_Median(baseline);
   if(denominator<=DBL_EPSILON) return(TF_Median(recent)<=DBL_EPSILON ? 1.0 : DBL_MAX);
   return(TF_Median(recent)/denominator);
  }

int TF_Classify(const double efficiency,const double entropy,const double vol_percentile,const double expansion)
  {
   if(vol_percentile>=90.0 || expansion>=1.8) return(TF_REGIME_SHOCK);
   if(efficiency>=0.35 && entropy<=0.90) return(TF_REGIME_TREND);
   if(efficiency<=0.20 && entropy>=0.95) return(TF_REGIME_RANGE);
   return(TF_REGIME_TRANSITION);
  }

#endif
