diff --git a/changes.md b/changes.md index b92b32ed00..6bbf49f83a 100644 --- a/changes.md +++ b/changes.md @@ -14,7 +14,12 @@ to the userbase. - [Check for -municode doesn't work on WSL](https://core.tcl-lang.org/tcl/tktview/632710) - [configure --enable-man-compression error](https://core.tcl-lang.org/tcl/tktview/886549) - [nmake: rmdir and mkdir are picked from cygwin if available](https://core.tcl-lang.org/tcl/tktview/be40b7) - + - [lseq: has incorrect results in edge cases](https://core.tcl-lang.org/tcl/tktview/999b69) + - [lseq: "count" error persists across calls](https://core.tcl-lang.org/tcl/tktview/8d1fc7) + - [lseq: tip-746 removes the ability to pass expressions as numeric + values in the lseq + command.](https://core.tcl-lang.org/tips/doc/trunk/tip/746.md) + # Updated bundled packages, libraries, standards, data - Itcl 4.3.6 - sqlite3 3.51.2 diff --git a/doc/lseq.n b/doc/lseq.n index bca2b12156..93590cc667 100644 --- a/doc/lseq.n +++ b/doc/lseq.n @@ -31,40 +31,48 @@ The \fBlseq\fR command can produce both increasing and decreasing sequences. When both \fIstart\fR and \fIend\fR are provided without a \fIstep\fR value, then if \fIstart\fR <= \fIend\fR, the sequence will be increasing and if \fIstart\fR > \fIend\fR it will be decreasing. If a -\fIstep\fR vale is included, it's sign should agree with the direction of the -sequence (descending \(-> negative and ascending \(-> positive), otherwise an -empty list is returned. For example: +\fIstep\fR value is included, it's sign should agree with the direction of the +sequence (descending \(-> negative and ascending \(-> positive), otherwise the +list will have a length of 0. For example: .RS .PP .CS \" -% \fBlseq\fR 1 to 5 ;# increasing +# increasing: +.br +% \fBlseq\fR 1 to 5 \fI\(-> 1 2 3 4 5 -% \fBlseq\fR 5 to 1 ;# decreasing +# decreasing: +.br +% \fBlseq\fR 5 to 1 \fI\(-> 5 4 3 2 1 -% \fBlseq\fR 0 0.5 by 0.1 ;# doubles +# doubles: +.br +% \fBlseq\fR 0 0.5 by 0.1 \fI\(-> 0.0 0.1 0.2 0.3 0.4 0.5\fR -% \fBlseq\fR 6 to 1 by 2 ;# decreasing, step wrong sign, empty list +# decreasing, step with wrong sign: +.br +% \fBlseq\fR 6 to 1 by 2 +\fI\(-> {} -% \fBlseq\fR 1 to 5 by 0 ;# all step sizes of 0 produce an empty list +# step of 0, +.br +% \fBlseq\fR 3 to 9 by 0 +\fI\(-> 3 .\" .CE .RE .PP -The numeric arguments, \fIstart\fR, \fIend\fR, \fIstep\fR, and \fIcount\fR, -may also be a valid expression. The expression will be evaluated and the -numeric result will be used. An expression that does not evaluate to a number -will produce an invalid argument error. -.PP \fIStart\fR defines the initial value and \fIend\fR defines the limit, not necessarily the last value. \fBlseq\fR produces a list with \fIcount\fR -elements, and if \fIcount\fR is not supplied, it is computed as: +elements, always, even if the step value is 0. and if \fIcount\fR is not +supplied, it is computed as: .RS .PP .CS -\fIcount\fR = int( (\fIend\fR - \fIstart\fR + \fIstep\fR) / \fIstep\fR ) +\fIcount\fR = int( ( (\fIend\fR - \fIstart\fR) / \fIstep\fR ) + 1 ) .CE .RE .SH EXAMPLES @@ -82,6 +90,9 @@ elements, and if \fIcount\fR is not supplied, it is computed as: set l [\fBlseq\fR 0 -5] \fI\(-> 0 -1 -2 -3 -4 -5\fR +\fBlseq\fR 1 count 5 by 0 +\fI\(-> 1 1 1 1 1\fR + foreach i [\fBlseq\fR [llength $l]] { puts l($i)=[lindex $l $i] } @@ -103,13 +114,13 @@ foreach i [\fBlseq\fR {[llength $l]-1} 0] { \fI\(-> l(0)=0\fR set i 17 - \fI\(-> 17\fR +\fI\(-> 17\fR + if {$i in [\fBlseq\fR 0 50]} { # equivalent to: (0 <= $i && $i <= 50) puts "Ok" } else { puts "outside :(" -} -\fI\(-> Ok\fR +} \fI\(-> Ok\fR set sqrs [lmap i [\fBlseq\fR 1 10] { expr {$i*$i} }] \fI\(-> 1 4 9 16 25 36 49 64 81 100\fR diff --git a/generic/tclArithSeries.c b/generic/tclArithSeries.c index 6e8f9f50db..f987909655 100644 --- a/generic/tclArithSeries.c +++ b/generic/tclArithSeries.c @@ -289,6 +289,13 @@ maxObjPrecision( *---------------------------------------------------------------------- */ static Tcl_WideInt +ArithSeriesLenDbl( + double start, + double end, + double step, + unsigned precision); + +static Tcl_WideInt ArithSeriesLenInt( Tcl_WideInt start, Tcl_WideInt end, @@ -296,13 +303,21 @@ ArithSeriesLenInt( { Tcl_WideInt len; + // Check for opposite sequence directions + if ((step < 0 && (end-start) > 0) || + (step > 0 && (end-start) < 0)) { + return 0; + } if (step == 0) { - return 0; + // Sequence consists of a single value + return 1; } - len = (end - start) / step + 1; + + len = ((end - start) / step) + 1; if (len < 0) { - return 0; + len = 0; } + return len; } @@ -320,7 +335,7 @@ ArithSeriesLenDbl( * with and w/o FPU_INLINE_ASM, _CONTROLFP, etc) */ if (step == 0) { - return 0; + return 1; } if (precision) { scaleFactor = power10(precision); @@ -330,6 +345,7 @@ ArithSeriesLenDbl( } /* distance */ end -= start; + /* * To improve numerical stability use wide arithmetic instead of IEEE-754 * when distance and step do not exceed wide-integers. @@ -339,7 +355,8 @@ ArithSeriesLenDbl( Tcl_WideInt iend = end < 0 ? end - 0.5 : end + 0.5; Tcl_WideInt istep = step < 0 ? step - 0.5 : step + 0.5; if (istep) { /* avoid div by zero, steps like 0.1, precision 0 */ - return (iend / istep) + 1; + Tcl_WideInt ilen = (iend / istep) + 1; + return (ilen < 0 ? 0 : ilen); } } /* @@ -348,10 +365,10 @@ ArithSeriesLenDbl( */ len = (end / step) + 1; if (len >= (double)TCL_SIZE_MAX) { - return TCL_SIZE_MAX; + len = TCL_SIZE_MAX; } - if (len < 0) { - return 0; + if (len <= 0) { + len = 1; } return (Tcl_WideInt)len; } @@ -460,10 +477,13 @@ NewArithSeriesInt( TclNewObj(arithSeriesObj); - if (length <= 0) { - /* TODO - should negative lengths be an error? */ + if (length < 0) { + // Negative length implies the step direction is opposite to the + // start/end direction. In mathmatical terms, an infinite length. + // Map this to an empty list (length of 0). An empty set is a vaild + // sequence. return arithSeriesObj; - } else if (length > 1) { + } else if (length >= 1) { /* Check for numeric overflow. Not needed for single element lists */ Tcl_WideUInt absoluteStep; Tcl_WideInt numIntervals = length - 1; @@ -481,9 +501,10 @@ NewArithSeriesInt( absoluteStep = -step; } /* First, step*number of intervals should not overflow */ - if ((UWIDE_MAX / absoluteStep) < (Tcl_WideUInt) numIntervals) { + if (absoluteStep && (UWIDE_MAX / absoluteStep) < (Tcl_WideUInt) numIntervals) { goto invalid_range; } + if (step > 0) { /* * Because of check above and UWIDE_MAX=2*WIDE_MAX+1, @@ -675,53 +696,61 @@ TclNewArithSeriesObj( { double dstart, dend, dstep = 1.0; Tcl_WideInt start, end, step = 1; - Tcl_WideInt len = -1; + Tcl_WideInt len; Tcl_Obj *objPtr; unsigned precision = (unsigned)-1; /* unknown precision */ + + if (lenObj) { + if (Tcl_GetWideIntFromObj(interp, lenObj, &len) != TCL_OK) { + return NULL; + } + } else { + // Default + len = -1; + } + + if (stepObj) { + if (assignNumber(interp, useDoubles, &step, &dstep, stepObj) != TCL_OK) { + return NULL; + } + } else { + // Default + step = 1; + dstep = step; + } + if (startObj) { if (assignNumber(interp, useDoubles, &start, &dstart, startObj) != TCL_OK) { return NULL; } } else { + // Default start = 0; - dstart = 0.0; - } - if (stepObj) { - if (assignNumber(interp, useDoubles, &step, &dstep, stepObj) != TCL_OK) { - return NULL; - } - if (!useDoubles ? !step : !dstep) { - TclNewObj(objPtr); - return objPtr; - } + dstart = start; } + if (endObj) { if (assignNumber(interp, useDoubles, &end, &dend, endObj) != TCL_OK) { return NULL; } } - if (lenObj) { - if (Tcl_GetWideIntFromObj(interp, lenObj, &len) != TCL_OK) { - return NULL; - } - } if (endObj) { if (!stepObj) { if (useDoubles) { if (dstart > dend) { - dstep = -1.0; step = -1; + dstep = step; } } else { if (start > end) { step = -1; - dstep = -1.0; + dstep = step; } } } - assert(dstep!=0); + if (!lenObj) { if (useDoubles) { if (isinf(dstart) || isinf(dend)) { @@ -763,6 +792,13 @@ TclNewArithSeriesObj( * similar to plain lists, otherwise it'd generate an error or panic later * (0x0ffffffffffffffa instead of 0x7fffffffffffffff by 64bit) */ + /* + * Comment for future reference: The size concern would only be triggered + * via a shimmer or getElements of the abstract list. Otherwise, the + * abstract list is only limited by the max index value. Even then, + * indexing with a bignum is theoretically possible. This is because + * individual values are not stored. They are created upon request. + */ if (len > TCL_SIZE_MAX) { exceeded: Tcl_SetObjResult(interp, Tcl_NewStringObj( diff --git a/generic/tclCmdIL.c b/generic/tclCmdIL.c index ca101e6a94..f1ac1c7d58 100644 --- a/generic/tclCmdIL.c +++ b/generic/tclCmdIL.c @@ -4047,10 +4047,6 @@ SequenceIdentifyArgument( void *internalPtr; if (allowedArgs & NumericArg) { - /* speed-up a bit (and avoid shimmer for compiled expressions) */ - if (TclHasInternalRep(argPtr, &tclExprCodeType)) { - goto doExpr; - } result = Tcl_GetNumberFromObj(NULL, argPtr, &internalPtr, keywordIndexPtr); if (result == TCL_OK) { *numValuePtr = argPtr; @@ -4072,24 +4068,15 @@ SequenceIdentifyArgument( *keywordIndexPtr = opmode; return RangeKeywordArg; } else { - Tcl_Obj *exprValueObj; + int keyword; if (!(allowedArgs & NumericArg)) { return NoneArg; } - doExpr: - /* Check for an index expression */ - if (Tcl_ExprObj(interp, argPtr, &exprValueObj) != TCL_OK) { + if (Tcl_GetNumberFromObj(interp, argPtr, &internalPtr, &keyword) != TCL_OK) { return ErrArg; } - int keyword; - /* Determine if result of expression is double or int */ - if (Tcl_GetNumberFromObj(interp, exprValueObj, &internalPtr, - &keyword) != TCL_OK - ) { - return ErrArg; - } - *numValuePtr = exprValueObj; /* incremented in Tcl_ExprObj */ - *keywordIndexPtr = keyword; /* type of expression result */ + *numValuePtr = argPtr; + *keywordIndexPtr = keyword; /* type of result */ return NumericArg; } } diff --git a/tests/lseq.test b/tests/lseq.test index 4e6626ccdc..395cadfd9c 100644 --- a/tests/lseq.test +++ b/tests/lseq.test @@ -19,6 +19,7 @@ testConstraint arithSeriesShimmer 1 testConstraint arithSeriesShimmerOk 1 testConstraint has64BitLengths [expr {$tcl_platform(pointerSize) == 8}] testConstraint has32BitLengths [expr {$tcl_platform(pointerSize) == 4}] +testConstraint TIP746 0 ;# Obsolete expr behavior in lseq on numeric arguments proc memusage {} { set fd [open /proc/[pid]/statm] @@ -154,35 +155,30 @@ test lseq-1.24 {consistence, use double (even if representable as integer) in al [lseq 0 count 3 by 1.0] [lseq 0 .. 2.0] [lseq 0 to 2 by 1.0] } [lrepeat 6 {0.0 1.0 2.0}] test lseq-1.25 {consistence, use double (even if representable as integer) in all variants, if contains a double somewhere} { - list [lseq double(0) 2] [lseq 0 double(2)] [lseq double(0) count 3] \ - [lseq 0 count 3 by double(1)] [lseq 0 .. double(2)] [lseq 0 to 2 by double(1)] + list [lseq [expr {double(0)}] 2] [lseq 0 [expr {double(2)}]] [lseq [expr {double(0)}] count 3] \ + [lseq 0 count 3 by [expr {double(1)}]] [lseq 0 .. [expr {double(2)}]] [lseq 0 to 2 by [expr {double(1)}]] } [lrepeat 6 {0.0 1.0 2.0}] test lseq-1.26 {consistence, double always remains double} { list [lseq 1 3.0 ] \ [lseq 1 [expr {3.0+0}] ] \ - [lseq 1 {3.0+0} ] \ [lseq 1.0 3.0 1] \ - [lseq [expr {1.0+0}] [expr {3.0+0}] 1] \ - [lseq {1.0+0} {3.0+0} 1] -} [lrepeat 6 {1.0 2.0 3.0}] + [lseq [expr {1.0+0}] [expr {3.0+0}] 1] +} [lrepeat 4 {1.0 2.0 3.0}] test lseq-1.27 {consistence, double always remains double} { list [lseq 1e50 [expr {1e50+1}] ] \ - [lseq 1e50 {1e50+1} ] \ [lseq [expr {1e50+0}] [expr {1e50+1}] 1] \ - [lseq {1e50+0} {1e50+1} 1] \ - [lseq [expr {1e50+0}] count 1 1] \ - [lseq {1e50+0} count 1 1] -} [lrepeat 6 [expr {1e50}]] + [lseq [expr {1e50+0}] count 1 1] +} [lrepeat 3 [expr {1e50}]] # # Short-hand use cases # test lseq-2.2 {step magnitude} { - lseq 10 1 2 ;# this is an empty case since step has wrong sign + lseq 10 1 2 ;# An error where step has wrong sign. Not an error, but list has len 1 } {} test lseq-2.3 {step wrong sign} arithSeriesDouble { - lseq 25. 5. 5 ;# ditto - empty list + lseq 25. 5. 5 ;# ditto - same as lseq-2.2 above } {} test lseq-2.4 {integer decreasing} { @@ -215,7 +211,7 @@ test lseq-2.10 {integer lseq with step} { lseq 1 10 2 } {1 3 5 7 9} -test lseq-2.11 {error case: increasing wrong step direction} { +test lseq-2.11 {case: increasing wrong step direction} { lseq 1 10 -2 } {} @@ -255,20 +251,20 @@ test lseq-2.18 {signs} { } {{-10 -8 -6 -4 -2} {} {} {10 6 2} {-10 -7 -4 -1} {10 5}} -test lseq-2.19 {expressions as indices} { +test lseq-2.19 {expressions as indices} -constraints TIP746 -body { list [lseq {1+1}] \ [lseq {1+1} {2+2}] \ [lseq {1+1} count {2+2}] \ [lseq {1+1} {5+5} {2+2}] \ [lseq {1+1} count {2+2} by {2+2}] -} {{0 1} {2 3 4} {2 3 4 5} {2 6 10} {2 6 10 14}} +} -result {{0 1} {2 3 4} {2 3 4 5} {2 6 10} {2 6 10 14}} -test lseq-2.20 {expressions as indices, no duplicative eval of expr} { +test lseq-2.20 {expressions as indices, no duplicative eval of expr} -constraints TIP746 -body { set i 1 list [lseq {[incr i]}] $i [lseq {0 + [incr i]}] $i [lseq {0.0 + [incr i]}] $i -} {{0 1} 2 {0 1 2} 3 {0 1 2 3} 4} +} -result {{0 1} 2 {0 1 2} 3 {0 1 2 3} 4} -test lseq-3.0 {expr error: don't swalow expr error (here: divide by zero)} -body { +test lseq-3.0 {expr error: don't swalow expr error (here: divide by zero)} -constraints TIP746 -body { set i 0; lseq {3/$i} } -returnCodes [catch {expr {3/0}} res] -result $res @@ -296,15 +292,15 @@ test lseq-3.1 {experiement} -body { test lseq-3.2 {error case} -body { lseq foo -} -returnCodes 1 -match glob -result {invalid bareword "foo"*} +} -returnCodes 1 -match glob -result {expected number but got "foo"} test lseq-3.3 {error case} -body { lseq 10 foo -} -returnCodes 1 -match glob -result {invalid bareword "foo"*} +} -returnCodes 1 -match glob -result {expected number but got "foo"} test lseq-3.4 {error case} -body { lseq 25 or 6 -} -returnCodes 1 -match glob -result {invalid bareword "or"*} +} -returnCodes 1 -match glob -result {expected number but got "or"} test lseq-3.5 {simple count and step arguments} -body { set s [lseq 25 by 6] @@ -421,14 +417,24 @@ test lseq-3.18 {error case} -body { test lseq-3.19 {edge case} -body { lseq 1 count 5 by 0 -} -result {} -# 1 1 1 1 1 +} -result {1 1 1 1 1} -# My thought is that this is likely a user error, since they can always use lrepeat for this. +# My thought is that this is likely a user error, since they can +# always use lrepeat for this. + +# bsg: Nah. The count defines the required list length, regardless of +# the values produced by the equation. Consider scenarios +# where arguments are variables that may include 0 +# intentionally. A reasonable valid answer is better than +# always having to safeguard against errors. +# +# Another way to look at it is that the sequence is an equation +# of a straight line. Every sequnce should be representable on a +# cartisian plane where X (the index) is always positive. test lseq-3.20 {edge case} -body { lseq 1 to 1 by 0 -} -result {} +} -result {1} # hmmm, I guess this is right, in a way, so... @@ -556,7 +562,7 @@ test lseq-3.34 {"in" operator} -body { } -result {{3012.0 3012.3 3012.6 3012.9} {3012.03 3012.06 3012.09 3012.12 3012.15 3012.18 3012.21 3012.24 3012.27 3012.33 3012.36 3012.39 3012.42 3012.45 3012.48 3012.51 3012.54 3012.57 3012.63 3012.66 3012.69 3012.72 3012.75 3012.78 3012.81 3012.84 3012.87 3012.93 3012.96 3012.99}} test lseq-3.35 {"in" operator integer} -body { - set seq [lseq 3 int(15e4) 5] + set seq [lseq 3 [expr {int(15e4)}] 5] set inlist {} set nilist {} foreach y [lseq 3012 3213 3] { @@ -584,12 +590,12 @@ test lseq-3.36 {"in" non-numeric case} -body { test lseq-4.1 {end expressions} -body { set start 7 - lseq $start $start+11 + lseq $start [expr {$start+11}] } -cleanup {unset start} -result {7 8 9 10 11 12 13 14 15 16 17 18} test lseq-4.2 {start expressions} -body { set base [clock seconds] - set tl [lseq $base-60 $base 10] + set tl [lseq [expr {$base-60}] $base 10] lmap t $tl {expr {$t - $base + 60}} } -cleanup {unset base tl t} -result {0 10 20 30 40 50 60} @@ -620,11 +626,11 @@ test lseq-4.3 {TIP examples} -body { lseq 25. to -25. by -5 # -> 25.0 20.0 15.0 10.0 5.0 0.0 -5.0 -10.0 -15.0 -20.0 -25.0 lseq 5 5 - # -> 5 + # -> lseq 5 5 2 - # -> 5 + # -> lseq 5 5 -2 - # -> 5 + # -> } set res {} foreach {cmd expect} [split $examples \n] { @@ -852,7 +858,7 @@ test lseq-4.21.1 {Corner cases: overflows by Inf} -body { }] test lseq-4.21.2 {Corner cases: expected Inf} -body { set res {} - lappend res [lseq {1e5555+0} count 5] + lappend res [lseq [expr {1e5555+0}] count 5] lappend res [lseq Inf count 5] lappend res [lseq Inf count 5 by 100] lappend res [lseq Inf count 5 by Inf] @@ -878,7 +884,7 @@ test lseq-4.21.2 {Corner cases: expected Inf} -body { }] test lseq-4.21.3 {Corner cases: expected -Inf} -body { set res {} - lappend res [lseq {-1e5555+0} count 5] + lappend res [lseq [expr {-1e5555+0}] count 5] lappend res [lseq -Inf count 5] lappend res [lseq -Inf count 5 by 100] lappend res [lseq -Inf count 5 by -Inf] @@ -906,7 +912,7 @@ test lseq-4.21.4 {Corner cases: unexpected Inf - Inf, result to +/-NaN, unexpect set res {} lappend res [list [catch {lseq Inf count 5 by -Inf} msg opt] $msg [dict getd $opt -errorcode ""]] lappend res [list [catch {lseq -Inf count 5 by Inf} msg opt] $msg [dict getd $opt -errorcode ""]] - lappend res [list [catch {lseq {Inf - Inf} count 5} msg opt] $msg [dict getd $opt -errorcode ""]] + lappend res [list [catch {lseq [expr {Inf - Inf}] count 5} msg opt] $msg [dict getd $opt -errorcode ""]] lappend res [list [catch {lseq NaN count 5} msg opt] $msg [dict getd $opt -errorcode ""]] lappend res [list [catch {lseq NaN count 5 by 100} msg opt] $msg [dict getd $opt -errorcode ""]] lappend res [list [catch {lseq NaN count 5 by NaN} msg opt] $msg [dict getd $opt -errorcode ""]] @@ -916,6 +922,7 @@ test lseq-4.21.4 {Corner cases: unexpected Inf - Inf, result to +/-NaN, unexpect } -cleanup { unset -nocomplain res msg opt } -result [join [lrepeat 8 {1 {domain error: argument not in valid range} {ARITH DOMAIN {domain error: argument not in valid range}}}] \n] + test lseq-4.21.5 {Corner cases: unexpected NaN} -body { set res {} lappend res [catch {lseq NaN} msg] $msg @@ -923,6 +930,7 @@ test lseq-4.21.5 {Corner cases: unexpected NaN} -body { } -cleanup { unset -nocomplain res msg } -result {1 {expected integer but got "NaN"} 1 {cannot use non-numeric floating-point value "NaN" to estimate length of arith-series}} + test lseq-4.21.6 {Corner cases: empty list, reversed step} -body { set res {} lappend res [lseq -5 .. 0 by -1] @@ -932,36 +940,55 @@ test lseq-4.21.6 {Corner cases: empty list, reversed step} -body { } -cleanup { unset -nocomplain res } -result {{} {} {} {}} + test lseq-4.21.6-lran {Corner cases: lrange empty list, reversed step} -body { set res {} # not shared: lappend res [lrange [lseq -5 .. 0 by -1] 1 end-1] + lappend res [lrange [lseq -5 .. 0 by 1] 1 end-1] + lappend res [lrange [lseq 5 .. 0 by -1] 1 end-1] lappend res [lrange [lseq 5 .. 0 by 1] 1 end-1] lappend res [lrange [lseq 0 .. 5 by -1] 1 end-1] + lappend res [lrange [lseq 0 .. 5 by 1] 1 end-1] + lappend res [lrange [lseq 0 .. -5 by -1] 1 end-1] lappend res [lrange [lseq 0 .. -5 by 1] 1 end-1] # shared: lappend res [lrange [set l [lseq -5 .. 0 by -1]] 1 end-1] + lappend res [lrange [set l [lseq -5 .. 0 by 1]] 1 end-1] + lappend res [lrange [set l [lseq 5 .. 0 by -1]] 1 end-1] lappend res [lrange [set l [lseq 5 .. 0 by 1]] 1 end-1] lappend res [lrange [set l [lseq 0 .. 5 by -1]] 1 end-1] + lappend res [lrange [set l [lseq 0 .. 5 by 1]] 1 end-1] + lappend res [lrange [set l [lseq 0 .. -5 by -1]] 1 end-1] lappend res [lrange [set l [lseq 0 .. -5 by 1]] 1 end-1] } -cleanup { unset -nocomplain res l -} -result {{} {} {} {} {} {} {} {}} +} -result {{} {-4 -3 -2 -1} {4 3 2 1} {} {} {1 2 3 4} {-1 -2 -3 -4} {} {} {-4 -3 -2 -1} {4 3 2 1} {} {} {1 2 3 4} {-1 -2 -3 -4} {}} + test lseq-4.21.6-lrev {Corner cases: lreverse empty list, reversed step} -body { set res {} # not shared: lappend res [lreverse [lseq -5 .. 0 by -1]] + lappend res [lreverse [lseq -5 .. 0 by 1]] + lappend res [lreverse [lseq 5 .. 0 by -1]] lappend res [lreverse [lseq 5 .. 0 by 1]] - lappend res [lreverse [lseq 0 .. 5 by -1]] + lappend res [lreverse [lseq 0 .. -5 by -1]] lappend res [lreverse [lseq 0 .. -5 by 1]] + lappend res [lreverse [lseq 0 .. 5 by -1]] + lappend res [lreverse [lseq 0 .. 5 by 1]] # shared: lappend res [lreverse [set l [lseq -5 .. 0 by -1]]] + lappend res [lreverse [set l [lseq -5 .. 0 by 1]]] + lappend res [lreverse [set l [lseq 5 .. 0 by -1]]] lappend res [lreverse [set l [lseq 5 .. 0 by 1]]] lappend res [lreverse [set l [lseq 0 .. 5 by -1]]] - lappend res [lreverse [set l [lseq 0 .. -5 by 1]]] + lappend res [lreverse [set l [lseq 0 .. 5 by 1]]] + lappend res [lreverse [set l [lseq 0 .. -5 by -1]]] + lappend res [lreverse [set l [lseq 0 .. -5 by -1]]] } -cleanup { unset -nocomplain res l -} -result {{} {} {} {} {} {} {} {}} +} -result {{} {0 -1 -2 -3 -4 -5} {0 1 2 3 4 5} {} {-5 -4 -3 -2 -1 0} {} {} {5 4 3 2 1 0} {} {0 -1 -2 -3 -4 -5} {0 1 2 3 4 5} {} {} {5 4 3 2 1 0} {-5 -4 -3 -2 -1 0} {-5 -4 -3 -2 -1 0}} + test lseq-4.21.7 {Corner cases: non-empty list, normal step} -body { set res {} lappend res [lseq -5 .. 0 ] @@ -1116,6 +1143,17 @@ test lseq-bug-0ee626dfb2-1 {Bug 0ee626dfb2 - integer overflow} -body { lseq 0x7fffffffffffffff count 3 by -0x8000000000000000 } -result {invalid arithmetic series parameter values} -returnCodes error +test lseq-bug-8d1fc709957-0 {Bug 8d1fc709957 - lseq "count" error persists across calls} -body { + catch {lseq 0 count 5} err0 + catch {lseq count 5} err1 + catch {lseq 1 count 5} err2 + list $err0 $err1 $err2 +} -result {{0 1 2 3 4} {expected number but got "count"} {1 2 3 4 5}} + +test lseq-bug-999b6966b2-0 {Bug 999b6966b2 - lseq has incorrect results in edge cases} -body { + lmap i [lseq -9 9] { lseq 10 10 $i } +} -result {10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10} + # cleanup ::tcltest::cleanupTests