/*
* Karinto Library Project
*
* This software is distributed under a zlib-style license.
* See license.txt for more information.
*/
using System;
using System.Collections.Generic;
using System.Text;
namespace Karinto
{
///
/// 3次スプライン
///
public class Spline
{
private int n;
private double[] x;
private double[] a; //0次係数
private double[] b; //1次係数
private double[] c; //2次係数
private double[] d; //3次係数
public int N
{
get { return n; }
}
public double[] XArray
{
get { return x; }
}
public double[] YArray
{
get { return a; }
}
///
/// 自然3次スプライン(両端の2階微分がゼロ)
///
/// 点列
public Spline(PointList list)
: this(list, Double.NaN, Double.NaN)
{
}
///
/// 締3次スプライン
///
/// 点列
/// 最初のxにおける1階微分(NaNで自然境界)
/// 最後のxにおける1階微分(NaNで自然境界)
public Spline(PointList list, double firstGradient, double lastGradient)
{
x = list.XArray;
a = list.YArray;
n = list.Count;
if (n < 3) throw new ArgumentException("The point list requires more than 2 points.");
b = new double[n];
c = new double[n];
d = new double[n];
double[] h = new double[n - 1];
double[] z = new double[n]; //A * c
double[] m = new double[n]; //右側
double l;
for (int i = 0; i < n - 1; i++)
{
h[i] = x[i + 1] - x[i];
}
if (Double.IsNaN(firstGradient))
{
z[0] = 0;
m[0] = 0;
}
else
{
z[0] = (3 * (a[1] - a[0]) / h[0] - 3 * firstGradient) / (2 * h[0]);
m[0] = 0.5;
}
for (int i = 1; i < n - 1; i++)
{
z[i] = 3 * (a[i + 1] - a[i]) / h[i] - 3 * (a[i] - a[i - 1]) / h[i - 1];
}
for (int i = 1; i < n - 1; i++) //左下をゼロにする
{
l = 2 * (h[i] + h[i - 1]) - h[i - 1] * m[i - 1]; // 対角成分
// lで割ることで対角成分を1にする
m[i] = h[i] / l;
z[i] = (z[i] - h[i - 1] * z[i - 1]) / l;
}
if (Double.IsNaN(lastGradient))
{
c[n - 1] = 0;
}
else
{
z[n - 1] = 3 * lastGradient - 3 * (a[n - 1] - a[n - 2]) / h[n - 2];
l = 2 * h[n - 2] - h[n - 2] * m[n - 2]; // 対角成分
c[n - 1] = (z[n - 1] - h[n - 2] * z[n - 2]) / l;
}
for (int i = n - 2; i >= 0; i--)
{
c[i] = z[i] - m[i] * c[i + 1];
b[i] = (a[i + 1] - a[i]) / h[i] - h[i] * (c[i + 1] + 2 * c[i]) / 3;
d[i] = (c[i + 1] - c[i]) / (3 * h[i]);
}
b[n - 1] = b[n - 2] + (2 * c[n - 2] + 3 * d[n - 2] * h[n - 2]) * h[n - 2];
}
public override string ToString()
{
string s = "i\tx\ta\tb\tc\td\r\n";
int i;
for (i = 0; i < x.Length - 1; i++)
{
s += "" + i + "\t" + x[i] + "\t" + a[i] + "\t" + b[i] + "\t" + c[i] + "\t" + d[i] + "\r\n";
}
s += "" + i + "\t" + x[i] + "\t" + a[i] + "\t\t\t\r\n";
return s;
}
public double GetY(double t)
{
int i;
if (x[0] > t) return a[0] - b[0] * (x[0] - t);//線形補外
if (x[n - 1] < t) return a[n - 1] + b[n - 1] * (t - x[n - 1]);//線形補外
for (i = 0; i < n - 2; i++)
{
if (x[i + 1] >= t) break;
}
double h = t - x[i];
return a[i] + (b[i] + (c[i] + d[i] * h) * h) * h;
}
public double[] GetCoefficients(int order)
{
switch (order)
{
case 0:
return a;
case 1:
return b;
case 2:
return c;
case 3:
return d;
}
throw new ArgumentException("Invalid order");
}
public PointList GetFrozenList(double step)
{
return GetFrozenList(new Range(x[0], x[n - 1]), step);
}
public PointList GetFrozenList(Range range, double step)
{
PointList list = new PointList();
if (Double.Epsilon > step) throw new ArgumentException("step is too small.");
foreach (double t in range.Step(step))
{
list.Add(new Point(t, GetY(t)));
}
return list;
}
}
}