module Enumerable
  def all?
    self.each{|e|
      unless yield(e)
        return false
      end
    }
    true
  end
end

# Maximum clique solver MCQ

class Graph
  private
  def initialize(fp)
    mask = [
    0b10000000,
    0b01000000,
    0b00100000,
    0b00010000,
    0b00001000,
    0b00000100,
    0b00000010,
    0b00000001
    ]
    fp.rewind
    siz = Integer(fp.gets)
    nv = 0
    fp.read(siz).split(/\n/).each {|l|
      if md = /\Ap\s+\D+\s+(\d+)\s+(\d+)/.match(l)
        nv = md[1].to_i
        break
      end
    }
    @a = Array.new(nv) { Array.new(nv, false) }
    nv.times {|i|
      len = (i+8)/8
      buf = fp.read(len)
      i.times {|j|
        @a[i][j] = @a[j][i] = (buf[j>>3]&mask[j&7]).nonzero?
      }
    }
  end
  
  def sort_noninc(v, n)
    s = @a.size
    d = Array.new(s)
    b = Array.new(s)
    
    # sort
    s.times {|i|
      d[i] = 0
      b[i] = []
      s.times {|j|
        d[i] += 1 if @a[i][j]
      }
    }
    
    m = 0
    s.times {|i|
      m = d[i] if d[i] > m
      b[d[i]] << i
    }
    v.clear
    b.reverse_each {|k|
      next if k.empty?
      k.each {|i| v << i }
    }

    # number
    n.clear
    (0...m).each {|i|
      n << i+1
    }
    (m...s).each {|i|
      n << m+1
    }
  end
  
  def number_sort(v, n)
    b = Array.new(v.size){|e|
      Array.new
    }
    
    v.each {|i|
      b.each {|c|
        if c.all? {|j|
          not @a[i][j]
        } then
          c << i
          break
        end
      }
    }
    v.clear
    
    j = 0
    b.each{|i|
      i.each {|k|
        v << k
        n << j + 1
      }
      j+=1
    }
  end
  
  def expand(v, n, qnow, qmax)
    while not v.empty? and qnow.size + n[v.size - 1] > qmax.size do
      @branch += 1
      p = v.pop
      qnow << p

      
      rp = v.select {|i|
        @a[p][i]
      }
      
      if not rp.empty? then
        # recur
        np = []
        number_sort(rp, np)
        expand(rp, np, qnow, qmax)
      elsif qnow.size > qmax.size then
        qmax.replace qnow
        # printf "candidate size:%d\n", qmax.size
      end
      qnow.pop
    end
    qmax
  end
  
  public
  attr_reader :branch
  def maxclique
    @branch = 0
    v = []
    n = []
    sort_noninc(v, n)
    expand(v, n, [], [])
  end
end


file = ARGV[0] || 'c-fat200-5.clq.b'
g = File.open('../benchmark/contrib/' + file, 'rb') {|fp|
  Graph.new(fp)
}
q = g.maxclique
