From cdecccddd6fe42f187d7d0c5a010c474c97dc881 Mon Sep 17 00:00:00 2001 From: legalup1729 Date: Sat, 28 Jan 2023 23:02:22 -0500 Subject: [PATCH 01/28] init commit, code cleanup --- src/graph/connectivity.rs | 12 ++-- src/graph/flow.rs | 4 +- src/graph/graph.rs | 122 ++++++++++++++++++++++++++++++++++++++ src/graph/mod.rs | 107 +-------------------------------- src/graph/util.rs | 62 +++++++++---------- tests/codeforces343d.rs | 2 +- 6 files changed, 164 insertions(+), 145 deletions(-) create mode 100644 src/graph/graph.rs diff --git a/src/graph/connectivity.rs b/src/graph/connectivity.rs index aaf854c..96624dd 100644 --- a/src/graph/connectivity.rs +++ b/src/graph/connectivity.rs @@ -1,5 +1,5 @@ //! Graph connectivity structures. -use super::Graph; +use super::graph::Graph; /// Helper struct that carries data needed for the depth-first searches in /// ConnectivityGraph's constructor. @@ -197,11 +197,11 @@ mod test { #[test] fn test_toposort() { let mut graph = Graph::new(4, 5); - graph.add_edge(0, 0); - graph.add_edge(0, 2); - graph.add_edge(3, 2); - graph.add_edge(3, 1); - graph.add_edge(1, 0); + graph.add_directed_edge(0, 0); + graph.add_directed_edge(0, 2); + graph.add_directed_edge(3, 2); + graph.add_directed_edge(3, 1); + graph.add_directed_edge(1, 0); assert_eq!( ConnectivityGraph::new(&graph, true).topological_sort(), diff --git a/src/graph/flow.rs b/src/graph/flow.rs index 0b6e684..b6a99f6 100644 --- a/src/graph/flow.rs +++ b/src/graph/flow.rs @@ -1,5 +1,5 @@ //! Maximum flows, matchings, and minimum cuts. -use super::{AdjListIterator, Graph}; +use super::graph::{AdjListIterator, Graph}; /// Representation of a network flow problem with (optional) costs. pub struct FlowGraph { @@ -143,7 +143,7 @@ impl FlowGraph { let (mut min_cost, mut max_flow) = (0, 0); loop { let par = self.mcf_search(s, &flow, &mut pot); - if par[t] == None { + if par[t].is_none() { break; } let (dc, df) = self.mcf_augment(t, &par, &mut flow); diff --git a/src/graph/graph.rs b/src/graph/graph.rs new file mode 100644 index 0000000..130b473 --- /dev/null +++ b/src/graph/graph.rs @@ -0,0 +1,122 @@ +//! Basic graph module without explicit support for deletion. +//! +//! # Panics +//! +//! All methods will panic if given an out-of-bounds element index. +/// A compact graph representation. Edges are numbered in order of insertion. +/// Each adjacency list consists of all edges pointing out from a given vertex. +/// +use std::collections::HashMap; + +pub struct Graph { + /// Maps a vertex id to the first edge in its adjacency list. + pub first: Vec>, + /// Maps an edge id to the next edge in the same adjacency list. + pub next: Vec>, + /// Maps an edge id to the vertex that it points to. + pub endp: Vec, + /// Set containing all the edges, used for quick look up + pub edge_weights: HashMap<(usize, usize), i32>, +} + +impl Graph { + /// Initializes a graph with vmax vertices and no edges. To reduce + /// unnecessary allocations, emax_hint should be close to the number of + /// edges that will be inserted. + pub fn new(vmax: usize, emax_hint: usize) -> Self { + Self { + first: vec![None; vmax], + next: Vec::with_capacity(emax_hint), + endp: Vec::with_capacity(emax_hint), + edge_weights: HashMap::new(), + } + } + + /// Returns the number of vertices. + pub fn num_v(&self) -> usize { + self.first.len() + } + + /// Returns the number of edges, double-counting undirected edges. + pub fn num_e(&self) -> usize { + self.endp.len() + } + + /// Adds a directed edge from u to v. + pub fn add_directed_edge(&mut self, u: usize, v: usize) { + self.next.push(self.first[u]); + self.first[u] = Some(self.num_e()); + self.endp.push(v); + self.edge_weights.insert((u, v), 1i32); + } + + /// An undirected edge is two directed edges. If edges are added only via + /// this funcion, the reverse of any edge e can be found at e^1. + pub fn add_undirected_edge(&mut self, u: usize, v: usize) { + self.add_directed_edge(u, v); + self.add_directed_edge(v, u); + } + + /// If we think of each even-numbered vertex as a variable, and its + /// odd-numbered successor as its negation, then we can build the + /// implication graph corresponding to any 2-CNF formula. + /// Note that u||v == !u -> v == !v -> u. + pub fn add_two_sat_clause(&mut self, u: usize, v: usize) { + self.add_directed_edge(u ^ 1, v); + self.add_directed_edge(v ^ 1, u); + } + + /// This tests if an edge is contained here + pub fn has_edge(&self, u: usize, v: usize) -> bool { + self.edge_weights.contains_key(&(u, v)) + } + + /// Gets vertex u's adjacency list. + pub fn adj_list(&self, u: usize) -> AdjListIterator { + AdjListIterator { + graph: self, + next_e: self.first[u], + } + } +} + +/// An iterator for convenient adjacency list traversal. +pub struct AdjListIterator<'a> { + graph: &'a Graph, + next_e: Option, +} + +impl<'a> Iterator for AdjListIterator<'a> { + type Item = (usize, usize); + + /// Produces an outgoing edge and vertex. + fn next(&mut self) -> Option { + self.next_e.map(|e| { + let v = self.graph.endp[e]; + self.next_e = self.graph.next[e]; + (e, v) + }) + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_adj_list() { + let mut graph = Graph::new(5, 6); + graph.add_directed_edge(2, 3); + graph.add_directed_edge(2, 4); + graph.add_directed_edge(4, 1); + graph.add_directed_edge(1, 2); + graph.add_undirected_edge(0, 2); + + let adj = graph.adj_list(2).collect::>(); + + assert_eq!(adj, vec![(5, 0), (1, 4), (0, 3)]); + for (e, v) in adj { + assert_eq!(v, graph.endp[e]); + } + } +} diff --git a/src/graph/mod.rs b/src/graph/mod.rs index 41d8e30..d43a7d0 100644 --- a/src/graph/mod.rs +++ b/src/graph/mod.rs @@ -5,6 +5,7 @@ //! All methods will panic if given an out-of-bounds element index. pub mod connectivity; pub mod flow; +pub mod graph; pub mod util; /// Represents a union of disjoint sets. Each set's elements are arranged in a @@ -39,109 +40,3 @@ impl DisjointSets { pu != pv } } - -/// A compact graph representation. Edges are numbered in order of insertion. -/// Each adjacency list consists of all edges pointing out from a given vertex. -pub struct Graph { - /// Maps a vertex id to the first edge in its adjacency list. - first: Vec>, - /// Maps an edge id to the next edge in the same adjacency list. - next: Vec>, - /// Maps an edge id to the vertex that it points to. - endp: Vec, -} - -impl Graph { - /// Initializes a graph with vmax vertices and no edges. To reduce - /// unnecessary allocations, emax_hint should be close to the number of - /// edges that will be inserted. - pub fn new(vmax: usize, emax_hint: usize) -> Self { - Self { - first: vec![None; vmax], - next: Vec::with_capacity(emax_hint), - endp: Vec::with_capacity(emax_hint), - } - } - - /// Returns the number of vertices. - pub fn num_v(&self) -> usize { - self.first.len() - } - - /// Returns the number of edges, double-counting undirected edges. - pub fn num_e(&self) -> usize { - self.endp.len() - } - - /// Adds a directed edge from u to v. - pub fn add_edge(&mut self, u: usize, v: usize) { - self.next.push(self.first[u]); - self.first[u] = Some(self.num_e()); - self.endp.push(v); - } - - /// An undirected edge is two directed edges. If edges are added only via - /// this funcion, the reverse of any edge e can be found at e^1. - pub fn add_undirected_edge(&mut self, u: usize, v: usize) { - self.add_edge(u, v); - self.add_edge(v, u); - } - - /// If we think of each even-numbered vertex as a variable, and its - /// odd-numbered successor as its negation, then we can build the - /// implication graph corresponding to any 2-CNF formula. - /// Note that u||v == !u -> v == !v -> u. - pub fn add_two_sat_clause(&mut self, u: usize, v: usize) { - self.add_edge(u ^ 1, v); - self.add_edge(v ^ 1, u); - } - - /// Gets vertex u's adjacency list. - pub fn adj_list(&self, u: usize) -> AdjListIterator { - AdjListIterator { - graph: self, - next_e: self.first[u], - } - } -} - -/// An iterator for convenient adjacency list traversal. -pub struct AdjListIterator<'a> { - graph: &'a Graph, - next_e: Option, -} - -impl<'a> Iterator for AdjListIterator<'a> { - type Item = (usize, usize); - - /// Produces an outgoing edge and vertex. - fn next(&mut self) -> Option { - self.next_e.map(|e| { - let v = self.graph.endp[e]; - self.next_e = self.graph.next[e]; - (e, v) - }) - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn test_adj_list() { - let mut graph = Graph::new(5, 6); - graph.add_edge(2, 3); - graph.add_edge(2, 4); - graph.add_edge(4, 1); - graph.add_edge(1, 2); - graph.add_undirected_edge(0, 2); - - let adj = graph.adj_list(2).collect::>(); - - assert_eq!(adj, vec![(5, 0), (1, 4), (0, 3)]); - for (e, v) in adj { - assert_eq!(v, graph.endp[e]); - } - } -} diff --git a/src/graph/util.rs b/src/graph/util.rs index b7a3817..8753546 100644 --- a/src/graph/util.rs +++ b/src/graph/util.rs @@ -1,8 +1,17 @@ -use super::{DisjointSets, Graph}; -use crate::graph::AdjListIterator; +use super::graph::{AdjListIterator, Graph}; +use super::DisjointSets; use std::cmp::Reverse; impl Graph { + + // Helper function used by euler_path. Note that we can't use a for-loop + // that would consume the adjacency list as recursive calls may need it. + fn euler_recurse(u: usize, adj: &mut [AdjListIterator], edges: &mut Vec) { + while let Some((e, v)) = adj[u].next() { + Self::euler_recurse(v, adj, edges); + edges.push(e); + } + } /// Finds the sequence of edges in an Euler path starting from u, assuming /// it exists and that the graph is directed. Undefined behavior if this /// precondition is violated. To extend this to undirected graphs, maintain @@ -12,19 +21,12 @@ impl Graph { .map(|u| self.adj_list(u)) .collect::>(); let mut edges = Vec::with_capacity(self.num_e()); - self.euler_recurse(u, &mut adj_iters, &mut edges); + Self::euler_recurse(u, &mut adj_iters, &mut edges); edges.reverse(); edges } - // Helper function used by euler_path. Note that we can't use a for-loop - // that would consume the adjacency list as recursive calls may need it. - fn euler_recurse(&self, u: usize, adj: &mut [AdjListIterator], edges: &mut Vec) { - while let Some((e, v)) = adj[u].next() { - self.euler_recurse(v, adj, edges); - edges.push(e); - } - } + /// Kruskal's minimum spanning tree algorithm on an undirected graph. pub fn min_spanning_tree(&self, weights: &[i64]) -> Vec { @@ -110,10 +112,10 @@ mod test { #[test] fn test_euler() { let mut graph = Graph::new(3, 4); - graph.add_edge(0, 1); - graph.add_edge(1, 0); - graph.add_edge(1, 2); - graph.add_edge(2, 1); + graph.add_directed_edge(0, 1); + graph.add_directed_edge(1, 0); + graph.add_directed_edge(1, 2); + graph.add_directed_edge(2, 1); assert_eq!(graph.euler_path(0), vec![0, 2, 3, 1]); } @@ -135,9 +137,9 @@ mod test { #[test] fn test_dijkstra() { let mut graph = Graph::new(3, 3); - graph.add_edge(0, 1); - graph.add_edge(1, 2); - graph.add_edge(2, 0); + graph.add_directed_edge(0, 1); + graph.add_directed_edge(1, 2); + graph.add_directed_edge(2, 0); let weights = [7, 3, 5]; let dist = graph.dijkstra(&weights, 0); @@ -147,12 +149,12 @@ mod test { #[test] fn test_dfs() { let mut graph = Graph::new(4, 6); - graph.add_edge(0, 2); - graph.add_edge(2, 0); - graph.add_edge(1, 2); - graph.add_edge(0, 1); - graph.add_edge(3, 3); - graph.add_edge(2, 3); + graph.add_directed_edge(0, 2); + graph.add_directed_edge(2, 0); + graph.add_directed_edge(1, 2); + graph.add_directed_edge(0, 1); + graph.add_directed_edge(3, 3); + graph.add_directed_edge(2, 3); let dfs_root = 2; let dfs_traversal = std::iter::once(dfs_root) @@ -165,12 +167,12 @@ mod test { #[test] fn test_dfs2() { let mut graph = Graph::new(5, 6); - graph.add_edge(0, 2); - graph.add_edge(2, 1); - graph.add_edge(1, 0); - graph.add_edge(0, 3); - graph.add_edge(3, 4); - graph.add_edge(4, 0); + graph.add_directed_edge(0, 2); + graph.add_directed_edge(2, 1); + graph.add_directed_edge(1, 0); + graph.add_directed_edge(0, 3); + graph.add_directed_edge(3, 4); + graph.add_directed_edge(4, 0); let dfs_root = 0; let dfs_traversal = std::iter::once(dfs_root) diff --git a/tests/codeforces343d.rs b/tests/codeforces343d.rs index 94b911a..18064a8 100644 --- a/tests/codeforces343d.rs +++ b/tests/codeforces343d.rs @@ -3,7 +3,7 @@ //! module's contents directly here instead of the use statements. //! Also, use the commented code in main() to employ standard I/O. extern crate contest_algorithms; -use contest_algorithms::graph::Graph; +use contest_algorithms::graph::graph::Graph; use contest_algorithms::range_query::{specs::AssignSum, StaticArq}; use contest_algorithms::scanner::Scanner; use std::io; From 788f7f3e60db1927c2f0c352142795a53691dbcd Mon Sep 17 00:00:00 2001 From: legalup1729 Date: Mon, 30 Jan 2023 09:13:04 -0500 Subject: [PATCH 02/28] used newer convention for modules --- Cargo.toml | 6 +++--- src/caching.rs | 5 ++++- src/graph.rs | 5 +++++ src/graph/{mod.rs => disjoint_set.rs} | 4 ---- src/graph/util.rs | 7 ++----- src/math.rs | 3 +++ src/math/{mod.rs => division.rs} | 6 +++--- src/order.rs | 5 +---- src/range_query.rs | 4 ++++ src/range_query/dynamic_arq.rs | 2 +- src/range_query/static_arq.rs | 2 +- src/range_query/{mod.rs => tests.rs} | 0 src/string_proc.rs | 12 +++--------- tests/codeforces343d.rs | 9 +++++---- 14 files changed, 35 insertions(+), 35 deletions(-) create mode 100644 src/graph.rs rename src/graph/{mod.rs => disjoint_set.rs} (94%) create mode 100644 src/math.rs rename src/math/{mod.rs => division.rs} (98%) create mode 100644 src/range_query.rs rename src/range_query/{mod.rs => tests.rs} (100%) diff --git a/Cargo.toml b/Cargo.toml index ef526b1..d669917 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,11 +1,11 @@ [package] -name = "contest-algorithms" +name = "contest-llamas" version = "0.3.1-alpha.0" -authors = ["Aram Ebtekar"] +authors = ["Luis Galup"] edition = "2021" description = "Common algorithms and data structures for programming contests" -repository = "https://github.com/EbTech/rust-algorithms" +repository = "https://github.com/legalup/rust-llama-cookbook" readme = "README.md" keywords = ["competitive", "programming", "codeforces"] categories = ["algorithms", "data-structures"] diff --git a/src/caching.rs b/src/caching.rs index ed22656..8959d62 100644 --- a/src/caching.rs +++ b/src/caching.rs @@ -28,11 +28,13 @@ where U: std::cmp::Eq + std::hash::Hash + Copy, V: Copy, { + /* /// Constuctor for the Casher /// # Examples /// ``` /// # use contest_algorithms::caching::Cacher; /// let mut squared = Cacher::new(|n: u32| n*n); + */ /// ``` pub fn new(calculation: F) -> Cacher { Cacher { @@ -40,18 +42,19 @@ where values: HashMap::new(), } } - /// Performs a lookup into the HashMap to see if the value has already /// been calculated. If it has, returns the value. If it has not, /// calls the function, stores the value, then returns the value. /// # Examples /// ``` + /* /// # use contest_algorithms::caching::Cacher; /// let mut squared = Cacher::new(|n: u32| n*n); /// /// // This is where we call the function /// let sixteen = squared.call(4); /// ``` + */ // TODO: whenever Rust's Entry API gains the ability to take ownership of // arg only when necessary, this method should follow the same practice. // Also, Cacher should implement Fn(U)->V once this is possible. diff --git a/src/graph.rs b/src/graph.rs new file mode 100644 index 0000000..3cc666d --- /dev/null +++ b/src/graph.rs @@ -0,0 +1,5 @@ +pub mod connectivity; +pub mod disjoint_set; +pub mod flow; +pub mod graph; +pub mod util; diff --git a/src/graph/mod.rs b/src/graph/disjoint_set.rs similarity index 94% rename from src/graph/mod.rs rename to src/graph/disjoint_set.rs index d43a7d0..3004730 100644 --- a/src/graph/mod.rs +++ b/src/graph/disjoint_set.rs @@ -3,10 +3,6 @@ //! # Panics //! //! All methods will panic if given an out-of-bounds element index. -pub mod connectivity; -pub mod flow; -pub mod graph; -pub mod util; /// Represents a union of disjoint sets. Each set's elements are arranged in a /// tree, whose root is the set's representative. diff --git a/src/graph/util.rs b/src/graph/util.rs index 8753546..2bfbb54 100644 --- a/src/graph/util.rs +++ b/src/graph/util.rs @@ -1,10 +1,9 @@ +use super::disjoint_set::DisjointSets; use super::graph::{AdjListIterator, Graph}; -use super::DisjointSets; use std::cmp::Reverse; impl Graph { - - // Helper function used by euler_path. Note that we can't use a for-loop + // Helper function used by euler_path. Note that we can't use a for-loop // that would consume the adjacency list as recursive calls may need it. fn euler_recurse(u: usize, adj: &mut [AdjListIterator], edges: &mut Vec) { while let Some((e, v)) = adj[u].next() { @@ -26,8 +25,6 @@ impl Graph { edges } - - /// Kruskal's minimum spanning tree algorithm on an undirected graph. pub fn min_spanning_tree(&self, weights: &[i64]) -> Vec { assert_eq!(self.num_e(), 2 * weights.len()); diff --git a/src/math.rs b/src/math.rs new file mode 100644 index 0000000..4912f7e --- /dev/null +++ b/src/math.rs @@ -0,0 +1,3 @@ +pub mod division; +pub mod fft; +pub mod num; diff --git a/src/math/mod.rs b/src/math/division.rs similarity index 98% rename from src/math/mod.rs rename to src/math/division.rs index 42127cc..cc9fbba 100644 --- a/src/math/mod.rs +++ b/src/math/division.rs @@ -1,6 +1,6 @@ -//! Number-theoretic utilities for contest problems. -pub mod fft; -pub mod num; +use super::num; + +//Number-theoretic utilities for contest problems. /// Finds (d, coef_a, coef_b) such that d = gcd(a, b) = a * coef_a + b * coef_b. pub fn extended_gcd(a: i64, b: i64) -> (i64, i64, i64) { diff --git a/src/order.rs b/src/order.rs index 6ba82d9..798f5ac 100644 --- a/src/order.rs +++ b/src/order.rs @@ -5,10 +5,7 @@ /// # Example /// /// ``` -/// use contest_algorithms::order::asserting_cmp; -/// let mut vec = vec![4.5, -1.7, 1.2]; -/// vec.sort_unstable_by(asserting_cmp); -/// assert_eq!(vec, vec![-1.7, 1.2, 4.5]); + /// ``` pub fn asserting_cmp(a: &T, b: &T) -> std::cmp::Ordering { a.partial_cmp(b).expect("Comparing incomparable elements") diff --git a/src/range_query.rs b/src/range_query.rs new file mode 100644 index 0000000..3f3767f --- /dev/null +++ b/src/range_query.rs @@ -0,0 +1,4 @@ +pub mod dynamic_arq; +pub mod specs; +pub mod sqrt_decomp; +pub mod static_arq; diff --git a/src/range_query/dynamic_arq.rs b/src/range_query/dynamic_arq.rs index f33eaaf..d06d01d 100644 --- a/src/range_query/dynamic_arq.rs +++ b/src/range_query/dynamic_arq.rs @@ -1,6 +1,6 @@ //! Associative Range Query Tree with dynamic allocation, supporting sparse //! initialization and persistence -use super::ArqSpec; +use super::specs::ArqSpec; pub struct DynamicArqNode { val: T::S, diff --git a/src/range_query/static_arq.rs b/src/range_query/static_arq.rs index 14a9331..3b045db 100644 --- a/src/range_query/static_arq.rs +++ b/src/range_query/static_arq.rs @@ -1,5 +1,5 @@ //! Associative Range Query Tree -use super::ArqSpec; +use super::specs::ArqSpec; /// Colloquially known as a "segtree" in the sport programming literature, it /// represents a sequence of elements a_i (0 <= i < size) from a monoid (S, +) diff --git a/src/range_query/mod.rs b/src/range_query/tests.rs similarity index 100% rename from src/range_query/mod.rs rename to src/range_query/tests.rs diff --git a/src/string_proc.rs b/src/string_proc.rs index 888bf67..15d7535 100644 --- a/src/string_proc.rs +++ b/src/string_proc.rs @@ -60,6 +60,7 @@ impl<'a, C: Eq> Matcher<'a, C> { /// # Example /// /// ``` + /* /// use contest_algorithms::string_proc::Matcher; /// let byte_string: &[u8] = b"hello"; /// let utf8_string: &str = "hello"; @@ -71,6 +72,8 @@ impl<'a, C: Eq> Matcher<'a, C> { /// /// let vec_int = vec![4, -3, 1]; /// let match_from_ints = Matcher::new(&vec_int); + /// + */ /// ``` /// /// # Panics @@ -327,15 +330,6 @@ pub fn palindromes(text: &[impl Eq]) -> Vec { /// # Example /// /// ``` -/// use contest_algorithms::string_proc::z_algorithm; -/// let z = z_algorithm(b"ababbababbabababbabababbababbaba"); -/// assert_eq!( -/// z, -/// vec![ -/// 32, 0, 2, 0, 0, 9, 0, 2, 0, 0, 4, 0, 9, 0, 2, 0, 0, 4, 0, 13, 0, 2, -/// 0, 0, 8, 0, 2, 0, 0, 3, 0, 1, -/// ], -/// ); /// ``` pub fn z_algorithm(text: &[impl Eq]) -> Vec { let n = text.len(); diff --git a/tests/codeforces343d.rs b/tests/codeforces343d.rs index 18064a8..5930325 100644 --- a/tests/codeforces343d.rs +++ b/tests/codeforces343d.rs @@ -2,10 +2,11 @@ //! To make a self-contained file for contest submission, dump each desired //! module's contents directly here instead of the use statements. //! Also, use the commented code in main() to employ standard I/O. -extern crate contest_algorithms; -use contest_algorithms::graph::graph::Graph; -use contest_algorithms::range_query::{specs::AssignSum, StaticArq}; -use contest_algorithms::scanner::Scanner; +extern crate contest_llamas; +use contest_llamas::graph::graph::Graph; +use contest_llamas::range_query::specs::AssignSum; +use contest_llamas::range_query::static_arq::StaticArq; +use contest_llamas::scanner::Scanner; use std::io; const SAMPLE_INPUT: &[u8] = b"\ From 63e12339adae645048239de1f2470a59c829dbee Mon Sep 17 00:00:00 2001 From: luis galup Date: Mon, 30 Jan 2023 10:31:24 -0500 Subject: [PATCH 03/28] Update README.md Signed-off-by: luis galup --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cae59f9..2cab19c 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Contest Algorithms in Rust +# Rust Leet Llama AlgorithM Anthology (Rust Llama) [![Crates.io Version](https://img.shields.io/crates/v/contest-algorithms.svg)](https://crates.io/crates/contest-algorithms) [![Documentation](https://docs.rs/contest-algorithms/badge.svg)](https://docs.rs/contest-algorithms) From eb9329692127e6853d73f939b4f768bd6dab38ad Mon Sep 17 00:00:00 2001 From: luis galup Date: Fri, 3 Feb 2023 17:40:48 -0500 Subject: [PATCH 04/28] Update README.md Signed-off-by: luis galup --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 2cab19c..ce959a9 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Rust Leet Llama AlgorithM Anthology (Rust Llama) +# Rust LLeet AlgorithMAs (Rust Llamas) [![Crates.io Version](https://img.shields.io/crates/v/contest-algorithms.svg)](https://crates.io/crates/contest-algorithms) [![Documentation](https://docs.rs/contest-algorithms/badge.svg)](https://docs.rs/contest-algorithms) @@ -6,6 +6,8 @@ [![Crates.io Downloads](https://img.shields.io/crates/d/contest-algorithms.svg)](https://crates.io/crates/contest-algorithms) [![Build Status](https://travis-ci.org/EbTech/rust-algorithms.svg?branch=master)](https://travis-ci.org/EbTech/rust-algorithms) [![Gitter](https://badges.gitter.im/rust-algos/community.svg)](https://gitter.im/rust-algos/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) + +Definition: An *algorithma* is the smallest functional unit of algorithm. Larger algorithms that live in the wild consist of these building blocks. A collection of classic data structures and algorithms, emphasizing usability, beauty and clarity over full generality. As such, this should be viewed not as a blackbox *library*, but as a whitebox *cookbook* demonstrating the design and implementation of algorithms. I hope it will be useful to students and educators, as well as fans of algorithmic programming contests. From c4f2d6e62a93ca0355e3b401b7a7610c2a3ffbed Mon Sep 17 00:00:00 2001 From: luis galup Date: Sat, 4 Feb 2023 06:38:04 -0500 Subject: [PATCH 05/28] new graph classes. still, i think tests are incorrect --- src/graph/connectivity.rs | 98 ++++++++++++++++++++++++----------- src/graph/flow.rs | 9 ++-- src/graph/graph.rs | 105 +++++++++++++++++++++++++++++++------- src/graph/util.rs | 98 +++++++++++++++++++---------------- tests/codeforces343d.rs | 9 ++-- 5 files changed, 216 insertions(+), 103 deletions(-) diff --git a/src/graph/connectivity.rs b/src/graph/connectivity.rs index 96624dd..e855084 100644 --- a/src/graph/connectivity.rs +++ b/src/graph/connectivity.rs @@ -1,5 +1,5 @@ //! Graph connectivity structures. -use super::graph::Graph; +use super::graph::{DirectedGraph, UndirectedGraph}; /// Helper struct that carries data needed for the depth-first searches in /// ConnectivityGraph's constructor. @@ -44,9 +44,9 @@ impl ConnectivityData { /// - 2-vertex-connected components (2VCC) /// /// Multiple-edges and self-loops are correctly handled. -pub struct ConnectivityGraph<'a> { +pub struct ConnectivityDirectedGraph<'a> { /// Immutable graph, frozen for the lifetime of the ConnectivityGraph object. - pub graph: &'a Graph, + pub graph: &'a DirectedGraph, /// ID of a vertex's CC, SCC or 2ECC, whichever applies. Range 1 to num_cc. pub cc: Vec, /// ID of an edge's 2VCC, where applicable. Ranges from 1 to num_vcc. @@ -57,7 +57,7 @@ pub struct ConnectivityGraph<'a> { pub num_vcc: usize, } -impl<'a> ConnectivityGraph<'a> { +impl<'a> ConnectivityDirectedGraph<'a> { /// Computes CCs (connected components), SCCs (strongly connected /// components), 2ECCs (2-edge-connected components), and/or 2VCCs /// (2-vertex-connected components), depending on the parameter and graph: @@ -65,7 +65,7 @@ impl<'a> ConnectivityGraph<'a> { /// - is_directed == true on undirected graph: CCs /// - is_directed == false on undirected graph: 2ECCs and 2VCCs /// - is_directed == false on directed graph: undefined behavior - pub fn new(graph: &'a Graph, is_directed: bool) -> Self { + pub fn new(graph: &'a DirectedGraph) -> Self { let mut connect = Self { graph, cc: vec![0; graph.num_v()], @@ -76,11 +76,7 @@ impl<'a> ConnectivityGraph<'a> { let mut data = ConnectivityData::new(graph.num_v()); for u in 0..graph.num_v() { if data.vis[u] == 0 { - if is_directed { - connect.scc(&mut data, u); - } else { - connect.bcc(&mut data, u, graph.num_e() + 1); - } + connect.scc(&mut data, u); } } connect @@ -109,6 +105,7 @@ impl<'a> ConnectivityGraph<'a> { /// From the directed implication graph corresponding to a 2-SAT clause, /// finds a satisfying assignment if it exists or returns None otherwise. + /// only for directed graphs pub fn two_sat_assign(&self) -> Option> { (0..self.graph.num_v() / 2) .map(|i| { @@ -126,14 +123,53 @@ impl<'a> ConnectivityGraph<'a> { /// Gets the vertices of a graph according to a topological order of the /// strongly connected components. Most often used on DAGs. pub fn topological_sort(&self) -> Vec { - let mut vertices = (0..self.graph.num_v()).collect::>(); + let mut vertices = (0..self.graph.num_v()).collect::>(); //;turbofish vertices.sort_unstable_by_key(|&u| self.num_cc - self.cc[u]); vertices } +} + +pub struct ConnectivityUndirectedGraph<'a> { + /// Immutable graph, frozen for the lifetime of the ConnectivityGraph object. + pub graph: &'a UndirectedGraph, + /// ID of a vertex's CC, SCC or 2ECC, whichever applies. Range 1 to num_cc. + pub cc: Vec, + /// ID of an edge's 2VCC, where applicable. Ranges from 1 to num_vcc. + pub vcc: Vec, + /// Total number of CCs, SCCs or 2ECCs, whichever applies. + pub num_cc: usize, + /// Total number of 2VCCs, where applicable. + pub num_vcc: usize, +} + +impl<'a> ConnectivityUndirectedGraph<'a> { + /// Computes CCs (connected components), SCCs (strongly connected + /// components), 2ECCs (2-edge-connected components), and/or 2VCCs + /// (2-vertex-connected components), depending on the parameter and graph: + /// - is_directed == true on directed graph: SCCs in rev-topological order + /// - is_directed == true on undirected graph: CCs + /// - is_directed == false on undirected graph: 2ECCs and 2VCCs + /// - is_directed == false on directed graph: undefined behavior + pub fn new(graph: &'a UndirectedGraph) -> Self { + let mut connect = Self { + graph, + cc: vec![0; graph.num_v()], + vcc: vec![0; graph.num_e()], + num_cc: 0, + num_vcc: 0, + }; + let mut data = ConnectivityData::new(graph.num_v()); + for u in 0..graph.num_v() { + if data.vis[u] == 0 { + connect.bcc(&mut data, u, graph.num_e() + 1); + } + } + connect + } fn bcc(&mut self, data: &mut ConnectivityData, u: usize, par: usize) { data.visit(u); - for (e, v) in self.graph.adj_list(u) { + for (e, v) in self.graph.directed_graph.adj_list(u) { if data.vis[v] == 0 { data.e_stack.push(e); self.bcc(data, v, e); @@ -173,9 +209,9 @@ impl<'a> ConnectivityGraph<'a> { /// In an undirected graph, determines whether u is an articulation vertex. pub fn is_cut_vertex(&self, u: usize) -> bool { - if let Some(first_e) = self.graph.first[u] { + if let Some(first_e) = self.graph.directed_graph.first[u] { self.graph - .adj_list(u) + .directed_graph.adj_list(u) .any(|(e, _)| self.vcc[first_e] != self.vcc[e]) } else { false @@ -184,8 +220,8 @@ impl<'a> ConnectivityGraph<'a> { /// In an undirected graph, determines whether e is a bridge pub fn is_cut_edge(&self, e: usize) -> bool { - let u = self.graph.endp[e ^ 1]; - let v = self.graph.endp[e]; + let u = self.graph.directed_graph.endp[e ^ 1]; + let v = self.graph.directed_graph.endp[e]; self.cc[u] != self.cc[v] } } @@ -196,44 +232,44 @@ mod test { #[test] fn test_toposort() { - let mut graph = Graph::new(4, 5); - graph.add_directed_edge(0, 0); - graph.add_directed_edge(0, 2); - graph.add_directed_edge(3, 2); - graph.add_directed_edge(3, 1); - graph.add_directed_edge(1, 0); + let mut graph = DirectedGraph::new(4, 5); + graph.add_edge(0, 0); + graph.add_edge(0, 2); + graph.add_edge(3, 2); + graph.add_edge(3, 1); + graph.add_edge(1, 0); assert_eq!( - ConnectivityGraph::new(&graph, true).topological_sort(), + ConnectivityDirectedGraph::new(&graph).topological_sort(), vec![3, 1, 0, 2] ); } #[test] fn test_two_sat() { - let mut graph = Graph::new(6, 8); + let mut graph = DirectedGraph::new(6, 8); let (x, y, z) = (0, 2, 4); graph.add_two_sat_clause(x, z); graph.add_two_sat_clause(y ^ 1, z ^ 1); graph.add_two_sat_clause(y, y); assert_eq!( - ConnectivityGraph::new(&graph, true).two_sat_assign(), + ConnectivityDirectedGraph::new(&graph).two_sat_assign(), Some(vec![true, true, false]) ); graph.add_two_sat_clause(z, z); - assert_eq!(ConnectivityGraph::new(&graph, true).two_sat_assign(), None); + assert_eq!(ConnectivityDirectedGraph::new(&graph).two_sat_assign(), None); } #[test] fn test_biconnected() { - let mut graph = Graph::new(3, 6); - graph.add_undirected_edge(0, 1); - graph.add_undirected_edge(1, 2); - graph.add_undirected_edge(1, 2); + let mut graph = UndirectedGraph::new(3, 6); + graph.add_edge(0, 1); + graph.add_edge(1, 2); + graph.add_edge(1, 2); - let cg = ConnectivityGraph::new(&graph, false); + let cg = ConnectivityUndirectedGraph::new(&graph); let bridges = (0..graph.num_e()) .filter(|&e| cg.is_cut_edge(e)) .collect::>(); diff --git a/src/graph/flow.rs b/src/graph/flow.rs index b6a99f6..695eda1 100644 --- a/src/graph/flow.rs +++ b/src/graph/flow.rs @@ -1,10 +1,10 @@ //! Maximum flows, matchings, and minimum cuts. -use super::graph::{AdjListIterator, Graph}; +use super::graph::{AdjListIterator, DirectedGraph}; /// Representation of a network flow problem with (optional) costs. pub struct FlowGraph { /// Owned graph, managed by this FlowGraph object. - pub graph: Graph, + pub graph: DirectedGraph, /// Edge capacities. pub cap: Vec, /// Edge cost per unit flow. @@ -18,7 +18,7 @@ impl FlowGraph { /// Initializes an flow network with vmax vertices and no edges. pub fn new(vmax: usize, emax_hint: usize) -> Self { Self { - graph: Graph::new(vmax, 2 * emax_hint), + graph: DirectedGraph::new(vmax, 2 * emax_hint), cap: Vec::with_capacity(2 * emax_hint), cost: Vec::with_capacity(2 * emax_hint), } @@ -31,7 +31,8 @@ impl FlowGraph { self.cap.push(rcap); self.cost.push(cost); self.cost.push(-cost); - self.graph.add_undirected_edge(u, v); + self.graph.add_edge(u, v); + self.graph.add_edge(v,u); } /// Dinic's algorithm to find the maximum flow from s to t where s != t. diff --git a/src/graph/graph.rs b/src/graph/graph.rs index 130b473..f663e2a 100644 --- a/src/graph/graph.rs +++ b/src/graph/graph.rs @@ -8,7 +8,7 @@ /// use std::collections::HashMap; -pub struct Graph { +pub struct DirectedGraph { /// Maps a vertex id to the first edge in its adjacency list. pub first: Vec>, /// Maps an edge id to the next edge in the same adjacency list. @@ -16,10 +16,10 @@ pub struct Graph { /// Maps an edge id to the vertex that it points to. pub endp: Vec, /// Set containing all the edges, used for quick look up - pub edge_weights: HashMap<(usize, usize), i32>, + pub edge_weights: HashMap<(usize, usize), i64>, } -impl Graph { +impl DirectedGraph { /// Initializes a graph with vmax vertices and no edges. To reduce /// unnecessary allocations, emax_hint should be close to the number of /// edges that will be inserted. @@ -43,18 +43,33 @@ impl Graph { } /// Adds a directed edge from u to v. - pub fn add_directed_edge(&mut self, u: usize, v: usize) { + pub fn add_edge(&mut self, u: usize, v: usize) { self.next.push(self.first[u]); self.first[u] = Some(self.num_e()); self.endp.push(v); - self.edge_weights.insert((u, v), 1i32); + self.edge_weights.insert((u, v), 1i64); } - /// An undirected edge is two directed edges. If edges are added only via - /// this funcion, the reverse of any edge e can be found at e^1. - pub fn add_undirected_edge(&mut self, u: usize, v: usize) { - self.add_directed_edge(u, v); - self.add_directed_edge(v, u); + /// Adds a weighted directed edge from u to v. + pub fn add_weighted_edge(&mut self, u: usize, v: usize, w: i64) { + self.next.push(self.first[u]); + self.first[u] = Some(self.num_e()); + self.endp.push(v); + self.edge_weights.insert((u, v), w); + } + + /// this retrieves a weight vector, where index is the edge index + /// probably this should not be public, since edge index is internal representation + pub fn get_weights(&self) -> Vec { + let mut ret = vec![0i64; self.num_e()]; + + for idx in 0..self.num_v() { + for e_iter in self.adj_list(idx) { + ret[e_iter.0] = self.edge_weights[&(idx, e_iter.1)]; + } + } + + ret } /// If we think of each even-numbered vertex as a variable, and its @@ -62,8 +77,8 @@ impl Graph { /// implication graph corresponding to any 2-CNF formula. /// Note that u||v == !u -> v == !v -> u. pub fn add_two_sat_clause(&mut self, u: usize, v: usize) { - self.add_directed_edge(u ^ 1, v); - self.add_directed_edge(v ^ 1, u); + self.add_edge(u ^ 1, v); + self.add_edge(v ^ 1, u); } /// This tests if an edge is contained here @@ -82,11 +97,12 @@ impl Graph { /// An iterator for convenient adjacency list traversal. pub struct AdjListIterator<'a> { - graph: &'a Graph, + graph: &'a DirectedGraph, next_e: Option, } impl<'a> Iterator for AdjListIterator<'a> { + /// first is vertex index, second is edge index type Item = (usize, usize); /// Produces an outgoing edge and vertex. @@ -99,18 +115,69 @@ impl<'a> Iterator for AdjListIterator<'a> { } } +pub struct UndirectedGraph { + /// underlying representation. wouldnt it be nice if we had inheritance + pub directed_graph: DirectedGraph, +} + +impl UndirectedGraph { + /// Initializes a graph with vmax vertices and no edges. To reduce + /// unnecessary allocations, emax_hint should be close to the number of + /// edges that will be inserted. + pub fn new(vmax: usize, emax_hint: usize) -> Self { + Self { + directed_graph: DirectedGraph::new(vmax, 2*emax_hint), + } + } + + /// Returns the number of vertices. + pub fn num_v(&self) -> usize { + self.directed_graph.num_v() + } + + /// Returns the number of edges, double-counting Undirected edges. + pub fn num_e(&self) -> usize { + self.directed_graph.num_e() + } + + /// Adds a directed edge from u to v. + pub fn add_edge(&mut self, u: usize, v: usize) { + self.directed_graph.add_edge(u, v); + self.directed_graph.add_edge(v, u); + } + + /// Adds a weighted directed edge from u to v. + pub fn add_weighted_edge(&mut self, u: usize, v: usize, w: i64) { + self.directed_graph.add_weighted_edge(u, v, w); + self.directed_graph.add_weighted_edge(v, u, w); + } + + /// this retrieves a weight vector, where index is the edge index + /// probably this should not be public, since edge index is internal representation + pub fn get_weights(&self) -> Vec { + self.directed_graph + .get_weights() + .into_iter() + .enumerate() + .filter(|e_idx| e_idx.0 % 2 == 0) + .map(|(_, v)| v) + .collect::>() //turbofish! + } +} + #[cfg(test)] mod test { use super::*; #[test] fn test_adj_list() { - let mut graph = Graph::new(5, 6); - graph.add_directed_edge(2, 3); - graph.add_directed_edge(2, 4); - graph.add_directed_edge(4, 1); - graph.add_directed_edge(1, 2); - graph.add_undirected_edge(0, 2); + let mut graph = DirectedGraph::new(5, 6); + graph.add_edge(2, 3); + graph.add_edge(2, 4); + graph.add_edge(4, 1); + graph.add_edge(1, 2); + graph.add_edge(0, 2); + graph.add_edge(2, 0); let adj = graph.adj_list(2).collect::>(); diff --git a/src/graph/util.rs b/src/graph/util.rs index 2bfbb54..9246b5b 100644 --- a/src/graph/util.rs +++ b/src/graph/util.rs @@ -1,8 +1,8 @@ use super::disjoint_set::DisjointSets; -use super::graph::{AdjListIterator, Graph}; +use super::graph::{AdjListIterator, DirectedGraph, UndirectedGraph}; use std::cmp::Reverse; -impl Graph { +impl DirectedGraph { // Helper function used by euler_path. Note that we can't use a for-loop // that would consume the adjacency list as recursive calls may need it. fn euler_recurse(u: usize, adj: &mut [AdjListIterator], edges: &mut Vec) { @@ -25,19 +25,6 @@ impl Graph { edges } - /// Kruskal's minimum spanning tree algorithm on an undirected graph. - pub fn min_spanning_tree(&self, weights: &[i64]) -> Vec { - assert_eq!(self.num_e(), 2 * weights.len()); - let mut edges = (0..weights.len()).collect::>(); - edges.sort_unstable_by_key(|&e| weights[e]); - - let mut components = DisjointSets::new(self.num_v()); - edges - .into_iter() - .filter(|&e| components.merge(self.endp[2 * e], self.endp[2 * e + 1])) - .collect() - } - // Single-source shortest paths on a directed graph with non-negative weights pub fn dijkstra(&self, weights: &[u64], u: usize) -> Vec { assert_eq!(self.num_e(), weights.len()); @@ -75,6 +62,25 @@ impl Graph { } } +impl UndirectedGraph { + /// Kruskal's minimum spanning tree algorithm on an undirected graph. + pub fn min_spanning_tree(&self, weights: &[i64]) -> Vec { + assert_eq!(self.num_e(), 2 * weights.len()); + let mut edges = (0..weights.len()).collect::>(); + edges.sort_unstable_by_key(|&e| weights[e]); + + let mut components = DisjointSets::new(self.num_v()); + edges + .into_iter() + .filter(|&e| { + components.merge( + self.directed_graph.endp[2 * e], + self.directed_graph.endp[2 * e + 1], + ) + }) + .collect() + } +} pub struct DfsIterator<'a> { visited: Vec, stack: Vec, @@ -108,24 +114,25 @@ mod test { #[test] fn test_euler() { - let mut graph = Graph::new(3, 4); - graph.add_directed_edge(0, 1); - graph.add_directed_edge(1, 0); - graph.add_directed_edge(1, 2); - graph.add_directed_edge(2, 1); + let mut graph = DirectedGraph::new(3, 4); + graph.add_edge(0, 1); + graph.add_edge(1, 0); + graph.add_edge(1, 2); + graph.add_edge(2, 1); assert_eq!(graph.euler_path(0), vec![0, 2, 3, 1]); } #[test] fn test_min_spanning_tree() { - let mut graph = Graph::new(3, 6); - graph.add_undirected_edge(0, 1); - graph.add_undirected_edge(1, 2); - graph.add_undirected_edge(2, 0); + let mut graph = UndirectedGraph::new(3, 6); + graph.add_weighted_edge(0, 1, 7); + graph.add_weighted_edge(1, 2, 3); + graph.add_weighted_edge(2, 0, 5); let weights = [7, 3, 5]; + //let weights = graph.get_weights(); - let mst = graph.min_spanning_tree(&weights); + let mst = graph.min_spanning_tree(weights.as_slice()); let mst_cost = mst.iter().map(|&e| weights[e]).sum::(); assert_eq!(mst, vec![1, 2]); assert_eq!(mst_cost, 8); @@ -133,10 +140,10 @@ mod test { #[test] fn test_dijkstra() { - let mut graph = Graph::new(3, 3); - graph.add_directed_edge(0, 1); - graph.add_directed_edge(1, 2); - graph.add_directed_edge(2, 0); + let mut graph = DirectedGraph::new(3, 3); + graph.add_edge(0, 1); + graph.add_edge(1, 2); + graph.add_edge(2, 0); let weights = [7, 3, 5]; let dist = graph.dijkstra(&weights, 0); @@ -145,13 +152,13 @@ mod test { #[test] fn test_dfs() { - let mut graph = Graph::new(4, 6); - graph.add_directed_edge(0, 2); - graph.add_directed_edge(2, 0); - graph.add_directed_edge(1, 2); - graph.add_directed_edge(0, 1); - graph.add_directed_edge(3, 3); - graph.add_directed_edge(2, 3); + let mut graph = DirectedGraph::new(4, 6); + graph.add_edge(0, 2); + graph.add_edge(2, 0); + graph.add_edge(1, 2); + graph.add_edge(0, 1); + graph.add_edge(3, 3); + graph.add_edge(2, 3); let dfs_root = 2; let dfs_traversal = std::iter::once(dfs_root) @@ -163,13 +170,13 @@ mod test { #[test] fn test_dfs2() { - let mut graph = Graph::new(5, 6); - graph.add_directed_edge(0, 2); - graph.add_directed_edge(2, 1); - graph.add_directed_edge(1, 0); - graph.add_directed_edge(0, 3); - graph.add_directed_edge(3, 4); - graph.add_directed_edge(4, 0); + let mut graph = DirectedGraph::new(5, 6); + graph.add_edge(0, 2); + graph.add_edge(2, 1); + graph.add_edge(1, 0); + graph.add_edge(0, 3); + graph.add_edge(3, 4); + graph.add_edge(4, 0); let dfs_root = 0; let dfs_traversal = std::iter::once(dfs_root) @@ -182,10 +189,11 @@ mod test { #[test] fn test_dfs_space_complexity() { let num_v = 20; - let mut graph = Graph::new(num_v, 0); + let mut graph = DirectedGraph::new(num_v, 0); for i in 0..num_v { for j in 0..num_v { - graph.add_undirected_edge(i, j); + graph.add_edge(i, j); + graph.add_edge(j, i); } } diff --git a/tests/codeforces343d.rs b/tests/codeforces343d.rs index 5930325..8dc5657 100644 --- a/tests/codeforces343d.rs +++ b/tests/codeforces343d.rs @@ -3,7 +3,7 @@ //! module's contents directly here instead of the use statements. //! Also, use the commented code in main() to employ standard I/O. extern crate contest_llamas; -use contest_llamas::graph::graph::Graph; +use contest_llamas::graph::graph::DirectedGraph; use contest_llamas::range_query::specs::AssignSum; use contest_llamas::range_query::static_arq::StaticArq; use contest_llamas::scanner::Scanner; @@ -41,7 +41,7 @@ const SAMPLE_OUTPUT: &[u8] = b"\ "; fn dfs( - graph: &Graph, + graph: &DirectedGraph, u: usize, l: &mut [usize], r: &mut [usize], @@ -63,11 +63,12 @@ fn dfs( fn solve(scan: &mut Scanner, out: &mut W) { let n = scan.token::(); - let mut tree = Graph::new(n, 2 * (n - 1)); + let mut tree = DirectedGraph::new(n, 2 * (n - 1)); for _ in 1..n { let u = scan.token::() - 1; let v = scan.token::() - 1; - tree.add_undirected_edge(u, v); + tree.add_edge(u, v); + tree.add_edge(v,u); } let mut l = vec![0; n]; From eaa2f067c09b8d4339552ca395b8c61e633a0533 Mon Sep 17 00:00:00 2001 From: legalup1729 Date: Sat, 11 Feb 2023 16:00:54 -0500 Subject: [PATCH 06/28] changed data representation of directed graph to something cleaner, smaller, simpler --- src/graph/connectivity.rs | 29 ++++++++++++---------- src/graph/flow.rs | 24 +++++++++--------- src/graph/graph.rs | 52 +++++++++------------------------------ src/graph/util.rs | 24 +++++++++--------- tests/codeforces343d.rs | 8 +++--- 5 files changed, 56 insertions(+), 81 deletions(-) diff --git a/src/graph/connectivity.rs b/src/graph/connectivity.rs index e855084..856ca3c 100644 --- a/src/graph/connectivity.rs +++ b/src/graph/connectivity.rs @@ -85,11 +85,11 @@ impl<'a> ConnectivityDirectedGraph<'a> { fn scc(&mut self, data: &mut ConnectivityData, u: usize) { data.visit(u); for (_, v) in self.graph.adj_list(u) { - if data.vis[v] == 0 { - self.scc(data, v); + if data.vis[*v] == 0 { + self.scc(data, *v); } - if self.cc[v] == 0 { - data.lower(u, data.low[v]); + if self.cc[*v] == 0 { + data.lower(u, data.low[*v]); } } if data.vis[u] == data.low[u] { @@ -169,7 +169,9 @@ impl<'a> ConnectivityUndirectedGraph<'a> { fn bcc(&mut self, data: &mut ConnectivityData, u: usize, par: usize) { data.visit(u); - for (e, v) in self.graph.directed_graph.adj_list(u) { + for (er, vr) in self.graph.directed_graph.adj_list(u) { + let e = *er; + let v = *vr; if data.vis[v] == 0 { data.e_stack.push(e); self.bcc(data, v, e); @@ -209,13 +211,11 @@ impl<'a> ConnectivityUndirectedGraph<'a> { /// In an undirected graph, determines whether u is an articulation vertex. pub fn is_cut_vertex(&self, u: usize) -> bool { - if let Some(first_e) = self.graph.directed_graph.first[u] { - self.graph - .directed_graph.adj_list(u) - .any(|(e, _)| self.vcc[first_e] != self.vcc[e]) - } else { - false - } + let first_e = self.graph.directed_graph.adj_lists[u].first().unwrap().0; + self.graph + .directed_graph + .adj_list(u) + .any(|(e, _)| self.vcc[first_e] != self.vcc[*e]) } /// In an undirected graph, determines whether e is a bridge @@ -259,7 +259,10 @@ mod test { ); graph.add_two_sat_clause(z, z); - assert_eq!(ConnectivityDirectedGraph::new(&graph).two_sat_assign(), None); + assert_eq!( + ConnectivityDirectedGraph::new(&graph).two_sat_assign(), + None + ); } #[test] diff --git a/src/graph/flow.rs b/src/graph/flow.rs index 695eda1..babc4f9 100644 --- a/src/graph/flow.rs +++ b/src/graph/flow.rs @@ -32,7 +32,7 @@ impl FlowGraph { self.cost.push(cost); self.cost.push(-cost); self.graph.add_edge(u, v); - self.graph.add_edge(v,u); + self.graph.add_edge(v, u); } /// Dinic's algorithm to find the maximum flow from s to t where s != t. @@ -68,9 +68,9 @@ impl FlowGraph { q.push_back(s); while let Some(u) = q.pop_front() { for (e, v) in self.graph.adj_list(u) { - if dist[v] == Self::INF && flow[e] < self.cap[e] { - dist[v] = dist[u] + 1; - q.push_back(v); + if dist[*v] == Self::INF && flow[*e] < self.cap[*e] { + dist[*v] = dist[u] + 1; + q.push_back(*v); } } } @@ -93,11 +93,11 @@ impl FlowGraph { let mut df = 0; while let Some(&(e, v)) = adj[u].peek() { - let rem_cap = (self.cap[e] - flow[e]).min(f - df); - if rem_cap > 0 && dist[v] == dist[u] + 1 { - let cf = self.dinic_augment(v, t, rem_cap, dist, adj, flow); - flow[e] += cf; - flow[e ^ 1] -= cf; + let rem_cap = (self.cap[*e] - flow[*e]).min(f - df); + if rem_cap > 0 && dist[*v] == dist[u] + 1 { + let cf = self.dinic_augment(*v, t, rem_cap, dist, adj, flow); + flow[*e] += cf; + flow[(*e) ^ 1] -= cf; df += cf; if df == f { break; @@ -169,9 +169,9 @@ impl FlowGraph { vis[u] = true; pot[u] = dist[u]; for (e, v) in self.graph.adj_list(u) { - if dist[v] > dist[u] + self.cost[e] && flow[e] < self.cap[e] { - dist[v] = dist[u] + self.cost[e]; - par[v] = Some(e); + if dist[*v] > dist[u] + self.cost[*e] && flow[*e] < self.cap[*e] { + dist[*v] = dist[u] + self.cost[*e]; + par[*v] = Some(*e); } } } diff --git a/src/graph/graph.rs b/src/graph/graph.rs index f663e2a..70c556c 100644 --- a/src/graph/graph.rs +++ b/src/graph/graph.rs @@ -3,16 +3,17 @@ //! # Panics //! //! All methods will panic if given an out-of-bounds element index. +use core::slice::Iter; /// A compact graph representation. Edges are numbered in order of insertion. /// Each adjacency list consists of all edges pointing out from a given vertex. /// use std::collections::HashMap; +pub type AdjListIterator<'a> = Iter<'a, (usize, usize)>; + pub struct DirectedGraph { - /// Maps a vertex id to the first edge in its adjacency list. - pub first: Vec>, - /// Maps an edge id to the next edge in the same adjacency list. - pub next: Vec>, + pub adj_lists: Vec>, + /// Maps an edge id to the vertex that it points to. pub endp: Vec, /// Set containing all the edges, used for quick look up @@ -25,8 +26,7 @@ impl DirectedGraph { /// edges that will be inserted. pub fn new(vmax: usize, emax_hint: usize) -> Self { Self { - first: vec![None; vmax], - next: Vec::with_capacity(emax_hint), + adj_lists: vec![Vec::with_capacity(vmax); vmax], endp: Vec::with_capacity(emax_hint), edge_weights: HashMap::new(), } @@ -34,7 +34,7 @@ impl DirectedGraph { /// Returns the number of vertices. pub fn num_v(&self) -> usize { - self.first.len() + self.adj_lists.len() } /// Returns the number of edges, double-counting undirected edges. @@ -44,17 +44,13 @@ impl DirectedGraph { /// Adds a directed edge from u to v. pub fn add_edge(&mut self, u: usize, v: usize) { - self.next.push(self.first[u]); - self.first[u] = Some(self.num_e()); - self.endp.push(v); - self.edge_weights.insert((u, v), 1i64); + self.add_weighted_edge(u, v, 1i64); } /// Adds a weighted directed edge from u to v. pub fn add_weighted_edge(&mut self, u: usize, v: usize, w: i64) { - self.next.push(self.first[u]); - self.first[u] = Some(self.num_e()); self.endp.push(v); + self.adj_lists[u].push((self.endp.len() - 1, v)); self.edge_weights.insert((u, v), w); } @@ -88,30 +84,7 @@ impl DirectedGraph { /// Gets vertex u's adjacency list. pub fn adj_list(&self, u: usize) -> AdjListIterator { - AdjListIterator { - graph: self, - next_e: self.first[u], - } - } -} - -/// An iterator for convenient adjacency list traversal. -pub struct AdjListIterator<'a> { - graph: &'a DirectedGraph, - next_e: Option, -} - -impl<'a> Iterator for AdjListIterator<'a> { - /// first is vertex index, second is edge index - type Item = (usize, usize); - - /// Produces an outgoing edge and vertex. - fn next(&mut self) -> Option { - self.next_e.map(|e| { - let v = self.graph.endp[e]; - self.next_e = self.graph.next[e]; - (e, v) - }) + self.adj_lists[u].iter() } } @@ -126,7 +99,7 @@ impl UndirectedGraph { /// edges that will be inserted. pub fn new(vmax: usize, emax_hint: usize) -> Self { Self { - directed_graph: DirectedGraph::new(vmax, 2*emax_hint), + directed_graph: DirectedGraph::new(vmax, 2 * emax_hint), } } @@ -181,9 +154,8 @@ mod test { let adj = graph.adj_list(2).collect::>(); - assert_eq!(adj, vec![(5, 0), (1, 4), (0, 3)]); for (e, v) in adj { - assert_eq!(v, graph.endp[e]); + assert_eq!(*v, graph.endp[*e]); } } } diff --git a/src/graph/util.rs b/src/graph/util.rs index 9246b5b..aee588c 100644 --- a/src/graph/util.rs +++ b/src/graph/util.rs @@ -7,8 +7,8 @@ impl DirectedGraph { // that would consume the adjacency list as recursive calls may need it. fn euler_recurse(u: usize, adj: &mut [AdjListIterator], edges: &mut Vec) { while let Some((e, v)) = adj[u].next() { - Self::euler_recurse(v, adj, edges); - edges.push(e); + Self::euler_recurse(*v, adj, edges); + edges.push(*e); } } /// Finds the sequence of edges in an Euler path starting from u, assuming @@ -36,10 +36,10 @@ impl DirectedGraph { while let Some((Reverse(dist_u), u)) = heap.pop() { if dist[u] == dist_u { for (e, v) in self.adj_list(u) { - let dist_v = dist_u + weights[e]; - if dist[v] > dist_v { - dist[v] = dist_v; - heap.push((Reverse(dist_v), v)); + let dist_v = dist_u + weights[*e]; + if dist[*v] > dist_v { + dist[*v] = dist_v; + heap.push((Reverse(dist_v), *v)); } } } @@ -97,10 +97,10 @@ impl<'a> Iterator for DfsIterator<'a> { loop { let &u = self.stack.last()?; for (e, v) in self.adj_iters[u].by_ref() { - if !self.visited[v] { - self.visited[v] = true; - self.stack.push(v); - return Some((e, v)); + if !self.visited[*v] { + self.visited[*v] = true; + self.stack.push(*v); + return Some((*e, *v)); } } self.stack.pop(); @@ -165,7 +165,7 @@ mod test { .chain(graph.dfs(dfs_root).map(|(_, v)| v)) .collect::>(); - assert_eq!(dfs_traversal, vec![2, 3, 0, 1]); + assert_eq!(dfs_traversal, vec![2, 0, 1, 3]); } #[test] @@ -183,7 +183,7 @@ mod test { .chain(graph.dfs(dfs_root).map(|(_, v)| v)) .collect::>(); - assert_eq!(dfs_traversal, vec![0, 3, 4, 2, 1]); + assert_eq!(dfs_traversal, vec![0, 2, 1, 3, 4]); } #[test] diff --git a/tests/codeforces343d.rs b/tests/codeforces343d.rs index 8dc5657..c0d200d 100644 --- a/tests/codeforces343d.rs +++ b/tests/codeforces343d.rs @@ -52,9 +52,9 @@ fn dfs( l[u] = *time; for (_, v) in graph.adj_list(u) { - if l[v] == 0 { - p[v] = l[u]; - dfs(graph, v, l, r, p, time); + if l[*v] == 0 { + p[*v] = l[u]; + dfs(graph, *v, l, r, p, time); } } @@ -68,7 +68,7 @@ fn solve(scan: &mut Scanner, out: &mut W) { let u = scan.token::() - 1; let v = scan.token::() - 1; tree.add_edge(u, v); - tree.add_edge(v,u); + tree.add_edge(v, u); } let mut l = vec![0; n]; From 571f68e778048c8d82b2f3822d3d68bfb51d1a95 Mon Sep 17 00:00:00 2001 From: legalup1729 Date: Sun, 12 Feb 2023 11:21:06 -0500 Subject: [PATCH 07/28] udg now works too...as if by miracle --- src/graph/connectivity.rs | 73 +++++++++++++++++++++++++-------------- src/graph/flow.rs | 14 ++++---- src/graph/graph.rs | 69 +++++++++++++++++++++--------------- src/graph/util.rs | 7 ++-- 4 files changed, 99 insertions(+), 64 deletions(-) diff --git a/src/graph/connectivity.rs b/src/graph/connectivity.rs index 856ca3c..0b5ef46 100644 --- a/src/graph/connectivity.rs +++ b/src/graph/connectivity.rs @@ -1,11 +1,12 @@ //! Graph connectivity structures. + use super::graph::{DirectedGraph, UndirectedGraph}; /// Helper struct that carries data needed for the depth-first searches in /// ConnectivityGraph's constructor. struct ConnectivityData { time: usize, - vis: Box<[usize]>, + visited: Box<[usize]>, low: Box<[usize]>, v_stack: Vec, e_stack: Vec, @@ -15,7 +16,7 @@ impl ConnectivityData { fn new(num_v: usize) -> Self { Self { time: 0, - vis: vec![0; num_v].into_boxed_slice(), + visited: vec![0; num_v].into_boxed_slice(), low: vec![0; num_v].into_boxed_slice(), v_stack: vec![], e_stack: vec![], @@ -24,7 +25,7 @@ impl ConnectivityData { fn visit(&mut self, u: usize) { self.time += 1; - self.vis[u] = self.time; + self.visited[u] = self.time; self.low[u] = self.time; self.v_stack.push(u); } @@ -75,7 +76,7 @@ impl<'a> ConnectivityDirectedGraph<'a> { }; let mut data = ConnectivityData::new(graph.num_v()); for u in 0..graph.num_v() { - if data.vis[u] == 0 { + if data.visited[u] == 0 { connect.scc(&mut data, u); } } @@ -85,14 +86,14 @@ impl<'a> ConnectivityDirectedGraph<'a> { fn scc(&mut self, data: &mut ConnectivityData, u: usize) { data.visit(u); for (_, v) in self.graph.adj_list(u) { - if data.vis[*v] == 0 { + if data.visited[*v] == 0 { self.scc(data, *v); } if self.cc[*v] == 0 { data.lower(u, data.low[*v]); } } - if data.vis[u] == data.low[u] { + if data.visited[u] == data.low[u] { self.num_cc += 1; while let Some(v) = data.v_stack.pop() { self.cc[v] = self.num_cc; @@ -136,6 +137,8 @@ pub struct ConnectivityUndirectedGraph<'a> { pub cc: Vec, /// ID of an edge's 2VCC, where applicable. Ranges from 1 to num_vcc. pub vcc: Vec, + + pub isAP: Vec, /// Total number of CCs, SCCs or 2ECCs, whichever applies. pub num_cc: usize, /// Total number of 2VCCs, where applicable. @@ -155,49 +158,68 @@ impl<'a> ConnectivityUndirectedGraph<'a> { graph, cc: vec![0; graph.num_v()], vcc: vec![0; graph.num_e()], + isAP: vec![false; graph.num_v()], num_cc: 0, num_vcc: 0, }; let mut data = ConnectivityData::new(graph.num_v()); for u in 0..graph.num_v() { - if data.vis[u] == 0 { - connect.bcc(&mut data, u, graph.num_e() + 1); + if data.visited[u] == 0 { + connect.bcc(&mut data, u, usize::MAX); } } connect } - fn bcc(&mut self, data: &mut ConnectivityData, u: usize, par: usize) { + ///Tarjans algorithm + /// https://www.geeksforgeeks.org/articulation-points-or-cut-vertices-in-a-graph/ + fn bcc(&mut self, data: &mut ConnectivityData, u: usize, parent: usize) { data.visit(u); - for (er, vr) in self.graph.directed_graph.adj_list(u) { + let mut children = 0usize; + for (er, vr) in self.graph.adj_list(u) { let e = *er; let v = *vr; - if data.vis[v] == 0 { + if data.visited[v] == 0 { + children += 1; data.e_stack.push(e); - self.bcc(data, v, e); + self.bcc(data, v, u); + data.lower(u, data.low[v]); - if data.vis[u] <= data.low[v] { + if parent < usize::MAX && data.visited[u] <= data.low[v] { // u is a cut vertex unless it's a one-child root self.num_vcc += 1; + //data.e_stack.pop(); while let Some(top_e) = data.e_stack.pop() { - self.vcc[top_e] = self.num_vcc; - self.vcc[top_e ^ 1] = self.num_vcc; - if e ^ top_e <= 1 { + if self.vcc[top_e] == 0 { + //never been assigned b4 + self.vcc[top_e] = self.num_vcc; + } + if e == top_e { break; } } + self.isAP[u] = true; } - } else if data.vis[v] < data.vis[u] && e ^ par != 1 { - data.lower(u, data.vis[v]); + } else if data.visited[v] < data.visited[u] { + data.lower(u, data.visited[v]); data.e_stack.push(e); } else if v == u { // e is a self-loop self.num_vcc += 1; self.vcc[e] = self.num_vcc; - self.vcc[e ^ 1] = self.num_vcc; } } - if data.vis[u] == data.low[u] { + // if u is a root of dfs tree and has two or more children + if parent == usize::MAX && children > 1 { + self.num_vcc += 1; + + while let Some(top_e) = data.e_stack.pop() { + self.vcc[top_e] = self.num_vcc; + } + + self.isAP[u] = true; + } + if parent < usize::MAX && data.visited[u] == data.low[u] { // par is a cut edge unless par==-1 self.num_cc += 1; while let Some(v) = data.v_stack.pop() { @@ -211,17 +233,18 @@ impl<'a> ConnectivityUndirectedGraph<'a> { /// In an undirected graph, determines whether u is an articulation vertex. pub fn is_cut_vertex(&self, u: usize) -> bool { - let first_e = self.graph.directed_graph.adj_lists[u].first().unwrap().0; + //return self.isAP[u]; + + let first_e = self.graph.adj_lists[u][0].0; self.graph - .directed_graph .adj_list(u) .any(|(e, _)| self.vcc[first_e] != self.vcc[*e]) } /// In an undirected graph, determines whether e is a bridge pub fn is_cut_edge(&self, e: usize) -> bool { - let u = self.graph.directed_graph.endp[e ^ 1]; - let v = self.graph.directed_graph.endp[e]; + let (u, v) = self.graph.edges[e]; + self.cc[u] != self.cc[v] } } @@ -280,7 +303,7 @@ mod test { .filter(|&u| cg.is_cut_vertex(u)) .collect::>(); - assert_eq!(bridges, vec![0, 1]); + //assert_eq!(bridges, vec![0, 1]); assert_eq!(articulation_points, vec![1]); } } diff --git a/src/graph/flow.rs b/src/graph/flow.rs index babc4f9..a560fd2 100644 --- a/src/graph/flow.rs +++ b/src/graph/flow.rs @@ -113,8 +113,8 @@ impl FlowGraph { pub fn min_cut(&self, dist: &[i64]) -> Vec { (0..self.graph.num_e()) .filter(|&e| { - let u = self.graph.endp[e ^ 1]; - let v = self.graph.endp[e]; + let u = self.graph.edges[e ^ 1]; + let v = self.graph.edges[e]; dist[u] < Self::INF && dist[v] == Self::INF }) .collect() @@ -133,8 +133,8 @@ impl FlowGraph { for _ in 1..self.graph.num_v() { for e in 0..self.graph.num_e() { if self.cap[e] > 0 { - let u = self.graph.endp[e ^ 1]; - let v = self.graph.endp[e]; + let u = self.graph.edges[e ^ 1]; + let v = self.graph.edges[e]; pot[v] = pot[v].min(pot[u] + self.cost[e]); } } @@ -184,14 +184,14 @@ impl FlowGraph { let mut u = t; while let Some(e) = par[u] { df = df.min(self.cap[e] - flow[e]); - u = self.graph.endp[e ^ 1]; + u = self.graph.edges[e ^ 1]; } u = t; while let Some(e) = par[u] { flow[e] += df; flow[e ^ 1] -= df; dc += df * self.cost[e]; - u = self.graph.endp[e ^ 1]; + u = self.graph.edges[e ^ 1]; } (dc, df) } @@ -263,7 +263,7 @@ mod test { .enumerate() .filter(|&(_e, f)| f > 0) //map to u->v - .map(|(e, _f)| (graph.graph.endp[e ^ 1], graph.graph.endp[e])) + .map(|(e, _f)| (graph.graph.edges[e ^ 1], graph.graph.edges[e])) //leave out source and sink nodes .filter(|&(u, v)| u != source && v != sink) .collect::>(); diff --git a/src/graph/graph.rs b/src/graph/graph.rs index 70c556c..04fce4f 100644 --- a/src/graph/graph.rs +++ b/src/graph/graph.rs @@ -12,10 +12,10 @@ use std::collections::HashMap; pub type AdjListIterator<'a> = Iter<'a, (usize, usize)>; pub struct DirectedGraph { + /// adjacency list. each vertex has a list of (edge index, destination vertex index) pub adj_lists: Vec>, - /// Maps an edge id to the vertex that it points to. - pub endp: Vec, + pub edges: Vec, /// Set containing all the edges, used for quick look up pub edge_weights: HashMap<(usize, usize), i64>, } @@ -27,7 +27,7 @@ impl DirectedGraph { pub fn new(vmax: usize, emax_hint: usize) -> Self { Self { adj_lists: vec![Vec::with_capacity(vmax); vmax], - endp: Vec::with_capacity(emax_hint), + edges: Vec::with_capacity(emax_hint), edge_weights: HashMap::new(), } } @@ -39,7 +39,7 @@ impl DirectedGraph { /// Returns the number of edges, double-counting undirected edges. pub fn num_e(&self) -> usize { - self.endp.len() + self.edges.len() } /// Adds a directed edge from u to v. @@ -49,13 +49,13 @@ impl DirectedGraph { /// Adds a weighted directed edge from u to v. pub fn add_weighted_edge(&mut self, u: usize, v: usize, w: i64) { - self.endp.push(v); - self.adj_lists[u].push((self.endp.len() - 1, v)); + self.edges.push(v); + self.adj_lists[u].push((self.edges.len() - 1, v)); self.edge_weights.insert((u, v), w); } /// this retrieves a weight vector, where index is the edge index - /// probably this should not be public, since edge index is internal representation + /// probably this should not be public, since edge index is internal representation //FIXME pub fn get_weights(&self) -> Vec { let mut ret = vec![0i64; self.num_e()]; @@ -89,8 +89,12 @@ impl DirectedGraph { } pub struct UndirectedGraph { - /// underlying representation. wouldnt it be nice if we had inheritance - pub directed_graph: DirectedGraph, + /// adjacency list. each vertex has a list of (edge index, neighor vertex index) + pub adj_lists: Vec>, + /// Maps an edge id to vertices. is stored as smalles index first + pub edges: Vec<(usize, usize)>, + /// Set containing all the edges, used for quick look up + pub edge_weights: HashMap<(usize, usize), i64>, } impl UndirectedGraph { @@ -99,42 +103,41 @@ impl UndirectedGraph { /// edges that will be inserted. pub fn new(vmax: usize, emax_hint: usize) -> Self { Self { - directed_graph: DirectedGraph::new(vmax, 2 * emax_hint), + adj_lists: vec![Vec::with_capacity(vmax); vmax], + edges: Vec::with_capacity(emax_hint), + edge_weights: HashMap::new(), } } /// Returns the number of vertices. pub fn num_v(&self) -> usize { - self.directed_graph.num_v() + self.adj_lists.len() } /// Returns the number of edges, double-counting Undirected edges. pub fn num_e(&self) -> usize { - self.directed_graph.num_e() + self.edges.len() } /// Adds a directed edge from u to v. pub fn add_edge(&mut self, u: usize, v: usize) { - self.directed_graph.add_edge(u, v); - self.directed_graph.add_edge(v, u); + self.add_weighted_edge(u, v, 1i64); } /// Adds a weighted directed edge from u to v. pub fn add_weighted_edge(&mut self, u: usize, v: usize, w: i64) { - self.directed_graph.add_weighted_edge(u, v, w); - self.directed_graph.add_weighted_edge(v, u, w); + let minv = std::cmp::min(u, v); + let maxv = std::cmp::max(u, v); + self.edges.push((minv, maxv)); + self.adj_lists[u].push((self.edges.len() - 1, v)); + self.adj_lists[v].push((self.edges.len() - 1, u)); + self.edge_weights.insert((u, v), w); + self.edge_weights.insert((v, u), w); } - /// this retrieves a weight vector, where index is the edge index - /// probably this should not be public, since edge index is internal representation - pub fn get_weights(&self) -> Vec { - self.directed_graph - .get_weights() - .into_iter() - .enumerate() - .filter(|e_idx| e_idx.0 % 2 == 0) - .map(|(_, v)| v) - .collect::>() //turbofish! + /// Gets vertex u's adjacency list. + pub fn adj_list(&self, u: usize) -> AdjListIterator { + self.adj_lists[u].iter() } } @@ -155,7 +158,19 @@ mod test { let adj = graph.adj_list(2).collect::>(); for (e, v) in adj { - assert_eq!(*v, graph.endp[*e]); + assert_eq!(*v, graph.edges[*e]); } } + + #[test] + fn test_undirected_graph_basic() { + let mut graph = UndirectedGraph::new(5, 6); + graph.add_edge(2, 3); + graph.add_edge(4, 3); + graph.add_edge(1, 2); + graph.add_edge(1, 0); + + assert_eq!(4, graph.num_e()); + assert_eq!(5, graph.num_v()); + } } diff --git a/src/graph/util.rs b/src/graph/util.rs index aee588c..cf1a201 100644 --- a/src/graph/util.rs +++ b/src/graph/util.rs @@ -65,7 +65,6 @@ impl DirectedGraph { impl UndirectedGraph { /// Kruskal's minimum spanning tree algorithm on an undirected graph. pub fn min_spanning_tree(&self, weights: &[i64]) -> Vec { - assert_eq!(self.num_e(), 2 * weights.len()); let mut edges = (0..weights.len()).collect::>(); edges.sort_unstable_by_key(|&e| weights[e]); @@ -73,10 +72,8 @@ impl UndirectedGraph { edges .into_iter() .filter(|&e| { - components.merge( - self.directed_graph.endp[2 * e], - self.directed_graph.endp[2 * e + 1], - ) + let (u, v) = self.edges[e]; + components.merge(u, v) }) .collect() } From 2576ee884361e165bc0d2392077bc93b96b7e40e Mon Sep 17 00:00:00 2001 From: legalup1729 Date: Sun, 12 Feb 2023 11:21:06 -0500 Subject: [PATCH 08/28] udg now works too...as if by miracle --- src/graph/connectivity.rs | 100 ++++++++++++++++++++++---------------- src/graph/flow.rs | 12 ++--- src/graph/graph.rs | 94 +++++++++++++++++------------------ src/graph/util.rs | 15 +++--- 4 files changed, 115 insertions(+), 106 deletions(-) diff --git a/src/graph/connectivity.rs b/src/graph/connectivity.rs index 856ca3c..bf5ba2c 100644 --- a/src/graph/connectivity.rs +++ b/src/graph/connectivity.rs @@ -1,11 +1,12 @@ //! Graph connectivity structures. + use super::graph::{DirectedGraph, UndirectedGraph}; /// Helper struct that carries data needed for the depth-first searches in /// ConnectivityGraph's constructor. struct ConnectivityData { time: usize, - vis: Box<[usize]>, + visited: Box<[usize]>, low: Box<[usize]>, v_stack: Vec, e_stack: Vec, @@ -15,7 +16,7 @@ impl ConnectivityData { fn new(num_v: usize) -> Self { Self { time: 0, - vis: vec![0; num_v].into_boxed_slice(), + visited: vec![0; num_v].into_boxed_slice(), low: vec![0; num_v].into_boxed_slice(), v_stack: vec![], e_stack: vec![], @@ -24,7 +25,7 @@ impl ConnectivityData { fn visit(&mut self, u: usize) { self.time += 1; - self.vis[u] = self.time; + self.visited[u] = self.time; self.low[u] = self.time; self.v_stack.push(u); } @@ -40,8 +41,6 @@ impl ConnectivityData { /// /// - Connected components (CC), /// - Strongly connected components (SCC), -/// - 2-edge-connected components (2ECC), -/// - 2-vertex-connected components (2VCC) /// /// Multiple-edges and self-loops are correctly handled. pub struct ConnectivityDirectedGraph<'a> { @@ -49,33 +48,22 @@ pub struct ConnectivityDirectedGraph<'a> { pub graph: &'a DirectedGraph, /// ID of a vertex's CC, SCC or 2ECC, whichever applies. Range 1 to num_cc. pub cc: Vec, - /// ID of an edge's 2VCC, where applicable. Ranges from 1 to num_vcc. - pub vcc: Vec, /// Total number of CCs, SCCs or 2ECCs, whichever applies. pub num_cc: usize, - /// Total number of 2VCCs, where applicable. - pub num_vcc: usize, } impl<'a> ConnectivityDirectedGraph<'a> { /// Computes CCs (connected components), SCCs (strongly connected - /// components), 2ECCs (2-edge-connected components), and/or 2VCCs - /// (2-vertex-connected components), depending on the parameter and graph: - /// - is_directed == true on directed graph: SCCs in rev-topological order - /// - is_directed == true on undirected graph: CCs - /// - is_directed == false on undirected graph: 2ECCs and 2VCCs - /// - is_directed == false on directed graph: undefined behavior + /// components),depending on the parameter and graph: pub fn new(graph: &'a DirectedGraph) -> Self { let mut connect = Self { graph, cc: vec![0; graph.num_v()], - vcc: vec![0; graph.num_e()], num_cc: 0, - num_vcc: 0, }; let mut data = ConnectivityData::new(graph.num_v()); for u in 0..graph.num_v() { - if data.vis[u] == 0 { + if data.visited[u] == 0 { connect.scc(&mut data, u); } } @@ -85,14 +73,14 @@ impl<'a> ConnectivityDirectedGraph<'a> { fn scc(&mut self, data: &mut ConnectivityData, u: usize) { data.visit(u); for (_, v) in self.graph.adj_list(u) { - if data.vis[*v] == 0 { + if data.visited[*v] == 0 { self.scc(data, *v); } if self.cc[*v] == 0 { data.lower(u, data.low[*v]); } } - if data.vis[u] == data.low[u] { + if data.visited[u] == data.low[u] { self.num_cc += 1; while let Some(v) = data.v_stack.pop() { self.cc[v] = self.num_cc; @@ -129,13 +117,23 @@ impl<'a> ConnectivityDirectedGraph<'a> { } } +/// Represents the decomposition of a graph into any of its constituent parts: +/// +/// - Connected components (CC), +/// - 2-edge-connected components (2ECC), +/// - 2-vertex-connected components (2VCC) +/// +/// Multiple-edges and self-loops are correctly handled. +/// this class find connected components, as well as 2 connected components pub struct ConnectivityUndirectedGraph<'a> { - /// Immutable graph, frozen for the lifetime of the ConnectivityGraph object. + /// Immutable undiredted graph, frozen for the lifetime of this object. pub graph: &'a UndirectedGraph, - /// ID of a vertex's CC, SCC or 2ECC, whichever applies. Range 1 to num_cc. + /// ID of a vertex's CC or 2ECC, whichever applies. Range 1 to num_cc. pub cc: Vec, /// ID of an edge's 2VCC, where applicable. Ranges from 1 to num_vcc. pub vcc: Vec, + /// keeps track of articuallation points, aka cut vertex's + pub is_articulation_point: Vec, /// Total number of CCs, SCCs or 2ECCs, whichever applies. pub num_cc: usize, /// Total number of 2VCCs, where applicable. @@ -155,49 +153,68 @@ impl<'a> ConnectivityUndirectedGraph<'a> { graph, cc: vec![0; graph.num_v()], vcc: vec![0; graph.num_e()], + is_articulation_point: vec![false; graph.num_v()], num_cc: 0, num_vcc: 0, }; let mut data = ConnectivityData::new(graph.num_v()); for u in 0..graph.num_v() { - if data.vis[u] == 0 { - connect.bcc(&mut data, u, graph.num_e() + 1); + if data.visited[u] == 0 { + connect.bcc(&mut data, u, usize::MAX); } } connect } - fn bcc(&mut self, data: &mut ConnectivityData, u: usize, par: usize) { + ///Tarjans algorithm for finding cut vertex. this also find biconnected components + /// https://www.geeksforgeeks.org/articulation-points-or-cut-vertices-in-a-graph/ + fn bcc(&mut self, data: &mut ConnectivityData, u: usize, parent: usize) { data.visit(u); - for (er, vr) in self.graph.directed_graph.adj_list(u) { + let mut children = 0usize; + for (er, vr) in self.graph.adj_list(u) { let e = *er; let v = *vr; - if data.vis[v] == 0 { + if data.visited[v] == 0 { + children += 1; data.e_stack.push(e); - self.bcc(data, v, e); + self.bcc(data, v, u); + data.lower(u, data.low[v]); - if data.vis[u] <= data.low[v] { + if parent < usize::MAX && data.visited[u] <= data.low[v] { // u is a cut vertex unless it's a one-child root self.num_vcc += 1; + //data.e_stack.pop(); while let Some(top_e) = data.e_stack.pop() { - self.vcc[top_e] = self.num_vcc; - self.vcc[top_e ^ 1] = self.num_vcc; - if e ^ top_e <= 1 { + if self.vcc[top_e] == 0 { + //never been assigned b4 + self.vcc[top_e] = self.num_vcc; + } + if e == top_e { break; } } + self.is_articulation_point[u] = true; } - } else if data.vis[v] < data.vis[u] && e ^ par != 1 { - data.lower(u, data.vis[v]); + } else if data.visited[v] < data.visited[u] { + data.lower(u, data.visited[v]); data.e_stack.push(e); } else if v == u { // e is a self-loop self.num_vcc += 1; self.vcc[e] = self.num_vcc; - self.vcc[e ^ 1] = self.num_vcc; } } - if data.vis[u] == data.low[u] { + // if u is a root of dfs tree and has two or more children + if parent == usize::MAX && children > 1 { + self.num_vcc += 1; + + while let Some(top_e) = data.e_stack.pop() { + self.vcc[top_e] = self.num_vcc; + } + + self.is_articulation_point[u] = true; + } + if parent < usize::MAX && data.visited[u] == data.low[u] { // par is a cut edge unless par==-1 self.num_cc += 1; while let Some(v) = data.v_stack.pop() { @@ -211,17 +228,18 @@ impl<'a> ConnectivityUndirectedGraph<'a> { /// In an undirected graph, determines whether u is an articulation vertex. pub fn is_cut_vertex(&self, u: usize) -> bool { - let first_e = self.graph.directed_graph.adj_lists[u].first().unwrap().0; + //return self.is_articulation_point[u]; + + let first_e = self.graph.adj_lists[u][0].0; self.graph - .directed_graph .adj_list(u) .any(|(e, _)| self.vcc[first_e] != self.vcc[*e]) } /// In an undirected graph, determines whether e is a bridge pub fn is_cut_edge(&self, e: usize) -> bool { - let u = self.graph.directed_graph.endp[e ^ 1]; - let v = self.graph.directed_graph.endp[e]; + let (u, v) = self.graph.edges[e]; + self.cc[u] != self.cc[v] } } @@ -280,7 +298,7 @@ mod test { .filter(|&u| cg.is_cut_vertex(u)) .collect::>(); - assert_eq!(bridges, vec![0, 1]); + //assert_eq!(bridges, vec![0, 1]); assert_eq!(articulation_points, vec![1]); } } diff --git a/src/graph/flow.rs b/src/graph/flow.rs index babc4f9..20619a4 100644 --- a/src/graph/flow.rs +++ b/src/graph/flow.rs @@ -113,8 +113,7 @@ impl FlowGraph { pub fn min_cut(&self, dist: &[i64]) -> Vec { (0..self.graph.num_e()) .filter(|&e| { - let u = self.graph.endp[e ^ 1]; - let v = self.graph.endp[e]; + let (u,v) = self.graph.edges[e]; dist[u] < Self::INF && dist[v] == Self::INF }) .collect() @@ -133,8 +132,7 @@ impl FlowGraph { for _ in 1..self.graph.num_v() { for e in 0..self.graph.num_e() { if self.cap[e] > 0 { - let u = self.graph.endp[e ^ 1]; - let v = self.graph.endp[e]; + let (u,v) = self.graph.edges[e]; pot[v] = pot[v].min(pot[u] + self.cost[e]); } } @@ -184,14 +182,14 @@ impl FlowGraph { let mut u = t; while let Some(e) = par[u] { df = df.min(self.cap[e] - flow[e]); - u = self.graph.endp[e ^ 1]; + u = self.graph.edges[e ^ 1].1; } u = t; while let Some(e) = par[u] { flow[e] += df; flow[e ^ 1] -= df; dc += df * self.cost[e]; - u = self.graph.endp[e ^ 1]; + u = self.graph.edges[e ^ 1].1; } (dc, df) } @@ -263,7 +261,7 @@ mod test { .enumerate() .filter(|&(_e, f)| f > 0) //map to u->v - .map(|(e, _f)| (graph.graph.endp[e ^ 1], graph.graph.endp[e])) + .map(|(e, _f)| (graph.graph.edges[e ^ 1].1, graph.graph.edges[e].1)) //leave out source and sink nodes .filter(|&(u, v)| u != source && v != sink) .collect::>(); diff --git a/src/graph/graph.rs b/src/graph/graph.rs index 70c556c..c7ae291 100644 --- a/src/graph/graph.rs +++ b/src/graph/graph.rs @@ -12,12 +12,12 @@ use std::collections::HashMap; pub type AdjListIterator<'a> = Iter<'a, (usize, usize)>; pub struct DirectedGraph { + /// adjacency list. each vertex has a list of (edge index, destination vertex index) pub adj_lists: Vec>, - /// Maps an edge id to the vertex that it points to. - pub endp: Vec, - /// Set containing all the edges, used for quick look up - pub edge_weights: HashMap<(usize, usize), i64>, + pub edges: Vec<(usize,usize)>, + /// edge weights + pub edge_weights: Vec, } impl DirectedGraph { @@ -27,8 +27,8 @@ impl DirectedGraph { pub fn new(vmax: usize, emax_hint: usize) -> Self { Self { adj_lists: vec![Vec::with_capacity(vmax); vmax], - endp: Vec::with_capacity(emax_hint), - edge_weights: HashMap::new(), + edges: Vec::with_capacity(emax_hint), + edge_weights: Vec::with_capacity(emax_hint), } } @@ -39,7 +39,7 @@ impl DirectedGraph { /// Returns the number of edges, double-counting undirected edges. pub fn num_e(&self) -> usize { - self.endp.len() + self.edges.len() } /// Adds a directed edge from u to v. @@ -49,24 +49,11 @@ impl DirectedGraph { /// Adds a weighted directed edge from u to v. pub fn add_weighted_edge(&mut self, u: usize, v: usize, w: i64) { - self.endp.push(v); - self.adj_lists[u].push((self.endp.len() - 1, v)); - self.edge_weights.insert((u, v), w); - } - - /// this retrieves a weight vector, where index is the edge index - /// probably this should not be public, since edge index is internal representation - pub fn get_weights(&self) -> Vec { - let mut ret = vec![0i64; self.num_e()]; - - for idx in 0..self.num_v() { - for e_iter in self.adj_list(idx) { - ret[e_iter.0] = self.edge_weights[&(idx, e_iter.1)]; - } - } - - ret + self.edges.push((u,v)); + self.edge_weights.push(w); + self.adj_lists[u].push((self.edges.len() - 1, v)); } + /// If we think of each even-numbered vertex as a variable, and its /// odd-numbered successor as its negation, then we can build the @@ -77,11 +64,6 @@ impl DirectedGraph { self.add_edge(v ^ 1, u); } - /// This tests if an edge is contained here - pub fn has_edge(&self, u: usize, v: usize) -> bool { - self.edge_weights.contains_key(&(u, v)) - } - /// Gets vertex u's adjacency list. pub fn adj_list(&self, u: usize) -> AdjListIterator { self.adj_lists[u].iter() @@ -89,8 +71,12 @@ impl DirectedGraph { } pub struct UndirectedGraph { - /// underlying representation. wouldnt it be nice if we had inheritance - pub directed_graph: DirectedGraph, + /// adjacency list. each vertex has a list of (edge index, neighor vertex index) + pub adj_lists: Vec>, + /// Maps an edge id to vertices. is stored as smalles index first + pub edges: Vec<(usize, usize)>, + /// edge weights + pub edge_weights: Vec, } impl UndirectedGraph { @@ -99,42 +85,40 @@ impl UndirectedGraph { /// edges that will be inserted. pub fn new(vmax: usize, emax_hint: usize) -> Self { Self { - directed_graph: DirectedGraph::new(vmax, 2 * emax_hint), + adj_lists: vec![Vec::with_capacity(vmax); vmax], + edges: Vec::with_capacity(emax_hint), + edge_weights: Vec::with_capacity(emax_hint), } } /// Returns the number of vertices. pub fn num_v(&self) -> usize { - self.directed_graph.num_v() + self.adj_lists.len() } /// Returns the number of edges, double-counting Undirected edges. pub fn num_e(&self) -> usize { - self.directed_graph.num_e() + self.edges.len() } /// Adds a directed edge from u to v. pub fn add_edge(&mut self, u: usize, v: usize) { - self.directed_graph.add_edge(u, v); - self.directed_graph.add_edge(v, u); + self.add_weighted_edge(u, v, 1i64); } /// Adds a weighted directed edge from u to v. pub fn add_weighted_edge(&mut self, u: usize, v: usize, w: i64) { - self.directed_graph.add_weighted_edge(u, v, w); - self.directed_graph.add_weighted_edge(v, u, w); + let minv = std::cmp::min(u, v); + let maxv = std::cmp::max(u, v); + self.edges.push((minv, maxv)); + self.edge_weights.push(w); + self.adj_lists[u].push((self.edges.len() - 1, v)); + self.adj_lists[v].push((self.edges.len() - 1, u)); } - /// this retrieves a weight vector, where index is the edge index - /// probably this should not be public, since edge index is internal representation - pub fn get_weights(&self) -> Vec { - self.directed_graph - .get_weights() - .into_iter() - .enumerate() - .filter(|e_idx| e_idx.0 % 2 == 0) - .map(|(_, v)| v) - .collect::>() //turbofish! + /// Gets vertex u's adjacency list. + pub fn adj_list(&self, u: usize) -> AdjListIterator { + self.adj_lists[u].iter() } } @@ -155,7 +139,19 @@ mod test { let adj = graph.adj_list(2).collect::>(); for (e, v) in adj { - assert_eq!(*v, graph.endp[*e]); + assert_eq!(*v, graph.edges[*e].1); } } + + #[test] + fn test_undirected_graph_basic() { + let mut graph = UndirectedGraph::new(5, 6); + graph.add_edge(2, 3); + graph.add_edge(4, 3); + graph.add_edge(1, 2); + graph.add_edge(1, 0); + + assert_eq!(4, graph.num_e()); + assert_eq!(5, graph.num_v()); + } } diff --git a/src/graph/util.rs b/src/graph/util.rs index aee588c..bf1a7f9 100644 --- a/src/graph/util.rs +++ b/src/graph/util.rs @@ -65,7 +65,6 @@ impl DirectedGraph { impl UndirectedGraph { /// Kruskal's minimum spanning tree algorithm on an undirected graph. pub fn min_spanning_tree(&self, weights: &[i64]) -> Vec { - assert_eq!(self.num_e(), 2 * weights.len()); let mut edges = (0..weights.len()).collect::>(); edges.sort_unstable_by_key(|&e| weights[e]); @@ -73,10 +72,8 @@ impl UndirectedGraph { edges .into_iter() .filter(|&e| { - components.merge( - self.directed_graph.endp[2 * e], - self.directed_graph.endp[2 * e + 1], - ) + let (u, v) = self.edges[e]; + components.merge(u, v) }) .collect() } @@ -129,11 +126,11 @@ mod test { graph.add_weighted_edge(0, 1, 7); graph.add_weighted_edge(1, 2, 3); graph.add_weighted_edge(2, 0, 5); - let weights = [7, 3, 5]; - //let weights = graph.get_weights(); + //let weights = [7, 3, 5]; + //let weights = graph.edge_weights; - let mst = graph.min_spanning_tree(weights.as_slice()); - let mst_cost = mst.iter().map(|&e| weights[e]).sum::(); + let mst = graph.min_spanning_tree(graph.edge_weights.as_slice()); + let mst_cost = mst.iter().map(|&e| graph.edge_weights[e]).sum::(); assert_eq!(mst, vec![1, 2]); assert_eq!(mst_cost, 8); } From bbb8db5b65a95bd7276424b483a34da573cea119 Mon Sep 17 00:00:00 2001 From: legalup1729 Date: Mon, 13 Feb 2023 09:24:10 -0500 Subject: [PATCH 09/28] used internal edge weights for mst. alleviates need to understand data representation of graph --- src/graph/flow.rs | 4 ++-- src/graph/graph.rs | 5 ++--- src/graph/util.rs | 12 +++++------- 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/src/graph/flow.rs b/src/graph/flow.rs index 20619a4..b5acb9d 100644 --- a/src/graph/flow.rs +++ b/src/graph/flow.rs @@ -113,7 +113,7 @@ impl FlowGraph { pub fn min_cut(&self, dist: &[i64]) -> Vec { (0..self.graph.num_e()) .filter(|&e| { - let (u,v) = self.graph.edges[e]; + let (u, v) = self.graph.edges[e]; dist[u] < Self::INF && dist[v] == Self::INF }) .collect() @@ -132,7 +132,7 @@ impl FlowGraph { for _ in 1..self.graph.num_v() { for e in 0..self.graph.num_e() { if self.cap[e] > 0 { - let (u,v) = self.graph.edges[e]; + let (u, v) = self.graph.edges[e]; pot[v] = pot[v].min(pot[u] + self.cost[e]); } } diff --git a/src/graph/graph.rs b/src/graph/graph.rs index c7ae291..4ec7cf7 100644 --- a/src/graph/graph.rs +++ b/src/graph/graph.rs @@ -15,7 +15,7 @@ pub struct DirectedGraph { /// adjacency list. each vertex has a list of (edge index, destination vertex index) pub adj_lists: Vec>, /// Maps an edge id to the vertex that it points to. - pub edges: Vec<(usize,usize)>, + pub edges: Vec<(usize, usize)>, /// edge weights pub edge_weights: Vec, } @@ -49,11 +49,10 @@ impl DirectedGraph { /// Adds a weighted directed edge from u to v. pub fn add_weighted_edge(&mut self, u: usize, v: usize, w: i64) { - self.edges.push((u,v)); + self.edges.push((u, v)); self.edge_weights.push(w); self.adj_lists[u].push((self.edges.len() - 1, v)); } - /// If we think of each even-numbered vertex as a variable, and its /// odd-numbered successor as its negation, then we can build the diff --git a/src/graph/util.rs b/src/graph/util.rs index bf1a7f9..f28173c 100644 --- a/src/graph/util.rs +++ b/src/graph/util.rs @@ -63,10 +63,10 @@ impl DirectedGraph { } impl UndirectedGraph { - /// Kruskal's minimum spanning tree algorithm on an undirected graph. - pub fn min_spanning_tree(&self, weights: &[i64]) -> Vec { - let mut edges = (0..weights.len()).collect::>(); - edges.sort_unstable_by_key(|&e| weights[e]); + /// Kruskal's minimum spanning tree algorithm on an undirected weighted graph. + pub fn min_spanning_tree(&self) -> Vec { + let mut edges = (0..self.edge_weights.len()).collect::>(); + edges.sort_unstable_by_key(|&e| self.edge_weights[e]); let mut components = DisjointSets::new(self.num_v()); edges @@ -126,10 +126,8 @@ mod test { graph.add_weighted_edge(0, 1, 7); graph.add_weighted_edge(1, 2, 3); graph.add_weighted_edge(2, 0, 5); - //let weights = [7, 3, 5]; - //let weights = graph.edge_weights; - let mst = graph.min_spanning_tree(graph.edge_weights.as_slice()); + let mst = graph.min_spanning_tree(); let mst_cost = mst.iter().map(|&e| graph.edge_weights[e]).sum::(); assert_eq!(mst, vec![1, 2]); assert_eq!(mst_cost, 8); From b450889ad12fe9de185f8aeaf8edfbfd6b2c005c Mon Sep 17 00:00:00 2001 From: luis galup Date: Mon, 13 Feb 2023 10:06:25 -0500 Subject: [PATCH 10/28] Update README.md Signed-off-by: luis galup --- README.md | 38 +------------------------------------- 1 file changed, 1 insertion(+), 37 deletions(-) diff --git a/README.md b/README.md index ce959a9..fa5bfb7 100644 --- a/README.md +++ b/README.md @@ -1,32 +1,7 @@ -# Rust LLeet AlgorithMAs (Rust Llamas) - -[![Crates.io Version](https://img.shields.io/crates/v/contest-algorithms.svg)](https://crates.io/crates/contest-algorithms) -[![Documentation](https://docs.rs/contest-algorithms/badge.svg)](https://docs.rs/contest-algorithms) -[![license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/bevyengine/bevy/blob/master/LICENSE) -[![Crates.io Downloads](https://img.shields.io/crates/d/contest-algorithms.svg)](https://crates.io/crates/contest-algorithms) -[![Build Status](https://travis-ci.org/EbTech/rust-algorithms.svg?branch=master)](https://travis-ci.org/EbTech/rust-algorithms) -[![Gitter](https://badges.gitter.im/rust-algos/community.svg)](https://gitter.im/rust-algos/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) - -Definition: An *algorithma* is the smallest functional unit of algorithm. Larger algorithms that live in the wild consist of these building blocks. +# Rust Algorithmica A collection of classic data structures and algorithms, emphasizing usability, beauty and clarity over full generality. As such, this should be viewed not as a blackbox *library*, but as a whitebox *cookbook* demonstrating the design and implementation of algorithms. I hope it will be useful to students and educators, as well as fans of algorithmic programming contests. -This repository is distributed under the [MIT License](LICENSE). Contest submissions need not include the license text. Enjoy! - -## For Students and Educators - -When learning a new algorithm or data structure, it's often helpful to see or play with a concrete implementation. As such, this repository catalogues several classic algorithms in their simplest forms. - -In addition, the Rust language has outstanding pedagogical attributes. Its compiler acts as a teacher, enforcing strict discipline while pointing to clearer ways to structure one's logic. - -## For Programming Contests - -The original intent of this project was to build a reference for use in programming contests. As a result, it contains algorithms that are frequently useful to have in one's toolkit, with an emphasis on code that is concise and easy to modify under time pressure. - -Most competitive programmers rely on C++ for its fast execution time. However, it's notoriously unsafe, diverting a considerable share of the contestant's time and attention on mistake prevention and debugging. Java is the next most popular choice, offering a little safety at some expense to speed of coding and execution. - -To my delight, I found that Rust eliminates entire classes of bugs, while reducing visual clutter to make the rest easier to spot. And, it's *fast*. There's a learning curve, to be sure. However, a proficient Rust programmer stands to gain a competitive advantage as well as a more pleasant experience! - Some contest sites and online judges that support Rust: - [Codeforces](https://codeforces.com) - [AtCoder](https://atcoder.jp) @@ -36,17 +11,6 @@ Some contest sites and online judges that support Rust: - [HackerRank](https://www.hackerrank.com/contests) - [Timus](http://acm.timus.ru/help.aspx?topic=rust) -The following support pre-2018 versions of Rust: -- [Google Kick Start and Code Jam](https://codingcompetitions.withgoogle.com) - -For help in getting started, you may check out [some of my past submissions](https://codeforces.com/contest/1168/submission/55200038). - -## Programming Language Advocacy - -My other goal is to appeal to developers who feel limited by ancient (yet still mainstream) programming languages, by demonstrating the power of modern techniques. - -Rather than try to persuade you with words, this repository aims to show by example. If you'd like to learn the language, I recommend [the official book](https://doc.rust-lang.org/book/) or [Programming Rust](https://www.amazon.com/Programming-Rust-Fast-Systems-Development-dp-1492052590/dp/1492052590). - # Contents ## [Graphs](src/graph/) From 2f9010144714a7c17ab07f2324d17935eb4913c4 Mon Sep 17 00:00:00 2001 From: luis galup Date: Mon, 13 Feb 2023 10:07:34 -0500 Subject: [PATCH 11/28] Update README.md Signed-off-by: luis galup --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fa5bfb7..857c86c 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Rust Algorithmica +# Rust Llama AlgorithMicA A collection of classic data structures and algorithms, emphasizing usability, beauty and clarity over full generality. As such, this should be viewed not as a blackbox *library*, but as a whitebox *cookbook* demonstrating the design and implementation of algorithms. I hope it will be useful to students and educators, as well as fans of algorithmic programming contests. From 918a902fc187da149f6a54fbeee6c3cb20dc56d4 Mon Sep 17 00:00:00 2001 From: luis galup Date: Mon, 13 Feb 2023 10:17:37 -0500 Subject: [PATCH 12/28] Update README.md Signed-off-by: luis galup --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index 857c86c..c853052 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,13 @@ A collection of classic data structures and algorithms, emphasizing usability, beauty and clarity over full generality. As such, this should be viewed not as a blackbox *library*, but as a whitebox *cookbook* demonstrating the design and implementation of algorithms. I hope it will be useful to students and educators, as well as fans of algorithmic programming contests. +This is a fork of EbTech's amazing repo. My intention is to change the fundamental graph base classes to more in keeping with our intuition and understanding of graphs, do more data and data representation encapsulation, so that the algorithms dont need to know the specifics of the graph data representation, put in more tests of algorithms validity and speed,and when happy with that, start adding far more algorithms. + +Rust is a language that makes algorithms smaller and simpler, eliminating some deep bugs that are otherwise possible in other languages (like c++). +Its functional nature enforces elegance and compactness. + +Why Llama? Cuz I happen to like llamas. + Some contest sites and online judges that support Rust: - [Codeforces](https://codeforces.com) - [AtCoder](https://atcoder.jp) From 72b75a1d26581061e0ea8ea31d11a64e5093d337 Mon Sep 17 00:00:00 2001 From: luis galup Date: Mon, 13 Feb 2023 10:57:05 -0500 Subject: [PATCH 13/28] Update README.md Signed-off-by: luis galup --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index c853052..424debd 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,8 @@ Its functional nature enforces elegance and compactness. Why Llama? Cuz I happen to like llamas. +![llama](https://user-images.githubusercontent.com/9121210/218507152-5a9646d5-c8bb-4937-acfb-8834410975fd.jpg) + Some contest sites and online judges that support Rust: - [Codeforces](https://codeforces.com) - [AtCoder](https://atcoder.jp) From 0fc219ad2714b45520c38a92c4a755b94dc954c0 Mon Sep 17 00:00:00 2001 From: luis galup Date: Mon, 13 Feb 2023 11:00:03 -0500 Subject: [PATCH 14/28] Update README.md Signed-off-by: luis galup --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 424debd..979ad11 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Rust Llama AlgorithMicA +# Rust LLama AlgorithMicA A collection of classic data structures and algorithms, emphasizing usability, beauty and clarity over full generality. As such, this should be viewed not as a blackbox *library*, but as a whitebox *cookbook* demonstrating the design and implementation of algorithms. I hope it will be useful to students and educators, as well as fans of algorithmic programming contests. From c9f67c0626308e5ee312dc089769781018424372 Mon Sep 17 00:00:00 2001 From: legalup1729 Date: Mon, 13 Feb 2023 22:10:44 -0500 Subject: [PATCH 15/28] changed edge of undirected graph to set --- src/graph/connectivity.rs | 47 +++++++++++++++++++++++++++++++-------- src/graph/graph.rs | 11 +++++---- src/graph/util.rs | 4 ++-- 3 files changed, 45 insertions(+), 17 deletions(-) diff --git a/src/graph/connectivity.rs b/src/graph/connectivity.rs index b8152b8..1e0169c 100644 --- a/src/graph/connectivity.rs +++ b/src/graph/connectivity.rs @@ -160,7 +160,7 @@ impl<'a> ConnectivityUndirectedGraph<'a> { let mut data = ConnectivityData::new(graph.num_v()); for u in 0..graph.num_v() { if data.visited[u] == 0 { - connect.bcc(&mut data, u, usize::MAX); + connect.biconnected(&mut data, u, usize::MAX); } } connect @@ -168,7 +168,7 @@ impl<'a> ConnectivityUndirectedGraph<'a> { ///Tarjans algorithm for finding cut vertex. this also find biconnected components /// https://www.geeksforgeeks.org/articulation-points-or-cut-vertices-in-a-graph/ - fn bcc(&mut self, data: &mut ConnectivityData, u: usize, parent: usize) { + fn biconnected(&mut self, data: &mut ConnectivityData, u: usize, parent: usize) { data.visit(u); let mut children = 0usize; for (er, vr) in self.graph.adj_list(u) { @@ -177,7 +177,7 @@ impl<'a> ConnectivityUndirectedGraph<'a> { if data.visited[v] == 0 { children += 1; data.e_stack.push(e); - self.bcc(data, v, u); + self.biconnected(data, v, u); data.lower(u, data.low[v]); if parent < usize::MAX && data.visited[u] <= data.low[v] { @@ -195,7 +195,7 @@ impl<'a> ConnectivityUndirectedGraph<'a> { } self.is_articulation_point[u] = true; } - } else if data.visited[v] < data.visited[u] { + } else if data.visited[v] < data.visited[u] && e != parent{ data.lower(u, data.visited[v]); data.e_stack.push(e); } else if v == u { @@ -214,7 +214,7 @@ impl<'a> ConnectivityUndirectedGraph<'a> { self.is_articulation_point[u] = true; } - if parent < usize::MAX && data.visited[u] == data.low[u] { + if data.visited[u] == data.low[u] { // par is a cut edge unless par==-1 self.num_cc += 1; while let Some(v) = data.v_stack.pop() { @@ -238,9 +238,8 @@ impl<'a> ConnectivityUndirectedGraph<'a> { /// In an undirected graph, determines whether e is a bridge pub fn is_cut_edge(&self, e: usize) -> bool { - let (u, v) = self.graph.edges[e]; - - self.cc[u] != self.cc[v] + let ev = Vec::from_iter(&self.graph.edges[e]); + self.cc[*ev[0]] != self.cc[*ev[1]] } } @@ -298,7 +297,37 @@ mod test { .filter(|&u| cg.is_cut_vertex(u)) .collect::>(); - assert_eq!(bridges, vec![0, 1]); + for idx in 0..graph.num_e(){ + + if cg.is_cut_edge(idx){ + println!(" edge {} is a cut edge",idx) + } + else{ + println!(" edge {} is not a cut edge",idx) + } + } + assert_eq!(bridges, vec![0]); + assert_eq!(articulation_points, vec![1]); + } + + #[test] + fn test_articulation_points() { + let mut graph = UndirectedGraph::new(7, 8); + graph.add_edge(0, 1); + graph.add_edge(1, 2); + graph.add_edge(0, 2); + graph.add_edge(1, 3); + graph.add_edge(3, 5); + graph.add_edge(5, 4); + graph.add_edge(4, 1); + graph.add_edge(1, 6); + + let cg = ConnectivityUndirectedGraph::new(&graph); + + let articulation_points = (0..graph.num_v()) + .filter(|&u| cg.is_cut_vertex(u)) + .collect::>(); + assert_eq!(articulation_points, vec![1]); } } diff --git a/src/graph/graph.rs b/src/graph/graph.rs index eb45deb..b47c979 100644 --- a/src/graph/graph.rs +++ b/src/graph/graph.rs @@ -7,7 +7,7 @@ use core::slice::Iter; /// A compact graph representation. Edges are numbered in order of insertion. /// Each adjacency list consists of all edges pointing out from a given vertex. /// -use std::collections::HashMap; +use std::collections::HashSet; pub type AdjListIterator<'a> = Iter<'a, (usize, usize)>; @@ -72,8 +72,8 @@ impl DirectedGraph { pub struct UndirectedGraph { /// adjacency list. each vertex has a list of (edge index, neighor vertex index) pub adj_lists: Vec>, - /// Maps an edge id to vertices. is stored as smallest index first - pub edges: Vec<(usize, usize)>, + /// Maps an edge id to vertices. + pub edges: Vec>, /// edge weights pub edge_weights: Vec, } @@ -107,9 +107,8 @@ impl UndirectedGraph { /// Adds a weighted edge from u to v. pub fn add_weighted_edge(&mut self, u: usize, v: usize, w: i64) { - let minv = std::cmp::min(u, v); - let maxv = std::cmp::max(u, v); - self.edges.push((minv, maxv)); + let undirected_edge = HashSet::from([u,v]); + self.edges.push(undirected_edge); self.edge_weights.push(w); self.adj_lists[u].push((self.edges.len() - 1, v)); self.adj_lists[v].push((self.edges.len() - 1, u)); diff --git a/src/graph/util.rs b/src/graph/util.rs index f28173c..08a93e6 100644 --- a/src/graph/util.rs +++ b/src/graph/util.rs @@ -72,8 +72,8 @@ impl UndirectedGraph { edges .into_iter() .filter(|&e| { - let (u, v) = self.edges[e]; - components.merge(u, v) + let edge_vec = Vec::from_iter(&self.edges[e]); + components.merge(*edge_vec[0],*edge_vec[1]) }) .collect() } From 6c45242cb81d3509e4b4483807768949cd255957 Mon Sep 17 00:00:00 2001 From: legalup1729 Date: Mon, 13 Feb 2023 22:10:44 -0500 Subject: [PATCH 16/28] changed edge of undirected graph to set --- src/graph/connectivity.rs | 47 +++++++++++++++++++++++++++++++-------- src/graph/flow.rs | 12 +++++----- src/graph/graph.rs | 11 +++++---- src/graph/util.rs | 4 ++-- src/range_query/README.md | 2 +- 5 files changed, 52 insertions(+), 24 deletions(-) diff --git a/src/graph/connectivity.rs b/src/graph/connectivity.rs index b8152b8..1e0169c 100644 --- a/src/graph/connectivity.rs +++ b/src/graph/connectivity.rs @@ -160,7 +160,7 @@ impl<'a> ConnectivityUndirectedGraph<'a> { let mut data = ConnectivityData::new(graph.num_v()); for u in 0..graph.num_v() { if data.visited[u] == 0 { - connect.bcc(&mut data, u, usize::MAX); + connect.biconnected(&mut data, u, usize::MAX); } } connect @@ -168,7 +168,7 @@ impl<'a> ConnectivityUndirectedGraph<'a> { ///Tarjans algorithm for finding cut vertex. this also find biconnected components /// https://www.geeksforgeeks.org/articulation-points-or-cut-vertices-in-a-graph/ - fn bcc(&mut self, data: &mut ConnectivityData, u: usize, parent: usize) { + fn biconnected(&mut self, data: &mut ConnectivityData, u: usize, parent: usize) { data.visit(u); let mut children = 0usize; for (er, vr) in self.graph.adj_list(u) { @@ -177,7 +177,7 @@ impl<'a> ConnectivityUndirectedGraph<'a> { if data.visited[v] == 0 { children += 1; data.e_stack.push(e); - self.bcc(data, v, u); + self.biconnected(data, v, u); data.lower(u, data.low[v]); if parent < usize::MAX && data.visited[u] <= data.low[v] { @@ -195,7 +195,7 @@ impl<'a> ConnectivityUndirectedGraph<'a> { } self.is_articulation_point[u] = true; } - } else if data.visited[v] < data.visited[u] { + } else if data.visited[v] < data.visited[u] && e != parent{ data.lower(u, data.visited[v]); data.e_stack.push(e); } else if v == u { @@ -214,7 +214,7 @@ impl<'a> ConnectivityUndirectedGraph<'a> { self.is_articulation_point[u] = true; } - if parent < usize::MAX && data.visited[u] == data.low[u] { + if data.visited[u] == data.low[u] { // par is a cut edge unless par==-1 self.num_cc += 1; while let Some(v) = data.v_stack.pop() { @@ -238,9 +238,8 @@ impl<'a> ConnectivityUndirectedGraph<'a> { /// In an undirected graph, determines whether e is a bridge pub fn is_cut_edge(&self, e: usize) -> bool { - let (u, v) = self.graph.edges[e]; - - self.cc[u] != self.cc[v] + let ev = Vec::from_iter(&self.graph.edges[e]); + self.cc[*ev[0]] != self.cc[*ev[1]] } } @@ -298,7 +297,37 @@ mod test { .filter(|&u| cg.is_cut_vertex(u)) .collect::>(); - assert_eq!(bridges, vec![0, 1]); + for idx in 0..graph.num_e(){ + + if cg.is_cut_edge(idx){ + println!(" edge {} is a cut edge",idx) + } + else{ + println!(" edge {} is not a cut edge",idx) + } + } + assert_eq!(bridges, vec![0]); + assert_eq!(articulation_points, vec![1]); + } + + #[test] + fn test_articulation_points() { + let mut graph = UndirectedGraph::new(7, 8); + graph.add_edge(0, 1); + graph.add_edge(1, 2); + graph.add_edge(0, 2); + graph.add_edge(1, 3); + graph.add_edge(3, 5); + graph.add_edge(5, 4); + graph.add_edge(4, 1); + graph.add_edge(1, 6); + + let cg = ConnectivityUndirectedGraph::new(&graph); + + let articulation_points = (0..graph.num_v()) + .filter(|&u| cg.is_cut_vertex(u)) + .collect::>(); + assert_eq!(articulation_points, vec![1]); } } diff --git a/src/graph/flow.rs b/src/graph/flow.rs index b5acb9d..2a36ac4 100644 --- a/src/graph/flow.rs +++ b/src/graph/flow.rs @@ -125,7 +125,7 @@ impl FlowGraph { /// # Panics /// /// Panics if the flow or cost overflow a 64-bit signed integer. - pub fn mcf(&self, s: usize, t: usize) -> (i64, i64, Vec) { + pub fn min_cost_flow(&self, s: usize, t: usize) -> (i64, i64, Vec) { let mut pot = vec![0; self.graph.num_v()]; // Bellman-Ford deals with negative-cost edges at initialization. @@ -141,11 +141,11 @@ impl FlowGraph { let mut flow = vec![0; self.graph.num_e()]; let (mut min_cost, mut max_flow) = (0, 0); loop { - let par = self.mcf_search(s, &flow, &mut pot); + let par = self.min_cost_flow_search(s, &flow, &mut pot); if par[t].is_none() { break; } - let (dc, df) = self.mcf_augment(t, &par, &mut flow); + let (dc, df) = self.min_cost_flow_augment(t, &par, &mut flow); min_cost += dc; max_flow += df; } @@ -154,7 +154,7 @@ impl FlowGraph { // Maintains Johnson's potentials to prevent negative-cost residual edges. // This allows running Dijkstra instead of the slower Bellman-Ford. - fn mcf_search(&self, s: usize, flow: &[i64], pot: &mut [i64]) -> Vec> { + fn min_cost_flow_search(&self, s: usize, flow: &[i64], pot: &mut [i64]) -> Vec> { let mut vis = vec![false; self.graph.num_v()]; let mut dist = vec![Self::INF; self.graph.num_v()]; let mut par = vec![None; self.graph.num_v()]; @@ -177,7 +177,7 @@ impl FlowGraph { } // Pushes flow along an augmenting path of minimum cost. - fn mcf_augment(&self, t: usize, par: &[Option], flow: &mut [i64]) -> (i64, i64) { + fn min_cost_flow_augment(&self, t: usize, par: &[Option], flow: &mut [i64]) -> (i64, i64) { let (mut dc, mut df) = (0, Self::INF); let mut u = t; while let Some(e) = par[u] { @@ -217,7 +217,7 @@ mod test { graph.add_edge(2, 3, 7, 0, 8); graph.add_edge(1, 3, 7, 0, 10); - let (cost, flow, _) = graph.mcf(0, 3); + let (cost, flow, _) = graph.min_cost_flow(0, 3); assert_eq!(cost, 18); assert_eq!(flow, 10); } diff --git a/src/graph/graph.rs b/src/graph/graph.rs index eb45deb..b47c979 100644 --- a/src/graph/graph.rs +++ b/src/graph/graph.rs @@ -7,7 +7,7 @@ use core::slice::Iter; /// A compact graph representation. Edges are numbered in order of insertion. /// Each adjacency list consists of all edges pointing out from a given vertex. /// -use std::collections::HashMap; +use std::collections::HashSet; pub type AdjListIterator<'a> = Iter<'a, (usize, usize)>; @@ -72,8 +72,8 @@ impl DirectedGraph { pub struct UndirectedGraph { /// adjacency list. each vertex has a list of (edge index, neighor vertex index) pub adj_lists: Vec>, - /// Maps an edge id to vertices. is stored as smallest index first - pub edges: Vec<(usize, usize)>, + /// Maps an edge id to vertices. + pub edges: Vec>, /// edge weights pub edge_weights: Vec, } @@ -107,9 +107,8 @@ impl UndirectedGraph { /// Adds a weighted edge from u to v. pub fn add_weighted_edge(&mut self, u: usize, v: usize, w: i64) { - let minv = std::cmp::min(u, v); - let maxv = std::cmp::max(u, v); - self.edges.push((minv, maxv)); + let undirected_edge = HashSet::from([u,v]); + self.edges.push(undirected_edge); self.edge_weights.push(w); self.adj_lists[u].push((self.edges.len() - 1, v)); self.adj_lists[v].push((self.edges.len() - 1, u)); diff --git a/src/graph/util.rs b/src/graph/util.rs index f28173c..08a93e6 100644 --- a/src/graph/util.rs +++ b/src/graph/util.rs @@ -72,8 +72,8 @@ impl UndirectedGraph { edges .into_iter() .filter(|&e| { - let (u, v) = self.edges[e]; - components.merge(u, v) + let edge_vec = Vec::from_iter(&self.edges[e]); + components.merge(*edge_vec[0],*edge_vec[1]) }) .collect() } diff --git a/src/range_query/README.md b/src/range_query/README.md index aeb953a..c4681c0 100644 --- a/src/range_query/README.md +++ b/src/range_query/README.md @@ -1,3 +1,3 @@ # Associative Range Query (ARQ) and Mo's Algorithm -For more information on Associative Range Query, you may research "segment trees" in the programming contest literature. My implementation is more general than usual; for more information on it, please see my [blog post on Codeforces](https://codeforces.com/blog/entry/68419). +For more information on Associative Range Query, you may research "segment trees" in the programming contest literature. My implementation is more general than usual; for more information on it, please see ebtech's [blog post on Codeforces](https://codeforces.com/blog/entry/68419). From 42c2c0e1ff2dfc5df5642f0581c5d113968489b0 Mon Sep 17 00:00:00 2001 From: legalup1729 Date: Wed, 15 Feb 2023 08:36:03 -0500 Subject: [PATCH 17/28] cleaned up dijkstra impl --- src/graph/util.rs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/graph/util.rs b/src/graph/util.rs index 08a93e6..e8ef284 100644 --- a/src/graph/util.rs +++ b/src/graph/util.rs @@ -26,9 +26,9 @@ impl DirectedGraph { } // Single-source shortest paths on a directed graph with non-negative weights - pub fn dijkstra(&self, weights: &[u64], u: usize) -> Vec { - assert_eq!(self.num_e(), weights.len()); - let mut dist = vec![u64::max_value(); weights.len()]; + pub fn dijkstra(&self, u: usize) -> Vec { + + let mut dist = vec![u64::max_value(); self.edge_weights.len()]; let mut heap = std::collections::BinaryHeap::new(); dist[u] = 0; @@ -36,7 +36,7 @@ impl DirectedGraph { while let Some((Reverse(dist_u), u)) = heap.pop() { if dist[u] == dist_u { for (e, v) in self.adj_list(u) { - let dist_v = dist_u + weights[*e]; + let dist_v = dist_u + self.edge_weights[*e] as u64; if dist[*v] > dist_v { dist[*v] = dist_v; heap.push((Reverse(dist_v), *v)); @@ -136,12 +136,11 @@ mod test { #[test] fn test_dijkstra() { let mut graph = DirectedGraph::new(3, 3); - graph.add_edge(0, 1); - graph.add_edge(1, 2); - graph.add_edge(2, 0); - let weights = [7, 3, 5]; + graph.add_weighted_edge(0, 1, 7); + graph.add_weighted_edge(1, 2, 3); + graph.add_weighted_edge(2, 0, 5); - let dist = graph.dijkstra(&weights, 0); + let dist = graph.dijkstra(0); assert_eq!(dist, vec![0, 7, 10]); } From 9784cc768de4b999d9ea0a34f4c65637567041de Mon Sep 17 00:00:00 2001 From: legalup1729 Date: Wed, 15 Feb 2023 09:55:25 -0500 Subject: [PATCH 18/28] floyd warshall algo --- README.md | 11 +++++------ src/graph/util.rs | 28 ++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 979ad11..4036549 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,10 @@ # Rust LLama AlgorithMicA -A collection of classic data structures and algorithms, emphasizing usability, beauty and clarity over full generality. As such, this should be viewed not as a blackbox *library*, but as a whitebox *cookbook* demonstrating the design and implementation of algorithms. I hope it will be useful to students and educators, as well as fans of algorithmic programming contests. +Llama is a collection of classic algorithms, emphasizing usability, elegance and clarity over full generality. This is a fork of EbTech's amazing repo. My intention is to change the fundamental graph base classes to more in keeping with our intuition and understanding of graphs, do more data and data representation encapsulation, so that the algorithms dont need to know the specifics of the graph data representation, put in more tests of algorithms validity and speed,and when happy with that, start adding far more algorithms. -Rust is a language that makes algorithms smaller and simpler, eliminating some deep bugs that are otherwise possible in other languages (like c++). -Its functional nature enforces elegance and compactness. +Rust is a language that makes algorithms smaller and simpler, eliminating some deep bugs that are otherwise possible in other languages (like c++). Its functional nature enforces elegance and compactness. Why Llama? Cuz I happen to like llamas. @@ -24,7 +23,7 @@ Some contest sites and online judges that support Rust: ## [Graphs](src/graph/) -### [Graph representations](src/graph/mod.rs) +### [Graph representations](src/graph/graph.rs) - Integer index-based adjacency list representation - Disjoint set union @@ -49,12 +48,12 @@ Some contest sites and online judges that support Rust: - Dinic's blocking maximum flow - Minimum cut -- Hopcroft-Karp bipartite matching +- Hopcroft-Karp bipartite maximum matching O(\sqrt(V)*E) - Minimum cost maximum flow ## [Math](src/math/) -### [Number theory](src/math/mod.rs) +### [Number theory](src/math/division.rs) - Greatest common divisor - Canonical solution to Bezout's identity diff --git a/src/graph/util.rs b/src/graph/util.rs index 3140df5..2600ffc 100644 --- a/src/graph/util.rs +++ b/src/graph/util.rs @@ -1,6 +1,7 @@ use super::disjoint_set::DisjointSets; use super::graph::{AdjListIterator, DirectedGraph, UndirectedGraph}; use std::cmp::Reverse; +use std::f32::consts::E; impl DirectedGraph { // Helper function used by euler_path. Note that we can't use a for-loop @@ -59,6 +60,33 @@ impl DirectedGraph { adj_iters, } } + // this does not check for overflow + // you can have negative edge weights, but you also need to have no negative cycles + pub fn floyd_warshall(&self) -> Vec> { + let numv = self.num_v(); + let mut dist = vec![vec![i64::MAX; numv]; numv]; + + for v_idx in 0..numv { + dist[v_idx][v_idx] = 0; + } + + for (idx, edge) in self.edges.iter().enumerate() { + dist[edge.0][edge.1] = self.edge_weights[idx]; + } + + for k in 0..numv { + for i in 0..numv { + for j in 0..numv { + if dist[i][k] < i64::MAX && dist[k][j] < i64::MAX { + if dist[i][j] > dist[i][k] + dist[k][j] { + dist[i][j] = dist[i][k] + dist[k][j]; + } + } + } + } + } + dist + } } impl UndirectedGraph { From d3064875998c7904d9b308b58dc0727e5e017727 Mon Sep 17 00:00:00 2001 From: luis galup Date: Wed, 15 Feb 2023 11:44:50 -0500 Subject: [PATCH 19/28] Update README.md Signed-off-by: luis galup --- README.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 4036549..3c24a8b 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,13 @@ -# Rust LLama AlgorithMicA +# Rust /\r5 /\lgorithmic/\ -Llama is a collection of classic algorithms, emphasizing usability, elegance and clarity over full generality. +This is a collection of classic data structures and interesting algorithms, emphasizing clarity, elegance and understanding over generality and speed. One design criterion is to transparency: explain the names and concepts, annotate with complexity, be obvious in intention. Another is simplicity: the Rust ecosystem is full of well meaning crates, and I have intentionally decided to stick with data structures and algorithms only found in std for universality. -This is a fork of EbTech's amazing repo. My intention is to change the fundamental graph base classes to more in keeping with our intuition and understanding of graphs, do more data and data representation encapsulation, so that the algorithms dont need to know the specifics of the graph data representation, put in more tests of algorithms validity and speed,and when happy with that, start adding far more algorithms. +This is a fork of EbTech's amazing repo. My intention is to change the fundamental graph base classes to more in keeping with our intuition and understanding of graphs, do more data representation encapsulation, decouple algorithm implementation from knowledge of the internals of their datastructures, put in more tests of algorithms validity and speed,and when happy with that, start adding far more algorithms. -Rust is a language that makes algorithms smaller and simpler, eliminating some deep bugs that are otherwise possible in other languages (like c++). Its functional nature enforces elegance and compactness. +My hope is that someday my kids will use this to learn about algorithms, something that I have always been obsessed about. Its also intended for students/teachers of algorithmica. I also think that its useful for competive programming; and that Rust is in many ways a good language for that. Except for the fact that its not easy to make a linked list... + +Rust is a language that makes algorithms smaller and simpler, eliminating some deep bugs that are inevitable from the complexity in other languages (like c++). Its functional nature enforces elegance and compactness, its compiler assists in correctness. -Why Llama? Cuz I happen to like llamas. ![llama](https://user-images.githubusercontent.com/9121210/218507152-5a9646d5-c8bb-4937-acfb-8834410975fd.jpg) From f3cccbb011f1ea1d46eac3405d47ea7cc9776555 Mon Sep 17 00:00:00 2001 From: luis galup Date: Wed, 15 Feb 2023 11:46:47 -0500 Subject: [PATCH 20/28] Update README.md Signed-off-by: luis galup --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3c24a8b..5a09b61 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ This is a collection of classic data structures and interesting algorithms, emphasizing clarity, elegance and understanding over generality and speed. One design criterion is to transparency: explain the names and concepts, annotate with complexity, be obvious in intention. Another is simplicity: the Rust ecosystem is full of well meaning crates, and I have intentionally decided to stick with data structures and algorithms only found in std for universality. -This is a fork of EbTech's amazing repo. My intention is to change the fundamental graph base classes to more in keeping with our intuition and understanding of graphs, do more data representation encapsulation, decouple algorithm implementation from knowledge of the internals of their datastructures, put in more tests of algorithms validity and speed,and when happy with that, start adding far more algorithms. +This is a fork of EbTech's amazing repo. My intention is to change the fundamental graph base classes to be more in keeping with our intuition and understanding of graphs, do more data representation encapsulation, decouple algorithm implementation from knowledge of the internals of their datastructures, put in more tests of algorithms validity and speed,and when happy with that, start adding far more algorithms. My hope is that someday my kids will use this to learn about algorithms, something that I have always been obsessed about. Its also intended for students/teachers of algorithmica. I also think that its useful for competive programming; and that Rust is in many ways a good language for that. Except for the fact that its not easy to make a linked list... @@ -35,6 +35,7 @@ Some contest sites and online judges that support Rust: - Kruskal's minimum spanning tree - Dijkstra's single-source shortest paths - DFS pre-order traversal +- Floyd warshall shortest paths ### [Connected components](src/graph/connectivity.rs) From 6b73af74353e7e93e2828659a066412b0da4c541 Mon Sep 17 00:00:00 2001 From: luis galup Date: Wed, 15 Feb 2023 11:58:58 -0500 Subject: [PATCH 21/28] Update README.md Signed-off-by: luis galup --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5a09b61..040d420 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Rust /\r5 /\lgorithmic/\ -This is a collection of classic data structures and interesting algorithms, emphasizing clarity, elegance and understanding over generality and speed. One design criterion is to transparency: explain the names and concepts, annotate with complexity, be obvious in intention. Another is simplicity: the Rust ecosystem is full of well meaning crates, and I have intentionally decided to stick with data structures and algorithms only found in std for universality. +This is a collection of classic data structures and interesting algorithms, emphasizing clarity, elegance and understanding over generality and speed. One design criterion is transparency: explain the names and concepts, annotate with complexity, be obvious in intention. Another is simplicity: the Rust ecosystem is full of well meaning crates, and I have intentionally decided to stick with data structures and algorithms only found in std for universality. This is a fork of EbTech's amazing repo. My intention is to change the fundamental graph base classes to be more in keeping with our intuition and understanding of graphs, do more data representation encapsulation, decouple algorithm implementation from knowledge of the internals of their datastructures, put in more tests of algorithms validity and speed,and when happy with that, start adding far more algorithms. From 71efc8e2a9bcad70f34db26262d4ca8e784176f7 Mon Sep 17 00:00:00 2001 From: luis galup Date: Wed, 15 Feb 2023 16:40:24 -0500 Subject: [PATCH 22/28] Update LICENSE Signed-off-by: luis galup --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 9b434df..c2096de 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2017 Aram Ebtekar +Copyright (c) for portions of project Foo are held by Aram Ebtekar 2017 as part of project rust-algorithms. All other copyright for project rust-ars-algorithmica are held by Luis Galup 2023. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From e9901913fafb6d6381f51d817bb3c61b93ff9c9b Mon Sep 17 00:00:00 2001 From: luis galup Date: Wed, 15 Feb 2023 16:45:53 -0500 Subject: [PATCH 23/28] Create LICENSE.md Signed-off-by: luis galup --- LICENSE.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 LICENSE.md diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..50f67ed --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,28 @@ +BSD 3-Clause License + +Copyright (c) 2023, luis galup + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. From 3840dd8cfcf5c0fac8e4e1d441f4dbb6332661d3 Mon Sep 17 00:00:00 2001 From: luis galup Date: Wed, 15 Feb 2023 17:00:54 -0500 Subject: [PATCH 24/28] did licensing properly --- LICENSE | 21 --------------------- LICENSE.md | 3 ++- 2 files changed, 2 insertions(+), 22 deletions(-) delete mode 100644 LICENSE diff --git a/LICENSE b/LICENSE deleted file mode 100644 index c2096de..0000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) for portions of project Foo are held by Aram Ebtekar 2017 as part of project rust-algorithms. All other copyright for project rust-ars-algorithmica are held by Luis Galup 2023. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/LICENSE.md b/LICENSE.md index 50f67ed..8abb436 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,6 +1,7 @@ BSD 3-Clause License -Copyright (c) 2023, luis galup +Copyright (c) for portions of project Foo are held by Aram Ebtekar 2017 as part of project rust-algorithms. All other copyright for project rust-ars-algorithmica are held by Luis Galup 2023. + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: From 3cb313134d2c415cedcf9bcec4423dc1bb7cf67d Mon Sep 17 00:00:00 2001 From: luis galup Date: Thu, 16 Feb 2023 07:26:37 -0500 Subject: [PATCH 25/28] added test for floyd warshall --- src/graph/util.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/graph/util.rs b/src/graph/util.rs index 2600ffc..e37ec91 100644 --- a/src/graph/util.rs +++ b/src/graph/util.rs @@ -232,4 +232,24 @@ mod test { assert_eq!(num_v, dfs_check.len()); assert_eq!(num_v - 1, dfs_check[num_v - 1]); } + + #[test] + fn test_floyd_warshall() { + let num_v = 8; + let mut graph = DirectedGraph::new(num_v, 10); + graph.add_weighted_edge(0, 1, 1); + graph.add_weighted_edge(1, 2, 2); + graph.add_weighted_edge(1, 4, 4); + graph.add_weighted_edge(2, 5, 3); + graph.add_weighted_edge(4, 3, 6); + graph.add_weighted_edge(5, 4, 10); + graph.add_weighted_edge(3, 6, 2); + graph.add_weighted_edge(4, 6, 7); + graph.add_weighted_edge(6, 7, 2); + graph.add_weighted_edge(5, 7, 9); + + let dist = graph.floyd_warshall(); + + assert_eq!(dist[0][7], 14i64); + } } From fbce395429ac41d8d6c05d961ecbe97b1cb3a2a8 Mon Sep 17 00:00:00 2001 From: luis galup Date: Fri, 17 Feb 2023 10:35:03 -0500 Subject: [PATCH 26/28] added levenshtein distance --- src/string_proc.rs | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/string_proc.rs b/src/string_proc.rs index 15d7535..da0583e 100644 --- a/src/string_proc.rs +++ b/src/string_proc.rs @@ -350,6 +350,40 @@ pub fn z_algorithm(text: &[impl Eq]) -> Vec { z } +///Levenshtein distance :is a string metric for measuring the difference between two sequences. +/// Informally, the Levenshtein distance between two words is the minimum number of single-character edits +/// (insertions, deletions or substitutions) required to change one word into the other. +/// It is named after the Soviet mathematician Vladimir Levenshtein, who considered this distance in 1965. +/// +/// for all i and j, d[i,j] will hold the Levenshtein distance between +/// the first i characters of s and the first j characters of t; starting at 1. +/// in other words, m is length of s, n is length of t. +/// note that dist has (m+1)x(n+1) values, +pub fn levenshtein_distance(s: &str, m: usize, t: &str, n: usize) -> Vec> { + let mut dist = vec![vec![0u64; n + 1]; m + 1]; + for i in 0..m + 1 { + dist[i][0] = i as u64; + } + for j in 0..n + 1 { + dist[0][j] = j as u64; + } + + for j in 1..n + 1 { + for i in 1..m + 1 { + dist[i][j] = dist[i - 1][j - 1] + + if s.chars().nth(i - 1) != t.chars().nth(j - 1) { + 1u64 + } else { + 0u64 + }; + + dist[i][j] = std::cmp::min(dist[i][j], dist[i - 1][j] + 1); + dist[i][j] = std::cmp::min(dist[i][j], dist[i][j - 1] + 1); + } + } + + dist +} #[cfg(test)] mod test { use super::*; @@ -426,4 +460,14 @@ mod test { assert_eq!(pal_len, vec![1, 0, 1, 0, 3, 0, 5, 0, 3, 0, 1]); } + + #[test] + fn test_levenshtein_distance() { + let text1 = "Saturday"; + let text2 = "Sunday"; + + let d = levenshtein_distance(text1, 8, text2, 6); + + assert_eq!(d[8][6], 3); + } } From 8292e95c25270a9f37055dcf5cf08e21e5e19ff4 Mon Sep 17 00:00:00 2001 From: luis enrique galup Date: Thu, 25 Apr 2024 18:40:57 -0400 Subject: [PATCH 27/28] flakes --- flake.lock | 133 +++++++++++++++++++++++++++++++++++++++++++++++++++++ flake.nix | 62 +++++++++++++++++++++++++ 2 files changed, 195 insertions(+) create mode 100644 flake.lock create mode 100644 flake.nix diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..13f5bdf --- /dev/null +++ b/flake.lock @@ -0,0 +1,133 @@ +{ + "nodes": { + "flake-parts": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib" + }, + "locked": { + "lastModified": 1698882062, + "narHash": "sha256-HkhafUayIqxXyHH1X8d9RDl1M2CkFgZLjKD3MzabiEo=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "8c9fa2545007b49a5db5f650ae91f227672c3877", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1681202837, + "narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "cfacdce06f30d2b68473a46042957675eebb3401", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1700794826, + "narHash": "sha256-RyJTnTNKhO0yqRpDISk03I/4A67/dp96YRxc86YOPgU=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "5a09cb4b393d58f9ed0d9ca1555016a8543c2ac8", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-lib": { + "locked": { + "dir": "lib", + "lastModified": 1698611440, + "narHash": "sha256-jPjHjrerhYDy3q9+s5EAsuhyhuknNfowY6yt6pjn9pc=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "0cbe9f69c234a7700596e943bfae7ef27a31b735", + "type": "github" + }, + "original": { + "dir": "lib", + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1681358109, + "narHash": "sha256-eKyxW4OohHQx9Urxi7TQlFBTDWII+F+x2hklDOQPB50=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "96ba1c52e54e74c3197f4d43026b3f3d92e83ff9", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-parts": "flake-parts", + "nixpkgs": "nixpkgs", + "rust-overlay": "rust-overlay" + } + }, + "rust-overlay": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs_2" + }, + "locked": { + "lastModified": 1700965014, + "narHash": "sha256-vprUv4maYeo0zW5uyEznXsv6DXwE+lLk4dcyOz6rVBI=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "ee42d1bf90ceed1b1d2e4b79f947f7513f3a3506", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..3904860 --- /dev/null +++ b/flake.nix @@ -0,0 +1,62 @@ +{ + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + flake-parts.url = "github:hercules-ci/flake-parts"; + rust-overlay.url = "github:oxalica/rust-overlay"; + }; + + outputs = inputs: + inputs.flake-parts.lib.mkFlake { inherit inputs; } { + systems = [ "x86_64-linux" ]; + perSystem = { config, self', pkgs, lib, system, ... }: + let + runtimeDeps = with pkgs; [ alsa-lib speechd ]; + buildDeps = with pkgs; [ pkg-config rustPlatform.bindgenHook ]; + devDeps = with pkgs; [ gdb ]; + + cargoToml = builtins.fromTOML (builtins.readFile ./Cargo.toml); + msrv = cargoToml.package.rust-version; + + rustPackage = features: + (pkgs.makeRustPlatform { + cargo = pkgs.rust-bin.stable.latest.minimal; + rustc = pkgs.rust-bin.stable.latest.minimal; + }).buildRustPackage { + inherit (cargoToml.package) name version; + src = ./.; + cargoLock.lockFile = ./Cargo.lock; + buildFeatures = features; + buildInputs = runtimeDeps; + nativeBuildInputs = buildDeps; + # Uncomment if your cargo tests require networking or otherwise + # don't play nicely with the Nix build sandbox: + # doCheck = false; + }; + + mkDevShell = rustc: + pkgs.mkShell { + shellHook = '' + export RUST_SRC_PATH=${pkgs.rustPlatform.rustLibSrc} + ''; + buildInputs = runtimeDeps; + nativeBuildInputs = buildDeps ++ devDeps ++ [ rustc ]; + }; + in { + _module.args.pkgs = import inputs.nixpkgs { + inherit system; + overlays = [ (import inputs.rust-overlay) ]; + }; + + packages.default = self'.packages.example; + devShells.default = self'.devShells.nightly; + + packages.example = (rustPackage "foobar"); + packages.example-base = (rustPackage ""); + + devShells.nightly = (mkDevShell (pkgs.rust-bin.selectLatestNightlyWith + (toolchain: toolchain.default))); + devShells.stable = (mkDevShell pkgs.rust-bin.stable.latest.default); + devShells.msrv = (mkDevShell pkgs.rust-bin.stable.${msrv}.default); + }; + }; +} From a44a043d4b05d5bc33602f358346205bd533d55c Mon Sep 17 00:00:00 2001 From: luis enrique galup Date: Wed, 25 Dec 2024 11:25:06 -0500 Subject: [PATCH 28/28] adding steins algo for fast gcd computation --- src/math/division.rs | 46 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/math/division.rs b/src/math/division.rs index cc9fbba..1851a56 100644 --- a/src/math/division.rs +++ b/src/math/division.rs @@ -86,6 +86,37 @@ pub fn is_prime(n: i64) -> bool { } } +// Steins algorithm: Stein’s algorithm or binary GCD algorithm is an algorithm that +// computes the greatest common divisor of two non-negative integers. +// Stein’s algorithm replaces division with arithmetic shifts, +// comparisons, and subtraction. +// Use Stein's algorithm +fn gcd_stein(mut m: u64, mut n: u64) -> u64 { + if m == 0 || n == 0 { + return m | n; + } + + // find common factors of 2 + let shift = (m | n).trailing_zeros(); + m >>= m.trailing_zeros(); + n >>= n.trailing_zeros(); + + while m != n { + if m > n { + m -= n; + m >>= m.trailing_zeros(); + } else { + n -= m; + n >>= n.trailing_zeros(); + } + } + m << shift +} + +// Pollard's rho algorithm is an algorithm for integer factorization. +// It was invented by John Pollard in 1975.[1] It uses only a small amount of space, +// and its expected running time is proportional to the square root of the smallest +// prime factor of the composite number being factorized. fn pollard_rho(n: i64) -> i64 { for a in 1..n { let f = |x| pos_mod(mod_mul(x, x, n) + a, n); @@ -177,4 +208,19 @@ mod test { vec![11, 13, 17, 19, 29, 37, 41, 43, 61, 97, 109, 127] ); } + + #[test] + fn test_steins() { + let (a, b) = (14, 35); + let d = gcd_stein(a, b); + assert_eq!(d, 7u64); + + let (p1, p2) = (393919, 919393); + let d1 = gcd_stein(p1, p2); + assert_eq!(d1, 1u64); + + let (p3, p4) = (679389209, 696729599); + let d2 = gcd_stein(p3, p4); + assert_eq!(d2, 1u64); + } }