Skip to content

Commit

Permalink
feat: add some fn
Browse files Browse the repository at this point in the history
Signed-off-by: feathercyc <[email protected]>
  • Loading branch information
GG2002 committed Jul 11, 2024
1 parent daa5a9c commit 77f1c81
Show file tree
Hide file tree
Showing 16 changed files with 2,202 additions and 509 deletions.
64 changes: 64 additions & 0 deletions .github/workflows/pr_check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
name: PR Checks

on:
push:
branches: [main]
pull_request:
types: [opened, synchronize, reopened]

jobs:
test:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Install Rust toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: stable
componets: clippy
override: true

- name: Make script executable
run: chmod +x ci/scripts/check-trailing-spaces.sh

- name: Trailing spaces check
run: ci/scripts/check-trailing-spaces.sh

- name: Audit
run: cargo audit

- name: Format
run: cargo fmt --all -- --check

- name: Clippy
run: cargo clippy --all-targets --all-features -- -D warnings

- name: Test
run: cargo test --verbose

commit:
name: Commit Message Validation
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- run: git show-ref
- uses: actions-rs/[email protected]
with:
crate: git-cz
version: latest
- name: Validate commit messages
run: git-cz check ${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }}

spell-check:
name: Spell Check
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Check Spelling
uses: crate-ci/[email protected]
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 7 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,20 @@ license = "Apache-2.0"
keywords = ["Interval Tree", "Augmented Tree", "Red-Black Tree"]

[dependencies]
serde = { version = "1.0", default-features = false, features = [
"derive",
"std",
], optional = true }

[dev-dependencies]
criterion = "0.5.1"
rand = "0.8.5"
serde_json = "1.0"

[features]
default = []
interval_tree_find_overlap_ordered = []
graphviz = []
serde = ["dep:serde"]

[[bench]]
name = "bench"
Expand Down
21 changes: 11 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ The implementation of the interval tree in interval_map references "Introduction
To safely and efficiently handle insertion and deletion operations in Rust, `interval_map` innovatively **uses arrays to simulate pointers** for managing the parent-child references in the red-black tree. This approach also ensures that interval_map has the `Send` and `Unpin` traits, allowing it to be safely transferred between threads and to maintain a fixed memory location during asynchronous operations.

`interval_map` implements an `IntervalMap` struct:
- It accepts `Interval<T>` as the key, where `T` can be any type that implements `Ord+Clone` trait. Therefore, intervals such as $[1, 2)$ and $["aaa", "bbb")$ are allowed
- It accepts `Interval<T>` as the key, where `T` can be any type that implements `Ord` trait. Therefore, intervals such as $[1, 2)$ and $["aaa", "bbb")$ are allowed
- The value can be of any type

`interval_map` supports `insert`, `delete`, and `iter` fns. Traversal is performed in the order of `Interval<T>` . For instance, with intervals of type `Interval<u32>`:
Expand All @@ -22,15 +22,16 @@ Currently, `interval_map` only supports half-open intervals, i.e., $[...,...)$.

The benchmark was conducted on a platform with `AMD R7 7840H + DDR5 5600MHz`. The result are as follows:
1. Only insert
| insert | 100 | 1000 | 10, 000 | 100, 000 |
| --------------- | --------- | --------- | --------- | --------- |
| Time per insert | 5.4168 µs | 80.518 µs | 2.2823 ms | 36.528 ms |
| insert | 100 | 1000 | 10, 000 | 100, 000 |
| ---------- | --------- | --------- | --------- | --------- |
| Total time | 5.4168 µs | 80.518 µs | 2.2823 ms | 36.528 ms |
2. Insert N and remove N
| insert_and_remove | 100 | 1000 | 10, 000 | 100, 000 |
| ------------------ | --------- | --------- | --------- | --------- |
| Time per operation | 10.333 µs | 223.43 µs | 4.9358 ms | 81.634 ms |
| insert_and_remove | 100 | 1000 | 10, 000 | 100, 000 |
| ----------------- | --------- | --------- | --------- | --------- |
| Total time | 10.333 µs | 223.43 µs | 4.9358 ms | 81.634 ms |

