1 /**
  2  * Returns an array of numbers, starting at <tt>start</tt>, incrementing by
  3  * <tt>step</tt>, until <tt>stop</tt> is reached. The stop value is
  4  * exclusive. If only a single argument is specified, this value is interpeted
  5  * as the <i>stop</i> value, with the <i>start</i> value as zero. If only two
  6  * arguments are specified, the step value is implied to be one.
  7  *
  8  * <p>The method is modeled after the built-in <tt>range</tt> method from
  9  * Python. See the Python documentation for more details.
 10  *
 11  * @see <a href="http://docs.python.org/library/functions.html#range">Python range</a>
 12  * @param {number} [start] the start value.
 13  * @param {number} stop the stop value.
 14  * @param {number} [step] the step value.
 15  * @returns {number[]} an array of numbers.
 16  */
 17 pv.range = function(start, stop, step) {
 18   if (arguments.length == 1) {
 19     stop = start;
 20     start = 0;
 21   }
 22   if (step == undefined) step = 1;
 23   if ((stop - start) / step == Infinity) throw new Error("range must be finite");
 24   var array = [], i = 0, j;
 25   if (step < 0) {
 26     while ((j = start + step * i++) > stop) {
 27       array.push(j);
 28     }
 29   } else {
 30     while ((j = start + step * i++) < stop) {
 31       array.push(j);
 32     }
 33   }
 34   return array;
 35 };
 36 
 37 /**
 38  * Returns a random number in the range [<tt>start</tt>, <tt>stop</tt>) that is
 39  * a multiple of <tt>step</tt>. More specifically, the returned number is of the
 40  * form <tt>start</tt> + <i>n</i> * <tt>step</tt>, where <i>n</i> is a
 41  * nonnegative integer. If <tt>step</tt> is not specified, it defaults to 1,
 42  * returning a random integer if <tt>start</tt> is also an integer.
 43  *
 44  * @param {number} [start] the start value value.
 45  * @param {number} stop the stop value.
 46  * @param {number} [step] the step value.
 47  * @returns {number} a random number between <i>start</i> and <i>stop</i>.
 48  */
 49 pv.random = function(start, stop, step) {
 50   if (arguments.length == 1) {
 51     stop = start;
 52     start = 0;
 53   }
 54   if (step == undefined) step = 1;
 55   return step
 56       ? (Math.floor(Math.random() * (stop - start) / step) * step + start)
 57       : (Math.random() * (stop - start) + start);
 58 };
 59 
 60 /**
 61  * Returns the sum of the specified array. If the specified array is not an
 62  * array of numbers, an optional accessor function <tt>f</tt> can be specified
 63  * to map the elements to numbers. See {@link #normalize} for an example.
 64  * Accessor functions can refer to <tt>this.index</tt>.
 65  *
 66  * @param {array} array an array of objects, or numbers.
 67  * @param {function} [f] an optional accessor function.
 68  * @returns {number} the sum of the specified array.
 69  */
 70 pv.sum = function(array, f) {
 71   var o = {};
 72   return array.reduce(f
 73       ? function(p, d, i) { o.index = i; return p + f.call(o, d); }
 74       : function(p, d) { return p + d; }, 0);
 75 };
 76 
 77 /**
 78  * Returns the maximum value of the specified array. If the specified array is
 79  * not an array of numbers, an optional accessor function <tt>f</tt> can be
 80  * specified to map the elements to numbers. See {@link #normalize} for an
 81  * example. Accessor functions can refer to <tt>this.index</tt>.
 82  *
 83  * @param {array} array an array of objects, or numbers.
 84  * @param {function} [f] an optional accessor function.
 85  * @returns {number} the maximum value of the specified array.
 86  */
 87 pv.max = function(array, f) {
 88   if (f == pv.index) return array.length - 1;
 89   return Math.max.apply(null, f ? pv.map(array, f) : array);
 90 };
 91 
 92 /**
 93  * Returns the index of the maximum value of the specified array. If the
 94  * specified array is not an array of numbers, an optional accessor function
 95  * <tt>f</tt> can be specified to map the elements to numbers. See
 96  * {@link #normalize} for an example. Accessor functions can refer to
 97  * <tt>this.index</tt>.
 98  *
 99  * @param {array} array an array of objects, or numbers.
100  * @param {function} [f] an optional accessor function.
101  * @returns {number} the index of the maximum value of the specified array.
102  */
103 pv.max.index = function(array, f) {
104   if (!array.length) return -1;
105   if (f == pv.index) return array.length - 1;
106   if (!f) f = pv.identity;
107   var maxi = 0, maxx = -Infinity, o = {};
108   for (var i = 0; i < array.length; i++) {
109     o.index = i;
110     var x = f.call(o, array[i]);
111     if (x > maxx) {
112       maxx = x;
113       maxi = i;
114     }
115   }
116   return maxi;
117 }
118 
119 /**
120  * Returns the minimum value of the specified array of numbers. If the specified
121  * array is not an array of numbers, an optional accessor function <tt>f</tt>
122  * can be specified to map the elements to numbers. See {@link #normalize} for
123  * an example. Accessor functions can refer to <tt>this.index</tt>.
124  *
125  * @param {array} array an array of objects, or numbers.
126  * @param {function} [f] an optional accessor function.
127  * @returns {number} the minimum value of the specified array.
128  */
129 pv.min = function(array, f) {
130   if (f == pv.index) return 0;
131   return Math.min.apply(null, f ? pv.map(array, f) : array);
132 };
133 
134 /**
135  * Returns the index of the minimum value of the specified array. If the
136  * specified array is not an array of numbers, an optional accessor function
137  * <tt>f</tt> can be specified to map the elements to numbers. See
138  * {@link #normalize} for an example. Accessor functions can refer to
139  * <tt>this.index</tt>.
140  *
141  * @param {array} array an array of objects, or numbers.
142  * @param {function} [f] an optional accessor function.
143  * @returns {number} the index of the minimum value of the specified array.
144  */
145 pv.min.index = function(array, f) {
146   if (!array.length) return -1;
147   if (f == pv.index) return 0;
148   if (!f) f = pv.identity;
149   var mini = 0, minx = Infinity, o = {};
150   for (var i = 0; i < array.length; i++) {
151     o.index = i;
152     var x = f.call(o, array[i]);
153     if (x < minx) {
154       minx = x;
155       mini = i;
156     }
157   }
158   return mini;
159 }
160 
161 /**
162  * Returns the arithmetic mean, or average, of the specified array. If the
163  * specified array is not an array of numbers, an optional accessor function
164  * <tt>f</tt> can be specified to map the elements to numbers. See
165  * {@link #normalize} for an example. Accessor functions can refer to
166  * <tt>this.index</tt>.
167  *
168  * @param {array} array an array of objects, or numbers.
169  * @param {function} [f] an optional accessor function.
170  * @returns {number} the mean of the specified array.
171  */
172 pv.mean = function(array, f) {
173   return pv.sum(array, f) / array.length;
174 };
175 
176 /**
177  * Returns the median of the specified array. If the specified array is not an
178  * array of numbers, an optional accessor function <tt>f</tt> can be specified
179  * to map the elements to numbers. See {@link #normalize} for an example.
180  * Accessor functions can refer to <tt>this.index</tt>.
181  *
182  * @param {array} array an array of objects, or numbers.
183  * @param {function} [f] an optional accessor function.
184  * @returns {number} the median of the specified array.
185  */
186 pv.median = function(array, f) {
187   if (f == pv.index) return (array.length - 1) / 2;
188   array = pv.map(array, f).sort(pv.naturalOrder);
189   if (array.length % 2) return array[Math.floor(array.length / 2)];
190   var i = array.length / 2;
191   return (array[i - 1] + array[i]) / 2;
192 };
193 
194 /**
195  * Returns the unweighted variance of the specified array. If the specified
196  * array is not an array of numbers, an optional accessor function <tt>f</tt>
197  * can be specified to map the elements to numbers. See {@link #normalize} for
198  * an example. Accessor functions can refer to <tt>this.index</tt>.
199  *
200  * @param {array} array an array of objects, or numbers.
201  * @param {function} [f] an optional accessor function.
202  * @returns {number} the variance of the specified array.
203  */
204 pv.variance = function(array, f) {
205   if (array.length < 1) return NaN;
206   if (array.length == 1) return 0;
207   var mean = pv.mean(array, f), sum = 0, o = {};
208   if (!f) f = pv.identity;
209   for (var i = 0; i < array.length; i++) {
210     o.index = i;
211     var d = f.call(o, array[i]) - mean;
212     sum += d * d;
213   }
214   return sum;
215 };
216 
217 /**
218  * Returns an unbiased estimation of the standard deviation of a population,
219  * given the specified random sample. If the specified array is not an array of
220  * numbers, an optional accessor function <tt>f</tt> can be specified to map the
221  * elements to numbers. See {@link #normalize} for an example. Accessor
222  * functions can refer to <tt>this.index</tt>.
223  *
224  * @param {array} array an array of objects, or numbers.
225  * @param {function} [f] an optional accessor function.
226  * @returns {number} the standard deviation of the specified array.
227  */
228 pv.deviation = function(array, f) {
229   return Math.sqrt(pv.variance(array, f) / (array.length - 1));
230 };
231 
232 /**
233  * Returns the logarithm with a given base value.
234  *
235  * @param {number} x the number for which to compute the logarithm.
236  * @param {number} b the base of the logarithm.
237  * @returns {number} the logarithm value.
238  */
239 pv.log = function(x, b) {
240   return Math.log(x) / Math.log(b);
241 };
242 
243 /**
244  * Computes a zero-symmetric logarithm. Computes the logarithm of the absolute
245  * value of the input, and determines the sign of the output according to the
246  * sign of the input value.
247  *
248  * @param {number} x the number for which to compute the logarithm.
249  * @param {number} b the base of the logarithm.
250  * @returns {number} the symmetric log value.
251  */
252 pv.logSymmetric = function(x, b) {
253   return (x == 0) ? 0 : ((x < 0) ? -pv.log(-x, b) : pv.log(x, b));
254 };
255 
256 /**
257  * Computes a zero-symmetric logarithm, with adjustment to values between zero
258  * and the logarithm base. This adjustment introduces distortion for values less
259  * than the base number, but enables simultaneous plotting of log-transformed
260  * data involving both positive and negative numbers.
261  *
262  * @param {number} x the number for which to compute the logarithm.
263  * @param {number} b the base of the logarithm.
264  * @returns {number} the adjusted, symmetric log value.
265  */
266 pv.logAdjusted = function(x, b) {
267   if (!isFinite(x)) return x;
268   var negative = x < 0;
269   if (x < b) x += (b - x) / b;
270   return negative ? -pv.log(x, b) : pv.log(x, b);
271 };
272 
273 /**
274  * Rounds an input value down according to its logarithm. The method takes the
275  * floor of the logarithm of the value and then uses the resulting value as an
276  * exponent for the base value.
277  *
278  * @param {number} x the number for which to compute the logarithm floor.
279  * @param {number} b the base of the logarithm.
280  * @returns {number} the rounded-by-logarithm value.
281  */
282 pv.logFloor = function(x, b) {
283   return (x > 0)
284       ? Math.pow(b, Math.floor(pv.log(x, b)))
285       : -Math.pow(b, -Math.floor(-pv.log(-x, b)));
286 };
287 
288 /**
289  * Rounds an input value up according to its logarithm. The method takes the
290  * ceiling of the logarithm of the value and then uses the resulting value as an
291  * exponent for the base value.
292  *
293  * @param {number} x the number for which to compute the logarithm ceiling.
294  * @param {number} b the base of the logarithm.
295  * @returns {number} the rounded-by-logarithm value.
296  */
297 pv.logCeil = function(x, b) {
298   return (x > 0)
299       ? Math.pow(b, Math.ceil(pv.log(x, b)))
300       : -Math.pow(b, -Math.ceil(-pv.log(-x, b)));
301 };
302 
303 (function() {
304   var radians = Math.PI / 180,
305       degrees = 180 / Math.PI;
306 
307   /** Returns the number of radians corresponding to the specified degrees. */
308   pv.radians = function(degrees) { return radians * degrees; };
309 
310   /** Returns the number of degrees corresponding to the specified radians. */
311   pv.degrees = function(radians) { return degrees * radians; };
312 })();
313