/********************************************************************
 * @author:      Kaven
 * @email:       kaven@wuwenkai.com
 * @website:     http://kavenblog.com
 * @file:        [Kaven-Basic] /src/libs/hashing_algorithms/KavenMD5.ts
 * @create:      2019-04-05 07:31:11.304
 * @modify:      2019-04-05 09:59:02.180
 * @version:     1.1.20
 * @times:       26
 * @lines:       202
 * @copyright:   Copyright © 2019 Kaven. All Rights Reserved.
 * @description: [description]
 * @license:
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 ********************************************************************/
import { BitString } from "../BitString";
// tslint:disable: no-bitwise
/**
 * @see https://en.wikipedia.org/wiki/MD5
 */
var KavenMD5 = /** @class */ (function () {
    function KavenMD5() {
    }
    KavenMD5.prototype.Compute = function (str) {
        // Note: All variables are unsigned 32 bit and wrap modulo 2^32 when calculating
        var data = str.ToUTF8ByteArray();
        // s specifies the per-round shift amounts
        var s = [
            7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
            5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
            4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
            6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21,
        ];
        // Use binary integer part of the sines of integers (Radians) as constants
        // const K: number[] = new Array(64);
        // for (let i = 0; i <= 63; i++) {
        //     K[i] = Math.floor(Math.pow(2, 32) * Math.abs(Math.sin(i + 1)));
        // }
        // Or just use the following precomputed table
        var K = [
            0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
            0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
            0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
            0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
            0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
            0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
            0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
            0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
            0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
            0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
            0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
            0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
            0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
            0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
            0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
            0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391,
        ];
        // Initialize variables
        var a0 = 0x67452301;
        var b0 = 0xefcdab89;
        var c0 = 0x98badcfe;
        var d0 = 0x10325476;
        /// Pre-processing
        var L = data.length * 8;
        // append a single '1' bit, where the first bit is the most significant bit of the byte
        data.push(0x80); // 8 bits
        // padding with zeros
        var total = 512 - (L % 512);
        var bitsCount = total - 8 - 64;
        var n = bitsCount / 8;
        while (n-- > 0) {
            data.push(0);
        }
        // append original length in bits
        var lbe = new BitString(L).ToBytes(64);
        data.push.apply(data, lbe.reverse());
        /// Process the message in successive 512-bit chunks
        var chunks = data.Split(512 / 8, true);
        for (var _i = 0, chunks_1 = chunks; _i < chunks_1.length; _i++) {
            var chunk = chunks_1[_i];
            var M = new Array(64);
            // break chunk into sixteen 32-bit words M[j], 0 ≤ j ≤ 15
            var bit32Array = chunk.Split(4, true);
            for (var j = 0; j < 16; j++) {
                var bits32 = bit32Array[j];
                M[j] = bits32[3] << 24 | bits32[2] << 16 | bits32[1] << 8 | bits32[0];
            }
            // Initialize hash value for this chunk
            var A = a0;
            var B = b0;
            var C = c0;
            var D = d0;
            // Main loop
            for (var i = 0; i <= 63; i++) {
                var F = void 0;
                var g = void 0;
                if (0 <= i && i <= 15) {
                    // F := (B and C) or ((not B) and D)
                    F = (B & C) | ((~B) & D);
                    // g := i
                    g = i;
                }
                else if (16 <= i && i <= 31) {
                    // F := (D and B) or ((not D) and C)
                    F = (D & B) | ((~D) & C);
                    // g := (5×i + 1) mod 16
                    g = (5 * i + 1) % 16;
                }
                else if (32 <= i && i <= 47) {
                    // F := B xor C xor D
                    F = B ^ C ^ D;
                    // g := (3×i + 5) mod 16
                    g = (3 * i + 5) % 16;
                }
                else if (48 <= i && i <= 63) {
                    // F := C xor (B or (not D))
                    F = C ^ (B | (~D));
                    // g := (7×i) mod 16
                    g = (7 * i) % 16;
                }
                else {
                    throw new Error("Invalid index: " + i);
                }
                // Be wary of the below definitions of a,b,c,d
                F = this.addMany(F, A, K[i], M[g]);
                A = D;
                D = C;
                C = B;
                B = this.add(B, F.LeftRotate(s[i]));
            }
            // Add this chunk's hash to result so far
            a0 = this.add(a0, A);
            b0 = this.add(b0, B);
            c0 = this.add(c0, C);
            d0 = this.add(d0, D);
        }
        var result = [a0, b0, c0, d0].map(function (p) { return p.Swap32().ToUInt32(); });
        var hash = result.ToHEX();
        return hash;
    };
    KavenMD5.prototype.add = function (a, b) {
        return (a.ToUInt32() + b.ToUInt32()).ToUInt32();
    };
    KavenMD5.prototype.addMany = function () {
        var items = [];
        for (var _i = 0; _i < arguments.length; _i++) {
            items[_i] = arguments[_i];
        }
        var val = 0;
        for (var _a = 0, items_1 = items; _a < items_1.length; _a++) {
            var item = items_1[_a];
            val = this.add(val, item);
        }
        return val;
    };
    return KavenMD5;
}());
export { KavenMD5 };
//# sourceMappingURL=KavenMD5.js.map