use crate::{mesh_error::MeshSieveError, topology::point::PointId};
#[derive(Clone, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub struct Arrow<P = ()> {
pub src: PointId,
pub dst: PointId,
pub payload: P,
}
impl<P> Arrow<P> {
#[inline]
pub fn try_new_from_raw(src_id: u64, dst_id: u64, payload: P) -> Result<Self, MeshSieveError> {
let src = PointId::new(src_id)?;
let dst = PointId::new(dst_id)?;
Ok(Arrow { src, dst, payload })
}
#[inline]
pub fn new(src: PointId, dst: PointId, payload: P) -> Self {
Arrow { src, dst, payload }
}
#[inline]
pub fn endpoints(&self) -> (PointId, PointId) {
(self.src, self.dst)
}
#[inline]
pub fn map<Q>(self, f: impl FnOnce(P) -> Q) -> Arrow<Q> {
Arrow {
src: self.src,
dst: self.dst,
payload: f(self.payload),
}
}
}
impl Arrow<()> {
#[inline]
pub fn unit(src: PointId, dst: PointId) -> Self {
Arrow::new(src, dst, ())
}
}
impl Default for Arrow<()> {
fn default() -> Self {
let p1 = unsafe { PointId::new_unchecked(1) };
Arrow::new(p1, p1, ())
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
#[repr(u8)]
pub enum Polarity {
Forward = 0,
Reverse = 1,
}
impl Default for Polarity {
#[inline]
fn default() -> Self {
Polarity::Forward
}
}
impl Polarity {
#[inline]
pub fn invert(self) -> Self {
match self {
Polarity::Forward => Polarity::Reverse,
Polarity::Reverse => Polarity::Forward,
}
}
#[inline]
pub fn compose(self, other: Self) -> Self {
match ((self as u8) ^ (other as u8)) & 1 {
0 => Polarity::Forward,
_ => Polarity::Reverse,
}
}
#[inline]
pub fn as_bool(self) -> bool {
matches!(self, Polarity::Reverse)
}
#[inline]
pub fn from_bool(b: bool) -> Self {
if b {
Polarity::Reverse
} else {
Polarity::Forward
}
}
#[inline]
pub fn sign(self) -> i8 {
if self.as_bool() { -1 } else { 1 }
}
}
impl core::ops::Not for Polarity {
type Output = Self;
#[inline]
fn not(self) -> Self::Output {
self.invert()
}
}
impl core::ops::BitXor for Polarity {
type Output = Self;
#[inline]
fn bitxor(self, rhs: Self) -> Self::Output {
self.compose(rhs)
}
}
impl From<bool> for Polarity {
#[inline]
fn from(b: bool) -> Self {
Polarity::from_bool(b)
}
}
impl From<Polarity> for bool {
#[inline]
fn from(p: Polarity) -> bool {
p.as_bool()
}
}
impl From<Polarity> for crate::topology::orientation::Sign {
#[inline]
fn from(p: Polarity) -> Self {
crate::topology::orientation::BitFlip(p.as_bool())
}
}
impl From<crate::topology::orientation::Sign> for Polarity {
#[inline]
fn from(s: crate::topology::orientation::Sign) -> Self {
Polarity::from_bool(s.0)
}
}
#[allow(deprecated)]
#[deprecated(
since = "0.X.Y",
note = "Renamed to `Polarity` to avoid confusion with `sieve::oriented::Orientation`."
)]
pub use Polarity as Orientation;
#[cfg(test)]
mod tests {
use super::*;
use crate::topology::point::PointId;
#[test]
fn build_and_endpoints() {
let src = PointId::new(1).unwrap();
let dst = PointId::new(2).unwrap();
let arrow = Arrow::unit(src, dst);
assert_eq!(arrow.endpoints(), (src, dst));
}
#[test]
fn payload_map() {
let arrow = Arrow::new(PointId::new(3).unwrap(), PointId::new(4).unwrap(), 10u32);
let arrow2 = arrow.map(|v| v * 2);
assert_eq!(arrow2.payload, 20);
}
#[test]
fn equality_and_hash() {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
let a1 = Arrow::unit(PointId::new(5).unwrap(), PointId::new(6).unwrap());
let a2 = Arrow::unit(PointId::new(5).unwrap(), PointId::new(6).unwrap());
let mut h1 = DefaultHasher::new();
let mut h2 = DefaultHasher::new();
a1.hash(&mut h1);
a2.hash(&mut h2);
assert_eq!(h1.finish(), h2.finish());
}
#[test]
fn new_stores_payload() {
let a = Arrow::new(PointId::new(7).unwrap(), PointId::new(8).unwrap(), "foo");
assert_eq!(a.src.get(), 7);
assert_eq!(a.dst.get(), 8);
assert_eq!(a.payload, "foo");
}
#[test]
fn default_is_unit_on_1() {
let d = Arrow::<()>::default();
assert_eq!(
d,
Arrow::unit(PointId::new(1).unwrap(), PointId::new(1).unwrap())
);
}
#[test]
fn debug_prints_struct() {
let a = Arrow::new(PointId::new(1).unwrap(), PointId::new(2).unwrap(), 99u8);
let dbg = format!("{:?}", a);
assert!(dbg.contains("Arrow") && dbg.contains("99"));
}
#[test]
fn clone_eqs_original() {
let a = Arrow::new(
PointId::new(3).unwrap(),
PointId::new(4).unwrap(),
vec![1, 2, 3],
);
let b = a.clone();
assert_eq!(a, b);
}
#[test]
fn arrow_with_zero_payload() {
let a = Arrow::unit(PointId::new(1).unwrap(), PointId::new(2).unwrap());
let b = a.clone().map(|()| ());
assert_eq!(b.payload, ());
}
#[test]
fn serde_arrow_roundtrip() -> Result<(), Box<dyn std::error::Error>> {
let a = Arrow::new(PointId::new(1).unwrap(), PointId::new(2).unwrap(), vec![10]);
let json = serde_json::to_string(&a)?;
let a2: Arrow<Vec<u8>> = serde_json::from_str(&json)?;
assert_eq!(a, a2);
Ok(())
}
}