SequenceableCollection.st
author claus
Fri, 25 Feb 1994 14:00:53 +0100
changeset 56 be0ed17e6f85
parent 44 b262907c93ea
child 61 f8c30e686fbf
permissions -rw-r--r--
*** empty log message ***

"
 COPYRIGHT (c) 1989 by Claus Gittinger
              All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"

Collection subclass:#SequenceableCollection
       instanceVariableNames:''
       classVariableNames:''
       poolDictionaries:''
       category:'Collections-Abstract'
!

SequenceableCollection comment:'

COPYRIGHT (c) 1989 by Claus Gittinger
              All Rights Reserved

SequenceableCollections have ordered elements which can be accessed via
an index. SequenceableCollection is an abstract class - there are no
instances of it in the system.

$Header: /cvs/stx/stx/libbasic/SequenceableCollection.st,v 1.8 1994-01-16 03:45:26 claus Exp $

written spring 89 by claus
'!

!SequenceableCollection class methodsFor:'instance creation'!

new:size withAll:element
    "return a new Collection of size, where all elements are
     initialized to element"

    |newCollection|

    newCollection := self new:size.
    newCollection atAllPut:element.
    ^ newCollection
! !

!SequenceableCollection methodsFor:'accessing'!

first
    "return the first element"

    ^ self at:1
!

last
    "return the last element"

    ^ self at:(self size)
!

at:index ifAbsent:exceptionBlock
    "return the element at index if valid. If the index is invalid,
     return the result of evaluating the exceptionblock"

    ((index < 1) or:[index > self size]) ifTrue:[
        ^ exceptionBlock value
    ].
    ^ self at:index

    "#(1 2 3) at:4 ifAbsent:['no such index']"
! !

!SequenceableCollection methodsFor:'queries'!

firstIndex
    "return the first elements index"

    ^ 1
!

lastIndex
    "return the last elements index"

    ^ self size
!

size
    "return the number of elements in the collection.
     concrete implementations must define this"

    ^ self subclassResponsibility
! !

!SequenceableCollection methodsFor:'comparing'!

= aCollection
    "return true if the receiver and aCollection represent collections
     with equal contents."

    |index "{ Class: SmallInteger }"
     stop  "{ Class: SmallInteger }" |

    (aCollection == self) ifTrue:[^true].
    (aCollection isKindOf:SequenceableCollection) ifFalse:[^false].

    stop := self size.
    stop == (aCollection size) ifFalse:[^false].
    index := 1.
    [index <= stop] whileTrue:[
        (self at:index) = (aCollection at:index) ifFalse:[^false].
        index := index + 1
    ].
    ^ true

    "#(1 2 3 4 5) = #(1 2 3 4 5)"
    "#($1 $2 $3 $4 $5) = #(1 2 3 4 5)"
    "#($1 $2 $3 $4 $5) = '12345'"
    "#($1 $2 $3 $4 $5) = '54321' asSortedCollection"
!

startsWith:aCollection
    "return true, if the receivers first elements match those
     of aCollection"

    |index "{ Class: SmallInteger }"
     stop  "{ Class: SmallInteger }" |

    (aCollection == self) ifTrue:[^true].
    (aCollection isKindOf:SequenceableCollection) ifFalse:[^false].

    stop := aCollection size.
    stop > self size ifTrue:[^false].

    index := 1.
    [index <= stop] whileTrue:[
        (self at:index) = (aCollection at:index) ifFalse:[^false].
        index := index + 1
    ].
    ^ true

    "'abcde' startsWith:#($a $b $c)"
    "#[1 2 3 4] startsWith:#(1 2 3)"
    "#(1 2 3 4) asOrderedCollection startsWith:#(1 2 3)"
!

