kmat 0.0.3
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 +7 -0
- data/.gitattributes +3 -0
- data/.gitignore +15 -0
- data/CHANGELOG.md +15 -0
- data/Gemfile +4 -0
- data/LICENSE.md +675 -0
- data/README.md +224 -0
- data/Rakefile +26 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/ext/kmat/arith/binary.c +1121 -0
- data/ext/kmat/arith/logical.c +332 -0
- data/ext/kmat/arith/math.c +34 -0
- data/ext/kmat/arith/statistics.c +173 -0
- data/ext/kmat/arith/unary.c +165 -0
- data/ext/kmat/auto_collect.rb +118 -0
- data/ext/kmat/elementwise_function.rb +149 -0
- data/ext/kmat/extconf.rb +75 -0
- data/ext/kmat/id.txt +80 -0
- data/ext/kmat/id_sym.rb +40 -0
- data/ext/kmat/km_util.h +97 -0
- data/ext/kmat/kmat.h +96 -0
- data/ext/kmat/lapack_headers/blas.h +354 -0
- data/ext/kmat/lapack_headers/lapacke.h +19455 -0
- data/ext/kmat/lapack_headers/lapacke_config.h +119 -0
- data/ext/kmat/lapack_headers/lapacke_mangling.h +17 -0
- data/ext/kmat/lapack_headers/lapacke_utils.h +579 -0
- data/ext/kmat/linalg/dla.c +1629 -0
- data/ext/kmat/linalg/linalg.c +267 -0
- data/ext/kmat/linalg/norm.c +727 -0
- data/ext/kmat/linalg/vla.c +102 -0
- data/ext/kmat/linalg/working.c +240 -0
- data/ext/kmat/main.c +95 -0
- data/ext/kmat/smat/accessor.c +719 -0
- data/ext/kmat/smat/array.c +108 -0
- data/ext/kmat/smat/boxmuller.c +72 -0
- data/ext/kmat/smat/constructer.c +302 -0
- data/ext/kmat/smat/convert.c +375 -0
- data/ext/kmat/smat/elem.c +171 -0
- data/ext/kmat/smat/fund.c +702 -0
- data/ext/kmat/smat/share.c +427 -0
- data/ext/kmat/smat/smat.c +530 -0
- data/ext/kmat/smat/sort.c +1156 -0
- data/ext/kmat/sym.txt +34 -0
- data/kmat.gemspec +46 -0
- data/lib/kmat.rb +20 -0
- data/lib/kmat/accessor.rb +164 -0
- data/lib/kmat/arith.rb +189 -0
- data/lib/kmat/linalg.rb +279 -0
- data/lib/kmat/logical.rb +150 -0
- data/lib/kmat/misc.rb +122 -0
- data/lib/kmat/random.rb +106 -0
- data/lib/kmat/statistics.rb +98 -0
- data/lib/kmat/version.rb +3 -0
- metadata +156 -0
data/lib/kmat/linalg.rb
ADDED
@@ -0,0 +1,279 @@
|
|
1
|
+
class Mat
|
2
|
+
def least_squares!(a, b, tor: nil, weight: nil, transpose: false)
|
3
|
+
if tor
|
4
|
+
raise ArgumentError, "tor and weight must not specify at once" if weight
|
5
|
+
raise ArgumentError, "for conjugate LS, auto-transpose is not available" if transpose
|
6
|
+
_ls_conj(a, b, tor)
|
7
|
+
elsif weight
|
8
|
+
raise ArgumentError, "for weighted LS, auto-transpose is not available" if transpose
|
9
|
+
wls!(a, b, weight)
|
10
|
+
else
|
11
|
+
if transpose
|
12
|
+
tls!(a, b)
|
13
|
+
else
|
14
|
+
_ls(a, b)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
def least_squares(b, tor: nil, weight: nil, transpose: false)
|
19
|
+
if tor
|
20
|
+
raise ArgumentError, "tor and weight must not specify at once" if weight
|
21
|
+
raise ArgumentError, "for conjugate LS, auto-transpose is not available" if transpose
|
22
|
+
ls_conj(b, tor)
|
23
|
+
elsif weight
|
24
|
+
raise ArgumentError, "for weighted LS, auto-transpose is not available" if transpose
|
25
|
+
wls(b, weight)
|
26
|
+
else
|
27
|
+
if transpose
|
28
|
+
tls(b)
|
29
|
+
else
|
30
|
+
n = self.col_size
|
31
|
+
Mat.new(n, n, :float).__send__(:_ls, b)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
alias :ls :least_squares
|
36
|
+
alias :ls! :least_squares!
|
37
|
+
|
38
|
+
def ls_conj!(a, b, tor: Float::EPSILON)
|
39
|
+
_ls_conj(a, b, tor)
|
40
|
+
end
|
41
|
+
def ls_conj(b, tor: Float::EPSILON)
|
42
|
+
Mat.new(self.col_size, 1, :float).ls_conj!(self, b, tor: tor)
|
43
|
+
end
|
44
|
+
|
45
|
+
# weighted least squares
|
46
|
+
# weight `w' is a vector consists of standard deviations of errors
|
47
|
+
# a; (m, n)-matrix
|
48
|
+
# b: m-vector
|
49
|
+
# x: n-vector
|
50
|
+
def wls!(a, b, w)
|
51
|
+
nw = w.length
|
52
|
+
diag = Mat.new(nw, nw, :f)
|
53
|
+
diag.diag.temp do |dd|
|
54
|
+
begin
|
55
|
+
dd.copy_from(w)
|
56
|
+
rescue MismatchedDimensionError
|
57
|
+
dd.tcopy_from(w)
|
58
|
+
end
|
59
|
+
dd.sqrt!
|
60
|
+
end
|
61
|
+
glm!(a, diag, b)
|
62
|
+
end
|
63
|
+
def wls(b, w)
|
64
|
+
Mat.new(self.col_size, 1, :f).wls!(self, b, w)
|
65
|
+
end
|
66
|
+
|
67
|
+
def rand_orth(random: $MatRandom)
|
68
|
+
_rand_orth(random)
|
69
|
+
end
|
70
|
+
def self.rand_orth(n, random: $MatRandom)
|
71
|
+
Mat.new(n, n, :float).rand_orth(random: random)
|
72
|
+
end
|
73
|
+
|
74
|
+
def under(other)
|
75
|
+
if square?
|
76
|
+
begin
|
77
|
+
solve(other)
|
78
|
+
rescue UncomputableMatrixError
|
79
|
+
ls(other)
|
80
|
+
end
|
81
|
+
else
|
82
|
+
ls(other)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
def under!(a, b)
|
86
|
+
if a.square?
|
87
|
+
begin
|
88
|
+
solve!(a, b)
|
89
|
+
rescue UncomputableMatrixError
|
90
|
+
ls!(a, b)
|
91
|
+
end
|
92
|
+
else
|
93
|
+
ls!(a, b)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
def over(other)
|
97
|
+
if other.square?
|
98
|
+
begin
|
99
|
+
other.tsolve(self).t!
|
100
|
+
rescue UncomputableMatrixError
|
101
|
+
other.tls(self).t!
|
102
|
+
end
|
103
|
+
else
|
104
|
+
other.tls(self).t!
|
105
|
+
end
|
106
|
+
end
|
107
|
+
def over!(a, b)
|
108
|
+
if b.square?
|
109
|
+
begin
|
110
|
+
tsolve!(b, a).t!
|
111
|
+
rescue UncomputableMatrixError
|
112
|
+
tls!(b, a).t!
|
113
|
+
end
|
114
|
+
else
|
115
|
+
tls!(b, a).t!
|
116
|
+
end
|
117
|
+
end
|
118
|
+
def pinv
|
119
|
+
n = self.size.min
|
120
|
+
i = Mat.I(n)
|
121
|
+
if square?
|
122
|
+
begin
|
123
|
+
solve(i)
|
124
|
+
rescue UncomputableMatrixError
|
125
|
+
ls(i)
|
126
|
+
end
|
127
|
+
else
|
128
|
+
ls(i)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
def eigen_values
|
132
|
+
if symmetry?
|
133
|
+
symmetrize().sym_eigen_values()
|
134
|
+
else
|
135
|
+
ge_eigen_values()
|
136
|
+
end
|
137
|
+
end
|
138
|
+
def evd
|
139
|
+
if symmetry?
|
140
|
+
symmetrize().sym_evd()
|
141
|
+
else
|
142
|
+
ge_evd()
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
# inner product
|
147
|
+
# let x and y are column vectors and M is a square matrix
|
148
|
+
# the following is available
|
149
|
+
# (1) x.iprod => x'*x
|
150
|
+
# (2) x.iprod(y) => x'*y
|
151
|
+
# (3) M.iprod(x) => x'*M*x
|
152
|
+
# (4) M.iprod(x, y) => x'*M*y
|
153
|
+
# if `inverse' is true, M is replaced by its Moore–Penrose inverse
|
154
|
+
# in case (1) or (2), `inverse' is ignored
|
155
|
+
# if x or y is row vector, take transpose automatically
|
156
|
+
def iprod(*args, inverse: false)
|
157
|
+
case args.size
|
158
|
+
when 0
|
159
|
+
if vector? # case 1
|
160
|
+
_iprod(self)
|
161
|
+
else
|
162
|
+
raise MismatchedDimensionError, "self must be a vector"
|
163
|
+
end
|
164
|
+
when 1
|
165
|
+
o = args[0]
|
166
|
+
if o.vector?
|
167
|
+
if vector? # case 2
|
168
|
+
_iprod(o)
|
169
|
+
else # case 3
|
170
|
+
if !square?
|
171
|
+
raise MismatchecDimensionError, "M must be square for M.iprod(x)"
|
172
|
+
elsif inverse
|
173
|
+
temp = Mat.new(row_size, 1, vtype)
|
174
|
+
if o.col_size == 1
|
175
|
+
begin
|
176
|
+
temp.solve!(self, o)
|
177
|
+
rescue UncomputableMatrixError
|
178
|
+
temp.ls!(self, o)
|
179
|
+
end
|
180
|
+
else
|
181
|
+
begin
|
182
|
+
temp.tsolve!(self, o)
|
183
|
+
rescue UncomputableMatrixError
|
184
|
+
temp.tls!(self, o)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
else
|
188
|
+
temp = Mat.new(row_size, 1, vtype)
|
189
|
+
if o.col_size == 1
|
190
|
+
temp.mprod!(self, o)
|
191
|
+
else
|
192
|
+
temp.mprod!(o, self)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
temp.__send__(:_iprod, o)
|
196
|
+
end
|
197
|
+
else
|
198
|
+
raise MismatchedDimensionError, "as iprod with single argument, x.iprod(y) and M.iprod(x) are available, not x.iprod(M) or others"
|
199
|
+
end
|
200
|
+
when 2 # case 4
|
201
|
+
x, y = *args
|
202
|
+
if !square? || !x.vector? || !y.vector? || x.length != row_size || y.length != row_size
|
203
|
+
raise MismatchecDimensionError, "as iprod with 2 arguments, only M.iprod(x, y) is available, not others"
|
204
|
+
end
|
205
|
+
temp = Mat(row_size, 1, vtype)
|
206
|
+
if y.col_size == 1
|
207
|
+
temp.mprod!(y)
|
208
|
+
else
|
209
|
+
if y.frozen?
|
210
|
+
y = y.dup
|
211
|
+
y.transpose
|
212
|
+
temp.mprod!(y)
|
213
|
+
else
|
214
|
+
begin
|
215
|
+
y.transpose
|
216
|
+
temp.mprod!(y)
|
217
|
+
ensure
|
218
|
+
y.transpose
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
x.__send__(:_iprod, temp)
|
223
|
+
else
|
224
|
+
raise ArgumentError, "wrong number of arguments (given #{args.size}, expected 0..2)"
|
225
|
+
end
|
226
|
+
end
|
227
|
+
alias :inner_product :iprod
|
228
|
+
|
229
|
+
# distance (Frobenius norm of the difference)
|
230
|
+
def distance(other)
|
231
|
+
self.dup.sub!(other).normf
|
232
|
+
end
|
233
|
+
def squared_distance(other)
|
234
|
+
self.dup.sub!(other).iprod
|
235
|
+
end
|
236
|
+
|
237
|
+
# matrix norm
|
238
|
+
# `type' Symbol or Integer specifies norm definition
|
239
|
+
# if `elementwise' is true, return induced norm
|
240
|
+
# if `elementwise' is false, return elementwise norm
|
241
|
+
# if `type' is :fro, return Frobenius norm
|
242
|
+
def norm(type=:two, elementwise: false)
|
243
|
+
if elementwise
|
244
|
+
case type
|
245
|
+
when :one, :o, 1
|
246
|
+
norm_e1()
|
247
|
+
when :infinity, :i, :inf, :infty, Float::INFINITY
|
248
|
+
norm_einf()
|
249
|
+
when :two, :t, 2, :frobenius, :f, :fro
|
250
|
+
normf()
|
251
|
+
end
|
252
|
+
else
|
253
|
+
case type
|
254
|
+
when :one, :o, 1
|
255
|
+
norm1()
|
256
|
+
when :infinity, :i, :inf, :infty, Float::INFINITY
|
257
|
+
normi()
|
258
|
+
when :two, :t, 2
|
259
|
+
norm2()
|
260
|
+
when :frobenius, :f, :fro
|
261
|
+
normf()
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
def rcond(type=:two)
|
267
|
+
case type
|
268
|
+
when :one, :o, 1
|
269
|
+
_rcondoi(:one)
|
270
|
+
when :infinity, :i, :inf, :infty, Float::INFINITY
|
271
|
+
_rcondoi(:infinity)
|
272
|
+
when :two, :t, 2
|
273
|
+
_rcond2()
|
274
|
+
when :frobenius, :f, :fro
|
275
|
+
_rcondf()
|
276
|
+
end
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
data/lib/kmat/logical.rb
ADDED
@@ -0,0 +1,150 @@
|
|
1
|
+
class Mat
|
2
|
+
def all?(*args)
|
3
|
+
if block_given?
|
4
|
+
each do |ent|
|
5
|
+
return false unless yield(ent, *args)
|
6
|
+
end
|
7
|
+
elsif args.size == 0
|
8
|
+
if self.vtype == :bool
|
9
|
+
each do |ent|
|
10
|
+
return false unless ent
|
11
|
+
end
|
12
|
+
else
|
13
|
+
return enum_for(__method__)
|
14
|
+
end
|
15
|
+
else
|
16
|
+
each do |ent|
|
17
|
+
args.each do |arg|
|
18
|
+
return false unless arg === ent
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
return true
|
23
|
+
end
|
24
|
+
def any?(*args)
|
25
|
+
if block_given?
|
26
|
+
each do |ent|
|
27
|
+
return true if yield(ent, *args)
|
28
|
+
end
|
29
|
+
elsif args.size == 0
|
30
|
+
if self.vtype == :bool
|
31
|
+
each do |ent|
|
32
|
+
return true if ent
|
33
|
+
end
|
34
|
+
else
|
35
|
+
return enum_for(__method__)
|
36
|
+
end
|
37
|
+
else
|
38
|
+
each do |ent|
|
39
|
+
args.each do |arg|
|
40
|
+
return true if arg === ent
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
return false
|
45
|
+
end
|
46
|
+
|
47
|
+
def ==(other)
|
48
|
+
return true if self.equal?(other)
|
49
|
+
return false unless other.kind_of?(Mat)
|
50
|
+
if self.vtype == other.vtype && self.size == other.size
|
51
|
+
each_with_index do |e, i, j|
|
52
|
+
return false unless e == other[i, j]
|
53
|
+
end
|
54
|
+
true
|
55
|
+
else
|
56
|
+
false
|
57
|
+
end
|
58
|
+
end
|
59
|
+
def <(other)
|
60
|
+
return false if self.equal?(other)
|
61
|
+
unless other.kind_of?(Mat)
|
62
|
+
raise ArgumentError, "Mat < #{other.class} is not defined"
|
63
|
+
end
|
64
|
+
if self.vtype != other.vtype
|
65
|
+
raise ValueTypeError, "value types must be the same"
|
66
|
+
elsif self.size != other.size
|
67
|
+
raise MismatchedDimensionError, "the sizes must be the same, (#{self.size.join(', ')}) != (#{other.size.join(', ')})"
|
68
|
+
end
|
69
|
+
each_with_index do |e, i, j|
|
70
|
+
return false unless e < other[i, j]
|
71
|
+
end
|
72
|
+
true
|
73
|
+
end
|
74
|
+
def <=(other)
|
75
|
+
return false if self.equal?(other)
|
76
|
+
unless other.kind_of?(Mat)
|
77
|
+
raise ArgumentError, "Mat <= #{other.class} is not defined"
|
78
|
+
end
|
79
|
+
if self.vtype != other.vtype
|
80
|
+
raise ValueTypeError, "value types must be the same"
|
81
|
+
elsif self.size != other.size
|
82
|
+
raise MismatchedDimensionError, "the sizes must be the same, (#{self.size.join(', ')}) != (#{other.size.join(', ')})"
|
83
|
+
end
|
84
|
+
each_with_index do |e, i, j|
|
85
|
+
return false unless e <= other[i, j]
|
86
|
+
end
|
87
|
+
true
|
88
|
+
end
|
89
|
+
def >(other)
|
90
|
+
return false if self.equal?(other)
|
91
|
+
unless other.kind_of?(Mat)
|
92
|
+
raise ArgumentError, "Mat > #{other.class} is not defined"
|
93
|
+
end
|
94
|
+
if self.vtype != other.vtype
|
95
|
+
raise ValueTypeError, "value types must be the same"
|
96
|
+
elsif self.size != other.size
|
97
|
+
raise MismatchedDimensionError, "the sizes must be the same, (#{self.size.join(', ')}) != (#{other.size.join(', ')})"
|
98
|
+
end
|
99
|
+
each_with_index do |e, i, j|
|
100
|
+
return false unless e > other[i, j]
|
101
|
+
end
|
102
|
+
true
|
103
|
+
end
|
104
|
+
def >=(other)
|
105
|
+
return false if self.equal?(other)
|
106
|
+
unless other.kind_of?(Mat)
|
107
|
+
raise ArgumentError, "Mat > #{other.class} is not defined"
|
108
|
+
end
|
109
|
+
if self.vtype != other.vtype
|
110
|
+
raise ValueTypeError, "value types must be the same"
|
111
|
+
elsif self.size != other.size
|
112
|
+
raise MismatchedDimensionError, "the sizes must be the same, (#{self.size.join(', ')}) != (#{other.size.join(', ')})"
|
113
|
+
end
|
114
|
+
each_with_index do |e, i, j|
|
115
|
+
return false unless e >= other[i, j]
|
116
|
+
end
|
117
|
+
true
|
118
|
+
end
|
119
|
+
|
120
|
+
def eq(other)
|
121
|
+
unless other.kind_of?(Mat) && other.size == self.size
|
122
|
+
other = broadcast(other)
|
123
|
+
end
|
124
|
+
Mat.new(row_size(), col_size(), :bool).eq!(self, other)
|
125
|
+
end
|
126
|
+
def lt(other)
|
127
|
+
unless other.kind_of?(Mat) && other.size == self.size
|
128
|
+
other = broadcast(other)
|
129
|
+
end
|
130
|
+
Mat.new(row_size(), col_size(), :bool).lt!(self, other)
|
131
|
+
end
|
132
|
+
def le(other)
|
133
|
+
unless other.kind_of?(Mat) && other.size == self.size
|
134
|
+
other = broadcast(other)
|
135
|
+
end
|
136
|
+
Mat.new(row_size(), col_size(), :bool).le!(self, other)
|
137
|
+
end
|
138
|
+
def gt(other)
|
139
|
+
unless other.kind_of?(Mat) && other.size == self.size
|
140
|
+
other = broadcast(other)
|
141
|
+
end
|
142
|
+
Mat.new(row_size(), col_size(), :bool).gt!(self, other)
|
143
|
+
end
|
144
|
+
def ge(other)
|
145
|
+
unless other.kind_of?(Mat) && other.size == self.size
|
146
|
+
other = broadcast(other)
|
147
|
+
end
|
148
|
+
Mat.new(row_size(), col_size(), :bool).ge!(self, other)
|
149
|
+
end
|
150
|
+
end
|
data/lib/kmat/misc.rb
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
class << Mat
|
2
|
+
def hilb(n, type=:float)
|
3
|
+
self.new(n, n, type) do |i, j|
|
4
|
+
1.quo(i+j+1)
|
5
|
+
end
|
6
|
+
end
|
7
|
+
def load(file)
|
8
|
+
case file
|
9
|
+
when String
|
10
|
+
ret = nil
|
11
|
+
File.open(file) do |f|
|
12
|
+
ret = Marshal.load(f)
|
13
|
+
end
|
14
|
+
ret
|
15
|
+
when IO
|
16
|
+
Marshal.load(file)
|
17
|
+
else
|
18
|
+
raise ArgumentError, 'the argument must be a filepath or an IO object'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class Mat
|
24
|
+
def save(file)
|
25
|
+
case file
|
26
|
+
when String
|
27
|
+
File.open(file, 'w') do |f|
|
28
|
+
Marshal.dump(self, f)
|
29
|
+
end
|
30
|
+
when IO
|
31
|
+
Marshal.dump(self, file)
|
32
|
+
else
|
33
|
+
raise ArgumentError, 'the argument must be a filepath or an IO object'
|
34
|
+
end
|
35
|
+
nil
|
36
|
+
end
|
37
|
+
|
38
|
+
def geo_normalize!(arg=:all)
|
39
|
+
self.log!
|
40
|
+
foo = self.mean(arg)
|
41
|
+
if foo.kind_of?(Mat)
|
42
|
+
self.sub!(self.broadcast(foo))
|
43
|
+
else
|
44
|
+
self.s_sub!(foo)
|
45
|
+
end
|
46
|
+
self.exp!
|
47
|
+
end
|
48
|
+
def geo_normalize(arg=:all)
|
49
|
+
self.dup.geo_normalize!(arg)
|
50
|
+
end
|
51
|
+
def normalize!
|
52
|
+
if self.vector?
|
53
|
+
self.s_div!(self.normf)
|
54
|
+
elsif self.square?
|
55
|
+
ev = self.eigen_values
|
56
|
+
if ev[0] < 0
|
57
|
+
self.diag.s_add!(ev[0]*(-2))
|
58
|
+
ev.s_add!(ev[0]*(-2))
|
59
|
+
end
|
60
|
+
self.s_div!(Math.exp(ev.log!.mean))
|
61
|
+
else
|
62
|
+
raise MismatchedDimensionError, "normalize is available only for vectors or square matricies"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
def normalize
|
66
|
+
self.dup.normalize!
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
class Array
|
71
|
+
def argsort
|
72
|
+
ret = Array.new(self.size, &:itself)
|
73
|
+
if block_given?
|
74
|
+
ret.sort! do |a, b|
|
75
|
+
yield(self[a], self[b])
|
76
|
+
end
|
77
|
+
else
|
78
|
+
ret.sort_by! do |elm|
|
79
|
+
self[elm]
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
def argsort!(ary)
|
84
|
+
if block_given?
|
85
|
+
sort! do |a, b|
|
86
|
+
yield(ary[a], ary[b])
|
87
|
+
end
|
88
|
+
else
|
89
|
+
sort_by! do |elm|
|
90
|
+
self[elm]
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
def argsort_by
|
95
|
+
if block_given?
|
96
|
+
Array.new(self.size, &:itself).sort_by! do |elm|
|
97
|
+
yield(self[elm])
|
98
|
+
end
|
99
|
+
else
|
100
|
+
self.to_enum(:argsort_by)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
module Enumerable
|
105
|
+
def argsort(&block)
|
106
|
+
self.to_a.argsort(&block)
|
107
|
+
end
|
108
|
+
def argsort_by(&block)
|
109
|
+
self.to_a.argsort_by(&block)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
class Numeric
|
113
|
+
def limit(min, max)
|
114
|
+
if ( self < min ) then
|
115
|
+
min
|
116
|
+
elsif ( max < self ) then
|
117
|
+
max
|
118
|
+
else
|
119
|
+
self
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|