a9621fee449af062bf1cbd8a484cdaf4eea02e75
[nit.git] / src / counter.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 # Simple numerical statistical analysis and presentation
16 module counter
17
18 # A counter counts occurrences of things
19 # Use this instead of a HashMap[E, Int]
20 class Counter[E: Object]
21 super Map[E, Int]
22
23 # Total number of counted occurrences
24 var total: Int = 0
25
26 private var map = new HashMap[E, Int]
27
28 # The number of counted occurrences of `e'
29 redef fun [](e: E): Int
30 do
31 var map = self.map
32 if map.has_key(e) then return map[e]
33 return 0
34 end
35
36 redef fun []=(e: E, value: Int)
37 do
38 total -= self[e]
39 self.map[e] = value
40 total += value
41 end
42
43 redef fun keys do return map.keys
44
45 redef fun values do return map.values
46
47 # Count one more occurrence of `e'
48 fun inc(e: E)
49 do
50 self.map[e] = self[e] + 1
51 total += 1
52 end
53
54 # Return an array of elements sorted by occurrences
55 fun sort: Array[E]
56 do
57 var res = map.keys.to_a
58 var sorter = new CounterSorter[E](self)
59 sorter.sort(res)
60 #res.sort !cmp a, b = map[a] <=> map[b]
61 return res
62 end
63
64 # Display statistical information
65 fun print_summary
66 do
67 var list = self.sort
68 print " population: {list.length}"
69 if list.is_empty then return
70 print " minimum value: {self[list.first]}"
71 print " maximum value: {self[list.last]}"
72 print " total value: {self.total}"
73 print " average value: {div(self.total,list.length)}"
74 print " distribution:"
75 var count = 0
76 var sum = 0
77 var limit = self[list.first]
78 for t in list do
79 if self[t] > limit then
80 print " <={limit}: sub-population={count} ({div(count*100,list.length)}%); cumulated value={sum} ({div(sum*100,self.total)}%)"
81 count = 0
82 sum = 0
83 while self[t] > limit do
84 limit = limit * 2
85 if limit == 0 then limit = 1
86 end
87 end
88 count += 1
89 sum += self[t]
90 end
91 print " <={limit}: sub-population={count} ({div(count*100,list.length)}%); cumulated value={sum} ({div(sum*100,self.total)}%)"
92 end
93 end
94
95 private class CounterSorter[E: Object]
96 super AbstractSorter[E]
97 var counter: Counter[E]
98 redef fun compare(a,b) do return self.counter.map[a] <=> self.counter.map[b]
99 end
100
101 # Helper function to display n/d and handle division by 0
102 fun div(n: Int, d: Int): String
103 do
104 if d == 0 then return "na"
105 return ((100*n/d).to_f/100.0).to_precision(2)
106 end