SequenceableCollection.st
branchjv
changeset 25416 c9cd91278d66
parent 24911 fe2e546e6f2f
--- a/SequenceableCollection.st	Mon Aug 31 11:59:30 2020 +0100
+++ b/SequenceableCollection.st	Mon Aug 31 12:01:25 2020 +0100
@@ -62,21 +62,6 @@
 "
 ! !
 
-!SequenceableCollection class methodsFor:'initialization'!
-
-initialize
-"/ now a class based exception
-"/ method here left as a comment for a while.
-"/ will go away soon...
-"/    MissingClassInLiteralArrayErrorSignal isNil ifTrue:[
-"/        MissingClassInLiteralArrayErrorSignal := Error newSignalMayProceed:true.
-"/        MissingClassInLiteralArrayErrorSignal nameClass:self message:#missingClassInLiteralArrayErrorSignal.
-"/        MissingClassInLiteralArrayErrorSignal notifierString:'Missing class in literal encoding'.
-"/    ]
-
-    "Created: / 18.5.1999 / 14:49:51 / cg"
-! !
-
 !SequenceableCollection class methodsFor:'instance creation'!
 
 decodeFromLiteralArray:anArray
@@ -117,39 +102,37 @@
 
     |totalSize newColl idx|
 
-    totalSize := aCollectionOfCollections
-			inject:0
-			into:[:sumSoFar :el | sumSoFar + el size].
+    totalSize := aCollectionOfCollections sum:[:el| el size].
     newColl := self new:totalSize.
     idx := 1.
     aCollectionOfCollections do:[:el |
-	|sz|
-
-	sz := el size.
-	newColl replaceFrom:idx to:(idx+sz-1) with:el startingAt:1.
-	idx := idx + sz
+        |sz|
+
+        sz := el size.
+        newColl replaceFrom:idx to:(idx+sz-1) with:el startingAt:1.
+        idx := idx + sz
     ].
     ^ newColl
 
     "
      this method optimizes the following (common) operation:
 
-	 |s|
-
-	 s := ''.
-	 #('hello' ' ' 'world' ' ' 'this is ' 'ST/X') do:[:e|
-	    s := s , e.
-	 ].
-	 s
+         |s|
+
+         s := ''.
+         #('hello' ' ' 'world' ' ' 'this is ' 'ST/X') do:[:e|
+            s := s , e.
+         ].
+         s
 
      String
-	newWithConcatenationOfAll:#('hello' ' ' 'world' ' ' 'this is ' 'ST/X')
+        newWithConcatenationOfAll:#('hello' ' ' 'world' ' ' 'this is ' 'ST/X')
 
      String
-	newWithConcatenationOfAll:#()
+        newWithConcatenationOfAll:#()
 
      Array
-	newWithConcatenationOfAll:#( (1 2 3 4) (5 6 7 8) (9 10 11 12) )
+        newWithConcatenationOfAll:#( (1 2 3 4) (5 6 7 8) (9 10 11 12) )
 
      timing:
      -------
@@ -163,46 +146,50 @@
      arr3 := Array new:10000 withAll:'hello'.
 
      (Array with:arr1 with:arr2 with:arr3) with:#(10000 100 10) do:[:arr :cnt |
-	 t := Time millisecondsToRun:[
-	    cnt timesRepeat:[
-		String
-		    newWithConcatenationOfAll:arr
-	    ]
-	 ].
-	 Transcript showCR:(arr size printString , ' elements - time for #newWithConcatenationOfAll :' , (t/cnt) asFloat printString , 'mS').
-	 Transcript endEntry.
-
-	 t := Time millisecondsToRun:[
-	    cnt timesRepeat:[
-		 |s|
-
-		 s := ''.
-		 arr do:[:e|
-		    s := s , e.
-		 ].
-		 s
-	    ]
-	 ].
-	 Transcript showCR:(arr size printString , ' elements - time for loop over #, :' , (t/cnt) asFloat printString , 'mS').
-	 Transcript endEntry.
+         t := Time millisecondsToRun:[
+            cnt timesRepeat:[
+                String
+                    newWithConcatenationOfAll:arr
+            ]
+         ].
+         Transcript showCR:(arr size printString , ' elements - time for #newWithConcatenationOfAll :' , (t/cnt) asFloat printString , 'mS').
+         Transcript endEntry.
+
+         t := Time millisecondsToRun:[
+            cnt timesRepeat:[
+                 |s|
+
+                 s := ''.
+                 arr do:[:e|
+                    s := s , e.
+                 ].
+                 s
+            ]
+         ].
+         Transcript showCR:(arr size printString , ' elements - time for loop over #, :' , (t/cnt) asFloat printString , 'mS').
+         Transcript endEntry.
      ]
     "
+
+    "Modified: / 22-02-2019 / 09:58:25 / Stefan Vogel"
 !
 
 newWithSize:size
     "return a new collection of size.
      For variable size collections, this is different from #new:,
      in that #new: creates an empty collection with preallocated size,
-     while #withSize: creates a non empty one."
+     while #newWithSize: creates a non empty one."
 
     ^ (self new:size) grow:size.
 
     "
      (OrderedCollection new:10) inspect.
-     (OrderedCollection withSize:10) inspect.
+     (OrderedCollection newWithSize:10) inspect.
      (Array new:10) inspect.
-     (Array withSize:10) inspect.
-    "
+     (Array newWithSize:10) inspect.
+    "
+
+    "Modified (comment): / 09-10-2017 / 17:03:18 / stefan"
 ! !
 
 !SequenceableCollection class methodsFor:'Signal constants'!
@@ -220,7 +207,7 @@
     "Created: / 18.5.1999 / 14:50:04 / cg"
 ! !
 
-!SequenceableCollection class methodsFor:'instance creation-multiDimensional'!
+!SequenceableCollection class methodsFor:'instance creation - multiDimensional'!
 
 _at:nIndices
     "this is a synthetic selector, generated by the compiler,
@@ -232,43 +219,9 @@
     "
 
     ^ self new:nIndices
-!
-
-_at:dim1 at:dim2
-    "this is a synthetic selector, generated by the compiler,
-     if a construct of the form expr[idx...] is parsed.
-     I.e.
-        Array[n,m]
-     generates
-        Array _at:n at:m
-    "
-
-    |data|
-
-    data := self newWithSize:(dim1 * dim2).
-    ^ MultiDimensionalArrayAccessor
-        collection:data
-        dimensions:(Array with:dim1 with:dim2)
-!
-
-_at:dim1 at:dim2 at:dim3
-    "this is a synthetic selector, generated by the compiler,
-     if a construct of the form expr[idx...] is parsed.
-     I.e.
-        Array[n,m,o]
-     generates
-        Array _at:n at:m at:o
-    "
-
-    |data|
-
-    data := self newWithSize:(dim1 * dim2 * dim3).
-    ^ MultiDimensionalArrayAccessor
-        collection:data
-        dimensions:(Array with:dim1 with:dim2 with:dim3)
 ! !
 
-!SequenceableCollection class methodsFor:'instance creation-streaming'!
+!SequenceableCollection class methodsFor:'instance creation - streaming'!
 
 new:newSize streamContents:blockWithArg 
     "create a write-stream on an instance of the receiver-class with initial size,
@@ -302,26 +255,6 @@
     "
 !
 
-streamContents:blockWithArg
-    "create a write-stream on an instance of the receiver-class,
-     evaluate blockWithArg, passing that stream,
-     extract and return the streams contents."
-
-    |stream|
-
-    stream := self writeStream.
-    blockWithArg value:stream.
-    ^ stream contents
-
-    "
-     |rslt|
-
-     rslt := String streamContents:[:s | s nextPutAll:'hello'; space; nextPutAll:'world']
-    "
-
-    "Modified: / 29-03-2007 / 15:20:20 / cg"
-!
-
 streamContents:blockWithArg limitedTo:limit
     "create a limited write-stream on an instance of the receiver-class,
      evaluate blockWithArg, passing that stream,
@@ -397,17 +330,17 @@
     "
 !
 
-writeStream
+writeStream:count
     "create a write-stream on an instance of the receiver-class"
 
-    ^ self writeStreamClass on:(self new:50).
+    ^ self writeStreamClass on:(self new:count).
 
     "
      OrderedCollection writeStream
      Text writeStream
     "
 
-    "Modified: / 09-01-2011 / 10:37:35 / cg"
+    "Created: / 10-01-2018 / 18:32:59 / stefan"
 !
 
 writeStreamWithInitialSize:l
@@ -422,6 +355,83 @@
     "Created: / 09-01-2011 / 10:36:28 / cg"
 ! !
 
+!SequenceableCollection class methodsFor:'instance creation-multiDimensional'!
+
+_at:dim1 at:dim2 
+    "this is a synthetic selector, generated by the compiler,
+     if a construct of the form expr[idx] is parsed.
+     I.e.
+        Array[n][m]
+     generates
+        Array _at:n at:m"
+    
+    |data|
+
+    data := self newWithSize:(dim1 * dim2).
+    ^ MatrixAccessor collection:data dimensions:(Array with:dim1 with:dim2)
+
+    "
+     <pragma: +ArrayIndexSyntaxExtension>
+
+     Array[3][3] inspect.
+     DoubleArray[3][3] inspect.
+     Matrix[3][3] inspect.
+    "
+!
+
+_at:dim1 at:dim2 at:dim3 
+    "this is a synthetic selector, generated by the compiler,
+     if a construct of the form expr[idx...] is parsed.
+     I.e.
+        Array[n][m][o]
+     generates
+        Array _at:n at:m at:o"
+    
+    |data|
+
+    data := self newWithSize:(dim1 * dim2 * dim3).
+    ^ MatrixAccessor collection:data
+        dimensions:(Array with:dim1 with:dim2 with:dim3)
+! !
+
+!SequenceableCollection class methodsFor:'instance creation-streaming'!
+
+streamContents:blockWithArg
+    "create a write-stream on an instance of the receiver-class,
+     evaluate blockWithArg, passing that stream,
+     extract and return the streams contents."
+
+    |stream|
+
+    stream := self writeStream.
+    blockWithArg value:stream.
+    ^ stream contents
+
+    "
+     |rslt|
+
+     rslt := String streamContents:[:s | s nextPutAll:'hello'; space; nextPutAll:'world'].
+     rslt := Text streamContents:[:s | s nextPutAll:'hello' allBold; space; nextPutAll:'world'].
+    "
+
+    "Modified: / 29-03-2007 / 15:20:20 / cg"
+    "Modified (comment): / 28-05-2019 / 15:03:02 / Claus Gittinger"
+!
+
+writeStream
+    "create a write-stream on an instance of the receiver-class"
+
+    ^ self writeStreamClass on:(self new).
+
+    "
+     OrderedCollection writeStream
+     Text writeStream
+    "
+
+    "Modified: / 09-01-2011 / 10:37:35 / cg"
+    "Modified: / 10-01-2018 / 18:33:18 / stefan"
+! !
+
 !SequenceableCollection class methodsFor:'queries'!
 
 isAbstract
@@ -536,7 +546,8 @@
     ^ self atRandom:Random
 
     "
-     #(1 2 3) atRandom
+     #(1 2 3) atRandom 
+     #('a' 'b' 'c') atRandom 
     "
 !
 
@@ -605,53 +616,6 @@
     "
 !
 
-copyWithoutAll:elementsToSkip
-    "return a new collection consisting of a copy of the receiver, with
-     ALL elements equal to any in elementsToSkip are left out.
-     No error is reported, if any in elementsToSkip is not in the collection."
-
-    |n         "{ Class: SmallInteger }"
-     sz        "{ Class: SmallInteger }"
-     srcIndex  "{ Class: SmallInteger }"
-     dstIndex  "{ Class: SmallInteger }"
-     skipIndex "{ Class: SmallInteger }"
-     copy l|
-
-    "the code below may look like overkill,
-     however, for big collections its better to move data
-     around in big chunks"
-
-    n := self occurrencesOfAny:elementsToSkip.
-    n == 0 ifTrue:[^ self copyFrom:1].
-
-    sz := self size.
-    copy := self copyEmptyAndGrow:(sz - n).
-
-    srcIndex := 1.
-    dstIndex := 1.
-
-    n timesRepeat:[
-        skipIndex := self indexOfAny:elementsToSkip startingAt:srcIndex.
-        l := skipIndex - srcIndex.
-        l ~~ 0 ifTrue:[
-            copy replaceFrom:dstIndex to:(dstIndex + l - 1)
-                        with:self startingAt:srcIndex.
-            dstIndex := dstIndex + l
-        ].
-        srcIndex := skipIndex + 1
-    ].
-    l := sz - srcIndex.
-    copy replaceFrom:dstIndex to:(dstIndex + l)
-                with:self startingAt:srcIndex.
-    ^ copy
-
-    "
-     #($a $b $c $d $e $f $g) copyWithoutAll:#($d $b $f)
-     'abcdefghi' copyWithoutAll:'hai'
-     #(90 80 70 80 60 45 80 50) copyWithoutAll:#(80 70 45)
-    "
-!
-
 copyWithoutFirst
     "An alias for copyButFirst: for squeak compatibility.
      Raises an error, if the receiver is empty."
@@ -666,6 +630,32 @@
     "
 !
 
+destroy
+    "used with cryptographic keys, to wipe their contents after use"
+
+    self clearContents
+!
+
+forceTo:newSize paddingWith:padding
+    "sigh: a bad name; should be named forceToSize:..."
+
+    self size > newSize ifTrue:[
+        ^ self copyTo:newSize
+    ].
+    self size < newSize ifTrue:[
+        ^ (self species new:newSize) 
+            atAllPut:padding;
+            replaceFrom:1 with:self startingAt:1;
+            yourself.
+    ].
+    ^ self.
+
+    "
+     #[1 2 3 4] forceTo:10 paddingWith:255
+     #[1 2 3 4] forceTo:3 paddingWith:255
+    "
+!
+
 joinWith:separatingElement
     "return a collection generated by concatenating my elements
      and embedding separatingElement in between.
@@ -682,6 +672,12 @@
     "                                     
 !
 
+removeAt:index
+    ^ self removeIndex:index.
+
+    "Created: / 01-07-2018 / 00:55:23 / Claus Gittinger"
+!
+
 shuffled
     "return a randomly shuffled copy of the receiver"
 
@@ -803,17 +799,16 @@
     "
 !
 
-at:index ifAbsent:exceptionBlock
+at:index ifAbsent:exceptionValue
     "return the element at index if valid.
-     If the index is invalid, return the result of evaluating
-     the exceptionblock.
+     If the index is invalid, return the result from exceptionValue.
      NOTICE:
-	in ST-80, this message is only defined for Dictionaries,
-	however, having a common protocol with indexed collections
-	often simplifies things."
+        in ST-80, this message is only defined for Dictionaries,
+        however, having a common protocol with indexed collections
+        often simplifies things."
 
     (index between:1 and:self size) ifFalse:[
-	^ exceptionBlock value
+        ^ exceptionValue value
     ].
     ^ self at:index
 
