hll/cubic_interpolation.go (101 lines of code) (raw):

/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package hll import "fmt" // UsingXAndYTables returns the cubic interpolation using the X and Y tables. func usingXAndYTables(xArr []float64, yArr []float64, x float64) (float64, error) { if len(xArr) < 4 || len(xArr) != len(yArr) { return 0, fmt.Errorf("X value out of range: %f", x) } if x == xArr[len(xArr)-1] { return yArr[len(yArr)-1], nil // corer case } offset, err := findStraddle(xArr, x) //uses recursion if err != nil { return 0, err } if (offset < 0) || (offset > (len(xArr) - 2)) { return 0, fmt.Errorf("offset out of range: %d", offset) } if offset == 0 { return interpolateUsingXAndYTables(xArr, yArr, offset, x), nil // corner case } if offset == len(xArr)-2 { return interpolateUsingXAndYTables(xArr, yArr, offset-2, x), nil // corner case } return interpolateUsingXAndYTables(xArr, yArr, offset-1, x), nil } func interpolateUsingXAndYTables(xArr []float64, yArr []float64, offset int, x float64) float64 { return cubicInterpolate( xArr[offset], yArr[offset], xArr[offset+1], yArr[offset+1], xArr[offset+2], yArr[offset+2], xArr[offset+3], yArr[offset+3], x) } func usingXArrAndYStride(xArr []float64, yStride float64, x float64) (float64, error) { xArrLen := len(xArr) xArrLenM1 := xArrLen - 1 if xArrLen < 4 || x < xArr[0] || x > xArr[xArrLenM1] { return 0, fmt.Errorf("X value out of range: %f", x) } if x == xArr[xArrLenM1] { return yStride * float64(xArrLenM1), nil // corner case } offset, err := findStraddle(xArr, x) //uses recursion if err != nil { return 0, err } xArrLenM2 := xArrLen - 2 if (offset < 0) || (offset > xArrLenM2) { return 0, fmt.Errorf("offset out of range: %d", offset) } if offset == 0 { return interpolateUsingXArrAndYStride(xArr, yStride, offset, x), nil // corner case } if offset == xArrLenM2 { return interpolateUsingXArrAndYStride(xArr, yStride, offset-2, x), nil // corner case } return interpolateUsingXArrAndYStride(xArr, yStride, offset-1, x), nil } // interpolateUsingXArrAndYStride interpolates using the X array and the Y stride. func interpolateUsingXArrAndYStride(xArr []float64, yStride float64, offset int, x float64) float64 { return cubicInterpolate(xArr[offset+0], yStride*float64(offset+0), xArr[offset+1], yStride*float64(offset+1), xArr[offset+2], yStride*float64(offset+2), xArr[offset+3], yStride*float64(offset+3), x) } // cubicInterpolate interpolates using the cubic curve that passes through the four given points, using the // Lagrange interpolation formula. func cubicInterpolate(x0 float64, y0 float64, x1 float64, y1 float64, x2 float64, y2 float64, x3 float64, y3 float64, x float64) float64 { l0Numer := (x - x1) * (x - x2) * (x - x3) l1Numer := (x - x0) * (x - x2) * (x - x3) l2Numer := (x - x0) * (x - x1) * (x - x3) l3Numer := (x - x0) * (x - x1) * (x - x2) l0Denom := (x0 - x1) * (x0 - x2) * (x0 - x3) l1Denom := (x1 - x0) * (x1 - x2) * (x1 - x3) l2Denom := (x2 - x0) * (x2 - x1) * (x2 - x3) l3Denom := (x3 - x0) * (x3 - x1) * (x3 - x2) term0 := (y0 * l0Numer) / l0Denom term1 := (y1 * l1Numer) / l1Denom term2 := (y2 * l2Numer) / l2Denom term3 := (y3 * l3Numer) / l3Denom return term0 + term1 + term2 + term3 } // findStraddle returns the index of the largest value in the array that is less than or equal to the given value. func findStraddle(xArr []float64, x float64) (int, error) { if len(xArr) < 2 || x < xArr[0] || x > xArr[len(xArr)-1] { return 0, fmt.Errorf("X value out of range: %f", x) } return recursiveFindStraddle(xArr, 0, len(xArr)-1, x) } // recursiveFindStraddle returns the index of the largest value in the array that is less than or equal to the given value. func recursiveFindStraddle(xArr []float64, left int, right int, x float64) (int, error) { if left >= right { return 0, fmt.Errorf("left >= right: %d >= %d", left, right) } if xArr[left] > x || x >= xArr[right] { return 0, fmt.Errorf("X value out of range: %f", x) } if left+1 == right { return left, nil } middle := left + ((right - left) / 2) if xArr[middle] <= x { return recursiveFindStraddle(xArr, middle, right, x) } else { return recursiveFindStraddle(xArr, left, middle, x) } }