/* 编译:	gcc -O2 -s -flto -static -o md5.exe md5.c  再压缩:upx -9 md5.exe */

# include <math.h>
# include <stdio.h>
# include <string.h>
# include <malloc.h>

# define cls()					printf("\033[H\033[J")
# define F(X, Y, Z)             (X & Y) | (~X & Z)
# define G(X, Y, Z)             (X & Z) | (Y & ~Z)
# define H(X, Y, Z)             X ^ Y ^ Z
# define I(X, Y, Z)             Y ^ (X | ~Z)
# define L(X, s)                ((X << s) | (X >> (32 - s))) & 0xffffffff
# define T(x)                   (unsigned int)(fabs(sin(x)) * 0x100000000)
# define MathcalH(q, w, e, r)   (q << 0) | (w << 8) | (e << 16) | (r << 24)
# define Swap(x)                (x >> 24) & 0xff | ((x >> 16) & 0xff) << 8 | ((x >> 8) & 0xff) << 16 | (x & 0xff) << 24

unsigned int A = 0x67452301, B = 0xefcdab89, C = 0x98badcfe, D = 0x10325476;
char S[64] = {
    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
};	// s 元素的范围在 [4, 23], 用 char.

int main() {
    cls();
    char INPUT[] = "Ark";
    int N = sizeof(INPUT) - 1;		// C 语言数值结尾还有一个 '\0' 表述 null 空地址, 所以减一.
    int K;
    if (N < 56) {
        K = 0;
    } else {
        K = (N - 56) / 64 + 1;
    }
    char M = 64 * K + 55 - N;		// M 范围在 [0, 63], 用 char.
    // ---------------------------------------------------------------
    /* 字节流转化十进制数组. */
    // omega 元素范围在 [0, 255], char 类型可能会因为编译器不同而取了 -128~127, 所以保守用 short.
    short *omega;
    // 申请动态数组, 如果 N 太大, 也就是输入的字符串太长, 可能会申请失败.
    omega = (short *)malloc((N + 1 + M + 8) * sizeof(short));
    for (int i = 0; INPUT[i] != '\0'; ++i) {
        if (INPUT[i] < 0) {
            omega[i] = INPUT[i] + 256;
        } else {
            omega[i] = INPUT[i];
        }
    }
    // ---------------------------------------------------------------
    /* 零比特填充. */
    omega[N] = 128;
    for (int i = 0; i < M; ++i)  omega[N+1+i] = 0;
    // ---------------------------------------------------------------
    /* 有效信息存储填充. */
    int tmp = N << 3;
    for (int i = 0; i < 8; ++i) {
        omega[N+1+M+i] = tmp & 0xff;
        tmp = tmp >> 8;
    }
    // ---------------------------------------------------------------
    /* 加密迭代. */
    for (int i = 0; i < K+1; ++i) {
        int k;		// k 的取值和 omega 数组长度相关, 所以同 N 的 int 型.
        unsigned int tmp;		// tmp 临时值的范围要小于 0xffffffff, 所以 unsigned int 型.
        unsigned int Xk;		// MathcalH 函数计算的最终值.
        unsigned int AA = A, BB = B, CC = C, DD = D;
        for (int j = 0; j < 64; ++j) {
            unsigned int aa = DD, bb = AA, cc = BB, dd = CC;
            unsigned int a = AA, b = BB, c = CC, d = DD;
            if ((0 <= j) && (j <= 15)) {
                k = i * 64 + j * 4;
                Xk = MathcalH(omega[k], omega[k+1], omega[k+2], omega[k+3]);
                tmp = (a + (F(b, c, d) & 0xffffffff) + Xk + T(j+1)) & 0xffffffff;
            } else if ((16 <= j) && (j <= 31)) {
                k = i * 64 + (((5 * j) + 1) % 16) * 4;
                Xk = MathcalH(omega[k], omega[k+1], omega[k+2], omega[k+3]);
                tmp = (a + (G(b, c, d) & 0xffffffff) + Xk + T(j+1)) & 0xffffffff;
            } else if ((32 <= j) && (j <= 47)) {
                k = i * 64 + (((3 * j) + 5) % 16) * 4;
                Xk = MathcalH(omega[k], omega[k+1], omega[k+2], omega[k+3]);
                tmp = (a + (H(b, c, d) & 0xffffffff) + Xk + T(j+1)) & 0xffffffff;
            } else {
                k = i * 64 + ((7 * j) % 16) * 4;
                Xk = MathcalH(omega[k], omega[k+1], omega[k+2], omega[k+3]);
                tmp = (a + (I(b, c, d) & 0xffffffff) + Xk + T(j+1)) & 0xffffffff;
            }
            bb = b + L(tmp, S[j]);
            AA = aa; BB = bb; CC = cc; DD = dd;
        }
        A = (A + AA) & 0xffffffff; B = (B + BB) & 0xffffffff;
        C = (C + CC) & 0xffffffff; D = (D + DD) & 0xffffffff;
    }
    free(omega);
    // 合并四个迭代完成的幻数, 打印 MD5 哈希散列值.
    char md5[32+1];		// 数组最后还有一个 null 元素结束符.
    sprintf(md5, "%x%x%x%x", Swap(A), Swap(B), Swap(C), Swap(D));
    printf("'Ark' --> MD5 --> %s", md5);
    return 0;
}