# Gecoder C0 Coverage Information - RCov

## lib/gecoder/interface/mixin.rb

Name Total Lines Lines of Code Total Coverage Code Coverage
lib/gecoder/interface/mixin.rb 486 227
100.00%
100.00%

### Key

`Code reported as executed by Ruby looks like this...and this: this line is also marked as covered.Lines considered as run by rcov, but not reported by Ruby, look like this,and this: these lines were inferred by rcov (using simple heuristics).Finally, here's a line marked as not executed.`

### Coverage Details

 `1 module Gecode` `2 # Mixin contains the base functionality needed to formulate problems.` `3 #` `4 # == Formulating problems` `5 #` `6 # Problems are formulated by building a model that represents the` `7 # problem. A model is a class that mixes in Mixin and uses its` `8 # functionality to define variables and constraints that describe the` `9 # problem. Below is an example of a model that formulates the problem` `10 # of finding a solution to the following equation system.` `11 #` `12 # Equation system:` `13 # x + y = z` `14 # x = y - 3` `15 # 0 <= x,y,z <= 9` `16 #` `17 # Model:` `18 # class EquationProblem ` `19 # include Gecode::Mixin` `20 #` `21 # attr :vars` `22 #` `23 # def initialize` `24 # x, y, z = @vars = int_var_array(3, 0..9)` `25 #` `26 # (x + y).must == z` `27 # x.must == y - 3` `28 #` `29 # branch_on @vars` `30 # end` `31 # end` `32 # ` `33 # A model typically consists of three main parts:` `34 # [Variables] Variables specify how to view the problem. A solution is an ` `35 # assignment of the variables. In the example above we created ` `36 # an array of three integer variables with domains 0..9 and gave ` `37 # it the name +variables+.` `38 #` `39 # There are three types of variables: integer variables` `40 # (Gecode::IntVar, can be assigned one of many` `41 # possible integer values), boolean variables` `42 # (Gecode::BoolVar, can be assigned either true or` `43 # false) and set variables (Gecode::SetVar, can be` `44 # assigned a set of integers). Variables of the different` `45 # types are constructed using #int_var, #int_var_array,` `46 # #int_var_matrix, #bool_var, #bool_var_array,` `47 # #bool_var_matrix, #set_var, #set_var_array and` `48 # #set_var_matrix .` `49 #` `50 # The various variables all have the functionality of Operand ` `51 # and have many properties depending on their type. For ` `52 # instance integer variables have the properties defined` `53 # in Gecode::Int::IntOperand and` `54 # enumerations of integer variables (such as the array` `55 # +variables+ we used) have the properties defined in ` `56 # Gecode::IntEnum::IntEnumOperand .` `57 # ` `58 # [Constraints] Constraints are placed on the variables to ensure that a ` `59 # valid assignment of the variables must also be a solution.` `60 # In the example above we constrained the variables so` `61 # that all equations were satisfied (which is exactly when` `62 # we have found a solution).` `63 #` `64 # The various constraints that can be placed on the various` `65 # kinds of operands are found in the respective` `66 # constraint receivers. For instance, the constraints` `67 # that can be placed on integer operands are found in ` `68 # Gecode::Int::IntConstraintReceiver and` `69 # the constraints that can be placed on enumerations of` `70 # integer operands are found in ` `71 # Gecode::IntEnum::IntEnumConstraintReceiver .` `72 #` `73 # [Branching] "branch_on variables" in the example tells Gecode that` `74 # it should explore the search space until it has assigned` `75 # +variables+ (or exhausted the search space). It also` `76 # tells Gecode in what order the search space should be` `77 # explore, which can have a big effect on the search` `78 # performance. See #branch_on for details.` `79 #` `80 # == Finding solutions` `81 #` `82 # Solutions to a formulated problem are found are found by using` `83 # methods such as #solve!, #solution, #each_solution . If one wants to` `84 # find a solution that optimizes a certain quantity (i.e. maximizes a` `85 # certain variable) then one should have a look at #maximize!,` `86 # #minimize! and #optimize! .` `87 #` `88 # The first solution to the example above could for instance be found` `89 # using` `90 #` `91 # puts EquationProblem.new.solve!.vars.values.join(', ')` `92 #` `93 # which would find the first solution to the problem, access the` `94 # assigned values of +variables+ and print them (in order x, y, z).` `95 #` `96 # == Shorter ways of formulating problems` `97 #` `98 # Problems can also be formulated without defining a new class by` `99 # using Gecode#solve et al.` `100 #` `101 # Additionally one can use "foo_is_an ..." to create an accessor of ` `102 # name foo, without having to use instance variables. The above` `103 # problem becomes` `104 # class EquationProblem ` `105 # include Gecode::Mixin` `106 #` `107 # def initialize` `108 # x, y, z = vars_is_an int_var_array(3, 0..9)` `109 #` `110 # (x + y).must == z` `111 # x.must == y - 3` `112 #` `113 # branch_on vars` `114 # end` `115 # end` `116 #` `117 module Mixin` `118 # The largest integer allowed in the domain of an integer variable.` `119 MAX_INT = Gecode::Raw::IntLimits::MAX` `120 # The smallest integer allowed in the domain of an integer variable.` `121 MIN_INT = Gecode::Raw::IntLimits::MIN` `122 ` `123 # The largest integer allowed in the domain of a set variable.` `124 SET_MAX_INT = Gecode::Raw::SetLimits::MAX` `125 # The smallest integer allowed in the domain of a set variable.` `126 SET_MIN_INT = Gecode::Raw::SetLimits::MIN` `127 ` `128 # The largest possible domain for an integer variable.` `129 LARGEST_INT_DOMAIN = MIN_INT..MAX_INT` `130 # The largest possible domain, without negative integers, for an` `131 # integer variable.` `132 NON_NEGATIVE_INT_DOMAIN = 0..MAX_INT` `133 ` `134 # The largest possible bound for a set variable.` `135 LARGEST_SET_BOUND = SET_MIN_INT..SET_MAX_INT` `136 ` `137 # Creates a new integer variable with the specified domain. The domain can` `138 # either be a range, a single element, or an enumeration of elements. If no` `139 # domain is specified then the largest possible domain is used.` `140 def int_var(domain = LARGEST_INT_DOMAIN)` `141 args = domain_arguments(domain)` `142 IntVar.new(self, variable_creation_space.new_int_var(*args))` `143 end` `144 ` `145 # Creates an array containing the specified number of integer variables ` `146 # with the specified domain. The domain can either be a range, a single ` `147 # element, or an enumeration of elements. If no domain is specified then ` `148 # the largest possible domain is used.` `149 def int_var_array(count, domain = LARGEST_INT_DOMAIN)` `150 args = domain_arguments(domain)` `151 build_var_array(count) do` `152 IntVar.new(self, variable_creation_space.new_int_var(*args))` `153 end` `154 end` `155 ` `156 # Creates a matrix containing the specified number rows and columns of ` `157 # integer variables with the specified domain. The domain can either be a ` `158 # range, a single element, or an enumeration of elements. If no domain ` `159 # is specified then the largest possible domain is used.` `160 def int_var_matrix(row_count, col_count, domain = LARGEST_INT_DOMAIN)` `161 args = domain_arguments(domain)` `162 build_var_matrix(row_count, col_count) do` `163 IntVar.new(self, variable_creation_space.new_int_var(*args))` `164 end` `165 end` `166 ` `167 # Creates a new boolean variable.` `168 def bool_var` `169 BoolVar.new(self, variable_creation_space.new_bool_var)` `170 end` `171 ` `172 # Creates an array containing the specified number of boolean variables.` `173 def bool_var_array(count)` `174 build_var_array(count) do` `175 BoolVar.new(self, variable_creation_space.new_bool_var)` `176 end` `177 end` `178 ` `179 # Creates a matrix containing the specified number rows and columns of ` `180 # boolean variables.` `181 def bool_var_matrix(row_count, col_count)` `182 build_var_matrix(row_count, col_count) do` `183 BoolVar.new(self, variable_creation_space.new_bool_var)` `184 end` `185 end` `186 ` `187 # Creates a set variable with the specified domain for greatest lower bound` `188 # and least upper bound (specified as either a fixnum, range or enum). If ` `189 # no bounds are specified then the empty set is used as greatest lower ` `190 # bound and the largest possible set as least upper bound. ` `191 #` `192 # A range for the allowed cardinality of the set can also be` `193 # specified, if none is specified, or nil is given, then the default` `194 # range (anything) will be used. If only a single Fixnum is` `195 # specified as cardinality_range then it's used as lower bound.` `196 def set_var(glb_domain = [], lub_domain = LARGEST_SET_BOUND,` `197 cardinality_range = nil)` `198 args = set_bounds_to_parameters(glb_domain, lub_domain, cardinality_range)` `199 SetVar.new(self, variable_creation_space.new_set_var(*args))` `200 end` `201 ` `202 # Creates an array containing the specified number of set variables. The` `203 # parameters beyond count are the same as for #set_var .` `204 def set_var_array(count, glb_domain = [], lub_domain = LARGEST_SET_BOUND, ` `205 cardinality_range = nil)` `206 args = set_bounds_to_parameters(glb_domain, lub_domain, cardinality_range)` `207 build_var_array(count) do` `208 SetVar.new(self, variable_creation_space.new_set_var(*args))` `209 end` `210 end` `211 ` `212 # Creates a matrix containing the specified number of rows and columns ` `213 # filled with set variables. The parameters beyond row and column counts are` `214 # the same as for #set_var .` `215 def set_var_matrix(row_count, col_count, glb_domain = [], ` `216 lub_domain = LARGEST_SET_BOUND, cardinality_range = nil)` `217 args = set_bounds_to_parameters(glb_domain, lub_domain, cardinality_range)` `218 build_var_matrix(row_count, col_count) do` `219 SetVar.new(self, variable_creation_space.new_set_var(*args))` `220 end` `221 end` `222 ` `223 # Retrieves the currently used space. Calling this method is only allowed ` `224 # when sanctioned by the model beforehand, e.g. when the model asks a ` `225 # constraint to post itself. Otherwise an RuntimeError is raised.` `226 #` `227 # The space returned by this method should never be stored, it should be` `228 # rerequested from the model every time that it's needed.` `229 def active_space #:nodoc:` `230 unless @gecoder_mixin_allow_space_access` `231 raise 'Space access is restricted and the permission to access the ' + ` `232 'space has not been given.'` `233 end` `234 selected_space` `235 end` `236 ` `237 # Adds the specified constraint to the model. Returns the newly added ` `238 # constraint.` `239 def add_constraint(constraint) #:nodoc:` `240 add_interaction do` `241 constraint.post` `242 end` `243 return constraint` `244 end` `245 ` `246 # Adds a block containing something that interacts with Gecode to a queue` `247 # where it is potentially executed.` `248 def add_interaction(&block) #:nodoc:` `249 gecode_interaction_queue << block` `250 end` `251 ` `252 # Allows the model's active space to be accessed while the block is ` `253 # executed. Don't use this unless you know what you're doing. Anything that` `254 # the space is used for (such as bound variables) must be released before` `255 # the block ends.` `256 #` `257 # Returns the result of the block.` `258 def allow_space_access(&block) #:nodoc:` `259 # We store the old value so that nested calls don't become a problem, i.e.` `260 # access is allowed as long as one call to this method is still on the ` `261 # stack.` `262 old = @gecoder_mixin_allow_space_access` `263 @gecoder_mixin_allow_space_access = true` `264 res = yield` `265 @gecoder_mixin_allow_space_access = old` `266 return res` `267 end` `268 ` `269 # Starts tracking a variable that depends on the space. All variables ` `270 # created should call this method for their respective models.` `271 def track_variable(variable) #:nodoc:` `272 (@gecoder_mixin_variables ||= []) << variable` `273 end` `274 ` `275 def self.included(mod)` `276 mod.class_eval do` `277 alias_method :pre_gecoder_method_missing, :method_missing` `278 # Wraps method missing to handle #foo_is_a and #foo_is_an . ` `279 #` `280 # "_is_a " or "_is_an` `281 # ", # replacing "" with the variable's` `282 # name and "" with the variable, adds an instance` `283 # variable and accessor with the specified name.` `284 #` `285 # The method also returns the variable given.` `286 #` `287 # ==== Example` `288 #` `289 # # Add an instance variable and accessor named "foo" that return` `290 # # the integer variable.` `291 # foo_is_an int_var(0..9)` `292 #` `293 # # Add an instance variable and accessor named "bar" that return` `294 # # the boolean variable array.` `295 # bar_is_a bool_var_array(2)` `296 def method_missing(method, *args)` `297 name = method.to_s` `298 if name =~ /._is_an?\$/` `299 name.sub!(/_is_an?\$/, '')` `300 unless args.size == 1` `301 raise ArgumentError, ` `302 "Wrong number of argmuments (#{args.size} for 1)."` `303 end ` `304 if respond_to? name` `305 raise ArgumentError, "Method with name #{name} already exists."` `306 end` `307 if instance_variable_defined? "@#{name}"` `308 raise ArgumentError, ` `309 "Instance variable with name @#{name} already exists."` `310 end` `311 ` `312 # We use the meta class to avoid defining the variable in all` `313 # other instances of the class.` `314 eval <<-"end_eval"` `315 @#{name} = args.first` `316 class < e` `339 mixee_method_missing(*args)` `340 end` `341 end` `342 alias_method :method_missing, :combined_method_missing` `343 end` `344 end` `345 end` `346 end` `347 end` `348 ` `349 protected` `350 ` `351 # Gets a queue of objects that can be posted to the model's active_space ` `352 # (by calling their post method).` `353 def gecode_interaction_queue #:nodoc:` `354 @gecode_interaction_queue ||= []` `355 end` `356 ` `357 private` `358 ` `359 # Creates an array containing the specified number of variables, all` `360 # constructed using the provided block..` `361 def build_var_array(count, &block)` `362 variables = []` `363 count.times do ` `364 variables << yield` `365 end` `366 return wrap_enum(variables)` `367 end` `368 ` `369 # Creates a matrix containing the specified number rows and columns of ` `370 # variables, all constructed using the provided block. ` `371 def build_var_matrix(row_count, col_count, &block)` `372 rows = []` `373 row_count.times do |i|` `374 row = []` `375 col_count.times do |j|` `376 row << yield` `377 end` `378 rows << row` `379 end` `380 return wrap_enum(Util::EnumMatrix.rows(rows, false))` `381 end` `382 ` `383 # Returns the array of arguments that correspond to the specified ` `384 # domain when given to Gecode. The domain can be given as a range, ` `385 # a single number, or an enumerable of elements. ` `386 def domain_arguments(domain)` `387 if domain.respond_to?(:first) and domain.respond_to?(:last) and` `388 domain.respond_to?(:exclude_end?)` `389 if domain.exclude_end?` `390 return [domain.first, (domain.last - 1)]` `391 else` `392 return [domain.first, domain.last]` `393 end` `394 elsif domain.kind_of? Enumerable` `395 array = domain.to_a` `396 return [Gecode::Raw::IntSet.new(array, array.size)]` `397 elsif domain.kind_of? Fixnum` `398 return [domain, domain]` `399 else` `400 raise TypeError, 'The domain must be given as an instance of ' +` `401 "Enumerable or Fixnum, but #{domain.class} was given."` `402 end` `403 end` `404 ` `405 # Transforms the argument to a set cardinality range, returns nil if the` `406 # default range should be used. If arg is a range then that's used, ` `407 # otherwise if the argument is a fixnum it's used as lower bound.` `408 def to_set_cardinality_range(arg)` `409 if arg.kind_of? Fixnum` `410 arg..Gecode::Raw::SetLimits::MAX` `411 else` `412 arg` `413 end` `414 end` `415 ` `416 # Converts the specified set var domain to parameters accepted by` `417 # Gecode. The bounds can be specified as a fixnum, range or # enum. ` `418 # The parameters are returned as an array.` `419 def set_bounds_to_parameters(glb_domain, lub_domain, cardinality_range)` `420 check_set_bounds(glb_domain, lub_domain)` `421 args = []` `422 args << Gecode::Util.constant_set_to_int_set(glb_domain)` `423 args << Gecode::Util.constant_set_to_int_set(lub_domain)` `424 card_range = to_set_cardinality_range(cardinality_range)` `425 if card_range.nil?` `426 card_range = 0..Gecode::Raw::SetLimits::CARD` `427 end` `428 args << card_range.first << card_range.last` `429 end` `430 ` `431 # Checks whether the specified greatest lower bound is a subset of least ` `432 # upper bound. Raises ArgumentError if that is not the case.` `433 def check_set_bounds(glb, lub)` `434 unless valid_set_bounds?(glb, lub)` `435 raise ArgumentError, ` `436 "Invalid set bounds: #{glb} is not a subset of #{lub}."` `437 end` `438 end` `439 ` `440 # Returns whether the greatest lower bound is a subset of least upper ` `441 # bound.` `442 def valid_set_bounds?(glb, lub)` `443 return true if glb.respond_to?(:empty?) and glb.empty? ` `444 if glb.kind_of?(Range) and lub.kind_of?(Range)` `445 glb.first >= lub.first and glb.last <= lub.last` `446 else` `447 glb = [glb] if glb.kind_of?(Fixnum)` `448 lub = [lub] if lub.kind_of?(Fixnum)` `449 (glb.to_a - lub.to_a).empty?` `450 end` `451 end` `452 ` `453 # Retrieves the base from which searches are made. ` `454 def base_space` `455 @gecoder_mixin_base_space ||= Gecode::Raw::Space.new` `456 end` `457 ` `458 # Retrieves the currently selected space, the one which constraints and ` `459 # variables should be bound to.` `460 def selected_space` `461 return @gecoder_mixin_active_space unless @gecoder_mixin_active_space.nil?` `462 self.active_space = base_space` `463 end` `464 ` `465 # Retrieves the space that should be used for variable creation.` `466 def variable_creation_space` `467 @gecoder_mixin_variable_creation_space || selected_space` `468 end` `469 ` `470 # Executes any interactions with Gecode still waiting in the queue ` `471 # (emptying the queue) in the process.` `472 def perform_queued_gecode_interactions` `473 allow_space_access do` `474 gecode_interaction_queue.each{ |con| con.call }` `475 gecode_interaction_queue.clear # Empty the queue.` `476 end` `477 end` `478 ` `479 # Switches the active space used (the space from which variables are read` `480 # and to which constraints are posted). @gecoder_mixin_active_space ` `481 # should never be assigned directly.` `482 def active_space=(new_space)` `483 @gecoder_mixin_active_space = new_space` `484 end` `485 end` `486 end`

Generated on Thu Jan 08 13:27:03 +0100 2015 with rcov 1.0.0