1// Package math exposes functionality from Go's math package as an elvish
2// module.
3package math
4
5import (
6	"math"
7	"math/big"
8
9	"src.elv.sh/pkg/eval"
10	"src.elv.sh/pkg/eval/errs"
11	"src.elv.sh/pkg/eval/vals"
12	"src.elv.sh/pkg/eval/vars"
13)
14
15// Ns is the namespace for the math: module.
16var Ns = eval.BuildNsNamed("math").
17	AddVars(map[string]vars.Var{
18		"e":  vars.NewReadOnly(math.E),
19		"pi": vars.NewReadOnly(math.Pi),
20	}).
21	AddGoFns(map[string]interface{}{
22		"abs":           abs,
23		"acos":          math.Acos,
24		"acosh":         math.Acosh,
25		"asin":          math.Asin,
26		"asinh":         math.Asinh,
27		"atan":          math.Atan,
28		"atanh":         math.Atanh,
29		"ceil":          ceil,
30		"cos":           math.Cos,
31		"cosh":          math.Cosh,
32		"floor":         floor,
33		"is-inf":        isInf,
34		"is-nan":        isNaN,
35		"log":           math.Log,
36		"log10":         math.Log10,
37		"log2":          math.Log2,
38		"max":           max,
39		"min":           min,
40		"pow":           pow,
41		"round":         round,
42		"round-to-even": roundToEven,
43		"sin":           math.Sin,
44		"sinh":          math.Sinh,
45		"sqrt":          math.Sqrt,
46		"tan":           math.Tan,
47		"tanh":          math.Tanh,
48		"trunc":         trunc,
49	}).Ns()
50
51//elvdoc:var e
52//
53// ```elvish
54// $math:e
55// ```
56//
57// Approximate value of
58// [`e`](https://en.wikipedia.org/wiki/E_(mathematical_constant)):
59// 2.718281.... This variable is read-only.
60
61//elvdoc:var pi
62//
63// ```elvish
64// $math:pi
65// ```
66//
67// Approximate value of [`π`](https://en.wikipedia.org/wiki/Pi): 3.141592.... This
68// variable is read-only.
69
70//elvdoc:fn abs
71//
72// ```elvish
73// math:abs $number
74// ```
75//
76// Computes the absolute value `$number`. This function is exactness-preserving.
77// Examples:
78//
79// ```elvish-transcript
80// ~> math:abs 2
81// ▶ (num 2)
82// ~> math:abs -2
83// ▶ (num 2)
84// ~> math:abs 10000000000000000000
85// ▶ (num 10000000000000000000)
86// ~> math:abs -10000000000000000000
87// ▶ (num 10000000000000000000)
88// ~> math:abs 1/2
89// ▶ (num 1/2)
90// ~> math:abs -1/2
91// ▶ (num 1/2)
92// ~> math:abs 1.23
93// ▶ (num 1.23)
94// ~> math:abs -1.23
95// ▶ (num 1.23)
96// ```
97
98const (
99	maxInt = int(^uint(0) >> 1)
100	minInt = -maxInt - 1
101)
102
103var absMinInt = new(big.Int).Abs(big.NewInt(int64(minInt)))
104
105func abs(n vals.Num) vals.Num {
106	switch n := n.(type) {
107	case int:
108		if n < 0 {
109			if n == minInt {
110				return absMinInt
111			}
112			return -n
113		}
114		return n
115	case *big.Int:
116		if n.Sign() < 0 {
117			return new(big.Int).Abs(n)
118		}
119		return n
120	case *big.Rat:
121		if n.Sign() < 0 {
122			return new(big.Rat).Abs(n)
123		}
124		return n
125	case float64:
126		return math.Abs(n)
127	default:
128		panic("unreachable")
129	}
130}
131
132//elvdoc:fn acos
133//
134// ```elvish
135// math:acos $number
136// ```
137//
138// Outputs the arccosine of `$number`, in radians (not degrees). Examples:
139//
140// ```elvish-transcript
141// ~> math:acos 1
142// ▶ (float64 1)
143// ~> math:acos 1.00001
144// ▶ (float64 NaN)
145// ```
146
147//elvdoc:fn acosh
148//
149// ```elvish
150// math:acosh $number
151// ```
152//
153// Outputs the inverse hyperbolic cosine of `$number`. Examples:
154//
155// ```elvish-transcript
156// ~> math:acosh 1
157// ▶ (float64 0)
158// ~> math:acosh 0
159// ▶ (float64 NaN)
160// ```
161
162//elvdoc:fn asin
163//
164// ```elvish
165// math:asin $number
166// ```
167//
168// Outputs the arcsine of `$number`, in radians (not degrees). Examples:
169//
170// ```elvish-transcript
171// ~> math:asin 0
172// ▶ (float64 0)
173// ~> math:asin 1
174// ▶ (float64 1.5707963267948966)
175// ~> math:asin 1.00001
176// ▶ (float64 NaN)
177// ```
178
179//elvdoc:fn asinh
180//
181// ```elvish
182// math:asinh $number
183// ```
184//
185// Outputs the inverse hyperbolic sine of `$number`. Examples:
186//
187// ```elvish-transcript
188// ~> math:asinh 0
189// ▶ (float64 0)
190// ~> math:asinh inf
191// ▶ (float64 +Inf)
192// ```
193
194//elvdoc:fn atan
195//
196// ```elvish
197// math:atan $number
198// ```
199//
200// Outputs the arctangent of `$number`, in radians (not degrees). Examples:
201//
202// ```elvish-transcript
203// ~> math:atan 0
204// ▶ (float64 0)
205// ~> math:atan $math:inf
206// ▶ (float64 1.5707963267948966)
207// ```
208
209//elvdoc:fn atanh
210//
211// ```elvish
212// math:atanh $number
213// ```
214//
215// Outputs the inverse hyperbolic tangent of `$number`. Examples:
216//
217// ```elvish-transcript
218// ~> math:atanh 0
219// ▶ (float64 0)
220// ~> math:atanh 1
221// ▶ (float64 +Inf)
222// ```
223
224//elvdoc:fn ceil
225//
226// ```elvish
227// math:ceil $number
228// ```
229//
230// Computes the least integer greater than or equal to `$number`. This function
231// is exactness-preserving.
232//
233// The results for the special floating-point values -0.0, +0.0, -Inf, +Inf and
234// NaN are themselves.
235//
236// Examples:
237//
238// ```elvish-transcript
239// ~> math:floor 1
240// ▶ (num 1)
241// ~> math:floor 3/2
242// ▶ (num 1)
243// ~> math:floor -3/2
244// ▶ (num -2)
245// ~> math:floor 1.1
246// ▶ (num 1.0)
247// ~> math:floor -1.1
248// ▶ (num -2.0)
249// ```
250
251var (
252	big1 = big.NewInt(1)
253	big2 = big.NewInt(2)
254)
255
256func ceil(n vals.Num) vals.Num {
257	return integerize(n,
258		math.Ceil,
259		func(n *big.Rat) *big.Int {
260			q := new(big.Int).Div(n.Num(), n.Denom())
261			return q.Add(q, big1)
262		})
263}
264
265//elvdoc:fn cos
266//
267// ```elvish
268// math:cos $number
269// ```
270//
271// Computes the cosine of `$number` in units of radians (not degrees).
272// Examples:
273//
274// ```elvish-transcript
275// ~> math:cos 0
276// ▶ (float64 1)
277// ~> math:cos 3.14159265
278// ▶ (float64 -1)
279// ```
280
281//elvdoc:fn cosh
282//
283// ```elvish
284// math:cosh $number
285// ```
286//
287// Computes the hyperbolic cosine of `$number`. Example:
288//
289// ```elvish-transcript
290// ~> math:cosh 0
291// ▶ (float64 1)
292// ```
293
294//elvdoc:fn floor
295//
296// ```elvish
297// math:floor $number
298// ```
299//
300// Computes the greatest integer less than or equal to `$number`. This function
301// is exactness-preserving.
302//
303// The results for the special floating-point values -0.0, +0.0, -Inf, +Inf and
304// NaN are themselves.
305//
306// Examples:
307//
308// ```elvish-transcript
309// ~> math:floor 1
310// ▶ (num 1)
311// ~> math:floor 3/2
312// ▶ (num 1)
313// ~> math:floor -3/2
314// ▶ (num -2)
315// ~> math:floor 1.1
316// ▶ (num 1.0)
317// ~> math:floor -1.1
318// ▶ (num -2.0)
319// ```
320
321func floor(n vals.Num) vals.Num {
322	return integerize(n,
323		math.Floor,
324		func(n *big.Rat) *big.Int {
325			return new(big.Int).Div(n.Num(), n.Denom())
326		})
327}
328
329//elvdoc:fn is-inf
330//
331// ```elvish
332// math:is-inf &sign=0 $number
333// ```
334//
335// Tests whether the number is infinity. If sign > 0, tests whether `$number`
336// is positive infinity. If sign < 0, tests whether `$number` is negative
337// infinity. If sign == 0, tests whether `$number` is either infinity.
338//
339// ```elvish-transcript
340// ~> math:is-inf 123
341// ▶ $false
342// ~> math:is-inf inf
343// ▶ $true
344// ~> math:is-inf -inf
345// ▶ $true
346// ~> math:is-inf &sign=1 inf
347// ▶ $true
348// ~> math:is-inf &sign=-1 inf
349// ▶ $false
350// ~> math:is-inf &sign=-1 -inf
351// ▶ $true
352// ```
353
354type isInfOpts struct{ Sign int }
355
356func (opts *isInfOpts) SetDefaultOptions() { opts.Sign = 0 }
357
358func isInf(opts isInfOpts, n vals.Num) bool {
359	if f, ok := n.(float64); ok {
360		return math.IsInf(f, opts.Sign)
361	}
362	return false
363}
364
365//elvdoc:fn is-nan
366//
367// ```elvish
368// math:is-nan $number
369// ```
370//
371// Tests whether the number is a NaN (not-a-number).
372//
373// ```elvish-transcript
374// ~> math:is-nan 123
375// ▶ $false
376// ~> math:is-nan (float64 inf)
377// ▶ $false
378// ~> math:is-nan (float64 nan)
379// ▶ $true
380// ```
381
382func isNaN(n vals.Num) bool {
383	if f, ok := n.(float64); ok {
384		return math.IsNaN(f)
385	}
386	return false
387}
388
389//elvdoc:fn log
390//
391// ```elvish
392// math:log $number
393// ```
394//
395// Computes the natural (base *e*) logarithm of `$number`. Examples:
396//
397// ```elvish-transcript
398// ~> math:log 1.0
399// ▶ (float64 1)
400// ~> math:log -2.3
401// ▶ (float64 NaN)
402// ```
403
404//elvdoc:fn log10
405//
406// ```elvish
407// math:log10 $number
408// ```
409//
410// Computes the base 10 logarithm of `$number`. Examples:
411//
412// ```elvish-transcript
413// ~> math:log10 100.0
414// ▶ (float64 2)
415// ~> math:log10 -1.7
416// ▶ (float64 NaN)
417// ```
418
419//elvdoc:fn log2
420//
421// ```elvish
422// math:log2 $number
423// ```
424//
425// Computes the base 2 logarithm of `$number`. Examples:
426//
427// ```elvish-transcript
428// ~> math:log2 8
429// ▶ (float64 3)
430// ~> math:log2 -5.3
431// ▶ (float64 NaN)
432// ```
433
434//elvdoc:fn max
435//
436// ```elvish
437// math:max $number...
438// ```
439//
440// Outputs the maximum number in the arguments. If there are no arguments,
441// an exception is thrown. If any number is NaN then NaN is output. This
442// function is exactness-preserving.
443//
444// Examples:
445//
446// ```elvish-transcript
447// ~> math:max 3 5 2
448// ▶ (num 5)
449// ~> math:max (range 100)
450// ▶ (num 99)
451// ~> math:max 1/2 1/3 2/3
452// ▶ (num 2/3)
453// ```
454
455func max(rawNums ...vals.Num) (vals.Num, error) {
456	if len(rawNums) == 0 {
457		return nil, errs.ArityMismatch{What: "arguments", ValidLow: 1, ValidHigh: -1, Actual: 0}
458	}
459	nums := vals.UnifyNums(rawNums, 0)
460	switch nums := nums.(type) {
461	case []int:
462		n := nums[0]
463		for i := 1; i < len(nums); i++ {
464			if n < nums[i] {
465				n = nums[i]
466			}
467		}
468		return n, nil
469	case []*big.Int:
470		n := nums[0]
471		for i := 1; i < len(nums); i++ {
472			if n.Cmp(nums[i]) < 0 {
473				n = nums[i]
474			}
475		}
476		return n, nil
477	case []*big.Rat:
478		n := nums[0]
479		for i := 1; i < len(nums); i++ {
480			if n.Cmp(nums[i]) < 0 {
481				n = nums[i]
482			}
483		}
484		return n, nil
485	case []float64:
486		n := nums[0]
487		for i := 1; i < len(nums); i++ {
488			n = math.Max(n, nums[i])
489		}
490		return n, nil
491	default:
492		panic("unreachable")
493	}
494}
495
496//elvdoc:fn min
497//
498// ```elvish
499// math:min $number...
500// ```
501//
502// Outputs the minimum number in the arguments. If there are no arguments
503// an exception is thrown. If any number is NaN then NaN is output. This
504// function is exactness-preserving.
505//
506// Examples:
507//
508// ```elvish-transcript
509// ~> math:min
510// Exception: arity mismatch: arguments must be 1 or more values, but is 0 values
511// [tty 17], line 1: math:min
512// ~> math:min 3 5 2
513// ▶ (num 2)
514// ~> math:min 1/2 1/3 2/3
515// ▶ (num 1/3)
516// ```
517
518func min(rawNums ...vals.Num) (vals.Num, error) {
519	if len(rawNums) == 0 {
520		return nil, errs.ArityMismatch{What: "arguments", ValidLow: 1, ValidHigh: -1, Actual: 0}
521	}
522	nums := vals.UnifyNums(rawNums, 0)
523	switch nums := nums.(type) {
524	case []int:
525		n := nums[0]
526		for i := 1; i < len(nums); i++ {
527			if n > nums[i] {
528				n = nums[i]
529			}
530		}
531		return n, nil
532	case []*big.Int:
533		n := nums[0]
534		for i := 1; i < len(nums); i++ {
535			if n.Cmp(nums[i]) > 0 {
536				n = nums[i]
537			}
538		}
539		return n, nil
540	case []*big.Rat:
541		n := nums[0]
542		for i := 1; i < len(nums); i++ {
543			if n.Cmp(nums[i]) > 0 {
544				n = nums[i]
545			}
546		}
547		return n, nil
548	case []float64:
549		n := nums[0]
550		for i := 1; i < len(nums); i++ {
551			n = math.Min(n, nums[i])
552		}
553		return n, nil
554	default:
555		panic("unreachable")
556	}
557}
558
559//elvdoc:fn pow
560//
561// ```elvish
562// math:pow $base $exponent
563// ```
564//
565// Outputs the result of raising `$base` to the power of `$exponent`.
566//
567// This function produces an exact result when `$base` is exact and `$exponent`
568// is an exact integer. Otherwise it produces an inexact result.
569//
570// Examples:
571//
572// ```elvish-transcript
573// ~> math:pow 3 2
574// ▶ (num 9)
575// ~> math:pow -2 2
576// ▶ (num 4)
577// ~> math:pow 1/2 3
578// ▶ (num 1/8)
579// ~> math:pow 1/2 -3
580// ▶ (num 8)
581// ~> math:pow 9 1/2
582// ▶ (num 3.0)
583// ~> math:pow 12 1.1
584// ▶ (num 15.38506624784179)
585// ```
586
587func pow(base, exp vals.Num) vals.Num {
588	if isExact(base) && isExactInt(exp) {
589		// Produce exact result
590		switch exp {
591		case 0:
592			return 1
593		case 1:
594			return base
595		case -1:
596			return new(big.Rat).Inv(vals.PromoteToBigRat(base))
597		}
598		exp := vals.PromoteToBigInt(exp)
599		if isExactInt(base) && exp.Sign() > 0 {
600			base := vals.PromoteToBigInt(base)
601			return new(big.Int).Exp(base, exp, nil)
602		}
603		base := vals.PromoteToBigRat(base)
604		if exp.Sign() < 0 {
605			base = new(big.Rat).Inv(base)
606			exp = new(big.Int).Neg(exp)
607		}
608		return new(big.Rat).SetFrac(
609			new(big.Int).Exp(base.Num(), exp, nil),
610			new(big.Int).Exp(base.Denom(), exp, nil))
611	}
612
613	// Produce inexact result
614	basef := vals.ConvertToFloat64(base)
615	expf := vals.ConvertToFloat64(exp)
616	return math.Pow(basef, expf)
617}
618
619func isExact(n vals.Num) bool {
620	switch n.(type) {
621	case int, *big.Int, *big.Rat:
622		return true
623	default:
624		return false
625	}
626}
627
628func isExactInt(n vals.Num) bool {
629	switch n.(type) {
630	case int, *big.Int:
631		return true
632	default:
633		return false
634	}
635}
636
637//elvdoc:fn round
638//
639// ```elvish
640// math:round $number
641// ```
642//
643// Outputs the nearest integer, rounding half away from zero. This function is
644// exactness-preserving.
645//
646// The results for the special floating-point values -0.0, +0.0, -Inf, +Inf and
647// NaN are themselves.
648//
649// Examples:
650//
651// ```elvish-transcript
652// ~> math:round 2
653// ▶ (num 2)
654// ~> math:round 1/3
655// ▶ (num 0)
656// ~> math:round 1/2
657// ▶ (num 1)
658// ~> math:round 2/3
659// ▶ (num 1)
660// ~> math:round -1/3
661// ▶ (num 0)
662// ~> math:round -1/2
663// ▶ (num -1)
664// ~> math:round -2/3
665// ▶ (num -1)
666// ~> math:round 2.5
667// ▶ (num 3.0)
668// ```
669
670func round(n vals.Num) vals.Num {
671	return integerize(n,
672		math.Round,
673		func(n *big.Rat) *big.Int {
674			q, m := new(big.Int).QuoRem(n.Num(), n.Denom(), new(big.Int))
675			m = m.Mul(m, big2)
676			if m.CmpAbs(n.Denom()) < 0 {
677				return q
678			}
679			if n.Sign() < 0 {
680				return q.Sub(q, big1)
681			}
682			return q.Add(q, big1)
683		})
684}
685
686//elvdoc:fn round-to-even
687//
688// ```elvish
689// math:round-to-even $number
690// ```
691//
692// Outputs the nearest integer, rounding ties to even. This function is
693// exactness-preserving.
694//
695// The results for the special floating-point values -0.0, +0.0, -Inf, +Inf and
696// NaN are themselves.
697//
698// Examples:
699//
700// ```elvish-transcript
701// ~> math:round-to-even 2
702// ▶ (num 2)
703// ~> math:round-to-even 1/2
704// ▶ (num 0)
705// ~> math:round-to-even 3/2
706// ▶ (num 2)
707// ~> math:round-to-even 5/2
708// ▶ (num 2)
709// ~> math:round-to-even -5/2
710// ▶ (num -2)
711// ~> math:round-to-even 2.5
712// ▶ (num 2.0)
713// ~> math:round-to-even 1.5
714// ▶ (num 2.0)
715// ```
716
717func roundToEven(n vals.Num) vals.Num {
718	return integerize(n,
719		math.RoundToEven,
720		func(n *big.Rat) *big.Int {
721			q, m := new(big.Int).QuoRem(n.Num(), n.Denom(), new(big.Int))
722			m = m.Mul(m, big2)
723			if diff := m.CmpAbs(n.Denom()); diff < 0 || diff == 0 && q.Bit(0) == 0 {
724				return q
725			}
726			if n.Sign() < 0 {
727				return q.Sub(q, big1)
728			}
729			return q.Add(q, big1)
730		})
731}
732
733//elvdoc:fn sin
734//
735// ```elvish
736// math:sin $number
737// ```
738//
739// Computes the sine of `$number` in units of radians (not degrees). Examples:
740//
741// ```elvish-transcript
742// ~> math:sin 0
743// ▶ (float64 0)
744// ~> math:sin 3.14159265
745// ▶ (float64 3.5897930298416118e-09)
746// ```
747
748//elvdoc:fn sinh
749//
750// ```elvish
751// math:sinh $number
752// ```
753//
754// Computes the hyperbolic sine of `$number`. Example:
755//
756// ```elvish-transcript
757// ~> math:sinh 0
758// ▶ (float64 0)
759// ```
760
761//elvdoc:fn sqrt
762//
763// ```elvish
764// math:sqrt $number
765// ```
766//
767// Computes the square-root of `$number`. Examples:
768//
769// ```elvish-transcript
770// ~> math:sqrt 0
771// ▶ (float64 0)
772// ~> math:sqrt 4
773// ▶ (float64 2)
774// ~> math:sqrt -4
775// ▶ (float64 NaN)
776// ```
777
778//elvdoc:fn tan
779//
780// ```elvish
781// math:tan $number
782// ```
783//
784// Computes the tangent of `$number` in units of radians (not degrees). Examples:
785//
786// ```elvish-transcript
787// ~> math:tan 0
788// ▶ (float64 0)
789// ~> math:tan 3.14159265
790// ▶ (float64 -0.0000000035897930298416118)
791// ```
792
793//elvdoc:fn tanh
794//
795// ```elvish
796// math:tanh $number
797// ```
798//
799// Computes the hyperbolic tangent of `$number`. Example:
800//
801// ```elvish-transcript
802// ~> math:tanh 0
803// ▶ (float64 0)
804// ```
805
806//elvdoc:fn trunc
807//
808// ```elvish
809// math:trunc $number
810// ```
811//
812// Outputs the integer portion of `$number`. This function is exactness-preserving.
813//
814// The results for the special floating-point values -0.0, +0.0, -Inf, +Inf and
815// NaN are themselves.
816//
817// Examples:
818//
819// ```elvish-transcript
820// ~> math:trunc 1
821// ▶ (num 1)
822// ~> math:trunc 3/2
823// ▶ (num 1)
824// ~> math:trunc 5/3
825// ▶ (num 1)
826// ~> math:trunc -3/2
827// ▶ (num -1)
828// ~> math:trunc -5/3
829// ▶ (num -1)
830// ~> math:trunc 1.7
831// ▶ (num 1.0)
832// ~> math:trunc -1.7
833// ▶ (num -1.0)
834// ```
835
836func trunc(n vals.Num) vals.Num {
837	return integerize(n,
838		math.Trunc,
839		func(n *big.Rat) *big.Int {
840			return new(big.Int).Quo(n.Num(), n.Denom())
841		})
842}
843
844func integerize(n vals.Num, fnFloat func(float64) float64, fnRat func(*big.Rat) *big.Int) vals.Num {
845	switch n := n.(type) {
846	case int:
847		return n
848	case *big.Int:
849		return n
850	case *big.Rat:
851		if n.Denom().IsInt64() && n.Denom().Int64() == 1 {
852			// Elvish always normalizes *big.Rat with a denominator of 1 to
853			// *big.Int, but we still try to be defensive here.
854			return n.Num()
855		}
856		return fnRat(n)
857	case float64:
858		return fnFloat(n)
859	default:
860		panic("unreachable")
861	}
862}
863