Skip to content

Commit 6c94b3b

Browse files
committed
Add test to SlidingWindow
1 parent bcf5160 commit 6c94b3b

File tree

4 files changed

+166
-8
lines changed

4 files changed

+166
-8
lines changed
Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package org.hydev.mcpm.client.interaction;
22

33
import org.hydev.mcpm.utils.Pair;
4+
import org.hydev.mcpm.utils.arrays.FixedWindowSum;
5+
import org.hydev.mcpm.utils.arrays.SlidingWindow;
46

57
import java.util.ArrayDeque;
68
import java.util.Queue;
@@ -10,30 +12,40 @@
1012
*
1113
*/
1214
public class ProgressSpeedCalculator implements ProgressSpeedBoundary {
13-
private final Queue<Pair<Long, Long>> queue = new ArrayDeque<>();
15+
private final FixedWindowSum windowSum;
1416
private final long window;
1517
private final long start;
1618

1719
private long total = 0; // total to calculate inc in setProgress
1820
private long sum = 0; // sliding window sum
1921

2022
/**
23+
* Construct a ProgressSpeedCalculator with a window size.
2124
*
2225
* @param window Time length of the window to calculate speed over, in nanoseconds
2326
*/
2427
public ProgressSpeedCalculator(long window) {
25-
start = System.nanoTime();
28+
this(window, new SlidingWindow().setWindowSize(window));
29+
}
30+
31+
/**
32+
* Construct a ProgressSpeedCalculator with the specified FixedWindowSum to calculate speed.
33+
* Window speed should be in nanoseconds.
34+
*
35+
* @param window window size of the given windowSum
36+
* @param windowSum the FixedWindowSum used to calculate speed
37+
*/
38+
public ProgressSpeedCalculator(long window, FixedWindowSum windowSum) {
2639
this.window = window;
40+
start = System.nanoTime();
41+
this.windowSum = windowSum;
2742
}
2843

2944

3045
@Override
3146
public double getSpeed() {
3247
long time = System.nanoTime();
33-
while (!queue.isEmpty() && queue.peek().k() < time - window) { // remove all elements that are too old
34-
var e = queue.remove();
35-
sum -= e.v(); // update sliding window sum
36-
}
48+
long sum = windowSum.sum(time);
3749
long dt = Math.min(window, time - start);
3850
return sum / (dt / 1e9);
3951
}
@@ -47,7 +59,6 @@ public void setProgress(long progress) {
4759

4860
@Override
4961
public void incProgress(long inc) {
50-
queue.add(new Pair<>(System.nanoTime(), inc));
51-
sum += inc;
62+
windowSum.add(System.nanoTime(), inc);
5263
}
5364
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package org.hydev.mcpm.utils.arrays;
2+
3+
/**
4+
* A data structure supporting appending of index-value pairs
5+
* and querying the sum of all values in a fixed window size.
6+
* This only supports addition non-decreasing index pairs, and queries of non-increasing indices,
7+
* and queries of a fixed window size.
8+
*
9+
* @author Peter (https://github.com/MstrPikachu)
10+
* @since 2022-11-21
11+
*/
12+
public interface FixedWindowSum {
13+
14+
/**
15+
* Set the size of the window. After creating an instance, start by calling this and only call it at most once.
16+
*
17+
* @param window size of the window
18+
* @return this object for fluent access
19+
*/
20+
FixedWindowSum setWindowSize(long window);
21+
22+
/**
23+
* Add an index-value pair to the window.
24+
*
25+
* @param index the index of the pair
26+
* @param val value to add to data structure
27+
*/
28+
void add(long index, long val);
29+
30+
/**
31+
* Query the sum of all values that have an index within the window size (exclusive)
32+
* that ends at the index of the most recent addition.
33+
*
34+
* @return the result of the query
35+
*/
36+
long sum();
37+
38+
/**
39+
* Query the sum of all values that have an index within the window size (exclusive) that ends at the given index.
40+
*
41+
* @param index the given index
42+
* @return the result of the query
43+
*/
44+
long sum(long index);
45+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package org.hydev.mcpm.utils.arrays;
2+
3+
import org.hydev.mcpm.utils.Pair;
4+
5+
import java.util.ArrayDeque;
6+
import java.util.Queue;
7+
8+
/**
9+
* An implementation of FixedWindowSum using a queue.
10+
*
11+
* @author Peter (https://github.com/MstrPikachu)
12+
* @since 2022-11-21
13+
*/
14+
public class SlidingWindow implements FixedWindowSum {
15+
private long sum;
16+
private long window;
17+
private final Queue<Pair<Long, Long>> queue = new ArrayDeque<>();
18+
19+
@Override
20+
public FixedWindowSum setWindowSize(long window) {
21+
this.window = window;
22+
return this;
23+
}
24+
25+
@Override
26+
public void add(long index, long inc) {
27+
queue.add(new Pair<>(index, inc));
28+
sum += inc; // increase sum after adding a new value
29+
restore(index);
30+
}
31+
32+
@Override
33+
public long sum() {
34+
return sum;
35+
}
36+
37+
@Override
38+
public long sum(long index) {
39+
restore(index);
40+
return sum;
41+
}
42+
43+
/**
44+
* Restore the invariant that all values in the queue are within the window
45+
*
46+
* @param index index of the newest query/insert
47+
*/
48+
private void restore(long index) {
49+
// remove values outside the window, O(1) amortized
50+
while (!queue.isEmpty() && queue.peek().getKey() < index - window) {
51+
var element = queue.poll(); // remove the oldest value if it is outside the window
52+
sum -= element.getValue(); // decrement sum after removing values
53+
}
54+
}
55+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package org.hydev.mcpm.utils.arrays;
2+
3+
import org.hydev.mcpm.utils.Pair;
4+
import org.junit.jupiter.api.Test;
5+
6+
import java.util.ArrayList;
7+
import java.util.Random;
8+
9+
import static org.junit.jupiter.api.Assertions.*;
10+
11+
class SlidingWindowTest {
12+
13+
ArrayList<Pair<Long, Long>> arr;
14+
SlidingWindow slidingWindow;
15+
long window;
16+
17+
18+
long naiveSum(long index) {
19+
long ret = 0;
20+
for (int i = arr.size() - 1; i >= 0; i--) {
21+
if (arr.get(i).getKey() < index - window)
22+
break;
23+
ret += arr.get(i).getValue();
24+
}
25+
return ret;
26+
}
27+
28+
@Test
29+
void testAll() {
30+
Random r = new Random();
31+
for (int window = 1; window <= 5001; window += 5) {
32+
this.window = window;
33+
long cur = 0;
34+
arr = new ArrayList<>();
35+
slidingWindow = new SlidingWindow();
36+
slidingWindow.setWindowSize(window);
37+
for (int i = 0; i < 1000; i++) {
38+
long x = r.nextLong();
39+
int ind = Math.abs(r.nextInt());
40+
cur += ind;
41+
arr.add(new Pair<>(cur, x));
42+
slidingWindow.add(cur, x);
43+
assertEquals(slidingWindow.sum(cur), naiveSum(cur));
44+
}
45+
}
46+
}
47+
}

0 commit comments

Comments
 (0)