gecoder 0.8.2 → 0.8.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. data/CHANGES +14 -0
  2. data/ext/gecoder.cpp +181 -0
  3. data/ext/gecoder.h +94 -0
  4. data/ext/vararray.cpp +3 -3
  5. data/lib/gecoder/bindings/bindings.rb +104 -46
  6. data/lib/gecoder/interface/binding_changes.rb +1 -301
  7. data/lib/gecoder/interface/branch.rb +15 -11
  8. data/lib/gecoder/interface/constraints/bool/boolean.rb +56 -52
  9. data/lib/gecoder/interface/constraints/bool/channel.rb +1 -16
  10. data/lib/gecoder/interface/constraints/bool_enum/channel.rb +13 -8
  11. data/lib/gecoder/interface/constraints/bool_enum/extensional.rb +48 -0
  12. data/lib/gecoder/interface/constraints/extensional_regexp.rb +101 -0
  13. data/lib/gecoder/interface/constraints/int/channel.rb +1 -13
  14. data/lib/gecoder/interface/constraints/int_enum/channel.rb +15 -35
  15. data/lib/gecoder/interface/constraints/int_enum/extensional.rb +130 -0
  16. data/lib/gecoder/interface/constraints/set/channel.rb +54 -0
  17. data/lib/gecoder/interface/constraints/set_enum/channel.rb +37 -6
  18. data/lib/gecoder/interface/constraints/set_var_constraints.rb +1 -0
  19. data/lib/gecoder/interface/constraints.rb +38 -0
  20. data/lib/gecoder/interface/model.rb +110 -85
  21. data/lib/gecoder/interface/variables.rb +3 -21
  22. data/lib/gecoder/version.rb +1 -1
  23. data/specs/branch.rb +16 -1
  24. data/specs/constraints/bool_enum_relation.rb +6 -6
  25. data/specs/constraints/boolean.rb +31 -25
  26. data/specs/constraints/channel.rb +102 -4
  27. data/specs/constraints/extensional.rb +185 -2
  28. data/specs/constraints/reification_sugar.rb +2 -46
  29. data/specs/model.rb +85 -7
  30. data/tasks/dependencies.txt +1 -0
  31. data/vendor/rust/rust/class.rb +33 -35
  32. data/vendor/rust/rust/templates/ClassDeclarations.rusttpl +1 -1
  33. data/vendor/rust/rust/templates/CxxClassDefinitions.rusttpl +10 -1
  34. metadata +185 -184
  35. data/example/raw_bindings.rb +0 -44
  36. data/ext/missing.cpp +0 -328
  37. data/ext/missing.h +0 -120
  38. data/specs/binding_changes.rb +0 -76
@@ -1,117 +1,109 @@
1
1
  module Gecode
2
2
  # Model is the base class that all models must inherit from.
3
3
  class Model
4
+ # The largest integer allowed in the domain of an integer variable.
5
+ MAX_INT = Gecode::Raw::IntLimits::MAX
6
+ # The smallest integer allowed in the domain of an integer variable.
7
+ MIN_INT = Gecode::Raw::IntLimits::MIN
8
+
9
+ # The largest integer allowed in the domain of a set variable.
10
+ SET_MAX_INT = Gecode::Raw::SetLimits::MAX
11
+ # The smallest integer allowed in the domain of a set variable.
12
+ SET_MIN_INT = Gecode::Raw::SetLimits::MIN
13
+
14
+ # The largest possible domain for an integer variable.
15
+ LARGEST_INT_DOMAIN = MIN_INT..MAX_INT
16
+ # The largest possible domain, without negative integers, for an
17
+ # integer variable.
18
+ NON_NEGATIVE_INT_DOMAIN = 0..MAX_INT
19
+
20
+ # The largest possible bound for a set variable.
21
+ LARGEST_SET_BOUND = SET_MIN_INT..SET_MAX_INT
22
+
4
23
  # Creates a new integer variable with the specified domain. The domain can
5
24
  # either be a range, a single element, or an enumeration of elements. If no
6
25
  # domain is specified then the largest possible domain is used.
