From 313374d58e150eae47db13b6a1636e56d20933b3 Mon Sep 17 00:00:00 2001 From: Philippe PITTOLI Date: Sun, 26 May 2024 04:02:11 +0200 Subject: [PATCH] EfficientFIFO --- spec/test-fifo.cr | 17 +++++++++++++++++ spec/test-lists.cr | 13 +++++++++++++ src/fifo.cr | 36 ++++++++++++++++++++++++++++++++++++ src/list.cr | 32 +++++++++++++++++++++++++++++++- 4 files changed, 97 insertions(+), 1 deletion(-) diff --git a/spec/test-fifo.cr b/spec/test-fifo.cr index cf3231c..2de989f 100644 --- a/spec/test-fifo.cr +++ b/spec/test-fifo.cr @@ -17,3 +17,20 @@ describe "FIFO" do fifo.data.should eq([5, 4] of Int32) end end + +describe "EfficientFIFO" do + it "add and remove values" do + fifo = EfficientFIFO(Int32).new 3 # Only 3 allowed entries. + (fifo << 1).should be_nil # there is still room in the fifo + (fifo << 2).should be_nil # there is still room in the fifo + (fifo << 3).should be_nil # last entry without exceeding the allowed size + (fifo << 4).should eq 1 # -> 1 (least recently used data) + (fifo << 4).should be_nil # -> nil (already in the fifo) + (fifo << 2).should be_nil # -> nil (already in the fifo) + (fifo << 5).should eq 3 # -> 3 (least recently used data) + fifo.list.to_s.should eq "[ 5, 2, 4 ]" + + fifo.delete 2 + fifo.list.to_s.should eq "[ 5, 4 ]" + end +end diff --git a/spec/test-lists.cr b/spec/test-lists.cr index 5444f35..8ff2472 100644 --- a/spec/test-lists.cr +++ b/spec/test-lists.cr @@ -94,6 +94,19 @@ describe "DoubleLinkedList" do list.shift.value.should eq 4 end + it "unshift" do + list = DoubleLinkedList(Int32).new + list.unshift 1 + list.unshift 2 + list.unshift 3 + list.unshift 4 + list.pop + node = list.unshift 4 + list.to_s.should eq "[ 4, 4, 3, 2 ]" + list.delete node + list.to_s.should eq "[ 4, 3, 2 ]" + end + it "peek" do list = DoubleLinkedList(Int32).new list << 1 << 2 << 3 << 4 diff --git a/src/fifo.cr b/src/fifo.cr index 5315700..4402749 100644 --- a/src/fifo.cr +++ b/src/fifo.cr @@ -1,3 +1,5 @@ +require "./list.cr" + # This class enables to keep track of used data. # # Each time a value is added, it is put in a FIFO structure. @@ -46,6 +48,40 @@ end # TODO: this is a draft. class EfficientFIFO(V) + # This array is used as the *fifo structure*. + property list : DoubleLinkedList(V) + property hash : Hash(V, DoubleLinkedList::Node(V)) + def initialize(@max_entries : UInt32) + @list = DoubleLinkedList(V).new + @hash = Hash(V, DoubleLinkedList::Node(V)).new + end + + # Pushes a value in the FIFO and gets the oldest value whether it exceeds the allowed number of entries. + # NOTE: `#<<(v : V)` is (almost) the only function since it's enough for the intended use, feel free to improve this. + def <<(v : V) : V? + if node = hash[v]? + list.delete node + end + + # push as the first value of the structure + node = @list.unshift v + hash[v] = node + + # remove least recently used entry if `@list` is too big + if list.size > @max_entries + node = @list.pop + hash.delete node.value + node.value + else + nil + end + end + + # Removes a value. + def delete(v : V) + if node = hash[v]? + list.delete node + end end end diff --git a/src/list.cr b/src/list.cr index 29980ac..47b399f 100644 --- a/src/list.cr +++ b/src/list.cr @@ -88,7 +88,37 @@ class DoubleLinkedList(V) new_node end + # Removes an entry. + # + # ``` + # list = DoubleLinkedList(Int32).new + # list << 1 << 2 << 3 << 4 # -> [ 1, 2, 3, 4 ] + # list.delete_at 2 # -> [ 1, 2, 4 ] + # ``` + def delete(n : Node(V)) : Node(V) + if n == @first + @first = n.next + end + if n == @last + @last = n.previous + end + if prev_node = n.previous + prev_node.next = n.next + end + if next_node = n.next + next_node.previous = n.previous + end + @size -= 1 + n + end + # Removes an entry at an index. + # + # ``` + # list = DoubleLinkedList(Int32).new + # list << 1 << 2 << 3 << 4 # -> [ 1, 2, 3, 4 ] + # list.delete_at 2 # -> [ 1, 2, 4 ] + # ``` def delete_at(index : Int32) : Node(V) if index == 0 shift @@ -204,7 +234,7 @@ class DoubleLinkedList(V) @size += 1 new_node else - push(value) + push value end end