kxi 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/lib/kxi.rb +44 -39
  3. data/lib/kxi/application/config.rb +177 -177
  4. data/lib/kxi/application/config_reader.rb +16 -16
  5. data/lib/kxi/application/event.rb +35 -35
  6. data/lib/kxi/application/logger.rb +155 -155
  7. data/lib/kxi/application/version.rb +106 -74
  8. data/lib/kxi/application/version_expression.rb +94 -69
  9. data/lib/kxi/application/workspace.rb +105 -0
  10. data/lib/kxi/cli/anonymous_argument.rb +50 -50
  11. data/lib/kxi/cli/argument.rb +56 -56
  12. data/lib/kxi/cli/argument_values.rb +83 -83
  13. data/lib/kxi/cli/explicit_argument.rb +38 -38
  14. data/lib/kxi/cli/flag_argument.rb +15 -15
  15. data/lib/kxi/cli/named_argument.rb +59 -59
  16. data/lib/kxi/cli/property_list.rb +57 -48
  17. data/lib/kxi/cli/table.rb +82 -62
  18. data/lib/kxi/cli/verb.rb +282 -280
  19. data/lib/kxi/collections/array_collection.rb +106 -106
  20. data/lib/kxi/collections/enumerable.rb +527 -527
  21. data/lib/kxi/collections/enumerator.rb +31 -31
  22. data/lib/kxi/collections/hash_collection.rb +100 -100
  23. data/lib/kxi/collections/protected_collection.rb +20 -19
  24. data/lib/kxi/exceptions/abstract_exception.rb +34 -34
  25. data/lib/kxi/exceptions/argument_exception.rb +21 -21
  26. data/lib/kxi/exceptions/collection_exception.rb +13 -13
  27. data/lib/kxi/exceptions/configuration_exception.rb +36 -25
  28. data/lib/kxi/exceptions/dimension_mismatch_exception.rb +29 -0
  29. data/lib/kxi/exceptions/invalid_type_exception.rb +32 -32
  30. data/lib/kxi/exceptions/no_argument_exception.rb +20 -20
  31. data/lib/kxi/exceptions/not_implemented_exception.rb +12 -12
  32. data/lib/kxi/exceptions/out_of_range_exception.rb +43 -43
  33. data/lib/kxi/exceptions/parse_exception.rb +28 -20
  34. data/lib/kxi/exceptions/verb_expected_exception.rb +20 -20
  35. data/lib/kxi/exceptions/workspace_collision_exception.rb +21 -0
  36. data/lib/kxi/math/math.rb +45 -0
  37. data/lib/kxi/math/matrix.rb +303 -0
  38. data/lib/kxi/math/polynomial.rb +141 -101
  39. data/lib/kxi/math/vector.rb +181 -0
  40. data/lib/kxi/platform.rb +103 -57
  41. data/lib/kxi/reflection/stack_frame.rb +80 -80
  42. data/lib/kxi/version.rb +4 -4
  43. metadata +8 -3
  44. data/lib/kxi/exceptions/invalid_operation_exception.rb +0 -11