7
- def int_var(domain =
8
- Gecode::Raw::IntLimits::MIN..Gecode::Raw::IntLimits::MAX)
9
- enum = domain_enum(domain)
10
- index = variable_creation_space.new_int_vars(enum).first
11
- FreeIntVar.new(self, index)
26
+ def int_var(domain = LARGEST_INT_DOMAIN)
27
+ args = domain_arguments(domain)
28
+ FreeIntVar.new(self, variable_creation_space.new_int_var(*args))
12
29
  end
13
30
 
14
31
  # Creates an array containing the specified number of integer variables
15
32
  # with the specified domain. The domain can either be a range, a single
16
- # element, or an enumeration of elements.
17
- def int_var_array(count, domain)
18
- enum = domain_enum(domain)
19
- variables = []
20
- variable_creation_space.new_int_vars(enum, count).each do |index|
21
- variables << FreeIntVar.new(self, index)
33
+ # element, or an enumeration of elements. If no domain is specified then
34
+ # the largest possible domain is used.
35
+ def int_var_array(count, domain = LARGEST_INT_DOMAIN)
36
+ args = domain_arguments(domain)
37
+ build_var_array(count) do
38
+ FreeIntVar.new(self, variable_creation_space.new_int_var(*args))
22
39
  end
23
- return wrap_enum(variables)
24
40
  end
25
41
 
26
42
  # Creates a matrix containing the specified number rows and columns of
27
43
  # integer variables with the specified domain. The domain can either be a
28
- # range, a single element, or an enumeration of elements.
29
- def int_var_matrix(row_count, col_count, domain)
30
- enum = domain_enum(domain)
31
- indices = variable_creation_space.new_int_vars(enum, row_count*col_count)
32
- rows = []
33
- row_count.times do |i|
34
- rows << indices[(i*col_count)...(i.succ*col_count)].map! do |index|
35
- FreeIntVar.new(self, index)
36
- end
44
+ # range, a single element, or an enumeration of elements. If no domain
45
+ # is specified then the largest possible domain is used.
46
+ def int_var_matrix(row_count, col_count, domain = LARGEST_INT_DOMAIN)
47
+ args = domain_arguments(domain)
48
+ build_var_matrix(row_count, col_count) do
49
+ FreeIntVar.new(self, variable_creation_space.new_int_var(*args))
37
50
  end
38
- return wrap_enum(Util::EnumMatrix.rows(rows, false))
39
51
  end
40
52
 
41
53
  # Creates a new boolean variable.
42
54
  def bool_var
43
- index = variable_creation_space.new_bool_vars.first
44
- FreeBoolVar.new(self, index)
55
+ FreeBoolVar.new(self, variable_creation_space.new_bool_var)
45
56
  end
46
57
 
47
58
  # Creates an array containing the specified number of boolean variables.
48
59
  def bool_var_array(count)
49
- variables = []
50
- variable_creation_space.new_bool_vars(count).each do |index|
51
- variables << FreeBoolVar.new(self, index)
60
+ build_var_array(count) do
61
+ FreeBoolVar.new(self, variable_creation_space.new_bool_var)
52
62
  end
53
- return wrap_enum(variables)
54
63
  end
55
64
 
56
65
  # Creates a matrix containing the specified number rows and columns of
57
66
  # boolean variables.
58
67
  def bool_var_matrix(row_count, col_count)
59
- indices = variable_creation_space.new_bool_vars(row_count*col_count)
60
- rows = []
61
- row_count.times do |i|
62
- rows << indices[(i*col_count)...(i.succ*col_count)].map! do |index|
63
- FreeBoolVar.new(self, index)
64
- end
68
+ build_var_matrix(row_count, col_count) do
69
+ FreeBoolVar.new(self, variable_creation_space.new_bool_var)
65
70
  end
66
- return wrap_enum(Util::EnumMatrix.rows(rows, false))
67
71
  end
68
72
 
69
73
  # Creates a set variable with the specified domain for greatest lower bound
70
74
  # and least upper bound (specified as either a fixnum, range or enum). If