## TODO
- [] Support for $(...,...)$, $[...,...]$ and $(...,...]$ interval types.
- [] Add more tests like [etcd](https://github.com/etcd-io/etcd/blob/main/pkg/adt/interval_tree_test.go)
- [] Add Point type for Interval
- [ ] ~~Support for $(...,...)$, $[...,...]$ and $(...,...]$ interval types.~~ There's no way to support these interval type without performance loss now.
- [ ] ~~Add Point type for Interval~~ To support Point type, it should also support $[...,...]$, so it couldn't be supported now, either. But you could write code like [examples/new_point](examples/new_point.rs).
- [x] Add more tests like [etcd](https://github.com/etcd-io/etcd/blob/main/pkg/adt/interval_tree_test.go).
- [x] Refine iter mod.
101 changes: 68 additions & 33 deletions benches/bench.rs
Original file line number Diff line number Diff line change
@@ -1,44 +1,25 @@
use criterion::{criterion_group, criterion_main, Bencher, Criterion};
use interval_map::{Interval, IntervalMap};
use rand::{rngs::StdRng, Rng, SeedableRng};
use std::hint::black_box;

struct Rng {
state: u32,
}
impl Rng {
fn new() -> Self {
Self { state: 0x87654321 }
}

fn gen_u32(&mut self) -> u32 {
self.state ^= self.state << 13;
self.state ^= self.state >> 17;
self.state ^= self.state << 5;
self.state
}

fn gen_range_i32(&mut self, low: i32, high: i32) -> i32 {
let d = (high - low) as u32;
low + (self.gen_u32() % d) as i32
}
}

struct IntervalGenerator {
rng: Rng,
limit: i32,
rng: StdRng,
}
impl IntervalGenerator {
fn new() -> Self {
const LIMIT: i32 = 100000;
let seed: [u8; 32] = [
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30, 31,
];
Self {
rng: Rng::new(),
limit: LIMIT,
rng: StdRng::from_seed(seed),
}
}

fn next(&mut self) -> Interval<i32> {
let low = self.rng.gen_range_i32(0, self.limit - 1);
let high = self.rng.gen_range_i32(low + 1, self.limit);
fn next(&mut self) -> Interval<u32> {
let low = self.rng.gen();
let high = self.rng.gen_range(low + 1..=u32::MAX);
Interval::new(low, high)
}
}
Expand All @@ -65,7 +46,7 @@ fn interval_map_insert_remove(count: usize, bench: &mut Bencher) {
black_box(map.insert(i, ()));
}
for i in &intervals {
black_box(map.remove(&i));
black_box(map.remove(i));
}
});
}
Expand Down Expand Up @@ -100,14 +81,68 @@ fn bench_interval_map_insert_remove(c: &mut Criterion) {
});
}

// FilterIter helper fn
fn interval_map_filter_iter(count: usize, bench: &mut Bencher) {
let mut gen = IntervalGenerator::new();
let intervals: Vec<_> = std::iter::repeat_with(|| gen.next()).take(count).collect();
let mut map = IntervalMap::new();
for i in intervals.clone() {
map.insert(i, ());
}
bench.iter(|| {
for i in intervals.clone() {
black_box(map.filter_iter(&i).collect::<Vec<_>>());
}
});
}

// iter().filter() helper fn
fn interval_map_iter_filter(count: usize, bench: &mut Bencher) {
let mut gen = IntervalGenerator::new();
let intervals: Vec<_> = std::iter::repeat_with(|| gen.next()).take(count).collect();
let mut map = IntervalMap::new();
for i in intervals.clone() {
map.insert(i, ());
}
bench.iter(|| {
for i in intervals.clone() {
black_box(map.iter().filter(|v| v.0.overlap(&i)).collect::<Vec<_>>());
}
});
}

