// File: theuni[P]libutreexo/crypto/sha256.cpp
// Function: (anonymous namespace)::SelfTest()
bool SelfTest() {
    // Input state (equal to the initial SHA256 state)
    static const uint32_t init[8] = {
        0x6a09e667ul, 0xbb67ae85ul, 0x3c6ef372ul, 0xa54ff53aul, 0x510e527ful, 0x9b05688cul, 0x1f83d9abul, 0x5be0cd19ul
    };
    // Some random input data to test with
    static const unsigned char data[641] = "-" // Intentionally not aligned
        "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do "
        "eiusmod tempor incididunt ut labore et dolore magna aliqua. Et m"
        "olestie ac feugiat sed lectus vestibulum mattis ullamcorper. Mor"
        "bi blandit cursus risus at ultrices mi tempus imperdiet nulla. N"
        "unc congue nisi vita suscipit tellus mauris. Imperdiet proin fer"
        "mentum leo vel orci. Massa tempor nec feugiat nisl pretium fusce"
        " id velit. Telus in metus vulputate eu scelerisque felis. Mi tem"
        "pus imperdiet nulla malesuada pellentesque. Tristique magna sit.";
    // Expected output state for hashing the i*64 first input bytes above (excluding SHA256 padding).
    static const uint32_t result[9][8] = {
        {0x6a09e667ul, 0xbb67ae85ul, 0x3c6ef372ul, 0xa54ff53aul, 0x510e527ful, 0x9b05688cul, 0x1f83d9abul, 0x5be0cd19ul},
        {0x91f8ec6bul, 0x4da10fe3ul, 0x1c9c292cul, 0x45e18185ul, 0x435cc111ul, 0x3ca26f09ul, 0xeb954caeul, 0x402a7069ul},
        {0xcabea5acul, 0x374fb97cul, 0x182ad996ul, 0x7bd69cbful, 0x450ff900ul, 0xc1d2be8aul, 0x6a41d505ul, 0xe6212dc3ul},
        {0xbcff09d6ul, 0x3e76f36eul, 0x3ecb2501ul, 0x78866e97ul, 0xe1c1e2fdul, 0x32f4eafful, 0x8aa6c4e5ul, 0xdfc024bcul},
        {0xa08c5d94ul, 0x0a862f93ul, 0x6b7f2f40ul, 0x8f9fae76ul, 0x6d40439ful, 0x79dcee0cul, 0x3e39ff3aul, 0xdc3bdbb1ul},
        {0x216a0895ul, 0x9f1a3662ul, 0xe99946f9ul, 0x87ba4364ul, 0x0fb5db2cul, 0x12bed3d3ul, 0x6689c0c7ul, 0x292f1b04ul},
        {0xca3067f8ul, 0xbc8c2656ul, 0x37cb7e0dul, 0x9b6b8b0ful, 0x46dc380bul, 0xf1287f57ul, 0xc42e4b23ul, 0x3fefe94dul},
        {0x3e4c4039ul, 0xbb6fca8cul, 0x6f27d2f7ul, 0x301e44a4ul, 0x8352ba14ul, 0x5769ce37ul, 0x48a1155ful, 0xc0e1c4c6ul},
        {0xfe2fa9ddul, 0x69d0862bul, 0x1ae0db23ul, 0x471f9244ul, 0xf55c0145ul, 0xc30f9c3bul, 0x40a84ea0ul, 0x5b8a266cul},
    };
    // Expected output for each of the individual 8 64-byte messages under full double SHA256 (including padding).
    static const unsigned char result_d64[256] = {
        0x09, 0x3a, 0xc4, 0xd0, 0x0f, 0xf7, 0x57, 0xe1, 0x72, 0x85, 0x79, 0x42, 0xfe, 0xe7, 0xe0, 0xa0,
        0xfc, 0x52, 0xd7, 0xdb, 0x07, 0x63, 0x45, 0xfb, 0x53, 0x14, 0x7d, 0x17, 0x22, 0x86, 0xf0, 0x52,
        0x48, 0xb6, 0x11, 0x9e, 0x6e, 0x48, 0x81, 0x6d, 0xcc, 0x57, 0x1f, 0xb2, 0x97, 0xa8, 0xd5, 0x25,
        0x9b, 0x82, 0xaa, 0x89, 0xe2, 0xfd, 0x2d, 0x56, 0xe8, 0x28, 0x83, 0x0b, 0xe2, 0xfa, 0x53, 0xb7,
        0xd6, 0x6b, 0x07, 0x85, 0x83, 0xb0, 0x10, 0xa2, 0xf5, 0x51, 0x3c, 0xf9, 0x60, 0x03, 0xab, 0x45,
        0x6c, 0x15, 0x6e, 0xef, 0xb5, 0xac, 0x3e, 0x6c, 0xdf, 0xb4, 0x92, 0x22, 0x2d, 0xce, 0xbf, 0x3e,
        0xe9, 0xe5, 0xf6, 0x29, 0x0e, 0x01, 0x4f, 0xd2, 0xd4, 0x45, 0x65, 0xb3, 0xbb, 0xf2, 0x4c, 0x16,
        0x37, 0x50, 0x3c, 0x6e, 0x49, 0x8c, 0x5a, 0x89, 0x2b, 0x1b, 0xab, 0xc4, 0x37, 0xd1, 0x46, 0xe9,
        0x3d, 0x0e, 0x85, 0xa2, 0x50, 0x73, 0xa1, 0x5e, 0x54, 0x37, 0xd7, 0x94, 0x17, 0x56, 0xc2, 0xd8,
        0xe5, 0x9f, 0xed, 0x4e, 0xae, 0x15, 0x42, 0x06, 0x0d, 0x74, 0x74, 0x5e, 0x24, 0x30, 0xce, 0xd1,
        0x9e, 0x50, 0xa3, 0x9a, 0xb8, 0xf0, 0x4a, 0x57, 0x69, 0x78, 0x67, 0x12, 0x84, 0x58, 0xbe, 0xc7,
        0x36, 0xaa, 0xee, 0x7c, 0x64, 0xa3, 0x76, 0xec, 0xff, 0x55, 0x41, 0x00, 0x2a, 0x44, 0x68, 0x4d,
        0xb6, 0x53, 0x9e, 0x1c, 0x95, 0xb7, 0xca, 0xdc, 0x7f, 0x7d, 0x74, 0x27, 0x5c, 0x8e, 0xa6, 0x84,
        0xb5, 0xac, 0x87, 0xa9, 0xf3, 0xff, 0x75, 0xf2, 0x34, 0xcd, 0x1a, 0x3b, 0x82, 0x2c, 0x2b, 0x4e,
        0x6a, 0x46, 0x30, 0xa6, 0x89, 0x86, 0x23, 0xac, 0xf8, 0xa5, 0x15, 0xe9, 0x0a, 0xaa, 0x1e, 0x9a,
        0xd7, 0x93, 0x6b, 0x28, 0xe4, 0x3b, 0xfd, 0x59, 0xc6, 0xed, 0x7c, 0x5f, 0xa5, 0x41, 0xcb, 0x51
    };


    // Test Transform() for 0 through 8 transformations.
    for (size_t i = 0; i <= 8; ++i) {
        uint32_t state[8];
        std::copy(init, init + 8, state);
        Transform(state, data + 1, i);
        if (!std::equal(state, state + 8, result[i])) return false;
    }

    // Test TransformD64
    unsigned char out[32];
    TransformD64(out, data + 1);
    if (!std::equal(out, out + 32, result_d64)) return false;

    // Test TransformD64_2way, if available.
    if (TransformD64_2way) {
        unsigned char out[64];
        TransformD64_2way(out, data + 1);
        if (!std::equal(out, out + 64, result_d64)) return false;
    }

    // Test TransformD64_4way, if available.
    if (TransformD64_4way) {
        unsigned char out[128];
        TransformD64_4way(out, data + 1);
        if (!std::equal(out, out + 128, result_d64)) return false;
    }

    // Test TransformD64_8way, if available.
    if (TransformD64_8way) {
        unsigned char out[256];
        TransformD64_8way(out, data + 1);
        if (!std::equal(out, out + 256, result_d64)) return false;
    }

    return true;
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
subq $0x210, %rsp            # imm = 0x210
movq $0x0, -0x10(%rbp)
cmpq $0x8, -0x10(%rbp)
ja 0x1a2d
leaq -0x30(%rbp), %rdx
leaq 0x5071b(%rip), %rdi     # 0x520e0
movq %rdi, %rsi
addq $0x20, %rsi
callq 0xa790
movq 0x536c8(%rip), %rax     # 0x550a0
leaq -0x30(%rbp), %rdi
movq -0x10(%rbp), %rdx
leaq 0x50719(%rip), %rsi     # 0x52100
addq $0x1, %rsi
callq *%rax
leaq -0x30(%rbp), %rdi
leaq -0x30(%rbp), %rsi
addq $0x20, %rsi
movq -0x10(%rbp), %rax
leaq 0x5098c(%rip), %rdx     # 0x52390
shlq $0x5, %rax
addq %rax, %rdx
callq 0xa7d0
testb $0x1, %al
jne 0x1a1d
movb $0x0, -0x1(%rbp)
jmp 0x1b64
jmp 0x1a1f
movq -0x10(%rbp), %rax
addq $0x1, %rax
movq %rax, -0x10(%rbp)
jmp 0x19b3
movq 0x53674(%rip), %rax     # 0x550a8
leaq -0x50(%rbp), %rdi
leaq 0x506c1(%rip), %rsi     # 0x52100
addq $0x1, %rsi
callq *%rax
leaq -0x50(%rbp), %rdi
leaq -0x50(%rbp), %rsi
addq $0x20, %rsi
leaq 0x50a58(%rip), %rdx     # 0x524b0
callq 0xa800
testb $0x1, %al
jne 0x1a6a
movb $0x0, -0x1(%rbp)
jmp 0x1b64
cmpq $0x0, 0x53646(%rip)     # 0x550b8
je 0x1abc
movq 0x5363d(%rip), %rax     # 0x550b8
leaq -0x90(%rbp), %rdi
leaq 0x50677(%rip), %rsi     # 0x52100
addq $0x1, %rsi
callq *%rax
leaq -0x90(%rbp), %rdi
leaq -0x90(%rbp), %rsi
addq $0x40, %rsi
leaq 0x50a08(%rip), %rdx     # 0x524b0
callq 0xa800
testb $0x1, %al
jne 0x1aba
movb $0x0, -0x1(%rbp)
jmp 0x1b64
jmp 0x1abc
cmpq $0x0, 0x535fc(%rip)     # 0x550c0
je 0x1b0e
movq 0x535f3(%rip), %rax     # 0x550c0
leaq -0x110(%rbp), %rdi
leaq 0x50625(%rip), %rsi     # 0x52100
addq $0x1, %rsi
callq *%rax
leaq -0x110(%rbp), %rdi
leaq -0x110(%rbp), %rsi
addq $0x80, %rsi
leaq 0x509b3(%rip), %rdx     # 0x524b0
callq 0xa800
testb $0x1, %al
jne 0x1b0c
movb $0x0, -0x1(%rbp)
jmp 0x1b64
jmp 0x1b0e
cmpq $0x0, 0x535b2(%rip)     # 0x550c8
je 0x1b60
movq 0x535a9(%rip), %rax     # 0x550c8
leaq -0x210(%rbp), %rdi
leaq 0x505d3(%rip), %rsi     # 0x52100
addq $0x1, %rsi
callq *%rax
leaq -0x210(%rbp), %rdi
leaq -0x210(%rbp), %rsi
addq $0x100, %rsi            # imm = 0x100
leaq 0x50961(%rip), %rdx     # 0x524b0
callq 0xa800
testb $0x1, %al
jne 0x1b5e
movb $0x0, -0x1(%rbp)
jmp 0x1b64
jmp 0x1b60
movb $0x1, -0x1(%rbp)
movb -0x1(%rbp), %al
andb $0x1, %al
addq $0x210, %rsp            # imm = 0x210
popq %rbp
retq
nopw %cs:(%rax,%rax)