gecoder 0.7.0 → 0.7.1

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 (46) hide show
  1. data/CHANGES +6 -0
  2. data/README +1 -1
  3. data/example/square_tiling.rb +84 -0
  4. data/example/sudoku-set.rb +107 -0
  5. data/example/sudoku.rb +2 -6
  6. data/lib/gecoder/bindings.rb +1 -1
  7. data/lib/gecoder/bindings/bindings.rb +20 -0
  8. data/lib/gecoder/interface/binding_changes.rb +2 -2
  9. data/lib/gecoder/interface/branch.rb +50 -51
  10. data/lib/gecoder/interface/constraints.rb +10 -10
  11. data/lib/gecoder/interface/constraints/bool/boolean.rb +79 -5
  12. data/lib/gecoder/interface/constraints/bool/linear.rb +29 -0
  13. data/lib/gecoder/interface/constraints/bool_enum/boolean.rb +34 -4
  14. data/lib/gecoder/interface/constraints/bool_var_constraints.rb +14 -9
  15. data/lib/gecoder/interface/constraints/int/arithmetic.rb +26 -8
  16. data/lib/gecoder/interface/constraints/int/domain.rb +30 -3
  17. data/lib/gecoder/interface/constraints/int/linear.rb +82 -16
  18. data/lib/gecoder/interface/constraints/int_enum/arithmetic.rb +31 -3
  19. data/lib/gecoder/interface/constraints/int_enum/channel.rb +63 -3
  20. data/lib/gecoder/interface/constraints/int_enum/count.rb +20 -3
  21. data/lib/gecoder/interface/constraints/int_enum/distinct.rb +22 -2
  22. data/lib/gecoder/interface/constraints/int_enum/element.rb +23 -4
  23. data/lib/gecoder/interface/constraints/int_enum/equality.rb +9 -2
  24. data/lib/gecoder/interface/constraints/int_enum/sort.rb +28 -3
  25. data/lib/gecoder/interface/constraints/int_enum_constraints.rb +1 -1
  26. data/lib/gecoder/interface/constraints/int_var_constraints.rb +13 -8
  27. data/lib/gecoder/interface/constraints/set/cardinality.rb +27 -8
  28. data/lib/gecoder/interface/constraints/set/connection.rb +72 -6
  29. data/lib/gecoder/interface/constraints/set/domain.rb +46 -3
  30. data/lib/gecoder/interface/constraints/set/operation.rb +35 -4
  31. data/lib/gecoder/interface/constraints/set/relation.rb +59 -6
  32. data/lib/gecoder/interface/constraints/set_enum/distinct.rb +22 -3
  33. data/lib/gecoder/interface/constraints/set_enum/operation.rb +26 -2
  34. data/lib/gecoder/interface/constraints/set_enum/selection.rb +110 -36
  35. data/lib/gecoder/interface/constraints/set_var_constraints.rb +11 -7
  36. data/lib/gecoder/interface/model.rb +6 -6
  37. data/lib/gecoder/interface/search.rb +6 -6
  38. data/lib/gecoder/interface/variables.rb +56 -12
  39. data/lib/gecoder/version.rb +1 -1
  40. data/specs/constraints/linear.rb +167 -1
  41. data/specs/constraints/set_domain.rb +6 -0
  42. data/tasks/distribution.rake +25 -3
  43. data/tasks/website.rake +5 -12
  44. metadata +10 -4
  45. data/vendor/rust/configure.rb +0 -6
  46. data/vendor/rust/out.rb +0 -627
@@ -15,6 +15,9 @@ module Gecode::Constraints::Set
15
15
  # Adds a domain constraint for the specified relation name, constant set
16
16
  # and options.
17
17
  def add_domain_constraint(relation_name, constant_set, options)
18
+ unless Gecode::Constraints::Util.constant_set? constant_set
19
+ raise TypeError, "Expected constant set, got #{constant_set.class}."
20
+ end
18
21
  @params[:rhs] = constant_set
19
22
  @params[:relation] = relation_name
20
23
  @params.update Gecode::Constraints::Set::Util.decode_options(options)
@@ -28,8 +31,21 @@ module Gecode::Constraints::Set
28
31
  end
29
32
 
30
33
  # A module that gathers the classes and modules used in domain constraints.
