github.com/markusbkk/elvish@v0.0.0-20231204143114-91dc52438621/pkg/eval/builtin_fn_num.go (about) 1 package eval 2 3 import ( 4 "fmt" 5 "math" 6 "math/big" 7 "math/rand" 8 "strconv" 9 10 "github.com/markusbkk/elvish/pkg/eval/errs" 11 "github.com/markusbkk/elvish/pkg/eval/vals" 12 ) 13 14 // Numerical operations. 15 16 //elvdoc:fn rand 17 // 18 // ```elvish 19 // rand 20 // ``` 21 // 22 // Output a pseudo-random number in the interval [0, 1). Example: 23 // 24 // ```elvish-transcript 25 // ~> rand 26 // ▶ 0.17843564133528436 27 // ``` 28 29 func init() { 30 addBuiltinFns(map[string]interface{}{ 31 // Constructor 32 "float64": toFloat64, 33 "num": num, 34 "exact-num": exactNum, 35 36 // Comparison 37 "<": lt, 38 "<=": le, 39 "==": eqNum, 40 "!=": ne, 41 ">": gt, 42 ">=": ge, 43 44 // Arithmetic 45 "+": add, 46 "-": sub, 47 "*": mul, 48 // Also handles cd / 49 "/": slash, 50 "%": rem, 51 52 // Random 53 "rand": rand.Float64, 54 "randint": randint, 55 56 "range": rangeFn, 57 }) 58 } 59 60 //elvdoc:fn num 61 // 62 // ```elvish 63 // num $string-or-number 64 // ``` 65 // 66 // Constructs a [typed number](./language.html#number). 67 // 68 // If the argument is a string, this command outputs the typed number the 69 // argument represents, or raises an exception if the argument is not a valid 70 // representation of a number. If the argument is already a typed number, this 71 // command outputs it as is. 72 // 73 // This command is usually not needed for working with numbers; see the 74 // discussion of [numeric commands](#numeric-commands). 75 // 76 // Examples: 77 // 78 // ```elvish-transcript 79 // ~> num 10 80 // ▶ (num 10) 81 // ~> num 0x10 82 // ▶ (num 16) 83 // ~> num 1/12 84 // ▶ (num 1/12) 85 // ~> num 3.14 86 // ▶ (num 3.14) 87 // ~> num (num 10) 88 // ▶ (num 10) 89 // ``` 90 91 func num(n vals.Num) vals.Num { 92 // Conversion is actually handled in vals/conversion.go. 93 return n 94 } 95 96 //elvdoc:fn exact-num 97 // 98 // ```elvish 99 // exact-num $string-or-number 100 // ``` 101 // 102 // Coerces the argument to an exact number. If the argument is infinity or NaN, 103 // an exception is thrown. 104 // 105 // If the argument is a string, it is converted to a typed number first. If the 106 // argument is already an exact number, it is returned as is. 107 // 108 // Examples: 109 // 110 // ```elvish-transcript 111 // ~> exact-num (num 0.125) 112 // ▶ (num 1/8) 113 // ~> exact-num 0.125 114 // ▶ (num 1/8) 115 // ~> exact-num (num 1) 116 // ▶ (num 1) 117 // ``` 118 // 119 // Beware that seemingly simple fractions that can't be represented precisely in 120 // binary can result in the denominator being a very large power of 2: 121 // 122 // ```elvish-transcript 123 // ~> exact-num 0.1 124 // ▶ (num 3602879701896397/36028797018963968) 125 // ``` 126 127 func exactNum(n vals.Num) (vals.Num, error) { 128 if f, ok := n.(float64); ok { 129 r := new(big.Rat).SetFloat64(f) 130 if r == nil { 131 return nil, errs.BadValue{What: "argument here", 132 Valid: "finite float", Actual: vals.ToString(f)} 133 } 134 return r, nil 135 } 136 return n, nil 137 } 138 139 //elvdoc:fn float64 140 // 141 // ```elvish 142 // float64 $string-or-number 143 // ``` 144 // 145 // Constructs a floating-point number. 146 // 147 // This command is deprecated; use [`num`](#num) instead. 148 149 func toFloat64(f float64) float64 { 150 return f 151 } 152 153 //elvdoc:fn < <= == != > >= {#num-cmp} 154 // 155 // ```elvish 156 // < $number... # less 157 // <= $number... # less or equal 158 // == $number... # equal 159 // != $number... # not equal 160 // > $number... # greater 161 // >= $number... # greater or equal 162 // ``` 163 // 164 // Number comparisons. All of them accept an arbitrary number of arguments: 165 // 166 // 1. When given fewer than two arguments, all output `$true`. 167 // 168 // 2. When given two arguments, output whether the two arguments satisfy the named 169 // relationship. 170 // 171 // 3. When given more than two arguments, output whether every adjacent pair of 172 // numbers satisfy the named relationship. 173 // 174 // Examples: 175 // 176 // ```elvish-transcript 177 // ~> == 3 3.0 178 // ▶ $true 179 // ~> < 3 4 180 // ▶ $true 181 // ~> < 3 4 10 182 // ▶ $true 183 // ~> < 6 9 1 184 // ▶ $false 185 // ``` 186 // 187 // As a consequence of rule 3, the `!=` command outputs `$true` as long as any 188 // _adjacent_ pair of numbers are not equal, even if some numbers that are not 189 // adjacent are equal: 190 // 191 // ```elvish-transcript 192 // ~> != 5 5 4 193 // ▶ $false 194 // ~> != 5 6 5 195 // ▶ $true 196 // ``` 197 198 func lt(nums ...vals.Num) bool { 199 return chainCompare(nums, 200 func(a, b int) bool { return a < b }, 201 func(a, b *big.Int) bool { return a.Cmp(b) < 0 }, 202 func(a, b *big.Rat) bool { return a.Cmp(b) < 0 }, 203 func(a, b float64) bool { return a < b }) 204 205 } 206 207 func le(nums ...vals.Num) bool { 208 return chainCompare(nums, 209 func(a, b int) bool { return a <= b }, 210 func(a, b *big.Int) bool { return a.Cmp(b) <= 0 }, 211 func(a, b *big.Rat) bool { return a.Cmp(b) <= 0 }, 212 func(a, b float64) bool { return a <= b }) 213 } 214 215 func eqNum(nums ...vals.Num) bool { 216 return chainCompare(nums, 217 func(a, b int) bool { return a == b }, 218 func(a, b *big.Int) bool { return a.Cmp(b) == 0 }, 219 func(a, b *big.Rat) bool { return a.Cmp(b) == 0 }, 220 func(a, b float64) bool { return a == b }) 221 } 222 223 func ne(nums ...vals.Num) bool { 224 return chainCompare(nums, 225 func(a, b int) bool { return a != b }, 226 func(a, b *big.Int) bool { return a.Cmp(b) != 0 }, 227 func(a, b *big.Rat) bool { return a.Cmp(b) != 0 }, 228 func(a, b float64) bool { return a != b }) 229 } 230 231 func gt(nums ...vals.Num) bool { 232 return chainCompare(nums, 233 func(a, b int) bool { return a > b }, 234 func(a, b *big.Int) bool { return a.Cmp(b) > 0 }, 235 func(a, b *big.Rat) bool { return a.Cmp(b) > 0 }, 236 func(a, b float64) bool { return a > b }) 237 } 238 239 func ge(nums ...vals.Num) bool { 240 return chainCompare(nums, 241 func(a, b int) bool { return a >= b }, 242 func(a, b *big.Int) bool { return a.Cmp(b) >= 0 }, 243 func(a, b *big.Rat) bool { return a.Cmp(b) >= 0 }, 244 func(a, b float64) bool { return a >= b }) 245 } 246 247 func chainCompare(nums []vals.Num, 248 p1 func(a, b int) bool, p2 func(a, b *big.Int) bool, 249 p3 func(a, b *big.Rat) bool, p4 func(a, b float64) bool) bool { 250 251 for i := 0; i < len(nums)-1; i++ { 252 var r bool 253 a, b := vals.UnifyNums2(nums[i], nums[i+1], 0) 254 switch a := a.(type) { 255 case int: 256 r = p1(a, b.(int)) 257 case *big.Int: 258 r = p2(a, b.(*big.Int)) 259 case *big.Rat: 260 r = p3(a, b.(*big.Rat)) 261 case float64: 262 r = p4(a, b.(float64)) 263 } 264 if !r { 265 return false 266 } 267 } 268 return true 269 } 270 271 //elvdoc:fn + {#add} 272 // 273 // ```elvish 274 // + $num... 275 // ``` 276 // 277 // Outputs the sum of all arguments, or 0 when there are no arguments. 278 // 279 // This command is [exactness-preserving](#exactness-preserving). 280 // 281 // Examples: 282 // 283 // ```elvish-transcript 284 // ~> + 5 2 7 285 // ▶ (num 14) 286 // ~> + 1/2 1/3 1/4 287 // ▶ (num 13/12) 288 // ~> + 1/2 0.5 289 // ▶ (num 1.0) 290 // ``` 291 292 func add(rawNums ...vals.Num) vals.Num { 293 nums := vals.UnifyNums(rawNums, vals.BigInt) 294 switch nums := nums.(type) { 295 case []*big.Int: 296 acc := big.NewInt(0) 297 for _, num := range nums { 298 acc.Add(acc, num) 299 } 300 return vals.NormalizeBigInt(acc) 301 case []*big.Rat: 302 acc := big.NewRat(0, 1) 303 for _, num := range nums { 304 acc.Add(acc, num) 305 } 306 return vals.NormalizeBigRat(acc) 307 case []float64: 308 acc := float64(0) 309 for _, num := range nums { 310 acc += num 311 } 312 return acc 313 default: 314 panic("unreachable") 315 } 316 } 317 318 //elvdoc:fn - {#sub} 319 // 320 // ```elvish 321 // - $x-num $y-num... 322 // ``` 323 // 324 // Outputs the result of subtracting from `$x-num` all the `$y-num`s, working 325 // from left to right. When no `$y-num` is given, outputs the negation of 326 // `$x-num` instead (in other words, `- $x-num` is equivalent to `- 0 $x-num`). 327 // 328 // This command is [exactness-preserving](#exactness-preserving). 329 // 330 // Examples: 331 // 332 // ```elvish-transcript 333 // ~> - 5 334 // ▶ (num -5) 335 // ~> - 5 2 336 // ▶ (num 3) 337 // ~> - 5 2 7 338 // ▶ (num -4) 339 // ~> - 1/2 1/3 340 // ▶ (num 1/6) 341 // ~> - 1/2 0.3 342 // ▶ (num 0.2) 343 // ~> - 10 344 // ▶ (num -10) 345 // ``` 346 347 func sub(rawNums ...vals.Num) (vals.Num, error) { 348 if len(rawNums) == 0 { 349 return nil, errs.ArityMismatch{What: "arguments", ValidLow: 1, ValidHigh: -1, Actual: 0} 350 } 351 352 nums := vals.UnifyNums(rawNums, vals.BigInt) 353 switch nums := nums.(type) { 354 case []*big.Int: 355 acc := &big.Int{} 356 if len(nums) == 1 { 357 acc.Neg(nums[0]) 358 return acc, nil 359 } 360 acc.Set(nums[0]) 361 for _, num := range nums[1:] { 362 acc.Sub(acc, num) 363 } 364 return acc, nil 365 case []*big.Rat: 366 acc := &big.Rat{} 367 if len(nums) == 1 { 368 acc.Neg(nums[0]) 369 return acc, nil 370 } 371 acc.Set(nums[0]) 372 for _, num := range nums[1:] { 373 acc.Sub(acc, num) 374 } 375 return acc, nil 376 case []float64: 377 if len(nums) == 1 { 378 return -nums[0], nil 379 } 380 acc := nums[0] 381 for _, num := range nums[1:] { 382 acc -= num 383 } 384 return acc, nil 385 default: 386 panic("unreachable") 387 } 388 } 389 390 //elvdoc:fn * {#mul} 391 // 392 // ```elvish 393 // * $num... 394 // ``` 395 // 396 // Outputs the product of all arguments, or 1 when there are no arguments. 397 // 398 // This command is [exactness-preserving](#exactness-preserving). Additionally, 399 // when any argument is exact 0 and no other argument is a floating-point 400 // infinity, the result is exact 0. 401 // 402 // Examples: 403 // 404 // ```elvish-transcript 405 // ~> * 2 5 7 406 // ▶ (num 70) 407 // ~> * 1/2 0.5 408 // ▶ (num 0.25) 409 // ~> * 0 0.5 410 // ▶ (num 0) 411 // ``` 412 413 func mul(rawNums ...vals.Num) vals.Num { 414 hasExact0 := false 415 hasInf := false 416 for _, num := range rawNums { 417 if num == 0 { 418 hasExact0 = true 419 } 420 if f, ok := num.(float64); ok && math.IsInf(f, 0) { 421 hasInf = true 422 break 423 } 424 } 425 if hasExact0 && !hasInf { 426 return 0 427 } 428 429 nums := vals.UnifyNums(rawNums, vals.BigInt) 430 switch nums := nums.(type) { 431 case []*big.Int: 432 acc := big.NewInt(1) 433 for _, num := range nums { 434 acc.Mul(acc, num) 435 } 436 return vals.NormalizeBigInt(acc) 437 case []*big.Rat: 438 acc := big.NewRat(1, 1) 439 for _, num := range nums { 440 acc.Mul(acc, num) 441 } 442 return vals.NormalizeBigRat(acc) 443 case []float64: 444 acc := float64(1) 445 for _, num := range nums { 446 acc *= num 447 } 448 return acc 449 default: 450 panic("unreachable") 451 } 452 } 453 454 //elvdoc:fn / {#div} 455 // 456 // ```elvish 457 // / $x-num $y-num... 458 // ``` 459 // 460 // Outputs the result of dividing `$x-num` with all the `$y-num`s, working from 461 // left to right. When no `$y-num` is given, outputs the reciprocal of `$x-num` 462 // instead (in other words, `/ $y-num` is equivalent to `/ 1 $y-num`). 463 // 464 // Dividing by exact 0 raises an exception. Dividing by inexact 0 results with 465 // either infinity or NaN according to floating-point semantics. 466 // 467 // This command is [exactness-preserving](#exactness-preserving). Additionally, 468 // when `$x-num` is exact 0 and no `$y-num` is exact 0, the result is exact 0. 469 // 470 // Examples: 471 // 472 // ```elvish-transcript 473 // ~> / 2 474 // ▶ (num 1/2) 475 // ~> / 2.0 476 // ▶ (num 0.5) 477 // ~> / 10 5 478 // ▶ (num 2) 479 // ~> / 2 5 480 // ▶ (num 2/5) 481 // ~> / 2 5 7 482 // ▶ (num 2/35) 483 // ~> / 0 1.0 484 // ▶ (num 0) 485 // ~> / 2 0 486 // Exception: bad value: divisor must be number other than exact 0, but is exact 0 487 // [tty 6], line 1: / 2 0 488 // ~> / 2 0.0 489 // ▶ (num +Inf) 490 // ``` 491 // 492 // When given no argument, this command is equivalent to `cd /`, due to the 493 // implicit cd feature. (The implicit cd feature will probably change to avoid 494 // this oddity). 495 496 func slash(fm *Frame, args ...vals.Num) error { 497 if len(args) == 0 { 498 // cd / 499 return fm.Evaler.Chdir("/") 500 } 501 // Division 502 result, err := div(args...) 503 if err != nil { 504 return err 505 } 506 return fm.ValueOutput().Put(vals.FromGo(result)) 507 } 508 509 // ErrDivideByZero is thrown when attempting to divide by zero. 510 var ErrDivideByZero = errs.BadValue{ 511 What: "divisor", Valid: "number other than exact 0", Actual: "exact 0"} 512 513 func div(rawNums ...vals.Num) (vals.Num, error) { 514 for _, num := range rawNums[1:] { 515 if num == 0 { 516 return nil, ErrDivideByZero 517 } 518 } 519 if rawNums[0] == 0 { 520 return 0, nil 521 } 522 nums := vals.UnifyNums(rawNums, vals.BigRat) 523 switch nums := nums.(type) { 524 case []*big.Rat: 525 acc := &big.Rat{} 526 acc.Set(nums[0]) 527 if len(nums) == 1 { 528 acc.Inv(acc) 529 return acc, nil 530 } 531 for _, num := range nums[1:] { 532 acc.Quo(acc, num) 533 } 534 return acc, nil 535 case []float64: 536 acc := nums[0] 537 if len(nums) == 1 { 538 return 1 / acc, nil 539 } 540 for _, num := range nums[1:] { 541 acc /= num 542 } 543 return acc, nil 544 default: 545 panic("unreachable") 546 } 547 } 548 549 //elvdoc:fn % {#rem} 550 // 551 // ```elvish 552 // % $x $y 553 // ``` 554 // 555 // Output the remainder after dividing `$x` by `$y`. The result has the same 556 // sign as `$x`. Both must be integers that can represented in a machine word 557 // (this limit may be lifted in future). 558 // 559 // Examples: 560 // 561 // ```elvish-transcript 562 // ~> % 10 3 563 // ▶ 1 564 // ~> % -10 3 565 // ▶ -1 566 // ~> % 10 -3 567 // ▶ 1 568 // ``` 569 570 func rem(a, b int) (int, error) { 571 // TODO: Support other number types 572 if b == 0 { 573 return 0, ErrDivideByZero 574 } 575 return a % b, nil 576 } 577 578 //elvdoc:fn randint 579 // 580 // ```elvish 581 // randint $low? $high 582 // ``` 583 // 584 // Output a pseudo-random integer N such that `$low <= N < $high`. If not given, 585 // `$low` defaults to 0. Examples: 586 // 587 // ```elvish-transcript 588 // ~> # Emulate dice 589 // randint 1 7 590 // ▶ 6 591 // ``` 592 593 func randint(args ...int) (int, error) { 594 var low, high int 595 switch len(args) { 596 case 1: 597 low, high = 0, args[0] 598 case 2: 599 low, high = args[0], args[1] 600 default: 601 return -1, errs.ArityMismatch{What: "arguments", 602 ValidLow: 1, ValidHigh: 2, Actual: len(args)} 603 } 604 if high <= low { 605 return 0, errs.BadValue{What: "high value", 606 Valid: fmt.Sprint("larger than ", low), Actual: strconv.Itoa(high)} 607 } 608 return low + rand.Intn(high-low), nil 609 } 610 611 //elvdoc:fn range 612 // 613 // ```elvish 614 // range &step $start=0 $end 615 // ``` 616 // 617 // Outputs numbers, starting from `$start` and ending before `$end`, using 618 // `&step` as the increment. 619 // 620 // - If `$start` <= `$end`, `&step` defaults to 1, and `range` outputs values as 621 // long as they are smaller than `$end`. An exception is thrown if `&step` is 622 // given a negative value. 623 // 624 // - If `$start` > `$end`, `&step` defaults to -1, and `range` outputs values as 625 // long as they are greater than `$end`. An exception is thrown if `&step` is 626 // given a positive value. 627 // 628 // As a special case, if the outputs are floating point numbers, `range` also 629 // terminates if the values stop changing. 630 // 631 // This command is [exactness-preserving](#exactness-preserving). 632 // 633 // Examples: 634 // 635 // ```elvish-transcript 636 // ~> range 4 637 // ▶ (num 0) 638 // ▶ (num 1) 639 // ▶ (num 2) 640 // ▶ (num 3) 641 // ~> range 4 0 642 // ▶ (num 4) 643 // ▶ (num 3) 644 // ▶ (num 2) 645 // ▶ (num 1) 646 // ~> range -3 3 &step=2 647 // ▶ (num -3) 648 // ▶ (num -1) 649 // ▶ (num 1) 650 // ~> range 3 -3 &step=-2 651 // ▶ (num 3) 652 // ▶ (num 1) 653 // ▶ (num -1) 654 // ~> range (- (math:pow 2 53) 1) +inf 655 // ▶ (num 9007199254740991.0) 656 // ▶ (num 9007199254740992.0) 657 // ``` 658 // 659 // When using floating-point numbers, beware that numerical errors can result in 660 // an incorrect number of outputs: 661 // 662 // ```elvish-transcript 663 // ~> range 0.9 &step=0.3 664 // ▶ (num 0.0) 665 // ▶ (num 0.3) 666 // ▶ (num 0.6) 667 // ▶ (num 0.8999999999999999) 668 // ``` 669 // 670 // Avoid this problem by using exact rationals: 671 // 672 // ```elvish-transcript 673 // ~> range 9/10 &step=3/10 674 // ▶ (num 0) 675 // ▶ (num 3/10) 676 // ▶ (num 3/5) 677 // ``` 678 // 679 // One usage of this command is to execute something a fixed number of times by 680 // combining with [each](#each): 681 // 682 // ```elvish-transcript 683 // ~> range 3 | each {|_| echo foo } 684 // foo 685 // foo 686 // foo 687 // ``` 688 // 689 // Etymology: 690 // [Python](https://docs.python.org/3/library/functions.html#func-range). 691 692 type rangeOpts struct{ Step vals.Num } 693 694 // TODO: The default value can only be used implicitly; passing "range 695 // &step=nil" results in an error. 696 func (o *rangeOpts) SetDefaultOptions() { o.Step = nil } 697 698 func rangeFn(fm *Frame, opts rangeOpts, args ...vals.Num) error { 699 var rawNums []vals.Num 700 switch len(args) { 701 case 1: 702 rawNums = []vals.Num{0, args[0]} 703 case 2: 704 rawNums = []vals.Num{args[0], args[1]} 705 default: 706 return errs.ArityMismatch{What: "arguments", ValidLow: 1, ValidHigh: 2, Actual: len(args)} 707 } 708 if opts.Step != nil { 709 rawNums = append(rawNums, opts.Step) 710 } 711 nums := vals.UnifyNums(rawNums, vals.Int) 712 713 out := fm.ValueOutput() 714 715 switch nums := nums.(type) { 716 case []int: 717 return rangeInt(nums, out) 718 case []*big.Int: 719 return rangeBigInt(nums, out) 720 case []*big.Rat: 721 return rangeBitRat(nums, out) 722 case []float64: 723 return rangeFloat64(nums, out) 724 default: 725 panic("unreachable") 726 } 727 } 728 729 func rangeInt(nums []int, out ValueOutput) error { 730 start, end := nums[0], nums[1] 731 var step int 732 if start <= end { 733 if len(nums) == 3 { 734 step = nums[2] 735 if step <= 0 { 736 return errs.BadValue{ 737 What: "step", Valid: "positive", Actual: vals.ToString(step)} 738 } 739 } else { 740 step = 1 741 } 742 for cur := start; cur < end; cur += step { 743 err := out.Put(vals.FromGo(cur)) 744 if err != nil { 745 return err 746 } 747 if cur+step <= cur { 748 break 749 } 750 } 751 } else { 752 if len(nums) == 3 { 753 step = nums[2] 754 if step >= 0 { 755 return errs.BadValue{ 756 What: "step", Valid: "negative", Actual: vals.ToString(step)} 757 } 758 } else { 759 step = -1 760 } 761 for cur := start; cur > end; cur += step { 762 err := out.Put(vals.FromGo(cur)) 763 if err != nil { 764 return err 765 } 766 if cur+step >= cur { 767 break 768 } 769 } 770 } 771 return nil 772 } 773 774 // TODO: Use type parameters to deduplicate this with rangeInt when Elvish 775 // requires Go 1.18. 776 func rangeFloat64(nums []float64, out ValueOutput) error { 777 start, end := nums[0], nums[1] 778 var step float64 779 if start <= end { 780 if len(nums) == 3 { 781 step = nums[2] 782 if step <= 0 { 783 return errs.BadValue{ 784 What: "step", Valid: "positive", Actual: vals.ToString(step)} 785 } 786 } else { 787 step = 1 788 } 789 for cur := start; cur < end; cur += step { 790 err := out.Put(vals.FromGo(cur)) 791 if err != nil { 792 return err 793 } 794 if cur+step <= cur { 795 break 796 } 797 } 798 } else { 799 if len(nums) == 3 { 800 step = nums[2] 801 if step >= 0 { 802 return errs.BadValue{ 803 What: "step", Valid: "negative", Actual: vals.ToString(step)} 804 } 805 } else { 806 step = -1 807 } 808 for cur := start; cur > end; cur += step { 809 err := out.Put(vals.FromGo(cur)) 810 if err != nil { 811 return err 812 } 813 if cur+step >= cur { 814 break 815 } 816 } 817 } 818 return nil 819 } 820 821 var ( 822 bigInt1 = big.NewInt(1) 823 bigIntNeg1 = big.NewInt(-1) 824 ) 825 826 func rangeBigInt(nums []*big.Int, out ValueOutput) error { 827 start, end := nums[0], nums[1] 828 var step *big.Int 829 if start.Cmp(end) <= 0 { 830 if len(nums) == 3 { 831 step = nums[2] 832 if step.Sign() <= 0 { 833 return errs.BadValue{ 834 What: "step", Valid: "positive", Actual: vals.ToString(step)} 835 } 836 } else { 837 step = bigInt1 838 } 839 var cur, next *big.Int 840 for cur = start; cur.Cmp(end) < 0; cur = next { 841 err := out.Put(vals.FromGo(cur)) 842 if err != nil { 843 return err 844 } 845 next = &big.Int{} 846 next.Add(cur, step) 847 cur = next 848 } 849 } else { 850 if len(nums) == 3 { 851 step = nums[2] 852 if step.Sign() >= 0 { 853 return errs.BadValue{ 854 What: "step", Valid: "negative", Actual: vals.ToString(step)} 855 } 856 } else { 857 step = bigIntNeg1 858 } 859 var cur, next *big.Int 860 for cur = start; cur.Cmp(end) > 0; cur = next { 861 err := out.Put(vals.FromGo(cur)) 862 if err != nil { 863 return err 864 } 865 next = &big.Int{} 866 next.Add(cur, step) 867 cur = next 868 } 869 } 870 return nil 871 } 872 873 var ( 874 bigRat1 = big.NewRat(1, 1) 875 bigRatNeg1 = big.NewRat(-1, 1) 876 ) 877 878 // TODO: Use type parameters to deduplicate this with rangeBitInt when Elvish 879 // requires Go 1.18. 880 func rangeBitRat(nums []*big.Rat, out ValueOutput) error { 881 start, end := nums[0], nums[1] 882 var step *big.Rat 883 if start.Cmp(end) <= 0 { 884 if len(nums) == 3 { 885 step = nums[2] 886 if step.Sign() <= 0 { 887 return errs.BadValue{ 888 What: "step", Valid: "positive", Actual: vals.ToString(step)} 889 } 890 } else { 891 step = bigRat1 892 } 893 var cur, next *big.Rat 894 for cur = start; cur.Cmp(end) < 0; cur = next { 895 err := out.Put(vals.FromGo(cur)) 896 if err != nil { 897 return err 898 } 899 next = &big.Rat{} 900 next.Add(cur, step) 901 cur = next 902 } 903 } else { 904 if len(nums) == 3 { 905 step = nums[2] 906 if step.Sign() >= 0 { 907 return errs.BadValue{ 908 What: "step", Valid: "negative", Actual: vals.ToString(step)} 909 } 910 } else { 911 step = bigRatNeg1 912 } 913 var cur, next *big.Rat 914 for cur = start; cur.Cmp(end) > 0; cur = next { 915 err := out.Put(vals.FromGo(cur)) 916 if err != nil { 917 return err 918 } 919 next = &big.Rat{} 920 next.Add(cur, step) 921 cur = next 922 } 923 } 924 return nil 925 }