






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


lowerCamelCase     :							List[str]    =Mapping[str, np.ndarray]
lowerCamelCase     :							List[str]    =Mapping[str, Any]  # Is a nested dict.
lowerCamelCase     :							Optional[int]    =0.01





@dataclasses.dataclass(frozen=__lowercase	)
class       __a  :
 _lowerCAmelCase	:	np.ndarray  # [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	:	np.ndarray  # [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	:	np.ndarray  # [num_res, num_atom_type]

 # Residue index as used in PDB. It is not necessarily continuous or 0-indexed.
 _lowerCAmelCase	:	np.ndarray  # [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	:	np.ndarray  # [num_res, num_atom_type]

 # Chain indices for multi-chain predictions
 _lowerCAmelCase	:	Optional[np.ndarray]				=     None

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

 # Templates used to generate this protein (prediction-only)
 _lowerCAmelCase	:	Optional[Sequence[str]]				=     None

 # Chain corresponding to each parent
 _lowerCAmelCase	:	Optional[Sequence[int]]				=     None







def 		SCREAMING_SNAKE_CASE							( __lowerCAmelCase      )			->     Protein:
     UpperCamelCase__      :       int	    =  R'''(\[[A-Z]+\]\n)'''
     UpperCamelCase__      :       List[str]	    =  [tag.strip() for tag in re.split(__a ,   __a      ) if len(__a      ) > 0]
     UpperCamelCase__      :       Iterator[Tuple[str, List[str]]]	    =  zip(tags[0::2] ,   [l.split("\n"      ) for l in tags[1::2]]      )

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

     assert aatype is not None

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







def 		SCREAMING_SNAKE_CASE							( __lowerCAmelCase ,   __lowerCAmelCase = 0      )			->     List[str]:
     UpperCamelCase__      :       List[str]	    =  []

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

     UpperCamelCase__      :       Optional[int]	    =  prot.parents
     UpperCamelCase__      :       Tuple	    =  prot.parents_chain_index
     if parents is not None and parents_chain_index is not None:
          UpperCamelCase__      :       int	    =  [p for i, p in zip(__a ,   __a      ) if i == chain_id]

     if parents is None or len(__a      ) == 0:
          UpperCamelCase__      :       List[Any]	    =  ['''N/A''']

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

     return pdb_headers







def 		SCREAMING_SNAKE_CASE							( __lowerCAmelCase ,   __lowerCAmelCase      )			->     str:
     UpperCamelCase__      :       List[str]	    =  []
     UpperCamelCase__      :       Union[str, Any]	    =  pdb_str.split("\n"      )

     UpperCamelCase__      :       Any	    =  prot.remark
     if remark is not None:
          out_pdb_lines.append(f'REMARK {remark}'      )

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

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

     def make_parent_line(__lowerCAmelCase      ) -> str:
          return f'PARENT {" ".join(__a      )}'

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

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

               out_pdb_lines.append(make_parent_line(__a      )      )

     return "\n".join(__a      )







def 		SCREAMING_SNAKE_CASE							( __lowerCAmelCase      )			->     str:
     UpperCamelCase__      :       str	    =  residue_constants.restypes + ['''X''']

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

     UpperCamelCase__      :       Dict	    =  residue_constants.atom_types

     UpperCamelCase__      :       List[str]	    =  []

     UpperCamelCase__      :       List[Any]	    =  prot.atom_mask
     UpperCamelCase__      :       Union[str, Any]	    =  prot.aatype
     UpperCamelCase__      :       Optional[Any]	    =  prot.atom_positions
     UpperCamelCase__      :       Dict	    =  prot.residue_index.astype(np.intaa      )
     UpperCamelCase__      :       Tuple	    =  prot.b_factors
     UpperCamelCase__      :       str	    =  prot.chain_index

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

     UpperCamelCase__      :       List[Any]	    =  get_pdb_headers(__a      )
     if len(__a      ) > 0:
          pdb_lines.extend(__a      )

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

               UpperCamelCase__      :       Optional[Any]	    =  '''ATOM'''
               UpperCamelCase__      :       List[Any]	    =  atom_name if len(__a      ) == 4 else f' {atom_name}'
               UpperCamelCase__      :       Union[str, Any]	    =  ''''''
               UpperCamelCase__      :       Dict	    =  ''''''
               UpperCamelCase__      :       List[Any]	    =  1.0_0
               UpperCamelCase__      :       int	    =  atom_name[0]  # Protein supports only C, N, O, S, this works.
               UpperCamelCase__      :       int	    =  ''''''

               UpperCamelCase__      :       Union[str, Any]	    =  '''A'''
               if chain_index is not None:
                    UpperCamelCase__      :       int	    =  chain_tags[chain_index[i]]

               # PDB is a columnar format, every space matters here!
               UpperCamelCase__      :       Tuple	    =  (
                   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(__a      )
               atom_index += 1

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

          if should_terminate:
               # Close the chain.
               UpperCamelCase__      :       str	    =  '''TER'''
               UpperCamelCase__      :       Any	    =  (
                   f'{chain_end:<6}{atom_index:>5}      {res_atoa(aatype[i]      ):>3} {chain_tag:>1}{residue_index[i]:>4}'
               )
               pdb_lines.append(__a      )
               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(__a ,   __a      )      )

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







