1use crate::util::{python_mod, Axis, Tagged};
2use serde::{Deserialize, Serialize};
3use serde_json::Error as SerdeError;
4use std::str::FromStr;
5
6#[derive(Clone, Debug, Serialize, Deserialize)]
41pub struct Vertex {
42 source: usize,
43 target: usize,
44 delta: (i32, i32, i32),
45 tags: Option<Vec<String>>,
46}
47
48impl FromStr for Vertex {
49 type Err = SerdeError;
50 fn from_str(source: &str) -> Result<Vertex, Self::Err> {
51 serde_json::from_str(source)
52 }
53}
54
55impl Tagged for Vertex {
56 fn tags(&self) -> Option<Vec<&str>> {
57 self.tags
58 .as_ref()
59 .map(|tags| tags.iter().map(|tag| tag.as_ref()).collect())
60 }
61}
62
63impl Vertex {
64 pub fn new(source: usize, target: usize, delta: (i32, i32, i32)) -> Self {
66 Vertex {
67 source,
68 target,
69 delta,
70 tags: None,
71 }
72 }
73
74 pub fn source(&self) -> usize {
76 self.source
77 }
78
79 pub fn target(&self) -> usize {
81 self.target
82 }
83
84 pub fn delta(&self) -> (i32, i32, i32) {
86 self.delta
87 }
88
89 pub fn with_tags(mut self, tags: Vec<&str>) -> Self {
91 self.tags = Some(tags.iter().map(|s| s.to_string()).collect());
92 self
93 }
94
95 #[inline]
96 fn delta_along(&self, axis: Axis) -> i32 {
97 match axis {
98 Axis::X => self.delta.0,
99 Axis::Y => self.delta.1,
100 Axis::Z => self.delta.2,
101 }
102 }
103
104 fn move_along(mut self, axis: Axis, amount: usize, nsites: usize, limit: usize) -> Self {
123 debug_assert!(amount < limit);
124 let distance = amount * nsites;
125 let new_nsites = limit * nsites;
126 self.source += distance;
127 self.target += distance;
128 let delta = self.delta_along(axis);
129 let target = self.target as i32 + delta * nsites as i32;
130 let (target, delta) = python_mod(target, new_nsites);
131 self.target = target as usize;
132 match axis {
133 Axis::X => self.delta.0 = delta,
134 Axis::Y => self.delta.1 = delta,
135 Axis::Z => self.delta.2 = delta,
136 };
137 self
138 }
139
140 pub fn move_x(self, amount: usize, nsites: usize, limit: usize) -> Self {
142 self.move_along(Axis::X, amount, nsites, limit)
143 }
144
145 pub fn move_y(self, amount: usize, nsites: usize, limit: usize) -> Self {
147 self.move_along(Axis::Y, amount, nsites, limit)
148 }
149
150 pub fn move_z(self, amount: usize, nsites: usize, limit: usize) -> Self {
152 self.move_along(Axis::Z, amount, nsites, limit)
153 }
154
155 pub fn reindex(mut self, index: &[usize]) -> Self {
157 self.source = index[self.source];
158 self.target = index[self.target];
159 self
160 }
161}
162
163#[cfg(test)]
164mod test {
165 use super::Vertex;
166
167 #[test]
168 fn vertex_can_be_created() {
169 let vertex = Vertex::new(0, 1, (0, 0, 1));
170 assert_eq!(vertex.source, 0);
171 assert_eq!(vertex.target, 1);
172 assert_eq!(vertex.delta, (0, 0, 1));
173 }
174
175 #[test]
176 fn vertex_can_be_moved() {
177 let vertex = Vertex::new(0, 1, (0, 0, 1)).move_along(super::Axis::Z, 0, 2, 2);
179 assert_eq!(vertex.source, 0);
180 assert_eq!(vertex.target, 3);
181 assert_eq!(vertex.delta, (0, 0, 0));
182 }
183
184 #[test]
185 fn vertex_can_be_moved_2() {
186 let vertex = Vertex::new(0, 1, (0, 0, 0)).move_along(super::Axis::Z, 0, 2, 2);
188 assert_eq!(vertex.source, 0);
189 assert_eq!(vertex.target, 1);
190 assert_eq!(vertex.delta, (0, 0, 0));
191 }
192
193 #[test]
194 fn vertex_can_be_moved_3() {
195 let vertex = Vertex::new(0, 1, (0, 0, 0)).move_along(super::Axis::Z, 1, 2, 2);
197 assert_eq!(vertex.source, 2);
198 assert_eq!(vertex.target, 3);
199 assert_eq!(vertex.delta, (0, 0, 0));
200 }
201
202 #[test]
203 fn vertex_can_be_moved_4() {
204 let vertex = Vertex::new(0, 1, (0, 0, 1)).move_along(super::Axis::Z, 1, 2, 2);
207 assert_eq!(vertex.source, 2);
208 assert_eq!(vertex.target, 1);
209 assert_eq!(vertex.delta, (0, 0, 1));
210 }
211
212 #[test]
213 fn vertex_can_be_tagged() {
214 let vertex = Vertex::new(0, 1, (0, 0, 1)).with_tags(vec!["core"]);
215 assert_eq!(vertex.tags, Some(vec!["core".to_string()]));
216 }
217
218 #[test]
219 fn reindexing_kinda_works() {
220 let vertex = Vertex::new(0, 1, (0, 0, 1)).reindex(&[1, 0]);
221 assert_eq!(vertex.source, 1);
222 assert_eq!(vertex.target, 0);
223 }
224
225 #[test]
226 fn vertex_will_take_optional_tags() {
227 let data = r#"
228 {"source": 0, "target": 0, "delta": [0, 0, 1], "tags": ["core", "inner"]}
229 "#;
230 let vertex_result: Result<Vertex, _> = data.parse();
231 assert!(vertex_result.is_ok());
232 assert_eq!(
233 vertex_result.unwrap().tags,
234 Some(vec!["core".to_string(), "inner".to_string()])
235 );
236 }
237}