############################################################################ # # File: bfd.icn # # Subject: Program to compute best-fit-descending bin packing # # Author: Gregg M. Townsend # # Date: December 4, 1996 # ############################################################################ # # This file is in the public domain. # ############################################################################ # # Usage: bpack binsize [options] [file] # # Input: one entry per line, size in decimal followed by anything else # (anything else presumably being a file name or something) # # Output: all the input lines, unchanged but reordered, # with an empty line before each bin and a total afterward # # Options: # -t don't output anything except unannotated totals # -n don't output anything except the *number* of bins # -b i don't output anything except the details from bin i # ############################################################################ # # Links: options # ############################################################################ # possible options to add later: optional quantization and padding values # (e.g. to use with tar(1) you'd need it to round up to the next # 128 bytes and add 128 bytes for each file header -- or whatever) link options record obj(size,detail) global opts, binsize procedure main(args) local ifile, line, n, d local objlist, bins, o, b opts := options(args, "tnb+") binsize := integer(args[1]) | stop("usage: ", &progname, " binsize") if *args > 1 then ifile := open(args[2]) | stop("can't open ", args[2]) else ifile := &input objlist := [] while line := read(ifile) do line ? { tab(many(' \t')) n := integer(tab(many(&digits))) | next tab(many(' \t')) d := trim(tab(0), ' \t') put(objlist, obj(n, d)) } objlist := sortf(objlist, 1) bins := [] while o := pull(objlist) do { n := bestfit(bins, o.size) put(bins[n].detail, o) bins[n].size +:= o.size } if \opts["n"] then { write(*bins) return } if \opts["t"] then { every write((!bins).size) return } if n := \opts["b"] then { b := bins[n] | stop("no bin ", n, "; only " *bins, " bins") every write((!b.detail).detail) return } while b := get(bins) do { write() while o := get(b.detail) do write(right(o.size, 12), "\t", o.detail) write(right(b.size, 12), "\t--total--") } end procedure bestfit(bins, sz) local b, i, n, d, best every i := 1 to *bins do { b := bins[i] d := binsize - b.size - sz if d < 0 | d > \best then next best := d n := i } if \n then return n else { put(bins, obj(0, [])) return *bins } end