def 		SCREAMING_SNAKE_CASE							( __lowerCAmelCase      )			->     np.ndarray:
     return residue_constants.STANDARD_ATOM_MASK[prot.aatype]







def 		SCREAMING_SNAKE_CASE							( __lowerCAmelCase ,   __lowerCAmelCase ,   __lowerCAmelCase = None ,   __lowerCAmelCase = None ,   __lowerCAmelCase = None ,   __lowerCAmelCase = None ,   __lowerCAmelCase = None ,   )			->     Protein:
     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=__a ,   remark=__a ,   parents=__a ,   parents_chain_index=__a ,   )







from collections.abc import Generator
from math import sin







def 		SCREAMING_SNAKE_CASE							( __lowerCAmelCase      )			->     bytes:
     if len(__lowerCAmelCase      ) != 32:
          raise ValueError("Input must be of length 32"      )

     UpperCamelCase__      :       Optional[Any]	    =  B""
     for i in [3, 2, 1, 0]:
          little_endian += string_aa[8 * i : 8 * i + 8]
     return little_endian







def 		SCREAMING_SNAKE_CASE							( __lowerCAmelCase      )			->     bytes:
     if i < 0:
          raise ValueError("Input must be non-negative"      )

     UpperCamelCase__      :       int	    =  format(__lowerCAmelCase ,   "08x"      )[-8:]
     UpperCamelCase__      :       Union[str, Any]	    =  B""
     for i in [3, 2, 1, 0]:
          little_endian_hex += hex_rep[2 * i : 2 * i + 2].encode("utf-8"      )
     return little_endian_hex







def 		SCREAMING_SNAKE_CASE							( __lowerCAmelCase      )			->     bytes:
     UpperCamelCase__      :       Optional[Any]	    =  B""
     for char in message:
          bit_string += format(__lowerCAmelCase ,   "08b"      ).encode("utf-8"      )
     UpperCamelCase__      :       Any	    =  format(len(__lowerCAmelCase      ) ,   "064b"      ).encode("utf-8"      )

     # Pad bit_string to a multiple of 512 chars
     bit_string += b"1"
     while len(__lowerCAmelCase      ) % 512 != 448:
          bit_string += b"0"
     bit_string += to_little_endian(start_len[32:]      ) + to_little_endian(start_len[:32]      )

     return bit_string







def 		SCREAMING_SNAKE_CASE							( __lowerCAmelCase      )			->     Generator[list[int], None, None]:
     if len(__lowerCAmelCase      ) % 512 != 0:
          raise ValueError("Input must have length that's a multiple of 512"      )

     for pos in range(0 ,   len(__lowerCAmelCase      ) ,   512      ):
          UpperCamelCase__      :       Tuple	    =  bit_string[pos : pos + 512]
          UpperCamelCase__      :       Tuple	    =  []
          for i in range(0 ,   512 ,   32      ):
               block_words.append(int(to_little_endian(block[i : i + 32]      ) ,   2      )      )
          yield block_words







def 		SCREAMING_SNAKE_CASE							( __lowerCAmelCase      )			->     int:
     if i < 0:
          raise ValueError("Input must be non-negative"      )

     UpperCamelCase__      :       str	    =  format(__lowerCAmelCase ,   "032b"      )
     UpperCamelCase__      :       Optional[int]	    =  ""
     for c in i_str:
          new_str += "1" if c == "0" else "0"
     return int(__lowerCAmelCase ,   2      )







def 		SCREAMING_SNAKE_CASE							( __lowerCAmelCase ,   __lowerCAmelCase      )			->     int:
     return (a + b) % 2**32







