The problem of finding an interval and the closest points.

Just starting out? Need help? Post your questions and find answers here.
ZX80
Enthusiast
Enthusiast
Posts: 391
Joined: Mon Dec 12, 2016 1:37 pm

The problem of finding an interval and the closest points.

Post by ZX80 »

Hi, folks.

I am looking for a better and simpler solution to this problem, as what exists seems unnecessarily complicated and inconvenient to me.

The essence of the task:

There are some control points that are defined in advance and do not change. Next, a specific value is entered that falls exactly within the range of all control points. It will never be outside this range. What interests me in the end ? This is the interval (between which control points) in which the entered value lies.

Thanks in advance!

P.S. Sorry, I forgot to write... all values ​​are whole natural numbers !
User avatar
spikey
Enthusiast
Enthusiast
Posts: 789
Joined: Wed Sep 22, 2010 1:17 pm
Location: United Kingdom

Re: The problem of finding an interval and the closest points.

Post by spikey »

Are we talking 1D, 2D or 3D points here? How many control points are there likely to be? 10's, 100's or 1000's
ZX80
Enthusiast
Enthusiast
Posts: 391
Joined: Mon Dec 12, 2016 1:37 pm

Re: The problem of finding an interval and the closest points.

Post by ZX80 »

Good time, spikey !
Are we talking 1D, 2D or 3D points here?
Sorry, I expressed myself incorrectly. According to your classification, it will be one ! D
How many control points are there likely to be?
Just four !

Everything is much simpler ! I just want to make a timer.
Okay. This is how I have it done now. But I will have to do the calculations many times and I would like to take this out into a separate procedure. Now you can see a checkpoint ahead. Only forward. But sometimes I also need to get a point that is behind the given value.

So I ask professionals how they would do it.

Code: Select all

Structure Task
  timer.i
  value.i
  wait_for_n_min.i
  schedule_nr.i
  schedule_str1$
  schedule_str2$
EndStructure


Procedure.s ConvertMinutes(NbMinutes)
  Protected divisor = 60 ; minutes in an hour
  Protected hours, minutes
  Protected result$ = ""
  
  hours = NbMinutes / divisor
  minutes = NbMinutes % divisor
  
  If hours > 0
    result$ + Str(hours) + " h."
  EndIf

  If minutes > 0
    If hours > 0
      result$ + " "
    EndIf
    result$ + Str(minutes) + " min."
  EndIf
 
  ProcedureReturn result$
EndProcedure

Procedure.s Get_Timetable(value)
  Protected result$
  
  If value = 1
    result$ = "05:00"
  ElseIf value = 2
    result$ = "11:00"
  ElseIf value = 3
    result$ = "17:00"
  ElseIf value = 4
    result$ = "23:00"
  EndIf
  
  ProcedureReturn result$
EndProcedure