@@ -1,107 +1,107 @@
1
- # Created by Matyáš Pokorný on 2017-12-28.
2
-
3
- module KXI
4
- module Collections
5
- # Makes array enumerable
6
- class ArrayCollection < KXI::Collections::Enumerable
7
- # Instantiates the {KXI::Collections::ArrayCollection} class
8
- # @param array [Array] Array for enumeration
9
- def initialize(array = [])
10
- super()
11
- @arr = array
12
- end
13
-
14
- # Creates a new {KXI::Collections::Enumerator} bound to this instance
15
- # @return [KXI::Collections::Enumerator] Enumerator bound to this instance
16
- def create_enumerator
17
- ArrayEnumerator.new(@arr)
18
- end
19
-
20
- # Obtains value in array at specific index
21
- # @param index [Number] Index in array to obtain
22
- # @return [Object] Value in array at specified index
23
- # @raise [KXI::Exceptions::OutOfRangeException] Raised when given index is out of range of array
24
- def [](index)
25
- raise(KXI::Exceptions::OutOfRangeException.new(index)) if @arr.length == 0
26
- raise(KXI::Exceptions::OutOfRangeException.new(index, 0, @arr.length - 1)) if index < 0 or index >= @arr.length
27
- lock do
28
- return @arr[index]
29
- end
30
- end
31
-
32
- # Sets value of array at specific index
33
- # @param index [Number] Index in array to set
34
- # @param value [Object] Value to set
35
- # @return [Object] Set value
36
- # @raise [KXI::Exceptions::OutOfRangeException] Raised when given index is out of range of array
37
- # @raise [KXI::Exceptions::CollectionException] Raised when collection cannot be locked for writing
38
- def []=(index, value)
39
- raise(KXI::Exceptions::OutOfRangeException.new(index)) if @arr.length == 0
40
- raise(KXI::Exceptions::OutOfRangeException.new(index, 0, @arr.length - 1)) if index < 0 or index >= @arr.length
41
- lock(true) do
42
- @arr[index] = value
43
- end
44
- return value
45
- end
46
-
47
- # Adds value into array
48
- # @param value [Object] Value to set
49
- # @return [Number] Assigned index
50
- # @raise [KXI::Exceptions::CollectionException] Raised when collection cannot be locked for writing
51
- def add(value)
52
- idx = @arr.length
53
- lock(true) do
54
- @arr.push(value)
55
- end
56
- return idx
57
- end
58
-
59
- # Removes item at specific index
60
- # @param index [Number] Index to remove
61
- # @return [Object] Removed item
62
- # @raise [KXI::Exceptions::OutOfRangeException] Raised when given index is out of range of array
63
- # @raise [KXI::Exceptions::CollectionException] Raised when collection cannot be locked for writing
64
- def remove_at(index)
65
- raise(KXI::Exceptions::OutOfRangeException.new(index)) if @arr.length == 0
66
- raise(KXI::Exceptions::OutOfRangeException.new(index, 0, @arr.length - 1)) if index < 0 or index >= @arr.length
67
- ret = nil
68
- lock(true) do
69
- ret = @arr.delete_at(index)
70
- end
71
- return ret
72
- end
73
-
74
- # Enumerates array
75
- class ArrayEnumerator < KXI::Collections::Enumerator
76
- # Instantiates the {KXI::Collections::ArrayCollection::ArrayEnumerator} class
77
- # @param array [Array] Array for enumeration
78
- def initialize(array)
79
- @arr = array
80
- @current = 0
81
- end
82
-
83
- # Selects first item in collection
84
- # @return [Bool] True if collection contains elements; otherwise false
85
- def rewind
86
- @current = 0
87
- return @arr.length > 0
88
- end
89
-
90
- # Advances enumerator to next item
91
- # @return [Bool] True if item is available; false otherwise
92
- def next
93
- @current = @current + 1
94
- return @current < @arr.length
95
- end
96
-
97
- # Returns current item
98
- # @return [Object] Current item
99
- def current
100
- @arr[@current]
101
- end
102
- end
103
-
104
- private :create_enumerator
105
- end
106
- end
1
+ # Created by Matyáš Pokorný on 2017-12-28.
2
+
3
+ module KXI
4
+ module Collections
5
+ # Makes array enumerable
6
+ class ArrayCollection < KXI::Collections::Enumerable
7
+ # Instantiates the {KXI::Collections::ArrayCollection} class
8
+ # @param array [Array] Array for enumeration
9
+ def initialize(array = [])
10
+ super()
11
+ @arr = array
12
+ end
13
+
14
+ # Creates a new {KXI::Collections::Enumerator} bound to this instance
15
+ # @return [KXI::Collections::Enumerator] Enumerator bound to this instance
16
+ def create_enumerator
17
+ ArrayEnumerator.new(@arr)
18
+ end
19
+
20
+ # Obtains value in array at specific index
21
+ # @param index [Number] Index in array to obtain
22
+ # @return [Object] Value in array at specified index
23
+ # @raise [KXI::Exceptions::OutOfRangeException] Raised when given index is out of range of array
24
+ def [](index)
25
+ raise(KXI::Exceptions::OutOfRangeException.new(index)) if @arr.length == 0
26
+ raise(KXI::Exceptions::OutOfRangeException.new(index, 0, @arr.length - 1)) if index < 0 or index >= @arr.length
27
+ lock do
28
+ return @arr[index]
29
+ end
30
+ end
31
+
32
+ # Sets value of array at specific index
33
+ # @param index [Number] Index in array to set
34
+ # @param value [Object] Value to set
35
+ # @return [Object] Set value
36
+ # @raise [KXI::Exceptions::OutOfRangeException] Raised when given index is out of range of array
37
+ # @raise [KXI::Exceptions::CollectionException] Raised when collection cannot be locked for writing
38
+ def []=(index, value)
39
+ raise(KXI::Exceptions::OutOfRangeException.new(index)) if @arr.length == 0
40
+ raise(KXI::Exceptions::OutOfRangeException.new(index, 0, @arr.length - 1)) if index < 0 or index >= @arr.length
41
+ lock(true) do
42
+ @arr[index] = value
43
+ end
44
+ return value
45
+ end
46
+
47
+ # Adds value into array
48
+ # @param value [Object] Value to set
49
+ # @return [Number] Assigned index
50
+ # @raise [KXI::Exceptions::CollectionException] Raised when collection cannot be locked for writing
51
+ def add(value)
52
+ idx = @arr.length
53
+ lock(true) do
54
+ @arr.push(value)
55
+ end
56
+ return idx
57
+ end
58
+
59
+ # Removes item at specific index
60
+ # @param index [Number] Index to remove
61
+ # @return [Object] Removed item
62
+ # @raise [KXI::Exceptions::OutOfRangeException] Raised when given index is out of range of array
63
+ # @raise [KXI::Exceptions::CollectionException] Raised when collection cannot be locked for writing
64
+ def remove_at(index)
65
+ raise(KXI::Exceptions::OutOfRangeException.new(index)) if @arr.length == 0
66
+ raise(KXI::Exceptions::OutOfRangeException.new(index, 0, @arr.length - 1)) if index < 0 or index >= @arr.length
67
+ ret = nil
68
+ lock(true) do
69
+ ret = @arr.delete_at(index)
70
+ end
71
+ return ret
72
+ end
73
+
74
+ # Enumerates array
75
+ class ArrayEnumerator < KXI::Collections::Enumerator
76
+ # Instantiates the {KXI::Collections::ArrayCollection::ArrayEnumerator} class
77
+ # @param array [Array] Array for enumeration
78
+ def initialize(array)
79
+ @arr = array
80
+ @current = 0
81
+ end
82
+
83
+ # Selects first item in collection
84
+ # @return [Bool] True if collection contains elements; otherwise false
85
+ def rewind
86
+ @current = 0
87
+ return @arr.length > 0
88
+ end
89
+
90
+ # Advances enumerator to next item
91
+ # @return [Bool] True if item is available; false otherwise
92
+ def next
93
+ @current = @current + 1
94
+ return @current < @arr.length
95
+ end
96
+
97
+ # Returns current item
98
+ # @return [Object] Current item
99
+ def current
100
+ @arr[@current]
101
+ end
102
+ end
103
+
104
+ private :create_enumerator
105
+ end
106
+ end
107
107
  end
