#include <algorithm>
#include <functional>
#include <iostream>
#include <numeric>
#include <vector>

std::vector<int> seqLen(int start, int end) {
    std::vector<int> result;

    if (start == end) {
        result.resize(end + 1);
        std::iota(result.begin(), result.end(), 1);
    } else if (start < end) {
        result.resize(end - start + 1);
        std::iota(result.begin(), result.end(), start);
    } else {
        result.resize(start - end + 1);
        std::iota(result.rbegin(), result.rend(), end);
    }

    return result;
}

std::vector<int> order(const std::vector<double>& arr, bool decreasing) {
    std::vector<int> idx(arr.size());
    std::iota(idx.begin(), idx.end(), 0);

    std::function<bool(int, int)> cmp;
    if (decreasing) {
        cmp = [&arr](int a, int b) { return arr[b] < arr[a]; };
    } else {
        cmp = [&arr](int a, int b) { return arr[a] < arr[b]; };
    }

    std::sort(idx.begin(), idx.end(), cmp);
    return idx;
}

std::vector<double> cummin(const std::vector<double>& arr) {
    if (arr.empty()) throw std::runtime_error("cummin requries at least one element");
    std::vector<double> output(arr.size());
    double cumulativeMin = arr[0];
    std::transform(arr.cbegin(), arr.cend(), output.begin(), [&cumulativeMin](double a) {
        if (a < cumulativeMin) cumulativeMin = a;
        return cumulativeMin;
    });
    return output;
}

std::vector<double> cummax(const std::vector<double>& arr) {
    if (arr.empty()) throw std::runtime_error("cummax requries at least one element");
    std::vector<double> output(arr.size());
    double cumulativeMax = arr[0];
    std::transform(arr.cbegin(), arr.cend(), output.begin(), [&cumulativeMax](double a) {
        if (cumulativeMax < a) cumulativeMax = a;
        return cumulativeMax;
    });
    return output;
}

std::vector<double> pminx(const std::vector<double>& arr, double x) {
    if (arr.empty()) throw std::runtime_error("pmin requries at least one element");
    std::vector<double> result(arr.size());
    std::transform(arr.cbegin(), arr.cend(), result.begin(), [&x](double a) {
        if (a < x) return a;
        return x;
    });
    return result;
}

void doubleSay(const std::vector<double>& arr) {
    printf("[ 1] %.10f", arr[0]);
    for (size_t i = 1; i < arr.size(); ++i) {
        printf(" %.10f", arr[i]);
        if ((i + 1) % 5 == 0) printf("\n[%2d]", i + 1);
    }
}

std::vector<double> pAdjust(const std::vector<double>& pvalues, const std::string& str) {
    if (pvalues.empty()) throw std::runtime_error("pAdjust requires at least one element");
    size_t size = pvalues.size();

    int type;
    if ("bh" == str || "fdr" == str) {
        type = 0;
    } else if ("by" == str) {
        type = 1;
    } else if ("bonferroni" == str) {
        type = 2;
    } else if ("hochberg" == str) {
        type = 3;
    } else if ("holm" == str) {
        type = 4;
    } else if ("hommel" == str) {
        type = 5;
    } else {
        throw std::runtime_error(str + " doesn't match any accepted FDR types");
    }

    // Bonferroni method
    if (2 == type) {
        std::vector<double> result(size);
        for (size_t i = 0; i < size; ++i) {
            double b = pvalues[i] * size;
            if (b >= 1) {
                result[i] = 1;
            } else if (0 <= b && b < 1) {
                result[i] = b;
            } else {
                throw std::runtime_error("a value is outside [0, 1)");
            }
        }
        return result;
    }
    // Holm method
    else if (4 == type) {
        auto o = order(pvalues, false);
        std::vector<double> o2Double(o.begin(), o.end());
        std::vector<double> cummaxInput(size);
        for (size_t i = 0; i < size; ++i) {
            cummaxInput[i] = (size - i) * pvalues[o[i]];
        }
        auto ro = order(o2Double, false);
        auto cummaxOutput = cummax(cummaxInput);
        auto pmin = pminx(cummaxOutput, 1.0);
        std::vector<double> result(size);
        std::transform(ro.cbegin(), ro.cend(), result.begin(), [&pmin](int a) { return pmin[a]; });
        return result;
    }
    // Hommel
    else if (5 == type) {
        auto indices = seqLen(size, size);
        auto o = order(pvalues, false);
        std::vector<double> p(size);
        std::transform(o.cbegin(), o.cend(), p.begin(), [&pvalues](int a) { return pvalues[a]; });
        std::vector<double> o2Double(o.begin(), o.end());
        auto ro = order(o2Double, false);
        std::vector<double> q(size);
        std::vector<double> pa(size);
        std::vector<double> npi(size);
        for (size_t i = 0; i < size; ++i) {
            npi[i] = p[i] * size / indices[i];
        }
        double min = *std::min_element(npi.begin(), npi.end());
        std::fill(q.begin(), q.end(), min);
        std::fill(pa.begin(), pa.end(), min);
        for (int j = size; j >= 2; --j) {
            auto ij = seqLen(1, size - j + 1);
            std::transform(ij.cbegin(), ij.cend(), ij.begin(), [](int a) { return a - 1; });
            int i2Length = j - 1;
            std::vector<int> i2(i2Length);
            for (int i = 0; i < i2Length; ++i) {
                i2[i] = size - j + 2 + i - 1;
            }
            double q1 = j * p[i2[0]] / 2.0;
            for (int i = 1; i < i2Length; ++i) {
                double temp_q1 = p[i2[i]] * j / (2.0 + i);
                if (temp_q1 < q1) q1 = temp_q1;
            }
            for (size_t i = 0; i < size - j + 1; ++i) {
                q[ij[i]] = std::min(p[ij[i]] * j, q1);
            }
            for (int i = 0; i < i2Length; ++i) {
                q[i2[i]] = q[size - j];
            }
            for (size_t i = 0; i < size; ++i) {
                if (pa[i] < q[i]) {
                    pa[i] = q[i];
                }
            }
        }
        std::transform(ro.cbegin(), ro.cend(), q.begin(), [&pa](int a) { return pa[a]; });
        return q;
    }

    std::vector<double> ni(size);
    std::vector<int> o = order(pvalues, true);
    std::vector<double> od(o.begin(), o.end());
    for (size_t i = 0; i < size; ++i) {
        if (pvalues[i] < 0 || pvalues[i]>1) {
            throw std::runtime_error("a value is outside [0, 1]");
        }
        ni[i] = (double)size / (size - i);
    }
    auto ro = order(od, false);
    std::vector<double> cumminInput(size);
    if (0 == type) {        // BH method
        for (size_t i = 0; i < size; ++i) {
            cumminInput[i] = ni[i] * pvalues[o[i]];
        }
    } else if (1 == type) { // BY method
        double q = 0;
        for (size_t i = 1; i < size + 1; ++i) {
            q += 1.0 / i;
        }
        for (size_t i = 0; i < size; ++i) {
            cumminInput[i] = q * ni[i] * pvalues[o[i]];
        }
    } else if (3 == type) { // Hochberg method
        for (size_t i = 0; i < size; ++i) {
            cumminInput[i] = (i + 1) * pvalues[o[i]];
        }
    }
    auto cumminArray = cummin(cumminInput);
    auto pmin = pminx(cumminArray, 1.0);
    std::vector<double> result(size);
    for (size_t i = 0; i < size; ++i) {
        result[i] = pmin[ro[i]];
    }
    return result;
}

int main() {
    using namespace std;

    vector<double> pvalues{
        4.533744e-01, 7.296024e-01, 9.936026e-02, 9.079658e-02, 1.801962e-01,
        8.752257e-01, 2.922222e-01, 9.115421e-01, 4.355806e-01, 5.324867e-01,
        4.926798e-01, 5.802978e-01, 3.485442e-01, 7.883130e-01, 2.729308e-01,
        8.502518e-01, 4.268138e-01, 6.442008e-01, 3.030266e-01, 5.001555e-02,
        3.194810e-01, 7.892933e-01, 9.991834e-01, 1.745691e-01, 9.037516e-01,
        1.198578e-01, 3.966083e-01, 1.403837e-02, 7.328671e-01, 6.793476e-02,
        4.040730e-03, 3.033349e-04, 1.125147e-02, 2.375072e-02, 5.818542e-04,
        3.075482e-04, 8.251272e-03, 1.356534e-03, 1.360696e-02, 3.764588e-04,
        1.801145e-05, 2.504456e-07, 3.310253e-02, 9.427839e-03, 8.791153e-04,
        2.177831e-04, 9.693054e-04, 6.610250e-05, 2.900813e-02, 5.735490e-03
    };

    vector<vector<double>> correctAnswers{
        // Benjamini-Hochberg
        {
            6.126681e-01, 8.521710e-01, 1.987205e-01, 1.891595e-01, 3.217789e-01,
            9.301450e-01, 4.870370e-01, 9.301450e-01, 6.049731e-01, 6.826753e-01,
            6.482629e-01, 7.253722e-01, 5.280973e-01, 8.769926e-01, 4.705703e-01,
            9.241867e-01, 6.049731e-01, 7.856107e-01, 4.887526e-01, 1.136717e-01,
            4.991891e-01, 8.769926e-01, 9.991834e-01, 3.217789e-01, 9.301450e-01,
            2.304958e-01, 5.832475e-01, 3.899547e-02, 8.521710e-01, 1.476843e-01,
            1.683638e-02, 2.562902e-03, 3.516084e-02, 6.250189e-02, 3.636589e-03,
            2.562902e-03, 2.946883e-02, 6.166064e-03, 3.899547e-02, 2.688991e-03,
            4.502862e-04, 1.252228e-05, 7.881555e-02, 3.142613e-02, 4.846527e-03,
            2.562902e-03, 4.846527e-03, 1.101708e-03, 7.252032e-02, 2.205958e-02
        },
        // Benjamini & Yekutieli
        {
            1.000000e+00, 1.000000e+00, 8.940844e-01, 8.510676e-01, 1.000000e+00,
            1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
            1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
            1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 5.114323e-01,
            1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
            1.000000e+00, 1.000000e+00, 1.754486e-01, 1.000000e+00, 6.644618e-01,
            7.575031e-02, 1.153102e-02, 1.581959e-01, 2.812089e-01, 1.636176e-02,
            1.153102e-02, 1.325863e-01, 2.774239e-02, 1.754486e-01, 1.209832e-02,
            2.025930e-03, 5.634031e-05, 3.546073e-01, 1.413926e-01, 2.180552e-02,
            1.153102e-02, 2.180552e-02, 4.956812e-03, 3.262838e-01, 9.925057e-02
        },
        // Bonferroni
        {
            1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
            1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
            1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
            1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
            1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
            1.000000e+00, 1.000000e+00, 7.019185e-01, 1.000000e+00, 1.000000e+00,
            2.020365e-01, 1.516674e-02, 5.625735e-01, 1.000000e+00, 2.909271e-02,
            1.537741e-02, 4.125636e-01, 6.782670e-02, 6.803480e-01, 1.882294e-02,
            9.005725e-04, 1.252228e-05, 1.000000e+00, 4.713920e-01, 4.395577e-02,
            1.088915e-02, 4.846527e-02, 3.305125e-03, 1.000000e+00, 2.867745e-01
        },
        // Hochberg
        {
            9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01,
            9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01,
            9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01,
            9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01,
            9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01,
            9.991834e-01, 9.991834e-01, 4.632662e-01, 9.991834e-01, 9.991834e-01,
            1.575885e-01, 1.383967e-02, 3.938014e-01, 7.600230e-01, 2.501973e-02,
            1.383967e-02, 3.052971e-01, 5.426136e-02, 4.626366e-01, 1.656419e-02,
            8.825610e-04, 1.252228e-05, 9.930759e-01, 3.394022e-01, 3.692284e-02,
            1.023581e-02, 3.974152e-02, 3.172920e-03, 8.992520e-01, 2.179486e-01
        },
        // Holm
        {
            1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
            1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
            1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
            1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
            1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
            1.000000e+00, 1.000000e+00, 4.632662e-01, 1.000000e+00, 1.000000e+00,
            1.575885e-01, 1.395341e-02, 3.938014e-01, 7.600230e-01, 2.501973e-02,
            1.395341e-02, 3.052971e-01, 5.426136e-02, 4.626366e-01, 1.656419e-02,
            8.825610e-04, 1.252228e-05, 9.930759e-01, 3.394022e-01, 3.692284e-02,
            1.023581e-02, 3.974152e-02, 3.172920e-03, 8.992520e-01, 2.179486e-01
        },
        // Hommel
        {
            9.991834e-01, 9.991834e-01, 9.991834e-01, 9.987624e-01, 9.991834e-01,
            9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01,
            9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01,
            9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01, 9.595180e-01,
            9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01,
            9.991834e-01, 9.991834e-01, 4.351895e-01, 9.991834e-01, 9.766522e-01,
            1.414256e-01, 1.304340e-02, 3.530937e-01, 6.887709e-01, 2.385602e-02,
            1.322457e-02, 2.722920e-01, 5.426136e-02, 4.218158e-01, 1.581127e-02,
            8.825610e-04, 1.252228e-05, 8.743649e-01, 3.016908e-01, 3.516461e-02,
            9.582456e-03, 3.877222e-02, 3.172920e-03, 8.122276e-01, 1.950067e-01
        }
    };

    vector<string> types{ "bh", "by", "bonferroni", "hochberg", "holm", "hommel" };
    for (size_t type = 0; type < types.size(); ++type) {
        auto q = pAdjust(pvalues, types[type]);
        double error = 0.0;
        for (size_t i = 0; i < pvalues.size(); ++i) {
            error += abs(q[i] - correctAnswers[type][i]);
        }
        doubleSay(q);
        printf("\ntype = %d = '%s' has a cumulative error of %g\n\n\n", type, types[type].c_str(), error);
    }

    return 0;
}


