use std::cmp;
use std::num::FpCategory;
use std::vec;
#[derive(Clone, Debug, Default)]
pub struct SearchResults<T>(Vec<Scored<T>>);
impl<T> SearchResults<T> {
pub fn new() -> SearchResults<T> {
SearchResults(vec![])
}
pub fn push(&mut self, scored: Scored<T>) {
assert!(self.0.last().map_or(true, |smallest| &scored <= smallest));
self.0.push(scored);
}
#[allow(dead_code)]
pub fn normalize(&mut self) {
if let Some(top_score) = self.0.first().map(|s| s.score()) {
if top_score.classify() == FpCategory::Zero {
return;
}
for result in &mut self.0 {
let score = result.score();
result.set_score(score / top_score);
}
}
}
pub fn rescore<F: FnMut(&T) -> f64>(&mut self, mut rescore: F) {
for result in &mut self.0 {
let score = rescore(result.value());
result.set_score(score);
}
self.0.sort_by(|s1, s2| s1.cmp(s2).reverse());
}
#[allow(dead_code)]
pub fn trim(&mut self, size: usize) {
if self.0.len() > size {
self.0.drain(size..);
}
}
#[allow(dead_code)]
pub fn len(&self) -> usize {
self.0.len()
}
#[allow(dead_code)]
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
#[allow(dead_code)]
pub fn as_slice(&self) -> &[Scored<T>] {
&self.0
}
pub fn into_vec(self) -> Vec<Scored<T>> {
self.0
}
}
impl<T> IntoIterator for SearchResults<T> {
type IntoIter = vec::IntoIter<Scored<T>>;
type Item = Scored<T>;
fn into_iter(self) -> vec::IntoIter<Scored<T>> {
self.into_vec().into_iter()
}
}
#[derive(Clone, Copy, Debug)]
pub struct Scored<T> {
score: f64,
value: T,
}
impl<T> Scored<T> {
pub fn new(value: T) -> Scored<T> {
Scored { score: 1.0, value }
}
#[allow(dead_code)]
pub fn score(&self) -> f64 {
self.score
}
pub fn set_score(&mut self, score: f64) {
assert!(score.is_finite());
self.score = score;
}
#[allow(dead_code)]
pub fn with_score(mut self, score: f64) -> Scored<T> {
self.set_score(score);
self
}
#[allow(dead_code)]
pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Scored<U> {
Scored {
score: self.score,
value: f(self.value),
}
}
#[allow(dead_code)]
pub fn map_score<F: FnOnce(f64) -> f64>(self, f: F) -> Scored<T> {
let score = f(self.score);
self.with_score(score)
}
pub fn value(&self) -> &T {
&self.value
}
pub fn into_value(self) -> T {
self.value
}
#[allow(dead_code)]
pub fn into_pair(self) -> (f64, T) {
(self.score, self.value)
}
}
impl<T: Default> Default for Scored<T> {
fn default() -> Scored<T> {
Scored::new(T::default())
}
}
impl<T> Eq for Scored<T> {}
impl<T> PartialEq for Scored<T> {
fn eq(&self, other: &Scored<T>) -> bool {
let (s1, s2) = (self.score, other.score);
s1 == s2
}
}
impl<T> Ord for Scored<T> {
fn cmp(&self, other: &Scored<T>) -> cmp::Ordering {
self.score.partial_cmp(&other.score).unwrap()
}
}
impl<T> PartialOrd for Scored<T> {
fn partial_cmp(&self, other: &Scored<T>) -> Option<cmp::Ordering> {
Some(self.cmp(other))
}
}
#[cfg(test)]
mod tests {
use super::Scored;
#[test]
#[should_panic]
fn never_nan_1() {
Scored::new(()).set_score(f64::NAN);
}
#[test]
#[should_panic]
fn never_nan_2() {
Scored::new(()).with_score(f64::NAN);
}
#[test]
#[should_panic]
fn never_nan_3() {
Scored::new(()).map_score(|_| f64::NAN);
}
}