diff --git a/LICENSE b/LICENSE index b05c3f1cd13322e18f87e642077d8b67c7e99e9c..c4c761c0706abd3eaa70951fcebed56e40f9a6bd 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License - Copyright (c) 2017, MolIO contributors + Copyright (c) 2017, Mol* contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/docs/cif-schemas.md b/docs/cif-schemas.md index 7ff4633237a43c2ce33dae0bc299dc92a7a8958f..9bdba105e11e932833b591c362039a286aabbb61 100644 --- a/docs/cif-schemas.md +++ b/docs/cif-schemas.md @@ -74,6 +74,8 @@ And that's all there is to it. Extending the types to the "frame" level is left The advantage of this approach is that the types are generated directly from the data. This means we only need to define them once (as opposed to defining the data interfaces separately) and on top of that, the "schemas" also serve as a template for how to actually performs the transformation to the typed version of CIF (again without the need to do this "manually" except the one time definition of the schema). +This concept is further abstracted as `mol-base/collections/database`. + ---------------- diff --git a/examples/1grm_updated.cif b/examples/1grm_updated.cif new file mode 100644 index 0000000000000000000000000000000000000000..abcd10884b7b91a749b1d723af8c6aabbba4ac89 --- /dev/null +++ b/examples/1grm_updated.cif @@ -0,0 +1,2198 @@ +data_1GRM +# +_entry.id 1GRM + +# +loop_ +_citation.id +_citation.title +_citation.journal_abbrev +_citation.journal_volume +_citation.page_first +_citation.page_last +_citation.year +_citation.journal_id_ASTM +_citation.country +_citation.journal_id_ISSN +_citation.journal_id_CSD +_citation.book_publisher +_citation.pdbx_database_id_PubMed +_citation.pdbx_database_id_DOI +primary "Refinement of the Spatial Structure of the Gramicidin a Ion Channel" "Biol. Membrany" 18 182 ? 1992 BIMEE9 SU 0233-4755 2018 ? 1376600 ? + 1 "1H-NMR Study of Gramicidin a Transmembrane Ion Channel. Head-to-Head Right-Handed, Single-Stranded Helices." "FEBS Lett." 186 168 ? 1985 FEBLAL NE 0014-5793 0165 ? 2408920 DOI:10.1016/0014-5793(85)80702-X + 2 "Gramicidin a Transmembrane Ion-Channel. Three-Dimensional Structure Reconstruction Based on NMR Spectroscopy and Energy Refinement (Russian)" "Biol. Membrany" 3 1077 ? 1986 BIMEE9 SU 0233-4755 2018 ? ? ? + 3 "Spatial Structure of Gramicidin a Transmembrane Ion Channel-NMR Analysis in Micelles (Russian)" "Biol. Membrany" 3 437 ? 1986 BIMEE9 SU 0233-4755 2018 ? ? ? +# +loop_ +_citation_author.citation_id +_citation_author.name +_citation_author.ordinal +primary "Lomize, A.L." 1 +primary "Orekhov, V.I.U." 2 +primary "Arsen'Ev, A.S." 3 + 1 "Arseniev, A.S." 4 + 1 "Barsukov, I.L." 5 + 1 "Bystrov, V.F." 6 + 1 "Lomize, A.L." 7 + 1 "Ovchinnikov, Yu.A." 8 + 2 "Arseniev, A.S." 9 + 2 "Lomize, A.L." 10 + 2 "Barsukov, I.L." 11 + 2 "Bystrov, V.F." 12 + 3 "Arseniev, A.S." 13 + 3 "Barsukov, I.L." 14 + 3 "Bystrov, V.F." 15 + 3 "Ovchinnikov, Yu.A." 16 +# +_cell.entry_id 1GRM +_cell.length_a 1.000 +_cell.length_b 1.000 +_cell.length_c 1.000 +_cell.angle_alpha 90.00 +_cell.angle_beta 90.00 +_cell.angle_gamma 90.00 +_cell.Z_PDB 1 +_cell.pdbx_unique_axis ? + +# +_symmetry.entry_id 1GRM +_symmetry.space_group_name_H-M "P 1" +_symmetry.pdbx_full_space_group_name_H-M ? +_symmetry.cell_setting ? +_symmetry.Int_Tables_number 1 + +# +_entity.id 1 +_entity.type polymer +_entity.src_method man +_entity.pdbx_description "GRAMICIDIN A" +_entity.formula_weight 1882.294 +_entity.pdbx_number_of_molecules 2 +_entity.pdbx_ec ? +_entity.pdbx_mutation ? +_entity.pdbx_fragment ? +_entity.details ? + +# +_entity_name_com.entity_id 1 +_entity_name_com.name "VALYL GRAMICIDIN" + +# +_entity_poly.entity_id 1 +_entity_poly.type polypeptide(L) +_entity_poly.nstd_linkage no +_entity_poly.nstd_monomer yes +_entity_poly.pdbx_seq_one_letter_code (FVA)GA(DLE)A(DVA)V(DVA)W(DLE)W(DLE)W(DLE)W(ETA) +_entity_poly.pdbx_seq_one_letter_code_can VGALAVVVWLWLWLWX +_entity_poly.pdbx_strand_id A,B +_entity_poly.pdbx_target_identifier ? + +# +loop_ +_entity_poly_seq.entity_id +_entity_poly_seq.num +_entity_poly_seq.mon_id +_entity_poly_seq.hetero +1 1 FVA n +1 2 GLY n +1 3 ALA n +1 4 DLE n +1 5 ALA n +1 6 DVA n +1 7 VAL n +1 8 DVA n +1 9 TRP n +1 10 DLE n +1 11 TRP n +1 12 DLE n +1 13 TRP n +1 14 DLE n +1 15 TRP n +1 16 ETA n +# +_entity_src_gen.entity_id 1 +_entity_src_gen.pdbx_src_id 1 +_entity_src_gen.pdbx_alt_source_flag sample +_entity_src_gen.pdbx_seq_type ? +_entity_src_gen.pdbx_beg_seq_num ? +_entity_src_gen.pdbx_end_seq_num ? +_entity_src_gen.gene_src_common_name ? +_entity_src_gen.gene_src_genus ? +_entity_src_gen.pdbx_gene_src_gene ? +_entity_src_gen.gene_src_species ? +_entity_src_gen.gene_src_strain ? +_entity_src_gen.gene_src_tissue ? +_entity_src_gen.gene_src_tissue_fraction ? +_entity_src_gen.gene_src_details ? +_entity_src_gen.pdbx_gene_src_fragment ? +_entity_src_gen.pdbx_gene_src_scientific_name "BREVIBACILLUS BREVIS" +_entity_src_gen.pdbx_gene_src_ncbi_taxonomy_id 1393 +_entity_src_gen.pdbx_gene_src_variant ? +_entity_src_gen.pdbx_gene_src_cell_line ? +_entity_src_gen.pdbx_gene_src_atcc ? +_entity_src_gen.pdbx_gene_src_organ ? +_entity_src_gen.pdbx_gene_src_organelle ? +_entity_src_gen.pdbx_gene_src_cell ? +_entity_src_gen.pdbx_gene_src_cellular_location ? +_entity_src_gen.host_org_common_name ? +_entity_src_gen.pdbx_host_org_scientific_name ? +_entity_src_gen.pdbx_host_org_ncbi_taxonomy_id ? +_entity_src_gen.host_org_genus ? +_entity_src_gen.pdbx_host_org_gene ? +_entity_src_gen.pdbx_host_org_organ ? +_entity_src_gen.host_org_species ? +_entity_src_gen.pdbx_host_org_tissue ? +_entity_src_gen.pdbx_host_org_tissue_fraction ? +_entity_src_gen.pdbx_host_org_strain ? +_entity_src_gen.pdbx_host_org_variant ? +_entity_src_gen.pdbx_host_org_cell_line ? +_entity_src_gen.pdbx_host_org_atcc ? +_entity_src_gen.pdbx_host_org_culture_collection ? +_entity_src_gen.pdbx_host_org_cell ? +_entity_src_gen.pdbx_host_org_organelle ? +_entity_src_gen.pdbx_host_org_cellular_location ? +_entity_src_gen.pdbx_host_org_vector_type ? +_entity_src_gen.pdbx_host_org_vector ? +_entity_src_gen.host_org_details ? +_entity_src_gen.expression_system_id ? +_entity_src_gen.plasmid_name ? +_entity_src_gen.plasmid_details ? +_entity_src_gen.pdbx_description ? + +# +_struct_ref.id 1 +_struct_ref.db_name NOR +_struct_ref.db_code NOR00243 +_struct_ref.entity_id 1 +_struct_ref.pdbx_seq_one_letter_code ? +_struct_ref.pdbx_align_begin ? +_struct_ref.pdbx_db_accession NOR00243 +_struct_ref.pdbx_db_isoform ? + +# +loop_ +_struct_ref_seq.align_id +_struct_ref_seq.ref_id +_struct_ref_seq.pdbx_PDB_id_code +_struct_ref_seq.pdbx_strand_id +_struct_ref_seq.seq_align_beg +_struct_ref_seq.pdbx_seq_align_beg_ins_code +_struct_ref_seq.seq_align_end +_struct_ref_seq.pdbx_seq_align_end_ins_code +_struct_ref_seq.pdbx_db_accession +_struct_ref_seq.db_align_beg +_struct_ref_seq.pdbx_db_align_beg_ins_code +_struct_ref_seq.db_align_end +_struct_ref_seq.pdbx_db_align_end_ins_code +_struct_ref_seq.pdbx_auth_seq_align_beg +_struct_ref_seq.pdbx_auth_seq_align_end +1 1 1GRM A 1 ? 16 ? NOR00243 1 ? 16 ? 1 16 +2 1 1GRM B 1 ? 16 ? NOR00243 1 ? 16 ? 1 16 +# +loop_ +_chem_comp.id +_chem_comp.type +_chem_comp.mon_nstd_flag +_chem_comp.name +_chem_comp.pdbx_synonyms +_chem_comp.formula +_chem_comp.formula_weight +ALA "L-peptide linking" y ALANINE ? "C3 H7 N O2" 89.093 +DLE "D-peptide linking" . D-LEUCINE ? "C6 H13 N O2" 131.173 +DVA "D-peptide linking" . D-VALINE ? "C5 H11 N O2" 117.146 +ETA "L-peptide COOH carboxy terminus" . ETHANOLAMINE ? "C2 H7 N O" 61.083 +FVA "L-peptide linking" n N-formyl-L-valine ? "C6 H11 N O3" 145.156 +GLY "peptide linking" y GLYCINE ? "C2 H5 N O2" 75.067 +TRP "L-peptide linking" y TRYPTOPHAN ? "C11 H12 N2 O2" 204.225 +VAL "L-peptide linking" y VALINE ? "C5 H11 N O2" 117.146 +# +_pdbx_nmr_ensemble.entry_id 1GRM +_pdbx_nmr_ensemble.conformers_calculated_total_number ? +_pdbx_nmr_ensemble.conformers_submitted_total_number 5 +_pdbx_nmr_ensemble.conformer_selection_criteria ? + +# +_exptl.entry_id 1GRM +_exptl.method "Solution NMR" +_exptl.crystals_number ? + +# +_struct.entry_id 1GRM +_struct.title "REFINEMENT OF THE SPATIAL STRUCTURE OF THE GRAMICIDIN A TRANSMEMBRANE ION-CHANNEL (RUSSIAN)" +_struct.pdbx_descriptor "GRAMICIDIN A" +_struct.pdbx_model_details ? +_struct.pdbx_CASP_flag ? +_struct.pdbx_model_type_details ? + +# +_struct_keywords.entry_id 1GRM +_struct_keywords.pdbx_keywords ANTIBIOTIC +_struct_keywords.text "ANTIBIOTIC, GRAMICIDIN, ANTIFUNGAL, ANTIBACTERIAL, MEMBRANE ION CHANNEL, LINEAR GRAMICIDIN" + +# +loop_ +_struct_asym.id +_struct_asym.pdbx_blank_PDB_chainid_flag +_struct_asym.pdbx_modified +_struct_asym.entity_id +_struct_asym.details +A N N 1 ? +B N N 1 ? +# +_struct_biol.id 1 + +# +loop_ +_struct_conn.id +_struct_conn.conn_type_id +_struct_conn.pdbx_leaving_atom_flag +_struct_conn.pdbx_PDB_id +_struct_conn.ptnr1_label_asym_id +_struct_conn.ptnr1_label_comp_id +_struct_conn.ptnr1_label_seq_id +_struct_conn.ptnr1_label_atom_id +_struct_conn.pdbx_ptnr1_label_alt_id +_struct_conn.pdbx_ptnr1_PDB_ins_code +_struct_conn.pdbx_ptnr1_standard_comp_id +_struct_conn.ptnr1_symmetry +_struct_conn.ptnr2_label_asym_id +_struct_conn.ptnr2_label_comp_id +_struct_conn.ptnr2_label_seq_id +_struct_conn.ptnr2_label_atom_id +_struct_conn.pdbx_ptnr2_label_alt_id +_struct_conn.pdbx_ptnr2_PDB_ins_code +_struct_conn.ptnr1_auth_asym_id +_struct_conn.ptnr1_auth_comp_id +_struct_conn.ptnr1_auth_seq_id +_struct_conn.ptnr2_auth_asym_id +_struct_conn.ptnr2_auth_comp_id +_struct_conn.ptnr2_auth_seq_id +_struct_conn.ptnr2_symmetry +_struct_conn.pdbx_ptnr3_label_atom_id +_struct_conn.pdbx_ptnr3_label_seq_id +_struct_conn.pdbx_ptnr3_label_comp_id +_struct_conn.pdbx_ptnr3_label_asym_id +_struct_conn.pdbx_ptnr3_label_alt_id +_struct_conn.pdbx_ptnr3_PDB_ins_code +_struct_conn.details +_struct_conn.pdbx_dist_value +_struct_conn.pdbx_value_order + covale1 covale ? ? A FVA 1 C ? ? ? 1_555 A GLY 2 N ? ? A FVA 1 A GLY 2 1_555 ? ? ? ? ? ? ? 1.325 ? + covale2 covale ? ? A ALA 3 C ? ? ? 1_555 A DLE 4 N ? ? A ALA 3 A DLE 4 1_555 ? ? ? ? ? ? ? 1.325 ? + covale3 covale ? ? A DLE 4 C ? ? ? 1_555 A ALA 5 N ? ? A DLE 4 A ALA 5 1_555 ? ? ? ? ? ? ? 1.325 ? + covale4 covale ? ? A ALA 5 C ? ? ? 1_555 A DVA 6 N ? ? A ALA 5 A DVA 6 1_555 ? ? ? ? ? ? ? 1.325 ? + covale5 covale ? ? A DVA 6 C ? ? ? 1_555 A VAL 7 N ? ? A DVA 6 A VAL 7 1_555 ? ? ? ? ? ? ? 1.326 ? + covale6 covale ? ? A VAL 7 C ? ? ? 1_555 A DVA 8 N ? ? A VAL 7 A DVA 8 1_555 ? ? ? ? ? ? ? 1.325 ? + covale7 covale ? ? A DVA 8 C ? ? ? 1_555 A TRP 9 N ? ? A DVA 8 A TRP 9 1_555 ? ? ? ? ? ? ? 1.325 ? + covale8 covale ? ? A TRP 9 C ? ? ? 1_555 A DLE 10 N ? ? A TRP 9 A DLE 10 1_555 ? ? ? ? ? ? ? 1.325 ? + covale9 covale ? ? A DLE 10 C ? ? ? 1_555 A TRP 11 N ? ? A DLE 10 A TRP 11 1_555 ? ? ? ? ? ? ? 1.325 ? +covale10 covale ? ? A TRP 11 C ? ? ? 1_555 A DLE 12 N ? ? A TRP 11 A DLE 12 1_555 ? ? ? ? ? ? ? 1.324 ? +covale11 covale ? ? A DLE 12 C ? ? ? 1_555 A TRP 13 N ? ? A DLE 12 A TRP 13 1_555 ? ? ? ? ? ? ? 1.325 ? +covale12 covale ? ? A TRP 13 C ? ? ? 1_555 A DLE 14 N ? ? A TRP 13 A DLE 14 1_555 ? ? ? ? ? ? ? 1.325 ? +covale13 covale ? ? A DLE 14 C ? ? ? 1_555 A TRP 15 N ? ? A DLE 14 A TRP 15 1_555 ? ? ? ? ? ? ? 1.325 ? +covale14 covale ? ? A TRP 15 C ? ? ? 1_555 A ETA 16 N ? ? A TRP 15 A ETA 16 1_555 ? ? ? ? ? ? ? 1.325 ? +covale15 covale ? ? B FVA 1 C ? ? ? 1_555 B GLY 2 N ? ? B FVA 1 B GLY 2 1_555 ? ? ? ? ? ? ? 1.325 ? +covale16 covale ? ? B ALA 3 C ? ? ? 1_555 B DLE 4 N ? ? B ALA 3 B DLE 4 1_555 ? ? ? ? ? ? ? 1.324 ? +covale17 covale ? ? B DLE 4 C ? ? ? 1_555 B ALA 5 N ? ? B DLE 4 B ALA 5 1_555 ? ? ? ? ? ? ? 1.325 ? +covale18 covale ? ? B ALA 5 C ? ? ? 1_555 B DVA 6 N ? ? B ALA 5 B DVA 6 1_555 ? ? ? ? ? ? ? 1.325 ? +covale19 covale ? ? B DVA 6 C ? ? ? 1_555 B VAL 7 N ? ? B DVA 6 B VAL 7 1_555 ? ? ? ? ? ? ? 1.325 ? +covale20 covale ? ? B VAL 7 C ? ? ? 1_555 B DVA 8 N ? ? B VAL 7 B DVA 8 1_555 ? ? ? ? ? ? ? 1.325 ? +covale21 covale ? ? B DVA 8 C ? ? ? 1_555 B TRP 9 N ? ? B DVA 8 B TRP 9 1_555 ? ? ? ? ? ? ? 1.325 ? +covale22 covale ? ? B TRP 9 C ? ? ? 1_555 B DLE 10 N ? ? B TRP 9 B DLE 10 1_555 ? ? ? ? ? ? ? 1.324 ? +covale23 covale ? ? B DLE 10 C ? ? ? 1_555 B TRP 11 N ? ? B DLE 10 B TRP 11 1_555 ? ? ? ? ? ? ? 1.325 ? +covale24 covale ? ? B TRP 11 C ? ? ? 1_555 B DLE 12 N ? ? B TRP 11 B DLE 12 1_555 ? ? ? ? ? ? ? 1.325 ? +covale25 covale ? ? B DLE 12 C ? ? ? 1_555 B TRP 13 N ? ? B DLE 12 B TRP 13 1_555 ? ? ? ? ? ? ? 1.324 ? +covale26 covale ? ? B TRP 13 C ? ? ? 1_555 B DLE 14 N ? ? B TRP 13 B DLE 14 1_555 ? ? ? ? ? ? ? 1.325 ? +covale27 covale ? ? B DLE 14 C ? ? ? 1_555 B TRP 15 N ? ? B DLE 14 B TRP 15 1_555 ? ? ? ? ? ? ? 1.325 ? +covale28 covale ? ? B TRP 15 C ? ? ? 1_555 B ETA 16 N ? ? B TRP 15 B ETA 16 1_555 ? ? ? ? ? ? ? 1.325 ? +# +_struct_conn_type.id covale +_struct_conn_type.criteria ? +_struct_conn_type.reference ? + +# +_struct_sheet.id AA +_struct_sheet.type ? +_struct_sheet.number_strands 2 +_struct_sheet.details ? + +# +_struct_sheet_order.sheet_id AA +_struct_sheet_order.range_id_1 1 +_struct_sheet_order.range_id_2 2 +_struct_sheet_order.offset ? +_struct_sheet_order.sense anti-parallel + +# +loop_ +_struct_sheet_range.sheet_id +_struct_sheet_range.id +_struct_sheet_range.beg_label_comp_id +_struct_sheet_range.beg_label_asym_id +_struct_sheet_range.beg_label_seq_id +_struct_sheet_range.pdbx_beg_PDB_ins_code +_struct_sheet_range.end_label_comp_id +_struct_sheet_range.end_label_asym_id +_struct_sheet_range.end_label_seq_id +_struct_sheet_range.pdbx_end_PDB_ins_code +_struct_sheet_range.beg_auth_comp_id +_struct_sheet_range.beg_auth_asym_id +_struct_sheet_range.beg_auth_seq_id +_struct_sheet_range.end_auth_comp_id +_struct_sheet_range.end_auth_asym_id +_struct_sheet_range.end_auth_seq_id +AA 1 GLY A 2 ? TRP A 15 ? GLY A 2 TRP A 15 +AA 2 GLY B 2 ? TRP B 15 ? GLY B 2 TRP B 15 +# +_pdbx_struct_sheet_hbond.sheet_id AA +_pdbx_struct_sheet_hbond.range_id_1 1 +_pdbx_struct_sheet_hbond.range_id_2 2 +_pdbx_struct_sheet_hbond.range_1_label_atom_id N +_pdbx_struct_sheet_hbond.range_1_label_comp_id ALA +_pdbx_struct_sheet_hbond.range_1_label_asym_id A +_pdbx_struct_sheet_hbond.range_1_label_seq_id 3 +_pdbx_struct_sheet_hbond.range_1_PDB_ins_code ? +_pdbx_struct_sheet_hbond.range_1_auth_atom_id N +_pdbx_struct_sheet_hbond.range_1_auth_comp_id ALA +_pdbx_struct_sheet_hbond.range_1_auth_asym_id A +_pdbx_struct_sheet_hbond.range_1_auth_seq_id 3 +_pdbx_struct_sheet_hbond.range_2_label_atom_id O +_pdbx_struct_sheet_hbond.range_2_label_comp_id ALA +_pdbx_struct_sheet_hbond.range_2_label_asym_id B +_pdbx_struct_sheet_hbond.range_2_label_seq_id 3 +_pdbx_struct_sheet_hbond.range_2_PDB_ins_code ? +_pdbx_struct_sheet_hbond.range_2_auth_atom_id O +_pdbx_struct_sheet_hbond.range_2_auth_comp_id ALA +_pdbx_struct_sheet_hbond.range_2_auth_asym_id B +_pdbx_struct_sheet_hbond.range_2_auth_seq_id 3 + +# +loop_ +_struct_site.id +_struct_site.details +_struct_site.pdbx_evidence_code +_struct_site.pdbx_auth_comp_id +_struct_site.pdbx_auth_asym_id +_struct_site.pdbx_auth_seq_id +_struct_site.pdbx_auth_ins_code +AC1 "BINDING SITE FOR RESIDUE DLE" Software DLE A 4 . +AC2 "BINDING SITE FOR RESIDUE DVA" Software DVA A 6 . +AC3 "BINDING SITE FOR RESIDUE DVA" Software DVA A 8 . +AC4 "BINDING SITE FOR RESIDUE DLE" Software DLE A 10 . +AC5 "BINDING SITE FOR RESIDUE DLE" Software DLE A 12 . +AC6 "BINDING SITE FOR RESIDUE DLE" Software DLE A 14 . +AC7 "BINDING SITE FOR RESIDUE DLE" Software DLE B 4 . +AC8 "BINDING SITE FOR RESIDUE DVA" Software DVA B 6 . +AC9 "BINDING SITE FOR RESIDUE DVA" Software DVA B 8 . +BC1 "BINDING SITE FOR RESIDUE DLE" Software DLE B 10 . +BC2 "BINDING SITE FOR RESIDUE DLE" Software DLE B 12 . +BC3 "BINDING SITE FOR RESIDUE DLE" Software DLE B 14 . +# +loop_ +_struct_site_gen.id +_struct_site_gen.site_id +_struct_site_gen.auth_comp_id +_struct_site_gen.auth_asym_id +_struct_site_gen.auth_seq_id +_struct_site_gen.pdbx_auth_ins_code +_struct_site_gen.symmetry + 1 AC1 ALA A 3 . 1_555 + 2 AC1 ALA A 5 . 1_555 + 3 AC1 TRP A 9 . 1_555 + 4 AC1 DLE A 10 . 1_555 + 5 AC1 TRP A 11 . 1_555 + 6 AC1 FVA B 1 . 1_555 + 7 AC2 ALA A 5 . 1_555 + 8 AC2 VAL A 7 . 1_555 + 9 AC2 TRP A 11 . 1_555 +10 AC2 DLE A 12 . 1_555 +11 AC2 TRP A 13 . 1_555 +12 AC3 GLY A 2 . 1_555 +13 AC3 VAL A 7 . 1_555 +14 AC3 TRP A 9 . 1_555 +15 AC3 TRP A 13 . 1_555 +16 AC3 DLE A 14 . 1_555 +17 AC3 TRP A 15 . 1_555 +18 AC4 DLE A 4 . 1_555 +19 AC4 TRP A 9 . 1_555 +20 AC4 TRP A 11 . 1_555 +21 AC4 TRP A 15 . 1_555 +22 AC4 ETA A 16 . 1_555 +23 AC5 DVA A 6 . 1_555 +24 AC5 TRP A 11 . 1_555 +25 AC5 TRP A 13 . 1_555 +26 AC6 DVA A 8 . 1_555 +27 AC6 TRP A 13 . 1_555 +28 AC6 TRP A 15 . 1_555 +29 AC7 FVA A 1 . 1_555 +30 AC7 ALA B 3 . 1_555 +31 AC7 ALA B 5 . 1_555 +32 AC7 TRP B 9 . 1_555 +33 AC7 DLE B 10 . 1_555 +34 AC7 TRP B 11 . 1_555 +35 AC8 ALA B 5 . 1_555 +36 AC8 VAL B 7 . 1_555 +37 AC8 TRP B 11 . 1_555 +38 AC8 DLE B 12 . 1_555 +39 AC8 TRP B 13 . 1_555 +40 AC9 GLY B 2 . 1_555 +41 AC9 VAL B 7 . 1_555 +42 AC9 TRP B 9 . 1_555 +43 AC9 TRP B 13 . 1_555 +44 AC9 DLE B 14 . 1_555 +45 AC9 TRP B 15 . 1_555 +46 BC1 DLE B 4 . 1_555 +47 BC1 TRP B 9 . 1_555 +48 BC1 TRP B 11 . 1_555 +49 BC1 TRP B 15 . 1_555 +50 BC1 ETA B 16 . 1_555 +51 BC2 DVA B 6 . 1_555 +52 BC2 TRP B 11 . 1_555 +53 BC2 TRP B 13 . 1_555 +54 BC3 DVA B 8 . 1_555 +55 BC3 TRP B 13 . 1_555 +56 BC3 TRP B 15 . 1_555 +# +_atom_sites.entry_id 1GRM +_atom_sites.fract_transf_matrix[1][1] 1.000000 +_atom_sites.fract_transf_matrix[1][2] 0.000000 +_atom_sites.fract_transf_matrix[1][3] 0.000000 +_atom_sites.fract_transf_matrix[2][1] 0.000000 +_atom_sites.fract_transf_matrix[2][2] 1.000000 +_atom_sites.fract_transf_matrix[2][3] 0.000000 +_atom_sites.fract_transf_matrix[3][1] 0.000000 +_atom_sites.fract_transf_matrix[3][2] 0.000000 +_atom_sites.fract_transf_matrix[3][3] 1.000000 +_atom_sites.fract_transf_vector[1] 0.00000 +_atom_sites.fract_transf_vector[2] 0.00000 +_atom_sites.fract_transf_vector[3] 0.00000 + +# +_atom_sites_footnote.id 1 +_atom_sites_footnote.text "RESIDUES LEU 4, VAL 6, VAL 8, LEU 10, LEU 12, AND LEU 14 IN EACH CHAIN EXHIBIT THE D CONFIGURATION OF THE AMINO ACID." + +# +loop_ +_atom_type.symbol +C +N +O +# +loop_ +_atom_site.group_PDB +_atom_site.id +_atom_site.type_symbol +_atom_site.label_atom_id +_atom_site.label_alt_id +_atom_site.label_comp_id +_atom_site.label_asym_id +_atom_site.label_entity_id +_atom_site.label_seq_id +_atom_site.pdbx_PDB_ins_code +_atom_site.Cartn_x +_atom_site.Cartn_y +_atom_site.Cartn_z +_atom_site.occupancy +_atom_site.B_iso_or_equiv +_atom_site.pdbx_formal_charge +_atom_site.auth_seq_id +_atom_site.auth_comp_id +_atom_site.auth_asym_id +_atom_site.auth_atom_id +_atom_site.pdbx_PDB_model_num +_atom_site.pdbe_label_seq_id +HETATM 1 C C . FVA A 1 1 ? -3.595 0.079 3.555 1.00 0.00 ? 1 FVA A C 1 1 +HETATM 2 N N . FVA A 1 1 ? -2.330 -0.205 1.496 1.00 0.00 ? 1 FVA A N 1 1 +HETATM 3 O O . FVA A 1 1 ? -3.911 -1.055 3.906 1.00 0.00 ? 1 FVA A O 1 1 +HETATM 4 C CA . FVA A 1 1 ? -3.501 0.435 2.070 1.00 0.00 ? 1 FVA A CA 1 1 +HETATM 5 C CB . FVA A 1 1 ? -4.752 0.042 1.281 1.00 0.00 ? 1 FVA A CB 1 1 +HETATM 6 C CG1 . FVA A 1 1 ? -5.974 0.826 1.764 1.00 0.00 ? 1 FVA A CG1 1 1 +HETATM 7 C CG2 . FVA A 1 1 ? -4.535 0.232 -0.221 1.00 0.00 ? 1 FVA A CG2 1 1 +HETATM 8 O O1 . FVA A 1 1 ? -1.445 1.720 0.692 1.00 0.00 ? 1 FVA A O1 1 1 +HETATM 9 C CN . FVA A 1 1 ? -1.409 0.501 0.859 1.00 0.00 ? 1 FVA A CN 1 1 + ATOM 10 N N . GLY A 1 2 ? -3.315 1.072 4.387 1.00 0.00 ? 2 GLY A N 1 2 + ATOM 11 C CA . GLY A 1 2 ? -3.364 0.879 5.826 1.00 0.00 ? 2 GLY A CA 1 2 + ATOM 12 C C . GLY A 1 2 ? -2.503 1.917 6.549 1.00 0.00 ? 2 GLY A C 1 2 + ATOM 13 O O . GLY A 1 2 ? -3.009 2.947 6.992 1.00 0.00 ? 2 GLY A O 1 2 + ATOM 14 N N . ALA A 1 3 ? -1.218 1.610 6.645 1.00 0.00 ? 3 ALA A N 1 3 + ATOM 15 C CA . ALA A 1 3 ? -0.282 2.504 7.305 1.00 0.00 ? 3 ALA A CA 1 3 + ATOM 16 C C . ALA A 1 3 ? 1.118 2.286 6.729 1.00 0.00 ? 3 ALA A C 1 3 + ATOM 17 O O . ALA A 1 3 ? 1.488 1.161 6.395 1.00 0.00 ? 3 ALA A O 1 3 + ATOM 18 C CB . ALA A 1 3 ? -0.333 2.271 8.817 1.00 0.00 ? 3 ALA A CB 1 3 +HETATM 19 N N . DLE A 1 4 ? 1.860 3.379 6.631 1.00 0.00 ? 4 DLE A N 1 4 +HETATM 20 C CA . DLE A 1 4 ? 3.212 3.322 6.101 1.00 0.00 ? 4 DLE A CA 1 4 +HETATM 21 C CB . DLE A 1 4 ? 4.235 3.539 7.217 1.00 0.00 ? 4 DLE A CB 1 4 +HETATM 22 C CG . DLE A 1 4 ? 5.653 3.290 6.701 1.00 0.00 ? 4 DLE A CG 1 4 +HETATM 23 C CD1 . DLE A 1 4 ? 6.367 4.609 6.398 1.00 0.00 ? 4 DLE A CD1 1 4 +HETATM 24 C CD2 . DLE A 1 4 ? 6.446 2.419 7.676 1.00 0.00 ? 4 DLE A CD2 1 4 +HETATM 25 C C . DLE A 1 4 ? 3.344 4.316 4.945 1.00 0.00 ? 4 DLE A C 1 4 +HETATM 26 O O . DLE A 1 4 ? 3.076 5.504 5.109 1.00 0.00 ? 4 DLE A O 1 4 + ATOM 27 N N . ALA A 1 5 ? 3.758 3.791 3.801 1.00 0.00 ? 5 ALA A N 1 5 + ATOM 28 C CA . ALA A 1 5 ? 3.929 4.617 2.617 1.00 0.00 ? 5 ALA A CA 1 5 + ATOM 29 C C . ALA A 1 5 ? 2.939 4.169 1.540 1.00 0.00 ? 5 ALA A C 1 5 + ATOM 30 O O . ALA A 1 5 ? 2.972 3.022 1.100 1.00 0.00 ? 5 ALA A O 1 5 + ATOM 31 C CB . ALA A 1 5 ? 5.381 4.534 2.143 1.00 0.00 ? 5 ALA A CB 1 5 +HETATM 32 N N . DVA A 1 6 ? 2.080 5.099 1.149 1.00 0.00 ? 6 DVA A N 1 6 +HETATM 33 C CA . DVA A 1 6 ? 1.082 4.815 0.132 1.00 0.00 ? 6 DVA A CA 1 6 +HETATM 34 C CB . DVA A 1 6 ? 1.580 5.284 -1.237 1.00 0.00 ? 6 DVA A CB 1 6 +HETATM 35 C CG1 . DVA A 1 6 ? 2.863 4.551 -1.635 1.00 0.00 ? 6 DVA A CG1 1 6 +HETATM 36 C CG2 . DVA A 1 6 ? 0.496 5.113 -2.303 1.00 0.00 ? 6 DVA A CG2 1 6 +HETATM 37 C C . DVA A 1 6 ? -0.248 5.456 0.535 1.00 0.00 ? 6 DVA A C 1 6 +HETATM 38 O O . DVA A 1 6 ? -0.349 6.677 0.629 1.00 0.00 ? 6 DVA A O 1 6 + ATOM 39 N N . VAL A 1 7 ? -1.235 4.601 0.764 1.00 0.00 ? 7 VAL A N 1 7 + ATOM 40 C CA . VAL A 1 7 ? -2.554 5.068 1.156 1.00 0.00 ? 7 VAL A CA 1 7 + ATOM 41 C C . VAL A 1 7 ? -2.807 4.698 2.618 1.00 0.00 ? 7 VAL A C 1 7 + ATOM 42 O O . VAL A 1 7 ? -2.463 3.600 3.053 1.00 0.00 ? 7 VAL A O 1 7 + ATOM 43 C CB . VAL A 1 7 ? -3.612 4.505 0.205 1.00 0.00 ? 7 VAL A CB 1 7 + ATOM 44 C CG1 . VAL A 1 7 ? -4.962 5.192 0.419 1.00 0.00 ? 7 VAL A CG1 1 7 + ATOM 45 C CG2 . VAL A 1 7 ? -3.159 4.625 -1.252 1.00 0.00 ? 7 VAL A CG2 1 7 +HETATM 46 N N . DVA A 1 8 ? -3.408 5.635 3.337 1.00 0.00 ? 8 DVA A N 1 8 +HETATM 47 C CA . DVA A 1 8 ? -3.713 5.421 4.742 1.00 0.00 ? 8 DVA A CA 1 8 +HETATM 48 C CB . DVA A 1 8 ? -5.212 5.179 4.922 1.00 0.00 ? 8 DVA A CB 1 8 +HETATM 49 C CG1 . DVA A 1 8 ? -5.618 3.813 4.366 1.00 0.00 ? 8 DVA A CG1 1 8 +HETATM 50 C CG2 . DVA A 1 8 ? -5.618 5.317 6.390 1.00 0.00 ? 8 DVA A CG2 1 8 +HETATM 51 C C . DVA A 1 8 ? -3.197 6.608 5.557 1.00 0.00 ? 8 DVA A C 1 8 +HETATM 52 O O . DVA A 1 8 ? -3.535 7.756 5.271 1.00 0.00 ? 8 DVA A O 1 8 + ATOM 53 N N . TRP A 1 9 ? -2.386 6.292 6.556 1.00 0.00 ? 9 TRP A N 1 9 + ATOM 54 C CA . TRP A 1 9 ? -1.820 7.318 7.415 1.00 0.00 ? 9 TRP A CA 1 9 + ATOM 55 C C . TRP A 1 9 ? -0.305 7.108 7.467 1.00 0.00 ? 9 TRP A C 1 9 + ATOM 56 O O . TRP A 1 9 ? 0.164 5.987 7.660 1.00 0.00 ? 9 TRP A O 1 9 + ATOM 57 C CB . TRP A 1 9 ? -2.475 7.298 8.797 1.00 0.00 ? 9 TRP A CB 1 9 + ATOM 58 C CG . TRP A 1 9 ? -1.741 8.137 9.845 1.00 0.00 ? 9 TRP A CG 1 9 + ATOM 59 C CD1 . TRP A 1 9 ? -1.921 9.432 10.140 1.00 0.00 ? 9 TRP A CD1 1 9 + ATOM 60 C CD2 . TRP A 1 9 ? -0.709 7.727 10.733 1.00 0.00 ? 9 TRP A CD2 1 9 + ATOM 61 N NE1 . TRP A 1 9 ? -1.073 9.845 11.147 1.00 0.00 ? 9 TRP A NE1 1 9 + ATOM 62 C CE2 . TRP A 1 9 ? -0.304 8.748 11.518 1.00 0.00 ? 9 TRP A CE2 1 9 + ATOM 63 C CE3 . TRP A 1 9 ? -0.119 6.453 10.860 1.00 0.00 ? 9 TRP A CE3 1 9 + ATOM 64 C CZ2 . TRP A 1 9 ? 0.697 8.654 12.492 1.00 0.00 ? 9 TRP A CZ2 1 9 + ATOM 65 C CZ3 . TRP A 1 9 ? 0.882 6.358 11.833 1.00 0.00 ? 9 TRP A CZ3 1 9 + ATOM 66 C CH2 . TRP A 1 9 ? 1.299 7.409 12.641 1.00 0.00 ? 9 TRP A CH2 1 9 +HETATM 67 N N . DLE A 1 10 ? 0.419 8.204 7.292 1.00 0.00 ? 10 DLE A N 1 10 +HETATM 68 C CA . DLE A 1 10 ? 1.871 8.154 7.318 1.00 0.00 ? 10 DLE A CA 1 10 +HETATM 69 C CB . DLE A 1 10 ? 2.390 8.384 8.738 1.00 0.00 ? 10 DLE A CB 1 10 +HETATM 70 C CG . DLE A 1 10 ? 3.913 8.530 8.734 1.00 0.00 ? 10 DLE A CG 1 10 +HETATM 71 C CD1 . DLE A 1 10 ? 4.417 9.056 10.080 1.00 0.00 ? 10 DLE A CD1 1 10 +HETATM 72 C CD2 . DLE A 1 10 ? 4.589 7.215 8.343 1.00 0.00 ? 10 DLE A CD2 1 10 +HETATM 73 C C . DLE A 1 10 ? 2.428 9.145 6.292 1.00 0.00 ? 10 DLE A C 1 10 +HETATM 74 O O . DLE A 1 10 ? 2.400 10.354 6.513 1.00 0.00 ? 10 DLE A O 1 10 + ATOM 75 N N . TRP A 1 11 ? 2.921 8.593 5.193 1.00 0.00 ? 11 TRP A N 1 11 + ATOM 76 C CA . TRP A 1 11 ? 3.484 9.412 4.133 1.00 0.00 ? 11 TRP A CA 1 11 + ATOM 77 C C . TRP A 1 11 ? 2.866 8.961 2.807 1.00 0.00 ? 11 TRP A C 1 11 + ATOM 78 O O . TRP A 1 11 ? 2.395 7.830 2.692 1.00 0.00 ? 11 TRP A O 1 11 + ATOM 79 C CB . TRP A 1 11 ? 5.013 9.339 4.136 1.00 0.00 ? 11 TRP A CB 1 11 + ATOM 80 C CG . TRP A 1 11 ? 5.658 9.862 5.421 1.00 0.00 ? 11 TRP A CG 1 11 + ATOM 81 C CD1 . TRP A 1 11 ? 5.406 11.012 6.061 1.00 0.00 ? 11 TRP A CD1 1 11 + ATOM 82 C CD2 . TRP A 1 11 ? 6.662 9.244 6.214 1.00 0.00 ? 11 TRP A CD2 1 11 + ATOM 83 N NE1 . TRP A 1 11 ? 6.186 11.143 7.192 1.00 0.00 ? 11 TRP A NE1 1 11 + ATOM 84 C CE2 . TRP A 1 11 ? 6.985 10.008 7.279 1.00 0.00 ? 11 TRP A CE2 1 11 + ATOM 85 C CE3 . TRP A 1 11 ? 7.302 8.004 6.006 1.00 0.00 ? 11 TRP A CE3 1 11 + ATOM 86 C CZ2 . TRP A 1 11 ? 7.941 9.666 8.243 1.00 0.00 ? 11 TRP A CZ2 1 11 + ATOM 87 C CZ3 . TRP A 1 11 ? 8.258 7.662 6.969 1.00 0.00 ? 11 TRP A CZ3 1 11 + ATOM 88 C CH2 . TRP A 1 11 ? 8.590 8.449 8.066 1.00 0.00 ? 11 TRP A CH2 1 11 +HETATM 89 N N . DLE A 1 12 ? 2.890 9.867 1.842 1.00 0.00 ? 12 DLE A N 1 12 +HETATM 90 C CA . DLE A 1 12 ? 2.338 9.576 0.529 1.00 0.00 ? 12 DLE A CA 1 12 +HETATM 91 C CB . DLE A 1 12 ? 3.395 9.791 -0.556 1.00 0.00 ? 12 DLE A CB 1 12 +HETATM 92 C CG . DLE A 1 12 ? 4.677 9.029 -0.214 1.00 0.00 ? 12 DLE A CG 1 12 +HETATM 93 C CD1 . DLE A 1 12 ? 4.492 7.525 -0.421 1.00 0.00 ? 12 DLE A CD1 1 12 +HETATM 94 C CD2 . DLE A 1 12 ? 5.868 9.575 -1.005 1.00 0.00 ? 12 DLE A CD2 1 12 +HETATM 95 C C . DLE A 1 12 ? 1.067 10.402 0.320 1.00 0.00 ? 12 DLE A C 1 12 +HETATM 96 O O . DLE A 1 12 ? 1.135 11.556 -0.100 1.00 0.00 ? 12 DLE A O 1 12 + ATOM 97 N N . TRP A 1 13 ? -0.062 9.778 0.623 1.00 0.00 ? 13 TRP A N 1 13 + ATOM 98 C CA . TRP A 1 13 ? -1.346 10.442 0.474 1.00 0.00 ? 13 TRP A CA 1 13 + ATOM 99 C C . TRP A 1 13 ? -2.250 9.984 1.621 1.00 0.00 ? 13 TRP A C 1 13 + ATOM 100 O O . TRP A 1 13 ? -2.045 8.913 2.189 1.00 0.00 ? 13 TRP A O 1 13 + ATOM 101 C CB . TRP A 1 13 ? -1.947 10.169 -0.906 1.00 0.00 ? 13 TRP A CB 1 13 + ATOM 102 C CG . TRP A 1 13 ? -1.155 10.783 -2.061 1.00 0.00 ? 13 TRP A CG 1 13 + ATOM 103 C CD1 . TRP A 1 13 ? -1.276 12.011 -2.584 1.00 0.00 ? 13 TRP A CD1 1 13 + ATOM 104 C CD2 . TRP A 1 13 ? -0.120 10.186 -2.832 1.00 0.00 ? 13 TRP A CD2 1 13 + ATOM 105 N NE1 . TRP A 1 13 ? -0.387 12.210 -3.621 1.00 0.00 ? 13 TRP A NE1 1 13 + ATOM 106 C CE2 . TRP A 1 13 ? 0.346 11.038 -3.770 1.00 0.00 ? 13 TRP A CE2 1 13 + ATOM 107 C CE3 . TRP A 1 13 ? 0.421 8.889 -2.714 1.00 0.00 ? 13 TRP A CE3 1 13 + ATOM 108 C CZ2 . TRP A 1 13 ? 1.365 10.737 -4.682 1.00 0.00 ? 13 TRP A CZ2 1 13 + ATOM 109 C CZ3 . TRP A 1 13 ? 1.441 8.589 -3.625 1.00 0.00 ? 13 TRP A CZ3 1 13 + ATOM 110 C CH2 . TRP A 1 13 ? 1.920 9.466 -4.591 1.00 0.00 ? 13 TRP A CH2 1 13 +HETATM 111 N N . DLE A 1 14 ? -3.233 10.819 1.926 1.00 0.00 ? 14 DLE A N 1 14 +HETATM 112 C CA . DLE A 1 14 ? -4.168 10.515 2.996 1.00 0.00 ? 14 DLE A CA 1 14 +HETATM 113 C CB . DLE A 1 14 ? -5.603 10.493 2.462 1.00 0.00 ? 14 DLE A CB 1 14 +HETATM 114 C CG . DLE A 1 14 ? -5.836 9.251 1.600 1.00 0.00 ? 14 DLE A CG 1 14 +HETATM 115 C CD1 . DLE A 1 14 ? -6.394 8.100 2.440 1.00 0.00 ? 14 DLE A CD1 1 14 +HETATM 116 C CD2 . DLE A 1 14 ? -6.731 9.575 0.402 1.00 0.00 ? 14 DLE A CD2 1 14 +HETATM 117 C C . DLE A 1 14 ? -3.957 11.496 4.150 1.00 0.00 ? 14 DLE A C 1 14 +HETATM 118 O O . DLE A 1 14 ? -4.386 12.646 4.077 1.00 0.00 ? 14 DLE A O 1 14 + ATOM 119 N N . TRP A 1 15 ? -3.294 11.006 5.187 1.00 0.00 ? 15 TRP A N 1 15 + ATOM 120 C CA . TRP A 1 15 ? -3.020 11.827 6.354 1.00 0.00 ? 15 TRP A CA 1 15 + ATOM 121 C C . TRP A 1 15 ? -1.606 11.501 6.840 1.00 0.00 ? 15 TRP A C 1 15 + ATOM 122 O O . TRP A 1 15 ? -1.000 10.529 6.391 1.00 0.00 ? 15 TRP A O 1 15 + ATOM 123 C CB . TRP A 1 15 ? -4.086 11.619 7.433 1.00 0.00 ? 15 TRP A CB 1 15 + ATOM 124 C CG . TRP A 1 15 ? -5.514 11.885 6.956 1.00 0.00 ? 15 TRP A CG 1 15 + ATOM 125 C CD1 . TRP A 1 15 ? -6.165 13.056 6.907 1.00 0.00 ? 15 TRP A CD1 1 15 + ATOM 126 C CD2 . TRP A 1 15 ? -6.463 10.949 6.459 1.00 0.00 ? 15 TRP A CD2 1 15 + ATOM 127 N NE1 . TRP A 1 15 ? -7.445 12.904 6.417 1.00 0.00 ? 15 TRP A NE1 1 15 + ATOM 128 C CE2 . TRP A 1 15 ? -7.624 11.555 6.134 1.00 0.00 ? 15 TRP A CE2 1 15 + ATOM 129 C CE3 . TRP A 1 15 ? -6.312 9.557 6.289 1.00 0.00 ? 15 TRP A CE3 1 15 + ATOM 130 C CZ2 . TRP A 1 15 ? -8.748 10.897 5.619 1.00 0.00 ? 15 TRP A CZ2 1 15 + ATOM 131 C CZ3 . TRP A 1 15 ? -7.435 8.900 5.774 1.00 0.00 ? 15 TRP A CZ3 1 15 + ATOM 132 C CH2 . TRP A 1 15 ? -8.631 9.524 5.439 1.00 0.00 ? 15 TRP A CH2 1 15 +HETATM 133 C CA . ETA A 1 16 ? 0.210 12.145 8.302 1.00 0.00 ? 16 ETA A CA 1 16 +HETATM 134 N N . ETA A 1 16 ? -1.122 12.332 7.751 1.00 0.00 ? 16 ETA A N 1 16 +HETATM 135 C CB . ETA A 1 16 ? 1.207 13.019 7.538 1.00 0.00 ? 16 ETA A CB 1 16 +HETATM 136 O O . ETA A 1 16 ? 1.257 12.684 6.153 1.00 0.00 ? 16 ETA A O 1 16 +HETATM 137 C C . FVA B 1 1 ? 3.555 -0.104 3.532 1.00 0.00 ? 1 FVA B C 1 1 +HETATM 138 N N . FVA B 1 1 ? 2.276 0.179 1.481 1.00 0.00 ? 1 FVA B N 1 1 +HETATM 139 O O . FVA B 1 1 ? 3.873 1.031 3.880 1.00 0.00 ? 1 FVA B O 1 1 +HETATM 140 C CA . FVA B 1 1 ? 3.451 -0.461 2.047 1.00 0.00 ? 1 FVA B CA 1 1 +HETATM 141 C CB . FVA B 1 1 ? 4.697 -0.068 1.250 1.00 0.00 ? 1 FVA B CB 1 1 +HETATM 142 C CG1 . FVA B 1 1 ? 5.922 -0.852 1.725 1.00 0.00 ? 1 FVA B CG1 1 1 +HETATM 143 C CG2 . FVA B 1 1 ? 4.470 -0.260 -0.250 1.00 0.00 ? 1 FVA B CG2 1 1 +HETATM 144 O O1 . FVA B 1 1 ? 1.386 -1.747 0.684 1.00 0.00 ? 1 FVA B O1 1 1 +HETATM 145 C CN . FVA B 1 1 ? 1.351 -0.528 0.850 1.00 0.00 ? 1 FVA B CN 1 1 + ATOM 146 N N . GLY B 1 2 ? 3.281 -1.096 4.366 1.00 0.00 ? 2 GLY B N 1 2 + ATOM 147 C CA . GLY B 1 2 ? 3.339 -0.902 5.805 1.00 0.00 ? 2 GLY B CA 1 2 + ATOM 148 C C . GLY B 1 2 ? 2.482 -1.940 6.534 1.00 0.00 ? 2 GLY B C 1 2 + ATOM 149 O O . GLY B 1 2 ? 2.991 -2.970 6.974 1.00 0.00 ? 2 GLY B O 1 2 + ATOM 150 N N . ALA B 1 3 ? 1.198 -1.633 6.638 1.00 0.00 ? 3 ALA B N 1 3 + ATOM 151 C CA . ALA B 1 3 ? 0.267 -2.527 7.305 1.00 0.00 ? 3 ALA B CA 1 3 + ATOM 152 C C . ALA B 1 3 ? -1.138 -2.309 6.738 1.00 0.00 ? 3 ALA B C 1 3 + ATOM 153 O O . ALA B 1 3 ? -1.509 -1.185 6.406 1.00 0.00 ? 3 ALA B O 1 3 + ATOM 154 C CB . ALA B 1 3 ? 0.327 -2.293 8.816 1.00 0.00 ? 3 ALA B CB 1 3 +HETATM 155 N N . DLE B 1 4 ? -1.880 -3.402 6.645 1.00 0.00 ? 4 DLE B N 1 4 +HETATM 156 C CA . DLE B 1 4 ? -3.235 -3.345 6.124 1.00 0.00 ? 4 DLE B CA 1 4 +HETATM 157 C CB . DLE B 1 4 ? -4.251 -3.562 7.247 1.00 0.00 ? 4 DLE B CB 1 4 +HETATM 158 C CG . DLE B 1 4 ? -5.672 -3.313 6.740 1.00 0.00 ? 4 DLE B CG 1 4 +HETATM 159 C CD1 . DLE B 1 4 ? -6.388 -4.632 6.443 1.00 0.00 ? 4 DLE B CD1 1 4 +HETATM 160 C CD2 . DLE B 1 4 ? -6.459 -2.441 7.720 1.00 0.00 ? 4 DLE B CD2 1 4 +HETATM 161 C C . DLE B 1 4 ? -3.375 -4.340 4.970 1.00 0.00 ? 4 DLE B C 1 4 +HETATM 162 O O . DLE B 1 4 ? -3.106 -5.528 5.133 1.00 0.00 ? 4 DLE B O 1 4 + ATOM 163 N N . ALA B 1 5 ? -3.796 -3.816 3.828 1.00 0.00 ? 5 ALA B N 1 5 + ATOM 164 C CA . ALA B 1 5 ? -3.975 -4.642 2.646 1.00 0.00 ? 5 ALA B CA 1 5 + ATOM 165 C C . ALA B 1 5 ? -2.992 -4.195 1.562 1.00 0.00 ? 5 ALA B C 1 5 + ATOM 166 O O . ALA B 1 5 ? -3.028 -3.048 1.121 1.00 0.00 ? 5 ALA B O 1 5 + ATOM 167 C CB . ALA B 1 5 ? -5.431 -4.560 2.181 1.00 0.00 ? 5 ALA B CB 1 5 +HETATM 168 N N . DVA B 1 6 ? -2.136 -5.126 1.166 1.00 0.00 ? 6 DVA B N 1 6 +HETATM 169 C CA . DVA B 1 6 ? -1.144 -4.842 0.142 1.00 0.00 ? 6 DVA B CA 1 6 +HETATM 170 C CB . DVA B 1 6 ? -1.651 -5.312 -1.223 1.00 0.00 ? 6 DVA B CB 1 6 +HETATM 171 C CG1 . DVA B 1 6 ? -2.937 -4.579 -1.613 1.00 0.00 ? 6 DVA B CG1 1 6 +HETATM 172 C CG2 . DVA B 1 6 ? -0.574 -5.141 -2.296 1.00 0.00 ? 6 DVA B CG2 1 6 +HETATM 173 C C . DVA B 1 6 ? 0.188 -5.482 0.537 1.00 0.00 ? 6 DVA B C 1 6 +HETATM 174 O O . DVA B 1 6 ? 0.290 -6.704 0.631 1.00 0.00 ? 6 DVA B O 1 6 + ATOM 175 N N . VAL B 1 7 ? 1.176 -4.627 0.759 1.00 0.00 ? 7 VAL B N 1 7 + ATOM 176 C CA . VAL B 1 7 ? 2.498 -5.094 1.143 1.00 0.00 ? 7 VAL B CA 1 7 + ATOM 177 C C . VAL B 1 7 ? 2.761 -4.723 2.603 1.00 0.00 ? 7 VAL B C 1 7 + ATOM 178 O O . VAL B 1 7 ? 2.419 -3.626 3.039 1.00 0.00 ? 7 VAL B O 1 7 + ATOM 179 C CB . VAL B 1 7 ? 3.550 -4.532 0.184 1.00 0.00 ? 7 VAL B CB 1 7 + ATOM 180 C CG1 . VAL B 1 7 ? 4.901 -5.219 0.390 1.00 0.00 ? 7 VAL B CG1 1 7 + ATOM 181 C CG2 . VAL B 1 7 ? 3.088 -4.653 -1.269 1.00 0.00 ? 7 VAL B CG2 1 7 +HETATM 182 N N . DVA B 1 8 ? 3.367 -5.660 3.318 1.00 0.00 ? 8 DVA B N 1 8 +HETATM 183 C CA . DVA B 1 8 ? 3.680 -5.446 4.721 1.00 0.00 ? 8 DVA B CA 1 8 +HETATM 184 C CB . DVA B 1 8 ? 5.181 -5.203 4.891 1.00 0.00 ? 8 DVA B CB 1 8 +HETATM 185 C CG1 . DVA B 1 8 ? 5.583 -3.837 4.332 1.00 0.00 ? 8 DVA B CG1 1 8 +HETATM 186 C CG2 . DVA B 1 8 ? 5.596 -5.340 6.357 1.00 0.00 ? 8 DVA B CG2 1 8 +HETATM 187 C C . DVA B 1 8 ? 3.170 -6.632 5.540 1.00 0.00 ? 8 DVA B C 1 8 +HETATM 188 O O . DVA B 1 8 ? 3.506 -7.780 5.253 1.00 0.00 ? 8 DVA B O 1 8 + ATOM 189 N N . TRP B 1 9 ? 2.365 -6.315 6.544 1.00 0.00 ? 9 TRP B N 1 9 + ATOM 190 C CA . TRP B 1 9 ? 1.805 -7.341 7.408 1.00 0.00 ? 9 TRP B CA 1 9 + ATOM 191 C C . TRP B 1 9 ? 0.290 -7.131 7.470 1.00 0.00 ? 9 TRP B C 1 9 + ATOM 192 O O . TRP B 1 9 ? -0.177 -6.010 7.665 1.00 0.00 ? 9 TRP B O 1 9 + ATOM 193 C CB . TRP B 1 9 ? 2.469 -7.320 8.785 1.00 0.00 ? 9 TRP B CB 1 9 + ATOM 194 C CG . TRP B 1 9 ? 1.742 -8.158 9.839 1.00 0.00 ? 9 TRP B CG 1 9 + ATOM 195 C CD1 . TRP B 1 9 ? 1.924 -9.452 10.133 1.00 0.00 ? 9 TRP B CD1 1 9 + ATOM 196 C CD2 . TRP B 1 9 ? 0.716 -7.748 10.733 1.00 0.00 ? 9 TRP B CD2 1 9 + ATOM 197 N NE1 . TRP B 1 9 ? 1.083 -9.865 11.146 1.00 0.00 ? 9 TRP B NE1 1 9 + ATOM 198 C CE2 . TRP B 1 9 ? 0.316 -8.768 11.521 1.00 0.00 ? 9 TRP B CE2 1 9 + ATOM 199 C CE3 . TRP B 1 9 ? 0.127 -6.473 10.863 1.00 0.00 ? 9 TRP B CE3 1 9 + ATOM 200 C CZ2 . TRP B 1 9 ? -0.679 -8.673 12.502 1.00 0.00 ? 9 TRP B CZ2 1 9 + ATOM 201 C CZ3 . TRP B 1 9 ? -0.868 -6.378 11.843 1.00 0.00 ? 9 TRP B CZ3 1 9 + ATOM 202 C CH2 . TRP B 1 9 ? -1.280 -7.428 12.654 1.00 0.00 ? 9 TRP B CH2 1 9 +HETATM 203 N N . DLE B 1 10 ? -0.434 -8.227 7.300 1.00 0.00 ? 10 DLE B N 1 10 +HETATM 204 C CA . DLE B 1 10 ? -1.886 -8.177 7.335 1.00 0.00 ? 10 DLE B CA 1 10 +HETATM 205 C CB . DLE B 1 10 ? -2.396 -8.406 8.759 1.00 0.00 ? 10 DLE B CB 1 10 +HETATM 206 C CG . DLE B 1 10 ? -3.919 -8.552 8.765 1.00 0.00 ? 10 DLE B CG 1 10 +HETATM 207 C CD1 . DLE B 1 10 ? -4.414 -9.077 10.115 1.00 0.00 ? 10 DLE B CD1 1 10 +HETATM 208 C CD2 . DLE B 1 10 ? -4.597 -7.237 8.378 1.00 0.00 ? 10 DLE B CD2 1 10 +HETATM 209 C C . DLE B 1 10 ? -2.450 -9.168 6.314 1.00 0.00 ? 10 DLE B C 1 10 +HETATM 210 O O . DLE B 1 10 ? -2.421 -10.377 6.535 1.00 0.00 ? 10 DLE B O 1 10 + ATOM 211 N N . TRP B 1 11 ? -2.950 -8.617 5.218 1.00 0.00 ? 11 TRP B N 1 11 + ATOM 212 C CA . TRP B 1 11 ? -3.520 -9.437 4.162 1.00 0.00 ? 11 TRP B CA 1 11 + ATOM 213 C C . TRP B 1 11 ? -2.911 -8.986 2.832 1.00 0.00 ? 11 TRP B C 1 11 + ATOM 214 O O . TRP B 1 11 ? -2.441 -7.856 2.713 1.00 0.00 ? 11 TRP B O 1 11 + ATOM 215 C CB . TRP B 1 11 ? -5.049 -9.363 4.175 1.00 0.00 ? 11 TRP B CB 1 11 + ATOM 216 C CG . TRP B 1 11 ? -5.685 -9.886 5.464 1.00 0.00 ? 11 TRP B CG 1 11 + ATOM 217 C CD1 . TRP B 1 11 ? -5.430 -11.035 6.103 1.00 0.00 ? 11 TRP B CD1 1 11 + ATOM 218 C CD2 . TRP B 1 11 ? -6.685 -9.267 6.263 1.00 0.00 ? 11 TRP B CD2 1 11 + ATOM 219 N NE1 . TRP B 1 11 ? -6.203 -11.165 7.239 1.00 0.00 ? 11 TRP B NE1 1 11 + ATOM 220 C CE2 . TRP B 1 11 ? -7.000 -10.031 7.331 1.00 0.00 ? 11 TRP B CE2 1 11 + ATOM 221 C CE3 . TRP B 1 11 ? -7.326 -8.027 6.059 1.00 0.00 ? 11 TRP B CE3 1 11 + ATOM 222 C CZ2 . TRP B 1 11 ? -7.950 -9.688 8.301 1.00 0.00 ? 11 TRP B CZ2 1 11 + ATOM 223 C CZ3 . TRP B 1 11 ? -8.276 -7.685 7.028 1.00 0.00 ? 11 TRP B CZ3 1 11 + ATOM 224 C CH2 . TRP B 1 11 ? -8.600 -8.471 8.127 1.00 0.00 ? 11 TRP B CH2 1 11 +HETATM 225 N N . DLE B 1 12 ? -2.941 -9.893 1.867 1.00 0.00 ? 12 DLE B N 1 12 +HETATM 226 C CA . DLE B 1 12 ? -2.398 -9.603 0.550 1.00 0.00 ? 12 DLE B CA 1 12 +HETATM 227 C CB . DLE B 1 12 ? -3.462 -9.819 -0.527 1.00 0.00 ? 12 DLE B CB 1 12 +HETATM 228 C CG . DLE B 1 12 ? -4.741 -9.056 -0.177 1.00 0.00 ? 12 DLE B CG 1 12 +HETATM 229 C CD1 . DLE B 1 12 ? -4.559 -7.552 -0.386 1.00 0.00 ? 12 DLE B CD1 1 12 +HETATM 230 C CD2 . DLE B 1 12 ? -5.937 -9.603 -0.960 1.00 0.00 ? 12 DLE B CD2 1 12 +HETATM 231 C C . DLE B 1 12 ? -1.128 -10.429 0.334 1.00 0.00 ? 12 DLE B C 1 12 +HETATM 232 O O . DLE B 1 12 ? -1.199 -11.583 -0.085 1.00 0.00 ? 12 DLE B O 1 12 + ATOM 233 N N . TRP B 1 13 ? 0.002 -9.805 0.629 1.00 0.00 ? 13 TRP B N 1 13 + ATOM 234 C CA . TRP B 1 13 ? 1.286 -10.468 0.472 1.00 0.00 ? 13 TRP B CA 1 13 + ATOM 235 C C . TRP B 1 13 ? 2.197 -10.010 1.613 1.00 0.00 ? 13 TRP B C 1 13 + ATOM 236 O O . TRP B 1 13 ? 1.996 -8.939 2.181 1.00 0.00 ? 13 TRP B O 1 13 + ATOM 237 C CB . TRP B 1 13 ? 1.878 -10.197 -0.912 1.00 0.00 ? 13 TRP B CB 1 13 + ATOM 238 C CG . TRP B 1 13 ? 1.078 -10.811 -2.062 1.00 0.00 ? 13 TRP B CG 1 13 + ATOM 239 C CD1 . TRP B 1 13 ? 1.196 -12.040 -2.585 1.00 0.00 ? 13 TRP B CD1 1 13 + ATOM 240 C CD2 . TRP B 1 13 ? 0.038 -10.215 -2.826 1.00 0.00 ? 13 TRP B CD2 1 13 + ATOM 241 N NE1 . TRP B 1 13 ? 0.300 -12.239 -3.616 1.00 0.00 ? 13 TRP B NE1 1 13 + ATOM 242 C CE2 . TRP B 1 13 ? -0.434 -11.068 -3.761 1.00 0.00 ? 13 TRP B CE2 1 13 + ATOM 243 C CE3 . TRP B 1 13 ? -0.502 -8.918 -2.705 1.00 0.00 ? 13 TRP B CE3 1 13 + ATOM 244 C CZ2 . TRP B 1 13 ? -1.459 -10.767 -4.666 1.00 0.00 ? 13 TRP B CZ2 1 13 + ATOM 245 C CZ3 . TRP B 1 13 ? -1.528 -8.618 -3.610 1.00 0.00 ? 13 TRP B CZ3 1 13 + ATOM 246 C CH2 . TRP B 1 13 ? -2.013 -9.496 -4.572 1.00 0.00 ? 13 TRP B CH2 1 13 +HETATM 247 N N . DLE B 1 14 ? 3.182 -10.845 1.912 1.00 0.00 ? 14 DLE B N 1 14 +HETATM 248 C CA . DLE B 1 14 ? 4.124 -10.540 2.975 1.00 0.00 ? 14 DLE B CA 1 14 +HETATM 249 C CB . DLE B 1 14 ? 5.555 -10.519 2.432 1.00 0.00 ? 14 DLE B CB 1 14 +HETATM 250 C CG . DLE B 1 14 ? 5.783 -9.277 1.568 1.00 0.00 ? 14 DLE B CG 1 14 +HETATM 251 C CD1 . DLE B 1 14 ? 6.347 -8.125 2.403 1.00 0.00 ? 14 DLE B CD1 1 14 +HETATM 252 C CD2 . DLE B 1 14 ? 6.670 -9.602 0.364 1.00 0.00 ? 14 DLE B CD2 1 14 +HETATM 253 C C . DLE B 1 14 ? 3.921 -11.521 4.131 1.00 0.00 ? 14 DLE B C 1 14 +HETATM 254 O O . DLE B 1 14 ? 4.350 -12.671 4.056 1.00 0.00 ? 14 DLE B O 1 14 + ATOM 255 N N . TRP B 1 15 ? 3.265 -11.030 5.172 1.00 0.00 ? 15 TRP B N 1 15 + ATOM 256 C CA . TRP B 1 15 ? 2.998 -11.850 6.342 1.00 0.00 ? 15 TRP B CA 1 15 + ATOM 257 C C . TRP B 1 15 ? 1.587 -11.523 6.837 1.00 0.00 ? 15 TRP B C 1 15 + ATOM 258 O O . TRP B 1 15 ? 0.979 -10.552 6.391 1.00 0.00 ? 15 TRP B O 1 15 + ATOM 259 C CB . TRP B 1 15 ? 4.071 -11.642 7.414 1.00 0.00 ? 15 TRP B CB 1 15 + ATOM 260 C CG . TRP B 1 15 ? 5.496 -11.908 6.927 1.00 0.00 ? 15 TRP B CG 1 15 + ATOM 261 C CD1 . TRP B 1 15 ? 6.146 -13.079 6.875 1.00 0.00 ? 15 TRP B CD1 1 15 + ATOM 262 C CD2 . TRP B 1 15 ? 6.441 -10.972 6.424 1.00 0.00 ? 15 TRP B CD2 1 15 + ATOM 263 N NE1 . TRP B 1 15 ? 7.424 -12.927 6.376 1.00 0.00 ? 15 TRP B NE1 1 15 + ATOM 264 C CE2 . TRP B 1 15 ? 7.601 -11.578 6.091 1.00 0.00 ? 15 TRP B CE2 1 15 + ATOM 265 C CE3 . TRP B 1 15 ? 6.290 -9.580 6.254 1.00 0.00 ? 15 TRP B CE3 1 15 + ATOM 266 C CZ2 . TRP B 1 15 ? 8.721 -10.921 5.568 1.00 0.00 ? 15 TRP B CZ2 1 15 + ATOM 267 C CZ3 . TRP B 1 15 ? 7.409 -8.923 5.731 1.00 0.00 ? 15 TRP B CZ3 1 15 + ATOM 268 C CH2 . TRP B 1 15 ? 8.603 -9.548 5.388 1.00 0.00 ? 15 TRP B CH2 1 15 +HETATM 269 C CA . ETA B 1 16 ? -0.219 -12.167 8.311 1.00 0.00 ? 16 ETA B CA 1 16 +HETATM 270 N N . ETA B 1 16 ? 1.109 -12.354 7.751 1.00 0.00 ? 16 ETA B N 1 16 +HETATM 271 C CB . ETA B 1 16 ? -1.221 -13.041 7.554 1.00 0.00 ? 16 ETA B CB 1 16 +HETATM 272 O O . ETA B 1 16 ? -1.280 -12.708 6.169 1.00 0.00 ? 16 ETA B O 1 16 +HETATM 273 C C . FVA A 1 1 ? -3.645 0.293 3.490 1.00 0.00 ? 1 FVA A C 2 1 +HETATM 274 N N . FVA A 1 1 ? -2.419 -0.231 1.455 1.00 0.00 ? 1 FVA A N 2 1 +HETATM 275 O O . FVA A 1 1 ? -4.083 -0.770 3.929 1.00 0.00 ? 1 FVA A O 2 1 +HETATM 276 C CA . FVA A 1 1 ? -3.542 0.526 1.982 1.00 0.00 ? 1 FVA A CA 2 1 +HETATM 277 C CB . FVA A 1 1 ? -4.821 0.156 1.231 1.00 0.00 ? 1 FVA A CB 2 1 +HETATM 278 C CG1 . FVA A 1 1 ? -6.001 1.007 1.704 1.00 0.00 ? 1 FVA A CG1 2 1 +HETATM 279 C CG2 . FVA A 1 1 ? -4.625 0.281 -0.282 1.00 0.00 ? 1 FVA A CG2 2 1 +HETATM 280 O O1 . FVA A 1 1 ? -1.109 1.590 1.129 1.00 0.00 ? 1 FVA A O1 2 1 +HETATM 281 C CN . FVA A 1 1 ? -1.304 0.377 1.077 1.00 0.00 ? 1 FVA A CN 2 1 + ATOM 282 N N . GLY A 1 2 ? -3.234 1.302 4.243 1.00 0.00 ? 2 GLY A N 2 2 + ATOM 283 C CA . GLY A 1 2 ? -3.276 1.221 5.693 1.00 0.00 ? 2 GLY A CA 2 2 + ATOM 284 C C . GLY A 1 2 ? -2.392 2.297 6.328 1.00 0.00 ? 2 GLY A C 2 2 + ATOM 285 O O . GLY A 1 2 ? -2.843 3.419 6.556 1.00 0.00 ? 2 GLY A O 2 2 + ATOM 286 N N . ALA A 1 3 ? -1.153 1.917 6.597 1.00 0.00 ? 3 ALA A N 2 3 + ATOM 287 C CA . ALA A 1 3 ? -0.202 2.835 7.202 1.00 0.00 ? 3 ALA A CA 2 3 + ATOM 288 C C . ALA A 1 3 ? 1.202 2.524 6.684 1.00 0.00 ? 3 ALA A C 2 3 + ATOM 289 O O . ALA A 1 3 ? 1.513 1.374 6.374 1.00 0.00 ? 3 ALA A O 2 3 + ATOM 290 C CB . ALA A 1 3 ? -0.297 2.737 8.725 1.00 0.00 ? 3 ALA A CB 2 3 +HETATM 291 N N . DLE A 1 4 ? 2.014 3.568 6.604 1.00 0.00 ? 4 DLE A N 2 4 +HETATM 292 C CA . DLE A 1 4 ? 3.380 3.419 6.129 1.00 0.00 ? 4 DLE A CA 2 4 +HETATM 293 C CB . DLE A 1 4 ? 4.358 3.403 7.305 1.00 0.00 ? 4 DLE A CB 2 4 +HETATM 294 C CG . DLE A 1 4 ? 5.793 3.222 6.807 1.00 0.00 ? 4 DLE A CG 2 4 +HETATM 295 C CD1 . DLE A 1 4 ? 6.539 4.559 6.784 1.00 0.00 ? 4 DLE A CD1 2 4 +HETATM 296 C CD2 . DLE A 1 4 ? 6.532 2.167 7.632 1.00 0.00 ? 4 DLE A CD2 2 4 +HETATM 297 C C . DLE A 1 4 ? 3.678 4.510 5.098 1.00 0.00 ? 4 DLE A C 2 4 +HETATM 298 O O . DLE A 1 4 ? 3.452 5.691 5.356 1.00 0.00 ? 4 DLE A O 2 4 + ATOM 299 N N . ALA A 1 5 ? 4.180 4.074 3.952 1.00 0.00 ? 5 ALA A N 2 5 + ATOM 300 C CA . ALA A 1 5 ? 4.512 4.998 2.881 1.00 0.00 ? 5 ALA A CA 2 5 + ATOM 301 C C . ALA A 1 5 ? 3.693 4.646 1.638 1.00 0.00 ? 5 ALA A C 2 5 + ATOM 302 O O . ALA A 1 5 ? 3.752 3.519 1.150 1.00 0.00 ? 5 ALA A O 2 5 + ATOM 303 C CB . ALA A 1 5 ? 6.018 4.956 2.617 1.00 0.00 ? 5 ALA A CB 2 5 +HETATM 304 N N . DVA A 1 6 ? 2.946 5.631 1.161 1.00 0.00 ? 6 DVA A N 2 6 +HETATM 305 C CA . DVA A 1 6 ? 2.116 5.439 -0.017 1.00 0.00 ? 6 DVA A CA 2 6 +HETATM 306 C CB . DVA A 1 6 ? 2.849 5.942 -1.262 1.00 0.00 ? 6 DVA A CB 2 6 +HETATM 307 C CG1 . DVA A 1 6 ? 4.195 5.235 -1.429 1.00 0.00 ? 6 DVA A CG1 2 6 +HETATM 308 C CG2 . DVA A 1 6 ? 1.982 5.778 -2.511 1.00 0.00 ? 6 DVA A CG2 2 6 +HETATM 309 C C . DVA A 1 6 ? 0.765 6.125 0.200 1.00 0.00 ? 6 DVA A C 2 6 +HETATM 310 O O . DVA A 1 6 ? 0.653 7.341 0.057 1.00 0.00 ? 6 DVA A O 2 6 + ATOM 311 N N . VAL A 1 7 ? -0.226 5.314 0.541 1.00 0.00 ? 7 VAL A N 2 7 + ATOM 312 C CA . VAL A 1 7 ? -1.565 5.828 0.778 1.00 0.00 ? 7 VAL A CA 2 7 + ATOM 313 C C . VAL A 1 7 ? -2.076 5.303 2.121 1.00 0.00 ? 7 VAL A C 2 7 + ATOM 314 O O . VAL A 1 7 ? -1.803 4.163 2.491 1.00 0.00 ? 7 VAL A O 2 7 + ATOM 315 C CB . VAL A 1 7 ? -2.480 5.465 -0.393 1.00 0.00 ? 7 VAL A CB 2 7 + ATOM 316 C CG1 . VAL A 1 7 ? -1.968 6.079 -1.698 1.00 0.00 ? 7 VAL A CG1 2 7 + ATOM 317 C CG2 . VAL A 1 7 ? -2.630 3.948 -0.522 1.00 0.00 ? 7 VAL A CG2 2 7 +HETATM 318 N N . DVA A 1 8 ? -2.810 6.161 2.815 1.00 0.00 ? 8 DVA A N 2 8 +HETATM 319 C CA . DVA A 1 8 ? -3.363 5.798 4.108 1.00 0.00 ? 8 DVA A CA 2 8 +HETATM 320 C CB . DVA A 1 8 ? -4.874 5.593 3.993 1.00 0.00 ? 8 DVA A CB 2 8 +HETATM 321 C CG1 . DVA A 1 8 ? -5.204 4.514 2.959 1.00 0.00 ? 8 DVA A CG1 2 8 +HETATM 322 C CG2 . DVA A 1 8 ? -5.488 5.255 5.353 1.00 0.00 ? 8 DVA A CG2 2 8 +HETATM 323 C C . DVA A 1 8 ? -2.982 6.864 5.138 1.00 0.00 ? 8 DVA A C 2 8 +HETATM 324 O O . DVA A 1 8 ? -3.071 8.059 4.862 1.00 0.00 ? 8 DVA A O 2 8 + ATOM 325 N N . TRP A 1 9 ? -2.565 6.392 6.304 1.00 0.00 ? 9 TRP A N 2 9 + ATOM 326 C CA . TRP A 1 9 ? -2.170 7.290 7.375 1.00 0.00 ? 9 TRP A CA 2 9 + ATOM 327 C C . TRP A 1 9 ? -0.678 7.080 7.643 1.00 0.00 ? 9 TRP A C 2 9 + ATOM 328 O O . TRP A 1 9 ? -0.264 5.995 8.050 1.00 0.00 ? 9 TRP A O 2 9 + ATOM 329 C CB . TRP A 1 9 ? -3.036 7.075 8.618 1.00 0.00 ? 9 TRP A CB 2 9 + ATOM 330 C CG . TRP A 1 9 ? -2.637 7.943 9.814 1.00 0.00 ? 9 TRP A CG 2 9 + ATOM 331 C CD1 . TRP A 1 9 ? -3.040 9.188 10.100 1.00 0.00 ? 9 TRP A CD1 2 9 + ATOM 332 C CD2 . TRP A 1 9 ? -1.754 7.618 10.878 1.00 0.00 ? 9 TRP A CD2 2 9 + ATOM 333 N NE1 . TRP A 1 9 ? -2.465 9.651 11.266 1.00 0.00 ? 9 TRP A NE1 2 9 + ATOM 334 C CE2 . TRP A 1 9 ? -1.645 8.639 11.755 1.00 0.00 ? 9 TRP A CE2 2 9 + ATOM 335 C CE3 . TRP A 1 9 ? -1.037 6.421 11.086 1.00 0.00 ? 9 TRP A CE3 2 9 + ATOM 336 C CZ2 . TRP A 1 9 ? -0.850 8.620 12.906 1.00 0.00 ? 9 TRP A CZ2 2 9 + ATOM 337 C CZ3 . TRP A 1 9 ? -0.241 6.401 12.238 1.00 0.00 ? 9 TRP A CZ3 2 9 + ATOM 338 C CH2 . TRP A 1 9 ? -0.131 7.453 13.140 1.00 0.00 ? 9 TRP A CH2 2 9 +HETATM 339 N N . DLE A 1 10 ? 0.088 8.134 7.404 1.00 0.00 ? 10 DLE A N 2 10 +HETATM 340 C CA . DLE A 1 10 ? 1.525 8.078 7.613 1.00 0.00 ? 10 DLE A CA 2 10 +HETATM 341 C CB . DLE A 1 10 ? 1.851 8.134 9.107 1.00 0.00 ? 10 DLE A CB 2 10 +HETATM 342 C CG . DLE A 1 10 ? 3.364 8.180 9.323 1.00 0.00 ? 10 DLE A CG 2 10 +HETATM 343 C CD1 . DLE A 1 10 ? 3.700 8.512 10.779 1.00 0.00 ? 10 DLE A CD1 2 10 +HETATM 344 C CD2 . DLE A 1 10 ? 4.023 6.877 8.866 1.00 0.00 ? 10 DLE A CD2 2 10 +HETATM 345 C C . DLE A 1 10 ? 2.198 9.182 6.795 1.00 0.00 ? 10 DLE A C 2 10 +HETATM 346 O O . DLE A 1 10 ? 2.225 10.339 7.212 1.00 0.00 ? 10 DLE A O 2 10 + ATOM 347 N N . TRP A 1 11 ? 2.726 8.787 5.647 1.00 0.00 ? 11 TRP A N 2 11 + ATOM 348 C CA . TRP A 1 11 ? 3.397 9.728 4.768 1.00 0.00 ? 11 TRP A CA 2 11 + ATOM 349 C C . TRP A 1 11 ? 3.043 9.362 3.325 1.00 0.00 ? 11 TRP A C 2 11 + ATOM 350 O O . TRP A 1 11 ? 2.628 8.237 3.049 1.00 0.00 ? 11 TRP A O 2 11 + ATOM 351 C CB . TRP A 1 11 ? 4.905 9.742 5.028 1.00 0.00 ? 11 TRP A CB 2 11 + ATOM 352 C CG . TRP A 1 11 ? 5.291 10.252 6.418 1.00 0.00 ? 11 TRP A CG 2 11 + ATOM 353 C CD1 . TRP A 1 11 ? 4.921 11.396 7.010 1.00 0.00 ? 11 TRP A CD1 2 11 + ATOM 354 C CD2 . TRP A 1 11 ? 6.124 9.624 7.383 1.00 0.00 ? 11 TRP A CD2 2 11 + ATOM 355 N NE1 . TRP A 1 11 ? 5.469 11.514 8.270 1.00 0.00 ? 11 TRP A NE1 2 11 + ATOM 356 C CE2 . TRP A 1 11 ? 6.236 10.378 8.498 1.00 0.00 ? 11 TRP A CE2 2 11 + ATOM 357 C CE3 . TRP A 1 11 ? 6.792 8.386 7.289 1.00 0.00 ? 11 TRP A CE3 2 11 + ATOM 358 C CZ2 . TRP A 1 11 ? 6.990 10.025 9.624 1.00 0.00 ? 11 TRP A CZ2 2 11 + ATOM 359 C CZ3 . TRP A 1 11 ? 7.547 8.034 8.415 1.00 0.00 ? 11 TRP A CZ3 2 11 + ATOM 360 C CH2 . TRP A 1 11 ? 7.661 8.810 9.564 1.00 0.00 ? 11 TRP A CH2 2 11 +HETATM 361 N N . DLE A 1 12 ? 3.221 10.333 2.441 1.00 0.00 ? 12 DLE A N 2 12 +HETATM 362 C CA . DLE A 1 12 ? 2.926 10.128 1.033 1.00 0.00 ? 12 DLE A CA 2 12 +HETATM 363 C CB . DLE A 1 12 ? 4.132 10.510 0.171 1.00 0.00 ? 12 DLE A CB 2 12 +HETATM 364 C CG . DLE A 1 12 ? 5.409 9.872 0.724 1.00 0.00 ? 12 DLE A CG 2 12 +HETATM 365 C CD1 . DLE A 1 12 ? 5.375 8.351 0.563 1.00 0.00 ? 12 DLE A CD1 2 12 +HETATM 366 C CD2 . DLE A 1 12 ? 6.652 10.490 0.082 1.00 0.00 ? 12 DLE A CD2 2 12 +HETATM 367 C C . DLE A 1 12 ? 1.647 10.885 0.669 1.00 0.00 ? 12 DLE A C 2 12 +HETATM 368 O O . DLE A 1 12 ? 1.685 12.086 0.408 1.00 0.00 ? 12 DLE A O 2 12 + ATOM 369 N N . TRP A 1 13 ? 0.545 10.149 0.663 1.00 0.00 ? 13 TRP A N 2 13 + ATOM 370 C CA . TRP A 1 13 ? -0.744 10.736 0.334 1.00 0.00 ? 13 TRP A CA 2 13 + ATOM 371 C C . TRP A 1 13 ? -1.788 10.148 1.287 1.00 0.00 ? 13 TRP A C 2 13 + ATOM 372 O O . TRP A 1 13 ? -1.517 9.170 1.982 1.00 0.00 ? 13 TRP A O 2 13 + ATOM 373 C CB . TRP A 1 13 ? -1.087 10.515 -1.139 1.00 0.00 ? 13 TRP A CB 2 13 + ATOM 374 C CG . TRP A 1 13 ? -0.191 11.287 -2.110 1.00 0.00 ? 13 TRP A CG 2 13 + ATOM 375 C CD1 . TRP A 1 13 ? -0.359 12.532 -2.577 1.00 0.00 ? 13 TRP A CD1 2 13 + ATOM 376 C CD2 . TRP A 1 13 ? 1.014 10.853 -2.727 1.00 0.00 ? 13 TRP A CD2 2 13 + ATOM 377 N NE1 . TRP A 1 13 ? 0.658 12.892 -3.437 1.00 0.00 ? 13 TRP A NE1 2 13 + ATOM 378 C CE2 . TRP A 1 13 ? 1.529 11.812 -3.525 1.00 0.00 ? 13 TRP A CE2 2 13 + ATOM 379 C CE3 . TRP A 1 13 ? 1.668 9.611 -2.591 1.00 0.00 ? 13 TRP A CE3 2 13 + ATOM 380 C CZ2 . TRP A 1 13 ? 2.709 11.682 -4.267 1.00 0.00 ? 13 TRP A CZ2 2 13 + ATOM 381 C CZ3 . TRP A 1 13 ? 2.848 9.481 -3.333 1.00 0.00 ? 13 TRP A CZ3 2 13 + ATOM 382 C CH2 . TRP A 1 13 ? 3.377 10.468 -4.154 1.00 0.00 ? 13 TRP A CH2 2 13 +HETATM 383 N N . DLE A 1 14 ? -2.957 10.769 1.287 1.00 0.00 ? 14 DLE A N 2 14 +HETATM 384 C CA . DLE A 1 14 ? -4.043 10.318 2.142 1.00 0.00 ? 14 DLE A CA 2 14 +HETATM 385 C CB . DLE A 1 14 ? -5.360 10.284 1.363 1.00 0.00 ? 14 DLE A CB 2 14 +HETATM 386 C CG . DLE A 1 14 ? -5.294 9.241 0.246 1.00 0.00 ? 14 DLE A CG 2 14 +HETATM 387 C CD1 . DLE A 1 14 ? -5.760 7.872 0.745 1.00 0.00 ? 14 DLE A CD1 2 14 +HETATM 388 C CD2 . DLE A 1 14 ? -6.081 9.702 -0.982 1.00 0.00 ? 14 DLE A CD2 2 14 +HETATM 389 C C . DLE A 1 14 ? -4.094 11.193 3.396 1.00 0.00 ? 14 DLE A C 2 14 +HETATM 390 O O . DLE A 1 14 ? -4.790 12.207 3.422 1.00 0.00 ? 14 DLE A O 2 14 + ATOM 391 N N . TRP A 1 15 ? -3.350 10.768 4.406 1.00 0.00 ? 15 TRP A N 2 15 + ATOM 392 C CA . TRP A 1 15 ? -3.302 11.499 5.661 1.00 0.00 ? 15 TRP A CA 2 15 + ATOM 393 C C . TRP A 1 15 ? -1.867 11.440 6.188 1.00 0.00 ? 15 TRP A C 2 15 + ATOM 394 O O . TRP A 1 15 ? -1.204 10.410 6.079 1.00 0.00 ? 15 TRP A O 2 15 + ATOM 395 C CB . TRP A 1 15 ? -4.327 10.952 6.655 1.00 0.00 ? 15 TRP A CB 2 15 + ATOM 396 C CG . TRP A 1 15 ? -5.779 11.273 6.292 1.00 0.00 ? 15 TRP A CG 2 15 + ATOM 397 C CD1 . TRP A 1 15 ? -6.480 12.375 6.595 1.00 0.00 ? 15 TRP A CD1 2 15 + ATOM 398 C CD2 . TRP A 1 15 ? -6.697 10.477 5.557 1.00 0.00 ? 15 TRP A CD2 2 15 + ATOM 399 N NE1 . TRP A 1 15 ? -7.765 12.309 6.096 1.00 0.00 ? 15 TRP A NE1 2 15 + ATOM 400 C CE2 . TRP A 1 15 ? -7.892 11.092 5.435 1.00 0.00 ? 15 TRP A CE2 2 15 + ATOM 401 C CE3 . TRP A 1 15 ? -6.490 9.203 4.988 1.00 0.00 ? 15 TRP A CE3 2 15 + ATOM 402 C CZ2 . TRP A 1 15 ? -8.998 10.557 4.764 1.00 0.00 ? 15 TRP A CZ2 2 15 + ATOM 403 C CZ3 . TRP A 1 15 ? -7.596 8.668 4.316 1.00 0.00 ? 15 TRP A CZ3 2 15 + ATOM 404 C CH2 . TRP A 1 15 ? -8.827 9.303 4.192 1.00 0.00 ? 15 TRP A CH2 2 15 +HETATM 405 C CA . ETA A 1 16 ? -0.085 12.646 7.294 1.00 0.00 ? 16 ETA A CA 2 16 +HETATM 406 N N . ETA A 1 16 ? -1.429 12.558 6.749 1.00 0.00 ? 16 ETA A N 2 16 +HETATM 407 C CB . ETA A 1 16 ? -0.035 11.935 8.648 1.00 0.00 ? 16 ETA A CB 2 16 +HETATM 408 O O . ETA A 1 16 ? 1.292 11.855 9.160 1.00 0.00 ? 16 ETA A O 2 16 +HETATM 409 C C . FVA B 1 1 ? 3.603 -0.314 3.469 1.00 0.00 ? 1 FVA B C 2 1 +HETATM 410 N N . FVA B 1 1 ? 2.365 0.209 1.441 1.00 0.00 ? 1 FVA B N 2 1 +HETATM 411 O O . FVA B 1 1 ? 4.044 0.749 3.905 1.00 0.00 ? 1 FVA B O 2 1 +HETATM 412 C CA . FVA B 1 1 ? 3.491 -0.548 1.962 1.00 0.00 ? 1 FVA B CA 2 1 +HETATM 413 C CB . FVA B 1 1 ? 4.767 -0.178 1.203 1.00 0.00 ? 1 FVA B CB 2 1 +HETATM 414 C CG1 . FVA B 1 1 ? 5.949 -1.029 1.670 1.00 0.00 ? 1 FVA B CG1 2 1 +HETATM 415 C CG2 . FVA B 1 1 ? 4.562 -0.304 -0.308 1.00 0.00 ? 1 FVA B CG2 2 1 +HETATM 416 O O1 . FVA B 1 1 ? 1.053 -1.612 1.123 1.00 0.00 ? 1 FVA B O1 2 1 +HETATM 417 C CN . FVA B 1 1 ? 1.248 -0.399 1.069 1.00 0.00 ? 1 FVA B CN 2 1 + ATOM 418 N N . GLY B 1 2 ? 3.196 -1.323 4.225 1.00 0.00 ? 2 GLY B N 2 2 + ATOM 419 C CA . GLY B 1 2 ? 3.247 -1.241 5.675 1.00 0.00 ? 2 GLY B CA 2 2 + ATOM 420 C C . GLY B 1 2 ? 2.367 -2.317 6.316 1.00 0.00 ? 2 GLY B C 2 2 + ATOM 421 O O . GLY B 1 2 ? 2.819 -3.438 6.542 1.00 0.00 ? 2 GLY B O 2 2 + ATOM 422 N N . ALA B 1 3 ? 1.129 -1.937 6.591 1.00 0.00 ? 3 ALA B N 2 3 + ATOM 423 C CA . ALA B 1 3 ? 0.182 -2.855 7.202 1.00 0.00 ? 3 ALA B CA 2 3 + ATOM 424 C C . ALA B 1 3 ? -1.225 -2.543 6.692 1.00 0.00 ? 3 ALA B C 2 3 + ATOM 425 O O . ALA B 1 3 ? -1.538 -1.394 6.384 1.00 0.00 ? 3 ALA B O 2 3 + ATOM 426 C CB . ALA B 1 3 ? 0.286 -2.756 8.725 1.00 0.00 ? 3 ALA B CB 2 3 +HETATM 427 N N . DLE B 1 4 ? -2.038 -3.587 6.618 1.00 0.00 ? 4 DLE B N 2 4 +HETATM 428 C CA . DLE B 1 4 ? -3.407 -3.439 6.151 1.00 0.00 ? 4 DLE B CA 2 4 +HETATM 429 C CB . DLE B 1 4 ? -4.377 -3.423 7.332 1.00 0.00 ? 4 DLE B CB 2 4 +HETATM 430 C CG . DLE B 1 4 ? -5.816 -3.242 6.842 1.00 0.00 ? 4 DLE B CG 2 4 +HETATM 431 C CD1 . DLE B 1 4 ? -6.561 -4.579 6.824 1.00 0.00 ? 4 DLE B CD1 2 4 +HETATM 432 C CD2 . DLE B 1 4 ? -6.550 -2.186 7.671 1.00 0.00 ? 4 DLE B CD2 2 4 +HETATM 433 C C . DLE B 1 4 ? -3.710 -4.530 5.122 1.00 0.00 ? 4 DLE B C 2 4 +HETATM 434 O O . DLE B 1 4 ? -3.483 -5.711 5.378 1.00 0.00 ? 4 DLE B O 2 4 + ATOM 435 N N . ALA B 1 5 ? -4.219 -4.095 3.978 1.00 0.00 ? 5 ALA B N 2 5 + ATOM 436 C CA . ALA B 1 5 ? -4.557 -5.020 2.909 1.00 0.00 ? 5 ALA B CA 2 5 + ATOM 437 C C . ALA B 1 5 ? -3.745 -4.668 1.662 1.00 0.00 ? 5 ALA B C 2 5 + ATOM 438 O O . ALA B 1 5 ? -3.807 -3.541 1.174 1.00 0.00 ? 5 ALA B O 2 5 + ATOM 439 C CB . ALA B 1 5 ? -6.065 -4.978 2.655 1.00 0.00 ? 5 ALA B CB 2 5 +HETATM 440 N N . DVA B 1 6 ? -3.001 -5.653 1.181 1.00 0.00 ? 6 DVA B N 2 6 +HETATM 441 C CA . DVA B 1 6 ? -2.178 -5.462 -0.002 1.00 0.00 ? 6 DVA B CA 2 6 +HETATM 442 C CB . DVA B 1 6 ? -2.919 -5.966 -1.242 1.00 0.00 ? 6 DVA B CB 2 6 +HETATM 443 C CG1 . DVA B 1 6 ? -4.265 -5.258 -1.402 1.00 0.00 ? 6 DVA B CG1 2 6 +HETATM 444 C CG2 . DVA B 1 6 ? -2.059 -5.802 -2.497 1.00 0.00 ? 6 DVA B CG2 2 6 +HETATM 445 C C . DVA B 1 6 ? -0.826 -6.147 0.208 1.00 0.00 ? 6 DVA B C 2 6 +HETATM 446 O O . DVA B 1 6 ? -0.714 -7.364 0.065 1.00 0.00 ? 6 DVA B O 2 6 + ATOM 447 N N . VAL B 1 7 ? 0.167 -5.337 0.543 1.00 0.00 ? 7 VAL B N 2 7 + ATOM 448 C CA . VAL B 1 7 ? 1.507 -5.850 0.772 1.00 0.00 ? 7 VAL B CA 2 7 + ATOM 449 C C . VAL B 1 7 ? 2.026 -5.325 2.111 1.00 0.00 ? 7 VAL B C 2 7 + ATOM 450 O O . VAL B 1 7 ? 1.755 -4.184 2.483 1.00 0.00 ? 7 VAL B O 2 7 + ATOM 451 C CB . VAL B 1 7 ? 2.415 -5.488 -0.405 1.00 0.00 ? 7 VAL B CB 2 7 + ATOM 452 C CG1 . VAL B 1 7 ? 1.896 -6.102 -1.706 1.00 0.00 ? 7 VAL B CG1 2 7 + ATOM 453 C CG2 . VAL B 1 7 ? 2.565 -3.971 -0.536 1.00 0.00 ? 7 VAL B CG2 2 7 +HETATM 454 N N . DVA B 1 8 ? 2.764 -6.183 2.801 1.00 0.00 ? 8 DVA B N 2 8 +HETATM 455 C CA . DVA B 1 8 ? 3.325 -5.819 4.092 1.00 0.00 ? 8 DVA B CA 2 8 +HETATM 456 C CB . DVA B 1 8 ? 4.836 -5.614 3.967 1.00 0.00 ? 8 DVA B CB 2 8 +HETATM 457 C CG1 . DVA B 1 8 ? 5.159 -4.535 2.931 1.00 0.00 ? 8 DVA B CG1 2 8 +HETATM 458 C CG2 . DVA B 1 8 ? 5.457 -5.275 5.323 1.00 0.00 ? 8 DVA B CG2 2 8 +HETATM 459 C C . DVA B 1 8 ? 2.950 -6.885 5.124 1.00 0.00 ? 8 DVA B C 2 8 +HETATM 460 O O . DVA B 1 8 ? 3.037 -8.079 4.848 1.00 0.00 ? 8 DVA B O 2 8 + ATOM 461 N N . TRP B 1 9 ? 2.539 -6.412 6.292 1.00 0.00 ? 9 TRP B N 2 9 + ATOM 462 C CA . TRP B 1 9 ? 2.151 -7.309 7.366 1.00 0.00 ? 9 TRP B CA 2 9 + ATOM 463 C C . TRP B 1 9 ? 0.661 -7.099 7.643 1.00 0.00 ? 9 TRP B C 2 9 + ATOM 464 O O . TRP B 1 9 ? 0.248 -6.014 8.052 1.00 0.00 ? 9 TRP B O 2 9 + ATOM 465 C CB . TRP B 1 9 ? 3.024 -7.094 8.604 1.00 0.00 ? 9 TRP B CB 2 9 + ATOM 466 C CG . TRP B 1 9 ? 2.632 -7.961 9.802 1.00 0.00 ? 9 TRP B CG 2 9 + ATOM 467 C CD1 . TRP B 1 9 ? 3.036 -9.206 10.086 1.00 0.00 ? 9 TRP B CD1 2 9 + ATOM 468 C CD2 . TRP B 1 9 ? 1.755 -7.635 10.872 1.00 0.00 ? 9 TRP B CD2 2 9 + ATOM 469 N NE1 . TRP B 1 9 ? 2.468 -9.669 11.256 1.00 0.00 ? 9 TRP B NE1 2 9 + ATOM 470 C CE2 . TRP B 1 9 ? 1.652 -8.656 11.749 1.00 0.00 ? 9 TRP B CE2 2 9 + ATOM 471 C CE3 . TRP B 1 9 ? 1.039 -6.439 11.083 1.00 0.00 ? 9 TRP B CE3 2 9 + ATOM 472 C CZ2 . TRP B 1 9 ? 0.863 -8.636 12.905 1.00 0.00 ? 9 TRP B CZ2 2 9 + ATOM 473 C CZ3 . TRP B 1 9 ? 0.250 -6.418 12.240 1.00 0.00 ? 9 TRP B CZ3 2 9 + ATOM 474 C CH2 . TRP B 1 9 ? 0.145 -7.470 13.142 1.00 0.00 ? 9 TRP B CH2 2 9 +HETATM 475 N N . DLE B 1 10 ? -0.107 -8.154 7.408 1.00 0.00 ? 10 DLE B N 2 10 +HETATM 476 C CA . DLE B 1 10 ? -1.543 -8.098 7.625 1.00 0.00 ? 10 DLE B CA 2 10 +HETATM 477 C CB . DLE B 1 10 ? -1.860 -8.153 9.121 1.00 0.00 ? 10 DLE B CB 2 10 +HETATM 478 C CG . DLE B 1 10 ? -3.372 -8.199 9.347 1.00 0.00 ? 10 DLE B CG 2 10 +HETATM 479 C CD1 . DLE B 1 10 ? -3.700 -8.530 10.805 1.00 0.00 ? 10 DLE B CD1 2 10 +HETATM 480 C CD2 . DLE B 1 10 ? -4.034 -6.896 8.892 1.00 0.00 ? 10 DLE B CD2 2 10 +HETATM 481 C C . DLE B 1 10 ? -2.221 -9.202 6.812 1.00 0.00 ? 10 DLE B C 2 10 +HETATM 482 O O . DLE B 1 10 ? -2.245 -10.359 7.230 1.00 0.00 ? 10 DLE B O 2 10 + ATOM 483 N N . TRP B 1 11 ? -2.755 -8.807 5.667 1.00 0.00 ? 11 TRP B N 2 11 + ATOM 484 C CA . TRP B 1 11 ? -3.432 -9.749 4.792 1.00 0.00 ? 11 TRP B CA 2 11 + ATOM 485 C C . TRP B 1 11 ? -3.086 -9.383 3.347 1.00 0.00 ? 11 TRP B C 2 11 + ATOM 486 O O . TRP B 1 11 ? -2.673 -8.259 3.069 1.00 0.00 ? 11 TRP B O 2 11 + ATOM 487 C CB . TRP B 1 11 ? -4.938 -9.763 5.061 1.00 0.00 ? 11 TRP B CB 2 11 + ATOM 488 C CG . TRP B 1 11 ? -5.316 -10.272 6.454 1.00 0.00 ? 11 TRP B CG 2 11 + ATOM 489 C CD1 . TRP B 1 11 ? -4.942 -11.416 7.044 1.00 0.00 ? 11 TRP B CD1 2 11 + ATOM 490 C CD2 . TRP B 1 11 ? -6.143 -9.643 7.423 1.00 0.00 ? 11 TRP B CD2 2 11 + ATOM 491 N NE1 . TRP B 1 11 ? -5.483 -11.533 8.307 1.00 0.00 ? 11 TRP B NE1 2 11 + ATOM 492 C CE2 . TRP B 1 11 ? -6.248 -10.396 8.539 1.00 0.00 ? 11 TRP B CE2 2 11 + ATOM 493 C CE3 . TRP B 1 11 ? -6.812 -8.405 7.333 1.00 0.00 ? 11 TRP B CE3 2 11 + ATOM 494 C CZ2 . TRP B 1 11 ? -6.996 -10.044 9.670 1.00 0.00 ? 11 TRP B CZ2 2 11 + ATOM 495 C CZ3 . TRP B 1 11 ? -7.560 -8.053 8.463 1.00 0.00 ? 11 TRP B CZ3 2 11 + ATOM 496 C CH2 . TRP B 1 11 ? -7.667 -8.828 9.612 1.00 0.00 ? 11 TRP B CH2 2 11 +HETATM 497 N N . DLE B 1 12 ? -3.269 -10.355 2.465 1.00 0.00 ? 12 DLE B N 2 12 +HETATM 498 C CA . DLE B 1 12 ? -2.982 -10.150 1.055 1.00 0.00 ? 12 DLE B CA 2 12 +HETATM 499 C CB . DLE B 1 12 ? -4.192 -10.532 0.200 1.00 0.00 ? 12 DLE B CB 2 12 +HETATM 500 C CG . DLE B 1 12 ? -5.466 -9.894 0.760 1.00 0.00 ? 12 DLE B CG 2 12 +HETATM 501 C CD1 . DLE B 1 12 ? -5.433 -8.374 0.599 1.00 0.00 ? 12 DLE B CD1 2 12 +HETATM 502 C CD2 . DLE B 1 12 ? -6.713 -10.513 0.126 1.00 0.00 ? 12 DLE B CD2 2 12 +HETATM 503 C C . DLE B 1 12 ? -1.705 -10.907 0.683 1.00 0.00 ? 12 DLE B C 2 12 +HETATM 504 O O . DLE B 1 12 ? -1.745 -12.109 0.424 1.00 0.00 ? 12 DLE B O 2 12 + ATOM 505 N N . TRP B 1 13 ? -0.603 -10.172 0.671 1.00 0.00 ? 13 TRP B N 2 13 + ATOM 506 C CA . TRP B 1 13 ? 0.684 -10.759 0.335 1.00 0.00 ? 13 TRP B CA 2 13 + ATOM 507 C C . TRP B 1 13 ? 1.733 -10.170 1.281 1.00 0.00 ? 13 TRP B C 2 13 + ATOM 508 O O . TRP B 1 13 ? 1.466 -9.192 1.978 1.00 0.00 ? 13 TRP B O 2 13 + ATOM 509 C CB . TRP B 1 13 ? 1.018 -10.538 -1.141 1.00 0.00 ? 13 TRP B CB 2 13 + ATOM 510 C CG . TRP B 1 13 ? 0.117 -11.311 -2.106 1.00 0.00 ? 13 TRP B CG 2 13 + ATOM 511 C CD1 . TRP B 1 13 ? 0.283 -12.556 -2.573 1.00 0.00 ? 13 TRP B CD1 2 13 + ATOM 512 C CD2 . TRP B 1 13 ? -1.092 -10.878 -2.715 1.00 0.00 ? 13 TRP B CD2 2 13 + ATOM 513 N NE1 . TRP B 1 13 ? -0.739 -12.916 -3.427 1.00 0.00 ? 13 TRP B NE1 2 13 + ATOM 514 C CE2 . TRP B 1 13 ? -1.611 -11.837 -3.511 1.00 0.00 ? 13 TRP B CE2 2 13 + ATOM 515 C CE3 . TRP B 1 13 ? -1.745 -9.635 -2.577 1.00 0.00 ? 13 TRP B CE3 2 13 + ATOM 516 C CZ2 . TRP B 1 13 ? -2.796 -11.707 -4.245 1.00 0.00 ? 13 TRP B CZ2 2 13 + ATOM 517 C CZ3 . TRP B 1 13 ? -2.930 -9.505 -3.311 1.00 0.00 ? 13 TRP B CZ3 2 13 + ATOM 518 C CH2 . TRP B 1 13 ? -3.463 -10.493 -4.130 1.00 0.00 ? 13 TRP B CH2 2 13 +HETATM 519 N N . DLE B 1 14 ? 2.903 -10.791 1.275 1.00 0.00 ? 14 DLE B N 2 14 +HETATM 520 C CA . DLE B 1 14 ? 3.993 -10.340 2.124 1.00 0.00 ? 14 DLE B CA 2 14 +HETATM 521 C CB . DLE B 1 14 ? 5.305 -10.306 1.337 1.00 0.00 ? 14 DLE B CB 2 14 +HETATM 522 C CG . DLE B 1 14 ? 5.233 -9.264 0.220 1.00 0.00 ? 14 DLE B CG 2 14 +HETATM 523 C CD1 . DLE B 1 14 ? 5.702 -7.894 0.715 1.00 0.00 ? 14 DLE B CD1 2 14 +HETATM 524 C CD2 . DLE B 1 14 ? 6.013 -9.725 -1.012 1.00 0.00 ? 14 DLE B CD2 2 14 +HETATM 525 C C . DLE B 1 14 ? 4.052 -11.214 3.378 1.00 0.00 ? 14 DLE B C 2 14 +HETATM 526 O O . DLE B 1 14 ? 4.748 -12.229 3.400 1.00 0.00 ? 14 DLE B O 2 14 + ATOM 527 N N . TRP B 1 15 ? 3.313 -10.789 4.392 1.00 0.00 ? 15 TRP B N 2 15 + ATOM 528 C CA . TRP B 1 15 ? 3.272 -11.520 5.647 1.00 0.00 ? 15 TRP B CA 2 15 + ATOM 529 C C . TRP B 1 15 ? 1.841 -11.460 6.182 1.00 0.00 ? 15 TRP B C 2 15 + ATOM 530 O O . TRP B 1 15 ? 1.177 -10.430 6.077 1.00 0.00 ? 15 TRP B O 2 15 + ATOM 531 C CB . TRP B 1 15 ? 4.304 -10.972 6.635 1.00 0.00 ? 15 TRP B CB 2 15 + ATOM 532 C CG . TRP B 1 15 ? 5.753 -11.293 6.264 1.00 0.00 ? 15 TRP B CG 2 15 + ATOM 533 C CD1 . TRP B 1 15 ? 6.456 -12.395 6.563 1.00 0.00 ? 15 TRP B CD1 2 15 + ATOM 534 C CD2 . TRP B 1 15 ? 6.667 -10.497 5.523 1.00 0.00 ? 15 TRP B CD2 2 15 + ATOM 535 N NE1 . TRP B 1 15 ? 7.739 -12.329 6.057 1.00 0.00 ? 15 TRP B NE1 2 15 + ATOM 536 C CE2 . TRP B 1 15 ? 7.861 -11.113 5.395 1.00 0.00 ? 15 TRP B CE2 2 15 + ATOM 537 C CE3 . TRP B 1 15 ? 6.457 -9.223 4.954 1.00 0.00 ? 15 TRP B CE3 2 15 + ATOM 538 C CZ2 . TRP B 1 15 ? 8.964 -10.578 4.717 1.00 0.00 ? 15 TRP B CZ2 2 15 + ATOM 539 C CZ3 . TRP B 1 15 ? 7.559 -8.688 4.276 1.00 0.00 ? 15 TRP B CZ3 2 15 + ATOM 540 C CH2 . TRP B 1 15 ? 8.789 -9.324 4.146 1.00 0.00 ? 15 TRP B CH2 2 15 +HETATM 541 C CA . ETA B 1 16 ? 0.065 -12.665 7.300 1.00 0.00 ? 16 ETA B CA 2 16 +HETATM 542 N N . ETA B 1 16 ? 1.406 -12.578 6.747 1.00 0.00 ? 16 ETA B N 2 16 +HETATM 543 C CB . ETA B 1 16 ? 0.023 -11.954 8.654 1.00 0.00 ? 16 ETA B CB 2 16 +HETATM 544 O O . ETA B 1 16 ? -1.301 -11.873 9.173 1.00 0.00 ? 16 ETA B O 2 16 +HETATM 545 C C . FVA A 1 1 ? -3.599 0.106 3.491 1.00 0.00 ? 1 FVA A C 3 1 +HETATM 546 N N . FVA A 1 1 ? -2.333 -0.167 1.431 1.00 0.00 ? 1 FVA A N 3 1 +HETATM 547 O O . FVA A 1 1 ? -3.932 -1.030 3.827 1.00 0.00 ? 1 FVA A O 3 1 +HETATM 548 C CA . FVA A 1 1 ? -3.498 0.480 2.011 1.00 0.00 ? 1 FVA A CA 3 1 +HETATM 549 C CB . FVA A 1 1 ? -4.752 0.112 1.215 1.00 0.00 ? 1 FVA A CB 3 1 +HETATM 550 C CG1 . FVA A 1 1 ? -5.971 0.884 1.722 1.00 0.00 ? 1 FVA A CG1 3 1 +HETATM 551 C CG2 . FVA A 1 1 ? -4.539 0.346 -0.283 1.00 0.00 ? 1 FVA A CG2 3 1 +HETATM 552 O O1 . FVA A 1 1 ? -1.424 1.759 0.655 1.00 0.00 ? 1 FVA A O1 3 1 +HETATM 553 C CN . FVA A 1 1 ? -1.402 0.538 0.805 1.00 0.00 ? 1 FVA A CN 3 1 + ATOM 554 N N . GLY A 1 2 ? -3.307 1.084 4.336 1.00 0.00 ? 2 GLY A N 3 2 + ATOM 555 C CA . GLY A 1 2 ? -3.361 0.873 5.773 1.00 0.00 ? 2 GLY A CA 3 2 + ATOM 556 C C . GLY A 1 2 ? -2.490 1.891 6.510 1.00 0.00 ? 2 GLY A C 3 2 + ATOM 557 O O . GLY A 1 2 ? -2.989 2.907 6.993 1.00 0.00 ? 2 GLY A O 3 2 + ATOM 558 N N . ALA A 1 3 ? -1.202 1.584 6.574 1.00 0.00 ? 3 ALA A N 3 3 + ATOM 559 C CA . ALA A 1 3 ? -0.257 2.460 7.245 1.00 0.00 ? 3 ALA A CA 3 3 + ATOM 560 C C . ALA A 1 3 ? 1.139 2.245 6.654 1.00 0.00 ? 3 ALA A C 3 3 + ATOM 561 O O . ALA A 1 3 ? 1.506 1.122 6.313 1.00 0.00 ? 3 ALA A O 3 3 + ATOM 562 C CB . ALA A 1 3 ? -0.298 2.200 8.751 1.00 0.00 ? 3 ALA A CB 3 3 +HETATM 563 N N . DLE A 1 4 ? 1.878 3.340 6.552 1.00 0.00 ? 4 DLE A N 3 4 +HETATM 564 C CA . DLE A 1 4 ? 3.224 3.284 6.008 1.00 0.00 ? 4 DLE A CA 3 4 +HETATM 565 C CB . DLE A 1 4 ? 4.257 3.527 7.111 1.00 0.00 ? 4 DLE A CB 3 4 +HETATM 566 C CG . DLE A 1 4 ? 5.670 3.247 6.593 1.00 0.00 ? 4 DLE A CG 3 4 +HETATM 567 C CD1 . DLE A 1 4 ? 6.385 4.549 6.224 1.00 0.00 ? 4 DLE A CD1 3 4 +HETATM 568 C CD2 . DLE A 1 4 ? 6.470 2.419 7.600 1.00 0.00 ? 4 DLE A CD2 3 4 +HETATM 569 C C . DLE A 1 4 ? 3.337 4.261 4.837 1.00 0.00 ? 4 DLE A C 3 4 +HETATM 570 O O . DLE A 1 4 ? 2.979 5.430 4.961 1.00 0.00 ? 4 DLE A O 3 4 + ATOM 571 N N . ALA A 1 5 ? 3.838 3.745 3.723 1.00 0.00 ? 5 ALA A N 3 5 + ATOM 572 C CA . ALA A 1 5 ? 4.003 4.556 2.529 1.00 0.00 ? 5 ALA A CA 3 5 + ATOM 573 C C . ALA A 1 5 ? 3.008 4.095 1.463 1.00 0.00 ? 5 ALA A C 3 5 + ATOM 574 O O . ALA A 1 5 ? 3.005 2.927 1.075 1.00 0.00 ? 5 ALA A O 3 5 + ATOM 575 C CB . ALA A 1 5 ? 5.453 4.469 2.050 1.00 0.00 ? 5 ALA A CB 3 5 +HETATM 576 N N . DVA A 1 6 ? 2.187 5.036 1.020 1.00 0.00 ? 6 DVA A N 3 6 +HETATM 577 C CA . DVA A 1 6 ? 1.188 4.741 0.005 1.00 0.00 ? 6 DVA A CA 3 6 +HETATM 578 C CB . DVA A 1 6 ? 1.704 5.156 -1.374 1.00 0.00 ? 6 DVA A CB 3 6 +HETATM 579 C CG1 . DVA A 1 6 ? 3.046 4.487 -1.681 1.00 0.00 ? 6 DVA A CG1 3 6 +HETATM 580 C CG2 . DVA A 1 6 ? 0.674 4.846 -2.461 1.00 0.00 ? 6 DVA A CG2 3 6 +HETATM 581 C C . DVA A 1 6 ? -0.129 5.421 0.380 1.00 0.00 ? 6 DVA A C 3 6 +HETATM 582 O O . DVA A 1 6 ? -0.249 6.642 0.293 1.00 0.00 ? 6 DVA A O 3 6 + ATOM 583 N N . VAL A 1 7 ? -1.087 4.601 0.789 1.00 0.00 ? 7 VAL A N 3 7 + ATOM 584 C CA . VAL A 1 7 ? -2.392 5.108 1.177 1.00 0.00 ? 7 VAL A CA 3 7 + ATOM 585 C C . VAL A 1 7 ? -2.671 4.727 2.632 1.00 0.00 ? 7 VAL A C 3 7 + ATOM 586 O O . VAL A 1 7 ? -2.439 3.588 3.034 1.00 0.00 ? 7 VAL A O 3 7 + ATOM 587 C CB . VAL A 1 7 ? -3.461 4.596 0.209 1.00 0.00 ? 7 VAL A CB 3 7 + ATOM 588 C CG1 . VAL A 1 7 ? -4.778 5.352 0.398 1.00 0.00 ? 7 VAL A CG1 3 7 + ATOM 589 C CG2 . VAL A 1 7 ? -2.979 4.686 -1.239 1.00 0.00 ? 7 VAL A CG2 3 7 +HETATM 590 N N . DVA A 1 8 ? -3.164 5.702 3.382 1.00 0.00 ? 8 DVA A N 3 8 +HETATM 591 C CA . DVA A 1 8 ? -3.476 5.483 4.784 1.00 0.00 ? 8 DVA A CA 3 8 +HETATM 592 C CB . DVA A 1 8 ? -4.988 5.335 4.966 1.00 0.00 ? 8 DVA A CB 3 8 +HETATM 593 C CG1 . DVA A 1 8 ? -5.487 4.020 4.363 1.00 0.00 ? 8 DVA A CG1 3 8 +HETATM 594 C CG2 . DVA A 1 8 ? -5.375 5.444 6.442 1.00 0.00 ? 8 DVA A CG2 3 8 +HETATM 595 C C . DVA A 1 8 ? -2.882 6.619 5.618 1.00 0.00 ? 8 DVA A C 3 8 +HETATM 596 O O . DVA A 1 8 ? -3.000 7.788 5.254 1.00 0.00 ? 8 DVA A O 3 8 + ATOM 597 N N . TRP A 1 9 ? -2.256 6.237 6.721 1.00 0.00 ? 9 TRP A N 3 9 + ATOM 598 C CA . TRP A 1 9 ? -1.643 7.209 7.610 1.00 0.00 ? 9 TRP A CA 3 9 + ATOM 599 C C . TRP A 1 9 ? -0.128 6.999 7.568 1.00 0.00 ? 9 TRP A C 3 9 + ATOM 600 O O . TRP A 1 9 ? 0.345 5.863 7.558 1.00 0.00 ? 9 TRP A O 3 9 + ATOM 601 C CB . TRP A 1 9 ? -2.223 7.103 9.022 1.00 0.00 ? 9 TRP A CB 3 9 + ATOM 602 C CG . TRP A 1 9 ? -1.450 7.902 10.074 1.00 0.00 ? 9 TRP A CG 3 9 + ATOM 603 C CD1 . TRP A 1 9 ? -1.634 9.177 10.439 1.00 0.00 ? 9 TRP A CD1 3 9 + ATOM 604 C CD2 . TRP A 1 9 ? -0.371 7.466 10.890 1.00 0.00 ? 9 TRP A CD2 3 9 + ATOM 605 N NE1 . TRP A 1 9 ? -0.744 9.555 11.423 1.00 0.00 ? 9 TRP A NE1 3 9 + ATOM 606 C CE2 . TRP A 1 9 ? 0.058 8.455 11.703 1.00 0.00 ? 9 TRP A CE2 3 9 + ATOM 607 C CE3 . TRP A 1 9 ? 0.242 6.197 10.924 1.00 0.00 ? 9 TRP A CE3 3 9 + ATOM 608 C CZ2 . TRP A 1 9 ? 1.107 8.330 12.622 1.00 0.00 ? 9 TRP A CZ2 3 9 + ATOM 609 C CZ3 . TRP A 1 9 ? 1.292 6.072 11.843 1.00 0.00 ? 9 TRP A CZ3 3 9 + ATOM 610 C CH2 . TRP A 1 9 ? 1.734 7.090 12.680 1.00 0.00 ? 9 TRP A CH2 3 9 +HETATM 611 N N . DLE A 1 10 ? 0.591 8.111 7.545 1.00 0.00 ? 10 DLE A N 3 10 +HETATM 612 C CA . DLE A 1 10 ? 2.043 8.064 7.505 1.00 0.00 ? 10 DLE A CA 3 10 +HETATM 613 C CB . DLE A 1 10 ? 2.626 8.334 8.893 1.00 0.00 ? 10 DLE A CB 3 10 +HETATM 614 C CG . DLE A 1 10 ? 4.151 8.442 8.821 1.00 0.00 ? 10 DLE A CG 3 10 +HETATM 615 C CD1 . DLE A 1 10 ? 4.726 8.965 10.139 1.00 0.00 ? 10 DLE A CD1 3 10 +HETATM 616 C CD2 . DLE A 1 10 ? 4.776 7.107 8.411 1.00 0.00 ? 10 DLE A CD2 3 10 +HETATM 617 C C . DLE A 1 10 ? 2.550 9.024 6.427 1.00 0.00 ? 10 DLE A C 3 10 +HETATM 618 O O . DLE A 1 10 ? 2.672 10.225 6.668 1.00 0.00 ? 10 DLE A O 3 10 + ATOM 619 N N . TRP A 1 11 ? 2.833 8.460 5.262 1.00 0.00 ? 11 TRP A N 3 11 + ATOM 620 C CA . TRP A 1 11 ? 3.325 9.251 4.147 1.00 0.00 ? 11 TRP A CA 3 11 + ATOM 621 C C . TRP A 1 11 ? 2.648 8.744 2.872 1.00 0.00 ? 11 TRP A C 3 11 + ATOM 622 O O . TRP A 1 11 ? 2.073 7.656 2.862 1.00 0.00 ? 11 TRP A O 3 11 + ATOM 623 C CB . TRP A 1 11 ? 4.852 9.203 4.072 1.00 0.00 ? 11 TRP A CB 3 11 + ATOM 624 C CG . TRP A 1 11 ? 5.555 9.816 5.286 1.00 0.00 ? 11 TRP A CG 3 11 + ATOM 625 C CD1 . TRP A 1 11 ? 5.339 11.013 5.846 1.00 0.00 ? 11 TRP A CD1 3 11 + ATOM 626 C CD2 . TRP A 1 11 ? 6.587 9.248 6.082 1.00 0.00 ? 11 TRP A CD2 3 11 + ATOM 627 N NE1 . TRP A 1 11 ? 6.166 11.222 6.930 1.00 0.00 ? 11 TRP A NE1 3 11 + ATOM 628 C CE2 . TRP A 1 11 ? 6.959 10.088 7.071 1.00 0.00 ? 11 TRP A CE2 3 11 + ATOM 629 C CE3 . TRP A 1 11 ? 7.207 7.990 5.942 1.00 0.00 ? 11 TRP A CE3 3 11 + ATOM 630 C CZ2 . TRP A 1 11 ? 7.953 9.811 8.018 1.00 0.00 ? 11 TRP A CZ2 3 11 + ATOM 631 C CZ3 . TRP A 1 11 ? 8.201 7.713 6.889 1.00 0.00 ? 11 TRP A CZ3 3 11 + ATOM 632 C CH2 . TRP A 1 11 ? 8.584 8.577 7.907 1.00 0.00 ? 11 TRP A CH2 3 11 +HETATM 633 N N . DLE A 1 12 ? 2.737 9.557 1.829 1.00 0.00 ? 12 DLE A N 3 12 +HETATM 634 C CA . DLE A 1 12 ? 2.140 9.203 0.553 1.00 0.00 ? 12 DLE A CA 3 12 +HETATM 635 C CB . DLE A 1 12 ? 3.171 9.320 -0.571 1.00 0.00 ? 12 DLE A CB 3 12 +HETATM 636 C CG . DLE A 1 12 ? 4.130 8.128 -0.545 1.00 0.00 ? 12 DLE A CG 3 12 +HETATM 637 C CD1 . DLE A 1 12 ? 4.740 7.884 -1.927 1.00 0.00 ? 12 DLE A CD1 3 12 +HETATM 638 C CD2 . DLE A 1 12 ? 5.203 8.313 0.530 1.00 0.00 ? 12 DLE A CD2 3 12 +HETATM 639 C C . DLE A 1 12 ? 0.888 10.054 0.328 1.00 0.00 ? 12 DLE A C 3 12 +HETATM 640 O O . DLE A 1 12 ? 0.982 11.204 -0.098 1.00 0.00 ? 12 DLE A O 3 12 + ATOM 641 N N . TRP A 1 13 ? -0.256 9.454 0.625 1.00 0.00 ? 13 TRP A N 3 13 + ATOM 642 C CA . TRP A 1 13 ? -1.526 10.141 0.460 1.00 0.00 ? 13 TRP A CA 3 13 + ATOM 643 C C . TRP A 1 13 ? -2.449 9.706 1.599 1.00 0.00 ? 13 TRP A C 3 13 + ATOM 644 O O . TRP A 1 13 ? -2.208 8.687 2.244 1.00 0.00 ? 13 TRP A O 3 13 + ATOM 645 C CB . TRP A 1 13 ? -2.118 9.876 -0.926 1.00 0.00 ? 13 TRP A CB 3 13 + ATOM 646 C CG . TRP A 1 13 ? -1.351 10.545 -2.068 1.00 0.00 ? 13 TRP A CG 3 13 + ATOM 647 C CD1 . TRP A 1 13 ? -1.480 11.798 -2.526 1.00 0.00 ? 13 TRP A CD1 3 13 + ATOM 648 C CD2 . TRP A 1 13 ? -0.336 9.984 -2.890 1.00 0.00 ? 13 TRP A CD2 3 13 + ATOM 649 N NE1 . TRP A 1 13 ? -0.615 12.045 -3.573 1.00 0.00 ? 13 TRP A NE1 3 13 + ATOM 650 C CE2 . TRP A 1 13 ? 0.110 10.881 -3.797 1.00 0.00 ? 13 TRP A CE2 3 13 + ATOM 651 C CE3 . TRP A 1 13 ? 0.203 8.682 -2.850 1.00 0.00 ? 13 TRP A CE3 3 13 + ATOM 652 C CZ2 . TRP A 1 13 ? 1.107 10.623 -4.745 1.00 0.00 ? 13 TRP A CZ2 3 13 + ATOM 653 C CZ3 . TRP A 1 13 ? 1.200 8.425 -3.798 1.00 0.00 ? 13 TRP A CZ3 3 13 + ATOM 654 C CH2 . TRP A 1 13 ? 1.659 9.348 -4.731 1.00 0.00 ? 13 TRP A CH2 3 13 +HETATM 655 N N . DLE A 1 14 ? -3.490 10.500 1.812 1.00 0.00 ? 14 DLE A N 3 14 +HETATM 656 C CA . DLE A 1 14 ? -4.452 10.208 2.861 1.00 0.00 ? 14 DLE A CA 3 14 +HETATM 657 C CB . DLE A 1 14 ? -5.880 10.297 2.318 1.00 0.00 ? 14 DLE A CB 3 14 +HETATM 658 C CG . DLE A 1 14 ? -6.130 9.194 1.288 1.00 0.00 ? 14 DLE A CG 3 14 +HETATM 659 C CD1 . DLE A 1 14 ? -6.664 7.927 1.960 1.00 0.00 ? 14 DLE A CD1 3 14 +HETATM 660 C CD2 . DLE A 1 14 ? -7.055 9.686 0.172 1.00 0.00 ? 14 DLE A CD2 3 14 +HETATM 661 C C . DLE A 1 14 ? -4.190 11.129 4.056 1.00 0.00 ? 14 DLE A C 3 14 +HETATM 662 O O . DLE A 1 14 ? -4.741 12.225 4.131 1.00 0.00 ? 14 DLE A O 3 14 + ATOM 663 N N . TRP A 1 15 ? -3.348 10.648 4.959 1.00 0.00 ? 15 TRP A N 3 15 + ATOM 664 C CA . TRP A 1 15 ? -3.008 11.413 6.146 1.00 0.00 ? 15 TRP A CA 3 15 + ATOM 665 C C . TRP A 1 15 ? -1.489 11.364 6.320 1.00 0.00 ? 15 TRP A C 3 15 + ATOM 666 O O . TRP A 1 15 ? -0.885 10.295 6.238 1.00 0.00 ? 15 TRP A O 3 15 + ATOM 667 C CB . TRP A 1 15 ? -3.767 10.895 7.369 1.00 0.00 ? 15 TRP A CB 3 15 + ATOM 668 C CG . TRP A 1 15 ? -5.266 11.200 7.347 1.00 0.00 ? 15 TRP A CG 3 15 + ATOM 669 C CD1 . TRP A 1 15 ? -5.901 12.254 7.877 1.00 0.00 ? 15 TRP A CD1 3 15 + ATOM 670 C CD2 . TRP A 1 15 ? -6.309 10.434 6.760 1.00 0.00 ? 15 TRP A CD2 3 15 + ATOM 671 N NE1 . TRP A 1 15 ? -7.262 12.188 7.656 1.00 0.00 ? 15 TRP A NE1 3 15 + ATOM 672 C CE2 . TRP A 1 15 ? -7.511 11.022 6.942 1.00 0.00 ? 15 TRP A CE2 3 15 + ATOM 673 C CE3 . TRP A 1 15 ? -6.211 9.213 6.062 1.00 0.00 ? 15 TRP A CE3 3 15 + ATOM 674 C CZ2 . TRP A 1 15 ? -8.727 10.506 6.479 1.00 0.00 ? 15 TRP A CZ2 3 15 + ATOM 675 C CZ3 . TRP A 1 15 ? -7.428 8.697 5.598 1.00 0.00 ? 15 TRP A CZ3 3 15 + ATOM 676 C CH2 . TRP A 1 15 ? -8.664 9.303 5.786 1.00 0.00 ? 15 TRP A CH2 3 15 +HETATM 677 C CA . ETA A 1 16 ? 0.524 12.639 6.742 1.00 0.00 ? 16 ETA A CA 3 16 +HETATM 678 N N . ETA A 1 16 ? -0.914 12.534 6.556 1.00 0.00 ? 16 ETA A N 3 16 +HETATM 679 C CB . ETA A 1 16 ? 0.885 12.232 8.172 1.00 0.00 ? 16 ETA A CB 3 16 +HETATM 680 O O . ETA A 1 16 ? 2.294 12.183 8.374 1.00 0.00 ? 16 ETA A O 3 16 +HETATM 681 C C . FVA B 1 1 ? 3.559 -0.131 3.468 1.00 0.00 ? 1 FVA B C 3 1 +HETATM 682 N N . FVA B 1 1 ? 2.279 0.142 1.416 1.00 0.00 ? 1 FVA B N 3 1 +HETATM 683 O O . FVA B 1 1 ? 3.893 1.005 3.802 1.00 0.00 ? 1 FVA B O 3 1 +HETATM 684 C CA . FVA B 1 1 ? 3.448 -0.505 1.989 1.00 0.00 ? 1 FVA B CA 3 1 +HETATM 685 C CB . FVA B 1 1 ? 4.697 -0.138 1.184 1.00 0.00 ? 1 FVA B CB 3 1 +HETATM 686 C CG1 . FVA B 1 1 ? 5.919 -0.910 1.685 1.00 0.00 ? 1 FVA B CG1 3 1 +HETATM 687 C CG2 . FVA B 1 1 ? 4.474 -0.373 -0.312 1.00 0.00 ? 1 FVA B CG2 3 1 +HETATM 688 O O1 . FVA B 1 1 ? 1.365 -1.785 0.647 1.00 0.00 ? 1 FVA B O1 3 1 +HETATM 689 C CN . FVA B 1 1 ? 1.344 -0.564 0.796 1.00 0.00 ? 1 FVA B CN 3 1 + ATOM 690 N N . GLY B 1 2 ? 3.272 -1.108 4.315 1.00 0.00 ? 2 GLY B N 3 2 + ATOM 691 C CA . GLY B 1 2 ? 3.335 -0.896 5.752 1.00 0.00 ? 2 GLY B CA 3 2 + ATOM 692 C C . GLY B 1 2 ? 2.468 -1.914 6.495 1.00 0.00 ? 2 GLY B C 3 2 + ATOM 693 O O . GLY B 1 2 ? 2.971 -2.929 6.976 1.00 0.00 ? 2 GLY B O 3 2 + ATOM 694 N N . ALA B 1 3 ? 1.181 -1.607 6.567 1.00 0.00 ? 3 ALA B N 3 3 + ATOM 695 C CA . ALA B 1 3 ? 0.240 -2.483 7.245 1.00 0.00 ? 3 ALA B CA 3 3 + ATOM 696 C C . ALA B 1 3 ? -1.159 -2.267 6.663 1.00 0.00 ? 3 ALA B C 3 3 + ATOM 697 O O . ALA B 1 3 ? -1.528 -1.145 6.324 1.00 0.00 ? 3 ALA B O 3 3 + ATOM 698 C CB . ALA B 1 3 ? 0.291 -2.221 8.750 1.00 0.00 ? 3 ALA B CB 3 3 +HETATM 699 N N . DLE B 1 4 ? -1.899 -3.362 6.566 1.00 0.00 ? 4 DLE B N 3 4 +HETATM 700 C CA . DLE B 1 4 ? -3.249 -3.307 6.031 1.00 0.00 ? 4 DLE B CA 3 4 +HETATM 701 C CB . DLE B 1 4 ? -4.275 -3.549 7.140 1.00 0.00 ? 4 DLE B CB 3 4 +HETATM 702 C CG . DLE B 1 4 ? -5.690 -3.270 6.632 1.00 0.00 ? 4 DLE B CG 3 4 +HETATM 703 C CD1 . DLE B 1 4 ? -6.408 -4.571 6.267 1.00 0.00 ? 4 DLE B CD1 3 4 +HETATM 704 C CD2 . DLE B 1 4 ? -6.484 -2.441 7.643 1.00 0.00 ? 4 DLE B CD2 3 4 +HETATM 705 C C . DLE B 1 4 ? -3.369 -4.285 4.861 1.00 0.00 ? 4 DLE B C 3 4 +HETATM 706 O O . DLE B 1 4 ? -3.010 -5.454 4.984 1.00 0.00 ? 4 DLE B O 3 4 + ATOM 707 N N . ALA B 1 5 ? -3.877 -3.769 3.750 1.00 0.00 ? 5 ALA B N 3 5 + ATOM 708 C CA . ALA B 1 5 ? -4.050 -4.581 2.558 1.00 0.00 ? 5 ALA B CA 3 5 + ATOM 709 C C . ALA B 1 5 ? -3.061 -4.121 1.485 1.00 0.00 ? 5 ALA B C 3 5 + ATOM 710 O O . ALA B 1 5 ? -3.061 -2.953 1.096 1.00 0.00 ? 5 ALA B O 3 5 + ATOM 711 C CB . ALA B 1 5 ? -5.503 -4.495 2.088 1.00 0.00 ? 5 ALA B CB 3 5 +HETATM 712 N N . DVA B 1 6 ? -2.243 -5.062 1.037 1.00 0.00 ? 6 DVA B N 3 6 +HETATM 713 C CA . DVA B 1 6 ? -1.251 -4.767 0.016 1.00 0.00 ? 6 DVA B CA 3 6 +HETATM 714 C CB . DVA B 1 6 ? -1.776 -5.183 -1.359 1.00 0.00 ? 6 DVA B CB 3 6 +HETATM 715 C CG1 . DVA B 1 6 ? -3.120 -4.514 -1.658 1.00 0.00 ? 6 DVA B CG1 3 6 +HETATM 716 C CG2 . DVA B 1 6 ? -0.753 -4.874 -2.454 1.00 0.00 ? 6 DVA B CG2 3 6 +HETATM 717 C C . DVA B 1 6 ? 0.069 -5.447 0.383 1.00 0.00 ? 6 DVA B C 3 6 +HETATM 718 O O . DVA B 1 6 ? 0.188 -6.668 0.296 1.00 0.00 ? 6 DVA B O 3 6 + ATOM 719 N N . VAL B 1 7 ? 1.029 -4.627 0.785 1.00 0.00 ? 7 VAL B N 3 7 + ATOM 720 C CA . VAL B 1 7 ? 2.337 -5.134 1.165 1.00 0.00 ? 7 VAL B CA 3 7 + ATOM 721 C C . VAL B 1 7 ? 2.625 -4.752 2.618 1.00 0.00 ? 7 VAL B C 3 7 + ATOM 722 O O . VAL B 1 7 ? 2.396 -3.612 3.021 1.00 0.00 ? 7 VAL B O 3 7 + ATOM 723 C CB . VAL B 1 7 ? 3.400 -4.623 0.190 1.00 0.00 ? 7 VAL B CB 3 7 + ATOM 724 C CG1 . VAL B 1 7 ? 4.717 -5.379 0.371 1.00 0.00 ? 7 VAL B CG1 3 7 + ATOM 725 C CG2 . VAL B 1 7 ? 2.908 -4.714 -1.255 1.00 0.00 ? 7 VAL B CG2 3 7 +HETATM 726 N N . DVA B 1 8 ? 3.123 -5.726 3.365 1.00 0.00 ? 8 DVA B N 3 8 +HETATM 727 C CA . DVA B 1 8 ? 3.444 -5.507 4.765 1.00 0.00 ? 8 DVA B CA 3 8 +HETATM 728 C CB . DVA B 1 8 ? 4.957 -5.359 4.937 1.00 0.00 ? 8 DVA B CB 3 8 +HETATM 729 C CG1 . DVA B 1 8 ? 5.452 -4.044 4.330 1.00 0.00 ? 8 DVA B CG1 3 8 +HETATM 730 C CG2 . DVA B 1 8 ? 5.354 -5.467 6.411 1.00 0.00 ? 8 DVA B CG2 3 8 +HETATM 731 C C . DVA B 1 8 ? 2.855 -6.642 5.603 1.00 0.00 ? 8 DVA B C 3 8 +HETATM 732 O O . DVA B 1 8 ? 2.971 -7.811 5.239 1.00 0.00 ? 8 DVA B O 3 8 + ATOM 733 N N . TRP B 1 9 ? 2.236 -6.259 6.710 1.00 0.00 ? 9 TRP B N 3 9 + ATOM 734 C CA . TRP B 1 9 ? 1.629 -7.231 7.603 1.00 0.00 ? 9 TRP B CA 3 9 + ATOM 735 C C . TRP B 1 9 ? 0.114 -7.021 7.571 1.00 0.00 ? 9 TRP B C 3 9 + ATOM 736 O O . TRP B 1 9 ? -0.360 -5.885 7.563 1.00 0.00 ? 9 TRP B O 3 9 + ATOM 737 C CB . TRP B 1 9 ? 2.218 -7.124 9.012 1.00 0.00 ? 9 TRP B CB 3 9 + ATOM 738 C CG . TRP B 1 9 ? 1.452 -7.922 10.069 1.00 0.00 ? 9 TRP B CG 3 9 + ATOM 739 C CD1 . TRP B 1 9 ? 1.638 -9.198 10.434 1.00 0.00 ? 9 TRP B CD1 3 9 + ATOM 740 C CD2 . TRP B 1 9 ? 0.378 -7.486 10.892 1.00 0.00 ? 9 TRP B CD2 3 9 + ATOM 741 N NE1 . TRP B 1 9 ? 0.754 -9.575 11.424 1.00 0.00 ? 9 TRP B NE1 3 9 + ATOM 742 C CE2 . TRP B 1 9 ? -0.046 -8.475 11.708 1.00 0.00 ? 9 TRP B CE2 3 9 + ATOM 743 C CE3 . TRP B 1 9 ? -0.235 -6.217 10.929 1.00 0.00 ? 9 TRP B CE3 3 9 + ATOM 744 C CZ2 . TRP B 1 9 ? -1.089 -8.349 12.634 1.00 0.00 ? 9 TRP B CZ2 3 9 + ATOM 745 C CZ3 . TRP B 1 9 ? -1.279 -6.091 11.855 1.00 0.00 ? 9 TRP B CZ3 3 9 + ATOM 746 C CH2 . TRP B 1 9 ? -1.715 -7.109 12.695 1.00 0.00 ? 9 TRP B CH2 3 9 +HETATM 747 N N . DLE B 1 10 ? -0.605 -8.133 7.554 1.00 0.00 ? 10 DLE B N 3 10 +HETATM 748 C CA . DLE B 1 10 ? -2.058 -8.086 7.523 1.00 0.00 ? 10 DLE B CA 3 10 +HETATM 749 C CB . DLE B 1 10 ? -2.632 -8.355 8.915 1.00 0.00 ? 10 DLE B CB 3 10 +HETATM 750 C CG . DLE B 1 10 ? -4.157 -8.463 8.852 1.00 0.00 ? 10 DLE B CG 3 10 +HETATM 751 C CD1 . DLE B 1 10 ? -4.724 -8.985 10.174 1.00 0.00 ? 10 DLE B CD1 3 10 +HETATM 752 C CD2 . DLE B 1 10 ? -4.785 -7.128 8.446 1.00 0.00 ? 10 DLE B CD2 3 10 +HETATM 753 C C . DLE B 1 10 ? -2.572 -9.047 6.449 1.00 0.00 ? 10 DLE B C 3 10 +HETATM 754 O O . DLE B 1 10 ? -2.692 -10.247 6.691 1.00 0.00 ? 10 DLE B O 3 10 + ATOM 755 N N . TRP B 1 11 ? -2.862 -8.484 5.285 1.00 0.00 ? 11 TRP B N 3 11 + ATOM 756 C CA . TRP B 1 11 ? -3.361 -9.275 4.174 1.00 0.00 ? 11 TRP B CA 3 11 + ATOM 757 C C . TRP B 1 11 ? -2.692 -8.769 2.895 1.00 0.00 ? 11 TRP B C 3 11 + ATOM 758 O O . TRP B 1 11 ? -2.118 -7.681 2.880 1.00 0.00 ? 11 TRP B O 3 11 + ATOM 759 C CB . TRP B 1 11 ? -4.889 -9.227 4.109 1.00 0.00 ? 11 TRP B CB 3 11 + ATOM 760 C CG . TRP B 1 11 ? -5.584 -9.839 5.327 1.00 0.00 ? 11 TRP B CG 3 11 + ATOM 761 C CD1 . TRP B 1 11 ? -5.364 -11.036 5.887 1.00 0.00 ? 11 TRP B CD1 3 11 + ATOM 762 C CD2 . TRP B 1 11 ? -6.611 -9.271 6.130 1.00 0.00 ? 11 TRP B CD2 3 11 + ATOM 763 N NE1 . TRP B 1 11 ? -6.185 -11.244 6.977 1.00 0.00 ? 11 TRP B NE1 3 11 + ATOM 764 C CE2 . TRP B 1 11 ? -6.977 -10.111 7.122 1.00 0.00 ? 11 TRP B CE2 3 11 + ATOM 765 C CE3 . TRP B 1 11 ? -7.232 -8.013 5.993 1.00 0.00 ? 11 TRP B CE3 3 11 + ATOM 766 C CZ2 . TRP B 1 11 ? -7.965 -9.833 8.075 1.00 0.00 ? 11 TRP B CZ2 3 11 + ATOM 767 C CZ3 . TRP B 1 11 ? -8.220 -7.736 6.946 1.00 0.00 ? 11 TRP B CZ3 3 11 + ATOM 768 C CH2 . TRP B 1 11 ? -8.596 -8.599 7.968 1.00 0.00 ? 11 TRP B CH2 3 11 +HETATM 769 N N . DLE B 1 12 ? -2.789 -9.582 1.852 1.00 0.00 ? 12 DLE B N 3 12 +HETATM 770 C CA . DLE B 1 12 ? -2.199 -9.230 0.572 1.00 0.00 ? 12 DLE B CA 3 12 +HETATM 771 C CB . DLE B 1 12 ? -3.238 -9.347 -0.545 1.00 0.00 ? 12 DLE B CB 3 12 +HETATM 772 C CG . DLE B 1 12 ? -4.197 -8.155 -0.514 1.00 0.00 ? 12 DLE B CG 3 12 +HETATM 773 C CD1 . DLE B 1 12 ? -4.815 -7.912 -1.892 1.00 0.00 ? 12 DLE B CD1 3 12 +HETATM 774 C CD2 . DLE B 1 12 ? -5.263 -8.339 0.569 1.00 0.00 ? 12 DLE B CD2 3 12 +HETATM 775 C C . DLE B 1 12 ? -0.949 -10.080 0.340 1.00 0.00 ? 12 DLE B C 3 12 +HETATM 776 O O . DLE B 1 12 ? -1.046 -11.231 -0.085 1.00 0.00 ? 12 DLE B O 3 12 + ATOM 777 N N . TRP B 1 13 ? 0.197 -9.481 0.629 1.00 0.00 ? 13 TRP B N 3 13 + ATOM 778 C CA . TRP B 1 13 ? 1.465 -10.168 0.457 1.00 0.00 ? 13 TRP B CA 3 13 + ATOM 779 C C . TRP B 1 13 ? 2.397 -9.732 1.590 1.00 0.00 ? 13 TRP B C 3 13 + ATOM 780 O O . TRP B 1 13 ? 2.160 -8.712 2.235 1.00 0.00 ? 13 TRP B O 3 13 + ATOM 781 C CB . TRP B 1 13 ? 2.049 -9.903 -0.933 1.00 0.00 ? 13 TRP B CB 3 13 + ATOM 782 C CG . TRP B 1 13 ? 1.274 -10.573 -2.070 1.00 0.00 ? 13 TRP B CG 3 13 + ATOM 783 C CD1 . TRP B 1 13 ? 1.400 -11.826 -2.528 1.00 0.00 ? 13 TRP B CD1 3 13 + ATOM 784 C CD2 . TRP B 1 13 ? 0.254 -10.012 -2.886 1.00 0.00 ? 13 TRP B CD2 3 13 + ATOM 785 N NE1 . TRP B 1 13 ? 0.529 -12.073 -3.570 1.00 0.00 ? 13 TRP B NE1 3 13 + ATOM 786 C CE2 . TRP B 1 13 ? -0.197 -10.910 -3.790 1.00 0.00 ? 13 TRP B CE2 3 13 + ATOM 787 C CE3 . TRP B 1 13 ? -0.284 -8.711 -2.843 1.00 0.00 ? 13 TRP B CE3 3 13 + ATOM 788 C CZ2 . TRP B 1 13 ? -1.200 -10.653 -4.731 1.00 0.00 ? 13 TRP B CZ2 3 13 + ATOM 789 C CZ3 . TRP B 1 13 ? -1.288 -8.454 -3.785 1.00 0.00 ? 13 TRP B CZ3 3 13 + ATOM 790 C CH2 . TRP B 1 13 ? -1.752 -9.377 -4.714 1.00 0.00 ? 13 TRP B CH2 3 13 +HETATM 791 N N . DLE B 1 14 ? 3.438 -10.526 1.796 1.00 0.00 ? 14 DLE B N 3 14 +HETATM 792 C CA . DLE B 1 14 ? 4.407 -10.233 2.838 1.00 0.00 ? 14 DLE B CA 3 14 +HETATM 793 C CB . DLE B 1 14 ? 5.831 -10.322 2.287 1.00 0.00 ? 14 DLE B CB 3 14 +HETATM 794 C CG . DLE B 1 14 ? 6.075 -9.219 1.255 1.00 0.00 ? 14 DLE B CG 3 14 +HETATM 795 C CD1 . DLE B 1 14 ? 6.613 -7.952 1.922 1.00 0.00 ? 14 DLE B CD1 3 14 +HETATM 796 C CD2 . DLE B 1 14 ? 6.992 -9.713 0.133 1.00 0.00 ? 14 DLE B CD2 3 14 +HETATM 797 C C . DLE B 1 14 ? 4.153 -11.153 4.036 1.00 0.00 ? 14 DLE B C 3 14 +HETATM 798 O O . DLE B 1 14 ? 4.705 -12.249 4.108 1.00 0.00 ? 14 DLE B O 3 14 + ATOM 799 N N . TRP B 1 15 ? 3.317 -10.671 4.944 1.00 0.00 ? 15 TRP B N 3 15 + ATOM 800 C CA . TRP B 1 15 ? 2.984 -11.436 6.133 1.00 0.00 ? 15 TRP B CA 3 15 + ATOM 801 C C . TRP B 1 15 ? 1.466 -11.387 6.317 1.00 0.00 ? 15 TRP B C 3 15 + ATOM 802 O O . TRP B 1 15 ? 0.862 -10.318 6.239 1.00 0.00 ? 15 TRP B O 3 15 + ATOM 803 C CB . TRP B 1 15 ? 3.751 -10.917 7.352 1.00 0.00 ? 15 TRP B CB 3 15 + ATOM 804 C CG . TRP B 1 15 ? 5.250 -11.223 7.320 1.00 0.00 ? 15 TRP B CG 3 15 + ATOM 805 C CD1 . TRP B 1 15 ? 5.889 -12.276 7.846 1.00 0.00 ? 15 TRP B CD1 3 15 + ATOM 806 C CD2 . TRP B 1 15 ? 6.289 -10.456 6.725 1.00 0.00 ? 15 TRP B CD2 3 15 + ATOM 807 N NE1 . TRP B 1 15 ? 7.248 -12.210 7.617 1.00 0.00 ? 15 TRP B NE1 3 15 + ATOM 808 C CE2 . TRP B 1 15 ? 7.493 -11.045 6.901 1.00 0.00 ? 15 TRP B CE2 3 15 + ATOM 809 C CE3 . TRP B 1 15 ? 6.187 -9.236 6.027 1.00 0.00 ? 15 TRP B CE3 3 15 + ATOM 810 C CZ2 . TRP B 1 15 ? 8.705 -10.529 6.430 1.00 0.00 ? 15 TRP B CZ2 3 15 + ATOM 811 C CZ3 . TRP B 1 15 ? 7.400 -8.720 5.555 1.00 0.00 ? 15 TRP B CZ3 3 15 + ATOM 812 C CH2 . TRP B 1 15 ? 8.638 -9.326 5.736 1.00 0.00 ? 15 TRP B CH2 3 15 +HETATM 813 C CA . ETA B 1 16 ? -0.544 -12.661 6.753 1.00 0.00 ? 16 ETA B CA 3 16 +HETATM 814 N N . ETA B 1 16 ? 0.893 -12.557 6.557 1.00 0.00 ? 16 ETA B N 3 16 +HETATM 815 C CB . ETA B 1 16 ? -0.895 -12.254 8.185 1.00 0.00 ? 16 ETA B CB 3 16 +HETATM 816 O O . ETA B 1 16 ? -2.304 -12.205 8.396 1.00 0.00 ? 16 ETA B O 3 16 +HETATM 817 C C . FVA A 1 1 ? -3.580 0.135 3.412 1.00 0.00 ? 1 FVA A C 4 1 +HETATM 818 N N . FVA A 1 1 ? -2.327 -0.160 1.347 1.00 0.00 ? 1 FVA A N 4 1 +HETATM 819 O O . FVA A 1 1 ? -3.927 -0.993 3.759 1.00 0.00 ? 1 FVA A O 4 1 +HETATM 820 C CA . FVA A 1 1 ? -3.486 0.497 1.928 1.00 0.00 ? 1 FVA A CA 4 1 +HETATM 821 C CB . FVA A 1 1 ? -4.745 0.127 1.141 1.00 0.00 ? 1 FVA A CB 4 1 +HETATM 822 C CG1 . FVA A 1 1 ? -5.958 0.907 1.651 1.00 0.00 ? 1 FVA A CG1 4 1 +HETATM 823 C CG2 . FVA A 1 1 ? -4.539 0.349 -0.359 1.00 0.00 ? 1 FVA A CG2 4 1 +HETATM 824 O O1 . FVA A 1 1 ? -1.425 1.754 0.532 1.00 0.00 ? 1 FVA A O1 4 1 +HETATM 825 C CN . FVA A 1 1 ? -1.402 0.536 0.701 1.00 0.00 ? 1 FVA A CN 4 1 + ATOM 826 N N . GLY A 1 2 ? -3.266 1.113 4.248 1.00 0.00 ? 2 GLY A N 4 2 + ATOM 827 C CA . GLY A 1 2 ? -3.312 0.912 5.686 1.00 0.00 ? 2 GLY A CA 4 2 + ATOM 828 C C . GLY A 1 2 ? -2.427 1.928 6.410 1.00 0.00 ? 2 GLY A C 4 2 + ATOM 829 O O . GLY A 1 2 ? -2.915 2.951 6.890 1.00 0.00 ? 2 GLY A O 4 2 + ATOM 830 N N . ALA A 1 3 ? -1.142 1.612 6.467 1.00 0.00 ? 3 ALA A N 4 3 + ATOM 831 C CA . ALA A 1 3 ? -0.184 2.486 7.125 1.00 0.00 ? 3 ALA A CA 4 3 + ATOM 832 C C . ALA A 1 3 ? 1.201 2.270 6.512 1.00 0.00 ? 3 ALA A C 4 3 + ATOM 833 O O . ALA A 1 3 ? 1.527 1.165 6.080 1.00 0.00 ? 3 ALA A O 4 3 + ATOM 834 C CB . ALA A 1 3 ? -0.204 2.221 8.632 1.00 0.00 ? 3 ALA A CB 4 3 +HETATM 835 N N . DLE A 1 4 ? 1.979 3.342 6.494 1.00 0.00 ? 4 DLE A N 4 4 +HETATM 836 C CA . DLE A 1 4 ? 3.322 3.284 5.942 1.00 0.00 ? 4 DLE A CA 4 4 +HETATM 837 C CB . DLE A 1 4 ? 4.362 3.527 7.038 1.00 0.00 ? 4 DLE A CB 4 4 +HETATM 838 C CG . DLE A 1 4 ? 5.770 3.232 6.514 1.00 0.00 ? 4 DLE A CG 4 4 +HETATM 839 C CD1 . DLE A 1 4 ? 6.492 4.524 6.128 1.00 0.00 ? 4 DLE A CD1 4 4 +HETATM 840 C CD2 . DLE A 1 4 ? 6.568 2.408 7.525 1.00 0.00 ? 4 DLE A CD2 4 4 +HETATM 841 C C . DLE A 1 4 ? 3.428 4.257 4.766 1.00 0.00 ? 4 DLE A C 4 4 +HETATM 842 O O . DLE A 1 4 ? 3.043 5.419 4.882 1.00 0.00 ? 4 DLE A O 4 4 + ATOM 843 N N . ALA A 1 5 ? 3.951 3.746 3.662 1.00 0.00 ? 5 ALA A N 4 5 + ATOM 844 C CA . ALA A 1 5 ? 4.113 4.556 2.467 1.00 0.00 ? 5 ALA A CA 4 5 + ATOM 845 C C . ALA A 1 5 ? 3.100 4.108 1.412 1.00 0.00 ? 5 ALA A C 4 5 + ATOM 846 O O . ALA A 1 5 ? 3.119 2.959 0.976 1.00 0.00 ? 5 ALA A O 4 5 + ATOM 847 C CB . ALA A 1 5 ? 5.557 4.450 1.970 1.00 0.00 ? 5 ALA A CB 4 5 +HETATM 848 N N . DVA A 1 6 ? 2.237 5.040 1.033 1.00 0.00 ? 6 DVA A N 4 6 +HETATM 849 C CA . DVA A 1 6 ? 1.218 4.756 0.037 1.00 0.00 ? 6 DVA A CA 4 6 +HETATM 850 C CB . DVA A 1 6 ? 1.708 5.177 -1.349 1.00 0.00 ? 6 DVA A CB 4 6 +HETATM 851 C CG1 . DVA A 1 6 ? 2.978 4.418 -1.735 1.00 0.00 ? 6 DVA A CG1 4 6 +HETATM 852 C CG2 . DVA A 1 6 ? 0.612 4.985 -2.400 1.00 0.00 ? 6 DVA A CG2 4 6 +HETATM 853 C C . DVA A 1 6 ? -0.090 5.440 0.443 1.00 0.00 ? 6 DVA A C 4 6 +HETATM 854 O O . DVA A 1 6 ? -0.151 6.665 0.532 1.00 0.00 ? 6 DVA A O 4 6 + ATOM 855 N N . VAL A 1 7 ? -1.103 4.618 0.678 1.00 0.00 ? 7 VAL A N 4 7 + ATOM 856 C CA . VAL A 1 7 ? -2.404 5.129 1.072 1.00 0.00 ? 7 VAL A CA 4 7 + ATOM 857 C C . VAL A 1 7 ? -2.688 4.728 2.521 1.00 0.00 ? 7 VAL A C 4 7 + ATOM 858 O O . VAL A 1 7 ? -2.392 3.606 2.927 1.00 0.00 ? 7 VAL A O 4 7 + ATOM 859 C CB . VAL A 1 7 ? -3.476 4.639 0.095 1.00 0.00 ? 7 VAL A CB 4 7 + ATOM 860 C CG1 . VAL A 1 7 ? -4.791 5.392 0.302 1.00 0.00 ? 7 VAL A CG1 4 7 + ATOM 861 C CG2 . VAL A 1 7 ? -2.995 4.760 -1.352 1.00 0.00 ? 7 VAL A CG2 4 7 +HETATM 862 N N . DVA A 1 8 ? -3.258 5.667 3.260 1.00 0.00 ? 8 DVA A N 4 8 +HETATM 863 C CA . DVA A 1 8 ? -3.585 5.427 4.656 1.00 0.00 ? 8 DVA A CA 4 8 +HETATM 864 C CB . DVA A 1 8 ? -5.096 5.251 4.815 1.00 0.00 ? 8 DVA A CB 4 8 +HETATM 865 C CG1 . DVA A 1 8 ? -5.565 3.935 4.190 1.00 0.00 ? 8 DVA A CG1 4 8 +HETATM 866 C CG2 . DVA A 1 8 ? -5.505 5.334 6.288 1.00 0.00 ? 8 DVA A CG2 4 8 +HETATM 867 C C . DVA A 1 8 ? -3.021 6.562 5.510 1.00 0.00 ? 8 DVA A C 4 8 +HETATM 868 O O . DVA A 1 8 ? -3.112 7.730 5.135 1.00 0.00 ? 8 DVA A O 4 8 + ATOM 869 N N . TRP A 1 9 ? -2.452 6.181 6.645 1.00 0.00 ? 9 TRP A N 4 9 + ATOM 870 C CA . TRP A 1 9 ? -1.874 7.153 7.556 1.00 0.00 ? 9 TRP A CA 4 9 + ATOM 871 C C . TRP A 1 9 ? -0.379 6.850 7.684 1.00 0.00 ? 9 TRP A C 4 9 + ATOM 872 O O . TRP A 1 9 ? 0.004 5.739 8.045 1.00 0.00 ? 9 TRP A O 4 9 + ATOM 873 C CB . TRP A 1 9 ? -2.602 7.145 8.901 1.00 0.00 ? 9 TRP A CB 4 9 + ATOM 874 C CG . TRP A 1 9 ? -1.861 7.885 10.015 1.00 0.00 ? 9 TRP A CG 4 9 + ATOM 875 C CD1 . TRP A 1 9 ? -1.880 9.197 10.290 1.00 0.00 ? 9 TRP A CD1 4 9 + ATOM 876 C CD2 . TRP A 1 9 ? -0.990 7.344 11.001 1.00 0.00 ? 9 TRP A CD2 4 9 + ATOM 877 N NE1 . TRP A 1 9 ? -1.082 9.499 11.374 1.00 0.00 ? 9 TRP A NE1 4 9 + ATOM 878 C CE2 . TRP A 1 9 ? -0.520 8.309 11.821 1.00 0.00 ? 9 TRP A CE2 4 9 + ATOM 879 C CE3 . TRP A 1 9 ? -0.605 6.001 11.186 1.00 0.00 ? 9 TRP A CE3 4 9 + ATOM 880 C CZ2 . TRP A 1 9 ? 0.358 8.086 12.889 1.00 0.00 ? 9 TRP A CZ2 4 9 + ATOM 881 C CZ3 . TRP A 1 9 ? 0.272 5.778 12.253 1.00 0.00 ? 9 TRP A CZ3 4 9 + ATOM 882 C CH2 . TRP A 1 9 ? 0.756 6.770 13.097 1.00 0.00 ? 9 TRP A CH2 4 9 +HETATM 883 N N . DLE A 1 10 ? 0.425 7.859 7.381 1.00 0.00 ? 10 DLE A N 4 10 +HETATM 884 C CA . DLE A 1 10 ? 1.869 7.714 7.457 1.00 0.00 ? 10 DLE A CA 4 10 +HETATM 885 C CB . DLE A 1 10 ? 2.342 7.824 8.908 1.00 0.00 ? 10 DLE A CB 4 10 +HETATM 886 C CG . DLE A 1 10 ? 3.517 6.878 9.161 1.00 0.00 ? 10 DLE A CG 4 10 +HETATM 887 C CD1 . DLE A 1 10 ? 4.747 7.302 8.355 1.00 0.00 ? 10 DLE A CD1 4 10 +HETATM 888 C CD2 . DLE A 1 10 ? 3.820 6.769 10.657 1.00 0.00 ? 10 DLE A CD2 4 10 +HETATM 889 C C . DLE A 1 10 ? 2.529 8.726 6.518 1.00 0.00 ? 10 DLE A C 4 10 +HETATM 890 O O . DLE A 1 10 ? 2.865 9.834 6.931 1.00 0.00 ? 10 DLE A O 4 10 + ATOM 891 N N . TRP A 1 11 ? 2.692 8.308 5.272 1.00 0.00 ? 11 TRP A N 4 11 + ATOM 892 C CA . TRP A 1 11 ? 3.306 9.164 4.270 1.00 0.00 ? 11 TRP A CA 4 11 + ATOM 893 C C . TRP A 1 11 ? 2.749 8.766 2.902 1.00 0.00 ? 11 TRP A C 4 11 + ATOM 894 O O . TRP A 1 11 ? 2.297 7.636 2.717 1.00 0.00 ? 11 TRP A O 4 11 + ATOM 895 C CB . TRP A 1 11 ? 4.832 9.083 4.340 1.00 0.00 ? 11 TRP A CB 4 11 + ATOM 896 C CG . TRP A 1 11 ? 5.445 9.889 5.488 1.00 0.00 ? 11 TRP A CG 4 11 + ATOM 897 C CD1 . TRP A 1 11 ? 5.143 11.136 5.875 1.00 0.00 ? 11 TRP A CD1 4 11 + ATOM 898 C CD2 . TRP A 1 11 ? 6.467 9.490 6.392 1.00 0.00 ? 11 TRP A CD2 4 11 + ATOM 899 N NE1 . TRP A 1 11 ? 5.909 11.531 6.953 1.00 0.00 ? 11 TRP A NE1 4 11 + ATOM 900 C CE2 . TRP A 1 11 ? 6.751 10.472 7.275 1.00 0.00 ? 11 TRP A CE2 4 11 + ATOM 901 C CE3 . TRP A 1 11 ? 7.156 8.261 6.447 1.00 0.00 ? 11 TRP A CE3 4 11 + ATOM 902 C CZ2 . TRP A 1 11 ? 7.712 10.378 8.289 1.00 0.00 ? 11 TRP A CZ2 4 11 + ATOM 903 C CZ3 . TRP A 1 11 ? 8.118 8.167 7.461 1.00 0.00 ? 11 TRP A CZ3 4 11 + ATOM 904 C CH2 . TRP A 1 11 ? 8.409 9.177 8.369 1.00 0.00 ? 11 TRP A CH2 4 11 +HETATM 905 N N . DLE A 1 12 ? 2.798 9.715 1.979 1.00 0.00 ? 12 DLE A N 4 12 +HETATM 906 C CA . DLE A 1 12 ? 2.305 9.477 0.633 1.00 0.00 ? 12 DLE A CA 4 12 +HETATM 907 C CB . DLE A 1 12 ? 3.393 9.780 -0.398 1.00 0.00 ? 12 DLE A CB 4 12 +HETATM 908 C CG . DLE A 1 12 ? 4.661 8.983 -0.090 1.00 0.00 ? 12 DLE A CG 4 12 +HETATM 909 C CD1 . DLE A 1 12 ? 4.513 7.525 -0.531 1.00 0.00 ? 12 DLE A CD1 4 12 +HETATM 910 C CD2 . DLE A 1 12 ? 5.892 9.647 -0.710 1.00 0.00 ? 12 DLE A CD2 4 12 +HETATM 911 C C . DLE A 1 12 ? 1.016 10.274 0.419 1.00 0.00 ? 12 DLE A C 4 12 +HETATM 912 O O . DLE A 1 12 ? 1.062 11.475 0.157 1.00 0.00 ? 12 DLE A O 4 12 + ATOM 913 N N . TRP A 1 13 ? -0.102 9.574 0.541 1.00 0.00 ? 13 TRP A N 4 13 + ATOM 914 C CA . TRP A 1 13 ? -1.401 10.201 0.364 1.00 0.00 ? 13 TRP A CA 4 13 + ATOM 915 C C . TRP A 1 13 ? -2.286 9.795 1.543 1.00 0.00 ? 13 TRP A C 4 13 + ATOM 916 O O . TRP A 1 13 ? -1.977 8.842 2.256 1.00 0.00 ? 13 TRP A O 4 13 + ATOM 917 C CB . TRP A 1 13 ? -2.006 9.836 -0.993 1.00 0.00 ? 13 TRP A CB 4 13 + ATOM 918 C CG . TRP A 1 13 ? -1.280 10.458 -2.186 1.00 0.00 ? 13 TRP A CG 4 13 + ATOM 919 C CD1 . TRP A 1 13 ? -1.543 11.621 -2.798 1.00 0.00 ? 13 TRP A CD1 4 13 + ATOM 920 C CD2 . TRP A 1 13 ? -0.168 9.940 -2.906 1.00 0.00 ? 13 TRP A CD2 4 13 + ATOM 921 N NE1 . TRP A 1 13 ? -0.672 11.852 -3.843 1.00 0.00 ? 13 TRP A NE1 4 13 + ATOM 922 C CE2 . TRP A 1 13 ? 0.201 10.773 -3.902 1.00 0.00 ? 13 TRP A CE2 4 13 + ATOM 923 C CE3 . TRP A 1 13 ? 0.527 8.732 -2.692 1.00 0.00 ? 13 TRP A CE3 4 13 + ATOM 924 C CZ2 . TRP A 1 13 ? 1.263 10.536 -4.784 1.00 0.00 ? 13 TRP A CZ2 4 13 + ATOM 925 C CZ3 . TRP A 1 13 ? 1.587 8.495 -3.573 1.00 0.00 ? 13 TRP A CZ3 4 13 + ATOM 926 C CH2 . TRP A 1 13 ? 1.968 9.353 -4.599 1.00 0.00 ? 13 TRP A CH2 4 13 +HETATM 927 N N . DLE A 1 14 ? -3.370 10.538 1.712 1.00 0.00 ? 14 DLE A N 4 14 +HETATM 928 C CA . DLE A 1 14 ? -4.303 10.267 2.792 1.00 0.00 ? 14 DLE A CA 4 14 +HETATM 929 C CB . DLE A 1 14 ? -5.745 10.357 2.291 1.00 0.00 ? 14 DLE A CB 4 14 +HETATM 930 C CG . DLE A 1 14 ? -6.033 9.244 1.282 1.00 0.00 ? 14 DLE A CG 4 14 +HETATM 931 C CD1 . DLE A 1 14 ? -6.555 7.989 1.985 1.00 0.00 ? 14 DLE A CD1 4 14 +HETATM 932 C CD2 . DLE A 1 14 ? -6.987 9.728 0.188 1.00 0.00 ? 14 DLE A CD2 4 14 +HETATM 933 C C . DLE A 1 14 ? -4.002 11.200 3.967 1.00 0.00 ? 14 DLE A C 4 14 +HETATM 934 O O . DLE A 1 14 ? -4.506 12.321 4.019 1.00 0.00 ? 14 DLE A O 4 14 + ATOM 935 N N . TRP A 1 15 ? -3.180 10.703 4.880 1.00 0.00 ? 15 TRP A N 4 15 + ATOM 936 C CA . TRP A 1 15 ? -2.805 11.479 6.050 1.00 0.00 ? 15 TRP A CA 4 15 + ATOM 937 C C . TRP A 1 15 ? -1.308 11.274 6.293 1.00 0.00 ? 15 TRP A C 4 15 + ATOM 938 O O . TRP A 1 15 ? -0.801 10.161 6.161 1.00 0.00 ? 15 TRP A O 4 15 + ATOM 939 C CB . TRP A 1 15 ? -3.665 11.100 7.258 1.00 0.00 ? 15 TRP A CB 4 15 + ATOM 940 C CG . TRP A 1 15 ? -5.144 11.458 7.106 1.00 0.00 ? 15 TRP A CG 4 15 + ATOM 941 C CD1 . TRP A 1 15 ? -5.755 12.602 7.440 1.00 0.00 ? 15 TRP A CD1 4 15 + ATOM 942 C CD2 . TRP A 1 15 ? -6.192 10.655 6.576 1.00 0.00 ? 15 TRP A CD2 4 15 + ATOM 943 N NE1 . TRP A 1 15 ? -7.104 12.559 7.154 1.00 0.00 ? 15 TRP A NE1 4 15 + ATOM 944 C CE2 . TRP A 1 15 ? -7.372 11.311 6.603 1.00 0.00 ? 15 TRP A CE2 4 15 + ATOM 945 C CE3 . TRP A 1 15 ? -6.117 9.342 6.067 1.00 0.00 ? 15 TRP A CE3 4 15 + ATOM 946 C CZ2 . TRP A 1 15 ? -8.586 10.782 6.149 1.00 0.00 ? 15 TRP A CZ2 4 15 + ATOM 947 C CZ3 . TRP A 1 15 ? -7.331 8.813 5.614 1.00 0.00 ? 15 TRP A CZ3 4 15 + ATOM 948 C CH2 . TRP A 1 15 ? -8.546 9.488 5.641 1.00 0.00 ? 15 TRP A CH2 4 15 +HETATM 949 C CA . ETA A 1 16 ? 0.784 12.319 6.908 1.00 0.00 ? 16 ETA A CA 4 16 +HETATM 950 N N . ETA A 1 16 ? -0.644 12.365 6.645 1.00 0.00 ? 16 ETA A N 4 16 +HETATM 951 C CB . ETA A 1 16 ? 1.025 11.825 8.336 1.00 0.00 ? 16 ETA A CB 4 16 +HETATM 952 O O . ETA A 1 16 ? 2.413 11.753 8.649 1.00 0.00 ? 16 ETA A O 4 16 +HETATM 953 C C . FVA B 1 1 ? 3.539 -0.159 3.389 1.00 0.00 ? 1 FVA B C 4 1 +HETATM 954 N N . FVA B 1 1 ? 2.272 0.134 1.332 1.00 0.00 ? 1 FVA B N 4 1 +HETATM 955 O O . FVA B 1 1 ? 3.888 0.970 3.733 1.00 0.00 ? 1 FVA B O 4 1 +HETATM 956 C CA . FVA B 1 1 ? 3.435 -0.522 1.906 1.00 0.00 ? 1 FVA B CA 4 1 +HETATM 957 C CB . FVA B 1 1 ? 4.689 -0.152 1.111 1.00 0.00 ? 1 FVA B CB 4 1 +HETATM 958 C CG1 . FVA B 1 1 ? 5.905 -0.932 1.613 1.00 0.00 ? 1 FVA B CG1 4 1 +HETATM 959 C CG2 . FVA B 1 1 ? 4.474 -0.376 -0.387 1.00 0.00 ? 1 FVA B CG2 4 1 +HETATM 960 O O1 . FVA B 1 1 ? 1.365 -1.780 0.524 1.00 0.00 ? 1 FVA B O1 4 1 +HETATM 961 C CN . FVA B 1 1 ? 1.344 -0.562 0.693 1.00 0.00 ? 1 FVA B CN 4 1 + ATOM 962 N N . GLY B 1 2 ? 3.230 -1.136 4.228 1.00 0.00 ? 2 GLY B N 4 2 + ATOM 963 C CA . GLY B 1 2 ? 3.285 -0.935 5.666 1.00 0.00 ? 2 GLY B CA 4 2 + ATOM 964 C C . GLY B 1 2 ? 2.405 -1.950 6.396 1.00 0.00 ? 2 GLY B C 4 2 + ATOM 965 O O . GLY B 1 2 ? 2.896 -2.973 6.873 1.00 0.00 ? 2 GLY B O 4 2 + ATOM 966 N N . ALA B 1 3 ? 1.120 -1.635 6.461 1.00 0.00 ? 3 ALA B N 4 3 + ATOM 967 C CA . ALA B 1 3 ? 0.167 -2.507 7.125 1.00 0.00 ? 3 ALA B CA 4 3 + ATOM 968 C C . ALA B 1 3 ? -1.222 -2.292 6.521 1.00 0.00 ? 3 ALA B C 4 3 + ATOM 969 O O . ALA B 1 3 ? -1.551 -1.188 6.091 1.00 0.00 ? 3 ALA B O 4 3 + ATOM 970 C CB . ALA B 1 3 ? 0.196 -2.242 8.632 1.00 0.00 ? 3 ALA B CB 4 3 +HETATM 971 N N . DLE B 1 4 ? -2.000 -3.365 6.509 1.00 0.00 ? 4 DLE B N 4 4 +HETATM 972 C CA . DLE B 1 4 ? -3.346 -3.306 5.966 1.00 0.00 ? 4 DLE B CA 4 4 +HETATM 973 C CB . DLE B 1 4 ? -4.379 -3.549 7.068 1.00 0.00 ? 4 DLE B CB 4 4 +HETATM 974 C CG . DLE B 1 4 ? -5.791 -3.255 6.553 1.00 0.00 ? 4 DLE B CG 4 4 +HETATM 975 C CD1 . DLE B 1 4 ? -6.516 -4.546 6.172 1.00 0.00 ? 4 DLE B CD1 4 4 +HETATM 976 C CD2 . DLE B 1 4 ? -6.583 -2.429 7.569 1.00 0.00 ? 4 DLE B CD2 4 4 +HETATM 977 C C . DLE B 1 4 ? -3.460 -4.281 4.791 1.00 0.00 ? 4 DLE B C 4 4 +HETATM 978 O O . DLE B 1 4 ? -3.074 -5.443 4.905 1.00 0.00 ? 4 DLE B O 4 4 + ATOM 979 N N . ALA B 1 5 ? -3.991 -3.770 3.690 1.00 0.00 ? 5 ALA B N 4 5 + ATOM 980 C CA . ALA B 1 5 ? -4.160 -4.580 2.496 1.00 0.00 ? 5 ALA B CA 4 5 + ATOM 981 C C . ALA B 1 5 ? -3.154 -4.133 1.434 1.00 0.00 ? 5 ALA B C 4 5 + ATOM 982 O O . ALA B 1 5 ? -3.176 -2.984 0.998 1.00 0.00 ? 5 ALA B O 4 5 + ATOM 983 C CB . ALA B 1 5 ? -5.607 -4.475 2.008 1.00 0.00 ? 5 ALA B CB 4 5 +HETATM 984 N N . DVA B 1 6 ? -2.294 -5.065 1.051 1.00 0.00 ? 6 DVA B N 4 6 +HETATM 985 C CA . DVA B 1 6 ? -1.281 -4.782 0.048 1.00 0.00 ? 6 DVA B CA 4 6 +HETATM 986 C CB . DVA B 1 6 ? -1.779 -5.204 -1.335 1.00 0.00 ? 6 DVA B CB 4 6 +HETATM 987 C CG1 . DVA B 1 6 ? -3.052 -4.446 -1.713 1.00 0.00 ? 6 DVA B CG1 4 6 +HETATM 988 C CG2 . DVA B 1 6 ? -0.690 -5.013 -2.393 1.00 0.00 ? 6 DVA B CG2 4 6 +HETATM 989 C C . DVA B 1 6 ? 0.030 -5.466 0.446 1.00 0.00 ? 6 DVA B C 4 6 +HETATM 990 O O . DVA B 1 6 ? 0.092 -6.691 0.536 1.00 0.00 ? 6 DVA B O 4 6 + ATOM 991 N N . VAL B 1 7 ? 1.044 -4.644 0.674 1.00 0.00 ? 7 VAL B N 4 7 + ATOM 992 C CA . VAL B 1 7 ? 2.348 -5.154 1.060 1.00 0.00 ? 7 VAL B CA 4 7 + ATOM 993 C C . VAL B 1 7 ? 2.642 -4.753 2.507 1.00 0.00 ? 7 VAL B C 4 7 + ATOM 994 O O . VAL B 1 7 ? 2.348 -3.630 2.914 1.00 0.00 ? 7 VAL B O 4 7 + ATOM 995 C CB . VAL B 1 7 ? 3.413 -4.665 0.076 1.00 0.00 ? 7 VAL B CB 4 7 + ATOM 996 C CG1 . VAL B 1 7 ? 4.730 -5.418 0.275 1.00 0.00 ? 7 VAL B CG1 4 7 + ATOM 997 C CG2 . VAL B 1 7 ? 2.923 -4.788 -1.368 1.00 0.00 ? 7 VAL B CG2 4 7 +HETATM 998 N N . DVA B 1 8 ? 3.216 -5.691 3.243 1.00 0.00 ? 8 DVA B N 4 8 +HETATM 999 C CA . DVA B 1 8 ? 3.552 -5.450 4.636 1.00 0.00 ? 8 DVA B CA 4 8 +HETATM 1000 C CB . DVA B 1 8 ? 5.064 -5.274 4.786 1.00 0.00 ? 8 DVA B CB 4 8 +HETATM 1001 C CG1 . DVA B 1 8 ? 5.529 -3.959 4.157 1.00 0.00 ? 8 DVA B CG1 4 8 +HETATM 1002 C CG2 . DVA B 1 8 ? 5.482 -5.357 6.256 1.00 0.00 ? 8 DVA B CG2 4 8 +HETATM 1003 C C . DVA B 1 8 ? 2.994 -6.585 5.495 1.00 0.00 ? 8 DVA B C 4 8 +HETATM 1004 O O . DVA B 1 8 ? 3.082 -7.754 5.120 1.00 0.00 ? 8 DVA B O 4 8 + ATOM 1005 N N . TRP B 1 9 ? 2.431 -6.203 6.633 1.00 0.00 ? 9 TRP B N 4 9 + ATOM 1006 C CA . TRP B 1 9 ? 1.859 -7.174 7.548 1.00 0.00 ? 9 TRP B CA 4 9 + ATOM 1007 C C . TRP B 1 9 ? 0.366 -6.871 7.686 1.00 0.00 ? 9 TRP B C 4 9 + ATOM 1008 O O . TRP B 1 9 ? -0.015 -5.760 8.049 1.00 0.00 ? 9 TRP B O 4 9 + ATOM 1009 C CB . TRP B 1 9 ? 2.597 -7.165 8.889 1.00 0.00 ? 9 TRP B CB 4 9 + ATOM 1010 C CG . TRP B 1 9 ? 1.862 -7.905 10.008 1.00 0.00 ? 9 TRP B CG 4 9 + ATOM 1011 C CD1 . TRP B 1 9 ? 1.883 -9.217 10.284 1.00 0.00 ? 9 TRP B CD1 4 9 + ATOM 1012 C CD2 . TRP B 1 9 ? 0.998 -7.364 10.999 1.00 0.00 ? 9 TRP B CD2 4 9 + ATOM 1013 N NE1 . TRP B 1 9 ? 1.092 -9.518 11.373 1.00 0.00 ? 9 TRP B NE1 4 9 + ATOM 1014 C CE2 . TRP B 1 9 ? 0.533 -8.328 11.823 1.00 0.00 ? 9 TRP B CE2 4 9 + ATOM 1015 C CE3 . TRP B 1 9 ? 0.614 -6.020 11.186 1.00 0.00 ? 9 TRP B CE3 4 9 + ATOM 1016 C CZ2 . TRP B 1 9 ? -0.338 -8.104 12.896 1.00 0.00 ? 9 TRP B CZ2 4 9 + ATOM 1017 C CZ3 . TRP B 1 9 ? -0.256 -5.796 12.258 1.00 0.00 ? 9 TRP B CZ3 4 9 + ATOM 1018 C CH2 . TRP B 1 9 ? -0.735 -6.788 13.106 1.00 0.00 ? 9 TRP B CH2 4 9 +HETATM 1019 N N . DLE B 1 10 ? -0.440 -7.880 7.389 1.00 0.00 ? 10 DLE B N 4 10 +HETATM 1020 C CA . DLE B 1 10 ? -1.884 -7.736 7.474 1.00 0.00 ? 10 DLE B CA 4 10 +HETATM 1021 C CB . DLE B 1 10 ? -2.348 -7.845 8.928 1.00 0.00 ? 10 DLE B CB 4 10 +HETATM 1022 C CG . DLE B 1 10 ? -3.521 -6.898 9.188 1.00 0.00 ? 10 DLE B CG 4 10 +HETATM 1023 C CD1 . DLE B 1 10 ? -4.756 -7.323 8.390 1.00 0.00 ? 10 DLE B CD1 4 10 +HETATM 1024 C CD2 . DLE B 1 10 ? -3.815 -6.788 10.686 1.00 0.00 ? 10 DLE B CD2 4 10 +HETATM 1025 C C . DLE B 1 10 ? -2.550 -8.749 6.540 1.00 0.00 ? 10 DLE B C 4 10 +HETATM 1026 O O . DLE B 1 10 ? -2.884 -9.856 6.956 1.00 0.00 ? 10 DLE B O 4 10 + ATOM 1027 N N . TRP B 1 11 ? -2.721 -8.331 5.294 1.00 0.00 ? 11 TRP B N 4 11 + ATOM 1028 C CA . TRP B 1 11 ? -3.342 -9.188 4.297 1.00 0.00 ? 11 TRP B CA 4 11 + ATOM 1029 C C . TRP B 1 11 ? -2.793 -8.790 2.925 1.00 0.00 ? 11 TRP B C 4 11 + ATOM 1030 O O . TRP B 1 11 ? -2.343 -7.661 2.737 1.00 0.00 ? 11 TRP B O 4 11 + ATOM 1031 C CB . TRP B 1 11 ? -4.867 -9.107 4.377 1.00 0.00 ? 11 TRP B CB 4 11 + ATOM 1032 C CG . TRP B 1 11 ? -5.473 -9.912 5.529 1.00 0.00 ? 11 TRP B CG 4 11 + ATOM 1033 C CD1 . TRP B 1 11 ? -5.168 -11.159 5.915 1.00 0.00 ? 11 TRP B CD1 4 11 + ATOM 1034 C CD2 . TRP B 1 11 ? -6.489 -9.512 6.440 1.00 0.00 ? 11 TRP B CD2 4 11 + ATOM 1035 N NE1 . TRP B 1 11 ? -5.927 -11.553 6.998 1.00 0.00 ? 11 TRP B NE1 4 11 + ATOM 1036 C CE2 . TRP B 1 11 ? -6.767 -10.494 7.325 1.00 0.00 ? 11 TRP B CE2 4 11 + ATOM 1037 C CE3 . TRP B 1 11 ? -7.178 -8.283 6.498 1.00 0.00 ? 11 TRP B CE3 4 11 + ATOM 1038 C CZ2 . TRP B 1 11 ? -7.721 -10.399 8.345 1.00 0.00 ? 11 TRP B CZ2 4 11 + ATOM 1039 C CZ3 . TRP B 1 11 ? -8.133 -8.188 7.518 1.00 0.00 ? 11 TRP B CZ3 4 11 + ATOM 1040 C CH2 . TRP B 1 11 ? -8.418 -9.198 8.429 1.00 0.00 ? 11 TRP B CH2 4 11 +HETATM 1041 N N . DLE B 1 12 ? -2.849 -9.740 2.003 1.00 0.00 ? 12 DLE B N 4 12 +HETATM 1042 C CA . DLE B 1 12 ? -2.363 -9.503 0.654 1.00 0.00 ? 12 DLE B CA 4 12 +HETATM 1043 C CB . DLE B 1 12 ? -3.458 -9.807 -0.370 1.00 0.00 ? 12 DLE B CB 4 12 +HETATM 1044 C CG . DLE B 1 12 ? -4.724 -9.009 -0.054 1.00 0.00 ? 12 DLE B CG 4 12 +HETATM 1045 C CD1 . DLE B 1 12 ? -4.579 -7.551 -0.497 1.00 0.00 ? 12 DLE B CD1 4 12 +HETATM 1046 C CD2 . DLE B 1 12 ? -5.959 -9.674 -0.666 1.00 0.00 ? 12 DLE B CD2 4 12 +HETATM 1047 C C . DLE B 1 12 ? -1.076 -10.300 0.432 1.00 0.00 ? 12 DLE B C 4 12 +HETATM 1048 O O . DLE B 1 12 ? -1.123 -11.501 0.171 1.00 0.00 ? 12 DLE B O 4 12 + ATOM 1049 N N . TRP B 1 13 ? 0.043 -9.600 0.547 1.00 0.00 ? 13 TRP B N 4 13 + ATOM 1050 C CA . TRP B 1 13 ? 1.341 -10.227 0.362 1.00 0.00 ? 13 TRP B CA 4 13 + ATOM 1051 C C . TRP B 1 13 ? 2.233 -9.820 1.535 1.00 0.00 ? 13 TRP B C 4 13 + ATOM 1052 O O . TRP B 1 13 ? 1.929 -8.867 2.249 1.00 0.00 ? 13 TRP B O 4 13 + ATOM 1053 C CB . TRP B 1 13 ? 1.936 -9.863 -0.999 1.00 0.00 ? 13 TRP B CB 4 13 + ATOM 1054 C CG . TRP B 1 13 ? 1.203 -10.486 -2.188 1.00 0.00 ? 13 TRP B CG 4 13 + ATOM 1055 C CD1 . TRP B 1 13 ? 1.462 -11.649 -2.800 1.00 0.00 ? 13 TRP B CD1 4 13 + ATOM 1056 C CD2 . TRP B 1 13 ? 0.087 -9.968 -2.900 1.00 0.00 ? 13 TRP B CD2 4 13 + ATOM 1057 N NE1 . TRP B 1 13 ? 0.584 -11.881 -3.839 1.00 0.00 ? 13 TRP B NE1 4 13 + ATOM 1058 C CE2 . TRP B 1 13 ? -0.289 -10.801 -3.894 1.00 0.00 ? 13 TRP B CE2 4 13 + ATOM 1059 C CE3 . TRP B 1 13 ? -0.607 -8.760 -2.683 1.00 0.00 ? 13 TRP B CE3 4 13 + ATOM 1060 C CZ2 . TRP B 1 13 ? -1.356 -10.566 -4.769 1.00 0.00 ? 13 TRP B CZ2 4 13 + ATOM 1061 C CZ3 . TRP B 1 13 ? -1.673 -8.524 -3.557 1.00 0.00 ? 13 TRP B CZ3 4 13 + ATOM 1062 C CH2 . TRP B 1 13 ? -2.060 -9.382 -4.580 1.00 0.00 ? 13 TRP B CH2 4 13 +HETATM 1063 N N . DLE B 1 14 ? 3.318 -10.563 1.697 1.00 0.00 ? 14 DLE B N 4 14 +HETATM 1064 C CA . DLE B 1 14 ? 4.258 -10.292 2.771 1.00 0.00 ? 14 DLE B CA 4 14 +HETATM 1065 C CB . DLE B 1 14 ? 5.697 -10.382 2.261 1.00 0.00 ? 14 DLE B CB 4 14 +HETATM 1066 C CG . DLE B 1 14 ? 5.978 -9.270 1.249 1.00 0.00 ? 14 DLE B CG 4 14 +HETATM 1067 C CD1 . DLE B 1 14 ? 6.504 -8.014 1.948 1.00 0.00 ? 14 DLE B CD1 4 14 +HETATM 1068 C CD2 . DLE B 1 14 ? 6.925 -9.754 0.150 1.00 0.00 ? 14 DLE B CD2 4 14 +HETATM 1069 C C . DLE B 1 14 ? 3.964 -11.224 3.949 1.00 0.00 ? 14 DLE B C 4 14 +HETATM 1070 O O . DLE B 1 14 ? 4.469 -12.345 3.998 1.00 0.00 ? 14 DLE B O 4 14 + ATOM 1071 N N . TRP B 1 15 ? 3.148 -10.726 4.866 1.00 0.00 ? 15 TRP B N 4 15 + ATOM 1072 C CA . TRP B 1 15 ? 2.781 -11.501 6.039 1.00 0.00 ? 15 TRP B CA 4 15 + ATOM 1073 C C . TRP B 1 15 ? 1.286 -11.297 6.292 1.00 0.00 ? 15 TRP B C 4 15 + ATOM 1074 O O . TRP B 1 15 ? 0.777 -10.184 6.162 1.00 0.00 ? 15 TRP B O 4 15 + ATOM 1075 C CB . TRP B 1 15 ? 3.648 -11.121 7.242 1.00 0.00 ? 15 TRP B CB 4 15 + ATOM 1076 C CG . TRP B 1 15 ? 5.127 -11.480 7.080 1.00 0.00 ? 15 TRP B CG 4 15 + ATOM 1077 C CD1 . TRP B 1 15 ? 5.740 -12.624 7.411 1.00 0.00 ? 15 TRP B CD1 4 15 + ATOM 1078 C CD2 . TRP B 1 15 ? 6.171 -10.677 6.543 1.00 0.00 ? 15 TRP B CD2 4 15 + ATOM 1079 N NE1 . TRP B 1 15 ? 7.087 -12.581 7.116 1.00 0.00 ? 15 TRP B NE1 4 15 + ATOM 1080 C CE2 . TRP B 1 15 ? 7.351 -11.333 6.563 1.00 0.00 ? 15 TRP B CE2 4 15 + ATOM 1081 C CE3 . TRP B 1 15 ? 6.093 -9.365 6.034 1.00 0.00 ? 15 TRP B CE3 4 15 + ATOM 1082 C CZ2 . TRP B 1 15 ? 8.562 -10.804 6.101 1.00 0.00 ? 15 TRP B CZ2 4 15 + ATOM 1083 C CZ3 . TRP B 1 15 ? 7.304 -8.836 5.573 1.00 0.00 ? 15 TRP B CZ3 4 15 + ATOM 1084 C CH2 . TRP B 1 15 ? 8.519 -9.511 5.592 1.00 0.00 ? 15 TRP B CH2 4 15 +HETATM 1085 C CA . ETA B 1 16 ? -0.803 -12.341 6.921 1.00 0.00 ? 16 ETA B CA 4 16 +HETATM 1086 N N . ETA B 1 16 ? 0.624 -12.387 6.649 1.00 0.00 ? 16 ETA B N 4 16 +HETATM 1087 C CB . ETA B 1 16 ? -1.035 -11.846 8.350 1.00 0.00 ? 16 ETA B CB 4 16 +HETATM 1088 O O . ETA B 1 16 ? -2.421 -11.774 8.672 1.00 0.00 ? 16 ETA B O 4 16 +HETATM 1089 C C . FVA A 1 1 ? -3.451 0.088 3.147 1.00 0.00 ? 1 FVA A C 5 1 +HETATM 1090 N N . FVA A 1 1 ? -2.287 -0.142 1.022 1.00 0.00 ? 1 FVA A N 5 1 +HETATM 1091 O O . FVA A 1 1 ? -3.720 -1.072 3.455 1.00 0.00 ? 1 FVA A O 5 1 +HETATM 1092 C CA . FVA A 1 1 ? -3.408 0.510 1.677 1.00 0.00 ? 1 FVA A CA 5 1 +HETATM 1093 C CB . FVA A 1 1 ? -4.705 0.198 0.927 1.00 0.00 ? 1 FVA A CB 5 1 +HETATM 1094 C CG1 . FVA A 1 1 ? -5.878 0.990 1.508 1.00 0.00 ? 1 FVA A CG1 5 1 +HETATM 1095 C CG2 . FVA A 1 1 ? -4.550 0.465 -0.572 1.00 0.00 ? 1 FVA A CG2 5 1 +HETATM 1096 O O1 . FVA A 1 1 ? -1.287 1.795 0.401 1.00 0.00 ? 1 FVA A O1 5 1 +HETATM 1097 C CN . FVA A 1 1 ? -1.328 0.567 0.444 1.00 0.00 ? 1 FVA A CN 5 1 + ATOM 1098 N N . GLY A 1 2 ? -3.182 1.052 4.014 1.00 0.00 ? 2 GLY A N 5 2 + ATOM 1099 C CA . GLY A 1 2 ? -3.187 0.795 5.444 1.00 0.00 ? 2 GLY A CA 5 2 + ATOM 1100 C C . GLY A 1 2 ? -2.411 1.877 6.198 1.00 0.00 ? 2 GLY A C 5 2 + ATOM 1101 O O . GLY A 1 2 ? -2.974 2.908 6.565 1.00 0.00 ? 2 GLY A O 5 2 + ATOM 1102 N N . ALA A 1 3 ? -1.131 1.606 6.408 1.00 0.00 ? 3 ALA A N 5 3 + ATOM 1103 C CA . ALA A 1 3 ? -0.273 2.543 7.111 1.00 0.00 ? 3 ALA A CA 5 3 + ATOM 1104 C C . ALA A 1 3 ? 1.176 2.332 6.668 1.00 0.00 ? 3 ALA A C 5 3 + ATOM 1105 O O . ALA A 1 3 ? 1.544 1.242 6.234 1.00 0.00 ? 3 ALA A O 5 3 + ATOM 1106 C CB . ALA A 1 3 ? -0.452 2.366 8.620 1.00 0.00 ? 3 ALA A CB 5 3 +HETATM 1107 N N . DLE A 1 4 ? 1.960 3.393 6.794 1.00 0.00 ? 4 DLE A N 5 4 +HETATM 1108 C CA . DLE A 1 4 ? 3.361 3.338 6.412 1.00 0.00 ? 4 DLE A CA 5 4 +HETATM 1109 C CB . DLE A 1 4 ? 4.258 3.573 7.629 1.00 0.00 ? 4 DLE A CB 5 4 +HETATM 1110 C CG . DLE A 1 4 ? 5.732 3.561 7.218 1.00 0.00 ? 4 DLE A CG 5 4 +HETATM 1111 C CD1 . DLE A 1 4 ? 6.627 4.003 8.378 1.00 0.00 ? 4 DLE A CD1 5 4 +HETATM 1112 C CD2 . DLE A 1 4 ? 6.137 2.191 6.670 1.00 0.00 ? 4 DLE A CD2 5 4 +HETATM 1113 C C . DLE A 1 4 ? 3.612 4.320 5.265 1.00 0.00 ? 4 DLE A C 5 4 +HETATM 1114 O O . DLE A 1 4 ? 3.536 5.533 5.454 1.00 0.00 ? 4 DLE A O 5 4 + ATOM 1115 N N . ALA A 1 5 ? 3.905 3.758 4.101 1.00 0.00 ? 5 ALA A N 5 5 + ATOM 1116 C CA . ALA A 1 5 ? 4.167 4.569 2.924 1.00 0.00 ? 5 ALA A CA 5 5 + ATOM 1117 C C . ALA A 1 5 ? 3.244 4.123 1.788 1.00 0.00 ? 5 ALA A C 5 5 + ATOM 1118 O O . ALA A 1 5 ? 3.276 2.965 1.376 1.00 0.00 ? 5 ALA A O 5 5 + ATOM 1119 C CB . ALA A 1 5 ? 5.646 4.462 2.550 1.00 0.00 ? 5 ALA A CB 5 5 +HETATM 1120 N N . DVA A 1 6 ? 2.444 5.067 1.313 1.00 0.00 ? 6 DVA A N 5 6 +HETATM 1121 C CA . DVA A 1 6 ? 1.515 4.786 0.232 1.00 0.00 ? 6 DVA A CA 5 6 +HETATM 1122 C CB . DVA A 1 6 ? 2.117 5.227 -1.103 1.00 0.00 ? 6 DVA A CB 5 6 +HETATM 1123 C CG1 . DVA A 1 6 ? 3.438 4.505 -1.375 1.00 0.00 ? 6 DVA A CG1 5 6 +HETATM 1124 C CG2 . DVA A 1 6 ? 1.126 5.010 -2.249 1.00 0.00 ? 6 DVA A CG2 5 6 +HETATM 1125 C C . DVA A 1 6 ? 0.172 5.456 0.533 1.00 0.00 ? 6 DVA A C 5 6 +HETATM 1126 O O . DVA A 1 6 ? 0.092 6.680 0.625 1.00 0.00 ? 6 DVA A O 5 6 + ATOM 1127 N N . VAL A 1 7 ? -0.848 4.623 0.678 1.00 0.00 ? 7 VAL A N 5 7 + ATOM 1128 C CA . VAL A 1 7 ? -2.183 5.120 0.967 1.00 0.00 ? 7 VAL A CA 5 7 + ATOM 1129 C C . VAL A 1 7 ? -2.554 4.765 2.408 1.00 0.00 ? 7 VAL A C 5 7 + ATOM 1130 O O . VAL A 1 7 ? -2.341 3.636 2.848 1.00 0.00 ? 7 VAL A O 5 7 + ATOM 1131 C CB . VAL A 1 7 ? -3.178 4.571 -0.058 1.00 0.00 ? 7 VAL A CB 5 7 + ATOM 1132 C CG1 . VAL A 1 7 ? -4.525 5.290 0.046 1.00 0.00 ? 7 VAL A CG1 5 7 + ATOM 1133 C CG2 . VAL A 1 7 ? -2.613 4.667 -1.477 1.00 0.00 ? 7 VAL A CG2 5 7 +HETATM 1134 N N . DVA A 1 8 ? -3.103 5.750 3.104 1.00 0.00 ? 8 DVA A N 5 8 +HETATM 1135 C CA . DVA A 1 8 ? -3.505 5.557 4.486 1.00 0.00 ? 8 DVA A CA 5 8 +HETATM 1136 C CB . DVA A 1 8 ? -5.018 5.342 4.566 1.00 0.00 ? 8 DVA A CB 5 8 +HETATM 1137 C CG1 . DVA A 1 8 ? -5.410 3.984 3.981 1.00 0.00 ? 8 DVA A CG1 5 8 +HETATM 1138 C CG2 . DVA A 1 8 ? -5.517 5.485 6.005 1.00 0.00 ? 8 DVA A CG2 5 8 +HETATM 1139 C C . DVA A 1 8 ? -3.025 6.744 5.324 1.00 0.00 ? 8 DVA A C 5 8 +HETATM 1140 O O . DVA A 1 8 ? -3.184 7.895 4.923 1.00 0.00 ? 8 DVA A O 5 8 + ATOM 1141 N N . TRP A 1 9 ? -2.448 6.422 6.472 1.00 0.00 ? 9 TRP A N 5 9 + ATOM 1142 C CA . TRP A 1 9 ? -1.944 7.447 7.370 1.00 0.00 ? 9 TRP A CA 5 9 + ATOM 1143 C C . TRP A 1 9 ? -0.442 7.221 7.550 1.00 0.00 ? 9 TRP A C 5 9 + ATOM 1144 O O . TRP A 1 9 ? -0.005 6.098 7.801 1.00 0.00 ? 9 TRP A O 5 9 + ATOM 1145 C CB . TRP A 1 9 ? -2.712 7.444 8.693 1.00 0.00 ? 9 TRP A CB 5 9 + ATOM 1146 C CG . TRP A 1 9 ? -2.052 8.270 9.798 1.00 0.00 ? 9 TRP A CG 5 9 + ATOM 1147 C CD1 . TRP A 1 9 ? -2.232 9.568 10.078 1.00 0.00 ? 9 TRP A CD1 5 9 + ATOM 1148 C CD2 . TRP A 1 9 ? -1.104 7.841 10.767 1.00 0.00 ? 9 TRP A CD2 5 9 + ATOM 1149 N NE1 . TRP A 1 9 ? -1.462 9.966 11.152 1.00 0.00 ? 9 TRP A NE1 5 9 + ATOM 1150 C CE2 . TRP A 1 9 ? -0.745 8.856 11.583 1.00 0.00 ? 9 TRP A CE2 5 9 + ATOM 1151 C CE3 . TRP A 1 9 ? -0.548 6.557 10.939 1.00 0.00 ? 9 TRP A CE3 5 9 + ATOM 1152 C CZ2 . TRP A 1 9 ? 0.171 8.743 12.635 1.00 0.00 ? 9 TRP A CZ2 5 9 + ATOM 1153 C CZ3 . TRP A 1 9 ? 0.368 6.445 11.991 1.00 0.00 ? 9 TRP A CZ3 5 9 + ATOM 1154 C CH2 . TRP A 1 9 ? 0.737 7.489 12.831 1.00 0.00 ? 9 TRP A CH2 5 9 +HETATM 1155 N N . DLE A 1 10 ? 0.308 8.305 7.416 1.00 0.00 ? 10 DLE A N 5 10 +HETATM 1156 C CA . DLE A 1 10 ? 1.753 8.238 7.561 1.00 0.00 ? 10 DLE A CA 5 10 +HETATM 1157 C CB . DLE A 1 10 ? 2.157 8.484 9.016 1.00 0.00 ? 10 DLE A CB 5 10 +HETATM 1158 C CG . DLE A 1 10 ? 3.680 8.534 9.148 1.00 0.00 ? 10 DLE A CG 5 10 +HETATM 1159 C CD1 . DLE A 1 10 ? 4.095 9.099 10.507 1.00 0.00 ? 10 DLE A CD1 5 10 +HETATM 1160 C CD2 . DLE A 1 10 ? 4.298 7.159 8.885 1.00 0.00 ? 10 DLE A CD2 5 10 +HETATM 1161 C C . DLE A 1 10 ? 2.405 9.205 6.570 1.00 0.00 ? 10 DLE A C 5 10 +HETATM 1162 O O . DLE A 1 10 ? 2.541 10.393 6.855 1.00 0.00 ? 10 DLE A O 5 10 + ATOM 1163 N N . TRP A 1 11 ? 2.792 8.658 5.427 1.00 0.00 ? 11 TRP A N 5 11 + ATOM 1164 C CA . TRP A 1 11 ? 3.428 9.457 4.393 1.00 0.00 ? 11 TRP A CA 5 11 + ATOM 1165 C C . TRP A 1 11 ? 2.915 8.968 3.036 1.00 0.00 ? 11 TRP A C 5 11 + ATOM 1166 O O . TRP A 1 11 ? 2.497 7.819 2.904 1.00 0.00 ? 11 TRP A O 5 11 + ATOM 1167 C CB . TRP A 1 11 ? 4.952 9.398 4.513 1.00 0.00 ? 11 TRP A CB 5 11 + ATOM 1168 C CG . TRP A 1 11 ? 5.496 9.987 5.816 1.00 0.00 ? 11 TRP A CG 5 11 + ATOM 1169 C CD1 . TRP A 1 11 ? 5.267 11.203 6.331 1.00 0.00 ? 11 TRP A CD1 5 11 + ATOM 1170 C CD2 . TRP A 1 11 ? 6.363 9.375 6.761 1.00 0.00 ? 11 TRP A CD2 5 11 + ATOM 1171 N NE1 . TRP A 1 11 ? 5.932 11.379 7.527 1.00 0.00 ? 11 TRP A NE1 5 11 + ATOM 1172 C CE2 . TRP A 1 11 ? 6.629 10.206 7.792 1.00 0.00 ? 11 TRP A CE2 5 11 + ATOM 1173 C CE3 . TRP A 1 11 ? 6.925 8.082 6.724 1.00 0.00 ? 11 TRP A CE3 5 11 + ATOM 1174 C CZ2 . TRP A 1 11 ? 7.451 9.887 8.879 1.00 0.00 ? 11 TRP A CZ2 5 11 + ATOM 1175 C CZ3 . TRP A 1 11 ? 7.747 7.763 7.812 1.00 0.00 ? 11 TRP A CZ3 5 11 + ATOM 1176 C CH2 . TRP A 1 11 ? 8.021 8.619 8.872 1.00 0.00 ? 11 TRP A CH2 5 11 +HETATM 1177 N N . DLE A 1 12 ? 2.965 9.866 2.063 1.00 0.00 ? 12 DLE A N 5 12 +HETATM 1178 C CA . DLE A 1 12 ? 2.511 9.540 0.721 1.00 0.00 ? 12 DLE A CA 5 12 +HETATM 1179 C CB . DLE A 1 12 ? 3.635 9.762 -0.293 1.00 0.00 ? 12 DLE A CB 5 12 +HETATM 1180 C CG . DLE A 1 12 ? 4.928 9.101 0.187 1.00 0.00 ? 12 DLE A CG 5 12 +HETATM 1181 C CD1 . DLE A 1 12 ? 4.843 7.578 0.065 1.00 0.00 ? 12 DLE A CD1 5 12 +HETATM 1182 C CD2 . DLE A 1 12 ? 6.141 9.669 -0.552 1.00 0.00 ? 12 DLE A CD2 5 12 +HETATM 1183 C C . DLE A 1 12 ? 1.239 10.332 0.412 1.00 0.00 ? 12 DLE A C 5 12 +HETATM 1184 O O . DLE A 1 12 ? 1.303 11.522 0.105 1.00 0.00 ? 12 DLE A O 5 12 + ATOM 1185 N N . TRP A 1 13 ? 0.113 9.641 0.503 1.00 0.00 ? 13 TRP A N 5 13 + ATOM 1186 C CA . TRP A 1 13 ? -1.172 10.264 0.236 1.00 0.00 ? 13 TRP A CA 5 13 + ATOM 1187 C C . TRP A 1 13 ? -2.134 9.864 1.357 1.00 0.00 ? 13 TRP A C 5 13 + ATOM 1188 O O . TRP A 1 13 ? -1.873 8.914 2.094 1.00 0.00 ? 13 TRP A O 5 13 + ATOM 1189 C CB . TRP A 1 13 ? -1.686 9.889 -1.155 1.00 0.00 ? 13 TRP A CB 5 13 + ATOM 1190 C CG . TRP A 1 13 ? -0.879 10.500 -2.302 1.00 0.00 ? 13 TRP A CG 5 13 + ATOM 1191 C CD1 . TRP A 1 13 ? -1.087 11.667 -2.927 1.00 0.00 ? 13 TRP A CD1 5 13 + ATOM 1192 C CD2 . TRP A 1 13 ? 0.264 9.963 -2.954 1.00 0.00 ? 13 TRP A CD2 5 13 + ATOM 1193 N NE1 . TRP A 1 13 ? -0.151 11.884 -3.917 1.00 0.00 ? 13 TRP A NE1 5 13 + ATOM 1194 C CE2 . TRP A 1 13 ? 0.707 10.790 -3.926 1.00 0.00 ? 13 TRP A CE2 5 13 + ATOM 1195 C CE3 . TRP A 1 13 ? 0.925 8.744 -2.700 1.00 0.00 ? 13 TRP A CE3 5 13 + ATOM 1196 C CZ2 . TRP A 1 13 ? 1.815 10.537 -4.742 1.00 0.00 ? 13 TRP A CZ2 5 13 + ATOM 1197 C CZ3 . TRP A 1 13 ? 2.034 8.490 -3.516 1.00 0.00 ? 13 TRP A CZ3 5 13 + ATOM 1198 C CH2 . TRP A 1 13 ? 2.489 9.342 -4.516 1.00 0.00 ? 13 TRP A CH2 5 13 +HETATM 1199 N N . DLE A 1 14 ? -3.226 10.609 1.451 1.00 0.00 ? 14 DLE A N 5 14 +HETATM 1200 C CA . DLE A 1 14 ? -4.227 10.343 2.470 1.00 0.00 ? 14 DLE A CA 5 14 +HETATM 1201 C CB . DLE A 1 14 ? -5.630 10.364 1.860 1.00 0.00 ? 14 DLE A CB 5 14 +HETATM 1202 C CG . DLE A 1 14 ? -5.805 9.202 0.879 1.00 0.00 ? 14 DLE A CG 5 14 +HETATM 1203 C CD1 . DLE A 1 14 ? -6.370 7.968 1.586 1.00 0.00 ? 14 DLE A CD1 5 14 +HETATM 1204 C CD2 . DLE A 1 14 ? -6.661 9.620 -0.319 1.00 0.00 ? 14 DLE A CD2 5 14 +HETATM 1205 C C . DLE A 1 14 ? -4.046 11.329 3.626 1.00 0.00 ? 14 DLE A C 5 14 +HETATM 1206 O O . DLE A 1 14 ? -4.550 12.450 3.576 1.00 0.00 ? 14 DLE A O 5 14 + ATOM 1207 N N . TRP A 1 15 ? -3.325 10.875 4.640 1.00 0.00 ? 15 TRP A N 5 15 + ATOM 1208 C CA . TRP A 1 15 ? -3.071 11.703 5.807 1.00 0.00 ? 15 TRP A CA 5 15 + ATOM 1209 C C . TRP A 1 15 ? -1.582 11.602 6.143 1.00 0.00 ? 15 TRP A C 5 15 + ATOM 1210 O O . TRP A 1 15 ? -0.998 10.521 6.077 1.00 0.00 ? 15 TRP A O 5 15 + ATOM 1211 C CB . TRP A 1 15 ? -3.975 11.299 6.973 1.00 0.00 ? 15 TRP A CB 5 15 + ATOM 1212 C CG . TRP A 1 15 ? -5.460 11.587 6.738 1.00 0.00 ? 15 TRP A CG 5 15 + ATOM 1213 C CD1 . TRP A 1 15 ? -6.141 12.702 7.036 1.00 0.00 ? 15 TRP A CD1 5 15 + ATOM 1214 C CD2 . TRP A 1 15 ? -6.437 10.736 6.154 1.00 0.00 ? 15 TRP A CD2 5 15 + ATOM 1215 N NE1 . TRP A 1 15 ? -7.469 12.594 6.675 1.00 0.00 ? 15 TRP A NE1 5 15 + ATOM 1216 C CE2 . TRP A 1 15 ? -7.646 11.335 6.113 1.00 0.00 ? 15 TRP A CE2 5 15 + ATOM 1217 C CE3 . TRP A 1 15 ? -6.272 9.428 5.654 1.00 0.00 ? 15 TRP A CE3 5 15 + ATOM 1218 C CZ2 . TRP A 1 15 ? -8.807 10.749 5.594 1.00 0.00 ? 15 TRP A CZ2 5 15 + ATOM 1219 C CZ3 . TRP A 1 15 ? -7.433 8.842 5.135 1.00 0.00 ? 15 TRP A CZ3 5 15 + ATOM 1220 C CH2 . TRP A 1 15 ? -8.678 9.459 5.093 1.00 0.00 ? 15 TRP A CH2 5 15 +HETATM 1221 C CA . ETA A 1 16 ? 0.401 12.797 6.843 1.00 0.00 ? 16 ETA A CA 5 16 +HETATM 1222 N N . ETA A 1 16 ? -1.009 12.743 6.497 1.00 0.00 ? 16 ETA A N 5 16 +HETATM 1223 C CB . ETA A 1 16 ? 0.595 12.297 8.276 1.00 0.00 ? 16 ETA A CB 5 16 +HETATM 1224 O O . ETA A 1 16 ? 1.971 12.235 8.639 1.00 0.00 ? 16 ETA A O 5 16 +HETATM 1225 C C . FVA B 1 1 ? 3.451 -0.088 3.147 1.00 0.00 ? 1 FVA B C 5 1 +HETATM 1226 N N . FVA B 1 1 ? 2.287 0.142 1.022 1.00 0.00 ? 1 FVA B N 5 1 +HETATM 1227 O O . FVA B 1 1 ? 3.720 1.072 3.455 1.00 0.00 ? 1 FVA B O 5 1 +HETATM 1228 C CA . FVA B 1 1 ? 3.408 -0.510 1.677 1.00 0.00 ? 1 FVA B CA 5 1 +HETATM 1229 C CB . FVA B 1 1 ? 4.705 -0.198 0.927 1.00 0.00 ? 1 FVA B CB 5 1 +HETATM 1230 C CG1 . FVA B 1 1 ? 5.878 -0.990 1.508 1.00 0.00 ? 1 FVA B CG1 5 1 +HETATM 1231 C CG2 . FVA B 1 1 ? 4.550 -0.465 -0.572 1.00 0.00 ? 1 FVA B CG2 5 1 +HETATM 1232 O O1 . FVA B 1 1 ? 1.287 -1.795 0.401 1.00 0.00 ? 1 FVA B O1 5 1 +HETATM 1233 C CN . FVA B 1 1 ? 1.328 -0.567 0.444 1.00 0.00 ? 1 FVA B CN 5 1 + ATOM 1234 N N . GLY B 1 2 ? 3.182 -1.052 4.014 1.00 0.00 ? 2 GLY B N 5 2 + ATOM 1235 C CA . GLY B 1 2 ? 3.187 -0.795 5.444 1.00 0.00 ? 2 GLY B CA 5 2 + ATOM 1236 C C . GLY B 1 2 ? 2.411 -1.877 6.198 1.00 0.00 ? 2 GLY B C 5 2 + ATOM 1237 O O . GLY B 1 2 ? 2.974 -2.908 6.565 1.00 0.00 ? 2 GLY B O 5 2 + ATOM 1238 N N . ALA B 1 3 ? 1.131 -1.606 6.408 1.00 0.00 ? 3 ALA B N 5 3 + ATOM 1239 C CA . ALA B 1 3 ? 0.273 -2.543 7.111 1.00 0.00 ? 3 ALA B CA 5 3 + ATOM 1240 C C . ALA B 1 3 ? -1.176 -2.332 6.668 1.00 0.00 ? 3 ALA B C 5 3 + ATOM 1241 O O . ALA B 1 3 ? -1.544 -1.242 6.234 1.00 0.00 ? 3 ALA B O 5 3 + ATOM 1242 C CB . ALA B 1 3 ? 0.452 -2.366 8.620 1.00 0.00 ? 3 ALA B CB 5 3 +HETATM 1243 N N . DLE B 1 4 ? -1.960 -3.393 6.794 1.00 0.00 ? 4 DLE B N 5 4 +HETATM 1244 C CA . DLE B 1 4 ? -3.361 -3.338 6.412 1.00 0.00 ? 4 DLE B CA 5 4 +HETATM 1245 C CB . DLE B 1 4 ? -4.258 -3.573 7.629 1.00 0.00 ? 4 DLE B CB 5 4 +HETATM 1246 C CG . DLE B 1 4 ? -5.732 -3.561 7.218 1.00 0.00 ? 4 DLE B CG 5 4 +HETATM 1247 C CD1 . DLE B 1 4 ? -6.627 -4.003 8.378 1.00 0.00 ? 4 DLE B CD1 5 4 +HETATM 1248 C CD2 . DLE B 1 4 ? -6.137 -2.191 6.670 1.00 0.00 ? 4 DLE B CD2 5 4 +HETATM 1249 C C . DLE B 1 4 ? -3.612 -4.320 5.265 1.00 0.00 ? 4 DLE B C 5 4 +HETATM 1250 O O . DLE B 1 4 ? -3.536 -5.533 5.454 1.00 0.00 ? 4 DLE B O 5 4 + ATOM 1251 N N . ALA B 1 5 ? -3.905 -3.758 4.101 1.00 0.00 ? 5 ALA B N 5 5 + ATOM 1252 C CA . ALA B 1 5 ? -4.167 -4.569 2.924 1.00 0.00 ? 5 ALA B CA 5 5 + ATOM 1253 C C . ALA B 1 5 ? -3.244 -4.123 1.788 1.00 0.00 ? 5 ALA B C 5 5 + ATOM 1254 O O . ALA B 1 5 ? -3.276 -2.965 1.376 1.00 0.00 ? 5 ALA B O 5 5 + ATOM 1255 C CB . ALA B 1 5 ? -5.646 -4.462 2.550 1.00 0.00 ? 5 ALA B CB 5 5 +HETATM 1256 N N . DVA B 1 6 ? -2.444 -5.067 1.313 1.00 0.00 ? 6 DVA B N 5 6 +HETATM 1257 C CA . DVA B 1 6 ? -1.515 -4.786 0.232 1.00 0.00 ? 6 DVA B CA 5 6 +HETATM 1258 C CB . DVA B 1 6 ? -2.117 -5.227 -1.103 1.00 0.00 ? 6 DVA B CB 5 6 +HETATM 1259 C CG1 . DVA B 1 6 ? -3.438 -4.505 -1.375 1.00 0.00 ? 6 DVA B CG1 5 6 +HETATM 1260 C CG2 . DVA B 1 6 ? -1.126 -5.010 -2.249 1.00 0.00 ? 6 DVA B CG2 5 6 +HETATM 1261 C C . DVA B 1 6 ? -0.172 -5.456 0.533 1.00 0.00 ? 6 DVA B C 5 6 +HETATM 1262 O O . DVA B 1 6 ? -0.092 -6.680 0.625 1.00 0.00 ? 6 DVA B O 5 6 + ATOM 1263 N N . VAL B 1 7 ? 0.848 -4.623 0.678 1.00 0.00 ? 7 VAL B N 5 7 + ATOM 1264 C CA . VAL B 1 7 ? 2.183 -5.120 0.967 1.00 0.00 ? 7 VAL B CA 5 7 + ATOM 1265 C C . VAL B 1 7 ? 2.554 -4.765 2.408 1.00 0.00 ? 7 VAL B C 5 7 + ATOM 1266 O O . VAL B 1 7 ? 2.341 -3.636 2.848 1.00 0.00 ? 7 VAL B O 5 7 + ATOM 1267 C CB . VAL B 1 7 ? 3.178 -4.571 -0.058 1.00 0.00 ? 7 VAL B CB 5 7 + ATOM 1268 C CG1 . VAL B 1 7 ? 4.525 -5.290 0.046 1.00 0.00 ? 7 VAL B CG1 5 7 + ATOM 1269 C CG2 . VAL B 1 7 ? 2.613 -4.667 -1.477 1.00 0.00 ? 7 VAL B CG2 5 7 +HETATM 1270 N N . DVA B 1 8 ? 3.103 -5.750 3.104 1.00 0.00 ? 8 DVA B N 5 8 +HETATM 1271 C CA . DVA B 1 8 ? 3.505 -5.557 4.486 1.00 0.00 ? 8 DVA B CA 5 8 +HETATM 1272 C CB . DVA B 1 8 ? 5.018 -5.342 4.566 1.00 0.00 ? 8 DVA B CB 5 8 +HETATM 1273 C CG1 . DVA B 1 8 ? 5.410 -3.984 3.981 1.00 0.00 ? 8 DVA B CG1 5 8 +HETATM 1274 C CG2 . DVA B 1 8 ? 5.517 -5.485 6.005 1.00 0.00 ? 8 DVA B CG2 5 8 +HETATM 1275 C C . DVA B 1 8 ? 3.025 -6.744 5.324 1.00 0.00 ? 8 DVA B C 5 8 +HETATM 1276 O O . DVA B 1 8 ? 3.184 -7.895 4.923 1.00 0.00 ? 8 DVA B O 5 8 + ATOM 1277 N N . TRP B 1 9 ? 2.448 -6.422 6.472 1.00 0.00 ? 9 TRP B N 5 9 + ATOM 1278 C CA . TRP B 1 9 ? 1.944 -7.447 7.370 1.00 0.00 ? 9 TRP B CA 5 9 + ATOM 1279 C C . TRP B 1 9 ? 0.442 -7.221 7.550 1.00 0.00 ? 9 TRP B C 5 9 + ATOM 1280 O O . TRP B 1 9 ? 0.005 -6.098 7.801 1.00 0.00 ? 9 TRP B O 5 9 + ATOM 1281 C CB . TRP B 1 9 ? 2.712 -7.444 8.693 1.00 0.00 ? 9 TRP B CB 5 9 + ATOM 1282 C CG . TRP B 1 9 ? 2.052 -8.270 9.798 1.00 0.00 ? 9 TRP B CG 5 9 + ATOM 1283 C CD1 . TRP B 1 9 ? 2.232 -9.568 10.078 1.00 0.00 ? 9 TRP B CD1 5 9 + ATOM 1284 C CD2 . TRP B 1 9 ? 1.104 -7.841 10.767 1.00 0.00 ? 9 TRP B CD2 5 9 + ATOM 1285 N NE1 . TRP B 1 9 ? 1.462 -9.966 11.152 1.00 0.00 ? 9 TRP B NE1 5 9 + ATOM 1286 C CE2 . TRP B 1 9 ? 0.745 -8.856 11.583 1.00 0.00 ? 9 TRP B CE2 5 9 + ATOM 1287 C CE3 . TRP B 1 9 ? 0.548 -6.557 10.939 1.00 0.00 ? 9 TRP B CE3 5 9 + ATOM 1288 C CZ2 . TRP B 1 9 ? -0.171 -8.743 12.635 1.00 0.00 ? 9 TRP B CZ2 5 9 + ATOM 1289 C CZ3 . TRP B 1 9 ? -0.368 -6.445 11.991 1.00 0.00 ? 9 TRP B CZ3 5 9 + ATOM 1290 C CH2 . TRP B 1 9 ? -0.737 -7.489 12.831 1.00 0.00 ? 9 TRP B CH2 5 9 +HETATM 1291 N N . DLE B 1 10 ? -0.308 -8.305 7.416 1.00 0.00 ? 10 DLE B N 5 10 +HETATM 1292 C CA . DLE B 1 10 ? -1.753 -8.238 7.561 1.00 0.00 ? 10 DLE B CA 5 10 +HETATM 1293 C CB . DLE B 1 10 ? -2.157 -8.484 9.016 1.00 0.00 ? 10 DLE B CB 5 10 +HETATM 1294 C CG . DLE B 1 10 ? -3.680 -8.534 9.148 1.00 0.00 ? 10 DLE B CG 5 10 +HETATM 1295 C CD1 . DLE B 1 10 ? -4.095 -9.099 10.507 1.00 0.00 ? 10 DLE B CD1 5 10 +HETATM 1296 C CD2 . DLE B 1 10 ? -4.298 -7.159 8.885 1.00 0.00 ? 10 DLE B CD2 5 10 +HETATM 1297 C C . DLE B 1 10 ? -2.405 -9.205 6.570 1.00 0.00 ? 10 DLE B C 5 10 +HETATM 1298 O O . DLE B 1 10 ? -2.541 -10.393 6.855 1.00 0.00 ? 10 DLE B O 5 10 + ATOM 1299 N N . TRP B 1 11 ? -2.792 -8.658 5.427 1.00 0.00 ? 11 TRP B N 5 11 + ATOM 1300 C CA . TRP B 1 11 ? -3.428 -9.457 4.393 1.00 0.00 ? 11 TRP B CA 5 11 + ATOM 1301 C C . TRP B 1 11 ? -2.915 -8.968 3.036 1.00 0.00 ? 11 TRP B C 5 11 + ATOM 1302 O O . TRP B 1 11 ? -2.497 -7.819 2.904 1.00 0.00 ? 11 TRP B O 5 11 + ATOM 1303 C CB . TRP B 1 11 ? -4.952 -9.398 4.513 1.00 0.00 ? 11 TRP B CB 5 11 + ATOM 1304 C CG . TRP B 1 11 ? -5.496 -9.987 5.816 1.00 0.00 ? 11 TRP B CG 5 11 + ATOM 1305 C CD1 . TRP B 1 11 ? -5.267 -11.203 6.331 1.00 0.00 ? 11 TRP B CD1 5 11 + ATOM 1306 C CD2 . TRP B 1 11 ? -6.363 -9.375 6.761 1.00 0.00 ? 11 TRP B CD2 5 11 + ATOM 1307 N NE1 . TRP B 1 11 ? -5.932 -11.379 7.527 1.00 0.00 ? 11 TRP B NE1 5 11 + ATOM 1308 C CE2 . TRP B 1 11 ? -6.629 -10.206 7.792 1.00 0.00 ? 11 TRP B CE2 5 11 + ATOM 1309 C CE3 . TRP B 1 11 ? -6.925 -8.082 6.724 1.00 0.00 ? 11 TRP B CE3 5 11 + ATOM 1310 C CZ2 . TRP B 1 11 ? -7.451 -9.887 8.879 1.00 0.00 ? 11 TRP B CZ2 5 11 + ATOM 1311 C CZ3 . TRP B 1 11 ? -7.747 -7.763 7.812 1.00 0.00 ? 11 TRP B CZ3 5 11 + ATOM 1312 C CH2 . TRP B 1 11 ? -8.021 -8.619 8.872 1.00 0.00 ? 11 TRP B CH2 5 11 +HETATM 1313 N N . DLE B 1 12 ? -2.965 -9.866 2.063 1.00 0.00 ? 12 DLE B N 5 12 +HETATM 1314 C CA . DLE B 1 12 ? -2.511 -9.540 0.721 1.00 0.00 ? 12 DLE B CA 5 12 +HETATM 1315 C CB . DLE B 1 12 ? -3.635 -9.762 -0.293 1.00 0.00 ? 12 DLE B CB 5 12 +HETATM 1316 C CG . DLE B 1 12 ? -4.928 -9.101 0.187 1.00 0.00 ? 12 DLE B CG 5 12 +HETATM 1317 C CD1 . DLE B 1 12 ? -4.843 -7.578 0.065 1.00 0.00 ? 12 DLE B CD1 5 12 +HETATM 1318 C CD2 . DLE B 1 12 ? -6.141 -9.669 -0.552 1.00 0.00 ? 12 DLE B CD2 5 12 +HETATM 1319 C C . DLE B 1 12 ? -1.239 -10.332 0.412 1.00 0.00 ? 12 DLE B C 5 12 +HETATM 1320 O O . DLE B 1 12 ? -1.303 -11.522 0.105 1.00 0.00 ? 12 DLE B O 5 12 + ATOM 1321 N N . TRP B 1 13 ? -0.113 -9.641 0.503 1.00 0.00 ? 13 TRP B N 5 13 + ATOM 1322 C CA . TRP B 1 13 ? 1.172 -10.264 0.236 1.00 0.00 ? 13 TRP B CA 5 13 + ATOM 1323 C C . TRP B 1 13 ? 2.134 -9.864 1.357 1.00 0.00 ? 13 TRP B C 5 13 + ATOM 1324 O O . TRP B 1 13 ? 1.873 -8.914 2.094 1.00 0.00 ? 13 TRP B O 5 13 + ATOM 1325 C CB . TRP B 1 13 ? 1.686 -9.889 -1.155 1.00 0.00 ? 13 TRP B CB 5 13 + ATOM 1326 C CG . TRP B 1 13 ? 0.879 -10.500 -2.302 1.00 0.00 ? 13 TRP B CG 5 13 + ATOM 1327 C CD1 . TRP B 1 13 ? 1.087 -11.667 -2.927 1.00 0.00 ? 13 TRP B CD1 5 13 + ATOM 1328 C CD2 . TRP B 1 13 ? -0.264 -9.963 -2.954 1.00 0.00 ? 13 TRP B CD2 5 13 + ATOM 1329 N NE1 . TRP B 1 13 ? 0.151 -11.884 -3.917 1.00 0.00 ? 13 TRP B NE1 5 13 + ATOM 1330 C CE2 . TRP B 1 13 ? -0.707 -10.790 -3.926 1.00 0.00 ? 13 TRP B CE2 5 13 + ATOM 1331 C CE3 . TRP B 1 13 ? -0.925 -8.744 -2.700 1.00 0.00 ? 13 TRP B CE3 5 13 + ATOM 1332 C CZ2 . TRP B 1 13 ? -1.815 -10.537 -4.742 1.00 0.00 ? 13 TRP B CZ2 5 13 + ATOM 1333 C CZ3 . TRP B 1 13 ? -2.034 -8.490 -3.516 1.00 0.00 ? 13 TRP B CZ3 5 13 + ATOM 1334 C CH2 . TRP B 1 13 ? -2.489 -9.342 -4.516 1.00 0.00 ? 13 TRP B CH2 5 13 +HETATM 1335 N N . DLE B 1 14 ? 3.226 -10.609 1.451 1.00 0.00 ? 14 DLE B N 5 14 +HETATM 1336 C CA . DLE B 1 14 ? 4.227 -10.343 2.470 1.00 0.00 ? 14 DLE B CA 5 14 +HETATM 1337 C CB . DLE B 1 14 ? 5.630 -10.364 1.860 1.00 0.00 ? 14 DLE B CB 5 14 +HETATM 1338 C CG . DLE B 1 14 ? 5.805 -9.202 0.879 1.00 0.00 ? 14 DLE B CG 5 14 +HETATM 1339 C CD1 . DLE B 1 14 ? 6.370 -7.968 1.586 1.00 0.00 ? 14 DLE B CD1 5 14 +HETATM 1340 C CD2 . DLE B 1 14 ? 6.661 -9.620 -0.319 1.00 0.00 ? 14 DLE B CD2 5 14 +HETATM 1341 C C . DLE B 1 14 ? 4.046 -11.329 3.626 1.00 0.00 ? 14 DLE B C 5 14 +HETATM 1342 O O . DLE B 1 14 ? 4.550 -12.450 3.576 1.00 0.00 ? 14 DLE B O 5 14 + ATOM 1343 N N . TRP B 1 15 ? 3.325 -10.875 4.640 1.00 0.00 ? 15 TRP B N 5 15 + ATOM 1344 C CA . TRP B 1 15 ? 3.071 -11.703 5.807 1.00 0.00 ? 15 TRP B CA 5 15 + ATOM 1345 C C . TRP B 1 15 ? 1.582 -11.602 6.143 1.00 0.00 ? 15 TRP B C 5 15 + ATOM 1346 O O . TRP B 1 15 ? 0.998 -10.521 6.077 1.00 0.00 ? 15 TRP B O 5 15 + ATOM 1347 C CB . TRP B 1 15 ? 3.975 -11.299 6.973 1.00 0.00 ? 15 TRP B CB 5 15 + ATOM 1348 C CG . TRP B 1 15 ? 5.460 -11.587 6.738 1.00 0.00 ? 15 TRP B CG 5 15 + ATOM 1349 C CD1 . TRP B 1 15 ? 6.141 -12.702 7.036 1.00 0.00 ? 15 TRP B CD1 5 15 + ATOM 1350 C CD2 . TRP B 1 15 ? 6.437 -10.736 6.154 1.00 0.00 ? 15 TRP B CD2 5 15 + ATOM 1351 N NE1 . TRP B 1 15 ? 7.469 -12.594 6.675 1.00 0.00 ? 15 TRP B NE1 5 15 + ATOM 1352 C CE2 . TRP B 1 15 ? 7.646 -11.335 6.113 1.00 0.00 ? 15 TRP B CE2 5 15 + ATOM 1353 C CE3 . TRP B 1 15 ? 6.272 -9.428 5.654 1.00 0.00 ? 15 TRP B CE3 5 15 + ATOM 1354 C CZ2 . TRP B 1 15 ? 8.807 -10.749 5.594 1.00 0.00 ? 15 TRP B CZ2 5 15 + ATOM 1355 C CZ3 . TRP B 1 15 ? 7.433 -8.842 5.135 1.00 0.00 ? 15 TRP B CZ3 5 15 + ATOM 1356 C CH2 . TRP B 1 15 ? 8.678 -9.459 5.093 1.00 0.00 ? 15 TRP B CH2 5 15 +HETATM 1357 C CA . ETA B 1 16 ? -0.401 -12.797 6.843 1.00 0.00 ? 16 ETA B CA 5 16 +HETATM 1358 N N . ETA B 1 16 ? 1.009 -12.743 6.497 1.00 0.00 ? 16 ETA B N 5 16 +HETATM 1359 C CB . ETA B 1 16 ? -0.595 -12.297 8.276 1.00 0.00 ? 16 ETA B CB 5 16 +HETATM 1360 O O . ETA B 1 16 ? -1.971 -12.235 8.639 1.00 0.00 ? 16 ETA B O 5 16 +# +loop_ +_pdbx_poly_seq_scheme.asym_id +_pdbx_poly_seq_scheme.entity_id +_pdbx_poly_seq_scheme.seq_id +_pdbx_poly_seq_scheme.mon_id +_pdbx_poly_seq_scheme.ndb_seq_num +_pdbx_poly_seq_scheme.pdb_seq_num +_pdbx_poly_seq_scheme.auth_seq_num +_pdbx_poly_seq_scheme.pdb_mon_id +_pdbx_poly_seq_scheme.auth_mon_id +_pdbx_poly_seq_scheme.pdb_strand_id +_pdbx_poly_seq_scheme.pdb_ins_code +_pdbx_poly_seq_scheme.hetero +A 1 1 FVA 1 1 1 FVA FVA A . n +A 1 2 GLY 2 2 2 GLY GLY A . n +A 1 3 ALA 3 3 3 ALA ALA A . n +A 1 4 DLE 4 4 4 DLE DLE A . n +A 1 5 ALA 5 5 5 ALA ALA A . n +A 1 6 DVA 6 6 6 DVA DVA A . n +A 1 7 VAL 7 7 7 VAL VAL A . n +A 1 8 DVA 8 8 8 DVA DVA A . n +A 1 9 TRP 9 9 9 TRP TRP A . n +A 1 10 DLE 10 10 10 DLE DLE A . n +A 1 11 TRP 11 11 11 TRP TRP A . n +A 1 12 DLE 12 12 12 DLE DLE A . n +A 1 13 TRP 13 13 13 TRP TRP A . n +A 1 14 DLE 14 14 14 DLE DLE A . n +A 1 15 TRP 15 15 15 TRP TRP A . n +A 1 16 ETA 16 16 16 ETA ETA A . n +B 1 1 FVA 1 1 1 FVA FVA B . n +B 1 2 GLY 2 2 2 GLY GLY B . n +B 1 3 ALA 3 3 3 ALA ALA B . n +B 1 4 DLE 4 4 4 DLE DLE B . n +B 1 5 ALA 5 5 5 ALA ALA B . n +B 1 6 DVA 6 6 6 DVA DVA B . n +B 1 7 VAL 7 7 7 VAL VAL B . n +B 1 8 DVA 8 8 8 DVA DVA B . n +B 1 9 TRP 9 9 9 TRP TRP B . n +B 1 10 DLE 10 10 10 DLE DLE B . n +B 1 11 TRP 11 11 11 TRP TRP B . n +B 1 12 DLE 12 12 12 DLE DLE B . n +B 1 13 TRP 13 13 13 TRP TRP B . n +B 1 14 DLE 14 14 14 DLE DLE B . n +B 1 15 TRP 15 15 15 TRP TRP B . n +B 1 16 ETA 16 16 16 ETA ETA B . n +# +_pdbx_molecule_features.prd_id PRD_000150 +_pdbx_molecule_features.name "GRAMICIDIN A" +_pdbx_molecule_features.type Polypeptide +_pdbx_molecule_features.class Antibiotic +_pdbx_molecule_features.details +;GRAMICIDIN A IS A HEXADECAMERIC HELICAL PEPTIDE + WITH ALTERNATING D,L CHARACTERISTICS. + THE N-TERM IS FORMYLATED (RESIDUE 0). + THE C-TERM IS CAPPED WITH ETHANOLAMINE (RESIDUE 16). +; + + +# +loop_ +_pdbx_molecule.instance_id +_pdbx_molecule.prd_id +_pdbx_molecule.asym_id +1 PRD_000150 A +2 PRD_000150 B +# +_pdbx_struct_assembly.id 1 +_pdbx_struct_assembly.details software_defined_assembly +_pdbx_struct_assembly.method_details PQS +_pdbx_struct_assembly.oligomeric_details dimeric +_pdbx_struct_assembly.oligomeric_count 2 + +# +_pdbx_struct_assembly_gen.assembly_id 1 +_pdbx_struct_assembly_gen.oper_expression 1 +_pdbx_struct_assembly_gen.asym_id_list A,B + +# +_pdbx_struct_oper_list.id 1 +_pdbx_struct_oper_list.type "identity operation" +_pdbx_struct_oper_list.name 1_555 +_pdbx_struct_oper_list.symmetry_operation x,y,z +_pdbx_struct_oper_list.matrix[1][1] 1.0000000000 +_pdbx_struct_oper_list.matrix[1][2] 0.0000000000 +_pdbx_struct_oper_list.matrix[1][3] 0.0000000000 +_pdbx_struct_oper_list.vector[1] 0.0000000000 +_pdbx_struct_oper_list.matrix[2][1] 0.0000000000 +_pdbx_struct_oper_list.matrix[2][2] 1.0000000000 +_pdbx_struct_oper_list.matrix[2][3] 0.0000000000 +_pdbx_struct_oper_list.vector[2] 0.0000000000 +_pdbx_struct_oper_list.matrix[3][1] 0.0000000000 +_pdbx_struct_oper_list.matrix[3][2] 0.0000000000 +_pdbx_struct_oper_list.matrix[3][3] 1.0000000000 +_pdbx_struct_oper_list.vector[3] 0.0000000000 + +# +_pdbx_entry_details.entry_id 1GRM +_pdbx_entry_details.compound_details +;GRAMICIDIN IS A HETEROGENEOUS MIXTURE OF SEVERAL COMPOUNDS + INCLUDING GRAMICIDIN A, B AND C WHICH ARE OBTAINED FROM + BACILLUS BREVIS AND CALLED COLLECTIVELY GRAMICIDIN D + HERE, GRAMICIDIN A IS REPRESENTED BY THE SEQUENCE (SEQRES) +; + +_pdbx_entry_details.source_details ? +_pdbx_entry_details.nonpolymer_details ? +_pdbx_entry_details.sequence_details ? + +# +loop_ +_pdbx_validate_rmsd_angle.id +_pdbx_validate_rmsd_angle.PDB_model_num +_pdbx_validate_rmsd_angle.auth_atom_id_1 +_pdbx_validate_rmsd_angle.auth_asym_id_1 +_pdbx_validate_rmsd_angle.auth_comp_id_1 +_pdbx_validate_rmsd_angle.auth_seq_id_1 +_pdbx_validate_rmsd_angle.PDB_ins_code_1 +_pdbx_validate_rmsd_angle.label_alt_id_1 +_pdbx_validate_rmsd_angle.auth_atom_id_2 +_pdbx_validate_rmsd_angle.auth_asym_id_2 +_pdbx_validate_rmsd_angle.auth_comp_id_2 +_pdbx_validate_rmsd_angle.auth_seq_id_2 +_pdbx_validate_rmsd_angle.PDB_ins_code_2 +_pdbx_validate_rmsd_angle.label_alt_id_2 +_pdbx_validate_rmsd_angle.auth_atom_id_3 +_pdbx_validate_rmsd_angle.auth_asym_id_3 +_pdbx_validate_rmsd_angle.auth_comp_id_3 +_pdbx_validate_rmsd_angle.auth_seq_id_3 +_pdbx_validate_rmsd_angle.PDB_ins_code_3 +_pdbx_validate_rmsd_angle.label_alt_id_3 +_pdbx_validate_rmsd_angle.angle_value +_pdbx_validate_rmsd_angle.angle_target_value +_pdbx_validate_rmsd_angle.angle_deviation +_pdbx_validate_rmsd_angle.angle_standard_deviation +_pdbx_validate_rmsd_angle.linker_flag + 1 1 CG A TRP 9 ? ? CD2 A TRP 9 ? ? CE3 A TRP 9 ? ? 128.36 133.90 -5.54 0.90 N + 2 1 CG A TRP 11 ? ? CD2 A TRP 11 ? ? CE3 A TRP 11 ? ? 128.36 133.90 -5.54 0.90 N + 3 1 CG A TRP 13 ? ? CD2 A TRP 13 ? ? CE3 A TRP 13 ? ? 128.31 133.90 -5.59 0.90 N + 4 1 CG A TRP 15 ? ? CD2 A TRP 15 ? ? CE3 A TRP 15 ? ? 128.32 133.90 -5.58 0.90 N + 5 1 CG B TRP 9 ? ? CD2 B TRP 9 ? ? CE3 B TRP 9 ? ? 128.33 133.90 -5.57 0.90 N + 6 1 CG B TRP 11 ? ? CD2 B TRP 11 ? ? CE3 B TRP 11 ? ? 128.39 133.90 -5.51 0.90 N + 7 1 CG B TRP 13 ? ? CD2 B TRP 13 ? ? CE3 B TRP 13 ? ? 128.30 133.90 -5.60 0.90 N + 8 1 CG B TRP 15 ? ? CD2 B TRP 15 ? ? CE3 B TRP 15 ? ? 128.40 133.90 -5.50 0.90 N + 9 2 CG A TRP 9 ? ? CD2 A TRP 9 ? ? CE3 A TRP 9 ? ? 128.36 133.90 -5.54 0.90 N +10 2 CG A TRP 11 ? ? CD2 A TRP 11 ? ? CE3 A TRP 11 ? ? 128.35 133.90 -5.55 0.90 N +11 2 CG A TRP 13 ? ? CD2 A TRP 13 ? ? CE3 A TRP 13 ? ? 128.32 133.90 -5.58 0.90 N +12 2 CG A TRP 15 ? ? CD2 A TRP 15 ? ? CE3 A TRP 15 ? ? 128.33 133.90 -5.57 0.90 N +13 2 CG B TRP 9 ? ? CD2 B TRP 9 ? ? CE3 B TRP 9 ? ? 128.36 133.90 -5.54 0.90 N +14 2 CG B TRP 11 ? ? CD2 B TRP 11 ? ? CE3 B TRP 11 ? ? 128.41 133.90 -5.49 0.90 N +15 2 CG B TRP 13 ? ? CD2 B TRP 13 ? ? CE3 B TRP 13 ? ? 128.33 133.90 -5.57 0.90 N +16 2 CG B TRP 15 ? ? CD2 B TRP 15 ? ? CE3 B TRP 15 ? ? 128.35 133.90 -5.55 0.90 N +17 3 CG A TRP 9 ? ? CD2 A TRP 9 ? ? CE3 A TRP 9 ? ? 128.32 133.90 -5.58 0.90 N +18 3 CG A TRP 11 ? ? CD2 A TRP 11 ? ? CE3 A TRP 11 ? ? 128.34 133.90 -5.56 0.90 N +19 3 CG A TRP 13 ? ? CD2 A TRP 13 ? ? CE3 A TRP 13 ? ? 128.40 133.90 -5.50 0.90 N +20 3 CG A TRP 15 ? ? CD2 A TRP 15 ? ? CE3 A TRP 15 ? ? 128.34 133.90 -5.56 0.90 N +21 3 CG B TRP 9 ? ? CD2 B TRP 9 ? ? CE3 B TRP 9 ? ? 128.30 133.90 -5.60 0.90 N +22 3 CG B TRP 11 ? ? CD2 B TRP 11 ? ? CE3 B TRP 11 ? ? 128.30 133.90 -5.60 0.90 N +23 3 CG B TRP 13 ? ? CD2 B TRP 13 ? ? CE3 B TRP 13 ? ? 128.39 133.90 -5.51 0.90 N +24 3 CG B TRP 15 ? ? CD2 B TRP 15 ? ? CE3 B TRP 15 ? ? 128.41 133.90 -5.49 0.90 N +25 4 CG A TRP 9 ? ? CD2 A TRP 9 ? ? CE3 A TRP 9 ? ? 128.37 133.90 -5.53 0.90 N +26 4 CG A TRP 11 ? ? CD2 A TRP 11 ? ? CE3 A TRP 11 ? ? 128.37 133.90 -5.53 0.90 N +27 4 CG A TRP 13 ? ? CD2 A TRP 13 ? ? CE3 A TRP 13 ? ? 128.35 133.90 -5.55 0.90 N +28 4 CG A TRP 15 ? ? CD2 A TRP 15 ? ? CE3 A TRP 15 ? ? 128.38 133.90 -5.52 0.90 N +29 4 CG B TRP 9 ? ? CD2 B TRP 9 ? ? CE3 B TRP 9 ? ? 128.36 133.90 -5.54 0.90 N +30 4 CG B TRP 11 ? ? CD2 B TRP 11 ? ? CE3 B TRP 11 ? ? 128.36 133.90 -5.54 0.90 N +31 4 CG B TRP 13 ? ? CD2 B TRP 13 ? ? CE3 B TRP 13 ? ? 128.43 133.90 -5.47 0.90 N +32 4 CG B TRP 15 ? ? CD2 B TRP 15 ? ? CE3 B TRP 15 ? ? 128.41 133.90 -5.49 0.90 N +33 5 CG A TRP 9 ? ? CD2 A TRP 9 ? ? CE3 A TRP 9 ? ? 128.38 133.90 -5.52 0.90 N +34 5 CG A TRP 11 ? ? CD2 A TRP 11 ? ? CE3 A TRP 11 ? ? 128.35 133.90 -5.55 0.90 N +35 5 CG A TRP 13 ? ? CD2 A TRP 13 ? ? CE3 A TRP 13 ? ? 128.40 133.90 -5.50 0.90 N +36 5 CG A TRP 15 ? ? CD2 A TRP 15 ? ? CE3 A TRP 15 ? ? 128.37 133.90 -5.53 0.90 N +37 5 CG B TRP 9 ? ? CD2 B TRP 9 ? ? CE3 B TRP 9 ? ? 128.38 133.90 -5.52 0.90 N +38 5 CG B TRP 11 ? ? CD2 B TRP 11 ? ? CE3 B TRP 11 ? ? 128.35 133.90 -5.55 0.90 N +39 5 CG B TRP 13 ? ? CD2 B TRP 13 ? ? CE3 B TRP 13 ? ? 128.40 133.90 -5.50 0.90 N +40 5 CG B TRP 15 ? ? CD2 B TRP 15 ? ? CE3 B TRP 15 ? ? 128.37 133.90 -5.53 0.90 N +# +loop_ +_pdbx_validate_torsion.id +_pdbx_validate_torsion.PDB_model_num +_pdbx_validate_torsion.auth_comp_id +_pdbx_validate_torsion.auth_asym_id +_pdbx_validate_torsion.auth_seq_id +_pdbx_validate_torsion.PDB_ins_code +_pdbx_validate_torsion.label_alt_id +_pdbx_validate_torsion.phi +_pdbx_validate_torsion.psi +1 4 DLE A 10 ? ? 155.35 -87.67 +2 4 DLE B 10 ? ? 155.35 -87.66 +# +loop_ +_chem_comp_bond.comp_id +_chem_comp_bond.pdbx_stereo_config +_chem_comp_bond.pdbx_ordinal +_chem_comp_bond.pdbx_aromatic_flag +_chem_comp_bond.atom_id_1 +_chem_comp_bond.atom_id_2 +_chem_comp_bond.value_order +ALA N 1 N N CA SING +ALA N 2 N N H SING +ALA N 3 N N H2 SING +ALA N 4 N CA C SING +ALA N 5 N CA CB SING +ALA N 6 N CA HA SING +ALA N 7 N C O DOUB +ALA N 8 N C OXT SING +ALA N 9 N CB HB1 SING +ALA N 10 N CB HB2 SING +ALA N 11 N CB HB3 SING +ALA N 12 N OXT HXT SING +DLE N 1 N N CA SING +DLE N 2 N N H SING +DLE N 3 N N H2 SING +DLE N 4 N CA CB SING +DLE N 5 N CA C SING +DLE N 6 N CA HA SING +DLE N 7 N CB CG SING +DLE N 8 N CB HB2 SING +DLE N 9 N CB HB3 SING +DLE N 10 N CG CD1 SING +DLE N 11 N CG CD2 SING +DLE N 12 N CG HG SING +DLE N 13 N CD1 HD11 SING +DLE N 14 N CD1 HD12 SING +DLE N 15 N CD1 HD13 SING +DLE N 16 N CD2 HD21 SING +DLE N 17 N CD2 HD22 SING +DLE N 18 N CD2 HD23 SING +DLE N 19 N C O DOUB +DLE N 20 N C OXT SING +DLE N 21 N OXT HXT SING +DVA N 1 N N CA SING +DVA N 2 N N H SING +DVA N 3 N N H2 SING +DVA N 4 N CA CB SING +DVA N 5 N CA C SING +DVA N 6 N CA HA SING +DVA N 7 N CB CG1 SING +DVA N 8 N CB CG2 SING +DVA N 9 N CB HB SING +DVA N 10 N CG1 HG11 SING +DVA N 11 N CG1 HG12 SING +DVA N 12 N CG1 HG13 SING +DVA N 13 N CG2 HG21 SING +DVA N 14 N CG2 HG22 SING +DVA N 15 N CG2 HG23 SING +DVA N 16 N C O DOUB +DVA N 17 N C OXT SING +DVA N 18 N OXT HXT SING +ETA N 1 N CA N SING +ETA N 2 N CA CB SING +ETA N 3 N CA HA1 SING +ETA N 4 N CA HA2 SING +ETA N 5 N N HN1 SING +ETA N 6 N N HN2 SING +ETA N 7 N CB O SING +ETA N 8 N CB HB1 SING +ETA N 9 N CB HB2 SING +ETA N 10 N O HO SING +FVA N 1 N O C DOUB +FVA N 2 N C CA SING +FVA N 3 N H N SING +FVA N 4 N N CN SING +FVA N 5 N N CA SING +FVA N 6 N CB CA SING +FVA N 7 N CA HA SING +FVA N 8 N HB CB SING +FVA N 9 N CB CG2 SING +FVA N 10 N CB CG1 SING +FVA N 11 N HG13 CG1 SING +FVA N 12 N HG12 CG1 SING +FVA N 13 N CG1 HG11 SING +FVA N 14 N HG22 CG2 SING +FVA N 15 N HG23 CG2 SING +FVA N 16 N CG2 HG21 SING +FVA N 17 N CN O1 DOUB +FVA N 18 N HN CN SING +FVA N 19 N C OXT SING +FVA N 20 N OXT HXT SING +GLY N 1 N N CA SING +GLY N 2 N N H SING +GLY N 3 N N H2 SING +GLY N 4 N CA C SING +GLY N 5 N CA HA2 SING +GLY N 6 N CA HA3 SING +GLY N 7 N C O DOUB +GLY N 8 N C OXT SING +GLY N 9 N OXT HXT SING +TRP N 1 N N CA SING +TRP N 2 N N H SING +TRP N 3 N N H2 SING +TRP N 4 N CA C SING +TRP N 5 N CA CB SING +TRP N 6 N CA HA SING +TRP N 7 N C O DOUB +TRP N 8 N C OXT SING +TRP N 9 N CB CG SING +TRP N 10 N CB HB2 SING +TRP N 11 N CB HB3 SING +TRP N 12 Y CG CD1 DOUB +TRP N 13 Y CG CD2 SING +TRP N 14 Y CD1 NE1 SING +TRP N 15 N CD1 HD1 SING +TRP N 16 Y CD2 CE2 DOUB +TRP N 17 Y CD2 CE3 SING +TRP N 18 Y NE1 CE2 SING +TRP N 19 N NE1 HE1 SING +TRP N 20 Y CE2 CZ2 SING +TRP N 21 Y CE3 CZ3 DOUB +TRP N 22 N CE3 HE3 SING +TRP N 23 Y CZ2 CH2 DOUB +TRP N 24 N CZ2 HZ2 SING +TRP N 25 Y CZ3 CH2 SING +TRP N 26 N CZ3 HZ3 SING +TRP N 27 N CH2 HH2 SING +TRP N 28 N OXT HXT SING +VAL N 1 N N CA SING +VAL N 2 N N H SING +VAL N 3 N N H2 SING +VAL N 4 N CA C SING +VAL N 5 N CA CB SING +VAL N 6 N CA HA SING +VAL N 7 N C O DOUB +VAL N 8 N C OXT SING +VAL N 9 N CB CG1 SING +VAL N 10 N CB CG2 SING +VAL N 11 N CB HB SING +VAL N 12 N CG1 HG11 SING +VAL N 13 N CG1 HG12 SING +VAL N 14 N CG1 HG13 SING +VAL N 15 N CG2 HG21 SING +VAL N 16 N CG2 HG22 SING +VAL N 17 N CG2 HG23 SING +VAL N 18 N OXT HXT SING +# diff --git a/package-lock.json b/package-lock.json index 65edb5b9cf7b351137a82ca276ff669f2ff6c1ec..445a602d57c899a8e98f013ba74f05382363076a 100644 Binary files a/package-lock.json and b/package-lock.json differ diff --git a/package.json b/package.json index ae74e31e7843cfcee96df6862a932c6d13e567e9..3185d9554462f5b524b2e682a7c2708e9e305a68 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { - "name": "molio", + "name": "mol-star", "version": "0.1.0", - "description": "Parsers for molecular data.", + "description": "Comprehensive molecular library.", "main": "dist/molio.js", "module": "dist/molio.esm.js", "types": "src/index.d.ts", @@ -12,8 +12,8 @@ "bundle": "./node_modules/.bin/rollup -c", "test": "./node_modules/.bin/jest", "dist": "./node_modules/.bin/uglifyjs build/js/molio.dev.js -cm > dist/molio.js && cp build/js/molio.esm.js dist/molio.esm.js", - "script": "./node_modules/.bin/rollup build/js/src/script.js -e fs -f cjs -o build/js/script.js", - "runscript": "npm run script && node build/js/script.js", + "script": "./node_modules/.bin/rollup build/node_modules/script.js -e fs -f cjs -o build/js/script.js", + "runscript": "node build/node_modules/script.js", "download-dics": "./node_modules/.bin/download -o build/dics http://mmcif.wwpdb.org/dictionaries/ascii/mmcif_pdbx_v50.dic && ./node_modules/.bin/download -o build/dics http://mmcif.wwpdb.org/dictionaries/ascii/mmcif_ddl.dic" }, "jest": { @@ -24,26 +24,37 @@ "transform": { "\\.ts$": "<rootDir>/node_modules/ts-jest/preprocessor.js" }, + "moduleDirectories": [ + "node_modules", + "build/node_modules" + ], "testRegex": "\\.spec\\.ts$" }, "author": "", "license": "MIT", "devDependencies": { - "@types/jest": "^21.1.2", - "@types/node": "^8.0.34", + "@types/benchmark": "^1.0.30", + "@types/express": "^4.0.39", + "@types/jest": "^21.1.5", + "@types/node": "^8.0.47", + "@types/node-fetch": "^1.6.7", + "benchmark": "^2.1.4", "download-cli": "^1.0.5", "jest": "^21.2.1", "rollup": "^0.50.0", "rollup-plugin-buble": "^0.16.0", - "rollup-plugin-commonjs": "^8.2.1", + "rollup-plugin-commonjs": "^8.2.6", "rollup-plugin-json": "^2.3.0", "rollup-plugin-node-resolve": "^3.0.0", "rollup-watch": "^4.3.1", - "ts-jest": "^21.1.2", - "tslint": "^5.7.0", - "typescript": "^2.5.3", - "uglify-js": "^3.1.3", + "ts-jest": "^21.1.4", + "tslint": "^5.8.0", + "typescript": "^2.6.1", + "uglify-js": "^3.1.7", "util.promisify": "^1.0.0" }, - "dependencies": {} + "dependencies": { + "express": "^4.16.2", + "node-fetch": "^1.7.3" + } } diff --git a/src/apps/cif2bcif/converter.ts b/src/apps/cif2bcif/converter.ts new file mode 100644 index 0000000000000000000000000000000000000000..b663a301e60b8a7119875dbfac6b9fd3e850efaa --- /dev/null +++ b/src/apps/cif2bcif/converter.ts @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import Iterator from 'mol-data/iterator' +import CIF, { Category } from 'mol-io/reader/cif' +import * as Encoder from 'mol-io/writer/cif' +import * as fs from 'fs' +import classify from './field-classifier' + +async function getCIF(path: string) { + const str = fs.readFileSync(path, 'utf8'); + const parsed = await CIF.parseText(str)(); + if (parsed.isError) { + throw new Error(parsed.toString()); + } + return parsed.result; +} + +function createDefinition(cat: Category): Encoder.CategoryDefinition { + return { + name: cat.name, + fields: cat.fieldNames.map(n => classify(n, cat.getField(n)!)) + } +} + +function getCategoryInstanceProvider(cat: Category): Encoder.CategoryProvider { + return function (ctx: any) { + return { + data: cat, + definition: createDefinition(cat), + keys: () => Iterator.Range(0, cat.rowCount - 1), + rowCount: cat.rowCount + }; + } +} + +export default async function convert(path: string, asText = false) { + const cif = await getCIF(path); + + const encoder = Encoder.create({ binary: !asText, encoderName: 'mol* cif2bcif' }); + for (const b of cif.blocks) { + encoder.startDataBlock(b.header); + for (const c of b.categoryNames) { + encoder.writeCategory(getCategoryInstanceProvider(b.categories[c])); + } + } + return encoder.getData(); +} + diff --git a/src/apps/cif2bcif/field-classifier.ts b/src/apps/cif2bcif/field-classifier.ts new file mode 100644 index 0000000000000000000000000000000000000000..145cdc6f9b0dd5e99e0d54ba7aa5839ed22e1d5e --- /dev/null +++ b/src/apps/cif2bcif/field-classifier.ts @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { Column } from 'mol-data/db' +import { Field } from 'mol-io/reader/cif/data-model' +import { FieldDefinition, FieldType } from 'mol-io/writer/cif/encoder' + +const intRegex = /^-?\d+$/ +const floatRegex = /^-?(([0-9]+)[.]?|([0-9]*[.][0-9]+))([(][0-9]+[)])?([eE][+-]?[0-9]+)?/ + +function classify(name: string, field: Field): FieldDefinition { + let floatCount = 0, hasString = false; + for (let i = 0, _i = field.rowCount; i < _i; i++) { + const k = field.valueKind(i); + if (k !== Column.ValueKind.Present) continue; + const v = field.str(i); + if (intRegex.test(v)) continue; + else if (floatRegex.test(v)) floatCount++; + else { hasString = true; break; } + } + + if (hasString) return { name, type: FieldType.Str, value: field.str, valueKind: field.valueKind }; + if (floatCount > 0) return { name, type: FieldType.Float, value: field.float, valueKind: field.valueKind }; + return { name, type: FieldType.Int, value: field.int, valueKind: field.valueKind }; +} + +export default classify; \ No newline at end of file diff --git a/src/apps/cif2bcif/index.ts b/src/apps/cif2bcif/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..986b9ab7c9916a248a36c7a8af8962960e7804ce --- /dev/null +++ b/src/apps/cif2bcif/index.ts @@ -0,0 +1,20 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import * as fs from 'fs' +import convert from './converter' + +(async function () { + if (process.argv.length !== 4) { + console.log('Usage:\nnode cif2bcif input.cif output.bcif'); + return; + } + const src = process.argv[2]; + const out = process.argv[3]; + + const res = await convert(src); + fs.writeFileSync(out, res); +}()); \ No newline at end of file diff --git a/src/apps/domain-annotation-server/mapping.ts b/src/apps/domain-annotation-server/mapping.ts new file mode 100644 index 0000000000000000000000000000000000000000..da87d9b4698b6e8db3014c4a8dd52e8d0717095e --- /dev/null +++ b/src/apps/domain-annotation-server/mapping.ts @@ -0,0 +1,117 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { Table } from 'mol-data/db' +import { CIFEncoder, create as createEncoder } from 'mol-io/writer/cif' +import * as S from './schemas' +import { getCategoryInstanceProvider } from './utils' + +export default function create(allData: any) { + const mols = Object.keys(allData); + const enc = createEncoder(); + enc.startDataBlock(mols[0]); + + if (!mols.length) return enc.getData(); + + const data = allData[mols[0]]; + + const sources = getSources(data); + if (!sources._rowCount) return enc.getData(); + + enc.writeCategory(getCategoryInstanceProvider(`pdbx_domain_annotation_sources`, sources)); + + for (const cat of Object.keys(S.categories)) { + writeDomain(enc, getDomain(cat, (S.categories as any)[cat], data)); + } + return enc.getData(); +} + +interface DomainAnnotation { + name: string, + domains: Table<any>, + mappings: Table<S.mapping> +} +type MappingRow = Table.Row<S.mapping>; + +function writeDomain(enc: CIFEncoder<any>, domain: DomainAnnotation | undefined) { + if (!domain) return; + enc.writeCategory(getCategoryInstanceProvider(`pdbx_${domain.name}_domain_annotation`, domain.domains)); + enc.writeCategory(getCategoryInstanceProvider(`pdbx_${domain.name}_domain_mapping`, domain.mappings)); +} + +function getSources(data: any): Table<S.Sources> { + const rows: Table.Row<S.Sources>[] = []; + for (const name of Object.keys(S.categories)) { + if (!data[name]) continue; + const row: Table.Row<S.Sources> = { id: name, count: Object.keys(data[name]).length }; + if (row.count > 0) rows.push(row); + } + return Table.ofRows(S.Sources, rows); +} + +function getMappings(startId: number, group_id: number, mappings: any): MappingRow[] { + const rows: MappingRow[] = []; + + const n = (v: any) => v === null ? void 0 : v; + + for (const entry of mappings) { + if (entry.start && entry.end) { + rows.push({ + id: startId++, + group_id, + label_entity_id: '' + entry.entity_id, + label_asym_id: entry.struct_asym_id, + auth_asym_id: entry.chain_id, + beg_label_seq_id: n(entry.start.residue_number), + beg_auth_seq_id: n(entry.start.author_residue_number), + pdbx_beg_PDB_ins_code: entry.start.author_insertion_code, + end_label_seq_id: n(entry.end.residue_number), + end_auth_seq_id: n(entry.end.author_residue_number), + pdbx_end_PDB_ins_code: entry.end.author_insertion_code + }); + } else { + rows.push({ + id: startId++, + group_id, + label_entity_id: '' + entry.entity_id, + label_asym_id: entry.struct_asym_id, + auth_asym_id: entry.chain_id + } as any); + } + } + return rows; +} + +function getDomainInfo(id: string, mapping_group_id: number, data: any, schema: any) { + const props = Object.create(null); + for (const k of Object.keys(schema)) props[k] = data[k]; + return { id, mapping_group_id, identifier: data.identifier, ...props }; +} + +function getDomain(name: string, schema: any, allData: any) { + if (!allData[name]) return void 0; + + const data = allData[name]; + + const domains: any[] = []; + const mappings: MappingRow[] = []; + + let mappingSerialId = 1, mapping_group_id = 1; + + for (const id of Object.keys(data)) { + const domain = data[id]; + domains.push(getDomainInfo(id, mapping_group_id, domain, schema)); + mappings.push(...getMappings(mappingSerialId, mapping_group_id, domain.mappings)); + mappingSerialId = mappings.length + 1; + mapping_group_id++; + } + + return domains.length > 0 ? { + name, + domains: Table.ofRows({ ...S.Base, ...schema }, domains), + mappings: Table.ofRows(S.mapping, mappings) + } : void 0; +} \ No newline at end of file diff --git a/src/apps/domain-annotation-server/schemas.ts b/src/apps/domain-annotation-server/schemas.ts new file mode 100644 index 0000000000000000000000000000000000000000..e6caffdac60c92bc2d8f364b0f89120edf011941 --- /dev/null +++ b/src/apps/domain-annotation-server/schemas.ts @@ -0,0 +1,95 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { Column } from 'mol-data/db' + +import Type = Column.Schema + +export const Sources = { + id: Type.str, + count: Type.int +} +export type Sources = typeof Sources + +export const Base = { + id: Type.str, + identifier: Type.str, + mapping_group_id: Type.int +} +export type Base = typeof Base + +export const mapping = { + id: Type.int, + group_id: Type.int, + + label_entity_id: Type.str, + label_asym_id: Type.str, + auth_asym_id: Type.str, + + beg_label_seq_id: Type.int, + beg_auth_seq_id: Type.int, + pdbx_beg_PDB_ins_code: Type.str, + + end_label_seq_id: Type.int, + end_auth_seq_id: Type.int, + pdbx_end_PDB_ins_code: Type.str +} +export type mapping = typeof mapping + +export const Pfam = { + description: Type.str +} +export type Pfam = typeof Pfam + +export const InterPro = { + name: Type.str +} +export type InterPro = typeof InterPro + +export const CATH = { + name: Type.str, + homology: Type.str, + architecture: Type.str, + identifier: Type.str, + class: Type.str, + topology: Type.str, +} +export type CATH = typeof CATH + +export const EC = { + accepted_name: Type.str, + reaction: Type.str, + systematic_name: Type.str +} +export type EC = typeof EC + +export const UniProt = { + name: Type.str +} +export type UniProt = typeof UniProt + +export const SCOP = { + sccs: Type.str, + description: Type.str +} +export type SCOP = typeof SCOP + +export const GO = { + category: Type.str, + definition: Type.str, + name: Type.str +} +export type GO = typeof GO + +export const categories = { + Pfam, + InterPro, + CATH, + EC, + UniProt, + SCOP, + GO +} \ No newline at end of file diff --git a/src/apps/domain-annotation-server/server.ts b/src/apps/domain-annotation-server/server.ts new file mode 100644 index 0000000000000000000000000000000000000000..1d4f192cad0713c96b559e427535986532c8dc54 --- /dev/null +++ b/src/apps/domain-annotation-server/server.ts @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import * as express from 'express' +import fetch from 'node-fetch' +import createMapping from './mapping' + +async function getMappings(id: string) { + const data = await fetch(`https://www.ebi.ac.uk/pdbe/api/mappings/${id}`); + const json = await data.json(); + return createMapping(json); +}; + + +let PORT = process.env.port || 1338; + +const app = express(); + +const PREFIX = '/' + +app.get(`${PREFIX}/:id`, async (req, res) => { + try { + console.log('Requesting ' + req.params.id); + const mapping = await getMappings(req.params.id); + res.writeHead(200, { + 'Content-Type': 'text/plain; charset=utf-8', + 'Access-Control-Allow-Origin': '*', + 'Access-Control-Allow-Headers': 'X-Requested-With' + }); + res.end(mapping); + } catch { + console.log('Failed ' + req.params.id); + res.writeHead(404, { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Headers': 'X-Requested-With' }); + res.end(); + } +}); + +app.get(`${PREFIX}`, (req, res) => { + res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' }); + res.end('Usage: /pdb_id, e.g. /1tqn'); +}) + +app.listen(PORT); + +console.log('Running on port ' + PORT); \ No newline at end of file diff --git a/src/apps/domain-annotation-server/test.ts b/src/apps/domain-annotation-server/test.ts new file mode 100644 index 0000000000000000000000000000000000000000..66911dec6ce7484f2bd24bc3784b126cbe974259 --- /dev/null +++ b/src/apps/domain-annotation-server/test.ts @@ -0,0 +1,14 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import fetch from 'node-fetch' +import createMapping from './mapping' + +(async function () { + const data = await fetch('https://www.ebi.ac.uk/pdbe/api/mappings/1tqn?pretty=true'); + const json = await data.json(); + console.log(createMapping(json)); +}()); \ No newline at end of file diff --git a/src/apps/domain-annotation-server/utils.ts b/src/apps/domain-annotation-server/utils.ts new file mode 100644 index 0000000000000000000000000000000000000000..de3e40b92592a2995294eb77eca27dafdcbe555d --- /dev/null +++ b/src/apps/domain-annotation-server/utils.ts @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { Table } from 'mol-data/db' +import Iterator from 'mol-data/iterator' +import * as Encoder from 'mol-io/writer/cif' + +function columnValue(k: string) { + return (i: number, d: any) => d[k].value(i); +} + +function columnValueKind(k: string) { + return (i: number, d: any) => d[k].valueKind(i); +} + +function ofSchema(schema: Table.Schema) { + const fields: Encoder.FieldDefinition[] = []; + for (const k of Object.keys(schema)) { + const t = schema[k]; + const type: any = t.valueType === 'str' ? Encoder.FieldType.Str : t.valueType === 'int' ? Encoder.FieldType.Int : Encoder.FieldType.Float; + fields.push({ name: k, type, value: columnValue(k), valueKind: columnValueKind(k) }) + } + return fields; +} + +function ofTable<S extends Table.Schema>(name: string, table: Table<S>): Encoder.CategoryDefinition<number> { + return { name, fields: ofSchema(table._schema) } +} + +export function getCategoryInstanceProvider(name: string, table: Table<any>): Encoder.CategoryProvider { + return () => { + return { + data: table, + definition: ofTable(name, table), + keys: () => Iterator.Range(0, table._rowCount - 1), + rowCount: table._rowCount + }; + } +} diff --git a/src/index.d.ts b/src/index.d.ts deleted file mode 100644 index 8b44890300fb37efb32f0be8cbce0d9cf052fc6c..0000000000000000000000000000000000000000 --- a/src/index.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright (c) 2017 molio contributors, licensed under MIT, See LICENSE file for more info. - * - * @author Alexander Rose <alexander.rose@weirdbyte.de> - */ - -// TODO: fix me \ No newline at end of file diff --git a/src/index.ts b/src/index.ts deleted file mode 100644 index e20061a1285f910b291863a4776f1663a706478f..0000000000000000000000000000000000000000 --- a/src/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Copyright (c) 2017 molio contributors, licensed under MIT, See LICENSE file for more info. - * - * @author Alexander Rose <alexander.rose@weirdbyte.de> - */ - -// TODO: fix me diff --git a/src/mol-data/_spec/equiv-index.spec.ts b/src/mol-data/_spec/equiv-index.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..9083c0145d0258be5815f9bc0c6f49c9effa5062 --- /dev/null +++ b/src/mol-data/_spec/equiv-index.spec.ts @@ -0,0 +1,18 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import EquivalenceClasses from '../util/equivalence-classes' + +describe('equiv-classes', () => { + it('integer mod classes', () => { + const cls = EquivalenceClasses<number, number>(x => x % 2, (a, b) => (a - b) % 2 === 0); + for (let i = 0; i < 6; i++) cls.add(i, i); + + expect(cls.groups.length).toBe(2); + expect(cls.groups[0]).toEqual([0, 2, 4]); + expect(cls.groups[1]).toEqual([1, 3, 5]); + }); +}); \ No newline at end of file diff --git a/src/mol-data/_spec/iterators.spec.ts b/src/mol-data/_spec/iterators.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..a2e9a353650010c377c64699c9e47ab19fcc7556 --- /dev/null +++ b/src/mol-data/_spec/iterators.spec.ts @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import Iterator from '../iterator' + +function iteratorToArray<T>(it: Iterator<T>): T[] { + const ret = []; + while (it.hasNext) { + const v = it.move(); + ret[ret.length] = v; + } + return ret; +} + +describe('basic iterators', () => { + function check<T>(name: string, iter: Iterator<T>, expected: T[]) { + it(name, () => { + expect(iteratorToArray(iter)).toEqual(expected); + }); + } + + check('empty', Iterator.Empty, []); + check('singleton', Iterator.Value(10), [10]); + check('array', Iterator.Array([1, 2, 3]), [1, 2, 3]); + check('range', Iterator.Range(0, 3), [0, 1, 2, 3]); + check('map', Iterator.map(Iterator.Range(0, 1), x => x + 1), [1, 2]); + check('filter', Iterator.filter(Iterator.Range(0, 3), x => x >= 2), [2, 3]); +}); \ No newline at end of file diff --git a/src/mol-data/_spec/sort.spec.ts b/src/mol-data/_spec/sort.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..9d2a122ccfead9d33a783df1a05c7c21e3cc8d06 --- /dev/null +++ b/src/mol-data/_spec/sort.spec.ts @@ -0,0 +1,90 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import * as Sort from '../util/sort' + +function shuffle<T>(data: T, len: number, clone: (s: T) => T, swap: Sort.Swapper = Sort.arraySwap) { + const a = clone(data); + for (let i = len - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + swap(a, i, j); + } + return a; +} + +function shuffleArray(data: any[]) { + return shuffle(data, data.length, t => [...t]); +} + +describe('qsort-array asc', () => { + const data0 = new Array(50); + for (let i = 0; i < data0.length; i++) data0[i] = i; + const data1 = [1, 1, 2, 2, 3, 3, 4, 4, 4, 6, 6, 6]; + + function test(name: string, data: any[], randomize: boolean) { + it(name, () => { + // [ 3, 1, 6, 4, 4, 6, 4, 2, 6, 1, 2, 3 ]; + if (randomize) { + for (let i = 0; i < 10; i++) { + expect(Sort.sortArray(shuffleArray(data))).toEqual(data); + } + } else { + expect(Sort.sortArray([...data])).toEqual(data); + } + }); + } + test('uniq', data0, false); + test('uniq shuffle', data0, true); + test('rep', data1, false); + test('rep shuffle', data1, true); +}) + +describe('qsort-array generic', () => { + const data0 = new Array(50); + for (let i = 0; i < data0.length; i++) data0[i] = i; + const data1 = [1, 1, 2, 2, 3, 3, 4, 4, 4, 6, 6, 6]; + + function test(name: string, data: any[], randomize: boolean) { + it(name, () => { + // [ 3, 1, 6, 4, 4, 6, 4, 2, 6, 1, 2, 3 ]; + if (randomize) { + for (let i = 0; i < 10; i++) { + expect(Sort.sort(shuffleArray(data), 0, data.length, Sort.arrayLess, Sort.arraySwap)).toEqual(data); + } + } else { + expect(Sort.sort([...data], 0, data.length, Sort.arrayLess, Sort.arraySwap)).toEqual(data); + } + }); + } + test('uniq', data0, false); + test('uniq shuffle', data0, true); + test('rep', data1, false); + test('rep shuffle', data1, true); +}) + +describe('qsort-dual array', () => { + const len = 3; + const data = { xs: [0, 1, 2], ys: ['x', 'y', 'z'] }; + + const cmp: Sort.Comparer<typeof data> = (data, i, j) => data.xs[i] - data.xs[j]; + const swap: Sort.Swapper<typeof data> = (data, i, j) => { Sort.arraySwap(data.xs, i, j); Sort.arraySwap(data.ys, i, j); } + const clone = (d: typeof data) => ({ xs: [...d.xs], ys: [...d.ys] }) + + function test(name: string, src: typeof data, randomize: boolean) { + it(name, () => { + // [ 3, 1, 6, 4, 4, 6, 4, 2, 6, 1, 2, 3 ]; + if (randomize) { + for (let i = 0; i < 10; i++) { + expect(Sort.sort(shuffle(src, len, clone, swap), 0, len, cmp, swap)).toEqual(data); + } + } else { + expect(Sort.sort(clone(src), 0, len, cmp, swap)).toEqual(data); + } + }); + } + test('sorted', data, false); + test('shuffled', data, true); +}) \ No newline at end of file diff --git a/src/mol-data/db.ts b/src/mol-data/db.ts new file mode 100644 index 0000000000000000000000000000000000000000..367b533a0c7f22c7ce359d3112ebae25a23007f5 --- /dev/null +++ b/src/mol-data/db.ts @@ -0,0 +1,12 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import Database from './db/database' +import Table from './db/table' +import Column from './db/column' +import * as ColumnHelpers from './db/column-helpers' + +export { Database, Table, Column, ColumnHelpers } \ No newline at end of file diff --git a/src/mol-data/db/_spec/table.spec.ts b/src/mol-data/db/_spec/table.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..51965938a4597c63847e02ee1a0d14a814d55e31 --- /dev/null +++ b/src/mol-data/db/_spec/table.spec.ts @@ -0,0 +1,122 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import * as ColumnHelpers from '../column-helpers' +import Column from '../column' +import Table from '../table' + +describe('column', () => { + const cc = Column.ofConst(10, 2, Column.Schema.int); + const arr = Column.ofArray({ array: [1, 2, 3, 4], schema: Column.Schema.int }); + const arrWindow = Column.window(arr, 1, 3); + + const typed = Column.ofArray({ array: new Int32Array([1, 2, 3, 4]), schema: Column.Schema.int }); + const typedWindow = Column.window(typed, 1, 3); + + const numStr = Column.ofArray({ array: [1, 2] as any, schema: Column.Schema.str }); + + it('constant', () => { + expect(cc.rowCount).toBe(2); + expect(cc.value(0)).toBe(10); + }); + + it('arr', () => { + expect(arr.rowCount).toBe(4); + expect(arr.value(1)).toBe(2); + expect(arrWindow.value(0)).toBe(2); + expect(arrWindow.rowCount).toBe(2); + }); + + it('typed', () => { + expect(typedWindow.value(0)).toBe(2); + expect(typedWindow.rowCount).toBe(2); + expect(ColumnHelpers.isTypedArray(typedWindow.toArray())).toBe(true); + }); + + it('numStr', () => { + expect(numStr.value(0)).toBe('1'); + expect(numStr.toArray()).toEqual(['1', '2']); + }); + + it('view', () => { + expect(Column.view(arr, [1, 0, 3, 2]).toArray()).toEqual([2, 1, 4, 3]); + expect(Column.view(arr, [1, 3]).toArray()).toEqual([2, 4]); + }); + + it('map to array', () => { + expect(Column.mapToArray(arrWindow, x => x + 1)).toEqual([3, 4]); + }); +}) + +describe('table', () => { + const schema = { + x: Column.Schema.int, + n: Column.Schema.str + }; + + it('ofRows', () => { + const t = Table.ofRows(schema, [ + { x: 10, n: 'row1' }, + { x: -1, n: 'row2' }, + ]); + expect(t.x.toArray()).toEqual([10, -1]); + expect(t.n.toArray()).toEqual(['row1', 'row2']); + }); + + it('ofColumns', () => { + const t = Table.ofColumns(schema, { + x: Column.ofArray({ array: [10, -1], schema: Column.Schema.int }), + n: Column.ofArray({ array: ['row1', 'row2'], schema: Column.Schema.str }), + }); + expect(t.x.toArray()).toEqual([10, -1]); + expect(t.n.toArray()).toEqual(['row1', 'row2']); + }); + + it('ofArrays', () => { + const t = Table.ofArrays(schema, { + x: [10, -1], + n: ['row1', 'row2'], + }); + expect(t.x.toArray()).toEqual([10, -1]); + expect(t.n.toArray()).toEqual(['row1', 'row2']); + }); + + it('pickColumns', () => { + const t = Table.ofColumns(schema, { + x: Column.ofArray({ array: [10, -1], schema: Column.Schema.int }), + n: Column.ofArray({ array: ['row1', 'row2'], schema: Column.Schema.str }), + }); + const s = { x: Column.Schema.int, y: Column.Schema.int }; + const picked = Table.pickColumns(s, t, { y: Column.ofArray({ array: [3, 4], schema: Column.Schema.int })}); + expect(picked._columns).toEqual(['x', 'y']); + expect(picked._rowCount).toEqual(2); + expect(picked.x.toArray()).toEqual([10, -1]); + expect(picked.y.toArray()).toEqual([3, 4]); + }); + + it('view', () => { + const t = Table.ofColumns(schema, { + x: Column.ofArray({ array: [10, -1], schema: Column.Schema.int }), + n: Column.ofArray({ array: ['row1', 'row2'], schema: Column.Schema.str }), + }); + const s = { x: Column.Schema.int }; + const view = Table.view(t, s, [1]); + expect(view._columns).toEqual(['x']); + expect(view._rowCount).toEqual(1); + expect(view.x.toArray()).toEqual([-1]); + }); + + it('sort', () => { + const t = Table.ofColumns<typeof schema>(schema, { + x: Column.ofArray({ array: [10, -1], schema: Column.Schema.int }), + n: Column.ofArray({ array: ['row1', 'row2'], schema: Column.Schema.str }), + }); + const { x } = t; + const sorted = Table.sort(t, (i, j) => x.value(i) - x.value(j)) + expect(sorted.x.toArray()).toEqual([-1, 10]); + expect(sorted.n.toArray()).toEqual(['row2', 'row1']); + }); +}); \ No newline at end of file diff --git a/src/mol-data/db/column-helpers.ts b/src/mol-data/db/column-helpers.ts new file mode 100644 index 0000000000000000000000000000000000000000..b1645fb061f9346e4ebfd8f3bb45c1e27397e435 --- /dev/null +++ b/src/mol-data/db/column-helpers.ts @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import Column from './column' + +export function getArrayBounds(rowCount: number, params?: Column.ToArrayParams<any>) { + const start = params && typeof params.start !== 'undefined' ? Math.max(Math.min(params.start, rowCount - 1), 0) : 0; + const end = params && typeof params.end !== 'undefined' ? Math.min(params.end, rowCount) : rowCount; + return { start, end }; +} + +export function createArray(rowCount: number, params?: Column.ToArrayParams<any>) { + const c = params && typeof params.array !== 'undefined' ? params.array : Array; + const { start, end } = getArrayBounds(rowCount, params); + return { array: new c(end - start) as any[], start, end }; +} + +export function fillArrayValues(value: (row: number) => any, target: any[], start: number) { + for (let i = 0, _e = target.length; i < _e; i++) target[i] = value(start + i); + return target; +} + +export function createAndFillArray(rowCount: number, value: (row: number) => any, params?: Column.ToArrayParams<any>) { + const { array, start } = createArray(rowCount, params); + return fillArrayValues(value, array, start); +} + +export function isTypedArray(data: any): boolean { + return !!data.buffer && typeof data.byteLength === 'number' && typeof data.BYTES_PER_ELEMENT === 'number'; +} + +export function typedArrayWindow(data: any, params?: Column.ToArrayParams<any>): ReadonlyArray<number> { + const { constructor, buffer, length, byteOffset, BYTES_PER_ELEMENT } = data; + const { start, end } = getArrayBounds(length, params); + if (start === 0 && end === length) return data; + return new constructor(buffer, byteOffset + BYTES_PER_ELEMENT * start, Math.min(length, end - start)); +} \ No newline at end of file diff --git a/src/mol-data/db/column.ts b/src/mol-data/db/column.ts new file mode 100644 index 0000000000000000000000000000000000000000..54fe666de2589facbd59bc5107c4063a3d8cc194 --- /dev/null +++ b/src/mol-data/db/column.ts @@ -0,0 +1,336 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import * as ColumnHelpers from './column-helpers' +import { Tensor as Tensors } from 'mol-math/linear-algebra' + +interface Column<T> { + readonly schema: Column.Schema, + readonly '@array': ArrayLike<any> | undefined, + + readonly isDefined: boolean, + readonly rowCount: number, + value(row: number): T, + valueKind(row: number): Column.ValueKind, + toArray(params?: Column.ToArrayParams<T>): ArrayLike<T>, + areValuesEqual(rowA: number, rowB: number): boolean +} + +namespace Column { + export type ArrayCtor<T> = { new(size: number): ArrayLike<T> } + + export type Schema<T = any> = Schema.Str | Schema.Int | Schema.Float | Schema.Coordinate | Schema.Aliased<T> | Schema.Tensor + + export namespace Schema { + // T also serves as a default value for undefined columns + + type Base<T extends string> = { valueType: T } + export type Str = { '@type': 'str', T: string } & Base<'str'> + export type Int = { '@type': 'int', T: number } & Base<'int'> + export type Float = { '@type': 'float', T: number } & Base<'float'> + export type Coordinate = { '@type': 'coord', T: number } & Base<'float'> + + export type Tensor = { '@type': 'tensor', T: Tensors, space: Tensors.Space } & Base<'tensor'> + export type Aliased<T> = { '@type': 'aliased', T: T } & Base<'str' | 'int'> + + export const str: Str = { '@type': 'str', T: '', valueType: 'str' }; + export const int: Int = { '@type': 'int', T: 0, valueType: 'int' }; + export const coord: Coordinate = { '@type': 'coord', T: 0, valueType: 'float' }; + export const float: Float = { '@type': 'float', T: 0, valueType: 'float' }; + + export function Str(defaultValue = ''): Str { return { '@type': 'str', T: defaultValue, valueType: 'str' } }; + export function Int(defaultValue = 0): Int { return { '@type': 'int', T: defaultValue, valueType: 'int' } }; + export function Float(defaultValue = 0): Float { return { '@type': 'float', T: defaultValue, valueType: 'float' } }; + export function Tensor(space: Tensors.Space): Tensor { return { '@type': 'tensor', T: space.create(), space, valueType: 'tensor' }; } + export function Vector(dim: number): Tensor { return Tensor(Tensors.Vector(dim)); } + export function Matrix(rows: number, cols: number): Tensor { return Tensor(Tensors.ColumnMajorMatrix(rows, cols)); } + + export function Aliased<T>(t: Str | Int, defaultValue?: T): Aliased<T> { + if (typeof defaultValue !== 'undefined') return { ...t, T: defaultValue } as any as Aliased<T>; + return t as any as Aliased<T>; + } + } + + export interface ToArrayParams<T> { + array?: ArrayCtor<T>, + start?: number, + /** Last row (exclusive) */ + end?: number + } + + export interface LambdaSpec<T extends Schema> { + value: (row: number) => T['T'], + rowCount: number, + schema: T, + valueKind?: (row: number) => ValueKind, + } + + export interface ArraySpec<T extends Schema> { + array: ArrayLike<T['T']>, + schema: T, + valueKind?: (row: number) => ValueKind + } + + export interface MapSpec<S extends Schema, T extends Schema> { + f: (v: S['T']) => T['T'], + schema: T, + valueKind?: (row: number) => ValueKind, + } + + export const enum ValueKind { Present = 0, NotPresent = 1, Unknown = 2 } + + export function Undefined<T extends Schema>(rowCount: number, schema: T): Column<T['T']> { + return constColumn(schema['T'], rowCount, schema, ValueKind.NotPresent); + } + + export function ofConst<T extends Schema>(v: T['T'], rowCount: number, type: T): Column<T['T']> { + return constColumn(v, rowCount, type, ValueKind.Present); + } + + export function ofLambda<T extends Schema>(spec: LambdaSpec<T>): Column<T['T']> { + return lambdaColumn(spec); + } + + export function ofArray<T extends Column.Schema>(spec: Column.ArraySpec<T>): Column<T['T']> { + return arrayColumn(spec); + } + + export function ofIntArray(array: ArrayLike<number>) { + return arrayColumn({ array, schema: Schema.int }); + } + + export function ofFloatArray(array: ArrayLike<number>) { + return arrayColumn({ array, schema: Schema.float }); + } + + export function ofStringArray(array: ArrayLike<string>) { + return arrayColumn({ array, schema: Schema.str }); + } + + export function window<T>(column: Column<T>, start: number, end: number) { + return windowColumn(column, start, end); + } + + export function view<T>(column: Column<T>, indices: ArrayLike<number>, checkIndentity = true) { + return columnView(column, indices, checkIndentity); + } + + /** A map of the 1st occurence of each value. */ + export function createFirstIndexMap<T>(column: Column<T>) { + return createFirstIndexMapOfColumn(column); + } + + export function mapToArray<T, S>(column: Column<T>, f: (v: T) => S, ctor?: ArrayCtor<S>): ArrayLike<S> { + return mapToArrayImpl(column, f, ctor || Array); + } + + export function areEqual<T>(a: Column<T>, b: Column<T>) { + return areColumnsEqual(a, b); + } + + export function indicesOf<T>(c: Column<T>, test: (e: T) => boolean) { + return columnIndicesOf(c, test); + } + + /** Makes the column backned by an array. Useful for columns that accessed often. */ + export function asArrayColumn<T>(c: Column<T>, array?: ArrayCtor<T>): Column<T> { + if (c['@array']) return c; + if (!c.isDefined) return Undefined(c.rowCount, c.schema) as any as Column<T>; + return arrayColumn({ array: c.toArray({ array }), schema: c.schema, valueKind: c.valueKind }); + } +} + +export default Column; + +function createFirstIndexMapOfColumn<T>(c: Column<T>): Map<T, number> { + const map = new Map<T, number>(); + for (let i = 0, _i = c.rowCount; i < _i; i++) { + const v = c.value(i); + if (!map.has(v)) return map.set(c.value(i), i); + } + return map; +} + +function constColumn<T extends Column.Schema>(v: T['T'], rowCount: number, schema: T, valueKind: Column.ValueKind): Column<T['T']> { + const value: Column<T['T']>['value'] = row => v; + return { + schema: schema, + '@array': void 0, + isDefined: valueKind === Column.ValueKind.Present, + rowCount, + value, + valueKind: row => valueKind, + toArray: params => { + const { array } = ColumnHelpers.createArray(rowCount, params); + for (let i = 0, _i = array.length; i < _i; i++) array[i] = v; + return array; + }, + areValuesEqual: (rowA, rowB) => true + } +} + +function lambdaColumn<T extends Column.Schema>({ value, valueKind, rowCount, schema }: Column.LambdaSpec<T>): Column<T['T']> { + return { + schema: schema, + '@array': void 0, + isDefined: true, + rowCount, + value, + valueKind: valueKind ? valueKind : row => Column.ValueKind.Present, + toArray: params => { + const { array, start } = ColumnHelpers.createArray(rowCount, params); + for (let i = 0, _i = array.length; i < _i; i++) array[i] = value(i + start); + return array; + }, + areValuesEqual: (rowA, rowB) => value(rowA) === value(rowB) + } +} + +function arrayColumn<T extends Column.Schema>({ array, schema, valueKind }: Column.ArraySpec<T>): Column<T['T']> { + const rowCount = array.length; + const value: Column<T['T']>['value'] = schema.valueType === 'str' + ? row => { const v = array[row]; return typeof v === 'string' ? v : '' + v; } + : row => array[row]; + + const isTyped = ColumnHelpers.isTypedArray(array); + return { + schema: schema, + '@array': array, + isDefined: true, + rowCount, + value, + valueKind: valueKind ? valueKind : row => Column.ValueKind.Present, + toArray: schema.valueType === 'str' + ? params => { + const { start, end } = ColumnHelpers.getArrayBounds(rowCount, params); + const ret = new (params && typeof params.array !== 'undefined' ? params.array : (array as any).constructor)(end - start) as any; + for (let i = 0, _i = end - start; i < _i; i++) { + const v = array[start + i]; + ret[i] = typeof v === 'string' ? v : '' + v; + } + return ret; + } + : isTyped + ? params => ColumnHelpers.typedArrayWindow(array, params) as any as ReadonlyArray<T> + : params => { + const { start, end } = ColumnHelpers.getArrayBounds(rowCount, params); + if (start === 0 && end === array.length) return array as ReadonlyArray<T['T']>; + const ret = new (params && typeof params.array !== 'undefined' ? params.array : (array as any).constructor)(end - start) as any; + for (let i = 0, _i = end - start; i < _i; i++) ret[i] = array[start + i]; + return ret; + }, + areValuesEqual: (rowA, rowB) => array[rowA] === array[rowB] + } +} + +function windowColumn<T>(column: Column<T>, start: number, end: number) { + if (!column.isDefined) return Column.Undefined(end - start, column.schema); + if (!!column['@array'] && ColumnHelpers.isTypedArray(column['@array'])) return windowTyped(column, start, end); + return windowFull(column, start, end); +} + +function windowTyped<T>(c: Column<T>, start: number, end: number): Column<T> { + const array = ColumnHelpers.typedArrayWindow(c['@array'], { start, end }); + return arrayColumn({ array, schema: c.schema, valueKind: c.valueKind }) as any; +} + +function windowFull<T>(c: Column<T>, start: number, end: number): Column<T> { + const v = c.value, vk = c.valueKind, ave = c.areValuesEqual; + const value: Column<T>['value'] = start === 0 ? v : row => v(row + start); + const rowCount = end - start; + return { + schema: c.schema, + '@array': void 0, + isDefined: c.isDefined, + rowCount, + value, + valueKind: start === 0 ? vk : row => vk(row + start), + toArray: params => { + const { array } = ColumnHelpers.createArray(rowCount, params); + for (let i = 0, _i = array.length; i < _i; i++) array[i] = v(i + start); + return array; + }, + areValuesEqual: start === 0 ? ave : (rowA, rowB) => ave(rowA + start, rowB + start) + }; +} + +function isIdentity(map: ArrayLike<number>, rowCount: number) { + if (map.length !== rowCount) return false; + for (let i = 0, _i = map.length; i < _i; i++) { + if (map[i] !== i) return false; + } + return true; +} + +function columnView<T>(c: Column<T>, map: ArrayLike<number>, checkIdentity: boolean): Column<T> { + if (!c.isDefined) return c; + if (checkIdentity && isIdentity(map, c.rowCount)) return c; + if (!!c['@array']) return arrayView(c, map); + return viewFull(c, map); +} + +function arrayView<T>(c: Column<T>, map: ArrayLike<number>): Column<T> { + const array = c['@array']!; + const ret = new (array as any).constructor(map.length); + for (let i = 0, _i = map.length; i < _i; i++) ret[i] = array[map[i]]; + return arrayColumn({ array: ret, schema: c.schema, valueKind: c.valueKind }); +} + +function viewFull<T>(c: Column<T>, map: ArrayLike<number>): Column<T> { + const v = c.value, vk = c.valueKind, ave = c.areValuesEqual; + const value: Column<T>['value'] = row => v(map[row]); + const rowCount = map.length; + return { + schema: c.schema, + '@array': void 0, + isDefined: c.isDefined, + rowCount, + value, + valueKind: row => vk(map[row]), + toArray: params => { + const { array } = ColumnHelpers.createArray(rowCount, params); + for (let i = 0, _i = array.length; i < _i; i++) array[i] = v(map[i]); + return array; + }, + areValuesEqual: (rowA, rowB) => ave(map[rowA], map[rowB]) + }; +} + +function mapToArrayImpl<T, S>(c: Column<T>, f: (v: T) => S, ctor: Column.ArrayCtor<S>): ArrayLike<S> { + const ret = new ctor(c.rowCount) as any; + for (let i = 0, _i = c.rowCount; i < _i; i++) ret[i] = f(c.value(i)); + return ret; +} + +function areColumnsEqual(a: Column<any>, b: Column<any>) { + if (a.rowCount !== b.rowCount || a.isDefined !== b.isDefined || a.schema.valueType !== b.schema.valueType) return false; + if (!!a['@array'] && !!b['@array']) return areArraysEqual(a, b); + return areValuesEqual(a, b); +} + +function areArraysEqual(a: Column<any>, b: Column<any>) { + const xs = a['@array']!, ys = b['@array']!; + for (let i = 0, _i = a.rowCount; i < _i; i++) { + if (xs[i] !== ys[i]) return false; + } + return true; +} + +function areValuesEqual(a: Column<any>, b: Column<any>) { + const va = a.value, vb = b.value; + for (let i = 0, _i = a.rowCount; i < _i; i++) { + if (va(i) !== vb(i)) return false; + } + return true; +} + +function columnIndicesOf<T>(c: Column<T>, test: (e: T) => boolean) { + const ret = [], v = c.value; + for (let i = 0, _i = c.rowCount; i < _i; i++) { + if (test(v(i))) ret[ret.length] = i; + } + return ret; +} \ No newline at end of file diff --git a/src/mol-data/db/database.ts b/src/mol-data/db/database.ts new file mode 100644 index 0000000000000000000000000000000000000000..e25041fc0364e9160cf27f26ead92bdfcd90b2d5 --- /dev/null +++ b/src/mol-data/db/database.ts @@ -0,0 +1,36 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import Table from './table' + +/** A collection of tables */ +type Database<Schema extends Database.Schema> = { + readonly _name: string, + readonly _tableNames: ReadonlyArray<string>, + readonly _schema: Schema +} & Database.Tables<Schema> + +namespace Database { + export type Tables<S extends Schema> = { [T in keyof S]: Table<S[T]> } + export type Schema = { [table: string]: Table.Schema } + + export function ofTables<S extends Schema>(name: string, schema: Schema, tables: Tables<S>) { + const keys = Object.keys(tables); + const ret = Object.create(null); + const tableNames: string[] = []; + ret._name = name; + ret._tableNames = tableNames; + ret._schema = schema; + for (const k of keys) { + if (!Table.is(tables[k])) continue; + ret[k] = tables[k]; + tableNames[tableNames.length] = k; + } + return ret; + } +} + +export default Database \ No newline at end of file diff --git a/src/mol-data/db/table.ts b/src/mol-data/db/table.ts new file mode 100644 index 0000000000000000000000000000000000000000..d01629bf1650134e148ad469bcc31d37a4517cda --- /dev/null +++ b/src/mol-data/db/table.ts @@ -0,0 +1,135 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import Column from './column' +import { sortArray } from '../util/sort' + +/** A collection of columns */ +type Table<Schema extends Table.Schema> = { + readonly _rowCount: number, + readonly _columns: ReadonlyArray<string>, + readonly _schema: Schema +} & Table.Columns<Schema> + +/** An immutable table */ +namespace Table { + export type Schema = { [field: string]: Column.Schema } + export type Columns<S extends Schema> = { [C in keyof S]: Column<S[C]['T']> } + export type Row<S extends Schema> = { [C in keyof S]: S[C]['T'] } + export type Arrays<S extends Schema> = { [C in keyof S]: ArrayLike<S[C]['T']> } + export type PartialTable<S extends Table.Schema> = { readonly _rowCount: number, readonly _columns: ReadonlyArray<string> } & { [C in keyof S]?: Column<S[C]['T']> } + + export function is(t: any): t is Table<any> { + return t && typeof t._rowCount === 'number' && !!t._columns && !!t._schema; + } + + export function pickColumns<S extends Schema>(schema: S, table: PartialTable<S>, guard: Partial<Columns<S>> = {}): Table<S> { + const ret = Object.create(null); + const keys = Object.keys(schema); + ret._rowCount = table._rowCount; + ret._columns = keys; + ret._schema = schema; + for (const k of keys) { + if (!!table[k]) ret[k] = table[k]; + else if (!!guard[k]) ret[k] = guard[k]; + else throw Error(`Cannot find column '${k}'.`); + } + return ret; + } + + export function ofColumns<S extends Schema, R extends Table<S> = Table<S>>(schema: S, columns: Columns<S>): R { + const _columns = Object.keys(columns); + const _rowCount = columns[_columns[0]].rowCount; + return { _rowCount, _columns, _schema: schema, ...(columns as any) }; + } + + export function ofRows<S extends Schema, R extends Table<S> = Table<S>>(schema: Schema, rows: ArrayLike<Row<S>>): R { + const ret = Object.create(null); + const rowCount = rows.length; + const columns = Object.keys(schema); + ret._rowCount = rowCount; + ret._columns = columns; + ret._schema = schema; + for (const k of columns) { + (ret as any)[k] = Column.ofLambda({ + rowCount, + schema: schema[k], + value: r => rows[r][k], + valueKind: r => typeof rows[r][k] === 'undefined' ? Column.ValueKind.NotPresent : Column.ValueKind.Present + }) + } + return ret as R; + } + + export function ofArrays<S extends Schema, R extends Table<S> = Table<S>>(schema: Schema, arrays: Arrays<S>): R { + const ret = Object.create(null); + const columns = Object.keys(schema); + ret._rowCount = arrays[columns[0]].length; + ret._columns = columns; + ret._schema = schema; + for (const k of columns) { + (ret as any)[k] = Column.ofArray({ array: arrays[k], schema: schema[k] }) + } + return ret as R; + } + + export function view<S extends R, R extends Schema>(table: Table<S>, schema: R, view: ArrayLike<number>) { + const ret = Object.create(null); + const columns = Object.keys(schema); + ret._rowCount = view.length; + ret._columns = columns; + ret._schema = schema; + for (const k of columns) { + (ret as any)[k] = Column.view(table[k], view); + } + return ret as Table<R>; + } + + export function columnToArray<S extends Schema>(table: Table<S>, name: keyof S, array?: Column.ArrayCtor<any>) { + table[name] = Column.asArrayColumn(table[name], array); + } + + /** Sort and return a new table */ + export function sort<T extends Table<S>, S extends Schema>(table: T, cmp: (i: number, j: number) => number) { + const indices = new Int32Array(table._rowCount); + for (let i = 0, _i = indices.length; i < _i; i++) indices[i] = i; + sortArray(indices, (_, i, j) => cmp(i, j)); + + let isIdentity = true; + for (let i = 0, _i = indices.length; i < _i; i++) { + if (indices[i] !== i) { + isIdentity = false; + break; + } + } + if (isIdentity) return table; + + const ret = Object.create(null); + ret._rowCount = table._rowCount; + ret._columns = table._columns; + ret._schema = table._schema; + for (const c of table._columns) { + ret[c] = Column.view((table as any)[c], indices, false); + } + return ret; + } + + export function areEqual<T extends Table<Schema>>(a: T, b: T) { + if (a._rowCount !== b._rowCount) return false; + if (a._columns.length !== b._columns.length) return false; + for (const c of a._columns) { + if (!b[c]) return false; + } + + for (const c of a._columns) { + if (!Column.areEqual(a[c], b[c])) return false; + } + + return true; + } +} + +export default Table \ No newline at end of file diff --git a/src/mol-data/index.ts b/src/mol-data/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..1db90c26e324d3098c0a50b16b3ec2f957f37f4e --- /dev/null +++ b/src/mol-data/index.ts @@ -0,0 +1,12 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import * as DB from './db' +import * as Int from './int' +import Iterator from './iterator' +import * as Util from './util' + +export { DB, Int, Iterator, Util } \ No newline at end of file diff --git a/src/mol-data/int.ts b/src/mol-data/int.ts new file mode 100644 index 0000000000000000000000000000000000000000..54c2795bbc6f772ebd25ee58501119ded48b35fe --- /dev/null +++ b/src/mol-data/int.ts @@ -0,0 +1,15 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import Interval from './int/interval' +import OrderedSet from './int/ordered-set' +import Segmentation from './int/segmentation' +import SortedArray from './int/sorted-array' +import Tuple from './int/tuple' +import LinkedIndex from './int/linked-index' +import Iterator from './iterator' + +export { Interval, OrderedSet, Segmentation, SortedArray, Tuple, LinkedIndex, Iterator } \ No newline at end of file diff --git a/src/mol-data/int/_spec/interval.spec.ts b/src/mol-data/int/_spec/interval.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..0373891338e14a3df9a2390023780dee51d80063 --- /dev/null +++ b/src/mol-data/int/_spec/interval.spec.ts @@ -0,0 +1,76 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import Interval from '../interval' + +describe('interval', () => { + function testI(name: string, a: Interval, b: Interval) { + it(name, () => expect(Interval.areEqual(a, b)).toBe(true)); + } + + function test(name: string, a: any, b: any) { + it(name, () => expect(a).toEqual(b)); + } + + const e = Interval.Empty; + const r05 = Interval.ofRange(0, 5); + const se05 = Interval.ofBounds(0, 5); + + test('size', Interval.size(e), 0); + test('size', Interval.size(r05), 6); + test('size', Interval.size(se05), 5); + + test('min/max', [Interval.min(e), Interval.max(e)], [0, -1]); + test('min/max', [Interval.min(r05), Interval.max(r05)], [0, 5]); + test('min/max', [Interval.min(se05), Interval.max(se05)], [0, 4]); + + test('start/end', [Interval.start(e), Interval.end(e)], [0, 0]); + test('start/end', [Interval.start(r05), Interval.end(r05)], [0, 6]); + test('start/end', [Interval.start(se05), Interval.end(se05)], [0, 5]); + + test('has', Interval.has(e, 5), false); + test('has', Interval.has(r05, 5), true); + test('has', Interval.has(r05, 6), false); + test('has', Interval.has(r05, -1), false); + test('has', Interval.has(se05, 5), false); + test('has', Interval.has(se05, 4), true); + + test('indexOf', Interval.indexOf(e, 5), -1); + test('indexOf', Interval.indexOf(r05, 5), 5); + test('indexOf', Interval.indexOf(r05, 6), -1); + + test('getAt', Interval.getAt(r05, 5), 5); + + test('areEqual', Interval.areEqual(r05, se05), false); + test('areIntersecting1', Interval.areIntersecting(r05, se05), true); + test('areIntersecting2', Interval.areIntersecting(r05, e), false); + test('areIntersecting3', Interval.areIntersecting(e, r05), false); + test('areIntersecting4', Interval.areIntersecting(e, e), true); + + test('areIntersecting5', Interval.areIntersecting(Interval.ofRange(0, 5), Interval.ofRange(-4, 3)), true); + test('areIntersecting6', Interval.areIntersecting(Interval.ofRange(0, 5), Interval.ofRange(-4, -3)), false); + test('areIntersecting7', Interval.areIntersecting(Interval.ofRange(0, 5), Interval.ofRange(1, 2)), true); + test('areIntersecting8', Interval.areIntersecting(Interval.ofRange(0, 5), Interval.ofRange(3, 6)), true); + + test('isSubInterval', Interval.isSubInterval(Interval.ofRange(0, 5), Interval.ofRange(3, 6)), false); + test('isSubInterval', Interval.isSubInterval(Interval.ofRange(0, 5), Interval.ofRange(3, 5)), true); + + testI('intersect', Interval.intersect(Interval.ofRange(0, 5), Interval.ofRange(-4, 3)), Interval.ofRange(0, 3)); + testI('intersect1', Interval.intersect(Interval.ofRange(0, 5), Interval.ofRange(1, 3)), Interval.ofRange(1, 3)); + testI('intersect2', Interval.intersect(Interval.ofRange(0, 5), Interval.ofRange(3, 5)), Interval.ofRange(3, 5)); + testI('intersect3', Interval.intersect(Interval.ofRange(0, 5), Interval.ofRange(-4, -3)), Interval.Empty); + + test('predIndex1', Interval.findPredecessorIndex(r05, 5), 5); + test('predIndex2', Interval.findPredecessorIndex(r05, -1), 0); + test('predIndex3', Interval.findPredecessorIndex(r05, 6), 6); + test('predIndexInt', Interval.findPredecessorIndexInInterval(r05, 0, Interval.ofRange(2, 3)), 2); + test('predIndexInt1', Interval.findPredecessorIndexInInterval(r05, 4, Interval.ofRange(2, 3)), 4); + + test('predIndexInt2', Interval.findPredecessorIndex(Interval.ofRange(3, 10), 5), 2); + test('predIndexInt3', Interval.findPredecessorIndexInInterval(Interval.ofRange(3, 10), 5, Interval.ofRange(2, 6)), 2); + + testI('findRange', Interval.findRange(r05, 2, 3), Interval.ofRange(2, 3)); +}); \ No newline at end of file diff --git a/src/mol-data/int/_spec/linked-index.spec.ts b/src/mol-data/int/_spec/linked-index.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..743c39a3e0b3170e988c1fcb012981db5141a9f2 --- /dev/null +++ b/src/mol-data/int/_spec/linked-index.spec.ts @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import LinkedIndex from '../linked-index' + +describe('linked-index', () => { + it('initial state', () => { + const index = LinkedIndex(2); + expect(index.head).toBe(0); + expect(index.has(0)).toBe(true); + expect(index.has(1)).toBe(true); + }); + + it('singleton', () => { + const index = LinkedIndex(1); + expect(index.head).toBe(0); + expect(index.has(0)).toBe(true); + index.remove(0); + expect(index.head).toBe(-1); + expect(index.has(0)).toBe(false); + }); + + it('remove 0', () => { + const index = LinkedIndex(2); + index.remove(0); + expect(index.head).toBe(1); + expect(index.has(0)).toBe(false); + expect(index.has(1)).toBe(true); + }); + + it('remove 1', () => { + const index = LinkedIndex(2); + index.remove(1); + expect(index.head).toBe(0); + expect(index.has(0)).toBe(true); + expect(index.has(1)).toBe(false); + }); + + it('remove 01', () => { + const index = LinkedIndex(2); + index.remove(0); + index.remove(1); + expect(index.head).toBe(-1); + expect(index.has(0)).toBe(false); + expect(index.has(1)).toBe(false); + }); +}); \ No newline at end of file diff --git a/src/mol-data/int/_spec/ordered-set.spec.ts b/src/mol-data/int/_spec/ordered-set.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..37d5ae0d42676d864434d5af6b7e0f91003e93fc --- /dev/null +++ b/src/mol-data/int/_spec/ordered-set.spec.ts @@ -0,0 +1,160 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import OrderedSet from '../ordered-set' +import Interval from '../interval' + +describe('ordered set', () => { + function ordSetToArray(set: OrderedSet) { + const ret = []; + for (let i = 0, _i = OrderedSet.size(set); i < _i; i++) ret.push(OrderedSet.getAt(set, i)); + return ret; + } + + function testEq(name: string, set: OrderedSet, expected: number[]) { + it(name, () => { + // copy the arrays to ensure "compatibility" between typed and native arrays + expect(Array.prototype.slice.call(ordSetToArray(set))).toEqual(Array.prototype.slice.call(expected)); + }); + } + + const empty = OrderedSet.Empty; + const singleton10 = OrderedSet.ofSingleton(10); + const range1_4 = OrderedSet.ofRange(1, 4); + const arr136 = OrderedSet.ofSortedArray([1, 3, 6]); + + const iB = (s: number, e: number) => Interval.ofBounds(s, e); + + testEq('empty', empty, []); + testEq('singleton', singleton10, [10]); + testEq('range', range1_4, [1, 2, 3, 4]); + testEq('sorted array', arr136, [1, 3, 6]); + + it('equality', () => { + expect(OrderedSet.areEqual(empty, singleton10)).toBe(false); + expect(OrderedSet.areEqual(singleton10, singleton10)).toBe(true); + expect(OrderedSet.areEqual(range1_4, singleton10)).toBe(false); + expect(OrderedSet.areEqual(arr136, OrderedSet.ofSortedArray([1, 3, 6]))).toBe(true); + expect(OrderedSet.areEqual(arr136, OrderedSet.ofSortedArray([1, 4, 6]))).toBe(false); + }); + + it('areIntersecting', () => { + expect(OrderedSet.areIntersecting(range1_4, arr136)).toBe(true); + expect(OrderedSet.areIntersecting(empty, empty)).toBe(true); + expect(OrderedSet.areIntersecting(empty, singleton10)).toBe(false); + expect(OrderedSet.areIntersecting(empty, range1_4)).toBe(false); + expect(OrderedSet.areIntersecting(empty, arr136)).toBe(false); + }); + + it('isSubset', () => { + expect(OrderedSet.isSubset(singleton10, empty)).toBe(true); + expect(OrderedSet.isSubset(range1_4, empty)).toBe(true); + expect(OrderedSet.isSubset(arr136, empty)).toBe(true); + expect(OrderedSet.isSubset(empty, empty)).toBe(true); + expect(OrderedSet.isSubset(empty, singleton10)).toBe(false); + expect(OrderedSet.isSubset(empty, range1_4)).toBe(false); + expect(OrderedSet.isSubset(empty, arr136)).toBe(false); + + expect(OrderedSet.isSubset(singleton10, range1_4)).toBe(false); + expect(OrderedSet.isSubset(range1_4, OrderedSet.ofRange(2, 3))).toBe(true); + expect(OrderedSet.isSubset(arr136, range1_4)).toBe(false); + expect(OrderedSet.isSubset(arr136, arr136)).toBe(true); + expect(OrderedSet.isSubset(arr136, OrderedSet.ofSortedArray([1, 3]))).toBe(true); + expect(OrderedSet.isSubset(arr136, OrderedSet.ofSortedArray([1, 3, 7]))).toBe(false); + expect(OrderedSet.isSubset(OrderedSet.ofSortedArray([0, 1, 2, 3, 7, 10]), OrderedSet.ofSortedArray([1, 3, 7]))).toBe(true); + expect(OrderedSet.isSubset(arr136, OrderedSet.ofSortedArray([1, 3, 10, 45]))).toBe(false); + expect(OrderedSet.isSubset(arr136, OrderedSet.ofSortedArray([12, 13, 16]))).toBe(false); + }); + + it('access/membership', () => { + expect(OrderedSet.has(empty, 10)).toBe(false); + expect(OrderedSet.indexOf(empty, 10)).toBe(-1); + + expect(OrderedSet.has(singleton10, 10)).toBe(true); + expect(OrderedSet.has(singleton10, 11)).toBe(false); + expect(OrderedSet.indexOf(singleton10, 10)).toBe(0); + expect(OrderedSet.indexOf(singleton10, 11)).toBe(-1); + + expect(OrderedSet.has(range1_4, 4)).toBe(true); + expect(OrderedSet.has(range1_4, 5)).toBe(false); + expect(OrderedSet.indexOf(range1_4, 4)).toBe(3); + expect(OrderedSet.indexOf(range1_4, 11)).toBe(-1); + + expect(OrderedSet.has(arr136, 3)).toBe(true); + expect(OrderedSet.has(arr136, 4)).toBe(false); + expect(OrderedSet.indexOf(arr136, 3)).toBe(1); + expect(OrderedSet.indexOf(arr136, 11)).toBe(-1); + }); + + it('interval range', () => { + expect(OrderedSet.findRange(empty, 9, 11)).toEqual(iB(0, 0)); + expect(OrderedSet.findRange(empty, -9, -6)).toEqual(iB(0, 0)); + expect(OrderedSet.findRange(singleton10, 9, 11)).toEqual(iB(0, 1)); + expect(OrderedSet.findRange(range1_4, 2, 3)).toEqual(iB(1, 3)); + expect(OrderedSet.findRange(range1_4, -10, 2)).toEqual(iB(0, 2)); + expect(OrderedSet.findRange(range1_4, -10, 20)).toEqual(iB(0, 4)); + expect(OrderedSet.findRange(range1_4, 3, 20)).toEqual(iB(2, 4)); + expect(OrderedSet.findRange(arr136, 0, 1)).toEqual(iB(0, 1)); + expect(OrderedSet.findRange(arr136, 0, 3)).toEqual(iB(0, 2)); + expect(OrderedSet.findRange(arr136, 0, 4)).toEqual(iB(0, 2)); + expect(OrderedSet.findRange(arr136, 2, 4)).toEqual(iB(1, 2)); + expect(OrderedSet.findRange(arr136, 2, 7)).toEqual(iB(1, 3)); + }) + + testEq('union ES', OrderedSet.union(empty, singleton10), [10]); + testEq('union ER', OrderedSet.union(empty, range1_4), [1, 2, 3, 4]); + testEq('union EA', OrderedSet.union(empty, arr136), [1, 3, 6]); + testEq('union SS', OrderedSet.union(singleton10, OrderedSet.ofSingleton(16)), [10, 16]); + testEq('union SR', OrderedSet.union(range1_4, singleton10), [1, 2, 3, 4, 10]); + testEq('union SA', OrderedSet.union(arr136, singleton10), [1, 3, 6, 10]); + testEq('union SA1', OrderedSet.union(arr136, OrderedSet.ofSingleton(3)), [1, 3, 6]); + testEq('union RR', OrderedSet.union(range1_4, range1_4), [1, 2, 3, 4]); + testEq('union RR1', OrderedSet.union(range1_4, OrderedSet.ofRange(6, 7)), [1, 2, 3, 4, 6, 7]); + testEq('union RR2', OrderedSet.union(range1_4, OrderedSet.ofRange(3, 5)), [1, 2, 3, 4, 5]); + testEq('union RA', OrderedSet.union(range1_4, arr136), [1, 2, 3, 4, 6]); + testEq('union AA', OrderedSet.union(arr136, OrderedSet.ofSortedArray([2, 4, 6, 7])), [1, 2, 3, 4, 6, 7]); + testEq('union AA1', OrderedSet.union(arr136, OrderedSet.ofSortedArray([2, 3, 4, 6, 7])), [1, 2, 3, 4, 6, 7]); + testEq('union AA2', OrderedSet.union(arr136, OrderedSet.ofSortedArray([2, 4, 5, 6, 7])), [1, 2, 3, 4, 5, 6, 7]); + testEq('union AA3', OrderedSet.union(OrderedSet.ofSortedArray([1, 3]), OrderedSet.ofSortedArray([2, 4])), [1, 2, 3, 4]); + testEq('union AA4', OrderedSet.union(OrderedSet.ofSortedArray([1, 3]), OrderedSet.ofSortedArray([1, 3, 4])), [1, 3, 4]); + testEq('union AA5', OrderedSet.union(OrderedSet.ofSortedArray([1, 3, 4]), OrderedSet.ofSortedArray([1, 3])), [1, 3, 4]); + it('union AA6', () => expect(OrderedSet.union(arr136, OrderedSet.ofSortedArray([1, 3, 6]))).toBe(arr136)); + + testEq('intersect ES', OrderedSet.intersect(empty, singleton10), []); + testEq('intersect ER', OrderedSet.intersect(empty, range1_4), []); + testEq('intersect EA', OrderedSet.intersect(empty, arr136), []); + testEq('intersect SS', OrderedSet.intersect(singleton10, OrderedSet.ofSingleton(16)), []); + testEq('intersect SS1', OrderedSet.intersect(singleton10, singleton10), [10]); + testEq('intersect SR', OrderedSet.intersect(range1_4, singleton10), []); + testEq('intersect RR', OrderedSet.intersect(range1_4, range1_4), [1, 2, 3, 4]); + testEq('intersect RR2', OrderedSet.intersect(range1_4, OrderedSet.ofRange(3, 5)), [3, 4]); + testEq('intersect RA', OrderedSet.intersect(range1_4, arr136), [1, 3]); + testEq('intersect AA', OrderedSet.intersect(arr136, OrderedSet.ofSortedArray([2, 3, 4, 6, 7])), [3, 6]); + it('intersect AA1', () => expect(OrderedSet.union(arr136, OrderedSet.ofSortedArray([1, 3, 6]))).toBe(arr136)); + + testEq('subtract ES', OrderedSet.subtract(empty, singleton10), []); + testEq('subtract ER', OrderedSet.subtract(empty, range1_4), []); + testEq('subtract EA', OrderedSet.subtract(empty, arr136), []); + testEq('subtract SS', OrderedSet.subtract(singleton10, OrderedSet.ofSingleton(16)), [10]); + testEq('subtract SS1', OrderedSet.subtract(singleton10, singleton10), []); + testEq('subtract SR', OrderedSet.subtract(range1_4, singleton10), [1, 2, 3, 4]); + testEq('subtract SR1', OrderedSet.subtract(range1_4, OrderedSet.ofSingleton(4)), [1, 2, 3]); + testEq('subtract SR2', OrderedSet.subtract(range1_4, OrderedSet.ofSingleton(3)), [1, 2, 4]); + testEq('subtract RR', OrderedSet.subtract(range1_4, range1_4), []); + testEq('subtract RR1', OrderedSet.subtract(range1_4, OrderedSet.ofRange(3, 5)), [1, 2]); + + testEq('subtract RA', OrderedSet.subtract(range1_4, arr136), [2, 4]); + testEq('subtract RA1', OrderedSet.subtract(range1_4, OrderedSet.ofSortedArray([0, 1, 2, 3, 4, 7])), []); + testEq('subtract RA2', OrderedSet.subtract(range1_4, OrderedSet.ofSortedArray([0, 2, 3])), [1, 4]); + + testEq('subtract AR', OrderedSet.subtract(arr136, range1_4), [6]); + testEq('subtract AR1', OrderedSet.subtract(arr136, OrderedSet.ofRange(0, 10)), []); + testEq('subtract AR1', OrderedSet.subtract(arr136, OrderedSet.ofRange(2, 10)), [1]); + + testEq('subtract AA', OrderedSet.subtract(arr136, arr136), []); + testEq('subtract AA1', OrderedSet.subtract(arr136, OrderedSet.ofSortedArray([2, 3, 4, 6, 7])), [1]); + testEq('subtract AA2', OrderedSet.subtract(arr136, OrderedSet.ofSortedArray([0, 1, 6])), [3]); +}); \ No newline at end of file diff --git a/src/mol-data/int/_spec/segmentation.spec.ts b/src/mol-data/int/_spec/segmentation.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..f91eff82ac3ad56bdaa039d07a933faf015400bc --- /dev/null +++ b/src/mol-data/int/_spec/segmentation.spec.ts @@ -0,0 +1,117 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import OrderedSet from '../ordered-set' +import Interval from '../interval' +import Segmentation from '../segmentation' + +describe('segments', () => { + const data = OrderedSet.ofSortedArray([4, 9, 10, 11, 14, 15, 16]); + const segs = Segmentation.create([0, 4, 10, 12, 13, 15, 25]); + + it('size', () => expect(Segmentation.count(segs)).toBe(6)); + + it('project', () => { + const p = Segmentation.projectValue(segs, data, 4); + expect(p).toBe(Interval.ofBounds(0, 2)) + }); + + it('ofOffsetts', () => { + const p = Segmentation.ofOffsets([10, 12], Interval.ofBounds(10, 14)); + expect(p.segments).toEqual(new Int32Array([0, 2, 4])) + }); + + it('map', () => { + const segs = Segmentation.create([0, 1, 2]); + expect(segs.segmentMap).toEqual(new Int32Array([0, 1])); + expect(Segmentation.getSegment(segs, 0)).toBe(0); + expect(Segmentation.getSegment(segs, 1)).toBe(1); + }); + + it('iteration', () => { + const it = Segmentation.transientSegments(segs, data); + + const t = Object.create(null); + let count = 0; + while (it.hasNext) { + count++; + const s = it.move(); + for (let j = s.start; j < s.end; j++) { + const x = t[s.index]; + const v = OrderedSet.getAt(data, j); + if (!x) t[s.index] = [v]; + else x[x.length] = v; + } + } + expect(t).toEqual({ 1: [4, 9], 2: [10, 11], 4: [14], 5: [15, 16] }); + expect(count).toBe(4); + }); + + it('units', () => { + const data = OrderedSet.ofBounds(0, 4); + const segs = Segmentation.create([0, 1, 2, 3, 4]); + const it = Segmentation.transientSegments(segs, data, { index: 0, start: 2, end: 4 }); + + const t = Object.create(null); + let count = 0; + while (it.hasNext) { + count++; + const s = it.move(); + for (let j = s.start; j < s.end; j++) { + const x = t[s.index]; + const v = OrderedSet.getAt(data, j); + if (!x) t[s.index] = [v]; + else x[x.length] = v; + } + } + expect(t).toEqual({ 2: [2], 3: [3] }); + expect(count).toBe(2); + }); + + it('iteration range', () => { + const segs = Segmentation.create([0, 2, 4]); + const dataRange = OrderedSet.ofBounds(0, 4); + + const it = Segmentation.transientSegments(segs, dataRange); + + const t = Object.create(null); + let count = 0; + while (it.hasNext) { + count++; + const s = it.move(); + for (let j = s.start; j < s.end; j++) { + const x = t[s.index]; + const v = OrderedSet.getAt(dataRange, j); + if (!x) t[s.index] = [v]; + else x[x.length] = v; + } + } + expect(count).toBe(2); + expect(t).toEqual({ 0: [0, 1], 1: [2, 3] }); + }); + + it('iteration range 1', () => { + const segs = Segmentation.create([0, 2, 4]); + const dataRange = OrderedSet.ofBounds(0, 4); + + const it = Segmentation.transientSegments(segs, dataRange, { index: 0, start: 2, end: 4 }); + + const t = Object.create(null); + let count = 0; + while (it.hasNext) { + count++; + const s = it.move(); + for (let j = s.start; j < s.end; j++) { + const x = t[s.index]; + const v = OrderedSet.getAt(dataRange, j); + if (!x) t[s.index] = [v]; + else x[x.length] = v; + } + } + expect(count).toBe(1); + expect(t).toEqual({ 1: [2, 3] }); + }); +}); diff --git a/src/mol-data/int/_spec/sorted-array.spec.ts b/src/mol-data/int/_spec/sorted-array.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..db8cd15d7c90b396bc1226635c34b3222982dcbb --- /dev/null +++ b/src/mol-data/int/_spec/sorted-array.spec.ts @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import Interval from '../interval' +import SortedArray from '../sorted-array' + +describe('sortedArray', () => { + function testI(name: string, a: Interval, b: Interval) { + it(name, () => expect(Interval.areEqual(a, b)).toBe(true)); + } + + function test(name: string, a: any, b: any) { + it(name, () => expect(a).toEqual(b)); + } + + const a1234 = SortedArray.ofSortedArray([1, 2, 3, 4]); + const a2468 = SortedArray.ofSortedArray([2, 4, 6, 8]); + + test('size', SortedArray.size(a1234), 4); + + test('min/max', [SortedArray.min(a1234), SortedArray.max(a1234)], [1, 4]); + test('start/end', [SortedArray.start(a1234), SortedArray.end(a1234)], [1, 5]); + + test('has', SortedArray.has(a1234, 5), false); + test('has', SortedArray.has(a1234, 4), true); + + it('has-all', () => { + for (let i = 1; i <= 4; i++) expect(SortedArray.has(a1234, i)).toBe(true); + }); + + test('indexOf', SortedArray.indexOf(a2468, 5), -1); + test('indexOf', SortedArray.indexOf(a2468, 2), 0); + + test('getAt', a2468[1], 4); + + test('areEqual', SortedArray.areEqual(a2468, a2468), true); + test('areEqual1', SortedArray.areEqual(a2468, SortedArray.ofUnsortedArray([4, 2, 8, 6])), true); + test('areEqual2', SortedArray.areEqual(a1234, a2468), false); + + test('predIndex1', SortedArray.findPredecessorIndex(a1234, 5), 4); + test('predIndex2', SortedArray.findPredecessorIndex(a1234, 2), 1); + test('predIndex3', SortedArray.findPredecessorIndex(a2468, 4), 1); + test('predIndex4', SortedArray.findPredecessorIndex(a2468, 3), 1); + test('predIndexInt', SortedArray.findPredecessorIndexInInterval(a1234, 0, Interval.ofRange(2, 3)), 2); + + testI('findRange', SortedArray.findRange(a2468, 2, 4), Interval.ofRange(0, 1)); + + // console.log(Interval.findPredecessorIndexInInterval(Interval.ofBounds(0, 3), 2, Interval.ofBounds(0, 3))) + // console.log(SortedArray.findPredecessorIndexInInterval(SortedArray.ofSortedArray([0, 1, 2]), 2, Interval.ofBounds(0, 3))) +}); \ No newline at end of file diff --git a/src/mol-data/int/_spec/tuple.spec.ts b/src/mol-data/int/_spec/tuple.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..37610b8393b40f1305d460dfab128d99d8954887 --- /dev/null +++ b/src/mol-data/int/_spec/tuple.spec.ts @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import IntTuple from '../tuple' + +describe('int pair', () => { + it('works', () => { + for (let i = 0; i < 10; i++) { + for (let j = -10; j < 5; j++) { + const t = IntTuple.create(i, j); + expect(IntTuple.fst(t)).toBe(i); + expect(IntTuple.snd(t)).toBe(j); + } + } + }) +}) \ No newline at end of file diff --git a/src/mol-data/int/impl/interval.ts b/src/mol-data/int/impl/interval.ts new file mode 100644 index 0000000000000000000000000000000000000000..79b9b12730e6e89a4e6f4392533b4d56d08888b7 --- /dev/null +++ b/src/mol-data/int/impl/interval.ts @@ -0,0 +1,61 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import Tuple from '../tuple' + +export const Empty = Tuple.Zero; +export function ofRange(min: number, max: number) { return max < min ? Tuple.create(min, min) : Tuple.create(min, max + 1); } +export function ofBounds(min: number, max: number) { return max <= min ? Tuple.create(min, min) : Tuple.create(min, max); } +export const is = Tuple.is; + +export const start = Tuple.fst; +export const end = Tuple.snd; +export const min = Tuple.fst; +export function max(i: Tuple) { return Tuple.snd(i) - 1; } +export function size(i: Tuple) { return Tuple.snd(i) - Tuple.fst(i); } +export const hashCode = Tuple.hashCode; + +export function has(int: Tuple, v: number) { return Tuple.fst(int) <= v && v < Tuple.snd(int); } +export function indexOf(int: Tuple, x: number) { const m = start(int); return x >= m && x < end(int) ? x - m : -1; } +export function getAt(int: Tuple, i: number) { return Tuple.fst(int) + i; } + +export const areEqual = Tuple.areEqual; +export function areIntersecting(a: Tuple, b: Tuple) { + const sa = size(a), sb = size(b); + if (sa === 0 && sb === 0) return true; + return sa > 0 && sb > 0 && max(a) >= min(b) && min(a) <= max(b); +} +export function isSubInterval(a: Tuple, b: Tuple) { + if (!size(a)) return size(b) === 0; + if (!size(b)) return true; + return start(a) <= start(b) && end(a) >= end(b); +} + +export function findPredecessorIndex(int: Tuple, v: number) { + const s = start(int); + if (v <= s) return 0; + const e = end(int); + if (v >= e) return e - s; + return v - s; +} + +export function findPredecessorIndexInInterval(int: Tuple, v: number, bounds: Tuple) { + const bS = start(bounds); + const s = start(int); + if (v <= bS + s) return bS; + const bE = end(bounds); + if (v >= bE + s) return bE; + return v - s; +} + +export function findRange(int: Tuple, min: number, max: number) { + return ofBounds(findPredecessorIndex(int, min), findPredecessorIndex(int, max + 1)); +} + +export function intersect(a: Tuple, b: Tuple) { + if (!areIntersecting(a, b)) return Empty; + return ofBounds(Math.max(start(a), start(b)), Math.min(end(a), end(b))); +} \ No newline at end of file diff --git a/src/mol-data/int/impl/ordered-set.ts b/src/mol-data/int/impl/ordered-set.ts new file mode 100644 index 0000000000000000000000000000000000000000..a5631f761af3bf18692ad13758ec304068da56c0 --- /dev/null +++ b/src/mol-data/int/impl/ordered-set.ts @@ -0,0 +1,255 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import S from '../sorted-array' +import I from '../interval' + +type OrderedSetImpl = I | S +type Nums = ArrayLike<number> + +export const Empty: OrderedSetImpl = I.Empty; + +export const ofSingleton = I.ofSingleton +export const ofRange = I.ofRange +export const ofBounds = I.ofBounds + +export function ofSortedArray(xs: Nums): OrderedSetImpl { + if (!xs.length) return Empty; + // check if the array is just a range + if (xs[xs.length - 1] - xs[0] + 1 === xs.length) return I.ofRange(xs[0], xs[xs.length - 1]); + return xs as any; +} + +export function size(set: OrderedSetImpl) { return I.is(set) ? I.size(set) : S.size(set); } +export function has(set: OrderedSetImpl, x: number) { return I.is(set) ? I.has(set, x) : S.has(set, x); } +export function indexOf(set: OrderedSetImpl, x: number) { return I.is(set) ? I.indexOf(set, x) : S.indexOf(set, x); } +export function getAt(set: OrderedSetImpl, i: number) { return I.is(set) ? I.getAt(set, i) : set[i]; } +export function min(set: OrderedSetImpl) { return I.is(set) ? I.min(set) : S.min(set); } +export function max(set: OrderedSetImpl) { return I.is(set) ? I.max(set) : S.max(set); } + +export function hashCode(set: OrderedSetImpl) { return I.is(set) ? I.hashCode(set) : S.hashCode(set); } +// TODO: possibly add more hash functions to allow for multilevel hashing. + +export function areEqual(a: OrderedSetImpl, b: OrderedSetImpl) { + if (I.is(a)) { + if (I.is(b)) return I.areEqual(a, b); + return areEqualIS(a, b); + } else if (I.is(b)) return areEqualIS(b, a); + return S.areEqual(a, b); +} + +export function areIntersecting(a: OrderedSetImpl, b: OrderedSetImpl) { + if (I.is(a)) { + if (I.is(b)) return I.areIntersecting(a, b); + return areIntersectingSI(b, a); + } else if (I.is(b)) return areIntersectingSI(a, b); + return S.areIntersecting(a, b); +} + +/** Check if the 2nd argument is a subset of the 1st */ +export function isSubset(a: OrderedSetImpl, b: OrderedSetImpl) { + if (I.is(a)) { + if (I.is(b)) return I.isSubInterval(a, b); + return isSubsetIS(a, b); + } else if (I.is(b)) return isSubsetSI(a, b); + return S.isSubset(a, b); +} + +export function findPredecessorIndex(set: OrderedSetImpl, x: number) { + return I.is(set) ? I.findPredecessorIndex(set, x) : S.findPredecessorIndex(set, x); +} + +export function findPredecessorIndexInInterval(set: OrderedSetImpl, x: number, bounds: I) { + return I.is(set) ? I.findPredecessorIndexInInterval(set, x, bounds) : S.findPredecessorIndexInInterval(set, x, bounds); +} + +export function findRange(set: OrderedSetImpl, min: number, max: number) { + return I.is(set) ? I.findRange(set, min, max) : S.findRange(set, min, max); +} + +export function union(a: OrderedSetImpl, b: OrderedSetImpl) { + if (I.is(a)) { + if (I.is(b)) return unionII(a, b); + return unionSI(b, a); + } else if (I.is(b)) return unionSI(a, b); + return ofSortedArray(S.union(a, b)); +} + +export function intersect(a: OrderedSetImpl, b: OrderedSetImpl) { + if (I.is(a)) { + if (I.is(b)) return I.intersect(a, b); + return intersectSI(b, a); + } else if (I.is(b)) return intersectSI(a, b); + return ofSortedArray(S.intersect(a, b)); +} + +export function subtract(a: OrderedSetImpl, b: OrderedSetImpl) { + if (I.is(a)) { + if (I.is(b)) return subtractII(a, b); + return subtractIS(a, b); + } else if (I.is(b)) return subtractSI(a, b); + return ofSortedArray(S.subtract(a, b)); +} + +function areEqualIS(a: I, b: S) { return I.size(a) === S.size(b) && I.start(a) === S.start(b) && I.end(a) === S.end(b); } + +function areIntersectingSI(a: S, b: I) { + return areRangesIntersecting(a, b); +} + +function isSubsetSI(a: S, b: I) { + const minB = I.min(b), maxB = I.max(b); + if (maxB - minB + 1 === 0) return true; + const minA = S.min(a), maxA = S.max(a); + if (minB < minA || maxB > maxA) return false; + const r = S.findRange(a, minB, maxB); + return I.size(r) === I.size(b); +} + +function isSubsetIS(a: I, b: S) { + const minA = I.min(a), maxA = I.max(a); + if (maxA - minA + 1 === 0) return false; + const minB = S.min(b), maxB = S.max(b); + return minB >= minA && maxA <= maxB; +} + +function areRangesIntersecting(a: OrderedSetImpl, b: OrderedSetImpl) { + const sa = size(a), sb = size(b); + if (sa === 0 && sb === 0) return true; + return sa > 0 && sb > 0 && max(a) >= min(b) && min(a) <= max(b); +} + +function isRangeSubset(a: OrderedSetImpl, b: OrderedSetImpl) { + if (!size(a)) return size(b) === 0; + if (!size(b)) return true; + return min(a) <= min(b) && max(a) >= max(b); +} + +function unionII(a: I, b: I) { + if (I.areEqual(a, b)) return a; + + const sizeA = I.size(a), sizeB = I.size(b); + if (!sizeA) return b; + if (!sizeB) return a; + const minA = I.min(a), minB = I.min(b); + if (areRangesIntersecting(a, b)) return I.ofRange(Math.min(minA, minB), Math.max(I.max(a), I.max(b))); + let lSize, lMin, rSize, rMin; + if (minA < minB) { lSize = sizeA; lMin = minA; rSize = sizeB; rMin = minB; } + else { lSize = sizeB; lMin = minB; rSize = sizeA; rMin = minA; } + const arr = new Int32Array(sizeA + sizeB); + for (let i = 0; i < lSize; i++) arr[i] = i + lMin; + for (let i = 0; i < rSize; i++) arr[i + lSize] = i + rMin; + return ofSortedArray(arr); +} + +function unionSI(a: S, b: I) { + const bSize = I.size(b); + if (!bSize) return a; + // is the array fully contained in the range? + if (isRangeSubset(b, a)) return b; + + const min = I.min(b), max = I.max(b); + const r = S.findRange(a, min, max); + const start = I.start(r), end = I.end(r); + const indices = new Int32Array(start + (a.length - end) + bSize); + let offset = 0; + for (let i = 0; i < start; i++) indices[offset++] = a[i]; + for (let i = min; i <= max; i++) indices[offset++] = i; + for (let i = end, _i = a.length; i < _i; i++) indices[offset] = a[i]; + + return ofSortedArray(indices); +} + +function intersectSI(a: S, b: I) { + if (!I.size(b)) return Empty; + + const r = S.findRange(a, I.min(b), I.max(b)); + const start = I.start(r), end = I.end(r); + const resultSize = end - start; + if (!resultSize) return Empty; + + const indices = new Int32Array(resultSize); + let offset = 0; + for (let i = start; i < end; i++) { + indices[offset++] = a[i]; + } + return ofSortedArray(indices); +} + +function subtractII(a: I, b: I) { + if (I.areEqual(a, b)) return Empty; + if (!I.areIntersecting(a, b)) return a; + + const minA = I.min(a), maxA = I.max(a); + const minB = I.min(b), maxB = I.max(b); + + if (maxA < minA || maxB < minB) return a; + // is A subset of B? ==> Empty + if (I.isSubInterval(b, a)) return Empty; + if (I.isSubInterval(a, b)) { + // this splits the interval into two, gotta represent it as a set. + const l = minB - minA, r = maxA - maxB; + if (l <= 0) return I.ofRange(maxB + 1, maxB + r); + if (r <= 0) return I.ofRange(minA, minA + l - 1); + const ret = new Int32Array(l + r); + let offset = 0; + for (let i = 0; i < l; i++) ret[offset++] = minA + i; + for (let i = 1; i <= r; i++) ret[offset++] = maxB + i; + return ofSortedArray(ret); + } + if (minA < minB) return I.ofRange(minA, minB - 1); + return I.ofRange(maxB + 1, maxA); +} + +function subtractSI(a: S, b: I) { + const min = I.min(b), max = I.max(b); + // is empty? + if (max < min) return a; + + const r = S.findRange(a, min, max); + const start = I.start(r), end = I.end(r); + const resultSize = a.length - (end - start); + // A is subset of B + if (resultSize <= 0) return Empty; + // No common elements + if (resultSize === a.length) return a; + + const ret = new Int32Array(resultSize); + let offset = 0; + for (let i = 0; i < start; i++) ret[offset++] = a[i]; + for (let i = end, _i = a.length; i < _i; i++) ret[offset++] = a[i]; + return ofSortedArray(ret); +} + +function subtractIS(a: I, b: S) { + const min = I.min(a), max = I.max(a); + + // is empty? + if (max < min) return a; + + const rSize = max - min + 1; + const interval = S.findRange(b, min, max); + const start = I.start(interval), end = I.end(interval); + const commonCount = end - start; + + // No common elements. + if (commonCount === 0) return a; + + const resultSize = rSize - commonCount; + // A is subset of B + if (resultSize <= 0) return Empty; + + const ret = new Int32Array(resultSize); + const li = b.length - 1; + const fst = b[Math.min(start, li)], last = b[Math.min(end, li)]; + let offset = 0; + for (let i = min; i < fst; i++) ret[offset++] = i; + for (let i = fst; i <= last; i++) { + if (S.indexOfInInterval(b, i, interval) < 0) ret[offset++] = i; + } + for (let i = last + 1; i <= max; i++) ret[offset++] = i; + return ofSortedArray(ret); +} \ No newline at end of file diff --git a/src/mol-data/int/impl/segmentation.ts b/src/mol-data/int/impl/segmentation.ts new file mode 100644 index 0000000000000000000000000000000000000000..7caa3bfb8919e5476fddf430ccc793f8df786fc3 --- /dev/null +++ b/src/mol-data/int/impl/segmentation.ts @@ -0,0 +1,104 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import Iterator from '../../iterator' +import OrderedSet from '../ordered-set' +import Interval from '../interval' +import SortedArray from '../sorted-array' +import Segs from '../segmentation' + +type Segmentation = { segments: SortedArray, segmentMap: Int32Array, count: number } + +export function create(values: ArrayLike<number>): Segmentation { + const segments = SortedArray.ofSortedArray(values); + const max = SortedArray.max(segments); + const segmentMap = new Int32Array(max); + for (let i = 0, _i = values.length - 1; i < _i; i++) { + for (let j = values[i], _j = values[i + 1]; j < _j; j++) { + segmentMap[j] = i; + } + } + return { segments, segmentMap, count: values.length - 1 }; +} + +export function ofOffsets(offsets: ArrayLike<number>, bounds: Interval): Segmentation { + const s = Interval.start(bounds); + const segments = new Int32Array(offsets.length + 1); + for (let i = 0, _i = offsets.length; i < _i; i++) { + segments[i] = offsets[i] - s; + } + segments[offsets.length] = Interval.end(bounds) - s; + return create(segments); +} + +export function count({ count }: Segmentation) { return count; } +export function getSegment({ segmentMap }: Segmentation, value: number) { return segmentMap[value]; } + +export function projectValue({ segments }: Segmentation, set: OrderedSet, value: number): Interval { + const last = OrderedSet.max(segments); + const idx = value >= last ? -1 : OrderedSet.findPredecessorIndex(segments, value - 1); + return OrderedSet.findRange(set, OrderedSet.getAt(segments, idx), OrderedSet.getAt(segments, idx + 1) - 1); +} + +export class SegmentIterator implements Iterator<Segs.Segment> { + private segmentMin = 0; + private segmentMax = 0; + private setRange = Interval.Empty; + private value: Segs.Segment = { index: 0, start: 0, end: 0 }; + + hasNext: boolean = false; + + move() { + while (this.hasNext) { + if (this.updateValue()) { + this.value.index = this.segmentMin++; + this.hasNext = this.segmentMax >= this.segmentMin && Interval.size(this.setRange) > 0; + break; + } else { + this.updateSegmentRange(); + } + } + return this.value; + } + + private updateValue() { + const segmentEnd = this.segments[this.segmentMin + 1]; + // TODO: add optimized version for interval and array? + const setEnd = OrderedSet.findPredecessorIndexInInterval(this.set, segmentEnd, this.setRange); + this.value.start = Interval.start(this.setRange); + this.value.end = setEnd; + this.setRange = Interval.ofBounds(setEnd, Interval.end(this.setRange)); + return setEnd > this.value.start; + } + + private updateSegmentRange() { + const sMin = Interval.min(this.setRange), sMax = Interval.max(this.setRange); + if (sMax < sMin) { + this.hasNext = false; + return; + } + + this.segmentMin = this.segmentMap[OrderedSet.getAt(this.set, sMin)]; + this.segmentMax = this.segmentMap[OrderedSet.getAt(this.set, sMax)]; + + this.hasNext = this.segmentMax >= this.segmentMin; + } + + setSegment(segment: Segs.Segment) { + this.setRange = Interval.ofBounds(segment.start, segment.end); + this.updateSegmentRange(); + } + + constructor(private segments: SortedArray, private segmentMap: Int32Array, private set: OrderedSet, inputRange: Interval) { + this.setRange = inputRange; + this.updateSegmentRange(); + } +} + +export function segments(segs: Segmentation, set: OrderedSet, segment?: Segs.Segment) { + const int = typeof segment !== 'undefined' ? Interval.ofBounds(segment.start, segment.end) : Interval.ofBounds(0, OrderedSet.size(set)); + return new SegmentIterator(segs.segments, segs.segmentMap, set, int); +} \ No newline at end of file diff --git a/src/mol-data/int/impl/sorted-array.ts b/src/mol-data/int/impl/sorted-array.ts new file mode 100644 index 0000000000000000000000000000000000000000..f3147c7086664c7900c78db8c89f1e87b15ff509 --- /dev/null +++ b/src/mol-data/int/impl/sorted-array.ts @@ -0,0 +1,287 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { sortArray, hash3, hash4 } from '../../util' +import Interval from '../interval' + +type Nums = ArrayLike<number> + + +export const Empty: Nums = [] + +export function ofSingleton(v: number) { return [v]; } +export function ofSortedArray(xs: Nums) { return xs; } +export function ofUnsortedArray(xs: Nums) { sortArray(xs); return xs; } +export function is(xs: any): xs is Nums { return xs && (Array.isArray(xs) || !!xs.buffer); } + +export function start(xs: Nums) { return xs[0]; } +export function end(xs: Nums) { return xs[xs.length - 1] + 1; } +export function min(xs: Nums) { return xs[0]; } +export function max(xs: Nums) { return xs[xs.length - 1]; } +export function size(xs: Nums) { return xs.length; } +export function hashCode(xs: Nums) { + // hash of tuple (size, min, max, mid) + const s = xs.length; + if (!s) return 0; + if (s > 2) return hash4(s, xs[0], xs[s - 1], xs[s << 1]); + return hash3(s, xs[0], xs[s - 1]); +} + +export function indexOf(xs: Nums, v: number) { + const l = xs.length; + return l === 0 ? -1 : xs[0] <= v && v <= xs[l - 1] ? binarySearchRange(xs, v, 0, l) : -1; +} +export function indexOfInInterval(xs: Nums, v: number, bounds: Interval) { + const l = xs.length; + const s = Interval.start(bounds), e = Interval.end(bounds); + return l === 0 || e <= s ? -1 : xs[s] <= v && v <= xs[e - 1] ? binarySearchRange(xs, v, s, e) : -1; +} +export function has(xs: Nums, v: number) { return indexOf(xs, v) >= 0; } + +export function getAt(xs: Nums, i: number) { return xs[i]; } + +export function areEqual(a: Nums, b: Nums) { + if (a === b) return true; + const aSize = a.length; + if (aSize !== b.length || a[0] !== b[0] || a[aSize - 1] !== b[aSize - 1]) return false; + for (let i = 0; i < aSize; i++) { + if (a[i] !== b[i]) return false; + } + return true; +} + +export function findPredecessorIndex(xs: Nums, v: number) { + const len = xs.length; + if (v <= xs[0]) return 0; + if (v > xs[len - 1]) return len; + return binarySearchPredIndexRange(xs, v, 0, len); +} + +export function findPredecessorIndexInInterval(xs: Nums, v: number, bounds: Interval) { + const s = Interval.start(bounds), e = Interval.end(bounds); + const sv = xs[s]; + if (v <= sv) return s; + if (e > s && v > xs[e - 1]) return e; + if (v - sv <= 11) return linearSearchPredInRange(xs, v, s + 1, e); + return binarySearchPredIndexRange(xs, v, s, e); +} + +export function findRange(xs: Nums, min: number, max: number) { + return Interval.ofBounds(findPredecessorIndex(xs, min), findPredecessorIndex(xs, max + 1)); +} + +function binarySearchRange(xs: Nums, value: number, start: number, end: number) { + let min = start, max = end - 1; + while (min <= max) { + if (min + 11 > max) { + for (let i = min; i <= max; i++) { + if (value === xs[i]) return i; + } + return -1; + } + + const mid = (min + max) >> 1; + const v = xs[mid]; + if (value < v) max = mid - 1; + else if (value > v) min = mid + 1; + else return mid; + } + return -1; +} + +function binarySearchPredIndexRange(xs: Nums, value: number, start: number, end: number) { + let min = start, max = end - 1; + while (min < max) { + if (min + 11 > max) { + for (let i = min; i <= max; i++) { + if (value <= xs[i]) return i; + } + return max + 1; + } + const mid = (min + max) >> 1; + const v = xs[mid]; + if (value < v) max = mid - 1; + else if (value > v) min = mid + 1; + else return mid; + } + if (min > max) return max + 1; + return xs[min] >= value ? min : min + 1; +} + +function linearSearchPredInRange(xs: Nums, value: number, start: number, end: number) { + for (let i = start; i < end; i++) { + if (value <= xs[i]) return i; + } + return end; +} + +export function areIntersecting(a: Nums, b: Nums) { + if (a === b) return true; + + let { startI: i, startJ: j, endI, endJ } = getSuitableIntersectionRange(a, b); + while (i < endI && j < endJ) { + const x = a[i], y = b[j]; + if (x < y) { i++; } + else if (x > y) { j++; } + else return true; + } + return false; +} + +export function isSubset(a: Nums, b: Nums) { + if (a === b) return true; + + const lenB = b.length; + let { startI: i, startJ: j, endI, endJ } = getSuitableIntersectionRange(a, b); + // must be able to advance by lenB elements + if (endJ - j < lenB || endI - i < lenB) return false; + + let equal = 0; + while (i < endI && j < endJ) { + const x = a[i], y = b[j]; + if (x < y) { i++; } + else if (x > y) { j++; } + else { i++; j++; equal++; } + } + return equal === lenB; +} + +export function union(a: Nums, b: Nums) { + if (a === b) return a; + + const { startI: sI, startJ: sJ, endI, endJ } = getSuitableIntersectionRange(a, b); + let i = sI, j = sJ; + let commonCount = 0; + while (i < endI && j < endJ) { + const x = a[i], y = b[j]; + if (x < y) { i++; } + else if (x > y) { j++; } + else { i++; j++; commonCount++; } + } + + const lenA = a.length, lenB = b.length; + // A === B || B is subset of A ==> A + if ((commonCount === lenA && commonCount === lenB) || commonCount === lenB) return a; + // A is subset of B ===> B + if (commonCount === lenA) return b; + + const indices = new Int32Array(lenA + lenB - commonCount); + let offset = 0; + + // insert the "prefixes" + for (let k = 0; k < sI; k++) indices[offset++] = a[k]; + for (let k = 0; k < sJ; k++) indices[offset++] = b[k]; + + // insert the common part + i = sI; + j = sJ; + while (i < endI && j < endJ) { + const x = a[i], y = b[j]; + if (x < y) { indices[offset++] = x; i++; } + else if (x > y) { indices[offset++] = y; j++; } + else { indices[offset++] = x; i++; j++; } + } + + // insert the "tail" + for (; i < lenA; i++) indices[offset++] = a[i]; + for (; j < lenB; j++) indices[offset++] = b[j]; + + return ofSortedArray(indices); +} + +export function intersect(a: Nums, b: Nums) { + if (a === b) return a; + + const { startI: sI, startJ: sJ, endI, endJ } = getSuitableIntersectionRange(a, b); + let i = sI, j = sJ; + let commonCount = 0; + while (i < endI && j < endJ) { + const x = a[i], y = b[j]; + if (x < y) { i++; } + else if (x > y) { j++; } + else { i++; j++; commonCount++; } + } + + const lenA = a.length, lenB = b.length; + // no common elements + if (!commonCount) return Empty; + // A === B || B is subset of A ==> B + if ((commonCount === lenA && commonCount === lenB) || commonCount === lenB) return b; + // A is subset of B ==> A + if (commonCount === lenA) return a; + + const indices = new Int32Array(commonCount); + let offset = 0; + i = sI; + j = sJ; + while (i < endI && j < endJ) { + const x = a[i], y = b[j]; + if (x < y) { i++; } + else if (x > y) { j++; } + else { indices[offset++] = x; i++; j++; } + } + + return ofSortedArray(indices); +} + +export function subtract(a: Nums, b: Nums) { + if (a === b) return Empty; + + const lenA = a.length; + const { startI: sI, startJ: sJ, endI, endJ } = getSuitableIntersectionRange(a, b); + let i = sI, j = sJ; + let commonCount = 0; + while (i < endI && j < endJ) { + const x = a[i], y = b[j]; + if (x < y) { i++; } + else if (x > y) { j++; } + else { i++; j++; commonCount++; } + } + + // A isnt intersecting B ===> A + if (!commonCount) return a; + // A === B || A is subset of B ===> Empty + if (commonCount >= lenA) return Empty; + + const indices = new Int32Array(lenA - commonCount); + let offset = 0; + + // insert the "prefix" + for (let k = 0; k < sI; k++) indices[offset++] = a[k]; + + i = sI; + j = sJ; + while (i < endI && j < endJ) { + const x = a[i], y = b[j]; + if (x < y) { indices[offset++] = x; i++; } + else if (x > y) { j++; } + else { i++; j++; } + } + + // insert the "tail" + for (; i < lenA; i++) indices[offset++] = a[i]; + + return ofSortedArray(indices); +} + +const _maxIntRangeRet = { startI: 0, startJ: 0, endI: 0, endJ: 0 }; +// for small sets, just gets the whole range, for large sets does a bunch of binary searches +function getSuitableIntersectionRange(a: Nums, b: Nums) { + const la = a.length, lb = b.length; + const ratio = la / lb; + if (la >= 128 || lb >= 128 || ratio <= 0.34 || ratio >= 2.99) { + _maxIntRangeRet.startI = findPredecessorIndex(a, start(b)); + _maxIntRangeRet.startJ = findPredecessorIndex(b, start(a)); + _maxIntRangeRet.endI = findPredecessorIndex(a, end(b)); + _maxIntRangeRet.endJ = findPredecessorIndex(b, end(a)); + } else { + _maxIntRangeRet.startI = 0; + _maxIntRangeRet.startJ = 0; + _maxIntRangeRet.endI = la; + _maxIntRangeRet.endJ = lb; + } + return _maxIntRangeRet; +} \ No newline at end of file diff --git a/src/mol-data/int/interval.ts b/src/mol-data/int/interval.ts new file mode 100644 index 0000000000000000000000000000000000000000..dc5b0de839c2eb0bbd5fe8d7f0b94a6eb27bf6e2 --- /dev/null +++ b/src/mol-data/int/interval.ts @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import * as Impl from './impl/interval' + +namespace Interval { + export const Empty: Interval = Impl.Empty as any; + + export const ofSingleton: (value: number) => Interval = (v) => Impl.ofRange(v, v) as any; + /** Create interval [min, max] */ + export const ofRange: (min: number, max: number) => Interval = Impl.ofRange as any; + /** Create interval [min, max) */ + export const ofBounds: (start: number, end: number) => Interval = Impl.ofBounds as any; + export const is: (v: any) => v is Interval = Impl.is as any; + + export const has: (interval: Interval, x: number) => boolean = Impl.has as any; + export const indexOf: (interval: Interval, x: number) => number = Impl.indexOf as any; + export const getAt: (interval: Interval, i: number) => number = Impl.getAt as any; + + export const start: (interval: Interval) => number = Impl.start as any; + export const end: (interval: Interval) => number = Impl.end as any; + export const min: (interval: Interval) => number = Impl.min as any; + export const max: (interval: Interval) => number = Impl.max as any; + export const size: (interval: Interval) => number = Impl.size as any; + export const hashCode: (interval: Interval) => number = Impl.hashCode as any; + + export const areEqual: (a: Interval, b: Interval) => boolean = Impl.areEqual as any; + export const areIntersecting: (a: Interval, b: Interval) => boolean = Impl.areIntersecting as any; + export const isSubInterval: (a: Interval, b: Interval) => boolean = Impl.isSubInterval as any; + + export const findPredecessorIndex: (interval: Interval, x: number) => number = Impl.findPredecessorIndex as any; + export const findPredecessorIndexInInterval: (interval: Interval, x: number, bounds: Interval) => number = Impl.findPredecessorIndexInInterval as any; + export const findRange: (interval: Interval, min: number, max: number) => Interval = Impl.findRange as any; + + export const intersect: (a: Interval, b: Interval) => Interval = Impl.intersect as any; +} + +interface Interval { '@type': 'int-interval' } + +export default Interval \ No newline at end of file diff --git a/src/mol-data/int/linked-index.ts b/src/mol-data/int/linked-index.ts new file mode 100644 index 0000000000000000000000000000000000000000..94f119c9bd061d60261b9095a983cceb38c73dec --- /dev/null +++ b/src/mol-data/int/linked-index.ts @@ -0,0 +1,58 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +/** A data structure useful for graph traversal */ +interface LinkedIndex { + readonly head: number, + has(i: number): boolean, + remove(i: number): void +} + +function LinkedIndex(size: number): LinkedIndex { + return new LinkedIndexImpl(size); +} + +class LinkedIndexImpl implements LinkedIndex { + private prev: Int32Array; + private next: Int32Array; + head: number; + + remove(i: number) { + const { prev, next } = this; + const p = prev[i], n = next[i]; + if (p >= 0) { + next[p] = n; + prev[i] = -1; + } + if (n >= 0) { + prev[n] = p; + next[i] = -1; + } + if (i === this.head) { + if (p < 0) this.head = n; + else this.head = p; + } + } + + has(i: number) { + return this.prev[i] >= 0 || this.next[i] >= 0 || this.head === i; + } + + constructor(size: number) { + this.head = size > 0 ? 0 : -1; + this.prev = new Int32Array(size); + this.next = new Int32Array(size); + + for (let i = 0; i < size; i++) { + this.next[i] = i + 1; + this.prev[i] = i - 1; + } + this.prev[0] = -1; + this.next[size - 1] = -1; + } +} + +export default LinkedIndex; \ No newline at end of file diff --git a/src/mol-data/int/ordered-set.ts b/src/mol-data/int/ordered-set.ts new file mode 100644 index 0000000000000000000000000000000000000000..2520643108bf28deb22e2bdc26852b17a37f91d4 --- /dev/null +++ b/src/mol-data/int/ordered-set.ts @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import * as Base from './impl/ordered-set' +import Interval from './interval' + +namespace OrderedSet { + export const Empty: OrderedSet = Base.Empty as any; + export const ofSingleton: (value: number) => OrderedSet = Base.ofSingleton as any; + export const ofRange: (min: number, max: number) => OrderedSet = Base.ofRange as any; + export const ofBounds: (min: number, max: number) => OrderedSet = Base.ofBounds as any; + /** It is the responsibility of the caller to ensure the array is sorted and contains unique values. */ + export const ofSortedArray: (xs: ArrayLike<number>) => OrderedSet = Base.ofSortedArray as any; + + export const has: (set: OrderedSet, x: number) => boolean = Base.has as any; + export const indexOf: (set: OrderedSet, x: number) => number = Base.indexOf as any; + export const getAt: (set: OrderedSet, i: number) => number = Base.getAt as any; + + export const min: (set: OrderedSet) => number = Base.min as any; + export const max: (set: OrderedSet) => number = Base.max as any; + export const size: (set: OrderedSet) => number = Base.size as any; + export const hashCode: (set: OrderedSet) => number = Base.hashCode as any; + + export const areEqual: (a: OrderedSet, b: OrderedSet) => boolean = Base.areEqual as any; + export const areIntersecting: (a: OrderedSet, b: OrderedSet) => boolean = Base.areIntersecting as any; + export const isSubset: (a: OrderedSet, b: OrderedSet) => boolean = Base.isSubset as any; + + export const union: (a: OrderedSet, b: OrderedSet) => OrderedSet = Base.union as any; + export const intersect: (a: OrderedSet, b: OrderedSet) => OrderedSet = Base.intersect as any; + export const subtract: (a: OrderedSet, b: OrderedSet) => OrderedSet = Base.subtract as any; + + export const findPredecessorIndex: (set: OrderedSet, x: number) => number = Base.findPredecessorIndex as any; + export const findPredecessorIndexInInterval: (set: OrderedSet, x: number, range: Interval) => number = Base.findPredecessorIndexInInterval as any; + export const findRange: (set: OrderedSet, min: number, max: number) => Interval = Base.findRange as any; +} + +interface OrderedSet { '@type': 'int-interval' | 'int-sorted-array' } + +export default OrderedSet \ No newline at end of file diff --git a/src/mol-data/int/segmentation.ts b/src/mol-data/int/segmentation.ts new file mode 100644 index 0000000000000000000000000000000000000000..51f5c5ecd54336d228da2de473d5fbc877c8d35c --- /dev/null +++ b/src/mol-data/int/segmentation.ts @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import Interval from './interval' +import OrderedSet from './ordered-set' +import * as Impl from './impl/segmentation' + +namespace Segmentation { + export interface Segment { index: number, start: number, end: number } + + export const create: (segs: ArrayLike<number>) => Segmentation = Impl.create as any; + export const ofOffsets: (offsets: ArrayLike<number>, bounds: Interval) => Segmentation = Impl.ofOffsets as any; + + export const count: (segs: Segmentation) => number = Impl.count as any; + export const getSegment: (segs: Segmentation, value: number) => number = Impl.getSegment as any; + export const projectValue: (segs: Segmentation, set: OrderedSet, value: number) => Interval = Impl.projectValue as any; + + // Segment iterator that mutates a single segment object to mark all the segments. + export const transientSegments: (segs: Segmentation, set: OrderedSet, segment?: Segment) => Impl.SegmentIterator = Impl.segments as any; +} + +interface Segmentation { + '@type': 'segmentation', + readonly segments: ArrayLike<number>, + readonly segmentMap: ArrayLike<number>, + readonly count: number +} + +export default Segmentation \ No newline at end of file diff --git a/src/mol-data/int/sorted-array.ts b/src/mol-data/int/sorted-array.ts new file mode 100644 index 0000000000000000000000000000000000000000..08235061224bfebfdf5f019281936e5c090bb65b --- /dev/null +++ b/src/mol-data/int/sorted-array.ts @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import * as Impl from './impl/sorted-array' +import Interval from './interval' + +namespace SortedArray { + export const Empty: SortedArray = Impl.Empty as any; + export const ofUnsortedArray: (xs: ArrayLike<number>) => SortedArray = Impl.ofUnsortedArray as any; + export const ofSingleton: (v: number) => SortedArray = Impl.ofSingleton as any; + export const ofSortedArray: (xs: ArrayLike<number>) => SortedArray = Impl.ofSortedArray as any; + export const is: (v: any) => v is Interval = Impl.is as any; + + export const has: (array: SortedArray, x: number) => boolean = Impl.has as any; + export const indexOf: (array: SortedArray, x: number) => number = Impl.indexOf as any; + export const indexOfInInterval: (array: SortedArray, x: number, bounds: Interval) => number = Impl.indexOfInInterval as any; + + export const start: (array: SortedArray) => number = Impl.start as any; + export const end: (array: SortedArray) => number = Impl.end as any; + export const min: (array: SortedArray) => number = Impl.min as any; + export const max: (array: SortedArray) => number = Impl.max as any; + export const size: (array: SortedArray) => number = Impl.size as any; + export const hashCode: (array: SortedArray) => number = Impl.hashCode as any; + + export const areEqual: (a: SortedArray, b: SortedArray) => boolean = Impl.areEqual as any; + export const areIntersecting: (a: SortedArray, b: SortedArray) => boolean = Impl.areIntersecting as any; + export const isSubset: (a: SortedArray, b: SortedArray) => boolean = Impl.isSubset as any; + + export const union: (a: SortedArray, b: SortedArray) => SortedArray = Impl.union as any; + export const intersect: (a: SortedArray, b: SortedArray) => SortedArray = Impl.intersect as any; + export const subtract: (a: SortedArray, b: SortedArray) => SortedArray = Impl.subtract as any; + + export const findPredecessorIndex: (array: SortedArray, x: number) => number = Impl.findPredecessorIndex as any; + export const findPredecessorIndexInInterval: (array: SortedArray, x: number, bounds: Interval) => number = Impl.findPredecessorIndexInInterval as any; + export const findRange: (array: SortedArray, min: number, max: number) => Interval = Impl.findRange as any; +} + +interface SortedArray extends ArrayLike<number> { '@type': 'int-sorted-array' } + +export default SortedArray \ No newline at end of file diff --git a/src/mol-data/int/tuple.ts b/src/mol-data/int/tuple.ts new file mode 100644 index 0000000000000000000000000000000000000000..7967f45cff1e784ec19e0db7a32b86b6e1633dcc --- /dev/null +++ b/src/mol-data/int/tuple.ts @@ -0,0 +1,78 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { hash2 } from '../util' + +/** + * Represents a pair of two integers as a double, + * Caution: === does not work, because of NaN, use IntTuple.areEqual for equality + */ +interface IntTuple { '@type': 'int-tuple' } + +namespace IntTuple { + export const Zero: IntTuple = 0 as any; + + const { _int32, _float64, _int32_1, _float64_1 } = (function() { + const data = new ArrayBuffer(8); + const data_1 = new ArrayBuffer(8); + return { + _int32: new Int32Array(data), + _float64: new Float64Array(data), + _int32_1: new Int32Array(data_1), + _float64_1: new Float64Array(data_1) + }; + }()); + + export function is(x: any): x is IntTuple { + return typeof x === 'number'; + } + + export function create(fst: number, snd: number): IntTuple { + _int32[0] = fst; + _int32[1] = snd; + return _float64[0] as any; + } + + export function fst(t: IntTuple): number { + _float64[0] = t as any; + return _int32[0]; + } + + export function snd(t: IntTuple): number { + _float64[0] = t as any; + return _int32[1]; + } + + /** Normal equality does not work, because NaN === NaN ~> false */ + export function areEqual(a: IntTuple, b: IntTuple) { + _float64[0] = a as any; + _float64_1[0] = b as any; + return _int32[0] === _int32_1[0] && _int32[1] === _int32_1[1]; + } + + export function compare(a: IntTuple, b: IntTuple) { + _float64[0] = a as any; + _float64_1[0] = b as any; + const x = _int32[0] - _int32_1[0]; + if (x !== 0) return x; + return _int32[1] - _int32_1[1]; + } + + export function compareInArray(xs: ArrayLike<IntTuple>, i: number, j: number) { + _float64[0] = xs[i] as any; + _float64_1[0] = xs[j] as any; + const x = _int32[0] - _int32_1[0]; + if (x !== 0) return x; + return _int32[1] - _int32_1[1]; + } + + export function hashCode(t: IntTuple) { + _float64[0] = t as any; + return hash2(_int32[0], _int32[1]); + } +} + +export default IntTuple \ No newline at end of file diff --git a/src/mol-data/iterator.ts b/src/mol-data/iterator.ts new file mode 100644 index 0000000000000000000000000000000000000000..106efa325b8c11f8d6640e1bbd135dbb18ff5ce8 --- /dev/null +++ b/src/mol-data/iterator.ts @@ -0,0 +1,120 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +/** + * "Idiomatic" usage: + * + * const it = ...; + * while (it.hasNext) { const v = it.move(); ... } + */ +interface Iterator<T> { + readonly hasNext: boolean, + move(): T +} + +class ArrayIteratorImpl<T> implements Iterator<T> { + private xs: ArrayLike<T> = []; + private index: number = -1; + private length: number = 0; + private lastValue: T; + + hasNext: boolean = false; + + move() { + ++this.index; + this.lastValue = this.xs[this.index]; + this.hasNext = this.index < this.length - 1; + return this.lastValue; + } + + constructor(xs: ArrayLike<T>) { + this.length = xs.length; + this.hasNext = xs.length > 0; + this.xs = xs; + this.index = -1; + // try to avoid deoptimization with undefined values + this.lastValue = xs.length > 0 ? xs[0] : void 0 as any; + } +} + +class RangeIteratorImpl implements Iterator<number> { + private value: number = 0; + hasNext: boolean = false; + + move() { + ++this.value; + this.hasNext = this.value < this.max; + return this.value; + } + + constructor(min: number, private max: number) { + this.value = min - 1; + this.hasNext = max >= min; + } +} + +class ValueIterator<T> implements Iterator<T> { + hasNext = true; + move() { this.hasNext = false; return this.value; } + constructor(private value: T) { } +} + +class MapIteratorImpl<T, R> implements Iterator<R> { + hasNext: boolean = false; + + move() { + const v = this.f(this.base.move()); + this.hasNext = this.base.hasNext; + return v; + } + + constructor(private base: Iterator<T>, private f: (v: T) => R) { + this.hasNext = base.hasNext; + } +} + +class FilterIteratorImpl<T> implements Iterator<T> { + private next: T; + hasNext: boolean; + + move() { + const ret = this.next; + this.hasNext = this.findNext(); + return ret; + } + + private findNext() { + while (this.base.hasNext) { + this.next = this.base.move(); + if (this.p(this.next)) return true; + } + return false; + } + + constructor(private base: Iterator<T>, private p: (v: T) => boolean) { + this.hasNext = this.findNext(); + } +} + +namespace Iterator { + export const Empty: Iterator<any> = new RangeIteratorImpl(0, -1); + export function Array<T>(xs: ArrayLike<T>): Iterator<T> { return new ArrayIteratorImpl<T>(xs); } + export function Value<T>(value: T): Iterator<T> { return new ValueIterator(value); } + export function Range(min: number, max: number): Iterator<number> { return new RangeIteratorImpl(min, max); } + export function map<T, R>(base: Iterator<T>, f: (v: T) => R): Iterator<R> { return new MapIteratorImpl(base, f); } + export function filter<T>(base: Iterator<T>, p: (v: T) => boolean): Iterator<T> { return new FilterIteratorImpl(base, p); } + + // f can return non-undefined falsy value to stop the iteration. + export function forEach<T, Ctx>(it: Iterator<T>, f: (v: T, ctx: Ctx) => boolean | void, ctx: Ctx): Ctx { + while (it.hasNext) { + const c = f(it.move(), ctx); + if (typeof c !== 'undefined' && !c) return ctx; + } + return ctx; + } +} + +export default Iterator \ No newline at end of file diff --git a/src/mol-data/util.ts b/src/mol-data/util.ts new file mode 100644 index 0000000000000000000000000000000000000000..ab0c73fcfc7be65d2974c403b7fc010dc118b95a --- /dev/null +++ b/src/mol-data/util.ts @@ -0,0 +1,15 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import ChunkedArray from './util/chunked-array' +import EquivalenceClasses from './util/chunked-array' +import HashSet from './util/hash-set' +import UniqueArray from './util/unique-array' + +export * from './util/hash-functions' +export * from './util/sort' + +export { ChunkedArray, EquivalenceClasses, HashSet, UniqueArray } \ No newline at end of file diff --git a/src/mol-data/util/chunked-array.ts b/src/mol-data/util/chunked-array.ts new file mode 100644 index 0000000000000000000000000000000000000000..0664a4c12ce99348f609b0d8558e28836f0b8dc2 --- /dev/null +++ b/src/mol-data/util/chunked-array.ts @@ -0,0 +1,137 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * from https://github.com/dsehnal/CIFTools.js + * @author David Sehnal <david.sehnal@gmail.com> + */ + +/** + * A generic chunked array builder. + * + * When adding elements, the array growns by a specified number + * of elements (either linear or exponential growth) and no copying + * is done until ChunkedArray.compact is called. + */ +interface ChunkedArray<T> { + ctor: (size: number) => any, + elementSize: number, + + linearGrowth: boolean, + + initialSize: number, + allocatedSize: number, + elementCount: number, + + currentSize: number, + currentChunk: any, + currentIndex: number, + + chunks: any[] +} + +// TODO: better api, write tests +namespace ChunkedArray { + export function is(x: any): x is ChunkedArray<any> { + return x.creator && x.chunkSize; + } + + function allocateNext(array: ChunkedArray<any>) { + let nextSize = !array.allocatedSize || array.linearGrowth + ? array.initialSize * array.elementSize + : Math.max(Math.ceil(0.61 * array.allocatedSize), 1); + if (nextSize % array.elementSize !== 0) nextSize += nextSize % array.elementSize; + array.currentSize = nextSize; + array.currentIndex = 0; + array.currentChunk = array.ctor(nextSize); + array.allocatedSize += nextSize; + array.chunks[array.chunks.length] = array.currentChunk; + } + + export function add4<T>(array: ChunkedArray<T>, x: T, y: T, z: T, w: T) { + if (array.currentIndex >= array.currentSize) allocateNext(array); + const c = array.currentChunk; + c[array.currentIndex++] = x; + c[array.currentIndex++] = y; + c[array.currentIndex++] = z; + c[array.currentIndex++] = w; + return array.elementCount++; + } + + export function add3<T>(array: ChunkedArray<T>, x: T, y: T, z: T) { + if (array.currentIndex >= array.currentSize) allocateNext(array); + const c = array.currentChunk; + c[array.currentIndex++] = x; + c[array.currentIndex++] = y; + c[array.currentIndex++] = z; + return array.elementCount++; + } + + export function add2<T>(array: ChunkedArray<T>, x: T, y: T) { + if (array.currentIndex >= array.currentSize) allocateNext(array); + const c = array.currentChunk; + c[array.currentIndex++] = x; + c[array.currentIndex++] = y; + return array.elementCount++; + } + + export function add<T>(array: ChunkedArray<T>, x: T) { + if (array.currentIndex >= array.currentSize) allocateNext(array); + array.currentChunk[array.currentIndex++] = x; + return array.elementCount++; + } + + + export function compact<T>(array: ChunkedArray<T>): ArrayLike<T> { + const { ctor, chunks, currentIndex } = array; + + if (!chunks.length) return ctor(0); + if (chunks.length === 1 && currentIndex === array.allocatedSize) { + return chunks[0]; + } + + const ret = ctor(array.elementSize * array.elementCount); + let offset = 0; + + if (ret.buffer) { + for (let i = 0, _i = chunks.length - 1; i < _i; i++) { + ret.set(chunks[i], offset); + offset += chunks[i].length; + } + } else { + for (let i = 0, _i = chunks.length - 1; i < _i; i++) { + const chunk = chunks[i]; + for (let j = 0, _j = chunk.length; j < _j; j++) ret[offset + j] = chunk[j]; + offset += chunk.length; + } + } + + const lastChunk = chunks[chunks.length - 1]; + if (ret.buffer && currentIndex >= array.currentSize) { + ret.set(lastChunk, offset); + } else { + for (let j = 0, _j = lastChunk.length; j < _j; j++) ret[offset + j] = lastChunk[j]; + } + + return ret; + } + + export function create<T>(ctor: (size: number) => any, elementSize: number, initialSize: number, linearGrowth: boolean): ChunkedArray<T> { + return { + ctor, + elementSize, + linearGrowth, + + initialSize, + allocatedSize: 0, + elementCount: 0, + + currentSize: 0, + currentChunk: void 0, + currentIndex: 0, + + chunks: [] + } as ChunkedArray<T>; + } +} + +export default ChunkedArray \ No newline at end of file diff --git a/src/mol-data/util/equivalence-classes.ts b/src/mol-data/util/equivalence-classes.ts new file mode 100644 index 0000000000000000000000000000000000000000..936d39b299ed33956605642cd7b2c8a7041ef368 --- /dev/null +++ b/src/mol-data/util/equivalence-classes.ts @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +class EquivalenceClassesImpl<K, V> { + private id = 0; + private byHash: { [hash: number]: { id: number, keys: K[], value: V }[] } = Object.create(null); + + readonly groups: K[][] = []; + + private createGroup(key: K, value: V) { + const id = this.id++; + const keys = [key]; + this.groups[id] = keys; + return { id, keys, value }; + } + + add(key: K, a: V) { + const hash = this.getHash(a); + if (!!this.byHash[hash]) { + const groups = this.byHash[hash]; + for (let i = 0, _i = groups.length; i < _i; i++) { + const group = groups[i]; + if (this.areEqual(a, group.value)) { + group.keys[group.keys.length] = key; + return group.id; + } + } + const group = this.createGroup(key, a); + groups[groups.length] = group; + return group.id; + } else { + const group = this.createGroup(key, a); + this.byHash[hash] = [group]; + return group.id; + } + } + + constructor(private getHash: (v: V) => any, private areEqual: (a: V, b: V) => boolean) { } +} + +function EquivalenceClasses<K, V>(getHash: (x: V) => any, areEqual: (a: V, b: V) => boolean) { + return new EquivalenceClassesImpl<K, V>(getHash, areEqual); +} + +export default EquivalenceClasses; \ No newline at end of file diff --git a/src/mol-data/util/hash-functions.ts b/src/mol-data/util/hash-functions.ts new file mode 100644 index 0000000000000000000000000000000000000000..00cae92c4d6f16ba47ed7281b5a773d432198164 --- /dev/null +++ b/src/mol-data/util/hash-functions.ts @@ -0,0 +1,54 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +// from http://burtleburtle.net/bob/hash/integer.html +export function hash1(i: number) { + let a = i ^ (i >> 4); + a = (a ^ 0xdeadbeef) + (a << 5); + a = a ^ (a >> 11); + return a; +} + +export function hash2(i: number, j: number) { + let a = 23; + a = (31 * a + i) | 0; + a = (31 * a + j) | 0; + a = a ^ (a >> 4) + a = (a ^ 0xdeadbeef) + (a << 5); + a = a ^ (a >> 11); + return a; +} + +export function hash3(i: number, j: number, k: number) { + let a = 23; + a = (31 * a + i) | 0; + a = (31 * a + j) | 0; + a = (31 * a + k) | 0; + a = a ^ (a >> 4) + a = (a ^ 0xdeadbeef) + (a << 5); + a = a ^ (a >> 11); + return a; +} + +export function hash4(i: number, j: number, k: number, l: number) { + let a = 23; + a = (31 * a + i) | 0; + a = (31 * a + j) | 0; + a = (31 * a + k) | 0; + a = (31 * a + l) | 0; + a = a ^ (a >> 4) + a = (a ^ 0xdeadbeef) + (a << 5); + a = a ^ (a >> 11); + return a; +} + +/** + * A unique number for each pair of integers + * Biggest representable pair is (67108863, 67108863) (limit imposed by Number.MAX_SAFE_INTEGER) + */ +export function cantorPairing(a: number, b: number) { + return (a + b) * (a + b + 1) / 2 + b; +} \ No newline at end of file diff --git a/src/mol-data/util/hash-set.ts b/src/mol-data/util/hash-set.ts new file mode 100644 index 0000000000000000000000000000000000000000..297b8eeb6b9c09c121739a7c9a468d3443a343dc --- /dev/null +++ b/src/mol-data/util/hash-set.ts @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +interface SetLike<T> { + readonly size: number; + add(a: T): boolean; + has(a: T): boolean; +} + +class HashSetImpl<T> implements SetLike<T> { + size: number = 0; + private byHash: { [hash: number]: T[] } = Object.create(null); + + add(a: T) { + const hash = this.getHash(a); + if (!!this.byHash[hash]) { + const xs = this.byHash[hash]; + for (let i = 0, _i = xs.length; i < _i; i++) { + if (this.areEqual(a, xs[i])) return false; + } + xs[xs.length] = a; + this.size++; + return true; + } else { + this.byHash[hash] = [a]; + this.size++; + return true; + } + } + + has(v: T) { + const hash = this.getHash(v); + if (!this.byHash[hash]) return false; + const xs = this.byHash[hash]; + for (let i = 0, _i = xs.length; i < _i; i++) { + if (this.areEqual(v, xs[i])) return true; + } + return false; + } + + constructor(private getHash: (v: T) => any, private areEqual: (a: T, b: T) => boolean) { } +} +// TODO: add implementations with multilevel hashing support? + +function HashSet<T>(getHash: (v: T) => any, areEqual: (a: T, b: T) => boolean): SetLike<T> { + return new HashSetImpl<T>(getHash, areEqual); +} + +export default HashSet; \ No newline at end of file diff --git a/src/mol-data/util/sort.ts b/src/mol-data/util/sort.ts new file mode 100644 index 0000000000000000000000000000000000000000..e6eb6571ea82387a9e121e364ecb6528847d7da9 --- /dev/null +++ b/src/mol-data/util/sort.ts @@ -0,0 +1,159 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +export type Comparer<T = any> = (data: T, i: number, j: number) => number +export type Swapper<T = any> = (data: T, i: number, j: number) => void + +type Ctx = { cmp: Comparer, swap: Swapper, parts: number[], data: any } + +export function arrayLess(arr: ArrayLike<number>, i: number, j: number) { + return arr[i] - arr[j]; +} + +export function arraySwap(arr: any[], i: number, j: number) { + const temp = arr[i]; + arr[i] = arr[j]; + arr[j] = temp; +} + +function medianPivotIndex(data: any, cmp: Comparer, l: number, r: number) { + const m = (l + r) >> 1; + if (cmp(data, l, r) > 0) return cmp(data, l, m) > 0 ? cmp(data, m, r) > 0 ? m : r : l; + else return cmp(data, r, m) > 0 ? cmp(data, m, l) > 0 ? m : l : r; +} + +function partition(ctx: Ctx, l: number, r: number) { + const { cmp, swap, data, parts } = ctx; + let equals = l + 1, tail = r; + + // move the median to the 1st spot + swap(data, l, medianPivotIndex(data, cmp, l, r)); + + while (cmp(data, tail, l) > 0) { --tail; } + for (let i = l + 1; i <= tail; i++) { + const c = cmp(data, i, l); + if (c > 0) { + swap(data, i, tail); + --tail; + while (cmp(data, tail, l) > 0) { --tail; } + i--; + } else if (c === 0) { + swap(data, i, equals); + equals++; + } + } + + // move the medians to the correct spots + for (let i = l; i < equals; i++) { swap(data, i, l + tail - i); } + parts[0] = tail - equals + l + 1; + parts[1] = tail; +} + +function insertionSort({ data, cmp, swap }: Ctx, start: number, end: number) { + for (let i = start + 1; i <= end; i++) { + let j = i - 1; + while (j >= start && cmp(data, j, j + 1) > 0) { + swap(data, j, j + 1); + j = j - 1; + } + } +} + +function quickSort(ctx: Ctx, low: number, high: number) { + const { parts } = ctx; + while (low < high) { + if (high - low < 16) { + insertionSort(ctx, low, high); + return; + } + + partition(ctx, low, high); + const li = parts[0], ri = parts[1]; + + if (li - low < high - ri) { + quickSort(ctx, low, li - 1); + low = ri + 1; + } else { + quickSort(ctx, ri + 1, high); + high = li - 1; + } + } +} + +function partitionArrayAsc(data: number[], parts: number[], l: number, r: number) { + let equals = l + 1, tail = r; + + // move the median to the 1st spot + arraySwap(data, l, medianPivotIndex(data, arrayLess, l, r)); + const pivot = data[l]; + + while (data[tail] > pivot) { --tail; } + for (let i = l + 1; i <= tail; i++) { + const v = data[i]; + if (v > pivot) { + arraySwap(data, i, tail); + --tail; + while (data[tail] > pivot) { --tail; } + i--; + } else if (v === pivot) { + arraySwap(data, i, equals); + ++equals; + } + } + + // move all medians to the correct spots + for (let i = l; i < equals; i++) { arraySwap(data, i, l + tail - i); } + parts[0] = tail - equals + l + 1; + parts[1] = tail; +} + +function insertionSortArrayAsc(data: number[], start: number, end: number) { + for (let i = start + 1; i <= end; i++) { + const key = data[i]; + let j = i - 1; + while (j >= start && data[j] > key) { + data[j + 1] = data[j]; + j = j - 1; + } + data[j + 1] = key; + } +} + +function quickSortArrayAsc(data: number[], parts: number[], low: number, high: number) { + while (low < high) { + if (high - low < 16) { + insertionSortArrayAsc(data, low, high); + return; + } + + partitionArrayAsc(data, parts, low, high); + const li = parts[0], ri = parts[1]; + + if (li - low < high - ri) { + quickSortArrayAsc(data, parts, low, li - 1); + low = ri + 1; + } else { + quickSortArrayAsc(data, parts, ri + 1, high); + high = li - 1; + } + } +} + +export function sortArray(data: ArrayLike<number>, cmp: Comparer<ArrayLike<number>> = arrayLess): ArrayLike<number> { + return sortArrayRange(data, 0, data.length, cmp); +} + +export function sortArrayRange(data: ArrayLike<number>, start: number, end: number, cmp: Comparer<ArrayLike<number>> = arrayLess): ArrayLike<number> { + if (cmp === arrayLess) quickSortArrayAsc(data as any, [0, 0], start, end - 1); + else quickSort({ data, cmp, swap: arraySwap, parts: [0, 0] }, start, end - 1); + return data; +} + +export function sort<T>(data: T, start: number, end: number, cmp: Comparer<T>, swap: Swapper<T>): T { + const ctx: Ctx = { data, cmp, swap, parts: [0, 0] }; + quickSort(ctx, start, end - 1); + return data; +} \ No newline at end of file diff --git a/src/mol-data/util/unique-array.ts b/src/mol-data/util/unique-array.ts new file mode 100644 index 0000000000000000000000000000000000000000..f628baa9601ff5d14c01a8af256b996fcf150786 --- /dev/null +++ b/src/mol-data/util/unique-array.ts @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +interface UniqueArray<K, T> { + keys: Set<K>, + array: T[] +} + +namespace UniqueArray { + export function create<K, T>(): UniqueArray<K, T> { + return { keys: new Set<K>(), array: [] }; + } + + export function add<K, T>({ keys, array }: UniqueArray<K, T>, key: K, value: T) { + if (keys.has(key)) return; + keys.add(key); + array[array.length] = value; + } +} + +export default UniqueArray \ No newline at end of file diff --git a/src/mol-io/common/binary-cif.ts b/src/mol-io/common/binary-cif.ts new file mode 100644 index 0000000000000000000000000000000000000000..f63c05d25981e1eb1abf07ec27ce0c67696cccd9 --- /dev/null +++ b/src/mol-io/common/binary-cif.ts @@ -0,0 +1,11 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import decode from './binary-cif/decoder' + +export * from './binary-cif/encoding' +export * from './binary-cif/array-encoder' +export { decode } \ No newline at end of file diff --git a/src/mol-io/common/binary-cif/array-encoder.ts b/src/mol-io/common/binary-cif/array-encoder.ts new file mode 100644 index 0000000000000000000000000000000000000000..344171b8a8a910f45d32d6c6d885ab9c46b6200f --- /dev/null +++ b/src/mol-io/common/binary-cif/array-encoder.ts @@ -0,0 +1,396 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * Adapted from CIFTools.js (https://github.com/dsehnal/CIFTools.js; MIT) and MMTF (https://github.com/rcsb/mmtf-javascript/; MIT) + * + * @author David Sehnal <david.sehnal@gmail.com> + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +import ChunkedArray from 'mol-data/util/chunked-array' +import { Encoding, EncodedData } from './encoding' + +export interface ArrayEncoder { + and(f: ArrayEncoding.Provider): ArrayEncoder, + encode(data: ArrayLike<any>): EncodedData +} + +export class ArrayEncoderImpl implements ArrayEncoder { + and(f: ArrayEncoding.Provider) { + return new ArrayEncoderImpl(this.providers.concat([f])); + } + + encode(data: ArrayLike<any>): EncodedData { + let encoding: Encoding[] = []; + for (let p of this.providers) { + let t = p(data); + + if (!t.encodings.length) { + throw new Error('Encodings must be non-empty.'); + } + + data = t.data; + for (let e of t.encodings) { + encoding.push(e); + } + } + if (!(data instanceof Uint8Array)) { + throw new Error('The encoding must result in a Uint8Array. Fix your encoding chain.'); + } + return { + encoding, + data + } + } + + constructor(private providers: ArrayEncoding.Provider[]) { + + } +} + +export namespace ArrayEncoder { + export function by(f: ArrayEncoding.Provider): ArrayEncoder { + return new ArrayEncoderImpl([f]); + } +} + +export namespace ArrayEncoding { + export type TypedArrayCtor = { new(size: number): ArrayLike<number> & { buffer: ArrayBuffer, byteLength: number, byteOffset: number, BYTES_PER_ELEMENT: number } } + + export interface Result { + encodings: Encoding[], + data: any + } + + export type Provider = (data: any) => Result + + export function by(f: Provider): ArrayEncoder { + return new ArrayEncoderImpl([f]); + } + + function uint8(data: Uint8Array): Result { + return { + encodings: [{ kind: 'ByteArray', type: Encoding.IntDataType.Uint8 }], + data + }; + } + + function int8(data: Int8Array): Result { + return { + encodings: [{ kind: 'ByteArray', type: Encoding.IntDataType.Int8 }], + data: new Uint8Array(data.buffer, data.byteOffset) + }; + } + + const writers = { + [Encoding.IntDataType.Int16]: function (v: DataView, i: number, a: number) { v.setInt16(2 * i, a, true) }, + [Encoding.IntDataType.Uint16]: function (v: DataView, i: number, a: number) { v.setUint16(2 * i, a, true) }, + [Encoding.IntDataType.Int32]: function (v: DataView, i: number, a: number) { v.setInt32(4 * i, a, true) }, + [Encoding.IntDataType.Uint32]: function (v: DataView, i: number, a: number) { v.setUint32(4 * i, a, true) }, + [Encoding.FloatDataType.Float32]: function (v: DataView, i: number, a: number) { v.setFloat32(4 * i, a, true) }, + [Encoding.FloatDataType.Float64]: function (v: DataView, i: number, a: number) { v.setFloat64(8 * i, a, true) } + } + + const byteSizes = { + [Encoding.IntDataType.Int16]: 2, + [Encoding.IntDataType.Uint16]: 2, + [Encoding.IntDataType.Int32]: 4, + [Encoding.IntDataType.Uint32]: 4, + [Encoding.FloatDataType.Float32]: 4, + [Encoding.FloatDataType.Float64]: 8 + } + + export function byteArray(data: Encoding.FloatArray | Encoding.IntArray) { + let type = Encoding.getDataType(data); + + if (type === Encoding.IntDataType.Int8) return int8(data as Int8Array); + else if (type === Encoding.IntDataType.Uint8) return uint8(data as Uint8Array); + + let result = new Uint8Array(data.length * byteSizes[type]); + let w = writers[type]; + let view = new DataView(result.buffer); + for (let i = 0, n = data.length; i < n; i++) { + w(view, i, data[i]); + } + return { + encodings: [<Encoding.ByteArray>{ kind: 'ByteArray', type }], + data: result + }; + } + + function _fixedPoint(data: Encoding.FloatArray, factor: number): Result { + let srcType = Encoding.getDataType(data) as Encoding.FloatDataType; + let result = new Int32Array(data.length); + for (let i = 0, n = data.length; i < n; i++) { + result[i] = Math.round(data[i] * factor); + } + return { + encodings: [{ kind: 'FixedPoint', factor, srcType }], + data: result + }; + } + export function fixedPoint(factor: number): Provider { return data => _fixedPoint(data as Encoding.FloatArray, factor); } + + function _intervalQuantizaiton(data: Encoding.FloatArray, min: number, max: number, numSteps: number, arrayType: new (size: number) => Encoding.IntArray): Result { + let srcType = Encoding.getDataType(data) as Encoding.FloatDataType; + if (!data.length) { + return { + encodings: [{ kind: 'IntervalQuantization', min, max, numSteps, srcType }], + data: new Int32Array(0) + }; + } + + if (max < min) { + let t = min; + min = max; + max = t; + } + + let delta = (max - min) / (numSteps - 1); + + let output = new arrayType(data.length); + for (let i = 0, n = data.length; i < n; i++) { + let v = data[i]; + if (v <= min) output[i] = 0; + else if (v >= max) output[i] = numSteps; + else output[i] = (Math.round((v - min) / delta)) | 0; + } + + return { + encodings: [{ kind: 'IntervalQuantization', min, max, numSteps, srcType }], + data: output + }; + } + export function intervalQuantizaiton(min: number, max: number, numSteps: number, arrayType: new (size: number) => Encoding.IntArray = Int32Array): Provider { + return data => _intervalQuantizaiton(data as Encoding.FloatArray, min, max, numSteps, arrayType); + } + + export function runLength(data: Encoding.IntArray): Result { + let srcType = Encoding.getDataType(data) as Encoding.IntDataType; + if (srcType === void 0) { + data = new Int32Array(data); + srcType = Encoding.IntDataType.Int32; + } + + if (!data.length) { + return { + encodings: [{ kind: 'RunLength', srcType, srcSize: 0 }], + data: new Int32Array(0) + }; + } + + // calculate output size + let fullLength = 2; + for (let i = 1, il = data.length; i < il; i++) { + if (data[i - 1] !== data[i]) { + fullLength += 2; + } + } + let output = new Int32Array(fullLength); + let offset = 0; + let runLength = 1; + for (let i = 1, il = data.length; i < il; i++) { + if (data[i - 1] !== data[i]) { + output[offset] = data[i - 1]; + output[offset + 1] = runLength; + runLength = 1; + offset += 2; + } else { + ++runLength; + } + } + output[offset] = data[data.length - 1]; + output[offset + 1] = runLength; + return { + encodings: [{ kind: 'RunLength', srcType, srcSize: data.length }], + data: output + }; + } + + export function delta(data: Int8Array | Int16Array | Int32Array): Result { + if (!Encoding.isSignedIntegerDataType(data)) { + throw new Error('Only signed integer types can be encoded using delta encoding.'); + } + + let srcType = Encoding.getDataType(data) as Encoding.IntDataType; + if (srcType === void 0) { + data = new Int32Array(data); + srcType = Encoding.IntDataType.Int32; + } + if (!data.length) { + return { + encodings: [{ kind: 'Delta', origin: 0, srcType }], + data: new (data as any).constructor(0) + }; + } + + let output = new (data as any).constructor(data.length); + let origin = data[0]; + output[0] = data[0]; + for (let i = 1, n = data.length; i < n; i++) { + output[i] = data[i] - data[i - 1]; + } + output[0] = 0; + return { + encodings: [{ kind: 'Delta', origin, srcType }], + data: output + }; + } + + function isSigned(data: Int32Array) { + for (let i = 0, n = data.length; i < n; i++) { + if (data[i] < 0) return true; + } + return false; + } + + function packingSize(data: Int32Array, upperLimit: number) { + let lowerLimit = -upperLimit - 1; + let size = 0; + for (let i = 0, n = data.length; i < n; i++) { + let value = data[i]; + if (value === 0) { + size += 1; + } else if (value > 0) { + size += Math.ceil(value / upperLimit); + if (value % upperLimit === 0) size += 1; + } else { + size += Math.ceil(value / lowerLimit); + if (value % lowerLimit === 0) size += 1; + } + } + return size; + } + + function determinePacking(data: Int32Array): { isSigned: boolean, size: number, bytesPerElement: number } { + let signed = isSigned(data); + let size8 = signed ? packingSize(data, 0x7F) : packingSize(data, 0xFF); + let size16 = signed ? packingSize(data, 0x7FFF) : packingSize(data, 0xFFFF); + + if (data.length * 4 < size16 * 2) { + // 4 byte packing is the most effective + return { + isSigned: signed, + size: data.length, + bytesPerElement: 4 + }; + } else if (size16 * 2 < size8) { + // 2 byte packing is the most effective + return { + isSigned: signed, + size: size16, + bytesPerElement: 2 + } + } else { + // 1 byte packing is the most effective + return { + isSigned: signed, + size: size8, + bytesPerElement: 1 + } + }; + } + + function _integerPacking(data: Int32Array, packing: { isSigned: boolean, size: number, bytesPerElement: number }): Result { + let upperLimit = packing.isSigned + ? (packing.bytesPerElement === 1 ? 0x7F : 0x7FFF) + : (packing.bytesPerElement === 1 ? 0xFF : 0xFFFF); + + let lowerLimit = -upperLimit - 1; + let n = data.length; + let packed = packing.isSigned + ? packing.bytesPerElement === 1 ? new Int8Array(packing.size) : new Int16Array(packing.size) + : packing.bytesPerElement === 1 ? new Uint8Array(packing.size) : new Uint16Array(packing.size); + let j = 0; + for (let i = 0; i < n; i++) { + let value = data[i]; + if (value >= 0) { + while (value >= upperLimit) { + packed[j] = upperLimit; + ++j; + value -= upperLimit; + } + } else { + while (value <= lowerLimit) { + packed[j] = lowerLimit; + ++j; + value -= lowerLimit; + } + } + packed[j] = value; + ++j; + } + + let result = byteArray(packed); + return { + encodings: [{ + kind: 'IntegerPacking', + byteCount: packing.bytesPerElement, + isUnsigned: !packing.isSigned, + srcSize: n + }, + result.encodings[0] + ], + data: result.data + }; + } + + /** + * Packs Int32 array. The packing level is determined automatically to either 1-, 2-, or 4-byte words. + */ + export function integerPacking(data: Int32Array): Result { + if (!(data instanceof Int32Array)) { + throw new Error('Integer packing can only be applied to Int32 data.'); + } + + let packing = determinePacking(data); + + if (packing.bytesPerElement === 4) { + // no packing done, Int32 encoding will be used + return byteArray(data); + } + + return _integerPacking(data, packing); + } + + export function stringArray(data: string[]): Result { + let map: any = Object.create(null); + let strings: string[] = []; + let accLength = 0; + let offsets = ChunkedArray.create<number>(s => new Int32Array(s), 1, 1024, true) + let output = new Int32Array(data.length); + + ChunkedArray.add(offsets, 0); + let i = 0; + for (let s of data) { + // handle null strings. + if (s === null || s === void 0) { + output[i++] = -1; + continue; + } + + let index = map[s]; + if (index === void 0) { + // increment the length + accLength += s.length; + + // store the string and index + index = strings.length; + strings[index] = s; + map[s] = index; + + // write the offset + ChunkedArray.add(offsets, accLength); + } + output[i++] = index; + } + + let encOffsets = ArrayEncoder.by(delta).and(integerPacking).encode(ChunkedArray.compact(offsets)); + let encOutput = ArrayEncoder.by(delta).and(runLength).and(integerPacking).encode(output); + + return { + encodings: [{ kind: 'StringArray', dataEncoding: encOutput.encoding, stringData: strings.join(''), offsetEncoding: encOffsets.encoding, offsets: encOffsets.data }], + data: encOutput.data + }; + } +} \ No newline at end of file diff --git a/src/reader/cif/binary/decoder.ts b/src/mol-io/common/binary-cif/decoder.ts similarity index 90% rename from src/reader/cif/binary/decoder.ts rename to src/mol-io/common/binary-cif/decoder.ts index 2e06613900110aa4b9f463be8764b1d9453e1c45..e9720e52fd6d36570a4eb59162124f62c9f165e5 100644 --- a/src/reader/cif/binary/decoder.ts +++ b/src/mol-io/common/binary-cif/decoder.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2017 molio contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. * * From CIFTools.js * @author David Sehnal <david.sehnal@gmail.com> @@ -137,7 +137,7 @@ function runLength(data: Int32Array, encoding: Encoding.RunLength) { function delta(data: (Int8Array | Int16Array | Int32Array), encoding: Encoding.Delta) { let n = data.length; let output = getIntArray(encoding.srcType, n); - if (!n) return output; + if (!n) return data; output[0] = data[0] + (encoding.origin | 0); for (let i = 1; i < n; ++i) { output[i] = data[i] + output[i - 1]; @@ -189,27 +189,25 @@ function integerPackingUnsigned(data: (Int8Array | Int16Array), encoding: Encodi } function integerPacking(data: (Int8Array | Int16Array), encoding: Encoding.IntegerPacking) { + if (data.length === encoding.srcSize) return data; return encoding.isUnsigned ? integerPackingUnsigned(data, encoding) : integerPackingSigned(data, encoding); } function stringArray(data: Uint8Array, encoding: Encoding.StringArray) { - let str = encoding.stringData; - let offsets = decode({ encoding: encoding.offsetEncoding, data: encoding.offsets }); - let indices = decode({ encoding: encoding.dataEncoding, data }); - let cache: any = Object.create(null); - let result = new Array(indices.length); + const offsets = decode({ encoding: encoding.offsetEncoding, data: encoding.offsets }); + const indices = decode({ encoding: encoding.dataEncoding, data }); + + const str = encoding.stringData; + const strings = new Array(offsets.length); + strings[0] = ''; + for (let i = 1, _i = offsets.length; i < _i; i++) { + strings[i] = str.substring(offsets[i - 1], offsets[i]); + } + let offset = 0; - for (let i of indices) { - if (i < 0) { - result[offset++] = null; - continue; - } - let v = cache[i]; - if (v === void 0) { - v = str.substring(offsets[i], offsets[i + 1]); - cache[i] = v; - } - result[offset++] = v; + const result = new Array(indices.length); + for (let i = 0, _i = indices.length; i < _i; i++) { + result[offset++] = strings[indices[i] + 1]; } return result; } \ No newline at end of file diff --git a/src/reader/cif/binary/encoding.ts b/src/mol-io/common/binary-cif/encoding.ts similarity index 97% rename from src/reader/cif/binary/encoding.ts rename to src/mol-io/common/binary-cif/encoding.ts index 6bf50cf1a8db22ee8fc4657b3cff4208965b4cf9..f36abc52790b011b41a9737f78636b781f2f73c3 100644 --- a/src/reader/cif/binary/encoding.ts +++ b/src/mol-io/common/binary-cif/encoding.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2017 molio contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. * * From CIFTools.js * @author David Sehnal <david.sehnal@gmail.com> diff --git a/src/utils/msgpack/decode.ts b/src/mol-io/common/msgpack/decode.ts similarity index 98% rename from src/utils/msgpack/decode.ts rename to src/mol-io/common/msgpack/decode.ts index 3c43aefcb9192429059934b0d4a499b5de76ae1f..5d465550eab4b6e0a6eb1ccfbb6e811f9d72afdd 100644 --- a/src/utils/msgpack/decode.ts +++ b/src/mol-io/common/msgpack/decode.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 molio contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. * * Adapted from https://github.com/rcsb/mmtf-javascript * @author Alexander Rose <alexander.rose@weirdbyte.de> diff --git a/src/utils/msgpack/encode.ts b/src/mol-io/common/msgpack/encode.ts similarity index 97% rename from src/utils/msgpack/encode.ts rename to src/mol-io/common/msgpack/encode.ts index 4c636d3026f8716710dfafa8614f802b7a0b60e9..fb79a65496c56c1dc40e9667b805092b84d1770e 100644 --- a/src/utils/msgpack/encode.ts +++ b/src/mol-io/common/msgpack/encode.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 molio contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. * * Adapted from https://github.com/rcsb/mmtf-javascript * @author Alexander Rose <alexander.rose@weirdbyte.de> @@ -284,7 +284,8 @@ function encodeInternal(value: any, view: DataView, bytes: Uint8Array, offset: n } } else { - for (let key of keys!) { + for (let i = 0, _i = keys!.length; i < _i; i++) { + const key = keys![i]; size += encodeInternal(key, view, bytes, offset + size); size += encodeInternal(value[key], view, bytes, offset + size); } diff --git a/src/utils/utf8.ts b/src/mol-io/common/utf8.ts similarity index 97% rename from src/utils/utf8.ts rename to src/mol-io/common/utf8.ts index ff7c8a6fb7ef0d374e99b160f969b3a70d260f5e..74f35811b389c4b4dd1e2266422cd6f028e52fdf 100644 --- a/src/utils/utf8.ts +++ b/src/mol-io/common/utf8.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2017 molio contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. * * Adapted from https://github.com/rcsb/mmtf-javascript * @author Alexander Rose <alexander.rose@weirdbyte.de> diff --git a/src/reader/spec/cif.spec.ts b/src/mol-io/reader/_spec/cif.spec.ts similarity index 64% rename from src/reader/spec/cif.spec.ts rename to src/mol-io/reader/_spec/cif.spec.ts index accb0ab6abf1d8df80344aed155d013653f9435a..90fae982aebd67108262b6672414c6c257c0c593 100644 --- a/src/reader/spec/cif.spec.ts +++ b/src/mol-io/reader/_spec/cif.spec.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2017 molio contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author David Sehnal <david.sehnal@gmail.com> */ @@ -7,34 +7,35 @@ import * as Data from '../cif/data-model' import TextField from '../cif/text/field' import * as Schema from '../cif/schema' +import { Column } from 'mol-data/db' const columnData = `123abc`; const intField = TextField({ data: columnData, indices: [0, 1, 1, 2, 2, 3], count: 3 }, 3); const strField = TextField({ data: columnData, indices: [3, 4, 4, 5, 5, 6], count: 3 }, 3); -const testBlock = Data.Block({ - _atoms: Data.Category(3, { +const testBlock = Data.Block(['atoms'], { + atoms: Data.Category('atoms', 3, ['x', 'name'], { x: intField, name: strField }) }, 'test'); namespace TestSchema { - export const atoms = { x: Schema.Field.int(), name: Schema.Field.str() } + export const atoms = { x: Column.Schema.int, name: Column.Schema.str } export const schema = { atoms } } describe('schema', () => { - const data = Schema.toTypedFrame(TestSchema.schema, testBlock); + const db = Schema.toDatabase(TestSchema.schema, testBlock); it('property access', () => { - const { x, name } = data.atoms; + const { x, name } = db.atoms; expect(x.value(0)).toBe(1); expect(name.value(1)).toBe('b'); }); it('toArray', () => { - const ret = data.atoms.x.toArray({ array: Int32Array }); + const ret = db.atoms.x.toArray({ array: Int32Array }); expect(ret.length).toBe(3); expect(ret[0]).toBe(1); expect(ret[1]).toBe(2); diff --git a/src/reader/spec/column.spec.ts b/src/mol-io/reader/_spec/column.spec.ts similarity index 81% rename from src/reader/spec/column.spec.ts rename to src/mol-io/reader/_spec/column.spec.ts index 1bd08dfdb5f7e537ef173b34c5d982310b2e3265..1f7b4be1d6b753b773e2b4119742959c447adb04 100644 --- a/src/reader/spec/column.spec.ts +++ b/src/mol-io/reader/_spec/column.spec.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2017 molio contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Alexander Rose <alexander.rose@weirdbyte.de> * @author David Sehnal <david.sehnal@gmail.com> @@ -7,7 +7,7 @@ import FixedColumn from '../common/text/column/fixed' import TokenColumn from '../common/text/column/token' -import { ColumnType, typedArrayWindow } from '../common/column' +import { Column, ColumnHelpers } from 'mol-data/db' const lines = [ '1.123 abc', @@ -32,8 +32,8 @@ const linesTokens = (function () { describe('fixed text column', () => { const col = FixedColumn({ data: linesData, indices: linesTokens, count: lines.length }); - const col1 = col(0, 5, ColumnType.float); - const col2 = col(5, 4, ColumnType.str); + const col1 = col(0, 5, Column.Schema.float); + const col2 = col(5, 4, Column.Schema.str); it('number', () => { expect(col1.value(0)).toBe(1.123); expect(col1.value(1)).toBe(1.0); @@ -53,7 +53,7 @@ describe('fixed text column', () => { describe('token text column', () => { const tokensData = '321'; const col = TokenColumn({ data: tokensData, indices: [0, 1, 1, 2, 2, 3], count: 3 }); - const col1 = col(ColumnType.int); + const col1 = col(Column.Schema.int); it('number', () => { expect(col1.value(0)).toBe(3); expect(col1.value(1)).toBe(2); @@ -64,8 +64,8 @@ describe('token text column', () => { describe('binary column', () => { it('window works', () => { const xs = new Float64Array([1, 2, 3, 4]); - const w1 = typedArrayWindow(xs, { start: 1 }); - const w2 = typedArrayWindow(xs, { start: 2, end: 4 }); + const w1 = ColumnHelpers.typedArrayWindow(xs, { start: 1 }); + const w2 = ColumnHelpers.typedArrayWindow(xs, { start: 2, end: 4 }); expect(w1.length).toBe(3); for (let i = 0; i < w1.length; i++) expect(w1[i]).toBe(xs[i + 1]); diff --git a/src/reader/spec/gro.spec.ts b/src/mol-io/reader/_spec/gro.spec.ts similarity index 97% rename from src/reader/spec/gro.spec.ts rename to src/mol-io/reader/_spec/gro.spec.ts index 9f74a79602714530491eb479b3ddb48125620e0b..888f23c83369fa8fc27e4adec486aab86003e7ab 100644 --- a/src/reader/spec/gro.spec.ts +++ b/src/mol-io/reader/_spec/gro.spec.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2017 molio contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Alexander Rose <alexander.rose@weirdbyte.de> * @author David Sehnal <david.sehnal@gmail.com> diff --git a/src/mol-io/reader/cif.ts b/src/mol-io/reader/cif.ts new file mode 100644 index 0000000000000000000000000000000000000000..7e2782710310b5f599b9cb71d811703c16c81413 --- /dev/null +++ b/src/mol-io/reader/cif.ts @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import parseText from './cif/text/parser' +import parseBinary from './cif/binary/parser' +import { Frame } from './cif/data-model' +import { toDatabase } from './cif/schema' +import { mmCIF_Schema, mmCIF_Database } from './cif/schema/mmcif' + +export default { + parseText, + parseBinary, + toDatabase, + schema: { + mmCIF: (frame: Frame) => toDatabase<mmCIF_Schema, mmCIF_Database>(mmCIF_Schema, frame) + } +} + +export * from './cif/data-model' \ No newline at end of file diff --git a/src/reader/cif/binary/field.ts b/src/mol-io/reader/cif/binary/field.ts similarity index 54% rename from src/reader/cif/binary/field.ts rename to src/mol-io/reader/cif/binary/field.ts index 2c8821e685a49c205f23fbe7cd4f7be86b3040e1..c3b2ad33faaffda2590d25d08f34b1c4d215218f 100644 --- a/src/reader/cif/binary/field.ts +++ b/src/mol-io/reader/cif/binary/field.ts @@ -1,26 +1,25 @@ /** - * Copyright (c) 2017 molio contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author David Sehnal <david.sehnal@gmail.com> */ -import * as Column from '../../common/column' +import { Column, ColumnHelpers } from 'mol-data/db' import * as Data from '../data-model' -import { EncodedColumn } from './encoding' -import decode from './decoder' +import { EncodedColumn, decode } from '../../../common/binary-cif' import { parseInt as fastParseInt, parseFloat as fastParseFloat } from '../../common/text/number-parser' export default function Field(column: EncodedColumn): Data.Field { const mask = column.mask ? decode(column.mask) as number[] : void 0; const data = decode(column.data); - const isNumeric = Column.isTypedArray(data); + const isNumeric = ColumnHelpers.isTypedArray(data); const str: Data.Field['str'] = isNumeric ? mask - ? row => mask[row] === Data.ValuePresence.Present ? '' + data[row] : '' + ? row => mask[row] === Column.ValueKind.Present ? '' + data[row] : '' : row => '' + data[row] : mask - ? row => mask[row] === Data.ValuePresence.Present ? data[row] : '' + ? row => mask[row] === Column.ValueKind.Present ? data[row] : '' : row => data[row]; const int: Data.Field['int'] = isNumeric @@ -31,27 +30,27 @@ export default function Field(column: EncodedColumn): Data.Field { ? row => data[row] : row => { const v = data[row]; return fastParseFloat(v, 0, v.length); }; - const presence: Data.Field['presence'] = mask + const valueKind: Data.Field['valueKind'] = mask ? row => mask[row] - : row => Data.ValuePresence.Present; + : row => Column.ValueKind.Present; const rowCount = data.length; return { + '@array': data, isDefined: true, rowCount, str, int, float, - presence, + valueKind, areValuesEqual: (rowA, rowB) => data[rowA] === data[rowB], - stringEquals: (row, v) => str(row) === v, - toStringArray: params => Column.createAndFillArray(rowCount, str, params), + toStringArray: params => ColumnHelpers.createAndFillArray(rowCount, str, params), toIntArray: isNumeric - ? params => Column.typedArrayWindow(data, params) - : params => Column.createAndFillArray(rowCount, int, params), + ? params => ColumnHelpers.typedArrayWindow(data, params) + : params => ColumnHelpers.createAndFillArray(rowCount, int, params), toFloatArray: isNumeric - ? params => Column.typedArrayWindow(data, params) - : params => Column.createAndFillArray(rowCount, float, params) + ? params => ColumnHelpers.typedArrayWindow(data, params) + : params => ColumnHelpers.createAndFillArray(rowCount, float, params) }; } \ No newline at end of file diff --git a/src/reader/cif/binary/parser.ts b/src/mol-io/reader/cif/binary/parser.ts similarity index 61% rename from src/reader/cif/binary/parser.ts rename to src/mol-io/reader/cif/binary/parser.ts index 958f6bfa291aef580a6d56ac3f1adaca1cc510cc..4f1b705eb62810531f2d5665def1b88b189e82d4 100644 --- a/src/reader/cif/binary/parser.ts +++ b/src/mol-io/reader/cif/binary/parser.ts @@ -1,15 +1,15 @@ /** - * Copyright (c) 2017 molio contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author David Sehnal <david.sehnal@gmail.com> */ import * as Data from '../data-model' -import * as Encoding from './encoding' +import { EncodedCategory, EncodedFile } from '../../../common/binary-cif' import Field from './field' import Result from '../../result' -import decodeMsgPack from '../../../utils/msgpack/decode' -import Computation from '../../../utils/computation' +import decodeMsgPack from '../../../common/msgpack/decode' +import Computation from 'mol-util/computation' function checkVersions(min: number[], current: number[]) { for (let i = 0; i < 2; i++) { @@ -18,14 +18,20 @@ function checkVersions(min: number[], current: number[]) { return true; } -function Category(data: Encoding.EncodedCategory): Data.Category { +function Category(data: EncodedCategory): Data.Category { const map = Object.create(null); + const cache = Object.create(null); for (const col of data.columns) map[col.name] = col; return { rowCount: data.rowCount, + name: data.name.substr(1), + fieldNames: data.columns.map(c => c.name), getField(name) { const col = map[name]; - return col ? Field(col) : Data.DefaultUndefinedField(data.rowCount); + if (!col) return void 0; + if (!!cache[name]) return cache[name]; + cache[name] = Field(col); + return cache[name]; } } } @@ -35,14 +41,14 @@ export default function parse(data: Uint8Array) { const minVersion = [0, 3]; try { - const unpacked = decodeMsgPack(data) as Encoding.EncodedFile; + const unpacked = decodeMsgPack(data) as EncodedFile; if (!checkVersions(minVersion, unpacked.version.match(/(\d)\.(\d)\.\d/)!.slice(1).map(v => +v))) { return Result.error<Data.File>(`Unsupported format version. Current ${unpacked.version}, required ${minVersion.join('.')}.`); } const file = Data.File(unpacked.dataBlocks.map(block => { const cats = Object.create(null); - for (const cat of block.categories) cats[cat.name] = Category(cat); - return Data.Block(cats, block.header); + for (const cat of block.categories) cats[cat.name.substr(1)] = Category(cat); + return Data.Block(block.categories.map(c => c.name.substr(1)), cats, block.header); })); return Result.success(file); } catch (e) { diff --git a/src/mol-io/reader/cif/data-model.ts b/src/mol-io/reader/cif/data-model.ts new file mode 100644 index 0000000000000000000000000000000000000000..417692342e7eb7fb1235128197a56629583c4864 --- /dev/null +++ b/src/mol-io/reader/cif/data-model.ts @@ -0,0 +1,108 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { Column } from 'mol-data/db' +import { Tensor } from 'mol-math/linear-algebra' + +export interface File { + readonly name?: string, + readonly blocks: ReadonlyArray<Block> +} + +export function File(blocks: ArrayLike<Block>, name?: string): File { + return { name, blocks: blocks as any }; +} + +export interface Frame { + readonly header: string, + // Category names stored separately so that the ordering can be preserved. + readonly categoryNames: ReadonlyArray<string>, + readonly categories: Categories +} + +export interface Block extends Frame { + readonly saveFrames: Frame[] +} + +export function Block(categoryNames: string[], categories: Categories, header: string, saveFrames: Frame[] = []): Block { + return { categoryNames, header, categories, saveFrames }; +} + +export function SafeFrame(categoryNames: string[], categories: Categories, header: string): Frame { + return { categoryNames, header, categories }; +} + +export type Categories = { readonly [name: string]: Category } + +export interface Category { + readonly rowCount: number, + readonly name: string, + readonly fieldNames: ReadonlyArray<string>, + getField(name: string): Field | undefined +} + +export function Category(name: string, rowCount: number, fieldNames: string[], fields: { [name: string]: Field }): Category { + return { rowCount, name, fieldNames: [...fieldNames], getField(name) { return fields[name]; } }; +} + +export namespace Category { + export function empty(name: string): Category { + return { rowCount: 0, name, fieldNames: [], getField(name: string) { return void 0; } }; + }; +} + +/** + * Implementation note: + * Always implement without using "this." in any of the interface functions. + * This is to ensure that the functions can invoked without having to "bind" them. + */ +export interface Field { + readonly '@array': ArrayLike<any> | undefined + readonly isDefined: boolean, + readonly rowCount: number, + + str(row: number): string, + int(row: number): number, + float(row: number): number, + + valueKind(row: number): Column.ValueKind, + + areValuesEqual(rowA: number, rowB: number): boolean, + + toStringArray(params?: Column.ToArrayParams<string>): ReadonlyArray<string>, + toIntArray(params?: Column.ToArrayParams<number>): ReadonlyArray<number>, + toFloatArray(params?: Column.ToArrayParams<number>): ReadonlyArray<number> +} + +export function getTensor(category: Category, field: string, space: Tensor.Space, row: number): Tensor { + const ret = space.create(); + if (space.rank === 1) { + const rows = space.dimensions[0]; + for (let i = 0; i < rows; i++) { + const f = category.getField(`${field}[${i + 1}]`); + space.set(ret, i, !!f ? f.float(row) : 0.0); + } + } else if (space.rank === 2) { + const rows = space.dimensions[0], cols = space.dimensions[1]; + for (let i = 0; i < rows; i++) { + for (let j = 0; j < cols; j++) { + const f = category.getField(`${field}[${i + 1}][${j + 1}]`); + space.set(ret, i, j, !!f ? f.float(row) : 0.0); + } + } + } else if (space.rank === 3) { + const d0 = space.dimensions[0], d1 = space.dimensions[1], d2 = space.dimensions[2]; + for (let i = 0; i < d0; i++) { + for (let j = 0; j < d1; j++) { + for (let k = 0; k < d2; k++) { + const f = category.getField(`${field}[${i + 1}][${j + 1}][${k + 1}]`); + space.set(ret, i, j, k, !!f ? f.float(row) : 0.0); + } + } + } + } else throw new Error('Tensors with rank > 3 currently not supported.'); + return ret; +} \ No newline at end of file diff --git a/src/mol-io/reader/cif/schema.ts b/src/mol-io/reader/cif/schema.ts new file mode 100644 index 0000000000000000000000000000000000000000..1be7563d4c3343ec5ca4370daf372baf6e429693 --- /dev/null +++ b/src/mol-io/reader/cif/schema.ts @@ -0,0 +1,105 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { Database, Table, Column, ColumnHelpers } from 'mol-data/db' +import { Tensor } from 'mol-math/linear-algebra' +import * as Data from './data-model' + +export function toDatabase<Schema extends Database.Schema, Frame extends Database<Schema> = Database<Schema>>(schema: Schema, frame: Data.Frame): Frame { + return createDatabase(schema, frame) as Frame; +} + +export function toTable<Schema extends Table.Schema, R extends Table<Schema> = Table<Schema>>(schema: Schema, category: Data.Category): R { + return new CategoryTable(category, schema, true) as any; +} + +type ColumnCtor = (field: Data.Field, category: Data.Category, key: string) => Column<any> + +function getColumnCtor(t: Column.Schema): ColumnCtor { + switch (t.valueType) { + case 'str': return (f, c, k) => createColumn(t, f, f.str, f.toStringArray); + case 'int': return (f, c, k) => createColumn(t, f, f.int, f.toIntArray); + case 'float': return (f, c, k) => createColumn(t, f, f.float, f.toFloatArray); + case 'tensor': throw new Error(`Use createTensorColumn instead.`); + } +} + +function createColumn<T>(schema: Column.Schema, field: Data.Field, value: (row: number) => T, toArray: Column<T>['toArray']): Column<T> { + return { + schema, + '@array': field['@array'], + isDefined: field.isDefined, + rowCount: field.rowCount, + value, + valueKind: field.valueKind, + areValuesEqual: field.areValuesEqual, + toArray + }; +} + +function createTensorColumn(schema: Column.Schema.Tensor, category: Data.Category, key: string): Column<Tensor> { + const space = schema.space; + const first = category.getField(`${key}[1]`) || Column.Undefined(category.rowCount, schema); + const value = (row: number) => Data.getTensor(category, key, space, row); + const toArray: Column<Tensor>['toArray'] = params => ColumnHelpers.createAndFillArray(category.rowCount, value, params) + + return { + schema, + '@array': void 0, + isDefined: first.isDefined, + rowCount: category.rowCount, + value, + valueKind: first.valueKind, + areValuesEqual: (rowA, rowB) => Tensor.areEqualExact(value(rowA), value(rowB)), + toArray + }; +} + +class CategoryTable implements Table<any> { // tslint:disable-line:class-name + _rowCount: number; + _columns: ReadonlyArray<string>; + _schema: any; + [k: string]: any; + + constructor(category: Data.Category, schema: Table.Schema, public _isDefined: boolean) { + const fieldKeys = Object.keys(schema); + this._rowCount = category.rowCount; + this._columns = fieldKeys; + this._schema = schema; + const cache = Object.create(null); + for (const k of fieldKeys) { + Object.defineProperty(this, k, { + get: function() { + if (cache[k]) return cache[k]; + const fType = schema[k]; + if (fType.valueType === 'tensor') { + cache[k] = createTensorColumn(fType, category, k); + } else { + const ctor = getColumnCtor(fType); + const field = category.getField(k); + cache[k] = !!field ? ctor(field, category, k) : Column.Undefined(category.rowCount, fType); + } + return cache[k]; + }, + enumerable: true, + configurable: false + }); + } + } +} + +function createDatabase(schema: Database.Schema, frame: Data.Frame): Database<any> { + const tables = Object.create(null); + for (const k of Object.keys(schema)) { + tables[k] = createTable(k, (schema as any)[k], frame); + } + return Database.ofTables(frame.header, schema, tables); +} + +function createTable(key: string, schema: Table.Schema, frame: Data.Frame) { + const cat = frame.categories[key]; + return new CategoryTable(cat || Data.Category.empty(key), schema, !!cat); +} \ No newline at end of file diff --git a/src/reader/cif/schema/ccd.ts b/src/mol-io/reader/cif/schema/ccd.ts similarity index 100% rename from src/reader/cif/schema/ccd.ts rename to src/mol-io/reader/cif/schema/ccd.ts diff --git a/src/reader/cif/schema/ddl.ts b/src/mol-io/reader/cif/schema/ddl.ts similarity index 100% rename from src/reader/cif/schema/ddl.ts rename to src/mol-io/reader/cif/schema/ddl.ts diff --git a/src/reader/cif/schema/density.ts b/src/mol-io/reader/cif/schema/density.ts similarity index 100% rename from src/reader/cif/schema/density.ts rename to src/mol-io/reader/cif/schema/density.ts diff --git a/src/reader/cif/schema/dic.ts b/src/mol-io/reader/cif/schema/dic.ts similarity index 68% rename from src/reader/cif/schema/dic.ts rename to src/mol-io/reader/cif/schema/dic.ts index 77e4dac7e9c74c0e26ce0cbe242dae88a538c11f..07db825731e9860df7fa088af4d643128745abb8 100644 --- a/src/reader/cif/schema/dic.ts +++ b/src/mol-io/reader/cif/schema/dic.ts @@ -1,13 +1,15 @@ /** - * Copyright (c) 2017 molio contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import { Field, TypedFrame } from '../schema' +import { Database, Column } from 'mol-data/db' -const str = Field.str() -const float = Field.float() +import Schema = Column.Schema + +const str = Schema.str; +const float = Schema.float; const datablock = { id: str, @@ -58,7 +60,7 @@ const item_units_conversion = { // TODO save frame dic schema -const dic = { +export const CIFDictionary_Schema = { datablock, dictionary, dictionary_history, @@ -69,5 +71,5 @@ const dic = { item_units_conversion } -type dic = TypedFrame<typeof dic> -export default dic +export type CIFDictionary_Schema = typeof CIFDictionary_Schema; +export interface CIFDictionary_Database extends Database.Tables<CIFDictionary_Schema> { } \ No newline at end of file diff --git a/src/mol-io/reader/cif/schema/mmcif.ts b/src/mol-io/reader/cif/schema/mmcif.ts new file mode 100644 index 0000000000000000000000000000000000000000..62f3a395ce2a22d971779dd3785b2aa15ba81727 --- /dev/null +++ b/src/mol-io/reader/cif/schema/mmcif.ts @@ -0,0 +1,251 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { Database, Column } from 'mol-data/db' + +import Schema = Column.Schema + +const str = Schema.str; +const int = Schema.int; +const float = Schema.float; + +const entry = { + id: str +} + +type EntityType = 'polymer' | 'non-polymer' | 'water' | 'macrolide' + +const entity = { + id: str, + type: Schema.Aliased<EntityType>(str), + src_method: str, + pdbx_description: str, + formula_weight: float, + pdbx_number_of_molecules: int, + details: str, + pdbx_mutation: str, + pdbx_fragment: str, + pdbx_ec: str +} + +const exptl = { + entry_id: str, + method: str +} + +const cell = { + entry_id: str, + length_a: float, + length_b: float, + length_c: float, + angle_alpha: float, + angle_beta: float, + angle_gamma: float, + Z_PDB: int, + pdbx_unique_axis: str +} + +const symmetry = { + entry_id: str, + 'space_group_name_H-M': str, + 'pdbx_full_space_group_name_H': str, + cell_setting: str, + Int_Tables_number: int, + space_group_name_Hall: str +} + +const struct_conf = { + conf_type_id: str, + id: str, + pdbx_PDB_helix_id: int, + beg_label_comp_id: str, + beg_label_asym_id: str, + beg_label_seq_id: int, + pdbx_beg_PDB_ins_code: str, + end_label_comp_id: str, + end_label_asym_id: str, + end_label_seq_id: int, + pdbx_end_PDB_ins_code: str, + beg_auth_comp_id: str, + beg_auth_asym_id: str, + beg_auth_seq_id: int, + end_auth_comp_id: str, + end_auth_asym_id: str, + end_auth_seq_id: int, + pdbx_PDB_helix_class: int, + details: str, + pdbx_PDB_helix_length: int +} + +const struct_sheet_range = { + sheet_id: str, + id: int, + beg_label_comp_id: str, + beg_label_asym_id: str, + beg_label_seq_id: int, + pdbx_beg_PDB_ins_code: str, + end_label_comp_id: str, + end_label_asym_id: str, + end_label_seq_id: int, + pdbx_end_PDB_ins_code: str, + beg_auth_comp_id: str, + beg_auth_asym_id: str, + beg_auth_seq_id: int, + end_auth_comp_id: str, + end_auth_asym_id: str, + end_auth_seq_id: int +} + +type StructConnTypeId = + | 'covale' + | 'covale_base' + | 'covale_phosphate' + | 'covale_sugar' + | 'disulf' + | 'hydrog' + | 'metalc' + | 'mismat' + | 'modres' + | 'saltbr' + +type BondValueOrder = + | 'SING' + | 'DOUB' + | 'TRIP' + | 'QUAD' + +const struct_conn = { + id: str, + conn_type_id: Schema.Aliased<StructConnTypeId>(str), + pdbx_PDB_id: str, + ptnr1_label_asym_id: str, + ptnr1_label_comp_id: str, + ptnr1_label_seq_id: int, + ptnr1_label_atom_id: str, + pdbx_ptnr1_label_alt_id: str, + pdbx_ptnr1_PDB_ins_code: str, + pdbx_ptnr1_standard_comp_id: str, + ptnr1_symmetry: str, + ptnr2_label_asym_id: str, + ptnr2_label_comp_id: str, + ptnr2_label_seq_id: int, + ptnr2_label_atom_id: str, + pdbx_ptnr2_label_alt_id: str, + pdbx_ptnr2_PDB_ins_code: str, + ptnr1_auth_asym_id: str, + ptnr1_auth_comp_id: str, + ptnr1_auth_seq_id: int, + ptnr2_auth_asym_id: str, + ptnr2_auth_comp_id: str, + ptnr2_auth_seq_id: int, + ptnr2_symmetry: str, + pdbx_ptnr3_label_atom_id: str, + pdbx_ptnr3_label_seq_id: int, + pdbx_ptnr3_label_comp_id: str, + pdbx_ptnr3_label_asym_id: str, + pdbx_ptnr3_label_alt_id: str, + pdbx_ptnr3_PDB_ins_code: str, + details: str, + pdbx_dist_value: float, + pdbx_value_order: Schema.Aliased<BondValueOrder>(str) +} + +const struct_conn_type = { + id: Schema.Aliased<StructConnTypeId>(str), + criteria: str, + reference: str +} + +const chem_comp_bond = { + comp_id: str, + pdbx_stereo_config: str, + pdbx_ordinal: int, + pdbx_aromatic_flag: Schema.Aliased<'Y' | 'N'>(str), + atom_id_1: str, + atom_id_2: str, + value_order: Schema.Aliased<BondValueOrder>(str) +} + +const pdbx_struct_assembly = { + id: str, + details: str, + method_details: str, + oligomeric_details: str, + oligomeric_count: int +} + +const pdbx_struct_assembly_gen = { + assembly_id: str, + oper_expression: str, + asym_id_list: str +} + +const pdbx_struct_oper_list = { + id: str, + type: str, + name: str, + symmetry_operation: str, + matrix: Schema.Matrix(3, 3), + vector: Schema.Vector(3) +} + +const pdbx_struct_mod_residue = { + id: int, + label_asym_id: str, + label_seq_id: int, + label_comp_id: str, + auth_asym_id: str, + auth_seq_id: int, + auth_comp_id: str, + PDB_ins_code: str, + parent_comp_id: str, + details: str +} + +const atom_site = { + group_PDB: str, + id: int, + type_symbol: str, + label_atom_id: str, + label_alt_id: str, + label_comp_id: str, + label_asym_id: str, + label_entity_id: str, + label_seq_id: int, + pdbx_PDB_ins_code: str, + pdbx_formal_charge: str, + Cartn_x: Schema.coord, + Cartn_y: Schema.coord, + Cartn_z: Schema.coord, + occupancy: float, + B_iso_or_equiv: float, + auth_atom_id: str, + auth_comp_id: str, + auth_asym_id: str, + auth_seq_id: int, + pdbx_PDB_model_num: int +} + +export const mmCIF_Schema = { + entry, + entity, + exptl, + cell, + symmetry, + struct_conf, + struct_sheet_range, + struct_conn, + struct_conn_type, + chem_comp_bond, + pdbx_struct_assembly, + pdbx_struct_assembly_gen, + pdbx_struct_oper_list, + pdbx_struct_mod_residue, + atom_site +}; + +export type mmCIF_Schema = typeof mmCIF_Schema; +export interface mmCIF_Database extends Database<mmCIF_Schema> { } \ No newline at end of file diff --git a/src/reader/cif/schema/utils.ts b/src/mol-io/reader/cif/schema/utils.ts similarity index 94% rename from src/reader/cif/schema/utils.ts rename to src/mol-io/reader/cif/schema/utils.ts index 072257addba3f27eae13deb2b03b4042de50684b..41078ab078a80bc8bcb551390fe45947fa2c64ba 100644 --- a/src/reader/cif/schema/utils.ts +++ b/src/mol-io/reader/cif/schema/utils.ts @@ -7,7 +7,7 @@ export function getFieldType (type: string, values?: string[]) { case 'code': case 'ucode': if (values && values.length) { - return `str as Field.Schema<'${values.join("'|'")}'>` + return `str as Field.Schema<'${values.join(`'|'`)}'>` } else { return 'str' } @@ -87,7 +87,7 @@ function getField ( category: string, field: string, d: Data.Frame, ctx: FrameDa } function getEnums (d: Data.Frame, ctx: FrameData): string[]|undefined { - const value = getField('_item_enumeration', 'value', d, ctx) + const value = getField('item_enumeration', 'value', d, ctx) if (value) { const enums: string[] = [] for (let i = 0; i < value.rowCount; ++i) { @@ -101,7 +101,7 @@ function getEnums (d: Data.Frame, ctx: FrameData): string[]|undefined { } function getCode (d: Data.Frame, ctx: FrameData): [string, string[]]|undefined { - const code = getField('_item_type', 'code', d, ctx) + const code = getField('item_type', 'code', d, ctx) if (code) { let c = code.str(0) let e = [] @@ -116,7 +116,7 @@ function getCode (d: Data.Frame, ctx: FrameData): [string, string[]]|undefined { } const header = `/** - * Copyright (c) 2017 molio contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Your friendly code generator */ @@ -147,7 +147,7 @@ export function generateSchema (dic: Data.Block) { // todo Block needs to be sp dic.saveFrames.forEach(d => { if (d.header[0] !== '_') return categories[d.header] = d - const item_linked = d.categories['_item_linked'] + const item_linked = d.categories['item_linked'] if (item_linked) { const child_name = item_linked.getField('child_name') const parent_name = item_linked.getField('parent_name') diff --git a/src/mol-io/reader/cif/text/field.ts b/src/mol-io/reader/cif/text/field.ts new file mode 100644 index 0000000000000000000000000000000000000000..6d18884942e3186b817b48aa988e3ca78538c886 --- /dev/null +++ b/src/mol-io/reader/cif/text/field.ts @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { Column, ColumnHelpers } from 'mol-data/db' +import * as TokenColumn from '../../common/text/column/token' +import { Tokens } from '../../common/text/tokenizer' +import * as Data from '../data-model' +import { parseInt as fastParseInt, parseFloat as fastParseFloat } from '../../common/text/number-parser' + +export default function CifTextField(tokens: Tokens, rowCount: number): Data.Field { + const { data, indices } = tokens; + + const str: Data.Field['str'] = row => { + const ret = data.substring(indices[2 * row], indices[2 * row + 1]); + if (ret === '.' || ret === '?') return ''; + return ret; + }; + + const int: Data.Field['int'] = row => { + return fastParseInt(data, indices[2 * row], indices[2 * row + 1]) || 0; + }; + + const float: Data.Field['float'] = row => { + return fastParseFloat(data, indices[2 * row], indices[2 * row + 1]) || 0; + }; + + const valueKind: Data.Field['valueKind'] = row => { + const s = indices[2 * row]; + if (indices[2 * row + 1] - s !== 1) return Column.ValueKind.Present; + const v = data.charCodeAt(s); + if (v === 46 /* . */) return Column.ValueKind.NotPresent; + if (v === 63 /* ? */) return Column.ValueKind.Unknown; + return Column.ValueKind.Present; + }; + + return { + '@array': void 0, + isDefined: true, + rowCount, + str, + int, + float, + valueKind, + areValuesEqual: TokenColumn.areValuesEqualProvider(tokens), + toStringArray: params => ColumnHelpers.createAndFillArray(rowCount, str, params), + toIntArray: params => ColumnHelpers.createAndFillArray(rowCount, int, params), + toFloatArray: params => ColumnHelpers.createAndFillArray(rowCount, float, params) + } +} \ No newline at end of file diff --git a/src/reader/cif/text/parser.ts b/src/mol-io/reader/cif/text/parser.ts similarity index 88% rename from src/reader/cif/text/parser.ts rename to src/mol-io/reader/cif/text/parser.ts index 4b86e99a1ab1bda07751913005ac2bcc26a3dafa..064b014a9194aaa356d06a666c4ee31f80bee723 100644 --- a/src/reader/cif/text/parser.ts +++ b/src/mol-io/reader/cif/text/parser.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2017 molio contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author David Sehnal <david.sehnal@gmail.com> */ @@ -26,7 +26,7 @@ import * as Data from '../data-model' import Field from './field' import { Tokens, TokenBuilder } from '../../common/text/tokenizer' import Result from '../../result' -import Computation from '../../../utils/computation' +import Computation from 'mol-util/computation' /** * Types of supported mmCIF tokens. @@ -411,13 +411,23 @@ interface CifCategoryResult { errorMessage: string; } +type FrameContext = { + categoryNames: string[], + categories: { [name: string]: Data.Category } +} + +function FrameContext(): FrameContext { + return { categoryNames: [], categories: Object.create(null) }; +} + /** * Reads a category containing a single row. */ -function handleSingle(tokenizer: TokenizerState, categories: { [name: string]: Data.Category }): CifCategoryResult { +function handleSingle(tokenizer: TokenizerState, ctx: FrameContext): CifCategoryResult { const nsStart = tokenizer.tokenStart, nsEnd = getNamespaceEnd(tokenizer); const name = getNamespace(tokenizer, nsEnd); const fields = Object.create(null); + const fieldNames: string[] = []; let readingNames = true; while (readingNames) { @@ -436,10 +446,13 @@ function handleSingle(tokenizer: TokenizerState, categories: { [name: string]: D } } fields[fieldName] = Field({ data: tokenizer.data, indices: [tokenizer.tokenStart, tokenizer.tokenEnd], count: 1 }, 1); + fieldNames[fieldNames.length] = fieldName; moveNext(tokenizer); } - categories[name] = Data.Category(1, fields); + const catName = name.substr(1); + ctx.categories[catName] = Data.Category(catName, 1, fieldNames, fields); + ctx.categoryNames.push(catName); return { hasError: false, @@ -477,7 +490,7 @@ function readLoopChunks(state: LoopReadState) { /** * Reads a loop. */ -async function handleLoop(tokenizer: TokenizerState, categories: { [name: string]: Data.Category }): Promise<CifCategoryResult> { +async function handleLoop(tokenizer: TokenizerState, ctx: FrameContext): Promise<CifCategoryResult> { const loopLine = tokenizer.lineNumber; moveNext(tokenizer); @@ -489,7 +502,7 @@ async function handleLoop(tokenizer: TokenizerState, categories: { [name: string moveNext(tokenizer); } - const rowCountEstimate = name === '_atom_site' ? (tokenizer.data.length / 100) | 0 : 32; + const rowCountEstimate = name === 'atom_site' ? (tokenizer.data.length / 100) | 0 : 32; const tokens: Tokens[] = []; const fieldCount = fieldNames.length; for (let i = 0; i < fieldCount; i++) tokens[i] = TokenBuilder.create(tokenizer, rowCountEstimate); @@ -517,7 +530,9 @@ async function handleLoop(tokenizer: TokenizerState, categories: { [name: string fields[fieldNames[i]] = Field(tokens[i], rowCount); } - categories[name] = Data.Category(rowCount, fields); + const catName = name.substr(1); + ctx.categories[catName] = Data.Category(catName, rowCount, fieldNames, fields); + ctx.categoryNames.push(catName); return { hasError: false, @@ -549,14 +564,15 @@ async function parseInternal(data: string, ctx: Computation.Context) { const dataBlocks: Data.Block[] = []; const tokenizer = createTokenizer(data, ctx); let blockHeader: string = ''; - let blockCategories = Object.create(null); - let inSaveFrame = false + let blockCtx = FrameContext(); + + let inSaveFrame = false; // the next three initial values are never used in valid files let saveFrames: Data.Frame[] = []; - let saveCategories = Object.create(null); - let saveFrame: Data.Frame = Data.SafeFrame(saveCategories, ''); + let saveCtx = FrameContext(); + let saveFrame: Data.Frame = Data.SafeFrame(saveCtx.categoryNames, saveCtx.categories, ''); ctx.update({ message: 'Parsing...', current: 0, max: data.length }); @@ -567,41 +583,41 @@ async function parseInternal(data: string, ctx: Computation.Context) { // Data block if (token === CifTokenType.Data) { if (inSaveFrame) { - return error(tokenizer.lineNumber, "Unexpected data block inside a save frame."); + return error(tokenizer.lineNumber, 'Unexpected data block inside a save frame.'); } - if (Object.keys(blockCategories).length > 0) { - dataBlocks.push(Data.Block(blockCategories, blockHeader, saveFrames)); + if (blockCtx.categoryNames.length > 0) { + dataBlocks.push(Data.Block(blockCtx.categoryNames, blockCtx.categories, blockHeader, saveFrames)); } blockHeader = data.substring(tokenizer.tokenStart + 5, tokenizer.tokenEnd); - blockCategories = Object.create(null); + blockCtx = FrameContext(); saveFrames = [] moveNext(tokenizer); // Save frame } else if (token === CifTokenType.Save) { const saveHeader = data.substring(tokenizer.tokenStart + 5, tokenizer.tokenEnd); if (saveHeader.length === 0) { - if (Object.keys(saveCategories).length > 0) { - saveFrames[saveFrames.length] = saveFrame + if (saveCtx.categoryNames.length > 0) { + saveFrames[saveFrames.length] = saveFrame; } inSaveFrame = false; } else { if (inSaveFrame) { - return error(tokenizer.lineNumber, "Save frames cannot be nested."); + return error(tokenizer.lineNumber, 'Save frames cannot be nested.'); } inSaveFrame = true; - saveCategories = Object.create(null); - saveFrame = Data.SafeFrame(saveCategories, saveHeader); + saveCtx = FrameContext(); + saveFrame = Data.SafeFrame(saveCtx.categoryNames, saveCtx.categories, ''); } moveNext(tokenizer); // Loop } else if (token === CifTokenType.Loop) { - const cat = await handleLoop(tokenizer, inSaveFrame ? saveCategories : blockCategories); + const cat = await handleLoop(tokenizer, inSaveFrame ? saveCtx : blockCtx); if (cat.hasError) { return error(cat.errorLine, cat.errorMessage); } // Single row } else if (token === CifTokenType.ColumnName) { - const cat = handleSingle(tokenizer, inSaveFrame ? saveCategories : blockCategories); + const cat = handleSingle(tokenizer, inSaveFrame ? saveCtx : blockCtx); if (cat.hasError) { return error(cat.errorLine, cat.errorMessage); } @@ -613,11 +629,11 @@ async function parseInternal(data: string, ctx: Computation.Context) { // Check if the latest save frame was closed. if (inSaveFrame) { - return error(tokenizer.lineNumber, "Unfinished save frame (`" + saveFrame.header + "`)."); + return error(tokenizer.lineNumber, 'Unfinished save frame (`' + saveFrame.header + '`).'); } - if (Object.keys(blockCategories).length > 0) { - dataBlocks.push(Data.Block(blockCategories, blockHeader, saveFrames)); + if (blockCtx.categoryNames.length > 0) { + dataBlocks.push(Data.Block(blockCtx.categoryNames, blockCtx.categories, blockHeader, saveFrames)); } return result(Data.File(dataBlocks)); diff --git a/src/mol-io/reader/common/binary/column.ts b/src/mol-io/reader/common/binary/column.ts new file mode 100644 index 0000000000000000000000000000000000000000..0a8bc85d73c596f6305bdebe8c2561282fec9b1f --- /dev/null +++ b/src/mol-io/reader/common/binary/column.ts @@ -0,0 +1,5 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ \ No newline at end of file diff --git a/src/reader/common/text/column/fixed.ts b/src/mol-io/reader/common/text/column/fixed.ts similarity index 50% rename from src/reader/common/text/column/fixed.ts rename to src/mol-io/reader/common/text/column/fixed.ts index fb7534e85c8736929bbd740e8fb7a0fb76475f8e..7316f5234e4089cae76241c6ff68937676556257 100644 --- a/src/reader/common/text/column/fixed.ts +++ b/src/mol-io/reader/common/text/column/fixed.ts @@ -1,38 +1,30 @@ /** - * Copyright (c) 2017 molio contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author David Sehnal <david.sehnal@gmail.com> */ -import { Column, ColumnType, createAndFillArray } from '../../column' +import { Column, ColumnHelpers } from 'mol-data/db' import { trimStr, Tokens } from '../tokenizer' import { parseIntSkipLeadingWhitespace, parseFloatSkipLeadingWhitespace } from '../number-parser' -import StringPool from '../../../../utils/short-string-pool' export default function FixedColumnProvider(lines: Tokens) { - return function<T extends ColumnType>(offset: number, width: number, type: T) { + return function<T extends Column.Schema>(offset: number, width: number, type: T) { return FixedColumn(lines, offset, width, type); } } -export function FixedColumn<T extends ColumnType>(lines: Tokens, offset: number, width: number, type: T): Column<T['@type']> { +export function FixedColumn<T extends Column.Schema>(lines: Tokens, offset: number, width: number, schema: T): Column<T['T']> { const { data, indices, count: rowCount } = lines; - const { kind } = type; - const pool = kind === 'pooled-str' ? StringPool.create() : void 0; + const { valueType: type } = schema; - const value: Column<T['@type']>['value'] = kind === 'str' ? row => { + const value: Column<T['T']>['value'] = type === 'str' ? row => { let s = indices[2 * row] + offset, le = indices[2 * row + 1]; if (s >= le) return ''; let e = s + width; if (e > le) e = le; return trimStr(data, s, e); - } : kind === 'pooled-str' ? row => { - let s = indices[2 * row] + offset, le = indices[2 * row + 1]; - if (s >= le) return ''; - let e = s + width; - if (e > le) e = le; - return StringPool.get(pool!, trimStr(data, s, e)); - } : kind === 'int' ? row => { + } : type === 'int' ? row => { const s = indices[2 * row] + offset; if (s > indices[2 * row + 1]) return 0; return parseIntSkipLeadingWhitespace(data, s, s + width); @@ -42,12 +34,13 @@ export function FixedColumn<T extends ColumnType>(lines: Tokens, offset: number, return parseFloatSkipLeadingWhitespace(data, s, s + width); }; return { + schema: schema, + '@array': void 0, isDefined: true, rowCount, value, - isValueDefined: row => true, - toArray: params => createAndFillArray(rowCount, value, params), - stringEquals: (row, v) => value(row) === v, + valueKind: row => Column.ValueKind.Present, + toArray: params => ColumnHelpers.createAndFillArray(rowCount, value, params), areValuesEqual: (rowA, rowB) => value(rowA) === value(rowB) }; } \ No newline at end of file diff --git a/src/reader/common/text/column/token.ts b/src/mol-io/reader/common/text/column/token.ts similarity index 51% rename from src/reader/common/text/column/token.ts rename to src/mol-io/reader/common/text/column/token.ts index adfc613d074c1e030fa212ed0fee0c4b61935c74..fb828354d88e7092c5b6a345cf1bf7979a770e97 100644 --- a/src/reader/common/text/column/token.ts +++ b/src/mol-io/reader/common/text/column/token.ts @@ -1,50 +1,38 @@ /** - * Copyright (c) 2017 molio contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author David Sehnal <david.sehnal@gmail.com> */ -import { Column, ColumnType, createAndFillArray } from '../../column' +import { Column, ColumnHelpers } from 'mol-data/db' import { Tokens } from '../tokenizer' import { parseInt as fastParseInt, parseFloat as fastParseFloat } from '../number-parser' -import StringPool from '../../../../utils/short-string-pool' export default function TokenColumnProvider(tokens: Tokens) { - return function<T extends ColumnType>(type: T) { + return function<T extends Column.Schema>(type: T) { return TokenColumn(tokens, type); } } -export function TokenColumn<T extends ColumnType>(tokens: Tokens, type: T): Column<T['@type']> { +export function TokenColumn<T extends Column.Schema>(tokens: Tokens, schema: T): Column<T['T']> { const { data, indices, count: rowCount } = tokens; - const { kind } = type; - const pool = kind === 'pooled-str' ? StringPool.create() : void 0; + const { valueType: type } = schema; - const value: Column<T['@type']>['value'] = - kind === 'str' + const value: Column<T['T']>['value'] = + type === 'str' ? row => data.substring(indices[2 * row], indices[2 * row + 1]) - : kind === 'pooled-str' - ? row => StringPool.get(pool!, data.substring(indices[2 * row], indices[2 * row + 1])) - : kind === 'int' + : type === 'int' ? row => fastParseInt(data, indices[2 * row], indices[2 * row + 1]) || 0 : row => fastParseFloat(data, indices[2 * row], indices[2 * row + 1]) || 0; return { + schema: schema, + '@array': void 0, isDefined: true, rowCount, value, - isValueDefined: row => true, - toArray: params => createAndFillArray(rowCount, value, params), - stringEquals: (row, v) => { - const s = indices[2 * row]; - const value = v || ''; - const len = value.length; - if (len !== indices[2 * row + 1] - s) return false; - for (let i = 0; i < len; i++) { - if (data.charCodeAt(i + s) !== value.charCodeAt(i)) return false; - } - return true; - }, + valueKind: row => Column.ValueKind.Present, + toArray: params => ColumnHelpers.createAndFillArray(rowCount, value, params), areValuesEqual: areValuesEqualProvider(tokens) }; } diff --git a/src/reader/common/text/number-parser.ts b/src/mol-io/reader/common/text/number-parser.ts similarity index 96% rename from src/reader/common/text/number-parser.ts rename to src/mol-io/reader/common/text/number-parser.ts index f7b097f8241b7b70cb98f8eec9d05484626b18bd..664b0f04c6bb0dcfee34faf5e0fc589ad84cfd5f 100644 --- a/src/reader/common/text/number-parser.ts +++ b/src/mol-io/reader/common/text/number-parser.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2017 molio contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. * * from https://github.com/dsehnal/CIFTools.js * @author David Sehnal <david.sehnal@gmail.com> diff --git a/src/reader/common/text/tokenizer.ts b/src/mol-io/reader/common/text/tokenizer.ts similarity index 98% rename from src/reader/common/text/tokenizer.ts rename to src/mol-io/reader/common/text/tokenizer.ts index 17991fbdbb11828b824274ae84ce302a65f9cdbd..55b310e5d825008fa16a7b91dd443e08d3e8d768 100644 --- a/src/reader/common/text/tokenizer.ts +++ b/src/mol-io/reader/common/text/tokenizer.ts @@ -1,12 +1,12 @@ /** - * Copyright (c) 2017 molio contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. * * mostly from https://github.com/dsehnal/CIFTools.js * @author David Sehnal <david.sehnal@gmail.com> * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import Computation from '../../../utils/computation' +import Computation from 'mol-util/computation' export interface Tokenizer { data: string, diff --git a/src/reader/gro/parser.ts b/src/mol-io/reader/gro/parser.ts similarity index 85% rename from src/reader/gro/parser.ts rename to src/mol-io/reader/gro/parser.ts index 7ec1c994b4f4e1f9a906c15bb722117a0a4f2095..d1c71ec315c3f98d47f45692838c9dfa462d4142 100644 --- a/src/reader/gro/parser.ts +++ b/src/mol-io/reader/gro/parser.ts @@ -1,16 +1,16 @@ /** - * Copyright (c) 2017 molio contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Alexander Rose <alexander.rose@weirdbyte.de> * @author David Sehnal <david.sehnal@gmail.com> */ +import { Column } from 'mol-data/db' import Tokenizer from '../common/text/tokenizer' import FixedColumn from '../common/text/column/fixed' -import { ColumnType, UndefinedColumn } from '../common/column' import * as Schema from './schema' import Result from '../result' -import Computation from '../../utils/computation' +import Computation from 'mol-util/computation' interface State { tokenizer: Tokenizer, @@ -106,20 +106,20 @@ async function handleAtoms(state: State): Promise<Schema.Atoms> { const vW = state.header.precision.velocity + 4; const col = FixedColumn(lines); - const undef = UndefinedColumn(state.numberOfAtoms, ColumnType.float); + const undef = Column.Undefined(state.numberOfAtoms, Column.Schema.float); const ret = { count: state.numberOfAtoms, - residueNumber: col(0, 5, ColumnType.int), - residueName: col(5, 5, ColumnType.pooledStr), - atomName: col(10, 5, ColumnType.pooledStr), - atomNumber: col(15, 5, ColumnType.int), - x: col(pO, pW, ColumnType.float), - y: col(pO + pW, pW, ColumnType.float), - z: col(pO + 2 * pW, pW, ColumnType.float), - vx: hasVelocities ? col(vO, vW, ColumnType.float) : undef, - vy: hasVelocities ? col(vO + vW, vW, ColumnType.float) : undef, - vz: hasVelocities ? col(vO + 2 * vW, vW, ColumnType.float) : undef, + residueNumber: col(0, 5, Column.Schema.int), + residueName: col(5, 5, Column.Schema.str), + atomName: col(10, 5, Column.Schema.str), + atomNumber: col(15, 5, Column.Schema.int), + x: col(pO, pW, Column.Schema.float), + y: col(pO + pW, pW, Column.Schema.float), + z: col(pO + 2 * pW, pW, Column.Schema.float), + vx: hasVelocities ? col(vO, vW, Column.Schema.float) : undef, + vy: hasVelocities ? col(vO + vW, vW, Column.Schema.float) : undef, + vz: hasVelocities ? col(vO + 2 * vW, vW, Column.Schema.float) : undef, }; return ret; diff --git a/src/reader/gro/schema.d.ts b/src/mol-io/reader/gro/schema.d.ts similarity index 85% rename from src/reader/gro/schema.d.ts rename to src/mol-io/reader/gro/schema.d.ts index 2ee9bc0edf12b9cddb0c883c53d15e4046a2d546..9a0b1ca550718e16822320091b9500b5cef0597c 100644 --- a/src/reader/gro/schema.d.ts +++ b/src/mol-io/reader/gro/schema.d.ts @@ -1,11 +1,11 @@ /** - * Copyright (c) 2017 molio contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Alexander Rose <alexander.rose@weirdbyte.de> * @author David Sehnal <david.sehnal@gmail.com> */ -import { Column } from '../common/column' +import { Column } from 'mol-data/db' export interface Header { title: string, diff --git a/src/reader/mol2/schema.d.ts b/src/mol-io/reader/mol2/schema.d.ts similarity index 91% rename from src/reader/mol2/schema.d.ts rename to src/mol-io/reader/mol2/schema.d.ts index 7f1e73fb2077b0b647b5d13d62662e104bad6b0b..374b52d0d8063e5b0995336fb43590b0c5beae14 100644 --- a/src/reader/mol2/schema.d.ts +++ b/src/mol-io/reader/mol2/schema.d.ts @@ -1,10 +1,10 @@ /** - * Copyright (c) 2017 molio contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import { Column } from '../common/column' +import { Column } from 'mol-data/db' // Full format http://chemyang.ccnu.edu.cn/ccb/server/AIMMS/mol2.pdf // there are many records but for now ignore (pass over) all but the following diff --git a/src/reader/result.ts b/src/mol-io/reader/result.ts similarity index 90% rename from src/reader/result.ts rename to src/mol-io/reader/result.ts index a6b29f0200e2e1d84ceced57977b916311025cd9..4eb76dd373929b858e09e2de3d7f649abd078f86 100644 --- a/src/reader/result.ts +++ b/src/mol-io/reader/result.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 molio contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. * * from https://github.com/dsehnal/CIFTools.js * @author David Sehnal <david.sehnal@gmail.com> diff --git a/src/utils/short-string-pool.ts b/src/mol-io/utils/short-string-pool.ts similarity index 87% rename from src/utils/short-string-pool.ts rename to src/mol-io/utils/short-string-pool.ts index 870284bcca01277b4620dc6ce72aa0ae18fc013a..58858c1a876da86df9f6e76fad6f82aa51d8d5e3 100644 --- a/src/utils/short-string-pool.ts +++ b/src/mol-io/utils/short-string-pool.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2017 molio contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. * * from https://github.com/dsehnal/CIFTools.js * @author David Sehnal <david.sehnal@gmail.com> diff --git a/src/mol-io/writer/cif.ts b/src/mol-io/writer/cif.ts new file mode 100644 index 0000000000000000000000000000000000000000..4a6df8380a6cf9f80d216e064a2d30451879ad03 --- /dev/null +++ b/src/mol-io/writer/cif.ts @@ -0,0 +1,15 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import TextCIFEncoder from './cif/encoder/text' +import BinaryCIFEncoder from './cif/encoder/binary' + +export * from './cif/encoder' + +export function create(params?: { binary?: boolean, encoderName?: string }) { + const { binary = false, encoderName = 'mol*' } = params || {}; + return binary ? new BinaryCIFEncoder(encoderName) : new TextCIFEncoder(); +} \ No newline at end of file diff --git a/src/mol-io/writer/cif/encoder.ts b/src/mol-io/writer/cif/encoder.ts new file mode 100644 index 0000000000000000000000000000000000000000..e37380d02f0e6c7daea7eefdb8ebd1046f57c7ff --- /dev/null +++ b/src/mol-io/writer/cif/encoder.ts @@ -0,0 +1,73 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import Iterator from 'mol-data/iterator' +import { Column } from 'mol-data/db' +import Encoder from '../encoder' + +// TODO: support for "coordinate fields", make "coordinate precision" a parameter of the encoder +// TODO: automatically detect "precision" of floating point arrays. +// TODO: automatically detect "best encoding" for integer arrays. This could be used for "fixed-point" as well. +// TODO: add "repeat encoding"? [[1, 2], [1, 2], [1, 2]] --- Repeat ---> [[1, 2], 3] +// TODO: Add "higher level fields"? (i.e. generalization of repeat) +// TODO: Add tensor field definition +// TODO: align "data blocks" to 8 byte offsets + +export const enum FieldType { + Str, Int, Float +} + +export interface FieldDefinitionBase<Key, Data> { + name: string, + valueKind?: (key: Key, data: Data) => Column.ValueKind, + // TODO: + // shouldInclude?: (data: Data) => boolean +} + +export type FieldDefinition<Key = any, Data = any> = + | FieldDefinitionBase<Key, Data> & { type: FieldType.Str, value(key: Key, data: Data): string } + | FieldDefinitionBase<Key, Data> & { type: FieldType.Int, value(key: Key, data: Data): number } + | FieldDefinitionBase<Key, Data> & { type: FieldType.Float, value(key: Key, data: Data): number } + // TODO: add tensor + +export interface FieldFormat { + // TODO: do we actually need this? + // textDecimalPlaces: number, + // stringEncoder: ArrayEncoder, + // numericEncoder: ArrayEncoder, + // typedArray?: E.TypedArrayCtor +} + +export namespace FieldFormat { + export const Default: FieldFormat = { + // textDecimalPlaces: 3, + // stringEncoder: ArrayEncoder.by(E.stringArray), + // numericEncoder: ArrayEncoder.by(E.byteArray) + }; +} + +export interface CategoryDefinition<Key = any, Data = any> { + name: string, + fields: FieldDefinition<Key, Data>[] +} + +export interface CategoryInstance<Key = any, Data = any> { + data: Data, + definition: CategoryDefinition<Key, Data>, + formats?: { [name: string]: Partial<FieldFormat> }, + rowCount: number, + keys(): Iterator<Key> +} + +export interface CategoryProvider { + (ctx: any): CategoryInstance +} + +export interface CIFEncoder<T = string | Uint8Array, Context = any> extends Encoder<T> { + startDataBlock(header: string): void, + writeCategory(category: CategoryProvider, contexts?: Context[]): void, + getData(): T +} \ No newline at end of file diff --git a/src/mol-io/writer/cif/encoder/binary.ts b/src/mol-io/writer/cif/encoder/binary.ts new file mode 100644 index 0000000000000000000000000000000000000000..5817959635d31ea62a98aa18a11dec66a16b395d --- /dev/null +++ b/src/mol-io/writer/cif/encoder/binary.ts @@ -0,0 +1,135 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * Adapted from CIFTools.js (https://github.com/dsehnal/CIFTools.js) + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import Iterator from 'mol-data/iterator' +import { Column } from 'mol-data/db' +import encodeMsgPack from '../../../common/msgpack/encode' +import { + EncodedColumn, EncodedData, EncodedFile, EncodedDataBlock, EncodedCategory, ArrayEncoder, ArrayEncoding as E, VERSION +} from '../../../common/binary-cif' +import { FieldDefinition, FieldFormat, FieldType, CategoryProvider, CIFEncoder } from '../encoder' +import Writer from '../../writer' + +export default class BinaryCIFWriter<Context> implements CIFEncoder<Uint8Array, Context> { + private data: EncodedFile; + private dataBlocks: EncodedDataBlock[] = []; + private encodedData: Uint8Array; + + startDataBlock(header: string) { + this.dataBlocks.push({ + header: (header || '').replace(/[ \n\t]/g, '').toUpperCase(), + categories: [] + }); + } + + writeCategory(category: CategoryProvider, contexts?: Context[]) { + if (!this.data) { + throw new Error('The writer contents have already been encoded, no more writing.'); + } + + if (!this.dataBlocks.length) { + throw new Error('No data block created.'); + } + + const src = !contexts || !contexts.length ? [category(<any>void 0)] : contexts.map(c => category(c)); + const categories = src.filter(c => c && c.rowCount > 0); + if (!categories.length) return; + + const count = categories.reduce((a, c) => a + c.rowCount, 0); + if (!count) return; + + const first = categories[0]!; + const cat: EncodedCategory = { name: '_' + first.definition.name, columns: [], rowCount: count }; + const data = categories.map(c => ({ data: c.data, keys: () => c.keys() })); + for (const f of first.definition.fields) { + cat.columns.push(encodeField(f, data, count, FieldFormat.Default)); + } + this.dataBlocks[this.dataBlocks.length - 1].categories.push(cat); + } + + encode() { + if (this.encodedData) return; + this.encodedData = encodeMsgPack(this.data); + this.data = <any>null; + this.dataBlocks = <any>null; + } + + writeTo(writer: Writer<Uint8Array>) { + writer.write(this.encodedData); + } + + getData() { + this.encode(); + return this.encodedData; + } + + constructor(encoder: string) { + this.data = { + encoder, + version: VERSION, + dataBlocks: this.dataBlocks + }; + } +} + +function encodeField(field: FieldDefinition, data: { data: any, keys: () => Iterator<any> }[], totalCount: number, format: FieldFormat): EncodedColumn { + const isStr = field.type === FieldType.Str + let array: any[], encoder: ArrayEncoder; + + if (isStr) { + array = new Array(totalCount); + encoder = ArrayEncoder.by(E.stringArray); //format.stringEncoder; + } else { + //array = format.typedArray ? new format.typedArray(totalCount) as any : field.type === FieldType.Int ? new Int32Array(totalCount) : new Float32Array(totalCount); + array = (field.type === FieldType.Int ? new Int32Array(totalCount) : new Float32Array(totalCount)) as any; + encoder = ArrayEncoder.by(E.byteArray); + } + + const mask = new Uint8Array(totalCount); + const valueKind = field.valueKind; + const getter = field.value; + let allPresent = true; + + let offset = 0; + for (let _d = 0; _d < data.length; _d++) { + const d = data[_d].data; + const keys = data[_d].keys(); + while (keys.hasNext) { + const key = keys.move(); + const p = valueKind ? valueKind(key, d) : Column.ValueKind.Present; + if (p !== Column.ValueKind.Present) { + mask[offset] = p; + if (isStr) array[offset] = ''; + allPresent = false; + } else { + mask[offset] = Column.ValueKind.Present; + array[offset] = getter(key, d); + } + offset++; + } + } + + const encoded = encoder.encode(array); + + let maskData: EncodedData | undefined = void 0; + + if (!allPresent) { + const maskRLE = ArrayEncoder.by(E.runLength).and(E.byteArray).encode(mask); + if (maskRLE.data.length < mask.length) { + maskData = maskRLE; + } else { + maskData = ArrayEncoder.by(E.byteArray).encode(mask); + } + } + + return { + name: field.name, + data: encoded, + mask: maskData + }; +} \ No newline at end of file diff --git a/src/mol-io/writer/cif/encoder/text.ts b/src/mol-io/writer/cif/encoder/text.ts new file mode 100644 index 0000000000000000000000000000000000000000..ef51b642404edde680123778423f0c474b3e27c8 --- /dev/null +++ b/src/mol-io/writer/cif/encoder/text.ts @@ -0,0 +1,228 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * Adapted from CIFTools.js (https://github.com/dsehnal/CIFTools.js) + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { Column } from 'mol-data/db' +import StringBuilder from 'mol-util/string-builder' +import * as Enc from '../encoder' +import Writer from '../../writer' + +export default class TextCIFEncoder<Context> implements Enc.CIFEncoder<string, Context> { + private builder = StringBuilder.create(); + private encoded = false; + private dataBlockCreated = false; + + startDataBlock(header: string) { + this.dataBlockCreated = true; + StringBuilder.write(this.builder, `data_${(header || '').replace(/[ \n\t]/g, '').toUpperCase()}\n#\n`); + } + + writeCategory(category: Enc.CategoryProvider, contexts?: Context[]) { + if (this.encoded) { + throw new Error('The writer contents have already been encoded, no more writing.'); + } + + if (!this.dataBlockCreated) { + throw new Error('No data block created.'); + } + + const categories = !contexts || !contexts.length ? [category(<any>void 0)] : contexts.map(c => category(c)); + if (!categories.length) return; + + const rowCount = categories.reduce((v, c) => v + c.rowCount, 0); + + if (rowCount === 0) return; + + if (rowCount === 1) { + writeCifSingleRecord(categories[0]!, this.builder); + } else { + writeCifLoop(categories, this.builder); + } + } + + encode() { + this.encoded = true; + } + + writeTo(stream: Writer<string>) { + const chunks = StringBuilder.getChunks(this.builder); + for (let i = 0, _i = chunks.length; i < _i; i++) { + stream.write(chunks[i]); + } + } + + getData() { + return StringBuilder.getString(this.builder); + } +} + +function writeValue(builder: StringBuilder, data: any, key: any, f: Enc.FieldDefinition<any, any>): boolean { + const kind = f.valueKind; + const p = kind ? kind(key, data) : Column.ValueKind.Present; + if (p !== Column.ValueKind.Present) { + if (p === Column.ValueKind.NotPresent) writeNotPresent(builder); + else writeUnknown(builder); + } else { + const val = f.value(key, data); + const t = f.type; + if (t === Enc.FieldType.Str) { + if (isMultiline(val as string)) { + writeMultiline(builder, val as string); + return true; + } else { + return writeChecked(builder, val as string); + } + } else if (t === Enc.FieldType.Int) { + writeInteger(builder, val as number); + } else { + writeFloat(builder, val as number, 1000000); + } + } + return false; +} + +function writeCifSingleRecord(category: Enc.CategoryInstance<any>, builder: StringBuilder) { + const fields = category.definition.fields; + const data = category.data; + const width = fields.reduce((w, s) => Math.max(w, s.name.length), 0) + category.definition.name.length + 6; + + const it = category.keys(); + const key = it.move(); + + for (let _f = 0; _f < fields.length; _f++) { + const f = fields[_f]; + StringBuilder.writePadRight(builder, `_${category.definition.name}.${f.name}`, width); + const multiline = writeValue(builder, data, key, f); + if (!multiline) StringBuilder.newline(builder); + } + StringBuilder.write(builder, '#\n'); +} + +function writeCifLoop(categories: Enc.CategoryInstance[], builder: StringBuilder) { + const first = categories[0]; + const fields = first.definition.fields; + const fieldCount = fields.length; + + writeLine(builder, 'loop_'); + for (let i = 0; i < fieldCount; i++) { + writeLine(builder, `_${first.definition.name}.${fields[i].name}`); + } + + for (let _c = 0; _c < categories.length; _c++) { + const category = categories[_c]; + const data = category.data; + + if (category.rowCount === 0) continue; + + const it = category.keys(); + while (it.hasNext) { + const key = it.move(); + + let multiline = false; + for (let _f = 0; _f < fieldCount; _f++) { + multiline = writeValue(builder, data, key, fields[_f]); + } + if (!multiline) StringBuilder.newline(builder); + } + } + StringBuilder.write(builder, '#\n'); +} + +function isMultiline(value: string) { + return !!value && value.indexOf('\n') >= 0; +} + +function writeLine(builder: StringBuilder, val: string) { + StringBuilder.write(builder, val); + StringBuilder.newline(builder); +} + +function writeInteger(builder: StringBuilder, val: number) { + StringBuilder.writeInteger(builder, val); + StringBuilder.whitespace1(builder); +} + +function writeFloat(builder: StringBuilder, val: number, precisionMultiplier: number) { + StringBuilder.writeFloat(builder, val, precisionMultiplier); + StringBuilder.whitespace1(builder); +} + +function writeNotPresent(builder: StringBuilder) { + StringBuilder.writeSafe(builder, '. '); +} + +function writeUnknown(builder: StringBuilder) { + StringBuilder.writeSafe(builder, '? '); +} + +function writeChecked(builder: StringBuilder, val: string) { + if (!val) { + StringBuilder.writeSafe(builder, '. '); + return false; + } + + let escape = false, escapeCharStart = '\'', escapeCharEnd = '\' '; + let hasWhitespace = false; + let hasSingle = false; + let hasDouble = false; + for (let i = 0, _l = val.length - 1; i < _l; i++) { + const c = val.charCodeAt(i); + + switch (c) { + case 9: hasWhitespace = true; break; // \t + case 10: // \n + writeMultiline(builder, val); + return true; + case 32: hasWhitespace = true; break; // ' ' + case 34: // " + if (hasSingle) { + writeMultiline(builder, val); + return true; + } + + hasDouble = true; + escape = true; + escapeCharStart = '\''; + escapeCharEnd = '\' '; + break; + case 39: // ' + if (hasDouble) { + writeMultiline(builder, val); + return true; + } + + escape = true; + hasSingle = true; + escapeCharStart = '"'; + escapeCharEnd = '" '; + break; + } + } + + const fst = val.charCodeAt(0); + if (!escape && (fst === 35 /* # */ || fst === 59 /* ; */ || hasWhitespace)) { + escapeCharStart = '\''; + escapeCharEnd = '\' '; + escape = true; + } + + if (escape) { + StringBuilder.writeSafe(builder, escapeCharStart); + StringBuilder.writeSafe(builder, val); + StringBuilder.writeSafe(builder, escapeCharEnd); + } else { + StringBuilder.writeSafe(builder, val); + StringBuilder.writeSafe(builder, ' '); + } + + return false; +} + +function writeMultiline(builder: StringBuilder, val: string) { + StringBuilder.writeSafe(builder, '\n;' + val); + StringBuilder.writeSafe(builder, '\n;\n'); +} diff --git a/src/mol-io/writer/encoder.ts b/src/mol-io/writer/encoder.ts new file mode 100644 index 0000000000000000000000000000000000000000..3ea41229c787ea0dbb2358c1f6fabab7590ab09f --- /dev/null +++ b/src/mol-io/writer/encoder.ts @@ -0,0 +1,14 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import Writer from './writer' + +interface Encoder<T> { + encode(): void, + writeTo(writer: Writer<T>): void +} + +export default Encoder \ No newline at end of file diff --git a/src/mol-io/writer/writer.ts b/src/mol-io/writer/writer.ts new file mode 100644 index 0000000000000000000000000000000000000000..c1325d372d7226ab2db135e181f66f0cc9e46d6d --- /dev/null +++ b/src/mol-io/writer/writer.ts @@ -0,0 +1,15 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +interface Writer<T> { + write(data: T): boolean +} + +namespace Writer { + +} + +export default Writer \ No newline at end of file diff --git a/src/mol-math/geometry/grid-lookup.ts b/src/mol-math/geometry/grid-lookup.ts new file mode 100644 index 0000000000000000000000000000000000000000..f7723b745c8d2abec8f865fcd6f3f5db2d370353 --- /dev/null +++ b/src/mol-math/geometry/grid-lookup.ts @@ -0,0 +1,2 @@ +// TODO: 3d grid lookup (use single cell for small sets), make bounding sphere part of the structure +// TODO: assign radius to points? \ No newline at end of file diff --git a/src/mol-math/geometry/sphere.ts b/src/mol-math/geometry/sphere.ts new file mode 100644 index 0000000000000000000000000000000000000000..6135cbf4b6a96564aed1ffd3de8ef78dddd608b2 --- /dev/null +++ b/src/mol-math/geometry/sphere.ts @@ -0,0 +1 @@ +// TODO: rebranded vec4 \ No newline at end of file diff --git a/src/mol-math/geometry/symmetry-operator.ts b/src/mol-math/geometry/symmetry-operator.ts new file mode 100644 index 0000000000000000000000000000000000000000..1e3a57de38f5a782220061e838ca5ee2dafcdc28 --- /dev/null +++ b/src/mol-math/geometry/symmetry-operator.ts @@ -0,0 +1,141 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { Vec3, Mat4 } from '../linear-algebra/3d' + +interface SymmetryOperator { + readonly name: string, + readonly hkl: Vec3, + + readonly matrix: Mat4, + // cache the inverse of the transform + readonly inverse: Mat4, + // optimize the identity case + readonly isIdentity: boolean +} + +namespace SymmetryOperator { + export const DefaultName = '1_555' + export const Default: SymmetryOperator = create(DefaultName, Mat4.identity()); + + export function create(name: string, matrix: Mat4, hkl?: number[]): SymmetryOperator { + const _hkl = hkl ? Vec3.create(hkl[0], hkl[1], hkl[2]) : Vec3.zero(); + if (Mat4.isIdentity(matrix)) return { name, matrix, inverse: Mat4.identity(), isIdentity: true, hkl: _hkl }; + if (!Mat4.isRotationAndTranslation(matrix)) throw new Error('Symmetry operator must be a composition of rotation and translation.'); + return { name, matrix, inverse: Mat4.invert(Mat4.zero(), matrix), isIdentity: false, hkl: _hkl }; + } + + export interface CoordinateMapper { (index: number, slot: Vec3): Vec3 } + export interface ArrayMapping { + readonly invariantPosition: CoordinateMapper, + readonly position: CoordinateMapper, + x(index: number): number, + y(index: number): number, + z(index: number): number + } + + export interface Coordinates { x: ArrayLike<number>, y: ArrayLike<number>, z: ArrayLike<number> } + + export function createMapping(operator: SymmetryOperator, coords: Coordinates) { + const invariantPosition = SymmetryOperator.createCoordinateMapper(SymmetryOperator.Default, coords); + const position = operator.isIdentity ? invariantPosition : SymmetryOperator.createCoordinateMapper(operator, coords); + const { x, y, z } = createProjections(operator, coords); + return { invariantPosition, position, x, y, z }; + } + + export function createCoordinateMapper(t: SymmetryOperator, coords: Coordinates): CoordinateMapper { + if (t.isIdentity) return identityPosition(coords); + return generalPosition(t, coords); + } +} + +export default SymmetryOperator + +interface Projections { x(index: number): number, y(index: number): number, z(index: number): number } + +function createProjections(t: SymmetryOperator, coords: SymmetryOperator.Coordinates): Projections { + if (t.isIdentity) return { x: projectCoord(coords.x), y: projectCoord(coords.y), z: projectCoord(coords.z) }; + return { x: projectX(t, coords), y: projectY(t, coords), z: projectZ(t, coords) }; +} + +function projectCoord(xs: ArrayLike<number>) { + return (i: number) => xs[i]; +} + +function isW1(m: Mat4) { + return m[3] === 0 && m[7] === 0 && m[11] === 0 && m[15] === 1; +} + +function projectX({ matrix: m }: SymmetryOperator, { x: xs, y: ys, z: zs}: SymmetryOperator.Coordinates) { + const xx = m[0], yy = m[4], zz = m[8], tx = m[12]; + + if (isW1(m)) { + // this should always be the case. + return (i: number) => xx * xs[i] + yy * ys[i] + zz * zs[i] + tx; + } + + return (i: number) => { + const x = xs[i], y = ys[i], z = zs[i], w = (m[3] * x + m[7] * y + m[11] * z + m[15]) || 1.0; + return (xx * x + yy * y + zz * z + tx) / w; + } +} + +function projectY({ matrix: m }: SymmetryOperator, { x: xs, y: ys, z: zs}: SymmetryOperator.Coordinates) { + const xx = m[1], yy = m[5], zz = m[9], ty = m[13]; + + if (isW1(m)) { + // this should always be the case. + return (i: number) => xx * xs[i] + yy * ys[i] + zz * zs[i] + ty; + } + + return (i: number) => { + const x = xs[i], y = ys[i], z = zs[i], w = (m[3] * x + m[7] * y + m[11] * z + m[15]) || 1.0; + return (xx * x + yy * y + zz * z + ty) / w; + } +} + +function projectZ({ matrix: m }: SymmetryOperator, { x: xs, y: ys, z: zs}: SymmetryOperator.Coordinates) { + const xx = m[2], yy = m[6], zz = m[10], tz = m[14]; + + if (isW1(m)) { + // this should always be the case. + return (i: number) => xx * xs[i] + yy * ys[i] + zz * zs[i] + tz; + } + + return (i: number) => { + const x = xs[i], y = ys[i], z = zs[i], w = (m[3] * x + m[7] * y + m[11] * z + m[15]) || 1.0; + return (xx * x + yy * y + zz * z + tz) / w; + } +} + +function identityPosition({ x, y, z }: SymmetryOperator.Coordinates): SymmetryOperator.CoordinateMapper { + return (i, s) => { + s[0] = x[i]; + s[1] = y[i]; + s[2] = z[i]; + return s; + } +} + +function generalPosition({ matrix: m }: SymmetryOperator, { x: xs, y: ys, z: zs }: SymmetryOperator.Coordinates) { + if (isW1(m)) { + // this should always be the case. + return (i: number, r: Vec3): Vec3 => { + const x = xs[i], y = ys[i], z = zs[i]; + r[0] = m[0] * x + m[4] * y + m[8] * z + m[12]; + r[1] = m[1] * x + m[5] * y + m[9] * z + m[13]; + r[2] = m[2] * x + m[6] * y + m[10] * z + m[14]; + return r; + } + } + return (i: number, r: Vec3): Vec3 => { + r[0] = xs[i]; + r[1] = ys[i]; + r[2] = zs[i]; + Vec3.transformMat4(r, r, m); + return r; + } +} \ No newline at end of file diff --git a/src/mol-math/graph/graph.ts b/src/mol-math/graph/graph.ts new file mode 100644 index 0000000000000000000000000000000000000000..0ffdd02fcbce683e436c0030ffe0517135c6ceda --- /dev/null +++ b/src/mol-math/graph/graph.ts @@ -0,0 +1 @@ +// TODO \ No newline at end of file diff --git a/src/mol-math/linear-algebra.ts b/src/mol-math/linear-algebra.ts new file mode 100644 index 0000000000000000000000000000000000000000..20ffdc270398f55572af9a3ca3a8f71fc67adc61 --- /dev/null +++ b/src/mol-math/linear-algebra.ts @@ -0,0 +1,8 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +export * from './linear-algebra/3d' +export * from './linear-algebra/tensor' \ No newline at end of file diff --git a/src/mol-math/linear-algebra/3d.ts b/src/mol-math/linear-algebra/3d.ts new file mode 100644 index 0000000000000000000000000000000000000000..f989a61c08071c119239843a33b756dea1cbd267 --- /dev/null +++ b/src/mol-math/linear-algebra/3d.ts @@ -0,0 +1,699 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +/* + * This code has been modified from https://github.com/toji/gl-matrix/, + * copyright (c) 2015, Brandon Jones, Colin MacKenzie IV. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + */ + +export interface Mat4 { [d: number]: number, '@type': 'mat4' } +export interface Vec3 { [d: number]: number, '@type': 'vec3' | 'vec4' } +export interface Vec4 { [d: number]: number, '@type': 'vec4' } + +const enum EPSILON { Value = 0.000001 } + +export function Mat4() { + return Mat4.zero(); +} + +/** + * Stores a 4x4 matrix in a column major (j * 4 + i indexing) format. + */ +export namespace Mat4 { + export function zero(): Mat4 { + // force double backing array by 0.1. + const ret = [0.1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + ret[0] = 0.0; + return ret as any; + } + + export function identity(): Mat4 { + const out = zero(); + out[0] = 1; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = 1; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[10] = 1; + out[11] = 0; + out[12] = 0; + out[13] = 0; + out[14] = 0; + out[15] = 1; + return out; + } + + export function setIdentity(mat: Mat4): Mat4 { + mat[0] = 1; + mat[1] = 0; + mat[2] = 0; + mat[3] = 0; + mat[4] = 0; + mat[5] = 1; + mat[6] = 0; + mat[7] = 0; + mat[8] = 0; + mat[9] = 0; + mat[10] = 1; + mat[11] = 0; + mat[12] = 0; + mat[13] = 0; + mat[14] = 0; + mat[15] = 1; + return mat; + } + + export function ofRows(rows: number[][]): Mat4 { + const out = zero(); + for (let i = 0; i < 4; i++) { + const r = rows[i]; + for (let j = 0; j < 4; j++) { + out[4 * j + i] = r[j]; + } + } + return out; + } + + const _id = identity(); + export function isIdentity(m: Mat4, eps?: number) { + return areEqual(m, _id, typeof eps === 'undefined' ? EPSILON.Value : eps); + } + + export function areEqual(a: Mat4, b: Mat4, eps: number) { + for (let i = 0; i < 16; i++) { + if (Math.abs(a[i] - b[i]) > eps) return false; + } + return true; + } + + export function setValue(a: Mat4, i: number, j: number, value: number) { + a[4 * j + i] = value; + } + + export function copy(out: Mat4, a: Mat4) { + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + out[4] = a[4]; + out[5] = a[5]; + out[6] = a[6]; + out[7] = a[7]; + out[8] = a[8]; + out[9] = a[9]; + out[10] = a[10]; + out[11] = a[11]; + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + return out; + } + + export function clone(a: Mat4) { + return Mat4.copy(Mat4.zero(), a); + } + + export function invert(out: Mat4, a: Mat4) { + const a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], + a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], + a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], + a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15], + + b00 = a00 * a11 - a01 * a10, + b01 = a00 * a12 - a02 * a10, + b02 = a00 * a13 - a03 * a10, + b03 = a01 * a12 - a02 * a11, + b04 = a01 * a13 - a03 * a11, + b05 = a02 * a13 - a03 * a12, + b06 = a20 * a31 - a21 * a30, + b07 = a20 * a32 - a22 * a30, + b08 = a20 * a33 - a23 * a30, + b09 = a21 * a32 - a22 * a31, + b10 = a21 * a33 - a23 * a31, + b11 = a22 * a33 - a23 * a32; + + // Calculate the determinant + let det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; + + if (!det) { + console.warn('non-invertible matrix.', a); + return out; + } + det = 1.0 / det; + + out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det; + out[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det; + out[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det; + out[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det; + out[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det; + out[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det; + out[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det; + out[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det; + out[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det; + out[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det; + out[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det; + out[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det; + out[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det; + out[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det; + out[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det; + out[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det; + + return out; + } + + export function mul(out: Mat4, a: Mat4, b: Mat4) { + const a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], + a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], + a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], + a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15]; + + // Cache only the current line of the second matrix + let b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3]; + out[0] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30; + out[1] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31; + out[2] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32; + out[3] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33; + + b0 = b[4]; b1 = b[5]; b2 = b[6]; b3 = b[7]; + out[4] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30; + out[5] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31; + out[6] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32; + out[7] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33; + + b0 = b[8]; b1 = b[9]; b2 = b[10]; b3 = b[11]; + out[8] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30; + out[9] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31; + out[10] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32; + out[11] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33; + + b0 = b[12]; b1 = b[13]; b2 = b[14]; b3 = b[15]; + out[12] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30; + out[13] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31; + out[14] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32; + out[15] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33; + return out; + } + + export function mul3(out: Mat4, a: Mat4, b: Mat4, c: Mat4) { + return mul(out, mul(out, a, b), c); + } + + export function translate(out: Mat4, a: Mat4, v: Mat4) { + const x = v[0], y = v[1], z = v[2]; + let a00: number, a01: number, a02: number, a03: number, + a10: number, a11: number, a12: number, a13: number, + a20: number, a21: number, a22: number, a23: number; + + if (a === out) { + out[12] = a[0] * x + a[4] * y + a[8] * z + a[12]; + out[13] = a[1] * x + a[5] * y + a[9] * z + a[13]; + out[14] = a[2] * x + a[6] * y + a[10] * z + a[14]; + out[15] = a[3] * x + a[7] * y + a[11] * z + a[15]; + } else { + a00 = a[0]; a01 = a[1]; a02 = a[2]; a03 = a[3]; + a10 = a[4]; a11 = a[5]; a12 = a[6]; a13 = a[7]; + a20 = a[8]; a21 = a[9]; a22 = a[10]; a23 = a[11]; + + out[0] = a00; out[1] = a01; out[2] = a02; out[3] = a03; + out[4] = a10; out[5] = a11; out[6] = a12; out[7] = a13; + out[8] = a20; out[9] = a21; out[10] = a22; out[11] = a23; + + out[12] = a00 * x + a10 * y + a20 * z + a[12]; + out[13] = a01 * x + a11 * y + a21 * z + a[13]; + out[14] = a02 * x + a12 * y + a22 * z + a[14]; + out[15] = a03 * x + a13 * y + a23 * z + a[15]; + } + + return out; + } + + export function fromTranslation(out: Mat4, v: Mat4) { + out[0] = 1; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = 1; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[10] = 1; + out[11] = 0; + out[12] = v[0]; + out[13] = v[1]; + out[14] = v[2]; + out[15] = 1; + return out; + } + + export function rotate(out: Mat4, a: Mat4, rad: number, axis: Mat4) { + let x = axis[0], y = axis[1], z = axis[2], + len = Math.sqrt(x * x + y * y + z * z), + s, c, t, + a00, a01, a02, a03, + a10, a11, a12, a13, + a20, a21, a22, a23, + b00, b01, b02, + b10, b11, b12, + b20, b21, b22; + + if (Math.abs(len) < EPSILON.Value) { + return Mat4.identity(); + } + + len = 1 / len; + x *= len; + y *= len; + z *= len; + + s = Math.sin(rad); + c = Math.cos(rad); + t = 1 - c; + + a00 = a[0]; a01 = a[1]; a02 = a[2]; a03 = a[3]; + a10 = a[4]; a11 = a[5]; a12 = a[6]; a13 = a[7]; + a20 = a[8]; a21 = a[9]; a22 = a[10]; a23 = a[11]; + + // Construct the elements of the rotation matrix + b00 = x * x * t + c; b01 = y * x * t + z * s; b02 = z * x * t - y * s; + b10 = x * y * t - z * s; b11 = y * y * t + c; b12 = z * y * t + x * s; + b20 = x * z * t + y * s; b21 = y * z * t - x * s; b22 = z * z * t + c; + + // Perform rotation-specific matrix multiplication + out[0] = a00 * b00 + a10 * b01 + a20 * b02; + out[1] = a01 * b00 + a11 * b01 + a21 * b02; + out[2] = a02 * b00 + a12 * b01 + a22 * b02; + out[3] = a03 * b00 + a13 * b01 + a23 * b02; + out[4] = a00 * b10 + a10 * b11 + a20 * b12; + out[5] = a01 * b10 + a11 * b11 + a21 * b12; + out[6] = a02 * b10 + a12 * b11 + a22 * b12; + out[7] = a03 * b10 + a13 * b11 + a23 * b12; + out[8] = a00 * b20 + a10 * b21 + a20 * b22; + out[9] = a01 * b20 + a11 * b21 + a21 * b22; + out[10] = a02 * b20 + a12 * b21 + a22 * b22; + out[11] = a03 * b20 + a13 * b21 + a23 * b22; + + if (a !== out) { // If the source and destination differ, copy the unchanged last row + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + } + return out; + } + + export function fromRotation(out: Mat4, rad: number, axis: Vec3) { + let x = axis[0], y = axis[1], z = axis[2], + len = Math.sqrt(x * x + y * y + z * z), + s, c, t; + + if (Math.abs(len) < EPSILON.Value) { return setIdentity(out); } + + len = 1 / len; + x *= len; + y *= len; + z *= len; + + s = Math.sin(rad); + c = Math.cos(rad); + t = 1 - c; + + // Perform rotation-specific matrix multiplication + out[0] = x * x * t + c; + out[1] = y * x * t + z * s; + out[2] = z * x * t - y * s; + out[3] = 0; + out[4] = x * y * t - z * s; + out[5] = y * y * t + c; + out[6] = z * y * t + x * s; + out[7] = 0; + out[8] = x * z * t + y * s; + out[9] = y * z * t - x * s; + out[10] = z * z * t + c; + out[11] = 0; + out[12] = 0; + out[13] = 0; + out[14] = 0; + out[15] = 1; + return out; + } + + export function scale(out: Mat4, a: Mat4, v: Vec3) { + const x = v[0], y = v[1], z = v[2]; + + out[0] = a[0] * x; + out[1] = a[1] * x; + out[2] = a[2] * x; + out[3] = a[3] * x; + out[4] = a[4] * y; + out[5] = a[5] * y; + out[6] = a[6] * y; + out[7] = a[7] * y; + out[8] = a[8] * z; + out[9] = a[9] * z; + out[10] = a[10] * z; + out[11] = a[11] * z; + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + return out; + } + + export function fromScaling(out: Mat4, v: Vec3) { + out[0] = v[0]; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = v[1]; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[10] = v[2]; + out[11] = 0; + out[12] = 0; + out[13] = 0; + out[14] = 0; + out[15] = 1; + return out; + } + + export function makeTable(m: Mat4) { + let ret = ''; + for (let i = 0; i < 4; i++) { + for (let j = 0; j < 4; j++) { + ret += m[4 * j + i].toString(); + if (j < 3) ret += ' '; + } + if (i < 3) ret += '\n'; + } + return ret; + } + + export function determinant(a: Mat4) { + const a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], + a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], + a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], + a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15], + + b00 = a00 * a11 - a01 * a10, + b01 = a00 * a12 - a02 * a10, + b02 = a00 * a13 - a03 * a10, + b03 = a01 * a12 - a02 * a11, + b04 = a01 * a13 - a03 * a11, + b05 = a02 * a13 - a03 * a12, + b06 = a20 * a31 - a21 * a30, + b07 = a20 * a32 - a22 * a30, + b08 = a20 * a33 - a23 * a30, + b09 = a21 * a32 - a22 * a31, + b10 = a21 * a33 - a23 * a31, + b11 = a22 * a33 - a23 * a32; + + // Calculate the determinant + return b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; + } + + export function isRotationAndTranslation(a: Mat4) { + const a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], + a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], + a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], + /* a30 = a[12], a31 = a[13], a32 = a[14],*/ a33 = a[15]; + + if (a33 !== 1 || a03 !== 0 || a13 !== 0 || a23 !== 0) return false; + const det3x3 = a00 * (a11 * a22 - a21 * a21) - a01 * (a10 * a22 - a12 * a20) + a02 * (a10 * a21 - a11 * a20); + if (det3x3 < 1 - EPSILON.Value || det3x3 > 1 + EPSILON.Value) return false; + return true; + } +} + +export namespace Vec3 { + export function zero(): Vec3 { + const out = [0.1, 0.0, 0.0]; + out[0] = 0; + return out as any; + } + + export function clone(a: Vec3): Vec3 { + const out = zero(); + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + return out; + } + + export function fromObj(v: { x: number, y: number, z: number }): Vec3 { + return create(v.x, v.y, v.z); + } + + export function toObj(v: Vec3) { + return { x: v[0], y: v[1], z: v[2] }; + } + + export function create(x: number, y: number, z: number): Vec3 { + const out = zero(); + out[0] = x; + out[1] = y; + out[2] = z; + return out; + } + + export function set(out: Vec3, x: number, y: number, z: number): Vec3 { + out[0] = x; + out[1] = y; + out[2] = z; + return out; + } + + export function copy(out: Vec3, a: Vec3) { + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + return out; + } + + export function add(out: Vec3, a: Vec3, b: Vec3) { + out[0] = a[0] + b[0]; + out[1] = a[1] + b[1]; + out[2] = a[2] + b[2]; + return out; + } + + export function sub(out: Vec3, a: Vec3, b: Vec3) { + out[0] = a[0] - b[0]; + out[1] = a[1] - b[1]; + out[2] = a[2] - b[2]; + return out; + } + + export function scale(out: Vec3, a: Vec3, b: number) { + out[0] = a[0] * b; + out[1] = a[1] * b; + out[2] = a[2] * b; + return out; + } + + export function scaleAndAdd(out: Vec3, a: Vec3, b: Vec3, scale: number) { + out[0] = a[0] + (b[0] * scale); + out[1] = a[1] + (b[1] * scale); + out[2] = a[2] + (b[2] * scale); + return out; + } + + export function distance(a: Vec3, b: Vec3) { + const x = b[0] - a[0], + y = b[1] - a[1], + z = b[2] - a[2]; + return Math.sqrt(x * x + y * y + z * z); + } + + export function squaredDistance(a: Vec3, b: Vec3) { + const x = b[0] - a[0], + y = b[1] - a[1], + z = b[2] - a[2]; + return x * x + y * y + z * z; + } + + export function magnitude(a: Vec3) { + const x = a[0], + y = a[1], + z = a[2]; + return Math.sqrt(x * x + y * y + z * z); + } + + export function squaredMagnitude(a: Vec3) { + const x = a[0], + y = a[1], + z = a[2]; + return x * x + y * y + z * z; + } + + export function normalize(out: Vec3, a: Vec3) { + const x = a[0], + y = a[1], + z = a[2]; + let len = x * x + y * y + z * z; + if (len > 0) { + len = 1 / Math.sqrt(len); + out[0] = a[0] * len; + out[1] = a[1] * len; + out[2] = a[2] * len; + } + return out; + } + + export function dot(a: Vec3, b: Vec3) { + return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; + } + + export function cross(out: Vec3, a: Vec3, b: Vec3) { + const ax = a[0], ay = a[1], az = a[2], + bx = b[0], by = b[1], bz = b[2]; + + out[0] = ay * bz - az * by; + out[1] = az * bx - ax * bz; + out[2] = ax * by - ay * bx; + return out; + } + + export function lerp(out: Vec3, a: Vec3, b: Vec3, t: number) { + const ax = a[0], + ay = a[1], + az = a[2]; + out[0] = ax + t * (b[0] - ax); + out[1] = ay + t * (b[1] - ay); + out[2] = az + t * (b[2] - az); + return out; + } + + export function transformMat4(out: Vec3, a: Vec3, m: Mat4) { + const x = a[0], y = a[1], z = a[2], + w = (m[3] * x + m[7] * y + m[11] * z + m[15]) || 1.0; + out[0] = (m[0] * x + m[4] * y + m[8] * z + m[12]) / w; + out[1] = (m[1] * x + m[5] * y + m[9] * z + m[13]) / w; + out[2] = (m[2] * x + m[6] * y + m[10] * z + m[14]) / w; + return out; + } + + const angleTempA = zero(), angleTempB = zero(); + export function angle(a: Vec3, b: Vec3) { + copy(angleTempA, a); + copy(angleTempB, b); + + normalize(angleTempA, angleTempA); + normalize(angleTempB, angleTempB); + + const cosine = dot(angleTempA, angleTempB); + + if (cosine > 1.0) { + return 0; + } + else if (cosine < -1.0) { + return Math.PI; + } else { + return Math.acos(cosine); + } + } + + const rotTemp = zero(); + export function makeRotation(mat: Mat4, a: Vec3, b: Vec3): Mat4 { + const by = angle(a, b); + if (Math.abs(by) < 0.0001) return Mat4.setIdentity(mat); + const axis = cross(rotTemp, a, b); + return Mat4.fromRotation(mat, by, axis); + } +} + +export namespace Vec4 { + export function zero(): Vec4 { + // force double backing array by 0.1. + const ret = [0.1, 0, 0, 0]; + ret[0] = 0.0; + return ret as any; + } + + export function clone(a: Vec4) { + const out = zero(); + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + return out; + } + + export function create(x: number, y: number, z: number, w: number) { + const out = zero(); + out[0] = x; + out[1] = y; + out[2] = z; + out[3] = w; + return out; + } + + export function set(out: Vec4, x: number, y: number, z: number, w: number) { + out[0] = x; + out[1] = y; + out[2] = z; + out[3] = w; + return out; + } + + export function distance(a: Vec4, b: Vec4) { + const x = b[0] - a[0], + y = b[1] - a[1], + z = b[2] - a[2], + w = b[3] - a[3]; + return Math.sqrt(x * x + y * y + z * z + w * w); + } + + export function squaredDistance(a: Vec4, b: Vec4) { + const x = b[0] - a[0], + y = b[1] - a[1], + z = b[2] - a[2], + w = b[3] - a[3]; + return x * x + y * y + z * z + w * w; + } + + export function norm(a: Vec4) { + const x = a[0], + y = a[1], + z = a[2], + w = a[3]; + return Math.sqrt(x * x + y * y + z * z + w * w); + } + + export function squaredNorm(a: Vec4) { + const x = a[0], + y = a[1], + z = a[2], + w = a[3]; + return x * x + y * y + z * z + w * w; + } + + export function transform(out: Vec4, a: Vec4, m: Mat4) { + const x = a[0], y = a[1], z = a[2], w = a[3]; + out[0] = m[0] * x + m[4] * y + m[8] * z + m[12] * w; + out[1] = m[1] * x + m[5] * y + m[9] * z + m[13] * w; + out[2] = m[2] * x + m[6] * y + m[10] * z + m[14] * w; + out[3] = m[3] * x + m[7] * y + m[11] * z + m[15] * w; + return out; + } +} \ No newline at end of file diff --git a/src/mol-math/linear-algebra/_spec/tensor.spec.ts b/src/mol-math/linear-algebra/_spec/tensor.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..1c5ace38f1fe4a5b8affb8aa6e874e396f9f297c --- /dev/null +++ b/src/mol-math/linear-algebra/_spec/tensor.spec.ts @@ -0,0 +1,266 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { Tensor as T } from '../tensor' +import { Mat4 } from '../3d' + +describe('tensor', () => { + it('vector', () => { + const V = T.Vector(3); + const data = V.create(); + + V.set(data, 0, 1); + V.set(data, 1, 2); + V.set(data, 2, 3); + + expect(data).toEqual(new Float64Array([1, 2, 3])); + expect(V.get(data, 0)).toEqual(1); + expect(V.get(data, 1)).toEqual(2); + expect(V.get(data, 2)).toEqual(3); + }); + + it('matrix cm', () => { + const M = T.ColumnMajorMatrix(3, 2, Int32Array); + const data = M.create() + + // rows: [ [0, 1], [1, 2], [2, 3] ] + for (let i = 0; i < 3; i++) { + for (let j = 0; j < 2; j++) { + M.set(data, i, j, i + j); + } + } + + expect(data).toEqual(new Int32Array([0, 1, 2, 1, 2, 3])); + }); + + it('matrix rm', () => { + const M = T.RowMajorMatrix(3, 2, Int32Array); + const data = M.create(); + + // rows: [ [0, 1], [1, 2], [2, 3] ] + for (let i = 0; i < 3; i++) { + for (let j = 0; j < 2; j++) { + M.set(data, i, j, i + j); + } + } + expect(data).toEqual(new Int32Array([0, 1, 1, 2, 2, 3])); + }); + + it('mat4 equiv', () => { + const M = T.ColumnMajorMatrix(4, 4); + const data = M.create(); + const m = Mat4.zero(); + + for (let i = 0; i < 4; i++) { + for (let j = 0; j < 4; j++) { + const v = (i + 1) * (j + 2); + M.set(data, i, j, v); + Mat4.setValue(m, i, j, v); + } + } + + for (let i = 0; i < 16; i++) expect(data[i]).toEqual(m[i]); + }); + + it('2d ij', () => { + const M = T.Space([3, 4], [0, 1]); + const data = M.create(); + const exp = new Float64Array(3 * 4) + + let o = 0; + for (let i = 0; i < 3; i++) { + for (let j = 0; j < 4; j++) { + M.set(data, i, j, o); + expect(M.get(data, i, j)).toBe(o); + exp[o] = o; + o++; + } + } + + expect(data).toEqual(exp); + }); + + it('2d ji', () => { + const M = T.Space([3, 4], [1, 0]); + const data = M.create(); + const exp = new Float64Array(3 * 4) + + let o = 0; + for (let j = 0; j < 4; j++) { + for (let i = 0; i < 3; i++) { + M.set(data, i, j, o); + expect(M.get(data, i, j)).toBe(o); + exp[o] = o; + o++; + } + } + + expect(data).toEqual(exp); + }); + + it('3d ijk', () => { + const M = T.Space([3, 4, 5], [0, 1, 2]); + const data = M.create(); + const exp = new Float64Array(3 * 4 * 5) + + let o = 0; + for (let i = 0; i < 3; i++) { + for (let j = 0; j < 4; j++) { + for (let k = 0; k < 5; k++) { + M.set(data, i, j, k, o); + expect(M.get(data, i, j, k)).toBe(o); + exp[o] = o; + o++; + } + } + } + + expect(data).toEqual(exp); + }); + + it('3d ikj', () => { + const M = T.Space([3, 3, 3], [0, 2, 1]); + const data = M.create(); + const exp = new Float64Array(3 * 3 * 3) + + let o = 0; + for (let i = 0; i < 3; i++) { + for (let k = 0; k < 3; k++) { + for (let j = 0; j < 3; j++) { + M.set(data, i, j, k, o); + expect(M.get(data, i, j, k)).toBe(o); + exp[o] = o; + o++; + } + } + } + + expect(data).toEqual(exp); + }); + + it('3d jik', () => { + const M = T.Space([3, 3, 3], [1, 0, 2]); + const data = M.create(); + const exp = new Float64Array(3 * 3 * 3) + + let o = 0; + for (let j = 0; j < 3; j++) { + for (let i = 0; i < 3; i++) { + for (let k = 0; k < 3; k++) { + M.set(data, i, j, k, o); + expect(M.get(data, i, j, k)).toBe(o); + exp[o] = o; + o++; + } + } + } + + expect(data).toEqual(exp); + }); + it('3d jki', () => { + const M = T.Space([3, 3, 3], [1, 2, 0]); + const data = M.create(); + const exp = new Float64Array(3 * 3 * 3) + + let o = 0; + for (let j = 0; j < 3; j++) { + for (let k = 0; k < 3; k++) { + for (let i = 0; i < 3; i++) { + M.set(data, i, j, k, o); + expect(M.get(data, i, j, k)).toBe(o); + exp[o] = o; + o++; + } + } + } + + expect(data).toEqual(exp); + }); + + it('3d kij', () => { + const M = T.Space([3, 3, 3], [2, 0, 1]); + const data = M.create(); + const exp = new Float64Array(3 * 3 * 3) + + let o = 0; + for (let k = 0; k < 3; k++) { + for (let i = 0; i < 3; i++) { + for (let j = 0; j < 3; j++) { + M.set(data, i, j, k, o); + expect(M.get(data, i, j, k)).toBe(o); + exp[o] = o; + o++; + } + } + } + + expect(data).toEqual(exp); + }); + + it('3d kji', () => { + const M = T.Space([3, 3, 3], [2, 1, 0]); + const data = M.create(); + const exp = new Float64Array(3 * 3 * 3) + + let o = 0; + for (let k = 0; k < 3; k++) { + for (let j = 0; j < 3; j++) { + for (let i = 0; i < 3; i++) { + M.set(data, i, j, k, o); + expect(M.get(data, i, j, k)).toBe(o); + exp[o] = o; + o++; + } + } + } + + expect(data).toEqual(exp); + }); + + it('4d jikl', () => { + const M = T.Space([2, 3, 4, 5], [1, 0, 2, 3]); + const data = M.create(); + const exp = new Float64Array(2 * 3 * 4 * 5) + + let o = 0; + for (let j = 0; j < 3; j++) { + for (let i = 0; i < 2; i++) { + for (let k = 0; k < 4; k++) { + for (let l = 0; l < 5; l++) { + M.set(data, i, j, k, l, o); + expect(M.get(data, i, j, k, l)).toBe(o); + exp[o] = o; + o++; + } + } + } + } + + expect(data).toEqual(exp); + }); + + it('4d jilk', () => { + const M = T.Space([2, 3, 4, 5], [1, 0, 3, 2]); + const data = M.create(); + const exp = new Float64Array(2 * 3 * 4 * 5) + + let o = 0; + for (let j = 0; j < 3; j++) { + for (let i = 0; i < 2; i++) { + for (let l = 0; l < 5; l++) { + for (let k = 0; k < 4; k++) { + M.set(data, i, j, k, l, o); + expect(M.get(data, i, j, k, l)).toBe(o); + exp[o] = o; + o++; + } + } + } + } + + expect(data).toEqual(exp); + }); +}); \ No newline at end of file diff --git a/src/mol-math/linear-algebra/tensor.ts b/src/mol-math/linear-algebra/tensor.ts new file mode 100644 index 0000000000000000000000000000000000000000..3efd39d60ebb057788b8e4bda0e12ff292ea971c --- /dev/null +++ b/src/mol-math/linear-algebra/tensor.ts @@ -0,0 +1,151 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { Mat4, Vec3, Vec4 } from './3d' + +export interface Tensor extends Array<number> { '@type': 'tensor' } + +export namespace Tensor { + export type ArrayCtor = { new (size: number): ArrayLike<number> } + + export interface Space { + readonly rank: number, + readonly dimensions: ReadonlyArray<number>, + readonly axisOrderSlowToFast: ReadonlyArray<number>, + create(array?: ArrayCtor): Tensor, + get(data: Tensor, ...coords: number[]): number + set(data: Tensor, ...coordsAndValue: number[]): number + } + + interface Layout { + dimensions: number[], + axisOrderSlowToFast: number[], + axisOrderFastToSlow: number[], + accessDimensions: number[], + // if not specified, use Float64Array + defaultCtor: ArrayCtor + } + + function Layout(dimensions: number[], axisOrderSlowToFast: number[], ctor?: ArrayCtor): Layout { + // need to reverse the axis order for better access. + const axisOrderFastToSlow: number[] = []; + for (let i = 0; i < axisOrderSlowToFast.length; i++) axisOrderFastToSlow[i] = axisOrderSlowToFast[axisOrderSlowToFast.length - i - 1]; + + const accessDimensions = [1]; + for (let i = 1; i < dimensions.length; i++) accessDimensions[i] = dimensions[axisOrderFastToSlow[i - 1]]; + return { dimensions, axisOrderFastToSlow, axisOrderSlowToFast, accessDimensions, defaultCtor: ctor || Float64Array } + } + + export function Space(dimensions: number[], axisOrderSlowToFast: number[], ctor?: ArrayCtor): Space { + const layout = Layout(dimensions, axisOrderSlowToFast, ctor); + const { get, set } = accessors(layout); + return { rank: dimensions.length, dimensions, axisOrderSlowToFast, create: creator(layout), get, set }; + } + + export function Vector(d: number, ctor?: ArrayCtor) { return Space([d], [0], ctor); } + export function ColumnMajorMatrix(rows: number, cols: number, ctor?: ArrayCtor) { return Space([rows, cols], [1, 0], ctor); } + export function RowMajorMatrix(rows: number, cols: number, ctor?: ArrayCtor) { return Space([rows, cols], [0, 1], ctor); } + + export function toMat4(space: Space, data: Tensor): Mat4 { + if (space.rank !== 2) throw new Error('Invalid tensor rank'); + const mat = Mat4.zero(); + const d0 = Math.min(4, space.dimensions[0]), d1 = Math.min(4, space.dimensions[1]); + for (let i = 0; i < d0; i++) { + for (let j = 0; j < d1; j++) Mat4.setValue(mat, i, j, space.get(data, i, j)); + } + return mat; + } + + export function toVec3(space: Space, data: Tensor): Vec3 { + if (space.rank !== 1) throw new Error('Invalid tensor rank'); + const vec = Vec3.zero(); + const d0 = Math.min(3, space.dimensions[0]); + for (let i = 0; i < d0; i++) vec[i] = data[i]; + return vec; + } + + export function toVec4(space: Space, data: Tensor): Vec4 { + if (space.rank !== 1) throw new Error('Invalid tensor rank'); + const vec = Vec4.zero(); + const d0 = Math.min(4, space.dimensions[0]); + for (let i = 0; i < d0; i++) vec[i] = data[i]; + return vec; + } + + export function areEqualExact(a: Tensor, b: Tensor) { + const len = a.length; + if (len !== b.length) return false; + for (let i = 0; i < len; i++) if (a[i] !== b[i]) return false; + return true; + } + + function accessors(layout: Layout): { get: Space['get'], set: Space['set'] } { + const { dimensions, axisOrderFastToSlow: ao } = layout; + switch (dimensions.length) { + case 1: return { get: (t, d) => t[d], set: (t, d, x) => t[d] = x }; + case 2: { + // column major + if (ao[0] === 0 && ao[1] === 1) { + const rows = dimensions[0]; + return { get: (t, i, j) => t[j * rows + i], set: (t, i, j, x) => t[j * rows + i] = x }; + } + if (ao[0] === 1 && ao[1] === 0) { + const cols = dimensions[1]; + return { get: (t, i, j) => t[i * cols + j], set: (t, i, j, x) => t[i * cols + j] = x }; + } + throw new Error('bad axis order') + } + case 3: { + if (ao[0] === 0 && ao[1] === 1 && ao[2] === 2) { // 012 ijk + const u = dimensions[0], v = dimensions[1], uv = u * v; + return { get: (t, i, j, k) => t[i + j * u + k * uv], set: (t, i, j, k, x ) => t[i + j * u + k * uv] = x }; + } + if (ao[0] === 0 && ao[1] === 2 && ao[2] === 1) { // 021 ikj + const u = dimensions[0], v = dimensions[2], uv = u * v; + return { get: (t, i, j, k) => t[i + k * u + j * uv], set: (t, i, j, k, x ) => t[i + k * u + j * uv] = x }; + } + if (ao[0] === 1 && ao[1] === 0 && ao[2] === 2) { // 102 jik + const u = dimensions[1], v = dimensions[0], uv = u * v; + return { get: (t, i, j, k) => t[j + i * u + k * uv], set: (t, i, j, k, x ) => t[j + i * u + k * uv] = x }; + } + if (ao[0] === 1 && ao[1] === 2 && ao[2] === 0) { // 120 jki + const u = dimensions[1], v = dimensions[2], uv = u * v; + return { get: (t, i, j, k) => t[j + k * u + i * uv], set: (t, i, j, k, x ) => t[j + k * u + i * uv] = x }; + } + if (ao[0] === 2 && ao[1] === 0 && ao[2] === 1) { // 201 kij + const u = dimensions[2], v = dimensions[0], uv = u * v; + return { get: (t, i, j, k) => t[k + i * u + j * uv], set: (t, i, j, k, x ) => t[k + i * u + j * uv] = x }; + } + if (ao[0] === 2 && ao[1] === 1 && ao[2] === 0) { // 210 kji + const u = dimensions[2], v = dimensions[1], uv = u * v; + return { get: (t, i, j, k) => t[k + j * u + i * uv], set: (t, i, j, k, x ) => t[k + j * u + i * uv] = x }; + } + throw new Error('bad axis order') + } + default: return { + get: (t, ...c) => t[dataOffset(layout, c)], + set: (t, ...c) => t[dataOffset(layout, c)] = c[c.length - 1] + }; + } + } + + function creator(layout: Layout): Space['create'] { + const { dimensions: ds } = layout; + let size = 1; + for (let i = 0, _i = ds.length; i < _i; i++) size *= ds[i]; + return ctor => new (ctor || layout.defaultCtor)(size) as Tensor; + } + + function dataOffset(layout: Layout, coord: number[]) { + const { accessDimensions: acc, axisOrderFastToSlow: ao } = layout; + const d = acc.length - 1; + let o = acc[d] * coord[ao[d]]; + for (let i = d - 1; i >= 0; i--) { + o = (o + coord[ao[i]]) * acc[i]; + } + return o; + } +} \ No newline at end of file diff --git a/src/mol-model/sequence/TODO b/src/mol-model/sequence/TODO new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/mol-model/structure.ts b/src/mol-model/structure.ts new file mode 100644 index 0000000000000000000000000000000000000000..38d5890d5f0fada2ea72a32c23e6e9d3f2e0261b --- /dev/null +++ b/src/mol-model/structure.ts @@ -0,0 +1,9 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +export * from './structure/model' +export * from './structure/structure' +export * from './structure/query' \ No newline at end of file diff --git a/src/mol-model/structure/_spec/atom-set.spec.ts b/src/mol-model/structure/_spec/atom-set.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..d77dd5e869d41acd27f28c0f32ecaeb70525816d --- /dev/null +++ b/src/mol-model/structure/_spec/atom-set.spec.ts @@ -0,0 +1,157 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { OrderedSet } from 'mol-data/int' +import AtomSet from '../structure/atom/set' +import Atom from '../structure/atom' + +describe('atom set', () => { + const p = (i: number, j: number) => Atom.create(i, j); + + function setToPairs(set: AtomSet): ArrayLike<Atom> { + const ret: Atom[] = []; + const it = AtomSet.atoms(set); + while (it.hasNext) { + ret[ret.length] = it.move(); + } + return ret; + } + + it('singleton pair', () => { + const set = AtomSet.create(p(10, 11)); + expect(setToPairs(set)).toEqual([p(10, 11)]); + expect(AtomSet.atomHas(set, p(10, 11))).toBe(true); + expect(AtomSet.atomHas(set, p(11, 11))).toBe(false); + expect(AtomSet.atomGetAt(set, 0)).toBe(p(10, 11)); + expect(AtomSet.atomCount(set)).toBe(1); + }); + + it('singleton number', () => { + const set = AtomSet.create(p(10, 11)); + expect(setToPairs(set)).toEqual([p(10, 11)]); + }); + + it('multi', () => { + const set = AtomSet.create({ + 1: OrderedSet.ofSortedArray([4, 6, 7]), + 3: OrderedSet.ofRange(0, 1), + }); + const ret = [p(1, 4), p(1, 6), p(1, 7), p(3, 0), p(3, 1)]; + expect(AtomSet.atomCount(set)).toBe(ret.length); + expect(setToPairs(set)).toEqual([p(1, 4), p(1, 6), p(1, 7), p(3, 0), p(3, 1)]); + expect(AtomSet.atomHas(set, p(10, 11))).toBe(false); + expect(AtomSet.atomHas(set, p(3, 0))).toBe(true); + expect(AtomSet.atomHas(set, p(1, 7))).toBe(true); + for (let i = 0; i < AtomSet.atomCount(set); i++) { + expect(Atom.areEqual(AtomSet.atomGetAt(set, i), ret[i])).toBe(true); + } + }); + + it('element at / index of', () => { + const control: Atom[] = []; + const sets = Object.create(null); + for (let i = 1; i < 10; i++) { + const set = []; + for (let j = 1; j < 7; j++) { + control[control.length] = p(i * i, j * j + 1); + set[set.length] = j * j + 1; + } + sets[i * i] = OrderedSet.ofSortedArray(set); + } + const ms = AtomSet.create(sets); + for (let i = 0; i < control.length; i++) { + expect(Atom.areEqual(AtomSet.atomGetAt(ms, i), control[i])).toBe(true); + } + + for (let i = 0; i < control.length; i++) { + expect(AtomSet.atomIndexOf(ms, control[i])).toBe(i); + } + }); + + it('packed pairs', () => { + const set = AtomSet.create([p(1, 3), p(0, 1), p(0, 6), p(0, 2)]); + expect(setToPairs(set)).toEqual([p(0, 1), p(0, 2), p(0, 6), p(1, 3)]); + }); + + it('equality', () => { + const a = AtomSet.create([p(1, 3), p(0, 1), p(0, 6), p(0, 2)]); + const b = AtomSet.create([p(1, 3), p(0, 1), p(0, 6), p(0, 2)]); + const c = AtomSet.create([p(1, 3), p(0, 4), p(0, 6), p(0, 2)]); + const d = AtomSet.create([p(1, 3)]); + const e = AtomSet.create([p(1, 3)]); + const f = AtomSet.create([p(3, 3)]); + + expect(AtomSet.areEqual(a, a)).toBe(true); + expect(AtomSet.areEqual(a, b)).toBe(true); + expect(AtomSet.areEqual(a, c)).toBe(false); + expect(AtomSet.areEqual(a, d)).toBe(false); + expect(AtomSet.areEqual(d, d)).toBe(true); + expect(AtomSet.areEqual(d, e)).toBe(true); + expect(AtomSet.areEqual(d, f)).toBe(false); + }); + + it('are intersecting', () => { + const a = AtomSet.create([p(1, 3), p(0, 1), p(0, 6), p(0, 2)]); + const b = AtomSet.create([p(1, 3), p(0, 1), p(0, 6), p(0, 2)]); + const c = AtomSet.create([p(1, 3), p(0, 4), p(0, 6), p(0, 2)]); + const d = AtomSet.create([p(1, 3)]); + const e = AtomSet.create([p(1, 3)]); + const f = AtomSet.create([p(3, 3)]); + const g = AtomSet.create([p(10, 3), p(8, 1), p(7, 6), p(3, 2)]); + + expect(AtomSet.areIntersecting(a, a)).toBe(true); + expect(AtomSet.areIntersecting(a, b)).toBe(true); + expect(AtomSet.areIntersecting(a, c)).toBe(true); + expect(AtomSet.areIntersecting(a, d)).toBe(true); + expect(AtomSet.areIntersecting(a, g)).toBe(false); + expect(AtomSet.areIntersecting(d, d)).toBe(true); + expect(AtomSet.areIntersecting(d, e)).toBe(true); + expect(AtomSet.areIntersecting(d, f)).toBe(false); + }); + + it('intersection', () => { + const a = AtomSet.create([p(1, 3), p(0, 1), p(0, 6), p(0, 2)]); + const b = AtomSet.create([p(10, 3), p(0, 1), p(0, 6), p(4, 2)]); + const c = AtomSet.create([p(1, 3)]); + const d = AtomSet.create([p(2, 3)]); + expect(AtomSet.intersect(a, a)).toBe(a); + expect(setToPairs(AtomSet.intersect(a, b))).toEqual([p(0, 1), p(0, 6)]); + expect(setToPairs(AtomSet.intersect(a, c))).toEqual([p(1, 3)]); + expect(setToPairs(AtomSet.intersect(c, d))).toEqual([]); + }); + + it('subtract', () => { + const a = AtomSet.create([p(1, 3), p(0, 1), p(0, 6), p(0, 2)]); + const a1 = AtomSet.create([p(1, 3), p(0, 1), p(0, 6), p(0, 2)]); + const b = AtomSet.create([p(10, 3), p(0, 1), p(0, 6), p(4, 2)]); + const c = AtomSet.create([p(1, 3)]); + const d = AtomSet.create([p(2, 3)]); + const e = AtomSet.create([p(0, 2)]); + expect(setToPairs(AtomSet.subtract(a, a))).toEqual([]); + expect(setToPairs(AtomSet.subtract(a, a1))).toEqual([]); + expect(setToPairs(AtomSet.subtract(a, b))).toEqual([p(0, 2), p(1, 3)]); + expect(setToPairs(AtomSet.subtract(c, d))).toEqual([p(1, 3)]); + expect(setToPairs(AtomSet.subtract(a, c))).toEqual([p(0, 1), p(0, 2), p(0, 6)]); + expect(setToPairs(AtomSet.subtract(c, a))).toEqual([]); + expect(setToPairs(AtomSet.subtract(d, a))).toEqual([p(2, 3)]); + expect(setToPairs(AtomSet.subtract(a, e))).toEqual([p(0, 1), p(0, 6), p(1, 3)]); + }); + + it('union', () => { + const a = AtomSet.create([p(1, 3), p(0, 1)]); + const a1 = AtomSet.create([p(1, 3), p(0, 1)]); + const b = AtomSet.create([p(10, 3), p(0, 1)]); + const c = AtomSet.create([p(1, 3)]); + const d = AtomSet.create([p(2, 3)]); + expect(AtomSet.unionMany([a])).toBe(a); + expect(AtomSet.union(a, a)).toBe(a); + expect(setToPairs(AtomSet.union(a, a))).toEqual([p(0, 1), p(1, 3)]); + expect(setToPairs(AtomSet.union(a, a1))).toEqual([p(0, 1), p(1, 3)]); + expect(setToPairs(AtomSet.union(a, b))).toEqual([p(0, 1), p(1, 3), p(10, 3)]); + expect(setToPairs(AtomSet.union(c, d))).toEqual([p(1, 3), p(2, 3)]); + expect(setToPairs(AtomSet.unionMany([a, b, c, d]))).toEqual([p(0, 1), p(1, 3), p(2, 3), p(10, 3)]); + }); +}); \ No newline at end of file diff --git a/src/mol-model/structure/export/mmcif.ts b/src/mol-model/structure/export/mmcif.ts new file mode 100644 index 0000000000000000000000000000000000000000..09d54ab04d10396846bd09b69d6393aba03c30c5 --- /dev/null +++ b/src/mol-model/structure/export/mmcif.ts @@ -0,0 +1,158 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { Column, Table } from 'mol-data/db' +import Iterator from 'mol-data/iterator' +import * as Encoder from 'mol-io/writer/cif' +//import { mmCIF_Schema } from 'mol-io/reader/cif/schema/mmcif' +import { Structure, Atom, AtomSet } from '../structure' +import { Model } from '../model' +import P from '../query/properties' + +interface Context { + structure: Structure, + model: Model +} + +function str<K, D>(name: string, value: (k: K, d: D) => string, valueKind?: (k: K) => Column.ValueKind): Encoder.FieldDefinition<K, D> { + return { name, type: Encoder.FieldType.Str, value, valueKind } +} + +function int<K, D = any>(name: string, value: (k: K, d: D) => number, valueKind?: (k: K) => Column.ValueKind): Encoder.FieldDefinition<K, D> { + return { name, type: Encoder.FieldType.Int, value, valueKind } +} + +function float<K, D = any>(name: string, value: (k: K, d: D) => number, valueKind?: (k: K) => Column.ValueKind): Encoder.FieldDefinition<K, D> { + return { name, type: Encoder.FieldType.Float, value, valueKind } +} + +// function col<K, D>(name: string, c: (data: D) => Column<any>): Encoder.FieldDefinition<K, D> { +// const kind = c['@type'].kind; +// // TODO: matrix/vector/support +// const type = kind === 'str' ? Encoder.FieldType.Str : kind === 'int' ? Encoder.FieldType.Int : Encoder.FieldType.Float +// return { name, type, value, valueKind } +// } + +function columnValue(k: string) { + return (i: number, d: any) => d[k].value(i); +} + +function columnValueKind(k: string) { + return (i: number, d: any) => d[k].valueKind(i); +} + +function ofSchema(schema: Table.Schema) { + const fields: Encoder.FieldDefinition[] = []; + for (const k of Object.keys(schema)) { + const t = schema[k]; + // TODO: matrix/vector/support + const type: any = t.valueType === 'str' ? Encoder.FieldType.Str : t.valueType === 'int' ? Encoder.FieldType.Int : Encoder.FieldType.Float; + fields.push({ name: k, type, value: columnValue(k), valueKind: columnValueKind(k) }) + } + return fields; +} + +function ofTable<S extends Table.Schema>(name: string, table: Table<S>): Encoder.CategoryDefinition<number> { + return { name, fields: ofSchema(table._schema) } +} + +// type Entity = Table.Columns<typeof mmCIF_Schema.entity> + +// const entity: Encoder.CategoryDefinition<number, Entity> = { +// name: 'entity', +// fields: ofSchema(mmCIF_Schema.entity) +// } + + +// [ +// str('id', (i, e) => e.id.value(i)), +// str('type', (i, e) => e.type.value(i)), +// str('src_method', (i, e) => e.src_method.value(i)), +// str('pdbx_description', (i, e) => e.pdbx_description.value(i)), +// int('formula_weight', (i, e) => e.formula_weight.value(i)), +// float('pdbx_number_of_molecules', (i, e) => e.pdbx_number_of_molecules.value(i)), +// str('details', (i, e) => e.details.value(i)), +// str('pdbx_mutation', (i, e) => e.pdbx_mutation.value(i)), +// str('pdbx_fragment', (i, e) => e.pdbx_fragment.value(i)), +// str('pdbx_ec', (i, e) => e.pdbx_ec.value(i)), +// ] + +// type AtomSite = typeof mmCIF_Schema.atom_site; +// type DataSource<Key, Schema extends Table.Schema> = { [P in keyof Schema]: (key: Key) => Schema[P]['T'] } + +// export const atom_site1: Partial<DataSource<Atom.Location, AtomSite>> = { +// group_PDB: P.residue.group_PDB, +// id: P.atom.id, +// type_symbol: P.atom.type_symbol as any, +// label_atom_id: P.atom.label_atom_id, +// //... +// } + +const atom_site: Encoder.CategoryDefinition<Atom.Location> = { + name: 'atom_site', + fields: [ + str('group_PDB', P.residue.group_PDB), + int('id', P.atom.id), + str('type_symbol', P.atom.type_symbol as any), + str('label_atom_id', P.atom.label_atom_id), + str('label_alt_id', P.atom.label_alt_id), + + str('label_comp_id', P.residue.label_comp_id), + int('label_seq_id', P.residue.label_seq_id), + str('pdbx_PDB_ins_code', P.residue.pdbx_PDB_ins_code), + + str('label_asym_id', P.chain.label_asym_id), + str('label_entity_id', P.chain.label_entity_id), + + float('Cartn_x', P.atom.x), + float('Cartn_y', P.atom.y), + float('Cartn_z', P.atom.z), + float('occupancy', P.atom.occupancy), + str('pdbx_formal_charge', P.atom.pdbx_formal_charge), + + str('auth_atom_id', P.atom.auth_atom_id), + str('auth_comp_id', P.residue.auth_comp_id), + int('auth_seq_id', P.residue.auth_seq_id), + str('auth_asym_id', P.chain.auth_asym_id), + + int('pdbx_PDB_model_num', P.unit.model_num), + str('pdbx_operator_name', P.unit.operator_name) + ] +}; + +function entityProvider({ model }: Context): Encoder.CategoryInstance { + return { + data: model.hierarchy.entities, + definition: ofTable('entity', model.hierarchy.entities), //entity, + keys: () => Iterator.Range(0, model.hierarchy.entities._rowCount - 1), + rowCount: model.hierarchy.entities._rowCount + } +} + +function atomSiteProvider({ structure }: Context): Encoder.CategoryInstance { + return { + data: void 0, + definition: atom_site, + keys: () => Structure.atomLocationsTransient(structure), + rowCount: AtomSet.atomCount(structure.atoms) + } +} + +function to_mmCIF(name: string, structure: Structure, asBinary = false) { + const models = Structure.getModels(structure); + if (models.length !== 1) throw 'cant export stucture composed from multiple models.'; + const model = models[0]; + + const ctx: Context = { structure, model }; + const w = Encoder.create({ binary: asBinary }); + + w.startDataBlock(name); + w.writeCategory(entityProvider, [ctx]); + w.writeCategory(atomSiteProvider, [ctx]); + return w.getData(); +} + +export default to_mmCIF \ No newline at end of file diff --git a/src/mol-model/structure/model.ts b/src/mol-model/structure/model.ts new file mode 100644 index 0000000000000000000000000000000000000000..0693de0dfa574bfd37b713d6f5e219b9811e49ba --- /dev/null +++ b/src/mol-model/structure/model.ts @@ -0,0 +1,11 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import Model from './model/model' +import * as Types from './model/types' +import Format from './model/format' + +export { Model, Types, Format } \ No newline at end of file diff --git a/src/mol-model/structure/model/format.ts b/src/mol-model/structure/model/format.ts new file mode 100644 index 0000000000000000000000000000000000000000..3f376d1fe0ea4acc400dfc413629b6d54b8420b6 --- /dev/null +++ b/src/mol-model/structure/model/format.ts @@ -0,0 +1,16 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { mmCIF_Database } from 'mol-io/reader/cif/schema/mmcif' + +type Format = + | Format.mmCIF + +namespace Format { + export interface mmCIF { kind: 'mmCIF', data: mmCIF_Database } +} + +export default Format \ No newline at end of file diff --git a/src/mol-model/structure/model/formats/mmcif.ts b/src/mol-model/structure/model/formats/mmcif.ts new file mode 100644 index 0000000000000000000000000000000000000000..b0f421ca8420ff32f22abfeda7ee75ece663f503 --- /dev/null +++ b/src/mol-model/structure/model/formats/mmcif.ts @@ -0,0 +1,128 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import UUID from 'mol-util/uuid' +import { Column, Table } from 'mol-data/db' +import { Interval, Segmentation } from 'mol-data/int' +import Format from '../format' +import Model from '../model' +import * as Hierarchy from '../properties/hierarchy' +import Conformation from '../properties/conformation' +import findHierarchyKeys from '../utils/hierarchy-keys' +import { ElementSymbol} from '../types' + +import mmCIF_Format = Format.mmCIF + +function findModelBounds({ data }: mmCIF_Format, startIndex: number) { + const num = data.atom_site.pdbx_PDB_model_num; + const atomCount = num.rowCount; + if (!num.isDefined) return Interval.ofBounds(startIndex, atomCount); + let endIndex = startIndex + 1; + while (endIndex < atomCount && num.areValuesEqual(startIndex, endIndex)) endIndex++; + return Interval.ofBounds(startIndex, endIndex); +} + +function findHierarchyOffsets({ data }: mmCIF_Format, bounds: Interval) { + const start = Interval.start(bounds), end = Interval.end(bounds); + const residues = [start], chains = [start]; + + const { label_entity_id, auth_asym_id, auth_seq_id, pdbx_PDB_ins_code, label_comp_id } = data.atom_site; + + for (let i = start + 1; i < end; i++) { + const newChain = !label_entity_id.areValuesEqual(i - 1, i) || !auth_asym_id.areValuesEqual(i - 1, i); + const newResidue = newChain + || !auth_seq_id.areValuesEqual(i - 1, i) + || !pdbx_PDB_ins_code.areValuesEqual(i - 1, i) + || !label_comp_id.areValuesEqual(i - 1, i); + + if (newResidue) residues[residues.length] = i; + if (newChain) chains[chains.length] = i; + } + return { residues, chains }; +} + +function createHierarchyData({ data }: mmCIF_Format, bounds: Interval, offsets: { residues: ArrayLike<number>, chains: ArrayLike<number> }): Hierarchy.Data { + const { atom_site } = data; + const start = Interval.start(bounds), end = Interval.end(bounds); + const atoms = Table.ofColumns(Hierarchy.AtomsSchema, { + type_symbol: Column.ofArray({ array: Column.mapToArray(Column.window(atom_site.type_symbol, start, end), ElementSymbol), schema: Column.Schema.Aliased<ElementSymbol>(Column.Schema.str) }), + label_atom_id: Column.window(atom_site.label_atom_id, start, end), + auth_atom_id: Column.window(atom_site.auth_atom_id, start, end), + label_alt_id: Column.window(atom_site.label_alt_id, start, end), + pdbx_formal_charge: Column.window(atom_site.pdbx_formal_charge, start, end) + }); + const residues = Table.view(atom_site, Hierarchy.ResiduesSchema, offsets.residues); + // Optimize the numeric columns + Table.columnToArray(residues, 'label_seq_id', Int32Array); + Table.columnToArray(residues, 'auth_seq_id', Int32Array); + const chains = Table.view(atom_site, Hierarchy.ChainsSchema, offsets.chains); + return { atoms, residues, chains, entities: data.entity }; +} + +function getConformation({ data }: mmCIF_Format, bounds: Interval): Conformation { + const start = Interval.start(bounds), end = Interval.end(bounds); + const { atom_site } = data; + return { + id: UUID.create(), + atomId: Column.window(atom_site.id, start, end), + occupancy: Column.window(atom_site.occupancy, start, end), + B_iso_or_equiv: Column.window(atom_site.B_iso_or_equiv, start, end), + x: atom_site.Cartn_x.toArray({ array: Float32Array, start, end }), + y: atom_site.Cartn_y.toArray({ array: Float32Array, start, end }), + z: atom_site.Cartn_z.toArray({ array: Float32Array, start, end }), + } +} + +function isHierarchyDataEqual(a: Hierarchy.Hierarchy, b: Hierarchy.Data) { + // need to cast because of how TS handles type resolution for interfaces https://github.com/Microsoft/TypeScript/issues/15300 + return Table.areEqual(a.chains as Table<Hierarchy.ChainsSchema>, b.chains as Table<Hierarchy.ChainsSchema>) + && Table.areEqual(a.residues as Table<Hierarchy.ResiduesSchema>, b.residues as Table<Hierarchy.ResiduesSchema>) + && Table.areEqual(a.atoms as Table<Hierarchy.AtomsSchema>, b.atoms as Table<Hierarchy.AtomsSchema>) +} + +function createModel(format: mmCIF_Format, bounds: Interval, previous?: Model): Model { + const hierarchyOffsets = findHierarchyOffsets(format, bounds); + const hierarchyData = createHierarchyData(format, bounds, hierarchyOffsets); + + if (previous && isHierarchyDataEqual(previous.hierarchy, hierarchyData)) { + return { + ...previous, + conformation: getConformation(format, bounds) + }; + } + + const hierarchySegments: Hierarchy.Segments = { + residueSegments: Segmentation.ofOffsets(hierarchyOffsets.residues, bounds), + chainSegments: Segmentation.ofOffsets(hierarchyOffsets.chains, bounds), + } + const hierarchyKeys = findHierarchyKeys(hierarchyData, hierarchySegments); + return { + id: UUID.create(), + sourceData: format, + modelNum: format.data.atom_site.pdbx_PDB_model_num.value(Interval.start(bounds)), + hierarchy: { ...hierarchyData, ...hierarchyKeys, ...hierarchySegments }, + conformation: getConformation(format, bounds), + atomCount: Interval.size(bounds) + }; +} + +function buildModels(format: mmCIF_Format): ReadonlyArray<Model> { + const models: Model[] = []; + const atomCount = format.data.atom_site._rowCount; + + if (atomCount === 0) return models; + + let modelStart = 0; + while (modelStart < atomCount) { + const bounds = findModelBounds(format, modelStart); + const model = createModel(format, bounds, models.length > 0 ? models[models.length - 1] : void 0); + models.push(model); + modelStart = Interval.end(bounds); + } + return models; +} + +export default buildModels; \ No newline at end of file diff --git a/src/mol-model/structure/model/model.ts b/src/mol-model/structure/model/model.ts new file mode 100644 index 0000000000000000000000000000000000000000..c7a33db549de634b695c23a9baadc7ab5a4842f9 --- /dev/null +++ b/src/mol-model/structure/model/model.ts @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import UUID from 'mol-util/uuid' +import Format from './format' +import HierarchyProperties from './properties/hierarchy' +import ConformationProperties from './properties/conformation' +import from_mmCIF from './formats/mmcif' + + +/** + * Interface to the "source data" of the molecule. + * + * "Atoms" are integers in the range [0, atomCount). + */ +interface Model extends Readonly<{ + id: UUID, + + modelNum: number, + + sourceData: Format, + + hierarchy: HierarchyProperties, + conformation: ConformationProperties, + + atomCount: number +}> { } + +namespace Model { + export function create(format: Format) { + switch (format.kind) { + case 'mmCIF': return from_mmCIF(format); + } + } +} + +export default Model \ No newline at end of file diff --git a/src/mol-model/structure/model/properties/computed.ts b/src/mol-model/structure/model/properties/computed.ts new file mode 100644 index 0000000000000000000000000000000000000000..39896495fbaa30bcf65f3575e6664b7517a9af68 --- /dev/null +++ b/src/mol-model/structure/model/properties/computed.ts @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +// TODO: stuff like "residue type/flags", isRingAtom, rings, bonds??, wrap these in a computation? +// secondary structure is also a computed property + +// import { SecondaryStructureType } from '../constants' + + +// interface SecondaryStructure { +// ofResidue: ArrayLike<SecondaryStructureType>, +// // atom segmentation?? +// segments: Segmentation +// } + +// interface Conformation { +// positions: Conformation, +// secondaryStructure: SecondaryStructure +// } \ No newline at end of file diff --git a/src/mol-model/structure/model/properties/conformation.ts b/src/mol-model/structure/model/properties/conformation.ts new file mode 100644 index 0000000000000000000000000000000000000000..ce94b8597d2d1920b469f86d3942898e403fa5de --- /dev/null +++ b/src/mol-model/structure/model/properties/conformation.ts @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { Column } from 'mol-data/db' +import UUID from 'mol-util/uuid' + +interface Conformation { + id: UUID, + + // ID is part of conformation because mmCIF is a leaky abstraction + // that assigns different atom ids to corresponding atoms in different models + // ... go figure. + atomId: Column<number>, + + occupancy: Column<number>, + B_iso_or_equiv: Column<number> + + // Coordinates. Generally, not to be accessed directly because the coordinate might be + // transformed by an operator. Use Unit.getPosition instead. + x: ArrayLike<number>, + y: ArrayLike<number>, + z: ArrayLike<number> +} + +export default Conformation \ No newline at end of file diff --git a/src/mol-model/structure/model/properties/format-specific.ts b/src/mol-model/structure/model/properties/format-specific.ts new file mode 100644 index 0000000000000000000000000000000000000000..a978e6aca0854c64d8432445da85b89855d267fe --- /dev/null +++ b/src/mol-model/structure/model/properties/format-specific.ts @@ -0,0 +1,7 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +// TODO add access to things like MOL2 charge ... \ No newline at end of file diff --git a/src/mol-model/structure/model/properties/hierarchy.ts b/src/mol-model/structure/model/properties/hierarchy.ts new file mode 100644 index 0000000000000000000000000000000000000000..92d5f006a82c551c31a1904e4d7317363f585f8e --- /dev/null +++ b/src/mol-model/structure/model/properties/hierarchy.ts @@ -0,0 +1,81 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { Column, Table } from 'mol-data/db' +import { Segmentation } from 'mol-data/int' +import { mmCIF_Schema as mmCIF } from 'mol-io/reader/cif/schema/mmcif' +import { ElementSymbol} from '../types' + +export const AtomsSchema = { + type_symbol: Column.Schema.Aliased<ElementSymbol>(mmCIF.atom_site.type_symbol), + label_atom_id: mmCIF.atom_site.label_atom_id, + auth_atom_id: mmCIF.atom_site.auth_atom_id, + label_alt_id: mmCIF.atom_site.label_alt_id, + pdbx_formal_charge: mmCIF.atom_site.pdbx_formal_charge + // id, occupancy and B_iso_or_equiv are part of conformation +}; + +export type AtomsSchema = typeof AtomsSchema +export interface Atoms extends Table<AtomsSchema> { } + +export const ResiduesSchema = { + group_PDB: mmCIF.atom_site.group_PDB, + label_comp_id: mmCIF.atom_site.label_comp_id, + auth_comp_id: mmCIF.atom_site.auth_comp_id, + label_seq_id: mmCIF.atom_site.label_seq_id, + auth_seq_id: mmCIF.atom_site.auth_seq_id, + pdbx_PDB_ins_code: mmCIF.atom_site.pdbx_PDB_ins_code +}; +export type ResiduesSchema = typeof ResiduesSchema +export interface Residues extends Table<ResiduesSchema> { } + +export const ChainsSchema = { + label_asym_id: mmCIF.atom_site.label_asym_id, + auth_asym_id: mmCIF.atom_site.auth_asym_id, + label_entity_id: mmCIF.atom_site.label_entity_id +} +export type ChainsSchema = typeof ChainsSchema +export interface Chains extends Table<ChainsSchema> { } + +export const EntitySchema = mmCIF['entity'] +export type EntitySchema = typeof EntitySchema +export interface Entities extends Table<EntitySchema> { } + +export interface Data { + atoms: Atoms, + residues: Residues, + chains: Chains, + entities: Entities +} + +export interface Segments { + residueSegments: Segmentation, + chainSegments: Segmentation +} + +export interface Keys { + // indicate whether the keys form an increasing sequence and within each chain, sequence numbers + // are in increasing order. + // monotonous sequences enable for example faster secodnary structure assignment. + isMonotonous: boolean, + + // assign a key to each residue index. + residueKey: Column<number>, + // assign a key to each chain index + chainKey: Column<number>, + // assigne a key to each chain index + // also index to the Entities table. + entityKey: Column<number>, + + findEntityKey(id: string): number, + findChainKey(entityId: string, label_asym_id: string): number, + findResidueKey(entityId: string, label_asym_id: string, label_comp_id: string, auth_seq_id: number, pdbx_PDB_ins_code: string): number +} + +type _Hierarchy = Data & Segments & Keys +export interface Hierarchy extends _Hierarchy { } + +export default Hierarchy \ No newline at end of file diff --git a/src/mol-model/structure/model/properties/transforms.ts b/src/mol-model/structure/model/properties/transforms.ts new file mode 100644 index 0000000000000000000000000000000000000000..42d9b7797614f887871b1196fd5816ceef643987 --- /dev/null +++ b/src/mol-model/structure/model/properties/transforms.ts @@ -0,0 +1,9 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +// TODO: symmetry and assebmlies descriptors + +//import Column from 'mol-base/collections/column' \ No newline at end of file diff --git a/src/mol-model/structure/model/types.ts b/src/mol-model/structure/model/types.ts new file mode 100644 index 0000000000000000000000000000000000000000..d105c1f81c8e0e59ea64430ae12e815bc71a2a28 --- /dev/null +++ b/src/mol-model/structure/model/types.ts @@ -0,0 +1,340 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import BitFlags from 'mol-util/bit-flags' + +const _esCache = (function () { + const cache = Object.create(null); + const letters: string[] = []; + for (let i = 'A'.charCodeAt(0); i <= 'Z'.charCodeAt(0); i++) letters[letters.length] = String.fromCharCode(i); + for (let i = 'a'.charCodeAt(0); i <= 'z'.charCodeAt(0); i++) letters[letters.length] = String.fromCharCode(i); + for (let i = '0'.charCodeAt(0); i <= '9'.charCodeAt(0); i++) letters[letters.length] = String.fromCharCode(i); + + for (const k of letters) { + cache[k] = k.toUpperCase(); + for (const l of letters) { + cache[k + l] = (k + l).toUpperCase(); + for (const m of letters) { + cache[k + l + m] = (k + l + m).toUpperCase(); + } + } + } + return cache; +}()); +export interface ElementSymbol extends String { '@type': 'element-symbol' } +export function ElementSymbol(s: string): ElementSymbol { + return _esCache[s] || s.toUpperCase(); +} + +export const enum EntityType { + Unknown = 'unknown', + Polymer = 'polymer', + NonPolymer = 'non-polymer', + Macrolide = 'macrolide', + Water = 'water' +} + +export const enum MoleculeType { + Unknown, + Water, + Ion, + Protein, + RNA, + DNA, + Saccharide +} + +export const enum BackboneType { + Unknown, + Protein, + RNA, + DNA, + CgProtein, + CgRNA, + CgDNA +} + +const _chemCompNonPolymer = ['NON-POLYMER']; +const _chemCompOther = ['OTHER']; +const _chemCompSaccharide = [ + 'D-SACCHARIDE', 'D-SACCHARIDE 1,4 AND 1,4 LINKING', 'D-SACCHARIDE 1,4 AND 1,6 LINKING', + 'L-SACCHARIDE', 'L-SACCHARIDE 1,4 AND 1,4 LINKING', 'L-SACCHARIDE 1,4 AND 1,6 LINKING', + 'SACCHARIDE' +]; + +export const ChemComp = { + Protein: [ + 'D-BETA-PEPTIDE, C-GAMMA LINKING', 'D-GAMMA-PEPTIDE, C-DELTA LINKING', + 'D-PEPTIDE COOH CARBOXY TERMINUS', 'D-PEPTIDE NH3 AMINO TERMINUS', 'D-PEPTIDE LINKING', + 'L-BETA-PEPTIDE, C-GAMMA LINKING', 'L-GAMMA-PEPTIDE, C-DELTA LINKING', + 'L-PEPTIDE COOH CARBOXY TERMINUS', 'L-PEPTIDE NH3 AMINO TERMINUS', 'L-PEPTIDE LINKING', + 'PEPTIDE LINKING', 'PEPTIDE-LIKE' + ], + RNA: [ + 'RNA OH 3 PRIME TERMINUS', 'RNA OH 5 PRIME TERMINUS', 'RNA LINKING' + ], + DNA: [ + 'DNA OH 3 PRIME TERMINUS', 'DNA OH 5 PRIME TERMINUS', 'DNA LINKING', + 'L-DNA LINKING', 'L-RNA LINKING' + ], + Saccharide: _chemCompSaccharide, + Other: _chemCompOther, + NonPolymer: _chemCompNonPolymer, + Hetero: _chemCompNonPolymer.concat(_chemCompOther, _chemCompSaccharide) +} + +export interface SecondaryStructureType extends BitFlags<SecondaryStructureType.Flag> { } +export namespace SecondaryStructureType { + export const Helix = ['h', 'g', 'i'] + export const Sheet = ['e', 'b'] + export const Turn = ['s', 't', 'l', ''] + + export const is: (ss: SecondaryStructureType, f: Flag) => boolean = BitFlags.has + + export const enum Flag { + None = 0x0, + + // category + DoubleHelix = 0x1, + Helix = 0x2, + Beta = 0x4, + Turn = 0x8, + + // category variant + LeftHanded = 0x10, // helix + RightHanded = 0x20, + + ClassicTurn = 0x40, // turn + InverseTurn = 0x80, + + // sub-category + HelixOther = 0x100, // protein + Helix27 = 0x200, + Helix3Ten = 0x400, + HelixAlpha = 0x800, + HelixGamma = 0x1000, + HelixOmega = 0x2000, + HelixPi = 0x4000, + HelixPolyproline = 0x8000, + + DoubleHelixOther = 0x10000, // nucleic + DoubleHelixZ = 0x20000, + DoubleHelixA = 0x40000, + DoubleHelixB = 0x80000, + + BetaOther = 0x100000, // protein + BetaStrand = 0x200000, // single strand + BetaSheet = 0x400000, // multiple hydrogen bonded strands + BetaBarell = 0x800000, // closed series of sheets + + TurnOther = 0x1000000, // protein + Turn1 = 0x2000000, + Turn2 = 0x4000000, + Turn3 = 0x8000000, + + NA = 0x10000000, // not applicable/available + } + + export const SecondaryStructureMmcif: { [value: string]: number } = { + HELX_LH_27_P: Flag.Helix | Flag.LeftHanded | Flag.Helix27, // left-handed 2-7 helix (protein) + HELX_LH_3T_P: Flag.Helix | Flag.LeftHanded | Flag.Helix3Ten, // left-handed 3-10 helix (protein) + HELX_LH_AL_P: Flag.Helix | Flag.LeftHanded | Flag.HelixAlpha, // left-handed alpha helix (protein) + HELX_LH_A_N: Flag.DoubleHelix | Flag.LeftHanded | Flag.DoubleHelixA, // left-handed A helix (nucleic acid) + HELX_LH_B_N: Flag.DoubleHelix | Flag.LeftHanded | Flag.DoubleHelixB, // left-handed B helix (nucleic acid) + HELX_LH_GA_P: Flag.Helix | Flag.LeftHanded | Flag.HelixGamma, // left-handed gamma helix (protein) + HELX_LH_N: Flag.DoubleHelix | Flag.LeftHanded, // left-handed helix with type not specified (nucleic acid) + HELX_LH_OM_P: Flag.Helix | Flag.LeftHanded | Flag.HelixOmega, // left-handed omega helix (protein) + HELX_LH_OT_N: Flag.DoubleHelix | Flag.LeftHanded | Flag.DoubleHelixOther, // left-handed helix with type that does not conform to an accepted category (nucleic acid) + HELX_LH_OT_P: Flag.Helix | Flag.LeftHanded | Flag.HelixOther, // left-handed helix with type that does not conform to an accepted category (protein) + HELX_LH_P: Flag.Helix | Flag.LeftHanded, // left-handed helix with type not specified (protein) + HELX_LH_PI_P: Flag.Helix | Flag.LeftHanded | Flag.HelixPi, // left-handed pi helix (protein) + HELX_LH_PP_P: Flag.Helix | Flag.LeftHanded | Flag.HelixPolyproline, // left-handed polyproline helix (protein) + HELX_LH_Z_N: Flag.DoubleHelix | Flag.LeftHanded | Flag.DoubleHelixZ, // left-handed Z helix (nucleic acid) + HELX_N: Flag.DoubleHelix, // helix with handedness and type not specified (nucleic acid) + HELX_OT_N: Flag.DoubleHelix, // helix with handedness and type that do not conform to an accepted category (nucleic acid) + HELX_OT_P: Flag.Helix, // helix with handedness and type that do not conform to an accepted category (protein) + HELX_P: Flag.Helix, // helix with handedness and type not specified (protein) + HELX_RH_27_P: Flag.Helix | Flag.RightHanded | Flag.Helix27, // right-handed 2-7 helix (protein) + HELX_RH_3T_P: Flag.Helix | Flag.RightHanded | Flag.Helix3Ten, // right-handed 3-10 helix (protein) + HELX_RH_AL_P: Flag.Helix | Flag.RightHanded | Flag.HelixAlpha, // right-handed alpha helix (protein) + HELX_RH_A_N: Flag.DoubleHelix | Flag.RightHanded | Flag.DoubleHelixA, // right-handed A helix (nucleic acid) + HELX_RH_B_N: Flag.DoubleHelix | Flag.RightHanded | Flag.DoubleHelixB, // right-handed B helix (nucleic acid) + HELX_RH_GA_P: Flag.Helix | Flag.RightHanded | Flag.HelixGamma, // right-handed gamma helix (protein) + HELX_RH_N: Flag.DoubleHelix | Flag.RightHanded, // right-handed helix with type not specified (nucleic acid) + HELX_RH_OM_P: Flag.Helix | Flag.RightHanded | Flag.HelixOmega, // right-handed omega helix (protein) + HELX_RH_OT_N: Flag.DoubleHelix | Flag.RightHanded | Flag.DoubleHelixOther, // right-handed helix with type that does not conform to an accepted category (nucleic acid) + HELX_RH_OT_P: Flag.Helix | Flag.RightHanded | Flag.HelixOther, // right-handed helix with type that does not conform to an accepted category (protein) + HELX_RH_P: Flag.Helix | Flag.RightHanded, // right-handed helix with type not specified (protein) + HELX_RH_PI_P: Flag.Helix | Flag.RightHanded | Flag.HelixPi, // right-handed pi helix (protein) + HELX_RH_PP_P: Flag.Helix | Flag.RightHanded | Flag.HelixPolyproline, // right-handed polyproline helix (protein) + HELX_RH_Z_N: Flag.DoubleHelix | Flag.RightHanded | Flag.DoubleHelixZ, // right-handed Z helix (nucleic acid) + STRN: Flag.Beta | Flag.BetaStrand, // beta strand (protein) + TURN_OT_P: Flag.Turn | Flag.TurnOther, // turn with type that does not conform to an accepted category (protein) + TURN_P: Flag.Turn, // turn with type not specified (protein) + TURN_TY1P_P: Flag.Turn | Flag.InverseTurn | Flag.Turn1, // type I prime turn (protein) + TURN_TY1_P: Flag.Turn | Flag.ClassicTurn | Flag.Turn1, // type I turn (protein) + TURN_TY2P_P: Flag.Turn | Flag.InverseTurn | Flag.Turn2, // type II prime turn (protein) + TURN_TY2_P: Flag.Turn | Flag.ClassicTurn | Flag.Turn2, // type II turn (protein) + TURN_TY3P_P: Flag.Turn | Flag.InverseTurn | Flag.Turn3, // type III prime turn (protein) + TURN_TY3_P: Flag.Turn | Flag.ClassicTurn | Flag.Turn3, // type III turn (protein) + } + + export const SecondaryStructurePdb: { [value: string]: number } = { + 1: Flag.Helix | Flag.RightHanded | Flag.HelixAlpha, // Right-handed alpha (default) + 2: Flag.Helix | Flag.RightHanded | Flag.HelixOmega, // Right-handed omega + 3: Flag.Helix | Flag.RightHanded | Flag.HelixPi, // Right-handed pi + 4: Flag.Helix | Flag.RightHanded | Flag.HelixGamma, // Right-handed gamma + 5: Flag.Helix | Flag.RightHanded | Flag.Helix3Ten, // Right-handed 310 + 6: Flag.Helix | Flag.LeftHanded | Flag.HelixAlpha, // Left-handed alpha + 7: Flag.Helix | Flag.LeftHanded | Flag.HelixOmega, // Left-handed omega + 8: Flag.Helix | Flag.LeftHanded | Flag.HelixGamma, // Left-handed gamma + 9: Flag.Helix | Flag.Helix27, // 27 ribbon/helix + 10: Flag.Helix | Flag.HelixPolyproline, // Polyproline + } + + export const SecondaryStructureStride: { [value: string]: number } = { + H: Flag.Helix | Flag.HelixAlpha, // Alpha helix + G: Flag.Helix | Flag.Helix3Ten, // 3-10 helix + I: Flag.Helix | Flag.HelixPi, // PI-helix + E: Flag.Beta | Flag.BetaSheet, // Extended conformation + B: Flag.Beta | Flag.BetaStrand, // Isolated bridge + b: Flag.Beta | Flag.BetaStrand, // Isolated bridge + T: Flag.Turn, // Turn + C: Flag.NA, // Coil (none of the above) + } + + export const SecondaryStructureDssp: { [value: string]: number } = { + H: Flag.Helix | Flag.HelixAlpha, // alpha-helix + B: Flag.Beta | Flag.BetaStrand, // residue in isolated beta-bridge + E: Flag.Beta | Flag.BetaSheet, // extended strand, participates in beta ladder + G: Flag.Helix | Flag.Helix3Ten, // 3-helix (310 helix) + I: Flag.Helix | Flag.HelixPi, // 5 helix (pi-helix) + T: Flag.Turn, // hydrogen bonded turn + S: Flag.Turn, // bend + } +} + +export const VdwRadii = { + 'H': 1.1, + 'HE': 1.4, + 'LI': 1.81, + 'BE': 1.53, + 'B': 1.92, + 'C': 1.7, + 'N': 1.55, + 'O': 1.52, + 'F': 1.47, + 'NE': 1.54, + 'NA': 2.27, + 'MG': 1.73, + 'AL': 1.84, + 'SI': 2.1, + 'P': 1.8, + 'S': 1.8, + 'CL': 1.75, + 'AR': 1.88, + 'K': 2.75, + 'CA': 2.31, + 'SC': 2.3, + 'TI': 2.15, + 'V': 2.05, + 'CR': 2.05, + 'MN': 2.05, + 'FE': 2.05, + 'CO': 2.0, + 'NI': 2.0, + 'CU': 2.0, + 'ZN': 2.1, + 'GA': 1.87, + 'GE': 2.11, + 'AS': 1.85, + 'SE': 1.9, + 'BR': 1.83, + 'KR': 2.02, + 'RB': 3.03, + 'SR': 2.49, + 'Y': 2.4, + 'ZR': 2.3, + 'NB': 2.15, + 'MO': 2.1, + 'TC': 2.05, + 'RU': 2.05, + 'RH': 2.0, + 'PD': 2.05, + 'AG': 2.1, + 'CD': 2.2, + 'IN': 2.2, + 'SN': 1.93, + 'SB': 2.17, + 'TE': 2.06, + 'I': 1.98, + 'XE': 2.16, + 'CS': 3.43, + 'BA': 2.68, + 'LA': 2.5, + 'CE': 2.48, + 'PR': 2.47, + 'ND': 2.45, + 'PM': 2.43, + 'SM': 2.42, + 'EU': 2.4, + 'GD': 2.38, + 'TB': 2.37, + 'DY': 2.35, + 'HO': 2.33, + 'ER': 2.32, + 'TM': 2.3, + 'YB': 2.28, + 'LU': 2.27, + 'HF': 2.25, + 'TA': 2.2, + 'W': 2.1, + 'RE': 2.05, + 'OS': 2.0, + 'IR': 2.0, + 'PT': 2.05, + 'AU': 2.1, + 'HG': 2.05, + 'TL': 1.96, + 'PB': 2.02, + 'BI': 2.07, + 'PO': 1.97, + 'AT': 2.02, + 'RN': 2.2, + 'FR': 3.48, + 'RA': 2.83, + 'AC': 2.0, + 'TH': 2.4, + 'PA': 2.0, + 'U': 2.3, + 'NP': 2.0, + 'PU': 2.0, + 'AM': 2.0, + 'CM': 2.0, + 'BK': 2.0, + 'CF': 2.0, + 'ES': 2.0, + 'FM': 2.0, + 'MD': 2.0, + 'NO': 2.0, + 'LR': 2.0, + 'RF': 2.0, + 'DB': 2.0, + 'SG': 2.0, + 'BH': 2.0, + 'HS': 2.0, + 'MT': 2.0, + 'DS': 2.0, + 'RG': 2.0, + 'CN': 2.0, + 'UUT': 2.0, + 'FL': 2.0, + 'UUP': 2.0, + 'LV': 2.0, + 'UUH': 2.0 +} +export const DefaultVdwRadius = 2.0 \ No newline at end of file diff --git a/src/mol-model/structure/model/utils/hierarchy-keys.ts b/src/mol-model/structure/model/utils/hierarchy-keys.ts new file mode 100644 index 0000000000000000000000000000000000000000..c09484245f80c577b7a948624a56cc469be9adc5 --- /dev/null +++ b/src/mol-model/structure/model/utils/hierarchy-keys.ts @@ -0,0 +1,115 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { Column } from 'mol-data/db' +import { Data, Segments, Keys } from '../properties/hierarchy' +import { Interval, Segmentation } from 'mol-data/int' + +function getResidueId(comp_id: string, seq_id: number, ins_code: string) { + return `${comp_id} ${seq_id} ${ins_code}`; +} + +function getElementKey(map: Map<string, number>, key: string, counter: { index: number }) { + if (map.has(key)) return map.get(key)!; + const ret = counter.index++; + map.set(key, ret); + return ret; +} + +function getElementSubstructureKeyMap(map: Map<number, Map<string, number>>, key: number) { + if (map.has(key)) return map.get(key)!; + const ret = new Map<string, number>(); + map.set(key, ret); + return ret; +} + +function createLookUp(entity: Map<string, number>, chain: Map<number, Map<string, number>>, residue: Map<number, Map<string, number>>) { + const findEntityKey: Keys['findEntityKey'] = (id) => entity.has(id) ? entity.get(id)! : -1; + const findChainKey: Keys['findChainKey'] = (e, c) => { + if (!entity.has(e)) return -1; + const cm = chain.get(entity.get(e)!)!; + if (!cm.has(c)) return -1; + return cm.get(c)!; + } + const findResidueKey: Keys['findResidueKey'] = (e, c, name, seq, ins) => { + if (!entity.has(e)) return -1; + const cm = chain.get(entity.get(e)!)!; + if (!cm.has(c)) return -1; + const rm = residue.get(cm.get(c)!)! + const id = getResidueId(name, seq, ins); + if (!rm.has(id)) return -1; + return rm.get(id)!; + } + return { findEntityKey, findChainKey, findResidueKey }; +} + +function checkMonotonous(xs: ArrayLike<number>) { + for (let i = 1, _i = xs.length; i < _i; i++) { + if (xs[i] < xs[i - 1]) { + return false; + } + } + return true; +} + +function create(data: Data, segments: Segments): Keys { + const { chains, residues, entities } = data; + + const entityMap = Column.createFirstIndexMap(entities.id); + const chainMaps = new Map<number, Map<string, number>>(), chainCounter = { index: 0 }; + const residueMaps = new Map<number, Map<string, number>>(), residueCounter = { index: 0 }; + + const residueKey = new Int32Array(residues._rowCount); + const chainKey = new Int32Array(chains._rowCount); + const entityKey = new Int32Array(chains._rowCount); + + const { label_comp_id, auth_seq_id, pdbx_PDB_ins_code } = data.residues; + const { label_entity_id, label_asym_id } = data.chains; + + const atomSet = Interval.ofBounds(0, data.atoms._rowCount); + + let isMonotonous = true; + + const chainsIt = Segmentation.transientSegments(segments.chainSegments, atomSet); + while (chainsIt.hasNext) { + const chainSegment = chainsIt.move(); + const cI = chainSegment.index; + + const eKey = entityMap.get(label_entity_id.value(cI)) || 0; + const chainMap = getElementSubstructureKeyMap(chainMaps, eKey); + const cKey = getElementKey(chainMap, label_asym_id.value(cI), chainCounter); + + chainKey[cI] = cKey; + entityKey[cI] = eKey; + + const residueMap = getElementSubstructureKeyMap(residueMaps, cKey); + const residuesIt = Segmentation.transientSegments(segments.residueSegments, atomSet, chainSegment); + let last_seq_id = Number.NEGATIVE_INFINITY; + while (residuesIt.hasNext) { + const residueSegment = residuesIt.move(); + const rI = residueSegment.index; + const seq_id = auth_seq_id.value(rI); + if (seq_id < last_seq_id) isMonotonous = false; + last_seq_id = seq_id; + const residueId = getResidueId(label_comp_id.value(rI), auth_seq_id.value(rI), pdbx_PDB_ins_code.value(rI)); + residueKey[rI] = getElementKey(residueMap, residueId, residueCounter); + } + } + + const { findEntityKey, findChainKey, findResidueKey } = createLookUp(entityMap, chainMaps, residueMaps); + + return { + isMonotonous: isMonotonous && checkMonotonous(entityKey) && checkMonotonous(chainKey) && checkMonotonous(residueKey), + residueKey: Column.ofIntArray(residueKey), + chainKey: Column.ofIntArray(chainKey), + entityKey: Column.ofIntArray(entityKey), + findEntityKey, + findChainKey, + findResidueKey + }; +} + +export default create; \ No newline at end of file diff --git a/src/mol-model/structure/query.ts b/src/mol-model/structure/query.ts new file mode 100644 index 0000000000000000000000000000000000000000..55dff3a8e548d080534bb4f171d356689fbfc9b1 --- /dev/null +++ b/src/mol-model/structure/query.ts @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import Selection from './query/selection' +import Query from './query/query' +import * as generators from './query/generators' +import props from './query/properties' +import pred from './query/predicates' + +export const Queries = { + generators, + props, + pred +} + +export { Selection, Query } \ No newline at end of file diff --git a/src/mol-model/structure/query/generators.ts b/src/mol-model/structure/query/generators.ts new file mode 100644 index 0000000000000000000000000000000000000000..12d43abc4ef8c40f3b0f44735a3fbc4e58d9030c --- /dev/null +++ b/src/mol-model/structure/query/generators.ts @@ -0,0 +1,193 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import Query from './query' +import Selection from './selection' +import P from './properties' +import { Structure, AtomSet, Atom } from '../structure' +import { OrderedSet, Segmentation } from 'mol-data/int' + +export const all: Query = s => s; + +export interface AtomGroupsParams { + entityTest: Atom.Predicate, + chainTest: Atom.Predicate, + residueTest: Atom.Predicate, + atomTest: Atom.Predicate, + groupBy: Atom.Property<any> +} + +export function atoms(params?: Partial<AtomGroupsParams>): Query { + if (!params || (!params.atomTest && !params.residueTest && !params.chainTest && !params.entityTest && !params.groupBy)) return all; + if (!!params.atomTest && !params.residueTest && !params.chainTest && !params.entityTest && !params.groupBy) return atomGroupsLinear(params.atomTest); + + const normalized: AtomGroupsParams = { + entityTest: params.entityTest || P.constant.true, + chainTest: params.chainTest || P.constant.true, + residueTest: params.residueTest || P.constant.true, + atomTest: params.atomTest || P.constant.true, + groupBy: params.groupBy || P.constant.zero, + }; + + if (!params.groupBy) return atomGroupsSegmented(normalized) + return atomGroupsGrouped(normalized); +} + +function atomGroupsLinear(atomTest: Atom.Predicate): Query { + return structure => { + const { atoms, units } = structure; + const unitIds = AtomSet.unitIds(atoms); + const l = Atom.Location(); + const builder = AtomSet.LinearBuilder(atoms); + + for (let i = 0, _i = unitIds.length; i < _i; i++) { + const unitId = unitIds[i]; + l.unit = units[unitId]; + const set = AtomSet.unitGetByIndex(atoms, i); + + builder.beginUnit(); + for (let j = 0, _j = OrderedSet.size(set); j < _j; j++) { + l.atom = OrderedSet.getAt(set, j); + if (atomTest(l)) builder.addToUnit(l.atom); + } + builder.commitUnit(unitId); + } + + return Structure.create(units, builder.getSet()); + }; +} + +function atomGroupsSegmented({ entityTest, chainTest, residueTest, atomTest }: AtomGroupsParams): Query { + return structure => { + const { atoms, units } = structure; + const unitIds = AtomSet.unitIds(atoms); + const l = Atom.Location(); + const builder = AtomSet.LinearBuilder(atoms); + + for (let i = 0, _i = unitIds.length; i < _i; i++) { + const unitId = unitIds[i]; + const unit = units[unitId]; + l.unit = unit; + const set = AtomSet.unitGetByIndex(atoms, i); + + builder.beginUnit(); + const chainsIt = Segmentation.transientSegments(unit.hierarchy.chainSegments, set); + const residuesIt = Segmentation.transientSegments(unit.hierarchy.residueSegments, set); + while (chainsIt.hasNext) { + const chainSegment = chainsIt.move(); + l.atom = OrderedSet.getAt(set, chainSegment.start); + // test entity and chain + if (!entityTest(l) || !chainTest(l)) continue; + + residuesIt.setSegment(chainSegment); + while (residuesIt.hasNext) { + const residueSegment = residuesIt.move(); + l.atom = OrderedSet.getAt(set, residueSegment.start); + + // test residue + if (!residueTest(l)) continue; + + for (let j = residueSegment.start, _j = residueSegment.end; j < _j; j++) { + l.atom = OrderedSet.getAt(set, j); + if (atomTest(l)) builder.addToUnit(l.atom); + } + } + } + builder.commitUnit(unitId); + } + + return Structure.create(units, builder.getSet()); + }; +} + +class LinearGroupingBuilder { + private builders: AtomSet.Builder[] = []; + private builderMap: { [key: string]: AtomSet.Builder } = Object.create(null); + + add(key: any, unit: number, atom: number) { + let b = this.builderMap[key]; + if (!b) { + b = AtomSet.LinearBuilder(this.structure.atoms); + this.builders[this.builders.length] = b; + this.builderMap[key] = b; + } + b.add(unit, atom); + } + + private allSingletons() { + for (let i = 0, _i = this.builders.length; i < _i; i++) { + if (this.builders[i].atomCount > 1) return false; + } + return true; + } + + private singletonStructure(): Structure { + const atoms: Atom[] = Atom.createEmptyArray(this.builders.length); + for (let i = 0, _i = this.builders.length; i < _i; i++) { + atoms[i] = this.builders[i].singleton(); + } + return Structure.create(this.structure.units, AtomSet.create(atoms)); + } + + private fullSelection() { + const ret: Structure[] = []; + for (let i = 0, _i = this.builders.length; i < _i; i++) { + ret[i] = Structure.create(this.structure.units, this.builders[i].getSet()); + } + return ret; + } + + getSelection(): Selection { + const len = this.builders.length; + if (len === 0) return Selection.Empty; + if (len === 1) return Structure.create(this.structure.units, this.builders[0].getSet()); + if (this.allSingletons()) return this.singletonStructure(); + return this.fullSelection(); + } + + constructor(private structure: Structure) { } +} + +function atomGroupsGrouped({ entityTest, chainTest, residueTest, atomTest, groupBy }: AtomGroupsParams): Query { + return structure => { + const { atoms, units } = structure; + const unitIds = AtomSet.unitIds(atoms); + const l = Atom.Location(); + const builder = new LinearGroupingBuilder(structure); + + for (let i = 0, _i = unitIds.length; i < _i; i++) { + const unitId = unitIds[i]; + const unit = units[unitId]; + l.unit = unit; + const set = AtomSet.unitGetByIndex(atoms, i); + + const chainsIt = Segmentation.transientSegments(unit.hierarchy.chainSegments, set); + const residuesIt = Segmentation.transientSegments(unit.hierarchy.residueSegments, set); + while (chainsIt.hasNext) { + const chainSegment = chainsIt.move(); + l.atom = OrderedSet.getAt(set, chainSegment.start); + // test entity and chain + if (!entityTest(l) || !chainTest(l)) continue; + + residuesIt.setSegment(chainSegment); + while (residuesIt.hasNext) { + const residueSegment = residuesIt.move(); + l.atom = OrderedSet.getAt(set, residueSegment.start); + + // test residue + if (!residueTest(l)) continue; + + for (let j = residueSegment.start, _j = residueSegment.end; j < _j; j++) { + l.atom = OrderedSet.getAt(set, j); + if (atomTest(l)) builder.add(groupBy(l), unitId, l.atom); + } + } + } + } + + return builder.getSelection(); + }; +} \ No newline at end of file diff --git a/src/mol-model/structure/query/predicates.ts b/src/mol-model/structure/query/predicates.ts new file mode 100644 index 0000000000000000000000000000000000000000..772eb803022029425e2d6bd11792585601da278c --- /dev/null +++ b/src/mol-model/structure/query/predicates.ts @@ -0,0 +1,100 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { Atom } from '../structure' +import P from './properties' + +namespace Predicates { + export interface SetLike<A> { has(v: A): boolean } + function isSetLike<A>(x: any): x is SetLike<A> { return !!x && !!x.has } + + export function eq<A>(p: Atom.Property<A>, value: A): Atom.Predicate { return l => p(l) === value; } + export function lt<A>(p: Atom.Property<A>, value: A): Atom.Predicate { return l => p(l) < value; } + export function lte<A>(p: Atom.Property<A>, value: A): Atom.Predicate { return l => p(l) <= value; } + export function gt<A>(p: Atom.Property<A>, value: A): Atom.Predicate { return l => p(l) > value; } + export function gte<A>(p: Atom.Property<A>, value: A): Atom.Predicate { return l => p(l) >= value; } + + export function inSet<A>(p: Atom.Property<A>, values: SetLike<A> | ArrayLike<A>): Atom.Predicate { + if (isSetLike(values)) { + return l => values.has(p(l)); + } else { + if (values.length === 0) return P.constant.false; + const set = new Set<A>(); + for (let i = 0; i < values.length; i++) set.add(values[i]); + return l => set.has(p(l)); + } + } + + export function and(...ps: Atom.Predicate[]): Atom.Predicate { + switch (ps.length) { + case 0: return P.constant.true; + case 1: return ps[0]; + case 2: { + const a = ps[0], b = ps[1]; + return l => a(l) && b(l); + } + case 3: { + const a = ps[0], b = ps[1], c = ps[2]; + return l => a(l) && b(l) && c(l); + } + case 4: { + const a = ps[0], b = ps[1], c = ps[2], d = ps[3]; + return l => a(l) && b(l) && c(l) && d(l); + } + case 5: { + const a = ps[0], b = ps[1], c = ps[2], d = ps[3], e = ps[4]; + return l => a(l) && b(l) && c(l) && d(l) && e(l); + } + case 6: { + const a = ps[0], b = ps[1], c = ps[2], d = ps[3], e = ps[4], f = ps[5]; + return l => a(l) && b(l) && c(l) && d(l) && e(l) && f(l); + } + default: { + const count = ps.length; + return l => { + for (let i = 0; i < count; i++) if (!ps[i]) return false; + return true; + } + } + } + } + + export function or(...ps: Atom.Predicate[]): Atom.Predicate { + switch (ps.length) { + case 0: return P.constant.true; + case 1: return ps[0]; + case 2: { + const a = ps[0], b = ps[1]; + return l => a(l) || b(l); + } + case 3: { + const a = ps[0], b = ps[1], c = ps[2]; + return l => a(l) || b(l) || c(l); + } + case 4: { + const a = ps[0], b = ps[1], c = ps[2], d = ps[3]; + return l => a(l) || b(l) || c(l) || d(l); + } + case 5: { + const a = ps[0], b = ps[1], c = ps[2], d = ps[3], e = ps[4]; + return l => a(l) || b(l) || c(l) || d(l) || e(l); + } + case 6: { + const a = ps[0], b = ps[1], c = ps[2], d = ps[3], e = ps[4], f = ps[5]; + return l => a(l) || b(l) || c(l) || d(l) || e(l) || f(l); + } + default: { + const count = ps.length; + return l => { + for (let i = 0; i < count; i++) if (ps[i]) return true; + return false; + } + } + } + } +} + +export default Predicates \ No newline at end of file diff --git a/src/mol-model/structure/query/properties.ts b/src/mol-model/structure/query/properties.ts new file mode 100644 index 0000000000000000000000000000000000000000..87cfa8cec12ca175e6dbba9650f2501def27754d --- /dev/null +++ b/src/mol-model/structure/query/properties.ts @@ -0,0 +1,85 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { Atom } from '../structure' + +const constant = { + true: Atom.property(l => true), + false: Atom.property(l => false), + zero: Atom.property(l => 0) +} + +const atom = { + key: Atom.property(l => l.atom), + + // Conformation + x: Atom.property(l => l.unit.x(l.atom)), + y: Atom.property(l => l.unit.y(l.atom)), + z: Atom.property(l => l.unit.z(l.atom)), + id: Atom.property(l => l.unit.conformation.atomId.value(l.atom)), + occupancy: Atom.property(l => l.unit.conformation.occupancy.value(l.atom)), + B_iso_or_equiv: Atom.property(l => l.unit.conformation.B_iso_or_equiv.value(l.atom)), + + // Hierarchy + type_symbol: Atom.property(l => l.unit.hierarchy.atoms.type_symbol.value(l.atom)), + label_atom_id: Atom.property(l => l.unit.hierarchy.atoms.label_atom_id.value(l.atom)), + auth_atom_id: Atom.property(l => l.unit.hierarchy.atoms.auth_atom_id.value(l.atom)), + label_alt_id: Atom.property(l => l.unit.hierarchy.atoms.label_alt_id.value(l.atom)), + pdbx_formal_charge: Atom.property(l => l.unit.hierarchy.atoms.pdbx_formal_charge.value(l.atom)) +} + +const residue = { + key: Atom.property(l => l.unit.hierarchy.residueKey.value(l.unit.residueIndex[l.atom])), + + group_PDB: Atom.property(l => l.unit.hierarchy.residues.group_PDB.value(l.unit.residueIndex[l.atom])), + label_comp_id: Atom.property(l => l.unit.hierarchy.residues.label_comp_id.value(l.unit.residueIndex[l.atom])), + auth_comp_id: Atom.property(l => l.unit.hierarchy.residues.auth_comp_id.value(l.unit.residueIndex[l.atom])), + label_seq_id: Atom.property(l => l.unit.hierarchy.residues.label_seq_id.value(l.unit.residueIndex[l.atom])), + auth_seq_id: Atom.property(l => l.unit.hierarchy.residues.auth_seq_id.value(l.unit.residueIndex[l.atom])), + pdbx_PDB_ins_code: Atom.property(l => l.unit.hierarchy.residues.pdbx_PDB_ins_code.value(l.unit.residueIndex[l.atom])) +} + +const chain = { + key: Atom.property(l => l.unit.hierarchy.chainKey.value(l.unit.chainIndex[l.atom])), + + label_asym_id: Atom.property(l => l.unit.hierarchy.chains.label_asym_id.value(l.unit.chainIndex[l.atom])), + auth_asym_id: Atom.property(l => l.unit.hierarchy.chains.auth_asym_id.value(l.unit.chainIndex[l.atom])), + label_entity_id: Atom.property(l => l.unit.hierarchy.chains.label_entity_id.value(l.unit.chainIndex[l.atom])) +} + +function eK(l: Atom.Location) { return l.unit.hierarchy.entityKey.value(l.unit.chainIndex[l.atom]); } + +const entity = { + key: eK, + + id: Atom.property(l => l.unit.hierarchy.entities.id.value(eK(l))), + type: Atom.property(l => l.unit.hierarchy.entities.type.value(eK(l))), + src_method: Atom.property(l => l.unit.hierarchy.entities.src_method.value(eK(l))), + pdbx_description: Atom.property(l => l.unit.hierarchy.entities.pdbx_description.value(eK(l))), + formula_weight: Atom.property(l => l.unit.hierarchy.entities.formula_weight.value(eK(l))), + pdbx_number_of_molecules: Atom.property(l => l.unit.hierarchy.entities.pdbx_number_of_molecules.value(eK(l))), + details: Atom.property(l => l.unit.hierarchy.entities.details.value(eK(l))), + pdbx_mutation: Atom.property(l => l.unit.hierarchy.entities.pdbx_mutation.value(eK(l))), + pdbx_fragment: Atom.property(l => l.unit.hierarchy.entities.pdbx_fragment.value(eK(l))), + pdbx_ec: Atom.property(l => l.unit.hierarchy.entities.pdbx_ec.value(eK(l))) +} + +const unit = { + operator_name: Atom.property(l => l.unit.operator.name), + model_num: Atom.property(l => l.unit.model.modelNum) +} + +const Properties = { + constant, + atom, + residue, + chain, + entity, + unit +} + +type Properties = typeof Properties +export default Properties \ No newline at end of file diff --git a/src/mol-model/structure/query/query.ts b/src/mol-model/structure/query/query.ts new file mode 100644 index 0000000000000000000000000000000000000000..20c569b6d96b66351c2aca3fb2837f056bb47c49 --- /dev/null +++ b/src/mol-model/structure/query/query.ts @@ -0,0 +1,11 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { Structure } from '../structure' +import Selection from './selection' + +interface Query { (s: Structure): Selection } +export default Query \ No newline at end of file diff --git a/src/mol-model/structure/query/selection.ts b/src/mol-model/structure/query/selection.ts new file mode 100644 index 0000000000000000000000000000000000000000..8504e425237b3761fdb7c3704aef40f3b8fd1432 --- /dev/null +++ b/src/mol-model/structure/query/selection.ts @@ -0,0 +1,124 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import Iterator from 'mol-data/iterator' +import HashSet from 'mol-data/util/hash-set' +import { Structure, Atom, AtomSet } from '../structure' + +type Selection = + | Structure // each atom is interpreted as a singleton structure + | Structure[] + +namespace Selection { + export const Empty: Selection = []; + + function isStructure(x: Selection): x is Structure { return !!(x as Structure).units && !!(x as Structure).atoms; } + + export function structureCount(sel: Selection) { + if (isStructure(sel)) return AtomSet.atomCount(sel.atoms); + return sel.length; + } + + export function union(sel: Selection): Structure { + if (isStructure(sel)) return sel; + if (!sel.length) return Structure.Empty; + const sets = []; + for (let i = 0, _i = sel.length; i < _i; i++) sets[sets.length] = sel[i].atoms; + return Structure.create(unionUnits(sel), AtomSet.unionMany(sets)); + } + + export function structures(sel: Selection): Iterator<Structure> { + if (isStructure(sel)) { + const units = sel.units; + return Iterator.map<Atom, Structure>(AtomSet.atoms(sel.atoms), atoms => Structure.create(units, atoms)); + } + return Iterator.Array(sel); + } + + export function getAt(sel: Selection, i: number): Structure { + if (isStructure(sel)) { + return Structure.create(sel.units, AtomSet.atomGetAt(sel.atoms, i)); + } + return sel[i]; + } + + export interface Builder { + add(s: Structure): void, + getSelection(): Selection + } + + class LinearBuilderImpl implements Builder { + private structures: Structure[] = []; + private allSingletons = true; + + add(s: Structure) { + const atomCount = AtomSet.atomCount(s.atoms); + if (atomCount === 0) return; + this.structures[this.structures.length] = s; + if (atomCount !== 1) this.allSingletons = false; + } + + getSelection() { + const len = this.structures.length; + if (len === 0) return Empty; + if (len === 1) return this.structures[0]; + if (this.allSingletons) return union(this.structures); + return this.structures; + } + + constructor() { } + } + + class HashBuilderImpl implements Builder { + private structures: Structure[] = []; + private allSingletons = true; + private sets = HashSet(AtomSet.hashCode, AtomSet.areEqual); + + add(s: Structure) { + const atomCount = AtomSet.atomCount(s.atoms); + if (atomCount === 0 || !this.sets.add(s.atoms)) return; + this.structures[this.structures.length] = s; + if (atomCount !== 1) this.allSingletons = false; + } + + getSelection() { + const len = this.structures.length; + if (len === 0) return Empty; + if (len === 1) return this.structures[0]; + if (this.allSingletons) return union(this.structures); + return this.structures; + } + + constructor() { } + } + + export function LinearBuilder(): Builder { return new LinearBuilderImpl(); } + export function UniqueBuilder(): Builder { return new HashBuilderImpl(); } + + // TODO: spatial lookup +} + +export default Selection + +function unionUnits(xs: Structure[]): Structure['units'] { + let prev = xs[0].units; + let sameModel = true; + for (let i = 1, _i = xs.length; i < _i; i++) { + if (xs[i].units !== prev) sameModel = false; + } + if (sameModel) return prev; + + let ret: any = { ...prev }; + for (let i = 1, _i = xs.length; i < _i; i++) { + const units = xs[i].units; + if (units !== prev) { + const keys = Object.keys(units); + for (let j = 0; j < keys.length; j++) ret[keys[j]] = (units as any)[keys[j]]; + } + prev = xs[i]; + } + return ret; +} diff --git a/src/mol-model/structure/structure.ts b/src/mol-model/structure/structure.ts new file mode 100644 index 0000000000000000000000000000000000000000..4fe835cfaffe28eeb60b503fc14b6e713f2074b5 --- /dev/null +++ b/src/mol-model/structure/structure.ts @@ -0,0 +1,12 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import Atom from './structure/atom' +import AtomSet from './structure/atom/set' +import Structure from './structure/structure' +import Unit from './structure/unit' + +export { Atom, AtomSet, Structure, Unit } \ No newline at end of file diff --git a/src/mol-model/structure/structure/atom.ts b/src/mol-model/structure/structure/atom.ts new file mode 100644 index 0000000000000000000000000000000000000000..217bbb1eb581c5d869c3c91f70d100e8c598fa8f --- /dev/null +++ b/src/mol-model/structure/structure/atom.ts @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { Tuple } from 'mol-data/int' +import Unit from './unit' +import Structure from './structure' + +/** Atom pointer */ +interface Atom { '@type': Tuple['@type'] } + +namespace Atom { + export const Zero: Atom = Tuple.Zero; + export const create: (unit: number, index: number) => Atom = Tuple.create; + export const is: (x: any) => x is Atom = Tuple.is; + export const unit: (a: Atom) => number = Tuple.fst; + export const index: (a: Atom) => number = Tuple.snd; + export const areEqual: (a: Atom, b: Atom) => boolean = Tuple.areEqual; + export const hashCode: (a: Atom) => number = Tuple.hashCode; + + export function createEmptyArray(n: number): Atom[] { return new Float64Array(n) as any; } + + /** All the information required to access atom properties */ + export interface Location { unit: Unit, atom: number } + export function Location(): Location { return { unit: {} as any, atom: 0 }; } + export interface Property<T> { (location: Location): T } + export interface Predicate extends Property<boolean> { } + + export function updateLocation(structure: Structure, l: Location, atom: Atom) { + l.unit = structure.units[unit(atom)]; + l.atom = index(atom); + return l; + } + + export function property<T>(p: Property<T>) { return p; } +} + +export default Atom \ No newline at end of file diff --git a/src/mol-model/structure/structure/atom/set.ts b/src/mol-model/structure/structure/atom/set.ts new file mode 100644 index 0000000000000000000000000000000000000000..3ce250e2477c195d6fa10ed29b12e7dd21c06371 --- /dev/null +++ b/src/mol-model/structure/structure/atom/set.ts @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { OrderedSet, SortedArray, Iterator } from 'mol-data/int' +import Atom from '../atom' +import * as Impl from './set/impl' +import createBuilder, { Builder as AtomSetBuilder } from './set/builder' + +/** A map-like representation of grouped atom set */ +namespace AtomSet { + export const Empty: AtomSet = Impl.Empty as any; + + export const create: (data: Atom | ArrayLike<Atom> | { [unitId: number]: OrderedSet }) => AtomSet = Impl.create as any; + + export const unitCount: (set: AtomSet) => number = Impl.keyCount as any; + export const unitIds: (set: AtomSet) => SortedArray = Impl.getKeys as any; + export const unitHas: (set: AtomSet, id: number) => boolean = Impl.hasKey as any; + export const unitGetId: (set: AtomSet, i: number) => number = Impl.getKey as any; + + export const unitGetById: (set: AtomSet, key: number) => OrderedSet = Impl.getByKey as any; + export const unitGetByIndex: (set: AtomSet, i: number) => OrderedSet = Impl.getByIndex as any; + + export const atomCount: (set: AtomSet) => number = Impl.size as any; + export const atomHas: (set: AtomSet, x: Atom) => boolean = Impl.hasAtom as any; + export const atomIndexOf: (set: AtomSet, x: Atom) => number = Impl.indexOf as any; + export const atomGetAt: (set: AtomSet, i: number) => Atom = Impl.getAt as any; + export const atoms: (set: AtomSet) => Iterator<Atom> = Impl.values as any; + + export const hashCode: (set: AtomSet) => number = Impl.hashCode as any; + export const areEqual: (a: AtomSet, b: AtomSet) => boolean = Impl.areEqual as any; + export const areIntersecting: (a: AtomSet, b: AtomSet) => boolean = Impl.areIntersecting as any; + + export const union: (a: AtomSet, b: AtomSet) => AtomSet = Impl.union as any; + export const unionMany: (sets: AtomSet[]) => AtomSet = Impl.unionMany as any; + export const intersect: (a: AtomSet, b: AtomSet) => AtomSet = Impl.intersect as any; + export const subtract: (a: AtomSet, b: AtomSet) => AtomSet = Impl.subtract as any; + + export type Builder = AtomSetBuilder + export function LinearBuilder(parent: AtomSet): Builder { return createBuilder(parent, true); } + export function UnsortedBuilder(parent: AtomSet): Builder { return createBuilder(parent, false); } + + // TODO: bounding sphere + // TODO: distance, areWithIn? + // TODO: check connected + // TODO: add "parent" property? how to avoid using too much memory? Transitive parents? Parent unlinking? +} + +interface AtomSet { '@type': 'atom-set' | Atom['@type'] } + +export default AtomSet \ No newline at end of file diff --git a/src/mol-model/structure/structure/atom/set/builder.ts b/src/mol-model/structure/structure/atom/set/builder.ts new file mode 100644 index 0000000000000000000000000000000000000000..fdda1a1c5c0d658c19f93d84953122abd276a08d --- /dev/null +++ b/src/mol-model/structure/structure/atom/set/builder.ts @@ -0,0 +1,70 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import AtomSet from '../set' +import Atom from '../../atom' +import { OrderedSet } from 'mol-data/int' +import { sortArray } from 'mol-data/util/sort' + +export class Builder { + private keys: number[] = []; + private units: number[][] = Object.create(null); + private currentUnit: number[] = []; + + atomCount = 0; + + add(u: number, a: number) { + const unit = this.units[u]; + if (!!unit) { unit[unit.length] = a; } + else { + this.units[u] = [a]; + this.keys[this.keys.length] = u; + } + this.atomCount++; + } + + beginUnit() { this.currentUnit = this.currentUnit.length > 0 ? [] : this.currentUnit; } + addToUnit(a: number) { this.currentUnit[this.currentUnit.length] = a; this.atomCount++; } + commitUnit(u: number) { + if (this.currentUnit.length === 0) return; + this.keys[this.keys.length] = u; + this.units[u] = this.currentUnit; + } + + getSet(): AtomSet { + const sets: { [key: number]: OrderedSet } = Object.create(null); + + let allEqual = this.keys.length === AtomSet.unitCount(this.parent); + + for (let i = 0, _i = this.keys.length; i < _i; i++) { + const k = this.keys[i]; + const unit = this.units[k]; + const l = unit.length; + if (!this.sorted && l > 1) sortArray(unit); + + const set = l === 1 ? OrderedSet.ofSingleton(unit[0]) : OrderedSet.ofSortedArray(unit); + const parentSet = AtomSet.unitGetById(this.parent, k); + if (OrderedSet.areEqual(set, parentSet)) { + sets[k] = parentSet; + } else { + sets[k] = set; + allEqual = false; + } + } + return allEqual ? this.parent : AtomSet.create(sets); + } + + singleton(): Atom { + const u = this.keys[0]; + return Atom.create(u, this.units[u][0]); + } + + constructor(private parent: AtomSet, private sorted: boolean) { } +} + +export default function createBuilder(parent: AtomSet, sorted: boolean) { + return new Builder(parent, sorted); +} \ No newline at end of file diff --git a/src/mol-model/structure/structure/atom/set/impl.ts b/src/mol-model/structure/structure/atom/set/impl.ts new file mode 100644 index 0000000000000000000000000000000000000000..f60658f1f7ee283061f8cdd662811d714efdc804 --- /dev/null +++ b/src/mol-model/structure/structure/atom/set/impl.ts @@ -0,0 +1,497 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { SortedArray, Interval, Iterator, OrderedSet } from 'mol-data/int' +import { sortArray } from 'mol-data/util/sort' +import { hash1 } from 'mol-data/util/hash-functions' +import Atom from '../../atom' + +/** Long and painful implementation starts here */ + +export interface AtomSetElements { [id: number]: OrderedSet, offsets: number[], hashCode: number, keys: SortedArray } +export type AtomSetImpl = Atom | AtomSetElements + +export const Empty: AtomSetImpl = { offsets: [0], hashCode: 0, keys: SortedArray.Empty }; + +export function create(data: Atom | ArrayLike<Atom> | { [id: number]: OrderedSet }): AtomSetImpl { + if (typeof data === 'number' || Atom.is(data)) return data; + if (isArrayLike(data)) return ofAtoms(data); + return ofObject(data as { [id: number]: OrderedSet }); +} + +export function isSingleton(set: AtomSetImpl) { + return typeof set === 'number'; +} + +export function getKeys(set: AtomSetImpl): SortedArray { + if (typeof set === 'number') return SortedArray.ofSingleton(set); + return (set as AtomSetElements).keys; +} + +export function keyCount(set: AtomSetImpl): number { + if (typeof set === 'number') return 1; + return (set as AtomSetElements).keys.length; +} + +export function hasKey(set: AtomSetImpl, key: number): boolean { + if (typeof set === 'number') return Atom.unit(set) === key; + return !!(set as AtomSetElements)[key] +} + +export function getKey(set: AtomSetImpl, index: number): number { + if (typeof set === 'number') return Atom.unit(set); + return (set as AtomSetElements).keys[index]; +} + +export function hasAtom(set: AtomSetImpl, t: Atom): boolean { + if (typeof set === 'number') return Atom.areEqual(t, set); + const os = (set as AtomSetElements)[Atom.unit(t)]; + return !!os && OrderedSet.has(os, Atom.index(t)); +} + +export function getByKey(set: AtomSetImpl, key: number): OrderedSet { + if (typeof set === 'number') { + return Atom.unit(set) === key ? OrderedSet.ofSingleton(Atom.index(set)) : OrderedSet.Empty; + } + return (set as AtomSetElements)[key] || OrderedSet.Empty; +} + +export function getByIndex(set: AtomSetImpl, index: number): OrderedSet { + if (typeof set === 'number') return index === 0 ? OrderedSet.ofSingleton(Atom.index(set)) : OrderedSet.Empty; + const key = (set as AtomSetElements).keys[index]; + return (set as AtomSetElements)[key] || OrderedSet.Empty; +} + +export function getAt(set: AtomSetImpl, i: number): Atom { + if (typeof set === 'number') return set; + return getAtE(set as AtomSetElements, i); +} + +export function indexOf(set: AtomSetImpl, t: Atom) { + if (typeof set === 'number') return Atom.areEqual(set, t) ? 0 : -1; + return indexOfE(set as AtomSetElements, t); +} + +/** Number elements in the "child" sets */ +export function size(set: AtomSetImpl) { + if (typeof set === 'number') return 1; + return (set as AtomSetElements).offsets[(set as AtomSetElements).offsets.length - 1]; +} + +export function hashCode(set: AtomSetImpl) { + if (typeof set === 'number') return Atom.hashCode(set); + if ((set as AtomSetElements).hashCode !== -1) return (set as AtomSetElements).hashCode; + return computeHash((set as AtomSetElements)); +} + +export function areEqual(a: AtomSetImpl, b: AtomSetImpl) { + if (typeof a === 'number') { + if (typeof b === 'number') return Atom.areEqual(a, b); + return false; + } + if (typeof b === 'number') return false; + return areEqualEE(a as AtomSetElements, b as AtomSetElements); +} + +export function areIntersecting(a: AtomSetImpl, b: AtomSetImpl) { + if (typeof a === 'number') { + if (typeof b === 'number') return Atom.areEqual(a, b); + return areIntersectingNE(a, b as AtomSetElements); + } + if (typeof b === 'number') return areIntersectingNE(b, a as AtomSetElements); + return areIntersectingEE(a as AtomSetElements, b as AtomSetElements); +} + +export function intersect(a: AtomSetImpl, b: AtomSetImpl) { + if (typeof a === 'number') { + if (typeof b === 'number') return Atom.areEqual(a, b) ? a : Empty; + return intersectNE(a, b as AtomSetElements); + } + if (typeof b === 'number') return intersectNE(b, a as AtomSetElements); + return intersectEE(a as AtomSetElements, b as AtomSetElements); +} + +export function subtract(a: AtomSetImpl, b: AtomSetImpl) { + if (typeof a === 'number') { + if (typeof b === 'number') return Atom.areEqual(a, b) ? Empty : a; + return subtractNE(a, b as AtomSetElements); + } + if (typeof b === 'number') return subtractEN(a as AtomSetElements, b); + return subtractEE(a as AtomSetElements, b as AtomSetElements); +} + +export function union(a: AtomSetImpl, b: AtomSetImpl) { + return findUnion([a, b]); +} + +export function unionMany(sets: ArrayLike<AtomSetImpl>) { + return findUnion(sets); +} + +class ElementsIterator implements Iterator<Atom> { + private unit: number = 0; + private keyCount: number; + private setIndex = -1; + private currentIndex = 0; + private currentSize = 0; + private currentSet: OrderedSet = OrderedSet.Empty; + + hasNext: boolean = false; + + move() { + if (!this.hasNext) return Atom.Zero; + const ret = Atom.create(this.unit, OrderedSet.getAt(this.currentSet, this.currentIndex++)); + if (this.currentIndex >= this.currentSize) this.advance(); + return ret; + } + + private advance() { + if (++this.setIndex >= this.keyCount) { + this.hasNext = false; + return false; + } + this.unit = this.elements.keys[this.setIndex]; + this.currentSet = this.elements[this.unit]; + this.currentIndex = 0; + this.currentSize = OrderedSet.size(this.currentSet); + return true; + } + + constructor(private elements: AtomSetElements) { + this.keyCount = elements.keys.length; + this.hasNext = this.keyCount > 0; + this.advance(); + } +} + +export function values(set: AtomSetImpl): Iterator<Atom> { + if (typeof set === 'number') return Iterator.Value(set as Atom); + return new ElementsIterator(set as AtomSetElements); +} + +function isArrayLike(x: any): x is ArrayLike<Atom> { + return x && (typeof x.length === 'number' && (Array.isArray(x) || !!x.buffer)); +} + +function ofObject(data: { [id: number]: OrderedSet }) { + const keys = []; + + const _keys = Object.keys(data); + for (let i = 0, _i = _keys.length; i < _i; i++) { + const k = +_keys[i]; + if (OrderedSet.size(data[k]) > 0) keys[keys.length] = k; + } + if (!keys.length) return Empty; + if (keys.length === 1) { + const set = data[keys[0]]; + if (OrderedSet.size(set) === 1) return Atom.create(keys[0], OrderedSet.getAt(set, 0)); + } + return ofObject1(keys, data); +} + +function ofObject1(keys: number[], data: { [id: number]: OrderedSet }) { + if (keys.length === 1) { + const k = keys[0]; + const set = data[k]; + if (OrderedSet.size(set) === 1) return Atom.create(k, OrderedSet.getAt(set, 0)); + } + sortArray(keys); + return _createObjectOrdered(SortedArray.ofSortedArray(keys), data); +} + +function ofObjectOrdered(keys: SortedArray, data: { [id: number]: OrderedSet }) { + if (keys.length === 1) { + const k = keys[0]; + const set = data[k]; + if (OrderedSet.size(set) === 1) return Atom.create(k, OrderedSet.getAt(set, 0)); + } + return _createObjectOrdered(keys, data); +} + +function _createObjectOrdered(keys: SortedArray, data: { [id: number]: OrderedSet }) { + const ret: AtomSetElements = Object.create(null); + ret.keys = keys; + const offsets = [0]; + let runningSize = 0; + for (let i = 0, _i = keys.length; i < _i; i++) { + const k = keys[i]; + const set = data[k]; + ret[k] = set; + runningSize += OrderedSet.size(set); + offsets[offsets.length] = runningSize; + } + ret.offsets = offsets; + ret.hashCode = -1; + return ret; +} + +function getUniqueElements(xs: number[]) { + let count = 1; + for (let i = 1, _i = xs.length; i < _i; i++) { + if (xs[i - 1] !== xs[i]) count++; + } + const ret = new (xs as any).constructor(count); + ret[0] = xs[0]; + let offset = 1; + for (let i = 1, _i = xs.length; i < _i; i++) { + if (xs[i - 1] !== xs[i]) ret[offset++] = xs[i]; + } + return ret; +} + +function normalizeArray(xs: number[]) { + sortArray(xs); + for (let i = 1, _i = xs.length; i < _i; i++) { + if (xs[i - 1] === xs[i]) return getUniqueElements(xs); + } + return xs; +} + +function ofAtoms(xs: ArrayLike<Atom>) { + if (xs.length === 0) return Empty; + const sets: { [key: number]: number[] } = Object.create(null); + for (let i = 0, _i = xs.length; i < _i; i++) { + const x = xs[i]; + const u = Atom.unit(x), v = Atom.index(x); + const set = sets[u]; + if (set) set[set.length] = v; + else sets[u] = [v]; + } + const ret: { [key: number]: OrderedSet } = Object.create(null); + const keys = []; + const _keys = Object.keys(sets); + for (let i = 0, _i = _keys.length; i < _i; i++) { + const k = +_keys[i]; + keys[keys.length] = k; + ret[k] = OrderedSet.ofSortedArray(normalizeArray(sets[k])); + } + return ofObject1(keys, ret); +} + +function getOffsetIndex(xs: ArrayLike<number>, value: number) { + let min = 0, max = xs.length - 1; + while (min < max) { + const mid = (min + max) >> 1; + const v = xs[mid]; + if (value < v) max = mid - 1; + else if (value > v) min = mid + 1; + else return mid; + } + if (min > max) { + return max; + } + return value < xs[min] ? min - 1 : min; +} + +function getAtE(set: AtomSetElements, i: number): Atom { + const { offsets, keys } = set; + const o = getOffsetIndex(offsets, i); + if (o >= offsets.length - 1) return 0 as any; + const k = keys[o]; + const e = OrderedSet.getAt(set[k], i - offsets[o]); + return Atom.create(k, e); +} + +function indexOfE(set: AtomSetElements, t: Atom) { + const { keys } = set; + const u = Atom.unit(t); + const setIdx = SortedArray.indexOf(keys, u); + if (setIdx < 0) return -1; + const o = OrderedSet.indexOf(set[u], Atom.index(t)); + if (o < 0) return -1; + return set.offsets[setIdx] + o; +} + +function computeHash(set: AtomSetElements) { + const { keys } = set; + let hash = 23; + for (let i = 0, _i = keys.length; i < _i; i++) { + const k = keys[i]; + hash = (31 * hash + k) | 0; + hash = (31 * hash + OrderedSet.hashCode(set[k])) | 0; + } + hash = (31 * hash + size(set)) | 0; + hash = hash1(hash); + set.hashCode = hash; + return hash; +} + +function areEqualEE(a: AtomSetElements, b: AtomSetElements) { + if (a === b) return true; + if (size(a) !== size(a)) return false; + + const keys = a.keys; + if (!SortedArray.areEqual(keys, b.keys)) return false; + for (let i = 0, _i = keys.length; i < _i; i++) { + const k = keys[i]; + if (!OrderedSet.areEqual(a[k], b[k])) return false; + } + return true; +} + +function areIntersectingNE(a: Atom, b: AtomSetElements) { + const u = Atom.unit(a); + return SortedArray.has(b.keys, u) && OrderedSet.has(b[u], Atom.index(a)); +} + +function areIntersectingEE(a: AtomSetElements, b: AtomSetElements) { + if (a === b) return true; + const keysA = a.keys, keysB = b.keys; + if (!SortedArray.areIntersecting(a.keys, b.keys)) return false; + const r = SortedArray.findRange(keysA, SortedArray.min(keysB), SortedArray.max(keysB)); + const start = Interval.start(r), end = Interval.end(r); + for (let i = start; i < end; i++) { + const k = keysA[i]; + const ak = a[k], bk = b[k]; + if (!!ak && !!bk && OrderedSet.areIntersecting(ak, bk)) return true; + } + return false; +} + +function intersectNE(a: Atom, b: AtomSetElements) { + const u = Atom.unit(a); + return !!b[u] && OrderedSet.has(b[u], Atom.index(a)) ? a : Empty; +} + +function intersectEE(a: AtomSetElements, b: AtomSetElements) { + if (a === b) return a; + + const keysA = a.keys, keysB = b.keys; + if (!SortedArray.areIntersecting(a.keys, b.keys)) return Empty; + const r = SortedArray.findRange(keysA, SortedArray.min(keysB), SortedArray.max(keysB)); + const start = Interval.start(r), end = Interval.end(r); + + const keys = [], ret = Object.create(null); + for (let i = start; i < end; i++) { + const k = keysA[i]; + const bk = b[k]; + if (!bk) continue; + const intersection = OrderedSet.intersect(a[k], b[k]); + if (OrderedSet.size(intersection) > 0) { + keys[keys.length] = k; + ret[k] = intersection; + } + } + return ofObjectOrdered(SortedArray.ofSortedArray(keys), ret); +} + +function subtractNE(a: Atom, b: AtomSetElements) { + return hasAtom(b, a) ? Empty : a; +} + +function subtractEN(a: AtomSetElements, b: Atom): AtomSetImpl { + if (!hasAtom(a, b)) return a; + + const u = Atom.unit(b), v = Atom.index(b); + const set = a[u]; + if (OrderedSet.size(set) === 1) { + // remove the entire unit. + return ofObjectOrdered(SortedArray.subtract(a.keys, SortedArray.ofSingleton(u)), a); + } else { + const ret: { [key: number]: OrderedSet } = Object.create(null); + for (let i = 0, _i = a.keys.length; i < _i; i++) { + const k = a.keys[i]; + if (k === u) { + ret[k] = OrderedSet.subtract(set, OrderedSet.ofSingleton(v)); + } else ret[k] = a[k]; + } + return ofObjectOrdered(a.keys, ret); + } +} + +function subtractEE(a: AtomSetElements, b: AtomSetElements) { + if (a === b) return Empty; + + const keysA = a.keys, keysB = b.keys; + if (!SortedArray.areIntersecting(keysA, keysB)) return a; + const r = SortedArray.findRange(keysA, SortedArray.min(keysB), SortedArray.max(keysB)); + const start = Interval.start(r), end = Interval.end(r); + + const keys = [], ret = Object.create(null); + for (let i = 0; i < start; i++) { + const k = keysA[i]; + keys[keys.length] = k; + ret[k] = a[k]; + } + for (let i = start; i < end; i++) { + const k = keysA[i]; + const ak = a[k], bk = b[k]; + if (!!bk) { + const subtraction = OrderedSet.subtract(ak, bk); + if (OrderedSet.size(subtraction) > 0) { + keys[keys.length] = k; + ret[k] = subtraction; + } + } else { + keys[keys.length] = k; + ret[k] = a[k]; + } + } + for (let i = end, _i = keysA.length; i < _i; i++) { + const k = keysA[i]; + keys[keys.length] = k; + ret[k] = a[k]; + } + return ofObjectOrdered(SortedArray.ofSortedArray(keys), ret); +} + +function findUnion(sets: ArrayLike<AtomSetImpl>) { + if (!sets.length) return Empty; + if (sets.length === 1) return sets[0]; + if (sets.length === 2 && areEqual(sets[0], sets[1])) return sets[0]; + + const eCount = { count: 0 }; + const ns = unionN(sets, eCount); + if (!eCount.count) return ns; + const ret = Object.create(null); + for (let i = 0, _i = sets.length; i < _i; i++) { + const s = sets[i]; + if (typeof s !== 'number') unionInto(ret, s as AtomSetElements); + } + if (size(ns as AtomSetImpl) > 0) { + if (typeof ns === 'number') unionIntoN(ret, ns as any); + else unionInto(ret, ns as AtomSetElements); + } + return ofObject(ret); +} + +function unionN(sets: ArrayLike<AtomSetImpl>, eCount: { count: number }) { + let countN = 0, countE = 0; + for (let i = 0, _i = sets.length; i < _i; i++) { + if (typeof sets[i] === 'number') countN++; + else countE++; + } + eCount.count = countE; + if (!countN) return Empty; + if (countN === sets.length) return ofAtoms(sets as ArrayLike<Atom>); + const packed = Atom.createEmptyArray(countN); + let offset = 0; + for (let i = 0, _i = sets.length; i < _i; i++) { + const s = sets[i]; + if (typeof s === 'number') packed[offset++] = s; + } + return ofAtoms(packed as any); +} + +function unionInto(data: { [key: number]: OrderedSet }, a: AtomSetElements) { + const keys = a.keys; + for (let i = 0, _i = keys.length; i < _i; i++) { + const k = keys[i]; + const set = data[k]; + if (set) data[k] = OrderedSet.union(set, a[k]); + else data[k] = a[k]; + } +} + +function unionIntoN(data: { [key: number]: OrderedSet }, a: Atom) { + const u = Atom.unit(a); + const set = data[u]; + if (set) { + data[u] = OrderedSet.union(set, OrderedSet.ofSingleton(Atom.index(a))); + } else { + data[u] = OrderedSet.ofSingleton(Atom.index(a)); + } +} \ No newline at end of file diff --git a/src/mol-model/structure/structure/atom/set/properties.ts b/src/mol-model/structure/structure/atom/set/properties.ts new file mode 100644 index 0000000000000000000000000000000000000000..03ae27d163b402e0026bcfefd54bd706739187a0 --- /dev/null +++ b/src/mol-model/structure/structure/atom/set/properties.ts @@ -0,0 +1,10 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +// TODO: bounding sphere +// TODO: distance, areWithIn? +// TODO: check connected +// TODO: add "parent" property? how to avoid using too much memory? Transitive parents? Parent unlinking? \ No newline at end of file diff --git a/src/mol-model/structure/structure/structure.ts b/src/mol-model/structure/structure/structure.ts new file mode 100644 index 0000000000000000000000000000000000000000..0d262041252f50b998b7465ce7625921028560c0 --- /dev/null +++ b/src/mol-model/structure/structure/structure.ts @@ -0,0 +1,85 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { OrderedSet, Iterator } from 'mol-data/int' +import UniqueArray from 'mol-data/util/unique-array' +import SymmetryOperator from 'mol-math/geometry/symmetry-operator' +import { Model, Format } from '../model' +import Unit from './unit' +import AtomSet from './atom/set' +import Atom from './atom' + + +interface Structure extends Readonly<{ + units: { readonly [id: number]: Unit }, + atoms: AtomSet +}> { } + +namespace Structure { + export const Empty = { units: {}, atoms: AtomSet.Empty }; + + export function create(units: Structure['units'], atoms: AtomSet): Structure { + return { units, atoms }; + } + + export function ofData(format: Format) { + const models = Model.create(format); + return models.map(ofModel); + } + + export function ofModel(model: Model): Structure { + const chains = model.hierarchy.chainSegments; + const builder = Builder(); + + for (let c = 0; c < chains.count; c++) { + const unit = Unit.create(model, SymmetryOperator.Default); + builder.addUnit(unit); + builder.addAtoms(unit.id, OrderedSet.ofBounds(chains.segments[c], chains.segments[c + 1])); + } + + return builder.getStructure(); + } + + export interface Builder { + addUnit(unit: Unit): void, + addAtoms(unitId: number, atoms: OrderedSet): void, + getStructure(): Structure, + readonly atomCount: number + } + + class BuilderImpl implements Builder { + private units = Object.create(null); + private atoms = Object.create(null); + atomCount = 0; + + addUnit(unit: Unit) { this.units[unit.id] = unit; } + addAtoms(unitId: number, atoms: OrderedSet) { this.atoms[unitId] = atoms; this.atomCount += OrderedSet.size(atoms); } + getStructure(): Structure { return this.atomCount > 0 ? Structure.create(this.units, AtomSet.create(this.atoms)) : Empty; } + } + + export function Builder(): Builder { return new BuilderImpl(); } + + /** Transient = location gets overwritten when move() is called. */ + export function atomLocationsTransient(s: Structure): Iterator<Atom.Location> { + const l = Atom.Location(); + const update = Atom.updateLocation; + return Iterator.map(AtomSet.atoms(s.atoms), a => update(s, l, a)); + } + + export function getModels(s: Structure) { + const arr = UniqueArray.create<Model['id'], Model>(); + for (const k of Object.keys(s.units)) { + const u = s.units[+k]; + UniqueArray.add(arr, u.model.id, u.model); + } + return arr.array; + } + + // TODO: "lift" atom set operators? + // TODO: "diff" +} + +export default Structure \ No newline at end of file diff --git a/src/mol-model/structure/structure/unit.ts b/src/mol-model/structure/structure/unit.ts new file mode 100644 index 0000000000000000000000000000000000000000..be83164a38f62a4d8dc99d4c190aa27d04c58343 --- /dev/null +++ b/src/mol-model/structure/structure/unit.ts @@ -0,0 +1,57 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import SymmetryOperator from 'mol-math/geometry/symmetry-operator' +import { Model } from '../model' + +interface Unit extends SymmetryOperator.ArrayMapping { + // Structure-level unique identifier of the unit. + readonly id: number, + + // Provides access to the underlying data. + readonly model: Model, + + // Determines the operation applied to this unit. + // The transform and and inverse are baked into the "getPosition" function + readonly operator: SymmetryOperator, + + // Reference some commonly accessed things for faster access. + readonly residueIndex: ArrayLike<number>, + readonly chainIndex: ArrayLike<number>, + readonly hierarchy: Model['hierarchy'], + readonly conformation: Model['conformation'] + + // TODO: add velocity? +} + +namespace Unit { + export function create(model: Model, operator: SymmetryOperator): Unit { + const h = model.hierarchy; + const { invariantPosition, position, x, y, z } = SymmetryOperator.createMapping(operator, model.conformation); + + return { + id: nextUnitId(), + model, + operator, + residueIndex: h.residueSegments.segmentMap, + chainIndex: h.chainSegments.segmentMap, + hierarchy: model.hierarchy, + conformation: model.conformation, + invariantPosition, + position, + x, y, z + }; + } +} + +export default Unit; + +let _id = 0; +function nextUnitId() { + const ret = _id; + _id = (_id + 1) % 0x3fffffff; + return ret; +} \ No newline at end of file diff --git a/src/mol-model/volume/TODO b/src/mol-model/volume/TODO new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/mol-util/_spec/string-builder.spec.ts b/src/mol-util/_spec/string-builder.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..bd0feb28b2d1fe634cca714740fe88c2b2a4e35d --- /dev/null +++ b/src/mol-util/_spec/string-builder.spec.ts @@ -0,0 +1,36 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import SB from '../string-builder' + +describe('string-builder', () => { + + function check(name: string, bb: (sb: SB) => void, expected: string) { + const sb = SB.create(); + bb(sb); + it(name, () => expect(SB.getString(sb)).toEqual(expected)); + } + + check('write', sb => SB.write(sb, '123'), '123'); + check('whitespace', sb => SB.whitespace(sb, 3), ' '); + check('writePadLeft', sb => SB.writePadLeft(sb, '1', 3), ' 1'); + check('writePadRight', sb => SB.writePadRight(sb, '1', 3), '1 '); + check('writeIntegerPadLeft', sb => SB.writeIntegerPadLeft(sb, -125, 5), ' -125'); + check('writeIntegerPadRight', sb => SB.writeIntegerPadRight(sb, -125, 5), '-125 '); + check('writeFloat', sb => SB.writeFloat(sb, 1.123, 100), '1.12'); + check('writeFloatPadLeft', sb => SB.writeFloatPadLeft(sb, 1.123, 100, 6), ' 1.12'); + check('writeFloatPadRight', sb => SB.writeFloatPadRight(sb, -1.123, 100, 6), '-1.12 '); + + it('chunks', () => { + const sb = SB.create(2); + SB.write(sb, '1'); + SB.write(sb, '2'); + SB.write(sb, '3'); + + expect(SB.getChunks(sb)).toEqual(['12', '3']); + expect(SB.getString(sb)).toEqual('123'); + }) +}); \ No newline at end of file diff --git a/src/mol-util/bit-flags.ts b/src/mol-util/bit-flags.ts new file mode 100644 index 0000000000000000000000000000000000000000..0cf3b761d5c4297081fab1eb9cd7c340e80fe99b --- /dev/null +++ b/src/mol-util/bit-flags.ts @@ -0,0 +1,17 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +interface BitFlags<Flags> { '@type': Flags } + +namespace BitFlags { + export function create<F>(flags: F): BitFlags<F> { return flags as any; } + + export function has<F>(flags: BitFlags<F>, flag: F) { return ((flags as any) & (flag as any)) !== 0; } + // toCheck must be non-zero + export function hasAll<F>(flags: BitFlags<F>, toCheck: BitFlags<F>) { return !!toCheck && ((flags as any) & (toCheck as any)) === (toCheck as any); } +} + +export default BitFlags \ No newline at end of file diff --git a/src/utils/computation.ts b/src/mol-util/computation.ts similarity index 92% rename from src/utils/computation.ts rename to src/mol-util/computation.ts index efaade35521c6e045065c311e7986ea8d775c771..6bafb7a99b7159cc6fa0edc65554b86ab32e8458 100644 --- a/src/utils/computation.ts +++ b/src/mol-util/computation.ts @@ -1,11 +1,12 @@ /** - * Copyright (c) 2017 molio contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. * * Adapted from https://github.com/dsehnal/LiteMol * @author David Sehnal <david.sehnal@gmail.com> */ import Scheduler from './scheduler' +import timeNow from './time' interface Computation<A> { (ctx?: Computation.Context): Promise<A> @@ -79,22 +80,7 @@ namespace Computation { return ret; } - declare var process: any; - declare var window: any; - - export const now: () => number = (function () { - if (typeof window !== 'undefined' && window.performance) { - const perf = window.performance; - return () => perf.now(); - } else if (typeof process !== 'undefined' && process.hrtime !== 'undefined') { - return () => { - let t = process.hrtime(); - return t[0] * 1000 + t[1] / 1000000; - }; - } else { - return () => +new Date(); - } - }()); + export const now = timeNow; /** A utility for splitting large computations into smaller parts. */ export interface Chunker { @@ -195,7 +181,9 @@ class ObservableContext implements Computation.Context { if (this.observers) { const p = { ...this.progress }; - for (const o of this.observers) Scheduler.immediate(o, p); + for (let i = 0, _i = this.observers.length; i < _i; i++) { + Scheduler.immediate(this.observers[i], p); + } } this.lastDelta = time - this.lastUpdated; diff --git a/src/mol-util/index.ts b/src/mol-util/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..18a0aa3cd809aab11fc83dfe015e3d99cf98c3f8 --- /dev/null +++ b/src/mol-util/index.ts @@ -0,0 +1,14 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import BitFlags from './bit-flags' +import Computation from './computation' +import Scheduler from './scheduler' +import StringBuilder from './string-builder' +import Time from './time' +import UUID from './uuid' + +export { BitFlags, Computation, Scheduler, StringBuilder, Time, UUID } \ No newline at end of file diff --git a/src/utils/scheduler.ts b/src/mol-util/scheduler.ts similarity index 98% rename from src/utils/scheduler.ts rename to src/mol-util/scheduler.ts index d061959047740ce72126413192885c5546153f9a..85ad75eff32fcfe5e464aa876a76e449849f40af 100644 --- a/src/utils/scheduler.ts +++ b/src/mol-util/scheduler.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2017 molio contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author David Sehnal <david.sehnal@gmail.com> */ diff --git a/src/mol-util/string-builder.ts b/src/mol-util/string-builder.ts new file mode 100644 index 0000000000000000000000000000000000000000..3de0deffc765fe169aaa8da97e18236f1debe52c --- /dev/null +++ b/src/mol-util/string-builder.ts @@ -0,0 +1,154 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * Adapted from CIFTools.js (https://github.com/dsehnal/CIFTools.js) + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +interface StringBuilder { + current: string[], + offset: number, + capacity: number, + chunks: string[] +} + +namespace StringBuilder { + export function create(chunkCapacity = 512): StringBuilder { + return { + current: [], + offset: 0, + capacity: chunkCapacity, + chunks: [] + }; + } + + export function getString(builder: StringBuilder) { + if (!builder.chunks.length) { + if (builder.current.length === builder.offset) return builder.current.join(''); + return builder.current.splice(0, builder.offset).join(''); + } + + if (builder.offset > 0) { + builder.chunks[builder.chunks.length] = builder.current.length === builder.offset + ? builder.current.join('') + : builder.current.slice(0, builder.offset).join(''); + } + + return builder.chunks.join(''); + } + + export function getChunks(builder: StringBuilder): string[] { + if (builder.offset > 0) { + if (builder.current.length === builder.offset) builder.chunks[builder.chunks.length] = builder.current.join(''); + else builder.chunks[builder.chunks.length] = builder.current.slice(0, builder.offset).join(''); + builder.offset = 0; + } + return builder.chunks; + } + + const enum PaddingSpaces { Count = 512 } + const __paddingSpaces: string[] = []; + (function () { + let s = ''; + for (let i = 0; i < PaddingSpaces.Count; i++) { + __paddingSpaces[i] = s; + s = s + ' '; + } + })(); + + export function newline(builder: StringBuilder) { + writeSafe(builder, '\n'); + } + + export function whitespace(builder: StringBuilder, len: number) { + if (len > 0) writeSafe(builder, __paddingSpaces[len]); + } + + export function whitespace1(builder: StringBuilder) { + writeSafe(builder, ' '); + } + + export function write(builder: StringBuilder, val: string) { + if (!val) return; + + if (builder.offset === builder.capacity) { + builder.chunks[builder.chunks.length] = builder.current.join(''); + builder.offset = 0; + } + + builder.current[builder.offset++] = val; + } + + /** Write without check. */ + export function writeSafe(builder: StringBuilder, val: string) { + if (builder.offset === builder.capacity) { + builder.chunks[builder.chunks.length] = builder.current.join(''); + builder.offset = 0; + } + + builder.current[builder.offset++] = val; + } + + export function writePadLeft(builder: StringBuilder, val: string, totalWidth: number) { + if (!val) { whitespace(builder, totalWidth); return; } + + let padding = totalWidth - val.length; + whitespace(builder, padding); + writeSafe(builder, val); + } + + export function writePadRight(builder: StringBuilder, val: string, totalWidth: number) { + if (!val) { whitespace(builder, totalWidth); return; } + + let padding = totalWidth - val.length; + writeSafe(builder, val); + whitespace(builder, padding); + } + + + export function writeInteger(builder: StringBuilder, val: number) { + writeSafe(builder, '' + val); + } + + export function writeIntegerAndSpace(builder: StringBuilder, val: number) { + writeSafe(builder, '' + val + ' '); + } + + export function writeIntegerPadLeft(builder: StringBuilder, val: number, totalWidth: number) { + let s = '' + val; + let padding = totalWidth - s.length; + whitespace(builder, padding); + writeSafe(builder, s); + } + + export function writeIntegerPadRight(builder: StringBuilder, val: number, totalWidth: number) { + let s = '' + val; + let padding = totalWidth - s.length; + writeSafe(builder, s); + whitespace(builder, padding); + } + + /** + * @example writeFloat(123.2123, 100) -- 2 decim + */ + export function writeFloat(builder: StringBuilder, val: number, precisionMultiplier: number) { + writeSafe(builder, '' + Math.round(precisionMultiplier * val) / precisionMultiplier) + } + + export function writeFloatPadLeft(builder: StringBuilder, val: number, precisionMultiplier: number, totalWidth: number) { + let s = '' + Math.round(precisionMultiplier * val) / precisionMultiplier; + let padding = totalWidth - s.length; + whitespace(builder, padding); + writeSafe(builder, s); + } + + export function writeFloatPadRight(builder: StringBuilder, val: number, precisionMultiplier: number, totalWidth: number) { + let s = '' + Math.round(precisionMultiplier * val) / precisionMultiplier; + let padding = totalWidth - s.length; + writeSafe(builder, s); + whitespace(builder, padding); + } +} + +export default StringBuilder \ No newline at end of file diff --git a/src/mol-util/time.ts b/src/mol-util/time.ts new file mode 100644 index 0000000000000000000000000000000000000000..a0ee3abfdeccd9b60ee442d460e49b2f496f7813 --- /dev/null +++ b/src/mol-util/time.ts @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +declare var process: any; +declare var window: any; + +const now: () => number = (function () { + if (typeof window !== 'undefined' && window.performance) { + const perf = window.performance; + return () => perf.now(); + } else if (typeof process !== 'undefined' && process.hrtime !== 'undefined') { + return () => { + let t = process.hrtime(); + return t[0] * 1000 + t[1] / 1000000; + }; + } else { + return () => +new Date(); + } +}()); + +export default now; \ No newline at end of file diff --git a/src/mol-util/uuid.ts b/src/mol-util/uuid.ts new file mode 100644 index 0000000000000000000000000000000000000000..471d349695e2ff619c8166a5066fbffc539ef652 --- /dev/null +++ b/src/mol-util/uuid.ts @@ -0,0 +1,23 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import now from './time' + +interface UUID extends String { '@type': 'uuid' } + +namespace UUID { + export function create(): UUID { + let d = (+new Date()) + now(); + const uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { + const r = (d + Math.random()*16)%16 | 0; + d = Math.floor(d/16); + return (c==='x' ? r : (r&0x3|0x8)).toString(16); + }); + return uuid as any; + } +} + +export default UUID; \ No newline at end of file diff --git a/src/perf-tests/binary-search.ts b/src/perf-tests/binary-search.ts new file mode 100644 index 0000000000000000000000000000000000000000..b5d717e55b427adaa1cf934d8dfd8d2d0124de2c --- /dev/null +++ b/src/perf-tests/binary-search.ts @@ -0,0 +1,171 @@ + +function createData(n: number) { + const data = [];//new Int32Array(n); + let last = (15 * Math.random()) | 0; + for (let i = 0; i < n; i++) { + data[i] = last; + last += (15 * Math.random()) | 0; + } + return data; +} + +function binarySearchHelper(list: ArrayLike<number>, t: number) { + let min = 0, max = list.length - 1; + while (min <= max) { + if (min + 11 > max) { + for (let i = min; i <= max; i++) { + if (t === list[i]) return i; + } + return -1; + } + + const mid = (min + max) >> 1; + const v = list[mid]; + if (t < v) max = mid - 1; + else if (t > v) min = mid + 1; + else return mid; + } + return -1; +} + +function objSearch(obj: any, val: number) { + return typeof obj[val] !== 'undefined'; +} + +function setSearch(set: Set<number>, val: number) { + return set.has(val); +} + +type Mask = { min: number, max: number, mask: ArrayLike<number> } +function maskSearch({ min, max, mask }: Mask, val: number) { + return val >= min && val <= max && !!mask[val - min]; +} + +function prepare(list: ArrayLike<number>) { + const obj = Object.create(null), set = new Set<number>(); + for (let i = 0; i < list.length; i++) { + const v = list[i]; + obj[v] = i; + set.add(v); + } + + return { list, obj, set }; +} + +function prepareSet(list: ArrayLike<number>) { + const set = new Set<number>(); + for (let i = 0; i < list.length; i++) { + const v = list[i]; + set.add(v); + } + return set; +} + +function prepareObj(list: ArrayLike<number>) { + const obj = Object.create(null); + for (let i = 0; i < list.length; i++) { + const v = list[i]; + obj[v] = i; + } + + return obj; +} + +function prepareMask(list: ArrayLike<number>): Mask { + let max = Number.NEGATIVE_INFINITY, min = Number.POSITIVE_INFINITY; + for (let i = 0; i < list.length; i++) { + const v = list[i]; + if (max < v) max = v; + if (min > v) min = v; + } + const mask = new Uint8Array(max - min + 1); + for (let i = 0; i < list.length; i++) { + const v = list[i]; + mask[v - min] = 1; + } + + return { min, max, mask }; +} + +function testBinary(list: ArrayLike<number>, points: ArrayLike<number>) { + let r = 0; + for (let i = 0, _i = points.length; i < _i; i++) { + if (binarySearchHelper(list, points[i]) >= 0) r += points[i]; + } + return r; +} + +function testObj(obj: any, points: ArrayLike<number>) { + let r = 0; + for (let i = 0, _i = points.length; i < _i; i++) { + if (objSearch(obj, points[i])) r += points[i]; + } + return r; +} + +function testSet(set: Set<number>, points: ArrayLike<number>) { + let r = 0; + for (let i = 0, _i = points.length; i < _i; i++) { + if (setSearch(set, points[i])) r += points[i]; + } + return r; +} + +function testMask(mask: Mask, points: ArrayLike<number>) { + let r = 0; + for (let i = 0, _i = points.length; i < _i; i++) { + if (maskSearch(mask, points[i])) r += points[i]; + } + return r; +} + +function run(f: () => number, n: number) { + for (let i = 0; i < n; i++) f(); +} + +(function () { + const size = 10000; + const list = createData(size); + const queryPoints = createData(size); + + let obj = prepareObj(list); + let set = prepareSet(list); + let mask = prepareMask(list); + + console.log('list', testBinary(list, queryPoints)); + console.log('obj', testObj(obj, queryPoints)); + console.log('set', testSet(set, queryPoints)); + console.log('mask', testMask(mask, queryPoints)); + + console.log('----------------------') + + console.time('obj'); + run(() => testObj(obj, queryPoints), 100); + console.timeEnd('obj'); + + console.time('set'); + run(() => testSet(set, queryPoints), 100); + console.timeEnd('set'); + + console.time('bin-search'); + run(() => testBinary(list, queryPoints), 100); + console.timeEnd('bin-search'); + + console.time('mask-search'); + run(() => testMask(mask, queryPoints), 100); + console.timeEnd('mask-search'); + + console.log('----------------------') + + console.time('prepare-obj'); + run(() => prepareObj(list), 1); + console.timeEnd('prepare-obj'); + + console.time('prepare-set'); + run(() => prepareSet(list).size, 1); + console.timeEnd('prepare-set'); + + console.time('prepare-mask'); + run(() => prepareMask(list).min, 1); + console.timeEnd('prepare-mask'); +}()) diff --git a/src/perf-tests/chunked-array-vs-native.ts b/src/perf-tests/chunked-array-vs-native.ts new file mode 100644 index 0000000000000000000000000000000000000000..1b145425d76503a02dcd3e6ddad59d9496273707 --- /dev/null +++ b/src/perf-tests/chunked-array-vs-native.ts @@ -0,0 +1,44 @@ +import * as B from 'benchmark' +import ChunkedArray from 'mol-data/util/chunked-array' + +function testNative(size: number) { + const xs = new Array(size); + for (let i = 0; i < size; i++) xs[i] = i * i; + return xs; +} + +function testChunkedTyped(size: number, chunk: number, linear: boolean) { + const xs = ChunkedArray.create(s => new Int32Array(s), 1, chunk, linear); + for (let i = 0; i < size; i++) ChunkedArray.add(xs, i * i); + return ChunkedArray.compact(xs); +} + +function testChunkedNative(size: number, chunk: number) { + const xs = ChunkedArray.create(s => [], 1, chunk, false); + for (let i = 0; i < size; i++) ChunkedArray.add(xs, i * i); + return ChunkedArray.compact(xs); +} + +const suite = new B.Suite(); + +const N = 70000; + +suite + .add('native', () => testNative(N)) + .add('chunkedT 0.1k', () => testChunkedTyped(N, 100, false)) + .add('chunkedT 4k', () => testChunkedTyped(N, 4096, false)) + .add('chunkedT 4k lin', () => testChunkedTyped(N, 4096, true)) + .add('chunkedT N / 2', () => testChunkedTyped(N, N / 2, false)) + .add('chunkedT N', () => testChunkedTyped(N, N, false)) + .add('chunkedT 2 * N', () => testChunkedTyped(N, 2 * N, false)) + + .add('chunkedN N', () => testChunkedNative(N, N)) + .add('chunkedN 0.1k', () => testChunkedNative(N, 100)) + .add('chunkedN N / 2', () => testChunkedNative(N, N / 2)) + .add('chunkedN 2 * N', () => testChunkedNative(N, 2 * N)) + .on('cycle', (e: any) => { + console.log(String(e.target)); + }) + .run(); + +//console.log(testChunkedTyped(10, 16)); diff --git a/src/perf-tests/cif-encoder.ts b/src/perf-tests/cif-encoder.ts new file mode 100644 index 0000000000000000000000000000000000000000..5f22508252cf5991273ced66326491b48f587b86 --- /dev/null +++ b/src/perf-tests/cif-encoder.ts @@ -0,0 +1,58 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import Iterator from 'mol-data/iterator' +import * as Enc from 'mol-io/writer/cif' + +const category1: Enc.CategoryDefinition<number> = { + name: 'test', + fields: [{ + name: 'f1', + type: Enc.FieldType.Str, + value: i => 'v' + i + }, { + name: 'f2', + type: Enc.FieldType.Int, + value: i => i * i + }, { + name: 'f3', + type: Enc.FieldType.Float, + value: i => Math.random() + }] +} + +const category2: Enc.CategoryDefinition<number> = { + name: 'test2', + fields: [{ + name: 'e1', + type: Enc.FieldType.Str, + value: i => 'v\n' + i + }, { + name: 'e2', + type: Enc.FieldType.Int, + value: i => i * i + }, { + name: 'e3', + type: Enc.FieldType.Float, + value: i => Math.random() + }] +} + +function getInstance(ctx: { cat: Enc.CategoryDefinition<number>, rowCount: number }): Enc.CategoryInstance { + return { + data: void 0, + definition: ctx.cat, + keys: () => Iterator.Range(0, ctx.rowCount - 1), + rowCount: ctx.rowCount + } +} + +const w = Enc.create(); + +w.startDataBlock('test'); +w.writeCategory(getInstance, [{ rowCount: 5, cat: category1 }]); +w.writeCategory(getInstance, [{ rowCount: 1, cat: category2 }]); +console.log(w.getData()); diff --git a/src/perf-tests/column.ts b/src/perf-tests/column.ts new file mode 100644 index 0000000000000000000000000000000000000000..bc1dda619ce874501bce1cfec13bb4aa8f849f1f --- /dev/null +++ b/src/perf-tests/column.ts @@ -0,0 +1,78 @@ +import * as B from 'benchmark' +import { Column as C } from 'mol-data/db' + +export namespace Column { + function createData(n: number) { + const ret = new Float32Array(n); + for (let i = 0; i < n; i++) { + ret[i] = i * i + 1; + } + return ret; + } + + function raw(xs: ArrayLike<number>) { + let sum = 0; + for (let i = 0, _i = xs.length; i < _i; i++) { + sum += xs[i]; + } + return sum; + } + + function column(col: C<number>) { + let sum = 0; + for (let i = 0, _i = col.rowCount; i < _i; i++) { + sum += col.value(i); + } + return sum; + } + + function column1(col: C<number>) { + let sum = 0; + for (let i = 0, _i = col.rowCount; i < _i; i++) { + sum += col.value(i); + } + return sum; + } + + function val(i: number) { return i * i + 1; } + + export function runMono() { + const suite = new B.Suite(); + const data = createData(1000); + const nativeData = [...data as any]; + const col = C.ofArray({ array: data, schema: C.Schema.float }); + const lambda = C.ofLambda({ value: val, rowCount: data.length, schema: C.Schema.float }); + const cnst = C.ofConst(10, data.length, C.Schema.float); + suite + .add('raw', () => raw(data)) + .add('native raw', () => raw(nativeData)) + .add('arraycol', () => column(col)) + .add('arraycol1', () => column(col)) + .add('const', () => column1(cnst)) + .add('arraycol2', () => column(col)) + .add('lambda', () => column1(lambda)) + .on('cycle', (e: any) => console.log(String(e.target))) + .run(); + } + + export function runPoly() { + const suite = new B.Suite(); + const data = createData(10000); + const nativeData = [...data as any]; + const col = C.ofArray({ array: data, schema: C.Schema.float }); + const lambda = C.ofLambda({ value: val, rowCount: data.length, schema: C.Schema.float }); + const cnst = C.ofConst(10, data.length, C.Schema.float); + suite + .add('raw', () => raw(data)) + .add('native raw', () => raw(nativeData)) + .add('arraycol', () => column(col)) + .add('const', () => column(cnst)) + .add('arraycol2', () => column(col)) + .add('lambda', () => column(lambda)) + .on('cycle', (e: any) => console.log(String(e.target))) + .run(); + } +} + +Column.runMono(); +Column.runPoly(); \ No newline at end of file diff --git a/src/perf-tests/iterators.ts b/src/perf-tests/iterators.ts new file mode 100644 index 0000000000000000000000000000000000000000..a08ad0fc5279542b0f46023845904c0e59deb070 --- /dev/null +++ b/src/perf-tests/iterators.ts @@ -0,0 +1,234 @@ +import * as B from 'benchmark' +import It from 'mol-data/iterator' + +function createData(n: number) { + const data = [];//new Int32Array(n); + let last = (15 * Math.random()) | 0; + for (let i = 0; i < n; i++) { + data[i] = last; + last += (15 * Math.random()) | 0; + } + return data; +} + +export namespace Iterators { + const data = createData(100000); + + export function forLoop() { + let sum = 0; + for (let i = 0, _i = data.length; i < _i; i++) { + sum += data[i]; + } + return sum; + } + + export function forOf() { + let sum = 0; + for (const e of data) { + sum += e; + } + return sum; + } + + export function forEach() { + const ctx = { sum: 0 }; + data.forEach(function (this: typeof ctx, v: number) { this.sum += v }, ctx); + return ctx.sum; + } + + export function forEachAllParams() { + const ctx = { sum: 0 }; + data.forEach(function (this: typeof ctx, v: number, _: any, __: any) { this.sum += v }, ctx); + return ctx.sum; + } + + export function forEachClosure() { + let sum = 0; + data.forEach(v => sum += v); + return sum; + } + + export function forEachClosureAll() { + let sum = 0; + data.forEach((v, _, __) => sum += v); + return sum; + } + + + export function forEachClosureAllFunction() { + let sum = 0; + data.forEach(function (v, _, __) { sum += v }); + return sum; + } + + interface ES6Iterator { + [Symbol.iterator](): ES6Iterator, + done: boolean; + value: number; + next(): { done: boolean, value: number } + reset(data: any[]): ES6Iterator; + } + + class _MutableES6Iterator implements ES6Iterator { + done = true; + value = 0; + + private xs: any[] = void 0 as any; + private index: number = -1; + private length: number = 0; + + [Symbol.iterator]() { return this }; + + next() { + const index = ++this.index; + if (index < this.length) this.value = this.xs[index]; + else this.done = true; + return this; + } + + reset(xs: any[]) { + this.value = xs[0]; + this.length = xs.length; + this.done = false; + this.xs = xs; + this.index = -1; + return this; + } + } + + class _ImmutableES6Iterator implements ES6Iterator { + done = true; + value = 0; + + private xs: any[] = void 0 as any; + private index: number = -1; + private length: number = 0; + + [Symbol.iterator]() { return this }; + + next() { + const index = ++this.index; + if (index < this.length) this.value = this.xs[index]; + else this.done = true; + return { done: this.done, value: this.value }; + } + + reset(xs: any[]) { + this.value = xs[0]; + this.length = xs.length; + this.done = false; + this.xs = xs; + this.index = -1; + return this; + } + } + + export function mutableES6Iterator() { + const it = new _MutableES6Iterator(); + let sum = 0; + for (let e = it.reset(data).next().value; !it.done; e = it.next().value) { + sum += e; + } + return sum; + } + + // export function mutableES6IteratorOf() { + // const it = new _ImmutableES6Iterator(); + // let sum = 0; + // for (const e of it.reset(data)) { + // sum += e; + // } + // return sum; + // } + + export function immutableES6Iterator() { + const it = new _ImmutableES6Iterator(); + let sum = 0; + it.reset(data); + while (true) { + const { value, done } = it.next(); + if (done) break; + sum += value; + } + return sum; + } + + // export function immutableES6IteratorOf() { + // const it = new _MutableES6Iterator(); + // let sum = 0; + // for (const e of it.reset(data)) { + // sum += e; + // } + // return sum; + // } + + interface MutableIterator { + done: boolean; + next(): number; + start(data: any[]): number; + } + + class _MutableIterator implements MutableIterator { + done = true; + + private xs: any[] = void 0 as any; + private index: number = -1; + private length: number = 0; + + next() { + const index = ++this.index; + if (index < this.length) return this.xs[index]; + else { + this.done = true; + return 0; + } + } + + start(xs: any[]) { + this.length = xs.length; + this.done = !this.length; + this.xs = xs; + this.index = 0; + return this.done ? 0 : this.xs[0]; + } + } + + export function mutableIterator() { + const it = new _MutableIterator(); + let sum = 0; + for (let e = it.start(data); !it.done; e = it.next()) { + sum += e; + } + return sum; + } + + export function run() { + const suite = new B.Suite(); + + suite + .add('for', () => Iterators.forLoop()) + .add('forOf', () => Iterators.forOf()) + .add('forEach', () => Iterators.forEach()) + .add('forEach all params', () => Iterators.forEachAllParams()) + .add('forEachClosure', () => Iterators.forEachClosure()) + .add('forEachClosure all', () => Iterators.forEachClosureAll()) + .add('forEachClosure all function', () => Iterators.forEachClosureAllFunction()) + .add('mutableIterator ES6', () => Iterators.mutableES6Iterator()) + //.add('mutableIteratorOf ES6', () => Iterators.mutableES6IteratorOf()) + .add('immutableIterator ES6', () => Iterators.immutableES6Iterator()) + //.add('immutableIteratorOf ES6', () => Iterators.immutableES6IteratorOf()) + .add('mutableIterator', () => Iterators.mutableIterator()) + .on('cycle', (e: any) => { + console.log(String(e.target)); + }) + // .on('complete', function (this: any) { + // console.log('Fastest is ' + this.filter('fastest').map('name')); + // }) + .run(); + } +} + +const it = It.Array([1, 2, 3]); +while (it.hasNext) { + console.log(it.move()); +} \ No newline at end of file diff --git a/src/perf-tests/sets.ts b/src/perf-tests/sets.ts new file mode 100644 index 0000000000000000000000000000000000000000..699c9037cafc14c7502954b09df33cee123854d4 --- /dev/null +++ b/src/perf-tests/sets.ts @@ -0,0 +1,344 @@ +import * as B from 'benchmark' +import { Tuple, Segmentation, OrderedSet as OrdSet } from 'mol-data/int' +import { AtomSet } from 'mol-model/structure' + +export namespace Iteration { + const U = 1000, V = 2500; + + const control: number[] = []; + const sets = Object.create(null); + for (let i = 1; i < U; i++) { + const set = []; + for (let j = 1; j < V; j++) { + control[control.length] = j * j + 1; + set[set.length] = j * j + 1; + } + sets[i * i] = OrdSet.ofSortedArray(set); + } + const ms = AtomSet.create(sets); + + export function native() { + let s = 0; + for (let i = 0, _i = control.length; i < _i; i++) s += control[i]; + return s; + } + + export function iterators() { + let s = 0; + const it = AtomSet.atoms(ms); + while (it.hasNext) { + const v = it.move(); + s += Tuple.snd(v); + } + return s; + } + + export function elementAt() { + let s = 0; + for (let i = 0, _i = AtomSet.atomCount(ms); i < _i; i++) s += Tuple.snd(AtomSet.atomGetAt(ms, i)); + return s; + } + + export function manual() { + let s = 0; + const keys = AtomSet.unitIds(ms); + for (let i = 0, _i = OrdSet.size(keys); i < _i; i++) { + const set = AtomSet.unitGetById(ms, OrdSet.getAt(keys, i)); + for (let j = 0, _j = OrdSet.size(set); j < _j; j++) { + s += OrdSet.getAt(set, j); + } + } + return s; + } + + export function manual1() { + let s = 0; + for (let i = 0, _i = AtomSet.unitCount(ms); i < _i; i++) { + const set = AtomSet.unitGetByIndex(ms, i); + for (let j = 0, _j = OrdSet.size(set); j < _j; j++) { + s += OrdSet.getAt(set, j); + } + } + return s; + } + + export function run() { + const suite = new B.Suite(); + + // const values: number[] = []; + // for (let i = 0; i < 1000000; i++) values[i] = (Math.random() * 1000) | 0; + + console.log(Iteration.native(), Iteration.iterators(), Iteration.elementAt(), Iteration.manual(), Iteration.manual1()); + + suite + .add('native', () => Iteration.native()) + .add('iterators', () => Iteration.iterators()) + .add('manual', () => Iteration.manual()) + .add('manual1', () => Iteration.manual1()) + .add('el at', () => Iteration.elementAt()) + .on('cycle', (e: any) => console.log(String(e.target))) + .run(); + } +} + +export namespace Union { + function createArray(n: number) { + const data = new Int32Array(n); + let c = (Math.random() * 100) | 0; + for (let i = 0; i < n; i++) { + data[i] = c; + c += 1 + (Math.random() * 100) | 0 + } + return data; + } + + function rangeArray(o: number, n: number) { + const data = new Int32Array(n); + for (let i = 0; i < n; i++) { + data[i] = o + i; + } + return data; + } + + function createData(array: ArrayLike<number>) { + const obj = Object.create(null); + const set = new Set<number>(); + for (let i = 0; i < array.length; i++) { + const a = array[i]; + obj[a] = 1; + set.add(a); + } + + return { ordSet: OrdSet.ofSortedArray(array), obj, set } + } + + function unionOS(a: OrdSet, b: OrdSet) { + return OrdSet.union(a, b); + } + + function intOS(a: OrdSet, b: OrdSet) { + return OrdSet.intersect(a, b); + } + + function unionO(a: object, b: object) { + const ret = Object.create(null); + for (const k of Object.keys(a)) ret[k] = 1; + for (const k of Object.keys(b)) ret[k] = 1; + return ret; + } + + function intO(a: object, b: object) { + const ret = Object.create(null); + for (const k of Object.keys(a)) if ((b as any)[k]) ret[k] = 1; + return ret; + } + + function _setAdd(this: Set<number>, x: number) { this.add(x) } + function unionS(a: Set<number>, b: Set<number>) { + const ret = new Set<number>(); + a.forEach(_setAdd, ret); + b.forEach(_setAdd, ret); + return ret; + } + + function _setInt(this: { set: Set<number>, other: Set<number> }, x: number) { if (this.other.has(x)) this.set.add(x) } + function intS(a: Set<number>, b: Set<number>) { + if (a.size < b.size) { + const ctx = { set: new Set<number>(), other: b }; + a.forEach(_setInt, ctx); + return ctx.set; + } else { + const ctx = { set: new Set<number>(), other: a }; + b.forEach(_setInt, ctx); + return ctx.set; + } + } + + export function run() { + const suite = new B.Suite(); + + const { ordSet: osA, set: sA, obj: oA } = createData(createArray(1000)); + const { ordSet: osB, set: sB, obj: oB } = createData(createArray(1000)); + + console.log(OrdSet.size(unionOS(osA, osB)), Object.keys(unionO(oA, oB)).length, unionS(sA, sB).size); + console.log(OrdSet.size(intOS(osA, osB)), Object.keys(intO(oA, oB)).length, intS(sA, sB).size); + + suite + .add('u ord set', () => unionOS(osA, osB)) + .add('u obj', () => unionO(oA, oB)) + .add('u ES6 set', () => unionS(sA, sB)) + .add('i ord set', () => intOS(osA, osB)) + .add('i obj', () => intO(oA, oB)) + .add('i ES6 set', () => intS(sA, sB)) + .on('cycle', (e: any) => console.log(String(e.target))) + .run(); + } + + export function runR() { + const suite = new B.Suite(); + + const rangeA = rangeArray(145, 1000); + const rangeB = rangeArray(456, 1000); + + const { ordSet: osA, set: sA, obj: oA } = createData(rangeA); + const { ordSet: osB, set: sB, obj: oB } = createData(rangeB); + + console.log(OrdSet.size(unionOS(osA, osB)), Object.keys(unionO(oA, oB)).length, unionS(sA, sB).size); + console.log(OrdSet.size(intOS(osA, osB)), Object.keys(intO(oA, oB)).length, intS(sA, sB).size); + + suite + .add('u ord set', () => unionOS(osA, osB)) + .add('u obj', () => unionO(oA, oB)) + .add('u ES6 set', () => unionS(sA, sB)) + .add('i ord set', () => intOS(osA, osB)) + .add('i obj', () => intO(oA, oB)) + .add('i ES6 set', () => intS(sA, sB)) + .on('cycle', (e: any) => console.log(String(e.target))) + .run(); + } +} + +export namespace Build { + function createSorted() { + const b = AtomSet.LinearBuilder(AtomSet.Empty); + for (let i = 0; i < 10; i++) { + for (let j = 0; j < 1000; j++) { + b.add(i, j); + } + } + return b.getSet(); + } + + function createByUnit() { + const b = AtomSet.LinearBuilder(AtomSet.Empty); + for (let i = 0; i < 10; i++) { + b.beginUnit(); + for (let j = 0; j < 1000; j++) { + b.addToUnit(j); + } + b.commitUnit(i); + } + return b.getSet(); + } + + + export function run() { + const suite = new B.Suite(); + suite + .add('create sorted', () => createSorted()) + .add('create by unit', () => createByUnit()) + .on('cycle', (e: any) => console.log(String(e.target))) + .run(); + } +} + +export namespace Tuples { + function createData(n: number) { + const ret: Tuple[] = new Float64Array(n) as any; + for (let i = 0; i < n; i++) { + ret[i] = Tuple.create(i, i * i + 1); + } + return ret; + } + + function sum1(data: ArrayLike<Tuple>) { + let s = 0; + for (let i = 0, _i = data.length; i < _i; i++) { + s += Tuple.fst(data[i]) + Tuple.snd(data[i]); + } + return s; + } + + function sum2(data: ArrayLike<Tuple>) { + let s = 0; + for (let i = 0, _i = data.length; i < _i; i++) { + const t = data[i]; + s += Tuple.fst(t) + Tuple.snd(t); + } + return s; + } + + export function run() { + const suite = new B.Suite(); + const data = createData(10000); + suite + .add('sum fst/snd', () => sum1(data)) + .add('sum fst/snd 1', () => sum2(data)) + .on('cycle', (e: any) => console.log(String(e.target))) + .run(); + } +} + +export function testSegments() { + const data = OrdSet.ofSortedArray([4, 9, 10, 11, 14, 15, 16]); + const segs = Segmentation.create([0, 4, 10, 12, 13, 15, 25]); + const it = Segmentation.transientSegments(segs, data); + + while (it.hasNext) { + const s = it.move(); + for (let j = s.start; j < s.end; j++) { + console.log(`${s.index}: ${OrdSet.getAt(data, j)}`); + } + } +} + +export namespace ObjectVsMap { + function objCreate(keys: string[]) { + const m = Object.create(null); + m.x = 0; + delete m.x; + for (let i = 0, _i = keys.length; i < _i; i++) { + m[keys[i]] = i * i; + } + return m; + } + + function mapCreate(keys: string[]) { + const m = new Map<string, number>(); + for (let i = 0, _i = keys.length; i < _i; i++) { + m.set(keys[i], i * i); + } + return m; + } + + function objQuery(keys: string[], n: number, obj: any) { + let ret = 0; + for (let i = 0; i < n; i++) ret += obj[keys[i % n]]; + return ret; + } + + function mapQuery(keys: string[], n: number, map: Map<string, number>) { + let ret = 0; + for (let i = 0; i < n; i++) ret += map.get(keys[i % n])!; + return ret; + } + + export function run() { + const suite = new B.Suite(); + const keys: string[] = []; + for (let i = 0; i < 1000; i++) keys[i] = 'k' + i; + + const N = 100000; + const obj = objCreate(keys); + const map = mapCreate(keys); + suite + .add('c obj', () => objCreate(keys)) + .add('c map', () => mapCreate(keys)) + .add('q obj', () => objQuery(keys, N, obj)) + .add('q map', () => mapQuery(keys, N, map)) + .on('cycle', (e: any) => console.log(String(e.target))) + .run(); + } +} + +ObjectVsMap.run(); + +//testSegments(); + +//Tuples.run(); + +// interface AA { kind: 'a' } +// //interface BB { kind: 'b' } +// interface AB { kind: 'a' | 'b' } +// declare const a: AA; +// export const ab: AB = a; diff --git a/src/perf-tests/sort.ts b/src/perf-tests/sort.ts new file mode 100644 index 0000000000000000000000000000000000000000..2c136f83e9d6ae53a69624c369e2e4cd7b33d1f2 --- /dev/null +++ b/src/perf-tests/sort.ts @@ -0,0 +1,86 @@ +import * as B from 'benchmark' +import * as Sort from 'mol-data/util' + +function shuffle(a: number[]) { + for (let i = a.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + const t = a[i]; + a[i] = a[j]; + a[j] = t; + } + return a; +} + +function createTestData(n: number) { + const data = new Int32Array(n); //new Array(n); + for (let i = 0; i < n; i++) { + data[i] = i; + //data[i] = (n * Math.random()) | 0; + } + shuffle(data as any); + return data; +} + +export function copyArray(xs: any) { + const ret = new xs.constructor(xs.length); + for (let i = 0, _i = xs.length; i < _i; i++) ret[i] = xs[i]; + return ret; +} + +export function checkSorted(arr: ArrayLike<number>) { + for (let i = 0; i < arr.length - 1; i++) { + if (arr[i] > arr[i + 1]) { + return false; + } + } + return true; +} + + +export function runTest(size: number) { + const _data = createTestData(size); + + const dataCopies: number[][] = []; + let dataOffset = 0; + function prepareData() { + dataOffset = 0; + for (let i = 0; i < 100; i++) { + dataCopies[i] = copyArray(_data); + } + } + + function getData() { + if (dataOffset < dataCopies.length) return dataCopies[dataOffset++]; + return copyArray(_data); + } + + prepareData(); + + const suite = new B.Suite(); + + + function le(x: number, y: number) { return x - y; } + + function name(n: string) { return `${n} (${size} elems)` } + + // TODO: the data copying skewes the benchmark -- write a simple benchmark util that allows for a preparation step. + suite + .add(name('native'), () => Array.prototype.sort.call(getData(), le)) + .add(name('qsort'), () => Sort.sortArray(getData())) + //.add(name('qsort'), () => Sort.sort(getData(), 0, _data.length, Sort.arrayLess, Sort.arraySwap)) + .add(name('native sorted'), () => Array.prototype.sort.call(_data, le)) + .add(name('qsort sorted'), () => Sort.sortArray(_data)) + //.add(name('qsort sorted'), () => Sort.sort(_data, 0, _data.length, Sort.arrayLess, Sort.arraySwap)) + .on('cycle', (e: any) => { + prepareData(); + console.log(String(e.target)); + }) + .run(); + console.log('---------------------'); +} + +//runTest(10); +//runTest(100); +//runTest(1000); +runTest(10000); +//runTest(100000); \ No newline at end of file diff --git a/src/perf-tests/string-builder.ts b/src/perf-tests/string-builder.ts new file mode 100644 index 0000000000000000000000000000000000000000..fa73eb6004622dfb141488d4dc0ac9b49b9eea71 --- /dev/null +++ b/src/perf-tests/string-builder.ts @@ -0,0 +1,58 @@ +import * as B from 'benchmark' +import SB from 'mol-util/string-builder' + +export namespace Test { + function createData(n: number) { + const ret: string[] = []; + for (let i = 0; i < n; i++) { + ret[i] = '' + ((100000000 * Math.random() + 1) | 0); + } + return ret; + } + + function build(data: string[], chunkSize: number): SB { + const sb = SB.create(chunkSize); + for (let i = 0, _i = data.length; i < _i; i++) { + SB.writeSafe(sb, data[i]); + SB.whitespace1(sb); + } + return sb; + } + + function buildWS(data: string[], chunkSize: number): SB { + const sb = SB.create(chunkSize); + for (let i = 0, _i = data.length; i < _i; i++) { + SB.writeSafe(sb, data[i] + ' '); + } + return sb; + } + + // function naive(data: string[]) { + // let ret = ''; + // for (let i = 0, _i = data.length; i < _i; i++) ret += data[i]; + // return ret; + // } + + // function join(data: string[]) { + // let ret = []; + // for (let i = 0, _i = data.length; i < _i; i++) ret[i] = data[i]; + // return ret.join(''); + // } + + export function run() { + const data = createData(26 * 100000); + + const N = 512; + const suite = new B.Suite(); + suite + // .add(`naive`, () => naive(data)) + // .add(`join`, () => join(data)) + //.add(`${N} chunks`, () => SB.getChunks(build(data, N))) + .add(`${N} str`, () => SB.getString(build(data, N))) + .add(`${N} str ws`, () => SB.getString(buildWS(data, N))) + .on('cycle', (e: any) => console.log(String(e.target))) + .run(); + } +} + +Test.run(); \ No newline at end of file diff --git a/src/perf-tests/structure.ts b/src/perf-tests/structure.ts new file mode 100644 index 0000000000000000000000000000000000000000..156d441f8393095f4d3236f42e561262d88b7719 --- /dev/null +++ b/src/perf-tests/structure.ts @@ -0,0 +1,337 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import * as B from 'benchmark' + +import * as util from 'util' +import * as fs from 'fs' +import CIF from 'mol-io/reader/cif' + +import { Structure, Model, Queries as Q, Atom, AtomSet, Selection } from 'mol-model/structure' +import { OrderedSet as OrdSet, Segmentation } from 'mol-data/int' + +import to_mmCIF from 'mol-model/structure/export/mmcif' + +require('util.promisify').shim(); +const readFileAsync = util.promisify(fs.readFile); + +async function readData(path: string) { + if (path.match(/\.bcif$/)) { + const input = await readFileAsync(path) + const data = new Uint8Array(input.byteLength); + for (let i = 0; i < input.byteLength; i++) data[i] = input[i]; + return data; + } else { + return readFileAsync(path, 'utf8'); + } +} + +export async function readCIF(path: string) { + console.time('readData'); + const input = await readData(path) + console.timeEnd('readData'); + + console.time('parse'); + const comp = typeof input === 'string' ? CIF.parseText(input) : CIF.parseBinary(input); + const parsed = await comp(); + console.timeEnd('parse'); + if (parsed.isError) { + throw parsed; + } + + const data = parsed.result.blocks[0]; + console.time('schema') + const mmcif = CIF.schema.mmCIF(data); + + console.timeEnd('schema') + console.time('buildModels') + const models = Model.create({ kind: 'mmCIF', data: mmcif }); + console.timeEnd('buildModels') + const structures = models.map(Structure.ofModel); + + return { mmcif, models, structures }; +} + +export namespace PropertyAccess { + function baseline(model: Model) { + const atom_site = model.sourceData.data.atom_site; + const id = atom_site.id.value; + let s = 0; + for (let i = 0, _i = atom_site._rowCount; i < _i; i++) { + s += id(i); + } + return s; + } + + function sumProperty(structure: Structure, p: Atom.Property<number>) { + const { atoms, units } = structure; + const unitIds = AtomSet.unitIds(atoms); + const l = Atom.Location(); + + let s = 0; + + for (let i = 0, _i = unitIds.length; i < _i; i++) { + l.unit = units[unitIds[i]]; + const set = AtomSet.unitGetByIndex(atoms, i); + + for (let j = 0, _j = OrdSet.size(set); j < _j; j++) { + l.atom = OrdSet.getAt(set, j); + s += p(l); + } + } + + return s; + } + + function sumPropertySegmented(structure: Structure, p: Atom.Property<number>) { + const { atoms, units } = structure; + const unitIds = AtomSet.unitIds(atoms); + const l = Atom.Location(); + + let s = 0; + + let vA = 0, cC = 0, rC = 0; + for (let i = 0, _i = unitIds.length; i < _i; i++) { + const unit = units[unitIds[i]]; + l.unit = unit; + const set = AtomSet.unitGetByIndex(atoms, i); + + const chainsIt = Segmentation.transientSegments(unit.hierarchy.chainSegments, set); + const residues = unit.hierarchy.residueSegments; + while (chainsIt.hasNext) { + cC++; + + const chainSegment = chainsIt.move(); + const residuesIt = Segmentation.transientSegments(residues, set, chainSegment); + while (residuesIt.hasNext) { + rC++; + const residueSegment = residuesIt.move(); + // l.atom = OrdSet.getAt(set, residueSegment.start); + // console.log(unit.hierarchy.residues.auth_comp_id.value(unit.residueIndex[l.atom]), l.atom, OrdSet.getAt(set, residueSegment.end)) + for (let j = residueSegment.start, _j = residueSegment.end; j < _j; j++) { + l.atom = OrdSet.getAt(set, j); + vA++; + s += p(l); + } + } + } + } + + console.log('seg atom count', vA, cC, rC); + + return s; + } + + function sumPropertyResidue(structure: Structure, p: Atom.Property<number>) { + const { atoms, units } = structure; + const unitIds = AtomSet.unitIds(atoms); + const l = Atom.Location(); + + let s = 0; + + for (let i = 0, _i = unitIds.length; i < _i; i++) { + const unit = units[unitIds[i]]; + l.unit = unit; + const set = AtomSet.unitGetByIndex(atoms, i); + const residuesIt = Segmentation.transientSegments(unit.hierarchy.residueSegments, set); + while (residuesIt.hasNext) { + l.atom = OrdSet.getAt(set, residuesIt.move().start); + s += p(l); + } + } + + return s; + } + + function sumPropertyAtomSetIt(structure: Structure, p: Atom.Property<number>) { + const { atoms, units } = structure; + + let s = 0; + const atomsIt = AtomSet.atoms(atoms); + const l = Atom.Location(); + while (atomsIt.hasNext) { + const a = atomsIt.move(); + l.unit = units[Atom.unit(a)]; + l.atom = Atom.index(a); + s += p(l); + } + return s; + } + + // function sumPropertySegmentedMutable(structure: Structure, p: Property<number>) { + // const { atoms, units } = structure; + // const unitIds = AtomSet.unitIds(atoms); + // const l = Property.createLocation(); + + // let s = 0; + + // for (let i = 0, _i = unitIds.length; i < _i; i++) { + // const unit = units[unitIds[i]]; + // l.unit = unit; + // const set = AtomSet.unitGetByIndex(atoms, i); + + // const chainsIt = Segmentation.transientSegments(unit.hierarchy.chainSegments, set); + // const residuesIt = Segmentation.transientSegments(unit.hierarchy.residueSegments, set); + // while (chainsIt.hasNext) { + // const chainSegment = chainsIt.move(); + // residuesIt.updateRange(chainSegment); + // while (residuesIt.hasNext) { + // const residueSegment = residuesIt.move(); + // for (let j = residueSegment.start, _j = residueSegment.end; j < _j; j++) { + // l.atom = OrdSet.getAt(set, j); + // s += p(l); + // } + // } + // } + // } + + // return s; + // } + + function sumDirect(structure: Structure) { + const { atoms, units } = structure; + const unitIds = AtomSet.unitIds(atoms); + + let s = 0; + + for (let i = 0, _i = unitIds.length; i < _i; i++) { + const unitId = unitIds[i]; + const unit = units[unitId]; + const set = AtomSet.unitGetByIndex(atoms, i); + //const { residueIndex, chainIndex } = unit; + const p = unit.conformation.atomId.value; + for (let j = 0, _j = OrdSet.size(set); j < _j; j++) { + const aI = OrdSet.getAt(set, j); + s += p(aI); + } + } + + return s; + } + + // function concatProperty(structure: Structure, p: Property<string>) { + // const { atoms, units } = structure; + // const unitIds = AtomSet.unitIds(atoms); + // const l = Property.createLocation(structure); + + // let s = []; + + // for (let i = 0, _i = unitIds.length; i < _i; i++) { + // const unitId = unitIds[i]; + // l.unit = units[unitId]; + // const set = AtomSet.unitGetByIndex(atoms, i); + // const { residueIndex, chainIndex } = l.unit; + + // for (let j = 0, _j = OrdSet.size(set); j < _j; j++) { + // const aI = OrdSet.getAt(set, j); + // l.atom = aI; + // l.residueIndex = residueIndex[aI]; + // l.chainIndex = chainIndex[aI]; + // s[s.length] = p(l); + // } + // } + + // return s; + // } + + export function write(s: Structure) { + console.log(to_mmCIF('test', s)); + } + + export async function run() { + const { structures, models, mmcif } = await readCIF('./examples/1cbs_full.bcif'); + //const { structures, models, mmcif } = await readCIF('e:/test/quick/3j3q_full.bcif'); + //const { structures, models, mmcif } = await readCIF('e:/test/quick/1cbs_updated.cif'); + + console.log(mmcif.pdbx_struct_oper_list.matrix.toArray()); + console.log(mmcif.pdbx_struct_oper_list.vector.toArray()); + + //const { structures, models } = await readCIF('e:/test/molstar/3j3q.bcif'); + + // fs.writeFileSync('e:/test/molstar/3j3q.bcif', to_mmCIF('test', structures[0], true)); + // return; + + // console.log(toMmCIFString('test', structures[0])); + + // return; + + console.log(baseline(models[0])); + console.log(sumProperty(structures[0], l => l.unit.model.conformation.atomId.value(l.atom))); + console.log(sumPropertySegmented(structures[0], l => l.unit.model.conformation.atomId.value(l.atom))); + + //console.log(sumPropertySegmentedMutable(structures[0], l => l.unit.model.conformation.atomId.value(l.atom))); + console.log(sumPropertyAtomSetIt(structures[0], l => l.unit.model.conformation.atomId.value(l.atom))); + //console.log(sumProperty(structures[0], Property.cachedAtomColumn(m => m.conformation.atomId))); + console.log(sumDirect(structures[0])); + console.log('r', sumPropertyResidue(structures[0], l => l.unit.hierarchy.residues.auth_seq_id.value(l.unit.residueIndex[l.atom]))); + + console.time('atom.x'); + console.log('atom.x', sumProperty(structures[0], Q.props.atom.x)); + console.timeEnd('atom.x'); + console.time('__x') + console.log('__x', sumProperty(structures[0], l => l.unit.conformation.x[l.atom])); + console.timeEnd('__x') + + //const authSeqId = Atom.property(l => l.unit.hierarchy.residues.auth_seq_id.value(l.unit.residueIndex[l.atom])); + + //const auth_seq_id = Q.props.residue.auth_seq_id; + const auth_comp_id = Q.props.residue.auth_comp_id; + //const auth_asym_id = Q.props.chain.auth_asym_id; + //const set = new Set(['A', 'B', 'C', 'D']); + //const q = Q.generators.atomGroups({ atomTest: l => auth_seq_id(l) < 3 }); + const q = Q.generators.atoms({ atomTest: Q.pred.eq(Q.props.residue.auth_comp_id, 'ALA') }); + const P = Q.props + //const q0 = Q.generators.atoms({ atomTest: l => auth_comp_id(l) === 'ALA' }); + const q1 = Q.generators.atoms({ residueTest: l => auth_comp_id(l) === 'ALA' }); + const q2 = Q.generators.atoms({ residueTest: l => auth_comp_id(l) === 'ALA', groupBy: Q.props.residue.key }); + const q3 = Q.generators.atoms({ + chainTest: Q.pred.inSet(P.chain.auth_asym_id, ['A', 'B', 'C', 'D']), + residueTest: Q.pred.eq(P.residue.auth_comp_id, 'ALA') + }); + q(structures[0]); + //console.log(to_mmCIF('test', Selection.union(q0r))); + + console.time('q1') + q1(structures[0]); + console.timeEnd('q1') + console.time('q1') + q1(structures[0]); + console.timeEnd('q1') + console.time('q2') + const q2r = q2(structures[0]); + console.timeEnd('q2') + console.log(Selection.structureCount(q2r)); + //console.log(q1(structures[0])); + + //const col = models[0].conformation.atomId.value; + const suite = new B.Suite(); + suite + //.add('test q', () => q1(structures[0])) + //.add('test q', () => q(structures[0])) + .add('test q1', () => q1(structures[0])) + .add('test q3', () => q3(structures[0])) + //.add('test int', () => sumProperty(structures[0], l => col(l.atom))) + // .add('sum residue', () => sumPropertyResidue(structures[0], l => l.unit.hierarchy.residues.auth_seq_id.value(l.unit.residueIndex[l.atom]))) + + // .add('baseline', () => baseline(models[0])) + // .add('direct', () => sumDirect(structures[0])) + //.add('normal int', () => sumProperty(structures[0], l => l.unit.model.conformation.atomId.value(l.atom))) + //.add('atom set it int', () => sumPropertyAtomSetIt(structures[0], l => l.unit.conformation.atomId.value(l.atom))) + // .add('segmented faster int', () => sumPropertySegmented(structures[0], l => l.unit.conformation.atomId.value(l.atom))) + // .add('faster int', () => sumProperty(structures[0], l => l.unit.conformation.atomId.value(l.atom))) + //.add('segmented faster _x', () => sumPropertySegmented(structures[0], l => l.unit.conformation.__x[l.atom])) + //.add('faster _x', () => sumProperty(structures[0], l => l.unit.conformation.__x[l.atom] + l.unit.conformation.__y[l.atom] + l.unit.conformation.__z[l.atom])) + //.add('segmented mut faster int', () => sumPropertySegmentedMutable(structures[0], l => l.unit.conformation.atomId.value(l.atom))) + //.add('normal shortcut int', () => sumProperty(structures[0], l => l.conformation.atomId.value(l.atom))) + //.add('cached int', () => sumProperty(structures[0], Property.cachedAtomColumn(m => m.conformation.atomId))) + //.add('concat str', () => concatProperty(structures[0], l => l.unit.model.hierarchy.atoms.auth_atom_id.value(l.atom))) + //.add('cached concat str', () => concatProperty(structures[0], Property.cachedAtomColumn(m => m.hierarchy.atoms.auth_atom_id))) + .on('cycle', (e: any) => console.log(String(e.target))) + .run(); + } +} + +PropertyAccess.run(); \ No newline at end of file diff --git a/src/reader/cif/data-model.ts b/src/reader/cif/data-model.ts deleted file mode 100644 index aa8a4d89caa11ff39cf73f926485f2c0cc94b05a..0000000000000000000000000000000000000000 --- a/src/reader/cif/data-model.ts +++ /dev/null @@ -1,121 +0,0 @@ -/** - * Copyright (c) 2017 molio contributors, licensed under MIT, See LICENSE file for more info. - * - * @author David Sehnal <david.sehnal@gmail.com> - */ - -import * as Column from '../common/column' - -export interface File { - readonly name?: string, - readonly blocks: ReadonlyArray<Block> -} - -export function File(blocks: ArrayLike<Block>, name?: string): File { - return { name, blocks: blocks as any }; -} - -export interface Frame { - readonly header: string, - readonly categories: Categories -} - -export interface Block extends Frame { - readonly saveFrames: Frame[] -} - -export function Block(categories: Categories, header: string, saveFrames: Frame[] = []): Block { - if (Object.keys(categories).some(k => k[0] !== '_')) { - throw new Error(`Category names must start with '_'.`); - } - return { header, categories, saveFrames }; -} - -export function SafeFrame(categories: Categories, header: string): Frame { - return { header, categories }; -} - -export type Categories = { readonly [name: string]: Category } - -export interface Category { - readonly rowCount: number, - getField(name: string): Field | undefined -} - -export function Category(rowCount: number, fields: { [name: string]: Field }): Category { - return { rowCount, getField(name) { return fields[name]; } }; -} - -export namespace Category { - export const Empty: Category = { rowCount: 0, getField(name: string) { return void 0; } }; -} - -export const enum ValuePresence { - Present = 0, - NotSpecified = 1, - Unknown = 2 -} - -/** - * Implementation note: - * Always implement this as a "plain" object so that the functions are "closures" - * by default. This is to ensure that the schema access works without definiting - * additional closures. - */ -export interface Field { - readonly isDefined: boolean, - readonly rowCount: number, - - str(row: number): string, - int(row: number): number, - float(row: number): number, - - presence(row: number): ValuePresence, - - areValuesEqual(rowA: number, rowB: number): boolean, - stringEquals(row: number, value: string): boolean, - - toStringArray(params?: Column.ToArrayParams): ReadonlyArray<string>, - toIntArray(params?: Column.ToArrayParams): ReadonlyArray<number>, - toFloatArray(params?: Column.ToArrayParams): ReadonlyArray<number> -} - -export function DefaultUndefinedField(rowCount: number): Field { - return { - isDefined: false, - rowCount, - str: row => '', - int: row => 0, - float: row => 0, - - presence: row => ValuePresence.NotSpecified, - areValuesEqual: (rowA, rowB) => true, - stringEquals: (row, value) => value === null, - - toStringArray: (p) => Column.createArray(rowCount, p).array, - toIntArray: (p) => Column.createArray(rowCount, p).array, - toFloatArray: (p) => Column.createArray(rowCount, p).array - }; -} - -export function getMatrix(category: Category, field: string, rows: number, cols: number, row: number) { - const ret: number[][] = []; - for (let i = 0; i < rows; i++) { - const r: number[] = []; - for (let j = 0; j < cols; j++) { - const f = category.getField(`${field}[${i + 1}][${j + 1}]`); - r[j] = f ? f.float(row) : 0.0; - } - ret[i] = r; - } - return ret; -} - -export function getVector(category: Category, field: string, rows: number, row: number) { - const ret: number[] = []; - for (let i = 0; i < rows; i++) { - const f = category.getField(`${field}[${i + 1}]`); - ret[i] = f ? f.float(row) : 0.0; - } - return ret; -} \ No newline at end of file diff --git a/src/reader/cif/index.ts b/src/reader/cif/index.ts deleted file mode 100644 index aa10d1ffa6063cd5338d1b0a01d038501937bc39..0000000000000000000000000000000000000000 --- a/src/reader/cif/index.ts +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Copyright (c) 2017 molio contributors, licensed under MIT, See LICENSE file for more info. - * - * @author David Sehnal <david.sehnal@gmail.com> - */ - -import parseText from './text/parser' -import parseBinary from './binary/parser' -import { Block } from './data-model' -import { toTypedFrame as applySchema } from './schema' -import mmCIF from './schema/mmcif' - -export default { - parseText, - parseBinary, - applySchema, - schema: { - mmCIF: (block: Block) => applySchema(mmCIF, block) - } -} - -export * from './data-model' \ No newline at end of file diff --git a/src/reader/cif/schema.ts b/src/reader/cif/schema.ts deleted file mode 100644 index 4723e404eb62cfcb544a97fcb3ece5f621e4fa4f..0000000000000000000000000000000000000000 --- a/src/reader/cif/schema.ts +++ /dev/null @@ -1,145 +0,0 @@ -/** - * Copyright (c) 2017 molio contributors, licensed under MIT, See LICENSE file for more info. - * - * @author David Sehnal <david.sehnal@gmail.com> - */ - -import * as Data from './data-model' -import * as Column from '../common/column' -import StringPool from '../../utils/short-string-pool' - -/** - * A schema defines the shape of categories and fields. - * - * @example: - * const atom_site = { - * '@alias': '_atom_site', - * label_atom_id: Field.str(), - * Cartn_x: Field.float(), - * Cartn_y: Field.float(), - * Cartn_z: Field.float(), - * } - * - * const mmCIF = { atom_site }; - */ - -////////////////////////////////////////////// - -export function toTypedFrame<Schema extends FrameSchema>(schema: Schema, frame: Data.Frame): TypedFrame<Schema> { - return createTypedFrame(schema, frame) as TypedFrame<Schema>; -} - -export function toTypedCategory<Schema extends CategorySchema>(schema: Schema, category: Data.Category): TypedCategory<Schema> { - return new _TypedCategory(category, schema, true) as TypedCategory<any>; -} - -export type FrameSchema = { [category: string]: CategorySchema } -export type TypedFrame<Schema extends FrameSchema> = { - readonly _header?: string, - readonly _frame: Data.Frame -} & { [C in keyof Schema]: TypedCategory<Schema[C]> } - - -export type CategorySchema = { [field: string]: Field.Schema<any> } -export type TypedCategory<Schema extends CategorySchema> = { - readonly _rowCount: number, - readonly _isDefined: boolean, - readonly _category: Data.Category -} & { [F in keyof Schema]: Column.Column<Schema[F]['T']> } - -export namespace Field { - export interface Schema<T> { T: T, ctor: (field: Data.Field, category: Data.Category, key: string) => Column.Column<T>, undefinedField: (c: number) => Data.Field, alias?: string }; - export interface Spec { undefinedField?: (c: number) => Data.Field, alias?: string } - - export function alias(name: string): Schema<any> { return { alias: name } as any; } - export function pooledStr(spec?: Spec) { return createSchema(spec, PooledStr); } - export function str(spec?: Spec) { return createSchema(spec, Str); } - export function int(spec?: Spec) { return createSchema(spec, Int); } - export function float(spec?: Spec) { return createSchema(spec, Float); } - export function vector(rows: number, spec?: Spec) { return createSchema(spec, Vector(rows)); } - export function matrix(rows: number, cols: number, spec?: Spec) { return createSchema(spec, Matrix(rows, cols)); } - - function create<T>(field: Data.Field, value: (row: number) => T, toArray: Column.Column<T>['toArray']): Column.Column<T> { - const presence = field.presence; - return { - isDefined: field.isDefined, - rowCount: field.rowCount, - value, - isValueDefined: row => presence(row) === Data.ValuePresence.Present, - stringEquals: field.stringEquals, - areValuesEqual: field.areValuesEqual, - toArray - }; - } - - function PooledStr(field: Data.Field) { - const pool = StringPool.create(); - const value = (row: number) => StringPool.get(pool, field.str(row)); - const array = (params?: Column.ToArrayParams) => Column.createAndFillArray(field.rowCount, value, params); - return create<string>(field, value, array); - } - function Str(field: Data.Field) { return create(field, field.str, field.toStringArray); } - function Int(field: Data.Field) { return create(field, field.int, field.toIntArray); } - function Float(field: Data.Field) { return create(field, field.float, field.toFloatArray); } - - function Vector(rows: number) { - return function(field: Data.Field, category: Data.Category, key: string) { - const value = (row: number) => Data.getVector(category, key, rows, row); - return create(field, value, params => Column.createAndFillArray(field.rowCount, value, params)); - } - } - - function Matrix(rows: number, cols: number) { - return function(field: Data.Field, category: Data.Category, key: string) { - const value = (row: number) => Data.getMatrix(category, key, rows, cols, row); - return create(field, value, params => Column.createAndFillArray(field.rowCount, value, params)); - } - } - - // spec argument is to allow for specialised implementation for undefined fields - function createSchema<T>(spec: Spec | undefined, ctor: (field: Data.Field, category: Data.Category, key: string) => Column.Column<T>): Schema<T> { - return { T: 0 as any, ctor, undefinedField: (spec && spec.undefinedField) || Data.DefaultUndefinedField, alias: spec && spec.alias }; - } -} - -class _TypedFrame implements TypedFrame<any> { // tslint:disable-line:class-name - header = this._frame.header; - constructor(public _frame: Data.Frame, schema: FrameSchema) { - for (const k of Object.keys(schema)) { - Object.defineProperty(this, k, { value: createTypedCategory(k, schema[k], _frame), enumerable: true, writable: false, configurable: false }); - } - } -} - -class _TypedCategory implements TypedCategory<any> { // tslint:disable-line:class-name - _rowCount = this._category.rowCount; - constructor(public _category: Data.Category, schema: CategorySchema, public _isDefined: boolean) { - const fieldKeys = Object.keys(schema).filter(k => k !== '@alias'); - const cache = Object.create(null); - for (const k of fieldKeys) { - const s = schema[k]; - Object.defineProperty(this, k, { - get: function() { - if (cache[k]) return cache[k]; - const name = s.alias || k; - const field = _category.getField(name) || s.undefinedField(_category.rowCount); - cache[k] = s.ctor(field, _category, name); - return cache[k]; - }, - enumerable: true, - configurable: false - }); - } - } -} - -function createTypedFrame(schema: FrameSchema, frame: Data.Frame): any { - return new _TypedFrame(frame, schema); -} - -function createTypedCategory(key: string, schema: CategorySchema, frame: Data.Frame) { - const alias = (schema['@alias'] && schema['@alias'].alias) || key; - const name = alias[0] === '_' ? alias : '_' + alias; - const cat = frame.categories[name]; - return new _TypedCategory(cat || Data.Category.Empty, schema, !!cat); -} \ No newline at end of file diff --git a/src/reader/cif/schema/mmcif.ts b/src/reader/cif/schema/mmcif.ts deleted file mode 100644 index 45f31a003d75608660acc7a369a83496caca9605..0000000000000000000000000000000000000000 --- a/src/reader/cif/schema/mmcif.ts +++ /dev/null @@ -1,247 +0,0 @@ -/** - * Copyright (c) 2017 molio contributors, licensed under MIT, See LICENSE file for more info. - * - * @author David Sehnal <david.sehnal@gmail.com> - */ - -import { Field, TypedFrame } from '../schema' - -const pooledStr = Field.pooledStr(); -const str = Field.str(); -const int = Field.int(); -const float = Field.float(); - -const entry = { - id: str -} - -const entity = { - id: str, - type: str as Field.Schema<'polymer' | 'non-polymer' | 'water'>, - src_method: str, - pdbx_description: str, - formula_weight: float, - pdbx_number_of_molecules: int, - details: str, - pdbx_mutation: str, - pdbx_fragment: str, - pdbx_ec: str -} - -const exptl = { - entry_id: str, - method: str -} - -const cell = { - entry_id: str, - length_a: float, - length_b: float, - length_c: float, - angle_alpha: float, - angle_beta: float, - angle_gamma: float, - Z_PDB: int, - pdbx_unique_axis: str -} - -const symmetry = { - entry_id: str, - space_group_name_HM: Field.str({ alias: 'space_group_name_H-M' }), - pdbx_full_space_group_name_HM: Field.str({ alias: 'pdbx_full_space_group_name_H-M' }), - cell_setting: str, - Int_Tables_number: int, - space_group_name_Hall: str -} - -const struct_conf = { - conf_type_id: str, - id: str, - pdbx_PDB_helix_id: int, - beg_label_comp_id: pooledStr, - beg_label_asym_id: pooledStr, - beg_label_seq_id: int, - pdbx_beg_PDB_ins_code: pooledStr, - end_label_comp_id: pooledStr, - end_label_asym_id: pooledStr, - end_label_seq_id: int, - pdbx_end_PDB_ins_code: pooledStr, - beg_auth_comp_id: pooledStr, - beg_auth_asym_id: pooledStr, - beg_auth_seq_id: int, - end_auth_comp_id: pooledStr, - end_auth_asym_id: pooledStr, - end_auth_seq_id: int, - pdbx_PDB_helix_class: int, - details: str, - pdbx_PDB_helix_length: int -} - -const struct_sheet_range = { - sheet_id: pooledStr, - id: int, - beg_label_comp_id: pooledStr, - beg_label_asym_id: pooledStr, - beg_label_seq_id: int, - pdbx_beg_PDB_ins_code: pooledStr, - end_label_comp_id: pooledStr, - end_label_asym_id: pooledStr, - end_label_seq_id: int, - pdbx_end_PDB_ins_code: pooledStr, - beg_auth_comp_id: pooledStr, - beg_auth_asym_id: pooledStr, - beg_auth_seq_id: int, - end_auth_comp_id: pooledStr, - end_auth_asym_id: pooledStr, - end_auth_seq_id: int -} - -type StructConnTypeId = - | 'covale' - | 'covale_base' - | 'covale_phosphate' - | 'covale_sugar' - | 'disulf' - | 'hydrog' - | 'metalc' - | 'mismat' - | 'modres' - | 'saltbr' - -type BondValueOrder = - | 'SING' - | 'DOUB' - | 'TRIP' - | 'QUAD' - -const struct_conn = { - id: str, - conn_type_id: pooledStr as Field.Schema<StructConnTypeId>, - pdbx_PDB_id: str, - ptnr1_label_asym_id: pooledStr, - ptnr1_label_comp_id: pooledStr, - ptnr1_label_seq_id: int, - ptnr1_label_atom_id: pooledStr, - pdbx_ptnr1_label_alt_id: pooledStr, - pdbx_ptnr1_PDB_ins_code: pooledStr, - pdbx_ptnr1_standard_comp_id: pooledStr, - ptnr1_symmetry: pooledStr, - ptnr2_label_asym_id: pooledStr, - ptnr2_label_comp_id: pooledStr, - ptnr2_label_seq_id: int, - ptnr2_label_atom_id: pooledStr, - pdbx_ptnr2_label_alt_id: pooledStr, - pdbx_ptnr2_PDB_ins_code: pooledStr, - ptnr1_auth_asym_id: pooledStr, - ptnr1_auth_comp_id: pooledStr, - ptnr1_auth_seq_id: int, - ptnr2_auth_asym_id: pooledStr, - ptnr2_auth_comp_id: pooledStr, - ptnr2_auth_seq_id: int, - ptnr2_symmetry: pooledStr, - pdbx_ptnr3_label_atom_id: pooledStr, - pdbx_ptnr3_label_seq_id: int, - pdbx_ptnr3_label_comp_id: pooledStr, - pdbx_ptnr3_label_asym_id: pooledStr, - pdbx_ptnr3_label_alt_id: pooledStr, - pdbx_ptnr3_PDB_ins_code: pooledStr, - details: pooledStr, - pdbx_dist_value: float, - pdbx_value_order: pooledStr as Field.Schema<BondValueOrder> -} - -const struct_conn_type = { - id: str as Field.Schema<StructConnTypeId>, - criteria: str, - reference: str -} - -const chem_comp_bond = { - comp_id: pooledStr, - pdbx_stereo_config: pooledStr, - pdbx_ordinal: int, - pdbx_aromatic_flag: pooledStr as Field.Schema<'Y' | 'N'>, - atom_id_1: pooledStr, - atom_id_2: pooledStr, - value_order: pooledStr as Field.Schema<BondValueOrder> -} - -const pdbx_struct_assembly = { - id: str, - details: str, - method_details: str, - oligomeric_details: str, - oligomeric_count: int -} - -const pdbx_struct_assembly_gen = { - assembly_id: str, - oper_expression: str, - asym_id_list: str -} - -const pdbx_struct_oper_list = { - id: str, - type: str, - name: str, - symmetry_operation: str, - matrix: Field.matrix(3, 3), - vector: Field.vector(3) -} - -const pdbx_struct_mod_residue = { - id: int, - label_asym_id: pooledStr, - label_seq_id: int, - label_comp_id: pooledStr, - auth_asym_id: pooledStr, - auth_seq_id: int, - auth_comp_id: pooledStr, - PDB_ins_code: pooledStr, - parent_comp_id: pooledStr, - details: str -} - -const atom_site = { - group_PDB: pooledStr, - id: int, - type_symbol: pooledStr, - label_atom_id: pooledStr, - label_alt_id: pooledStr, - label_comp_id: pooledStr, - label_asym_id: pooledStr, - label_entity_id: pooledStr, - label_seq_id: int, - pdbx_PDB_ins_code: pooledStr, - pdbx_formal_charge: pooledStr, - Cartn_x: float, - Cartn_y: float, - Cartn_z: float, - occupancy: float, - B_iso_or_equiv: float, - auth_atom_id: pooledStr, - auth_comp_id: pooledStr, - auth_asym_id: pooledStr, - auth_seq_id: int, - pdbx_PDB_model_num: int -} - -const mmCIF = { - entry, - entity, - exptl, - cell, - symmetry, - struct_conf, - struct_sheet_range, - struct_conn, - struct_conn_type, - chem_comp_bond, - pdbx_struct_assembly, - pdbx_struct_assembly_gen, - pdbx_struct_oper_list, - pdbx_struct_mod_residue, - atom_site -}; -type mmCIF = TypedFrame<typeof mmCIF> -export default mmCIF; \ No newline at end of file diff --git a/src/reader/cif/text/field.ts b/src/reader/cif/text/field.ts deleted file mode 100644 index 2e09cd1b0cf369ac0d4cef3346d7d2fb88aad4e9..0000000000000000000000000000000000000000 --- a/src/reader/cif/text/field.ts +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Copyright (c) 2017 molio contributors, licensed under MIT, See LICENSE file for more info. - * - * @author David Sehnal <david.sehnal@gmail.com> - */ - -import * as Column from '../../common/column' -import * as TokenColumn from '../../common/text/column/token' -import { Tokens } from '../../common/text/tokenizer' -import * as Data from '../data-model' -import { parseInt as fastParseInt, parseFloat as fastParseFloat } from '../../common/text/number-parser' - -export default function CifTextField(tokens: Tokens, rowCount: number): Data.Field { - const { data, indices } = tokens; - - const str: Data.Field['str'] = row => { - const ret = data.substring(indices[2 * row], indices[2 * row + 1]); - if (ret === '.' || ret === '?') return ''; - return ret; - }; - - const int: Data.Field['int'] = row => { - return fastParseInt(data, indices[2 * row], indices[2 * row + 1]) || 0; - }; - - const float: Data.Field['float'] = row => { - return fastParseFloat(data, indices[2 * row], indices[2 * row + 1]) || 0; - }; - - const presence: Data.Field['presence'] = row => { - const s = indices[2 * row]; - if (indices[2 * row + 1] - s !== 1) return Data.ValuePresence.Present; - const v = data.charCodeAt(s); - if (v === 46 /* . */) return Data.ValuePresence.NotSpecified; - if (v === 63 /* ? */) return Data.ValuePresence.Unknown; - return Data.ValuePresence.Present; - }; - - return { - isDefined: true, - rowCount, - str, - int, - float, - presence, - areValuesEqual: TokenColumn.areValuesEqualProvider(tokens), - stringEquals: (row, v) => { - const s = indices[2 * row]; - const value = v || ''; - if (!value && presence(row) !== Data.ValuePresence.Present) return true; - const len = value.length; - if (len !== indices[2 * row + 1] - s) return false; - for (let i = 0; i < len; i++) { - if (data.charCodeAt(i + s) !== value.charCodeAt(i)) return false; - } - return true; - }, - toStringArray: params => Column.createAndFillArray(rowCount, str, params), - toIntArray: params => Column.createAndFillArray(rowCount, int, params), - toFloatArray: params => Column.createAndFillArray(rowCount, float, params) - } -} \ No newline at end of file diff --git a/src/reader/common/binary/column.ts b/src/reader/common/binary/column.ts deleted file mode 100644 index 28ce9a00172e698d50f8e908a3908248acf2ccb9..0000000000000000000000000000000000000000 --- a/src/reader/common/binary/column.ts +++ /dev/null @@ -1,5 +0,0 @@ -/** - * Copyright (c) 2017 molio contributors, licensed under MIT, See LICENSE file for more info. - * - * @author David Sehnal <david.sehnal@gmail.com> - */ \ No newline at end of file diff --git a/src/reader/common/column.ts b/src/reader/common/column.ts deleted file mode 100644 index 13dc3f2bafb0ea0ce3576ab829cec9bb2cf257a2..0000000000000000000000000000000000000000 --- a/src/reader/common/column.ts +++ /dev/null @@ -1,114 +0,0 @@ -/** - * Copyright (c) 2017 molio contributors, licensed under MIT, See LICENSE file for more info. - * - * @author David Sehnal <david.sehnal@gmail.com> - */ - -export type ColumnType = typeof ColumnType.str | typeof ColumnType.pooledStr | typeof ColumnType.int | typeof ColumnType.float - -export namespace ColumnType { - export const str = { '@type': '' as string, kind: 'str' as 'str' }; - export const pooledStr = { '@type': '' as string, kind: 'pooled-str' as 'pooled-str' }; - export const int = { '@type': 0 as number, kind: 'int' as 'int' }; - export const float = { '@type': 0 as number, kind: 'float' as 'float' }; -} - -export interface ToArrayParams { - array?: { new(size: number): ArrayLike<number> }, - /** First row */ - start?: number, - /** Last row (exclusive) */ - end?: number -} - -export interface Column<T> { - readonly isDefined: boolean, - readonly rowCount: number, - value(row: number): T, - isValueDefined(row: number): boolean, - toArray(params?: ToArrayParams): ReadonlyArray<T>, - stringEquals(row: number, value: string): boolean, - areValuesEqual(rowA: number, rowB: number): boolean -} - -export function UndefinedColumn<T extends ColumnType>(rowCount: number, type: T): Column<T['@type']> { - const value: Column<T['@type']>['value'] = type.kind === 'str' ? row => '' : row => 0; - return { - isDefined: false, - rowCount, - value, - isValueDefined: row => false, - toArray: params => { - const { array } = createArray(rowCount, params); - for (let i = 0, _i = array.length; i < _i; i++) array[i] = value(0) - return array; - }, - stringEquals: (row, value) => !value, - areValuesEqual: (rowA, rowB) => true - } -} - -export function ArrayColumn<T>(array: ArrayLike<T>): Column<T> { - const rowCount = array.length; - const value: Column<T>['value'] = row => array[row]; - const isTyped = isTypedArray(array); - return { - isDefined: false, - rowCount, - value, - isValueDefined: row => true, - toArray: isTyped - ? params => typedArrayWindow(array, params) as any as ReadonlyArray<T> - : params => { - const { start, end } = getArrayBounds(rowCount, params); - const ret = new Array(end - start); - for (let i = 0, _i = end - start; i < _i; i++) ret[i] = array[start + i]; - return ret; - }, - stringEquals: isTyped - ? (row, value) => (array as any)[row] === +value - : (row, value) => { - const v = array[row]; - if (typeof v !== 'string') return '' + v === value; - return v === value; - }, - areValuesEqual: (rowA, rowB) => array[rowA] === array[rowB] - } -} - -/** A helped function for Column.toArray */ -export function getArrayBounds(rowCount: number, params?: ToArrayParams) { - const start = params && typeof params.start !== 'undefined' ? Math.max(Math.min(params.start, rowCount - 1), 0) : 0; - const end = params && typeof params.end !== 'undefined' ? Math.min(params.end, rowCount) : rowCount; - return { start, end }; -} - -/** A helped function for Column.toArray */ -export function createArray(rowCount: number, params?: ToArrayParams) { - const c = params && typeof params.array !== 'undefined' ? params.array : Array; - const { start, end } = getArrayBounds(rowCount, params); - return { array: new c(end - start) as any[], start, end }; -} - -/** A helped function for Column.toArray */ -export function fillArrayValues(value: (row: number) => any, target: any[], start: number) { - for (let i = 0, _e = target.length; i < _e; i++) target[i] = value(start + i); - return target; -} - -/** A helped function for Column.toArray */ -export function createAndFillArray(rowCount: number, value: (row: number) => any, params?: ToArrayParams) { - const { array, start } = createArray(rowCount, params); - return fillArrayValues(value, array, start); -} - -export function isTypedArray(data: any) { - return data.buffer && typeof data.byteLength === 'number' && data.BYTES_PER_ELEMENT; -} - -export function typedArrayWindow(data: any, params?: ToArrayParams): ReadonlyArray<number> { - const { constructor, buffer, length, byteOffset, BYTES_PER_ELEMENT } = data; - const { start, end } = getArrayBounds(length, params); - if (start === 0 && end === length) return data; - return new constructor(buffer, byteOffset + BYTES_PER_ELEMENT * start, Math.min(length, end - start)); -} \ No newline at end of file diff --git a/src/script.ts b/src/script.ts index c10a3e1bd579544c51b1f0a0915e20b76315a95f..03c5d970ae0e3540eecf67b85967d1af0ecad3fa 100644 --- a/src/script.ts +++ b/src/script.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2017 molio contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Alexander Rose <alexander.rose@weirdbyte.de> * @author David Sehnal <david.sehnal@gmail.com> @@ -12,11 +12,15 @@ require('util.promisify').shim(); const readFileAsync = util.promisify(fs.readFile); const writeFileAsync = util.promisify(fs.writeFile); -import Gro from './reader/gro/parser' -import CIF from './reader/cif/index' +import Gro from 'mol-io/reader/gro/parser' +import CIF from 'mol-io/reader/cif' + +import Computation from 'mol-util/computation' + +import { Model } from 'mol-model/structure' // import { toTypedFrame as applySchema } from './reader/cif/schema' -import { generateSchema } from './reader/cif/schema/utils' +import { generateSchema } from 'mol-io/reader/cif/schema/utils' const file = '1crn.gro' // const file = 'water.gro' @@ -110,6 +114,25 @@ async function runCIF(input: string | Uint8Array) { console.log(mmcif.entity.type.toArray()); console.log(mmcif.pdbx_struct_oper_list.matrix.value(0)); + console.time('createModels'); + const models = Model.create({ kind: 'mmCIF', data: mmcif }); + console.timeEnd('createModels'); + + for (let i = 0; i < models.length; i++) { + console.log(models[i].id, models[i].conformation.id); + } + + // console.log(models[0].hierarchy.isMonotonous); + // console.log(models[0].hierarchy.atoms.type_symbol.value(0)); + // console.log(models[0].hierarchy.residues.auth_comp_id.value(0)); + // console.log(models[0].hierarchy.residues.auth_comp_id.value(1)); + // console.log(models[0].hierarchy.chains.auth_asym_id.value(0)); + // console.log(models[0].hierarchy.chains.auth_asym_id.value(1)); + // console.log(models[0].hierarchy.chains.label_asym_id.value(1)); + // console.log(models[0].conformation.x[0]); + // console.log(models[0].conformation.y[0]); + // console.log(models[0].conformation.z[0]); + // const schema = await _dic() // if (schema) { // const mmcif2 = applySchema(schema, data) @@ -123,7 +146,7 @@ async function runCIF(input: string | Uint8Array) { } export async function _cif() { - let path = `./examples/1cbs_updated.cif`; + let path = `./examples/1grm_updated.cif`; // path = '../test/3j3q.cif' // lets have a relative path for big test files const input = await readFileAsync(path, 'utf8') console.log('------------------'); @@ -173,7 +196,6 @@ export async function _dic() { _dic(); -import Computation from './utils/computation' const comp = Computation.create(async ctx => { for (let i = 0; i < 0; i++) { await new Promise(res => setTimeout(res, 500)); diff --git a/src/utils/chunked-array.ts b/src/utils/chunked-array.ts deleted file mode 100644 index fa1459e6a22fe29f31326291815882ec2c753300..0000000000000000000000000000000000000000 --- a/src/utils/chunked-array.ts +++ /dev/null @@ -1,133 +0,0 @@ -/** - * Copyright (c) 2017 molio contributors, licensed under MIT, See LICENSE file for more info. - * - * from https://github.com/dsehnal/CIFTools.js - * @author David Sehnal <david.sehnal@gmail.com> - */ - -/** - * A generic chunked array builder. - * - * When adding elements, the array growns by a specified number - * of elements and no copying is done until ChunkedArray.compact - * is called. - */ -export interface ChunkedArray<T> { - creator: (size: number) => any; - elementSize: number; - chunkSize: number; - current: any; - currentIndex: number; - - parts: any[]; - elementCount: number; -} - -export namespace ChunkedArray { - export function is(x: any): x is ChunkedArray<any> { - return x.creator && x.chunkSize; - } - - export function add4<T>(array: ChunkedArray<T>, x: T, y: T, z: T, w: T) { - if (array.currentIndex >= array.chunkSize) { - array.currentIndex = 0; - array.current = array.creator(array.chunkSize); - array.parts[array.parts.length] = array.current; - } - - array.current[array.currentIndex++] = x; - array.current[array.currentIndex++] = y; - array.current[array.currentIndex++] = z; - array.current[array.currentIndex++] = w; - return array.elementCount++; - } - - export function add3<T>(array: ChunkedArray<T>, x: T, y: T, z: T) { - if (array.currentIndex >= array.chunkSize) { - array.currentIndex = 0; - array.current = array.creator(array.chunkSize); - array.parts[array.parts.length] = array.current; - } - - array.current[array.currentIndex++] = x; - array.current[array.currentIndex++] = y; - array.current[array.currentIndex++] = z; - return array.elementCount++; - } - - export function add2<T>(array: ChunkedArray<T>, x: T, y: T) { - if (array.currentIndex >= array.chunkSize) { - array.currentIndex = 0; - array.current = array.creator(array.chunkSize); - array.parts[array.parts.length] = array.current; - } - - array.current[array.currentIndex++] = x; - array.current[array.currentIndex++] = y; - return array.elementCount++; - } - - export function add<T>(array: ChunkedArray<T>, x: T) { - if (array.currentIndex >= array.chunkSize) { - array.currentIndex = 0; - array.current = array.creator(array.chunkSize); - array.parts[array.parts.length] = array.current; - } - - array.current[array.currentIndex++] = x; - return array.elementCount++; - } - - - export function compact<T>(array: ChunkedArray<T>): T[] { - const ret = array.creator(array.elementSize * array.elementCount) - const offset = (array.parts.length - 1) * array.chunkSize - let offsetInner = 0 - let part: any - - if (array.parts.length > 1) { - if (array.parts[0].buffer) { - for (let i = 0; i < array.parts.length - 1; i++) { - ret.set(array.parts[i], array.chunkSize * i); - } - } else { - - for (let i = 0; i < array.parts.length - 1; i++) { - offsetInner = array.chunkSize * i; - part = array.parts[i]; - - for (let j = 0; j < array.chunkSize; j++) { - ret[offsetInner + j] = part[j]; - } - } - } - } - - if (array.current.buffer && array.currentIndex >= array.chunkSize) { - ret.set(array.current, array.chunkSize * (array.parts.length - 1)); - } else { - for (let i = 0; i < array.currentIndex; i++) { - ret[offset + i] = array.current[i]; - } - } - return ret as any; - } - - export function create<T>(creator: (size: number) => any, chunkElementCount: number, elementSize: number): ChunkedArray<T> { - chunkElementCount = chunkElementCount | 0; - if (chunkElementCount <= 0) chunkElementCount = 1; - - let chunkSize = chunkElementCount * elementSize; - let current = creator(chunkSize) - - return { - elementSize, - chunkSize, - creator, - current, - parts: [current], - currentIndex: 0, - elementCount: 0 - } as ChunkedArray<T> - } -} diff --git a/tsconfig.json b/tsconfig.json index 21016d5620035aa70f28141cd4fcef5f015af6f6..c90e01741681c4b27613733792f8ef82c91bd6c6 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,14 +1,24 @@ { "compilerOptions": { - "target": "es6", + "target": "es5", "alwaysStrict": true, "noImplicitAny": true, "noImplicitThis": true, "sourceMap": false, "noUnusedLocals": true, "strictNullChecks": true, + "strictFunctionTypes": true, + //"downlevelIteration": true, "lib": [ "es6", "dom" ], - "outDir": "build/js/src" + "outDir": "build/node_modules", + "baseUrl": "src", + "paths": { + "mol-util": ["./mol-util", "./mol-util/index.ts"], + "mol-data": ["./mol-data", "./mol-data/index.ts"], + "mol-math": ["./mol-math"], + "mol-io": ["./mol-io"], + "mol-model": ["./mol-model"] + } }, - "include": [ "src/**/*" ] + "include": [ "**/*" ] } \ No newline at end of file diff --git a/tslint.json b/tslint.json index 656391e9c651908e7a1f6f60d1f3741938bd6df4..1c88592ea6631a73315ede0d282296f9626b68c6 100644 --- a/tslint.json +++ b/tslint.json @@ -8,7 +8,7 @@ "no-var-keyword": true, "ordered-imports": [false], "trailing-comma": [false], - "class-name": true, + "class-name": false, "comment-format": [ true, "check-space"