import std.algorithm;
import std.conv;
import std.math;
import std.stdio;
import std.string;

int[] seqLen(int start, int end) {
    int[] result;
    if (start == end) {
        result.length = end+1;
        for (int i; i<result.length; i++) {
            result[i] = i+1;
        }
    } else if (start < end) {
        result.length = end - start + 1;
        for (int i; i<result.length; i++) {
            result[i] = start+i;
        }
    } else {
        result.length = start - end + 1;
        for (int i; i<result.length; i++) {
            result[i] = start-i;
        }
    }
    return result;
}

int[] order(double[] array, bool decreasing) {
    int size = array.length;
    int[] idx;
    idx.length = size;
    double[] baseArr;
    baseArr.length = size;
    for (int i; i<size; i++) {
        baseArr[i] = array[i];
        idx[i] = i;
    }
    if (!decreasing) {
        alias comp = (a,b) => baseArr[a] < baseArr[b];
        idx.sort!comp;
    } else {
        alias comp = (a,b) => baseArr[b] < baseArr[a];
        idx.sort!comp;
    }
    return idx;
}

double[] cummin(double[] array) {
    int size = array.length;
    if (size < 1) throw new Exception("cummin requires at least one element");
    double[] output;
    output.length = size;
    auto cumulativeMin = array[0];
    foreach (i; 0..size) {
        if (array[i] < cumulativeMin) cumulativeMin = array[i];
        output[i] = cumulativeMin;
    }
    return output;
}

double[] cummax(double[] array) {
    auto size = array.length;
    if (size < 1) throw new Exception("cummax requires at least one element");
    double[] output;
    output.length = size;
    auto cumulativeMax = array[0];
    foreach (i; 0..size) {
        if (array[i] > cumulativeMax) cumulativeMax = array[i];
        output[i] = cumulativeMax;
    }
    return output;
}

double[] pminx(double[] array, double x) {
    auto size = array.length;
    if (size < 1) throw new Exception("pmin requires at least one element");
    double[] result;
    result.length = size;
    foreach (i; 0..size) {
        if (array[i] < x) {
            result[i] = array[i];
        } else {
            result[i] = x;
        }
    }
    return result;
}

void doubleSay(double[] array) {
    writef("[ 1] %e", array[0]);
    foreach (i; 1..array.length) {
        writef(" %.10f", array[i]);
        if ((i+1) % 5 == 0) writef("\n[%2d]", i+1);
    }
    writeln;
}

auto toArray(T,U)(U[] array) {
    T[] result;
    result.length = array.length;
    foreach(i; 0..array.length) {
        result[i] = to!T(array[i]);
    }
    return result;
}

double[] pAdjust(double[] pvalues, string str) {
    auto size = pvalues.length;
    if (size < 1) throw new Exception("pAdjust requires at least one element");
    int type = str.toLower.predSwitch!"a==b"(
        "bh",         0,
        "fdr",        0,
        "by",         1,
        "bonferroni", 2,
        "hochberg",   3,
        "holm",       4,
        "hommel",     5,
        { throw new Exception(text("'",str,"' doesn't match any accepted FDR types")); }()
    );
    if (type == 2) {  // Bonferroni method
        double[] result;
        result.length = size;
        foreach (i; 0..size) {
            auto b = pvalues[i] * size;
            if (b >= 1) {
                result[i] = 1;
            } else if (0 <= b && b < 1) {
                result[i] = b;
            } else {
                throw new Exception(text(b," is outside [0, 1)"));
            }
        }
        return result;
    } else if (type == 4) {  // Holm method
        auto o = order(pvalues, false);
        auto o2Double = toArray!(double,int)(o);
        double[] cummaxInput;
        cummaxInput.length = size;
        foreach (i; 0..size) {
            cummaxInput[i] = (size-i) * pvalues[o[i]];
        }
        auto ro = order(o2Double, false);
        auto cummaxOutput = cummax(cummaxInput);
        auto pmin = pminx(cummaxOutput, 1.0);
        double[] result;
        result.length = size;
        foreach (i; 0..size) {
            result[i] = pmin[ro[i]];
        }
        return result;
    } else if (type == 5) {
        auto indices = seqLen(size, size);
        auto o = order(pvalues, false);
        double[] p;
        p.length = size;
        foreach (i; 0..size) {
            p[i] = pvalues[o[i]];
        }
        auto o2Double = toArray!double(o);
        auto ro = order(o2Double, false);
        double[] q;
        q.length = size;
        double[] pa;
        pa.length = size;
        double[] npi;
        npi.length = size;
        foreach (i; 0..size) {
            npi[i] = p[i] * size / indices[i];
        }
        auto min_ = reduce!min(npi);
        q[] = min_;
        pa[] = min_;
        foreach_reverse (j; 2..size) {
            auto ij = seqLen(1, size - j + 1);
            foreach (i; 0..size-j+1) {
                ij[i]--;
            }
            auto i2Length = j-1;
            int[] i2;
            i2.length = i2Length;
            foreach(i; 0..i2Length) {
                i2[i] = size-j+2+i-1;
            }
            auto pi2Length = i2Length;
            double q1 = j*p[i2[0]] / 2.0;
            foreach (i; 1..pi2Length) {
                auto temp_q1 = p[i2[i]] * j / (2.0 + i);
                if (temp_q1 < q1) q1 = temp_q1;
            }
            foreach (i; 0..size-j+1) {
                q[ij[i]] = min(p[ij[i]] * j, q1);
            }
            foreach(i; 0..i2Length) {
                q[i2[i]] = q[size-j];
            }
            foreach(i; 0..size) if (pa[i] < q[i]) pa[i] = q[i];
        }
        foreach (index; 0..size) {
            q[index] = pa[ro[index]];
        }
        return q;
    }

    double[] ni;
    ni.length = size;
    auto o = order(pvalues, true);
    auto oDouble = toArray!double(o);
    foreach (index; 0..size) {
        if (pvalues[index] < 0 || pvalues[index] > 1) {
            throw new Exception(text("array[", index, "] = ", pvalues[index], " is outside [0, 1]"));
        }
        ni[index] = cast(double) size / (size - index);
    }
    auto ro = order(oDouble, false);
    double[] cumminInput;
    cumminInput.length = size;
    if (type == 0) {  // BH method
        foreach (index; 0..size) {
            cumminInput[index] = ni[index] * pvalues[o[index]];
        }
    } else if (type == 1) {  // BY method
        double q = 0;
        foreach (index; 1..size+1) q += 1.0 / index;
        foreach (index; 0..size) {
            cumminInput[index] = q * ni[index] * pvalues[o[index]];
        }
    } else if (type == 3) {  // Hochberg method
        foreach (index; 0..size) {
            cumminInput[index] = (index + 1) * pvalues[o[index]];
        }
    }
    auto cumminArray  =cummin(cumminInput);
    auto pmin = pminx(cumminArray, 1.0);
    double[] result;
    result.length = size;
    foreach (i; 0..size) {
        result[i] = pmin[ro[i]];
    }
    return result;
}

void main() {
    double[] pvalues = [
        4.533744e-01, 7.296024e-01, 9.936026e-02, 9.079658e-02, 1.801962e-01,
        8.752257e-01, 2.922222e-01, 9.115421e-01, 4.355806e-01, 5.324867e-01,
        4.926798e-01, 5.802978e-01, 3.485442e-01, 7.883130e-01, 2.729308e-01,
        8.502518e-01, 4.268138e-01, 6.442008e-01, 3.030266e-01, 5.001555e-02,
        3.194810e-01, 7.892933e-01, 9.991834e-01, 1.745691e-01, 9.037516e-01,
        1.198578e-01, 3.966083e-01, 1.403837e-02, 7.328671e-01, 6.793476e-02,
        4.040730e-03, 3.033349e-04, 1.125147e-02, 2.375072e-02, 5.818542e-04,
        3.075482e-04, 8.251272e-03, 1.356534e-03, 1.360696e-02, 3.764588e-04,
        1.801145e-05, 2.504456e-07, 3.310253e-02, 9.427839e-03, 8.791153e-04,
        2.177831e-04, 9.693054e-04, 6.610250e-05, 2.900813e-02, 5.735490e-03
    ];

    double[][] correctAnswers = [
        [  // Benjamini-Hochberg
            6.126681e-01, 8.521710e-01, 1.987205e-01, 1.891595e-01, 3.217789e-01,
            9.301450e-01, 4.870370e-01, 9.301450e-01, 6.049731e-01, 6.826753e-01,
            6.482629e-01, 7.253722e-01, 5.280973e-01, 8.769926e-01, 4.705703e-01,
            9.241867e-01, 6.049731e-01, 7.856107e-01, 4.887526e-01, 1.136717e-01,
            4.991891e-01, 8.769926e-01, 9.991834e-01, 3.217789e-01, 9.301450e-01,
            2.304958e-01, 5.832475e-01, 3.899547e-02, 8.521710e-01, 1.476843e-01,
            1.683638e-02, 2.562902e-03, 3.516084e-02, 6.250189e-02, 3.636589e-03,
            2.562902e-03, 2.946883e-02, 6.166064e-03, 3.899547e-02, 2.688991e-03,
            4.502862e-04, 1.252228e-05, 7.881555e-02, 3.142613e-02, 4.846527e-03,
            2.562902e-03, 4.846527e-03, 1.101708e-03, 7.252032e-02, 2.205958e-02
        ],
        [  // Benjamini & Yekutieli
            1.000000e+00, 1.000000e+00, 8.940844e-01, 8.510676e-01, 1.000000e+00,
            1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
            1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
            1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 5.114323e-01,
            1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
            1.000000e+00, 1.000000e+00, 1.754486e-01, 1.000000e+00, 6.644618e-01,
            7.575031e-02, 1.153102e-02, 1.581959e-01, 2.812089e-01, 1.636176e-02,
            1.153102e-02, 1.325863e-01, 2.774239e-02, 1.754486e-01, 1.209832e-02,
            2.025930e-03, 5.634031e-05, 3.546073e-01, 1.413926e-01, 2.180552e-02,
            1.153102e-02, 2.180552e-02, 4.956812e-03, 3.262838e-01, 9.925057e-02
        ],
        [  // Bonferroni
            1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
            1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
            1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
            1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
            1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
            1.000000e+00, 1.000000e+00, 7.019185e-01, 1.000000e+00, 1.000000e+00,
            2.020365e-01, 1.516674e-02, 5.625735e-01, 1.000000e+00, 2.909271e-02,
            1.537741e-02, 4.125636e-01, 6.782670e-02, 6.803480e-01, 1.882294e-02,
            9.005725e-04, 1.252228e-05, 1.000000e+00, 4.713920e-01, 4.395577e-02,
            1.088915e-02, 4.846527e-02, 3.305125e-03, 1.000000e+00, 2.867745e-01
        ],
        [  // Hochberg
            9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01,
            9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01,
            9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01,
            9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01,
            9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01,
            9.991834e-01, 9.991834e-01, 4.632662e-01, 9.991834e-01, 9.991834e-01,
            1.575885e-01, 1.383967e-02, 3.938014e-01, 7.600230e-01, 2.501973e-02,
            1.383967e-02, 3.052971e-01, 5.426136e-02, 4.626366e-01, 1.656419e-02,
            8.825610e-04, 1.252228e-05, 9.930759e-01, 3.394022e-01, 3.692284e-02,
            1.023581e-02, 3.974152e-02, 3.172920e-03, 8.992520e-01, 2.179486e-01
        ],
        [  // Holm
            1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
            1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
            1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
            1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
            1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
            1.000000e+00, 1.000000e+00, 4.632662e-01, 1.000000e+00, 1.000000e+00,
            1.575885e-01, 1.395341e-02, 3.938014e-01, 7.600230e-01, 2.501973e-02,
            1.395341e-02, 3.052971e-01, 5.426136e-02, 4.626366e-01, 1.656419e-02,
            8.825610e-04, 1.252228e-05, 9.930759e-01, 3.394022e-01, 3.692284e-02,
            1.023581e-02, 3.974152e-02, 3.172920e-03, 8.992520e-01, 2.179486e-01
        ],
        [   // Hommel
            9.991834e-01, 9.991834e-01, 9.991834e-01, 9.987624e-01, 9.991834e-01,
            9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01,
            9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01,
            9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01, 9.595180e-01,
            9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01,
            9.991834e-01, 9.991834e-01, 4.351895e-01, 9.991834e-01, 9.766522e-01,
            1.414256e-01, 1.304340e-02, 3.530937e-01, 6.887709e-01, 2.385602e-02,
            1.322457e-02, 2.722920e-01, 5.426136e-02, 4.218158e-01, 1.581127e-02,
            8.825610e-04, 1.252228e-05, 8.743649e-01, 3.016908e-01, 3.516461e-02,
            9.582456e-03, 3.877222e-02, 3.172920e-03, 8.122276e-01, 1.950067e-01
        ]
    ];
    auto types = ["bh", "by", "bonferroni", "hochberg", "holm", "hommel"];
    foreach (type; 0..types.length) {
        auto q = pAdjust(pvalues, types[type]);
        double error = 0.0;
        foreach (i; 0..pvalues.length) {
            error += abs(q[i] - correctAnswers[type][i]);
        }
        doubleSay(q);
        writefln("\ntype %d = '%s' has a cumulative error of %g", type, types[type], error);
    }
}


