From d7296155211025b48c985464d39bf402a460564f Mon Sep 17 00:00:00 2001 From: HossamSaberr Date: Tue, 24 Feb 2026 19:59:53 +0200 Subject: [PATCH 1/4] Test: Add failing test for issue (#11) --- .../AbstractCTTrieTest.class.st | 13 +++-- .../CTOptimizedTrieTest.class.st | 26 +++++++-- .../CTSuffixTreeTest.class.st | 13 +++-- .../CTTrieNodeTest.class.st | 21 +++---- src/Containers-Trie-Tests/CTTrieTest.class.st | 57 ++++++++++--------- src/Containers-Trie-Tests/package.st | 2 +- 6 files changed, 77 insertions(+), 55 deletions(-) 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..390683e 100644 --- a/src/Containers-Trie-Tests/CTOptimizedTrieTest.class.st +++ b/src/Containers-Trie-Tests/CTOptimizedTrieTest.class.st @@ -1,11 +1,29 @@ 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 >> testOptimizedTrieCompressesOnDeletion [ + | trie rootNode | + trie := CTOptimizedTrie new. + trie at: 'car' put: 1. + trie at: 'cat' put: 2. + + trie removeKey: 'cat'. + + self assert: (trie at: 'car' ifAbsent: [ nil ]) equals: 1. + self assert: (trie at: 'cat' ifAbsent: [ nil ]) isNil. + + rootNode := trie instVarNamed: 'root'. + + self assert: rootNode children first children isEmpty. +] 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' } From e707774927151a97133e47a8b8649a63a6df3f23 Mon Sep 17 00:00:00 2001 From: HossamSaberr Date: Tue, 24 Feb 2026 20:49:58 +0200 Subject: [PATCH 2/4] Fix: Implement Radix merge logic on deletion (#11) --- src/Containers-Trie/CTOptimizedTrie.class.st | 21 ++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/Containers-Trie/CTOptimizedTrie.class.st b/src/Containers-Trie/CTOptimizedTrie.class.st index 0bdb427..5ad9209 100644 --- a/src/Containers-Trie/CTOptimizedTrie.class.st +++ b/src/Containers-Trie/CTOptimizedTrie.class.st @@ -51,6 +51,27 @@ CTOptimizedTrie >> at: aString update: updateBlock initial: initBlocktOrValue [ ] +{ #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 [ From 05bdf26678c77caae31dde9494dd645cc91c3b07 Mon Sep 17 00:00:00 2001 From: HossamSaberr Date: Sun, 1 Mar 2026 15:53:05 +0200 Subject: [PATCH 3/4] Add tests for CTOptimizedTrie compression --- .../CTOptimizedTrieTest.class.st | 46 ++++++++++++++++++- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/src/Containers-Trie-Tests/CTOptimizedTrieTest.class.st b/src/Containers-Trie-Tests/CTOptimizedTrieTest.class.st index 390683e..8edce92 100644 --- a/src/Containers-Trie-Tests/CTOptimizedTrieTest.class.st +++ b/src/Containers-Trie-Tests/CTOptimizedTrieTest.class.st @@ -20,10 +20,52 @@ CTOptimizedTrieTest >> testOptimizedTrieCompressesOnDeletion [ trie removeKey: 'cat'. - self assert: (trie at: 'car' ifAbsent: [ nil ]) equals: 1. - self assert: (trie at: 'cat' ifAbsent: [ nil ]) isNil. + 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. +] From 58b08c6de10677d762743f3757e4d8d97655151b Mon Sep 17 00:00:00 2001 From: HossamSaberr Date: Tue, 3 Mar 2026 21:41:38 +0200 Subject: [PATCH 4/4] Add direct tests for compressNode:ancestors: --- .../CTOptimizedTrieTest.class.st | 122 ++++++++++++++++++ 1 file changed, 122 insertions(+) diff --git a/src/Containers-Trie-Tests/CTOptimizedTrieTest.class.st b/src/Containers-Trie-Tests/CTOptimizedTrieTest.class.st index 8edce92..99740a9 100644 --- a/src/Containers-Trie-Tests/CTOptimizedTrieTest.class.st +++ b/src/Containers-Trie-Tests/CTOptimizedTrieTest.class.st @@ -11,6 +11,128 @@ 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 |