Pure Lua hashing method - lua

This has been bothering be for a while now, I cannot seem to find a pure Lua implementation of a popular hashing method like SHA256, SHA512, or Whirlpool. I need it as I will be hashing the password client side before sending it of to a server. Speed isn't a worry here, I don't care if it takes 10 or so seconds to hash 10,000 times, I will be running it on a thread.
I have tried a couple before, which seemed like they worked perfectly fine at first, but when I tried a different input strings (usually longer ones), the hash comes out as a totally incorrect hash output.
I am using the LuaJIT version of Love2D, so it already has the BitOp library implemented. If any of you know any good implementations of these hashing methods or any similar secure ones then please let me know!
Thank you!
UPDATE: Here are some results!
Firstly this is the test code I am using.
https://github.com/JustAPerson/LuaCrypt
INPUT: Test string
OUTPUT: a3e49d843df13c2e2a7786f6ecd7e0d184f45d718d1ac1a8a63e570466e489dd
EXPECTED: a3e49d843df13c2e2a7786f6ecd7e0d184f45d718d1ac1a8a63e570466e489dd
INPUT: This is a test string to hash
OUTPUT: 05b4ac920d4130cb9d9bb046cac7476f35d7404cf116dc8d6d4a113c3c79d904
EXPECTED: f70b476ff948472f8e4e52793a5a2779e636c20dd5336d3a8a4455374318db35
https://bitbucket.org/Boolsheet/sil/raw/tip/hash.lua
INPUT: Test string
OUTPUT: 8f1a5b37fbe986953c343d5b839b14843c6c29d47a6a7e52f263cd82ad6141a3
EXPECTED: a3e49d843df13c2e2a7786f6ecd7e0d184f45d718d1ac1a8a63e570466e489dd
INPUT: This is a test string to hash
OUTPUT: 167bf7b9000442419b3016a6e1edfcc7c8d40b5f0b80518a31ddb0bbd388e87ac
EXPECTED: f70b476ff948472f8e4e52793a5a2779e636c20dd5336d3a8a4455374318db35

