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
@@ -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
@@ -35,19 +35,7 @@ module Gecode::Constraints::Int
35
35
  # set to k then the integer variable takes value i+k exactly
36
36
  # when the variable at index i in the boolean enumration is true
37
37
  # and the rest are false.
38
- def channel(bool_enum, options = {})
39
- unless bool_enum.respond_to? :to_bool_var_array
40
- raise TypeError, 'Expected an enumration of boolean variables, got ' +
41
- "#{bool_enum.class}."
42
- end
43
-
44
- # Just provide commutivity with the boolean enum channel constraint.
45
- if @params[:negate]
46
- bool_enum.must_not.channel(@params[:lhs], options)
47
- else
48
- bool_enum.must.channel(@params[:lhs], options)
49
- end
50
- end
38
+ provide_commutivity(:channel){ |rhs, _| rhs.respond_to? :to_bool_var_array }
51
39
  end
52
40
 
53
41
  # A module that gathers the classes and modules used in channel constraints
@@ -7,23 +7,20 @@ module Gecode::Constraints::IntEnum
7
7
  raise Gecode::MissingConstraintError, 'A negated channel constraint ' +
8
8
  'is not implemented.'
9
9
  end
10
- unless enum.respond_to?(:to_int_var_array) or
11
- enum.respond_to?(:to_set_var_array)
12
- raise TypeError, "Expected int or set enum, got #{enum.class}."
10
+ unless enum.respond_to?(:to_int_var_array)
11
+ raise TypeError, "Expected int enum, got #{enum.class}."
13
12
  end
14
- if enum.respond_to? :to_set_var_array
15
- if options.has_key? :reify
16
- raise ArgumentError, 'The channel constraints does not support the ' +
17
- 'reification option.'
18
- end
19
- @params.update(Gecode::Constraints::Set::Util.decode_options(options))
20
- else
21
- @params.update(Gecode::Constraints::Util.decode_options(options))
13
+ if options.has_key? :reify
14
+ raise ArgumentError, 'The channel constraints does not support the ' +
15
+ 'reification option.'
22
16
  end
23
17
 
18
+ @params.update(Gecode::Constraints::Util.decode_options(options))
24
19
  @params.update(:rhs => enum)
25
20
  @model.add_constraint Channel::ChannelConstraint.new(@model, @params)
26
21
  end
22
+
23
+ provide_commutivity(:channel){ |rhs, _| rhs.respond_to? :to_set_var_array }
27
24
  end
28
25
 
29
26
  # A module that gathers the classes and modules used in channel constraints.
@@ -33,14 +30,12 @@ module Gecode::Constraints::IntEnum
33
30
  # enumeration of set variables. Channel constraints are used to give
34
31
  # access to multiple viewpoints when modelling.
35
32
  #
36
- # When used on two integer enumeration, the channel constraint can be
37
- # thought of as constraining the arrays to be each other's inverses. When
38
- # used with an enumeration of sets the i:th set includes the value of the
39
- # j:th integer.
33
+ # The channel constraint can be thought of as constraining the arrays to
34
+ # be each other's inverses. I.e. if the i:th value in the first enumeration
35
+ # is j, then the j:th value in the second enumeration is constrained to be
36
+ # i.
40
37
  #
41
- # Neither reification nor negation is supported. Selecting strength is only
42
- # supported when using the constraint between two integer enumerations,
43
- # it's not supported when a set enumeration is used.
38
+ # Neither reification nor negation is supported.
44
39
  #
45
40
  # == Example
46
41
  #
@@ -82,26 +77,11 @@ module Gecode::Constraints::IntEnum
82
77
  #
83
78
  # elements.must.channel positions
84
79
  #
85
- # == Example (sets)
86
- #
87
- # # +set_enum+ is constrained to channel +int_enum+.
88
- # int_enum.must.channel set_enum
89
- #
90
- # # This is another way of saying the above.
91
- # set_enum.must.channel int_enum
92
80
  class ChannelConstraint < Gecode::Constraints::Constraint
93
81
  def post
94
82
  lhs, rhs = @params.values_at(:lhs, :rhs)
