Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ num-traits = "0.2.12"
arrayvec = "0.5.1"
alga = "0.9.3"
rand = "0.7.3"
fxhash = "0.2.1"

[dev-dependencies]
criterion = "0.3.3"
Expand Down
4 changes: 4 additions & 0 deletions rust/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ where
fn pop(&mut self);
/// Combines the values in fifo order and returns the result, e.g., `1+v1+v2+...+vn`.
fn query(&self) -> Value;
/// Returns the number of elements inside the window.
fn len(&self) -> usize;
/// Returns true if the window contains no elements.
fn is_empty(&self) -> bool;
}

/// An abstract data type which maintains sliding sub-window aggregates.
Expand Down
21 changes: 14 additions & 7 deletions rust/src/reactive/flat_fat.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use alga::general::AbstractMonoid;
use alga::general::Operator;
use std::collections::HashSet;
use fxhash::FxHashSet as HashSet;
use std::marker::PhantomData;

pub(crate) trait FAT<Value, BinOp>: Clone
where
Expand Down Expand Up @@ -49,7 +50,7 @@ where
pub(crate) tree: Vec<Value>,
/// Number of leaves which can be stored in the tree
pub(crate) capacity: usize,
binop: std::marker::PhantomData<BinOp>,
binop: PhantomData<BinOp>,
}

impl<Value, BinOp> FlatFAT<Value, BinOp>
Expand All @@ -58,26 +59,32 @@ where
BinOp: Operator,
{
/// Returns all leaf nodes of the tree
#[inline(always)]
pub(crate) fn leaves(&self) -> &[Value] {
&self.tree[self.leaf(0)..]
}
/// Returns the index of the root node
#[inline(always)]
fn root(&self) -> usize {
0
}
/// Returns the index of a leaf node
#[inline(always)]
fn leaf(&self, i: usize) -> usize {
i + self.capacity - 1
}
/// Returns the index of an node's left child
#[inline(always)]
fn left(&self, i: usize) -> usize {
2 * (i + 1) - 1
2 * i + 1
}
/// Returns the index of an node's right child
#[inline(always)]
fn right(&self, i: usize) -> usize {
2 * (i + 1)
2 * i + 2
}
/// Returns the index of an node's parent
#[inline(always)]
fn parent(&self, i: usize) -> usize {
(i - 1) / 2
}
Expand All @@ -96,10 +103,10 @@ where
}

fn with_capacity(capacity: usize) -> Self {
assert_ne!(capacity, 0, "Capacity of window must be greater than 0");
assert!(capacity > 1, "Capacity of window must be greater than 1");
Self {
tree: vec![Value::identity(); 2 * capacity - 1],
binop: std::marker::PhantomData,
binop: PhantomData,
capacity,
}
}
Expand All @@ -116,7 +123,7 @@ where
self.parent(leaf)
})
.collect();
let mut new_parents: HashSet<usize> = HashSet::new();
let mut new_parents: HashSet<usize> = HashSet::default();
loop {
parents.drain().for_each(|parent| {
let left = self.left(parent);
Expand Down
26 changes: 17 additions & 9 deletions rust/src/reactive/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ where
}
}
fn inverted(&self) -> bool {
self.front > self.back
self.front >= self.back
}
fn resize(&mut self, capacity: usize) {
let leaves = self.fat.leaves();
Expand Down Expand Up @@ -60,7 +60,7 @@ where
{
fn new() -> Self {
Self {
fat: FlatFAT::with_capacity(8),
fat: FlatFAT::with_capacity(2),
size: 0,
front: 0,
back: 0,
Expand All @@ -69,18 +69,20 @@ where
fn push(&mut self, v: Value) {
self.fat.update([(self.back, v)].iter().cloned());
self.size += 1;
self.back += 1;
self.back = (self.back + 1) % self.fat.capacity;
if self.size > (3 * self.fat.capacity) / 4 {
self.resize(self.fat.capacity * 2);
}
}
fn pop(&mut self) {
self.fat
.update([(self.front, Value::identity())].iter().cloned());
self.size -= 1;
self.front += 1;
if self.size <= self.fat.capacity / 4 {
self.resize(self.fat.capacity / 2);
if self.size > 0 {
self.fat
.update([(self.front, Value::identity())].iter().cloned());
self.size -= 1;
self.front = (self.front + 1) % self.fat.capacity;
if self.size <= self.fat.capacity / 4 && self.size > 0 {
self.resize(self.fat.capacity / 2);
}
}
}
fn query(&self) -> Value {
Expand All @@ -92,4 +94,10 @@ where
self.fat.aggregate()
}
}
fn len(&self) -> usize {
self.size
}
fn is_empty(&self) -> bool {
self.size == 0
}
}
6 changes: 6 additions & 0 deletions rust/src/recalc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,10 @@ where
.iter()
.fold(Value::identity(), |acc, elem| acc.operate(&elem))
}
fn len(&self) -> usize {
self.stack.len()
}
fn is_empty(&self) -> bool {
self.stack.is_empty()
}
}
6 changes: 6 additions & 0 deletions rust/src/soe/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,10 @@ where
fn query(&self) -> Value {
self.agg.clone()
}
fn len(&self) -> usize {
self.stack.len()
}
fn is_empty(&self) -> bool {
self.stack.is_empty()
}
}
6 changes: 6 additions & 0 deletions rust/src/two_stacks/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ where
fn query(&self) -> Value {
Self::agg(&self.front).operate(&Self::agg(&self.back))
}
fn len(&self) -> usize {
self.front.len() + self.back.len()
}
fn is_empty(&self) -> bool {
self.front.is_empty() && self.back.is_empty()
}
}

