lib/standard: introduce ReserveDefaultComparator
[nit.git] / lib / standard / collection / sorter.nit
index 0b4cd39..ca16460 100644 (file)
@@ -4,34 +4,90 @@
 #
 # This file is free software, which comes along with NIT.  This software is
 # distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
-# without  even  the implied warranty of  MERCHANTABILITY or  FITNESS FOR A 
+# without  even  the implied warranty of  MERCHANTABILITY or  FITNESS FOR A
 # PARTICULAR PURPOSE.  You can modify it is you want,  provided this header
 # is kept unaltered, and a notification of the changes is added.
 # You  are  allowed  to  redistribute it and sell it, alone or is a part of
 # another product.
 
-# This module contains classes used to sorts arrays.
-# In order to provide your own sort class you should define a subclass of AbstractSorter with
-# a custom `compare' function.
-package sorter
+# This module contains classes used to compare things and sorts arrays.
+#
+# In order to provide your own sort class you should define a subclass of `Comparator` with
+# a custom `Comparator::compare` function and a specific `COMPARED` virtual type.
+module sorter
 
+import range
 import array
 
 # This abstract class generalizes ways to sort an array
-# TODO: rename *Sorter to *Comparator
-interface AbstractSorter[E]
-       # Compare `a' and `b'.
+interface Comparator
+       # What to compare to
+       type COMPARED: nullable Object
+
+       # Compare `a` and `b`.
        # Returns:
-       #       -1 if a < b
+       #       -1 if a < b
        #       0  if a = b
        #       1  if a > b
-       fun compare(a: E, b: E): Int is abstract
+       fun compare(a: COMPARED, b: COMPARED): Int is abstract
+
+       # Is `seq` sorted?
+       #
+       #     assert default_comparator.is_sorted([1,2,2,3])   == true
+       #     assert default_comparator.is_sorted([1,10,2,3])  == false
+       #     assert alpha_comparator.is_sorted([1,10,2,3])    == true
+       fun is_sorted(seq: SequenceRead[COMPARED]): Bool
+       do
+               if seq.length <= 1 then return true
+               var prev = seq.first
+               for e in seq do
+                       if compare(prev, e) > 0 then return false
+                       prev = e
+               end
+               return true
+       end
+
+       # Returns the minimum between `a` and `b`.
+       #
+       #     assert default_comparator.min(2,10) == 2
+       #     assert alpha_comparator.min(2,10)   == 10
+       #
+       # If both are equivalent, then returns `a`.
+       #
+       #     var m = alpha_comparator.min(1, "1")
+       #     assert m == 1
+       #     assert m != "1"
+       fun min(a,b: COMPARED): COMPARED
+       do
+               if compare(a,b) > 0 then return b else return a
+       end
+
+       # Returns the maximum between `a` and `b`.
+       #
+       #     assert default_comparator.max(2,10) == 10
+       #     assert alpha_comparator.max(2,10)   == 2
+       #
+       # If both are equivalent, then returns `a`.
+       #
+       #     var m = alpha_comparator.max(1, "1")
+       #     assert m == 1
+       #     assert m != "1"
+       fun max(a,b: COMPARED): COMPARED
+       do
+               if compare(a,b) < 0 then return b else return a
+       end
 
-       # Sort `array' using the `compare' function.
-       fun sort(array: Array[E]) do sub_sort(array, 0, array.length-1)
+       # Sort `array` using the `compare` function.
+       #
+       #     var a = [10, 2, 3, 1, 4]
+       #     default_comparator.sort(a)
+       #     assert a == [1, 2, 3, 4, 10]
+       #     alpha_comparator.sort(a)
+       #     assert a == [1, 10, 2, 3, 4]
+       fun sort(array: Array[COMPARED]) do sub_sort(array, 0, array.length-1)
 
-       # Sort `array' between `from' and `to' indices
-       private fun sub_sort(array: Array[E], from: Int, to: Int)
+       # Sort `array` between `from` and `to` indices
+       private fun sub_sort(array: Array[COMPARED], from: Int, to: Int)
        do
                if from >= to then
                        return
@@ -42,9 +98,13 @@ interface AbstractSorter[E]
                end
        end
 
