gecoder 0.8.2 → 0.8.3

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.
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