]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.history/src/org/simantics/history/util/WeightedMedian2.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.history / src / org / simantics / history / util / WeightedMedian2.java
1 /*******************************************************************************\r
2  * Copyright (c) 2007, 2011 Association for Decentralized Information Management in\r
3  * Industry THTH ry.\r
4  * All rights reserved. This program and the accompanying materials\r
5  * are made available under the terms of the Eclipse Public License v1.0\r
6  * which accompanies this distribution, and is available at\r
7  * http://www.eclipse.org/legal/epl-v10.html\r
8  *\r
9  * Contributors:\r
10  *     VTT Technical Research Centre of Finland - initial API and implementation\r
11  *******************************************************************************/\r
12 package org.simantics.history.util;\r
13 \r
14 import org.simantics.databoard.annotations.Optional;\r
15 import org.simantics.databoard.annotations.Referable;\r
16 \r
17 /**\r
18  * \r
19  * @author toni.kalajainen\r
20  */\r
21 public class WeightedMedian2 {\r
22 \r
23         public int maxItems;\r
24         public int itemCount;\r
25         @Optional public Item median;\r
26         \r
27         // Relative pos = pos - median.accumulated weight \r
28         public double rpos;\r
29         \r
30         public WeightedMedian2()\r
31         {\r
32                 maxItems = Integer.MAX_VALUE;\r
33         }\r
34         \r
35         public WeightedMedian2(int maxItems)\r
36         {\r
37                 this.maxItems = maxItems;\r
38         }\r
39         \r
40         public void clear() {\r
41                 itemCount = 0;\r
42                 median = null;\r
43                 rpos = 0;\r
44         }\r
45         \r
46         public void add(double weight, double value)\r
47         {\r
48                 if (weight<=0) return;\r
49                 if (median == null) {\r
50                         median = new Item( weight, value );\r
51                         itemCount++;\r
52                         rpos = weight / 2;\r
53                         return;                 \r
54                 }\r
55                 \r
56                 Item i = median;\r
57                 while (true) {\r
58                         \r
59                         if (value>=i.low && value<=i.high) {\r
60                                 double totWeight = i.weight+weight;\r
61                                 i.value = ( i.value * i.weight + value * weight ) / totWeight;\r
62                                 i.weight = totWeight;\r
63                                 break;\r
64                         }\r
65                         \r
66                         if (value < i.low) {\r
67                                 \r
68                                 // i = 1st, add new item as first\r
69                                 if (i.prev==null) {\r
70                                         i.prev = new Item( weight, value, null, i );\r
71                                         itemCount++;\r
72                                         break;\r
73                                 }\r
74                                 \r
75                                 // value is between two items, add new item\r
76                                 if (i.prev.high < value) {\r
77                                         Item n = new Item( weight, value, i.prev, i );\r
78                                         itemCount++;\r
79                                         i.prev.next = n;\r
80                                         i.prev = n;\r
81                                         break;\r
82                                 }\r
83                                 \r
84                                 i = i.prev;\r
85                                 continue;\r
86                         }                       \r
87                         \r
88                         if (value > i.high) {\r
89                                 \r
90                                 // i = last, add new item as last\r
91                                 if (i.next==null) {\r
92                                         i.next = new Item( weight, value, i, null );                                    \r
93                                         itemCount++;\r
94                                         break;\r
95                                 }\r
96                                 \r
97                                 // value is between two items, add new item\r
98                                 if (i.next.low > value) {\r
99                                         Item n = new Item( weight, value, i, i.next );\r
100                                         itemCount++;\r
101                                         i.next.prev = n;\r
102                                         i.next = n;\r
103                                         break;\r
104                                 }\r
105                                 \r
106                                 i = i.next;\r
107                                 continue;\r
108                         }\r
109                 }\r
110 \r
111                 rpos += (value<median.low?-1:1) * weight / 2;\r
112                 while (rpos<0) {\r
113                         median = median.prev;\r
114                         rpos += median.weight;\r
115                 }\r
116                 while (rpos>=median.weight) {\r
117                         rpos -= median.weight;\r
118                         median = median.next;\r
119                 }\r
120                 \r
121                 if (itemCount>maxItems) coalesce();\r
122         }\r
123         \r
124         public void coalesce() \r
125         {\r
126                 Item i = median.next;\r
127                 while (i!=null) {\r
128                         Item n = i.next;\r
129                         if (n==null) break;\r
130                         i.next = n.next;\r
131                         if (n.next!=null) n.next.prev = i;\r
132                         double totWeight = i.weight+n.weight;\r
133                         i.high = n.high;\r
134                         i.value = ( i.value * i.weight + n.value * n.weight ) / totWeight;\r
135                         i.weight = totWeight;\r
136                         i = i.next;\r
137                 }\r
138                 \r
139                 i = median.prev;\r
140                 while (i!=null) {\r
141                         Item p = i.prev;\r
142                         if (p==null) break;\r
143                         i.prev = p.prev;\r
144                         if (p.prev!=null) p.prev.next = i;\r
145                         double totWeight = i.weight+p.weight;\r
146                         i.low = p.low;\r
147                         i.value = ( i.value * i.weight + p.value * p.weight ) / totWeight;\r
148                         i.weight = totWeight;\r
149                         i = i.prev;\r
150                 }               \r
151         }\r
152         \r
153         public double getMedian() \r
154         {\r
155                 if (median==null) return Double.NaN;\r
156                 if (median.low == median.high) return median.value;\r
157                 double d = median.high - median.low;\r
158                 double r = rpos/median.weight;\r
159                 return median.low + d*r;\r
160         }\r
161         \r
162         @Override\r
163         public String toString() {\r
164                 StringBuilder sb = new StringBuilder();\r
165                 sb.append( "Median="+getMedian()+" ");\r
166                 \r
167                 Item i = first();\r
168                 while (i!=null) {\r
169                         sb.append("(");\r
170                         sb.append("value=");\r
171                         if (i.low==i.high) sb.append(i.value); else \r
172                                 sb.append(i.value+" ("+i.low+".."+i.high+")");\r
173                         sb.append(", w="+i.weight);\r
174                         if (i==median) {\r
175                                 sb.append(", pos="+rpos);\r
176                         }\r
177                         sb.append(")");\r
178                         i = i.next;\r
179                 }\r
180                 \r
181                 return sb.toString();\r
182         } \r
183         \r
184         Item first() {\r
185                 if (median==null) return null;\r
186                 Item i = median;\r
187                 while (i.prev!=null) i = i.prev;\r
188                 return i;\r
189         }\r
190         \r
191         @Referable\r
192         static public class Item {\r
193                 public double weight;\r
194                 public double low;\r
195                 public double high;\r
196                 public double value;\r
197                 \r
198                 public Item() {}\r
199                 \r
200                 public Item(double weight, double value) {\r
201                         this.weight = weight;\r
202                         this.low = this.high = value;\r
203                 }\r
204 \r
205                 public Item(double weight, double value, Item prev, Item next) {\r
206                         this.weight = weight;\r
207                         this.value = this.low = this.high = value;\r
208                         this.prev = prev;\r
209                         this.next = next;\r
210                 }\r
211                 \r
212                 @Optional\r
213                 public Item prev;\r
214                 \r
215                 @Optional\r
216                 public Item next;\r
217                 \r
218                 @Override\r
219                 public String toString() {\r
220                         if (low==high) return "(value="+value+", w="+weight+")";\r
221                         return "(value="+value+" ("+low+".."+high+"), w="+weight+")";\r
222                 }\r
223         }\r
224         \r
225 }\r