Procedure Calculate_TriggerTime(*Task.Task)
  Protected CurrentTime = Date()
  Protected CurrentTimeHour = 22;Hour(CurrentTime)
  Protected CurrentTimeMinute = 59;Minute(CurrentTime)
  Protected inpval = (CurrentTimeHour * 60) + CurrentTimeMinute
  Protected i=1, found=0, Index, _timer, wait_for_n_min
  
  *Task\schedule_nr = *Task\schedule_nr + 1
  
  NewList Timer.i()
  Dim *Pointers(0)
  
  ; values ​​of checkpoints in minutes
  AddElement(Timer())
  Timer() = 300 ; minutes = 05:00 AM   etc...
  AddElement(Timer())
  Timer() = 660
  AddElement(Timer())
  Timer() = 1020
  AddElement(Timer())
  Timer() = 1380

  ForEach Timer()
    If Timer() = inpval
      found=1
      Break
    EndIf
    i+1
  Next
  
  If found ; execute immediately !
    If i = 4
      *Task\value        = 1
    Else
      *Task\value        = i+1
    EndIf
    *Task\wait_for_n_min = 360
    *Task\schedule_str1$ = Get_Timetable(*Task\value)
    *Task\schedule_str2$ = ConvertMinutes(*Task\wait_for_n_min)
    ;PostEvent(#print_estimated_time) ; send a command to the main loop
  Else
    AddElement(Timer())
    Timer() = inpval
    SortList(Timer(), #PB_Sort_Ascending)
    
    ReDim *Pointers(ListSize(Timer()))
    ForEach Timer()
      *Pointers(ListIndex(Timer())) = @Timer()
    Next
    
    i=1
    ForEach Timer()
      If Timer() = inpval
        Break 
      EndIf
      i+1
    Next
    
    If i=5
      i=1
    EndIf
    
    Index = i
    ;ResetList(Timer())
    ChangeCurrentElement(Timer(), *Pointers(Index))
    _timer = Timer()
    
    If inpval > 1380 ; if inpval > 23:00
      wait_for_n_min = (1440 - inpval) + 300
    Else
      wait_for_n_min = _timer - inpval
    EndIf
    
    *Task\value          = i
    *Task\wait_for_n_min = wait_for_n_min
    *Task\schedule_str1$ = Get_Timetable(i)
    *Task\schedule_str2$ = ConvertMinutes(wait_for_n_min)
    ;PostEvent(#print_estimated_time) ; send a command to the main loop
  EndIf
EndProcedure


Define Task.Task

Calculate_TriggerTime(@Task)

Debug "Task # " + Str(Task\schedule_nr)
Debug ""
text$ = "scheduled for " + Task\schedule_str1$ + #CRLF$ + Task\schedule_str2$ + " left"
Debug text$
Thanks in advance to everyone who responds.
User avatar
spikey
Enthusiast
Enthusiast
Posts: 789
Joined: Wed Sep 22, 2010 1:17 pm
Location: United Kingdom

Re: The problem of finding an interval and the closest points.

Post by spikey »

If you always have four time slots you don't need to muck about with a list. Each task can have a fixed sized array.

I would also store Unix epoch timestamps (i.e. Q values from the Date() function) of the next event time in the array. Then you can also take advantage of the AddDate() function too.
When the timer procedure 'ticks' at its resolution, I'd check to see if any of the timestamps have passed and so the event is due and needs triggering. Then recalculating the next time to trigger. Something like this:
(this particular code may not work properly if you start it up in the 59th minute of the hour, so don't - but you get the idea, I hope).

Code: Select all

Structure TASK
  Name.S
  Times.Q[4]
  Sequence.B
  ; ...
EndStructure

NewList Tasks.TASK()
Define.W Yr = Year(Date()), Mn = Month(Date()), Dy = Day(Date()), Hr = Hour(Date()), Mt = Minute(Date())

AddElement(Tasks())
Tasks()\Name = "First task"
Tasks()\Times[0] = Date(Yr, Mn, Dy, Hr, Mt + 1, 00)
Tasks()\Times[1] = Date(Yr, Mn, Dy, Hr, Mt + 1, 15)
Tasks()\Times[2] = Date(Yr, Mn, Dy, Hr, Mt + 1, 30)
Tasks()\Times[3] = Date(Yr, Mn, Dy, Hr, Mt + 1, 45)

AddElement(Tasks())
Tasks()\Name = "Second task"
Tasks()\Times[0] = Date(Yr, Mn, Dy, Hr, Mt + 1, 10)
Tasks()\Times[1] = Date(Yr, Mn, Dy, Hr, Mt + 1, 25)
Tasks()\Times[2] = Date(Yr, Mn, Dy, Hr, Mt + 1, 35)
Tasks()\Times[3] = Date(Yr, Mn, Dy, Hr, Mt + 1, 55)

Debug "Started, waiting for a trigger..."

Repeat
  
  ForEach Tasks()
    
    For i = 0 To 3
      
      If Date() >= Tasks()\Times[i]
        ; Task is due.
        Tasks()\Sequence = i
        Debug Tasks()\Name + " triggered at " + FormatDate("%dd/%mm/%yy %hh:%ii:%ss", Tasks()\Times[i]) + " #" + Str(Tasks()\Sequence) + "."
        ; Recalculate next Tasks()\Times[i].
        Tasks()\Times[i] = AddDate(Tasks()\Times[i], #PB_Date_Minute, 1)
        Debug "Next trigger will be " + FormatDate("%dd/%mm/%yy %hh:%ii:%ss", Tasks()\Times[i]) + " #" + Str(Tasks()\Sequence) + "."
        
        ; Do task...
        
        Break
      EndIf
      
    Next i
    
  Next Tasks()
  
  ; Whatever timer resolution makes sense.
  Delay(500) 
    
ForEver
It depends, however, on how long tasks might take to execute, how accurate start times must be and if tasks must execute concurrently. If tasks are long, start times must be accurate and concurrent then I'd be looking at a timer thread and a number of executive threads to do the work.
ZX80
Enthusiast
Enthusiast
Posts: 391
Joined: Mon Dec 12, 2016 1:37 pm

Re: The problem of finding an interval and the closest points.

Post by ZX80 »

spikey, thanks for your version and explanations.
Post Reply