@@ -1,528 +1,528 @@
1
- # Created by Matyáš Pokorný on 2017-12-28.
2
-
3
- module KXI
4
- module Collections
5
- # Allows it's superclass to be enumerated
6
- # @note Order of items is not guaranteed
7
- # @abstract
8
- class Enumerable
9
- # Instantiates the {KXI::Collections::Enumerable} class
10
- def initialize
11
- @locks = 0
12
- @alter = false
13
- end
14
-
15
- # Allows protected usage of {KXI::Collections::Enumerator} within bounds of block
16
- # @return [nil]
17
- # @yield [enumerator] Protected block of code
18
- # @yieldparam enumerator [KXI::Collections::Enumerator] Enumerator bound to this instance
19
- # @yieldreturn [void]
20
- # @raise [KXI::Exceptions::AbstractException] When method {#create_enumerator} is not implemented in superclass
21
- def enumerator
22
- lock do
23
- yield(create_enumerator)
24
- end
25
- return nil
26
- end
27
-
28
- # Creates a new {KXI::Collections::Enumerator} bound to this instance
29
- # @return [KXI::Collections::Enumerator] Enumerator bound to this instance
30
- # @raise [KXI::Exceptions::AbstractException] When method is not implemented in superclass
31
- # @abstract
32
- def create_enumerator
33
- raise(KXI::Exceptions::AbstractException.new(Enumerable))
34
- end
35
-
36
- # Calls block with each item of collection
37
- # @return [nil]
38
- # @yield [item] Function that works with items of collection
39
- # @yieldparam item [Object] Item of collection
40
- # @yieldreturn [Void]
41
- def foreach
42
- enumerator do |e|
43
- return nil unless e.rewind
44
- loop do
45
- yield(e.current)
46
- break unless e.next
47
- end
48
- end
49
- return nil
50
- end
51
-
52
- # Selects specific values
53
- # @return [KXI::Collections::ArrayCollection] Collection of selected values
54
- # @yield [item] Function that selects values from items of collection
55
- # @yieldparam item [Object] Item of collection
56
- # @yieldreturn [Object] Selected value from item
57
- def select
58
- ret = []
59
- foreach do |i|
60
- ret.push(yield(i))
61
- end
62
- return KXI::Collections::ArrayCollection.new(ret)
63
- end
64
-
65
- # Concatenates selected collections of items
66
- # @return [KXI::Collections::ArrayCollection] Collection of selected values
67
- # @yield [item] Function that selects collections of items from given items
68
- # @yieldparam item [Object] Item of collection
69
- # @yieldreturn [Array, KXI::Collections::Enumerable] Selected collection of items
70
- # @raise [KXI::Exceptions::CollectionException] Raised if selection function returns object that is not of type {KXI::Collections::Enumerable} nor Array
71
- def select_many
72
- ret = []
73
- foreach do |i|
74
- col = yield(i)
75
- if col.is_a?(Array)
76
- col.each { |j| ret.push(j) }
77
- elsif col.is_a?(Enumerable)
78
- col.foreach { |j| ret.push(j) }
79
- else
80
- raise(KXI::Exceptions::CollectionException.new('Selection function returned invalid type of object!'))
81
- end
82
- end
83
- return KXI::Collections::ArrayCollection.new(ret)
84
- end
85
-
86
- # Selects only those items that satisfy given function
87
- # @return [KXI::Collections::ArrayCollection] Collection of selected items
88
- # @yield [item] Function that evaluates whether item should be selected
89
- # @yieldparam item [Object] Item of collection
90
- # @yieldreturn [Bool] True if item should be selected; otherwise false
91
- def where
92
- ret = []
93
- foreach do |i|
94
- ret.push(i) if yield(i)
95
- end
96
- return KXI::Collections::ArrayCollection.new(ret)
97
- end
98
-
99
- # Counts items in collection
100
- # @return [Number] Number of items
101
- # @overload count()
102
- # Counts the total number of items in collection
103
- # @return [Number] Number of items in collection
104
- # @overload count()
105
- # Counts the number of items that satisfy given function
106
- # @return [Number] Number of items that satisfy given function
107
- # @yield [item] Function that evaluates whether item should be counted
108
- # @yieldparam item [Object] Item of collection
109
- # @yieldreturn [Bool] True if item should be counted; otherwise false
110
- def count
111
- count = 0
112
- if block_given?
113
- foreach do |item|
114
- count = count + 1 if yield(item)
115
- end
116
- else
117
- enumerator do |e|
118
- return 0 unless e.rewind
119
- count += 1
120
- while e.next
121
- count = count + 1
122
- end
123
- end
124
- end
125
- return count
126
- end
127
-
128
- # Checks if collection contains any item
129
- # @return [Bool] True if collection contains any item; otherwise false
130
- # @overload any()
131
- # Checks if collection is not empty
132
- # @return [Bool] True if collection is not empty; otherwise false
133
- # @overload any()
134
- # Checks if there is any item in collection that satisfies given function
135
- # @return [Bool] True if collection contains item that satisfies given function; otherwise false
136
- # @yield [item] Function used for checking
137
- # @yieldparam item [Object] Item of collection
138
- # @yieldreturn [Bool] True if item satisfies given function; otherwise false
139
- def any
140
- if block_given?
141
- foreach do |item|
142
- return true if yield(item)
143
- end
144
- return false
145
- else
146
- enumerator do |e|
147
- return false unless e.rewind
148
- return true
149
- end
150
- end
151
- end
152
-
153
- # Checks if all items in collection satisfy given function
154
- # @return [Bool] True if all items of collection satisfy given function; otherwise false
155
- # @yield [item] Function used for checking
156
- # @yieldparam item [Object] Item of collection
157
- # @yieldreturn [Bool] True if item satisfies given function; otherwise false
158
- def all
159
- foreach do |item|
160
- return false unless yield(item)
161
- end
162
- return true
163
- end
164
-
165
- # Gets first item, or default
166
- # @overload first(df = nil)
167
- # Gets first item of collection
168
- # @param df [Object, nil] Default value
169
- # @return [Object, nil] First item of collection; default value if collection is empty
170
- # @overload first(df = nil)
171
- # Gets first item of collection that satisfies given function
172
- # @param df [Object, nil] Default value
173
- # @return [Object, nil] First item of collection that satisfies given function; default value if no such item exists
174
- # @yield [item] Function for evaluation
175
- # @yieldparam item [Object] Item of collection
176
- # @yieldreturn [Bool] True if item satisfies given function; false otherwise
177
- def first(df = nil)
178
- if block_given?
179
- foreach do |item|
180
- return item if yield(item)
181
- end
182
- else
183
- enumerator do |e|
184
- return e.current if e.rewind
185
- end
186
- end
187
- return df
188
- end
189
-
190
-
191
- # Gets first item
192
- # @overload first!()
193
- # Gets first item of collection
194
- # @return [Object, nil] First item of collection
195
- # @raise [KXI::Exceptions::CollectionException] Raised when collection is empty
196
- # @overload first!()
197
- # Gets first item of collection that satisfies given function
198
- # @return [Object, nil] First item of collection that satisfies given function
199
- # @raise [KXI::Exceptions::CollectionException] Raised when collection contains no element that satisfies given function
200
- # @yield [item] Function for evaluation
201
- # @yieldparam item [Object] Item of collection
202
- # @yieldreturn [Bool] True if item satisfies given function; false otherwise
203
- def first!
204
- if block_given?
205
- foreach do |item|
206
- return item if yield(item)
207
- end
208
- else
209
- enumerator do |e|
210
- return e.current if e.rewind
211
- end
212
- end
213
- raise(KXI::Exceptions::CollectionException.new("Collection contains no #{(block_given? ? 'such ' : '')}items!"))
214
- end
215
-
216
- # Gets last item, or default
217
- # @overload last(df = nil)
218
- # Gets last item of collection
219
- # @param df [Object, nil] Default value
220
- # @return [Object, nil] Last item of collection; default value if collection is empty
221
- # @overload last(df = nil)
222
- # Gets last item of collection that satisfies given function
223
- # @param df [Object, nil] Default value
224
- # @return [Object, nil] Last item of collection that satisfies given function; default value if no such item exists
225
- # @yield [item] Function for evaluation
226
- # @yieldparam item [Object] Item of collection
227
- # @yieldreturn [Bool] True if item satisfies given function; false otherwise
228
- def last(df = nil)
229
- found = false
230
- ret = nil
231
- if block_given?
232
- foreach do |item|
233
- if yield(item)
234
- ret = item
235
- found = true
236
- end
237
- end
238
- else
239
- foreach do |item|
240
- ret = item
241
- found = true
242
- end
243
- end
244
- return df unless found
245
- return ret
246
- end
247
-
248
- # Gets last item
249
- # @overload last!()
250
- # Gets last item of collection
251
- # @return [Object, nil] Last item of collection
252
- # @raise [KXI::Exceptions::CollectionException] Raised if collection is empty
253
- # @overload last!()
254
- # Gets last item of collection that satisfies given function
255
- # @return [Object, nil] Last item of collection that satisfies given function
256
- # @raise [KXI::Exceptions::CollectionException] Raised when collection doesn't contain element that satisfies given function
257
- # @yield [item] Function for evaluation
258
- # @yieldparam item [Object] Item of collection
259
- # @yieldreturn [Bool] True if item satisfies given function; false otherwise
260
- def last!
261
- found = false
262
- ret = nil
263
- if block_given?
264
- foreach do |item|
265
- if yield(item)
266
- ret = item
267
- found = true
268
- end
269
- end
270
- else
271
- foreach do |item|
272
- ret = item
273
- found = true
274
- end
275
- end
276
- raise(KXI::Exceptions::CollectionException.new("Collection contains no #{(block_given? ? 'such ' : '')}items!")) unless found
277
- return ret
278
- end
279
-
280
- # Skips given number of items and returns the rest
281
- # @param count [Number] Number of items to skip
282
- # @return [KXI::Collections::ArrayCollection] Collection of items that weren't skipped
283
- def skip(count)
284
- ret = []
285
- foreach do |item|
286
- if count > 0
287
- count -= 1
288
- else
289
- ret.push(item)
290
- end
291
- end
292
- return KXI::Collections::ArrayCollection.new(ret)
293
- end
294
-
295
- # Returns first N elements
296
- # @param count [Number] Number of items to take
297
- # @return [KXI::Collections::ArrayCollection] Collection of first N items
298
- def take(count)
299
- ret = []
300
- foreach do |item|
301
- break unless count > 0
302
- count -= 1
303
- ret.push(item)
304
- end
305
- return KXI::Collections::ArrayCollection.new(ret)
306
- end
307
-
308
- # Aggregates all items of collection
309
- # @param start [Object, nil] Starting value
310
- # @return [Object] Aggregated value
311
- # @yield [current, item] Aggregation function
312
- # @yieldparam current [Object] Current aggregate
313
- # @yieldparam item [Object] Item of collection
314
- # @yieldreturn [Object] Aggregated value
315
- def aggregate(start = nil)
316
- foreach do |item|
317
- start = yield(start, item)
318
- end
319
- return start
320
- end
321
-
322
- # Orders collection to ascending order
323
- # @note Ordering is done using merge sort algorithm
324
- # @return [KXI::Collections::ArrayCollection] Ordered collection
325
- # @overload order_by()
326
- # Orders collection using spaceship operator (<=>)
327
- # @return [KXI::Collections::ArrayCollection] Ordered collection
328
- # @overload order_by()
329
- # Orders collection using given comparison function
330
- # @return [KXI::Collections::ArrayCollection] Ordered collection
331
- # @yield [what, with] Comparison function
332
- # @yieldparam what [Object] Object that is being compared
333
- # @yieldparam with [Object] Object that is being compared to
334
- # @yieldreturn [Number] Less then 0 if what < with, equal to 0 if what = with and bigger than 0 if what > with
335
- def order_by(&block)
336
- block = lambda { |a, b| a <=> b } if block == nil
337
- groups = []
338
- g = []
339
- foreach do |item|
340
- if g.length == 0
341
- g.push(item)
342
- else
343
- if block.call(g.last, item) <= 0
344
- g.push(item)
345
- else
346
- groups.push(g)
347
- g = [item]
348
- end
349
- end
350
- end
351
- groups.push(g) if g.length > 0
352
- return KXI::Collections::ArrayCollection.new if groups.length == 0
353
- while groups.length > 1
354
- merges = []
355
- while groups.length >= 2
356
- merges.push(merge(groups.shift, groups.shift, &block))
357
- end
358
- merges.push(groups[0]) if groups.length > 0
359
- groups = merges
360
- end
361
- return KXI::Collections::ArrayCollection.new(groups[0])
362
- end
363
-
364
- # Orders collection to descending order
365
- # @note Ordering is done using merge sort algorithm
366
- # @return [KXI::Collections::ArrayCollection] Ordered collection
367
- # @overload order_by()
368
- # Orders collection using spaceship operator (<=>)
369
- # @return [KXI::Collections::ArrayCollection] Ordered collection
370
- # @overload order_by()
371
- # Orders collection using given comparison function
372
- # @return [KXI::Collections::ArrayCollection] Ordered collection
373
- # @yield [what, with] Comparison function
374
- # @yieldparam what [Object] Object that is being compared
375
- # @yieldparam with [Object] Object that is being compared to
376
- # @yieldreturn [Number] Less then 0 if what < with, equal to 0 if what = with and bigger than 0 if what > with
377
- def order_by_descending(&block)
378
- if block == nil
379
- return order_by { |a, b| b <=> a }
380
- else
381
- return order_by { |a, b| -block.call(a, b) }
382
- end
383
- end
384
-
385
- # Selects only those items that are of at least one of specified types
386
- # @param klass [Class] Types to use for selection
387
- # @return [KXI::Collections::ArrayCollection] Selected items
388
- # @see Object#is_a?
389
- def of_type(*klass)
390
- ret = []
391
- foreach do |i|
392
- klass.each do |k|
393
- if i.is_a?(k)
394
- ret.push(i)
395
- break
396
- end
397
- end
398
- end
399
- return KXI::Collections::ArrayCollection.new(ret)
400
- end
401
-
402
- # Selects only those items that are exactly of at least one of specified types
403
- # @param klass [Class] Types to use for selection
404
- # @return [KXI::Collections::ArrayCollection] Selected items
405
- # @see Object#class
406
- def of_type!(*klass)
407
- ret = []
408
- foreach do |i|
409
- klass.each do |k|
410
- if i.class == k
411
- ret.push(i)
412
- break
413
- end
414
- end
415
- end
416
- return KXI::Collections::ArrayCollection.new(ret)
417
- end
418
-
419
- # Obtains item with the first maximal value
420
- # @overload max()
421
- # Compares items using spaceship operator (<=>)
422
- # @return [Object] Item with first maximal value; nil if collection is empty
423
- # @overload max()
424
- # Compares items using given comparison function
425
- # @return [Object] Item with first maximal value; nil if collection is empty
426
- # @yield [what, with] Comparison function
427
- # @yieldparam what [Object] Object that is being compared
428
- # @yieldparam with [Object] Object that is being compared to
429
- # @yieldreturn [Number] Less then 0 if what < with, equal to 0 if what = with and bigger than 0 if what > with
430
- def max(&block)
431
- block = lambda { |a, b| a <=> b } if block == nil
432
- ret = nil
433
- first = true
434
- foreach do |i|
435
- if first
436
- ret = i
437
- first = false
438
- elsif block.call(i, ret) > 0
439
- ret = i
440
- end
441
- end
442
- return ret
443
- end
444
-
445
- # Obtains item with the first minimal value
446
- # @overload min()
447
- # Compares items using spaceship operator (<=>)
448
- # @return [Object] Item with first minimal value; nil if collection is empty
449
- # @overload min()
450
- # Compares items using given comparison function
451
- # @return [Object] Item with first minimal value; nil if collection is empty
452
- # @yield [what, with] Comparison function
453
- # @yieldparam what [Object] Object that is being compared
454
- # @yieldparam with [Object] Object that is being compared to
455
- # @yieldreturn [Number] Less then 0 if what < with, equal to 0 if what = with and bigger than 0 if what > with
456
- def min(&block)
457
- block = lambda { |a, b| a <=> b } if block == nil
458
- ret = nil
459
- first = true
460
- foreach do |i|
461
- if first
462
- ret = i
463
- first = false
464
- elsif block.call(i, ret) < 0
465
- ret = i
466
- end
467
- end
468
- return ret
469
- end
470
-
471
- # Coverts collection to array
472
- # @return [Array] Array equivalent to collection
473
- def to_array
474
- ret = []
475
- foreach { |item| ret.push(item) }
476
- return ret
477
- end
478
-
479
- # Merges two arrays in order using comparison function
480
- # @param a [Array] First array
481
- # @param b [Array] Second array
482
- # @return [Array] Merged ordered array
483
- def merge(a, b, &comp)
484
- ret = []
485
- while a.length > 0 or b.length > 0
486
- if a.length > 0 and b.length > 0
487
- c = comp.call(a[0], b[0])
488
- if c <= 0
489
- ret.push(a.shift)
490
- else
491
- ret.push(b.shift)
492
- end
493
- else
494
- ret.push(a.length > 0 ? a.shift : b.shift)
495
- end
496
- end
497
- return ret
498
- end
499
-
500
- # Indicates whether collection can be altered
501
- # @return [Bool] True if collection can be altered; otherwise false
502
- def can_alter?
503
- @locks == 0 and not @alter
504
- end
505
-
506
- def lock(write = false)
507
- if write
508
- raise(KXI::Exceptions::CollectionException.new('Collection cannot be locked for writing!')) unless can_alter?
509
- @alter = true
510
- yield
511
- else
512
- raise(KXI::Exceptions::CollectionException.new('Collection cannot be locked for reading!')) if @alter
513
- @locks += 1
514
- yield
515
- end
516
- ensure
517
- if write
518
- @alter = false
519
- else
520
- @locks -= 1
521
- end
522
- end
523
-
524
- protected :lock, :create_enumerator
525
- private :merge
526
- end
527
- end
1
+ # Created by Matyáš Pokorný on 2017-12-28.
2
+
3
+ module KXI
4
+ module Collections
5
+ # Allows it's superclass to be enumerated
6
+ # @note Order of items is not guaranteed
7
+ # @abstract
8
+ class Enumerable
9
+ # Instantiates the {KXI::Collections::Enumerable} class
10
+ def initialize
11
+ @locks = 0
12
+ @alter = false
13
+ end
14
+
15
+ # Allows protected usage of {KXI::Collections::Enumerator} within bounds of block
16
+ # @return [nil]
17
+ # @yield [enumerator] Protected block of code
18
+ # @yieldparam enumerator [KXI::Collections::Enumerator] Enumerator bound to this instance
19
+ # @yieldreturn [void]
20
+ # @raise [KXI::Exceptions::AbstractException] When method {#create_enumerator} is not implemented in superclass
21
+ def enumerator
22
+ lock do
23
+ yield(create_enumerator)
24
+ end
25
+ return nil
26
+ end
27
+
28
+ # Creates a new {KXI::Collections::Enumerator} bound to this instance
29
+ # @return [KXI::Collections::Enumerator] Enumerator bound to this instance
30
+ # @raise [KXI::Exceptions::AbstractException] When method is not implemented in superclass
31
+ # @abstract
32
+ def create_enumerator
33
+ raise(KXI::Exceptions::AbstractException.new(Enumerable))
34
+ end
35
+
36
+ # Calls block with each item of collection
37
+ # @return [nil]
38
+ # @yield [item] Function that works with items of collection
39
+ # @yieldparam item [Object] Item of collection
40
+ # @yieldreturn [Void]
41
+ def foreach
42
+ enumerator do |e|
43
+ return nil unless e.rewind
44
+ loop do
45
+ yield(e.current)
46
+ break unless e.next
47
+ end
48
+ end
49
+ return nil
50
+ end
51
+
52
+ # Selects specific values
53
+ # @return [KXI::Collections::ArrayCollection] Collection of selected values
54
+ # @yield [item] Function that selects values from items of collection
55
+ # @yieldparam item [Object] Item of collection
56
+ # @yieldreturn [Object] Selected value from item
57
+ def select
58
+ ret = []
59
+ foreach do |i|
60
+ ret.push(yield(i))
61
+ end
62
+ return KXI::Collections::ArrayCollection.new(ret)
63
+ end
64
+
65
+ # Concatenates selected collections of items
66
+ # @return [KXI::Collections::ArrayCollection] Collection of selected values
67
+ # @yield [item] Function that selects collections of items from given items
68
+ # @yieldparam item [Object] Item of collection
69
+ # @yieldreturn [Array, KXI::Collections::Enumerable] Selected collection of items
70
+ # @raise [KXI::Exceptions::CollectionException] Raised if selection function returns object that is not of type {KXI::Collections::Enumerable} nor Array
71
+ def select_many
72
+ ret = []
73
+ foreach do |i|
74
+ col = yield(i)
75
+ if col.is_a?(Array)
76
+ col.each { |j| ret.push(j) }
77
+ elsif col.is_a?(Enumerable)
78
+ col.foreach { |j| ret.push(j) }
79
+ else
80
+ raise(KXI::Exceptions::CollectionException.new('Selection function returned invalid type of object!'))
81
+ end
82
+ end
83
+ return KXI::Collections::ArrayCollection.new(ret)
84
+ end
85
+
86
+ # Selects only those items that satisfy given function
87
+ # @return [KXI::Collections::ArrayCollection] Collection of selected items
88
+ # @yield [item] Function that evaluates whether item should be selected
89
+ # @yieldparam item [Object] Item of collection
90
+ # @yieldreturn [Bool] True if item should be selected; otherwise false
91
+ def where
92
+ ret = []
93
+ foreach do |i|
94
+ ret.push(i) if yield(i)
95
+ end
96
+ return KXI::Collections::ArrayCollection.new(ret)
97
+ end
98
+
99
+ # Counts items in collection
100
+ # @return [Number] Number of items
101
+ # @overload count()
102
+ # Counts the total number of items in collection
103
+ # @return [Number] Number of items in collection
104
+ # @overload count()
105
+ # Counts the number of items that satisfy given function
106
+ # @return [Number] Number of items that satisfy given function
107
+ # @yield [item] Function that evaluates whether item should be counted
108
+ # @yieldparam item [Object] Item of collection
109
+ # @yieldreturn [Bool] True if item should be counted; otherwise false
110
+ def count
111
+ count = 0
112
+ if block_given?
113
+ foreach do |item|
114
+ count = count + 1 if yield(item)
115
+ end
116
+ else
117
+ enumerator do |e|
118
+ return 0 unless e.rewind
119
+ count += 1
120
+ while e.next
121
+ count = count + 1
122
+ end
123
+ end
124
+ end
125
+ return count
126
+ end
127
+
128
+ # Checks if collection contains any item
129
+ # @return [Bool] True if collection contains any item; otherwise false
130
+ # @overload any()
131
+ # Checks if collection is not empty
132
+ # @return [Bool] True if collection is not empty; otherwise false
133
+ # @overload any()
134
+ # Checks if there is any item in collection that satisfies given function
135
+ # @return [Bool] True if collection contains item that satisfies given function; otherwise false
136
+ # @yield [item] Function used for checking
137
+ # @yieldparam item [Object] Item of collection
138
+ # @yieldreturn [Bool] True if item satisfies given function; otherwise false
139
+ def any
140
+ if block_given?
141
+ foreach do |item|
142
+ return true if yield(item)
143
+ end
144
+ return false
145
+ else
146
+ enumerator do |e|
147
+ return false unless e.rewind
148
+ return true
149
+ end
150
+ end
151
+ end
152
+
153
+ # Checks if all items in collection satisfy given function
154
+ # @return [Bool] True if all items of collection satisfy given function; otherwise false
155
+ # @yield [item] Function used for checking
156
+ # @yieldparam item [Object] Item of collection
157
+ # @yieldreturn [Bool] True if item satisfies given function; otherwise false
158
+ def all
159
+ foreach do |item|
160
+ return false unless yield(item)
161
+ end
162
+ return true
163
+ end
164
+
165
+ # Gets first item, or default
166
+ # @overload first(df = nil)
167
+ # Gets first item of collection
168
+ # @param df [Object, nil] Default value
169
+ # @return [Object, nil] First item of collection; default value if collection is empty
170
+ # @overload first(df = nil)
171
+ # Gets first item of collection that satisfies given function
172
+ # @param df [Object, nil] Default value
173
+ # @return [Object, nil] First item of collection that satisfies given function; default value if no such item exists
174
+ # @yield [item] Function for evaluation
175
+ # @yieldparam item [Object] Item of collection
176
+ # @yieldreturn [Bool] True if item satisfies given function; false otherwise
177
+ def first(df = nil)
178
+ if block_given?
179
+ foreach do |item|
180
+ return item if yield(item)
181
+ end
182
+ else
183
+ enumerator do |e|
184
+ return e.current if e.rewind
185
+ end
186
+ end
187
+ return df
188
+ end
189
+
190
+
191
+ # Gets first item
192
+ # @overload first!()
193
+ # Gets first item of collection
194
+ # @return [Object, nil] First item of collection
195
+ # @raise [KXI::Exceptions::CollectionException] Raised when collection is empty
196
+ # @overload first!()
197
+ # Gets first item of collection that satisfies given function
198
+ # @return [Object, nil] First item of collection that satisfies given function
199
+ # @raise [KXI::Exceptions::CollectionException] Raised when collection contains no element that satisfies given function
200
+ # @yield [item] Function for evaluation
201
+ # @yieldparam item [Object] Item of collection
202
+ # @yieldreturn [Bool] True if item satisfies given function; false otherwise
203
+ def first!
204
+ if block_given?
205
+ foreach do |item|
206
+ return item if yield(item)
207
+ end
208
+ else
209
+ enumerator do |e|
210
+ return e.current if e.rewind
211
+ end
212
+ end
213
+ raise(KXI::Exceptions::CollectionException.new("Collection contains no #{(block_given? ? 'such ' : '')}items!"))
214
+ end
215
+
216
+ # Gets last item, or default
217
+ # @overload last(df = nil)
218
+ # Gets last item of collection
219
+ # @param df [Object, nil] Default value
220
+ # @return [Object, nil] Last item of collection; default value if collection is empty
221
+ # @overload last(df = nil)
222
+ # Gets last item of collection that satisfies given function
223
+ # @param df [Object, nil] Default value
224
+ # @return [Object, nil] Last item of collection that satisfies given function; default value if no such item exists
225
+ # @yield [item] Function for evaluation
226
+ # @yieldparam item [Object] Item of collection
227
+ # @yieldreturn [Bool] True if item satisfies given function; false otherwise
228
+ def last(df = nil)
229
+ found = false
230
+ ret = nil
231
+ if block_given?
232
+ foreach do |item|
233
+ if yield(item)
234
+ ret = item
235
+ found = true
236
+ end
237
+ end
238
+ else
239
+ foreach do |item|
240
+ ret = item
241
+ found = true
242
+ end
243
+ end
244
+ return df unless found
245
+ return ret
246
+ end
247
+
248
+ # Gets last item
249
+ # @overload last!()
250
+ # Gets last item of collection
251
+ # @return [Object, nil] Last item of collection
252
+ # @raise [KXI::Exceptions::CollectionException] Raised if collection is empty
253
+ # @overload last!()
254
+ # Gets last item of collection that satisfies given function
255
+ # @return [Object, nil] Last item of collection that satisfies given function
256
+ # @raise [KXI::Exceptions::CollectionException] Raised when collection doesn't contain element that satisfies given function
257
+ # @yield [item] Function for evaluation
258
+ # @yieldparam item [Object] Item of collection
259
+ # @yieldreturn [Bool] True if item satisfies given function; false otherwise
260
+ def last!
261
+ found = false
262
+ ret = nil
263
+ if block_given?
264
+ foreach do |item|
265
+ if yield(item)
266
+ ret = item
267
+ found = true
268
+ end
269
+ end
270
+ else
271
+ foreach do |item|
272
+ ret = item
273
+ found = true
274
+ end
275
+ end
276
+ raise(KXI::Exceptions::CollectionException.new("Collection contains no #{(block_given? ? 'such ' : '')}items!")) unless found
277
+ return ret
278
+ end
279
+
280
+ # Skips given number of items and returns the rest
281
+ # @param count [Number] Number of items to skip
282
+ # @return [KXI::Collections::ArrayCollection] Collection of items that weren't skipped
283
+ def skip(count)
284
+ ret = []
285
+ foreach do |item|
286
+ if count > 0
287
+ count -= 1
288
+ else
289
+ ret.push(item)
290
+ end
291
+ end
292
+ return KXI::Collections::ArrayCollection.new(ret)
293
+ end
294
+
295
+ # Returns first N elements
296
+ # @param count [Number] Number of items to take
297
+ # @return [KXI::Collections::ArrayCollection] Collection of first N items
298
+ def take(count)
299
+ ret = []
300
+ foreach do |item|
301
+ break unless count > 0
302
+ count -= 1
303
+ ret.push(item)
304
+ end
305
+ return KXI::Collections::ArrayCollection.new(ret)
306
+ end
307
+
308
+ # Aggregates all items of collection
309
+ # @param start [Object, nil] Starting value
310
+ # @return [Object] Aggregated value
311
+ # @yield [current, item] Aggregation function
312
+ # @yieldparam current [Object] Current aggregate
313
+ # @yieldparam item [Object] Item of collection
314
+ # @yieldreturn [Object] Aggregated value
315
+ def aggregate(start = nil)
316
+ foreach do |item|
317
+ start = yield(start, item)
318
+ end
319
+ return start
320
+ end
321
+
322
+ # Orders collection to ascending order
323
+ # @note Ordering is done using merge sort algorithm
324
+ # @return [KXI::Collections::ArrayCollection] Ordered collection
325
+ # @overload order_by()
326
+ # Orders collection using spaceship operator (<=>)
327
+ # @return [KXI::Collections::ArrayCollection] Ordered collection
328
+ # @overload order_by()
329
+ # Orders collection using given comparison function
330
+ # @return [KXI::Collections::ArrayCollection] Ordered collection
331
+ # @yield [what, with] Comparison function
332
+ # @yieldparam what [Object] Object that is being compared
333
+ # @yieldparam with [Object] Object that is being compared to
334
+ # @yieldreturn [Number] Less then 0 if what < with, equal to 0 if what = with and bigger than 0 if what > with
335
+ def order_by(&block)
336
+ block = lambda { |a, b| a <=> b } if block == nil
337
+ groups = []
338
+ g = []
339
+ foreach do |item|
340
+ if g.length == 0
341
+ g.push(item)
342
+ else
343
+ if block.call(g.last, item) <= 0
344
+ g.push(item)
345
+ else
346
+ groups.push(g)
347
+ g = [item]
348
+ end
349
+ end
350
+ end
351
+ groups.push(g) if g.length > 0
352
+ return KXI::Collections::ArrayCollection.new if groups.length == 0
353
+ while groups.length > 1
354
+ merges = []
355
+ while groups.length >= 2
356
+ merges.push(merge(groups.shift, groups.shift, &block))
357
+ end
358
+ merges.push(groups[0]) if groups.length > 0
359
+ groups = merges
360
+ end
361
+ return KXI::Collections::ArrayCollection.new(groups[0])
362
+ end
363
+
364
+ # Orders collection to descending order
365
+ # @note Ordering is done using merge sort algorithm
366
+ # @return [KXI::Collections::ArrayCollection] Ordered collection
367
+ # @overload order_by()
368
+ # Orders collection using spaceship operator (<=>)
369
+ # @return [KXI::Collections::ArrayCollection] Ordered collection
370
+ # @overload order_by()
371
+ # Orders collection using given comparison function
372
+ # @return [KXI::Collections::ArrayCollection] Ordered collection
373
+ # @yield [what, with] Comparison function
374
+ # @yieldparam what [Object] Object that is being compared
375
+ # @yieldparam with [Object] Object that is being compared to
376
+ # @yieldreturn [Number] Less then 0 if what < with, equal to 0 if what = with and bigger than 0 if what > with
377
+ def order_by_descending(&block)
378
+ if block == nil
379
+ return order_by { |a, b| b <=> a }
380
+ else
381
+ return order_by { |a, b| -block.call(a, b) }
382
+ end
383
+ end
384
+
385
+ # Selects only those items that are of at least one of specified types
386
+ # @param klass [Class] Types to use for selection
387
+ # @return [KXI::Collections::ArrayCollection] Selected items
388
+ # @see Object#is_a?
389
+ def of_type(*klass)
390
+ ret = []
391
+ foreach do |i|
392
+ klass.each do |k|
393
+ if i.is_a?(k)
394
+ ret.push(i)
395
+ break
396
+ end
397
+ end
398
+ end
399
+ return KXI::Collections::ArrayCollection.new(ret)
400
+ end
401
+
402
+ # Selects only those items that are exactly of at least one of specified types
403
+ # @param klass [Class] Types to use for selection
404
+ # @return [KXI::Collections::ArrayCollection] Selected items
405
+ # @see Object#class
406
+ def of_type!(*klass)
407
+ ret = []
408
+ foreach do |i|
409
+ klass.each do |k|
410
+ if i.class == k
411
+ ret.push(i)
412
+ break
413
+ end
414
+ end
415
+ end
416
+ return KXI::Collections::ArrayCollection.new(ret)
417
+ end
418
+
419
+ # Obtains item with the first maximal value
420
+ # @overload max()
421
+ # Compares items using spaceship operator (<=>)
422
+ # @return [Object] Item with first maximal value; nil if collection is empty
423
+ # @overload max()
424
+ # Compares items using given comparison function
425
+ # @return [Object] Item with first maximal value; nil if collection is empty
426
+ # @yield [what, with] Comparison function
427
+ # @yieldparam what [Object] Object that is being compared
428
+ # @yieldparam with [Object] Object that is being compared to
429
+ # @yieldreturn [Number] Less then 0 if what < with, equal to 0 if what = with and bigger than 0 if what > with
430
+ def max(&block)
431
+ block = lambda { |a, b| a <=> b } if block == nil
432
+ ret = nil
433
+ first = true
434
+ foreach do |i|
435
+ if first
436
+ ret = i
437
+ first = false
438
+ elsif block.call(i, ret) > 0
439
+ ret = i
440
+ end
441
+ end
442
+ return ret
443
+ end
444
+
445
+ # Obtains item with the first minimal value
446
+ # @overload min()
447
+ # Compares items using spaceship operator (<=>)
448
+ # @return [Object] Item with first minimal value; nil if collection is empty
449
+ # @overload min()
450
+ # Compares items using given comparison function
451
+ # @return [Object] Item with first minimal value; nil if collection is empty
452
+ # @yield [what, with] Comparison function
453
+ # @yieldparam what [Object] Object that is being compared
454
+ # @yieldparam with [Object] Object that is being compared to
455
+ # @yieldreturn [Number] Less then 0 if what < with, equal to 0 if what = with and bigger than 0 if what > with
456
+ def min(&block)
457
+ block = lambda { |a, b| a <=> b } if block == nil
458
+ ret = nil
459
+ first = true
460
+ foreach do |i|
461
+ if first
462
+ ret = i
463
+ first = false
464
+ elsif block.call(i, ret) < 0
465
+ ret = i
466
+ end
467
+ end
468
+ return ret
469
+ end
470
+
471
+ # Coverts collection to array
472
+ # @return [Array] Array equivalent to collection
473
+ def to_array
474
+ ret = []
475
+ foreach { |item| ret.push(item) }
476
+ return ret
477
+ end
478
+
479
+ # Merges two arrays in order using comparison function
480
+ # @param a [Array] First array
481
+ # @param b [Array] Second array
482
+ # @return [Array] Merged ordered array
483
+ def merge(a, b, &comp)
484
+ ret = []
485
+ while a.length > 0 or b.length > 0
486
+ if a.length > 0 and b.length > 0
487
+ c = comp.call(a[0], b[0])
488
+ if c <= 0
489
+ ret.push(a.shift)
490
+ else
491
+ ret.push(b.shift)
492
+ end
493
+ else
494
+ ret.push(a.length > 0 ? a.shift : b.shift)
495
+ end
496
+ end
497
+ return ret
498
+ end
499
+
500
+ # Indicates whether collection can be altered
501
+ # @return [Bool] True if collection can be altered; otherwise false
502
+ def can_alter?
503
+ @locks == 0 and not @alter
504
+ end
505
+
506
+ def lock(write = false)
507
+ if write
508
+ raise(KXI::Exceptions::CollectionException.new('Collection cannot be locked for writing!')) unless can_alter?
509
+ @alter = true
510
+ yield
511
+ else
512
+ raise(KXI::Exceptions::CollectionException.new('Collection cannot be locked for reading!')) if @alter
513
+ @locks += 1
514
+ yield
515
+ end
516
+ ensure
517
+ if write
518
+ @alter = false
519
+ else
520
+ @locks -= 1
521
+ end
522
+ end
523
+
524
+ protected :lock, :create_enumerator
525
+ private :merge
526
+ end
527
+ end
528
528
  end