OteHmacSha256
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © OgahTerkenal (ote.digital.currency@gmail.com)

//@version=5

// @description Library to use HMAC SHA-256 by OgahTerkenal
library(title="OteHmacSha256", overlay=true)

// TABLE OF CONTENTS:
// - SECTION (ID: 01) == UTILITY FUNCTION
// - SECTION (ID: 02) == EXPORTED FUNCTION
// - SECTION (ID: 03) == USAGE EXAMPLE
// - SECTION (ID: 04) == REFERENCES
// * Use it to quickly jump/find

// IMPORTANT NOTE:
// - Only ASCII 32 to 126 characters only, outside that might produce
//   unexpected result.
// - Using barstate.islast or anything that prevent this function to run when
//   script is LOADED is a MUST
// - Running this function during load (reload/switch chart/anything else which
//   will make calculation on historical bar re-iterated) will most likely
//   causes SCRIPT TIMEOUT

// SCRIPT HISTORY:
// 2022-09-26: - Update functions in UTILITY FUNCTION to strictly declare
//               parameter type to prevent compilation error as stated by
//               @stormRidr
//             - Re-arranged sections and contents of the script
// 2021-12-24: - Initial


// == SECTION (ID: 01) == UTILITY FUNCTION == END ==
XORGate(string a, string b) =>
    if (a != "1" and a != "0") or (b != "1" and b != "0")
        runtime.error("OTE HMAC SHA-256 LibError at XORGate(): One of the input is not '1' neither '0', input is: a(" + str.tostring(a) + "), b(" + str.tostring(b) + ")")
    a == b ? "0" : "1" // the actual xor gate should sum the active bits, if it's odd then return true, but since we dealing only 2 this should be enough
ANDGate(string a, string b) =>
    if (a != "1" and a != "0") or (b != "1" and b != "0")
        runtime.error("OTE HMAC SHA-256 LibError at ANDGate(): One of the input is not '1' neither '0', input is: a(" + str.tostring(a) + "), b(" + str.tostring(b) + ")")
    a == "1" and b == "1" ? "1" : "0"
NOTGate(string a) =>
    if a != "1" and a != "0"
        runtime.error("OTE HMAC SHA-256 LibError at NOTGate(): Input is not '1' neither '0', input is: " + str.tostring(a))
    a == "1" ? "0" : "1"

binaryRotateRight(string binaryString, int _rotateLen) =>
    len = str.length(binaryString)
    rotateLen = _rotateLen % len // so when string is rotated the same number as it's own length, the result is same
    lastIndex = len - 1
    startIndex = lastIndex - (rotateLen - 1)
    endIndex = startIndex + rotateLen
    str.substring(binaryString, startIndex, endIndex) + str.substring(binaryString, 0, len - rotateLen)
binaryShiftRight(string binaryString, int shiftLen) =>
    len = str.length(binaryString)
    newBinaryString = string("")
    if shiftLen >= len
        for i = 1 to len
            newBinaryString := newBinaryString + "0"
    else
        for i = 1 to shiftLen
            newBinaryString := newBinaryString + "0"
        newBinaryString := newBinaryString + str.substring(binaryString, 0, len - shiftLen)
    newBinaryString
