ruby-nuggets 0.6.9 → 0.7.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.
- data/.rspec +1 -0
- data/README +1 -1
- data/Rakefile +4 -4
- data/lib/nuggets/array/correlation.rb +5 -0
- data/lib/nuggets/array/correlation_mixin.rb +62 -0
- data/lib/nuggets/array/histogram.rb +5 -0
- data/lib/nuggets/array/histogram_mixin.rb +155 -0
- data/lib/nuggets/array/mean.rb +5 -0
- data/lib/nuggets/array/mean_mixin.rb +139 -0
- data/lib/nuggets/array/median.rb +5 -0
- data/lib/nuggets/array/median_mixin.rb +74 -0
- data/lib/nuggets/array/mode.rb +5 -0
- data/lib/nuggets/array/mode_mixin.rb +70 -0
- data/lib/nuggets/array/monotone.rb +5 -5
- data/lib/nuggets/array/only.rb +3 -2
- data/lib/nuggets/array/regression.rb +5 -0
- data/lib/nuggets/array/regression_mixin.rb +63 -0
- data/lib/nuggets/array/runiq_mixin.rb +1 -1
- data/lib/nuggets/array/standard_deviation_mixin.rb +5 -8
- data/lib/nuggets/array/variance_mixin.rb +30 -11
- data/lib/nuggets/enumerable/all_any_extended.rb +6 -6
- data/lib/nuggets/enumerable/minmax.rb +2 -2
- data/lib/nuggets/env/set_mixin.rb +4 -4
- data/lib/nuggets/env/user_home_mixin.rb +1 -1
- data/lib/nuggets/file/replace_mixin.rb +3 -3
- data/lib/nuggets/file/sub_mixin.rb +2 -2
- data/lib/nuggets/file/which_mixin.rb +15 -14
- data/lib/nuggets/hash/nest_mixin.rb +3 -3
- data/lib/nuggets/hash/only.rb +6 -4
- data/lib/nuggets/hash/unroll_mixin.rb +1 -1
- data/lib/nuggets/io/modes.rb +12 -12
- data/lib/nuggets/numeric/duration.rb +3 -3
- data/lib/nuggets/object/blank_mixin.rb +6 -6
- data/lib/nuggets/object/boolean_mixin.rb +3 -3
- data/lib/nuggets/object/singleton_class_mixin.rb +3 -3
- data/lib/nuggets/range/quantile_mixin.rb +1 -1
- data/lib/nuggets/statistics.rb +11 -0
- data/lib/nuggets/statistics_mixins.rb +11 -0
- data/lib/nuggets/string/case.rb +4 -4
- data/lib/nuggets/string/msub.rb +1 -1
- data/lib/nuggets/string/nsub.rb +2 -2
- data/lib/nuggets/string/sub_with_md.rb +2 -2
- data/lib/nuggets/tempfile/open.rb +1 -1
- data/lib/nuggets/uri/content_type_mixin.rb +1 -1
- data/lib/nuggets/uri/exist_mixin.rb +2 -2
- data/lib/nuggets/util/content_type.rb +1 -1
- data/lib/nuggets/util/i18n.rb +63 -63
- data/lib/nuggets/util/ruby.rb +7 -7
- data/lib/nuggets/version.rb +2 -2
- data/spec/nuggets/array/mean_spec.rb +127 -0
- data/spec/nuggets/array/median_spec.rb +79 -0
- data/spec/nuggets/array/mode_spec.rb +59 -0
- data/spec/nuggets/string/evaluate_spec.rb +2 -2
- metadata +29 -15
@@ -0,0 +1,70 @@
|
|
1
|
+
#--
|
2
|
+
###############################################################################
|
3
|
+
# #
|
4
|
+
# A component of ruby-nuggets, some extensions to the Ruby programming #
|
5
|
+
# language. #
|
6
|
+
# #
|
7
|
+
# Copyright (C) 2007-2011 Jens Wille #
|
8
|
+
# #
|
9
|
+
# Authors: #
|
10
|
+
# Jens Wille <jens.wille@uni-koeln.de> #
|
11
|
+
# #
|
12
|
+
# ruby-nuggets is free software; you can redistribute it and/or modify it #
|
13
|
+
# under the terms of the GNU General Public License as published by the Free #
|
14
|
+
# Software Foundation; either version 3 of the License, or (at your option) #
|
15
|
+
# any later version. #
|
16
|
+
# #
|
17
|
+
# ruby-nuggets is distributed in the hope that it will be useful, but WITHOUT #
|
18
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
19
|
+
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
20
|
+
# more details. #
|
21
|
+
# #
|
22
|
+
# You should have received a copy of the GNU General Public License along #
|
23
|
+
# with ruby-nuggets. If not, see <http://www.gnu.org/licenses/>. #
|
24
|
+
# #
|
25
|
+
###############################################################################
|
26
|
+
#++
|
27
|
+
|
28
|
+
require 'nuggets/array/histogram_mixin'
|
29
|
+
|
30
|
+
module Nuggets
|
31
|
+
class Array
|
32
|
+
module ModeMixin
|
33
|
+
|
34
|
+
def self.included(base)
|
35
|
+
base.send :include, Nuggets::Array::HistogramMixin
|
36
|
+
end
|
37
|
+
|
38
|
+
# call-seq:
|
39
|
+
# array.mode => anObject
|
40
|
+
# array.mode(+true+) => anArray
|
41
|
+
#
|
42
|
+
# Returns the mode[http://en.wikipedia.org/wiki/Mode_%28statistics%29] of
|
43
|
+
# the values in _array_ (via #histogram).
|
44
|
+
#
|
45
|
+
# If parameter +true+ is passed, an Array of all modes is returned.
|
46
|
+
def mode(all = false, &block)
|
47
|
+
hist, modes = histogram(&block), []
|
48
|
+
freq = hist.values.max
|
49
|
+
|
50
|
+
hist.each { |key, value|
|
51
|
+
if value == freq
|
52
|
+
modes << key
|
53
|
+
break unless all
|
54
|
+
end
|
55
|
+
}
|
56
|
+
|
57
|
+
all ? modes : modes.first
|
58
|
+
end
|
59
|
+
|
60
|
+
# call-seq:
|
61
|
+
# array.modes => anArray
|
62
|
+
#
|
63
|
+
# Returns an Array of all modes of the values in _array_ (see #mode).
|
64
|
+
def modes(&block)
|
65
|
+
mode(true, &block)
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -28,7 +28,7 @@
|
|
28
28
|
class Array
|
29
29
|
|
30
30
|
# call-seq:
|
31
|
-
# array.monotone?(operator) => true or false
|
31
|
+
# array.monotone?(operator) => +true+ or +false+
|
32
32
|
#
|
33
33
|
# Check whether _array_ is monotone according to +operator+.
|
34
34
|
def monotone?(operator)
|
@@ -42,7 +42,7 @@ class Array
|
|
42
42
|
alias_method :monotonic?, :monotone?
|
43
43
|
|
44
44
|
# call-seq:
|
45
|
-
# array.ascending? => true or false
|
45
|
+
# array.ascending? => +true+ or +false+
|
46
46
|
#
|
47
47
|
# Check whether _array_ is (strictly) ascending.
|
48
48
|
def ascending?(strict = false)
|
@@ -51,7 +51,7 @@ class Array
|
|
51
51
|
alias_method :increasing?, :ascending?
|
52
52
|
|
53
53
|
# call-seq:
|
54
|
-
# array.strictly_ascending? => true or false
|
54
|
+
# array.strictly_ascending? => +true+ or +false+
|
55
55
|
#
|
56
56
|
# Check whether _array_ is strictly ascending.
|
57
57
|
def strictly_ascending?
|
@@ -60,7 +60,7 @@ class Array
|
|
60
60
|
alias_method :strictly_increasing?, :strictly_ascending?
|
61
61
|
|
62
62
|
# call-seq:
|
63
|
-
# array.descending? => true or false
|
63
|
+
# array.descending? => +true+ or +false+
|
64
64
|
#
|
65
65
|
# Check whether _array_ is (strictly) descending.
|
66
66
|
def descending?(strict = false)
|
@@ -69,7 +69,7 @@ class Array
|
|
69
69
|
alias_method :decreasing?, :descending?
|
70
70
|
|
71
71
|
# call-seq:
|
72
|
-
# array.strictly_descending? => true or false
|
72
|
+
# array.strictly_descending? => +true+ or +false+
|
73
73
|
#
|
74
74
|
# Check whether _array_ is strictly descending.
|
75
75
|
def strictly_descending?
|
data/lib/nuggets/array/only.rb
CHANGED
@@ -28,10 +28,11 @@
|
|
28
28
|
class Array
|
29
29
|
|
30
30
|
# call-seq:
|
31
|
-
# array.only
|
31
|
+
# array.only => anObject
|
32
|
+
# array.only(+true+) => anObject
|
32
33
|
#
|
33
34
|
# Returns the only element of _array_. Raises an IndexError if _array_'s
|
34
|
-
# size is not 1, unless +
|
35
|
+
# size is not 1, unless parameter +true+ is passed.
|
35
36
|
#
|
36
37
|
# Idea stolen from Gavin Sinclair's Ruby Extensions Project.
|
37
38
|
def only(relax = size == 1)
|
@@ -0,0 +1,63 @@
|
|
1
|
+
#--
|
2
|
+
###############################################################################
|
3
|
+
# #
|
4
|
+
# A component of ruby-nuggets, some extensions to the Ruby programming #
|
5
|
+
# language. #
|
6
|
+
# #
|
7
|
+
# Copyright (C) 2007-2011 Jens Wille #
|
8
|
+
# #
|
9
|
+
# Authors: #
|
10
|
+
# Jens Wille <jens.wille@uni-koeln.de> #
|
11
|
+
# #
|
12
|
+
# ruby-nuggets is free software; you can redistribute it and/or modify it #
|
13
|
+
# under the terms of the GNU General Public License as published by the Free #
|
14
|
+
# Software Foundation; either version 3 of the License, or (at your option) #
|
15
|
+
# any later version. #
|
16
|
+
# #
|
17
|
+
# ruby-nuggets is distributed in the hope that it will be useful, but WITHOUT #
|
18
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
19
|
+
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
20
|
+
# more details. #
|
21
|
+
# #
|
22
|
+
# You should have received a copy of the GNU General Public License along #
|
23
|
+
# with ruby-nuggets. If not, see <http://www.gnu.org/licenses/>. #
|
24
|
+
# #
|
25
|
+
###############################################################################
|
26
|
+
#++
|
27
|
+
|
28
|
+
require 'nuggets/array/correlation_mixin'
|
29
|
+
|
30
|
+
module Nuggets
|
31
|
+
class Array
|
32
|
+
module RegressionMixin
|
33
|
+
|
34
|
+
def self.included(base)
|
35
|
+
base.send :include, Nuggets::Array::CorrelationMixin
|
36
|
+
end
|
37
|
+
|
38
|
+
# call-seq:
|
39
|
+
# array.linear_least_squares => anArray
|
40
|
+
#
|
41
|
+
# Calculates the {linear least squares regression}[http://en.wikipedia.org/wiki/Ordinary_least_squares]
|
42
|
+
# for the <tt>{x,y}</tt> pairs in _array_.
|
43
|
+
def linear_least_squares
|
44
|
+
sx, sy = 0.0, 0.0
|
45
|
+
|
46
|
+
return [] if empty?
|
47
|
+
|
48
|
+
each { |x, y|
|
49
|
+
sx += x
|
50
|
+
sy += y
|
51
|
+
}
|
52
|
+
|
53
|
+
b = corr * sy / sx
|
54
|
+
a = (sy - b * sx) / size.to_f
|
55
|
+
|
56
|
+
map { |x, _| [x, a + b * x] }
|
57
|
+
end
|
58
|
+
|
59
|
+
alias_method :llsq, :linear_least_squares
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -4,7 +4,7 @@
|
|
4
4
|
# A component of ruby-nuggets, some extensions to the Ruby programming #
|
5
5
|
# language. #
|
6
6
|
# #
|
7
|
-
# Copyright (C) 2007-
|
7
|
+
# Copyright (C) 2007-2011 Jens Wille #
|
8
8
|
# #
|
9
9
|
# Authors: #
|
10
10
|
# Jens Wille <jens.wille@uni-koeln.de> #
|
@@ -38,13 +38,10 @@ module Nuggets
|
|
38
38
|
# call-seq:
|
39
39
|
# array.standard_deviation => aFloat
|
40
40
|
#
|
41
|
-
# Calculates the standard deviation
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
rescue Errno::EDOM
|
46
|
-
0.0
|
47
|
-
end
|
41
|
+
# Calculates the {standard deviation}[http://en.wikipedia.org/wiki/Standard_deviation]
|
42
|
+
# of the values in _array_.
|
43
|
+
def standard_deviation(&block)
|
44
|
+
Math.sqrt(variance(&block))
|
48
45
|
end
|
49
46
|
|
50
47
|
alias_method :std, :standard_deviation
|
@@ -4,7 +4,7 @@
|
|
4
4
|
# A component of ruby-nuggets, some extensions to the Ruby programming #
|
5
5
|
# language. #
|
6
6
|
# #
|
7
|
-
# Copyright (C) 2007-
|
7
|
+
# Copyright (C) 2007-2011 Jens Wille #
|
8
8
|
# #
|
9
9
|
# Authors: #
|
10
10
|
# Jens Wille <jens.wille@uni-koeln.de> #
|
@@ -32,27 +32,46 @@ module Nuggets
|
|
32
32
|
# call-seq:
|
33
33
|
# array.variance => aFloat
|
34
34
|
#
|
35
|
-
# Calculates the variance of the
|
36
|
-
#
|
37
|
-
# Based on <http://warrenseen.com/blog/2006/03/13/how-to-calculate-standard-deviation/>.
|
35
|
+
# Calculates the variance[http://en.wikipedia.org/wiki/Variance] of the
|
36
|
+
# values in _array_.
|
38
37
|
def variance
|
39
|
-
|
38
|
+
sx, sq = 0.0, 0.0
|
40
39
|
|
41
|
-
return
|
40
|
+
return sx if empty?
|
42
41
|
|
43
|
-
|
42
|
+
each { |x|
|
44
43
|
x = yield x if block_given?
|
45
44
|
|
46
|
-
|
47
|
-
|
48
|
-
s += delta * (x - mean)
|
45
|
+
sx += x
|
46
|
+
sq += x ** 2
|
49
47
|
}
|
50
48
|
|
51
|
-
|
49
|
+
(sq - sx ** 2 / size) / size
|
52
50
|
end
|
53
51
|
|
54
52
|
alias_method :var, :variance
|
55
53
|
|
54
|
+
# call-seq:
|
55
|
+
# array.covariance => aFloat
|
56
|
+
#
|
57
|
+
# Calculates the covariance[http://en.wikipedia.org/wiki/Covariance] of the
|
58
|
+
# <tt>{x,y}</tt> pairs in _array_.
|
59
|
+
def covariance
|
60
|
+
sx, sy, sp = 0.0, 0.0, 0.0
|
61
|
+
|
62
|
+
return sx if empty?
|
63
|
+
|
64
|
+
each { |x, y|
|
65
|
+
sx += x
|
66
|
+
sy += y
|
67
|
+
sp += x * y
|
68
|
+
}
|
69
|
+
|
70
|
+
(sp - sx * sy / size) / size
|
71
|
+
end
|
72
|
+
|
73
|
+
alias_method :cov, :covariance
|
74
|
+
|
56
75
|
end
|
57
76
|
end
|
58
77
|
end
|
@@ -31,23 +31,23 @@ module Enumerable
|
|
31
31
|
alias_method :_nuggets_original_any?, :any?
|
32
32
|
|
33
33
|
# call-seq:
|
34
|
-
# enum.all?(obj[, operator]) => true or false
|
35
|
-
# enum.all? { ... } => true or false
|
34
|
+
# enum.all?(obj[, operator]) => +true+ or +false+
|
35
|
+
# enum.all? { ... } => +true+ or +false+
|
36
36
|
#
|
37
37
|
# Adds the ability to pass an +object+ instead of a block, which will then
|
38
38
|
# be tested against each item in _enum_ according to +operator+, defaulting
|
39
|
-
# to
|
39
|
+
# to <tt>:===</tt>.
|
40
40
|
def all?(object = default = Object.new, operator = :===, &block)
|
41
41
|
_nuggets_original_all?(&_block_for_all_any_extended(object, default, operator, &block))
|
42
42
|
end
|
43
43
|
|
44
44
|
# call-seq:
|
45
|
-
# enum.any?(obj[, operator]) => true or false
|
46
|
-
# enum.any? { ... } => true or false
|
45
|
+
# enum.any?(obj[, operator]) => +true+ or +false+
|
46
|
+
# enum.any? { ... } => +true+ or +false+
|
47
47
|
#
|
48
48
|
# Adds the ability to pass an +object+ instead of a block, which will then
|
49
49
|
# be tested against each item in _enum_ according to +operator+, defaulting
|
50
|
-
# to
|
50
|
+
# to <tt>:===</tt>.
|
51
51
|
def any?(object = default = Object.new, operator = :===, &block)
|
52
52
|
_nuggets_original_any?(&_block_for_all_any_extended(object, default, operator, &block))
|
53
53
|
end
|
@@ -85,7 +85,7 @@ module Enumerable
|
|
85
85
|
# call-seq:
|
86
86
|
# enum.max(what) => aValue
|
87
87
|
#
|
88
|
-
# Maximum #minmax. If +what+ is omitted, or nil
|
88
|
+
# Maximum #minmax. If +what+ is omitted, or +nil+, the original Enumerable#max
|
89
89
|
# is called.
|
90
90
|
def max(what = nil)
|
91
91
|
what ? minmax(:max, what) : block_given? ?
|
@@ -95,7 +95,7 @@ module Enumerable
|
|
95
95
|
# call-seq:
|
96
96
|
# enum.min(what) => aValue
|
97
97
|
#
|
98
|
-
# Minimum #minmax. If +what+ is omitted, or nil
|
98
|
+
# Minimum #minmax. If +what+ is omitted, or +nil+, the original Enumerable#min
|
99
99
|
# is called.
|
100
100
|
def min(what = nil)
|
101
101
|
what ? minmax(:min, what) : block_given? ?
|
@@ -30,10 +30,10 @@ module Nuggets
|
|
30
30
|
module SetMixin
|
31
31
|
|
32
32
|
# call-seq:
|
33
|
-
# ENV.set(env
|
34
|
-
# ENV.set(env
|
33
|
+
# ENV.set([env[, clear]]) => aHash
|
34
|
+
# ENV.set([env[, clear]]) { ... } => anObject
|
35
35
|
#
|
36
|
-
# Overrides ENV with +env+, clearing it beforehand if +clear+ is true
|
36
|
+
# Overrides ENV with +env+, clearing it beforehand if +clear+ is +true+. If a
|
37
37
|
# block is given, restores ENV to its original state afterwards and returns
|
38
38
|
# the result of the block; otherwise returns the original ENV as a hash.
|
39
39
|
def set(env = {}, clear = true)
|
@@ -56,7 +56,7 @@ module Nuggets
|
|
56
56
|
alias_method :without, :set
|
57
57
|
|
58
58
|
# call-seq:
|
59
|
-
# ENV.with(env
|
59
|
+
# ENV.with([env[, clear]]) { ... } => anObject
|
60
60
|
#
|
61
61
|
# Temporarily overrides ENV with +env+ for the block execution. See #set.
|
62
62
|
def with(env = {}, clear = false)
|
@@ -30,7 +30,7 @@ module Nuggets
|
|
30
30
|
module UserHomeMixin
|
31
31
|
|
32
32
|
# call-seq:
|
33
|
-
# ENV.user_home(default
|
33
|
+
# ENV.user_home([default]) => aString
|
34
34
|
#
|
35
35
|
# Returns the user's home directory, or +default+ if it could not be found.
|
36
36
|
def user_home(default = ::File::ALT_SEPARATOR ? 'C:/' : '/')
|
@@ -30,13 +30,13 @@ module Nuggets
|
|
30
30
|
module ReplaceMixin
|
31
31
|
|
32
32
|
# call-seq:
|
33
|
-
# File.replace(name, create_if_missing
|
34
|
-
# File.replace(name, create_if_missing
|
33
|
+
# File.replace(name[, create_if_missing]) { ... } => aString
|
34
|
+
# File.replace(name[, create_if_missing]) { |content| ... } => aString
|
35
35
|
#
|
36
36
|
# Replaces the contents of file +name+ with the result of the block. Yields
|
37
37
|
# the file's contents to the block if requested. Returns the new content.
|
38
38
|
#
|
39
|
-
# If +create_if_missing+ is true and the file does not exist, it will be
|
39
|
+
# If +create_if_missing+ is +true+ and the file does not exist, it will be
|
40
40
|
# created.
|
41
41
|
def replace(name, create_if_missing = false, &block)
|
42
42
|
open(name, create_if_missing && !exist?(name) ? 'w+' : 'r+') { |f|
|
@@ -47,7 +47,7 @@ module Nuggets
|
|
47
47
|
end
|
48
48
|
|
49
49
|
# call-seq:
|
50
|
-
# File.sub!(name, *args, &block) => aString or nil
|
50
|
+
# File.sub!(name, *args, &block) => aString or +nil+
|
51
51
|
#
|
52
52
|
# Calls String#sub! on file +name+'s contents with +args+ and (optional)
|
53
53
|
# +block+ and replaces the file with the new content. Returns the result
|
@@ -75,7 +75,7 @@ module Nuggets
|
|
75
75
|
end
|
76
76
|
|
77
77
|
# call-seq:
|
78
|
-
# File.gsub!(name, *args, &block) => aString or nil
|
78
|
+
# File.gsub!(name, *args, &block) => aString or +nil+
|
79
79
|
#
|
80
80
|
# Calls String#gsub! on file +name+'s contents with +args+ and (optional)
|
81
81
|
# +block+ and replaces the file with the new content. Returns the result
|
@@ -34,7 +34,7 @@ module Nuggets
|
|
34
34
|
DEFAULT_EXTENSIONS = [RbConfig::CONFIG['EXEEXT']]
|
35
35
|
|
36
36
|
# call-seq:
|
37
|
-
# File.which(executable, extensions
|
37
|
+
# File.which(executable[, extensions]) => aString or +nil+
|
38
38
|
#
|
39
39
|
# Returns +executable+ if it's executable, or the full path to +executable+
|
40
40
|
# found in PATH, or +nil+ otherwise. Checks +executable+ with each extension
|
@@ -44,27 +44,28 @@ module Nuggets
|
|
44
44
|
def which(executable, extensions = DEFAULT_EXTENSIONS)
|
45
45
|
extensions |= ['']
|
46
46
|
|
47
|
-
|
48
|
-
|
49
|
-
|
47
|
+
if env = ENV['PATH']
|
48
|
+
dirs = env.split(self::PATH_SEPARATOR)
|
49
|
+
dirs.map! { |dir| expand_path(dir) }
|
50
|
+
end
|
50
51
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
return candidate if executable?(candidate)
|
55
|
-
}
|
56
|
-
end
|
57
|
-
}
|
52
|
+
extensions.find { |extension|
|
53
|
+
file = "#{executable}#{extension}"
|
54
|
+
return file if file?(file) && executable?(file)
|
58
55
|
|
59
|
-
|
56
|
+
dirs.find { |dir|
|
57
|
+
path = join(dir, file)
|
58
|
+
return path if file?(path) && executable?(path)
|
59
|
+
} if dirs
|
60
|
+
}
|
60
61
|
end
|
61
62
|
|
62
63
|
# call-seq:
|
63
|
-
# File.which_command(commands) => aString or nil
|
64
|
+
# File.which_command(commands) => aString or +nil+
|
64
65
|
#
|
65
66
|
# Returns the first of +commands+ that is executable (according to #which).
|
66
67
|
def which_command(commands, extensions = DEFAULT_EXTENSIONS)
|
67
|
-
commands.find { |command| which(command[/\S+/], extensions) }
|
68
|
+
commands.find { |command| which(command.to_s[/\S+/], extensions) }
|
68
69
|
end
|
69
70
|
|
70
71
|
end
|