def 		SCREAMING_SNAKE_CASE							( __lowerCAmelCase ,   __lowerCAmelCase      )			->     int:
     if i < 0:
          raise ValueError("Input must be non-negative"      )
     if shift < 0:
          raise ValueError("Shift must be non-negative"      )
     return ((i << shift) ^ (i >> (32 - shift))) % 2**32







def 		SCREAMING_SNAKE_CASE							( __lowerCAmelCase      )			->     bytes:
     UpperCamelCase__      :       int	    =  preprocess(__lowerCAmelCase      )

     UpperCamelCase__      :       Optional[Any]	    =  [int(2**32 * abs(sin(i + 1      )      )      ) for i in range(64      )]

     # Starting states
     UpperCamelCase__      :       Any	    =  0x6745_2301
     UpperCamelCase__      :       Optional[int]	    =  0xEFCD_AB89
     UpperCamelCase__      :       Union[str, Any]	    =  0x98BA_DCFE
     UpperCamelCase__      :       Any	    =  0x1032_5476

     UpperCamelCase__      :       List[Any]	    =  [
         7,
         12,
         17,
         22,
         7,
         12,
         17,
         22,
         7,
         12,
         17,
         22,
         7,
         12,
         17,
         22,
         5,
         9,
         14,
         20,
         5,
         9,
         14,
         20,
         5,
         9,
         14,
         20,
         5,
         9,
         14,
         20,
         4,
         11,
         16,
         23,
         4,
         11,
         16,
         23,
         4,
         11,
         16,
         23,
         4,
         11,
         16,
         23,
         6,
         10,
         15,
         21,
         6,
         10,
         15,
         21,
         6,
         10,
         15,
         21,
         6,
         10,
         15,
         21,
     ]

     # Process bit string in chunks, each with 16 32-char words
     for block_words in get_block_words(__lowerCAmelCase      ):
          UpperCamelCase__      :       Union[str, Any]	    =  aa
          UpperCamelCase__      :       int	    =  ba
          UpperCamelCase__      :       Union[str, Any]	    =  ca
          UpperCamelCase__      :       str	    =  da

          # Hash current chunk
          for i in range(64      ):
               if i <= 15:
                    # f = (b & c) | (not_32(b) & d)     # Alternate definition for f
                    UpperCamelCase__      :       Any	    =  d ^ (b & (c ^ d))
                    UpperCamelCase__      :       Optional[int]	    =  i
               elif i <= 31:
                    # f = (d & b) | (not_32(d) & c)     # Alternate definition for f
                    UpperCamelCase__      :       Any	    =  c ^ (d & (b ^ c))
                    UpperCamelCase__      :       str	    =  (5 * i + 1) % 16
               elif i <= 47:
                    UpperCamelCase__      :       Dict	    =  b ^ c ^ d
                    UpperCamelCase__      :       Union[str, Any]	    =  (3 * i + 5) % 16
               else:
                    UpperCamelCase__      :       Optional[int]	    =  c ^ (b | not_aa(__lowerCAmelCase      ))
                    UpperCamelCase__      :       Optional[int]	    =  (7 * i) % 16
               UpperCamelCase__      :       List[str]	    =  (f + a + added_consts[i] + block_words[g]) % 2**32
               UpperCamelCase__      :       Optional[int]	    =  d
               UpperCamelCase__      :       Dict	    =  c
               UpperCamelCase__      :       List[Any]	    =  b
               UpperCamelCase__      :       List[Any]	    =  sum_aa(__lowerCAmelCase ,   left_rotate_aa(__lowerCAmelCase ,   shift_amounts[i]      )      )

          # Add hashed chunk to running total
          UpperCamelCase__      :       Optional[int]	    =  sum_aa(__lowerCAmelCase ,   __lowerCAmelCase      )
          UpperCamelCase__      :       Any	    =  sum_aa(__lowerCAmelCase ,   __lowerCAmelCase      )
          UpperCamelCase__      :       Dict	    =  sum_aa(__lowerCAmelCase ,   __lowerCAmelCase      )
          UpperCamelCase__      :       List[str]	    =  sum_aa(__lowerCAmelCase ,   __lowerCAmelCase      )

     UpperCamelCase__      :       List[str]	    =  reformat_hex(__lowerCAmelCase      ) + reformat_hex(__lowerCAmelCase      ) + reformat_hex(__lowerCAmelCase      ) + reformat_hex(__lowerCAmelCase      )
     return digest


if __name__ == "__main__":
 import doctest

 doctest.testmod()