"======================================================================
|
|   LookupTable Method Definitions
|
|   $Revision: 1.7.5$
|   $Date: 2000/05/28 16:56:52$
|   $Author: pb$
|
 ======================================================================"


"======================================================================
|
| Copyright 1988-92, 1994-95, 1999, 2000 Free Software Foundation, Inc.
| Written by Steve Byrne and Paolo Bonzini.
|
| This file is part of the GNU Smalltalk class library.
|
| The GNU Smalltalk class library is free software; you can redistribute it
| and/or modify it under the terms of the GNU Lesser General Public License
| as published by the Free Software Foundation; either version 2.1, or (at
| your option) any later version.
| 
| The GNU Smalltalk class library is distributed in the hope that it will be
| useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
| General Public License for more details.
| 
| You should have received a copy of the GNU Lesser General Public License
| along with the GNU Smalltalk class library; see the file COPYING.LESSER.
| If not, write to the Free Software Foundation, 59 Temple Place - Suite
| 330, Boston, MA 02111-1307, USA.  
|
 ======================================================================"

Dictionary variableSubclass: #LookupTable
	   instanceVariableNames: 'values'
	   classVariableNames: ''
	   poolDictionaries: ''
	   category: 'Collections-Keyed'
!

LookupTable comment:
'I am similar to Dictionary, except that my representation is
different (more efficient, but not as friendly to the virtual
machine). I use the object equality comparision message =
to determine equivalence of indices.' !


!LookupTable class methodsFor: 'instance creation'!

new
    "Create a new LookupTable with a default size"
    ^self new: 5
! !



!LookupTable methodsFor: 'accessing'!

add: anAssociation
    "Add the anAssociation key to the receiver"
    self at: anAssociation key put: anAssociation value.
    ^anAssociation
!

at: key put: value
    "Store value as associated to the given key"
    | index absent |
    absent := false.
    index := self findIndex: key ifAbsent: [ absent := true ].
    self primAt: index put: key.
    self valueAt: index put: value.
    absent ifTrue: [ self incrementTally ].
    ^value
!

at: key ifAbsent: aBlock
    "Answer the value associated to the given key, or the result of evaluating
     aBlock if the key is not found"
    | index |
    index := self findIndex: key ifAbsent: [ ^aBlock value ].
    ^self valueAt: index
!    
    
at: aKey ifPresent: aBlock
    "If aKey is absent, answer nil. Else, evaluate aBlock passing the
     associated value and answer the result of the invocation"
    | index |
    index := self findIndex: aKey ifAbsent: [^nil ].
    ^aBlock value: (self valueAt: index)
!

associationAt: key ifAbsent: aBlock
    "Answer the key/value Association for the given key. Evaluate aBlock
     (answering the result) if the key is not found"
    | value |
    value := self at: key ifAbsent: [ ^aBlock value ].
    ^Association key: key value: value
! !


!LookupTable methodsFor: 'copying'!

deepCopy
    "Returns a deep copy of the receiver (the instance variables are
     copies of the receiver's instance variables)"
    | key newDict |
    newDict := self copyEmpty: self primSize.
    1 to: self primSize do: [ :index |
	key := self primAt: index.
	key isNil
	    ifFalse: [ newDict whileGrowingAt: key put: (self valueAt: index) ]
    ].
    ^self
! !



!LookupTable methodsFor: 'removing'!

removeKey: key ifAbsent: aBlock
    "Remove the passed key from the LookupTable, answer the result of
     evaluating aBlock if it is not found"
    | index value |
    index := self findIndex: key ifAbsent: [ ^aBlock value ].
    value := self valueAt: index.
    self primAt: index put: nil.
    self valueAt: index put: nil.
    self decrementTally.
    self rehashObjectsAfter: index.
    ^ value
! !



!LookupTable methodsFor: 'enumerating'!

associationsDo: aBlock
    "Pass each association in the LookupTable to aBlock"
    self keysAndValuesDo: [ :key :val | aBlock value:
	(Association key: key value: val)
    ]
!

keysAndValuesDo: aBlock
    "Pass each key/value pair in the LookupTable as two distinct parameters
     to aBlock"

    1 to: self primSize do: [:i |
	(self primAt: i) notNil
	    ifTrue: [ aBlock value: (self primAt: i) value: (self valueAt: i) ]
    ]
! !



!LookupTable methodsFor: 'rehashing'!

rehash
    "Rehash the receiver"
    | copy |
    copy := self copy.
    self resetTally.

    1 to: self primSize do: [:i |
	self primAt: i put: nil.
	self valueAt: i put: nil ].

    copy keysAndValuesDo: [ :key :val | self whileGrowingAt: key put: val ].
! !


!LookupTable methodsFor: 'storing'!

storeOn: aStream
    "Print Smalltalk code compiling to the receiver on aStream"
    | hasElements |
    aStream nextPutAll: '(', self class name , ' new'.
    hasElements := false.
    self keysAndValuesDo:
    	[ :key :value | aStream nextPutAll: ' at: ';
		   store: key;
		   nextPutAll: ' put: ';
		   store: value;
		   nextPut: $;.
	       hasElements := true ].
    hasElements ifTrue: [ aStream nextPutAll: ' yourself' ].
    aStream nextPut: $)
! !



!LookupTable methodsFor: 'private methods'!

initialize: anInteger
    "Private - Instance variable initialization."
    super initialize: anInteger.
    self initValues
!

initValues
    "Private - Initialize the values instance variable."
    values := Array new: self primSize
!

rehashObjectsAfter: index
    "Rehashes all the objects in the collection after index to see if any of
    them hash to index.  If so, that object is copied to index, and the
    process repeats with that object's index, until a nil is encountered."

    | i j size count key |
    i := index.
    size := self primSize.
    [   i = size ifTrue: [ i := 1 ] ifFalse: [ i := i + 1 ].
	key := self primAt: i.
	key notNil
    ]   whileTrue: [
	j := self findIndex: key.
	(self primAt: j) isNil ifTrue: [
	    self primAt: j put: key.
	    self valueAt: j put: (self valueAt: i).
	    self primAt: i put: nil.
	    self valueAt: i put: nil
	]
    ]
!

copyAllFrom: aDictionary
    | key |
    1 to: aDictionary primSize do: [ :index |
	key := aDictionary primAt: index.
	key isNil
	    ifFalse: [ self whileGrowingAt: key put: (aDictionary valueAt: index) ]
    ].
    ^self
!

addWhileGrowing: association
    self whileGrowingAt: association key put: association value
!

whileGrowingAt: key put: value
    "Private - Add the given key/value pair to the receiver. Don't check for
     the LookupTable to be full nor for the key's presence - we want SPEED!"
    | index |
    self
	primAt: (index := self findIndex: key)
	put: key.
    
    self valueAt: index put: value.
    tally := tally + 1.
    ^value
!

valueAt: index
    ^values at: index
!

valueAt: index put: object
    ^values at: index put: object
!

is: anElement sameAs: searchedObject

    "Answer whether findIndex: should stop scanning the receiver: anElement has
     been found and findIndex:'s parameter was searchedObject"

    ^anElement = searchedObject
! !
