// Generated by tmpl
// https://github.com/benbjohnson/tmpl
//
// DO NOT EDIT!
// Source: influxql.gen.go.tmpl

package kapacitor

import (
	"fmt"
	"reflect"
	"time"

	"github.com/influxdata/influxdb/influxql"
	"github.com/influxdata/kapacitor/edge"
	"github.com/influxdata/kapacitor/models"
	"github.com/influxdata/kapacitor/pipeline"
)

func convertFloatPoint(
	name string,
	p edge.FieldsTagsTimeGetter,
	field string,
	isSimpleSelector bool,
	topBottomInfo *pipeline.TopBottomCallInfo,
) (*influxql.FloatPoint, error) {
	value, ok := p.Fields()[field]
	if !ok {
		return nil, fmt.Errorf("field %s missing from point cannot aggregate", field)
	}
	typed, ok := value.(float64)
	if !ok {
		return nil, fmt.Errorf("field %s has wrong type: got %T exp float64", field, value)
	}
	ap := &influxql.FloatPoint{
		Name:  name,
		Tags:  influxql.NewTags(p.Tags()),
		Time:  p.Time().UnixNano(),
		Value: typed,
	}
	if topBottomInfo != nil {
		// We need to populate the Aux fields
		floatPopulateAuxFieldsAndTags(ap, topBottomInfo.FieldsAndTags, p.Fields(), p.Tags())
	}

	if isSimpleSelector {
		ap.Aux = []interface{}{p.Tags(), p.Fields()}
	}

	return ap, nil
}

type floatPointAggregator struct {
	field            string
	topBottomInfo    *pipeline.TopBottomCallInfo
	isSimpleSelector bool
	aggregator       influxql.FloatPointAggregator
}

func floatPopulateAuxFieldsAndTags(ap *influxql.FloatPoint, fieldsAndTags []string, fields models.Fields, tags models.Tags) {
	ap.Aux = make([]interface{}, len(fieldsAndTags))
	for i, name := range fieldsAndTags {
		if f, ok := fields[name]; ok {
			ap.Aux[i] = f
		} else {
			ap.Aux[i] = tags[name]
		}
	}
}

func (a *floatPointAggregator) AggregatePoint(name string, p edge.FieldsTagsTimeGetter) error {
	ap, err := convertFloatPoint(name, p, a.field, a.isSimpleSelector, a.topBottomInfo)
	if err != nil {
		return nil
	}
	a.aggregator.AggregateFloat(ap)
	return nil
}

type floatPointEmitter struct {
	baseReduceContext
	emitter          influxql.FloatPointEmitter
	isSimpleSelector bool
	byName           bool
}

func (e *floatPointEmitter) EmitPoint() (edge.PointMessage, error) {
	slice := e.emitter.Emit()
	if len(slice) != 1 {
		return nil, nil
	}
	ap := slice[0]
	var t time.Time
	if e.pointTimes {
		if ap.Time == influxql.ZeroTime {
			t = e.time
		} else {
			t = time.Unix(0, ap.Time).UTC()
		}
	} else {
		t = e.time
	}

	var fields models.Fields
	var tags models.Tags
	if e.isSimpleSelector {
		tags = ap.Aux[0].(models.Tags)
		fields = ap.Aux[1].(models.Fields)
		if e.as != e.field {
			fields = fields.Copy()
			fields[e.as] = fields[e.field]
			delete(fields, e.field)
		}
	} else {
		tags = e.groupInfo.Tags
		fields = map[string]interface{}{e.as: ap.Value}
	}

	return edge.NewPointMessage(
		e.name, "", "",
		e.groupInfo.Dimensions,
		fields,
		tags,
		t,
	), nil
}

