pipecircuit 0.0.10

Correct. Robust. Fast. Portable.
Documentation
use alloc::vec::Vec;
use core::cmp::Ordering;
use core::marker::PhantomData;
use core::num::{NonZeroU32, NonZeroUsize};

use super::selection::SelectionMembership;
use super::{RangeIndexLookupOutcome, SecondaryIndexLookupOutcome, Selection};
use crate::storage::warp_table::secondary_index::RangeIndexOrderingLevel;
use crate::storage::warp_table::{WarpTable, WarpTableRow};

mod first_rows;

crate::value_wrapper! {
    #[derive(Clone, Copy)]
    pub struct OrderedRowLimit(NonZeroU32);
}

impl OrderedRowLimit {
    fn row_count(self) -> NonZeroUsize {
        NonZeroUsize::try_from(self.value()).unwrap_or(NonZeroUsize::MAX)
    }
}

#[must_use]
pub struct RangeIndexOrdering<'table, Row: WarpTableRow, Brand = Row> {
    first_level: RangeIndexOrderingLevel<'table>,
    later_levels: Vec<RangeIndexOrderingLevel<'table>>,
    first_sortable_word: fn(&Row::Ref<'table>) -> u64,
    later_sortable_words: Vec<fn(&Row::Ref<'table>) -> u64>,
    table: PhantomData<fn() -> &'table WarpTable<Row, Brand>>,
}

impl<'table, Row: WarpTableRow, Brand> RangeIndexOrdering<'table, Row, Brand> {
    pub fn then(mut self, next: Self) -> Self {
        self.later_levels.push(next.first_level);
        self.later_levels.extend(next.later_levels);
        self.later_sortable_words.push(next.first_sortable_word);
        self.later_sortable_words.extend(next.later_sortable_words);
        self
    }

    fn levels(
        &self,
    ) -> impl Iterator<
        Item = (
            &RangeIndexOrderingLevel<'table>,
            fn(&Row::Ref<'table>) -> u64,
        ),
    > {
        core::iter::once((&self.first_level, self.first_sortable_word)).chain(
            self.later_levels
                .iter()
                .zip(self.later_sortable_words.iter().copied()),
        )
    }

    fn level_count(&self) -> NonZeroUsize {
        NonZeroUsize::MIN.saturating_add(self.later_levels.len())
    }
}

enum OrderedCandidates<'table> {
    EveryRow { row_count: usize },
    Membership(SelectionMembership<'table>),
}

#[must_use]
pub struct OrderedSelection<'table, Row: WarpTableRow, Brand = Row> {
    table: &'table WarpTable<Row, Brand>,
    candidates: OrderedCandidates<'table>,
    ordering: RangeIndexOrdering<'table, Row, Brand>,
}

#[must_use]
pub struct OrderedSelectionComparingRows<'table, Row: WarpTableRow, Brand, Compare> {
    ordered: OrderedSelection<'table, Row, Brand>,
    compare: Compare,
}

impl<'table, Row: WarpTableRow, Brand> OrderedSelection<'table, Row, Brand> {
    pub fn then_compare_rows<Compare>(
        self,
        compare: Compare,
    ) -> OrderedSelectionComparingRows<'table, Row, Brand, Compare>
    where
        Compare: FnMut(&Row::Ref<'table>, &Row::Ref<'table>) -> Ordering,
    {
        OrderedSelectionComparingRows {
            ordered: self,
            compare,
        }
    }
}

#[must_use]
pub enum OrderedSelectionFirstCuttingTiesOutcome<RowRef> {
    Exact {
        rows: Vec<RowRef>,
    },
    TiesCut {
        rows: Vec<RowRef>,
        tied_rows_left_out: usize,
    },
}

#[must_use]
pub enum OrderedSelectionFirstKeepingTiesOutcome<RowRef> {
    Exact {
        rows: Vec<RowRef>,
    },
    TiesKept {
        leaders: Vec<RowRef>,
        tied: Vec<RowRef>,
    },
}

impl<'table, Row: WarpTableRow, Brand> Selection<'table, Row, Brand> {
    pub fn ordered_by(
        self,
        order: impl FnOnce(Row::Query<'table, Brand>) -> RangeIndexOrdering<'table, Row, Brand>,
    ) -> OrderedSelection<'table, Row, Brand>
    where
        Brand: 'table,
    {
        let table = self.table;
        OrderedSelection {
            table,
            candidates: OrderedCandidates::Membership(self.membership),
            ordering: order(Row::query(table)),
        }
    }
}

macro_rules! impl_lookup_ordering {
    ($outcome:ident) => {
        impl<'table, Row: WarpTableRow, Brand, Key> $outcome<'table, Row, Brand, Key> {
            pub fn ordered_by(
                self,
                order: impl FnOnce(
                    Row::Query<'table, Brand>,
                ) -> RangeIndexOrdering<'table, Row, Brand>,
            ) -> OrderedSelection<'table, Row, Brand>
            where
                Brand: 'table,
            {
                self.into_selection().ordered_by(order)
            }
        }
    };
}

impl_lookup_ordering!(SecondaryIndexLookupOutcome);
impl_lookup_ordering!(RangeIndexLookupOutcome);

#[doc(hidden)]
pub fn bind_range_ordering<'table, Row: WarpTableRow, Brand>(
    level: RangeIndexOrderingLevel<'table>,
    sortable_word: fn(&Row::Ref<'table>) -> u64,
) -> RangeIndexOrdering<'table, Row, Brand> {
    RangeIndexOrdering {
        first_level: level,
        later_levels: Vec::new(),
        first_sortable_word: sortable_word,
        later_sortable_words: Vec::new(),
        table: PhantomData,
    }
}

#[doc(hidden)]
pub fn bind_every_row_ordering<'table, Row: WarpTableRow, Brand>(
    table: &'table WarpTable<Row, Brand>,
    ordering: RangeIndexOrdering<'table, Row, Brand>,
) -> OrderedSelection<'table, Row, Brand> {
    OrderedSelection {
        table,
        candidates: OrderedCandidates::EveryRow {
            row_count: table.len(),
        },
        ordering,
    }
}

#[cfg(test)]
mod tests;