Find the MD4 message digest of a string of octets.
Use the ASCII encoded string “Rosetta Code” (without quotes).
You may either call an MD4 library, or implement MD4 in your language.

MD4 is an obsolete hash function that computes a 128-bit message digest that sometimes appears in obsolete protocols.

RFC 1320 specifies the MD4 algorithm. RFC 6150 declares that MD4 is obsolete.

import "/fmt" for Fmt
 
var toBytes = Fn.new { |val|
    var bytes = List.filled(4, 0)
    bytes[0] = val         & 255
    bytes[1] = (val >> 8)  & 255
    bytes[2] = (val >> 16) & 255
    bytes[3] = (val >> 24) & 255
    return bytes
}
 
var toInt = Fn.new { |bytes| bytes[0] | bytes[1] << 8 | bytes[2] << 16 | bytes[3] << 24 }
 
var md4 = Fn.new { |initMsg|
    var f = Fn.new { |x, y, z| (x & y) | (~x & z) }
    var g = Fn.new { |x, y, z| (x & y) | (x & z) | (y & z) }
    var h = Fn.new { |x, y, z| x ^ y ^ z }
    var r = Fn.new { |v, s| (v << s) | (v >> (32 - s)) }
 
    var a = 0x67452301
    var b = 0xefcdab89
    var c = 0x98badcfe
    var d = 0x10325476
    var initBytes = initMsg.bytes
    var initLen = initBytes.count
    var newLen = initLen + 1
    while (newLen % 64 != 56) newLen = newLen + 1
    var msg = List.filled(newLen + 8, 0)
    for (i in 0...initLen) msg[i] = initBytes[i]
    msg[initLen] = 0x80 // remaining bytes already 0
    var lenBits = toBytes.call(initLen * 8)
    for (i in newLen...newLen+4) msg[i] = lenBits[i-newLen]
    var extraBits = toBytes.call(initLen >> 29)
    for (i in newLen+4...newLen+8) msg[i] = extraBits[i-newLen-4]
    var offset = 0
    var x = List.filled(16, 0)
    while (offset < newLen) {
        for (i in 0...16) x[i] = toInt.call(msg[offset+i*4...offset + i*4 + 4])
        var a2 = a
        var b2 = b
        var c2 = c
        var d2 = d
        for (i in [0, 4, 8, 12]) {
            a = r.call(a + f.call(b, c, d) + x[i+0],  3)
            d = r.call(d + f.call(a, b, c) + x[i+1],  7)
            c = r.call(c + f.call(d, a, b) + x[i+2], 11)
            b = r.call(b + f.call(c, d, a) + x[i+3], 19)
        }
        for (i in 0..3) {
            a = r.call(a + g.call(b, c, d) + x[i+0]  + 0x5a827999,  3)
            d = r.call(d + g.call(a, b, c) + x[i+4]  + 0x5a827999,  5)
            c = r.call(c + g.call(d, a, b) + x[i+8]  + 0x5a827999,  9)
            b = r.call(b + g.call(c, d, a) + x[i+12] + 0x5a827999, 13)
        }
        for (i in [0, 2, 1, 3]) {
            a = r.call(a + h.call(b, c, d) + x[i+0]  + 0x6ed9eba1,  3)
            d = r.call(d + h.call(a, b, c) + x[i+8]  + 0x6ed9eba1,  9)
            c = r.call(c + h.call(d, a, b) + x[i+4]  + 0x6ed9eba1, 11)
            b = r.call(b + h.call(c, d, a) + x[i+12] + 0x6ed9eba1, 15)
        }
        a = a + a2
        b = b + b2
        c = c + c2
        d = d + d2
        offset = offset + 64
    }
    var digest = List.filled(16, 0)
    var dBytes = toBytes.call(a)
    for (i in 0...4) digest[i] = dBytes[i]
    dBytes = toBytes.call(b)
    for (i in 0...4) digest[i+4] = dBytes[i]
    dBytes = toBytes.call(c)
    for (i in 0...4) digest[i+8] = dBytes[i]
    dBytes = toBytes.call(d)
    for (i in 0...4) digest[i+12] = dBytes[i]
    return digest
}
 
var strings = [
    "",
    "a",
    "abc",
    "message digest",
    "abcdefghijklmnopqrstuvwxyz",
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
    "12345678901234567890123456789012345678901234567890123456789012345678901234567890",
    "Rosetta Code"
]
 
for (s in strings) {
    var digest = md4.call(s)
    Fmt.print("$s <== '$0s'", Fmt.v("xz", 2, digest, 0, "", ""), s)
}