func (e *floatPointEmitter) EmitBatch() edge.BufferedBatchMessage {
	slice := e.emitter.Emit()
	begin := edge.NewBeginBatchMessage(
		e.name,
		e.groupInfo.Tags,
		e.groupInfo.Dimensions.ByName,
		e.time,
		len(slice),
	)
	points := make([]edge.BatchPointMessage, len(slice))
	var t time.Time
	for i, ap := range slice {
		if e.pointTimes {
			if ap.Time == influxql.ZeroTime {
				t = e.time
			} else {
				t = time.Unix(0, ap.Time).UTC()
			}
		} else {
			t = e.time
		}
		var tags models.Tags
		if l := len(ap.Tags.KeyValues()); l > 0 {
			// Merge batch and point specific tags
			tags = make(models.Tags, len(e.groupInfo.Tags)+l)
			for k, v := range e.groupInfo.Tags {
				tags[k] = v
			}
			for k, v := range ap.Tags.KeyValues() {
				tags[k] = v
			}
		} else {
			tags = e.groupInfo.Tags
		}
		points[i] = edge.NewBatchPointMessage(
			models.Fields{e.as: ap.Value},
			tags,
			t,
		)
		if t.After(begin.Time()) {
			begin.SetTime(t)
		}
	}
	return edge.NewBufferedBatchMessage(
		begin,
		points,
		edge.NewEndBatchMessage(),
	)
}

func convertIntegerPoint(
	name string,
	p edge.FieldsTagsTimeGetter,
	field string,
	isSimpleSelector bool,
	topBottomInfo *pipeline.TopBottomCallInfo,
) (*influxql.IntegerPoint, error) {
	value, ok := p.Fields()[field]
	if !ok {
		return nil, fmt.Errorf("field %s missing from point cannot aggregate", field)
	}
	typed, ok := value.(int64)
	if !ok {
		return nil, fmt.Errorf("field %s has wrong type: got %T exp int64", field, value)
	}
	ap := &influxql.IntegerPoint{
		Name:  name,
		Tags:  influxql.NewTags(p.Tags()),
		Time:  p.Time().UnixNano(),
		Value: typed,
	}
	if topBottomInfo != nil {
		// We need to populate the Aux fields
		integerPopulateAuxFieldsAndTags(ap, topBottomInfo.FieldsAndTags, p.Fields(), p.Tags())
	}

	if isSimpleSelector {
		ap.Aux = []interface{}{p.Tags(), p.Fields()}
	}

	return ap, nil
}

type integerPointAggregator struct {
	field            string
	topBottomInfo    *pipeline.TopBottomCallInfo
	isSimpleSelector bool
	aggregator       influxql.IntegerPointAggregator
}

func integerPopulateAuxFieldsAndTags(ap *influxql.IntegerPoint, fieldsAndTags []string, fields models.Fields, tags models.Tags) {
	ap.Aux = make([]interface{}, len(fieldsAndTags))
	for i, name := range fieldsAndTags {
		if f, ok := fields[name]; ok {
			ap.Aux[i] = f
		} else {
			ap.Aux[i] = tags[name]
		}
	}
}

func (a *integerPointAggregator) AggregatePoint(name string, p edge.FieldsTagsTimeGetter) error {
	ap, err := convertIntegerPoint(name, p, a.field, a.isSimpleSelector, a.topBottomInfo)
	if err != nil {
		return nil
	}
	a.aggregator.AggregateInteger(ap)
	return nil
}

type integerPointEmitter struct {
	baseReduceContext
	emitter          influxql.IntegerPointEmitter
	isSimpleSelector bool
	byName           bool
}

func (e *integerPointEmitter) EmitPoint() (edge.PointMessage, error) {
	slice := e.emitter.Emit()
	if len(slice) != 1 {
		return nil, nil
	}
	ap := slice[0]
	var t time.Time
	if e.pointTimes {
		if ap.Time == influxql.ZeroTime {
			t = e.time
		} else {
			t = time.Unix(0, ap.Time).UTC()
		}
	} else {
		t = e.time
	}

	var fields models.Fields
	var tags models.Tags
	if e.isSimpleSelector {
		tags = ap.Aux[0].(models.Tags)
		fields = ap.Aux[1].(models.Fields)
		if e.as != e.field {
			fields = fields.Copy()
			fields[e.as] = fields[e.field]
			delete(fields, e.field)
		}
	} else {
		tags = e.groupInfo.Tags
		fields = map[string]interface{}{e.as: ap.Value}
	}

	return edge.NewPointMessage(
		e.name, "", "",
		e.groupInfo.Dimensions,
		fields,
		tags,
		t,
	), nil
}

