1 /*
  2  * MathUtils.js - Misc math utility routines
  3  *
  4  * Copyright © 2013-2015, 2018 JEDLSoft
  5  *
  6  * Licensed under the Apache License, Version 2.0 (the "License");
  7  * you may not use this file except in compliance with the License.
  8  * 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, software
 13  * distributed under the License is distributed on an "AS IS" BASIS,
 14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 15  *
 16  * See the License for the specific language governing permissions and
 17  * limitations under the License.
 18  */
 19 
 20 var MathUtils = {};
 21 
 22 /**
 23  * Return the sign of the given number. If the sign is negative, this function
 24  * returns -1. If the sign is positive or zero, this function returns 1.
 25  * @static
 26  * @param {number} num the number to test
 27  * @return {number} -1 if the number is negative, and 1 otherwise
 28  */
 29 MathUtils.signum = function (num) {
 30     var n = num;
 31     if (typeof(num) === 'string') {
 32         n = parseInt(num, 10);
 33     } else if (typeof(num) !== 'number') {
 34         return 1;
 35     }
 36     return (n < 0) ? -1 : 1;
 37 };
 38 
 39 /**
 40  * @static
 41  * @protected
 42  * @param {number} num number to round
 43  * @return {number} rounded number
 44  */
 45 MathUtils.floor = function (num) {
 46     return Math.floor(num);
 47 };
 48 
 49 /**
 50  * @static
 51  * @protected
 52  * @param {number} num number to round
 53  * @return {number} rounded number
 54  */
 55 MathUtils.ceiling = function (num) {
 56     return Math.ceil(num);
 57 };
 58 
 59 /**
 60  * @static
 61  * @protected
 62  * @param {number} num number to round
 63  * @return {number} rounded number
 64  */
 65 MathUtils.down = function (num) {
 66     return (num < 0) ? Math.ceil(num) : Math.floor(num);
 67 };
 68 
 69 /**
 70  * @static
 71  * @protected
 72  * @param {number} num number to round
 73  * @return {number} rounded number
 74  */
 75 MathUtils.up = function (num) {
 76     return (num < 0) ? Math.floor(num) : Math.ceil(num);
 77 };
 78 
 79 /**
 80  * @static
 81  * @protected
 82  * @param {number} num number to round
 83  * @return {number} rounded number
 84  */
 85 MathUtils.halfup = function (num) {
 86     return (num < 0) ? Math.ceil(num - 0.5) : Math.floor(num + 0.5);
 87 };
 88 
 89 /**
 90  * @static
 91  * @protected
 92  * @param {number} num number to round
 93  * @return {number} rounded number
 94  */
 95 MathUtils.halfdown = function (num) {
 96     return (num < 0) ? Math.floor(num + 0.5) : Math.ceil(num - 0.5);
 97 };
 98 
 99 /**
100  * @static
101  * @protected
102  * @param {number} num number to round
103  * @return {number} rounded number
104  */
105 MathUtils.halfeven = function (num) {
106     return (Math.floor(num) % 2 === 0) ? Math.ceil(num - 0.5) : Math.floor(num + 0.5);
107 };
108 
109 /**
110  * @static
111  * @protected
112  * @param {number} num number to round
113  * @return {number} rounded number
114  */
115 MathUtils.halfodd = function (num) {
116     return (Math.floor(num) % 2 !== 0) ? Math.ceil(num - 0.5) : Math.floor(num + 0.5);
117 };
118 
119 /**
120  * Do a proper modulo function. The Javascript % operator will give the truncated
121  * division algorithm, but for calendrical calculations, we need the Euclidean
122  * division algorithm where the remainder of any division, whether the dividend
123  * is negative or not, is always a positive number in the range [0, modulus).<p>
124  *
125  *
126  * @static
127  * @param {number} dividend the number being divided
128  * @param {number} modulus the number dividing the dividend. This should always be a positive number.
129  * @return the remainder of dividing the dividend by the modulus.
130  */
131 MathUtils.mod = function (dividend, modulus) {
132     if (modulus === 0) {
133         return 0;
134     }
135     var x = dividend % modulus;
136     return (x < 0) ? x + modulus : x;
137 };
138 
139 /**
140  * Do a proper adjusted modulo function. The Javascript % operator will give the truncated
141  * division algorithm, but for calendrical calculations, we need the Euclidean
142  * division algorithm where the remainder of any division, whether the dividend
143  * is negative or not, is always a positive number in the range (0, modulus]. The adjusted
144  * modulo function differs from the regular modulo function in that when the remainder is
145  * zero, the modulus should be returned instead.<p>
146  *
147  *
148  * @static
149  * @param {number} dividend the number being divided
150  * @param {number} modulus the number dividing the dividend. This should always be a positive number.
151  * @return the remainder of dividing the dividend by the modulus.
152  */
153 MathUtils.amod = function (dividend, modulus) {
154     if (modulus === 0) {
155         return 0;
156     }
157     var x = dividend % modulus;
158     return (x <= 0) ? x + modulus : x;
159 };
160 
161 /**
162  * Return the number with the decimal shifted by the given precision.
163  * Positive precisions shift the decimal to the right giving larger
164  * numbers, and negative ones shift the decimal to the left giving
165  * smaller numbers.
166  *
167  * @static
168  * @param {number} number the number to shift
169  * @param {number} precision the number of places to move the decimal point
170  * @returns {number} the number with the decimal point shifted by the
171  * given number of decimals
172  */
173 MathUtils.shiftDecimal = function shift(number, precision) {
174     var numArray = ("" + number).split("e");
175     return +(numArray[0] + "e" + (numArray[1] ? (+numArray[1] + precision) : precision));
176 };
177 
178 /**
179  * Returns the base 10 logarithm of a number. For platforms that support
180  * Math.log10() it is used directly. For plaforms that do not, such as Qt/QML,
181  * it will be calculated using the natural logarithm.
182  *
183  * @param {number} num the number to take the logarithm of
184  * @returns {number} the base-10 logarithm of the given number
185  */
186 MathUtils.log10 = function(num) {
187     if (typeof(Math.log10) === "function") {
188         return Math.log10(num);
189     }
190 
191     return Math.log(num) / Math.LN10;
192 };
193 
194 /**
195  * Return the given number with only the given number of significant digits.
196  * The number of significant digits can start with the digits greater than
197  * 1 and straddle the decimal point, or it may start after the decimal point.
198  * If the number of digits requested is less than 1, the original number
199  * will be returned unchanged.
200  *
201  * @static
202  * @param {number} number the number to return with only significant digits
203  * @param {number} digits the number of significant digits to include in the
204  * returned number
205  * @param {function(number): number=} round a rounding function to use
206  * @returns {number} the given number with only the requested number of
207  * significant digits
208  */
209 MathUtils.significant = function(number, digits, round) {
210     if (digits < 1 || number === 0) return number;
211     var rnd = round || Math.round;
212     var factor = -Math.floor(MathUtils.log10(Math.abs(number))) + digits - 1;
213     return MathUtils.shiftDecimal(rnd(MathUtils.shiftDecimal(number, factor)), -factor);
214 };
215 
216 module.exports = MathUtils;
217