ArrayBsearch Bug/deadlock with Terminal not responding

 

Hello all,


this code does not come back in the last line with ArrayBsearch in MT4  (see comments).

You have to manually stop the debugger to get out of it. Code hangs in an endless loop in the ArrayBsearch function.


I searched the Forum and the Web for any answer but didnt find any.

Seems there is a problem searching for elements that are not in the array.

Havent tried the Library yet with the CArrayInt-Class, which has a CArrayInt::QuickSearch Method that should not have the error.

Any ideas or maybe a known bug?


Thanks a lot

JHawk



string getBoolStr(bool b) {
  return (b ? "true" : "false");
}

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//-----------------------------------------------------------
  if(1==1) {

   int num_array[10]={0,1,2,3,4,5,6,7,8,9}; 
   ArraySetAsSeries(num_array, true);
   // --> num_array[0]=9
   bool b1 = ArrayGetAsSeries(num_array);
   Print("b2 = " + getBoolStr(b1));

   string str="";
   for(int i=0; i<=9; i++) 
     { 
      str="index "+IntegerToString(i)+": "; 
      str+=IntegerToString(num_array[i]); 
      Print(str); 
     } 

   int search_val = 7;
   Print("Searching for " + IntegerToString(search_val) + " in num_array[10]={0,1,2,3,4,5,6,7,8,9}; with MODE_DESCEND/Series");
   int found_i =ArrayBsearch(num_array,search_val,WHOLE_ARRAY,0,MODE_DESCEND);
   Print("found_i = " + IntegerToString(found_i));

   int num_array2[10]={0,1,2,3,4,5,6,8,9,10}; 
   ArraySetAsSeries(num_array2, true);
   bool b2 = ArrayGetAsSeries(num_array2);
   Print("b2 = " + getBoolStr(b2));

   str="";
   for(int i=0; i<=9; i++) 
     { 
      str="index "+IntegerToString(i)+": "; 
      str+=IntegerToString(num_array2[i]); 
      Print(str); 
     } 
     
    // this works! -->
   // search_val = 6;
   // this DOES NOT WORK. TERMINAL NOT RESPONDING IN DEBUGGER IN ArrayBsearch = DEAD LOCK -->
   search_val = 7;
    // this works! -->
   // search_val = 8;
   Print("Searching MISSING VALUE " + IntegerToString(search_val) + " in num_array2[10]={0,1,2,3,4,5,6,8,9,10}; with MODE_DESCEND/Series");

   
   // DEAD LOCK after calling this with search_val=7 -->
   int found_i2=ArrayBsearch(num_array2,search_val,WHOLE_ARRAY,0,MODE_DESCEND);
   Print("found_i2 = " + IntegerToString(found_i2) + " value num_array2[" + IntegerToString(found_i2) + "]=" + IntegerToString(num_array2[found_i2]));
  }

}

 

Ok, I found out a bit more:

The Bug does not occur in MODE_ASSCEND/NOT SERIES, as the new script shows.

So there is a problem with the combination of ArrayBsearch and the use of SERIES-arrays and MODE_ASCEND, when a search value is missing.

Any information about that?


string getBoolStr(bool b) {
  return (b ? "true" : "false");
}


//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {

//-----------------------------------------------------------
  if(1==1) {

   int num_array[10]={0,1,2,3,4,5,6,7,8,9}; 
   ArraySetAsSeries(num_array, false);
   bool b1 = ArrayGetAsSeries(num_array);
   Print("b1 = " + getBoolStr(b1));

   string str="";
   for(int i=0; i<=9; i++) 
     { 
      str="index "+IntegerToString(i)+": "; 
      str+=IntegerToString(num_array[i]); 
      Print(str); 
     } 

   int search_val = 7;
   Print("Searching for " + IntegerToString(search_val) + " in num_array[10]={0,1,2,3,4,5,6,7,8,9}; with MODE_ASCEND/NOT Series");
   int found_i =ArrayBsearch(num_array,search_val,WHOLE_ARRAY,0,MODE_ASCEND);
   Print("found_i = " + IntegerToString(found_i));

   int num_array2[10]={0,1,2,3,4,5,6,8,9,10}; 
   ArraySetAsSeries(num_array2, false);
   bool b2 = ArrayGetAsSeries(num_array2);
   Print("b2 = " + getBoolStr(b2));

   str="";
   for(int i=0; i<=9; i++) 
     { 
      str="index "+IntegerToString(i)+": "; 
      str+=IntegerToString(num_array2[i]); 
      Print(str); 
     } 
     
    // this works! -->
   // search_val = 6;
   // this DOES WORK NOW with an array that is NOT SERIES -->
   search_val = 7;
    // this works! -->
   // search_val = 8;
   Print("Searching MISSING VALUE " + IntegerToString(search_val) + " in num_array2[10]={0,1,2,3,4,5,6,8,9,10}; with MODE_ASCEND/NOT Series");
   int found_i2=ArrayBsearch(num_array2,search_val,WHOLE_ARRAY,0,MODE_ASCEND);
   Print("found_i2 = " + IntegerToString(found_i2) + " value num_array2[" + IntegerToString(found_i2) + "]=" + IntegerToString(num_array2[found_i2]));
  }

}
 
