Skip to content

Commit

Permalink
chore(merge): merge pull request #16
Browse files Browse the repository at this point in the history
Move `trie` into a standalone feature and update APIs
  • Loading branch information
ChieloNewctle authored Oct 26, 2023
2 parents a3e82c2 + 177b39d commit a108b7e
Show file tree
Hide file tree
Showing 9 changed files with 147 additions and 210 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,5 @@ jobs:
- run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }}
- run: cargo build --verbose
- run: cargo test --verbose
- run: cargo build --all-features --verbose
- run: cargo test --all-features --verbose
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/target
Cargo.lock

# Byte-compiled / optimized / DLL files
__pycache__/
Expand Down Expand Up @@ -69,4 +70,4 @@ docs/_build/
.vscode/

# Pyenv
.python-version
.python-version
75 changes: 0 additions & 75 deletions Cargo.lock

This file was deleted.

4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,7 @@ name = "general_sam"

[dev-dependencies]
rand = "0.8.5"

[features]
trie = []
all = ["trie"]
10 changes: 8 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
//! ```
//!
//! ```rust
//! # #[cfg(feature = "trie")] {
//! use general_sam::{sam::GeneralSAM, trie::Trie};
//!
//! let mut trie = Trie::default();
Expand All @@ -82,6 +83,7 @@
//!
//! assert!(!sam.get_root_state().feed_chars("bye").is_accepting());
//! assert!(sam.get_root_state().feed_chars("bye").is_nil());
//! # }
//! ```
//!
//! # References
Expand All @@ -95,15 +97,19 @@
//! [general-sam-oi-wiki]: https://oi-wiki.org/string/general-sam/
pub mod sam;
pub mod trie;
pub mod trie_alike;

#[cfg(feature = "trie")]
pub mod trie;

pub use sam::{
GeneralSAM, GeneralSAMNode, GeneralSAMNodeID, GeneralSAMState, SAM_NIL_NODE_ID,
SAM_ROOT_NODE_ID,
};
pub use trie::{Trie, TrieNode, TrieNodeID, TrieState, TRIE_NIL_NODE_ID, TRIE_ROOT_NODE_ID};
pub use trie_alike::{IterAsChain, TravelEvent, TrieNodeAlike};

#[cfg(feature = "trie")]
pub use trie::{Trie, TrieNode, TrieNodeID, TrieState, TRIE_NIL_NODE_ID, TRIE_ROOT_NODE_ID};

#[cfg(test)]
mod tests;
30 changes: 11 additions & 19 deletions src/sam/mod.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
mod state;
pub use state::GeneralSAMState;

use std::{
collections::{BTreeMap, VecDeque},
convert::Infallible,
};
use std::{collections::BTreeMap, convert::Infallible};

use crate::trie_alike::{IterAsChain, TravelEvent, TrieNodeAlike};
use crate::{
trie_alike::{IterAsChain, TrieNodeAlike},
TravelEvent,
};

