1use super::Score;
2use crate::SolverForgeError;
3use rust_decimal::Decimal;
4use serde::{Deserialize, Serialize};
5use std::cmp::Ordering;
6use std::fmt;
7use std::ops::{Add, Neg, Sub};
8
9#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
10pub struct BendableScore {
11 pub hard_scores: Vec<i64>,
12 pub soft_scores: Vec<i64>,
13}
14
15impl BendableScore {
16 pub fn of(hard_scores: Vec<i64>, soft_scores: Vec<i64>) -> Self {
17 Self {
18 hard_scores,
19 soft_scores,
20 }
21 }
22
23 pub fn zero(hard_levels: usize, soft_levels: usize) -> Self {
24 Self {
25 hard_scores: vec![0; hard_levels],
26 soft_scores: vec![0; soft_levels],
27 }
28 }
29
30 pub fn of_hard(hard_level: usize, hard_levels: usize, soft_levels: usize, score: i64) -> Self {
31 let mut hard_scores = vec![0; hard_levels];
32 if hard_level < hard_levels {
33 hard_scores[hard_level] = score;
34 }
35 Self {
36 hard_scores,
37 soft_scores: vec![0; soft_levels],
38 }
39 }
40
41 pub fn of_soft(soft_level: usize, hard_levels: usize, soft_levels: usize, score: i64) -> Self {
42 let mut soft_scores = vec![0; soft_levels];
43 if soft_level < soft_levels {
44 soft_scores[soft_level] = score;
45 }
46 Self {
47 hard_scores: vec![0; hard_levels],
48 soft_scores,
49 }
50 }
51
52 pub fn hard_levels_size(&self) -> usize {
53 self.hard_scores.len()
54 }
55
56 pub fn soft_levels_size(&self) -> usize {
57 self.soft_scores.len()
58 }
59
60 pub fn parse(text: &str) -> Result<Self, SolverForgeError> {
61 let text = text.trim();
62
63 let hard_end = text.find("]hard/[").ok_or_else(|| {
65 SolverForgeError::Serialization(format!("Invalid BendableScore format: {}", text))
66 })?;
67
68 let hard_part = &text[1..hard_end];
69 let soft_start = hard_end + 7;
70 let soft_end = text.len() - 5;
71 let soft_part = &text[soft_start..soft_end];
72
73 let hard_scores = if hard_part.is_empty() {
74 vec![]
75 } else {
76 hard_part
77 .split('/')
78 .map(|s| s.trim().parse::<i64>())
79 .collect::<Result<Vec<_>, _>>()
80 .map_err(|e| {
81 SolverForgeError::Serialization(format!("Invalid hard score: {}", e))
82 })?
83 };
84
85 let soft_scores = if soft_part.is_empty() {
86 vec![]
87 } else {
88 soft_part
89 .split('/')
90 .map(|s| s.trim().parse::<i64>())
91 .collect::<Result<Vec<_>, _>>()
92 .map_err(|e| {
93 SolverForgeError::Serialization(format!("Invalid soft score: {}", e))
94 })?
95 };
96
97 Ok(Self {
98 hard_scores,
99 soft_scores,
100 })
101 }
102}
103
104impl Score for BendableScore {
105 fn is_feasible(&self) -> bool {
106 self.hard_scores.iter().all(|&s| s >= 0)
107 }
108
109 fn is_solution_initialized(&self) -> bool {
110 true
111 }
112
113 fn zero() -> Self {
114 Self {
115 hard_scores: vec![],
116 soft_scores: vec![],
117 }
118 }
119
120 fn negate(&self) -> Self {
121 Self {
122 hard_scores: self.hard_scores.iter().map(|&s| -s).collect(),
123 soft_scores: self.soft_scores.iter().map(|&s| -s).collect(),
124 }
125 }
126
127 fn add(&self, other: &Self) -> Self {
128 Self {
129 hard_scores: self
130 .hard_scores
131 .iter()
132 .zip(other.hard_scores.iter())
133 .map(|(&a, &b)| a + b)
134 .collect(),
135 soft_scores: self
136 .soft_scores
137 .iter()
138 .zip(other.soft_scores.iter())
139 .map(|(&a, &b)| a + b)
140 .collect(),
141 }
142 }
143
144 fn subtract(&self, other: &Self) -> Self {
145 Self {
146 hard_scores: self
147 .hard_scores
148 .iter()
149 .zip(other.hard_scores.iter())
150 .map(|(&a, &b)| a - b)
151 .collect(),
152 soft_scores: self
153 .soft_scores
154 .iter()
155 .zip(other.soft_scores.iter())
156 .map(|(&a, &b)| a - b)
157 .collect(),
158 }
159 }
160}
161
162impl PartialOrd for BendableScore {
163 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
164 Some(self.cmp(other))
165 }
166}
167
168impl Ord for BendableScore {
169 fn cmp(&self, other: &Self) -> Ordering {
170 for (a, b) in self.hard_scores.iter().zip(other.hard_scores.iter()) {
171 match a.cmp(b) {
172 Ordering::Equal => continue,
173 ord => return ord,
174 }
175 }
176 for (a, b) in self.soft_scores.iter().zip(other.soft_scores.iter()) {
177 match a.cmp(b) {
178 Ordering::Equal => continue,
179 ord => return ord,
180 }
181 }
182 Ordering::Equal
183 }
184}
185
186impl fmt::Display for BendableScore {
187 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
188 let hard = self
189 .hard_scores
190 .iter()
191 .map(|s| s.to_string())
192 .collect::<Vec<_>>()
193 .join("/");
194 let soft = self
195 .soft_scores
196 .iter()
197 .map(|s| s.to_string())
198 .collect::<Vec<_>>()
199 .join("/");
200 write!(f, "[{}]hard/[{}]soft", hard, soft)
201 }
202}
203
204impl Add for BendableScore {
205 type Output = Self;
206 fn add(self, other: Self) -> Self {
207 Score::add(&self, &other)
208 }
209}
210
211impl Sub for BendableScore {
212 type Output = Self;
213 fn sub(self, other: Self) -> Self {
214 Score::subtract(&self, &other)
215 }
216}
217
218impl Neg for BendableScore {
219 type Output = Self;
220 fn neg(self) -> Self {
221 Score::negate(&self)
222 }
223}
224
225#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
228pub struct BendableDecimalScore {
229 pub hard_scores: Vec<Decimal>,
230 pub soft_scores: Vec<Decimal>,
231}
232
233impl BendableDecimalScore {
234 pub fn of(hard_scores: Vec<Decimal>, soft_scores: Vec<Decimal>) -> Self {
235 Self {
236 hard_scores,
237 soft_scores,
238 }
239 }
240
241 pub fn zero(hard_levels: usize, soft_levels: usize) -> Self {
242 Self {
243 hard_scores: vec![Decimal::ZERO; hard_levels],
244 soft_scores: vec![Decimal::ZERO; soft_levels],
245 }
246 }
247
248 pub fn hard_levels_size(&self) -> usize {
249 self.hard_scores.len()
250 }
251
252 pub fn soft_levels_size(&self) -> usize {
253 self.soft_scores.len()
254 }
255
256 pub fn parse(text: &str) -> Result<Self, SolverForgeError> {
257 let text = text.trim();
258
259 let hard_end = text.find("]hard/[").ok_or_else(|| {
260 SolverForgeError::Serialization(format!(
261 "Invalid BendableDecimalScore format: {}",
262 text
263 ))
264 })?;
265
266 let hard_part = &text[1..hard_end];
267 let soft_start = hard_end + 7;
268 let soft_end = text.len() - 5;
269 let soft_part = &text[soft_start..soft_end];
270
271 let hard_scores = if hard_part.is_empty() {
272 vec![]
273 } else {
274 hard_part
275 .split('/')
276 .map(|s| s.trim().parse::<Decimal>())
277 .collect::<Result<Vec<_>, _>>()
278 .map_err(|e| {
279 SolverForgeError::Serialization(format!("Invalid hard score: {}", e))
280 })?
281 };
282
283 let soft_scores = if soft_part.is_empty() {
284 vec![]
285 } else {
286 soft_part
287 .split('/')
288 .map(|s| s.trim().parse::<Decimal>())
289 .collect::<Result<Vec<_>, _>>()
290 .map_err(|e| {
291 SolverForgeError::Serialization(format!("Invalid soft score: {}", e))
292 })?
293 };
294
295 Ok(Self {
296 hard_scores,
297 soft_scores,
298 })
299 }
300}
301
302impl Score for BendableDecimalScore {
303 fn is_feasible(&self) -> bool {
304 self.hard_scores.iter().all(|&s| s >= Decimal::ZERO)
305 }
306
307 fn is_solution_initialized(&self) -> bool {
308 true
309 }
310
311 fn zero() -> Self {
312 Self {
313 hard_scores: vec![],
314 soft_scores: vec![],
315 }
316 }
317
318 fn negate(&self) -> Self {
319 Self {
320 hard_scores: self.hard_scores.iter().map(|&s| -s).collect(),
321 soft_scores: self.soft_scores.iter().map(|&s| -s).collect(),
322 }
323 }
324
325 fn add(&self, other: &Self) -> Self {
326 Self {
327 hard_scores: self
328 .hard_scores
329 .iter()
330 .zip(other.hard_scores.iter())
331 .map(|(&a, &b)| a + b)
332 .collect(),
333 soft_scores: self
334 .soft_scores
335 .iter()
336 .zip(other.soft_scores.iter())
337 .map(|(&a, &b)| a + b)
338 .collect(),
339 }
340 }
341
342 fn subtract(&self, other: &Self) -> Self {
343 Self {
344 hard_scores: self
345 .hard_scores
346 .iter()
347 .zip(other.hard_scores.iter())
348 .map(|(&a, &b)| a - b)
349 .collect(),
350 soft_scores: self
351 .soft_scores
352 .iter()
353 .zip(other.soft_scores.iter())
354 .map(|(&a, &b)| a - b)
355 .collect(),
356 }
357 }
358}
359
360impl PartialOrd for BendableDecimalScore {
361 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
362 Some(self.cmp(other))
363 }
364}
365
366impl Ord for BendableDecimalScore {
367 fn cmp(&self, other: &Self) -> Ordering {
368 for (a, b) in self.hard_scores.iter().zip(other.hard_scores.iter()) {
369 match a.cmp(b) {
370 Ordering::Equal => continue,
371 ord => return ord,
372 }
373 }
374 for (a, b) in self.soft_scores.iter().zip(other.soft_scores.iter()) {
375 match a.cmp(b) {
376 Ordering::Equal => continue,
377 ord => return ord,
378 }
379 }
380 Ordering::Equal
381 }
382}
383
384impl fmt::Display for BendableDecimalScore {
385 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
386 let hard = self
387 .hard_scores
388 .iter()
389 .map(|s| s.to_string())
390 .collect::<Vec<_>>()
391 .join("/");
392 let soft = self
393 .soft_scores
394 .iter()
395 .map(|s| s.to_string())
396 .collect::<Vec<_>>()
397 .join("/");
398 write!(f, "[{}]hard/[{}]soft", hard, soft)
399 }
400}
401
402impl Add for BendableDecimalScore {
403 type Output = Self;
404 fn add(self, other: Self) -> Self {
405 Score::add(&self, &other)
406 }
407}
408
409impl Sub for BendableDecimalScore {
410 type Output = Self;
411 fn sub(self, other: Self) -> Self {
412 Score::subtract(&self, &other)
413 }
414}
415
416impl Neg for BendableDecimalScore {
417 type Output = Self;
418 fn neg(self) -> Self {
419 Score::negate(&self)
420 }
421}
422
423#[cfg(test)]
424mod tests {
425 use super::*;
426
427 mod bendable {
428 use super::*;
429
430 #[test]
431 fn test_of() {
432 let score = BendableScore::of(vec![-1, 0], vec![10, 20, 30]);
433 assert_eq!(score.hard_scores, vec![-1, 0]);
434 assert_eq!(score.soft_scores, vec![10, 20, 30]);
435 }
436
437 #[test]
438 fn test_zero() {
439 let score = BendableScore::zero(2, 3);
440 assert_eq!(score.hard_scores, vec![0, 0]);
441 assert_eq!(score.soft_scores, vec![0, 0, 0]);
442 }
443
444 #[test]
445 fn test_levels_size() {
446 let score = BendableScore::of(vec![1, 2], vec![3, 4, 5]);
447 assert_eq!(score.hard_levels_size(), 2);
448 assert_eq!(score.soft_levels_size(), 3);
449 }
450
451 #[test]
452 fn test_is_feasible() {
453 assert!(BendableScore::of(vec![0, 0], vec![-100]).is_feasible());
454 assert!(BendableScore::of(vec![1, 0], vec![-100]).is_feasible());
455 assert!(!BendableScore::of(vec![-1, 0], vec![100]).is_feasible());
456 assert!(!BendableScore::of(vec![0, -1], vec![100]).is_feasible());
457 }
458
459 #[test]
460 fn test_comparison() {
461 assert!(
462 BendableScore::of(vec![1, 0], vec![0]) > BendableScore::of(vec![0, 100], vec![100])
463 );
464 assert!(
465 BendableScore::of(vec![0, 1], vec![0]) > BendableScore::of(vec![0, 0], vec![100])
466 );
467 assert!(
468 BendableScore::of(vec![0, 0], vec![10]) > BendableScore::of(vec![0, 0], vec![5])
469 );
470 }
471
472 #[test]
473 fn test_arithmetic() {
474 let a = BendableScore::of(vec![-2, 1], vec![10, 20]);
475 let b = BendableScore::of(vec![-1, 1], vec![5, 10]);
476
477 let sum = a.clone() + b.clone();
478 assert_eq!(sum.hard_scores, vec![-3, 2]);
479 assert_eq!(sum.soft_scores, vec![15, 30]);
480
481 let diff = a.clone() - b;
482 assert_eq!(diff.hard_scores, vec![-1, 0]);
483 assert_eq!(diff.soft_scores, vec![5, 10]);
484
485 let neg = -a;
486 assert_eq!(neg.hard_scores, vec![2, -1]);
487 assert_eq!(neg.soft_scores, vec![-10, -20]);
488 }
489
490 #[test]
491 fn test_parse() {
492 let score = BendableScore::parse("[-1/0]hard/[10/20/30]soft").unwrap();
493 assert_eq!(score.hard_scores, vec![-1, 0]);
494 assert_eq!(score.soft_scores, vec![10, 20, 30]);
495
496 let empty = BendableScore::parse("[]hard/[]soft").unwrap();
497 assert!(empty.hard_scores.is_empty());
498 assert!(empty.soft_scores.is_empty());
499 }
500
501 #[test]
502 fn test_display() {
503 let score = BendableScore::of(vec![-1, 0], vec![10, 20]);
504 assert_eq!(format!("{}", score), "[-1/0]hard/[10/20]soft");
505 }
506
507 #[test]
508 fn test_json_serialization() {
509 let score = BendableScore::of(vec![-1, 0], vec![10, 20]);
510 let json = serde_json::to_string(&score).unwrap();
511 let parsed: BendableScore = serde_json::from_str(&json).unwrap();
512 assert_eq!(parsed, score);
513 }
514 }
515
516 mod bendable_decimal {
517 use super::*;
518
519 #[test]
520 fn test_of() {
521 let score = BendableDecimalScore::of(
522 vec![Decimal::new(-10, 1), Decimal::ZERO],
523 vec![Decimal::new(100, 1)],
524 );
525 assert_eq!(score.hard_scores.len(), 2);
526 assert_eq!(score.soft_scores.len(), 1);
527 }
528
529 #[test]
530 fn test_is_feasible() {
531 assert!(
532 BendableDecimalScore::of(vec![Decimal::ZERO], vec![Decimal::new(-100, 0)])
533 .is_feasible()
534 );
535 assert!(!BendableDecimalScore::of(vec![Decimal::new(-1, 0)], vec![]).is_feasible());
536 }
537
538 #[test]
539 fn test_parse() {
540 let score = BendableDecimalScore::parse("[-1.5/0]hard/[10.25]soft").unwrap();
541 assert_eq!(score.hard_scores, vec![Decimal::new(-15, 1), Decimal::ZERO]);
542 assert_eq!(score.soft_scores, vec![Decimal::new(1025, 2)]);
543 }
544
545 #[test]
546 fn test_display() {
547 let score = BendableDecimalScore::of(
548 vec![Decimal::new(-15, 1), Decimal::ZERO],
549 vec![Decimal::new(1025, 2)],
550 );
551 assert_eq!(format!("{}", score), "[-1.5/0]hard/[10.25]soft");
552 }
553 }
554}