premise 0.1.5

the core pattern library of Premise: sequences, tables, records, money, days and text, every function carrying the reason it exists
Documentation
use crate::arith::{away, Floor};
use crate::select::{either, is_below};
use crate::seq::Ranked;
use core::cmp::Ordering;
use patterns_macros::because;

pub fn highest<T: Ranked + Clone>(items: &[T]) -> Option<T> {
    let mut best: Option<&T> = None;
    for item in items {
        let better = match best {
            Some(held) => item.rank() > held.rank(),
            None => true,
        };
        if better {
            best = Some(item);
        }
    }
    best.cloned()
}
because!(highest, "the element a maximum picks out, kept as an Option so an empty shelf answers with a value, and a tie goes to the earliest so a queue order survives the choice");

pub fn lowest<T: Ranked + Clone>(items: &[T]) -> Option<T> {
    let mut best: Option<&T> = None;
    for item in items {
        let better = match best {
            Some(held) => item.rank() < held.rank(),
            None => true,
        };
        if better {
            best = Some(item);
        }
    }
    best.cloned()
}
because!(lowest, "the mirror of highest, separate because the smallest rank is the next thing due and a caller asking for it should not negate a rank to get there");

pub fn placed<T: Ranked + Clone>(items: &[T], item: T) -> Vec<T> {
    let mut out = Vec::new();
    let mut put = false;
    for held in items {
        if !put && held.rank() > item.rank() {
            out.push(item.clone());
            put = true;
        }
        out.push(held.clone());
    }
    if !put {
        out.push(item);
    }
    out
}
because!(placed, "insertion that keeps a list sorted by rank, with a tie landing after its equals so arrival order is never lost, which with would break by appending");

pub fn ordered<T: Ranked + Clone>(items: &[T]) -> Vec<T> {
    let mut out = items.to_vec();
    out.sort_by(|a, b| a.rank().partial_cmp(&b.rank()).unwrap_or(Ordering::Equal));
    out
}
because!(ordered, "a list sorted by its rank in one stable sort, separate from placed because building a list by repeated insertion copies it once per element, while placed stays the single insert into a list already in order; both land equal ranks in arrival order");

pub fn keep_between<T: Ranked + Clone>(items: &[T], lo: T::Rank, hi: T::Rank) -> Vec<T> {
    let mut out = Vec::new();
    for item in items {
        let rank = item.rank();
        if rank >= lo && rank < hi {
            out.push(item.clone());
        }
    }
    out
}
because!(keep_between, "the elements inside a window, half open at the top so adjoining windows share no element and a day belongs to one period only");

pub fn split_above<T: Ranked + Clone>(items: &[T], bound: T::Rank) -> (Vec<T>, Vec<T>) {
    let mut over = Vec::new();
    let mut under = Vec::new();
    for item in items {
        if item.rank() > bound {
            over.push(item.clone());
        } else {
            under.push(item.clone());
        }
    }
    (over, under)
}
because!(split_above, "both halves of a partition in one pass, so the two lists are complementary by construction instead of by two filters that may drift apart");

pub fn nearest<T: Floor + Copy + PartialOrd>(want: T, from: &[T]) -> Option<T> {
    from.iter()
        .copied()
        .reduce(|best, one| either(is_below(away(one, want), away(best, want)), one, best))
}
because!(nearest, "the candidate closest to what was wanted, which is asked once by anything choosing a note, a step, a bay or a size from a set that does not hold the exact answer; a tie keeps the one found first, so the order of the candidates decides it and the caller can say what that order is");