y_support 1.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.
Files changed (58) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +20 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE +22 -0
  5. data/README.md +29 -0
  6. data/Rakefile +2 -0
  7. data/lib/y_support/all.rb +40 -0
  8. data/lib/y_support/core_ext/array/misc.rb +45 -0
  9. data/lib/y_support/core_ext/array.rb +1 -0
  10. data/lib/y_support/core_ext/enumerable/misc.rb +32 -0
  11. data/lib/y_support/core_ext/enumerable.rb +1 -0
  12. data/lib/y_support/core_ext/hash/misc.rb +90 -0
  13. data/lib/y_support/core_ext/hash.rb +1 -0
  14. data/lib/y_support/core_ext/module/misc.rb +43 -0
  15. data/lib/y_support/core_ext/module.rb +2 -0
  16. data/lib/y_support/core_ext/numeric/misc.rb +13 -0
  17. data/lib/y_support/core_ext/numeric.rb +1 -0
  18. data/lib/y_support/core_ext/object/misc.rb +31 -0
  19. data/lib/y_support/core_ext/object.rb +1 -0
  20. data/lib/y_support/core_ext/string/misc.rb +80 -0
  21. data/lib/y_support/core_ext/string.rb +1 -0
  22. data/lib/y_support/core_ext/symbol/misc.rb +19 -0
  23. data/lib/y_support/core_ext/symbol.rb +1 -0
  24. data/lib/y_support/core_ext.rb +5 -0
  25. data/lib/y_support/inert_recorder.rb +51 -0
  26. data/lib/y_support/local_object.rb +39 -0
  27. data/lib/y_support/misc.rb +28 -0
  28. data/lib/y_support/name_magic.rb +373 -0
  29. data/lib/y_support/null_object.rb +96 -0
  30. data/lib/y_support/respond_to.rb +32 -0
  31. data/lib/y_support/stdlib_ext/matrix/misc.rb +134 -0
  32. data/lib/y_support/stdlib_ext/matrix.rb +2 -0
  33. data/lib/y_support/stdlib_ext.rb +3 -0
  34. data/lib/y_support/typing/array/typing.rb +17 -0
  35. data/lib/y_support/typing/array.rb +1 -0
  36. data/lib/y_support/typing/enumerable/typing.rb +75 -0
  37. data/lib/y_support/typing/enumerable.rb +1 -0
  38. data/lib/y_support/typing/hash/typing.rb +76 -0
  39. data/lib/y_support/typing/hash.rb +1 -0
  40. data/lib/y_support/typing/module/typing.rb +42 -0
  41. data/lib/y_support/typing/module.rb +1 -0
  42. data/lib/y_support/typing/object/typing.rb +178 -0
  43. data/lib/y_support/typing/object.rb +1 -0
  44. data/lib/y_support/typing.rb +43 -0
  45. data/lib/y_support/unicode.rb +76 -0
  46. data/lib/y_support/version.rb +3 -0
  47. data/lib/y_support.rb +33 -0
  48. data/test/inert_recorder_test.rb +34 -0
  49. data/test/local_object_test.rb +37 -0
  50. data/test/misc_test/test_module/fixture_class.rb +8 -0
  51. data/test/misc_test.rb +289 -0
  52. data/test/name_magic_test.rb +57 -0
  53. data/test/null_object_test.rb +50 -0
  54. data/test/respond_to_test.rb +46 -0
  55. data/test/typing_test.rb +213 -0
  56. data/test/unicode_test.rb +39 -0
  57. data/y_support.gemspec +22 -0
  58. metadata +137 -0
