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 &lt; &lt;= == != &gt; &gt;= {#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  }