github.com/kolbycrouch/elvish@v0.14.1-0.20210614162631-215b9ac1c423/pkg/eval/builtin_fn_num.go (about) 1 package eval 2 3 import ( 4 "math" 5 "math/big" 6 "math/rand" 7 8 "src.elv.sh/pkg/eval/errs" 9 "src.elv.sh/pkg/eval/vals" 10 ) 11 12 // Numerical operations. 13 14 //elvdoc:fn rand 15 // 16 // ```elvish 17 // rand 18 // ``` 19 // 20 // Output a pseudo-random number in the interval [0, 1). Example: 21 // 22 // ```elvish-transcript 23 // ~> rand 24 // ▶ 0.17843564133528436 25 // ``` 26 27 func init() { 28 addBuiltinFns(map[string]interface{}{ 29 // Constructor 30 "float64": toFloat64, 31 "num": num, 32 "exact-num": exactNum, 33 34 // Comparison 35 "<": lt, 36 "<=": le, 37 "==": eqNum, 38 "!=": ne, 39 ">": gt, 40 ">=": ge, 41 42 // Arithmetic 43 "+": add, 44 "-": sub, 45 "*": mul, 46 // Also handles cd / 47 "/": slash, 48 "%": rem, 49 50 // Random 51 "rand": rand.Float64, 52 "randint": randint, 53 }) 54 } 55 56 //elvdoc:fn num 57 // 58 // ```elvish 59 // num $string-or-number 60 // ``` 61 // 62 // Constructs a [typed number](./language.html#number). 63 // 64 // If the argument is a string, this command outputs the typed number the 65 // argument represents, or raises an exception if the argument is not a valid 66 // representation of a number. If the argument is already a typed number, this 67 // command outputs it as is. 68 // 69 // This command is usually not needed for working with numbers; see the 70 // discussion of [numerical commands](#numerical-commands). 71 // 72 // Examples: 73 // 74 // ```elvish-transcript 75 // ~> num 10 76 // ▶ (num 10) 77 // ~> num 0x10 78 // ▶ (num 16) 79 // ~> num 1/12 80 // ▶ (num 1/12) 81 // ~> num 3.14 82 // ▶ (num 3.14) 83 // ~> num (num 10) 84 // ▶ (num 10) 85 // ``` 86 87 func num(n vals.Num) vals.Num { 88 // Conversion is actually handled in vals/conversion.go. 89 return n 90 } 91 92 //elvdoc:fn exact-num 93 // 94 // ```elvish 95 // exact-num $string-or-number 96 // ``` 97 // 98 // Coerces the argument to an exact number. If the argument is infinity or NaN, 99 // an exception is thrown. 100 // 101 // If the argument is a string, it is converted to a typed number first. If the 102 // argument is already an exact number, it is returned as is. 103 // 104 // Examples: 105 // 106 // ```elvish-transcript 107 // ~> exact-num (num 0.125) 108 // ▶ (num 1/8) 109 // ~> exact-num 0.125 110 // ▶ (num 1/8) 111 // ~> exact-num (num 1) 112 // ▶ (num 1) 113 // ``` 114 // 115 // Beware that seemingly simple fractions that can't be represented precisely in 116 // binary can result in the denominator being a very large power of 2: 117 // 118 // ```elvish-transcript 119 // ~> exact-num 0.1 120 // ▶ (num 3602879701896397/36028797018963968) 121 // ``` 122 123 func exactNum(n vals.Num) (vals.Num, error) { 124 if f, ok := n.(float64); ok { 125 r := new(big.Rat).SetFloat64(f) 126 if r == nil { 127 return nil, errs.BadValue{What: "argument here", 128 Valid: "finite float", Actual: vals.ToString(f)} 129 } 130 return r, nil 131 } 132 return n, nil 133 } 134 135 //elvdoc:fn float64 136 // 137 // ```elvish 138 // float64 $string-or-number 139 // ``` 140 // 141 // Constructs a floating-point number. 142 // 143 // This command is deprecated; use [`num`](#num) instead. 144 145 func toFloat64(f float64) float64 { 146 return f 147 } 148 149 //elvdoc:fn < <= == != > >= {#num-cmp} 150 // 151 // ```elvish 152 // < $number... # less 153 // <= $number... # less or equal 154 // == $number... # equal 155 // != $number... # not equal 156 // > $number... # greater 157 // >= $number... # greater or equal 158 // ``` 159 // 160 // Number comparisons. All of them accept an arbitrary number of arguments: 161 // 162 // 1. When given fewer than two arguments, all output `$true`. 163 // 164 // 2. When given two arguments, output whether the two arguments satisfy the named 165 // relationship. 166 // 167 // 3. When given more than two arguments, output whether every adjacent pair of 168 // numbers satisfy the named relationship. 169 // 170 // Examples: 171 // 172 // ```elvish-transcript 173 // ~> == 3 3.0 174 // ▶ $true 175 // ~> < 3 4 176 // ▶ $true 177 // ~> < 3 4 10 178 // ▶ $true 179 // ~> < 6 9 1 180 // ▶ $false 181 // ``` 182 // 183 // As a consequence of rule 3, the `!=` command outputs `$true` as long as any 184 // _adjacent_ pair of numbers are not equal, even if some numbers that are not 185 // adjacent are equal: 186 // 187 // ```elvish-transcript 188 // ~> != 5 5 4 189 // ▶ $false 190 // ~> != 5 6 5 191 // ▶ $true 192 // ``` 193 194 func lt(nums ...vals.Num) bool { 195 return chainCompare(nums, 196 func(a, b int) bool { return a < b }, 197 func(a, b *big.Int) bool { return a.Cmp(b) < 0 }, 198 func(a, b *big.Rat) bool { return a.Cmp(b) < 0 }, 199 func(a, b float64) bool { return a < b }) 200 201 } 202 203 func le(nums ...vals.Num) bool { 204 return chainCompare(nums, 205 func(a, b int) bool { return a <= b }, 206 func(a, b *big.Int) bool { return a.Cmp(b) <= 0 }, 207 func(a, b *big.Rat) bool { return a.Cmp(b) <= 0 }, 208 func(a, b float64) bool { return a <= b }) 209 } 210 211 func eqNum(nums ...vals.Num) bool { 212 return chainCompare(nums, 213 func(a, b int) bool { return a == b }, 214 func(a, b *big.Int) bool { return a.Cmp(b) == 0 }, 215 func(a, b *big.Rat) bool { return a.Cmp(b) == 0 }, 216 func(a, b float64) bool { return a == b }) 217 } 218 219 func ne(nums ...vals.Num) bool { 220 return chainCompare(nums, 221 func(a, b int) bool { return a != b }, 222 func(a, b *big.Int) bool { return a.Cmp(b) != 0 }, 223 func(a, b *big.Rat) bool { return a.Cmp(b) != 0 }, 224 func(a, b float64) bool { return a != b }) 225 } 226 227 func gt(nums ...vals.Num) bool { 228 return chainCompare(nums, 229 func(a, b int) bool { return a > b }, 230 func(a, b *big.Int) bool { return a.Cmp(b) > 0 }, 231 func(a, b *big.Rat) bool { return a.Cmp(b) > 0 }, 232 func(a, b float64) bool { return a > b }) 233 } 234 235 func ge(nums ...vals.Num) bool { 236 return chainCompare(nums, 237 func(a, b int) bool { return a >= b }, 238 func(a, b *big.Int) bool { return a.Cmp(b) >= 0 }, 239 func(a, b *big.Rat) bool { return a.Cmp(b) >= 0 }, 240 func(a, b float64) bool { return a >= b }) 241 } 242 243 func chainCompare(nums []vals.Num, 244 p1 func(a, b int) bool, p2 func(a, b *big.Int) bool, 245 p3 func(a, b *big.Rat) bool, p4 func(a, b float64) bool) bool { 246 247 for i := 0; i < len(nums)-1; i++ { 248 var r bool 249 a, b := vals.UnifyNums2(nums[i], nums[i+1], 0) 250 switch a := a.(type) { 251 case int: 252 r = p1(a, b.(int)) 253 case *big.Int: 254 r = p2(a, b.(*big.Int)) 255 case *big.Rat: 256 r = p3(a, b.(*big.Rat)) 257 case float64: 258 r = p4(a, b.(float64)) 259 } 260 if !r { 261 return false 262 } 263 } 264 return true 265 } 266 267 //elvdoc:fn + {#add} 268 // 269 // ```elvish 270 // + $num... 271 // ``` 272 // 273 // Outputs the sum of all arguments, or 0 when there are no arguments. 274 // 275 // This command is [exactness-preserving](#exactness-preserving). 276 // 277 // Examples: 278 // 279 // ```elvish-transcript 280 // ~> + 5 2 7 281 // ▶ (num 14) 282 // ~> + 1/2 1/3 1/4 283 // ▶ (num 13/12) 284 // ~> + 1/2 0.5 285 // ▶ (num 1.0) 286 // ``` 287 288 func add(rawNums ...vals.Num) vals.Num { 289 nums := vals.UnifyNums(rawNums, vals.BigInt) 290 switch nums := nums.(type) { 291 case []*big.Int: 292 acc := big.NewInt(0) 293 for _, num := range nums { 294 acc.Add(acc, num) 295 } 296 return vals.NormalizeBigInt(acc) 297 case []*big.Rat: 298 acc := big.NewRat(0, 1) 299 for _, num := range nums { 300 acc.Add(acc, num) 301 } 302 return vals.NormalizeBigRat(acc) 303 case []float64: 304 acc := float64(0) 305 for _, num := range nums { 306 acc += num 307 } 308 return acc 309 default: 310 panic("unreachable") 311 } 312 } 313 314 //elvdoc:fn - {#sub} 315 // 316 // ```elvish 317 // - $x-num $y-num... 318 // ``` 319 // 320 // Outputs the result of substracting from `$x-num` all the `$y-num`s, working 321 // from left to right. When no `$y-num` is given, outputs the negation of 322 // `$x-num` instead (in other words, `- $x-num` is equivalent to `- 0 $x-num`). 323 // 324 // This command is [exactness-preserving](#exactness-preserving). 325 // 326 // Examples: 327 // 328 // ```elvish-transcript 329 // ~> - 5 330 // ▶ (num -5) 331 // ~> - 5 2 332 // ▶ (num 3) 333 // ~> - 5 2 7 334 // ▶ (num -4) 335 // ~> - 1/2 1/3 336 // ▶ (num 1/6) 337 // ~> - 1/2 0.3 338 // ▶ (num 0.2) 339 // ~> - 10 340 // ▶ (num -10) 341 // ``` 342 343 func sub(rawNums ...vals.Num) (vals.Num, error) { 344 if len(rawNums) == 0 { 345 return nil, errs.ArityMismatch{ 346 What: "arguments here", 347 ValidLow: 1, ValidHigh: -1, Actual: 0, 348 } 349 } 350 351 nums := vals.UnifyNums(rawNums, vals.BigInt) 352 switch nums := nums.(type) { 353 case []*big.Int: 354 acc := &big.Int{} 355 if len(nums) == 1 { 356 acc.Neg(nums[0]) 357 return acc, nil 358 } 359 acc.Set(nums[0]) 360 for _, num := range nums[1:] { 361 acc.Sub(acc, num) 362 } 363 return acc, nil 364 case []*big.Rat: 365 acc := &big.Rat{} 366 if len(nums) == 1 { 367 acc.Neg(nums[0]) 368 return acc, nil 369 } 370 acc.Set(nums[0]) 371 for _, num := range nums[1:] { 372 acc.Sub(acc, num) 373 } 374 return acc, nil 375 case []float64: 376 if len(nums) == 1 { 377 return -nums[0], nil 378 } 379 acc := nums[0] 380 for _, num := range nums[1:] { 381 acc -= num 382 } 383 return acc, nil 384 default: 385 panic("unreachable") 386 } 387 } 388 389 //elvdoc:fn * {#mul} 390 // 391 // ```elvish 392 // * $num... 393 // ``` 394 // 395 // Outputs the product of all arguments, or 1 when there are no arguments. 396 // 397 // This command is [exactness-preserving](#exactness-preserving). Additionally, 398 // when any argument is exact 0 and no other argument is a floating-point 399 // infinity, the result is exact 0. 400 // 401 // Examples: 402 // 403 // ```elvish-transcript 404 // ~> * 2 5 7 405 // ▶ (num 70) 406 // ~> * 1/2 0.5 407 // ▶ (num 0.25) 408 // ~> * 0 0.5 409 // ▶ (num 0) 410 // ``` 411 412 func mul(rawNums ...vals.Num) vals.Num { 413 hasExact0 := false 414 hasInf := false 415 for _, num := range rawNums { 416 if num == 0 { 417 hasExact0 = true 418 } 419 if f, ok := num.(float64); ok && math.IsInf(f, 0) { 420 hasInf = true 421 break 422 } 423 } 424 if hasExact0 && !hasInf { 425 return 0 426 } 427 428 nums := vals.UnifyNums(rawNums, vals.BigInt) 429 switch nums := nums.(type) { 430 case []*big.Int: 431 acc := big.NewInt(1) 432 for _, num := range nums { 433 acc.Mul(acc, num) 434 } 435 return vals.NormalizeBigInt(acc) 436 case []*big.Rat: 437 acc := big.NewRat(1, 1) 438 for _, num := range nums { 439 acc.Mul(acc, num) 440 } 441 return vals.NormalizeBigRat(acc) 442 case []float64: 443 acc := float64(1) 444 for _, num := range nums { 445 acc *= num 446 } 447 return acc 448 default: 449 panic("unreachable") 450 } 451 } 452 453 //elvdoc:fn / {#div} 454 // 455 // ```elvish 456 // / $x-num $y-num... 457 // ``` 458 // 459 // Outputs the result of dividing `$x-num` with all the `$y-num`s, working from 460 // left to right. When no `$y-num` is given, outputs the reciprocal of `$x-num` 461 // instead (in other words, `/ $y-num` is equivalent to `/ 1 $y-num`). 462 // 463 // Dividing by exact 0 raises an exception. Dividing by inexact 0 results with 464 // either infinity or NaN according to floating-point semantics. 465 // 466 // This command is [exactness-preserving](#exactness-preserving). Additionally, 467 // when `$x-num` is exact 0 and no `$y-num` is exact 0, the result is exact 0. 468 // 469 // Examples: 470 // 471 // ```elvish-transcript 472 // ~> / 2 473 // ▶ (num 1/2) 474 // ~> / 2.0 475 // ▶ (num 0.5) 476 // ~> / 10 5 477 // ▶ (num 2) 478 // ~> / 2 5 479 // ▶ (num 2/5) 480 // ~> / 2 5 7 481 // ▶ (num 2/35) 482 // ~> / 0 1.0 483 // ▶ (num 0) 484 // ~> / 2 0 485 // Exception: bad value: divisor must be number other than exact 0, but is exact 0 486 // [tty 6], line 1: / 2 0 487 // ~> / 2 0.0 488 // ▶ (num +Inf) 489 // ``` 490 // 491 // When given no argument, this command is equivalent to `cd /`, due to the 492 // implicit cd feature. (The implicit cd feature will probably change to avoid 493 // this oddity). 494 495 func slash(fm *Frame, args ...vals.Num) error { 496 if len(args) == 0 { 497 // cd / 498 return fm.Evaler.Chdir("/") 499 } 500 // Division 501 result, err := div(args...) 502 if err == nil { 503 fm.OutputChan() <- vals.FromGo(result) 504 } 505 return err 506 } 507 508 // ErrDivideByZero is thrown when attempting to divide by zero. 509 var ErrDivideByZero = errs.BadValue{ 510 What: "divisor", Valid: "number other than exact 0", Actual: "exact 0"} 511 512 func div(rawNums ...vals.Num) (vals.Num, error) { 513 for _, num := range rawNums[1:] { 514 if num == 0 { 515 return nil, ErrDivideByZero 516 } 517 } 518 if rawNums[0] == 0 { 519 return 0, nil 520 } 521 nums := vals.UnifyNums(rawNums, vals.BigRat) 522 switch nums := nums.(type) { 523 case []*big.Rat: 524 acc := &big.Rat{} 525 acc.Set(nums[0]) 526 if len(nums) == 1 { 527 acc.Inv(acc) 528 return acc, nil 529 } 530 for _, num := range nums[1:] { 531 acc.Quo(acc, num) 532 } 533 return acc, nil 534 case []float64: 535 acc := nums[0] 536 if len(nums) == 1 { 537 return 1 / acc, nil 538 } 539 for _, num := range nums[1:] { 540 acc /= num 541 } 542 return acc, nil 543 default: 544 panic("unreachable") 545 } 546 } 547 548 //elvdoc:fn % {#rem} 549 // 550 // ```elvish 551 // % $x $y 552 // ``` 553 // 554 // Output the remainder after dividing `$x` by `$y`. The result has the same 555 // sign as `$x`. Both must be integers that can represented in a machine word 556 // (this limit may be lifted in future). 557 // 558 // Examples: 559 // 560 // ```elvish-transcript 561 // ~> % 10 3 562 // ▶ 1 563 // ~> % -10 3 564 // ▶ -1 565 // ~> % 10 -3 566 // ▶ 1 567 // ``` 568 569 func rem(a, b int) (int, error) { 570 // TODO: Support other number types 571 if b == 0 { 572 return 0, ErrDivideByZero 573 } 574 return a % b, nil 575 } 576 577 //elvdoc:fn randint 578 // 579 // ```elvish 580 // randint $low $high 581 // ``` 582 // 583 // Output a pseudo-random integer in the interval [$low, $high). Example: 584 // 585 // ```elvish-transcript 586 // ~> # Emulate dice 587 // randint 1 7 588 // ▶ 6 589 // ``` 590 591 func randint(low, high int) (int, error) { 592 if low >= high { 593 return 0, ErrArgs 594 } 595 return low + rand.Intn(high-low), nil 596 }