whatnot 1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,3 @@
1
+ module Whatnot
2
+ VERSION = 1.0
3
+ end
@@ -0,0 +1,65 @@
1
+ require 'spec_helper'
2
+
3
+ module Whatnot
4
+ describe DimacsCNFVar do
5
+ let(:test_table) do
6
+ {:name=>:Y5A3T3,
7
+ :domain=>
8
+ [{:kid_ids=>[2, 10, 11],
9
+ :subject_ids=>[4, 3, 2, 1],
10
+ :grade_id=>1,
11
+ :hosting_slot=>:A1H1,
12
+ :class_id=>1,
13
+ :day=>5},
14
+ {:kid_ids=>[2, 10, 11],
15
+ :subject_ids=>[4, 3, 2, 1],
16
+ :grade_id=>1,
17
+ :hosting_slot=>:A2H1,
18
+ :class_id=>4,
19
+ :day=>5},
20
+ {:kid_ids=>[2, 10, 11],
21
+ :subject_ids=>[4, 3, 2, 1],
22
+ :grade_id=>1,
23
+ :hosting_slot=>:A2H2,
24
+ :class_id=>7,
25
+ :day=>5},
26
+ {:null_class=>1},
27
+ {:null_class=>2},
28
+ {:null_class=>3},
29
+ {:null_class=>4}],
30
+ :argument_set=>
31
+ [[{:kid_ids=>[2, 10, 11],
32
+ :subject_ids=>[4, 3, 2, 1],
33
+ :grade_id=>1,
34
+ :hosting_slot=>"A1H1",
35
+ :class_id=>1,
36
+ :day=>5},
37
+ 579],
38
+ [{:kid_ids=>[2, 10, 11],
39
+ :subject_ids=>[4, 3, 2, 1],
40
+ :grade_id=>1,
41
+ :hosting_slot=>"A2H1",
42
+ :class_id=>4,
43
+ :day=>5},
44
+ 580],
45
+ [{:kid_ids=>[2, 10, 11],
46
+ :subject_ids=>[4, 3, 2, 1],
47
+ :grade_id=>1,
48
+ :hosting_slot=>"A2H2",
49
+ :class_id=>7,
50
+ :day=>5},
51
+ 581],
52
+ [{:null_class=>1}, 582],
53
+ [{:null_class=>2}, 583],
54
+ [{:null_class=>3}, 584],
55
+ [{:null_class=>4}, 585]]}
56
+ end
57
+
58
+ describe '#argument_set' do
59
+ it 'works' do
60
+ var = DimacsCNFVar.new(test_table[:name], test_table[:domain], key_iter: 579)
61
+ expect(var.argument_set).to eq(test_table[:argument_set])
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,47 @@
1
+ require 'spec_helper'
2
+
3
+ module Whatnot
4
+ describe SolutionEnumerator do
5
+ def noop(str); str end
6
+
7
+ describe "#each" do
8
+ context "simplest" do
9
+ let(:dimacs) do
10
+ %(
11
+ c Test DIMACS 1
12
+ c -------------
13
+ p cnf 2 2
14
+ 1 -2 0
15
+ -1 2 0
16
+ )
17
+ end
18
+
19
+ let(:interpreter) { method(:noop) }
20
+
21
+ it "finds all solutions" do
22
+ enum = SolutionEnumerator.new(interpreter, dimacs)
23
+
24
+ expect(enum.entries).to eq(["-1 -2 0", "1 2 0"])
25
+ end
26
+ end
27
+
28
+ context "with SwitchGroup" do
29
+ before do
30
+ Switch.class_variable_set(:"@@all", {})
31
+ Switch.class_variable_set(:"@@next_number", 1)
32
+ Switch.new({foo: 1})
33
+ Switch.new({foo: 2})
34
+ Switch.new({foo: 3})
35
+ end
36
+
37
+ let(:interpreter) { method(:noop) }
38
+
39
+ it "finds all solutions" do
40
+ group = SwitchGroup.new(max_on: 2) { true }
41
+ enum = SolutionEnumerator.new(interpreter, group.dimacs)
42
+ expect(enum.entries).to eq(["-1 -2 3 0", "1 -2 -3 0", "-1 2 -3 0", "1 2 -3 0", "-1 2 3 0", "1 -2 3 0"])
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,67 @@
1
+ require 'spec_helper'
2
+
3
+ module Whatnot
4
+ describe SwitchGroup do
5
+ describe "dimacs" do
6
+ let(:min_one_max_one_dimacs) do
7
+ %(1 2 3 0
8
+ -1 -2 0
9
+ -1 -3 0
10
+ -2 -3 0).lines.map(&:strip).join("\n") + "\n"
11
+ end
12
+
13
+ let(:min_two_max_two_dimacs) do
14
+ %(1 2 0
15
+ 1 3 0
16
+ 2 3 0
17
+ -1 -2 -3 0).lines.map(&:strip).join("\n") + "\n"
18
+ end
19
+
20
+ let(:min_zero_max_two_dimacs) do
21
+ %(-1 -2 -3 0).lines.map(&:strip).join("\n") + "\n"
22
+ end
23
+
24
+ let(:min_one_max_two_dimacs) do
25
+ %(1 2 3 0
26
+ -1 -2 -3 0).lines.map(&:strip).join("\n") + "\n"
27
+ end
28
+
29
+ before do
30
+ Switch.class_variable_set(:"@@all", {})
31
+ Switch.class_variable_set(:"@@next_number", 1)
32
+ Switch.new({foo: 1})
33
+ Switch.new({foo: 2})
34
+ Switch.new({foo: 3})
35
+ end
36
+
37
+ context "simplest" do
38
+ it "produces correct clauses" do
39
+
40
+ group = SwitchGroup.new { true }
41
+ expect(group.dimacs).to include(min_one_max_one_dimacs)
42
+ end
43
+ end
44
+
45
+ context "min_on: 2, max_on: 2" do
46
+ it "produces correct clauses" do
47
+ group = SwitchGroup.new(min_on: 2, max_on: 2) { true }
48
+ expect(group.dimacs).to include(min_two_max_two_dimacs)
49
+ end
50
+ end
51
+
52
+ context "min_on: 0, max_on: 2" do
53
+ it "produces correct clauses" do
54
+ group = SwitchGroup.new(min_on: 0, max_on: 2) { true }
55
+ expect(group.dimacs).to include(min_zero_max_two_dimacs)
56
+ end
57
+ end
58
+
59
+ context "min_on: 1, max_on: 2" do
60
+ it "produces correct clauses" do
61
+ group = SwitchGroup.new(max_on: 2) { true }
62
+ expect(group.dimacs).to include(min_one_max_two_dimacs)
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,188 @@
1
+ require 'spec_helper'
2
+
3
+ def print_sudoku(solution)
4
+ puts "+-------+-------+-------+"
5
+ ("A".."C").to_a.each do |letter|
6
+ print "| "
7
+ print solution.fetch(:"#{letter}1", " ").to_s + " "
8
+ print solution.fetch(:"#{letter}2", " ").to_s + " "
9
+ print solution.fetch(:"#{letter}3", " ").to_s
10
+ print " | "
11
+ print solution.fetch(:"#{letter}4", " ").to_s + " "
12
+ print solution.fetch(:"#{letter}5", " ").to_s + " "
13
+ print solution.fetch(:"#{letter}6", " ").to_s
14
+ print " | "
15
+ print solution.fetch(:"#{letter}7", " ").to_s + " "
16
+ print solution.fetch(:"#{letter}8", " ").to_s + " "
17
+ print solution.fetch(:"#{letter}9", " ").to_s
18
+ print " |\n"
19
+ end
20
+ puts "+-------+-------+-------+"
21
+ ("D".."F").to_a.each do |letter|
22
+ print "| "
23
+ print solution.fetch(:"#{letter}1", " ").to_s + " "
24
+ print solution.fetch(:"#{letter}2", " ").to_s + " "
25
+ print solution.fetch(:"#{letter}3", " ").to_s
26
+ print " | "
27
+ print solution.fetch(:"#{letter}4", " ").to_s + " "
28
+ print solution.fetch(:"#{letter}5", " ").to_s + " "
29
+ print solution.fetch(:"#{letter}6", " ").to_s
30
+ print " | "
31
+ print solution.fetch(:"#{letter}7", " ").to_s + " "
32
+ print solution.fetch(:"#{letter}8", " ").to_s + " "
33
+ print solution.fetch(:"#{letter}9", " ").to_s
34
+ print " |\n"
35
+ end
36
+ puts "+-------+-------+-------+"
37
+ ("G".."I").to_a.each do |letter|
38
+ print "| "
39
+ print solution.fetch(:"#{letter}1", " ").to_s + " "
40
+ print solution.fetch(:"#{letter}2", " ").to_s + " "
41
+ print solution.fetch(:"#{letter}3", " ").to_s
42
+ print " | "
43
+ print solution.fetch(:"#{letter}4", " ").to_s + " "
44
+ print solution.fetch(:"#{letter}5", " ").to_s + " "
45
+ print solution.fetch(:"#{letter}6", " ").to_s
46
+ print " | "
47
+ print solution.fetch(:"#{letter}7", " ").to_s + " "
48
+ print solution.fetch(:"#{letter}8", " ").to_s + " "
49
+ print solution.fetch(:"#{letter}9", " ").to_s
50
+ print " |\n"
51
+ end
52
+ puts "+-------+-------+-------+"
53
+ end
54
+
55
+ module Whatnot
56
+ describe SwitchInterpreter do
57
+ describe "full usage" do
58
+ context "simplest with set, slot, & constraint" do
59
+ let(:expected) do
60
+ [{:B=>2}, {:B=>1}, {:A=>[3], :B=>1}, {:A=>[5], :B=>1}, {:A=>[4], :B=>1}, {:A=>[3, 4], :B=>1}, {:A=>[4, 5], :B=>1}, {:A=>[3, 5], :B=>1}]
61
+ end
62
+
63
+ it "works" do
64
+ i = SwitchInterpreter.new
65
+ i.create_set(:A, [3,4,5], allow_empty: true, max_values: 2)
66
+ i.create_slot(:B, [1,2], allow_empty: false)
67
+ i.create_constraint(:A, :B) do |**sol| sol[:A].nil? || sol[:B] == 1; end
68
+
69
+ solutions = SolutionEnumerator.new(i.method(:interpret), i.dimacs).entries
70
+ expect(solutions).to eq(expected)
71
+ end
72
+ end
73
+
74
+ context "mutually exclusive sets" do
75
+ let(:expected) do
76
+ [{:B=>[3, 4, 5]},
77
+ {:A=>[3], :B=>[4, 5]},
78
+ {:A=>[4], :B=>[3, 5]},
79
+ {:A=>[3, 4, 5]},
80
+ {:A=>[5], :B=>[3, 4]},
81
+ {:A=>[3, 4], :B=>[5]},
82
+ {:A=>[3, 5], :B=>[4]},
83
+ {:A=>[4, 5], :B=>[3]}]
84
+ end
85
+
86
+ it "works" do
87
+ i = SwitchInterpreter.new
88
+ i.create_mutually_exclusive_sets([:A, :B], [3,4,5], allow_empty: true, max_values: 3)
89
+
90
+ solutions = SolutionEnumerator.new(i.method(:interpret), i.dimacs).entries
91
+ expect(solutions).to eq(expected)
92
+ end
93
+ end
94
+ end
95
+
96
+ describe "sudoku" do
97
+ it "works" do
98
+ puts "\nSudoku: "
99
+
100
+ puzzle = {
101
+ :A1 => 8,
102
+ :C2 => 3,
103
+ :D2 => 6,
104
+ :B3 => 7,
105
+ :E3 => 9,
106
+ :G3 => 2,
107
+ :B4 => 5,
108
+ :F4 => 7,
109
+ :E5 => 4,
110
+ :F5 => 5,
111
+ :G5 => 7,
112
+ :D6 => 1,
113
+ :H6 => 3,
114
+ :C7 => 1,
115
+ :H7 => 6,
116
+ :I7 => 8,
117
+ :C8 => 8,
118
+ :D8 => 5,
119
+ :H8 => 1,
120
+ :B9 => 9,
121
+ :G9 => 4
122
+ }
123
+
124
+ print_sudoku(puzzle)
125
+
126
+ i = SwitchInterpreter.new
127
+
128
+ ("A".."I").to_a.each do |letter|
129
+ ('1'..'9').to_a.each do |num|
130
+ i.create_slot(:"#{letter}#{num}", [1,2,3,4,5,6,7,8,9], allow_empty: false)
131
+ end
132
+ end
133
+
134
+ (1..9).to_a.each do |slotnum|
135
+ slots_for_column = ("A".."I").to_a.map { |letter| :"#{letter}#{slotnum}" }
136
+
137
+ slots_for_column.combination(2) do |slot1, slot2|
138
+ i.create_constraint(slot1, slot2) do |**solution|
139
+ solution[slot1] != solution[slot2]
140
+ end
141
+ end
142
+ end
143
+
144
+ ("A".."I").to_a.map(&:to_sym).each do |slotletter|
145
+ slots_for_row = ('1'..'9').to_a.map { |num| :"#{slotletter}#{num}" }
146
+
147
+ slots_for_row.combination(2) do |slot1, slot2|
148
+ i.create_constraint(slot1, slot2) do |**solution|
149
+ solution[slot1] != solution[slot2]
150
+ end
151
+ end
152
+ end
153
+
154
+ squares = [
155
+ ["A1","A2","A3","B1","B2","B3","C1","C2","C3"],
156
+ ["A4","A5","A6","B4","B5","B6","C4","C5","C6"],
157
+ ["A7","A8","A9","B7","B8","B9","C7","C8","C9"],
158
+ ["D1","D2","D3","E1","E2","E3","F1","F2","F3"],
159
+ ["D4","D5","D6","E4","E5","E6","F4","F5","F6"],
160
+ ["D7","D8","D9","E7","E8","E9","F7","F8","F9"],
161
+ ["G1","G2","G3","H1","H2","H3","I1","I2","I3"],
162
+ ["G4","G5","G6","H4","H5","H6","I4","I5","I6"],
163
+ ["G7","G8","G9","H7","H8","H9","I7","I8","I9"]
164
+ ]
165
+
166
+ squares.each do |square|
167
+ slots_for_square = square.map(&:to_sym)
168
+
169
+ slots_for_square.combination(2) do |slot1, slot2|
170
+ i.create_constraint(slot1, slot2) do |**solution|
171
+ solution[slot1] != solution[slot2]
172
+ end
173
+ end
174
+ end
175
+
176
+ puzzle.each do |slot, val|
177
+ i.create_constraint(slot) { |**sol| sol[slot] == val }
178
+ end
179
+
180
+ solution = SolutionEnumerator.new(i.method(:interpret), i.dimacs).first
181
+
182
+ print_sudoku(solution)
183
+
184
+ puts
185
+ end
186
+ end
187
+ end
188
+ end
@@ -0,0 +1,89 @@
1
+ require(File.expand_path("../../lib/whatnot.rb", __FILE__))
2
+
3
+ # This file was generated by the `rails generate rspec:install` command. Conventionally, all
4
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
5
+ # The generated `.rspec` file contains `--require spec_helper` which will cause
6
+ # this file to always be loaded, without a need to explicitly require it in any
7
+ # files.
8
+ #
9
+ # Given that it is always loaded, you are encouraged to keep this file as
10
+ # light-weight as possible. Requiring heavyweight dependencies from this file
11
+ # will add to the boot time of your test suite on EVERY test run, even for an
12
+ # individual file that may not need all of that loaded. Instead, consider making
13
+ # a separate helper file that requires the additional dependencies and performs
14
+ # the additional setup, and require it from the spec files that actually need
15
+ # it.
16
+ #
17
+ # The `.rspec` file also contains a few flags that are not defaults but that
18
+ # users commonly want.
19
+ #
20
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
21
+ RSpec.configure do |config|
22
+ # rspec-expectations config goes here. You can use an alternate
23
+ # assertion/expectation library such as wrong or the stdlib/minitest
24
+ # assertions if you prefer.
25
+ config.expect_with :rspec do |expectations|
26
+ # This option will default to `true` in RSpec 4. It makes the `description`
27
+ # and `failure_message` of custom matchers include text for helper methods
28
+ # defined using `chain`, e.g.:
29
+ # be_bigger_than(2).and_smaller_than(4).description
30
+ # # => "be bigger than 2 and smaller than 4"
31
+ # ...rather than:
32
+ # # => "be bigger than 2"
33
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
34
+ end
35
+
36
+ # rspec-mocks config goes here. You can use an alternate test double
37
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
38
+ config.mock_with :rspec do |mocks|
39
+ # Prevents you from mocking or stubbing a method that does not exist on
40
+ # a real object. This is generally recommended, and will default to
41
+ # `true` in RSpec 4.
42
+ mocks.verify_partial_doubles = true
43
+ end
44
+
45
+ # The settings below are suggested to provide a good initial experience
46
+ # with RSpec, but feel free to customize to your heart's content.
47
+ =begin
48
+ # These two settings work together to allow you to limit a spec run
49
+ # to individual examples or groups you care about by tagging them with
50
+ # `:focus` metadata. When nothing is tagged with `:focus`, all examples
51
+ # get run.
52
+ config.filter_run :focus
53
+ config.run_all_when_everything_filtered = true
54
+
55
+ # Limits the available syntax to the non-monkey patched syntax that is
56
+ # recommended. For more details, see:
57
+ # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
58
+ # - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
59
+ # - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching
60
+ config.disable_monkey_patching!
61
+
62
+ # Many RSpec users commonly either run the entire suite or an individual
63
+ # file, and it's useful to allow more verbose output when running an
64
+ # individual spec file.
65
+ if config.files_to_run.one?
66
+ # Use the documentation formatter for detailed output,
67
+ # unless a formatter has already been configured
68
+ # (e.g. via a command-line flag).
69
+ config.default_formatter = 'doc'
70
+ end
71
+
72
+ # Print the 10 slowest examples and example groups at the
73
+ # end of the spec run, to help surface which specs are running
74
+ # particularly slow.
75
+ config.profile_examples = 10
76
+
77
+ # Run specs in random order to surface order dependencies. If you find an
78
+ # order dependency and want to debug it, you can fix the order by providing
79
+ # the seed, which is printed after each run.
80
+ # --seed 1234
81
+ config.order = :random
82
+
83
+ # Seed global randomization in this process using the `--seed` CLI option.
84
+ # Setting this allows you to use `--seed` to deterministically reproduce
85
+ # test failures related to randomization by passing the same `--seed` value
86
+ # as the one that triggered the failure.
87
+ Kernel.srand config.seed
88
+ =end
89
+ end