package main

import (
    "fmt"
    "log"
    "math"
    "os"
    "sort"
    "strconv"
    "strings"
)

type pvalues = []float64

type iv1 struct {
    index int
    value float64
}
type iv2 struct{ index, value int }

type direction int

const (
    up direction = iota
    down
)

// Test also for 'Unknown' correction type.
var ctypes = []string{
    "Benjamini-Hochberg", "Benjamini-Yekutieli", "Bonferroni", "Hochberg",
    "Holm", "Hommel", "Šidák", "Unknown",
}

func minimum(p pvalues) float64 {
    m := p[0]
    for i := 1; i < len(p); i++ {
        if p[i] < m {
            m = p[i]
        }
    }
    return m
}

func maximum(p pvalues) float64 {
    m := p[0]
    for i := 1; i < len(p); i++ {
        if p[i] > m {
            m = p[i]
        }
    }
    return m
}

func adjusted(p pvalues, ctype string) (string, error) {
    err := check(p)
    if err != nil {
        return "", err
    }
    temp := pformat(adjust(p, ctype), 5)
    return fmt.Sprintf("\n%s\n%s", ctype, temp), nil
}

func pformat(p pvalues, cols int) string {
    var lines []string
    for i := 0; i < len(p); i += cols {
        fchunk := p[i : i+cols]
        schunk := make([]string, cols)
        for j := 0; j < cols; j++ {
            schunk[j] = strconv.FormatFloat(fchunk[j], 'f', 10, 64)
        }
        lines = append(lines, fmt.Sprintf("[%2d]  %s", i, strings.Join(schunk, " ")))
    }
    return strings.Join(lines, "\n")
}

func check(p []float64) error {
    cond := len(p) > 0 && minimum(p) >= 0 && maximum(p) <= 1
    if !cond {
        return fmt.Errorf("p-values must be in range 0.0 to 1.0")
    }
    return nil
}

func ratchet(p pvalues, dir direction) {
    size := len(p)
    m := p[0]
    if dir == up {
        for i := 1; i < size; i++ {
            if p[i] > m {
                p[i] = m
            }
            m = p[i]
        }
    } else {
        for i := 1; i < size; i++ {
            if p[i] < m {
                p[i] = m
            }
            m = p[i]
        }
    }
    for i := 0; i < size; i++ {
        if p[i] > 1.0 {
            p[i] = 1.0
        }
    }
}

func schwartzian(p pvalues, mult pvalues, dir direction) pvalues {
    size := len(p)
    order := make([]int, size)
    iv1s := make([]iv1, size)
    for i := 0; i < size; i++ {
        iv1s[i] = iv1{i, p[i]}
    }
    if dir == up {
        sort.Slice(iv1s, func(i, j int) bool {
            return iv1s[i].value > iv1s[j].value
        })
    } else {
        sort.Slice(iv1s, func(i, j int) bool {
            return iv1s[i].value < iv1s[j].value
        })
    }
    for i := 0; i < size; i++ {
        order[i] = iv1s[i].index
    }
    pa := make(pvalues, size)
    for i := 0; i < size; i++ {
        pa[i] = mult[i] * p[order[i]]
    }
    ratchet(pa, dir)
    order2 := make([]int, size)
    iv2s := make([]iv2, size)
    for i := 0; i < size; i++ {
        iv2s[i] = iv2{i, order[i]}
    }
    sort.Slice(iv2s, func(i, j int) bool {
        return iv2s[i].value < iv2s[j].value
    })
    for i := 0; i < size; i++ {
        order2[i] = iv2s[i].index
    }
    pa2 := make(pvalues, size)
    for i := 0; i < size; i++ {
        pa2[i] = pa[order2[i]]
    }
    return pa2
}

func adjust(p pvalues, ctype string) pvalues {
    size := len(p)
    if size == 0 {
        return p
    }
    fsize := float64(size)
    switch ctype {
    case "Benjamini-Hochberg":
        mult := make(pvalues, size)
        for i := 0; i < size; i++ {
            mult[i] = fsize / float64(size-i)
        }
        return schwartzian(p, mult, up)
    case "Benjamini-Yekutieli":
        q := 0.0
        for i := 1; i <= size; i++ {
            q += 1.0 / float64(i)
        }
        mult := make(pvalues, size)
        for i := 0; i < size; i++ {
            mult[i] = q * fsize / (fsize - float64(i))
        }
        return schwartzian(p, mult, up)
    case "Bonferroni":
        p2 := make(pvalues, size)
        for i := 0; i < size; i++ {
            p2[i] = math.Min(p[i]*fsize, 1.0)
        }
        return p2
    case "Hochberg":
        mult := make(pvalues, size)
        for i := 0; i < size; i++ {
            mult[i] = float64(i) + 1
        }
        return schwartzian(p, mult, up)
    case "Holm":
        mult := make(pvalues, size)
        for i := 0; i < size; i++ {
            mult[i] = fsize - float64(i)
        }
        return schwartzian(p, mult, down)
    case "Hommel":
        order := make([]int, size)
        iv1s := make([]iv1, size)
        for i := 0; i < size; i++ {
            iv1s[i] = iv1{i, p[i]}
        }
        sort.Slice(iv1s, func(i, j int) bool {
            return iv1s[i].value < iv1s[j].value
        })
        for i := 0; i < size; i++ {
            order[i] = iv1s[i].index
        }
        s := make(pvalues, size)
        for i := 0; i < size; i++ {
            s[i] = p[order[i]]
        }
        m := make(pvalues, size)
        for i := 0; i < size; i++ {
            m[i] = s[i] * fsize / (float64(i) + 1)
        }
        min := minimum(m)
        q := make(pvalues, size)
        for i := 0; i < size; i++ {
            q[i] = min
        }
        pa := make(pvalues, size)
        for i := 0; i < size; i++ {
            pa[i] = min
        }
        for j := size - 1; j >= 2; j-- {
            lower := make([]int, size-j+1) // lower indices
            for i := 0; i < len(lower); i++ {
                lower[i] = i
            }
            upper := make([]int, j-1) // upper indices
            for i := 0; i < len(upper); i++ {
                upper[i] = size - j + 1 + i
            }
            qmin := float64(j) * s[upper[0]] / 2.0
            for i := 1; i < len(upper); i++ {
                temp := s[upper[i]] * float64(j) / (2.0 + float64(i))
                if temp < qmin {
                    qmin = temp
                }
            }
            for i := 0; i < len(lower); i++ {
                q[lower[i]] = math.Min(s[lower[i]]*float64(j), qmin)
            }
            for i := 0; i < len(upper); i++ {
                q[upper[i]] = q[size-j]
            }
            for i := 0; i < size; i++ {
                if pa[i] < q[i] {
                    pa[i] = q[i]
                }
            }
        }
        order2 := make([]int, size)
        iv2s := make([]iv2, size)
        for i := 0; i < size; i++ {
            iv2s[i] = iv2{i, order[i]}
        }
        sort.Slice(iv2s, func(i, j int) bool {
            return iv2s[i].value < iv2s[j].value
        })
        for i := 0; i < size; i++ {
            order2[i] = iv2s[i].index
        }
        pa2 := make(pvalues, size)
        for i := 0; i < size; i++ {
            pa2[i] = pa[order2[i]]
        }
        return pa2
    case "Šidák":
        p2 := make(pvalues, size)
        for i := 0; i < size; i++ {
            p2[i] = 1.0 - math.Pow(1.0-float64(p[i]), fsize)
        }
        return p2
    default:
        fmt.Printf("\nSorry, do not know how to do '%s' correction.\n", ctype)
        fmt.Println("Perhaps you want one of these?:")
        temp := make([]string, len(ctypes)-1)
        for i := 0; i < len(temp); i++ {
            temp[i] = fmt.Sprintf("  %s", ctypes[i])
        }
        fmt.Println(strings.Join(temp, "\n"))
        os.Exit(1)
    }
    return p
}

func main() {
    p := pvalues{
        4.533744e-01, 7.296024e-01, 9.936026e-02, 9.079658e-02, 1.801962e-01,
        8.752257e-01, 2.922222e-01, 9.115421e-01, 4.355806e-01, 5.324867e-01,
        4.926798e-01, 5.802978e-01, 3.485442e-01, 7.883130e-01, 2.729308e-01,
        8.502518e-01, 4.268138e-01, 6.442008e-01, 3.030266e-01, 5.001555e-02,
        3.194810e-01, 7.892933e-01, 9.991834e-01, 1.745691e-01, 9.037516e-01,
        1.198578e-01, 3.966083e-01, 1.403837e-02, 7.328671e-01, 6.793476e-02,
        4.040730e-03, 3.033349e-04, 1.125147e-02, 2.375072e-02, 5.818542e-04,
        3.075482e-04, 8.251272e-03, 1.356534e-03, 1.360696e-02, 3.764588e-04,
        1.801145e-05, 2.504456e-07, 3.310253e-02, 9.427839e-03, 8.791153e-04,
        2.177831e-04, 9.693054e-04, 6.610250e-05, 2.900813e-02, 5.735490e-03,
    }
    for _, ctype := range ctypes {
        s, err := adjusted(p, ctype)
        if err != nil {
            log.Fatal(err)
        }
        fmt.Println(s)
    }
}


import java.util.Arrays;
import java.util.Comparator;

public class PValueCorrection {
    private static int[] seqLen(int start, int end) {
        int[] result;
        if (start == end) {
            result = new int[end + 1];
            for (int i = 0; i < result.length; ++i) {
                result[i] = i + 1;
            }
        } else if (start < end) {
            result = new int[end - start + 1];
            for (int i = 0; i < result.length; ++i) {
                result[i] = start + i;
            }
        } else {
            result = new int[start - end + 1];
            for (int i = 0; i < result.length; ++i) {
                result[i] = start - i;
            }
        }
        return result;
    }

    private static int[] order(double[] array, boolean decreasing) {
        int size = array.length;
        int[] idx = new int[size];
        double[] baseArr = new double[size];
        for (int i = 0; i < size; ++i) {
            baseArr[i] = array[i];
            idx[i] = i;
        }

        Comparator<Integer> cmp;
        if (!decreasing) {
            cmp = Comparator.comparingDouble(a -> baseArr[a]);
        } else {
            cmp = (a, b) -> Double.compare(baseArr[b], baseArr[a]);
        }

        return Arrays.stream(idx)
            .boxed()
            .sorted(cmp)
            .mapToInt(a -> a)
            .toArray();
    }

    private static double[] cummin(double[] array) {
        if (array.length < 1) throw new IllegalArgumentException("cummin requires at least one element");
        double[] output = new double[array.length];
        double cumulativeMin = array[0];
        for (int i = 0; i < array.length; ++i) {
            if (array[i] < cumulativeMin) cumulativeMin = array[i];
            output[i] = cumulativeMin;
        }
        return output;
    }

    private static double[] cummax(double[] array) {
        if (array.length < 1) throw new IllegalArgumentException("cummax requires at least one element");
        double[] output = new double[array.length];
        double cumulativeMax = array[0];
        for (int i = 0; i < array.length; ++i) {
            if (array[i] > cumulativeMax) cumulativeMax = array[i];
            output[i] = cumulativeMax;
        }
        return output;
    }

    private static double[] pminx(double[] array, double x) {
        if (array.length < 1) throw new IllegalArgumentException("pmin requires at least one element");
        double[] result = new double[array.length];
        for (int i = 0; i < array.length; ++i) {
            if (array[i] < x) {
                result[i] = array[i];
            } else {
                result[i] = x;
            }
        }
        return result;
    }

    private static void doubleSay(double[] array) {
        System.out.printf("[ 1] %e", array[0]);
        for (int i = 1; i < array.length; ++i) {
            System.out.printf(" %.10f", array[i]);
            if ((i + 1) % 5 == 0) System.out.printf("\n[%2d]", i + 1);
        }
        System.out.println();
    }

