diff --git a/src/Containers-Trie-Tests/AbstractCTTrieTest.class.st b/src/Containers-Trie-Tests/AbstractCTTrieTest.class.st index 70d97f8..9e2107b 100644 --- a/src/Containers-Trie-Tests/AbstractCTTrieTest.class.st +++ b/src/Containers-Trie-Tests/AbstractCTTrieTest.class.st @@ -1,25 +1,26 @@ Class { - #name : #AbstractCTTrieTest, - #superclass : #TestCase, + #name : 'AbstractCTTrieTest', + #superclass : 'TestCase', #instVars : [ 'trie' ], - #category : #'Containers-Trie-Tests' + #category : 'Containers-Trie-Tests', + #package : 'Containers-Trie-Tests' } -{ #category : #utils } +{ #category : 'utils' } AbstractCTTrieTest >> classUnderTest [ ^ CTTrie ] -{ #category : #utils } +{ #category : 'utils' } AbstractCTTrieTest >> newInstance [ ^ self classUnderTest new ] -{ #category : #running } +{ #category : 'running' } AbstractCTTrieTest >> setUp [ "example taken from " diff --git a/src/Containers-Trie-Tests/CTOptimizedTrieTest.class.st b/src/Containers-Trie-Tests/CTOptimizedTrieTest.class.st index e2691a5..99740a9 100644 --- a/src/Containers-Trie-Tests/CTOptimizedTrieTest.class.st +++ b/src/Containers-Trie-Tests/CTOptimizedTrieTest.class.st @@ -1,11 +1,193 @@ Class { - #name : #CTOptimizedTrieTest, - #superclass : #CTTrieTest, - #category : #'Containers-Trie-Tests' + #name : 'CTOptimizedTrieTest', + #superclass : 'CTTrieTest', + #category : 'Containers-Trie-Tests', + #package : 'Containers-Trie-Tests' } -{ #category : #utils } +{ #category : 'utils' } CTOptimizedTrieTest >> classUnderTest [ ^ CTOptimizedTrie ] + +{ #category : 'tests' } +CTOptimizedTrieTest >> testCompressNodeDoesNotMergeWhenAncestorsTooSmall [ + | rootNode nodeToCheck ancestors | + trie := CTOptimizedTrie new. + + trie at: 'cat' put: 1. + + rootNode := trie instVarNamed: 'root'. + nodeToCheck := rootNode children first. + + ancestors := OrderedCollection new. + ancestors add: nodeToCheck. + + trie compressNode: nodeToCheck ancestors: ancestors. + + self assert: rootNode keys size equals: 1. + self assert: rootNode keys first equals: 'cat'. + self assert: (trie at: 'cat') equals: 1. +] + +{ #category : 'tests' } +CTOptimizedTrieTest >> testCompressNodeDoesNotMergeWhenHasValue [ + | rootNode nodeToCheck ancestors | + trie := CTOptimizedTrie new. + + trie at: 'cat' put: 1. + trie at: 'ca' put: 2. + + rootNode := trie instVarNamed: 'root'. + nodeToCheck := rootNode children first. + + ancestors := OrderedCollection new. + ancestors add: rootNode. + ancestors add: nodeToCheck. + + trie compressNode: nodeToCheck ancestors: ancestors. + + self assert: rootNode keys size equals: 1. + self assert: rootNode keys first equals: 'ca'. + self assert: (trie at: 'ca') equals: 2. +] + +{ #category : 'tests' } +CTOptimizedTrieTest >> testCompressNodeDoesNotMergeWithMultipleChildren [ + | rootNode nodeToCheck ancestors | + trie := CTOptimizedTrie new. + + trie at: 'cat' put: 1. + trie at: 'car' put: 2. + + rootNode := trie instVarNamed: 'root'. + nodeToCheck := rootNode children first. + + nodeToCheck instVarNamed: 'nodeValue' put: nil. + + ancestors := OrderedCollection new. + ancestors add: rootNode. + ancestors add: nodeToCheck. + + trie compressNode: nodeToCheck ancestors: ancestors. + + self assert: rootNode keys size equals: 1. + self assert: rootNode keys first equals: 'ca'. + self assert: nodeToCheck keys size equals: 2. + self assert: (nodeToCheck keys includesAll: #('t' 'r')). + self assert: (trie at: 'cat') equals: 1. + self assert: (trie at: 'car') equals: 2. +] + +{ #category : 'tests' } +CTOptimizedTrieTest >> testCompressNodeMergesDeepSingleChild [ + | rootNode parentNode nodeToCheck ancestors | + trie := CTOptimizedTrie new. + + trie at: 'a' put: 1. + trie at: 'ab' put: 2. + trie at: 'abc' put: 3. + + rootNode := trie instVarNamed: 'root'. + parentNode := rootNode children first. + nodeToCheck := parentNode children first. + + nodeToCheck instVarNamed: 'nodeValue' put: nil. + + ancestors := OrderedCollection new. + ancestors add: rootNode. + ancestors add: parentNode. + ancestors add: nodeToCheck. + + trie compressNode: nodeToCheck ancestors: ancestors. + + self assert: parentNode keys size equals: 1. + self assert: parentNode keys first equals: 'bc'. + + self assert: (trie at: 'abc') equals: 3. + self assert: (trie at: 'a') equals: 1. +] + +{ #category : 'tests' } +CTOptimizedTrieTest >> testCompressNodeMergesSingleChildWithoutValue [ + | rootNode nodeToCheck ancestors | + trie := CTOptimizedTrie new. + + trie at: 'cat' put: 1. + trie at: 'ca' put: 2. + + rootNode := trie instVarNamed: 'root'. + nodeToCheck := rootNode children first. + + nodeToCheck instVarNamed: 'nodeValue' put: nil. + + ancestors := OrderedCollection new. + ancestors add: rootNode. + ancestors add: nodeToCheck. + + trie compressNode: nodeToCheck ancestors: ancestors. + + self assert: rootNode keys size equals: 1. + self assert: rootNode keys first equals: 'cat'. + self assert: (trie at: 'cat') equals: 1. +] + +{ #category : 'tests' } +CTOptimizedTrieTest >> testOptimizedTrieCompressesOnDeletion [ + | trie rootNode | + trie := CTOptimizedTrie new. + trie at: 'car' put: 1. + trie at: 'cat' put: 2. + + trie removeKey: 'cat'. + + self assert: (trie at: 'car') equals: 1. + + rootNode := trie instVarNamed: 'root'. + + self assert: rootNode children first children isEmpty. +] + +{ #category : 'tests' } +CTOptimizedTrieTest >> testOptimizedTrieDoesNotMergeWhenParentIsWord [ + | trie rootNode parentNode | + trie := CTOptimizedTrie new. + + trie at: 'ca' put: 1. + trie at: 'cat' put: 2. + trie at: 'car' put: 3. + + trie removeKey: 'cat'. + + self assert: (trie at: 'ca') equals: 1. + self assert: (trie at: 'car') equals: 3. + + rootNode := trie instVarNamed: 'root'. + parentNode := rootNode children first. + + self assert: parentNode isWord. + self assert: parentNode nodeValue equals: 1. + self assert: parentNode keys size equals: 1. +] + +{ #category : 'tests' } +CTOptimizedTrieTest >> testOptimizedTrieDoesNotMergeWithMultipleChildren [ + | trie rootNode parentNode | + trie := CTOptimizedTrie new. + + trie at: 'cat' put: 1. + trie at: 'car' put: 2. + trie at: 'cab' put: 3. + + trie removeKey: 'cat'. + + self assert: (trie at: 'car') equals: 2. + self assert: (trie at: 'cab') equals: 3. + + rootNode := trie instVarNamed: 'root'. + parentNode := rootNode children first. + + self assert: parentNode isWord not. + self assert: parentNode keys size equals: 2. +] diff --git a/src/Containers-Trie-Tests/CTSuffixTreeTest.class.st b/src/Containers-Trie-Tests/CTSuffixTreeTest.class.st index 1c87818..ceabdb0 100644 --- a/src/Containers-Trie-Tests/CTSuffixTreeTest.class.st +++ b/src/Containers-Trie-Tests/CTSuffixTreeTest.class.st @@ -1,20 +1,21 @@ Class { - #name : #CTSuffixTreeTest, - #superclass : #TestCase, + #name : 'CTSuffixTreeTest', + #superclass : 'TestCase', #instVars : [ 'suffixTree' ], - #category : #'Containers-Trie-Tests' + #category : 'Containers-Trie-Tests', + #package : 'Containers-Trie-Tests' } -{ #category : #running } +{ #category : 'running' } CTSuffixTreeTest >> setUp [ super setUp. suffixTree := CTSuffixTree new ] -{ #category : #tests } +{ #category : 'tests' } CTSuffixTreeTest >> testAddingAStringAddsAllSubstring [ suffixTree atSuffixesOf: 'banana' put: 1. @@ -24,7 +25,7 @@ CTSuffixTreeTest >> testAddingAStringAddsAllSubstring [ ] -{ #category : #tests } +{ #category : 'tests' } CTSuffixTreeTest >> testRemovingAStringRemovesAllSuffixes [ suffixTree atSuffixesOf: 'banana' put: 1. diff --git a/src/Containers-Trie-Tests/CTTrieNodeTest.class.st b/src/Containers-Trie-Tests/CTTrieNodeTest.class.st index 13cf4d4..e2afb80 100644 --- a/src/Containers-Trie-Tests/CTTrieNodeTest.class.st +++ b/src/Containers-Trie-Tests/CTTrieNodeTest.class.st @@ -1,10 +1,11 @@ Class { - #name : #CTTrieNodeTest, - #superclass : #AbstractCTTrieTest, - #category : #'Containers-Trie-Tests' + #name : 'CTTrieNodeTest', + #superclass : 'AbstractCTTrieTest', + #category : 'Containers-Trie-Tests', + #package : 'Containers-Trie-Tests' } -{ #category : #'tests - nodes' } +{ #category : 'tests - nodes' } CTTrieNodeTest >> testIsLeaf [ | aTrie aWord aLongerWord aNode | aTrie := self newInstance. @@ -26,7 +27,7 @@ CTTrieNodeTest >> testIsLeaf [ self assert: aLongerWord isLeaf ] -{ #category : #'tests - nodes' } +{ #category : 'tests - nodes' } CTTrieNodeTest >> testIsLeafWikipediaExample [ self assert: (trie find: 'to') isLeaf. @@ -41,7 +42,7 @@ CTTrieNodeTest >> testIsLeafWikipediaExample [ self assert: (trie isCompressed) ] -{ #category : #'tests - nodes' } +{ #category : 'tests - nodes' } CTTrieNodeTest >> testIsNode [ | aTrie aWord aLongerWord aNode | aTrie := self newInstance. @@ -74,7 +75,7 @@ CTTrieNodeTest >> testIsNode [ self deny: trie rootNode isNode ] -{ #category : #'tests - nodes' } +{ #category : 'tests - nodes' } CTTrieNodeTest >> testIsNotCompressed [ | aTrie | @@ -90,7 +91,7 @@ CTTrieNodeTest >> testIsNotCompressed [ self deny: aTrie isCompressed ] -{ #category : #'tests - nodes' } +{ #category : 'tests - nodes' } CTTrieNodeTest >> testIsRoot [ | aTrie aWord aLongerWord aNode | aTrie := self newInstance. @@ -123,7 +124,7 @@ CTTrieNodeTest >> testIsRoot [ self assert: trie rootNode isRoot ] -{ #category : #'tests - nodes' } +{ #category : 'tests - nodes' } CTTrieNodeTest >> testIsWord [ | aTrie aWord aLongerWord aNode | aTrie := self newInstance. @@ -145,7 +146,7 @@ CTTrieNodeTest >> testIsWord [ self assert: aLongerWord isWord ] -{ #category : #'tests - nodes' } +{ #category : 'tests - nodes' } CTTrieNodeTest >> testIsWordWikipediaExample [ self assert: (trie find: 'to') isWord. diff --git a/src/Containers-Trie-Tests/CTTrieTest.class.st b/src/Containers-Trie-Tests/CTTrieTest.class.st index e2065d5..83e1364 100644 --- a/src/Containers-Trie-Tests/CTTrieTest.class.st +++ b/src/Containers-Trie-Tests/CTTrieTest.class.st @@ -4,12 +4,13 @@ SUnit tests for class Trie. For licensing, see class method #license " Class { - #name : #CTTrieTest, - #superclass : #AbstractCTTrieTest, - #category : #'Containers-Trie-Tests' + #name : 'CTTrieTest', + #superclass : 'AbstractCTTrieTest', + #category : 'Containers-Trie-Tests', + #package : 'Containers-Trie-Tests' } -{ #category : #license } +{ #category : 'license' } CTTrieTest class >> license [ " Author: Benoit St-Jean @@ -19,7 +20,7 @@ CTTrieTest class >> license [ " ] -{ #category : #'tests - adding' } +{ #category : 'tests - adding' } CTTrieTest >> testAddValueWithBlock [ | aTrie aBlock | @@ -58,7 +59,7 @@ CTTrieTest >> testAddValueWithBlock [ self assert: (aTrie isCompressed) ] -{ #category : #'tests - adding' } +{ #category : 'tests - adding' } CTTrieTest >> testAddWord [ | unTrie | unTrie := self newInstance. @@ -68,7 +69,7 @@ CTTrieTest >> testAddWord [ self assert: (unTrie find: 'bon') nodeValue equals: 3 ] -{ #category : #'tests - querying' } +{ #category : 'tests - querying' } CTTrieTest >> testAllValues [ | aTrie | @@ -80,7 +81,7 @@ CTTrieTest >> testAllValues [ self assertCollection: #(1 2 3) hasSameElements: aTrie allValues ] -{ #category : #'tests - testing' } +{ #category : 'tests - testing' } CTTrieTest >> testAnEmptyTrieDoesNotContainPrefix [ self deny: (self newInstance containsPrefix: 'z'). @@ -88,7 +89,7 @@ CTTrieTest >> testAnEmptyTrieDoesNotContainPrefix [ ] -{ #category : #'tests - adding' } +{ #category : 'tests - adding' } CTTrieTest >> testAt [ | aTrie | @@ -99,7 +100,7 @@ CTTrieTest >> testAt [ self should: [aTrie at: 'two'] raise: KeyNotFound. ] -{ #category : #'tests - adding' } +{ #category : 'tests - adding' } CTTrieTest >> testAtIfAbsent [ | aTrie | @@ -110,7 +111,7 @@ CTTrieTest >> testAtIfAbsent [ self assert: (aTrie at: 'two' ifAbsent: [2]) equals: 2. ] -{ #category : #'tests - adding' } +{ #category : 'tests - adding' } CTTrieTest >> testAtPut [ | aTrie | @@ -134,7 +135,7 @@ CTTrieTest >> testAtPut [ self assert: (aTrie find: 'four') nodeValue equals: 4 ] -{ #category : #'tests - adding' } +{ #category : 'tests - adding' } CTTrieTest >> testAtUpdate [ | aTrie | @@ -150,7 +151,7 @@ CTTrieTest >> testAtUpdate [ self assert: aTrie isCompressed. ] -{ #category : #'tests - adding' } +{ #category : 'tests - adding' } CTTrieTest >> testAtUpdateInitial [ | aTrie | @@ -168,7 +169,7 @@ CTTrieTest >> testAtUpdateInitial [ self assert: aTrie isCompressed. ] -{ #category : #'tests - adding' } +{ #category : 'tests - adding' } CTTrieTest >> testAtUpdateWithNilValueRemovesTheKey [ | aTrie | @@ -182,7 +183,7 @@ CTTrieTest >> testAtUpdateWithNilValueRemovesTheKey [ self assert: aTrie isCompressed. ] -{ #category : #'tests - testing' } +{ #category : 'tests - testing' } CTTrieTest >> testContains [ | aTrie | aTrie := self newInstance. @@ -211,7 +212,7 @@ CTTrieTest >> testContains [ self deny: (trie contains: 'innz') ] -{ #category : #'tests - testing' } +{ #category : 'tests - testing' } CTTrieTest >> testContainsPrefix [ self deny: (trie containsPrefix: 'z'). @@ -236,7 +237,7 @@ CTTrieTest >> testContainsPrefix [ self deny: (trie containsPrefix: 'innz') ] -{ #category : #'tests - nodes' } +{ #category : 'tests - nodes' } CTTrieTest >> testIsCompressed [ | aTrie | @@ -250,7 +251,7 @@ CTTrieTest >> testIsCompressed [ self assert: aTrie isCompressed ] -{ #category : #'tests - removing' } +{ #category : 'tests - removing' } CTTrieTest >> testRemoveKey [ | aTrie | @@ -267,7 +268,7 @@ CTTrieTest >> testRemoveKey [ self assert: aTrie isCompressed. ] -{ #category : #'tests - removing' } +{ #category : 'tests - removing' } CTTrieTest >> testRemoveKeyIfAbsent [ | aTrie | @@ -284,7 +285,7 @@ CTTrieTest >> testRemoveKeyIfAbsent [ self assert: aTrie isCompressed. ] -{ #category : #'tests - removing' } +{ #category : 'tests - removing' } CTTrieTest >> testRemoveKeyOfANodeWithChilds [ | aTrie | @@ -311,7 +312,7 @@ CTTrieTest >> testRemoveKeyOfANodeWithChilds [ ] -{ #category : #'tests - removing' } +{ #category : 'tests - removing' } CTTrieTest >> testRemoveKeyOfANodeWithoutChilds [ | aTrie | @@ -336,7 +337,7 @@ CTTrieTest >> testRemoveKeyOfANodeWithoutChilds [ ] -{ #category : #'tests - nodes' } +{ #category : 'tests - nodes' } CTTrieTest >> testRemovingWikipediaExampleKeepsCompressed [ #('to' 'tea' 'ted' 'ten' 'a' 'inn' 'i' 'in') @@ -345,13 +346,13 @@ CTTrieTest >> testRemovingWikipediaExampleKeepsCompressed [ ] -{ #category : #'tests - nodes' } +{ #category : 'tests - nodes' } CTTrieTest >> testWikipediaExampleIsCompressed [ self assert: trie isCompressed ] -{ #category : #'tests - querying' } +{ #category : 'tests - querying' } CTTrieTest >> testWithAllValuesBeginningWithdo [ | aTrie | @@ -364,7 +365,7 @@ CTTrieTest >> testWithAllValuesBeginningWithdo [ self assertCollection: #(2 3) hasSameElements: (aTrie allValuesBeginningWith: 't') ] -{ #category : #'tests - querying' } +{ #category : 'tests - querying' } CTTrieTest >> testWithAllValuesBeginningWithdoWithEmptyCollection [ | aTrie | @@ -373,7 +374,7 @@ CTTrieTest >> testWithAllValuesBeginningWithdoWithEmptyCollection [ self assertCollection: #() hasSameElements: (aTrie allValuesBeginningWith: 'z') ] -{ #category : #'tests - querying' } +{ #category : 'tests - querying' } CTTrieTest >> testWithAllValuesBeginningWithdoWithEmptyResults [ | aTrie | @@ -386,7 +387,7 @@ CTTrieTest >> testWithAllValuesBeginningWithdoWithEmptyResults [ self assertCollection: #() hasSameElements: (aTrie allValuesBeginningWith: 'z') ] -{ #category : #'tests - querying' } +{ #category : 'tests - querying' } CTTrieTest >> testWithAllValuesDo [ | aTrie elements | @@ -401,7 +402,7 @@ CTTrieTest >> testWithAllValuesDo [ self assertCollection: #(1 2 3) hasSameElements: elements ] -{ #category : #'tests - querying' } +{ #category : 'tests - querying' } CTTrieTest >> testWithAllValuesDoOnEmptyDoesNotCall [ | aTrie | diff --git a/src/Containers-Trie-Tests/package.st b/src/Containers-Trie-Tests/package.st index 4974821..441fa39 100644 --- a/src/Containers-Trie-Tests/package.st +++ b/src/Containers-Trie-Tests/package.st @@ -1 +1 @@ -Package { #name : #'Containers-Trie-Tests' } +Package { #name : 'Containers-Trie-Tests' } diff --git a/src/Containers-Trie/CTAbstractTrieNode.class.st b/src/Containers-Trie/CTAbstractTrieNode.class.st index 7429633..b8afcaa 100644 --- a/src/Containers-Trie/CTAbstractTrieNode.class.st +++ b/src/Containers-Trie/CTAbstractTrieNode.class.st @@ -3,21 +3,22 @@ I am an abstract node used to handle the common behaviour and state of the Trie I have a collection of children and a value. " Class { - #name : #CTAbstractTrieNode, - #superclass : #Object, + #name : 'CTAbstractTrieNode', + #superclass : 'Object', #instVars : [ 'nodeValue', 'children' ], - #category : #'Containers-Trie' + #category : 'Containers-Trie', + #package : 'Containers-Trie' } -{ #category : #accessing } +{ #category : 'accessing' } CTAbstractTrieNode >> children [ ^ children ] -{ #category : #testing } +{ #category : 'testing' } CTAbstractTrieNode >> isCompressed [ ^ self children @@ -25,24 +26,24 @@ CTAbstractTrieNode >> isCompressed [ ifNotEmpty: [ self children allSatisfy: [ :aChild | aChild isCompressed ] ] ] -{ #category : #testing } +{ #category : 'testing' } CTAbstractTrieNode >> isLeaf [ ^self isWord and: [self children isEmpty] ] -{ #category : #testing } +{ #category : 'testing' } CTAbstractTrieNode >> isWord [ ^self nodeValue notNil ] -{ #category : #accessing } +{ #category : 'accessing' } CTAbstractTrieNode >> nodeValue [ ^ nodeValue ] -{ #category : #accessing } +{ #category : 'accessing' } CTAbstractTrieNode >> nodeValue: anObject [ nodeValue := anObject ] diff --git a/src/Containers-Trie/CTOptimizedTrie.class.st b/src/Containers-Trie/CTOptimizedTrie.class.st index dc186cd..5ad9209 100644 --- a/src/Containers-Trie/CTOptimizedTrie.class.st +++ b/src/Containers-Trie/CTOptimizedTrie.class.st @@ -25,12 +25,13 @@ Optimized trie for aWord -> v1 and aTord -> v2 " Class { - #name : #CTOptimizedTrie, - #superclass : #CTTrie, - #category : #'Containers-Trie' + #name : 'CTOptimizedTrie', + #superclass : 'CTTrie', + #category : 'Containers-Trie', + #package : 'Containers-Trie' } -{ #category : #accessing } +{ #category : 'accessing' } CTOptimizedTrie >> at: aString update: updateBlock initial: initBlocktOrValue [ "I am used to update the value at a given key. The updateBlock is passed the existing value, and the result of the block is stored back. @@ -50,20 +51,41 @@ CTOptimizedTrie >> at: aString update: updateBlock initial: initBlocktOrValue [ ] -{ #category : #'private - accessing' } +{ #category : 'removing' } +CTOptimizedTrie >> compressNode: aNode ancestors: aCollection [ + | parent nodeToCheck grandchildKey grandchildNode myChildIndex myKeyIndex currentKey | + super compressNode: aNode ancestors: aCollection. + + aCollection size >= 2 ifTrue: [ + nodeToCheck := aCollection last. + parent := aCollection at: aCollection size - 1. + + (nodeToCheck isWord not and: [ nodeToCheck keys size = 1 ]) ifTrue: [ + grandchildKey := nodeToCheck keys first. + grandchildNode := nodeToCheck children first. + + myChildIndex := (parent instVarNamed: 'children') detectIndex: [ :each | each = nodeToCheck ]. + myKeyIndex := myChildIndex - 1. + currentKey := (parent instVarNamed: 'children') at: myKeyIndex. + + (parent instVarNamed: 'children') at: myKeyIndex put: (currentKey , grandchildKey). + (parent instVarNamed: 'children') at: myChildIndex put: grandchildNode ] ] +] + +{ #category : 'private - accessing' } CTOptimizedTrie >> findPrefix: aString [ ^ root findChildWithString: aString ] -{ #category : #initialization } +{ #category : 'initialization' } CTOptimizedTrie >> nodeSpecies [ ^ CTOptimizedTrieNode ] -{ #category : #removing } +{ #category : 'removing' } CTOptimizedTrie >> removeKey: aString ifAbsent: anAbsentBlock [ | currentNode oldValue ancestors | diff --git a/src/Containers-Trie/CTOptimizedTrieNode.class.st b/src/Containers-Trie/CTOptimizedTrieNode.class.st index 5e80dbf..21e9506 100644 --- a/src/Containers-Trie/CTOptimizedTrieNode.class.st +++ b/src/Containers-Trie/CTOptimizedTrieNode.class.st @@ -18,36 +18,37 @@ Private methods are: " Class { - #name : #CTOptimizedTrieNode, - #superclass : #CTAbstractTrieNode, - #category : #'Containers-Trie' + #name : 'CTOptimizedTrieNode', + #superclass : 'CTAbstractTrieNode', + #category : 'Containers-Trie', + #package : 'Containers-Trie' } -{ #category : #accessing } +{ #category : 'accessing' } CTOptimizedTrieNode >> at: aKey put: aNode [ children := children , { aKey. aNode } ] -{ #category : #accessing } +{ #category : 'accessing' } CTOptimizedTrieNode >> children [ ^ children pairsCollect: [ :aKey :aChild | aChild ] ] -{ #category : #accessing } +{ #category : 'accessing' } CTOptimizedTrieNode >> childrenDictionary [ ^ Dictionary newFromPairs: children ] -{ #category : #'looking-up' } +{ #category : 'looking-up' } CTOptimizedTrieNode >> findChildWithString: aString [ ^ self findChildWithString: aString storingAncestors: OrderedCollection new adding: false. ] -{ #category : #'looking-up' } +{ #category : 'looking-up' } CTOptimizedTrieNode >> findChildWithString: aString storingAncestors: ancestors adding: isAdding [ | newChild keyIndexToSplit splitKey keyToSplit nodeToSplit splittedNode | @@ -89,7 +90,7 @@ CTOptimizedTrieNode >> findChildWithString: aString storingAncestors: ancestors ] -{ #category : #'looking-up' } +{ #category : 'looking-up' } CTOptimizedTrieNode >> findKeyIndexToSplit: aString [ | indexToSplit lengthOfPrefix index | @@ -109,20 +110,20 @@ CTOptimizedTrieNode >> findKeyIndexToSplit: aString [ ^ indexToSplit. ] -{ #category : #initialization } +{ #category : 'initialization' } CTOptimizedTrieNode >> initialize [ super initialize. children := #(). ] -{ #category : #accessing } +{ #category : 'accessing' } CTOptimizedTrieNode >> keys [ ^ children pairsCollect: [ :aKey :aChild | aKey ] ] -{ #category : #printing } +{ #category : 'printing' } CTOptimizedTrieNode >> printOn: aStream [ super printOn: aStream. @@ -134,7 +135,7 @@ CTOptimizedTrieNode >> printOn: aStream [ print: self nodeValue. ] -{ #category : #'adding - removing' } +{ #category : 'adding - removing' } CTOptimizedTrieNode >> removeNode: aNode [ | nodeIndex keyIndex | @@ -147,7 +148,7 @@ CTOptimizedTrieNode >> removeNode: aNode [ children := children reject: [ :e | e isNil ]. ] -{ #category : #'adding - removing' } +{ #category : 'adding - removing' } CTOptimizedTrieNode >> splitOn: newKey oldKey: oldKey [ | index newParent | @@ -160,7 +161,7 @@ CTOptimizedTrieNode >> splitOn: newKey oldKey: oldKey [ ^ newParent ] -{ #category : #iterating } +{ #category : 'iterating' } CTOptimizedTrieNode >> withAllChildrenDo: aBlock [ aBlock cull: self. diff --git a/src/Containers-Trie/CTSuffixTree.class.st b/src/Containers-Trie/CTSuffixTree.class.st index 834d3ad..494240c 100644 --- a/src/Containers-Trie/CTSuffixTree.class.st +++ b/src/Containers-Trie/CTSuffixTree.class.st @@ -13,23 +13,24 @@ And also: - removeSuffixesOf: to remove all the suffixes of a given word " Class { - #name : #CTSuffixTree, - #superclass : #Object, + #name : 'CTSuffixTree', + #superclass : 'Object', #traits : 'CTTrieOperations', #classTraits : 'CTTrieOperations classTrait', #instVars : [ 'trie' ], - #category : #'Containers-Trie' + #category : 'Containers-Trie', + #package : 'Containers-Trie' } -{ #category : #accessing } +{ #category : 'accessing' } CTSuffixTree >> at: aString update: updateBlock initial: initBlocktOrValue [ ^ trie at: aString update: updateBlock initial: initBlocktOrValue ] -{ #category : #accessing } +{ #category : 'accessing' } CTSuffixTree >> atSuffixesOf: aString put: aValue [. aString suffixes do: [:aSuffix | self at: aSuffix put: aValue ]. @@ -37,7 +38,7 @@ CTSuffixTree >> atSuffixesOf: aString put: aValue [. ^ aValue ] -{ #category : #accessing } +{ #category : 'accessing' } CTSuffixTree >> atSuffixesOf: aString update: updateBlock initial: initBlocktOrValue [ aString suffixes do: [:aSuffix | @@ -45,26 +46,26 @@ CTSuffixTree >> atSuffixesOf: aString update: updateBlock initial: initBlocktOrV ] -{ #category : #'private - accessing' } +{ #category : 'private - accessing' } CTSuffixTree >> findPrefix: aString [ ^ trie findPrefix: aString ] -{ #category : #initialization } +{ #category : 'initialization' } CTSuffixTree >> initialize [ super initialize. trie := CTOptimizedTrie new ] -{ #category : #removing } +{ #category : 'removing' } CTSuffixTree >> removeKey: aString ifAbsent: anAbsentBlock [ ^ trie removeKey: aString ifAbsent: anAbsentBlock ] -{ #category : #accessing } +{ #category : 'accessing' } CTSuffixTree >> removeSuffixesOf: aString [ aString suffixes do: [:aSuffix | self removeKey: aSuffix ]. diff --git a/src/Containers-Trie/CTTrie.class.st b/src/Containers-Trie/CTTrie.class.st index 7959f17..fd242c1 100644 --- a/src/Containers-Trie/CTTrie.class.st +++ b/src/Containers-Trie/CTTrie.class.st @@ -12,22 +12,23 @@ MIT license " Class { - #name : #CTTrie, - #superclass : #Object, + #name : 'CTTrie', + #superclass : 'Object', #traits : 'CTTrieOperations', #classTraits : 'CTTrieOperations classTrait', #instVars : [ 'root' ], - #category : #'Containers-Trie' + #category : 'Containers-Trie', + #package : 'Containers-Trie' } -{ #category : #'instance creation - bulk' } +{ #category : 'instance creation - bulk' } CTTrie class >> readFromFile: anObject [ ^ self readFromFile: anObject withLineTransformer: [ :str | str ] ] -{ #category : #'instance creation - bulk' } +{ #category : 'instance creation - bulk' } CTTrie class >> readFromFile: anObject withLineTransformer: aBlock [ "A utility method that allows to bulk load a file containing one word per line and add them all to a trie structure. aBlock allows one to convert the line before inserting it in the trie (for example one can convert it to lowercase)." @@ -50,7 +51,7 @@ CTTrie class >> readFromFile: anObject withLineTransformer: aBlock [ ] -{ #category : #'example instances' } +{ #category : 'example instances' } CTTrie class >> wikipediaSampleInstance [ "example taken from " @@ -73,7 +74,7 @@ CTTrie class >> wikipediaSampleInstance [ ^ trie ] -{ #category : #adding } +{ #category : 'adding' } CTTrie >> add: aString value: anObject [ self @@ -84,7 +85,7 @@ CTTrie >> add: aString value: anObject [ ^ self at: aString put: anObject ] -{ #category : #accessing } +{ #category : 'accessing' } CTTrie >> at: aString update: updateBlock initial: initBlockOrValue [ "I am used to update the value at a given key. The updateBlock is passed the existing value, and the result of the block is stored back. @@ -111,7 +112,7 @@ CTTrie >> at: aString update: updateBlock initial: initBlockOrValue [ ] -{ #category : #private } +{ #category : 'private' } CTTrie >> compressNode: currentNode ancestors: ancestors [ | aNode | @@ -128,7 +129,7 @@ CTTrie >> compressNode: currentNode ancestors: ancestors [ aNode := aParent ] ] -{ #category : #'private - accessing' } +{ #category : 'private - accessing' } CTTrie >> findPrefix: aString [ "Answers a if the prefix is found in the receiver, otherwise" @@ -145,7 +146,7 @@ CTTrie >> findPrefix: aString [ ^ currentNode ] -{ #category : #inspecting } +{ #category : 'inspecting' } CTTrie >> gtInspectorItemsIn: composite [ @@ -157,27 +158,27 @@ CTTrie >> gtInspectorItemsIn: composite [ format: [ :each | 'Character: ' , each key printString , ' Node: ', each value printString ] ] -{ #category : #initialization } +{ #category : 'initialization' } CTTrie >> initialize [ super initialize. root := self nodeSpecies new ] -{ #category : #'private - testing' } +{ #category : 'private - testing' } CTTrie >> isCompressed [ ^ root children allSatisfy: [ :aChild | aChild isCompressed ] ] -{ #category : #initialization } +{ #category : 'initialization' } CTTrie >> nodeSpecies [ ^ CTTrieNode ] -{ #category : #printing } +{ #category : 'printing' } CTTrie >> printOn: aStream [ super printOn: aStream. @@ -185,7 +186,7 @@ CTTrie >> printOn: aStream [ ] -{ #category : #removing } +{ #category : 'removing' } CTTrie >> removeKey: aString ifAbsent: anAbsentBlock [ | currentNode oldValue ancestors | @@ -210,7 +211,7 @@ CTTrie >> removeKey: aString ifAbsent: anAbsentBlock [ ^ oldValue] ensure: [ self compressNode: currentNode ancestors: ancestors ] ] -{ #category : #'private - accessing' } +{ #category : 'private - accessing' } CTTrie >> rootNode [ ^ root diff --git a/src/Containers-Trie/CTTrieNode.class.st b/src/Containers-Trie/CTTrieNode.class.st index de617a3..97dbbb8 100644 --- a/src/Containers-Trie/CTTrieNode.class.st +++ b/src/Containers-Trie/CTTrieNode.class.st @@ -7,22 +7,23 @@ Use ifLeaf, isNode, isRoot, and isWord to test my condition. Users should not use objects of this class directly, they should use CTTrie. " Class { - #name : #CTTrieNode, - #superclass : #CTAbstractTrieNode, + #name : 'CTTrieNode', + #superclass : 'CTAbstractTrieNode', #instVars : [ 'character' ], - #category : #'Containers-Trie' + #category : 'Containers-Trie', + #package : 'Containers-Trie' } -{ #category : #private } +{ #category : 'private' } CTTrieNode class >> newWithCharacter: aCharacter [ ^ self basicNew initialize; character: aCharacter ] -{ #category : #private } +{ #category : 'private' } CTTrieNode >> addChildWithLetter: aCharacter [ | newTrie | @@ -33,47 +34,47 @@ CTTrieNode >> addChildWithLetter: aCharacter [ ^newTrie ] -{ #category : #accessing } +{ #category : 'accessing' } CTTrieNode >> character [ ^ character ] -{ #category : #accessing } +{ #category : 'accessing' } CTTrieNode >> character: aCharacter [ character := aCharacter ] -{ #category : #accessing } +{ #category : 'accessing' } CTTrieNode >> childrenDictionary [ ^ children ] -{ #category : #private } +{ #category : 'private' } CTTrieNode >> findChildWithString: aString [ ^self children at: aString ifAbsent: [nil] ] -{ #category : #initialization } +{ #category : 'initialization' } CTTrieNode >> initialize [ super initialize. children := IdentityDictionary new. ] -{ #category : #testing } +{ #category : 'testing' } CTTrieNode >> isNode [ ^self isRoot not ] -{ #category : #testing } +{ #category : 'testing' } CTTrieNode >> isRoot [ ^self character isNil ] -{ #category : #printing } +{ #category : 'printing' } CTTrieNode >> printOn: aStream [ super printOn: aStream. @@ -92,13 +93,13 @@ CTTrieNode >> printOn: aStream [ print: self isLeaf. ] -{ #category : #private } +{ #category : 'private' } CTTrieNode >> removeNode: aNode [ children removeKey: (children keyAtValue: aNode) ] -{ #category : #private } +{ #category : 'private' } CTTrieNode >> withAllChildrenDo: aBlock [ aBlock cull: self. diff --git a/src/Containers-Trie/CTTrieOperations.trait.st b/src/Containers-Trie/CTTrieOperations.trait.st index c8bec59..09cb39d 100644 --- a/src/Containers-Trie/CTTrieOperations.trait.st +++ b/src/Containers-Trie/CTTrieOperations.trait.st @@ -4,11 +4,12 @@ It requires the user to implement #at:update:initial to add/update elements, #fi All the other operations are performed using them. " Trait { - #name : #CTTrieOperations, - #category : #'Containers-Trie' + #name : 'CTTrieOperations', + #category : 'Containers-Trie', + #package : 'Containers-Trie' } -{ #category : #adding } +{ #category : 'adding' } CTTrieOperations >> add: aString valueWithBlock: aValueBlock [ "Add the word into the Trie structure and set its value by evaluating with one parameter. @@ -19,26 +20,26 @@ CTTrieOperations >> add: aString valueWithBlock: aValueBlock [ self at: aString put: (aValueBlock value: aString) ] -{ #category : #querying } +{ #category : 'querying' } CTTrieOperations >> allValues [ ^ OrderedCollection streamContents: [ :s | self withAllValuesDo: [ :aValue | s nextPut: aValue ] ] ] -{ #category : #querying } +{ #category : 'querying' } CTTrieOperations >> allValuesBeginningWith: aPrefix [ ^ OrderedCollection streamContents: [ :s | self withAllValuesBeginningWith: aPrefix do: [ :aValue | s nextPut: aValue ] ] ] -{ #category : #accessing } +{ #category : 'accessing' } CTTrieOperations >> at: key [ "Answer the value associated with the key." ^ self at: key ifAbsent: [self errorKeyNotFound: key] ] -{ #category : #accessing } +{ #category : 'accessing' } CTTrieOperations >> at: aString ifAbsent: aBlock [ "Answer the value associated with the aString or, if aString isn't found, answer the result of evaluating aBlock." @@ -46,7 +47,7 @@ CTTrieOperations >> at: aString ifAbsent: aBlock [ ^ (self find: aString) ifNil: aBlock ifNotNil: [ :aNode | aNode nodeValue ] ] -{ #category : #accessing } +{ #category : 'accessing' } CTTrieOperations >> at: aString put: anObject [ "Add the word aString into the Trie structure and set its value to anObject. If the key exists it replaces the value. @@ -57,14 +58,14 @@ CTTrieOperations >> at: aString put: anObject [ ] -{ #category : #accessing } +{ #category : 'accessing' } CTTrieOperations >> at: key update: updateBlock [ "I am used to update the value at a given key, or if the key does not exist, to throw an error" self at: key update: updateBlock initial: [ self errorKeyNotFound: key ]. ] -{ #category : #accessing } +{ #category : 'accessing' } CTTrieOperations >> at: aString update: updateBlock initial: initBlocktOrValue [ "I am used to update the value at a given key. The updateBlock is passed the existing value, and the result of the block is stored back. @@ -74,7 +75,7 @@ CTTrieOperations >> at: aString update: updateBlock initial: initBlocktOrValue [ self explicitRequirement ] -{ #category : #testing } +{ #category : 'testing' } CTTrieOperations >> contains: aString [ "Answer a telling if the receiver contains the *word* " @@ -86,20 +87,20 @@ CTTrieOperations >> contains: aString [ ] -{ #category : #testing } +{ #category : 'testing' } CTTrieOperations >> containsPrefix: aString [ "Answer a telling if the receiver contains (either as a prefix or complete word)" ^ (self findPrefix: aString) isNotNil ] -{ #category : #private } +{ #category : 'private' } CTTrieOperations >> errorKeyNotFound: key [ ^ KeyNotFound signalFor: key ] -{ #category : #'private - accessing' } +{ #category : 'private - accessing' } CTTrieOperations >> find: aString [ "Answers a if the word is found in the receiver, otherwise" @@ -117,13 +118,13 @@ CTTrieOperations >> find: aString [ ] -{ #category : #'private - accessing' } +{ #category : 'private - accessing' } CTTrieOperations >> findPrefix: aString [ self explicitRequirement ] -{ #category : #initialization } +{ #category : 'initialization' } CTTrieOperations >> occupationRate [ | intermediateNodes nodes | @@ -139,19 +140,19 @@ CTTrieOperations >> occupationRate [ ^ ((nodes - intermediateNodes) / nodes) asFloat ] -{ #category : #removing } +{ #category : 'removing' } CTTrieOperations >> removeKey: aString [ ^ self removeKey: aString ifAbsent: [ self errorKeyNotFound: aString ] ] -{ #category : #removing } +{ #category : 'removing' } CTTrieOperations >> removeKey: aString ifAbsent: anAbsentBlock [ self explicitRequirement ] -{ #category : #accessing } +{ #category : 'accessing' } CTTrieOperations >> size [ "I return the number of entries that I have" @@ -161,7 +162,7 @@ CTTrieOperations >> size [ ^ count ] -{ #category : #querying } +{ #category : 'querying' } CTTrieOperations >> withAllValuesBeginningWith: aPrefix do: aBlock [ | node | @@ -171,7 +172,7 @@ CTTrieOperations >> withAllValuesBeginningWith: aPrefix do: aBlock [ node withAllChildrenDo: [ :aNode | aNode isWord ifTrue: [ aBlock cull: aNode nodeValue ] ] ] -{ #category : #querying } +{ #category : 'querying' } CTTrieOperations >> withAllValuesDo: aBlock [ "It iterates all the values and evaluates aBlock on each of the values" diff --git a/src/Containers-Trie/String.extension.st b/src/Containers-Trie/String.extension.st index 3b146a0..a18664b 100644 --- a/src/Containers-Trie/String.extension.st +++ b/src/Containers-Trie/String.extension.st @@ -1,13 +1,13 @@ -Extension { #name : #String } +Extension { #name : 'String' } -{ #category : #'*Containers-Trie' } +{ #category : '*Containers-Trie' } String >> suffixes [ self ifEmpty: [ ^ #() ]. ^ { self } , self allButFirst suffixes ] -{ #category : #'*Containers-Trie' } +{ #category : '*Containers-Trie' } String >> suffixesDo: aBlock minLength: minLength [ 2 to: self size - minLength do: [ :initial | diff --git a/src/Containers-Trie/package.st b/src/Containers-Trie/package.st index 332908b..4fdee93 100644 --- a/src/Containers-Trie/package.st +++ b/src/Containers-Trie/package.st @@ -1 +1 @@ -Package { #name : #'Containers-Trie' } +Package { #name : 'Containers-Trie' }