//****************************************************************************************************** // PiecewiseLinearFunction.cs - Gbtc // // Copyright © 2016, Grid Protection Alliance. All Rights Reserved. // // Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See // the NOTICE file distributed with this work for additional information regarding copyright ownership. // The GPA licenses this file to you under the MIT License (MIT), the "License"; you may // not use this file except in compliance with the License. You may obtain a copy of the License at: // // http://opensource.org/licenses/MIT // // Unless agreed to in writing, the subject software distributed under the License is distributed on an // "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the // License for the specific language governing permissions and limitations. // // Code Modification History: // ---------------------------------------------------------------------------------------------------- // 08/20/2016 - Stephen C. Wills // Generated original version of source code. // //****************************************************************************************************** using System; namespace GSF.NumericalAnalysis.Interpolation { /// /// Represents a piecewise linear function for /// calculating values between pivot points. /// /// /// /// The conversion function returned by this class uses a /// binary search algorithm to find the appropriate line segment /// to use for the calculation. Therefore, the domain must /// be specified either in increasing or decreasing order. /// /// /// /// Here is an example of how to use this class. /// /// /// /// Func<double, double> piecewiseLinearFunc = new PiecewiseLinearFunction() /// .SetDomain(-1, 0, 1) /// .SetRange(0, 1, 0); /// /// Console.WriteLine(piecewiseLinearFunc(-10)); // -9 /// Console.WriteLine(piecewiseLinearFunc(-1)); // 0 /// Console.WriteLine(piecewiseLinearFunc(-0.5)); // 0.5 /// Console.WriteLine(piecewiseLinearFunc(0)); // 1 /// Console.WriteLine(piecewiseLinearFunc(0.5)); // 0.5 /// Console.WriteLine(piecewiseLinearFunc(1)); // 0 /// Console.WriteLine(piecewiseLinearFunc(10)); // -9 /// /// public class PiecewiseLinearFunction { #region [ Members ] // Fields private Func m_converter; private double[] m_domain; private double[] m_range; #endregion #region [ Properties ] /// /// Gets the x-values of the pivot points in the piecewise linear function. /// public double[] Domain { get { return m_domain; } } /// /// Gets the y-values of the pivot points in the piecewise linear function. /// public double[] Range { get { return m_range; } } #endregion #region [ Methods ] /// /// Sets the x-values of the pivot points in the piecewise linear function. /// /// The x-values of the pivot points. /// A reference to the piecewise linear function. public PiecewiseLinearFunction SetDomain(params double[] domain) { m_converter = null; m_domain = domain; return this; } /// /// Sets the y-values of the pivot points in the piecewise linear function. /// /// The y-values of the pivot points. /// A refernce to the picewise linear function. public PiecewiseLinearFunction SetRange(params double[] range) { m_converter = null; m_range = range; return this; } private Func GetConverter() { double[] domain = m_domain ?? new double[0]; double[] range = m_range ?? new double[0]; if (domain.Length != range.Length) throw new InvalidOperationException($"Domain of size {domain.Length} does not match range of size {range.Length}."); if (domain.Length < 2) throw new InvalidOperationException($"At least two pivot points must be defined. Defined: {domain.Length}"); return m_converter ?? (m_converter = x => { int i = 0; int j = domain.Length - 1; double di = domain[i]; double dj = domain[j]; while (j - i > 1) { int mid = (i + j) / 2; double dmid = domain[mid]; if ((di < dmid && x <= dmid) || (di > dmid && x >= dmid)) { j = mid; dj = dmid; } else { i = mid; di = dmid; } } if (di == dj) return range[i]; double ri = range[i]; double rj = range[j]; return (((x - di) / (dj - di)) * (rj - ri)) + ri; }); } #endregion #region [ Operators ] /// /// Converts the object to a /// to start converting values. /// /// The piecewise linear function to be converted. /// /// the size of the domain does not equal the size of the range /// - or - /// less than two pivot points are defined /// public static implicit operator Func(PiecewiseLinearFunction func) { return func.GetConverter(); } #endregion } }