-       # Quick-sort `array' between `from' and `to' indices
-       private fun quick_sort(array: Array[E], from: Int, to: Int)
-       do
+       # Quick-sort `array` between `from` and `to` indices
+       # Worst case: O(n^2), Average case: O(n lg n)
+       #
+       #     var a = [5, 2, 3, 1, 4]
+       #     default_comparator.quick_sort(a, 0, a.length - 1)
+       #     assert a == [1, 2, 3, 4, 5]
+       fun quick_sort(array: Array[COMPARED], from: Int, to: Int) do
                var pivot = array[from]
                var i = from
                var j = to
@@ -62,9 +122,14 @@ interface AbstractSorter[E]
                sub_sort(array, from, i-2)
                sub_sort(array, i, to)
        end
-       
-       # Bubble-sort `array' between `from' and `to' indices
-       private fun bubble_sort(array: Array[E], from: Int, to: Int)
+
+       # Bubble-sort `array` between `from` and `to` indices
+       # Worst case: O(n^2), average case: O(n^2)
+       #
+       #     var a = [5, 2, 3, 1, 4]
+       #     default_comparator.bubble_sort(a, 0, a.length - 1)
+       #     assert a == [1, 2, 3, 4, 5]
+       fun bubble_sort(array: Array[COMPARED], from: Int, to: Int)
        do
                var i = from
                while i < to do
@@ -85,15 +150,209 @@ interface AbstractSorter[E]
                        i += 1
                end
        end
