Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Suggestion] Methods to be added #12

Open
Berkmann18 opened this issue Jan 5, 2019 · 8 comments
Open

[Suggestion] Methods to be added #12

Berkmann18 opened this issue Jan 5, 2019 · 8 comments

Comments

@Berkmann18
Copy link

Berkmann18 commented Jan 5, 2019

Aside from the randomInteger from #8 and the Math.sum from #4, I think that including the methods below would help, especially when languages like Python, Matlab and Processing have them:

  • Math.clamp
Math.clamp = (low, x, high) => (x < low) ? low : ((x > high) ? high: x);
//Or using other methods
Math.clamp = (low, x, high) => Math.max(low, Math.min(x, high))
//Examples
Math.clamp(0, 5, 10); //5
Math.clamp(0, -1, 10); //0
Math.clamp(0, 42, 10); //10
  • Math.log which would allow the current Math.log to be more flexible without breaking it.
Math.log = (a, b = Math.E) => Math.log(a) / Math.log(b)
  • Math.range
Math.range = range = (...args) => {
 if (args.length === 1) { //Math.range(max)
   return range(0, 1, args[0])
 } else if (args.length === 2) { //Math.range(max, inc)
   return range(0, args[1], args[0])
 }
 
 let val = [],
   n = 0,
   [min, inc, max] = args;

 if (inc > 0) {
   for (let i = min; i <= max; i += inc) val[n++] = i
 } else {
   for (let i = min; i >= max; i -= inc) val[n++] = i
 }
 return val
};

// Examples
Math.range(5); // [0, 1, 2, 3, 4, 5]
Math.range(10, 2); // [0, 2, 4, 6, 8, 10]
Math.range(-1, .5, 1); // [-1, -0.5, 0, 0.5, 1]
  • Math.gcd
Math.gcd = (a, b) => b ? Math.gcd(b, a % b): Math.abs(a)
  • Math.lcm
Math.lcm = (a, b) => {
  let multiple = a;
  Math.range(a, 1, b).forEach((n) => multiple = (multiple * n) / gcd(multiple, n));
  return multiple;
}
  • Math.factorial or perhaps a syntactic sugar (like 5! or something not ambiguous)
Math.factorial = (a) => (a <= 1) ? a : a * Math.factorial(a - 1)
  • Math.combination/Math.choose
Math.combination = (a, b) => Math.factorial(a) / (Math.factorial(b) * Math.factorial(a - b))
  • Math.equivalent/Math.equiv/Math.approx
Math.equivalent = (a, b) => Math.round(a) === Math.round(b) || Math.floor(a) === Math.floor(b) || Math.ceil(a) === Math.floor(b) || Math.floor(a) === Math.ceil(b);

// Or
Math.equivalent = (a, b) => {
  let flatA = a | 0,
    flatB = b | 0,
    ceilA = a | 1,
    ceilB = b | 1;
  return Math.round(a) === Math.round(b) || flatA === flatB || ceilA === flatB || flatA === ceilB;
}

There are other methods I have in mind but the above would be a good start (e.g. Math.mean, Math.trimmedAverage).

@Berkmann18 Berkmann18 changed the title Methods to be added [Suggestion] Methods to be added Jan 5, 2019
@Crissov
Copy link

Crissov commented Feb 10, 2020

IEEE 754, in subsection 9.2, recommends some additional operations to be predefined:

  • Math.rsqrt(x) => 1/Math.sqrt(x)
  • Math.compound(x, n) => Math.pow(x + 1, Math.round(n)), i.e. integer n
  • Math.rootn(x, n) => Math.pow(x, 1/Math.round(n)), i.e. integer n
  • Math.pown(x, n) => Math.pow(x, Math.round(n)), i.e. integer n
  • Math.powr(x, y) => Math.pow(Math.abs(x), y)
  • Math.sinpi(x) => Math.sin(x * Math.PI)
  • Math.cospi(x) => Math.cos(x * Math.PI)
  • Math.tanpi(x) => Math.tan(x * Math.PI)
  • Math.asinpi(x) => Math.asin(x) / Math.PI
  • Math.acospi(x) => Math.acos(x) / Math.PI
  • Math.atanpi(x) => Math.atan(x) / Math.PI
  • Math.atan2pi(y, x) => Math.atan2(y, x) / Math.PI (?)

I think ECMA-262 has all operations that are required in section 5.
Section 9 has even more operations.

@Crissov
Copy link

Crissov commented Feb 10, 2020