@@ -823,9 +818,11 @@
      #(1 2 3) at:4 ifAbsent:['no such index']
 
      (Dictionary with:(#foo -> #bar)
-		 with:(#frob -> #baz))
-	 at:#foobar ifAbsent:['no such index']
-    "
+                 with:(#frob -> #baz))
+         at:#foobar ifAbsent:['no such index']
+    "
+
+    "Modified (comment): / 22-12-2018 / 16:50:39 / Claus Gittinger"
 !
 
 atAllIndices:indexCollection
@@ -842,19 +839,23 @@
 !
 
 atIndex:index
-    "return an element at a given index. This allows for seqentialCollections
-     and orderedDictionaries to be both accessed via a numeric index."
+    "return an element at a given index."
 
     ^ self at:index
 
     "Created: / 08-08-2010 / 00:50:10 / cg"
+    "Modified (comment): / 15-06-2017 / 01:42:27 / mawalch"
+    "Modified (comment): / 22-12-2018 / 16:50:53 / Claus Gittinger"
 !
 
 atIndex:index ifAbsent:absentBlock
-    "return an element at a given index. This allows for seqenveableCollections
-     and orderedDictionaries to be both accessed via a numeric index."
+    "return an element at a given index.
+     If the index is invalid, return the value from absentBlock"
 
     ^ self at:index ifAbsent:absentBlock
+
+    "Modified (comment): / 15-06-2017 / 01:43:09 / mawalch"
+    "Modified (comment): / 22-12-2018 / 16:50:12 / Claus Gittinger"
 !
 
 atIndex:index put:newValue
@@ -864,6 +865,36 @@
     ^ self at:index put:newValue
 
     "Created: / 08-08-2010 / 00:50:27 / cg"
+    "Modified (comment): / 15-06-2017 / 01:43:25 / mawalch"
+!
+
+atLastIndex:index
+    "return an element at a given index, counting index from the end."
+
+    ^ self at:(self size + 1 - index)
+
+    "
+     #(10 20 30 40) atLastIndex:1
+     #(10 20 30 40) atLastIndex:4
+     #(10 20 30 40) atLastIndex:5
+    "
+
+    "Created: / 22-12-2018 / 16:49:35 / Claus Gittinger"
+!
+
+atLastIndex:index ifAbsent:exceptionValue
+    "return an element at a given index, counting index from the end.
+     If the index is invalid, return the value from absentBlock"
+
+    ^ self at:(self size + 1 - index) ifAbsent:exceptionValue
+
+    "
+     #(10 20 30 40) atLastIndex:1 ifAbsent:nil
+     #(10 20 30 40) atLastIndex:4 ifAbsent:nil
+     #(10 20 30 40) atLastIndex:5 ifAbsent:nil
+    "
+
+    "Created: / 22-12-2018 / 16:51:07 / Claus Gittinger"
 !
 
 before:anObject
@@ -931,7 +962,7 @@
      No longer raises an error if there are not enough elements;
      instead, returns what is there"
 
-    n < 0 ifTrue:[self error:'bad (negative) argument'].
+    n < 0 ifTrue:[ArgumentError raiseErrorString:'bad (negative) argument'].
 
     "/ OLD:
     "/ "error if collection has not enough elements"
@@ -949,6 +980,22 @@
      #(1 2 3 4 5) asSet first:3
      'hello world' first:5
     "
+
+    "Modified: / 06-06-2019 / 23:23:58 / Claus Gittinger"
+!
+
+firstOrNil
+    "return the first element or nil, if the collection is empty."
+
+    self isEmpty ifTrue:[^ nil].
+    ^ self at:1
+
+    "
+     args:
+     returns: firstElement <object>
+    "
+
+    "Created: / 13-12-2017 / 23:09:37 / stefan"
 !
 
 indexOfNth:n occurrenceOf:what
@@ -975,34 +1022,80 @@
 
 keyAtEqualValue:value
     "return the index of a value.
-     This is normally not used (use indexOf:), but makes the
-     protocol more compatible with dictionaries."
+     This is normally not used (use indexOf:), 
+     but makes the protocol more compatible with dictionaries.
+     This is a slow access, since the receiver is searched sequentially.
+     NOTICE:
+        The value is searched using equality compare"
 
     ^ self indexOf:value
+
+    "Modified (comment): / 07-02-2017 / 11:10:10 / cg"
 !
 
 keyAtEqualValue:value ifAbsent:exceptionBlock
     "return the index of a value.
-     This is normally not used (use indexOf:), but makes the
-     protocol more compatible with dictionaries."
+     This is normally not used (use indexOf:), 
+     but makes the protocol more compatible with dictionaries.
+     This is a slow access, since the receiver is searched sequentially.
+     NOTICE:
+        The value is searched using equality compare"
 
     ^ self indexOf:value ifAbsent:exceptionBlock
+
+    "Modified (comment): / 07-02-2017 / 11:10:03 / cg"
+!
+
+keyAtIdenticalValue:value
+    "return the identity index of a value.
+     This is normally not used (use indexOf:), 
+     but makes the protocol more compatible with dictionaries.
+     This is a slow access, since the receiver is searched sequentially."
+
+    ^ self identityIndexOf:value
+
+    "Created: / 07-02-2017 / 11:10:43 / cg"
+!
+
+keyAtIdenticalValue:value ifAbsent:exceptionBlock
+    "return the identity index of a value.
+     This is normally not used (use indexOf:), 
+     but makes the protocol more compatible with dictionaries.
+     This is a slow access, since the receiver is searched sequentially.
+     NOTICE:
+        The value is searched using identity compare"
+
+    ^ self identityIndexOf:value ifAbsent:exceptionBlock
+
+    "Created: / 07-02-2017 / 11:10:59 / cg"
 !
 
 keyAtValue:value
     "return the index of a value.
-     This is normally not used (use indexOf:), but makes the
-     protocol more compatible with dictionaries."
+     This is normally not used (use indexOf:), 
+     but makes the protocol more compatible with dictionaries.
+     This is a slow access, since the receiver is searched sequentially.
+     NOTICE:
+        The value is searched using identity compare;
+        use #keyAtEqualValue: to compare for equality."
 
     ^ self identityIndexOf:value
+
+    "Modified (comment): / 07-02-2017 / 11:09:43 / cg"
 !
 
 keyAtValue:value ifAbsent:exceptionBlock
     "return the index of a value.
-     This is normally not used (use indexOf:), but makes the
-     protocol more compatible with dictionaries."
+     This is normally not used (use indexOf:), 
+     but makes the protocol more compatible with dictionaries.
+     This is a slow access, since the receiver is searched sequentially.
+     NOTICE:
+        The value is searched using identity compare;
+        use #keyAtEqualValue:ifAbsent: to compare for equality."
 
     ^ self identityIndexOf:value ifAbsent:exceptionBlock
+
+    "Modified (comment): / 07-02-2017 / 11:04:31 / cg"
 !
 
 last
@@ -1028,7 +1121,7 @@
      No longer raises an error if there are not enough elements;
      instead, returns what is there."
 
-    n < 0 ifTrue:[self error:'bad (negative) argument'].
+    n < 0 ifTrue:[ArgumentError raiseErrorString:'bad (negative) argument'].
     "/ OLD:
     "/ n > self size ifTrue:[^ self notEnoughElementsError].
 
@@ -1041,6 +1134,8 @@
      'hello world' last:5
      'hello' last:10
     "
+
+    "Modified: / 06-06-2019 / 23:24:02 / Claus Gittinger"
 !
 
 nth:n
@@ -1112,6 +1207,16 @@
     self at:index2 put:t
 
     "Modified: 15.10.1997 / 19:27:08 / cg"
+!
+
+swapIndex:i1 and:i2
+    "spap element at i1 and i2"
+
+    <resource: #obsolete>
+
+    self swap:i1 with:i2
+
+    "Created: / 17-07-2017 / 10:35:19 / cg"
 ! !
 
 !SequenceableCollection methodsFor:'adding & removing'!
@@ -1169,7 +1274,7 @@
 
 addAll:aCollection beforeIndex:index
     "insert all elements of the argument, anObject to become located at index.
-     The collection may be unordered, but then order of the sliced-in elements
+     The collection may be unordered, but then the order of the sliced-in elements
      is undefined.
      Return the receiver."
 
@@ -1480,7 +1585,8 @@
 !
 
 removeIndex:index
-    "remove the argument stored at index. Return the receiver.
+    "remove the argument stored at index. 
+     Returns the receiver.
 
      Notice that this modifies the receiver, NOT a copy.
      Also note that it may be a slow operation for some collections,
@@ -1494,6 +1600,8 @@
      #(1 2 3 4 5) asOrderedCollection removeIndex:6
      #($a $b $c $d $e $f $g) removeIndex:3
     "
+
+    "Modified (comment): / 24-06-2019 / 12:48:18 / Claus Gittinger"
 !
 
 removeLast
@@ -1543,8 +1651,7 @@
     self combinationsStartingAt:1 prefix:#() do:aBlock
 
     "
-     (Array 
-            with:($a to:$d)) 
+     (Array with:($a to:$d)) 
         combinationsDo:[:eachCombination | Transcript showCR: eachCombination]
     "
     "
@@ -1568,6 +1675,8 @@
             with:#(A)) 
         combinationsDo:[:eachCombination | Transcript showCR: eachCombination]
     "
+
+    "Modified (comment): / 29-08-2017 / 15:34:45 / cg"
 !
 
 combinationsStartingAt:anInteger prefix:prefix do:aBlock
@@ -1908,7 +2017,7 @@
     "return the common prefix of myself and the argument, aCollection.
      If there is none, an empty collection is returned."
 
-    ^ self commonPrefixWith:aCollection ignoreCase:false
+    ^ self commonPrefixWith:aCollection caseSensitive:true
 
     "
      'hello' commonPrefixWith:'hello'
@@ -1931,6 +2040,8 @@
      'ababab' commonPrefixWith:'abcd'
      'abcdef' commonPrefixWith:'abcd'
     "
+
+    "Modified: / 31-03-2017 / 18:01:08 / stefan"
 !
 
 commonPrefixWith:aCollection caseSensitive:caseSensitive
@@ -2016,7 +2127,7 @@
     "return the common suffix (tail) of myself and the argument, aCollection.
      If there is none, an empty collection is returned."
 
-    ^ self commonSuffixWith:aCollection ignoreCase:false
+    ^ self commonSuffixWith:aCollection caseSensitive:true
 
     "
      'hello' commonSuffixWith:'hello'
@@ -2041,6 +2152,8 @@
      'bababa' commonSuffixWith:'dcba'
      'fdcba' commonSuffixWith:'dcba'
     "
+
+    "Modified: / 31-03-2017 / 18:01:21 / stefan"
 !
 
 commonSuffixWith:aCollection caseSensitive:caseSensitive
@@ -2154,6 +2267,41 @@
     ^ 0
 !
 
+compareWith:aSequenceableCollection using:compareBlock
+    "Compare the receiver with the argument and return 1 if the receiver is
+     greater, 0 if equal and -1 if less than the argument.
+     Uses compareBlock on each element, which ought to return -1,0 or 1 when
+     comparing individual elements"
+
+    |mySize    "{ Class: SmallInteger }"
+     otherSize "{ Class: SmallInteger }"
+     n         "{ Class: SmallInteger }"
+     e1 e2 cmp|
+
+    mySize := self size.
+    otherSize := aSequenceableCollection size.
+    n := mySize min:otherSize.
+
+    1 to:n do:[:index |
+        e1 := self at:index.
+        e2 := aSequenceableCollection at:index.
+        cmp := compareBlock value:e1 value:e2.
+        cmp ~~ 0 ifTrue:[
+            "identity compare is faster"    
+            ^ cmp
+        ].
+    ].
+    mySize > otherSize ifTrue:[^ 1].
+    mySize < otherSize ifTrue:[^ -1].
+    ^ 0
+
+    "
+     #(1 2 1.1 2.9 4) compareWith:#(1 2 1 3 4) using:[:a :b | a rounded compareWith:b rounded].
+    "
+
+    "Created: / 29-06-2018 / 11:30:21 / Claus Gittinger"
+!
+
 deepSameContentsAs:aCollection
     "return true, if the receiver and the arg have the same contents
      in both the named instance vars and any indexed instVars.
@@ -2218,6 +2366,40 @@
     "
 !
 
+endsWith:aCollection using:compareBlock
+    "return true, if the receiver's last elements match those of aCollection,
+     using compareBlock to compare individual elements.
+     compareBlock should return true if elements are considered the same.
+     If aCollection is empty, true is returned (incompatible to some other dialect's endsWith.)"
+
+    |index1 "{ Class: SmallInteger }"
+     index2 "{ Class: SmallInteger }"
+     stop   "{ Class: SmallInteger }"
+     sz     "{ Class: SmallInteger }"|
+
+    (aCollection == self) ifTrue:[^ true].
+    (aCollection isSequenceable) ifFalse:[^ false].
+
+    stop := aCollection size.
+    sz := self size.
+    stop > sz ifTrue:[^false].
+
+    index1 := sz.
+    index2 := stop.
+    [index2 > 0] whileTrue:[
+        (compareBlock value:(self at:index1) value:(aCollection at:index2)) ifFalse:[^ false].
+        index1 := index1 - 1.
+        index2 := index2 - 1
+    ].
+    ^ true
+
+    "
+     'abcde' endsWith:#($d $e) using:[:a :b | a asLowercase = b asLowercase]
+    "
+
+    "Created: / 29-06-2018 / 11:36:25 / Claus Gittinger"
+!
+
 endsWithAnyOf:aCollectionOfCollections
     "return true, if the receiver endswith any in aCollection"
 
@@ -2273,6 +2455,23 @@
     "Modified: / 27.3.1998 / 17:33:49 / cg"
 !
 
+indexOfFirstDifferenceWith:anotherCollection
+    "return the index of the first element which is different to the corresponding
+     element in anotherCollection. 0 if they are all the same.
+     The comparison is by equality, i.e. using #="
+     
+    ^ self with:anotherCollection findFirst:[:a :b | a ~= b] 
+
+    "
+     'hello' indexOfFirstDifferenceWith:'helLo' 
+     'hello' indexOfFirstDifferenceWith:'hello'   
+     'hello' indexOfFirstDifferenceWith:'hello1'   
+     'hello1' indexOfFirstDifferenceWith:'hello' 
+    "
+
+    "Created: / 31-08-2017 / 20:10:36 / cg"
+!
+
 isSameSequenceAs:otherCollection
     "Answer whether the receiver's size is the same as otherCollection's size,
      and each of the receiver's elements equal the corresponding element of otherCollection.
@@ -2395,8 +2594,7 @@
 !
 
 startsWith:aCollection
-    "return true, if the receiver's first elements match those
-     of aCollection
+    "return true, if the receiver's first elements match those of aCollection
      If the argument is empty, true is returned.
      Notice, that this is similar to, but slightly different from VW's and Squeak's beginsWith:,
      which are both inconsistent w.r.t. an empty argument."
@@ -2426,6 +2624,41 @@
      #(1 2 3 4) asOrderedCollection startsWith:#(1 2 3)
      #(1 2 3 4) asOrderedCollection startsWith:#()
     "
+
+    "Modified (comment): / 12-02-2017 / 11:19:40 / cg"
+!
+
+startsWith:aCollection using:compareBlock
+    "return true, if the receiver's first elements match those of aCollection,
+     using compareBlock to compare individual elements.
+     compareBlock should return true if elements are considered the same.
+     If the argument is empty, true is returned.
+     Notice, that this is similar to, but slightly different from VW's and Squeak's beginsWith:,
+     which are both inconsistent w.r.t. an empty argument."
+
+    |index "{ Class: SmallInteger }"
+     stop  "{ Class: SmallInteger }" |
+
+    (aCollection == self) ifTrue:[^true].
+    (aCollection isSequenceable) ifFalse:[^false].
+
+    stop := aCollection size.
+    stop > self size ifTrue:[^false].
+
+    index := 1.
+    [index <= stop] whileTrue:[
+        (compareBlock value:(self at:index) value:(aCollection at:index)) ifFalse:[^false].
+        index := index + 1
+    ].
+    ^ true
+
+    "
+     'aBCde' startsWith:'abc' using:[:a :b | a asLowercase = b asLowercase]
+     'abCde' startsWith:'abc' using:[:a :b | a asLowercase = b asLowercase]
+     'axCde' startsWith:'abc' using:[:a :b | a asLowercase = b asLowercase]
+    "
+
+    "Created: / 29-06-2018 / 11:36:38 / Claus Gittinger"
 !
 
 startsWithAnyOf:aCollectionOfCollections
@@ -2473,40 +2706,12 @@
 
 !SequenceableCollection methodsFor:'converting'!
 
-asCollectionOfSubCollectionsOfSize:pieceSize
-    "slice into pieces; return a collection containing pieces of size pieceSize from the receiver.
-     The last piece may be smaller, if the receiver's size is not a multiple of pieceSize."
-
-    |pieces
-     start  "{ Class:SmallInteger }"
-     stop   "{ Class:SmallInteger }"
-     mySize "{ Class:SmallInteger }"|
-
-    pieces := self speciesForSubcollection new.
-    start := 1. stop := start + pieceSize - 1.
-    mySize := self size.
-    [stop <= mySize] whileTrue:[
-        pieces add:(self copyFrom:start to:stop).
-        start := start + pieceSize.
-        stop := stop + pieceSize.
-    ].
-    (start <= mySize) ifTrue:[
-        pieces add:(self copyFrom:start to:mySize).
-    ].
-    ^ pieces
-
-    "
-     '123123123123123123' asCollectionOfSubCollectionsOfSize:3 
-     '12312312312312312312' asCollectionOfSubCollectionsOfSize:3 
-    "
-
-    "Modified: / 24-01-2017 / 18:55:07 / stefan"
-!
-
 asCollectionOfSubCollectionsSeparatedBy:anElement
     "return a collection containing the subcollections (separated by anElement)
      of the receiver. If anElement occurs multiple times in a row,
      the result will contain empty collections.
+     If the receiver starts with anElement, an initial empty collection is added. 
+     If the receiver ends with anElement, NO final empty collection is added. 
      This algorithm uses equality-compare to detect the element."
 
     |cols myClass
@@ -2541,73 +2746,21 @@
      #(a b c d e f g h) asCollectionOfSubCollectionsSeparatedBy: #d. 
      #(a b c d e f d d g h) asCollectionOfSubCollectionsSeparatedBy: #d.
      'foo-bar-baz' asCollectionOfSubCollectionsSeparatedBy: $-.
+     '-foo-bar-baz' asCollectionOfSubCollectionsSeparatedBy: $-.
+     'foo-bar-baz-' asCollectionOfSubCollectionsSeparatedBy: $-.
+     '-foo-bar-baz-' asCollectionOfSubCollectionsSeparatedBy: $-.
      'foobarbaz' asCollectionOfSubCollectionsSeparatedBy: $-. 
      '' asCollectionOfSubCollectionsSeparatedBy: $-. 
     "
 
     "Modified (format): / 24-01-2017 / 18:55:53 / stefan"
-!
-
-asCollectionOfSubCollectionsSeparatedBy:anElement do:aBlock
-    "evaluate aBlock for each subcollection generated by separating elements
-     of the receiver by anElement.
-     If anElement occurs multiple times in a row,
-     the block will be invoked with empty collections as argument.
-     This algorithm uses equality-compare to detect the element."
-
-    |subCollection
-     endIndex      "{ Class:SmallInteger }"
-     startIndex    "{ Class:SmallInteger }"
-     stopIndex     "{ Class:SmallInteger }" |
-
-    startIndex := 0.
-    endIndex := self size.
-
-    [startIndex <= endIndex] whileTrue:[
-        stopIndex := self indexOf:anElement startingAt:startIndex+1.
-        stopIndex == 0 ifTrue:[
-            stopIndex := self size
-        ] ifFalse: [
-            stopIndex := stopIndex - 1.
-        ].
-
-        (stopIndex < startIndex) ifTrue: [
-            subCollection := self species new:0
-        ] ifFalse: [
-            subCollection := self copyFrom:startIndex+1 to:stopIndex
-        ].
-        aBlock value:subCollection.
-        startIndex := stopIndex + 1
-    ].
-
-    "
-     '' 
-        asCollectionOfSubCollectionsSeparatedBy:$: do:[:each | Transcript showCR:each storeString]
-
-     '1 one' 
-        asCollectionOfSubCollectionsSeparatedBy:$: do:[:each | Transcript showCR:each storeString]
-
-     '1 one:2 two:3 three:4 four:5 five' 
-        asCollectionOfSubCollectionsSeparatedBy:$: do:[:each | Transcript showCR:each storeString]
-
-     'a::b' 
-        asCollectionOfSubCollectionsSeparatedBy:$: do:[:each | Transcript showCR:each storeString]
-
-     ':' 
-        asCollectionOfSubCollectionsSeparatedBy:$: do:[:each | Transcript showCR:each storeString]
-
-     ':a' 
-        asCollectionOfSubCollectionsSeparatedBy:$: do:[:each | Transcript showCR:each storeString]
-
-     'a:' 
-        asCollectionOfSubCollectionsSeparatedBy:$: do:[:each | Transcript showCR:each storeString]
-    "
+    "Modified (comment): / 11-02-2019 / 23:45:11 / Claus Gittinger"
 !
 
 asCollectionOfSubCollectionsSeparatedByAll:aSeparatorCollection
     "return a collection containing the subcollections (separated by aSeparatorCollection)
      of the receiver. If aSeparatorCollection occurs multiple times in a row,
-     the result will contain empty strings.
+     the result may contain empty strings.
      Uses equality-compare when searching for aSeparatorCollection."
 
     |items done myClass
@@ -2639,70 +2792,13 @@
 
     "
      '1::2::3::4::5::' asCollectionOfSubCollectionsSeparatedByAll:'::'
+     '::2' asCollectionOfSubCollectionsSeparatedByAll:'::'
      #(1 2 3 1 2 3 4 3 1 1 2 3 1 4 5) asCollectionOfSubCollectionsSeparatedByAll:#(3 1)
      'hello+#world+#here' asCollectionOfSubCollectionsSeparatedByAll:'+#'
     "
 
     "Modified (comment): / 24-01-2017 / 18:57:03 / stefan"
-!
-
-asCollectionOfSubCollectionsSeparatedByAny:aCollectionOfSeparators
-    "return a collection containing the subCollection
-     (separated by any from aCollectionOfSeparators) of the receiver.
-     This allows breaking up strings using a number of elements as separator.
-     Uses equality-compare when searching for separators."
-
-    ^ self asCollectionOfSubCollectionsSeparatedByAnyForWhich:[:el | aCollectionOfSeparators includes:el]
-
-    "
-     'hello:world:isnt:this nice' asCollectionOfSubCollectionsSeparatedByAny:#($:)
-     'hello:world:isnt:this nice' asCollectionOfSubCollectionsSeparatedByAny:':'
-     'hello:world:isnt:this nice' asCollectionOfSubCollectionsSeparatedByAny:(Array with:$: with:Character space)
-     'hello:world:isnt:this nice' asCollectionOfSubCollectionsSeparatedByAny:': '
-     'h1e2l3l4o' asCollectionOfSubCollectionsSeparatedByAny:($1 to: $9)
-     #(1 9 2 8 3 7 4 6 5 5) asCollectionOfSubCollectionsSeparatedByAny:#(1 2 3)
-    "
-!
-
-asCollectionOfSubCollectionsSeparatedByAnyForWhich:aBlock
-    "return a collection containing the subCollection
-     (separated by elements for which aBlock evaluates to true) of the receiver.
-     This allows breaking up strings using an arbitrary condition."
-
-    |words
-     start  "{ Class:SmallInteger }"
-     stop   "{ Class:SmallInteger }"
-     mySize "{ Class:SmallInteger }"|
-
-    words := self speciesForSubcollection new.
-    start := 1.
-    mySize := self size.
-    [start <= mySize] whileTrue:[
-        "skip multiple separators"
-        [ aBlock value:(self at:start)] whileTrue:[
-            start := start + 1 .
-            start > mySize ifTrue:[
-                ^ words
-            ].
-        ].
-
-        stop := self findFirst:aBlock startingAt:start.
-        stop == 0 ifTrue:[
-            words add:(self copyFrom:start to:mySize).
-            ^ words
-        ].
-        words add:(self copyFrom:start to:(stop - 1)).
-        start := stop
-    ].
-    ^ words
-
-    "
-     'hello:world:isnt:this nice' asCollectionOfSubCollectionsSeparatedByAnyForWhich:[:ch | ch = $:]
-     'h1e2l3l4o' asCollectionOfSubCollectionsSeparatedByAnyForWhich:[:ch | ch isDigit]
-     #(1 9 2 8 3 7 4 6 5 5) asCollectionOfSubCollectionsSeparatedByAnyForWhich:[:n | n odd]
-    "
-
-    "Modified (format): / 24-01-2017 / 18:57:57 / stefan"
+    "Modified (comment): / 03-07-2018 / 11:00:24 / Claus Gittinger"
 !
 
 asCollectionOfSubCollectionsSeparatedByAnyForWhich:aBlock withSeparatorsIncluded:aBoolean
@@ -2753,6 +2849,19 @@
     "Created: / 29-05-2018 / 13:58:54 / svestkap"
 !
 
+asKeysAndValues
+    "return a new OrderedDictionary with the receiver collection's elements,
+     which must be associations"
+
+    ^ OrderedDictionary withAssociations:self.
+
+    "
+     { 'ten' -> 10 . 'twenty' -> 20 . 'thirty' -> 30 } asKeysAndValues 
+    "
+
+    "Created: / 14-09-2018 / 18:00:58 / Stefan Vogel"
+!
+
 asSequenceableCollection
     "return myself as a SequenceableCollection.
      I am already a SequenceableCollection."
@@ -2766,173 +2875,68 @@
     "return a new string collection containing the elements;
      these ought to be strings. (i.e. String or Text instances)"
 
-    |newColl sz|
+    |sz|
 
     sz := self size.
-    newColl := StringCollection newWithSize:sz.
-    newColl replaceFrom:1 to:sz with:self startingAt:1.
-    ^ newColl
-
-    "Created: 18.5.1996 / 13:53:55 / cg"
-    "Modified: 18.5.1996 / 14:00:16 / cg"
-!
-
-asStringWith:sepCharOrString
-    "return a string generated by concatenating my elements
-     (which must be strings or nil) and embedding sepCharOrString characters in between.
-     The argument sepCharOrString may be a character, a string or nil.
-     Nil entries and empty strings are counted as empty lines.
-     Similar to joinWith:, but specifically targeted towards collections of strings."
-
-    ^ self
-        from:1 to:(self size)
-        asStringWith:sepCharOrString
-        compressTabs:false
-        final:nil
-
-    "
-     #('hello' 'world' 'foo' 'bar' 'baz') asStringWith:$;
-     #('hello' 'world' 'foo' 'bar' 'baz') asStringWith:'|'
-     'hello|world|foo|bar|baz' asCollectionOfSubstringsSeparatedBy:$|
-    "
-
-    "Modified: / 10-07-2010 / 22:59:29 / cg"
-!
-
-asStringWith:sepCharacterOrString from:firstLine to:lastLine
-    "return part of myself as a string with embedded sepCharacters.
-     My elements must be strings or nil; nil entries and empty strings are
-     taken as empty lines.
-     The argument sepCharOrString may be a character, a string or nil.
-     Similar to joinWith:, but specifically targeted towards collections of strings."
-
-    ^ self
-        from:firstLine to:lastLine
-        asStringWith:sepCharacterOrString
-        compressTabs:false
-        final:nil
-    "
-     creating entries for searchpath:
-
-     #('foo' 'bar' 'baz' '/foo/bar') asStringWith:$;
-
-     #('foo' 'bar' 'baz' '/foo/bar') asStringWith:$: from:1 to:3
-    "
-
-    "Modified: 23.2.1996 / 15:28:55 / cg"
-!
-
-asStringWith:sepCharacterOrString from:firstLine to:lastLine compressTabs:compressTabs final:endCharacterOrString
-    "return part of myself as a string or text with embedded sepCharacters.
-     My elements must be strings or nil; nil entries and empty strings are
-     taken as empty lines.
-     The arguments sepCharacterOrString and endCharacterOrString may be nil, a character or a string.
-     If the argument compressTabs is true, leading spaces are converted
-     to tab-characters (8col tabs). The last line is followed by a final
-     character (if non-nil).
-     Similar to joinWith:, but specifically targeted towards collections of strings."
-
-    ^ self
-        from:firstLine to:lastLine
-        asStringWith:sepCharacterOrString
-        compressTabs:compressTabs
-        final:endCharacterOrString
-        withEmphasis:true
-
-    "Modified: / 17.6.1998 / 12:31:19 / cg"
-!
-
-asStringWith:sepCharacterOrString from:firstLine to:lastLine compressTabs:compressTabs final:endCharacterOrString withEmphasis:withEmphasis
-    "return part of myself as a string or text with embedded sepCharacters
-     and followup endCharacter.
-     My elements must be strings or nil; nil entries and empty strings are
-     taken as empty lines.
-     The arguments sepCharacterOrString and endCharacterOrString may be nil, a character or a string.
-     If the argument compressTabs is true, leading spaces are converted
-     to tab-characters (8col tabs). The last line is followed by a final
-     character (if non-nil).
-     The withEmphais argument controls if the returned string should preserve
-     any emphasis. If false, a plain string is returned.
-     This method is tuned for big collections, in not creating many
-     intermediate strings (has linear runtime). For very small collections
-     and small strings, it may be faster to use the comma , operation.
-     Similar to joinWith:, but specifically targeted towards collections of strings."
-
-    ^ self
-        from:firstLine to:lastLine
-        asStringWith:sepCharacterOrString
-        compressTabs:compressTabs
-        final:endCharacterOrString
-        withEmphasis:withEmphasis
-!
-
-asStringWithCRs
-    "return a string generated by concatenating my elements
-     (which must be strings or nil) and embedding cr characters in between.
-     Nil entries and empty strings are counted as empty lines."
-
-    ^ self asStringWithCRsFrom:1 to:(self size)
-
-    "
-     #('hello' 'world' 'foo' 'bar' 'baz') asStringWithCRs
-
-     (OrderedCollection new
-	add:'hello';
-	add:'world';
-	add:'foo';
-	add:('bar' asText allBold);
-	yourself) asStringWithCRs
-
-     Transcript showCR:
-	 (OrderedCollection new
-	    add:'hello';
-	    add:'world';
-	    add:'foo';
-	    add:('bar' asText allBold);
-	    yourself) asStringWithCRs
-    "
-
-    "Modified: 18.5.1996 / 16:43:47 / cg"
-!
-
-asStringWithCRsFrom:firstLine to:lastLine
-    "return a string generated by concatenating some of my elements
-     (which must be strings or nil) and embedding cr characters in between.
-     Nil entries and empty strings are counted as empty lines."
-
-    ^ self asStringWithCRsFrom:firstLine to:lastLine compressTabs:false withCR:true
-
-    "
-     #('hello' 'world' 'foo' 'bar' 'baz') asStringWithCRsFrom:2 to:4
-
-    "
-
-    "Modified: 18.5.1996 / 16:50:55 / cg"
-!
-
-asStringWithCRsFrom:firstLine to:lastLine compressTabs:compressTabs
-    "return part of myself as a string with embedded cr's.
-     My elements must be strings or nil.
-     If the argument compressTabs is true, leading spaces are converted
-     to tab-characters (8col tabs).
-     Nil entries and empty strings are taken as empty lines."
-
-    ^ self asStringWithCRsFrom:firstLine to:lastLine compressTabs:compressTabs withCR:true
-!
-
-asStringWithCRsFrom:firstLine to:lastLine compressTabs:compressTabs withCR:withCR
-    "return part of myself as a string with embedded cr's.
-     My elements must be strings or nil; nil entries and empty strings are
-     taken as empty lines.
-     If the argument compressTabs is true, leading spaces are converted
-     to tab-characters (8col tabs). WithCR controls whether the last line
-     should be followed by a cr or not."
+    ^ (StringCollection newWithSize:sz)
+        replaceFrom:1 to:sz with:self startingAt:1;
+        yourself.
+
+    "Created: / 18-05-1996 / 13:53:55 / cg"
+    "Modified: / 09-10-2017 / 17:05:19 / stefan"
+!
+
+asStringWithoutEmphasis
+    "return myself as a string with embedded cr's, but drop any emphasis"
 
     ^ self 
-        asStringWith:(Character cr)
-        from:firstLine to:lastLine
-        compressTabs:compressTabs
-        final:(withCR ifTrue:[Character cr] ifFalse:[nil])
+        asStringWith:Character cr
+        from:1 to:(self size) 
+        compressTabs:false 
+        final:Character cr
+        withEmphasis:false
+
+    "Created: / 17.6.1998 / 12:32:48 / cg"
+!
+
+asVersionNumberCollection
+    "Convert a collection of strings or numbers to a version number.
+     Remove zeroes from the end."
+
+    |coll trailingZerosCount|
+
+    coll := self collect:[:each| each isInteger 
+                                        ifTrue:[each] 
+                                        ifFalse:[Integer readFromString:each onError:each]
+                            ] as:Array.
+
+    coll last == 0 ifTrue:[
+        trailingZerosCount := 0.
+        coll reversed doWhileTrue:[:each |
+            each == 0 ifTrue:[
+                trailingZerosCount := trailingZerosCount + 1.
+                true.
+            ] ifFalse:[
+                false
+            ].
+        ].
+
+        trailingZerosCount ~~ 0 ifTrue:[
+            coll := coll copyTo:coll size - trailingZerosCount
+        ].
+    ].
+
+    ^ coll
+
+   "
+     #(1) asVersionNumberCollection.
+     #(1 '1') asVersionNumberCollection.
+     #(1 '1a') asVersionNumberCollection.
+     #(1 1 0) asVersionNumberCollection.
+     #('expecco' 18 10) asVersionNumberCollection.
+    "
+
+    "Created: / 20-06-2018 / 17:33:26 / Stefan Vogel"
 !
 
 decodeAsLiteralArray
@@ -2989,52 +2993,6 @@
     "Modified: / 26-03-2007 / 13:57:10 / cg"
 !
 
-from:firstLine to:lastLine asStringWith:sepCharacterOrString
-    "return part of myself as a string with embedded sepCharacterOrStrings.
-     The argument sepCharacterOrString may be a character, a string or nil.
-     My elements must be strings or nil; nil entries and empty strings are
-     taken as empty lines."
-
-    ^ self
-        from:firstLine
-        to:lastLine
-        asStringWith:sepCharacterOrString
-        compressTabs:false
-        final:nil
-    "
-     creating entries for searchpath:
-
-     #('foo' 'bar' 'baz' '/foo/bar') asStringWith:$;
-
-     #('foo' 'bar' 'baz' '/foo/bar') from:1 to:3 asStringWith:$:
-
-     (#('foo' 'bar' 'baz' '/foo/bar') copyFrom:1 to:3) asStringWith:$:
-    "
-
-    "Modified: 23.2.1996 / 15:28:55 / cg"
-!
-
-from:firstLine to:lastLine asStringWith:sepCharacterOrString compressTabs:compressTabs final:endCharacterOrString
-    "return part of myself as a string or text with embedded sepCharacters.
-     My elements must be strings or nil; nil entries and empty strings are
-     taken as empty lines.
-     The arguments sepCharacterOrString and endCharacterOrString may be characters,
-     strings or nil.
-     If the argument compressTabs is true, leading spaces are converted
-     to tab-characters (8col tabs). 
-     The last line is followed by a final character (if non-nil)."
-
-    ^ self
-        from:firstLine
-        to:lastLine
-        asStringWith:sepCharacterOrString 
-        compressTabs:compressTabs
-        final:endCharacterOrString 
-        withEmphasis:true
-
-    "Modified: / 17.6.1998 / 12:31:19 / cg"
-!
-
 from:firstLine to:lastLine asStringWith:sepCharacterOrString compressTabs:compressTabs final:endCharacterOrString withEmphasis:withEmphasis
     "return part of myself as a string or text with embedded sepCharacterOrString
      and followup endCharacterOrString.
@@ -3055,7 +3013,7 @@
      totalLength "{ Class:SmallInteger }"
      pos         "{ Class:SmallInteger }"
      sepCnt      "{ Class:SmallInteger }"
-     newString lineString spaces idx nTabs
+     newString lineString idx nTabs
      maxBitsPerCharacter stringClass needEmphasis newRuns c
      thisLen anyTab|
 
@@ -3083,8 +3041,8 @@
     idx2 := lastLine.
     idx1 to:idx2 do:[:lineIndex |
         lineString := self at:lineIndex.
-
         lineString notNil ifTrue:[
+            lineString := lineString asString.
             withEmphasis ifTrue:[
                 lineString hasChangeOfEmphasis ifTrue:[
                     needEmphasis := true
@@ -3116,7 +3074,6 @@
     ].
     totalLength <= 0 ifTrue:[^ ''].
 
-    spaces := '        '.
     newString := stringClass new:totalLength.
 
     needEmphasis ifTrue:[
@@ -3132,6 +3089,9 @@
     pos := 1.
     idx1 to:idx2 do:[:lineIndex |
         lineString := self at:lineIndex.
+        lineString notNil ifTrue:[
+            lineString := lineString asString.
+        ].
         thisLen := lineString size.
         thisLen ~~ 0 ifTrue:[
             withEmphasis ifFalse:[
@@ -3288,89 +3248,24 @@
 
     "
 
-    "Created: / 17.6.1998 / 12:30:32 / cg"
-    "Modified: / 17.6.1998 / 12:31:59 / cg"
-!
-
-joinWithAll:separatingCollection
-    "return a collection generated by concatenating my elements
-     and slicing separatingCollection in between.
-     Similar to asStringWith:, but not specifically targeted towards collections of strings."
-
-    ^ self
-        joinWithAll:separatingCollection
-        from:1 to:(self size) as:nil
-
-    "
-     #('hello' 'world' 'foo' 'bar' 'baz') joinWithAll:' ; '   
-     #('hello' 'world' 'foo' 'bar' 'baz') joinWithAll:' | '
-    "
-!
-
-joinWithAll:separatingCollection from:startIndex to:endIndex as:speciesOrNil 
-    "extract parts of myself as a new collection with optional embedded separator.
-     Separator may be nil, or a collection of elements to be sliced in between.
-     SpeciesOrNil specifies the species of the resultig object, allowing for Arrays to be converted
-     as OrderedCollection or vice versa on the fly. If nil is passed in, the species of the first non-nil
-     element is used.
-     This counts the overall size first, then allocates the new collection once and replaces elements
-     via bulk copies. For very small collections, it may be faster to use the comma , operation.
-     Similar to asStringWith:, but not specifically targeted towards string handling."
-
-    |totalLength "{ Class:SmallInteger }"
-     pos         "{ Class:SmallInteger }"
-     sepCnt      "{ Class:SmallInteger }"
-     subColl newColl 
-     species|
-
-    startIndex = endIndex ifTrue:[ ^ self at:startIndex ].
-
-    species := speciesOrNil.
-
-    "
-     first accumulate the size of the result, 
-     to avoid countless reallocations.
-    "
-    totalLength := 0.
-    sepCnt := separatingCollection size.
-
-    startIndex to:endIndex do:[:index |
-        subColl := self at:index.
-        totalLength := totalLength + subColl size.
-        species isNil ifTrue:[
-            subColl notNil ifTrue:[
-                species := subColl species
-            ]
-        ]
-    ].
-    totalLength := totalLength + ((endIndex - startIndex) * sepCnt).
-    newColl := species newWithSize:totalLength.
-
-    pos := 1.
-    startIndex to:endIndex do:[:index |
-        subColl := self at:index.
-        subColl size ~~ 0 ifTrue:[
-            newColl replaceFrom:pos with:subColl startingAt:1.
-            pos := pos + subColl size.
-        ].
-        ((sepCnt ~~ 0) and:[index ~~ endIndex]) ifTrue:[
-            newColl replaceFrom:pos to:(pos+sepCnt-1) with:separatingCollection startingAt:1.
-            pos := pos + sepCnt.
-        ].
-    ].
-
-    ^ newColl
-
-    "
-     #( 'aa' 'bb' '' 'cc' ) joinWith:'|' from:1 to:4 as:String  
-     #( 'aa' 'bb' '' 'cc' ) joinWith:nil from:1 to:4 as:String  
-     #( 'aa' 'bb' '' 'cc' ) joinWith:'|' from:1 to:4 as:Array   
-     #( (1 2 3) (4 5 6) (7 6 8) ) joinWith:#(nil) from:1 to:3 as:OrderedCollection  
-     #( (1 2 3) (4 5 6) (7 6 8) ) joinWith:nil from:1 to:3 as:nil                
-    "
-
-    "Created: / 17.6.1998 / 12:30:32 / cg"
-    "Modified: / 17.6.1998 / 12:31:59 / cg"
+    "Created: / 17-06-1998 / 12:30:32 / cg"
+    "Modified: / 21-09-2017 / 12:46:43 / stefan"
+    "Modified: / 22-02-2019 / 09:51:42 / Stefan Vogel"
+!
+
+pairsAsDictionary
+    "return a new Dictionary with the receiver collection's elements,
+     each of which must be a SequenceableCollection with two elements"
+
+    ^ OrderedDictionary withKeyValuePairs:self.
+
+    "
+     #( ('ten' 10) ('twenty' 20) ('thirty' 30)) asSet pairsAsDictionary 
+     #( ('ten' 10) ('twenty' 20) ('thirty' 30)) pairsAsDictionary 
+    "
+
+    "Created: / 11-09-2018 / 12:30:16 / Stefan Vogel"
+    "Modified (comment): / 11-09-2018 / 15:37:18 / Stefan Vogel"
 !
 
 splitBy:anElement
@@ -3387,90 +3282,44 @@
      '1 one 2 two 3 three 4 four 5 five' withCRs splitBy:Character space
      #(a b c d e f g h) splitBy: #d.
      #(a b c d e f d d g h) splitBy: #d.
-    "
-!
-
-splitBy:anElement do:aBlock
-    "evaluate aBlock for each subcollection generated by separating elements
-     of the receiver by anElement.
-     If anElement occurs multiple times in a row,
-     the block will be invoked with empty collections as argument.
-     This algorithm uses equality-compare to detect the element."
-
-    ^ self asCollectionOfSubCollectionsSeparatedBy:anElement do:aBlock
-
-    "
-     '' 
-        splitBy:$: do:[:each | Transcript showCR:each storeString]
-
-     '1 one' 
-        splitBy:$: do:[:each | Transcript showCR:each storeString]
-
-     '1 one:2 two:3 three:4 four:5 five' 
-        splitBy:$: do:[:each | Transcript showCR:each storeString]
-
-     'a::b' 
-        splitBy:$: do:[:each | Transcript showCR:each storeString]
-
-     ':' 
-        splitBy:$: do:[:each | Transcript showCR:each storeString]
-
-     ':a' 
-        splitBy:$: do:[:each | Transcript showCR:each storeString]
-
-     'a:' 
-        splitBy:$: do:[:each | Transcript showCR:each storeString]
-    "
+     'a;b;c;d' splitBy: $;.
+    "
+
+    "Modified (comment): / 02-10-2018 / 17:30:21 / Claus Gittinger"
 !
 
 splitByAll:aSeparatorCollection
     "return a collection containing the subcollections (separated by aSeparatorCollection)
      of the receiver. If aSeparatorCollection occurs multiple times in a row,
      the result will contain empty strings.
-     Uses equality-compare when searching for aSeparatorCollection."
+     Uses equality-compare when searching for aSeparatorCollection.
+     More or less the same as spitOn: (but has been around earlier)"
 
     ^ self asCollectionOfSubCollectionsSeparatedByAll:aSeparatorCollection
 
     "
-     '1::2::3::4::5::' splitByAll:'::'
+     '1::2::3::4::5::' splitByAll:'::'   
      #(1 2 3 1 2 3 4 3 1 1 2 3 1 4 5) splitByAll:#(3 1)
     "
-!
-
-splitByAny:aCollectionOfSeparators
-    "return a collection containing the subCollection
-     (separated by any from aCollectionOfSeparators) of the receiver.
-     This allows breaking up strings using a number of elements as separator.
-     Uses equality-compare when searching for separators."
-
-    ^ self asCollectionOfSubCollectionsSeparatedByAny:aCollectionOfSeparators
-
-    "
-     'hello:world:isnt:this nice' splitByAny:#($:)
-     'hello:world:isnt:this nice' splitByAny:':'
-     'hello:world:isnt:this nice' splitByAny:(Array with:$: with:Character space)
-     'hello:world:isnt:this nice' splitByAny:#( $: $ ) 
-     'hello:world:isnt:this nice' splitByAny:{ $: . $ }
-     'hello:world:isnt:this nice' splitByAny:': '
-     'h1e2l3l4o' splitByAny:($1 to: $9)
-     #(1 9 2 8 3 7 4 6 5 5) splitByAny:#(1 2 3)
-    "
-!
-
-splitByAnyForWhich:aBlock
+    "
+     '1::2::3::4::5::' splitOn:'::'
+    "
+!
+
+splitByAnyForWhich:aBlock 
     "return a collection containing the subCollection
      (separated by elements for which aBlock evaluates to true) of the receiver.
      This allows breaking up strings using an arbitrary condition."
-    
-    ^ self asCollectionOfSubCollectionsSeparatedByAnyForWhich:aBlock      
+
+    ^ self asCollectionOfSubCollectionsSeparatedByAnyForWhich:aBlock
 
     "
      'hello:world:isnt:this nice' splitByAnyForWhich:[:ch | ch = $:]
      'h1e2l3l4o' splitByAnyForWhich:[:ch | ch isDigit]
-     #(1 9 2 8 3 7 4 6 5 5 1 2 9) splitByAnyForWhich:[:n | n odd]
-    "
-
-    "Modified (format): / 29-05-2018 / 13:24:06 / svestkap"
+     #(1 9 2 8 3 7 4 6 5 5) splitByAnyForWhich:[:n | n odd]
+    "
+
+    "Modified: / 22-02-2019 / 11:59:30 / Stefan Vogel"
 !
 
 splitByAnyForWhich:aBlock withSeparatorIncluded:aBoolean
@@ -3492,44 +3341,34 @@
     "Created: / 29-05-2018 / 14:01:45 / svestkap"
 !
 
-splitForSize:pieceSize
-    "slice into pieces; return a collection containing pieces of size pieceSize from the receiver.
-     The last piece may be smaller, if the receiver's size is not a multiple of pieceSize."
-
-    ^ self asCollectionOfSubCollectionsOfSize:pieceSize
-
-    "
-     '123123123123123123' splitForSize:3 
-     '12312312312312312312' splitForSize:3 
-    "
-!
-
 subCollections:aBlock 
-    "Answser an ordered collection of ordered collections
+    "Answer an ordered collection of ordered collections
      where each subcollection is delimited by an element of the receiver
      for which the given block evaluates to true."
     
-    |str answer current e|
+    |str answer currentSubCollection currentElement|
 
     str := self readStream.
     answer := OrderedCollection new.
-    current := OrderedCollection new.
+    currentSubCollection := OrderedCollection new.
     [ str atEnd ] whileFalse:[
-        e := str next.
-        current add:e.
-        (aBlock value:e) ifTrue:[
-            answer add:current.
-            current := OrderedCollection new
+        currentElement := str next.
+        currentSubCollection add:currentElement.
+        (aBlock value:currentElement) ifTrue:[
+            answer add:currentSubCollection.
+            currentSubCollection := OrderedCollection new
         ]
     ].
-    current notEmpty ifTrue:[
-        answer add:current
+    currentSubCollection notEmpty ifTrue:[
+        answer add:currentSubCollection
     ].
     ^ answer
 
     "
      #( 1 2 3 nil 4 5 6 nil 7 8 9 nil ) subCollections:[:el | el isNil].
     "
+
+    "Modified (comment): / 17-03-2017 / 18:10:56 / stefan"
 ! !
 
 !SequenceableCollection methodsFor:'converting-reindexed'!
@@ -3541,13 +3380,11 @@
      however, physically, no copy is made).
      Warning:
         The slice SHARES the memory for the element-data with the original,
-        this means that any modifications in the original are visible in the slice
-        and vice versa."
-
-    ^ self
-        from:startIndex
-        to:self size
-        by:1
+        it is like a readOnly pointer INTO the receiver.
+        This means that any modifications in the original are visible in the slice
+        and vice versa (well, no: because the slice is readOnly)."
+
+    ^ self from:startIndex to:(self size) by:1
 
     "
      #(1 2 3 4 5 6 7) from:3
@@ -3555,6 +3392,9 @@
      ( #(1 2 3 4 5 6 7) from:3 ) last
      ( #(1 2 3 4 5 6 7) from:3 ) size
     "
+
+    "Modified (comment): / 22-02-2017 / 10:49:20 / cg"
+    "Modified (comment): / 08-03-2019 / 13:29:39 / Claus Gittinger"
 !
 
 from:startIndex by:step
@@ -3564,8 +3404,9 @@
      however, physically, no copy is made).
      Warning:
         The slice SHARES the memory for the element-data with the original,
-        this means that any modifications in the original are visible in the slice
-        and vice versa."
+        it is like a readOnly pointer INTO the receiver.
+        This means that any modifications in the original are visible in the slice
+        and vice versa (well, no: because the slice is readOnly)."
 
     ^ self
         from:startIndex
@@ -3579,7 +3420,11 @@
      ( #(1 2 3 4 5 6 7) from:3 by:2) first
      ( #(1 2 3 4 5 6 7) from:3 by:2) last
      ( #(1 2 3 4 5 6 7) from:3 by:2) size
-    "
+     ( #(1 2 3 4 5 6 7) from:5 by:-2)
+    "
+
+    "Modified (comment): / 22-02-2017 / 10:51:44 / cg"
+    "Modified (comment): / 08-03-2019 / 13:27:49 / Claus Gittinger"
 !
 
 from:startIndex count:numberOfElements
@@ -3587,12 +3432,13 @@
      in the receiver starting at index start (i.e. reference to a slice).
      Warning:
         The slice SHARES the memory for the element-data with the original,
-        this means that any modifications in the original are visible in the slice
-        and vice versa."
-
-    ^ self
-        from:startIndex
-        to:(startIndex + numberOfElements - 1)
+        it is like a readOnly pointer INTO the receiver.
+        This means that any modifications in the original are visible in the slice
+        and vice versa (well, no: because the slice is readOnly)."
+
+    ^ self 
+        from:startIndex to:(startIndex + numberOfElements - 1)
+        by:1 
 
     "
      #($a $b $c $d $e $f $g) from:2 count:3
@@ -3618,24 +3464,8 @@
      slice at:1 put:40.
      slice.
     "
-!
-
-from:startIndex count:numberOfElements put:newElement
-    "replace numberOfElements elements from startIndex of the collection
-     by the argument, newElement.
-     Notice: This operation modifies the receiver, NOT a copy;
-     therefore the change may affect all others referencing the receiver."
-
-    ^ self
-        from:startIndex
-        to:(startIndex + numberOfElements - 1)
-        put:newElement
-
-    "
-     #($a $b $c $d $e $f $g) copy from:2 count:3 put:nil
-     '1234567890' copy from:2 count:5 put:$*
-     '1234567890' copy from:2 count:20 put:$* -> error
-    "
+
+    "Modified (comment): / 08-03-2019 / 13:29:33 / Claus Gittinger"
 !
 
 from:startIndex to:endIndex
@@ -3645,8 +3475,9 @@
      however, physically, no copy is made).
      Warning:
         The slice SHARES the memory for the element-data with the original,
-        this means that any modifications in the original are visible in the slice
-        and vice versa."
+        it is like a readOnly pointer INTO the receiver.
+        This means that any modifications in the original are visible in the slice
+        and vice versa (well, no: because the slice is readOnly)."
 
     ^ self
         from:startIndex
@@ -3660,7 +3491,15 @@
      ( #(1 2 3 4 5 6 7) from:3 to:6) first
      ( #(1 2 3 4 5 6 7) from:3 to:6) last
      ( #(1 2 3 4 5 6 7) from:3 to:6) size
-    "
+     
+     #(1 2 3 4 5 6 7) from:3 to:1
+     ( #(1 2 3 4 5 6 7) from:3 to:1 ) first
+     ( #(1 2 3 4 5 6 7) from:3 to:1 ) last
+     ( #(1 2 3 4 5 6 7) from:3 to:1 ) size
+    "
+
+    "Modified (comment): / 22-02-2017 / 10:51:06 / cg"
+    "Modified (comment): / 08-03-2019 / 13:28:01 / Claus Gittinger"
 !
 
 from:startIndex to:endIndex by:step
@@ -3670,8 +3509,9 @@
      however, physically, no copy is made).
      Warning:
         The slice SHARES the memory for the element-data with the original,
-        this means that any modifications in the original are visible in the slice
-        and vice versa."
+        it is like a readOnly pointer INTO the receiver.
+        This means that any modifications in the original are visible in the slice
+        and vice versa (well, no: because the slice is readOnly)."
 
     ^ ReindexedCollection
         on:self
@@ -3684,7 +3524,12 @@
      ( #(1 2 3 4 5 6 7) from:3 to:7 by:2) first
      ( #(1 2 3 4 5 6 7) from:3 to:7 by:2) last
      ( #(1 2 3 4 5 6 7) from:3 to:7 by:2) size
-    "
+
+     ( #(1 2 3 4 5 6 7) from:7 to:3 by:-2) 
+    "
+
+    "Modified (comment): / 22-02-2017 / 10:52:25 / cg"
+    "Modified (comment): / 08-03-2019 / 13:28:06 / Claus Gittinger"
 !
 
 to:endIndex
@@ -3694,13 +3539,11 @@
      however, physically, no copy is made).
      Warning:
         The slice SHARES the memory for the element-data with the original,
-        this means that any modifications in the original are visible in the slice
-        and vice versa."
-
-    ^ self
-        from:1
-        to:endIndex
-        by:1
+        it is like a readOnly pointer INTO the receiver.
+        This means that any modifications in the original are visible in the slice
+        and vice versa (well, no: because the slice is readOnly)."
+
+    ^ self from:1 to:endIndex by:1
 
     "
      #(1 2 3 4 5 6 7) to:4
@@ -3708,6 +3551,9 @@
      ( #(1 2 3 4 5 6 7) to:4) last
      ( #(1 2 3 4 5 6 7) to:4) size
     "
+
+    "Modified (comment): / 22-02-2017 / 10:49:38 / cg"
+    "Modified (comment): / 08-03-2019 / 13:27:37 / Claus Gittinger"
 !
 
 to:endIndex by:step
@@ -3717,8 +3563,9 @@
      however, physically, no copy is made).
      Warning:
         The slice SHARES the memory for the element-data with the original,
-        this means that any modifications in the original are visible in the slice
-        and vice versa."
+        it is like a readOnly pointer INTO the receiver.
+        This means that any modifications in the original are visible in the slice
+        and vice versa (well, no: because the slice is readOnly)."
 
     ^ self
         from:(step > 0
@@ -3732,7 +3579,11 @@
      ( #(1 2 3 4 5 6 7) to:4 by:2) first
      ( #(1 2 3 4 5 6 7) to:4 by:2) last
      ( #(1 2 3 4 5 6 7) to:4 by:2) size
-    "
+     #(1 2 3 4 5 6 7) to:1 by:-1
+    "
+
+    "Modified (comment): / 22-02-2017 / 10:50:18 / cg"
+    "Modified (comment): / 08-03-2019 / 13:28:32 / Claus Gittinger"
 ! !
 
 !SequenceableCollection methodsFor:'copying'!
@@ -3860,6 +3711,31 @@
     "
 !
 
+copyAfterAll:aCollectionOfElements
+    "Answer a copy of the receiver from after the first occurrence
+     of aCollectionOfElements up to the end. 
+     If no such subsequence exists, answer an empty copy."
+
+    |idx|
+
+    aCollectionOfElements isEmpty ifTrue:[^ self].
+    idx := self indexOfSubCollection:aCollectionOfElements.
+    idx == 0 ifTrue:[idx := self size].
+    ^ self copyFrom:idx + aCollectionOfElements size
+
+    "
+     'hello world' copyAfterAll:'bla'
+     'hello world' copyAfterAll:'hello'
+     '123456123456' copyAfterAll:#($1 $2 $3)
+     #(1 2 3 4 1 2 3 3 4 5 6) copyAfterAll:#(2 3 4) 
+     #(1 2 3 4 1 2 3 3 4 5 6) copyAfterAll:#()
+     #(1 2 3 4 1 2 3 3 4 5 6) copyAfterAll:#(6)
+     #(1 2 3 4 1 2 3 3 4 5 6) copyAfterAll:#(7)
+    "
+
+    "Created: / 01-11-2018 / 12:23:17 / Claus Gittinger"
+!
+
 copyAfterLast:element
     "return a copy of the receiver from (but excluding) the last occurrence
      of element to the end; uses = for comparison"
@@ -3879,6 +3755,31 @@
     "
 !
 
+copyAfterLastAll:aCollectionOfElements
+    "Answer a copy of the receiver from after the last occurrence
+     of aCollectionOfElements up to the end. 
+     If no such subsequence exists, answer an empty copy."
+
+    |idx|
+
+    aCollectionOfElements isEmpty ifTrue:[^ #()].
+    idx := self lastIndexOfSubCollection:aCollectionOfElements.
+    idx == 0 ifTrue:[idx := self size].
+    ^ self copyFrom:idx + aCollectionOfElements size
+
+    "
+     'hello world' copyAfterLastAll:'bla'
+     'hello world' copyAfterLastAll:'hello'
+     '123456123456' copyAfterLastAll:#($1 $2 $3)
+     #(1 2 3 4 1 2 3 3 4 5 6) copyAfterLastAll:#(2 3 4) 
+     #(1 2 3 4 1 2 3 3 4 5 6) copyAfterLastAll:#()
+     #(1 2 3 4 1 2 3 3 4 5 6) copyAfterLastAll:#(6)
+     #(1 2 3 4 1 2 3 3 4 5 6) copyAfterLastAll:#(7)
+    "
+
+    "Created: / 01-11-2018 / 12:25:58 / Claus Gittinger"
+!
+
 copyButFirst
     "return a new collection consisting of the receiver's elements
      except for the first element.
@@ -3917,6 +3818,25 @@
     "
 !
 
+copyButFirstAndLast
+    "return a new collection consisting of the receiver's elements
+     except for the first and last elements.
+     Raises an error if the receiver's size is less than 2."
+
+    |sz|
+
+    (sz := self size) < 2 ifTrue:[self notEnoughElementsError].
+    ^ self copyFrom:2 to:(sz - 1)
+
+    "
+     #($a $b $c $d $e $f $g) copyButFirstAndLast
+     '1234567890' copyButFirstAndLast
+     '1' copyButFirstAndLast
+     '' copyButFirstAndLast
+     'ab' copyButFirstAndLast
+    "
+!
+
 copyButLast
     "return a new collection consisting of the receiver's elements
      except for the last element.
@@ -4007,6 +3927,29 @@
     "Modified: 20.2.1997 / 14:23:01 / cg"
 !
 
+copyFrom:startIndex through:anElement
+    "return a new collection consisting of receiver's elements from startIndex
+     up to (and including) the next occurence of anElement.
+     Return the remaining elements (up to the end), if anElement is not found. 
+     Return an empty collection, if startIndex is beyond the receiver's size."
+
+    |endIndex|
+
+    endIndex := self indexOf:anElement startingAt:startIndex+1.
+    endIndex == 0 ifTrue:[
+        ^ self copyFrom:startIndex
+    ].        
+    ^ self copyFrom:startIndex to:endIndex
+
+    "
+     #($a $b $c $d $e $f $g) copyFrom:2 through:$f
+     '1234567890' copyFrom:2 through:$8
+     (10 to:19) copyFrom:5 through:18
+    "
+
+    "Created: / 09-11-2018 / 09:37:10 / Claus Gittinger"
+!
+
 copyFrom:startIndex to:stopIndex
     "return a new collection consisting of receiver's elements
      between start and stop.
@@ -4015,6 +3958,13 @@
 
     |newCollection newSize|
 
+    (startIndex < 1) ifTrue:[
+        self subscriptBoundsError:'startindex out of bounds' 
+    ].
+    (stopIndex > self size) ifTrue:[
+        self subscriptBoundsError:'stopindex out of bounds' 
+    ].
+
     newSize := stopIndex - startIndex + 1.
     newSize <= 0 ifTrue:[
         ^ self copyEmpty:0.
@@ -4034,6 +3984,29 @@
     "
 !
 
+copyFrom:startIndex upTo:anElement
+    "return a new collection consisting of receiver's elements from startIndex
+     up to (but excluding) the next occurence of anElement.
+     Return the remaining elements (up to the end), if anElement is not found. 
+     Return an empty collection, if startIndex is beyond the receiver's size."
+
+    |endIndex|
+
+    endIndex := self indexOf:anElement startingAt:startIndex+1.
+    endIndex == 0 ifTrue:[
+        ^ self copyFrom:startIndex
+    ].        
+    ^ self copyFrom:startIndex to:(endIndex - 1)
+
+    "
+     #($a $b $c $d $e $f $g) copyFrom:2 upTo:$f
+     '1234567890' copyFrom:2 upTo:$8
+     (10 to:19) copyFrom:5 upTo:18
+    "
+
+    "Created: / 09-11-2018 / 09:36:11 / Claus Gittinger"
+!
+
 copyLast:count
     "return a new collection consisting of the receiver's last count elements.
      Raises an error if there are not enough elements.
@@ -4169,6 +4142,49 @@
     "Modified (comment): / 16-11-2016 / 21:35:36 / cg"
 !
 
+copyReplaceAllForWhich:checkBlock with:newElement
+    "return a copy of the receiver, where all elements for which checkBlock
+     returns true will are replaced by newElement."
+
+    ^ (self copyFrom:1) replaceAllForWhich:checkBlock with:newElement
+
+    "
+     #(1 2 1 3 4 5 1 2 1 2) copyReplaceAllForWhich:[:e | e > 2] with:99
+     'hello 1234 world' copyReplaceAllForWhich:#isDigit with:$*
+    "
+!
+
+copyReplaceAllSubcollections:subColl with:newColl
+    "return a copy of the receiver, with all sequences of subColl replaced
+     by newColl (i.e. slice in the newColl in place of the subColl)."
+
+    |tmpStream idx idx1|
+
+    tmpStream := self species writeStream.
+    idx := 1.
+    [idx ~~ 0] whileTrue:[
+        idx1 := idx.
+        idx := self indexOfSubCollection:subColl startingAt:idx.
+        idx ~~ 0 ifTrue:[
+            tmpStream nextPutAll:(self copyFrom:idx1 to:idx-1).
+            tmpStream nextPutAll:newColl.
+            idx := idx + subColl size
+        ]
+    ].
+    tmpStream nextPutAll:(self copyFrom:idx1).
+    ^ tmpStream contents
+
+   "
+     #[1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0] copyReplaceAllSubcollections:#[1 2 3] with:#[9 8 7]
+
+     '12345678901234567890' copyReplaceAllSubcollections:'123' with:'OneTwoThree'
+     '12345678901234567890' copyReplaceAllSubcollections:'123' with:'*'
+     '12345678901234567890' copyReplaceAllSubcollections:'234' with:'foo'
+    "
+
+    "Created: / 01-08-2017 / 23:21:11 / cg"
+!
+
 copyReplaceAny:collectionOfOldElements with:newElement
     "return a copy of the receiver, where all elements equal to any in collectionOfOldElements
      have been replaced by newElement."
@@ -4246,6 +4262,67 @@
     "
 !
 
+copyReplacePrefix:oldPrefix with:newPrefix
+    "if the receiver startsWith oldPrefix, return a copy with that suffix replaced by newPrefix.
+     Otherwise return the receiver"
+
+    (self startsWith:oldPrefix) ifTrue:[
+        ^ newPrefix,(self copyFrom:oldPrefix size+1)
+    ].
+    ^ self
+
+    "
+     'helloworld' copyReplacePrefix:'hello' with:'Hello'  
+     'foo class' copyReplacePrefix:'foo' with:'bar'      
+     'ffoo class' copyReplacePrefix:'foo' with:'bar'      
+    "
+
+    "Created: / 12-06-2020 / 14:31:45 / cg"
+!
+
+copyReplaceSubcollection:subColl with:newColl
+    "return a copy of the receiver, with the first occurrence of
+     the subColl sequence replaced by newColl 
+     (i.e. slice in the newColl in place of the first subColl)."
+
+    |idx|
+
+    idx := self indexOfSubCollection:subColl startingAt:1.
+    idx ~~ 0 ifTrue:[
+        ^ (self copyTo:idx-1),newColl,(self copyFrom:idx+subColl size)
+    ].
+    ^ self
+
+   "
+     #[1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0] copyReplaceSubcollection:#[1 2 3] with:#[9 8 7]
+     #[0 0 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0] copyReplaceSubcollection:#[1 2 3] with:#[9 8 7]
+
+     '12345678901234567890' copyReplaceSubcollection:'123' with:'OneTwoThree'
+     '12345678901234567890' copyReplaceSubcollection:'123' with:'*'
+     '12345678901234567890' copyReplaceSubcollection:'234' with:'foo'
+    "
+
+    "Created: / 01-08-2017 / 23:21:42 / cg"
+!
+
+copyReplaceSuffix:oldSuffix with:newSuffix
+    "if the receiver endsWith oldSuffix, return a copy with that suffix replaced by newSuffix.
+     Otherwise return the receiver"
+
+    (self endsWith:oldSuffix) ifTrue:[
+        ^ (self copyButLast:oldSuffix size),newSuffix
+    ].
+    ^ self
+
+    "
+     'helloworld' copyReplaceSuffix:'world' with:'welt'  
+     'foo class' copyReplaceSuffix:' class' with:'Class'     
+     'foo xlass' copyReplaceSuffix:' class' with:'Class'     
+    "
+
+    "Created: / 12-06-2020 / 14:31:55 / cg"
+!
+
 copyReplacing:oldElement withObject:newElement
     "return a copy of the receiver, where all elements equal to oldElement
      have been replaced by newElement.
@@ -4292,7 +4369,7 @@
 copyToMax:stop
     "return a new collection consisting of receiver's elements
      from 1 up to (including) index stop, or up to the receiver's end,
-     whichever is smaller (i.e. like copyTo:, but do not err if receiver is smaller"
+     whichever is smaller (i.e. like copyTo:, but do not err if receiver is smaller)"
 
     ^ self copyFrom:1 to:(self size min:stop)
 
@@ -4300,6 +4377,32 @@
      #($a $b $c $d $e $f $g) copyTo:10  - raises an error
      #($a $b $c $d $e $f $g) copyToMax:10
     "
+
+    "Modified (comment): / 09-06-2018 / 13:07:10 / Claus Gittinger"
+!
+
+copyToMax:stop ifLargerCopyWith:whatToAppendIfLarger
+    "return a new collection consisting of receiver's elements
+     from 1 up to (including) index stop, or up to the receiver's end,
+     whichever is smaller (i.e. like copyTo:, but do not err if receiver is smaller).
+     If the copy is smaller, append the element whatToAppendIfLarger.
+     This is useful to cut off long texts and mark it with '...' at the end
+     as seen in the example below."
+
+    |copySize copy|
+
+    copySize := (self size min:stop).
+    copy := self copyFrom:1 to:copySize.
+    copySize < self size ifTrue:[
+        copy := copy copyWith:whatToAppendIfLarger
+    ].
+    ^ copy
+
+    "
+     (#('one' 'two' 'three' 'four') copyToMax:2 ifLargerCopyWith:'...')
+    "
+
+    "Created: / 09-06-2018 / 13:06:39 / Claus Gittinger"
 !
 
 copyTransliterating:oldElements to:newElements
@@ -4661,6 +4764,16 @@
     "
 !
 
+copyValuesFrom:startIndex 
+    "Return a copy of the receiver that contains values from 
+     position startIndex to the end.
+     For compatibility with OrderedDictionary protocol."
+
+    ^ self copyFrom:startIndex to:self size
+
+    "Created: / 26-02-2019 / 02:24:39 / Claus Gittinger"
+!
+
 copyWith:newElement
     "return a new collection containing the receiver's elements
      and the single new element, newElement.
@@ -4702,7 +4815,7 @@
 
 copyWith:newElement insertedBeforeIndex:index
     "return a new collection with newElement inserted after index.
-     With a 0 index, newElement is prepended;  
+     With a 1 index, newElement is prepended;  
      if index is my size, it is appended.
      The receiver remains unchanged"
 
@@ -4715,6 +4828,8 @@
      #(1 2 3 4 5 6 7 8 9 0 1 2 3 4) copyWith:#a insertedBeforeIndex:15 
      #(1 2 3 4 5 6 7 8 9 0 1 2 3 4) copyWith:#a insertedBeforeIndex:16  --> error
     "
+
+    "Modified (comment): / 16-02-2017 / 14:37:58 / stefan"
 !
 
 copyWithAll:aCollection insertedAfterIndex:index
@@ -4761,14 +4876,14 @@
      This is different from concatentation, which expects two collections
      as argument, but equivalent to copy-and-addFirst."
 
-    |newCollection mySize newSize|
-
-    mySize := self size.
-    newSize := mySize + 1.
+    |newCollection newSize|
+
+    newSize := self size + 1.
     newCollection := self copyEmptyAndGrow:newSize.
-    newCollection replaceFrom:2 to:mySize+1 with:self startingAt:1.
-    newCollection at:1 put:newFirstElement.
-    ^newCollection
+    ^ newCollection 
+        at:1 put:newFirstElement;
+        replaceFrom:2 to:newSize with:self startingAt:1;
+        yourself.
 
     "
      #(1 2 3 4 5) copyWithFirst:$a  
@@ -4776,6 +4891,8 @@
      'abcdefg' copyWithFirst:'123'   -- will fail: string cannot be stored into string
      'abcdefg' copyWithFirst:1       -- will fail: integer cannot be stored into string
     "
+
+    "Modified: / 16-02-2017 / 14:45:03 / stefan"
 !
 
 copyWithout:elementToSkip
@@ -4788,7 +4905,7 @@
      srcIndex  "{ Class: SmallInteger }"
      dstIndex  "{ Class: SmallInteger }"
      skipIndex "{ Class: SmallInteger }"
-     copy l|
+     copy len|
 
     "the code below may look like overkill,
      however, for big collections its better to move data
@@ -4805,6 +4922,63 @@
 
     n timesRepeat:[
         skipIndex := self indexOf:elementToSkip startingAt:srcIndex.
+        len := skipIndex - srcIndex.
+        len ~~ 0 ifTrue:[
+            copy replaceFrom:dstIndex to:(dstIndex + len - 1)
+                        with:self startingAt:srcIndex.
+            dstIndex := dstIndex + len
+        ].
+        srcIndex := skipIndex + 1
+    ].
+    len := sz - srcIndex.
+    copy replaceFrom:dstIndex to:(dstIndex + len)
+                with:self startingAt:srcIndex.
+    ^ copy
+
+    "
+     #($a $b $c $d $e $f $g) copyWithout:$d
+     #($a $b $c $d $e $f $g) copyWithout:$a
+     #($a $b $c $d $e $f $g) copyWithout:$g
+     'abcdefghi' copyWithout:$h
+     'abcdefg' copyWithout:$h
+     'abcdefabcghi' copyWithout:$a
+     'abcdefabcg' copyWithout:$h
+     #($a $b $c $a $a $d $e $a $f $g) copyWithout:$a
+     #($a $b $c $d $e $f $g) copyWithout:$x
+     #(90 80 70 60 50) copyWithout:70
+     #(90 80 70 80 60 45 80 50) copyWithout:80
+    "
+
+    "Modified (format): / 24-10-2017 / 14:45:53 / cg"
+!
+
+copyWithoutAll:elementsToSkip
+    "return a new collection consisting of a copy of the receiver, with
+     ALL elements equal to any in elementsToSkip are left out.
+     No error is reported, if any in elementsToSkip is not in the collection."
+
+    |n         "{ Class: SmallInteger }"
+     sz        "{ Class: SmallInteger }"
+     srcIndex  "{ Class: SmallInteger }"
+     dstIndex  "{ Class: SmallInteger }"
+     skipIndex "{ Class: SmallInteger }"
+     copy l|
+
+    "the code below may look like overkill,
+     however, for big collections its better to move data
+     around in big chunks"
+
+    n := self occurrencesOfAny:elementsToSkip.
+    n == 0 ifTrue:[^ self copyFrom:1].
+
+    sz := self size.
+    copy := self copyEmptyAndGrow:(sz - n).
+
+    srcIndex := 1.
+    dstIndex := 1.
+
+    n timesRepeat:[
+        skipIndex := self indexOfAny:elementsToSkip startingAt:srcIndex.
         l := skipIndex - srcIndex.
         l ~~ 0 ifTrue:[
             copy replaceFrom:dstIndex to:(dstIndex + l - 1)
@@ -4819,15 +4993,9 @@
     ^ copy
 
     "
-     #($a $b $c $d $e $f $g) copyWithout:$d
-     #($a $b $c $d $e $f $g) copyWithout:$a
-     #($a $b $c $d $e $f $g) copyWithout:$g
-     'abcdefghi' copyWithout:$h
-     'abcdefg' copyWithout:$h
-     #($a $b $c $a $a $d $e $a $f $g) copyWithout:$a
-     #($a $b $c $d $e $f $g) copyWithout:$x
-     #(90 80 70 60 50) copyWithout:70
-     #(90 80 70 80 60 45 80 50) copyWithout:80
+     #($a $b $c $d $e $f $g) copyWithoutAll:#($d $b $f)
+     'abcdefghi' copyWithoutAll:'hai'
+     #(90 80 70 80 60 45 80 50) copyWithoutAll:#(80 70 45)
     "
 !
 
@@ -4983,7 +5151,7 @@
      (but excluding) anElement.
      If anElement is not in the receiver, the returned collection
      will be empty.
-     See also #upTo:."
+     See also #upTo:/uptoAll:."
 
     |pos|
 
@@ -5000,6 +5168,31 @@
      '1234.5678' upTo:$.
      '1234.5678' restAfter:$.
     "
+
+    "Modified (comment): / 02-10-2018 / 13:10:07 / Claus Gittinger"
+!
+
+restAfterAll:anElementCollection
+    "return a new collection consisting of the receiver's elements after
+     (but excluding) anElementCollection.
+     If anElementCollection is not in the receiver, the returned collection
+     will be empty.
+     See also #upTo:/upToAll:."
+
+    |pos|
+
+    pos := self indexOfSubCollection:anElementCollection.
+    pos == 0 ifTrue:[^ self copyEmpty].
+
+    ^ self copyFrom:(pos + anElementCollection size)
+
+    "
+     #(1 2 3 4 5 6 7 8 9) upToAll:#(5 6)
+     #(1 2 3 4 5 6 7 8 9) restAfterAll:#(5 6)
+     'hello world' restAfterAll:'hello'
+    "
+
+    "Created: / 02-10-2018 / 13:11:57 / Claus Gittinger"
 !
 
 trimForWhich:aCheckBlock
@@ -5196,6 +5389,44 @@
     "
 !
 
+upToOrSelf:anElement
+    "return a new collection consisting of the receiver's elements upTo
+     (but excluding) anElement.
+     If anElement is not in the receiver, return myself (not a copy!!).
+     See also #restAfter:  , #copyFrom:index.
+
+     This is similar to #upTo: but avoids garbage if anElement is not
+     present in the collection and a copy of the collection is not needed."
+
+    |pos|
+
+    pos := self indexOf:anElement.
+    pos == 0 ifTrue:[^ self].
+
+    ^ self copyFrom:1 to:(pos - 1)
+
+    "
+     #(1 2 3 4 5 6 7 8 9) upToOrSelf:5
+     'hello world' upToOrSelf:Character space
+     #(9 8 7 6 5 4 3 2 1) asSortedCollection upToOrSelf:5
+     '1234.5678' upToOrSelf:$.
+     '1234'      upToOrSelf:$.
+     '.'      upToOrSelf:$.
+
+
+     raises an error:
+
+     (Dictionary new
+        at:#foo put:'foo';
+        at:#bar put:'bar';
+        at:#baz put:'baz';
+        yourself) upToOrSelf:#bar
+
+    "
+
+    "Created: / 09-10-2019 / 10:57:04 / Stefan Vogel"
+!
+
 upToSeparator
     "Return the next elements up to but not including the next separator.
      The next read will return the separator.
@@ -5234,6 +5465,57 @@
     "
 !
 
+withoutPrefix:aStringOrCharacter
+    "if the receiver startsWith aString, return a copy without it.
+     Otherwise return the receiver"
+
+    |startIndex|
+
+    (self startsWith:aStringOrCharacter) ifTrue:[
+        aStringOrCharacter isCharacter ifTrue:[
+            startIndex := 2
+        ] ifFalse:[    
+            startIndex := aStringOrCharacter size+1
+        ].
+        ^ self copyFrom:startIndex
+    ].
+    ^ self
+
+    "
+     'helloworld' withoutPrefix:'hello'   -> 'world'
+     'helloworld' withoutPrefix:'foo'     -> 'helloworld'
+     'helloworld' withoutPrefix:$h        -> 'elloworld'
+     'helloworld' withoutPrefix:#( $h )   -> 'elloworld'
+    "
+
+    "Modified: / 31-07-2018 / 17:05:36 / Claus Gittinger"
+    "Modified (comment): / 24-05-2019 / 09:22:54 / Claus Gittinger"
+    "Modified (comment): / 12-06-2020 / 14:51:20 / cg"
+!
+
+withoutSuffix:aStringOrCharacter
+    "if the receiver endsWith aString, return a copy without it.
+     Otherwise return the receiver"
+
+    (self endsWith:aStringOrCharacter) ifTrue:[
+        aStringOrCharacter isCharacter ifTrue:[
+            ^ self copyButLast:1
+        ] ifFalse:[
+            ^ self copyButLast:aStringOrCharacter size
+        ].
+    ].
+    ^ self
+
+    "
+     'helloworld' withoutSuffix:'world'
+     'helloworld' withoutSuffix:'foo'
+     'helloworldx' withoutSuffix:$x
+    "
+
+    "Created: / 23-10-2017 / 15:01:37 / cg"
+    "Modified: / 31-07-2018 / 17:06:34 / Claus Gittinger"
+!
+
 withoutTrailingForWhich:aCheckBlock
     "return a copy of myself without trailing characters for which aCheckBlock returns true.
      Returns an empty collection, if the receiver consist only of matching chars."
@@ -5280,18 +5562,21 @@
 !
 
 do:aBlock
-    "evaluate the argument, aBlock for every element in the collection."
+    "evaluate the argument, aBlock for every element in the collection in
+     sequence order."
 
     |stop "{ Class:SmallInteger }"|
 
     stop := self size.
     1 to:stop do:[:index |
-	aBlock value:(self at:index).
+        aBlock value:(self at:index).
     ]
 
     "
      #(one two three four five six) do:[:element | Transcript showCR:element]
     "
+
+    "Modified (comment): / 22-06-2017 / 14:51:15 / mawalch"
 !
 
 do:aBlock separatedBy:sepBlock
@@ -5320,27 +5605,27 @@
     "
 !
 
-from:start collect:aBlock
-    "evaluate the argument, aBlock for the elements starting at start
+from:startIndex collect:aBlock
+    "evaluate the argument, aBlock for the elements starting at startIndex
      to the end and return a collection of the results"
 
-    ^ self from:start to:self size collect:aBlock
+    ^ self from:startIndex to:self size collect:aBlock
 
     "
      #(one two three four five six)
-	from:2
-	collect:[:element | element asUppercase]
+        from:2
+        collect:[:element | element asUppercase]
     "
 
     "Created: / 30.1.2000 / 01:02:28 / cg"
 !
 
-from:start conform:aOneArgBlock
-    "return true, if the elements starting at the start-index conform to some condition.
+from:startIndex conform:aOneArgBlock
+    "return true, if the elements starting at the startIndex conform to some condition.
      I.e. return false, if aBlock returns false for any of those elements;
      true otherwise."
 
-    self from:start to:(self size) do:[:element | 
+    self from:startIndex to:(self size) do:[:element | 
         (aOneArgBlock value:element) ifFalse:[^ false]
     ].
     ^ true
@@ -5350,21 +5635,23 @@
 
 from:startIndex do:aBlock
     "evaluate the argument, aBlock for the elements starting with the
-     element at startIndex to the end."
-
-    ^ self from:startIndex to:self size do:aBlock
+     startIndex to the end."
+
+    self from:startIndex to:self size do:aBlock
 
     "
      #(one two three four five six)
-	from:3
-	do:[:element | Transcript showCR:element]
+        from:3
+        do:[:element | Transcript showCR:element]
     "
 !
 
 from:startIndex doWithExit:aBlock
     "evaluate the argument, aBlock for the elements starting with the
-     element at startIndex to the end. Passes an additional exitBlock as second
-     argument, which can be used to exit the loop early."
+     startIndex to the end. Passes an additional exitBlock as second
+     argument, which can be used to exit the loop early.
+     For convenience, return the exit argument if there was an early exit,
+     and nil, if there was not."
 
     ^ self from:startIndex to:self size doWithExit:aBlock
 
@@ -5373,23 +5660,51 @@
         from:3
         doWithExit:[:element :exit | 
             Transcript showCR:element.
-            element = 'four' ifTrue:[ exit value:nil ]
-        ]
+            element = 'four' ifTrue:[ exit value:999 ]
+        ]      
     "
 
     "Created: / 28-07-2013 / 22:37:28 / cg"
 !
 
+from:startIndex doWithIndex:aTwoArgBlock
+    "evaluate the argument, aTwoArgBlock for the elements starting with the
+     startIndex to the end,
+     passing both the element and its index as argument."
+
+    self from:startIndex to:self size doWithIndex:aTwoArgBlock
+
+    "
+     #(one two three four five six)
+        from:3
+        doWithIndex:[:element :idx | Transcript showCR:idx->element]
+    "
+
+    "Created: / 02-05-2019 / 21:01:44 / Claus Gittinger"
+!
+
 from:startIndex keysAndValuesDo:aBlock
     "evaluate the argument, aBlock for the elements and indices starting with the
      element at startIndex to the end."
 
-    ^ self from:startIndex to:self size keysAndValuesDo:aBlock
+    self from:startIndex to:self size keysAndValuesDo:aBlock
 
     "
      #(one two three four five six)
-	from:3
-	keysAndValuesDo:[:element :idx | Transcript showCR:(idx -> element) ]
+        from:3
+        keysAndValuesDo:[:element :idx | Transcript showCR:(idx -> element) ]
+    "
+!
+
+from:startIndex select:aBlock
+    "evaluate the argument, aBlock for the elements starting at startIndex
+     and return a collection of those elements for which the block return true."
+
+    ^ self from:startIndex to:self size select:aBlock
+
+    "
+     #(faba one two three four five six)
+        from:3 select:[:element | element startsWith:'f']
     "
 !
 
@@ -5487,14 +5802,17 @@
 from:index1 to:index2 doWithExit:aBlock
     "evaluate the argument, aBlock for the elements with index index1 to
      index2 in the collection. Pass an additional exitBlock as second argument,
-     which can be used to exit the loop early."
+     which can be used to exit the loop early.
+     For convenience, return the exit argument if there was an early exit,
+     and nil, if there was not."
 
     |exitBlock|
 
-    exitBlock := [:value | ^ value].
-    ^ self from:index1 to:index2 do:[:el |
+    exitBlock := [:exitValue | ^ exitValue firstIfEmpty:nil] asVarArgBlock.
+    self from:index1 to:index2 do:[:el |
         aBlock value:el value:exitBlock
     ].
+    ^ nil
 
     "
      #(one two three four five six)
@@ -5644,7 +5962,8 @@
      and collect the results.
      The block is called with n arguments for group of n consecutive elements in the receiver.
      An error will be reported, if the number of elements in the receiver
-     is not a multiple of n."
+     is not a multiple of n.
+     This is similar to slicesOf:collect:, but here, an N-arg block is expected."
 
     |stop "{ Class:SmallInteger }" newCollection dstIdx argVector rslt|
 
@@ -5694,11 +6013,74 @@
     "Modified: / 27-10-2006 / 10:07:02 / cg"
 !
 
+inGroupsOf:n detect:anNArgBlock thenDo:anotherNArgBlock ifNone:exceptionValue
+    "evaluate the argument, anNArgBlock for every group of n elements in the collection,
+     until the block returns true. Then deliver the to anotherNArgBlock and return
+     that block's result. If none matches, return the valeu from exceptionValue.
+     An error will be reported, if the number of elements in the receiver
+     is not a multiple of n.
+     This is similar to slicesOf:detect:, but here, an N-arg block is expected."
+
+    |stop argVector|
+
+    stop := self size.
+
+    "/ the reason for inlining the cases for 2/3 args is to avoid the temporary object creation, and to    
+    "/ allow for the compiler (jitter) to generate better code for the block-call
+    n == 2 ifTrue:[
+        1 to:stop by:2 do:[:index |
+            |a1 a2|
+
+            a1 := self at:index.
+            a2 := self at:index+1.
+            (anNArgBlock value:a1 value:a2) ifTrue:[
+                ^ anotherNArgBlock value:a1 value:a2
+            ].
+        ].
+        ^ exceptionValue value.
+    ].
+    n == 3 ifTrue:[
+        1 to:stop by:3 do:[:index |
+            |a1 a2 a3|
+
+            a1 := self at:index.
+            a2 := self at:index+1.
+            a3 := self at:index+2.
+            (anNArgBlock value:a1 value:a2 value:a3) ifTrue:[
+                ^ anotherNArgBlock value:a1 value:a2 value:a3
+            ].
+        ].
+        ^ exceptionValue value.
+    ].
+
+    argVector := Array new:n.
+    1 to:stop by:n do:[:index |
+        argVector replaceFrom:1 to:n with:self startingAt:index.
+        (anNArgBlock valueWithArguments:argVector) ifTrue:[
+            ^ anotherNArgBlock valueWithArguments:argVector
+        ].
+    ].
+    ^ exceptionValue value.
+
+    "
+     #(1 one 2 two 3 three 4 four 5 five 6 six)
+         inGroupsOf:2 detect:[:num :sym | num > 3]
+         thenDo:[:num :sym | sym] ifNone:'ouch'   
+
+     #(1 one 2 two 3 three 4 four 5 five 6 six)
+         inGroupsOf:2 detect:[:num :sym | num > 99]
+         thenDo:[:num :sym | sym] ifNone:'ouch'      
+    "
+
+    "Modified: / 27-10-2006 / 10:07:02 / cg"
+!
+
 inGroupsOf:n do:anNArgBlock
     "evaluate the argument, anNArgBlock for every group of n elements in the collection.
      The block is called with n arguments for group of n consecutive elements in the receiver.
      An error will be reported, if the number of elements in the receiver
-     is not a multiple of n."
+     is not a multiple of n.
+     This is similar to slicesOf:do:, but here, an N-arg block is expected."
 
     |stop "{ Class:SmallInteger }" argVector|
 
@@ -5782,22 +6164,6 @@
     "
 !
 
-keysAndValuesConform:aTwoArgBlock
-    "evaluate the argument, aBlock for every element in the collection,
-     passing both index and element as arguments.
-     Return false if any such evaluation returns false, true otherwise."
-
-    self keysAndValuesDo:[:index :el | 
-        (aTwoArgBlock value:index value:el) ifFalse:[^  false].
-    ].
-    ^  true
-
-    "
-     #(10 20 30 40) keysAndValuesConform:[:key :element | element = (key * 10) ]. 
-     #(10 20 30 33 40) keysAndValuesConform:[:key :element | element = (key * 10) ]. 
-    "
-!
-
 keysAndValuesDo:aTwoArgBlock
     "evaluate the argument, aBlock for every element in the collection,
      passing both index and element as arguments."
@@ -5878,9 +6244,9 @@
 
     | mySize "{ Class: SmallInteger }" retval |
 
-    mySize := self size.
-    retval := Array ofSize: mySize - 1.
-    1 to: mySize - 1 do: [:i | 
+    mySize := self size - 1.
+    retval := Array newWithSize: mySize.
+    1 to: mySize do: [:i | 
         retval at: i put: (aTwoArgBlock value: (self at: i) value: (self at: i + 1)) 
     ].
     ^ retval
@@ -5890,10 +6256,12 @@
 
      'hello world how nice' overlappingPairsCollect: [:a :b | a,b]
 
-     (('hello worlld aa bb' overlappingPairsCollect: [:a :b | a,b]) 
+     (('hello world aa bb' overlappingPairsCollect: [:a :b | a,b]) 
         asBag select:[:p | p asSet size = 1])
              valuesSortedByCounts first
     "
+
+    "Modified (comment): / 09-10-2017 / 17:09:01 / stefan"
 !
 
 overlappingPairsDo: aTwoArgBlock 
@@ -5958,7 +6326,7 @@
         but the Squeak-pairsDo: does the same as our pairWiseDo: 
         (sigh: but we were first, so they should have adapted...)"
 
-    ^ self inGroupsOf:2 do:aTwoArgBlock
+    self inGroupsOf:2 do:aTwoArgBlock
 
     "
      #(1 one 2 two 3 three 4 four 5 five 6 six)
@@ -5990,8 +6358,7 @@
 
 select:aBlock
     "evaluate the argument, aBlock for every element in the collection
-     and return a collection of all elements for which the block return
-     true"
+     and return a collection of all elements for which the blocks return true"
 
     |element newColl species needCopy
      sz  "{ Class:SmallInteger }"|
@@ -6029,12 +6396,49 @@
     "
 !
 
+selectIndices:aBlock
+    "evaluate the argument, aBlock for every INDEX in the collection
+     and return a collection of all elements for which the block returns true"
+
+    |element newColl species needCopy
+     sz  "{ Class:SmallInteger }"|
+
+    sz := self size.
+    species := self species.
+    species growIsCheap ifTrue:[
+        newColl := self copyEmpty:sz.
+        needCopy := false
+    ] ifFalse:[
+        sz == 0 ifTrue:[
+            ^ (species new:0) postCopyFrom:self.
+        ].
+        newColl := self speciesForAdding new:sz.
+        needCopy := true
+    ].
+    1 to:sz do:[:index |
+        element := self at:index.
+        (aBlock value:index) ifTrue:[
+            newColl add:element
+        ].
+    ].
+    needCopy ifTrue:[
+        newColl := (species withAll:newColl) postCopyFrom:self
+    ].
+    ^ newColl
+
+    "
+     #(one two three four five six) selectIndices:[:idx | idx odd]   
+     #(1 2 3 4 5 6 7 8 9) selectIndices:#even   
+    "
+!
+
 slicesOf:n collect:aOneArgBlock
     "evaluate the argument, aOneArg for every slice of n elements of the collection,
      and collect the results as instances of targetContainerClass.
      The block is called with n element subcollections for groups of n consecutive elements in the receiver.
      If the number of elements in the receiver is not a multiple of n, the last block evaluation will
-     get a short slice as argument"
+     get a short slice as argument.
+     This is similar to inGroupsOf:collect:, but here, a 1-arg block is expected."
 
     |out|
 
@@ -6055,7 +6459,8 @@
     "evaluate the argument, aOneArg for every slice of n elements of the collection.
      The block is called with n element subcollections for groups of n consecutive elements in the receiver.
      If the number of elements in the receiver is not a multiple of n, the last block evaluation will
-     get a short slice as argument"
+     get a short slice as argument.
+     This is similar to inGroupsOf:do:, but here, a 1-arg block is expected."
 
     |i stop|
 
@@ -6078,6 +6483,194 @@
     "
 !
 
+to:endIndex collect:aBlock
+    "evaluate the argument, aBlock for the elements up 
+     endIndex and return a collection of the results"
+
+    ^ self from:1 to:endIndex collect:aBlock
+
+    "
+     #(one two three four five six)
+        to:3
+        collect:[:element | element asUppercase]
+    "
+!
+
+to:end conform:aOneArgBlock
+    "return true, if the elements up to endIndex conform to some condition.
+     I.e. return false, if aBlock returns false for any of those elements;
+     true otherwise."
+
+    ^ self from:1 to:end conform:aOneArgBlock
+
+
+!
+
+to:endIndex do:aBlock
+    "evaluate the argument, aBlock for the elements starting with the
+     first to the endIndex."
+
+    self from:1 to:endIndex do:aBlock
+
+    "
+     #(one two three four five six)
+        to:3
+        do:[:element | Transcript showCR:element]
+    "
+!
+
+to:endIndex doWithIndex:aTwoArgBlock
+    "evaluate the argument, aTwoArgBlock for the elements starting 
+     with the first to endIndex,
+     passing both the element and its index as argument."
+
+    self from:1 to:endIndex doWithIndex:aTwoArgBlock
+
+    "
+     #(one two three four five six)
+        to:3
+        doWithIndex:[:element :idx | Transcript showCR:idx->element]
+    "
+
+    "Created: / 02-05-2019 / 21:01:44 / Claus Gittinger"
+!
+
+to:endIndex keysAndValuesDo:aBlock
+    "evaluate the argument, aBlock for the elements and indices 
+     starting with the first element to the endIndex."
+
+    self from:1 to:endIndex keysAndValuesDo:aBlock
+
+    "
+     #(one two three four five six)
+        to:3
+        keysAndValuesDo:[:element :idx | Transcript showCR:(idx -> element) ]
+    "
+!
+
+to:endIndex select:aBlock
+    "evaluate the argument, aBlock for the elements up to endIndex
+     and return a collection of those elements for which the block return true."
+
+    ^ self from:1 to:endIndex select:aBlock
+
+    "
+     #(faba one two three four five six)
+        to:3 select:[:element | element startsWith:'f']
+    "
+!
+
+with:aCollection andDefault:defaultElement do:aTwoArgBlock
+    "evaluate the argument, aBlock for successive elements from
+     each the receiver and the argument, aCollection.
+     If the receiver has more elements than the argument, use defaultElement 
+     for remaining evaluations.
+     The third argument, aTwoArgBlock must be a two-argument block.
+     This method fails if neither the receiver nor aCollection is
+     a sequenceable collection (i.e. implements numeric key access)"
+
+    |index  "{ Class: SmallInteger }"
+     sz  "{ Class: SmallInteger }"|
+
+    index := 1.
+    sz := self size.
+
+    "aCollection may be non-sequenceable, but we are"
+    aCollection do:[:eachElement |
+        index >= sz ifTrue:[
+           ^ self.
+        ].
+        aTwoArgBlock value:(self at:index) value:eachElement.
+        index := index + 1.
+    ].
+
+    "I have more elements than aCollection"
+    index to:sz do:[:i|
+        aTwoArgBlock value:(self at:index) value:defaultElement.
+    ].
+        
+
+    "
+     #(1 2 3) with:#(one two) andDefault:99 do:[:num :sym |
+        Transcript showCR:(num->sym)
+     ]
+
+     #() with:#(one two) andDefault:99 do:[:num :sym |
+        Transcript showCR:(num->sym)
+     ]
+
+     'this example does not really make sense'
+     #(1 2 3) with:#(one two) asSet andDefault:99 do:[:num :sym |
+        Transcript showCR:(num->sym)
+     ]
+    "
+
+    "Created: / 28-04-2017 / 12:13:34 / stefan"
+    "Modified: / 28-04-2017 / 14:56:40 / stefan"
+!
+
+with:collection2 collect:aTwoArgBlock
+    "evaluate the argument, aBlock for successive elements from
+     each the receiver and the collection argument,
+     and collect the results.
+     The last argument, aBlock must be a two-argument block.
+     The collection arguments must implement access via a numeric key 
+     and the sizes must be the same."
+
+    ^ self with:collection2 collect:aTwoArgBlock as:self speciesForAdding
+
+    "
+     #(one two three four five six)
+        with:(100 to:600 by:100)
+        collect:[:el1 :el2 | 
+            el1 printString , el2 printString
+        ]
+    "
+
+    "Modified (Format): / 30-06-2011 / 17:39:59 / cg"
+
+    "Created: / 20-12-2018 / 11:12:04 / Claus Gittinger"
+    "Modified: / 22-12-2018 / 09:57:38 / Claus Gittinger"
+!
+
+with:collection2 collect:aTwoArgBlock as:classOfResult
+    "evaluate the argument, aBlock for successive elements from
+     each the receiver and the collection argument,
+     and collect the results.
+     The last argument, aBlock must be a two-argument block.
+     The collection arguments must implement access via a numeric key 
+     and the sizes must be the same."
+
+    |stop  "{ Class: SmallInteger }" 
+     newCollection|
+
+    stop := self size.
+    (collection2 size == stop)  ifFalse:[
+        NotEnoughElementsSignal raiseRequestErrorString:'collections must be of the same size'.
+    ].
+    
+    newCollection := classOfResult new.
+    1 to:stop do:[:index |
+        newCollection add:(
+            aTwoArgBlock 
+                value:(self at:index) 
+                value:(collection2 at:index))
+    ].
+    ^ newCollection
+    
+    "
+     #(one two three four five six)
+        with:(100 to:600 by:100)
+        collect:[:el1 :el2 | 
+            el1 printString , el2 printString
+        ]
+    "
+
+    "Modified (Format): / 30-06-2011 / 17:39:59 / cg"
+
+    "Created: / 22-12-2018 / 09:56:59 / Claus Gittinger"
+!
+
 with:aSequenceableCollection do:aTwoArgBlock
     "evaluate the argument, aBlock for successive elements from
      each the receiver and the argument, aSequenceableCollection.
@@ -6136,6 +6729,96 @@
     "Modified (comment): / 08-01-2012 / 17:18:59 / cg"
 !
 
+with:collection2 select:aTwoArgBlock
+    "evaluate the argument, aBlock for successive elements from
+     each the receiver and the collection argument,
+     and select values from the receiver, where aTwoArgBlock returns true.
+     The collection arguments must implement access via a numeric key 
+     and the sizes must be the same."
+
+    |newCollection|
+    
+    newCollection := self speciesForAdding new.
+    self with:collection2 
+        do:[:el :el2 | 
+            (aTwoArgBlock value:el value:el2) ifTrue:[
+                newCollection add:el.
+            ]
+        ].
+    ^ newCollection
+    
+    "to fetch all elements at even indices:
+    
+     #(one two three four five six seven eight) with:(1 to:8) select:[:el :idx | idx even]
+    "
+
+    "Created: / 19-07-2019 / 11:13:58 / Claus Gittinger"
+!
+
+with:collection2 with:collection3 collect:aThreeArgBlock
+    "evaluate the argument, aBlock for successive elements from
+     each the receiver and the collection arguments
+     and collect the results.
+     The last argument, aBlock must be a three-argument block.
+     The collection arguments must implement access via a numeric key 
+     and the sizes must be the same."
+
+    ^ self with:collection2 with:collection3 collect:aThreeArgBlock as:self speciesForAdding
+
+    "
+     #(one two three four five six)
+        with:(1 to:6)
+        with:(100 to:600 by:100)
+        collect:[:el1 :el2 :el3 | 
+            el1 printString , el2 printString , el3 printString
+        ]
+    "
+
+    "Modified (Format): / 30-06-2011 / 17:39:59 / cg"
+
+    "Created: / 20-12-2018 / 11:11:07 / Claus Gittinger"
+    "Modified: / 22-12-2018 / 09:58:50 / Claus Gittinger"
+!
+
+with:collection2 with:collection3 collect:aThreeArgBlock as:classOfResult
+    "evaluate the argument, aBlock for successive elements from
+     each the receiver and the collection arguments
+     and collect the results.
+     The last argument, aBlock must be a three-argument block.
+     The collection arguments must implement access via a numeric key 
+     and the sizes must be the same."
+
+    |stop  "{ Class: SmallInteger }" 
+     newCollection|
+
+    stop := self size.
+    (collection2 size == stop and:[collection3 size == stop])  ifFalse:[
+        NotEnoughElementsSignal raiseRequestErrorString:'collections must be of the same size'.
+    ].
+
+    newCollection := classOfResult new.
+    1 to:stop do:[:index |
+        newCollection add:(aThreeArgBlock 
+            value:(self at:index) 
+            value:(collection2 at:index)
+            value:(collection3 at:index)).
+    ].
+    ^ newCollection
+
+    "
+     #(one two three four five six)
+        with:(1 to:6)
+        with:(100 to:600 by:100)
+        collect:[:el1 :el2 :el3 | 
+            el1 printString , el2 printString , el3 printString
+        ]
+    "
+
+    "Modified (Format): / 30-06-2011 / 17:39:59 / cg"
+
+    "Created: / 22-12-2018 / 09:57:50 / Claus Gittinger"
+!
+
 with:collection2 with:collection3 do:aThreeArgBlock
     "evaluate the argument, aBlock for successive elements from
      each the receiver and the collection arguments.
@@ -6171,6 +6854,7 @@
 
 atAllPut:anObject
     "replace all elements of the collection by the argument, anObject.
+     Return the receiver.
      Notice: This operation modifies the receiver, NOT a copy;
      therefore the change may affect all others referencing the receiver."
 
@@ -6187,7 +6871,32 @@
      (String new:10) atAllPut:$a
     "
 
-    "Modified: / 20.5.1998 / 15:14:11 / cg"
+    "Modified: / 20-05-1998 / 15:14:11 / cg"
+    "Modified (comment): / 26-03-2019 / 11:53:31 / Claus Gittinger"
+!
+
+clearContents
+    "to be used with cryptographic keys, to wipe their contents after use"
+
+    self atAllPut:0
+!
+
+from:startIndex count:numberOfElements put:newElement
+    "replace numberOfElements elements from startIndex of the collection
+     by the argument, newElement.
+     Notice: This operation modifies the receiver, NOT a copy;
+     therefore the change may affect all others referencing the receiver."
+
+    ^ self
+        from:startIndex
+        to:(startIndex + numberOfElements - 1)
+        put:newElement
+
+    "
+     #($a $b $c $d $e $f $g) copy from:2 count:3 put:nil
+     '1234567890' copy from:2 count:5 put:$*
+     '1234567890' copy from:2 count:20 put:$* -> error
+    "
 !
 
 from:index1 to:index2 put:anObject
@@ -6303,17 +7012,17 @@
 
     start := startIndex.
     stop := stopIndex.
-    startIndex to:stopIndex do:[:index |
-	(self at:index) = oldObject ifTrue:[
-	    self at:index put:newObject
-	]
+    start to:stop do:[:index |
+        (self at:index) = oldObject ifTrue:[
+            self at:index put:newObject
+        ]
     ]
 
     "
      args:    oldObject  : <object>
-	      newObject  : <object>
-	      startIndex : <integer>
-	      stopIndex  : <integer>
+              newObject  : <object>
+              startIndex : <integer>
+              stopIndex  : <integer>
 
      returns: self
     "
@@ -6324,7 +7033,8 @@
      #(1 2 3 4 1 2 3 4) replaceAll:1 with:'one' from:1 to:4
     "
 
-    "Modified: / 20.5.1998 / 15:23:10 / cg"
+    "Modified: / 20-05-1998 / 15:23:10 / cg"
+    "Modified: / 22-02-2019 / 10:08:08 / Stefan Vogel"
 !
 
 replaceAllForWhich:aConditionBlock with:newObject
@@ -6335,15 +7045,28 @@
      therefore the change may affect all others referencing the receiver."
 
     self
-	replaceAllForWhich:aConditionBlock with:newObject
-	from:1 to:(self size)
+        replaceAllForWhich:aConditionBlock with:newObject
+        from:1 to:(self size)
 
     "
      args:    aConditionBlock  : <block>
-	      newObject        : <object>
+              newObject        : <object>
 
      returns: self
     "
+    
+    "
+     ('bla',Character tab,'bla',Character cr,'bla') 
+        replaceAllForWhich:[ch: ch isSeparator]
+        with:(Character space)
+
+     ('bla',Character tab,'bla',Character cr,'bla') 
+        replaceAllForWhich:#isSeparator
+        with:(Character space)
+
+    "
+
+    "Modified (format): / 18-07-2017 / 13:28:45 / cg"
 !
 
 replaceAllForWhich:aConditionBlock with:newObject from:startIndex to:stopIndex
@@ -6358,20 +7081,22 @@
 
     start := startIndex.
     stop := stopIndex.
-    startIndex to:stopIndex do:[:index |
-	(aConditionBlock value:(self at:index)) ifTrue:[
-	    self at:index put:newObject
-	]
+    start to:stop do:[:index |
+        (aConditionBlock value:(self at:index)) ifTrue:[
+            self at:index put:newObject
+        ]
     ]
 
     "
      args:    aConditionBlock  : <block>
-	      newObject        : <object>
-	      startIndex       : <integer>
-	      stopIndex        : <integer>
+              newObject        : <object>
+              startIndex       : <integer>
+              stopIndex        : <integer>
 
      returns: self
     "
+
+    "Modified: / 22-02-2019 / 11:22:27 / Stefan Vogel"
 !
 
 replaceAllIdentical:oldObject with:newObject
@@ -6414,17 +7139,17 @@
 
     start := startIndex.
     stop := stopIndex.
-    startIndex to:stopIndex do:[:index |
-	(self at:index) == oldObject ifTrue:[
-	    self at:index put:newObject
-	]
+    start to:stop do:[:index |
+        (self at:index) == oldObject ifTrue:[
+            self at:index put:newObject
+        ]
     ]
 
     "
      args:    oldObject  : <object>
-	      newObject  : <object>
-	      startIndex : <integer>
-	      stopIndex  : <integer>
+              newObject  : <object>
+              startIndex : <integer>
+              stopIndex  : <integer>
 
      returns: self
     "
@@ -6434,6 +7159,8 @@
      #(1 2 3 4 1.0 2.0 3.0 4.0) replaceAll:1 with:'one' from:1 to:8
      #(1 2 3 4 1.0 2.0 3.0 4.0) replaceAllIdentical:1 with:'one' from:1 to:8
     "
+
+    "Modified: / 22-02-2019 / 11:22:48 / Stefan Vogel"
 !
 
 replaceAny:aCollection by:newObject
@@ -6520,17 +7247,17 @@
 
     start := startIndex.
     stop := stopIndex.
-    startIndex to:stopIndex do:[:index |
-	(aCollection includes:(self at:index)) ifTrue:[
-	    self at:index put:newObject
-	]
+    start to:stop do:[:index |
+        (aCollection includes:(self at:index)) ifTrue:[
+            self at:index put:newObject
+        ]
     ]
 
     "
      args:    aCollection    : <colleciton of <object> >
-	      newObject      : <object>
-	      startIndex     : <integer>
-	      stopIndex      : <integer>
+              newObject      : <object>
+              startIndex     : <integer>
+              stopIndex      : <integer>
 
      returns: self
     "
@@ -6540,7 +7267,8 @@
      #('foo' 'bar' 'foo' 'baz' foo 1 2 3) replaceAny:#(foo 1) with:'*'
     "
 
-    "Modified: / 20.5.1998 / 15:22:43 / cg"
+    "Modified: / 20-05-1998 / 15:22:43 / cg"
+    "Modified: / 22-02-2019 / 11:23:00 / Stefan Vogel"
 !
 
 replaceFrom:startIndex count:numberOfElements with:replacementCollection
@@ -6628,7 +7356,7 @@
      end      "{ Class: SmallInteger }" |
 
     replacementCollection == self ifTrue:[
-        repStartIndex == startIndex ifTrue:[ "noting to copy" ^ self ].
+        repStartIndex == startIndex ifTrue:[ "nothing to copy" ^ self ].
 
         "beware the overlapping copy"
         (repStartIndex < startIndex) ifTrue:[
@@ -6678,6 +7406,7 @@
     "
 
     "Modified: / 08-05-2012 / 13:23:51 / cg"
+    "Modified (format): / 29-05-2017 / 16:12:44 / mawalch"
 !
 
 replaceFrom:startIndex with:replacementCollection
@@ -7138,7 +7867,7 @@
 printStringWithSeparator:aSeparatorStringOrCharacter
     |s|
 
-    s := CharacterWriteStream on:''.
+    s := CharacterWriteStream new.
     self printOn:s withSeparator:aSeparatorStringOrCharacter.
     ^ s contents.
 
@@ -7149,10 +7878,10 @@
 
 !SequenceableCollection methodsFor:'private-sorting helpers'!
 
-mergeFirst:first middle:middle last:last into:dst by:aBlock 
+mergeFirst:first middle:middle last:last into:dst by:aBlock
     "Private!!
      Merge the sorted ranges [first..middle] and [middle+1..last] of the receiver into the range [first..last] of dst."
-    
+
     |i1 i2 val1 val2 out|
 
     i1 := first.
@@ -7164,7 +7893,7 @@
         val2 := self at:i2.
         "select 'lower' half of the elements based on comparator"
         [(i1 <= middle) and:[i2 <= last]] whileTrue:[
-            "this is stable if #< or #> ist used for comparison (and not #<= or #>=)"
+            "this is stable if #< or #> is used for comparison (and not #<= or #>=)"
             (aBlock value:val2 value:val1) ifTrue:[
                 dst at:out put:val2.
                 i2 := i2 + 1.
@@ -7182,18 +7911,20 @@
 
     "copy the remaining elements"
     i1 <= middle ifTrue:[
-        dst 
+        dst
             replaceFrom:out
             to:last
             with:self
             startingAt:i1
     ] ifFalse:[
-        dst 
+        dst
             replaceFrom:out
             to:last
             with:self
             startingAt:i2
     ].
+
+    "Modified (format): / 21-02-2017 / 14:33:35 / mawalch"
 !
 
 mergeSortFrom: first to: last by: aBlock
@@ -7859,9 +8590,15 @@
 !
 
 isSequenceable
-    "return true, if the receiver is some kind of sequenceableCollection"
+    "return true, if the receiver is sequenceable,
+     i.e. if its elements are accessible via the #at: and #at:put: messages
+     by an integer index, and support the do:-protocol."
 
     ^ true
+
+    "Modified (comment): / 03-03-2019 / 00:09:21 / Claus Gittinger"
+    "Modified (comment): / 15-04-2019 / 19:24:14 / Stefan Vogel"
+    "Modified (comment): / 26-05-2020 / 17:11:18 / cg"
 !
 
 isSorted
@@ -7906,13 +8643,13 @@
 !
 
 keys
-    "return a collection with all keys in the Smalltalk dictionary"
+    "return a collection with all keys of the receiver"
 
     |sz|
 
     sz := self size.
     sz == 0 ifTrue:[
-	^ #()
+        ^ #()
     ].
     ^ 1 to:sz
 !
@@ -8124,7 +8861,26 @@
 !
 
 findFirst:aBlock startingAt:startIndex
-    "find the first element, for which evaluation of the argument, aBlock returns true.
+    "find the index of the first element, for which evaluation of the argument, aBlock returns true.
+     Start the search at startIndex.
+     Return its index or 0 if none detected.
+     This is much like #detect:startingAt:, however, here an INDEX is returned,
+     whereas #detect: returns the element."
+
+    ^ self findFirst:aBlock startingAt:startIndex ifNone:0
+
+    "
+     #(1 4 3 4 3 6) findFirst:[:x | (x > 3)] startingAt:4    
+     'one.two.three' findFirst:[:c | (c == $.)] startingAt:5 
+     'one.two.three' findFirst:[:c | (c == $.)] startingAt:9 
+    "
+
+    "Modified: / 21-10-1998 / 18:48:22 / cg"
+    "Modified (comment): / 28-05-2018 / 13:10:43 / Claus Gittinger"
+!
+
+findFirst:aBlock startingAt:startIndex ifNone:exceptionalValue
+    "find the index of the first element, for which evaluation of the argument, aBlock returns true.
      Start the search at startIndex.
      Return its index or 0 if none detected.
      This is much like #detect:startingAt:, however, here an INDEX is returned,
@@ -8138,14 +8894,17 @@
     start to:stop do:[:index |
         (aBlock value:(self at:index)) ifTrue:[^ index].
     ].
-    ^ 0
+    ^ exceptionalValue value
 
     "
      #(1 4 3 4 3 6) findFirst:[:x | (x > 3)] startingAt:4
      'one.two.three' findFirst:[:c | (c == $.)] startingAt:5
-    "
-
-    "Modified: / 21.10.1998 / 18:48:22 / cg"
+     'one.two.three' findFirst:[:c | (c == $.)] startingAt:10
+     'one.two.three' findFirst:[:c | (c == $.)] startingAt:10 ifNone:nil
+    "
+
+    "Modified: / 21-10-1998 / 18:48:22 / cg"
+    "Modified (comment): / 28-05-2018 / 13:10:33 / Claus Gittinger"
 !
 
 findLast:aBlock ifNone:exceptionalValue
@@ -8338,7 +9097,10 @@
      #(1 2 1 2 1 2 3) indexOfSubCollection:#(1 2)   startingAt:2
      #(1 2 1 2 1 2 3) indexOfSubCollection:#(1 2)   startingAt:3
      #(1 2 1 2 1 2 3) indexOfSubCollection:#(1 2)   startingAt:4
-    "
+     #(0 1 2 3 15921909 15921909 15921909 15921909 15921909 15921909 15921909 15921909 15921909 15921909 15921909 15921909) indexOfSubCollection:#(15921909 15921909 15921909 15921909 15921909 15921909)
+    "
+
+    "Modified (comment): / 03-02-2017 / 15:57:26 / cg"
 !
 
 lastIndexOfSubCollection:aCollection
@@ -8666,6 +9428,43 @@
     "Modified: / 20.5.1998 / 14:58:05 / cg"
 !
 
+indexOf:anElement startingAt:start step:step
+    "search the collection for anElement, starting the search at index start;
+     if found, return the index otherwise return 0.
+     Only look at every step element.
+     The comparison is done using =
+     (i.e. equality test - not identity test)."
+
+    |startIndex "{ Class: SmallInteger }"
+     stepCount  "{ Class: SmallInteger }" 
+     stop       "{ Class: SmallInteger }" |
+
+    startIndex := start.
+    stop := self size.
+    stepCount := step.
+    startIndex to:stop by:stepCount do:[:index |
+        anElement = (self at:index) ifTrue:[^ index].
+    ].
+    ^ 0
+
+    "
+     args:    anElement : <object>
+              start     : <integer>
+              step     : <integer>
+
+     returns: elementIndex - if found
+              0            - if not found
+    "
+
+    "
+     #(10 20 30 40 10 20 30 40) indexOf:40   startingAt:5
+     #(10 20 30 40 10 20 30 40) indexOf:40.0 startingAt:5
+    "
+
+    "Created: / 18-09-2018 / 14:06:32 / Stefan Vogel"
+    "Modified (comment): / 22-10-2019 / 19:57:19 / Stefan Vogel"
+!
+
 indexOfAny:aCollection
     "search the collection for an element in aCollection.
      if found, return the index otherwise return 0.
@@ -8768,6 +9567,31 @@
 
 !
 
+indicesOf:anElement
+    "search the collection for all occurrences of anElement;
+     return a collection of indices, or an empty collection if not found.
+     The comparison is done using =
+     (i.e. equality test - not identity test)."
+
+     |indices startIndex index|
+
+     indices := OrderedCollection new.
+     startIndex := 1.
+     [ (index := self indexOf:anElement startingAt:startIndex) ~~ 0 ] whileTrue:[
+        indices add:index.
+        startIndex := index + 1.
+     ].
+     ^ indices
+
+"<<END
+     #(10 20 30 40 10 20 30 50 60 10 20 30 70) indicesOf:20 
+     'abc "123" def "555"' indicesOf:$"
+     'abc: bla bla bla: "555"' indicesOf:$:
+END"
+
+    "Modified: / 20.5.1998 / 14:59:55 / cg"
+!
+
 lastIndexOf:anElement
     "search the collection backwards for anElement;
      if found, return the index otherwise return 0.
@@ -8915,7 +9739,7 @@
 
     startIndex := start.
     stopIndex :=  stop.
-    startIndex to:stop do:[:index |
+    startIndex to:stopIndex do:[:index |
         anElement = (self at:index) ifTrue:[^ index].
     ].
     ^ 0
@@ -8937,6 +9761,7 @@
 
     "Modified: / 23-09-2011 / 14:03:05 / cg"
     "Modified (comment): / 19-11-2016 / 13:04:56 / cg"
+    "Modified: / 22-02-2019 / 10:07:17 / Stefan Vogel"
 !
 
 nextIndexOf:anElement from:start to:stop ifAbsent:exceptionBlock
@@ -9477,6 +10302,22 @@
     self addFirst:anElement.
 !
 
+removeAndAddFirst:anElement limitSizeTo:maxSize
+    "if the anElement is in the receiver collection, remove it (compare by equality);
+     then add it to the beginning.
+     Effectively moving the element to the beginning if it is already present,
+     or adding it to the beginning if not already there.
+     If the receiver's size grows over maxSize, then the last element is removed,
+     to implement a history behavior (keep the last maxSize used elements)"
+
+    self removeAndAddFirst:anElement.
+    self size > maxSize ifTrue:[
+        self removeFromIndex:(maxSize + 1).
+    ].
+
+    "Created: / 19-05-2020 / 12:27:37 / cg"
+!
+
 removeAndAddLast:anElement
     "if the anElement is in the receiver collection, remove it (compare by equality);
      then add it to the end.
@@ -9492,12 +10333,26 @@
      WARNING: this is a destructive operation, which modifies the receiver.
               Please use reversed (with a d) for a functional version."
 
+    self reverseFrom:1 to:self size
+
+    "
+     #(4 5 6 7 7) copy reverse
+     #(1 4 7 10 2 5) asOrderedCollection reverse
+    "
+
+    "Modified (comment): / 01-05-2017 / 12:46:23 / cg"
+!
+
+reverseFrom:startIndex to:endIndex
+    "destructively reverse the order of some elements inplace.
+     WARNING: this is a destructive operation, which modifies the receiver."
+
     |lowIndex "{ Class: SmallInteger }"
      hiIndex  "{ Class: SmallInteger }"
      t1 t2|
 
-    hiIndex := self size.
-    lowIndex := 1.
+    hiIndex := endIndex.
+    lowIndex := startIndex.
     [lowIndex < hiIndex] whileTrue:[
         t1 := self at:lowIndex.  t2 := self at:hiIndex.
         self at:lowIndex put:t2.  self at:hiIndex put:t1.
@@ -9507,9 +10362,10 @@
     ]
 
     "
-     #(4 5 6 7 7) reverse
-     #(1 4 7 10 2 5) asOrderedCollection reverse
-    "
+     #(1 2 3 4 5) copy reverseFrom:2 to:4
+    "
+
+    "Created: / 01-05-2017 / 12:45:26 / cg"
 !
 
 reversed
@@ -9623,6 +10479,26 @@
     "
 !
 
+sortByValue
+    "Sort my contents inplace based on sending #value to my
+     elements. 
+     Sorting by a #value selector is so common, that its worth a separate utility"
+
+    ^ self sort:[:a :b | a value < b value ]
+
+    "
+     replace all uses of sort as in:
+        ... sort:[:a :b | a value < b value]
+     by:
+        ... sortByValue
+
+     find these by searching for code matching (code-search in the browser's method list):
+        `@e sort:[:a :b | a value < b value ]
+    "
+
+    "Created: / 13-07-2017 / 20:38:42 / cg"
+!
+
 sortWith:aCollection
     "sort the receiver collection inplace, using '<' to compare elements.
      Also, the elements of aCollection are reordered with it.
@@ -9968,7 +10844,7 @@
 
 insertionSort:sortBlock from:inBegin to:inEnd
     "binary insertion sort.
-     The implementation uses the insertionSort algorithm, 
+     The implementation uses the insertionSort algorithm,
      which is slow for large collections O(n*n), but good for small or
      almost sorted collections O(N)."
 
@@ -9982,7 +10858,7 @@
     begin to:end do:[:idx|
         temp := self at:idx.
         prevIdx := idx-1.
-        "this is stable if #< or #> ist used for comparison (and not #<= or #>=)"
+        "this is stable if #< or #> is used for comparison (and not #<= or #>=)"
         [prevIdx >= inBegin and:[sortBlock value:temp value:(self at:prevIdx)]] whileTrue:[
             self at:prevIdx+1 put:(self at:prevIdx).
             prevIdx := prevIdx - 1.
@@ -10006,6 +10882,8 @@
      data reverse.
      Transcript show:'insert reverse '; showCR:(Time millisecondsToRun:[data insertionSort]).
     "
+
+    "Modified (comment): / 21-02-2017 / 14:33:09 / mawalch"
 !
 
 mergeSort
@@ -10114,7 +10992,7 @@
     startIndex = stopIndex ifTrue:[
         ^ self
     ].
-    (startIndex >= 1 and:[ startIndex < stopIndex ]) ifFalse:[
+    (startIndex > 0 and:[ startIndex < stopIndex ]) ifFalse:[
         self error:'bad start index'
     ].
     stopIndex > mySize ifTrue:[
@@ -10188,7 +11066,7 @@
 
     stop := self size.
     (stop > 1) ifTrue:[
-        sortBlock numArgs == 3 ifTrue:[
+        sortBlock argumentCount == 3 ifTrue:[
             "/ TODO: pass a collating policy to aBlock
             self quickSortFrom:1 to:stop sortBlock:sortBlock policy:(StringCollationPolicy new)
         ] ifFalse:[
@@ -10402,20 +11280,772 @@
     "
 ! !
 
+!SequenceableCollection methodsFor:'splitting & joining'!
+
+asCollectionOfSubCollectionsOfSize:pieceSize
+    "slice into pieces; return a collection containing pieces of size pieceSize from the receiver.
+     The last piece may be smaller, if the receiver's size is not a multiple of pieceSize."
+
+    |pieces
+     start  "{ Class:SmallInteger }"
+     stop   "{ Class:SmallInteger }"
+     mySize "{ Class:SmallInteger }"|
+
+    pieces := self speciesForSubcollection new.
+    start := 1. stop := start + pieceSize - 1.
+    mySize := self size.
+    [stop <= mySize] whileTrue:[
+        pieces add:(self copyFrom:start to:stop).
+        start := start + pieceSize.
+        stop := stop + pieceSize.
+    ].
+    (start <= mySize) ifTrue:[
+        pieces add:(self copyFrom:start to:mySize).
+    ].
+    ^ pieces
+
+    "
+     '123123123123123123' asCollectionOfSubCollectionsOfSize:3 
+     '12312312312312312312' asCollectionOfSubCollectionsOfSize:3 
+    "
+
+    "Modified: / 24-01-2017 / 18:55:07 / stefan"
+!
+
+asCollectionOfSubCollectionsSeparatedBy:anElement do:aBlock
+    "evaluate aBlock for each subcollection generated by separating elements
+     of the receiver by anElement.
+     If anElement occurs multiple times in a row,
+     the block will be invoked with empty collections as argument.
+     This algorithm uses equality-compare to detect the element."
+
+    |subCollection
+     endIndex      "{ Class:SmallInteger }"
+     startIndex    "{ Class:SmallInteger }"
+     stopIndex     "{ Class:SmallInteger }" |
+
+    startIndex := 0.
+    endIndex := self size.
+
+    [startIndex <= endIndex] whileTrue:[
+        stopIndex := self indexOf:anElement startingAt:startIndex+1.
+        stopIndex == 0 ifTrue:[
+            stopIndex := self size
+        ] ifFalse: [
+            stopIndex := stopIndex - 1.
+        ].
+
+        (stopIndex < startIndex) ifTrue: [
+            subCollection := self species new:0
+        ] ifFalse: [
+            subCollection := self copyFrom:startIndex+1 to:stopIndex
+        ].
+        aBlock value:subCollection.
+        startIndex := stopIndex + 1
+    ].
+
+    "
+     '' 
+        asCollectionOfSubCollectionsSeparatedBy:$: do:[:each | Transcript showCR:each storeString]
+
+     '1 one' 
+        asCollectionOfSubCollectionsSeparatedBy:$: do:[:each | Transcript showCR:each storeString]
+
+     '1 one:2 two:3 three:4 four:5 five' 
+        asCollectionOfSubCollectionsSeparatedBy:$: do:[:each | Transcript showCR:each storeString]
+
+     'a::b' 
+        asCollectionOfSubCollectionsSeparatedBy:$: do:[:each | Transcript showCR:each storeString]
+
+     ':' 
+        asCollectionOfSubCollectionsSeparatedBy:$: do:[:each | Transcript showCR:each storeString]
+
+     ':a' 
+        asCollectionOfSubCollectionsSeparatedBy:$: do:[:each | Transcript showCR:each storeString]
+
+     'a:' 
+        asCollectionOfSubCollectionsSeparatedBy:$: do:[:each | Transcript showCR:each storeString]
+    "
+!
+
+asCollectionOfSubCollectionsSeparatedByAll:aSeparatorCollection do:aBlock
+    "evaluate aBlock for each subcollection generated by separating elements
+     of the receiver by aSeparatorCollection. 
+     If aSeparatorCollection occurs multiple times in a row,
+     the result will contain empty strings.
+     Uses equality-compare when searching for aSeparatorCollection."
+
+    |items done myClass
+     startIndex    "{ Class:SmallInteger }"
+     stopIndex     "{ Class:SmallInteger }" |
+
+    items := self speciesForSubcollection new.
+    myClass := self species.
+
+    startIndex := 1.
+    done := false.
+    [done] whileFalse:[
+        |subCollection|
+
+        stopIndex := self indexOfSubCollection:aSeparatorCollection startingAt:startIndex.
+        stopIndex == 0 ifTrue:[
+            stopIndex := self size.
+            done := true.
+        ] ifFalse: [
+            stopIndex := stopIndex - 1.
+        ].
+
+        (stopIndex < startIndex) ifTrue: [
+            subCollection := myClass new:0.
+        ] ifFalse: [
+            subCollection := self copyFrom:startIndex to:stopIndex.
+        ].
+        aBlock value:subCollection.
+        startIndex := stopIndex + (aSeparatorCollection size) + 1.
+    ].
+    ^ items
+
+    "
+     '1::2::3::4::5::' asCollectionOfSubCollectionsSeparatedByAll:'::'
+     #(1 2 3 1 2 3 4 3 1 1 2 3 1 4 5) asCollectionOfSubCollectionsSeparatedByAll:#(3 1)
+     'hello+#world+#here' asCollectionOfSubCollectionsSeparatedByAll:'+#'
+    "
+
+    "Created: / 20-09-2017 / 18:58:11 / stefan"
+!
+
+asCollectionOfSubCollectionsSeparatedByAny:aCollectionOfSeparators
+    "return a collection containing the subCollection
+     (separated by any from aCollectionOfSeparators) of the receiver.
+     This allows breaking up strings using a number of elements as separator.
+     Uses equality-compare when searching for separators."
+
+    ^ self asCollectionOfSubCollectionsSeparatedByAnyForWhich:[:el | aCollectionOfSeparators includes:el]
+
+    "
+     'hello:world:isnt:this nice' asCollectionOfSubCollectionsSeparatedByAny:#($:)
+     'hello:world:isnt:this nice' asCollectionOfSubCollectionsSeparatedByAny:':'
+     'hello:world:isnt:this nice' asCollectionOfSubCollectionsSeparatedByAny:(Array with:$: with:Character space)
+     'hello:world:isnt:this nice' asCollectionOfSubCollectionsSeparatedByAny:': '
+     'h1e2l3l4o' asCollectionOfSubCollectionsSeparatedByAny:($1 to: $9)
+     #(1 9 2 8 3 7 4 6 5 5) asCollectionOfSubCollectionsSeparatedByAny:#(1 2 3)
+    "
+!
+
+asCollectionOfSubCollectionsSeparatedByAnyChange:aTwoArgBlock 
+    "Answer an ordered collection of ordered collections
+     where each subcollection is delimited by an element of the receiver
+     for which the given block evaluates to true.
+     The block is evaluated with a previous element of the collection 
+     and the following element"
+    
+    |str answer currentSubCollection currentElement previousElement|
+
+    str := self readStream.
+    answer := OrderedCollection new.
+    currentSubCollection := OrderedCollection new.
+    [ str atEnd ] whileFalse:[
+        currentElement := str next.
+        (previousElement notNil 
+         and:[aTwoArgBlock value:previousElement value:currentElement]) ifTrue:[
+            answer add:currentSubCollection.
+            currentSubCollection := OrderedCollection new
+        ].
+        currentSubCollection add:currentElement.
+        previousElement := currentElement.
+    ].
+    currentSubCollection notEmpty ifTrue:[
+        answer add:currentSubCollection
+    ].
+    ^ answer
+
+    "
+     #( 1 3 5 2 4 6 7 9 11 ) asCollectionOfSubCollectionsSeparatedByAnyChange:[:prev :curr | prev even ~= curr even].
+    "
+
+    "Created: / 17-03-2017 / 18:27:52 / stefan"
+!
+
+asCollectionOfSubCollectionsSeparatedByAnyForWhich:aBlock
+    "return a collection containing the subCollection
+     (separated by elements for which aBlock evaluates to true) of the receiver.
+     This allows breaking up strings using an arbitrary condition."
+
+    |words
+     start  "{ Class:SmallInteger }"
+     stop   "{ Class:SmallInteger }"
+     mySize "{ Class:SmallInteger }"|
+
+    words := self speciesForSubcollection new.
+    start := 1.
+    mySize := self size.
+    [start <= mySize] whileTrue:[
+        "skip multiple separators"
+        [ aBlock value:(self at:start)] whileTrue:[
+            start := start + 1 .
+            start > mySize ifTrue:[
+                ^ words
+            ].
+        ].
+
+        stop := self findFirst:aBlock startingAt:start.
+        stop == 0 ifTrue:[
+            words add:(self copyFrom:start to:mySize).
+            ^ words
+        ].
+        words add:(self copyFrom:start to:(stop - 1)).
+        start := stop
+    ].
+    ^ words
+
+    "
+     'hello:world:isnt:this nice' asCollectionOfSubCollectionsSeparatedByAnyForWhich:[:ch | ch = $:]
+     'h1e2l3l4o' asCollectionOfSubCollectionsSeparatedByAnyForWhich:[:ch | ch isDigit]
+     #(1 9 2 8 3 7 4 6 5 5) asCollectionOfSubCollectionsSeparatedByAnyForWhich:[:n | n odd]
+    "
+
+    "Modified (format): / 24-01-2017 / 18:57:57 / stefan"
+!
+
+asCollectionOfSubCollectionsSeparatedByAnyForWhich:aCheckBlock do:aBlock
+    "evaluate aBlock for each subcollection generated by separating elements
+     by elements for which aCheckBlock evaluates to true of the receiver.
+     This allows breaking up strings using an arbitrary condition."
+
+    |start  "{ Class:SmallInteger }"
+     stop   "{ Class:SmallInteger }"
+     mySize "{ Class:SmallInteger }"|
+
+    start := 1.
+    mySize := self size.
+    [start <= mySize] whileTrue:[
+        "skip multiple separators"
+        [ aCheckBlock value:(self at:start)] whileTrue:[
+            start := start + 1 .
+            start > mySize ifTrue:[
+                ^ self
+            ].
+        ].
+
+        stop := self findFirst:aCheckBlock startingAt:start.
+        stop == 0 ifTrue:[
+            aBlock value:(self copyFrom:start to:mySize).
+            ^ self
+        ].
+        aBlock value:(self copyFrom:start to:(stop - 1)).
+        start := stop
+    ].
+
+    "
+     'hello:world:isnt:this nice' asCollectionOfSubCollectionsSeparatedByAnyForWhich:[:ch | ch = $:] do:[:component| Transcript showCR:component]
+     'h1e2l3l4o' asCollectionOfSubCollectionsSeparatedByAnyForWhich:[:ch | ch isDigit]  do:[:component| Transcript showCR:component]
+     #(1 9 2 8 3 7 4 6 5 5) asCollectionOfSubCollectionsSeparatedByAnyForWhich:[:n | n odd]  do:[:component| Transcript showCR:component]
+    "
+
+    "Created: / 20-09-2017 / 19:03:35 / stefan"
+    "Modified: / 22-02-2019 / 10:06:27 / Stefan Vogel"
+!
+
+asStringWith:sepCharOrString
+    "return a string generated by concatenating my elements
+     (which must be strings or nil) and embedding sepCharOrString characters in between.
+     The argument sepCharOrString may be a character, a string or nil.
+     Nil entries and empty strings are counted as empty lines.
+     Similar to joinWith:, but specifically targeted towards collections of strings."
+
+    ^ self
+        from:1 to:(self size)
+        asStringWith:sepCharOrString
+        compressTabs:false
+        final:nil
+
+    "
+     #('hello' 'world' 'foo' 'bar' 'baz') asStringWith:$;
+     #('hello' 'world' 'foo' 'bar' 'baz') asStringWith:'|'
+     'hello|world|foo|bar|baz' asCollectionOfSubstringsSeparatedBy:$|
+    "
+
+    "Modified: / 10-07-2010 / 22:59:29 / cg"
+!
+
+asStringWith:sepCharacterOrString from:firstLine to:lastLine
+    "return part of myself as a string with embedded sepCharacters.
+     My elements must be strings or nil; nil entries and empty strings are
+     taken as empty lines.
+     The argument sepCharOrString may be a character, a string or nil.
+     Similar to joinWith:, but specifically targeted towards collections of strings."
+
+    ^ self
+        from:firstLine to:lastLine
+        asStringWith:sepCharacterOrString
+        compressTabs:false
+        final:nil
+    "
+     creating entries for searchpath:
+
+     #('foo' 'bar' 'baz' '/foo/bar') asStringWith:$;
+
+     #('foo' 'bar' 'baz' '/foo/bar') asStringWith:$: from:1 to:3
+    "
+
+    "Modified: 23.2.1996 / 15:28:55 / cg"
+!
+
+asStringWith:sepCharacterOrString from:firstLine to:lastLine compressTabs:compressTabs final:endCharacterOrString
+    "return part of myself as a string or text with embedded sepCharacters.
+     My elements must be strings or nil; nil entries and empty strings are
+     taken as empty lines.
+     The arguments sepCharacterOrString and endCharacterOrString may be nil, a character or a string.
+     If the argument compressTabs is true, leading spaces are converted
+     to tab-characters (8col tabs). The last line is followed by a final
+     character (if non-nil).
+     Similar to joinWith:, but specifically targeted towards collections of strings."
+
+    ^ self
+        from:firstLine to:lastLine
+        asStringWith:sepCharacterOrString
+        compressTabs:compressTabs
+        final:endCharacterOrString
+        withEmphasis:true
+
+    "Modified: / 17.6.1998 / 12:31:19 / cg"
+!
+
+asStringWith:sepCharacterOrString from:firstLine to:lastLine compressTabs:compressTabs final:endCharacterOrString withEmphasis:withEmphasis
+    "return part of myself as a string or text with embedded sepCharacters
+     and followup endCharacter.
+     My elements must be strings or nil; nil entries and empty strings are
+     taken as empty lines.
+     The arguments sepCharacterOrString and endCharacterOrString may be nil, a character or a string.
+     If the argument compressTabs is true, leading spaces are converted
+     to tab-characters (8col tabs). The last line is followed by a final
+     character (if non-nil).
+     The withEmphais argument controls if the returned string should preserve
+     any emphasis. If false, a plain string is returned.
+     This method is tuned for big collections, in not creating many
+     intermediate strings (has linear runtime). For very small collections
+     and small strings, it may be faster to use the comma , operation.
+     Similar to joinWith:, but specifically targeted towards collections of strings."
+
+    ^ self
+        from:firstLine to:lastLine
+        asStringWith:sepCharacterOrString
+        compressTabs:compressTabs
+        final:endCharacterOrString
+        withEmphasis:withEmphasis
+!
+
+asStringWithCRs
+    "return a string generated by concatenating my elements
+     (which must be strings or nil) and embedding cr characters in between.
+     Nil entries and empty strings are counted as empty lines."
+
+    ^ self asStringWithCRsFrom:1 to:(self size)
+
+    "
+     #('hello' 'world' 'foo' 'bar' 'baz') asStringWithCRs
+
+     (OrderedCollection new
+	add:'hello';
+	add:'world';
+	add:'foo';
+	add:('bar' asText allBold);
+	yourself) asStringWithCRs
+
+     Transcript showCR:
+	 (OrderedCollection new
+	    add:'hello';
+	    add:'world';
+	    add:'foo';
+	    add:('bar' asText allBold);
+	    yourself) asStringWithCRs
+    "
+
+    "Modified: 18.5.1996 / 16:43:47 / cg"
+!
+
+asStringWithCRsFrom:firstLine to:lastLine
+    "return a string generated by concatenating some of my elements
+     (which must be strings or nil) and embedding cr characters in between.
+     Nil entries and empty strings are counted as empty lines."
+
+    ^ self asStringWithCRsFrom:firstLine to:lastLine compressTabs:false withCR:true
+
+    "
+     #('hello' 'world' 'foo' 'bar' 'baz') asStringWithCRsFrom:2 to:4
+
+    "
+
+    "Modified: 18.5.1996 / 16:50:55 / cg"
+!
+
+asStringWithCRsFrom:firstLine to:lastLine compressTabs:compressTabs
+    "return part of myself as a string with embedded cr's.
+     My elements must be strings or nil.
+     If the argument compressTabs is true, leading spaces are converted
+     to tab-characters (8col tabs).
+     Nil entries and empty strings are taken as empty lines."
+
+    ^ self asStringWithCRsFrom:firstLine to:lastLine compressTabs:compressTabs withCR:true
+!
+
+asStringWithCRsFrom:firstLine to:lastLine compressTabs:compressTabs withCR:withCR
+    "return part of myself as a string with embedded cr's.
+     My elements must be strings or nil; nil entries and empty strings are
+     taken as empty lines.
+     If the argument compressTabs is true, leading spaces are converted
+     to tab-characters (8col tabs). WithCR controls whether the last line
+     should be followed by a cr or not."
+
+    ^ self 
+        asStringWith:(Character cr)
+        from:firstLine to:lastLine
+        compressTabs:compressTabs
+        final:(withCR ifTrue:[Character cr] ifFalse:[nil])
+!
+
+from:firstLine to:lastLine asStringWith:sepCharacterOrString
+    "return part of myself as a string with embedded sepCharacterOrStrings.
+     The argument sepCharacterOrString may be a character, a string or nil.
+     My elements must be strings or nil; nil entries and empty strings are
+     taken as empty lines."
+
+    ^ self
+        from:firstLine
+        to:lastLine
+        asStringWith:sepCharacterOrString
+        compressTabs:false
+        final:nil
+    "
+     creating entries for searchpath:
+
+     #('foo' 'bar' 'baz' '/foo/bar') asStringWith:$;
+
+     #('foo' 'bar' 'baz' '/foo/bar') from:1 to:3 asStringWith:$:
+
+     (#('foo' 'bar' 'baz' '/foo/bar') copyFrom:1 to:3) asStringWith:$:
+    "
+
+    "Modified: 23.2.1996 / 15:28:55 / cg"
+!
+
+from:firstLine to:lastLine asStringWith:sepCharacterOrString compressTabs:compressTabs final:endCharacterOrString
+    "return part of myself as a string or text with embedded sepCharacters.
+     My elements must be strings or nil; nil entries and empty strings are
+     taken as empty lines.
+     The arguments sepCharacterOrString and endCharacterOrString may be characters,
+     strings or nil.
+     If the argument compressTabs is true, leading spaces are converted
+     to tab-characters (8col tabs). 
+     The last line is followed by a final character (if non-nil)."
+
+    ^ self
+        from:firstLine
+        to:lastLine
+        asStringWith:sepCharacterOrString 
+        compressTabs:compressTabs
+        final:endCharacterOrString 
+        withEmphasis:true
+
+    "Modified: / 17.6.1998 / 12:31:19 / cg"
+!
+
+joinWithAll:separatingCollection
+    "return a collection generated by concatenating my elements
+     and slicing separatingCollection in between.
+     Similar to asStringWith:, but not specifically targeted towards collections of strings."
+
+    ^ self
+        joinWithAll:separatingCollection
+        from:1 to:(self size) as:nil
+
+    "
+     #('hello' 'world' 'foo' 'bar' 'baz') joinWithAll:' ; '   
+     #('hello' 'world' 'foo' 'bar' 'baz') joinWithAll:' | '
+    "
+!
+
+joinWithAll:separatingCollection from:startIndex to:endIndex as:speciesOrNil 
+    "extract parts of myself as a new collection with optional embedded separator.
+     Separator may be nil, or a collection of elements to be sliced in between.
+     SpeciesOrNil specifies the species of the resultig object, allowing for Arrays to be converted
+     as OrderedCollection or vice versa on the fly. If nil is passed in, the species of the first non-nil
+     element is used.
+     This counts the overall size first, then allocates the new collection once and replaces elements
+     via bulk copies. For very small collections, it may be faster to use the comma , operation.
+     Similar to asStringWith:, but not specifically targeted towards string handling."
+
+    |totalLength "{ Class:SmallInteger }"
+     pos         "{ Class:SmallInteger }"
+     sepCnt      "{ Class:SmallInteger }"
+     subColl newColl 
+     species|
+
+    startIndex = endIndex ifTrue:[ ^ self at:startIndex ].
+
+    species := speciesOrNil.
+
+    "
+     first accumulate the size of the result, 
+     to avoid countless reallocations.
+    "
+    totalLength := 0.
+    sepCnt := separatingCollection size.
+
+    startIndex to:endIndex do:[:index |
+        subColl := self at:index.
+        totalLength := totalLength + subColl size.
+        species isNil ifTrue:[
+            subColl notNil ifTrue:[
+                species := subColl species
+            ]
+        ]
+    ].
+    totalLength := totalLength + ((endIndex - startIndex) * sepCnt).
+    newColl := species newWithSize:totalLength.
+
+    pos := 1.
+    startIndex to:endIndex do:[:index |
+        subColl := self at:index.
+        subColl size ~~ 0 ifTrue:[
+            newColl replaceFrom:pos with:subColl startingAt:1.
+            pos := pos + subColl size.
+        ].
+        ((sepCnt ~~ 0) and:[index ~~ endIndex]) ifTrue:[
+            newColl replaceFrom:pos to:(pos+sepCnt-1) with:separatingCollection startingAt:1.
+            pos := pos + sepCnt.
+        ].
+    ].
+
+    ^ newColl
+
+    "
+     #( 'aa' 'bb' '' 'cc' ) joinWith:'|' from:1 to:4 as:String  
+     #( 'aa' 'bb' '' 'cc' ) joinWith:nil from:1 to:4 as:String  
+     #( 'aa' 'bb' '' 'cc' ) joinWith:'|' from:1 to:4 as:Array   
+     #( (1 2 3) (4 5 6) (7 6 8) ) joinWith:#(nil) from:1 to:3 as:OrderedCollection  
+     #( (1 2 3) (4 5 6) (7 6 8) ) joinWith:nil from:1 to:3 as:nil                
+    "
+
+    "Created: / 17.6.1998 / 12:30:32 / cg"
+    "Modified: / 17.6.1998 / 12:31:59 / cg"
+!
+
+split:aCollection indicesDo:aTwoArgBlock
+    "Split a collection by myself as a delimiter.
+     see Object >> split: for optimized version for single delimiters.
+     Example:
+        '||' split: 'foo||bar||2'"
+
+    |position oldPosition|
+
+    position := 1.
+    oldPosition := position.
+    position := aCollection indexOfSubCollection:self startingAt:position.
+    [position ~~ 0] whileTrue:[
+        aTwoArgBlock value:oldPosition value:position-1.
+        position := position + self size.
+        oldPosition := position.
+        position := aCollection indexOfSubCollection:self startingAt:position.        
+    ].
+    aTwoArgBlock value:oldPosition value:aCollection size
+    
+    "
+     'xx' split:'helloxxworldxxthisxxisxxsplitted' indicesDo: [:start :stop | Transcript show:start; show:' to '; showCR:stop ]
+     'xx' split:'helloxxworldxxthisxxisxxsplitted' do: [:frag | Transcript showCR:frag ]
+
+     'hello world' 
+        splitOn: ' ' 
+        do: [:part | Transcript showCR:part ]
+    "
+    
+    "
+     'hello world' 
+        splitOn: ' ' 
+        indicesDo: [:start :stop | Transcript show:start; show:' to '; showCR:stop ]
+    "
+
+    "Created: / 13-07-2017 / 16:46:58 / cg"
+    "Modified (comment): / 13-07-2017 / 18:39:05 / cg"
+    "Modified: / 30-07-2018 / 09:03:26 / Stefan Vogel"
+!
+
+splitBy:anElement do:aBlock
+    "evaluate aBlock for each subcollection generated by separating elements
+     of the receiver by anElement.
+     If anElement occurs multiple times in a row,
+     the block will be invoked with empty collections as argument.
+     This algorithm uses equality-compare to detect the element."
+
+    ^ self asCollectionOfSubCollectionsSeparatedBy:anElement do:aBlock
+
+    "
+     '' 
+        splitBy:$: do:[:each | Transcript showCR:each storeString]
+
+     '1 one' 
+        splitBy:$: do:[:each | Transcript showCR:each storeString]
+
+     '1 one:2 two:3 three:4 four:5 five' 
+        splitBy:$: do:[:each | Transcript showCR:each storeString]
+
+     'a::b' 
+        splitBy:$: do:[:each | Transcript showCR:each storeString]
+
+     ':' 
+        splitBy:$: do:[:each | Transcript showCR:each storeString]
+
+     ':a' 
+        splitBy:$: do:[:each | Transcript showCR:each storeString]
+
+     'a:' 
+        splitBy:$: do:[:each | Transcript showCR:each storeString]
+    "
+!
+
+splitByAny:aCollectionOfSeparators
+    "return a collection containing the subCollection
+     (separated by any from aCollectionOfSeparators) of the receiver.
+     This allows breaking up strings using a number of elements as separator.
+     Uses equality-compare when searching for separators."
+
+    ^ self asCollectionOfSubCollectionsSeparatedByAny:aCollectionOfSeparators
+
+    "
+     'hello:world:isnt:this nice' splitByAny:#($:)
+     'hello:world:isnt:this nice' splitByAny:':'
+     'hello:world:isnt:this nice' splitByAny:(Array with:$: with:Character space)
+     'hello:world:isnt:this nice' splitByAny:#( $: $ ) 
+     'hello:world:isnt:this nice' splitByAny:{ $: . $ }
+     'hello:world:isnt:this nice' splitByAny:': '
+     'h1e2l3l4o' splitByAny:($1 to: $9)
+     #(1 9 2 8 3 7 4 6 5 5) splitByAny:#(1 2 3)
+    "
+!
+
+splitForSize:pieceSize
+    "slice into pieces; return a collection containing pieces of size pieceSize from the receiver.
+     The last piece may be smaller, if the receiver's size is not a multiple of pieceSize."
+
+    ^ self asCollectionOfSubCollectionsOfSize:pieceSize
+
+    "
+     '123123123123123123' splitForSize:3 
+     '12312312312312312312' splitForSize:3 
+    "
+!
+
+splitOn:splitter
+    "Split at any encounter of splitter in the receiver.
+     splitter can be any object which implements #split:;
+     in particular, Strings, Regexes and Blocks can be used as spitter.
+     Any other object used as splitter is treated as an element 
+     used as split object (i.e. I wil split at that element).
+     If the splitter is not encountered, a single element collection containing
+     the receiver is returned"
+
+    ^ splitter split:self
+
+    "
+     'hello world' splitOn:' '
+     'abacadae' splitOn:$a
+     'abacadae' splitOn:'a'
+     'abaacaadaae' splitOn:'aa'
+     'abaacaadaae' splitOn:[:ch | ch == $a]
+     'abaacaadaae' splitOn:('a+' asRegex)
+     'helloworld' splitOn:' '        
+     'helloworld ' splitOn:' '       
+     ' helloworld ' splitOn:' '      
+
+     #(0 1 2 3 1 4 5 6 1 7 1 9 10) splitOn:1
+    "
+
+    "Created: / 13-07-2017 / 16:37:44 / cg"
+!
+
+splitOn:splitter do:aBlock
+    "split the receiver using splitter (can be a string or regex),
+     and evaluate aBlock on each fragment.
+     splitter can be any object which implements #split:;
+     in particular, Strings, Regexes and Blocks can be
+     used as spitter.
+     Any other object used as splitter is treated as an Array 
+     containing that split object"
+
+    ^ splitter split:self do:aBlock
+
+    "
+     'hello world' 
+        splitOn:' ' 
+        do:[:fragment | Transcript showCR:fragment].
+    "
+
+    "Created: / 13-07-2017 / 16:38:35 / cg"
+!
+
+splitOn:splitter indicesDo:aTwoArgBlock
+    "split the receiver using splitter (can be a string or regex),
+     and evaluate aTwoArgBlock on each pair of start- and stop index.
+     Splitter can be any object which implements #split:;
+     in particular, Strings, Regexes and Blocks can be used.
+     Any other splitter object is treated as an Array 
+     containing that split object"
+
+    ^ splitter split:self indicesDo:aTwoArgBlock
+
+    "
+     'hello world' 
+        splitOn:' ' 
+        indicesDo: [:start :stop | Transcript show:start; show:' to '; showCR:stop ]
+    "
+
+    "Created: / 13-07-2017 / 16:48:47 / cg"
+!
+
+splitOnFirst:splitter
+    "Split at the first encounter of splitter in the receiver;
+     return the two parts or nil, if the splitter was not encountered.
+     splitter can be any object which implements #split:;
+     in particular, Strings, Regexes and Blocks can be used as spitter.
+     Any other object used as splitter is treated as an element 
+     used as split object (i.e. I wil split at that element).
+     Returns a two-element collection containing the left and right parts.
+     If the splitter is not encountered, the returned right part is nil.
+     If the splitter is encountered at the end, the returned right part is empty"
+
+    splitter splitFirstIn:self do:[:leftPart :rightPart | ^ {leftPart . rightPart}].
+    ^ nil
+
+    "
+     'hello world more there' splitOnFirst:' ' 
+     'bacadae' splitOnFirst:$a               
+     'abaacaadaae' splitOnFirst:'aa'         
+     'baacAadaae' splitOnFirst:[:ch | ch isUppercase]  
+     'abaacaadaae' splitOnFirst:('a+' asRegex)
+     'helloworld' splitOnFirst:' '  
+     'helloworld ' splitOnFirst:' '       
+
+     #(1 2 true 3 4 5) splitOnFirst:true     
+    "
+
+    "Created: / 13-07-2017 / 16:37:44 / cg"
+! !
+
 !SequenceableCollection methodsFor:'testing'!
 
 includesKey:anIndex
     "return true, if anIndex is a valid key.
      NOTICE: in ST-80, this message is only defined for Dictionaries,
-	     however, having a common protocol with indexed collections
-	     often simplifies things."
+             however, having a common protocol with indexed collections
+             often simplifies things."
 
     anIndex isInteger ifFalse:[^ false].
-    ^ (anIndex >= 1) and:[anIndex <= self size]
+    ^ (anIndex > 0) and:[anIndex <= self size]
 
     "
      #(1 2 3) includesKey:4
      #(1 2 3) includesKey:3
+     #(1 2 3) includesKey:0
     "
 ! !
 
@@ -10429,7 +12059,7 @@
 
     mySize := self size.
     mySize = aFloatVector size ifFalse:[
-        ^ self error:'Must be of equal size'
+        ^ ArgumentError raiseErrorString:'Vector be of equal size'
     ].
     result := 0.0.
     1 to: mySize do:[:i|
@@ -10446,10 +12076,14 @@
      v := #(2.0 2.0 1.0) asDoubleArray.
      v dot:v.            
     "
+
+    "Modified: / 06-06-2019 / 23:23:48 / Claus Gittinger"
 !
 
 hornerMultiplyAndAdd:x
-    "fallback for horner's-method computation of polynomials.
+    "horner's-method computation of polynomials.
+     (this is a fallback - there are highspeed versions in the floatArray subclasses.
+
      The vector is interpreted as providing the factors for a polynomial,
         an*x^n + (an-1)*x^(n-1) + ... + a2(x) + a1
      where the ai are the elements of the Array.
@@ -10537,5 +12171,3 @@
     ^ '$Changeset: <not expanded> $'
 ! !
 
-
-SequenceableCollection initialize!