To me this sounds pretty much like an endless loop you enter - find out where with the debugger then why..
 
JHawk:


this code does not come back in the last line with ArrayBsearch in MT4  (see comments).


So, I am thinking . . . 

If you want to use the array as a series, i.e. ArraySetAsSeries(num_array2, true), not only do you need to set MODE_DESCEND, but you also need to set the starting position (fourth argument) to (ArraySize(num_array2) - 1) instead of 0.

 
Anthony Garot:

So, I am thinking . . . 

If you want to use the array as a series, i.e. ArraySetAsSeries(num_array2, true), not only do you need to set MODE_DESCEND, but you also need to set the starting position (fourth argument) to (ArraySize(num_array2) - 1) instead of 0.


Hi Anthony,

no.

---

The following is a copy from the docu. Original MetaQuotes Knowhow.

They use it on the Time-Series of Daily Bars. No Problem.

---

Secondly said:

I dont have any problem with array elements that exist.

Please ... anybody can start Script 1 and test it with

search_val = 6;

and it works.

test it with

search_val = 7;

and it crashs. So I advise you to only start it from the debugger. Then you dont have to kill the Terminal Task :-)


It would help me much if only someone tells me if his terminal behaves the same with script 1.

Mine is fresh Build 1090 from 19 May 2017

Greetings and Thanks

JHawk











 datetime daytimes[]; 
   int      shift=10,dayshift; 
   // All the Time[] series are sorted in descendant mode 
   ArrayCopySeries(daytimes,MODE_TIME,Symbol(),PERIOD_D1); 
   if(Time[shift]>=daytimes[0]) dayshift=0; 
   else 
     { 
      dayshift=ArrayBsearch(daytimes,Time[shift],WHOLE_ARRAY,0,MODE_DESCEND); 
      if(Period()<PERIOD_D1) dayshift++; 
     } 
   Print(TimeToStr(Time[shift])," corresponds to ",dayshift," day bar opened at ", 
         TimeToStr(daytimes[dayshift]));
 

ArraySetAsSeries cannot be used for a statically allocated array, it only works on dynamic arrays. Using CArrayInt is much easier. 

#include <Arrays\ArrayInt.mqh>
void OnStart()
{
   CArrayInt arr_int;
   int my[]={1,2,3,4,5,6,7,8,9,10};
   arr_int.AddArray(my);
   //use SearchLinear if you don't want to sort your array;
   Print(arr_int.SearchLinear(99));
   
   //otherwise sort then use Search
   arr_int.Sort();
   Print(arr_int.Search(99));
}
 
nicholishen:

ArraySetAsSeries cannot be used for a statically allocated array, it only works on dynamic arrays. 

Hello nicholishen,


thank you very much for your comment which is true.

