use std::{fmt, num::NonZeroU32};
use crate::error::{Error, ErrorKind, Result};
pub(crate) mod defaults {
use super::{ClosedUnitF64, FractionF64, IterationCount, NonNegativeF64, NonZeroU32, PositiveF64};
pub(crate) const PAGERANK_ALPHA: ClosedUnitF64 = ClosedUnitF64(0.85);
pub(crate) const PAGERANK_EPSILON: NonNegativeF64 = NonNegativeF64(1e-5);
pub(crate) const PAGERANK_MAX_ITERATIONS: IterationCount = IterationCount(NonZeroU32::new(100).unwrap());
pub(crate) const FORCE_ATLAS2_MAX_ITER: IterationCount = IterationCount(NonZeroU32::new(500).unwrap());
pub(crate) const FORCE_ATLAS2_OVERLAP_SCALING_RATIO: PositiveF64 = PositiveF64(2.0);
pub(crate) const FORCE_ATLAS2_EDGE_WEIGHT_INFLUENCE: NonNegativeF64 = NonNegativeF64(1.0);
pub(crate) const FORCE_ATLAS2_JITTER_TOLERANCE: NonNegativeF64 = NonNegativeF64(1.0);
pub(crate) const FORCE_ATLAS2_BARNES_HUT_THETA: ClosedUnitF64 = ClosedUnitF64(0.5);
pub(crate) const FORCE_ATLAS2_SCALING_RATIO: PositiveF64 = PositiveF64(2.0);
pub(crate) const FORCE_ATLAS2_GRAVITY: NonNegativeF64 = NonNegativeF64(1.0);
pub(crate) const LOUVAIN_MAX_LEVEL: IterationCount = IterationCount(NonZeroU32::new(100).unwrap());
pub(crate) const LOUVAIN_THRESHOLD: NonNegativeF64 = NonNegativeF64(1e-7);
pub(crate) const LOUVAIN_RESOLUTION: PositiveF64 = PositiveF64(1.0);
pub(crate) const ECG_THRESHOLD: FractionF64 = FractionF64(1e-7);
pub(crate) const ECG_RESOLUTION: FractionF64 = FractionF64(1.0);
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct IterationCount(NonZeroU32);
impl IterationCount {
pub fn new(value: u32) -> Result<Self> {
NonZeroU32::new(value).map(IterationCount).ok_or_else(|| {
Error::new(ErrorKind::InvalidInput, "IterationCount::new")
.with_message("iteration count must be greater than zero")
})
}
pub const fn from_non_zero(value: NonZeroU32) -> Self {
IterationCount(value)
}
pub const fn get(self) -> u32 {
self.0.get()
}
pub const fn into_inner(self) -> NonZeroU32 {
self.0
}
}
impl fmt::Display for IterationCount {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl From<IterationCount> for u32 {
fn from(count: IterationCount) -> u32 {
count.get()
}
}
impl From<IterationCount> for usize {
fn from(count: IterationCount) -> usize {
count.get() as usize
}
}
impl TryFrom<IterationCount> for i32 {
type Error = Error;
fn try_from(count: IterationCount) -> Result<i32> {
i32::try_from(count.get()).map_err(|_| {
Error::new(ErrorKind::InvalidInput, "IterationCount::try_into<i32>")
.with_message("iteration count exceeds i32::MAX")
.with_context("value", count.get().to_string())
})
}
}
impl TryFrom<u32> for IterationCount {
type Error = Error;
fn try_from(value: u32) -> Result<Self> {
Self::new(value)
}
}
impl TryFrom<i32> for IterationCount {
type Error = Error;
fn try_from(value: i32) -> Result<Self> {
if value <= 0 {
return Err(Error::new(ErrorKind::InvalidInput, "IterationCount::try_from<i32>")
.with_message("iteration count must be greater than zero")
.with_context("value", value.to_string()));
}
Self::new(value as u32)
}
}
impl TryFrom<usize> for IterationCount {
type Error = Error;
fn try_from(value: usize) -> Result<Self> {
let val_u32 = u32::try_from(value).map_err(|_| {
Error::new(ErrorKind::InvalidInput, "IterationCount::try_from<usize>")
.with_message("iteration count exceeds u32::MAX")
.with_context("value", value.to_string())
})?;
Self::new(val_u32)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct BfsDepthLimit(usize);
impl BfsDepthLimit {
pub const fn new(value: usize) -> Self {
BfsDepthLimit(value)
}
pub const fn get(self) -> usize {
self.0
}
}
impl fmt::Display for BfsDepthLimit {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl From<BfsDepthLimit> for usize {
fn from(limit: BfsDepthLimit) -> usize {
limit.get()
}
}
impl From<usize> for BfsDepthLimit {
fn from(value: usize) -> Self {
Self::new(value)
}
}
impl From<u32> for BfsDepthLimit {
fn from(value: u32) -> Self {
Self::new(value as usize)
}
}
impl TryFrom<i32> for BfsDepthLimit {
type Error = Error;
fn try_from(value: i32) -> Result<Self> {
usize::try_from(value).map(Self::new).map_err(|_| {
Error::new(ErrorKind::InvalidInput, "BfsDepthLimit::try_from<i32>")
.with_message("BFS depth limit must not be negative")
.with_context("value", value.to_string())
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub struct PositiveF64(f64);
impl PositiveF64 {
pub fn new(value: f64) -> Result<Self> {
if !value.is_finite() || value <= 0.0 {
return Err(Error::new(ErrorKind::InvalidInput, "PositiveF64::new")
.with_message("value must be a finite number greater than zero")
.with_context("value", format!("{value}")));
}
Ok(PositiveF64(value))
}
pub const fn get(self) -> f64 {
self.0
}
}
impl fmt::Display for PositiveF64 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl From<PositiveF64> for f64 {
fn from(val: PositiveF64) -> f64 {
val.get()
}
}
impl TryFrom<f64> for PositiveF64 {
type Error = Error;
fn try_from(value: f64) -> Result<Self> {
Self::new(value)
}
}
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub struct UnitF64(f64);
impl UnitF64 {
pub fn new(value: f64) -> Result<Self> {
if !value.is_finite() || value <= 0.0 || value >= 1.0 {
return Err(Error::new(ErrorKind::InvalidInput, "UnitF64::new")
.with_message("value must be in the open interval (0.0, 1.0)")
.with_context("value", format!("{value}")));
}
Ok(UnitF64(value))
}
pub const fn get(self) -> f64 {
self.0
}
}
impl fmt::Display for UnitF64 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl From<UnitF64> for f64 {
fn from(val: UnitF64) -> f64 {
val.get()
}
}
impl TryFrom<f64> for UnitF64 {
type Error = Error;
fn try_from(value: f64) -> Result<Self> {
Self::new(value)
}
}
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub struct NonNegativeF64(f64);
impl NonNegativeF64 {
pub fn new(value: f64) -> Result<Self> {
if !value.is_finite() || value < 0.0 {
return Err(Error::new(ErrorKind::InvalidInput, "NonNegativeF64::new")
.with_message("value must be a finite non-negative number")
.with_context("value", format!("{value}")));
}
Ok(NonNegativeF64(value))
}
pub const fn get(self) -> f64 {
self.0
}
}
impl fmt::Display for NonNegativeF64 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl From<NonNegativeF64> for f64 {
fn from(val: NonNegativeF64) -> f64 {
val.get()
}
}
impl TryFrom<f64> for NonNegativeF64 {
type Error = Error;
fn try_from(value: f64) -> Result<Self> {
Self::new(value)
}
}
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub struct ClosedUnitF64(f64);
impl ClosedUnitF64 {
pub fn new(value: f64) -> Result<Self> {
if !(0.0..=1.0).contains(&value) {
return Err(Error::new(ErrorKind::InvalidInput, "ClosedUnitF64::new")
.with_message("value must be in the closed interval [0.0, 1.0]")
.with_context("value", format!("{value}")));
}
Ok(ClosedUnitF64(value))
}
pub const fn get(self) -> f64 {
self.0
}
}
impl fmt::Display for ClosedUnitF64 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl From<ClosedUnitF64> for f64 {
fn from(val: ClosedUnitF64) -> f64 {
val.get()
}
}
impl TryFrom<f64> for ClosedUnitF64 {
type Error = Error;
fn try_from(value: f64) -> Result<Self> {
Self::new(value)
}
}
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub struct FractionF64(f64);
impl FractionF64 {
pub fn new(value: f64) -> Result<Self> {
if !(value > 0.0 && value <= 1.0) {
return Err(Error::new(ErrorKind::InvalidInput, "FractionF64::new")
.with_message("value must be in the interval (0.0, 1.0]")
.with_context("value", format!("{value}")));
}
Ok(FractionF64(value))
}
pub const fn get(self) -> f64 {
self.0
}
}
impl fmt::Display for FractionF64 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl From<FractionF64> for f64 {
fn from(val: FractionF64) -> f64 {
val.get()
}
}
impl TryFrom<f64> for FractionF64 {
type Error = Error;
fn try_from(value: f64) -> Result<Self> {
Self::new(value)
}
}
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub struct HalfOpenUnitF64(f64);
impl HalfOpenUnitF64 {
pub fn new(value: f64) -> Result<Self> {
if !(0.0..1.0).contains(&value) {
return Err(Error::new(ErrorKind::InvalidInput, "HalfOpenUnitF64::new")
.with_message("value must be in the interval [0.0, 1.0)")
.with_context("value", format!("{value}")));
}
Ok(HalfOpenUnitF64(value))
}
pub const fn get(self) -> f64 {
self.0
}
}
impl fmt::Display for HalfOpenUnitF64 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl From<HalfOpenUnitF64> for f64 {
fn from(val: HalfOpenUnitF64) -> f64 {
val.get()
}
}
impl TryFrom<f64> for HalfOpenUnitF64 {
type Error = Error;
fn try_from(value: f64) -> Result<Self> {
Self::new(value)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ClusterCount(usize);
impl ClusterCount {
pub fn new(value: usize) -> Result<Self> {
if value < 2 {
return Err(Error::new(ErrorKind::InvalidInput, "ClusterCount::new")
.with_message("cluster count must be at least 2")
.with_context("value", value.to_string()));
}
Ok(ClusterCount(value))
}
pub const fn get(self) -> usize {
self.0
}
}
impl fmt::Display for ClusterCount {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl From<ClusterCount> for usize {
fn from(count: ClusterCount) -> usize {
count.get()
}
}
impl TryFrom<usize> for ClusterCount {
type Error = Error;
fn try_from(value: usize) -> Result<Self> {
Self::new(value)
}
}