95
-
96
- lhs = lhs.to_int_var_array
97
- if rhs.respond_to? :to_int_var_array
98
- # Int var array.
99
- Gecode::Raw::channel(@model.active_space, lhs, rhs.to_int_var_array,
100
- *propagation_options)
101
- else
102
- # Set var array, no propagation options.
103
- Gecode::Raw::channel(@model.active_space, lhs, rhs.to_set_var_array)
104
- end
83
+ Gecode::Raw::channel(@model.active_space, lhs.to_int_var_array,
84
+ rhs.to_int_var_array, *propagation_options)
105
85
  end
106
86
  end
107
87
  end
@@ -26,6 +26,27 @@ module Gecode::Constraints::IntEnum
26
26
  @model.add_constraint Extensional::TupleConstraint.new(@model,
27
27
  @params.update(util.decode_options(options)))
28
28
  end
29
+
30
+ # Adds a constraint that forces the enumeration to match the
31
+ # specified regular expression over the integer domain. The regular
32
+ # expression is expressed using arrays and integers. See
33
+ # IntEnum::Extensional::RegexpConstraint for more information and examples of
34
+ # such regexps.
35
+ def match(regexp, options = {})
36
+ if @params[:negate]
37
+ raise Gecode::MissingConstraintError, 'A negated regexp constraint ' +
38
+ 'is not implemented.'
39
+ end
40
+ unless options[:reify].nil?
41
+ raise ArgumentError, 'Reification is not supported by the regexp ' +
42
+ 'constraint.'
43
+ end
44
+
45
+ @params[:regexp] =
46
+ Gecode::Constraints::Util::Extensional.parse_regexp regexp
47
+ @params.update Gecode::Constraints::Util.decode_options(options)
48
+ @model.add_constraint Extensional::RegexpConstraint.new(@model, @params)
49
+ end
29
50
  end
30
51
 
31
52
  # A module that gathers the classes and modules used in extensional
@@ -60,5 +81,114 @@ module Gecode::Constraints::IntEnum
60
81
  *propagation_options)
61
82
  end
62
83
  end