func (e *integerPointEmitter) EmitBatch() edge.BufferedBatchMessage {
	slice := e.emitter.Emit()
	begin := edge.NewBeginBatchMessage(
		e.name,
		e.groupInfo.Tags,
		e.groupInfo.Dimensions.ByName,
		e.time,
		len(slice),
	)
	points := make([]edge.BatchPointMessage, len(slice))
	var t time.Time
	for i, ap := range slice {
		if e.pointTimes {
			if ap.Time == influxql.ZeroTime {
				t = e.time
			} else {
				t = time.Unix(0, ap.Time).UTC()
			}
		} else {
			t = e.time
		}
		var tags models.Tags
		if l := len(ap.Tags.KeyValues()); l > 0 {
			// Merge batch and point specific tags
			tags = make(models.Tags, len(e.groupInfo.Tags)+l)
			for k, v := range e.groupInfo.Tags {
				tags[k] = v
			}
			for k, v := range ap.Tags.KeyValues() {
				tags[k] = v
			}
		} else {
			tags = e.groupInfo.Tags
		}
		points[i] = edge.NewBatchPointMessage(
			models.Fields{e.as: ap.Value},
			tags,
			t,
		)
		if t.After(begin.Time()) {
			begin.SetTime(t)
		}
	}
	return edge.NewBufferedBatchMessage(
		begin,
		points,
		edge.NewEndBatchMessage(),
	)
}

func convertStringPoint(
	name string,
	p edge.FieldsTagsTimeGetter,
	field string,
	isSimpleSelector bool,
	topBottomInfo *pipeline.TopBottomCallInfo,
) (*influxql.StringPoint, error) {
	value, ok := p.Fields()[field]
	if !ok {
		return nil, fmt.Errorf("field %s missing from point cannot aggregate", field)
	}
	typed, ok := value.(string)
	if !ok {
		return nil, fmt.Errorf("field %s has wrong type: got %T exp string", field, value)
	}
	ap := &influxql.StringPoint{
		Name:  name,
		Tags:  influxql.NewTags(p.Tags()),
		Time:  p.Time().UnixNano(),
		Value: typed,
	}
	if topBottomInfo != nil {
		// We need to populate the Aux fields
		stringPopulateAuxFieldsAndTags(ap, topBottomInfo.FieldsAndTags, p.Fields(), p.Tags())
	}

	if isSimpleSelector {
		ap.Aux = []interface{}{p.Tags(), p.Fields()}
	}

	return ap, nil
}

type stringPointAggregator struct {
	field            string
	topBottomInfo    *pipeline.TopBottomCallInfo
	isSimpleSelector bool
	aggregator       influxql.StringPointAggregator
}

func stringPopulateAuxFieldsAndTags(ap *influxql.StringPoint, fieldsAndTags []string, fields models.Fields, tags models.Tags) {
	ap.Aux = make([]interface{}, len(fieldsAndTags))
	for i, name := range fieldsAndTags {
		if f, ok := fields[name]; ok {
			ap.Aux[i] = f
		} else {
			ap.Aux[i] = tags[name]
		}
	}
}

func (a *stringPointAggregator) AggregatePoint(name string, p edge.FieldsTagsTimeGetter) error {
	ap, err := convertStringPoint(name, p, a.field, a.isSimpleSelector, a.topBottomInfo)
	if err != nil {
		return nil
	}
	a.aggregator.AggregateString(ap)
	return nil
}

type stringPointEmitter struct {
	baseReduceContext
	emitter          influxql.StringPointEmitter
	isSimpleSelector bool
	byName           bool
}