endsWith:aCollection
    "return true, if the receivers last elements match those
     of aCollection"

    |index1 "{ Class: SmallInteger }"
     index2 "{ Class: SmallInteger }" 
     stop   "{ Class: SmallInteger }" |

    (aCollection == self) ifTrue:[^true].
    (aCollection isKindOf:SequenceableCollection) ifFalse:[^false].

    stop := aCollection size.
    stop > self size ifTrue:[^false].

    index1 := self size.
    index2 := stop.
    [index2 > 0] whileTrue:[
        (self at:index1) = (aCollection at:index2) ifFalse:[^false].
        index1 := index1 - 1.
        index2 := index2 - 1
    ].
    ^ true

    "'abcde' endsWith:#($d $e)"
    "#[1 2 3 4] endsWith:#(3 4)"
    "#(1 2 3 4) asOrderedCollection endsWith:#(3 4)"
! !

!SequenceableCollection methodsFor:'copying'!

, aCollection
    "return a new collection formed from concatenating the receiver with
     the argument. The class of the new collection is determined by the
     receivers class, so mixing classes is possible, if the second collections
     elements can be stored into instances of the receivers class."

    |newCollection 
     mySize    "{ Class: SmallInteger }"
     newSize   "{ Class: SmallInteger }"
     otherSize "{ Class: SmallInteger }"
     dstIndex  "{ Class: SmallInteger }"|

    mySize := self size.
    otherSize := aCollection size.
    newSize := mySize + otherSize.
    newCollection := self species new:newSize.

    newCollection replaceFrom:1 to:mySize with:self startingAt:1.
    dstIndex := mySize + 1.
    (aCollection isKindOf:SequenceableCollection) ifTrue:[
        "yes, aCollection has indexed elements"
        newCollection replaceFrom:dstIndex to:newSize
                             with:aCollection startingAt:1.
        ^ newCollection
    ] ifFalse:[
        "no, enumerate aCollection"
        aCollection do:[:element |
            newCollection at:dstIndex put:element.
            dstIndex := dstIndex + 1
        ]
    ].
    ^ newCollection

    "#($a $b $c) , #(1 2 3)"
    "#($a $b $c) , '123'"
    "'abc' , '123'"
    "'abc' , #($q $w $e $r $t $y) asSortedCollection"
    "'abc' , #(1 2 3 4 5)"  "-- will fail, since strings cannot store integers"
!

copyWith:newElement
    "return a new collection consisting of receivers elements
     plus the argument."

    |newCollection mySize newSize|

    mySize := self size.
    newSize := mySize + 1.
    newCollection := self species new:newSize.
    newCollection replaceFrom:1 to:mySize with:self startingAt:1.
    newCollection at:newSize put:newElement.
    ^newCollection

    "#(1 2 3 4 5) copyWith:$a"
    "'abcdefg' copyWith:$h"
    "'abcdefg' copyWith:'123'"  "-- will fail: string cannot be stored into string"
    "'abcdefg' copyWith:1"      "-- will fail: integer cannot be stored into string"
!

copyWithout:elementToSkip
    "return a new collection consisting of receivers elements
     without elementToSkip, if it was present. No error is reported,
     if elementToSkip is not in the collection."

    |copy skipIndex sz|

    skipIndex := self indexOf:elementToSkip startingAt:1.
    (skipIndex == 0) ifTrue:[^ self copy].

    sz := self size.
    copy := self class new:(sz - 1).
    copy replaceFrom:1 to:(skipIndex - 1) with:self startingAt:1.
    copy replaceFrom:skipIndex to:(sz - 1) with:self startingAt:(skipIndex + 1).
    ^ copy

    "#($a $b $c $d $e $f $g) copyWithout:$d"
    "#($a $b $c $d $e $f $g) copyWithout:$x"
    "#(90 80 70 60 50) asSortedCollection copyWithout:70"
!

copyFrom:start to:stop
    "return a new collection consisting of receivers elements
     between start and stop"

    |newCollection newSize|

    newSize := stop - start + 1.
    newCollection := self class new:newSize.
    newCollection replaceFrom:1 to:newSize with:self startingAt:start.
    ^ newCollection

    "#($a $b $c $d $e $f $g) copyFrom:2 to:5"
    "'1234567890' copyFrom:2 to:5"
!