84
+
85
+ # Describes a regexp constraint, which constrains the enumeration of
86
+ # integer variables to match a specified regexp in the integer
87
+ # domain. Neither negation nor reification is supported.
88
+ #
89
+ # == Regexp syntax
90
+ #
91
+ # The regular expressions are specified using arrays, integers and a
92
+ # few methods provided by Model. Arrays are used to group the
93
+ # integers in sequences that must be matched. The following array
94
+ # describes a regular expression matching a 1 followed by a 7.
95
+ #
96
+ # [1, 7]
97
+ #
98
+ # Arrays can be nested or left out when not needed. I.e. the above
99
+ # is semantically equal to
100
+ #
101
+ # [[[1], 7]]
102
+ #
103
+ # A couple of methods provided by Model are used to express patterns
104
+ # beyond mere sequences:
105
+ #
106
+ # [Model#repeat] Used for specifying patterns that include patterns
107
+ # that may be repeated a given number of times. The
108
+ # number of times to repeat a pattern can be specified
109
+ # using a lower and upper bound, but the bounds can be
110
+ # omitted to for instance allow an expression to be
111
+ # repeated any number of times.
112
+ # [Model#any] Used for specifying alternatives.
113
+ #
114
+ # Additionally Model#at_least_once and Model#at_most_once are
115
+ # provided as convenience methods.
116
+ #
117
+ # === Examples
118
+ #
119
+ # # Matches 1 followed by any number of 2s.
120
+ # [1, repeat(2)]
121
+ #
122
+ # # Semantically the same as above. It just has a bunch of
123
+ # # needless brackets thrown in.
124
+ # [[1], [repeat([2])]]
125
+ #
126
+ # # Matches 1 followed by [a 2 followed by a 3] at least two times.
127
+ # # Matches e.g. 1, 2, 3, 2, 3
128
+ # [1, repeat([2, 3], 2)]
129
+ #
130
+ # # Matches between one and two [2 followed by [at least three 1]]
131
+ # # followed by between three and four 3. Matches e.g.
132
+ # # 2, 1, 1, 1, 2, 1, 1, 1, 3, 3, 3
133
+ # [repeat([2, repeat(1, 3], 1, 2), repeat(3, 3, 4)]
134
+ #
135
+ # # Matches [1, 2 or 3] followed by 4. Matches e.g. 2, 4
136
+ # [any(1, 2, 3), 4]
137
+ #
138
+ # # Matches 0 followed by [[1 followed by 2] or [3 followed by 5]].
139
+ # # Matches e.g. 0, 1, 2 as well as 0, 3, 5
140
+ # [0, any([1, 2], [3, 5])]
141
+ #
142
+ # # Matches 0 followed by [[[1 followed by 7] at least two times]
143
+ # # or [[8, 9], at most two times]. Matches e.g.
144
+ # # 0, 1, 7, 1, 7, 1, 7 as well as 0, 8, 9
145
+ # [0, any(repeat([1, 7], 2), repeat([8, 9], 0, 2)]
146
+ #
147
+ # # Matches 0 followed by at least one 1.
148
+ # [0, at_least_once(1)]
149
+ #
150
+ # # Exactly the same as the above.
151
+ # [0, repeat(1, 1)]
152
+ #
153
+ # # Matches 0 followed by at least one [[1 followed by 7] or [3
154
+ # # followed by 2]]. Matches e.g. 0, 1, 7, 3, 2, 1, 7
155
+ # [0, at_least_once(any([1, 7], [3, 2]]
156
+ #
157
+ # # Matches 0 followed by at either [[1 followed by 7] at least once]
158
+ # # or [[3 followed by 2] at least once]. Matches e.g.
159
+ # # 0, 1, 7, 1, 7 but does _not_ match 0, 1, 7, 3, 2, 1, 7
160
+ # [0, any(at_least_once([1, 7]), at_least_once([3, 2])]
161
+ #
162
+ # # Matches 0, followed by at most one 1. Matches 0 as well as
163
+ # # 0, 1
164
+ # [0, at_most_once(1)]
165
+ #
166
+ # # Exactly the same as the above.
167
+ # [0, repeat(1, 0, 1)]
168
+ #
169
+ # == Example
170
+ #
171
+ # # Constrains the two integer variables in +numbers+ to have
172
+ # # values 1 and 7.
173
+ # numbers.must.match [1, 7]
174
+ #
175
+ # # Constrains the integer variables in +numbers+ to contain the
176
+ # # value 47 followed by 11, with all other values set to -1.
177
+ # numbers.must.match [repeat(-1), 47, 11, repeat(-1)]
178
+ #
179
+ # # Constrains exactly three of the integer variables in +numbers+ to
180
+ # # contain 47 or 11, each followed by at least two
181
+ # # variables set to -1. All other variables are constrained to
182
+ # # equal -1.
183
+ # numbers.must.match repeat([repeat(-1), any(11, 47),
184
+ # repeat(-1, 2)], 3, 3)
185
+ #
186
+ class RegexpConstraint < Gecode::Constraints::Constraint
187
+ def post
188
+ lhs, regexp = @params.values_at(:lhs, :regexp)
189
+ Gecode::Raw::extensional(@model.active_space, lhs.to_int_var_array,
190
+ regexp, *propagation_options)
191
+ end
192
+ end
63
193
  end
64
194
  end
