[comp.lang.smalltalk] Smalltalk/V for PM V1.0 bug fixes & enhancements Part 2

cowan@marob.masa.com (John Cowan) (07/27/90)

"

Purposes of COLL.ST:

Support a whole bunch of new collection types.  Requires
BUGFIX.ST first.

BitArray: efficiently stored Arrays whose elements must
	be 0 or 1.

BooleanArray: efficiently stored Arrays whose elements
	must be true or false.

IdentitySet: variant of Set where comparison criterion is ==,
not =.

NumericSet:  efficiently stored Set whose elements must be
	members of a prespecified Interval.

CharacterSet: efficiently stored Set whose elements must be
	Characters.

CharacterInterval:  variant of Interval ranging over
	Characters rather than numbers.

Link: utility class for LinkedCollection.

LinkedCollection: variant of OrderedCollection using a
	linked-list technology rather than an array
	technology (insertion and removal are fast,
	random access is very slow).  Optionally, instances
	may also keep a Bag of their contents for fast
	lookup.

LinkedSet: variant of LinkedCollection which does not allow
	duplicate elements.

SortedLinkedCollection:  variant of LinkedCollection which
	maintains elements in sorted order.

SortedLinkedSet: combines virtues of LinkedSet and
	SortedLinkedCollection.

RectArray:  generalized n-dimensional arrays, allowing
	detailed specification of upper and lower bounds.
	Arrays are stored rectangularly in row-major order.
	Useful for converting matrix programs.

SparseArray: efficiently stored Arrays most of whose elements
	should have the same value (need not be 0).

Tree: data structure supporting a value, a parent link,
	and 0 to N child links, with methods for traversal.


"

Array variableSubclass: #BitArray
  instanceVariableNames: 
    'bytes size '
  classVariableNames: 
    'Masks '
  poolDictionaries: '' !


!BitArray class methods !

fromCharacter: aCharacter
        "Private - Answer 0 or 1 if aCharacter is $0 or $1.
        Otherwise signal an error."
    aCharacter == $0 ifTrue: [^0].
    aCharacter == $1 ifTrue: [^1].
    self error: 'Character must be 0 or 1.'!
   
fromString: aString
        "Answer a new BitArray initialized by the
        characters in aString, which must be $0 or $1."
    | size answer |
    size := aString size.
    answer := self new: size.
    1 to: size do: [:i | answer at: i
        put: (self fromCharacter: (aString at: i))].
    ^answer!
   
initialize
        "Private - Set up Masks class variable."
    Masks := #(1 2 4 8 16 32 64 128)!
 
new
        "Answer an empty BitArray."
    ^self new: 0!
 
new: anInteger
        "Answer a new BitArray with anInteger elements,
        all zeros."
    ^self basicNew initialize: anInteger! !



!BitArray methods !

asCharacter: aBit
        "Private - Answer aBit as a character."
    aBit == 1 ifTrue: [^$1] ifFalse: [^$0]!
 
asString
        "Answers the receiver represented as a String
        of $0 and $1 characters."
    | answer |
    answer := String new: size.
    1 to: size do: [:i |
        answer at: i put: (self asCharacter: (self at: i))].
    ^answer!
   