There is a bunch of trigonometric functions used historically that could potentially make some calculations more precise or at least shorter.

  • Math.cot(x) => 1 / Math.tan(x) cotangent
  • Math.sec(x) => 1 / Math.cos(x) secant
  • Math.csc(x) => 1 / Math.sin(x) cosecant
  • Math.vsn(x) => 1 - Math.cos(x) versed sine versin
  • Math.vcs(x) => 1 + Math.cos(x) versed cosine
  • Math.cvs(x) => 1 - Math.sin(x) coversed sine
  • Math.cvc(x) => 1 + Math.sin(x) coversed cosine
  • Math.hvs(x) => Math.vsn(x) / 2 haversed sine
  • Math.hvc(x) => Math.vcs(x) / 2 haversed cosine
  • Math.hcs(x) => Math.cvs(x) / 2 hacoversed sine
  • Math.hcc(x) => Math.cvc(x) / 2 hacoversed cosine
  • Math.xsc(x) => Math.sec(x) - 1 exsecant exsec
  • Math.xcs(x) => Math.csc(x) - 1 excosecant excsc
  • Math.crd(x) => 2 * Math.sin(x/2) chord

graphical construction of the various trigonometric functions from a chord AD (angle θ) of the unit circle centered at O. modern trigonometric functions sin (sine), cos (cosine), tan (tangent), cot (cotangent), sec (secant) and csc (cosecant), additionally: chord, versin (versed sine), exsec (exsecant), cvs (coversed sine) and excsc (excosecant)

@OlsonDev
Copy link

I suggest Math.clamp argument order to be changed to (min, value, max) instead of (value, min, max) so it's in line with CSS's clamp().

@Berkmann18
Copy link
Author

@OlsonDev Good point, I'll amend the OP.

@Andrew-Cottrell
Copy link

Andrew-Cottrell commented Jun 5, 2020

There is a bunch of trigonometric functions used historically that could potentially make some calculations more precise or at least shorter.

  • Math.hvs(x) => Math.vsn(x) / 2 haversed sine

These may potentially also be useful

// restatement of the above suggestion, in the closed interval [0, 1]
Math.hvs = function (radians) {
    return Math.pow(Math.sin(radians / 2), 2);
};

// return value in radians, in the closed interval [0, π]
Math.ahvs = function (haversine) {
    return 2 * Math.asin(Math.sqrt(haversine));
};

// haversine of the central angle, in the closed interval [0, 1]
Math.haversineFormula = function (latA, lonA, latB, lonB) {
    return Math.hvs(latB - latA) + Math.cos(latA) * Math.cos(latB) * Math.hvs(lonB - lonA);
};

// on the unit sphere, in the closed interval [0, π]
Math.sphericalDistance = function (latA, lonA, latB, lonB) {
    return Math.ahvs(Math.haversineFormula(latA, lonA, latB, lonB));
};

@Rudxain
Copy link

Rudxain commented Apr 29, 2022

I also agree that factorial should be added since its use is common. Also because a native implementation would definitely be faster. And if this proposal gets to Stage 4, a native factorial optimized for BigInts would be a great idea, because efficient implementations are hard to get right, and require a lot of code, this repo is proof of it

@Rudxain
Copy link

Rudxain commented Jun 11, 2022

gcd and lcm should be variadic because code like this:

//num_list is an arbitrary iterable object here
let d = [...num_list].reduce(Math.gcd, 0)

Or this:

let d = 0;
for (const x of num_list)
	d = Math.gcd(d, x);

Can be refactored as this:

let d = Math.gcd(...num_list)

The output of the empty GCD gcd() should equal the initial value of the algorithm. In all cases, the init is 0, because if it were 1 the results would be wrong. gcd(x) == x (except for NaN) because gcd(x, 0) == x && gcd(0, x) == x. Reference

LCM can be defined as:

Math.lcm = function(...args) {
	return args.reduce((a, b) => {
		a = +a; b = +b;
		return a != 0 || b != 0 ? a / Math.gcd(a, b) * b : 0
	}, 1)
}

Since 0 gives wrong results as init, it seems lcm() == 1 is valid. References (see comments)

Now the real problem is non-ints. Should these functions return NaN? actually, there's no need for that. The solution is to let implementations choose an algorithm to convert floats into fractions, then compute the GCD or LCM of those fractions, and finally convert the resulting fraction to a float. The main problem is that the accuracy of the algorithm would be implementation-defined. I proposed the fraction solution because the Euclidean algorithm for floats has intrinsic rounding errors becase the % op treats floats as fractions whose denominator is a power of 2 (because they are)

@Rudxain
Copy link

Rudxain commented Jul 8, 2022

@Crissov I would also recommend this one:

Math.sinc = x => +x == 0 ? 1 : Math.sin(x) / x

It's important and useful

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants