use std::cmp::Ordering;
use std::iter;

pub type Score = usize;

#[derive(Debug)]
pub struct Borda<T> {
    items: Vec<(T, Score)>,
}
impl<T: Ord> Borda<T> {
    pub fn new<I>(items: I) -> Self
    where
        I: Iterator<Item = T>,
    {
        Self {
            items: items.zip(iter::repeat(0)).collect(),
        }
    }

    pub fn compete<F>(&mut self, f: F)
    where
        F: Fn(&T, &T) -> Ordering,
    {
        for i in 0..self.items.len() {
            for j in (0..self.items.len()).filter(|&j| j != i) {
                if f(&self.items[i].0, &self.items[j].0) == Ordering::Less {
                    self.items[i].1 += 1;
                }
            }
        }
    }

    pub fn scores(&self) -> impl '_ + Iterator<Item = Score> {
        self.items.iter().map(|t| t.1)
    }
}

#[derive(Debug)]
pub struct Firsts<T> {
    items: Vec<(T, Score)>,
}
impl<T: Ord> Firsts<T> {
    pub fn new<I>(items: I) -> Self
    where
        I: Iterator<Item = T>,
    {
        Self {
            items: items.zip(iter::repeat(0)).collect(),
        }
    }

    pub fn compete<F>(&mut self, f: F)
    where
        F: Fn(&T, &T) -> Ordering,
    {
        for i in 0..self.items.len() {
            let mut is_first = true;
            for j in (0..self.items.len()).filter(|&j| j != i) {
                if f(&self.items[i].0, &self.items[j].0) == Ordering::Greater {
                    is_first = false;
                    break;
                }
            }
            if is_first {
                self.items[i].1 += 1;
            }
        }
    }

    pub fn scores(&self) -> impl '_ + Iterator<Item = Score> {
        self.items.iter().map(|t| t.1)
    }
}
