number.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one
  3. * or more contributor license agreements. See the NOTICE file
  4. * distributed with this work for additional information
  5. * regarding copyright ownership. The ASF licenses this file
  6. * to you under the Apache License, Version 2.0 (the
  7. * "License"); you may not use this file except in compliance
  8. * with the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing,
  13. * software distributed under the License is distributed on an
  14. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15. * KIND, either express or implied. See the License for the
  16. * specific language governing permissions and limitations
  17. * under the License.
  18. */
  19. /**
  20. * AUTO-GENERATED FILE. DO NOT MODIFY.
  21. */
  22. /*
  23. * Licensed to the Apache Software Foundation (ASF) under one
  24. * or more contributor license agreements. See the NOTICE file
  25. * distributed with this work for additional information
  26. * regarding copyright ownership. The ASF licenses this file
  27. * to you under the Apache License, Version 2.0 (the
  28. * "License"); you may not use this file except in compliance
  29. * with the License. You may obtain a copy of the License at
  30. *
  31. * http://www.apache.org/licenses/LICENSE-2.0
  32. *
  33. * Unless required by applicable law or agreed to in writing,
  34. * software distributed under the License is distributed on an
  35. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  36. * KIND, either express or implied. See the License for the
  37. * specific language governing permissions and limitations
  38. * under the License.
  39. */
  40. /*
  41. * A third-party license is embedded for some of the code in this file:
  42. * The method "quantile" was copied from "d3.js".
  43. * (See more details in the comment of the method below.)
  44. * The use of the source code of this file is also subject to the terms
  45. * and consitions of the license of "d3.js" (BSD-3Clause, see
  46. * </licenses/LICENSE-d3>).
  47. */
  48. import * as zrUtil from 'zrender/lib/core/util.js';
  49. var RADIAN_EPSILON = 1e-4;
  50. // A `RangeError` may be thrown if `n` is out of this range when calling `toFixed(n)`.
  51. // Although Chrome and ES2017+ have enlarged this number to 100, but we sill follow
  52. // the ES3~ES6 spec (0 <= n <= 20) for backward and cross-platform compatibility.
  53. var TO_FIXED_SUPPORTED_PRECISION_MAX = 20;
  54. // For rounding error like `2.9999999999999996`, with respect to IEEE754 64bit float.
  55. // NOTICE: It only works when the expected result is a rational number with low
  56. // precision. See method `round` for details.
  57. export var DEFAULT_PRECISION_FOR_ROUNDING_ERROR = 14;
  58. function _trim(str) {
  59. return str.replace(/^\s+|\s+$/g, '');
  60. }
  61. export var mathMin = Math.min;
  62. export var mathMax = Math.max;
  63. export var mathAbs = Math.abs;
  64. export var mathRound = Math.round;
  65. export var mathFloor = Math.floor;
  66. export var mathCeil = Math.ceil;
  67. export var mathPow = Math.pow;
  68. export var mathLog = Math.log;
  69. export var mathLN10 = Math.LN10;
  70. export var mathPI = Math.PI;
  71. export var mathRandom = Math.random;
  72. /**
  73. * Linear mapping a value from domain to range
  74. * @param val
  75. * @param domain Domain extent domain[0] can be bigger than domain[1]
  76. * @param range Range extent range[0] can be bigger than range[1]
  77. * @param clamp Default to be false
  78. */
  79. export function linearMap(val, domain, range, clamp) {
  80. var d0 = domain[0];
  81. var d1 = domain[1];
  82. var r0 = range[0];
  83. var r1 = range[1];
  84. var subDomain = d1 - d0;
  85. var subRange = r1 - r0;
  86. if (subDomain === 0) {
  87. return subRange === 0 ? r0 : (r0 + r1) / 2;
  88. }
  89. // Avoid accuracy problem in edge, such as
  90. // 146.39 - 62.83 === 83.55999999999999.
  91. // See echarts/test/ut/spec/util/number.js#linearMap#accuracyError
  92. // It is a little verbose for efficiency considering this method
  93. // is a hotspot.
  94. if (clamp) {
  95. if (subDomain > 0) {
  96. if (val <= d0) {
  97. return r0;
  98. } else if (val >= d1) {
  99. return r1;
  100. }
  101. } else {
  102. if (val >= d0) {
  103. return r0;
  104. } else if (val <= d1) {
  105. return r1;
  106. }
  107. }
  108. } else {
  109. if (val === d0) {
  110. return r0;
  111. }
  112. if (val === d1) {
  113. return r1;
  114. }
  115. }
  116. return (val - d0) / subDomain * subRange + r0;
  117. }
  118. /**
  119. * Preserve the name `parsePercent` for backward compatibility,
  120. * and it's effectively published as `echarts.number.parsePercent`.
  121. */
  122. export var parsePercent = parsePositionOption;
  123. /**
  124. * @see {parsePositionSizeOption} and also accept a string preset.
  125. * @see {PositionSizeOption}
  126. */
  127. export function parsePositionOption(option, percentBase, percentOffset) {
  128. switch (option) {
  129. case 'center':
  130. case 'middle':
  131. option = '50%';
  132. break;
  133. case 'left':
  134. case 'top':
  135. option = '0%';
  136. break;
  137. case 'right':
  138. case 'bottom':
  139. option = '100%';
  140. break;
  141. }
  142. return parsePositionSizeOption(option, percentBase, percentOffset);
  143. }
  144. /**
  145. * Accept number, or numeric string (`'123'`), or percentage ('100%'), as x/y/width/height pixel number.
  146. * If null/undefined or invalid, return NaN.
  147. * (But allow JS type coercion (`+option`) due to backward compatibility)
  148. * @see {PositionSizeOption}
  149. */
  150. export function parsePositionSizeOption(option, percentBase,
  151. // Typical usage of `percentOffset`: percent value is based on an specific rect rather than canvas viewport:
  152. // `parsePercent(percentOrAbsoluteLeft, rect.width, rect.x)`
  153. percentOffset) {
  154. if (zrUtil.isString(option)) {
  155. if (isOptionStringPercent(option)) {
  156. return parseFloat(option) / 100 * percentBase + (percentOffset || 0);
  157. }
  158. return parseFloat(option);
  159. }
  160. // Allow flexible input due to backward compatibility.
  161. return option == null ? NaN : +option;
  162. }
  163. /**
  164. * Perserve the same rule with `parsePositionSizeOption`.
  165. */
  166. export function isPositionSizeOptionPercent(option) {
  167. return zrUtil.isString(option) && isOptionStringPercent(option);
  168. }
  169. function isOptionStringPercent(option) {
  170. return !!_trim(option).match(/%$/);
  171. }
  172. export function round(x, precision, returnStr) {
  173. if (process.env.NODE_ENV !== 'production') {
  174. // NOTICE: We should not provided a default precision, since there is no universally adaptable
  175. // precision. The caller need to input a precision according to the scenarios.
  176. zrUtil.assert(precision != null);
  177. }
  178. if (isNaN(precision)) {
  179. // precision utils (such as getAcceptableTickPrecision) may return NaN.
  180. return returnStr ? '' + x : +x;
  181. }
  182. // Avoid range error
  183. precision = mathMin(mathMax(0, precision), TO_FIXED_SUPPORTED_PRECISION_MAX);
  184. // PENDING: 1.005.toFixed(2) is '1.00' rather than '1.01'
  185. x = (+x).toFixed(precision);
  186. return returnStr ? x : +x;
  187. }
  188. export function roundLegacy(x, precision, returnStr) {
  189. if (precision == null) {
  190. precision = 10;
  191. }
  192. return round(x, precision, returnStr);
  193. }
  194. /**
  195. * Inplacd asc sort arr.
  196. * The input arr will be modified.
  197. */
  198. export function asc(arr) {
  199. arr.sort(function (a, b) {
  200. return a - b;
  201. });
  202. return arr;
  203. }
  204. /**
  205. * Get precision.
  206. * e.g. `getPrecisionSafe(100.123)` return `3`.
  207. * e.g. `getPrecisionSafe(100)` return `0`.
  208. */
  209. export function getPrecision(val) {
  210. val = +val;
  211. if (isNaN(val)) {
  212. return 0;
  213. }
  214. // It is much faster than methods converting number to string as follows
  215. // let tmp = val.toString();
  216. // return tmp.length - 1 - tmp.indexOf('.');
  217. // especially when precision is low
  218. // Notice:
  219. // (1) If the loop count is over about 20, it is slower than `getPrecisionSafe`.
  220. // (see https://jsbench.me/2vkpcekkvw/1)
  221. // (2) If the val is less than for example 1e-15, the result may be incorrect.
  222. // (see test/ut/spec/util/number.test.ts `getPrecision_equal_random`)
  223. if (val > 1e-14) {
  224. var e = 1;
  225. for (var i = 0; i < 15; i++, e *= 10) {
  226. if (mathRound(val * e) / e === val) {
  227. return i;
  228. }
  229. }
  230. }
  231. return getPrecisionSafe(val);
  232. }
  233. /**
  234. * Get precision with slow but safe method
  235. * e.g. `getPrecisionSafe(100.123)` return `3`.
  236. * e.g. `getPrecisionSafe(100)` return `0`.
  237. */
  238. export function getPrecisionSafe(val) {
  239. // toLowerCase for: '3.4E-12'
  240. var str = val.toString().toLowerCase();
  241. // Consider scientific notation: '3.4e-12' '3.4e+12'
  242. var eIndex = str.indexOf('e');
  243. var exp = eIndex > 0 ? +str.slice(eIndex + 1) : 0;
  244. var significandPartLen = eIndex > 0 ? eIndex : str.length;
  245. var dotIndex = str.indexOf('.');
  246. var decimalPartLen = dotIndex < 0 ? 0 : significandPartLen - 1 - dotIndex;
  247. return mathMax(0, decimalPartLen - exp);
  248. }
  249. /**
  250. * @deprecated Use `getAcceptableTickPrecision` instead. See bad case in `test/ut/spec/util/number.test.ts`
  251. * NOTE: originally introduced in commit `ff93e3e7f9ff24902e10d4469fd3187393b05feb`
  252. *
  253. * Minimal discernible data precision according to a single pixel.
  254. */
  255. export function getPixelPrecision(dataExtent, pixelExtent) {
  256. var dataQuantity = mathFloor(mathLog(dataExtent[1] - dataExtent[0]) / mathLN10);
  257. var sizeQuantity = mathRound(mathLog(mathAbs(pixelExtent[1] - pixelExtent[0])) / mathLN10);
  258. // toFixed() digits argument must be between 0 and 20.
  259. var precision = mathMin(mathMax(-dataQuantity + sizeQuantity, 0), TO_FIXED_SUPPORTED_PRECISION_MAX);
  260. return !isFinite(precision) ? TO_FIXED_SUPPORTED_PRECISION_MAX : precision;
  261. }
  262. /**
  263. * This method chooses a reasonable "data" precision that can be used in `round` method.
  264. * A reasonable precision is suitable for display; it may cause cumulative error but acceptable.
  265. *
  266. * "data" is linearly mapped to pixel according to the ratio determined by `dataSpan` and `pxSpan`.
  267. * The diff from the original "data" to the rounded "data" (with the result precision) should be
  268. * equal or less than `pxDiffAcceptable`, which is typically `1` pixel.
  269. * And the result precision should be as small as possible for a concise display.
  270. *
  271. * [NOTICE]: using arbitrary parameters is NOT preferable - a discernible misalign (e.g., over 1px)
  272. * may occur, especially when `splitLine` is displayed.
  273. *
  274. * PENDING: Only the linear case is addressed for now; other mapping methods (like logarithm) will
  275. * not be covered until necessary.
  276. */
  277. export function getAcceptableTickPrecision(dataExtent,
  278. // Typically, `Math.abs(pixelExtent[1] - pixelExtent[0])`.
  279. pxSpan,
  280. // By default, `1`.
  281. pxDiffAcceptable
  282. // Return a precision >= 0
  283. // This precision can be used in method `round`.
  284. // Return `NaN` for edge case or illegal inputs. Callers need to handle that.
  285. ) {
  286. var dataSpan = mathAbs(dataExtent[1] - dataExtent[0]);
  287. if (!isFinite(dataSpan) || dataSpan === 0) {
  288. return NaN;
  289. }
  290. // Formula for choosing an acceptable precision:
  291. // Let `pxDiff = abs(dataSpan - round(dataSpan, precision))`.
  292. // We require `pxDiff <= dataSpan * pxDiffAcceptable / pxSpan`.
  293. // Consider the nature of "round", the max `pxDiff` is: `pow(10, -precision) / 2`,
  294. // Hence: `pow(10, -precision) / 2 <= dataSpan * pxDiffAcceptable / pxSpan`
  295. // Hence: `precision >= -log10(2 * dataSpan * pxDiffAcceptable / pxSpan)`
  296. var dataExp2 = mathLog(2 * mathAbs(pxDiffAcceptable || 1) * mathAbs(dataSpan)) / mathLN10;
  297. var pxExp = mathLog(mathAbs(pxSpan)) / mathLN10;
  298. // PENDING: Rounding error generally does not matter; do not fix it before `Math.ceil`
  299. // until bad case occur.
  300. var precision = mathMax(0, mathCeil(-dataExp2 + pxExp));
  301. if (!isFinite(precision)) {
  302. // If dataSpan is near `0`, the result should not be too big or even `Infinity`.
  303. precision = NaN;
  304. }
  305. return precision;
  306. }
  307. /**
  308. * Get a data of given precision, assuring the sum of percentages
  309. * in valueList is 1.
  310. * The largest remainder method is used.
  311. * https://en.wikipedia.org/wiki/Largest_remainder_method
  312. *
  313. * @param valueList a list of all data
  314. * @param idx index of the data to be processed in valueList
  315. * @param precision integer number showing digits of precision
  316. * @return percent ranging from 0 to 100
  317. */
  318. export function getPercentWithPrecision(valueList, idx, precision) {
  319. if (!valueList[idx]) {
  320. return 0;
  321. }
  322. var seats = getPercentSeats(valueList, precision);
  323. return seats[idx] || 0;
  324. }
  325. /**
  326. * Get a data of given precision, assuring the sum of percentages
  327. * in valueList is 1.
  328. * The largest remainder method is used.
  329. * https://en.wikipedia.org/wiki/Largest_remainder_method
  330. *
  331. * @param valueList a list of all data
  332. * @param precision integer number showing digits of precision
  333. * @return {Array<number>}
  334. */
  335. export function getPercentSeats(valueList, precision) {
  336. var sum = zrUtil.reduce(valueList, function (acc, val) {
  337. return acc + (isNaN(val) ? 0 : val);
  338. }, 0);
  339. if (sum === 0) {
  340. return [];
  341. }
  342. var digits = mathPow(10, precision);
  343. var votesPerQuota = zrUtil.map(valueList, function (val) {
  344. return (isNaN(val) ? 0 : val) / sum * digits * 100;
  345. });
  346. var targetSeats = digits * 100;
  347. var seats = zrUtil.map(votesPerQuota, function (votes) {
  348. // Assign automatic seats.
  349. return mathFloor(votes);
  350. });
  351. var currentSum = zrUtil.reduce(seats, function (acc, val) {
  352. return acc + val;
  353. }, 0);
  354. var remainder = zrUtil.map(votesPerQuota, function (votes, idx) {
  355. return votes - seats[idx];
  356. });
  357. // Has remainding votes.
  358. while (currentSum < targetSeats) {
  359. // Find next largest remainder.
  360. var max = Number.NEGATIVE_INFINITY;
  361. var maxId = null;
  362. for (var i = 0, len = remainder.length; i < len; ++i) {
  363. if (remainder[i] > max) {
  364. max = remainder[i];
  365. maxId = i;
  366. }
  367. }
  368. // Add a vote to max remainder.
  369. ++seats[maxId];
  370. remainder[maxId] = 0;
  371. ++currentSum;
  372. }
  373. return zrUtil.map(seats, function (seat) {
  374. return seat / digits;
  375. });
  376. }
  377. /**
  378. * Solve the floating point adding problem like 0.1 + 0.2 === 0.30000000000000004
  379. * See <http://0.30000000000000004.com/>
  380. */
  381. export function addSafe(val0, val1) {
  382. var maxPrecision = mathMax(getPrecision(val0), getPrecision(val1));
  383. // const multiplier = Math.pow(10, maxPrecision);
  384. // return (mathRound(val0 * multiplier) + mathRound(val1 * multiplier)) / multiplier;
  385. var sum = val0 + val1;
  386. // // PENDING: support more?
  387. return maxPrecision > TO_FIXED_SUPPORTED_PRECISION_MAX ? sum : round(sum, maxPrecision);
  388. }
  389. // Number.MAX_SAFE_INTEGER, ie do not support.
  390. export var MAX_SAFE_INTEGER = mathPow(2, 53) - 1;
  391. /**
  392. * To 0 - 2 * PI, considering negative radian.
  393. */
  394. export function remRadian(radian) {
  395. var pi2 = mathPI * 2;
  396. return (radian % pi2 + pi2) % pi2;
  397. }
  398. /**
  399. * @param {type} radian
  400. * @return {boolean}
  401. */
  402. export function isRadianAroundZero(val) {
  403. return val > -RADIAN_EPSILON && val < RADIAN_EPSILON;
  404. }
  405. // eslint-disable-next-line
  406. var TIME_REG = /^(?:(\d{4})(?:[-\/](\d{1,2})(?:[-\/](\d{1,2})(?:[T ](\d{1,2})(?::(\d{1,2})(?::(\d{1,2})(?:[.,](\d+))?)?)?(Z|[\+\-]\d\d:?\d\d)?)?)?)?)?$/; // jshint ignore:line
  407. /**
  408. * @param value valid type: number | string | Date, otherwise return `new Date(NaN)`
  409. * These values can be accepted:
  410. * + An instance of Date, represent a time in its own time zone.
  411. * + Or string in a subset of ISO 8601, only including:
  412. * + only year, month, date: '2012-03', '2012-03-01', '2012-03-01 05', '2012-03-01 05:06',
  413. * + separated with T or space: '2012-03-01T12:22:33.123', '2012-03-01 12:22:33.123',
  414. * + time zone: '2012-03-01T12:22:33Z', '2012-03-01T12:22:33+8000', '2012-03-01T12:22:33-05:00',
  415. * all of which will be treated as local time if time zone is not specified
  416. * (see <https://momentjs.com/>).
  417. * + Or other string format, including (all of which will be treated as local time):
  418. * '2012', '2012-3-1', '2012/3/1', '2012/03/01',
  419. * '2009/6/12 2:00', '2009/6/12 2:05:08', '2009/6/12 2:05:08.123'
  420. * + a timestamp, which represent a time in UTC.
  421. * @return date Never be null/undefined. If invalid, return `new Date(NaN)`.
  422. */
  423. export function parseDate(value) {
  424. if (value instanceof Date) {
  425. return value;
  426. } else if (zrUtil.isString(value)) {
  427. // Different browsers parse date in different way, so we parse it manually.
  428. // Some other issues:
  429. // new Date('1970-01-01') is UTC,
  430. // new Date('1970/01/01') and new Date('1970-1-01') is local.
  431. // See issue #3623
  432. var match = TIME_REG.exec(value);
  433. if (!match) {
  434. // return Invalid Date.
  435. return new Date(NaN);
  436. }
  437. // Use local time when no timezone offset is specified.
  438. if (!match[8]) {
  439. // match[n] can only be string or undefined.
  440. // But take care of '12' + 1 => '121'.
  441. return new Date(+match[1], +(match[2] || 1) - 1, +match[3] || 1, +match[4] || 0, +(match[5] || 0), +match[6] || 0, match[7] ? +match[7].substring(0, 3) : 0);
  442. }
  443. // Timezoneoffset of Javascript Date has considered DST (Daylight Saving Time,
  444. // https://tc39.github.io/ecma262/#sec-daylight-saving-time-adjustment).
  445. // For example, system timezone is set as "Time Zone: America/Toronto",
  446. // then these code will get different result:
  447. // `new Date(1478411999999).getTimezoneOffset(); // get 240`
  448. // `new Date(1478412000000).getTimezoneOffset(); // get 300`
  449. // So we should not use `new Date`, but use `Date.UTC`.
  450. else {
  451. var hour = +match[4] || 0;
  452. if (match[8].toUpperCase() !== 'Z') {
  453. hour -= +match[8].slice(0, 3);
  454. }
  455. return new Date(Date.UTC(+match[1], +(match[2] || 1) - 1, +match[3] || 1, hour, +(match[5] || 0), +match[6] || 0, match[7] ? +match[7].substring(0, 3) : 0));
  456. }
  457. } else if (value == null) {
  458. return new Date(NaN);
  459. }
  460. return new Date(mathRound(value));
  461. }
  462. /**
  463. * Quantity of a number. e.g. 0.1, 1, 10, 100
  464. *
  465. * @param val
  466. * @return
  467. */
  468. export function quantity(val) {
  469. return mathPow(10, quantityExponent(val));
  470. }
  471. /**
  472. * Exponent of the quantity of a number
  473. * e.g., 9876 equals to 9.876*10^3, so quantityExponent(9876) is 3
  474. * e.g., 0.09876 equals to 9.876*10^-2, so quantityExponent(0.09876) is -2
  475. *
  476. * @param val non-negative value
  477. * @return
  478. */
  479. export function quantityExponent(val) {
  480. if (val === 0) {
  481. // PENDING: like IEEE754 use exponent `0` in this case.
  482. // but methematically, exponent of zero is `-Infinity`.
  483. return 0;
  484. }
  485. var exp = mathFloor(mathLog(val) / mathLN10);
  486. /**
  487. * exp is expected to be the rounded-down result of the base-10 log of val.
  488. * But due to the precision loss with Math.log(val), we need to restore it
  489. * using 10^exp to make sure we can get val back from exp. #11249
  490. */
  491. if (val / mathPow(10, exp) >= 10) {
  492. exp++;
  493. }
  494. return exp;
  495. }
  496. export var NICE_MODE_ROUND = 1;
  497. export var NICE_MODE_MIN = 2;
  498. /**
  499. * find a “nice” number approximately equal to x. Round the number if 'round',
  500. * take ceiling if 'round'. The primary observation is that the “nicest”
  501. * numbers in decimal are 1, 2, and 5, and all power-of-ten multiples of these numbers.
  502. *
  503. * See "Nice Numbers for Graph Labels" of Graphic Gems.
  504. *
  505. * @param val Non-negative value.
  506. * @return Niced number
  507. */
  508. export function nice(val,
  509. // All non-`NICE_MODE_MIN`-truthy values means `NICE_MODE_ROUND`, for backward compatibility.
  510. mode) {
  511. // Consider the scientific notation of `val`:
  512. // - `exponent` is its exponent.
  513. // - `f` is its coefficient. `1 <= f < 10`.
  514. // e.g., if `val` is `0.0054321`, `exponent` is `-3`, `f` is `5.4321`,
  515. // The result is `0.005` on NICE_MODE_ROUND.
  516. // e.g., if `val` is `987.12345`, `exponent` is `2`, `f` is `9.8712345`,
  517. // The result is `1000` on NICE_MODE_ROUND.
  518. // e.g., if `val` is `0`,
  519. // The result is `1`.
  520. var exponent = quantityExponent(val);
  521. // No rounding error in Math.pow(10, integer).
  522. var exp10 = mathPow(10, exponent);
  523. var f = val / exp10;
  524. var nf;
  525. if (mode === NICE_MODE_MIN) {
  526. nf = 1;
  527. } else if (mode) {
  528. if (f < 1.5) {
  529. nf = 1;
  530. } else if (f < 2.5) {
  531. nf = 2;
  532. } else if (f < 4) {
  533. nf = 3;
  534. } else if (f < 7) {
  535. nf = 5;
  536. } else {
  537. nf = 10;
  538. }
  539. } else {
  540. if (f < 1) {
  541. nf = 1;
  542. } else if (f < 2) {
  543. nf = 2;
  544. } else if (f < 3) {
  545. nf = 3;
  546. } else if (f < 5) {
  547. nf = 5;
  548. } else {
  549. nf = 10;
  550. }
  551. }
  552. val = nf * exp10;
  553. // Fix IEEE 754 float rounding error
  554. return round(val, -exponent);
  555. }
  556. /**
  557. * This code was copied from "d3.js"
  558. * <https://github.com/d3/d3/blob/9cc9a875e636a1dcf36cc1e07bdf77e1ad6e2c74/src/arrays/quantile.js>.
  559. * See the license statement at the head of this file.
  560. * @param ascArr
  561. */
  562. export function quantile(ascArr, p) {
  563. var H = (ascArr.length - 1) * p + 1;
  564. var h = mathFloor(H);
  565. var v = +ascArr[h - 1];
  566. var e = H - h;
  567. return e ? v + e * (ascArr[h] - v) : v;
  568. }
  569. /**
  570. * Order intervals asc, and split them when overlap.
  571. * expect(numberUtil.reformIntervals([
  572. * {interval: [18, 62], close: [1, 1]},
  573. * {interval: [-Infinity, -70], close: [0, 0]},
  574. * {interval: [-70, -26], close: [1, 1]},
  575. * {interval: [-26, 18], close: [1, 1]},
  576. * {interval: [62, 150], close: [1, 1]},
  577. * {interval: [106, 150], close: [1, 1]},
  578. * {interval: [150, Infinity], close: [0, 0]}
  579. * ])).toEqual([
  580. * {interval: [-Infinity, -70], close: [0, 0]},
  581. * {interval: [-70, -26], close: [1, 1]},
  582. * {interval: [-26, 18], close: [0, 1]},
  583. * {interval: [18, 62], close: [0, 1]},
  584. * {interval: [62, 150], close: [0, 1]},
  585. * {interval: [150, Infinity], close: [0, 0]}
  586. * ]);
  587. * @param list, where `close` mean open or close
  588. * of the interval, and Infinity can be used.
  589. * @return The origin list, which has been reformed.
  590. */
  591. export function reformIntervals(list) {
  592. list.sort(function (a, b) {
  593. return littleThan(a, b, 0) ? -1 : 1;
  594. });
  595. var curr = -Infinity;
  596. var currClose = 1;
  597. for (var i = 0; i < list.length;) {
  598. var interval = list[i].interval;
  599. var close_1 = list[i].close;
  600. for (var lg = 0; lg < 2; lg++) {
  601. if (interval[lg] <= curr) {
  602. interval[lg] = curr;
  603. close_1[lg] = !lg ? 1 - currClose : 1;
  604. }
  605. curr = interval[lg];
  606. currClose = close_1[lg];
  607. }
  608. if (interval[0] === interval[1] && close_1[0] * close_1[1] !== 1) {
  609. list.splice(i, 1);
  610. } else {
  611. i++;
  612. }
  613. }
  614. return list;
  615. function littleThan(a, b, lg) {
  616. return a.interval[lg] < b.interval[lg] || a.interval[lg] === b.interval[lg] && (a.close[lg] - b.close[lg] === (!lg ? 1 : -1) || !lg && littleThan(a, b, 1));
  617. }
  618. }
  619. /**
  620. * [Numeric is defined as]:
  621. * `parseFloat(val) == val`
  622. * For example:
  623. * numeric:
  624. * typeof number except NaN, '-123', '123', '2e3', '-2e3', '011', 'Infinity', Infinity,
  625. * and they rounded by white-spaces or line-terminal like ' -123 \n ' (see es spec)
  626. * not-numeric:
  627. * null, undefined, [], {}, true, false, 'NaN', NaN, '123ab',
  628. * empty string, string with only white-spaces or line-terminal (see es spec),
  629. * 0x12, '0x12', '-0x12', 012, '012', '-012',
  630. * non-string, ...
  631. *
  632. * @test See full test cases in `test/ut/spec/util/number.js`.
  633. * @return Must be a typeof number. If not numeric, return NaN.
  634. */
  635. export function numericToNumber(val) {
  636. var valFloat = parseFloat(val);
  637. return valFloat == val // eslint-disable-line eqeqeq
  638. && (valFloat !== 0 || !zrUtil.isString(val) || val.indexOf('x') <= 0) // For case ' 0x0 '.
  639. ? valFloat : NaN;
  640. }
  641. /**
  642. * Definition of "numeric": see `numericToNumber`.
  643. */
  644. export function isNumeric(val) {
  645. return !isNaN(numericToNumber(val));
  646. }
  647. /**
  648. * Use random base to prevent users hard code depending on
  649. * this auto generated marker id.
  650. * @return An positive integer.
  651. */
  652. export function getRandomIdBase() {
  653. return mathRound(mathRandom() * 9);
  654. }
  655. /**
  656. * Get the greatest common divisor.
  657. *
  658. * @param {number} a one number
  659. * @param {number} b the other number
  660. */
  661. export function getGreatestCommonDividor(a, b) {
  662. if (b === 0) {
  663. return a;
  664. }
  665. return getGreatestCommonDividor(b, a % b);
  666. }
  667. /**
  668. * Get the least common multiple.
  669. *
  670. * @param {number} a one number
  671. * @param {number} b the other number
  672. */
  673. export function getLeastCommonMultiple(a, b) {
  674. if (a == null) {
  675. return b;
  676. }
  677. if (b == null) {
  678. return a;
  679. }
  680. return a * b / getGreatestCommonDividor(a, b);
  681. }
  682. /**
  683. * NOTICE: Assume the input `val` is number or null/undefined, no type check, no support of BitInt.
  684. * Therefore, it is NOT suitable for processing user input, but sufficient for
  685. * internal usage in most cases.
  686. * For platform-agnosticism, `Number.isFinite` is not used.
  687. */
  688. export function isNullableNumberFinite(val) {
  689. return val != null && isFinite(val);
  690. }