ctioga 1.11.1

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 (103) hide show
  1. data/COPYING +340 -0
  2. data/ctioga/bin/ctable +28 -0
  3. data/ctioga/bin/ctioga +37 -0
  4. data/ctioga/doc/ctable.1 +156 -0
  5. data/ctioga/doc/ctioga.1 +2363 -0
  6. data/ctioga/examples/README +46 -0
  7. data/ctioga/examples/ctioga.gnuplot +4 -0
  8. data/ctioga/examples/ctioga_within_tioga.rb +53 -0
  9. data/ctioga/examples/ctiogarc.rb +24 -0
  10. data/ctioga/examples/include_1.rb +15 -0
  11. data/ctioga/examples/noise.dat +100 -0
  12. data/ctioga/examples/noise.rb +13 -0
  13. data/ctioga/examples/trig.csv +100 -0
  14. data/ctioga/examples/trig.dat +100 -0
  15. data/ctioga/examples/trig.rb +14 -0
  16. data/ctioga/examples/trigh.dat +100 -0
  17. data/ctioga/examples/trigh.rb +10 -0
  18. data/ctioga/examples/tutorial +763 -0
  19. data/ctioga/examples/tutorial.sh +269 -0
  20. data/ctioga/tests/README +14 -0
  21. data/ctioga/tests/axes.sh +40 -0
  22. data/ctioga/tests/basic.sh +11 -0
  23. data/ctioga/tests/draw.sh +24 -0
  24. data/ctioga/tests/histograms.sh +14 -0
  25. data/ctioga/tests/insets.sh +41 -0
  26. data/ctioga/tests/layouts.sh +29 -0
  27. data/ctioga/tests/legends.sh +113 -0
  28. data/ctioga/tests/styles.sh +43 -0
  29. data/ctioga/tests/test_style.sh +8 -0
  30. data/ctioga/tests/tests.sh +24 -0
  31. data/ctioga/tests/text_backend.sh +83 -0
  32. data/ctioga/tests/tioga_defaults.rb +18 -0
  33. data/lib/CTioga/axes.rb +904 -0
  34. data/lib/CTioga/backends.rb +88 -0
  35. data/lib/CTioga/boundaries.rb +224 -0
  36. data/lib/CTioga/ctable.rb +134 -0
  37. data/lib/CTioga/curve_style.rb +246 -0
  38. data/lib/CTioga/debug.rb +199 -0
  39. data/lib/CTioga/dimension.rb +133 -0
  40. data/lib/CTioga/elements.rb +17 -0
  41. data/lib/CTioga/elements/base.rb +84 -0
  42. data/lib/CTioga/elements/containers.rb +578 -0
  43. data/lib/CTioga/elements/curves.rb +368 -0
  44. data/lib/CTioga/elements/tioga_primitives.rb +440 -0
  45. data/lib/CTioga/layout.rb +595 -0
  46. data/lib/CTioga/legends.rb +29 -0
  47. data/lib/CTioga/legends/cmdline.rb +187 -0
  48. data/lib/CTioga/legends/item.rb +164 -0
  49. data/lib/CTioga/legends/style.rb +257 -0
  50. data/lib/CTioga/log.rb +73 -0
  51. data/lib/CTioga/movingarrays.rb +131 -0
  52. data/lib/CTioga/partition.rb +271 -0
  53. data/lib/CTioga/plot_style.rb +230 -0
  54. data/lib/CTioga/plotmaker.rb +1677 -0
  55. data/lib/CTioga/shortcuts.rb +69 -0
  56. data/lib/CTioga/structures.rb +82 -0
  57. data/lib/CTioga/styles.rb +140 -0
  58. data/lib/CTioga/themes.rb +581 -0
  59. data/lib/CTioga/themes/classical.rb +82 -0
  60. data/lib/CTioga/themes/demo.rb +63 -0
  61. data/lib/CTioga/themes/fits.rb +91 -0
  62. data/lib/CTioga/themes/mono.rb +33 -0
  63. data/lib/CTioga/tioga.rb +32 -0
  64. data/lib/CTioga/utils.rb +173 -0
  65. data/lib/MetaBuilder/Parameters/dates.rb +38 -0
  66. data/lib/MetaBuilder/Parameters/lists.rb +132 -0
  67. data/lib/MetaBuilder/Parameters/numbers.rb +69 -0
  68. data/lib/MetaBuilder/Parameters/strings.rb +86 -0
  69. data/lib/MetaBuilder/Parameters/styles.rb +75 -0
  70. data/lib/MetaBuilder/Qt4/Parameters/dates.rb +51 -0
  71. data/lib/MetaBuilder/Qt4/Parameters/numbers.rb +65 -0
  72. data/lib/MetaBuilder/Qt4/Parameters/strings.rb +106 -0
  73. data/lib/MetaBuilder/Qt4/parameter.rb +172 -0
  74. data/lib/MetaBuilder/Qt4/parameters.rb +9 -0
  75. data/lib/MetaBuilder/descriptions.rb +603 -0
  76. data/lib/MetaBuilder/factory.rb +101 -0
  77. data/lib/MetaBuilder/group.rb +57 -0
  78. data/lib/MetaBuilder/metabuilder.rb +10 -0
  79. data/lib/MetaBuilder/parameter.rb +374 -0
  80. data/lib/MetaBuilder/parameters.rb +11 -0
  81. data/lib/MetaBuilder/qt4.rb +8 -0
  82. data/lib/SciYAG/Backends/backend.rb +379 -0
  83. data/lib/SciYAG/Backends/binner.rb +168 -0
  84. data/lib/SciYAG/Backends/cache.rb +102 -0
  85. data/lib/SciYAG/Backends/dataset.rb +158 -0
  86. data/lib/SciYAG/Backends/descriptions.rb +469 -0
  87. data/lib/SciYAG/Backends/filters.rb +25 -0
  88. data/lib/SciYAG/Backends/filters/average.rb +134 -0
  89. data/lib/SciYAG/Backends/filters/cumulate.rb +37 -0
  90. data/lib/SciYAG/Backends/filters/filter.rb +70 -0
  91. data/lib/SciYAG/Backends/filters/norm.rb +39 -0
  92. data/lib/SciYAG/Backends/filters/smooth.rb +63 -0
  93. data/lib/SciYAG/Backends/filters/sort.rb +43 -0
  94. data/lib/SciYAG/Backends/filters/strip.rb +34 -0
  95. data/lib/SciYAG/Backends/filters/trim.rb +64 -0
  96. data/lib/SciYAG/Backends/gnuplot.rb +131 -0
  97. data/lib/SciYAG/Backends/math.rb +108 -0
  98. data/lib/SciYAG/Backends/mdb.rb +462 -0
  99. data/lib/SciYAG/Backends/multitext.rb +96 -0
  100. data/lib/SciYAG/Backends/source.rb +64 -0
  101. data/lib/SciYAG/Backends/text.rb +339 -0
  102. data/lib/SciYAG/backends.rb +16 -0
  103. metadata +191 -0
