たけぼーの備忘録

暇を持て余した人間の無意味なブログ

大浦版FFTをJavaに移植する

f:id:takebo_blog:20171019164554j:plain
今回は、高速な処理で有名な大浦版FFTJavaに移植してみました。

大浦版FFTとは、Cで書かれた高速フーリエ変換(FFT)で、1次元・2次元・3次元のバージョンがあります。
FFT Package 1-dim / 2-dim

ググってみると、Java移植の話題はちょくちょくあるのですが、ソースコードが載っているものが少ないので、書いてみました。

正直言いますと、こちらのソースをかなり参考にさせていただいております。
要するに、配列をずらしたりする処理を書き換えればいいのです。
大浦版FFTのJava移植

移植での変更点

Javaではポインタが使えないため、配列をそのまま引数として渡さなければなりません。

また、配列をずらす、という動作を行うための処理部分を変更しなければなりません。
Cでは例えば、配列 a [ n ] があったなら、a+2で配列aを2つずらすことができ、これをそのまま関数へ渡すことができます。しかし、Javaにはそのような機能はなく、配列を2つずらすなら、a [ n + 2 ]と書かなければなりません。
この二つの違いを直すと、正常に動くようになります。

今回移植したのは、FFT4gだけです。手抜きですいません。
間違ってたらごめんなさい。勘弁してください。
ということで、ソースコード全体はこちら。

FFT4g.java

package …;

import static java.lang.Math.atan;
import static java.lang.Math.cos;
import static java.lang.Math.sin;

public class FFT4g {
    private int[] ip;
    private double[] w;
    private int n;
    private double t[];

    FFT4g(int n) {
        this.n = n;
        ip = new int[2+(int)Math.sqrt((double)n/2.0)+1];
        w = new double[n*5/4];
        t = new double[n/2];

        ip[0] = 0;
    }

    public void cdft(int isgn, double[] a)
    {
        if (n > (ip[0] << 2)) {
            makewt(n >> 2);
        }
        if (n > 4) {
            if (isgn >= 0) {
                bitrv2(n, a);
                cftfsub(n, a);
            } else {
                bitrv2conj(n, a);
                cftbsub(n, a);
            }
        } else if (n == 4) {
            cftfsub(n, a);
        }
    }

    public void rdft(int isgn, double[] a) {
        int nw, nc;
        double xi;

        nw = ip[0];
        if (n > (nw << 2)) {
            nw = n >> 2;
            makewt(nw);
        }
        nc = ip[1];
        if (n > (nc << 2)) {
            nc = n >> 2;
            makect(nc,w, nw);
        }
        if (isgn >= 0) {
            if (n > 4) {
                bitrv2(n, a);
                cftfsub(n, a);
                rftfsub(n, a, nc, w, nw);
            } else if (n == 4) {
                cftfsub(n, a);
            }
                xi = a[0] - a[1];
                a[0] += a[1];
                a[1] = xi;
        } else {
            a[1] = 0.5 * (a[0] - a[1]);
            a[0] -= a[1];
            if (n > 4) {
                rftbsub(n, a, nc, w, nw);
                bitrv2(n, a);
                cftbsub(n, a);
            } else if (n == 4) {
                cftfsub(n, a);
            }
        }
    }


     public void ddct(int isgn, double[] a){
            int j, nw, nc;
            double xr;

            nw = ip[0];
            if (n > (nw << 2)) {
                nw = n >> 2;
                makewt(nw);
            }
            nc = ip[1];
            if (n > nc) {
                nc = n;
                makect(nc, w, nw);
                }
            if (isgn < 0) {
                xr = a[n - 1];
                for (j = n - 2; j >= 2; j -= 2) {
                    a[j + 1] = a[j] - a[j - 1];
                    a[j] += a[j - 1];
                }
                a[1] = a[0] - xr;
                a[0] += xr;
            if (n > 4) {
                rftbsub(n, a, nc, w, nw);
                bitrv2(n, a);
                cftbsub(n, a);
            } else if (n == 4) {
                cftfsub(n, a);
            }
        }
        dctsub(n, a, nc, w, nw);
        if (isgn >= 0) {
            if (n > 4) {
                bitrv2(n, a);
                cftfsub(n, a);
                rftfsub(n, a, nc, w, nw);
            } else if (n == 4) {
                cftfsub(n, a);
            }
            xr = a[0] - a[1];
            a[0] += a[1];
            for (j = 2; j < n; j += 2) {
            a[j - 1] = a[j] - a[j + 1];
            a[j] += a[j + 1];
            }
            a[n - 1] = xr;
        }
    }

    public void ddst(int isgn, double[] a)
    {
        int j, nw, nc;
        double xr;

        nw = ip[0];
        if (n > (nw << 2)) {
            nw = n >> 2;
            makewt(nw);
        }
        nc = ip[1];
        if (n > nc) {
            nc = n;
            makect(nc, w, nw);
        }
        if (isgn < 0) {
            xr = a[n - 1];
            for (j = n - 2; j >= 2; j -= 2) {
                a[j + 1] = -a[j] - a[j - 1];
                a[j] -= a[j - 1];
            }
            a[1] = a[0] + xr;
            a[0] -= xr;
            if (n > 4) {
                rftbsub(n, a, nc, w, nw);
                bitrv2(n, a);
                cftbsub(n, a);
            } else if (n == 4) {
                cftfsub(n, a);
            }
        }
        dstsub(n, a, nc, w, nw);
        if (isgn >= 0) {
            if (n > 4) {
                bitrv2(n, a);
                cftfsub(n, a);
                rftfsub(n, a, nc, w, nw);
            } else if (n == 4) {
                cftfsub(n, a);
            }
            xr = a[0] - a[1];
            a[0] += a[1];
            for (j = 2; j < n; j += 2) {
                a[j - 1] = -a[j] - a[j + 1];
                a[j] -= a[j + 1];
            }
            a[n - 1] = -xr;
        }
    }

    public void dfct(double[] a)
    {
        int j, k, l, m, mh, nw, nc;
        double xr, xi, yr, yi;

        nw = ip[0];
        if (n > (nw << 3)) {
            nw = n >> 3;
            makewt(nw);
        }
        nc = ip[1];
        if (n > (nc << 1)) {
            nc = n >> 1;
            makect(nc, w, nw);
        }
        m = n >> 1;
        yi = a[m];
        xi = a[0] + a[n];
        a[0] -= a[n];
        t[0] = xi - yi;
        t[m] = xi + yi;
        if (n > 2) {
            mh = m >> 1;
            for (j = 1; j < mh; j++) {
                k = m - j;
                xr = a[j] - a[n - j];
                xi = a[j] + a[n - j];
                yr = a[k] - a[n - k];
                yi = a[k] + a[n - k];
                a[j] = xr;
                a[k] = yr;
                t[j] = xi - yi;
                t[k] = xi + yi;
            }
            t[mh] = a[mh] + a[n - mh];
            a[mh] -= a[n - mh];
            dctsub(m, a, nc, w, nw);
            if (m > 4) {
                bitrv2(m, a);
                cftfsub(m, a);
                rftfsub(m, a, nc, w, nw);
            } else if (m == 4) {
                cftfsub(m, a);
            }
            a[n - 1] = a[0] - a[1];
            a[1] = a[0] + a[1];
            for (j = m - 2; j >= 2; j -= 2) {
                a[2 * j + 1] = a[j] + a[j + 1];
                a[2 * j - 1] = a[j] - a[j + 1];
            }
            l = 2;
            m = mh;
            while (m >= 2) {
                dctsub(m, t, nc, w, nw);
                if (m > 4) {
                    bitrv2(m, t);
                    cftfsub(m, t);
                    rftfsub(m, t, nc, w, nw);
                } else if (m == 4) {
                    cftfsub(m, t);
                }
                a[n - l] = t[0] - t[1];
                a[l] = t[0] + t[1];
                k = 0;
                for (j = 2; j < m; j += 2) {
                    k += l << 2;
                    a[k - l] = t[j] - t[j + 1];
                    a[k + l] = t[j] + t[j + 1];
                }
                l <<= 1;
                mh = m >> 1;
                for (j = 0; j < mh; j++) {
                    k = m - j;
                    t[j] = t[m + k] - t[m + j];
                    t[k] = t[m + k] + t[m + j];
                }
                t[mh] = t[m + mh];
                m = mh;
            }
            a[l] = t[0];
            a[n] = t[2] - t[1];
            a[0] = t[2] + t[1];
        } else {
            a[1] = a[0];
            a[2] = t[0];
            a[0] = t[1];
        }
    }

    public void dfst(double[] a)
    {
        int j, k, l, m, mh, nw, nc;
        double xr, xi, yr, yi;

        nw = ip[0];
        if (n > (nw << 3)) {
            nw = n >> 3;
                makewt(nw);
        }
        nc = ip[1];
        if (n > (nc << 1)) {
            nc = n >> 1;
            makect(nc, w, nw);
        }
        if (n > 2) {
                m = n >> 1;
                mh = m >> 1;
            for (j = 1; j < mh; j++) {
                k = m - j;
                xr = a[j] + a[n - j];
                xi = a[j] - a[n - j];
                yr = a[k] + a[n - k];
                yi = a[k] - a[n - k];
                a[j] = xr;
                a[k] = yr;
                t[j] = xi + yi;
                t[k] = xi - yi;
            }
            t[0] = a[mh] - a[n - mh];
            a[mh] += a[n - mh];
            a[0] = a[m];
            dstsub(m, a, nc, w, nw);
            if (m > 4) {
                bitrv2(m, a);
                cftfsub(m, a);
                rftfsub(m, a, nc, w, nw);
            } else if (m == 4) {
                cftfsub(m, a);
            }
            a[n - 1] = a[1] - a[0];
            a[1] = a[0] + a[1];
            for (j = m - 2; j >= 2; j -= 2) {
                a[2 * j + 1] = a[j] - a[j + 1];
                a[2 * j - 1] = -a[j] - a[j + 1];
            }
            l = 2;
            m = mh;
            while (m >= 2) {
                dstsub(m, t, nc, w, nw);
                if (m > 4) {
                    bitrv2(m, t);
                    cftfsub(m, t);
                    rftfsub(m, t, nc, w, nw);
                } else if (m == 4) {
                    cftfsub(m, t);
                }
                a[n - l] = t[1] - t[0];
                a[l] = t[0] + t[1];
                k = 0;
                for (j = 2; j < m; j += 2) {
                    k += l << 2;
                    a[k - l] = -t[j] - t[j + 1];
                    a[k + l] = t[j] - t[j + 1];
                }
                l <<= 1;
                mh = m >> 1;
                for (j = 1; j < mh; j++) {
                    k = m - j;
                    t[j] = t[m + k] + t[m + j];
                    t[k] = t[m + k] - t[m + j];
                }
                t[0] = t[m + mh];
                m = mh;
            }
            a[l] = t[0];
        }
        a[0] = 0;
    }

/* -------- initializing routines -------- */

    private void makewt(int nw)
    {
        int j, nwh;
        double delta, x, y;

        ip[0] = nw;
        ip[1] = 1;
        if (nw > 2) {
            nwh = nw >> 1;
            delta = atan(1.0) / nwh;
            w[0] = 1;
            w[1] = 0;
            w[nwh] = cos(delta * nwh);
            w[nwh + 1] = w[nwh];
            if (nwh > 2) {
                for (j = 2; j < nwh; j += 2) {
                    x = cos(delta * j);
                    y = sin(delta * j);
                    w[j] = x;
                    w[j + 1] = y;
                    w[nw - j] = y;
                    w[nw - j + 1] = x;
                }
                bitrv2(nw, w);
            }
        }
    }


    private void makect(int nc, double[] c, int nw)
    {
        int j, nch;
        double delta;

        ip[1] = nc;
        if (nc > 1) {
            nch = nc >> 1;
            delta = atan(1.0) / nch;
            c[nw + 0] = cos(delta * nch);
            c[nw + nch] = 0.5 * c[0];
            for (j = 1; j < nch; j++) {
                c[nw + j] = 0.5 * cos(delta * j);
                c[nw + nc - j] = 0.5 * sin(delta * j);
            }
        }
    }

/* -------- child routines -------- */

    private void bitrv2(int n, double[] a)
    {
        int j, j1, k, k1, l, m, m2;
        double xr, xi, yr, yi;

        ip[2 + 0] = 0;
        l = n;
        m = 1;
        while ((m << 3) < l) {
            l >>= 1;
            for (j = 0; j < m; j++) {
                ip[2 + m + j] = ip[2 + j] + l;
            }
            m <<= 1;
        }
        m2 = 2 * m;
        if ((m << 3) == l) {
            for (k = 0; k < m; k++) {
                for (j = 0; j < k; j++) {
                    j1 = 2 * j + ip[2 + k];
                    k1 = 2 * k + ip[2 + j];
                    xr = a[j1];
                    xi = a[j1 + 1];
                    yr = a[k1];
                    yi = a[k1 + 1];
                    a[j1] = yr;
                    a[j1 + 1] = yi;
                    a[k1] = xr;
                    a[k1 + 1] = xi;
                    j1 += m2;
                    k1 += 2 * m2;
                    xr = a[j1];
                    xi = a[j1 + 1];
                    yr = a[k1];
                    yi = a[k1 + 1];
                    a[j1] = yr;
                    a[j1 + 1] = yi;
                    a[k1] = xr;
                    a[k1 + 1] = xi;
                    j1 += m2;
                    k1 -= m2;
                    xr = a[j1];
                    xi = a[j1 + 1];
                    yr = a[k1];
                    yi = a[k1 + 1];
                    a[j1] = yr;
                    a[j1 + 1] = yi;
                    a[k1] = xr;
                    a[k1 + 1] = xi;
                    j1 += m2;
                    k1 += 2 * m2;
                    xr = a[j1];
                    xi = a[j1 + 1];
                    yr = a[k1];
                    yi = a[k1 + 1];
                    a[j1] = yr;
                    a[j1 + 1] = yi;
                    a[k1] = xr;
                    a[k1 + 1] = xi;
                }
                j1 = 2 * k + m2 + ip[2 + k];
                k1 = j1 + m2;
                xr = a[j1];
                xi = a[j1 + 1];
                yr = a[k1];
                yi = a[k1 + 1];
                a[j1] = yr;
                a[j1 + 1] = yi;
                a[k1] = xr;
                a[k1 + 1] = xi;
            }
        } else {
            for (k = 1; k < m; k++) {
                for (j = 0; j < k; j++) {
                    j1 = 2 * j + ip[2 + k];
                    k1 = 2 * k + ip[2 + j];
                    xr = a[j1];
                    xi = a[j1 + 1];
                    yr = a[k1];
                    yi = a[k1 + 1];
                    a[j1] = yr;
                    a[j1 + 1] = yi;
                    a[k1] = xr;
                    a[k1 + 1] = xi;
                    j1 += m2;
                    k1 += m2;
                    xr = a[j1];
                    xi = a[j1 + 1];
                    yr = a[k1];
                    yi = a[k1 + 1];
                    a[j1] = yr;
                    a[j1 + 1] = yi;
                    a[k1] = xr;
                    a[k1 + 1] = xi;
                }
            }
        }
    }

    private void bitrv2conj(int n, double[] a)
    {
        int j, j1, k, k1, l, m, m2;
        double xr, xi, yr, yi;

        ip[2 + 0] = 0;
        l = n;
        m = 1;
        while ((m << 3) < l) {
            l >>= 1;
            for (j = 0; j < m; j++) {
                ip[2 + m + j] = ip[2 + j] + l;
            }
            m <<= 1;
        }
        m2 = 2 * m;
        if ((m << 3) == l) {
            for (k = 0; k < m; k++) {
                for (j = 0; j < k; j++) {
                    j1 = 2 * j + ip[2 + k];
                    k1 = 2 * k + ip[2 + j];
                    xr = a[j1];
                    xi = -a[j1 + 1];
                    yr = a[k1];
                    yi = -a[k1 + 1];
                    a[j1] = yr;
                    a[j1 + 1] = yi;
                    a[k1] = xr;
                    a[k1 + 1] = xi;
                    j1 += m2;
                    k1 += 2 * m2;
                    xr = a[j1];
                    xi = -a[j1 + 1];
                    yr = a[k1];
                    yi = -a[k1 + 1];
                    a[j1] = yr;
                    a[j1 + 1] = yi;
                    a[k1] = xr;
                    a[k1 + 1] = xi;
                    j1 += m2;
                    k1 -= m2;
                    xr = a[j1];
                    xi = -a[j1 + 1];
                    yr = a[k1];
                    yi = -a[k1 + 1];
                    a[j1] = yr;
                    a[j1 + 1] = yi;
                    a[k1] = xr;
                    a[k1 + 1] = xi;
                    j1 += m2;
                    k1 += 2 * m2;
                    xr = a[j1];
                    xi = -a[j1 + 1];
                    yr = a[k1];
                    yi = -a[k1 + 1];
                    a[j1] = yr;
                    a[j1 + 1] = yi;
                    a[k1] = xr;
                    a[k1 + 1] = xi;
                }
                k1 = 2 * k + ip[2 + k];
                a[k1 + 1] = -a[k1 + 1];
                j1 = k1 + m2;
                k1 = j1 + m2;
                xr = a[j1];
                xi = -a[j1 + 1];
                yr = a[k1];
                yi = -a[k1 + 1];
                a[j1] = yr;
                a[j1 + 1] = yi;
                a[k1] = xr;
                a[k1 + 1] = xi;
                k1 += m2;
                a[k1 + 1] = -a[k1 + 1];
            }
        } else {
            a[1] = -a[1];
            a[m2 + 1] = -a[m2 + 1];
            for (k = 1; k < m; k++) {
                for (j = 0; j < k; j++) {
                    j1 = 2 * j + ip[2 + k];
                    k1 = 2 * k + ip[2 + j];
                    xr = a[j1];
                    xi = -a[j1 + 1];
                    yr = a[k1];
                    yi = -a[k1 + 1];
                    a[j1] = yr;
                    a[j1 + 1] = yi;
                    a[k1] = xr;
                    a[k1 + 1] = xi;
                    j1 += m2;
                    k1 += m2;
                    xr = a[j1];
                    xi = -a[j1 + 1];
                    yr = a[k1];
                    yi = -a[k1 + 1];
                    a[j1] = yr;
                    a[j1 + 1] = yi;
                    a[k1] = xr;
                    a[k1 + 1] = xi;
                }
                k1 = 2 * k + ip[2 + k];
                a[k1 + 1] = -a[k1 + 1];
                a[k1 + m2 + 1] = -a[k1 + m2 + 1];
            }
        }
    }

    private void cftfsub(int n, double[] a)
    {
        int j, j1, j2, j3, l;
        double x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i;

        l = 2;
        if (n > 8) {
            cft1st(a);
            l = 8;
            while ((l << 2) < n) {
                cftmdl(l, a);
                l <<= 2;
            }
        }
        if ((l << 2) == n) {
            for (j = 0; j < l; j += 2) {
                j1 = j + l;
                j2 = j1 + l;
                j3 = j2 + l;
                x0r = a[j] + a[j1];
                x0i = a[j + 1] + a[j1 + 1];
                x1r = a[j] - a[j1];
                x1i = a[j + 1] - a[j1 + 1];
                x2r = a[j2] + a[j3];
                x2i = a[j2 + 1] + a[j3 + 1];
                x3r = a[j2] - a[j3];
                x3i = a[j2 + 1] - a[j3 + 1];
                a[j] = x0r + x2r;
                a[j + 1] = x0i + x2i;
                a[j2] = x0r - x2r;
                a[j2 + 1] = x0i - x2i;
                a[j1] = x1r - x3i;
                a[j1 + 1] = x1i + x3r;
                a[j3] = x1r + x3i;
                a[j3 + 1] = x1i - x3r;
            }
        } else {
            for (j = 0; j < l; j += 2) {
                j1 = j + l;
                x0r = a[j] - a[j1];
                x0i = a[j + 1] - a[j1 + 1];
                a[j] += a[j1];
                a[j + 1] += a[j1 + 1];
                a[j1] = x0r;
                a[j1 + 1] = x0i;
            }
        }
    }

    private void cftbsub(int n, double[] a)
    {
        int j, j1, j2, j3, l;
        double x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i;

        l = 2;
        if (n > 8) {
            cft1st(a);
            l = 8;
            while ((l << 2) < n) {
                cftmdl(l, a);
                l <<= 2;
            }
        }
        if ((l << 2) == n) {
            for (j = 0; j < l; j += 2) {
                j1 = j + l;
                j2 = j1 + l;
                j3 = j2 + l;
                x0r = a[j] + a[j1];
                x0i = -a[j + 1] - a[j1 + 1];
                x1r = a[j] - a[j1];
                x1i = -a[j + 1] + a[j1 + 1];
                x2r = a[j2] + a[j3];
                x2i = a[j2 + 1] + a[j3 + 1];
                x3r = a[j2] - a[j3];
                x3i = a[j2 + 1] - a[j3 + 1];
                a[j] = x0r + x2r;
                a[j + 1] = x0i - x2i;
                a[j2] = x0r - x2r;
                a[j2 + 1] = x0i + x2i;
                a[j1] = x1r - x3i;
                a[j1 + 1] = x1i - x3r;
                a[j3] = x1r + x3i;
                a[j3 + 1] = x1i + x3r;
            }
        } else {
            for (j = 0; j < l; j += 2) {
                j1 = j + l;
                x0r = a[j] - a[j1];
                x0i = -a[j + 1] + a[j1 + 1];
                a[j] += a[j1];
                a[j + 1] = -a[j + 1] - a[j1 + 1];
                a[j1] = x0r;
                a[j1 + 1] = x0i;
            }
        }
    }

    private void cft1st(double[] a)
    {
        int j, k1, k2;
        double wk1r, wk1i, wk2r, wk2i, wk3r, wk3i;
        double x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i;

        x0r = a[0] + a[2];
        x0i = a[1] + a[3];
        x1r = a[0] - a[2];
        x1i = a[1] - a[3];
        x2r = a[4] + a[6];
        x2i = a[5] + a[7];
        x3r = a[4] - a[6];
        x3i = a[5] - a[7];
        a[0] = x0r + x2r;
        a[1] = x0i + x2i;
        a[4] = x0r - x2r;
        a[5] = x0i - x2i;
        a[2] = x1r - x3i;
        a[3] = x1i + x3r;
        a[6] = x1r + x3i;
        a[7] = x1i - x3r;
        wk1r = w[2];
        x0r = a[8] + a[10];
        x0i = a[9] + a[11];
        x1r = a[8] - a[10];
        x1i = a[9] - a[11];
        x2r = a[12] + a[14];
        x2i = a[13] + a[15];
        x3r = a[12] - a[14];
        x3i = a[13] - a[15];
        a[8] = x0r + x2r;
        a[9] = x0i + x2i;
        a[12] = x2i - x0i;
        a[13] = x0r - x2r;
        x0r = x1r - x3i;
        x0i = x1i + x3r;
        a[10] = wk1r * (x0r - x0i);
        a[11] = wk1r * (x0r + x0i);
        x0r = x3i + x1r;
        x0i = x3r - x1i;
        a[14] = wk1r * (x0i - x0r);
        a[15] = wk1r * (x0i + x0r);
        k1 = 0;
        for (j = 16; j < n; j += 16) {
            k1 += 2;
            k2 = 2 * k1;
            wk2r = w[k1];
            wk2i = w[k1 + 1];
            wk1r = w[k2];
            wk1i = w[k2 + 1];
            wk3r = wk1r - 2 * wk2i * wk1i;
            wk3i = 2 * wk2i * wk1r - wk1i;
            x0r = a[j] + a[j + 2];
            x0i = a[j + 1] + a[j + 3];
            x1r = a[j] - a[j + 2];
            x1i = a[j + 1] - a[j + 3];
            x2r = a[j + 4] + a[j + 6];
            x2i = a[j + 5] + a[j + 7];
            x3r = a[j + 4] - a[j + 6];
            x3i = a[j + 5] - a[j + 7];
            a[j] = x0r + x2r;
            a[j + 1] = x0i + x2i;
            x0r -= x2r;
            x0i -= x2i;
            a[j + 4] = wk2r * x0r - wk2i * x0i;
            a[j + 5] = wk2r * x0i + wk2i * x0r;
            x0r = x1r - x3i;
            x0i = x1i + x3r;
            a[j + 2] = wk1r * x0r - wk1i * x0i;
            a[j + 3] = wk1r * x0i + wk1i * x0r;
            x0r = x1r + x3i;
            x0i = x1i - x3r;
            a[j + 6] = wk3r * x0r - wk3i * x0i;
            a[j + 7] = wk3r * x0i + wk3i * x0r;
            wk1r = w[k2 + 2];
            wk1i = w[k2 + 3];
            wk3r = wk1r - 2 * wk2r * wk1i;
            wk3i = 2 * wk2r * wk1r - wk1i;
            x0r = a[j + 8] + a[j + 10];
            x0i = a[j + 9] + a[j + 11];
            x1r = a[j + 8] - a[j + 10];
            x1i = a[j + 9] - a[j + 11];
            x2r = a[j + 12] + a[j + 14];
            x2i = a[j + 13] + a[j + 15];
            x3r = a[j + 12] - a[j + 14];
            x3i = a[j + 13] - a[j + 15];
            a[j + 8] = x0r + x2r;
            a[j + 9] = x0i + x2i;
            x0r -= x2r;
            x0i -= x2i;
            a[j + 12] = -wk2i * x0r - wk2r * x0i;
            a[j + 13] = -wk2i * x0i + wk2r * x0r;
            x0r = x1r - x3i;
            x0i = x1i + x3r;
            a[j + 10] = wk1r * x0r - wk1i * x0i;
            a[j + 11] = wk1r * x0i + wk1i * x0r;
            x0r = x1r + x3i;
            x0i = x1i - x3r;
            a[j + 14] = wk3r * x0r - wk3i * x0i;
            a[j + 15] = wk3r * x0i + wk3i * x0r;
        }
    }

    private void cftmdl(int l, double[] a)
    {
        int j, j1, j2, j3, k, k1, k2, m, m2;
        double wk1r, wk1i, wk2r, wk2i, wk3r, wk3i;
        double x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i;

        m = l << 2;
        for (j = 0; j < l; j += 2) {
            j1 = j + l;
            j2 = j1 + l;
            j3 = j2 + l;
            x0r = a[j] + a[j1];
            x0i = a[j + 1] + a[j1 + 1];
            x1r = a[j] - a[j1];
            x1i = a[j + 1] - a[j1 + 1];
            x2r = a[j2] + a[j3];
            x2i = a[j2 + 1] + a[j3 + 1];
            x3r = a[j2] - a[j3];
            x3i = a[j2 + 1] - a[j3 + 1];
            a[j] = x0r + x2r;
            a[j + 1] = x0i + x2i;
            a[j2] = x0r - x2r;
            a[j2 + 1] = x0i - x2i;
            a[j1] = x1r - x3i;
            a[j1 + 1] = x1i + x3r;
            a[j3] = x1r + x3i;
            a[j3 + 1] = x1i - x3r;
        }
        wk1r = w[2];
        for (j = m; j < l + m; j += 2) {
            j1 = j + l;
            j2 = j1 + l;
            j3 = j2 + l;
            x0r = a[j] + a[j1];
            x0i = a[j + 1] + a[j1 + 1];
            x1r = a[j] - a[j1];
            x1i = a[j + 1] - a[j1 + 1];
            x2r = a[j2] + a[j3];
            x2i = a[j2 + 1] + a[j3 + 1];
            x3r = a[j2] - a[j3];
            x3i = a[j2 + 1] - a[j3 + 1];
            a[j] = x0r + x2r;
            a[j + 1] = x0i + x2i;
            a[j2] = x2i - x0i;
            a[j2 + 1] = x0r - x2r;
            x0r = x1r - x3i;
            x0i = x1i + x3r;
            a[j1] = wk1r * (x0r - x0i);
            a[j1 + 1] = wk1r * (x0r + x0i);
            x0r = x3i + x1r;
            x0i = x3r - x1i;
            a[j3] = wk1r * (x0i - x0r);
            a[j3 + 1] = wk1r * (x0i + x0r);
        }
        k1 = 0;
        m2 = 2 * m;
        for (k = m2; k < n; k += m2) {
            k1 += 2;
            k2 = 2 * k1;
            wk2r = w[k1];
            wk2i = w[k1 + 1];
            wk1r = w[k2];
            wk1i = w[k2 + 1];
            wk3r = wk1r - 2 * wk2i * wk1i;
            wk3i = 2 * wk2i * wk1r - wk1i;
            for (j = k; j < l + k; j += 2) {
                j1 = j + l;
                j2 = j1 + l;
                j3 = j2 + l;
                x0r = a[j] + a[j1];
                x0i = a[j + 1] + a[j1 + 1];
                x1r = a[j] - a[j1];
                x1i = a[j + 1] - a[j1 + 1];
                x2r = a[j2] + a[j3];
                x2i = a[j2 + 1] + a[j3 + 1];
                x3r = a[j2] - a[j3];
                x3i = a[j2 + 1] - a[j3 + 1];
                a[j] = x0r + x2r;
                a[j + 1] = x0i + x2i;
                x0r -= x2r;
                x0i -= x2i;
                a[j2] = wk2r * x0r - wk2i * x0i;
                a[j2 + 1] = wk2r * x0i + wk2i * x0r;
                x0r = x1r - x3i;
                x0i = x1i + x3r;
                a[j1] = wk1r * x0r - wk1i * x0i;
                a[j1 + 1] = wk1r * x0i + wk1i * x0r;
                x0r = x1r + x3i;
                x0i = x1i - x3r;
                a[j3] = wk3r * x0r - wk3i * x0i;
                a[j3 + 1] = wk3r * x0i + wk3i * x0r;
            }
            wk1r = w[k2 + 2];
            wk1i = w[k2 + 3];
            wk3r = wk1r - 2 * wk2r * wk1i;
            wk3i = 2 * wk2r * wk1r - wk1i;
            for (j = k + m; j < l + (k + m); j += 2) {
                j1 = j + l;
                j2 = j1 + l;
                j3 = j2 + l;
                x0r = a[j] + a[j1];
                x0i = a[j + 1] + a[j1 + 1];
                x1r = a[j] - a[j1];
                x1i = a[j + 1] - a[j1 + 1];
                x2r = a[j2] + a[j3];
                x2i = a[j2 + 1] + a[j3 + 1];
                x3r = a[j2] - a[j3];
                x3i = a[j2 + 1] - a[j3 + 1];
                a[j] = x0r + x2r;
                a[j + 1] = x0i + x2i;
                x0r -= x2r;
                x0i -= x2i;
                a[j2] = -wk2i * x0r - wk2r * x0i;
                a[j2 + 1] = -wk2i * x0i + wk2r * x0r;
                x0r = x1r - x3i;
                x0i = x1i + x3r;
                a[j1] = wk1r * x0r - wk1i * x0i;
                a[j1 + 1] = wk1r * x0i + wk1i * x0r;
                x0r = x1r + x3i;
                x0i = x1i - x3r;
                a[j3] = wk3r * x0r - wk3i * x0i;
                a[j3 + 1] = wk3r * x0i + wk3i * x0r;
            }
        }
    }

    private void rftfsub(int n, double[] a, int nc, double[] c, int nw)
    {
        int j, k, kk, ks, m;
        double wkr, wki, xr, xi, yr, yi;

        m = n >> 1;
        ks = 2 * nc / m;
        kk = 0;
        for (j = 2; j < m; j += 2) {
            k = n - j;
            kk += ks;
            wkr = 0.5 - c[nw + nc - kk];
            wki = c[nw + kk];
            xr = a[j] - a[k];
            xi = a[j + 1] + a[k + 1];
            yr = wkr * xr - wki * xi;
            yi = wkr * xi + wki * xr;
            a[j] -= yr;
            a[j + 1] -= yi;
            a[k] += yr;
            a[k + 1] -= yi;
        }
    }

    private void rftbsub(int n, double[] a, int nc, double[] c, int nw)
    {
        int j, k, kk, ks, m;
        double wkr, wki, xr, xi, yr, yi;

        a[1] = -a[1];
        m = n >> 1;
        ks = 2 * nc / m;
        kk = 0;
        for (j = 2; j < m; j += 2) {
            k = n - j;
            kk += ks;
            wkr = 0.5 - c[nw + nc - kk];
            wki = c[nw + kk];
            xr = a[j] - a[k];
            xi = a[j + 1] + a[k + 1];
            yr = wkr * xr + wki * xi;
            yi = wkr * xi - wki * xr;
            a[j] -= yr;
            a[j + 1] = yi - a[j + 1];
            a[k] += yr;
            a[k + 1] = yi - a[k + 1];
        }
        a[m + 1] = -a[m + 1];
    }

