set flatlist [lremove $flatlist {*}[lseq 0 to $flen-1 by $cols_remaining]]
incr cols_remaining -1
}
return $zip_l
namespace eval argdoc {
variable PUNKARGS
lappend PUNKARGS [list {
@id -id ::punk::lib::lzipn
@cmd -name punk::lib::lzipn\
-summary\
"zip any number of lists together (unoptimised)."\
-help\
"Conceptually equivalent to converting a list of rows
to a list of columns.
See lzip which provides the same functionality but with
optimisations depending on the number of supplied lists.
"
@values -min 1 -max 1
lvar -type string -help\
"name of list variable"
a -type indexexpression
z -type indexexpression
}]
}
#keep both lzipn_tclX functions available for side-by-side testing in Tcl versions where it's possible
if {![package vsatisfies [package present Tcl] 9.0-] || [dict get [punk::lib::check::has_tclbug_lsearch_strideallinline] bug]} {
#-stride either not available - or has bug preventing use of main algorithm below
proc lzipn {args} [info body ::punk::lib::lzipn_tcl8]
proc lzipn {args} [info body ::punk::lib::system::lzipn_tcl8]
} else {
proc lzipn {args} [info body ::punk::lib::lzipn_tcl9a]
proc lzipn {args} [info body ::punk::lib::system::lzipn_tcl9a]
}
namespace import ::punk::args::lib::tstr
namespace eval argdoc {
@ -2291,13 +2364,31 @@ namespace eval punk::lib {
proc is_list_all_ni_list2 {a b} $body
}
#somewhat like struct::set difference - but order preserving, and doesn't treat as a 'set' so preserves dupes in fromlist
#struct::set difference may happen to preserve ordering when items are integers, but order can't be relied on,
# especially as struct::set has 2 differing implementations (tcl vs critcl) which return results with different ordering to each other and different deduping behaviour in some cases (e.g empty 2nd arg)
proc ldiff {fromlist removeitems} {
if {[llength $removeitems] == 0} {return $fromlist}
namespace eval argdoc {
variable PUNKARGS
lappend PUNKARGS [list {
@id -id ::punk::lib::ldiff
@cmd -name punk::lib::ldiff\
-summary\
"Difference consisting of items with removeitems removed."\
-help\
"Somewhat like struct::set difference, but order preserving, and doesn't
treat as a 'set' so preserves any duplicates in items.
struct::set difference may happen to preserve ordering when items are
integers, but order can't be relied on, especially as struct::set has
2 differening implementations (tcl vs critcl) which return results with
different ordering to each other and different deduping behaviour in
some cases (e.g when 2nd arg is empty)"
@values -min 2 -max 2
items -type list
removeitems -type list
}]
}
proc ldiff {items removeitems} {
if {[llength $removeitems] == 0} {return $items}
set result {}
foreach item $fromlist {
foreach item $items {
if {$item ni $removeitems} {
lappend result $item
}
@ -2361,6 +2452,28 @@ namespace eval punk::lib {
return [array names tmp]
}
namespace eval argdoc {
variable PUNKARGS
lappend PUNKARGS [list {
@id -id ::punk::lib::lunique_unordered
@cmd -name punk::lib::lunique_unordered\
-summary\
"unique values in list"\
-help\
"Return unique values in provided list.
This removes duplicates but *may* rearrange the
order of the returned elements compared to the
original list.
When struct::set is available this will be used
for the implementation, as it can be *slightly*
faster if acceleration is present. When struct::set
is not available it will fallback to lunique and
provide the same functionality with order preserved."
@values -min 1 -max 1
list -type list
}]
}
#default/fallback implementation
proc lunique_unordered {list} {
lunique $list
@ -2371,13 +2484,33 @@ namespace eval punk::lib {
struct::set union $list {}
}
} else {
puts stderr "WARNING: struct::set union <list> <emptylist> no longer dedupes!"
#struct::set union operates on a 'set' - so this probably won't change, and hopefully is
#consistent across unacelerated versions and those implemented in accelerators,
#but if it ever does change - be a little noisy about it.
puts stderr "punk::lib WARNING: struct::set union <list> <emptylist> no longer dedupes!"
#we could also test a sequence of: struct::set add
}
}
#order-preserving
namespace eval argdoc {
variable PUNKARGS
lappend PUNKARGS [list {
@id -id ::punk::lib::lunique
@cmd -name punk::lib::lunique\
-summary\
"Order-preserving unique values in list"\
-help\
"Return unique values in provided list.
This removes duplicates whilst preserving the
original order of the provided list.
When struct::set is available with acceleration,
lunique_unordered may be slightly faster."
@values -min 1 -max 1
list -type list
}]
}
proc lunique {list} {
set new {}
foreach item $list {
@ -2569,18 +2702,21 @@ namespace eval punk::lib {
To validate if an indexset is strictly within range, both the length of the data and the base would
need to be considered.
The normal 'range' specifier is ..
The normal 'range' specifier is .. but can be of the form .x. where x is the step value.
The range specifier can appear at the beginning, middle or end, or even alone to indicate the entire
range of valid values.
e.g the following are all valid ranges
1..
(index 1 to 'max')
..10
(index 'base' to 10)
2..11
(index 2 to 11)
..
(all indices)
1..
(index 1 to 'max')
..10
(index 'base' to 10)
2..11
(index 2 to 11)
..
(all indices)
.3.
(1st index and every 3rd index thereafter)
Common whitespace elements space,tab,newlines are ignored.
Each index (or endpoint of an index-range) can be of the forms accepted by Tcl list or string commands,
e.g end-2 or 2+2.
@ -2670,20 +2806,19 @@ namespace eval punk::lib {
.-1. would represent end to base with step -1
If start is omitted and only the end is supplied:
The default step is 1 indicating ascension and the missing end is equivalent to 'end'
indexset_resolve 5 2..
-> 2 3 4
The default end is the base if the step is negative
indexset_resolve 5 2.-1.
-> 2 1 0
If end is omitted and onlthe start is supplied:
The default step is 1 indicating ascension and the missing start is equivalent to the base.
indexset_resolve 5 ..2
-> 0 1 2
The default start is 'end' if the step is negative
indexset_resolve 5 .-1.2
-> 4 3 2
If end is omitted and only the start is supplied:
The default step is 1 indicating ascension and the missing end is equivalent to 'end'
indexset_resolve 5 2..
-> 2 3 4
The default end is the base if the step is negative
indexset_resolve 5 2.-1.
-> 2 1 0
Like the tcl9 lseq command - a step (by) value of zero produces no results.
@ -2703,7 +2838,7 @@ namespace eval punk::lib {
indexset examples:
These assume the default 0-based indices (base == 0)
These assume the default 0-based indices (-base 0)
1,3..
output the index 1 (2nd item) followed by all from index 3 to the end.
@ -3604,7 +3739,7 @@ namespace eval punk::lib {
@id -id ::punk::lib::gcd
@cmd -name punk::lib::gcd\
-summary\
"Gretest common divisor of m and n."\
"Greatest common divisor of m and n."\
-help\
"Return the greatest common divisor of m and n.
Straight from Lars Hellström's math::numtheory library in Tcllib
@ -3643,12 +3778,22 @@ namespace eval punk::lib {
return $m
}
namespace eval argdoc {
variable PUNKARGS
lappend PUNKARGS [list {
@id -id ::punk::lib::lcm
@cmd -name punk::lib::lcm\
-summary\
"Lowest common multiple of m and n."\
-help\
"Return the lowest common multiple of m and n.
Straight from Lars Hellström's math::numtheory library in Tcllib"
@values -min 2 -max 2
m -type integer
n -type integer
}]
}
proc lcm {n m} {
#*** !doctools
#[call [fun gcd] [arg n] [arg m]]
#[para]Return the lowest common multiple of m and n
#[para]Straight from Lars Hellström's math::numtheory library in Tcllib
set flatlist [lremove $flatlist {*}[lseq 0 to $flen-1 by $cols_remaining]]
incr cols_remaining -1
}
return $zip_l
namespace eval argdoc {
variable PUNKARGS
lappend PUNKARGS [list {
@id -id ::punk::lib::lzipn
@cmd -name punk::lib::lzipn\
-summary\
"zip any number of lists together (unoptimised)."\
-help\
"Conceptually equivalent to converting a list of rows
to a list of columns.
See lzip which provides the same functionality but with
optimisations depending on the number of supplied lists.
"
@values -min 1 -max 1
lvar -type string -help\
"name of list variable"
a -type indexexpression
z -type indexexpression
}]
}
#keep both lzipn_tclX functions available for side-by-side testing in Tcl versions where it's possible
if {![package vsatisfies [package present Tcl] 9.0-] || [dict get [punk::lib::check::has_tclbug_lsearch_strideallinline] bug]} {
#-stride either not available - or has bug preventing use of main algorithm below
proc lzipn {args} [info body ::punk::lib::lzipn_tcl8]
proc lzipn {args} [info body ::punk::lib::system::lzipn_tcl8]
} else {
proc lzipn {args} [info body ::punk::lib::lzipn_tcl9a]
proc lzipn {args} [info body ::punk::lib::system::lzipn_tcl9a]
}
namespace import ::punk::args::lib::tstr
namespace eval argdoc {
@ -2291,13 +2364,31 @@ namespace eval punk::lib {
proc is_list_all_ni_list2 {a b} $body
}
#somewhat like struct::set difference - but order preserving, and doesn't treat as a 'set' so preserves dupes in fromlist
#struct::set difference may happen to preserve ordering when items are integers, but order can't be relied on,
# especially as struct::set has 2 differing implementations (tcl vs critcl) which return results with different ordering to each other and different deduping behaviour in some cases (e.g empty 2nd arg)
proc ldiff {fromlist removeitems} {
if {[llength $removeitems] == 0} {return $fromlist}
namespace eval argdoc {
variable PUNKARGS
lappend PUNKARGS [list {
@id -id ::punk::lib::ldiff
@cmd -name punk::lib::ldiff\
-summary\
"Difference consisting of items with removeitems removed."\
-help\
"Somewhat like struct::set difference, but order preserving, and doesn't
treat as a 'set' so preserves any duplicates in items.
struct::set difference may happen to preserve ordering when items are
integers, but order can't be relied on, especially as struct::set has
2 differening implementations (tcl vs critcl) which return results with
different ordering to each other and different deduping behaviour in
some cases (e.g when 2nd arg is empty)"
@values -min 2 -max 2
items -type list
removeitems -type list
}]
}
proc ldiff {items removeitems} {
if {[llength $removeitems] == 0} {return $items}
set result {}
foreach item $fromlist {
foreach item $items {
if {$item ni $removeitems} {
lappend result $item
}
@ -2361,6 +2452,28 @@ namespace eval punk::lib {
return [array names tmp]
}
namespace eval argdoc {
variable PUNKARGS
lappend PUNKARGS [list {
@id -id ::punk::lib::lunique_unordered
@cmd -name punk::lib::lunique_unordered\
-summary\
"unique values in list"\
-help\
"Return unique values in provided list.
This removes duplicates but *may* rearrange the
order of the returned elements compared to the
original list.
When struct::set is available this will be used
for the implementation, as it can be *slightly*
faster if acceleration is present. When struct::set
is not available it will fallback to lunique and
provide the same functionality with order preserved."
@values -min 1 -max 1
list -type list
}]
}
#default/fallback implementation
proc lunique_unordered {list} {
lunique $list
@ -2371,13 +2484,33 @@ namespace eval punk::lib {
struct::set union $list {}
}
} else {
puts stderr "WARNING: struct::set union <list> <emptylist> no longer dedupes!"
#struct::set union operates on a 'set' - so this probably won't change, and hopefully is
#consistent across unacelerated versions and those implemented in accelerators,
#but if it ever does change - be a little noisy about it.
puts stderr "punk::lib WARNING: struct::set union <list> <emptylist> no longer dedupes!"
#we could also test a sequence of: struct::set add
}
}
#order-preserving
namespace eval argdoc {
variable PUNKARGS
lappend PUNKARGS [list {
@id -id ::punk::lib::lunique
@cmd -name punk::lib::lunique\
-summary\
"Order-preserving unique values in list"\
-help\
"Return unique values in provided list.
This removes duplicates whilst preserving the
original order of the provided list.
When struct::set is available with acceleration,
lunique_unordered may be slightly faster."
@values -min 1 -max 1
list -type list
}]
}
proc lunique {list} {
set new {}
foreach item $list {
@ -2569,18 +2702,21 @@ namespace eval punk::lib {
To validate if an indexset is strictly within range, both the length of the data and the base would
need to be considered.
The normal 'range' specifier is ..
The normal 'range' specifier is .. but can be of the form .x. where x is the step value.
The range specifier can appear at the beginning, middle or end, or even alone to indicate the entire
range of valid values.
e.g the following are all valid ranges
1..
(index 1 to 'max')
..10
(index 'base' to 10)
2..11
(index 2 to 11)
..
(all indices)
1..
(index 1 to 'max')
..10
(index 'base' to 10)
2..11
(index 2 to 11)
..
(all indices)
.3.
(1st index and every 3rd index thereafter)
Common whitespace elements space,tab,newlines are ignored.
Each index (or endpoint of an index-range) can be of the forms accepted by Tcl list or string commands,
e.g end-2 or 2+2.
@ -2670,20 +2806,19 @@ namespace eval punk::lib {
.-1. would represent end to base with step -1
If start is omitted and only the end is supplied:
The default step is 1 indicating ascension and the missing end is equivalent to 'end'
indexset_resolve 5 2..
-> 2 3 4
The default end is the base if the step is negative
indexset_resolve 5 2.-1.
-> 2 1 0
If end is omitted and onlthe start is supplied:
The default step is 1 indicating ascension and the missing start is equivalent to the base.
indexset_resolve 5 ..2
-> 0 1 2
The default start is 'end' if the step is negative
indexset_resolve 5 .-1.2
-> 4 3 2
If end is omitted and only the start is supplied:
The default step is 1 indicating ascension and the missing end is equivalent to 'end'
indexset_resolve 5 2..
-> 2 3 4
The default end is the base if the step is negative
indexset_resolve 5 2.-1.
-> 2 1 0
Like the tcl9 lseq command - a step (by) value of zero produces no results.
@ -2703,7 +2838,7 @@ namespace eval punk::lib {
indexset examples:
These assume the default 0-based indices (base == 0)
These assume the default 0-based indices (-base 0)
1,3..
output the index 1 (2nd item) followed by all from index 3 to the end.
@ -3604,7 +3739,7 @@ namespace eval punk::lib {
@id -id ::punk::lib::gcd
@cmd -name punk::lib::gcd\
-summary\
"Gretest common divisor of m and n."\
"Greatest common divisor of m and n."\
-help\
"Return the greatest common divisor of m and n.
Straight from Lars Hellström's math::numtheory library in Tcllib
@ -3643,12 +3778,22 @@ namespace eval punk::lib {
return $m
}
namespace eval argdoc {
variable PUNKARGS
lappend PUNKARGS [list {
@id -id ::punk::lib::lcm
@cmd -name punk::lib::lcm\
-summary\
"Lowest common multiple of m and n."\
-help\
"Return the lowest common multiple of m and n.
Straight from Lars Hellström's math::numtheory library in Tcllib"
@values -min 2 -max 2
m -type integer
n -type integer
}]
}
proc lcm {n m} {
#*** !doctools
#[call [fun gcd] [arg n] [arg m]]
#[para]Return the lowest common multiple of m and n
#[para]Straight from Lars Hellström's math::numtheory library in Tcllib
set flatlist [lremove $flatlist {*}[lseq 0 to $flen-1 by $cols_remaining]]
incr cols_remaining -1
}
return $zip_l
namespace eval argdoc {
variable PUNKARGS
lappend PUNKARGS [list {
@id -id ::punk::lib::lzipn
@cmd -name punk::lib::lzipn\
-summary\
"zip any number of lists together (unoptimised)."\
-help\
"Conceptually equivalent to converting a list of rows
to a list of columns.
See lzip which provides the same functionality but with
optimisations depending on the number of supplied lists.
"
@values -min 1 -max 1
lvar -type string -help\
"name of list variable"
a -type indexexpression
z -type indexexpression
}]
}
#keep both lzipn_tclX functions available for side-by-side testing in Tcl versions where it's possible
if {![package vsatisfies [package present Tcl] 9.0-] || [dict get [punk::lib::check::has_tclbug_lsearch_strideallinline] bug]} {
#-stride either not available - or has bug preventing use of main algorithm below
proc lzipn {args} [info body ::punk::lib::lzipn_tcl8]
proc lzipn {args} [info body ::punk::lib::system::lzipn_tcl8]
} else {
proc lzipn {args} [info body ::punk::lib::lzipn_tcl9a]
proc lzipn {args} [info body ::punk::lib::system::lzipn_tcl9a]
}
namespace import ::punk::args::lib::tstr
namespace eval argdoc {
@ -2291,13 +2364,31 @@ namespace eval punk::lib {
proc is_list_all_ni_list2 {a b} $body
}
#somewhat like struct::set difference - but order preserving, and doesn't treat as a 'set' so preserves dupes in fromlist
#struct::set difference may happen to preserve ordering when items are integers, but order can't be relied on,
# especially as struct::set has 2 differing implementations (tcl vs critcl) which return results with different ordering to each other and different deduping behaviour in some cases (e.g empty 2nd arg)
proc ldiff {fromlist removeitems} {
if {[llength $removeitems] == 0} {return $fromlist}
namespace eval argdoc {
variable PUNKARGS
lappend PUNKARGS [list {
@id -id ::punk::lib::ldiff
@cmd -name punk::lib::ldiff\
-summary\
"Difference consisting of items with removeitems removed."\
-help\
"Somewhat like struct::set difference, but order preserving, and doesn't
treat as a 'set' so preserves any duplicates in items.
struct::set difference may happen to preserve ordering when items are
integers, but order can't be relied on, especially as struct::set has
2 differening implementations (tcl vs critcl) which return results with
different ordering to each other and different deduping behaviour in
some cases (e.g when 2nd arg is empty)"
@values -min 2 -max 2
items -type list
removeitems -type list
}]
}
proc ldiff {items removeitems} {
if {[llength $removeitems] == 0} {return $items}
set result {}
foreach item $fromlist {
foreach item $items {
if {$item ni $removeitems} {
lappend result $item
}
@ -2361,6 +2452,28 @@ namespace eval punk::lib {
return [array names tmp]
}
namespace eval argdoc {
variable PUNKARGS
lappend PUNKARGS [list {
@id -id ::punk::lib::lunique_unordered
@cmd -name punk::lib::lunique_unordered\
-summary\
"unique values in list"\
-help\
"Return unique values in provided list.
This removes duplicates but *may* rearrange the
order of the returned elements compared to the
original list.
When struct::set is available this will be used
for the implementation, as it can be *slightly*
faster if acceleration is present. When struct::set
is not available it will fallback to lunique and
provide the same functionality with order preserved."
@values -min 1 -max 1
list -type list
}]
}
#default/fallback implementation
proc lunique_unordered {list} {
lunique $list
@ -2371,13 +2484,33 @@ namespace eval punk::lib {
struct::set union $list {}
}
} else {
puts stderr "WARNING: struct::set union <list> <emptylist> no longer dedupes!"
#struct::set union operates on a 'set' - so this probably won't change, and hopefully is
#consistent across unacelerated versions and those implemented in accelerators,
#but if it ever does change - be a little noisy about it.
puts stderr "punk::lib WARNING: struct::set union <list> <emptylist> no longer dedupes!"
#we could also test a sequence of: struct::set add
}
}
#order-preserving
namespace eval argdoc {
variable PUNKARGS
lappend PUNKARGS [list {
@id -id ::punk::lib::lunique
@cmd -name punk::lib::lunique\
-summary\
"Order-preserving unique values in list"\
-help\
"Return unique values in provided list.
This removes duplicates whilst preserving the
original order of the provided list.
When struct::set is available with acceleration,
lunique_unordered may be slightly faster."
@values -min 1 -max 1
list -type list
}]
}
proc lunique {list} {
set new {}
foreach item $list {
@ -2569,18 +2702,21 @@ namespace eval punk::lib {
To validate if an indexset is strictly within range, both the length of the data and the base would
need to be considered.
The normal 'range' specifier is ..
The normal 'range' specifier is .. but can be of the form .x. where x is the step value.
The range specifier can appear at the beginning, middle or end, or even alone to indicate the entire
range of valid values.
e.g the following are all valid ranges
1..
(index 1 to 'max')
..10
(index 'base' to 10)
2..11
(index 2 to 11)
..
(all indices)
1..
(index 1 to 'max')
..10
(index 'base' to 10)
2..11
(index 2 to 11)
..
(all indices)
.3.
(1st index and every 3rd index thereafter)
Common whitespace elements space,tab,newlines are ignored.
Each index (or endpoint of an index-range) can be of the forms accepted by Tcl list or string commands,
e.g end-2 or 2+2.
@ -2670,20 +2806,19 @@ namespace eval punk::lib {
.-1. would represent end to base with step -1
If start is omitted and only the end is supplied:
The default step is 1 indicating ascension and the missing end is equivalent to 'end'
indexset_resolve 5 2..
-> 2 3 4
The default end is the base if the step is negative
indexset_resolve 5 2.-1.
-> 2 1 0
If end is omitted and onlthe start is supplied:
The default step is 1 indicating ascension and the missing start is equivalent to the base.
indexset_resolve 5 ..2
-> 0 1 2
The default start is 'end' if the step is negative
indexset_resolve 5 .-1.2
-> 4 3 2
If end is omitted and only the start is supplied:
The default step is 1 indicating ascension and the missing end is equivalent to 'end'
indexset_resolve 5 2..
-> 2 3 4
The default end is the base if the step is negative
indexset_resolve 5 2.-1.
-> 2 1 0
Like the tcl9 lseq command - a step (by) value of zero produces no results.
@ -2703,7 +2838,7 @@ namespace eval punk::lib {
indexset examples:
These assume the default 0-based indices (base == 0)
These assume the default 0-based indices (-base 0)
1,3..
output the index 1 (2nd item) followed by all from index 3 to the end.
@ -3604,7 +3739,7 @@ namespace eval punk::lib {
@id -id ::punk::lib::gcd
@cmd -name punk::lib::gcd\
-summary\
"Gretest common divisor of m and n."\
"Greatest common divisor of m and n."\
-help\
"Return the greatest common divisor of m and n.
Straight from Lars Hellström's math::numtheory library in Tcllib
@ -3643,12 +3778,22 @@ namespace eval punk::lib {
return $m
}
namespace eval argdoc {
variable PUNKARGS
lappend PUNKARGS [list {
@id -id ::punk::lib::lcm
@cmd -name punk::lib::lcm\
-summary\
"Lowest common multiple of m and n."\
-help\
"Return the lowest common multiple of m and n.
Straight from Lars Hellström's math::numtheory library in Tcllib"
@values -min 2 -max 2
m -type integer
n -type integer
}]
}
proc lcm {n m} {
#*** !doctools
#[call [fun gcd] [arg n] [arg m]]
#[para]Return the lowest common multiple of m and n
#[para]Straight from Lars Hellström's math::numtheory library in Tcllib
set flatlist [lremove $flatlist {*}[lseq 0 to $flen-1 by $cols_remaining]]
incr cols_remaining -1
}
return $zip_l
namespace eval argdoc {
variable PUNKARGS
lappend PUNKARGS [list {
@id -id ::punk::lib::lzipn
@cmd -name punk::lib::lzipn\
-summary\
"zip any number of lists together (unoptimised)."\
-help\
"Conceptually equivalent to converting a list of rows
to a list of columns.
See lzip which provides the same functionality but with
optimisations depending on the number of supplied lists.
"
@values -min 1 -max 1
lvar -type string -help\
"name of list variable"
a -type indexexpression
z -type indexexpression
}]
}
#keep both lzipn_tclX functions available for side-by-side testing in Tcl versions where it's possible
if {![package vsatisfies [package present Tcl] 9.0-] || [dict get [punk::lib::check::has_tclbug_lsearch_strideallinline] bug]} {
#-stride either not available - or has bug preventing use of main algorithm below
proc lzipn {args} [info body ::punk::lib::lzipn_tcl8]
proc lzipn {args} [info body ::punk::lib::system::lzipn_tcl8]
} else {
proc lzipn {args} [info body ::punk::lib::lzipn_tcl9a]
proc lzipn {args} [info body ::punk::lib::system::lzipn_tcl9a]
}
namespace import ::punk::args::lib::tstr
namespace eval argdoc {
@ -2291,13 +2364,31 @@ namespace eval punk::lib {
proc is_list_all_ni_list2 {a b} $body
}
#somewhat like struct::set difference - but order preserving, and doesn't treat as a 'set' so preserves dupes in fromlist
#struct::set difference may happen to preserve ordering when items are integers, but order can't be relied on,
# especially as struct::set has 2 differing implementations (tcl vs critcl) which return results with different ordering to each other and different deduping behaviour in some cases (e.g empty 2nd arg)
proc ldiff {fromlist removeitems} {
if {[llength $removeitems] == 0} {return $fromlist}
namespace eval argdoc {
variable PUNKARGS
lappend PUNKARGS [list {
@id -id ::punk::lib::ldiff
@cmd -name punk::lib::ldiff\
-summary\
"Difference consisting of items with removeitems removed."\
-help\
"Somewhat like struct::set difference, but order preserving, and doesn't
treat as a 'set' so preserves any duplicates in items.
struct::set difference may happen to preserve ordering when items are
integers, but order can't be relied on, especially as struct::set has
2 differening implementations (tcl vs critcl) which return results with
different ordering to each other and different deduping behaviour in
some cases (e.g when 2nd arg is empty)"
@values -min 2 -max 2
items -type list
removeitems -type list
}]
}
proc ldiff {items removeitems} {
if {[llength $removeitems] == 0} {return $items}
set result {}
foreach item $fromlist {
foreach item $items {
if {$item ni $removeitems} {
lappend result $item
}
@ -2361,6 +2452,28 @@ namespace eval punk::lib {
return [array names tmp]
}
namespace eval argdoc {
variable PUNKARGS
lappend PUNKARGS [list {
@id -id ::punk::lib::lunique_unordered
@cmd -name punk::lib::lunique_unordered\
-summary\
"unique values in list"\
-help\
"Return unique values in provided list.
This removes duplicates but *may* rearrange the
order of the returned elements compared to the
original list.
When struct::set is available this will be used
for the implementation, as it can be *slightly*
faster if acceleration is present. When struct::set
is not available it will fallback to lunique and
provide the same functionality with order preserved."
@values -min 1 -max 1
list -type list
}]
}
#default/fallback implementation
proc lunique_unordered {list} {
lunique $list
@ -2371,13 +2484,33 @@ namespace eval punk::lib {
struct::set union $list {}
}
} else {
puts stderr "WARNING: struct::set union <list> <emptylist> no longer dedupes!"
#struct::set union operates on a 'set' - so this probably won't change, and hopefully is
#consistent across unacelerated versions and those implemented in accelerators,
#but if it ever does change - be a little noisy about it.
puts stderr "punk::lib WARNING: struct::set union <list> <emptylist> no longer dedupes!"
#we could also test a sequence of: struct::set add
}
}
#order-preserving
namespace eval argdoc {
variable PUNKARGS
lappend PUNKARGS [list {
@id -id ::punk::lib::lunique
@cmd -name punk::lib::lunique\
-summary\
"Order-preserving unique values in list"\
-help\
"Return unique values in provided list.
This removes duplicates whilst preserving the
original order of the provided list.
When struct::set is available with acceleration,
lunique_unordered may be slightly faster."
@values -min 1 -max 1
list -type list
}]
}
proc lunique {list} {
set new {}
foreach item $list {
@ -2569,18 +2702,21 @@ namespace eval punk::lib {
To validate if an indexset is strictly within range, both the length of the data and the base would
need to be considered.
The normal 'range' specifier is ..
The normal 'range' specifier is .. but can be of the form .x. where x is the step value.
The range specifier can appear at the beginning, middle or end, or even alone to indicate the entire
range of valid values.
e.g the following are all valid ranges
1..
(index 1 to 'max')
..10
(index 'base' to 10)
2..11
(index 2 to 11)
..
(all indices)
1..
(index 1 to 'max')
..10
(index 'base' to 10)
2..11
(index 2 to 11)
..
(all indices)
.3.
(1st index and every 3rd index thereafter)
Common whitespace elements space,tab,newlines are ignored.
Each index (or endpoint of an index-range) can be of the forms accepted by Tcl list or string commands,
e.g end-2 or 2+2.
@ -2670,20 +2806,19 @@ namespace eval punk::lib {
.-1. would represent end to base with step -1
If start is omitted and only the end is supplied:
The default step is 1 indicating ascension and the missing end is equivalent to 'end'
indexset_resolve 5 2..
-> 2 3 4
The default end is the base if the step is negative
indexset_resolve 5 2.-1.
-> 2 1 0
If end is omitted and onlthe start is supplied:
The default step is 1 indicating ascension and the missing start is equivalent to the base.
indexset_resolve 5 ..2
-> 0 1 2
The default start is 'end' if the step is negative
indexset_resolve 5 .-1.2
-> 4 3 2
If end is omitted and only the start is supplied:
The default step is 1 indicating ascension and the missing end is equivalent to 'end'
indexset_resolve 5 2..
-> 2 3 4
The default end is the base if the step is negative
indexset_resolve 5 2.-1.
-> 2 1 0
Like the tcl9 lseq command - a step (by) value of zero produces no results.
@ -2703,7 +2838,7 @@ namespace eval punk::lib {
indexset examples:
These assume the default 0-based indices (base == 0)
These assume the default 0-based indices (-base 0)
1,3..
output the index 1 (2nd item) followed by all from index 3 to the end.
@ -3604,7 +3739,7 @@ namespace eval punk::lib {
@id -id ::punk::lib::gcd
@cmd -name punk::lib::gcd\
-summary\
"Gretest common divisor of m and n."\
"Greatest common divisor of m and n."\
-help\
"Return the greatest common divisor of m and n.
Straight from Lars Hellström's math::numtheory library in Tcllib
@ -3643,12 +3778,22 @@ namespace eval punk::lib {
return $m
}
namespace eval argdoc {
variable PUNKARGS
lappend PUNKARGS [list {
@id -id ::punk::lib::lcm
@cmd -name punk::lib::lcm\
-summary\
"Lowest common multiple of m and n."\
-help\
"Return the lowest common multiple of m and n.
Straight from Lars Hellström's math::numtheory library in Tcllib"
@values -min 2 -max 2
m -type integer
n -type integer
}]
}
proc lcm {n m} {
#*** !doctools
#[call [fun gcd] [arg n] [arg m]]
#[para]Return the lowest common multiple of m and n
#[para]Straight from Lars Hellström's math::numtheory library in Tcllib
set overtext [lpop inputchunks 0] ;#could be a list 'ansisplit' or text 'plain|mixed'
#use eq test with emptystring instead of 'string length' - test for emptiness shouldn't cause shimmering if popped inputchunks member if an 'ansisplit' list
set opt_colstart [tcl::dict::get $opts -startcolumn] ;#lhs limit for overlay - an offset to cursor_column - first visible column is 1. 0 or < 0 are before the start of the underlay
set opt_colcursor [tcl::dict::get $opts -cursor_column];#start cursor column relative to overlay
set opt_row_context [tcl::dict::get $opts -cursor_row]
set opt_overtext_type [tcl::dict::get $opts -overtext_type]
if {[string length $opt_row_context]} {
if {![tcl::string::is integer -strict $opt_row_context] || $opt_row_context <1 } {
error "overtype::renderline -cursor_row must be empty for unspecified/unknown or a non-zero positive integer. received: '$opt_row_context'"
set overlay_grapheme_control_list [list] ;#tag each with g, sgr or other. 'other' are things like cursor-movement or insert-mode or codes we don't recognise/use
#experiment
set overlay_grapheme_control_stacks [list]
#REVIEW - even if we pass in a pre-split overtext (-overtext_type ansisplit)
#we are re-generating the overlay_grapheme_control_stacks list each time
#this is a big issue when overtext is not broken into lines, but is just a big long ansi and/or plain text string.
#todo - return also the unapplied portion of the overlay_grapheme_control_stacks list??
foreach {pt code} $overmap {
if {$pt ne ""} {
#todo - wrap in test for empty pt (we used split_codes_single - and it may be common for sgr sequences to be unmerged and so have empty pts between)
set flatlist [lremove $flatlist {*}[lseq 0 to $flen-1 by $cols_remaining]]
incr cols_remaining -1
}
return $zip_l
namespace eval argdoc {
variable PUNKARGS
lappend PUNKARGS [list {
@id -id ::punk::lib::lzipn
@cmd -name punk::lib::lzipn\
-summary\
"zip any number of lists together (unoptimised)."\
-help\
"Conceptually equivalent to converting a list of rows
to a list of columns.
See lzip which provides the same functionality but with
optimisations depending on the number of supplied lists.
"
@values -min 1 -max 1
lvar -type string -help\
"name of list variable"
a -type indexexpression
z -type indexexpression
}]
}
#keep both lzipn_tclX functions available for side-by-side testing in Tcl versions where it's possible
if {![package vsatisfies [package present Tcl] 9.0-] || [dict get [punk::lib::check::has_tclbug_lsearch_strideallinline] bug]} {
#-stride either not available - or has bug preventing use of main algorithm below
proc lzipn {args} [info body ::punk::lib::lzipn_tcl8]
proc lzipn {args} [info body ::punk::lib::system::lzipn_tcl8]
} else {
proc lzipn {args} [info body ::punk::lib::lzipn_tcl9a]
proc lzipn {args} [info body ::punk::lib::system::lzipn_tcl9a]
}
namespace import ::punk::args::lib::tstr
namespace eval argdoc {
@ -2291,13 +2364,31 @@ namespace eval punk::lib {
proc is_list_all_ni_list2 {a b} $body
}
#somewhat like struct::set difference - but order preserving, and doesn't treat as a 'set' so preserves dupes in fromlist
#struct::set difference may happen to preserve ordering when items are integers, but order can't be relied on,
# especially as struct::set has 2 differing implementations (tcl vs critcl) which return results with different ordering to each other and different deduping behaviour in some cases (e.g empty 2nd arg)
proc ldiff {fromlist removeitems} {
if {[llength $removeitems] == 0} {return $fromlist}
namespace eval argdoc {
variable PUNKARGS
lappend PUNKARGS [list {
@id -id ::punk::lib::ldiff
@cmd -name punk::lib::ldiff\
-summary\
"Difference consisting of items with removeitems removed."\
-help\
"Somewhat like struct::set difference, but order preserving, and doesn't
treat as a 'set' so preserves any duplicates in items.
struct::set difference may happen to preserve ordering when items are
integers, but order can't be relied on, especially as struct::set has
2 differening implementations (tcl vs critcl) which return results with
different ordering to each other and different deduping behaviour in
some cases (e.g when 2nd arg is empty)"
@values -min 2 -max 2
items -type list
removeitems -type list
}]
}
proc ldiff {items removeitems} {
if {[llength $removeitems] == 0} {return $items}
set result {}
foreach item $fromlist {
foreach item $items {
if {$item ni $removeitems} {
lappend result $item
}
@ -2361,6 +2452,28 @@ namespace eval punk::lib {
return [array names tmp]
}
namespace eval argdoc {
variable PUNKARGS
lappend PUNKARGS [list {
@id -id ::punk::lib::lunique_unordered
@cmd -name punk::lib::lunique_unordered\
-summary\
"unique values in list"\
-help\
"Return unique values in provided list.
This removes duplicates but *may* rearrange the
order of the returned elements compared to the
original list.
When struct::set is available this will be used
for the implementation, as it can be *slightly*
faster if acceleration is present. When struct::set
is not available it will fallback to lunique and
provide the same functionality with order preserved."
@values -min 1 -max 1
list -type list
}]
}
#default/fallback implementation
proc lunique_unordered {list} {
lunique $list
@ -2371,13 +2484,33 @@ namespace eval punk::lib {
struct::set union $list {}
}
} else {
puts stderr "WARNING: struct::set union <list> <emptylist> no longer dedupes!"
#struct::set union operates on a 'set' - so this probably won't change, and hopefully is
#consistent across unacelerated versions and those implemented in accelerators,
#but if it ever does change - be a little noisy about it.
puts stderr "punk::lib WARNING: struct::set union <list> <emptylist> no longer dedupes!"
#we could also test a sequence of: struct::set add
}
}
#order-preserving
namespace eval argdoc {
variable PUNKARGS
lappend PUNKARGS [list {
@id -id ::punk::lib::lunique
@cmd -name punk::lib::lunique\
-summary\
"Order-preserving unique values in list"\
-help\
"Return unique values in provided list.
This removes duplicates whilst preserving the
original order of the provided list.
When struct::set is available with acceleration,
lunique_unordered may be slightly faster."
@values -min 1 -max 1
list -type list
}]
}
proc lunique {list} {
set new {}
foreach item $list {
@ -2569,18 +2702,21 @@ namespace eval punk::lib {
To validate if an indexset is strictly within range, both the length of the data and the base would
need to be considered.
The normal 'range' specifier is ..
The normal 'range' specifier is .. but can be of the form .x. where x is the step value.
The range specifier can appear at the beginning, middle or end, or even alone to indicate the entire
range of valid values.
e.g the following are all valid ranges
1..
(index 1 to 'max')
..10
(index 'base' to 10)
2..11
(index 2 to 11)
..
(all indices)
1..
(index 1 to 'max')
..10
(index 'base' to 10)
2..11
(index 2 to 11)
..
(all indices)
.3.
(1st index and every 3rd index thereafter)
Common whitespace elements space,tab,newlines are ignored.
Each index (or endpoint of an index-range) can be of the forms accepted by Tcl list or string commands,
e.g end-2 or 2+2.
@ -2670,20 +2806,19 @@ namespace eval punk::lib {
.-1. would represent end to base with step -1
If start is omitted and only the end is supplied:
The default step is 1 indicating ascension and the missing end is equivalent to 'end'
indexset_resolve 5 2..
-> 2 3 4
The default end is the base if the step is negative
indexset_resolve 5 2.-1.
-> 2 1 0
If end is omitted and onlthe start is supplied:
The default step is 1 indicating ascension and the missing start is equivalent to the base.
indexset_resolve 5 ..2
-> 0 1 2
The default start is 'end' if the step is negative
indexset_resolve 5 .-1.2
-> 4 3 2
If end is omitted and only the start is supplied:
The default step is 1 indicating ascension and the missing end is equivalent to 'end'
indexset_resolve 5 2..
-> 2 3 4
The default end is the base if the step is negative
indexset_resolve 5 2.-1.
-> 2 1 0
Like the tcl9 lseq command - a step (by) value of zero produces no results.
@ -2703,7 +2838,7 @@ namespace eval punk::lib {
indexset examples:
These assume the default 0-based indices (base == 0)
These assume the default 0-based indices (-base 0)
1,3..
output the index 1 (2nd item) followed by all from index 3 to the end.
@ -3604,7 +3739,7 @@ namespace eval punk::lib {
@id -id ::punk::lib::gcd
@cmd -name punk::lib::gcd\
-summary\
"Gretest common divisor of m and n."\
"Greatest common divisor of m and n."\
-help\
"Return the greatest common divisor of m and n.
Straight from Lars Hellström's math::numtheory library in Tcllib
@ -3643,12 +3778,22 @@ namespace eval punk::lib {
return $m
}
namespace eval argdoc {
variable PUNKARGS
lappend PUNKARGS [list {
@id -id ::punk::lib::lcm
@cmd -name punk::lib::lcm\
-summary\
"Lowest common multiple of m and n."\
-help\
"Return the lowest common multiple of m and n.
Straight from Lars Hellström's math::numtheory library in Tcllib"
@values -min 2 -max 2
m -type integer
n -type integer
}]
}
proc lcm {n m} {
#*** !doctools
#[call [fun gcd] [arg n] [arg m]]
#[para]Return the lowest common multiple of m and n
#[para]Straight from Lars Hellström's math::numtheory library in Tcllib