@@ -0,0 +1,96 @@
1
+ # multitext.rb : A simple backend to deal with multiple basic text files.
2
+ # Copyright (C) 2006 Jean-Julien Fleck
3
+ # Directly inspired from Vincent Fourmond's text.rb
4
+
5
+ # This program is free software; you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation; either version 2 of the License, or
8
+ # (at your option) any later version.
9
+
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program; if not, write to the Free Software
17
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18
+
19
+
20
+
21
+ require 'SciYAG/Backends/text'
22
+ require 'Dobjects/Dvector'
23
+ require 'Dobjects/Function'
24
+
25
+ module SciYAG
26
+
27
+ module Backends
28
+
29
+ class MultiTextBackend < TextBackend
30
+
31
+ include Dobjects
32
+
33
+ describe 'multitext', 'MultiText format', <<EOD
34
+ This backend can read multiple text files in a format close to the one
35
+ understood by gnuplot and the like. Allows to plot one column form one file
36
+ with respect to another column of another file.
37
+ EOD
38
+
39
+ inherit_parameters :skip
40
+
41
+ def initialize
42
+ @dummy = nil
43
+ @current = nil
44
+ # @current is an array of Dvectors holding the contents of the most
45
+ # recently read files, so that there is no need to read it again.
46
+ @skip = 0
47
+ @included_modules = [] # to make sure we give them to
48
+ # compute_formula
49
+ super()
50
+ end
51
+
52
+ def extend(mod)
53
+ super
54
+ @included_modules << mod
55
+ end
56
+
57
+ # This is called by the architecture to get the data. It splits
58
+ # the set name into filename@cols, reads the file if necessary and
59
+ # calls get_data
60
+ def query_xy_data(set)
61
+ cols = []
62
+ files= []
63
+ nb_match = -1
64
+ new_set = set.gsub(/\[(.*?)@(.*?)\]/) do |match|
65
+ cols.push($2)
66
+ files.push($1)
67
+ nb_match += 1
68
+ "column[#{nb_match}]"
69
+ end
70
+ @current = Hash.new
71
+ files.uniq.each do |file|
72
+ @current[file] = Dvector.fancy_read(file, nil,
73
+ 'index_col' => true,
74
+ 'skip_first' => @skip)
75
+ end
76
+ return Function.new(*get_data(cols,files,new_set))
77
+ end
78
+
79
+ # Reads the data using the columns specification, provided that
80
+ # the appropriate files have already been loaded into @current. For now
81
+ # no single sanity check.
82
+ def get_data(cols,files,set)
83
+ vectors = []
84
+ cols.each_index do |i|
85
+ vectors.push(@current[files[i]][cols[i].to_i])
86
+ end
87
+ x_formula, y_formula = set.split(':')
88
+ mods = @included_modules
89
+ return [Dvector.compute_formula(x_formula,vectors,mods),
90
+ Dvector.compute_formula(y_formula,vectors,mods)]
91
+ end
92
+
93
+ end
94
+
95
+ end
96
+ end
@@ -0,0 +1,64 @@
1
+ # source.rb : The implementation of a data source
2
+ # Copyright (C) 2006 Vincent Fourmond
3
+
4
+ # This program is free software; you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation; either version 2 of the License, or
7
+ # (at your option) any later version.
8
+
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program; if not, write to the Free Software
16
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17
+
18
+ module SciYAG
19
+
20
+ module Backends
21
+
22
+ # A data source is a class containing raw data, most probably under
23
+ # the form of a list of Dvectors. It is the job of Representations to
24
+ # transform that source into a 2D or 3D set.
25
+ class DataSource
26
+
27
+ # The list of the columns
28
+ attr_reader :columns
29
+
30
+ # A list of hashes of informations about the columns
31
+ attr_reader :columns_info
32
+
33
+ # Some informations about the source. Different possibilities:
34
+ # * :filename, the name of the file from where the source is
35
+ # coming;
36
+ # * :dataset, the name of dataset, if applicable.
37
+ # and so on...
38
+ attr_accessor :informations
39
+
40
+ # Creates a datasource, and fills it with the columns _columns_.
41
+ # It is better however to use directly #add_column to fill
42
+ # the source with Data.
43
+ def initialize(*columns)
44
+ @columns = []
45
+ @columns_info = []
46
+ @informations = {}
47
+ end
48
+
49
+ # Automatic naming of the columns
50
+ def auto_name(number)
51
+ return "$#{number}"
52
+ end
53
+
54
+ # Add a column and its information to the list.
55
+ def add_column(col, name = nil, infos = {})
56
+ name = auto_name(col.length) unless name
57
+ @columns << col
58
+ infos[:name] = name
59
+ @columns_info << infos
60
+ end
61
+ end
62
+
63
+ end
64
+ end
@@ -0,0 +1,339 @@
1
+ # text.rb : A simple backend to deal with basic text files.
2
+ # Copyright (C) 2006 Vincent Fourmond
3
+
4
+ # This program is free software; you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation; either version 2 of the License, or
7
+ # (at your option) any later version.
8
+
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program; if not, write to the Free Software
16
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17
+
18
+
19
+
20
+ require 'SciYAG/Backends/backend'
21
+ require 'Dobjects/Dvector'
22
+ require 'Dobjects/Function'
23
+
24
+ # For separated sets
25
+ require 'stringio'
26
+
27
+ module SciYAG
28
+
29
+ # A module for easy use of NaN in operations
30
+ module NaN
31
+ NaN = 0.0/0.0
32
+ def nan
33
+ return NaN
34
+ end
35
+ end
36
+
37
+ module Backends
38
+
39
+ class TextBackend < Backend
40
+
41
+ # A constant holding a relation extension -> command to
42
+ # decompress (to be fed to sprintf with the filename as argument)
43
+ UNCOMPRESSORS = {
44
+ ".gz" => "gunzip -c %s",
45
+ ".bz2" => "bunzip2 -c %s",
46
+ ".lzma" => "unlzma -c %s",
47
+ }
48
+
49
+ include Dobjects
50
+
51
+ describe 'text', 'Text format', <<EOD
52
+ This backend can read text files in a format close to the one understood
53
+ by gnuplot and the like.
54
+ EOD
55
+
56
+ # Inherit the baseline handling, can be useful !
57
+ inherit_parameters :base_line
58
+
59
+ param_accessor :skip, 'skip', "Skip lines", {:type => :integer},
60
+ "Number of lines to be skipped at the beginning of the file"
61
+
62
+ param_accessor :default_column_spec, 'col',
63
+ "Default column specification", {:type => :string},
64
+ "Which columns to use when the @1:2 syntax is not used"
65
+
66
+ param_accessor :split, 'split', "Split into subsets",
67
+ {:type => :boolean},
68
+ "If true, splits files into subsets on blank/non number lines"
69
+
70
+
71
+ param_accessor :separator, 'separator', "Data columns separator",
72
+ {:type => :string_or_regexp},
73
+ "The columns separator. Defaults to /\s+/"
74
+
75
+ # param_accessor :select, 'select', "Select lines", {:type => :string},
76
+ # "Skips line where the code returns false"
77
+
78
+ def initialize
79
+ @dummy = nil
80
+ @current = nil
81
+ # Current is the name of the last file used. Necessary for '' specs.
82
+ @current_data = nil # The data of the last file used.
83
+ @skip = 0
84
+ @included_modules = [NaN] # to make sure we give them to
85
+ # Dvector.compute_formula
86
+ @default_column_spec = "1:2"
87
+
88
+ @separator = /\s+/
89
+
90
+ # We don't split data by default.
91
+ @split = false
92
+
93
+ super()
94
+
95
+ # Override Backend's cache - for now.
96
+ @cache = {} # A cache file_name -> data
97
+
98
+ end
99
+
100
+ def extend(mod)
101
+ super
102
+ @included_modules << mod
103
+ end
104
+
105
+ # Returns a IO object suitable to acquire data from it for
106
+ # the given _file_, which can be one of the following:
107
+ # * a real file name
108
+ # * a compressed file name
109
+ # * a pipe command.
110
+ def get_io_object(file)
111
+ if file == "-"
112
+ return $stdin
113
+ elsif file =~ /(.*?)\|\s*$/ # A pipe
114
+ return IO.popen($1)
115
+ elsif not File.readable?(file)
116
+ # Try to find a compressed version
117
+ for ext,method in UNCOMPRESSORS
118
+ if File.readable? "#{file}#{ext}"
119
+ info "Using compressed file #{name}#{ext} in stead of #{name}"
120
+ return IO.popen(method % "#{file}#{ext}")
121
+ end
122
+ end
123
+ else
124
+ for ext, method in UNCOMPRESSORS
125
+ if file =~ /#{ext}$/
126
+ info "Taking file #{file} as a compressed file"
127
+ return IO.popen(method % file)
128
+ end
129
+ end
130
+ return File::open(file)
131
+ end
132
+ error "Could not open #{file}"
133
+ return nil
134
+ end
135
+
136
+ # A line is invalid if it is blank or starts
137
+ # neither with a digit nor +, - or .
138
+ #
139
+ # Maybe to be improved later.
140
+ InvalidLineRE = /^\s*$|^\s*[^\d+.\s-]+/
141
+
142
+ # Returns a string corresponding to the given _set_ of the
143
+ # given _io_ object.
144
+ #
145
+ # Sets are 1-based.
146
+ def get_set_string(io, set)
147
+ cur_set = 1
148
+ last_line_is_invalid = true
149
+ str = ""
150
+ line_number = 0
151
+ while line = io.gets
152
+ line_number += 1
153
+ if line =~ InvalidLineRE
154
+ debug "Found invalid line at #{line_number}"
155
+ if ! last_line_is_invalid
156
+ # We begin a new set.
157
+ cur_set += 1
158
+ debug "Found set #{cur_set} at line #{line_number}"
159
+ if(cur_set > set)
160
+ return str
161
+ end
162
+ end
163
+ last_line_is_invalid = true
164
+ else
165
+ last_line_is_invalid = false
166
+ if cur_set == set
167
+ str += line
168
+ end
169
+ end
170
+ end
171
+ return str
172
+ end
173
+
174
+ # Returns an IO object corresponding to the given file.
175
+ def get_io_set(file)
176
+ if not @split
177
+ return get_io_object(file)
178
+ else
179
+ file =~ /(.*?)(?:#(\d+))?$/; # ; to make ruby-mode indent correctly.
180
+ filename = $1
181
+ if $2
182
+ set = $2.to_i
183
+ else
184
+ set = 1
185
+ end
186
+ debug "Trying to get set #{set} from file '#{filename}'"
187
+ str = get_set_string(get_io_object(filename), set)
188
+ return StringIO.new(str)
189
+ end
190
+ end
191
+
192
+ # Reads data from a file. If needed, extract the file from the columns
193
+ # specification.
194
+ def read_file(file)
195
+ if file =~ /(.*)@.*/
196
+ file = $1
197
+ end
198
+ name = file # As file will be modified.
199
+ if ! @cache.key?(file) # Read the file if it is not cached.
200
+ fancy_read_options = {'index_col' => true,
201
+ 'skip_first' => @skip,
202
+ 'sep' => @separator
203
+ }
204
+ io_set = get_io_set(file)
205
+ debug "Fancy read '#{file}', options #{fancy_read_options.inspect}"
206
+ @cache[name] = Dvector.fancy_read(io_set, nil, fancy_read_options)
207
+ end
208
+ return @cache[name]
209
+ end
210
+
211
+
212
+ # This is called by the architecture to get the data. It splits
213
+ # the set name into filename@cols, reads the file if necessary and
214
+ # calls get_data
215
+ def query_xy_data(set)
216
+ if set =~ /(.*)@(.*)/
217
+ col_spec = $2
218
+ file = $1
219
+ else
220
+ col_spec = @default_column_spec
221
+ file = set
222
+ end
223
+ if file.length > 0
224
+ @current_data = read_file(file)
225
+ @current = file
226
+ end
227
+ x,y,err = get_data(col_spec)
228
+ return [Function.new(x,y),err]
229
+ end
230
+
231
+ # Reads the data using the columns specification, provided that
232
+ # the appropriate fle has already been loaded into @current. For now
233
+ # no single sanity check.
234
+ def get_data(col_spec)
235
+ # First, we must split the column specification into what
236
+ # I would call target specifications. A target is in the form
237
+ # of stuff=spec, where stuff can be basically anything. x=, y=,
238
+ # yea= are implied for the first specifications.
239
+
240
+ defaults = [:x,:y,:yea]
241
+ specifications = {} # A hash value spec => column spec
242
+ col_spec.split(/:/).each do |spec|
243
+ d = defaults.shift
244
+ if spec =~ /^\s*(\w+)\s*=(.*)/
245
+ spec = $2
246
+ d = $1.to_sym
247
+ end
248
+ specifications[d] = spec
249
+ end
250
+
251
+ debug "spec #{col_spec} becomes #{specifications.inspect}"
252
+
253
+ values = {}
254
+ if col_spec =~ /\$/ # There is a formula in the specification
255
+ for key,spec in specifications
256
+ formula = spec.gsub(/\$(\d+)/, 'column[\1]')
257
+ debug "Using formula '#{formula}' for #{key}"
258
+ values[key] = Dvector.
259
+ compute_formula(formula,
260
+ @current_data,
261
+ @included_modules)
262
+ end
263
+ else
264
+ for key,spec in specifications
265
+ values[key] = @current_data[spec.to_i].dup
266
+ end
267
+ end
268
+ errors = compute_error_bars(values)
269
+ # Now, we're left with a hash...
270
+ return [values[:x],values[:y], errors]
271
+ end
272
+
273
+ # Turns a target => values specification into something usable as
274
+ # error bars, that is :xmin, :xmax and the like hashes. The rules
275
+ # are the following:
276
+ # * ?min/?max are passed on directly;
277
+ # * ?e(abs) are transformed into ?min = ? - ?eabs, ?max = ? + ?eabs
278
+ # * ?eu(p/?ed(own) are transformed respectively into ? +/- ?...
279
+ # * ?er(el) become ?min = ?*(1 - ?erel, ?max = ?(1 + ?erel)
280
+ # * ?erup/?erdown follow the same pattern...
281
+ def compute_error_bars(values)
282
+ target = {}
283
+ for key in values.keys
284
+ case key.to_s
285
+ when /^[xy](min|max)?$/
286
+ target[key] = values[key].dup # Just to make sure.
287
+ when /^(.)e(a(bs?)?)?$/
288
+ target["#{$1}min".to_sym] = values[$1.to_sym] - values[key]
289
+ target["#{$1}max".to_sym] = values[$1.to_sym] + values[key]
290
+ when /^(.)eu(p)?$/
291
+ target["#{$1}max".to_sym] = values[$1.to_sym] + values[key]
292
+ when /^(.)ed(o(wn?)?)?$/
293
+ target["#{$1}min".to_sym] = values[$1.to_sym] - values[key]
294
+ when /^(.)er(el?)?$/
295
+ target["#{$1}min".to_sym] = values[$1.to_sym] *
296
+ (values[key].neg + 1)
297
+ target["#{$1}max".to_sym] = values[$1.to_sym] *
298
+ (values[key] + 1)
299
+ when /^(.)erd(o(wn?)?)?$/
300
+ target["#{$1}min".to_sym] = values[$1.to_sym] *
301
+ (values[key].neg + 1)
302
+ when /^(.)erup?$/
303
+ target["#{$1}max".to_sym] = values[$1.to_sym] *
304
+ (values[key] + 1)
305
+ else
306
+ warn "Somehow, the target specification #{key} " +
307
+ "didn't make it through"
308
+ end
309
+ end
310
+ return target
311
+ end
312
+
313
+ # Expands specifications into few sets. This function will separate the
314
+ # set into a file spec and a col spec. Within the col spec, the 2##6
315
+ # keyword is used to expand to 2,3,4,5,6. 2## followed by a non-digit
316
+ # expands to 2,...,last column in the file. For now, the expansions
317
+ # stops on the first occurence found, and the second form doesn't
318
+ # work yet. But soon...
319
+ def expand_sets(spec)
320
+ if m = /(\d+)##(\D|$)/.match(spec)
321
+ a = m[1].to_i
322
+ trail = m[2]
323
+ b = read_file(spec)
324
+ b = (b.length - 1)
325
+ ret = []
326
+ a.upto(b) do |i|
327
+ ret << m.pre_match + i.to_s + trail + m.post_match
328
+ end
329
+ return ret
330
+ else
331
+ return super
332
+ end
333
+ end
334
+
335
+ end
336
+
337
+ end
338
+
339
+ end