copyFrom:start
    "return a new collection consisting of receivers elements
     from start to the end of the collection"

    ^ self copyFrom:start to:(self size)

    "#($a $b $c $d $e $f $g) copyFrom:2"
    "'1234567890' copyFrom:2"
!

copyTo:stop
    "return a new collection consisting of receivers elements
     from 1 up to index stop"

    ^ self copyFrom:1 to:stop

    "#($a $b $c $d $e $f $g) copyTo:5"
    "'1234567890' copyTo:4"
!

copyWithoutIndex:omitIndex
    "return a new collection consisting of receivers elements
     without the argument stored at omitIndex"

    |copy|

    copy := self class new:(self size - 1).
    copy replaceFrom:1 to:(omitIndex - 1) with:self startingAt:1.
    copy replaceFrom:omitIndex to:(copy size) 
                with:self startingAt:(omitIndex + 1).
    ^ copy

    "#(1 2 3 4 5 6 7 8 9 0) copyWithoutIndex:3"
    "'abcdefghijkl' copyWithoutIndex:5"

! !

!SequenceableCollection methodsFor:'filling and replacing'!

from:index1 to:index2 put:anObject
    "replace the elements from index1 to index2 of the collection
     by the argument, anObject.
     Notice: This operation modifies the receiver, NOT a copy;
     therefore the change may affect all others referencing the receiver."

    |index "{ Class: SmallInteger }"
     end   "{ Class: SmallInteger }"|

    index := index1.
    end := index2.
    [index <= end] whileTrue:[
        self at:index put:anObject.
        index := index + 1
    ]

    "#(1 2 3 4 5 6 7 8 9 0) from:3 to:6 put:$X"
    "'abcdefghijkl' from:3 to:6 put:$X"
!

atAllPut:anObject
    "replace all elements of the collection by the argument, anObject.
     Notice: This operation modifies the receiver, NOT a copy;
     therefore the change may affect all others referencing the receiver."

    self from:1 to:(self size) put:anObject
!

atAll:indexCollection put:anObject
    "put anObject into all indexes from indexCollection in the receiver.
     Notice: This operation modifies the receiver, NOT a copy;
     therefore the change may affect all others referencing the receiver."

    indexCollection do:[:index | self at:index put:anObject]

    "(Array new:10) atAll:(1 to:5) put:0"
    "(Array new:10) atAll:#(1 5 6 9) put:0"
!

replaceAll:oldObject by:newObject
    "replace all oldObjects by newObject in the receiver.
     Notice: This operation modifies the receiver, NOT a copy;
     therefore the change may affect all others referencing the receiver."

    1 to:self size do:[:index |
        (self at:index) = oldObject ifTrue:[
            self at:index put:newObject
        ]
    ]

    "'123123abc123' replaceAll:$1 by:$*"
    "#($a $b $a $c $a $d $a $e) replaceAll:$a by:$A"
!

replaceFrom:start with:replacementCollection
    "replace elements in the receiver starting at start,
     with elements taken from replacementCollection starting at 1
     to the end of replacementCollection.
     Notice: This operation modifies the receiver, NOT a copy;
     therefore the change may affect all others referencing the receiver."

    ^ self replaceFrom:start 
                    to:(start + replacementCollection size - 1)
                  with:replacementCollection
            startingAt:1

    "'1234567890' replaceFrom:5 with:'abc'"
    "#($a $b $c $d $e) replaceFrom:2 with:'123'"
!

replaceFrom:start with:replacementCollection startingAt:offset
    "replace elements in the receiver starting at start,
     with elements taken from replacementCollection starting at offset
     to the end of replacementCollection.
     Notice: This operation modifies the receiver, NOT a copy;
     therefore the change may affect all others referencing the receiver."

    ^ self replaceFrom:start 
                    to:(start + replacementCollection size - offset)
                  with:replacementCollection
            startingAt:offset

    "'1234567890' replaceFrom:5 with:'abcdef' startingAt:3"
    "#($a $b $c $d $e) replaceFrom:2 with:'12345' startingAt:4"
!

