// Yoinked from https://easings.net/
const MamiEasings = (() => {
    const elasticConst = 2 * Math.PI / .3;
    const elasticConst2 = .3 / 4;

    const backConst = 1.70158;
    const backConst2 = backConst * 1.525;

    const bounceConst = 1 / 2.75;

    const expoOffset = Math.pow(2, -10);
    const elasticOffsetFull = Math.pow(2, -11);
    const elasticOffsetHalf = Math.pow(2, -10) * Math.sin((.5 - elasticConst2) * elasticConst);
    const elasticOffsetQuarter = Math.pow(2, -10) * Math.sin((.25 - elasticConst2) * elasticConst);
    const inOutElasticOffset = Math.pow(2, -10) * Math.sin((1 - elasticConst2 * 1.5) * elasticConst / 1.5);

    let outBounce;

    return {
        linear: t => t,

        inQuad: t => t * t,
        outQuad: t => t * (2 - t),
        inOutQuad: t => {
            if(t < .5)
                return t * t * 2;
            return --t * t * -2 + 1;
        },

        inCubic: t => t * t * t,
        outCubic: t => --t * t * t + 1,
        inOutCubic: t => {
            if(t < .5)
                return t * t * t * 4;
            return --t * t * t * 4 + 1;
        },

        inQuart: t => t * t * t * t,
        outQuart: t => 1 - --t * t * t * t,
        inOutQuart: t => {
            if(t < .5)
                return t * t * t * t * 8;
            return --t * t * t * t * -8 + 1;
        },

        inQuint: t => t * t * t * t * t,
        outQuint: t => --t * t * t * t * t + 1,
        inOutQuint: t => {
            if(t < .5)
                return t * t * t * t * t * 16;
            return --t * t * t * t * t * 16 + 1;
        },

        inSine: t => 1 - Math.cos(t * Math.PI * .5),
        outSine: t => Math.sin(t * Math.PI * .5),
        inOutSine: t => .5 - .5 * Math.cos(Math.PI * t),

        inCirc: t => 1 - Math.sqrt(1 - t * t),
        outCirc: t => Math.sqrt(1 - --t * t),
        inOutCirc: t => {
            if((t *= 2) < 1)
                return .5 - .5 * Math.sqrt(1 - t * t);
            return .5 * Math.sqrt(1 - (t -= 2) * t) + .5;
        },

        inElastic: t => -Math.pow(2, -10 + 10 * t) * Math.sin((1 - elasticConst2 - t) * elasticConst) + elasticOffsetFull * (1 - t),
        outElastic: t => Math.pow(2, -10 * t) * Math.sin((t - elasticConst2) * elasticConst) + 1 - elasticOffsetFull * t,
        outElasticHalf: t => Math.pow(2, -10 * t) * Math.sin((.5 * t - elasticConst2) * elasticConst) + 1 - elasticOffsetHalf * t,
        outElasticQuarter: t => Math.pow(2, -10 * t) * Math.sin((.25 * t - elasticConst2) * elasticConst) + 1 - elasticOffsetQuarter * t,
        inOutElastic: t => {
            if((t *= 2) < 1)
                return -.5 * (Math.pow(2, -10 + 10 * t) * Math.sin((1 - elasticConst2 * 1.5 - t) * elasticConst / 1.5) - inOutElasticOffset * (1 - t));
            return .5 * (Math.pow(2, -10 * --t) * Math.sin((t - elasticConst2 * 1.5) * elasticConst / 1.5) - inOutElasticOffset * t) + 1;
        },

        inExpo: t => Math.pow(2, 10 * (t - 1)) + expoOffset * (t - 1),
        outExpo: t => -Math.pow(2, -10 * t) + 1 + expoOffset * t,
        inOutExpo: t => {
            if(t < .5)
                return .5 * (Math.pow(2, 20 * t - 10)) + expoOffset * (2 * t - 1);
            return 1 - .5 * (Math.pow(2, -20 * t + 10)) + expoOffset * (-2 * t + 1);
        },

        inBack: t => t * t * ((backConst + 1) * t - backConst),
        outBack: t => --t * t * ((backConst + 1) * t + backConst) + 1,
        inOutBack: t => {
            if((t *= 2) < 1)
                return .5 * t * t * ((backConst2 + 1) * t - backConst2);
            return .5 * ((t -= 2) * t * ((backConst2 + 1) * t + backConst2) + 2);
        },

        inBounce: t => {
            t = 1 - t;
            if(t < bounceConst)
                return 1 - 7.5625 * t * t;
            if(t < 2 * bounceConst)
                return 1 - (7.5625 * (t -= 1.5 * bounceConst) * t + .75);
            if(t < 2.5 * bounceConst)
                return 1 - (7.5625 * (t -= 2.25 * bounceConst) * t + .9375);
            return 1 - (7.5625 * (t -= 2.625 * bounceConst) * t + .984375);
        },
        outBounce: (() => {
            return outBounce = t => {
                if(t < bounceConst)
                    return 7.5625 * t * t;
                if(t < 2 * bounceConst)
                    return 7.5625 * (t -= 1.5 * bounceConst) * t + .75;
                if(t < 2.5 * bounceConst)
                    return 7.5625 * (t -= 2.25 * bounceConst) * t + .9375;
                return 7.5625 * (t -= 2.625 * bounceConst) * t + .984375;
            };
        })(),
        inOutBounce: t => {
            if(t < .5)
                return .5 - .5 * outBounce(1 - t * 2);
            return outBounce((t - .5) * 2) * .5 + .5;
        },

        outPow10: t => --t * Math.pow(t, 10) + 1,
    };
})();