func (e *stringPointEmitter) EmitPoint() (edge.PointMessage, error) {
	slice := e.emitter.Emit()
	if len(slice) != 1 {
		return nil, nil
	}
	ap := slice[0]
	var t time.Time
	if e.pointTimes {
		if ap.Time == influxql.ZeroTime {
			t = e.time
		} else {
			t = time.Unix(0, ap.Time).UTC()
		}
	} else {
		t = e.time
	}

	var fields models.Fields
	var tags models.Tags
	if e.isSimpleSelector {
		tags = ap.Aux[0].(models.Tags)
		fields = ap.Aux[1].(models.Fields)
		if e.as != e.field {
			fields = fields.Copy()
			fields[e.as] = fields[e.field]
			delete(fields, e.field)
		}
	} else {
		tags = e.groupInfo.Tags
		fields = map[string]interface{}{e.as: ap.Value}
	}

	return edge.NewPointMessage(
		e.name, "", "",
		e.groupInfo.Dimensions,
		fields,
		tags,
		t,
	), nil
}

func (e *stringPointEmitter) EmitBatch() edge.BufferedBatchMessage {
	slice := e.emitter.Emit()
	begin := edge.NewBeginBatchMessage(
		e.name,
		e.groupInfo.Tags,
		e.groupInfo.Dimensions.ByName,
		e.time,
		len(slice),
	)
	points := make([]edge.BatchPointMessage, len(slice))
	var t time.Time
	for i, ap := range slice {
		if e.pointTimes {
			if ap.Time == influxql.ZeroTime {
				t = e.time
			} else {
				t = time.Unix(0, ap.Time).UTC()
			}
		} else {
			t = e.time
		}
		var tags models.Tags
		if l := len(ap.Tags.KeyValues()); l > 0 {
			// Merge batch and point specific tags
			tags = make(models.Tags, len(e.groupInfo.Tags)+l)
			for k, v := range e.groupInfo.Tags {
				tags[k] = v
			}
			for k, v := range ap.Tags.KeyValues() {
				tags[k] = v
			}
		} else {
			tags = e.groupInfo.Tags
		}
		points[i] = edge.NewBatchPointMessage(
			models.Fields{e.as: ap.Value},
			tags,
			t,
		)
		if t.After(begin.Time()) {
			begin.SetTime(t)
		}
	}
	return edge.NewBufferedBatchMessage(
		begin,
		points,
		edge.NewEndBatchMessage(),
	)
}

func convertBooleanPoint(
	name string,
	p edge.FieldsTagsTimeGetter,
	field string,
	isSimpleSelector bool,
	topBottomInfo *pipeline.TopBottomCallInfo,
) (*influxql.BooleanPoint, error) {
	value, ok := p.Fields()[field]
	if !ok {
		return nil, fmt.Errorf("field %s missing from point cannot aggregate", field)
	}
	typed, ok := value.(bool)
	if !ok {
		return nil, fmt.Errorf("field %s has wrong type: got %T exp bool", field, value)
	}
	ap := &influxql.BooleanPoint{
		Name:  name,
		Tags:  influxql.NewTags(p.Tags()),
		Time:  p.Time().UnixNano(),
		Value: typed,
	}
	if topBottomInfo != nil {
		// We need to populate the Aux fields
		booleanPopulateAuxFieldsAndTags(ap, topBottomInfo.FieldsAndTags, p.Fields(), p.Tags())
	}

	if isSimpleSelector {
		ap.Aux = []interface{}{p.Tags(), p.Fields()}
	}

	return ap, nil
}

type booleanPointAggregator struct {
	field            string
	topBottomInfo    *pipeline.TopBottomCallInfo
	isSimpleSelector bool
	aggregator       influxql.BooleanPointAggregator
}

func booleanPopulateAuxFieldsAndTags(ap *influxql.BooleanPoint, fieldsAndTags []string, fields models.Fields, tags models.Tags) {
	ap.Aux = make([]interface{}, len(fieldsAndTags))
	for i, name := range fieldsAndTags {
		if f, ok := fields[name]; ok {
			ap.Aux[i] = f
		} else {
			ap.Aux[i] = tags[name]
		}
	}
}