    private static double[] intToDouble(int[] array) {
        double[] result = new double[array.length];
        for (int i = 0; i < array.length; i++) {
            result[i] = array[i];
        }
        return result;
    }

    private static double doubleArrayMin(double[] array) {
        if (array.length < 1) throw new IllegalArgumentException("pAdjust requires at least one element");
        return Arrays.stream(array).min().orElse(Double.NaN);
    }

    private static double[] pAdjust(double[] pvalues, String str) {
        int size = pvalues.length;
        if (size < 1) throw new IllegalArgumentException("pAdjust requires at least one element");
        int type;
        switch (str.toLowerCase()) {
            case "bh":
            case "fdr":
                type = 0;
                break;
            case "by":
                type = 1;
                break;
            case "bonferroni":
                type = 2;
                break;
            case "hochberg":
                type = 3;
                break;
            case "holm":
                type = 4;
                break;
            case "hommel":
                type = 5;
                break;
            default:
                throw new IllegalArgumentException(str + " doesn't match any accepted FDR types");
        }

        if (type == 2) {  // Bonferroni method
            double[] result = new double[size];
            for (int i = 0; i < size; ++i) {
                double b = pvalues[i] * size;
                if (b >= 1) {
                    result[i] = 1;
                } else if (0 <= b && b < 1) {
                    result[i] = b;
                } else {
                    throw new RuntimeException("" + b + " is outside [0, 1)");
                }
            }
            return result;
        } else if (type == 4) {  // Holm method
            int[] o = order(pvalues, false);
            double[] o2Double = intToDouble(o);
            double[] cummaxInput = new double[size];
            for (int i = 0; i < size; ++i) {
                cummaxInput[i] = (size - i) * pvalues[o[i]];
            }
            int[] ro = order(o2Double, false);
            double[] cummaxOutput = cummax(cummaxInput);
            double[] pmin = pminx(cummaxOutput, 1.0);
            double[] result = new double[size];
            for (int i = 0; i < size; ++i) {
                result[i] = pmin[ro[i]];
            }
            return result;
        } else if (type == 5) {
            int[] indices = seqLen(size, size);
            int[] o = order(pvalues, false);
            double[] p = new double[size];
            for (int i = 0; i < size; ++i) {
                p[i] = pvalues[o[i]];
            }
            double[] o2Double = intToDouble(o);
            int[] ro = order(o2Double, false);
            double[] q = new double[size];
            double[] pa = new double[size];
            double[] npi = new double[size];
            for (int i = 0; i < size; ++i) {
                npi[i] = p[i] * size / indices[i];
            }
            double min = doubleArrayMin(npi);
            Arrays.fill(q, min);
            Arrays.fill(pa, min);
            for (int j = size; j >= 2; --j) {
                int[] ij = seqLen(1, size - j + 1);
                for (int i = 0; i < size - j + 1; ++i) {
                    ij[i]--;
                }
                int i2Length = j - 1;
                int[] i2 = new int[i2Length];
                for (int i = 0; i < i2Length; ++i) {
                    i2[i] = size - j + 2 + i - 1;
                }
                double q1 = j * p[i2[0]] / 2.0;
                for (int i = 1; i < i2Length; ++i) {
                    double temp_q1 = p[i2[i]] * j / (2.0 + i);
                    if (temp_q1 < q1) q1 = temp_q1;
                }
                for (int i = 0; i < size - j + 1; ++i) {
                    q[ij[i]] = Math.min(p[ij[i]] * j, q1);
                }
                for (int i = 0; i < i2Length; ++i) {
                    q[i2[i]] = q[size - j];
                }
                for (int i = 0; i < size; ++i) {
                    if (pa[i] < q[i]) {
                        pa[i] = q[i];
                    }
                }
            }
            for (int i = 0; i < size; ++i) {
                q[i] = pa[ro[i]];
            }
            return q;
        }

        double[] ni = new double[size];
        int[] o = order(pvalues, true);
        double[] oDouble = intToDouble(o);
        for (int i = 0; i < size; ++i) {
            if (pvalues[i] < 0 || pvalues[i] > 1) {
                throw new RuntimeException("array[" + i + "] = " + pvalues[i] + " is outside [0, 1]");
            }
            ni[i] = (double) size / (size - i);
        }
        int[] ro = order(oDouble, false);
        double[] cumminInput = new double[size];
        if (type == 0) {  // BH method
            for (int i = 0; i < size; ++i) {
                cumminInput[i] = ni[i] * pvalues[o[i]];
            }
        } else if (type == 1) {  // BY method
            double q = 0;
            for (int i = 1; i < size + 1; ++i) {
                q += 1.0 / i;
            }
            for (int i = 0; i < size; ++i) {
                cumminInput[i] = q * ni[i] * pvalues[o[i]];
            }
        } else if (type == 3) {  // Hochberg method
            for (int i = 0; i < size; ++i) {
                cumminInput[i] = (i + 1) * pvalues[o[i]];
            }
        }
        double[] cumminArray = cummin(cumminInput);
        double[] pmin = pminx(cumminArray, 1.0);
        double[] result = new double[size];
        for (int i = 0; i < size; ++i) {
            result[i] = pmin[ro[i]];
        }
        return result;
    }

    public static void main(String[] args) {
        double[] pvalues = new double[]{
            4.533744e-01, 7.296024e-01, 9.936026e-02, 9.079658e-02, 1.801962e-01,
            8.752257e-01, 2.922222e-01, 9.115421e-01, 4.355806e-01, 5.324867e-01,
            4.926798e-01, 5.802978e-01, 3.485442e-01, 7.883130e-01, 2.729308e-01,
            8.502518e-01, 4.268138e-01, 6.442008e-01, 3.030266e-01, 5.001555e-02,
            3.194810e-01, 7.892933e-01, 9.991834e-01, 1.745691e-01, 9.037516e-01,
            1.198578e-01, 3.966083e-01, 1.403837e-02, 7.328671e-01, 6.793476e-02,
            4.040730e-03, 3.033349e-04, 1.125147e-02, 2.375072e-02, 5.818542e-04,
            3.075482e-04, 8.251272e-03, 1.356534e-03, 1.360696e-02, 3.764588e-04,
            1.801145e-05, 2.504456e-07, 3.310253e-02, 9.427839e-03, 8.791153e-04,
            2.177831e-04, 9.693054e-04, 6.610250e-05, 2.900813e-02, 5.735490e-03
        };

        double[][] correctAnswers = new double[][]{
            new double[]{  // Benjamini-Hochberg
                6.126681e-01, 8.521710e-01, 1.987205e-01, 1.891595e-01, 3.217789e-01,
                9.301450e-01, 4.870370e-01, 9.301450e-01, 6.049731e-01, 6.826753e-01,
                6.482629e-01, 7.253722e-01, 5.280973e-01, 8.769926e-01, 4.705703e-01,
                9.241867e-01, 6.049731e-01, 7.856107e-01, 4.887526e-01, 1.136717e-01,
                4.991891e-01, 8.769926e-01, 9.991834e-01, 3.217789e-01, 9.301450e-01,
                2.304958e-01, 5.832475e-01, 3.899547e-02, 8.521710e-01, 1.476843e-01,
                1.683638e-02, 2.562902e-03, 3.516084e-02, 6.250189e-02, 3.636589e-03,
                2.562902e-03, 2.946883e-02, 6.166064e-03, 3.899547e-02, 2.688991e-03,
                4.502862e-04, 1.252228e-05, 7.881555e-02, 3.142613e-02, 4.846527e-03,
                2.562902e-03, 4.846527e-03, 1.101708e-03, 7.252032e-02, 2.205958e-02
            },
            new double[]{  // Benjamini & Yekutieli
                1.000000e+00, 1.000000e+00, 8.940844e-01, 8.510676e-01, 1.000000e+00,
                1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
                1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
                1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 5.114323e-01,
                1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
                1.000000e+00, 1.000000e+00, 1.754486e-01, 1.000000e+00, 6.644618e-01,
                7.575031e-02, 1.153102e-02, 1.581959e-01, 2.812089e-01, 1.636176e-02,
                1.153102e-02, 1.325863e-01, 2.774239e-02, 1.754486e-01, 1.209832e-02,
                2.025930e-03, 5.634031e-05, 3.546073e-01, 1.413926e-01, 2.180552e-02,
                1.153102e-02, 2.180552e-02, 4.956812e-03, 3.262838e-01, 9.925057e-02
            },
            new double[]{  // Bonferroni
                1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
                1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
                1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
                1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
                1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
                1.000000e+00, 1.000000e+00, 7.019185e-01, 1.000000e+00, 1.000000e+00,
                2.020365e-01, 1.516674e-02, 5.625735e-01, 1.000000e+00, 2.909271e-02,
                1.537741e-02, 4.125636e-01, 6.782670e-02, 6.803480e-01, 1.882294e-02,
                9.005725e-04, 1.252228e-05, 1.000000e+00, 4.713920e-01, 4.395577e-02,
                1.088915e-02, 4.846527e-02, 3.305125e-03, 1.000000e+00, 2.867745e-01
            },
            new double[]{  // Hochberg
                9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01,
                9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01,
                9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01,
                9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01,
                9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01,
                9.991834e-01, 9.991834e-01, 4.632662e-01, 9.991834e-01, 9.991834e-01,
                1.575885e-01, 1.383967e-02, 3.938014e-01, 7.600230e-01, 2.501973e-02,
                1.383967e-02, 3.052971e-01, 5.426136e-02, 4.626366e-01, 1.656419e-02,
                8.825610e-04, 1.252228e-05, 9.930759e-01, 3.394022e-01, 3.692284e-02,
                1.023581e-02, 3.974152e-02, 3.172920e-03, 8.992520e-01, 2.179486e-01
            },
            new double[]{  // Holm
                1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
                1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
                1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
                1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
                1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
                1.000000e+00, 1.000000e+00, 4.632662e-01, 1.000000e+00, 1.000000e+00,
                1.575885e-01, 1.395341e-02, 3.938014e-01, 7.600230e-01, 2.501973e-02,
                1.395341e-02, 3.052971e-01, 5.426136e-02, 4.626366e-01, 1.656419e-02,
                8.825610e-04, 1.252228e-05, 9.930759e-01, 3.394022e-01, 3.692284e-02,
                1.023581e-02, 3.974152e-02, 3.172920e-03, 8.992520e-01, 2.179486e-01
            },
            new double[]{  // Hommel
                9.991834e-01, 9.991834e-01, 9.991834e-01, 9.987624e-01, 9.991834e-01,
                9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01,
                9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01,
                9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01, 9.595180e-01,
                9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01,
                9.991834e-01, 9.991834e-01, 4.351895e-01, 9.991834e-01, 9.766522e-01,
                1.414256e-01, 1.304340e-02, 3.530937e-01, 6.887709e-01, 2.385602e-02,
                1.322457e-02, 2.722920e-01, 5.426136e-02, 4.218158e-01, 1.581127e-02,
                8.825610e-04, 1.252228e-05, 8.743649e-01, 3.016908e-01, 3.516461e-02,
                9.582456e-03, 3.877222e-02, 3.172920e-03, 8.122276e-01, 1.950067e-01
            }
        };

        String[] types = new String[]{"bh", "by", "bonferroni", "hochberg", "holm", "hommel"};
        for (int type = 0; type < types.length; ++type) {
            double[] q = pAdjust(pvalues, types[type]);
            double error = 0.0;
            for (int i = 0; i < pvalues.length; ++i) {
                error += Math.abs(q[i] - correctAnswers[type][i]);
            }
            doubleSay(q);
            System.out.printf("\ntype %d = '%s' has a cumulative error of %g\n", type, types[type], error);
        }
    }
}


// version 1.1.51

import java.util.Arrays

typealias IAE = IllegalArgumentException

fun seqLen(start: Int, end: Int) =
    when {
        start == end -> IntArray(end + 1) { it + 1 }
        start < end  -> IntArray(end - start + 1) { start + it }
        else         -> IntArray(start - end + 1) { start - it }
    }

var baseArr: DoubleArray? = null

fun compareIncrease(a: Int, b: Int): Int = baseArr!![b].compareTo(baseArr!![a])

fun compareDecrease(a: Int, b: Int): Int = baseArr!![a].compareTo(baseArr!![b])

fun order(array: DoubleArray, decreasing: Boolean): IntArray {
    val size = array.size
    var idx = IntArray(size) { it }
    baseArr = array.copyOf()
    if (!decreasing) {
        idx = Arrays.stream(idx)
                    .boxed()
                    .sorted { a, b -> compareDecrease(a, b) }
                    .mapToInt { it }
                    .toArray()
    }
    else {
        idx = Arrays.stream(idx)
                    .boxed()
                    .sorted { a, b -> compareIncrease(a, b) }
                    .mapToInt { it }
                    .toArray()
    }
    baseArr = null
    return idx
}