at: anInteger
        "Answers the bit at the index anInteger."
    | mask offset |
    self checkIndex: anInteger.
    mask := Masks at: ((anInteger - 1) \\ 8) + 1.
    offset := ((anInteger - 1) // 8) + 1.
    ((bytes at: offset) bitAnd: mask) ~= 0 ifTrue:
        [^1]
    ifFalse:
        [^0]!
 
at: anInteger put: aBit
        "Stores aBit at element anInteger."
    | bit mask offset byte |
    self checkIndex: anInteger.
    bit := self bit: aBit.
    mask := Masks at: ((anInteger - 1) \\ 8) + 1.
    offset := ((anInteger - 1) // 8) + 1.
    byte := bytes at: offset.
    bit == 1 ifTrue:
        [byte := byte bitOr: mask]
    ifFalse:
        [byte := byte bitAnd: (mask bitXor: 255)].
    bytes at: offset put: byte.!
  
atAllPut: aBit
        "Fill every bit in the receiver with aBit."
    | value |
    (self bit: aBit) == 1 ifTrue:
        [value := 255]
    ifFalse:
        [value := 0].
    1 to: bytes size do: [:n | bytes at: n put: value]!
 
bit: aBit
        "Signal an error if aBit is not 0 or 1."
    aBit == 0 ifTrue: [^0].
    aBit == 1 ifTrue: [^1].
    self error: 'A bit  must be 0 or 1'!
 
bitAnd: aBitArray
        "Answer a BitArray produced by AND-ing the
        bits of the receiver and aBitArray."
    | answer |
    ^self copy bitAndWith: aBitArray!
  
bitAndWith: aBitArray
        "Modify the receiver by AND-ing its bits with
        those of aBitArray."
    self conformCheck: aBitArray.
    1 to: bytes size do: [:n |
        bytes at: n put:
            ((bytes at: n) bitAnd: (aBitArray byteAt: n))]!

bitOr: aBitArray
        "Answer a BitArray produced by OR-ing the
        bits of the receiver and aBitArray."
    ^self copy bitOrWith: aBitArray!
 
bitOrWith: aBitArray
        "Modify the receiver by OR-ing its bits with
        those of aBitArray."
    self conformCheck: aBitArray.
    1 to: bytes size do: [:n |
        bytes at: n put:
            ((bytes at: n) bitOr: (aBitArray byteAt: n))]!
   
bitXor: aBitArray
        "Answer a BitArray produced by XOR-ing the
        bits of the receiver and aBitArray."
    ^self copy bitXorWith: aBitArray!
  
bitXorWith: aBitArray
        "Modify the receiver by XOR-ing its bits with
        those of aBitArray."
    self conformCheck: aBitArray.
    1 to: bytes size do: [:n |
        bytes at: n put:
            ((bytes at: n) bitXor: (aBitArray byteAt: n))]!

byteAt: anInteger
        "Private - Answer a byte from the bytes array."
    ^bytes at: anInteger!
   
conformCheck: aBitArray
        "Signal an error if aBitArray does not have the
        same size as the receiver."
    size = aBitArray size ifFalse:
        [self error: 'Bit arrays not conformable']!
  
deepCopy
        "Since the contents are SmallIntegers,
        deepCopy  == shallowCopy."
    ^self shallowCopy!

flip
        "Invert the sense of every bit in the receiver."
    1 to: bytes size do: [:n |
        bytes at: n put: ((bytes at: n) bitXor: 255)]!
  
initialize: anInteger
        "Private - Initialize instance variables.
        Size of receiver will be anInteger."
    | byteCount |
    size := anInteger.
    byteCount := size // 8.
    (size \\ 8) == 0 ifFalse:
        [byteCount := byteCount + 1].
    bytes := ByteArray new: byteCount.!
   
not
        "Answer a BitArray with every bit in the receiver
        inverted."
    | answer |
    answer := self copy.
    ^answer flip!
 
replace: aBitArray
        "Private - Replace our bits with aBitArray's bits."
    1 to: bytes size do: [:n |
        bytes at: n put: (aBitArray byteAt: n)]!
   
shallowCopy
        "Answer a copy of the receiver."
    ^(BitArray new: size) replace: self.!

size
        "Answer the number of elements in the receiver."
    ^size!
  
species
        "Species of BitArray is Array."
    ^Array! !

BitArray variableSubclass: #BooleanArray
  instanceVariableNames: ''
  classVariableNames: ''
  poolDictionaries: ''   !


!BooleanArray class methods !

fromCharacter: aCharacter
        "Private - Answer true or false if aCharacter is
        $T or $t or $F or $f.  Otherwise signal an error."
    aCharacter == $T ifTrue: [^true].
    aCharacter == $t ifTrue: [^true].
    aCharacter == $F ifTrue: [^false].
    aCharacter == $f ifTrue: [^false].
    self error: 'Character must be T, t, F, or F.'! !



!BooleanArray methods !
 
asCharacter: aBoolean
        "Private - Answer a Boolean as $T or $F."
    aBoolean ifTrue: [^$T] ifFalse: [^$F]!

at: anInteger
        "Answer the Boolean at the index anInteger."
    ^(super at: anInteger) == 1!
   
bit: aBit
        "Answer 1 if aBit is true, 0 if aBit is false.
        Signal an error otherwise."
    aBit ifTrue: [^1] ifFalse: [^0]! !

Set subclass: #IdentitySet
  instanceVariableNames: ''
  classVariableNames: ''
  poolDictionaries: '' !


!IdentitySet class methods ! !



!IdentitySet methods !
 
findElementIndex: anObject
        "Private - Answer the index position of anObject in the
         receiver or the first empty element position.
        Copied from Set method, but with = changed to ==."
    | index indexedObject lastIndex |
    lastIndex := contents size.
    index := (anObject hash) \\ lastIndex + 1.
    [(indexedObject := contents at: index) == anObject]
        whileFalse: [
            (indexedObject == nil)
                ifTrue: [^index].
            (index := index + 1) > lastIndex
                ifTrue: [ "index wraparound"
                    index := 1]].
    ^index! !

Set subclass: #NumericSet
  instanceVariableNames: 
    'interval '
  classVariableNames: ''
  poolDictionaries: ''   !


!NumericSet class methods !
  
new
        "Must use over: to create an NumericSet."
    ^self invalidMessage!
   
new: anInteger
        "Must use over: to create an NumericSet."
    ^self invalidMessage!

over: anInterval
        "Answer a new empty NumericSet defined over
        anInterval.  If anInterval is really a number,
        use 1 to: anInterval instead."
    | interval |
    (anInterval isKindOf: Number) ifTrue:
        [interval := 1 to: anInterval]
    ifFalse:
        [interval := anInterval].
    ^self basicNew initialize: interval.! !



!NumericSet methods !
   
add: anElement
        "Adds anElement to the receiver."
    self index: anElement ifFound: [:index | self]
    ifNotFound: [:index |
        elementCount := elementCount + 1.
        contents at: index put: 1.
        index]
    ifInvalid: [^self error: anElement printString,
        ' cannot belong to ', self printString].
    ^anElement!
 
contents: aBitArray count: anInteger
        "Private - Set up instance variables for shallowCopy."
    contents := aBitArray.
    elementCount := anInteger.!
   
do: aBlock
        "Evaluate the one-argument block over the
        elements of the receiver."
    interval do: [:element |
        (self includes: element) ifTrue:
            [aBlock value: element]]!

includes: anElement
        "Answers true if anElement is a member of
        the receiver."
    self index: anElement ifFound: [:index | ^true]
    ifNotFound: [:index | ^false]
    ifInvalid: [^false].!
   
index: anInteger ifFound: block1 ifNotFound: block2 ifInvalid: block3
        "Private - Get index into contents based on anInteger.
        If bit is true, evaluate block1 with index and return.
        If bit is false, evaluate block2 with index and return.
        If index is out of range, evaluate block3 and return."
     | index |
    anInteger < interval first ifTrue:
        [^block3 value].
    anInteger > interval last ifTrue:
        [^block3 value].
    index := ((anInteger - interval first)
        / interval increment) + 1.
    (index isKindOf: Integer) ifFalse:
        [^block3 value].
    (contents at: index) ~~ 0
        ifTrue: [^block1 value: index]
        ifFalse: [^block2 value: index]!

initialize: anInterval
        "Private - Set up instance variables."
    interval := anInterval.
    contents := BitArray new: interval size.
    elementCount := 0.!
  
maximumSize
        "Maximum size to which receiver can grow."
    ^interval size.!
   
remove: anElement ifAbsent: aBlock
        "Removes the specified element from the
        receiver.  If not found, evaluates aBlock with
        no argument and answers it."
    self index: anElement ifFound: [:index |
        contents at: index put: 0.
        elementCount := elementCount - 1.
        ^anElement]
    ifNotFound: [:index |
        aBlock value]
    ifInvalid: [aBlock value]!
   
shallowCopy
        "Answer a shallow copy of the receiver."
    ^(self class over: interval) contents: contents count: elementCount!
 
species
        "Species of NumericSet is Set."
    ^Set! !

NumericSet subclass: #CharacterSet
  instanceVariableNames: ''
  classVariableNames: ''
  poolDictionaries: '' !


!CharacterSet class methods !

new
        "Answer a new empty CharacterSet."
    | temporaries |
    ^super over: 256! !



!CharacterSet methods !

add: aCharacter
        "Adds aCharacter to the receiver."
    ^super add: aCharacter asciiValue!
 
do: aBlock
        "Evaluate the one-argument block over the
        elements of the receiver."
    interval do: [:element |
        (super includes: element) ifTrue:
            [aBlock value: (Character value: element)]]!

includes: aCharacter
        "Answers true if aCharacter is an element of
        the receiver."
    ^super includes: aCharacter asciiValue!
 
remove: aCharacter
        "Removes aCharacter from the receiver and
        answers it.  If not present in the receiver,
        signal an error."
    super remove: aCharacter asciiValue.
    ^aCharacter!
  
remove: aCharacter ifAbsent: aBlock
        "Removes aCharacter from the receiver and
        answers it.  If not present in the receiver,
        evaluate aBlock and answer the result."
    super remove: aCharacter asciiValue
        ifAbsent: [^aBlock value].
    ^aCharacter! !

Interval subclass: #CharacterInterval
  instanceVariableNames: ''
  classVariableNames: ''
  poolDictionaries: ''  !


!CharacterInterval class methods !
   
from: beginningCharacter to: endCharacter
        "Answer an Interval from beginningCharacter to
         endCharacter incrementing by one."
    ^self basicNew
        initBegin: beginningCharacter asciiValue
        end: endCharacter asciiValue
        incr: 1!

from: beginningCharacter
    to: endCharacter
    by: incrementInteger
        "Answer an Interval from beginningCharacter to
         endCharacter incrementing by incrementInteger."
    ^self basicNew
        initBegin: beginningCharacter asciiValue
        end: endCharacter asciiValue
        incr: incrementInteger! !



!CharacterInterval methods !
   
at: anInteger
        "Answer the character at index position
         anInteger in the receiver interval."
    ^Character value: (super at: anInteger)!
 
first
        "Answer the first element of the Interval."
    ^Character value: beginning!

last
        "Answer the last element of the Interval."
    ^Character value: end!

printOn: aStream
        "Display terse version of receiver on aStream."
    (Character value: beginning) printOn: aStream.
    aStream nextPutAll: ' to: '.
    (Character value: end) printOn: aStream.
    increment ~= 1 ifTrue:
        [aStream nextPutAll: ' by: '.
        increment printOn: aStream]! !

Object subclass: #Link
  instanceVariableNames: 
    'value before after '
  classVariableNames: ''
  poolDictionaries: ''    !


!Link class methods !

value: anObject
        "Answer a new unlinked Link with value set
        to anObject."
    ^(super new) value: anObject; yourself! !



!Link methods !

addAfter: aLink
        "Make the receiver the successor of aLink.
        Answer true if aLink formerly had no successor."
    self unlink.
    after := aLink after.
    before := aLink.
    aLink after: self.
    after isNil
        ifTrue: [^true]
        ifFalse: [after before: self.
            ^false]!
 
addBefore: aLink
        "Make the receiver the predecessor of aLink.
        Answer true if aLink formerly had no predecessor."
    self unlink.
    before := aLink before.
    after := aLink.
    aLink before: self.
    before isNil
        ifTrue: [^true]
        ifFalse: [before after: self.
            ^false]!
 
after
        "Answer the Link after the receiver, or nil
        if none."
    ^after!
  
after: aLink
        "Private - Set the after instance variable
        to aLink.  One-way and dangerous."
    after := aLink!
   
before
        "Answer the Link before the receiver, or nil
        if none."
    ^before!
   
before: aLink
        "Private - Set the before instance variable
        to aLink.  One-way and dangerous."
    before := aLink!

unlink
        "Remove all links to and from receiver."
    before notNil ifTrue: [before after: after].
    after notNil ifTrue: [after before: before].
    after := before := nil!
   
value
        "Answer the value of the receiver."
    ^value!
 
value: anObject
        "Set the value of the receiver to anObject.
        Answer anObject."
    ^value := anObject! !

OrderedCollection subclass: #LinkedCollection
  instanceVariableNames: ''
  classVariableNames: ''
  poolDictionaries: ''  !


!LinkedCollection class methods !

new
        "Answer a new, empty LinkedCollection."
    ^self basicNew!
   
new: anInteger
        "Answer a new LinkedCollection initially
        capable of holding anInteger entries.
        The concept makes no sense for
        LinkedCollections, so use new."
    ^self new.!
   
quickNew
        "Returns a new LinkedCollection that has a
        Bag inside for fast lookup of entries."
    ^(self basicNew) setContents: Bag new! !



!LinkedCollection methods !
  
= aCollection
        "Answer true if the elements contained by
         the receiver are equal to the elements
         contained by the argument aCollection."
    | link |
    self == aCollection ifTrue: [^true].
    aCollection class == self class ifFalse: [^false].
    link := startPosition.
    aCollection do: [:element |
        link isNil ifTrue: [^false].
        link value = element ifFalse: [^false].
        link := link after].
    ^link isNil!
 
add: anObject
        "Answer anObject.  Add anObject after the
         last element of the receiver collection."
    | link |
    link := Link value: anObject.
    (self hookAdd: link) ifTrue: [^anObject].
    link addAfter: endPosition.
    endPosition := link.
    ^anObject!
 
add: newObject after: oldObject
        "Answer newObject.  Insert newObject immediately after
         the element oldObject in the receiver collection.  If
         oldObject is not in the collection, report an error."
    | newLink oldLink |
    newLink := Link value: newObject.
    (self hookAdd: newLink) ifTrue: [^newObject].
    oldLink := self findLink: oldObject.
    (newLink addAfter: oldLink)
        ifTrue: [endPosition := newLink].
    ^newObject!
   
add: anObject afterIndex: anInteger
        "Answer anObject.  Insert anObject at index position
         anInteger + 1 in the receiver collection.  If anInteger
         is out of the collection bounds, report an error."
    | newLink oldLink |
    newLink := Link value: anObject.
    (self hookAdd: newLink) ifTrue: [^anObject].
    oldLink := self linkAt: anInteger.
    (newLink addAfter: oldLink)
        ifTrue: [endPosition := newLink].
    ^anObject!
   
add: newObject before: oldObject
        "Answer newObject.  Insert newObject immediately before
         the element oldObject in the receiver collection.  If
         oldObject is not in the collection, report an error."
    | newLink oldLink |
    newLink := Link value: newObject.
    (self hookAdd: newLink) ifTrue: [^newObject].
    oldLink := self findLink: oldObject.
    (newLink addBefore: oldLink)
        ifTrue: [startPosition := newLink].
    ^newObject!
  
add: anObject beforeIndex: anInteger
        "Answer anObject.  Insert anObject at index position
         anInteger - 1 in the receiver collection.  If anInteger
         is out of the collection bounds, report an error."
    | newLink oldLink |
    newLink := Link value: anObject.
    (self hookAdd: newLink) ifTrue: [^anObject].
    oldLink := self linkAt: anInteger.
    (newLink addBefore: oldLink)
        ifTrue: [startPosition := newLink].
    ^anObject!
   
addFirst: anObject
        "Answer anObject.  Add anObject before the
         first element of the receiver collection."
    | link |
    link := Link value: anObject.
    (self hookAdd: link) ifTrue: [^anObject].
    link addBefore: startPosition.
    startPosition := link.
    ^anObject!
 
addLast: anObject
        "Answer anObject.  Add anObject after the
         last element of the receiver collection."
    | link |
    link := Link value: anObject.
    (self hookAdd: link) ifTrue: [^anObject].
    link addAfter: endPosition.
    endPosition := link.
    ^anObject!
 
after: anObject ifNone: aBlock
        "Answer the element that immediately follows
         anObject in the receiver collection.  If anObject
         is not an element of the receiver, aBlock is
         evaluated (with no arguments)."
    | link |
    link := self findLink: anObject
        ifNone: [^aBlock value].
    ^self checkedValue: link after!
 
at: anInteger
        "Answer the element of the receiver at index
         position anInteger.  If anInteger is an invalid
         index for the receiver collection, report an error."
    ^(self linkAt: anInteger) value!
  
at: anInteger put: anObject
        "Answer anObject.  Replace the element of the
         receiver at index position anInteger with the
         anObject.  If anInteger is an invalid index
         for the receiver collection, report an error."
    ^(self linkAt: anInteger) value: anObject!
   
atAllPut: anObject
        "Answer the receiver after each element
         has been replaced with anObject."
    self linksDo: [:link | link value: anObject].!
 
before: anObject ifNone: aBlock
        "Answer the element that immediately precedes
         anObject in the receiver collection.  If anObject
         is not an element of the receiver, aBlock is
         evaluated (with no arguments)."
    | link |
    link := self findLink: anObject
        ifNone: [^aBlock value].
    link isNil ifTrue: [^self errorInBounds].
    ^self checkedValue: link before!
   
checkedValue: aLink
        "Private - If aLink is a link, return value.
        If aLink is nil, generate error."
    aLink isNil
        ifTrue: [^self errorAbsentElement]
        ifFalse: [^aLink value]!
 
consistent
        "Answer true if receiver link pointers are fully
        consistent."
    | otherLink |
    startPosition before isNil ifFalse: [^false].
    endPosition after isNil ifFalse: [^false].
    self linksDo: [:link |
        otherLink := link after.
        otherLink notNil ifTrue:
            [otherLink before = link ifFalse: [^false]]].
    self reverseLinksDo: [:link |
            otherLink := link before.
            otherLink notNil ifTrue:
            [otherLink after = link ifFalse: [^false]]].
    contents notNil
        ifTrue: [contents size = self size ifFalse: [^false].
            self do: [:obj |
                (contents includes: obj) ifFalse: [^false]]].
    ^true!
  
copyFrom: beginning to: end
        "Answer an OrderedCollection containing the
         elements of the receiver from index position
         beginning through index position end."
    | answer |
    answer := self emptyCopy.
    self linksDo: [:link | answer add: link value]
        from: beginning to: end.
    ^answer!
 
copyReplaceFrom: start to: stop with: aCollection
        "Answer a new collection containing a copy of the
         receiver with the elements at index positions from
         start through stop replaced with the elements of
         aCollection."
    | answer |
    answer := self emptyCopy: self.
    self linksDo: [:link | answer add: link value]
        from: 1 to: start - 1.
    aCollection do: [:obj | answer add: obj].
    self linksDo: [:link | answer add: link value]
        from: stop + 1 to: self size.
    ^answer!
   
copyWith: anObject
        "Answer a copy of the receiver with
         anObject added to it as an element."
    ^(self copy) add: anObject; yourself!
   
copyWithout: anObject
        "Answer a copy of the receiver excluding
         the first element that equals anObject,
         if any."
    ^(self copy) remove: anObject; yourself!
  
do: aBlock
        "Answer the receiver.  For each value in the receiver,
         evaluate aBlock with that value as the argument."
    self linksDo: [:link | aBlock value: link value].!
  
emptyCopy
        "Return a new instance which shares the
        properties of the receiver, but is empty."
    contents notNil
        ifTrue: [^self new contents: contents emptyCopy]
        ifFalse: [^self new].!
   
emptyCopy: size
        "Return a new instance which shares the
        properties of the receiver, but is empty.
        The size argument is ignored for LinkedCollections."
    ^self emptyCopy!
 
findFirst: aBlock
        "Answer the index of the first element of the
         receiver that causes aBlock to evaluate to true
         (with that element as the argument).  If no such
         element is found, report an error."
    | index |
    index := 0.
    self linksDo: [:link |
        index := index + 1.
        (aBlock value: link value)
            ifTrue: [^index]].
    self errorAbsentElement!
  
findLast: aBlock
        "Answer the index of the last element of the
         receiver that causes aBlock to evaluate to true
         (with that element as the argument).  If no such
         element is found, report an error."
    | index |
    index := self size + 1.
    self reverseLinksDo: [:link |
        index := index - 1.
        (aBlock value: link value)
            ifTrue: [^index]].
    self errorAbsentElement!
 
findLink: anObject
        "Return a Link whose value is anObject.
        Error if no such Link."
    ^self findLink: anObject
        ifNone: [self errorAbsentElement].!
 
findLink: anObject ifNone: aBlock
        "Return a Link whose value is anObject.
        If no such link, evaluate aBlock with no arguments."
    self linksDo: [:link |
        link value = anObject ifTrue: [^link]].
    ^aBlock value.!
  
first
        "Answer the first element of the receiver.
         Report an error if the receiver has no elements."
    startPosition isNil
        ifTrue: [^self accessEmptyCollection].
    ^startPosition value.!
  
firstLink
        "Answer the first link of receiver."
    ^startPosition.!
   
grow
        "Private - Answer the receiver; growing is
        not necessary for LinkedCollections."!

hookAdd: aLink
        "Private - Answers true if the add has been done
        within this method; useful for subclasses to get
        a handle on adds.  By default, handles the case
        of an empty receiver."
    contents notNil ifTrue: [contents add: aLink value].
    startPosition isNil
        ifTrue: [startPosition := endPosition := aLink.
                ^true]
        ifFalse: [^false].!

hookRemove: aLink
        "Private - Allows subclasses to get a handle
        on removes.  By default, handles the case of
        removing the only element."
    aLink == startPosition
        ifTrue: [startPosition := aLink after].
    aLink == endPosition
        ifTrue: [endPosition := aLink before].
    contents notNil isTrue: [contents remove: aLink value]!
  
includes: anObject
        "Answer true if the receiver contains an element
         equal to anObject, else answer false."
    contents notNil ifTrue: [^contents includes: anObject].
    self do:
        [:object | object = anObject ifTrue: [^true]].
    ^false.!
  
indexOf: anObject ifAbsent: aBlock
        "Answer the index position of the element equal
         to anObject in the receiver.  If no such element
         is found, evaluate aBlock (without any arguments)."
    | index |
    index := 0.
    self linksDo: [:link |
        index := index + 1.
        link value = anObject
            ifTrue: [^index]].
    ^aBlock value.!
   
isEmpty
        "Answer true if receiver has no elements"
    ^startPosition isNil!
   
last
        "Answer the last element of the receiver.
         Report an error if the receiver has no elements."
    startPosition isNil
        ifTrue: [^self accessEmptyCollection].
    ^endPosition value.!
  
lastLink
        "Answer the last link of receiver."
    ^endPosition.!
   
linkAt: anInteger
        "Return a Link whose index is anInteger.
        If no such link, error."
    | index |
    index := 0.
    self linksDo: [:link |
        index := index + 1.
        index = anInteger ifTrue: [^link]].
    ^self errorAbsentElement.!
 
linksDo: aBlock
        "Answer the receiver.  For each link in the receiver,
         evaluate aBlock with that link as the argument."
    | link |
    link := startPosition.
    [link == endPosition] whileFalse:
        [aBlock value: link.
        link := link after].
    aBlock value: link.!

linksDo: aBlock from: start to: stop
        "Evaluate aBlock on each of the links of the receiver
         at index positions start through stop."
    | index |
    index := 0.
    self linksDo: [:link |
        index := index + 1.
        index > stop ifTrue: [^self].
        index < start ifFalse: [aBlock value: link]].!
   
notEmpty
        "Answer true if receiver has at least one element"
    ^startPosition notNil!

occurrencesOf: anObject
        "Answer the number of elements contained
         in the receiver collection that are equal
         to anObject."
    contents notNil
        ifTrue: [^contents occurrencesOf: anObject]
        ifFalse: [^super occurrencesOf: anObject]!
 
remove: anObject ifAbsent: aBlock
        "Answer anObject.  Remove the element anObject from
         the receiver collection.  If anObject is not an
         element of the receiver, aBlock is evaluated
         (with no arguments)."
    | link |
    link := self findLink: anObject
        ifNone: [^aBlock value].
    self hookRemove: link.
    ^link unlink value.!
  
removeFirst
        "Remove and answer the first element of the receiver.
         If the collection is empty, report an error."
    | link |
    startPosition isNil
        ifTrue: [self accessEmptyCollection].
    link := startPosition.
    startPosition := link after.
    self hookRemove: link.
    ^link unlink value.!

removeIndex: anInteger
        "Answer the receiver.  Remove the element of the receiver
         at index position anInteger.  If anInteger is an invalid
         index for the receiver, report an error."
    | link |
    link := self linkAt: anInteger.
    self hookRemove: link.
    ^link unlink value.!
   
removeLast
        "Remove and answer the kast element of the receiver.
         If the collection is empty, report an error."
    | link |
    startPosition isNil
        ifTrue: [^self accessEmptyCollection].
    link := endPosition.
    endPosition := link before.
    self hookRemove: link.
    ^link unlink value.!

replaceFrom: start to: stop withObject: anObject
        "Replace each of the elements of the receiver
         at index positions start through stop with
         anObject.  Answer anObject."
    self linksDo: [:link | link value: anObject]
        from: start to: stop.
    ^anObject!

reverseDo: aBlock
        "Answer the receiver.  For each value in the receiver,
         evaluate aBlock with that value as the argument.
        Values are processed in reverse order."
    self reverseLinksDo: [:link | aBlock value: link value]!
 
reverseLinksDo: aBlock
        "Answer the receiver.  For each link in the receiver,
         evaluate aBlock with that link as the argument.
        Links are processed in reverse order."
    | link |
    link := endPosition.
    [link notNil] whileTrue:
        [aBlock value: link.
        link := link before.]!
 
setContents: aCollection
        "Private - Sets the contents instance variable to an
        auxiliary collection used for fast lookup."
    contents := aCollection!
   
size
        "Answer the number of elements contained by
         the receiver collection."
    | answer |
    answer := 0.
    self linksDo: [:link | answer := answer + 1].
    ^answer! !

LinkedCollection subclass: #LinkedSet
  instanceVariableNames: ''
  classVariableNames: ''
  poolDictionaries: ''  !


-- 
cowan@marob.masa.com			(aka ...!hombre!marob!cowan)
			e'osai ko sarji la lojban