func (a *booleanPointAggregator) AggregatePoint(name string, p edge.FieldsTagsTimeGetter) error {
	ap, err := convertBooleanPoint(name, p, a.field, a.isSimpleSelector, a.topBottomInfo)
	if err != nil {
		return nil
	}
	a.aggregator.AggregateBoolean(ap)
	return nil
}

type booleanPointEmitter struct {
	baseReduceContext
	emitter          influxql.BooleanPointEmitter
	isSimpleSelector bool
	byName           bool
}

func (e *booleanPointEmitter) EmitPoint() (edge.PointMessage, error) {
	slice := e.emitter.Emit()
	if len(slice) != 1 {
		return nil, nil
	}
	ap := slice[0]
	var t time.Time
	if e.pointTimes {
		if ap.Time == influxql.ZeroTime {
			t = e.time
		} else {
			t = time.Unix(0, ap.Time).UTC()
		}
	} else {
		t = e.time
	}

	var fields models.Fields
	var tags models.Tags
	if e.isSimpleSelector {
		tags = ap.Aux[0].(models.Tags)
		fields = ap.Aux[1].(models.Fields)
		if e.as != e.field {
			fields = fields.Copy()
			fields[e.as] = fields[e.field]
			delete(fields, e.field)
		}
	} else {
		tags = e.groupInfo.Tags
		fields = map[string]interface{}{e.as: ap.Value}
	}

	return edge.NewPointMessage(
		e.name, "", "",
		e.groupInfo.Dimensions,
		fields,
		tags,
		t,
	), nil
}

func (e *booleanPointEmitter) EmitBatch() edge.BufferedBatchMessage {
	slice := e.emitter.Emit()
	begin := edge.NewBeginBatchMessage(
		e.name,
		e.groupInfo.Tags,
		e.groupInfo.Dimensions.ByName,
		e.time,
		len(slice),
	)
	points := make([]edge.BatchPointMessage, len(slice))
	var t time.Time
	for i, ap := range slice {
		if e.pointTimes {
			if ap.Time == influxql.ZeroTime {
				t = e.time
			} else {
				t = time.Unix(0, ap.Time).UTC()
			}
		} else {
			t = e.time
		}
		var tags models.Tags
		if l := len(ap.Tags.KeyValues()); l > 0 {
			// Merge batch and point specific tags
			tags = make(models.Tags, len(e.groupInfo.Tags)+l)
			for k, v := range e.groupInfo.Tags {
				tags[k] = v
			}
			for k, v := range ap.Tags.KeyValues() {
				tags[k] = v
			}
		} else {
			tags = e.groupInfo.Tags
		}
		points[i] = edge.NewBatchPointMessage(
			models.Fields{e.as: ap.Value},
			tags,
			t,
		)
		if t.After(begin.Time()) {
			begin.SetTime(t)
		}
	}
	return edge.NewBufferedBatchMessage(
		begin,
		points,
		edge.NewEndBatchMessage(),
	)
}

// floatReduceContext uses composition to implement the reduceContext interface
type floatReduceContext struct {
	floatPointAggregator
	floatPointEmitter
}

// floatIntegerReduceContext uses composition to implement the reduceContext interface
type floatIntegerReduceContext struct {
	floatPointAggregator
	integerPointEmitter
}

// floatStringReduceContext uses composition to implement the reduceContext interface
type floatStringReduceContext struct {
	floatPointAggregator
	stringPointEmitter
}

// floatBooleanReduceContext uses composition to implement the reduceContext interface
type floatBooleanReduceContext struct {
	floatPointAggregator
	booleanPointEmitter
}

// integerFloatReduceContext uses composition to implement the reduceContext interface
type integerFloatReduceContext struct {
	integerPointAggregator
	floatPointEmitter
}

// integerReduceContext uses composition to implement the reduceContext interface
type integerReduceContext struct {
	integerPointAggregator
	integerPointEmitter
}

// integerStringReduceContext uses composition to implement the reduceContext interface
type integerStringReduceContext struct {
	integerPointAggregator
	stringPointEmitter
}

