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"