fn bench_interval_map_filter_iter(c: &mut Criterion) {
c.bench_function("bench_interval_map_filter_iter_100", |b| {
interval_map_filter_iter(100, b)
});
c.bench_function("bench_interval_map_filter_iter_1000", |b| {
interval_map_filter_iter(1000, b)
});
}

fn bench_interval_map_iter_filter(c: &mut Criterion) {
c.bench_function("bench_interval_map_iter_filter_100", |b| {
interval_map_iter_filter(100, b)
});
c.bench_function("bench_interval_map_iter_filter_1000", |b| {
interval_map_iter_filter(1000, b)
});
}

fn criterion_config() -> Criterion {
Criterion::default().configure_from_args().without_plots()
}

criterion_group! {
name = benches;
name = benches_basic_op;
config = criterion_config();
targets = bench_interval_map_insert, bench_interval_map_insert_remove,
}

criterion_group! {
name = benches_iter;
config = criterion_config();
targets = bench_interval_map_insert, bench_interval_map_insert_remove
targets = bench_interval_map_filter_iter, bench_interval_map_iter_filter
}

criterion_main!(benches);
criterion_main!(benches_basic_op, benches_iter);
74 changes: 74 additions & 0 deletions ci/scripts/check-trailing-spaces.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#!/usr/bin/env bash

# Script Author: https://github.com/stdrc
# Repo: https://github.com/risingwavelabs/risingwave/blame/main/scripts/check/check-trailing-spaces.sh

# Exits as soon as any line fails.
set -euo pipefail

self=$0

# Shell colors
RED='\033[0;31m'
BLUE='\033[0;34m'
GREEN='\033[0;32m'
ORANGE='\033[0;33m'
BOLD='\033[1m'
NONE='\033[0m'

print_help() {
echo "Usage: $self [-f|--fix]"
echo
echo "Options:"
echo " -f, --fix Fix trailing spaces."
echo " -h, --help Show this help message and exit."
}

fix=false
while [ $# -gt 0 ]; do
case $1 in
-f | --fix)
fix=true
;;
-h | --help)
print_help
exit 0
;;
*)
echo -e "${RED}${BOLD}$self: invalid option \`$1\`\n${NONE}"
print_help
exit 1
;;
esac
shift
done

temp_file=$(mktemp)

echo -ne "${BLUE}"
git config --global --add safe.directory '*'
git grep -nIP --untracked '[[:space:]]+$' | tee $temp_file || true
echo -ne "${NONE}"

bad_files=$(cat $temp_file | cut -f1 -d ':' | sort -u)
rm $temp_file

if [ ! -z "$bad_files" ]; then
if [[ $fix == true ]]; then
for file in $bad_files; do
sed -i '' -e's/[[:space:]]*$//' "$file"
done

echo
echo -e "${GREEN}${BOLD}All trailing spaces listed above have been cleaned.${NONE}"
exit 0
else
echo
echo -e "${RED}${BOLD}Please clean all the trailing spaces listed above.${NONE}"
echo -e "${BOLD}You can run '$self --fix' for convenience.${NONE}"
exit 1
fi
else
echo -e "${GREEN}${BOLD}No trailing spaces found.${NONE}"
exit 0
fi
27 changes: 27 additions & 0 deletions examples/new_point.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
use interval_map::{Interval, IntervalMap};

trait Point<T> {
fn new_point(x: T) -> Interval<T>;
}

impl Point<u32> for Interval<u32> {
fn new_point(x: u32) -> Self {
Interval::new(x, x + 1)
}
}

fn main() {
let mut interval_map = IntervalMap::<u32, i32>::new();
interval_map.insert(Interval::new(3, 7), 20);
interval_map.insert(Interval::new(2, 6), 15);

let tmp_point = Interval::new_point(5);
assert_eq!(tmp_point, Interval::new(5, 6));

interval_map.insert(tmp_point.clone(), 10);
assert_eq!(interval_map.get(&tmp_point).unwrap(), &10);
assert_eq!(
interval_map.find_all_overlap(&Interval::new_point(5)).len(),
3
);
}
Loading

0 comments on commit 77f1c81

Please sign in to comment.