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