Mercurial > gemma
diff pkg/controllers/bottlenecks.go @ 3027:84e6577a474b
Fairway availability: More robust time and value interpolations including corner cases. Still TODO: Distribute to classes.
author | Sascha L. Teichmann <sascha.teichmann@intevation.de> |
---|---|
date | Fri, 12 Apr 2019 12:11:40 +0200 |
parents | 7c301ff449bc |
children | 7f12a87c56ff |
line wrap: on
line diff
--- a/pkg/controllers/bottlenecks.go Fri Apr 12 11:49:08 2019 +0200 +++ b/pkg/controllers/bottlenecks.go Fri Apr 12 12:11:40 2019 +0200 @@ -107,79 +107,86 @@ return b } -func interpolate( - m1, m2 *availMeasurement, - diff time.Duration, -) int { - tdiff := m2.when.Sub(m1.when) - - // f(0) = m1.value - // f(tdiff) = m2.value - // f(x) = m*x + b - // m1.value = m*0 + b <=> b = m1.value - // m2.value = m*tdiff + b <=> m = (m2.value - b)/tdiff - // f(diff) = diff*(m2.value - m1.value)/tdiff + m1.value - - return int(diff*time.Duration(m2.value-m1.value)/tdiff) + m1.value -} - func classifyAvailMeasurements( from, to time.Time, measurements []availMeasurement, classes []availReferenceValue, ) []time.Duration { - results := make([]time.Duration, len(classes)+2) - - if from.Before(measurements[0].when) { - results[len(results)-1] = measurements[0].when.Sub(from) - from = measurements[0].when + type classValues struct { + when time.Time + kind common.ValueRangeKind } - if to.After(measurements[len(measurements)-1].when) { - results[len(results)-1] += to.Sub(measurements[len(measurements)-1].when) - to = measurements[len(measurements)-1].when + var invalid time.Duration + + cvs := make([]classValues, len(classes)) + + classify := func(v func(float64) (time.Time, common.ValueRangeKind)) { + for j := range classes { + cvs[j].when, cvs[j].kind = v(float64(classes[j].value)) + } } + valInt := func(p1, p2 *availMeasurement) func(time.Time) (float64, common.ValueRangeKind) { + return common.InterpolateValueByTime( + p1.when, float64(p1.value), + p2.when, float64(p2.value), + ) + } + +pairs: for i := 0; i < len(measurements)-1; i++ { p1 := &measurements[i] p2 := &measurements[i+1] - tdiff := p2.when.Sub(p1.when) - if tdiff <= 0 { - continue - } + + switch { + case !p2.when.After(p2.when): + // Segment invalid + continue pairs - if from.After(p2.when) || to.Before(p1.when) { - continue - } + case p1.when.After(to) || p2.when.Before(from): + // Segment complete outside. + continue pairs - if from.After(p1.when) { - tdiff2 := from.Sub(p1.when) - vf := interpolate(p1, p2, tdiff2) - p1 = &availMeasurement{when: from, value: vf} - tdiff = p2.when.Sub(from) - } + case p1.when.After(from) && p2.when.Before(to): + // (from-to) is complete inside segment. + invalid += p1.when.Sub(from) + invalid += to.Sub(p2.when) + v := valInt(p1, p2) + f, _ := v(from) + t, _ := v(to) + classify(common.InterpolateTimeByValue(from, f, to, t)) - if to.Before(p2.when) { - tdiff2 := p2.when.Sub(to) - vt := interpolate(p1, p2, tdiff2) - p2 = &availMeasurement{when: to, value: vt} - tdiff = p2.when.Sub(p1.when) - } + case p1.when.After(from): + // from is inside segment + invalid += p1.when.Sub(from) + f, _ := valInt(p1, p2)(from) + classify(common.InterpolateTimeByValue( + from, f, + p2.when, float64(p2.value), + )) - if max(p1.value, p2.value) <= classes[0].value { - results[0] += tdiff - continue + case p2.when.Before(to): + // to is inside segment + invalid += to.Sub(p2.when) + t, _ := valInt(p1, p2)(to) + classify(common.InterpolateTimeByValue( + p1.when, float64(p1.value), + to, t, + )) + + case !p1.when.Before(from) && !to.After(p2.when): + // Segment complete inside. + classify(common.InterpolateTimeByValue( + p1.when, float64(p1.value), + p2.when, float64(p2.value), + )) } - if min(p1.value, p2.value) > classes[len(classes)-1].value { - results[len(results)-2] += tdiff - continue - } - - // TODO: Do the real classes. + // TODO: Distribute to classes. } - return results + return nil } func bottleneckAvailabilty(