1 /* ============================================================
2  * JRobin : Pure java implementation of RRDTool's functionality
3  * ============================================================
4  *
5  * Project Info:  http://www.jrobin.org
6  * Project Lead:  Sasa Markovic (saxon@jrobin.org);
7  *
8  * (C) Copyright 2003-2005, by Sasa Markovic.
9  *
10  * Developers:    Sasa Markovic (saxon@jrobin.org)
11  *
12  *
13  * This library is free software; you can redistribute it and/or modify it under the terms
14  * of the GNU Lesser General Public License as published by the Free Software Foundation;
15  * either version 2.1 of the License, or (at your option) any later version.
16  *
17  * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
18  * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
19  * See the GNU Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public License along with this
22  * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
23  * Boston, MA 02111-1307, USA.
24  */

25
26 package org.jrobin.data;
27
28 import org.jrobin.core.ConsolFuns;
29 import org.jrobin.core.Util;
30
31 import java.util.ArrayList;
32 import java.util.Arrays;
33 import java.util.List;
34
35 class Aggregator implements ConsolFuns {
36     private long timestamps[], step;
37     private double[] values;
38
39     Aggregator(long[] timestamps, double[] values) {
40         assert timestamps.length == values.length: "Incompatible timestamps/values arrays (unequal lengths)";
41         assert timestamps.length >= 2: "At least two timestamps must be supplied";
42         this.timestamps = timestamps;
43         this.values = values;
44         this.step = timestamps[1] - timestamps[0];
45     }
46
47     Aggregates getAggregates(long tStart, long tEnd) {
48         Aggregates agg = new Aggregates();
49         long totalSeconds = 0;
50         boolean firstFound = false;
51         for (int i = 0; i < timestamps.length; i++) {
52             long left = Math.max(timestamps[i] - step, tStart);
53             long right = Math.min(timestamps[i], tEnd);
54             long delta = right - left;
55
56             // delta is only > 0 when the timestamp for a given buck is within the range of tStart and tEnd
57             if (delta > 0) {
58                 double value = values[i];
59                 agg.min = Util.min(agg.min, value);
60                 agg.max = Util.max(agg.max, value);
61                 if (!firstFound) {
62                     agg.first = value;
63                     firstFound = true;
64                     agg.last = value;
65                 } else if (delta >= step) {  // an entire bucket is included in this range
66                     agg.last = value;
67
68                     /*
69                      * Algorithmically, we're only updating last if it's either the first
70                      * bucket encountered, or it's a "full" bucket.
71
72                     if ( !isInRange(tEnd, left, right) ||
73                              (isInRange(tEnd, left, right) && !Double.isNaN(value))
74                              ) {
75                             agg.last = value;
76                         }
77                     */

78
79                 }
80                 if (!Double.isNaN(value)) {
81                     agg.total = Util.sum(agg.total, delta * value);
82                     totalSeconds += delta;
83                 }
84             }
85         }
86         agg.average = totalSeconds > 0 ? (agg.total / totalSeconds) : Double.NaN;
87         return agg;
88     }
89
90     double getPercentile(long tStart, long tEnd, double percentile) {
91         List<Double> valueList = new ArrayList<Double>();
92         // create a list of included datasource values (different from NaN)
93         for (int i = 0; i < timestamps.length; i++) {
94             long left = Math.max(timestamps[i] - step, tStart);
95             long right = Math.min(timestamps[i], tEnd);
96             if (right > left && !Double.isNaN(values[i])) {
97                 valueList.add(values[i]);
98             }
99         }
100         // create an array to work with
101         int count = valueList.size();
102         if (count > 1) {
103             double[] valuesCopy = new double[count];
104             for (int i = 0; i < count; i++) {
105                 valuesCopy[i] = valueList.get(i);
106             }
107             // sort array
108             Arrays.sort(valuesCopy);
109             // skip top (100% - percentile) values
110             double topPercentile = (100.0 - percentile) / 100.0;
111             count -= (int) Math.ceil(count * topPercentile);
112             // if we have anything left...
113             if (count > 0) {
114                 return valuesCopy[count - 1];
115             }
116         }
117         // not enough data available
118         return Double.NaN;
119     }
120 }
121