binaryAddition(string binaryA, string binaryB) =>
    lenA = str.length(binaryA)
    lenB = str.length(binaryB)
    if lenA != lenB
        runtime.error("OTE HMAC SHA-256 LibError at binaryAddition(): Length of A (" + str.tostring(lenA) + ") is not equal to length of B (" + str.tostring(lenB) + "), input [A, B] is: [" + binaryA + ", " + binaryB + "]")
        ""
    else
        lastCharIndex = lenA - 1
        prevRemainder = "0"
        newBinary = ""
        for i = lastCharIndex to 0
            a = str.substring(binaryA, i, i + 1)
            b = str.substring(binaryB, i, i + 1)
            // Main Logic Table
            // | a | b | prevRemainder | result | remainder |
            // | 0 | 0 |             0 |      0 |         0 |
            // | 0 | 0 |             1 |      1 |         0 |
            // | 0 | 1 |             0 |      1 |         0 |
            // | 0 | 1 |             1 |      0 |         1 |
            // | 1 | 0 |             0 |      1 |         0 |
            // | 1 | 0 |             1 |      0 |         1 |
            // | 1 | 1 |             0 |      0 |         1 |
            // | 1 | 1 |             1 |      1 |         1 |
            result = a == "0" and b == "0" and prevRemainder == "0" ? "0" : a == "0" and b == "0" and prevRemainder == "1" ? "1" : a == "0" and b == "1" and prevRemainder == "0" ? "1" : a == "0" and b == "1" and prevRemainder == "1" ? "0" : a == "1" and b == "0" and prevRemainder == "0" ? "1" : a == "1" and b == "0" and prevRemainder == "1" ? "0" : a == "1" and b == "1" and prevRemainder == "0" ? "0" : a == "1" and b == "1" and prevRemainder == "1" ? "1" : ""
            remainder = a == "0" and b == "0" and prevRemainder == "0" ? "0" : a == "0" and b == "0" and prevRemainder == "1" ? "0" : a == "0" and b == "1" and prevRemainder == "0" ? "0" : a == "0" and b == "1" and prevRemainder == "1" ? "1" : a == "1" and b == "0" and prevRemainder == "0" ? "0" : a == "1" and b == "0" and prevRemainder == "1" ? "1" : a == "1" and b == "1" and prevRemainder == "0" ? "1" : a == "1" and b == "1" and prevRemainder == "1" ? "1" : ""
            if result == "" or remainder == ""
                runtime.error("OTE HMAC SHA-256 LibError at binaryAddition(): Logic table for result not found for a: " + a + ", b: " + b + ", prevRemainder: " + prevRemainder)
            newBinary := result + newBinary
            prevRemainder := remainder
        newBinary

intToBinary(int _n, int len) =>
    s = string("")
    n = int(_n)
    while n > 0
        s := ((n % 2) == 0 ? "0" : "1") + s
        n := int(n / 2)
    if str.length(s) < len
        for i = 1 to len - str.length(s)
            s := "0" + s
    s
strBinaryToInt(string sb) =>
    len = str.length(sb)
    lastIndex = len - 1
    int pow = 0
    int sum = 0
    for i = lastIndex to 0
        b = str.substring(sb, i, i + 1)
        if b != "1" and b != "0"
            runtime.error("OTE HMAC SHA-256 LibError at strBinaryToInt(): Cannot calculate bit (" + str.tostring(b) + ") in input: " + str.tostring(sb))
        sum := sum + (b == "1" ? int(math.pow(2, pow)) : 0)
        pow := pow + 1
    sum

charToAsciiBinary(string c) =>
    c == " " ? "00100000" : c == "!" ? "00100001" : c == "\"" ? "00100010" : c == "#" ? "00100011" : c == "$" ? "00100100" : c == "%" ? "00100101" : c == "&" ? "00100110" : c == "'" ? "00100111" : c == "(" ? "00101000" : c == ")" ? "00101001" : c == "*" ? "00101010" : c == "+" ? "00101011" : c == "," ? "00101100" : c == "-" ? "00101101" : c == "." ? "00101110" : c == "/" ? "00101111" : c == "0" ? "00110000" : c == "1" ? "00110001" : c == "2" ? "00110010" : c == "3" ? "00110011" : c == "4" ? "00110100" : c == "5" ? "00110101" : c == "6" ? "00110110" : c == "7" ? "00110111" : c == "8" ? "00111000" : c == "9" ? "00111001" : c == ":" ? "00111010" : c == ";" ? "00111011" : c == "<" ? "00111100" : c == "=" ? "00111101" : c == ">" ? "00111110" : c == "?" ? "00111111" : c == "@" ? "01000000" : c == "A" ? "01000001" : c == "B" ? "01000010" : c == "C" ? "01000011" : c == "D" ? "01000100" : c == "E" ? "01000101" : c == "F" ? "01000110" : c == "G" ? "01000111" : c == "H" ? "01001000" : c == "I" ? "01001001" : c == "J" ? "01001010" : c == "K" ? "01001011" : c == "L" ? "01001100" : c == "M" ? "01001101" : c == "N" ? "01001110" : c == "O" ? "01001111" : c == "P" ? "01010000" : c == "Q" ? "01010001" : c == "R" ? "01010010" : c == "S" ? "01010011" : c == "T" ? "01010100" : c == "U" ? "01010101" : c == "V" ? "01010110" : c == "W" ? "01010111" : c == "X" ? "01011000" : c == "Y" ? "01011001" : c == "Z" ? "01011010" : c == "[" ? "01011011" : c == "\\" ? "01011100" : c == "]" ? "01011101" : c == "^" ? "01011110" : c == "_" ? "01011111" : c == "`" ? "01100000" : c == "a" ? "01100001" : c == "b" ? "01100010" : c == "c" ? "01100011" : c == "d" ? "01100100" : c == "e" ? "01100101" : c == "f" ? "01100110" : c == "g" ? "01100111" : c == "h" ? "01101000" : c == "i" ? "01101001" : c == "j" ? "01101010" : c == "k" ? "01101011" : c == "l" ? "01101100" : c == "m" ? "01101101" : c == "n" ? "01101110" : c == "o" ? "01101111" : c == "p" ? "01110000" : c == "q" ? "01110001" : c == "r" ? "01110010" : c == "s" ? "01110011" : c == "t" ? "01110100" : c == "u" ? "01110101" : c == "v" ? "01110110" : c == "w" ? "01110111" : c == "x" ? "01111000" : c == "y" ? "01111001" : c == "z" ? "01111010" : c == "{" ? "01111011" : c == "|" ? "01111100" : c == "}" ? "01111101" : c == "~" ? "01111110" : na
