func (f *Frame) PopRange(args ...rangeArg) ([]row.Data, error) {
	opts := rangeArgsToOptions(args)
	rows, err := f.forRange(opts, func(row row.Row) (interface{}, error) {
		return row, nil
	})
	if err != nil {
		return nil, err
	}
	var (
		indices []row.Index
		data    []row.Data
	)
	for _, r := range rows {
		indices = append(indices, r.(row.Row).Index)
		data = append(data, r.(row.Row).Data)
	}
	for _, i := range indices {
		f.bt.Delete(i)
	}
	return data, nil
}
PopRange returns a list of all values in the given range and deletes them
from the Frame. See GetRange for details on the arguments.
package godata
import (
	"bytes"
	"fmt"
	"github.com/google/btree"
	"github.com/google/godata/group"
	"github.com/google/godata/row"
)
type Frame struct {
	bt *btree.BTree
	indexer row.Indexer
}
func NewFrame(indexer row.Indexer) *Frame {
	return &Frame{
		bt:      btree.New(2),
		indexer: indexer,
	}
}
func (f *Frame) Put(data row.Data) (row.Data, error) {
	index, err := f.indexer.Index(data)
	if err != nil {
		return nil, fmt.Errorf("AddRow: %v", err)
	}
	got := f.bt.ReplaceOrInsert(row.Row{
		Index: index,
		Data:  data,
	})
	if got == nil {
		return nil, nil
	}
	return got.(row.Row).Data, nil
}
func (f *Frame) Get(key row.Data) (row.Data, error) {
	index, err := f.indexer.Index(key)
	if err != nil {
		return nil, err
	}
	got := f.bt.Get(index)
	if got == nil {
		return nil, nil
	}
	return got.(row.Row).Data, nil
}
func (f *Frame) Pop(key row.Data) (row.Data, error) {
	index, err := f.indexer.Index(key)
	if err != nil {
		return nil, err
	}
	got := f.bt.Delete(index)
	if got == nil {
		return nil, nil
	}
	return got.(row.Row).Data, nil
}
type rangeOptions struct {
	greaterOrEqual row.Data
	lessThan       row.Data
}
type rangeArg func(*rangeOptions)
func rangeArgsToOptions(args []rangeArg) *rangeOptions {
	var opts rangeOptions
	for _, a := range args {
		a(&opts)
	}
	return &opts
}
func GreaterOrEqual(val row.Data) rangeArg {
	return func(opts *rangeOptions) {
		opts.greaterOrEqual = val
	}
}
func LessThan(val row.Data) rangeArg {
	return func(opts *rangeOptions) {
		opts.lessThan = val
	}
}
type RowAction func(row.Data) (interface{}, error)
type rowAction func(row.Row) (interface{}, error)
func (f *Frame) forRange(opts *rangeOptions, action rowAction) ([]interface{}, error) {
	var (
		returnError  error
		returnValues []interface{}
	)
	iterator := func(item btree.Item) bool {
		val, err := action(item.(row.Row))
		if err != nil {
			returnError = err
			return false
		}
		returnValues = append(returnValues, val)
		return true
	}
	if opts.lessThan == nil && opts.greaterOrEqual == nil {
		f.bt.Ascend(iterator)
	} else if opts.lessThan == nil {
		pivot, err := f.indexer.Index(opts.greaterOrEqual)
		if err != nil {
			return nil, err
		}
		f.bt.AscendGreaterOrEqual(pivot, iterator)
	} else if opts.greaterOrEqual == nil {
		pivot, err := f.indexer.Index(opts.lessThan)
		if err != nil {
			return nil, err
		}
		f.bt.AscendLessThan(pivot, iterator)
	} else {
		begin, err := f.indexer.Index(opts.greaterOrEqual)
		if err != nil {
			return nil, err
		}
		end, err := f.indexer.Index(opts.lessThan)
		if err != nil {
			return nil, err
		}
		f.bt.AscendRange(begin, end, iterator)
	}
	return returnValues, returnError
}
func (f *Frame) String() string {
	rows, err := f.GetRange()
	if err != nil {
		return fmt.Sprintf("Frame.String: %v", err)
	}
	var buf bytes.Buffer
	for i, r := range rows {
		buf.WriteString(fmt.Sprintf("%d: %v\n", i, r))
	}
	return buf.String()
}
func (f *Frame) GetRange(args ...rangeArg) ([]row.Data, error) {
	opts := rangeArgsToOptions(args)
	rows, err := f.forRange(opts, func(row row.Row) (interface{}, error) {
		return row.Data, nil
	})
	if err != nil {
		return nil, err
	}
	var castRows []row.Data
	for _, r := range rows {
		castRows = append(castRows, r.(row.Data))
	}
	return castRows, nil
}