impl<Value, BinOp> TwoStacks<Value, BinOp>
Expand Down
4 changes: 2 additions & 2 deletions rust/tests/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ use alga::general::TwoSidedInverse;

/// An integer value
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct Int(pub i32);
pub struct Int(pub i64);

/// Binary operator for calculating the arithmetic sum.
/// Has the following properties:
Expand Down Expand Up @@ -94,7 +94,7 @@ impl Operator for Max {

impl Identity<Max> for Int {
fn identity() -> Int {
Int(std::i32::MIN)
Int(std::i64::MIN)
}
}

Expand Down
134 changes: 75 additions & 59 deletions rust/tests/fifo_window.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,29 @@
use rand::Rng;
use swag::reactive::*;
use swag::recalc::*;
use swag::soe::*;
use swag::two_stacks::*;
use swag::*;

mod common;
use common::*;

/// Macro for generating test cases for different algorithms.
macro_rules! test_matrix {
{
$(
$name:ident => [$($module:ident::$algorithm:ident),*]
),*
} => {
$(
mod $name {
$(
#[test]
fn $module() {
super::$name::<swag::$module::$algorithm<_,_>>();
}
)*
}
)*
}
}

/// Basic test for integer sums.
fn test1<Window>()
where
Expand All @@ -34,21 +50,21 @@ where
assert_eq!(window.query(), Int(5));
}

fn generate() -> Vec<Int> {
fn synthesize(size: usize) -> Vec<Int> {
let mut rng = rand::thread_rng();
(0..1000)
(0..size)
.map(|_| rng.gen_range(1, 5))
.map(Int)
.collect::<Vec<_>>()
}

/// Tries to aggregate the sum of 1000 randomly generated integers.
/// Tries to aggregate the sum of 1K randomly generated integers.
fn test2<Window>()
where
Window: FifoWindow<Int, Sum>,
{
let values = generate();
let sum: i32 = values.iter().fold(0, |acc, Int(x)| acc + x);
let values = synthesize(1_000);
let sum = values.iter().fold(0, |acc, Int(x)| acc + x);
let mut window = Window::new();
for v in values.clone() {
window.push(v);
Expand All @@ -60,13 +76,13 @@ where
assert_eq!(window.query(), Int(0));
}

/// Tries to find the maximum value out 1000 randomly generated integers.
/// Tries to find the maximum value out 1K randomly generated integers.
fn test3<Window>()
where
Window: FifoWindow<Int, Max>,
{
let mut window = Window::new();
let values = generate();
let values = synthesize(1_000);
let max = values.iter().map(|Int(x)| *x).max().unwrap();
for v in values.clone() {
window.push(v);
Expand All @@ -75,60 +91,60 @@ where
for _ in values {
window.pop();
}
assert_eq!(window.query(), Int(std::i32::MIN));
}

#[test]
fn test1_recalc() {
test1::<ReCalc<Int, Sum>>();
}

#[test]
fn test2_recalc() {
test2::<ReCalc<Int, Sum>>();
}

#[test]
fn test3_recalc() {
test3::<ReCalc<Int, Max>>();
}

#[test]
fn test1_soe() {
test1::<SoE<Int, Sum>>();
assert_eq!(window.query(), Int(std::i64::MIN));
}

#[test]
fn test2_soe() {
test2::<SoE<Int, Sum>>();
}

#[test]
fn test1_two_stacks() {
test1::<TwoStacks<Int, Sum>>();
}

#[test]
fn test2_two_stacks() {
test2::<TwoStacks<Int, Sum>>();
}

#[test]
fn test3_two_stacks() {
test3::<TwoStacks<Int, Max>>();
/// Fills a window with 1K elements and pushes/pops/queries 1K times.
fn test4<Window>()
where
Window: FifoWindow<Int, Sum>,
{
let mut window = Window::new();
let values = synthesize(1_000);
let sum = values.iter().fold(0, |acc, Int(x)| acc + x);
for v in values.clone() {
window.push(v);
}
for v in values {
window.push(v);
window.pop();
window.query();
assert_eq!(window.query(), Int(sum));
}
}

#[test]
fn test1_reactive() {
test1::<Reactive<Int, Sum>>();
/// Pops more elements from a window than what it contains.
fn test5<Window>()
where
Window: FifoWindow<Int, Sum>,
{
let mut window = Window::new();
window.push(Int(0));
window.push(Int(0));
window.pop();
window.pop();
window.pop();
}

#[test]
fn test2_reactive() {
test2::<Reactive<Int, Sum>>();
/// Pops more elements from a window than what it contains.
fn test6<Window>()
where
Window: FifoWindow<Int, Sum>,
{
let mut window = Window::new();
window.push(Int(1));
window.push(Int(2));
window.push(Int(3));
window.pop();
window.push(Int(4));
window.push(Int(5));
}

#[test]
fn test3_reactive() {
test3::<Reactive<Int, Max>>();
test_matrix! {
test1 => [ recalc::ReCalc, soe::SoE, reactive::Reactive, two_stacks::TwoStacks ],
test2 => [ recalc::ReCalc, soe::SoE, reactive::Reactive, two_stacks::TwoStacks ],
test3 => [ recalc::ReCalc, reactive::Reactive, two_stacks::TwoStacks ],
test4 => [ recalc::ReCalc, soe::SoE, reactive::Reactive, two_stacks::TwoStacks ],
test5 => [ recalc::ReCalc, soe::SoE, reactive::Reactive, two_stacks::TwoStacks ],
test6 => [ recalc::ReCalc, soe::SoE, reactive::Reactive, two_stacks::TwoStacks ]
}