replaceFrom:start to:stop with:replacementCollection
    "replace elements in the receiver between index start and stop,
     with elements taken from replacementCollection starting at 1.
     Notice: This operation modifies the receiver, NOT a copy;
     therefore the change may affect all others referencing the receiver."

    ^ self replaceFrom:start
                    to:stop
                  with:replacementCollection
            startingAt:1
!

replaceFrom:start to:stop with:replacementCollection startingAt:repStart
    "replace elements in the receiver between index start and stop,
     with elements  taken from replacementCollection starting at repStart.
     Notice: This operation modifies the receiver, NOT a copy;
     therefore the change may affect all others referencing the receiver."

    |srcIndex "{ Class: SmallInteger }"
     dstIndex "{ Class: SmallInteger }"
     end      "{ Class: SmallInteger }" |

    (replacementCollection == self) ifTrue:[
        (repStart < start) ifTrue:[
            " must do reverse copy "
            srcIndex := repStart + (stop - start).
            dstIndex := stop.
            end := start.
            [dstIndex >= end] whileTrue:[
                self at:dstIndex put:(replacementCollection at:srcIndex).
                srcIndex := srcIndex - 1.
                dstIndex := dstIndex - 1
            ].
            ^ self
        ]
    ].

    srcIndex := repStart.
    dstIndex := start.
    end := stop.
    [dstIndex <= end] whileTrue:[
        self at:dstIndex put:(replacementCollection at:srcIndex).
        srcIndex := srcIndex + 1.
        dstIndex := dstIndex + 1
    ]
!

withCRs
    "return a new collection consisting of receivers elements
     with all \-characters replaced by cr-characters.
     Notice: This operation modifies the receiver, NOT a copy;
     therefore the change may affect all others referencing the receiver."

    ^ self copy replaceAll:$\ by:(Character cr)
!

withoutCRs
    "return a new collection consisting of receivers elements
     with all cr-characters replaced by \-characters.
     Notice: This operation modifies the receiver, NOT a copy;
     therefore the change may affect all others referencing the receiver."

    ^ self copy replaceAll:(Character cr) by:$\
! !

!SequenceableCollection methodsFor:'adding & removing'!

addFirst:anElement
    "prepend the argument, anElement to the collection."

    |newSize|

    newSize := self size + 1.
    self grow:newSize.
    self replaceFrom:2 to:newSize with:self startingAt:1.
    self at:1 put:anElement

    "#(1 2 3 4 5 6 7 8) addFirst:'hello'"
!

add:anElement
    "append the argument, anElement to the collection"

    |newSize|

    newSize := self size + 1.
    self grow:newSize.
    self at:newSize put:anElement

    "#(1 2 3 4 5 6 7 8) add:'hello'"
!

add:anElement beforeIndex:index
    "insert the first argument, anObject into the collection before slot index"

    |newSize|

    newSize := self size + 1.
    self grow:newSize.
    self replaceFrom:index + 1 to:newSize with:self startingAt:index.
    self at:index put:anElement

    "#(1 2 3 4 5 6 7 8) add:'hello' beforeIndex:5"
!

remove:anElement ifAbsent:aBlock
    "search for anElement and, if present remove it; if not present
     return the value of evaluating aBlock"

    |any 
     dstIndex "{ Class: SmallInteger }"
     sz       "{ Class: SmallInteger }"|

    dstIndex := 1.
    any := false.
    sz := self size.
    1 to:sz do:[:srcIndex |
        (anElement = (self at:srcIndex)) ifTrue:[
            any := true
        ] ifFalse:[
            (dstIndex ~~ srcIndex) ifTrue:[
                self at:dstIndex put:(self at:srcIndex)
            ].
            dstIndex := dstIndex + 1
        ]
    ].
    any ifTrue:[
        self grow:dstIndex - 1
    ] ifFalse:[
        aBlock value
    ]

    "#(1 2 3 4 5 6 7 8 9 0) remove:3 ifAbsent:[Transcript showCr:'no']"
    "#(1 2 3 4 5 6 7 8 9 0) remove:99 ifAbsent:[Transcript showCr:'no']"