I would recommend against using SHA256 for passwords. They are easy to bruteforce nowadays, and the way you are using them is vulnerable to replay attacks.
Also if you must use SHA256, use the version from OpenSSL if possible (especially if your program already depends on OpenSSL.)
But if you must use it (and cannot link with OpenSSL, but can use FFI) here is a LuaJIT version of SHA256 (only) that I am using in one of my projects.
local bit = require 'bit'
local ffi = require 'ffi'
local type = type
local band, bnot, bswap, bxor, rol, ror, rshift, tobit =
bit.band, bit.bnot, bit.bswap, bit.bxor, bit.rol, bit.ror, bit.rshift, bit.tobit
local min, max = math.min, math.max
local C = ffi.C
local istype, new, fill, copy, cast, sizeof, ffi_string =
ffi.istype, ffi.new, ffi.fill, ffi.copy, ffi.cast, ffi.sizeof, ffi.string
local sha256 = {}
ffi.cdef [[
void *malloc(size_t size);
void free(void *ptr);
]]
local ctHashState = ffi.typeof 'uint32_t[8]'
local cbHashState = ffi.sizeof(ctHashState)
local ctBlock = ffi.typeof 'uint32_t[64]'
local cbBlock = ffi.sizeof(ctBlock)
local ctpu8 = ffi.typeof 'uint8_t *'
local ctpcu8 = ffi.typeof 'const uint8_t *'
local ctpu32 = ffi.typeof 'uint32_t *'
local ctpu64 = ffi.typeof 'uint64_t *'
-- This struct is used by the 'preprocess' iterator function. It keeps track
-- of the end of the input string + the total input length in bits + a pointer
-- to the block buffer (where expansion takes place.)
local ctBlockIter
local cmtBlockIter = {}
function cmtBlockIter.__sub(a, b)
if istype(ctBlockIter, a) then a = a.limit end
if istype(ctBlockIter, b) then b = b.limit end
return a - b
end
function cmtBlockIter:__tostring()
return string.format("<ctBlockIter: limit=%s; keyLength=%s>",
tostring(self.base), tostring(self.keyLength))
end
ctBlockIter = ffi.metatype([[
struct {
const uint8_t *limit;
uint32_t *blockBuffer;
uint64_t keyLength;
}
]], cmtBlockIter)
-- Initial state of the hash
local init_h = new('const uint32_t[8]', {
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
})
-- Constants used in the add step of the compression function
local k = new('const uint32_t[64]', {
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
})
-- Expand block from 512 to 2048 bits
local function expand(w)
for i = 16, 63 do
local s0 = bxor(ror(w[i-15], 7), ror(w[i-15], 18), rshift(w[i-15], 3))
local s1 = bxor(ror(w[i-2], 17), ror(w[i-2], 19), rshift(w[i-2], 10))
w[i] = w[i-16] + s0 + w[i-7] + s1
end
end
-- Process one expanded block and update the hash state
local function compress(hh, w)
local a, b, c, d, e, f, g, h =
hh[0],hh[1],hh[2],hh[3],hh[4],hh[5],hh[6],hh[7]
for i = 0, 63 do
local S1 = bxor(ror(e, 6), ror(e, 11), ror(e, 25))
local ch = bxor(band(e, f), band(bnot(e), g))
local t = tobit(h + S1 + ch + k[i] + w[i])
local S0 = bxor(ror(a, 2), ror(a, 13), ror(a, 22))
local maj = bxor(band(a, bxor(b, c)), band(b, c))
a, b, c, d, e, f, g, h =
tobit(t + S0 + maj),
a, b, c,
tobit(d + t),
e, f, g
end
hh[0],hh[1],hh[2],hh[3],hh[4],hh[5],hh[6],hh[7] =
hh[0]+a, hh[1]+b, hh[2]+c, hh[3]+d,
hh[4]+e, hh[5]+f, hh[6]+g, hh[7]+h
end
-- Take a 512-bit chunk from the input.
-- If it is the final chunk, also add padding
local keyLengthOfs = ffi.offsetof(ctBlockIter, 'keyLength')
local function nextBlock(state, input)
local w = state.blockBuffer
local cLen = min(state - input, 64)
if cLen < -8 then return nil end
fill(w, 256, 0)
copy(w, input, max(0, cLen))
if 0 <= cLen and cLen < 64 then
copy(cast(ctpu8, w)+cLen, '\128', 1)
end
for i = 0, 15 do w[i] = bswap(w[i]) end
if cLen <= (64-8-1) then
copy(cast(ctpu64, w) + 7, cast(ctpu8, state) + keyLengthOfs, 8)
w[14], w[15] = w[15], w[14]
end
input = input + 64
return input
end
-- Iterator that yields one block (possibly padded) at a time from the input
local function preprocess(input, len, w)
len = len or (type(input) == 'string' and #input or sizeof(input))
input = cast(ctpu8, input)
local it = new(ctBlockIter)
it.blockBuffer = w
it.limit = input+len
it.keyLength = len*8
return nextBlock, it, input
end
-- Compute a binary hash (32-byte binary string) from the input
function sha256.binFromBin(input, len)
local h = new(ctHashState)
local w = cast(ctpu32, C.malloc(cbBlock))
copy(h, init_h, cbHashState)
for _ in preprocess(input, len, w) do
expand(w)
compress(h, w)
end
for i = 0, 7 do h[i] = bswap(h[i]) end
C.free(w)
return ffi_string(h, 32)
end
local hexDigits = new('char[16]', "0123456789abcdef")
local hexOut = new('char[65]')
-- Compute the hash and convert to hexadecimal
function sha256.hexFromBin(input, len)
local h = new(ctHashState)
local w = cast(ctpu32, C.malloc(cbBlock))
copy(h, init_h, cbHashState)
for _ in preprocess(input, len, w) do
expand(w)
compress(h, w)
end
for i = 0, 7 do
local w = h[i]
for j = 0, 3 do
w = rol(w, 8)
hexOut[i*8 + j*2] = hexDigits[band(rshift(w, 4), 15)]
hexOut[i*8 + j*2 + 1] = hexDigits[band(w, 15)]
end
end
C.free(w)
return ffi_string(hexOut, 64)
end
return sha256

There is an implementation of SHA256 at the Lua User's Wiki. The page observes it is Lua 5.2. I would imagine that it would be practical to make that work in LuaJIT without too much trouble.
Do pay attention to the larger security issues surrounding passwords and authentication. The usual advice applies; rolling your own security rather than using an existing tested and supported implementation is not something to be done lightly.
Since you are using LuaJIT, you should be able to leverage its very powerful FFI capabilities to use crypto supplied on your native platform. That will likely require writing some FFI-flavored Lua that is platform specific to each platform on which your client expects to run, but from what I've seen by lurking in the LuaJIT mailing list that shouldn't be too painful.

Related

PBKDF2 Lua Implementation Issue

I am trying to write a PBKDF2 implementation in pure lua. I am writing it because I want to use it in a sandboxed lua environment that does not allow outside libraries. I had a look at the standard document from the IETF and had at it. Below is the code I have come up with:
do
package.preload["pbkdf2"] = function()
local hmac = require 'hmac'
local len = string.len
local gsub = string.gsub
local format = string.format
local byte = string.byte
local char = string.char
local concat = table.concat
local ceil = math.ceil
local function toBytes(str)
local tmp = {}
for i = 1, len(str) do
tmp[i] = byte(str, i)
end
return tmp
end
local function toString(bArray)
local tmp = {}
for i = 1, #bArray do
tmp[i] = char(bArray[i])
end
tmp = concat(tmp)
return tmp
end
-- transform a string of bytes in a string of hexadecimal digits
local function asHex(s)
local h = gsub(s, ".", function(c)
return format("%02x", byte(c))
end)
return h
end
local num2string = function(l, n)
local s = {}
for i = 1, n do
local idx = (n + 1) - i
s[idx] = char(l & 255)
l = l >> 8
end
s = concat(s)
return s
end
local buildBlock = function(hFun, password, salt, c, int)
local tmp
local tmp2
for i = 1, c do
if i == 1 then
print(int)
print(salt .. int)
-- PRF(password, salt || INT_32_BE(i)
-- return result of hash as a byte string
tmp = hmac.hash(hFun, password, salt .. num2string(int, 4), true)
else
-- returns result of hash as byte string
tmp2 = hmac.hash(hFun, password, tmp, true)
-- transform to byte arrays
tmp2 = toBytes(tmp2)
tmp = toBytes(tmp)
assert(#tmp == #tmp2)
-- apply XOR over bytes in both arrays
-- save results to final array
for j = 1, #tmp do
-- perform XOR operation on both elements in the respective arrays
tmp[j] = tmp[j] ~ tmp2[j]
end
-- transform back into byte string to pass to next hash
tmp = toString(tmp)
end
end
return tmp
end
local truncate = function(str, pos)
return string.sub(str, 1, pos)
end
local deriveKey = function(hFun, message, salt, c, dLen)
local hLen = hFun.outputSize
-- the derived key cannot be larger than (2^32 * hLen)
if dLen > (2^32) * hLen then error("The derived key cannot be larger than 2^32 times the output size of the hash function.") end
-- the block size is the desired key length divided by the output size of the underlying hash function, rounded up
local blockSize = ceil(dLen/hLen)
-- to store our blocks
local final = {}
for i = 1, blockSize do
-- lets make our blocks in here
final[i] = buildBlock(hFun, message, salt, c, i)
end
local result
if #final == 1 then
result = final[1] -- we only have one block
else
result = concat(final) -- turns final into a bytestring to be outputted
end
--if #result > dLen then truncate(final, dLen) end
assert(#result == dLen)
return asHex(result) -- outputs as a hex value
end
return {deriveKey = deriveKey}
end
end
This code is not getting the correct answers. Testing this code with test vectors provided here, assuming that the underlying PRF is HMAC-SHA256, the output is below:
key: "password"
salt: "salt"
c: 1
dkLen: 32
Got: 13463842ec330934dc124494b40d8baade465b72f3fcadad741f2d0e052fd2f5
Expected: 120fb6cffcf8b32c43e7225256c4f837a86548c92ccc35480805987cb70be17b
key: "password"
salt: "salt"
c: 2
dkLen: 32
Got: 8b82aed26f503effdbc6c14bc7f0338b2b90e387f14ac1f91f9ad74e618f9558
Expected: AE4D0C95AF6B46D32D0ADFF928F06DD02A303F8EF3C251DFD6E2D85A95474C43
I believe it may have something to do with the string to byte encoding, but I cannot pinpoint what exactly is causing the issue. When I was testing my HMAC code, I had to rely on online generators because I couldn't find vectors for HMAC-SHA224 and HMAC-SHA256. Some calculators would give me completely different output values for the same key, message combination. That could be because of how they are processing the inputs, but I am not sure. I would appreciate it if someone more experienced could help me out with this.
EDIT: This problem is solved. Seems that all that was needed was to pass int as a binary string of length 4. I updated the code with the fixes.
EDIT 2: I read the standard again to realize the solution was in my face the entire time (standard says to encode i as a 32-bit big endian integer).
The solution was to convert int to a binary string of length 4. Thanks to #EgorSkriptunoff for his insight.

Adaptation of SHA2 512 gives incorrect results

I am trying to adapt the pure Lua implementation of the SecureHashAlgorithm found here for SHA2 512 instead of SHA2 256. When I try to use the adaptation, it does not give the correct answer.
Here is the adaptation:
--
-- UTILITY FUNCTIONS
--
-- transform a string of bytes in a string of hexadecimal digits
local function str2hexa (s)
local h = string.gsub(s, ".", function(c)
return string.format("%02x", string.byte(c))
end)
return h
end
-- transforms number 'l' into a big-endian sequence of 'n' bytes
--(coded as a string)
local function num2string(l, n)
local s = ""
for i = 1, n do
--most significant byte of l
local remainder = l % 256
s = string.char(remainder) .. s
--remove from l the bits we have already transformed
l = (l-remainder) / 256
end
return s
end
-- transform the big-endian sequence of eight bytes starting at
-- index 'i' in 's' into a number
local function s264num (s, i)
local n = 0
for i = i, i + 7 do
n = n*256 + string.byte(s, i)
end
return n
end
--
-- MAIN SECTION
--
-- FIRST STEP: INITIALIZE HASH VALUES
--(second 32 bits of the fractional parts of the square roots of the first 9th through 16th primes 23..53)
local HH = {}
local function initH512(H)
H = {0x6a09e667f3bcc908, 0xbb67ae8584caa73b, 0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1, 0x510e527fade682d1, 0x9b05688c2b3e6c1f, 0x1f83d9abfb41bd6b, 0x5be0cd19137e2179}
return H
end
-- SECOND STEP: INITIALIZE ROUND CONSTANTS
--(first 80 bits of the fractional parts of the cube roots of the first 80 primes 2..409)
local k = {
0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc, 0x3956c25bf348b538,
0x59f111f1b605d019, 0x923f82a4af194f9b, 0xab1c5ed5da6d8118, 0xd807aa98a3030242, 0x12835b0145706fbe,
0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2, 0x72be5d74f27b896f, 0x80deb1fe3b1696b1, 0x9bdc06a725c71235,
0xc19bf174cf692694, 0xe49b69c19ef14ad2, 0xefbe4786384f25e3, 0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65,
0x2de92c6f592b0275, 0x4a7484aa6ea6e483, 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5, 0x983e5152ee66dfab,
0xa831c66d2db43210, 0xb00327c898fb213f, 0xbf597fc7beef0ee4, 0xc6e00bf33da88fc2, 0xd5a79147930aa725,
0x06ca6351e003826f, 0x142929670a0e6e70, 0x27b70a8546d22ffc, 0x2e1b21385c26c926, 0x4d2c6dfc5ac42aed,
0x53380d139d95b3df, 0x650a73548baf63de, 0x766a0abb3c77b2a8, 0x81c2c92e47edaee6, 0x92722c851482353b,
0xa2bfe8a14cf10364, 0xa81a664bbc423001, 0xc24b8b70d0f89791, 0xc76c51a30654be30, 0xd192e819d6ef5218,
0xd69906245565a910, 0xf40e35855771202a, 0x106aa07032bbd1b8, 0x19a4c116b8d2d0c8, 0x1e376c085141ab53,
0x2748774cdf8eeb99, 0x34b0bcb5e19b48a8, 0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb, 0x5b9cca4f7763e373,
0x682e6ff3d6b2b8a3, 0x748f82ee5defb2fc, 0x78a5636f43172f60, 0x84c87814a1f0ab72, 0x8cc702081a6439ec,
0x90befffa23631e28, 0xa4506cebde82bde9, 0xbef9a3f7b2c67915, 0xc67178f2e372532b, 0xca273eceea26619c,
0xd186b8c721c0c207, 0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178, 0x06f067aa72176fba, 0x0a637dc5a2c898a6,
0x113f9804bef90dae, 0x1b710b35131c471b, 0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc,
0x431d67c49c100d4c, 0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, 0x5fcb6fab3ad6faec, 0x6c44198c4a475817
}
-- THIRD STEP: PRE-PROCESSING (padding)
local function preprocess(toProcess, len)
--append a single '1' bit
--append K '0' bits, where K is the minimum number >= 0 such that L + 1 + K = 896mod1024
local extra = 128 - (len + 9) % 128
len = num2string(8 * len, 8)
toProcess = toProcess .. "\128" .. string.rep("\0", extra) .. len
assert(#toProcess % 128 == 0)
return toProcess
end
local function rrotate(rot, n)
return (rot >> n) | ((rot << 64 - n))
end
local function digestblock(msg, i, H)
local w = {}
for j = 1, 16 do w[j] = s264num(msg, i + (j - 1)*4) end
for j = 17, 80 do
local v = w[j - 15]
local s0 = rrotate(v, 1) ~ rrotate(v, 8) ~ (v >> 7)
v = w[j - 2]
w[j] = w[j - 16] + s0 + w[j - 7] + ((rrotate(v, 19) ~ rrotate(v, 61)) ~ (v >> 6))
end
local a, b, c, d, e, f, g, h = H[1], H[2], H[3], H[4], H[5], H[6], H[7], H[8]
for i = 1, 80 do
a, b, c, d, e, f, g, h = a , b , c , d , e , f , g , h
local s0 = rrotate(a, 28) ~ (rrotate(a, 34) ~ rrotate(a, 39))
local maj = ((a & b) ~ (a & c)) ~ (b & c)
local t2 = s0 + maj
local s1 = rrotate(e, 14) ~ (rrotate(e, 18) ~ rrotate(e, 41))
local ch = (e & f) ~ (~e & g)
local t1 = h + s1 + ch + k[i] + w[i]
h, g, f, e, d, c, b, a = g, f, e, d + t1, c, b, a, t1 + t2
end
H[1] = (H[1] + a)
H[2] = (H[2] + b)
H[3] = (H[3] + c)
H[4] = (H[4] + d)
H[5] = (H[5] + e)
H[6] = (H[6] + f)
H[7] = (H[7] + g)
H[8] = (H[8] + h)
end
local function finalresult512 (H)
-- Produce the final hash value:
return
str2hexa(num2string(H[1], 8)..num2string(H[2], 8)..num2string(H[3], 8)..num2string(H[4], 8)..
num2string(H[5], 8)..num2string(H[6], 8)..num2string(H[7], 8)..num2string(H[8], 8))
end
-- Returns the hash512 for the given string.
local function hash512 (msg)
msg = preprocess(msg, #msg)
local H = initH512(HH)
-- Process the message in successive 1024-bit (128 bytes) chunks:
for i = 1, #msg, 128 do
digestblock(msg, i, H)
end
return finalresult512(H)
end
Given hash512("a"):
Expect: 1f40fc92da241694750979ee6cf582f2d5d7d28e18335de05abc54d0560e0f5302860c652bf08d560252aa5e74210546f369fbbbce8c12cfc7957b2652fe9a75
Actual: e0b9623f2194cb81f2a62616a183edbe390be0d0b20430cadc3371efc237fa6bf7f8b48311f2fa249131c347fee3e8cde6acfdab286d648054541f92102cfc9c
I know that I am creating a message of the correct bit size (1024 bits) and also working in 1024-bit chunks, or at least I believe I am.
I am not sure if it has to do with the handling of the integers (the standard requires unsigned integers) or whether I made a mistake in one of the utility functions, or both. If it is indeed an issue with the handling of the integers, how would I go about taking care of the problem. I was able to resolve this when working on the 256-bit version of the adaptation by using mod 2^32 when working with numbers in the digestblock method. I attempted to do mod 2^64 and 2^63 with the 512-bit version and it does not correct the problem. I am stumped.
I should mention that I cannot use one of the many library implementations as I am using a sandboxed Lua that does not provide this access, which is why I need a pure lua implementation. Thanks in advance.
Unfortunately, after introducing integers in Lua 5.3 writing scripts for Lua becomes a more complicated task.
You must always think about transformations between integers and floating point numbers.
ALWAYS. Yes, that's boring.
One of your mistakes is an excellent example of this "dark corner of Lua".
local remainder = l % 256
s = string.char(remainder) .. s
--remove from l the bits we have already transformed
l = (l-remainder) / 256
Your value l is initially a 64-bit integer.
After cutting off its first byte l contains (64-8) = 56 bits, but now it's a floating point-number (with 53-bit precision, of course).
Possible solution: use l = l >> 8 or l = l // 256 instead of l = (l-remainder) / 256
Another mistake is using s264num(msg, i + (j - 1) * 4) instead of s264num(msg, i + (j - 1) * 8)
One more mistake is in the following line:
local extra = 128 - (len + 9) % 128
The correct code is
local extra = - (len + 17) % 128 + 8
(Please note that -a%m+b is not the same as b-a%m due to operator precedence)
After fixing these 3 mistakes your code works correctly.

Element by Element comparison in Lua

I'm trying to find a way to do element-by-element comparison in Lua using the standard < operator. For example, here's what I'd like to do:
a = {5, 7, 10}
b = {6, 4, 15}
c = a < b -- should return {true, false, true}
I already have code working for addition (and subtraction, multiplication, etc). My issue is that Lua forces the result of a comparison to a boolean. I don't want a boolean, I want a table as the result of the comparison.
Here is my code so far, with addition working, but less-than comparison not working:
m = {}
m['__add'] = function (a, b)
-- Add two tables together
-- Works fine
c = {}
for i = 1, #a do
c[i] = a[i] + b[i]
end
return c
end
m['__lt'] = function (a, b)
-- Should do a less-than operator on each element
-- Doesn't work, Lua forces result to boolean
c = {}
for i = 1, #a do
c[i] = a[i] < b[i]
end
return c
end
a = {5, 7, 10}
b = {6, 4, 15}
setmetatable(a, m)
c = a + b -- Expecting {11, 11, 25}
print(c[1], c[2], c[3]) -- Works great!
c = a < b -- Expecting {true, false, true}
print(c[1], c[2], c[3]) -- Error, lua makes c into boolean
The Lua programming manual says that the result of the __lt metamethod call is always converted to a boolean. My question is, how can I work around that? I heard that Lua is good for DSL, and I really need the syntax to work here. I think it should be possible using MetaLua, but I'm not really sure where to start.
A coworker suggested that I just use << instead with the __shl metamethod. I tried it and it works, but I really want to use < for less than, rather than a hack using the wrong symbol.
Thanks!
You only have two choices to make this work with your syntax:
Option 1: Patch the Lua core.
This is probably going to be very difficult, and it'll be a maintenance nightmare in the future. The biggest issue is that Lua assumes on a very low level that the comparison operators <, >, ==, ~= return a bool value.
The byte-code that Lua generates actually does a jump on any comparison. For example, something like c = 4 < 5 gets compiled to byte-code that looks much more like if (4 < 5) then c = true else c = false end.
You can see what the byte-code looks like with luac -l file.lua. If you compare the byte-code of c=4<5 with c=4+5 you'll see what I mean. The addition code is shorter and simpler. Lua assumes you'll do branching with comparisons, not assignment.
Option 2: Parse your code, change it, and run that
This is what I think you should do. It would be very hard, expect most of the work is already done for you (using something like LuaMinify).
First of all, write a function you can use for comparisons of anything. The idea here is to do your special comparison if it's a table, but fall back on using < for everything else.
my_less = function(a, b)
if (type(a) == 'table') then
c = {}
for i = 1, #a do
c[i] = a[i] < b[i]
end
return c
else
return a < b
end
end
Now all we need to do is replace every less than operator a<b with my_less(a,b).
Let's use the parser from LuaMinify. We'll call it with the following code:
local parse = require('ParseLua').ParseLua
local ident = require('FormatIdentity')
local code = "c=a*b<c+d"
local ret, ast = parse(code)
local _, f = ident(ast)
print(f)
All this will do is parse the code into a syntax tree, and then spit it back out again. We'll change FormatIdentity.lua to make it do the substitution. Replace the section near line 138 with the following code:
elseif expr.AstType == 'BinopExpr' then --line 138
if (expr.Op == '<') then
tok_it = tok_it + 1
out:appendStr('my_less(')
formatExpr(expr.Lhs)
out:appendStr(',')
formatExpr(expr.Rhs)
out:appendStr(')')
else
formatExpr(expr.Lhs)
appendStr( expr.Op )
formatExpr(expr.Rhs)
end
That's all there is to it. It will replace something like c=a*b<c+d with my_less(a*b,c+d). Just shove all your code through at runtime.
Comparisons in Lua return a boolean value.
There is nothing you can do about it short of changing the core of Lua.
Can you put up with a bit verbose v()-notation:
v(a < b) instead of a < b ?
local vec_mt = {}
local operations = {
copy = function (a, b) return a end,
lt = function (a, b) return a < b end,
add = function (a, b) return a + b end,
tostring = tostring,
}
local function create_vector_instance(operand1, operation, operand2)
local func, vec = operations[operation], {}
for k, elem1 in ipairs(operand1) do
local elem2 = operand2 and operand2[k]
vec[k] = func(elem1, elem2)
end
return setmetatable(vec, vec_mt)
end
local saved_result
function v(...) -- constructor for class "vector"
local result = ...
local tp = type(result)
if tp == 'boolean' and saved_result then
result, saved_result = saved_result
elseif tp ~= 'table' then
result = create_vector_instance({...}, 'copy')
end
return result
end
function vec_mt.__add(v1, v2)
return create_vector_instance(v1, 'add', v2)
end
function vec_mt.__lt(v1, v2)
saved_result = create_vector_instance(v1, 'lt', v2)
end
function vec_mt.__tostring(vec)
return
'Vector ('
..table.concat(create_vector_instance(vec, 'tostring'), ', ')
..')'
end
Usage:
a = v(5, 7, 10); print(a)
b = v(6, 4, 15); print(b)
c = a + b ; print(c) -- result is v(11, 11, 25)
c = v(a + b); print(c) -- result is v(11, 11, 25)
c = v(a < b); print(c) -- result is v(true, false, true)
As others have already mentioned, there is no straight-forward solution to this. However, with the use of a generic Python-like zip() function, such as the one shown below, you can simplify the problem, like so:
--------------------------------------------------------------------------------
-- Python-like zip() iterator
--------------------------------------------------------------------------------
function zip(...)
local arrays, ans = {...}, {}
local index = 0
return
function()
index = index + 1
for i,t in ipairs(arrays) do
if type(t) == 'function' then ans[i] = t() else ans[i] = t[index] end
if ans[i] == nil then return end
end
return table.unpack(ans)
end
end
--------------------------------------------------------------------------------
a = {5, 7, 10}
b = {6, 4, 15}
c = {}
for a,b in zip(a,b) do
c[#c+1] = a < b -- should return {true, false, true}
end
-- display answer
for _,v in ipairs(c) do print(v) end

Lua 3 point crossover help to start

I want to implement a 3 point crossover for genetic programming but I don't know how to do it and where to start.
My input is:
a = {(first pair), (second pair), ... etc.}
For example a = {(12345,67890), (09876,54321)} (those are numbers, not strings)
Output: Something like this:
Example: a_1 = {(12895), (67340)} also numbers.
Thanks for reply and sorry for my bad English.
Here is my quick implementation of k-point crossover for integers using mostly integer arithmetic. Starting with this, you can extend it to crossover your chromosomes of many pairs of integers using a loop.
math.randomseed(111)
-- math.randomseed(os.time())
a = 12345
b = 67890
len = 5 -- number of digits
function split(mum, dad, len, base)
local split = math.pow(base, math.random(len))
local son = math.floor(dad / split) * split + mum % split
local daughter = math.floor(mum / split) * split + dad % split
return son, daughter
end
function kpoint(mum, dad, len, base, k)
for i=1, k do
mum, dad = split(mum, dad, len, base)
end
return mum, dad
end
s, d = kpoint(a, b, len, 10, 3) -- 3 point crossover in base 10
print(s) -- 67395
print(d) -- 12840
-- binary, (crossover binary representation)
s, d = kpoint(tonumber("10001", 2), tonumber("10110", 2), 5, 2, 3)
print(s) -- 23 which is (10111) in base 2
print(d) -- 16 which is (10000) in base 2
-- binary, (crossover base 10, but interpret as binary)
s, d = kpoint(1101, 1010, 4, 10, 3)
print(s) -- 1001
print(d) -- 1110

How to make LPeg.match return nil

I'm currently getting familiar with the LPeg parser module. For this I want to match a version string (e.g. 11.4) against a list.
Such a list is a string with a tight syntax that can also contain ranges. Here is an EBNF-like, but in any case quite simple grammar (I write it down because LPeg code below can be a bit difficult to read):
S = R, { ',', R }
R = N, [ '-', N ]
N = digit+, [ '.', digit+ ]
An example string would be 1-9,10.1-11,12. Here is my enormous code:
local L = require "lpeg"
local LV, LP, LC, LR, floor = L.V, L.P, L.C, L.R, math.floor
local version = "7.25"
local function check(a, op, b)
if op and a+0 <= version and version <= b+0 then
return a..op..b -- range
elseif not op and floor(version) == floor(a+0) then
return a -- single item
end
end
local grammar = LP({ "S",
S = LV"R" * (LP"," * LV"R")^0,
R = LV"V" * (LC(LP"-") * LV"V")^-1 / check,
V = LC(LV"D" * (LP"." * LV"D")^-1),
D = (LR("09")^1),
})
function checkversion(str)
return grammar:match(str)
end
So you would call it like checkversion("1-7,8.1,8.3,9") and if the current version is not matched by the list you should get nil.
Now, the trouble is, if all calls to check return nothing (meaning, if the versions do not match), grammar:match(...) will actually have no captures and so return the current position of the string. But this is exactly what I do not want, I want checkversion to return nil or false if there is no match and something that evaluates to true otherwise, actually just like string:match would do.
If I on the other hand return false or nil from check in case of a non-match, I end up with return values from match like nil, "1", nil, nil which is basically impossible to handle.
Any ideas?
I think you can or + it with a constant capture of nil:
grammar = grammar + lpeg.Cc(nil)
This is the pattern I eventually used:
nil_capturing_pattern * lpeg.Cc(nil)
I incorporated it into the grammar in the S rule (Note that this also includes changed grammar to "correctly" determine version order, since in version numbering "4.7" < "4.11" is true, but not in calculus)
local Minor_mag = log10(Minor);
local function check(a, am, op, b, bm)
if op then
local mag = floor(max(log10(am), log10(bm), Minor_mag, 1))+1;
local a, b, v = a*10^mag+am, b*10^mag+bm, Major*10^mag+Minor;
if a <= v and v <= b then
return a..op..b;
end
elseif a == Major and (am == "0" or am == Minor) then
return a.."."..am;
end
end
local R, V, C, Cc = lpeg.R, lpeg.V, lpeg.C, lpeg.Cc
local g = lpeg.P({ "S",
S = V("R") * ("," * V("R"))^0 * Cc(nil),
R = (V("Vm") + V("VM")) * (C("-") * (V("Vm") + V("VM")))^-1 / check,
VM = V("D") * Cc("0"),
Vm = V("D") * "." * V("D"),
D = C(R("09")^1),
});
Multiple returns from match are not impossible to handle, if you catch them in a way that makes handling them easier. I added a function matched that does that, and added the fallback return of false to your check.
do
local L = require "lpeg"
local LV, LP, LC, LR, floor = L.V, L.P, L.C, L.R, math.floor
local version = 6.25
local function check(a, op, b)
if op and a+0 <= version and version <= b+0 then
return a..op..b -- range
elseif not op and floor(version) == floor(a+0) then
return a -- single item
end
return false
end
local grammar = LP({ "S",
S = LV"R" * (LP"," * LV"R")^0,
R = LV"V" * (LC(LP"-") * LV"V")^-1 / check,
V = LC(LV"D" * (LP"." * LV"D")^-1),
D = (LR("09")^1),
})
local function matched(...)
local n = select('#',...)
if n == 0 then return false end
for i=1,n do
if select(i,...) then return true end
end
return false
end
function checkversion(ver,str)
version = ver
return matched(grammar:match(str))
end
end
I enclosed the whole thing in do ... end so that the local version which is used here as an upvalue to check would have constrained scope, and added a parameter to checversion() to make it clearer to run through few test cases. For example:
cases = { 1, 6.25, 7.25, 8, 8.5, 10 }
for _,v in ipairs(cases) do
print(v, checkversion(v, "1-7,8.1,8.3,9"))
end
When run, I get:
C:\Users\Ross\Documents\tmp\SOQuestions>q18793493.lua
1 true
6.25 true
7.25 false
8 true
8.5 true
10 false
C:\Users\Ross\Documents\tmp\SOQuestions>
Note that either nil or false would work equally well in this case. It just feels saner to have collected a list that can be handled as a normal Lua array-like table without concern for the holes.

Resources