MobinMQL/Include/Arrays/List.mqh
2025-07-22 14:47:41 +03:00

657 lines
20 KiB
MQL5

//+------------------------------------------------------------------+
//| List.mqh |
//| Copyright 2000-2025, MetaQuotes Ltd. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#include <Object.mqh>
//+------------------------------------------------------------------+
//| Class CList. |
//| Purpose: Provides the possibility of working with the list of |
//| CObject instances and its dervivatives |
//| Derives from class CObject. |
//+------------------------------------------------------------------+
class CList : public CObject
{
protected:
CObject *m_first_node; // pointer to the first element of the list
CObject *m_last_node; // pointer to the last element of the list
CObject *m_curr_node; // pointer to the current element of the list
int m_curr_idx; // index of the current list item
int m_data_total; // number of elements
bool m_free_mode; // flag of the necessity of "physical" deletion of object
bool m_data_sort; // flag if the list is sorted or not
int m_sort_mode; // mode of sorting of array
public:
CList(void);
~CList(void);
//--- methods of access to protected data
bool FreeMode(void) const { return(m_free_mode); }
void FreeMode(bool mode) { m_free_mode=mode; }
int Total(void) const { return(m_data_total); }
bool IsSorted(void) const { return(m_data_sort); }
int SortMode(void) const { return(m_sort_mode); }
//--- method of identifying the object
virtual int Type(void) const { return(0x7779); }
//--- methods for working with files
virtual bool Save(const int file_handle);
virtual bool Load(const int file_handle);
//--- method of creating an element of the list
virtual CObject *CreateElement(void) { return(NULL); }
//--- methods of filling the list
int Add(CObject *new_node);
int Insert(CObject *new_node,int index);
//--- methods for navigating
int IndexOf(CObject *node);
CObject *GetNodeAtIndex(int index);
CObject *GetFirstNode(void);
CObject *GetPrevNode(void);
CObject *GetCurrentNode(void);
CObject *GetNextNode(void);
CObject *GetLastNode(void);
//--- methods for deleting
CObject *DetachCurrent(void);
bool DeleteCurrent(void);
bool Delete(int index);
void Clear(void);
//--- method for comparing lists
bool CompareList(CList *List);
//--- methods for changing
void Sort(int mode);
bool MoveToIndex(int index);
bool Exchange(CObject *node1,CObject *node2);
//--- method for searching
CObject *Search(CObject *element);
protected:
void QuickSort(int beg,int end,int mode);
CObject *QuickSearch(CObject *element);
};
//+------------------------------------------------------------------+
//| Constructor |
//+------------------------------------------------------------------+
CList::CList(void) : m_first_node(NULL),
m_last_node(NULL),
m_curr_node(NULL),
m_curr_idx(-1),
m_data_total(0),
m_free_mode(true),
m_data_sort(false),
m_sort_mode(0)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CList::~CList(void)
{
Clear();
}
//+------------------------------------------------------------------+
//| Method QuickSort |
//+------------------------------------------------------------------+
void CList::QuickSort(int beg,int end,int mode)
{
int i,j,k;
CObject *i_ptr,*j_ptr,*k_ptr;
//---
i_ptr=GetNodeAtIndex(i=beg);
j_ptr=GetNodeAtIndex(j=end);
while(i<end)
{
//--- ">>1" is quick division by 2
k_ptr=GetNodeAtIndex(k=(beg+end)>>1);
while(i<j)
{
while(i_ptr.Compare(k_ptr,mode)<0)
{
//--- control the output of the array bounds
if(i==m_data_total-1)
break;
i++;
i_ptr=i_ptr.Next();
}
while(j_ptr.Compare(k_ptr,mode)>0)
{
//--- control the output of the array bounds
if(j==0)
break;
j--;
j_ptr=j_ptr.Prev();
}
if(i<=j)
{
Exchange(i_ptr,j_ptr);
i++;
i_ptr=GetNodeAtIndex(i);
//--- control the output of the array bounds
if(j==0)
break;
else
{
j--;
j_ptr=GetNodeAtIndex(j);
}
}
}
if(beg<j)
QuickSort(beg,j,mode);
beg=i;
i_ptr=GetNodeAtIndex(i=beg);
j_ptr=GetNodeAtIndex(j=end);
}
}
//+------------------------------------------------------------------+
//| Index of element specified via the pointer to the list item |
//+------------------------------------------------------------------+
int CList::IndexOf(CObject *node)
{
//--- check
if(!CheckPointer(node) || !CheckPointer(m_curr_node))
return(-1);
//--- searching index
if(node==m_curr_node)
return(m_curr_idx);
if(GetFirstNode()==node)
return(0);
for(int i=1;i<m_data_total;i++)
if(GetNextNode()==node)
return(i);
//---
return(-1);
}
//+------------------------------------------------------------------+
//| Adding a new element to the end of the list |
//+------------------------------------------------------------------+
int CList::Add(CObject *new_node)
{
//--- check
if(!CheckPointer(new_node))
return(-1);
//--- add
if(m_first_node==NULL)
m_first_node=new_node;
else
{
m_last_node.Next(new_node);
new_node.Prev(m_last_node);
}
m_curr_node=new_node;
m_curr_idx=m_data_total;
m_last_node=new_node;
m_data_sort=false;
//--- result
return(m_data_total++);
}
//+------------------------------------------------------------------+
//| Inserting a new element to the specified position in the list. |
//| Inserting element to the current position of the list if index=-1|
//+------------------------------------------------------------------+
int CList::Insert(CObject *new_node,int index)
{
CObject *tmp_node;
//--- check
if(!CheckPointer(new_node))
return(-1);
if(index>m_data_total || index<0)
return(-1);
//--- adjust
if(index==-1)
{
if(m_curr_node==NULL)
return(Add(new_node));
}
else
{
if(GetNodeAtIndex(index)==NULL)
return(Add(new_node));
}
//--- no need to check m_curr_node
tmp_node=m_curr_node.Prev();
new_node.Prev(tmp_node);
if(tmp_node!=NULL)
tmp_node.Next(new_node);
else
m_first_node=new_node;
new_node.Next(m_curr_node);
m_curr_node.Prev(new_node);
m_data_total++;
m_data_sort=false;
m_curr_node=new_node;
//--- result
return(index);
}
//+------------------------------------------------------------------+
//| Get a pointer to the position of element in the list |
//+------------------------------------------------------------------+
CObject *CList::GetNodeAtIndex(int index)
{
int i;
bool revers;
CObject *result;
//--- check
if(index>=m_data_total)
return(NULL);
if(index==m_curr_idx)
return(m_curr_node);
//--- optimize bust list
if(index<m_curr_idx)
{
//--- index to the left of the current
if(m_curr_idx-index<index)
{
//--- closer to the current index
i=m_curr_idx;
revers=true;
result=m_curr_node;
}
else
{
//--- closer to the top of the list
i=0;
revers=false;
result=m_first_node;
}
}
else
{
//--- index to the right of the current
if(index-m_curr_idx<m_data_total-index-1)
{
//--- closer to the current index
i=m_curr_idx;
revers=false;
result=m_curr_node;
}
else
{
//--- closer to the end of the list
i=m_data_total-1;
revers=true;
result=m_last_node;
}
}
if(!CheckPointer(result))
return(NULL);
//---
if(revers)
{
//--- search from right to left
for(;i>index;i--)
{
result=result.Prev();
if(result==NULL)
return(NULL);
}
}
else
{
//--- search from left to right
for(;i<index;i++)
{
result=result.Next();
if(result==NULL)
return(NULL);
}
}
m_curr_idx=index;
//--- result
return(m_curr_node=result);
}
//+------------------------------------------------------------------+
//| Get a pointer to the first itme of the list |
//+------------------------------------------------------------------+
CObject *CList::GetFirstNode(void)
{
//--- check
if(!CheckPointer(m_first_node))
return(NULL);
//--- save
m_curr_idx=0;
//--- result
return(m_curr_node=m_first_node);
}
//+------------------------------------------------------------------+
//| Get a pointer to the previous itme of the list |
//+------------------------------------------------------------------+
CObject *CList::GetPrevNode(void)
{
//--- check
if(!CheckPointer(m_curr_node) || m_curr_node.Prev()==NULL)
return(NULL);
//--- decrement
m_curr_idx--;
//--- result
return(m_curr_node=m_curr_node.Prev());
}
//+------------------------------------------------------------------+
//| Get a pointer to the current item of the list |
//+------------------------------------------------------------------+
CObject *CList::GetCurrentNode(void)
{
return(m_curr_node);
}
//+------------------------------------------------------------------+
//| Get a pointer to the next item of the list |
//+------------------------------------------------------------------+
CObject *CList::GetNextNode(void)
{
//--- check
if(!CheckPointer(m_curr_node) || m_curr_node.Next()==NULL)
return(NULL);
//--- increment
m_curr_idx++;
//--- result
return(m_curr_node=m_curr_node.Next());
}
//+------------------------------------------------------------------+
//| Get a pointer to the last itme of the list |
//+------------------------------------------------------------------+
CObject *CList::GetLastNode(void)
{
//--- check
if(!CheckPointer(m_last_node))
return(NULL);
//---
m_curr_idx=m_data_total-1;
//--- result
return(m_curr_node=m_last_node);
}
//+------------------------------------------------------------------+
//| Detach current item in the list |
//+------------------------------------------------------------------+
CObject *CList::DetachCurrent(void)
{
CObject *tmp_node,*result=NULL;
//--- check
if(!CheckPointer(m_curr_node))
return(result);
//--- "explode" list
result=m_curr_node;
m_curr_node=NULL;
//--- if the deleted item was not the last one, pull up the "tail" of the list
if((tmp_node=result.Next())!=NULL)
{
tmp_node.Prev(result.Prev());
m_curr_node=tmp_node;
}
//--- if the deleted item was not the first one, pull up "head" list
if((tmp_node=result.Prev())!=NULL)
{
tmp_node.Next(result.Next());
//--- if "last_node" is removed, move the current pointer to the end of the list
if(m_curr_node==NULL)
{
m_curr_node=tmp_node;
m_curr_idx=m_data_total-2;
}
}
m_data_total--;
//--- if necessary, adjust the settings of the first and last elements
if(m_first_node==result)
m_first_node=result.Next();
if(m_last_node==result)
m_last_node=result.Prev();
//--- complete the processing of element removed from the list
//--- remove references to the list
result.Prev(NULL);
result.Next(NULL);
//--- result
return(result);
}
//+------------------------------------------------------------------+
//| Delete current item of list item |
//+------------------------------------------------------------------+
bool CList::DeleteCurrent(void)
{
CObject *result=DetachCurrent();
//--- check
if(result==NULL)
return(false);
//--- complete the processing of element removed from the list
if(m_free_mode)
{
//--- delete it "physically"
if(CheckPointer(result)==POINTER_DYNAMIC)
delete result;
}
//--- successful
return(true);
}
//+------------------------------------------------------------------+
//| Delete an item from a given position in the list |
//+------------------------------------------------------------------+
bool CList::Delete(int index)
{
if(GetNodeAtIndex(index)==NULL)
return(false);
//--- result
return(DeleteCurrent());
}
//+------------------------------------------------------------------+
//| Remove all items from the list |
//+------------------------------------------------------------------+
void CList::Clear(void)
{
GetFirstNode();
while(m_data_total!=0)
if(!DeleteCurrent())
break;
}
//+------------------------------------------------------------------+
//| Equality comparing of two lists |
//+------------------------------------------------------------------+
bool CList::CompareList(CList *List)
{
CObject *node,*lnode;
//--- check
if(!CheckPointer(List))
return(false);
if((node=GetFirstNode())==NULL)
return(false);
if((lnode=List.GetFirstNode())==NULL)
return(false);
//--- compare
if(node.Compare(lnode)!=0)
return(false);
while((node=GetNextNode())!=NULL)
{
if((lnode=List.GetNextNode())==NULL)
return(false);
if(node.Compare(lnode)!=0)
return(false);
}
//--- equal
return(true);
}
//+------------------------------------------------------------------+
//| Sorting an array in ascending order |
//+------------------------------------------------------------------+
void CList::Sort(int mode)
{
//--- check
if(m_data_total==0)
return;
if(m_data_sort && m_sort_mode==mode)
return;
//--- sort
QuickSort(0,m_data_total-1,mode);
m_sort_mode=mode;
m_data_sort=true;
}
//+------------------------------------------------------------------+
//| Move the current item of list to the specified position |
//+------------------------------------------------------------------+
bool CList::MoveToIndex(int index)
{
//--- check
if(index>=m_data_total || !CheckPointer(m_curr_node))
return(false);
//--- tune
if(m_curr_idx==index)
return(true);
if(m_curr_idx<index)
index--;
//--- move
Insert(DetachCurrent(),index);
//--- successful
return(true);
}
//+------------------------------------------------------------------+
//| Move an item of the list from the specified position to the |
//| current one |
//+------------------------------------------------------------------+
bool CList::Exchange(CObject *node1,CObject *node2)
{
CObject *tmp_node,*node;
//--- check
if(!CheckPointer(node1) || !CheckPointer(node2))
return(false);
//---
tmp_node=node1.Prev();
node1.Prev(node2.Prev());
if(node1.Prev()!=NULL)
{
node=node1.Prev();
node.Next(node1);
}
else
m_first_node=node1;
node2.Prev(tmp_node);
if(node2.Prev()!=NULL)
{
node=node2.Prev();
node.Next(node2);
}
else
m_first_node=node2;
tmp_node=node1.Next();
node1.Next(node2.Next());
if(node1.Next()!=NULL)
{
node=node1.Next();
node.Prev(node1);
}
else
m_last_node=node1;
node2.Next(tmp_node);
if(node2.Next()!=NULL)
{
node=node2.Next();
node.Prev(node2);
}
else
m_last_node=node2;
//---
m_curr_idx=0;
m_curr_node=m_first_node;
m_data_sort=false;
//--- successful
return(true);
}
//+------------------------------------------------------------------+
//| Search position of an element in a sorted list |
//+------------------------------------------------------------------+
CObject *CList::QuickSearch(CObject *element)
{
int i,j,m;
CObject *t_node=NULL;
//--- check
if(m_data_total==0)
return(NULL);
//--- check the pointer is not needed
i=0;
j=m_data_total;
while(j>=i)
{
//--- ">>1" is quick division by 2
m=(j+i)>>1;
if(m<0 || m>=m_data_total)
break;
t_node=GetNodeAtIndex(m);
if(t_node.Compare(element,m_sort_mode)==0)
break;
if(t_node.Compare(element,m_sort_mode)>0)
j=m-1;
else
i=m+1;
t_node=NULL;
}
//--- result
return(t_node);
}
//+------------------------------------------------------------------+
//| Search position of an element in a sorted list |
//+------------------------------------------------------------------+
CObject *CList::Search(CObject *element)
{
CObject *result;
//--- check
if(!CheckPointer(element) || !m_data_sort)
return(NULL);
//--- search
result=QuickSearch(element);
//--- result
return(result);
}
//+------------------------------------------------------------------+
//| Writing list to file |
//+------------------------------------------------------------------+
bool CList::Save(const int file_handle)
{
CObject *node;
bool result=true;
//--- check
if(!CheckPointer(m_curr_node) || file_handle==INVALID_HANDLE)
return(false);
//--- write start marker - 0xFFFFFFFFFFFFFFFF
if(FileWriteLong(file_handle,-1)!=sizeof(long))
return(false);
//--- write type
if(FileWriteInteger(file_handle,Type(),INT_VALUE)!=INT_VALUE)
return(false);
//--- write list size
if(FileWriteInteger(file_handle,m_data_total,INT_VALUE)!=INT_VALUE)
return(false);
//--- sequential scannning of elements in the list using the call of method Save()
node=m_first_node;
while(node!=NULL)
{
result&=node.Save(file_handle);
node=node.Next();
}
//--- successful
return(result);
}
//+------------------------------------------------------------------+
//| Reading list from file |
//+------------------------------------------------------------------+
bool CList::Load(const int file_handle)
{
uint i,num;
CObject *node;
bool result=true;
//--- check
if(file_handle==INVALID_HANDLE)
return(false);
//--- read and checking begin marker - 0xFFFFFFFFFFFFFFFF
if(FileReadLong(file_handle)!=-1)
return(false);
//--- read and checking type
if(FileReadInteger(file_handle,INT_VALUE)!=Type())
return(false);
//--- read list size
num=FileReadInteger(file_handle,INT_VALUE);
//--- sequential creation of list items using the call of method Load()
Clear();
for(i=0;i<num;i++)
{
node=CreateElement();
if(node==NULL)
return(false);
Add(node);
result&=node.Load(file_handle);
}
//--- successful
return(result);
}
//+------------------------------------------------------------------+