Repository: petermattis/pebble
Pull request #205 (2019-08-03 20:52:00)

Add introspection commands to pebble tool
Move the `pebble` benchmarks into a new `bench` subcommand. Add
skeletons for `db`, `manifest`, `sstable`, and `wal` introspection
commands.

Implement `sstable layout` and `sstable properties`. The `sstable
layout` command shows the layout of blocks and records in an
sstable. The `properties` command shows the contents of the properties
block.

See #202

petermattis: 

<!-- Reviewable:start -->
This change is [<img src="https://reviewable.io/review_button.png" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/petermattis/pebble/205)
<!-- Reviewable:end -->


Patch:
diff --git a/cmd/pebble/main.go b/cmd/pebble/main.go
index 30945e3..8971f29 100644
--- a/cmd/pebble/main.go
+++ b/cmd/pebble/main.go
@@ -10,6 +10,7 @@ import (
 	"os"
 	"time"
 
+	"github.com/petermattis/pebble/tool"
 	"github.com/spf13/cobra"
 )
 
@@ -21,27 +22,32 @@ var (
 	maxOpsPerSec    int
 	rocksdb         bool
 	verbose         bool
-	walOnly         bool
 	waitCompactions bool
 	wipe            bool
 )
 
-var rootCmd = &cobra.Command{
-	Use:   "pebble [command] (flags)",
-	Short: "pebble benchmarking/introspection tool",
-	Long:  ``,
-}
-
 func main() {
 	log.SetFlags(0)
 
 	cobra.EnableCommandSorting = false
-	rootCmd.AddCommand(
+
+	benchCmd := &cobra.Command{
+		Use:   "bench",
+		Short: "benchmarks",
+	}
+	benchCmd.AddCommand(
 		scanCmd,
 		syncCmd,
 		ycsbCmd,
 	)
 
+	rootCmd := &cobra.Command{
+		Use:   "pebble [command] (flags)",
+		Short: "pebble benchmarking/introspection tool",
+	}
+	rootCmd.AddCommand(benchCmd)
+	rootCmd.AddCommand(tool.AllCmds...)
+
 	for _, cmd := range []*cobra.Command{scanCmd, syncCmd, ycsbCmd} {
 		cmd.Flags().Int64Var(
 			&cacheSize, "cache", 1<<30, "cache size")
diff --git a/cmd/pebble/ycsb.go b/cmd/pebble/ycsb.go
index f7dd440..11dc785 100644
--- a/cmd/pebble/ycsb.go
+++ b/cmd/pebble/ycsb.go
@@ -44,7 +44,7 @@ var ycsbConfig struct {
 
 var ycsbCmd = &cobra.Command{
 	Use:   "ycsb <dir>",
-	Short: "run customizable YCSB workload",
+	Short: "run customizable YCSB benchmark",
 	Long: `
 Run a customizable YCSB workload. The workload is specified by the --workload
 flag which can take either one of the standard workload mixes (A-F), or
diff --git a/compaction.go b/compaction.go
index 3f7e59f..b00b0ea 100644
--- a/compaction.go
+++ b/compaction.go
@@ -6,12 +6,14 @@ package pebble
 
 import (
 	"bytes"
+	"context"
 	"errors"
 	"fmt"
 	"math"
 	"os"
 	"sort"
 	"sync/atomic"
+	"time"
 	"unsafe"
 
 	"github.com/petermattis/pebble/internal/base"
@@ -68,12 +70,6 @@ type compaction struct {
 	flushing []flushable
 	// bytesIterated contains the number of bytes that have been flushed/compacted.
 	bytesIterated uint64
-	// atomicBytesIterated points to the variable to increment during iteration.
-	// atomicBytesIterated must be read/written atomically. Flushing will increment
-	// the shared variable which compaction will read. This allows for the
-	// compaction routine to know how many bytes have been flushed before the flush
-	// is applied.
-	atomicBytesIterated *uint64
 	// inputs are the tables to be compacted.
 	inputs [2][]fileMetadata
 
@@ -84,13 +80,7 @@ type compaction struct {
 	seenKey         bool   // some output key has been seen
 }
 
-func newCompaction(
-	opts *Options,
-	cur *version,
-	startLevel,
-	baseLevel int,
-	bytesCompacted *uint64,
-) *compaction {
+func newCompaction(opts *Options, cur *version, startLevel, baseLevel int) *compaction {
 	if startLevel > 0 && startLevel < baseLevel {
 		panic(fmt.Sprintf("invalid compaction: start level %d should be empty (base level %d)",
 			startLevel, baseLevel))
@@ -109,34 +99,26 @@ func newCompaction(
 	adjustedOutputLevel := 1 + outputLevel - baseLevel
 
 	return &compaction{
-		cmp:                 opts.Comparer.Compare,
-		version:             cur,
-		startLevel:          startLevel,
-		outputLevel:         outputLevel,
-		maxOutputFileSize:   uint64(opts.Level(adjustedOutputLevel).TargetFileSize),
-		maxOverlapBytes:     maxGrandparentOverlapBytes(opts, adjustedOutputLevel),
-		maxExpandedBytes:    expandedCompactionByteSizeLimit(opts, adjustedOutputLevel),
-		atomicBytesIterated: bytesCompacted,
+		cmp:               opts.Comparer.Compare,
+		version:           cur,
+		startLevel:        startLevel,
+		outputLevel:       outputLevel,
+		maxOutputFileSize: uint64(opts.Level(adjustedOutputLevel).TargetFileSize),
+		maxOverlapBytes:   maxGrandparentOverlapBytes(opts, adjustedOutputLevel),
+		maxExpandedBytes:  expandedCompactionByteSizeLimit(opts, adjustedOutputLevel),
 	}
 }
 
-func newFlush(
-	opts *Options,
-	cur *version,
-	baseLevel int,
-	flushing []flushable,
-	bytesFlushed *uint64,
-) *compaction {
+func newFlush(opts *Options, cur *version, baseLevel int, flushing []flushable) *compaction {
 	c := &compaction{
-		cmp:                 opts.Comparer.Compare,
-		version:             cur,
-		startLevel:          -1,
-		outputLevel:         0,
-		maxOutputFileSize:   math.MaxUint64,
-		maxOverlapBytes:     math.MaxUint64,
-		maxExpandedBytes:    math.MaxUint64,
-		flushing:            flushing,
-		atomicBytesIterated: bytesFlushed,
+		cmp:               opts.Comparer.Compare,
+		version:           cur,
+		startLevel:        -1,
+		outputLevel:       0,
+		maxOutputFileSize: math.MaxUint64,
+		maxOverlapBytes:   math.MaxUint64,
+		maxExpandedBytes:  math.MaxUint64,
+		flushing:          flushing,
 	}
 
 	// TODO(peter): When we allow flushing to create multiple tables we'll want
@@ -595,26 +577,6 @@ type manualCompaction struct {
 	end         InternalKey
 }
 
-func (d *DB) getCompactionPacerInfo() compactionPacerInfo {
-	bytesFlushed := atomic.LoadUint64(&d.bytesFlushed)
-
-	d.mu.Lock()
-	estimatedMaxWAmp := d.mu.versions.picker.estimatedMaxWAmp
-	pacerInfo := compactionPacerInfo{
-		slowdownThreshold: uint64(estimatedMaxWAmp * float64(d.opts.MemTableSize)),
-		totalCompactionDebt: d.mu.versions.picker.estimatedCompactionDebt(bytesFlushed),
-	}
-	d.mu.Unlock()
-
-	return pacerInfo
-}
-
-func (d *DB) getFlushPacerInfo() flushPacerInfo {
-	return flushPacerInfo{
-		totalBytes: d.memTableTotalBytes(),
-	}
-}
-
 // maybeScheduleFlush schedules a flush if necessary.
 //
 // d.mu must be held when calling this.
@@ -670,7 +632,7 @@ func (d *DB) flush1() error {
 	}
 
 	c := newFlush(d.opts, d.mu.versions.currentVersion(),
-		d.mu.versions.picker.baseLevel, d.mu.mem.queue[:n], &d.bytesFlushed)
+		d.mu.versions.picker.baseLevel, d.mu.mem.queue[:n])
 
 	jobID := d.mu.nextJobID
 	d.mu.nextJobID++
@@ -680,12 +642,7 @@ func (d *DB) flush1() error {
 		})
 	}
 
-	flushPacer := newFlushPacer(flushPacerEnv{
-		limiter:      d.flushLimiter,
-		memTableSize: uint64(d.opts.MemTableSize),
-		getInfo:      d.getFlushPacerInfo,
-	})
-	ve, pendingOutputs, err := d.runCompaction(c, flushPacer)
+	ve, pendingOutputs, err := d.runCompaction(c)
 
 	if d.opts.EventListener.FlushEnd != nil {
 		info := FlushInfo{
@@ -797,12 +754,12 @@ func (d *DB) compact1() (err error) {
 	if len(d.mu.compact.manual) > 0 {
 		manual := d.mu.compact.manual[0]
 		d.mu.compact.manual = d.mu.compact.manual[1:]
-		c = d.mu.versions.picker.pickManual(d.opts, manual, &d.bytesCompacted)
+		c = d.mu.versions.picker.pickManual(d.opts, manual)
 		defer func() {
 			manual.done <- err
 		}()
 	} else {
-		c = d.mu.versions.picker.pickAuto(d.opts, &d.bytesCompacted)
+		c = d.mu.versions.picker.pickAuto(d.opts)
 	}
 	if c == nil {
 		return nil
@@ -827,12 +784,7 @@ func (d *DB) compact1() (err error) {
 		d.opts.EventListener.CompactionBegin(info)
 	}
 
-	compactionPacer := newCompactionPacer(compactionPacerEnv{
-		limiter:      d.compactionLimiter,
-		memTableSize: uint64(d.opts.MemTableSize),
-		getInfo:      d.getCompactionPacerInfo,
-	})
-	ve, pendingOutputs, err := d.runCompaction(c, compactionPacer)
+	ve, pendingOutputs, err := d.runCompaction(c)
 
 	if d.opts.EventListener.CompactionEnd != nil {
 		info.Err = err
@@ -869,7 +821,7 @@ func (d *DB) compact1() (err error) {
 //
 // d.mu must be held when calling this, but the mutex may be dropped and
 // re-acquired during the course of this method.
-func (d *DB) runCompaction(c *compaction, pacer pacer) (
+func (d *DB) runCompaction(c *compaction) (
 	ve *versionEdit, pendingOutputs []uint64, retErr error,
 ) {
 	// Check for a trivial move of one table from one level to the next. We avoid
@@ -1046,13 +998,118 @@ func (d *DB) runCompaction(c *compaction, pacer pacer) (
 		return nil
 	}
 
+	var prevBytesIterated uint64
+	var iterCount int
+	totalBytes := d.memTableTotalBytes()
+	refreshDirtyBytesThreshold := uint64(d.opts.MemTableSize * 5 / 100)
+
+	var compactionSlowdownThreshold uint64
+	var totalCompactionDebt uint64
+	var estimatedMaxWAmp float64
+
 	for key, val := iter.First(); key != nil; key, val = iter.Next() {
-		atomic.StoreUint64(c.atomicBytesIterated, c.bytesIterated)
+		// Slow down memtable flushing to match fill rate.
+		if c.flushing != nil {
+			// Recalculate total memtable bytes only once every 1000 iterations or
+			// when the refresh threshold is hit since getting the total memtable
+			// byte count requires grabbing DB.mu which is expensive.
+			if iterCount >= 1000 || c.bytesIterated > refreshDirtyBytesThreshold {
+				totalBytes = d.memTableTotalBytes()
+				refreshDirtyBytesThreshold = c.bytesIterated + uint64(d.opts.MemTableSize*5/100)
+				iterCount = 0
+			}
+			iterCount++
+
+			// dirtyBytes is the total number of bytes in the memtables minus the number of
+			// bytes flushed. It represents unflushed bytes in all the memtables, even the
+			// ones which aren't being flushed such as the mutable memtable.
+			dirtyBytes := totalBytes - c.bytesIterated
+			flushAmount := c.bytesIterated - prevBytesIterated
+			prevBytesIterated = c.bytesIterated
+
+			atomic.StoreUint64(&d.bytesFlushed, c.bytesIterated)
+
+			// We slow down memtable flushing when the dirty bytes indicator falls
+			// below the low watermark, which is 105% memtable size. This will only
+			// occur if memtable flushing can keep up with the pace of incoming
+			// writes. If writes come in faster than how fast the memtable can flush,
+			// flushing proceeds at maximum (unthrottled) speed.
+			if dirtyBytes <= uint64(d.opts.MemTableSize*105/100) {
+				burst := d.flushLimiter.Burst()
+				for flushAmount > uint64(burst) {
+					err := d.flushLimiter.WaitN(context.Background(), burst)
+					if err != nil {
+						return nil, pendingOutputs, err
+					}
+					flushAmount -= uint64(burst)
+				}
+				err := d.flushLimiter.WaitN(context.Background(), int(flushAmount))
+				if err != nil {
+					return nil, pendingOutputs, err
+				}
+			} else {
+				burst := d.flushLimiter.Burst()
+				for flushAmount > uint64(burst) {
+					d.flushLimiter.AllowN(time.Now(), burst)
+					flushAmount -= uint64(burst)
+				}
+				d.flushLimiter.AllowN(time.Now(), int(flushAmount))
+			}
+		} else {
+			bytesFlushed := atomic.LoadUint64(&d.bytesFlushed)
+
+			if iterCount >= 1000 || c.bytesIterated > refreshDirtyBytesThreshold {
+				d.mu.Lock()
+				estimatedMaxWAmp = d.mu.versions.picker.estimatedMaxWAmp
+				// compactionSlowdownThreshold is the low watermark for compaction debt. If compaction
+				// debt is below this threshold, we slow down compactions. If compaction debt is above
+				// this threshold, we let compactions continue as fast as possible. We want to keep
+				// compaction speed as slow as possible to match the speed of flushes. This threshold
+				// is set so that a single flush cannot contribute enough compaction debt to overshoot
+				// the threshold.
+				compactionSlowdownThreshold = uint64(estimatedMaxWAmp * float64(d.opts.MemTableSize))
+				totalCompactionDebt = d.mu.versions.picker.estimatedCompactionDebt(bytesFlushed)
+				d.mu.Unlock()
+				refreshDirtyBytesThreshold = c.bytesIterated + uint64(d.opts.MemTableSize*5/100)
+				iterCount = 0
+			}
+			iterCount++
 
-		if err := pacer.maybeThrottle(c.bytesIterated); err != nil {
-			return nil, pendingOutputs, err
-		}
+			var curCompactionDebt uint64
+			if totalCompactionDebt > c.bytesIterated {
+				curCompactionDebt = totalCompactionDebt - c.bytesIterated
+			}
 
+			compactAmount := c.bytesIterated - prevBytesIterated
+			// We slow down compactions when the compaction debt falls below the slowdown
+			// threshold, which is set dynamically based on the number of non-empty levels.
+			// This will only occur if compactions can keep up with the pace of flushes. If
+			// bytes are flushed faster than how fast compactions can occur, compactions
+			// proceed at maximum (unthrottled) speed.
+			if curCompactionDebt <= compactionSlowdownThreshold {
+				burst := d.compactionLimiter.Burst()
+				for compactAmount > uint64(burst) {
+					err := d.compactionLimiter.WaitN(context.Background(), burst)
+					if err != nil {
+						return nil, pendingOutputs, err
+					}
+					compactAmount -= uint64(burst)
+				}
+				err := d.compactionLimiter.WaitN(context.Background(), int(compactAmount))
+				if err != nil {
+					return nil, pendingOutputs, err
+				}
+			} else {
+				burst := d.compactionLimiter.Burst()
+				for compactAmount > uint64(burst) {
+					d.compactionLimiter.AllowN(time.Now(), burst)
+					compactAmount -= uint64(burst)
+				}
+				d.compactionLimiter.AllowN(time.Now(), int(compactAmount))
+			}
+
+			prevBytesIterated = c.bytesIterated
+		}
 		// TODO(peter,rangedel): Need to incorporate the range tombstones in the
 		// shouldStopBefore decision.
 		if tw != nil && (tw.EstimatedSize() >= c.maxOutputFileSize || c.shouldStopBefore(*key)) {
diff --git a/compaction_picker.go b/compaction_picker.go
index af5694b..899bedd 100644
--- a/compaction_picker.go
+++ b/compaction_picker.go
@@ -251,16 +251,13 @@ func (p *compactionPicker) initTarget(v *version, opts *Options) {
 }
 
 // pickAuto picks the best compaction, if any.
-func (p *compactionPicker) pickAuto(
-	opts *Options,
-	bytesCompacted *uint64,
-) (c *compaction) {
+func (p *compactionPicker) pickAuto(opts *Options) (c *compaction) {
 	if !p.compactionNeeded() {
 		return nil
 	}
 
 	vers := p.vers
-	c = newCompaction(opts, vers, p.level, p.baseLevel, bytesCompacted)
+	c = newCompaction(opts, vers, p.level, p.baseLevel)
 	c.inputs[0] = vers.files[c.startLevel][p.file : p.file+1]
 
 	// Files in level 0 may overlap each other, so pick up all overlapping ones.
@@ -277,18 +274,14 @@ func (p *compactionPicker) pickAuto(
 	return c
 }
 
-func (p *compactionPicker) pickManual(
-	opts *Options,
-	manual *manualCompaction,
-	bytesCompacted *uint64,
-) (c *compaction) {
+func (p *compactionPicker) pickManual(opts *Options, manual *manualCompaction) (c *compaction) {
 	if p == nil {
 		return nil
 	}
 
 	// TODO(peter): The logic here is untested and possibly incomplete.
 	cur := p.vers
-	c = newCompaction(opts, cur, manual.level, p.baseLevel, bytesCompacted)
+	c = newCompaction(opts, cur, manual.level, p.baseLevel)
 	manual.outputLevel = c.outputLevel
 	cmp := opts.Comparer.Compare
 	c.inputs[0] = cur.overlaps(manual.level, cmp, manual.start.UserKey, manual.end.UserKey)
diff --git a/db.go b/db.go
index 9dc9f42..1b576d2 100644
--- a/db.go
+++ b/db.go
@@ -14,6 +14,7 @@ import (
 
 	"github.com/petermattis/pebble/internal/arenaskl"
 	"github.com/petermattis/pebble/internal/base"
+	"github.com/petermattis/pebble/internal/rate"
 	"github.com/petermattis/pebble/internal/record"
 	"github.com/petermattis/pebble/vfs"
 )
@@ -172,18 +173,14 @@ type DB struct {
 
 	closed int32 // updated atomically
 
-	compactionLimiter limiter
+	compactionLimiter *rate.Limiter
 
 	// bytesFlushed is the number of bytes flushed in the current flush. This
 	// must be read/written atomically since it is accessed by both the flush
 	// and compaction routines.
 	bytesFlushed uint64
-	// bytesCompacted is the number of bytes compacted in the current compaction.
-	// This is used as a dummy variable to increment during compaction, and the
-	// value is not used anywhere.
-	bytesCompacted uint64
 
-	flushLimiter limiter
+	flushLimiter *rate.Limiter
 
 	// TODO(peter): describe exactly what this mutex protects. So far: every
 	// field in the struct.
diff --git a/ingest.go b/ingest.go
index a4759f2..0388577 100644
--- a/ingest.go
+++ b/ingest.go
@@ -39,8 +39,11 @@ func ingestLoad1(opts *Options, path string, dbNum, fileNum uint64) (*fileMetada
 		return nil, err
 	}
 
-	r := sstable.NewReader(f, dbNum, fileNum, opts)
+	r, err := sstable.NewReader(f, dbNum, fileNum, opts)
 	defer r.Close()
+	if err != nil {
+		return nil, err
+	}
 
 	meta := &fileMetadata{}
 	meta.fileNum = fileNum
diff --git a/internal/base/options.go b/internal/base/options.go
index b035800..a23bac0 100644
--- a/internal/base/options.go
+++ b/internal/base/options.go
@@ -241,8 +241,6 @@ type Options struct {
 	// debugging tools which may be used on multiple databases configured with
 	// different comparers. It is not necessary to populate this comparers map
 	// during normal usage of a DB.
-	//
-	// TODO(peter): unimplemented.
 	Comparers map[string]*Comparer
 
 	// Disable the write-ahead log (WAL). Disabling the write-ahead log prohibits
@@ -265,8 +263,6 @@ type Options struct {
 	// debugging tools which may be used on multiple databases configured with
 	// different filter policies. It is not necessary to populate this filters
 	// map during normal usage of a DB.
-	//
-	// TODO(peter): unimplemented.
 	Filters map[string]FilterPolicy
 
 	// FS provides the interface for persistent file storage.
@@ -330,8 +326,6 @@ type Options struct {
 	// tools which may be used on multiple databases configured with different
 	// mergers. It is not necessary to populate this mergers map during normal
 	// usage of a DB.
-	//
-	// TODO(peter): unimplemented.
 	Mergers map[string]*Merger
 
 	// MinCompactionRate sets the minimum rate at which compactions occur. The
@@ -378,6 +372,9 @@ func (o *Options) EnsureDefaults() *Options {
 	if o.Comparer == nil {
 		o.Comparer = DefaultComparer
 	}
+	if o.FS == nil {
+		o.FS = vfs.Default
+	}
 	if o.L0CompactionThreshold <= 0 {
 		o.L0CompactionThreshold = 4
 	}
@@ -428,12 +425,43 @@ func (o *Options) EnsureDefaults() *Options {
 	if o.MinFlushRate == 0 {
 		o.MinFlushRate = 1 << 20 // 1 MB/s
 	}
-	if o.FS == nil {
-		o.FS = vfs.Default
-	}
+
+	o.initMaps()
 	return o
 }
 
+// initMaps initializes the Comparers, Filters, and Mergers maps.
+func (o *Options) initMaps() {
+	if o.Comparers == nil {
+		o.Comparers = make(map[string]*Comparer)
+	}
+	if _, ok := o.Comparers[o.Comparer.Name]; !ok {
+		o.Comparers[o.Comparer.Name] = o.Comparer
+	}
+
+	if o.Merger != nil {
+		if o.Mergers == nil {
+			o.Mergers = make(map[string]*Merger)
+		}
+		if _, ok := o.Mergers[o.Merger.Name]; !ok {
+			o.Mergers[o.Merger.Name] = o.Merger
+		}
+	}
+
+	for i := range o.Levels {
+		l := &o.Levels[i]
+		if l.FilterPolicy != nil {
+			if o.Filters == nil {
+				o.Filters = make(map[string]FilterPolicy)
+			}
+			name := l.FilterPolicy.Name()
+			if _, ok := o.Filters[name]; !ok {
+				o.Filters[name] = l.FilterPolicy
+			}
+		}
+	}
+}
+
 // Level returns the LevelOptions for the specified level.
 func (o *Options) Level(level int) LevelOptions {
 	if level < len(o.Levels) {
@@ -447,6 +475,15 @@ func (o *Options) Level(level int) LevelOptions {
 	return l
 }
 
+// Clone creates a shallow-copy of the supplied options.
+func (o *Options) Clone() *Options {
+	n := &Options{}
+	if o != nil {
+		*n = *o
+	}
+	return n
+}
+
 func (o *Options) String() string {
 	var buf bytes.Buffer
 
diff --git a/open.go b/open.go
index 9a4aea1..f628dff 100644
--- a/open.go
+++ b/open.go
@@ -69,7 +69,10 @@ func createDB(dirname string, opts *Options) (retErr error) {
 
 // Open opens a LevelDB whose files live in the given directory.
 func Open(dirname string, opts *Options) (*DB, error) {
+	// Make a copy of the options so that we don't mutate the passed in options.
+	opts = opts.Clone()
 	opts = opts.EnsureDefaults()
+
 	d := &DB{
 		dbNum:          allocDBNum(),
 		dirname:        dirname,
@@ -338,8 +341,8 @@ func (d *DB) replayWAL(
 
 	if mem != nil && !mem.empty() {
 		c := newFlush(d.opts, d.mu.versions.currentVersion(),
-			1 /* base level */, []flushable{mem}, &d.bytesFlushed)
-		newVE, pendingOutputs, err := d.runCompaction(c, nilPacer)
+			1 /* base level */, []flushable{mem})
+		newVE, pendingOutputs, err := d.runCompaction(c)
 		if err != nil {
 			return 0, err
 		}
diff --git a/pacer.go b/pacer.go
deleted file mode 100644
index 4b42d9c..0000000
--- a/pacer.go
+++ /dev/null
@@ -1,219 +0,0 @@
-// Copyright 2019 The LevelDB-Go and Pebble Authors. All rights reserved. Use
-// of this source code is governed by a BSD-style license that can be found in
-// the LICENSE file.
-
-package pebble
-
-import (
-	"context"
-	"errors"
-	"time"
-)
-
-var nilPacer = &noopPacer{}
-
-type limiter interface {
-	WaitN(ctx context.Context, n int) (err error)
-	AllowN(now time.Time, n int) bool
-	Burst() int
-}
-
-// pacer is the interface for flush and compaction rate limiters. The rate limiter
-// is possible applied on each iteration step of a flush or compaction. This is to
-// limit background IO usage so that it does not contend with foreground traffic.
-type pacer interface {
-	maybeThrottle(bytesIterated uint64) error
-}
-
-// internalPacer contains fields and methods common to both compactionPacer and
-// flushPacer.
-type internalPacer struct {
-	limiter limiter
-
-	iterCount             uint64
-	prevBytesIterated     uint64
-	refreshBytesThreshold uint64
-	slowdownThreshold     uint64
-}
-
-// limit applies rate limiting if the current byte level is below the configured
-// threshold.
-func (p *internalPacer) limit(amount, currentLevel uint64) error {
-	if currentLevel <= p.slowdownThreshold {
-		burst := p.limiter.Burst()
-		for amount > uint64(burst) {
-			err := p.limiter.WaitN(context.Background(), burst)
-			if err != nil {
-				return err
-			}
-			amount -= uint64(burst)
-		}
-		err := p.limiter.WaitN(context.Background(), int(amount))
-		if err != nil {
-			return err
-		}
-	} else {
-		burst := p.limiter.Burst()
-		for amount > uint64(burst) {
-			p.limiter.AllowN(time.Now(), burst)
-			amount -= uint64(burst)
-		}
-		p.limiter.AllowN(time.Now(), int(amount))
-	}
-	return nil
-}
-
-// compactionPacerInfo contains information necessary for compaction pacing.
-type compactionPacerInfo struct {
-	// slowdownThreshold is the low watermark for compaction debt. If compaction debt is
-	// below this threshold, we slow down compactions. If compaction debt is above this
-	// threshold, we let compactions continue as fast as possible. We want to keep
-	// compaction speed as slow as possible to match the speed of flushes. This threshold
-	// is set so that a single flush cannot contribute enough compaction debt to overshoot
-	// the threshold.
-	slowdownThreshold   uint64
-	totalCompactionDebt uint64
-}
-
-// compactionPacerEnv defines the environment in which the compaction rate limiter
-// is applied.
-type compactionPacerEnv struct {
-	limiter      limiter
-	memTableSize uint64
-
-	getInfo func() compactionPacerInfo
-}
-
-// compactionPacer rate limits compactions depending on compaction debt. The rate
-// limiter is applied at a rate that keeps compaction debt at a steady level. If
-// compaction debt increases at a rate that is faster than the system can handle,
-// no rate limit is applied.
-type compactionPacer struct {
-	internalPacer
-	env                 compactionPacerEnv
-	totalCompactionDebt uint64
-}
-
-func newCompactionPacer(env compactionPacerEnv) *compactionPacer {
-	return &compactionPacer{
-		env: env,
-		internalPacer: internalPacer{
-			limiter: env.limiter,
-		},
-	}
-}
-
-// maybeThrottle slows down compactions to match memtable flush rate. The DB
-// provides a compaction debt estimate and a slowdown threshold. We subtract the
-// compaction debt estimate by the bytes iterated in the current compaction. If
-// the new compaction debt estimate is below the threshold, the rate limiter is
-// applied. If the new compaction debt is above the threshold, the rate limiter
-// is not applied.
-func (p *compactionPacer) maybeThrottle(bytesIterated uint64) error {
-	if bytesIterated == 0 {
-		return errors.New("pebble: maybeThrottle supplied with invalid bytesIterated")
-	}
-
-	// Recalculate total compaction debt and the slowdown threshold only once
-	// every 1000 iterations or when the refresh threshold is hit since it
-	// requires grabbing DB.mu which is expensive.
-	if p.iterCount == 0 || bytesIterated > p.refreshBytesThreshold {
-		pacerInfo := p.env.getInfo()
-		p.slowdownThreshold = pacerInfo.slowdownThreshold
-		p.totalCompactionDebt = pacerInfo.totalCompactionDebt
-		p.refreshBytesThreshold = bytesIterated + (p.env.memTableSize*5/100)
-		p.iterCount = 1000
-	}
-	p.iterCount--
-
-	var curCompactionDebt uint64
-	if p.totalCompactionDebt > bytesIterated {
-		curCompactionDebt = p.totalCompactionDebt - bytesIterated
-	}
-
-	compactAmount := bytesIterated - p.prevBytesIterated
-	p.prevBytesIterated = bytesIterated
-
-	// We slow down compactions when the compaction debt falls below the slowdown
-	// threshold, which is set dynamically based on the number of non-empty levels.
-	// This will only occur if compactions can keep up with the pace of flushes. If
-	// bytes are flushed faster than how fast compactions can occur, compactions
-	// proceed at maximum (unthrottled) speed.
-	return p.limit(compactAmount, curCompactionDebt)
-}
-
-// flushPacerInfo contains information necessary for compaction pacing.
-type flushPacerInfo struct {
-	totalBytes uint64
-}
-
-// flushPacerEnv defines the environment in which the compaction rate limiter is
-// applied.
-type flushPacerEnv struct {
-	limiter      limiter
-	memTableSize uint64
-
-	getInfo func() flushPacerInfo
-}
-
-// flushPacer rate limits memtable flushing to match the speed of incoming user
-// writes. If user writes come in faster than the memtable can be flushed, no
-// rate limit is applied.
-type flushPacer struct {
-	internalPacer
-	env        flushPacerEnv
-	totalBytes uint64
-}
-
-func newFlushPacer(env flushPacerEnv) *flushPacer {
-	return &flushPacer{
-		env: env,
-		internalPacer: internalPacer{
-			limiter:           env.limiter,
-			slowdownThreshold: env.memTableSize*105/100,
-		},
-	}
-}
-
-// maybeThrottle slows down memtable flushing to match user write rate. The DB
-// provides the total number of bytes in all the memtables. We subtract this total
-// by the number of bytes flushed in the current flush to get a "dirty byte" count.
-// If the dirty byte count is below the watermark (105% memtable size), the rate
-// limiter is applied. If the dirty byte count is above the watermark, the rate
-// limiter is not applied.
-func (p *flushPacer) maybeThrottle(bytesIterated uint64) error {
-	if bytesIterated == 0 {
-		return errors.New("pebble: maybeThrottle supplied with invalid bytesIterated")
-	}
-
-	// Recalculate total memtable bytes only once every 1000 iterations or
-	// when the refresh threshold is hit since getting the total memtable
-	// byte count requires grabbing DB.mu which is expensive.
-	if p.iterCount == 0 || bytesIterated > p.refreshBytesThreshold {
-		pacerInfo := p.env.getInfo()
-		p.totalBytes = pacerInfo.totalBytes
-		p.refreshBytesThreshold = bytesIterated + (p.env.memTableSize*5/100)
-		p.iterCount = 1000
-	}
-	p.iterCount--
-
-	// dirtyBytes is the total number of bytes in the memtables minus the number of
-	// bytes flushed. It represents unflushed bytes in all the memtables, even the
-	// ones which aren't being flushed such as the mutable memtable.
-	dirtyBytes := p.totalBytes - bytesIterated
-	flushAmount := bytesIterated - p.prevBytesIterated
-	p.prevBytesIterated = bytesIterated
-
-	// We slow down memtable flushing when the dirty bytes indicator falls
-	// below the low watermark, which is 105% memtable size. This will only
-	// occur if memtable flushing can keep up with the pace of incoming
-	// writes. If writes come in faster than how fast the memtable can flush,
-	// flushing proceeds at maximum (unthrottled) speed.
-	return p.limit(flushAmount, dirtyBytes)
-}
-
-type noopPacer struct {}
-
-func (p *noopPacer) maybeThrottle(_ uint64) error {
-	return nil
-}
diff --git a/sstable/properties.go b/sstable/properties.go
index f5a0f43..58041e2 100644
--- a/sstable/properties.go
+++ b/sstable/properties.go
@@ -57,8 +57,8 @@ type Properties struct {
 	// Name of the column family with which this SST file is associated. Empty if
 	// the column family is unknown.
 	ColumnFamilyName string `prop:"rocksdb.column.family.name"`
-	// The name of the comparator used in this table.
-	ComparatorName string `prop:"rocksdb.comparator"`
+	// The name of the comparer used in this table.
+	ComparerName string `prop:"rocksdb.comparator"`
 	// The compression algorithm used to compress blocks.
 	CompressionName string `prop:"rocksdb.compression"`
 	// The compression options used to compress blocks.
@@ -92,9 +92,8 @@ type Properties struct {
 	IndexType uint32 `prop:"rocksdb.block.based.table.index.type"`
 	// Whether delta encoding is used to encode the index values.
 	IndexValueIsDeltaEncoded uint64 `prop:"rocksdb.index.value.is.delta.encoded"`
-	// The name of the merge operator used in this table. Empty if no merge
-	// operator is used.
-	MergeOperatorName string `prop:"rocksdb.merge.operator"`
+	// The name of the merger used in this table. Empty if no merger is used.
+	MergerName string `prop:"rocksdb.merge.operator"`
 	// The number of blocks in this table.
 	NumDataBlocks uint64 `prop:"rocksdb.num.data.blocks"`
 	// The number of deletion entries in this table.
@@ -168,7 +167,7 @@ func (p *Properties) String() string {
 	}
 	sort.Strings(keys)
 	for _, key := range keys {
-		fmt.Fprintf(&buf, "%s: %s\n", keys, p.UserProperties[key])
+		fmt.Fprintf(&buf, "%s: %s\n", key, p.UserProperties[key])
 	}
 	return buf.String()
 }
@@ -254,8 +253,8 @@ func (p *Properties) save(w *rawBlockWriter) {
 	if p.ColumnFamilyName != "" {
 		p.saveString(m, unsafe.Offsetof(p.ColumnFamilyName), p.ColumnFamilyName)
 	}
-	if p.ComparatorName != "" {
-		p.saveString(m, unsafe.Offsetof(p.ComparatorName), p.ComparatorName)
+	if p.ComparerName != "" {
+		p.saveString(m, unsafe.Offsetof(p.ComparerName), p.ComparerName)
 	}
 	if p.CompressionName != "" {
 		p.saveString(m, unsafe.Offsetof(p.CompressionName), p.CompressionName)
@@ -283,8 +282,8 @@ func (p *Properties) save(w *rawBlockWriter) {
 	p.saveUvarint(m, unsafe.Offsetof(p.IndexSize), p.IndexSize)
 	p.saveUint32(m, unsafe.Offsetof(p.IndexType), p.IndexType)
 	p.saveUvarint(m, unsafe.Offsetof(p.IndexValueIsDeltaEncoded), p.IndexValueIsDeltaEncoded)
-	if p.MergeOperatorName != "" {
-		p.saveString(m, unsafe.Offsetof(p.MergeOperatorName), p.MergeOperatorName)
+	if p.MergerName != "" {
+		p.saveString(m, unsafe.Offsetof(p.MergerName), p.MergerName)
 	}
 	p.saveUvarint(m, unsafe.Offsetof(p.NumDataBlocks), p.NumDataBlocks)
 	p.saveUvarint(m, unsafe.Offsetof(p.NumEntries), p.NumEntries)
diff --git a/sstable/reader.go b/sstable/reader.go
index 1c63476..5d1a5a5 100644
--- a/sstable/reader.go
+++ b/sstable/reader.go
@@ -9,7 +9,10 @@ import (
 	"encoding/binary"
 	"errors"
 	"fmt"
+	"io"
+	"sort"
 	"sync"
+	"unsafe"
 
 	"github.com/golang/snappy"
 	"github.com/petermattis/pebble/cache"
@@ -19,26 +22,26 @@ import (
 	"github.com/petermattis/pebble/vfs"
 )
 
-// blockHandle is the file offset and length of a block.
-type blockHandle struct {
-	offset, length uint64
+// BlockHandle is the file offset and length of a block.
+type BlockHandle struct {
+	Offset, Length uint64
 }
 
 // decodeBlockHandle returns the block handle encoded at the start of src, as
 // well as the number of bytes it occupies. It returns zero if given invalid
 // input.
-func decodeBlockHandle(src []byte) (blockHandle, int) {
+func decodeBlockHandle(src []byte) (BlockHandle, int) {
 	offset, n := binary.Uvarint(src)
 	length, m := binary.Uvarint(src[n:])
 	if n == 0 || m == 0 {
-		return blockHandle{}, 0
+		return BlockHandle{}, 0
 	}
-	return blockHandle{offset, length}, n + m
+	return BlockHandle{offset, length}, n + m
 }
 
-func encodeBlockHandle(dst []byte, b blockHandle) int {
-	n := binary.PutUvarint(dst, b.offset)
-	m := binary.PutUvarint(dst[n:], b.length)
+func encodeBlockHandle(dst []byte, b BlockHandle) int {
+	n := binary.PutUvarint(dst, b.Offset)
+	m := binary.PutUvarint(dst[n:], b.Length)
 	return n + m
 }
 
@@ -69,7 +72,7 @@ type singleLevelIterator struct {
 	reader     *Reader
 	index      blockIter
 	data       blockIter
-	dataBH     blockHandle
+	dataBH     BlockHandle
 	err        error
 	closeHook  func(i Iterator) error
 }
@@ -289,7 +292,7 @@ func (i *singleLevelIterator) SeekLT(key []byte) (*InternalKey, []byte) {
 		// be chosen as "compleu". The SeekGE in the index block will then point
 		// us to the block containing "complexion". If this happens, we want the
 		// last key from the previous data block.
-		if ikey, val = i.index.Prev(); ikey == nil {
+		if ikey, _ = i.index.Prev(); ikey == nil {
 			return nil, nil
 		}
 		if !i.loadBlock() {
@@ -513,20 +516,20 @@ func (i *compactionIterator) First() (*InternalKey, []byte) {
 		// An empty sstable will still encode the block trailer and restart points, so bytes
 		// iterated must be incremented.
 
-		// We must use i.dataBH.length instead of (4*(i.data.numRestarts+1)) to calculate the
-		// number of bytes for the restart points, since i.dataBH.length accounts for
-		// compression. When uncompressed, i.dataBH.length == (4*(i.data.numRestarts+1))
-		*i.bytesIterated += blockTrailerLen + i.dataBH.length
+		// We must use i.dataBH.Length instead of (4*(i.data.numRestarts+1)) to calculate the
+		// number of bytes for the restart points, since i.dataBH.Length accounts for
+		// compression. When uncompressed, i.dataBH.Length == (4*(i.data.numRestarts+1))
+		*i.bytesIterated += blockTrailerLen + i.dataBH.Length
 		return nil, nil
 	}
 	// If the sstable only has 1 entry, we are at the last entry in the block and we must
 	// increment bytes iterated by the size of the block trailer and restart points.
 	if i.data.nextOffset+(4*(i.data.numRestarts+1)) == int32(len(i.data.data)) {
-		i.prevOffset = blockTrailerLen + i.dataBH.length
+		i.prevOffset = blockTrailerLen + i.dataBH.Length
 	} else {
-		// i.dataBH.length/len(i.data.data) is the compression ratio. If uncompressed, this is 1.
+		// i.dataBH.Length/len(i.data.data) is the compression ratio. If uncompressed, this is 1.
 		// i.data.nextOffset is the uncompressed size of the first record.
-		i.prevOffset = (uint64(i.data.nextOffset) * i.dataBH.length) / uint64(len(i.data.data))
+		i.prevOffset = (uint64(i.data.nextOffset) * i.dataBH.Length) / uint64(len(i.data.data))
 	}
 	*i.bytesIterated += i.prevOffset
 	return key, val
@@ -562,15 +565,15 @@ func (i *compactionIterator) Next() (*InternalKey, []byte) {
 		}
 	}
 
-	// i.dataBH.length/len(i.data.data) is the compression ratio. If uncompressed, this is 1.
+	// i.dataBH.Length/len(i.data.data) is the compression ratio. If uncompressed, this is 1.
 	// i.data.nextOffset is the uncompressed position of the current record in the block.
-	// i.dataBH.offset is the offset of the block in the sstable before decompression.
-	recordOffset := (uint64(i.data.nextOffset) * i.dataBH.length) / uint64(len(i.data.data))
-	curOffset := i.dataBH.offset + recordOffset
+	// i.dataBH.Offset is the offset of the block in the sstable before decompression.
+	recordOffset := (uint64(i.data.nextOffset) * i.dataBH.Length) / uint64(len(i.data.data))
+	curOffset := i.dataBH.Offset + recordOffset
 	// Last entry in the block must increment bytes iterated by the size of the block trailer
 	// and restart points.
 	if i.data.nextOffset+(4*(i.data.numRestarts+1)) == int32(len(i.data.data)) {
-		curOffset = i.dataBH.offset + i.dataBH.length + blockTrailerLen
+		curOffset = i.dataBH.Offset + i.dataBH.Length + blockTrailerLen
 	}
 	*i.bytesIterated += uint64(curOffset - i.prevOffset)
 	i.prevOffset = curOffset
@@ -849,20 +852,20 @@ func (i *twoLevelCompactionIterator) First() (*InternalKey, []byte) {
 		// An empty sstable will still encode the block trailer and restart points, so bytes
 		// iterated must be incremented.
 
-		// We must use i.dataBH.length instead of (4*(i.data.numRestarts+1)) to calculate the
-		// number of bytes for the restart points, since i.dataBH.length accounts for
-		// compression. When uncompressed, i.dataBH.length == (4*(i.data.numRestarts+1))
-		*i.bytesIterated += blockTrailerLen + i.dataBH.length
+		// We must use i.dataBH.Length instead of (4*(i.data.numRestarts+1)) to calculate the
+		// number of bytes for the restart points, since i.dataBH.Length accounts for
+		// compression. When uncompressed, i.dataBH.Length == (4*(i.data.numRestarts+1))
+		*i.bytesIterated += blockTrailerLen + i.dataBH.Length
 		return nil, nil
 	}
 	// If the sstable only has 1 entry, we are at the last entry in the block and we must
 	// increment bytes iterated by the size of the block trailer and restart points.
 	if i.data.nextOffset+(4*(i.data.numRestarts+1)) == int32(len(i.data.data)) {
-		i.prevOffset = blockTrailerLen + i.dataBH.length
+		i.prevOffset = blockTrailerLen + i.dataBH.Length
 	} else {
-		// i.dataBH.length/len(i.data.data) is the compression ratio. If uncompressed, this is 1.
+		// i.dataBH.Length/len(i.data.data) is the compression ratio. If uncompressed, this is 1.
 		// i.data.nextOffset is the uncompressed size of the first record.
-		i.prevOffset = (uint64(i.data.nextOffset) * i.dataBH.length) / uint64(len(i.data.data))
+		i.prevOffset = (uint64(i.data.nextOffset) * i.dataBH.Length) / uint64(len(i.data.data))
 	}
 	*i.bytesIterated += i.prevOffset
 	return key, val
@@ -898,15 +901,15 @@ func (i *twoLevelCompactionIterator) Next() (*InternalKey, []byte) {
 		}
 	}
 
-	// i.dataBH.length/len(i.data.data) is the compression ratio. If uncompressed, this is 1.
+	// i.dataBH.Length/len(i.data.data) is the compression ratio. If uncompressed, this is 1.
 	// i.data.nextOffset is the uncompressed position of the current record in the block.
-	// i.dataBH.offset is the offset of the block in the sstable before decompression.
-	recordOffset := (uint64(i.data.nextOffset) * i.dataBH.length) / uint64(len(i.data.data))
-	curOffset := i.dataBH.offset + recordOffset
+	// i.dataBH.Offset is the offset of the block in the sstable before decompression.
+	recordOffset := (uint64(i.data.nextOffset) * i.dataBH.Length) / uint64(len(i.data.data))
+	curOffset := i.dataBH.Offset + recordOffset
 	// Last entry in the block must increment bytes iterated by the size of the block trailer
 	// and restart points.
 	if i.data.nextOffset+(4*(i.data.numRestarts+1)) == int32(len(i.data.data)) {
-		curOffset = i.dataBH.offset + i.dataBH.length + blockTrailerLen
+		curOffset = i.dataBH.Offset + i.dataBH.Length + blockTrailerLen
 	}
 	*i.bytesIterated += uint64(curOffset - i.prevOffset)
 	i.prevOffset = curOffset
@@ -918,7 +921,7 @@ func (i *twoLevelCompactionIterator) Prev() (*InternalKey, []byte) {
 }
 
 type weakCachedBlock struct {
-	bh     blockHandle
+	bh     BlockHandle
 	mu     sync.RWMutex
 	handle cache.WeakHandle
 }
@@ -935,6 +938,9 @@ type Reader struct {
 	filter            weakCachedBlock
 	rangeDel          weakCachedBlock
 	rangeDelTransform blockTransform
+	propertiesBH      BlockHandle
+	metaIndexBH       BlockHandle
+	footerBH          BlockHandle
 	opts              *Options
 	cache             *cache.Cache
 	compare           Compare
@@ -1039,7 +1045,7 @@ func (r *Reader) NewCompactionIter(bytesIterated *uint64) Iterator {
 // range-del block for the table. Returns nil if the table does not contain any
 // range deletions.
 func (r *Reader) NewRangeDelIter() *blockIter {
-	if r.rangeDel.bh.length == 0 {
+	if r.rangeDel.bh.Length == 0 {
 		return nil
 	}
 	b, err := r.readRangeDel()
@@ -1098,25 +1104,25 @@ func (r *Reader) readWeakCachedBlock(
 
 // readBlock reads and decompresses a block from disk into memory.
 func (r *Reader) readBlock(
-	bh blockHandle, transform blockTransform,
+	bh BlockHandle, transform blockTransform,
 ) (cache.Handle, error) {
-	if h := r.cache.Get(r.dbNum, r.fileNum, bh.offset); h.Get() != nil {
+	if h := r.cache.Get(r.dbNum, r.fileNum, bh.Offset); h.Get() != nil {
 		return h, nil
 	}
 
-	b := r.cache.Alloc(int(bh.length + blockTrailerLen))
-	if _, err := r.file.ReadAt(b, int64(bh.offset)); err != nil {
+	b := r.cache.Alloc(int(bh.Length + blockTrailerLen))
+	if _, err := r.file.ReadAt(b, int64(bh.Offset)); err != nil {
 		return cache.Handle{}, err
 	}
 
-	checksum0 := binary.LittleEndian.Uint32(b[bh.length+1:])
-	checksum1 := crc.New(b[:bh.length+1]).Value()
+	checksum0 := binary.LittleEndian.Uint32(b[bh.Length+1:])
+	checksum1 := crc.New(b[:bh.Length+1]).Value()
 	if checksum0 != checksum1 {
 		return cache.Handle{}, errors.New("pebble/table: invalid table (checksum mismatch)")
 	}
 
-	typ := b[bh.length]
-	b = b[:bh.length]
+	typ := b[bh.Length]
+	b = b[:bh.Length]
 
 	switch typ {
 	case noCompressionBlockType:
@@ -1146,7 +1152,7 @@ func (r *Reader) readBlock(
 		}
 	}
 
-	h := r.cache.Set(r.dbNum, r.fileNum, bh.offset, b)
+	h := r.cache.Set(r.dbNum, r.fileNum, bh.Offset, b)
 	return h, nil
 }
 
@@ -1192,7 +1198,7 @@ func (r *Reader) transformRangeDelV1(b []byte) ([]byte, error) {
 	return rangeDelBlock.finish(), nil
 }
 
-func (r *Reader) readMetaindex(metaindexBH blockHandle, o *Options) error {
+func (r *Reader) readMetaindex(metaindexBH BlockHandle, o *Options) error {
 	b, err := r.readBlock(metaindexBH, nil /* transform */)
 	if err != nil {
 		return err
@@ -1203,7 +1209,7 @@ func (r *Reader) readMetaindex(metaindexBH blockHandle, o *Options) error {
 		return err
 	}
 
-	meta := map[string]blockHandle{}
+	meta := map[string]BlockHandle{}
 	for valid := i.First(); valid; valid = i.Next() {
 		bh, n := decodeBlockHandle(i.Value())
 		if n == 0 {
@@ -1221,7 +1227,8 @@ func (r *Reader) readMetaindex(metaindexBH blockHandle, o *Options) error {
 			return err
 		}
 		data := b.Get()
-		err := r.Properties.load(data, bh.offset)
+		r.propertiesBH = bh
+		err := r.Properties.load(data, bh.Offset)
 		b.Release()
 		if err != nil {
 			return err
@@ -1235,11 +1242,7 @@ func (r *Reader) readMetaindex(metaindexBH blockHandle, o *Options) error {
 		r.rangeDelTransform = r.transformRangeDelV1
 	}
 
-	for level := range r.opts.Levels {
-		fp := r.opts.Levels[level].FilterPolicy
-		if fp == nil {
-			continue
-		}
+	for name, fp := range r.opts.Filters {
 		types := []struct {
 			ftype  FilterType
 			prefix string
@@ -1248,7 +1251,7 @@ func (r *Reader) readMetaindex(metaindexBH blockHandle, o *Options) error {
 		}
 		var done bool
 		for _, t := range types {
-			if bh, ok := meta[t.prefix+fp.Name()]; ok {
+			if bh, ok := meta[t.prefix+name]; ok {
 				r.filter.bh = bh
 
 				switch t.ftype {
@@ -1269,10 +1272,70 @@ func (r *Reader) readMetaindex(metaindexBH blockHandle, o *Options) error {
 	return nil
 }
 
+// Layout returns the layout (block organization) for an sstable.
+func (r *Reader) Layout() (*Layout, error) {
+	if r.err != nil {
+		return nil, r.err
+	}
+
+	l := &Layout{
+		Data:       make([]BlockHandle, 0, r.Properties.NumDataBlocks),
+		Filter:     r.filter.bh,
+		RangeDel:   r.rangeDel.bh,
+		Properties: r.propertiesBH,
+		MetaIndex:  r.metaIndexBH,
+		Footer:     r.footerBH,
+	}
+
+	index, err := r.readIndex()
+	if err != nil {
+		return nil, err
+	}
+
+	if r.Properties.IndexPartitions == 0 {
+		l.Index = append(l.Index, r.index.bh)
+		iter, _ := newBlockIter(r.compare, index)
+		for key, value := iter.First(); key != nil; key, value = iter.Next() {
+			dataBH, n := decodeBlockHandle(value)
+			if n == 0 || n != len(value) {
+				return nil, errors.New("pebble/table: corrupt index entry")
+			}
+			l.Data = append(l.Data, dataBH)
+		}
+	} else {
+		l.TopIndex = r.index.bh
+		topIter, _ := newBlockIter(r.compare, index)
+		for key, value := topIter.First(); key != nil; key, value = topIter.Next() {
+			indexBH, n := decodeBlockHandle(value)
+			if n == 0 || n != len(value) {
+				return nil, errors.New("pebble/table: corrupt index entry")
+			}
+			l.Index = append(l.Index, indexBH)
+
+			subIndex, err := r.readBlock(indexBH, nil /* transform */)
+			if err != nil {
+				return nil, err
+			}
+			iter, _ := newBlockIter(r.compare, subIndex.Get())
+			for key, value := iter.First(); key != nil; key, value = iter.Next() {
+				dataBH, n := decodeBlockHandle(value)
+				if n == 0 || n != len(value) {
+					return nil, errors.New("pebble/table: corrupt index entry")
+				}
+				l.Data = append(l.Data, dataBH)
+			}
+			subIndex.Release()
+		}
+	}
+
+	return l, nil
+}
+
 // NewReader returns a new table reader for the file. Closing the reader will
 // close the file.
-func NewReader(f vfs.File, dbNum, fileNum uint64, o *Options) *Reader {
+func NewReader(f vfs.File, dbNum, fileNum uint64, o *Options) (*Reader, error) {
 	o = o.EnsureDefaults()
+
 	r := &Reader{
 		file:    f,
 		dbNum:   dbNum,
@@ -1284,24 +1347,201 @@ func NewReader(f vfs.File, dbNum, fileNum uint64, o *Options) *Reader {
 	}
 	if f == nil {
 		r.err = errors.New("pebble/table: nil file")
-		return r
+		return r, r.err
 	}
 	footer, err := readFooter(f)
 	if err != nil {
 		r.err = err
-		return r
+		return r, r.err
 	}
 	// Read the metaindex.
 	if err := r.readMetaindex(footer.metaindexBH, o); err != nil {
 		r.err = err
-		return r
+		return r, r.err
 	}
 	r.index.bh = footer.indexBH
+	r.metaIndexBH = footer.metaindexBH
+	r.footerBH = footer.footerBH
+
+	if r.Properties.ComparerName == "" {
+		r.compare = o.Comparer.Compare
+		r.split = o.Comparer.Split
+	} else if comparer, ok := o.Comparers[r.Properties.ComparerName]; ok {
+		r.compare = comparer.Compare
+		r.split = comparer.Split
+	} else {
+		r.err = fmt.Errorf("pebble/table: %d: unknown comparer %s",
+			fileNum, r.Properties.ComparerName)
+	}
+
+	if name := r.Properties.MergerName; name != "" && name != "nullptr" {
+		if _, ok := o.Mergers[r.Properties.MergerName]; !ok {
+			r.err = fmt.Errorf("pebble/table: %d: unknown merger %s",
+				fileNum, r.Properties.MergerName)
+		}
+	}
+	return r, r.err
+}
+
+// Layout describes the block organization of an sstable.
+type Layout struct {
+	Data       []BlockHandle
+	Index      []BlockHandle
+	TopIndex   BlockHandle
+	Filter     BlockHandle
+	RangeDel   BlockHandle
+	Properties BlockHandle
+	MetaIndex  BlockHandle
+	Footer     BlockHandle
+}
+
+// Describe returns a description of the layout. If the verbose parameter is
+// true, details of the structure of each block are returned as well.
+func (l *Layout) Describe(w io.Writer, verbose bool, r *Reader) {
+	type block struct {
+		BlockHandle
+		name string
+	}
+	var blocks []block
+
+	for i := range l.Data {
+		blocks = append(blocks, block{l.Data[i], "data"})
+	}
+	for i := range l.Index {
+		blocks = append(blocks, block{l.Index[i], "index"})
+	}
+	if l.TopIndex.Length != 0 {
+		blocks = append(blocks, block{l.TopIndex, "top-index"})
+	}
+	if l.Filter.Length != 0 {
+		blocks = append(blocks, block{l.Filter, "filter"})
+	}
+	if l.RangeDel.Length != 0 {
+		blocks = append(blocks, block{l.RangeDel, "range-del"})
+	}
+	if l.Properties.Length != 0 {
+		blocks = append(blocks, block{l.Properties, "properties"})
+	}
+	if l.MetaIndex.Length != 0 {
+		blocks = append(blocks, block{l.MetaIndex, "meta-index"})
+	}
+	if l.Footer.Length != 0 {
+		if l.Footer.Length == levelDBFooterLen {
+			blocks = append(blocks, block{l.Footer, "leveldb-footer"})
+		} else {
+			blocks = append(blocks, block{l.Footer, "footer"})
+		}
+	}
+
+	sort.Slice(blocks, func(i, j int) bool {
+		return blocks[i].Offset < blocks[j].Offset
+	})
+
+	for i := range blocks {
+		b := &blocks[i]
+		fmt.Fprintf(w, "%10d  %s (%d)\n", b.Offset, b.name, b.Length)
+
+		if !verbose {
+			continue
+		}
+		if b.name == "footer" || b.name == "leveldb-footer" || b.name == "filter" {
+			continue
+		}
+
+		h, err := r.readBlock(b.BlockHandle, nil /* transform */)
+		if err != nil {
+			fmt.Fprintf(w, "  [err: %s]\n", err)
+			continue
+		}
+
+		getRestart := func(data []byte, restarts, i int32) int32 {
+			return int32(binary.LittleEndian.Uint32(data[restarts+4*i:]))
+		}
+
+		formatIsRestart := func(data []byte, restarts, numRestarts, offset int32) {
+			i := sort.Search(int(numRestarts), func(i int) bool {
+				return getRestart(data, restarts, int32(i)) >= offset
+			})
+			if i < int(numRestarts) && getRestart(data, restarts, int32(i)) == offset {
+				fmt.Fprintf(w, " [restart]\n")
+			} else {
+				fmt.Fprintf(w, "\n")
+			}
+		}
 
-	// index, r.err = r.readIndex()
-	// iter, _ := newBlockIter(r.compare, index)
-	// for valid := iter.First(); valid; valid = iter.Next() {
-	// 	fmt.Printf("%s#%d\n", iter.Key().UserKey, iter.Key().SeqNum())
-	// }
-	return r
+		formatRestarts := func(data []byte, restarts, numRestarts int32) {
+			for i := int32(0); i < numRestarts; i++ {
+				offset := getRestart(data, restarts, i)
+				fmt.Fprintf(w, "%10d    [restart %d]\n",
+					b.Offset+uint64(restarts+4*i), b.Offset+uint64(offset))
+			}
+		}
+
+		switch b.name {
+		case "data":
+			iter, _ := newBlockIter(r.compare, h.Get())
+			for key, _ := iter.First(); key != nil; key, _ = iter.Next() {
+				ptr := unsafe.Pointer(uintptr(iter.ptr) + uintptr(iter.offset))
+				shared, ptr := decodeVarint(ptr)
+				unshared, ptr := decodeVarint(ptr)
+				value, _ := decodeVarint(ptr)
+
+				total := iter.nextOffset - iter.offset
+				// The format of the numbers in the record line is:
+				//
+				//   (<total> = <length> [<shared>] + <unshared> + <value>)
+				//
+				// <total>    is the total number of bytes for the record.
+				// <length>   is the size of the 3 varint encoded integers for <shared>,
+				//            <unshared>, and <value>.
+				// <shared>   is the number of key bytes shared with the previous key.
+				// <unshared> is the number of unshared key bytes.
+				// <value>    is the number of value bytes.
+				fmt.Fprintf(w, "%10d    record (%d = %d [%d] + %d + %d)",
+					b.Offset+uint64(iter.offset), total,
+					total-int32(unshared+value), shared, unshared, value)
+				formatIsRestart(iter.data, iter.restarts, iter.numRestarts, iter.offset)
+			}
+			formatRestarts(iter.data, iter.restarts, iter.numRestarts)
+		case "index", "top-index":
+			iter, _ := newBlockIter(r.compare, h.Get())
+			for key, value := iter.First(); key != nil; key, value = iter.Next() {
+				bh, n := decodeBlockHandle(value)
+				if n == 0 || n != len(value) {
+					fmt.Fprintf(w, "%10d    [err: %s]\n", b.Offset+uint64(iter.offset), err)
+					continue
+				}
+				fmt.Fprintf(w, "%10d    block:%d/%d",
+					b.Offset+uint64(iter.offset), bh.Offset, bh.Length)
+				formatIsRestart(iter.data, iter.restarts, iter.numRestarts, iter.offset)
+			}
+			formatRestarts(iter.data, iter.restarts, iter.numRestarts)
+		case "properties":
+			iter, _ := newRawBlockIter(r.compare, h.Get())
+			for valid := iter.First(); valid; valid = iter.Next() {
+				fmt.Fprintf(w, "%10d    %s (%d)",
+					b.Offset+uint64(iter.offset), iter.Key().UserKey, iter.nextOffset-iter.offset)
+				formatIsRestart(iter.data, iter.restarts, iter.numRestarts, iter.offset)
+			}
+			formatRestarts(iter.data, iter.restarts, iter.numRestarts)
+		case "meta-index":
+			iter, _ := newRawBlockIter(r.compare, h.Get())
+			for valid := iter.First(); valid; valid = iter.Next() {
+				value := iter.Value()
+				bh, n := decodeBlockHandle(value)
+				if n == 0 || n != len(value) {
+					fmt.Fprintf(w, "%10d    [err: %s]\n", b.Offset+uint64(iter.offset), err)
+					continue
+				}
+
+				fmt.Fprintf(w, "%10d    %s block:%d/%d",
+					b.Offset+uint64(iter.offset), iter.Key().UserKey,
+					bh.Offset, bh.Length)
+				formatIsRestart(iter.data, iter.restarts, iter.numRestarts, iter.offset)
+			}
+			formatRestarts(iter.data, iter.restarts, iter.numRestarts)
+		}
+
+		h.Release()
+	}
 }
diff --git a/sstable/table.go b/sstable/table.go
index 60f2511..6f3f68b 100644
--- a/sstable/table.go
+++ b/sstable/table.go
@@ -171,8 +171,7 @@ const (
 	// doesn't use zstd compression, the string will always be the same.
 	// This should be removed if we ever decide to diverge from the RocksDB
 	// properties block.
-	rocksDBCompressionOptions =
-		"window_bits=-14; level=32767; strategy=0; max_dict_bytes=0; zstd_max_train_bytes=0; enabled=0; "
+	rocksDBCompressionOptions = "window_bits=-14; level=32767; strategy=0; max_dict_bytes=0; zstd_max_train_bytes=0; enabled=0; "
 )
 
 // legacy (LevelDB) footer format:
@@ -190,8 +189,9 @@ const (
 type footer struct {
 	format      TableFormat
 	checksum    uint8
-	metaindexBH blockHandle
-	indexBH     blockHandle
+	metaindexBH BlockHandle
+	indexBH     BlockHandle
+	footerBH    BlockHandle
 }
 
 func readFooter(f vfs.File) (footer, error) {
@@ -220,7 +220,9 @@ func readFooter(f vfs.File) (footer, error) {
 		if len(buf) < levelDBFooterLen {
 			return footer, fmt.Errorf("pebble/table: invalid table (footer too short): %d", len(buf))
 		}
+		footer.footerBH.Offset = uint64(off+int64(len(buf))) - levelDBFooterLen
 		buf = buf[len(buf)-levelDBFooterLen:]
+		footer.footerBH.Length = uint64(len(buf))
 		footer.format = TableFormatLevelDB
 		footer.checksum = checksumCRC32c
 
@@ -228,7 +230,9 @@ func readFooter(f vfs.File) (footer, error) {
 		if len(buf) < rocksDBFooterLen {
 			return footer, fmt.Errorf("pebble/table: invalid table (footer too short): %d", len(buf))
 		}
+		footer.footerBH.Offset = uint64(off+int64(len(buf))) - rocksDBFooterLen
 		buf = buf[len(buf)-rocksDBFooterLen:]
+		footer.footerBH.Length = uint64(len(buf))
 		version := binary.LittleEndian.Uint32(buf[rocksDBVersionOffset:rocksDBMagicOffset])
 		if version != rocksDBFormatVersion2 {
 			return footer, fmt.Errorf("pebble/table: unsupported format version %d", version)
diff --git a/sstable/writer.go b/sstable/writer.go
index 3d1c963..a1f19d2 100644
--- a/sstable/writer.go
+++ b/sstable/writer.go
@@ -126,7 +126,7 @@ type Writer struct {
 	// pendingBH is the blockHandle of a finished block that is waiting for
 	// the next call to Set. If the writer is not in this state, pendingBH
 	// is zero.
-	pendingBH      blockHandle
+	pendingBH      BlockHandle
 	block          blockWriter
 	indexBlock     blockWriter
 	rangeDelBlock  blockWriter
@@ -337,7 +337,7 @@ func (w *Writer) maybeFlush(key InternalKey, value []byte) error {
 
 // flushPendingBH adds any pending block handle to the index entries.
 func (w *Writer) flushPendingBH(key InternalKey) {
-	if w.pendingBH.length == 0 {
+	if w.pendingBH.Length == 0 {
 		// A valid blockHandle must be non-zero.
 		// In particular, it must have a non-zero length.
 		return
@@ -359,7 +359,7 @@ func (w *Writer) flushPendingBH(key InternalKey) {
 
 	w.indexBlock.add(sep, w.tmp[:n])
 
-	w.pendingBH = blockHandle{}
+	w.pendingBH = BlockHandle{}
 }
 
 func shouldFlush(key InternalKey, value []byte, block blockWriter, blockSize, sizeThreshold int) bool {
@@ -388,7 +388,7 @@ func shouldFlush(key InternalKey, value []byte, block blockWriter, blockSize, si
 
 // finishBlock finishes the current block and returns its block handle, which is
 // its offset and length in the table.
-func (w *Writer) finishBlock(block *blockWriter) (blockHandle, error) {
+func (w *Writer) finishBlock(block *blockWriter) (BlockHandle, error) {
 	bh, err := w.writeRawBlock(block.finish(), w.compression)
 
 	// Calculate filters.
@@ -410,7 +410,7 @@ func (w *Writer) finishIndexBlock() {
 	}
 }
 
-func (w *Writer) writeTwoLevelIndex() (blockHandle, error) {
+func (w *Writer) writeTwoLevelIndex() (BlockHandle, error) {
 	// Add the final unfinished index.
 	w.finishIndexBlock()
 
@@ -439,7 +439,7 @@ func (w *Writer) writeTwoLevelIndex() (blockHandle, error) {
 	return w.finishBlock(&w.topLevelIndexBlock)
 }
 
-func (w *Writer) writeRawBlock(b []byte, compression Compression) (blockHandle, error) {
+func (w *Writer) writeRawBlock(b []byte, compression Compression) (BlockHandle, error) {
 	blockType := noCompressionBlockType
 	if compression == SnappyCompression {
 		// Compress the buffer, discarding the result if the improvement isn't at
@@ -456,17 +456,17 @@ func (w *Writer) writeRawBlock(b []byte, compression Compression) (blockHandle,
 	// Calculate the checksum.
 	checksum := crc.New(b).Update(w.tmp[:1]).Value()
 	binary.LittleEndian.PutUint32(w.tmp[1:5], checksum)
-	bh := blockHandle{w.meta.Size, uint64(len(b))}
+	bh := BlockHandle{w.meta.Size, uint64(len(b))}
 
 	// Write the bytes to the file.
 	n, err := w.writer.Write(b)
 	if err != nil {
-		return blockHandle{}, err
+		return BlockHandle{}, err
 	}
 	w.meta.Size += uint64(n)
 	n, err = w.writer.Write(w.tmp[:blockTrailerLen])
 	if err != nil {
-		return blockHandle{}, err
+		return BlockHandle{}, err
 	}
 	w.meta.Size += uint64(n)
 
@@ -521,10 +521,10 @@ func (w *Writer) Close() (err error) {
 		n := encodeBlockHandle(w.tmp[:], bh)
 		metaindex.add(InternalKey{UserKey: []byte(w.filter.metaName())}, w.tmp[:n])
 		w.props.FilterPolicyName = w.filter.policyName()
-		w.props.FilterSize = bh.length
+		w.props.FilterSize = bh.Length
 	}
 
-	var indexBH blockHandle
+	var indexBH BlockHandle
 	if w.twoLevelIndex {
 		w.props.IndexType = twoLevelIndex
 		// Write the two level index block.
@@ -716,9 +716,9 @@ func NewWriter(f writeCloseSyncer, o *Options, lo TableOptions) *Writer {
 	}
 
 	w.props.ColumnFamilyID = math.MaxInt32
-	w.props.ComparatorName = o.Comparer.Name
+	w.props.ComparerName = o.Comparer.Name
 	w.props.CompressionName = lo.Compression.String()
-	w.props.MergeOperatorName = o.Merger.Name
+	w.props.MergerName = o.Merger.Name
 	w.props.PropertyCollectorNames = "[]"
 	w.props.Version = 2 // TODO(peter): what is this?
 
diff --git a/table_cache.go b/table_cache.go
index cefa10b..65f1e9f 100644
--- a/table_cache.go
+++ b/table_cache.go
@@ -333,11 +333,10 @@ func (n *tableCacheNode) load(c *tableCacheShard) {
 		close(n.loaded)
 		return
 	}
-	r := sstable.NewReader(f, c.dbNum, n.meta.fileNum, c.opts)
+	n.reader, n.err = sstable.NewReader(f, c.dbNum, n.meta.fileNum, c.opts)
 	if n.meta.smallestSeqNum == n.meta.largestSeqNum {
-		r.Properties.GlobalSeqNum = n.meta.largestSeqNum
+		n.reader.Properties.GlobalSeqNum = n.meta.largestSeqNum
 	}
-	n.reader = r
 	close(n.loaded)
 }
 
diff --git a/tool/db.go b/tool/db.go
new file mode 100644
index 0000000..95df197
--- /dev/null
+++ b/tool/db.go
@@ -0,0 +1,84 @@
+// Copyright 2019 The LevelDB-Go and Pebble Authors. All rights reserved. Use
+// of this source code is governed by a BSD-style license that can be found in
+// the LICENSE file.
+
+package tool
+
+import (
+	"fmt"
+
+	"github.com/spf13/cobra"
+)
+
+// AllCmds is a list of all of the tool commands.
+var AllCmds = []*cobra.Command{
+	DBCmd,
+	ManifestCmd,
+	SSTableCmd,
+	WALCmd,
+}
+
+// DBCmd is the root of the DB commands.
+var DBCmd = &cobra.Command{
+	Use:   "db",
+	Short: "DB introspection tools",
+}
+
+// DBCheckCmd implements db check.
+var DBCheckCmd = &cobra.Command{
+	Use:   "check <dir>",
+	Short: "verify checksums and metadata",
+	Long: `
+Verify sstable, manifest, and WAL checksums. Requires that the specified
+database not be in use by another process.
+`,
+	Args: cobra.ExactArgs(1),
+	Run:  runDBCheck,
+}
+
+// DBLSMCmd implements db lsm.
+var DBLSMCmd = &cobra.Command{
+	Use:   "lsm <dir>",
+	Short: "print LSM structure",
+	Long: `
+Print the structure of the LSM tree. Requires that the specified database not
+be in use by another process.
+`,
+	Args: cobra.ExactArgs(1),
+	Run:  runDBLSM,
+}
+
+// DBScanCmd implements db scan.
+var DBScanCmd = &cobra.Command{
+	Use:   "scan <dir>",
+	Short: "print db records",
+	Long: `
+Print the records in the DB. Requires that the specified database not be in use
+by another process.
+`,
+	Args: cobra.ExactArgs(1),
+	Run:  runDBScan,
+}
+
+func init() {
+	DBCmd.AddCommand(
+		DBCheckCmd,
+		DBLSMCmd,
+		DBScanCmd,
+	)
+}
+
+func runDBCheck(cmd *cobra.Command, args []string) {
+	fmt.Fprintf(stderr, "TODO(peter): \"db check\" unimplemented\n")
+	osExit(1)
+}
+
+func runDBLSM(cmd *cobra.Command, args []string) {
+	fmt.Fprintf(stderr, "TODO(peter): \"db lsm\" unimplemented\n")
+	osExit(1)
+}
+
+func runDBScan(cmd *cobra.Command, args []string) {
+	fmt.Fprintf(stderr, "TODO(peter): \"db scan\" unimplemented\n")
+	osExit(1)
+}
diff --git a/tool/manifest.go b/tool/manifest.go
new file mode 100644
index 0000000..473999a
--- /dev/null
+++ b/tool/manifest.go
@@ -0,0 +1,37 @@
+// Copyright 2019 The LevelDB-Go and Pebble Authors. All rights reserved. Use
+// of this source code is governed by a BSD-style license that can be found in
+// the LICENSE file.
+
+package tool
+
+import (
+	"fmt"
+
+	"github.com/spf13/cobra"
+)
+
+// ManifestCmd is the root of the manifest commands.
+var ManifestCmd = &cobra.Command{
+	Use:   "manifest",
+	Short: "manifest introspection tools",
+}
+
+// ManifestDumpCmd implements manifest dump.
+var ManifestDumpCmd = &cobra.Command{
+	Use:   "dump <manifest-files>",
+	Short: "print manifest contents",
+	Long: `
+Print the contents of the MANIFEST files.
+`,
+	Args: cobra.MinimumNArgs(1),
+	Run:  runManifestDump,
+}
+
+func init() {
+	ManifestCmd.AddCommand(ManifestDumpCmd)
+}
+
+func runManifestDump(cmd *cobra.Command, args []string) {
+	fmt.Fprintf(stderr, "TODO(peter): \"manifest dump\" unimplemented\n")
+	osExit(1)
+}
diff --git a/tool/sstable.go b/tool/sstable.go
new file mode 100644
index 0000000..d97d2a3
--- /dev/null
+++ b/tool/sstable.go
@@ -0,0 +1,295 @@
+// Copyright 2019 The LevelDB-Go and Pebble Authors. All rights reserved. Use
+// of this source code is governed by a BSD-style license that can be found in
+// the LICENSE file.
+
+package tool
+
+import (
+	"fmt"
+	"sort"
+	"text/tabwriter"
+
+	"github.com/petermattis/pebble/bloom"
+	"github.com/petermattis/pebble/internal/base"
+	"github.com/petermattis/pebble/sstable"
+	"github.com/petermattis/pebble/vfs"
+	"github.com/spf13/cobra"
+)
+
+var sstableConfig struct {
+	// TODO(peter): Make the options configurable. In particular, we want to be
+	// able to register comparers, and mergers. We also want to be able to
+	// register key/value pretty printers.
+	opts    *sstable.Options
+	start   string
+	end     string
+	verbose bool
+}
+
+// SSTableCmd is the root of the sstable commands.
+var SSTableCmd = &cobra.Command{
+	Use:   "sstable",
+	Short: "sstable introspection tools",
+}
+
+// SSTableCheckCmd implements sstable check.
+var SSTableCheckCmd = &cobra.Command{
+	Use:   "check <sstables>",
+	Short: "verify checksums and metadata",
+	Long:  ``,
+	Args:  cobra.MinimumNArgs(1),
+	Run:   runSSTableCheck,
+}
+
+// SSTableLayoutCmd implements sstable layout.
+var SSTableLayoutCmd = &cobra.Command{
+	Use:   "layout <sstables>",
+	Short: "print sstable block and record layout",
+	Long: `
+Print the layout for the sstables. The -v flag controls whether record layout
+is displayed or omitted.
+`,
+	Args: cobra.MinimumNArgs(1),
+	Run:  runSSTableLayout,
+}
+
+// SSTablePropertiesCmd implements sstable properties.
+var SSTablePropertiesCmd = &cobra.Command{
+	Use:   "properties <sstables>",
+	Short: "print sstable properties",
+	Long: `
+Print the properties for the sstables. The -v flag controls whether the
+properties are pretty-printed or displayed in a verbose/raw format.
+`,
+	Args: cobra.MinimumNArgs(1),
+	Run:  runSSTableProperties,
+}
+
+// SSTableScanCmd implements sstable scan.
+var SSTableScanCmd = &cobra.Command{
+	Use:   "scan <sstables>",
+	Short: "print sstable records",
+	Long: `
+Print the records in the sstables. The sstables are scanned in command line
+order which means the records will be printed in that order.
+`,
+	Args: cobra.MinimumNArgs(1),
+	Run:  runSSTableScan,
+}
+
+func init() {
+	SSTableCmd.AddCommand(
+		SSTableCheckCmd,
+		SSTableLayoutCmd,
+		SSTablePropertiesCmd,
+		SSTableScanCmd,
+	)
+	for _, cmd := range []*cobra.Command{SSTableLayoutCmd, SSTablePropertiesCmd} {
+		cmd.Flags().BoolVarP(
+			&sstableConfig.verbose, "verbose", "v", false,
+			"verbose output")
+	}
+
+	SSTableScanCmd.Flags().StringVar(
+		&sstableConfig.start, "start", "", "start key for the scan")
+	SSTableScanCmd.Flags().StringVar(
+		&sstableConfig.end, "end", "", "end key for the scan")
+
+	sstableConfig.opts = &sstable.Options{
+		Levels: []sstable.TableOptions{{
+			FilterPolicy: bloom.FilterPolicy(10),
+			FilterType:   base.TableFilter,
+		}},
+	}
+}
+
+func runSSTableCheck(cmd *cobra.Command, args []string) {
+	for _, arg := range args {
+		func() {
+			f, err := vfs.Default.Open(arg)
+			if err != nil {
+				fmt.Fprintf(stderr, "%s\n", err)
+				return
+			}
+
+			fmt.Fprintf(stdout, "%s\n", arg)
+
+			r, err := sstable.NewReader(f, nextDBNum(), 0, sstableConfig.opts)
+			defer r.Close()
+
+			if err != nil {
+				fmt.Fprintf(stdout, "%s\n", err)
+				return
+			}
+
+			iter := r.NewIter(nil, nil)
+			for key, _ := iter.First(); key != nil; key, _ = iter.Next() {
+			}
+			if err := iter.Close(); err != nil {
+				fmt.Fprintf(stdout, "%s\n", err)
+			}
+		}()
+	}
+}
+
+func runSSTableLayout(cmd *cobra.Command, args []string) {
+	for _, arg := range args {
+		func() {
+			f, err := vfs.Default.Open(arg)
+			if err != nil {
+				fmt.Fprintf(stderr, "%s\n", err)
+				return
+			}
+
+			fmt.Fprintf(stdout, "%s\n", arg)
+
+			r, err := sstable.NewReader(f, nextDBNum(), 0, sstableConfig.opts)
+			defer r.Close()
+
+			if err != nil {
+				fmt.Fprintf(stdout, "%s\n", err)
+				return
+			}
+
+			l, err := r.Layout()
+			if err != nil {
+				fmt.Fprintf(stderr, "%s\n", err)
+				return
+			}
+			l.Describe(stdout, sstableConfig.verbose, r)
+		}()
+	}
+}
+
+func runSSTableProperties(cmd *cobra.Command, args []string) {
+	for _, arg := range args {
+		func() {
+			f, err := vfs.Default.Open(arg)
+			if err != nil {
+				fmt.Fprintf(stderr, "%s\n", err)
+				return
+			}
+
+			fmt.Fprintf(stdout, "%s\n", arg)
+
+			r, err := sstable.NewReader(f, nextDBNum(), 0, sstableConfig.opts)
+			defer r.Close()
+
+			if err != nil {
+				fmt.Fprintf(stdout, "%s\n", err)
+				return
+			}
+
+			if sstableConfig.verbose {
+				fmt.Fprintf(stdout, "%s", r.Properties.String())
+				return
+			}
+
+			stat, err := f.Stat()
+			if err != nil {
+				fmt.Fprintf(stderr, "%s\n", err)
+				return
+			}
+
+			formatNull := func(s string) string {
+				switch s {
+				case "", "nullptr":
+					return "-"
+				}
+				return s
+			}
+
+			tw := tabwriter.NewWriter(stdout, 2, 1, 2, ' ', 0)
+			fmt.Fprintf(tw, "version\t%d\n", r.Properties.Version)
+			fmt.Fprintf(tw, "size\t\n")
+			fmt.Fprintf(tw, "  file\t%d\n", stat.Size())
+			fmt.Fprintf(tw, "  data\t%d\n", r.Properties.DataSize)
+			fmt.Fprintf(tw, "    blocks\t%d\n", r.Properties.NumDataBlocks)
+			fmt.Fprintf(tw, "  index\t%d\n", r.Properties.IndexSize)
+			fmt.Fprintf(tw, "    blocks\t%d\n", 1+r.Properties.IndexPartitions)
+			fmt.Fprintf(tw, "    top-level\t%d\n", r.Properties.TopLevelIndexSize)
+			fmt.Fprintf(tw, "  filter\t%d\n", r.Properties.FilterSize)
+			fmt.Fprintf(tw, "  raw-key\t%d\n", r.Properties.RawKeySize)
+			fmt.Fprintf(tw, "  raw-value\t%d\n", r.Properties.RawValueSize)
+			fmt.Fprintf(tw, "records\t%d\n", r.Properties.NumEntries)
+			fmt.Fprintf(tw, "  set\t%d\n", r.Properties.NumEntries-
+				(r.Properties.NumDeletions+r.Properties.NumRangeDeletions+r.Properties.NumMergeOperands))
+			fmt.Fprintf(tw, "  delete\t%d\n", r.Properties.NumDeletions)
+			fmt.Fprintf(tw, "  range-delete\t%d\n", r.Properties.NumRangeDeletions)
+			fmt.Fprintf(tw, "  merge\t%d\n", r.Properties.NumMergeOperands)
+			fmt.Fprintf(tw, "  global-seq-num\t%d\n", r.Properties.GlobalSeqNum)
+			fmt.Fprintf(tw, "index\t\n")
+			fmt.Fprintf(tw, "  key\t")
+			if r.Properties.IndexKeyIsUserKey != 0 {
+				fmt.Fprintf(tw, "user key\n")
+			} else {
+				fmt.Fprintf(tw, "internal key\n")
+			}
+			fmt.Fprintf(tw, "  value\t")
+			if r.Properties.IndexValueIsDeltaEncoded != 0 {
+				fmt.Fprintf(tw, "delta encoded\n")
+			} else {
+				fmt.Fprintf(tw, "raw encoded\n")
+			}
+			fmt.Fprintf(tw, "comparer\t%s\n", r.Properties.ComparerName)
+			fmt.Fprintf(tw, "merger\t%s\n", formatNull(r.Properties.MergerName))
+			fmt.Fprintf(tw, "filter\t%s\n", formatNull(r.Properties.FilterPolicyName))
+			fmt.Fprintf(tw, "  prefix\t%t\n", r.Properties.PrefixFiltering)
+			fmt.Fprintf(tw, "  whole-key\t%t\n", r.Properties.WholeKeyFiltering)
+			fmt.Fprintf(tw, "compression\t%s\n", r.Properties.CompressionName)
+			fmt.Fprintf(tw, "  options\t%s\n", r.Properties.CompressionOptions)
+			fmt.Fprintf(tw, "user properties\t\n")
+			fmt.Fprintf(tw, "  collectors\t%s\n", r.Properties.PropertyCollectorNames)
+			keys := make([]string, 0, len(r.Properties.UserProperties))
+			for key := range r.Properties.UserProperties {
+				keys = append(keys, key)
+			}
+			sort.Strings(keys)
+			for _, key := range keys {
+				fmt.Fprintf(tw, "  %s\t%s\n", key, r.Properties.UserProperties[key])
+			}
+			tw.Flush()
+		}()
+	}
+}
+
+func runSSTableScan(cmd *cobra.Command, args []string) {
+	for _, arg := range args {
+		func() {
+			f, err := vfs.Default.Open(arg)
+			if err != nil {
+				fmt.Fprintf(stderr, "%s\n", err)
+				return
+			}
+
+			fmt.Fprintf(stdout, "%s\n", arg)
+
+			r, err := sstable.NewReader(f, nextDBNum(), 0, sstableConfig.opts)
+			defer r.Close()
+
+			if err != nil {
+				fmt.Fprintf(stdout, "%s\n", err)
+				return
+			}
+
+			// TODO(peter): Allow the start and end key to be specified in hex.
+			var start, end []byte
+			if sstableConfig.start != "" {
+				start = []byte(sstableConfig.start)
+			}
+			if sstableConfig.end != "" {
+				end = []byte(sstableConfig.end)
+			}
+
+			iter := r.NewIter(nil, end)
+			for key, value := iter.SeekGE(start); key != nil; key, value = iter.Next() {
+				// TODO(peter): Allow a pretty printer to be provided for keys and
+				// values.
+				fmt.Fprintf(stdout, "%s [% x]\n", key, value)
+			}
+			if err := iter.Close(); err != nil {
+				fmt.Fprintf(stdout, "%s\n", err)
+			}
+		}()
+	}
+}
diff --git a/tool/util.go b/tool/util.go
new file mode 100644
index 0000000..5ed1572
--- /dev/null
+++ b/tool/util.go
@@ -0,0 +1,20 @@
+// Copyright 2019 The LevelDB-Go and Pebble Authors. All rights reserved. Use
+// of this source code is governed by a BSD-style license that can be found in
+// the LICENSE file.
+
+package tool
+
+import (
+	"io"
+	"os"
+)
+
+var stdout = io.Writer(os.Stdout)
+var stderr = io.Writer(os.Stderr)
+var osExit = os.Exit
+var dbNum uint64
+
+func nextDBNum() uint64 {
+	dbNum++
+	return dbNum
+}
diff --git a/tool/wal.go b/tool/wal.go
new file mode 100644
index 0000000..306a67b
--- /dev/null
+++ b/tool/wal.go
@@ -0,0 +1,37 @@
+// Copyright 2019 The LevelDB-Go and Pebble Authors. All rights reserved. Use
+// of this source code is governed by a BSD-style license that can be found in
+// the LICENSE file.
+
+package tool
+
+import (
+	"fmt"
+
+	"github.com/spf13/cobra"
+)
+
+// WALCmd is the root of the WAL commands.
+var WALCmd = &cobra.Command{
+	Use:   "wal",
+	Short: "WAL introspection tools",
+}
+
+// WALDumpCmd implements WAL dump.
+var WALDumpCmd = &cobra.Command{
+	Use:   "dump <wal-files>",
+	Short: "print WAL contents",
+	Long: `
+Print the contents of the WAL files.
+`,
+	Args: cobra.MinimumNArgs(1),
+	Run:  runWALDump,
+}
+
+func init() {
+	WALCmd.AddCommand(WALDumpCmd)
+}
+
+func runWALDump(cmd *cobra.Command, args []string) {
+	fmt.Fprintf(stderr, "TODO(peter): \"wal dump\" unimplemented\n")
+	osExit(1)
+}


Test patch:
diff --git a/compaction_test.go b/compaction_test.go
index ed6d8ad..a3eb3e9 100644
--- a/compaction_test.go
+++ b/compaction_test.go
@@ -7,7 +7,6 @@ package pebble
 import (
 	"bytes"
 	"fmt"
-	"math"
 	"regexp"
 	"sort"
 	"strconv"
@@ -433,7 +432,7 @@ func TestPickCompaction(t *testing.T) {
 		vs.picker = &tc.picker
 		vs.picker.vers = &tc.version
 
-		c, got := vs.picker.pickAuto(opts, new(uint64)), ""
+		c, got := vs.picker.pickAuto(opts), ""
 		if c != nil {
 			got0 := fileNums(c.inputs[0])
 			got1 := fileNums(c.inputs[1])
@@ -598,8 +597,6 @@ func TestCompaction(t *testing.T) {
 	if err != nil {
 		t.Fatalf("Open: %v", err)
 	}
-	mockLimiter := mockCountLimiter{burst: int(math.MaxInt32)}
-	d.compactionLimiter = &mockLimiter
 
 	get1 := func(iter internalIterator) (ret string) {
 		b := &bytes.Buffer{}
@@ -627,7 +624,10 @@ func TestCompaction(t *testing.T) {
 					return "", "", fmt.Errorf("Open: %v", err)
 				}
 				defer f.Close()
-				r := sstable.NewReader(f, 0, meta.fileNum, nil)
+				r, err := sstable.NewReader(f, 0, meta.fileNum, nil)
+				if err != nil {
+					return "", "", fmt.Errorf("NewReader: %v", err)
+				}
 				defer r.Close()
 				ss = append(ss, get1(r.NewIter(nil /* lower */, nil /* upper */))+".")
 			}
@@ -697,13 +697,6 @@ func TestCompaction(t *testing.T) {
 	if err := d.Close(); err != nil {
 		t.Fatalf("db Close: %v", err)
 	}
-
-	if !(mockLimiter.allowCount > 0) {
-		t.Errorf("limiter allow: got %d, want >%d", mockLimiter.allowCount, 0)
-	}
-	if mockLimiter.waitCount != 0 {
-		t.Errorf("limiter wait: got %d, want %d", mockLimiter.waitCount, 0)
-	}
 }
 
 func TestManualCompaction(t *testing.T) {
@@ -889,7 +882,7 @@ func TestCompactionOutputLevel(t *testing.T) {
 				var start, base int
 				d.ScanArgs(t, "start", &start)
 				d.ScanArgs(t, "base", &base)
-				c := newCompaction(opts, version, start, base, new(uint64))
+				c := newCompaction(opts, version, start, base)
 				return fmt.Sprintf("output=%d\nmax-output-file-size=%d\n",
 					c.outputLevel, c.maxOutputFileSize)
 
diff --git a/data_test.go b/data_test.go
index eae7a87..9a68a26 100644
--- a/data_test.go
+++ b/data_test.go
@@ -302,9 +302,9 @@ func runDBDefineCmd(td *datadriven.TestData, opts *Options) (*DB, error) {
 		}
 
 		c := newFlush(d.opts, d.mu.versions.currentVersion(),
-			d.mu.versions.picker.baseLevel, []flushable{mem}, &d.bytesFlushed)
+			d.mu.versions.picker.baseLevel, []flushable{mem})
 		c.disableRangeTombstoneElision = true
-		newVE, _, err := d.runCompaction(c, nilPacer)
+		newVE, _, err := d.runCompaction(c)
 		if err != nil {
 			return nil
 		}
diff --git a/level_iter_test.go b/level_iter_test.go
index b3d6e88..cbadb73 100644
--- a/level_iter_test.go
+++ b/level_iter_test.go
@@ -187,7 +187,11 @@ func TestLevelIterBoundaries(t *testing.T) {
 			if err != nil {
 				return err.Error()
 			}
-			readers = append(readers, sstable.NewReader(f1, 0, 0, nil))
+			r, err := sstable.NewReader(f1, 0, 0, nil)
+			if err != nil {
+				return err.Error()
+			}
+			readers = append(readers, r)
 			files = append(files, fileMetadata{
 				fileNum:  fileNum,
 				smallest: meta.Smallest(cmp),
@@ -258,9 +262,12 @@ func buildLevelIterTables(
 		if err != nil {
 			b.Fatal(err)
 		}
-		readers[i] = sstable.NewReader(f, 0, uint64(i), &Options{
+		readers[i], err = sstable.NewReader(f, 0, uint64(i), &Options{
 			Cache: cache,
 		})
+		if err != nil {
+			b.Fatal(err)
+		}
 	}
 
 	meta := make([]fileMetadata, len(readers))
diff --git a/merging_iter_test.go b/merging_iter_test.go
index 2c5bdd1..c520543 100644
--- a/merging_iter_test.go
+++ b/merging_iter_test.go
@@ -180,9 +180,12 @@ func buildMergingIterTables(
 		if err != nil {
 			b.Fatal(err)
 		}
-		readers[i] = sstable.NewReader(f, 0, uint64(i), &Options{
+		readers[i], err = sstable.NewReader(f, 0, uint64(i), &Options{
 			Cache: cache,
 		})
+		if err != nil {
+			b.Fatal(err)
+		}
 	}
 	return readers, keys
 }
diff --git a/pacer_test.go b/pacer_test.go
deleted file mode 100644
index fd2a6f9..0000000
--- a/pacer_test.go
+++ /dev/null
@@ -1,146 +0,0 @@
-// Copyright 2019 The LevelDB-Go and Pebble Authors. All rights reserved. Use
-// of this source code is governed by a BSD-style license that can be found in
-// the LICENSE file.
-
-package pebble
-
-import (
-	"bytes"
-	"context"
-	"fmt"
-	"strconv"
-	"strings"
-	"testing"
-	"time"
-
-	"github.com/petermattis/pebble/internal/datadriven"
-)
-
-type mockCountLimiter struct {
-	waitCount  int
-	allowCount int
-	burst      int
-}
-
-func (m *mockCountLimiter) WaitN(ctx context.Context, n int) error {
-	m.waitCount += n
-	return nil
-}
-
-func (m *mockCountLimiter) AllowN(now time.Time, n int) bool {
-	m.allowCount += n
-	return true
-}
-
-func (m *mockCountLimiter) Burst() int {
-	return m.burst
-}
-
-type mockPrintLimiter struct {
-	buf   bytes.Buffer
-	burst int
-}
-
-func (m *mockPrintLimiter) WaitN(ctx context.Context, n int) error {
-	fmt.Fprintf(&m.buf, "wait: %d\n", n)
-	return nil
-}
-
-func (m *mockPrintLimiter) AllowN(now time.Time, n int) bool {
-	fmt.Fprintf(&m.buf, "allow: %d\n", n)
-	return true
-}
-
-func (m *mockPrintLimiter) Burst() int {
-	return m.burst
-}
-
-func TestCompactionPacerMaybeThrottle(t *testing.T) {
-	datadriven.RunTest(t, "testdata/compaction_pacer_maybe_throttle",
-		func(d *datadriven.TestData) string {
-			switch d.Cmd {
-			case "init":
-				if len(d.CmdArgs) != 1 {
-					return fmt.Sprintf("%s expects 1 argument", d.Cmd)
-				}
-
-				burst := uint64(1)
-				var bytesIterated uint64
-				var currentTotal uint64
-				var slowdownThreshold uint64
-				if len(d.Input) > 0 {
-					for _, data := range strings.Split(d.Input, "\n") {
-						parts := strings.Split(data, ":")
-						if len(parts) != 2 {
-							return fmt.Sprintf("malformed test:\n%s", d.Input)
-						}
-						varKey := parts[0]
-						varValue, err := strconv.ParseUint(strings.TrimSpace(parts[1]), 10, 64)
-						if err != nil {
-							return err.Error()
-						}
-
-						switch varKey {
-						case "burst":
-							burst = varValue
-						case "bytesIterated":
-							bytesIterated = varValue
-						case "currentTotal":
-							currentTotal = varValue
-						case "slowdownThreshold":
-							slowdownThreshold = varValue
-						default:
-							return fmt.Sprintf("unknown command: %s", varKey)
-						}
-					}
-				}
-
-				mockLimiter := mockPrintLimiter{burst: int(burst)}
-				switch d.CmdArgs[0].Key {
-				case "compaction":
-					getInfo := func() compactionPacerInfo {
-						return compactionPacerInfo{
-							slowdownThreshold:   slowdownThreshold,
-							totalCompactionDebt: currentTotal,
-						}
-					}
-					compactionPacer := newCompactionPacer(compactionPacerEnv{
-						limiter:      &mockLimiter,
-						memTableSize: 100,
-						getInfo:      getInfo,
-					})
-
-					err := compactionPacer.maybeThrottle(bytesIterated)
-					if err != nil {
-						return err.Error()
-					}
-
-					return mockLimiter.buf.String()
-				case "flush":
-					getInfo := func() flushPacerInfo {
-						return flushPacerInfo{
-							totalBytes: currentTotal,
-						}
-					}
-					flushPacer := newFlushPacer(flushPacerEnv{
-						limiter:      &mockLimiter,
-						memTableSize: 100,
-						getInfo:      getInfo,
-					})
-					flushPacer.slowdownThreshold = slowdownThreshold
-
-					err := flushPacer.maybeThrottle(bytesIterated)
-					if err != nil {
-						return err.Error()
-					}
-
-					return mockLimiter.buf.String()
-				default:
-					return fmt.Sprintf("unknown command: %s", d.Cmd)
-				}
-
-			default:
-				return fmt.Sprintf("unknown command: %s", d.Cmd)
-			}
-		})
-}
diff --git a/sstable/properties_test.go b/sstable/properties_test.go
index 44c881a..56f6c82 100644
--- a/sstable/properties_test.go
+++ b/sstable/properties_test.go
@@ -21,12 +21,12 @@ import (
 func TestPropertiesLoad(t *testing.T) {
 	expected := Properties{
 		ColumnFamilyID:         math.MaxInt32,
-		ComparatorName:         "leveldb.BytewiseComparator",
+		ComparerName:           "leveldb.BytewiseComparator",
 		CompressionName:        "Snappy",
 		CompressionOptions:     "window_bits=-14; level=32767; strategy=0; max_dict_bytes=0; zstd_max_train_bytes=0; enabled=0; ",
 		DataSize:               13913,
 		IndexSize:              325,
-		MergeOperatorName:      "nullptr",
+		MergerName:             "nullptr",
 		NumDataBlocks:          14,
 		NumEntries:             1710,
 		PrefixExtractorName:    "nullptr",
@@ -78,7 +78,10 @@ func TestPropertiesLoad(t *testing.T) {
 			t.Fatal(err)
 		}
 		defer f.Close()
-		r := NewReader(f, 0, 0, nil)
+		r, err := NewReader(f, 0, 0, nil)
+		if err != nil {
+			t.Fatal(err)
+		}
 
 		if diff := pretty.Diff(expected, r.Properties); diff != nil {
 			t.Fatalf("%s", strings.Join(diff, "\n"))
@@ -90,7 +93,7 @@ func TestPropertiesSave(t *testing.T) {
 	expected := &Properties{
 		ColumnFamilyID:           1,
 		ColumnFamilyName:         "column family name",
-		ComparatorName:           "comparator name",
+		ComparerName:             "comparator name",
 		CompressionName:          "compression name",
 		CompressionOptions:       "compression option",
 		CreationTime:             2,
@@ -105,7 +108,7 @@ func TestPropertiesSave(t *testing.T) {
 		IndexSize:                10,
 		IndexType:                11,
 		IndexValueIsDeltaEncoded: 12,
-		MergeOperatorName:        "merge operator name",
+		MergerName:               "merge operator name",
 		NumDataBlocks:            13,
 		NumDeletions:             14,
 		NumEntries:               15,
diff --git a/sstable/reader_test.go b/sstable/reader_test.go
index 1c5844d..30ec8c6 100644
--- a/sstable/reader_test.go
+++ b/sstable/reader_test.go
@@ -179,7 +179,10 @@ func runTestReader(t *testing.T, o Options, dir string) {
 				if err != nil {
 					return err.Error()
 				}
-				r = NewReader(f, dbNum, 0, &o)
+				r, err = NewReader(f, dbNum, 0, &o)
+				if err != nil {
+					return err.Error()
+				}
 				dbNum++
 				return ""
 
@@ -362,9 +365,13 @@ func buildTestTable(
 	if err != nil {
 		t.Fatal(err)
 	}
-	return NewReader(f1, 0, 0, &Options{
+	r, err := NewReader(f1, 0, 0, &Options{
 		Cache: cache.New(128 << 20),
 	})
+	if err != nil {
+		t.Fatal(err)
+	}
+	return r
 }
 
 func buildBenchmarkTable(b *testing.B, blockSize, restartInterval int) (*Reader, [][]byte) {
@@ -400,9 +407,13 @@ func buildBenchmarkTable(b *testing.B, blockSize, restartInterval int) (*Reader,
 	if err != nil {
 		b.Fatal(err)
 	}
-	return NewReader(f1, 0, 0, &Options{
+	r, err := NewReader(f1, 0, 0, &Options{
 		Cache: cache.New(128 << 20),
-	}), keys
+	})
+	if err != nil {
+		b.Fatal(err)
+	}
+	return r, keys
 }
 
 func BenchmarkTableIterSeekGE(b *testing.B) {
diff --git a/sstable/table_test.go b/sstable/table_test.go
index c2571ec..684bb3d 100644
--- a/sstable/table_test.go
+++ b/sstable/table_test.go
@@ -115,12 +115,15 @@ func init() {
 }
 
 func check(f vfs.File, comparer *Comparer, fp FilterPolicy) error {
-	r := NewReader(f, 0, 0, &Options{
+	r, err := NewReader(f, 0, 0, &Options{
 		Comparer: comparer,
 		Levels: []TableOptions{{
 			FilterPolicy: fp,
 		}},
 	})
+	if err != nil {
+		return err
+	}
 
 	// Check that each key/value pair in wordCount is also in the table.
 	words := make([]string, 0, len(wordCount))
@@ -467,11 +470,14 @@ func TestBloomFilterFalsePositiveRate(t *testing.T) {
 	c := &countingFilterPolicy{
 		FilterPolicy: bloom.FilterPolicy(1),
 	}
-	r := NewReader(f, 0, 0, &Options{
+	r, err := NewReader(f, 0, 0, &Options{
 		Levels: []TableOptions{{
 			FilterPolicy: c,
 		}},
 	})
+	if err != nil {
+		t.Fatal(err)
+	}
 
 	const n = 10000
 	// key is a buffer that will be re-used for n Get calls, each with a
@@ -603,7 +609,10 @@ func TestFinalBlockIsWritten(t *testing.T) {
 						t.Errorf("nk=%d, vLen=%d: memFS open: %v", nk, vLen, err)
 						continue
 					}
-					r := NewReader(rf, 0, 0, nil)
+					r, err := NewReader(rf, 0, 0, nil)
+					if err != nil {
+						t.Errorf("nk=%d, vLen=%d: reader open: %v", nk, vLen, err)
+					}
 					i := iterAdapter{r.NewIter(nil /* lower */, nil /* upper */)}
 					for valid := i.First(); valid; valid = i.Next() {
 						got++
@@ -632,7 +641,10 @@ func TestReaderGlobalSeqNum(t *testing.T) {
 	if err != nil {
 		t.Fatal(err)
 	}
-	r := NewReader(f, 0, 0, nil)
+	r, err := NewReader(f, 0, 0, nil)
+	if err != nil {
+		t.Fatal(err)
+	}
 	defer r.Close()
 
 	const globalSeqNum = 42
@@ -658,10 +670,10 @@ func TestFooterRoundTrip(t *testing.T) {
 					footer := footer{
 						format:      format,
 						checksum:    checksum,
-						metaindexBH: blockHandle{offset: 1, length: 2},
-						indexBH:     blockHandle{offset: 3, length: 4},
+						metaindexBH: BlockHandle{Offset: 1, Length: 2},
+						indexBH:     BlockHandle{Offset: 3, Length: 4},
 					}
-					for offset := range []int64{0, 1, 100} {
+					for _, offset := range []int64{0, 1, 100} {
 						t.Run(fmt.Sprintf("offset=%d", offset), func(t *testing.T) {
 							mem := vfs.NewMem()
 							f, err := mem.Create("test")
@@ -671,12 +683,15 @@ func TestFooterRoundTrip(t *testing.T) {
 							if _, err := f.Write(buf[:offset]); err != nil {
 								t.Fatal(err)
 							}
-							if _, err := f.Write(footer.encode(buf[100:])); err != nil {
+							encoded := footer.encode(buf[100:])
+							if _, err := f.Write(encoded); err != nil {
 								t.Fatal(err)
 							}
 							if err := f.Close(); err != nil {
 								t.Fatal(err)
 							}
+							footer.footerBH.Offset = uint64(offset)
+							footer.footerBH.Length = uint64(len(encoded))
 
 							f, err = mem.Open("test")
 							if err != nil {
diff --git a/sstable/writer_test.go b/sstable/writer_test.go
index 869bf71..397ef72 100644
--- a/sstable/writer_test.go
+++ b/sstable/writer_test.go
@@ -83,7 +83,10 @@ func TestWriter(t *testing.T) {
 			if err != nil {
 				return err.Error()
 			}
-			r = NewReader(f1, 0, 0, nil)
+			r, err = NewReader(f1, 0, 0, nil)
+			if err != nil {
+				return err.Error()
+			}
 			return fmt.Sprintf("point:   [%s,%s]\nrange:   [%s,%s]\nseqnums: [%d,%d]\n",
 				meta.SmallestPoint, meta.LargestPoint,
 				meta.SmallestRange, meta.LargestRange,
@@ -130,7 +133,10 @@ func TestWriter(t *testing.T) {
 			if err != nil {
 				return err.Error()
 			}
-			r = NewReader(f1, 0, 0, nil)
+			r, err = NewReader(f1, 0, 0, nil)
+			if err != nil {
+				return err.Error()
+			}
 			return fmt.Sprintf("point:   [%s,%s]\nrange:   [%s,%s]\nseqnums: [%d,%d]\n",
 				meta.SmallestPoint, meta.LargestPoint,
 				meta.SmallestRange, meta.LargestRange,
diff --git a/testdata/compaction_pacer_maybe_throttle b/testdata/compaction_pacer_maybe_throttle
deleted file mode 100644
index 63ccf16..0000000
--- a/testdata/compaction_pacer_maybe_throttle
+++ /dev/null
@@ -1,239 +0,0 @@
-init compaction
-bytesIterated: 0
-currentTotal: 10
-----
-pebble: maybeThrottle supplied with invalid bytesIterated
-
-init compaction
-bytesIterated: 1
-currentTotal: 10
-----
-allow: 1
-
-init compaction
-bytesIterated: 2
-currentTotal: 10
-----
-allow: 1
-allow: 1
-
-init compaction
-bytesIterated: 3
-currentTotal: 10
-----
-allow: 1
-allow: 1
-allow: 1
-
-init compaction
-burst: 2
-bytesIterated: 1
-currentTotal: 10
-----
-allow: 1
-
-init compaction
-burst: 2
-bytesIterated: 2
-currentTotal: 10
-----
-allow: 2
-
-init compaction
-burst: 2
-bytesIterated: 3
-currentTotal: 10
-----
-allow: 2
-allow: 1
-
-init compaction
-burst: 10
-bytesIterated: 1
-currentTotal: 10
-----
-allow: 1
-
-init compaction
-burst: 10
-bytesIterated: 2
-currentTotal: 10
-----
-allow: 2
-
-init compaction
-burst: 10
-bytesIterated: 3
-currentTotal: 10
-----
-allow: 3
-
-init compaction
-burst: 10
-bytesIterated: 11
-currentTotal: 10
-----
-wait: 10
-wait: 1
-
-init compaction
-bytesIterated: 3
-currentTotal: 3
-slowdownThreshold: 1
-----
-wait: 1
-wait: 1
-wait: 1
-
-init compaction
-burst: 10
-bytesIterated: 11
-currentTotal: 5
-slowdownThreshold: 10
-----
-wait: 10
-wait: 1
-
-init compaction
-burst: 10
-bytesIterated: 11
-currentTotal: 11
-slowdownThreshold: 10
-----
-wait: 10
-wait: 1
-
-init compaction
-burst: 10
-bytesIterated: 1
-currentTotal: 12
-slowdownThreshold: 10
-----
-allow: 1
-
-init compaction
-burst: 10
-bytesIterated: 2
-currentTotal: 12
-slowdownThreshold: 10
-----
-wait: 2
-
-init compaction
-burst: 10
-bytesIterated: 3
-currentTotal: 14
-slowdownThreshold: 10
-----
-allow: 3
-
-init flush
-bytesIterated: 0
-----
-pebble: maybeThrottle supplied with invalid bytesIterated
-
-init flush
-bytesIterated: 1
-currentTotal: 10
-----
-allow: 1
-
-init flush
-bytesIterated: 2
-currentTotal: 10
-----
-allow: 1
-allow: 1
-
-init flush
-bytesIterated: 3
-currentTotal: 10
-----
-allow: 1
-allow: 1
-allow: 1
-
-init flush
-burst: 2
-bytesIterated: 1
-currentTotal: 10
-----
-allow: 1
-
-init flush
-burst: 2
-bytesIterated: 2
-currentTotal: 10
-----
-allow: 2
-
-init flush
-burst: 2
-bytesIterated: 3
-currentTotal: 10
-----
-allow: 2
-allow: 1
-
-init flush
-burst: 10
-bytesIterated: 1
-currentTotal: 10
-----
-allow: 1
-
-init flush
-burst: 10
-bytesIterated: 2
-currentTotal: 10
-----
-allow: 2
-
-init flush
-burst: 10
-bytesIterated: 3
-currentTotal: 10
-----
-allow: 3
-
-init flush
-bytesIterated: 3
-currentTotal: 3
-slowdownThreshold: 1
-----
-wait: 1
-wait: 1
-wait: 1
-
-init flush
-burst: 10
-bytesIterated: 11
-currentTotal: 11
-slowdownThreshold: 10
-----
-wait: 10
-wait: 1
-
-init flush
-burst: 10
-bytesIterated: 1
-currentTotal: 12
-slowdownThreshold: 10
-----
-allow: 1
-
-init flush
-burst: 10
-bytesIterated: 2
-currentTotal: 12
-slowdownThreshold: 10
-----
-wait: 2
-
-init flush
-burst: 10
-bytesIterated: 3
-currentTotal: 14
-slowdownThreshold: 10
-----
-allow: 3
diff --git a/tool/data_test.go b/tool/data_test.go
new file mode 100644
index 0000000..0311ee6
--- /dev/null
+++ b/tool/data_test.go
@@ -0,0 +1,50 @@
+// Copyright 2019 The LevelDB-Go and Pebble Authors. All rights reserved. Use
+// of this source code is governed by a BSD-style license that can be found in
+// the LICENSE file.
+
+package tool
+
+import (
+	"bytes"
+	"os"
+	"strings"
+	"testing"
+
+	"github.com/petermattis/pebble/internal/datadriven"
+	"github.com/spf13/cobra"
+)
+
+func TestCommands(t *testing.T) {
+	datadriven.Walk(t, "testdata", func(t *testing.T, path string) {
+		datadriven.RunTest(t, path, func(d *datadriven.TestData) string {
+			args := []string{d.Cmd}
+			for _, arg := range d.CmdArgs {
+				args = append(args, arg.String())
+			}
+			args = append(args, strings.Fields(d.Input)...)
+
+			var buf bytes.Buffer
+			stdout = &buf
+			stderr = &buf
+			osExit = func(int) {}
+
+			defer func() {
+				stdout = os.Stdout
+				stderr = os.Stderr
+				osExit = os.Exit
+				sstableConfig.start = ""
+				sstableConfig.end = ""
+				sstableConfig.verbose = false
+			}()
+
+			c := &cobra.Command{}
+			c.AddCommand(AllCmds...)
+			c.SetArgs(args)
+			c.SetOutput(&buf)
+			if err := c.Execute(); err != nil {
+				return err.Error()
+			}
+			return buf.String()
+		})
+	})
+}
diff --git a/tool/testdata/sstable_check b/tool/testdata/sstable_check
new file mode 100644
index 0000000..24e3dcd
--- /dev/null
+++ b/tool/testdata/sstable_check
@@ -0,0 +1,6 @@
+sstable check
+../sstable/testdata/h.sst
+----
+../sstable/testdata/h.sst
+
+# TODO(peter): generate an sstable with corruption
diff --git a/tool/testdata/sstable_layout b/tool/testdata/sstable_layout
new file mode 100644
index 0000000..f1067ea
--- /dev/null
+++ b/tool/testdata/sstable_layout
@@ -0,0 +1,2018 @@
+sstable layout
+----
+requires at least 1 arg(s), only received 0
+
+sstable layout
+../sstable/testdata/h.sst
+----
+../sstable/testdata/h.sst
+         0  data (1094)
+      1099  data (1057)
+      2161  data (1074)
+      3240  data (1051)
+      4296  data (1046)
+      5347  data (1091)
+      6443  data (996)
+      7444  data (1060)
+      8509  data (1051)
+      9565  data (1016)
+     10586  data (1026)
+     11617  data (1100)
+     12722  data (1025)
+     13752  data (156)
+     13913  index (245)
+     14163  properties (719)
+     14887  meta-index (33)
+     14925  footer (53)
+
+sstable layout
+../sstable/testdata/h.ldb
+----
+../sstable/testdata/h.ldb
+         0  data (1094)
+      1099  data (1057)
+      2161  data (1074)
+      3240  data (1051)
+      4296  data (1046)
+      5347  data (1091)
+      6443  data (996)
+      7444  data (1060)
+      8509  data (1051)
+      9565  data (1016)
+     10586  data (1026)
+     11617  data (1100)
+     12722  data (1025)
+     13752  data (156)
+     13913  index (245)
+     14163  properties (673)
+     14841  meta-index (33)
+     14879  leveldb-footer (48)
+
+sstable layout
+../sstable/testdata/h.table-bloom.no-compression.sst
+----
+../sstable/testdata/h.table-bloom.no-compression.sst
+         0  data (2041)
+      2046  data (2044)
+      4095  data (2039)
+      6139  data (2036)
+      8180  data (2032)
+     10217  data (2042)
+     12264  data (2039)
+     14308  data (2037)
+     16350  data (2029)
+     18384  data (2040)
+     20429  data (2030)
+     22464  data (2035)
+     24504  data (2036)
+     26545  data (249)
+     26799  filter (2245)
+     29049  index (325)
+     29379  properties (717)
+     30101  meta-index (83)
+     30189  footer (53)
+
+sstable layout
+../sstable/testdata/h.no-compression.two_level_index.sst
+----
+../sstable/testdata/h.no-compression.two_level_index.sst
+         0  data (2041)
+      2046  data (2044)
+      4095  data (2039)
+      6139  data (2036)
+      8180  data (2032)
+     10217  data (2042)
+     12264  data (2039)
+     14308  data (2037)
+     16350  data (2029)
+     18384  data (2040)
+     20429  data (2030)
+     22464  data (2035)
+     24504  data (2036)
+     26545  data (249)
+     26799  index (120)
+     26924  index (118)
+     27047  index (95)
+     27147  top-index (70)
+     27222  properties (765)
+     27992  meta-index (34)
+     28031  footer (53)
+
+sstable layout
+-v
+../sstable/testdata/h.no-compression.two_level_index.sst
+----
+../sstable/testdata/h.no-compression.two_level_index.sst
+         0  data (2041)
+         0    record (14 = 3 [0] + 9 + 2) [restart]
+        14    record (17 = 3 [1] + 13 + 1)
+        31    record (14 = 3 [3] + 10 + 1)
+        45    record (14 = 3 [3] + 10 + 1)
+        59    record (16 = 3 [2] + 12 + 1)
+        75    record (16 = 3 [2] + 12 + 1)
+        91    record (16 = 3 [2] + 12 + 1)
+       107    record (17 = 3 [1] + 13 + 1)
+       124    record (15 = 3 [4] + 11 + 1)
+       139    record (22 = 3 [2] + 18 + 1)
+       161    record (18 = 3 [2] + 14 + 1)
+       179    record (13 = 3 [2] + 9 + 1)
+       192    record (15 = 3 [3] + 11 + 1)
+       207    record (13 = 3 [6] + 9 + 1)
+       220    record (19 = 3 [1] + 15 + 1)
+       239    record (16 = 3 [3] + 12 + 1)
+       255    record (17 = 3 [0] + 13 + 1) [restart]
+       272    record (20 = 3 [2] + 16 + 1)
+       292    record (18 = 3 [2] + 14 + 1)
+       310    record (20 = 3 [2] + 16 + 1)
+       330    record (19 = 3 [2] + 15 + 1)
+       349    record (15 = 3 [3] + 11 + 1)
+       364    record (17 = 3 [1] + 13 + 1)
+       381    record (18 = 3 [3] + 14 + 1)
+       399    record (15 = 3 [2] + 11 + 1)
+       414    record (16 = 3 [5] + 12 + 1)
+       430    record (17 = 3 [1] + 12 + 2)
+       447    record (14 = 3 [5] + 10 + 1)
+       461    record (13 = 3 [1] + 9 + 1)
+       474    record (14 = 3 [1] + 10 + 1)
+       488    record (13 = 3 [3] + 9 + 1)
+       501    record (15 = 3 [1] + 11 + 1)
+       516    record (16 = 3 [0] + 11 + 2) [restart]
+       532    record (15 = 3 [3] + 11 + 1)
+       547    record (14 = 3 [3] + 10 + 1)
+       561    record (16 = 3 [2] + 12 + 1)
+       577    record (15 = 3 [2] + 11 + 1)
+       592    record (13 = 3 [4] + 9 + 1)
+       605    record (17 = 3 [2] + 13 + 1)
+       622    record (16 = 3 [2] + 12 + 1)
+       638    record (13 = 3 [1] + 9 + 1)
+       651    record (16 = 3 [2] + 12 + 1)
+       667    record (19 = 3 [2] + 15 + 1)
+       686    record (17 = 3 [4] + 13 + 1)
+       703    record (14 = 3 [1] + 9 + 2)
+       717    record (15 = 3 [2] + 9 + 3)
+       732    record (15 = 3 [2] + 11 + 1)
+       747    record (13 = 3 [5] + 9 + 1)
+       760    record (17 = 3 [0] + 13 + 1) [restart]
+       777    record (14 = 3 [3] + 10 + 1)
+       791    record (17 = 3 [2] + 13 + 1)
+       808    record (16 = 3 [2] + 12 + 1)
+       824    record (15 = 3 [2] + 11 + 1)
+       839    record (13 = 3 [2] + 9 + 1)
+       852    record (18 = 3 [1] + 14 + 1)
+       870    record (17 = 3 [5] + 13 + 1)
+       887    record (15 = 3 [3] + 11 + 1)
+       902    record (13 = 3 [6] + 9 + 1)
+       915    record (16 = 3 [4] + 12 + 1)
+       931    record (16 = 3 [3] + 12 + 1)
+       947    record (13 = 3 [2] + 9 + 1)
+       960    record (15 = 3 [1] + 10 + 2)
+       975    record (13 = 3 [2] + 9 + 1)
+       988    record (14 = 3 [3] + 10 + 1)
+      1002    record (18 = 3 [0] + 14 + 1) [restart]
+      1020    record (13 = 3 [3] + 9 + 1)
+      1033    record (16 = 3 [2] + 12 + 1)
+      1049    record (13 = 3 [2] + 9 + 1)
+      1062    record (15 = 3 [3] + 11 + 1)
+      1077    record (16 = 3 [3] + 12 + 1)
+      1093    record (13 = 3 [7] + 9 + 1)
+      1106    record (14 = 3 [1] + 9 + 2)
+      1120    record (15 = 3 [2] + 11 + 1)
+      1135    record (16 = 3 [2] + 12 + 1)
+      1151    record (16 = 3 [2] + 12 + 1)
+      1167    record (18 = 3 [3] + 14 + 1)
+      1185    record (15 = 3 [3] + 11 + 1)
+      1200    record (14 = 3 [1] + 9 + 2)
+      1214    record (20 = 3 [2] + 16 + 1)
+      1234    record (13 = 3 [5] + 9 + 1)
+      1247    record (21 = 3 [0] + 17 + 1) [restart]
+      1268    record (19 = 3 [1] + 15 + 1)
+      1287    record (15 = 3 [2] + 11 + 1)
+      1302    record (20 = 3 [2] + 16 + 1)
+      1322    record (16 = 3 [1] + 12 + 1)
+      1338    record (15 = 3 [3] + 11 + 1)
+      1353    record (16 = 3 [1] + 12 + 1)
+      1369    record (13 = 3 [3] + 9 + 1)
+      1382    record (16 = 3 [2] + 12 + 1)
+      1398    record (13 = 3 [1] + 9 + 1)
+      1411    record (16 = 3 [0] + 12 + 1)
+      1427    record (14 = 3 [2] + 10 + 1)
+      1441    record (15 = 3 [2] + 11 + 1)
+      1456    record (14 = 3 [2] + 10 + 1)
+      1470    record (13 = 3 [3] + 9 + 1)
+      1483    record (14 = 3 [2] + 10 + 1)
+      1497    record (17 = 3 [0] + 13 + 1) [restart]
+      1514    record (15 = 3 [2] + 11 + 1)
+      1529    record (14 = 3 [1] + 9 + 2)
+      1543    record (14 = 3 [2] + 10 + 1)
+      1557    record (13 = 3 [4] + 9 + 1)
+      1570    record (15 = 3 [4] + 11 + 1)
+      1585    record (13 = 3 [4] + 9 + 1)
+      1598    record (14 = 3 [3] + 10 + 1)
+      1612    record (16 = 3 [3] + 12 + 1)
+      1628    record (15 = 3 [3] + 11 + 1)
+      1643    record (15 = 3 [3] + 11 + 1)
+      1658    record (17 = 3 [2] + 13 + 1)
+      1675    record (13 = 3 [2] + 9 + 1)
+      1688    record (14 = 3 [2] + 10 + 1)
+      1702    record (16 = 3 [3] + 12 + 1)
+      1718    record (18 = 3 [2] + 14 + 1)
+      1736    record (18 = 3 [0] + 14 + 1) [restart]
+      1754    record (13 = 3 [2] + 9 + 1)
+      1767    record (16 = 3 [3] + 12 + 1)
+      1783    record (16 = 3 [2] + 12 + 1)
+      1799    record (15 = 3 [4] + 11 + 1)
+      1814    record (15 = 3 [2] + 11 + 1)
+      1829    record (16 = 3 [2] + 12 + 1)
+      1845    record (14 = 3 [5] + 10 + 1)
+      1859    record (13 = 3 [3] + 9 + 1)
+      1872    record (14 = 3 [2] + 10 + 1)
+      1886    record (16 = 3 [3] + 12 + 1)
+      1902    record (15 = 3 [4] + 11 + 1)
+      1917    record (19 = 3 [2] + 14 + 2)
+      1936    record (17 = 3 [2] + 13 + 1)
+      1953    record (17 = 3 [3] + 13 + 1)
+      1970    record (13 = 3 [3] + 9 + 1)
+      1983    record (18 = 3 [0] + 14 + 1) [restart]
+      2001    [restart 0]
+      2005    [restart 255]
+      2009    [restart 516]
+      2013    [restart 760]
+      2017    [restart 1002]
+      2021    [restart 1247]
+      2025    [restart 1497]
+      2029    [restart 1736]
+      2033    [restart 1983]
+      2046  data (2044)
+      2046    record (21 = 3 [0] + 17 + 1) [restart]
+      2067    record (15 = 3 [3] + 11 + 1)
+      2082    record (16 = 3 [3] + 12 + 1)
+      2098    record (16 = 3 [2] + 12 + 1)
+      2114    record (16 = 3 [2] + 12 + 1)
+      2130    record (14 = 3 [1] + 10 + 1)
+      2144    record (14 = 3 [2] + 10 + 1)
+      2158    record (14 = 3 [3] + 10 + 1)
+      2172    record (15 = 3 [2] + 11 + 1)
+      2187    record (15 = 3 [3] + 11 + 1)
+      2202    record (16 = 3 [1] + 12 + 1)
+      2218    record (14 = 3 [3] + 10 + 1)
+      2232    record (17 = 3 [5] + 13 + 1)
+      2249    record (13 = 3 [5] + 9 + 1)
+      2262    record (15 = 3 [3] + 11 + 1)
+      2277    record (14 = 3 [4] + 10 + 1)
+      2291    record (20 = 3 [0] + 16 + 1) [restart]
+      2311    record (15 = 3 [2] + 11 + 1)
+      2326    record (17 = 3 [3] + 13 + 1)
+      2343    record (14 = 3 [3] + 10 + 1)
+      2357    record (16 = 3 [1] + 12 + 1)
+      2373    record (13 = 3 [3] + 9 + 1)
+      2386    record (15 = 3 [2] + 11 + 1)
+      2401    record (14 = 3 [3] + 10 + 1)
+      2415    record (14 = 3 [2] + 10 + 1)
+      2429    record (13 = 3 [4] + 9 + 1)
+      2442    record (14 = 3 [2] + 10 + 1)
+      2456    record (17 = 3 [3] + 13 + 1)
+      2473    record (15 = 3 [6] + 11 + 1)
+      2488    record (15 = 3 [2] + 11 + 1)
+      2503    record (14 = 3 [2] + 10 + 1)
+      2517    record (15 = 3 [2] + 11 + 1)
+      2532    record (21 = 3 [0] + 17 + 1) [restart]
+      2553    record (13 = 3 [2] + 9 + 1)
+      2566    record (13 = 3 [2] + 9 + 1)
+      2579    record (16 = 3 [1] + 12 + 1)
+      2595    record (13 = 3 [3] + 9 + 1)
+      2608    record (15 = 3 [3] + 11 + 1)
+      2623    record (16 = 3 [2] + 12 + 1)
+      2639    record (13 = 3 [4] + 9 + 1)
+      2652    record (15 = 3 [5] + 11 + 1)
+      2667    record (14 = 3 [4] + 10 + 1)
+      2681    record (15 = 3 [6] + 11 + 1)
+      2696    record (15 = 3 [2] + 11 + 1)
+      2711    record (14 = 3 [3] + 10 + 1)
+      2725    record (17 = 3 [2] + 13 + 1)
+      2742    record (16 = 3 [3] + 12 + 1)
+      2758    record (13 = 3 [3] + 9 + 1)
+      2771    record (17 = 3 [0] + 13 + 1) [restart]
+      2788    record (15 = 3 [1] + 11 + 1)
+      2803    record (16 = 3 [2] + 12 + 1)
+      2819    record (14 = 3 [3] + 10 + 1)
+      2833    record (13 = 3 [4] + 9 + 1)
+      2846    record (14 = 3 [3] + 10 + 1)
+      2860    record (18 = 3 [2] + 14 + 1)
+      2878    record (14 = 3 [2] + 9 + 2)
+      2892    record (16 = 3 [3] + 12 + 1)
+      2908    record (13 = 3 [2] + 9 + 1)
+      2921    record (14 = 3 [1] + 9 + 2)
+      2935    record (16 = 3 [0] + 12 + 1)
+      2951    record (19 = 3 [3] + 15 + 1)
+      2970    record (14 = 3 [2] + 10 + 1)
+      2984    record (13 = 3 [2] + 9 + 1)
+      2997    record (15 = 3 [3] + 11 + 1)
+      3012    record (18 = 3 [0] + 14 + 1) [restart]
+      3030    record (13 = 3 [5] + 9 + 1)
+      3043    record (14 = 3 [3] + 10 + 1)
+      3057    record (16 = 3 [5] + 12 + 1)
+      3073    record (14 = 3 [3] + 10 + 1)
+      3087    record (13 = 3 [2] + 9 + 1)
+      3100    record (19 = 3 [2] + 15 + 1)
+      3119    record (17 = 3 [3] + 13 + 1)
+      3136    record (16 = 3 [4] + 12 + 1)
+      3152    record (14 = 3 [3] + 10 + 1)
+      3166    record (14 = 3 [2] + 10 + 1)
+      3180    record (14 = 3 [4] + 10 + 1)
+      3194    record (15 = 3 [2] + 11 + 1)
+      3209    record (16 = 3 [2] + 12 + 1)
+      3225    record (15 = 3 [4] + 11 + 1)
+      3240    record (21 = 3 [1] + 17 + 1)
+      3261    record (21 = 3 [0] + 17 + 1) [restart]
+      3282    record (18 = 3 [3] + 14 + 1)
+      3300    record (17 = 3 [2] + 13 + 1)
+      3317    record (19 = 3 [2] + 15 + 1)
+      3336    record (16 = 3 [3] + 12 + 1)
+      3352    record (18 = 3 [1] + 14 + 1)
+      3370    record (14 = 3 [4] + 10 + 1)
+      3384    record (18 = 3 [3] + 14 + 1)
+      3402    record (14 = 3 [4] + 10 + 1)
+      3416    record (16 = 3 [4] + 12 + 1)
+      3432    record (17 = 3 [5] + 13 + 1)
+      3449    record (13 = 3 [4] + 9 + 1)
+      3462    record (15 = 3 [3] + 11 + 1)
+      3477    record (15 = 3 [2] + 11 + 1)
+      3492    record (15 = 3 [2] + 11 + 1)
+      3507    record (15 = 3 [5] + 11 + 1)
+      3522    record (18 = 3 [0] + 14 + 1) [restart]
+      3540    record (15 = 3 [3] + 11 + 1)
+      3555    record (24 = 3 [1] + 20 + 1)
+      3579    record (17 = 3 [7] + 13 + 1)
+      3596    record (15 = 3 [1] + 11 + 1)
+      3611    record (17 = 3 [3] + 13 + 1)
+      3628    record (17 = 3 [2] + 13 + 1)
+      3645    record (14 = 3 [3] + 10 + 1)
+      3659    record (15 = 3 [2] + 11 + 1)
+      3674    record (19 = 3 [3] + 15 + 1)
+      3693    record (15 = 3 [2] + 11 + 1)
+      3708    record (15 = 3 [3] + 11 + 1)
+      3723    record (15 = 3 [1] + 11 + 1)
+      3738    record (14 = 3 [2] + 10 + 1)
+      3752    record (14 = 3 [4] + 10 + 1)
+      3766    record (19 = 3 [3] + 15 + 1)
+      3785    record (18 = 3 [0] + 14 + 1) [restart]
+      3803    record (16 = 3 [2] + 12 + 1)
+      3819    record (14 = 3 [6] + 10 + 1)
+      3833    record (16 = 3 [4] + 12 + 1)
+      3849    record (14 = 3 [3] + 9 + 2)
+      3863    record (13 = 3 [4] + 9 + 1)
+      3876    record (13 = 3 [5] + 9 + 1)
+      3889    record (16 = 3 [3] + 12 + 1)
+      3905    record (15 = 3 [3] + 11 + 1)
+      3920    record (16 = 3 [3] + 12 + 1)
+      3936    record (16 = 3 [7] + 12 + 1)
+      3952    record (15 = 3 [4] + 11 + 1)
+      3967    record (16 = 3 [7] + 12 + 1)
+      3983    record (14 = 3 [4] + 10 + 1)
+      3997    record (16 = 3 [3] + 12 + 1)
+      4013    record (17 = 3 [4] + 13 + 1)
+      4030    record (20 = 3 [0] + 16 + 1) [restart]
+      4050    [restart 2046]
+      4054    [restart 2291]
+      4058    [restart 2532]
+      4062    [restart 2771]
+      4066    [restart 3012]
+      4070    [restart 3261]
+      4074    [restart 3522]
+      4078    [restart 3785]
+      4082    [restart 4030]
+      4095  data (2039)
+      4095    record (22 = 3 [0] + 18 + 1) [restart]
+      4117    record (20 = 3 [4] + 16 + 1)
+      4137    record (16 = 3 [3] + 12 + 1)
+      4153    record (17 = 3 [2] + 13 + 1)
+      4170    record (20 = 3 [3] + 16 + 1)
+      4190    record (16 = 3 [3] + 12 + 1)
+      4206    record (15 = 3 [4] + 11 + 1)
+      4221    record (13 = 3 [7] + 9 + 1)
+      4234    record (18 = 3 [3] + 14 + 1)
+      4252    record (16 = 3 [3] + 12 + 1)
+      4268    record (18 = 3 [4] + 14 + 1)
+      4286    record (19 = 3 [3] + 15 + 1)
+      4305    record (18 = 3 [4] + 14 + 1)
+      4323    record (15 = 3 [5] + 11 + 1)
+      4338    record (21 = 3 [3] + 17 + 1)
+      4359    record (14 = 3 [4] + 10 + 1)
+      4373    record (18 = 3 [0] + 14 + 1) [restart]
+      4391    record (19 = 3 [2] + 15 + 1)
+      4410    record (19 = 3 [3] + 15 + 1)
+      4429    record (19 = 3 [3] + 15 + 1)
+      4448    record (14 = 3 [3] + 10 + 1)
+      4462    record (16 = 3 [2] + 12 + 1)
+      4478    record (15 = 3 [2] + 11 + 1)
+      4493    record (14 = 3 [3] + 10 + 1)
+      4507    record (20 = 3 [3] + 16 + 1)
+      4527    record (14 = 3 [5] + 10 + 1)
+      4541    record (15 = 3 [7] + 11 + 1)
+      4556    record (15 = 3 [3] + 11 + 1)
+      4571    record (15 = 3 [3] + 11 + 1)
+      4586    record (13 = 3 [6] + 9 + 1)
+      4599    record (13 = 3 [4] + 9 + 1)
+      4612    record (16 = 3 [5] + 12 + 1)
+      4628    record (20 = 3 [0] + 16 + 1) [restart]
+      4648    record (15 = 3 [3] + 11 + 1)
+      4663    record (18 = 3 [2] + 14 + 1)
+      4681    record (16 = 3 [1] + 12 + 1)
+      4697    record (17 = 3 [2] + 13 + 1)
+      4714    record (17 = 3 [3] + 13 + 1)
+      4731    record (13 = 3 [3] + 9 + 1)
+      4744    record (15 = 3 [2] + 11 + 1)
+      4759    record (13 = 3 [4] + 9 + 1)
+      4772    record (15 = 3 [3] + 11 + 1)
+      4787    record (15 = 3 [2] + 11 + 1)
+      4802    record (16 = 3 [3] + 12 + 1)
+      4818    record (13 = 3 [4] + 9 + 1)
+      4831    record (13 = 3 [4] + 9 + 1)
+      4844    record (15 = 3 [2] + 11 + 1)
+      4859    record (15 = 3 [1] + 11 + 1)
+      4874    record (18 = 3 [0] + 14 + 1) [restart]
+      4892    record (16 = 3 [2] + 12 + 1)
+      4908    record (15 = 3 [6] + 11 + 1)
+      4923    record (13 = 3 [2] + 9 + 1)
+      4936    record (14 = 3 [0] + 9 + 2)
+      4950    record (16 = 3 [1] + 12 + 1)
+      4966    record (19 = 3 [2] + 15 + 1)
+      4985    record (14 = 3 [2] + 10 + 1)
+      4999    record (14 = 3 [4] + 10 + 1)
+      5013    record (14 = 3 [2] + 10 + 1)
+      5027    record (15 = 3 [3] + 11 + 1)
+      5042    record (15 = 3 [2] + 11 + 1)
+      5057    record (13 = 3 [4] + 9 + 1)
+      5070    record (18 = 3 [2] + 14 + 1)
+      5088    record (17 = 3 [2] + 13 + 1)
+      5105    record (13 = 3 [2] + 9 + 1)
+      5118    record (16 = 3 [0] + 12 + 1) [restart]
+      5134    record (15 = 3 [1] + 11 + 1)
+      5149    record (13 = 3 [3] + 9 + 1)
+      5162    record (15 = 3 [4] + 11 + 1)
+      5177    record (14 = 3 [4] + 10 + 1)
+      5191    record (14 = 3 [3] + 10 + 1)
+      5205    record (17 = 3 [2] + 13 + 1)
+      5222    record (14 = 3 [2] + 10 + 1)
+      5236    record (13 = 3 [4] + 9 + 1)
+      5249    record (13 = 3 [3] + 9 + 1)
+      5262    record (18 = 3 [2] + 14 + 1)
+      5280    record (14 = 3 [4] + 10 + 1)
+      5294    record (14 = 3 [4] + 10 + 1)
+      5308    record (18 = 3 [2] + 14 + 1)
+      5326    record (17 = 3 [2] + 13 + 1)
+      5343    record (16 = 3 [3] + 12 + 1)
+      5359    record (19 = 3 [0] + 15 + 1) [restart]
+      5378    record (22 = 3 [2] + 18 + 1)
+      5400    record (18 = 3 [2] + 13 + 2)
+      5418    record (15 = 3 [3] + 11 + 1)
+      5433    record (16 = 3 [2] + 12 + 1)
+      5449    record (16 = 3 [3] + 12 + 1)
+      5465    record (16 = 3 [3] + 12 + 1)
+      5481    record (16 = 3 [2] + 12 + 1)
+      5497    record (14 = 3 [4] + 10 + 1)
+      5511    record (18 = 3 [3] + 14 + 1)
+      5529    record (15 = 3 [8] + 11 + 1)
+      5544    record (13 = 3 [2] + 9 + 1)
+      5557    record (13 = 3 [3] + 9 + 1)
+      5570    record (19 = 3 [2] + 15 + 1)
+      5589    record (15 = 3 [1] + 10 + 2)
+      5604    record (14 = 3 [3] + 10 + 1)
+      5618    record (15 = 3 [0] + 11 + 1) [restart]
+      5633    record (13 = 3 [3] + 9 + 1)
+      5646    record (13 = 3 [3] + 9 + 1)
+      5659    record (17 = 3 [2] + 13 + 1)
+      5676    record (16 = 3 [2] + 12 + 1)
+      5692    record (14 = 3 [3] + 10 + 1)
+      5706    record (22 = 3 [2] + 18 + 1)
+      5728    record (17 = 3 [4] + 13 + 1)
+      5745    record (18 = 3 [3] + 14 + 1)
+      5763    record (17 = 3 [4] + 13 + 1)
+      5780    record (18 = 3 [4] + 14 + 1)
+      5798    record (17 = 3 [3] + 13 + 1)
+      5815    record (17 = 3 [3] + 13 + 1)
+      5832    record (19 = 3 [4] + 15 + 1)
+      5851    record (18 = 3 [3] + 14 + 1)
+      5869    record (16 = 3 [6] + 12 + 1)
+      5885    record (22 = 3 [0] + 18 + 1) [restart]
+      5907    record (16 = 3 [2] + 12 + 1)
+      5923    record (14 = 3 [1] + 9 + 2)
+      5937    record (14 = 3 [2] + 10 + 1)
+      5951    record (14 = 3 [2] + 10 + 1)
+      5965    record (14 = 3 [2] + 10 + 1)
+      5979    record (14 = 3 [2] + 10 + 1)
+      5993    record (16 = 3 [4] + 12 + 1)
+      6009    record (14 = 3 [2] + 10 + 1)
+      6023    record (16 = 3 [2] + 12 + 1)
+      6039    record (13 = 3 [4] + 9 + 1)
+      6052    record (15 = 3 [5] + 11 + 1)
+      6067    record (14 = 3 [2] + 10 + 1)
+      6081    record (17 = 3 [1] + 13 + 1)
+      6098    [restart 4095]
+      6102    [restart 4373]
+      6106    [restart 4628]
+      6110    [restart 4874]
+      6114    [restart 5118]
+      6118    [restart 5359]
+      6122    [restart 5618]
+      6126    [restart 5885]
+      6139  data (2036)
+      6139    record (16 = 3 [0] + 12 + 1) [restart]
+      6155    record (17 = 3 [3] + 13 + 1)
+      6172    record (13 = 3 [3] + 9 + 1)
+      6185    record (13 = 3 [4] + 9 + 1)
+      6198    record (15 = 3 [2] + 11 + 1)
+      6213    record (14 = 3 [5] + 10 + 1)
+      6227    record (15 = 3 [5] + 11 + 1)
+      6242    record (13 = 3 [4] + 9 + 1)
+      6255    record (13 = 3 [5] + 9 + 1)
+      6268    record (15 = 3 [2] + 11 + 1)
+      6283    record (13 = 3 [5] + 9 + 1)
+      6296    record (18 = 3 [2] + 14 + 1)
+      6314    record (13 = 3 [8] + 9 + 1)
+      6327    record (14 = 3 [2] + 10 + 1)
+      6341    record (18 = 3 [3] + 14 + 1)
+      6359    record (15 = 3 [1] + 11 + 1)
+      6374    record (18 = 3 [0] + 14 + 1) [restart]
+      6392    record (13 = 3 [4] + 9 + 1)
+      6405    record (14 = 3 [2] + 10 + 1)
+      6419    record (14 = 3 [2] + 10 + 1)
+      6433    record (16 = 3 [2] + 12 + 1)
+      6449    record (13 = 3 [3] + 9 + 1)
+      6462    record (19 = 3 [1] + 15 + 1)
+      6481    record (14 = 3 [1] + 10 + 1)
+      6495    record (13 = 3 [0] + 9 + 1)
+      6508    record (15 = 3 [1] + 11 + 1)
+      6523    record (15 = 3 [2] + 11 + 1)
+      6538    record (14 = 3 [2] + 10 + 1)
+      6552    record (13 = 3 [2] + 9 + 1)
+      6565    record (13 = 3 [3] + 9 + 1)
+      6578    record (14 = 3 [3] + 10 + 1)
+      6592    record (14 = 3 [5] + 10 + 1)
+      6606    record (16 = 3 [0] + 12 + 1) [restart]
+      6622    record (13 = 3 [3] + 9 + 1)
+      6635    record (16 = 3 [4] + 12 + 1)
+      6651    record (18 = 3 [1] + 14 + 1)
+      6669    record (15 = 3 [1] + 11 + 1)
+      6684    record (17 = 3 [1] + 13 + 1)
+      6701    record (17 = 3 [1] + 13 + 1)
+      6718    record (14 = 3 [2] + 10 + 1)
+      6732    record (17 = 3 [3] + 13 + 1)
+      6749    record (17 = 3 [1] + 13 + 1)
+      6766    record (16 = 3 [2] + 12 + 1)
+      6782    record (17 = 3 [2] + 13 + 1)
+      6799    record (13 = 3 [1] + 9 + 1)
+      6812    record (19 = 3 [2] + 15 + 1)
+      6831    record (17 = 3 [3] + 13 + 1)
+      6848    record (13 = 3 [2] + 9 + 1)
+      6861    record (17 = 3 [0] + 13 + 1) [restart]
+      6878    record (16 = 3 [2] + 12 + 1)
+      6894    record (16 = 3 [2] + 12 + 1)
+      6910    record (16 = 3 [2] + 11 + 2)
+      6926    record (17 = 3 [5] + 13 + 1)
+      6943    record (20 = 3 [5] + 16 + 1)
+      6963    record (17 = 3 [3] + 13 + 1)
+      6980    record (17 = 3 [4] + 13 + 1)
+      6997    record (17 = 3 [7] + 13 + 1)
+      7014    record (16 = 3 [1] + 12 + 1)
+      7030    record (13 = 3 [1] + 9 + 1)
+      7043    record (13 = 3 [2] + 9 + 1)
+      7056    record (18 = 3 [2] + 14 + 1)
+      7074    record (18 = 3 [2] + 14 + 1)
+      7092    record (16 = 3 [2] + 12 + 1)
+      7108    record (18 = 3 [2] + 14 + 1)
+      7126    record (19 = 3 [0] + 15 + 1) [restart]
+      7145    record (17 = 3 [1] + 13 + 1)
+      7162    record (13 = 3 [1] + 9 + 1)
+      7175    record (17 = 3 [2] + 13 + 1)
+      7192    record (15 = 3 [5] + 11 + 1)
+      7207    record (15 = 3 [1] + 11 + 1)
+      7222    record (14 = 3 [4] + 10 + 1)
+      7236    record (13 = 3 [3] + 9 + 1)
+      7249    record (19 = 3 [4] + 15 + 1)
+      7268    record (13 = 3 [4] + 9 + 1)
+      7281    record (18 = 3 [1] + 14 + 1)
+      7299    record (19 = 3 [2] + 15 + 1)
+      7318    record (16 = 3 [2] + 12 + 1)
+      7334    record (14 = 3 [2] + 10 + 1)
+      7348    record (17 = 3 [2] + 13 + 1)
+      7365    record (17 = 3 [2] + 13 + 1)
+      7382    record (20 = 3 [0] + 16 + 1) [restart]
+      7402    record (20 = 3 [3] + 16 + 1)
+      7422    record (14 = 3 [1] + 10 + 1)
+      7436    record (13 = 3 [3] + 9 + 1)
+      7449    record (16 = 3 [0] + 12 + 1)
+      7465    record (15 = 3 [2] + 11 + 1)
+      7480    record (14 = 3 [2] + 10 + 1)
+      7494    record (13 = 3 [3] + 9 + 1)
+      7507    record (13 = 3 [4] + 9 + 1)
+      7520    record (14 = 3 [3] + 10 + 1)
+      7534    record (17 = 3 [2] + 13 + 1)
+      7551    record (14 = 3 [3] + 10 + 1)
+      7565    record (18 = 3 [2] + 14 + 1)
+      7583    record (15 = 3 [2] + 11 + 1)
+      7598    record (16 = 3 [3] + 12 + 1)
+      7614    record (13 = 3 [2] + 9 + 1)
+      7627    record (16 = 3 [0] + 12 + 1) [restart]
+      7643    record (16 = 3 [4] + 12 + 1)
+      7659    record (17 = 3 [2] + 13 + 1)
+      7676    record (13 = 3 [3] + 9 + 1)
+      7689    record (13 = 3 [2] + 9 + 1)
+      7702    record (13 = 3 [3] + 9 + 1)
+      7715    record (13 = 3 [4] + 9 + 1)
+      7728    record (16 = 3 [3] + 11 + 2)
+      7744    record (13 = 3 [6] + 9 + 1)
+      7757    record (15 = 3 [4] + 11 + 1)
+      7772    record (15 = 3 [2] + 11 + 1)
+      7787    record (16 = 3 [2] + 12 + 1)
+      7803    record (15 = 3 [1] + 11 + 1)
+      7818    record (15 = 3 [4] + 11 + 1)
+      7833    record (13 = 3 [2] + 9 + 1)
+      7846    record (13 = 3 [2] + 9 + 1)
+      7859    record (16 = 3 [0] + 12 + 1) [restart]
+      7875    record (14 = 3 [4] + 10 + 1)
+      7889    record (13 = 3 [2] + 9 + 1)
+      7902    record (14 = 3 [1] + 10 + 1)
+      7916    record (15 = 3 [3] + 11 + 1)
+      7931    record (16 = 3 [2] + 12 + 1)
+      7947    record (16 = 3 [2] + 12 + 1)
+      7963    record (14 = 3 [2] + 10 + 1)
+      7977    record (16 = 3 [3] + 12 + 1)
+      7993    record (14 = 3 [2] + 10 + 1)
+      8007    record (13 = 3 [4] + 9 + 1)
+      8020    record (14 = 3 [3] + 10 + 1)
+      8034    record (13 = 3 [2] + 9 + 1)
+      8047    record (13 = 3 [3] + 9 + 1)
+      8060    record (16 = 3 [3] + 12 + 1)
+      8076    record (13 = 3 [2] + 9 + 1)
+      8089    record (18 = 3 [0] + 14 + 1) [restart]
+      8107    record (13 = 3 [3] + 9 + 1)
+      8120    record (15 = 3 [2] + 11 + 1)
+      8135    [restart 6139]
+      8139    [restart 6374]
+      8143    [restart 6606]
+      8147    [restart 6861]
+      8151    [restart 7126]
+      8155    [restart 7382]
+      8159    [restart 7627]
+      8163    [restart 7859]
+      8167    [restart 8089]
+      8180  data (2032)
+      8180    record (17 = 3 [0] + 13 + 1) [restart]
+      8197    record (17 = 3 [3] + 13 + 1)
+      8214    record (18 = 3 [2] + 14 + 1)
+      8232    record (14 = 3 [1] + 10 + 1)
+      8246    record (16 = 3 [2] + 12 + 1)
+      8262    record (13 = 3 [6] + 9 + 1)
+      8275    record (14 = 3 [2] + 10 + 1)
+      8289    record (14 = 3 [2] + 10 + 1)
+      8303    record (13 = 3 [3] + 9 + 1)
+      8316    record (13 = 3 [4] + 9 + 1)
+      8329    record (13 = 3 [3] + 9 + 1)
+      8342    record (14 = 3 [2] + 9 + 2)
+      8356    record (15 = 3 [3] + 11 + 1)
+      8371    record (15 = 3 [3] + 11 + 1)
+      8386    record (16 = 3 [3] + 12 + 1)
+      8402    record (19 = 3 [4] + 15 + 1)
+      8421    record (20 = 3 [0] + 16 + 1) [restart]
+      8441    record (16 = 3 [3] + 12 + 1)
+      8457    record (15 = 3 [3] + 11 + 1)
+      8472    record (13 = 3 [5] + 9 + 1)
+      8485    record (13 = 3 [3] + 9 + 1)
+      8498    record (13 = 3 [4] + 9 + 1)
+      8511    record (14 = 3 [3] + 10 + 1)
+      8525    record (17 = 3 [4] + 13 + 1)
+      8542    record (17 = 3 [5] + 13 + 1)
+      8559    record (13 = 3 [4] + 9 + 1)
+      8572    record (15 = 3 [4] + 11 + 1)
+      8587    record (16 = 3 [3] + 12 + 1)
+      8603    record (16 = 3 [2] + 12 + 1)
+      8619    record (13 = 3 [3] + 9 + 1)
+      8632    record (18 = 3 [1] + 14 + 1)
+      8650    record (14 = 3 [3] + 10 + 1)
+      8664    record (18 = 3 [0] + 14 + 1) [restart]
+      8682    record (17 = 3 [5] + 12 + 2)
+      8699    record (14 = 3 [2] + 10 + 1)
+      8713    record (14 = 3 [4] + 10 + 1)
+      8727    record (14 = 3 [4] + 10 + 1)
+      8741    record (16 = 3 [3] + 12 + 1)
+      8757    record (16 = 3 [2] + 12 + 1)
+      8773    record (15 = 3 [6] + 11 + 1)
+      8788    record (13 = 3 [6] + 9 + 1)
+      8801    record (15 = 3 [2] + 10 + 2)
+      8816    record (14 = 3 [3] + 10 + 1)
+      8830    record (17 = 3 [5] + 13 + 1)
+      8847    record (18 = 3 [2] + 14 + 1)
+      8865    record (15 = 3 [1] + 11 + 1)
+      8880    record (17 = 3 [2] + 13 + 1)
+      8897    record (17 = 3 [2] + 13 + 1)
+      8914    record (19 = 3 [0] + 15 + 1) [restart]
+      8933    record (17 = 3 [0] + 13 + 1)
+      8950    record (16 = 3 [2] + 12 + 1)
+      8966    record (13 = 3 [3] + 9 + 1)
+      8979    record (16 = 3 [2] + 12 + 1)
+      8995    record (13 = 3 [4] + 9 + 1)
+      9008    record (14 = 3 [2] + 10 + 1)
+      9022    record (17 = 3 [2] + 13 + 1)
+      9039    record (15 = 3 [3] + 11 + 1)
+      9054    record (15 = 3 [2] + 11 + 1)
+      9069    record (15 = 3 [2] + 11 + 1)
+      9084    record (18 = 3 [1] + 14 + 1)
+      9102    record (15 = 3 [5] + 11 + 1)
+      9117    record (15 = 3 [3] + 11 + 1)
+      9132    record (15 = 3 [6] + 11 + 1)
+      9147    record (18 = 3 [2] + 14 + 1)
+      9165    record (15 = 3 [0] + 11 + 1) [restart]
+      9180    record (17 = 3 [1] + 12 + 2)
+      9197    record (17 = 3 [1] + 13 + 1)
+      9214    record (15 = 3 [2] + 11 + 1)
+      9229    record (14 = 3 [2] + 10 + 1)
+      9243    record (14 = 3 [2] + 10 + 1)
+      9257    record (15 = 3 [2] + 10 + 2)
+      9272    record (13 = 3 [4] + 9 + 1)
+      9285    record (15 = 3 [3] + 11 + 1)
+      9300    record (15 = 3 [1] + 11 + 1)
+      9315    record (18 = 3 [2] + 14 + 1)
+      9333    record (15 = 3 [2] + 11 + 1)
+      9348    record (13 = 3 [3] + 9 + 1)
+      9361    record (14 = 3 [1] + 9 + 2)
+      9375    record (16 = 3 [2] + 12 + 1)
+      9391    record (13 = 3 [2] + 9 + 1)
+      9404    record (16 = 3 [0] + 12 + 1) [restart]
+      9420    record (15 = 3 [2] + 11 + 1)
+      9435    record (14 = 3 [2] + 10 + 1)
+      9449    record (15 = 3 [2] + 10 + 2)
+      9464    record (14 = 3 [4] + 10 + 1)
+      9478    record (16 = 3 [1] + 12 + 1)
+      9494    record (13 = 3 [5] + 9 + 1)
+      9507    record (16 = 3 [4] + 12 + 1)
+      9523    record (16 = 3 [3] + 12 + 1)
+      9539    record (14 = 3 [3] + 10 + 1)
+      9553    record (13 = 3 [5] + 9 + 1)
+      9566    record (15 = 3 [2] + 11 + 1)
+      9581    record (16 = 3 [5] + 12 + 1)
+      9597    record (14 = 3 [3] + 10 + 1)
+      9611    record (16 = 3 [4] + 12 + 1)
+      9627    record (15 = 3 [2] + 11 + 1)
+      9642    record (20 = 3 [0] + 16 + 1) [restart]
+      9662    record (15 = 3 [2] + 11 + 1)
+      9677    record (15 = 3 [3] + 11 + 1)
+      9692    record (13 = 3 [3] + 9 + 1)
+      9705    record (13 = 3 [4] + 9 + 1)
+      9718    record (13 = 3 [4] + 9 + 1)
+      9731    record (16 = 3 [1] + 12 + 1)
+      9747    record (16 = 3 [2] + 12 + 1)
+      9763    record (14 = 3 [0] + 10 + 1)
+      9777    record (15 = 3 [2] + 11 + 1)
+      9792    record (14 = 3 [2] + 9 + 2)
+      9806    record (14 = 3 [2] + 10 + 1)
+      9820    record (13 = 3 [3] + 9 + 1)
+      9833    record (16 = 3 [2] + 12 + 1)
+      9849    record (18 = 3 [2] + 12 + 3)
+      9867    record (14 = 3 [2] + 10 + 1)
+      9881    record (17 = 3 [0] + 13 + 1) [restart]
+      9898    record (13 = 3 [3] + 9 + 1)
+      9911    record (13 = 3 [2] + 9 + 1)
+      9924    record (16 = 3 [3] + 12 + 1)
+      9940    record (20 = 3 [2] + 16 + 1)
+      9960    record (13 = 3 [3] + 9 + 1)
+      9973    record (13 = 3 [4] + 9 + 1)
+      9986    record (15 = 3 [3] + 11 + 1)
+     10001    record (13 = 3 [6] + 9 + 1)
+     10014    record (13 = 3 [2] + 9 + 1)
+     10027    record (13 = 3 [3] + 9 + 1)
+     10040    record (13 = 3 [4] + 9 + 1)
+     10053    record (15 = 3 [2] + 11 + 1)
+     10068    record (14 = 3 [3] + 9 + 2)
+     10082    record (15 = 3 [2] + 10 + 2)
+     10097    record (15 = 3 [3] + 11 + 1)
+     10112    record (15 = 3 [0] + 10 + 2) [restart]
+     10127    record (14 = 3 [2] + 10 + 1)
+     10141    record (14 = 3 [4] + 10 + 1)
+     10155    record (17 = 3 [4] + 13 + 1)
+     10172    [restart 8180]
+     10176    [restart 8421]
+     10180    [restart 8664]
+     10184    [restart 8914]
+     10188    [restart 9165]
+     10192    [restart 9404]
+     10196    [restart 9642]
+     10200    [restart 9881]
+     10204    [restart 10112]
+     10217  data (2042)
+     10217    record (18 = 3 [0] + 14 + 1) [restart]
+     10235    record (13 = 3 [3] + 9 + 1)
+     10248    record (13 = 3 [4] + 9 + 1)
+     10261    record (15 = 3 [4] + 11 + 1)
+     10276    record (13 = 3 [4] + 9 + 1)
+     10289    record (14 = 3 [5] + 10 + 1)
+     10303    record (14 = 3 [4] + 9 + 2)
+     10317    record (15 = 3 [5] + 11 + 1)
+     10332    record (13 = 3 [5] + 9 + 1)
+     10345    record (13 = 3 [3] + 9 + 1)
+     10358    record (16 = 3 [3] + 11 + 2)
+     10374    record (13 = 3 [6] + 9 + 1)
+     10387    record (13 = 3 [4] + 9 + 1)
+     10400    record (17 = 3 [2] + 13 + 1)
+     10417    record (16 = 3 [2] + 12 + 1)
+     10433    record (14 = 3 [2] + 10 + 1)
+     10447    record (16 = 3 [0] + 12 + 1) [restart]
+     10463    record (13 = 3 [3] + 9 + 1)
+     10476    record (13 = 3 [2] + 9 + 1)
+     10489    record (17 = 3 [3] + 13 + 1)
+     10506    record (17 = 3 [3] + 13 + 1)
+     10523    record (14 = 3 [3] + 9 + 2)
+     10537    record (17 = 3 [4] + 13 + 1)
+     10554    record (14 = 3 [4] + 10 + 1)
+     10568    record (14 = 3 [1] + 10 + 1)
+     10582    record (17 = 3 [2] + 13 + 1)
+     10599    record (14 = 3 [2] + 10 + 1)
+     10613    record (14 = 3 [2] + 10 + 1)
+     10627    record (14 = 3 [4] + 10 + 1)
+     10641    record (14 = 3 [2] + 10 + 1)
+     10655    record (13 = 3 [4] + 9 + 1)
+     10668    record (14 = 3 [2] + 9 + 2)
+     10682    record (19 = 3 [0] + 15 + 1) [restart]
+     10701    record (14 = 3 [2] + 9 + 2)
+     10715    record (16 = 3 [2] + 12 + 1)
+     10731    record (14 = 3 [6] + 10 + 1)
+     10745    record (13 = 3 [1] + 9 + 1)
+     10758    record (14 = 3 [2] + 10 + 1)
+     10772    record (15 = 3 [4] + 11 + 1)
+     10787    record (13 = 3 [4] + 9 + 1)
+     10800    record (14 = 3 [3] + 10 + 1)
+     10814    record (13 = 3 [3] + 9 + 1)
+     10827    record (16 = 3 [2] + 12 + 1)
+     10843    record (15 = 3 [3] + 11 + 1)
+     10858    record (16 = 3 [6] + 12 + 1)
+     10874    record (15 = 3 [2] + 11 + 1)
+     10889    record (18 = 3 [2] + 13 + 2)
+     10907    record (17 = 3 [3] + 13 + 1)
+     10924    record (20 = 3 [0] + 16 + 1) [restart]
+     10944    record (14 = 3 [2] + 10 + 1)
+     10958    record (13 = 3 [2] + 9 + 1)
+     10971    record (14 = 3 [2] + 10 + 1)
+     10985    record (14 = 3 [3] + 10 + 1)
+     10999    record (13 = 3 [2] + 9 + 1)
+     11012    record (18 = 3 [3] + 14 + 1)
+     11030    record (17 = 3 [1] + 13 + 1)
+     11047    record (17 = 3 [2] + 13 + 1)
+     11064    record (19 = 3 [2] + 15 + 1)
+     11083    record (19 = 3 [1] + 15 + 1)
+     11102    record (15 = 3 [0] + 9 + 3)
+     11117    record (14 = 3 [1] + 10 + 1)
+     11131    record (14 = 3 [1] + 9 + 2)
+     11145    record (20 = 3 [1] + 16 + 1)
+     11165    record (13 = 3 [1] + 9 + 1)
+     11178    record (15 = 3 [0] + 11 + 1) [restart]
+     11193    record (17 = 3 [1] + 13 + 1)
+     11210    record (16 = 3 [4] + 12 + 1)
+     11226    record (16 = 3 [1] + 12 + 1)
+     11242    record (19 = 3 [4] + 15 + 1)
+     11261    record (19 = 3 [2] + 15 + 1)
+     11280    record (17 = 3 [3] + 13 + 1)
+     11297    record (17 = 3 [3] + 13 + 1)
+     11314    record (16 = 3 [2] + 12 + 1)
+     11330    record (16 = 3 [6] + 12 + 1)
+     11346    record (17 = 3 [4] + 13 + 1)
+     11363    record (22 = 3 [3] + 18 + 1)
+     11385    record (15 = 3 [5] + 11 + 1)
+     11400    record (16 = 3 [3] + 12 + 1)
+     11416    record (19 = 3 [3] + 15 + 1)
+     11435    record (19 = 3 [4] + 15 + 1)
+     11454    record (21 = 3 [0] + 17 + 1) [restart]
+     11475    record (16 = 3 [6] + 12 + 1)
+     11491    record (15 = 3 [8] + 11 + 1)
+     11506    record (16 = 3 [4] + 12 + 1)
+     11522    record (16 = 3 [3] + 12 + 1)
+     11538    record (15 = 3 [1] + 9 + 3)
+     11553    record (16 = 3 [2] + 12 + 1)
+     11569    record (16 = 3 [6] + 12 + 1)
+     11585    record (18 = 3 [3] + 14 + 1)
+     11603    record (17 = 3 [3] + 13 + 1)
+     11620    record (16 = 3 [2] + 12 + 1)
+     11636    record (17 = 3 [2] + 13 + 1)
+     11653    record (17 = 3 [3] + 13 + 1)
+     11670    record (18 = 3 [3] + 14 + 1)
+     11688    record (15 = 3 [3] + 11 + 1)
+     11703    record (21 = 3 [2] + 17 + 1)
+     11724    record (16 = 3 [0] + 12 + 1) [restart]
+     11740    record (17 = 3 [2] + 13 + 1)
+     11757    record (20 = 3 [4] + 16 + 1)
+     11777    record (16 = 3 [2] + 12 + 1)
+     11793    record (13 = 3 [6] + 9 + 1)
+     11806    record (13 = 3 [3] + 9 + 1)
+     11819    record (15 = 3 [2] + 11 + 1)
+     11834    record (21 = 3 [2] + 17 + 1)
+     11855    record (16 = 3 [3] + 12 + 1)
+     11871    record (21 = 3 [3] + 17 + 1)
+     11892    record (16 = 3 [2] + 12 + 1)
+     11908    record (14 = 3 [1] + 9 + 2)
+     11922    record (15 = 3 [2] + 11 + 1)
+     11937    record (15 = 3 [1] + 9 + 3)
+     11952    record (13 = 3 [2] + 9 + 1)
+     11965    record (15 = 3 [3] + 11 + 1)
+     11980    record (14 = 3 [0] + 10 + 1) [restart]
+     11994    record (16 = 3 [0] + 12 + 1)
+     12010    record (16 = 3 [1] + 12 + 1)
+     12026    record (17 = 3 [1] + 13 + 1)
+     12043    record (15 = 3 [2] + 11 + 1)
+     12058    record (16 = 3 [5] + 12 + 1)
+     12074    record (13 = 3 [2] + 9 + 1)
+     12087    record (19 = 3 [1] + 15 + 1)
+     12106    record (15 = 3 [2] + 11 + 1)
+     12121    record (16 = 3 [2] + 12 + 1)
+     12137    record (14 = 3 [2] + 10 + 1)
+     12151    record (16 = 3 [0] + 12 + 1)
+     12167    record (13 = 3 [4] + 9 + 1)
+     12180    record (14 = 3 [2] + 10 + 1)
+     12194    record (16 = 3 [2] + 12 + 1)
+     12210    record (13 = 3 [2] + 9 + 1)
+     12223    [restart 10217]
+     12227    [restart 10447]
+     12231    [restart 10682]
+     12235    [restart 10924]
+     12239    [restart 11178]
+     12243    [restart 11454]
+     12247    [restart 11724]
+     12251    [restart 11980]
+     12264  data (2039)
+     12264    record (15 = 3 [0] + 11 + 1) [restart]
+     12279    record (13 = 3 [3] + 9 + 1)
+     12292    record (14 = 3 [3] + 9 + 2)
+     12306    record (15 = 3 [4] + 11 + 1)
+     12321    record (16 = 3 [1] + 12 + 1)
+     12337    record (14 = 3 [2] + 10 + 1)
+     12351    record (17 = 3 [2] + 13 + 1)
+     12368    record (14 = 3 [3] + 9 + 2)
+     12382    record (13 = 3 [4] + 9 + 1)
+     12395    record (13 = 3 [4] + 9 + 1)
+     12408    record (20 = 3 [0] + 16 + 1)
+     12428    record (16 = 3 [6] + 12 + 1)
+     12444    record (14 = 3 [2] + 10 + 1)
+     12458    record (13 = 3 [4] + 9 + 1)
+     12471    record (18 = 3 [2] + 13 + 2)
+     12489    record (14 = 3 [2] + 10 + 1)
+     12503    record (17 = 3 [0] + 13 + 1) [restart]
+     12520    record (16 = 3 [2] + 12 + 1)
+     12536    record (14 = 3 [2] + 10 + 1)
+     12550    record (15 = 3 [4] + 11 + 1)
+     12565    record (14 = 3 [2] + 10 + 1)
+     12579    record (13 = 3 [2] + 9 + 1)
+     12592    record (16 = 3 [3] + 12 + 1)
+     12608    record (13 = 3 [2] + 9 + 1)
+     12621    record (15 = 3 [2] + 11 + 1)
+     12636    record (15 = 3 [1] + 11 + 1)
+     12651    record (14 = 3 [3] + 10 + 1)
+     12665    record (14 = 3 [3] + 10 + 1)
+     12679    record (14 = 3 [5] + 10 + 1)
+     12693    record (14 = 3 [2] + 10 + 1)
+     12707    record (17 = 3 [2] + 13 + 1)
+     12724    record (14 = 3 [2] + 10 + 1)
+     12738    record (18 = 3 [0] + 14 + 1) [restart]
+     12756    record (13 = 3 [4] + 9 + 1)
+     12769    record (15 = 3 [3] + 11 + 1)
+     12784    record (18 = 3 [2] + 14 + 1)
+     12802    record (14 = 3 [2] + 10 + 1)
+     12816    record (14 = 3 [4] + 10 + 1)
+     12830    record (14 = 3 [2] + 9 + 2)
+     12844    record (14 = 3 [3] + 10 + 1)
+     12858    record (13 = 3 [3] + 9 + 1)
+     12871    record (16 = 3 [2] + 12 + 1)
+     12887    record (18 = 3 [2] + 14 + 1)
+     12905    record (20 = 3 [1] + 16 + 1)
+     12925    record (14 = 3 [2] + 10 + 1)
+     12939    record (18 = 3 [2] + 14 + 1)
+     12957    record (13 = 3 [3] + 9 + 1)
+     12970    record (14 = 3 [2] + 10 + 1)
+     12984    record (18 = 3 [0] + 14 + 1) [restart]
+     13002    record (15 = 3 [2] + 11 + 1)
+     13017    record (15 = 3 [5] + 11 + 1)
+     13032    record (15 = 3 [2] + 10 + 2)
+     13047    record (14 = 3 [2] + 10 + 1)
+     13061    record (14 = 3 [2] + 10 + 1)
+     13075    record (14 = 3 [2] + 10 + 1)
+     13089    record (16 = 3 [2] + 12 + 1)
+     13105    record (14 = 3 [2] + 10 + 1)
+     13119    record (13 = 3 [4] + 9 + 1)
+     13132    record (16 = 3 [2] + 12 + 1)
+     13148    record (14 = 3 [2] + 10 + 1)
+     13162    record (14 = 3 [4] + 10 + 1)
+     13176    record (13 = 3 [4] + 9 + 1)
+     13189    record (14 = 3 [1] + 9 + 2)
+     13203    record (13 = 3 [1] + 9 + 1)
+     13216    record (16 = 3 [0] + 12 + 1) [restart]
+     13232    record (18 = 3 [3] + 14 + 1)
+     13250    record (14 = 3 [2] + 10 + 1)
+     13264    record (13 = 3 [4] + 9 + 1)
+     13277    record (15 = 3 [2] + 11 + 1)
+     13292    record (15 = 3 [2] + 11 + 1)
+     13307    record (14 = 3 [2] + 10 + 1)
+     13321    record (14 = 3 [4] + 10 + 1)
+     13335    record (15 = 3 [2] + 10 + 2)
+     13350    record (13 = 3 [4] + 9 + 1)
+     13363    record (14 = 3 [3] + 10 + 1)
+     13377    record (15 = 3 [2] + 10 + 2)
+     13392    record (13 = 3 [4] + 9 + 1)
+     13405    record (15 = 3 [5] + 11 + 1)
+     13420    record (14 = 3 [2] + 10 + 1)
+     13434    record (13 = 3 [4] + 9 + 1)
+     13447    record (16 = 3 [0] + 12 + 1) [restart]
+     13463    record (13 = 3 [3] + 9 + 1)
+     13476    record (14 = 3 [2] + 10 + 1)
+     13490    record (14 = 3 [2] + 10 + 1)
+     13504    record (13 = 3 [4] + 9 + 1)
+     13517    record (15 = 3 [3] + 11 + 1)
+     13532    record (15 = 3 [1] + 11 + 1)
+     13547    record (16 = 3 [2] + 12 + 1)
+     13563    record (13 = 3 [0] + 9 + 1)
+     13576    record (16 = 3 [1] + 12 + 1)
+     13592    record (13 = 3 [3] + 9 + 1)
+     13605    record (16 = 3 [3] + 12 + 1)
+     13621    record (14 = 3 [2] + 10 + 1)
+     13635    record (14 = 3 [4] + 10 + 1)
+     13649    record (13 = 3 [3] + 9 + 1)
+     13662    record (20 = 3 [2] + 16 + 1)
+     13682    record (19 = 3 [0] + 15 + 1) [restart]
+     13701    record (14 = 3 [2] + 10 + 1)
+     13715    record (13 = 3 [4] + 9 + 1)
+     13728    record (15 = 3 [3] + 11 + 1)
+     13743    record (19 = 3 [2] + 15 + 1)
+     13762    record (14 = 3 [2] + 9 + 2)
+     13776    record (15 = 3 [3] + 11 + 1)
+     13791    record (13 = 3 [6] + 9 + 1)
+     13804    record (15 = 3 [3] + 11 + 1)
+     13819    record (13 = 3 [3] + 9 + 1)
+     13832    record (16 = 3 [2] + 12 + 1)
+     13848    record (19 = 3 [3] + 14 + 2)
+     13867    record (13 = 3 [4] + 9 + 1)
+     13880    record (13 = 3 [3] + 9 + 1)
+     13893    record (17 = 3 [3] + 13 + 1)
+     13910    record (14 = 3 [5] + 10 + 1)
+     13924    record (18 = 3 [0] + 14 + 1) [restart]
+     13942    record (13 = 3 [4] + 9 + 1)
+     13955    record (13 = 3 [3] + 9 + 1)
+     13968    record (15 = 3 [4] + 11 + 1)
+     13983    record (15 = 3 [3] + 11 + 1)
+     13998    record (15 = 3 [2] + 11 + 1)
+     14013    record (15 = 3 [3] + 11 + 1)
+     14028    record (14 = 3 [2] + 9 + 2)
+     14042    record (14 = 3 [1] + 9 + 2)
+     14056    record (14 = 3 [2] + 10 + 1)
+     14070    record (13 = 3 [4] + 9 + 1)
+     14083    record (14 = 3 [3] + 10 + 1)
+     14097    record (20 = 3 [2] + 16 + 1)
+     14117    record (14 = 3 [2] + 10 + 1)
+     14131    record (15 = 3 [4] + 11 + 1)
+     14146    record (14 = 3 [2] + 10 + 1)
+     14160    record (18 = 3 [0] + 14 + 1) [restart]
+     14178    record (13 = 3 [2] + 9 + 1)
+     14191    record (15 = 3 [2] + 11 + 1)
+     14206    record (13 = 3 [3] + 9 + 1)
+     14219    record (14 = 3 [4] + 10 + 1)
+     14233    record (17 = 3 [2] + 13 + 1)
+     14250    record (13 = 3 [2] + 9 + 1)
+     14263    [restart 12264]
+     14267    [restart 12503]
+     14271    [restart 12738]
+     14275    [restart 12984]
+     14279    [restart 13216]
+     14283    [restart 13447]
+     14287    [restart 13682]
+     14291    [restart 13924]
+     14295    [restart 14160]
+     14308  data (2037)
+     14308    record (20 = 3 [0] + 16 + 1) [restart]
+     14328    record (17 = 3 [4] + 13 + 1)
+     14345    record (15 = 3 [3] + 11 + 1)
+     14360    record (17 = 3 [1] + 13 + 1)
+     14377    record (15 = 3 [2] + 11 + 1)
+     14392    record (16 = 3 [5] + 12 + 1)
+     14408    record (14 = 3 [2] + 10 + 1)
+     14422    record (14 = 3 [2] + 10 + 1)
+     14436    record (13 = 3 [3] + 9 + 1)
+     14449    record (18 = 3 [3] + 14 + 1)
+     14467    record (15 = 3 [3] + 11 + 1)
+     14482    record (13 = 3 [6] + 9 + 1)
+     14495    record (15 = 3 [2] + 11 + 1)
+     14510    record (15 = 3 [1] + 11 + 1)
+     14525    record (15 = 3 [4] + 11 + 1)
+     14540    record (18 = 3 [2] + 14 + 1)
+     14558    record (18 = 3 [0] + 14 + 1) [restart]
+     14576    record (14 = 3 [3] + 10 + 1)
+     14590    record (14 = 3 [2] + 10 + 1)
+     14604    record (16 = 3 [2] + 12 + 1)
+     14620    record (15 = 3 [2] + 11 + 1)
+     14635    record (13 = 3 [5] + 9 + 1)
+     14648    record (15 = 3 [2] + 11 + 1)
+     14663    record (13 = 3 [3] + 9 + 1)
+     14676    record (15 = 3 [2] + 10 + 2)
+     14691    record (13 = 3 [3] + 9 + 1)
+     14704    record (15 = 3 [4] + 11 + 1)
+     14719    record (15 = 3 [2] + 10 + 2)
+     14734    record (14 = 3 [2] + 10 + 1)
+     14748    record (15 = 3 [3] + 11 + 1)
+     14763    record (15 = 3 [3] + 11 + 1)
+     14778    record (14 = 3 [4] + 10 + 1)
+     14792    record (17 = 3 [0] + 13 + 1) [restart]
+     14809    record (15 = 3 [5] + 11 + 1)
+     14824    record (14 = 3 [3] + 10 + 1)
+     14838    record (14 = 3 [3] + 10 + 1)
+     14852    record (15 = 3 [2] + 11 + 1)
+     14867    record (15 = 3 [1] + 11 + 1)
+     14882    record (16 = 3 [2] + 12 + 1)
+     14898    record (15 = 3 [2] + 10 + 2)
+     14913    record (15 = 3 [1] + 9 + 3)
+     14928    record (16 = 3 [2] + 12 + 1)
+     14944    record (16 = 3 [0] + 12 + 1)
+     14960    record (17 = 3 [2] + 13 + 1)
+     14977    record (14 = 3 [4] + 10 + 1)
+     14991    record (16 = 3 [3] + 12 + 1)
+     15007    record (14 = 3 [5] + 9 + 2)
+     15021    record (13 = 3 [2] + 9 + 1)
+     15034    record (14 = 3 [0] + 10 + 1) [restart]
+     15048    record (14 = 3 [2] + 10 + 1)
+     15062    record (21 = 3 [2] + 17 + 1)
+     15083    record (14 = 3 [2] + 10 + 1)
+     15097    record (15 = 3 [4] + 11 + 1)
+     15112    record (13 = 3 [4] + 9 + 1)
+     15125    record (17 = 3 [2] + 13 + 1)
+     15142    record (16 = 3 [2] + 12 + 1)
+     15158    record (16 = 3 [2] + 12 + 1)
+     15174    record (16 = 3 [3] + 12 + 1)
+     15190    record (15 = 3 [2] + 11 + 1)
+     15205    record (15 = 3 [2] + 11 + 1)
+     15220    record (13 = 3 [2] + 9 + 1)
+     15233    record (13 = 3 [3] + 9 + 1)
+     15246    record (17 = 3 [1] + 12 + 2)
+     15263    record (14 = 3 [5] + 10 + 1)
+     15277    record (19 = 3 [0] + 15 + 1) [restart]
+     15296    record (13 = 3 [5] + 9 + 1)
+     15309    record (15 = 3 [2] + 11 + 1)
+     15324    record (17 = 3 [2] + 13 + 1)
+     15341    record (14 = 3 [1] + 9 + 2)
+     15355    record (18 = 3 [2] + 14 + 1)
+     15373    record (14 = 3 [3] + 10 + 1)
+     15387    record (14 = 3 [2] + 10 + 1)
+     15401    record (14 = 3 [2] + 9 + 2)
+     15415    record (15 = 3 [3] + 11 + 1)
+     15430    record (14 = 3 [2] + 9 + 2)
+     15444    record (13 = 3 [3] + 9 + 1)
+     15457    record (16 = 3 [3] + 12 + 1)
+     15473    record (14 = 3 [2] + 9 + 2)
+     15487    record (14 = 3 [0] + 9 + 2)
+     15501    record (15 = 3 [1] + 11 + 1)
+     15516    record (16 = 3 [0] + 12 + 1) [restart]
+     15532    record (16 = 3 [2] + 12 + 1)
+     15548    record (20 = 3 [2] + 16 + 1)
+     15568    record (20 = 3 [2] + 16 + 1)
+     15588    record (18 = 3 [4] + 14 + 1)
+     15606    record (13 = 3 [8] + 9 + 1)
+     15619    record (16 = 3 [7] + 12 + 1)
+     15635    record (18 = 3 [3] + 14 + 1)
+     15653    record (19 = 3 [1] + 15 + 1)
+     15672    record (14 = 3 [1] + 10 + 1)
+     15686    record (15 = 3 [1] + 9 + 3)
+     15701    record (13 = 3 [2] + 9 + 1)
+     15714    record (16 = 3 [3] + 12 + 1)
+     15730    record (13 = 3 [5] + 9 + 1)
+     15743    record (14 = 3 [6] + 10 + 1)
+     15757    record (13 = 3 [4] + 9 + 1)
+     15770    record (15 = 3 [0] + 11 + 1) [restart]
+     15785    record (14 = 3 [1] + 10 + 1)
+     15799    record (15 = 3 [1] + 11 + 1)
+     15814    record (14 = 3 [1] + 9 + 2)
+     15828    record (14 = 3 [2] + 10 + 1)
+     15842    record (13 = 3 [2] + 9 + 1)
+     15855    record (15 = 3 [1] + 11 + 1)
+     15870    record (13 = 3 [3] + 9 + 1)
+     15883    record (18 = 3 [2] + 13 + 2)
+     15901    record (17 = 3 [2] + 13 + 1)
+     15918    record (17 = 3 [2] + 13 + 1)
+     15935    record (17 = 3 [5] + 13 + 1)
+     15952    record (16 = 3 [3] + 12 + 1)
+     15968    record (14 = 3 [1] + 9 + 2)
+     15982    record (17 = 3 [2] + 13 + 1)
+     15999    record (18 = 3 [2] + 14 + 1)
+     16017    record (18 = 3 [0] + 14 + 1) [restart]
+     16035    record (16 = 3 [1] + 12 + 1)
+     16051    record (15 = 3 [1] + 10 + 2)
+     16066    record (16 = 3 [3] + 12 + 1)
+     16082    record (15 = 3 [6] + 11 + 1)
+     16097    record (13 = 3 [2] + 9 + 1)
+     16110    record (14 = 3 [1] + 10 + 1)
+     16124    record (16 = 3 [3] + 12 + 1)
+     16140    record (16 = 3 [0] + 12 + 1)
+     16156    record (13 = 3 [4] + 9 + 1)
+     16169    record (13 = 3 [3] + 9 + 1)
+     16182    record (13 = 3 [4] + 9 + 1)
+     16195    record (16 = 3 [2] + 12 + 1)
+     16211    record (14 = 3 [3] + 10 + 1)
+     16225    record (13 = 3 [5] + 9 + 1)
+     16238    record (13 = 3 [3] + 9 + 1)
+     16251    record (22 = 3 [0] + 18 + 1) [restart]
+     16273    record (15 = 3 [5] + 11 + 1)
+     16288    record (17 = 3 [2] + 13 + 1)
+     16305    [restart 14308]
+     16309    [restart 14558]
+     16313    [restart 14792]
+     16317    [restart 15034]
+     16321    [restart 15277]
+     16325    [restart 15516]
+     16329    [restart 15770]
+     16333    [restart 16017]
+     16337    [restart 16251]
+     16350  data (2029)
+     16350    record (19 = 3 [0] + 15 + 1) [restart]
+     16369    record (13 = 3 [3] + 9 + 1)
+     16382    record (15 = 3 [4] + 11 + 1)
+     16397    record (14 = 3 [2] + 10 + 1)
+     16411    record (16 = 3 [3] + 12 + 1)
+     16427    record (13 = 3 [2] + 9 + 1)
+     16440    record (13 = 3 [1] + 9 + 1)
+     16453    record (15 = 3 [2] + 11 + 1)
+     16468    record (17 = 3 [2] + 13 + 1)
+     16485    record (19 = 3 [2] + 15 + 1)
+     16504    record (16 = 3 [3] + 12 + 1)
+     16520    record (15 = 3 [4] + 11 + 1)
+     16535    record (16 = 3 [3] + 12 + 1)
+     16551    record (17 = 3 [3] + 13 + 1)
+     16568    record (18 = 3 [3] + 14 + 1)
+     16586    record (19 = 3 [3] + 15 + 1)
+     16605    record (20 = 3 [0] + 16 + 1) [restart]
+     16625    record (14 = 3 [4] + 10 + 1)
+     16639    record (14 = 3 [6] + 10 + 1)
+     16653    record (13 = 3 [6] + 9 + 1)
+     16666    record (18 = 3 [3] + 14 + 1)
+     16684    record (16 = 3 [2] + 12 + 1)
+     16700    record (18 = 3 [2] + 14 + 1)
+     16718    record (14 = 3 [3] + 10 + 1)
+     16732    record (21 = 3 [1] + 17 + 1)
+     16753    record (16 = 3 [2] + 12 + 1)
+     16769    record (16 = 3 [1] + 12 + 1)
+     16785    record (13 = 3 [2] + 9 + 1)
+     16798    record (16 = 3 [2] + 12 + 1)
+     16814    record (14 = 3 [3] + 10 + 1)
+     16828    record (14 = 3 [2] + 10 + 1)
+     16842    record (13 = 3 [3] + 9 + 1)
+     16855    record (17 = 3 [0] + 13 + 1) [restart]
+     16872    record (14 = 3 [3] + 10 + 1)
+     16886    record (16 = 3 [3] + 12 + 1)
+     16902    record (17 = 3 [3] + 13 + 1)
+     16919    record (17 = 3 [3] + 13 + 1)
+     16936    record (13 = 3 [3] + 9 + 1)
+     16949    record (16 = 3 [2] + 12 + 1)
+     16965    record (15 = 3 [3] + 11 + 1)
+     16980    record (16 = 3 [1] + 12 + 1)
+     16996    record (17 = 3 [2] + 13 + 1)
+     17013    record (13 = 3 [3] + 9 + 1)
+     17026    record (18 = 3 [3] + 13 + 2)
+     17044    record (19 = 3 [2] + 15 + 1)
+     17063    record (14 = 3 [2] + 10 + 1)
+     17077    record (13 = 3 [3] + 9 + 1)
+     17090    record (17 = 3 [2] + 13 + 1)
+     17107    record (22 = 3 [0] + 18 + 1) [restart]
+     17129    record (19 = 3 [3] + 15 + 1)
+     17148    record (17 = 3 [2] + 13 + 1)
+     17165    record (13 = 3 [5] + 9 + 1)
+     17178    record (13 = 3 [3] + 9 + 1)
+     17191    record (14 = 3 [2] + 10 + 1)
+     17205    record (15 = 3 [2] + 11 + 1)
+     17220    record (15 = 3 [1] + 11 + 1)
+     17235    record (15 = 3 [4] + 11 + 1)
+     17250    record (19 = 3 [2] + 15 + 1)
+     17269    record (15 = 3 [5] + 11 + 1)
+     17284    record (16 = 3 [4] + 12 + 1)
+     17300    record (21 = 3 [3] + 17 + 1)
+     17321    record (17 = 3 [3] + 13 + 1)
+     17338    record (13 = 3 [6] + 9 + 1)
+     17351    record (17 = 3 [4] + 13 + 1)
+     17368    record (16 = 3 [0] + 12 + 1) [restart]
+     17384    record (15 = 3 [2] + 11 + 1)
+     17399    record (14 = 3 [3] + 10 + 1)
+     17413    record (17 = 3 [3] + 13 + 1)
+     17430    record (13 = 3 [4] + 9 + 1)
+     17443    record (15 = 3 [3] + 11 + 1)
+     17458    record (15 = 3 [3] + 11 + 1)
+     17473    record (16 = 3 [3] + 12 + 1)
+     17489    record (13 = 3 [4] + 9 + 1)
+     17502    record (19 = 3 [2] + 15 + 1)
+     17521    record (16 = 3 [3] + 12 + 1)
+     17537    record (17 = 3 [4] + 13 + 1)
+     17554    record (17 = 3 [3] + 13 + 1)
+     17571    record (17 = 3 [3] + 13 + 1)
+     17588    record (16 = 3 [3] + 12 + 1)
+     17604    record (20 = 3 [3] + 16 + 1)
+     17624    record (21 = 3 [0] + 17 + 1) [restart]
+     17645    record (19 = 3 [4] + 15 + 1)
+     17664    record (14 = 3 [5] + 10 + 1)
+     17678    record (15 = 3 [1] + 11 + 1)
+     17693    record (14 = 3 [2] + 10 + 1)
+     17707    record (15 = 3 [3] + 11 + 1)
+     17722    record (16 = 3 [3] + 12 + 1)
+     17738    record (14 = 3 [3] + 10 + 1)
+     17752    record (16 = 3 [4] + 12 + 1)
+     17768    record (13 = 3 [2] + 9 + 1)
+     17781    record (13 = 3 [3] + 9 + 1)
+     17794    record (19 = 3 [0] + 15 + 1)
+     17813    record (15 = 3 [2] + 11 + 1)
+     17828    record (17 = 3 [3] + 13 + 1)
+     17845    record (16 = 3 [8] + 12 + 1)
+     17861    record (21 = 3 [2] + 17 + 1)
+     17882    record (17 = 3 [0] + 13 + 1) [restart]
+     17899    record (14 = 3 [5] + 10 + 1)
+     17913    record (15 = 3 [3] + 11 + 1)
+     17928    record (19 = 3 [0] + 15 + 1)
+     17947    record (14 = 3 [2] + 10 + 1)
+     17961    record (14 = 3 [4] + 10 + 1)
+     17975    record (14 = 3 [2] + 10 + 1)
+     17989    record (17 = 3 [3] + 13 + 1)
+     18006    record (13 = 3 [1] + 9 + 1)
+     18019    record (17 = 3 [2] + 13 + 1)
+     18036    record (13 = 3 [3] + 9 + 1)
+     18049    record (15 = 3 [3] + 11 + 1)
+     18064    record (16 = 3 [2] + 12 + 1)
+     18080    record (18 = 3 [2] + 14 + 1)
+     18098    record (17 = 3 [4] + 13 + 1)
+     18115    record (13 = 3 [4] + 9 + 1)
+     18128    record (19 = 3 [0] + 15 + 1) [restart]
+     18147    record (15 = 3 [4] + 11 + 1)
+     18162    record (13 = 3 [2] + 9 + 1)
+     18175    record (13 = 3 [3] + 9 + 1)
+     18188    record (15 = 3 [2] + 11 + 1)
+     18203    record (16 = 3 [2] + 12 + 1)
+     18219    record (15 = 3 [5] + 11 + 1)
+     18234    record (16 = 3 [2] + 12 + 1)
+     18250    record (17 = 3 [3] + 13 + 1)
+     18267    record (17 = 3 [6] + 13 + 1)
+     18284    record (15 = 3 [3] + 11 + 1)
+     18299    record (13 = 3 [6] + 9 + 1)
+     18312    record (16 = 3 [2] + 12 + 1)
+     18328    record (15 = 3 [2] + 11 + 1)
+     18343    [restart 16350]
+     18347    [restart 16605]
+     18351    [restart 16855]
+     18355    [restart 17107]
+     18359    [restart 17368]
+     18363    [restart 17624]
+     18367    [restart 17882]
+     18371    [restart 18128]
+     18384  data (2040)
+     18384    record (18 = 3 [0] + 14 + 1) [restart]
+     18402    record (17 = 3 [2] + 13 + 1)
+     18419    record (15 = 3 [4] + 11 + 1)
+     18434    record (17 = 3 [2] + 13 + 1)
+     18451    record (18 = 3 [3] + 14 + 1)
+     18469    record (14 = 3 [5] + 10 + 1)
+     18483    record (13 = 3 [3] + 9 + 1)
+     18496    record (20 = 3 [2] + 16 + 1)
+     18516    record (15 = 3 [3] + 11 + 1)
+     18531    record (16 = 3 [2] + 12 + 1)
+     18547    record (13 = 3 [4] + 9 + 1)
+     18560    record (15 = 3 [4] + 11 + 1)
+     18575    record (16 = 3 [3] + 12 + 1)
+     18591    record (18 = 3 [1] + 14 + 1)
+     18609    record (15 = 3 [1] + 11 + 1)
+     18624    record (13 = 3 [2] + 9 + 1)
+     18637    record (17 = 3 [0] + 13 + 1) [restart]
+     18654    record (14 = 3 [2] + 10 + 1)
+     18668    record (16 = 3 [2] + 12 + 1)
+     18684    record (14 = 3 [3] + 10 + 1)
+     18698    record (15 = 3 [1] + 11 + 1)
+     18713    record (16 = 3 [2] + 12 + 1)
+     18729    record (13 = 3 [4] + 9 + 1)
+     18742    record (13 = 3 [3] + 9 + 1)
+     18755    record (14 = 3 [2] + 10 + 1)
+     18769    record (14 = 3 [3] + 10 + 1)
+     18783    record (16 = 3 [2] + 12 + 1)
+     18799    record (17 = 3 [2] + 13 + 1)
+     18816    record (14 = 3 [3] + 10 + 1)
+     18830    record (15 = 3 [2] + 11 + 1)
+     18845    record (16 = 3 [1] + 12 + 1)
+     18861    record (17 = 3 [2] + 13 + 1)
+     18878    record (18 = 3 [0] + 14 + 1) [restart]
+     18896    record (14 = 3 [0] + 9 + 2)
+     18910    record (16 = 3 [1] + 12 + 1)
+     18926    record (16 = 3 [2] + 12 + 1)
+     18942    record (14 = 3 [2] + 10 + 1)
+     18956    record (13 = 3 [3] + 9 + 1)
+     18969    record (14 = 3 [3] + 10 + 1)
+     18983    record (14 = 3 [2] + 10 + 1)
+     18997    record (14 = 3 [2] + 10 + 1)
+     19011    record (20 = 3 [2] + 16 + 1)
+     19031    record (14 = 3 [2] + 10 + 1)
+     19045    record (14 = 3 [3] + 10 + 1)
+     19059    record (17 = 3 [2] + 13 + 1)
+     19076    record (13 = 3 [2] + 9 + 1)
+     19089    record (13 = 3 [3] + 9 + 1)
+     19102    record (14 = 3 [2] + 9 + 2)
+     19116    record (18 = 3 [0] + 14 + 1) [restart]
+     19134    record (13 = 3 [3] + 9 + 1)
+     19147    record (16 = 3 [1] + 12 + 1)
+     19163    record (16 = 3 [3] + 12 + 1)
+     19179    record (15 = 3 [4] + 11 + 1)
+     19194    record (15 = 3 [3] + 11 + 1)
+     19209    record (17 = 3 [3] + 13 + 1)
+     19226    record (15 = 3 [2] + 11 + 1)
+     19241    record (13 = 3 [4] + 9 + 1)
+     19254    record (17 = 3 [2] + 13 + 1)
+     19271    record (13 = 3 [7] + 9 + 1)
+     19284    record (14 = 3 [4] + 10 + 1)
+     19298    record (15 = 3 [2] + 11 + 1)
+     19313    record (14 = 3 [1] + 10 + 1)
+     19327    record (13 = 3 [3] + 9 + 1)
+     19340    record (15 = 3 [3] + 11 + 1)
+     19355    record (16 = 3 [0] + 12 + 1) [restart]
+     19371    record (16 = 3 [2] + 12 + 1)
+     19387    record (16 = 3 [3] + 12 + 1)
+     19403    record (13 = 3 [5] + 9 + 1)
+     19416    record (13 = 3 [6] + 9 + 1)
+     19429    record (15 = 3 [3] + 11 + 1)
+     19444    record (16 = 3 [2] + 12 + 1)
+     19460    record (13 = 3 [2] + 9 + 1)
+     19473    record (13 = 3 [3] + 9 + 1)
+     19486    record (15 = 3 [3] + 11 + 1)
+     19501    record (13 = 3 [3] + 9 + 1)
+     19514    record (13 = 3 [3] + 9 + 1)
+     19527    record (15 = 3 [4] + 11 + 1)
+     19542    record (13 = 3 [4] + 9 + 1)
+     19555    record (13 = 3 [3] + 9 + 1)
+     19568    record (16 = 3 [2] + 12 + 1)
+     19584    record (18 = 3 [0] + 14 + 1) [restart]
+     19602    record (13 = 3 [3] + 9 + 1)
+     19615    record (15 = 3 [2] + 11 + 1)
+     19630    record (16 = 3 [4] + 12 + 1)
+     19646    record (13 = 3 [3] + 9 + 1)
+     19659    record (19 = 3 [2] + 15 + 1)
+     19678    record (17 = 3 [2] + 13 + 1)
+     19695    record (16 = 3 [3] + 12 + 1)
+     19711    record (16 = 3 [3] + 12 + 1)
+     19727    record (13 = 3 [7] + 9 + 1)
+     19740    record (15 = 3 [4] + 11 + 1)
+     19755    record (13 = 3 [2] + 9 + 1)
+     19768    record (16 = 3 [1] + 12 + 1)
+     19784    record (15 = 3 [3] + 10 + 2)
+     19799    record (13 = 3 [4] + 9 + 1)
+     19812    record (14 = 3 [3] + 10 + 1)
+     19826    record (20 = 3 [0] + 16 + 1) [restart]
+     19846    record (14 = 3 [3] + 10 + 1)
+     19860    record (13 = 3 [5] + 9 + 1)
+     19873    record (14 = 3 [3] + 10 + 1)
+     19887    record (13 = 3 [2] + 9 + 1)
+     19900    record (16 = 3 [3] + 12 + 1)
+     19916    record (13 = 3 [5] + 9 + 1)
+     19929    record (15 = 3 [2] + 11 + 1)
+     19944    record (20 = 3 [3] + 16 + 1)
+     19964    record (15 = 3 [2] + 11 + 1)
+     19979    record (13 = 3 [3] + 9 + 1)
+     19992    record (15 = 3 [3] + 11 + 1)
+     20007    record (14 = 3 [6] + 10 + 1)
+     20021    record (14 = 3 [6] + 10 + 1)
+     20035    record (13 = 3 [3] + 9 + 1)
+     20048    record (13 = 3 [4] + 9 + 1)
+     20061    record (20 = 3 [0] + 16 + 1) [restart]
+     20081    record (15 = 3 [3] + 11 + 1)
+     20096    record (15 = 3 [3] + 11 + 1)
+     20111    record (15 = 3 [1] + 11 + 1)
+     20126    record (14 = 3 [2] + 10 + 1)
+     20140    record (15 = 3 [2] + 11 + 1)
+     20155    record (17 = 3 [2] + 13 + 1)
+     20172    record (15 = 3 [3] + 11 + 1)
+     20187    record (16 = 3 [2] + 12 + 1)
+     20203    record (13 = 3 [2] + 9 + 1)
+     20216    record (14 = 3 [3] + 10 + 1)
+     20230    record (15 = 3 [3] + 11 + 1)
+     20245    record (16 = 3 [3] + 12 + 1)
+     20261    record (13 = 3 [2] + 9 + 1)
+     20274    record (13 = 3 [3] + 9 + 1)
+     20287    record (16 = 3 [2] + 12 + 1)
+     20303    record (15 = 3 [0] + 11 + 1) [restart]
+     20318    record (13 = 3 [3] + 9 + 1)
+     20331    record (17 = 3 [1] + 13 + 1)
+     20348    record (18 = 3 [1] + 14 + 1)
+     20366    record (18 = 3 [3] + 14 + 1)
+     20384    [restart 18384]
+     20388    [restart 18637]
+     20392    [restart 18878]
+     20396    [restart 19116]
+     20400    [restart 19355]
+     20404    [restart 19584]
+     20408    [restart 19826]
+     20412    [restart 20061]
+     20416    [restart 20303]
+     20429  data (2030)
+     20429    record (16 = 3 [0] + 12 + 1) [restart]
+     20445    record (17 = 3 [2] + 13 + 1)
+     20462    record (14 = 3 [3] + 10 + 1)
+     20476    record (15 = 3 [5] + 11 + 1)
+     20491    record (14 = 3 [2] + 10 + 1)
+     20505    record (16 = 3 [1] + 12 + 1)
+     20521    record (13 = 3 [5] + 9 + 1)
+     20534    record (15 = 3 [4] + 11 + 1)
+     20549    record (16 = 3 [2] + 12 + 1)
+     20565    record (14 = 3 [3] + 10 + 1)
+     20579    record (14 = 3 [1] + 9 + 2)
+     20593    record (13 = 3 [2] + 9 + 1)
+     20606    record (14 = 3 [2] + 10 + 1)
+     20620    record (14 = 3 [2] + 10 + 1)
+     20634    record (17 = 3 [2] + 13 + 1)
+     20651    record (13 = 3 [7] + 9 + 1)
+     20664    record (18 = 3 [0] + 14 + 1) [restart]
+     20682    record (14 = 3 [3] + 10 + 1)
+     20696    record (15 = 3 [2] + 10 + 2)
+     20711    record (17 = 3 [4] + 13 + 1)
+     20728    record (15 = 3 [5] + 11 + 1)
+     20743    record (13 = 3 [8] + 9 + 1)
+     20756    record (16 = 3 [4] + 12 + 1)
+     20772    record (13 = 3 [2] + 9 + 1)
+     20785    record (14 = 3 [3] + 10 + 1)
+     20799    record (14 = 3 [2] + 10 + 1)
+     20813    record (15 = 3 [3] + 11 + 1)
+     20828    record (13 = 3 [4] + 9 + 1)
+     20841    record (13 = 3 [3] + 9 + 1)
+     20854    record (14 = 3 [2] + 10 + 1)
+     20868    record (13 = 3 [4] + 9 + 1)
+     20881    record (14 = 3 [3] + 10 + 1)
+     20895    record (20 = 3 [0] + 16 + 1) [restart]
+     20915    record (15 = 3 [3] + 11 + 1)
+     20930    record (21 = 3 [2] + 17 + 1)
+     20951    record (17 = 3 [1] + 12 + 2)
+     20968    record (15 = 3 [5] + 11 + 1)
+     20983    record (15 = 3 [3] + 11 + 1)
+     20998    record (13 = 3 [4] + 9 + 1)
+     21011    record (14 = 3 [3] + 10 + 1)
+     21025    record (17 = 3 [2] + 13 + 1)
+     21042    record (16 = 3 [2] + 12 + 1)
+     21058    record (13 = 3 [6] + 9 + 1)
+     21071    record (14 = 3 [3] + 10 + 1)
+     21085    record (15 = 3 [2] + 11 + 1)
+     21100    record (16 = 3 [2] + 12 + 1)
+     21116    record (14 = 3 [6] + 10 + 1)
+     21130    record (17 = 3 [1] + 13 + 1)
+     21147    record (14 = 3 [0] + 10 + 1) [restart]
+     21161    record (15 = 3 [2] + 11 + 1)
+     21176    record (13 = 3 [4] + 9 + 1)
+     21189    record (13 = 3 [5] + 9 + 1)
+     21202    record (14 = 3 [3] + 10 + 1)
+     21216    record (14 = 3 [3] + 10 + 1)
+     21230    record (13 = 3 [5] + 9 + 1)
+     21243    record (13 = 3 [3] + 9 + 1)
+     21256    record (13 = 3 [4] + 9 + 1)
+     21269    record (13 = 3 [4] + 9 + 1)
+     21282    record (14 = 3 [5] + 10 + 1)
+     21296    record (14 = 3 [3] + 10 + 1)
+     21310    record (14 = 3 [5] + 10 + 1)
+     21324    record (15 = 3 [4] + 11 + 1)
+     21339    record (13 = 3 [3] + 9 + 1)
+     21352    record (15 = 3 [2] + 11 + 1)
+     21367    record (17 = 3 [0] + 13 + 1) [restart]
+     21384    record (17 = 3 [3] + 13 + 1)
+     21401    record (17 = 3 [2] + 13 + 1)
+     21418    record (14 = 3 [3] + 10 + 1)
+     21432    record (14 = 3 [3] + 10 + 1)
+     21446    record (13 = 3 [3] + 9 + 1)
+     21459    record (16 = 3 [4] + 12 + 1)
+     21475    record (15 = 3 [2] + 11 + 1)
+     21490    record (16 = 3 [3] + 12 + 1)
+     21506    record (14 = 3 [3] + 10 + 1)
+     21520    record (13 = 3 [3] + 9 + 1)
+     21533    record (14 = 3 [3] + 10 + 1)
+     21547    record (17 = 3 [2] + 13 + 1)
+     21564    record (13 = 3 [7] + 9 + 1)
+     21577    record (16 = 3 [3] + 12 + 1)
+     21593    record (15 = 3 [3] + 11 + 1)
+     21608    record (18 = 3 [0] + 14 + 1) [restart]
+     21626    record (16 = 3 [3] + 12 + 1)
+     21642    record (14 = 3 [4] + 10 + 1)
+     21656    record (15 = 3 [3] + 11 + 1)
+     21671    record (22 = 3 [2] + 18 + 1)
+     21693    record (16 = 3 [3] + 12 + 1)
+     21709    record (14 = 3 [3] + 10 + 1)
+     21723    record (18 = 3 [1] + 14 + 1)
+     21741    record (18 = 3 [3] + 14 + 1)
+     21759    record (15 = 3 [2] + 10 + 2)
+     21774    record (16 = 3 [2] + 12 + 1)
+     21790    record (14 = 3 [2] + 10 + 1)
+     21804    record (13 = 3 [4] + 9 + 1)
+     21817    record (20 = 3 [2] + 16 + 1)
+     21837    record (16 = 3 [2] + 12 + 1)
+     21853    record (15 = 3 [4] + 11 + 1)
+     21868    record (15 = 3 [0] + 11 + 1) [restart]
+     21883    record (15 = 3 [3] + 11 + 1)
+     21898    record (20 = 3 [2] + 16 + 1)
+     21918    record (16 = 3 [4] + 12 + 1)
+     21934    record (16 = 3 [4] + 12 + 1)
+     21950    record (14 = 3 [2] + 10 + 1)
+     21964    record (18 = 3 [3] + 14 + 1)
+     21982    record (18 = 3 [3] + 14 + 1)
+     22000    record (17 = 3 [3] + 13 + 1)
+     22017    record (21 = 3 [2] + 17 + 1)
+     22038    record (16 = 3 [3] + 12 + 1)
+     22054    record (21 = 3 [1] + 17 + 1)
+     22075    record (16 = 3 [2] + 11 + 2)
+     22091    record (14 = 3 [4] + 10 + 1)
+     22105    record (14 = 3 [3] + 10 + 1)
+     22119    record (13 = 3 [4] + 9 + 1)
+     22132    record (17 = 3 [0] + 13 + 1) [restart]
+     22149    record (16 = 3 [3] + 12 + 1)
+     22165    record (15 = 3 [2] + 11 + 1)
+     22180    record (13 = 3 [4] + 9 + 1)
+     22193    record (14 = 3 [0] + 9 + 2)
+     22207    record (13 = 3 [1] + 9 + 1)
+     22220    record (15 = 3 [2] + 11 + 1)
+     22235    record (13 = 3 [5] + 9 + 1)
+     22248    record (15 = 3 [2] + 11 + 1)
+     22263    record (15 = 3 [2] + 10 + 2)
+     22278    record (13 = 3 [4] + 9 + 1)
+     22291    record (13 = 3 [4] + 9 + 1)
+     22304    record (14 = 3 [2] + 10 + 1)
+     22318    record (13 = 3 [3] + 9 + 1)
+     22331    record (14 = 3 [2] + 10 + 1)
+     22345    record (13 = 3 [2] + 9 + 1)
+     22358    record (17 = 3 [0] + 13 + 1) [restart]
+     22375    record (14 = 3 [3] + 10 + 1)
+     22389    record (14 = 3 [2] + 10 + 1)
+     22403    record (16 = 3 [2] + 12 + 1)
+     22419    [restart 20429]
+     22423    [restart 20664]
+     22427    [restart 20895]
+     22431    [restart 21147]
+     22435    [restart 21367]
+     22439    [restart 21608]
+     22443    [restart 21868]
+     22447    [restart 22132]
+     22451    [restart 22358]
+     22464  data (2035)
+     22464    record (17 = 3 [0] + 13 + 1) [restart]
+     22481    record (17 = 3 [2] + 13 + 1)
+     22498    record (18 = 3 [4] + 14 + 1)
+     22516    record (13 = 3 [3] + 9 + 1)
+     22529    record (14 = 3 [4] + 10 + 1)
+     22543    record (13 = 3 [6] + 9 + 1)
+     22556    record (14 = 3 [2] + 10 + 1)
+     22570    record (13 = 3 [4] + 9 + 1)
+     22583    record (16 = 3 [2] + 12 + 1)
+     22599    record (15 = 3 [3] + 11 + 1)
+     22614    record (16 = 3 [1] + 11 + 2)
+     22630    record (14 = 3 [4] + 10 + 1)
+     22644    record (14 = 3 [3] + 9 + 2)
+     22658    record (13 = 3 [3] + 9 + 1)
+     22671    record (15 = 3 [2] + 9 + 3)
+     22686    record (14 = 3 [3] + 9 + 2)
+     22700    record (18 = 3 [0] + 13 + 2) [restart]
+     22718    record (14 = 3 [3] + 9 + 2)
+     22732    record (13 = 3 [4] + 9 + 1)
+     22745    record (14 = 3 [3] + 9 + 2)
+     22759    record (15 = 3 [3] + 10 + 2)
+     22774    record (16 = 3 [5] + 12 + 1)
+     22790    record (14 = 3 [5] + 10 + 1)
+     22804    record (15 = 3 [3] + 10 + 2)
+     22819    record (14 = 3 [3] + 10 + 1)
+     22833    record (14 = 3 [3] + 9 + 2)
+     22847    record (14 = 3 [2] + 10 + 1)
+     22861    record (13 = 3 [4] + 9 + 1)
+     22874    record (13 = 3 [4] + 9 + 1)
+     22887    record (13 = 3 [5] + 9 + 1)
+     22900    record (14 = 3 [4] + 9 + 2)
+     22914    record (15 = 3 [5] + 11 + 1)
+     22929    record (17 = 3 [0] + 13 + 1) [restart]
+     22946    record (14 = 3 [3] + 9 + 2)
+     22960    record (16 = 3 [2] + 12 + 1)
+     22976    record (13 = 3 [5] + 9 + 1)
+     22989    record (14 = 3 [3] + 10 + 1)
+     23003    record (14 = 3 [3] + 9 + 2)
+     23017    record (15 = 3 [4] + 10 + 2)
+     23032    record (13 = 3 [6] + 9 + 1)
+     23045    record (13 = 3 [7] + 9 + 1)
+     23058    record (16 = 3 [2] + 12 + 1)
+     23074    record (14 = 3 [4] + 10 + 1)
+     23088    record (15 = 3 [3] + 11 + 1)
+     23103    record (14 = 3 [4] + 10 + 1)
+     23117    record (15 = 3 [4] + 11 + 1)
+     23132    record (13 = 3 [4] + 9 + 1)
+     23145    record (17 = 3 [2] + 13 + 1)
+     23162    record (16 = 3 [0] + 12 + 1) [restart]
+     23178    record (14 = 3 [2] + 9 + 2)
+     23192    record (16 = 3 [3] + 12 + 1)
+     23208    record (15 = 3 [1] + 11 + 1)
+     23223    record (15 = 3 [2] + 10 + 2)
+     23238    record (13 = 3 [4] + 9 + 1)
+     23251    record (14 = 3 [2] + 9 + 2)
+     23265    record (15 = 3 [1] + 9 + 3)
+     23280    record (13 = 3 [2] + 9 + 1)
+     23293    record (18 = 3 [2] + 14 + 1)
+     23311    record (15 = 3 [2] + 11 + 1)
+     23326    record (14 = 3 [2] + 10 + 1)
+     23340    record (16 = 3 [2] + 12 + 1)
+     23356    record (13 = 3 [2] + 9 + 1)
+     23369    record (13 = 3 [2] + 9 + 1)
+     23382    record (20 = 3 [2] + 16 + 1)
+     23402    record (20 = 3 [0] + 16 + 1) [restart]
+     23422    record (16 = 3 [2] + 12 + 1)
+     23438    record (13 = 3 [2] + 9 + 1)
+     23451    record (13 = 3 [3] + 9 + 1)
+     23464    record (19 = 3 [1] + 15 + 1)
+     23483    record (16 = 3 [3] + 12 + 1)
+     23499    record (15 = 3 [3] + 11 + 1)
+     23514    record (18 = 3 [4] + 14 + 1)
+     23532    record (18 = 3 [3] + 14 + 1)
+     23550    record (16 = 3 [2] + 12 + 1)
+     23566    record (16 = 3 [4] + 12 + 1)
+     23582    record (16 = 3 [3] + 12 + 1)
+     23598    record (15 = 3 [2] + 11 + 1)
+     23613    record (17 = 3 [3] + 13 + 1)
+     23630    record (16 = 3 [3] + 12 + 1)
+     23646    record (16 = 3 [3] + 12 + 1)
+     23662    record (19 = 3 [0] + 15 + 1) [restart]
+     23681    record (13 = 3 [7] + 9 + 1)
+     23694    record (16 = 3 [2] + 12 + 1)
+     23710    record (13 = 3 [3] + 9 + 1)
+     23723    record (17 = 3 [4] + 13 + 1)
+     23740    record (14 = 3 [3] + 10 + 1)
+     23754    record (16 = 3 [3] + 12 + 1)
+     23770    record (13 = 3 [7] + 9 + 1)
+     23783    record (18 = 3 [3] + 14 + 1)
+     23801    record (16 = 3 [3] + 12 + 1)
+     23817    record (14 = 3 [3] + 10 + 1)
+     23831    record (15 = 3 [1] + 11 + 1)
+     23846    record (17 = 3 [1] + 13 + 1)
+     23863    record (14 = 3 [3] + 10 + 1)
+     23877    record (15 = 3 [2] + 11 + 1)
+     23892    record (14 = 3 [3] + 10 + 1)
+     23906    record (17 = 3 [0] + 13 + 1) [restart]
+     23923    record (13 = 3 [2] + 9 + 1)
+     23936    record (18 = 3 [0] + 14 + 1)
+     23954    record (17 = 3 [1] + 13 + 1)
+     23971    record (15 = 3 [2] + 11 + 1)
+     23986    record (17 = 3 [2] + 13 + 1)
+     24003    record (17 = 3 [5] + 13 + 1)
+     24020    record (15 = 3 [10] + 11 + 1)
+     24035    record (21 = 3 [2] + 17 + 1)
+     24056    record (19 = 3 [2] + 15 + 1)
+     24075    record (15 = 3 [3] + 11 + 1)
+     24090    record (16 = 3 [4] + 12 + 1)
+     24106    record (18 = 3 [5] + 14 + 1)
+     24124    record (20 = 3 [2] + 16 + 1)
+     24144    record (16 = 3 [2] + 12 + 1)
+     24160    record (15 = 3 [3] + 11 + 1)
+     24175    record (20 = 3 [0] + 16 + 1) [restart]
+     24195    record (20 = 3 [2] + 16 + 1)
+     24215    record (17 = 3 [2] + 13 + 1)
+     24232    record (14 = 3 [4] + 10 + 1)
+     24246    record (15 = 3 [5] + 11 + 1)
+     24261    record (14 = 3 [3] + 10 + 1)
+     24275    record (19 = 3 [2] + 15 + 1)
+     24294    record (22 = 3 [2] + 18 + 1)
+     24316    record (20 = 3 [4] + 16 + 1)
+     24336    record (21 = 3 [5] + 17 + 1)
+     24357    record (21 = 3 [2] + 17 + 1)
+     24378    record (18 = 3 [2] + 14 + 1)
+     24396    record (17 = 3 [3] + 13 + 1)
+     24413    record (14 = 3 [2] + 10 + 1)
+     24427    record (18 = 3 [2] + 14 + 1)
+     24445    record (18 = 3 [2] + 14 + 1)
+     24463    [restart 22464]
+     24467    [restart 22700]
+     24471    [restart 22929]
+     24475    [restart 23162]
+     24479    [restart 23402]
+     24483    [restart 23662]
+     24487    [restart 23906]
+     24491    [restart 24175]
+     24504  data (2036)
+     24504    record (15 = 3 [0] + 10 + 2) [restart]
+     24519    record (19 = 3 [2] + 15 + 1)
+     24538    record (15 = 3 [2] + 10 + 2)
+     24553    record (14 = 3 [1] + 9 + 2)
+     24567    record (13 = 3 [2] + 9 + 1)
+     24580    record (13 = 3 [3] + 9 + 1)
+     24593    record (15 = 3 [2] + 11 + 1)
+     24608    record (13 = 3 [0] + 9 + 1)
+     24621    record (17 = 3 [1] + 13 + 1)
+     24638    record (13 = 3 [3] + 9 + 1)
+     24651    record (17 = 3 [2] + 13 + 1)
+     24668    record (16 = 3 [2] + 12 + 1)
+     24684    record (19 = 3 [3] + 15 + 1)
+     24703    record (14 = 3 [2] + 10 + 1)
+     24717    record (15 = 3 [1] + 11 + 1)
+     24732    record (15 = 3 [1] + 11 + 1)
+     24747    record (19 = 3 [0] + 15 + 1) [restart]
+     24766    record (16 = 3 [2] + 12 + 1)
+     24782    record (14 = 3 [2] + 10 + 1)
+     24796    record (16 = 3 [3] + 12 + 1)
+     24812    record (18 = 3 [2] + 14 + 1)
+     24830    record (13 = 3 [5] + 9 + 1)
+     24843    record (16 = 3 [2] + 12 + 1)
+     24859    record (13 = 3 [6] + 9 + 1)
+     24872    record (15 = 3 [5] + 11 + 1)
+     24887    record (16 = 3 [2] + 12 + 1)
+     24903    record (15 = 3 [3] + 11 + 1)
+     24918    record (13 = 3 [4] + 9 + 1)
+     24931    record (16 = 3 [1] + 12 + 1)
+     24947    record (19 = 3 [2] + 15 + 1)
+     24966    record (15 = 3 [3] + 11 + 1)
+     24981    record (13 = 3 [2] + 9 + 1)
+     24994    record (16 = 3 [0] + 12 + 1) [restart]
+     25010    record (17 = 3 [1] + 13 + 1)
+     25027    record (16 = 3 [0] + 12 + 1)
+     25043    record (14 = 3 [2] + 10 + 1)
+     25057    record (13 = 3 [4] + 9 + 1)
+     25070    record (15 = 3 [2] + 11 + 1)
+     25085    record (13 = 3 [2] + 9 + 1)
+     25098    record (16 = 3 [3] + 12 + 1)
+     25114    record (16 = 3 [3] + 12 + 1)
+     25130    record (16 = 3 [3] + 12 + 1)
+     25146    record (13 = 3 [3] + 9 + 1)
+     25159    record (13 = 3 [3] + 9 + 1)
+     25172    record (14 = 3 [2] + 9 + 2)
+     25186    record (16 = 3 [3] + 12 + 1)
+     25202    record (16 = 3 [2] + 11 + 2)
+     25218    record (15 = 3 [5] + 11 + 1)
+     25233    record (17 = 3 [0] + 13 + 1) [restart]
+     25250    record (15 = 3 [2] + 11 + 1)
+     25265    record (13 = 3 [2] + 9 + 1)
+     25278    record (13 = 3 [3] + 9 + 1)
+     25291    record (14 = 3 [1] + 9 + 2)
+     25305    record (14 = 3 [2] + 10 + 1)
+     25319    record (14 = 3 [3] + 10 + 1)
+     25333    record (13 = 3 [4] + 9 + 1)
+     25346    record (17 = 3 [2] + 13 + 1)
+     25363    record (14 = 3 [2] + 10 + 1)
+     25377    record (13 = 3 [3] + 9 + 1)
+     25390    record (15 = 3 [2] + 11 + 1)
+     25405    record (15 = 3 [5] + 11 + 1)
+     25420    record (17 = 3 [2] + 13 + 1)
+     25437    record (14 = 3 [3] + 9 + 2)
+     25451    record (14 = 3 [2] + 10 + 1)
+     25465    record (16 = 3 [0] + 12 + 1) [restart]
+     25481    record (14 = 3 [2] + 10 + 1)
+     25495    record (16 = 3 [4] + 12 + 1)
+     25511    record (16 = 3 [1] + 12 + 1)
+     25527    record (14 = 3 [3] + 9 + 2)
+     25541    record (18 = 3 [4] + 14 + 1)
+     25559    record (14 = 3 [2] + 10 + 1)
+     25573    record (14 = 3 [4] + 10 + 1)
+     25587    record (14 = 3 [3] + 10 + 1)
+     25601    record (16 = 3 [5] + 12 + 1)
+     25617    record (14 = 3 [5] + 10 + 1)
+     25631    record (14 = 3 [5] + 10 + 1)
+     25645    record (16 = 3 [3] + 12 + 1)
+     25661    record (16 = 3 [2] + 11 + 2)
+     25677    record (14 = 3 [3] + 10 + 1)
+     25691    record (13 = 3 [5] + 9 + 1)
+     25704    record (18 = 3 [0] + 14 + 1) [restart]
+     25722    record (17 = 3 [3] + 13 + 1)
+     25739    record (16 = 3 [3] + 12 + 1)
+     25755    record (13 = 3 [2] + 9 + 1)
+     25768    record (14 = 3 [3] + 10 + 1)
+     25782    record (16 = 3 [5] + 12 + 1)
+     25798    record (14 = 3 [3] + 10 + 1)
+     25812    record (14 = 3 [2] + 9 + 2)
+     25826    record (17 = 3 [1] + 13 + 1)
+     25843    record (14 = 3 [2] + 10 + 1)
+     25857    record (14 = 3 [2] + 10 + 1)
+     25871    record (14 = 3 [2] + 10 + 1)
+     25885    record (14 = 3 [3] + 9 + 2)
+     25899    record (15 = 3 [4] + 11 + 1)
+     25914    record (14 = 3 [7] + 10 + 1)
+     25928    record (13 = 3 [3] + 9 + 1)
+     25941    record (16 = 3 [0] + 12 + 1) [restart]
+     25957    record (13 = 3 [4] + 9 + 1)
+     25970    record (13 = 3 [4] + 9 + 1)
+     25983    record (14 = 3 [3] + 10 + 1)
+     25997    record (14 = 3 [2] + 10 + 1)
+     26011    record (16 = 3 [2] + 12 + 1)
+     26027    record (13 = 3 [6] + 9 + 1)
+     26040    record (15 = 3 [3] + 11 + 1)
+     26055    record (15 = 3 [3] + 11 + 1)
+     26070    record (13 = 3 [2] + 9 + 1)
+     26083    record (14 = 3 [3] + 10 + 1)
+     26097    record (17 = 3 [5] + 13 + 1)
+     26114    record (14 = 3 [3] + 9 + 2)
+     26128    record (14 = 3 [4] + 10 + 1)
+     26142    record (15 = 3 [4] + 10 + 2)
+     26157    record (15 = 3 [4] + 11 + 1)
+     26172    record (19 = 3 [0] + 15 + 1) [restart]
+     26191    record (19 = 3 [3] + 15 + 1)
+     26210    record (14 = 3 [1] + 10 + 1)
+     26224    record (15 = 3 [2] + 11 + 1)
+     26239    record (13 = 3 [3] + 9 + 1)
+     26252    record (13 = 3 [2] + 9 + 1)
+     26265    record (15 = 3 [3] + 11 + 1)
+     26280    record (15 = 3 [6] + 11 + 1)
+     26295    record (16 = 3 [4] + 12 + 1)
+     26311    record (13 = 3 [3] + 9 + 1)
+     26324    record (19 = 3 [2] + 15 + 1)
+     26343    record (14 = 3 [2] + 10 + 1)
+     26357    record (13 = 3 [4] + 9 + 1)
+     26370    record (13 = 3 [3] + 9 + 1)
+     26383    record (13 = 3 [3] + 9 + 1)
+     26396    record (14 = 3 [3] + 10 + 1)
+     26410    record (16 = 3 [0] + 12 + 1) [restart]
+     26426    record (14 = 3 [3] + 10 + 1)
+     26440    record (13 = 3 [5] + 9 + 1)
+     26453    record (16 = 3 [2] + 11 + 2)
+     26469    record (14 = 3 [5] + 10 + 1)
+     26483    record (17 = 3 [1] + 13 + 1)
+     26500    [restart 24504]
+     26504    [restart 24747]
+     26508    [restart 24994]
+     26512    [restart 25233]
+     26516    [restart 25465]
+     26520    [restart 25704]
+     26524    [restart 25941]
+     26528    [restart 26172]
+     26532    [restart 26410]
+     26545  data (249)
+     26545    record (16 = 3 [0] + 12 + 1) [restart]
+     26561    record (15 = 3 [4] + 11 + 1)
+     26576    record (15 = 3 [2] + 11 + 1)
+     26591    record (15 = 3 [2] + 11 + 1)
+     26606    record (15 = 3 [0] + 11 + 1)
+     26621    record (13 = 3 [2] + 9 + 1)
+     26634    record (20 = 3 [3] + 16 + 1)
+     26654    record (13 = 3 [2] + 9 + 1)
+     26667    record (19 = 3 [1] + 15 + 1)
+     26686    record (14 = 3 [1] + 10 + 1)
+     26700    record (13 = 3 [3] + 9 + 1)
+     26713    record (15 = 3 [2] + 9 + 3)
+     26728    record (14 = 3 [3] + 10 + 1)
+     26742    record (14 = 3 [3] + 9 + 2)
+     26756    record (16 = 3 [4] + 12 + 1)
+     26772    record (14 = 3 [3] + 10 + 1)
+     26786    [restart 26545]
+     26799  index (120)
+     26799    block:0/2041 [restart]
+     26817    block:2046/2044 [restart]
+     26839    block:4095/2039 [restart]
+     26858    block:6139/2036 [restart]
+     26876    block:8180/2032 [restart]
+     26895    [restart 26799]
+     26899    [restart 26817]
+     26903    [restart 26839]
+     26907    [restart 26858]
+     26911    [restart 26876]
+     26924  index (118)
+     26924    block:10217/2042 [restart]
+     26941    block:12264/2039 [restart]
+     26959    block:14308/2037 [restart]
+     26979    block:16350/2029 [restart]
+     26998    block:18384/2040 [restart]
+     27018    [restart 26924]
+     27022    [restart 26941]
+     27026    [restart 26959]
+     27030    [restart 26979]
+     27034    [restart 26998]
+     27047  index (95)
+     27047    block:20429/2030 [restart]
+     27068    block:22464/2035 [restart]
+     27086    block:24504/2036 [restart]
+     27105    block:26545/249 [restart]
+     27122    [restart 27047]
+     27126    [restart 27068]
+     27130    [restart 27086]
+     27134    [restart 27105]
+     27147  top-index (70)
+     27147    block:26799/120 [restart]
+     27166    block:26924/118 [restart]
+     27185    block:27047/95 [restart]
+     27201    [restart 27147]
+     27205    [restart 27166]
+     27209    [restart 27185]
+     27222  properties (765)
+     27222    rocksdb.block.based.table.index.type (43) [restart]
+     27265    rocksdb.block.based.table.prefix.filtering (20)
+     27285    rocksdb.block.based.table.whole.key.filtering (23)
+     27308    rocksdb.column.family.id (24)
+     27332    rocksdb.comparator (37)
+     27369    rocksdb.compression (23)
+     27392    rocksdb.compression_options (106)
+     27498    rocksdb.creation.time (16)
+     27514    rocksdb.data.size (15)
+     27529    rocksdb.deleted.keys (15)
+     27544    rocksdb.external_sst_file.global_seqno (41)
+     27585    rocksdb.external_sst_file.version (14)
+     27599    rocksdb.filter.size (15)
+     27614    rocksdb.fixed.key.length (18)
+     27632    rocksdb.format.version (17)
+     27649    rocksdb.index.key.is.user.key (25)
+     27674    rocksdb.index.partitions (14)
+     27688    rocksdb.index.size (9)
+     27697    rocksdb.index.value.is.delta.encoded (26)
+     27723    rocksdb.merge.operands (18)
+     27741    rocksdb.merge.operator (13)
+     27754    rocksdb.num.data.blocks (19)
+     27773    rocksdb.num.entries (12)
+     27785    rocksdb.num.range-deletions (19)
+     27804    rocksdb.oldest.key.time (19)
+     27823    rocksdb.prefix.extractor.name (31)
+     27854    rocksdb.property.collectors (47)
+     27901    rocksdb.raw.key.size (18)
+     27919    rocksdb.raw.value.size (15)
+     27934    rocksdb.top-level.index.size (24)
+     27958    test.key-count (21)
+     27979    [restart 27222]
+     27992  meta-index (34)
+     27992    rocksdb.properties block:27222/765 [restart]
+     28018    [restart 27992]
+     28031  footer (53)
diff --git a/tool/testdata/sstable_properties b/tool/testdata/sstable_properties
new file mode 100644
index 0000000..5116dea
--- /dev/null
+++ b/tool/testdata/sstable_properties
@@ -0,0 +1,147 @@
+sstable properties
+----
+requires at least 1 arg(s), only received 0
+
+sstable properties
+../sstable/testdata/h.sst
+----
+../sstable/testdata/h.sst
+version           2
+size              
+  file            14978
+  data            13913
+    blocks        14
+  index           325
+    blocks        1
+    top-level     0
+  filter          0
+  raw-key         23717
+  raw-value       1835
+records           1710
+  set             1710
+  delete          0
+  range-delete    0
+  merge           0
+  global-seq-num  0
+index             
+  key             internal key
+  value           raw encoded
+comparer          leveldb.BytewiseComparator
+merger            -
+filter            -
+  prefix          false
+  whole-key       false
+compression       Snappy
+  options         window_bits=-14; level=32767; strategy=0; max_dict_bytes=0; zstd_max_train_bytes=0; enabled=0; 
+user properties   
+  collectors      [KeyCountPropertyCollector]
+  test.key-count  1710
+
+sstable properties
+../sstable/testdata/h.ldb
+----
+../sstable/testdata/h.ldb
+version           2
+size              
+  file            14927
+  data            13913
+    blocks        14
+  index           325
+    blocks        1
+    top-level     0
+  filter          0
+  raw-key         23717
+  raw-value       1835
+records           1710
+  set             1710
+  delete          0
+  range-delete    0
+  merge           0
+  global-seq-num  0
+index             
+  key             internal key
+  value           raw encoded
+comparer          leveldb.BytewiseComparator
+merger            -
+filter            -
+  prefix          false
+  whole-key       false
+compression       Snappy
+  options         window_bits=-14; level=32767; strategy=0; max_dict_bytes=0; zstd_max_train_bytes=0; enabled=0; 
+user properties   
+  collectors      []
+
+sstable properties
+../sstable/testdata/h.no-compression.two_level_index.sst
+----
+../sstable/testdata/h.no-compression.two_level_index.sst
+version           2
+size              
+  file            28084
+  data            26799
+    blocks        14
+  index           408
+    blocks        4
+    top-level     70
+  filter          0
+  raw-key         23717
+  raw-value       1835
+records           1710
+  set             1710
+  delete          0
+  range-delete    0
+  merge           0
+  global-seq-num  0
+index             
+  key             internal key
+  value           raw encoded
+comparer          leveldb.BytewiseComparator
+merger            -
+filter            -
+  prefix          false
+  whole-key       false
+compression       NoCompression
+  options         window_bits=-14; level=32767; strategy=0; max_dict_bytes=0; zstd_max_train_bytes=0; enabled=0; 
+user properties   
+  collectors      [KeyCountPropertyCollector]
+  test.key-count  1710
+
+sstable properties
+-v
+../sstable/testdata/h.no-compression.two_level_index.sst
+----
+../sstable/testdata/h.no-compression.two_level_index.sst
+rocksdb.column.family.id: -
+rocksdb.column.family.name: 
+rocksdb.comparator: leveldb.BytewiseComparator
+rocksdb.compression: NoCompression
+rocksdb.compression_options: window_bits=-14; level=32767; strategy=0; max_dict_bytes=0; zstd_max_train_bytes=0; enabled=0; 
+rocksdb.creation.time: 0
+rocksdb.data.size: 26799
+rocksdb.file.creation.time: 0
+rocksdb.filter.policy: 
+rocksdb.filter.size: 0
+rocksdb.fixed.key.length: 0
+rocksdb.format.version: 0
+rocksdb.external_sst_file.global_seqno: 0
+rocksdb.index.key.is.user.key: 0
+rocksdb.index.partitions: 3
+rocksdb.index.size: 408
+rocksdb.block.based.table.index.type: 2
+rocksdb.index.value.is.delta.encoded: 0
+rocksdb.merge.operator: nullptr
+rocksdb.num.data.blocks: 14
+rocksdb.deleted.keys: 0
+rocksdb.num.entries: 1710
+rocksdb.merge.operands: 0
+rocksdb.num.range-deletions: 0
+rocksdb.oldest.key.time: 0
+rocksdb.prefix.extractor.name: nullptr
+rocksdb.block.based.table.prefix.filtering: false
+rocksdb.property.collectors: [KeyCountPropertyCollector]
+rocksdb.raw.key.size: 23717
+rocksdb.raw.value.size: 1835
+rocksdb.top-level.index.size: 70
+rocksdb.external_sst_file.version: 2
+rocksdb.block.based.table.whole.key.filtering: false
+test.key-count: 1710
diff --git a/tool/testdata/sstable_scan b/tool/testdata/sstable_scan
new file mode 100644
index 0000000..4cb780c
--- /dev/null
+++ b/tool/testdata/sstable_scan
@@ -0,0 +1,39 @@
+sstable scan
+--start=arm
+--end=aside
+../sstable/testdata/h.sst
+----
+../sstable/testdata/h.sst
+arm#0,1 [32]
+armed#0,1 [32]
+armour#0,1 [31]
+arms#0,1 [32]
+arrant#0,1 [31]
+art#0,1 [36]
+artery#0,1 [31]
+article#0,1 [31]
+articles#0,1 [31]
+as#0,1 [35 36]
+
+sstable scan
+--end=abused
+../sstable/testdata/h.sst
+----
+../sstable/testdata/h.sst
+a#0,1 [39 37]
+aboard#0,1 [32]
+about#0,1 [32]
+above#0,1 [31]
+abroad#0,1 [31]
+absurd#0,1 [31]
+
+sstable scan
+--start=you
+../sstable/testdata/h.sst
+----
+../sstable/testdata/h.sst
+you#0,1 [31 31 30]
+young#0,1 [36]
+your#0,1 [34 39]
+yourself#0,1 [37]
+youth#0,1 [35]