1use crate::feature::AtomKind;
2use super::Bond;
3
4#[derive(Debug,PartialEq)]
6pub struct Atom {
7 pub kind: AtomKind,
8 pub bonds: Vec<Bond>
9}
10
11impl Atom {
12 pub fn new(kind: AtomKind) -> Self {
14 Self {
15 kind,
16 bonds: vec![ ]
17 }
18 }
19
20 pub fn is_aromatic(&self) -> bool {
22 self.kind.is_aromatic()
23 }
24
25 pub fn subvalence(&self) -> u8 {
30 let hcount: u8 = match &self.kind {
31 AtomKind::Bracket { hcount, .. } => match hcount {
32 Some(hcount) => hcount.into(),
33 None => 0
34 }
35 _ => 0
36 };
37 let valence = self.bonds.iter().fold(hcount, |sum,bond| {
38 sum + bond.order()
39 });
40 let targets = self.kind.targets().iter()
41 .find(|&&target| target >= valence);
42
43 let target = match targets {
44 Some(target) => target,
45 None => return 0
46 };
47
48 target - valence
49 }
50
51 pub fn suppressed_hydrogens(&self) -> u8 {
54 match &self.kind {
55 AtomKind::Star => 0,
56 AtomKind::Aromatic(_) => {
57 let subvalence = self.subvalence();
58
59 if subvalence > 1 {
60 self.subvalence() - 1
61 } else {
62 0
63 }
64 },
65 AtomKind::Aliphatic(_) => self.subvalence(),
66 AtomKind::Bracket { hcount, .. } => match hcount {
67 Some(hcount) => hcount.into(),
68 None => 0
69 }
70 }
71 }
72
73}
74
75#[cfg(test)]
76mod subvalence {
77 use crate::feature::{
78 BondKind, BracketSymbol, Element, BracketAromatic, Aliphatic, Aromatic,
79 Charge, VirtualHydrogen
80 };
81 use super::*;
82
83 #[test]
84 fn star() {
85 let atom = Atom {
86 kind: AtomKind::Star,
87 bonds: vec![ ]
88 };
89
90 assert_eq!(atom.subvalence(), 0)
91 }
92
93 #[test]
94 fn star_single() {
95 let atom = Atom {
96 kind: AtomKind::Star,
97 bonds: vec![
98 Bond::new(BondKind::Single, 1)
99 ]
100 };
101
102 assert_eq!(atom.subvalence(), 0)
103 }
104
105 #[test]
106 fn carbon_single() {
107 let atom = Atom {
108 kind: AtomKind::Aliphatic(Aliphatic::C),
109 bonds: vec![
110 Bond::new(BondKind::Single, 1)
111 ]
112 };
113
114 assert_eq!(atom.subvalence(), 3)
115 }
116
117 #[test]
118 fn aromatic_carbon_single() {
119 let atom = Atom {
120 kind: AtomKind::Aromatic(Aromatic::C),
121 bonds: vec![
122 Bond::new(BondKind::Single, 1)
123 ]
124 };
125
126 assert_eq!(atom.subvalence(), 3)
127 }
128
129 #[test]
130 fn bracket_star_single() {
131 let atom = Atom {
132 kind: AtomKind::Bracket {
133 isotope: None,
134 symbol: BracketSymbol::Star,
135 configuration: None,
136 hcount: None,
137 charge: None,
138 map: None
139 },
140 bonds: vec![
141 Bond::new(BondKind::Single, 1)
142 ]
143 };
144
145 assert_eq!(atom.subvalence(), 0)
146 }
147
148 #[test]
149 fn bracket_carbon_h1() {
150 let atom = Atom {
151 kind: AtomKind::Bracket {
152 isotope: None,
153 symbol: BracketSymbol::Element(Element::C),
154 configuration: None,
155 hcount: Some(VirtualHydrogen::H1),
156 charge: None,
157 map: None
158 },
159 bonds: vec![
160
161 ]
162 };
163
164 assert_eq!(atom.subvalence(), 3)
165 }
166
167 #[test]
168 fn bracket_carbon_h0_single() {
169 let atom = Atom {
170 kind: AtomKind::Bracket {
171 isotope: None,
172 symbol: BracketSymbol::Element(Element::C),
173 configuration: None,
174 hcount: None,
175 charge: None,
176 map: None
177 },
178 bonds: vec![
179 Bond::new(BondKind::Single, 1)
180 ]
181 };
182
183 assert_eq!(atom.subvalence(), 3)
184 }
185
186 #[test]
187 fn bracket_aromatic_h0_single() {
188 let atom = Atom {
189 kind: AtomKind::Bracket {
190 isotope: None,
191 symbol: BracketSymbol::Aromatic(BracketAromatic::C),
192 configuration: None,
193 hcount: None,
194 charge: None,
195 map: None
196 },
197 bonds: vec![
198 Bond::new(BondKind::Single, 1)
199 ]
200 };
201
202 assert_eq!(atom.subvalence(), 3)
203 }
204
205 #[test]
206 fn bracket_aromatic_carbon_h1_single() {
207 let atom = Atom {
208 kind: AtomKind::Bracket {
209 isotope: None,
210 symbol: BracketSymbol::Aromatic(BracketAromatic::C),
211 configuration: None,
212 hcount: Some(VirtualHydrogen::H1),
213 charge: None,
214 map: None
215 },
216 bonds: vec![
217 Bond::new(BondKind::Single, 1)
218 ]
219 };
220
221 assert_eq!(atom.subvalence(), 2)
222 }
223
224 #[test]
225 fn sulfur_charged_divalent() {
226 let atom = Atom {
227 kind: AtomKind::Bracket {
228 isotope: None,
229 symbol: BracketSymbol::Aromatic(BracketAromatic::S),
230 configuration: None,
231 hcount: None,
232 charge: Some(Charge::One),
233 map: None
234 },
235 bonds: vec![
236 Bond::new(BondKind::Single, 1),
237 Bond::new(BondKind::Single, 2)
238 ]
239 };
240
241 assert_eq!(atom.subvalence(), 1)
242 }
243}
244
245#[cfg(test)]
246mod suppressed_hydrogens {
247 use pretty_assertions::assert_eq;
248 use crate::feature::{
249 Aromatic, Aliphatic, BondKind, BracketSymbol, Element, VirtualHydrogen
250 };
251 use super::*;
252
253 #[test]
254 fn star() {
255 let atom = Atom::new(AtomKind::Star);
256
257 assert_eq!(atom.suppressed_hydrogens(), 0)
258 }
259
260 #[test]
261 fn aromatic_subvalence_1() {
262 let atom = Atom {
263 kind: AtomKind::Aromatic(Aromatic::C),
264 bonds: vec![
265 Bond::new(BondKind::Elided, 1),
266 Bond::new(BondKind::Elided, 2),
267 Bond::new(BondKind::Elided, 3)
268 ]
269 };
270
271 assert_eq!(atom.suppressed_hydrogens(), 0)
272 }
273
274 #[test]
275 fn aromatic_subvalence_2() {
276 let atom = Atom {
277 kind: AtomKind::Aromatic(Aromatic::C),
278 bonds: vec![
279 Bond::new(BondKind::Elided, 1),
280 Bond::new(BondKind::Elided, 2)
281 ]
282 };
283
284 assert_eq!(atom.suppressed_hydrogens(), 1)
285 }
286
287 #[test]
288 fn aliphatic_subvalence_0() {
289 let atom = Atom {
290 kind: AtomKind::Aliphatic(Aliphatic::C),
291 bonds: vec![
292 Bond::new(BondKind::Elided, 1),
293 Bond::new(BondKind::Elided, 2),
294 Bond::new(BondKind::Elided, 3),
295 Bond::new(BondKind::Elided, 4)
296 ]
297 };
298
299 assert_eq!(atom.suppressed_hydrogens(), 0)
300 }
301
302 #[test]
303 fn aliphatic_subvalence_1() {
304 let atom = Atom {
305 kind: AtomKind::Aliphatic(Aliphatic::C),
306 bonds: vec![
307 Bond::new(BondKind::Elided, 1),
308 Bond::new(BondKind::Elided, 2),
309 Bond::new(BondKind::Elided, 3)
310 ]
311 };
312
313 assert_eq!(atom.suppressed_hydrogens(), 1)
314 }
315
316 #[test]
317 fn aliphatic_subvalence_2() {
318 let atom = Atom {
319 kind: AtomKind::Aliphatic(Aliphatic::C),
320 bonds: vec![
321 Bond::new(BondKind::Elided, 1),
322 Bond::new(BondKind::Elided, 2)
323 ]
324 };
325
326 assert_eq!(atom.suppressed_hydrogens(), 2)
327 }
328
329 #[test]
330 fn bracket_h_none() {
331 let atom = Atom {
332 kind: AtomKind::Bracket {
333 isotope: None,
334 symbol: BracketSymbol::Element(Element::C),
335 hcount: None,
336 charge: None,
337 configuration: None,
338 map: None
339 },
340 bonds: vec![ ]
341 };
342
343 assert_eq!(atom.suppressed_hydrogens(), 0)
344 }
345
346 #[test]
347 fn bracket_h0() {
348 let atom = Atom {
349 kind: AtomKind::Bracket {
350 isotope: None,
351 symbol: BracketSymbol::Element(Element::C),
352 hcount: Some(VirtualHydrogen::H0),
353 charge: None,
354 configuration: None,
355 map: None
356 },
357 bonds: vec![ ]
358 };
359
360 assert_eq!(atom.suppressed_hydrogens(), 0)
361 }
362
363 #[test]
364 fn bracket_h1() {
365 let atom = Atom {
366 kind: AtomKind::Bracket {
367 isotope: None,
368 symbol: BracketSymbol::Element(Element::C),
369 hcount: Some(VirtualHydrogen::H1),
370 charge: None,
371 configuration: None,
372 map: None
373 },
374 bonds: vec![ ]
375 };
376
377 assert_eq!(atom.suppressed_hydrogens(), 1)
378 }
379}