+
+       # Insertion-sort `array` between `from` and `to` indices
+       # Worst case: O(n^2), average case: O(n^2)
+       #
+       #     var a = [5, 2, 3, 1, 4]
+       #     default_comparator.insertion_sort(a, 0, a.length - 1)
+       #     assert a == [1, 2, 3, 4, 5]
+       fun insertion_sort(array: Array[COMPARED], from: Int, to: Int) do
+               for i in [from..to] do
+                       var j = i
+                       while j > 0 and compare(array[j], array[j - 1]) < 0 do
+                               array.swap_at(j, j - 1)
+                               j -= 1
+                       end
+               end
+       end
+
+       # Merge-sort `array` between `from` and `to` indices
+       # Worst case: O(n lg n), average: O(n lg n)
+       #
+       #     var a = [5, 2, 3, 1, 4]
+       #     default_comparator.merge_sort(a, 0, a.length - 1)
+       #     assert a == [1, 2, 3, 4, 5]
+       fun merge_sort(array: Array[COMPARED], from, to: Int) do
+               if from >= to then return
+               var mid = (to + from) / 2
+               merge_sort(array, from, mid)
+               merge_sort(array, mid + 1, to)
+               merge(array, from, mid, to)
+       end
+
+       private fun merge(array: Array[COMPARED], from, mid, to: Int) do
+               var l = new Array[COMPARED]
+               for i in [from..mid] do l.add array[i]
+               var r = new Array[COMPARED]
+               for i in [mid + 1..to] do r.add array[i]
+               var i = 0
+               var j = 0
+               for k in [from..to] do
+                       if i >= l.length then
+                               array[k] = r[j]
+                               j += 1
+                       else if j >= r.length then
+                               array[k] = l[i]
+                               i += 1
+                       else if compare(l[i], r[j]) <= 0 then
+                               array[k] = l[i]
+                               i += 1
+                       else
+                               array[k] = r[j]
+                               j += 1
+                       end
+               end
+       end
+
+       # Heap-sort `array` between `from` and `to` indices
+       # Worst case: O(n lg n), average: O(n lg n)
+       #
+       #     var a = [5, 2, 3, 1, 4]
+       #     default_comparator.heap_sort(a, 0, a.length - 1)
+       #     assert a == [1, 2, 3, 4, 5]
+       fun heap_sort(array: Array[COMPARED], from, to: Int) do
+               var size = build_heap(array)
+               for j in [from..to[ do
+                       array.swap_at(0, size)
+                       size -= 1
+                       heapify(array, 0, size)
+               end
+       end
+
+       private fun build_heap(array: Array[COMPARED]): Int do
+               var size = array.length - 1
+               var i = size / 2
+               while i >= 0 do
+                       heapify(array, i, size)
+                       i -= 1
+               end
+               return size
+       end
+
+       private fun heapify(array: Array[COMPARED], from, to: Int) do
+               var l = from * 2
+               var r = l + 1
+               var largest: Int
+               if l < to and compare(array[l], array[from]) > 0 then
+                       largest = l
+               else
+                       largest = from
+               end
+               if r < to and compare(array[r], array[largest]) > 0 then
+                       largest = r
+               end
+               if largest != from then
+                       array.swap_at(from, largest)
+                       heapify(array, largest, to)
+               end
+       end
+
+end
+
+redef class MapRead[K,V]
+       # Return an array of all values sorted with their keys using `comparator`.
+       #
+       # ~~~
+       # var map = new HashMap[Int, String]
+       # map[10] = "ten"
+       # map[2]  = "two"
+       # map[1]  = "one"
+       # assert map.values_sorted_by_key(default_comparator) == ["one", "two", "ten"]
+       # assert map.values_sorted_by_key(alpha_comparator) == ["one", "ten", "two"]
+       # ~~~
+       fun values_sorted_by_key(comparator: Comparator): Array[V]
+       do
+               var keys = self.keys.to_a
+               comparator.sort(keys)
+               return [for k in keys do self[k]]
+       end
+
+       # Return an array of all keys sorted with their values using `comparator`.
+       #
+       # ~~~
+       # var map = new HashMap[String, Int]
+       # map["ten"] = 10
+       # map["two"] = 2
+       # map["one"] = 1
+       # assert map.keys_sorted_by_values(default_comparator) == ["one", "two", "ten"]
+       # assert map.keys_sorted_by_values(alpha_comparator) == ["one", "ten", "two"]
+       # ~~~
+       #
+       # See: `to_map_comparator` to get the comparator used internally.
+       fun keys_sorted_by_values(comparator: Comparator): Array[K]
+       do
+               var keys = self.keys.to_a
+               var map_cmp = to_map_comparator(comparator)
+               map_cmp.sort(keys)
+               return keys
+       end
+
+       # A comparator that compares things with their values in self.
+       #
+       # See `MapComparator` for details.
+       fun to_map_comparator(comparator: Comparator): MapComparator[K, V] do return new MapComparator[K,V](self, comparator)
 end
 
-# This class uses the operator <=> to sort arrays.
-# You can also use the `sort' method of the `Array' class.
-class ComparableSorter[E: Comparable]
-       super AbstractSorter[E]
+# A comparator that compares things with their values in a map.
+#
+# ~~~
+# var map = new HashMap[String, Int]
+# map["ten"] = 10
+# map["two"] = 2
+# map["one"] = 1
+#
+# var map_cmp = map.to_map_comparator(default_comparator)
+# var a = ["ten", "one", "two"]
+# map_cmp.sort(a)
+# assert a == ["one", "two", "ten"]
+# map_cmp = map.to_map_comparator(alpha_comparator)
+# map_cmp.sort(a)
+# assert a == ["one", "ten", "two"]
+# ~~~
+class MapComparator[K,V]
+       super Comparator
+
+       # What is compared are the keys of the values
+       redef type COMPARED: K
+
+       # The map that associates compared elements to the value used to compare them
+       var map: MapRead[K,V]
+
+       # The comparator used to compare values
+       var comparator: Comparator
+
+       redef fun compare(a,b) do return comparator.compare(map[a], map[b])
+end
+
+# This comparator uses the operator `<=>` to compare objects.
+# see `default_comparator` for an easy-to-use general stateless default comparator.
+class DefaultComparator
+       super Comparator
+       redef type COMPARED: Comparable
        # Return a <=> b
        redef fun compare(a, b) do return a <=> b
+end
+
+# This comparator uses the operator `<=>` to compare objects in a reverse order.
+#
+# See `default_reverse_comparator` for an easy-to-use general stateless reverse
+# default comparator.
+class DefaultReverseComparator
+       super Comparator
+
+       redef type COMPARED: Comparable
 
-       init do end
+       # Returns `b <=> a`.
+       redef fun compare(a, b) do return b <=> a
 end
 
+# Easy-to-use general stateless default comparator that uses `<=>` to compare things.
+fun default_comparator: DefaultComparator do return once new DefaultComparator
+
+# Easy-to-use general stateless default reverse comparator.
+#
+# Does the same as `default_comparator` but in reverse order.
+fun default_reverse_comparator: DefaultReverseComparator do
+       return once new DefaultReverseComparator
+end