    private void dctsub(int n, double[] a, int nc, double[] c, int nw)
    {
        int j, k, kk, ks, m;
        double wkr, wki, xr;

        m = n >> 1;
        ks = nc / n;
        kk = 0;
        for (j = 1; j < m; j++) {
            k = n - j;
            kk += ks;
            wkr = c[nw + kk] - c[nw + nc - kk];
            wki = c[nw + kk] + c[nw + nc - kk];
            xr = wki * a[j] - wkr * a[k];
            a[j] = wkr * a[j] + wki * a[k];
            a[k] = xr;
        }
        a[m] *= c[nw + 0];
    }

    private void dstsub(int n, double[] a, int nc, double[] c, int nw)
    {
        int j, k, kk, ks, m;
        double wkr, wki, xr;

        m = n >> 1;
        ks = nc / n;
        kk = 0;
        for (j = 1; j < m; j++) {
            k = n - j;
            kk += ks;
            wkr = c[nw + kk] - c[nw + nc - kk];
            wki = c[nw + kk] + c[nw + nc - kk];
            xr = wki * a[k] - wkr * a[j];
            a[k] = wkr * a[k] + wki * a[j];
            a[j] = xr;
        }
        a[m] *= c[nw + 0];
    }

}


ご自由にお使いください。他人のソースコードを書き換えただけですから。

ではまた。

DJ Snakeの「A Different Way」にハマった

こんにちは。今日は久しぶりに洋楽の話題です。

今日は、DJ SnakeのA Different Wayという曲についてです。

まだPVは公開されていないようですが、非常にいい曲です。

曲の内容は、失恋した女の子に、俺なら幸せにできる!って伝える歌詞のようです。

韻を踏んだリズムのよい歌詞と、その内容にマッチしたメロディーがなんともいえません。

私は、google play musicでこの曲を聞いてからめちゃくちゃハマってしまいました。

レコード会社の回し者みたいになりましたが、みなさんも是非聞いてください!

ではまた。

A Different Way (feat. Lauv)

A Different Way (feat. Lauv)

  • DJスネイク
  • ダンス
  • ¥250

英単語アプリ「Memoria」を公開しました!

f:id:takebo_blog:20170925135928p:plain
こんにちは。今日は私が開発したアプリを紹介させていただきます。

中学生に最適!英単語アプリ「Memoria

今回公開したアプリは、「Memoria」です。

Memoriaは中学生で学習する英単語を収録した英単語アプリです。
英単語は大阪府教育委員会が公表している「大阪版中学校で学ぶ英単語集」を元に選定いたしました。
これは大阪府の公立高校入試にて、出題される英単語を集めたものです。

これを勉強すれば、高校入試に必要な英単語をしっかり暗記することができます。

現代では英語力が必須となってまいりました。
中学校で学ぶ英単語は社会人として覚えていて当然のものばかり!
みなさんも英単語の復習として、ぜひご活用ください。

技術的な話

ということで、宣伝はここまでです。
ここではこのアプリの技術的な話をしたいと思います。

使用したライブラリ

このアプリには、暗記した単語数や学習した時間をグラフで表示する機能があります。
ここでは、「MPAndroidChart」というライブラリを導入しました!
github.com

こちらで使い方が紹介されています。
moneyforward.com

このライブラリは様々なアプリで活用されて、非常に使い勝手のよいライブラリです。
データを設定すれば、アニメーション付きで表示され、とても見た目が美しいグラフが表示されます。
自分で実装するととても大変だと思いますので、非常に便利なライブラリです。

また、ライセンス表示には「AboutLibraries」というライブラリを使用しました。
github.com

こちらで使い方が紹介されています。
cutmail.hatenablog.com

有名なライブラリであれば、ライセンスが自動的に表示されます。
自分で追加することもできます。

日付処理には「ThreeTenABP」を使用しています。
github.com

こちらで使い方が紹介されています。
techblog.timers-inc.com

日付処理は、標準機能では大変扱いにくいものです。
それを簡単にしてくれるのがこのライブラリです。
日付処理はJodaTimeが有名ですが、JodaTimeはヘビーなライブラリという噂をきき、今回はこれを選びました。
これのお陰で1ヶ月前というような処理が簡単に行えるようになりました。
ThreeTenABPはJava8の日付処理APIのバックポートですので、もしAndroidがJava8に対応したとしても、大幅に書き換える必要はありません。

苦労した点

このアプリは私の人生で初めて開発したAndroidアプリです。
知らないことだらけで、すべてに苦戦しました。
単語のリスト表示には大いに悩まされました。

タブとして単語を分けるため、Fragmentを導入したり、リストに画像と文字を並べて表示できるようにしたり。
非常に大変でした。ほんとに苦労しました(笑)
アプリ作りに手慣れた方には非常に簡単なことだと思いますが、私にはチョモランマ級に高い壁でした。
リスト作成時にヒープが足らなくなったり、散々エラーに見舞われました…。

まあ、なにはともあれ完成してよかった!(笑)

ダウンロード

Memoriaはこちらからダウンロード可能です。
残念ながら現在は、Android版のみの公開となっております。
どんな仕上がりになっているか、またバグ探しにご協力ください!
Google Play で手に入れよう

海外で人気!おしゃれな無料HTMLテンプレート20選

こんにちは!今日は海外で人気のHTMLテンプレートをご紹介します。

  • はじめに
  • おすすめHTMLテンプレート
    • Box Portfolio
      • ダウンロード
      • デモ
    • Multiverse
      • ダウンロード&デモ
    • Snow
      • ダウンロード
      • デモ
    • THOMSOON
      • ダウンロード
      • デモ
    • Spectral
      • ダウンロード&デモ
    • Snapshot
      • ダウンロード&デモ
    • Mobirize
      • ダウンロード
      • デモ
    • Mobile App Template
      • ダウンロード
      • デモ
    • Present
      • ダウンロード&デモ
    • Radius
      • ダウンロード&デモ
    • Xenon
      • ダウンロード
      • デモ
    • Circle
      • ダウンロード
      • デモ
    • Visualize
      • ダウンロード&デモ
    • Squadfree
      • ダウンロード&デモ
    • AdminLTE 2
      • ダウンロード
      • デモ
    • Crafty
      • ダウンロード
      • デモ
    • Treviso
      • ダウンロード
      • デモ
    • Dimension
      • ダウンロード&デモ
    • Dance Whirl
      • ダウンロード
      • デモ
    • Sonic
      • ダウンロード
      • デモ
  • 最後に
続きを読む

おしゃれなギャラリーが作れるCMS「Lychee」を使ってみた

Photo Gallery
こんにちは。今日はWebの話題です。

みなさん、CMSってご存じですか?

Contents Management Systemの略で、htmlを直接編集することなく、ブログやホームページが作れるシステムのことです。
世界中で広く利用されている「WordPress」は、このCMSの一種です。

WordPressなら、ブログを書くときにいちいちhtmlをまるごと制作する、なんてことはないですよね?
これがCMSの特徴です。

CMSは、WordPress以外にもDrupalConcrete5など様々な種類があります。
余談ですが、個人的にはConcrete5が使いやすくて好きだったりします。

今日は、このCMSの中から「Lychee」をご紹介します。

Lycheeとは

タイトルに合ったようにLycheeはギャラリーを作るためのものです。

ギャラリーとは、写真を一覧で表示して、画像をクリックすると拡大される、といった機能を持つものです。
スマホの中にも写真を見るためのギャラリーアプリが入っていますよね。

写真をメインで扱うようなサイトでは、なるべく使いやすく、きれいに見せたいところですよね。
そんな望みを叶えてくれるのが、Lycheeです。

WordPressプラグインをいれてギャラリーを作ってもいいのですが、そうすると大掛かりなシステムになりますよね。
ちょっと写真が見れるサイトを作りたいんだ、なんて時には面倒だったりします。

ということで、Lycheeが活用されるのです。

lychee.electerious.com

