spruz 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README ADDED
File without changes
data/Rakefile ADDED
@@ -0,0 +1,85 @@
1
+ begin
2
+ require 'rake/gempackagetask'
3
+ rescue LoadError
4
+ end
5
+ require 'rake/clean'
6
+ CLEAN.include 'coverage', 'doc'
7
+ require 'rbconfig'
8
+ include Config
9
+
10
+ PKG_NAME = 'spruz'
11
+ PKG_VERSION = File.read('VERSION').chomp
12
+ PKG_FILES = FileList['**/*'].exclude(/(CVS|\.svn|pkg|coverage|doc)/)
13
+
14
+ desc "Installing library"
15
+ task :install do
16
+ ruby 'install.rb'
17
+ end
18
+
19
+ desc "Creating documentation"
20
+ task :doc do
21
+ ruby 'make_doc.rb'
22
+ end
23
+
24
+ desc "Testing library"
25
+ task :test do
26
+ ruby '-Ilib tests/test_spruz.rb'
27
+ end
28
+
29
+ desc "Testing library with coverage"
30
+ task :coverage do
31
+ sh "rcov -Ilib -x 'tests/.*\.rb' tests/test_spruz.rb"
32
+ end
33
+
34
+ if defined? Gem
35
+ spec = Gem::Specification.new do |s|
36
+ s.name = PKG_NAME
37
+ s.version = PKG_VERSION
38
+ s.summary = "Useful stuff."
39
+ s.description = "All the stuff that isn't good/big enough for a real library."
40
+
41
+ s.files = PKG_FILES
42
+
43
+ s.require_path = 'lib'
44
+
45
+ s.bindir = "bin"
46
+ s.executables << "enum"
47
+
48
+ s.has_rdoc = true
49
+ s.extra_rdoc_files << 'README'
50
+ s.rdoc_options << '--title' << 'Spruz' << '--main' << 'README'
51
+ s.test_files << 'tests/test_spruz.rb'
52
+
53
+ s.author = "Florian Frank"
54
+ s.email = "flori@ping.de"
55
+ s.homepage = "http://flori.github.com/spruz"
56
+ end
57
+
58
+ Rake::GemPackageTask.new(spec) do |pkg|
59
+ pkg.need_tar = true
60
+ pkg.package_files += PKG_FILES
61
+ end
62
+ end
63
+
64
+ desc m = "Writing version information for #{PKG_VERSION}"
65
+ task :version do
66
+ puts m
67
+ File.open(File.join('lib', PKG_NAME, 'version.rb'), 'w') do |v|
68
+ v.puts <<EOT
69
+ module Spruz
70
+ # Spruz version
71
+ VERSION = '#{PKG_VERSION}'
72
+ VERSION_ARRAY = VERSION.split(/\\./).map { |x| x.to_i } # :nodoc:
73
+ VERSION_MAJOR = VERSION_ARRAY[0] # :nodoc:
74
+ VERSION_MINOR = VERSION_ARRAY[1] # :nodoc:
75
+ VERSION_BUILD = VERSION_ARRAY[2] # :nodoc:
76
+ end
77
+ EOT
78
+ end
79
+ end
80
+
81
+ desc "Default task: write version and test"
82
+ task :default => [ :version, :test ]
83
+
84
+ desc "Prepare a release"
85
+ task :release => [ :clean, :version, :package ]
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
data/bin/enum ADDED
@@ -0,0 +1,167 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'spruz'
4
+ include Spruz
5
+ include Spruz::GO
6
+
7
+ class CompoundRange
8
+ def initialize
9
+ @ranges = []
10
+ end
11
+
12
+ def concat(range)
13
+ @ranges << range
14
+ self
15
+ end
16
+
17
+ def each(&block)
18
+ @ranges.each { |r| r.each(&block) }
19
+ end
20
+ end
21
+
22
+ class Step
23
+ def initialize(a, b, step = 1, exclusive = false)
24
+ @a = a
25
+ @b = b
26
+ @step = step
27
+ if exclusive
28
+ if step < 0
29
+ @b += 1
30
+ else
31
+ @b -= 1
32
+ end
33
+ end
34
+ end
35
+
36
+ def each(&block)
37
+ @a.step(@b, @step, &block)
38
+ end
39
+ end
40
+
41
+ class StringRange
42
+ def initialize(a, b, exclusive = false)
43
+ @range = if a > b
44
+ if exclusive
45
+ (b.succ..a).to_a.reverse
46
+ else
47
+ (b..a).to_a.reverse
48
+ end
49
+ else
50
+ Range.new(a, b, exclusive)
51
+ end
52
+ end
53
+
54
+ def each(&block)
55
+ @range.each(&block)
56
+ end
57
+ end
58
+
59
+ class IntegerRange < Step
60
+ def initialize(a, b, exclusive = false)
61
+ @a = a
62
+ @b = b
63
+ super(a, b, a > b ? -1 : 1, exclusive)
64
+ end
65
+ end
66
+
67
+ def parse_range(range)
68
+ case range
69
+ when /,/
70
+ range.split(/,/).inject(CompoundRange.new) do |a,x|
71
+ a.concat(parse_range(x))
72
+ end
73
+ when /\A(-?\d+)\Z/
74
+ ($1.to_i)..($1.to_i)
75
+ when /\A(\w+)\Z/
76
+ $1..$1
77
+ when /\A(-?\d+)..(-?\d+)\Z/
78
+ IntegerRange.new($1.to_i, $2.to_i)
79
+ when /\A(-?\d+)...(-?\d+)\Z/
80
+ IntegerRange.new($1.to_i, $2.to_i, true)
81
+ when /\A(-?\d+)..(-?\d+)([+-]\d+)\Z/
82
+ Step.new($1.to_i, $2.to_i, $3.to_i)
83
+ when /\A(-?\d+)...(-?\d+)([+-]\d+)\Z/
84
+ Step.new($1.to_i, $2.to_i, $3.to_i, true)
85
+ when /\A(\w+)..(\w+)\Z/
86
+ StringRange.new($1, $2)
87
+ when /\A(\w+)...(\w+)\Z/
88
+ StringRange.new($1, $2, true)
89
+ else
90
+ fail "parsing range failed due to '#{range}'"
91
+ end
92
+ end
93
+
94
+ def parse_ranges(pattern)
95
+ pattern.split(/:/).map { |range| parse_range(range) }
96
+ end
97
+
98
+ def execute_command(command, format, tuple)
99
+ formatted = (format % tuple).split(/:/)
100
+ if $limited
101
+ $limited.execute do
102
+ system sprintf(command, *formatted)
103
+ end
104
+ else
105
+ system sprintf(command, *formatted)
106
+ end
107
+ end
108
+
109
+ def usage
110
+ puts <<EOT
111
+ Usage: #{File.basename($0)} [OPTIONS] RANGES
112
+
113
+ RANGES has to be a string of the from R1:R2:...Rn, where R1 to Rn are ranges of
114
+ values for each dimension. A range can be specified like in one of
115
+ those examples:
116
+ 2
117
+ 1..3
118
+ 3..1
119
+ 1...3
120
+ 1,2,4
121
+ 1..2,4
122
+ b
123
+ a..c
124
+ a...c
125
+ c..a
126
+ c...a
127
+ a,b,d
128
+ a..b,d
129
+
130
+
131
+ OPTIONS are
132
+
133
+ -p PARALLEL how many threads in parallel should be forked to handle the
134
+ execution of commands.
135
+
136
+ -e COMMAND this command is executed for every created tuple of values.
137
+ The tuple values can be fetch by using %s in order,
138
+ e. g. COMMAND = "a=%s;b=%s;echo a is $a. b is $b."
139
+
140
+ -f FORMAT format the created values with FORMAT = F1:F2:...:Fn each
141
+ format is a sprintf percent format, e. g. %02u.
142
+
143
+ -h this help display this help
144
+
145
+ EOT
146
+ exit 1
147
+ end
148
+
149
+ $opts = {
150
+ }.update(go('p:e:f:h')) do |o,default,set|
151
+ set || default
152
+ end
153
+ usage if $opts['h'] || ARGV.size != 1
154
+ $ranges = ARGV.shift
155
+ $limited = $opts['p'] ? Limited.new($opts['p']) : nil
156
+ ranges = parse_ranges($ranges)
157
+ generator = Generator.new(ranges)
158
+ $opts['f'] ||= %w[ %s ] * generator.size * ':'
159
+
160
+ generator.each do |tuple|
161
+ if $opts['e']
162
+ execute_command($opts['e'], $opts['f'], tuple)
163
+ else
164
+ puts $opts['f'] % tuple
165
+ end
166
+ end
167
+ exit 0
data/install.rb ADDED
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rbconfig'
4
+ include Config
5
+ require 'fileutils'
6
+ include FileUtils::Verbose
7
+
8
+ if $DEBUG
9
+ def install(*a)
10
+ p [ :install ] + a
11
+ end
12
+
13
+ def mkdir_p(*a)
14
+ p [ :mkdir_p ] + a
15
+ end
16
+ end
17
+
18
+ file = File.join('lib', 'spruz.rb')
19
+ dst = CONFIG["sitelibdir"]
20
+ install file, File.join(dst, File.join('lib/spruz.rb'))
21
+ src = File.join(*%w[lib spruz *.rb])
22
+ dst = File.join(CONFIG["sitelibdir"], 'spruz')
23
+ mkdir_p dst
24
+ for file in Dir[src]
25
+ install file, File.join(dst, file)
26
+ end
27
+ src = File.join(*%w[lib spruz xt *.rb])
28
+ dst = File.join(CONFIG["sitelibdir"], 'spruz', 'xt')
29
+ mkdir_p dst
30
+ for file in Dir[src]
31
+ install file, File.join(dst, file)
32
+ end
33
+ install 'bin/enum', File.join(CONFIG['bindir'], 'enum')
data/lib/spruz.rb ADDED
@@ -0,0 +1,22 @@
1
+ module Spruz
2
+ require 'spruz/version'
3
+ require 'spruz/bijection'
4
+ require 'spruz/count_by'
5
+ require 'spruz/generator'
6
+ require 'spruz/go'
7
+ require 'spruz/hash_union'
8
+ require 'spruz/limited'
9
+ require 'spruz/memoize'
10
+ require 'spruz/minimize'
11
+ require 'spruz/module_group'
12
+ require 'spruz/null'
13
+ require 'spruz/once'
14
+ require 'spruz/p'
15
+ require 'spruz/partial_application'
16
+ require 'spruz/round'
17
+ require 'spruz/shuffle'
18
+ require 'spruz/subhash'
19
+ require 'spruz/time_dummy'
20
+ require 'spruz/to_proc'
21
+ require 'spruz/uniq_by'
22
+ end
@@ -0,0 +1,31 @@
1
+ module Spruz
2
+ class Bijection < Hash
3
+ def initialize(inverted = Bijection.new(self))
4
+ @inverted = inverted
5
+ end
6
+
7
+ def fill
8
+ if empty?
9
+ yield self
10
+ freeze
11
+ end
12
+ self
13
+ end
14
+
15
+ def freeze
16
+ r = super
17
+ unless @inverted.frozen?
18
+ @inverted.freeze
19
+ end
20
+ r
21
+ end
22
+
23
+ def []=(key, value)
24
+ return if key?(key)
25
+ super
26
+ @inverted[value] = key
27
+ end
28
+
29
+ attr_reader :inverted
30
+ end
31
+ end
@@ -0,0 +1,8 @@
1
+ module Spruz
2
+ module CountBy
3
+ def count_by(&b)
4
+ b ||= lambda { |x| true }
5
+ inject(0) { |s, e| s += 1 if b[e]; s }
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,66 @@
1
+ module Spruz
2
+ # This class can create generator objects, that can produce all tuples, that
3
+ # would be created by as many for-loops as dimensions were given.
4
+ #
5
+ # The generator
6
+ # g = Spruz::Generator[1..2, %w[a b c]]
7
+ # produces
8
+ # g.to_a # => [[1, "a"], [1, "b"], [1, "c"], [2, "a"], [2, "b"], [2, "c"]]
9
+ #
10
+ # The 'each' method can be used to iterate over the tuples
11
+ # g.each { |a, b| puts "#{a} #{b}" }
12
+ # and Spruz::Generator includes the Enumerable module, so
13
+ # Enumerable.instance_methods can be used as well:
14
+ # g.select { |a, b| %w[a c].include? b } # => [[1, "a"], [1, "c"], [2, "a"], [2, "c"]]
15
+ #
16
+ class Generator
17
+ include Enumerable
18
+
19
+ # Create a new Generator object from the enumberables _enums_.
20
+ def self.[](*enums)
21
+ new(enums)
22
+ end
23
+
24
+ # Create a new Generator instance. Use the objects in the Array _enums_
25
+ # as dimensions. The should all respond to the :each method (see module
26
+ # Enumerable in the core ruby library).
27
+ def initialize(enums)
28
+ @enums, @iterators, @n = [], [], 0
29
+ enums.each { |e| add_dimension(e) }
30
+ end
31
+
32
+ # Iterate over all tuples produced by this generator and yield to them.
33
+ def each(&block) # :yield: tuple
34
+ recurse(&block)
35
+ self
36
+ end
37
+
38
+ def recurse(tuple = [ nil ] * @n, i = 0, &block)
39
+ if i < @n - 1 then
40
+ @enums[i].__send__(@iterators[i]) do |x|
41
+ tuple[i] = x
42
+ recurse(tuple, i + 1, &block)
43
+ end
44
+ else
45
+ @enums[i].__send__(@iterators[i]) do |x|
46
+ tuple[i] = x
47
+ yield tuple.dup
48
+ end
49
+ end
50
+ end
51
+ private :recurse
52
+
53
+ # Add another dimension to this generator. _enum_ is an object, that ought
54
+ # to respond to the _iterator_ method (defaults to :each).
55
+ def add_dimension(enum, iterator = :each)
56
+ @enums << enum
57
+ @iterators << iterator
58
+ @n += 1
59
+ end
60
+
61
+ # Return the size of this generator, that is the number of its dimensions.
62
+ def size
63
+ @enums.size
64
+ end
65
+ end
66
+ end
data/lib/spruz/go.rb ADDED
@@ -0,0 +1,37 @@
1
+ module Spruz
2
+ module GO
3
+ module_function
4
+
5
+ # Parses the argument array _args_, according to the pattern _s_, to
6
+ # retrieve the single character command line options from it. If _s_ is
7
+ # 'xy:' an option '-x' without an option argument is searched, and an
8
+ # option '-y foo' with an option argument ('foo').
9
+ #
10
+ # An option hash is returned with all found options set to true or the
11
+ # found option argument.
12
+ def go(s, args = ARGV)
13
+ b,v = s.scan(/(.)(:?)/).inject([{},{}]) { |t,(o,a)|
14
+ a = a == ':'
15
+ t[a ? 1 : 0][o] = a ? nil : false
16
+ t
17
+ }
18
+ while a = args.shift
19
+ a !~ /\A-(.+)/ and args.unshift a and break
20
+ p = $1
21
+ until p == ''
22
+ o = p.slice!(0, 1)
23
+ if v.key?(o)
24
+ v[o] = if p == '' then args.shift or break 1 else p end
25
+ break
26
+ elsif b.key?(o)
27
+ b[o] = true
28
+ else
29
+ args.unshift a
30
+ break 1
31
+ end
32
+ end and break
33
+ end
34
+ b.merge(v)
35
+ end
36
+ end
37
+ end