strToBinary(string s) =>
    sb = ""
    for i = 0 to str.length(s) - 1
        c = str.substring(s, i, i + 1)
        b = charToAsciiBinary(c)
        if na(b)
            runtime.error("OTE HMAC SHA-256 LibError at strToBinary(): Character (" + str.tostring(c) + ") does not exist in pre-registered ASCII binary.")
        sb := sb + b
    sb

binaryToHex(string b) =>
    b == "0000" ? "0" : b == "0001" ? "1" : b == "0010" ? "2" : b == "0011" ? "3" : b == "0100" ? "4" : b == "0101" ? "5" : b == "0110" ? "6" : b == "0111" ? "7" : b == "1000" ? "8" : b == "1001" ? "9" : b == "1010" ? "a" : b == "1011" ? "b" : b == "1100" ? "c" : b == "1101" ? "d" : b == "1110" ? "e" : b == "1111" ? "f" : na
strBinaryToStrHex(string s) =>
    if str.length(s) % 4 != 0
        runtime.error("OTE HMAC SHA-256 LibError at strBinaryToStrHex(): Binary string is not divisible by 4, input is: " + str.tostring(s))
    sh = ""
    for i = 0 to str.length(s) - 1 by 4
        b = str.substring(s, i, i + 4)
        h = binaryToHex(b)
        if na(h)
            runtime.error("OTE HMAC SHA-256 LibError at strBinaryToStrHex(): Binary sequence (" + str.tostring(b) + ") does not exist in pre-registered hex.")
        sh := sh + h
    sh
// == SECTION (ID: 01) == UTILITY FUNCTION == END ==


