# # LinearGraphTemplate.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 'LinearGraph' class LinearGraphTemplate @@log = Logger.new(STDOUT) @@log.level = Logger::WARN @@log.formatter = proc {|s, d, p, m| sprintf("%-5s %s: %s\n", s, p, m) } class LevelCount attr_reader :level, :count def initialize(level, count) @level = level @count = count end def to_s "#{ level }^#{ count <= 0 ? 'n' : count }" end def inspect to_s end end attr_reader :size, :graphs, :levels def initialize(size, args = '') @size_x = log2(size) @size = 2 ** @size_x @safe_list = (0...@size_x).map {|x| 2 ** x } @graphs = make_graphs(LinearGraph.new([1]), (2...@size).to_a, make_hints(@size_x, args)) @levels = make_levels(@graphs) end def make_hints(size_x, args) hints = args.split(',').map {|x| x =~ /(\d+)(?:\^(\d+))?/ LevelCount.new(2 ** log2($1.to_i), $2.to_i) } if hints.empty? hints = [LevelCount.new(2 ** ((size_x + 1) / 2), 0)] end hints end def log2(x) x.to_s(2).size - 1 end def make_graphs(graph, values, hints) @@log.info { "make_graphs(#{ graph.inspect }, " + "#{ values.inspect }, #{ hints.inspect })" } if values.empty? return [graph] end if hints.empty? || graph.level < hints[0].level points = values # 線点図の点が 2^n の場合、それ以外は検索しない。 if @safe_list.include?(graph.points[0]) points &= @safe_list end candiate = nil dejavu = [] points.each {|x| if !dejavu.include?(x) && graph.points.last < x g = LinearGraph.new([x], graph) lines = g.lines - graph.lines dejavu += lines if lines.all? {|line| values.include?(line) } gs = make_graphs(g, values - g.values, hints) @@log.debug { "gs = #{ gs.inspect }" } # 線点図の点が 2^n の場合、検索を終了する。 return gs if @safe_list.include?(g.points[0]) # 最後のレベルが2より大きい場合、検索を終了する。 return gs if gs.last.level > 2 # 2回まで検索を行う。 if !candiate candiate = gs else if gs.size < candiate.size candiate = gs end return candiate end end end } if candiate return candiate end end hints = next_hints(hints, graph) # レベルが2になったらすべて点で埋める。 if !hints.empty? && hints[0].level == 2 return [graph] + values.map {|x| LinearGraph.new([x]) } end [graph] + make_graphs(LinearGraph.new(values.take(1)), values.drop(1), hints) end def next_hints(hints, graph) hints = hints.drop_while {|x| graph.level < x.level } if hints.empty? return [LevelCount.new(graph.level, 0)] end if graph.level > hints[0].level || hints[0].count <= 0 return hints end count = hints[0].count - 1 if count > 0 return [LevelCount.new(hints[0].level, count)] + hints.drop(1) end hints = hints.drop(1) if hints.empty? return [LevelCount.new(graph.level / 2, 0)] end hints end def make_levels(graphs) levels = [] level = 0; count = 0 graphs.each {|g| if level != g.level if count != 0 levels << LevelCount.new(level, count) end level = g.level count = 1 else count += 1 end } if count != 0 levels << LevelCount.new(level, count) end levels end def make_row(original) row = [] graphs.each {|g| value = 0 g.points.each {|x| value = value * 2 + original[x - 1] } row << value } row end def summary "L#{ size }: #{ levels.join(', ') }" end def to_s "L#{ size }: #{ graphs.join(', ') }" end def inspect to_s end end