From 195f257e36b20282a2d8434f5310476ace9586ea Mon Sep 17 00:00:00 2001 From: "arne.prinzler" Date: Fri, 22 Mar 2024 20:17:26 +0100 Subject: [PATCH 1/8] bringing remote branch up to date --- {.github => git_hooks/.github}/CODEOWNERS | 0 {.github => git_hooks/.github}/pull_request_template.md | 0 {.github => git_hooks/.github}/workflows/build.yml | 0 {.github => git_hooks/.github}/workflows/directory_workflow.yml | 0 .../.github}/workflows/scripts/build_directory/Cargo.toml | 0 .../.github}/workflows/scripts/build_directory/src/lib.rs | 0 .../.github}/workflows/scripts/build_directory/src/main.rs | 0 {.github => git_hooks/.github}/workflows/stale.yml | 0 .../.github}/workflows/upload_coverage_report.yml | 0 9 files changed, 0 insertions(+), 0 deletions(-) rename {.github => git_hooks/.github}/CODEOWNERS (100%) rename {.github => git_hooks/.github}/pull_request_template.md (100%) rename {.github => git_hooks/.github}/workflows/build.yml (100%) rename {.github => git_hooks/.github}/workflows/directory_workflow.yml (100%) rename {.github => git_hooks/.github}/workflows/scripts/build_directory/Cargo.toml (100%) rename {.github => git_hooks/.github}/workflows/scripts/build_directory/src/lib.rs (100%) rename {.github => git_hooks/.github}/workflows/scripts/build_directory/src/main.rs (100%) rename {.github => git_hooks/.github}/workflows/stale.yml (100%) rename {.github => git_hooks/.github}/workflows/upload_coverage_report.yml (100%) diff --git a/.github/CODEOWNERS b/git_hooks/.github/CODEOWNERS similarity index 100% rename from .github/CODEOWNERS rename to git_hooks/.github/CODEOWNERS diff --git a/.github/pull_request_template.md b/git_hooks/.github/pull_request_template.md similarity index 100% rename from .github/pull_request_template.md rename to git_hooks/.github/pull_request_template.md diff --git a/.github/workflows/build.yml b/git_hooks/.github/workflows/build.yml similarity index 100% rename from .github/workflows/build.yml rename to git_hooks/.github/workflows/build.yml diff --git a/.github/workflows/directory_workflow.yml b/git_hooks/.github/workflows/directory_workflow.yml similarity index 100% rename from .github/workflows/directory_workflow.yml rename to git_hooks/.github/workflows/directory_workflow.yml diff --git a/.github/workflows/scripts/build_directory/Cargo.toml b/git_hooks/.github/workflows/scripts/build_directory/Cargo.toml similarity index 100% rename from .github/workflows/scripts/build_directory/Cargo.toml rename to git_hooks/.github/workflows/scripts/build_directory/Cargo.toml diff --git a/.github/workflows/scripts/build_directory/src/lib.rs b/git_hooks/.github/workflows/scripts/build_directory/src/lib.rs similarity index 100% rename from .github/workflows/scripts/build_directory/src/lib.rs rename to git_hooks/.github/workflows/scripts/build_directory/src/lib.rs diff --git a/.github/workflows/scripts/build_directory/src/main.rs b/git_hooks/.github/workflows/scripts/build_directory/src/main.rs similarity index 100% rename from .github/workflows/scripts/build_directory/src/main.rs rename to git_hooks/.github/workflows/scripts/build_directory/src/main.rs diff --git a/.github/workflows/stale.yml b/git_hooks/.github/workflows/stale.yml similarity index 100% rename from .github/workflows/stale.yml rename to git_hooks/.github/workflows/stale.yml diff --git a/.github/workflows/upload_coverage_report.yml b/git_hooks/.github/workflows/upload_coverage_report.yml similarity index 100% rename from .github/workflows/upload_coverage_report.yml rename to git_hooks/.github/workflows/upload_coverage_report.yml From b065ccab0968451bb94d9c85c5df508b60c7a6bd Mon Sep 17 00:00:00 2001 From: "arne.prinzler" Date: Fri, 22 Mar 2024 20:19:57 +0100 Subject: [PATCH 2/8] Revert "bringing remote branch up to date" This reverts commit 195f257e36b20282a2d8434f5310476ace9586ea. --- {git_hooks/.github => .github}/CODEOWNERS | 0 {git_hooks/.github => .github}/pull_request_template.md | 0 {git_hooks/.github => .github}/workflows/build.yml | 0 {git_hooks/.github => .github}/workflows/directory_workflow.yml | 0 .../workflows/scripts/build_directory/Cargo.toml | 0 .../workflows/scripts/build_directory/src/lib.rs | 0 .../workflows/scripts/build_directory/src/main.rs | 0 {git_hooks/.github => .github}/workflows/stale.yml | 0 .../.github => .github}/workflows/upload_coverage_report.yml | 0 9 files changed, 0 insertions(+), 0 deletions(-) rename {git_hooks/.github => .github}/CODEOWNERS (100%) rename {git_hooks/.github => .github}/pull_request_template.md (100%) rename {git_hooks/.github => .github}/workflows/build.yml (100%) rename {git_hooks/.github => .github}/workflows/directory_workflow.yml (100%) rename {git_hooks/.github => .github}/workflows/scripts/build_directory/Cargo.toml (100%) rename {git_hooks/.github => .github}/workflows/scripts/build_directory/src/lib.rs (100%) rename {git_hooks/.github => .github}/workflows/scripts/build_directory/src/main.rs (100%) rename {git_hooks/.github => .github}/workflows/stale.yml (100%) rename {git_hooks/.github => .github}/workflows/upload_coverage_report.yml (100%) diff --git a/git_hooks/.github/CODEOWNERS b/.github/CODEOWNERS similarity index 100% rename from git_hooks/.github/CODEOWNERS rename to .github/CODEOWNERS diff --git a/git_hooks/.github/pull_request_template.md b/.github/pull_request_template.md similarity index 100% rename from git_hooks/.github/pull_request_template.md rename to .github/pull_request_template.md diff --git a/git_hooks/.github/workflows/build.yml b/.github/workflows/build.yml similarity index 100% rename from git_hooks/.github/workflows/build.yml rename to .github/workflows/build.yml diff --git a/git_hooks/.github/workflows/directory_workflow.yml b/.github/workflows/directory_workflow.yml similarity index 100% rename from git_hooks/.github/workflows/directory_workflow.yml rename to .github/workflows/directory_workflow.yml diff --git a/git_hooks/.github/workflows/scripts/build_directory/Cargo.toml b/.github/workflows/scripts/build_directory/Cargo.toml similarity index 100% rename from git_hooks/.github/workflows/scripts/build_directory/Cargo.toml rename to .github/workflows/scripts/build_directory/Cargo.toml diff --git a/git_hooks/.github/workflows/scripts/build_directory/src/lib.rs b/.github/workflows/scripts/build_directory/src/lib.rs similarity index 100% rename from git_hooks/.github/workflows/scripts/build_directory/src/lib.rs rename to .github/workflows/scripts/build_directory/src/lib.rs diff --git a/git_hooks/.github/workflows/scripts/build_directory/src/main.rs b/.github/workflows/scripts/build_directory/src/main.rs similarity index 100% rename from git_hooks/.github/workflows/scripts/build_directory/src/main.rs rename to .github/workflows/scripts/build_directory/src/main.rs diff --git a/git_hooks/.github/workflows/stale.yml b/.github/workflows/stale.yml similarity index 100% rename from git_hooks/.github/workflows/stale.yml rename to .github/workflows/stale.yml diff --git a/git_hooks/.github/workflows/upload_coverage_report.yml b/.github/workflows/upload_coverage_report.yml similarity index 100% rename from git_hooks/.github/workflows/upload_coverage_report.yml rename to .github/workflows/upload_coverage_report.yml From d0c2b1814ea2881f14fa511c4527825609f07f12 Mon Sep 17 00:00:00 2001 From: "arne.prinzler" Date: Fri, 22 Mar 2024 20:51:41 +0100 Subject: [PATCH 3/8] added implementation for plus-minus-one rmq --- src/data_structures/mod.rs | 2 + src/data_structures/plus_minus_rmq.rs | 224 ++++++++++++++++++++++++++ 2 files changed, 226 insertions(+) create mode 100644 src/data_structures/plus_minus_rmq.rs diff --git a/src/data_structures/mod.rs b/src/data_structures/mod.rs index d02b40e19b7..ad2304c9581 100644 --- a/src/data_structures/mod.rs +++ b/src/data_structures/mod.rs @@ -9,6 +9,7 @@ mod heap; mod infix_to_postfix; mod lazy_segment_tree; mod linked_list; +mod plus_minus_rmq; mod postfix_evaluation; mod probabilistic; mod queue; @@ -34,6 +35,7 @@ pub use self::heap::Heap; pub use self::infix_to_postfix::infix_to_postfix; pub use self::lazy_segment_tree::LazySegmentTree; pub use self::linked_list::LinkedList; +pub use self::plus_minus_rmq::PlusMinusOneRMQ; pub use self::postfix_evaluation::evaluate_postfix; pub use self::probabilistic::bloom_filter; pub use self::probabilistic::count_min_sketch; diff --git a/src/data_structures/plus_minus_rmq.rs b/src/data_structures/plus_minus_rmq.rs new file mode 100644 index 00000000000..44f6ca847f8 --- /dev/null +++ b/src/data_structures/plus_minus_rmq.rs @@ -0,0 +1,224 @@ +use std::cmp::PartialOrd; + +/// A data-structure for answering +-1 range minimum queries on arrays +/// +/// # Complexity +/// Precomputation in O(n) and queries in O(1) time +/// +/// # Notes +/// This is NOT the general RMQ on arrays but 'just' the +-1 RMQ, which is used in combination with LCA and +/// cartiesian trees on arrays to get a general RMQ implementation +/// +/// # Sources +/// used as reference +pub struct PlusMinusOneRMQ { + array: Vec, + n: usize, + k: usize, + block_min: Vec, + block_min_idx: Vec, + sparse_idx: Vec>, + block_rmq: Vec>>, + block_mask: Vec, +} + +impl PlusMinusOneRMQ { + pub fn new(mut array: Vec) -> Self { + input_padding(&mut array); + let n = array.len() as u32; + let k = n.ilog2()/ 2; + let mut new = Self { + array: array, + n: n as usize, + k: k as usize, + block_min: Vec::new(), + block_min_idx: Vec::new(), + sparse_idx: vec![Vec::new()], // is a sparse table, which only stores the indeces + block_rmq: Vec::new(), + block_mask: Vec::new(), + }; + new.calc_block_min(); + new.build_sparse(); + new.fill_block_rmq(); + new.precompute_masks(); + + return new; + } + fn calc_block_min(&mut self) { + for i in 0..(self.n + self.k -1) / self.k { + let (min, min_idx) = self.calc_min(i*self.k); + self.block_min.push(min); + self.block_min_idx.push(min_idx) + } + } + + fn calc_min(&mut self, i: usize) -> (T, usize) { + let mut current_min = self.array[i]; + let mut min_idx: usize = i; + for j in i..i + self.k { + match self.array.get(j) { + Some(x) => { + current_min = min(current_min, *x); + min_idx = self.min_idx(min_idx, j); + }, + None => break, + }; + } + return (current_min, min_idx); + } + + fn build_sparse(&mut self) { + for x in self.block_min_idx.iter() { + self.sparse_idx[0].push(*x); + } + let m = self.block_min_idx.len(); + for loglen in 1..=(m.ilog2() as usize) { + self.sparse_idx.push(Vec::new()); + for i in 0..= m - (1 << loglen) { + let a = self.sparse_idx[loglen-1][i]; + let b = self.sparse_idx[loglen-1][i + (1 << (loglen - 1))]; + let tmp = self.min_idx(a,b); + self.sparse_idx[loglen].push(tmp); + } + } + } + + pub fn get(&self, mut l: usize, mut r: usize) -> T { + // println!("RMQ({},{})", l, r); + if l > r { + let tmp = l; + l = r; + r = tmp; + } + + let block_l = l/self.k ; + let block_r = r/self.k ; + let l_suffix = self.get_in_block(block_l, l % self.k, self.k - 1); + let r_prefix = self.get_in_block(block_r, 0, r % self.k); + match block_r - block_l { + 0 => return self.array[self.get_in_block(block_l, l % self.k, r % self.k)], + 1 => return self.array[self.min_idx(l_suffix, r_prefix)], + _ => return self.array[self.min_idx(self.min_idx(l_suffix, self.get_on_blocks(block_l+1, block_r-1)), r_prefix)], + }; + } + + fn get_on_blocks(&self, l: usize, r: usize) -> usize { + let loglen = (r-l+1).ilog2() as usize; + let idx: usize = ((r as i64) - (1 << loglen as i64) + 1) as usize; + let a = self.sparse_idx[loglen][l as usize]; + let b = self.sparse_idx[loglen][idx]; + return self.min_idx(a,b); + } + + fn get_in_block(&self, block_idx: usize, l: usize, r: usize) -> usize { + let mask = self.block_mask[block_idx]; + let min_idx = self.block_rmq[mask as usize][l][r]; + return min_idx + block_idx * self.k; + } + + fn fill_block_rmq(&mut self) { + let mask_amount = 1 << (self.k - 1); + for mask in 0..mask_amount { + let tmp = self.rmq_bitmask(mask as u32); // maybe change to usize + self.block_rmq.push(tmp); + } + } + + fn rmq_bitmask(&mut self, mask: u32) -> Vec> { + let mut rmq_matrix: Vec> = vec![vec![0;self.k]; self.k]; + let list = bitmask_to_array(self.k, mask); + for i in 0..self.k { + for j in i..self.k { + if i == j { + rmq_matrix[i][j] = i; + } + else { + let min = list[rmq_matrix[i][j-1]]; //Do we want range-minimum or range-maximum + if list[j] < min { + rmq_matrix[i][j] = j; + } + else { + rmq_matrix[i][j] = rmq_matrix[i][j-1]; + } + } + } + } + return rmq_matrix; + } + + fn precompute_masks(&mut self) { + for i in 0..self.block_min.len() { + self.block_mask.push(self.calc_bitmask(i)); + } + } + + // we initialize the mask with k-1 ones + // this is necessary so if blocks are of size < k the bitmask is still correct + fn calc_bitmask(&self, block_idx: usize) -> u32{ + let mut mask: u32 = (1 << (self.k - 1)) - 1; + for i in self.k*block_idx + 1..self.k * (block_idx + 1) { + let last = self.array[i-1]; + match self.array.get(i) { + Some(&x) => { + if last >= x { + mask -= 1 << (self.k-1-(i % self.k)); + } + }, + None => break, + }; + } + return mask; + } + fn min_idx(&self, i: usize, j: usize) -> usize { + if self.array[i] < self.array[j] { + return i; + } + return j; + } +} + +// padds the given array to have at least length 4 +// this is needed to have a valid k = 0.5 * log n +fn input_padding(array: &mut Vec) { + while array.len() < 4 { + let last = array[array.len() - 1]; + array.push(last); + } +} + +fn min(a: T, b: T) -> T { + match a < b { + true => a, + _ => b, + } +} + +fn bitmask_to_array(k: usize, mut mask: u32) -> Vec { + let mut list: Vec = vec![0]; + for i in 0..k-1{ + match mask % 2 { + 1 => list.push(list[i] - 1), + _ => list.push(list[i] + 1), + }; + mask /= 2; + } + list.reverse(); + return list; +} + +#[cfg(test)] +mod tests { + #[test] + fn simple_query_tests() { + let v1 = vec![1, 2, 3, 2, 3, 4, 5, 4, 3, 2, 1, 0, -1]; + let sparse_v1 = super::PlusMinusOneRMQ::new(v1); + + assert_eq!(2, sparse_v1.get(1, 5)); + assert_eq!(1, sparse_v1.get(0, 9)); + assert_eq!(-1, sparse_v1.get(10, 12)); + // assert!(sparse_v1.get_range_min(4, 3).is_err()); + // assert!(sparse_v1.get_range_min(0, 1000).is_err()); + // assert!(sparse_v1.get_range_min(1000, 1001).is_err()); + } + +} \ No newline at end of file From 6a3b67a50b8032ae7b3441c665f09be756c871a1 Mon Sep 17 00:00:00 2001 From: "arne.prinzler" Date: Fri, 22 Mar 2024 20:58:08 +0100 Subject: [PATCH 4/8] changed getter to get_range_min like in sparse_table implementation --- src/data_structures/plus_minus_rmq.rs | 35 ++++++++++++--------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/src/data_structures/plus_minus_rmq.rs b/src/data_structures/plus_minus_rmq.rs index 44f6ca847f8..e46ede01434 100644 --- a/src/data_structures/plus_minus_rmq.rs +++ b/src/data_structures/plus_minus_rmq.rs @@ -83,22 +83,19 @@ impl PlusMinusOneRMQ { } } - pub fn get(&self, mut l: usize, mut r: usize) -> T { - // println!("RMQ({},{})", l, r); - if l > r { - let tmp = l; - l = r; - r = tmp; + pub fn get_range_min(&self, start: usize, end: usize) -> Result { + if start >= end || start >= self.array.len() || end > self.array.len() { + return Err("invalid range"); } - let block_l = l/self.k ; - let block_r = r/self.k ; - let l_suffix = self.get_in_block(block_l, l % self.k, self.k - 1); - let r_prefix = self.get_in_block(block_r, 0, r % self.k); + let block_l = start / self.k ; + let block_r = (end - 1) / self.k ; + let l_suffix = self.get_in_block(block_l, start % self.k, self.k - 1); + let r_prefix = self.get_in_block(block_r, 0, (end - 1) % self.k); match block_r - block_l { - 0 => return self.array[self.get_in_block(block_l, l % self.k, r % self.k)], - 1 => return self.array[self.min_idx(l_suffix, r_prefix)], - _ => return self.array[self.min_idx(self.min_idx(l_suffix, self.get_on_blocks(block_l+1, block_r-1)), r_prefix)], + 0 => return Ok(self.array[self.get_in_block(block_l, start % self.k, (end - 1) % self.k)]), + 1 => return Ok(self.array[self.min_idx(l_suffix, r_prefix)]), + _ => return Ok(self.array[self.min_idx(self.min_idx(l_suffix, self.get_on_blocks(block_l+1, block_r-1)), r_prefix)]), }; } @@ -213,12 +210,12 @@ mod tests { let v1 = vec![1, 2, 3, 2, 3, 4, 5, 4, 3, 2, 1, 0, -1]; let sparse_v1 = super::PlusMinusOneRMQ::new(v1); - assert_eq!(2, sparse_v1.get(1, 5)); - assert_eq!(1, sparse_v1.get(0, 9)); - assert_eq!(-1, sparse_v1.get(10, 12)); - // assert!(sparse_v1.get_range_min(4, 3).is_err()); - // assert!(sparse_v1.get_range_min(0, 1000).is_err()); - // assert!(sparse_v1.get_range_min(1000, 1001).is_err()); + assert_eq!(Ok(2), sparse_v1.get_range_min(1, 6)); + assert_eq!(Ok(1), sparse_v1.get_range_min(0, 10)); + assert_eq!(Ok(-1), sparse_v1.get_range_min(10, 13)); + assert!(sparse_v1.get_range_min(4, 3).is_err()); + assert!(sparse_v1.get_range_min(0, 1000).is_err()); + assert!(sparse_v1.get_range_min(1000, 1001).is_err()); } } \ No newline at end of file From d3b59c0530518bd10698ce1067edb08921636138 Mon Sep 17 00:00:00 2001 From: "arne.prinzler" Date: Fri, 22 Mar 2024 21:12:18 +0100 Subject: [PATCH 5/8] reused sparse table implementation of range_minimum_query.rs --- src/data_structures/plus_minus_rmq.rs | 19 ++----------------- src/data_structures/range_minimum_query.rs | 2 +- 2 files changed, 3 insertions(+), 18 deletions(-) diff --git a/src/data_structures/plus_minus_rmq.rs b/src/data_structures/plus_minus_rmq.rs index e46ede01434..1a2ab5dcdfe 100644 --- a/src/data_structures/plus_minus_rmq.rs +++ b/src/data_structures/plus_minus_rmq.rs @@ -1,4 +1,5 @@ use std::cmp::PartialOrd; +use super:: range_minimum_query::build_sparse_table; /// A data-structure for answering +-1 range minimum queries on arrays /// @@ -38,7 +39,7 @@ impl PlusMinusOneRMQ { block_mask: Vec::new(), }; new.calc_block_min(); - new.build_sparse(); + new.sparse_idx = build_sparse_table(&new.array); new.fill_block_rmq(); new.precompute_masks(); @@ -67,22 +68,6 @@ impl PlusMinusOneRMQ { return (current_min, min_idx); } - fn build_sparse(&mut self) { - for x in self.block_min_idx.iter() { - self.sparse_idx[0].push(*x); - } - let m = self.block_min_idx.len(); - for loglen in 1..=(m.ilog2() as usize) { - self.sparse_idx.push(Vec::new()); - for i in 0..= m - (1 << loglen) { - let a = self.sparse_idx[loglen-1][i]; - let b = self.sparse_idx[loglen-1][i + (1 << (loglen - 1))]; - let tmp = self.min_idx(a,b); - self.sparse_idx[loglen].push(tmp); - } - } - } - pub fn get_range_min(&self, start: usize, end: usize) -> Result { if start >= end || start >= self.array.len() || end > self.array.len() { return Err("invalid range"); diff --git a/src/data_structures/range_minimum_query.rs b/src/data_structures/range_minimum_query.rs index fd82be5563d..3dfcd105ccd 100644 --- a/src/data_structures/range_minimum_query.rs +++ b/src/data_structures/range_minimum_query.rs @@ -41,7 +41,7 @@ impl RangeMinimumQuery { } } -fn build_sparse_table(array: &[T]) -> Vec> { +pub fn build_sparse_table(array: &[T]) -> Vec> { let mut table: Vec> = vec![(0..array.len()).collect()]; let len = array.len(); From cb8da54e8cb7b460b09647d330f1298038d55b2e Mon Sep 17 00:00:00 2001 From: "arne.prinzler" Date: Fri, 22 Mar 2024 21:20:40 +0100 Subject: [PATCH 6/8] changed style to satisfy clippy and cargo fmt --- src/data_structures/plus_minus_rmq.rs | 89 ++++++++++++++------------- 1 file changed, 45 insertions(+), 44 deletions(-) diff --git a/src/data_structures/plus_minus_rmq.rs b/src/data_structures/plus_minus_rmq.rs index 1a2ab5dcdfe..26a0db004c6 100644 --- a/src/data_structures/plus_minus_rmq.rs +++ b/src/data_structures/plus_minus_rmq.rs @@ -1,5 +1,5 @@ +use super::range_minimum_query::build_sparse_table; use std::cmp::PartialOrd; -use super:: range_minimum_query::build_sparse_table; /// A data-structure for answering +-1 range minimum queries on arrays /// @@ -14,7 +14,7 @@ use super:: range_minimum_query::build_sparse_table; /// used as reference pub struct PlusMinusOneRMQ { array: Vec, - n: usize, + n: usize, k: usize, block_min: Vec, block_min_idx: Vec, @@ -27,9 +27,9 @@ impl PlusMinusOneRMQ { pub fn new(mut array: Vec) -> Self { input_padding(&mut array); let n = array.len() as u32; - let k = n.ilog2()/ 2; + let k = n.ilog2() / 2; let mut new = Self { - array: array, + array, n: n as usize, k: k as usize, block_min: Vec::new(), @@ -43,11 +43,11 @@ impl PlusMinusOneRMQ { new.fill_block_rmq(); new.precompute_masks(); - return new; + new } fn calc_block_min(&mut self) { - for i in 0..(self.n + self.k -1) / self.k { - let (min, min_idx) = self.calc_min(i*self.k); + for i in 0..(self.n + self.k - 1) / self.k { + let (min, min_idx) = self.calc_min(i * self.k); self.block_min.push(min); self.block_min_idx.push(min_idx) } @@ -61,41 +61,44 @@ impl PlusMinusOneRMQ { Some(x) => { current_min = min(current_min, *x); min_idx = self.min_idx(min_idx, j); - }, + } None => break, }; } - return (current_min, min_idx); + (current_min, min_idx) } - pub fn get_range_min(&self, start: usize, end: usize) -> Result { + pub fn get_range_min(&self, start: usize, end: usize) -> Result { if start >= end || start >= self.array.len() || end > self.array.len() { return Err("invalid range"); } - let block_l = start / self.k ; - let block_r = (end - 1) / self.k ; + let block_l = start / self.k; + let block_r = (end - 1) / self.k; let l_suffix = self.get_in_block(block_l, start % self.k, self.k - 1); let r_prefix = self.get_in_block(block_r, 0, (end - 1) % self.k); match block_r - block_l { - 0 => return Ok(self.array[self.get_in_block(block_l, start % self.k, (end - 1) % self.k)]), - 1 => return Ok(self.array[self.min_idx(l_suffix, r_prefix)]), - _ => return Ok(self.array[self.min_idx(self.min_idx(l_suffix, self.get_on_blocks(block_l+1, block_r-1)), r_prefix)]), - }; + 0 => Ok(self.array[self.get_in_block(block_l, start % self.k, (end - 1) % self.k)]), + 1 => Ok(self.array[self.min_idx(l_suffix, r_prefix)]), + _ => Ok(self.array[self.min_idx( + self.min_idx(l_suffix, self.get_on_blocks(block_l + 1, block_r - 1)), + r_prefix, + )]), + } } fn get_on_blocks(&self, l: usize, r: usize) -> usize { - let loglen = (r-l+1).ilog2() as usize; + let loglen = (r - l + 1).ilog2() as usize; let idx: usize = ((r as i64) - (1 << loglen as i64) + 1) as usize; - let a = self.sparse_idx[loglen][l as usize]; + let a = self.sparse_idx[loglen][l]; let b = self.sparse_idx[loglen][idx]; - return self.min_idx(a,b); + self.min_idx(a, b) } - fn get_in_block(&self, block_idx: usize, l: usize, r: usize) -> usize { + fn get_in_block(&self, block_idx: usize, l: usize, r: usize) -> usize { let mask = self.block_mask[block_idx]; let min_idx = self.block_rmq[mask as usize][l][r]; - return min_idx + block_idx * self.k; + min_idx + block_idx * self.k } fn fill_block_rmq(&mut self) { @@ -106,26 +109,24 @@ impl PlusMinusOneRMQ { } } - fn rmq_bitmask(&mut self, mask: u32) -> Vec> { - let mut rmq_matrix: Vec> = vec![vec![0;self.k]; self.k]; + fn rmq_bitmask(&mut self, mask: u32) -> Vec> { + let mut rmq_matrix: Vec> = vec![vec![0; self.k]; self.k]; let list = bitmask_to_array(self.k, mask); for i in 0..self.k { for j in i..self.k { if i == j { rmq_matrix[i][j] = i; - } - else { - let min = list[rmq_matrix[i][j-1]]; //Do we want range-minimum or range-maximum + } else { + let min = list[rmq_matrix[i][j - 1]]; //Do we want range-minimum or range-maximum if list[j] < min { rmq_matrix[i][j] = j; - } - else { - rmq_matrix[i][j] = rmq_matrix[i][j-1]; + } else { + rmq_matrix[i][j] = rmq_matrix[i][j - 1]; } } } } - return rmq_matrix; + rmq_matrix } fn precompute_masks(&mut self) { @@ -136,26 +137,27 @@ impl PlusMinusOneRMQ { // we initialize the mask with k-1 ones // this is necessary so if blocks are of size < k the bitmask is still correct - fn calc_bitmask(&self, block_idx: usize) -> u32{ - let mut mask: u32 = (1 << (self.k - 1)) - 1; - for i in self.k*block_idx + 1..self.k * (block_idx + 1) { - let last = self.array[i-1]; + fn calc_bitmask(&self, block_idx: usize) -> u32 { + let mut mask: u32 = (1 << (self.k - 1)) - 1; + for i in self.k * block_idx + 1..self.k * (block_idx + 1) { + let last = self.array[i - 1]; match self.array.get(i) { Some(&x) => { if last >= x { - mask -= 1 << (self.k-1-(i % self.k)); + mask -= 1 << (self.k - 1 - (i % self.k)); } - }, + } None => break, }; - } - return mask; + } + mask } + fn min_idx(&self, i: usize, j: usize) -> usize { if self.array[i] < self.array[j] { return i; } - return j; + j } } @@ -172,12 +174,12 @@ fn min(a: T, b: T) -> T { match a < b { true => a, _ => b, - } + } } fn bitmask_to_array(k: usize, mut mask: u32) -> Vec { let mut list: Vec = vec![0]; - for i in 0..k-1{ + for i in 0..k - 1 { match mask % 2 { 1 => list.push(list[i] - 1), _ => list.push(list[i] + 1), @@ -185,7 +187,7 @@ fn bitmask_to_array(k: usize, mut mask: u32) -> Vec { mask /= 2; } list.reverse(); - return list; + list } #[cfg(test)] @@ -202,5 +204,4 @@ mod tests { assert!(sparse_v1.get_range_min(0, 1000).is_err()); assert!(sparse_v1.get_range_min(1000, 1001).is_err()); } - -} \ No newline at end of file +} From 9d402aa46b7954b76a6f5012946317925d4254a4 Mon Sep 17 00:00:00 2001 From: "arne.prinzler" Date: Fri, 22 Mar 2024 21:24:36 +0100 Subject: [PATCH 7/8] updated DIRECTORY.md --- DIRECTORY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/DIRECTORY.md b/DIRECTORY.md index be77b9ae155..bb7d0848e4a 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -59,6 +59,7 @@ * [Infix To Postfix](https://github.com/TheAlgorithms/Rust/blob/master/src/data_structures/infix_to_postfix.rs) * [Lazy Segment Tree](https://github.com/TheAlgorithms/Rust/blob/master/src/data_structures/lazy_segment_tree.rs) * [Linked List](https://github.com/TheAlgorithms/Rust/blob/master/src/data_structures/linked_list.rs) + * [Plus-Minus-One Range Minimum Query](https://github.com/TheAlgorithms/Rust/blob/master/src/data_structures/plus_minus_rmq.rs) * [Postfix Evaluation](https://github.com/TheAlgorithms/Rust/blob/master/src/data_structures/postfix_evaluation.rs) * Probabilistic * [Bloom Filter](https://github.com/TheAlgorithms/Rust/blob/master/src/data_structures/probabilistic/bloom_filter.rs) From 136c724147730d8f03760dc4609e2a98762271b2 Mon Sep 17 00:00:00 2001 From: "arne.prinzler" Date: Fri, 22 Mar 2024 21:29:12 +0100 Subject: [PATCH 8/8] removed unused struct attribute n --- src/data_structures/plus_minus_rmq.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/data_structures/plus_minus_rmq.rs b/src/data_structures/plus_minus_rmq.rs index 26a0db004c6..33fec6d157d 100644 --- a/src/data_structures/plus_minus_rmq.rs +++ b/src/data_structures/plus_minus_rmq.rs @@ -14,7 +14,6 @@ use std::cmp::PartialOrd; /// used as reference pub struct PlusMinusOneRMQ { array: Vec, - n: usize, k: usize, block_min: Vec, block_min_idx: Vec, @@ -26,12 +25,10 @@ pub struct PlusMinusOneRMQ { impl PlusMinusOneRMQ { pub fn new(mut array: Vec) -> Self { input_padding(&mut array); - let n = array.len() as u32; - let k = n.ilog2() / 2; + let k = (array.len().ilog2() / 2) as usize; let mut new = Self { array, - n: n as usize, - k: k as usize, + k, block_min: Vec::new(), block_min_idx: Vec::new(), sparse_idx: vec![Vec::new()], // is a sparse table, which only stores the indeces @@ -46,7 +43,7 @@ impl PlusMinusOneRMQ { new } fn calc_block_min(&mut self) { - for i in 0..(self.n + self.k - 1) / self.k { + for i in 0..(self.array.len() + self.k - 1) / self.k { let (min, min_idx) = self.calc_min(i * self.k); self.block_min.push(min); self.block_min_idx.push(min_idx)