1 /* 2 * Created on june 12th, 2003 3 * Copyright (C) Azureus Software, Inc, All Rights Reserved. 4 * 5 * This program is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU General Public License 7 * as published by the Free Software Foundation; either version 2 8 * of the License, or (at your option) any later version. 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * You should have received a copy of the GNU General Public License 14 * along with this program; if not, write to the Free Software 15 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 16 * 17 */ 18 package org.gudy.azureus2.core3.util; 19 20 /** 21 * 22 * This class is used to compute average (mostly for speed transfer). 23 * 24 * @author Olivier 25 * 26 */ 27 public class Average { 28 /** 29 * It uses a simple array of longs to store values in a cycling way. 30 * The array has 2 more elements than really needed to compute the average. 31 * One is the next one to be filled, and its value is always 0, 32 * and the other one is the one currently filled, 33 * which value is not taken into account for the average. 34 */ 35 36 //The refresh rate of the average (ms) 37 private final int refreshRate; 38 39 //the period (in ms) 40 private final int period; 41 42 //The number of elements in the average 43 private final int nbElements; 44 45 //The time the average was last updated (divided by the refreshRate). 46 private long lastUpdate; 47 48 //The values 49 private long values[]; 50 51 /** 52 * Private constructor for an Average 53 * @param _refreshRate the refresh rate in ms 54 * @param _period the period in s 55 */ Average(int _refreshRate, int _period)56 protected Average(int _refreshRate, int _period) { 57 this.refreshRate = _refreshRate; 58 this.period = _period; 59 60 this.nbElements = (_period * 1000) / _refreshRate + 2; 61 this.lastUpdate = getEffectiveTime() / _refreshRate; 62 //this.values = new long[this.nbElements]; 63 } 64 65 /** 66 * The way to get a new Average Object, it does some parameter checking. 67 * refreshRate must be greater than 100, 68 * and period*1000 must be greater than refreshRate 69 * @param refreshRate in ms 70 * @param period in s 71 * @return the newlly created Average, or null if parameters are wrong 72 */ getInstance(int refreshRate, int period)73 public static Average getInstance(int refreshRate, int period) { 74 if (refreshRate < 100) 75 return null; 76 if ((period * 1000) < refreshRate) 77 return null; 78 return new Average(refreshRate, period); 79 } 80 81 public synchronized void clear()82 clear() 83 { 84 values = null; 85 lastUpdate = getEffectiveTime() / refreshRate; 86 } 87 88 public synchronized void cloneFrom( Average other )89 cloneFrom( 90 Average other ) 91 { 92 Object[] details = other.getCloneDetails(); 93 94 values = (long[])details[0]; 95 lastUpdate = ((Long)details[1]).longValue(); 96 } 97 98 private synchronized Object[] getCloneDetails()99 getCloneDetails() 100 { 101 return( new Object[]{ values, new Long( lastUpdate )} ); 102 } 103 104 /** 105 * This method is used to update the buffer tha stores the values, 106 * in fact it mostly does clean-up over this buffer, 107 * erasing all values that have not been updated. 108 * @param timeFactor which is the currentTime divided by the refresh Rate 109 */ update(long timeFactor)110 private void update(long timeFactor) { 111 //If we have a really OLD lastUpdate, we could erase the buffer a 112 //huge number of time, so if it's really old, we change it so we'll only 113 //erase the buffer once. 114 115 if (lastUpdate < timeFactor - nbElements) 116 lastUpdate = timeFactor - nbElements - 1; 117 118 if(values != null) 119 { 120 //For all values between lastUpdate + 1 (next value than last updated) 121 //and timeFactor (which is the new value insertion position) 122 for (long i = lastUpdate + 1; i <= timeFactor; i++) { 123 //We set the value to 0. 124 values[(int) (i % nbElements)] = 0; 125 } 126 //We also clear the next value to be inserted (so on next time change...) 127 values[(int) ((timeFactor + 1) % nbElements)] = 0; 128 } 129 130 //And we update lastUpdate. 131 lastUpdate = timeFactor; 132 } 133 134 /** 135 * Public method to add a value to the average, 136 * the time it is added is the time this method is called. 137 * @param value the value to be added to the Average 138 */ addValue(long value)139 public synchronized void addValue(long value) { 140 if(values == null && value != 0) 141 values = new long[nbElements]; 142 if(values != null) 143 { 144 //We get the current time factor. 145 long timeFactor = getEffectiveTime() / refreshRate; 146 //We first update the buffer. 147 update(timeFactor); 148 //And then we add our value to current element 149 values[(int) (timeFactor % nbElements)] += value; 150 } 151 152 } 153 154 /** 155 * This method can be called to get the current average value. 156 * @return the current Average computed. 157 */ getAverage()158 public long getAverage() { 159 return( getSum() / period ); 160 } 161 getDoubleAverage()162 public double getDoubleAverage() { 163 return( (double)getSum() / period ); 164 } 165 getDoubleAverageAsString( int precision )166 public String getDoubleAverageAsString( int precision ) { 167 return( DisplayFormatters.formatDecimal( getDoubleAverage(), precision )); 168 } 169 getAverage(int average_period )170 public long getAverage(int average_period ) 171 { 172 int slots = average_period<=0?(nbElements - 2):(average_period / refreshRate); 173 174 if ( slots <= 0 ){ 175 176 slots = 1; 177 178 }else if ( slots > nbElements - 2 ){ 179 180 slots = nbElements - 2; 181 } 182 183 if ( slots == 1 ){ 184 185 return( getPointValue()); 186 } 187 188 long res = getSum(slots) / ( period * slots / ( nbElements - 2 )); 189 190 return( res ); 191 } 192 193 public synchronized long getPointValue()194 getPointValue() 195 { 196 long timeFactor = getEffectiveTime() / refreshRate; 197 //We first update the buffer 198 update(timeFactor); 199 200 return(values != null ? values[(int)((timeFactor-1)% nbElements)] : 0); 201 } 202 getSum()203 protected synchronized final long getSum() { 204 //The sum of all elements used for the average. 205 long sum = 0; 206 207 if(values != null) 208 { 209 //We get the current timeFactor 210 long timeFactor = getEffectiveTime() / refreshRate; 211 //We first update the buffer 212 update(timeFactor); 213 //Starting on oldest one (the one after the next one) 214 //Ending on last one fully updated (the one previous current one) 215 for (long i = timeFactor + 2; i < timeFactor + nbElements; i++) { 216 //Simple addition 217 sum += values[(int) (i % nbElements)]; 218 } 219 } 220 221 222 //We return the sum divided by the period 223 return(sum); 224 } 225 getSum(int slots)226 protected synchronized final long getSum(int slots) { 227 //We get the current timeFactor 228 long timeFactor = getEffectiveTime() / refreshRate; 229 //We first update the buffer 230 update(timeFactor); 231 232 //The sum of all elements used for the average. 233 long sum = 0; 234 235 if ( slots < 1 ){ 236 237 slots = 1; 238 239 }else if ( slots > nbElements-2 ){ 240 241 slots = nbElements-2; 242 } 243 244 //Starting on oldest one (the one after the next one) 245 //Ending on last one fully updated (the one previous current one) 246 247 long end_slot = timeFactor + nbElements; 248 long start_slot = end_slot - slots; 249 250 if (values != null) 251 for (long i = start_slot; i < end_slot; i++) 252 { 253 sum += values[(int) (i % nbElements)]; 254 } 255 256 //We return the sum divided by the period 257 return(sum); 258 } 259 260 protected long getEffectiveTime()261 getEffectiveTime() 262 { 263 return( SystemTime.getSteppedMonotonousTime()); 264 } 265 } 266