lib/goog/math/math.js

1// Copyright 2006 The Closure Library Authors. All Rights Reserved.
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/**
16 * @fileoverview Additional mathematical functions.
17 */
18
19goog.provide('goog.math');
20
21goog.require('goog.array');
22goog.require('goog.asserts');
23
24
25/**
26 * Returns a random integer greater than or equal to 0 and less than {@code a}.
27 * @param {number} a The upper bound for the random integer (exclusive).
28 * @return {number} A random integer N such that 0 <= N < a.
29 */
30goog.math.randomInt = function(a) {
31 return Math.floor(Math.random() * a);
32};
33
34
35/**
36 * Returns a random number greater than or equal to {@code a} and less than
37 * {@code b}.
38 * @param {number} a The lower bound for the random number (inclusive).
39 * @param {number} b The upper bound for the random number (exclusive).
40 * @return {number} A random number N such that a <= N < b.
41 */
42goog.math.uniformRandom = function(a, b) {
43 return a + Math.random() * (b - a);
44};
45
46
47/**
48 * Takes a number and clamps it to within the provided bounds.
49 * @param {number} value The input number.
50 * @param {number} min The minimum value to return.
51 * @param {number} max The maximum value to return.
52 * @return {number} The input number if it is within bounds, or the nearest
53 * number within the bounds.
54 */
55goog.math.clamp = function(value, min, max) {
56 return Math.min(Math.max(value, min), max);
57};
58
59
60/**
61 * The % operator in JavaScript returns the remainder of a / b, but differs from
62 * some other languages in that the result will have the same sign as the
63 * dividend. For example, -1 % 8 == -1, whereas in some other languages
64 * (such as Python) the result would be 7. This function emulates the more
65 * correct modulo behavior, which is useful for certain applications such as
66 * calculating an offset index in a circular list.
67 *
68 * @param {number} a The dividend.
69 * @param {number} b The divisor.
70 * @return {number} a % b where the result is between 0 and b (either 0 <= x < b
71 * or b < x <= 0, depending on the sign of b).
72 */
73goog.math.modulo = function(a, b) {
74 var r = a % b;
75 // If r and b differ in sign, add b to wrap the result to the correct sign.
76 return (r * b < 0) ? r + b : r;
77};
78
79
80/**
81 * Performs linear interpolation between values a and b. Returns the value
82 * between a and b proportional to x (when x is between 0 and 1. When x is
83 * outside this range, the return value is a linear extrapolation).
84 * @param {number} a A number.
85 * @param {number} b A number.
86 * @param {number} x The proportion between a and b.
87 * @return {number} The interpolated value between a and b.
88 */
89goog.math.lerp = function(a, b, x) {
90 return a + x * (b - a);
91};
92
93
94/**
95 * Tests whether the two values are equal to each other, within a certain
96 * tolerance to adjust for floating point errors.
97 * @param {number} a A number.
98 * @param {number} b A number.
99 * @param {number=} opt_tolerance Optional tolerance range. Defaults
100 * to 0.000001. If specified, should be greater than 0.
101 * @return {boolean} Whether {@code a} and {@code b} are nearly equal.
102 */
103goog.math.nearlyEquals = function(a, b, opt_tolerance) {
104 return Math.abs(a - b) <= (opt_tolerance || 0.000001);
105};
106
107
108// TODO(user): Rename to normalizeAngle, retaining old name as deprecated
109// alias.
110/**
111 * Normalizes an angle to be in range [0-360). Angles outside this range will
112 * be normalized to be the equivalent angle with that range.
113 * @param {number} angle Angle in degrees.
114 * @return {number} Standardized angle.
115 */
116goog.math.standardAngle = function(angle) {
117 return goog.math.modulo(angle, 360);
118};
119
120
121/**
122 * Normalizes an angle to be in range [0-2*PI). Angles outside this range will
123 * be normalized to be the equivalent angle with that range.
124 * @param {number} angle Angle in radians.
125 * @return {number} Standardized angle.
126 */
127goog.math.standardAngleInRadians = function(angle) {
128 return goog.math.modulo(angle, 2 * Math.PI);
129};
130
131
132/**
133 * Converts degrees to radians.
134 * @param {number} angleDegrees Angle in degrees.
135 * @return {number} Angle in radians.
136 */
137goog.math.toRadians = function(angleDegrees) {
138 return angleDegrees * Math.PI / 180;
139};
140
141
142/**
143 * Converts radians to degrees.
144 * @param {number} angleRadians Angle in radians.
145 * @return {number} Angle in degrees.
146 */
147goog.math.toDegrees = function(angleRadians) {
148 return angleRadians * 180 / Math.PI;
149};
150
151
152/**
153 * For a given angle and radius, finds the X portion of the offset.
154 * @param {number} degrees Angle in degrees (zero points in +X direction).
155 * @param {number} radius Radius.
156 * @return {number} The x-distance for the angle and radius.
157 */
158goog.math.angleDx = function(degrees, radius) {
159 return radius * Math.cos(goog.math.toRadians(degrees));
160};
161
162
163/**
164 * For a given angle and radius, finds the Y portion of the offset.
165 * @param {number} degrees Angle in degrees (zero points in +X direction).
166 * @param {number} radius Radius.
167 * @return {number} The y-distance for the angle and radius.
168 */
169goog.math.angleDy = function(degrees, radius) {
170 return radius * Math.sin(goog.math.toRadians(degrees));
171};
172
173
174/**
175 * Computes the angle between two points (x1,y1) and (x2,y2).
176 * Angle zero points in the +X direction, 90 degrees points in the +Y
177 * direction (down) and from there we grow clockwise towards 360 degrees.
178 * @param {number} x1 x of first point.
179 * @param {number} y1 y of first point.
180 * @param {number} x2 x of second point.
181 * @param {number} y2 y of second point.
182 * @return {number} Standardized angle in degrees of the vector from
183 * x1,y1 to x2,y2.
184 */
185goog.math.angle = function(x1, y1, x2, y2) {
186 return goog.math.standardAngle(goog.math.toDegrees(Math.atan2(y2 - y1,
187 x2 - x1)));
188};
189
190
191/**
192 * Computes the difference between startAngle and endAngle (angles in degrees).
193 * @param {number} startAngle Start angle in degrees.
194 * @param {number} endAngle End angle in degrees.
195 * @return {number} The number of degrees that when added to
196 * startAngle will result in endAngle. Positive numbers mean that the
197 * direction is clockwise. Negative numbers indicate a counter-clockwise
198 * direction.
199 * The shortest route (clockwise vs counter-clockwise) between the angles
200 * is used.
201 * When the difference is 180 degrees, the function returns 180 (not -180)
202 * angleDifference(30, 40) is 10, and angleDifference(40, 30) is -10.
203 * angleDifference(350, 10) is 20, and angleDifference(10, 350) is -20.
204 */
205goog.math.angleDifference = function(startAngle, endAngle) {
206 var d = goog.math.standardAngle(endAngle) -
207 goog.math.standardAngle(startAngle);
208 if (d > 180) {
209 d = d - 360;
210 } else if (d <= -180) {
211 d = 360 + d;
212 }
213 return d;
214};
215
216
217/**
218 * Returns the sign of a number as per the "sign" or "signum" function.
219 * @param {number} x The number to take the sign of.
220 * @return {number} -1 when negative, 1 when positive, 0 when 0. Preserves
221 * signed zeros and NaN.
222 */
223goog.math.sign = Math.sign || function(x) {
224 if (x > 0) {
225 return 1;
226 }
227 if (x < 0) {
228 return -1;
229 }
230 return x; // Preserves signed zeros and NaN.
231};
232
233
234/**
235 * JavaScript implementation of Longest Common Subsequence problem.
236 * http://en.wikipedia.org/wiki/Longest_common_subsequence
237 *
238 * Returns the longest possible array that is subarray of both of given arrays.
239 *
240 * @param {Array<Object>} array1 First array of objects.
241 * @param {Array<Object>} array2 Second array of objects.
242 * @param {Function=} opt_compareFn Function that acts as a custom comparator
243 * for the array ojects. Function should return true if objects are equal,
244 * otherwise false.
245 * @param {Function=} opt_collectorFn Function used to decide what to return
246 * as a result subsequence. It accepts 2 arguments: index of common element
247 * in the first array and index in the second. The default function returns
248 * element from the first array.
249 * @return {!Array<Object>} A list of objects that are common to both arrays
250 * such that there is no common subsequence with size greater than the
251 * length of the list.
252 */
253goog.math.longestCommonSubsequence = function(
254 array1, array2, opt_compareFn, opt_collectorFn) {
255
256 var compare = opt_compareFn || function(a, b) {
257 return a == b;
258 };
259
260 var collect = opt_collectorFn || function(i1, i2) {
261 return array1[i1];
262 };
263
264 var length1 = array1.length;
265 var length2 = array2.length;
266
267 var arr = [];
268 for (var i = 0; i < length1 + 1; i++) {
269 arr[i] = [];
270 arr[i][0] = 0;
271 }
272
273 for (var j = 0; j < length2 + 1; j++) {
274 arr[0][j] = 0;
275 }
276
277 for (i = 1; i <= length1; i++) {
278 for (j = 1; j <= length2; j++) {
279 if (compare(array1[i - 1], array2[j - 1])) {
280 arr[i][j] = arr[i - 1][j - 1] + 1;
281 } else {
282 arr[i][j] = Math.max(arr[i - 1][j], arr[i][j - 1]);
283 }
284 }
285 }
286
287 // Backtracking
288 var result = [];
289 var i = length1, j = length2;
290 while (i > 0 && j > 0) {
291 if (compare(array1[i - 1], array2[j - 1])) {
292 result.unshift(collect(i - 1, j - 1));
293 i--;
294 j--;
295 } else {
296 if (arr[i - 1][j] > arr[i][j - 1]) {
297 i--;
298 } else {
299 j--;
300 }
301 }
302 }
303
304 return result;
305};
306
307
308/**
309 * Returns the sum of the arguments.
310 * @param {...number} var_args Numbers to add.
311 * @return {number} The sum of the arguments (0 if no arguments were provided,
312 * {@code NaN} if any of the arguments is not a valid number).
313 */
314goog.math.sum = function(var_args) {
315 return /** @type {number} */ (goog.array.reduce(arguments,
316 function(sum, value) {
317 return sum + value;
318 }, 0));
319};
320
321
322/**
323 * Returns the arithmetic mean of the arguments.
324 * @param {...number} var_args Numbers to average.
325 * @return {number} The average of the arguments ({@code NaN} if no arguments
326 * were provided or any of the arguments is not a valid number).
327 */
328goog.math.average = function(var_args) {
329 return goog.math.sum.apply(null, arguments) / arguments.length;
330};
331
332
333/**
334 * Returns the unbiased sample variance of the arguments. For a definition,
335 * see e.g. http://en.wikipedia.org/wiki/Variance
336 * @param {...number} var_args Number samples to analyze.
337 * @return {number} The unbiased sample variance of the arguments (0 if fewer
338 * than two samples were provided, or {@code NaN} if any of the samples is
339 * not a valid number).
340 */
341goog.math.sampleVariance = function(var_args) {
342 var sampleSize = arguments.length;
343 if (sampleSize < 2) {
344 return 0;
345 }
346
347 var mean = goog.math.average.apply(null, arguments);
348 var variance = goog.math.sum.apply(null, goog.array.map(arguments,
349 function(val) {
350 return Math.pow(val - mean, 2);
351 })) / (sampleSize - 1);
352
353 return variance;
354};
355
356
357/**
358 * Returns the sample standard deviation of the arguments. For a definition of
359 * sample standard deviation, see e.g.
360 * http://en.wikipedia.org/wiki/Standard_deviation
361 * @param {...number} var_args Number samples to analyze.
362 * @return {number} The sample standard deviation of the arguments (0 if fewer
363 * than two samples were provided, or {@code NaN} if any of the samples is
364 * not a valid number).
365 */
366goog.math.standardDeviation = function(var_args) {
367 return Math.sqrt(goog.math.sampleVariance.apply(null, arguments));
368};
369
370
371/**
372 * Returns whether the supplied number represents an integer, i.e. that is has
373 * no fractional component. No range-checking is performed on the number.
374 * @param {number} num The number to test.
375 * @return {boolean} Whether {@code num} is an integer.
376 */
377goog.math.isInt = function(num) {
378 return isFinite(num) && num % 1 == 0;
379};
380
381
382/**
383 * Returns whether the supplied number is finite and not NaN.
384 * @param {number} num The number to test.
385 * @return {boolean} Whether {@code num} is a finite number.
386 */
387goog.math.isFiniteNumber = function(num) {
388 return isFinite(num) && !isNaN(num);
389};
390
391
392/**
393 * @param {number} num The number to test.
394 * @return {boolean} Whether it is negative zero.
395 */
396goog.math.isNegativeZero = function(num) {
397 return num == 0 && 1 / num < 0;
398};
399
400
401/**
402 * Returns the precise value of floor(log10(num)).
403 * Simpler implementations didn't work because of floating point rounding
404 * errors. For example
405 * <ul>
406 * <li>Math.floor(Math.log(num) / Math.LN10) is off by one for num == 1e+3.
407 * <li>Math.floor(Math.log(num) * Math.LOG10E) is off by one for num == 1e+15.
408 * <li>Math.floor(Math.log10(num)) is off by one for num == 1e+15 - 1.
409 * </ul>
410 * @param {number} num A floating point number.
411 * @return {number} Its logarithm to base 10 rounded down to the nearest
412 * integer if num > 0. -Infinity if num == 0. NaN if num < 0.
413 */
414goog.math.log10Floor = function(num) {
415 if (num > 0) {
416 var x = Math.round(Math.log(num) * Math.LOG10E);
417 return x - (parseFloat('1e' + x) > num ? 1 : 0);
418 }
419 return num == 0 ? -Infinity : NaN;
420};
421
422
423/**
424 * A tweaked variant of {@code Math.floor} which tolerates if the passed number
425 * is infinitesimally smaller than the closest integer. It often happens with
426 * the results of floating point calculations because of the finite precision
427 * of the intermediate results. For example {@code Math.floor(Math.log(1000) /
428 * Math.LN10) == 2}, not 3 as one would expect.
429 * @param {number} num A number.
430 * @param {number=} opt_epsilon An infinitesimally small positive number, the
431 * rounding error to tolerate.
432 * @return {number} The largest integer less than or equal to {@code num}.
433 */
434goog.math.safeFloor = function(num, opt_epsilon) {
435 goog.asserts.assert(!goog.isDef(opt_epsilon) || opt_epsilon > 0);
436 return Math.floor(num + (opt_epsilon || 2e-15));
437};
438
439
440/**
441 * A tweaked variant of {@code Math.ceil}. See {@code goog.math.safeFloor} for
442 * details.
443 * @param {number} num A number.
444 * @param {number=} opt_epsilon An infinitesimally small positive number, the
445 * rounding error to tolerate.
446 * @return {number} The smallest integer greater than or equal to {@code num}.
447 */
448goog.math.safeCeil = function(num, opt_epsilon) {
449 goog.asserts.assert(!goog.isDef(opt_epsilon) || opt_epsilon > 0);
450 return Math.ceil(num - (opt_epsilon || 2e-15));
451};