71
- # no bounds are specified then the empty set is used as greates lower bound
72
- # and the universe as least upper bound. A range for the allowed cardinality
73
- # of the set can also be specified, if none is specified, or nil is given,
74
- # then the default range (anything) will be used. If only a single Fixnum is
75
+ # no bounds are specified then the empty set is used as greatest lower
76
+ # bound and the largest possible set as least upper bound.
77
+ #
78
+ # A range for the allowed cardinality of the set can also be
79
+ # specified, if none is specified, or nil is given, then the default
80
+ # range (anything) will be used. If only a single Fixnum is
75
81
  # specified as cardinality_range then it's used as lower bound.
76
- def set_var(glb_domain = [], lub_domain =
77
- Gecode::Raw::SetLimits::MIN..Gecode::Raw::SetLimits::MAX,
82
+ def set_var(glb_domain = [], lub_domain = LARGEST_SET_BOUND,
78
83
  cardinality_range = nil)
79
- check_set_bounds(glb_domain, lub_domain)
80
-
81
- index = variable_creation_space.new_set_vars(glb_domain, lub_domain,
82
- to_set_cardinality_range(cardinality_range)).first
83
- FreeSetVar.new(self, index)
84
+ args = set_bounds_to_parameters(glb_domain, lub_domain, cardinality_range)
85
+ FreeSetVar.new(self, variable_creation_space.new_set_var(*args))
84
86
  end
85
87
 
86
88
  # Creates an array containing the specified number of set variables. The
87
89
  # parameters beyond count are the same as for #set_var .
88
- def set_var_array(count, glb_domain, lub_domain, cardinality_range = nil)
89
- check_set_bounds(glb_domain, lub_domain)
90
-
91
- variables = []
92
- variable_creation_space.new_set_vars(glb_domain, lub_domain,
93
- to_set_cardinality_range(cardinality_range), count).each do |index|
94
- variables << FreeSetVar.new(self, index)
90
+ def set_var_array(count, glb_domain = [], lub_domain = LARGEST_SET_BOUND,
91
+ cardinality_range = nil)
92
+ args = set_bounds_to_parameters(glb_domain, lub_domain, cardinality_range)
93
+ build_var_array(count) do
94
+ FreeSetVar.new(self, variable_creation_space.new_set_var(*args))
95
95
  end
96
- return wrap_enum(variables)
97
96
  end
98
97
 
99
98
  # Creates a matrix containing the specified number of rows and columns
100
99
  # filled with set variables. The parameters beyond row and column counts are
101
100
  # the same as for #set_var .
102
- def set_var_matrix(row_count, col_count, glb_domain, lub_domain,
103
- cardinality_range = nil)
104
- check_set_bounds(glb_domain, lub_domain)
105
-
106
- indices = variable_creation_space.new_set_vars(glb_domain, lub_domain,
107
- to_set_cardinality_range(cardinality_range), row_count*col_count)
108
- rows = []
109
- row_count.times do |i|
110
- rows << indices[(i*col_count)...(i.succ*col_count)].map! do |index|
111
- FreeSetVar.new(self, index)
112
- end
101
+ def set_var_matrix(row_count, col_count, glb_domain = [],
102
+ lub_domain = LARGEST_SET_BOUND, cardinality_range = nil)
103
+ args = set_bounds_to_parameters(glb_domain, lub_domain, cardinality_range)
104
+ build_var_matrix(row_count, col_count) do
105
+ FreeSetVar.new(self, variable_creation_space.new_set_var(*args))
113
106
  end
114
- return wrap_enum(Util::EnumMatrix.rows(rows, false))
115
107
  end
116
108
 
117
109
  # Retrieves the currently used space. Calling this method is only allowed
@@ -176,20 +168,49 @@ module Gecode
176
168
 
177
169
  private
178
170
 
179
- # Returns an enumeration of the specified domain arguments, which can
180
- # either be given as a range, a single number, or an enumerable of elements.
181
- def domain_enum(domain)
171
+ # Creates an array containing the specified number of variables, all
172
+ # constructed using the provided block..
173
+ def build_var_array(count, &block)
174
+ variables = []
175
+ count.times do
176
+ variables << yield
177
+ end
178
+ return wrap_enum(variables)
179
+ end
180
+
181
+ # Creates a matrix containing the specified number rows and columns of
182
+ # variables, all constructed using the provided block.
183
+ def build_var_matrix(row_count, col_count, &block)
184
+ rows = []
185
+ row_count.times do |i|
186
+ row = []
187
+ col_count.times do |j|
188
+ row << yield
189
+ end
190
+ rows << row
191
+ end
192
+ return wrap_enum(Util::EnumMatrix.rows(rows, false))
193
+ end
194
+
195
+ # Returns the array of arguments that correspond to the specified
196
+ # domain when given to Gecode. The domain can be given as a range,
197
+ # a single number, or an enumerable of elements.
198
+ def domain_arguments(domain)
182
199
  if domain.respond_to?(:first) and domain.respond_to?(:last) and
183
200
  domain.respond_to?(:exclude_end?)
184
201
  if domain.exclude_end?
185
- return domain.first..(domain.last - 1)
202
+ return [domain.first, (domain.last - 1)]
186
203
  else
187
- return domain
204
+ return [domain.first, domain.last]
188
205
  end
189
206
  elsif domain.kind_of? Enumerable
190
- return domain
207
+ array = domain.to_a
208
+ return [Gecode::Raw::IntSet.new(array, array.size)]
209
+ elsif domain.kind_of? Fixnum
210
+ return [domain, domain]
191
211
  else
192
- return domain..domain
212
+ raise TypeError, 'The domain must be given as an instance of ' +
213
+ "Enumerable or Fixnum, but #{domain.class} was given."
193
214
  end
194
215
  end
195
216
 
@@ -204,6 +225,21 @@ module Gecode
204
225
  end
205
226
  end
206
227
 
228
+ # Converts the specified set var domain to parameters accepted by
229
+ # Gecode. The bounds can be specified as a fixnum, range or # enum.
230
+ # The parameters are returned as an array.
231
+ def set_bounds_to_parameters(glb_domain, lub_domain, cardinality_range)
232
+ check_set_bounds(glb_domain, lub_domain)
233
+ args = []
234
+ args << Gecode::Constraints::Util.constant_set_to_int_set(glb_domain)
235
+ args << Gecode::Constraints::Util.constant_set_to_int_set(lub_domain)
236
+ card_range = to_set_cardinality_range(cardinality_range)
237
+ if card_range.nil?
238
+ card_range = 0..Gecode::Raw::SetLimits::CARD
239
+ end
240
+ args << card_range.first << card_range.last
241
+ end
242
+
207
243
  # Checks whether the specified greatest lower bound is a subset of least
208
244
  # upper bound. Raises ArgumentError if that is not the case.
209
245
  def check_set_bounds(glb, lub)
@@ -243,15 +279,6 @@ module Gecode
243
279
  @variable_creation_space || selected_space
244
280
  end
245
281
 
246
- # Refreshes all cached variables. This should be called if the variables
247
- # in an existing space were changed.
248
- def refresh_variables
249
- return if @variables.nil?
250
- @variables.each do |variable|
251
- variable.refresh if variable.cached?
252
- end
253
- end
254
-
255
282
  # Executes any interactions with Gecode still waiting in the queue
256
283
  # (emptying the queue) in the process.
257
284
  def perform_queued_gecode_interactions
@@ -266,8 +293,6 @@ module Gecode
266
293
  # assigned directly.
267
294
  def active_space=(new_space)
268
295
  @active_space = new_space
269
- new_space.refresh
270
- refresh_variables
271
296
  end
272
297
  end
273
- end
298
+ end
@@ -8,21 +8,9 @@ module Gecode
8
8
  def initialize(model, index)
9
9
  @model = model
10
10
  @index = index
11
- @bound_space = @bound_var = nil
12
11
  model.track_variable(self)
13
12
  end
14
-
15
- # Checks whether the variable is cached, i.e. whether it needs to be
16
- # rebound after changes to a space.
17
- def cached?
18
- not @bound_space.nil?
19
- end
20
-
21
- # Forces the variable to refresh itself.
22
- def refresh
23
- @bound_space = nil
24
- end
25
-
13
+
26
14
  def inspect
27
15
  if assigned?
28
16
  "#<#{self.class} #{domain}>"
@@ -54,13 +42,7 @@ module Gecode
54
42
  # Binds the int variable to the currently active space of the model,
55
43
  # returning the bound int variable.
56
44
  def bind
57
- space = active_space
58
- unless @bound_space == space
59
- # We have not bound the variable to this space, so we do it now.
60
- @bound = space.method(:#{space_bind_method}).call(@index)
61
- @bound_space = space
62
- end
63
- return @bound
45
+ active_space.method(:#{space_bind_method}).call(@index)
64
46
  end
65
47
 
66
48
  private
@@ -251,4 +233,4 @@ module Gecode
251
233
  end
252
234
  end
253
235
  end
254
- end
236
+ end
@@ -1,4 +1,4 @@
1
1
  module GecodeR
2
2
  # A string representation of the Gecode/R version.
3
- VERSION = '0.8.2'
3
+ VERSION = '0.8.3'
4
4
  end
data/specs/branch.rb CHANGED
@@ -37,6 +37,16 @@ describe Gecode::Model, ' (integer branch)' do
37
37
  @model.solve!.bools.each{ |var| var.should be_assigned }
38
38
  end
39
39
 
40
+ it 'should allow branching on a single integer variable' do
41
+ @model.branch_on @vars.first
42
+ @model.solve!.vars.first.should be_assigned
43
+ end
44
+
45
+ it 'should allow branching on a single boolean variable' do
46
+ @model.branch_on @bools.first
47
+ @model.solve!.bools.first.should be_assigned
48
+ end
49
+
40
50
  supported_var_selectors = {
41
51
  :none => Gecode::Raw::INT_VAR_NONE,
42
52
  :smallest_min => Gecode::Raw::INT_VAR_MIN_MIN,
@@ -120,6 +130,11 @@ describe Gecode::Model, ' (set branch)' do
120
130
  @model.branch_on @sets
121
131
  @model.solve!.sets.each{ |var| var.should be_assigned }
122
132
  end
133
+
134
+ it 'should allow branching on a single set variable' do
135
+ @model.branch_on @sets.first
136
+ @model.solve!.sets.first.should be_assigned
137
+ end
123
138
 
124
139
  supported_var_selectors = {
125
140
  :none => Gecode::Raw::SET_VAR_NONE,
@@ -167,4 +182,4 @@ describe Gecode::Model, ' (set branch)' do
167
182
  @model.branch_on @sets, :foo => 5
168
183
  end.should raise_error(ArgumentError)
169
184
  end
170
- end
185
+ end
@@ -15,8 +15,8 @@ class BoolEnumSampleProblem < Gecode::Model
15
15
  end
16
16
  end
17
17
 
18
- # Expects @stub, which contains the started constraint and @compute_result which
19
- # computes whether the left hand side is true or not.
18
+ # Expects @stub, which contains the started constraint and @compute_result
19
+ # which computes whether the left hand side is true or not.
20
20
  describe 'bool enum relation constraint', :shared => true do
21
21
  it 'should handle being constrained to be true' do
22
22
  @stub.must_be.true
@@ -76,7 +76,7 @@ describe Gecode::Constraints::BoolEnum::Relation, ' (conjunction)' do
76
76
 
77
77
  # For constraint option spec.
78
78
  @invoke_options = lambda do |hash|
79
- @bools.conjunction.must_be.equal_to(true, hash)
79
+ @bools.conjunction.must.equal(@b1, hash)
80
80
  @model.solve!
81
81
  end
82
82
  @expect_options = option_expectation do |strength, kind, reif_var|
@@ -94,7 +94,7 @@ describe Gecode::Constraints::BoolEnum::Relation, ' (conjunction)' do
94
94
  an_instance_of(Gecode::Raw::BoolVar),
95
95
  anything,
96
96
  an_instance_of(Gecode::Raw::BoolVar),
97
- anything, anything, anything)
97
+ anything, anything)
98
98
  Gecode::Raw.should_receive(:rel).once.with(
99
99
  an_instance_of(Gecode::Raw::Space),
100
100
  Gecode::Raw::BOT_AND,
@@ -122,7 +122,7 @@ describe Gecode::Constraints::BoolEnum::Relation, ' (disjunction)' do
122
122
 
123
123
  # For constraint option spec.
124
124
  @invoke_options = lambda do |hash|
125
- @bools.disjunction.must_be.equal_to(true, hash)
125
+ @bools.disjunction.must.equal(@b1, hash)
126
126
  @model.solve!
127
127
  end
128
128
  @expect_options = option_expectation do |strength, kind, reif_var|
@@ -140,7 +140,7 @@ describe Gecode::Constraints::BoolEnum::Relation, ' (disjunction)' do
140
140
  an_instance_of(Gecode::Raw::BoolVar),
141
141
  anything,
142
142
  an_instance_of(Gecode::Raw::BoolVar),
143
- anything, anything, anything)
143
+ anything, anything)
144
144
  Gecode::Raw.should_receive(:rel).once.with(
145
145
  an_instance_of(Gecode::Raw::Space),
146
146
  Gecode::Raw::BOT_OR,
@@ -22,28 +22,22 @@ describe Gecode::Constraints::Bool do
22
22
 
23
23
  # For constraint option spec.
24
24
  @invoke_options = lambda do |hash|
25
- (@b1 | @b2).must_be.equal_to(true, hash)
25
+ (@b1 | @b2).must_be.true(hash)
26
26
  @model.solve!
27
27
  end
28
28
  @expect_options = option_expectation do |strength, kind, reif_var|
29
29
  @model.allow_space_access do
30
- Gecode::Raw.should_receive(:rel).once.with(
31
- an_instance_of(Gecode::Raw::Space),
32
- an_instance_of(Gecode::Raw::BoolVar),
33
- Gecode::Raw::BOT_OR,
34
- an_instance_of(Gecode::Raw::BoolVar),
35
- an_instance_of(Gecode::Raw::BoolVar),
36
- Gecode::Raw::ICL_DEF, Gecode::Raw::PK_DEF)
30
+ # We only test the non-MiniModel parts.
37
31
  unless reif_var.nil?
38
32
  Gecode::Raw.should_receive(:rel).once.with(
39
33
  an_instance_of(Gecode::Raw::Space),
40
- an_instance_of(Gecode::Raw::BoolVar), Gecode::Raw::BOT_EQV,
41
- an_instance_of(Gecode::Raw::BoolVar), 1, strength, kind)
34
+ an_instance_of(Gecode::Raw::BoolVar), Gecode::Raw::IRT_EQ,
35
+ an_instance_of(Gecode::Raw::BoolVar), strength, kind)
42
36
  end
43
37
  end
44
38
  end
45
39
  end
46
-
40
+
47
41
  it 'should handle single variables constrainted to be true' do
48
42
  @b1.must_be.true
49
43
  b1 = @model.solve!.b1
@@ -79,7 +73,7 @@ describe Gecode::Constraints::Bool do
79
73
  sol.b1.value.should_not be_true
80
74
  sol.b2.value.should be_true
81
75
  end
82
-
76
+
83
77
  it 'should handle negated disjunction' do
84
78
  @b1.must_be.false
85
79
  (@b1 | @b2).must_not_be.true
@@ -87,7 +81,7 @@ describe Gecode::Constraints::Bool do
87
81
  sol.b1.value.should_not be_true
88
82
  sol.b2.value.should_not be_true
89
83
  end
90
-
84
+
91
85
  it 'should handle conjunction' do
92
86
  (@b1 & @b2).must_be.true
93
87
  sol = @model.solve!
@@ -142,7 +136,7 @@ describe Gecode::Constraints::Bool do
142
136
  sol.b1.value.should_not be_true
143
137
  sol.b2.value.should_not be_true
144
138
  end
145
-
139
+
146
140
  it 'should handle imply after must_not' do
147
141
  @b1.must_be.true
148
142
  @b1.must_not.imply @b2
@@ -166,7 +160,7 @@ describe Gecode::Constraints::Bool do
166
160
  sol.b1.value.should be_true
167
161
  sol.b2.value.should_not be_true
168
162
  end
169
-
163
+
170
164
  it 'should handle expressions as right hand side' do
171
165
  @b1.must == (@b2 | @b3)
172
166
  @b2.must_be.true
@@ -174,7 +168,7 @@ describe Gecode::Constraints::Bool do
174
168
  sol.b1.value.should be_true
175
169
  sol.b2.value.should be_true
176
170
  end
177
-
171
+
178
172
  it 'should handle nested expressions as left hand side' do
179
173
  ((@b1 & @b2) | @b3 | (@b1 & @b3)).must_be.true
180
174
  @b1.must_be.false
@@ -182,7 +176,7 @@ describe Gecode::Constraints::Bool do
182
176
  sol.b1.value.should_not be_true
183
177
  sol.b3.value.should be_true
184
178
  end
185
-
179
+
186
180
  it 'should handle nested expressions on both side' do
187
181
  ((@b1 & @b1) | @b3).must == ((@b1 & @b3) & @b2)
188
182
  @b1.must_be.true
@@ -191,7 +185,15 @@ describe Gecode::Constraints::Bool do
191
185
  sol.b2.value.should be_true
192
186
  sol.b3.value.should be_true
193
187
  end
194
-
188
+
189
+ it 'should handle nested expressions with implication' do
190
+ ((@b1 & @b1) | @b3).must.imply(@b1 ^ @b2)
191
+ @b1.must_be.true
192
+ sol = @model.solve!
193
+ sol.b1.value.should be_true
194
+ sol.b2.value.should be_false
195
+ end
196
+
195
197
  it 'should handle nested expressions containing exclusive or' do
196
198
  ((@b1 ^ @b1) & @b3).must == ((@b2 | @b3) ^ @b2)
197
199
  @b1.must_be.true
@@ -201,7 +203,7 @@ describe Gecode::Constraints::Bool do
201
203
  sol.b2.value.should_not be_true
202
204
  sol.b3.value.should_not be_true
203
205
  end
204
-
206
+
205
207
  it 'should handle nested expressions on both sides with negation' do
206
208
  ((@b1 & @b1) | @b3).must_not == ((@b1 | @b3) & @b2)
207
209
  @b1.must_be.true
@@ -211,7 +213,7 @@ describe Gecode::Constraints::Bool do
211
213
  sol.b2.value.should_not be_true
212
214
  sol.b3.value.should be_true
213
215
  end
214
-
216
+
215
217
  it 'should translate reification with a variable right hand side' do
216
218
  @b1.must_be.equal_to(@b2, :reify => @b3)
217
219
  @b1.must_be.true
@@ -219,8 +221,8 @@ describe Gecode::Constraints::Bool do
219
221
  sol = @model.solve!
220
222
  sol.b3.value.should_not be_true
221
223
  end
222
-
223
- it 'should translate reification with a variable right hand side and negation' do
224
+
225
+ it 'should translate reification with a variable right hand side and negation' do
224
226
  @b1.must_not_be.equal_to(@b2, :reify => @b3)
225
227
  @b1.must_be.true
226
228
  @b2.must_be.false
@@ -228,9 +230,13 @@ describe Gecode::Constraints::Bool do
228
230
  sol.b3.value.should be_true
229
231
  end
230
232
 
231
- it 'should raise error on right hand sides of the wrong type' do
233
+ it 'should raise error on right hand sides of incorrect type given to #==' do
232
234
  lambda{ @b1.must == 'hello' }.should raise_error(TypeError)
233
235
  end
234
-
236
+
237
+ it 'should raise error on right hand sides of incorrect type given to #imply' do
238
+ lambda{ @b1.must.imply 'hello' }.should raise_error(TypeError)
239
+ end
240
+
235
241
  it_should_behave_like 'reifiable constraint'
236
- end
242
+ end