func (m *Map) ParallelReduceFloat64Product(reduce func(map[interface{}]interface{}) float64) float64 {
	var recur func(splits []Split) float64
	recur = func(splits []Split) float64 {
		if len(splits) < 2 {
			return splits[0].reduceFloat64(reduce)
		}
		var left, right float64
		var p interface{}
		var wg sync.WaitGroup
		wg.Add(1)
		switch len(splits) {
		case 2:
			go func() {
				defer func() {
					p = internal.WrapPanic(recover())
					wg.Done()
				}()
				right = splits[1].reduceFloat64(reduce)
			}()
			left = splits[0].reduceFloat64(reduce)
		default:
			half := len(splits) / 2
			go func() {
				defer func() {
					p = internal.WrapPanic(recover())
					wg.Done()
				}()
				right = recur(splits[half:])
			}()
			left = recur(splits[:half])
		}
		wg.Wait()
		if p != nil {
			panic(p)
		}
		return left * right
	}
	return recur(m.splits)
}
ParallelReduceFloat64Product calls reduce for every split of m in parallel.
The results of the reduce invocations are then multiplied with each other.

ParallelReduceFloat64Product returns only when all goroutines it spawns have
terminated.

While reduce is executed on a split of m, ParallelReduceFloat64Product holds
the corresponding lock.

If one or more reduce invocations panic, the corresponding goroutines recover
the panics, and ParallelReduceFloat64Product eventually panics with the
left-most recovered panic value.