@@ -0,0 +1,54 @@
1
+ module Gecode::Constraints::Set
2
+ class Expression
3
+ # Adds a channel constraint on the set variable with the specified enum of
4
+ # boolean variables.
5
+ def channel(bool_enum, options = {})
6
+ if @params[:negate]
7
+ raise Gecode::MissingConstraintError, 'A negated channel constraint ' +
8
+ 'is not implemented.'
9
+ end
10
+ if options.has_key? :reify
11
+ raise ArgumentError, 'The channel constraint does not support the ' +
12
+ 'reification option.'
13
+ end
14
+ unless bool_enum.respond_to? :to_bool_var_array
15
+ raise TypeError, 'Expected an enum of bool variables, ' +
16
+ "got #{bool_enum.class}."
17
+ end
18
+
19
+ @params.update(:rhs => bool_enum)
20
+ @params.update Gecode::Constraints::Set::Util.decode_options(options)
21
+ @model.add_constraint Channel::ChannelConstraint.new(@model, @params)
22
+ end
23
+ end
24
+
25
+ # A module that gathers the classes and modules used in channel constraints
26
+ # involving one set variable and a boolean enum.
27
+ module Channel #:nodoc:
28
+ # Describes a channel constraint that "channels" a set variable and an
29
+ # enumerations of boolean variables. This constrains the set variable to
30
+ # include value i exactly when the variable at index i in the boolean
31
+ # enumeration is true.
32
+ #
33
+ # Neither reification nor negation is supported. The boolean enum and set
34
+ # can be interchanged.
35
+ #
36
+ # == Examples
37
+ #
38
+ # # Constrains the enumeration of boolean variables called +bools+ to at
39
+ # # least have the first and third variables set to true
40
+ # set.must_be.superset_of [0, 2]
41
+ # set.must.channel bools
42
+ #
43
+ # # An alternative way of writing the above.
44
+ # set.must_be.superset_of [0, 2]
45
+ # bools.must.channel set
46
+ class ChannelConstraint < Gecode::Constraints::Constraint
47
+ def post
48
+ lhs, rhs = @params.values_at(:lhs, :rhs)
49
+ Gecode::Raw::channel(@model.active_space, rhs.to_bool_var_array,
50
+ lhs.bind)
51
+ end
52
+ end
53
+ end
54
+ end
@@ -2,16 +2,47 @@ module Gecode::Constraints::SetEnum
2
2
  class Expression
3
3
  # Posts a channel constraint on the variables in the enum with the specified
4
4
  # int enum.
5
- def channel(enum)
5
+ def channel(enum, options = {})
6
6
  unless enum.respond_to? :to_int_var_array
7
7
  raise TypeError, "Expected integer variable enum, for #{enum.class}."
8
8
  end
9
-
10
- # Just provide commutativity to the corresponding int enum constraint.
11
9
  if @params[:negate]
12
- enum.must_not.channel(@params[:lhs])
13
- else
14
- enum.must.channel(@params[:lhs])
10
+ raise Gecode::MissingConstraintError, 'A negated channel constraint ' +
11
+ 'is not implemented.'
12
+ end
13
+ if options.has_key? :reify
14
+ raise ArgumentError, 'The channel constraints does not support the ' +
15
+ 'reification option.'
16
+ end
17
+
18
+ @params.update(Gecode::Constraints::Set::Util.decode_options(options))
19
+ @params.update(:rhs => enum)
20
+ @model.add_constraint Channel::IntChannelConstraint.new(@model, @params)
21
+ end
22
+ end
23
+
24
+ # A module that gathers the classes and modules used in channel constraints.
25
+ module Channel #:nodoc:
26
+ # Describes a channel constraint which "channels" one enumeration of
27
+ # integer variables with one enumeration of set variables. The i:th set
28
+ # in the enumeration of set variables is constrainde to includes the value
29
+ # of the j:th integer variable.
30
+ #
31
+ # Neither reification nor negation is supported.
32
+ #
33
+ # == Examples
34
+ #
35
+ # # +set_enum+ is constrained to channel +int_enum+.
36
+ # int_enum.must.channel set_enum
37
+ #
38
+ # # This is another way of saying the above.
39
+ # set_enum.must.channel int_enum
40
+ #
41
+ class IntChannelConstraint < Gecode::Constraints::Constraint
42
+ def post
43
+ lhs, rhs = @params.values_at(:lhs, :rhs)
44
+ Gecode::Raw::channel(@model.active_space, rhs.to_int_var_array,
45
+ lhs.to_set_var_array)
15
46
  end
16
47
  end
17
48
  end
@@ -75,3 +75,4 @@ require 'gecoder/interface/constraints/set/relation'
75
75
  require 'gecoder/interface/constraints/set/cardinality'
76
76
  require 'gecoder/interface/constraints/set/connection'
77
77
  require 'gecoder/interface/constraints/set/operation'
78
+ require 'gecoder/interface/constraints/set/channel'
@@ -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'