/*
* easy_elliptic.hpp --- boost を簡単に使って、K(k),sn(u;k),cn(u;k),dn(u;k)計算
* よく考えてみたら、agm.cpp の内容を移せば、boost に依存しないように出来る。
* つまり全く自前の計算で済ませられる。これは驚いた。
*/
#include <boost/math/special_functions.hpp>
static double K(double k)
{
return boost::math::ellint_1(k);
}
static double F(double k, double phi)
{
return boost::math::ellint_1(k, phi);
}
static double sn(double k, double x)
{
return boost::math::jacobi_sn(k, x);
}
static double cn(double k, double x)
{
return boost::math::jacobi_cn(k, x);
}
static double dn(double k, double x)
{
return boost::math::jacobi_dn(k, x);
}
// 複素関数として sn()
static std::complex<double> jacobi_sn(double k, std::complex<double> z)
{
double kp,m,x,y,s,c,d,s1,c1,d1,den;
x = z.real(); y = z.imag(); kp = sqrt(1 - k * k); m = k * k;
#ifdef OLD
s = boost::math::jacobi_sn(k, x);
c = boost::math::jacobi_cn(k, x);
d = boost::math::jacobi_dn(k, x);
s1 = boost::math::jacobi_sn(kp, y);
c1 = boost::math::jacobi_cn(kp, y);
d1 = boost::math::jacobi_dn(kp, y);
#else
s = boost::math::jacobi_elliptic(k, x, &c, &d);
s1 = boost::math::jacobi_elliptic(kp, y, &c1, &d1);
#endif
den = c1 * c1 + m * s * s * s1 * s1; // denominator: 分母
return std::complex<double>(s * d1 / den, c * d * s1 * c1 / den);
}
// 複素数版 Jacobi の sn(), cn(), dn()
static std::complex<double> jacobi_elliptic(double k,
std::complex<double> z,
std::complex<double> *cn,
std::complex<double> *dn)
{
double kp,m,x,y,s,c,d,s1,c1,d1,den;
std::complex<double> sn;
x = z.real(); y = z.imag(); kp = sqrt(1 - k * k); m = k * k;
#ifdef OLD
s = boost::math::jacobi_sn(k, x);
c = boost::math::jacobi_cn(k, x);
d = boost::math::jacobi_dn(k, x);
s1 = boost::math::jacobi_sn(kp, y);
c1 = boost::math::jacobi_cn(kp, y);
d1 = boost::math::jacobi_dn(kp, y);
#else
s = boost::math::jacobi_elliptic(k, x, &c, &d);
s1 = boost::math::jacobi_elliptic(kp, y, &c1, &d1);
#endif
den = c1 * c1 + m * s * s * s1 * s1; // denominator: 分母
sn = std::complex<double>(s * d1 / den, c * d * s1 * c1 / den);
*cn = std::complex<double>(c * c1 / den, - s * d * s1 * d1 / den);
*dn = std::complex<double>(d * c1 * d1 / den, - m * s * c * s1 / den);
return sn;
}
// まだ十分テストしていないけれど
static double my_jacobi_elliptic(double k, double u, double *cn, double *dn)
{
int n, maxn = 10, N, success;
double kp, a[maxn+1], b[maxn+1], c[maxn+1], phi[maxn+1], eps, myK, pi;
success = 0;
pi = 4.0 * atan(1.0);
// AGM
eps = 1e-15;
kp = sqrt(1 - k * k);
a[0] = 1.0; b[0] = kp; c[0] = k;
for (n = 1; n <= maxn; n++) {
a[n] = (a[n-1] + b[n-1]) / 2;
b[n] = sqrt(a[n-1] * b[n-1]);
c[n] = (a[n-1] - b[n-1]) / 2;
if (c[n] < eps * a[n]) {
success = 1;
break;
}
}
if (!success) {
std::cerr << "収束しませんでした。" << std::endl;
}
N = n;
// 完全楕円積分
myK = pi / (2 * a[N]); // これは返さない...
// 楕円関数の amplitude の計算
phi[N] = (1 << n) * a[N] * u; // φN
for (n = N; n >= 1; n--)
phi[n-1] = (phi[n] + asin(c[n] * sin(phi[n]) / a[n])) / 2;
//
*cn = cos(phi[0]);
*dn = cos(phi[0]) / cos(phi[1]-phi[0]);
return sin(phi[0]);
}