math.js 14 KB

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