1use super::types::{Element, Point};
9use std::fmt;
10
11#[derive(Debug, Clone, PartialEq)]
17pub struct Atom {
18 pub name: String,
20 pub element: Element,
22 pub pos: Point,
24}
25
26impl Atom {
27 pub fn new(name: &str, element: Element, pos: Point) -> Self {
42 Self {
43 name: name.to_string(),
44 element,
45 pos,
46 }
47 }
48
49 pub fn distance_squared(&self, other: &Atom) -> f64 {
62 nalgebra::distance_squared(&self.pos, &other.pos)
63 }
64
65 pub fn distance(&self, other: &Atom) -> f64 {
78 nalgebra::distance(&self.pos, &other.pos)
79 }
80
81 pub fn translate_by(&mut self, vector: &nalgebra::Vector3<f64>) {
90 self.pos += vector;
91 }
92}
93
94impl fmt::Display for Atom {
95 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
96 write!(
97 f,
98 "Atom {{ name: \"{}\", element: {}, pos: [{:.3}, {:.3}, {:.3}] }}",
99 self.name, self.element, self.pos.x, self.pos.y, self.pos.z
100 )
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107
108 #[test]
109 fn atom_new_creates_correct_atom() {
110 let pos = Point::new(1.0, 2.0, 3.0);
111 let atom = Atom::new("C1", Element::C, pos);
112
113 assert_eq!(atom.name, "C1");
114 assert_eq!(atom.element, Element::C);
115 assert_eq!(atom.pos, pos);
116 }
117
118 #[test]
119 fn atom_distance_squared_calculates_correctly() {
120 let atom1 = Atom::new("A", Element::H, Point::new(0.0, 0.0, 0.0));
121 let atom2 = Atom::new("B", Element::H, Point::new(3.0, 4.0, 0.0));
122
123 let dist_sq = atom1.distance_squared(&atom2);
124 assert!((dist_sq - 25.0).abs() < 1e-10);
125 }
126
127 #[test]
128 fn atom_distance_calculates_correctly() {
129 let atom1 = Atom::new("A", Element::H, Point::new(0.0, 0.0, 0.0));
130 let atom2 = Atom::new("B", Element::H, Point::new(3.0, 4.0, 0.0));
131
132 let dist = atom1.distance(&atom2);
133 assert!((dist - 5.0).abs() < 1e-10);
134 }
135
136 #[test]
137 fn atom_distance_squared_zero_for_same_position() {
138 let pos = Point::new(1.5, -2.3, 4.7);
139 let atom1 = Atom::new("A", Element::O, pos);
140 let atom2 = Atom::new("B", Element::O, pos);
141
142 let dist_sq = atom1.distance_squared(&atom2);
143 assert!((dist_sq - 0.0).abs() < 1e-10);
144 }
145
146 #[test]
147 fn atom_distance_zero_for_same_position() {
148 let pos = Point::new(1.5, -2.3, 4.7);
149 let atom1 = Atom::new("A", Element::O, pos);
150 let atom2 = Atom::new("B", Element::O, pos);
151
152 let dist = atom1.distance(&atom2);
153 assert!((dist - 0.0).abs() < 1e-10);
154 }
155
156 #[test]
157 fn atom_translate_by_updates_position_correctly() {
158 let mut atom = Atom::new("Test", Element::N, Point::new(1.0, 2.0, 3.0));
159 let vector = nalgebra::Vector3::new(0.5, -1.0, 2.5);
160
161 atom.translate_by(&vector);
162
163 assert!((atom.pos.x - 1.5).abs() < 1e-10);
164 assert!((atom.pos.y - 1.0).abs() < 1e-10);
165 assert!((atom.pos.z - 5.5).abs() < 1e-10);
166 }
167
168 #[test]
169 fn atom_translate_by_with_zero_vector_no_change() {
170 let mut atom = Atom::new("Test", Element::N, Point::new(1.0, 2.0, 3.0));
171 let original_pos = atom.pos;
172 let vector = nalgebra::Vector3::new(0.0, 0.0, 0.0);
173
174 atom.translate_by(&vector);
175
176 assert_eq!(atom.pos, original_pos);
177 }
178
179 #[test]
180 fn atom_display_formats_correctly() {
181 let atom = Atom::new("CA", Element::C, Point::new(1.234, -5.678, 9.012));
182
183 let display = format!("{}", atom);
184 let expected = "Atom { name: \"CA\", element: C, pos: [1.234, -5.678, 9.012] }";
185
186 assert_eq!(display, expected);
187 }
188
189 #[test]
190 fn atom_display_with_unknown_element() {
191 let atom = Atom::new("UNK", Element::Unknown, Point::new(0.0, 0.0, 0.0));
192
193 let display = format!("{}", atom);
194 let expected = "Atom { name: \"UNK\", element: Unknown, pos: [0.000, 0.000, 0.000] }";
195
196 assert_eq!(display, expected);
197 }
198
199 #[test]
200 fn atom_clone_creates_identical_copy() {
201 let atom = Atom::new("CloneTest", Element::Fe, Point::new(7.89, -1.23, 4.56));
202 let cloned = atom.clone();
203
204 assert_eq!(atom, cloned);
205 assert_eq!(atom.name, cloned.name);
206 assert_eq!(atom.element, cloned.element);
207 assert_eq!(atom.pos, cloned.pos);
208 }
209
210 #[test]
211 fn atom_partial_eq_compares_correctly() {
212 let atom1 = Atom::new("Test", Element::O, Point::new(1.0, 2.0, 3.0));
213 let atom2 = Atom::new("Test", Element::O, Point::new(1.0, 2.0, 3.0));
214 let atom3 = Atom::new("Different", Element::O, Point::new(1.0, 2.0, 3.0));
215 let atom4 = Atom::new("Test", Element::N, Point::new(1.0, 2.0, 3.0));
216 let atom5 = Atom::new("Test", Element::O, Point::new(1.1, 2.0, 3.0));
217
218 assert_eq!(atom1, atom2);
219 assert_ne!(atom1, atom3);
220 assert_ne!(atom1, atom4);
221 assert_ne!(atom1, atom5);
222 }
223
224 #[test]
225 fn atom_distance_with_negative_coordinates() {
226 let atom1 = Atom::new("A", Element::H, Point::new(-1.0, -2.0, -3.0));
227 let atom2 = Atom::new("B", Element::H, Point::new(1.0, 2.0, 3.0));
228
229 let dist_sq = atom1.distance_squared(&atom2);
230 assert!((dist_sq - 56.0).abs() < 1e-10);
231
232 let dist = atom1.distance(&atom2);
233 assert!((dist - (56.0_f64).sqrt()).abs() < 1e-10);
234 }
235
236 #[test]
237 fn atom_translate_by_multiple_times_accumulates() {
238 let mut atom = Atom::new("Test", Element::C, Point::new(0.0, 0.0, 0.0));
239
240 atom.translate_by(&nalgebra::Vector3::new(1.0, 0.0, 0.0));
241 atom.translate_by(&nalgebra::Vector3::new(0.0, 2.0, 0.0));
242 atom.translate_by(&nalgebra::Vector3::new(0.0, 0.0, 3.0));
243
244 assert!((atom.pos.x - 1.0).abs() < 1e-10);
245 assert!((atom.pos.y - 2.0).abs() < 1e-10);
246 assert!((atom.pos.z - 3.0).abs() < 1e-10);
247 }
248}