// integerBooleanReduceContext uses composition to implement the reduceContext interface
type integerBooleanReduceContext struct {
	integerPointAggregator
	booleanPointEmitter
}

// stringFloatReduceContext uses composition to implement the reduceContext interface
type stringFloatReduceContext struct {
	stringPointAggregator
	floatPointEmitter
}

// stringIntegerReduceContext uses composition to implement the reduceContext interface
type stringIntegerReduceContext struct {
	stringPointAggregator
	integerPointEmitter
}

// stringReduceContext uses composition to implement the reduceContext interface
type stringReduceContext struct {
	stringPointAggregator
	stringPointEmitter
}

// stringBooleanReduceContext uses composition to implement the reduceContext interface
type stringBooleanReduceContext struct {
	stringPointAggregator
	booleanPointEmitter
}

// booleanFloatReduceContext uses composition to implement the reduceContext interface
type booleanFloatReduceContext struct {
	booleanPointAggregator
	floatPointEmitter
}

// booleanIntegerReduceContext uses composition to implement the reduceContext interface
type booleanIntegerReduceContext struct {
	booleanPointAggregator
	integerPointEmitter
}

// booleanStringReduceContext uses composition to implement the reduceContext interface
type booleanStringReduceContext struct {
	booleanPointAggregator
	stringPointEmitter
}

// booleanReduceContext uses composition to implement the reduceContext interface
type booleanReduceContext struct {
	booleanPointAggregator
	booleanPointEmitter
}

