1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4use core::fmt;
5
6#[derive(Debug, Clone, PartialEq, Eq)]
8pub struct SchlafliSymbol {
9 entries: Vec<usize>,
10}
11
12impl SchlafliSymbol {
13 #[must_use]
15 pub fn new(entries: Vec<usize>) -> Option<Self> {
16 if !entries.is_empty() && entries.iter().all(|entry| *entry >= 2) {
17 Some(Self { entries })
18 } else {
19 None
20 }
21 }
22
23 #[must_use]
25 pub fn polygon(p: usize) -> Option<Self> {
26 if p >= 3 { Self::new(vec![p]) } else { None }
27 }
28
29 #[must_use]
31 pub fn polyhedron(p: usize, q: usize) -> Option<Self> {
32 if p >= 3 && q >= 3 {
33 Self::new(vec![p, q])
34 } else {
35 None
36 }
37 }
38
39 #[must_use]
41 pub fn polychoron(p: usize, q: usize, r: usize) -> Option<Self> {
42 if p >= 3 && q >= 3 && r >= 3 {
43 Self::new(vec![p, q, r])
44 } else {
45 None
46 }
47 }
48
49 #[must_use]
51 pub fn entries(&self) -> &[usize] {
52 &self.entries
53 }
54
55 #[must_use]
57 pub fn rank(&self) -> usize {
58 self.entries.len() + 1
59 }
60
61 #[must_use]
63 pub fn dimension(&self) -> usize {
64 self.entries.len()
65 }
66}
67
68impl fmt::Display for SchlafliSymbol {
69 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
70 formatter.write_str("{")?;
71
72 for (index, entry) in self.entries.iter().enumerate() {
73 if index > 0 {
74 formatter.write_str(", ")?;
75 }
76
77 write!(formatter, "{entry}")?;
78 }
79
80 formatter.write_str("}")
81 }
82}
83
84#[cfg(test)]
85mod tests {
86 use super::SchlafliSymbol;
87
88 #[test]
89 fn formats_schlafli_symbols() {
90 let symbol = SchlafliSymbol::polychoron(3, 4, 3).expect("valid symbol");
91
92 assert_eq!(symbol.entries(), &[3, 4, 3]);
93 assert_eq!(symbol.rank(), 4);
94 assert_eq!(symbol.dimension(), 3);
95 assert_eq!(symbol.to_string(), "{3, 4, 3}");
96 assert_eq!(SchlafliSymbol::polygon(2), None);
97 }
98}