use crate::error::{ConfigError, IrithyllError, Result};
#[derive(Debug, Clone)]
pub struct NGRCConfig {
pub k: usize,
pub s: usize,
pub degree: usize,
pub forgetting_factor: f64,
pub delta: f64,
pub include_bias: bool,
}
impl Default for NGRCConfig {
fn default() -> Self {
Self {
k: 2,
s: 1,
degree: 2,
forgetting_factor: 0.998,
delta: 100.0,
include_bias: true,
}
}
}
impl NGRCConfig {
pub fn builder() -> NGRCConfigBuilder {
NGRCConfigBuilder::new()
}
}
#[derive(Debug, Clone)]
pub struct NGRCConfigBuilder {
config: NGRCConfig,
}
impl NGRCConfigBuilder {
pub fn new() -> Self {
Self {
config: NGRCConfig::default(),
}
}
pub fn k(mut self, k: usize) -> Self {
self.config.k = k;
self
}
pub fn s(mut self, s: usize) -> Self {
self.config.s = s;
self
}
pub fn degree(mut self, degree: usize) -> Self {
self.config.degree = degree;
self
}
pub fn forgetting_factor(mut self, ff: f64) -> Self {
self.config.forgetting_factor = ff;
self
}
pub fn delta(mut self, delta: f64) -> Self {
self.config.delta = delta;
self
}
pub fn include_bias(mut self, include_bias: bool) -> Self {
self.config.include_bias = include_bias;
self
}
pub fn build(self) -> Result<NGRCConfig> {
let c = &self.config;
if c.k < 1 {
return Err(IrithyllError::InvalidConfig(ConfigError::out_of_range(
"k",
"must be >= 1",
c.k,
)));
}
if c.s < 1 {
return Err(IrithyllError::InvalidConfig(ConfigError::out_of_range(
"s",
"must be >= 1",
c.s,
)));
}
if c.degree < 2 {
return Err(IrithyllError::InvalidConfig(ConfigError::out_of_range(
"degree",
"must be >= 2",
c.degree,
)));
}
if c.forgetting_factor <= 0.0 || c.forgetting_factor > 1.0 {
return Err(IrithyllError::InvalidConfig(ConfigError::out_of_range(
"forgetting_factor",
"must be in (0, 1]",
c.forgetting_factor,
)));
}
if c.delta <= 0.0 {
return Err(IrithyllError::InvalidConfig(ConfigError::out_of_range(
"delta",
"must be > 0",
c.delta,
)));
}
Ok(self.config)
}
}
impl Default for NGRCConfigBuilder {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_config_builds() {
let config = NGRCConfig::builder().build().unwrap();
assert_eq!(config.k, 2);
assert_eq!(config.s, 1);
assert_eq!(config.degree, 2);
assert!((config.forgetting_factor - 0.998).abs() < 1e-12);
assert!((config.delta - 100.0).abs() < 1e-12);
assert!(config.include_bias);
}
#[test]
fn custom_config_builds() {
let config = NGRCConfig::builder()
.k(5)
.s(3)
.degree(3)
.forgetting_factor(0.99)
.delta(50.0)
.include_bias(false)
.build()
.unwrap();
assert_eq!(config.k, 5);
assert_eq!(config.s, 3);
assert_eq!(config.degree, 3);
assert!((config.forgetting_factor - 0.99).abs() < 1e-12);
assert!((config.delta - 50.0).abs() < 1e-12);
assert!(!config.include_bias);
}
#[test]
fn k_zero_fails() {
let result = NGRCConfig::builder().k(0).build();
assert!(result.is_err());
}
#[test]
fn s_zero_fails() {
let result = NGRCConfig::builder().s(0).build();
assert!(result.is_err());
}
#[test]
fn degree_one_fails() {
let result = NGRCConfig::builder().degree(1).build();
assert!(result.is_err());
}
#[test]
fn forgetting_factor_zero_fails() {
let result = NGRCConfig::builder().forgetting_factor(0.0).build();
assert!(result.is_err());
}
#[test]
fn forgetting_factor_above_one_fails() {
let result = NGRCConfig::builder().forgetting_factor(1.01).build();
assert!(result.is_err());
}
#[test]
fn delta_zero_fails() {
let result = NGRCConfig::builder().delta(0.0).build();
assert!(result.is_err());
}
#[test]
fn delta_negative_fails() {
let result = NGRCConfig::builder().delta(-1.0).build();
assert!(result.is_err());
}
}