Module: CTioga2::Utils

Defined in:
lib/ctioga2/utils.rb

Overview

Various utilities

Constant Summary collapse

NaturalSubdivisions =
[1.0, 2.0, 5.0, 10.0]

Class Method Summary collapse

Class Method Details

.closest_subdivision(x, below = true) ⇒ Object

Returns the closest element of the correct power of ten of NaturalSubdivisions above or below the given number



182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
# File 'lib/ctioga2/utils.rb', line 182

def self.closest_subdivision(x, below = true)
  fact = 10**(Math.log10(x).floor)

  normed_x = x/fact
  (NaturalSubdivisions.size()-1).times do |i|
    if normed_x == NaturalSubdivisions[i]
      return x
    end
    if (normed_x > NaturalSubdivisions[i]) && 
        (normed_x < NaturalSubdivisions[i+1])
      if below
        return NaturalSubdivisions[i]*fact
      else
        return NaturalSubdivisions[i+1]*fact
      end
    end
  end
  raise "Should not reach this !"
end

.cluster_by_value(list, funcall) ⇒ Object

TODO:

with block too ?

Cluster a series of objects by the values returned by the given funcall. It returns an array of arrays where the elements are in the same order, and in each sub-array, the functions all return the same value



316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
# File 'lib/ctioga2/utils.rb', line 316

def self.cluster_by_value(list, funcall)
 if list.size == 0
   return []
 end
 ret = [ [list[0]] ]
 cur = ret[0]
 last = cur.first.send(funcall)

 for o in list[1..-1]
   val = o.send(funcall)
   if last == val
     cur << o
   else
     cur = [o]
     ret << cur
     last = val
   end
 end

 return ret
end

.cnk(n, k) ⇒ Object

Binomial coefficients (for the smooth filter)



113
114
115
116
117
118
# File 'lib/ctioga2/utils.rb', line 113

def self.cnk(n,k)
  res = 1.0
  n.downto(n - k) { |i| res *= i}
  k.downto(1) {|i| res = res/i }
  return res
end

.common_pow10(vect, method = :floor, tolerance = 1e-8) ⇒ Object

Returns the smallest power of 10 within the given buffer (excluding 0 or anything really close). That is, if you multiply by 10 to the power what is returned, the smallest will be in the range 1-10.



207
208
209
210
211
212
213
214
215
216
217
218
# File 'lib/ctioga2/utils.rb', line 207

def self.common_pow10(vect, method = :floor, tolerance = 1e-8)
  a = vect.abs
  a.sort!
  while (a.size > 1) && (a.first < tolerance * a.last)
    a.shift
  end
  if a.first == 0
    return 0
  end

  return Math.log10(a.first).send(method)
end

.group_by_prefix(strings, pref_re) ⇒ Object

Groups the given strings by prefixes



163
164
165
166
167
168
169
170
171
172
173
174
# File 'lib/ctioga2/utils.rb', line 163

def self.group_by_prefix(strings, pref_re)
  sets_by_prefix = {}
  for s in strings
    pref = s
    if s =~ pref_re
      pref = $1
    end
    sets_by_prefix[pref] ||= []
    sets_by_prefix[pref] << s
  end
  return sets_by_prefix
end

.integer_subdivisions(bot, top, delta) ⇒ Object

Returns the biggest vector of multiples of delta contained within bot and top



251
252
253
254
255
256
257
258
259
260
261
262
263
264
# File 'lib/ctioga2/utils.rb', line 251

def self.integer_subdivisions(bot, top, delta)
  if bot > top
    bot, top = top, bot
  end
  tn = (top/delta).floor
  bn = (bot/delta).ceil
  ret = Dobjects::Dvector.new()
  nb = (tn - bn).to_i + 1

  nb.times do |i|
    ret << (bn + i) * delta
  end
  return ret
end

.mix_objects(a, b, r) ⇒ Object

Takes two arrays of the same size (vectors) and mix them a * r + b * (1 - r)



74
75
76
77
78
79
80
# File 'lib/ctioga2/utils.rb', line 74

def self.mix_objects(a,b,r)
  ret = a.dup
  a.each_index do |i|
    ret[i] = a[i] * r + b[i] * (1 - r)
  end
  return ret
end

.osObject



292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
# File 'lib/ctioga2/utils.rb', line 292

def self.os
  host_os = RbConfig::CONFIG['host_os']
  case host_os
  when /mswin|msys|mingw|cygwin|bccwin|wince|emc/
    :windows
  when /darwin|mac os/
    :macosx
  when /linux/
    :linux
  when /solaris|bsd/
    :unix
  else
    warn {"Unknown os: #{host_os.inspect}"}
    :unknown
  end
end

.parse_formula(formula, parameters = nil, header = nil) ⇒ Object