func determineReduceContextCreateFn(method string, kind reflect.Kind, rc pipeline.ReduceCreater) (fn createReduceContextFunc, err error) {
	switch kind {

	case reflect.Float64:
		switch {

		case rc.CreateFloatReducer != nil:
			fn = func(c baseReduceContext) reduceContext {
				a, e := rc.CreateFloatReducer()
				return &floatReduceContext{
					floatPointAggregator: floatPointAggregator{
						field:            c.field,
						topBottomInfo:    rc.TopBottomCallInfo,
						isSimpleSelector: rc.IsSimpleSelector,
						aggregator:       a,
					},
					floatPointEmitter: floatPointEmitter{
						baseReduceContext: c,
						emitter:           e,
						isSimpleSelector:  rc.IsSimpleSelector,
					},
				}
			}

		case rc.CreateFloatIntegerReducer != nil:
			fn = func(c baseReduceContext) reduceContext {
				a, e := rc.CreateFloatIntegerReducer()
				return &floatIntegerReduceContext{
					floatPointAggregator: floatPointAggregator{
						field:            c.field,
						topBottomInfo:    rc.TopBottomCallInfo,
						isSimpleSelector: rc.IsSimpleSelector,
						aggregator:       a,
					},
					integerPointEmitter: integerPointEmitter{
						baseReduceContext: c,
						emitter:           e,
						isSimpleSelector:  rc.IsSimpleSelector,
					},
				}
			}

		case rc.CreateFloatStringReducer != nil:
			fn = func(c baseReduceContext) reduceContext {
				a, e := rc.CreateFloatStringReducer()
				return &floatStringReduceContext{
					floatPointAggregator: floatPointAggregator{
						field:            c.field,
						topBottomInfo:    rc.TopBottomCallInfo,
						isSimpleSelector: rc.IsSimpleSelector,
						aggregator:       a,
					},
					stringPointEmitter: stringPointEmitter{
						baseReduceContext: c,
						emitter:           e,
						isSimpleSelector:  rc.IsSimpleSelector,
					},
				}
			}

		case rc.CreateFloatBooleanReducer != nil:
			fn = func(c baseReduceContext) reduceContext {
				a, e := rc.CreateFloatBooleanReducer()
				return &floatBooleanReduceContext{
					floatPointAggregator: floatPointAggregator{
						field:            c.field,
						topBottomInfo:    rc.TopBottomCallInfo,
						isSimpleSelector: rc.IsSimpleSelector,
						aggregator:       a,
					},
					booleanPointEmitter: booleanPointEmitter{
						baseReduceContext: c,
						emitter:           e,
						isSimpleSelector:  rc.IsSimpleSelector,
					},
				}
			}

		default:
			err = fmt.Errorf("cannot apply %s to float64 field", method)
		}

	case reflect.Int64:
		switch {

		case rc.CreateIntegerFloatReducer != nil:
			fn = func(c baseReduceContext) reduceContext {
				a, e := rc.CreateIntegerFloatReducer()
				return &integerFloatReduceContext{
					integerPointAggregator: integerPointAggregator{
						field:            c.field,
						topBottomInfo:    rc.TopBottomCallInfo,
						isSimpleSelector: rc.IsSimpleSelector,
						aggregator:       a,
					},
					floatPointEmitter: floatPointEmitter{
						baseReduceContext: c,
						emitter:           e,
						isSimpleSelector:  rc.IsSimpleSelector,
					},
				}
			}

		case rc.CreateIntegerReducer != nil:
			fn = func(c baseReduceContext) reduceContext {
				a, e := rc.CreateIntegerReducer()
				return &integerReduceContext{
					integerPointAggregator: integerPointAggregator{
						field:            c.field,
						topBottomInfo:    rc.TopBottomCallInfo,
						isSimpleSelector: rc.IsSimpleSelector,
						aggregator:       a,
					},
					integerPointEmitter: integerPointEmitter{
						baseReduceContext: c,
						emitter:           e,
						isSimpleSelector:  rc.IsSimpleSelector,
					},
				}
			}

		case rc.CreateIntegerStringReducer != nil:
			fn = func(c baseReduceContext) reduceContext {
				a, e := rc.CreateIntegerStringReducer()
				return &integerStringReduceContext{
					integerPointAggregator: integerPointAggregator{
						field:            c.field,
						topBottomInfo:    rc.TopBottomCallInfo,
						isSimpleSelector: rc.IsSimpleSelector,
						aggregator:       a,
					},
					stringPointEmitter: stringPointEmitter{
						baseReduceContext: c,
						emitter:           e,
						isSimpleSelector:  rc.IsSimpleSelector,
					},
				}
			}

		case rc.CreateIntegerBooleanReducer != nil:
			fn = func(c baseReduceContext) reduceContext {
				a, e := rc.CreateIntegerBooleanReducer()
				return &integerBooleanReduceContext{
					integerPointAggregator: integerPointAggregator{
						field:            c.field,
						topBottomInfo:    rc.TopBottomCallInfo,
						isSimpleSelector: rc.IsSimpleSelector,
						aggregator:       a,
					},
					booleanPointEmitter: booleanPointEmitter{
						baseReduceContext: c,
						emitter:           e,
						isSimpleSelector:  rc.IsSimpleSelector,
					},
				}
			}

		default:
			err = fmt.Errorf("cannot apply %s to int64 field", method)
		}

	case reflect.String:
		switch {

		case rc.CreateStringFloatReducer != nil:
			fn = func(c baseReduceContext) reduceContext {
				a, e := rc.CreateStringFloatReducer()
				return &stringFloatReduceContext{
					stringPointAggregator: stringPointAggregator{
						field:            c.field,
						topBottomInfo:    rc.TopBottomCallInfo,
						isSimpleSelector: rc.IsSimpleSelector,
						aggregator:       a,
					},
					floatPointEmitter: floatPointEmitter{
						baseReduceContext: c,
						emitter:           e,
						isSimpleSelector:  rc.IsSimpleSelector,
					},
				}
			}

		case rc.CreateStringIntegerReducer != nil:
			fn = func(c baseReduceContext) reduceContext {
				a, e := rc.CreateStringIntegerReducer()
				return &stringIntegerReduceContext{
					stringPointAggregator: stringPointAggregator{
						field:            c.field,
						topBottomInfo:    rc.TopBottomCallInfo,
						isSimpleSelector: rc.IsSimpleSelector,
						aggregator:       a,
					},
					integerPointEmitter: integerPointEmitter{
						baseReduceContext: c,
						emitter:           e,
						isSimpleSelector:  rc.IsSimpleSelector,
					},
				}
			}

		case rc.CreateStringReducer != nil:
			fn = func(c baseReduceContext) reduceContext {
				a, e := rc.CreateStringReducer()
				return &stringReduceContext{
					stringPointAggregator: stringPointAggregator{
						field:            c.field,
						topBottomInfo:    rc.TopBottomCallInfo,
						isSimpleSelector: rc.IsSimpleSelector,
						aggregator:       a,
					},
					stringPointEmitter: stringPointEmitter{
						baseReduceContext: c,
						emitter:           e,
						isSimpleSelector:  rc.IsSimpleSelector,
					},
				}
			}

		case rc.CreateStringBooleanReducer != nil:
			fn = func(c baseReduceContext) reduceContext {
				a, e := rc.CreateStringBooleanReducer()
				return &stringBooleanReduceContext{
					stringPointAggregator: stringPointAggregator{
						field:            c.field,
						topBottomInfo:    rc.TopBottomCallInfo,
						isSimpleSelector: rc.IsSimpleSelector,
						aggregator:       a,
					},
					booleanPointEmitter: booleanPointEmitter{
						baseReduceContext: c,
						emitter:           e,
						isSimpleSelector:  rc.IsSimpleSelector,
					},
				}
			}

		default:
			err = fmt.Errorf("cannot apply %s to string field", method)
		}

	case reflect.Bool:
		switch {

		case rc.CreateBooleanFloatReducer != nil:
			fn = func(c baseReduceContext) reduceContext {
				a, e := rc.CreateBooleanFloatReducer()
				return &booleanFloatReduceContext{
					booleanPointAggregator: booleanPointAggregator{
						field:            c.field,
						topBottomInfo:    rc.TopBottomCallInfo,
						isSimpleSelector: rc.IsSimpleSelector,
						aggregator:       a,
					},
					floatPointEmitter: floatPointEmitter{
						baseReduceContext: c,
						emitter:           e,
						isSimpleSelector:  rc.IsSimpleSelector,
					},
				}
			}

		case rc.CreateBooleanIntegerReducer != nil:
			fn = func(c baseReduceContext) reduceContext {
				a, e := rc.CreateBooleanIntegerReducer()
				return &booleanIntegerReduceContext{
					booleanPointAggregator: booleanPointAggregator{
						field:            c.field,
						topBottomInfo:    rc.TopBottomCallInfo,
						isSimpleSelector: rc.IsSimpleSelector,
						aggregator:       a,
					},
					integerPointEmitter: integerPointEmitter{
						baseReduceContext: c,
						emitter:           e,
						isSimpleSelector:  rc.IsSimpleSelector,
					},
				}
			}

		case rc.CreateBooleanStringReducer != nil:
			fn = func(c baseReduceContext) reduceContext {
				a, e := rc.CreateBooleanStringReducer()
				return &booleanStringReduceContext{
					booleanPointAggregator: booleanPointAggregator{
						field:            c.field,
						topBottomInfo:    rc.TopBottomCallInfo,
						isSimpleSelector: rc.IsSimpleSelector,
						aggregator:       a,
					},
					stringPointEmitter: stringPointEmitter{
						baseReduceContext: c,
						emitter:           e,
						isSimpleSelector:  rc.IsSimpleSelector,
					},
				}
			}

		case rc.CreateBooleanReducer != nil:
			fn = func(c baseReduceContext) reduceContext {
				a, e := rc.CreateBooleanReducer()
				return &booleanReduceContext{
					booleanPointAggregator: booleanPointAggregator{
						field:            c.field,
						topBottomInfo:    rc.TopBottomCallInfo,
						isSimpleSelector: rc.IsSimpleSelector,
						aggregator:       a,
					},
					booleanPointEmitter: booleanPointEmitter{
						baseReduceContext: c,
						emitter:           e,
						isSimpleSelector:  rc.IsSimpleSelector,
					},
				}
			}

		default:
			err = fmt.Errorf("cannot apply %s to bool field", method)
		}

	default:
		err = fmt.Errorf("invalid field kind: %v", kind)
	}
	return
}
