##
# File: BaseCSParserListener.py
# Date: 08-Apr-2025
#
# Updates:
""" ParserLister base class for any chemical shift file.
    @author: Masashi Yokochi
"""
__docformat__ = "restructuredtext en"
__author__ = "Masashi Yokochi"
__email__ = "yokochi@protein.osaka-u.ac.jp"
__license__ = "Apache License 2.0"
__version__ = "1.1.1"

import collections
import copy
import functools
import sys
from typing import IO, List, Optional, Tuple, Union

try:
    from wwpdb.utils.nmr.NmrDpConstant import (LARGE_ASYM_ID,
                                               EMPTY_VALUE,
                                               STD_MON_DICT,
                                               ELEMENT_SYMBOLS,
                                               PROTON_BEGIN_CODE,
                                               PSE_PRO_BEGIN_CODE,
                                               ZINC_ION_CODE,
                                               CALCIUM_ION_CODE,
                                               MAX_PREF_LABEL_SCHEME_COUNT,
                                               PERIPH_OFFSET_ATTEMPT,
                                               MAX_ALLOWED_EXT_SEQ,
                                               UNREAL_AUTH_SEQ_NUM,
                                               MAX_CONFLICT_ATTEMPT,
                                               CS_RESTRAINT_RANGE,
                                               CS_RESTRAINT_ERROR,
                                               WEIGHT_RANGE,
                                               CS_RANGE_MIN,
                                               CS_RANGE_MAX,
                                               CS_ERROR_MIN,
                                               CS_ERROR_MAX,
                                               WEIGHT_RANGE_MIN,
                                               WEIGHT_RANGE_MAX,
                                               ASSIGNMENT_SEPARATOR_PAT,
                                               ASSIGNMENT_RESID_PAT,
                                               ASSIGNMENT_HALF_SPIN_NUCLEUS)
    from wwpdb.utils.nmr.AlignUtil import (deepcopy,
                                           getOneLetterCode,
                                           updatePolySeqRst,
                                           revertPolySeqRst,
                                           sortPolySeqRst,
                                           syncCompIdOfPolySeqRst,
                                           alignPolymerSequence,
                                           alignPolymerSequenceWithConflicts,
                                           assignPolymerSequence,
                                           trimSequenceAlignment,
                                           updatePolySeqRstAmbig,
                                           retrieveRemappedSeqId,
                                           retrieveRemappedSeqIdAndCompId,
                                           splitPolySeqRstForExactNoes,
                                           retrieveRemappedChainId,
                                           retrieveRemappedNonPoly)
    from wwpdb.utils.nmr.nef.NefTranslator import NefTranslator
    from wwpdb.utils.nmr.mr.ParserListenerUtil import (translateToStdResName,
                                                       translateToStdAtomName,
                                                       translateToStdAtomNameNoRef,
                                                       translateToStdAtomNameWithRef,
                                                       backTranslateFromStdResName,
                                                       guessCompIdFromAtomId,
                                                       getRestraintName,
                                                       contentSubtypeOf,
                                                       incListIdCounter,
                                                       decListIdCounter,
                                                       getSaveframe,
                                                       getLoop,
                                                       getCsRow)
except ImportError:
    from nmr.NmrDpConstant import (LARGE_ASYM_ID,
                                   EMPTY_VALUE,
                                   STD_MON_DICT,
                                   ELEMENT_SYMBOLS,
                                   PROTON_BEGIN_CODE,
                                   PSE_PRO_BEGIN_CODE,
                                   ZINC_ION_CODE,
                                   CALCIUM_ION_CODE,
                                   MAX_PREF_LABEL_SCHEME_COUNT,
                                   PERIPH_OFFSET_ATTEMPT,
                                   MAX_ALLOWED_EXT_SEQ,
                                   UNREAL_AUTH_SEQ_NUM,
                                   MAX_CONFLICT_ATTEMPT,
                                   CS_RESTRAINT_RANGE,
                                   CS_RESTRAINT_ERROR,
                                   WEIGHT_RANGE,
                                   CS_RANGE_MIN,
                                   CS_RANGE_MAX,
                                   CS_ERROR_MIN,
                                   CS_ERROR_MAX,
                                   WEIGHT_RANGE_MIN,
                                   WEIGHT_RANGE_MAX,
                                   ASSIGNMENT_SEPARATOR_PAT,
                                   ASSIGNMENT_RESID_PAT)
    from nmr.AlignUtil import (deepcopy,
                               getOneLetterCode,
                               updatePolySeqRst,
                               revertPolySeqRst,
                               sortPolySeqRst,
                               syncCompIdOfPolySeqRst,
                               alignPolymerSequence,
                               alignPolymerSequenceWithConflicts,
                               assignPolymerSequence,
                               trimSequenceAlignment,
                               updatePolySeqRstAmbig,
                               retrieveRemappedSeqId,
                               retrieveRemappedSeqIdAndCompId,
                               splitPolySeqRstForExactNoes,
                               retrieveRemappedChainId,
                               retrieveRemappedNonPoly)
    from nmr.nef.NefTranslator import NefTranslator
    from nmr.mr.ParserListenerUtil import (translateToStdResName,
                                           translateToStdAtomName,
                                           translateToStdAtomNameNoRef,
                                           translateToStdAtomNameWithRef,
                                           backTranslateFromStdResName,
                                           guessCompIdFromAtomId,
                                           getRestraintName,
                                           contentSubtypeOf,
                                           incListIdCounter,
                                           decListIdCounter,
                                           getSaveframe,
                                           getLoop,
                                           getCsRow)


