Skip to content

Commit

Permalink
pkg/eval: Refactor the argument parsing logic of randint.
Browse files Browse the repository at this point in the history
  • Loading branch information
xiaq committed Jan 5, 2025
1 parent 5aafdd5 commit 1ecd68d
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 47 deletions.
52 changes: 24 additions & 28 deletions pkg/eval/builtin_fn_num.go
Original file line number Diff line number Diff line change
Expand Up @@ -348,43 +348,39 @@ func randint(args ...vals.Num) (vals.Num, error) {
return -1, errs.ArityMismatch{What: "arguments",
ValidLow: 1, ValidHigh: 2, Actual: len(args)}
}
allInt := true
for _, arg := range args {
if err := checkExactIntArg(arg); err != nil {
return nil, err
}
if _, ok := arg.(*big.Int); ok {
allInt = false
}
}
if allInt {
var low, high int
if len(args) == 1 {
low, high = 0, args[0].(int)
} else {
low, high = args[0].(int), args[1].(int)
}
if high <= low {
return 0, errs.BadValue{What: "high value",
Valid: fmt.Sprint("larger than ", low), Actual: strconv.Itoa(high)}
}
x := withRand(func(r *rand.Rand) int { return r.Intn(high - low) })
return low + x, nil
}
var low, high *big.Int
if len(args) == 1 {
low, high = big.NewInt(0), args[0].(*big.Int)
} else {
var casted_args [2]*big.Int
for i, arg := range args {
if casted_arg, ok := arg.(*big.Int); ok {
casted_args[i] = casted_arg
} else {
casted_args[i] = big.NewInt(int64(arg.(int)))
if high, ok := args[0].(int); ok {
return randIntSmallInt(0, high)
} else { // *big.Int
return randIntBigInt(&big.Int{}, args[0].(*big.Int))
}
} else { // len(args) == 2
if low, ok := args[0].(int); ok {
if high, ok := args[1].(int); ok {
return randIntSmallInt(low, high)
}
}
low, high = casted_args[0], casted_args[1]
// One or both of low and high is *big.Int
return randIntBigInt(
vals.PromoteToBigInt(args[0]), vals.PromoteToBigInt(args[1]))
}
}

func randIntSmallInt(low, high int) (vals.Num, error) {
if high <= low {
return 0, errs.BadValue{What: "high value",
Valid: fmt.Sprint("larger than ", low), Actual: strconv.Itoa(high)}
}
x := withRand(func(r *rand.Rand) int { return r.Intn(high - low) })
return low + x, nil
}

func randIntBigInt(low, high *big.Int) (vals.Num, error) {
if high.Cmp(low) <= 0 {
return 0, errs.BadValue{What: "high value",
Valid: fmt.Sprint("larger than ", low), Actual: high.String()}
Expand Down
34 changes: 15 additions & 19 deletions pkg/eval/builtin_fn_num_test.elvts
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,21 @@ Exception: arity mismatch: arguments must be 1 to 2 values, but is 3 values
Exception: bad value: argument must be exact integer, but is (num 1.0)
[tty]:1:1-11: randint 1.0

## mixed argument type ##
// Regression test for https://b.elv.sh/1856.
~> var bound = 100000000000000000000000000
var x = (randint 0 $bound)
and (<= 0 $x) (< $x $bound)
▶ $true
~> var bound = 100000000000000000000000000
var x = (randint -$bound $bound)
and (<= -$bound $x) (< $x $bound)
▶ $true
~> var bound = 100000000000000000000000000
var x = (randint -$bound 0)
and (<= -$bound $x) (< $x 0)
▶ $true

/////////////
# -randseed #
/////////////
Expand Down Expand Up @@ -614,22 +629,3 @@ Exception: bad value: step must be negative, but is 0.5
~> range 1.2 >&-
Exception: port does not support value output
[tty]:1:1-13: range 1.2 >&-

## randint big arguments mixed ##
~> var bound = 100000000000000000000000000
var x = (randint $bound)
and (<= 0 $x) (< $x $bound)
▶ $true
~> var bound = 100000000000000000000000000
var x = (randint 0 $bound)
and (<= 0 $x) (< $x $bound)
▶ $true
~> var bound = 100000000000000000000000000
var x = (randint -$bound $bound)
and (<= -$bound $x) (< $x $bound)
▶ $true
~> var bound = 100000000000000000000000000
var x = (randint -$bound 0)
and (<= -$bound $x) (< $x 0)
▶ $true

0 comments on commit 1ecd68d

Please sign in to comment.