solverforge_core/score/
simple.rs1use std::cmp::Ordering;
4use std::fmt;
5use std::ops::{Add, Neg, Sub};
6
7use super::traits::{ParseableScore, Score, ScoreParseError};
8
9#[derive(Clone, Copy, PartialEq, Eq, Hash, Default)]
26pub struct SimpleScore {
27 score: i64,
28}
29
30impl SimpleScore {
31 pub const ZERO: SimpleScore = SimpleScore { score: 0 };
33
34 pub const ONE: SimpleScore = SimpleScore { score: 1 };
36
37 #[inline]
39 pub const fn of(score: i64) -> Self {
40 SimpleScore { score }
41 }
42
43 #[inline]
45 pub const fn score(&self) -> i64 {
46 self.score
47 }
48}
49
50impl Score for SimpleScore {
51 #[inline]
52 fn is_feasible(&self) -> bool {
53 self.score >= 0
54 }
55
56 #[inline]
57 fn zero() -> Self {
58 SimpleScore::ZERO
59 }
60
61 #[inline]
62 fn levels_count() -> usize {
63 1
64 }
65
66 fn to_level_numbers(&self) -> Vec<i64> {
67 vec![self.score]
68 }
69
70 fn from_level_numbers(levels: &[i64]) -> Self {
71 assert_eq!(levels.len(), 1, "SimpleScore requires exactly 1 level");
72 SimpleScore::of(levels[0])
73 }
74
75 fn multiply(&self, multiplicand: f64) -> Self {
76 SimpleScore::of((self.score as f64 * multiplicand).round() as i64)
77 }
78
79 fn divide(&self, divisor: f64) -> Self {
80 SimpleScore::of((self.score as f64 / divisor).round() as i64)
81 }
82
83 fn abs(&self) -> Self {
84 SimpleScore::of(self.score.abs())
85 }
86}
87
88impl Ord for SimpleScore {
89 fn cmp(&self, other: &Self) -> Ordering {
90 self.score.cmp(&other.score)
91 }
92}
93
94impl PartialOrd for SimpleScore {
95 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
96 Some(self.cmp(other))
97 }
98}
99
100impl Add for SimpleScore {
101 type Output = Self;
102
103 fn add(self, other: Self) -> Self {
104 SimpleScore::of(self.score + other.score)
105 }
106}
107
108impl Sub for SimpleScore {
109 type Output = Self;
110
111 fn sub(self, other: Self) -> Self {
112 SimpleScore::of(self.score - other.score)
113 }
114}
115
116impl Neg for SimpleScore {
117 type Output = Self;
118
119 fn neg(self) -> Self {
120 SimpleScore::of(-self.score)
121 }
122}
123
124impl fmt::Debug for SimpleScore {
125 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
126 write!(f, "SimpleScore({})", self.score)
127 }
128}
129
130impl fmt::Display for SimpleScore {
131 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
132 write!(f, "{}", self.score)
133 }
134}
135
136impl ParseableScore for SimpleScore {
137 fn parse(s: &str) -> Result<Self, ScoreParseError> {
138 let s = s.trim();
139 let s = s.strip_suffix("init").unwrap_or(s);
141
142 s.parse::<i64>()
143 .map(SimpleScore::of)
144 .map_err(|e| ScoreParseError {
145 message: format!("Invalid SimpleScore '{}': {}", s, e),
146 })
147 }
148
149 fn to_string_repr(&self) -> String {
150 self.score.to_string()
151 }
152}
153
154impl From<i64> for SimpleScore {
155 fn from(score: i64) -> Self {
156 SimpleScore::of(score)
157 }
158}
159
160#[cfg(test)]
161mod tests {
162 use super::*;
163
164 #[test]
165 fn test_creation() {
166 let score = SimpleScore::of(-5);
167 assert_eq!(score.score(), -5);
168 }
169
170 #[test]
171 fn test_feasibility() {
172 assert!(SimpleScore::of(0).is_feasible());
173 assert!(SimpleScore::of(10).is_feasible());
174 assert!(!SimpleScore::of(-1).is_feasible());
175 }
176
177 #[test]
178 fn test_comparison() {
179 let s1 = SimpleScore::of(-10);
180 let s2 = SimpleScore::of(-5);
181 let s3 = SimpleScore::of(0);
182
183 assert!(s3 > s2);
184 assert!(s2 > s1);
185 assert!(s1 < s2);
186 }
187
188 #[test]
189 fn test_arithmetic() {
190 let s1 = SimpleScore::of(10);
191 let s2 = SimpleScore::of(3);
192
193 assert_eq!(s1 + s2, SimpleScore::of(13));
194 assert_eq!(s1 - s2, SimpleScore::of(7));
195 assert_eq!(-s1, SimpleScore::of(-10));
196 }
197
198 #[test]
199 fn test_multiply_divide() {
200 let score = SimpleScore::of(10);
201
202 assert_eq!(score.multiply(2.0), SimpleScore::of(20));
203 assert_eq!(score.divide(2.0), SimpleScore::of(5));
204 }
205
206 #[test]
207 fn test_parse() {
208 assert_eq!(SimpleScore::parse("42").unwrap(), SimpleScore::of(42));
209 assert_eq!(SimpleScore::parse("-10").unwrap(), SimpleScore::of(-10));
210 assert_eq!(SimpleScore::parse("0init").unwrap(), SimpleScore::of(0));
211 }
212
213 #[test]
214 fn test_level_numbers() {
215 let score = SimpleScore::of(-5);
216 assert_eq!(score.to_level_numbers(), vec![-5]);
217 assert_eq!(SimpleScore::from_level_numbers(&[-5]), score);
218 }
219}