The error exists still though as the following script with your proposals shows :-(


string getBoolStr(bool b) {
  return (b ? "true" : "false");
}

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {

//-----------------------------------------------------------
  if(1==1) {

   int num_array[];
   int ret = ArrayResize(num_array,10);
   if (ret<0) {
    Print("ERROR: ArrayResize(num_array,10)<0");
    return;
   }
   
   for (int i=0;i<10;i++) {
      num_array[i]=i;
   }
   
   // int num_array[10]={0,1,2,3,4,5,6,7,8,9}; 
   ArraySetAsSeries(num_array, true);
   bool b1 = ArrayGetAsSeries(num_array);
   Print("b1 = " + getBoolStr(b1));

   string str="";
   for(int i=0; i<=9; i++) 
     { 
      str="index "+IntegerToString(i)+": "; 
      str+=IntegerToString(num_array[i]); 
      Print(str); 
     } 

   int search_val = 7;
   Print("Searching for " + IntegerToString(search_val) + " in num_array[10]={0,1,2,3,4,5,6,7,8,9}; with MODE_DESCEND/Series");
   int found_i =ArrayBsearch(num_array,search_val,WHOLE_ARRAY,0,MODE_DESCEND);
   Print("found_i = " + IntegerToString(found_i));

   // -------------
   int num_array2[];
   ret = ArrayResize(num_array2,10);
   if (ret<0) {
    Print("ERROR: ArrayResize(num_array2,10)<0");
    return;
   }
   
   int add_i=0;
   for (int i=0;i<10;i++) {
      if (i<=6)
        add_i=0;
      else
        add_i=1;
        
      num_array2[i]=i+add_i;
   }

   // int num_array2[10]={0,1,2,3,4,5,6,8,9,10}; 
   ArraySetAsSeries(num_array2, true);
   bool b2 = ArrayGetAsSeries(num_array2);
   Print("b2 = " + getBoolStr(b2));

   str="";
   for(int i=0; i<=9; i++) 
     { 
      str="index "+IntegerToString(i)+": "; 
      str+=IntegerToString(num_array2[i]); 
      Print(str); 
     } 
     
    // this works! -->
   // search_val = 6;
   // this DOES NOT WORK. TERMINAL NOT RESPONDING IN DEBUGGER IN ArrayBsearch = DEAD LOCK -->
   search_val = 7;
    // this works! -->
   // search_val = 8;
   Print("Searching MISSING VALUE " + IntegerToString(search_val) + " in num_array2[10]={0,1,2,3,4,5,6,8,9,10}; with MODE_DESCEND/Series");
   int found_i2=ArrayBsearch(num_array2,search_val,WHOLE_ARRAY,0,MODE_DESCEND);
   Print("found_i2 = " + IntegerToString(found_i2) + " value num_array2[" + IntegerToString(found_i2) + "]=" + IntegerToString(num_array2[found_i2]));
  }
 }
 
JHawk:

Hello nicholishen,


thank you very much for your comment which is true.

The error exists still though as the following script with your proposals shows :-(


I see you are using MQL4... it's a bug in ArrayBSearch. Don't use it. Use the CArrayInt class instead. Here's an example of solving a complex problem the easy way:   

Find a random number between 0-99,999 in an dynamic array with a random number of elements filled with random numbers between 0-99,999.


#include <Arrays\ArrayInt.mqh>
void OnStart()
{
   CArrayInt arr_int;
   srand(GetTickCount());
   for(int i=rand()%100+1;i>=0;i--)
      arr_int.Add(rand()%100000);
   arr_int.Sort();
   int num_tries = 1;
   int index = -1;
   for(;index < 0;index=arr_int.Search(rand()%100000))
      num_tries++;
  
   Print("It took ",num_tries," tries to find ",arr_int[index]," @ index ",index,
         " out of a total array size of ",arr_int.Total());
}
 
nicholishen:

I see you are using MQL4... it's a bug in ArrayBSearch. Don't use it. Use the CArrayInt class instead. Here's an example of solving a complex problem the easy way:   

Find a random number between 0-99,999 in an dynamic array with a random number of elements filled with random numbers between 0-99,999.


Thank you very much for that information!


Anyway I don't see how I can use CArray and descendants because I want to apply a Bsearch on Time in the onCalculate time[] array :-(

My script was just a test if and how it works with series and to see how it behaves when a value is missing (docu says nearest which could be anything) but then I stumbled over this bug.

So the new question is: How can I search in a quick way in the time-series for times?

I have a framework to work with opening and closing hours but I want to get rid of copying arrays to arrays again and again and using for-loops to search for certain points of time.

I need to find very quickly the opening and closing bars and the daily time range - this with dst correction. It works but it does not perform enough for me.

I remember I used ArrayBSearch one day to look for times but now I cannot use it because of the bug.


So using the Classes would be again copying from the time[]-array into the class and searchlinear-method is even again a for loop. Thats not what I need those classes for.

Any Ideas are welcome.


Greetings and thanks for your help

JHawk

P.S.: Late here. I need some sleep. coming back tomorrow.

 
JHawk:

Thank you very much for that information!


Anyway I don't see how I can use CArray and descendants because I want to apply a Bsearch on Time in the onCalculate time[] array :-(

My script was just a test if and how it works with series and to see how it behaves when a value is missing (docu says nearest which could be anything) but then I stumbled over this bug.

So the new question is: How can I search in a quick way in the time-series for times?

I have a framework to work with opening and closing hours but I want to get rid of copying arrays to arrays again and again and using for-loops to search for certain points of time.

I need to find very quickly the opening and closing bars and the daily time range - this with dst correction. It works but it does not perform enough for me.

I remember I used ArrayBSearch one day to look for times but now I cannot use it because of the bug.


So using the Classes would be again copying from the time[]-array into the class and searchlinear-method is even again a for loop. Thats not what I need those classes for.

Any Ideas are welcome.


Greetings and thanks for your help

JHawk

P.S.: Late here. I need some sleep. coming back tomorrow.

//+------------------------------------------------------------------+
//|                                                    TimeArray.mq5 |
//|                                                      nicholishen |
//+------------------------------------------------------------------+
#property copyright "nicholishen"
#property version   "1.00"
#property indicator_chart_window
#property strict
#include <Arrays\ArrayInt.mqh>
class CArrayDatetime : public CArrayInt
{
public:
   bool AssignArray(const datetime &src[])
   {
      int num=ArraySize(src);
//--- check/reserve elements of array
      Clear();
      if(m_data_max<num)
      {
         if(!Reserve(num))
            return(false);
      }
      else
         Resize(num);
      //--- copy array
      for(int i=0;i<num;i++)
      {
         m_data[i]=(int)src[i];
         m_data_total++;
      }
      m_sort_mode=-1;
      Sort();
      return(true);  
   }
   bool  Add(const datetime time)
   {
      return CArrayInt::Add((int)time);
   }
   int Search(const datetime time)const
   {
      return CArrayInt::Search((int)time);
   }
   int SearchLinear(const datetime time)const
   {
      return CArrayInt::SearchLinear((int)time);
   }
   datetime At(const int index)const
   {
      return (datetime)CArrayInt::At(index);
   }
};
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//---
   CArrayDatetime time_arr;
   time_arr.AssignArray(time);
   srand(GetTickCount());
   MqlDateTime t; 
   t.sec=0;
   //int index = -1;
   int num_tries=1;
   datetime rand_time=0;
   for(int index=-1;index < 0;index = time_arr.Search(rand_time))
   {
      t.year=2000+(rand()%18);
      t.mon = rand()%12+1;
      t.day = rand()%30+1;
      t.hour = rand()%24;
      t.min = rand()%60;
      rand_time = StructToTime(t);
      num_tries++;
   }
   Print("First random time found = ",rand_time," in ",num_tries," tries");
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
 

Or if you're looking for a simple function replacement...

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//---
   ArraySetAsSeries(time,false);
   srand(GetTickCount());
   MqlDateTime t; 
   t.sec=0;
   int num_tries=0;
   datetime rand_time=0;
   int index = -1;
   for(;index < 0;index = TimeQuickSearch(time,rand_time))
   {
      t.year=2000+(rand()%18);
      t.mon = rand()%12+1;
      t.day = rand()%30+1;
      t.hour = rand()%24;
      t.min = 0;//rand()%60;
      rand_time = StructToTime(t);
      num_tries++;
   }
   Print("First random time found = ",rand_time," in ",num_tries," tries @ ",index,
         " -- ",time[index]);
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+

int TimeQuickSearch(const datetime &src[],const datetime time)
{
   bool series = ArrayGetAsSeries(src);
   int  data_total = ArraySize(src);
   int  i,j,m=-1;
   datetime  t_time;
   i=0;
   j=data_total-1;
   while(j>=i)
   {
      m=(j+i)>>1;
      if(m<0 || m>=data_total)
         break;
      t_time=src[m];
      if(t_time==time)
         break;
      if((!series && t_time>time)||(series && t_time<time))
         j=m-1;
      else
         i=m+1;
   }
   return(src[m]==time?m:-1);
}
Reason: