collection_utils 1.0.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.coveralls.yml +1 -0
- data/Gemfile +3 -0
- data/README.md +1 -1
- data/lib/collection_utils.rb +1 -1
- data/lib/collection_utils/hash_deserialized_object.rb +114 -0
- data/lib/collection_utils/heap.rb +190 -109
- data/lib/collection_utils/heap_utils/max_heap.rb +165 -33
- data/lib/collection_utils/heap_utils/min_heap.rb +165 -35
- data/lib/collection_utils/queue.rb +23 -3
- data/lib/collection_utils/set.rb +144 -0
- data/lib/collection_utils/stack.rb +8 -2
- data/lib/collection_utils/tree_utils/bst.rb +194 -38
- data/lib/collection_utils/version.rb +1 -1
- metadata +5 -2
|
@@ -22,7 +22,7 @@ module CollectionUtils
|
|
|
22
22
|
# @example Add element in queue
|
|
23
23
|
# queue = CollectionUtils::Queue.new()
|
|
24
24
|
# queue.enqueue(1)
|
|
25
|
-
# queue.enqueue([1,3,4
|
|
25
|
+
# queue.enqueue([1,3,4])
|
|
26
26
|
def enqueue(element)
|
|
27
27
|
@queue << element
|
|
28
28
|
end
|
|
@@ -33,8 +33,8 @@ module CollectionUtils
|
|
|
33
33
|
# @example delete element from queue
|
|
34
34
|
# queue = CollectionUtils::Queue.new()
|
|
35
35
|
# queue.enqueue(1)
|
|
36
|
-
# queue.enqueue([1,3,4
|
|
37
|
-
# element = queue.dequeue #element = 1
|
|
36
|
+
# queue.enqueue([1,3,4])
|
|
37
|
+
# element = queue.dequeue #element = 1, queue = [[1,3,4]]
|
|
38
38
|
def dequeue
|
|
39
39
|
element = @queue.first
|
|
40
40
|
@queue = @queue.slice(1..-1)
|
|
@@ -43,21 +43,41 @@ module CollectionUtils
|
|
|
43
43
|
|
|
44
44
|
|
|
45
45
|
# @return element that has been recently added
|
|
46
|
+
# @example return front element from queue
|
|
47
|
+
# queue = CollectionUtils::Queue.new()
|
|
48
|
+
# queue.enqueue(1)
|
|
49
|
+
# queue.enqueue([1,3,4])
|
|
50
|
+
# element = queue.front #element = 1, queue = [1,[1,3,4]]
|
|
46
51
|
def front
|
|
47
52
|
return @queue.first
|
|
48
53
|
end
|
|
49
54
|
|
|
50
55
|
# @return element that will be dequeued next
|
|
56
|
+
# @example return rear element from queue
|
|
57
|
+
# queue = CollectionUtils::Queue.new()
|
|
58
|
+
# queue.enqueue(1)
|
|
59
|
+
# queue.enqueue([1,3,4])
|
|
60
|
+
# element = queue.rear #element = [1,3,4], queue = [1,[1,3,4]]
|
|
51
61
|
def rear
|
|
52
62
|
return @queue.last
|
|
53
63
|
end
|
|
54
64
|
|
|
55
65
|
# @return [Boolean] which tells queue's emptiness
|
|
66
|
+
# @example return emptiness of queue
|
|
67
|
+
# queue = CollectionUtils::Queue.new()
|
|
68
|
+
# queue.enqueue(1)
|
|
69
|
+
# queue.enqueue([1,3,4])
|
|
70
|
+
# queue.is_empty? #false
|
|
56
71
|
def is_empty?
|
|
57
72
|
return @queue.size == 0
|
|
58
73
|
end
|
|
59
74
|
|
|
60
75
|
# @return [Integer] size of queue
|
|
76
|
+
# @example return size of queue
|
|
77
|
+
# queue = CollectionUtils::Queue.new()
|
|
78
|
+
# queue.enqueue(1)
|
|
79
|
+
# queue.enqueue([1,3,4])
|
|
80
|
+
# size = queue.size #size = 2
|
|
61
81
|
def size
|
|
62
82
|
return @queue.size
|
|
63
83
|
end
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
module CollectionUtils
|
|
2
|
+
class Set
|
|
3
|
+
private
|
|
4
|
+
attr_accessor :set
|
|
5
|
+
|
|
6
|
+
def get_key_value(val)
|
|
7
|
+
instance_variables = val.instance_variables
|
|
8
|
+
if instance_variables.empty?
|
|
9
|
+
return val.hash, val
|
|
10
|
+
end
|
|
11
|
+
variables = instance_variables.inject(Hash.new(0)) {|h,e|
|
|
12
|
+
h[e] = val.instance_variable_get(e);h
|
|
13
|
+
}
|
|
14
|
+
return variables.hash, val
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
public
|
|
18
|
+
def initialize(values = [])
|
|
19
|
+
@set = {}
|
|
20
|
+
values.each do |value|
|
|
21
|
+
insert(value)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Insert an element into set. This is done in O(1) operations
|
|
26
|
+
#
|
|
27
|
+
# @param val element that needs to be added
|
|
28
|
+
# @return [Boolean] true if element is added, false if element is already present
|
|
29
|
+
# @example Add element into set
|
|
30
|
+
# => set = CollectionUtils::Set.new([1,2,3,4])
|
|
31
|
+
# => set.size #4
|
|
32
|
+
# => set.insert(5)
|
|
33
|
+
# => set.size #5
|
|
34
|
+
# => set.insert(4)
|
|
35
|
+
# => set.size #5
|
|
36
|
+
def insert(val)
|
|
37
|
+
key, value = get_key_value(val)
|
|
38
|
+
@set[key] = value; return true if @set[key].nil?
|
|
39
|
+
return false
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# checks whether an element is present or not.
|
|
43
|
+
# This is done in O(1) operations
|
|
44
|
+
# @param val which has to be checked whether in set or not
|
|
45
|
+
# @return [Boolean] true if element is present, false if element is already present
|
|
46
|
+
# @example check an element in set
|
|
47
|
+
# => set = CollectionUtils::Set.new([1,2,3,4])
|
|
48
|
+
# => set.check(4) #true
|
|
49
|
+
# => set.check(5) #false
|
|
50
|
+
def check(val)
|
|
51
|
+
key, value = get_key_value(val)
|
|
52
|
+
return !@set[key].nil?
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# deletes an element from the set.
|
|
56
|
+
# This is done in O(1) operations
|
|
57
|
+
# @param val which has to be deleted
|
|
58
|
+
# @example check an element in set
|
|
59
|
+
# => set = CollectionUtils::Set.new([1,2,3,4])
|
|
60
|
+
# => set.delete(4) #set = [1,2,3]
|
|
61
|
+
# => set.delete(5) #set = [1,2,3]
|
|
62
|
+
def delete(val)
|
|
63
|
+
key, value = get_key_value(val)
|
|
64
|
+
@set.delete(key)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
# Get a random element from the set.
|
|
69
|
+
#
|
|
70
|
+
# @return a random element from set.
|
|
71
|
+
# @example get an element from set
|
|
72
|
+
# => set = CollectionUtils::Set.new([1,2,3,4])
|
|
73
|
+
# => set.get # 3
|
|
74
|
+
# => set.get # 4
|
|
75
|
+
# => set.get # 3
|
|
76
|
+
def get
|
|
77
|
+
@set.values.sample
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Get all the values from a set.
|
|
81
|
+
#
|
|
82
|
+
# @return all the elements from the set.
|
|
83
|
+
# @example get all element from set
|
|
84
|
+
# => set = CollectionUtils::Set.new([1,2,3,4])
|
|
85
|
+
# => set.get_values #[1,2,3,4]
|
|
86
|
+
def get_values
|
|
87
|
+
@set.values
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# @return [Integer] size of the
|
|
91
|
+
# @example return size of set
|
|
92
|
+
# => set = CollectionUtils::Set.new([1,2,3,4])
|
|
93
|
+
# => set.size #4
|
|
94
|
+
def size
|
|
95
|
+
@set.keys.size
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Returns whether the set is empty. This is just a syntax_sugar
|
|
99
|
+
# for size == 0
|
|
100
|
+
# @return [Boolean] set's emptiness
|
|
101
|
+
def is_empty?
|
|
102
|
+
size == 0
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Intersection of two sets and return the intersected set
|
|
106
|
+
#
|
|
107
|
+
# @param [Set] set with which Intersection of the current set needs to be taken
|
|
108
|
+
# @return [Set] intersected set
|
|
109
|
+
# @example Intesect two sets and return it.
|
|
110
|
+
# => set1 = CollectionUtils::Set.new([1,2,3,4])
|
|
111
|
+
# => set2 = CollectionUtils::Set.new([2,3,4,5])
|
|
112
|
+
# => set3 = set1 & set2 #[2,3,4]
|
|
113
|
+
def &(set)
|
|
114
|
+
new_set = Set.new(get_values & set.get_values)
|
|
115
|
+
return new_set
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Union of two sets and return the united set
|
|
119
|
+
#
|
|
120
|
+
# @param [Set] set with which Union of the current set needs to be taken
|
|
121
|
+
# @return [Set] united set
|
|
122
|
+
# @example Union two sets and return it.
|
|
123
|
+
# => set1 = CollectionUtils::Set.new([1,2,3,4])
|
|
124
|
+
# => set2 = CollectionUtils::Set.new([2,3,4,5])
|
|
125
|
+
# => set3 = set1 + set2 #[1,2,3,4,5]
|
|
126
|
+
def +(set)
|
|
127
|
+
new_set = Set.new(get_values + set.get_values)
|
|
128
|
+
return new_set
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Difference of two sets and return the subtracted set
|
|
132
|
+
#
|
|
133
|
+
# @param [Set] set with which Difference of the current set needs to be taken
|
|
134
|
+
# @return [Set] subtracted set
|
|
135
|
+
# @example subtracted two sets and return it.
|
|
136
|
+
# => set1 = CollectionUtils::Set.new([1,2,3,4])
|
|
137
|
+
# => set2 = CollectionUtils::Set.new([2,3,4,5])
|
|
138
|
+
# => set3 = set1 - set2 #[1]
|
|
139
|
+
def -(set)
|
|
140
|
+
new_set = Set.new(get_values - set.get_values)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
end
|
|
144
|
+
end
|
|
@@ -18,7 +18,7 @@ module CollectionUtils
|
|
|
18
18
|
#
|
|
19
19
|
# @example Create a stack [1,2,3,4,5] and remove 5 using pop
|
|
20
20
|
# stack = CollectionUtils::Stack.new([1,2,3,4,5])
|
|
21
|
-
# top_element = stack.pop()
|
|
21
|
+
# top_element = stack.pop() # top_element = 5, stack = [1,2,3,4]
|
|
22
22
|
def pop
|
|
23
23
|
return @stack.pop
|
|
24
24
|
end
|
|
@@ -36,6 +36,9 @@ module CollectionUtils
|
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
# @return [Boolean] stack's emptiness
|
|
39
|
+
# @example Create a stack [1,2,3,4,5] and add check emptiness.
|
|
40
|
+
# stack = CollectionUtils::Stack.new([1,2,3,4,5])
|
|
41
|
+
# stack.is_empty? # false
|
|
39
42
|
def is_empty?
|
|
40
43
|
return @stack.size == 0
|
|
41
44
|
end
|
|
@@ -46,12 +49,15 @@ module CollectionUtils
|
|
|
46
49
|
#
|
|
47
50
|
# @example Create a stack [1,2,3,4,5] and get 5 using peek
|
|
48
51
|
# stack = CollectionUtils::Stack.new([1,2,3,4,5])
|
|
49
|
-
# top_element = stack.peek # top_element = 5
|
|
52
|
+
# top_element = stack.peek # top_element = 5, stack = [1,2,3,4,5]
|
|
50
53
|
def peek
|
|
51
54
|
return @stack.last
|
|
52
55
|
end
|
|
53
56
|
|
|
54
57
|
# @return [Integer] size of stack
|
|
58
|
+
# @example Create a stack [1,2,3,4,5] and check size.
|
|
59
|
+
# stack = CollectionUtils::Stack.new([1,2,3,4,5])
|
|
60
|
+
# stack.size # 5
|
|
55
61
|
def size
|
|
56
62
|
return @stack.size
|
|
57
63
|
end
|
|
@@ -3,65 +3,221 @@ module CollectionUtils
|
|
|
3
3
|
module TreeUtils
|
|
4
4
|
class BST < CollectionUtils::Tree
|
|
5
5
|
private
|
|
6
|
-
attr_accessor :bst, :height
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
7
|
+
def delete_node(element, node)
|
|
8
|
+
if element == node.val
|
|
9
|
+
@size -= 1
|
|
10
|
+
@leaf_set.delete(node)
|
|
11
|
+
# if the node is leaf then add the parent to leaf set if
|
|
12
|
+
# parent.is_leaf? and assign the left or right to nil based on
|
|
13
|
+
# where the node is attached to the parent.
|
|
14
|
+
if node.is_leaf?
|
|
15
|
+
parent = node.parent
|
|
16
|
+
@leaf_set.delete(parent)
|
|
17
|
+
parent.left == node ? parent.left = nil : parent.right = nil
|
|
18
|
+
@leaf_set.insert(parent) if parent.is_leaf?
|
|
19
|
+
return
|
|
20
|
+
# if the right child is leaf for the node then exchange the value of
|
|
21
|
+
# right child and node and delete the right child.
|
|
22
|
+
elsif node.right.is_leaf?
|
|
23
|
+
node.val = node.right.val
|
|
24
|
+
node.right = nil
|
|
25
|
+
@leaf_set.insert(node) if node.is_leaf?
|
|
26
|
+
return
|
|
27
|
+
# if the left child is leaf for the node then exchange the value of
|
|
28
|
+
# left child and node and delete the left child.
|
|
29
|
+
elsif node.left.is_leaf?
|
|
30
|
+
node.val = node.left.val
|
|
31
|
+
node.left = nil
|
|
32
|
+
@leaf_set.insert(node) if node.is_leaf?
|
|
33
|
+
return
|
|
34
|
+
else
|
|
35
|
+
# find the smallest child on the right side and exchange the
|
|
36
|
+
# value with the node. Balance the tree accordingly.
|
|
37
|
+
smallest = find_smallest(node.right)
|
|
38
|
+
if smallest.is_leaf?
|
|
39
|
+
@leaf_set.delete(smallest)
|
|
40
|
+
node.val = smallest.val
|
|
41
|
+
@leaf_set.insert(node) if node.is_leaf?
|
|
42
|
+
else
|
|
43
|
+
if smallest == smallest.parent.left
|
|
44
|
+
smallest.parent.left = smallest.right
|
|
45
|
+
node.val = smallest.val
|
|
46
|
+
smallest = nil
|
|
47
|
+
else
|
|
48
|
+
smallest.parent.right = smallest.right
|
|
49
|
+
node.val = smallest.val
|
|
50
|
+
smallest = nil
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
end
|
|
55
|
+
elsif element > node.val
|
|
56
|
+
delete_node(element, node.right)
|
|
57
|
+
else
|
|
58
|
+
delete_node(element, node.left)
|
|
14
59
|
end
|
|
15
60
|
end
|
|
16
61
|
|
|
17
|
-
def
|
|
18
|
-
|
|
19
|
-
|
|
62
|
+
def find_smallest(node)
|
|
63
|
+
smallest = node.val
|
|
64
|
+
left = node.left
|
|
65
|
+
while left != nil do
|
|
66
|
+
smallest = left.val
|
|
67
|
+
left = left.left
|
|
20
68
|
end
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
69
|
+
return smallest
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def find_largest(node)
|
|
73
|
+
largest = node.val
|
|
74
|
+
right = node.right
|
|
75
|
+
while right != nil do
|
|
76
|
+
largest = right.val
|
|
77
|
+
right = right.right
|
|
25
78
|
end
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
79
|
+
return largest
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def insert_node(node, parent_node)
|
|
83
|
+
if node.val >= parent_node.val
|
|
84
|
+
if parent_node.right.nil?
|
|
85
|
+
@leaf_set.delete(parent_node)
|
|
86
|
+
parent_node.right = node
|
|
87
|
+
node.level = parent_node.level + 1
|
|
88
|
+
@level = node.level if node.level > @level
|
|
89
|
+
@leaf_set.insert(node) if node.is_leaf?
|
|
34
90
|
return
|
|
35
91
|
else
|
|
36
|
-
|
|
37
|
-
return
|
|
92
|
+
insert_node(node, parent_node.right)
|
|
38
93
|
end
|
|
39
94
|
else
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
95
|
+
if parent_node.left.nil?
|
|
96
|
+
@leaf_set.delete(parent_node)
|
|
97
|
+
parent_node.left = node
|
|
98
|
+
node.level = parent_node.level + 1
|
|
99
|
+
@level = node.level if node.level > @level
|
|
100
|
+
@leaf_set.insert(node) if node.is_leaf?
|
|
46
101
|
return
|
|
47
102
|
else
|
|
48
|
-
|
|
49
|
-
return
|
|
103
|
+
insert_node(node, parent_node.left)
|
|
50
104
|
end
|
|
51
105
|
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
public
|
|
109
|
+
def initialize(arr = [])
|
|
110
|
+
@size = 0
|
|
111
|
+
@root = nil
|
|
112
|
+
@incomplete_set = CollectionUtils::Set.new()
|
|
113
|
+
@leaf_set = CollectionUtils::Set.new()
|
|
114
|
+
arr.each do |element|
|
|
115
|
+
insert(element)
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# Insert would insert the next element in BST according to
|
|
120
|
+
# BST properties that is if element added is less than the parent_node
|
|
121
|
+
# then element will be at left side of the node else will be on the right
|
|
122
|
+
# side of the node. This operation is in O(logn)
|
|
123
|
+
# @param element Element which needs to be added to BST
|
|
124
|
+
# @example if tree has values [5,2,3,4]
|
|
125
|
+
# => @bst = CollectionUtils::TreeUtils::BST.new([5,2,3,4])
|
|
126
|
+
# => # BST would look like this
|
|
127
|
+
# => # 5
|
|
128
|
+
# => # /
|
|
129
|
+
# => # 2
|
|
130
|
+
# => # \
|
|
131
|
+
# => # 3
|
|
132
|
+
# => # \
|
|
133
|
+
# => # 4
|
|
134
|
+
# => @bst.insert(6)
|
|
135
|
+
# => # Now BST would look like this
|
|
136
|
+
# => # 5
|
|
137
|
+
# => # / \
|
|
138
|
+
# => # 2 6
|
|
139
|
+
# => # \
|
|
140
|
+
# => # 3
|
|
141
|
+
# => # \
|
|
142
|
+
# => # 4
|
|
52
143
|
|
|
144
|
+
def insert(element)
|
|
145
|
+
node = Node.new(element)
|
|
146
|
+
if is_empty?
|
|
147
|
+
@root = node
|
|
148
|
+
@root.level = 1
|
|
149
|
+
@level = 1
|
|
150
|
+
@size += 1
|
|
151
|
+
@leaf_set.insert(node)
|
|
152
|
+
return
|
|
153
|
+
end
|
|
154
|
+
insert_node(node, @root)
|
|
53
155
|
end
|
|
54
156
|
|
|
55
|
-
|
|
56
|
-
|
|
157
|
+
# Delete would delete the element in BST and balance the BST accordingly.
|
|
158
|
+
# The delete option uses the BST property that is every node has lesser value
|
|
159
|
+
# on the left side and larger value lies on the right side.
|
|
160
|
+
# This operation is in O(logn)
|
|
161
|
+
# @param element Element which needs to be deleted from BST
|
|
162
|
+
# @example if tree has values [5,2,3,4,6]
|
|
163
|
+
# => @bst = CollectionUtils::TreeUtils::BST.new([5,2,3,4])
|
|
164
|
+
# => # BST would look like this
|
|
165
|
+
# => # 5
|
|
166
|
+
# => # / \
|
|
167
|
+
# => # 2 6
|
|
168
|
+
# => # \
|
|
169
|
+
# => # 3
|
|
170
|
+
# => # \
|
|
171
|
+
# => # 4
|
|
172
|
+
# => @bst.insert(5)
|
|
173
|
+
# => # Now BST would look like this
|
|
174
|
+
# => # 6
|
|
175
|
+
# => # /
|
|
176
|
+
# => # 2
|
|
177
|
+
# => # \
|
|
178
|
+
# => # 3
|
|
179
|
+
# => # \
|
|
180
|
+
# => # 4
|
|
181
|
+
def delete(element)
|
|
182
|
+
delete_node(element, @root)
|
|
57
183
|
end
|
|
58
184
|
|
|
59
|
-
|
|
60
|
-
|
|
185
|
+
# Leftmost value will be the smallest value returned using this function
|
|
186
|
+
# This operation is in O(logn)
|
|
187
|
+
# @return element which is smallest in the BST
|
|
188
|
+
# @example if tree has values [5,2,3,4,6]
|
|
189
|
+
# => @bst = CollectionUtils::TreeUtils::BST.new([5,2,3,4])
|
|
190
|
+
# => # BST would look like this
|
|
191
|
+
# => # 5
|
|
192
|
+
# => # / \
|
|
193
|
+
# => # 2 6
|
|
194
|
+
# => # \
|
|
195
|
+
# => # 3
|
|
196
|
+
# => # \
|
|
197
|
+
# => # 4
|
|
198
|
+
# => value = @bst.smallest
|
|
199
|
+
# => value == 2
|
|
200
|
+
def smallest
|
|
201
|
+
find_smallest(@root)
|
|
61
202
|
end
|
|
62
203
|
|
|
63
|
-
|
|
64
|
-
|
|
204
|
+
# Rightmost value will be the largest value returned using this function
|
|
205
|
+
# This operation is in O(logn)
|
|
206
|
+
# @return element which is largest in the BST
|
|
207
|
+
# @example if tree has values [5,2,3,4,6]
|
|
208
|
+
# => @bst = CollectionUtils::TreeUtils::BST.new([5,2,3,4])
|
|
209
|
+
# => # BST would look like this
|
|
210
|
+
# => # 5
|
|
211
|
+
# => # / \
|
|
212
|
+
# => # 2 6
|
|
213
|
+
# => # \
|
|
214
|
+
# => # 3
|
|
215
|
+
# => # \
|
|
216
|
+
# => # 4
|
|
217
|
+
# => value = @bst.largest
|
|
218
|
+
# => value == 6
|
|
219
|
+
def largest
|
|
220
|
+
find_largest(@root)
|
|
65
221
|
end
|
|
66
222
|
|
|
67
223
|
end
|