nokhwa-iter helper crate

This commit is contained in:
l1npengtul
2025-09-12 20:01:01 +09:00
parent 97048c2d49
commit f6594f7852
41 changed files with 613 additions and 21 deletions
+12
View File
@@ -0,0 +1,12 @@
[package]
name = "nokhwa-iter-extensions"
version = "0.1.0"
authors = ["l1npengtul <l1npengtul@protonmail.com>"]
edition = "2024"
description = "Core type definitions for nokhwa"
keywords = ["iter", "iterator", "interweave", "duplicate"]
categories = ["algorithms", "rust-patterns", "no-std", "no-std::no-alloc"]
license = "MIT OR Apache-2.0"
repository = "https://github.com/l1npengtul/nokhwa"
[dependencies]
+138
View File
@@ -0,0 +1,138 @@
use core::iter::FusedIterator;
#[derive(Copy, Clone, Debug, Default, Hash, Ord, PartialOrd, Eq, PartialEq)]
enum DuplicateConstState {
#[default]
Started,
EmitDupe,
EmitReal,
Finished
}
/// Duplicates the items in the iterator `MULTIPLIER` times.
///
/// 0 acts as 1.
///
/// This iterator is _fused_.
pub struct DuplicateConst<I, const MULTIPLIER: usize> where
I: Iterator,
<I as Iterator>::Item: Clone {
iter: I,
last_iter_item: Option<I::Item>,
running_count: usize,
state: DuplicateConstState,
}
impl<I, const MULTIPLIER: usize> DuplicateConst<I, MULTIPLIER> where I: Iterator,
<I as Iterator>::Item: Clone {
pub fn new(iter: I) -> DuplicateConst<I, MULTIPLIER> {
DuplicateConst {
iter,
last_iter_item: None,
running_count: MULTIPLIER.saturating_sub(1),
state: DuplicateConstState::default(),
}
}
}
impl<I, const MULTIPLIER: usize> Iterator for DuplicateConst<I, MULTIPLIER> where
I: Iterator,
<I as Iterator>::Item: Clone
{
type Item = I::Item;
fn next(&mut self) -> Option<Self::Item> {
match self.state {
DuplicateConstState::Started => {
match self.iter.next() {
Some(i) => {
if MULTIPLIER <= 1 {
self.state = DuplicateConstState::EmitReal;
} else {
self.state = DuplicateConstState::EmitDupe;
}
self.last_iter_item = Some(i.clone());
Some(i)
}
None => {
self.state = DuplicateConstState::Finished;
None
}
}
}
DuplicateConstState::EmitDupe => {
self.running_count = self.running_count.saturating_sub(1);
if self.running_count <= 0 {
self.state = DuplicateConstState::EmitReal;
}
self.last_iter_item.clone()
}
DuplicateConstState::EmitReal => {
match self.iter.next() {
Some(i) => {
self.last_iter_item = Some(i.clone());
self.running_count = MULTIPLIER.saturating_sub(1);
if MULTIPLIER <= 1 {
self.state = DuplicateConstState::EmitReal;
} else {
self.state = DuplicateConstState::EmitDupe;
}
Some(i)
}
None => {
self.state = DuplicateConstState::Finished;
None
}
}
}
DuplicateConstState::Finished => {
None
}
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let (lower, upper) = self.iter.size_hint();
let multi = if MULTIPLIER == 0 { 1 } else { MULTIPLIER + 1 };
(lower * multi, upper.map(|u| u * multi))
}
}
impl<I, const MULTIPLIER: usize> FusedIterator for DuplicateConst<I, MULTIPLIER> where I: Iterator, <I as Iterator>::Item: Clone {}
pub trait IterDuplicateConst: Iterator {
fn duplicate_const<const MULTIPLIER: usize>(self) -> DuplicateConst<Self, MULTIPLIER> where Self: Sized, Self::Item: Clone {
DuplicateConst::new(self)
}
}
impl<I: ?Sized> IterDuplicateConst for I where I: Iterator {}
#[cfg(test)]
mod test {
use crate::duplicate::IterDuplicateConst;
#[test]
pub fn zero_acts_as_one() {
let initial = vec![0, 0, 0, 0];
let test_condition = vec![0, 0, 0, 0];
let result = initial.into_iter().duplicate_const::<0>().collect::<Vec<i32>>();
assert_eq!(test_condition, result)
}
#[test]
pub fn one_is_one() {
let initial = vec![0, 0, 0, 0];
let test_condition = vec![0, 0, 0, 0];
let result = initial.into_iter().duplicate_const::<1>().collect::<Vec<i32>>();
assert_eq!(test_condition, result)
}
#[test]
pub fn multiples() {
let initial = vec![0, 0, 0, 0, 0];
let test_condition = vec![0; 25];
let result = initial.into_iter().duplicate_const::<5>().collect::<Vec<i32>>();
assert_eq!(test_condition, result)
}
}
+155
View File
@@ -0,0 +1,155 @@
use core::iter::FusedIterator;
#[derive(Copy, Clone, Debug, Default, Hash, Ord, PartialOrd, Eq, PartialEq)]
enum InterweaveState {
EmitFake,
#[default]
EmitReal,
Finished
}
/// Interweaves a value for every `PER` values of the original iterator.
///
/// `emit_last`: Allows you to set if a last item should be emitted even though it is not the "turn"
/// of the "value" yet, e.g.
///
/// ```
/// let initial = vec![1, 2, 1, 2, 1];
// let test_condition = vec![1, 2, 3, 1, 2, 3, 1, 3];
// let result = initial.iter().interweave::<2>(&3, true).cloned().collect::<Vec<i32>>();
// assert_eq!(test_condition, result);
/// ```
///
/// This iterator is _fused_.
pub struct Interweave<I, const PER: usize> where
I: Iterator,
<I as Iterator>::Item: Clone {
element: I::Item,
iter: I,
prev_state: InterweaveState,
state: InterweaveState,
count: usize,
emit_last: bool,
}
impl<I, const PER: usize> Interweave<I, PER> where I: Iterator, <I as Iterator>::Item: Clone {
pub fn new(item: I::Item, iterator: I, emit_last: bool) -> Interweave<I, PER> {
Interweave {
element: item,
iter: iterator,
prev_state: InterweaveState::default(),
state: InterweaveState::default(),
count: PER,
emit_last,
}
}
}
impl<I, const PER: usize> Iterator for Interweave<I, PER> where I: Iterator, <I as Iterator>::Item: Clone {
type Item = I::Item;
fn next(&mut self) -> Option<Self::Item> {
match self.state {
InterweaveState::EmitFake => {
self.count = PER;
self.prev_state = InterweaveState::EmitFake;
self.state = InterweaveState::EmitReal;
Some(self.element.clone())
}
InterweaveState::EmitReal => {
self.count = self.count.saturating_sub(1);
match self.iter.next() {
Some(i) => {
if self.count <= 0 {
self.state = InterweaveState::EmitFake;
} else {
self.state = InterweaveState::EmitReal;
}
self.prev_state = InterweaveState::EmitReal;
Some(i)
}
None => {
self.state = InterweaveState::Finished;
if self.emit_last && self.prev_state != InterweaveState::EmitFake {
Some(self.element.clone())
} else {
None
}
}
}
}
InterweaveState::Finished => {
None
}
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let (lower, upper) = self.iter.size_hint();
if PER == 1 || PER == 0 {
return (lower * 2, upper.map(|u| u * 2))
}
let last = if self.emit_last { 1 } else { 0 };
let new_lower = lower + (lower / PER) + last;
let new_upper = upper.map(|u| { u + (u / PER) + last });
(new_lower, new_upper)
}
}
impl<I, const PER: usize> FusedIterator for Interweave<I, PER> where I: Iterator, <I as Iterator>::Item: Clone {}
pub trait IterInterweave: Iterator {
fn interweave<const PER: usize>(self, item: Self::Item, emit_last: bool) -> Interweave<Self, PER> where Self: Sized, Self::Item: Clone {
Interweave::new(item, self, emit_last)
}
}
impl<I: ?Sized> IterInterweave for I where I: Iterator {}
#[cfg(test)]
mod test {
use crate::interweave::IterInterweave;
#[test]
pub fn zero_interweave() {
let initial = vec![0, 0, 0, 0, 0];
let test_condition = vec![0, 1, 0, 1, 0, 1, 0, 1, 0, 1];
let result = initial.iter().interweave::<1>(&1, false).cloned().collect::<Vec<i32>>();
assert_eq!(test_condition, result);
}
#[test]
pub fn empty_interweave_no_last() {
let initial: Vec<i32> = vec![];
let test_condition: Vec<i32> = vec![];
let result = initial.iter().interweave::<1>(&1, false).cloned().collect::<Vec<i32>>();
assert_eq!(test_condition, result);
}
#[test]
pub fn empty_interweave_with_last() {
let initial: Vec<i32> = vec![];
let test_condition: Vec<i32> = vec![1];
let result = initial.iter().interweave::<1>(&1, true).cloned().collect::<Vec<i32>>();
assert_eq!(test_condition, result);
}
#[test]
pub fn interweave_every_other() {
let initial = vec![1, 2, 1, 2, 1 ,2];
let test_condition = vec![1, 2, 3, 1, 2, 3, 1 , 2, 3];
let result = initial.iter().interweave::<2>(&3, true).cloned().collect::<Vec<i32>>();
assert_eq!(test_condition, result);
}
#[test]
pub fn interweave_every_other_not_fitting() {
let initial = vec![1, 2, 1, 2, 1];
let test_condition = vec![1, 2, 3, 1, 2, 3, 1, 3];
let result = initial.iter().interweave::<2>(&3, true).cloned().collect::<Vec<i32>>();
assert_eq!(test_condition, result);
}
}
+6
View File
@@ -0,0 +1,6 @@
#![no_std]
#[warn(clippy::pedantic)]
pub mod interweave;
pub mod duplicate;