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