fun cummin(array: DoubleArray): DoubleArray {
    val size = array.size
    if (size < 1) throw IAE("cummin requires at least one element")
    val output = DoubleArray(size)
    var cumulativeMin = array[0]
    for (i in 0 until size) {
        if (array[i] < cumulativeMin) cumulativeMin = array[i]
        output[i] = cumulativeMin
    }
    return output
}

fun cummax(array: DoubleArray): DoubleArray {
    val size = array.size
    if (size < 1) throw IAE("cummax requires at least one element")
    val output = DoubleArray(size)
    var cumulativeMax = array[0]
    for (i in 0 until size) {
        if (array[i] > cumulativeMax) cumulativeMax = array[i]
        output[i] = cumulativeMax
    }
    return output
}

fun pminx(array: DoubleArray, x: Double): DoubleArray {
    val size = array.size
    if (size < 1) throw IAE("pmin requires at least one element")
    return DoubleArray(size) { if (array[it] < x) array[it] else x }
}

fun doubleSay(array: DoubleArray) {
    print("[ 1] %e".format(array[0]))
    for (i in 1 until array.size) {
        print(" %.10f".format(array[i]))
        if ((i + 1) % 5 == 0) print("\n[%2d]".format(i + 1))
    }
    println()
}

fun intToDouble(array: IntArray) = DoubleArray(array.size) { array[it].toDouble() }

fun doubleArrayMin(array: DoubleArray) =
    if (array.size < 1) throw IAE("pAdjust requires at least one element")
    else array.min()!!

fun pAdjust(pvalues: DoubleArray, str: String): DoubleArray {
    val size = pvalues.size
    if (size < 1) throw IAE("pAdjust requires at least one element")
    val type = when(str.toLowerCase()) {
        "bh", "fdr"  -> 0
        "by"         -> 1
        "bonferroni" -> 2
        "hochberg"   -> 3
        "holm"       -> 4
        "hommel"     -> 5
        else         -> throw IAE("'$str' doesn't match any accepted FDR types")
    }
    if (type == 2) {  // Bonferroni method
        return DoubleArray(size) {
            val b = pvalues[it] * size
            when {
                b >= 1           -> 1.0
                0 <= b && b < 1  -> b
                else -> throw RuntimeException("$b is outside [0, 1)")
            }
        }
    }
    else if (type == 4) {  // Holm method
        val o = order(pvalues, false)
        val o2Double = intToDouble(o)
        val cummaxInput = DoubleArray(size) { (size - it) * pvalues[o[it]] }
        val ro = order(o2Double, false)
        val cummaxOutput = cummax(cummaxInput)
        val pmin = pminx(cummaxOutput, 1.0)
        return DoubleArray(size) { pmin[ro[it]] }
    }
    else if (type == 5) { // Hommel method
        val indices = seqLen(size, size)
        val o = order(pvalues, false)
        val p = DoubleArray(size) { pvalues[o[it]] }
        val o2Double = intToDouble(o)
        val ro = order(o2Double, false)
        val q = DoubleArray(size)
        val pa = DoubleArray(size)
        val npi = DoubleArray(size) { p[it] * size / indices[it] }
        val min = doubleArrayMin(npi)
        q.fill(min)
        pa.fill(min)
        for (j in size - 1 downTo 2) {
            val ij = seqLen(1, size - j + 1)
            for (i in 0 until size - j + 1) ij[i]--
            val i2Length = j - 1
            val i2 = IntArray(i2Length) { size - j + 2 + it - 1 }
            val pi2Length = i2Length
            var q1 = j * p[i2[0]] / 2.0
            for (i in 1 until pi2Length) {
                val temp_q1 = p[i2[i]] * j / (2.0 + i)
                if(temp_q1 < q1) q1 = temp_q1
            }
            for (i in 0 until size - j + 1) {
                q[ij[i]] = minOf(p[ij[i]] * j, q1)
            }
            for (i in 0 until i2Length) q[i2[i]] = q[size - j]
            for (i in 0 until size) if (pa[i] < q[i]) pa[i] = q[i]
        }
        for (index in 0 until size) q[index] = pa[ro[index]]
        return q
    }
    val ni = DoubleArray(size)
    val o = order(pvalues, true)
    val oDouble = intToDouble(o)
    for (index in 0 until size) {
        if (pvalues[index] !in 0.0 .. 1.0) {
            throw RuntimeException("array[$index] = ${pvalues[index]} is outside [0, 1]")
        }
        ni[index] = size.toDouble() / (size - index)
    }
    val ro = order(oDouble, false)
    val cumminInput = DoubleArray(size)
    if (type == 0) {  // BH method
        for (index in 0 until size) {
            cumminInput[index] = ni[index] * pvalues[o[index]]
        }
    }
    else if (type == 1) {  // BY method
        var q = 0.0
        for (index in 1 until size + 1) q += 1.0 / index
        for (index in 0 until size) {
            cumminInput[index] = q * ni[index] * pvalues[o[index]]
        }
    }
    else if (type == 3) {  // Hochberg method
        for (index in 0 until size) {
            cumminInput[index] = (index + 1) * pvalues[o[index]]
        }
    }
    val cumminArray = cummin(cumminInput)
    val pmin = pminx(cumminArray, 1.0)
    return DoubleArray(size) { pmin[ro[it]] }
}

fun main(args: Array<String>) {
    val pvalues = doubleArrayOf(
        4.533744e-01, 7.296024e-01, 9.936026e-02, 9.079658e-02, 1.801962e-01,
        8.752257e-01, 2.922222e-01, 9.115421e-01, 4.355806e-01, 5.324867e-01,
        4.926798e-01, 5.802978e-01, 3.485442e-01, 7.883130e-01, 2.729308e-01,
        8.502518e-01, 4.268138e-01, 6.442008e-01, 3.030266e-01, 5.001555e-02,
        3.194810e-01, 7.892933e-01, 9.991834e-01, 1.745691e-01, 9.037516e-01,
        1.198578e-01, 3.966083e-01, 1.403837e-02, 7.328671e-01, 6.793476e-02,
        4.040730e-03, 3.033349e-04, 1.125147e-02, 2.375072e-02, 5.818542e-04,
        3.075482e-04, 8.251272e-03, 1.356534e-03, 1.360696e-02, 3.764588e-04,
        1.801145e-05, 2.504456e-07, 3.310253e-02, 9.427839e-03, 8.791153e-04,
        2.177831e-04, 9.693054e-04, 6.610250e-05, 2.900813e-02, 5.735490e-03
    )

    val correctAnswers = listOf(
        doubleArrayOf(  // Benjamini-Hochberg
            6.126681e-01, 8.521710e-01, 1.987205e-01, 1.891595e-01, 3.217789e-01,
            9.301450e-01, 4.870370e-01, 9.301450e-01, 6.049731e-01, 6.826753e-01,
            6.482629e-01, 7.253722e-01, 5.280973e-01, 8.769926e-01, 4.705703e-01,
            9.241867e-01, 6.049731e-01, 7.856107e-01, 4.887526e-01, 1.136717e-01,
            4.991891e-01, 8.769926e-01, 9.991834e-01, 3.217789e-01, 9.301450e-01,
            2.304958e-01, 5.832475e-01, 3.899547e-02, 8.521710e-01, 1.476843e-01,
            1.683638e-02, 2.562902e-03, 3.516084e-02, 6.250189e-02, 3.636589e-03,
            2.562902e-03, 2.946883e-02, 6.166064e-03, 3.899547e-02, 2.688991e-03,
            4.502862e-04, 1.252228e-05, 7.881555e-02, 3.142613e-02, 4.846527e-03,
            2.562902e-03, 4.846527e-03, 1.101708e-03, 7.252032e-02, 2.205958e-02
        ),
        doubleArrayOf(  // Benjamini & Yekutieli
            1.000000e+00, 1.000000e+00, 8.940844e-01, 8.510676e-01, 1.000000e+00,
            1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
            1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
            1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 5.114323e-01,
            1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
            1.000000e+00, 1.000000e+00, 1.754486e-01, 1.000000e+00, 6.644618e-01,
            7.575031e-02, 1.153102e-02, 1.581959e-01, 2.812089e-01, 1.636176e-02,
            1.153102e-02, 1.325863e-01, 2.774239e-02, 1.754486e-01, 1.209832e-02,
            2.025930e-03, 5.634031e-05, 3.546073e-01, 1.413926e-01, 2.180552e-02,
            1.153102e-02, 2.180552e-02, 4.956812e-03, 3.262838e-01, 9.925057e-02
        ),
        doubleArrayOf(  // Bonferroni
            1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
            1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
            1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
            1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
            1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
            1.000000e+00, 1.000000e+00, 7.019185e-01, 1.000000e+00, 1.000000e+00,
            2.020365e-01, 1.516674e-02, 5.625735e-01, 1.000000e+00, 2.909271e-02,
            1.537741e-02, 4.125636e-01, 6.782670e-02, 6.803480e-01, 1.882294e-02,
            9.005725e-04, 1.252228e-05, 1.000000e+00, 4.713920e-01, 4.395577e-02,
            1.088915e-02, 4.846527e-02, 3.305125e-03, 1.000000e+00, 2.867745e-01
        ),
        doubleArrayOf(  // Hochberg
            9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01,
            9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01,
            9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01,
            9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01,
            9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01,
            9.991834e-01, 9.991834e-01, 4.632662e-01, 9.991834e-01, 9.991834e-01,
            1.575885e-01, 1.383967e-02, 3.938014e-01, 7.600230e-01, 2.501973e-02,
            1.383967e-02, 3.052971e-01, 5.426136e-02, 4.626366e-01, 1.656419e-02,
            8.825610e-04, 1.252228e-05, 9.930759e-01, 3.394022e-01, 3.692284e-02,
            1.023581e-02, 3.974152e-02, 3.172920e-03, 8.992520e-01, 2.179486e-01
        ),
        doubleArrayOf(  // Holm
            1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
            1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
            1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
            1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
            1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
            1.000000e+00, 1.000000e+00, 4.632662e-01, 1.000000e+00, 1.000000e+00,
            1.575885e-01, 1.395341e-02, 3.938014e-01, 7.600230e-01, 2.501973e-02,
            1.395341e-02, 3.052971e-01, 5.426136e-02, 4.626366e-01, 1.656419e-02,
            8.825610e-04, 1.252228e-05, 9.930759e-01, 3.394022e-01, 3.692284e-02,
            1.023581e-02, 3.974152e-02, 3.172920e-03, 8.992520e-01, 2.179486e-01
        ),
        doubleArrayOf(  // Hommel
            9.991834e-01, 9.991834e-01, 9.991834e-01, 9.987624e-01, 9.991834e-01,
            9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01,
            9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01,
            9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01, 9.595180e-01,
            9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01,
            9.991834e-01, 9.991834e-01, 4.351895e-01, 9.991834e-01, 9.766522e-01,
            1.414256e-01, 1.304340e-02, 3.530937e-01, 6.887709e-01, 2.385602e-02,
            1.322457e-02, 2.722920e-01, 5.426136e-02, 4.218158e-01, 1.581127e-02,
            8.825610e-04, 1.252228e-05, 8.743649e-01, 3.016908e-01, 3.516461e-02,
            9.582456e-03, 3.877222e-02, 3.172920e-03, 8.122276e-01, 1.950067e-01
        )
    )
    val types = listOf("bh", "by", "bonferroni", "hochberg", "holm", "hommel")
    val f = "\ntype %d = '%s' has cumulative error of %g"
    for (type in 0 until types.size) {
        val q = pAdjust(pvalues, types[type])
        var error = 0.0
        for (i in 0 until pvalues.size) {
            error += Math.abs(q[i] - correctAnswers[type][i])
        }
        doubleSay(q)
        println(f.format(type, types[type], error))
    }
}


import algorithm, math, sequtils, strformat, strutils, sugar

type

  CorrectionType {.pure.} = enum
    BenjaminiHochberg = "Benjamini-Hochberg"
    BenjaminiYekutieli = "Benjamini-Yekutieli"
    Bonferroni = "Bonferroni"
    Hochberg = "Hochberg"
    Holm = "Holm"
    Hommel = "Hommel"
    Šidák = "Šidák"

  Direction {.pure.} = enum Up, Down

  PValues = seq[float]

template newPValues(length: Natural): PValues =
  ## Create a PValues object of given length.
  newSeq[float](length)

func ratchet(p: var PValues; dir: Direction) =
  var m = p[0]
  case dir
  of Up:
    for i in 1..p.high:
      if p[i] > m: p[i] = m
      m = p[i]
  of Down:
    for i in 1..p.high:
      if p[i] < m: p[i] = m
      m = p[i]
  for i in 0..p.high:
    if p[i] > 1: p[i] = 1

func schwartzian(p, mult: PValues; dir: Direction): PValues =

  let length = p.len
  let sortOrder = if dir == Up: Descending else: Ascending
  let order1 = toSeq(p.pairs).sorted((x, y) => cmp(x.val, y.val), sortOrder).mapIt(it.key)

  var pa = newPValues(length)
  for i in 0..pa.high:
    pa[i] = mult[i] * p[order1[i]]

  ratchet(pa, dir)

  let order2 = toSeq(order1.pairs).sortedByIt(it.val).mapIt(it.key)
  for idx in order2:
    result.add pa[idx]

proc adjust(p: PValues; ctype: CorrectionType): PValues =
  let length = p.len
  assert length > 0
  let flength = length.toFloat

  case ctype

  of BenjaminiHochberg:
    var mult = newPValues(length)
    for i in 0..mult.high:
      mult[i] = flength / (flength - i.toFloat)
    return schwartzian(p, mult, Up)

  of BenjaminiYekutieli:
    var q = 0.0
    for i in 1..length: q += 1 / i
    var mult = newPValues(length)
    for i in 0..mult.high:
      mult[i] = (q * flength) / (flength - i.toFloat)
    return schwartzian(p, mult, Up)

  of Bonferroni:
    result = newPValues(length)
    for i in 0..result.high:
      result[i] = min(p[i] * flength, 1)
    return

  of Hochberg:
    var mult = newPValues(length)
    for i in 0..mult.high:
      mult[i] = i.toFloat + 1
    return schwartzian(p, mult, Up)

  of Holm:
    var mult = newPValues(length)
    for i in 0..mult.high:
      mult[i] = flength - i.toFloat
    return schwartzian(p, mult, Down)

  of Hommel:
    let order1 = toSeq(p.pairs).sortedByIt(it.val).mapIt(it.key)
    let s = order1.mapIt(p[it])
    var m = Inf
    for i in 0..s.high:
      m = min(m, s[i] * flength / (i + 1).toFloat)
    var q, pa = repeat(m, length)

    for j in countdown(length - 1, 2):
      let lower = toSeq(0..length - j)
      let upper = toSeq((length - j + 1)..<length)
      var qmin = j.toFloat * s[upper[0]] / 2
      for i in 1..upper.high:
        let val = s[upper[i]] * j.toFloat / (i + 2).toFloat
        if val < qmin: qmin = val
      for idx in lower: q[idx] = min(s[idx] * j.toFloat, qmin)
      for idx in upper: q[idx] = q[^j]
      for i, val in q:
        if pa[i] < val: pa[i] = val

    let order2 = toSeq(order1.pairs).sortedByIt(it.val).mapIt(it.key)
    return order2.mapIt(pa[it])

  of Šidák:
    result = newPValues(length)
    for i in 0..result.high:
      result[i] = 1 - (1 - p[i])^length
    return

func pformat(p: PValues; cols = 5): string =
  var lines: seq[string]
  for i in countup(0, p.high, cols):
    let fchunk = p[i..<(i + cols)]
    var schunk = newSeq[string](fchunk.len)
    for j in 0..<cols:
      schunk[j] = fchunk[j].formatFloat(ffDecimal, 10)
    lines.add &"[{i:2}]  {schunk.join(\" \")}"
  result = lines.join("\n")

func adjusted(p: PValues; ctype: CorrectionType): string =
  doAssert p.len > 0 and min(p) >= 0 and max(p) <= 1, "p-values must be in range 0.0 to 1.0."
  result = &"\n{ctype}\n{pformat(p.adjust(ctype))}"

when isMainModule:

  const PVals = @[
        4.533744e-01, 7.296024e-01, 9.936026e-02, 9.079658e-02, 1.801962e-01,
        8.752257e-01, 2.922222e-01, 9.115421e-01, 4.355806e-01, 5.324867e-01,
        4.926798e-01, 5.802978e-01, 3.485442e-01, 7.883130e-01, 2.729308e-01,
        8.502518e-01, 4.268138e-01, 6.442008e-01, 3.030266e-01, 5.001555e-02,
        3.194810e-01, 7.892933e-01, 9.991834e-01, 1.745691e-01, 9.037516e-01,
        1.198578e-01, 3.966083e-01, 1.403837e-02, 7.328671e-01, 6.793476e-02,
        4.040730e-03, 3.033349e-04, 1.125147e-02, 2.375072e-02, 5.818542e-04,
        3.075482e-04, 8.251272e-03, 1.356534e-03, 1.360696e-02, 3.764588e-04,
        1.801145e-05, 2.504456e-07, 3.310253e-02, 9.427839e-03, 8.791153e-04,
        2.177831e-04, 9.693054e-04, 6.610250e-05, 2.900813e-02, 5.735490e-03]

  for ctype in CorrectionType:
    echo adjusted(PVals, ctype)


#!/usr/bin/env perl

use strict;
use warnings FATAL => 'all';
use autodie ':all';
use List::Util 'min';
use feature 'say';

sub pmin {
	my $array = shift;
	my $x = 1;
	my @pmin_array;
	my $n = scalar @$array;
	for (my $index = 0; $index < $n; $index++) {
		$pmin_array[$index] = min(@$array[$index], $x);
	}
	@pmin_array
}

sub cummin {
	my $array_ref = shift;
	my @cummin;
	my $cumulative_min = @$array_ref[0];
	foreach my $p (@$array_ref) {
		if ($p < $cumulative_min) {
			$cumulative_min = $p;
		}
		push @cummin, $cumulative_min;
	}
	@cummin
}

sub cummax {
	my $array_ref = shift;
	my @cummax;
	my $cumulative_max = @$array_ref[0];
	foreach my $p (@$array_ref) {
		if ($p > $cumulative_max) {
			$cumulative_max = $p;
		}
		push @cummax, $cumulative_max;
	}
	@cummax
}

sub order {#made to match R's "order"
	my $array_ref = shift;
	my $decreasing = 'false';
	if (defined $_[0]) {
		my $option = shift;
		if ($option =~ m/true/i) {
			$decreasing = 'true';
		} elsif ($option =~ m/false/i) {
			#do nothing, it's already set to false
		} else {
			print "2nd option should only be case-insensitive 'true' or 'false'";
			die;
		}
	}
	my @array;
	my $max_index = scalar @$array_ref-1;
	if ($decreasing eq 'false') {
		@array = sort { @$array_ref[$a] <=> @$array_ref[$b] } 0..$max_index;
	} elsif ($decreasing eq 'true') {
		@array = sort { @$array_ref[$b] <=> @$array_ref[$a] } 0..$max_index;
	}
	@array
}

sub p_adjust {
	my $pvalues_ref = shift;
	my $method;
	if (defined $_[0]) {
		$method = shift
	} else {
		$method = 'Holm'
	}
	my %methods = (
						'bh'         => 1,
						'fdr'        => 1,
						'by'         => 1,
						'holm'       => 1,
						'hommel'     => 1,
						'bonferroni' => 1,
						'hochberg'   => 1
						);
	my $method_found = 'no';
	foreach my $key (keys %methods) {
		if ((uc $method) eq (uc $key)) {
			$method = $key;
			$method_found = 'yes';
			last
		}
	}
	if ($method_found eq 'no') {
		if ($method =~ m/benjamini-?\s*hochberg/i) {
			$method = 'bh';
			$method_found = 'yes';
		} elsif ($method =~ m/benjamini-?\s*yekutieli/i) {
			$method = 'by';
			$method_found = 'yes';
		}
	}
	if ($method_found eq 'no') {
		print "No method could be determined from $method.\n";
		die
	}
	my $lp = scalar @$pvalues_ref;
	my $n  = $lp;
	my @qvalues;
	if ($method eq 'hochberg') {
		my @o = order($pvalues_ref, 'TRUE');
		my @cummin_input;
		for (my $index = 0; $index < $n; $index++) {
			$cummin_input[$index] = ($index+1)* @$pvalues_ref[$o[$index]];#PVALUES[$o[$index]] is p[o]
		}
		my @cummin = cummin(\@cummin_input);
		my @pmin   = pmin(\@cummin);
		my @ro = order(\@o);
		@qvalues = @pmin[@ro];
	} elsif ($method eq 'bh') {
		my @o = order($pvalues_ref, 'TRUE');
		my @cummin_input;
		for (my $index = 0; $index < $n; $index++) {
			$cummin_input[$index] = ($n/($n-$index))* @$pvalues_ref[$o[$index]];#PVALUES[$o[$index]] is p[o]
		}
		my @ro = order(\@o);
		my @cummin = cummin(\@cummin_input);
		my @pmin   = pmin(\@cummin);
		@qvalues = @pmin[@ro];
	} elsif ($method eq 'by') {
		my $q = 0.0;
		my @o = order($pvalues_ref, 'TRUE');
		my @ro = order(\@o);
		for (my $index = 1; $index < ($n+1); $index++) {
			$q += 1.0 / $index;
		}
		my @cummin_input;
		for (my $index = 0; $index < $n; $index++) {
			$cummin_input[$index] = $q * ($n/($n-$index)) * @$pvalues_ref[$o[$index]];#PVALUES[$o[$index]] is p[o]
		}
#		say join (',', @cummin_input);
#		say '@cummin_input # of elements = ' . scalar @cummin_input;
		my @cummin = cummin(\@cummin_input);
		undef @cummin_input;
		my @pmin   = pmin(\@cummin);
		@qvalues = @pmin[@ro];
	} elsif ($method eq 'bonferroni') {
		for (my $index = 0; $index < $n; $index++) {
			my $q = @$pvalues_ref[$index]*$n;
			if ((0 <= $q) && ($q < 1)) {
				$qvalues[$index] = $q;
			} elsif ($q >= 1) {
				$qvalues[$index] = 1.0;
			} else {
				say 'Failed to get Bonferroni adjusted p.';
				die;
			}
		}
	} elsif ($method eq 'holm') {
		my @o = order($pvalues_ref);
		my @cummax_input;
		for (my $index = 0; $index < $n; $index++) {
			$cummax_input[$index] = ($n - $index) * @$pvalues_ref[$o[$index]];
		}
		my @ro = order(\@o);
		undef @o;
		my @cummax = cummax(\@cummax_input);
		undef @cummax_input;
		my @pmin = pmin(\@cummax);
		undef @cummax;
		@qvalues = @pmin[@ro];
	} elsif ($method eq 'hommel') {
		my @o = order($pvalues_ref);
		my @p = @$pvalues_ref[@o];
		my @ro = order(\@o);
		undef @o;
		my (@q, @pa);
		my $min = $n*$p[0];
		for (my $index = 0; $index < $n; $index++) {
			my $temp = $n*$p[$index] / ($index + 1);
			$min = min($min, $temp);
		}
		for (my $index = 0; $index < $n; $index++) {
			$pa[$index] = $min;#q <- pa <- rep.int(min(n * p/i), n)
			 $q[$index] = $min;#q <- pa <- rep.int(min(n * p/i), n)
		}
		for (my $j = ($n-1); $j >= 2; $j--) {
			my @ij = 0..($n - $j);#ij <- seq_len(n - j + 1)
			my $I2_LENGTH = $j - 1;
			my @i2;
			for (my $i = 0; $i < $I2_LENGTH; $i++) {
				$i2[$i] = $n-$j+2+$i-1;
#R's indices are 1-based, C's are 0-based, I added the -1
			}

			my $q1 = $j * $p[$i2[0]] / 2.0;
			for (my $i = 1; $i < $I2_LENGTH; $i++) {#loop through 2:j
				my $TEMP_Q1 = $j * $p[$i2[$i]] / (2 + $i);
				$q1 = min($TEMP_Q1, $q1);
			}
			for (my $i = 0; $i < ($n - $j + 1); $i++) {#q[ij] <- pmin(j * p[ij], q1)
				$q[$ij[$i]] = min( $j*$p[$ij[$i]], $q1);
			}

			for (my $i = 0; $i < $I2_LENGTH; $i++) {#q[i2] <- q[n - j + 1]
				$q[$i2[$i]] = $q[$n - $j];
			}

			for (my $i = 0; $i < $n; $i++) {#pa <- pmax(pa, q)
				if ($pa[$i] < $q[$i]) {
					$pa[$i] = $q[$i];
				}
			}
#			printf("j = %zu, pa = \n", j);
#				double_say(pa, N);
		}#end j loop
		@qvalues = @pa[@ro];
	} else {
		print "$method doesn't fit my types.\n";
		die
	}
	@qvalues
}
my @pvalues = (4.533744e-01, 7.296024e-01, 9.936026e-02, 9.079658e-02, 1.801962e-01,
8.752257e-01, 2.922222e-01, 9.115421e-01, 4.355806e-01, 5.324867e-01,
4.926798e-01, 5.802978e-01, 3.485442e-01, 7.883130e-01, 2.729308e-01,
8.502518e-01, 4.268138e-01, 6.442008e-01, 3.030266e-01, 5.001555e-02,
3.194810e-01, 7.892933e-01, 9.991834e-01, 1.745691e-01, 9.037516e-01,
1.198578e-01, 3.966083e-01, 1.403837e-02, 7.328671e-01, 6.793476e-02,
4.040730e-03, 3.033349e-04, 1.125147e-02, 2.375072e-02, 5.818542e-04,
3.075482e-04, 8.251272e-03, 1.356534e-03, 1.360696e-02, 3.764588e-04,
1.801145e-05, 2.504456e-07, 3.310253e-02, 9.427839e-03, 8.791153e-04,
2.177831e-04, 9.693054e-04, 6.610250e-05, 2.900813e-02, 5.735490e-03);

my %correct_answers = (
	'Benjamini-Hochberg' => [6.126681e-01, 8.521710e-01, 1.987205e-01, 1.891595e-01, 3.217789e-01,
9.301450e-01, 4.870370e-01, 9.301450e-01, 6.049731e-01, 6.826753e-01,
6.482629e-01, 7.253722e-01, 5.280973e-01, 8.769926e-01, 4.705703e-01,
9.241867e-01, 6.049731e-01, 7.856107e-01, 4.887526e-01, 1.136717e-01,
4.991891e-01, 8.769926e-01, 9.991834e-01, 3.217789e-01, 9.301450e-01,
2.304958e-01, 5.832475e-01, 3.899547e-02, 8.521710e-01, 1.476843e-01,
1.683638e-02, 2.562902e-03, 3.516084e-02, 6.250189e-02, 3.636589e-03,
2.562902e-03, 2.946883e-02, 6.166064e-03, 3.899547e-02, 2.688991e-03,
4.502862e-04, 1.252228e-05, 7.881555e-02, 3.142613e-02, 4.846527e-03,
2.562902e-03, 4.846527e-03, 1.101708e-03, 7.252032e-02, 2.205958e-02],
	'Benjamini-Yekutieli' => [1.000000e+00, 1.000000e+00, 8.940844e-01, 8.510676e-01, 1.000000e+00,
1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 5.114323e-01,
1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
1.000000e+00, 1.000000e+00, 1.754486e-01, 1.000000e+00, 6.644618e-01,
7.575031e-02, 1.153102e-02, 1.581959e-01, 2.812089e-01, 1.636176e-02,
1.153102e-02, 1.325863e-01, 2.774239e-02, 1.754486e-01, 1.209832e-02,
2.025930e-03, 5.634031e-05, 3.546073e-01, 1.413926e-01, 2.180552e-02,
1.153102e-02, 2.180552e-02, 4.956812e-03, 3.262838e-01, 9.925057e-02],
	'Bonferroni' => [1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
1.000000e+00, 1.000000e+00, 7.019185e-01, 1.000000e+00, 1.000000e+00,
2.020365e-01, 1.516674e-02, 5.625735e-01, 1.000000e+00, 2.909271e-02,
1.537741e-02, 4.125636e-01, 6.782670e-02, 6.803480e-01, 1.882294e-02,
9.005725e-04, 1.252228e-05, 1.000000e+00, 4.713920e-01, 4.395577e-02,
1.088915e-02, 4.846527e-02, 3.305125e-03, 1.000000e+00, 2.867745e-01],

	'Hochberg' => [9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01,
9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01,
9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01,
9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01,
9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01,
9.991834e-01, 9.991834e-01, 4.632662e-01, 9.991834e-01, 9.991834e-01,
1.575885e-01, 1.383967e-02, 3.938014e-01, 7.600230e-01, 2.501973e-02,
1.383967e-02, 3.052971e-01, 5.426136e-02, 4.626366e-01, 1.656419e-02,
8.825610e-04, 1.252228e-05, 9.930759e-01, 3.394022e-01, 3.692284e-02,
1.023581e-02, 3.974152e-02, 3.172920e-03, 8.992520e-01, 2.179486e-01],
	'Holm' => [1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
1.000000e+00, 1.000000e+00, 4.632662e-01, 1.000000e+00, 1.000000e+00,
1.575885e-01, 1.395341e-02, 3.938014e-01, 7.600230e-01, 2.501973e-02,
1.395341e-02, 3.052971e-01, 5.426136e-02, 4.626366e-01, 1.656419e-02,
8.825610e-04, 1.252228e-05, 9.930759e-01, 3.394022e-01, 3.692284e-02,
1.023581e-02, 3.974152e-02, 3.172920e-03, 8.992520e-01, 2.179486e-01],

	'Hommel' => [9.991834e-01, 9.991834e-01, 9.991834e-01, 9.987624e-01, 9.991834e-01,
9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01,
9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01,
9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01, 9.595180e-01,
9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01,
9.991834e-01, 9.991834e-01, 4.351895e-01, 9.991834e-01, 9.766522e-01,
1.414256e-01, 1.304340e-02, 3.530937e-01, 6.887709e-01, 2.385602e-02,
1.322457e-02, 2.722920e-01, 5.426136e-02, 4.218158e-01, 1.581127e-02,
8.825610e-04, 1.252228e-05, 8.743649e-01, 3.016908e-01, 3.516461e-02,
9.582456e-03, 3.877222e-02, 3.172920e-03, 8.122276e-01, 1.950067e-01]);

foreach my $method ('Hochberg','Benjamini-Hochberg','Benjamini-Yekutieli', 'Bonferroni', 'Holm', 'Hommel') {
	print "$method\n";
	my @qvalues = p_adjust(\@pvalues, $method);
	my $error = 0.0;
	foreach my $q (0..$#qvalues) {
		$error += abs($qvalues[$q] - $correct_answers{$method}[$q]);
	}
	printf("type $method has cumulative error of %g.\n", $error);
}


with javascript_semantics
enum UP, DOWN

function ratchet(sequence p, integer direction)
    atom m = p[1]
    for i=1 to length(p) do
        if iff(direction=UP?p[i]>m:p[i]<m) then p[i] = m end if
        m = p[i]
    end for
    return sq_min(p,1)
end function

function schwartzian(sequence p, mult, integer direction)
    sequence order = custom_sort(p,tagset(length(p)))
    if direction=UP then order = reverse(order) end if
    sequence pa = ratchet(sq_mul(mult,extract(p,order)), direction)
    return extract(pa,order,invert:=true)
end function

function adjust(sequence p, string method)
    integer size = length(p)
    sequence mult = tagset(size)
    switch method

        case "Benjamini-Hochberg":
            mult = sq_div(size,sq_sub(size+1,mult))
            return schwartzian(p, mult, UP)

        case "Benjamini-Yekutieli":
            atom q = sum(sq_div(1,mult))
            mult = sq_div(q*size,sq_sub(size+1,mult))
            return schwartzian(p, mult, UP)

        case "Bonferroni":
            return sq_min(sq_mul(p,size),1)

        case "Hochberg":
            return schwartzian(p, mult, UP)

        case "Holm":
            mult = sq_sub(size+1,mult)
            return schwartzian(p, mult, DOWN)

        case "Hommel":
            sequence ivdx = repeat(0,size)
            for i=1 to size do ivdx[i] = {p[i],i} end for
            ivdx = sort(ivdx)
            sequence s = vslice(ivdx,1),
                     m = sq_div(sq_mul(s,size),mult),
                     qh = repeat(min(m),size),
                     pa = repeat(min(m),size),
                     order = vslice(ivdx,2)
            for j=size-1 to 2 by -1 do
                sequence lwr = tagset(size-j+1),
                         upr = sq_add(size-j+1,tagset(j-1))
                atom qmin = j*s[upr[1]]/2
                for i=2 to length(upr) do
                    qmin = min(s[upr[i]]*j/(i+1),qmin)
                end for
                for i=1 to length(lwr) do
                    qh[lwr[i]] = min(s[lwr[i]]*j, qmin)
                end for
                for i=1 to length(upr) do
                    qh[upr[i]] = qh[size-j+1]
                end for
                pa = sq_max(pa,qh)
            end for
            return extract(pa,order,invert:=true)

        case "Sidak":
            p = deep_copy(p)
            for i=1 to length(p) do
                p[i] = 1 - power(1-p[i],size)
            end for
            return p

        else
            return {}   -- (unknown method)

    end switch
    return p
end function

constant {types,correct_answers} = columnize({
    {"Benjamini-Hochberg",
     {6.126681e-01, 8.521710e-01, 1.987205e-01, 1.891595e-01, 3.217789e-01,
      9.301450e-01, 4.870370e-01, 9.301450e-01, 6.049731e-01, 6.826753e-01,
      6.482629e-01, 7.253722e-01, 5.280973e-01, 8.769926e-01, 4.705703e-01,
      9.241867e-01, 6.049731e-01, 7.856107e-01, 4.887526e-01, 1.136717e-01,
      4.991891e-01, 8.769926e-01, 9.991834e-01, 3.217789e-01, 9.301450e-01,
      2.304958e-01, 5.832475e-01, 3.899547e-02, 8.521710e-01, 1.476843e-01,
      1.683638e-02, 2.562902e-03, 3.516084e-02, 6.250189e-02, 3.636589e-03,
      2.562902e-03, 2.946883e-02, 6.166064e-03, 3.899547e-02, 2.688991e-03,
      4.502862e-04, 1.252228e-05, 7.881555e-02, 3.142613e-02, 4.846527e-03,
      2.562902e-03, 4.846527e-03, 1.101708e-03, 7.252032e-02, 2.205958e-02}},
    {"Benjamini-Yekutieli",
     {1.000000e+00, 1.000000e+00, 8.940844e-01, 8.510676e-01, 1.000000e+00,
      1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
      1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
      1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 5.114323e-01,
      1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
      1.000000e+00, 1.000000e+00, 1.754486e-01, 1.000000e+00, 6.644618e-01,
      7.575031e-02, 1.153102e-02, 1.581959e-01, 2.812089e-01, 1.636176e-02,
      1.153102e-02, 1.325863e-01, 2.774239e-02, 1.754486e-01, 1.209832e-02,
      2.025930e-03, 5.634031e-05, 3.546073e-01, 1.413926e-01, 2.180552e-02,
      1.153102e-02, 2.180552e-02, 4.956812e-03, 3.262838e-01, 9.925057e-02}},
    {"Bonferroni",
     {1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
      1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
      1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
      1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
      1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
      1.000000e+00, 1.000000e+00, 7.019185e-01, 1.000000e+00, 1.000000e+00,
      2.020365e-01, 1.516674e-02, 5.625735e-01, 1.000000e+00, 2.909271e-02,
      1.537741e-02, 4.125636e-01, 6.782670e-02, 6.803480e-01, 1.882294e-02,
      9.005725e-04, 1.252228e-05, 1.000000e+00, 4.713920e-01, 4.395577e-02,
      1.088915e-02, 4.846527e-02, 3.305125e-03, 1.000000e+00, 2.867745e-01}},
    {"Hochberg",
     {9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01,
      9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01,
      9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01,
      9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01,
      9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01,
      9.991834e-01, 9.991834e-01, 4.632662e-01, 9.991834e-01, 9.991834e-01,
      1.575885e-01, 1.383967e-02, 3.938014e-01, 7.600230e-01, 2.501973e-02,
      1.383967e-02, 3.052971e-01, 5.426136e-02, 4.626366e-01, 1.656419e-02,
      8.825610e-04, 1.252228e-05, 9.930759e-01, 3.394022e-01, 3.692284e-02,
      1.023581e-02, 3.974152e-02, 3.172920e-03, 8.992520e-01, 2.179486e-01}},
    {"Holm",
     {1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
      1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
      1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
      1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
      1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
      1.000000e+00, 1.000000e+00, 4.632662e-01, 1.000000e+00, 1.000000e+00,
      1.575885e-01, 1.395341e-02, 3.938014e-01, 7.600230e-01, 2.501973e-02,
      1.395341e-02, 3.052971e-01, 5.426136e-02, 4.626366e-01, 1.656419e-02,
      8.825610e-04, 1.252228e-05, 9.930759e-01, 3.394022e-01, 3.692284e-02,
      1.023581e-02, 3.974152e-02, 3.172920e-03, 8.992520e-01, 2.179486e-01}},
    {"Hommel",
     {9.991834e-01, 9.991834e-01, 9.991834e-01, 9.987624e-01, 9.991834e-01,
      9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01,
      9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01,
      9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01, 9.595180e-01,
      9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01,
      9.991834e-01, 9.991834e-01, 4.351895e-01, 9.991834e-01, 9.766522e-01,
      1.414256e-01, 1.304340e-02, 3.530937e-01, 6.887709e-01, 2.385602e-02,
      1.322457e-02, 2.722920e-01, 5.426136e-02, 4.218158e-01, 1.581127e-02,
      8.825610e-04, 1.252228e-05, 8.743649e-01, 3.016908e-01, 3.516461e-02,
      9.582456e-03, 3.877222e-02, 3.172920e-03, 8.122276e-01, 1.950067e-01}},
    {"Sidak",
     {1.0000000000, 1.0000000000, 0.9946598274, 0.9914285749, 0.9999515274,
      1.0000000000, 0.9999999688, 1.0000000000, 1.0000000000, 1.0000000000,
      1.0000000000, 1.0000000000, 0.9999999995, 1.0000000000, 0.9999998801,
      1.0000000000, 1.0000000000, 1.0000000000, 0.9999999855, 0.9231179729,
      0.9999999956, 1.0000000000, 1.0000000000, 0.9999317605, 1.0000000000,
      0.9983109511, 1.0000000000, 0.5068253940, 1.0000000000, 0.9703301333,
      0.1832692440, 0.0150545753, 0.4320729669, 0.6993672225, 0.0286818157,
      0.0152621104, 0.3391808707, 0.0656206307, 0.4959194266, 0.0186503726,
      0.0009001752, 0.0000125222, 0.8142104886, 0.3772612062, 0.0430222116,
      0.0108312558, 0.0473319661, 0.0032997780, 0.7705015898, 0.2499384839}}})
