github.com/kolbycrouch/elvish@v0.14.1-0.20210614162631-215b9ac1c423/pkg/eval/mods/math/math.go (about) 1 // Package math exposes functionality from Go's math package as an elvish 2 // module. 3 package math 4 5 import ( 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. 16 var Ns = eval.NsBuilder{ 17 "e": vars.NewReadOnly(math.E), 18 "pi": vars.NewReadOnly(math.Pi), 19 }.AddGoFns("math:", fns).Ns() 20 21 var fns = 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": math.Ceil, // TODO: Make exactness-preserving 30 "cos": math.Cos, 31 "cosh": math.Cosh, 32 "floor": math.Floor, // TODO: Make exactness-preserving 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": math.Pow, // TODO: Make exactness-preserving for integer exponents 41 "pow10": math.Pow10, // TODO: Make exactness-preserving for integer exponents 42 "round": math.Round, // TODO: Make exactness-preserving 43 "round-to-even": math.RoundToEven, // TODO: Make exactness-preserving 44 "sin": math.Sin, 45 "sinh": math.Sinh, 46 "sqrt": math.Sqrt, 47 "tan": math.Tan, 48 "tanh": math.Tanh, 49 "trunc": math.Trunc, // TODO: Make exactness-preserving 50 } 51 52 //elvdoc:var e 53 // 54 // ```elvish 55 // $math:e 56 // ``` 57 // 58 // Approximate value of 59 // [`e`](https://en.wikipedia.org/wiki/E_(mathematical_constant)): 60 // 2.718281.... This variable is read-only. 61 62 //elvdoc:var pi 63 // 64 // ```elvish 65 // $math:pi 66 // ``` 67 // 68 // Approximate value of [`π`](https://en.wikipedia.org/wiki/Pi): 3.141592.... This 69 // variable is read-only. 70 71 //elvdoc:fn abs 72 // 73 // ```elvish 74 // math:abs $number 75 // ``` 76 // 77 // Computes the absolute value `$number`. This function is exactness-preserving. 78 // Examples: 79 // 80 // ```elvish-transcript 81 // ~> math:abs 2 82 // ▶ (num 2) 83 // ~> math:abs -2 84 // ▶ (num 2) 85 // ~> math:abs 10000000000000000000 86 // ▶ (num 10000000000000000000) 87 // ~> math:abs -10000000000000000000 88 // ▶ (num 10000000000000000000) 89 // ~> math:abs 1/2 90 // ▶ (num 1/2) 91 // ~> math:abs -1/2 92 // ▶ (num 1/2) 93 // ~> math:abs 1.23 94 // ▶ (num 1.23) 95 // ~> math:abs -1.23 96 // ▶ (num 1.23) 97 // ``` 98 99 const ( 100 maxInt = int(^uint(0) >> 1) 101 minInt = -maxInt - 1 102 ) 103 104 var absMinInt = new(big.Int).Abs(big.NewInt(int64(minInt))) 105 106 func abs(n vals.Num) vals.Num { 107 switch n := n.(type) { 108 case int: 109 if n < 0 { 110 if n == minInt { 111 return absMinInt 112 } 113 return -n 114 } 115 return n 116 case *big.Int: 117 if n.Sign() < 0 { 118 return new(big.Int).Abs(n) 119 } 120 return n 121 case *big.Rat: 122 if n.Sign() < 0 { 123 return new(big.Rat).Abs(n) 124 } 125 return n 126 case float64: 127 return math.Abs(n) 128 default: 129 panic("unreachable") 130 } 131 } 132 133 //elvdoc:fn ceil 134 // 135 // ```elvish 136 // math:ceil $number 137 // ``` 138 // 139 // Computes the ceiling of `$number`. 140 // Read the [Go documentation](https://godoc.org/math#Ceil) for the details of 141 // how this behaves. Examples: 142 // 143 // ```elvish-transcript 144 // ~> math:ceil 1.1 145 // ▶ (float64 2) 146 // ~> math:ceil -2.3 147 // ▶ (float64 -2) 148 // ``` 149 150 //elvdoc:fn acos 151 // 152 // ```elvish 153 // math:acos $number 154 // ``` 155 // 156 // Outputs the arccosine of `$number`, in radians (not degrees). Examples: 157 // 158 // ```elvish-transcript 159 // ~> math:acos 1 160 // ▶ (float64 1) 161 // ~> math:acos 1.00001 162 // ▶ (float64 NaN) 163 // ``` 164 165 //elvdoc:fn acosh 166 // 167 // ```elvish 168 // math:acosh $number 169 // ``` 170 // 171 // Outputs the inverse hyperbolic cosine of `$number`. Examples: 172 // 173 // ```elvish-transcript 174 // ~> math:acosh 1 175 // ▶ (float64 0) 176 // ~> math:acosh 0 177 // ▶ (float64 NaN) 178 // ``` 179 180 //elvdoc:fn asin 181 // 182 // ```elvish 183 // math:asin $number 184 // ``` 185 // 186 // Outputs the arcsine of `$number`, in radians (not degrees). Examples: 187 // 188 // ```elvish-transcript 189 // ~> math:asin 0 190 // ▶ (float64 0) 191 // ~> math:asin 1 192 // ▶ (float64 1.5707963267948966) 193 // ~> math:asin 1.00001 194 // ▶ (float64 NaN) 195 // ``` 196 197 //elvdoc:fn asinh 198 // 199 // ```elvish 200 // math:asinh $number 201 // ``` 202 // 203 // Outputs the inverse hyperbolic sine of `$number`. Examples: 204 // 205 // ```elvish-transcript 206 // ~> math:asinh 0 207 // ▶ (float64 0) 208 // ~> math:asinh inf 209 // ▶ (float64 +Inf) 210 // ``` 211 212 //elvdoc:fn atan 213 // 214 // ```elvish 215 // math:atan $number 216 // ``` 217 // 218 // Outputs the arctangent of `$number`, in radians (not degrees). Examples: 219 // 220 // ```elvish-transcript 221 // ~> math:atan 0 222 // ▶ (float64 0) 223 // ~> math:atan $math:inf 224 // ▶ (float64 1.5707963267948966) 225 // ``` 226 227 //elvdoc:fn atanh 228 // 229 // ```elvish 230 // math:atanh $number 231 // ``` 232 // 233 // Outputs the inverse hyperbolic tangent of `$number`. Examples: 234 // 235 // ```elvish-transcript 236 // ~> math:atanh 0 237 // ▶ (float64 0) 238 // ~> math:atanh 1 239 // ▶ (float64 +Inf) 240 // ``` 241 242 //elvdoc:fn cos 243 // 244 // ```elvish 245 // math:cos $number 246 // ``` 247 // 248 // Computes the cosine of `$number` in units of radians (not degrees). 249 // Examples: 250 // 251 // ```elvish-transcript 252 // ~> math:cos 0 253 // ▶ (float64 1) 254 // ~> math:cos 3.14159265 255 // ▶ (float64 -1) 256 // ``` 257 258 //elvdoc:fn cosh 259 // 260 // ```elvish 261 // math:cosh $number 262 // ``` 263 // 264 // Computes the hyperbolic cosine of `$number`. Example: 265 // 266 // ```elvish-transcript 267 // ~> math:cosh 0 268 // ▶ (float64 1) 269 // ``` 270 271 //elvdoc:fn floor 272 // 273 // ```elvish 274 // math:floor $number 275 // ``` 276 // 277 // Computes the floor of `$number`. 278 // Read the [Go documentation](https://godoc.org/math#Floor) for the details of 279 // how this behaves. Examples: 280 // 281 // ```elvish-transcript 282 // ~> math:floor 1.1 283 // ▶ (float64 1) 284 // ~> math:floor -2.3 285 // ▶ (float64 -3) 286 // ``` 287 288 //elvdoc:fn is-inf 289 // 290 // ```elvish 291 // math:is-inf &sign=0 $number 292 // ``` 293 // 294 // Tests whether the number is infinity. If sign > 0, tests whether `$number` 295 // is positive infinity. If sign < 0, tests whether `$number` is negative 296 // infinity. If sign == 0, tests whether `$number` is either infinity. 297 // 298 // ```elvish-transcript 299 // ~> math:is-inf 123 300 // ▶ $false 301 // ~> math:is-inf inf 302 // ▶ $true 303 // ~> math:is-inf -inf 304 // ▶ $true 305 // ~> math:is-inf &sign=1 inf 306 // ▶ $true 307 // ~> math:is-inf &sign=-1 inf 308 // ▶ $false 309 // ~> math:is-inf &sign=-1 -inf 310 // ▶ $true 311 // ``` 312 313 type isInfOpts struct{ Sign int } 314 315 func (opts *isInfOpts) SetDefaultOptions() { opts.Sign = 0 } 316 317 func isInf(opts isInfOpts, n vals.Num) bool { 318 if f, ok := n.(float64); ok { 319 return math.IsInf(f, opts.Sign) 320 } 321 return false 322 } 323 324 //elvdoc:fn is-nan 325 // 326 // ```elvish 327 // math:is-nan $number 328 // ``` 329 // 330 // Tests whether the number is a NaN (not-a-number). 331 // 332 // ```elvish-transcript 333 // ~> math:is-nan 123 334 // ▶ $false 335 // ~> math:is-nan (float64 inf) 336 // ▶ $false 337 // ~> math:is-nan (float64 nan) 338 // ▶ $true 339 // ``` 340 341 func isNaN(n vals.Num) bool { 342 if f, ok := n.(float64); ok { 343 return math.IsNaN(f) 344 } 345 return false 346 } 347 348 //elvdoc:fn log 349 // 350 // ```elvish 351 // math:log $number 352 // ``` 353 // 354 // Computes the natural (base *e*) logarithm of `$number`. Examples: 355 // 356 // ```elvish-transcript 357 // ~> math:log 1.0 358 // ▶ (float64 1) 359 // ~> math:log -2.3 360 // ▶ (float64 NaN) 361 // ``` 362 363 //elvdoc:fn log10 364 // 365 // ```elvish 366 // math:log10 $number 367 // ``` 368 // 369 // Computes the base 10 logarithm of `$number`. Examples: 370 // 371 // ```elvish-transcript 372 // ~> math:log10 100.0 373 // ▶ (float64 2) 374 // ~> math:log10 -1.7 375 // ▶ (float64 NaN) 376 // ``` 377 378 //elvdoc:fn log2 379 // 380 // ```elvish 381 // math:log2 $number 382 // ``` 383 // 384 // Computes the base 2 logarithm of `$number`. Examples: 385 // 386 // ```elvish-transcript 387 // ~> math:log2 8 388 // ▶ (float64 3) 389 // ~> math:log2 -5.3 390 // ▶ (float64 NaN) 391 // ``` 392 393 //elvdoc:fn max 394 // 395 // ```elvish 396 // math:max $number... 397 // ``` 398 // 399 // Outputs the maximum number in the arguments. If there are no arguments, 400 // an exception is thrown. If any number is NaN then NaN is output. This 401 // function is exactness-preserving. 402 // 403 // Examples: 404 // 405 // ```elvish-transcript 406 // ~> math:max 3 5 2 407 // ▶ (num 5) 408 // ~> math:max (range 100) 409 // ▶ (num 99) 410 // ~> math:max 1/2 1/3 2/3 411 // ▶ (num 2/3) 412 // ``` 413 414 func max(rawNums ...vals.Num) (vals.Num, error) { 415 if len(rawNums) == 0 { 416 return nil, errs.ArityMismatch{ 417 What: "arguments here", ValidLow: 1, ValidHigh: -1, Actual: 0} 418 } 419 nums := vals.UnifyNums(rawNums, 0) 420 switch nums := nums.(type) { 421 case []int: 422 n := nums[0] 423 for i := 1; i < len(nums); i++ { 424 if n < nums[i] { 425 n = nums[i] 426 } 427 } 428 return n, nil 429 case []*big.Int: 430 n := nums[0] 431 for i := 1; i < len(nums); i++ { 432 if n.Cmp(nums[i]) < 0 { 433 n = nums[i] 434 } 435 } 436 return n, nil 437 case []*big.Rat: 438 n := nums[0] 439 for i := 1; i < len(nums); i++ { 440 if n.Cmp(nums[i]) < 0 { 441 n = nums[i] 442 } 443 } 444 return n, nil 445 case []float64: 446 n := nums[0] 447 for i := 1; i < len(nums); i++ { 448 n = math.Max(n, nums[i]) 449 } 450 return n, nil 451 default: 452 panic("unreachable") 453 } 454 } 455 456 //elvdoc:fn min 457 // 458 // ```elvish 459 // math:min $number... 460 // ``` 461 // 462 // Outputs the minimum number in the arguments. If there are no arguments 463 // an exception is thrown. If any number is NaN then NaN is output. This 464 // function is exactness-preserving. 465 // 466 // Examples: 467 // 468 // ```elvish-transcript 469 // ~> math:min 470 // Exception: arity mismatch: arguments here must be 1 or more values, but is 0 values 471 // [tty 17], line 1: math:min 472 // ~> math:min 3 5 2 473 // ▶ (num 2) 474 // ~> math:min 1/2 1/3 2/3 475 // ▶ (num 1/3) 476 // ``` 477 478 func min(rawNums ...vals.Num) (vals.Num, error) { 479 if len(rawNums) == 0 { 480 return nil, errs.ArityMismatch{ 481 What: "arguments here", ValidLow: 1, ValidHigh: -1, Actual: 0} 482 } 483 nums := vals.UnifyNums(rawNums, 0) 484 switch nums := nums.(type) { 485 case []int: 486 n := nums[0] 487 for i := 1; i < len(nums); i++ { 488 if n > nums[i] { 489 n = nums[i] 490 } 491 } 492 return n, nil 493 case []*big.Int: 494 n := nums[0] 495 for i := 1; i < len(nums); i++ { 496 if n.Cmp(nums[i]) > 0 { 497 n = nums[i] 498 } 499 } 500 return n, nil 501 case []*big.Rat: 502 n := nums[0] 503 for i := 1; i < len(nums); i++ { 504 if n.Cmp(nums[i]) > 0 { 505 n = nums[i] 506 } 507 } 508 return n, nil 509 case []float64: 510 n := nums[0] 511 for i := 1; i < len(nums); i++ { 512 n = math.Min(n, nums[i]) 513 } 514 return n, nil 515 default: 516 panic("unreachable") 517 } 518 } 519 520 //elvdoc:fn pow 521 // 522 // ```elvish 523 // math:pow $base $exponent 524 // ``` 525 // 526 // Output the result of raising `$base` to the power of `$exponent`. Examples: 527 // 528 // ```elvish-transcript 529 // ~> math:pow 3 2 530 // ▶ (float64 9) 531 // ~> math:pow -2 2 532 // ▶ (float64 4) 533 // ``` 534 // 535 // @cf math:pow10 536 537 //elvdoc:fn pow10 538 // 539 // ```elvish 540 // math:pow10 $exponent 541 // ``` 542 // 543 // Output the result of raising ten to the power of `$exponent` which must be 544 // an integer. Note that `$exponent > 308` results in +Inf and `$exponent < 545 // -323` results in zero. Examples: 546 // 547 // ```elvish-transcript 548 // ~> math:pow10 2 549 // ▶ (float64 100) 550 // ~> math:pow10 -3 551 // ▶ (float64 0.001) 552 // ``` 553 // 554 // @cf math:pow 555 556 //elvdoc:fn round 557 // 558 // ```elvish 559 // math:round $number 560 // ``` 561 // 562 // Outputs the nearest integer, rounding half away from zero. 563 // 564 // ```elvish-transcript 565 // ~> math:round -1.1 566 // ▶ (float64 -1) 567 // ~> math:round 2.5 568 // ▶ (float64 3) 569 // ``` 570 571 //elvdoc:fn round-to-even 572 // 573 // ```elvish 574 // math:round-to-even $number 575 // ``` 576 // 577 // Outputs the nearest integer, rounding ties to even. Examples: 578 // 579 // ```elvish-transcript 580 // ~> math:round-to-even -1.1 581 // ▶ (float64 -1) 582 // ~> math:round-to-even 2.5 583 // ▶ (float64 2) 584 // ``` 585 586 //elvdoc:fn sin 587 // 588 // ```elvish 589 // math:sin $number 590 // ``` 591 // 592 // Computes the sine of `$number` in units of radians (not degrees). Examples: 593 // 594 // ```elvish-transcript 595 // ~> math:sin 0 596 // ▶ (float64 0) 597 // ~> math:sin 3.14159265 598 // ▶ (float64 3.5897930298416118e-09) 599 // ``` 600 601 //elvdoc:fn sinh 602 // 603 // ```elvish 604 // math:sinh $number 605 // ``` 606 // 607 // Computes the hyperbolic sine of `$number`. Example: 608 // 609 // ```elvish-transcript 610 // ~> math:sinh 0 611 // ▶ (float64 0) 612 // ``` 613 614 //elvdoc:fn sqrt 615 // 616 // ```elvish 617 // math:sqrt $number 618 // ``` 619 // 620 // Computes the square-root of `$number`. Examples: 621 // 622 // ```elvish-transcript 623 // ~> math:sqrt 0 624 // ▶ (float64 0) 625 // ~> math:sqrt 4 626 // ▶ (float64 2) 627 // ~> math:sqrt -4 628 // ▶ (float64 NaN) 629 // ``` 630 631 //elvdoc:fn tan 632 // 633 // ```elvish 634 // math:tan $number 635 // ``` 636 // 637 // Computes the tangent of `$number` in units of radians (not degrees). Examples: 638 // 639 // ```elvish-transcript 640 // ~> math:tan 0 641 // ▶ (float64 0) 642 // ~> math:tan 3.14159265 643 // ▶ (float64 -0.0000000035897930298416118) 644 // ``` 645 646 //elvdoc:fn tanh 647 // 648 // ```elvish 649 // math:tanh $number 650 // ``` 651 // 652 // Computes the hyperbolic tangent of `$number`. Example: 653 // 654 // ```elvish-transcript 655 // ~> math:tanh 0 656 // ▶ (float64 0) 657 // ``` 658 659 //elvdoc:fn trunc 660 // 661 // ```elvish 662 // math:trunc $number 663 // ``` 664 // 665 // Outputs the integer portion of `$number`. 666 // 667 // ```elvish-transcript 668 // ~> math:trunc -1.1 669 // ▶ (float64 -1) 670 // ~> math:trunc 2.5 671 // ▶ (float64 2) 672 // ```