1use std::collections::{HashMap, HashSet};
21use std::sync::OnceLock;
22
23#[rustfmt::skip]
24#[allow(dead_code)]
25pub(crate) fn default_distance_range(a: &str, b: &str) -> Option<(f32, f32)> {
26 match (a, b) {
27 ("B", "C" ) => Some((1.556 - 2.0*0.015, 1.556 + 2.0*0.015)),
31 ("BR", "C" ) => Some((1.875 - 2.0*0.029, 1.966 + 2.0*0.029)),
32 ("BR", "O" ) => Some((1.581 - 2.0*0.007, 1.581 + 2.0*0.007)),
33 ("C", "C" ) => Some((1.174 - 2.0*0.011, 1.588 + 2.0*0.025)),
34 ("C", "CL") => Some((1.713 - 2.0*0.011, 1.849 + 2.0*0.011)),
35 ("C", "F" ) => Some((1.320 - 2.0*0.009, 1.428 + 2.0*0.009)),
36 ("C", "H" ) => Some((1.059 - 2.0*0.030, 1.099 + 2.0*0.007)),
37 ("C", "I" ) => Some((2.095 - 2.0*0.015, 2.162 + 2.0*0.015)),
38 ("C", "N" ) => Some((1.325 - 2.0*0.009, 1.552 + 2.0*0.023)),
39 ("C", "O" ) => Some((1.187 - 2.0*0.011, 1.477 + 2.0*0.008)),
40 ("C", "P" ) => Some((1.791 - 2.0*0.006, 1.855 + 2.0*0.019)),
41 ("C", "S" ) => Some((1.630 - 2.0*0.014, 1.863 + 2.0*0.015)),
42 ("C", "SE") => Some((1.893 - 2.0*0.013, 1.970 + 2.0*0.032)),
43 ("C", "SI") => Some((1.837 - 2.0*0.012, 1.888 + 2.0*0.023)),
44 ("CL", "O" ) => Some((1.414 - 2.0*0.026, 1.414 + 2.0*0.026)),
45 ("CL", "P" ) => Some((1.997 - 2.0*0.035, 2.008 + 2.0*0.035)),
46 ("CL", "S" ) => Some((2.072 - 2.0*0.023, 2.072 + 2.0*0.023)),
47 ("CL", "SI") => Some((2.072 - 2.0*0.009, 2.072 + 2.0*0.009)),
48 ("F", "N" ) => Some((1.406 - 2.0*0.016, 1.406 + 2.0*0.016)),
49 ("F", "P" ) => Some((1.495 - 2.0*0.016, 1.579 + 2.0*0.025)),
50 ("F", "S" ) => Some((1.640 - 2.0*0.011, 1.640 + 2.0*0.011)),
51 ("F", "SI") => Some((1.588 - 2.0*0.014, 1.694 + 2.0*0.013)),
52 ("H", "N" ) => Some((1.009 - 2.0*0.022, 1.033 + 2.0*0.022)),
53 ("H", "O" ) => Some((0.967 - 2.0*0.010, 1.015 + 2.0*0.017)),
54 ("I", "O" ) => Some((2.144 - 2.0*0.028, 2.144 + 2.0*0.028)),
55 ("N", "N" ) => Some((1.124 - 2.0*0.015, 1.454 + 2.0*0.021)),
56 ("N", "O" ) => Some((1.210 - 2.0*0.011, 1.463 + 2.0*0.012)),
57 ("N", "P" ) => Some((1.571 - 2.0*0.013, 1.697 + 2.0*0.015)),
58 ("N", "S" ) => Some((1.541 - 2.0*0.022, 1.710 + 2.0*0.019)),
59 ("N", "SI") => Some((1.711 - 2.0*0.019, 1.748 + 2.0*0.022)),
60 ("O", "P" ) => Some((1.449 - 2.0*0.007, 1.689 + 2.0*0.024)),
61 ("O", "S" ) => Some((1.423 - 2.0*0.008, 1.580 + 2.0*0.015)),
62 ("O", "SI") => Some((1.622 - 2.0*0.014, 1.680 + 2.0*0.008)),
63 ("P", "P" ) => Some((2.214 - 2.0*0.022, 2.214 + 2.0*0.022)),
64 ("P", "S" ) => Some((1.913 - 2.0*0.014, 1.954 + 2.0*0.005)),
65 ("P", "SE") => Some((2.093 - 2.0*0.019, 2.093 + 2.0*0.019)),
66 ("P", "SI") => Some((2.264 - 2.0*0.019, 2.264 + 2.0*0.019)),
67 ("S", "S" ) => Some((1.897 - 2.0*0.012, 2.070 + 2.0*0.022)),
68 ("S", "SE") => Some((2.193 - 2.0*0.015, 2.193 + 2.0*0.015)),
69 ("S", "SI") => Some((2.145 - 2.0*0.020, 2.145 + 2.0*0.020)),
70 ("SE", "SE") => Some((2.340 - 2.0*0.024, 2.340 + 2.0*0.024)),
71 ("SI", "SE") => Some((2.359 - 2.0*0.012, 2.359 + 2.0*0.012)),
72 _ => None,
73 }
74}
75
76#[allow(clippy::type_complexity)] static AA_BONDS: OnceLock<HashMap<&'static str, Vec<(&'static str, &'static str, i32)>>> =
78 OnceLock::new();
79
80#[rustfmt::skip]
81pub(crate) fn get_bonds_canonical20() -> &'static HashMap<&'static str, Vec<(&'static str, &'static str, i32)>> {
87 AA_BONDS.get_or_init(|| {
88 let mut m = HashMap::new();
89 m.insert("ALA", vec![
90 ("C","O",2), ("C","OXT",1), ("C","CA",1), ("CA","CB",1), ("CA","HA",1),
91 ("CB","HB1",1), ("CB","HB2",1), ("CB","HB3",1), ("CA","N",1), ("H","N",1),
92 ("H2","N",1), ("HXT","OXT",1)
93 ]);
94 m.insert("ARG", vec![
95 ("C","O",2), ("C","OXT",1), ("C","CA",1), ("CA","CB",1), ("CA","HA",1),
96 ("CB","CG",1), ("CB","HB2",1), ("CB","HB3",1), ("CD","HD2",1), ("CD","HD3",1),
97 ("CD","NE",1), ("CD","CG",1), ("CG","HG2",1), ("CG","HG3",1), ("CZ","NH1",1),
98 ("CZ","NH2",2), ("CA","N",1), ("H","N",1), ("H2","N",1), ("CZ","NE",1),
99 ("HE","NE",1), ("HH11","NH1",1), ("HH12","NH1",1), ("HH21","NH2",1),
100 ("HH22","NH2",1), ("HXT","OXT",1)
101 ]);
102 m.insert("ASN", vec![
103 ("C","O",2), ("C","OXT",1), ("C","CA",1), ("CA","CB",1), ("CA","HA",1),
104 ("CB","CG",1), ("CB","HB2",1), ("CB","HB3",1), ("CG","ND2",1), ("CG","OD1",2),
105 ("CA","N",1), ("H","N",1), ("H2","N",1), ("HD21","ND2",1), ("HD22","ND2",1),
106 ("HXT","OXT",1)
107 ]);
108 m.insert("ASP", vec![
109 ("C","O",2), ("C","OXT",1), ("C","CA",1), ("CA","CB",1), ("CA","HA",1),
110 ("CB","CG",1), ("CB","HB2",1), ("CB","HB3",1), ("CG","OD1",2), ("CG","OD2",1),
111 ("CA","N",1), ("H","N",1), ("H2","N",1), ("HD2","OD2",1), ("HXT","OXT",1)
112 ]);
113 m.insert("CYS", vec![
114 ("C","O",2), ("C","OXT",1), ("C","CA",1), ("CA","CB",1), ("CA","HA",1),
115 ("CB","HB2",1), ("CB","HB3",1), ("CB","SG",1), ("CA","N",1), ("H","N",1),
116 ("H2","N",1), ("HXT","OXT",1), ("HG","SG",1)
117 ]);
118 m.insert("GLN", vec![
119 ("C","O",2), ("C","OXT",1), ("C","CA",1), ("CA","CB",1), ("CA","HA",1),
120 ("CB","CG",1), ("CB","HB2",1), ("CB","HB3",1), ("CD","NE2",1), ("CD","OE1",2),
121 ("CD","CG",1), ("CG","HG2",1), ("CG","HG3",1), ("CA","N",1), ("H","N",1),
122 ("H2","N",1), ("HE21","NE2",1), ("HE22","NE2",1), ("HXT","OXT",1)
123 ]);
124 m.insert("GLU", vec![
125 ("C","O",2), ("C","OXT",1), ("C","CA",1), ("CA","CB",1), ("CA","HA",1),
126 ("CB","CG",1), ("CB","HB2",1), ("CB","HB3",1), ("CD","OE1",2), ("CD","OE2",1),
127 ("CD","CG",1), ("CG","HG2",1), ("CG","HG3",1), ("CA","N",1), ("H","N",1),
128 ("H2","N",1), ("HE2","OE2",1), ("HXT","OXT",1)
129 ]);
130 m.insert("GLY", vec![
131 ("C","O",2), ("C","OXT",1), ("C","CA",1), ("CA","HA2",1), ("CA","HA3",1),
132 ("CA","N",1), ("H","N",1), ("H2","N",1), ("HXT","OXT",1)
133 ]);
134 m.insert("HIS", vec![
135 ("C","O",2), ("C","OXT",1), ("C","CA",1), ("CA","CB",1), ("CA","HA",1),
136 ("CB","CG",1), ("CB","HB2",1), ("CB","HB3",1), ("CD2","HD2",1), ("CD2","NE2",5),
137 ("CE1","HE1",1), ("CE1","NE2",5), ("CD2","CG",6), ("CG","ND1",5), ("CA","N",1),
138 ("H","N",1), ("H2","N",1), ("CE1","ND1",6), ("HD1","ND1",1), ("HE2","NE2",1),
139 ("HXT","OXT",1)
140 ]);
141 m.insert("ILE", vec![
142 ("C","O",2), ("C","OXT",1), ("C","CA",1), ("CA","CB",1), ("CA","HA",1),
143 ("CB","CG1",1), ("CB","CG2",1), ("CB","HB",1), ("CD1","HD11",1), ("CD1","HD12",1),
144 ("CD1","HD13",1), ("CD1","CG1",1), ("CG1","HG12",1), ("CG1","HG13",1),
145 ("CG2","HG21",1), ("CG2","HG22",1), ("CG2","HG23",1), ("CA","N",1), ("H","N",1),
146 ("H2","N",1), ("HXT","OXT",1)
147 ]);
148 m.insert("LEU", vec![
149 ("C","O",2), ("C","OXT",1), ("C","CA",1), ("CA","CB",1), ("CA","HA",1),
150 ("CB","CG",1), ("CB","HB2",1), ("CB","HB3",1), ("CD1","HD11",1), ("CD1","HD12",1),
151 ("CD1","HD13",1), ("CD2","HD21",1), ("CD2","HD22",1), ("CD2","HD23",1),
152 ("CD1","CG",1), ("CD2","CG",1), ("CG","HG",1), ("CA","N",1), ("H","N",1),
153 ("H2","N",1), ("HXT","OXT",1)
154 ]);
155 m.insert("LYS", vec![
156 ("C","O",2), ("C","OXT",1), ("C","CA",1), ("CA","CB",1), ("CA","HA",1),
157 ("CB","CG",1), ("CB","HB2",1), ("CB","HB3",1), ("CD","CE",1), ("CD","HD2",1),
158 ("CD","HD3",1), ("CE","HE2",1), ("CE","HE3",1), ("CE","NZ",1), ("CD","CG",1),
159 ("CG","HG2",1), ("CG","HG3",1), ("CA","N",1), ("H","N",1), ("H2","N",1),
160 ("HZ1","NZ",1), ("HZ2","NZ",1), ("HZ3","NZ",1), ("HXT","OXT",1)
161 ]);
162 m.insert("MET", vec![
163 ("C","O",2), ("C","OXT",1), ("C","CA",1), ("CA","CB",1), ("CA","HA",1),
164 ("CB","CG",1), ("CB","HB2",1), ("CB","HB3",1), ("CE","HE1",1), ("CE","HE2",1),
165 ("CE","HE3",1), ("CG","HG2",1), ("CG","HG3",1), ("CG","SD",1), ("CA","N",1),
166 ("H","N",1), ("H2","N",1), ("HXT","OXT",1), ("CE","SD",1)
167 ]);
168 m.insert("PHE", vec![
169 ("C","O",2), ("C","OXT",1), ("C","CA",1), ("CA","CB",1), ("CA","HA",1),
170 ("CB","CG",1), ("CB","HB2",1), ("CB","HB3",1), ("CD1","CE1",5), ("CD1","HD1",1),
171 ("CD2","CE2",6), ("CD2","HD2",1), ("CE1","CZ",6), ("CE1","HE1",1), ("CE2","CZ",5),
172 ("CE2","HE2",1), ("CD1","CG",6), ("CD2","CG",5), ("CZ","HZ",1), ("CA","N",1),
173 ("H","N",1), ("H2","N",1), ("HXT","OXT",1)
174 ]);
175 m.insert("PRO", vec![
176 ("C","O",2), ("C","OXT",1), ("C","CA",1), ("CA","CB",1), ("CA","HA",1),
177 ("CB","CG",1), ("CB","HB2",1), ("CB","HB3",1), ("CD","HD2",1), ("CD","HD3",1),
178 ("CD","CG",1), ("CG","HG2",1), ("CG","HG3",1), ("CA","N",1), ("CD","N",1),
179 ("H","N",1), ("HXT","OXT",1)
180 ]);
181 m.insert("SER", vec![
182 ("C","O",2), ("C","OXT",1), ("C","CA",1), ("CA","CB",1), ("CA","HA",1),
183 ("CB","HB2",1), ("CB","HB3",1), ("CB","OG",1), ("CA","N",1), ("H","N",1),
184 ("H2","N",1), ("HG","OG",1), ("HXT","OXT",1)
185 ]);
186 m.insert("THR", vec![
187 ("C","O",2), ("C","OXT",1), ("C","CA",1), ("CA","CB",1), ("CA","HA",1),
188 ("CB","CG2",1), ("CB","HB",1), ("CB","OG1",1), ("CG2","HG21",1), ("CG2","HG22",1),
189 ("CG2","HG23",1), ("CA","N",1), ("H","N",1), ("H2","N",1), ("HG1","OG1",1),
190 ("HXT","OXT",1)
191 ]);
192 m.insert("TRP", vec![
193 ("C","O",2), ("C","OXT",1), ("C","CA",1), ("CA","CB",1), ("CA","HA",1),
194 ("CB","CG",1), ("CB","HB2",1), ("CB","HB3",1), ("CD1","HD1",1), ("CD1","NE1",5),
195 ("CD2","CE2",6), ("CD2","CE3",5), ("CE2","CZ2",5), ("CE3","CZ3",6), ("CE3","HE3",1),
196 ("CD1","CG",6), ("CD2","CG",5), ("CH2","HH2",1), ("CH2","CZ2",6), ("CZ2","HZ2",1),
197 ("CH2","CZ3",5), ("CZ3","HZ3",1), ("CA","N",1), ("H","N",1), ("H2","N",1),
198 ("CE2","NE1",5), ("HE1","NE1",1), ("HXT","OXT",1)
199 ]);
200 m.insert("TYR", vec![
201 ("C","O",2), ("C","OXT",1), ("C","CA",1), ("CA","CB",1), ("CA","HA",1),
202 ("CB","CG",1), ("CB","HB2",1), ("CB","HB3",1), ("CD1","CE1",5), ("CD1","HD1",1),
203 ("CD2","CE2",6), ("CD2","HD2",1), ("CE1","CZ",6), ("CE1","HE1",1), ("CE2","CZ",5),
204 ("CE2","HE2",1), ("CD1","CG",6), ("CD2","CG",5), ("CZ","OH",1), ("CA","N",1),
205 ("H","N",1), ("H2","N",1), ("HH","OH",1), ("HXT","OXT",1)
206 ]);
207 m.insert("VAL", vec![
208 ("C","O",2), ("C","OXT",1), ("C","CA",1), ("CA","CB",1), ("CA","HA",1),
209 ("CB","CG1",1), ("CB","CG2",1), ("CB","HB",1), ("CG1","HG11",1), ("CG1","HG12",1),
210 ("CG1","HG13",1), ("CG2","HG21",1), ("CG2","HG22",1), ("CG2","HG23",1),
211 ("CA","N",1), ("H","N",1), ("H2","N",1), ("HXT","OXT",1)
212 ]);
213 m
214 })
215}
216
217static AMINO_ACIDS: OnceLock<HashSet<&'static str>> = OnceLock::new();
218static CARBOHYDRATES: OnceLock<HashSet<&'static str>> = OnceLock::new();
219static NUCLEOTIDES: OnceLock<HashSet<&'static str>> = OnceLock::new();
220
221fn get_amino_acids() -> &'static HashSet<&'static str> {
222 AMINO_ACIDS.get_or_init(|| include_str!("ccddata/amino_acids.txt").lines().collect())
223}
224
225fn get_carbohydrates() -> &'static HashSet<&'static str> {
226 CARBOHYDRATES.get_or_init(|| include_str!("ccddata/carbohydrates.txt").lines().collect())
227}
228
229fn get_nucleotides() -> &'static HashSet<&'static str> {
230 NUCLEOTIDES.get_or_init(|| include_str!("ccddata/nucleotides.txt").lines().collect())
231}
232
233pub(crate) fn is_amino_acid(symbol: &str) -> bool {
234 get_amino_acids().contains(symbol)
235}
236
237pub(crate) fn is_carbohydrate(symbol: &str) -> bool {
238 get_carbohydrates().contains(symbol)
239}
240
241pub(crate) fn is_nucleotide(symbol: &str) -> bool {
242 get_nucleotides().contains(symbol)
243}
244
245#[cfg(test)]
246mod tests {
247 use super::*;
248
249 #[test]
250 fn test_residue_checking() {
251 assert!(is_amino_acid("ALA"));
252 assert!(is_amino_acid("ARG"));
253 assert!(!is_amino_acid("ZZZ"));
254
255 assert!(is_carbohydrate("045"));
256 assert!(is_carbohydrate("05L"));
257 assert!(!is_carbohydrate("ZZZ"));
258
259 assert!(is_nucleotide("02I"));
260 assert!(is_nucleotide("05A"));
261 assert!(!is_nucleotide("ZZZ"));
262 }
263}