//******************************************************************************************************
// 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
}
}