pub type GeneralSAMNodeID = usize;
pub const SAM_NIL_NODE_ID: GeneralSAMNodeID = 0;
Expand Down Expand Up @@ -142,22 +142,14 @@ impl<T: Ord + Clone> GeneralSAM<T> {
where
TN::InnerType: Into<T>,
{
let mut queue = VecDeque::new();
let mut last_node_id = SAM_ROOT_NODE_ID;
node.bfs_travel(|event| -> Result<(), Infallible> {
node.bfs_travel(|event| -> Result<GeneralSAMNodeID, Infallible> {
match event {
TravelEvent::Push(_, None) => {
queue.push_back(SAM_ROOT_NODE_ID);
}
TravelEvent::Pop(_) => {
last_node_id = queue.pop_front().unwrap();
TravelEvent::PushRoot(_) => Ok(SAM_ROOT_NODE_ID),
TravelEvent::Push(cur_tn, cur_node_id, key) => {
Ok(self.insert_node_trans(*cur_node_id, key, cur_tn.is_accepting()))
}
TravelEvent::Push(tn, Some(key)) => {
let new_node_id = self.insert_node_trans(last_node_id, key, tn.is_accepting());
queue.push_back(new_node_id);
}
};
Ok(())
TravelEvent::Pop(_, cur_node_id) => Ok(cur_node_id),
}
})
.unwrap();
}
Expand Down
148 changes: 64 additions & 84 deletions src/sam/state.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use std::collections::VecDeque;

use crate::trie_alike::{TravelEvent, TrieNodeAlike};

use super::{GeneralSAM, GeneralSAMNode, SAM_NIL_NODE_ID, SAM_ROOT_NODE_ID};
Expand All @@ -16,13 +14,13 @@ impl<'s> GeneralSAMState<'s, u8> {
}
}

impl<'s> GeneralSAMState<'s, char> {
pub fn feed_chars(self, seq: &'s str) -> Self {
impl GeneralSAMState<'_, char> {
pub fn feed_chars(self, seq: &str) -> Self {
self.feed(seq.chars())
}
}

impl<'s, T: Ord + Clone> GeneralSAMState<'s, T> {
impl<T: Ord + Clone> GeneralSAMState<'_, T> {
pub fn is_nil(&self) -> bool {
self.node_id == SAM_NIL_NODE_ID
}
Expand Down Expand Up @@ -58,116 +56,98 @@ impl<'s, T: Ord + Clone> GeneralSAMState<'s, T> {
}
}

pub fn feed_ref<Seq: IntoIterator<Item = &'s T>>(self, seq: Seq) -> Self {
self.feed_ref_iter(seq.into_iter())
pub fn feed<Seq: IntoIterator<Item = T>>(self, seq: Seq) -> Self {
self.feed_iter(seq.into_iter())
}

pub fn feed_ref_iter<Iter: Iterator<Item = &'s T>>(mut self, iter: Iter) -> Self {
pub fn feed_iter<Iter: Iterator<Item = T>>(mut self, iter: Iter) -> Self {
for t in iter {
if self.is_nil() {
break;
}
self.goto(t)
self.goto(&t)
}
self
}
}

pub fn feed<Seq: IntoIterator<Item = T>>(self, seq: Seq) -> Self {
self.feed_iter(seq.into_iter())
impl<'s, T: Ord + Clone> GeneralSAMState<'s, T> {
pub fn feed_ref<Seq: IntoIterator<Item = &'s T>>(self, seq: Seq) -> Self {
self.feed_ref_iter(seq.into_iter())
}

pub fn feed_iter<Iter: Iterator<Item = T>>(mut self, iter: Iter) -> Self {
pub fn feed_ref_iter<Iter: Iterator<Item = &'s T>>(mut self, iter: Iter) -> Self {
for t in iter {
if self.is_nil() {
break;
}
self.goto(&t)
self.goto(t)
}
self
}
}

pub fn bfs_along<
TN: TrieNodeAlike<InnerType = T> + Sized,
E,
F: FnMut(TravelEvent<(GeneralSAMState<'_, T>, &TN), TN::InnerType>) -> Result<(), E>,
impl<'s, T: Ord + Clone> GeneralSAMState<'s, T> {
fn wrap_travel_along_callback<
TN: TrieNodeAlike<InnerType = T>,
ExtraType,
ErrorType,
F: 's
+ FnMut(
TravelEvent<(&GeneralSAMState<T>, &TN), ExtraType, TN::InnerType>,
) -> Result<ExtraType, ErrorType>,
>(
&self,
trie_node: TN,
&'s self,
mut callback: F,
) -> Result<(), E> {
let mut queue = VecDeque::new();
let mut cur_node_id = self.node_id;

trie_node.bfs_travel(|event| match event {
TravelEvent::Push(tn, Some(key)) => {
let next_node_id = self
.sam
.node_pool
.get(cur_node_id)
.and_then(|x| x.trans.get(&key).copied())
.unwrap_or(SAM_NIL_NODE_ID);
callback(TravelEvent::Push(
(self.sam.get_state(next_node_id), tn),
Some(key),
))?;
queue.push_back(next_node_id);
Ok(())
) -> impl FnMut(
TravelEvent<&TN, (GeneralSAMState<'s, T>, ExtraType), TN::InnerType>,
) -> Result<(GeneralSAMState<'s, T>, ExtraType), ErrorType> {
move |event| match event {
TravelEvent::PushRoot(trie_root) => {
let res = callback(TravelEvent::PushRoot((self, trie_root)))?;
Ok((self.clone(), res))
}
TravelEvent::Push(tn, None) => {
callback(TravelEvent::Push(
(self.sam.get_state(self.node_id), tn),
None,
))?;
queue.push_back(self.node_id);
Ok(())
TravelEvent::Push(cur_tn, (cur_state, cur_extra), key) => {
let mut next_state = cur_state.clone();
next_state.goto(&key);
let next_extra =
callback(TravelEvent::Push((&next_state, &cur_tn), cur_extra, key))?;
Ok((next_state, next_extra))
}
TravelEvent::Pop(tn) => {
cur_node_id = queue.pop_front().unwrap();
callback(TravelEvent::Pop((self.sam.get_state(cur_node_id), tn)))?;
Ok(())
TravelEvent::Pop(cur_tn, (cur_state, extra)) => {
let res = callback(TravelEvent::Pop((&cur_state, cur_tn), extra))?;
Ok((cur_state, res))
}
})
}
}

pub fn dfs_along<
TN: TrieNodeAlike<InnerType = T> + Clone,
E,
F: FnMut(TravelEvent<(GeneralSAMState<'_, T>, &TN), TN::InnerType>) -> Result<(), E>,
ExtraType,
ErrorType,
F: FnMut(
TravelEvent<(&GeneralSAMState<'_, T>, &TN), ErrorType, TN::InnerType>,
) -> Result<ErrorType, ExtraType>,
>(
&self,
trie_node: TN,
mut callback: F,
) -> Result<(), E> {
let mut stack: Vec<usize> = Vec::new();

trie_node.dfs_travel(|event| match event {
TravelEvent::Push(tn, Some(key)) => {
let next_node_id = self
.sam
.node_pool
.get(*stack.last().unwrap())
.and_then(|x| x.trans.get(&key).copied())
.unwrap_or(SAM_NIL_NODE_ID);
callback(TravelEvent::Push(
(self.sam.get_state(next_node_id), tn),
Some(key),
))?;
stack.push(next_node_id);
Ok(())
}
TravelEvent::Push(tn, None) => {
callback(TravelEvent::Push(
(self.sam.get_state(self.node_id), tn),
None,
))?;
stack.push(self.node_id);
Ok(())
}
TravelEvent::Pop(tn) => {
let node_id = stack.pop().unwrap();
callback(TravelEvent::Pop((self.sam.get_state(node_id), tn)))?;
Ok(())
}
})
callback: F,
) -> Result<(), ExtraType> {
trie_node.dfs_travel(self.wrap_travel_along_callback(callback))
}

pub fn bfs_along<
TN: TrieNodeAlike<InnerType = T> + Clone,
ExtraType,
ErrorType,
F: FnMut(
TravelEvent<(&GeneralSAMState<'_, T>, &TN), ErrorType, TN::InnerType>,
) -> Result<ErrorType, ExtraType>,
>(
&self,
trie_node: TN,
callback: F,
) -> Result<(), ExtraType> {
trie_node.bfs_travel(self.wrap_travel_along_callback(callback))
}
}
Loading

0 comments on commit a108b7e

Please sign in to comment.