lseq bugfixes: 8d1fc7,999b69,tip-746
Some checks failed
Linux / plan (push) Has been cancelled
macOS / plan (push) Has been cancelled
macOS / xcode (push) Has been cancelled
Build Binaries / Linux (push) Has been cancelled
Build Binaries / macOS (push) Has been cancelled
Build Binaries / Windows (push) Has been cancelled
Windows / plan (push) Has been cancelled
Linux / gcc (push) Has been cancelled
macOS / clang (push) Has been cancelled
Build Binaries / Combine Artifacts (prototype) (push) Has been cancelled
Windows / msvc (push) Has been cancelled
Windows / gcc (push) Has been cancelled

This commit is contained in:
griffin
2026-02-25 19:48:16 +00:00
5 changed files with 183 additions and 106 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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(

View File

@@ -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;
}
}

View File

@@ -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