



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    :		Tuple		      =    Mapping[str, np.ndarray]
_lowerCamelCase    :		Any		      =    Mapping[str, Any]  # Is a nested dict.
_lowerCamelCase    :		Tuple		      =    0.01


@dataclasses.dataclass(frozen=SCREAMING_SNAKE_CASE_)
class 						lowercase    :
						'''simple docstring'''


						UpperCAmelCase			:		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'.
						UpperCAmelCase			:		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.
						UpperCAmelCase			:		np.ndarray  # [num_res, num_atom_type]

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

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

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

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

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


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

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

					assert aatype is not None

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

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

					SCREAMING_SNAKE_CASE							:     Any						=				prot.remark
					if remark is not None:
										pdb_headers.append(F'''REMARK {remark}'''   )

					SCREAMING_SNAKE_CASE							:     List[str]						=				prot.parents
					SCREAMING_SNAKE_CASE							:     int						=				prot.parents_chain_index
					if parents is not None and parents_chain_index is not None:
										SCREAMING_SNAKE_CASE							:     Any						=				[p for i, p in zip(__lowerCAmelCase      ,	__lowerCAmelCase   ) if i == chain_id]

					if parents is None or len(__lowerCAmelCase   ) == 0:
										SCREAMING_SNAKE_CASE							:     int						=				['N/A']

					pdb_headers.append(F'''PARENT {' '.join(__lowerCAmelCase   )}'''   )

					return pdb_headers

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

					SCREAMING_SNAKE_CASE							:     Dict						=				prot.remark
					if remark is not None:
										out_pdb_lines.append(F'''REMARK {remark}'''   )

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

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

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

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

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

															out_pdb_lines.append(make_parent_line(__lowerCAmelCase   )   )

					return "\n".join(__lowerCAmelCase   )

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

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

					SCREAMING_SNAKE_CASE							:     List[str]						=				residue_constants.atom_types

					SCREAMING_SNAKE_CASE							:     List[str]						=				[]

					SCREAMING_SNAKE_CASE							:     Dict						=				prot.atom_mask
					SCREAMING_SNAKE_CASE							:     List[str]						=				prot.aatype
					SCREAMING_SNAKE_CASE							:     List[str]						=				prot.atom_positions
					SCREAMING_SNAKE_CASE							:     str						=				prot.residue_index.astype(np.intaa   )
					SCREAMING_SNAKE_CASE							:     Tuple						=				prot.b_factors
					SCREAMING_SNAKE_CASE							:     Dict						=				prot.chain_index

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

					SCREAMING_SNAKE_CASE							:     str						=				get_pdb_headers(__lowerCAmelCase   )
					if len(__lowerCAmelCase   ) > 0:
										pdb_lines.extend(__lowerCAmelCase   )

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

															SCREAMING_SNAKE_CASE							:     Dict						=				'ATOM'
															SCREAMING_SNAKE_CASE							:     Optional[int]						=				atom_name if len(__lowerCAmelCase   ) == 4 else F''' {atom_name}'''
															SCREAMING_SNAKE_CASE							:     int						=				''
															SCREAMING_SNAKE_CASE							:     Tuple						=				''
															SCREAMING_SNAKE_CASE							:     int						=				1.00
															SCREAMING_SNAKE_CASE							:     List[str]						=				atom_name[0]  # Protein supports only C, N, O, S, this works.
															SCREAMING_SNAKE_CASE							:     str						=				''

															SCREAMING_SNAKE_CASE							:     Any						=				'A'
															if chain_index is not None:
																				SCREAMING_SNAKE_CASE							:     int						=				chain_tags[chain_index[i]]

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

										SCREAMING_SNAKE_CASE							:     Tuple						=				i == n - 1
										if chain_index is not None:
															if i != n - 1 and chain_index[i + 1] != prev_chain_index:
																				SCREAMING_SNAKE_CASE							:     Dict						=				True
																				SCREAMING_SNAKE_CASE							:     Any						=				chain_index[i + 1]

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

					pdb_lines.append('END'   )
					pdb_lines.append(''   )
					return "\n".join(__lowerCAmelCase   )

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



def        __a			(   __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=__lowerCAmelCase      ,	remark=__lowerCAmelCase      ,	parents=__lowerCAmelCase      ,	parents_chain_index=__lowerCAmelCase      ,	)




def        __a			(   __lowerCAmelCase   )     ->     list[list[float]]:
					SCREAMING_SNAKE_CASE							:     list[list[float]]						=				[]
					for data in source_data:
										for i, el in enumerate(__lowerCAmelCase   ):
															if len(__lowerCAmelCase   ) < i + 1:
																				data_lists.append([]   )
															data_lists[i].append(float(__lowerCAmelCase   )   )
					return data_lists

def        __a			(   __lowerCAmelCase      ,	__lowerCAmelCase   )     ->     list[list[float]]:
					SCREAMING_SNAKE_CASE							:     list[list[float]]						=				[]
					for dlist, weight in zip(__lowerCAmelCase      ,	__lowerCAmelCase   ):
										SCREAMING_SNAKE_CASE							:     str						=				min(__lowerCAmelCase   )
										SCREAMING_SNAKE_CASE							:     int						=				max(__lowerCAmelCase   )

										SCREAMING_SNAKE_CASE							:     list[float]						=				[]
										# for weight 0 score is 1 - actual score
										if weight == 0:
															for item in dlist:
																				try:
																									score.append(1 - ((item - mind) / (maxd - mind))   )
																				except ZeroDivisionError:
																									score.append(1   )

										elif weight == 1:
															for item in dlist:
																				try:
																									score.append((item - mind) / (maxd - mind)   )
																				except ZeroDivisionError:
																									score.append(0   )

        # weight not 0 or 1
										else:
															SCREAMING_SNAKE_CASE							:     Union[str, Any]						=				F'''Invalid weight of {weight:f} provided'''
															raise ValueError(__lowerCAmelCase   )

										score_lists.append(__lowerCAmelCase   )

					return score_lists

def        __a			(   __lowerCAmelCase   )     ->     list[float]:
					SCREAMING_SNAKE_CASE							:     list[float]						=				[0 for i in range(len(score_lists[0]   )   )]

					for slist in score_lists:
										for j, ele in enumerate(__lowerCAmelCase   ):
															SCREAMING_SNAKE_CASE							:     str						=				final_scores[j] + ele

					return final_scores



def        __a			(   __lowerCAmelCase      ,	__lowerCAmelCase   )     ->     list[list[float]]:
					SCREAMING_SNAKE_CASE							:     Tuple						=				get_data(__lowerCAmelCase   )
					SCREAMING_SNAKE_CASE							:     List[str]						=				calculate_each_score(__lowerCAmelCase      ,	__lowerCAmelCase   )
					SCREAMING_SNAKE_CASE							:     str						=				generate_final_scores(__lowerCAmelCase   )

					# append scores to source data
					for i, ele in enumerate(__lowerCAmelCase   ):
										source_data[i].append(__lowerCAmelCase   )

					return source_data