class BaseCSParserListener():
    """ ParserLister base class for any chemical shift file.
    """
    __slots__ = ('polySeq',
                 'entityAssembly',
                 'ccU',
                 'hasPolySeq',
                 'labelToAuthSeq',
                 'authToLabelSeq',
                 'chainIdSet',
                 'compIdSet',
                 'cyanaCompIdSet',
                 'polyPeptide',
                 'polyDeoxyribonucleotide',
                 'polyRibonucleotide',
                 'csStat',
                 'nefT',
                 'pA',
                 'reasons',
                 '__preferAuthSeqCount',
                 '__preferLabelSeqCount',
                 'reasonsForReParsing',
                 'chemShifts',
                 'sfDict',
                 '__cachedDictForStarAtom',
                 'chainNumberDict',
                 'offset',
                 'polySeqCs',
                 'polySeqCsFailed',
                 'polySeqCsFailedAmbig',
                 'compIdMap',
                 'f')

    file_type = None
    software_name = None

    __debug = False
    __verbose_debug = False

    __createSfDict = False

    # data item names for auth_asym_id, auth_seq_id, auth_atom_id in 'atom_site' category
    __authSeqId = None

    __shiftNonPosSeq = None
    __defaultSegId = None
    __defaultSegId__ = None

    __preferAuthSeq = True
    __extendAuthSeq = False

    seqAlign = None
    chainAssign = None

    # current subtype
    cur_subtype = ''
    cur_list_id = -1
    cur_line_num = -1

    # whether to allow extended sequence temporary
    __allow_ext_seq = False

    # whether current assignment derived not from unreliable extra comment
    no_extra_comment = False

    # collection of atom selection set for multiple assignments
    atomSelectionSets = []

    # collection of atom selection
    atomSelectionSet = []

    # collection of any selection
    anySelection = []

    warningMessage = None

    # original source MR file name
    __originalFileName = '.'

    # list id counter
    __listIdCounter = {}

    # reserved list ids for NMR data remediation Phase 2
    __reservedListIds = {}

    # entry ID
    __entryId = '.'

    # default saveframe name for error handling
    __def_err_sf_framecode = None

    def __init__(self, verbose: bool = True, log: IO = sys.stdout,  # pylint: disable=unused-argument
                 polySeq: List[dict] = None, entityAssembly: Optional[dict] = None,
                 nefT: NefTranslator = None,
                 reasons: Optional[dict] = None) -> None:

        self.polySeq = polySeq
        # key=Entity_assembly_ID (str), value=dictionary of 'entity_id' (int) and 'auth_asym_id' (str)
        self.entityAssembly = entityAssembly

        self.nefT = nefT
        self.ccU = nefT.ccU
        self.csStat = nefT.csStat
        self.pA = nefT.pA

        self.hasPolySeq = self.polySeq is not None and len(self.polySeq) > 0

        self.polyPeptide = False
        self.polyDeoxyribonucleotide = False
        self.polyRibonucleotide = False

        if self.hasPolySeq:
            self.labelToAuthSeq = {}
            for ps in self.polySeq:
                chainId = ps['chain_id']
                authChainId = ps['auth_chain_id' if 'auth_chain_id' in ps else 'chain_id']
                if 'auth_seq_id' not in ps:
                    for seqId in ps['seq_id']:
                        self.labelToAuthSeq[(chainId, seqId)] = (authChainId, seqId)
                else:
                    for seqId, authSeqId in zip(ps['seq_id'], ps['auth_seq_id']):
                        self.labelToAuthSeq[(chainId, seqId)] = (authChainId, authSeqId)
            self.authToLabelSeq = {v: k for k, v in self.labelToAuthSeq.items()}

            self.chainIdSet = set(ps['chain_id'] for ps in self.polySeq)
            self.compIdSet = set()

            def is_data(array: list) -> bool:
                return not any(True for d in array if d in EMPTY_VALUE)

            for ps in self.polySeq:
                self.compIdSet.update(set(filter(is_data, ps['comp_id'])))

            for compId in self.compIdSet:
                if compId in STD_MON_DICT:
                    if len(compId) == 3:
                        self.polyPeptide = True
                    elif len(compId) == 2 and compId.startswith('D'):
                        self.polyDeoxyribonucleotide = True
                    elif len(compId) == 1:
                        self.polyRibonucleotide = True

        else:
            self.labelToAuthSeq = None
            self.authToLabelSeq = None

            self.chainIdSet = set()
            self.compIdSet = set(STD_MON_DICT.keys())

        self.cyanaCompIdSet = set()
        for compId in self.compIdSet:
            self.cyanaCompIdSet |= backTranslateFromStdResName(compId)

        # reasons for re-parsing request from the previous trial
        self.reasons = reasons
        self.__preferAuthSeqCount = 0
        self.__preferLabelSeqCount = 0

        self.reasonsForReParsing = {}  # reset to prevent interference from the previous run

        self.chemShifts = 0

        self.sfDict = {}  # dictionary of pynmrstar saveframes

        self.__cachedDictForStarAtom = {}

        self.chainNumberDict = {}
        self.offset = {}

        self.polySeqCs = []
        self.polySeqCsFailed = []
        self.polySeqCsFailedAmbig = []
        self.compIdMap = {}

        self.f = []

    @property
    def debug(self) -> bool:
        """ Retrieve debug mode.
        """

        return self.__debug

    @debug.setter
    def debug(self, debug: bool) -> None:
        self.__debug = debug

    @property
    def verbose_debug(self) -> bool:
        """ Retrieve vebose debug mode.
        """

        return self.__verbose_debug

    @verbose_debug.setter
    def verbose_debug(self, verbose_debug: bool) -> None:
        self.__verbose_debug = verbose_debug

    @property
    def createSfDict(self) -> bool:
        """ Whether to create saveframe dictionary.
        """

        return self.__createSfDict

    @createSfDict.setter
    def createSfDict(self, createSfDict: bool) -> None:
        self.__createSfDict = createSfDict

    @property
    def originalFileName(self) -> str:
        """ Retrieve the original file name.
        """

        return self.__originalFileName

    @originalFileName.setter
    def originalFileName(self, originalFileName: str) -> None:
        self.__originalFileName = originalFileName

    @property
    def listIdCounter(self) -> dict:
        """ Retrieve list ID counter dictionary.
        """

        return self.__listIdCounter

    @listIdCounter.setter
    def listIdCounter(self, listIdCounter: dict) -> None:
        self.__listIdCounter = listIdCounter

    @property
    def reservedListIds(self) -> dict:
        """ Retrieve reserved list IDs.
        """

        return self.__reservedListIds

    @reservedListIds.setter
    def reservedListIds(self, reservedListIds: dict) -> None:
        self.__reservedListIds = reservedListIds

    @property
    def entryId(self) -> str:
        """ Retrieve entry ID.
        """

        return self.__entryId

    @entryId.setter
    def entryId(self, entryId: str) -> None:
        self.__entryId = entryId

    def exit(self) -> None:
        """ Common function to exit a parse tree.
        """

        try:

            if self.hasPolySeq and self.polySeqCs is not None:
                sortPolySeqRst(self.polySeqCs,
                               None if self.reasons is None else self.reasons.get('non_poly_remap'))

                self.seqAlign, _ = alignPolymerSequence(self.pA, self.polySeq, self.polySeqCs,
                                                        resolvedMultimer=self.reasons is not None)
                self.chainAssign, message = assignPolymerSequence(self.pA, self.ccU, self.file_type,
                                                                  self.polySeq, self.polySeqCs, self.seqAlign)

                if len(self.seqAlign) == 0:
                    for c in range(1, MAX_CONFLICT_ATTEMPT):
                        self.seqAlign, _ = alignPolymerSequenceWithConflicts(self.pA, self.polySeq, self.polySeqCs, c)
                        if len(self.seqAlign) > 0:
                            self.chainAssign, message = assignPolymerSequence(self.pA, self.ccU, self.file_type,
                                                                              self.polySeq, self.polySeqCs, self.seqAlign)
                            break

                if len(message) > 0:
                    self.f.extend(message)

                if self.chainAssign is not None:

                    if len(self.polySeq) == len(self.polySeqCs):

                        chain_mapping = {}

                        for ca in self.chainAssign:
                            ref_chain_id = ca['ref_chain_id']
                            test_chain_id = ca['test_chain_id']

                            if ref_chain_id != test_chain_id:
                                chain_mapping[test_chain_id] = ref_chain_id

                        if len(chain_mapping) == len(self.polySeq):

                            for ps in self.polySeqCs:
                                if ps['chain_id'] in chain_mapping:
                                    ps['chain_id'] = chain_mapping[ps['chain_id']]

                            self.seqAlign, _ = alignPolymerSequence(self.pA, self.polySeq, self.polySeqCs,
                                                                    resolvedMultimer=self.reasons is not None)
                            self.chainAssign, _ = assignPolymerSequence(self.pA, self.ccU, self.file_type,
                                                                        self.polySeq, self.polySeqCs, self.seqAlign)

                    trimSequenceAlignment(self.seqAlign, self.chainAssign)

                    if self.reasons is None and any(True for f in self.f
                                                    if '[Atom not found]' in f or '[Sequence mismatch]' in f
                                                    or '[Invalid atom nomenclature]' in f):

                        seqIdRemap = []

                        for ca in self.chainAssign:
                            ref_chain_id = ca['ref_chain_id']
                            test_chain_id = ca['test_chain_id']

                            sa = next(sa for sa in self.seqAlign
                                      if sa['ref_chain_id'] == ref_chain_id
                                      and sa['test_chain_id'] == test_chain_id)

                            poly_seq_model = next(ps for ps in self.polySeq
                                                  if ps['auth_chain_id' if 'auth_chain_id' in ps else 'chain_id'] == ref_chain_id)
                            poly_seq_rst = next(ps for ps in self.polySeqCs
                                                if ps['chain_id'] == test_chain_id)

                            seq_id_mapping = {}
                            offset = None
                            for ref_seq_id, mid_code, test_seq_id in zip(sa['ref_seq_id'], sa['mid_code'], sa['test_seq_id']):
                                if test_seq_id is None:
                                    continue
                                if mid_code == '|':
                                    try:
                                        seq_id_mapping[test_seq_id] =\
                                            next(auth_seq_id for auth_seq_id, seq_id
                                                 in zip(poly_seq_model['auth_seq_id' if 'auth_seq_id' in poly_seq_model
                                                                       else 'seq_id'],
                                                        poly_seq_model['seq_id'])
                                                 if seq_id == ref_seq_id and isinstance(auth_seq_id, int))
                                        if offset is None:
                                            offset = seq_id_mapping[test_seq_id] - test_seq_id
                                    except StopIteration:
                                        pass
                                elif mid_code == ' ' and test_seq_id in poly_seq_rst['seq_id']:
                                    idx = poly_seq_rst['seq_id'].index(test_seq_id)
                                    if poly_seq_rst['comp_id'][idx] == '.' and poly_seq_rst['auth_comp_id'][idx] not in EMPTY_VALUE:
                                        seq_id_mapping[test_seq_id] =\
                                            next(auth_seq_id for auth_seq_id, seq_id
                                                 in zip(poly_seq_model['auth_seq_id' if 'auth_seq_id' in poly_seq_model
                                                                       else 'seq_id'],
                                                        poly_seq_model['seq_id'])
                                                 if seq_id == ref_seq_id and isinstance(auth_seq_id, int))

                            if offset is not None and all(v - k == offset for k, v in seq_id_mapping.items()):
                                test_seq_id_list = list(seq_id_mapping.keys())
                                min_test_seq_id = min(test_seq_id_list)
                                max_test_seq_id = max(test_seq_id_list)
                                for test_seq_id in range(min_test_seq_id + 1, max_test_seq_id):
                                    if test_seq_id not in seq_id_mapping:
                                        seq_id_mapping[test_seq_id] = test_seq_id + offset

                            if any(True for k, v in seq_id_mapping.items() if k != v)\
                               and not any(True for k, v in seq_id_mapping.items()
                                           if v in poly_seq_model['seq_id']
                                           and k == poly_seq_model['auth_seq_id' if 'auth_seq_id' in poly_seq_model else 'seq_id']
                                           [poly_seq_model['seq_id'].index(v)]):
                                offsets = [v - k for k, v in seq_id_mapping.items()]
                                offsets = collections.Counter(offsets).most_common()
                                if len(offsets) == 1:
                                    offset = offsets[0][0]
                                    seq_id_mapping =\
                                        {ref_seq_id - offset: ref_seq_id for ref_seq_id
                                         in poly_seq_model['auth_seq_id' if 'auth_seq_id' in poly_seq_model else 'seq_id']}
                                item = {'chain_id': test_chain_id, 'seq_id_dict': seq_id_mapping}
                                if item not in seqIdRemap:
                                    seqIdRemap.append(item)

                        if len(seqIdRemap) > 0:
                            if 'seq_id_remap' not in self.reasonsForReParsing:
                                self.reasonsForReParsing['seq_id_remap'] = seqIdRemap

                        if len(self.polySeq) == 1 and len(self.polySeqCs) == 1:
                            polySeqCs, chainIdMapping, _ =\
                                splitPolySeqRstForExactNoes(self.pA, self.polySeq, self.polySeqCs,
                                                            self.chainAssign)

                            if polySeqCs is not None:
                                self.polySeqCs = polySeqCs
                                if 'chain_id_clone' not in self.reasonsForReParsing:
                                    self.reasonsForReParsing['chain_id_clone'] = chainIdMapping

                        if len(self.polySeqCsFailed) > 0:
                            sortPolySeqRst(self.polySeqCsFailed)
                            if not any(True for f in self.f if '[Sequence mismatch]' in f):  # 2n6y
                                syncCompIdOfPolySeqRst(self.polySeqCsFailed, self.compIdMap)  # 2mx9

                            seqAlignFailed, _ = alignPolymerSequence(self.pA, self.polySeq, self.polySeqCsFailed)
                            chainAssignFailed, _ = assignPolymerSequence(self.pA, self.ccU, self.file_type,
                                                                         self.polySeq, self.polySeqCsFailed, seqAlignFailed)

                            if chainAssignFailed is not None:
                                seqIdRemapFailed = []

                                uniq_ps = not any(True for ps in self.polySeq if 'identical_chain_id' in ps)

                                for ca in chainAssignFailed:
                                    if ca['conflict'] > 0:
                                        continue
                                    ref_chain_id = ca['ref_chain_id']
                                    test_chain_id = ca['test_chain_id']

                                    sa = next((sa for sa in seqAlignFailed
                                               if sa['ref_chain_id'] == ref_chain_id
                                               and sa['test_chain_id'] == test_chain_id), None)

                                    if sa is None:
                                        continue

                                    poly_seq_model =\
                                        next(ps for ps in self.polySeq
                                             if ps['auth_chain_id' if 'auth_chain_id' in ps else 'chain_id'] == ref_chain_id)

                                    seq_id_mapping = {}
                                    for ref_seq_id, mid_code, test_seq_id\
                                            in zip(sa['ref_seq_id'], sa['mid_code'], sa['test_seq_id']):
                                        if test_seq_id is None:
                                            continue
                                        if mid_code == '|':
                                            try:
                                                seq_id_mapping[test_seq_id] =\
                                                    next(auth_seq_id for auth_seq_id, seq_id
                                                         in zip(poly_seq_model['auth_seq_id' if 'auth_seq_id' in poly_seq_model
                                                                               else 'seq_id'],
                                                                poly_seq_model['seq_id'])
                                                         if seq_id == ref_seq_id and isinstance(auth_seq_id, int))
                                            except StopIteration:
                                                if uniq_ps:
                                                    seq_id_mapping[test_seq_id] = ref_seq_id

                                    offset = None
                                    offsets = [v - k for k, v in seq_id_mapping.items()]
                                    if len(offsets) > 0\
                                       and ('gap_in_auth_seq' not in poly_seq_model or not poly_seq_model['gap_in_auth_seq']):
                                        offsets = collections.Counter(offsets).most_common()
                                        if len(offsets) > 1:
                                            offset = offsets[0][0]
                                            for k, v in seq_id_mapping.items():
                                                if v - k != offset:
                                                    seq_id_mapping[k] = k + offset

                                    if uniq_ps and offset is not None and len(seq_id_mapping) > 0\
                                       and ('gap_in_auth_seq' not in poly_seq_model or not poly_seq_model['gap_in_auth_seq']):
                                        for ref_seq_id, mid_code, test_seq_id, ref_code, test_code\
                                                in zip(sa['ref_seq_id'], sa['mid_code'], sa['test_seq_id'],
                                                       sa['ref_code'], sa['test_code']):
                                            if test_seq_id is None:
                                                continue
                                            if mid_code == '|' and test_seq_id not in seq_id_mapping:
                                                seq_id_mapping[test_seq_id] = test_seq_id + offset
                                            elif ref_code != '.' and test_code == '.':
                                                seq_id_mapping[test_seq_id] = test_seq_id + offset

                                    if any(True for k, v in seq_id_mapping.items() if k != v)\
                                       and not any(True for k, v in seq_id_mapping.items()
                                                   if v in poly_seq_model['seq_id']
                                                   and k == poly_seq_model['auth_seq_id' if 'auth_seq_id' in poly_seq_model
                                                                           else 'seq_id']
                                                   [poly_seq_model['seq_id'].index(v)]):
                                        offsets = [v - k for k, v in seq_id_mapping.items()]
                                        offsets = collections.Counter(offsets).most_common()
                                        if len(offsets) == 1:
                                            offset = offsets[0][0]
                                            seq_id_mapping = {ref_seq_id - offset: ref_seq_id for ref_seq_id
                                                              in poly_seq_model['auth_seq_id' if 'auth_seq_id' in poly_seq_model
                                                                                else 'seq_id']}
                                        item = {'chain_id': ref_chain_id,
                                                'seq_id_dict': seq_id_mapping,
                                                'comp_id_set': list(set(poly_seq_model['comp_id']))}
                                        if item not in seqIdRemapFailed:
                                            seqIdRemapFailed.append(item)

                                if len(seqIdRemapFailed) > 0:
                                    if 'chain_seq_id_remap' not in self.reasonsForReParsing:
                                        seqIdRemap =\
                                            self.reasonsForReParsing['seq_id_remap'] if 'seq_id_remap' in self.reasonsForReParsing\
                                            else []
                                        if len(seqIdRemap) != len(seqIdRemapFailed)\
                                           or seqIdRemap[0]['chain_id'] != seqIdRemapFailed[0]['chain_id']\
                                           or not all(src_seq_id in seqIdRemap[0] for src_seq_id in seqIdRemapFailed[0]):
                                            self.reasonsForReParsing['chain_seq_id_remap'] = seqIdRemapFailed

                                else:
                                    for ps in self.polySeqCsFailed:
                                        for ca in self.chainAssign:
                                            ref_chain_id = ca['ref_chain_id']
                                            test_chain_id = ca['test_chain_id']

                                            if test_chain_id != ps['chain_id']:
                                                continue

                                            sa = next(sa for sa in self.seqAlign
                                                      if sa['ref_chain_id'] == ref_chain_id
                                                      and sa['test_chain_id'] == test_chain_id)

                                            if len(sa['test_seq_id']) != len(sa['ref_seq_id']):
                                                continue

                                            poly_seq_model = next(ps for ps in self.polySeq
                                                                  if ps['auth_chain_id' if 'auth_chain_id' in ps
                                                                        else 'chain_id'] == ref_chain_id)

                                            seq_id_mapping, comp_id_mapping = {}, {}

                                            for seq_id, comp_id in zip(ps['seq_id'], ps['comp_id']):
                                                if seq_id in sa['test_seq_id']:
                                                    idx = sa['test_seq_id'].index(seq_id)
                                                    auth_seq_id = sa['ref_seq_id'][idx]
                                                    seq_id_mapping[seq_id] = auth_seq_id
                                                    comp_id_mapping[seq_id] = comp_id
                                            if any(True for k, v in seq_id_mapping.items() if k != v)\
                                               or ('label_seq_scheme' not in self.reasonsForReParsing
                                                   and all(v not in poly_seq_model['auth_seq_id' if 'auth_seq_id' in poly_seq_model
                                                                                   else 'seq_id']
                                                           for v in seq_id_mapping.values())):
                                                seqIdRemapFailed.append({'chain_id': ref_chain_id, 'seq_id_dict': seq_id_mapping,
                                                                         'comp_id_dict': comp_id_mapping})

                                    if len(seqIdRemapFailed) > 0:
                                        if 'ext_chain_seq_id_remap' not in self.reasonsForReParsing:
                                            seqIdRemap = []
                                            if 'seq_id_remap' in self.reasonsForReParsing:
                                                seqIdRemap = self.reasonsForReParsing['seq_id_remap']
                                            if len(seqIdRemap) != len(seqIdRemapFailed)\
                                               or seqIdRemap[0]['chain_id'] != seqIdRemapFailed[0]['chain_id']\
                                               or not all(src_seq_id in seqIdRemap[0] for src_seq_id in seqIdRemapFailed[0]):
                                                self.reasonsForReParsing['ext_chain_seq_id_remap'] = seqIdRemapFailed

        finally:
            self.warningMessage = sorted(list(set(self.f)), key=self.f.index)

            self.translateToStdResNameWrapper.cache_clear()
            self.__getCoordAtomSiteOf.cache_clear()

            translateToStdAtomNameNoRef.cache_clear()
            translateToStdAtomNameWithRef.cache_clear()

    def validateCsValue(self, index: int, pos: float, pos_unc: Optional[float],
                        occupancy: Optional[float] = None,
                        figure_of_merit: Optional[Union[float, int]] = None
                        ) -> Optional[dict]:
        """ Validate range of assigned chemical shift.
        """

        validRange = True
        dstFunc = {}

        if CS_ERROR_MIN < pos < CS_ERROR_MAX:
            dstFunc['position'] = str(pos)
        else:
            validRange = False
            self.f.append(f"[Range value error] {self.getCurrentAssignment(n=index)}"
                          f"The position='{pos}' must be within range {CS_RESTRAINT_ERROR}.")

        if not validRange:
            return None

        if CS_RANGE_MIN <= pos <= CS_RANGE_MAX:
            pass
        else:
            self.f.append(f"[Range value warning] {self.getCurrentAssignment(n=index)}"
                          f"The position='{pos}' should be within range {CS_RESTRAINT_RANGE}.")

        if pos_unc is not None and pos_unc != 0.0:
            dstFunc['position_uncertainty'] = str(pos_unc) if pos_unc > 0.0 else str(abs(pos_unc))

        if occupancy is not None:
            if WEIGHT_RANGE_MIN <= occupancy <= WEIGHT_RANGE_MAX:
                dstFunc['occupancy'] = str(occupancy)
            else:
                self.f.append(f"[Range value warning] {self.getCurrentAssignment(n=index)}"
                              f"The occupancy='{occupancy}' should be within range {WEIGHT_RANGE}.")

        if figure_of_merit is not None:
            if WEIGHT_RANGE_MIN <= figure_of_merit <= WEIGHT_RANGE_MAX:
                dstFunc['figure_of_merit'] = str(figure_of_merit)
            else:
                self.f.append(f"[Range value warning] {self.getCurrentAssignment(n=index)}"
                              f"The figure_of_merit='{figure_of_merit}' should be within range {WEIGHT_RANGE}.")

        if self.chemShifts == 0:
            self.__defaultSegId = None
            if self.reasons is not None and 'default_seg_id' in self.reasons:
                try:
                    self.__defaultSegId = self.reasons['default_seg_id'][self.cur_list_id]
                except KeyError:
                    pass

        return dstFunc

    def predictSequenceNumberOffsetByFirstResidue(self, chain_id: Optional[str], seq_id: int, comp_id: Optional[str]) -> None:
        """ Find sequence offset by the given first residue.
        """

        if self.reasons is not None or self.polySeq is None:
            return

        if chain_id is not None\
           and any(True for ps in self.polySeq if ps['auth_chain_id' if 'auth_chain_id' in ps else 'chain_id']):

            if chain_id in self.offset:
                return

            ps = next(ps for ps in self.polySeq if ps['auth_chain_id' if 'auth_chain_id' in ps else 'chain_id'])
            seq_ids = ps['auth_seq_id' if 'auth_seq_id' in ps else 'seq_id']
            min_seq_id, max_seq_id = seq_ids[0], seq_ids[-1]

            if seq_id < min_seq_id or seq_id > max_seq_id:
                offset = min_seq_id - seq_id

                if comp_id is not None:
                    _seq_id = seq_id + offset
                    if _seq_id in seq_ids and comp_id == ps['comp_id'][seq_ids.index(_seq_id)]:
                        self.offset[chain_id] = offset
                        return

                    for shift in range(1, PERIPH_OFFSET_ATTEMPT):
                        _seq_id = seq_id + offset + shift
                        if _seq_id in seq_ids and comp_id == ps['comp_id'][seq_ids.index(_seq_id)]:
                            self.offset[chain_id] = offset + shift
                            return

                self.offset[chain_id] = offset

            return

        if None in self.offset:
            return

        min_seq_id, max_seq_id = 1000, -1000
        for ps in self.polySeq:
            min_seq_id = min(min_seq_id, ps['auth_seq_id' if 'auth_seq_id' in ps else 'seq_id'][0])
            max_seq_id = max(max_seq_id, ps['auth_seq_id' if 'auth_seq_id' in ps else 'seq_id'][-1])

        if seq_id < min_seq_id or seq_id > max_seq_id:
            offset = min_seq_id - seq_id

            if comp_id is not None:
                for ps in self.polySeq:
                    seq_ids = ps['auth_seq_id' if 'auth_seq_id' in ps else 'seq_id']
                    min_seq_id = seq_ids[0]
                    offset = min_seq_id - seq_id

                    _seq_id = seq_id + offset
                    if _seq_id in seq_ids and comp_id == ps['comp_id'][seq_ids.index(_seq_id)]:
                        self.offset[None] = offset
                        return

                for shift in range(1, PERIPH_OFFSET_ATTEMPT):
                    for ps in self.polySeq:
                        seq_ids = ps['auth_seq_id' if 'auth_seq_id' in ps else 'seq_id']
                        min_seq_id = seq_ids[0]
                        offset = min_seq_id - seq_id

                        _seq_id = seq_id + offset + shift
                        if _seq_id in seq_ids and comp_id == ps['comp_id'][seq_ids.index(_seq_id)]:
                            self.offset[None] = offset + shift
                            return

            self.offset[None] = offset

    def checkAssignment(self, index: int, assignment: List[dict]
                        ) -> Tuple[bool, bool, Optional[bool]]:
        """ Evaluate assigned chemical shift.
        """

        has_assignments = has_multiple_assignments = False

        if assignment is not None:

            self.retrieveLocalSeqScheme()

            try:

                hasChainId = assignment[0]['chain_id'] is not None
                hasCompId = assignment[0]['comp_id'] is not None

                has_multiple_assignments = len(assignment) > 1

                for a in assignment:

                    self.atomSelectionSet.clear()

                    if hasChainId and hasCompId:
                        chainAssign, _ =\
                            self.assignCoordPolymerSequenceWithChainId(a['chain_id'], a['seq_id'], a['comp_id'], a['atom_id'],
                                                                       index)

                    elif hasChainId:
                        chainAssign =\
                            self.assignCoordPolymerSequenceWithChainIdWithoutCompId(a['chain_id'], a['seq_id'], a['atom_id'], index)

                    elif hasCompId:
                        chainAssign, _ =\
                            self.assignCoordPolymerSequence(a['chain_id'], a['seq_id'], a['comp_id'], a['atom_id'], index)

                    else:
                        chainAssign =\
                            self.assignCoordPolymerSequenceWithoutCompId(a['seq_id'], a['atom_id'], index)

                    if len(chainAssign) > 0:
                        self.selectCoordAtoms(chainAssign, a['seq_id'], a['comp_id'], a['atom_id'], index)

                        if len(self.atomSelectionSet) == 1:
                            has_assignments = True
                            self.atomSelectionSets.append(deepcopy(self.atomSelectionSet))
                        else:
                            has_assignments = False
                            break

            except (KeyError, TypeError):
                pass

        return has_assignments, has_multiple_assignments

    def addCsRow(self, index: int, dstFunc: dict, has_assignments: bool, has_multiple_assignments: bool, auth_seq_id_map: dict,
                 debug_label: Optional[str], details: Optional[str] = None, default_ambig_code: Optional[int] = None) -> None:
        """ Add assigned chemical shift.
        """

        if self.__debug:
            if not has_assignments:
                print(f"subtype={self.cur_subtype} id={self.chemShifts + 1} (index={index}) "
                      f"{debug_label}None None {dstFunc}")
            for idx, atomSelectionSet in enumerate(self.atomSelectionSets, start=1):
                if has_multiple_assignments:
                    print(f"subtype={self.cur_subtype} id={self.chemShifts + 1} (index={index}) combination_id={idx} "
                          f"{debug_label}{atomSelectionSet[0]} {dstFunc}")
                else:
                    print(f"subtype={self.cur_subtype} id={self.chemShifts + 1} (index={index}) "
                          f"{debug_label}{atomSelectionSet[0]} {dstFunc}")

        if self.__createSfDict:
            sf = self.getSf()

            if sf is not None:
                sf['id'] = index
                sf['index_id'] += 1
                ambig_code = default_ambig_code
                if has_assignments and not has_multiple_assignments and default_ambig_code is None:
                    atom = self.atomSelectionSet[0][0]
                    if len(self.atomSelectionSet[0]) > 1:
                        ambig_code = self.csStat.getMaxAmbigCodeWoSetId(atom['comp_id'], atom['atom_id'])
                        if ambig_code == 0:
                            ambig_code = None
                else:
                    atom = None
                    return

                if len(auth_seq_id_map) > 0 and atom['seq_id'] in auth_seq_id_map:
                    atom['auth_seq_id'] = auth_seq_id_map[atom['seq_id']]

                row = getCsRow(self.cur_subtype, sf['index_id'], sf['list_id'], self.__entryId,
                               dstFunc, self.entityAssembly,
                               atom, ambig_code=ambig_code, details=details)
                sf['loop'].add_data(row)

    def extractAssignment(self, numOfDim: int, string: str, src_index: int,
                          with_segid: Optional[str] = None, with_compid: Optional[str] = None,
                          hint: Optional[List[dict]] = None
                          ) -> Optional[List[dict]]:
        """ Extract assignment from a given string.
        """

        if numOfDim not in (1, 2, 3, 4) or string is None:
            return None

        _str_ = ASSIGNMENT_SEPARATOR_PAT.sub(' ', string).split()
        _str = ASSIGNMENT_SEPARATOR_PAT.sub(' ', string.upper()).split()
        lenStr = len(_str)

        segIdLike, resIdLike, resNameLike, atomNameLike, _atomNameLike, __atomNameLike, ___atomNameLike, atomNameLike_ =\
            [False] * lenStr, [False] * lenStr, [False] * lenStr, [False] * lenStr, \
            [False] * lenStr, [False] * lenStr, [False] * lenStr, [False] * lenStr

        segIdSpan, resIdSpan, resNameSpan, atomNameSpan, _atomNameSpan, __atomNameSpan, ___atomNameSpan, siblingAtomName =\
            [None] * lenStr, [None] * lenStr, [None] * lenStr, [None] * lenStr, \
            [None] * lenStr, [None] * lenStr, [None] * lenStr, [None] * lenStr

        oneLetterCodeSet = []
        extMonDict3 = {}
        if self.polyPeptide and not self.polyDeoxyribonucleotide and not self.polyRibonucleotide:
            oneLetterCodeSet = [getOneLetterCode(compId) for compId in self.compIdSet if len(compId) == 3]
            extMonDict3 = {compId: getOneLetterCode(compId) for compId in self.compIdSet if len(compId) == 3}
        elif not self.polyPeptide and self.polyDeoxyribonucleotide and not self.polyRibonucleotide:
            oneLetterCodeSet = [getOneLetterCode(compId) for compId in self.compIdSet if len(compId) == 2]
            extMonDict3 = {compId: getOneLetterCode(compId) for compId in self.compIdSet if len(compId) == 2}
        elif not self.polyPeptide and not self.polyDeoxyribonucleotide and self.polyRibonucleotide:
            oneLetterCodeSet = [getOneLetterCode(compId) for compId in self.compIdSet if len(compId) == 1]
            extMonDict3 = {compId: getOneLetterCode(compId) for compId in self.compIdSet if len(compId) == 1}

        for np in self.polySeq:
            if len(np['seq_id']) != 1:
                continue
            if np['comp_id'][0][0].isalpha() and np['comp_id'][0][0] not in oneLetterCodeSet:
                oneLetterCodeSet.append(np['comp_id'][0][0])
                extMonDict3[np['comp_id'][0]] = np['comp_id'][0][0]
            if 'auth_comp_id' in np and np['auth_comp_id'][0][0].isalpha() and np['auth_comp_id'][0][0] not in oneLetterCodeSet:
                oneLetterCodeSet.append(np['auth_comp_id'][0][0])
                extMonDict3[np['comp_id'][0]] = np['auth_comp_id'][0][0]
            if 'alt_comp_id' in np and np['alt_comp_id'][0][0].isalpha() and np['alt_comp_id'][0][0] not in oneLetterCodeSet:
                oneLetterCodeSet.append(np['alt_comp_id'][0][0])
                extMonDict3[np['comp_id'][0]] = np['alt_comp_id'][0][0]

        hasOneLetterCodeSet = len(oneLetterCodeSet) > 0
        useOneLetterCodeSet = forceOneLetterCodeSet =\
            self.polyRibonucleotide and not self.polyPeptide and not self.polyDeoxyribonucleotide
        ligCompId = ligAtomId = None
        _ligSeqId = _ligCompId = _ligAtomId = None

        for idx, term in enumerate(_str):
            for segId in self.chainIdSet:
                if term.startswith(segId) and (with_segid is None or term.startswith(with_segid)):
                    segIdLike[idx] = True
                    segIdSpan[idx] = (0, len(segId))
                    break

            resIdTest = ASSIGNMENT_RESID_PAT.search(term)
            if resIdTest:
                if term[0] == 'D' and len(term) == 3 and term[-1] in ('5', '3')\
                   and translateToStdResName(term, ccU=self.ccU) in self.compIdSet:
                    pass
                else:
                    resIdLike[idx] = True
                    resIdSpan[idx] = resIdTest.span()

            minIndex = len(term)

            for compId in self.compIdSet:
                if compId in term:
                    resNameLike[idx] = True
                    index = term.index(compId)
                    if index < minIndex:
                        resNameSpan[idx] = (index, index + len(compId))
                        minIndex = index

            if not resNameLike[idx] and self.cyanaCompIdSet is not None:
                for compId in self.cyanaCompIdSet:
                    if compId in term:
                        resNameLike[idx] = True
                        index = term.index(compId)
                        if index < minIndex:
                            resNameSpan[idx] = (index, index + len(compId))
                            minIndex = index

            if hasOneLetterCodeSet and not useOneLetterCodeSet and resNameLike[idx]\
               and len(term[resNameSpan[idx][0]:resNameSpan[idx][1]]) > 1:
                hasOneLetterCodeSet = False

            if with_compid is not None and len(with_compid) > 1:
                hasOneLetterCodeSet = False

            if not resNameLike[idx] and hasOneLetterCodeSet:
                if not any(compId in term for compId in STD_MON_DICT if len(compId) == 3):
                    for compId in oneLetterCodeSet:
                        if compId in term:
                            resNameLike[idx] = True
                            index = term.index(compId)
                            if index < minIndex:
                                resNameSpan[idx] = (index, index + len(compId))
                                minIndex = index
                elif resIdLike[idx] and self.hasPolySeq:
                    _compId = next(compId for compId in STD_MON_DICT if len(compId) == 3 and compId in term)
                    resId = int(term[resIdSpan[idx][0]:resIdSpan[idx][1]])
                    for ps in self.polySeq:
                        _, _, compId = self.getRealChainSeqId(ps, resId, None)
                        if len(compId) == 3 and compId in STD_MON_DICT and _compId[0:2] == compId[0:2]:
                            resNameLike[idx] = True
                            index = term.index(_compId)
                            resNameSpan[idx] = (index, index + len(compId))
                            term = _str[idx] = term.replace(_compId, compId)
                            break

            if resNameLike[idx]:
                compId = term[resNameSpan[idx][0]:resNameSpan[idx][1]]
                if compId in self.compIdSet and compId not in STD_MON_DICT:
                    ligCompId = compId

                if len(compId) == 1:
                    _ligCompId = next((k for k, v in extMonDict3.items() if v == compId and k not in STD_MON_DICT), None)

                    if resIdLike[idx] and self.hasPolySeq:
                        resId = int(term[resIdSpan[idx][0]:resIdSpan[idx][1]])
                        for ps in self.polySeq:
                            _, _, _compId = self.getRealChainSeqId(ps, resId, None)
                            if _compId is not None and _compId not in STD_MON_DICT:
                                resNameLike[idx] = False
                                break
                    if resNameLike[idx]:
                        index = resNameSpan[idx][1]
                        if index < len(term):
                            if term[index].isdigit() or term[index] in ASSIGNMENT_HALF_SPIN_NUCLEUS\
                               or term[index] in PSE_PRO_BEGIN_CODE:
                                pass
                            else:
                                resNameLike[idx] = False

            if ligCompId is not None and ligCompId != term:
                _, _, details = self.nefT.get_valid_star_atom_in_xplor(ligCompId, term, leave_unmatched=True)
                if details is None or term[0] in ASSIGNMENT_HALF_SPIN_NUCLEUS:
                    atomNameLike[idx] = True
                    atomNameSpan[idx] = (0, len(term) + 1)
                    ligAtomId = term

            if _ligCompId is not None and resNameLike[idx] and len(term[resNameSpan[idx][1]:]) > 0:
                _, _, details = self.nefT.get_valid_star_atom_in_xplor(_ligCompId, term[resNameSpan[idx][1]:], leave_unmatched=True)
                if details is None or term[resNameSpan[idx][1]] in ASSIGNMENT_HALF_SPIN_NUCLEUS:
                    atomNameLike[idx] = True
                    atomNameSpan[idx] = (resNameSpan[idx][1], len(term) + 1)
                    _ligAtomId = term[resNameSpan[idx][1]:len(term) + 1]
                    for np in self.polySeq:
                        if np['comp_id'][0] == _ligCompId:
                            _ligSeqId = np['auth_seq_id' if 'auth_seq_id' in ps else 'seq_id'][0]

            if resIdLike[idx] and resIdSpan[idx][1] + 1 <= len(term) and _str_[idx][resIdSpan[idx][1]].islower()\
               and _str[idx][resIdSpan[idx][1]].isupper():
                if resIdSpan[idx][1] + 1 < len(term)\
                   and any(_str_[idx][resIdSpan[idx][1] + 1].startswith(elem) for elem in ASSIGNMENT_HALF_SPIN_NUCLEUS):
                    term = _str[idx] = term[0:resIdSpan[idx][1]] + term[resIdSpan[idx][1] + 1:]
                elif resIdSpan[idx][1] + 1 == len(term):
                    term = _str[idx] = term[0:resIdSpan[idx][1]]

            for elem in reversed(ASSIGNMENT_HALF_SPIN_NUCLEUS) if 'NH' in term else ASSIGNMENT_HALF_SPIN_NUCLEUS:
                if len(elem) == 1 and ligAtomId is None and _ligAtomId is None:
                    if elem in term:

                        # handle ambiguous assigned peak '(14Trp/11Trp)Hh2'
                        # seen in 6r28/bmr34380/work/data/D_1292101294_nmr-peaks-upload_P1.dat.V1
                        if idx > 0 and not resNameLike[idx] and any(resIdLike[_idx] and resNameLike[_idx] and not atomNameLike[_idx]
                                                                    for _idx in range(idx)):
                            _index = term.index(elem)
                            _atomId = term[_index:len(term)]
                            for _idx in range(idx):
                                if resIdLike[_idx] and resNameLike[_idx] and not atomNameLike[_idx]:
                                    _compId = _str[_idx][resNameSpan[_idx][0]:resNameSpan[_idx][1]]
                                    if len(_compId) == 1 and hasOneLetterCodeSet:
                                        _compId = next(k for k, v in extMonDict3.items() if k in self.compIdSet and v == _compId)
                                        _, _, details =\
                                            self.nefT.get_valid_star_atom_in_xplor(_compId, _atomId, leave_unmatched=True)
                                        if details is None:
                                            atomNameLike[idx] = atomNameLike_[_idx] = useOneLetterCodeSet = True
                                            atomNameSpan[idx] = (_index, len(term))
                                            if siblingAtomName[_idx] is None:
                                                siblingAtomName[_idx] = []
                                            if _atomId not in siblingAtomName[_idx]:
                                                siblingAtomName[_idx].append(_atomId)
                                            break
                                    if (with_compid is not None and _atomId.startswith(with_compid)) or _atomId.startswith('MET'):
                                        continue
                                    _atomId = translateToStdAtomName(_atomId, _compId, ccU=self.ccU)
                                    _, _, details = self.nefT.get_valid_star_atom_in_xplor(_compId, _atomId, leave_unmatched=True)
                                    if details is None:
                                        atomNameLike[idx] = atomNameLike_[_idx] = True
                                        atomNameSpan[idx] = (_index, len(term))
                                        if siblingAtomName[_idx] is None:
                                            siblingAtomName[_idx] = []
                                        if _atomId not in siblingAtomName[_idx]:
                                            siblingAtomName[_idx].append(_atomId)
                                        break
                            if atomNameLike[idx]:
                                break

                        # prevent to split HH2 -> res_name:'HIS', atom_name:'H2'
                        if resNameLike[idx] and resNameSpan[idx][1] - resNameSpan[idx][0] == 1\
                           and resNameSpan[idx][1] == term.rindex(elem)\
                           and term[resNameSpan[idx][0]] in ASSIGNMENT_HALF_SPIN_NUCLEUS:
                            _index = term.index(elem)
                            _atomId = term[_index:len(term)]
                            for compId in self.compIdSet:
                                _, _, details = self.nefT.get_valid_star_atom_in_xplor(compId, _atomId, leave_unmatched=True)
                                if details is None:
                                    atomNameLike[idx] = True
                                    atomNameSpan[idx] = (_index, len(term))
                                    resNameLike[idx] = False
                                    break
                            if atomNameLike[idx]:
                                break

                        # resolve concatenation of residue number and XPLOR-NIH atom nomenclature of proton,
                        # D1391HB -> res_id:139, res_name:'ASP', atom_name:'1HB'
                        # seen in 8e1d/bmr31038/work/data/D_1000267621_nmr-peaks-upload_P6.dat.V1
                        if self.hasPolySeq and resIdLike[idx] and resNameLike[idx]\
                           and resIdSpan[idx][1] == term.rindex(elem) and elem == 'H'\
                           and term[resIdSpan[idx][1] - 1] in ('1', '2', '3') and resIdSpan[idx][1] - resIdSpan[idx][0] > 3:
                            _resId = int(term[resIdSpan[idx][0]:resIdSpan[idx][1]])
                            _compId = term[resNameSpan[idx][0]:resNameSpan[idx][1]]
                            if len(_compId) == 1 and hasOneLetterCodeSet:
                                _compId = next((k for k, v in extMonDict3.items() if k in self.compIdSet and v == _compId), _compId)
                            valid = False
                            for ps in self.polySeq:
                                _, _, _compId_ = self.getRealChainSeqId(ps, _resId, None)
                                if _compId is not None and _compId == _compId_:
                                    valid = True
                                    break
                            if not valid:
                                _resId = int(term[resIdSpan[idx][0]:resIdSpan[idx][1] - 1])
                                for ps in self.polySeq:
                                    _, _, _compId_ = self.getRealChainSeqId(ps, _resId, None)
                                    if _compId is not None and _compId == _compId_:
                                        valid = True
                                        break
                                if valid:
                                    _index = term.rindex(elem)
                                    _atomId = term[_index:len(term)] + term[resIdSpan[idx][1] - 1]
                                    _, _, details = self.nefT.get_valid_star_atom_in_xplor(_compId, _atomId, leave_unmatched=True)
                                    if details is None:
                                        atomNameLike[idx] = True
                                        atomNameSpan[idx] = (_index - 1, len(term))
                                        resIdSpan[idx] = (resIdSpan[idx][0], resIdSpan[idx][1] - 1)
                                        break
                        index = term.rindex(elem)
                        atomId = term[index:len(term)]
                        if index - 1 >= 0 and term[index - 1] in ASSIGNMENT_HALF_SPIN_NUCLEUS:
                            if not resNameLike[idx]:
                                if hint is not None and 'comp_id' in hint[0]:
                                    compId = hint[0]['comp_id']
                                    _index = term.index(elem)
                                    _atomId = term[_index:len(term)]
                                    _, _, details = self.nefT.get_valid_star_atom_in_xplor(compId, _atomId, leave_unmatched=True)
                                    if details is None:
                                        atomNameLike[idx] = True
                                        atomNameSpan[idx] = (_index, len(term))
                                    else:
                                        _, _, details = self.nefT.get_valid_star_atom_in_xplor(compId, atomId, leave_unmatched=True)
                                        if details is None:
                                            atomNameLike[idx] = True
                                            atomNameSpan[idx] = (index, len(term))
                                continue
                            if resNameLike[idx] and compId[-1] in ASSIGNMENT_HALF_SPIN_NUCLEUS and index == resNameSpan[idx][1]:
                                pass
                            else:
                                continue
                        if atomId[0] in ('Q', 'M') and index + 1 < len(term) and term[index + 1].isdigit():
                            if resNameLike[idx] and resNameSpan[idx][0] == index:
                                continue
                            if self.csStat.peptideLike(compId):
                                ligand = False
                                if resIdLike[idx] and self.reasons is not None:
                                    resId = int(term[resIdSpan[idx][0]:resIdSpan[idx][1]])
                                    if 'non_poly_remap' in self.reasons\
                                       and compId in self.reasons['non_poly_remap']\
                                       and resId in self.reasons['non_poly_remap'][compId]:
                                        ligand = True
                                if not ligand:
                                    continue
                        if ((with_compid is not None and atomId.startswith(with_compid)) or atomId.startswith('MET'))\
                           and ((index + 3 < len(term) and term[index + 3].isdigit()
                                 or (index + 4 < len(term) and term[index + 4].isdigit()))):
                            continue
                        if resNameLike[idx] and len(compId) > 1 and compId[-1] == elem and index + 1 == resNameSpan[idx][1]:
                            continue
                        if resNameLike[idx]:
                            compId = term[resNameSpan[idx][0]:resNameSpan[idx][1]]
                            if len(compId) == 1 and hasOneLetterCodeSet and not forceOneLetterCodeSet:
                                compId = next(k for k, v in extMonDict3.items() if k in self.compIdSet and v == compId)
                                _, _, details = self.nefT.get_valid_star_atom_in_xplor(compId, atomId, leave_unmatched=True)
                                if details is None:
                                    atomNameLike[idx] = useOneLetterCodeSet = True
                                    atomNameSpan[idx] = (index, len(term))
                                    if resNameSpan[idx][0] == atomNameSpan[idx][0]:
                                        resNameLike[idx] = False
                                    break
                                if with_compid is not None\
                                   and (atomId.startswith(with_compid) or (atomId in with_compid and index < resNameSpan[idx][1])):
                                    continue
                                _atomId = translateToStdAtomName(atomId, compId, ccU=self.ccU)
                                _, _, details = self.nefT.get_valid_star_atom_in_xplor(compId, _atomId, leave_unmatched=True)
                                if details is None:
                                    atomNameLike[idx] = True
                                    atomNameSpan[idx] = (index, len(term))
                                    if resNameSpan[idx][0] == atomNameSpan[idx][0]:
                                        resNameLike[idx] = False
                                    break
                        for compId in self.compIdSet:
                            _, _, details = self.nefT.get_valid_star_atom_in_xplor(compId, atomId, leave_unmatched=True)
                            if details is None:
                                atomNameLike[idx] = True
                                atomNameSpan[idx] = (index, len(term))
                                break
                            if with_compid is not None and atomId.startswith(with_compid):
                                continue
                            _atomId = translateToStdAtomName(atomId, compId, ccU=self.ccU)
                            _, _, details = self.nefT.get_valid_star_atom_in_xplor(compId, _atomId, leave_unmatched=True)
                            if details is None:
                                atomNameLike[idx] = True
                                atomNameSpan[idx] = (index, len(term))
                                break
                        if atomNameLike[idx]:
                            break

            if atomNameLike[idx]:
                _term = term[0:atomNameSpan[idx][0]]
                for elem in reversed(ASSIGNMENT_HALF_SPIN_NUCLEUS) if 'NH' in _term else ASSIGNMENT_HALF_SPIN_NUCLEUS:
                    if len(elem) == 1 and ligAtomId is None and _ligAtomId is None:
                        if elem in _term:
                            index = _term.rindex(elem)
                            atomId = _term[index:len(_term)]
                            if index - 1 >= 0 and _term[index - 1] in ASSIGNMENT_HALF_SPIN_NUCLEUS:
                                if resNameLike[idx] and compId[-1] in ASSIGNMENT_HALF_SPIN_NUCLEUS and index == resNameSpan[idx][1]:
                                    pass
                                else:
                                    continue
                            if atomId[0] in ('Q', 'M') and index + 1 < len(_term) and _term[index + 1].isdigit():
                                continue
                            if ((with_compid is not None and atomId.startswith(with_compid)) or atomId.startswith('MET'))\
                               and ((index + 3 < len(_term) and _term[index + 3].isdigit()
                                     or (index + 4 < len(_term) and _term[index + 4].isdigit()))):
                                continue
                            if resNameLike[idx] and len(compId) > 1 and compId[-1] == elem and index + 1 == resNameSpan[idx][1]:
                                continue
                            if len(_term) == atomNameSpan[idx][0]:
                                continue
                            if resNameLike[idx]:
                                compId = term[resNameSpan[idx][0]:resNameSpan[idx][1]]
                                if len(compId) == 1 and hasOneLetterCodeSet:
                                    compId = next(k for k, v in extMonDict3.items() if k in self.compIdSet and v == compId)
                                    _, _, details = self.nefT.get_valid_star_atom_in_xplor(compId, atomId, leave_unmatched=True)
                                    if details is None:
                                        _atomNameLike[idx] = useOneLetterCodeSet = True
                                        _atomNameSpan[idx] = (index, len(_term))
                                        if resNameSpan[idx][0] == _atomNameSpan[idx][0]:
                                            resNameLike[idx] = False
                                        break
                                    if with_compid is not None and (atomId.startswith(with_compid)
                                                                    or (atomId in with_compid and index < resNameSpan[idx][1])):
                                        continue
                                    _atomId = translateToStdAtomName(atomId, compId, ccU=self.ccU)
                                    _, _, details = self.nefT.get_valid_star_atom_in_xplor(compId, _atomId, leave_unmatched=True)
                                    if details is None:
                                        _atomNameLike[idx] = True
                                        _atomNameSpan[idx] = (index, len(_term))
                                        if resNameSpan[idx][0] == _atomNameSpan[idx][0]:
                                            resNameLike[idx] = False
                                        break
                            for compId in self.compIdSet:
                                _, _, details = self.nefT.get_valid_star_atom_in_xplor(compId, atomId, leave_unmatched=True)
                                if details is None:
                                    _atomNameLike[idx] = True
                                    _atomNameSpan[idx] = (index, len(_term))
                                    break
                                if with_compid is not None and atomId.startswith(with_compid):
                                    continue
                                _atomId = translateToStdAtomName(atomId, compId, ccU=self.ccU)
                                _, _, details = self.nefT.get_valid_star_atom_in_xplor(compId, _atomId, leave_unmatched=True)
                                if details is None:
                                    _atomNameLike[idx] = True
                                    _atomNameSpan[idx] = (index, len(_term))
                                    break
                            if _atomNameLike[idx]:
                                break

            if numOfDim >= 3 and _atomNameLike[idx]:
                __term = term[0:_atomNameSpan[idx][0]]
                for elem in reversed(ASSIGNMENT_HALF_SPIN_NUCLEUS) if 'NH' in __term else ASSIGNMENT_HALF_SPIN_NUCLEUS:
                    if len(elem) == 1 and ligAtomId is None and _ligAtomId is None:
                        if elem in __term:
                            index = __term.rindex(elem)
                            atomId = __term[index:len(__term)]
                            if index - 1 >= 0 and __term[index - 1] in ASSIGNMENT_HALF_SPIN_NUCLEUS:
                                if resNameLike[idx] and compId[-1] in ASSIGNMENT_HALF_SPIN_NUCLEUS and index == resNameSpan[idx][1]:
                                    pass
                                else:
                                    continue
                            if atomId[0] in ('Q', 'M') and index + 1 < len(__term) and __term[index + 1].isdigit():
                                continue
                            if ((with_compid is not None and atomId.startswith(with_compid)) or atomId.startswith('MET'))\
                               and ((index + 3 < len(__term) and __term[index + 3].isdigit()
                                     or (index + 4 < len(__term) and __term[index + 4].isdigit()))):
                                continue
                            if resNameLike[idx] and len(compId) > 1 and compId[-1] == elem and index + 1 == resNameSpan[idx][1]:
                                continue
                            if len(__term) == _atomNameSpan[idx][0]:
                                continue
                            if resNameLike[idx]:
                                compId = term[resNameSpan[idx][0]:resNameSpan[idx][1]]
                                if len(compId) == 1 and hasOneLetterCodeSet:
                                    compId = next(k for k, v in extMonDict3.items() if k in self.compIdSet and v == compId)
                                    _, _, details = self.nefT.get_valid_star_atom_in_xplor(compId, atomId, leave_unmatched=True)
                                    if details is None:
                                        __atomNameLike[idx] = useOneLetterCodeSet = True
                                        __atomNameSpan[idx] = (index, len(__term))
                                        if resNameSpan[idx][0] == __atomNameSpan[idx][0]:
                                            resNameLike[idx] = False
                                        break
                                    if with_compid is not None and atomId.startswith(with_compid):
                                        continue
                                    _atomId = translateToStdAtomName(atomId, compId, ccU=self.ccU)
                                    _, _, details = self.nefT.get_valid_star_atom_in_xplor(compId, _atomId, leave_unmatched=True)
                                    if details is None:
                                        __atomNameLike[idx] = True
                                        __atomNameSpan[idx] = (index, len(__term))
                                        if resNameSpan[idx][0] == __atomNameSpan[idx][0]:
                                            resNameLike[idx] = False
                                        break
                            for compId in self.compIdSet:
                                _, _, details = self.nefT.get_valid_star_atom_in_xplor(compId, atomId, leave_unmatched=True)
                                if details is None:
                                    __atomNameLike[idx] = True
                                    __atomNameSpan[idx] = (index, len(__term))
                                    break
                                if with_compid is not None and atomId.startswith(with_compid):
                                    continue
                                _atomId = translateToStdAtomName(atomId, compId, ccU=self.ccU)
                                _, _, details = self.nefT.get_valid_star_atom_in_xplor(compId, _atomId, leave_unmatched=True)
                                if details is None:
                                    __atomNameLike[idx] = True
                                    __atomNameSpan[idx] = (index, len(__term))
                                    break
                            if __atomNameLike[idx]:
                                break

            if numOfDim >= 4 and __atomNameLike[idx]:
                ___term = term[0:__atomNameSpan[idx][0]]
                for elem in reversed(ASSIGNMENT_HALF_SPIN_NUCLEUS) if 'NH' in ___term else ASSIGNMENT_HALF_SPIN_NUCLEUS:
                    if len(elem) == 1 and ligAtomId is None and _ligAtomId is None:
                        if elem in ___term:
                            index = ___term.rindex(elem)
                            atomId = ___term[index:len(___term)]
                            if index - 1 >= 0 and ___term[index - 1] in ASSIGNMENT_HALF_SPIN_NUCLEUS:
                                if resNameLike[idx] and compId[-1] in ASSIGNMENT_HALF_SPIN_NUCLEUS and index == resNameSpan[idx][1]:
                                    pass
                                else:
                                    continue
                            if atomId[0] in ('Q', 'M') and index + 1 < len(___term) and ___term[index + 1].isdigit():
                                continue
                            if ((with_compid is not None and atomId.startswith(with_compid)) or atomId.startswith('MET'))\
                               and ((index + 3 < len(___term) and ___term[index + 3].isdigit()
                                     or (index + 4 < len(___term) and ___term[index + 4].isdigit()))):
                                continue
                            if resNameLike[idx] and len(compId) > 1 and compId[-1] == elem and index + 1 == resNameSpan[idx][1]:
                                continue
                            if len(___term) == __atomNameSpan[idx][0]:
                                continue
                            if resNameLike[idx]:
                                compId = term[resNameSpan[idx][0]:resNameSpan[idx][1]]
                                if len(compId) == 1 and hasOneLetterCodeSet:
                                    compId = next(k for k, v in extMonDict3.items() if k in self.compIdSet and v == compId)
                                    _, _, details = self.nefT.get_valid_star_atom_in_xplor(compId, atomId, leave_unmatched=True)
                                    if details is None:
                                        ___atomNameLike[idx] = useOneLetterCodeSet = True
                                        ___atomNameSpan[idx] = (index, len(___term))
                                        if resNameSpan[idx][0] == ___atomNameSpan[idx][0]:
                                            resNameLike[idx] = False
                                        break
                                    if with_compid is not None and atomId.startswith(with_compid):
                                        continue
                                    _atomId = translateToStdAtomName(atomId, compId, ccU=self.ccU)
                                    _, _, details = self.nefT.get_valid_star_atom_in_xplor(compId, _atomId, leave_unmatched=True)
                                    if details is None:
                                        ___atomNameLike[idx] = True
                                        ___atomNameSpan[idx] = (index, len(___term))
                                        if resNameSpan[idx][0] == ___atomNameSpan[idx][0]:
                                            resNameLike[idx] = False
                                        break
                            for compId in self.compIdSet:
                                _, _, details = self.nefT.get_valid_star_atom_in_xplor(compId, atomId, leave_unmatched=True)
                                if details is None:
                                    ___atomNameLike[idx] = True
                                    ___atomNameSpan[idx] = (index, len(___term))
                                    break
                                if with_compid is not None and atomId.startswith(with_compid):
                                    continue
                                _atomId = translateToStdAtomName(atomId, compId, ccU=self.ccU)
                                _, _, details = self.nefT.get_valid_star_atom_in_xplor(compId, _atomId, leave_unmatched=True)
                                if details is None:
                                    ___atomNameLike[idx] = True
                                    ___atomNameSpan[idx] = (index, len(___term))
                                    break
                            if ___atomNameLike[idx]:
                                break

            if _atomNameLike[idx] and atomNameLike[idx]:
                concat = False
                if _atomNameSpan[idx][1] == atomNameSpan[idx][0]:
                    atomId = term[_atomNameSpan[idx][0]:atomNameSpan[idx][1]]
                    if resNameLike[idx]:
                        compId = term[resNameSpan[idx][0]:resNameSpan[idx][1]]
                        resId = term[resIdSpan[idx][0]:resIdSpan[idx][1]] if resIdLike[idx] else '.'
                        if len(compId) == 1 and hasOneLetterCodeSet:
                            compId = next(k for k, v in extMonDict3.items() if k in self.compIdSet and v == compId)
                            _, _, details = self.nefT.get_valid_star_atom_in_xplor(compId, atomId, leave_unmatched=True)
                            if details is None:
                                _atomNameLike[idx] = False
                                atomNameSpan[idx] = (_atomNameSpan[idx][0], atomNameSpan[idx][1])
                                if resNameSpan[idx][0] == atomNameSpan[idx][0]:
                                    resNameLike[idx] = False
                                concat = True
                            else:
                                _atomId = translateToStdAtomName(atomId, compId, ccU=self.ccU)
                                _, _, details = self.nefT.get_valid_star_atom_in_xplor(compId, _atomId, leave_unmatched=True)
                                if details is None:
                                    _atomNameLike[idx] = False
                                    atomNameSpan[idx] = (_atomNameSpan[idx][0], atomNameSpan[idx][1])
                                    if resNameSpan[idx][0] == atomNameSpan[idx][0]:
                                        resNameLike[idx] = False
                                    concat = True
                            if details is not None and numOfDim == 1:
                                _atomNameLike[idx] = False
                                for shift in range(_atomNameSpan[idx][0], atomNameSpan[idx][1]):
                                    _atomId = term[_atomNameSpan[idx][0] + shift:atomNameSpan[idx][1]]
                                    if len(_atomId) == 0:
                                        break
                                    if _atomId[0] == resId[0]:
                                        continue
                                    _, _, details = self.nefT.get_valid_star_atom_in_xplor(compId, _atomId, leave_unmatched=True)
                                    if details is None:
                                        _atomNameSpan[idx] = (_atomNameSpan[idx][0] + shift, len(_atomId))
                                        atomNameSpan[idx] = (_atomNameSpan[idx][0], atomNameSpan[idx][1])
                                        concat = True
                                        break
                                    __atomId = translateToStdAtomName(_atomId, compId, ccU=self.ccU)
                                    _, _, details = self.nefT.get_valid_star_atom_in_xplor(compId, __atomId, leave_unmatched=True)
                                    if details is None:
                                        _atomNameSpan[idx] = (_atomNameSpan[idx][0] + shift, len(_atomId))
                                        atomNameSpan[idx] = (_atomNameSpan[idx][0], atomNameSpan[idx][1])
                                        concat = True
                                        break
                        else:
                            _, _, details = self.nefT.get_valid_star_atom_in_xplor(compId, atomId, leave_unmatched=True)
                            if details is None:
                                _atomNameLike[idx] = False
                                atomNameSpan[idx] = (_atomNameSpan[idx][0], atomNameSpan[idx][1])
                                concat = True
                            else:
                                _atomId = translateToStdAtomName(atomId, compId, ccU=self.ccU)
                                _, _, details = self.nefT.get_valid_star_atom_in_xplor(compId, _atomId, leave_unmatched=True)
                                if details is None:
                                    _atomNameLike[idx] = False
                                    atomNameSpan[idx] = (_atomNameSpan[idx][0], atomNameSpan[idx][1])
                                    concat = True
                            if details is not None and numOfDim == 1:
                                _atomNameLike[idx] = False
                                for shift in range(_atomNameSpan[idx][0], atomNameSpan[idx][1]):
                                    _atomId = term[_atomNameSpan[idx][0] + shift:atomNameSpan[idx][1]]
                                    if len(_atomId) == 0:
                                        break
                                    if _atomId[0] == resId[0]:
                                        continue
                                    _, _, details = self.nefT.get_valid_star_atom_in_xplor(compId, _atomId, leave_unmatched=True)
                                    if details is None:
                                        _atomNameSpan[idx] = (_atomNameSpan[idx][0] + shift, len(_atomId))
                                        atomNameSpan[idx] = (_atomNameSpan[idx][0], atomNameSpan[idx][1])
                                        concat = True
                                        break
                                    __atomId = translateToStdAtomName(_atomId, compId, ccU=self.ccU)
                                    _, _, details = self.nefT.get_valid_star_atom_in_xplor(compId, __atomId, leave_unmatched=True)
                                    if details is None:
                                        _atomNameSpan[idx] = (_atomNameSpan[idx][0] + shift, len(_atomId))
                                        atomNameSpan[idx] = (_atomNameSpan[idx][0], atomNameSpan[idx][1])
                                        concat = True
                                        break
                    if not concat:
                        for compId in self.compIdSet:
                            _, _, details = self.nefT.get_valid_star_atom_in_xplor(compId, atomId, leave_unmatched=True)
                            if details is None:
                                _atomNameLike[idx] = False
                                atomNameSpan[idx] = (_atomNameSpan[idx][0], atomNameSpan[idx][1])
                                break
                            _atomId = translateToStdAtomName(atomId, compId, ccU=self.ccU)
                            _, _, details = self.nefT.get_valid_star_atom_in_xplor(compId, _atomId, leave_unmatched=True)
                            if details is None:
                                _atomNameLike[idx] = False
                                atomNameSpan[idx] = (_atomNameSpan[idx][0], atomNameSpan[idx][1])
                                break

            if __atomNameLike[idx] and _atomNameLike[idx] and atomNameLike[idx]:
                concat = False
                if __atomNameSpan[idx][1] == _atomNameSpan[idx][0] and _atomNameSpan[idx][1] == atomNameSpan[idx][0]:
                    atomId = term[__atomNameSpan[idx][0]:atomNameSpan[idx][1]]
                    if resNameLike[idx]:
                        compId = term[resNameSpan[idx][0]:resNameSpan[idx][1]]
                        if len(compId) == 1 and hasOneLetterCodeSet:
                            compId = next(k for k, v in extMonDict3.items() if k in self.compIdSet and v == compId)
                            _, _, details = self.nefT.get_valid_star_atom_in_xplor(compId, atomId, leave_unmatched=True)
                            if details is None:
                                __atomNameLike[idx] = _atomNameLike[idx] = False
                                atomNameSpan[idx] = (__atomNameSpan[idx][0], atomNameSpan[idx][1])
                                if resNameSpan[idx][0] == atomNameSpan[idx][0]:
                                    resNameLike[idx] = False
                                concat = True
                            else:
                                _atomId = translateToStdAtomName(atomId, compId, ccU=self.ccU)
                                _, _, details = self.nefT.get_valid_star_atom_in_xplor(compId, _atomId, leave_unmatched=True)
                                if details is None:
                                    __atomNameLike[idx] = _atomNameLike[idx] = False
                                    atomNameSpan[idx] = (__atomNameSpan[idx][0], atomNameSpan[idx][1])
                                    if resNameSpan[idx][0] == atomNameSpan[idx][0]:
                                        resNameLike[idx] = False
                                    concat = True

                    if not concat:
                        for compId in self.compIdSet:
                            _, _, details = self.nefT.get_valid_star_atom_in_xplor(compId, atomId, leave_unmatched=True)
                            if details is None:
                                __atomNameLike[idx] = _atomNameLike[idx] = False
                                atomNameSpan[idx] = (__atomNameSpan[idx][0], atomNameSpan[idx][1])
                                break
                            _atomId = translateToStdAtomName(atomId, compId, ccU=self.ccU)
                            _, _, details = self.nefT.get_valid_star_atom_in_xplor(compId, _atomId, leave_unmatched=True)
                            if details is None:
                                __atomNameLike[idx] = _atomNameLike[idx] = False
                                atomNameSpan[idx] = (__atomNameSpan[idx][0], atomNameSpan[idx][1])
                                break

            if ___atomNameLike[idx] and __atomNameLike[idx] and _atomNameLike[idx] and atomNameLike[idx]:
                concat = False
                if ___atomNameSpan[idx][1] == __atomNameSpan[idx][0] and __atomNameSpan[idx][1] == _atomNameSpan[idx][0]\
                   and _atomNameSpan[idx][1] == atomNameSpan[idx][0]:
                    atomId = term[___atomNameSpan[idx][0]:atomNameSpan[idx][1]]
                    if resNameLike[idx]:
                        compId = term[resNameSpan[idx][0]:resNameSpan[idx][1]]
                        if len(compId) == 1 and hasOneLetterCodeSet:
                            compId = next(k for k, v in extMonDict3.items() if k in self.compIdSet and v == compId)
                            _, _, details = self.nefT.get_valid_star_atom_in_xplor(compId, atomId, leave_unmatched=True)
                            if details is None:
                                ___atomNameLike[idx] = __atomNameLike[idx] = _atomNameLike[idx] = False
                                atomNameSpan[idx] = (___atomNameSpan[idx][0], atomNameSpan[idx][1])
                                if resNameSpan[idx][0] == atomNameSpan[idx][0]:
                                    resNameLike[idx] = False
                                concat = True
                            else:
                                _atomId = translateToStdAtomName(atomId, compId, ccU=self.ccU)
                                _, _, details = self.nefT.get_valid_star_atom_in_xplor(compId, _atomId, leave_unmatched=True)
                                if details is None:
                                    ___atomNameLike[idx] = __atomNameLike[idx] = _atomNameLike[idx] = False
                                    atomNameSpan[idx] = (___atomNameSpan[idx][0], atomNameSpan[idx][1])
                                    if resNameSpan[idx][0] == atomNameSpan[idx][0]:
                                        resNameLike[idx] = False
                                    concat = True
                    if not concat:
                        for compId in self.compIdSet:
                            _, _, details = self.nefT.get_valid_star_atom_in_xplor(compId, atomId, leave_unmatched=True)
                            if details is None:
                                ___atomNameLike[idx] = __atomNameLike[idx] = _atomNameLike[idx] = False
                                atomNameSpan[idx] = (___atomNameSpan[idx][0], atomNameSpan[idx][1])
                                break
                            _atomId = translateToStdAtomName(atomId, compId, ccU=self.ccU)
                            _, _, details = self.nefT.get_valid_star_atom_in_xplor(compId, _atomId, leave_unmatched=True)
                            if details is None:
                                ___atomNameLike[idx] = __atomNameLike[idx] = _atomNameLike[idx] = False
                                atomNameSpan[idx] = (___atomNameSpan[idx][0], atomNameSpan[idx][1])
                                break

            if __atomNameLike[idx] and _atomNameLike[idx]:
                concat = False
                if __atomNameSpan[idx][1] == _atomNameSpan[idx][0]:
                    atomId = term[__atomNameSpan[idx][0]:_atomNameSpan[idx][1]]
                    if resNameLike[idx]:
                        compId = term[resNameSpan[idx][0]:resNameSpan[idx][1]]
                        if len(compId) == 1 and hasOneLetterCodeSet:
                            compId = next(k for k, v in extMonDict3.items() if k in self.compIdSet and v == compId)
                            _, _, details = self.nefT.get_valid_star_atom_in_xplor(compId, atomId, leave_unmatched=True)
                            if details is None:
                                __atomNameLike[idx] = False
                                _atomNameSpan[idx] = (__atomNameSpan[idx][0], _atomNameSpan[idx][1])
                                if resNameSpan[idx][0] == _atomNameSpan[idx][0]:
                                    resNameLike[idx] = False
                                concat = True
                            else:
                                _atomId = translateToStdAtomName(atomId, compId, ccU=self.ccU)
                                _, _, details = self.nefT.get_valid_star_atom_in_xplor(compId, _atomId, leave_unmatched=True)
                                if details is None:
                                    __atomNameLike[idx] = False
                                    _atomNameSpan[idx] = (__atomNameSpan[idx][0], _atomNameSpan[idx][1])
                                    if resNameSpan[idx][0] == _atomNameSpan[idx][0]:
                                        resNameLike[idx] = False
                                    concat = True
                    if not concat:
                        for compId in self.compIdSet:
                            _, _, details = self.nefT.get_valid_star_atom_in_xplor(compId, atomId, leave_unmatched=True)
                            if details is None:
                                __atomNameLike[idx] = False
                                _atomNameSpan[idx] = (__atomNameSpan[idx][0], _atomNameSpan[idx][1])
                                break
                            _atomId = translateToStdAtomName(atomId, compId, ccU=self.ccU)
                            _, _, details = self.nefT.get_valid_star_atom_in_xplor(compId, _atomId, leave_unmatched=True)
                            if details is None:
                                __atomNameLike[idx] = False
                                _atomNameSpan[idx] = (__atomNameSpan[idx][0], _atomNameSpan[idx][1])
                                break

            if ___atomNameLike[idx] and __atomNameLike[idx] and _atomNameLike[idx]:
                concat = False
                if ___atomNameSpan[idx][1] == __atomNameSpan[idx][0] and __atomNameSpan[idx][1] == _atomNameSpan[idx][0]:
                    atomId = term[___atomNameSpan[idx][0]:_atomNameSpan[idx][1]]
                    if resNameLike[idx]:
                        compId = term[resNameSpan[idx][0]:resNameSpan[idx][1]]
                        if len(compId) == 1 and hasOneLetterCodeSet:
                            compId = next(k for k, v in extMonDict3.items() if k in self.compIdSet and v == compId)
                            _, _, details = self.nefT.get_valid_star_atom_in_xplor(compId, atomId, leave_unmatched=True)
                            if details is None:
                                ___atomNameLike[idx] = __atomNameLike[idx] = False
                                _atomNameSpan[idx] = (___atomNameSpan[idx][0], _atomNameSpan[idx][1])
                                if resNameSpan[idx][0] == _atomNameSpan[idx][0]:
                                    resNameLike[idx] = False
                                concat = True
                            else:
                                _atomId = translateToStdAtomName(atomId, compId, ccU=self.ccU)
                                _, _, details = self.nefT.get_valid_star_atom_in_xplor(compId, _atomId, leave_unmatched=True)
                                if details is None:
                                    ___atomNameLike[idx] = __atomNameLike[idx] = False
                                    _atomNameSpan[idx] = (___atomNameSpan[idx][0], _atomNameSpan[idx][1])
                                    if resNameSpan[idx][0] == _atomNameSpan[idx][0]:
                                        resNameLike[idx] = False
                                    concat = True
                    if not concat:
                        for compId in self.compIdSet:
                            _, _, details = self.nefT.get_valid_star_atom_in_xplor(compId, atomId, leave_unmatched=True)
                            if details is None:
                                ___atomNameLike[idx] = __atomNameLike[idx] = False
                                _atomNameSpan[idx] = (___atomNameSpan[idx][0], _atomNameSpan[idx][1])
                                break
                            _atomId = translateToStdAtomName(atomId, compId, ccU=self.ccU)
                            _, _, details = self.nefT.get_valid_star_atom_in_xplor(compId, _atomId, leave_unmatched=True)
                            if details is None:
                                ___atomNameLike[idx] = __atomNameLike[idx] = False
                                _atomNameSpan[idx] = (___atomNameSpan[idx][0], _atomNameSpan[idx][1])
                                break

            if ___atomNameLike[idx] and __atomNameLike[idx]:
                concat = False
                if ___atomNameSpan[idx][1] == __atomNameSpan[idx][0]:
                    atomId = term[___atomNameSpan[idx][0]:__atomNameSpan[idx][1]]
                    if resNameLike[idx]:
                        compId = term[resNameSpan[idx][0]:resNameSpan[idx][1]]
                        if len(compId) == 1 and hasOneLetterCodeSet:
                            compId = next(k for k, v in extMonDict3.items() if k in self.compIdSet and v == compId)
                            _, _, details = self.nefT.get_valid_star_atom_in_xplor(compId, atomId, leave_unmatched=True)
                            if details is None:
                                ___atomNameLike[idx] = False
                                __atomNameSpan[idx] = (___atomNameSpan[idx][0], __atomNameSpan[idx][1])
                                if resNameSpan[idx][0] == __atomNameSpan[idx][0]:
                                    resNameLike[idx] = False
                                concat = True
                            else:
                                _atomId = translateToStdAtomName(atomId, compId, ccU=self.ccU)
                                _, _, details = self.nefT.get_valid_star_atom_in_xplor(compId, _atomId, leave_unmatched=True)
                                if details is None:
                                    ___atomNameLike[idx] = False
                                    __atomNameSpan[idx] = (___atomNameSpan[idx][0], __atomNameSpan[idx][1])
                                    if resNameSpan[idx][0] == __atomNameSpan[idx][0]:
                                        resNameLike[idx] = False
                                    concat = True
                    if not concat:
                        for compId in self.compIdSet:
                            _, _, details = self.nefT.get_valid_star_atom_in_xplor(compId, atomId, leave_unmatched=True)
                            if details is None:
                                ___atomNameLike[idx] = False
                                __atomNameSpan[idx] = (___atomNameSpan[idx][0], __atomNameSpan[idx][1])
                                break
                            _atomId = translateToStdAtomName(atomId, compId, ccU=self.ccU)
                            _, _, details = self.nefT.get_valid_star_atom_in_xplor(compId, _atomId, leave_unmatched=True)
                            if details is None:
                                ___atomNameLike[idx] = False
                                __atomNameSpan[idx] = (___atomNameSpan[idx][0], __atomNameSpan[idx][1])
                                break

            if self.__verbose_debug:
                print(f'{idx} {term!r} segid:{segIdLike[idx]} {term[segIdSpan[idx][0]:segIdSpan[idx][1]] if segIdLike[idx] else ""}, '  # noqa: E501, pylint: disable=line-too-long
                      f'resid:{resIdLike[idx]} {term[resIdSpan[idx][0]:resIdSpan[idx][1]] if resIdLike[idx] else ""}, '
                      f'resname:{resNameLike[idx]} {term[resNameSpan[idx][0]:resNameSpan[idx][1]] if resNameLike[idx] else ""}, '
                      f'atomname:{atomNameLike[idx]} {term[atomNameSpan[idx][0]:atomNameSpan[idx][1]] if atomNameLike[idx] else ""}, '  # noqa: E501, pylint: disable=line-too-long
                      f'_atomname:{_atomNameLike[idx]} {term[_atomNameSpan[idx][0]:_atomNameSpan[idx][1]] if _atomNameLike[idx] else ""}, '  # noqa: E501, pylint: disable=line-too-long
                      f'__atomname:{__atomNameLike[idx]} {term[__atomNameSpan[idx][0]:__atomNameSpan[idx][1]] if __atomNameLike[idx] else ""}, '  # noqa: E501, pylint: disable=line-too-long
                      f'___atomname:{___atomNameLike[idx]} {term[___atomNameSpan[idx][0]:___atomNameSpan[idx][1]] if ___atomNameLike[idx] else ""}')  # noqa: E501, pylint: disable=line-too-long

        atomNameCount = 0
        for idx in range(lenStr):
            if atomNameLike[idx]:
                atomNameCount += 1
            if _atomNameLike[idx]:
                atomNameCount += 1
            if __atomNameLike[idx]:
                atomNameCount += 1
            if ___atomNameLike[idx]:
                atomNameCount += 1

        if atomNameCount < numOfDim:
            return None

        if atomNameCount > numOfDim > 1:
            atomNameCount = 0
            ignoreBefore = False
            for idx in range(lenStr - 1, 0, -1):
                if ignoreBefore:
                    atomNameLike[idx] = _atomNameLike[idx] = __atomNameLike[idx] = ___atomNameLike[idx] = False
                else:
                    if atomNameLike[idx]:
                        atomNameCount += 1
                    if _atomNameLike[idx]:
                        atomNameCount += 1
                    if __atomNameLike[idx]:
                        atomNameCount += 1
                    if ___atomNameLike[idx]:
                        atomNameCount += 1
                    if atomNameCount >= numOfDim:
                        ignoreBefore = True

        hasResName = hasResId = False
        for idx in range(lenStr):
            if ___atomNameLike[idx]:
                if resNameLike[idx]:
                    if not hasResName and resNameSpan[idx][0] < ___atomNameSpan[idx][0]\
                       and resNameSpan[idx][1] >= ___atomNameSpan[idx][1]:
                        ___atomNameLike[idx] = False
                    elif resNameSpan[idx][1] > ___atomNameSpan[idx][0]:
                        term = _str[idx]
                        if not hasResId and resNameSpan[idx][1] - resNameSpan[idx][0] == 3\
                           and term[resNameSpan[idx][0]:resNameSpan[idx][1]] ==\
                           term[___atomNameSpan[idx][0]:___atomNameSpan[idx][1]]\
                           and any(True for _idx in range(idx + 1, lenStr)
                                   if __atomNameLike[_idx] or _atomNameLike[_idx] or atomNameLike[_idx]):
                            ___atomNameLike[idx] = False
                        else:
                            resNameLike[idx] = False
                if resIdLike[idx]:
                    if resIdSpan[idx][1] > ___atomNameSpan[idx][0]:
                        resIdLike[idx] = False
                if segIdLike[idx]:
                    if segIdSpan[idx][1] > ___atomNameSpan[idx][0]:
                        segIdLike[idx] = False

            elif __atomNameLike[idx]:
                if resNameLike[idx]:
                    if not hasResName and resNameSpan[idx][0] < __atomNameSpan[idx][0]\
                       and resNameSpan[idx][1] >= __atomNameSpan[idx][1]:
                        __atomNameLike[idx] = False
                    elif resNameSpan[idx][1] > __atomNameSpan[idx][0]:
                        term = _str[idx]
                        if not hasResId and resNameSpan[idx][1] - resNameSpan[idx][0] == 3\
                           and term[resNameSpan[idx][0]:resNameSpan[idx][1]] == term[__atomNameSpan[idx][0]:__atomNameSpan[idx][1]]\
                           and any(True for _idx in range(idx + 1, lenStr) if _atomNameLike[_idx] or atomNameLike[_idx]):
                            __atomNameLike[idx] = False
                        else:
                            resNameLike[idx] = False
                if resIdLike[idx]:
                    if resIdSpan[idx][1] > __atomNameSpan[idx][0]:
                        resIdLike[idx] = False
                if segIdLike[idx]:
                    if segIdSpan[idx][1] > __atomNameSpan[idx][0]:
                        segIdLike[idx] = False

            elif _atomNameLike[idx]:
                if resNameLike[idx]:
                    if not hasResName and resNameSpan[idx][0] < _atomNameSpan[idx][0]\
                       and resNameSpan[idx][1] >= _atomNameSpan[idx][1]:
                        _atomNameLike[idx] = False
                    elif resNameSpan[idx][1] > _atomNameSpan[idx][0]:
                        term = _str[idx]
                        if not hasResId and resNameSpan[idx][1] - resNameSpan[idx][0] == 3\
                           and term[resNameSpan[idx][0]:resNameSpan[idx][1]] == term[_atomNameSpan[idx][0]:_atomNameSpan[idx][1]]\
                           and any(True for _idx in range(idx + 1, lenStr) if atomNameLike[_idx]):
                            _atomNameLike[idx] = False
                        else:
                            resNameLike[idx] = False
                if resIdLike[idx]:
                    if resIdSpan[idx][1] > _atomNameSpan[idx][0]:
                        resIdLike[idx] = False
                if segIdLike[idx]:
                    if segIdSpan[idx][1] > _atomNameSpan[idx][0]:
                        segIdLike[idx] = False

            elif atomNameLike[idx]:
                if resNameLike[idx]:
                    if not hasResName and resNameSpan[idx][0] < atomNameSpan[idx][0]\
                       and resNameSpan[idx][1] >= atomNameSpan[idx][1]:
                        atomNameLike[idx] = False
                    elif resNameSpan[idx][1] > atomNameSpan[idx][0]:
                        term = _str[idx]
                        if not hasResId and resNameSpan[idx][1] - resNameSpan[idx][0] == 3\
                           and term[resNameSpan[idx][0]:resNameSpan[idx][1]] == term[atomNameSpan[idx][0]:atomNameSpan[idx][1]]\
                           and any(True for _idx in range(idx + 1, lenStr) if atomNameLike[_idx]):
                            atomNameLike[idx] = False
                        else:
                            if forceOneLetterCodeSet and resIdLike[idx] and len(atomNameLike) > idx + 1 and atomNameLike[idx + 1]:
                                atomNameLike[idx] = False
                            else:
                                resNameLike[idx] = False
                if resIdLike[idx]:
                    if resIdSpan[idx][1] > atomNameSpan[idx][0]:
                        if forceOneLetterCodeSet and resNameLike[idx]:
                            pass
                        else:
                            resIdLike[idx] = False
                if segIdLike[idx]:
                    if segIdSpan[idx][1] > atomNameSpan[idx][0]:
                        segIdLike[idx] = False

            if resNameLike[idx]:
                hasResName = True
                if segIdLike[idx]:
                    if segIdSpan[idx][1] > resNameSpan[idx][0]:
                        if numOfDim > 1 or not any(resNameLike[_idx] for _idx in range(idx + 1, lenStr))\
                           or (resIdLike[idx] and segIdSpan[idx][1] == resIdSpan[idx][0]):
                            _resName = _str[idx][resNameSpan[idx][0]:resNameSpan[idx][1]]
                            _resId = next((int(_str[_idx][resIdSpan[_idx][0]:resIdSpan[_idx][1]])
                                           for _idx in range(idx + 1, lenStr)
                                           if resIdLike[_idx]), None)
                            _atomName = next((_str[_idx][atomNameSpan[_idx][0]:atomNameSpan[_idx][1]]
                                              for _idx in range(idx + 1, lenStr)
                                              if atomNameLike[_idx]), None)
                            checked = True
                            if _resId is not None and _atomName is not None and len(_resName) == 1 and len(self.polySeq) > 1:
                                for ps in self.polySeq:
                                    _, _, _compId_ = self.getRealChainSeqId(ps, _resId, None)
                                    if _resName == ps['auth_chain_id'] and _compId_ in STD_MON_DICT\
                                       and _resName != STD_MON_DICT[_compId_]:
                                        checked = False
                                        break
                            if checked:
                                segIdLike[idx] = False
                            else:
                                resNameLike[idx] = False
                        else:
                            if not hasResId and resNameSpan[idx][1] - resNameSpan[idx][0] == 3\
                               and term[resNameSpan[idx][0]:resNameSpan[idx][1]] == term[atomNameSpan[idx][0]:atomNameSpan[idx][1]]\
                               and any(True for _idx in range(idx + 1, lenStr) if atomNameLike[_idx]):
                                atomNameLike[idx] = False
                            else:
                                resNameLike[idx] = False

            if resIdLike[idx]:
                hasResId = True
                if atomNameLike[idx]:
                    if resIdSpan[idx][1] > atomNameSpan[idx][0]:
                        resIdLike[idx] = False

            if self.__verbose_debug:
                print(f' -> {idx} segid:{segIdLike[idx]}, resid:{resIdLike[idx]}, resname:{resNameLike[idx]}, '
                      f'atomname:{atomNameLike[idx]}, _atomname:{_atomNameLike[idx]}, '
                      f'__atomname:{__atomNameLike[idx]}, ___atomname:{___atomNameLike[idx]}')

        resIdCount = 0
        for idx in range(lenStr):
            if resIdLike[idx]:
                resIdCount += 1

        _resId = [h['seq_id'] for h in hint] if hint is not None else None
        if resIdCount == 0:
            if _resId is None and _ligAtomId is None:
                return None

        _resNameDict = [{h['auth_seq_id']: h['comp_id']} for h in hint] if hint is not None else None

        resIdLater = resIdCount == numOfDim
        if resIdLater:
            atomNameCount = 0
            for idx in range(lenStr):
                if atomNameLike[idx]:
                    atomNameCount += 1
            resIdLater = atomNameCount == numOfDim
            if resIdLater:
                anyResId = False
                for idx in range(lenStr):
                    if resIdLike[idx]:
                        anyResId = True
                    if atomNameLike[idx]:
                        if anyResId:
                            resIdLater = False
                        break

        if self.__verbose_debug:
            print(f'num_of_dim: {numOfDim}, resid_count: {resIdCount}, resid_later:{resIdLater}')

        def is_valid_chain_assign(chain_assign, res_name):
            return len(chain_assign) > 0 and ((res_name in extMonDict3 and any(True for a in chain_assign if a[2] == res_name))
                                              or res_name not in extMonDict3)

        ret = []

        segId = resId = resName = atomName = _segId_ = _resId_ = authResId = None
        dimId = 1
        for idx, term in enumerate(_str):
            if segIdLike[idx]:
                segId = term[segIdSpan[idx][0]:segIdSpan[idx][1]]
                if _segId_ is not None and segId != _segId_:
                    resId = resName = None
                _segId_ = segId
            if resIdLike[idx]:
                resId = authResId = int(term[resIdSpan[idx][0]:resIdSpan[idx][1]])
                if _resId_ is not None and resId != _resId_:
                    resName = None
                _resId_ = resId
            if resNameLike[idx]:
                resName = term[resNameSpan[idx][0]:resNameSpan[idx][1]]
                if len(resName) == 1 and hasOneLetterCodeSet:
                    resName = next(k for k, v in extMonDict3.items() if k in self.compIdSet and v == resName)
            elif _resNameDict is not None and resName is None and len(ret) < len(_resNameDict)\
                    and _resId is not None and len(ret) < len(_resId) and _resId[len(ret)] in _resNameDict[len(ret)]\
                    and (authResId is None or authResId == _resId[len(ret)]):
                resName = _resNameDict[len(ret)][_resId[len(ret)]]
            if ___atomNameLike[idx]:
                if resIdLater:
                    for _idx, _term in enumerate(_str):
                        if _idx > idx and resIdLike[_idx]:
                            resId = int(_term[resIdSpan[_idx][0]:resIdSpan[_idx][1]])
                            segId = resName = None
                            break
                if resId is None:
                    if _resId is None or len(ret) >= len(_resId):
                        return None
                    resId = _resId[len(ret)]
                atomName = term[___atomNameSpan[idx][0]:___atomNameSpan[idx][1]]
                if self.hasPolySeq:
                    if segId is None and resName is None:
                        chainAssign =\
                            self.assignCoordPolymerSequenceWithChainIdWithoutCompId(self.__defaultSegId, resId, atomName, src_index)
                        if len(chainAssign) > 0:
                            if self.__defaultSegId is None:
                                idx = next((chainAssign.index(a) for a in chainAssign if a[1] == resId), -1)
                            else:
                                idx = next((chainAssign.index(a) for a in chainAssign
                                            if a[0] == self.__defaultSegId and a[1] == resId), -1)
                                if idx == -1:
                                    idx = next((chainAssign.index(a) for a in chainAssign if a[1] == resId), -1)
                            if idx != -1:
                                # if self.__defaultSegId is None:
                                self.__defaultSegId = chainAssign[idx][0]
                            else:
                                idx = 0
                            segId, _, resName, _ = chainAssign[idx]
                        else:
                            chainAssign = self.assignCoordPolymerSequenceWithoutCompId(resId, atomName, src_index)
                            if len(chainAssign) > 0:
                                if self.__defaultSegId is None:
                                    idx = next((chainAssign.index(a) for a in chainAssign if a[1] == resId), -1)
                                else:
                                    idx = next((chainAssign.index(a) for a in chainAssign
                                                if a[0] == self.__defaultSegId and a[1] == resId), -1)
                                    if idx == -1:
                                        idx = next((chainAssign.index(a) for a in chainAssign if a[1] == resId), -1)
                                if idx != -1:
                                    # if self.__defaultSegId is None:
                                    # self.__defaultSegId = chainAssign[idx][0]
                                    pass
                                else:
                                    idx = 0
                                segId, _, resName, _ = chainAssign[idx]
                    elif segId is None:
                        chainAssign, _ = self.assignCoordPolymerSequence(self.__defaultSegId,
                                                                         resId, resName, atomName, src_index)
                        is_valid = is_valid_chain_assign(chainAssign, resName)
                        if not is_valid:
                            if self.__defaultSegId != self.__defaultSegId__:
                                __preferAuthSeq = self.__preferAuthSeq
                                self.__preferAuthSeq = not __preferAuthSeq
                                chainAssign, _ = self.assignCoordPolymerSequenceWithChainId(self.__defaultSegId__,
                                                                                            resId, resName, atomName, src_index)
                                is_valid = is_valid_chain_assign(chainAssign, resName)
                                self.__preferAuthSeq = __preferAuthSeq
                        if is_valid:
                            idx = next((chainAssign.index(a) for a in chainAssign if a[2] == resName), -1)
                            if idx != -1:
                                # if self.__defaultSegId is None:
                                self.__defaultSegId = chainAssign[idx][0]
                            else:
                                idx = 0
                            segId = chainAssign[idx][0]
                        else:
                            chainAssign, _ = self.assignCoordPolymerSequence(None,
                                                                             resId, resName, atomName, src_index)
                            if is_valid_chain_assign(chainAssign, resName):
                                idx = next((chainAssign.index(a) for a in chainAssign if a[2] == resName), -1)
                                if idx != -1:
                                    # if self.__defaultSegId is None:
                                    self.__defaultSegId = chainAssign[idx][0]
                                    if self.reasons is None:
                                        r = self.__getNamedReasonsForReparsing('default_seg_id')
                                        if self.cur_list_id not in r:
                                            r[self.cur_list_id] = self.__defaultSegId
                                else:
                                    idx = 0
                                segId = chainAssign[idx][0]
                    elif resName is None:
                        chainAssign = self.assignCoordPolymerSequenceWithChainIdWithoutCompId(segId, resId, atomName, src_index)
                        if len(chainAssign) > 0:
                            idx = next((chainAssign.index(a) for a in chainAssign if a[1] == resId), 0)
                            resName = chainAssign[idx][2]
                    _, _, details = self.nefT.get_valid_star_atom_in_xplor(resName, atomName, leave_unmatched=True)
                    if details is not None:
                        atomName = translateToStdAtomName(atomName, resName, ccU=self.ccU)
                    ret.append({'dim_id': dimId, 'chain_id': segId, 'seq_id': resId,
                                'auth_seq_id': authResId, 'comp_id': resName, 'atom_id': atomName})
                else:
                    ass = {'dim': dimId, 'atom_id': atomName}
                    if segId is not None:
                        ass['chain_id'] = segId
                    if resId is not None:
                        ass['seq_id'] = resId
                    if resName is not None:
                        ass['comp_id'] = resName
                    ret.append(ass)
                dimId += 1
            if __atomNameLike[idx]:
                if resIdLater:
                    for _idx, _term in enumerate(_str):
                        if _idx > idx and resIdLike[_idx]:
                            resId = int(_term[resIdSpan[_idx][0]:resIdSpan[_idx][1]])
                            segId = resName = None
                            break
                if resId is None:
                    if _resId is None or len(ret) >= len(_resId):
                        return None
                    resId = _resId[len(ret)]
                atomName = term[__atomNameSpan[idx][0]:__atomNameSpan[idx][1]]
                if self.hasPolySeq:
                    if segId is None and resName is None:
                        chainAssign =\
                            self.assignCoordPolymerSequenceWithChainIdWithoutCompId(self.__defaultSegId, resId, atomName, src_index)
                        if len(chainAssign) > 0:
                            if self.__defaultSegId is None:
                                idx = next((chainAssign.index(a) for a in chainAssign if a[1] == resId), -1)
                            else:
                                idx = next((chainAssign.index(a) for a in chainAssign
                                            if a[0] == self.__defaultSegId and a[1] == resId), -1)
                                if idx == -1:
                                    idx = next((chainAssign.index(a) for a in chainAssign if a[1] == resId), -1)
                            if idx != -1:
                                # if self.__defaultSegId is None:
                                self.__defaultSegId = chainAssign[idx][0]
                            else:
                                idx = 0
                            segId, _, resName, _ = chainAssign[idx]
                        else:
                            chainAssign = self.assignCoordPolymerSequenceWithoutCompId(resId, atomName, src_index)
                            if len(chainAssign) > 0:
                                if self.__defaultSegId is None:
                                    idx = next((chainAssign.index(a) for a in chainAssign if a[1] == resId), -1)
                                else:
                                    idx = next((chainAssign.index(a) for a in chainAssign
                                                if a[0] == self.__defaultSegId and a[1] == resId), -1)
                                    if idx == -1:
                                        idx = next((chainAssign.index(a) for a in chainAssign if a[1] == resId), -1)
                                if idx != -1:
                                    # if self.__defaultSegId is None:
                                    # self.__defaultSegId = chainAssign[idx][0]
                                    pass
                                else:
                                    idx = 0
                                segId, _, resName, _ = chainAssign[idx]
                    elif segId is None:
                        chainAssign, _ = self.assignCoordPolymerSequence(self.__defaultSegId,
                                                                         resId, resName, atomName, src_index)
                        is_valid = is_valid_chain_assign(chainAssign, resName)
                        if not is_valid:
                            if self.__defaultSegId != self.__defaultSegId__:
                                __preferAuthSeq = self.__preferAuthSeq
                                self.__preferAuthSeq = not __preferAuthSeq
                                chainAssign, _ = self.assignCoordPolymerSequenceWithChainId(self.__defaultSegId__,
                                                                                            resId, resName, atomName, src_index)
                                is_valid = is_valid_chain_assign(chainAssign, resName)
                                self.__preferAuthSeq = __preferAuthSeq
                        if is_valid:
                            idx = next((chainAssign.index(a) for a in chainAssign if a[2] == resName), -1)
                            if idx != -1:
                                # if self.__defaultSegId is None:
                                self.__defaultSegId = chainAssign[idx][0]
                            else:
                                idx = 0
                            segId = chainAssign[idx][0]
                        else:
                            chainAssign, _ = self.assignCoordPolymerSequence(None,
                                                                             resId, resName, atomName, src_index)
                            if is_valid_chain_assign(chainAssign, resName):
                                idx = next((chainAssign.index(a) for a in chainAssign if a[2] == resName), -1)
                                if idx != -1:
                                    # if self.__defaultSegId is None:
                                    self.__defaultSegId = chainAssign[idx][0]
                                    if self.reasons is None:
                                        r = self.__getNamedReasonsForReparsing('default_seg_id')
                                        if self.cur_list_id not in r:
                                            r[self.cur_list_id] = self.__defaultSegId
                                else:
                                    idx = 0
                                segId = chainAssign[idx][0]
                    elif resName is None:
                        chainAssign = self.assignCoordPolymerSequenceWithChainIdWithoutCompId(segId, resId, atomName, src_index)
                        if len(chainAssign) > 0:
                            idx = next((chainAssign.index(a) for a in chainAssign if a[1] == resId), 0)
                            resName = chainAssign[idx][2]
                    _, _, details = self.nefT.get_valid_star_atom_in_xplor(resName, atomName, leave_unmatched=True)
                    if details is not None:
                        atomName = translateToStdAtomName(atomName, resName, ccU=self.ccU)
                    ret.append({'dim_id': dimId, 'chain_id': segId, 'seq_id': resId,
                                'auth_seq_id': authResId, 'comp_id': resName, 'atom_id': atomName})
                else:
                    ass = {'dim': dimId, 'atom_id': atomName}
                    if segId is not None:
                        ass['chain_id'] = segId
                    if resId is not None:
                        ass['seq_id'] = resId
                    if resName is not None:
                        ass['comp_id'] = resName
                    ret.append(ass)
                dimId += 1
            if _atomNameLike[idx]:
                if resIdLater:
                    for _idx, _term in enumerate(_str):
                        if _idx > idx and resIdLike[_idx]:
                            resId = int(_term[resIdSpan[_idx][0]:resIdSpan[_idx][1]])
                            segId = resName = None
                            break
                if resId is None:
                    if _resId is None or len(ret) >= len(_resId):
                        return None
                    resId = _resId[len(ret)]
                atomName = term[_atomNameSpan[idx][0]:_atomNameSpan[idx][1]]
                if self.hasPolySeq:
                    if segId is None and resName is None:
                        chainAssign =\
                            self.assignCoordPolymerSequenceWithChainIdWithoutCompId(self.__defaultSegId, resId, atomName, src_index)
                        if len(chainAssign) > 0:
                            if self.__defaultSegId is None:
                                idx = next((chainAssign.index(a) for a in chainAssign if a[1] == resId), -1)
                            else:
                                idx = next((chainAssign.index(a) for a in chainAssign
                                            if a[0] == self.__defaultSegId and a[1] == resId), -1)
                                if idx == -1:
                                    idx = next((chainAssign.index(a) for a in chainAssign if a[1] == resId), -1)
                            if idx != -1:
                                # if self.__defaultSegId is None:
                                self.__defaultSegId = chainAssign[idx][0]
                            else:
                                idx = 0
                            segId, _, resName, _ = chainAssign[idx]
                        else:
                            chainAssign = self.assignCoordPolymerSequenceWithoutCompId(resId, atomName, src_index)
                            if len(chainAssign) > 0:
                                if self.__defaultSegId is None:
                                    idx = next((chainAssign.index(a) for a in chainAssign if a[1] == resId), -1)
                                else:
                                    idx = next((chainAssign.index(a) for a in chainAssign
                                                if a[0] == self.__defaultSegId and a[1] == resId), -1)
                                    if idx == -1:
                                        idx = next((chainAssign.index(a) for a in chainAssign if a[1] == resId), -1)
                                if idx != -1:
                                    # if self.__defaultSegId is None:
                                    # self.__defaultSegId = chainAssign[idx][0]
                                    pass
                                else:
                                    idx = 0
                                segId, _, resName, _ = chainAssign[idx]
                    elif segId is None:
                        chainAssign, _ = self.assignCoordPolymerSequence(self.__defaultSegId,
                                                                         resId, resName, atomName, src_index)
                        is_valid = is_valid_chain_assign(chainAssign, resName)
                        if not is_valid:
                            if self.__defaultSegId != self.__defaultSegId__:
                                __preferAuthSeq = self.__preferAuthSeq
                                self.__preferAuthSeq = not __preferAuthSeq
                                chainAssign, _ = self.assignCoordPolymerSequenceWithChainId(self.__defaultSegId__,
                                                                                            resId, resName, atomName, src_index)
                                is_valid = is_valid_chain_assign(chainAssign, resName)
                                self.__preferAuthSeq = __preferAuthSeq
                        if is_valid:
                            idx = next((chainAssign.index(a) for a in chainAssign if a[2] == resName), -1)
                            if idx != -1:
                                # if self.__defaultSegId is None:
                                self.__defaultSegId = chainAssign[idx][0]
                            else:
                                idx = 0
                            segId = chainAssign[idx][0]
                        else:
                            chainAssign, _ = self.assignCoordPolymerSequence(None,
                                                                             resId, resName, atomName, src_index)
                            if is_valid_chain_assign(chainAssign, resName):
                                idx = next((chainAssign.index(a) for a in chainAssign if a[2] == resName), -1)
                                if idx != -1:
                                    # if self.__defaultSegId is None:
                                    self.__defaultSegId = chainAssign[idx][0]
                                    if self.reasons is None:
                                        r = self.__getNamedReasonsForReparsing('default_seg_id')
                                        if self.cur_list_id not in r:
                                            r[self.cur_list_id] = self.__defaultSegId
                                else:
                                    idx = 0
                                segId = chainAssign[idx][0]
                    elif resName is None:
                        chainAssign = self.assignCoordPolymerSequenceWithChainIdWithoutCompId(segId, resId, atomName, src_index)
                        if len(chainAssign) > 0:
                            idx = next((chainAssign.index(a) for a in chainAssign if a[1] == resId), 0)
                            resName = chainAssign[idx][2]
                    _, _, details = self.nefT.get_valid_star_atom_in_xplor(resName, atomName, leave_unmatched=True)
                    if details is not None:
                        atomName = translateToStdAtomName(atomName, resName, ccU=self.ccU)
                    ret.append({'dim_id': dimId, 'chain_id': segId, 'seq_id': resId,
                                'auth_seq_id': authResId, 'comp_id': resName, 'atom_id': atomName})
                else:
                    ass = {'dim': dimId, 'atom_id': atomName}
                    if segId is not None:
                        ass['chain_id'] = segId
                    if resId is not None:
                        ass['seq_id'] = resId
                    if resName is not None:
                        ass['comp_id'] = resName
                    ret.append(ass)
                dimId += 1
            if atomNameLike[idx]:
                if resIdLater:
                    for _idx, _term in enumerate(_str):
                        if _idx > idx and resIdLike[_idx]:
                            resId = int(_term[resIdSpan[_idx][0]:resIdSpan[_idx][1]])
                            segId = resName = None
                            break
                if resId is None and _ligAtomId is None:
                    if _resId is None or len(ret) >= len(_resId):
                        return None
                    resId = _resId[len(ret)]
                if _ligAtomId is not None:
                    resId = _ligSeqId
                atomName = term[atomNameSpan[idx][0]:atomNameSpan[idx][1]]
                if self.hasPolySeq:
                    if segId is None and resName is None:
                        chainAssign =\
                            self.assignCoordPolymerSequenceWithChainIdWithoutCompId(self.__defaultSegId, resId, atomName, src_index)
                        if len(chainAssign) > 0:
                            if self.__defaultSegId is None:
                                idx = next((chainAssign.index(a) for a in chainAssign if a[1] == resId), -1)
                            else:
                                idx = next((chainAssign.index(a) for a in chainAssign
                                            if a[0] == self.__defaultSegId and a[1] == resId), -1)
                                if idx == -1:
                                    idx = next((chainAssign.index(a) for a in chainAssign if a[1] == resId), -1)
                            if idx != -1:
                                # if self.__defaultSegId is None:
                                self.__defaultSegId = chainAssign[idx][0]
                            else:
                                idx = 0
                            segId, _, resName, _ = chainAssign[idx]
                        else:
                            chainAssign = self.assignCoordPolymerSequenceWithoutCompId(resId, atomName, src_index)
                            if len(chainAssign) > 0:
                                if self.__defaultSegId is None:
                                    idx = next((chainAssign.index(a) for a in chainAssign if a[1] == resId), -1)
                                else:
                                    idx = next((chainAssign.index(a) for a in chainAssign
                                                if a[0] == self.__defaultSegId and a[1] == resId), -1)
                                    if idx == -1:
                                        idx = next((chainAssign.index(a) for a in chainAssign if a[1] == resId), -1)
                                if idx != -1:
                                    # if self.__defaultSegId is None:
                                    # self.__defaultSegId = chainAssign[idx][0]
                                    pass
                                else:
                                    idx = 0
                                segId, _, resName, _ = chainAssign[idx]
                    elif segId is None:
                        chainAssign, _ = self.assignCoordPolymerSequence(self.__defaultSegId,
                                                                         resId, resName, atomName, src_index)
                        is_valid = is_valid_chain_assign(chainAssign, resName)
                        if not is_valid:
                            if self.__defaultSegId != self.__defaultSegId__:
                                __preferAuthSeq = self.__preferAuthSeq
                                self.__preferAuthSeq = not __preferAuthSeq
                                chainAssign, _ = self.assignCoordPolymerSequenceWithChainId(self.__defaultSegId__,
                                                                                            resId, resName, atomName, src_index)
                                is_valid = is_valid_chain_assign(chainAssign, resName)
                                self.__preferAuthSeq = __preferAuthSeq
                        if is_valid:
                            idx = next((chainAssign.index(a) for a in chainAssign if a[2] == resName), -1)
                            if idx != -1:
                                # if self.__defaultSegId is None:
                                self.__defaultSegId = chainAssign[idx][0]
                            else:
                                idx = 0
                            segId = chainAssign[idx][0]
                        else:
                            chainAssign, _ = self.assignCoordPolymerSequence(None,
                                                                             resId, resName, atomName, src_index)
                            if is_valid_chain_assign(chainAssign, resName):
                                idx = next((chainAssign.index(a) for a in chainAssign if a[2] == resName), -1)
                                if idx != -1:
                                    # if self.__defaultSegId is None:
                                    self.__defaultSegId = chainAssign[idx][0]
                                    if self.reasons is None:
                                        r = self.__getNamedReasonsForReparsing('default_seg_id')
                                        if self.cur_list_id not in r:
                                            r[self.cur_list_id] = self.__defaultSegId
                                else:
                                    idx = 0
                                segId = chainAssign[idx][0]
                    elif resName is None:
                        chainAssign = self.assignCoordPolymerSequenceWithChainIdWithoutCompId(segId, resId, atomName, src_index)
                        if len(chainAssign) > 0:
                            idx = next((chainAssign.index(a) for a in chainAssign if a[1] == resId), 0)
                            resName = chainAssign[idx][2]
                    _, _, details = self.nefT.get_valid_star_atom_in_xplor(resName, atomName, leave_unmatched=True)
                    if details is not None:
                        atomName = translateToStdAtomName(atomName, resName, ccU=self.ccU)
                    ret.append({'dim_id': dimId, 'chain_id': segId, 'seq_id': resId,
                                'auth_seq_id': authResId, 'comp_id': resName, 'atom_id': atomName})
                else:
                    ass = {'dim': dimId, 'atom_id': atomName}
                    if segId is not None:
                        ass['chain_id'] = segId
                    if resId is not None:
                        ass['seq_id'] = resId
                    if resName is not None:
                        ass['comp_id'] = resName
                    ret.append(ass)
                dimId += 1
            elif atomNameLike_[idx] and siblingAtomName[idx] is not None:
                if resIdLater:
                    for _idx, _term in enumerate(_str):
                        if _idx > idx and resIdLike[_idx]:
                            resId = int(_term[resIdSpan[_idx][0]:resIdSpan[_idx][1]])
                            segId = resName = None
                            break
                if resId is None:
                    if _resId is None or len(ret) >= len(_resId):
                        return None
                    resId = _resId[len(ret)]
                for atomName in siblingAtomName[idx]:
                    if self.hasPolySeq:
                        if segId is None and resName is None:
                            chainAssign =\
                                self.assignCoordPolymerSequenceWithChainIdWithoutCompId(self.__defaultSegId, resId, atomName,
                                                                                        src_index)
                            if len(chainAssign) > 0:
                                if self.__defaultSegId is None:
                                    idx = next((chainAssign.index(a) for a in chainAssign if a[1] == resId), -1)
                                else:
                                    idx = next((chainAssign.index(a) for a in chainAssign
                                                if a[0] == self.__defaultSegId and a[1] == resId), -1)
                                    if idx == -1:
                                        idx = next((chainAssign.index(a) for a in chainAssign if a[1] == resId), -1)
                                if idx != -1:
                                    # if self.__defaultSegId is None:
                                    self.__defaultSegId = chainAssign[idx][0]
                                else:
                                    idx = 0
                                segId, _, resName, _ = chainAssign[idx]
                            else:
                                chainAssign = self.assignCoordPolymerSequenceWithoutCompId(resId, atomName, src_index)
                                if len(chainAssign) > 0:
                                    if self.__defaultSegId is None:
                                        idx = next((chainAssign.index(a) for a in chainAssign if a[1] == resId), -1)
                                    else:
                                        idx = next((chainAssign.index(a) for a in chainAssign
                                                    if a[0] == self.__defaultSegId and a[1] == resId), -1)
                                        if idx == -1:
                                            idx = next((chainAssign.index(a) for a in chainAssign if a[1] == resId), -1)
                                    if idx != -1:
                                        # if self.__defaultSegId is None:
                                        # self.__defaultSegId = chainAssign[idx][0]
                                        pass
                                    else:
                                        idx = 0
                                    segId, _, resName, _ = chainAssign[idx]
                        elif segId is None:
                            chainAssign, _ = self.assignCoordPolymerSequence(self.__defaultSegId,
                                                                             resId, resName, atomName, src_index)
                            is_valid = is_valid_chain_assign(chainAssign, resName)
                            if not is_valid:
                                if self.__defaultSegId != self.__defaultSegId__:
                                    __preferAuthSeq = self.__preferAuthSeq
                                    self.__preferAuthSeq = not __preferAuthSeq
                                    chainAssign, _ = self.assignCoordPolymerSequenceWithChainId(self.__defaultSegId__,
                                                                                                resId, resName, atomName, src_index)
                                    is_valid = is_valid_chain_assign(chainAssign, resName)
                                    self.__preferAuthSeq = __preferAuthSeq
                            if is_valid:
                                idx = next((chainAssign.index(a) for a in chainAssign if a[2] == resName), -1)
                                if idx != -1:
                                    # if self.__defaultSegId is None:
                                    self.__defaultSegId = chainAssign[idx][0]
                                else:
                                    idx = 0
                                segId = chainAssign[idx][0]
                            else:
                                chainAssign, _ = self.assignCoordPolymerSequence(None,
                                                                                 resId, resName, atomName, src_index)
                                if is_valid_chain_assign(chainAssign, resName):
                                    idx = next((chainAssign.index(a) for a in chainAssign if a[2] == resName), -1)
                                    if idx != -1:
                                        # if self.__defaultSegId is None:
                                        self.__defaultSegId = chainAssign[idx][0]
                                        if self.reasons is None:
                                            r = self.__getNamedReasonsForReparsing('default_seg_id')
                                            if self.cur_list_id not in r:
                                                r[self.cur_list_id] = self.__defaultSegId
                                    else:
                                        idx = 0
                                    segId = chainAssign[idx][0]
                        elif resName is None:
                            chainAssign = self.assignCoordPolymerSequenceWithChainIdWithoutCompId(segId, resId, atomName, src_index)
                            if len(chainAssign) > 0:
                                idx = next((chainAssign.index(a) for a in chainAssign if a[1] == resId), 0)
                                resName = chainAssign[idx][2]
                        _, _, details = self.nefT.get_valid_star_atom_in_xplor(resName, atomName, leave_unmatched=True)
                        if details is not None:
                            atomName = translateToStdAtomName(atomName, resName, ccU=self.ccU)
                        ret.append({'dim_id': dimId, 'chain_id': segId, 'seq_id': resId, 'auth_seq_id': authResId,
                                    'comp_id': resName, 'atom_id': atomName})
                    else:
                        ass = {'dim': dimId, 'atom_id': atomName}
                        if segId is not None:
                            ass['chain_id'] = segId
                        if resId is not None:
                            ass['seq_id'] = resId
                        if resName is not None:
                            ass['comp_id'] = resName
                        ret.append(ass)
                    dimId += 1

        return ret if len(ret) > 0 else None

    def getRealChainSeqId(self, ps: dict, seqId: int, compId: Optional[str], isPolySeq: bool = True,
                          isFirstTrial: bool = True
                          ) -> Tuple[str, int, Optional[str]]:
        """ Return a realistic sequence for a given polymer sequence, sequence code, and residue name.
        """

        if compId is not None:
            compId = _compId = translateToStdResName(compId, ccU=self.ccU)
            if len(_compId) == 2 and _compId.startswith('D'):
                _compId = compId[1]
        if not self.__preferAuthSeq:
            seqKey = (ps['auth_chain_id' if 'auth_chain_id' in ps else 'chain_id'], seqId)
            if seqKey in self.labelToAuthSeq:
                _chainId, _seqId = self.labelToAuthSeq[seqKey]
                if _seqId in ps['auth_seq_id' if 'auth_seq_id' in ps else 'seq_id']:
                    return _chainId, _seqId, ps['comp_id'][ps['seq_id'].index(seqId)]
                if seqKey[1] in ps['seq_id']:  # resolve conflict between label/auth sequence schemes of polymer/non-polymer (2l90)
                    idx = ps['seq_id'].index(seqKey[1])
                    return _chainId, ps['auth_seq_id'][idx], ps['comp_id'][idx]
        if seqId in ps['auth_seq_id' if 'auth_seq_id' in ps else 'seq_id']:
            if compId is None:
                return ps['auth_chain_id' if 'auth_chain_id' in ps else 'chain_id'], \
                    seqId, ps['comp_id'][ps['auth_seq_id'
                                            if 'auth_seq_id' in ps else 'seq_id'].index(seqId)]
            for idx in [_idx for _idx, _seqId in enumerate(ps['auth_seq_id'
                                                              if 'auth_seq_id' in ps else 'seq_id']) if _seqId == seqId]:
                if 'alt_comp_id' in ps and idx < len(ps['alt_comp_id']):
                    if compId in (ps['comp_id'][idx], ps['auth_comp_id'
                                                         if 'auth_comp_id' in ps else 'comp_id'][idx], ps['alt_comp_id'][idx]):
                        return ps['auth_chain_id' if 'auth_chain_id' in ps else 'chain_id'], seqId, ps['comp_id'][idx]
                    if compId != _compId\
                       and _compId in (ps['comp_id'][idx], ps['auth_comp_id'
                                                              if 'auth_comp_id' in ps else 'comp_id'][idx], ps['alt_comp_id'][idx]):
                        return ps['auth_chain_id' if 'auth_chain_id' in ps else 'chain_id'], seqId, ps['comp_id'][idx]
                if compId in (ps['comp_id'][idx], ps['auth_comp_id'
                                                     if 'auth_comp_id' in ps else 'comp_id'][idx])\
                   or (isPolySeq and seqId == 1
                       and ((compId.endswith('-N') and all(c in ps['comp_id'][idx] for c in compId.split('-')[0]))
                            or (ps['comp_id'][idx] == 'PCA' and 'P' == compId[0] and ('GL' in compId or 'N' in compId)))):
                    return ps['auth_chain_id' if 'auth_chain_id' in ps else 'chain_id'], seqId, ps['comp_id'][idx]
                if compId != _compId and _compId in (ps['comp_id'][idx],
                                                     ps['auth_comp_id' if 'auth_comp_id' in ps else 'comp_id'][idx]):
                    return ps['auth_chain_id' if 'auth_chain_id' in ps else 'chain_id'], seqId, ps['comp_id'][idx]
        if self.reasons is not None and 'extend_seq_scheme' in self.reasons:
            _ps = next((_ps for _ps in self.reasons['extend_seq_scheme']
                        if _ps['chain_id'] == ps['auth_chain_id' if 'auth_chain_id' in ps else 'chain_id']), None)
            if _ps is not None:
                if seqId in _ps['seq_id']:
                    return ps['auth_chain_id' if 'auth_chain_id' in ps else 'chain_id'], \
                        seqId, _ps['comp_id'][_ps['seq_id'].index(seqId)]
        if 'Check the 1th row of' in self.getCurrentAssignment(-1) and isFirstTrial and isPolySeq\
           and (self.reasons is None
                or not ('seq_id_remap' in self.reasons or 'chain_seq_id_remap' in self.reasons
                        or 'ext_chain_seq_id_remap' in self.reasons)):
            try:
                if not any(_ps['auth_seq_id'][0] - len(_ps['seq_id']) <= seqId <= _ps['auth_seq_id'][-1] + len(_ps['seq_id'])
                           for _ps in self.polySeq):
                    self.__preferAuthSeq = not self.__preferAuthSeq
                    trial = self.getRealChainSeqId(ps, seqId, compId, isPolySeq, False)
                    if trial[2] is not None and compId == trial[2]:
                        return trial
                    self.__preferAuthSeq = not self.__preferAuthSeq
            except ValueError:
                pass
        return ps['auth_chain_id' if 'auth_chain_id' in ps else 'chain_id'], seqId, None

    @functools.lru_cache(maxsize=256)
    def translateToStdResNameWrapper(self, seqId: int, compId: str, preferNonPoly: bool = False) -> str:
        """ A wrapper function for ParserListenerUtil.translateToStdResName().
        """

        _compId = compId
        refCompId = None
        for ps in self.polySeq:
            if preferNonPoly:
                continue
            _, _, refCompId = self.getRealChainSeqId(ps, seqId, _compId)
            if refCompId is not None:
                compId = translateToStdResName(_compId, refCompId=refCompId, ccU=self.ccU)
                if compId != _compId and compId in STD_MON_DICT and _compId in STD_MON_DICT:
                    continue
                break
        if refCompId is None:
            compId = translateToStdResName(_compId, ccU=self.ccU)
        return compId

    def assignCoordPolymerSequence(self, refChainId: str, seqId: int, compId: str, atomId: str, index: int
                                   ) -> Tuple[List[Tuple[str, int, str, bool]], bool]:
        """ Assign polymer sequences of the coordinates.
        """

        _refChainId = refChainId

        chainAssign = set()
        asis = False

        _seqId = seqId
        _compId = compId

        fixedChainId = fixedSeqId = fixedCompId = None

        preferNonPoly = False

        self.__allow_ext_seq = False

        compId = self.translateToStdResNameWrapper(_seqId, _compId, preferNonPoly)

        if self.reasons is not None:
            if 'non_poly_remap' in self.reasons and _compId in self.reasons['non_poly_remap']\
               and seqId in self.reasons['non_poly_remap'][_compId]:
                fixedChainId, fixedSeqId = retrieveRemappedNonPoly(self.reasons['non_poly_remap'], None, refChainId, seqId, _compId)
                refChainId = fixedChainId
                preferNonPoly = True
            if 'branched_remap' in self.reasons and seqId in self.reasons['branched_remap']:
                fixedChainId, fixedSeqId = retrieveRemappedChainId(self.reasons['branched_remap'], seqId)
                refChainId = fixedChainId
                preferNonPoly = True
            if not preferNonPoly:
                if 'chain_id_remap' in self.reasons and seqId in self.reasons['chain_id_remap']:
                    fixedChainId, fixedSeqId = retrieveRemappedChainId(self.reasons['chain_id_remap'], seqId)
                    refChainId = fixedChainId
                elif 'chain_id_clone' in self.reasons and seqId in self.reasons['chain_id_clone']:
                    fixedChainId, fixedSeqId = retrieveRemappedChainId(self.reasons['chain_id_clone'], seqId)
                    refChainId = fixedChainId
                elif 'seq_id_remap' in self.reasons\
                        or 'chain_seq_id_remap' in self.reasons\
                        or 'ext_chain_seq_id_remap' in self.reasons:
                    if 'ext_chain_seq_id_remap' in self.reasons:
                        fixedChainId, fixedSeqId, fixedCompId =\
                            retrieveRemappedSeqIdAndCompId(self.reasons['ext_chain_seq_id_remap'], refChainId, seqId,
                                                           compId if compId in STD_MON_DICT else None)
                        self.__allow_ext_seq = fixedCompId is not None
                        if fixedSeqId is not None:
                            refChainId = fixedChainId
                    if fixedSeqId is None and 'chain_seq_id_remap' in self.reasons:
                        fixedChainId, fixedSeqId = retrieveRemappedSeqId(self.reasons['chain_seq_id_remap'], refChainId, seqId,
                                                                         compId if compId in STD_MON_DICT else None)
                        if fixedSeqId is not None:
                            refChainId = fixedChainId
                    if fixedSeqId is None and 'seq_id_remap' in self.reasons:
                        _, fixedSeqId = retrieveRemappedSeqId(self.reasons['seq_id_remap'], refChainId, seqId)
            if fixedSeqId is not None:
                _seqId = fixedSeqId

        updatePolySeqRst(self.polySeqCs, self.polySeq[0]['chain_id'] if refChainId is None else refChainId, _seqId, compId, _compId)

        types = self.csStat.getTypeOfCompId(compId)
        if all(not t for t in types) or compId in ('MTS', 'ORI'):
            types = None
        elif compId != _compId:
            if types != self.csStat.getTypeOfCompId(_compId):
                types = None

        def comp_id_unmatched_with(ps, cif_comp_id):
            if 'alt_comp_id' in ps and self.csStat.peptideLike(cif_comp_id) and compId.startswith('D') and len(compId) >= 3\
               and self.ccU.lastChemCompDict['type'].upper() == 'D-PEPTIDE LINKING':
                revertPolySeqRst(self.polySeqCs, ps['chain_id'] if fixedChainId is None else fixedChainId, _seqId, compId)

            if types is None or ('alt_comp_id' in ps and _compId in ps['alt_comp_id']):
                return False
            if compId not in STD_MON_DICT and cif_comp_id not in STD_MON_DICT:
                return False
            return types != self.csStat.getTypeOfCompId(cif_comp_id)

        if refChainId is not None or refChainId != _refChainId:
            if any(True for ps in self.polySeq if ps['auth_chain_id' if 'auth_chain_id' in ps else 'chain_id'] == _refChainId):
                fixedChainId = _refChainId

        for ps in self.polySeq:
            if preferNonPoly:
                continue
            chainId, seqId, cifCompId = self.getRealChainSeqId(ps, _seqId, compId)
            if fixedChainId is None and refChainId is not None and refChainId != chainId and refChainId in self.chainNumberDict:
                if chainId != self.chainNumberDict[refChainId]:
                    continue
            if self.reasons is not None:
                if fixedChainId is not None:
                    if fixedChainId != chainId:
                        continue
                    if fixedSeqId is not None:
                        seqId = fixedSeqId
                elif fixedSeqId is not None:
                    seqId = fixedSeqId
            if seqId <= 0 and self.__shiftNonPosSeq is not None and chainId in self.__shiftNonPosSeq:
                seqId -= 1
            if seqId in ps['auth_seq_id' if 'auth_seq_id' in ps else 'seq_id'] or fixedCompId is not None:
                if fixedCompId is not None:
                    cifCompId = origCompId = fixedCompId
                else:
                    if cifCompId is not None:
                        idx = next((_idx for _idx, (_seqId_, _cifCompId_)
                                    in enumerate(zip(ps['auth_seq_id'if 'auth_seq_id' in ps else 'seq_id'], ps['comp_id']))
                                    if _seqId_ == seqId and _cifCompId_ == cifCompId),
                                   ps['auth_seq_id' if 'auth_seq_id' in ps else 'seq_id'].index(seqId))
                    else:
                        idx = ps['auth_seq_id' if 'auth_seq_id' in ps else 'seq_id'].index(seqId)
                    cifCompId = ps['comp_id'][idx]
                    origCompId = ps['auth_comp_id' if 'auth_comp_id' in ps else 'comp_id'][idx]
                    if comp_id_unmatched_with(ps, cifCompId):
                        continue
                if cifCompId != compId:
                    if (self.__shiftNonPosSeq is None or chainId not in self.__shiftNonPosSeq)\
                       and seqId <= 0 and seqId - 1 in ps['auth_seq_id' if 'auth_seq_id' in ps else 'seq_id']\
                       and compId == ps['comp_id'][ps['auth_seq_id' if 'auth_seq_id' in ps else 'seq_id'].index(seqId - 1)]:
                        seqId -= 1
                        if self.__shiftNonPosSeq is None:
                            self.__shiftNonPosSeq = {}
                        self.__shiftNonPosSeq[chainId] = True
                    compIds = [_compId for _seqId, _compId
                               in zip(ps['auth_seq_id' if 'auth_seq_id' in ps else 'seq_id'], ps['comp_id'])
                               if _seqId == seqId]
                    if compId in compIds:
                        cifCompId = compId
                        origCompId = next(origCompId for _seqId, _compId, origCompId
                                          in zip(ps['auth_seq_id' if 'auth_seq_id' in ps else 'seq_id'],
                                                 ps['comp_id'],
                                                 ps['auth_comp_id' if 'auth_comp_id' in ps else 'comp_id'])
                                          if _seqId == seqId and _compId == compId)
                if compId in (cifCompId, origCompId, 'MTS', 'ORI'):
                    if len(self.nefT.get_valid_star_atom(cifCompId, atomId)[0]) > 0:
                        chainAssign.add((chainId, seqId, cifCompId, True))
                        if refChainId is not None and refChainId != chainId and refChainId not in self.chainNumberDict:
                            self.chainNumberDict[refChainId] = chainId
                else:
                    _atomId, _, details = self.nefT.get_valid_star_atom(cifCompId, atomId)
                    if len(_atomId) > 0 and (details is None or _compId not in STD_MON_DICT):
                        chainAssign.add((chainId, seqId, cifCompId, True))
                        if refChainId is not None and refChainId != chainId and refChainId not in self.chainNumberDict:
                            self.chainNumberDict[refChainId] = chainId

            elif 'gap_in_auth_seq' in ps and ps['gap_in_auth_seq']:
                auth_seq_id_list = list(filter(None, ps['auth_seq_id']))
                if len(auth_seq_id_list) > 0:
                    min_auth_seq_id = min(auth_seq_id_list)
                    max_auth_seq_id = max(auth_seq_id_list)
                    if min_auth_seq_id <= seqId <= max_auth_seq_id:
                        _seqId_ = seqId + 1
                        while _seqId_ <= max_auth_seq_id:
                            if _seqId_ in ps['auth_seq_id']:
                                break
                            _seqId_ += 1
                        if _seqId_ not in ps['auth_seq_id']:
                            _seqId_ = seqId - 1
                            while _seqId_ >= min_auth_seq_id:
                                if _seqId_ in ps['auth_seq_id']:
                                    break
                                _seqId_ -= 1
                        if _seqId_ in ps['auth_seq_id']:
                            idx = ps['auth_seq_id'].index(_seqId_) - (_seqId_ - seqId)
                            try:
                                seqId_ = ps['auth_seq_id'][idx]
                                cifCompId = ps['comp_id'][idx]
                                origCompId = ps['auth_comp_id'][idx]
                                if comp_id_unmatched_with(ps, cifCompId):
                                    continue
                                if cifCompId != compId:
                                    compIds = [_compId for _seqId, _compId in zip(ps['auth_seq_id'], ps['comp_id'])
                                               if _seqId == seqId]
                                    if compId in compIds:
                                        cifCompId = compId
                                        origCompId = next(origCompId for _seqId, _compId, origCompId
                                                          in zip(ps['auth_seq_id'], ps['comp_id'], ps['auth_comp_id'])
                                                          if _seqId == seqId and _compId == compId)
                                if compId in (cifCompId, origCompId, 'MTS', 'ORI'):
                                    if len(self.nefT.get_valid_star_atom(cifCompId, atomId)[0]) > 0:
                                        chainAssign.add((chainId, seqId_, cifCompId, True))
                                        if refChainId is not None and refChainId != chainId\
                                           and refChainId not in self.chainNumberDict:
                                            self.chainNumberDict[refChainId] = chainId
                                else:
                                    _atomId, _, details = self.nefT.get_valid_star_atom(cifCompId, atomId)
                                    if len(_atomId) > 0 and (details is None or _compId not in STD_MON_DICT):
                                        chainAssign.add((chainId, seqId_, cifCompId, True))
                                        if refChainId is not None and refChainId != chainId\
                                           and refChainId not in self.chainNumberDict:
                                            self.chainNumberDict[refChainId] = chainId
                            except IndexError:
                                pass

        if len(chainAssign) == 0:
            for ps in self.polySeq:
                if preferNonPoly:
                    continue
                chainId = ps['chain_id']
                if refChainId is not None and refChainId != chainId and refChainId in self.chainNumberDict:
                    if chainId != self.chainNumberDict[refChainId]:
                        continue
                if fixedChainId is not None and fixedChainId != chainId:
                    continue
                seqKey = (chainId, _seqId)
                if seqKey in self.authToLabelSeq:
                    _, seqId = self.authToLabelSeq[seqKey]
                    if seqId in ps['seq_id']:
                        idx = ps['seq_id'].index(seqId)
                        cifCompId = ps['comp_id'][idx]
                        origCompId = ps['auth_comp_id' if 'auth_comp_id' in ps else 'comp_id'][idx]
                        if comp_id_unmatched_with(ps, cifCompId):
                            continue
                        if cifCompId != compId:
                            compIds = [_compId for _seqId, _compId
                                       in zip(ps['auth_seq_id' if 'auth_seq_id' in ps else 'seq_id'], ps['comp_id'])
                                       if _seqId == seqId]
                            if compId in compIds:
                                cifCompId = compId
                                origCompId =\
                                    next(origCompId for _seqId, _compId, origCompId
                                         in zip(ps['auth_seq_id' if 'auth_seq_id' in ps else 'seq_id'],
                                                ps['comp_id'],
                                                ps['auth_comp_id' if 'auth_comp_id' in ps else 'comp_id'])
                                         if _seqId == seqId and _compId == compId)
                        if compId in (cifCompId, origCompId, 'MTS', 'ORI'):
                            if len(self.nefT.get_valid_star_atom(cifCompId, atomId)[0]) > 0:
                                chainAssign.add((ps['auth_chain_id' if 'auth_chain_id' in ps else 'chain_id'],
                                                 _seqId, cifCompId, True))
                                if refChainId is not None and refChainId != chainId and refChainId not in self.chainNumberDict:
                                    self.chainNumberDict[refChainId] = chainId
                        else:
                            _atomId, _, details = self.nefT.get_valid_star_atom(cifCompId, atomId)
                            if len(_atomId) > 0 and (details is None or _compId not in STD_MON_DICT):
                                chainAssign.add((ps['auth_chain_id' if 'auth_chain_id' in ps else 'chain_id'],
                                                 _seqId, cifCompId, True))
                                if refChainId is not None and refChainId != chainId and refChainId not in self.chainNumberDict:
                                    self.chainNumberDict[refChainId] = chainId

        if len(chainAssign) == 0 and (self.__preferAuthSeqCount - self.__preferLabelSeqCount < MAX_PREF_LABEL_SCHEME_COUNT
                                      or len(self.polySeq) > 1):
            for ps in self.polySeq:
                if preferNonPoly:
                    continue
                chainId = ps['chain_id']
                if refChainId is not None and refChainId != chainId and refChainId in self.chainNumberDict:
                    if chainId != self.chainNumberDict[refChainId]:
                        continue
                if fixedChainId is not None and fixedChainId != chainId:
                    continue
                seqKey = (chainId, _seqId)
                if seqKey in self.labelToAuthSeq:
                    _, seqId = self.labelToAuthSeq[seqKey]
                    if seqId in ps['auth_seq_id' if 'auth_seq_id' in ps else 'seq_id']:
                        idx = ps['seq_id'].index(_seqId)
                        cifCompId = ps['comp_id'][idx]
                        origCompId = ps['auth_comp_id' if 'auth_comp_id' in ps else 'comp_id'][idx]
                        if comp_id_unmatched_with(ps, cifCompId):
                            continue
                        if cifCompId != compId:
                            compIds = [_compId for _seqId, _compId
                                       in zip(ps['auth_seq_id' if 'auth_seq_id' in ps else 'seq_id'], ps['comp_id'])
                                       if _seqId == seqId]
                            if compId in compIds:
                                cifCompId = compId
                                origCompId =\
                                    next(origCompId for _seqId, _compId, origCompId
                                         in zip(ps['auth_seq_id' if 'auth_seq_id' in ps else 'seq_id'],
                                                ps['comp_id'],
                                                ps['auth_comp_id' if 'auth_comp_id' in ps else 'comp_id'])
                                         if _seqId == seqId and _compId == compId)
                        if compId in (cifCompId, origCompId, 'MTS', 'ORI'):
                            if len(self.nefT.get_valid_star_atom(cifCompId, atomId)[0]) > 0:
                                chainAssign.add((ps['auth_chain_id' if 'auth_chain_id' in ps else 'chain_id'],
                                                 seqId, cifCompId, True))
                                if compId in (cifCompId, origCompId):
                                    self.__authSeqId = 'label_seq_id'
                                    self.__setLocalSeqScheme()
                                    if refChainId is not None and refChainId != chainId and refChainId not in self.chainNumberDict:
                                        self.chainNumberDict[refChainId] = chainId
                        else:
                            _atomId, _, details = self.nefT.get_valid_star_atom(cifCompId, atomId)
                            if len(_atomId) > 0 and (details is None or _compId not in STD_MON_DICT):
                                chainAssign.add((ps['auth_chain_id' if 'auth_chain_id' in ps else 'chain_id'],
                                                 seqId, cifCompId, True))
                                self.__authSeqId = 'label_seq_id'
                                self.__setLocalSeqScheme()
                                if refChainId is not None and refChainId != chainId and refChainId not in self.chainNumberDict:
                                    self.chainNumberDict[refChainId] = chainId

        if len(chainAssign) == 0:
            auth_seq_id_list = list(filter(None, self.polySeq[0]['auth_seq_id']))
            min_auth_seq_id = max_auth_seq_id = UNREAL_AUTH_SEQ_NUM
            if len(auth_seq_id_list) > 0:
                min_auth_seq_id = min(auth_seq_id_list)
                max_auth_seq_id = max(auth_seq_id_list)
            if len(self.polySeq) == 1\
               and (seqId < 1
                    or (compId == 'ACE' and seqId == min_auth_seq_id - 1)
                    or (compId == 'NH2' and seqId == max_auth_seq_id + 1)
                    or (compId in STD_MON_DICT
                        and self.__preferAuthSeqCount - self.__preferLabelSeqCount >= MAX_PREF_LABEL_SCHEME_COUNT)):
                refChainId = self.polySeq[0]['auth_chain_id' if 'auth_chain_id' in self.polySeq[0] else 'chain_id']
                if (compId == 'ACE' and seqId == min_auth_seq_id - 1)\
                   or (compId == 'NH2' and seqId == max_auth_seq_id + 1)\
                   or (compId in STD_MON_DICT
                       and self.__preferAuthSeqCount - self.__preferLabelSeqCount >= MAX_PREF_LABEL_SCHEME_COUNT
                       and (min_auth_seq_id - MAX_ALLOWED_EXT_SEQ <= seqId < min_auth_seq_id
                            or max_auth_seq_id < seqId <= max_auth_seq_id + MAX_ALLOWED_EXT_SEQ)):
                    self.f.append(f"[Sequence mismatch warning] {self.getCurrentAssignment(n=index)}"
                                  f"The residue '{_seqId}:{_compId}' is not present in polymer sequence "
                                  f"of chain {refChainId} of the coordinates. "
                                  "Please update the sequence in the Macromolecules page.")
                    chainAssign.add((refChainId, _seqId, compId, True))
                    asis = True
                elif compId in STD_MON_DICT\
                        and self.__preferAuthSeqCount - self.__preferLabelSeqCount >= MAX_PREF_LABEL_SCHEME_COUNT:
                    self.f.append(f"[Sequence mismatch warning] {self.getCurrentAssignment(n=index)}"
                                  f"The residue '{_seqId}:{_compId}' is not present in polymer sequence "
                                  f"of chain {refChainId} of the coordinates. "
                                  "Please update the sequence in the Macromolecules page.")
                elif self.no_extra_comment:
                    self.f.append(f"[Atom not found] {self.getCurrentAssignment(n=index)}"
                                  f"{_seqId}:{_compId}:{atomId} is not present in the coordinates. "
                                  f"The residue number '{_seqId}' is not present in polymer sequence "
                                  f"of chain {refChainId} of the coordinates. "
                                  "Please update the sequence in the Macromolecules page.")
            else:
                ext_seq = False
                if (compId in STD_MON_DICT or compId in ('ACE', 'NH2'))\
                   and self.__preferAuthSeqCount - self.__preferLabelSeqCount >= MAX_PREF_LABEL_SCHEME_COUNT:
                    refChainIds = []
                    _auth_seq_id_list = auth_seq_id_list
                    for idx, ps in enumerate(self.polySeq):
                        if idx > 0:
                            auth_seq_id_list = list(filter(None, ps['auth_seq_id' if 'auth_seq_id' in ps else 'seq_id']))
                            _auth_seq_id_list.extend(auth_seq_id_list)
                        if len(auth_seq_id_list) > 0:
                            if idx > 0:
                                min_auth_seq_id = min(auth_seq_id_list)
                                max_auth_seq_id = max(auth_seq_id_list)
                            if min_auth_seq_id - MAX_ALLOWED_EXT_SEQ <= seqId < min_auth_seq_id\
                               and (compId in STD_MON_DICT or (compId == 'ACE' and seqId == min_auth_seq_id - 1)):
                                refChainIds.append(ps['auth_chain_id' if 'auth_chain_id' in ps else 'chain_id'])
                                ext_seq = True
                            if max_auth_seq_id < seqId <= max_auth_seq_id + MAX_ALLOWED_EXT_SEQ\
                               and (compId in STD_MON_DICT or (compId == 'NH2' and seqId == max_auth_seq_id + 1)):
                                refChainIds.append(ps['auth_chain_id' if 'auth_chain_id' in ps else 'chain_id'])
                                ext_seq = True
                    if ext_seq and seqId in _auth_seq_id_list:
                        ext_seq = False
                if ext_seq:
                    refChainId = refChainIds[0] if len(refChainIds) == 1 else refChainIds
                    self.f.append(f"[Sequence mismatch warning] {self.getCurrentAssignment(n=index)}"
                                  f"The residue '{_seqId}:{_compId}' is not present in polymer sequence "
                                  f"of chain {refChainId} of the coordinates. "
                                  "Please update the sequence in the Macromolecules page.")
                    if isinstance(refChainId, str):
                        chainAssign.add((refChainId, _seqId, compId, True))
                    else:
                        for _refChainId in refChainIds:
                            chainAssign.add((_refChainId, _seqId, compId, True))
                    asis = True
                elif self.no_extra_comment:
                    self.f.append(f"[Atom not found] {self.getCurrentAssignment(n=index)}"
                                  f"{_seqId}:{_compId}:{atomId} is not present in the coordinates.")
                updatePolySeqRst(self.polySeqCsFailed,
                                 self.polySeq[0]['chain_id'] if refChainId is None else refChainId, _seqId, compId, _compId)

        elif any(True for ca in chainAssign if ca[0] == refChainId) and any(True for ca in chainAssign if ca[0] != refChainId):
            _chainAssign = copy.copy(chainAssign)
            for _ca in _chainAssign:
                if _ca[0] != refChainId:
                    chainAssign.remove(_ca)

        return list(chainAssign), asis

    def assignCoordPolymerSequenceWithChainId(self, refChainId: str, seqId: int, compId: str, atomId: str, index: int
                                              ) -> Tuple[List[Tuple[str, int, str, bool]], bool]:
        """ Assign polymer sequences of the coordinates.
        """

        _refChainId = refChainId

        chainAssign = set()
        asis = False
        _seqId = seqId
        _compId = compId

        fixedChainId = fixedSeqId = fixedCompId = None

        preferNonPoly = False

        compId = self.translateToStdResNameWrapper(_seqId, _compId, preferNonPoly)

        self.__allow_ext_seq = False

        if self.reasons is not None:
            if 'unambig_atom_id_remap' in self.reasons and _compId in self.reasons['unambig_atom_id_remap']\
               and atomId in self.reasons['unambig_atom_id_remap'][_compId]:
                atomId = self.reasons['unambig_atom_id_remap'][_compId][atomId][0]  # select representative one
            if 'non_poly_remap' in self.reasons and _compId in self.reasons['non_poly_remap']\
               and seqId in self.reasons['non_poly_remap'][_compId]:
                fixedChainId, fixedSeqId = retrieveRemappedNonPoly(self.reasons['non_poly_remap'], None,
                                                                   str(refChainId), seqId, _compId)
                refChainId = fixedChainId
                preferNonPoly = True
            if 'branched_remap' in self.reasons and seqId in self.reasons['branched_remap']:
                fixedChainId, fixedSeqId = retrieveRemappedChainId(self.reasons['branched_remap'], seqId)
                refChainId = fixedChainId
                preferNonPoly = True
            if not preferNonPoly:
                if 'chain_id_remap' in self.reasons and seqId in self.reasons['chain_id_remap']:
                    fixedChainId, fixedSeqId = retrieveRemappedChainId(self.reasons['chain_id_remap'], seqId)
                    refChainId = fixedChainId
                elif 'chain_id_clone' in self.reasons and seqId in self.reasons['chain_id_clone']:
                    fixedChainId, fixedSeqId = retrieveRemappedChainId(self.reasons['chain_id_clone'], seqId)
                    refChainId = fixedChainId
                elif 'seq_id_remap' in self.reasons\
                        or 'chain_seq_id_remap' in self.reasons\
                        or 'ext_chain_seq_id_remap' in self.reasons:
                    if 'ext_chain_seq_id_remap' in self.reasons:
                        fixedChainId, fixedSeqId, fixedCompId =\
                            retrieveRemappedSeqIdAndCompId(self.reasons['ext_chain_seq_id_remap'], str(refChainId), seqId,
                                                           compId if compId in STD_MON_DICT else None)
                        self.__allow_ext_seq = fixedCompId is not None
                        if fixedSeqId is not None:
                            refChainId = fixedChainId
                    if fixedSeqId is None and 'chain_seq_id_remap' in self.reasons:
                        fixedChainId, fixedSeqId = retrieveRemappedSeqId(self.reasons['chain_seq_id_remap'], str(refChainId), seqId,
                                                                         compId if compId in STD_MON_DICT else None)
                        if fixedSeqId is not None:
                            refChainId = fixedChainId
                    if fixedSeqId is None and 'seq_id_remap' in self.reasons:
                        _, fixedSeqId = retrieveRemappedSeqId(self.reasons['seq_id_remap'], str(refChainId), seqId)
            if fixedSeqId is not None:
                _seqId = fixedSeqId

        updatePolySeqRst(self.polySeqCs, str(refChainId), _seqId, compId, _compId)

        types = self.csStat.getTypeOfCompId(compId)
        if all(not t for t in types) or compId in ('MTS', 'ORI'):
            types = None
        elif compId != _compId:
            if types != self.csStat.getTypeOfCompId(_compId):
                types = None

        def comp_id_unmatched_with(ps, cif_comp_id):
            if 'alt_comp_id' in ps and self.csStat.peptideLike(cif_comp_id) and compId.startswith('D') and len(compId) >= 3\
               and self.ccU.lastChemCompDict['type'].upper() == 'D-PEPTIDE LINKING':
                revertPolySeqRst(self.polySeqCs, str(refChainId), _seqId, compId)

            if types is None or ('alt_comp_id' in ps and _compId in ps['alt_comp_id']):
                return False
            if compId not in STD_MON_DICT and cif_comp_id not in STD_MON_DICT:
                return False
            return types != self.csStat.getTypeOfCompId(cif_comp_id)

        if refChainId is not None or refChainId != _refChainId:
            if any(True for ps in self.polySeq if ps['auth_chain_id' if 'auth_chain_id' in ps else 'chain_id'] == _refChainId):
                fixedChainId = _refChainId

        for ps in self.polySeq:
            if preferNonPoly:
                continue
            chainId, seqId, cifCompId = self.getRealChainSeqId(ps, _seqId, compId)
            if fixedChainId is None and refChainId is not None and refChainId != chainId and refChainId in self.chainNumberDict:
                if chainId != self.chainNumberDict[refChainId]:
                    continue
            if self.reasons is not None:
                if fixedChainId is not None:
                    if fixedChainId != chainId:
                        continue
                    if fixedSeqId is not None:
                        seqId = fixedSeqId
                elif fixedSeqId is not None:
                    seqId = fixedSeqId
            if seqId <= 0 and self.__shiftNonPosSeq is not None and chainId in self.__shiftNonPosSeq:
                seqId -= 1
            if seqId in ps['auth_seq_id' if 'auth_seq_id' in ps else 'seq_id'] or fixedCompId is not None:
                if fixedCompId is not None:
                    cifCompId = origCompId = fixedCompId
                else:
                    if cifCompId is not None:
                        idx = next((_idx for _idx, (_seqId_, _cifCompId_)
                                    in enumerate(zip(ps['auth_seq_id' if 'auth_seq_id' in ps else 'seq_id'], ps['comp_id']))
                                    if _seqId_ == seqId and _cifCompId_ == cifCompId),
                                   ps['auth_seq_id' if 'auth_seq_id' in ps else 'seq_id'].index(seqId))
                    else:
                        idx = ps['auth_seq_id' if 'auth_seq_id' in ps else 'seq_id'].index(seqId)
                    cifCompId = ps['comp_id'][idx]
                    origCompId = ps['auth_comp_id' if 'auth_comp_id' in ps else 'comp_id'][idx]
                    if comp_id_unmatched_with(ps, cifCompId):
                        continue
                if cifCompId != compId:
                    if (self.__shiftNonPosSeq is None or chainId not in self.__shiftNonPosSeq)\
                       and seqId <= 0 and seqId - 1 in ps['auth_seq_id' if 'auth_seq_id' in ps else 'seq_id']\
                       and compId == ps['comp_id'][ps['auth_seq_id' if 'auth_seq_id' in ps else 'seq_id'].index(seqId - 1)]:
                        seqId -= 1
                        if self.__shiftNonPosSeq is None:
                            self.__shiftNonPosSeq = {}
                        self.__shiftNonPosSeq[chainId] = True
                    compIds = [_compId for _seqId, _compId
                               in zip(ps['auth_seq_id' if 'auth_seq_id' in ps else 'seq_id'], ps['comp_id'])
                               if _seqId == seqId]
                    if compId in compIds:
                        cifCompId = compId
                        origCompId = next(origCompId for _seqId, _compId, origCompId
                                          in zip(ps['auth_seq_id' if 'auth_seq_id' in ps else 'seq_id'],
                                                 ps['comp_id'],
                                                 ps['auth_comp_id' if 'auth_comp_id' in ps else 'comp_id'])
                                          if _seqId == seqId and _compId == compId)
                if compId in (cifCompId, origCompId, 'MTS', 'ORI'):
                    if len(self.nefT.get_valid_star_atom(cifCompId, atomId)[0]) > 0:
                        chainAssign.add((chainId, seqId, cifCompId, True))
                        if refChainId is not None and refChainId != chainId and refChainId not in self.chainNumberDict:
                            self.chainNumberDict[refChainId] = chainId
                else:
                    _atomId, _, details = self.nefT.get_valid_star_atom(cifCompId, atomId)
                    if len(_atomId) > 0 and (details is None or _compId not in STD_MON_DICT):
                        chainAssign.add((chainId, seqId, cifCompId, True))
                        if refChainId is not None and refChainId != chainId and refChainId not in self.chainNumberDict:
                            self.chainNumberDict[refChainId] = chainId

            elif 'gap_in_auth_seq' in ps and ps['gap_in_auth_seq']:
                auth_seq_id_list = list(filter(None, ps['auth_seq_id']))
                if len(auth_seq_id_list) > 0:
                    min_auth_seq_id = min(auth_seq_id_list)
                    max_auth_seq_id = max(auth_seq_id_list)
                    if min_auth_seq_id <= seqId <= max_auth_seq_id:
                        _seqId_ = seqId + 1
                        while _seqId_ <= max_auth_seq_id:
                            if _seqId_ in ps['auth_seq_id']:
                                break
                            _seqId_ += 1
                        if _seqId_ not in ps['auth_seq_id']:
                            _seqId_ = seqId - 1
                            while _seqId_ >= min_auth_seq_id:
                                if _seqId_ in ps['auth_seq_id']:
                                    break
                                _seqId_ -= 1
                        if _seqId_ in ps['auth_seq_id']:
                            idx = ps['auth_seq_id'].index(_seqId_) - (_seqId_ - seqId)
                            try:
                                seqId_ = ps['auth_seq_id'][idx]
                                cifCompId = ps['comp_id'][idx]
                                origCompId = ps['auth_comp_id'][idx]
                                if comp_id_unmatched_with(ps, cifCompId):
                                    continue
                                if cifCompId != compId:
                                    compIds = [_compId for _seqId, _compId in zip(ps['auth_seq_id'], ps['comp_id'])
                                               if _seqId == seqId]
                                    if compId in compIds:
                                        cifCompId = compId
                                        origCompId = next(origCompId for _seqId, _compId, origCompId
                                                          in zip(ps['auth_seq_id'], ps['comp_id'], ps['auth_comp_id'])
                                                          if _seqId == seqId and _compId == compId)
                                if compId in (cifCompId, origCompId, 'MTS', 'ORI'):
                                    if len(self.nefT.get_valid_star_atom(cifCompId, atomId)[0]) > 0:
                                        chainAssign.add((chainId, seqId_, cifCompId, True))
                                    else:
                                        _atomId, _, details = self.nefT.get_valid_star_atom(cifCompId, atomId)
                                        if len(_atomId) > 0 and (details is None or _compId not in STD_MON_DICT):
                                            chainAssign.add((chainId, seqId_, cifCompId, True))
                            except IndexError:
                                pass

        if len(chainAssign) == 0:
            for ps in self.polySeq:
                if preferNonPoly:
                    continue
                chainId = ps['chain_id']
                if refChainId is not None and refChainId != chainId and refChainId in self.chainNumberDict:
                    if chainId != self.chainNumberDict[refChainId]:
                        continue
                if fixedChainId is not None and fixedChainId != chainId:
                    continue
                seqKey = (chainId, _seqId)
                if seqKey in self.authToLabelSeq:
                    _, seqId = self.authToLabelSeq[seqKey]
                    if seqId in ps['seq_id']:
                        idx = ps['seq_id'].index(seqId)
                        cifCompId = ps['comp_id'][idx]
                        origCompId = ps['auth_comp_id' if 'auth_comp_id' in ps else 'comp_id'][idx]
                        if comp_id_unmatched_with(ps, cifCompId):
                            continue
                        if cifCompId != compId:
                            compIds = [_compId for _seqId, _compId
                                       in zip(ps['auth_seq_id' if 'auth_seq_id' in ps else 'seq_id'], ps['comp_id'])
                                       if _seqId == seqId]
                            if compId in compIds:
                                cifCompId = compId
                                origCompId =\
                                    next(origCompId for _seqId, _compId, origCompId
                                         in zip(ps['auth_seq_id' if 'auth_seq_id' in ps else 'seq_id'],
                                                ps['comp_id'],
                                                ps['auth_comp_id' if 'auth_comp_id' in ps else 'comp_id'])
                                         if _seqId == seqId and _compId == compId)
                        if compId in (cifCompId, origCompId, 'MTS', 'ORI'):
                            if len(self.nefT.get_valid_star_atom(cifCompId, atomId)[0]) > 0:
                                chainAssign.add((ps['auth_chain_id' if 'auth_chain_id' in ps else 'chain_id'],
                                                 _seqId, cifCompId, True))
                                if refChainId is not None and refChainId != chainId and refChainId not in self.chainNumberDict:
                                    self.chainNumberDict[refChainId] = chainId
                        else:
                            _atomId, _, details = self.nefT.get_valid_star_atom(cifCompId, atomId)
                            if len(_atomId) > 0 and (details is None or _compId not in STD_MON_DICT):
                                chainAssign.add((ps['auth_chain_id' if 'auth_chain_id' in ps else 'chain_id'],
                                                 _seqId, cifCompId, True))
                                if refChainId is not None and refChainId != chainId and refChainId not in self.chainNumberDict:
                                    self.chainNumberDict[refChainId] = chainId

        if len(chainAssign) == 0 and (self.__preferAuthSeqCount - self.__preferLabelSeqCount < MAX_PREF_LABEL_SCHEME_COUNT
                                      or len(self.polySeq) > 1):
            for ps in self.polySeq:
                if preferNonPoly:
                    continue
                chainId = ps['chain_id']
                if refChainId is not None and refChainId != chainId and refChainId in self.chainNumberDict:
                    if chainId != self.chainNumberDict[refChainId]:
                        continue
                if fixedChainId is not None and fixedChainId != chainId:
                    continue
                seqKey = (chainId, _seqId)
                if seqKey in self.labelToAuthSeq:
                    _, seqId = self.labelToAuthSeq[seqKey]
                    if seqId in ps['auth_seq_id' if 'auth_seq_id' in ps else 'seq_id']:
                        idx = ps['seq_id'].index(_seqId)
                        cifCompId = ps['comp_id'][idx]
                        origCompId = ps['auth_comp_id' if 'auth_comp_id' in ps else 'comp_id'][idx]
                        if comp_id_unmatched_with(ps, cifCompId):
                            continue
                        if cifCompId != compId:
                            compIds = [_compId for _seqId, _compId
                                       in zip(ps['auth_seq_id' if 'auth_seq_id' in ps else 'seq_id'], ps['comp_id'])
                                       if _seqId == seqId]
                            if compId in compIds:
                                cifCompId = compId
                                origCompId =\
                                    next(origCompId for _seqId, _compId, origCompId
                                         in zip(ps['auth_seq_id' if 'auth_seq_id' in ps else 'seq_id'],
                                                ps['comp_id'],
                                                ps['auth_comp_id' if 'auth_comp_id' in ps else 'comp_id'])
                                         if _seqId == seqId and _compId == compId)
                        if compId in (cifCompId, origCompId, 'MTS', 'ORI'):
                            if len(self.nefT.get_valid_star_atom(cifCompId, atomId)[0]) > 0:
                                chainAssign.add((ps['auth_chain_id' if 'auth_chain_id' in ps else 'chain_id'],
                                                 seqId, cifCompId, True))
                                if compId in (cifCompId, origCompId):
                                    self.__authSeqId = 'label_seq_id'
                                    self.__setLocalSeqScheme()
                                    if refChainId is not None and refChainId != chainId and refChainId not in self.chainNumberDict:
                                        self.chainNumberDict[refChainId] = chainId
                        else:
                            _atomId, _, details = self.nefT.get_valid_star_atom(cifCompId, atomId)
                            if len(_atomId) > 0 and (details is None or _compId not in STD_MON_DICT):
                                chainAssign.add((ps['auth_chain_id' if 'auth_chain_id' in ps else 'chain_id'],
                                                 seqId, cifCompId, True))
                                self.__authSeqId = 'label_seq_id'
                                self.__setLocalSeqScheme()
                                if refChainId is not None and refChainId != chainId and refChainId not in self.chainNumberDict:
                                    self.chainNumberDict[refChainId] = chainId

        if len(chainAssign) == 0:
            auth_seq_id_list = list(filter(None, self.polySeq[0]['auth_seq_id']))
            min_auth_seq_id = max_auth_seq_id = UNREAL_AUTH_SEQ_NUM
            if len(auth_seq_id_list) > 0:
                min_auth_seq_id = min(auth_seq_id_list)
                max_auth_seq_id = max(auth_seq_id_list)
            if len(self.polySeq) == 1\
               and (seqId < 1
                    or (compId == 'ACE' and seqId == min_auth_seq_id - 1)
                    or (compId == 'NH2' and seqId == max_auth_seq_id + 1)
                    or (compId in STD_MON_DICT
                        and self.__preferAuthSeqCount - self.__preferLabelSeqCount >= MAX_PREF_LABEL_SCHEME_COUNT)):
                refChainId = self.polySeq[0]['auth_chain_id' if 'auth_chain_id' in self.polySeq[0] else 'chain_id']
                if (compId == 'ACE' and seqId == min_auth_seq_id - 1)\
                   or (compId == 'NH2' and seqId == max_auth_seq_id + 1)\
                   or (compId in STD_MON_DICT
                       and self.__preferAuthSeqCount - self.__preferLabelSeqCount >= MAX_PREF_LABEL_SCHEME_COUNT
                       and (min_auth_seq_id - MAX_ALLOWED_EXT_SEQ <= seqId < min_auth_seq_id
                            or max_auth_seq_id < seqId <= max_auth_seq_id + MAX_ALLOWED_EXT_SEQ)):
                    self.f.append(f"[Sequence mismatch warning] {self.getCurrentAssignment(n=index)}"
                                  f"The residue '{_seqId}:{_compId}' is not present in polymer sequence "
                                  f"of chain {refChainId} of the coordinates. "
                                  "Please update the sequence in the Macromolecules page.")
                    chainAssign.add((refChainId, _seqId, compId, True))
                    asis = True
                elif compId in STD_MON_DICT\
                        and self.__preferAuthSeqCount - self.__preferLabelSeqCount >= MAX_PREF_LABEL_SCHEME_COUNT:
                    self.f.append(f"[Sequence mismatch warning] {self.getCurrentAssignment(n=index)}"
                                  f"The residue '{_seqId}:{_compId}' is not present in polymer sequence "
                                  f"of chain {refChainId} of the coordinates. "
                                  "Please update the sequence in the Macromolecules page.")
                elif self.no_extra_comment:
                    self.f.append(f"[Atom not found] {self.getCurrentAssignment(n=index)}"
                                  f"{_seqId}:{_compId}:{atomId} is not present in the coordinates. "
                                  f"The residue number '{_seqId}' is not present in polymer sequence "
                                  f"of chain {refChainId} of the coordinates. "
                                  "Please update the sequence in the Macromolecules page.")
            else:
                ext_seq = False
                if (compId in STD_MON_DICT or compId in ('ACE', 'NH2'))\
                   and self.__preferAuthSeqCount - self.__preferLabelSeqCount >= MAX_PREF_LABEL_SCHEME_COUNT:
                    refChainIds = []
                    _auth_seq_id_list = auth_seq_id_list
                    for idx, ps in enumerate(self.polySeq):
                        if idx > 0:
                            auth_seq_id_list = list(filter(None, ps['auth_seq_id' if 'auth_seq_id' in ps else 'seq_id']))
                            _auth_seq_id_list.extend(auth_seq_id_list)
                        if len(auth_seq_id_list) > 0:
                            if idx > 0:
                                min_auth_seq_id = min(auth_seq_id_list)
                                max_auth_seq_id = max(auth_seq_id_list)
                            if min_auth_seq_id - MAX_ALLOWED_EXT_SEQ <= seqId < min_auth_seq_id\
                               and (compId in STD_MON_DICT or (compId == 'ACE' and seqId == min_auth_seq_id - 1)):
                                refChainIds.append(ps['auth_chain_id' if 'auth_chain_id' in ps else 'chain_id'])
                                ext_seq = True
                            if max_auth_seq_id < seqId <= max_auth_seq_id + MAX_ALLOWED_EXT_SEQ\
                               and (compId in STD_MON_DICT or (compId == 'NH2' and seqId == max_auth_seq_id + 1)):
                                refChainIds.append(ps['auth_chain_id' if 'auth_chain_id' in ps else 'chain_id'])
                                ext_seq = True
                    if ext_seq and seqId in _auth_seq_id_list:
                        ext_seq = False
                if ext_seq:
                    refChainId = refChainIds[0] if len(refChainIds) == 1 else refChainIds
                    self.f.append(f"[Sequence mismatch warning] {self.getCurrentAssignment(n=index)}"
                                  f"The residue '{_seqId}:{_compId}' is not present in polymer sequence "
                                  f"of chain {refChainId} of the coordinates. "
                                  "Please update the sequence in the Macromolecules page.")
                    if isinstance(refChainId, str):
                        chainAssign.add((refChainId, _seqId, compId, True))
                    else:
                        for _refChainId in refChainIds:
                            chainAssign.add((_refChainId, _seqId, compId, True))
                    asis = True
                elif self.no_extra_comment:
                    self.f.append(f"[Atom not found] {self.getCurrentAssignment(n=index)}"
                                  f"{_seqId}:{_compId}:{atomId} is not present in the coordinates.")
                updatePolySeqRst(self.polySeqCsFailed, str(refChainId), _seqId, compId, _compId)

        elif any(True for ca in chainAssign if ca[0] == refChainId) and any(True for ca in chainAssign if ca[0] != refChainId):
            _chainAssign = copy.copy(chainAssign)
            for _ca in _chainAssign:
                if _ca[0] != refChainId:
                    chainAssign.remove(_ca)

        return list(chainAssign), asis

    def assignCoordPolymerSequenceWithoutCompId(self, seqId: int, atomId: str, index: int
                                                ) -> List[Tuple[str, int, str, bool]]:
        """ Assign polymer sequences of the coordinates.
        """

        chainAssign = set()
        _seqId = seqId

        fixedChainId = fixedSeqId = fixedCompId = None

        self.__allow_ext_seq = False

        if self.reasons is not None:
            if 'branched_remap' in self.reasons and seqId in self.reasons['branched_remap']:
                fixedChainId, fixedSeqId = retrieveRemappedChainId(self.reasons['branched_remap'], seqId)
            if 'chain_id_remap' in self.reasons and seqId in self.reasons['chain_id_remap']:
                fixedChainId, fixedSeqId = retrieveRemappedChainId(self.reasons['chain_id_remap'], seqId)
            elif 'chain_id_clone' in self.reasons and seqId in self.reasons['chain_id_clone']:
                fixedChainId, fixedSeqId = retrieveRemappedChainId(self.reasons['chain_id_clone'], seqId)
            if fixedSeqId is not None:
                seqId = _seqId = fixedSeqId

        for ps in self.polySeq:
            chainId, seqId, cifCompId = self.getRealChainSeqId(ps, _seqId, None)
            if self.reasons is not None:
                if 'seq_id_remap' not in self.reasons\
                   and 'chain_seq_id_remap' not in self.reasons\
                   and 'ext_chain_seq_id_remap' not in self.reasons:
                    if fixedChainId is not None and fixedChainId != chainId:
                        continue
                else:
                    if 'ext_chain_seq_id_remap' in self.reasons:
                        fixedChainId, fixedSeqId, fixedCompId =\
                            retrieveRemappedSeqIdAndCompId(self.reasons['ext_chain_seq_id_remap'], chainId, seqId)
                        if fixedChainId is not None and fixedChainId != chainId:
                            continue
                        if fixedSeqId is not None:
                            self.__allow_ext_seq = fixedCompId is not None
                            seqId = _seqId = fixedSeqId
                    if fixedSeqId is None and 'chain_seq_id_remap' in self.reasons:
                        fixedChainId, fixedSeqId = retrieveRemappedSeqId(self.reasons['chain_seq_id_remap'], chainId, seqId)
                        if fixedChainId is not None and fixedChainId != chainId:
                            continue
                        if fixedSeqId is not None:
                            seqId = _seqId = fixedSeqId
                    if fixedSeqId is None and 'seq_id_remap' in self.reasons:
                        _, fixedSeqId = retrieveRemappedSeqId(self.reasons['seq_id_remap'], chainId, seqId)
                        if fixedSeqId is not None:
                            seqId = _seqId = fixedSeqId
            if seqId in ps['auth_seq_id' if 'auth_seq_id' in ps else 'seq_id'] or fixedCompId is not None:
                if fixedCompId is not None:
                    cifCompId = fixedCompId
                else:
                    if cifCompId is not None:
                        idx = next((_idx for _idx, (_seqId_, _cifCompId_)
                                    in enumerate(zip(ps['auth_seq_id' if 'auth_seq_id' in ps else 'seq_id'], ps['comp_id']))
                                    if _seqId_ == seqId and _cifCompId_ == cifCompId),
                                   ps['auth_seq_id' if 'auth_seq_id' in ps else 'seq_id'].index(seqId))
                    else:
                        idx = ps['auth_seq_id' if 'auth_seq_id' in ps else 'seq_id'].index(seqId)
                    cifCompId = ps['comp_id'][idx]
                if self.reasons is not None:
                    if 'non_poly_remap' in self.reasons and cifCompId in self.reasons['non_poly_remap']\
                       and seqId in self.reasons['non_poly_remap'][cifCompId]:
                        fixedChainId, fixedSeqId = retrieveRemappedNonPoly(self.reasons['non_poly_remap'], None,
                                                                           chainId, seqId, cifCompId)
                        if fixedSeqId is not None:
                            seqId = _seqId = fixedSeqId
                        if (fixedChainId is not None and fixedChainId != chainId)\
                           or seqId not in ps['auth_seq_id' if 'auth_seq_id' in ps else 'seq_id']:
                            continue
                updatePolySeqRst(self.polySeqCs, chainId, _seqId, cifCompId)
                if atomId is None or len(self.nefT.get_valid_star_atom(cifCompId, atomId)[0]) > 0:
                    chainAssign.add((chainId, seqId, cifCompId, True))
            elif 'gap_in_auth_seq' in ps and ps['gap_in_auth_seq']:
                auth_seq_id_list = list(filter(None, ps['auth_seq_id']))
                if len(auth_seq_id_list) > 0:
                    min_auth_seq_id = min(auth_seq_id_list)
                    max_auth_seq_id = max(auth_seq_id_list)
                    if min_auth_seq_id <= seqId <= max_auth_seq_id:
                        _seqId_ = seqId + 1
                        while _seqId_ <= max_auth_seq_id:
                            if _seqId_ in ps['auth_seq_id']:
                                break
                            _seqId_ += 1
                        if _seqId_ not in ps['auth_seq_id']:
                            _seqId_ = seqId - 1
                            while _seqId_ >= min_auth_seq_id:
                                if _seqId_ in ps['auth_seq_id']:
                                    break
                                _seqId_ -= 1
                        if _seqId_ in ps['auth_seq_id']:
                            idx = ps['auth_seq_id'].index(_seqId_) - (_seqId_ - seqId)
                            try:
                                seqId_ = ps['auth_seq_id'][idx]
                                cifCompId = ps['comp_id'][idx]
                                updatePolySeqRst(self.polySeqCs, chainId, _seqId, cifCompId)
                                if atomId is None or len(self.nefT.get_valid_star_atom(cifCompId, atomId)[0]) > 0:
                                    chainAssign.add((chainId, seqId_, cifCompId, True))
                            except IndexError:
                                pass

        if len(chainAssign) == 0:
            for ps in self.polySeq:
                chainId = ps['chain_id']
                if fixedChainId is not None and fixedChainId != chainId:
                    continue
                seqKey = (chainId, _seqId)
                if seqKey in self.authToLabelSeq:
                    _, seqId = self.authToLabelSeq[seqKey]
                    if seqId in ps['seq_id']:
                        cifCompId = ps['comp_id'][ps['seq_id'].index(seqId)]
                        updatePolySeqRst(self.polySeqCs, chainId, _seqId, cifCompId)
                        if atomId is None or len(self.nefT.get_valid_star_atom(cifCompId, atomId)[0]) > 0:
                            chainAssign.add((ps['auth_chain_id' if 'auth_chain_id' in ps else 'chain_id'], _seqId, cifCompId, True))

        if len(chainAssign) == 0 and (self.__preferAuthSeqCount - self.__preferLabelSeqCount < MAX_PREF_LABEL_SCHEME_COUNT
                                      or len(self.polySeq) > 1):
            for ps in self.polySeq:
                chainId = ps['chain_id']
                if fixedChainId is not None and fixedChainId != chainId:
                    continue
                seqKey = (chainId, _seqId)
                if seqKey in self.labelToAuthSeq:
                    _, seqId = self.labelToAuthSeq[seqKey]
                    if seqId in ps['auth_seq_id' if 'auth_seq_id' in ps else 'seq_id']:
                        cifCompId = ps['comp_id'][ps['auth_seq_id' if 'auth_seq_id' in ps else 'seq_id'].index(seqId)]
                        updatePolySeqRst(self.polySeqCs, chainId, seqId, cifCompId)
                        if atomId is None or len(self.nefT.get_valid_star_atom(cifCompId, atomId)[0]) > 0:
                            chainAssign.add((ps['auth_chain_id' if 'auth_chain_id' in ps else 'chain_id'], seqId, cifCompId, True))
                            self.__authSeqId = 'label_seq_id'
                            self.__setLocalSeqScheme()

        if len(chainAssign) == 0:
            if atomId is not None and (('-' in atomId and ':' in atomId) or '.' in atomId):
                if self.no_extra_comment:
                    self.f.append(f"[Atom not found] {self.getCurrentAssignment(n=index)}"
                                  f"{_seqId}:?:{atomId} is not present in the coordinates.")
            elif atomId is not None:
                if len(self.polySeq) == 1 and seqId < 1:
                    refChainId = self.polySeq[0]['auth_chain_id' if 'auth_chain_id' in self.polySeq[0] else 'chain_id']
                    if self.no_extra_comment:
                        self.f.append(f"[Atom not found] {self.getCurrentAssignment(n=index)}"
                                      f"{_seqId}:?:{atomId} is not present in the coordinates. "
                                      f"The residue number '{_seqId}' is not present in polymer sequence "
                                      f"of chain {refChainId} of the coordinates. "
                                      "Please update the sequence in the Macromolecules page.")
                else:
                    if self.no_extra_comment:
                        self.f.append(f"[Atom not found] {self.getCurrentAssignment(n=index)}"
                                      f"{_seqId}:{atomId} is not present in the coordinates.")
                    compIds = guessCompIdFromAtomId([atomId], self.polySeq, self.nefT)
                    if compIds is not None:
                        chainId = fixedChainId
                        if chainId is None and len(self.polySeq) == 1:
                            chainId = self.polySeq[0]['chain_id']
                        if chainId is not None:
                            if len(compIds) == 1:
                                updatePolySeqRst(self.polySeqCsFailed, chainId, seqId, compIds[0])
                            else:
                                updatePolySeqRstAmbig(self.polySeqCsFailedAmbig, chainId, seqId, compIds)

        return list(chainAssign)

    def assignCoordPolymerSequenceWithChainIdWithoutCompId(self, fixedChainId: str, seqId: int, atomId: str, index: int
                                                           ) -> List[Tuple[str, int, str, bool]]:
        """ Assign polymer sequences of the coordinates.
        """

        chainAssign = set()
        _seqId = seqId

        fixedSeqId = fixedCompId = None

        self.__allow_ext_seq = False

        if self.reasons is not None:
            if 'branched_remap' in self.reasons and seqId in self.reasons['branched_remap']:
                fixedChainId, fixedSeqId = retrieveRemappedChainId(self.reasons['branched_remap'], seqId)
            if 'chain_id_remap' in self.reasons and seqId in self.reasons['chain_id_remap']:
                fixedChainId, fixedSeqId = retrieveRemappedChainId(self.reasons['chain_id_remap'], seqId)
            elif 'chain_id_clone' in self.reasons and seqId in self.reasons['chain_id_clone']:
                fixedChainId, fixedSeqId = retrieveRemappedChainId(self.reasons['chain_id_clone'], seqId)
            if fixedSeqId is not None:
                seqId = _seqId = fixedSeqId

        for ps in self.polySeq:
            chainId, seqId, cifCompId = self.getRealChainSeqId(ps, _seqId, None)
            if chainId != fixedChainId:
                continue
            if self.reasons is not None:
                if 'seq_id_remap' not in self.reasons\
                   and 'chain_seq_id_remap' not in self.reasons\
                   and 'ext_chain_seq_id_remap' not in self.reasons:
                    if fixedChainId is not None and fixedChainId != chainId:
                        continue
                else:
                    if 'ext_chain_seq_id_remap' in self.reasons:
                        fixedChainId, fixedSeqId, fixedCompId =\
                            retrieveRemappedSeqIdAndCompId(self.reasons['ext_chain_seq_id_remap'], chainId, seqId)
                        if fixedChainId is not None and fixedChainId != chainId:
                            continue
                        if fixedSeqId is not None:
                            self.__allow_ext_seq = fixedCompId is not None
                            seqId = _seqId = fixedSeqId
                    if fixedSeqId is None and 'chain_seq_id_remap' in self.reasons:
                        fixedChainId, fixedSeqId = retrieveRemappedSeqId(self.reasons['chain_seq_id_remap'], chainId, seqId)
                        if fixedChainId is not None and fixedChainId != chainId:
                            continue
                        if fixedSeqId is not None:
                            seqId = _seqId = fixedSeqId
                    if fixedSeqId is None and 'seq_id_remap' in self.reasons:
                        _, fixedSeqId = retrieveRemappedSeqId(self.reasons['seq_id_remap'], chainId, seqId)
                        if fixedSeqId is not None:
                            seqId = _seqId = fixedSeqId
            if seqId in ps['auth_seq_id' if 'auth_seq_id' in ps else 'seq_id'] or fixedCompId is not None:
                if fixedCompId is not None:
                    cifCompId = fixedCompId
                else:
                    if cifCompId is not None:
                        idx = next((_idx for _idx, (_seqId_, _cifCompId_)
                                    in enumerate(zip(ps['auth_seq_id' if 'auth_seq_id' in ps else 'seq_id'], ps['comp_id']))
                                    if _seqId_ == seqId and _cifCompId_ == cifCompId),
                                   ps['auth_seq_id' if 'auth_seq_id' in ps else 'seq_id'].index(seqId))
                    else:
                        idx = ps['auth_seq_id' if 'auth_seq_id' in ps else 'seq_id'].index(seqId)
                    cifCompId = ps['comp_id'][idx]
                if self.reasons is not None:
                    if 'non_poly_remap' in self.reasons and cifCompId in self.reasons['non_poly_remap']\
                       and seqId in self.reasons['non_poly_remap'][cifCompId]:
                        fixedChainId, fixedSeqId = retrieveRemappedNonPoly(self.reasons['non_poly_remap'], None,
                                                                           chainId, seqId, cifCompId)
                        if fixedSeqId is not None:
                            seqId = _seqId = fixedSeqId
                        if (fixedChainId is not None and fixedChainId != chainId)\
                           or seqId not in ps['auth_seq_id' if 'auth_seq_id' in ps else 'seq_id']:
                            continue
                updatePolySeqRst(self.polySeqCs, fixedChainId, _seqId, cifCompId)
                if len(self.nefT.get_valid_star_atom(cifCompId, atomId)[0]) > 0:
                    chainAssign.add((chainId, seqId, cifCompId, True))
            elif 'gap_in_auth_seq' in ps and ps['gap_in_auth_seq']:
                auth_seq_id_list = list(filter(None, ps['auth_seq_id']))
                if len(auth_seq_id_list) > 0:
                    min_auth_seq_id = min(auth_seq_id_list)
                    max_auth_seq_id = max(auth_seq_id_list)
                    if min_auth_seq_id <= seqId <= max_auth_seq_id:
                        _seqId_ = seqId + 1
                        while _seqId_ <= max_auth_seq_id:
                            if _seqId_ in ps['auth_seq_id']:
                                break
                            _seqId_ += 1
                        if _seqId_ not in ps['auth_seq_id']:
                            _seqId_ = seqId - 1
                            while _seqId_ >= min_auth_seq_id:
                                if _seqId_ in ps['auth_seq_id']:
                                    break
                                _seqId_ -= 1
                        if _seqId_ in ps['auth_seq_id']:
                            idx = ps['auth_seq_id'].index(_seqId_) - (_seqId_ - seqId)
                            try:
                                seqId_ = ps['auth_seq_id'][idx]
                                cifCompId = ps['comp_id'][idx]
                                updatePolySeqRst(self.polySeqCs, fixedChainId, _seqId, cifCompId)
                                if len(self.nefT.get_valid_star_atom(cifCompId, atomId)[0]) > 0:
                                    chainAssign.add((chainId, seqId_, cifCompId, True))
                            except IndexError:
                                pass

        if len(chainAssign) == 0:
            for ps in self.polySeq:
                chainId = ps['chain_id']
                if chainId != fixedChainId:
                    continue
                seqKey = (chainId, _seqId)
                if seqKey in self.authToLabelSeq:
                    _, seqId = self.authToLabelSeq[seqKey]
                    if seqId in ps['seq_id']:
                        cifCompId = ps['comp_id'][ps['seq_id'].index(seqId)]
                        updatePolySeqRst(self.polySeqCs, fixedChainId, _seqId, cifCompId)
                        if len(self.nefT.get_valid_star_atom(cifCompId, atomId)[0]) > 0:
                            chainAssign.add((ps['auth_chain_id' if 'auth_chain_id' in ps else 'chain_id'], _seqId, cifCompId, True))

        if len(chainAssign) == 0 and (self.__preferAuthSeqCount - self.__preferLabelSeqCount < MAX_PREF_LABEL_SCHEME_COUNT
                                      or len(self.polySeq) > 1):
            for ps in self.polySeq:
                chainId = ps['chain_id']
                if chainId != fixedChainId:
                    continue
                seqKey = (chainId, _seqId)
                if seqKey in self.labelToAuthSeq:
                    _, seqId = self.labelToAuthSeq[seqKey]
                    if seqId in ps['auth_seq_id' if 'auth_seq_id' in ps else 'seq_id']:
                        cifCompId = ps['comp_id'][ps['auth_seq_id' if 'auth_seq_id' in ps else 'seq_id'].index(seqId)]
                        updatePolySeqRst(self.polySeqCs, fixedChainId, seqId, cifCompId)
                        if len(self.nefT.get_valid_star_atom(cifCompId, atomId)[0]) > 0:
                            chainAssign.add((ps['auth_chain_id' if 'auth_chain_id' in ps else 'chain_id'], seqId, cifCompId, True))
                            self.__authSeqId = 'label_seq_id'
                            self.__setLocalSeqScheme()

        if len(chainAssign) == 0:
            if (('-' in atomId and ':' in atomId) or '.' in atomId):
                if self.no_extra_comment:
                    self.f.append(f"[Atom not found] {self.getCurrentAssignment(n=index)}"
                                  f"{fixedChainId}:{_seqId}:?:{atomId} is not present in the coordinates.")
            else:
                if len(self.polySeq) == 1 and seqId < 1:
                    refChainId = self.polySeq[0]['auth_chain_id' if 'auth_chain_id' in self.polySeq[0] else 'chain_id']
                    if self.no_extra_comment:
                        self.f.append(f"[Atom not found] {self.getCurrentAssignment(n=index)}"
                                      f"{_seqId}:?:{atomId} is not present in the coordinates. "
                                      f"The residue number '{_seqId}' is not present in polymer sequence "
                                      f"of chain {refChainId} of the coordinates. "
                                      "Please update the sequence in the Macromolecules page.")
                else:
                    if self.no_extra_comment:
                        self.f.append(f"[Atom not found] {self.getCurrentAssignment(n=index)}"
                                      f"{fixedChainId}:{_seqId}:{atomId} is not present in the coordinates.")
                    compIds = guessCompIdFromAtomId([atomId], self.polySeq, self.nefT)
                    if compIds is not None:
                        if len(compIds) == 1:
                            updatePolySeqRst(self.polySeqCsFailed, fixedChainId, seqId, compIds[0])
                        else:
                            updatePolySeqRstAmbig(self.polySeqCsFailedAmbig, fixedChainId, seqId, compIds)

        return list(chainAssign)

    def selectCoordAtoms(self, chainAssign: List[Tuple[str, int, str, bool]], seqId: int, compId: str, atomId: str,
                         index: int, allowAmbig: bool = True, offset: int = 0) -> None:
        """ Select atoms of the coordinates.
        """

        atomSelection = []

        authAtomId = atomId

        __compId = compId
        __atomId = atomId

        compId = self.translateToStdResNameWrapper(seqId, __compId)

        for chainId, cifSeqId, cifCompId, isPolySeq in chainAssign:

            if offset != 0:
                cifSeqId += offset
                cifCompId = compId

            _, coordAtomSite = self.getCoordAtomSiteOf(chainId, cifSeqId, cifCompId, asis=self.__preferAuthSeq)

            _atomId = []
            if not isPolySeq and atomId[0] in ('Q', 'M') and coordAtomSite is not None:
                key = (chainId, cifSeqId, compId, atomId)
                if key in self.__cachedDictForStarAtom:
                    _atomId = deepcopy(self.__cachedDictForStarAtom[key])
            if len(_atomId) > 1:
                details = None
            else:
                _atomId, _, details = self.nefT.get_valid_star_atom_in_xplor(cifCompId, atomId, leave_unmatched=True)
                if details is not None:
                    if atomId != __atomId:
                        _atomId, _, details = self.nefT.get_valid_star_atom_in_xplor(cifCompId, __atomId, leave_unmatched=True)
                    elif len(atomId) > 1 and not atomId[-1].isalpha()\
                            and (atomId[0] in PSE_PRO_BEGIN_CODE or atomId[0] in ('C', 'N', 'P', 'F')):
                        _atomId, _, details = self.nefT.get_valid_star_atom_in_xplor(cifCompId, atomId[:-1], leave_unmatched=True)
                        if atomId[-1].isdigit() and int(atomId[-1]) <= len(_atomId):
                            _atomId = [_atomId[int(atomId[-1]) - 1]]

            if details is not None or atomId.endswith('"'):
                _atomId_ = translateToStdAtomName(atomId, cifCompId, ccU=self.ccU)
                if _atomId_ != atomId:
                    if atomId.startswith('HT') and len(_atomId_) == 2:
                        _atomId_ = 'H'
                    __atomId__ = self.nefT.get_valid_star_atom_in_xplor(cifCompId, _atomId_)[0]
                    if coordAtomSite is not None:
                        if any(True for _atomId_ in __atomId__ if _atomId_ in coordAtomSite['atom_id']):
                            _atomId = __atomId__
                        elif __atomId__[0][0] in PROTON_BEGIN_CODE:
                            __bondedTo = self.ccU.getBondedAtoms(cifCompId, __atomId__[0])
                            if len(__bondedTo) > 0 and __bondedTo[0] in coordAtomSite['atom_id']:
                                _atomId = __atomId__
                elif coordAtomSite is not None:
                    _atomId = []
            # _atomId = self.nefT.get_valid_star_atom(cifCompId, atomId)[0]

            if coordAtomSite is not None\
               and not any(True for _atomId_ in _atomId if _atomId_ in coordAtomSite['atom_id']):
                if atomId in coordAtomSite['atom_id']:
                    _atomId = [atomId]
                elif seqId == 1 and atomId == 'H1' and self.csStat.peptideLike(compId) and 'H' in coordAtomSite['atom_id']:
                    _atomId = ['H']

            if coordAtomSite is not None:
                atomSiteAtomId = coordAtomSite['atom_id']
                if len(_atomId) == 0 and __atomId in ZINC_ION_CODE and 'ZN' in atomSiteAtomId:
                    compId = atomId = 'ZN'
                    _atomId = [atomId]
                elif len(_atomId) == 0 and __atomId in CALCIUM_ION_CODE and 'CA' in atomSiteAtomId:
                    compId = atomId = 'CA'
                    _atomId = [atomId]
                elif not any(_atomId_ in atomSiteAtomId for _atomId_ in _atomId):
                    pass
                elif atomId[0] not in PSE_PRO_BEGIN_CODE and not all(_atomId in atomSiteAtomId for _atomId in _atomId):
                    _atomId = [_atomId_ for _atomId_ in _atomId if _atomId_ in atomSiteAtomId]

            lenAtomId = len(_atomId)
            if self.reasons is not None and compId != cifCompId and __compId == cifCompId:
                compId = cifCompId
            if compId != cifCompId and compId in STD_MON_DICT and cifCompId in STD_MON_DICT:
                multiChain = insCode = False
                if len(chainAssign) > 0:
                    chainIds = [ca[0] for ca in chainAssign]
                    multiChain = len(collections.Counter(chainIds).most_common()) > 1
                ps = next((ps for ps in self.polySeq
                           if ps['auth_chain_id' if 'auth_chain_id' in ps else 'chain_id'] == chainId), None)
                if ps is not None:
                    compIds = [_compId for _seqId, _compId
                               in zip(ps['auth_seq_id' if 'auth_seq_id' in ps else 'seq_id'], ps['comp_id'])
                               if _seqId == cifSeqId]
                    if compId in compIds:
                        insCode = True
                        cifCompId = compId
                if not multiChain and not insCode:
                    if self.__preferAuthSeq:
                        _seqKey, _coordAtomSite = self.getCoordAtomSiteOf(chainId, cifSeqId, cifCompId, asis=False)
                        if _coordAtomSite is not None and _coordAtomSite['comp_id'] == compId:
                            if lenAtomId > 0 and _atomId[0] in _coordAtomSite['atom_id']:
                                self.__authSeqId = 'label_seq_id'
                                self.__setLocalSeqScheme()
                                continue
                    self.f.append(f"[Sequence mismatch] {self.getCurrentAssignment(n=index)}"
                                  f"Residue name {__compId!r} of the chemical shift does not match with "
                                  f"{chainId}:{cifSeqId}:{cifCompId} of the coordinates.")
                    continue

            if compId != cifCompId and cifCompId in STD_MON_DICT and not isPolySeq:
                continue

            if lenAtomId == 0 and not isPolySeq and cifCompId in ELEMENT_SYMBOLS:
                _atomId = [cifCompId]
                lenAtomId = 1

            if lenAtomId == 0:
                if compId != cifCompId and any(True for item in chainAssign if item[2] == compId):
                    continue
                if seqId == 1 and isPolySeq and cifCompId == 'ACE' and cifCompId != compId and offset == 0:
                    self.selectCoordAtoms(chainAssign, seqId, compId, atomId, index, allowAmbig, offset=1)
                    return
                self.f.append(f"[Invalid atom nomenclature] {self.getCurrentAssignment(n=index)}"
                              f"{seqId}:{__compId}:{__atomId} is invalid atom nomenclature.")
                continue
            if lenAtomId > 1 and not allowAmbig:
                self.f.append(f"[Invalid atom selection] {self.getCurrentAssignment(n=index)}"
                              f"Ambiguous atom selection '{seqId}:{__compId}:{__atomId}' is not allowed.")
                continue

            if __compId != cifCompId and __compId not in self.compIdMap:
                self.compIdMap[__compId] = cifCompId

            for cifAtomId in _atomId:

                if authAtomId in ('H', 'HN') and cifAtomId in ('HN1', 'HN2', 'HNA') and self.csStat.peptideLike(cifCompId)\
                   and coordAtomSite is not None and cifAtomId not in coordAtomSite['atom_id']:
                    if cifAtomId in ('HN2', 'HNA'):
                        if 'H2' not in coordAtomSite['atom_id']:
                            continue
                        cifAtomId = 'H2'
                    if cifAtomId == 'HN1' and 'H' in coordAtomSite['atom_id']:
                        cifAtomId = 'H'

                atomSelection.append({'chain_id': chainId, 'seq_id': cifSeqId, 'comp_id': cifCompId,
                                      'atom_id': cifAtomId, 'auth_seq_id': seqId, 'auth_atom_id': authAtomId})

                self.testCoordAtomIdConsistency(chainId, cifSeqId, cifCompId, cifAtomId, coordAtomSite, index)

        if len(atomSelection) > 0:
            self.atomSelectionSet.append(atomSelection)

    def testCoordAtomIdConsistency(self, chainId: str, seqId: int, compId: str, atomId: str,
                                   coordAtomSite: Optional[dict], index: int
                                   ) -> Tuple[str, bool]:
        """ Perform consistency test for each atom in reference to the coordinates.
        """

        asis = False
        if not self.hasPolySeq:
            return atomId, asis

        found = False

        if coordAtomSite is not None:
            if atomId in coordAtomSite['atom_id']:
                found = True
            elif atomId in ('HN1', 'HN2', 'HN3') and (f'{atomId[-1]}HN' in coordAtomSite['atom_id']
                                                      or f'H{atomId[-1]}' in coordAtomSite['atom_id']):
                atomId = f'{atomId[-1]}HN' if f'{atomId[-1]}HN' in coordAtomSite['atom_id'] else f'H{atomId[-1]}'
                found = True
            elif 'alt_atom_id' in coordAtomSite and atomId in coordAtomSite['alt_atom_id']:
                found = True
                # self.__authAtomId = 'auth_atom_id'

            elif self.__preferAuthSeq:
                _seqKey, _coordAtomSite = self.getCoordAtomSiteOf(chainId, seqId, compId, asis=False)
                if _coordAtomSite is not None and _coordAtomSite['comp_id'] == compId and not self.__extendAuthSeq:
                    if atomId in _coordAtomSite['atom_id']:
                        found = True
                        self.__preferAuthSeq = False
                        self.__authSeqId = 'label_seq_id'
                        # seqKey = _seqKey
                        self.__setLocalSeqScheme()
                    elif atomId in ('HN1', 'HN2', 'HN3') and (f'{atomId[-1]}HN' in _coordAtomSite['atom_id']
                                                              or f'H{atomId[-1]}' in _coordAtomSite['atom_id']):
                        atomId = f'{atomId[-1]}HN' if f'{atomId[-1]}HN' in _coordAtomSite['atom_id'] else f'H{atomId[-1]}'
                        found = True
                        self.__preferAuthSeq = False
                        self.__authSeqId = 'label_seq_id'
                        # seqKey = _seqKey
                        self.__setLocalSeqScheme()
                    elif 'alt_atom_id' in _coordAtomSite and atomId in _coordAtomSite['alt_atom_id']:
                        found = True
                        self.__preferAuthSeq = False
                        self.__authSeqId = 'label_seq_id'
                        # self.__authAtomId = 'auth_atom_id'
                        # seqKey = _seqKey
                        self.__setLocalSeqScheme()

            else:
                self.__preferAuthSeq = True
                _seqKey, _coordAtomSite = self.getCoordAtomSiteOf(chainId, seqId, compId)
                if _coordAtomSite is not None and _coordAtomSite['comp_id'] == compId:
                    if atomId in _coordAtomSite['atom_id']:
                        found = True
                        self.__authSeqId = 'auth_seq_id'
                        # seqKey = _seqKey
                        self.__setLocalSeqScheme()
                    elif atomId in ('HN1', 'HN2', 'HN3') and (f'{atomId[-1]}HN' in _coordAtomSite['atom_id']
                                                              or f'H{atomId[-1]}' in _coordAtomSite['atom_id']):
                        atomId = f'{atomId[-1]}HN' if f'{atomId[-1]}HN' in _coordAtomSite['atom_id'] else f'H{atomId[-1]}'
                        found = True
                        self.__authSeqId = 'auth_seq_id'
                        # seqKey = _seqKey
                        self.__setLocalSeqScheme()
                    elif 'alt_atom_id' in _coordAtomSite and atomId in _coordAtomSite['alt_atom_id']:
                        found = True
                        self.__authSeqId = 'auth_seq_id'
                        # self.__authAtomId = 'auth_atom_id'
                        # seqKey = _seqKey
                        self.__setLocalSeqScheme()
                    elif not self.__extendAuthSeq:
                        self.__preferAuthSeq = False
                elif not self.__extendAuthSeq:
                    self.__preferAuthSeq = False

        elif self.__preferAuthSeq:
            _seqKey, _coordAtomSite = self.getCoordAtomSiteOf(chainId, seqId, compId, asis=False)
            if _coordAtomSite is not None and _coordAtomSite['comp_id'] == compId and not self.__extendAuthSeq:
                if atomId in _coordAtomSite['atom_id']:
                    found = True
                    self.__preferAuthSeq = False
                    self.__authSeqId = 'label_seq_id'
                    # seqKey = _seqKey
                    self.__setLocalSeqScheme()
                elif atomId in ('HN1', 'HN2', 'HN3') and (f'{atomId[-1]}HN' in _coordAtomSite['atom_id']
                                                          or f'H{atomId[-1]}' in _coordAtomSite['atom_id']):
                    atomId = f'{atomId[-1]}HN' if f'{atomId[-1]}HN' in _coordAtomSite['atom_id'] else f'H{atomId[-1]}'
                    found = True
                    self.__preferAuthSeq = False
                    self.__authSeqId = 'label_seq_id'
                    # seqKey = _seqKey
                    self.__setLocalSeqScheme()
                elif 'alt_atom_id' in _coordAtomSite and atomId in _coordAtomSite['alt_atom_id']:
                    found = True
                    self.__preferAuthSeq = False
                    self.__authSeqId = 'label_seq_id'
                    # self.__authAtomId = 'auth_atom_id'
                    # seqKey = _seqKey
                    self.__setLocalSeqScheme()

        elif not self.__preferAuthSeq:
            self.__preferAuthSeq = True
            _seqKey, _coordAtomSite = self.getCoordAtomSiteOf(chainId, seqId, compId)
            if _coordAtomSite is not None and _coordAtomSite['comp_id'] == compId:
                if atomId in _coordAtomSite['atom_id']:
                    found = True
                    self.__authSeqId = 'auth_seq_id'
                    # seqKey = _seqKey
                    self.__setLocalSeqScheme()
                elif atomId in ('HN1', 'HN2', 'HN3') and (f'{atomId[-1]}HN' in _coordAtomSite['atom_id']
                                                          or f'H{atomId[-1]}' in _coordAtomSite['atom_id']):
                    atomId = f'{atomId[-1]}HN' if f'{atomId[-1]}HN' in _coordAtomSite['atom_id'] else f'H{atomId[-1]}'
                    found = True
                    self.__authSeqId = 'auth_seq_id'
                    # seqKey = _seqKey
                    self.__setLocalSeqScheme()
                elif 'alt_atom_id' in _coordAtomSite and atomId in _coordAtomSite['alt_atom_id']:
                    found = True
                    self.__authSeqId = 'auth_seq_id'
                    # self.__authAtomId = 'auth_atom_id'
                    # seqKey = _seqKey
                    self.__setLocalSeqScheme()
                elif not self.__extendAuthSeq:
                    self.__preferAuthSeq = False
            elif not self.__extendAuthSeq:
                self.__preferAuthSeq = False

        if found:
            if self.__preferAuthSeq:
                self.__preferAuthSeqCount += 1
            return atomId, asis

        if chainId in self.chainNumberDict.values():

            if self.__preferAuthSeq:
                _seqKey, _coordAtomSite = self.getCoordAtomSiteOf(chainId, seqId, compId, asis=False)
                if _coordAtomSite is not None and _coordAtomSite['comp_id'] == compId and not self.__extendAuthSeq:
                    if atomId in _coordAtomSite['atom_id']:
                        found = True
                        self.__preferAuthSeq = False
                        self.__authSeqId = 'label_seq_id'
                        # seqKey = _seqKey
                        self.__setLocalSeqScheme()
                    elif atomId in ('HN1', 'HN2', 'HN3') and (f'{atomId[-1]}HN' in _coordAtomSite['atom_id']
                                                              or f'H{atomId[-1]}' in _coordAtomSite['atom_id']):
                        atomId = f'{atomId[-1]}HN' if f'{atomId[-1]}HN' in _coordAtomSite['atom_id'] else f'H{atomId[-1]}'
                        found = True
                        self.__preferAuthSeq = False
                        self.__authSeqId = 'label_seq_id'
                        # seqKey = _seqKey
                        self.__setLocalSeqScheme()
                    elif 'alt_atom_id' in _coordAtomSite and atomId in _coordAtomSite['alt_atom_id']:
                        found = True
                        self.__preferAuthSeq = False
                        self.__authSeqId = 'label_seq_id'
                        # self.__authAtomId = 'auth_atom_id'
                        # seqKey = _seqKey
                        self.__setLocalSeqScheme()
            elif not self.__preferAuthSeq:
                self.__preferAuthSeq = True
                _seqKey, _coordAtomSite = self.getCoordAtomSiteOf(chainId, seqId, compId)
                if _coordAtomSite is not None and _coordAtomSite['comp_id'] == compId:
                    if atomId in _coordAtomSite['atom_id']:
                        found = True
                        self.__authSeqId = 'auth_seq_id'
                        # seqKey = _seqKey
                        self.__setLocalSeqScheme()
                    elif atomId in ('HN1', 'HN2', 'HN3') and (f'{atomId[-1]}HN' in _coordAtomSite['atom_id']
                                                              or f'H{atomId[-1]}' in _coordAtomSite['atom_id']):
                        atomId = f'{atomId[-1]}HN' if f'{atomId[-1]}HN' in _coordAtomSite['atom_id'] else f'H{atomId[-1]}'
                        found = True
                        self.__authSeqId = 'auth_seq_id'
                        # seqKey = _seqKey
                        self.__setLocalSeqScheme()
                    elif 'alt_atom_id' in _coordAtomSite and atomId in _coordAtomSite['alt_atom_id']:
                        found = True
                        self.__authSeqId = 'auth_seq_id'
                        # self.__authAtomId = 'auth_atom_id'
                        # seqKey = _seqKey
                        self.__setLocalSeqScheme()
                    elif not self.__extendAuthSeq:
                        self.__preferAuthSeq = False
                elif not self.__extendAuthSeq:
                    self.__preferAuthSeq = False

            if found:
                if self.__preferAuthSeq:
                    self.__preferAuthSeqCount += 1
                return atomId, asis

        if self.ccU.updateChemCompDict(compId):
            cca = next((cca for cca in self.ccU.lastAtomDictList if cca['atom_id'] == atomId), None)
            if cca is not None and self.ccU.lastChemCompDict['release_status'] == 'REL':
                ps = next((ps for ps in self.polySeq
                           if ps['auth_chain_id' if 'auth_chain_id' in ps else 'chain_id'] == chainId), None)
                auth_seq_id_list =\
                    list(filter(None, ps['auth_seq_id' if 'auth_seq_id' in ps else 'seq_id'])) if ps is not None else None
                min_auth_seq_id = max_auth_seq_id = UNREAL_AUTH_SEQ_NUM
                if auth_seq_id_list is not None and len(auth_seq_id_list) > 0:
                    min_auth_seq_id = min(auth_seq_id_list)
                    max_auth_seq_id = max(auth_seq_id_list)
                if atomId[0] in PROTON_BEGIN_CODE:
                    bondedTo = self.ccU.getBondedAtoms(compId, atomId)
                    if len(bondedTo) > 0 and bondedTo[0][0] != 'P':
                        if coordAtomSite is not None and bondedTo[0] in coordAtomSite['atom_id']:
                            if cca['leaving_atom_flag'] != 'Y'\
                               or (self.csStat.peptideLike(compId)
                                   and cca['n_terminal_atom_flag'] == 'N'
                                   and cca['c_terminal_atom_flag'] == 'N'):
                                self.f.append(f"[Hydrogen not instantiated] {self.getCurrentAssignment(n=index)}"
                                              f"{chainId}:{seqId}:{compId}:{atomId} is not properly instantiated "
                                              "in the coordinates. Please re-upload the model file.")
                                return atomId, asis
                        if bondedTo[0][0] == 'O':
                            return 'Ignorable hydroxyl group', asis

                ext_seq = False
                if auth_seq_id_list is not None and len(auth_seq_id_list) > 0:
                    if (compId == 'ACE' and seqId == min_auth_seq_id - 1)\
                       or (compId == 'NH2' and seqId == max_auth_seq_id + 1)\
                       or (compId in STD_MON_DICT
                           and self.__preferAuthSeqCount - self.__preferLabelSeqCount >= MAX_PREF_LABEL_SCHEME_COUNT
                           and (min_auth_seq_id - MAX_ALLOWED_EXT_SEQ <= seqId < min_auth_seq_id
                                or max_auth_seq_id < seqId <= max_auth_seq_id + MAX_ALLOWED_EXT_SEQ)):
                        ext_seq = True
                if chainId in LARGE_ASYM_ID:
                    if ext_seq:
                        return atomId, asis
                    if self.__allow_ext_seq:
                        self.f.append(f"[Sequence mismatch warning] {self.getCurrentAssignment(n=index)}"
                                      f"The residue '{chainId}:{seqId}:{compId}' is not present in polymer sequence "
                                      f"of chain {chainId} of the coordinates. "
                                      "Please update the sequence in the Macromolecules page.")
                        asis = True
                    else:
                        if self.no_extra_comment:
                            self.f.append(f"[Atom not found] {self.getCurrentAssignment(n=index)}"
                                          f"{chainId}:{seqId}:{compId}:{atomId} is not present in the coordinates.")
                        updatePolySeqRst(self.polySeqCsFailed, chainId, seqId, compId)
        return atomId, asis

    def getCoordAtomSiteOf(self, chainId: str, seqId: int, compId: Optional[str] = None, cifCheck: bool = True, asis: bool = True
                           ) -> Tuple[Tuple[str, int], Optional[dict]]:
        """ A wrapper function to retrieve sequence key and coordinates for given conditions.
        """

        return self.__getCoordAtomSiteOf(chainId, seqId, compId, cifCheck, asis, self.__preferAuthSeq)

    @functools.lru_cache(maxsize=2048)
    def __getCoordAtomSiteOf(self, chainId: str, seqId: int, compId: Optional[str] = None, cifCheck: bool = True, asis: bool = True,
                             __preferAuthSeq: bool = True
                             ) -> Tuple[Tuple[str, int], Optional[dict]]:

        def get_dummy_coord_atom_site(comp_id):
            if self.ccU.updateChemCompDict(comp_id):
                atom_id_list = [cca['atom_id'] for cca in self.ccU.lastAtomDictList if cca['leaving_atom_flag'] != 'Y']
                return {'atom_id': atom_id_list} if len(atom_id_list) > 0 else None
            return None

        seqKey = (chainId, seqId)
        if cifCheck:
            preferAuthSeq = __preferAuthSeq if asis else not __preferAuthSeq
            if preferAuthSeq:
                ps = next((ps for ps in self.polySeq if 'auth_chain_id' in ps and ps['auth_chain_id'] == chainId), None)
                if ps is None:
                    ps = next((ps for ps in self.polySeq if ps['chain_id'] == chainId), None)
                if ps is None:
                    return seqKey, None
                if 'auth_seq_id' in ps:
                    if seqId in ps['auth_seq_id']:
                        _compId = ps['comp_id'][ps['auth_seq_id'].index(seqId)]
                        if compId is None or compId == _compId:
                            return seqKey, get_dummy_coord_atom_site(_compId)
                elif seqId in ps['seq_id']:
                    _compId = ps['comp_id'][ps['seq_id'].index(seqId)]
                    if compId is None or compId == _compId:
                        return seqKey, get_dummy_coord_atom_site(_compId)
            else:
                ps = next((ps for ps in self.polySeq if ps['chain_id'] == chainId), None)
                if ps is None:
                    return seqKey, None
                if seqId in ps['seq_id']:
                    _compId = ps['comp_id'][ps['seq_id'].index(seqId)]
                    if compId is None or compId == _compId:
                        return seqKey, get_dummy_coord_atom_site(compId)
        return seqKey, None

    def getCurrentAssignment(self, n: int) -> str:
        """ Retrieve indicator of the current chemical shift.
        """

        if self.cur_subtype == 'chem_shift':
            return f"[Check the {self.chemShifts + 1}th row of assigned chemical shifts "\
                f"(list_id={self.cur_list_id}, index={n}), {self.__def_err_sf_framecode}] "
        return ''

    def __getNamedReasonsForReparsing(self, name: str) -> dict:
        """ Retrieve reasons for re-parsing by name. create a dictionary if name does not exist.
        """

        if name in self.reasonsForReParsing:
            return self.reasonsForReParsing[name]
        self.reasonsForReParsing[name] = {}
        return self.reasonsForReParsing[name]

    def __setLocalSeqScheme(self) -> None:
        """ Set sequence scheme for each chemical shift.
        """

        r = self.__getNamedReasonsForReparsing('local_seq_scheme')
        preferAuthSeq = self.__authSeqId == 'auth_seq_id'
        if self.cur_subtype == 'chem_shift':
            r[(self.cur_subtype, self.cur_list_id, self.chemShifts)] = preferAuthSeq
        if not preferAuthSeq:
            self.__preferLabelSeqCount += 1
            if self.__preferLabelSeqCount > MAX_PREF_LABEL_SCHEME_COUNT:
                self.reasonsForReParsing['label_seq_scheme'] = True

    def retrieveLocalSeqScheme(self) -> None:
        """ Retrieve sequence scheme for each chemical shift.
        """

        if self.reasons is None\
           or ('label_seq_scheme' not in self.reasons
               and 'local_seq_scheme' not in self.reasons
               and 'extend_seq_scheme' not in self.reasons):
            return
        if 'extend_seq_scheme' in self.reasons:
            self.__preferAuthSeq = self.__extendAuthSeq = True
            return
        if 'label_seq_scheme' in self.reasons and self.reasons['label_seq_scheme']:
            self.__preferAuthSeq = False
            # self.__authSeqId = 'label_seq_id'
            return
        if self.cur_subtype == 'chem_shift':
            key = (self.cur_subtype, self.cur_list_id, self.chemShifts)
        else:
            return

        if key in self.reasons['local_seq_scheme']:
            self.__preferAuthSeq = self.reasons['local_seq_scheme'][key]

    def __addSf(self) -> None:
        """ Add saveframe for given conditions if not exists.
        """

        content_subtype = contentSubtypeOf(self.cur_subtype)

        if content_subtype is None:
            return

        self.__listIdCounter = incListIdCounter(self.cur_subtype, self.__listIdCounter, reservedListIds=self.__reservedListIds)

        key = (self.cur_subtype, self.cur_list_id)

        if key in self.sfDict:
            if len(self.sfDict[key]) > 0:
                decListIdCounter(self.cur_subtype, self.__listIdCounter, reservedListIds=self.__reservedListIds)
                return
        else:
            self.sfDict[key] = []

        list_id = self.__listIdCounter[content_subtype]

        restraint_name = getRestraintName(self.cur_subtype)

        sf_framecode = (f'{self.software_name}_' if self.software_name is not None else '')\
            + restraint_name.replace(' ', '_') + f'_{list_id}'

        sf = getSaveframe(self.cur_subtype, sf_framecode, list_id, self.__entryId, self.__originalFileName)

        lp = getLoop(self.cur_subtype)

        item = {'file_type': self.file_type, 'saveframe': sf, 'loop': lp, 'list_id': list_id,
                'id': 0, 'index_id': 0,
                'sf_framecode': sf_framecode}

        self.sfDict[key].append(item)

    def getSf(self) -> dict:
        """ Retrieve saveframe for given conditions.
        """

        key = (self.cur_subtype, self.cur_list_id)

        if key not in self.sfDict:
            self.__addSf()

        cur_sf = self.sfDict[key][-1]

        self.__def_err_sf_framecode = cur_sf['sf_framecode']

        return cur_sf

    def getContentSubtype(self) -> dict:
        """ Return content subtype of CS file.
        """

        n = self.cur_list_id

        return {'chem_shift': n} if n > 0 else {}

    def getPolymerSequence(self) -> Optional[List[dict]]:
        """ Return polymer sequence of CS file.
        """

        return None if self.polySeqCs is None or len(self.polySeqCs) == 0 else self.polySeqCs

    def getSequenceAlignment(self) -> Optional[List[dict]]:
        """ Return sequence alignment between (coordinates or nmr_data) and CS.
        """

        return None if self.seqAlign is None or len(self.seqAlign) == 0 else self.seqAlign

    def getChainAssignment(self) -> Optional[List[dict]]:
        """ Return chain assignment between (coordinates or nmr_data) and CS.
        """

        return None if self.chainAssign is None or len(self.chainAssign) == 0 else self.chainAssign

    def getReasonsForReparsing(self) -> Optional[dict]:
        """ Return reasons for re-parsing CS file.
        """

        return None if len(self.reasonsForReParsing) == 0 else self.reasonsForReParsing

    def getSfDict(self) -> Tuple[dict, Optional[dict]]:
        """ Return a dictionary of pynmrstar saveframes.
        """

        if len(self.sfDict) == 0:
            return self.__listIdCounter, None
        ign_keys = []
        for k, v in self.sfDict.items():
            for item in reversed(v):
                if item['index_id'] == 0:
                    v.remove(item)
                    if len(v) == 0:
                        ign_keys.append(k)
                    self.__listIdCounter = decListIdCounter(k[0], self.__listIdCounter, reservedListIds=self.__reservedListIds)
        for k in ign_keys:
            del self.sfDict[k]
        return self.__listIdCounter, None if len(self.sfDict) == 0 else self.sfDict
