# SDK Pullenti Lingvo, version 4.33, fabruary 2026. Copyright (c) 2013-2026, Pullenti. All rights reserved.
# Non-Commercial Freeware and Commercial Software.
# This class is generated using the converter Unisharping (www.unisharping.ru) from Pullenti C# project.
# The latest version of the code is available on the site www.pullenti.ru

import typing
import io
import math
from pullenti.unisharp.Utils import Utils
from pullenti.unisharp.Misc import RefOutArgWrapper

from pullenti.ner.core.BracketHelper import BracketHelper
from pullenti.ner.address.internal.AddressItemType import AddressItemType
from pullenti.ner.NumberToken import NumberToken
from pullenti.ner.person.PersonReferent import PersonReferent
from pullenti.ner.person.internal.PersonNormalNode import PersonNormalNode
from pullenti.morph.MorphNumber import MorphNumber
from pullenti.ner.person.internal.ShortNameHelper import ShortNameHelper
from pullenti.ner.SourceOfAnalysis import SourceOfAnalysis
from pullenti.morph.LanguageHelper import LanguageHelper
from pullenti.morph.MorphLang import MorphLang
from pullenti.ner.person.PersonNormalResult import PersonNormalResult
from pullenti.morph.MorphologyService import MorphologyService
from pullenti.ner.TextToken import TextToken
from pullenti.ner.person.internal.PersonItemToken import PersonItemToken
from pullenti.ner.ProcessorService import ProcessorService
from pullenti.ner.address.internal.AddressItemToken import AddressItemToken

