use std::cmp::Ordering;
use std::fmt;
use std::ops::{Add, Neg, Sub};
use super::traits::Score;
use super::ScoreLevel;
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct BendableScore<const H: usize, const S: usize> {
hard: [i64; H],
soft: [i64; S],
}
impl<const H: usize, const S: usize> BendableScore<H, S> {
pub const fn of(hard: [i64; H], soft: [i64; S]) -> Self {
BendableScore { hard, soft }
}
pub const fn zero() -> Self {
BendableScore {
hard: [0; H],
soft: [0; S],
}
}
pub const fn hard_levels_count(&self) -> usize {
H
}
pub const fn soft_levels_count(&self) -> usize {
S
}
pub const fn hard_score(&self, level: usize) -> i64 {
self.hard[level]
}
pub const fn soft_score(&self, level: usize) -> i64 {
self.soft[level]
}
pub const fn hard_scores(&self) -> &[i64; H] {
&self.hard
}
pub const fn soft_scores(&self) -> &[i64; S] {
&self.soft
}
pub const fn one_hard(level: usize) -> Self {
let mut hard = [0; H];
hard[level] = 1;
BendableScore { hard, soft: [0; S] }
}
pub const fn one_soft(level: usize) -> Self {
let mut soft = [0; S];
soft[level] = 1;
BendableScore { hard: [0; H], soft }
}
}
impl<const H: usize, const S: usize> Default for BendableScore<H, S> {
fn default() -> Self {
Self::zero()
}
}
impl<const H: usize, const S: usize> Score for BendableScore<H, S> {
fn is_feasible(&self) -> bool {
self.hard.iter().all(|&s| s >= 0)
}
fn zero() -> Self {
BendableScore::zero()
}
fn levels_count() -> usize {
H + S
}
fn to_level_numbers(&self) -> Vec<i64> {
let mut levels = Vec::with_capacity(H + S);
levels.extend_from_slice(&self.hard);
levels.extend_from_slice(&self.soft);
levels
}
fn from_level_numbers(levels: &[i64]) -> Self {
assert!(levels.len() >= H + S, "Not enough levels provided");
let mut hard = [0; H];
let mut soft = [0; S];
hard.copy_from_slice(&levels[..H]);
soft.copy_from_slice(&levels[H..H + S]);
BendableScore { hard, soft }
}
fn multiply(&self, multiplicand: f64) -> Self {
let mut hard = [0; H];
let mut soft = [0; S];
for (i, item) in hard.iter_mut().enumerate().take(H) {
*item = (self.hard[i] as f64 * multiplicand).round() as i64;
}
for (i, item) in soft.iter_mut().enumerate().take(S) {
*item = (self.soft[i] as f64 * multiplicand).round() as i64;
}
BendableScore { hard, soft }
}
fn divide(&self, divisor: f64) -> Self {
let mut hard = [0; H];
let mut soft = [0; S];
for (i, item) in hard.iter_mut().enumerate().take(H) {
*item = (self.hard[i] as f64 / divisor).round() as i64;
}
for (i, item) in soft.iter_mut().enumerate().take(S) {
*item = (self.soft[i] as f64 / divisor).round() as i64;
}
BendableScore { hard, soft }
}
fn abs(&self) -> Self {
let mut hard = [0; H];
let mut soft = [0; S];
for (i, item) in hard.iter_mut().enumerate().take(H) {
*item = self.hard[i].abs();
}
for (i, item) in soft.iter_mut().enumerate().take(S) {
*item = self.soft[i].abs();
}
BendableScore { hard, soft }
}
fn level_label(index: usize) -> ScoreLevel {
if index < H {
ScoreLevel::Hard
} else if index < H + S {
ScoreLevel::Soft
} else {
panic!(
"BendableScore<{}, {}> has {} levels, got index {}",
H,
S,
H + S,
index
)
}
}
#[inline]
fn to_scalar(&self) -> f64 {
let total_levels = H + S;
let mut scalar = 0.0;
for (i, &v) in self.hard.iter().enumerate() {
let weight = 10_f64.powi(((total_levels - 1 - i) * 6) as i32);
scalar += v as f64 * weight;
}
for (i, &v) in self.soft.iter().enumerate() {
let weight = 10_f64.powi(((S - 1 - i) * 6) as i32);
scalar += v as f64 * weight;
}
scalar
}
}
impl<const H: usize, const S: usize> Ord for BendableScore<H, S> {
fn cmp(&self, other: &Self) -> Ordering {
for i in 0..H {
match self.hard[i].cmp(&other.hard[i]) {
Ordering::Equal => continue,
ord => return ord,
}
}
for i in 0..S {
match self.soft[i].cmp(&other.soft[i]) {
Ordering::Equal => continue,
ord => return ord,
}
}
Ordering::Equal
}
}
impl<const H: usize, const S: usize> PartialOrd for BendableScore<H, S> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<const H: usize, const S: usize> Add for BendableScore<H, S> {
type Output = Self;
fn add(self, other: Self) -> Self {
let mut hard = [0; H];
let mut soft = [0; S];
for (i, item) in hard.iter_mut().enumerate().take(H) {
*item = self.hard[i] + other.hard[i];
}
for (i, item) in soft.iter_mut().enumerate().take(S) {
*item = self.soft[i] + other.soft[i];
}
BendableScore { hard, soft }
}
}
impl<const H: usize, const S: usize> Sub for BendableScore<H, S> {
type Output = Self;
fn sub(self, other: Self) -> Self {
let mut hard = [0; H];
let mut soft = [0; S];
for (i, item) in hard.iter_mut().enumerate().take(H) {
*item = self.hard[i] - other.hard[i];
}
for (i, item) in soft.iter_mut().enumerate().take(S) {
*item = self.soft[i] - other.soft[i];
}
BendableScore { hard, soft }
}
}
impl<const H: usize, const S: usize> Neg for BendableScore<H, S> {
type Output = Self;
fn neg(self) -> Self {
let mut hard = [0; H];
let mut soft = [0; S];
for (i, item) in hard.iter_mut().enumerate().take(H) {
*item = -self.hard[i];
}
for (i, item) in soft.iter_mut().enumerate().take(S) {
*item = -self.soft[i];
}
BendableScore { hard, soft }
}
}
impl<const H: usize, const S: usize> fmt::Debug for BendableScore<H, S> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"BendableScore(hard: {:?}, soft: {:?})",
self.hard, self.soft
)
}
}
impl<const H: usize, const S: usize> fmt::Display for BendableScore<H, S> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let hard_str: Vec<String> = self.hard.iter().map(|s| s.to_string()).collect();
let soft_str: Vec<String> = self.soft.iter().map(|s| s.to_string()).collect();
write!(
f,
"[{}]hard/[{}]soft",
hard_str.join("/"),
soft_str.join("/")
)
}
}