ParallelReduceFloat64Product does not necessarily correspond to any
consistent snapshot of the Map's contents: no split will be visited more than
once, but if the value for any key is stored or deleted concurrently,
ParallelReduceFloat64Product may reflect any mapping for that key from any
point during the ParallelReduceFloat64Product call.
package sync
import (
	"runtime"
	"sync"
	"github.com/exascience/pargo/internal"
	"github.com/exascience/pargo/parallel"
	"github.com/exascience/pargo/speculative"
)
type Hasher interface {
	Hash() uint64
}
type Split struct {
	sync.RWMutex
	Map map[interface{}]interface{}
}
type Map struct {
	splits []Split
}
func NewMap(size int) *Map {
	if size <= 0 {
		size = runtime.GOMAXPROCS(0)
	}
	splits := make([]Split, size)
	for i := range splits {
		splits[i].Map = make(map[interface{}]interface{})
	}
	return &Map{splits}
}
func (m *Map) Split(key Hasher) *Split {
	splits := m.splits
	return &splits[key.Hash()%uint64(len(splits))]
}
func (m *Map) Delete(key Hasher) {
	split := m.Split(key)
	split.Lock()
	delete(split.Map, key)
	split.Unlock()
}
func (m *Map) Load(key Hasher) (value interface{}, ok bool) {
	split := m.Split(key)
	split.RLock()
	value, ok = split.Map[key]
	split.RUnlock()
	return
}
func (m *Map) LoadOrStore(key Hasher, value interface{}) (actual interface{}, loaded bool) {
	split := m.Split(key)
	split.RLock()
	actual, loaded = split.Map[key]
	split.RUnlock()
	if loaded {
		return
	}
	split.Lock()
	if actual, loaded = split.Map[key]; !loaded {
		actual = value
		split.Map[key] = value
	}
	split.Unlock()
	return
}
func (m *Map) LoadOrCompute(key Hasher, computer func() interface{}) (actual interface{}, loaded bool) {
	split := m.Split(key)
	split.RLock()
	actual, loaded = split.Map[key]
	split.RUnlock()
	if loaded {
		return
	}
	value := computer()
	split.Lock()
	if actual, loaded = split.Map[key]; !loaded {
		actual = value
		split.Map[key] = actual
	}
	split.Unlock()
	return
}
func (m *Map) DeleteOrStore(key Hasher, value interface{}) (actual interface{}, deleted bool) {
	split := m.Split(key)
	split.Lock()
	if actual, deleted = split.Map[key]; deleted {
		delete(split.Map, key)
	} else {
		actual = value
		split.Map[key] = value
	}
	split.Unlock()
	return
}
func (m *Map) DeleteOrCompute(key Hasher, computer func() interface{}) (actual interface{}, deleted bool) {
	split := m.Split(key)
	split.Lock()
	if actual, deleted = split.Map[key]; deleted {
		delete(split.Map, key)
	} else {
		actual = computer()
		split.Map[key] = actual
	}
	split.Unlock()
	return
}
func (m *Map) Modify(
	key Hasher,
	modifier func(value interface{}, ok bool) (replacement interface{}, storeNotDelete bool),
) (replacement interface{}, storeNotDelete bool) {
	split := m.Split(key)
	split.Lock()
	value, ok := split.Map[key]
	if replacement, storeNotDelete = modifier(value, ok); storeNotDelete {
		split.Map[key] = replacement
	} else {
		delete(split.Map, key)
	}
	split.Unlock()
	return
}
func (split *Split) srange(f func(key, value interface{}) bool) bool {
	split.Lock()
	defer split.Unlock()
	for key, value := range split.Map {
		if !f(key, value) {
			return false
		}
	}
	return true
}
func (m *Map) Range(f func(key, value interface{}) bool) {
	splits := m.splits
	for i := 0; i < len(splits); i++ {
		if !splits[i].srange(f) {
			return
		}
	}
}
func (split *Split) parallelRange(f func(key, value interface{})) {
	split.Lock()
	defer split.Unlock()
	for key, value := range split.Map {
		f(key, value)
	}
}
func (m *Map) ParallelRange(f func(key, value interface{})) {
	splits := m.splits
	parallel.Range(0, len(splits), 0, func(low, high int) {
		for i := low; i < high; i++ {
			splits[i].parallelRange(f)
		}
	})
}
func (m *Map) SpeculativeRange(f func(key, value interface{}) bool) {
	splits := m.splits
	speculative.Range(0, len(splits), 0, func(low, high int) bool {
		for i := low; i < high; i++ {
			if !splits[i].srange(f) {
				return false
			}
		}
		return true
	})
}
func (split *Split) predicate(p func(map[interface{}]interface{}) bool) bool {
	split.Lock()
	defer split.Unlock()
	return p(split.Map)
}
func (m *Map) And(predicate func(map[interface{}]interface{}) bool) bool {
	splits := m.splits
	for i := 0; i < len(splits); i++ {
		if ok := splits[i].predicate(predicate); !ok {
			return false
		}
	}
	return true
}
func (m *Map) ParallelAnd(predicate func(map[interface{}]interface{}) bool) bool {
	splits := m.splits
	return parallel.RangeAnd(0, len(splits), 0, func(low, high int) bool {
		for i := low; i < high; i++ {
			if ok := splits[i].predicate(predicate); !ok {
				return false
			}
		}
		return true
	})
}
func (m *Map) SpeculativeAnd(predicate func(map[interface{}]interface{}) bool) bool {
	splits := m.splits
	return speculative.RangeAnd(0, len(splits), 0, func(low, high int) bool {
		for i := low; i < high; i++ {
			if ok := splits[i].predicate(predicate); !ok {
				return false
			}
		}
		return true
	})
}
func (m *Map) Or(predicate func(map[interface{}]interface{}) bool) bool {
	splits := m.splits
	for i := 0; i < len(splits); i++ {
		if ok := splits[i].predicate(predicate); ok {
			return true
		}
	}
	return false
}
func (m *Map) ParallelOr(predicate func(map[interface{}]interface{}) bool) bool {
	splits := m.splits
	return parallel.RangeOr(0, len(splits), 0, func(low, high int) bool {
		for i := low; i < high; i++ {
			if ok := splits[i].predicate(predicate); ok {
				return true
			}
		}
		return false
	})
}
func (m *Map) SpeculativeOr(predicate func(map[interface{}]interface{}) bool) bool {
	splits := m.splits
	return speculative.RangeOr(0, len(splits), 0, func(low, high int) bool {
		for i := low; i < high; i++ {
			if ok := splits[i].predicate(predicate); ok {
				return true
			}
		}
		return false
	})
}
func (split *Split) reduce(r func(map[interface{}]interface{}) interface{}) interface{} {
	split.Lock()
	defer split.Unlock()
	return r(split.Map)
}
func (m *Map) Reduce(
	reduce func(map[interface{}]interface{}) interface{},
	join func(x, y interface{}) interface{},
) interface{} {
	splits := m.splits
	result := splits[0].reduce(reduce)
	for i := 1; i < len(splits); i++ {
		result = join(result, splits[i].reduce(reduce))
	}
	return result
}
func (split *Split) reduceFloat64(r func(map[interface{}]interface{}) float64) float64 {
	split.Lock()
	defer split.Unlock()
	return r(split.Map)
}
func (m *Map) ReduceFloat64(
	reduce func(map[interface{}]interface{}) float64,
	join func(x, y float64) float64,
) float64 {
	splits := m.splits
	result := splits[0].reduceFloat64(reduce)
	for i := 1; i < len(splits); i++ {
		result = join(result, splits[i].reduceFloat64(reduce))
	}
	return result
}
func (m *Map) ReduceFloat64Sum(reduce func(map[interface{}]interface{}) float64) float64 {
	result := float64(0)
	splits := m.splits
	for i := 0; i < len(splits); i++ {
		result += splits[i].reduceFloat64(reduce)
	}
	return result
}
func (m *Map) ReduceFloat64Product(reduce func(map[interface{}]interface{}) float64) float64 {
	result := float64(1)
	splits := m.splits
	for i := 0; i < len(splits); i++ {
		result *= splits[i].reduceFloat64(reduce)
	}
	return result
}
func (split *Split) reduceInt(r func(map[interface{}]interface{}) int) int {
	split.Lock()
	defer split.Unlock()
	return r(split.Map)
}
func (m *Map) ReduceInt(
	reduce func(map[interface{}]interface{}) int,
	join func(x, y int) int,
) int {
	splits := m.splits
	result := splits[0].reduceInt(reduce)
	for i := 1; i < len(splits); i++ {
		result = join(result, splits[i].reduceInt(reduce))
	}
	return result
}
func (m *Map) ReduceIntSum(reduce func(map[interface{}]interface{}) int) int {
	result := 0
	splits := m.splits
	for i := 0; i < len(splits); i++ {
		result += splits[i].reduceInt(reduce)
	}
	return result
}
func (m *Map) ReduceIntProduct(reduce func(map[interface{}]interface{}) int) int {
	result := 1
	splits := m.splits
	for i := 0; i < len(splits); i++ {
		result *= splits[i].reduceInt(reduce)
	}
	return result
}
func (split *Split) reduceString(r func(map[interface{}]interface{}) string) string {
	split.Lock()
	defer split.Unlock()
	return r(split.Map)
}
func (m *Map) ReduceString(
	reduce func(map[interface{}]interface{}) string,
	join func(x, y string) string,
) string {
	splits := m.splits
	result := splits[0].reduceString(reduce)
	for i := 1; i < len(splits); i++ {
		result = join(result, splits[i].reduceString(reduce))
	}
	return result
}
func (m *Map) ReduceStringSum(reduce func(map[interface{}]interface{}) string) string {
	result := ""
	splits := m.splits
	for i := 0; i < len(splits); i++ {
		result += splits[i].reduceString(reduce)
	}
	return result
}
func (m *Map) ParallelReduce(
	reduce func(map[interface{}]interface{}) interface{},
	join func(x, y interface{}) interface{},
) interface{} {
	var recur func(splits []Split) interface{}
	recur = func(splits []Split) interface{} {
		if len(splits) < 2 {
			return splits[0].reduce(reduce)
		}
		var left, right interface{}
		var p interface{}
		var wg sync.WaitGroup
		wg.Add(1)
		switch len(splits) {
		case 2:
			go func() {
				defer func() {
					p = internal.WrapPanic(recover())
					wg.Done()
				}()
				right = splits[1].reduce(reduce)
			}()
			left = splits[0].reduce(reduce)
		default:
			half := len(splits) / 2
			go func() {
				defer func() {
					p = internal.WrapPanic(recover())
					wg.Done()
				}()
				right = recur(splits[half:])
			}()
			left = recur(splits[:half])
		}
		wg.Wait()
		if p != nil {
			panic(p)
		}
		return join(left, right)
	}
	return recur(m.splits)
}
func (m *Map) ParallelReduceFloat64(
	reduce func(map[interface{}]interface{}) float64,
	join func(x, y float64) float64,
) float64 {
	var recur func(splits []Split) float64
	recur = func(splits []Split) float64 {
		if len(splits) < 2 {
			return splits[0].reduceFloat64(reduce)
		}
		var left, right float64
		var p interface{}
		var wg sync.WaitGroup
		wg.Add(1)
		switch len(splits) {
		case 2:
			go func() {
				defer func() {
					p = internal.WrapPanic(recover())
					wg.Done()
				}()
				right = splits[1].reduceFloat64(reduce)
			}()
			left = splits[0].reduceFloat64(reduce)
		default:
			half := len(splits) / 2
			go func() {
				defer func() {
					p = internal.WrapPanic(recover())
					wg.Done()
				}()
				right = recur(splits[half:])
			}()
			left = recur(splits[:half])
		}
		wg.Wait()
		if p != nil {
			panic(p)
		}
		return join(left, right)
	}
	return recur(m.splits)
}
func (m *Map) ParallelReduceFloat64Sum(reduce func(map[interface{}]interface{}) float64) float64 {
	var recur func(splits []Split) float64
	recur = func(splits []Split) float64 {
		if len(splits) < 2 {
			return splits[0].reduceFloat64(reduce)
		}
		var left, right float64
		var p interface{}
		var wg sync.WaitGroup
		wg.Add(1)
		switch len(splits) {
		case 2:
			go func() {
				defer func() {
					p = internal.WrapPanic(recover())
					wg.Done()
				}()
				right = splits[1].reduceFloat64(reduce)
			}()
			left = splits[0].reduceFloat64(reduce)
		default:
			half := len(splits) / 2
			go func() {
				defer func() {
					p = internal.WrapPanic(recover())
					wg.Done()
				}()
				right = recur(splits[half:])
			}()
			left = recur(splits[:half])
		}
		wg.Wait()
		if p != nil {
			panic(p)
		}
		return left + right
	}
	return recur(m.splits)
}