Module:VerseCalculations

From Psalms: Layer by Layer
Jump to: navigation, search

Documentation for this module may be created at Module:VerseCalculations/doc

local p = {}

-- Function to process verse numbers and adjust for letter suffixes
local function normalizeV(verse)
    -- Extract the base number and optional letter suffix
    local baseNumber, letters = verse:match("^(%d+)([a-z]*)$")

    if baseNumber then
        baseNumber = tonumber(baseNumber) or 0  -- Convert base number to integer
        if letters ~= "" then
            local letterValue = 0
            for i = 1, #letters do
                local char = letters:sub(i, i)
                letterValue = letterValue + (char:byte() - string.byte("a") + 1) * (0.1 ^ i)  -- Scale each letter's value
            end
            baseNumber = baseNumber + letterValue
        end
        return baseNumber
    end

    return "Error: Invalid verse format"
end

function p.normalizeVerse(frame)
	local verse = nil
	if frame.args then 
		verse = frame.args[1]
	end
	if not verse then
		verse = frame
	end
	
	return normalizeV(verse)
end
function p.normalizeVerse2(frame)
	local verse = nil
	if frame.args then 
		verse = frame.args[1]
	end
	if not verse then
		verse = frame
	end
	
	return normalizeV(verse) * 10
end

function p.rangeMinimum(frame)
    -- Ensure input is a string
    local input = frame.args[1] or ""
    
    -- Normalize and clean the input string
    local cleanInput = input:lower()
        :gsub("vv%.?", "")   -- Remove "vv."
        :gsub("v%.?", "")    -- Remove "v."
        :gsub("verses", "")  -- Remove "verses"
        :gsub("verse", "")   -- Remove "verse" (handles singular form)
        :gsub("%s+", " ")    -- Normalize spaces
        :gsub("^%s*(.-)%s*$", "%1")  -- Trim leading/trailing spaces

    -- Match different range formats (supports multi-digit numbers and suffixes like "123b")
    local startVerse, endVerse = cleanInput:match("(%d+%a*)%s*-%s*(%d+%a*)")  -- Handles "100-123b"

    if not startVerse then
        startVerse, endVerse = cleanInput:match("(%d+%a*)%s+to%s+(%d+%a*)")  -- Handles "3 to 5"
    end
    
    -- Handle single verse (e.g., "verse 3", "v. 100")
    if not startVerse then
        startVerse = cleanInput:match("(%d+%a*)")
        return normalizeV(startVerse)
        -- endVerse = startVerse  -- Single verse means start = end
    end

    -- Return extracted values or error if no match
    if startVerse and endVerse then
        return normalizeV(startVerse)
    else
        return "Error: Could not extract range"
    end
end

function p.rangeMaximum(frame)
    -- Ensure input is a string
    local input = frame.args[1] or ""
    
    -- Normalize and clean the input string
    local cleanInput = input:lower()
        :gsub("vv%.?", "")   -- Remove "vv."
        :gsub("v%.?", "")    -- Remove "v."
        :gsub("verses", "")  -- Remove "verses"
        :gsub("verse", "")   -- Remove "verse" (handles singular form)
        :gsub("%s+", " ")    -- Normalize spaces
        :gsub("^%s*(.-)%s*$", "%1")  -- Trim leading/trailing spaces

    -- Match different range formats (supports multi-digit numbers and suffixes like "123b")
    local startVerse, endVerse = cleanInput:match("(%d+%a*)%s*-%s*(%d+%a*)")  -- Handles "100-123b"

    if not startVerse then
        startVerse, endVerse = cleanInput:match("(%d+%a*)%s+to%s+(%d+%a*)")  -- Handles "3 to 5"
    end
    
    -- Handle single verse (e.g., "verse 3", "v. 100")
    if not startVerse then
        startVerse = cleanInput:match("(%d+%a*)")
        endVerse = startVerse  -- Single verse means start = end
    end

    -- Return extracted values or error if no match
    if endVerse then
        return normalizeV(endVerse)
    else
        return "Error: Could not extract range"
    end
end


function p.expandVerses(frame)
    local input = frame.args[1] or ""
    local output = {}
    local seen = {}

    -- Remove common verse prefixes
    input = input:gsub("^[Vv][Vv]?%.?%s*", "")
    input = input:gsub("^[Vv]erse?s?%s*", "")
    input = input:gsub("%s+and%s+", ", ")

    for part in input:gmatch("[^,]+") do
        part = part:match("^%s*(.-)%s*$") -- Trim whitespace

        -- Handle range (e.g. "44a-45b")
        local start_v, end_v = part:match("^(%d+)[a-zA-Z]*%s*%-%s*(%d+)[a-zA-Z]*$")
        if start_v and end_v then
            start_v, end_v = tonumber(start_v), tonumber(end_v)
            if start_v and end_v then
                for i = start_v, end_v do
                    if not seen[i] then
                        table.insert(output, i)
                        seen[i] = true
                    end
                end
            end
        else
            -- Single value: extract just the number part
            local num = part:match("^(%d+)")
            num = tonumber(num)
            if num and not seen[num] then
                table.insert(output, num)
                seen[num] = true
            end
        end
    end

    return table.concat(output, ", ")
end

return p