// == SECTION (ID: 02) == EXPORTED FUNCTION == START ==
// @function HMAC SHA-256
// @param string msg String to be hashed
// @returns [string, array] Return a hashed string in hex format and an array of 8 32 bits integer
export hmac_sha256(string msg) =>
    // == Initialize first hash value ==
    h0 = "01101010000010011110011001100111" // 0x6a09e667
    h1 = "10111011011001111010111010000101" // 0xbb67ae85
    h2 = "00111100011011101111001101110010" // 0x3c6ef372
    h3 = "10100101010011111111010100111010" // 0xa54ff53a
    h4 = "01010001000011100101001001111111" // 0x510e527f
    h5 = "10011011000001010110100010001100" // 0x9b05688c
    h6 = "00011111100000111101100110101011" // 0x1f83d9ab
    h7 = "01011011111000001100110100011001" // 0x5be0cd19
    // == Initialize array of round constants ==
    arr_k = array.new_string()
    array.push(arr_k, "01000010100010100010111110011000") // 0x428a2f98
    array.push(arr_k, "01110001001101110100010010010001") // 0x71374491
    array.push(arr_k, "10110101110000001111101111001111") // 0xb5c0fbcf
    array.push(arr_k, "11101001101101011101101110100101") // 0xe9b5dba5
    array.push(arr_k, "00111001010101101100001001011011") // 0x3956c25b
    array.push(arr_k, "01011001111100010001000111110001") // 0x59f111f1
    array.push(arr_k, "10010010001111111000001010100100") // 0x923f82a4
    array.push(arr_k, "10101011000111000101111011010101") // 0xab1c5ed5
    array.push(arr_k, "11011000000001111010101010011000") // 0xd807aa98
    array.push(arr_k, "00010010100000110101101100000001") // 0x12835b01
    array.push(arr_k, "00100100001100011000010110111110") // 0x243185be
    array.push(arr_k, "01010101000011000111110111000011") // 0x550c7dc3
    array.push(arr_k, "01110010101111100101110101110100") // 0x72be5d74
    array.push(arr_k, "10000000110111101011000111111110") // 0x80deb1fe
    array.push(arr_k, "10011011110111000000011010100111") // 0x9bdc06a7
    array.push(arr_k, "11000001100110111111000101110100") // 0xc19bf174
    array.push(arr_k, "11100100100110110110100111000001") // 0xe49b69c1
    array.push(arr_k, "11101111101111100100011110000110") // 0xefbe4786
    array.push(arr_k, "00001111110000011001110111000110") // 0x0fc19dc6
    array.push(arr_k, "00100100000011001010000111001100") // 0x240ca1cc
    array.push(arr_k, "00101101111010010010110001101111") // 0x2de92c6f
    array.push(arr_k, "01001010011101001000010010101010") // 0x4a7484aa
    array.push(arr_k, "01011100101100001010100111011100") // 0x5cb0a9dc
    array.push(arr_k, "01110110111110011000100011011010") // 0x76f988da
    array.push(arr_k, "10011000001111100101000101010010") // 0x983e5152
    array.push(arr_k, "10101000001100011100011001101101") // 0xa831c66d
    array.push(arr_k, "10110000000000110010011111001000") // 0xb00327c8
    array.push(arr_k, "10111111010110010111111111000111") // 0xbf597fc7
    array.push(arr_k, "11000110111000000000101111110011") // 0xc6e00bf3
    array.push(arr_k, "11010101101001111001000101000111") // 0xd5a79147
    array.push(arr_k, "00000110110010100110001101010001") // 0x06ca6351
    array.push(arr_k, "00010100001010010010100101100111") // 0x14292967
    array.push(arr_k, "00100111101101110000101010000101") // 0x27b70a85
    array.push(arr_k, "00101110000110110010000100111000") // 0x2e1b2138
    array.push(arr_k, "01001101001011000110110111111100") // 0x4d2c6dfc
    array.push(arr_k, "01010011001110000000110100010011") // 0x53380d13
    array.push(arr_k, "01100101000010100111001101010100") // 0x650a7354
    array.push(arr_k, "01110110011010100000101010111011") // 0x766a0abb
    array.push(arr_k, "10000001110000101100100100101110") // 0x81c2c92e
    array.push(arr_k, "10010010011100100010110010000101") // 0x92722c85
    array.push(arr_k, "10100010101111111110100010100001") // 0xa2bfe8a1
    array.push(arr_k, "10101000000110100110011001001011") // 0xa81a664b
    array.push(arr_k, "11000010010010111000101101110000") // 0xc24b8b70
    array.push(arr_k, "11000111011011000101000110100011") // 0xc76c51a3
    array.push(arr_k, "11010001100100101110100000011001") // 0xd192e819
    array.push(arr_k, "11010110100110010000011000100100") // 0xd6990624
    array.push(arr_k, "11110100000011100011010110000101") // 0xf40e3585
    array.push(arr_k, "00010000011010101010000001110000") // 0x106aa070
    array.push(arr_k, "00011001101001001100000100010110") // 0x19a4c116
    array.push(arr_k, "00011110001101110110110000001000") // 0x1e376c08
    array.push(arr_k, "00100111010010000111011101001100") // 0x2748774c
    array.push(arr_k, "00110100101100001011110010110101") // 0x34b0bcb5
    array.push(arr_k, "00111001000111000000110010110011") // 0x391c0cb3
    array.push(arr_k, "01001110110110001010101001001010") // 0x4ed8aa4a
    array.push(arr_k, "01011011100111001100101001001111") // 0x5b9cca4f
    array.push(arr_k, "01101000001011100110111111110011") // 0x682e6ff3
    array.push(arr_k, "01110100100011111000001011101110") // 0x748f82ee
    array.push(arr_k, "01111000101001010110001101101111") // 0x78a5636f
    array.push(arr_k, "10000100110010000111100000010100") // 0x84c87814
    array.push(arr_k, "10001100110001110000001000001000") // 0x8cc70208
    array.push(arr_k, "10010000101111101111111111111010") // 0x90befffa
    array.push(arr_k, "10100100010100000110110011101011") // 0xa4506ceb
    array.push(arr_k, "10111110111110011010001111110111") // 0xbef9a3f7
    array.push(arr_k, "11000110011100010111100011110010") // 0xc67178f2
    // == Pre-processing ==
    preProcessedMsg = strToBinary(msg) + "1" // append 1 bit to the end of original message
    L = str.length(preProcessedMsg) - 1 // obtain the original message bit count
    // append 0 bit to bits string, until the length is the multiple of 512 bits - 64 bits
    while ((str.length(preProcessedMsg) % 512)) != 448
        preProcessedMsg := preProcessedMsg + "0"
    preProcessedMsg := preProcessedMsg + intToBinary(L, 64) // append 64 bits of L
    // break message into 512-bit chunks
    arr_mainChunk = array.new_string()
    for i = 0 to str.length(preProcessedMsg) - 1 by 512
        array.push(arr_mainChunk, str.substring(preProcessedMsg, i, i + 512))
    // == Processing hash ==
    // for each chunk in main chunk
    //     break each chunk into smaller 32 bits chunk (sub-chunk)
    //     add additional 32 bits chunks of 0 into sub-chunk list
    //     for each additional chunk in sub-chunk
    //         perform 1st hash computation
    //     create working variable
    //     for each chunk in sub-chunk
    //         perform compression algorithm
    //     modify initial hash value to be used as next iteration working variable
    for n = 0 to array.size(arr_mainChunk) - 1
        mainChunk = array.get(arr_mainChunk, n) // get current main chunk
        // break main chunk into sub-chunk
        arr_subChunk = array.new_string()
        for i = 0 to 511 by 32
            array.push(arr_subChunk, str.substring(mainChunk, i, i + 32))
        // add additional sub-chunk
        for i = 1 to 48
            array.push(arr_subChunk, "00000000000000000000000000000000")
        // perform hash computation for each additional sub-chunk
        for i = 16 to 63
            prev15SubChunk = array.get(arr_subChunk, i - 15) // store for faster access
            s0_0 = binaryRotateRight(prev15SubChunk, 7) // calculate logic gate later with s1 so we only do 1 loop
            s0_1 = binaryRotateRight(prev15SubChunk, 18) // calculate logic gate later with s1 so we only do 1 loop
            s0_2 = binaryShiftRight(prev15SubChunk, 3) // calculate logic gate later with s1 so we only do 1 loop
            prev02SubChunk = array.get(arr_subChunk, i - 2) // store for faster access
            s1_0 = binaryRotateRight(prev02SubChunk, 17) // calculate logic gate later with s0 so we only do 1 loop
            s1_1 = binaryRotateRight(prev02SubChunk, 19) // calculate logic gate later with s0 so we only do 1 loop
            s1_2 = binaryShiftRight(prev02SubChunk, 10) // calculate logic gate later with s0 so we only do 1 loop
            s0 = ""
            s1 = ""
            for j = 31 to 0
                s0 := XORGate(XORGate(str.substring(s0_0, j, j + 1), str.substring(s0_1, j, j + 1)), str.substring(s0_2, j, j + 1)) + s0
                s1 := XORGate(XORGate(str.substring(s1_0, j, j + 1), str.substring(s1_1, j, j + 1)), str.substring(s1_2, j, j + 1)) + s1
            array.set(arr_subChunk, i, binaryAddition(binaryAddition(binaryAddition(array.get(arr_subChunk, i - 16), s0), array.get(arr_subChunk, i - 7)), s1))
        // create working variable
        a = h0
        b = h1
        c = h2
        d = h3
        e = h4
        f = h5
        g = h6
        h = h7
        // perform compression computation for each sub-chunk
        for i = 0 to 63
            S1_0 = binaryRotateRight(e, 6) // calculate logic gate later with ch, S0, and maj so we only do 1 loop
            S1_1 = binaryRotateRight(e, 11) // calculate logic gate later with ch, S0, and maj so we only do 1 loop
            S1_2 = binaryRotateRight(e, 25) // calculate logic gate later with ch, S0, and maj so we only do 1 loop
            S0_0 = binaryRotateRight(a, 2) // calculate logic gate later with ch, S1, and maj so we only do 1 loop
            S0_1 = binaryRotateRight(a, 13) // calculate logic gate later with ch, S1, and maj so we only do 1 loop
            S0_2 = binaryRotateRight(a, 22) // calculate logic gate later with ch, S1, and maj so we only do 1 loop
            S1 = ""
            ch = ""
            S0 = ""
            maj = ""
            for j = 0 to 31
                S1 := S1 + XORGate(XORGate(str.substring(S1_0, j, j + 1), str.substring(S1_1, j, j + 1)), str.substring(S1_2, j, j + 1))
                ch := ch + XORGate(ANDGate(str.substring(e, j, j + 1), str.substring(f, j, j + 1)),  ANDGate(NOTGate(str.substring(e, j, j + 1)), str.substring(g, j, j + 1)))
                S0 := S0 + XORGate(XORGate(str.substring(S0_0, j, j + 1), str.substring(S0_1, j, j + 1)), str.substring(S0_2, j, j + 1))
                maj := maj + XORGate(XORGate(ANDGate(str.substring(a, j, j + 1), str.substring(b, j, j + 1)), ANDGate(str.substring(a, j, j + 1), str.substring(c, j, j + 1))), ANDGate(str.substring(b, j, j + 1), str.substring(c, j, j + 1)))
            temp1 = binaryAddition(binaryAddition(binaryAddition(binaryAddition(h, S1), ch), array.get(arr_k, i)), array.get(arr_subChunk, i))
            temp2 = binaryAddition(S0, maj)
            h := g
            g := f
            f := e
            e := binaryAddition(d, temp1)
            d := c
            c := b
            b := a
            a := binaryAddition(temp1, temp2)
        // modify initial hash value to be used as next iteration working variable
        h0 := binaryAddition(h0, a)
        h1 := binaryAddition(h1, b)
        h2 := binaryAddition(h2, c)
        h3 := binaryAddition(h3, d)
        h4 := binaryAddition(h4, e)
        h5 := binaryAddition(h5, f)
        h6 := binaryAddition(h6, g)
        h7 := binaryAddition(h7, h)
    // == Concatenating hash ==
    // convert h0 - h7 from binary string to hex decimal, then concatenate
    hashMsg = strBinaryToStrHex(h0) + strBinaryToStrHex(h1) + strBinaryToStrHex(h2) + strBinaryToStrHex(h3) + strBinaryToStrHex(h4) + strBinaryToStrHex(h5) + strBinaryToStrHex(h6) + strBinaryToStrHex(h7)
    // convert h0 - h7 from binary string to int, then append to array
    arr_h = array.new_int()
    array.push(arr_h, strBinaryToInt(h0))
    array.push(arr_h, strBinaryToInt(h1))
    array.push(arr_h, strBinaryToInt(h2))
    array.push(arr_h, strBinaryToInt(h3))
    array.push(arr_h, strBinaryToInt(h4))
    array.push(arr_h, strBinaryToInt(h5))
    array.push(arr_h, strBinaryToInt(h6))
    array.push(arr_h, strBinaryToInt(h7))
    [hashMsg, arr_h]
