//+------------------------------------------------------------------+
//|                                                    Introsort.mqh |
//|                             Copyright 2000-2026, MetaQuotes Ltd. |
//|                                                     www.mql5.com |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Struct Introsort<TKey,TItem>.                                    |
//| Usage: Used by the sort methods for instances of array.          |
//+------------------------------------------------------------------+
template<typename TKey,typename TItem>
struct Introsort
  {
public:
   IComparer<TKey>*  comparer;
   TKey              keys[];
   TItem             items[];

                     Introsort(void) {}
                    ~Introsort(void) {}
   //--- method for sort array
   void              Sort(const int index,const int length);
private:
   //--- methods for introspective sorting
   void              IntroSort(const int lo,const int hi,int depthLimit);
   int               PickPivotAndPartition(const int lo,const int hi);
   void              InsertionSort(const int lo,const int hi);
   //--- methods for heap sorting
   void              Heapsort(const int lo,const int hi);
   void              DownHeap(const int i,const int n,const int lo);
   //--- swap methods
   void              SwapIfGreaterWithItems(const int a,const int b);
   void              Swap(const int i,const int j);
   //--- service methods
   int               FloorLog2(int n) const;
  };
//+------------------------------------------------------------------+
//| IntrospectiveSort is a hybrid sorting algorithm that provides    |
//| both fast average performance and (asymptotically) optimal       |
//| worst-case performance. It begins with quicksort and switches to |
//| heapsort when the recursion depth exceeds a level based on the   |
//| number of elements being sorted.                                 |
//+------------------------------------------------------------------+
template<typename TKey,typename TItem>
void Introsort::Sort(const int index,const int length)
  {
   if(length<2)
      return;
   IntroSort(index,length+index-1,2*FloorLog2(ArraySize(keys)));
  }
//+------------------------------------------------------------------+
//| Exchanges the values of a and b, if a greater b.                 |
//+------------------------------------------------------------------+
template<typename TKey,typename TItem>
void Introsort::SwapIfGreaterWithItems(const int a,const int b)
  {
   if(a!=b)
     {
      if(comparer.Compare(keys[a],keys[b])>0)
        {
         TKey key=keys[a];
         keys[a]=keys[b];
         keys[b]=key;
         if(ArraySize(items)!=NULL)
           {
            TItem item=items[a];
            items[a]=items[b];
            items[b]=item;
           }
        }
     }
  }
//+------------------------------------------------------------------+
//| Exchanges the values of a and b.                                 |
//+------------------------------------------------------------------+
template<typename TKey,typename TItem>
void Introsort::Swap(const int i,const int j)
  {
   TKey key=keys[i];
   keys[i]=keys[j];
   keys[j]=key;
   if(ArraySize(items)!=NULL)
     {
      TItem item=items[i];
      items[i]=items[j];
      items[j]=item;
     }
  }
//+------------------------------------------------------------------+
//| Returns the closest integer value less than or equal to the base |
//| 2 log of the input value.                                        |
//+------------------------------------------------------------------+
template<typename TKey,typename TItem>
int Introsort::FloorLog2(int n) const
  {
   int result=0;
   while(n>=1)
     {
      result++;
      n=n/2;
     }
   return(result);
  }
//+------------------------------------------------------------------+
//| Introspective sort.                                              |
//+------------------------------------------------------------------+
template<typename TKey,typename TItem>
void Introsort::IntroSort(const int lo,int hi,int depthLimit)
  {
   const int IntrosortSizeThreshold=16;
   while(hi>lo)
     {
      int partitionSize = hi - lo + 1;
      if(partitionSize <= IntrosortSizeThreshold)
        {
         if(partitionSize==1)
           {
            return;
           }
         if(partitionSize==2)
           {
            SwapIfGreaterWithItems(lo,hi);
            return;
           }
         if(partitionSize==3)
           {
            SwapIfGreaterWithItems(lo,hi-1);
            SwapIfGreaterWithItems(lo,hi);
            SwapIfGreaterWithItems(hi-1,hi);
            return;
           }
         InsertionSort(lo,hi);
         return;
        }
      if(depthLimit==0)
        {
         Heapsort(lo,hi);
         return;
        }
      depthLimit--;
      int p=PickPivotAndPartition(lo,hi);
      IntroSort(p+1,hi,depthLimit);
      hi=p-1;
     }
  }
//+------------------------------------------------------------------+
//| Insertion sort.                                                  |
//+------------------------------------------------------------------+
template<typename TKey,typename TItem>
void Introsort::InsertionSort(const int lo,const int hi)
  {
   int i,j;
   TKey t;
   TItem dt;
   for(i=lo; i<hi; i++)
     {
      j = i;
      t = keys[i + 1];
      dt=(ArraySize(items)!=NULL) ? (TItem)items[i+1] : (TItem)NULL;
      while(j>=lo && comparer.Compare(t,keys[j])<0)
        {
         keys[j+1]=keys[j];
         if(ArraySize(items)!=NULL)
           {
            items[j+1]=items[j];
           }
         j--;
        }
      keys[j+1]=t;
      if(ArraySize(items)!=NULL)
        {
         items[j+1]=dt;
        }
     }
  }
//+------------------------------------------------------------------+
//| Array partitioning by a quick sort algorithm.                    |
//+------------------------------------------------------------------+
template<typename TKey,typename TItem>
int Introsort::PickPivotAndPartition(const int lo,const int hi)
  {
//--- Compute median-of-three.  But also partition them, since we've done the comparison.
   int mid=lo+(hi-lo)/2;
   SwapIfGreaterWithItems(lo,mid);
   SwapIfGreaterWithItems(lo,hi);
   SwapIfGreaterWithItems(mid,hi);
   TKey pivot=keys[mid];
   Swap(mid,hi-1);
   int left=lo,right=hi-1;
   while(left<right)
     {
      while(comparer.Compare(keys[++left], pivot) < 0);
      while(comparer.Compare(pivot, keys[--right]) < 0);
      if(left>=right)
         break;
      Swap(left,right);
     }
//--- Put pivot in the right location.
   Swap(left,(hi-1));
   return (left);
  }
//+------------------------------------------------------------------+
//| Heap sorting algorithm.                                          |
//+------------------------------------------------------------------+
template<typename TKey,typename TItem>
void Introsort::Heapsort(const int lo,const int hi)
  {
   int n=hi-lo+1;
   for(int i=n/2; i>=1; i=i-1)
     {
      DownHeap(i,n,lo);
     }
   for(int i=n; i>1; i=i-1)
     {
      Swap(lo,lo+i-1);
      DownHeap(1,i-1,lo);
     }
  }
//+------------------------------------------------------------------+
//| Downheap function for heapsort.                                  |
//+------------------------------------------------------------------+
template<typename TKey,typename TItem>
void Introsort::DownHeap(int i,const int n,const int lo)
  {
   bool  f=ArraySize(items)!=0;
   TKey  d=keys[lo+i-1];
   TItem dt=f ? (TItem)items[lo+i-1] : (TItem)NULL;

   while(i<=n/2)
     {
      int child=2*i;
      if(child<n && comparer.Compare(keys[lo+child-1],keys[lo+child])<0)
         child++;
      if(!(comparer.Compare(d,keys[lo+child-1])<0))
         break;
      keys[lo+i-1]=keys[lo+child-1];
      if(f)
         items[lo+i-1]=items[lo+child-1];
      i=child;
     }
   keys[lo+i-1]=d;
   if(f)
      items[lo+i-1]=dt;
  }
//+------------------------------------------------------------------+