!

removeFromIndex:startIndex toIndex:endIndex
    "remove the elements stored at indexes between startIndex and endIndex"

    |newSize|

    newSize := self size - endIndex + startIndex - 1.
    self replaceFrom:startIndex to:newSize with:self startingAt:(endIndex + 1).
    self grow:newSize

    "#(1 2 3 4 5 6 7 8 9 0) removeFromIndex:3 toIndex:5"
!

removeIndex:index
    "remove the argument stored at index"

    self removeFromIndex:index toIndex:index

    "#(1 2 3 4 5 6 7 8 9 0) removeIndex:3"
! !

!SequenceableCollection methodsFor:'searching'!

detect:aBlock ifNone:exceptionBlock
    "find the first element, for which evaluation of the argument, aBlock
     return true; if none does so, return the evaluation of exceptionBlock

    reimplemented here for speed"

    |stop  "{ Class: SmallInteger }"
     element|

    stop := self size.
    1 to:stop do:[:index |
        element := self at:index.
        (aBlock value:element) ifTrue:[
            ^ element
        ].
    ].
    ^ exceptionBlock value
!

indexOf:anElement
    "search the collection for anElement;
     if found, return the index otherwise return 0.
     The comparison is done using = 
     (i.e. equality test - not identity test)."

    ^ self indexOf:anElement startingAt:1

    "#(10 20 30 40 50 60 70) indexOf: 40"
    "#(10 20 30 40 50 60 70) indexOf: 40.0"
!

indexOf:anElement ifAbsent:exceptionBlock
    "search the collection for anElement;
     if found, return the index otherwise return the value of the
     exceptionBlock.
     The comparison is done using = 
     (i.e. equality test - not identity test)."

    |index|

    index := self indexOf:anElement startingAt:1.
    (index == 0) ifTrue:[^ exceptionBlock value].
    ^ index
!

indexOf:anElement startingAt:start
    "search the collection for anElement staring search at index start;
     if found, return the index otherwise return 0.
     The comparison is done using = 
     (i.e. equality test - not identity test)."

    |startIndex "{ Class: SmallInteger }"
     stop       "{ Class: SmallInteger }" |

    startIndex := start.
    stop := self size.
    startIndex to:stop do:[:index |
        anElement = (self at:index) ifTrue:[^ index].
    ].
    ^ 0
!

indexOf:anElement startingAt:start ifAbsent:exceptionBlock
    "search the collection for anElement starting search at start;
     if found, return the index otherwise return the value of the
     exceptionBlock.
     The comparison is done using = 
     (i.e. equality test - not identity test)."

    |index|

    index := self indexOf:anElement startingAt:start.
    (index == 0) ifTrue:[^ exceptionBlock value].
    ^ index
!

identityIndexOf:anElement
    "search the collection for anElement using identity compare (i.e. ==);
     if found, return the index otherwise return 0."

    ^ self identityIndexOf:anElement startingAt:1

    "#(10 20 30 40 50 60 70) indexOf: 40"
    "#(10 20 30 40 50 60 70) indexOf: 40.0"
!

identityIndexOf:anElement ifAbsent:exceptionBlock
    "search the collection for anElement using identity compare (i.e. ==);
     if found, return the index otherwise return the value of the
     exceptionBlock."

    |index|

    index := self identityIndexOf:anElement startingAt:1.
    (index == 0) ifTrue:[^ exceptionBlock value].
    ^ index
!

identityIndexOf:anElement startingAt:start
    "search the collection for anElement staring search at index start
     using identity compare  (i.e. ==);
     if found, return the index otherwise return 0."

    |startIndex "{ Class: SmallInteger }"
     stop       "{ Class: SmallInteger }" |

    startIndex := start.
    stop := self size.
    startIndex to:stop do:[:index |
        anElement == (self at:index) ifTrue:[^ index].
    ].
    ^ 0

    "#(10 20 30 40 50 60 70) indexOf: 40"
    "#(10 20 30 40 50 60 70) indexOf: 40.0"
