spruz 0.1.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/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