
'''simple docstring'''

import dataclasses
import re
import string
from typing import Any, Dict, Iterator, List, Mapping, Optional, Sequence, Tuple

import numpy as np

from . import residue_constants


UpperCamelCase_   	=				Mapping[str, np.ndarray]
UpperCamelCase_   	=				Mapping[str, Any]  # Is a nested dict.
UpperCamelCase_   	=				0.0_1


@dataclasses.dataclass(frozen=_UpperCAmelCase			)
class      a_     :
       __lowerCAmelCase			: Optional[Any]			=					4_2  # [num_res, num_atom_type, 3]

       # Amino-acid type for each residue represented as an integer between 0 and
       # 20, where 20 is 'X'.
       __lowerCAmelCase			: Optional[int]			=					4_2  # [num_res]

       # Binary float mask to indicate presence of a particular atom. 1.0 if an atom
       # is present and 0.0 if not. This should be used for loss masking.
       __lowerCAmelCase			: Union[str, Any]			=					4_2  # [num_res, num_atom_type]

       # Residue index as used in PDB. It is not necessarily continuous or 0-indexed.
       __lowerCAmelCase			: Optional[Any]			=					4_2  # [num_res]

       # B-factors, or temperature factors, of each residue (in sq. angstroms units),
       # representing the displacement of the residue from its ground truth mean
       # value.
       __lowerCAmelCase			: Dict			=					4_2  # [num_res, num_atom_type]

       # Chain indices for multi-chain predictions
       __lowerCAmelCase			: List[Any]			=					None

       # Optional remark about the protein. Included as a comment in output PDB
       # files
       __lowerCAmelCase			: Optional[int]			=					None

       # Templates used to generate this protein (prediction-only)
       __lowerCAmelCase			: Dict			=					None

       # Chain corresponding to each parent
       __lowerCAmelCase			: str			=					None



