~rbn/fit

f8753b45a4555dc265374034235be3874a9b29f2 — Ruben Schuller 1 year, 3 months ago ddb2125
rename strategy to select, export ItemsBySize

package clients should decide if and in which order the input should be sorted,
don't do it automatically in pack.
5 files changed, 49 insertions(+), 34 deletions(-)

M cmd/fit/main.go
M item.go
M pack.go
R strategy.go => select.go
R strategy_test.go => select_test.go
M cmd/fit/main.go => cmd/fit/main.go +26 -14
@@ 6,6 6,7 @@ import (
	"io"
	"log"
	"os"
	"sort"
	"strings"

	"sr.ht/~rbn/fit"


@@ 35,14 36,16 @@ func printBins(bins []fit.Bin, w io.Writer) error {
func main() {
	capacity := flag.Int64("capacity", BRRSL, "bin capacity in bytes")
	blockSize := flag.Int64("blockSize", 1, "block size to multiply the input columns with")
	strategyBest := flag.Bool("best", false, "best fit decreasing")
	strategyFirst := flag.Bool("first", false, "first fit decreasing")
	strategyWorst := flag.Bool("worst", false, "worst fit decreasing")
	selectBest := flag.Bool("best", false, "best fit")
	selectFirst := flag.Bool("first", false, "first fit")
	selectWorst := flag.Bool("worst", false, "worst fit")
	descending := flag.Bool("descending", false, "sort input descending")
	ascending := flag.Bool("ascending", false, "sort input ascending")
	flag.Usage = func() {
		fmt.Fprintf(flag.CommandLine.Output(), "Usage of %s:\n", os.Args[0])
		flag.PrintDefaults()
		fmt.Fprintf(flag.CommandLine.Output(), `
fit uses (first|best|worst) fit decreasing heuristic algorithms [1] to put 
fit uses (first|best|worst) fit (ascending|descending) heuristic algorithms [1] to put 
items into containers of a given capacity. the input is read from stdin and
consists of lines of size and data columns seperated by a tabulator character,
like the output of du.


@@ 75,22 78,22 @@ subdirectories.

	flag.Parse()

	if *strategyBest && *strategyFirst && *strategyWorst {
	if *selectBest && *selectFirst || *selectFirst && *selectWorst || *selectWorst && *selectBest {
		log.Fatal("multiple strategies selected")
	}

	if !(*strategyBest || *strategyFirst || *strategyWorst) {
	if !(*selectBest || *selectFirst || *selectWorst) {
		log.Fatal("no strategy selected")
	}

	var strategy fit.StrategyFunc
	var binSelect fit.BinSelectFunc
	switch {
	case *strategyBest:
		strategy = fit.Best
	case *strategyFirst:
		strategy = fit.First
	case *strategyWorst:
		strategy = fit.Worst
	case *selectBest:
		binSelect = fit.Best
	case *selectFirst:
		binSelect = fit.First
	case *selectWorst:
		binSelect = fit.Worst
	}

	items, err := parseDu(os.Stdin, *blockSize)


@@ 98,7 101,16 @@ subdirectories.
		log.Fatal(err)
	}

	bins, err := fit.Pack(strategy, *capacity, items)
	switch {
	case *descending && *ascending:
		log.Fatal("can't sort descending and ascending at the same time.")
	case *descending:
		sort.Sort(sort.Reverse(fit.ItemsBySize(items)))
	case *ascending:
		sort.Sort(fit.ItemsBySize(items))
	}

	bins, err := fit.Pack(binSelect, *capacity, items)
	if err != nil {
		log.Fatal(err)
	}

M item.go => item.go +4 -4
@@ 5,8 5,8 @@ type Item struct {
	Size int64
}

type itemsBySize []Item
type ItemsBySize []Item

func (x itemsBySize) Len() int           { return len(x) }
func (x itemsBySize) Less(i, j int) bool { return x[i].Size < x[j].Size }
func (x itemsBySize) Swap(i, j int)      { x[i], x[j] = x[j], x[i] }
func (x ItemsBySize) Len() int           { return len(x) }
func (x ItemsBySize) Less(i, j int) bool { return x[i].Size < x[j].Size }
func (x ItemsBySize) Swap(i, j int)      { x[i], x[j] = x[j], x[i] }

M pack.go => pack.go +9 -6
@@ 2,22 2,25 @@ package fit

import (
	"fmt"
	"sort"
)

func Pack(strategy StrategyFunc, capacity int64, items []Item) ([]Bin, error) {
	sort.Sort(itemsBySize(items))
//Pack items into bins.
//
//Each item from items is placed into a bin, according to the bin selection strategy.
//After every item is processed, the resulting bins are returned. If an error occures
//during the process, nil bins and the error are returned.
func Pack(binSelect BinSelectFunc, capacity int64, items []Item) ([]Bin, error) {
	bins := []Bin{}

	for len(items) > 0 {
		var x Item
		x, items = items[len(items)-1], items[:len(items)-1]
		x, items = items[0], items[1:]

		if x.Size > capacity {
			return nil, fmt.Errorf("%v exceeds capacity: size: %v capacity: %v", string(x.Data), x.Size, capacity)
			return nil, fmt.Errorf("item size exceeds bin capacity: size: %v capacity: %v", x.Size, capacity)
		}
		var err error
		bins, err = strategy(bins, capacity, x)
		bins, err = binSelect(bins, capacity, x)
		if err != nil {
			return nil, err
		}

R strategy.go => select.go +1 -1
@@ 57,4 57,4 @@ func Worst(bins []Bin, capacity int64, x Item) ([]Bin, error) {
	return bins, nil
}

type StrategyFunc func([]Bin, int64, Item) ([]Bin, error)
type BinSelectFunc func([]Bin, int64, Item) ([]Bin, error)

R strategy_test.go => select_test.go +9 -9
@@ 5,13 5,13 @@ import (
	"testing"
)

type strategyTest struct {
type selectTest struct {
	item Item
	bin  int
	pos  int
}

var firstTests = []strategyTest{
var firstTests = []selectTest{
	{
		item: Item{Data: []byte("a"), Size: 8},
		bin:  0,


@@ 40,7 40,7 @@ var firstTests = []strategyTest{
}

// one must swap the bins in head, as they are sorted in place. this isn't nice.
var bestTests = []strategyTest{
var bestTests = []selectTest{
	{
		item: Item{Data: []byte("a"), Size: 6},
		bin:  0,


@@ 68,7 68,7 @@ var bestTests = []strategyTest{
	},
}

var worstTests = []strategyTest{
var worstTests = []selectTest{
	{
		item: Item{Data: []byte("a"), Size: 6},
		bin:  0,


@@ 96,12 96,12 @@ var worstTests = []strategyTest{
	},
}

func testStrategy(t *testing.T, strategy StrategyFunc, tests []strategyTest) {
func testSelect(t *testing.T, binSelect BinSelectFunc, tests []selectTest) {
	bins := []Bin{}

	for _, test := range tests {
		var err error
		bins, err = strategy(bins, 10, test.item)
		bins, err = binSelect(bins, 10, test.item)
		if err != nil {
			t.FailNow()
		}


@@ 116,13 116,13 @@ func testStrategy(t *testing.T, strategy StrategyFunc, tests []strategyTest) {
}

func TestFirst(t *testing.T) {
	testStrategy(t, First, firstTests)
	testSelect(t, First, firstTests)
}

func TestBest(t *testing.T) {
	testStrategy(t, Best, bestTests)
	testSelect(t, Best, bestTests)
}

func TestWorst(t *testing.T) {
	testStrategy(t, Worst, worstTests)
	testSelect(t, Worst, worstTests)
}