mod key_extractor;
mod sorter;
mod tile_index;
pub use key_extractor::{IdentityKey, KeyExtractor};
pub fn tilesort<T: Ord + Clone>(data: &mut [T]) {
sorter::tilesort_impl_inplace(data, false);
}
pub fn tilesort_reverse<T: Ord + Clone>(data: &mut [T]) {
sorter::tilesort_impl_inplace(data, true);
}
pub fn tilesorted<T: Ord + Clone>(data: &[T]) -> Vec<T> {
sorter::tilesort_copy(data, false)
}
pub fn tilesorted_reverse<T: Ord + Clone>(data: &[T]) -> Vec<T> {
sorter::tilesort_copy(data, true)
}
pub fn tilesort_by_key<T, K, F>(data: &mut [T], key_fn: F)
where
T: Clone,
K: Ord + Clone,
F: Fn(&T) -> K,
{
sorter::tilesort_impl_with_key_inplace(data, key_fn, false);
}
pub fn tilesort_by_key_reverse<T, K, F>(data: &mut [T], key_fn: F)
where
T: Clone,
K: Ord + Clone,
F: Fn(&T) -> K,
{
sorter::tilesort_impl_with_key_inplace(data, key_fn, true);
}
pub fn tilesorted_by_key<T, K, F>(data: &[T], key_fn: F) -> Vec<T>
where
T: Clone,
K: Ord + Clone,
F: Fn(&T) -> K,
{
sorter::tilesort_copy_with_key(data, key_fn, false)
}
pub fn tilesorted_by_key_reverse<T, K, F>(data: &[T], key_fn: F) -> Vec<T>
where
T: Clone,
K: Ord + Clone,
F: Fn(&T) -> K,
{
sorter::tilesort_copy_with_key(data, key_fn, true)
}
#[cfg(feature = "python")]
mod python_bindings {
use pyo3::prelude::*;
use pyo3::types::{PyAny, PyList};
use crate::key_extractor::KeyExtractor;
use crate::sorter::{tilesort_impl_inplace, tilesort_impl_with_key_inplace};
use std::cmp::Ordering;
struct PyOrd {
obj: PyObject,
}
impl Clone for PyOrd {
fn clone(&self) -> Self {
Python::with_gil(|py| Self {
obj: self.obj.clone_ref(py),
})
}
}
impl PyOrd {
fn new(obj: PyObject) -> Self {
Self { obj }
}
fn into_inner(self) -> PyObject {
self.obj
}
}
impl std::fmt::Debug for PyOrd {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Python::with_gil(|py| write!(f, "{:?}", self.obj.bind(py)))
}
}
impl PartialEq for PyOrd {
fn eq(&self, other: &Self) -> bool {
Python::with_gil(|py| self.obj.bind(py).eq(other.obj.bind(py)).unwrap_or(false))
}
}
impl Eq for PyOrd {}
impl PartialOrd for PyOrd {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for PyOrd {
fn cmp(&self, other: &Self) -> Ordering {
Python::with_gil(|py| {
let self_obj = self.obj.bind(py);
let other_obj = other.obj.bind(py);
if self_obj.lt(other_obj).unwrap_or(false) {
Ordering::Less
} else if self_obj.gt(other_obj).unwrap_or(false) {
Ordering::Greater
} else {
Ordering::Equal
}
})
}
}
struct PyKeyExtractor {
key_fn: Py<PyAny>,
}
impl KeyExtractor<PyOrd, PyOrd> for PyKeyExtractor {
fn extract_key(&self, item: &PyOrd) -> PyOrd {
Python::with_gil(|py| {
let result = self
.key_fn
.call1(py, (&item.obj,))
.expect("Key function call failed");
PyOrd::new(result)
})
}
}
#[pyfunction]
#[pyo3(signature = (list, key=None, reverse=false))]
fn sort(list: &Bound<'_, PyList>, key: Option<Py<PyAny>>, reverse: bool) -> PyResult<()> {
Python::with_gil(|py| {
let mut items: Vec<PyOrd> = list.iter().map(|item| PyOrd::new(item.into())).collect();
if let Some(key_fn) = key {
let extractor = PyKeyExtractor { key_fn };
tilesort_impl_with_key_inplace(&mut items, extractor, reverse);
} else {
tilesort_impl_inplace(&mut items, reverse);
}
let empty = PyList::empty(py);
list.set_slice(0, list.len(), &empty)?;
for item in items {
list.append(item.into_inner())?;
}
Ok(())
})
}
#[pyfunction]
#[pyo3(signature = (list, key=None, reverse=false))]
fn sorted(
list: &Bound<'_, PyList>,
key: Option<Py<PyAny>>,
reverse: bool,
) -> PyResult<Py<PyList>> {
Python::with_gil(|py| {
let mut items: Vec<PyOrd> = list.iter().map(|item| PyOrd::new(item.into())).collect();
if let Some(key_fn) = key {
let extractor = PyKeyExtractor { key_fn };
tilesort_impl_with_key_inplace(&mut items, extractor, reverse);
} else {
tilesort_impl_inplace(&mut items, reverse);
}
let unwrapped: Vec<PyObject> =
items.into_iter().map(|item| item.into_inner()).collect();
let new_list = PyList::new(py, unwrapped)?;
Ok(new_list.into())
})
}
#[pymodule]
fn _tilesort(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_function(wrap_pyfunction!(sort, m)?)?;
m.add_function(wrap_pyfunction!(sorted, m)?)?;
Ok(())
}
}