class OPL::Helper
Public Class Methods
add_ones(text, lp)
click to toggle source
# File lib/opl.rb, line 177 def self.add_ones(text, lp) equation = text equation = "#"+equation equation.scan(/[#+-][a-z]/).each do |p| if p.include?("+") q = p.gsub("+", "+1*") elsif p.include?("-") q = p.gsub("-","-1*") elsif p.include?("#") q = p.gsub("#","#1*") end equation = equation.gsub(p,q) end equation.gsub("#","") end
check_options_syntax(options)
click to toggle source
# File lib/opl.rb, line 648 def self.check_options_syntax(options) return if options.empty? options.each do |option| if option.include?(":") title = option.gsub(" ","").split(":")[0] value = option.gsub(" ","").split(":")[1] if !["nonnegative", "integer", "boolean", "data", "epsilon"].include?(title.downcase) raise "Did not recognize the TITLE parameter '#{title}' in the options." end else raise "Options parameter '#{option}' does not have a colon in it. The proper syntax of an option is TITLE: VALUE" end end end
coefficients(text, lp)
click to toggle source
# File lib/opl.rb, line 294 def self.coefficients(text, lp)#text is one side of the equation equation = self.add_ones(text, lp) if equation[0]=="-" equation.scan(/[+-][\d\.]+/) else ("#"+equation).scan(/[#+-][\d\.]+/).map{|e|e.gsub("#","+")} end end
data_coefficients()
click to toggle source
# File lib/opl.rb, line 303 def self.data_coefficients end
either_or(lp, constraint1, constraint2, i)
click to toggle source
# File lib/opl.rb, line 729 def self.either_or(lp, constraint1, constraint2, i) # in: lp, "10.0*x1+4.0*x2+5.0*x3<=600", "2.0*x1+2.0*x2+6.0*x3<=300" # out: "10.0*x1+4.0*x2+5.0*x3<=600+#{$default_m}", "2.0*x1+2.0*x2+6.0*x3<=300+#{$default_m}" index1 = lp.constraints.index(constraint1) lp.constraints[index1] = lp.constraints[index1]+"#{$default_m}*m[#{i}]" # add "+M[n]y[n]" to the rhs of the constraint end
forall(text)
click to toggle source
# File lib/opl.rb, line 90 def self.forall(text) #in: "i in (0..2), x[i] <= 5" #out: ["x[0] <= 5", "x[1] <= 5", "x[2] <= 5"] helper = self text = text.sub_paren_with_array if ((text.gsub(" ","")).scan(/\]\,/).size) + ((text.gsub(" ","")).scan(/\)\,/).size) != text.gsub(" ","").scan(/in/).size raise "The following forall() constraint is incorrectly formatted: #{text}. Please see the examples in test.rb for forall() constraints. I suspect you are missing a comma somewhere." end final_constraints = [] if text.include?("sum") indices = text.split("sum")[0].scan(/[a-z] in/).map{|sc|sc[0]} values = text.split("sum")[0].scan(/\s\[[\-\s\d+,]+\]/).map{|e|e.gsub(" ", "").scan(/[\-\d]+/)} else indices = text.scan(/[a-z] in/).map{|sc|sc[0]} values = text.scan(/\s\[[\-\s\d+,]+\]/).map{|e|e.gsub(" ", "").scan(/[\-\d]+/)} end #TODO: the indices and values should only be those #of the forall(), not of any sum() that is #inside the forall() index_value_pairs = indices.zip(values) variable = text.scan(/[a-z]\[/)[0].gsub("[","") value_combinations = helper.mass_product(values) value_combinations.each_index do |vc_index| value_combination = value_combinations[vc_index] value_combination = [value_combination] unless value_combination.is_a?(Array) if text.include?("sum") constraint = "sum"+text.split("sum")[1..-1].join("sum") else constraint = text.split(",")[-1].gsub(" ","") end e = constraint value_combination.each_index do |i| index = indices[i] value = value_combination[i] e = e.gsub("("+index, "("+value) e = e.gsub(index+")", value+")") e = e.gsub("["+index, "["+value) e = e.gsub(index+"]", value+"]") e = e.gsub("=>"+index, "=>"+value) e = e.gsub("<="+index, "<="+value) e = e.gsub(">"+index, ">"+value) e = e.gsub("<"+index, "<"+value) e = e.gsub("="+index, "="+value) e = e.gsub("=> "+index, "=> "+value) e = e.gsub("<= "+index, "<= "+value) e = e.gsub("> "+index, "> "+value) e = e.gsub("< "+index, "< "+value) e = e.gsub("= "+index, "= "+value) end final_constraints += [e] end final_constraints end
get_all_vars(constraints, lp)
click to toggle source
# File lib/opl.rb, line 316 def self.get_all_vars(constraints, lp) all_vars = [] constraints.each do |constraint| constraint = constraint.gsub(" ", "") value = constraint.split(":")[1] || constraint all_vars << self.variables(value, lp) end all_vars.flatten.uniq end
get_coefficient_variable_pairs(text)
click to toggle source
# File lib/opl.rb, line 404 def self.get_coefficient_variable_pairs(text) text.scan(/\d*[\*]*[a-z]\[*\d*\]*/) end
get_column_bounds(bound_info, all_variables)
click to toggle source
# File lib/opl.rb, line 539 def self.get_column_bounds(bound_info, all_variables) #in: ["NONNEGATIVE: x1"] #out: {:x1 => {:lower => 0}} column_bounds = {} bound_info.each do |info| type = info.gsub(" ","").split(":")[0] if type.downcase == "nonnegative" bounds = {:lower => 0} end variables = info.split(":")[1].gsub(" ","").split(",") variables.each do |root_var| all_variables_with_root = all_variables.find_all{|var|var.include?("[") && var.split("[")[0]==root_var}+[root_var] all_variables_with_root.each do |var| column_bounds[var.to_sym] = bounds end end end column_bounds end
get_constants(text)
click to toggle source
# File lib/opl.rb, line 326 def self.get_constants(text) #in: "-8 + x + y + 3" #out: "[-8, +3]" text = text.gsub(" ","") text = text+"#" cs = [] potential_constants = text.scan(/[\d\.]+[^a-z^\[^\]^\d^\.^\)^\*]/) constants = potential_constants.find_all{|c|![*('a'..'z'),*('A'..'Z'),"["].include?(text[text.index(c)-1])} searchable_text = text constants.each_index do |i| constant = constants[i] c = constant.scan(/[\d\.]+/)[0] index = searchable_text.index(constant) if index == 0 c = "+"+c else constant = constant.gsub('+','[+]') constant = constant.gsub('-','[-]') c = searchable_text.scan(/[\-\+]#{constant}/)[0] end cs << c.scan(/[\-\+][\d\.]+/)[0] searchable_text[index] = "**" end return({:formatted => cs, :unformatted => constants}) end
mass_product(array_of_arrays, base=[])
click to toggle source
# File lib/opl.rb, line 79 def self.mass_product(array_of_arrays, base=[]) return(base) if array_of_arrays.empty? array = array_of_arrays[0] new_array_of_arrays = array_of_arrays[1..-1] if base==[] self.mass_product(new_array_of_arrays, array) else self.mass_product(new_array_of_arrays, base.product(array).map{|e|e.flatten}) end end
negate(text, explicit=false)
click to toggle source
# File lib/opl.rb, line 663 def self.negate(text, explicit=false) # text is one side of an equation # there will be no foralls, no sums, and no abs # in: "z - 3" # out: "-z + 3" working_text = text = text.gsub(" ","") #!("a".."z").to_a.include?(text[0]) && if !["-","+"].include?(text[0]) working_text = "+"+working_text end indices_of_negatives = working_text.index_array("-") indices_of_positives = working_text.index_array("+") indices_of_negatives.each {|i| working_text[i] = "+"} indices_of_positives.each {|i| working_text[i] = "-"} if !explicit && working_text[0] == "+" working_text = working_text[1..-1] end return(working_text) end
operator(constraint)
click to toggle source
# File lib/opl.rb, line 408 def self.operator(constraint) if constraint.include?(">=") ">=" elsif constraint.include?("<=") "<=" elsif constraint.include?(">") ">" elsif constraint.include?("<") "<" elsif constraint.include?("=") "=" end end
parse_data(data_info)
click to toggle source
# File lib/opl.rb, line 559 def self.parse_data(data_info) #in: "DATA: {d => [1, 0.3, 1.5], o => [10.3, 4.0005, -1]}" #out: [data_object_1, data_object_2] data_hash_string = data_info.gsub(" ","").split(":")[1] data_string = data_hash_string.gsub("{",",").gsub("}",",") names = data_string.scan(/,[a-z]/).map{|comma_name|comma_name.gsub(",","")} string_values = data_string.scan(/\=\>[\[\d\.\]\,\-]+,/).map{|scanned_value|scanned_value[2..-2]} values = string_values.map{|sv|sv.to_a} data_hash = {} names.each_index do |i| name = names[i] value = values[i] value = value[0] if value.size == 1 data_hash[name] = value end return(data_hash) end
produce_variable_type_hash(variable_types, all_variables)
click to toggle source
# File lib/opl.rb, line 486 def self.produce_variable_type_hash(variable_types, all_variables) #in: ["BOOLEAN: x, y", "INTEGER: z"] #out: {:x => 3, :y => 3, :z => 2} variable_type_hash = {} variable_types.each do |vt| type = vt.gsub(" ","").split(":")[0] if type.downcase == "boolean" type_number = 3 elsif type.downcase == "integer" type_number = 2 end variables = vt.split(":")[1].gsub(" ","").split(",") variables.each do |root_var| all_variables_with_root = all_variables.find_all{|var|var.include?("[") && var.split("[")[0]==root_var}+[root_var] all_variables_with_root.each do |var| variable_type_hash[var.to_sym] = type_number end end end variable_type_hash end
put_constants_on_rhs(text)
click to toggle source
# File lib/opl.rb, line 352 def self.put_constants_on_rhs(text) #in: "-8 + x + y + 3 <= 100" #out: "x + y <= 100 + 5" text = text.gsub(" ","") s = self.sides(text) constants_results = self.get_constants(s[:lhs]) constants = [] constants_results[:formatted].each_index do |i| formatted_constant = constants_results[:formatted][i] unformatted_constant = constants_results[:unformatted][i] unless unformatted_constant.include?("*") constants << formatted_constant end end unless constants.empty? sum = constants.map{|cc|cc.to_f}.inject("+").to_s if sum.include?("-") sum = sum.gsub("-","+") else sum = "-"+sum end lhs = s[:lhs].gsub(" ","")+"#" constants_results[:unformatted].each do |constant| index = lhs.index(constant) if index == 0 lhs = lhs[(constant.size-1)..(lhs.size-1)] else lhs = lhs[0..(index-2)]+lhs[(index+(constant.size-1))..(lhs.size-1)] end end text = text.gsub(s[:lhs], lhs[0..-2]) text += sum end return(text) end
put_variables_on_lhs(text)
click to toggle source
# File lib/opl.rb, line 422 def self.put_variables_on_lhs(text) #in: "x + y - x[3] <= 3z + 2x[2] - 10" #out: "x + y - x[3] - 3z - 2x[2] <= -10" text = text.gsub(" ", "") s = self.sides(text) oper = self.operator(text) rhs = s[:rhs] lhs = s[:lhs] coefficient_variable_pairs = self.get_coefficient_variable_pairs(rhs) add_to_left = [] remove_from_right = [] coefficient_variable_pairs.each do |cvp| index = rhs.index(cvp) if index == 0 add_to_left << "-"+cvp remove_from_right << cvp else if rhs[index-1] == "+" add_to_left << "-"+cvp remove_from_right << "+"+cvp else add_to_left << "+"+cvp remove_from_right << "-"+cvp end end end new_lhs = lhs+add_to_left.join("") text = text.gsub(lhs+oper, new_lhs+oper) new_rhs = rhs remove_from_right.each do |rfr| new_rhs = new_rhs.gsub(rfr, "") end new_rhs = "0" if new_rhs == "" text = text.gsub(oper+rhs, oper+new_rhs) return(text) end
remove_constants(text)
click to toggle source
# File lib/opl.rb, line 613 def self.remove_constants(text) helper = self text = text.gsub(" ","") text = "+"+text if text[0]!="-" text = text+"#" constants = helper.get_constants(text) replacements = [] constants[:formatted].each_index do |i| formatted = constants[:formatted][i] unformatted = constants[:unformatted][i] if formatted.include?("+") if unformatted.include?("+") replacements << ["+"+unformatted, "+"] elsif unformatted.include?("-") replacements << ["-"+unformatted, "-"] elsif unformatted.include?("#") replacements << [formatted+"#",""] end elsif formatted.include?("-") if unformatted.include?("+") replacements << ["-"+unformatted, "+"] elsif unformatted.include?("-") replacements << ["-"+unformatted, "-"] elsif unformatted.include?("#") replacements << [formatted+"#",""] end end end replacements.each do |replacement| text = text.gsub(replacement[0],replacement[1]) end text = text[1..-1] if text[0] == "+" return(text) end
replace_absolute_value(text)
click to toggle source
# File lib/opl.rb, line 683 def self.replace_absolute_value(text) # text is a constraint # there will be no foralls and no sums # in: "abs(x) <= 1" # out: "x <= 1", "-x <= 1" # in: "4x + 3y - 6abs(z - 3) <= 4" # out: "4x + 3y - 6z + 18 <= 4", "4x + 3y + 6z - 18 <= 4" if text.include?("abs") helper = self constraints_to_delete = [text] text = text.gsub(" ","") constraints_to_delete << text working_text = "#"+text absolute_value_elements = working_text.scan(/[\-\+\#]\d*abs\([a-z\-\+\*\d\[\]]+\)/) constraints_to_add = [] absolute_value_elements.each do |ave| ave = ave.gsub("#","") inside_value = ave.split("(")[1].split(")")[0] positive_ave = inside_value negative_ave = helper.negate(inside_value) if ave[0] == "-" || ave[1] == "-" positive_ave = helper.negate(positive_ave, true) negative_ave = helper.negate(negative_ave, true) elsif ave[0] == "+" positive_ave = "+"+positive_ave end positive_constraint = text.gsub(ave, positive_ave) negative_constraint = text.gsub(ave, negative_ave) constraints_to_add << positive_constraint constraints_to_add << negative_constraint end return {:constraints_to_delete => constraints_to_delete, :constraints_to_add => constraints_to_add} else return {:constraints_to_delete => [], :constraints_to_add => []} end end
sides(text)
click to toggle source
# File lib/opl.rb, line 160 def self.sides(text) equation = text if equation.include?("<=") char = "<=" elsif equation.include?(">=") char = ">=" elsif equation.include?("<") char = "<" elsif equation.include?(">") char = ">" elsif equation.include?("=") char = "=" end sides = equation.split(char) {:lhs => sides[0], :rhs => sides[1]} end
split_equals(constraint)
click to toggle source
# File lib/opl.rb, line 459 def self.split_equals(constraint) [constraint.gsub("=", "<="), constraint.gsub("=", ">=")] end
split_equals_a(constraints)
click to toggle source
# File lib/opl.rb, line 463 def self.split_equals_a(constraints) constraints.map do |constraint| if (constraint.split("") & ["<=",">=","<",">"]).empty? self.split_equals(constraint) else constraint end end.flatten end
split_ors(lp, constraints)
click to toggle source
make a default OPTION that m is BOOLEAN
# File lib/opl.rb, line 738 def self.split_ors(lp, constraints) # in: ["x <= 10", "y <= 24 or y + x <= 14"] # out: ["x <= 10", "y - 1000000000000.0*m[0] <= 24", "y + x - 1000000000000.0 + 1000000000000.0*m[0] <= 14"] # add m[i+1] to left side of constraints end
strip_abs(text)
click to toggle source
# File lib/opl.rb, line 720 def self.strip_abs(text) # in: "3 + abs(x[1] - y) + abs(x[3]) <= 3*abs(-x[2])" # out: {:positive => "3 + x[1] - y + x[3] <= -3*x[2]", # :negative => "3 - x[1] + y - x[3] <= 3*x[2]"} text = text.gsub(" ","") working_text = "#"+text end
sub_forall(equation, indexvalues={:indices => [], :values => []})
click to toggle source
# File lib/opl.rb, line 144 def self.sub_forall(equation, indexvalues={:indices => [], :values => []}) #in: "forall(i in (0..2), x[i] <= 5)" #out: ["x[0] <= 5", "x[1] <= 5", "x[2] <= 5"] return equation unless equation.include?("forall") foralls = (equation+"#").split("forall(").map{|ee|ee.split(")")[0..-2].join(")")}.find_all{|eee|eee!=""} constraints = [] if foralls.empty? return(equation) else foralls.each do |text| constraints << self.forall(text) end return(constraints.flatten) end end
sub_rhs_with_summed_constants(constraint)
click to toggle source
# File lib/opl.rb, line 399 def self.sub_rhs_with_summed_constants(constraint) rhs = self.sides(constraint)[:rhs] constraint.gsub(rhs, self.sum_constants(rhs)) end
sub_sum(equation, lp, indexvalues={:indices => [], :values => []})
click to toggle source
# File lib/opl.rb, line 259 def self.sub_sum(equation, lp, indexvalues={:indices => [], :values => []}) #in: "sum(i in (0..3), x[i]) <= 100" #out: "x[0]+x[1]+x[2]+x[3] <= 100" if equation.include?("sum(") sums = (equation+"#").split("sum(").map{|ee|ee.split(")")[0..-2].join(")")}.find_all{|eee|eee!=""}.find_all{|eeee|!eeee.include?("forall")} sums.each do |text| e = text unless indexvalues[:indices].empty? indexvalues[:indices].each_index do |i| index = indexvalues[:indices][i] value = indexvalues[:values][i].to_s e = e.gsub("("+index, "("+value) e = e.gsub(index+")", value+")") e = e.gsub("["+index, "["+value) e = e.gsub(index+"]", value+"]") e = e.gsub("=>"+index, "=>"+value) e = e.gsub("<="+index, "<="+value) e = e.gsub(">"+index, ">"+value) e = e.gsub("<"+index, "<"+value) e = e.gsub("="+index, "="+value) e = e.gsub("=> "+index, "=> "+value) e = e.gsub("<= "+index, "<= "+value) e = e.gsub("> "+index, "> "+value) e = e.gsub("< "+index, "< "+value) e = e.gsub("= "+index, "= "+value) end end equation = equation.gsub(text, e) result = self.sum(text, lp) equation = equation.gsub("sum("+text+")", result) end end return(equation) end
substitute_data(text, lp)
click to toggle source
# File lib/opl.rb, line 577 def self.substitute_data(text, lp) helper = self potential_things_to_substitute = helper.variables(text, lp) data_names = lp.data.map{|d|d.name} things_to_substitute = {} data_values = {} lp.data.each do |data| dname = data.name dvalue = data.value targets = potential_things_to_substitute.find_all do |ptts| dname == ptts[0] end things_to_substitute[dname] = targets targets.each do |target| indices = target.scan(/\d+/).map{|ind|ind.to_i} indices = "" if indices.empty? if dvalue.is_a?(Array) value = dvalue.values_at_a(indices) else value = dvalue end data_values[dname+indices.to_s.gsub(",","][").gsub(" ","")] = value end end data_values.keys.each do |key| name = key value = data_values[key] text = text.gsub(name, value.to_s) end plus_minus = text.scan(/\+[\ ]+-/) plus_minus.each do |pm| text = text.gsub(pm,"-") end return(text) end
sum(text, lp, indexvalues={:indices => [], :values => []})
click to toggle source
# File lib/opl.rb, line 193 def self.sum(text, lp, indexvalues={:indices => [], :values => []}) #in: "i in [0,1], j in [4,-5], 3x[i][j]" #out: "3x[0][4] + 3x[0][-5] + 3x[1][4] + 3x[1][-5]" text = text.sub_paren_with_array if text.scan(/\(\d+\+\d+\)/).size > 0 text.scan(/\(\d+\+\d+\)/).each {|e| text = text.gsub(e,eval(e.gsub("(","").gsub(")","")).to_s) } end text = text.sub_paren_with_array if (text.gsub(" ","")).scan(/\]\,/).size != text.scan(/in/).size raise "The following sum() constraint is incorrectly formatted: #{text}. Please see the examples in test.rb for sum() constraints. I suspect you are missing a comma somewhere." elsif (text.gsub(" ","").include?("=") || text.gsub(" ","").include?("<") || text.gsub(" ","").include?(">")) raise "The following sum() constraint cannot have a equalities in it (a.k.a. =, <, >): #{text}" end final_text = "" element = text.split(",")[-1].gsub(" ","") indices = text.scan(/[a-z] in/).map{|sc|sc[0]} input_indices = indexvalues[:indices] - indices if not input_indices.empty? input_values = input_indices.map{|ii|indexvalues[:values][indexvalues[:indices].index(ii)]} else input_values = [] end values = text.scan(/\s\[[\-\s\d+,]+\]/).map{|e|e.gsub(" ", "").scan(/[\-\d]+/)} indices += input_indices values += input_values index_value_pairs = indices.zip(values) variable = text.scan(/[a-z]\[/)[0].gsub("[","") coefficient_a = text.split(",")[-1].split("[")[0].scan(/\-?[\d\*]+[a-z]/) if coefficient_a.empty? if text.split(",")[-1].split("[")[0].include?("-") coefficient = "-1" else coefficient = "1" end else coefficient = coefficient_a[0].scan(/[\d\-]+/) end value_combinations = OPL::Helper.mass_product(values) value_combinations.each_index do |vc_index| value_combination = value_combinations[vc_index] e = element value_combination = [value_combination] unless value_combination.is_a?(Array) value_combination.each_index do |i| index = indices[i] value = value_combination[i] e = e.gsub("("+index, "("+value) e = e.gsub(index+")", value+")") e = e.gsub("["+index, "["+value) e = e.gsub(index+"]", value+"]") e = e.gsub("=>"+index, "=>"+value) e = e.gsub("<="+index, "<="+value) e = e.gsub(">"+index, ">"+value) e = e.gsub("<"+index, "<"+value) e = e.gsub("="+index, "="+value) e = e.gsub("=> "+index, "=> "+value) e = e.gsub("<= "+index, "<= "+value) e = e.gsub("> "+index, "> "+value) e = e.gsub("< "+index, "< "+value) e = e.gsub("= "+index, "= "+value) end e = "+"+e unless (coefficient.include?("-") || vc_index==0) final_text += e end final_text end
sum_constants(text)
click to toggle source
# File lib/opl.rb, line 388 def self.sum_constants(text) #in: "100+ 10-3" #out: "107" constants = self.get_constants(text)[:formatted] if constants.to_s.include?(".") constants.map{|c|c.to_f}.inject("+").to_s else constants.map{|c|c.to_i}.inject("+").to_s end end
sum_indices(constraint)
click to toggle source
# File lib/opl.rb, line 473 def self.sum_indices(constraint) # pieces_to_sub = constraint.scan(/[a-z]\[\d[\d\+\-]+\]/) # pieces_to_sub = constraint.scan(/[a-z\]]\[\d[\d\+\-]+\]/) pieces_to_sub = constraint.scan(/\[[\d\+\-]+\]/) pieces_to_sub.each do |piece| characters_to_sum = piece.scan(/[\d\+\-]+/)[0] index_sum = self.sum_constants(characters_to_sum) new_piece = piece.gsub(characters_to_sum, index_sum) constraint = constraint.gsub(piece, new_piece) end return(constraint) end
sum_variables(formatted_constraint, lp)
click to toggle source
# File lib/opl.rb, line 508 def self.sum_variables(formatted_constraint, lp) #in: x + y - z + x[3] + z + y - z + x - y <= 0 #out: 2*x + y - z + x[3] <= 0 helper = self lhs = helper.sides(formatted_constraint)[:lhs] formatted_lhs = helper.add_ones(lhs, lp) vars = helper.variables(formatted_lhs, lp) coefs = helper.coefficients(formatted_lhs, lp) var_coef_hash = {} vars.each_index do |i| var = vars[i] coef = coefs[i] if var_coef_hash[var] var_coef_hash[var] += coefs[i].to_f else var_coef_hash[var] = coefs[i].to_f end end new_lhs = "" var_coef_hash.keys.each do |key| coef = var_coef_hash[key].to_s var = key coef = "+"+coef unless coef.include?("-") new_lhs += coef+"*"+var end if new_lhs[0] == "+" new_lhs = new_lhs[1..-1] end formatted_constraint.gsub(lhs, new_lhs) end
variables(text, lp)
click to toggle source
# File lib/opl.rb, line 307 def self.variables(text, lp)#parameter is one side of the equation text = self.add_ones(text, lp) text = text.gsub("abs","").gsub("(","").gsub(")","") variables = text.scan(/[a-z][\[\]\d]*/) raise("The variable letter a is reserved for special processes. Please rename your variable to something other than a.") if variables.join.include?("a") raise("The variable letter m is reserved for special processes. Please rename your variable to something other than m.") if variables.join.include?("m") return variables end