grokify/mogo

View on GitHub
math/mathutil/rangefloat64.go

Summary

Maintainability
B
6 hrs
Test Coverage
package mathutil

import (
    "fmt"

    "github.com/grokify/mogo/errors/errorsutil"
)

var MaxTries = int32(100000)

// RangeFloat64 creates a range with a fixed set of cells and
// returns the cell for a certain value.
type RangeFloat64 struct {
    Min   float64
    Max   float64
    Cells int32
    iter  int32
}

// CellIndexForValue returns a cell index for a requested value.
func (rng *RangeFloat64) CellIndexForValue(v float64) (int32, error) {
    err := rng.isInitialized()
    if err != nil {
        return int32(0), err
    }
    if v < rng.Min || v > rng.Max {
        return int32(0), fmt.Errorf("value (%v) out of range [%v,%v]", v, rng.Min, rng.Max)
    }
    rng.iter = 0
    return rng.binarySearch(v, 0, rng.Cells-1)
}

func (rng *RangeFloat64) binarySearch(v float64, l, r int32) (int32, error) {
    rng.iter += 1
    if rng.iter > MaxTries {
        return int32(0), fmt.Errorf("too many (%v) binary search iterations", MaxTries)
    }

    m := int32(float32(l) + (float32(r)-float32(l))/2.0)
    min, max, err := rng.CellMinMax(m)
    if err != nil {
        return int32(0), errorsutil.Wrap(err, "func CellMinMax() failed")
    }
    //fmt.Printf("{\"iter\":%v,\"val\":%v,\"l\":%v,\"r\":%v,\"m\":%v,\"minv\":%v,\"maxv\":%v}\n", rf.iter, v, l, r, m, min, max)

    if v >= min && (v < max || max == rng.Max) {
        return m, nil
    }
    if v < min {
        return rng.binarySearch(v, l, m-1)
    }
    return rng.binarySearch(v, m+1, r)
}

func (rng *RangeFloat64) isInitialized() error {
    if rng.Min > rng.Max {
        return fmt.Errorf("start (%v) is less than End (%v)", rng.Min, rng.Max)
    } else if rng.Cells <= 0 {
        return fmt.Errorf("num cells is <= 0 (%v)", rng.Cells)
    }
    return nil
}

func (rng *RangeFloat64) cellRange() (float64, error) {
    err := rng.isInitialized()
    if err != nil {
        return float64(0), err
    }
    return ((rng.Max - rng.Min) / float64(rng.Cells)), nil
}

func (rng *RangeFloat64) CellMinMax(idx int32) (float64, float64, error) {
    err := rng.isInitialized()
    if err != nil {
        return float64(0), float64(0), err
    }
    cellRange, err := rng.cellRange()
    if err != nil {
        return float64(0), float64(0), err
    }
    cellMin := float64(idx)*cellRange + rng.Min
    cellMax := rng.Max
    if idx < (rng.Cells - 1) {
        cellMax = (float64(idx)+1)*cellRange + rng.Min
    }
    return cellMin, cellMax, nil
}