#![forbid(unsafe_code)]
#![doc = include_str!("../README.md")]
use use_schlafli::SchlafliSymbol;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct RegularPolygon {
side_count: usize,
circumradius: f64,
}
impl RegularPolygon {
#[must_use]
pub const fn new(side_count: usize, circumradius: f64) -> Option<Self> {
if side_count >= 3 && circumradius.is_finite() && circumradius > 0.0 {
Some(Self {
side_count,
circumradius,
})
} else {
None
}
}
#[must_use]
pub const fn side_count(self) -> usize {
self.side_count
}
#[must_use]
pub const fn circumradius(self) -> f64 {
self.circumradius
}
#[must_use]
pub fn schlafli_symbol(self) -> SchlafliSymbol {
SchlafliSymbol::polygon(self.side_count).expect("regular polygon side count is valid")
}
}
pub type RegularPolytope2 = RegularPolygon;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PlatonicSolid {
Tetrahedron,
Cube,
Octahedron,
Dodecahedron,
Icosahedron,
}
impl PlatonicSolid {
#[must_use]
pub const fn name(self) -> &'static str {
match self {
Self::Tetrahedron => "tetrahedron",
Self::Cube => "cube",
Self::Octahedron => "octahedron",
Self::Dodecahedron => "dodecahedron",
Self::Icosahedron => "icosahedron",
}
}
#[must_use]
pub const fn face_count(self) -> usize {
match self {
Self::Tetrahedron => 4,
Self::Cube => 6,
Self::Octahedron => 8,
Self::Dodecahedron => 12,
Self::Icosahedron => 20,
}
}
#[must_use]
pub const fn edge_count(self) -> usize {
match self {
Self::Tetrahedron => 6,
Self::Cube => 12,
Self::Octahedron => 12,
Self::Dodecahedron => 30,
Self::Icosahedron => 30,
}
}
#[must_use]
pub const fn vertex_count(self) -> usize {
match self {
Self::Tetrahedron => 4,
Self::Cube => 8,
Self::Octahedron => 6,
Self::Dodecahedron => 20,
Self::Icosahedron => 12,
}
}
#[must_use]
pub fn schlafli_symbol(self) -> SchlafliSymbol {
let (p, q) = match self {
Self::Tetrahedron => (3, 3),
Self::Cube => (4, 3),
Self::Octahedron => (3, 4),
Self::Dodecahedron => (5, 3),
Self::Icosahedron => (3, 5),
};
SchlafliSymbol::polyhedron(p, q).expect("Platonic solid Schlafli entries are valid")
}
}
pub type RegularPolytope3 = PlatonicSolid;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct FiveCell;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Tesseract;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SixteenCell;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct TwentyFourCell;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SixHundredCell;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct OneHundredTwentyCell;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RegularPolytope4 {
FiveCell,
Tesseract,
SixteenCell,
TwentyFourCell,
SixHundredCell,
OneHundredTwentyCell,
}
impl RegularPolytope4 {
#[must_use]
pub const fn name(self) -> &'static str {
match self {
Self::FiveCell => "5-cell",
Self::Tesseract => "tesseract",
Self::SixteenCell => "16-cell",
Self::TwentyFourCell => "24-cell",
Self::SixHundredCell => "600-cell",
Self::OneHundredTwentyCell => "120-cell",
}
}
#[must_use]
pub const fn vertex_count(self) -> usize {
match self {
Self::FiveCell => 5,
Self::Tesseract => 16,
Self::SixteenCell => 8,
Self::TwentyFourCell => 24,
Self::SixHundredCell => 120,
Self::OneHundredTwentyCell => 600,
}
}
#[must_use]
pub const fn edge_count(self) -> usize {
match self {
Self::FiveCell => 10,
Self::Tesseract => 32,
Self::SixteenCell => 24,
Self::TwentyFourCell => 96,
Self::SixHundredCell => 720,
Self::OneHundredTwentyCell => 1200,
}
}
#[must_use]
pub const fn face_count(self) -> usize {
match self {
Self::FiveCell => 10,
Self::Tesseract => 24,
Self::SixteenCell => 32,
Self::TwentyFourCell => 96,
Self::SixHundredCell => 1200,
Self::OneHundredTwentyCell => 720,
}
}
#[must_use]
pub const fn cell_count(self) -> usize {
match self {
Self::FiveCell => 5,
Self::Tesseract => 8,
Self::SixteenCell => 16,
Self::TwentyFourCell => 24,
Self::SixHundredCell => 600,
Self::OneHundredTwentyCell => 120,
}
}
#[must_use]
pub fn schlafli_symbol(self) -> SchlafliSymbol {
let (p, q, r) = match self {
Self::FiveCell => (3, 3, 3),
Self::Tesseract => (4, 3, 3),
Self::SixteenCell => (3, 3, 4),
Self::TwentyFourCell => (3, 4, 3),
Self::SixHundredCell => (3, 3, 5),
Self::OneHundredTwentyCell => (5, 3, 3),
};
SchlafliSymbol::polychoron(p, q, r).expect("regular 4-polytope entries are valid")
}
}
impl FiveCell {
#[must_use]
pub const fn family() -> RegularPolytope4 {
RegularPolytope4::FiveCell
}
}
impl Tesseract {
#[must_use]
pub const fn family() -> RegularPolytope4 {
RegularPolytope4::Tesseract
}
}
impl SixteenCell {
#[must_use]
pub const fn family() -> RegularPolytope4 {
RegularPolytope4::SixteenCell
}
}
impl TwentyFourCell {
#[must_use]
pub const fn family() -> RegularPolytope4 {
RegularPolytope4::TwentyFourCell
}
}
impl SixHundredCell {
#[must_use]
pub const fn family() -> RegularPolytope4 {
RegularPolytope4::SixHundredCell
}
}
impl OneHundredTwentyCell {
#[must_use]
pub const fn family() -> RegularPolytope4 {
RegularPolytope4::OneHundredTwentyCell
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum RegularPolytope {
Polygon(RegularPolytope2),
PlatonicSolid(RegularPolytope3),
Polytope4(RegularPolytope4),
}
impl RegularPolytope {
#[must_use]
pub const fn dimension(self) -> usize {
match self {
Self::Polygon(_) => 2,
Self::PlatonicSolid(_) => 3,
Self::Polytope4(_) => 4,
}
}
#[must_use]
pub fn schlafli_symbol(self) -> SchlafliSymbol {
match self {
Self::Polygon(polygon) => polygon.schlafli_symbol(),
Self::PlatonicSolid(solid) => solid.schlafli_symbol(),
Self::Polytope4(polytope) => polytope.schlafli_symbol(),
}
}
}
#[cfg(test)]
mod tests {
use super::{PlatonicSolid, RegularPolygon, RegularPolytope, RegularPolytope4};
#[test]
fn stores_regular_polygon_data() {
let polygon = RegularPolygon::new(6, 2.0).expect("valid polygon");
assert_eq!(polygon.side_count(), 6);
assert_eq!(polygon.circumradius(), 2.0);
assert_eq!(polygon.schlafli_symbol().to_string(), "{6}");
assert_eq!(RegularPolygon::new(2, 2.0), None);
}
#[test]
fn exposes_platonic_solid_counts() {
assert_eq!(PlatonicSolid::Icosahedron.face_count(), 20);
assert_eq!(PlatonicSolid::Cube.edge_count(), 12);
assert_eq!(PlatonicSolid::Dodecahedron.vertex_count(), 20);
assert_eq!(
PlatonicSolid::Icosahedron.schlafli_symbol().to_string(),
"{3, 5}"
);
}
#[test]
fn exposes_four_dimensional_regular_polytope_metadata() {
assert_eq!(RegularPolytope4::TwentyFourCell.name(), "24-cell");
assert_eq!(RegularPolytope4::TwentyFourCell.vertex_count(), 24);
assert_eq!(RegularPolytope4::SixHundredCell.cell_count(), 600);
assert_eq!(RegularPolytope4::OneHundredTwentyCell.face_count(), 720);
assert_eq!(
RegularPolytope4::TwentyFourCell
.schlafli_symbol()
.to_string(),
"{3, 4, 3}"
);
}
#[test]
fn wraps_regular_polytope_families() {
let polytope = RegularPolytope::Polytope4(RegularPolytope4::Tesseract);
assert_eq!(polytope.dimension(), 4);
assert_eq!(polytope.schlafli_symbol().to_string(), "{4, 3, 3}");
}
}