@@ -0,0 +1,373 @@
1
+ #encoding: utf-8
2
+ require 'y_support'
3
+
4
+ # A mixin imitating Ruby constant magic, plus automation of :name alias :ɴ
5
+ # named argument. This allows to write:
6
+ #
7
+ # <tt>SomeName = SomeClass.new</tt>
8
+ #
9
+ # and the resulting object will know its #name:
10
+ #
11
+ # <tt>SomeName.name = "SomeName"</tt>
12
+ #
13
+ # This is done by searching the whole Ruby namespace for constants to which
14
+ # the object is assigned. The search is performed by calling #const_magic.
15
+ # This is only done until the name is found - once the object is named, its
16
+ # subsequent assignment to constants is without effect.
17
+ #
18
+ # Alternatively, a named object can be created by using :name alias :ɴ
19
+ # named argument:
20
+ #
21
+ # SomeName.new arg1, arg2, ..., name: "SomeName", named_arg1: val1, ...
22
+ #
23
+ # Hook is provided for when the name magic is performed.
24
+ #
25
+ module NameMagic
26
+ DEBUG = false
27
+ PROBLEM_MODULES = [ 'Gem', 'Rack', 'ActiveSupport' ]
28
+
29
+ def self.included target
30
+ case target
31
+ when Class then
32
+ class << target
33
+ # Make space for the decorator #new:
34
+ alias :original_method_new :new
35
+ end
36
+ # Attach the decorators etc.
37
+ target.extend ::NameMagic::ClassMethods
38
+ target.extend ::NameMagic::NamespaceMethods
39
+ # Attach namespace methods to also to the namespace, if given.
40
+ begin
41
+ unless target == target.namespace
42
+ target.namespace.extend ::NameMagic::NamespaceMethods
43
+ end
44
+ rescue NoMethodError
45
+ end
46
+ else # it is a Module; we'll infect it with this #included method
47
+ included_of_the_target = target.method( :included )
48
+ included_of_self = self.method( :included )
49
+ pre_included_of_the_target = begin
50
+ target.method( :pre_included )
51
+ rescue NameError
52
+ end
53
+ if pre_included_of_the_target then
54
+ target.define_singleton_method :included do |ç|
55
+ pre_included_of_the_target.( ç )
56
+ included_of_self.call( ç )
57
+ included_of_the_target.call( ç )
58
+ end
59
+ else
60
+ target.define_singleton_method :included do |ç|
61
+ included_of_self.( ç )
62
+ included_of_the_target.( ç )
63
+ end
64
+ end
65
+ end
66
+ end # self.included
67
+
68
+ # Retrieves an instance name (demodulized).
69
+ #
70
+ def name
71
+ self.class.const_magic
72
+ ɴ = self.class.__instances__[ self ]
73
+ if ɴ then
74
+ name_get_closure = self.class.instance_variable_get :@name_get_closure
75
+ return name_get_closure ? name_get_closure.( ɴ ) : ɴ
76
+ else
77
+ return nil
78
+ end
79
+ end
80
+ alias ɴ name
81
+
82
+ # Names an instance, cautiously (ie. no overwriting of existing names).
83
+ #
84
+ def name=( ɴ )
85
+ # get previous name of this instance, if any
86
+ old_ɴ = self.class.__instances__[ self ]
87
+ # honor the hook
88
+ name_set_closure = self.class.instance_variable_get :@name_set_closure
89
+ ɴ = name_set_closure.call( ɴ, self, old_ɴ ) if name_set_closure
90
+ ɴ = self.class.send( :validate_capitalization, ɴ ).to_sym
91
+ return if old_ɴ == ɴ # already named as required; nothing to do
92
+ # otherwise, be cautious about name collision
93
+ raise NameError, "Name '#{ɴ}' already exists in " +
94
+ "#{self.class} namespace!" if self.class.__instances__.rassoc( ɴ )
95
+ # since everything's ok...
96
+ self.class.namespace.const_set ɴ, self # write a constant
97
+ self.class.__instances__[ self ] = ɴ # write __instances__
98
+ self.class.__forget__ old_ɴ # forget the old name of self
99
+ end
100
+
101
+ # Names an instance, aggresively (overwrites existing names).
102
+ #
103
+ def name!( ɴ )
104
+ # get previous name of this instance, if any
105
+ old_ɴ = self.class.__instances__[ self ]
106
+ # honor the hook
107
+ name_set_closure = self.class.instance_variable_get :@name_set_closure
108
+ ɴ = name_set_closure.( ɴ, self, old_ɴ ) if name_set_closure
109
+ ɴ = self.class.send( :validate_capitalization, ɴ ).to_sym
110
+ return false if old_ɴ == ɴ # already named as required; nothing to do
111
+ # otherwise, rudely remove the collider, if any
112
+ pair = self.class.__instances__.rassoc( ɴ )
113
+ self.class.__forget__( pair[0] ) if pair
114
+ # and add add self to the namespace
115
+ self.class.namespace.const_set ɴ, self # write a constant
116
+ self.class.__instances__[ self ] = ɴ # write to __instances__
117
+ self.class.__forget__ old_ɴ # forget the old name of self
118
+ return true
119
+ end
120
+
121
+ module NamespaceMethods
122
+ # Presents class-owned @instances hash of { instance => name } pairs.
123
+ #
124
+ def instances
125
+ const_magic
126
+ __instances__.keys
127
+ end
128
+
129
+ # Presents an array of all the instance names (disregarding anonymous
130
+ # instances).
131
+ #
132
+ def instance_names
133
+ instances.map( &:name ).compact
134
+ end
135
+
136
+ # Presents class-owned @instances without const_magic.
137
+ #
138
+ def __instances__
139
+ namespace.instance_variable_get( :@instances ) or
140
+ namespace.instance_variable_set( :@instances, {} )
141
+ end
142
+
143
+ # Presents class-owned @avid_instances (no const_magic).
144
+ #
145
+ def __avid_instances__
146
+ namespace.instance_variable_get( :@avid_instances ) or
147
+ namespace.instance_variable_set( :@avid_instances, [] )
148
+ end
149
+
150
+ # Presents class-owned namespace. Normally, this is the class itself,
151
+ # but can be overriden so as to define constants holding the instances
152
+ # in some other module.
153
+ #
154
+ def namespace
155
+ self
156
+ end
157
+
158
+ # Returns the instance of the class using NameMagic, specified by the
159
+ # argument. NameError is raised, if the argument does not represent a valid
160
+ # instance name, or if the argument itself is not a valid instance (in
161
+ # which case it is returned unchanged).
162
+ #
163
+ def instance arg
164
+ const_magic
165
+ # if the argument is an actual instance, just return it
166
+ return arg if __instances__.keys.include? arg
167
+ # otherwise, treat it as name
168
+ r = begin
169
+ __instances__.rassoc( arg ) || __instances__.rassoc( arg.to_sym )
170
+ rescue NoMethodError
171
+ end or
172
+ raise NameError, "No instance #{arg} in #{namespace}."
173
+ return r[0]
174
+ end
175
+
176
+
177
+ # The method will search all the modules in the the object space for
178
+ # receiver class objects assigned to constants, and name these instances
179
+ # accordingly. Number of the remaining nameless instances is returned.
180
+ #
181
+ def const_magic
182
+ return 0 if nameless_instances.size == 0
183
+ serve_all_modules
184
+ return nameless_instances.size
185
+ end # def const_magic
186
+
187
+ # Returns those instances, which are nameless (@instances hash value is nil).
188
+ #
189
+ def nameless_instances
190
+ __instances__.select { |key, val| val.nil? }.keys
191
+ end
192
+
193
+ # Clears class-owned references to a specified instance.
194
+ #
195
+ def forget( which_instance )
196
+ inst = begin
197
+ instance( which_instance )
198
+ rescue ArgumentError
199
+ return nil # nothing to forget
200
+ end
201
+ ɴ = inst.nil? ? nil : inst.name
202
+ namespace.send :remove_const, ɴ if ɴ # clear constant assignment
203
+ __instances__.delete( inst ) # remove @instances entry
204
+ __avid_instances__.delete( inst ) # remove if any
205
+ return inst # return forgotten instance
206
+ end
207
+
208
+ # Clears class-owned references to a specified instance without performing
209
+ # #const_magic first. The argument must be an instance of the target class.
210
+ #
211
+ def __forget__( instance )
212
+ name = __instances__.delete instance # remove @instances entry
213
+ __avid_instances__.delete( instance ) # remove if any
214
+ namespace.send :remove_const, name if name
215
+ return instance
216
+ end
217
+
218
+ # Clears class-owned references anonymous instances.
219
+ #
220
+ def forget_anonymous_instances
221
+ nameless_instances.each { |inst, ɴ|
222
+ __instances__.delete inst
223
+ __avid_instances__.delete inst
224
+ }
225
+ end
226
+ alias :forget_nameless_instances :forget_anonymous_instances
227
+
228
+ # Clears class-owned references to all the instances.
229
+ #
230
+ def forget_all_instances
231
+ __instances__.clear # clears @instances
232
+ constants( false ).each { |ß| # clear constants in the namespace
233
+ namespace.send :remove_const, ß if const_get( ß ).is_a? self
234
+ }
235
+ end
236
+
237
+ # Registers a hook to execute whenever name magic creates a new instance
238
+ # of the class including NameMagic. The block should take one argument
239
+ # (the new instance that was created) and is called in #new method right
240
+ # after instantiation, but before naming.
241
+ #
242
+ def new_instance_closure &block; @new_instance_closure = block end
243
+
244
+ # Registers a hook to execute whenever name setting is performed on an
245
+ # instance. The block should take three arguments (instance, name, old_name).
246
+ # The output value of the block is the name to be actually used – the hook
247
+ # thus allows to define transformations on the name when naming. It is the
248
+ # responsibility of the block to output a suitable symbol (capitalized,
249
+ # usable as a constant name etc.)
250
+ #
251
+ def name_set_closure &block; @name_set_closure = block end
252
+
253
+ # Registers a hook to execute whenever the instance is asked about its
254
+ # name. The name object contained in __instances__[self] is subjected
255
+ # to the name_get_closure before being returned as instance name.
256
+ #
257
+ def name_get_closure &block; @name_get_closure = block end
258
+
259
+ private
260
+
261
+ # Checks all the constants in some module's namespace, recursively.
262
+ #
263
+ def serve_all_modules
264
+ incriminated_ids = ( nameless_instances + __avid_instances__ )
265
+ .map( &:object_id ).uniq
266
+ ObjectSpace.each_object Module do |ɱ|
267
+ # hack against bugs when getting constants from URI
268
+ next if ::NameMagic::PROBLEM_MODULES.any? { |problem_ς|
269
+ begin
270
+ ɱ.name.to_s.start_with? problem_ς
271
+ rescue NoMethodError, ArgumentError
272
+ end
273
+ }
274
+ puts ɱ if ::NameMagic::DEBUG
275
+ # check all the module constants:
276
+ ɱ.constants( false ).each do |const_ß|
277
+ begin # insurance against buggy dynamic loading of constants
278
+ ◉ = ɱ.const_get( const_ß )
279
+ rescue
280
+ next
281
+ end
282
+ # is it a wanted object?
283
+ if incriminated_ids.include? ◉.object_id then
284
+ if __avid_instances__.map( &:object_id ).include? ◉.object_id then
285
+ # name avidly
286
+ __avid_instances__.delete_if { |instance| # make not avid first
287
+ instance.object_id == ◉.object_id
288
+ }
289
+ ◉.name! const_ß # and then name it rudely
290
+ else # name this anonymous instance cautiously
291
+ # honor name_set_closure
292
+ ɴ = if @name_set_closure then
293
+ @name_set_closure.( const_ß, ◉, nil )
294
+ else const_ß end
295
+ ɴ = validate_capitalization( ɴ ).to_sym
296
+ conflicter = begin # be cautious
297
+ namespace.const_get( ɴ )
298
+ rescue NameError
299
+ end
300
+ if conflicter then
301
+ raise NameError, "Another #{self} named '#{ɴ}' already " +
302
+ "exists!" unless conflicter == ◉
303
+ else
304
+ # add the instance to the namespace
305
+ __instances__[ ◉ ] = ɴ
306
+ namespace.const_set ɴ, ◉
307
+ end
308
+ end
309
+ # and stop working in case there are no more unnamed instances
310
+ incriminated_ids.delete ◉.object_id
311
+ break if incriminated_ids.empty?
312
+ end
313
+ end # each
314
+ end # each_object Module
315
+ end # def serve_all_modules
316
+
317
+ # Checks whether a name starts with a capital letter.
318
+ #
319
+ def validate_capitalization( name )
320
+ ɴ = name.to_s
321
+ # check whether the name starts with 'A'..'Z'
322
+ raise NameError, "#{self.class} name must start with a capital " +
323
+ " letter 'A'..'Z' ('#{ɴ}' was given)!" unless ( ?A..?Z ) === ɴ[0]
324
+ return ɴ
325
+ end
326
+ end
327
+
328
+ module ClassMethods
329
+ # In addition to its ability to assign name to the target instance when
330
+ # the instance is assigned to a constant (aka. constant magic), NameMagic
331
+ # redefines #new class method to consume named parameter :name, alias :ɴ,
332
+ # thus providing another option for naming of the target instance.
333
+ #
334
+ def new *args, &block
335
+ oo = args[-1].is_a?( Hash ) ? args.pop : {} # extract hash
336
+ # consume :name named argument if it was supplied
337
+ ɴß = if oo[:name] then oo.delete :name
338
+ elsif oo[:ɴ] then oo.delete :ɴ
339
+ else nil end
340
+ # Expecting true/false, if :name_avid option is given
341
+ avid = oo[:name_avid] ? oo.delete( :name_avid ) : false
342
+ # Avoid name collisions unless avid
343
+ raise NameError, "#{self} instance #{ɴß} already exists!" if
344
+ __instances__.keys.include? ɴß unless avid
345
+ # instantiate
346
+ args << oo unless oo.empty? # fuse hash
347
+ new_inst = original_method_new *args, &block
348
+ # treat the instance as unnamed at first
349
+ __instances__.merge! new_inst => nil
350
+ # honor the hook
351
+ @new_instance_closure.call( new_inst ) if @new_instance_closure
352
+ # and then name it if name was supplied, or make it avid
353
+ # (avid instances will steal names from their competitors)
354
+ if ɴß then
355
+ if avid then new_inst.name! ɴß else new_inst.name = ɴß end
356
+ else
357
+ __avid_instances__ << new_inst
358
+ end
359
+ # return the new instance
360
+ return new_inst
361
+ end
362
+
363
+ # Compared to #new method, #new! uses avid mode: without
364
+ # concerns about overwriting existing named instances.
365
+ #
366
+ def new! *args, &block
367
+ # extract options
368
+ if args[-1].is_a? Hash then oo = args.pop else oo = {} end
369
+ # and call #new with added name_avid: true
370
+ new *args, oo.merge!( name_avid: true )
371
+ end
372
+ end # module ClassMethods
373
+ end # module NameMagic
@@ -0,0 +1,96 @@
1
+ #encoding: utf-8
2
+
3
+ require 'y_support'
4
+
5
+ # Null object pattern implementation in YSupport. apart from the expected null
6
+ # object behavior (such as returning self in response to almost all messages),
7
+ # this null object instances can carry a signature specified by the user upon
8
+ # creation, which can serve to hint the origin of the null object. (This
9
+ # signature is opional, default is <tt>nil</tt>.)
10
+ #
11
+ class NullObject
12
+ attr_reader :null_object_signature
13
+
14
+ # Signature can be given as an optional argument upon initialization.
15
+ #
16
+ def initialize null_object_signature=nil
17
+ @null_object_signature = null_object_signature
18
+ end
19
+
20
+ # Inquirer whether an object is a NullObject. Again, optional signature
21
+ # argument can be given to distinguish between different null objects.
22
+ #
23
+ def null_object? signature=nil
24
+ null_object_signature == signature
25
+ end
26
+ alias null? null_object?
27
+
28
+ # Empty array.
29
+ #
30
+ def to_a; [] end
31
+
32
+ # Description string.
33
+ #
34
+ def to_s
35
+ sgn = null_object_signature
36
+ sgn.nil? ? "#<NullObject>" : "#<NullObject #{sgn}>"
37
+ end
38
+
39
+ # Inspection string.
40
+ #
41
+ def inspect; to_s end
42
+
43
+ # Float zero.
44
+ #
45
+ def to_f; 0.0 end
46
+
47
+ # Integer zero.
48
+ #
49
+ def to_i; 0 end
50
+
51
+ # Always false.
52
+ #
53
+ def present?; false end
54
+
55
+ # Always true.
56
+ #
57
+ def empty?; true end
58
+
59
+ # Always true.
60
+ #
61
+ def blank?; true end
62
+
63
+ # True if and only if the other object is a NullObject with same signature.
64
+ #
65
+ def == other
66
+ other.is_a?( self.class ) &&
67
+ other.null_object_signature == null_object_signature
68
+ end
69
+
70
+ def method_missing ß, *args, &block # :nodoc:
71
+ self
72
+ end
73
+
74
+ def respond_to? ß, *args, &block # :nodoc:
75
+ true
76
+ end
77
+ end # class nullobject
78
+
79
+
80
+ class Object
81
+ # Always false for ordinary objects, overriden in NullObject instances.
82
+ #
83
+ def null_object? signature=nil; false end
84
+ alias :null? :null_object?
85
+
86
+ # Converts #nil?-positive objects to a NullObject. Second optional argument
87
+ # specifies the signature of the null object to be created.
88
+ #
89
+ def Maybe object, null_object_signature=nil
90
+ object.nil? ? NullObject.new( null_object_signature ) : object
91
+ end
92
+
93
+ # NullObject constructor.
94
+ #
95
+ def Null( signature=nil ); NullObject.new signature end
96
+ end
@@ -0,0 +1,32 @@
1
+ #encoding: utf-8
2
+
3
+ require 'y_support'
4
+
5
+ # RespondTo class for easy use of respond_to? in case statements.
6
+ #
7
+ class RespondTo
8
+ Matchers = {}
9
+ attr_reader :method
10
+ def self.create method; Matchers[ method ] ||= new method end
11
+ def initialize method; @method = method end
12
+ def === obj; obj.respond_to? method end
13
+ end
14
+
15
+
16
+ class Object
17
+ # RespondTo constructor.
18
+ #
19
+ def RespondTo method; RespondTo.create method end
20
+ end
21
+
22
+
23
+ class Symbol
24
+ # Creates a RespondTo object from the receiver symbol. Intended use for this
25
+ # is nin case statements: RespondTo has customized #=== method, that calls
26
+ # #respond_to? to determine the return value.
27
+ #
28
+ # For example, <tt>when ~:each</tt> in a case statement is valid only if the
29
+ # tested object respond_to?( :each ) returns true.
30
+ #
31
+ def ~@; RespondTo self end
32
+ end
@@ -0,0 +1,134 @@
1
+ #encoding: utf-8
2
+
3
+ require 'matrix'
4
+
5
+ class Matrix
6
+ # Pretty inspect
7
+ def pretty_inspect
8
+ return inspect if row_size == 0 or column_size == 0
9
+ aa = send(:rows).each.with_object [] do |row, memo|
10
+ memo << row.map{ |o|
11
+ os = o.to_s
12
+ case o
13
+ when Numeric then os[0] == '-' ? os : ' ' + os
14
+ else o.to_s end
15
+ }
16
+ end
17
+ width = aa.map{ |row| row.map( &:size ).max }.max + 1
18
+ aa.each_with_object "" do |row, memo|
19
+ row.each{ |e| memo << e << ' ' * ( width - e.size ) }
20
+ memo << "\n"
21
+ end
22
+ end
23
+
24
+ # Pretty print
25
+ def pretty_print
26
+ print pretty_inspect
27
+ return nil
28
+ end
29
+ alias :pp :pretty_print
30
+
31
+ # Given two arrays, creates correspondence matrix, with no. of cols
32
+ # equal to the 1st array, and no. of rows to the 2nd. This matrix can
33
+ # be used eg. for conversion between column vectors corresponding to
34
+ # the 1st and 2nd array:
35
+ #
36
+ # Matrix.correspondence_matrix( array1, array2 ) * col_vector_1
37
+ # #=> col_vector_2
38
+ #
39
+ def self.correspondence_matrix( array1, array2 )
40
+ return Matrix.empty 0, array1.size if array2.empty?
41
+ return Matrix.empty array2.size, 0 if array1.empty?
42
+ self[ *array2.map { |e2| array1.map { |e1| e1 == e2 ? 1 : 0 } } ] # FIXME: Ordinary zero
43
+ end
44
+
45
+ # Converts a column into array. If argument is given, it chooses
46
+ # column number, otherwise column 0 is assumed.
47
+ #
48
+ def column_to_a n=0; ( col = column( n ) ) ? col.to_a : nil end
49
+
50
+ # Converts a row into array. If argument is given, it chooses row
51
+ # number, otherwise row 0 is assumed.
52
+ #
53
+ def row_to_a n=0; ( r = row( n ) ) ? r.to_a : nil end
54
+
55
+ # Shorter aliases for #row_vector, #column_vector
56
+ #
57
+ def self.cv *aa, &b; column_vector *aa, &b end
58
+ def self.rv *aa, &b; row_vector *aa, &b end
59
+
60
+ #join_bottom method
61
+ #
62
+ def join_bottom other;
63
+ raise ArgumentError, "Column size mismatch" unless
64
+ column_size == other.column_size
65
+ return other.map { |e| e } if row_size == 0
66
+ return Matrix.empty row_size + other.row_size, 0 if column_size == 0
67
+ self.class[ *( row_vectors + other.row_vectors ) ]
68
+ end
69
+
70
+ #join_right methods
71
+ #
72
+ def join_right other;
73
+ raise ArgumentError, "Row size mismatch" unless row_size == other.row_size
74
+ ( t.join_bottom( other.t ) ).t
75
+ end
76
+
77
+ # aliasing #row_size, #column_size
78
+ alias :number_of_rows :row_size
79
+ alias :number_of_columns :column_size
80
+ alias :height :number_of_rows
81
+ alias :width :number_of_columns
82
+
83
+ #
84
+ # Creates a empty matrix of +row_size+ x +column_size+.
85
+ # At least one of +row_size+ or +column_size+ must be 0.
86
+ #
87
+ # m = Matrix.empty(2, 0)
88
+ # m == Matrix[ [], [] ]
89
+ # => true
90
+ # n = Matrix.empty(0, 3)
91
+ # n == Matrix.columns([ [], [], [] ])
92
+ # => true
93
+ # m * n
94
+ # => Matrix[[0, 0, 0], [0, 0, 0]]
95
+ #
96
+ def Matrix.empty(row_size = 0, column_size = 0)
97
+ Matrix.Raise ArgumentError, "One size must be 0" if column_size != 0 && row_size != 0
98
+ Matrix.Raise ArgumentError, "Negative size" if column_size < 0 || row_size < 0
99
+
100
+ new([[]]*row_size, column_size)
101
+ end
102
+
103
+ #
104
+ # Creates a matrix of size +row_size+ x +column_size+.
105
+ # It fills the values by calling the given block,
106
+ # passing the current row and column.
107
+ # Returns an enumerator if no block is given.
108
+ #
109
+ # m = Matrix.build(2, 4) {|row, col| col - row }
110
+ # => Matrix[[0, 1, 2, 3], [-1, 0, 1, 2]]
111
+ # m = Matrix.build(3) { rand }
112
+ # => a 3x3 matrix with random elements
113
+ #
114
+ def Matrix.build(row_size, column_size = row_size)
115
+ row_size = CoercionHelper.coerce_to_int(row_size)
116
+ column_size = CoercionHelper.coerce_to_int(column_size)
117
+ raise ArgumentError if row_size < 0 || column_size < 0
118
+ return to_enum :build, row_size, column_size unless block_given?
119
+ rows = Array.new(row_size) do |i|
120
+ Array.new(column_size) do |j|
121
+ yield i, j
122
+ end
123
+ end
124
+ new rows, column_size
125
+ end
126
+ end
127
+
128
+ class Vector
129
+ # .zero class method returns a vector filled with zeros
130
+ #
131
+ def zero( vector_size )
132
+ self[*([0] * vector_size)] # FIXME: Ordinary zero
133
+ end
134
+ end
@@ -0,0 +1,2 @@
1
+ require 'y_support'
2
+ require 'y_support/stdlib_ext/matrix/misc'
@@ -0,0 +1,3 @@
1
+ Dir["#{File.dirname( __FILE__ )}/stdlib_ext/*.rb"].sort.each do |path|
2
+ require "y_support/stdlib_ext/#{File.basename( path, '.rb' )}"
3
+ end
@@ -0,0 +1,17 @@
1
+ #encoding: utf-8
2
+ class Array
3
+ # === Duck typing support (aka. runtime assertions)
4
+
5
+ # This method takes a block and fails with TypeError, if the receiver array
6
+ # fails to include the specified element. An optional argument customizes the
7
+ # error message (element description).
8
+ #
9
+ def aT_includes element, what_is_element=nil
10
+ e = what_is_element ? what_is_element.to_s.capitalize :
11
+ "Element (#{element.class} instance)"
12
+ m = "#{e} is absent from the array."
13
+ raise TErr, m unless include? element
14
+ return self
15
+ end
16
+ alias :aT_include :aT_includes
17
+ end
@@ -0,0 +1 @@
1
+ require 'y_support/core_ext/array/typing'