gecoder-with-gecode 0.8.2-mswin32 → 0.8.3-mswin32
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/CHANGES +14 -0
- data/lib/gecode.dll +0 -0
- data/lib/gecoder/bindings/bindings.rb +104 -46
- data/lib/gecoder/interface/binding_changes.rb +1 -301
- data/lib/gecoder/interface/branch.rb +15 -11
- data/lib/gecoder/interface/constraints.rb +38 -0
- data/lib/gecoder/interface/constraints/bool/boolean.rb +56 -52
- data/lib/gecoder/interface/constraints/bool/channel.rb +1 -16
- data/lib/gecoder/interface/constraints/bool_enum/channel.rb +13 -8
- data/lib/gecoder/interface/constraints/bool_enum/extensional.rb +48 -0
- data/lib/gecoder/interface/constraints/extensional_regexp.rb +101 -0
- data/lib/gecoder/interface/constraints/int/channel.rb +1 -13
- data/lib/gecoder/interface/constraints/int_enum/channel.rb +15 -35
- data/lib/gecoder/interface/constraints/int_enum/extensional.rb +130 -0
- data/lib/gecoder/interface/constraints/set/channel.rb +54 -0
- data/lib/gecoder/interface/constraints/set_enum/channel.rb +37 -6
- data/lib/gecoder/interface/constraints/set_var_constraints.rb +1 -0
- data/lib/gecoder/interface/model.rb +110 -85
- data/lib/gecoder/interface/variables.rb +3 -21
- data/lib/gecoder/version.rb +1 -1
- data/specs/branch.rb +16 -1
- data/specs/constraints/bool_enum_relation.rb +6 -6
- data/specs/constraints/boolean.rb +31 -25
- data/specs/constraints/channel.rb +102 -4
- data/specs/constraints/extensional.rb +185 -2
- data/specs/constraints/reification_sugar.rb +2 -46
- data/specs/model.rb +85 -7
- data/tasks/dependencies.txt +1 -0
- data/vendor/rust/rust/class.rb +33 -35
- data/vendor/rust/rust/templates/ClassDeclarations.rusttpl +1 -1
- data/vendor/rust/rust/templates/CxxClassDefinitions.rusttpl +10 -1
- metadata +186 -185
- data/example/raw_bindings.rb +0 -44
- data/specs/binding_changes.rb +0 -76
@@ -1,13 +1,14 @@
|
|
1
1
|
module Gecode
|
2
2
|
class Model
|
3
|
-
# Specifies which variables that should be branched on
|
4
|
-
#
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
3
|
+
# Specifies which variables that should be branched on (given as an
|
4
|
+
# enum of variables or as a single variable). One can optionally
|
5
|
+
# also select which of the variables that should be used first with
|
6
|
+
# the :variable option and which value in that variable's domain
|
7
|
+
# that should be used with the :value option. If nothing is
|
8
|
+
# specified then :variable uses :none and value uses :min.
|
8
9
|
#
|
9
|
-
# The following values can be used with :variable for integer and
|
10
|
-
# enums:
|
10
|
+
# The following values can be used with :variable for integer and
|
11
|
+
# boolean enums:
|
11
12
|
# [:none] The first unassigned variable.
|
12
13
|
# [:smallest_min] The one with the smallest minimum.
|
13
14
|
# [:largest_min] The one with the largest minimum.
|
@@ -58,10 +59,13 @@ module Gecode
|
|
58
59
|
# elements
|
59
60
|
#
|
60
61
|
# The following values can be used with :value set enums:
|
61
|
-
# enums:
|
62
62
|
# [:min] Selects the smallest value in the unknown part of the set.
|
63
63
|
# [:max] Selects the largest value in the unknown part of the set.
|
64
64
|
def branch_on(variables, options = {})
|
65
|
+
if variables.respond_to? :bind
|
66
|
+
variables = wrap_enum [variables]
|
67
|
+
end
|
68
|
+
|
65
69
|
if variables.respond_to? :to_int_var_array or
|
66
70
|
variables.respond_to? :to_bool_var_array
|
67
71
|
add_branch(variables, options, Constants::BRANCH_INT_VAR_CONSTANTS,
|
@@ -78,8 +82,8 @@ module Gecode
|
|
78
82
|
|
79
83
|
# This is a hack to get RDoc to ignore the constants.
|
80
84
|
module Constants #:nodoc:
|
81
|
-
# Maps the names of the supported variable branch strategies for
|
82
|
-
# booleans to the corresponding constant in Gecode.
|
85
|
+
# Maps the names of the supported variable branch strategies for
|
86
|
+
# integer and booleans to the corresponding constant in Gecode.
|
83
87
|
BRANCH_INT_VAR_CONSTANTS = {
|
84
88
|
:none => Gecode::Raw::INT_VAR_NONE,
|
85
89
|
:smallest_min => Gecode::Raw::INT_VAR_MIN_MIN,
|
@@ -149,4 +153,4 @@ module Gecode
|
|
149
153
|
end
|
150
154
|
end
|
151
155
|
end
|
152
|
-
end
|
156
|
+
end
|
@@ -271,6 +271,43 @@ module Gecode
|
|
271
271
|
|
272
272
|
private
|
273
273
|
|
274
|
+
# Provides commutivity for the constraint with the specified method name.
|
275
|
+
# If the method with the specified method name is called with something
|
276
|
+
# that, when given to the block, evaluates to true, then the constraint
|
277
|
+
# will be called on the right hand side with the left hand side as
|
278
|
+
# argument.
|
279
|
+
#
|
280
|
+
# The original constraint method is assumed to take two arguments: a
|
281
|
+
# right hand side and a hash of options.
|
282
|
+
def self.provide_commutivity(constraint_name, &block)
|
283
|
+
unique_id = constraint_name.to_sym.to_i
|
284
|
+
pre_alias_method_name = 'pre_commutivity_' << unique_id.to_s
|
285
|
+
if method_defined? constraint_name
|
286
|
+
alias_method pre_alias_method_name, constraint_name
|
287
|
+
end
|
288
|
+
|
289
|
+
module_eval <<-end_code
|
290
|
+
@@commutivity_check_#{unique_id} = block
|
291
|
+
def #{constraint_name}(rhs, options = {})
|
292
|
+
if @@commutivity_check_#{unique_id}.call(rhs, options)
|
293
|
+
if @params[:negate]
|
294
|
+
rhs.must_not.method(:#{constraint_name}).call(
|
295
|
+
@params[:lhs], options)
|
296
|
+
else
|
297
|
+
rhs.must.method(:#{constraint_name}).call(
|
298
|
+
@params[:lhs], options)
|
299
|
+
end
|
300
|
+
else
|
301
|
+
if self.class.method_defined? :#{pre_alias_method_name}
|
302
|
+
#{pre_alias_method_name}(rhs, options)
|
303
|
+
else
|
304
|
+
raise TypeError, \"Unexpected argument type \#{rhs.class}.\"
|
305
|
+
end
|
306
|
+
end
|
307
|
+
end
|
308
|
+
end_code
|
309
|
+
end
|
310
|
+
|
274
311
|
# Creates aliases for any defined comparison methods.
|
275
312
|
def self.alias_comparison_methods
|
276
313
|
Gecode::Constraints::Util::COMPARISON_ALIASES.each_pair do |orig, aliases|
|
@@ -472,3 +509,4 @@ require 'gecoder/interface/constraints/bool_var_constraints'
|
|
472
509
|
require 'gecoder/interface/constraints/bool_enum_constraints'
|
473
510
|
require 'gecoder/interface/constraints/set_var_constraints'
|
474
511
|
require 'gecoder/interface/constraints/set_enum_constraints'
|
512
|
+
require 'gecoder/interface/constraints/extensional_regexp'
|
@@ -26,16 +26,15 @@ module Gecode
|
|
26
26
|
# Describes a boolean expression (following after must*).
|
27
27
|
class Expression #:nodoc:
|
28
28
|
def ==(expression, options = {})
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
29
|
+
if expression.kind_of? Gecode::Constraints::Int::Linear::ExpressionTree
|
30
|
+
return expression.must == @params[:lhs]
|
31
|
+
end
|
32
|
+
unless expression.respond_to? :to_minimodel_bool_expr
|
33
|
+
expression = Constraints::Bool::ExpressionNode.new(expression, @model)
|
34
34
|
end
|
35
|
-
|
36
35
|
@params.update Gecode::Constraints::Util.decode_options(options)
|
37
|
-
@
|
38
|
-
|
36
|
+
@params.update(:lhs => @params[:lhs], :rhs => expression)
|
37
|
+
@model.add_constraint BooleanConstraint.new(@model, @params)
|
39
38
|
end
|
40
39
|
alias_comparison_methods
|
41
40
|
|
@@ -47,14 +46,14 @@ module Gecode
|
|
47
46
|
end
|
48
47
|
|
49
48
|
# Constrains the boolean expression to be true.
|
50
|
-
def true
|
51
|
-
@params.update Gecode::Constraints::Util.decode_options(
|
49
|
+
def true(options = {})
|
50
|
+
@params.update Gecode::Constraints::Util.decode_options(options)
|
52
51
|
@model.add_constraint BooleanConstraint.new(@model,
|
53
52
|
@params.update(:rhs => true))
|
54
53
|
end
|
55
54
|
|
56
55
|
# Constrains the boolean expression to be false.
|
57
|
-
def false
|
56
|
+
def false(options = {})
|
58
57
|
@params[:negate] = !@params[:negate]
|
59
58
|
self.true
|
60
59
|
end
|
@@ -136,32 +135,37 @@ module Gecode
|
|
136
135
|
lhs, rhs, negate, reif_var =
|
137
136
|
@params.values_at(:lhs, :rhs, :negate, :reif)
|
138
137
|
space = (lhs.model || rhs.model).active_space
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
138
|
+
|
139
|
+
if lhs.kind_of?(Gecode::FreeBoolVar)
|
140
|
+
lhs = Constraints::Bool::ExpressionNode.new(lhs, @model)
|
141
|
+
end
|
142
|
+
|
143
|
+
bot_eqv = Gecode::Raw::IRT_EQ
|
144
|
+
bot_xor = Gecode::Raw::IRT_NQ
|
145
|
+
|
146
|
+
if rhs.respond_to? :to_minimodel_bool_expr
|
145
147
|
if reif_var.nil?
|
146
|
-
|
147
|
-
|
148
|
+
tree = ExpressionTree.new(lhs,
|
149
|
+
Gecode::Raw::MiniModel::BoolExpr::NT_EQV, rhs)
|
150
|
+
tree.to_minimodel_bool_expr.post(space, !negate,
|
151
|
+
*propagation_options)
|
148
152
|
else
|
149
|
-
|
150
|
-
Gecode::Raw::
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
reif_var.bind, *propagation_options)
|
155
|
-
end
|
153
|
+
tree = ExpressionTree.new(lhs,
|
154
|
+
Gecode::Raw::MiniModel::BoolExpr::NT_EQV, rhs)
|
155
|
+
var = tree.to_minimodel_bool_expr.post(space, *propagation_options)
|
156
|
+
Gecode::Raw::rel(space, var, (negate ? bot_xor : bot_eqv),
|
157
|
+
reif_var.bind, *propagation_options)
|
156
158
|
end
|
157
159
|
else
|
158
160
|
should_hold = !negate & rhs
|
159
161
|
if reif_var.nil?
|
160
|
-
|
161
|
-
|
162
|
+
lhs.to_minimodel_bool_expr.post(space, should_hold,
|
163
|
+
*propagation_options)
|
162
164
|
else
|
163
|
-
|
164
|
-
|
165
|
+
var = lhs.to_minimodel_bool_expr.post(space, *propagation_options)
|
166
|
+
Gecode::Raw::rel(space, var,
|
167
|
+
(should_hold ? bot_eqv : bot_xor),
|
168
|
+
reif_var.bind, *propagation_options)
|
165
169
|
end
|
166
170
|
end
|
167
171
|
end
|
@@ -177,10 +181,10 @@ module Gecode
|
|
177
181
|
# Maps the names of the methods to the corresponding bool constraint in
|
178
182
|
# Gecode.
|
179
183
|
OPERATION_TYPES = {
|
180
|
-
:| => Gecode::Raw::
|
181
|
-
:& => Gecode::Raw::
|
182
|
-
:^ => Gecode::Raw::
|
183
|
-
:implies => Gecode::Raw::
|
184
|
+
:| => Gecode::Raw::MiniModel::BoolExpr::NT_OR,
|
185
|
+
:& => Gecode::Raw::MiniModel::BoolExpr::NT_AND,
|
186
|
+
:^ => Gecode::Raw::MiniModel::BoolExpr::NT_XOR,
|
187
|
+
:implies => Gecode::Raw::MiniModel::BoolExpr::NT_IMP
|
184
188
|
}
|
185
189
|
|
186
190
|
public
|
@@ -191,13 +195,7 @@ module Gecode
|
|
191
195
|
unless expression.kind_of? ExpressionTree
|
192
196
|
expression = ExpressionNode.new(expression)
|
193
197
|
end
|
194
|
-
ExpressionTree.new(self, expression)
|
195
|
-
new_var = model.bool_var
|
196
|
-
Gecode::Raw::rel(model.active_space, var1.bind, #{operation},
|
197
|
-
var2.bind, new_var.bind, Gecode::Raw::ICL_DEF,
|
198
|
-
Gecode::Raw::PK_DEF)
|
199
|
-
new_var
|
200
|
-
end
|
198
|
+
ExpressionTree.new(self, #{operation}, expression)
|
201
199
|
end
|
202
200
|
end_code
|
203
201
|
end
|
@@ -216,17 +214,19 @@ module Gecode
|
|
216
214
|
class ExpressionTree #:nodoc:
|
217
215
|
include OperationMethods
|
218
216
|
|
219
|
-
# Constructs a new expression with the specified
|
220
|
-
#
|
221
|
-
def initialize(left_tree,
|
217
|
+
# Constructs a new expression with the specified binary operation
|
218
|
+
# applied to the specified trees.
|
219
|
+
def initialize(left_tree, operation, right_tree)
|
222
220
|
@left = left_tree
|
221
|
+
@operation = operation
|
223
222
|
@right = right_tree
|
224
|
-
@bind_proc = block
|
225
223
|
end
|
226
224
|
|
227
|
-
# Returns a
|
228
|
-
def
|
229
|
-
|
225
|
+
# Returns a MiniModel boolean expression representing the tree.
|
226
|
+
def to_minimodel_bool_expr
|
227
|
+
Gecode::Raw::MiniModel::BoolExpr.new(
|
228
|
+
@left.to_minimodel_bool_expr, @operation,
|
229
|
+
@right.to_minimodel_bool_expr)
|
230
230
|
end
|
231
231
|
|
232
232
|
# Fetches the space that the expression's variables is in.
|
@@ -242,14 +242,18 @@ module Gecode
|
|
242
242
|
attr :model
|
243
243
|
|
244
244
|
def initialize(value, model = nil)
|
245
|
+
unless value.kind_of?(Gecode::FreeBoolVar)
|
246
|
+
raise TypeError, 'Invalid type used in boolean equation: ' +
|
247
|
+
"#{value.class}."
|
248
|
+
end
|
245
249
|
@value = value
|
246
250
|
@model = model
|
247
251
|
end
|
248
252
|
|
249
|
-
# Returns a
|
250
|
-
def
|
251
|
-
@value.bind
|
253
|
+
# Returns a MiniModel boolean expression representing the tree.
|
254
|
+
def to_minimodel_bool_expr
|
255
|
+
Gecode::Raw::MiniModel::BoolExpr.new(@value.bind)
|
252
256
|
end
|
253
257
|
end
|
254
258
|
end
|
255
|
-
end
|
259
|
+
end
|
@@ -1,23 +1,8 @@
|
|
1
1
|
module Gecode::Constraints::Bool
|
2
2
|
class Expression
|
3
|
-
alias_method :pre_channel_equals, :==
|
4
|
-
|
5
3
|
# Constrains the boolean variable to be equal to the specified integer
|
6
4
|
# variable.
|
7
|
-
|
8
|
-
unless @params[:lhs].kind_of?(Gecode::FreeBoolVar) and
|
9
|
-
int.kind_of?(Gecode::FreeIntVar)
|
10
|
-
return pre_channel_equals(int, options)
|
11
|
-
end
|
12
|
-
|
13
|
-
# Provide commutivity to the corresponding int variable constraint.
|
14
|
-
if @params[:negate]
|
15
|
-
int.must_not.equal(@params[:lhs], options)
|
16
|
-
else
|
17
|
-
int.must.equal(@params[:lhs], options)
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
5
|
+
provide_commutivity(:==){ |rhs, _| rhs.kind_of?(Gecode::FreeIntVar) }
|
21
6
|
alias_comparison_methods
|
22
7
|
end
|
23
8
|
end
|
@@ -6,7 +6,7 @@ module Gecode::Constraints::BoolEnum
|
|
6
6
|
#
|
7
7
|
# [:offset] Specifies an offset for the integer variable. If the offset is
|
8
8
|
# set to k then the integer variable takes value i+k exactly
|
9
|
-
# when the variable at index i in the boolean
|
9
|
+
# when the variable at index i in the boolean enumeration is true
|
10
10
|
# and the rest are false.
|
11
11
|
def channel(int_var, options = {})
|
12
12
|
if @params[:negate]
|
@@ -25,28 +25,33 @@ module Gecode::Constraints::BoolEnum
|
|
25
25
|
@params.update(Gecode::Constraints::Util.decode_options(options))
|
26
26
|
@model.add_constraint Channel::ChannelConstraint.new(@model, @params)
|
27
27
|
end
|
28
|
+
|
29
|
+
# Adds a channel constraint on the variables in the enum with the specified
|
30
|
+
# set variable.
|
31
|
+
provide_commutivity(:channel){ |rhs, _| rhs.kind_of? Gecode::FreeSetVar }
|
28
32
|
end
|
29
33
|
|
30
34
|
# A module that gathers the classes and modules used in channel constraints
|
31
35
|
# involving one boolean enum and one integer variable.
|
32
36
|
module Channel #:nodoc:
|
33
|
-
# Describes a channel constraint that "channels" an
|
37
|
+
# Describes a channel constraint that "channels" an enumeration of
|
34
38
|
# boolean variables with an integer variable. This constrains the integer
|
35
39
|
# variable to take value i exactly when the variable at index i in the
|
36
40
|
# boolean enumeration is true and the others are false.
|
37
41
|
#
|
38
|
-
# Neither reification nor negation is supported.
|
42
|
+
# Neither reification nor negation is supported. The int variable
|
43
|
+
# and the enumeration can be interchanged.
|
39
44
|
#
|
40
45
|
# == Examples
|
41
46
|
#
|
42
|
-
# # Constrains the enumeration called +
|
47
|
+
# # Constrains the enumeration called +option_is_selected+ to be false in the
|
43
48
|
# # first four positions and have exactly one true variable in the other.
|
44
|
-
#
|
49
|
+
# option_is_selected.must.channel selected_option_index
|
45
50
|
# selected_option_index.must_be > 3
|
46
51
|
#
|
47
|
-
# # Constrains the enumeration called +
|
52
|
+
# # Constrains the enumeration called +option_is_selected+ to be false in the
|
48
53
|
# # first five positions and have exactly one true variable in the other.
|
49
|
-
#
|
54
|
+
# selected_option_index.must.channel(option_is_selected, :offset => 1)
|
50
55
|
# selected_option_index.must_be > 3
|
51
56
|
class ChannelConstraint < Gecode::Constraints::Constraint
|
52
57
|
def post
|
@@ -56,4 +61,4 @@ module Gecode::Constraints::BoolEnum
|
|
56
61
|
end
|
57
62
|
end
|
58
63
|
end
|
59
|
-
end
|
64
|
+
end
|
@@ -25,6 +25,27 @@ module Gecode::Constraints::BoolEnum
|
|
25
25
|
@model.add_constraint Extensional::TupleConstraint.new(@model,
|
26
26
|
@params.update(Gecode::Constraints::Util.decode_options(options)))
|
27
27
|
end
|
28
|
+
|
29
|
+
# Adds a constraint that forces the enumeration to match the
|
30
|
+
# specified regular expression over the boolean domain. The regular
|
31
|
+
# expression is expressed using arrays and boolean values (or
|
32
|
+
# integers). See BoolEnum::Extensional::RegexpConstraint for more information
|
33
|
+
# and examples of such regexps.
|
34
|
+
def match(regexp, options = {})
|
35
|
+
if @params[:negate]
|
36
|
+
raise Gecode::MissingConstraintError, 'A negated regexp constraint ' +
|
37
|
+
'is not implemented.'
|
38
|
+
end
|
39
|
+
unless options[:reify].nil?
|
40
|
+
raise ArgumentError, 'Reification is not supported by the regexp ' +
|
41
|
+
'constraint.'
|
42
|
+
end
|
43
|
+
|
44
|
+
@params[:regexp] =
|
45
|
+
Gecode::Constraints::Util::Extensional.parse_regexp regexp
|
46
|
+
@params.update Gecode::Constraints::Util.decode_options(options)
|
47
|
+
@model.add_constraint Extensional::RegexpConstraint.new(@model, @params)
|
48
|
+
end
|
28
49
|
end
|
29
50
|
|
30
51
|
# A module that gathers the classes and modules used in extensional
|
@@ -60,5 +81,32 @@ module Gecode::Constraints::BoolEnum
|
|
60
81
|
*propagation_options)
|
61
82
|
end
|
62
83
|
end
|
84
|
+
|
85
|
+
# Describes a regexp constraint, which constrains the enumeration of
|
86
|
+
# boolean variables to match a specified regexp in the boolean
|
87
|
+
# domain. Neither negation nor reification is supported.
|
88
|
+
#
|
89
|
+
# The regular expressions are specified as described in
|
90
|
+
# IntEnum::Extensional::RegexpConstraint but true and false can be
|
91
|
+
# used instead of integers.
|
92
|
+
#
|
93
|
+
# == Example
|
94
|
+
#
|
95
|
+
# # Constrains the two boolean variables in +bools+ to be false
|
96
|
+
# # and true respectively.
|
97
|
+
# bools.must.match [false, true]
|
98
|
+
#
|
99
|
+
# # Constrains the boolean variables in +bools+ to be false,
|
100
|
+
# # except for three consecutive variables which should be true
|
101
|
+
# # followed by false followed by true.
|
102
|
+
# bools.must.match [repeat(false), true, false, true, repeat(false)]]
|
103
|
+
#
|
104
|
+
class RegexpConstraint < Gecode::Constraints::Constraint
|
105
|
+
def post
|
106
|
+
lhs, regexp = @params.values_at(:lhs, :regexp)
|
107
|
+
Gecode::Raw::extensional(@model.active_space, lhs.to_bool_var_array,
|
108
|
+
regexp, *propagation_options)
|
109
|
+
end
|
110
|
+
end
|
63
111
|
end
|
64
112
|
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
module Gecode
|
2
|
+
class Model
|
3
|
+
# Specifies an integer regexp that matches +regexp+ repeated between
|
4
|
+
# +at_least+ and +at_most+ times (inclusive). If +at_most+ is
|
5
|
+
# omitted then no upper bound is placed. If both +at_least+ and
|
6
|
+
# +at_most+ are omitted then no bounds are placed.
|
7
|
+
#
|
8
|
+
# See Constraints::IntEnum::Extensional::RegexpConstraint for the
|
9
|
+
# allowed syntax of +regexp+.
|
10
|
+
def repeat(regexp, at_least = nil, at_most = nil)
|
11
|
+
unless at_least.nil? or at_least.kind_of? Fixnum
|
12
|
+
raise TypeError,
|
13
|
+
"Expected the at_least argument to be a Fixnum, got #{at_least.class}"
|
14
|
+
end
|
15
|
+
unless at_most.nil? or at_most.kind_of?(Fixnum)
|
16
|
+
raise TypeError,
|
17
|
+
"Expected the at_most argument to be a Fixnum, got #{at_most.class}"
|
18
|
+
end
|
19
|
+
|
20
|
+
reg = Constraints::Util::Extensional.parse_regexp regexp
|
21
|
+
if at_most.nil?
|
22
|
+
if at_least.nil?
|
23
|
+
reg.send '*'
|
24
|
+
else
|
25
|
+
reg.send('()', at_least)
|
26
|
+
end
|
27
|
+
else
|
28
|
+
reg.send('()', at_least, at_most)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Matches +regexp+ repeated zero or one time (i.e. like '?' in normal
|
33
|
+
# regexps). Produces the same result as calling
|
34
|
+
#
|
35
|
+
# repeat(regexp, 0, 1)
|
36
|
+
def at_most_once(regexp)
|
37
|
+
repeat(regexp, 0, 1)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Matches +regexp+ repeated at least one time (i.e. like '+' in normal
|
41
|
+
# regexps). Produces the same result as calling
|
42
|
+
#
|
43
|
+
# repeat(regexp, 1)
|
44
|
+
def at_least_once(regexp)
|
45
|
+
repeat(regexp, 1)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Matches any of the specified +regexps+.
|
49
|
+
def any(*regexps)
|
50
|
+
regexps.inject(Gecode::Raw::REG.new) do |result, regexp|
|
51
|
+
result | Constraints::Util::Extensional.parse_regexp(regexp)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
module Constraints::Util::Extensional
|
57
|
+
module_function
|
58
|
+
|
59
|
+
# Parses a regular expression over the integer domain, returning
|
60
|
+
# an instance of Gecode::REG .
|
61
|
+
#
|
62
|
+
# Pseudo-BNF of the integer regexp representation:
|
63
|
+
# regexp ::= <Fixnum> | <TrueClass> | <FalseClass> | <Gecode::Raw::REG>
|
64
|
+
# | [<regexp>, ...]
|
65
|
+
def parse_regexp(regexp)
|
66
|
+
# Check the involved types.
|
67
|
+
unless regexp.kind_of? Enumerable
|
68
|
+
regexp = [regexp]
|
69
|
+
end
|
70
|
+
regexp.to_a.flatten.each do |element|
|
71
|
+
unless element.kind_of?(Fixnum) or element.kind_of?(Gecode::Raw::REG) or
|
72
|
+
element.kind_of?(TrueClass) or element.kind_of?(FalseClass)
|
73
|
+
raise TypeError,
|
74
|
+
"Can't translate #{element.class} into integer or boolean regexp."
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# Convert it into a regexp.
|
79
|
+
internal_parse_regexp(regexp)
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
# Recursively converts arg into an instance of Gecode::REG. It is
|
85
|
+
# assumed that arg is of kind Gecode::Raw::REG, Fixnum, TrueClass,
|
86
|
+
# FalseClass or Enumerable.
|
87
|
+
def self.internal_parse_regexp(arg)
|
88
|
+
case arg
|
89
|
+
when Gecode::Raw::REG: arg
|
90
|
+
when Fixnum: Gecode::Raw::REG.new(arg)
|
91
|
+
when TrueClass: Gecode::Raw::REG.new(1)
|
92
|
+
when FalseClass: Gecode::Raw::REG.new(0)
|
93
|
+
when Enumerable
|
94
|
+
# Recursively convert the elements of the arg.
|
95
|
+
arg.inject(Gecode::Raw::REG.new) do |regexp, element|
|
96
|
+
regexp += internal_parse_regexp(element)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|