# 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 at6//7//      http://www.apache.org/licenses/LICENSE-2.08//9// Unless required by applicable law or agreed to in writing, software10// 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 and13// limitations under the License.1415/**16 * @fileoverview Additional mathematical functions.17 */1819goog.provide('goog.math');2021goog.require('goog.array');22goog.require('goog.asserts');232425/**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};333435/**36 * Returns a random number greater than or equal to {@code a} and less than37 * {@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};454647/**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 nearest53 *     number within the bounds.54 */55goog.math.clamp = function(value, min, max) {56  return Math.min(Math.max(value, min), max);57};585960/**61 * The % operator in JavaScript returns the remainder of a / b, but differs from62 * some other languages in that the result will have the same sign as the63 * dividend. For example, -1 % 8 == -1, whereas in some other languages64 * (such as Python) the result would be 7. This function emulates the more65 * correct modulo behavior, which is useful for certain applications such as66 * 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 < b71 *     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};787980/**81 * Performs linear interpolation between values a and b. Returns the value82 * between a and b proportional to x (when x is between 0 and 1. When x is83 * 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};929394/**95 * Tests whether the two values are equal to each other, within a certain96 * 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. Defaults100 *     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};106107108// TODO(user): Rename to normalizeAngle, retaining old name as deprecated109// alias.110/**111 * Normalizes an angle to be in range [0-360). Angles outside this range will112 * 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};119120121/**122 * Normalizes an angle to be in range [0-2*PI). Angles outside this range will123 * 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};130131132/**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};140141142/**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};150151152/**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};161162163/**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};172173174/**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 +Y177 * 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 from183 *     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};189190191/**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 to196 *     startAngle will result in endAngle. Positive numbers mean that the197 *     direction is clockwise. Negative numbers indicate a counter-clockwise198 *     direction.199 *     The shortest route (clockwise vs counter-clockwise) between the angles200 *     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};215216217/**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.221 */222goog.math.sign = function(x) {223  return x == 0 ? 0 : (x < 0 ? -1 : 1);224};225226227/**228 * JavaScript implementation of Longest Common Subsequence problem.229 * http://en.wikipedia.org/wiki/Longest_common_subsequence230 *231 * Returns the longest possible array that is subarray of both of given arrays.232 *233 * @param {Array.<Object>} array1 First array of objects.234 * @param {Array.<Object>} array2 Second array of objects.235 * @param {Function=} opt_compareFn Function that acts as a custom comparator236 *     for the array ojects. Function should return true if objects are equal,237 *     otherwise false.238 * @param {Function=} opt_collectorFn Function used to decide what to return239 *     as a result subsequence. It accepts 2 arguments: index of common element240 *     in the first array and index in the second. The default function returns241 *     element from the first array.242 * @return {!Array.<Object>} A list of objects that are common to both arrays243 *     such that there is no common subsequence with size greater than the244 *     length of the list.245 */246goog.math.longestCommonSubsequence = function(247    array1, array2, opt_compareFn, opt_collectorFn) {248249  var compare = opt_compareFn || function(a, b) {250    return a == b;251  };252253  var collect = opt_collectorFn || function(i1, i2) {254    return array1[i1];255  };256257  var length1 = array1.length;258  var length2 = array2.length;259260  var arr = [];261  for (var i = 0; i < length1 + 1; i++) {262    arr[i] = [];263    arr[i][0] = 0;264  }265266  for (var j = 0; j < length2 + 1; j++) {267    arr[0][j] = 0;268  }269270  for (i = 1; i <= length1; i++) {271    for (j = 1; j <= length2; j++) {272      if (compare(array1[i - 1], array2[j - 1])) {273        arr[i][j] = arr[i - 1][j - 1] + 1;274      } else {275        arr[i][j] = Math.max(arr[i - 1][j], arr[i][j - 1]);276      }277    }278  }279280  // Backtracking281  var result = [];282  var i = length1, j = length2;283  while (i > 0 && j > 0) {284    if (compare(array1[i - 1], array2[j - 1])) {285      result.unshift(collect(i - 1, j - 1));286      i--;287      j--;288    } else {289      if (arr[i - 1][j] > arr[i][j - 1]) {290        i--;291      } else {292        j--;293      }294    }295  }296297  return result;298};299300301/**302 * Returns the sum of the arguments.303 * @param {...number} var_args Numbers to add.304 * @return {number} The sum of the arguments (0 if no arguments were provided,305 *     {@code NaN} if any of the arguments is not a valid number).306 */307goog.math.sum = function(var_args) {308  return /** @type {number} */ (goog.array.reduce(arguments,309      function(sum, value) {310        return sum + value;311      }, 0));312};313314315/**316 * Returns the arithmetic mean of the arguments.317 * @param {...number} var_args Numbers to average.318 * @return {number} The average of the arguments ({@code NaN} if no arguments319 *     were provided or any of the arguments is not a valid number).320 */321goog.math.average = function(var_args) {322  return goog.math.sum.apply(null, arguments) / arguments.length;323};324325326/**327 * Returns the unbiased sample variance of the arguments. For a definition,328 * see e.g. http://en.wikipedia.org/wiki/Variance329 * @param {...number} var_args Number samples to analyze.330 * @return {number} The unbiased sample variance of the arguments (0 if fewer331 *     than two samples were provided, or {@code NaN} if any of the samples is332 *     not a valid number).333 */334goog.math.sampleVariance = function(var_args) {335  var sampleSize = arguments.length;336  if (sampleSize < 2) {337    return 0;338  }339340  var mean = goog.math.average.apply(null, arguments);341  var variance = goog.math.sum.apply(null, goog.array.map(arguments,342      function(val) {343        return Math.pow(val - mean, 2);344      })) / (sampleSize - 1);345346  return variance;347};348349350/**351 * Returns the sample standard deviation of the arguments.  For a definition of352 * sample standard deviation, see e.g.353 * http://en.wikipedia.org/wiki/Standard_deviation354 * @param {...number} var_args Number samples to analyze.355 * @return {number} The sample standard deviation of the arguments (0 if fewer356 *     than two samples were provided, or {@code NaN} if any of the samples is357 *     not a valid number).358 */359goog.math.standardDeviation = function(var_args) {360  return Math.sqrt(goog.math.sampleVariance.apply(null, arguments));361};362363364/**365 * Returns whether the supplied number represents an integer, i.e. that is has366 * no fractional component.  No range-checking is performed on the number.367 * @param {number} num The number to test.368 * @return {boolean} Whether {@code num} is an integer.369 */370goog.math.isInt = function(num) {371  return isFinite(num) && num % 1 == 0;372};373374375/**376 * Returns whether the supplied number is finite and not NaN.377 * @param {number} num The number to test.378 * @return {boolean} Whether {@code num} is a finite number.379 */380goog.math.isFiniteNumber = function(num) {381  return isFinite(num) && !isNaN(num);382};383384385/**386 * Returns the precise value of floor(log10(num)).387 * Simpler implementations didn't work because of floating point rounding388 * errors. For example389 * <ul>390 * <li>Math.floor(Math.log(num) / Math.LN10) is off by one for num == 1e+3.391 * <li>Math.floor(Math.log(num) * Math.LOG10E) is off by one for num == 1e+15.392 * <li>Math.floor(Math.log10(num)) is off by one for num == 1e+15 - 1.393 * </ul>394 * @param {number} num A floating point number.395 * @return {number} Its logarithm to base 10 rounded down to the nearest396 *     integer if num > 0. -Infinity if num == 0. NaN if num < 0.397 */398goog.math.log10Floor = function(num) {399  if (num > 0) {400    var x = Math.round(Math.log(num) * Math.LOG10E);401    return x - (parseFloat('1e' + x) > num);402  }403  return num == 0 ? -Infinity : NaN;404};405406407/**408 * A tweaked variant of {@code Math.floor} which tolerates if the passed number409 * is infinitesimally smaller than the closest integer. It often happens with410 * the results of floating point calculations because of the finite precision411 * of the intermediate results. For example {@code Math.floor(Math.log(1000) /412 * Math.LN10) == 2}, not 3 as one would expect.413 * @param {number} num A number.414 * @param {number=} opt_epsilon An infinitesimally small positive number, the415 *     rounding error to tolerate.416 * @return {number} The largest integer less than or equal to {@code num}.417 */418goog.math.safeFloor = function(num, opt_epsilon) {419  goog.asserts.assert(!goog.isDef(opt_epsilon) || opt_epsilon > 0);420  return Math.floor(num + (opt_epsilon || 2e-15));421};422423424/**425 * A tweaked variant of {@code Math.ceil}. See {@code goog.math.safeFloor} for426 * details.427 * @param {number} num A number.428 * @param {number=} opt_epsilon An infinitesimally small positive number, the429 *     rounding error to tolerate.430 * @return {number} The smallest integer greater than or equal to {@code num}.431 */432goog.math.safeCeil = function(num, opt_epsilon) {433  goog.asserts.assert(!goog.isDef(opt_epsilon) || opt_epsilon > 0);434  return Math.ceil(num - (opt_epsilon || 2e-15));435};