Przejdź do zawartości

Moduł:odmiana-rzeczownik-kirgiski

Z Wikisłownika – wolnego słownika wielojęzycznego

Moduł wykorzystywany przez szablon {{odmiana-rzeczownik-kirgiski}}.


local p = {}

local KYRGYZ_VOICELESS_CONSONANTS = 'пфктшсхцчщ'  -- voiceless consonants
local KYRGYZ_VOICED_CONSONANTS = 'бвгджзлмнрй'    -- voiced consonants  
local KYRGYZ_VOWELS = 'аыоуэеиөү'

endings = require('Moduł:odmiana-rzeczownik-kirgiski/końcówki')

--[[    Helper functions    ]]

x_in_y = function(x, y, on_true, on_false)
    if not y then return on_false or false end
    for _, n in pairs(y) do
        if x == n then
            return on_true or true
        end
    end
    
    return on_false or false
end

ifexpr = function(cond, on_true, on_false)
    if cond then
        return on_true
    end
    
    return on_false
end

is_string_empty = function(x)
    return x == nil or x == ''
end

ends_with = function(str, pattern)
    if is_string_empty(str) or is_string_empty(pattern) then
        return false
    end
    return mw.ustring.sub(str, -#pattern) == pattern
end

--[[    Other helper functions to construct a suffix    ]]

is_vowel = function(letter)
    if is_string_empty(letter) then
        return false
    end
    return mw.ustring.find(KYRGYZ_VOWELS, letter, 1, true) ~= nil
end

is_voiceless_consonant = function(letter)
    if is_string_empty(letter) then
        return false
    end
    return mw.ustring.find(KYRGYZ_VOICELESS_CONSONANTS, letter, 1, true) ~= nil
end

is_voiced_consonant = function(letter)
    if is_string_empty(letter) then
        return false
    end
    return mw.ustring.find(KYRGYZ_VOICED_CONSONANTS, letter, 1, true) ~= nil
end

get_last_letter_type = function(stem)
    if is_string_empty(stem) then
        return 'V' -- default to vowel type
    end
    
    local final_letter = mw.ustring.sub(stem, -1)
    
    if is_vowel(final_letter) then
        return 'V'
    elseif is_voiceless_consonant(final_letter) then
        return 'T' -- T for voiceless
    elseif is_voiced_consonant(final_letter) then
        return 'D' -- D for voiced
    end
    
    return 'V' -- default to vowel type for other characters
end

get_last_vowel_of_stem = function(stem)
    if is_string_empty(stem) then
        return 'а' -- default vowel
    end
    
    for i = -1,-#stem,-1 do
        local letter = mw.ustring.sub(stem, i, i)
        if is_vowel(letter) then
            return letter
        end
    end
    
    return 'а' -- default vowel if no vowel found
end

get_vowel_harmony = function(letter)
    if not is_vowel(letter) then
        return 'а' -- default vowel
    end
    
    local types = {
        ['а'] = 'а',
        ['ы'] = 'ы', 
        ['о'] = 'о',
        ['у'] = 'у',
        ['э'] = 'э',
        ['е'] = 'е',
        ['и'] = 'и',
        ['ө'] = 'ө',
        ['ү'] = 'ү'
    }
    
    return types[letter] or 'а'
end

-- Parameter [1] is used only for singular possessive forms

get_plural_suffix = function(letter, stem_type)
    if stem_type == 'V' or stem_type == 'й' or stem_type == 'р' then
        local plural_map = {
            ['а'] = 'лар',
            ['ы'] = 'лар',
            ['о'] = 'лор',
            ['у'] = 'лар',
            ['э'] = 'лер',
            ['е'] = 'лер', 
            ['и'] = 'лер',
            ['ө'] = 'лөр',
            ['ү'] = 'лөр'
        }
        return plural_map[letter] or 'лар'
    elseif stem_type == 'D' then
        local plural_map = {
            ['а'] = 'дар',
            ['ы'] = 'дар',
            ['о'] = 'дор',
            ['у'] = 'дар',
            ['э'] = 'дер',
            ['е'] = 'дер', 
            ['и'] = 'дер',
            ['ө'] = 'дөр',
            ['ү'] = 'дөр'
        }
        return plural_map[letter] or 'дар'
    else -- 'T' voiceless
        local plural_map = {
            ['а'] = 'тар',
            ['ы'] = 'тар',
            ['о'] = 'тор',
            ['у'] = 'тар',
            ['э'] = 'тер',
            ['е'] = 'тер', 
            ['и'] = 'тер',
            ['ө'] = 'төр',
            ['ү'] = 'төр'
        }
        return plural_map[letter] or 'тар'
    end
end

get_genitive_suffix = function(letter, stem_type)
    if stem_type == 'V' then
        local genitive_map = {
            ['а'] = 'нын',
            ['ы'] = 'нын',
            ['о'] = 'нун',
            ['у'] = 'нун',
            ['э'] = 'нин',
            ['е'] = 'нин',
            ['и'] = 'нин',
            ['ө'] = 'нүн',
            ['ү'] = 'нүн'
        }
        return genitive_map[letter] or 'нын'
    elseif stem_type == 'D' or stem_type == 'й' or stem_type == 'р' then
        local genitive_map = {
            ['а'] = 'дын',
            ['ы'] = 'дын',
            ['о'] = 'дун',
            ['у'] = 'дун',
            ['э'] = 'дин',
            ['е'] = 'дин',
            ['и'] = 'дин',
            ['ө'] = 'дүн',
            ['ү'] = 'дүн'
        }
        return genitive_map[letter] or 'дын'
    else -- 'T' voiceless
        local genitive_map = {
            ['а'] = 'тын',
            ['ы'] = 'тын',
            ['о'] = 'тун',
            ['у'] = 'тун',
            ['э'] = 'тин',
            ['е'] = 'тин',
            ['и'] = 'тин',
            ['ө'] = 'түн',
            ['ү'] = 'түн'
        }
        return genitive_map[letter] or 'тын'
    end
end

get_genitive_plural_suffix = function(letter, stem_type)
    if stem_type == 'V' or stem_type == 'й' or stem_type == 'р' then
        local genitive_map = {
            ['а'] = 'дын',
            ['ы'] = 'дын',
            ['о'] = 'дун',
            ['у'] = 'дын',
            ['э'] = 'дин',
            ['е'] = 'дин',
            ['и'] = 'дин',
            ['ө'] = 'дүн',
            ['ү'] = 'дүн'
        }
        return genitive_map[letter] or 'дын'
    else
        local genitive_map = {
            ['а'] = 'дын',
            ['ы'] = 'дын',
            ['о'] = 'дун',
            ['у'] = 'дын',
            ['э'] = 'дин',
            ['е'] = 'дин',
            ['и'] = 'дин',
            ['ө'] = 'дүн',
            ['ү'] = 'дүн'
        }
        return genitive_map[letter] or 'дын'
    end
end

get_dative_suffix = function(letter, stem_type)
    if stem_type == 'V' or stem_type == 'й' or stem_type == 'р' then
        local dative_map = {
            ['а'] = 'га',
            ['ы'] = 'га',
            ['о'] = 'го',
            ['у'] = 'га',
            ['э'] = 'ге',
            ['е'] = 'ге',
            ['и'] = 'ге',
            ['ө'] = 'гө',
            ['ү'] = 'гө'
        }
        return dative_map[letter] or 'га'
    elseif stem_type == 'D' then
        local dative_map = {
            ['а'] = 'га',
            ['ы'] = 'га',
            ['о'] = 'го',
            ['у'] = 'га',
            ['э'] = 'ге',
            ['е'] = 'ге',
            ['и'] = 'ге',
            ['ө'] = 'гө',
            ['ү'] = 'гө'
        }
        return dative_map[letter] or 'га'
    else -- 'T' voiceless
        local dative_map = {
            ['а'] = 'ка',
            ['ы'] = 'ка',
            ['о'] = 'ко',
            ['у'] = 'ка',
            ['э'] = 'ке',
            ['е'] = 'ке',
            ['и'] = 'ке',
            ['ө'] = 'кө',
            ['ү'] = 'кө'
        }
        return dative_map[letter] or 'ка'
    end
end

get_dative_plural_suffix = function(letter, stem_type)
    if stem_type == 'V' or stem_type == 'й' or stem_type == 'р' then
        local dative_map = {
            ['а'] = 'га',
            ['ы'] = 'га',
            ['о'] = 'го',
            ['у'] = 'га',
            ['э'] = 'ге',
            ['е'] = 'ге',
            ['и'] = 'ге',
            ['ө'] = 'гө',
            ['ү'] = 'гө'
        }
        return dative_map[letter] or 'га'
    else
        local dative_map = {
            ['а'] = 'га',
            ['ы'] = 'га',
            ['о'] = 'го',
            ['у'] = 'га',
            ['э'] = 'ге',
            ['е'] = 'ге',
            ['и'] = 'ге',
            ['ө'] = 'гө',
            ['ү'] = 'гө'
        }
        return dative_map[letter] or 'га'
    end
end

get_accusative_suffix = function(letter, stem_type)
    if stem_type == 'V' then
        local accusative_map = {
            ['а'] = 'ны',
            ['ы'] = 'ны',
            ['о'] = 'ну',
            ['у'] = 'ну',
            ['э'] = 'ни',
            ['е'] = 'ни',
            ['и'] = 'ни',
            ['ө'] = 'нү',
            ['ү'] = 'нү'
        }
        return accusative_map[letter] or 'ны'
    elseif stem_type == 'D' or stem_type == 'й' or stem_type == 'р' then
        local accusative_map = {
            ['а'] = 'ды',
            ['ы'] = 'ды',
            ['о'] = 'ду',
            ['у'] = 'ду',
            ['э'] = 'ди',
            ['е'] = 'ди',
            ['и'] = 'ди',
            ['ө'] = 'дү',
            ['ү'] = 'дү'
        }
        return accusative_map[letter] or 'ды'
    else -- 'T' voiceless
        local accusative_map = {
            ['а'] = 'ты',
            ['ы'] = 'ты',
            ['о'] = 'ту',
            ['у'] = 'ту',
            ['э'] = 'ти',
            ['е'] = 'ти',
            ['и'] = 'ти',
            ['ө'] = 'тү',
            ['ү'] = 'тү'
        }
        return accusative_map[letter] or 'ты'
    end
end

get_accusative_plural_suffix = function(letter, stem_type)
    if stem_type == 'V' or stem_type == 'й' or stem_type == 'р' then
        local accusative_map = {
            ['а'] = 'ды',
            ['ы'] = 'ды',
            ['о'] = 'ду',
            ['у'] = 'ду',
            ['э'] = 'ди',
            ['е'] = 'ди',
            ['и'] = 'ди',
            ['ө'] = 'дү',
            ['ү'] = 'дү'
        }
        return accusative_map[letter] or 'ды'
    else
        local accusative_map = {
            ['а'] = 'ды',
            ['ы'] = 'ды',
            ['о'] = 'ду',
            ['у'] = 'ды',
            ['э'] = 'ди',
            ['е'] = 'ди',
            ['и'] = 'ди',
            ['ө'] = 'дү',
            ['ү'] = 'дү'
        }
        return accusative_map[letter] or 'ды'
    end
end

get_locative_suffix = function(letter, stem_type)
    if stem_type == 'V' or stem_type == 'й' or stem_type == 'р' then
        local locative_map = {
            ['а'] = 'да',
            ['ы'] = 'да',
            ['о'] = 'до',
            ['у'] = 'до',
            ['э'] = 'де',
            ['е'] = 'де',
            ['и'] = 'де',
            ['ө'] = 'дө',
            ['ү'] = 'дө'
        }
        return locative_map[letter] or 'да'
    elseif stem_type == 'D' then
        local locative_map = {
            ['а'] = 'да',
            ['ы'] = 'да',
            ['о'] = 'до',
            ['у'] = 'да',
            ['э'] = 'де',
            ['е'] = 'де',
            ['и'] = 'де',
            ['ө'] = 'дө',
            ['ү'] = 'дө'
        }
        return locative_map[letter] or 'да'
    else -- 'T' voiceless
        local locative_map = {
            ['а'] = 'та',
            ['ы'] = 'та',
            ['о'] = 'то',
            ['у'] = 'та',
            ['э'] = 'те',
            ['е'] = 'те',
            ['и'] = 'те',
            ['ө'] = 'тө',
            ['ү'] = 'тө'
        }
        return locative_map[letter] or 'та'
    end
end

get_locative_plural_suffix = function(letter, stem_type)
    if stem_type == 'V' or stem_type == 'й' or stem_type == 'р' then
        local locative_map = {
            ['а'] = 'да',
            ['ы'] = 'да',
            ['о'] = 'до',
            ['у'] = 'да',
            ['э'] = 'де',
            ['е'] = 'де',
            ['и'] = 'де',
            ['ө'] = 'дө',
            ['ү'] = 'дө'
        }
        return locative_map[letter] or 'да'
    else
        local locative_map = {
            ['а'] = 'да',
            ['ы'] = 'да',
            ['о'] = 'до',
            ['у'] = 'да',
            ['э'] = 'де',
            ['е'] = 'де',
            ['и'] = 'де',
            ['ө'] = 'дө',
            ['ү'] = 'дө'
        }
        return locative_map[letter] or 'да'
    end
end

get_ablative_suffix = function(letter, stem_type)
    if stem_type == 'V' or stem_type == 'й' or stem_type == 'р' then
        local ablative_map = {
            ['а'] = 'дан',
            ['ы'] = 'дан',
            ['о'] = 'дон',
            ['у'] = 'дан',
            ['э'] = 'ден',
            ['е'] = 'ден',
            ['и'] = 'ден',
            ['ө'] = 'дөн',
            ['ү'] = 'дөн'
        }
        return ablative_map[letter] or 'дан'
    elseif stem_type == 'D' then
        local ablative_map = {
            ['а'] = 'дан',
            ['ы'] = 'дан',
            ['о'] = 'дон',
            ['у'] = 'дан',
            ['э'] = 'ден',
            ['е'] = 'ден',
            ['и'] = 'ден',
            ['ө'] = 'дөн',
            ['ү'] = 'дөн'
        }
        return ablative_map[letter] or 'дан'
    else -- 'T' voiceless
        local ablative_map = {
            ['а'] = 'тан',
            ['ы'] = 'тан',
            ['о'] = 'тон',
            ['у'] = 'тан',
            ['э'] = 'тен',
            ['е'] = 'тен',
            ['и'] = 'тен',
            ['ө'] = 'төн',
            ['ү'] = 'төн'
        }
        return ablative_map[letter] or 'тан'
    end
end

get_ablative_plural_suffix = function(letter, stem_type)
    if stem_type == 'V' or stem_type == 'й' or stem_type == 'р' then
        local ablative_map = {
            ['а'] = 'дан',
            ['ы'] = 'дан',
            ['о'] = 'дон',
            ['у'] = 'дан',
            ['э'] = 'ден',
            ['е'] = 'ден',
            ['и'] = 'ден',
            ['ө'] = 'дөн',
            ['ү'] = 'дөн'
        }
        return ablative_map[letter] or 'дан'
    else
        local ablative_map = {
            ['а'] = 'дан',
            ['ы'] = 'дан',
            ['о'] = 'дон',
            ['у'] = 'дан',
            ['э'] = 'ден',
            ['е'] = 'ден',
            ['и'] = 'ден',
            ['ө'] = 'дөн',
            ['ү'] = 'дөн'
        }
        return ablative_map[letter] or 'дан'
    end
end

p.get_buffer_letter = function(letter_table, class, default)
    return letter_table[class] or default or ''
end

p.has_dropping_vowel = function(x, y)
    if is_string_empty(x) or is_string_empty(y) or x == y then
        return false
    end
    
    local length = ifexpr(
        mw.ustring.len(x) < mw.ustring.len(y), 
        mw.ustring.len(x), 
        mw.ustring.len(y))
    
    for i=1,length do
        local fx = mw.ustring.sub(x, i, i)
        local fy = mw.ustring.sub(y, i, i)
        
        if fx ~= fy then
            return is_vowel(fx)    
        end
    end
    
    return false
end

construct_noun_ending = function(stem, pattern, ending_vowel, final_stem_class, is_possessive, is_plural_possessive)
    local ending = pattern
    local last_vowel = ending_vowel or get_last_vowel_of_stem(stem)
    local stem_type = final_stem_class or get_last_letter_type(stem)
    
    local special_chars = {
        ['{Vh}'] = function()
            return get_vowel_harmony(last_vowel)
        end,
        ['{P}'] = function()
            return get_plural_suffix(last_vowel, stem_type)
        end,
        ['{N}'] = function()
            return get_genitive_suffix(last_vowel, stem_type)
        end,
        ['{N2}'] = function()
            return get_genitive_plural_suffix(last_vowel, stem_type)
        end,
        ['{Da}'] = function()
            return get_dative_suffix(last_vowel, stem_type)
        end,
        ['{Da2}'] = function()
            return get_dative_plural_suffix(last_vowel, stem_type)
        end,
        ['{A}'] = function()
            return get_accusative_suffix(last_vowel, stem_type)
        end,
        ['{A2}'] = function()
            return get_accusative_plural_suffix(last_vowel, stem_type)
        end,
        ['{L}'] = function()
            return get_locative_suffix(last_vowel, stem_type)
        end,
        ['{L2}'] = function()
            return get_locative_plural_suffix(last_vowel, stem_type)
        end,
        ['{Ab}'] = function()
            return get_ablative_suffix(last_vowel, stem_type)
        end,
        ['{Ab2}'] = function()
            return get_ablative_plural_suffix(last_vowel, stem_type)
        end,
        -- Singular possessive - use the current stem (which could be parameter [1])
        ['{Ps1sg}'] = function()
            if stem_type == 'V' then
                return 'м'
            else
                local vowel = get_vowel_harmony(last_vowel)
                local third_person_map = {
                    ['а'] = 'ы',
                    ['ы'] = 'ы',
                    ['о'] = 'у',
                    ['у'] = 'у',
                    ['э'] = 'и',
                    ['е'] = 'и',
                    ['и'] = 'и',
                    ['ө'] = 'ү',
                    ['ү'] = 'ү'
                }
                return third_person_map[vowel] .. 'м'
            end
        end,
        ['{Ps2sg}'] = function()
            if stem_type == 'V' then
                return 'ң'
            else
                local vowel = get_vowel_harmony(last_vowel)
                local third_person_map = {
                    ['а'] = 'ы',
                    ['ы'] = 'ы',
                    ['о'] = 'у',
                    ['у'] = 'у',
                    ['э'] = 'и',
                    ['е'] = 'и',
                    ['и'] = 'и',
                    ['ө'] = 'ү',
                    ['ү'] = 'ү'
                }
                return third_person_map[vowel] .. 'ң'
            end
        end,
        ['{Ps2sgf}'] = function()
            if stem_type == 'V' then
                local vowel = get_vowel_harmony(last_vowel)
                local formal_map = {
                    ['а'] = 'ңыз',
                    ['ы'] = 'ңыз',
                    ['о'] = 'ңуз',
                    ['у'] = 'ңуз',
                    ['э'] = 'ңиз',
                    ['е'] = 'ңиз',
                    ['и'] = 'ңиз',
                    ['ө'] = 'ңүз',
                    ['ү'] = 'ңүз'
                }
                return formal_map[vowel] or 'ңыз'
            else
                local vowel = get_vowel_harmony(last_vowel)
                local possessive_vowel_map = {
                    ['а'] = 'ы',
                    ['ы'] = 'ы',
                    ['о'] = 'у',
                    ['у'] = 'у',
                    ['э'] = 'и',
                    ['е'] = 'и',
                    ['и'] = 'и',
                    ['ө'] = 'ү',
                    ['ү'] = 'ү'
                }
                local formal_map = {
                    ['а'] = 'ңыз',
                    ['ы'] = 'ңыз',
                    ['о'] = 'ңуз',
                    ['у'] = 'ңуз',
                    ['э'] = 'ңиз',
                    ['е'] = 'ңиз',
                    ['и'] = 'ңиз',
                    ['ө'] = 'ңүз',
                    ['ү'] = 'ңүз'
                }
                return possessive_vowel_map[vowel] .. (formal_map[vowel] or 'ңыз')
            end
        end,
        ['{Ps3sg}'] = function()
            if stem_type == 'V' then
                -- For vowel-ending stems: -сы/-си/-су/-сү
                local vowel = get_vowel_harmony(last_vowel)
                local third_person_map = {
                    ['а'] = 'сы',
                    ['ы'] = 'сы',
                    ['о'] = 'су',
                    ['у'] = 'су',
                    ['э'] = 'си',
                    ['е'] = 'си',
                    ['и'] = 'си',
                    ['ө'] = 'сү',
                    ['ү'] = 'сү'
                }
                return third_person_map[vowel] or 'сы'
            else
                local vowel = get_vowel_harmony(last_vowel)
                local third_person_map = {
                    ['а'] = 'ы',
                    ['ы'] = 'ы',
                    ['о'] = 'у',
                    ['у'] = 'у',
                    ['э'] = 'и',
                    ['е'] = 'и',
                    ['и'] = 'и',
                    ['ө'] = 'ү',
                    ['ү'] = 'ү'
                }
                return third_person_map[vowel] or 'ы'
            end
        end,
        ['{Ps1pl}'] = function()
            if stem_type == 'V' then
                local vowel = get_vowel_harmony(last_vowel)
                local plural_map = {
                    ['а'] = 'быз',
                    ['ы'] = 'быз',
                    ['о'] = 'буз',
                    ['у'] = 'буз',
                    ['э'] = 'биз',
                    ['е'] = 'биз',
                    ['и'] = 'биз',
                    ['ө'] = 'бүз',
                    ['ү'] = 'бүз'
                }
                return plural_map[vowel] or 'быз'
            else
                local vowel = get_vowel_harmony(last_vowel)
                local possessive_vowel_map = {
                    ['а'] = 'ы',
                    ['ы'] = 'ы',
                    ['о'] = 'у',
                    ['у'] = 'у',
                    ['э'] = 'и',
                    ['е'] = 'и',
                    ['и'] = 'и',
                    ['ө'] = 'ү',
                    ['ү'] = 'ү'
                }
                local plural_map = {
                    ['а'] = 'быз',
                    ['ы'] = 'быз',
                    ['о'] = 'буз',
                    ['у'] = 'буз',
                    ['э'] = 'биз',
                    ['е'] = 'биз',
                    ['и'] = 'биз',
                    ['ө'] = 'бүз',
                    ['ү'] = 'бүз'
                }
                return (possessive_vowel_map[vowel] or 'ы') .. (plural_map[vowel] or 'быз')
            end
        end,
        ['{Ps2pl}'] = function()
            if stem_type == 'V' then
                local vowel = get_vowel_harmony(last_vowel)
                local plural_map = {
                    ['а'] = 'ңар',
                    ['ы'] = 'ңар',
                    ['о'] = 'ңор',
                    ['у'] = 'ңар',
                    ['э'] = 'ңер',
                    ['е'] = 'ңeр',
                    ['и'] = 'ңер',
                    ['ө'] = 'ңөр',
                    ['ү'] = 'ңөр'
                }
                return plural_map[vowel] or 'ңар'
            else
                local vowel = get_vowel_harmony(last_vowel)
                local possessive_vowel_map = {
                    ['а'] = 'ы',
                    ['ы'] = 'ы',
                    ['о'] = 'у',
                    ['у'] = 'у',
                    ['э'] = 'и',
                    ['е'] = 'и',
                    ['и'] = 'и',
                    ['ө'] = 'ү',
                    ['ү'] = 'ү'
                }
                local plural_map = {
                    ['а'] = 'ңар',
                    ['ы'] = 'ңар',
                    ['о'] = 'ңар',
                    ['у'] = 'ңар',
                    ['э'] = 'ңер',
                    ['е'] = 'ңер',
                    ['и'] = 'ңер',
                    ['ө'] = 'ңөр',
                    ['ү'] = 'ңөр'
                }
                return (possessive_vowel_map[vowel] or 'ы') .. (plural_map[vowel] or 'ңар')
            end
        end,
        ['{Ps2plf}'] = function()
            if stem_type == 'V' then
                local vowel = get_vowel_harmony(last_vowel)
                local formal_map = {
                    ['а'] = 'ңыздар',
                    ['ы'] = 'ңыздар',
                    ['о'] = 'ңуздар',
                    ['у'] = 'ңуздар',
                    ['э'] = 'ңиздер',
                    ['е'] = 'ңиздер',
                    ['и'] = 'ңиздер',
                    ['ө'] = 'ңүздөр',
                    ['ү'] = 'ңүздөр'
                }
                return formal_map[vowel] or 'ңыздар'
            else
                local vowel = get_vowel_harmony(last_vowel)
                local possessive_vowel_map = {
                    ['а'] = 'ы',
                    ['ы'] = 'ы',
                    ['о'] = 'у',
                    ['у'] = 'у',
                    ['э'] = 'и',
                    ['е'] = 'и',
                    ['и'] = 'и',
                    ['ө'] = 'ү',
                    ['ү'] = 'ү'
                }
                local formal_map = {
                    ['а'] = 'ңыздар',
                    ['ы'] = 'ңыздар',
                    ['о'] = 'ңуздар',
                    ['у'] = 'ңуздар',
                    ['э'] = 'ңиздер',
                    ['е'] = 'ңиздер',
                    ['и'] = 'ңиздер',
                    ['ө'] = 'ңүздөр',
                    ['ү'] = 'ңүздөр'
                }
                return (possessive_vowel_map[vowel] or 'ы') .. (formal_map[vowel] or 'ңыздар')
            end
        end,
        ['{Ps3pl}'] = function()
            if stem_type == 'V' then
                -- For vowel-ending stems: -сы/-си/-су/-сү
                local vowel = get_vowel_harmony(last_vowel)
                local third_person_map = {
                    ['а'] = 'сы',
                    ['ы'] = 'сы',
                    ['о'] = 'су',
                    ['у'] = 'су',
                    ['э'] = 'си',
                    ['е'] = 'си',
                    ['и'] = 'си',
                    ['ө'] = 'сү',
                    ['ү'] = 'сү'
                }
                return third_person_map[vowel] or 'сы'
            else
                -- For consonant-ending stems: just the vowel
                return get_vowel_harmony(last_vowel)
            end
        end,
        -- Plural possessive - these will use the basic stem (not parameter [1])
        ['{Pps1sg}'] = function()
            local plural_suffix = get_plural_suffix(last_vowel, stem_type)
            
            if stem_type == 'V' then
                local vowel = get_vowel_harmony(last_vowel)
                local possessive_vowel_map = {
                    ['а'] = 'ы',
                    ['ы'] = 'ы',
                    ['о'] = 'у',
                    ['у'] = 'ы',
                    ['э'] = 'и',
                    ['е'] = 'и',
                    ['и'] = 'и',
                    ['ө'] = 'ү',
                    ['ү'] = 'ү'
                }
                return plural_suffix .. (possessive_vowel_map[vowel] or 'ы') .. 'м'
            else
                local vowel = get_vowel_harmony(last_vowel)
                local possessive_vowel_map = {
                    ['а'] = 'ы',
                    ['ы'] = 'ы',
                    ['о'] = 'у',
                    ['у'] = 'ы',
                    ['э'] = 'и',
                    ['е'] = 'и',
                    ['и'] = 'и',
                    ['ө'] = 'ү',
                    ['ү'] = 'ү'
                }
                return plural_suffix .. (possessive_vowel_map[vowel] or 'ы') .. 'м'
            end
        end,
        ['{Pps2sg}'] = function()
            local plural_suffix = get_plural_suffix(last_vowel, stem_type)
            
            if stem_type == 'V' then
                local vowel = get_vowel_harmony(last_vowel)
                local possessive_vowel_map = {
                    ['а'] = 'ы',
                    ['ы'] = 'ы',
                    ['о'] = 'у',
                    ['у'] = 'ы',
                    ['э'] = 'и',
                    ['е'] = 'и',
                    ['и'] = 'и',
                    ['ө'] = 'ү',
                    ['ү'] = 'ү'
                }
                return plural_suffix .. (possessive_vowel_map[vowel] or 'ы') .. 'ң'
            else
                local vowel = get_vowel_harmony(last_vowel)
                local possessive_vowel_map = {
                    ['а'] = 'ы',
                    ['ы'] = 'ы',
                    ['о'] = 'у',
                    ['у'] = 'ы',
                    ['э'] = 'и',
                    ['е'] = 'и',
                    ['и'] = 'и',
                    ['ө'] = 'ү',
                    ['ү'] = 'ү'
                }
                return plural_suffix .. (possessive_vowel_map[vowel] or 'ы') .. 'ң'
            end
        end,
        ['{Pps2sgf}'] = function()
            local plural_suffix = get_plural_suffix(last_vowel, stem_type)
            
            if stem_type == 'V' then
                local vowel = get_vowel_harmony(last_vowel)
                local possessive_vowel_map = {
                    ['а'] = 'ы',
                    ['ы'] = 'ы',
                    ['о'] = 'у',
                    ['у'] = 'ы',
                    ['э'] = 'и',
                    ['е'] = 'и',
                    ['и'] = 'и',
                    ['ө'] = 'ү',
                    ['ү'] = 'ү'
                }
                local plural_ending_map = {
                    ['а'] = 'ңыз',
                    ['ы'] = 'ңыз',
                    ['о'] = 'ңуз',
                    ['у'] = 'ңыз',
                    ['э'] = 'ңиз',
                    ['е'] = 'ңиз',
                    ['и'] = 'ңиз',
                    ['ө'] = 'ңүз',
                    ['ү'] = 'ңүз'
                }
                return plural_suffix .. (possessive_vowel_map[vowel] or 'ы') .. (plural_ending_map[vowel] or 'ңыз')
            else
                local vowel = get_vowel_harmony(last_vowel)
                local possessive_vowel_map = {
                    ['а'] = 'ы',
                    ['ы'] = 'ы',
                    ['о'] = 'у',
                    ['у'] = 'ы',
                    ['э'] = 'и',
                    ['е'] = 'и',
                    ['и'] = 'и',
                    ['ө'] = 'ү',
                    ['ү'] = 'ү'
                }
                local plural_ending_map = {
                    ['а'] = 'ңыз',
                    ['ы'] = 'ңыз',
                    ['о'] = 'ңуз',
                    ['у'] = 'ңыз',
                    ['э'] = 'ңиз',
                    ['е'] = 'ңиз',
                    ['и'] = 'ңиз',
                    ['ө'] = 'ңүз',
                    ['ү'] = 'ңүз'
                }
                return plural_suffix .. (possessive_vowel_map[vowel] or 'ы') .. (plural_ending_map[vowel] or 'ңыз')
            end
        end,
        ['{Pps3sg}'] = function()
            local plural_suffix = get_plural_suffix(last_vowel, stem_type)
            
            if stem_type == 'V' then
                local vowel = get_vowel_harmony(last_vowel)
                local possessive_vowel_map = {
                    ['а'] = 'ы',
                    ['ы'] = 'ы',
                    ['о'] = 'у',
                    ['у'] = 'ы',
                    ['э'] = 'и',
                    ['е'] = 'и',
                    ['и'] = 'и',
                    ['ө'] = 'ү',
                    ['ү'] = 'ү'
                }
                return plural_suffix .. (possessive_vowel_map[vowel] or 'ы')
            else
                local vowel = get_vowel_harmony(last_vowel)
                local possessive_vowel_map = {
                    ['а'] = 'ы',
                    ['ы'] = 'ы',
                    ['о'] = 'у',
                    ['у'] = 'у',
                    ['э'] = 'и',
                    ['е'] = 'и',
                    ['и'] = 'и',
                    ['ө'] = 'ү',
                    ['ү'] = 'ү'
                }
                return plural_suffix .. (possessive_vowel_map[vowel] or 'ы')
            end
        end,
        ['{Pps1pl}'] = function()
            local plural_suffix = get_plural_suffix(last_vowel, stem_type)
            
            if stem_type == 'V' then
                local vowel = get_vowel_harmony(last_vowel)
                local possessive_vowel_map = {
                    ['а'] = 'ы',
                    ['ы'] = 'ы',
                    ['о'] = 'у',
                    ['у'] = 'ы',
                    ['э'] = 'и',
                    ['е'] = 'и',
                    ['и'] = 'и',
                    ['ө'] = 'ү',
                    ['ү'] = 'ү'
                }
                local plural_ending_map = {
                    ['а'] = 'быз',
                    ['ы'] = 'быз',
                    ['о'] = 'буз',
                    ['у'] = 'быз',
                    ['э'] = 'биз',
                    ['е'] = 'биз',
                    ['и'] = 'биз',
                    ['ө'] = 'бүз',
                    ['ү'] = 'бүз'
                }
                return plural_suffix .. (possessive_vowel_map[vowel] or 'ы') .. (plural_ending_map[vowel] or 'быз')
            else
                local vowel = get_vowel_harmony(last_vowel)
                local possessive_vowel_map = {
                    ['а'] = 'ы',
                    ['ы'] = 'ы',
                    ['о'] = 'у',
                    ['у'] = 'ы',
                    ['э'] = 'и',
                    ['е'] = 'и',
                    ['и'] = 'и',
                    ['ө'] = 'ү',
                    ['ү'] = 'ү'
                }
                local plural_ending_map = {
                    ['а'] = 'быз',
                    ['ы'] = 'быз',
                    ['о'] = 'буз',
                    ['у'] = 'быз',
                    ['э'] = 'биз',
                    ['е'] = 'биз',
                    ['и'] = 'биз',
                    ['ө'] = 'бүз',
                    ['ү'] = 'бүз'
                }
                return plural_suffix .. (possessive_vowel_map[vowel] or 'ы') .. (plural_ending_map[vowel] or 'быз')
            end
        end,
        ['{Pps2pl}'] = function()
            local plural_suffix = get_plural_suffix(last_vowel, stem_type)
            
            if stem_type == 'V' then
                local vowel = get_vowel_harmony(last_vowel)
                local possessive_vowel_map = {
                    ['а'] = 'ы',
                    ['ы'] = 'ы',
                    ['о'] = 'у',
                    ['у'] = 'ы',
                    ['э'] = 'и',
                    ['е'] = 'и',
                    ['и'] = 'и',
                    ['ө'] = 'ү',
                    ['ү'] = 'ү'
                }
                local plural_ending_map = {
                    ['а'] = 'ңар',
                    ['ы'] = 'ңар',
                    ['о'] = 'ңар',
                    ['у'] = 'ңар',
                    ['э'] = 'ңер',
                    ['е'] = 'ңер',
                    ['и'] = 'ңер',
                    ['ө'] = 'ңөр',
                    ['ү'] = 'ңөр'
                }
                return plural_suffix .. (possessive_vowel_map[vowel] or 'ы') .. (plural_ending_map[vowel] or 'ңар')
            else
                local vowel = get_vowel_harmony(last_vowel)
                local possessive_vowel_map = {
                    ['а'] = 'ы',
                    ['ы'] = 'ы',
                    ['о'] = 'у',
                    ['у'] = 'ы',
                    ['э'] = 'и',
                    ['е'] = 'и',
                    ['и'] = 'и',
                    ['ө'] = 'ү',
                    ['ү'] = 'ү'
                }
                local plural_ending_map = {
                    ['а'] = 'ңар',
                    ['ы'] = 'ңар',
                    ['о'] = 'ңар',
                    ['у'] = 'ңар',
                    ['э'] = 'ңер',
                    ['е'] = 'ңер',
                    ['и'] = 'ңер',
                    ['ө'] = 'ңөр',
                    ['ү'] = 'ңөр'
                }
                return plural_suffix .. (possessive_vowel_map[vowel] or 'ы') .. (plural_ending_map[vowel] or 'ңар')
            end
        end,
        ['{Pps2plf}'] = function()
            local plural_suffix = get_plural_suffix(last_vowel, stem_type)
            
            if stem_type == 'V' then
                local vowel = get_vowel_harmony(last_vowel)
                local possessive_vowel_map = {
                    ['а'] = 'ы',
                    ['ы'] = 'ы',
                    ['о'] = 'у',
                    ['у'] = 'ы',
                    ['э'] = 'и',
                    ['е'] = 'и',
                    ['и'] = 'и',
                    ['ө'] = 'ү',
                    ['ү'] = 'ү'
                }
                local plural_ending_map = {
                    ['а'] = 'ңыздар',
                    ['ы'] = 'ңыздар',
                    ['о'] = 'ңуздар',
                    ['у'] = 'ңыздар',
                    ['э'] = 'ңиздер',
                    ['е'] = 'ңиздер',
                    ['и'] = 'ңиздер',
                    ['ө'] = 'ңүздөр',
                    ['ү'] = 'ңүздөр'
                }
                return plural_suffix .. (possessive_vowel_map[vowel] or 'ы') .. (plural_ending_map[vowel] or 'ңыздар')
            else
                local vowel = get_vowel_harmony(last_vowel)
                local possessive_vowel_map = {
                    ['а'] = 'ы',
                    ['ы'] = 'ы',
                    ['о'] = 'у',
                    ['у'] = 'ы',
                    ['э'] = 'и',
                    ['е'] = 'и',
                    ['и'] = 'и',
                    ['ө'] = 'ү',
                    ['ү'] = 'ү'
                }
                local plural_ending_map = {
                    ['а'] = 'ңыздар',
                    ['ы'] = 'ңыздар',
                    ['о'] = 'ңуздар',
                    ['у'] = 'ңыздар',
                    ['э'] = 'ңиздер',
                    ['е'] = 'ңиздер',
                    ['и'] = 'ңиздер',
                    ['ө'] = 'ңүздөр',
                    ['ү'] = 'ңүздөр'
                }
                return plural_suffix .. (possessive_vowel_map[vowel] or 'ы') .. (plural_ending_map[vowel] or 'ңыздар')
            end
        end,
        ['{Pps3pl}'] = function()
            local plural_suffix = get_plural_suffix(last_vowel, stem_type)
            
            if stem_type == 'V' then
                local vowel = get_vowel_harmony(last_vowel)
                local possessive_vowel_map = {
                    ['а'] = 'ы',
                    ['ы'] = 'ы',
                    ['о'] = 'у',
                    ['у'] = 'ы',
                    ['э'] = 'и',
                    ['е'] = 'и',
                    ['и'] = 'и',
                    ['ө'] = 'ү',
                    ['ү'] = 'ү'
                }
                return plural_suffix .. (possessive_vowel_map[vowel] or 'ы')
            else
                local vowel = get_vowel_harmony(last_vowel)
                local possessive_vowel_map = {
                    ['а'] = 'ы',
                    ['ы'] = 'ы',
                    ['о'] = 'у',
                    ['у'] = 'ы',
                    ['э'] = 'и',
                    ['е'] = 'и',
                    ['и'] = 'и',
                    ['ө'] = 'ү',
                    ['ү'] = 'ү'
                }
                return plural_suffix .. (possessive_vowel_map[vowel] or 'ы')
            end
        end,
        -- Predicate forms
        ['{Pr1sg}'] = function()
            if stem_type == 'V' then
                local vowel = get_vowel_harmony(last_vowel)
                local predicate_map = {
                    ['а'] = 'мын',
                    ['ы'] = 'мын',
                    ['о'] = 'мун',
                    ['у'] = 'мун',
                    ['э'] = 'мин',
                    ['е'] = 'мин',
                    ['и'] = 'мин',
                    ['ө'] = 'мүн',
                    ['ү'] = 'мүн'
                }
                return predicate_map[vowel] or 'мын'
            else
                local vowel = get_vowel_harmony(last_vowel)
                local predicate_map = {
                    ['а'] = 'мын',
                    ['ы'] = 'мын',
                    ['о'] = 'мун',
                    ['у'] = 'мун',
                    ['э'] = 'мин',
                    ['е'] = 'мин',
                    ['и'] = 'мин',
                    ['ө'] = 'мүн',
                    ['ү'] = 'мүн'
                }
                return predicate_map[vowel] or 'мын'
            end
        end,
        ['{Pr2sg}'] = function()
            if stem_type == 'V' then
                local vowel = get_vowel_harmony(last_vowel)
                local predicate_map = {
                    ['а'] = 'сың',
                    ['ы'] = 'сың',
                    ['о'] = 'суң',
                    ['у'] = 'суң',
                    ['э'] = 'сиң',
                    ['е'] = 'сиң',
                    ['и'] = 'сиң',
                    ['ө'] = 'сүң',
                    ['ү'] = 'сүң'
                }
                return predicate_map[vowel] or 'сың'
            else
                local vowel = get_vowel_harmony(last_vowel)
                local predicate_map = {
                    ['а'] = 'сың',
                    ['ы'] = 'сың',
                    ['о'] = 'суң',
                    ['у'] = 'суң',
                    ['э'] = 'сиң',
                    ['е'] = 'сиң',
                    ['и'] = 'сиң',
                    ['ө'] = 'сүң',
                    ['ү'] = 'сүң'
                }
                return predicate_map[vowel] or 'сың'
            end
        end,
        ['{Pr2sgf}'] = function()
            if stem_type == 'V' then
                local vowel = get_vowel_harmony(last_vowel)
                local predicate_map = {
                    ['а'] = 'сыз',
                    ['ы'] = 'сыз',
                    ['о'] = 'суз',
                    ['у'] = 'суз',
                    ['э'] = 'сиз',
                    ['е'] = 'сиз',
                    ['и'] = 'сиз',
                    ['ө'] = 'сүз',
                    ['ү'] = 'сүз'
                }
                return predicate_map[vowel] or 'сыз'
            else
                local vowel = get_vowel_harmony(last_vowel)
                local predicate_map = {
                    ['а'] = 'сыз',
                    ['ы'] = 'сыз',
                    ['о'] = 'суз',
                    ['у'] = 'суз',
                    ['э'] = 'сиз',
                    ['е'] = 'сиз',
                    ['и'] = 'сиз',
                    ['ө'] = 'сүз',
                    ['ү'] = 'сүз'
                }
                return predicate_map[vowel] or 'сыз'
            end
        end,
        ['{Pr1pl}'] = function()
            if stem_type == 'V' then
                local vowel = get_vowel_harmony(last_vowel)
                local predicate_map = {
                    ['а'] = 'быз',
                    ['ы'] = 'быз',
                    ['о'] = 'буз',
                    ['у'] = 'быз',
                    ['э'] = 'биз',
                    ['е'] = 'биз',
                    ['и'] = 'биз',
                    ['ө'] = 'бүз',
                    ['ү'] = 'бүз'
                }
                return predicate_map[vowel] or 'быз'
            elseif stem_type == 'T' then
                local vowel = get_vowel_harmony(last_vowel)
                local predicate_map = {
                    ['а'] = 'пыз',
                    ['ы'] = 'пыз',
                    ['о'] = 'пуз',
                    ['у'] = 'пыз',
                    ['э'] = 'пиз',
                    ['е'] = 'пиз',
                    ['и'] = 'пиз',
                    ['ө'] = 'пүз',
                    ['ү'] = 'пүз'
                }
                return predicate_map[vowel] or 'быз'
            else
                local vowel = get_vowel_harmony(last_vowel)
                local predicate_map = {
                    ['а'] = 'быз',
                    ['ы'] = 'быз',
                    ['о'] = 'буз',
                    ['у'] = 'быз',
                    ['э'] = 'биз',
                    ['е'] = 'биз',
                    ['и'] = 'биз',
                    ['ө'] = 'бүз',
                    ['ү'] = 'бүз'
                }
                return predicate_map[vowel] or 'быз'
            end
        end,
        ['{Ppr1pl}'] = function()
            local plural_suffix = get_plural_suffix(last_vowel, stem_type)
            if stem_type == 'V' then
                local vowel = get_vowel_harmony(last_vowel)
                local predicate_map = {
                    ['а'] = 'быз',
                    ['ы'] = 'быз',
                    ['о'] = 'буз',
                    ['у'] = 'быз',
                    ['э'] = 'биз',
                    ['е'] = 'биз',
                    ['и'] = 'биз',
                    ['ө'] = 'бүз',
                    ['ү'] = 'бүз'
                }
                return plural_suffix .. (predicate_map[vowel] or 'быз')
            else
                local vowel = get_vowel_harmony(last_vowel)
                local predicate_map = {
                    ['а'] = 'быз',
                    ['ы'] = 'быз',
                    ['о'] = 'буз',
                    ['у'] = 'быз',
                    ['э'] = 'биз',
                    ['е'] = 'биз',
                    ['и'] = 'биз',
                    ['ө'] = 'бүз',
                    ['ү'] = 'бүз'
                }
                return plural_suffix .. (predicate_map[vowel] or 'быз')
            end
        end,
        ['{Pr2pl}'] = function()
            if stem_type == 'V' then
                local vowel = get_vowel_harmony(last_vowel)
                local predicate_map = {
                    ['а'] = 'сыңар',
                    ['ы'] = 'сыңар',
                    ['о'] = 'суңар',
                    ['у'] = 'суңар',
                    ['э'] = 'сиңер',
                    ['е'] = 'сиңер',
                    ['и'] = 'сиңер',
                    ['ө'] = 'сүңөр',
                    ['ү'] = 'сүңөр'
                }
                return predicate_map[vowel] or 'сыңар'
            else
                local vowel = get_vowel_harmony(last_vowel)
                local predicate_map = {
                    ['а'] = 'сыңар',
                    ['ы'] = 'сыңар',
                    ['о'] = 'суңар',
                    ['у'] = 'суңар',
                    ['э'] = 'сиңер',
                    ['е'] = 'сиңер',
                    ['и'] = 'сиңер',
                    ['ө'] = 'сүңөр',
                    ['ү'] = 'сүңөр'
                }
                return predicate_map[vowel] or 'сыңар'
            end
        end,
        ['{Ppr2pl}'] = function()
            local plural_suffix = get_plural_suffix(last_vowel, stem_type)
            if stem_type == 'V' then
                local vowel = get_vowel_harmony(last_vowel)
                local predicate_map = {
                    ['а'] = 'сыңар',
                    ['ы'] = 'сыңар',
                    ['о'] = 'суңар',
                    ['у'] = 'сыңар',
                    ['э'] = 'сиңер',
                    ['е'] = 'сиңер',
                    ['и'] = 'сиңер',
                    ['ө'] = 'сүңөр',
                    ['ү'] = 'сүңөр'
                }
                return plural_suffix .. (predicate_map[vowel] or 'сыңар')
            else
                local vowel = get_vowel_harmony(last_vowel)
                local predicate_map = {
                    ['а'] = 'сыңар',
                    ['ы'] = 'сыңар',
                    ['о'] = 'суңар',
                    ['у'] = 'сыңар',
                    ['э'] = 'сиңер',
                    ['е'] = 'сиңер',
                    ['и'] = 'сиңер',
                    ['ө'] = 'сүңөр',
                    ['ү'] = 'сүңөр'
                }
                return plural_suffix .. (predicate_map[vowel] or 'сыңар')
            end
        end,
        ['{Pr2plf}'] = function()
            if stem_type == 'V' then
                local vowel = get_vowel_harmony(last_vowel)
                local predicate_map = {
                    ['а'] = 'сыздар',
                    ['ы'] = 'сыздар',
                    ['о'] = 'суздар',
                    ['у'] = 'суздар',
                    ['э'] = 'сиздер',
                    ['е'] = 'сиздер',
                    ['и'] = 'сиздер',
                    ['ө'] = 'сүздөр',
                    ['ү'] = 'сүздөр'
                }
                return predicate_map[vowel] or 'сыздар'
            else
                local vowel = get_vowel_harmony(last_vowel)
                local predicate_map = {
                    ['а'] = 'сыздар',
                    ['ы'] = 'сыздар',
                    ['о'] = 'суздар',
                    ['у'] = 'суздар',
                    ['э'] = 'сиздер',
                    ['е'] = 'сиздер',
                    ['и'] = 'сиздер',
                    ['ө'] = 'сүздөр',
                    ['ү'] = 'сүздөр'
                }
                return predicate_map[vowel] or 'сыздар'
            end
        end,
        ['{Ppr2plf}'] = function()
            local plural_suffix = get_plural_suffix(last_vowel, stem_type)
            if stem_type == 'V' then
                local vowel = get_vowel_harmony(last_vowel)
                local predicate_map = {
                    ['а'] = 'сыздар',
                    ['ы'] = 'сыздар',
                    ['о'] = 'суздар',
                    ['у'] = 'сыздар',
                    ['э'] = 'сиздер',
                    ['е'] = 'сиздер',
                    ['и'] = 'сиздер',
                    ['ө'] = 'сүздөр',
                    ['ү'] = 'сүздөр'
                }
                return plural_suffix .. (predicate_map[vowel] or 'сыздар')
            else
                local vowel = get_vowel_harmony(last_vowel)
                local predicate_map = {
                    ['а'] = 'сыздар',
                    ['ы'] = 'сыздар',
                    ['о'] = 'суздар',
                    ['у'] = 'сыздар',
                    ['э'] = 'сиздер',
                    ['е'] = 'сиздер',
                    ['и'] = 'сиздер',
                    ['ө'] = 'сүздөр',
                    ['ү'] = 'сүздөр'
                }
                return plural_suffix .. (predicate_map[vowel] or 'сыздар')
            end
        end
    }
    
    for k, v in pairs(special_chars) do
        if mw.ustring.find(ending, k, 1, true) ~= nil then
            ending = mw.ustring.gsub(ending, k, v())
        end
    end
    
    return ending
end

decline = function(stem, alt_stem, ending_pattern, ending_vowel, final_stem_class, apostrophe_required, is_possessive, is_plural_possessive)
    if is_string_empty(stem) then
        return ''
    end
    
    local apostrophe = ifexpr(apostrophe_required == 'tak', '\'', '')
    
    -- Use parameter [1] (alt_stem) only for singular possessive forms
    -- For plural possessives and all other forms, use the basic stem
    local current_stem = stem
    if is_possessive and not is_plural_possessive and not is_string_empty(alt_stem) then
        current_stem = alt_stem
    end
    
    return current_stem 
        .. apostrophe
        .. construct_noun_ending(
            current_stem, 
            ending_pattern, 
            ending_vowel, 
            final_stem_class,
            is_possessive,
            is_plural_possessive)
end

handle_alt_stem = function(stem, alt_stem, check_for_dropping_vowel)
    if is_string_empty(alt_stem) then
        return stem
    end

    if check_for_dropping_vowel then
        if p.has_dropping_vowel(stem, alt_stem) then
            return stem    
        end
    end
    
    return alt_stem
end

handle_frame_arg = function(arg, on_nil)
    if is_string_empty(arg) then
        return on_nil
    end
    
    return arg
end

get_case_endings_table = function(blp, stem)
    local no_singular = blp == 'tak'
    
    if is_string_empty(stem) then
        return endings.endings2['']
    end
    
    local ends_with_plural = ends_with(stem, 'лар') or ends_with(stem, 'лер') or 
                             ends_with(stem, 'лор') or ends_with(stem, 'лөр') or
                             ends_with(stem, 'дар') or ends_with(stem, 'дер') or
                             ends_with(stem, 'дор') or ends_with(stem, 'дөр') or
                             ends_with(stem, 'тар') or ends_with(stem, 'тер') or
                             ends_with(stem, 'тор') or ends_with(stem, 'төр')
    
    return endings.endings2[ifexpr(no_singular and ends_with_plural, 'blp-plural', '')]
end

get_case_names = function(case_endings)
    local res = {}
    
    for _, v in pairs(case_endings) do
        for k2, _ in pairs(v) do
            table.insert(res, k2)
        end
    end
    
    return res
end

fetch_args = function(frame, args_to_fetch)
    local ret = {}
    local args = frame.args
    
    for _, n in pairs(args_to_fetch) do
        if not is_string_empty(args[n]) then
            ret[n] = args[n]
        end
    end

    return ret 
end

decline_noun = function(args, case_endings, special_cases, 
stem, alt_stem, ending_vowel, final_stem_class, apostrophe_required, 
check_for_dropping_vowel, is_possessive, is_plural_possessive)
    local res = args or {}
    
    for k, v in pairs(case_endings) do
        if res[k] == nil then
            res[k] = decline(
                stem,
                alt_stem,
                v, 
                ending_vowel,
                final_stem_class,
                apostrophe_required,
                is_possessive,
                is_plural_possessive)
        end
    end
    
    return res
end

p.main = function(frame)
    local TABLE_NAME = 'Szablon:odmiana-rzeczownik-kirgiski/tabela'
    local args = frame.args
    local stem = args[1] or ''  -- Basic stem
    local alt_stem = handle_frame_arg(args[2], '')  -- Parameter [1] - stem with alternations for singular possessives
    local ending_vowel = handle_frame_arg(args[3], get_last_vowel_of_stem(stem))
    local final_stem_class = handle_frame_arg(args[4], get_last_letter_type(stem))
    local apostrophe_required = args[5]
    local create_predicate_table = args['formy osobowe']
    local is_animate = args['żywotny']
    local case_endings = get_case_endings_table(args['blp'], stem)
    
    local arguments = fetch_args(frame, get_case_names(case_endings))
    
    -- Always show cases and possessives
    local decline_for = {'normal', 'possessive'}
    
    -- Show predicatives only if both conditions are met
    local should_show_predicatives = (create_predicate_table == 'tak')
    
    if should_show_predicatives then
        table.insert(decline_for, 'predicate')
    end
    
    for _, v in ipairs(decline_for) do
        -- Check if case endings exist for this category before declining
        if case_endings[v] then
            local is_possessive_form = (v == 'possessive')
            local is_plural_possessive_form = false
            
            arguments = decline_noun(arguments, case_endings[v], {}, 
            stem, alt_stem, ending_vowel, final_stem_class, apostrophe_required,
            ifexpr(v == 'predicate', true, false),
            is_possessive_form,
            is_plural_possessive_form)
        end
    end
    
    -- Handle plural possessive forms separately - use basic stem (not parameter [1])
    if case_endings['possessive'] then
        local plural_possessive_forms = {
            'Mianownik lm 1 os lp dzierżawczy',
            'Mianownik lm 2 os lp dzierżawczy',
            'Mianownik lm 2 os lp grzecznościowy dzierżawczy', 
            'Mianownik lm 3 os lp dzierżawczy',
            'Mianownik lm 1 os lm dzierżawczy',
            'Mianownik lm 2 os lm dzierżawczy',
            'Mianownik lm 2 os lm grzecznościowy dzierżawczy',
            'Mianownik lm 3 os lm dzierżawczy',
        }
        
        for _, form_name in ipairs(plural_possessive_forms) do
            if case_endings['possessive'][form_name] and not arguments[form_name] then
                arguments[form_name] = decline(
                    stem,
                    '', -- Don't use parameter [1] for plural possessives - use basic stem
                    case_endings['possessive'][form_name], 
                    ending_vowel,
                    final_stem_class,
                    apostrophe_required,
                    true, -- is_possessive
                    true) -- is_plural_possessive
            end
        end
    end
    
    -- If predicatives shouldn't be shown, set them to empty strings
    if not should_show_predicatives then
        local predicate_forms = {
            'Mianownik lp 1 os lp osobowy', 
            'Mianownik lp 2 os lp osobowy',
            'Mianownik lp 2 os lp grzecznościowy osobowy',
            'Mianownik lp 1 os lm osobowy',
            'Mianownik lp 2 os lm osobowy',
            'Mianownik lp 2 os lm grzecznościowy osobowy',
            'Mianownik lm 1 os lp osobowy',
            'Mianownik lm 2 os lp osobowy',
            'Mianownik lm 2 os lp grzecznościowy osobowy',
            'Mianownik lp 3 os lp krótki osobowy',
            'Mianownik lp 3 os lp długi osobowy',
            'Mianownik lm 3 os lp krótki osobowy',
            'Mianownik lm 3 os lp długi osobowy',
            'Mianownik lm 1 os lm osobowy',
            'Mianownik lm 2 os lm osobowy',
            'Mianownik lm 2 os lm grzecznościowy osobowy',
            'Mianownik lp 3 os lm krótki osobowy',
            'Mianownik lp 3 os lm długi osobowy',
            'Mianownik lm 3 os lm osobowy'
        }
        
        for _, predicate_case in ipairs(predicate_forms) do
            arguments[predicate_case] = ''
        end
    end
    
    if args['blp'] == 'tak' then
        arguments['Mianownik lp'] = ''
    end
    
    if args['blm'] == 'tak' then
        arguments['Mianownik lm'] = ''
    end
    
    arguments['formy osobowe'] = create_predicate_table or nil
    arguments['żywotny'] = is_animate or nil
    arguments['blp'] = args['blp'] or nil
    arguments['blm'] = args['blm'] or nil
    
    return frame:expandTemplate{title = TABLE_NAME, args = arguments}
end

return p