class PersonNormalHelper:
    
    @staticmethod
    def analyze(res : 'PersonNormalData', txt : str, corr : typing.List[tuple]) -> None:
        from pullenti.ner.person.PersonNormalData import PersonNormalData
        co = None
        if (corr is not None and "" in corr): 
            co = corr[""]
        ttt = Utils.newStringIO(txt)
        i = 1
        while i < (ttt.tell() - 2): 
            if (Utils.getCharAtStringIO(ttt, i) == '-' and str.islower(Utils.getCharAtStringIO(ttt, i + 1))): 
                Utils.setCharAtStringIO(ttt, i + 1, str.upper(Utils.getCharAtStringIO(ttt, i + 1)))
            elif (Utils.getCharAtStringIO(ttt, i) == '-' and Utils.getCharAtStringIO(ttt, i + 1) == ' ' and str.isalpha(Utils.getCharAtStringIO(ttt, i + 2))): 
                Utils.setCharAtStringIO(ttt, i + 2, str.upper(Utils.getCharAtStringIO(ttt, i + 2)))
                Utils.removeStringIO(ttt, i + 1, 1)
            i += 1
        for i in range(ttt.tell() - 2, -1, -1):
            if (Utils.getCharAtStringIO(ttt, i) == ' ' and Utils.getCharAtStringIO(ttt, i + 1) == ' '): 
                Utils.removeStringIO(ttt, i + 1, 1)
            elif (LanguageHelper.is_hiphen(Utils.getCharAtStringIO(ttt, i)) and LanguageHelper.is_hiphen(Utils.getCharAtStringIO(ttt, i + 1))): 
                Utils.removeStringIO(ttt, i + 1, 1)
            elif (Utils.getCharAtStringIO(ttt, i) == '\t'): 
                Utils.setCharAtStringIO(ttt, i, '\n')
        txt = Utils.toStringStringIO(ttt)
        res.res_typ = PersonNormalResult.NOTPERSON
        res.error_message = "Похоже на просто текст"
        if (len(txt) > 200): 
            return
        ar = None
        alt_surname = None
        addcorr = None
        for kk in range(20):
            ar = ProcessorService.get_empty_processor().process(SourceOfAnalysis._new3442(txt, co, False, False), None, None)
            if (ar is None or ar.first_token is None): 
                return
            ch = False
            t = ar.first_token
            first_pass4399 = True
            while True:
                if first_pass4399: first_pass4399 = False
                else: t = t.next0_
                if (not (t is not None)): break
                if (((((isinstance(t, TextToken)) and not t.chars.is_letter and not t.is_whitespace_before) and (isinstance(t.previous, TextToken)) and t.previous.chars.is_cyrillic_letter) and not t.is_whitespace_after and (isinstance(t.next0_, TextToken))) and t.next0_.chars.is_cyrillic_letter): 
                    str0_ = t.previous.term + t.next0_.term
                    mb = MorphologyService.get_word_base_info(str0_, MorphLang.RU, True, True)
                    if (mb.class0_.is_proper_surname or mb.class0_.is_proper_name or mb.class0_.is_proper_secname): 
                        ok = True
                        if (t.is_hiphen and not t.next0_.chars.is_all_lower): 
                            ok = False
                            if (str0_ in PersonNormalHelper.__m_male_names or str0_ in PersonNormalHelper.__m_male_secnames or str0_ in PersonNormalHelper.__m_female_secnames): 
                                ok = True
                        if (ok): 
                            txt = "{0}{1}".format(txt[0:0+t.begin_char], txt[t.end_char + 1:])
                            ch = True
                            break
                if ((((isinstance(t, TextToken)) and ((t.chars.is_capital_upper or t.chars.is_all_upper)) and t.whitespaces_after_count == 1) and (isinstance(t.next0_, TextToken)) and ((t.next0_.chars.is_all_lower or t.length_char == 1))) and t.chars.is_letter and t.next0_.chars.is_letter): 
                    if (t.length_char == 1 and (t.next0_.length_char < 4)): 
                        continue
                    if ((t.length_char < 4) and t.next0_.length_char == 1): 
                        continue
                    str0_ = t.term + t.next0_.term
                    mb = MorphologyService.get_word_base_info(str0_, MorphLang.RU, True, True)
                    if (mb.class0_.is_proper_surname or mb.class0_.is_proper_name or mb.class0_.is_proper_secname): 
                        txt = (txt[0:0+t.end_char + 1] + txt[t.next0_.begin_char:])
                        ch = True
                        break
                elif (((isinstance(t, NumberToken)) and not t.is_whitespace_after and (isinstance(t.next0_, TextToken))) and t.next0_.chars.is_letter): 
                    n = t.value
                    str0_ = None
                    if (n == "0"): 
                        str0_ = ("О" + t.next0_.term)
                    elif (n == "3"): 
                        str0_ = ("З" + t.next0_.term)
                    if (str0_ is not None): 
                        mb = MorphologyService.get_word_base_info(str0_, MorphLang.RU, True, True)
                        if ((mb.class0_.is_proper_surname or mb.class0_.is_proper_name or mb.class0_.is_proper_secname) or t.length_char == 1): 
                            txt = "{0}{1}{2}".format(txt[0:0+t.end_char], str0_[0], txt[t.next0_.begin_char:])
                            ch = True
                            break
                    if (not t.is_whitespace_before and (isinstance(t.previous, TextToken)) and t.previous.chars.is_cyrillic_letter): 
                        mb = MorphologyService.get_word_base_info(t.previous.term + t.next0_.term, MorphLang.RU, True, True)
                        if (mb.class0_.is_proper_surname or mb.class0_.is_proper_name or mb.class0_.is_proper_secname): 
                            txt = "{0}{1}".format(txt[0:0+t.begin_char], txt[t.end_char + 1:])
                            ch = True
                            break
                elif (t.is_value("РАНЕЕ", None) and (isinstance(t.next0_, TextToken)) and t.previous is not None): 
                    if (t.next0_.end_char < (len(txt) - 1)): 
                        txt = (txt[0:0+t.previous.end_char + 1] + " " + txt[t.next0_.end_char + 1:])
                    else: 
                        txt = txt[0:0+t.previous.end_char + 1]
                    ch = True
                    break
                elif ((t.is_value("ДО", None) and (isinstance(t.next0_, TextToken)) and t.previous is not None) and t.next0_ is not None): 
                    tt = t.next0_
                    if (tt.is_hiphen or tt.is_char_of("\\/")): 
                        if (tt.next0_ is not None): 
                            tt = tt.next0_
                    if (((((tt.is_value("БРАК", None) or tt.is_value("ЗАМУЖЕСТВО", None) or tt.is_value("ПЕРЕМЕНА", None)) or tt.is_value("ПЕРЕМ", None) or tt.is_value("ПЕР", None)) or tt.is_value("СМЕНА", None) or tt.is_value("БР", None)) or tt.is_value("РАСТ", None) or tt.is_value("РАСТОРЖЕНИЕ", None)) or tt.is_value("РАСТОРЖ", None)): 
                        tt = tt.next0_
                        while tt is not None: 
                            if (((tt.is_char('.') or tt.is_value("ФАМИЛИЯ", None) or tt.is_value("ФАМ", None)) or tt.is_value("ФАМИЛ", None) or tt.is_value("БР", None)) or tt.is_value("БРАК", None)): 
                                pass
                            else: 
                                break
                            tt = tt.next0_
                        if (isinstance(tt, TextToken)): 
                            ppp = PersonItemToken.try_attach(tt, PersonItemToken.ParseAttr.CANBELOWER, None)
                            if (ppp is not None): 
                                if (ppp.lastname is not None): 
                                    alt_surname = ppp.value
                                    tt = ppp.end_token.next0_
                        if (t.previous is not None and t.previous.previous is not None and ((t.previous.is_hiphen or t.previous.is_char_of("\\/")))): 
                            t = t.previous
                        if (tt is not None): 
                            txt = (txt[0:0+t.previous.end_char + 1] + " " + txt[tt.begin_char:])
                        else: 
                            txt = txt[0:0+t.previous.end_char + 1]
                        ch = True
                        break
                elif (t.next0_ is None and (isinstance(t, TextToken)) and t.begin_char > 10): 
                    mc = t.get_morph_class_in_dictionary()
                    if ((mc.is_proper_secname or mc.is_proper_name or t.previous is None) or t.previous.previous is None): 
                        pass
                    else: 
                        rr = PersonNormalData(None)
                        rr.middlename = t.term
                        PersonNormalHelper.__corr_coef(t, rr, corr)
                        if (t.term in rr.corr_words): 
                            addcorr = rr.corr_words
                            txt = (txt[0:0+t.begin_char] + rr.corr_words[t.term])
                            ch = True
                            break
                if ((isinstance(t, TextToken)) and t.length_char > 10 and t.chars.is_cyrillic_letter): 
                    if ((not t.chars.is_all_lower and not t.chars.is_capital_upper and not t.chars.is_all_upper) and not t.chars.is_last_lower): 
                        str0_ = t.get_source_text()
                        if (str.isupper(str0_[0]) and str.islower(str0_[1])): 
                            ii = 2
                            while ii < (len(str0_) - 4): 
                                if (str.isupper(str0_[ii]) and str.islower(str0_[ii + 1]) and str.isalpha(str0_[ii + 1])): 
                                    s1 = str0_[0:0+ii].upper()
                                    s2 = str0_[ii:].upper()
                                    b1 = MorphologyService.get_word_base_info(s1, MorphLang.RU, True, True)
                                    b2 = MorphologyService.get_word_base_info(s2, MorphLang.RU, True, True)
                                    if (b1.class0_.is_proper and b2.class0_.is_proper): 
                                        txt = (txt[0:0+t.begin_char + ii] + " " + txt[t.begin_char + ii:])
                                        ch = True
                                    break
                                ii += 1
                        if (ch): 
                            break
                if (t.is_hiphen): 
                    okk = False
                    if (t.is_whitespace_before or t.is_whitespace_after): 
                        okk = True
                    elif ((t.previous is not None and t.previous.morph.class0_.is_proper_surname and t.next0_ is not None) and t.next0_.get_morph_class_in_dictionary().is_proper_name): 
                        okk = True
                    if (okk): 
                        txt = (txt[0:0+t.begin_char] + " " + txt[t.end_char + 1:])
                        ch = True
                        break
            if (not ch): 
                break
        max_char = 0
        t = ar.first_token
        while t is not None: 
            if (t.is_value("ДО", None) and t.next0_ is not None and t.next0_.is_value("БРАК", None)): 
                max_char = t.previous.end_char
                break
            elif (t.is_value("БЕЗ", None) and t.next0_ is not None and t.next0_.is_value("ОТЧЕСТВА", None)): 
                max_char = t.previous.end_char
                break
            elif (t.is_value("БЕЗ", None) and t.next0_ is not None and t.next0_.is_value("СОГЛАСИЕ", None)): 
                return
            elif (t.is_value("ПО", None) and t.next0_ is not None and t.next0_.is_value("ДАННЫЙ", None)): 
                max_char = t.previous.end_char
                break
            elif ((isinstance(t, TextToken)) and t.next0_ is None): 
                term = t.term
                if (((term == "НЕТ" or term == "РФ" or term == "РОССИЯ") or term == "МУЖ" or term == "ЖЕН") or term == "ЖЕНА"): 
                    max_char = t.previous.end_char
                    break
            t = t.next0_
        cou = 0
        dict0_ = 0
        prdict = 0
        props = 0
        org0_ = 0
        tt = ar.first_token
        first_pass4400 = True
        while True:
            if first_pass4400: first_pass4400 = False
            else: tt = tt.next0_
            if (not (tt is not None)): break
            if (max_char > 0 and tt.begin_char > max_char): 
                break
            cou += 1
            ait = AddressItemToken.try_parse_pure_item(tt, None, None)
            if (ait is not None): 
                if (ait.typ == AddressItemType.NUMBER or ait.value is None): 
                    continue
                res.res_typ = PersonNormalResult.NOTPERSON
                res.error_message = "Похоже на просто текст"
                return
            mc = tt.get_morph_class_in_dictionary()
            if (mc.is_proper_name or mc.is_proper_secname or mc.is_proper_surname): 
                prdict = 0
                dict0_ -= 1
                props += 1
                continue
            oty = tt.kit.process_referent("ORGANIZATION", tt, "TYPE")
            if (oty is not None and oty.end_token.is_value("ОЛИМПИАДА", None)): 
                oty = (None)
            if (oty is not None): 
                org0_ += 1
                if (oty.begin_token != oty.end_token or tt == ar.first_token): 
                    ok = False
                    if (oty.end_token.next0_ is not None): 
                        mc1 = oty.end_token.next0_.get_morph_class_in_dictionary()
                        if (mc1.is_proper_name or mc1.is_proper_secname): 
                            ok = True
                    if (not ok): 
                        res.error_message = "Похоже на организацию"
                        return
            if (BracketHelper.is_bracket(tt, True)): 
                if (org0_ > 0): 
                    res.error_message = "Похоже на организацию"
                    return
            if (tt.length_char >= 2 and ((tt.chars.is_all_lower or not mc.is_undefined))): 
                if (not tt.chars.is_all_lower and (tt.length_char < 5)): 
                    pass
                elif (mc.is_adjective): 
                    prdict += 1
                    dict0_ += prdict
            else: 
                prdict = 0
        if (dict0_ > (cou - 2) or ((dict0_ == 2 and (cou < 5))) or dict0_ >= 3): 
            if (props < 2): 
                return
        if (org0_ > 1 or ((org0_ > 0 and (props < 1)))): 
            res.error_message = "Похоже на организацию"
            return
        t0 = ar.first_token
        tt = t0
        first_pass4401 = True
        while True:
            if first_pass4401: first_pass4401 = False
            else: tt = tt.next0_
            if (not (tt is not None)): break
            if (max_char > 0 and tt.begin_char > max_char): 
                break
            if (tt.length_char == 1 and not tt.chars.is_letter): 
                t0 = tt.next0_
                continue
            if (not tt.chars.is_all_lower): 
                break
            mc = tt.get_morph_class_in_dictionary()
            if (mc.is_proper): 
                break
            if (mc.is_undefined): 
                if (tt.next0_ is not None and tt.next0_.is_char_of(",.")): 
                    pass
                else: 
                    break
            break
        attrs = ((PersonItemToken.ParseAttr.CANBELOWER) | (PersonItemToken.ParseAttr.MUSTBEITEMALWAYS) | (PersonItemToken.ParseAttr.NOMINATIVECASE)) | (PersonItemToken.ParseAttr.SURNAMEPREFIXNOTMERGE)
        pits = PersonItemToken.try_attach_list(t0, Utils.valToEnum(attrs, PersonItemToken.ParseAttr), 10)
        tt0 = ar.first_token
        if (pits is not None and len(pits) > 0): 
            pit = pits[len(pits) - 1]
            tt0 = pit.end_token.next0_
            if (tt0 is not None and pit.chars.is_all_lower and len(pits) > 1): 
                tt0 = pit.begin_token
                del pits[len(pits) - 1]
        else: 
            pits = list()
        coef_mult = 1
        while tt0 is not None: 
            if (max_char > 0 and tt0.begin_char > max_char): 
                break
            coef_mult *= 0.9
            pits1 = PersonItemToken.try_attach_list(tt0, Utils.valToEnum((PersonItemToken.ParseAttr.NOMINATIVECASE) | (PersonItemToken.ParseAttr.SURNAMEPREFIXNOTMERGE), PersonItemToken.ParseAttr), 10)
            if (pits1 is not None and len(pits) > 0): 
                pits.extend(pits1)
                tt0 = pits1[len(pits1) - 1].end_token
            tt0 = tt0.next0_
        if (len(pits) > 15): 
            return
        if (pits is not None and len(pits) > 3): 
            i = 0
            while i < (len(pits) - 3): 
                if (pits[i].value == pits[i + 2].value and pits[i + 1].value == pits[i + 3].value): 
                    pits[i + 1].end_token = pits[i + 3].end_token
                    del pits[i + 3]
                    del pits[i + 2]
                    i -= 1
                i += 1
            i = 0
            while i < (len(pits) - 2): 
                if (pits[i].value == "{0}-{1}".format(pits[i + 1].value, pits[i + 2].value)): 
                    pits[i + 1].begin_token = pits[i].begin_token
                    del pits[i]
                    i -= 1
                i += 1
            i = 0
            while i < (len(pits) - 2): 
                if (pits[i + 2].value == "{0}-{1}".format(pits[i].value, pits[i + 1].value)): 
                    pits[i + 1].end_token = pits[i + 2].end_token
                    del pits[i + 2]
                    i -= 1
                i += 1
            i = 0
            while i < (len(pits) - 1): 
                if (pits[i].value == pits[i + 1].value): 
                    pits[i].end_token = pits[i + 1].end_token
                    del pits[i + 1]
                    i -= 1
                i += 1
        if (pits is not None): 
            coef = PersonNormalHelper.__m_fio.process(pits)
            ciof = PersonNormalHelper.__m_iof.process(pits)
            ciof *= 0.9
            pn = PersonNormalHelper.__m_fio
            if (coef < ciof): 
                pn = PersonNormalHelper.__m_iof
                coef = ciof
            if (coef > 0.3): 
                pn.create_result(res)
                res.coef = (math.floor((coef * (100) * coef_mult)))
                res.lastname_alt = alt_surname
                PersonNormalHelper.__corr_coef(ar.first_token, res, corr)
                res.res_typ = PersonNormalResult.OK
                if (res.coef < 60): 
                    res.res_typ = PersonNormalResult.MANUAL
                if (addcorr is not None): 
                    for kp in addcorr.items(): 
                        if (not kp[0] in res.corr_words): 
                            res.corr_words[kp[0]] = kp[1]
                return
        ar = ProcessorService.get_standard_processor().process(SourceOfAnalysis._new3443(txt, co, False), None, None)
        if (ar is not None): 
            tt = ar.first_token
            while tt is not None: 
                r = tt.get_referent()
                if (r is not None and ((r.type_name == "GEO" or r.type_name == "STREET" or r.type_name == "ADDRESS"))): 
                    res.error_message = "Похоже на адрес"
                    return
                if (r is not None and r.type_name == "ORGANIZATION"): 
                    res.error_message = "Похоже на организацию"
                    return
                if (isinstance(r, PersonReferent)): 
                    pr = Utils.asObjectOrNull(r, PersonReferent)
                    res.coef = 100
                    res.res_typ = PersonNormalResult.OK
                    res.firstname = pr.get_string_value(PersonReferent.ATTR_FIRSTNAME)
                    if ((res.firstname) is None): 
                        res.coef = (math.floor((0.8 * (res.coef))))
                    res.middlename = pr.get_string_value(PersonReferent.ATTR_MIDDLENAME)
                    res.lastname = pr.get_string_value(PersonReferent.ATTR_LASTNAME)
                    if ((res.lastname) is None): 
                        res.coef = (math.floor((0.5 * (res.coef))))
                    tt2 = tt.next0_
                    while tt2 is not None: 
                        if ((isinstance(tt2, TextToken)) and not tt2.chars.is_letter): 
                            pass
                        else: 
                            res.coef = (math.floor((0.5 * (res.coef))))
                        tt2 = tt2.next0_
                    tt2 = tt.previous
                    while tt2 is not None: 
                        if ((isinstance(tt2, TextToken)) and not tt2.chars.is_letter): 
                            pass
                        else: 
                            res.coef = (math.floor((0.5 * (res.coef))))
                        tt2 = tt2.previous
                    PersonNormalHelper.__corr_coef(ar.first_token, res, corr)
                    if (res.coef < 90): 
                        res.res_typ = PersonNormalResult.MANUAL
                    return
                tt = tt.next0_
    
    __corr_middname_tails = None
    
    @staticmethod
    def __load_corr_middla_tails(txt : str) -> None:
        PersonNormalHelper.__corr_middname_tails = dict()
        for s in Utils.splitString(txt, '\n', False): 
            line = s.strip().upper()
            i = line.find(":")
            if (i < 0): 
                i = line.find(';')
            if (i < 0): 
                continue
            err = line[0:0+i]
            if (err.endswith("$")): 
                err = err[0:0+len(err) - 1]
            ok = line[i + 1:]
            if (not err in PersonNormalHelper.__corr_middname_tails): 
                PersonNormalHelper.__corr_middname_tails[err] = ok
    
    @staticmethod
    def __corr_coef(t : 'Token', r : 'PersonNormalData', corr : typing.List[tuple]) -> None:
        if (t.kit.corrected_tokens is not None): 
            for kp in t.kit.corrected_tokens.items(): 
                if (isinstance(kp[0], TextToken)): 
                    if (not kp[1] in r.corr_words): 
                        r.corr_words[kp[1]] = kp[0].term
        if (corr is not None): 
            dic = None
            wrapdic3448 = RefOutArgWrapper(None)
            inoutres3449 = Utils.tryGetValue(corr, "name", wrapdic3448)
            dic = wrapdic3448.value
            if (r.firstname is not None and inoutres3449): 
                if (r.firstname in dic): 
                    if (not r.firstname in r.corr_words): 
                        r.corr_words[r.firstname] = dic[r.firstname]
                    r.firstname = dic[r.firstname]
                    r.coef = (math.floor(((r.coef) * 0.95)))
            wrapdic3446 = RefOutArgWrapper(None)
            inoutres3447 = Utils.tryGetValue(corr, "midname", wrapdic3446)
            dic = wrapdic3446.value
            if (r.middlename is not None and inoutres3447): 
                if (r.middlename in dic): 
                    if (not r.middlename in r.corr_words): 
                        r.corr_words[r.middlename] = dic[r.middlename]
                    r.middlename = dic[r.middlename]
                    r.coef = (math.floor(((r.coef) * 0.95)))
            wrapdic3444 = RefOutArgWrapper(None)
            inoutres3445 = Utils.tryGetValue(corr, "lastname", wrapdic3444)
            dic = wrapdic3444.value
            if (r.lastname is not None and inoutres3445): 
                if (r.lastname in dic): 
                    if (not r.lastname in r.corr_words): 
                        r.corr_words[r.lastname] = dic[r.lastname]
                    r.lastname = dic[r.lastname]
                    r.coef = (math.floor(((r.coef) * 0.95)))
        if (r.middlename is not None): 
            for kp in PersonNormalHelper.__corr_middname_tails.items(): 
                if (r.middlename.endswith(kp[0])): 
                    mmm = r.middlename
                    r.middlename = r.middlename[0:0+len(r.middlename) - len(kp[0])]
                    r.middlename += kp[1]
                    r.coef = (math.floor(((r.coef) * 0.95)))
                    if (not mmm in r.corr_words): 
                        r.corr_words[mmm] = r.middlename
                    break
        if (r.firstname_alt is not None): 
            if (ShortNameHelper.get_names_for_shortname(r.firstname_alt) is None): 
                if (r.firstname_alt != "СВЕТА"): 
                    if (r.firstname in PersonNormalHelper.__m_male_names): 
                        if (not r.firstname_alt in r.corr_words): 
                            r.corr_words[r.firstname_alt] = r.firstname
                    else: 
                        r.firstname = r.firstname_alt
                    r.firstname_alt = (None)
        if (r.firstname is not None): 
            cc = PersonNormalHelper.__corr_dubls_chars(r.firstname, 1, r.gender)
            if (cc is not None): 
                if (not r.firstname in r.corr_words): 
                    r.corr_words[r.firstname] = cc
                r.firstname = cc
                r.coef = (math.floor(((r.coef) * 0.95)))
        if (r.middlename is not None): 
            cc = PersonNormalHelper.__corr_dubls_chars(r.middlename, 2, r.gender)
            if (cc is not None): 
                if (not r.middlename in r.corr_words): 
                    r.corr_words[r.middlename] = cc
                r.middlename = cc
                r.coef = (math.floor(((r.coef) * 0.95)))
            bb = MorphologyService.get_word_base_info(r.middlename, MorphLang.RU, True, True)
            if (not bb.class0_.is_proper_secname and (r.middlename.find('-') < 0)): 
                cc = MorphologyService.correct_word(r.middlename, None)
                if (cc is not None): 
                    if (((r.gender == 1 and cc in PersonNormalHelper.__m_male_secnames)) or ((r.gender == 2 and cc in PersonNormalHelper.__m_female_secnames))): 
                        if (not r.middlename in r.corr_words): 
                            r.corr_words[r.middlename] = cc
                        r.middlename = cc
                        r.coef = (math.floor(((r.coef) * 0.95)))
        if (r.lastname is not None): 
            glas = 0
            for ch in r.lastname: 
                if (LanguageHelper.is_cyrillic_vowel(ch)): 
                    glas += 1
            if (glas == 0): 
                r.coef = (math.floor(((r.coef) * 0.8)))
            elif (glas > 2 and glas == len(r.lastname)): 
                r.coef = (math.floor(((r.coef) * 0.6)))
            cc = PersonNormalHelper.__corr_dubls_chars(r.lastname, 3, r.gender)
            if (cc is not None): 
                if (not r.lastname in r.corr_words): 
                    r.corr_words[r.lastname] = cc
                r.lastname = cc
                r.coef = (math.floor(((r.coef) * 0.95)))
    
    @staticmethod
    def __corr_dubls_chars(str0_ : str, typ : int, gender : int) -> str:
        if (len(str0_) < 4): 
            return None
        cou = 0
        for i in range(len(str0_) - 2, 0, -1):
            if (str0_[i] != str0_[len(str0_) - 1]): 
                if (cou >= 2): 
                    res = str0_[0:0+i + 2]
                    bb = MorphologyService.get_word_base_info(res, MorphLang.RU, True, True)
                    if (bb.class0_.is_proper): 
                        return res
                    res0 = res[0:0+len(res) - 1]
                    bb = MorphologyService.get_word_base_info(res0, MorphLang.RU, True, True)
                    if (bb.class0_.is_proper): 
                        return res0
                    return res
                break
            else: 
                cou += 1
        if (typ == 3): 
            return None
        if (typ == 2): 
            if (gender == 1 and not str0_.endswith("ИЧ")): 
                str1 = str0_[0:0+len(str0_) - 1]
                if (str1.endswith("ИЧ")): 
                    return str1
        bb0 = MorphologyService.get_word_base_info(str0_, MorphLang.RU, True, True)
        if (typ == 1 and LanguageHelper.is_cyrillic_vowel(str0_[len(str0_) - 1])): 
            str1 = str0_ + "Й"
            if (str1 in PersonNormalHelper.__m_male_names): 
                return str1
        if (bb0.class0_.is_proper): 
            return None
        str0 = str0_[0:0+len(str0_) - 1]
        bb0 = MorphologyService.get_word_base_info(str0, MorphLang.RU, True, True)
        if (bb0.class0_.is_proper_name and typ == 1 and bb0.number == MorphNumber.SINGULAR): 
            if (str0[len(str0) - 1] == str0_[len(str0_) - 1]): 
                return str0
        if (bb0.class0_.is_proper_secname and typ == 2 and bb0.number == MorphNumber.SINGULAR): 
            return str0
        return None
    
    __m_male_names = None
    
    __m_male_secnames = None
    
    __m_female_secnames = None
    
    __m_fio = None
    
    __m_iof = None
    
    @staticmethod
    def initialize() -> None:
        PersonNormalHelper.__load_corr_middla_tails("слаовна$:славовна\nславона$:славовна\nславоич$:славович\nслаович$:славович\nвнана$:вна\nевана$:евна\nевнва$:евна\nевнаа$:евна\nевнна$:евна\nована$:овна\nовнва$:овна\nовнаа$:овна\nовнна$:овна\nевена$:евна\nевсна$:евна\nевона$:евна\nовена$:овна\nовсна$:овна\nовона$:овна\nовоич$:ович\nевича$:евич\nевичч$:евич\nевивч$:евич\nевиич$:евич\nеваич$:евич\nевнич$:евич\nовича$:ович\nовичч$:ович\nовивч$:ович\nовиич$:ович\nоваич$:ович\nовнич$:ович\nеана$:евна\nенва$:евна\nевнв$:евна\nевне$:евна\nевну$:евна\nевны$:евна\nевеа$:евна\nеван$:евна\nоана$:овна\nонва$:овна\nовнв$:овна\nовне$:овна\nовну$:овна\nовны$:овна\nовеа$:овна\nован$:овна\nевга$:евна\nовга$:овна\nвеич$:евич\nвоич$:ович\nивоч$:ович\nовоч$:ович\nевия$:евич\nевмч$:евич\nеивч$:евич\nеаич$:евич\nивеч$:евич\nевоч$:евич\nовия$:ович\nовмч$:ович\nоивч$:ович\nеаич$:ович\nевн$:евна\nена$:евна\nовн$:овна\nона$:овна\nеич$:евич\nови$:ович\nоич$:ович\nевч$:евич\nеви$:евич\nовч$:ович\nови$:ович\nишна$:ична\nофич$:ович\nефич$:евич\nева$:евна\nова$:овна")
    
    # static constructor for class PersonNormalHelper
    @staticmethod
    def _static_ctor():
        PersonNormalHelper.__m_male_names = list(["АНДРЕЙ", "АЛЕКСЕЙ", "АЛЕКСАНДР", "АНАТОЛИЙ", "АРКАДИЙ", "АРТЕМ", "БОРИС", "ВАЛЕНТИН", "ВАЛЕРИЙ", "ВАСИЛИЙ", "ВИКТОР", "ВИТАЛИЙ", "ВЛАДИМИР", "ВЛАДИСЛАВ", "ВСЕВОЛОД", "ГЕННАДИЙ", "ГЕНАДИЙ", "ГЕОРГИЙ", "ГРИГОРИЙ", "ДЕНИС", "ДМИТРИЙ", "ЕВГЕНИЙ", "ЕГОР", "ИВАН", "ИГОРЬ", "КИРИЛЛ", "КОНСТАНТИН", "ЛЕОНИД", "ЛЕВ", "МАКСИМ", "МИХАИЛ", "НИКОЛАЙ", "НИКИТА", "ОЛЕГ", "ПАВЕЛ", "ПЕТР", "РОМАН", "СЕМЕН", "СЕРГЕЙ", "СТАНИСЛАВ", "СТЕПАН", "ФЕДОР", "ЭДУАРД", "ЮРИЙ", "ЯКОВ", "ЯРОСЛАВ"])
        PersonNormalHelper.__m_male_secnames = list(["АЛЕКСЕЕВИЧ", "АЛЕКСАНДРОВИЧ", "АНАТОЛЬЕВИЧ", "АЛЬБЕРТОВИЧ", "АНДРЕЕВИЧ", "АРКАДЬЕВИЧ", "АРТЕМОВИЧ", "БОРИСОВИЧ", "ВАЛЕНТИНОВИЧ", "ВАЛЕРЬЕВИЧ", "ВАСИЛЬЕВИЧ", "ВИКТОРОВИЧ", "ВИТАЛЬЕВИЧ", "ВЛАДИМИРОВИЧ", "ВЛАДИСЛАВОВИЧ", "ВСЕВОЛОДОВИЧ", "ГЕННАДИЕВИЧ", "ГЕНАДИЕВИЧ", "ГЕОРГИЕВИЧ", "ГРИГОРЬЕВИЧ", "ДЕНИСОВИЧ", "ДМИТРИЕВИЧ", "ЕВГЕНЬЕВИЧ", "ЕГОРОВИЧ", "ИВАНОВИЧ", "ИГОРЕВИЧ", "ИЛЬИЧ", "КИРИЛЛОВИЧ", "КОНСТАНТИНОВИЧ", "ЛЕОНИДОВИЧ", "ЛЬВОВИЧ", "МАКСИМОВИЧ", "МИХАЙЛОВИЧ", "НИКОЛАЕВИЧ", "НИКИТИЧ", "ОЛЕГОВИЧ", "ПАВЛОВИЧ", "ПЕТРОВИЧ", "РОМАНОВИЧ", "СЕМЕНОВИЧ", "СЕРГЕЕВИЧ", "СТАНИСЛАВОВИЧ", "СТЕПАНОВИЧ", "ФЕДОРОВИЧ", "ЭДУАРДОВИЧ", "ЮРЬЕВИЧ", "ЯКОВЛЕВИЧ", "ЯРОСЛАВОВИЧ"])
        PersonNormalHelper.__m_female_secnames = list(["АЛЕКСЕЕВНА", "АЛЕКСАНДРОВНА", "АНАТОЛЬЕВНА", "АЛЬБЕРТОВНА", "АНДРЕЕВНА", "АРКАДЬЕВНА", "АРТЕМОВНА", "БОРИСОВНА", "ВАЛЕНТИНОВНА", "ВАЛЕРЬЕВНА", "ВАСИЛЬЕВНА", "ВИКТОРОВНА", "ВИТАЛЬЕВНА", "ВЛАДИМИРОВНА", "ВЛАДИСЛАВОВНА", "ВСЕВОЛОДОВНА", "ГЕННАДИЕВНА", "ГЕНАДИЕВНА", "ГЕОРГИЕВНА", "ГРИГОРЬЕВНА", "ДЕНИСОВНА", "ДМИТРИЕВНА", "ЕВГЕНЬЕВНА", "ЕГОРОВНА", "ИВАНОВНА", "ИГОРЕВНА", "ИЛЬИНИЧНА", "КИРИЛЛОВНА", "КОНСТАНТИНОВНА", "ЛЕОНИДОВНА", "ЛЬВОВНА", "МАКСИМОВНА", "МИХАЙЛОВНА", "НИКОЛАЕВНА", "НИКИТИЧНА", "ОЛЕГОВНА", "ПАВЛОВНА", "ПЕТРОВНА", "РОМАНОВНА", "СЕМЕНОВНА", "СЕРГЕЕВНА", "СТАНИСЛАВОВНА", "СТЕПАНОВНА", "ФЕДОРОВНА", "ЭДУАРДОВНА", "ЮРЬЕВНА", "ЯКОВЛЕВНА", "ЯРОСЛАВОВНА"])
        PersonNormalHelper.__m_fio = PersonNormalNode(False)
        PersonNormalHelper.__m_iof = PersonNormalNode(True)

PersonNormalHelper._static_ctor()