solverforge_core/score/
simple.rs1use super::Score;
2use crate::SolverForgeError;
3use serde::{Deserialize, Serialize};
4use std::cmp::Ordering;
5use std::fmt;
6use std::ops::{Add, Neg, Sub};
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
9pub struct SimpleScore {
10 pub score: i64,
11}
12
13impl SimpleScore {
14 pub const ZERO: SimpleScore = SimpleScore { score: 0 };
15 pub const ONE: SimpleScore = SimpleScore { score: 1 };
16
17 pub fn of(score: i64) -> Self {
18 Self { score }
19 }
20
21 pub fn parse(text: &str) -> Result<Self, SolverForgeError> {
22 let text = text.trim();
23 let score = text
24 .parse::<i64>()
25 .map_err(|e| SolverForgeError::Serialization(format!("Invalid SimpleScore: {}", e)))?;
26 Ok(Self { score })
27 }
28}
29
30impl Score for SimpleScore {
31 fn is_feasible(&self) -> bool {
32 self.score >= 0
33 }
34
35 fn is_solution_initialized(&self) -> bool {
36 true
37 }
38
39 fn zero() -> Self {
40 Self::ZERO
41 }
42
43 fn negate(&self) -> Self {
44 Self { score: -self.score }
45 }
46
47 fn add(&self, other: &Self) -> Self {
48 Self {
49 score: self.score + other.score,
50 }
51 }
52
53 fn subtract(&self, other: &Self) -> Self {
54 Self {
55 score: self.score - other.score,
56 }
57 }
58}
59
60impl PartialOrd for SimpleScore {
61 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
62 Some(self.cmp(other))
63 }
64}
65
66impl Ord for SimpleScore {
67 fn cmp(&self, other: &Self) -> Ordering {
68 self.score.cmp(&other.score)
69 }
70}
71
72impl fmt::Display for SimpleScore {
73 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74 write!(f, "{}", self.score)
75 }
76}
77
78impl Add for SimpleScore {
79 type Output = Self;
80 fn add(self, other: Self) -> Self {
81 Score::add(&self, &other)
82 }
83}
84
85impl Sub for SimpleScore {
86 type Output = Self;
87 fn sub(self, other: Self) -> Self {
88 Score::subtract(&self, &other)
89 }
90}
91
92impl Neg for SimpleScore {
93 type Output = Self;
94 fn neg(self) -> Self {
95 Score::negate(&self)
96 }
97}
98
99impl Default for SimpleScore {
100 fn default() -> Self {
101 Self::ZERO
102 }
103}
104
105#[cfg(test)]
106mod tests {
107 use super::*;
108
109 #[test]
110 fn test_of() {
111 assert_eq!(SimpleScore::of(42).score, 42);
112 assert_eq!(SimpleScore::of(-10).score, -10);
113 }
114
115 #[test]
116 fn test_constants() {
117 assert_eq!(SimpleScore::ZERO.score, 0);
118 assert_eq!(SimpleScore::ONE.score, 1);
119 }
120
121 #[test]
122 fn test_is_feasible() {
123 assert!(SimpleScore::of(0).is_feasible());
124 assert!(SimpleScore::of(10).is_feasible());
125 assert!(!SimpleScore::of(-1).is_feasible());
126 }
127
128 #[test]
129 fn test_arithmetic() {
130 let a = SimpleScore::of(10);
131 let b = SimpleScore::of(3);
132
133 assert_eq!(a + b, SimpleScore::of(13));
134 assert_eq!(a - b, SimpleScore::of(7));
135 assert_eq!(-a, SimpleScore::of(-10));
136 }
137
138 #[test]
139 fn test_comparison() {
140 assert!(SimpleScore::of(10) > SimpleScore::of(5));
141 assert!(SimpleScore::of(-5) < SimpleScore::of(0));
142 assert!(SimpleScore::of(5) == SimpleScore::of(5));
143 }
144
145 #[test]
146 fn test_parse() {
147 assert_eq!(SimpleScore::parse("42").unwrap(), SimpleScore::of(42));
148 assert_eq!(SimpleScore::parse("-10").unwrap(), SimpleScore::of(-10));
149 assert_eq!(SimpleScore::parse(" 0 ").unwrap(), SimpleScore::ZERO);
150 assert!(SimpleScore::parse("invalid").is_err());
151 }
152
153 #[test]
154 fn test_display() {
155 assert_eq!(format!("{}", SimpleScore::of(42)), "42");
156 assert_eq!(format!("{}", SimpleScore::of(-10)), "-10");
157 }
158
159 #[test]
160 fn test_json_serialization() {
161 let score = SimpleScore::of(42);
162 let json = serde_json::to_string(&score).unwrap();
163 assert_eq!(json, r#"{"score":42}"#);
164
165 let parsed: SimpleScore = serde_json::from_str(&json).unwrap();
166 assert_eq!(parsed, score);
167 }
168}