!

identityIndexOf:anElement startingAt:start ifAbsent:exceptionBlock
    "search the collection for anElement starting search at start;
     if found, return the index otherwise return the value of the
     exceptionBlock.
     This one searches for identical objects (i.e. ==)."

    |index|

    index := self identityIndexOf:anElement startingAt:start.
    (index == 0) ifTrue:[^ exceptionBlock value].
    ^ index
!

findFirst:aBlock
    "find the first element, for which evaluation of the argument, aBlock
     return true; return its index or 0 if none detected."

    |stop  "{ Class: SmallInteger }" |

    stop := self size.
    1 to:stop do:[:index |
        (aBlock value:(self at:index)) ifTrue:[^ index].
    ].
    ^ 0

    "#(1 2 3 4 5 6) findFirst:[:x | (x >= 3)]"
    "#(1 2 3 4 5 6) findFirst:[:x | (x >= 3) and:[x even]]"
    "'one.two.three' findFirst:[:c | (c == $.)]"
!

findLast:aBlock
    "find the last element, for which evaluation of the argument, aBlock
     return true; return its index or 0 if none detected."

    |start "{ Class: SmallInteger }"|

    start := self size.
    start to:1 by:-1 do:[:index |
        (aBlock value:(self at:index)) ifTrue:[^ index].
    ].
    ^ 0

    "#(1 99 3 99 5 6) findLast:[:x | (x == 99)]"
    "'one.two.three' findLast:[:c | (c == $.)]"
!

includes:anElement
    "return true if the collection contains anElement; false otherwise.
     Comparison is done using equality compare (i.e. =)."

    ((self indexOf:anElement startingAt:1) == 0) ifTrue:[^ false].
    ^ true
! !

!SequenceableCollection methodsFor:'sorting & reordering'!

reverse
    "reverse the order of the elements inplace"

    |lowIndex "{ Class: SmallInteger }"
     hiIndex  "{ Class: SmallInteger }"
     t|

    hiIndex := self size.
    lowIndex := 1.
    [lowIndex < hiIndex] whileTrue:[
        t := self at:lowIndex.
        self at:lowIndex put:(self at:hiIndex). 
        self at:hiIndex put:t.
        lowIndex := lowIndex + 1.
        hiIndex := hiIndex - 1
    ]
    "#(4 5 6 7 7) reverse"
    "#(1 4 7 10 2 5) asOrderedCollection reverse"
!