Lycheeは、主にPHPjavascriptで構成されており、写真の管理にMySQLが必要です。
WordPressがインストールできるサーバーであれば、Lycheeもインストール可能です。

インストール手順はとても簡単です。
まず、Lycheeのgithubにアクセスし、ファイルをダウンロードします。
git cloneしても構いません。
github.com

このファイルを解凍し、インストールしたいディレクトリに配置して、その場所にアクセスします。

あとは画面に従って、MySQLのユーザー名やパスワードなど必要な情報を入力し、管理者用パスワードを設定すれば完了です。

詳しい利用方法は、READMEの中に書かれています。
直感的に使い方がわかるので、迷うこともないと思います。

Lycheeの良い点・悪い点

Lycheeの良いところは、シンプルさと美しさです。

ギャラリーを作るCMSは、レイアウトが残念で全近代的なビジュアルだったりしますが、Lycheeは黒を基調としたシンプルなデザインです。
また、それでいて必要な機能はしっかり備わっています。
写真を一括でzipにしてダウンロードしたり、写真1枚1枚を全画面表示したり、名前を変更したり共有したり。
さらにはパスワードによって写真を保護する、なんてこともできます。
基本的な機能は揃っていて、便利です。

今まで3日ほど私が使ってきて困ったことがいくつかありました。
まず、パスワード保護に関するものです。
パスワードによって写真を保護している場合、フォルダ内の写真を見るたびにパスワードを求められることがあります。
写真を1枚見るごとにパスワードを求められ、非常に面倒でした。

この原因はchromeのパスワード保存機能が原因でした。
管理者用のアカウント情報をchromeに記憶させていたことで、このような面倒な現象が起こるようです。

また、写真の一括ダウンロードですが、写真の数が多くなるとうまく動かなくなりました。

原因は、zipファイルの送信にありました。
一括ダウンロードでは、毎回phpにてzipファイルの作成が行われ、そのまま送信されるのですが、これがファイルサイズを考慮しない作りとなっているようです。
サーバーのメモリ不足によって転送ができなくなるようです。

php/Module/Album.php内のgetArchive()を以下のように編集すると直りました。

<?php
public function getArchive() {

	// Check dependencies
	Validator::required(isset($this->albumIDs), __METHOD__);

	// Call plugins
	Plugins::get()->activate(__METHOD__, 0, func_get_args());
	// Illicit chars
	$badChars =	array_merge(
		array_map('chr', range(0,31)),
		array("<", ">", ":", '"', "/", "\\", "|", "?", "*")
	);

	// Photos query
	switch($this->albumIDs) {
		case 's':
			$photos   = Database::prepare(Database::get(), 'SELECT title, url FROM ? WHERE public = 1', array(LYCHEE_TABLE_PHOTOS));
			$zipTitle = 'Public';
			break;
		case 'f':
			$photos   = Database::prepare(Database::get(), 'SELECT title, url FROM ? WHERE star = 1', array(LYCHEE_TABLE_PHOTOS));
			$zipTitle = 'Starred';
			break;
		case 'r':
			$photos   = Database::prepare(Database::get(), 'SELECT title, url FROM ? WHERE LEFT(id, 10) >= unix_timestamp(DATE_SUB(NOW(), INTERVAL 1 DAY)) GROUP BY checksum', array(LYCHEE_TABLE_PHOTOS));
			$zipTitle = 'Recent';
			break;
		default:
			$photos   = Database::prepare(Database::get(), "SELECT title, url FROM ? WHERE album = '?'", array(LYCHEE_TABLE_PHOTOS, $this->albumIDs));
			$zipTitle = 'Unsorted';
	}
	// Get title from database when album is not a SmartAlbum
	if ($this->albumIDs!=0&&is_numeric($this->albumIDs)) {

		$query = Database::prepare(Database::get(), "SELECT title FROM ? WHERE id = '?' LIMIT 1", array(LYCHEE_TABLE_ALBUMS, $this->albumIDs));
		$album = Database::execute(Database::get(), $query, __METHOD__, __LINE__);

		if ($album===false) return false;
	
		// Get album object
		$album = $album->fetch_object();

		// Album not found?
		if ($album===null) {
			Log::error(Database::get(), __METHOD__, __LINE__, 'Could not find specified album');
			return false;
		}

		// Set title
		$zipTitle = $album->title;

	}

	// Escape title
	$zipTitle = str_replace($badChars, '', $zipTitle);

	$filename = LYCHEE_DATA . $zipTitle . '.zip';
		
	// Create zip
	$zip = new ZipArchive();
	if ($zip->open($filename, ZIPARCHIVE::CREATE)!==TRUE) {
		Log::error(Database::get(), __METHOD__, __LINE__, 'Could not create ZipArchive');
		return false;
	}

	// Execute query
	$photos = Database::execute(Database::get(), $photos, __METHOD__, __LINE__);

	// Check if album empty
	if ($photos->num_rows==0) {
		Log::error(Database::get(), __METHOD__, __LINE__, 'Could not create ZipArchive without images');
		return false;
	}

	// Parse each path
	$files = array();
	while ($photo = $photos->fetch_object()) {

		// Parse url
		$photo->url = LYCHEE_UPLOADS_BIG . $photo->url;

		// Parse title
		$photo->title = str_replace($badChars, '', $photo->title);
		if (!isset($photo->title)||$photo->title==='') $photo->title = 'Untitled';

		// Check if readable
		if (!@is_readable($photo->url)) continue;

		// Get extension of image
		$extension = getExtension($photo->url, false);

		// Set title for photo
		$zipFileName = $zipTitle . '/' . $photo->title . $extension;

		// Check for duplicates
		
		if (!empty($files)) {
			$i = 1;
			while (in_array($zipFileName, $files)) {
				// Set new title for photo
				$zipFileName = $zipTitle . '/' . $photo->title . '-' . $i . $extension;

				$i++;

			}
		}

		// Add to array
		$files[] = $zipFileName;

		// Add photo to zip
		$zip->addFile($photo->url, $zipFileName);

	}
	// Finish zip
	$zip->close();

	// Send zip
	header("Content-Type: application/zip");
	header("Content-Disposition: attachment; filename=\"$zipTitle.zip\"");
	header("Content-Length: " . filesize($filename));

	//変更ここから
	while (ob_get_level() > 0) {
		ob_end_clean();
	}
	ob_start();

	$file = fopen($filename, 'rb');
	if(!$file) {
		exit;
	}
	while(!feof($file) and (connection_status() == 0)) {
		 echo fread($file, '1048576'); 
		 ob_flush();
		
	  }
	ob_flush();
	ob_end_clean();
	//変更ここまで

	// Delete zip
	unlink($filename);

	// Call plugins
	Plugins::get()->activate(__METHOD__, 1, func_get_args());

	return true;

}

こちらの記事を参考にさせていただきました。ありがとうございます。
every-rating.com

最後に

いかがでしょうか。不具合が多々あるので、実用可能ではないかもしれませんが、おしゃれで使い勝手がいいので、一度使ってみてはいかがでしょうか。

ではまた。

Androidでデータベースの作成・更新中にProgressDialogを表示する

こんにちは。
今日は久しぶりにAndroidネタです。

開発中のアプリでSQLiteを使ったデータベースを使用しているのですが、今回そのデータベースの構造自体を大きく変更する必要が出てきました。

データベースの変更には少し時間がかかる上に、その間にどこか別の場所をタップされたりなんかしたら非常に面倒です。

ということで、この更新処理中にProgressDialogを表示させることにしました。

意外と手間取りましたので、メモしておきます。

ProgressDialogとは

ProgressDialogというのは、これです。
f:id:takebo_blog:20170811172139p:plain
上の画像では少しわかりづらいですが、左にはスピナー、右にはメッセージが表示されるダイアログです。

みなさんも、どこかでご覧になったことがあると思います。
よくデータを取得するときなんかに表示されたりしますよね。

今回は、これをデータベースの準備中に表示させようと思います。

実装

実装方法は非常にシンプル。
DatabaseHelperの中に、AsyncTaskを盛り込むだけです。

DatabaseHelper.java

public class DatabaseHelper extends SQLiteOpenHelper {

    private Context appContext;
    private int oldVersion,newVersion;

    public DatabaseHelper(Context context){
        super(context,"database.db",null,2);
        this.appContext = context;
    }

