1use std::{fmt, num::NonZeroU32};
35
36use crate::error::{Error, ErrorKind, Result};
37
38pub(crate) mod defaults {
39 use super::{ClosedUnitF64, FractionF64, IterationCount, NonNegativeF64, NonZeroU32, PositiveF64};
40
41 pub(crate) const PAGERANK_ALPHA: ClosedUnitF64 = ClosedUnitF64(0.85);
42 pub(crate) const PAGERANK_EPSILON: NonNegativeF64 = NonNegativeF64(1e-5);
43 pub(crate) const PAGERANK_MAX_ITERATIONS: IterationCount = IterationCount(NonZeroU32::new(100).unwrap());
44
45 pub(crate) const FORCE_ATLAS2_MAX_ITER: IterationCount = IterationCount(NonZeroU32::new(500).unwrap());
46 pub(crate) const FORCE_ATLAS2_OVERLAP_SCALING_RATIO: PositiveF64 = PositiveF64(2.0);
47 pub(crate) const FORCE_ATLAS2_EDGE_WEIGHT_INFLUENCE: NonNegativeF64 = NonNegativeF64(1.0);
48 pub(crate) const FORCE_ATLAS2_JITTER_TOLERANCE: NonNegativeF64 = NonNegativeF64(1.0);
49 pub(crate) const FORCE_ATLAS2_BARNES_HUT_THETA: ClosedUnitF64 = ClosedUnitF64(0.5);
50 pub(crate) const FORCE_ATLAS2_SCALING_RATIO: PositiveF64 = PositiveF64(2.0);
51 pub(crate) const FORCE_ATLAS2_GRAVITY: NonNegativeF64 = NonNegativeF64(1.0);
52
53 pub(crate) const LOUVAIN_MAX_LEVEL: IterationCount = IterationCount(NonZeroU32::new(100).unwrap());
54 pub(crate) const LOUVAIN_THRESHOLD: NonNegativeF64 = NonNegativeF64(1e-7);
55 pub(crate) const LOUVAIN_RESOLUTION: PositiveF64 = PositiveF64(1.0);
56
57 pub(crate) const ECG_THRESHOLD: FractionF64 = FractionF64(1e-7);
58 pub(crate) const ECG_RESOLUTION: FractionF64 = FractionF64(1.0);
59}
60
61#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
82pub struct IterationCount(NonZeroU32);
83
84impl IterationCount {
85 pub fn new(value: u32) -> Result<Self> {
91 NonZeroU32::new(value).map(IterationCount).ok_or_else(|| {
92 Error::new(ErrorKind::InvalidInput, "IterationCount::new")
93 .with_message("iteration count must be greater than zero")
94 })
95 }
96
97 pub const fn from_non_zero(value: NonZeroU32) -> Self {
99 IterationCount(value)
100 }
101
102 pub const fn get(self) -> u32 {
104 self.0.get()
105 }
106
107 pub const fn into_inner(self) -> NonZeroU32 {
109 self.0
110 }
111}
112
113impl fmt::Display for IterationCount {
114 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
115 write!(f, "{}", self.0)
116 }
117}
118
119impl From<IterationCount> for u32 {
120 fn from(count: IterationCount) -> u32 {
121 count.get()
122 }
123}
124
125impl From<IterationCount> for usize {
126 fn from(count: IterationCount) -> usize {
127 count.get() as usize
128 }
129}
130
131impl TryFrom<IterationCount> for i32 {
132 type Error = Error;
133
134 fn try_from(count: IterationCount) -> Result<i32> {
135 i32::try_from(count.get()).map_err(|_| {
136 Error::new(ErrorKind::InvalidInput, "IterationCount::try_into<i32>")
137 .with_message("iteration count exceeds i32::MAX")
138 .with_context("value", count.get().to_string())
139 })
140 }
141}
142
143impl TryFrom<u32> for IterationCount {
144 type Error = Error;
145
146 fn try_from(value: u32) -> Result<Self> {
147 Self::new(value)
148 }
149}
150
151impl TryFrom<i32> for IterationCount {
152 type Error = Error;
153
154 fn try_from(value: i32) -> Result<Self> {
155 if value <= 0 {
156 return Err(Error::new(ErrorKind::InvalidInput, "IterationCount::try_from<i32>")
157 .with_message("iteration count must be greater than zero")
158 .with_context("value", value.to_string()));
159 }
160 Self::new(value as u32)
161 }
162}
163
164impl TryFrom<usize> for IterationCount {
165 type Error = Error;
166
167 fn try_from(value: usize) -> Result<Self> {
168 let val_u32 = u32::try_from(value).map_err(|_| {
169 Error::new(ErrorKind::InvalidInput, "IterationCount::try_from<usize>")
170 .with_message("iteration count exceeds u32::MAX")
171 .with_context("value", value.to_string())
172 })?;
173 Self::new(val_u32)
174 }
175}
176
177#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
195pub struct BfsDepthLimit(usize);
196
197impl BfsDepthLimit {
198 pub const fn new(value: usize) -> Self {
200 BfsDepthLimit(value)
201 }
202
203 pub const fn get(self) -> usize {
205 self.0
206 }
207}
208
209impl fmt::Display for BfsDepthLimit {
210 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
211 write!(f, "{}", self.0)
212 }
213}
214
215impl From<BfsDepthLimit> for usize {
216 fn from(limit: BfsDepthLimit) -> usize {
217 limit.get()
218 }
219}
220
221impl From<usize> for BfsDepthLimit {
222 fn from(value: usize) -> Self {
223 Self::new(value)
224 }
225}
226
227impl From<u32> for BfsDepthLimit {
228 fn from(value: u32) -> Self {
229 Self::new(value as usize)
230 }
231}
232
233impl TryFrom<i32> for BfsDepthLimit {
234 type Error = Error;
235
236 fn try_from(value: i32) -> Result<Self> {
237 usize::try_from(value).map(Self::new).map_err(|_| {
238 Error::new(ErrorKind::InvalidInput, "BfsDepthLimit::try_from<i32>")
239 .with_message("BFS depth limit must not be negative")
240 .with_context("value", value.to_string())
241 })
242 }
243}
244
245#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
266pub struct PositiveF64(f64);
267
268impl PositiveF64 {
269 pub fn new(value: f64) -> Result<Self> {
276 if !value.is_finite() || value <= 0.0 {
277 return Err(Error::new(ErrorKind::InvalidInput, "PositiveF64::new")
278 .with_message("value must be a finite number greater than zero")
279 .with_context("value", format!("{value}")));
280 }
281 Ok(PositiveF64(value))
282 }
283
284 pub const fn get(self) -> f64 {
286 self.0
287 }
288}
289
290impl fmt::Display for PositiveF64 {
291 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
292 write!(f, "{}", self.0)
293 }
294}
295
296impl From<PositiveF64> for f64 {
297 fn from(val: PositiveF64) -> f64 {
298 val.get()
299 }
300}
301
302impl TryFrom<f64> for PositiveF64 {
303 type Error = Error;
304
305 fn try_from(value: f64) -> Result<Self> {
306 Self::new(value)
307 }
308}
309
310#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
328pub struct UnitF64(f64);
329
330impl UnitF64 {
331 pub fn new(value: f64) -> Result<Self> {
338 if !value.is_finite() || value <= 0.0 || value >= 1.0 {
339 return Err(Error::new(ErrorKind::InvalidInput, "UnitF64::new")
340 .with_message("value must be in the open interval (0.0, 1.0)")
341 .with_context("value", format!("{value}")));
342 }
343 Ok(UnitF64(value))
344 }
345
346 pub const fn get(self) -> f64 {
348 self.0
349 }
350}
351
352impl fmt::Display for UnitF64 {
353 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
354 write!(f, "{}", self.0)
355 }
356}
357
358impl From<UnitF64> for f64 {
359 fn from(val: UnitF64) -> f64 {
360 val.get()
361 }
362}
363
364impl TryFrom<f64> for UnitF64 {
365 type Error = Error;
366
367 fn try_from(value: f64) -> Result<Self> {
368 Self::new(value)
369 }
370}
371
372#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
392pub struct NonNegativeF64(f64);
393
394impl NonNegativeF64 {
395 pub fn new(value: f64) -> Result<Self> {
402 if !value.is_finite() || value < 0.0 {
403 return Err(Error::new(ErrorKind::InvalidInput, "NonNegativeF64::new")
404 .with_message("value must be a finite non-negative number")
405 .with_context("value", format!("{value}")));
406 }
407 Ok(NonNegativeF64(value))
408 }
409
410 pub const fn get(self) -> f64 {
412 self.0
413 }
414}
415
416impl fmt::Display for NonNegativeF64 {
417 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
418 write!(f, "{}", self.0)
419 }
420}
421
422impl From<NonNegativeF64> for f64 {
423 fn from(val: NonNegativeF64) -> f64 {
424 val.get()
425 }
426}
427
428impl TryFrom<f64> for NonNegativeF64 {
429 type Error = Error;
430
431 fn try_from(value: f64) -> Result<Self> {
432 Self::new(value)
433 }
434}
435
436#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
455pub struct ClosedUnitF64(f64);
456
457impl ClosedUnitF64 {
458 pub fn new(value: f64) -> Result<Self> {
465 if !(0.0..=1.0).contains(&value) {
466 return Err(Error::new(ErrorKind::InvalidInput, "ClosedUnitF64::new")
467 .with_message("value must be in the closed interval [0.0, 1.0]")
468 .with_context("value", format!("{value}")));
469 }
470 Ok(ClosedUnitF64(value))
471 }
472
473 pub const fn get(self) -> f64 {
475 self.0
476 }
477}
478
479impl fmt::Display for ClosedUnitF64 {
480 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
481 write!(f, "{}", self.0)
482 }
483}
484
485impl From<ClosedUnitF64> for f64 {
486 fn from(val: ClosedUnitF64) -> f64 {
487 val.get()
488 }
489}
490
491impl TryFrom<f64> for ClosedUnitF64 {
492 type Error = Error;
493
494 fn try_from(value: f64) -> Result<Self> {
495 Self::new(value)
496 }
497}
498
499#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
516pub struct FractionF64(f64);
517
518impl FractionF64 {
519 pub fn new(value: f64) -> Result<Self> {
526 if !(value > 0.0 && value <= 1.0) {
527 return Err(Error::new(ErrorKind::InvalidInput, "FractionF64::new")
528 .with_message("value must be in the interval (0.0, 1.0]")
529 .with_context("value", format!("{value}")));
530 }
531 Ok(FractionF64(value))
532 }
533
534 pub const fn get(self) -> f64 {
536 self.0
537 }
538}
539
540impl fmt::Display for FractionF64 {
541 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
542 write!(f, "{}", self.0)
543 }
544}
545
546impl From<FractionF64> for f64 {
547 fn from(val: FractionF64) -> f64 {
548 val.get()
549 }
550}
551
552impl TryFrom<f64> for FractionF64 {
553 type Error = Error;
554
555 fn try_from(value: f64) -> Result<Self> {
556 Self::new(value)
557 }
558}
559
560#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
578pub struct HalfOpenUnitF64(f64);
579
580impl HalfOpenUnitF64 {
581 pub fn new(value: f64) -> Result<Self> {
588 if !(0.0..1.0).contains(&value) {
589 return Err(Error::new(ErrorKind::InvalidInput, "HalfOpenUnitF64::new")
590 .with_message("value must be in the interval [0.0, 1.0)")
591 .with_context("value", format!("{value}")));
592 }
593 Ok(HalfOpenUnitF64(value))
594 }
595
596 pub const fn get(self) -> f64 {
598 self.0
599 }
600}
601
602impl fmt::Display for HalfOpenUnitF64 {
603 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
604 write!(f, "{}", self.0)
605 }
606}
607
608impl From<HalfOpenUnitF64> for f64 {
609 fn from(val: HalfOpenUnitF64) -> f64 {
610 val.get()
611 }
612}
613
614impl TryFrom<f64> for HalfOpenUnitF64 {
615 type Error = Error;
616
617 fn try_from(value: f64) -> Result<Self> {
618 Self::new(value)
619 }
620}
621
622#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
635pub struct ClusterCount(usize);
636
637impl ClusterCount {
638 pub fn new(value: usize) -> Result<Self> {
644 if value < 2 {
645 return Err(Error::new(ErrorKind::InvalidInput, "ClusterCount::new")
646 .with_message("cluster count must be at least 2")
647 .with_context("value", value.to_string()));
648 }
649 Ok(ClusterCount(value))
650 }
651
652 pub const fn get(self) -> usize {
654 self.0
655 }
656}
657
658impl fmt::Display for ClusterCount {
659 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
660 write!(f, "{}", self.0)
661 }
662}
663
664impl From<ClusterCount> for usize {
665 fn from(count: ClusterCount) -> usize {
666 count.get()
667 }
668}
669
670impl TryFrom<usize> for ClusterCount {
671 type Error = Error;
672
673 fn try_from(value: usize) -> Result<Self> {
674 Self::new(value)
675 }
676}