1use 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 level_number(&self, index: usize) -> i64 {
118 if index < H {
119 self.hard[index]
120 } else if index < H + S {
121 self.soft[index - H]
122 } else {
123 panic!(
124 "BendableScore<{}, {}> has {} levels, got index {}",
125 H,
126 S,
127 H + S,
128 index
129 )
130 }
131 }
132
133 fn from_level_numbers(levels: &[i64]) -> Self {
134 assert!(levels.len() >= H + S, "Not enough levels provided");
135 let mut hard = [0; H];
136 let mut soft = [0; S];
137 hard.copy_from_slice(&levels[..H]);
138 soft.copy_from_slice(&levels[H..H + S]);
139 BendableScore { hard, soft }
140 }
141
142 fn multiply(&self, multiplicand: f64) -> Self {
143 let mut hard = [0; H];
144 let mut soft = [0; S];
145 for (i, item) in hard.iter_mut().enumerate().take(H) {
146 *item = (self.hard[i] as f64 * multiplicand).round() as i64;
147 }
148 for (i, item) in soft.iter_mut().enumerate().take(S) {
149 *item = (self.soft[i] as f64 * multiplicand).round() as i64;
150 }
151 BendableScore { hard, soft }
152 }
153
154 fn divide(&self, divisor: f64) -> Self {
155 let mut hard = [0; H];
156 let mut soft = [0; S];
157 for (i, item) in hard.iter_mut().enumerate().take(H) {
158 *item = (self.hard[i] as f64 / divisor).round() as i64;
159 }
160 for (i, item) in soft.iter_mut().enumerate().take(S) {
161 *item = (self.soft[i] as f64 / divisor).round() as i64;
162 }
163 BendableScore { hard, soft }
164 }
165
166 fn abs(&self) -> Self {
167 let mut hard = [0; H];
168 let mut soft = [0; S];
169 for (i, item) in hard.iter_mut().enumerate().take(H) {
170 *item = self.hard[i].abs();
171 }
172 for (i, item) in soft.iter_mut().enumerate().take(S) {
173 *item = self.soft[i].abs();
174 }
175 BendableScore { hard, soft }
176 }
177
178 fn level_label(index: usize) -> ScoreLevel {
179 if index < H {
180 ScoreLevel::Hard
181 } else if index < H + S {
182 ScoreLevel::Soft
183 } else {
184 panic!(
185 "BendableScore<{}, {}> has {} levels, got index {}",
186 H,
187 S,
188 H + S,
189 index
190 )
191 }
192 }
193
194 #[inline]
195 fn to_scalar(&self) -> f64 {
196 let total_levels = H + S;
197 let mut scalar = 0.0;
198 for (i, &v) in self.hard.iter().enumerate() {
199 let weight = 10_f64.powi(((total_levels - 1 - i) * 6) as i32);
200 scalar += v as f64 * weight;
201 }
202 for (i, &v) in self.soft.iter().enumerate() {
203 let weight = 10_f64.powi(((S - 1 - i) * 6) as i32);
204 scalar += v as f64 * weight;
205 }
206 scalar
207 }
208}
209
210impl<const H: usize, const S: usize> Ord for BendableScore<H, S> {
211 fn cmp(&self, other: &Self) -> Ordering {
212 for i in 0..H {
214 match self.hard[i].cmp(&other.hard[i]) {
215 Ordering::Equal => continue,
216 ord => return ord,
217 }
218 }
219
220 for i in 0..S {
222 match self.soft[i].cmp(&other.soft[i]) {
223 Ordering::Equal => continue,
224 ord => return ord,
225 }
226 }
227
228 Ordering::Equal
229 }
230}
231
232impl<const H: usize, const S: usize> PartialOrd for BendableScore<H, S> {
233 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
234 Some(self.cmp(other))
235 }
236}
237
238impl<const H: usize, const S: usize> Add for BendableScore<H, S> {
239 type Output = Self;
240
241 fn add(self, other: Self) -> Self {
242 let mut hard = [0; H];
243 let mut soft = [0; S];
244 for (i, item) in hard.iter_mut().enumerate().take(H) {
245 *item = self.hard[i] + other.hard[i];
246 }
247 for (i, item) in soft.iter_mut().enumerate().take(S) {
248 *item = self.soft[i] + other.soft[i];
249 }
250 BendableScore { hard, soft }
251 }
252}
253
254impl<const H: usize, const S: usize> Sub for BendableScore<H, S> {
255 type Output = Self;
256
257 fn sub(self, other: Self) -> Self {
258 let mut hard = [0; H];
259 let mut soft = [0; S];
260 for (i, item) in hard.iter_mut().enumerate().take(H) {
261 *item = self.hard[i] - other.hard[i];
262 }
263 for (i, item) in soft.iter_mut().enumerate().take(S) {
264 *item = self.soft[i] - other.soft[i];
265 }
266 BendableScore { hard, soft }
267 }
268}
269
270impl<const H: usize, const S: usize> Neg for BendableScore<H, S> {
271 type Output = Self;
272
273 fn neg(self) -> Self {
274 let mut hard = [0; H];
275 let mut soft = [0; S];
276 for (i, item) in hard.iter_mut().enumerate().take(H) {
277 *item = -self.hard[i];
278 }
279 for (i, item) in soft.iter_mut().enumerate().take(S) {
280 *item = -self.soft[i];
281 }
282 BendableScore { hard, soft }
283 }
284}
285
286impl<const H: usize, const S: usize> fmt::Debug for BendableScore<H, S> {
287 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
288 write!(
289 f,
290 "BendableScore(hard: {:?}, soft: {:?})",
291 self.hard, self.soft
292 )
293 }
294}
295
296impl<const H: usize, const S: usize> fmt::Display for BendableScore<H, S> {
297 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
298 let hard_str: Vec<String> = self.hard.iter().map(|s| s.to_string()).collect();
299 let soft_str: Vec<String> = self.soft.iter().map(|s| s.to_string()).collect();
300
301 write!(
302 f,
303 "[{}]hard/[{}]soft",
304 hard_str.join("/"),
305 soft_str.join("/")
306 )
307 }
308}