// == SECTION (ID: 02) == EXPORTED FUNCTION == END ==


// // == SECTION (ID: 03) == USAGE EXAMPLE == START ==
// // function to split hash with space
// splitWithSpace(s, len) =>
//     string new_s = ""
//     for i = 0 to str.length(s) - 1
//         c = str.substring(s, i, i + 1)
//         new_s := new_s + (i % len == 0 and i != 0 ? " " : "") + c
//     new_s
// // input
// inputSecretKey = input.string("6a09e667bb67ae853c6ef372a54ff53a510e527f9b05688c1f83d9ab5be0cd19")
// // using time as a salt
// inputMsg = inputSecretKey// + str.tostring(time)
// // prepare variable
// hashMsg = ""
// arr_h = array.new_int()
// if barstate.islast
//     [_hashMsg, _arr_h] = hmac_sha256(inputMsg)
//     hashMsg := _hashMsg
//     arr_h := array.copy(_arr_h)
// // output to table
// var logTable = table.new(position=position.bottom_right, columns=2, rows=10, bgcolor=color.white, border_width=1, border_color=color.silver)
// if barstate.islast
//     table.cell(table_id=logTable, text_size=size.auto, text_halign=text.align_left, column=0, row=0, text="input")
//     table.cell(table_id=logTable, text_size=size.auto, text_halign=text.align_left, column=0, row=1, text="hash")
//     table.cell(table_id=logTable, text_size=size.auto, text_halign=text.align_left, column=0, row=2, text="h0")
//     table.cell(table_id=logTable, text_size=size.auto, text_halign=text.align_left, column=0, row=3, text="h1")
//     table.cell(table_id=logTable, text_size=size.auto, text_halign=text.align_left, column=0, row=4, text="h2")
//     table.cell(table_id=logTable, text_size=size.auto, text_halign=text.align_left, column=0, row=5, text="h3")
//     table.cell(table_id=logTable, text_size=size.auto, text_halign=text.align_left, column=0, row=6, text="h4")
//     table.cell(table_id=logTable, text_size=size.auto, text_halign=text.align_left, column=0, row=7, text="h5")
//     table.cell(table_id=logTable, text_size=size.auto, text_halign=text.align_left, column=0, row=8, text="h6")
//     table.cell(table_id=logTable, text_size=size.auto, text_halign=text.align_left, column=0, row=9, text="h7")
//     table.cell(table_id=logTable, text_size=size.auto, text_halign=text.align_left, column=1, row=0, text=inputMsg)
//     table.cell(table_id=logTable, text_size=size.auto, text_halign=text.align_left, column=1, row=1, text=splitWithSpace(hashMsg, 8))
//     table.cell(table_id=logTable, text_size=size.auto, text_halign=text.align_left, column=1, row=2, text=str.tostring( array.get(arr_h, 0) ))
//     table.cell(table_id=logTable, text_size=size.auto, text_halign=text.align_left, column=1, row=3, text=str.tostring( array.get(arr_h, 1) ))
//     table.cell(table_id=logTable, text_size=size.auto, text_halign=text.align_left, column=1, row=4, text=str.tostring( array.get(arr_h, 2) ))
//     table.cell(table_id=logTable, text_size=size.auto, text_halign=text.align_left, column=1, row=5, text=str.tostring( array.get(arr_h, 3) ))
//     table.cell(table_id=logTable, text_size=size.auto, text_halign=text.align_left, column=1, row=6, text=str.tostring( array.get(arr_h, 4) ))
//     table.cell(table_id=logTable, text_size=size.auto, text_halign=text.align_left, column=1, row=7, text=str.tostring( array.get(arr_h, 5) ))
//     table.cell(table_id=logTable, text_size=size.auto, text_halign=text.align_left, column=1, row=8, text=str.tostring( array.get(arr_h, 6) ))
//     table.cell(table_id=logTable, text_size=size.auto, text_halign=text.align_left, column=1, row=9, text=str.tostring( array.get(arr_h, 7) ))
// // output to plot
// plot(title="h0", series=barstate.islast ? array.get(arr_h, 0) : na, color=color.new(#f23645, 0), style=plot.style_circles)
// plot(title="h1", series=barstate.islast ? array.get(arr_h, 1) : na, color=color.new(#ff9800, 0), style=plot.style_circles)
// plot(title="h2", series=barstate.islast ? array.get(arr_h, 2) : na, color=color.new(#ffeb3b, 0), style=plot.style_circles)
// plot(title="h3", series=barstate.islast ? array.get(arr_h, 3) : na, color=color.new(#4caf50, 0), style=plot.style_circles)
// plot(title="h4", series=barstate.islast ? array.get(arr_h, 4) : na, color=color.new(#089981, 0), style=plot.style_circles)
// plot(title="h5", series=barstate.islast ? array.get(arr_h, 5) : na, color=color.new(#00bcd4, 0), style=plot.style_circles)
// plot(title="h6", series=barstate.islast ? array.get(arr_h, 6) : na, color=color.new(#2962ff, 0), style=plot.style_circles)
// plot(title="h7", series=barstate.islast ? array.get(arr_h, 7) : na, color=color.new(#673ab7, 0), style=plot.style_circles)
// // by using plot, you can call the variable for the alert message body
// // and on your server/client/whatever side sequentially convert each value
// // to hex and concatenate to form the hash string.
// // 
// // below is full scenario example assuming input hash (unsalted) is:
// // 6a09e667bb67ae853c6ef372a54ff53a510e527f9b05688c1f83d9ab5be0cd19
// // 
// // Example Alert Message Body:
// // {"key": [ {{plot("h0")}}, {{plot("h1")}}, {{plot("h2")}}, {{plot("h3")}}, {{plot("h4")}}, {{plot("h5")}}, {{plot("h6")}}, {{plot("h7")}} ]}
// // Which lead to JSON message:
// // {"key": [ 2076858297, 2282410289, 2130471305, 1905036923, 413802551, 286061022, 208206988, 4013625551 ]}
// // Example in receiver's side (using javascript):
// // let tvAlert = JSON.parse(jsonMessageReceivedFromTradingView)
// // let hash = tvAlert.key[0].toString(16) + tvAlert.key[1].toString(16) + tvAlert.key[2].toString(16) + tvAlert.key[3].toString(16) + tvAlert.key[4].toString(16) + tvAlert.key[5].toString(16) + tvAlert.key[6].toString(16) + tvAlert.key[7].toString(16)
// // == SECTION (ID: 03) == USAGE EXAMPLE == END ==