--  {"Unknown",{1}}})

constant pValues = {4.533744e-01, 7.296024e-01, 9.936026e-02, 9.079658e-02, 1.801962e-01,
                    8.752257e-01, 2.922222e-01, 9.115421e-01, 4.355806e-01, 5.324867e-01,
                    4.926798e-01, 5.802978e-01, 3.485442e-01, 7.883130e-01, 2.729308e-01,
                    8.502518e-01, 4.268138e-01, 6.442008e-01, 3.030266e-01, 5.001555e-02,
                    3.194810e-01, 7.892933e-01, 9.991834e-01, 1.745691e-01, 9.037516e-01,
                    1.198578e-01, 3.966083e-01, 1.403837e-02, 7.328671e-01, 6.793476e-02,
                    4.040730e-03, 3.033349e-04, 1.125147e-02, 2.375072e-02, 5.818542e-04,
                    3.075482e-04, 8.251272e-03, 1.356534e-03, 1.360696e-02, 3.764588e-04,
                    1.801145e-05, 2.504456e-07, 3.310253e-02, 9.427839e-03, 8.791153e-04,
                    2.177831e-04, 9.693054e-04, 6.610250e-05, 2.900813e-02, 5.735490e-03}

if length(pValues)=0 or min(pValues)<0 or max(pValues)>1 then
    crash("p-values must be in range 0.0 to 1.0")
end if

for i=1 to length(types) do
    string ti = types[i]
    sequence res = adjust(pValues,ti)
    if res={} then
        printf(1,"\nSorry, do not know how to do %s correction.\n"&
                 "Perhaps you want one of these?:\n  %s\n",
                 {ti,join(types[1..$-1],"\n  ")})
        exit
    end if
--  printf(1,"%s\n",{ti})
--  res = correct_answers[i] -- (for easier comparison only)
--  pp(res,{pp_FltFmt,"%13.10f",pp_IntFmt,"%13.10f",pp_Maxlen,75,pp_Pause,0})
    atom error = sum(sq_abs(sq_sub(res,correct_answers[i])))
    printf(1,"%s has cumulative error of %g\n", {ti,error})
end for


from __future__ import division
import sys

def pminf(array):
    x = 1
    pmin_list = []
    N = len(array)
    for index in range(N):
        if array[index] < x:
            pmin_list.insert(index, array[index])
        else:
            pmin_list.insert(index, x)
    return pmin_list
#end function

def cumminf(array):
    cummin = []
    cumulative_min = array[0]
    for p in array:
        if p < cumulative_min:
            cumulative_min = p
        cummin.append(cumulative_min)
    return cummin
#end

def cummaxf(array):
    cummax = []
    cumulative_max = array[0]
    for e in array:
        if e > cumulative_max:
            cumulative_max = e
        cummax.append(cumulative_max)
    return cummax
#end

def order(*args):
    if len(args) > 1:
        if args[1].lower() == 'false':# if ($string1 eq $string2) {
            return sorted(range(len(args[0])), key = lambda k: args[0][k])
        elif list(args[1].lower()) == list('true'):
            return sorted(range(len(args[0])), key = lambda k: args[0][k], reverse = True)
        else:
            print "%s isn't a recognized parameter" % args[1]
            sys.exit()
    elif len(args) == 1:
        return sorted(range(len(args[0])), key = lambda k: args[0][k])
#end

def p_adjust(*args):
    method = "bh"
    pvalues = args[0]
    if len(args) > 1:
        methods = {"bh", "fdr", "by", "holm", "hommel", "bonferroni", "hochberg"}
        metharg = arg[1].lower()
        if metharg in methods:
            method = metharg
    lp = len(pvalues)
    n = lp
    qvalues = []

    if method == 'hochberg':#already all lower case
        o = order(pvalues, 'TRUE')
        cummin_input = []
        for index in range(n):
            cummin_input.insert(index, (index+1)*pvalues[o[index]])
        cummin = cumminf(cummin_input)
        pmin = pminf(cummin)
        ro = order(o)
        qvalues = [pmin[i] for i in ro]
    elif method == 'bh':
        o = order(pvalues, 'TRUE')
        cummin_input = []
        for index in range(n):
            cummin_input.insert(index, (n/(n-index))* pvalues[o[index]])
        ro = order(o)
        cummin = cumminf(cummin_input)
        pmin = pminf(cummin)
        qvalues = [pmin[i] for i in ro]
    elif method == 'by':
        q = 0.0
        o = order(pvalues, 'TRUE')
        ro = order(o)
        for index in range(1, n+1):
            q += 1.0 / index;
        cummin_input = []
        for index in range(n):
            cummin_input.insert(index, q * (n/(n-index)) * pvalues[o[index]])
        cummin = cumminf(cummin_input)
        pmin = pminf(cummin)
        qvalues = [pmin[i] for i in ro]
    elif method == 'bonferroni':
        for index in range(n):
            q = pvalues[index] * n
            if (0 <= q) and (q < 1):
                qvalues.insert(index, q)
            elif q >= 1:
                qvalues.insert(index, 1)
            else:
                print '%g won\'t give a Bonferroni adjusted p' % q
                sys.exit()
    elif method == 'holm':
        o = order(pvalues)
        cummax_input = []
        for index in range(n):
            cummax_input.insert(index, (n - index) * pvalues[o[index]])
        ro = order(o)
        cummax = cummaxf(cummax_input)
        pmin = pminf(cummax)
        qvalues = [pmin[i] for i in ro]
    elif method == 'hommel':
        i = range(1,n+1)
        o = order(pvalues)
        p = [pvalues[index] for index in o]
        ro = order(o)
        pa = []
        q = []
        smin = n*p[0]
        for index in range(n):
            temp = n*p[index] / (index + 1)
            if temp < smin:
                smin = temp
        for index in range(n):
            pa.insert(index, smin)
            q.insert(index, smin)
        for j in range(n-1,1,-1):
            ij = range(1,n-j+2)
            for x in range(len(ij)):
                ij[x] -= 1
            I2_LENGTH = j - 1
            i2 = []
            for index in range(I2_LENGTH+1):
                i2.insert(index, n - j + 2 + index - 1)
            q1 = j * p[i2[0]] / 2.0
            for index in range(1,I2_LENGTH):
                TEMP_Q1 = j * p[i2[index]] / (2.0 + index)
                if TEMP_Q1 < q1:
                    q1 = TEMP_Q1
            for index in range(n - j + 1):
                q[ij[index]] = min(j * p[ij[index]], q1)
            for index in range(I2_LENGTH):
                q[i2[index]] = q[n-j]
            for index in range(n):
                if pa[index] < q[index]:
                    pa[index] = q[index]
            qvalues = [pa[index] for index in ro]
    else:
        print "method %s isn't defined." % method
        sys.exit()
    return qvalues

pvalues = [4.533744e-01, 7.296024e-01, 9.936026e-02, 9.079658e-02, 1.801962e-01,
8.752257e-01, 2.922222e-01, 9.115421e-01, 4.355806e-01, 5.324867e-01,
4.926798e-01, 5.802978e-01, 3.485442e-01, 7.883130e-01, 2.729308e-01,
8.502518e-01, 4.268138e-01, 6.442008e-01, 3.030266e-01, 5.001555e-02,
3.194810e-01, 7.892933e-01, 9.991834e-01, 1.745691e-01, 9.037516e-01,
1.198578e-01, 3.966083e-01, 1.403837e-02, 7.328671e-01, 6.793476e-02,
4.040730e-03, 3.033349e-04, 1.125147e-02, 2.375072e-02, 5.818542e-04,
3.075482e-04, 8.251272e-03, 1.356534e-03, 1.360696e-02, 3.764588e-04,
1.801145e-05, 2.504456e-07, 3.310253e-02, 9.427839e-03, 8.791153e-04,
2.177831e-04, 9.693054e-04, 6.610250e-05, 2.900813e-02, 5.735490e-03]

correct_answers = {}

correct_answers['bh'] = [6.126681e-01, 8.521710e-01, 1.987205e-01, 1.891595e-01, 3.217789e-01,
9.301450e-01, 4.870370e-01, 9.301450e-01, 6.049731e-01, 6.826753e-01,
6.482629e-01, 7.253722e-01, 5.280973e-01, 8.769926e-01, 4.705703e-01,
9.241867e-01, 6.049731e-01, 7.856107e-01, 4.887526e-01, 1.136717e-01,
4.991891e-01, 8.769926e-01, 9.991834e-01, 3.217789e-01, 9.301450e-01,
2.304958e-01, 5.832475e-01, 3.899547e-02, 8.521710e-01, 1.476843e-01,
1.683638e-02, 2.562902e-03, 3.516084e-02, 6.250189e-02, 3.636589e-03,
2.562902e-03, 2.946883e-02, 6.166064e-03, 3.899547e-02, 2.688991e-03,
4.502862e-04, 1.252228e-05, 7.881555e-02, 3.142613e-02, 4.846527e-03,
2.562902e-03, 4.846527e-03, 1.101708e-03, 7.252032e-02, 2.205958e-02]

correct_answers['by'] = [1.000000e+00, 1.000000e+00, 8.940844e-01, 8.510676e-01, 1.000000e+00,
1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 5.114323e-01,
1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
1.000000e+00, 1.000000e+00, 1.754486e-01, 1.000000e+00, 6.644618e-01,
7.575031e-02, 1.153102e-02, 1.581959e-01, 2.812089e-01, 1.636176e-02,
1.153102e-02, 1.325863e-01, 2.774239e-02, 1.754486e-01, 1.209832e-02,
2.025930e-03, 5.634031e-05, 3.546073e-01, 1.413926e-01, 2.180552e-02,
1.153102e-02, 2.180552e-02, 4.956812e-03, 3.262838e-01, 9.925057e-02]

correct_answers['bonferroni'] = [1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
1.000000e+00, 1.000000e+00, 7.019185e-01, 1.000000e+00, 1.000000e+00,
2.020365e-01, 1.516674e-02, 5.625735e-01, 1.000000e+00, 2.909271e-02,
1.537741e-02, 4.125636e-01, 6.782670e-02, 6.803480e-01, 1.882294e-02,
9.005725e-04, 1.252228e-05, 1.000000e+00, 4.713920e-01, 4.395577e-02,
1.088915e-02, 4.846527e-02, 3.305125e-03, 1.000000e+00, 2.867745e-01]

correct_answers['hochberg'] = [9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01,
9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01,
9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01,
9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01,
9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01,
9.991834e-01, 9.991834e-01, 4.632662e-01, 9.991834e-01, 9.991834e-01,
1.575885e-01, 1.383967e-02, 3.938014e-01, 7.600230e-01, 2.501973e-02,
1.383967e-02, 3.052971e-01, 5.426136e-02, 4.626366e-01, 1.656419e-02,
8.825610e-04, 1.252228e-05, 9.930759e-01, 3.394022e-01, 3.692284e-02,
1.023581e-02, 3.974152e-02, 3.172920e-03, 8.992520e-01, 2.179486e-01]

correct_answers['holm'] = [1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00,
1.000000e+00, 1.000000e+00, 4.632662e-01, 1.000000e+00, 1.000000e+00,
1.575885e-01, 1.395341e-02, 3.938014e-01, 7.600230e-01, 2.501973e-02,
1.395341e-02, 3.052971e-01, 5.426136e-02, 4.626366e-01, 1.656419e-02,
8.825610e-04, 1.252228e-05, 9.930759e-01, 3.394022e-01, 3.692284e-02,
1.023581e-02, 3.974152e-02, 3.172920e-03, 8.992520e-01, 2.179486e-01]

correct_answers['hommel'] = [9.991834e-01, 9.991834e-01, 9.991834e-01, 9.987624e-01, 9.991834e-01,
9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01,
9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01,
9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01, 9.595180e-01,
9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01, 9.991834e-01,
9.991834e-01, 9.991834e-01, 4.351895e-01, 9.991834e-01, 9.766522e-01,
1.414256e-01, 1.304340e-02, 3.530937e-01, 6.887709e-01, 2.385602e-02,
1.322457e-02, 2.722920e-01, 5.426136e-02, 4.218158e-01, 1.581127e-02,
8.825610e-04, 1.252228e-05, 8.743649e-01, 3.016908e-01, 3.516461e-02,
9.582456e-03, 3.877222e-02, 3.172920e-03, 8.122276e-01, 1.950067e-01]

for key in correct_answers.keys():
    error = 0.0
    q = p_adjust(pvalues, key)
    for i in range(len(q)):
        error += abs(q[i] - correct_answers[key][i])
    print '%s error = %g' % (key.upper(), error)