diff --git a/lab4/zad4/.gitignore b/lab4/zad4/.gitignore new file mode 100644 index 0000000..45568ce --- /dev/null +++ b/lab4/zad4/.gitignore @@ -0,0 +1,3 @@ +target +results +plots diff --git a/lab4/zad4/Cargo.lock b/lab4/zad4/Cargo.lock new file mode 100644 index 0000000..6105ce8 --- /dev/null +++ b/lab4/zad4/Cargo.lock @@ -0,0 +1,285 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "bitflags" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "dashmap" +version = "5.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +dependencies = [ + "cfg-if", + "hashbrown", + "lock_api", + "once_cell", + "parking_lot_core", +] + +[[package]] +name = "either" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]] +name = "libc" +version = "0.2.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" + +[[package]] +name = "libgen" +version = "0.1.0" +dependencies = [ + "rand", +] + +[[package]] +name = "libtree" +version = "0.1.0" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "redox_syscall" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" +dependencies = [ + "bitflags", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "windows-targets" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" + +[[package]] +name = "zad4" +version = "0.1.0" +dependencies = [ + "dashmap", + "libgen", + "libtree", + "rayon", +] diff --git a/lab4/zad4/Cargo.toml b/lab4/zad4/Cargo.toml new file mode 100644 index 0000000..a434ff7 --- /dev/null +++ b/lab4/zad4/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "zad4" +version = "0.1.0" +edition = "2021" + +[dependencies] +libtree = { path = "../../libtree" } +libgen = { path = "../../libgen" } +rayon = "1.10.0" +dashmap = "5.5.3" diff --git a/lab4/zad4/gnuplot/asc_comp.gp b/lab4/zad4/gnuplot/asc_comp.gp new file mode 100644 index 0000000..d19a54e --- /dev/null +++ b/lab4/zad4/gnuplot/asc_comp.gp @@ -0,0 +1,14 @@ +set title "Insert n asc, delete n rand - Comp, n ∈ {10000, 20000, ..., 100000}" +set style data lines + +set xlabel "n" +set ylabel "comparisons" +set term pdfcairo font "JetBrainsMono-NF,12" + +plot "./results/asc_comp" \ +using 1:2 t "Average", \ +"" using 1:3 t "Max", \ + +set terminal pdf + +# vim: ft=gnuplot diff --git a/lab4/zad4/gnuplot/asc_height.gp b/lab4/zad4/gnuplot/asc_height.gp new file mode 100644 index 0000000..31b220b --- /dev/null +++ b/lab4/zad4/gnuplot/asc_height.gp @@ -0,0 +1,14 @@ +set title "Insert n asc, delete n rand - Height, n ∈ {10000, 20000, ..., 100000}" +set style data lines + +set xlabel "n" +set ylabel "height" +set term pdfcairo font "JetBrainsMono-NF,12" + +plot "./results/asc_height" \ +using 1:2 t "Average", \ +"" using 1:3 t "Max", \ + +set terminal pdf + +# vim: ft=gnuplot diff --git a/lab4/zad4/gnuplot/asc_read.gp b/lab4/zad4/gnuplot/asc_read.gp new file mode 100644 index 0000000..e8f8b45 --- /dev/null +++ b/lab4/zad4/gnuplot/asc_read.gp @@ -0,0 +1,14 @@ +set title "Insert n asc, delete n rand - Reads, n ∈ {10000, 20000, ..., 100000}" +set style data lines + +set xlabel "n" +set ylabel "reads" +set term pdfcairo font "JetBrainsMono-NF,12" + +plot "./results/asc_read" \ +using 1:2 t "Average", \ +"" using 1:3 t "Max", \ + +set terminal pdf + +# vim: ft=gnuplot diff --git a/lab4/zad4/gnuplot/asc_write.gp b/lab4/zad4/gnuplot/asc_write.gp new file mode 100644 index 0000000..40bdeb3 --- /dev/null +++ b/lab4/zad4/gnuplot/asc_write.gp @@ -0,0 +1,14 @@ +set title "Insert n asc, delete n rand - Writes, n ∈ {10000, 20000, ..., 100000}" +set style data lines + +set xlabel "n" +set ylabel "writes" +set term pdfcairo font "JetBrainsMono-NF,12" + +plot "./results/asc_write" \ +using 1:2 t "Average", \ +"" using 1:3 t "Max", \ + +set terminal pdf + +# vim: ft=gnuplot diff --git a/lab4/zad4/gnuplot/rand_comp.gp b/lab4/zad4/gnuplot/rand_comp.gp new file mode 100644 index 0000000..706e4ac --- /dev/null +++ b/lab4/zad4/gnuplot/rand_comp.gp @@ -0,0 +1,14 @@ +set title "Insert n rand, delete n rand - Comp, n ∈ {10000, 20000, ..., 100000}" +set style data lines + +set xlabel "n" +set ylabel "comparisons" +set term pdfcairo font "JetBrainsMono-NF,12" + +plot "./results/rand_comp" \ +using 1:2 t "Average", \ +"" using 1:3 t "Max", \ + +set terminal pdf + +# vim: ft=gnuplot diff --git a/lab4/zad4/gnuplot/rand_height.gp b/lab4/zad4/gnuplot/rand_height.gp new file mode 100644 index 0000000..215c0e9 --- /dev/null +++ b/lab4/zad4/gnuplot/rand_height.gp @@ -0,0 +1,14 @@ +set title "Insert n rand, delete n rand - height, n ∈ {10000, 20000, ..., 100000}" +set style data lines + +set xlabel "n" +set ylabel "height" +set term pdfcairo font "JetBrainsMono-NF,12" + +plot "./results/rand_height" \ +using 1:2 t "Average", \ +"" using 1:3 t "Max", \ + +set terminal pdf + +# vim: ft=gnuplot diff --git a/lab4/zad4/gnuplot/rand_read.gp b/lab4/zad4/gnuplot/rand_read.gp new file mode 100644 index 0000000..f6d6a65 --- /dev/null +++ b/lab4/zad4/gnuplot/rand_read.gp @@ -0,0 +1,14 @@ +set title "Insert n rand, delete n rand - Reads, n ∈ {10000, 20000, ..., 100000}" +set style data lines + +set xlabel "n" +set ylabel "reads" +set term pdfcairo font "JetBrainsMono-NF,12" + +plot "./results/rand_read" \ +using 1:2 t "Average", \ +"" using 1:3 t "Max", \ + +set terminal pdf + +# vim: ft=gnuplot diff --git a/lab4/zad4/gnuplot/rand_write.gp b/lab4/zad4/gnuplot/rand_write.gp new file mode 100644 index 0000000..7ec5cd5 --- /dev/null +++ b/lab4/zad4/gnuplot/rand_write.gp @@ -0,0 +1,14 @@ +set title "Insert n rand, delete n rand - Writes, n ∈ {10000, 20000, ..., 100000}" +set style data lines + +set xlabel "n" +set ylabel "writes" +set term pdfcairo font "JetBrainsMono-NF,12" + +plot "./results/rand_write" \ +using 1:2 t "Average", \ +"" using 1:3 t "Max", \ + +set terminal pdf + +# vim: ft=gnuplot diff --git a/lab4/zad4/src/main.rs b/lab4/zad4/src/main.rs new file mode 100644 index 0000000..9191b58 --- /dev/null +++ b/lab4/zad4/src/main.rs @@ -0,0 +1,286 @@ +use std::{fs::{self, OpenOptions}, io::{self, Write}, sync::Arc}; + +use dashmap::DashMap; +use libgen::{gen_asc, gen_rand}; +use rayon::{current_thread_index, iter::{IntoParallelIterator, ParallelIterator}}; +use libtree::{RedBlackTree, Stats}; + +fn comp_avg(results_map: &DashMap>) -> DashMap { + results_map.iter() + .map(|ref_multi| { + let (i, results) = ref_multi.pair(); + (*i, (results.iter() + .map(|res| res.comparisons) + .sum::() as f64 / results.len() as f64)) + }) + .collect() +} + +fn comp_max(results_map: &DashMap>) -> DashMap { + results_map.iter() + .map(|ref_multi| { + let (i, results) = ref_multi.pair(); + (*i, results.iter() + .map(|s| s.comparisons) + .max() + .unwrap()) + }) + .collect() +} + +fn read_avg(results_map: &DashMap>) -> DashMap { + results_map.iter() + .map(|ref_multi| { + let (i, results) = ref_multi.pair(); + (*i, (results.iter() + .map(|res| res.reads) + .sum::() as f64 / results.len() as f64)) + }) + .collect() +} + +fn read_max(results_map: &DashMap>) -> DashMap { + results_map.iter() + .map(|ref_multi| { + let (i, results) = ref_multi.pair(); + (*i, results.iter() + .map(|s| s.reads) + .max() + .unwrap()) + }) + .collect() +} + +fn write_avg(results_map: &DashMap>) -> DashMap { + results_map.iter() + .map(|ref_multi| { + let (i, results) = ref_multi.pair(); + (*i, (results.iter() + .map(|res| res.writes) + .sum::() as f64 / results.len() as f64)) + }) + .collect() +} + +fn write_max(results_map: &DashMap>) -> DashMap { + results_map.iter() + .map(|ref_multi| { + let (i, results) = ref_multi.pair(); + (*i, results.iter() + .map(|s| s.writes) + .max() + .unwrap()) + }) + .collect() +} + +fn main() -> io::Result<()> { + + let m = 20; + + let asc_results: Arc>> = Arc::new(DashMap::new()); + let asc_heights: Arc>> = Arc::new(DashMap::new()); + let rand_results: Arc>> = Arc::new(DashMap::new()); + let rand_heights: Arc>> = Arc::new(DashMap::new()); + + let asc_results_clone = Arc::clone(&asc_results); + let asc_heights_clone = Arc::clone(&asc_heights); + let rand_results_clone = Arc::clone(&rand_results); + let rand_heights_clone = Arc::clone(&rand_heights); + + (10000u64..=100000u64).step_by(10000).collect::>().into_par_iter().for_each(move |n| { + if asc_results_clone.get(&n).is_some() { + return; + } + if asc_heights_clone.get(&n).is_some() { + return; + } + if rand_results_clone.get(&n).is_some() { + return; + } + if rand_heights_clone.get(&n).is_some() { + return; + } + println!("{thread}: starting n: {n}", thread = current_thread_index().unwrap()); + + asc_results_clone.insert(n, vec![]); + asc_heights_clone.insert(n, vec![]); + rand_results_clone.insert(n, vec![]); + rand_heights_clone.insert(n, vec![]); + + (1..=m).into_par_iter().for_each(|k| { + println!("{thread}: starting iteration {k}/{m} for n: {n}", thread = current_thread_index().unwrap()); + let mut asc = RedBlackTree::new(n as usize); + + for i in gen_asc(n) { + asc.insert(i); + asc_results_clone.get_mut(&n).unwrap().push(asc.stats.clone()); + asc.reset_stats(); + asc_heights_clone.get_mut(&n).unwrap().push(asc.height()); + } + for i in gen_rand(n) { + asc.delete(i); + asc_results_clone.get_mut(&n).unwrap().push(asc.stats.clone()); + asc.reset_stats(); + asc_heights_clone.get_mut(&n).unwrap().push(asc.height()); + } + + let mut rand = RedBlackTree::new(n as usize); + + for i in gen_rand(n) { + rand.insert(i); + rand_results_clone.get_mut(&n).unwrap().push(rand.stats.clone()); + rand.reset_stats(); + rand_heights_clone.get_mut(&n).unwrap().push(rand.height()); + } + for i in gen_rand(n) { + rand.delete(i); + rand_results_clone.get_mut(&n).unwrap().push(rand.stats.clone()); + rand.reset_stats(); + rand_heights_clone.get_mut(&n).unwrap().push(rand.height()); + } + + rand_results_clone.get_mut(&n).unwrap().push(rand.stats.clone()); + println!("{thread}: finished iteration {k}/{m} for n: {n}", thread = current_thread_index().unwrap()); + }); + println!("{thread}: finished n: {n}", thread = current_thread_index().unwrap()); + }); + + let asc_comp_averages = comp_avg(&asc_results); + let asc_read_averages = read_avg(&asc_results); + let asc_write_averages = write_avg(&asc_results); + + let asc_height_averages: DashMap<_, _> = asc_heights.iter() + .map(|ref_multi| { + let (i, results) = ref_multi.pair(); + (*i, results.iter().sum::() as f64 / results.len() as f64) + }) + .collect(); + + let asc_comp_max = comp_max(&asc_results); + let asc_read_max = read_max(&asc_results); + let asc_write_max = write_max(&asc_results); + + let asc_height_max: DashMap<_, _> = asc_heights.iter() + .map(|ref_multi| { + let (i, results) = ref_multi.pair(); + (*i, *results.iter().max().unwrap()) + }) + .collect(); + + let rand_comp_averages = comp_avg(&rand_results); + let rand_read_averages = read_avg(&rand_results); + let rand_write_averages = write_avg(&rand_results); + + let rand_height_averages: DashMap<_, _> = rand_heights.iter() + .map(|ref_multi| { + let (i, results) = ref_multi.pair(); + (*i, results.iter().sum::() as f64 / results.len() as f64) + }) + .collect(); + + let rand_comp_max = comp_max(&rand_results); + let rand_read_max = read_max(&rand_results); + let rand_write_max = write_max(&rand_results); + + let rand_height_max: DashMap<_, _> = rand_heights.iter() + .map(|ref_multi| { + let (i, results) = ref_multi.pair(); + (*i, *results.iter().max().unwrap()) + }) + .collect(); + + _ = fs::create_dir_all("./results"); + + let mut asc_comp_file = OpenOptions::new() + .create(true) + .write(true) + .truncate(true) + .open("./results/asc_comp")?; + + let mut asc_read_file = OpenOptions::new() + .create(true) + .write(true) + .truncate(true) + .open("./results/asc_read")?; + + let mut asc_write_file = OpenOptions::new() + .create(true) + .write(true) + .truncate(true) + .open("./results/asc_write")?; + + let mut asc_height_file = OpenOptions::new() + .create(true) + .write(true) + .truncate(true) + .open("./results/asc_height")?; + + let mut rand_comp_file = OpenOptions::new() + .create(true) + .write(true) + .truncate(true) + .open("./results/rand_comp")?; + + let mut rand_read_file = OpenOptions::new() + .create(true) + .write(true) + .truncate(true) + .open("./results/rand_read")?; + + let mut rand_write_file = OpenOptions::new() + .create(true) + .write(true) + .truncate(true) + .open("./results/rand_write")?; + + let mut rand_height_file = OpenOptions::new() + .create(true) + .write(true) + .truncate(true) + .open("./results/rand_height")?; + + for n in (10000u64..=100000u64).step_by(10000) { + writeln!(asc_comp_file, "{n} {} {}", + asc_comp_averages.get(&n).unwrap().value(), + asc_comp_max.get(&n).unwrap().value(), + )?; + + writeln!(asc_read_file, "{n} {} {}", + asc_read_averages.get(&n).unwrap().value(), + asc_read_max.get(&n).unwrap().value(), + )?; + + writeln!(asc_write_file, "{n} {} {}", + asc_write_averages.get(&n).unwrap().value(), + asc_write_max.get(&n).unwrap().value(), + )?; + + writeln!(asc_height_file, "{n} {} {}", + asc_height_averages.get(&n).unwrap().value(), + asc_height_max.get(&n).unwrap().value(), + )?; + + writeln!(rand_comp_file, "{n} {} {}", + rand_comp_averages.get(&n).unwrap().value(), + rand_comp_max.get(&n).unwrap().value(), + )?; + + writeln!(rand_read_file, "{n} {} {}", + rand_read_averages.get(&n).unwrap().value(), + rand_read_max.get(&n).unwrap().value(), + )?; + + writeln!(rand_write_file, "{n} {} {}", + rand_write_averages.get(&n).unwrap().value(), + rand_write_max.get(&n).unwrap().value(), + )?; + + writeln!(rand_height_file, "{n} {} {}", + rand_height_averages.get(&n).unwrap().value(), + rand_height_max.get(&n).unwrap().value(), + )?; + } + + Ok(()) +} diff --git a/lab4/zad5/.gitignore b/lab4/zad5/.gitignore new file mode 100644 index 0000000..eb5a316 --- /dev/null +++ b/lab4/zad5/.gitignore @@ -0,0 +1 @@ +target diff --git a/lab4/zad5/Cargo.lock b/lab4/zad5/Cargo.lock new file mode 100644 index 0000000..8739f7f --- /dev/null +++ b/lab4/zad5/Cargo.lock @@ -0,0 +1,87 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "libc" +version = "0.2.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" + +[[package]] +name = "libgen" +version = "0.1.0" +dependencies = [ + "rand", +] + +[[package]] +name = "libtree" +version = "0.1.0" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "zad5" +version = "0.1.0" +dependencies = [ + "libgen", + "libtree", +] diff --git a/lab4/zad5/Cargo.toml b/lab4/zad5/Cargo.toml new file mode 100644 index 0000000..2bc77df --- /dev/null +++ b/lab4/zad5/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "zad5" +version = "0.1.0" +edition = "2021" + +[dependencies] +libgen = { path = "../../libgen" } +libtree = { path = "../../libtree" } diff --git a/lab4/zad5/src/main.rs b/lab4/zad5/src/main.rs new file mode 100644 index 0000000..30dfce0 --- /dev/null +++ b/lab4/zad5/src/main.rs @@ -0,0 +1,34 @@ +use libgen::{gen_asc, gen_rand}; +use libtree::SplayTree; + +fn main() { + println!("1. insert 50 ascending; delete 50 random"); + let mut tree1 = SplayTree::new(50); + + for i in gen_asc(50) { + println!("insert {i}"); + tree1.insert(i); + println!("{}", tree1); + } + + for i in gen_rand(50) { + println!("delete {i}"); + tree1.delete(i); + println!("{}", tree1); + } + + println!("2. insert 50 random; delete 50 random"); + let mut tree2 = SplayTree::new(50); + + for i in gen_rand(50) { + println!("insert {i}"); + tree2.insert(i); + println!("{}", tree2); + } + + for i in gen_rand(50) { + println!("delete {i}"); + tree2.delete(i); + println!("{}", tree2); + } +} diff --git a/lab4/zad6/.gitignore b/lab4/zad6/.gitignore new file mode 100644 index 0000000..45568ce --- /dev/null +++ b/lab4/zad6/.gitignore @@ -0,0 +1,3 @@ +target +results +plots diff --git a/lab4/zad6/Cargo.lock b/lab4/zad6/Cargo.lock new file mode 100644 index 0000000..6105ce8 --- /dev/null +++ b/lab4/zad6/Cargo.lock @@ -0,0 +1,285 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "bitflags" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "dashmap" +version = "5.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +dependencies = [ + "cfg-if", + "hashbrown", + "lock_api", + "once_cell", + "parking_lot_core", +] + +[[package]] +name = "either" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]] +name = "libc" +version = "0.2.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" + +[[package]] +name = "libgen" +version = "0.1.0" +dependencies = [ + "rand", +] + +[[package]] +name = "libtree" +version = "0.1.0" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "redox_syscall" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" +dependencies = [ + "bitflags", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "windows-targets" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" + +[[package]] +name = "zad4" +version = "0.1.0" +dependencies = [ + "dashmap", + "libgen", + "libtree", + "rayon", +] diff --git a/lab4/zad6/Cargo.toml b/lab4/zad6/Cargo.toml new file mode 100644 index 0000000..a434ff7 --- /dev/null +++ b/lab4/zad6/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "zad4" +version = "0.1.0" +edition = "2021" + +[dependencies] +libtree = { path = "../../libtree" } +libgen = { path = "../../libgen" } +rayon = "1.10.0" +dashmap = "5.5.3" diff --git a/lab4/zad6/gnuplot/asc_comp.gp b/lab4/zad6/gnuplot/asc_comp.gp new file mode 100644 index 0000000..d19a54e --- /dev/null +++ b/lab4/zad6/gnuplot/asc_comp.gp @@ -0,0 +1,14 @@ +set title "Insert n asc, delete n rand - Comp, n ∈ {10000, 20000, ..., 100000}" +set style data lines + +set xlabel "n" +set ylabel "comparisons" +set term pdfcairo font "JetBrainsMono-NF,12" + +plot "./results/asc_comp" \ +using 1:2 t "Average", \ +"" using 1:3 t "Max", \ + +set terminal pdf + +# vim: ft=gnuplot diff --git a/lab4/zad6/gnuplot/asc_height.gp b/lab4/zad6/gnuplot/asc_height.gp new file mode 100644 index 0000000..31b220b --- /dev/null +++ b/lab4/zad6/gnuplot/asc_height.gp @@ -0,0 +1,14 @@ +set title "Insert n asc, delete n rand - Height, n ∈ {10000, 20000, ..., 100000}" +set style data lines + +set xlabel "n" +set ylabel "height" +set term pdfcairo font "JetBrainsMono-NF,12" + +plot "./results/asc_height" \ +using 1:2 t "Average", \ +"" using 1:3 t "Max", \ + +set terminal pdf + +# vim: ft=gnuplot diff --git a/lab4/zad6/gnuplot/asc_read.gp b/lab4/zad6/gnuplot/asc_read.gp new file mode 100644 index 0000000..e8f8b45 --- /dev/null +++ b/lab4/zad6/gnuplot/asc_read.gp @@ -0,0 +1,14 @@ +set title "Insert n asc, delete n rand - Reads, n ∈ {10000, 20000, ..., 100000}" +set style data lines + +set xlabel "n" +set ylabel "reads" +set term pdfcairo font "JetBrainsMono-NF,12" + +plot "./results/asc_read" \ +using 1:2 t "Average", \ +"" using 1:3 t "Max", \ + +set terminal pdf + +# vim: ft=gnuplot diff --git a/lab4/zad6/gnuplot/asc_write.gp b/lab4/zad6/gnuplot/asc_write.gp new file mode 100644 index 0000000..40bdeb3 --- /dev/null +++ b/lab4/zad6/gnuplot/asc_write.gp @@ -0,0 +1,14 @@ +set title "Insert n asc, delete n rand - Writes, n ∈ {10000, 20000, ..., 100000}" +set style data lines + +set xlabel "n" +set ylabel "writes" +set term pdfcairo font "JetBrainsMono-NF,12" + +plot "./results/asc_write" \ +using 1:2 t "Average", \ +"" using 1:3 t "Max", \ + +set terminal pdf + +# vim: ft=gnuplot diff --git a/lab4/zad6/gnuplot/rand_comp.gp b/lab4/zad6/gnuplot/rand_comp.gp new file mode 100644 index 0000000..706e4ac --- /dev/null +++ b/lab4/zad6/gnuplot/rand_comp.gp @@ -0,0 +1,14 @@ +set title "Insert n rand, delete n rand - Comp, n ∈ {10000, 20000, ..., 100000}" +set style data lines + +set xlabel "n" +set ylabel "comparisons" +set term pdfcairo font "JetBrainsMono-NF,12" + +plot "./results/rand_comp" \ +using 1:2 t "Average", \ +"" using 1:3 t "Max", \ + +set terminal pdf + +# vim: ft=gnuplot diff --git a/lab4/zad6/gnuplot/rand_height.gp b/lab4/zad6/gnuplot/rand_height.gp new file mode 100644 index 0000000..215c0e9 --- /dev/null +++ b/lab4/zad6/gnuplot/rand_height.gp @@ -0,0 +1,14 @@ +set title "Insert n rand, delete n rand - height, n ∈ {10000, 20000, ..., 100000}" +set style data lines + +set xlabel "n" +set ylabel "height" +set term pdfcairo font "JetBrainsMono-NF,12" + +plot "./results/rand_height" \ +using 1:2 t "Average", \ +"" using 1:3 t "Max", \ + +set terminal pdf + +# vim: ft=gnuplot diff --git a/lab4/zad6/gnuplot/rand_read.gp b/lab4/zad6/gnuplot/rand_read.gp new file mode 100644 index 0000000..f6d6a65 --- /dev/null +++ b/lab4/zad6/gnuplot/rand_read.gp @@ -0,0 +1,14 @@ +set title "Insert n rand, delete n rand - Reads, n ∈ {10000, 20000, ..., 100000}" +set style data lines + +set xlabel "n" +set ylabel "reads" +set term pdfcairo font "JetBrainsMono-NF,12" + +plot "./results/rand_read" \ +using 1:2 t "Average", \ +"" using 1:3 t "Max", \ + +set terminal pdf + +# vim: ft=gnuplot diff --git a/lab4/zad6/gnuplot/rand_write.gp b/lab4/zad6/gnuplot/rand_write.gp new file mode 100644 index 0000000..7ec5cd5 --- /dev/null +++ b/lab4/zad6/gnuplot/rand_write.gp @@ -0,0 +1,14 @@ +set title "Insert n rand, delete n rand - Writes, n ∈ {10000, 20000, ..., 100000}" +set style data lines + +set xlabel "n" +set ylabel "writes" +set term pdfcairo font "JetBrainsMono-NF,12" + +plot "./results/rand_write" \ +using 1:2 t "Average", \ +"" using 1:3 t "Max", \ + +set terminal pdf + +# vim: ft=gnuplot diff --git a/lab4/zad6/src/main.rs b/lab4/zad6/src/main.rs new file mode 100644 index 0000000..b64c34e --- /dev/null +++ b/lab4/zad6/src/main.rs @@ -0,0 +1,286 @@ +use std::{fs::{self, OpenOptions}, io::{self, Write}, sync::Arc}; + +use dashmap::DashMap; +use libgen::{gen_asc, gen_rand}; +use rayon::{current_thread_index, iter::{IntoParallelIterator, ParallelIterator}}; +use libtree::{SplayTree, Stats}; + +fn comp_avg(results_map: &DashMap>) -> DashMap { + results_map.iter() + .map(|ref_multi| { + let (i, results) = ref_multi.pair(); + (*i, (results.iter() + .map(|res| res.comparisons) + .sum::() as f64 / results.len() as f64)) + }) + .collect() +} + +fn comp_max(results_map: &DashMap>) -> DashMap { + results_map.iter() + .map(|ref_multi| { + let (i, results) = ref_multi.pair(); + (*i, results.iter() + .map(|s| s.comparisons) + .max() + .unwrap()) + }) + .collect() +} + +fn read_avg(results_map: &DashMap>) -> DashMap { + results_map.iter() + .map(|ref_multi| { + let (i, results) = ref_multi.pair(); + (*i, (results.iter() + .map(|res| res.reads) + .sum::() as f64 / results.len() as f64)) + }) + .collect() +} + +fn read_max(results_map: &DashMap>) -> DashMap { + results_map.iter() + .map(|ref_multi| { + let (i, results) = ref_multi.pair(); + (*i, results.iter() + .map(|s| s.reads) + .max() + .unwrap()) + }) + .collect() +} + +fn write_avg(results_map: &DashMap>) -> DashMap { + results_map.iter() + .map(|ref_multi| { + let (i, results) = ref_multi.pair(); + (*i, (results.iter() + .map(|res| res.writes) + .sum::() as f64 / results.len() as f64)) + }) + .collect() +} + +fn write_max(results_map: &DashMap>) -> DashMap { + results_map.iter() + .map(|ref_multi| { + let (i, results) = ref_multi.pair(); + (*i, results.iter() + .map(|s| s.writes) + .max() + .unwrap()) + }) + .collect() +} + +fn main() -> io::Result<()> { + + let m = 20; + + let asc_results: Arc>> = Arc::new(DashMap::new()); + let asc_heights: Arc>> = Arc::new(DashMap::new()); + let rand_results: Arc>> = Arc::new(DashMap::new()); + let rand_heights: Arc>> = Arc::new(DashMap::new()); + + let asc_results_clone = Arc::clone(&asc_results); + let asc_heights_clone = Arc::clone(&asc_heights); + let rand_results_clone = Arc::clone(&rand_results); + let rand_heights_clone = Arc::clone(&rand_heights); + + (10000u64..=100000u64).step_by(10000).collect::>().into_par_iter().for_each(move |n| { + if asc_results_clone.get(&n).is_some() { + return; + } + if asc_heights_clone.get(&n).is_some() { + return; + } + if rand_results_clone.get(&n).is_some() { + return; + } + if rand_heights_clone.get(&n).is_some() { + return; + } + println!("{thread}: starting n: {n}", thread = current_thread_index().unwrap()); + + asc_results_clone.insert(n, vec![]); + asc_heights_clone.insert(n, vec![]); + rand_results_clone.insert(n, vec![]); + rand_heights_clone.insert(n, vec![]); + + (1..=m).into_par_iter().for_each(|k| { + println!("{thread}: starting iteration {k}/{m} for n: {n}", thread = current_thread_index().unwrap()); + let mut asc = SplayTree::new(n as usize); + + for i in gen_asc(n) { + asc.insert(i); + asc_results_clone.get_mut(&n).unwrap().push(asc.stats.clone()); + asc.reset_stats(); + asc_heights_clone.get_mut(&n).unwrap().push(asc.height()); + } + for i in gen_rand(n) { + asc.delete(i); + asc_results_clone.get_mut(&n).unwrap().push(asc.stats.clone()); + asc.reset_stats(); + asc_heights_clone.get_mut(&n).unwrap().push(asc.height()); + } + + let mut rand = SplayTree::new(n as usize); + + for i in gen_rand(n) { + rand.insert(i); + rand_results_clone.get_mut(&n).unwrap().push(rand.stats.clone()); + rand.reset_stats(); + rand_heights_clone.get_mut(&n).unwrap().push(rand.height()); + } + for i in gen_rand(n) { + rand.delete(i); + rand_results_clone.get_mut(&n).unwrap().push(rand.stats.clone()); + rand.reset_stats(); + rand_heights_clone.get_mut(&n).unwrap().push(rand.height()); + } + + rand_results_clone.get_mut(&n).unwrap().push(rand.stats.clone()); + println!("{thread}: finished iteration {k}/{m} for n: {n}", thread = current_thread_index().unwrap()); + }); + println!("{thread}: finished n: {n}", thread = current_thread_index().unwrap()); + }); + + let asc_comp_averages = comp_avg(&asc_results); + let asc_read_averages = read_avg(&asc_results); + let asc_write_averages = write_avg(&asc_results); + + let asc_height_averages: DashMap<_, _> = asc_heights.iter() + .map(|ref_multi| { + let (i, results) = ref_multi.pair(); + (*i, results.iter().sum::() as f64 / results.len() as f64) + }) + .collect(); + + let asc_comp_max = comp_max(&asc_results); + let asc_read_max = read_max(&asc_results); + let asc_write_max = write_max(&asc_results); + + let asc_height_max: DashMap<_, _> = asc_heights.iter() + .map(|ref_multi| { + let (i, results) = ref_multi.pair(); + (*i, *results.iter().max().unwrap()) + }) + .collect(); + + let rand_comp_averages = comp_avg(&rand_results); + let rand_read_averages = read_avg(&rand_results); + let rand_write_averages = write_avg(&rand_results); + + let rand_height_averages: DashMap<_, _> = rand_heights.iter() + .map(|ref_multi| { + let (i, results) = ref_multi.pair(); + (*i, results.iter().sum::() as f64 / results.len() as f64) + }) + .collect(); + + let rand_comp_max = comp_max(&rand_results); + let rand_read_max = read_max(&rand_results); + let rand_write_max = write_max(&rand_results); + + let rand_height_max: DashMap<_, _> = rand_heights.iter() + .map(|ref_multi| { + let (i, results) = ref_multi.pair(); + (*i, *results.iter().max().unwrap()) + }) + .collect(); + + _ = fs::create_dir_all("./results"); + + let mut asc_comp_file = OpenOptions::new() + .create(true) + .write(true) + .truncate(true) + .open("./results/asc_comp")?; + + let mut asc_read_file = OpenOptions::new() + .create(true) + .write(true) + .truncate(true) + .open("./results/asc_read")?; + + let mut asc_write_file = OpenOptions::new() + .create(true) + .write(true) + .truncate(true) + .open("./results/asc_write")?; + + let mut asc_height_file = OpenOptions::new() + .create(true) + .write(true) + .truncate(true) + .open("./results/asc_height")?; + + let mut rand_comp_file = OpenOptions::new() + .create(true) + .write(true) + .truncate(true) + .open("./results/rand_comp")?; + + let mut rand_read_file = OpenOptions::new() + .create(true) + .write(true) + .truncate(true) + .open("./results/rand_read")?; + + let mut rand_write_file = OpenOptions::new() + .create(true) + .write(true) + .truncate(true) + .open("./results/rand_write")?; + + let mut rand_height_file = OpenOptions::new() + .create(true) + .write(true) + .truncate(true) + .open("./results/rand_height")?; + + for n in (10000u64..=100000u64).step_by(10000) { + writeln!(asc_comp_file, "{n} {} {}", + asc_comp_averages.get(&n).unwrap().value(), + asc_comp_max.get(&n).unwrap().value(), + )?; + + writeln!(asc_read_file, "{n} {} {}", + asc_read_averages.get(&n).unwrap().value(), + asc_read_max.get(&n).unwrap().value(), + )?; + + writeln!(asc_write_file, "{n} {} {}", + asc_write_averages.get(&n).unwrap().value(), + asc_write_max.get(&n).unwrap().value(), + )?; + + writeln!(asc_height_file, "{n} {} {}", + asc_height_averages.get(&n).unwrap().value(), + asc_height_max.get(&n).unwrap().value(), + )?; + + writeln!(rand_comp_file, "{n} {} {}", + rand_comp_averages.get(&n).unwrap().value(), + rand_comp_max.get(&n).unwrap().value(), + )?; + + writeln!(rand_read_file, "{n} {} {}", + rand_read_averages.get(&n).unwrap().value(), + rand_read_max.get(&n).unwrap().value(), + )?; + + writeln!(rand_write_file, "{n} {} {}", + rand_write_averages.get(&n).unwrap().value(), + rand_write_max.get(&n).unwrap().value(), + )?; + + writeln!(rand_height_file, "{n} {} {}", + rand_height_averages.get(&n).unwrap().value(), + rand_height_max.get(&n).unwrap().value(), + )?; + } + + Ok(()) +} diff --git a/libtree/src/lib.rs b/libtree/src/lib.rs index ccae51a..19ae8c2 100644 --- a/libtree/src/lib.rs +++ b/libtree/src/lib.rs @@ -1,8 +1,10 @@ mod binary_search_tree; mod red_black_tree; +mod splay_tree; pub use binary_search_tree::*; pub use red_black_tree::*; +pub use splay_tree::*; #[derive(Clone, Default)] pub struct Stats { diff --git a/libtree/src/red_black_tree.rs b/libtree/src/red_black_tree.rs index 7a690c9..2162815 100644 --- a/libtree/src/red_black_tree.rs +++ b/libtree/src/red_black_tree.rs @@ -1,5 +1,7 @@ use std::{cmp::Ordering, fmt::Display, ptr}; +use crate::Stats; + #[derive(Clone, Copy, PartialEq)] pub enum Colour { Red, Black } @@ -53,6 +55,22 @@ impl RBT { }))) } + pub fn height(&self) -> usize { + if self.is_null() { + return 0; + } + match (self.left().is_null(), self.right().is_null()) { + (true, true) => + 1, + (true, false) => + 1 + self.right().height(), + (false, true) => + 1 + self.left().height(), + (false, false) => + 1 + (self.left().height()).max(self.right().height()), + } + } + unsafe fn deep_clone(&self) -> Self { let mut node = Self::new((*self.0).key); if !self.left().is_null() { @@ -71,7 +89,8 @@ impl RBT { while !temp.left().is_null() { temp = temp.left(); } - return temp; + + temp } fn set_colour(&mut self, colour: Colour) { @@ -195,6 +214,7 @@ pub struct RedBlackTree { root: RBT, pub size: usize, print_capacity: usize, + pub stats: Stats, } impl Clone for RedBlackTree { @@ -214,51 +234,89 @@ impl RedBlackTree { root: RBT::null(), size: 0, print_capacity, + stats: Stats::default(), + } + } + + pub fn height(&self) -> usize { + match &self.root.is_null() { + false => self.root.height(), + true => 0, } } unsafe fn rotate_left(&mut self, mut node: RBT) { let mut right = node.right(); node.set_right(right.left()); + self.stats.writes += 1; + self.stats.reads += 2; + self.stats.reads += 1; if !right.left().is_null() { - right.left().set_parent(node.clone()); + right.left().set_parent(node); + self.stats.writes += 1; + self.stats.reads += 2; } right.set_parent(node.parent()); + self.stats.writes += 1; + self.stats.reads += 1; + self.stats.reads += 1; if node == self.root { - self.root = right.clone(); + self.root = right; + self.stats.writes += 1; } else if node == node.parent().left() { - node.parent().set_left(right.clone()); + node.parent().set_left(right); + self.stats.writes += 1; + self.stats.reads += 2; } else if node == node.parent().right() { - node.parent().set_right(right.clone()); + node.parent().set_right(right); + self.stats.writes += 1; + self.stats.reads += 2; } - right.set_left(node.clone()); - node.set_parent(right.clone()); + right.set_left(node); + node.set_parent(right); + self.stats.writes += 2; + self.stats.reads += 2; } unsafe fn rotate_right(&mut self, mut node: RBT) { let mut left = node.left(); node.set_left(left.right()); + self.stats.writes += 1; + self.stats.reads += 2; + self.stats.reads += 1; if !left.right().is_null() { - left.right().set_parent(node.clone()); + left.right().set_parent(node); + self.stats.writes += 1; + self.stats.reads += 2; } left.set_parent(node.parent()); + self.stats.writes += 1; + self.stats.reads += 1; + self.stats.reads += 1; if node == self.root { - self.root = left.clone(); + self.root = left; + self.stats.writes += 1; } else if node == node.parent().left() { - node.parent().set_left(left.clone()); + node.parent().set_left(left); + self.stats.writes += 1; + self.stats.reads += 2; } else if node == node.parent().right() { - node.parent().set_right(left.clone()); + node.parent().set_right(left); + self.stats.writes += 1; + self.stats.reads += 2; } - left.set_right(node.clone()); - node.set_parent(left.clone()); + left.set_right(node); + node.set_parent(left); + self.stats.writes += 2; + self.stats.reads += 2; } unsafe fn insert_fixup(&mut self, mut node: RBT) { @@ -267,11 +325,15 @@ impl RedBlackTree { let mut parent; let mut grandparent; + self.stats.reads += 1; while node.parent().colour() == Red { + self.stats.reads += 2; parent = node.parent(); grandparent = parent.parent(); + self.stats.reads += 1; if parent == grandparent.left() { + self.stats.reads += 1; let mut uncle = grandparent.right(); if !uncle.is_null() && uncle.colour() == Red { uncle.set_colour(Black); @@ -279,6 +341,7 @@ impl RedBlackTree { grandparent.set_colour(Red); node = grandparent; } else { + self.stats.reads += 1; if parent.right() == node { node = parent; self.rotate_left(node); @@ -290,12 +353,14 @@ impl RedBlackTree { } } else { let mut uncle = grandparent.left(); + self.stats.reads += 1; if !uncle.is_null() && uncle.colour() == Red { uncle.set_colour(Black); parent.set_colour(Black); grandparent.set_colour(Red); node = grandparent; } else { + self.stats.reads += 1; if parent.left() == node { node = parent; self.rotate_right(node); @@ -318,25 +383,34 @@ impl RedBlackTree { while !x.is_null() { y = x; + self.stats.comparisons += 1; match node.cmp(&&mut x) { Ordering::Less => { + self.stats.reads += 1; x = x.left(); } _ => { + self.stats.reads += 1; x = x.right(); } }; } + + self.stats.writes += 1; node.set_parent(y); if y.is_null() { + self.stats.writes += 1; self.root = node; } else { + self.stats.comparisons += 1; match node.cmp(&&mut y) { Ordering::Less => { + self.stats.writes += 1; y.set_left(node); } _ => { + self.stats.writes += 1; y.set_right(node); } }; @@ -346,18 +420,21 @@ impl RedBlackTree { unsafe { self.insert_fixup(node); } } - fn find_node(&self, key: u64) -> RBT { + fn find_node(&mut self, key: u64) -> RBT { if self.root.is_null() { return RBT::null(); } + self.stats.reads += 1; let mut temp = &self.root; unsafe { loop { + self.stats.comparisons += 1; let next = match key.cmp(&(*temp.0).key) { Ordering::Less => &mut (*temp.0).left, Ordering::Greater => &mut (*temp.0).right, Ordering::Equal => return *temp, }; + self.stats.reads += 1; if next.is_null() { break; } @@ -372,60 +449,78 @@ impl RedBlackTree { use Colour::*; let mut other; + self.stats.reads += 1; while node != self.root && node.colour() == Black { + self.stats.reads += 1; if parent.left() == node { + self.stats.reads += 1; other = parent.right(); if other.colour() == Red { other.set_colour(Black); parent.set_colour(Red); self.rotate_left(parent); + self.stats.reads += 1; other = parent.right(); } + self.stats.reads += 2; if other.left().colour() == Black && other.right().colour() == Black { other.set_colour(Red); node = parent; + self.stats.reads += 1; parent = node.parent(); } else { + self.stats.reads += 1; if other.right().colour() == Black { + self.stats.reads += 1; other.left().set_colour(Black); other.set_colour(Red); self.rotate_right(other); + self.stats.reads += 1; other = parent.right(); } other.set_colour(parent.colour()); parent.set_colour(Black); + self.stats.reads += 1; other.right().set_colour(Black); self.rotate_left(parent); node = self.root; break; } } else { + self.stats.reads += 1; other = parent.left(); if other.colour() == Red { other.set_colour(Black); parent.set_colour(Red); self.rotate_right(parent); + self.stats.reads += 1; other = parent.left(); } + self.stats.reads += 2; if other.left().colour() == Black && other.right().colour() == Black { other.set_colour(Red); node = parent; + self.stats.reads += 1; parent = node.parent(); } else { + self.stats.reads += 1; if other.left().colour() == Black { + self.stats.reads += 1; other.right().set_colour(Black); other.set_colour(Red); self.rotate_left(other); + self.stats.reads += 1; other = parent.left(); } other.set_colour(parent.colour()); parent.set_colour(Black); + self.stats.reads += 1; other.left().set_colour(Black); self.rotate_right(parent); node = self.root; @@ -444,18 +539,25 @@ impl RedBlackTree { self.size -= 1; + self.stats.reads += 2; if !node.left().is_null() && !node.right().is_null() { + self.stats.reads += 1; let mut replace = node.right().minimum(); if node == self.root { self.root = replace; } else { if node.parent().left() == node { + self.stats.reads += 1; + self.stats.writes += 1; node.parent().set_left(replace); } else { + self.stats.reads += 1; + self.stats.writes += 1; node.parent().set_right(replace); } } + self.stats.reads += 2; child = replace.right(); parent = replace.parent(); color = replace.colour(); @@ -463,13 +565,18 @@ impl RedBlackTree { parent = replace; } else { if !child.is_null() { + self.stats.writes += 1; child.set_parent(parent); } + self.stats.writes += 3; + self.stats.reads += 2; parent.set_left(child); replace.set_right(node.right()); node.right().set_parent(replace); } + self.stats.writes += 3; + self.stats.reads += 3; replace.set_parent(node.parent()); replace.set_colour(node.colour()); replace.set_left(node.left()); @@ -483,24 +590,32 @@ impl RedBlackTree { return obj.key; } + self.stats.reads += 1; if !node.left().is_null() { + self.stats.reads += 1; child = node.left(); } else { + self.stats.reads += 1; child = node.right(); } + self.stats.reads += 1; parent = node.parent(); color = node.colour(); if !child.is_null() { + self.stats.writes += 1; child.set_parent(parent); } if self.root == node { - self.root = child + self.root = child; } else { + self.stats.reads += 1; if parent.left() == node { + self.stats.writes += 1; parent.set_left(child); } else { + self.stats.writes += 1; parent.set_right(child); } } @@ -525,6 +640,10 @@ impl RedBlackTree { true } + + pub fn reset_stats(&mut self) { + self.stats = Stats::default(); + } } impl Display for RedBlackTree { diff --git a/libtree/src/splay_tree.rs b/libtree/src/splay_tree.rs new file mode 100644 index 0000000..534208c --- /dev/null +++ b/libtree/src/splay_tree.rs @@ -0,0 +1,447 @@ +use core::fmt::Display; +use std::{cmp::Ordering, ptr}; +use crate::Stats; + +#[derive(Clone)] +pub struct SplayTreeNode { + pub key: u64, + pub left: ST, + pub right: ST, + pub parent: ST, +} + +impl SplayTreeNode { + pub fn new(key: u64) -> Self { + Self { + key, + left: ST::null(), + right: ST::null(), + parent: ST::null(), + } + } +} + +pub struct ST(*mut SplayTreeNode); + + +impl Clone for ST { + fn clone(&self) -> Self { + Self(self.0) + } +} + +impl Copy for ST {} + +impl Ord for ST { + fn cmp(&self, other: &Self) -> Ordering { + unsafe { (*self.0).key.cmp(&(*other.0).key) } + } +} + +impl PartialOrd for ST { + fn partial_cmp(&self, other: &Self) -> Option { + unsafe { Some((*self.0).key.cmp(&(*other.0).key)) } + } +} + +impl PartialEq for ST { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } +} + +impl Eq for ST {} + +impl ST { + fn new(key: u64) -> Self { + Self(Box::into_raw(Box::new(SplayTreeNode { + key, + left: Self::null(), + right: Self::null(), + parent: Self::null(), + }))) + } + + pub fn height(&self) -> usize { + if self.is_null() { + return 0; + } + match (self.left().is_null(), self.right().is_null()) { + (true, true) => + 1, + (true, false) => + 1 + self.right().height(), + (false, true) => + 1 + self.left().height(), + (false, false) => + 1 + (self.left().height()).max(self.right().height()), + } + } + + fn minimum(self) -> Self { + let mut temp = self.clone(); + while !temp.left().is_null() { + temp = temp.left(); + } + + temp + } + + fn set_parent(&mut self, parent: Self) { + if !self.is_null() { + unsafe { (*self.0).parent = parent; } + } + } + + fn set_right(&mut self, right: Self) { + if !self.is_null() { + unsafe { (*self.0).right = right; } + } + } + + fn set_left(&mut self, left: Self) { + if !self.is_null() { + unsafe { (*self.0).left = left; } + } + } + + fn parent(&self) -> Self { + match self.is_null() { + true => Self::null(), + false => unsafe { (*self.0).parent.clone() } + } + } + + fn right(&self) -> Self { + match self.is_null() { + true => Self::null(), + false => unsafe { (*self.0).right.clone() } + } + } + + fn left(&self) -> Self { + match self.is_null() { + true => Self::null(), + false => unsafe { (*self.0).left.clone() } + } + } + + fn null() -> Self { + Self(ptr::null_mut()) + } + + fn is_null(&self) -> bool { + self.0.is_null() + } + + // https://stackoverflow.com/a/66661638 + fn replace_nth_char_ascii(s: &mut str, idx: usize, newchar: char) { + let s_bytes: &mut [u8] = unsafe { s.as_bytes_mut() }; + assert!(idx < s_bytes.len()); + assert!(s_bytes[idx].is_ascii()); + assert!(newchar.is_ascii()); + // we've made sure this is safe. + s_bytes[idx] = newchar as u8; + } + + fn _print(&self, f: &mut std::fmt::Formatter<'_>, depth: usize, prefix: char, left_trace: &mut String, right_trace: &mut String) -> std::fmt::Result { + if self.is_null() { + return Ok(()); + } + + if !self.left().is_null() { + self.left()._print(f, depth + 1, '/', left_trace, right_trace)?; + } + if prefix == '/' { + Self::replace_nth_char_ascii(left_trace, depth - 1, '|'); + } + if prefix == '\\' { + Self::replace_nth_char_ascii(right_trace, depth - 1, ' '); + } + if depth == 0 { + write!(f, "-")?; + } + if depth > 0 { + write!(f, " ")?; + } + + + if depth > 0 { + for i in 0..(depth - 1) { + if left_trace.chars().nth(i).unwrap() == '|' || right_trace.chars().nth(i).unwrap() == '|' { + write!(f, "| ")?; + } else { + write!(f, " ")?; + } + } + write!(f, "{}-", prefix)?; + } + writeln!(f, "[{}]", unsafe { (*self.0).key })?; + Self::replace_nth_char_ascii(left_trace, depth, ' '); + + if !self.right().is_null() { + Self::replace_nth_char_ascii(right_trace, depth, '|'); + self.right()._print(f, depth + 1, '\\', left_trace, right_trace)?; + } + + Ok(()) + } +} + +pub struct SplayTree { + pub root: ST, + pub size: usize, + print_capacity: usize, + pub stats: Stats, +} + +impl SplayTree { + pub fn new(print_capacity: usize) -> Self { + Self { + root: ST::null(), + size: 0, + print_capacity, + stats: Stats::default(), + } + } + + pub fn height(&self) -> usize { + match &self.root.is_null() { + false => self.root.height(), + true => 0, + } + } + + unsafe fn rotate_left(&mut self, mut node: ST) { + let mut right = node.right(); + node.set_right(right.left()); + self.stats.writes += 1; + self.stats.reads += 2; + + self.stats.reads += 1; + if !right.left().is_null() { + right.left().set_parent(node); + self.stats.writes += 1; + self.stats.reads += 2; + } + + right.set_parent(node.parent()); + self.stats.writes += 1; + self.stats.reads += 1; + + self.stats.reads += 1; + if node == self.root { + self.root = right; + self.stats.writes += 1; + } else if node == node.parent().left() { + node.parent().set_left(right); + self.stats.writes += 1; + self.stats.reads += 2; + } else if node == node.parent().right() { + node.parent().set_right(right); + self.stats.writes += 1; + self.stats.reads += 2; + } + + right.set_left(node); + node.set_parent(right); + self.stats.writes += 2; + self.stats.reads += 2; + } + + unsafe fn rotate_right(&mut self, mut node: ST) { + let mut left = node.left(); + node.set_left(left.right()); + self.stats.writes += 1; + self.stats.reads += 2; + + self.stats.reads += 1; + if !left.right().is_null() { + left.right().set_parent(node); + self.stats.writes += 1; + self.stats.reads += 2; + } + + left.set_parent(node.parent()); + self.stats.writes += 1; + self.stats.reads += 1; + + self.stats.reads += 1; + if node == self.root { + self.root = left; + self.stats.writes += 1; + } else if node == node.parent().left() { + node.parent().set_left(left); + self.stats.writes += 1; + self.stats.reads += 2; + } else if node == node.parent().right() { + node.parent().set_right(left); + self.stats.writes += 1; + self.stats.reads += 2; + } + + left.set_right(node); + node.set_parent(left); + self.stats.writes += 2; + self.stats.reads += 2; + } + + unsafe fn splay(&mut self, node: ST) { + while !node.parent().is_null() { + self.stats.reads += 1; + if node.parent() == self.root { + self.stats.reads += 1; + if node == node.parent().left() { + self.rotate_right(node.parent()); + } else { + self.rotate_left(node.parent()); + } + } else { + self.stats.reads += 2; + let parent = node.parent(); + let grandparent = node.parent().parent(); + + self.stats.reads += 4; + if node.parent().left() == node && parent.parent().left() == parent { + self.rotate_right(grandparent); + self.rotate_right(parent); + } else if node.parent().right() == node && parent.parent().right() == parent { + self.rotate_left(grandparent); + self.rotate_left(parent); + } else if node.parent().right() == node && parent.parent().left() == parent { + self.rotate_left(parent); + self.rotate_right(grandparent); + } else if node.parent().left() == node && parent.parent().right() == parent { + self.rotate_right(parent); + self.rotate_left(grandparent); + } + } + } + } + + pub fn insert(&mut self, key: u64) { + self.size += 1; + + let mut node = ST::new(key); + let mut new = ST::null(); + let mut temp = self.root; + + while !temp.is_null() { + new = temp; + + self.stats.comparisons += 1; + self.stats.reads += 1; + if key < unsafe { (*temp.0).clone() }.key { + temp = temp.left() + } else { + temp = temp.right(); + } + + self.stats.writes += 1; + node.set_parent(new); + } + + self.stats.writes += 2; + if new.is_null() { + self.root = node; + } else if key < unsafe { (*new.0).clone() }.key { + self.stats.comparisons += 1; + new.set_left(node); + } else { + new.set_right(node); + } + + unsafe { self.splay(node); } + } + + fn replace(&mut self, node: ST, mut with: ST) { + if node.parent().is_null() { + self.stats.reads += 1; + self.stats.writes += 1; + self.root = with; + } else if node == node.parent().left() { + self.stats.reads += 1; + node.parent().set_left(with); + } else { + self.stats.reads += 1; + node.parent().set_right(with); + } + + if !with.is_null() { + self.stats.reads += 1; + self.stats.writes += 1; + with.set_parent(node.parent()); + } + } + + fn find_node(&mut self, key: u64) -> ST { + if self.root.is_null() { + return ST::null(); + } + self.stats.reads += 1; + let mut temp = &self.root; + unsafe { + loop { + let next = match key.cmp(&(*temp.0).key) { + Ordering::Less => &mut (*temp.0).left, + Ordering::Greater => &mut (*temp.0).right, + Ordering::Equal => return *temp, + }; + self.stats.reads += 1; + if next.is_null() { + break; + } + temp = next; + } + } + + ST::null() + } + + pub fn delete(&mut self, key: u64) -> bool { + let node = self.find_node(key); + + if node.is_null() { + return false; + } + + unsafe { self.splay(node); } + + if node.left().is_null() { + self.stats.reads += 2; + self.replace(node, node.right()); + } else if node.right().is_null() { + self.stats.reads += 2; + self.replace(node, node.left()); + } else { + let mut temp = node.right().minimum(); + + self.stats.reads += 1; + if temp.parent() != node { + self.stats.reads += 2; + self.stats.writes += 2; + self.replace(temp, temp.right()); + temp.set_right(node.right()); + temp.right().set_parent(temp); + } + + self.replace(node, temp); + self.stats.reads += 2; + self.stats.writes += 2; + temp.set_left(node.left()); + temp.left().set_parent(temp); + } + + true + } + + pub fn reset_stats(&mut self) { + self.stats = Stats::default(); + } +} + +impl Display for SplayTree { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.root._print(f, 0, '-', &mut " ".repeat(self.print_capacity), &mut " ".repeat(self.print_capacity)) + } +}