quickSortFrom:begin to:end
    "actual quicksort worker for sort-message"

    |b "{ Class: SmallInteger }"
     e "{ Class: SmallInteger }"
     middleElement temp |

    b := begin.
    e := end.
    middleElement := self at:((b + e) // 2).

    [b < e] whileTrue:[
        [b < end and:[(self at:b) < middleElement]] whileTrue:[b := b + 1].
        [e > begin and:[middleElement < (self at:e)]] whileTrue:[e := e - 1].

        (b <= e) ifTrue:[
            (b == e) ifFalse:[
                temp := self at:b.
                self at:b put:(self at:e).
                self at:e put:temp
            ].
            b := b + 1.
            e := e - 1
        ]
    ].
    (begin < e) ifTrue:[self quickSortFrom:begin to:e].
    (b < end) ifTrue:[self quickSortFrom:b to:end]
!

quickSortFrom:begin to:end with:aCollection
    "actual quicksort worker for sortWith-message"

    |b "{ Class: SmallInteger }"
     e "{ Class: SmallInteger }"
     middleElement temp |

    b := begin.
    e := end.
    middleElement := self at:((b + e) // 2).

    [b < e] whileTrue:[
        [b < end and:[(self at:b) < middleElement]] whileTrue:[b := b + 1].
        [e > begin and:[middleElement < (self at:e)]] whileTrue:[e := e - 1].

        (b <= e) ifTrue:[
            (b == e) ifFalse:[
                temp := self at:b.
                self at:b put:(self at:e).
                self at:e put:temp.
                temp := aCollection at:b.
                aCollection at:b put:(aCollection at:e).
                aCollection at:e put:temp
            ].
            b := b + 1.
            e := e - 1
        ]
    ].
    (begin < e) ifTrue:[self quickSortFrom:begin to:e with:aCollection].
    (b < end) ifTrue:[self quickSortFrom:b to:end with:aCollection]
!

quickSortFrom:begin to:end sortBlock:sortBlock
    "actual quicksort worker for sort:-message"

    |b "{ Class: SmallInteger }"
     e "{ Class: SmallInteger }"
     middleElement temp |

    b := begin.
    e := end.
    middleElement := self at:((b + e) // 2).

    [b < e] whileTrue:[
        [b < end and:[sortBlock value:(self at:b) value:middleElement]] whileTrue:[b := b + 1].
        [e > begin and:[sortBlock value:middleElement value:(self at:e)]] whileTrue:[e := e - 1].

        (b <= e) ifTrue:[
            (b == e) ifFalse:[
                temp := self at:b.
                self at:b put:(self at:e).
                self at:e put:temp
            ].
            b := b + 1.
            e := e - 1
        ]
    ].
    (begin < e) ifTrue:[self quickSortFrom:begin to:e sortBlock:sortBlock].
    (b < end) ifTrue:[self quickSortFrom:b to:end sortBlock:sortBlock]
!

quickSortFrom:begin to:end sortBlock:sortBlock with:aCollection
    "actual quicksort worker for sort:with:-message"

    |b "{ Class: SmallInteger }"
     e "{ Class: SmallInteger }"
     middleElement temp |

    b := begin.
    e := end.
    middleElement := self at:((b + e) // 2).

    [b < e] whileTrue:[
        [b < end and:[sortBlock value:(self at:b) value:middleElement]] whileTrue:[b := b + 1].
        [e > begin and:[sortBlock value:middleElement value:(self at:e)]] whileTrue:[e := e - 1].

        (b <= e) ifTrue:[
            (b == e) ifFalse:[
                temp := self at:b.
                self at:b put:(self at:e).
                self at:e put:temp.
                temp := aCollection at:b.
                aCollection at:b put:(aCollection at:e).
                aCollection at:e put:temp
            ].
            b := b + 1.
            e := e - 1
        ]
    ].
    (begin < e) ifTrue:[self quickSortFrom:begin to:e sortBlock:sortBlock with:aCollection].
    (b < end) ifTrue:[self quickSortFrom:b to:end sortBlock:sortBlock with:aCollection]
!

bubbleSort
    "sort the collection inplace using bubbleSort (sloooow)
     - this one makes only sense to sort after inserting an element into
       an already sorted collection (if at all)"

    |index  "{ Class: SmallInteger }"
     index2 "{ Class: SmallInteger }"
     end    "{ Class: SmallInteger }"
     smallest smallestIndex thisOne|

    end := self size.
    index := 1.
    [index <= end] whileTrue:[
        smallest := self at:index.
        smallestIndex := index.
        index2 := index + 1.
        [index2 <= end] whileTrue:[
            (self at:index2) < smallest ifTrue:[
                smallestIndex := index2.
                smallest := self at:index2
            ].
            index2 := index2 + 1
        ].
        (smallestIndex ~~ index) ifTrue:[
            thisOne := self at:index.
            self at:index put:smallest.
            self at:smallestIndex put:thisOne
        ].
        index := index + 1
    ]

    "#(1 16 7 98 3 19 4 0) bubbleSort"
!

sort
    "sort the collection inplace. The elements are compared using
     > and < i.e. they should offer a magnitude-like protocol."

    |stop|

    stop := self size.
    (stop > 1) ifTrue:[
        self quickSortFrom:1 to:stop
    ]

    "#(1 16 7 98 3 19 4 0) sort"
!

sortWith:aCollection
    "sort the receiver collection inplace, also sort aCollection with it.
     Use, when you have a key collection to sort another collection with."

    |stop|

    stop := self size.
    (stop > 1) ifTrue:[
        self quickSortFrom:1 to:stop with:aCollection
    ]

    "|c1 c2|
     c1 := #(1 16 7 9).
     c2 := #('one' 'sixteen' 'seven' 'nine').
     c1 sortWith:c2.
     c1 printNewline.
     c2 printNewline"
!

sort:sortBlock
    "sort the collection inplace using the 2-arg block sortBlock
     for comparison. This allows any sort criteria to be implemented."

    |stop|

    stop := self size.
    (stop > 1) ifTrue:[
        self quickSortFrom:1 to:stop sortBlock:sortBlock
    ]

    "#(1 16 7 98 3 19 4 0) sort:[:a :b | a < b]"
    "#(1 16 7 98 3 19 4 0) sort:[:a :b | a > b]"
!

sort:sortBlock with:aCollection
    "sort the collection inplace using the 2-arg block sortBlock
     for comparison. Also reorder the elements in aCollection"

    |stop|

    stop := self size.
    (stop > 1) ifTrue:[
        self quickSortFrom:1 to:stop sortBlock:sortBlock with:aCollection
    ]

    "|c1 c2|
     c1 := #(1 16 7 9).
     c2 := #('one' 'sixteen' 'seven' 'nine').
     c1 sort:[:a :b | a > b] with:c2.
     c1 printNewline.
     c2 printNewline"
! !

!SequenceableCollection methodsFor:'enumerating'!

do:aBlock
    "evaluate the argument, aBlock for every element in the collection."

    |stop "{ Class:SmallInteger }"|

    stop := self size.
    1 to:stop do:[:index |
        aBlock value:(self at:index).
    ]
!

keysAndValuesDo:aTwoArgBlock
    "evaluate the argument, aBlock for every element in the collection,
     passing both index and element as arguments."

    |stop  "{ Class:SmallInteger }"|

    stop := self size.
    1 to:stop do:[:index |
        aTwoArgBlock value:index value:(self at:index).
    ]
!

from:index1 to:index2 do:aBlock
    "evaluate the argument, aBlock for the elements with index index1 to
     index2 in the collection"

    |start "{ Class:SmallInteger }"
     stop  "{ Class:SmallInteger }" |

    start := index1.
    stop := index2.
    start to:stop do:[:index |
        aBlock value:(self at:index).
    ]
!

from:index1 to:index2 reverseDo:aBlock
    "evaluate the argument, aBlock for the elements with index index1 to
     index2 in the collection. Step in reverse order"

    |start "{ Class:SmallInteger }"
     stop  "{ Class:SmallInteger }" |

    start := index1.
    stop := index2.
    stop to:start by:-1 do:[:index |
        aBlock value:(self at:index).
    ]
!

with:aCollection do:aTwoArgBlock
    "evaluate the argument, aBlock for successive elements from
     each of the two collections self and aCollection.
     aBlock must be a two-argument block"

    |stop  "{ Class: SmallInteger }" |

    stop := self size.
    1 to:stop do:[:index |
        aTwoArgBlock value:(self at:index) value:(aCollection at:index).
    ]
!

reverseDo:aBlock
    "evaluate the argument, aBlock for every element in the collection
     in reverse order"

    |sz  "{ Class:SmallInteger }"|

    sz := self size.
    sz to:1 by:-1 do:[:index |
        aBlock value:(self at:index).
    ]
!

collect:aBlock
    "evaluate the argument, aBlock for every element in the collection
     and return a collection of the results"

    |newCollection
     sz  "{ Class:SmallInteger }"|

    sz := self size.
    newCollection := self species new:sz.
    1 to:sz do:[:index |
        newCollection at:index put:(aBlock value:(self at:index)).
    ].
    ^ newCollection
!

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"

    |element newColl
     sz  "{ Class:SmallInteger }"|

    sz := self size.
    newColl := OrderedCollection new:sz.
    1 to:sz do:[:index |
        element := self at:index.
        (aBlock value:element) ifTrue:[
            newColl add:element
        ].
    ].
    ^ newColl
! !