31
- module Domain
32
- # Describes a domain constraint for equality.
34
+ module Domain #:nodoc:
35
+ # Describes a domain constraint which constrains a set to be equal to a
36
+ # constant set.
37
+ #
38
+ # == Examples
39
+ #
40
+ # # +set+ must equal [1,2,5]
41
+ # set.must == [1,2,5]
42
+ #
43
+ # # +set+ must not equal 1..67
44
+ # set.must_not == 1..67
45
+ #
46
+ # # +set+ must equal the singleton set 0. The constraint is reified with
47
+ # # the boolean varaible +is_singleton_zero+.
48
+ # set.must.equal(0, :reify => is_singleton_zero)
33
49
  class EqualityDomainConstraint < Gecode::Constraints::ReifiableConstraint
34
50
  def post
35
51
  var, domain, reif_var, negate = @params.values_at(:lhs, :rhs, :reif,
@@ -48,7 +64,34 @@ module Gecode::Constraints::Set
48
64
  end
49
65
  end
50
66
 
51
- # Describes a domain constraint for the relations other than equality.
67
+ # Describes a domain constraint which constrains a set to have a specific
68
+ # relation to a constant set. A constant set may be specified in three ways
69
+ #
70
+ # [Fixnum] Represents a singleton set.
71
+ # [Range] Represents a set containing all elements in the
72
+ # range. This represents the set more efficiently
73
+ # than when another enumeration with the same
74
+ # elements are used.
75
+ # [Enumeration of Fixnum] Represents a set containing the enumeration’s
76
+ # elements.
77
+ #
78
+ # The relations allowed are the same as in
79
+ # <tt>Set::Relation::RelationConstraint</tt>.
80
+ #
81
+ # == Examples
82
+ #
83
+ # # +set+ must be subset of [1,2,5]
84
+ # set.must_be.subset_of [1,2,5]
85
+ #
86
+ # # +set+ must be disjoint with 1..67
87
+ # set.must_be.disjoint_with 1..67
88
+ #
89
+ # # +set+ must not be a superset of [0].
90
+ # set.must_not_be.superset_of 0
91
+ #
92
+ # # +set+ must be subset of [1,3,5,7]. The constraint is reified with
93
+ # # the boolean varaible +only_constains_odd_values+.
94
+ # set.must_be.subset_of([1.3.5.7], :reify => only_contains_odd_values)
52
95
  class DomainConstraint < Gecode::Constraints::ReifiableConstraint
53
96
  def post
54
97
  var, domain, reif_var, relation = @params.values_at(:lhs, :rhs, :reif,
@@ -42,9 +42,9 @@ end
42
42
 
43
43
  module Gecode::Constraints::Set
44
44
  # A module that gathers the classes and modules used in operation constraints.
45
- module Operation
45
+ module Operation #:nodoc:
46
46
  # An expression with a set operand and two operands followed by must.
47
- class Expression < Gecode::Constraints::Expression
47
+ class Expression < Gecode::Constraints::Expression #:nodoc:
48
48
  Gecode::Constraints::Util::SET_RELATION_TYPES.each_pair do |name, type|
49
49
  module_eval <<-"end_code"
50
50
  # Creates an operation constraint using the specified expression.
@@ -76,8 +76,39 @@ module Gecode::Constraints::Set
76
76
  alias_set_methods
77
77
  end
78
78
 
79
- # Describes a constraint involving a set operator operating on two set
80
- # operands.
79
+ # Describes an operation constraint, which constrains the result of an
80
+ # operation with two sets as operands. Either constant sets or set
81
+ # variables may be used for the result and operands, with the exception of
82
+ # that all three may not be constant sets.
83
+ #
84
+ # The typical form is
85
+ # set_operand_1.<operation>(set_operand_2).must.<relation>(result_set)
86
+ #
87
+ # The following operations are supported:
88
+ #
89
+ # * union
90
+ # * disjoint_union
91
+ # * intersection
92
+ # * minus
93
+ #
94
+ # The allowed relations are the same as for
95
+ # <tt>Set::Relation::RelationConstraint</tt>.
96
+ #
97
+ # Neither reification nor negation is supported.
98
+ #
99
+ # == Examples
100
+ #
101
+ # # +set_1+ union +set_2+ must equal +set_3+.
102
+ # set_1.union(set_2).must == set_3
103
+ #
104
+ # # +set_1+ intersection [3,5,6] must equal +set_3+.
105
+ # set_1.intersection([3,5,6]).must == set_3
106
+ #
107
+ # # [0,1,2] minus +set_2+ must be superset of +set_3+.
108
+ # wrap_enum([0,1,2]).minus(set_2).must_be.superset_of(set_3)
109
+ #
110
+ # # +set_1+ disjoint union with [0] must be subset of 0..17.
111
+ # set_1.disjoint_union(0).must_be.subset_of 0..17
81
112
  class OperationConstraint < Gecode::Constraints::Constraint
82
113
  def post
83
114
  op1, op2, operation, relation, rhs, negate = @params.values_at(:lhs,
@@ -49,8 +49,22 @@ module Gecode::Constraints::Set
49
49
  end
50
50
 
51
51
  # A module that gathers the classes and modules used in relation constraints.
52
- module Relation
53
- # Describes a relation constraint for equality.
52
+ module Relation #:nodoc:
53
+ # Describes a relation constraint which constrains a set variable to be
54
+ # equal to another set variable. Equality may either be expressed as
55
+ # +==+, +equal+ or +equal_to+.
56
+ #
57
+ # == Examples
58
+ #
59
+ # # +set_1+ must be equal to +set_2+
60
+ # set_1.must == set_2
61
+ #
62
+ # # +set_1+ must not be equal to +set_2+
63
+ # set_1.must_not == set_2
64
+ #
65
+ # # The same as above but reified with the boolean variable
66
+ # # +are_not_equal+.
67
+ # set_1.must_not.equal(set_2, :reify => are_not_equal)
54
68
  class EqualityRelationConstraint < Gecode::Constraints::ReifiableConstraint
55
69
  def post
56
70
  var, rhs, reif_var, negate = @params.values_at(:lhs, :rhs, :reif,
@@ -69,7 +83,29 @@ module Gecode::Constraints::Set
69
83
  end
70
84
  end
71
85
 
72
- # Describes a relation constraint for the relations other than equality.
86
+ # Describes a relation constraint which constrains a set variable to have
87
+ # a specified relation to another set variable. The allowed relations and
88
+ # their aliases are
89
+ #
90
+ # * subset, subset_of
91
+ # * superset, superset_of
92
+ # * disjoint, disjoint_with
93
+ # * complement, complement_of
94
+ #
95
+ # == Examples
96
+ #
97
+ # # +set_1+ must be subset of +set_2+
98
+ # set_1.must_be.subset_of set_2
99
+ #
100
+ # # +set_1+ must not be superset of +set_2+
101
+ # set_1.must_not_be.superset_of set_2
102
+ #
103
+ # # +set_1+ must be disjoint with +set_2+
104
+ # set_1.must_be.disjoint set_2
105
+ #
106
+ # # The same as above but reified with the boolean variable
107
+ # # +are_disjoint+.
108
+ # set_1.must_be.disjoint(set_2, :reify => are_disjoint)
73
109
  class RelationConstraint < Gecode::Constraints::ReifiableConstraint
74
110
  def post
75
111
  var, rhs, reif_var, relation = @params.values_at(:lhs, :rhs, :reif,
@@ -84,7 +120,24 @@ module Gecode::Constraints::Set
84
120
  negate_using_reification
85
121
  end
86
122
 
87
- # Describes a relation constraint on the elements of a set.
123
+ # Describes an element relation constraint which constrains all elements in
124
+ # a set variable to satisfy an integer relation constraint. The relations
125
+ # supported are the same as in
126
+ # <tt>Int::Linear::SimpleRelationConstraint</tt>.
127
+ #
128
+ # Reification is not supported.
129
+ #
130
+ # == Examples
131
+ #
132
+ # # All elements in +set+ must be larger than 5.
133
+ # set.elements.must > 5
134
+ #
135
+ # # No element in +set+ may equal 0.
136
+ # set.elements.must_not == 0
137
+ #
138
+ # # No element in +set+ may contain the value of the integer variable
139
+ # # +forbidden_number+.
140
+ # set.elements.must_not == forbidden_number
88
141
  class ElementRelationConstraint < Gecode::Constraints::Constraint
89
142
  def post
90
143
  var, rhs, relation = @params.values_at(:lhs, :rhs, :relation)
@@ -103,8 +156,8 @@ module Gecode::Constraints::Set
103
156
  end
104
157
  end
105
158
 
106
- # Describes an expression which starts with set.element.must* .
107
- class ElementExpression < Gecode::Constraints::Expression
159
+ # Describes an expression which starts with set.elements.must* .
160
+ class ElementExpression < Gecode::Constraints::Expression #:nodoc:
108
161
  Gecode::Constraints::Util::RELATION_TYPES.each_key do |name|
109
162
  module_eval <<-"end_code"
110
163
  # Creates an elements constraint using the specified expression, which
@@ -41,8 +41,17 @@ module Gecode::Constraints::SetEnum
41
41
  end
42
42
 
43
43
  # A module that gathers the classes and modules used in distinct constraints.
44
- module Distinct
45
- # Describes a set distinct constraint.
44
+ module Distinct #:nodoc:
45
+ # Describes a set distinct constraint, which constrains all set variables
46
+ # in the enumeration to be distinct and of a specified size. Providing a
47
+ # size is not optional.
48
+ #
49
+ # Neither negation nor reification is supported.
50
+ #
51
+ # == Examples
52
+ #
53
+ # # All set variables in +sets+ must have cardinality 4 and be different.
54
+ # sets.must_be.distinct(:size => 4)
46
55
  class DistinctConstraint < Gecode::Constraints::Constraint
47
56
  def post
48
57
  sets, size = @params.values_at(:lhs, :size)
@@ -50,7 +59,17 @@ module Gecode::Constraints::SetEnum
50
59
  end
51
60
  end
52
61
 
53
- # Describes an at most one set constraint.
62
+ # Describes an at most one constraint, which constrains all pairs of set
63
+ # variables in the enumeration to at most have one element in common and be
64
+ # of a specified size. Providing a size is not optional.
65
+ #
66
+ # Neither negation nor reification is supported.
67
+ #
68
+ # == Examples
69
+ #
70
+ # # All set variables in +sets+ must have cardinality 17 and no pair may
71
+ # # have more than one element in common.
72
+ # sets.must.at_most_share_one_element(:size => 17)
54
73
  class AtMostOneConstraint < Gecode::Constraints::Constraint
55
74
  def post
56
75
  sets, size = @params.values_at(:lhs, :size)
@@ -14,8 +14,32 @@ module Gecode::SetEnumMethods
14
14
  end
15
15
 
16
16
  # A module that gathers the classes and modules used by operation constaints.
17
- module Gecode::Constraints::SetEnum::Operation
18
- # Describes a stub started with a set enumeration followed by a set operation.
17
+ module Gecode::Constraints::SetEnum::Operation #:nodoc:
18
+ # Describes a CompositeStub for the enumeration operation constraint, which
19
+ # constrains the result of applying an operation between all set variables in
20
+ # a set enumeration.
21
+ #
22
+ # The supported operations are:
23
+ # * union
24
+ # * disjoint_union
25
+ # * intersection
26
+ # * minus
27
+ #
28
+ # == Example
29
+ #
30
+ # # The union of all set variables in +sets+ must be subset of 1..17.
31
+ # sets.union.must_be.subset_of 1..17
32
+ #
33
+ # # The intersection of all set variables must equal [1,3,5].
34
+ # sets.intersection.must == [1,3,5]
35
+ #
36
+ # # The union of all set variable must be a subset of the set variable
37
+ # # +universe+.
38
+ # sets.union.must_be.subset_of universe
39
+ #
40
+ # # The same as above, but reified with the boolean variable
41
+ # # +is_within_universe+.
42
+ # sets.union.must_be.subset_of(universe, :reify => is_within_universe)
19
43
  class ExpressionStub < Gecode::Constraints::Set::CompositeStub
20
44
  def constrain_equal(variable, params, constrain)
21
45
  enum, operation = @params.values_at(:lhs, :operation)
@@ -2,7 +2,7 @@ module Gecode::SetEnumMethods
2
2
  # This adds the adder for the methods in the modules including it. The
3
3
  # reason for doing it so indirect is that the first #[] won't be defined
4
4
  # before the module that this is mixed into is mixed into an enum.
5
- def self.included(mod)
5
+ def self.included(mod) #:nodoc:
6
6
  mod.module_eval do
7
7
  # Now we enter the module that the module possibly defining #[]
8
8
  # is mixed into.
@@ -31,24 +31,10 @@ module Gecode::SetEnumMethods
31
31
  end
32
32
 
33
33
  # A module that gathers the classes and modules used by selection constraints.
34
- module Gecode::Constraints::SetEnum::Selection
35
- # Describes an expression stub started with a set var enum following with an
36
- # array access using an integer variable.
37
- class SelectExpressionStub < Gecode::Constraints::Set::CompositeStub
38
- def constrain_equal(variable, params, constrain)
39
- enum, index = @params.values_at(:lhs, :index)
40
- if constrain
41
- variable.must_be.subset_of enum.upper_bound_range
42
- end
43
-
44
- Gecode::Raw::selectSet(@model.active_space, enum.to_set_var_array,
45
- index.bind, variable.bind)
46
- end
47
- end
48
-
34
+ module Gecode::Constraints::SetEnum::Selection #:nodoc:
49
35
  # Describes an expression stub started with a set var enum followed with an
50
36
  # array access using a set variable.
51
- class SetAccessStub < Gecode::Constraints::ExpressionStub
37
+ class SetAccessStub < Gecode::Constraints::ExpressionStub #:nodoc:
52
38
  include Gecode::Constraints::LeftHandSideMethods
53
39
 
54
40
  # Starts a union selection constraint on the selected sets.
@@ -85,8 +71,69 @@ module Gecode::Constraints::SetEnum::Selection
85
71
  end
86
72
  end
87
73
 
88
- # Describes an expression stub started with a set var enum following with an
89
- # array access using a set variable followed by #union.
74
+ # Describes an expression that starts with an set variable enum followed with
75
+ # an array access using a set variable followed by some form of must.
76
+ class SetAccessExpression < Gecode::Constraints::Set::Expression #:nodoc:
77
+ # Constrains the selected sets to be disjoint.
78
+ def disjoint
79
+ if @params[:negate]
80
+ raise Gecode::MissingConstraintError, 'A negated set selection ' +
81
+ 'disjoint is not implemented.'
82
+ end
83
+
84
+ @model.add_constraint DisjointConstraint.new(@model, @params)
85
+ end
86
+ end
87
+
88
+ # Describes a CompositeStub for the set select constraint, which constrains
89
+ # the set in a position specified by an integer variable in an enumeration of
90
+ # set variable.
91
+ #
92
+ # == Examples
93
+ #
94
+ # # The set at the position described by the integer variable
95
+ # # +singleton_zero_position+ in the enumeration +sets+ of set variables
96
+ # # must equal [0].
97
+ # sets[singleton_zero_position].must == 0
98
+ #
99
+ # # The set at the position described by the integer variable +position+ in
100
+ # # the enumeration +sets+ of set variables must be a subset of +set+.
101
+ # sets[position].must_be.subset_of set
102
+ #
103
+ # # The same as above, but reified with the boolean variable +bool+.
104
+ # sets[position].must_be.subset_of(set, :reify => bool)
105
+ class SelectExpressionStub < Gecode::Constraints::Set::CompositeStub
106
+ def constrain_equal(variable, params, constrain)
107
+ enum, index = @params.values_at(:lhs, :index)
108
+ if constrain
109
+ variable.must_be.subset_of enum.upper_bound_range
110
+ end
111
+
112
+ Gecode::Raw::selectSet(@model.active_space, enum.to_set_var_array,
113
+ index.bind, variable.bind)
114
+ end
115
+ end
116
+
117
+ # Describes a CompositeStub for the set union selection constraint,
118
+ # which constrains the union of sets located at the positions
119
+ # specified by a set variable in an enumeration of set variables.
120
+ #
121
+ # == Examples
122
+ #
123
+ # # The sets in the enumeration set variable +sets+ located at the positions
124
+ # # described by the set variable +selected_sets+ must have a union that's
125
+ # # a superset of [0,4,17].
126
+ # sets[selected_sets].union.must_be.superset_of [0,4,17]
127
+ #
128
+ # # The sets in the enumeration set variable +sets+ located at the positions
129
+ # # described by the set variable +selected_sets+ must have a union that's
130
+ # # disjoint with the set variable +set+.
131
+ # sets[selected_sets].union.must_be.disjoint_with set
132
+ #
133
+ # # The same as above but reified with the boolean variable
134
+ # # +union_is_disjoint+.
135
+ # sets[selected_sets].union.must_be.disjoint_with(set,
136
+ # :reify => union_is_disjoin)
90
137
  class UnionExpressionStub < Gecode::Constraints::Set::CompositeStub
91
138
  def constrain_equal(variable, params, constrain)
92
139
  enum, indices = @params.values_at(:lhs, :indices)
@@ -99,8 +146,40 @@ module Gecode::Constraints::SetEnum::Selection
99
146
  end
100
147
  end
101
148
 
102
- # Describes an expression stub started with a set var enum following with an
103
- # array access using a set variable followed by #intersection.
149
+ # Describes a CompositeStub for the set intersection selection constraint,
150
+ # which constrains the intersection of sets located at the positions
151
+ # specified by a set variable in an enumeration of set variables.
152
+ #
153
+ # Optionally a universe may also be specified.
154
+ #
155
+ # == Examples
156
+ #
157
+ # # The sets in the enumeration set variable +sets+ located at the positions
158
+ # # described by the set variable +selected_sets+ must have an intersection
159
+ # # that's a superset of [0,4,17].
160
+ # sets[selected_sets].intersection.must_be.superset_of [0,4,17]
161
+ #
162
+ # # The sets in the enumeration set variable +sets+ located at the positions
163
+ # # described by the set variable +selected_sets+ must have an intersection
164
+ # # that's disjoint with the set variable +set+.
165
+ # sets[selected_sets].intersection.must_be.disjoint_with set
166
+ #
167
+ # # The sets in the enumeration set variable +sets+ located at the positions
168
+ # # described by the set variable +selected_sets+ must have an intersection
169
+ # # that's disjoint with the set variable +set+ inside the universe 0..17.
170
+ # sets[selected_sets].intersection(:with => 0..17).must_be.disjoint_with set
171
+ #
172
+ # # The sets in the enumeration set variable +sets+ located at the positions
173
+ # # described by the set variable +selected_sets+ must have an intersection
174
+ # # that's disjoint with the set variable +set+ inside the universe
175
+ # # described by the set variable +universe+.
176
+ # sets[selected_sets].intersection(:with => universe).must_be.disjoint_with set
177
+ #
178
+ #
179
+ # # The same as above but reified with the boolean variable
180
+ # # +intersection_is_disjoint+.
181
+ # sets[selected_sets].intersection(:with => universe).must_be.disjoint_with(
182
+ # set, :reifty => intersection_is_disjoin)
104
183
  class IntersectionExpressionStub < Gecode::Constraints::Set::CompositeStub
105
184
  def constrain_equal(variable, params, constrain)
106
185
  enum, indices, universe = @params.values_at(:lhs, :indices, :universe)
@@ -118,21 +197,16 @@ module Gecode::Constraints::SetEnum::Selection
118
197
  end
119
198
  end
120
199
 
121
- # Describes an expression that starts with an set variable enum followed with
122
- # an array access using a set variable followed by some form of must.
123
- class SetAccessExpression < Gecode::Constraints::Set::Expression
124
- # Constrains the selected sets to be disjoint.
125
- def disjoint
126
- if @params[:negate]
127
- raise Gecode::MissingConstraintError, 'A negated set selection ' +
128
- 'disjoint is not implemented.'
129
- end
130
-
131
- @model.add_constraint DisjointConstraint.new(@model, @params)
132
- end
133
- end
134
-
135
- # Describes a disjoint constraint produced by sets[set].must_be.disjoint .
200
+ # Describes a disjoint constraint, which constrains all set variable is an
201
+ # enumeration, at the position specified by a set variable, to be disjoint.
202
+ #
203
+ # Does not support negation nor reification.
204
+ #
205
+ # == Examples
206
+ #
207
+ # # The set variable located in the enumeration +sets+ at positions
208
+ # # described by +disjoint_set_positions+ must be disjoint.
209
+ # sets[disjoint_set_positions].must_be.disjoint
136
210
  class DisjointConstraint < Gecode::Constraints::Constraint
137
211
  def post
138
212
  enum, indices = @params.values_at(:lhs, :indices)