    @Override
    public void onCreate(SQLiteDatabase db){
        db.execSQL("テーブルの作成処理をここへ");
        db.close();
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion){
        this.oldVersion = oldVersion;
        this.newVersion = newVersion;
        new updateDB().execute(db);
    }

    public class updateDB extends AsyncTask<SQLiteDatabase,Void,Integer>{
        ProgressDialog progressDialog;

        @Override
        protected void onPreExecute(){
            super.onPreExecute();
            progressDialog = ProgressDialog.show(appContext,"データを更新中","この処理には少し時間がかかる場合があります");
        }

        @Override
        protected Integer doInBackground(SQLiteDatabase... params) {
            SQLiteDatabase db = params[0];
            //データベースの更新処理
            if(oldVersion == 1 && newVersion == 2) {
                db.execSQL("更新処理をここへ");
            }
            db.close();
            return 1;
        }

        @Override
        protected void onPostExecute(Integer result){
            progressDialog.dismiss();
            super.onPostExecute(result);
        }
    }
}

非同期処理の中にデータベースの更新処理を入れることで、ダイアログを表示しながら処理ができるようになります。
非同期処理を呼び出す際には、updateDB().execute()を使用します。

ポイントは、doInBackgroundの引数です。
execute()の引数がdoInBackgroundのparamsに配列として渡されます。

そのため、今回は変数dbをparams[0]に格納して受け渡ししています。

最後に

まとめてみると、とても簡単ですね。
ではまた。

Let's Encryptとはなにか、について調べる

lets-encrypt
こんにちは。ご無沙汰しております。

本業の方が大変忙しく、最近更新できておりませんでしたが、今日は張り切っていきたいと思います!

今日の話題は、Let's Encryptです。
こちらのブログでもたびたび話題にして参りました。

Let's Encryptは何者なのか

最近、ふと思ったんです。Let's Encryptって何者!?

皆さんの中にも私のように、不思議に思われている方もいらっしゃるのではないでしょうか。

ん?Let's Encryptって何?という方もいらっしゃると思いますので、簡単に説明しておきます。もう分かってるよ、という方は読み飛ばしてください。すでに理解されている方は、そもそもこちらの記事なんか読まないかもしれませんが(笑)

Let's Encryptとは

Let's Encryptは、無料SSL証明書を取得することが出来るサービスです。

そもそもSSL証明書って?

SSL証明書とは、httpsでの通信を可能にするために必要なものです。

URLがhttps:// で始まるホームページはご覧になったこと、ありませんか?
Chromehttpsのサイトにアクセスすると、アドレスバーの左端に緑の鍵マークが現れます。
https

httpsSSLという暗号化技術によって、データのやりとりを暗号化するものです。

そう、SSL証明書SSLは、暗号化技術の名称を表していたんです。

例えるなら、SSL証明書運転免許証のようなものです。

運転免許証を持っている人は、しっかりとした運転技術を持っていると認められた人ですから、車が運転することを許可されたわけです。
さらに公的に発行された信頼度のあるものですから、身分証明にも使われますよね。

SSL証明書もこれに似たような機能を持っています。
暗号化を行うためには、と呼ばれる情報が必要です。
SSL証明書は、この鍵にあたる情報が含まれているため、ブラウザがこの鍵を使って暗号化を行うことが出来るようになります。

SSL証明書があって初めて暗号化することができる、というところが運転免許証と似ていますね。

さらに、SSLは相手の保証を行うものでもあります。
通信相手が実在することを、公的に信頼された機関が確認し、保証してくれます。
これがあるので、利用者は安心して暗号化できるわけです。

身元を保証する、というところが運転免許証と似ています。

一般的に、SSL証明書有料です。企業などで使われているものは、何万単位の費用がかかる高級(?)なものです。

無料で手に入る証明書

無料でこれを手に入れようと思うと、主に手段は主に

の2つがあります。

オレオレ証明書とはなにか

オレオレ証明書というのは、自分で作成した証明書のことです。

本来、専門の機関によって発行されるべき証明書を自分で生み出すわけですから、当然費用はかかりません。
しかし逆に、自作した証明書ということは、公的な機関から認証を受けない、ということになります。よって、この証明書に対しての信頼度は低くなるです。

ですから、ブラウザを通してオレオレ証明書を使ったサイトにアクセスすると、デカデカと警告が出てしまいます
このサイト、危険なんじゃない?という警告です。だって、誰かよく分からない鍵を暗号化に使ってるんですから。

そんなサイトにアクセスしますか?私ならしないでしょうね(笑)

なぜオレオレ証明書が必要なのか

では、なぜオレオレ証明書なるものが存在するかと言いますと、そもそも証明書がなければhttpsで通信できないからです。

これは先ほど説明したとおり、鍵が必ず必要だからです。
逆に、証明書さえあればhttpsで通信できます。

オレオレ証明書なら、通信する相手を保証してもらうことはできないけど、とにかく暗号化通信が始められるわけです。
昔は公的な機関から発行される証明書は有料のものしかなかったため、お金をかけずにとにかく暗号化するために、これが使われていました。

ここまでの話でお分かりの通り、オレオレ証明書は一般向けのサイトには向きません。だって、警告が出てしまいますから。
しかし、社内ネットワークで暗号化通信したい場合など、相手がすでに分かっていて保証の必要がない場合では、これで十分です。ブラウザを通さずにhttpsにて通信する場合もそうですね。

Let'sEncryptとは?

それに対して、Let'sEncryptとはなんでしょうか。

Let's Encryptにて提供されるのは、秘密鍵証明書中間証明書の3種類で、公的に信頼度のある証明書です。
これが無料で取得できるんです。

では、Let's Encryptとオレオレ証明書はどう違うのでしょうか。
ずばり、これは誰が発行するか、です。

オレオレ証明書が個人が勝手に発行したものであるのに対して、Let's Encryptはしっかりとした第三者機関によって発行されています。
これにより証明書に対する信頼度が格段に違うため、Let's Encryptで発行された証明書を使うと、無料でありながらブラウザで警告がでることなくhttpsで通信できるのです。

Let's Encryptは何者なのか

ここまで書いてまいりましたが、私はこんな疑問を抱いていました。

  • どこが発行しているのか
  • なぜ無料なのか
  • どのような仕組みなのか
  • 今後なくなることはないのか
  • 誰でも発行できれば、サイトの信頼性を保証できないのではないか

ここからは、これらの疑問を一つ一つ解決していく形で、書き進めていきます。

Q1.どこが発行しているのか

一体どこが発行しているのでしょうか。
かなりの普及を見せているLet'sEncryptですから、発行元が怪しいところではないと思われますが、気になります。

なぜこんなに信頼されているのでしょうか。

A1.ISRGという非営利団体がサービスを提供している

では、ここでLet'sEncryptの公式ページを見てみましょう。

うわ、英語かよ!となった方、大丈夫ですよ!
非公式ですが、Let'sEncryptについてまとめている日本語サイトがあります。

こちらのLet'sEncryptについて、から少し引用させてもらいます。

非営利団体の ISRG (Internet Security Research Group) が運営しており、シスコ(Cisco Systems)、Akamai電子フロンティア財団(Electronic Frontier Foundation)、モジラ財団(Mozilla Foundation)などの大手企業・団体が、ISRG のスポンサーとして Let's Encrypt を支援しています。

これによると、ISRGという機関が運営しているようです。
調べてみると、ISRGというのはアメリカにある公益法人で、経済的・技術的・教育的な面でインターネット上での安全な通信を行う障壁を減らすことを目的としているようです。

firefoxの開発で有名なmozillaやサーバーなどの製品やサービスを手がけるCisco情報理論を確立した学者シャノンの出身校であるミシガン大学などの関係者が役員をつとめています。

さらにISRGは、先ほど紹介したmozillaCisco、さらにはfacebookchromeWordpress.comを運営するautomatticや日本でも利用者の多いJimdoなど、様々な企業や団体がスポンサーとなっています。

このスポンサーを見ると、ネット業界で名を馳せたサービスや商品のプロバイダばかりです。chromemozillaが関わっているとなると、信頼度が違いますね。

だからLet'sEncryptは信頼されているのです。逆にブラウザ側からすれば、自分たちがお金を出しているところから発行されているから、信用するといったところでしょうか。

Q2.なぜ無料なのか