// == SECTION (ID: 04) == REFERENCES == START ==
// - https://medium.com/@domspaulo/python-implementation-of-sha-256-from-scratch-924f660c5d57

// - Original Pseudocode for SHA-256
// Retrieved from: https://en.wikipedia.org/wiki/SHA-2#Pseudocode
// On: 2021-12-04, 00:04 GMT+7
// 
// 
// 
// Note 1: All variables are 32 bit unsigned integers and addition is calculated modulo 232
// Note 2: For each round, there is one round constant k[i] and one entry in the message schedule array w[i], 0 ≤ i ≤ 63
// Note 3: The compression function uses 8 working variables, a through h
// Note 4: Big-endian convention is used when expressing the constants in this pseudocode,
//     and when parsing message block data from bytes to words, for example,
//     the first word of the input message "abc" after padding is 0x61626380
// 
// Initialize hash values:
// (first 32 bits of the fractional parts of the square roots of the first 8 primes 2..19):
// h0 := 0x6a09e667
// h1 := 0xbb67ae85
// h2 := 0x3c6ef372
// h3 := 0xa54ff53a
// h4 := 0x510e527f
// h5 := 0x9b05688c
// h6 := 0x1f83d9ab
// h7 := 0x5be0cd19
// 
// Initialize array of round constants:
// (first 32 bits of the fractional parts of the cube roots of the first 64 primes 2..311):
// k[0..63] :=
//   0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
//   0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
//   0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
//   0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
//   0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
//   0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
//   0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
//   0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
// 
// Pre-processing (Padding):
// begin with the original message of length L bits
// append a single '1' bit
// append K '0' bits, where K is the minimum number >= 0 such that L + 1 + K + 64 is a multiple of 512
// append L as a 64-bit big-endian integer, making the total post-processed length a multiple of 512 bits
// 
// Process the message in successive 512-bit chunks:
// break message into 512-bit chunks
// for each chunk
//     create a 64-entry message schedule array w[0..63] of 32-bit words
//     (The initial values in w[0..63] don't matter, so many implementations zero them here)
//     copy chunk into first 16 words w[0..15] of the message schedule array
// 
//     Extend the first 16 words into the remaining 48 words w[16..63] of the message schedule array:
//     for i from 16 to 63
//         s0 := (w[i-15] rightrotate  7) xor (w[i-15] rightrotate 18) xor (w[i-15] rightshift  3)
//         s1 := (w[i- 2] rightrotate 17) xor (w[i- 2] rightrotate 19) xor (w[i- 2] rightshift 10)
//         w[i] := w[i-16] + s0 + w[i-7] + s1
// 
//     Initialize working variables to current hash value:
//     a := h0
//     b := h1
//     c := h2
//     d := h3
//     e := h4
//     f := h5
//     g := h6
//     h := h7
// 
//     Compression function main loop:
//     for i from 0 to 63
//         S1 := (e rightrotate 6) xor (e rightrotate 11) xor (e rightrotate 25)
//         ch := (e and f) xor ((not e) and g)
//         temp1 := h + S1 + ch + k[i] + w[i]
//         S0 := (a rightrotate 2) xor (a rightrotate 13) xor (a rightrotate 22)
//         maj := (a and b) xor (a and c) xor (b and c)
//         temp2 := S0 + maj
//  
//         h := g
//         g := f
//         f := e
//         e := d + temp1
//         d := c
//         c := b
//         b := a
//         a := temp1 + temp2
// 
//     Add the compressed chunk to the current hash value:
//     h0 := h0 + a
//     h1 := h1 + b
//     h2 := h2 + c
//     h3 := h3 + d
//     h4 := h4 + e
//     h5 := h5 + f
//     h6 := h6 + g
//     h7 := h7 + h
// 
// Produce the final hash value (big-endian):
// digest := hash := h0 append h1 append h2 append h3 append h4 append h5 append h6 append h7
// == SECTION (ID: 04) == REFERENCES == END ==
