#! /usr/bin/python
# PageCracker1c: Size pics then find best place on page. Iterate.
# 178 lines - 43 comment/blank lines = 135 lines of code
# Mike Sweeney (mjs), 07 July 05
# Minimal version - Debug and graphics code removed.

Algorithm = '''
Input, normalize, and index the pictures
While time is left:
    While layout is not complete:
        Find all (possibly overlapping) spaces in current layout
        For each picture (in random order):
            For each space:
                Find best placement of a picture in a space
        Add best placement to layout
    If layout is best, store it
Print best layout
'''

import sys, time, operator, bisect
from string import *
from random import randint, choice, shuffle

F,T = 0,1

def sum(L): return reduce( operator.__add__, L, 0 )
def sort(s): _s=list(s); _s.sort(); return _s
def rsort(s): _s=sort(s); _s.reverse(); return _s

# General purpose class definition:
class obj:
    def __init__(s,**kw): s.__dict__.update(kw)
    def upd(s,**kw): s.__dict__.update(kw)
    def __repr__(s): return `s.__dict__`

ARLIMIT = 0.02

def norm( p, goalarea ):
    # Normalise the picture to goal area and create valid size lists:
    goalarea = max( goalarea, p.minsz )
    ratio = (1.0*goalarea/p.area)**.5
    w,h = int(p.w*ratio), int(p.h*ratio)
    p.wlist = []
    maxwid = max( pg.w, pg.h )
    minwid = int( (pg.area/100)**0.5 )
    inc = 1 + maxwid / 2000
    for pw in range(max(minwid,w-5000),min(maxwid,w+5000),inc) :
        ph1 = int( pw / (p.aspect+0.02) + 1 )
        ph2 = int( pw / (p.aspect-0.02) )
        if ph2-ph1 >= 0:
            heights = [z for z in range(ph1,ph2+1) if pw*z>=p.minsz]
            if not heights: continue
            p.wlist.append( (pw,rsort(heights)) )
    p.ptr = bisect.bisect( p.wlist, (w,[]) )

def mkspace( layout,x,y,vert ):
    # Return the largest space in the layout including (x,y):
    for a in layout:
        if a.x1<=x<=a.x2 and a.y1<=y<=a.y2: return 'InObj'
    x1,y1,x2,y2 = 0, 0, pg.w-1, pg.h-1
    if vert: x1 = x
    else: y1 = y
    for a in layout:
        if vert:
            if a.x1<=x<=a.x2:
                if a.y1 > y: y2=min(y2,a.y1-1)
                else: y1=max(y1,a.y2+1)
        else:
            if a.y1<=y<=a.y2:
                if a.x1 > x: x2=min(x2,a.x1-1)
                else: x1=max(x1,a.x2+1)
    for a in layout:
        if vert:
            if a.y1 > y2 or a.y2 < y1 or a.x2 < x1: continue
            x2 = min(x2,a.x1-1)
        else:
            if a.x1 > x2 or a.x2 < x1 or a.y2 < y1: continue
            y2 = min(y2,a.y1-1)
    w,h = (x2-x1+1), (y2-y1+1)
    if w*h < pg.area/100: return 'TooSmall'
    if min( min(w,h)*10, max(w,h) ) * min(w,h) < pg.area/100: return 'TooThin'
    return obj(x1=x1,y1=y1,x2=x2,y2=y2,w=w,h=h)

def findspaces( layout ):
    # Return a list of all (possibly overlapping) spaces in the layout:
    if not layout: return [ obj(x1=0,y1=0,x2=pg.w-1,y2=pg.h-1,w=pg.w,h=pg.h) ]
    spaces = []
    for p in layout:
        for x,y,vert in ((p.x1,p.y2+1,0),(p.x2+1,p.y1,1)):
            space = mkspace(layout,x,y,vert)
            if type(space) != type(''): spaces.append( space )
    return spaces

def jam( pic, spc, bias ):
    # Try to jam a picture into a space. Return placement and fitness value:
    dim1,dim2 = max(spc.w,spc.h), min(spc.w,spc.h)
    opt = []
    wlist = pic.wlist
    initptr = max( 0, pic.ptr - bias )
    initptr = min( initptr, len(wlist)-1 )
    for w,h in ((dim2,dim1),(dim1,dim2)):
        found = F
        ptr = initptr
        if wlist[0][0] > w : continue
        while ptr >= 0 and wlist[ ptr ][0] > w :
            ptr -= 1
        limit = len(wlist)
        while ptr < limit and ptr < initptr+60 and wlist[ ptr ][0] <= w :
            ptr += 1
        ptr -= 1
        while ptr >= 0 :
            pw,hlist = wlist[ptr]
            if hlist[-1] >= h:
                ptr -= 1
                continue
            for ph in hlist :
                if ph <= h:
                    found = T
                    break
            if found: break
        if found:
            fit = max( 10.0*pw/w, 10.0*ph/h ) / (5+abs(ptr-initptr))
            opt.append( (fit,w,h,pw,ph) )
    if not opt: return None, None
    fit,w,h,pw,ph = sort(opt)[-1] # Choose highest fitness
    place = obj( n=pic.n, area=pw*ph, aspect=1.0*pw/ph )
    if w != spc.w : pw,ph = ph,pw
    place.upd( w=pw, h=ph )
    place.upd( x1=spc.x1, y1=spc.y1, x2=spc.x1+pw-1, y2=spc.y1+ph-1 )
    return fit, place

def getsol( piclist, pg, bias ):
    # Return one layout solution for this size bias:
    shuffle( piclist ) # Or order by size?
    fit, layout = 1, []
    while piclist:
        spaces = findspaces( layout )
        bestfit, bestplace = -1, None
        for i in range(len(piclist)):
            for spc in spaces:
                pic = piclist[i]
                fit, place = jam( pic, spc, bias )
                if fit == None: continue
                if fit > bestfit or randint(1,10)>7:
                    bestfit, bestplace = fit, (i,place)
        if not bestplace: return []
        layout.append( bestplace[1] )
        del piclist[ bestplace[0] ]
    return layout

if __name__ == '__main__':

    endtime = time.clock() + 15

    # Process inputs into objects:
    lines = [split(z) for z in open(sys.argv[1]).readlines() if strip(z)]
    lines = [d[:1]+[int(e) for e in d[1:]] for d in lines]
    pg = obj( w=lines[0][1], h=lines[0][2] )
    pg.upd( area=pg.w*pg.h )
    pics = [ obj(n=a,w=max(b,c),h=min(b,c)) for a,b,c in lines[1:] ]
    for p in pics: p.upd( aspect=1.0*p.w/p.h, area=p.w*p.h, minsz=pg.area/100 )
    pg.picarea = sum([p.area for p in pics])
    for p in pics: norm( p, pg.area/len(pics) )

    # Keep finding layouts until time is up. Keep best one:
    bestarea, bestlayout = -1, []
    bias = 0
    while time.clock() < endtime :
        layout = getsol( pics[:], pg, bias )
        layarea = sum( [ p.area for p in layout] )
        if layarea > bestarea: bestarea, bestlayout = layarea, layout
        if not layout: bias += 5
        else: bias -= 1

    # Print out best solution:
    for n,pl in sort([ (p.n,p) for p in bestlayout ]):
        print '%s %s %s %s %s' % (pl.n,pl.w,pl.h,pl.x1,pl.y1)