def 		_UpperCAmelCase (     _lowerCamelCase  :				Dict						)			->  Tuple:
  _lowerCAmelCase				:     Optional[int]       =    r'''(\[[A-Z]+\]\n)'''
  _lowerCAmelCase				:     List[str]       =    [tag.strip() for tag in re.split(lowercase__     ,					lowercase__						) if len(lowercase__						) > 0]
  _lowerCAmelCase				:     Iterator[Tuple[str, List[str]]]       =    zip(tags[0::2]     ,					[l.split("""\n"""						) for l in tags[1::2]]						)

  _lowerCAmelCase				:     List[str]       =    ["N", "CA", "C"]
  _lowerCAmelCase				:     Dict       =    None
  _lowerCAmelCase				:     List[str]       =    None
  _lowerCAmelCase				:     int       =    None
  for g in groups:
    if "[PRIMARY]" == g[0]:
      _lowerCAmelCase				:     Optional[Any]       =    g[1][0].strip()
      for i in range(len(lowercase__						)						):
        if seq[i] not in residue_constants.restypes:
          _lowerCAmelCase				:     Any       =    '''X'''  # FIXME: strings are immutable
      _lowerCAmelCase				:     str       =    np.array(
          [residue_constants.restype_order.get(lowercase__     ,					residue_constants.restype_num						) for res_symbol in seq]						)
    elif "[TERTIARY]" == g[0]:
      _lowerCAmelCase				:     List[List[float]]       =    []
      for axis in range(3						):
        tertiary.append(list(map(lowercase__     ,					g[1][axis].split()						)						)						)
      _lowerCAmelCase				:     List[str]       =    np.array(lowercase__						)
      _lowerCAmelCase				:     str       =    np.zeros((len(tertiary[0]						) // 3, residue_constants.atom_type_num, 3)						).astype(np.floataa						)
      for i, atom in enumerate(lowercase__						):
        _lowerCAmelCase				:     str       =    np.transpose(tertiary_np[:, i::3]						)
      atom_positions *= PICO_TO_ANGSTROM
    elif "[MASK]" == g[0]:
      _lowerCAmelCase				:     Optional[int]       =    np.array(list(map({"""-""": 0, """+""": 1}.get     ,					g[1][0].strip()						)						)						)
      _lowerCAmelCase				:     Union[str, Any]       =    np.zeros(
          (
              len(lowercase__						),
              residue_constants.atom_type_num,
          )						).astype(np.floataa						)
      for i, atom in enumerate(lowercase__						):
        _lowerCAmelCase				:     List[Any]       =    1
      atom_mask *= mask[..., None]

  assert aatype is not None

  return Protein(
      atom_positions=lowercase__     ,					atom_mask=lowercase__     ,					aatype=lowercase__     ,					residue_index=np.arange(len(lowercase__						)						)     ,					b_factors=lowercase__     ,					)


def 		_UpperCAmelCase (     _lowerCamelCase  :				str     ,					_lowerCamelCase  :				Optional[Any] = 0						)			->  Tuple:
  _lowerCAmelCase				:     List[str]       =    []

  _lowerCAmelCase				:     Tuple       =    prot.remark
  if remark is not None:
    pdb_headers.append(f'REMARK {remark}'						)

  _lowerCAmelCase				:     Optional[Any]       =    prot.parents
  _lowerCAmelCase				:     Tuple       =    prot.parents_chain_index
  if parents is not None and parents_chain_index is not None:
    _lowerCAmelCase				:     List[str]       =    [p for i, p in zip(lowercase__     ,					lowercase__						) if i == chain_id]

  if parents is None or len(lowercase__						) == 0:
    _lowerCAmelCase				:     Dict       =    ['''N/A''']

  pdb_headers.append(f'PARENT {" ".join(lowercase__						)}'						)

  return pdb_headers


def 		_UpperCAmelCase (     _lowerCamelCase  :				int     ,					_lowerCamelCase  :				Tuple						)			->  Tuple:
  _lowerCAmelCase				:     List[str]       =    []
  _lowerCAmelCase				:     str       =    pdb_str.split("""\n"""						)

  _lowerCAmelCase				:     Union[str, Any]       =    prot.remark
  if remark is not None:
    out_pdb_lines.append(f'REMARK {remark}'						)

  _lowerCAmelCase				:     List[List[str]]
  if prot.parents is not None and len(prot.parents						) > 0:
    _lowerCAmelCase				:     Tuple       =    []
    if prot.parents_chain_index is not None:
      _lowerCAmelCase				:     Dict[str, List[str]]       =    {}
      for p, i in zip(prot.parents     ,					prot.parents_chain_index						):
        parent_dict.setdefault(str(lowercase__						)     ,					[]						)
        parent_dict[str(lowercase__						)].append(lowercase__						)

      _lowerCAmelCase				:     int       =    max([int(lowercase__						) for chain_idx in parent_dict]						)
      for i in range(max_idx + 1						):
        _lowerCAmelCase				:     Dict       =    parent_dict.get(str(lowercase__						)     ,					["""N/A"""]						)
        parents_per_chain.append(lowercase__						)
    else:
      parents_per_chain.append(list(prot.parents						)						)
  else:
    _lowerCAmelCase				:     Union[str, Any]       =    [['''N/A''']]

  def make_parent_line(_lowerCamelCase  :				Union[str, Any]						) -> str:
    return f'PARENT {" ".join(lowercase__						)}'

  out_pdb_lines.append(make_parent_line(parents_per_chain[0]						)						)

  _lowerCAmelCase				:     Optional[Any]       =    0
  for i, l in enumerate(lowercase__						):
    if "PARENT" not in l and "REMARK" not in l:
      out_pdb_lines.append(lowercase__						)
    if "TER" in l and "END" not in lines[i + 1]:
      chain_counter += 1
      if not chain_counter >= len(lowercase__						):
        _lowerCAmelCase				:     Tuple       =    parents_per_chain[chain_counter]
      else:
        _lowerCAmelCase				:     Tuple       =    ['''N/A''']

      out_pdb_lines.append(make_parent_line(lowercase__						)						)

  return "\n".join(lowercase__						)


def 		_UpperCAmelCase (     _lowerCamelCase  :				List[str]						)			->  List[str]:
  _lowerCAmelCase				:     Union[str, Any]       =    residue_constants.restypes + ['''X''']

  def res_atoa(_lowerCamelCase  :				List[Any]						) -> str:
    return residue_constants.restype_atoa.get(restypes[r]     ,					"""UNK"""						)

  _lowerCAmelCase				:     Tuple       =    residue_constants.atom_types

  _lowerCAmelCase				:     List[str]       =    []

  _lowerCAmelCase				:     Any       =    prot.atom_mask
  _lowerCAmelCase				:     Dict       =    prot.aatype
  _lowerCAmelCase				:     Any       =    prot.atom_positions
  _lowerCAmelCase				:     List[Any]       =    prot.residue_index.astype(np.intaa						)
  _lowerCAmelCase				:     int       =    prot.b_factors
  _lowerCAmelCase				:     Any       =    prot.chain_index

  if np.any(aatype > residue_constants.restype_num						):
    raise ValueError("""Invalid aatypes."""						)

  _lowerCAmelCase				:     Optional[Any]       =    get_pdb_headers(lowercase__						)
  if len(lowercase__						) > 0:
    pdb_lines.extend(lowercase__						)

  _lowerCAmelCase				:     Optional[int]       =    aatype.shape[0]
  _lowerCAmelCase				:     Any       =    1
  _lowerCAmelCase				:     Optional[int]       =    0
  _lowerCAmelCase				:     Union[str, Any]       =    string.ascii_uppercase
  _lowerCAmelCase				:     List[str]       =    None
  # Add all atom sites.
  for i in range(lowercase__						):
    _lowerCAmelCase				:     Any       =    res_atoa(aatype[i]						)
    for atom_name, pos, mask, b_factor in zip(lowercase__     ,					atom_positions[i]     ,					atom_mask[i]     ,					b_factors[i]						):
      if mask < 0.5:
        continue

      _lowerCAmelCase				:     Optional[int]       =    '''ATOM'''
      _lowerCAmelCase				:     List[str]       =    atom_name if len(lowercase__						) == 4 else f' {atom_name}'
      _lowerCAmelCase				:     List[Any]       =    ''''''
      _lowerCAmelCase				:     Optional[Any]       =    ''''''
      _lowerCAmelCase				:     Optional[int]       =    1.00
      _lowerCAmelCase				:     List[Any]       =    atom_name[0]  # Protein supports only C, N, O, S, this works.
      _lowerCAmelCase				:     List[Any]       =    ''''''

      _lowerCAmelCase				:     Tuple       =    '''A'''
      if chain_index is not None:
        _lowerCAmelCase				:     int       =    chain_tags[chain_index[i]]

      # PDB is a columnar format, every space matters here!
      _lowerCAmelCase				:     Union[str, Any]       =    (
          f'{record_type:<6}{atom_index:>5} {name:<4}{alt_loc:>1}'
          f'{res_name_a:>3} {chain_tag:>1}'
          f'{residue_index[i]:>4}{insertion_code:>1}   '
          f'{pos[0]:>8.3f}{pos[1]:>8.3f}{pos[2]:>8.3f}'
          f'{occupancy:>6.2f}{b_factor:>6.2f}          '
          f'{element:>2}{charge:>2}'
      )
      pdb_lines.append(lowercase__						)
      atom_index += 1

    _lowerCAmelCase				:     str       =    i == n - 1
    if chain_index is not None:
      if i != n - 1 and chain_index[i + 1] != prev_chain_index:
        _lowerCAmelCase				:     Union[str, Any]       =    True
        _lowerCAmelCase				:     Optional[Any]       =    chain_index[i + 1]

    if should_terminate:
      # Close the chain.
      _lowerCAmelCase				:     Tuple       =    '''TER'''
      _lowerCAmelCase				:     Optional[int]       =    (
          f'{chain_end:<6}{atom_index:>5}      {res_atoa(aatype[i]						):>3} {chain_tag:>1}{residue_index[i]:>4}'
      )
      pdb_lines.append(lowercase__						)
      atom_index += 1

      if i != n - 1:
        # "prev" is a misnomer here. This happens at the beginning of
        # each new chain.
        pdb_lines.extend(get_pdb_headers(lowercase__     ,					lowercase__						)						)

  pdb_lines.append("""END"""						)
  pdb_lines.append(""""""						)
  return "\n".join(lowercase__						)


def 		_UpperCAmelCase (     _lowerCamelCase  :				str						)			->  Optional[Any]:
  return residue_constants.STANDARD_ATOM_MASK[prot.aatype]



def 		_UpperCAmelCase (     _lowerCamelCase  :				int     ,					_lowerCamelCase  :				Tuple     ,					_lowerCamelCase  :				Optional[Any] = None     ,					_lowerCamelCase  :				Tuple = None     ,					_lowerCamelCase  :				int = None     ,					_lowerCamelCase  :				Union[str, Any] = None     ,					_lowerCamelCase  :				Optional[Any] = None     ,					)			->  str:
  return Protein(
      aatype=features["""aatype"""]     ,					atom_positions=result["""final_atom_positions"""]     ,					atom_mask=result["""final_atom_mask"""]     ,					residue_index=features["""residue_index"""] + 1     ,					b_factors=b_factors if b_factors is not None else np.zeros_like(result["""final_atom_mask"""]						)     ,					chain_index=lowercase__     ,					remark=lowercase__     ,					parents=lowercase__     ,					parents_chain_index=lowercase__     ,					)





from collections import deque

from .hash_table import HashTable







class   __lowercase      (_UpperCAmelCase ):
 def __init__(							self      ,							*A_      ,							**A_					)					->int:



       '''simple docstring'''





       super().__init__(*A_      ,							**A_					)
 def 	UpperCamelCase__  (							self      ,							A_      ,							A_					)					->Optional[int]:



       '''simple docstring'''





       __lowerCAmelCase					:						Dict					=					deque([]					) if self.values[key] is None else self.values[key]
       self.values[key].appendleft(A_					)
       __lowerCAmelCase					:						int					=					self.values[key]
 def 	UpperCamelCase__  (							self					)					->Tuple:



       '''simple docstring'''





       return (
           sum(self.charge_factor - len(A_					) for slot in self.values					)
           / self.size_table
           * self.charge_factor
       )





 def 	UpperCamelCase__  (							self      ,							A_      ,							A_=None					)					->str:



       '''simple docstring'''





       if not (
           len(self.values[key]					) == self.charge_factor and self.values.count(A_					) == 0
       ):
             return key
       return super()._collision_resolution(A_      ,							A_					)


