solverforge_core/score/
bendable.rs1use std::cmp::Ordering;
6use std::fmt;
7use std::ops::{Add, Neg, Sub};
8
9use super::traits::Score;
10use super::ScoreLevel;
11
12#[derive(Clone, Copy, PartialEq, Eq, Hash)]
34pub struct BendableScore<const H: usize, const S: usize> {
35 hard: [i64; H],
36 soft: [i64; S],
37}
38
39impl<const H: usize, const S: usize> BendableScore<H, S> {
40 pub const fn of(hard: [i64; H], soft: [i64; S]) -> Self {
42 BendableScore { hard, soft }
43 }
44
45 pub const fn zero() -> Self {
47 BendableScore {
48 hard: [0; H],
49 soft: [0; S],
50 }
51 }
52
53 pub const fn hard_levels_count(&self) -> usize {
55 H
56 }
57
58 pub const fn soft_levels_count(&self) -> usize {
60 S
61 }
62
63 pub const fn hard_score(&self, level: usize) -> i64 {
68 self.hard[level]
69 }
70
71 pub const fn soft_score(&self, level: usize) -> i64 {
76 self.soft[level]
77 }
78
79 pub const fn hard_scores(&self) -> &[i64; H] {
81 &self.hard
82 }
83
84 pub const fn soft_scores(&self) -> &[i64; S] {
86 &self.soft
87 }
88
89 pub const fn one_hard(level: usize) -> Self {
91 let mut hard = [0; H];
92 hard[level] = 1;
93 BendableScore { hard, soft: [0; S] }
94 }
95
96 pub const fn one_soft(level: usize) -> Self {
98 let mut soft = [0; S];
99 soft[level] = 1;
100 BendableScore { hard: [0; H], soft }
101 }
102}
103
104impl<const H: usize, const S: usize> Default for BendableScore<H, S> {
105 fn default() -> Self {
106 Self::zero()
107 }
108}
109
110impl<const H: usize, const S: usize> Score for BendableScore<H, S> {
111 fn is_feasible(&self) -> bool {
112 self.hard.iter().all(|&s| s >= 0)
113 }
114
115 fn zero() -> Self {
116 BendableScore::zero()
117 }
118
119 fn levels_count() -> usize {
120 H + S
121 }
122
123 fn to_level_numbers(&self) -> Vec<i64> {
124 let mut levels = Vec::with_capacity(H + S);
125 levels.extend_from_slice(&self.hard);
126 levels.extend_from_slice(&self.soft);
127 levels
128 }
129
130 fn from_level_numbers(levels: &[i64]) -> Self {
131 assert!(levels.len() >= H + S, "Not enough levels provided");
132 let mut hard = [0; H];
133 let mut soft = [0; S];
134 hard.copy_from_slice(&levels[..H]);
135 soft.copy_from_slice(&levels[H..H + S]);
136 BendableScore { hard, soft }
137 }
138
139 fn multiply(&self, multiplicand: f64) -> Self {
140 let mut hard = [0; H];
141 let mut soft = [0; S];
142 for (i, item) in hard.iter_mut().enumerate().take(H) {
143 *item = (self.hard[i] as f64 * multiplicand).round() as i64;
144 }
145 for (i, item) in soft.iter_mut().enumerate().take(S) {
146 *item = (self.soft[i] as f64 * multiplicand).round() as i64;
147 }
148 BendableScore { hard, soft }
149 }
150
151 fn divide(&self, divisor: f64) -> Self {
152 let mut hard = [0; H];
153 let mut soft = [0; S];
154 for (i, item) in hard.iter_mut().enumerate().take(H) {
155 *item = (self.hard[i] as f64 / divisor).round() as i64;
156 }
157 for (i, item) in soft.iter_mut().enumerate().take(S) {
158 *item = (self.soft[i] as f64 / divisor).round() as i64;
159 }
160 BendableScore { hard, soft }
161 }
162
163 fn abs(&self) -> Self {
164 let mut hard = [0; H];
165 let mut soft = [0; S];
166 for (i, item) in hard.iter_mut().enumerate().take(H) {
167 *item = self.hard[i].abs();
168 }
169 for (i, item) in soft.iter_mut().enumerate().take(S) {
170 *item = self.soft[i].abs();
171 }
172 BendableScore { hard, soft }
173 }
174
175 fn level_label(index: usize) -> ScoreLevel {
176 if index < H {
177 ScoreLevel::Hard
178 } else if index < H + S {
179 ScoreLevel::Soft
180 } else {
181 panic!(
182 "BendableScore<{}, {}> has {} levels, got index {}",
183 H,
184 S,
185 H + S,
186 index
187 )
188 }
189 }
190
191 #[inline]
192 fn to_scalar(&self) -> f64 {
193 let total_levels = H + S;
194 let mut scalar = 0.0;
195 for (i, &v) in self.hard.iter().enumerate() {
196 let weight = 10_f64.powi(((total_levels - 1 - i) * 6) as i32);
197 scalar += v as f64 * weight;
198 }
199 for (i, &v) in self.soft.iter().enumerate() {
200 let weight = 10_f64.powi(((S - 1 - i) * 6) as i32);
201 scalar += v as f64 * weight;
202 }
203 scalar
204 }
205}
206
207impl<const H: usize, const S: usize> Ord for BendableScore<H, S> {
208 fn cmp(&self, other: &Self) -> Ordering {
209 for i in 0..H {
211 match self.hard[i].cmp(&other.hard[i]) {
212 Ordering::Equal => continue,
213 ord => return ord,
214 }
215 }
216
217 for i in 0..S {
219 match self.soft[i].cmp(&other.soft[i]) {
220 Ordering::Equal => continue,
221 ord => return ord,
222 }
223 }
224
225 Ordering::Equal
226 }
227}
228
229impl<const H: usize, const S: usize> PartialOrd for BendableScore<H, S> {
230 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
231 Some(self.cmp(other))
232 }
233}
234
235impl<const H: usize, const S: usize> Add for BendableScore<H, S> {
236 type Output = Self;
237
238 fn add(self, other: Self) -> Self {
239 let mut hard = [0; H];
240 let mut soft = [0; S];
241 for (i, item) in hard.iter_mut().enumerate().take(H) {
242 *item = self.hard[i] + other.hard[i];
243 }
244 for (i, item) in soft.iter_mut().enumerate().take(S) {
245 *item = self.soft[i] + other.soft[i];
246 }
247 BendableScore { hard, soft }
248 }
249}
250
251impl<const H: usize, const S: usize> Sub for BendableScore<H, S> {
252 type Output = Self;
253
254 fn sub(self, other: Self) -> Self {
255 let mut hard = [0; H];
256 let mut soft = [0; S];
257 for (i, item) in hard.iter_mut().enumerate().take(H) {
258 *item = self.hard[i] - other.hard[i];
259 }
260 for (i, item) in soft.iter_mut().enumerate().take(S) {
261 *item = self.soft[i] - other.soft[i];
262 }
263 BendableScore { hard, soft }
264 }
265}
266
267impl<const H: usize, const S: usize> Neg for BendableScore<H, S> {
268 type Output = Self;
269
270 fn neg(self) -> Self {
271 let mut hard = [0; H];
272 let mut soft = [0; S];
273 for (i, item) in hard.iter_mut().enumerate().take(H) {
274 *item = -self.hard[i];
275 }
276 for (i, item) in soft.iter_mut().enumerate().take(S) {
277 *item = -self.soft[i];
278 }
279 BendableScore { hard, soft }
280 }
281}
282
283impl<const H: usize, const S: usize> fmt::Debug for BendableScore<H, S> {
284 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
285 write!(
286 f,
287 "BendableScore(hard: {:?}, soft: {:?})",
288 self.hard, self.soft
289 )
290 }
291}
292
293impl<const H: usize, const S: usize> fmt::Display for BendableScore<H, S> {
294 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
295 let hard_str: Vec<String> = self.hard.iter().map(|s| s.to_string()).collect();
296 let soft_str: Vec<String> = self.soft.iter().map(|s| s.to_string()).collect();
297
298 write!(
299 f,
300 "[{}]hard/[{}]soft",
301 hard_str.join("/"),
302 soft_str.join("/")
303 )
304 }
305}