# # OrthogonalArray.rb # # Copyright (C) 2010 GLAD!! (ITO Yoshiichi) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, # either express or implied. See the License for the specific language # governing permissions and limitations under the License. # require 'logger' require 'LinearGraphTemplate' class OrthogonalArray @@log = Logger.new(STDOUT) @@log.level = Logger::WARN @@log.formatter = proc {|s, d, p, m| sprintf("%-5s %s: %s\n", s, p, m) } attr_reader :template, :table attr_accessor :row0, :col0, :val0 def initialize(size, args = '') @template = LinearGraphTemplate.new(size, args) @table = make_table(@template) @row0 = 1 @col0 = 1 @val0 = 0 end def make_table(template) (0...template.size).map {|i| make_row(i, template) } end def make_row(row_idx, template) original = make_original_row(row_idx, template.size - 1) template.make_row(original) end def make_original_row(row_idx, n_cols, div = 1) if n_cols == 0 return [] end heads = make_original_row(row_idx, n_cols / 2, div * 2) infix = (row_idx / div) % 2 heads + [infix] + heads.map {|x| x ^ infix } end def size template.size end def strength if !@strength @strength = calc_strength end @strength end def calc_strength cols = template.graphs.size for i in 2..cols rating = rating(i) return i - 1 if rating < 100 return i if rating == 100 end cols end def rating(strength = 2) count_of_combi = Hash.new(0) combi = (1..template.graphs.size).to_a.combination(strength) combi.each {|cols| values = make_values(cols) counts = make_count_hash(values) table.each {|row| key = cols.map {|col| row[col - 1] } counts[key] += 1 } counts.each {|k, v| count_of_combi[v] += 1 } } @@log.info { "#{ strength } => #{ count_of_combi.inspect }" } numerator = (count_of_combi[0] == 0) ? count_of_combi.map {|k, v| k * v }.reduce(:+) : count_of_combi.map {|k, v| k > 0 ? v : 0 }.reduce(:+) 100.0 * numerator / count_of_combi.values.reduce(:+) end def valid?(strength = 2) combi = (1..template.graphs.size).to_a.combination(strength) combi.each {|cols| values = make_values(cols) counts = make_count_hash(values) table.each {|row| key = cols.map {|col| row[col - 1] } counts[key] += 1 } @@log.debug { "#{ cols.inspect } => #{ counts.inspect }" } if counts.has_value?(0) raise "#{ cols.inspect } => #{ counts.reject {|k, v| v != 0 }.keys.sort }" end } return true end def make_values(cols) if cols.size == 1 graph = template.graphs[cols[0] - 1] count = 2 ** graph.points.size return (0...count).map {|x| [x] } end heads = make_values(cols[0, 1]) tails = make_values(cols[1..-1]) values = [] heads.each {|head| tails.each {|tail| values << head + tail } } values end def make_count_hash(keys) hash = Hash.new keys.each {|key| hash[key] = 0 } hash end def print(out = STDOUT) out.puts "\"#{ summary }\"" out.puts " ,#{ header.join(',') }" for i in 0...size out.puts "#{ row0 + i },#{ row(i).join(',') }" end end def summary #"OA(#{ size }; #{ template.levels.join(', ') }; #{ strength })" "OA(#{ size }; #{ template.levels.join(', ') })" end def header (0...template.graphs.count).map {|x| col0 + x } end def row(idx) table[idx].map {|x| val0 + x } end end