無料の理由、これが気になります。どうして無料なのでしょうか。
もしかして、有料サービスのお試しとか?なんて思ってしまいます。

先ほどのISRGのスポンサーを見ていると、有料サービスのお試し、ということはなさそうですが…

A2.非営利団体が運営する全自動認証のSSL証明書だから

では、先ほどのLet'sEncrypt日本語サイトを見てみましょう。

Let's Encrypt は、認証局(CA)として「SSL/TLSサーバ証明書」を無料で発行するとともに、証明書の発行・インストール・更新のプロセスを自動化することにより、TLSHTTPSTLSプロトコルによって提供されるセキュアな接続の上でのHTTP通信)を普及させることを目的としているプロジェクトです。

ここが、無料である一つの理由です。

Let'sEncryptの手続きはすべて自動で認証されています。
つまり、人の手が関わっていないわけです。
ですから、運営にかかる費用がうんと低くなるわけです。

さらに運営元のISRGは非営利団体ですから、元からお金を取る気はありません。

こんな理由で、無料で利用できるわけです。
役員のメンバーやスポンサーを見た限り、今後も有料化することはないでしょう。

先ほど、非営利団体が運営する全自動認証のSSL証明書だから無料なのだ、と書きましたが、私はその奥に別の理由もあるのではないかと思っています。

発行を行うISRGの出資元は、ほとんどが企業ですから、なにか目的があって出資するのだと思います。

例えば、chromeの開発元であるgoogleは、常時SSLを推奨しています。
これはサイトがハッキングされ、マルウェアに感染することを防ぎたいと考えているようです。これらのマルウェアに汚染されたサイトが、自分達のサービスに悪影響を与えるかもしれませんから。

しかし、有料の証明書しかなければ、金銭的な理由からなかなか常時SSLのサイトは増えません。
個人でやるブログなんかは、あまり常時SSLにする必要性を感じにくいですから。

ですから、常時SSLの普及にはLet'sEncryptのようなサービスが欠かせないわけですね。

このように、様々な企業や団体の思惑が重なった結果として、Let'sEncryptという無料のサービスが出来上がったわけです。

こんな理由もあるのではないかな、と私は思います。

Q3.どのような仕組みなのか

先ほど、自動化という出てきましたが、どのようにして証明書の発行を行うのでしょうか。

人力の発行ではない、ということは何となくお分かりいただけているのでは、と思います。

A3.クライアントソフトと認証局が発行を自動化している

Let'sEncryptにおける認証方法

認証には、独自のクライアントソフトを使って行います。

このクライアントソフトはcertbotと呼ばれ、多くのLinuxディストリビューションmacOS版が公開されています。
残念ながらWindows版のcertbotはないので、ほかのツールを使うか、最近Windowsで利用可能となったbashを使うこととなります。

取得したいドメインを本当に証明するために、Let'sEncryptには主に2種類の認証方法が用意されています。それは

  • ドメインDNSレコードに指定されたレコードが存在するか
  • 指定されたURLに認証用ファイルが存在するか

の2つです。

DNSでの認証では、_acme-challenge.(自分のドメイン名)に指定されたTXTレコードを設定します。
これを行うためには、DNSを自分で設定できる環境でなければならず、レンタルサーバーでは実現出来ないことが多々あります。

ファイルにて認証する場合、指定されたパスに指定されたファイルを置き、それを確認するというものです。
これは多くの方が実行可能でしょう。ホームページを運営しているなら、ファイルをアップできない、なんてことはあり得ませんから。

しかし、メールサーバーなどではこれは可能でないかもしれません。そんなときはDNS認証を行います。

認証を行う仕組み - ACME

認証の仕組みは少し専門用語が多くなります。暗号化に対する基礎知識がないと厳しいかもしれません。

でも大丈夫です。認証の仕組みを知らなくてもちゃんと使えるようになっています!

認証には、ACMEという通信プロトコルにて、クライアントソフトと認証局とのやりとりが行われます。

まず、クライアントソフトは認証してほしいドメイン認証局に通知します。

認証局はその通知に対して、認証に必要な情報をクライアントソフトに送ります。
ここで送られる情報は、認証用の番号認証用ファイルで、認証局によって自動的に生成されます。
認証用ファイルは、先ほど説明したファイル認証でサーバーにアップロードする為のもので、DNS認証の場合にはファイルの代わりに登録すべきDNSレコードが送られます。

クライアント側は認証に必要な設定(ファイル認証なら指定のパスにファイルをアップロードする)を行った後、認証のための準備ができたことを認証局に通知します。
このとき同時に、先ほど送られた認証用番号をクライアントソフトが生成した秘密鍵で署名し、公開鍵とともに認証局へ送ります。

認証局が認証に必要な設定がされたかを確認し、またクライアントソフトから送られてきた認証用番号の確認を行います。

すべて確認が終了すると、正式に認証されたことがクライアントソフトに通知されます。

この一連の流れで認証が自動で行われます。私たちが直接行わなければならないのはクライアントソフトの実行認証のための設定のみです。
認証自体の仕組みについては理解する必要はありません。

一度証明書が発行されたら、更新するのは簡単で、認証作業は必要ありません。

こちらが大変わかりやすく、参考になりました。詳しくは公式ページにも書かれています。

Q4.今後なくなることはないのか

Webサービスは、突如開発が中止されることはよくあります。

また、どこかの企業に吸収されてしまう、ということもしばしばです。

急に有料化してしまったら、httpsに対応させたものをわざわざhttpに戻さないといけないかもしれません。

それは非常に面倒ですよね。

では、無くなることはないのでしょうか。

A4.無くならない可能性が高い

正直なところ、Let'sEncryptが今後なくなる可能性はないとは言えません

しかし、googleSSLに対応しているかどうかを検索順位に考慮する、と発表するように、httpsの利用はさらに重視され普及が進むと思います。

ISRGが掲げる理念を基に考えると、この団体に金銭的な問題が起こらない限り、Let'sEncryptは無くならないでしょう。

スポンサーを見ると、主要なブラウザを開発する団体が資金提供を行っていますから、金銭的問題も起こりにくいのではないでしょうか。

つまり、Let'sEncryptが急になくなるとは考えにくいように思います。

Q5.誰でも発行できれば、サイトの信頼性を保証できないのではないか

最後の疑問となりました。
いろいろ調べていて、ふと思いました。

誰もが証明書を取得できたら、信頼性のある証明書では無いのではないでしょうか。

例えば、詐欺を行おうとする人であっても取得できるわけですから、サイトに対する信頼性を証明することが出来ないのではないか、と。

しかし、ここに私の思い違いがありました。

A5.サイト自体の信頼性を保証するSSL証明書は種類が別

SSL証明書httpsによって通信するためのツールであって、サイト自体の安全性を必ずしも保証するものではないのです。

私自身、『SSL証明書 = 安全性を保証する』という感覚を持っていましたが、実際にはそうとは限りません。

こちらにその事が丁寧に書かれています。

このページから引用すると、

SSL/TLSサーバ証明書には「ドメイン認証」「企業実在認証」「Extended Validation(EV)」の3種類が存在します。ドメイン認証は最も手軽な反面、ドメインの使用権のみを認証する証明書しか発行できません。組織の実在認証をするためには「企業実在認証」、「EV」を導入する必要があります。また企業実在認証もドメインの使用権と組織の法的実在の認証は可能ですが、それ以上の「組織の物理的実在」「組織の運営」などのより高度な認証を行うにはEVの導入が必要です。

と書かれています。

ここで書かれているように、企業実在認証EVでなければちゃんとした通信相手なのかを保証することはできないということです。

Let'sEncryptはドメイン認証を行うだけですから、ドメインが存在することを証明しただけですから、相手がどうであるかは証明されていないのです。

しかし、ブラウザでは緑の鍵マークがでて、いかにも安全です、という印象を与えます。

これでは、もし詐欺サイトで使われていた場合、間違った印象を与えるでしょう。

私はこれがLet'sEncryptの欠点なのではないかと思います。

証明書が有料であった昔は、証明書が安全の証のようになっていましたが、これを改める時が来たのかもしれません。

最後に

長々と書いてまいりましたが、いかがだったでしょうか。

Let'sEncryptは大変素晴らしいサービスですので、皆さんにぜひぜひ使っていただきたいです。

ではまた。