lib/: expand all packages in their own directories
[nit.git] / lib / event_queue.nit
diff --git a/lib/event_queue.nit b/lib/event_queue.nit
deleted file mode 100644 (file)
index c36c1af..0000000
+++ /dev/null
@@ -1,383 +0,0 @@
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Register, update and discard events in a timeline.
-#
-# A queue of event is used to register objects associated with a start time and
-# a duration and to return them when they are active.
-#
-# The main class is `EventQueue`. It controls a timeline and register events.
-# Event can be any kind of object, usually specific time-related domain objects should be used.
-#
-# The auxiliary class `EventInfo` registers and control information about each event.
-# It can also be used for fluent programming and create new events relatively to existing ones.
-#
-# ## Basic usage
-#
-# The event queue is created empty, and can handle any kind of data.
-#
-# ~~~
-# var eq = new EventQueue[String]
-# ~~~
-#
-# To register an event, add it with a start time and a duration.
-# Note that the time is relative to the current time frame in the queue.
-#
-# ~~~
-# eq.add("1..2", 1.0, 1.0)
-# eq.add("2..10", 2.0, 8.0)
-# ~~~
-#
-# To register instantaneous event, use a 0.0 duration (or no duration)
-#
-# ~~~
-# eq.add("1.5", 1.5)
-# ~~~
-#
-# To get active events, use `update` with a time difference.
-# This returns a sequence of currently active events.
-#
-# ~~~
-# var es = eq.update(1.0) # What is active after 1 unit of time?
-# assert es.length == 1 # only "1..2" is active
-# ~~~
-#
-# Each `update` increments the time frame of the queue and returns an updated information about active events.
-#
-# ~~~
-# es = eq.update(1.0) # What is active after an other unit of time?
-# assert eq.time == 2.0
-#
-# assert es.length == 3 # there is now 3 active events
-# ~~~
-#
-# Results of the `update` method contains a lot of information.
-#
-# Obviously the original events:
-#
-# ~~~
-# assert es[0].event == "1..2"
-# assert es[1].event == "1.5"
-# assert es[2].event == "2..10"
-# ~~~
-#
-# The time since the begin of the event:
-#
-# ~~~
-# assert es[0].time == 1.0 # Started 1 unit of time ago
-# assert es[1].time == 0.5
-# assert es[2].time == 0.0 # Started just now
-# ~~~
-#
-# The completion ratio of the event until its termination:
-#
-# ~~~
-# assert es[0].completion == 1.0 # has just finished
-# assert es[1].completion == inf # completion ratio does not make sense for instant events
-# assert es[2].completion == 0.0 # has just started
-# ~~~
-#
-# And a lot of other things, just look at `EventInfo`.
-#
-# ## Expiration and control
-#
-# When `update` returns events, `occurrences` count the number of time an
-# event is returned by update. It can be used for instance to see if it
-# is the first time that a specific event is active.
-#
-# ~~~
-# assert es[0].occurrences == 2 # it is the second time we see it
-# assert es[1].occurrences == 1 # instant one
-# assert es[2].occurrences == 1 # first time we see it
-# ~~~
-#
-# The duration is used to automatically manage the expiration of the event.
-#
-# Note that if a event expires during an `update`, then it is still returned by the function.
-# This is the way to handle instant events and newly expired events.
-#
-# To distinguish them `has_expired` can be used.
-#
-# ~~~
-# assert es[0].has_expired == true  # just finished
-# assert es[1].has_expired == true  # instant one
-# assert es[2].has_expired == false # sill active
-# ~~~
-#
-# On the next `update`, the already expired events are not returned again.
-#
-# ~~~
-# es = eq.update(1.0) # What is active after an other unit of time?
-# assert eq.time == 3.0
-# assert es.length == 1
-# assert es.first.event == "2..10" # No more "1..2" or "1.5"
-# ~~~
-#
-# The `expire` method can force the expiration of an event.
-#
-# ~~~
-# es.first.expire
-# es = eq.update(1.0)
-# assert es.is_empty # Sorry, "2..10" was expired
-# ~~~
-#
-# ## Fluent programming
-#
-# The `add` method (and its derivates) returns a `EventInfo` object that can be used to have
-# information about the newly registered event but also to chain the creation of time-related events.
-#
-# For instance, the following example registers 4 events, each one relative to the previous one:
-#
-# ~~~
-# eq = new EventQueue[String]
-# eq.add("e1", 10.0, 5.0).
-#    add_after("e2", 2.0, 5.0).
-#    add_sync("e3", 2.0, 5.0).
-#    add_before("e4", 2.0, 5.0)
-# ~~~
-#
-# This can be decomposed as:
-#
-# ~~~
-# eq = new EventQueue[String]
-# var e1 = eq.add("e1", 10.0, 5.0)
-# assert e1.start == 10.0 # First event starts at 10
-#
-# var e2 = e1.add_after("e2", 2.0, 5.0) # starts 2 units of time after the end of e1
-# assert e2.start == 17.0 # So starts at 10 + 5 + 2
-#
-# var e3 = e2.add_sync("e3", 2.0, 5.0) # starts 2 units of time after the begin of e2
-# assert e3.start == 19.0 # So starts at 17 + 2
-#
-# var e4 = e3.add_before("e4", 2.0, 5.0) # ends 2 units of time before the start of e3
-# assert e4.start == 12.0 # So starts at 19 - 2 - 5
-# ~~~
-module event_queue
-
-# Queuing and management of arbitrary events in a timeline
-class EventQueue[E]
-       # Comparator used by the queue
-       private var event_comparator = new EventComparator
-
-       # Efficient queue
-       private var queue = new MinHeap[EventInfo[E]](event_comparator)
-
-       # List of current active event
-       private var actives = new Array[EventInfo[E]]
-
-       # List of previously active event
-       private var old_actives = new Array[EventInfo[E]]
-
-       # Global time since the creation of the queue
-       var time = 0.0
-
-       # Nearest deadline, used to optimise queue access
-       #
-       # `inf` if no deadline is set
-       private var next: Float = inf
-
-       # Register an `event` in the queue to be active in `delay` units of time.
-       #
-       # If `duration` is given, this is duration after what the event is automatically discarded.
-       # If `null`, 0.0 or not given, the event will be executed only once
-       # Use `inf` for an event with an infinite duration.
-       fun add(event: E, delay: Float, duration: nullable Float): EventInfo[E]
-       do
-               return add_at(event, time + delay, duration)
-       end
-
-       # Register an `event` in the queue executed at a specific time.
-       fun add_at(event: E, start: Float, duration: nullable Float): EventInfo[E]
-       do
-               if start < next then next = start
-               var ei = new EventInfo[E](event, self, start, duration or else 0.0)
-               queue.add ei
-
-               return ei
-       end
-
-       # Add a given amount of time and return all the events that were active during the delay.
-       #
-       # Note that events that expired during the delay are marked `has_expired` and are still returned.
-       fun update(dt: Float): SequenceRead[EventInfo[E]]
-       do
-               var time = self.time
-               time += dt
-               self.time = time
-
-               # Switch things
-               var tmp = old_actives
-               old_actives = actives
-               actives = tmp
-
-               # Discard dead events
-               actives.clear
-               for ei in old_actives do
-                       if not ei.has_expired then actives.add ei
-               end
-
-               # Start new events
-               if time >= next then loop
-                       if queue.is_empty then
-                               next = inf
-                               break
-                       end
-                       var ei = queue.peek
-                       if ei.start > time then
-                               next = ei.start
-                               break
-                       end
-                       ei = queue.take
-                       if not ei.has_expired then
-                               actives.add ei
-                               ei.occurrences = 0
-                       end
-               end
-
-               if actives.is_empty then return actives
-
-               # Update event information
-               for ei in actives do ei.update(time, dt)
-
-               return actives
-       end
-end
-
-# Information and management about a registered event
-#
-# It allows to retrieve static and dynamic information about an event.
-# It also allows to register other time-related events with a fluent programming way.
-class EventInfo[E]
-       # The registered event.
-       var event: E
-
-       # The associated event queue.
-       #
-       # It is used internally for fluent programming.
-       var queue: EventQueue[E]
-
-       # Absolute start time for the registered event in the event queue time frame.
-       var start: Float
-
-       # Registered duration.
-       #
-       # Events with a 0.0 duration are called *instant events*.
-       var duration: Float
-
-       # Time since the begin of the event, in units of time.
-       var time: Float = nan
-
-       # Time since the last update (or the begin of the event if smaller)
-       #
-       # Note that if the event has expired during the last time lapse,
-       # then `dt` also counts the *expired* time between the expiration time
-       # and the update time.
-       var dt: Float = nan
-
-       # Ratio of completion
-       #
-       # Usually between 0.0 and 1.
-       # It might be > 1.0 if the event has expired during the last time lapse.
-       var completion = 0.0
-
-       # Number of times that an event was returned by `update`.
-       #
-       # If the event just started during the last update, 1 is returned.
-       #
-       # If the event was not returned by `update` yet, 0 is returned.
-       var occurrences = 0
-
-       # Has the event expired?
-       #
-       # Such an event will be not present in the next `update`.
-       #
-       # Note that an event can be `has_expired` and have `occurrences == 0` if it
-       # is entirely included in the last time lapse.
-       # It is especially the case for instant events.
-       var has_expired = false
-
-       ## Fluent methods
-
-       # Register an event that starts after the end of the current one.
-       #
-       # `delay` indicates the time between the end of the current event and the begin of the new one.
-       # Use 0.0 if both events should be contiguous and not overlap.
-       #
-       # Returns the new event information that can be used in fluent programming.
-       fun add_after(event: E, delay: Float, duration: nullable Float): EventInfo[E]
-       do
-               return queue.add_at(event, start + self.duration + delay, duration)
-       end
-
-       # Register an event that starts with the current one.
-       #
-       # `delay` indicates the time between the begin of the current event and the begin of the new one.
-       # Use 0.0 if both events should start at the same time and overlaps.
-       #
-       # Returns the new event information that can be used in fluent programming.
-       fun add_sync(event: E, delay: Float, duration: nullable Float): EventInfo[E]
-       do
-               return queue.add_at(event, start + delay, duration)
-       end
-
-       # Register an event that finishes before the begin of current one.
-       #
-       # `delay` indicates the time between the end of the new event and the begin of the current one.
-       # Use 0.0 if both event should be contiguous and not overlap.
-       #
-       # Returns the new event information that can be used in fluent programming.
-       fun add_before(event: E, delay: Float, duration: nullable Float): EventInfo[E]
-       do
-               duration = duration or else 0.0
-               return queue.add_at(event, start - delay - duration, duration)
-       end
-
-       # Update attributes. Is called by `EventQueue::update`
-       private fun update(queue_time, queue_dt: Float)
-       do
-               time = queue_time - start
-               if time >= duration then expire
-               dt = queue_dt.min(time)
-               completion = time / duration
-               occurrences += 1
-       end
-
-       # Force the event to expire.
-       #
-       # The event can be active of not.
-       #
-       # ~~~
-       # var eq = new EventQueue[String]
-       # var e1 = eq.add("active", 0.0, 10.0)
-       # var e2 = eq.add("not active", 2.0, 10.0)
-       # var es = eq.update(1.0)
-       # assert es == [e1]
-       # e1.expire
-       # e2.expire
-       # es = eq.update(2.0)
-       # assert es.is_empty
-       # ~~~
-       #
-       # Note that when an event is forced to expire,
-       # it will not appears in the next `update`.
-       fun expire do has_expired = true
-
-       redef fun to_s do return "{event or else "null"}@{start}+{duration}"
-end
-
-private class EventComparator
-       super Comparator
-       redef type COMPARED: EventInfo[nullable Object]
-       redef fun compare(a, b) do return a.start <=> b.start
-end