This converts a text formula that can contain:

  • any litteral thing

  • references to columns in the form of $1 for column 1 (ie the second one)

  • references to named columns in the form $name$

  • references to parameters

The return value is ready to be passed to Dvector.compute_formula



128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/ctioga2/utils.rb', line 128

def self.parse_formula(formula, parameters = nil, header = nil)
  formula = formula.dup
  if parameters
    for k,v in parameters
      formula.gsub!(/\b#{k}\b/, v.to_s)
    end
  end
  formula.gsub!(/\$(\d+)/, 'column[\1]')
  if header
    for k,v in header
      formula.gsub!("$#{k}$", "column[#{v}]")
    end
  end
  return formula
end

.pdftex_quote_string(str) ⇒ Object

Quotes a string so it can be included directly within a pdfinfo statement (for instance).



84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/ctioga2/utils.rb', line 84

def self.pdftex_quote_string(str)
  return str.gsub(/([%#])|([()])|([{}~_^])|\\/) do 
    if $1
      "\\#{$1}"
    elsif $2                  # Quoting (), as they can be quite nasty !!
      "\\string\\#{$2}"
    elsif $3
      "\\string#{$3}"
    else                      # Quoting \
      "\\string\\\\"
    end
  end
end

.shell_quote_string(str) ⇒ Object

Takes a string a returns a quoted version that should be able to go through shell expansion.



49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/ctioga2/utils.rb', line 49

def self.shell_quote_string(str)
  if str =~ /[\s"*$()\[\]{}';\\]/
    if str =~ /'/
      a = str.gsub(/(["$\\])/) { "\\#$1" }
      return "\"#{a}\""
    else 
      return "'#{str}'"
    end
  else
    return str
  end
end

.sort_by_value(list, funcall) ⇒ Object

Returns a hash value -> [elements] in which the elements are in the same order



341
342
343
344
345
346
347
348
349
350
351
# File 'lib/ctioga2/utils.rb', line 341

def self.sort_by_value(list, funcall)
  ret = {}
  
  for el in list
    val = el.send(funcall)
    ret[val] ||= []

    ret[val] << el
  end
  return ret
end

.suffix_numeric_sort(strings) ⇒ Object



145
146
147
148
149
150
151
152
153
154
155
156
157
158
# File 'lib/ctioga2/utils.rb', line 145

def self.suffix_numeric_sort(strings)
  strings.sort do |a,b|
    a =~ /.*?(\d+)$/
    a_i = $1 ? $1.to_i : nil
    b =~ /.*?(\d+)$/
    b_i = $1 ? $1.to_i : nil
    
    if a_i && b_i
      a_i <=> b_i
    else
      a <=> b
    end
  end
end

.tex_quote_string(str) ⇒ Object

Quotes a string so it can be included directly within a pdfinfo statement (for instance).



100
101
102
103
104
105
106
107
108
109
110
# File 'lib/ctioga2/utils.rb', line 100

def self.tex_quote_string(str)
  return str.gsub(/([%#])|([{}~_^])|\\/) do 
    if $1
      "\\#{$1}"
    elsif $2
      "\\string#{$2}"
    else                      # Quoting \
      "\\string\\\\"
    end
  end
end

.transcode_until_found(file) ⇒ Object

Transcodes the given string from all encodings into the target encoding until an encoding is found in which the named file exists.

Works around encoding problems on windows.



226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
# File 'lib/ctioga2/utils.rb', line 226

def self.transcode_until_found(file)
  if File.exists? file
    return file
  end
  begin                     # We wrap in a begin/rescue block for
                            # Ruby 1.8
    for e in Encoding::constants
      e = Encoding.const_get(e)
      if e.is_a? Encoding
        begin
          ts = file.encode(Encoding::default_external, e)
          if File.exists? ts
            return ts
          end
        rescue
        end
      end
    end
  rescue
  end
  return file               # But that will fail later on.
end

.txt_to_float(txt) ⇒ Object

Converts a number to a float while trying to stay as lenient as possible



64
65
66
67
68
69
70
# File 'lib/ctioga2/utils.rb', line 64

def self.txt_to_float(txt)
  v = txt.to_f
  if v == 0.0
    return Float(txt)
  end
  return v
end

.which(cmd) ⇒ Object

Cross-platform way of finding an executable in the $PATH.

This is adapted from stackoverflow.com/questions/2108727/which-in-ruby-checking-if-program-exists-in-path-from-ruby



272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
# File 'lib/ctioga2/utils.rb', line 272

def self.which(cmd)
  return nil unless cmd
  exts = ['']
  if ENV['PATHEXT']
    exts += ENV['PATHEXT'].split(';')
  end

  ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
    exts.each { |ext|
      exe = File.join(path, "#{cmd}#{ext}")
      return exe if File.executable? exe
    }
  end
  return nil
end