├── .travis.yml ├── LICENCE ├── README.md ├── common.h ├── deque.h ├── makefile ├── mono_wedge.h ├── runningmaxmin.cpp ├── runningmaxmin.h └── unit.cpp /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | sudo: false 3 | compiler: 4 | - clang 5 | 6 | branches: 7 | only: 8 | - master 9 | 10 | script: make && ./unit 11 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | This is made available under LGPL. 2 | 3 | Note that some of these algorithms may be protected by patents (though that's not the case 4 | for my streaming algorithm which is 100% patent-free). All algorithms were implemented 5 | strictly for research purposes. Do not sue me, I make no money off this. 6 | 7 | 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Fast running maximum-minimum filters implemented in C++. 2 | ========================================================== 3 | [![Build Status](https://travis-ci.org/lemire/runningmaxmin.png)](https://travis-ci.org/lemire/runningmaxmin) 4 | 5 | This code implements the algorithms described in the following paper: 6 | 7 | Daniel Lemire, [Streaming Maximum-Minimum Filter Using No More than 8 | Three Comparisons per Element](http://arxiv.org/abs/cs.DS/0610046). Nordic Journal of Computing, 13 (4), pages 328-339, 2006. 9 | 10 | Contributors: Daniel Lemire, Kai Wolf 11 | 12 | The main algorithm presented in this package is used in [Apache Hive](https://github.com/apache/hive). 13 | 14 | Usage 15 | ----- 16 | 17 | To reproduce the numbers from the paper, do the following: 18 | 19 | ``` 20 | make 21 | 22 | ./unit 23 | 24 | ./runningmaxmin --sine 1000000 10000 --windowrange 4 100 --times 1 25 | 26 | ./runningmaxmin --white 1000000 --windowrange 4 100 --times 1 27 | ``` 28 | 29 | Suitability 30 | ------------ 31 | 32 | The new algorithm introduced in the manuscript is most suitable for piecewise monotonic 33 | data or when low-latency is required. Otherwise, Gil-Kimmel and van Herk 34 | are good choices. 35 | 36 | See also 37 | --------- 38 | 39 | - Julia version: streaming maximum-minimum filter implementation in Julia https://github.com/sairus7/MaxMinFilters.jl 40 | - For a Python version, see https://github.com/lemire/pythonmaxmin 41 | - For an application of this idea to rolling statistics in JavaScript, see https://github.com/shimondoodkin/efficient-rolling-stats 42 | - For an application in Go, please see https://github.com/notnot/movingminmax 43 | - Another C++ library: STL Monotonic Wedge https://github.com/EvanBalster/STL_mono_wedge 44 | -------------------------------------------------------------------------------- /common.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef COMMON_H 3 | #define COMMON_H 4 | 5 | typedef unsigned int uint; 6 | typedef double floattype; 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /deque.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef DEQUE_H 3 | #define DEQUE_H 4 | 5 | #include "common.h" 6 | 7 | // nextPowerOfTwo returns a power of two that is larger or equal than x. 8 | inline uint nextPowerOfTwo(uint x) { 9 | uint result = 1; 10 | while (result < x) { 11 | result <<= 1; 12 | } 13 | return result; 14 | } 15 | 16 | struct intfloatnode { 17 | uint index; 18 | floattype value; 19 | }; 20 | 21 | struct intfloatqueue { 22 | intfloatnode * nodes; 23 | uint head; 24 | uint tail; 25 | uint mask; 26 | }; 27 | 28 | inline uint count(intfloatqueue * q) { 29 | return (q->tail - q->head) & q->mask; 30 | } 31 | 32 | inline void init(intfloatqueue * q, uint size) { 33 | size = nextPowerOfTwo(size + 1); 34 | q->nodes = 35 | reinterpret_cast(malloc(sizeof(intfloatnode) * size)); 36 | q->head = 0; 37 | q->tail = 0; 38 | q->mask = size - 1; 39 | } 40 | 41 | inline void free(intfloatqueue * q) { 42 | free(q->nodes); 43 | } 44 | 45 | inline uint headindex(intfloatqueue * q) { 46 | return q->nodes[q->head].index; 47 | } 48 | 49 | inline void push(intfloatqueue * q, uint index, floattype value) { 50 | q->nodes[q->tail].index = index; 51 | q->nodes[q->tail].value = value; 52 | q->tail = (q->tail + 1) & q->mask; 53 | } 54 | 55 | inline floattype tailvalue(intfloatqueue * q) { 56 | return q->nodes[(q->tail - 1) & q->mask].value; 57 | } 58 | 59 | inline floattype headvalue(intfloatqueue * q) { 60 | return q->nodes[q->head].value; 61 | } 62 | inline void prunehead(intfloatqueue * q) { 63 | q->head = (q->head + 1) & q->mask; 64 | } 65 | 66 | inline void prunetail(intfloatqueue * q) { 67 | q->tail = (q->tail - 1) & q->mask; 68 | } 69 | 70 | inline int nonempty(intfloatqueue * q) { 71 | return static_cast(q->tail != q->head); 72 | } 73 | 74 | #endif 75 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | # 2 | .SUFFIXES: 3 | # 4 | .SUFFIXES: .cpp .o .c .h 5 | 6 | RELEASEFLAGS = -std=c++11 -O3 -Wall -mavx -msse4.2 -Wextra -fexceptions -fPIC 7 | DEBUGFLAGS = -std=c++11 -g3 -Wall -mavx -msse4.2 -Wextra -fexceptions -fPIC 8 | SANITIZEFLAGS = -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined 9 | all: runningmaxmin unit 10 | 11 | debug: runningmaxmin.h runningmaxmin.cpp 12 | $(CXX) $(DEBUGFLAGS) -o runningmaxmin runningmaxmin.cpp 13 | 14 | runningmaxmin : runningmaxmin.h runningmaxmin.cpp 15 | $(CXX) $(RELEASEFLAGS) -o runningmaxmin runningmaxmin.cpp 16 | unit : runningmaxmin.h unit.cpp 17 | $(CXX) $(RELEASEFLAGS) -o unit unit.cpp 18 | 19 | 20 | sanerunningmaxmin : runningmaxmin.h runningmaxmin.cpp 21 | $(CXX) $(DEBUGFLAGS) $(SANITIZEFLAGS) -o sanerunningmaxmin runningmaxmin.cpp 22 | saneunit : runningmaxmin.h unit.cpp 23 | $(CXX) $(DEBUGFLAGS) $(SANITIZEFLAGS) -o saneunit unit.cpp 24 | 25 | 26 | 27 | 28 | clean: 29 | rm -f *.o runningmaxmin unit 30 | -------------------------------------------------------------------------------- /mono_wedge.h: -------------------------------------------------------------------------------- 1 | #ifndef MONOTONIC_WEDGE_H 2 | #define MONOTONIC_WEDGE_H 3 | 4 | #include 5 | 6 | #if __cplusplus > 199711L 7 | #include // For std::forward 8 | #endif 9 | 10 | /* 11 | This header presents algorithms for fast running minimum and maximum using 12 | the Daniel Lemire monotonic wedge algorithm with enhancements proposed 13 | by Ethan Fenn. 14 | 15 | The algorithm here is modeled on the C++ STL style and meant to be used 16 | with vector, deque or ring-buffer structures. 17 | 18 | The amortized complexity of the update operation is constant; 19 | IE, N updates can be performed in linear time on a given wedge. 20 | The worst-case complexity for a single update is below log2(N). 21 | 22 | 23 | Usage recommendations: 24 | 25 | This algorithm is most useful for "rolling" min / max evaluation. 26 | Most applications will thus prefer deques or ring-buffers as wedges. 27 | 28 | Generally, values beyond a certain age should be popped to limit the 29 | size of the wedge, though amortized complexity will remain linear even 30 | if this is not done. 31 | 32 | The wedge must be monotonic at all times with respect to Compare, 33 | EG. by only modifying the structure with wedge_update and pop_front. 34 | 35 | 36 | See bottom for (MIT) license and IP remarks. 37 | */ 38 | 39 | namespace mono_wedge 40 | { 41 | /* 42 | mono_wedge_search(begin, end, value, comp) 43 | 44 | Search routine used to determine deletion range in mono_wedge_update. 45 | 46 | Similar to std::lower_bound, returns first element in range for which 47 | comp(value, element) returns false. 48 | 49 | Range must be sorted with regard to comp. 50 | Iterator must be a random access iterator. 51 | Complexity is below log2(N) with respect to wedge size. 52 | Facilitates amortized constant complexity in mono_wedge_update. 53 | */ 54 | template 55 | Iterator mono_wedge_search( 56 | Iterator begin, 57 | Iterator end, 58 | const T &value, 59 | Compare comp) 60 | { 61 | long size = long(end - begin); 62 | if (size <= 0) return end; 63 | 64 | // Linear search through at most J elements, J = log2(N-J). 65 | long i = 1; 66 | for (; ((size-i) >> i) > 0; ++i) 67 | { 68 | // If you get a compile error here, your Wedge type does not 69 | // support random-access iterators and should be changed! 70 | Iterator test = end-i; 71 | if (comp(*test, value)) return test+1; 72 | } 73 | 74 | // Defer to lower_bound for binary search. 75 | return std::lower_bound(begin, end-(i-1), value, comp); 76 | } 77 | 78 | 79 | /* 80 | mono_wedge_update(wedge, value, comp) 81 | 82 | Update a monotonic wedge with a new value. 83 | 84 | Erases values which do not satisfy comp(element, value), then 85 | appends value to the wedge via push_back. 86 | 87 | Complexity is less than log2(N) with respect to wedge size. 88 | Complexity of N calls is O(N), if wedge is initially empty. 89 | Thus, amortized complexity over many calls is constant. 90 | 91 | Wedge type must: 92 | - Produce random access iterators via begin/end. 93 | - Support push_back. 94 | 95 | A "less" comparator yields a min-wedge. 96 | A "greater" comparator yields a max-wedge. 97 | */ 98 | template 99 | void mono_wedge_update( 100 | Wedge &wedge, 101 | const T &value, 102 | Compare comp) 103 | { 104 | wedge.erase( 105 | mono_wedge_search(wedge.begin(), wedge.end(), value, comp), 106 | wedge.end()); 107 | wedge.push_back(value); 108 | } 109 | 110 | 111 | /* 112 | min_wedge_search(wedge, value) 113 | min_wedge_search(wedge, value) 114 | 115 | Convenience variants of mono_wedge_search for min and max wedges. 116 | These will use std::greater/less, which default to operator >/<. 117 | */ 118 | template 119 | Iterator min_wedge_search(Iterator begin, Iterator end, const T &value) {return mono_wedge_search(begin, end, value, std::less());} 120 | 121 | template 122 | Iterator max_wedge_search(Iterator begin, Iterator end, const T &value) {return mono_wedge_search(begin, end, value, std::greater());} 123 | 124 | /* 125 | min_wedge_update(wedge, value) 126 | min_wedge_update(wedge, value) 127 | 128 | Convenience variants of mono_wedge_update for min and max wedges. 129 | These will use std::greater/less, which default to operator >/<. 130 | */ 131 | template 132 | void min_wedge_update(Wedge &wedge, const T &value) {return mono_wedge_update(wedge, value, std::less());} 133 | 134 | template 135 | void max_wedge_update(Wedge &wedge, const T &value) {return mono_wedge_update(wedge, value, std::greater());} 136 | 137 | 138 | #if __cplusplus > 199711L 139 | /* 140 | C++11 variants of mono_wedge_update supporting rvalue references. 141 | */ 142 | 143 | template 144 | void mono_wedge_update(Wedge &wedge, T &&value, Compare comp) 145 | { 146 | wedge.erase( 147 | mono_wedge_search(wedge.begin(), wedge.end(), value, comp), 148 | wedge.end()); 149 | wedge.push_back(std::forward(value)); 150 | } 151 | 152 | template 153 | void min_wedge_update(Wedge &wedge, T &&value) {mono_wedge_update(wedge, std::forward(value), std::less());} 154 | 155 | template 156 | void max_wedge_update(Wedge &wedge, T &&value) {mono_wedge_update(wedge, std::forward(value), std::greater());} 157 | #endif 158 | } 159 | 160 | #endif // MONOTONIC_WEDGE_H 161 | 162 | 163 | 164 | /* 165 | This code is available under the MIT license: 166 | 167 | Copyright (c) 2016 Evan Balster 168 | 169 | Permission is hereby granted, free of charge, to any person obtaining a copy of this 170 | software and associated documentation files (the "Software"), to deal in the Software 171 | without restriction, including without limitation the rights to use, copy, modify, merge, 172 | publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons 173 | to whom the Software is furnished to do so, subject to the following conditions: 174 | 175 | The above copyright notice and this permission notice shall be included in all copies or 176 | substantial portions of the Software. 177 | 178 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 179 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 180 | PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 181 | FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 182 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 183 | DEALINGS IN THE SOFTWARE. 184 | 185 | 186 | The original Lemire algorithm is "patent-free". For more information on the Lemire algorithm: 187 | 188 | Code: https://github.com/lemire/runningmaxmin 189 | 190 | Paper: https://arxiv.org/abs/cs/0610046 191 | */ 192 | -------------------------------------------------------------------------------- /runningmaxmin.cpp: -------------------------------------------------------------------------------- 1 | #include "runningmaxmin.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | 9 | std::vector getwhite(uint size) { 10 | std::vector data(size); 11 | for (uint k = 0; k < size; ++k) 12 | data[k] = (1.0 * rand() / (RAND_MAX)) - 0.5; 13 | return data; 14 | } 15 | 16 | std::vector getrandomwalk(uint size) { 17 | std::vector data(size); 18 | data[0] = 0.0; 19 | for (uint k = 1; k < size; ++k) 20 | data[k] = (1.0 * rand() / (RAND_MAX)) - 0.5 + data[k - 1]; 21 | return data; 22 | } 23 | 24 | std::vector getcin() { 25 | float val; 26 | std::cin >> val; 27 | std::vector v; 28 | while (std::cin) { 29 | v.push_back(val); 30 | std::cin >> val; 31 | } 32 | std::cout << "# Read " << v.size() << " data points. " << std::endl; 33 | return v; 34 | } 35 | 36 | void compareallalgos(std::vector & data, 37 | std::vector & timings, uint width, bool doslow) { 38 | if (timings.size() < 8) 39 | timings = std::vector(8, 0.0); 40 | clock_t start, finish; 41 | start = clock(); 42 | if (doslow) 43 | slowmaxmin A(data, width); 44 | finish = clock(); 45 | timings[0] += static_cast(finish - start) / CLOCKS_PER_SEC; 46 | start = clock(); 47 | vanHerkGilWermanmaxmin B(data, width); 48 | finish = clock(); 49 | timings[1] += static_cast(finish - start) / CLOCKS_PER_SEC; 50 | start = clock(); 51 | lemiremaxmin C(data, width); 52 | finish = clock(); 53 | timings[2] += static_cast(finish - start) / CLOCKS_PER_SEC; 54 | start = clock(); 55 | GilKimmel D(data, width); 56 | finish = clock(); 57 | timings[3] += static_cast(finish - start) / CLOCKS_PER_SEC; 58 | start = clock(); 59 | if (width < sizeof(unsigned long) * 8) 60 | lemirebitmapmaxmin BL(data, width); 61 | finish = clock(); 62 | timings[4] += static_cast(finish - start) / CLOCKS_PER_SEC; 63 | start = clock(); 64 | simplelemiremaxmin F(data, width); 65 | finish = clock(); 66 | timings[5] += static_cast(finish - start) / CLOCKS_PER_SEC; 67 | start = clock(); 68 | lemiremaxminwrap Cw(data, width); 69 | finish = clock(); 70 | timings[6] += static_cast(finish - start) / CLOCKS_PER_SEC; 71 | start = clock(); 72 | monowedgewrap Mw(data, width); 73 | finish = clock(); 74 | timings[7] += static_cast(finish - start) / CLOCKS_PER_SEC; 75 | } 76 | 77 | void process(std::vector & data, uint width = 30, uint times = 1, 78 | bool doslow = true) { 79 | std::vector timings; 80 | for (uint i = 0; i < times; ++i) { 81 | compareallalgos(data, timings, width, doslow); 82 | } 83 | std::cout << "#\tslow\t\tvanHerk\t\tlemire\tsimplelemire\t\tgilkimmel" 84 | << std::endl; 85 | std::cout << "\t" << timings[0] << "\t" << timings[1] << "\t" << timings[2] 86 | << "\t" << timings[5] << "\t\t" << timings[3] << std::endl; 87 | } 88 | 89 | void timings(uint width = 50, uint size = 10000, uint times = 500, 90 | bool doslow = true) { 91 | std::vector timings; 92 | for (uint i = 0; i < times; ++i) { 93 | std::vector data = getwhite(size); 94 | compareallalgos(data, timings, width, doslow); 95 | } 96 | std::cout << std::setw(15) << "slow"; 97 | std::cout << std::setw(15) << "vanHerk"; 98 | std::cout << std::setw(15) << "lemire"; 99 | std::cout << std::setw(15) << "gilkimmel"; 100 | std::cout << std::setw(15) << "bitmap"; 101 | std::cout << std::setw(15) << "simplelemire"; 102 | std::cout << std::setw(15) << "lemirew"; 103 | std::cout << std::setw(15) << "monowedge"; 104 | std::cout << std::endl; 105 | for (int i = 0; i <= 7; ++i) { 106 | std::cout << std::setw(15) << timings[i]; 107 | } 108 | std::cout << std::endl; 109 | } 110 | 111 | void walktimings(uint width = 50, uint size = 10000, uint times = 500, 112 | bool doslow = true) { 113 | std::vector timings; 114 | for (uint i = 0; i < times; ++i) { 115 | std::vector data = getrandomwalk(size); 116 | compareallalgos(data, timings, width, doslow); 117 | } 118 | std::cout << std::setw(15) << "slow"; 119 | std::cout << std::setw(15) << "vanHerk"; 120 | std::cout << std::setw(15) << "lemire"; 121 | std::cout << std::setw(15) << "gilkimmel"; 122 | std::cout << std::setw(15) << "bitmap"; 123 | std::cout << std::setw(15) << "simplelemire"; 124 | std::cout << std::setw(15) << "lemirew"; 125 | std::cout << std::setw(15) << "monowedge"; 126 | std::cout << std::endl; 127 | for (int i = 0; i <= 7; ++i) { 128 | std::cout << std::setw(15) << timings[i]; 129 | } 130 | std::cout << std::endl; 131 | } 132 | 133 | void sinetimings(uint width = 50, uint size = 10000, floattype period = 500.0, 134 | uint times = 500, bool doslow = true) { 135 | std::vector timings; 136 | for (uint i = 0; i < times; ++i) { 137 | std::vector data(size); 138 | for (uint j = 0; j < size; ++j) 139 | data[j] = sin(2 * M_PI * j / period); 140 | compareallalgos(data, timings, width, doslow); 141 | } 142 | std::cout << std::setw(15) << "slow"; 143 | std::cout << std::setw(15) << "vanHerk"; 144 | std::cout << std::setw(15) << "lemire"; 145 | std::cout << std::setw(15) << "gilkimmel"; 146 | std::cout << std::setw(15) << "bitmap"; 147 | std::cout << std::setw(15) << "simplelemire"; 148 | std::cout << std::setw(15) << "lemirew"; 149 | std::cout << std::setw(15) << "monowedge"; 150 | std::cout << std::endl; 151 | for (int i = 0; i <= 7; ++i) { 152 | std::cout << std::setw(15) << timings[i]; 153 | } 154 | std::cout << std::endl; 155 | } 156 | 157 | void timingsline(std::vector data, uint width = 30, 158 | bool doslow = false) { 159 | std::cout << " width = " << width << std::endl; 160 | clock_t start, finish; 161 | start = clock(); 162 | if (doslow) 163 | for (uint i = 0; i < 30; ++i) 164 | slowmaxmin A(data, width); 165 | finish = clock(); 166 | std::cout << "slow = " 167 | << static_cast(finish - start) / CLOCKS_PER_SEC 168 | << std::endl; 169 | start = clock(); 170 | for (uint i = 0; i < 30; ++i) 171 | vanHerkGilWermanmaxmin B(data, width); 172 | finish = clock(); 173 | std::cout << "vanHerk = " 174 | << static_cast(finish - start) / CLOCKS_PER_SEC 175 | << std::endl; 176 | start = clock(); 177 | for (uint i = 0; i < 30; ++i) 178 | lemiremaxmin C(data, width); 179 | finish = clock(); 180 | std::cout << "lemire = " 181 | << static_cast(finish - start) / CLOCKS_PER_SEC 182 | << std::endl; 183 | std::cout << "------------" << std::endl; 184 | } 185 | 186 | /** 187 | * use in conjunction with 188 | 189 | | awk '{print $2}' 190 | */ 191 | int main(int params, char ** args) { 192 | int whitesize = 0; 193 | int walksize = 0; 194 | int times = 1; 195 | int sinesize = 0; 196 | floattype sineperiod = 0.0; 197 | int windowbegin = 10; 198 | int windowend = 11; 199 | bool doslow = true; 200 | bool cininput = false; 201 | std::vector data(0); 202 | for (int i = 1; i < params; ++i) { 203 | if (strcmp(args[i], "--skipslow") == 0) { 204 | doslow = false; 205 | } 206 | if (strcmp(args[i], "--pipedata") == 0) { 207 | cininput = true; 208 | } 209 | if (strcmp(args[i], "--unit") == 0) { 210 | std::cout << "OK " << std::endl; 211 | return 0; 212 | } 213 | if (strcmp(args[i], "--window") == 0) { 214 | if (params - i > 1) { 215 | windowbegin = atoi(args[++i]); 216 | windowend = windowbegin + 1; 217 | } else { 218 | std::cerr << "--window expects an integer (length)" 219 | << std::endl; 220 | return -1; 221 | } 222 | continue; 223 | } 224 | if (strcmp(args[i], "--windowrange") == 0) { 225 | if (params - i > 2) { 226 | windowbegin = atoi(args[++i]); 227 | windowend = atoi(args[++i]) + 1; 228 | } else { 229 | std::cerr << "--windowrange expects two integers (length)" 230 | << std::endl; 231 | return -1; 232 | } 233 | continue; 234 | } 235 | if (strcmp(args[i], "--white") == 0) { 236 | if (params - i > 1) 237 | whitesize = atoi(args[++i]); 238 | else { 239 | std::cerr << "--white expects an integer (length)" << std::endl; 240 | return -1; 241 | } 242 | continue; 243 | } 244 | if (strcmp(args[i], "--walk") == 0) { 245 | if (params - i > 1) 246 | walksize = atoi(args[++i]); 247 | else { 248 | std::cerr << "--walk expects an integer (length)" << std::endl; 249 | return -1; 250 | } 251 | continue; 252 | } 253 | if (strcmp(args[i], "--sine") == 0) { 254 | if (params - i > 1) 255 | sinesize = atoi(args[++i]); 256 | else { 257 | std::cerr << "--sine expects an integer (length)" << std::endl; 258 | return -1; 259 | } 260 | if (params - i > 1) 261 | sineperiod = atof(args[++i]); 262 | else { 263 | std::cerr << "--sine expects an integer (length) followed by a " 264 | "float (freq) " 265 | << std::endl; 266 | return -1; 267 | } 268 | continue; 269 | } 270 | if (strcmp(args[i], "--times") == 0) { 271 | if (params - i > 1) 272 | times = atoi(args[++i]); 273 | else { 274 | std::cerr << "--times expects an integer" << std::endl; 275 | return -1; 276 | } 277 | continue; 278 | } 279 | } 280 | std::cout << "# we report timings (in seconds) so lower is better" << std::endl; 281 | for (int window = windowbegin; window < windowend; ++window) { 282 | if (whitesize > 0) { 283 | std::cout << "# window = " << window << " whitesize = " << whitesize 284 | << " times = " << times << " doslow = " << doslow 285 | << std::endl; 286 | assert(window + 1 < whitesize); 287 | timings(window, whitesize, times, doslow); 288 | } else if (walksize > 0) { 289 | std::cout << "# window = " << window << " walksize = " << walksize 290 | << " times = " << times << " doslow = " << doslow 291 | << std::endl; 292 | walktimings(window, walksize, times, doslow); 293 | 294 | } else if (sinesize > 0) { 295 | std::cout << "# window = " << window << " sinesize = " << sinesize 296 | << " sine period " << sineperiod << " times = " << times 297 | << " doslow = " << doslow << std::endl; 298 | assert(window + 1 < sinesize); 299 | sinetimings(window, sinesize, sineperiod, times, doslow); 300 | } else { 301 | if ((data.empty()) && cininput) { 302 | data = getcin(); 303 | std::cout << "# window = " << window << " times = " << times 304 | << " doslow = " << doslow << std::endl; 305 | process(data, window, times, doslow); 306 | } else { 307 | std::cout << "Generating sine waves." << std::endl; 308 | for (uint width = 3; width <= 100; ++width) { 309 | std::cout << "Using a min/max width of " << width 310 | << std::endl; 311 | sinetimings(width); 312 | } 313 | } 314 | } 315 | } 316 | return 0; 317 | } 318 | -------------------------------------------------------------------------------- /runningmaxmin.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2006-..., Daniel Lemire 3 | * Streaming Maximum-Minimum Filter Using No More than 3 Comparisons per Element 4 | * 5 | * This program is free software; you can 6 | * redistribute it and/or modify it under the terms of the GNU General Public 7 | * License as published by the Free Software Foundation (version 2). This 8 | * program is distributed in the hope that it will be useful, but WITHOUT ANY 9 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 10 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 11 | * details. You should have received a copy of the GNU General Public License 12 | * along with this program; if not, write to the Free Software Foundation, 13 | * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 14 | */ 15 | 16 | #ifndef RUNNINGMAXMIN_H 17 | #define RUNNINGMAXMIN_H 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "common.h" 27 | #include "deque.h" 28 | 29 | inline void display(const std::vector & a) { 30 | for (floattype i : a) 31 | std::cout << i << " "; 32 | std::cout << std::endl; 33 | } 34 | 35 | class minmaxfilter { 36 | public: 37 | virtual std::vector & getmaxvalues() = 0; 38 | virtual std::vector & getminvalues() = 0; 39 | 40 | virtual ~minmaxfilter() = default; 41 | }; 42 | 43 | /** 44 | * This is the naive algorithm one might try first. 45 | */ 46 | class slowmaxmin : public minmaxfilter { 47 | public: 48 | slowmaxmin(std::vector & array, int width) 49 | : maxvalues(array.size() - width + 1), 50 | minvalues(array.size() - width + 1) { 51 | floattype maxvalue, minvalue; 52 | for (uint s = 0; s < array.size() - width + 1; ++s) { 53 | maxvalue = array[s]; 54 | minvalue = array[s]; 55 | // could be done with iterators 56 | for (uint k = s + 1; k < s + width; ++k) { 57 | if (maxvalue < array[k]) 58 | maxvalue = array[k]; 59 | if (minvalue > array[k]) 60 | minvalue = array[k]; 61 | } 62 | maxvalues[s] = maxvalue; 63 | minvalues[s] = minvalue; 64 | } 65 | } 66 | std::vector & getmaxvalues() { 67 | return maxvalues; 68 | } 69 | std::vector & getminvalues() { 70 | return minvalues; 71 | } 72 | std::vector maxvalues; 73 | std::vector minvalues; 74 | }; 75 | 76 | /** 77 | * This is an implementation of the patented Gil-Kimmel algorithm. 78 | * Could be rewritten to use less memory. 79 | */ 80 | class GilKimmel : public minmaxfilter { 81 | public: 82 | GilKimmel(std::vector & array, int width) 83 | : maxvalues(array.size() - width + 1), 84 | minvalues(array.size() - width + 1) { 85 | std::vector R(array.size() + 1); 86 | std::vector S(array.size() + 1); 87 | computePrefixSuffixMax(R, S, array, 88 | width); // implements the cut in the middle trick 89 | for (int j = 0; j < static_cast(array.size()) - width + 1; 90 | j += width) { 91 | const int endofblock = 92 | std::min(j + width, static_cast(array.size()) - width + 1); 93 | int begin = j; 94 | int end = endofblock; 95 | int midpoint = (end - begin + 1) / 2 + begin; 96 | while (midpoint != end) { 97 | if (S[midpoint + width - 1] <= R[midpoint]) { 98 | begin = midpoint; 99 | midpoint = (end - begin + 1) / 2 + begin; 100 | } else { 101 | end = midpoint; 102 | midpoint = (end - begin + 1) / 2 + begin; 103 | } 104 | } 105 | for (int jj = j; jj < midpoint; ++jj) { 106 | maxvalues[jj] = R[jj]; 107 | } 108 | for (int jj = midpoint; jj < endofblock; ++jj) { 109 | maxvalues[jj] = S[jj + width - 1]; 110 | } 111 | } 112 | computePrefixSuffixMin(R, S, array, 113 | width); // implements the cut in the middle trick 114 | for (int j = 0; j < static_cast(array.size()) - width + 1; 115 | j += width) { 116 | const int endofblock = 117 | std::min(j + width, static_cast(array.size()) - width + 1); 118 | int begin = j; 119 | int end = endofblock; 120 | int midpoint = (end - begin + 1) / 2 + begin; 121 | while (midpoint != end) { 122 | if (S[midpoint + width - 1] >= R[midpoint]) { 123 | begin = midpoint; 124 | midpoint = (end - begin + 1) / 2 + begin; 125 | } else { 126 | end = midpoint; 127 | midpoint = (end - begin + 1) / 2 + begin; 128 | } 129 | } 130 | for (int jj = j; jj < midpoint; ++jj) { 131 | minvalues[jj] = R[jj]; 132 | } 133 | for (int jj = midpoint; jj < endofblock; ++jj) { 134 | minvalues[jj] = S[jj + width - 1]; 135 | } 136 | } 137 | } 138 | void computePrefixSuffixMax(std::vector & R, 139 | std::vector & S, 140 | const std::vector & array, 141 | const int width) { 142 | for (int j = 0; j < static_cast(array.size()); j += width) { 143 | const int begin = j; 144 | const int end = std::min(static_cast(array.size()), j + width); 145 | const int midpoint = (end - begin + 1) / 2 + begin; 146 | S[begin] = array[begin]; 147 | for (int jj = begin + 1; jj < midpoint; ++jj) { 148 | S[jj] = std::max(array[jj], S[jj - 1]); 149 | } 150 | R[end - 1] = array[end - 1]; 151 | for (int jj = end - 2; jj >= midpoint; --jj) { 152 | R[jj] = std::max(R[jj + 1], array[jj]); 153 | } 154 | if (std::max(R[midpoint], S[midpoint - 1]) == R[midpoint]) { 155 | for (int jj = midpoint; jj < end; ++jj) 156 | S[jj] = std::max(array[jj], S[jj - 1]); 157 | for (int jj = midpoint - 1; jj >= begin; --jj) 158 | R[jj] = R[midpoint]; 159 | } else { 160 | for (int jj = midpoint - 1; jj >= begin; --jj) 161 | R[jj] = std::max(R[jj + 1], array[jj]); 162 | for (int jj = midpoint; jj < end; ++jj) 163 | S[jj] = S[midpoint - 1]; 164 | } 165 | } 166 | } 167 | 168 | void computePrefixSuffixMin(std::vector & R, 169 | std::vector & S, 170 | const std::vector & array, 171 | const int width) { 172 | for (int j = 0; j < static_cast(array.size()); j += width) { 173 | const int begin = j; 174 | const int end = std::min(static_cast(array.size()), j + width); 175 | const int midpoint = (end - begin + 1) / 2 + begin; 176 | S[begin] = array[begin]; 177 | for (int jj = begin + 1; jj < midpoint; ++jj) { 178 | S[jj] = std::min(array[jj], S[jj - 1]); 179 | } 180 | R[end - 1] = array[end - 1]; 181 | for (int jj = end - 2; jj >= midpoint; --jj) { 182 | R[jj] = std::min(R[jj + 1], array[jj]); 183 | } 184 | if (std::min(R[midpoint], S[midpoint - 1]) == R[midpoint]) { 185 | for (int jj = midpoint; jj < end; ++jj) 186 | S[jj] = std::min(array[jj], S[jj - 1]); 187 | for (int jj = midpoint - 1; jj >= begin; --jj) 188 | R[jj] = R[midpoint]; 189 | } else { 190 | for (int jj = midpoint - 1; jj >= begin; --jj) 191 | R[jj] = std::min(R[jj + 1], array[jj]); 192 | for (int jj = midpoint; jj < end; ++jj) 193 | S[jj] = S[midpoint - 1]; 194 | } 195 | } 196 | } 197 | std::vector & getmaxvalues() { 198 | return maxvalues; 199 | } 200 | std::vector & getminvalues() { 201 | return minvalues; 202 | } 203 | std::vector maxvalues; 204 | std::vector minvalues; 205 | }; 206 | 207 | /** 208 | * This should be very close to the van Herk algorithm. 209 | */ 210 | class vanHerkGilWermanmaxmin : public minmaxfilter { 211 | public: 212 | vanHerkGilWermanmaxmin(std::vector & array, int width) 213 | : maxvalues(array.size() - width + 1), 214 | minvalues(array.size() - width + 1) { 215 | std::vector R(width); 216 | std::vector S(width); 217 | for (uint j = 0; j < array.size() - width + 1; j += width) { 218 | uint Rpos = std::min(j + width - 1, 219 | static_cast(array.size() - 1)); 220 | R[0] = array[Rpos]; 221 | for (uint i = Rpos - 1; i + 1 > j; i -= 1) 222 | R[Rpos - i] = std::max(R[Rpos - i - 1], array[i]); 223 | S[0] = array[Rpos]; 224 | uint m1 = std::min(j + 2 * width - 1, 225 | static_cast(array.size())); 226 | for (uint i = Rpos + 1; i < m1; ++i) { 227 | S[i - Rpos] = std::max(S[i - Rpos - 1], array[i]); 228 | } 229 | for (uint i = 0; i < m1 - Rpos; i += 1) 230 | maxvalues[j + i] = std::max(S[i], R[(Rpos - j + 1) - i - 1]); 231 | } 232 | for (uint j = 0; j < array.size() - width + 1; j += width) { 233 | uint Rpos = std::min(j + width - 1, 234 | static_cast(array.size() - 1)); 235 | R[0] = array[Rpos]; 236 | for (uint i = Rpos - 1; i + 1 > j; i -= 1) 237 | R[Rpos - i] = std::min(R[Rpos - i - 1], array[i]); 238 | S[0] = array[Rpos]; 239 | uint m1 = std::min(j + 2 * width - 1, 240 | static_cast(array.size())); 241 | for (uint i = Rpos + 1; i < m1; ++i) { 242 | S[i - Rpos] = std::min(S[i - Rpos - 1], array[i]); 243 | } 244 | for (uint i = 0; i < m1 - Rpos; i += 1) 245 | minvalues[j + i] = std::min(S[i], R[(Rpos - j + 1) - i - 1]); 246 | } 247 | assert(maxvalues.size() == array.size() - width + 1); 248 | assert(minvalues.size() == array.size() - width + 1); 249 | } 250 | std::vector & getmaxvalues() { 251 | return maxvalues; 252 | } 253 | std::vector & getminvalues() { 254 | return minvalues; 255 | } 256 | std::vector maxvalues; 257 | std::vector minvalues; 258 | }; 259 | 260 | /** 261 | * implementation of the streaming algorithm 262 | */ 263 | class lemiremaxmin : public minmaxfilter { 264 | public: 265 | lemiremaxmin(std::vector & array, uint width) 266 | : maxvalues(array.size() - width + 1), 267 | minvalues(array.size() - width + 1) { 268 | std::deque maxfifo, minfifo; 269 | for (uint i = 1; i < width; ++i) { 270 | if (array[i] > array[i - 1]) { // overshoot 271 | minfifo.push_back(i - 1); 272 | while (!maxfifo.empty()) { 273 | if (array[i] <= array[maxfifo.back()]) { 274 | if (i == width + maxfifo.front()) 275 | maxfifo.pop_front(); 276 | break; 277 | } 278 | maxfifo.pop_back(); 279 | } 280 | } else { 281 | maxfifo.push_back(i - 1); 282 | while (!minfifo.empty()) { 283 | if (array[i] >= array[minfifo.back()]) { 284 | if (i == width + minfifo.front()) 285 | minfifo.pop_front(); 286 | break; 287 | } 288 | minfifo.pop_back(); 289 | } 290 | } 291 | } 292 | for (uint i = width; i < array.size(); ++i) { 293 | maxvalues[i - width] = 294 | array[maxfifo.empty() ? i - 1 : maxfifo.front()]; 295 | minvalues[i - width] = 296 | array[minfifo.empty() ? i - 1 : minfifo.front()]; 297 | if (array[i] > array[i - 1]) { // overshoot 298 | minfifo.push_back(i - 1); 299 | if (i == width + minfifo.front()) 300 | minfifo.pop_front(); 301 | while (!maxfifo.empty()) { 302 | if (array[i] <= array[maxfifo.back()]) { 303 | if (i == width + maxfifo.front()) 304 | maxfifo.pop_front(); 305 | break; 306 | } 307 | maxfifo.pop_back(); 308 | } 309 | } else { 310 | maxfifo.push_back(i - 1); 311 | if (i == width + maxfifo.front()) 312 | maxfifo.pop_front(); 313 | while (!minfifo.empty()) { 314 | if (array[i] >= array[minfifo.back()]) { 315 | if (i == width + minfifo.front()) 316 | minfifo.pop_front(); 317 | break; 318 | } 319 | minfifo.pop_back(); 320 | } 321 | } 322 | } 323 | maxvalues[array.size() - width] = 324 | array[maxfifo.empty() ? array.size() - 1 : maxfifo.front()]; 325 | minvalues[array.size() - width] = 326 | array[minfifo.empty() ? array.size() - 1 : minfifo.front()]; 327 | } 328 | std::vector & getmaxvalues() { 329 | return maxvalues; 330 | } 331 | std::vector & getminvalues() { 332 | return minvalues; 333 | } 334 | std::vector maxvalues; 335 | std::vector minvalues; 336 | }; 337 | 338 | // actual streaming implementation 339 | class lemiremaxmintruestreaming { 340 | public: 341 | explicit lemiremaxmintruestreaming(uint width) 342 | : up(), lo(), n(0), ww(width) { 343 | init(&up, ww); 344 | init(&lo, ww); 345 | } 346 | 347 | ~lemiremaxmintruestreaming() { 348 | free(&up); 349 | free(&lo); 350 | } 351 | 352 | void update(floattype value) { 353 | if (nonempty(&up) != 0) { 354 | if (value > tailvalue(&up)) { 355 | prunetail(&up); 356 | while (((nonempty(&up)) != 0) && (value >= tailvalue(&up))) { 357 | prunetail(&up); 358 | } 359 | } else { 360 | prunetail(&lo); 361 | while (((nonempty(&lo)) != 0) && (value <= tailvalue(&lo))) { 362 | prunetail(&lo); 363 | } 364 | } 365 | } 366 | push(&up, n, value); 367 | if (n == ww + headindex(&up)) { 368 | prunehead(&up); 369 | } 370 | 371 | push(&lo, n, value); 372 | if (n == ww + headindex(&lo)) { 373 | prunehead(&lo); 374 | } 375 | n++; 376 | } 377 | 378 | floattype max() { 379 | return headvalue(&up); 380 | } 381 | floattype min() { 382 | return headvalue(&lo); 383 | } 384 | 385 | intfloatqueue up; 386 | intfloatqueue lo; 387 | uint n; 388 | uint ww; 389 | }; 390 | 391 | // wrapper over the streaming version 392 | class lemiremaxminwrap : public minmaxfilter { 393 | public: 394 | lemiremaxminwrap(std::vector & array, uint width) 395 | : maxvalues(array.size() - width + 1), 396 | minvalues(array.size() - width + 1) { 397 | lemiremaxmintruestreaming lts(width); 398 | for (uint i = 0; i < width - 1; ++i) { 399 | lts.update(array[i]); 400 | } 401 | for (uint i = width - 1; i < array.size(); ++i) { 402 | lts.update(array[i]); 403 | maxvalues[i - width + 1] = lts.max(); 404 | minvalues[i - width + 1] = lts.min(); 405 | } 406 | } 407 | std::vector & getmaxvalues() { 408 | return maxvalues; 409 | } 410 | std::vector & getminvalues() { 411 | return minvalues; 412 | } 413 | std::vector maxvalues; 414 | std::vector minvalues; 415 | }; 416 | 417 | #include "mono_wedge.h" 418 | 419 | struct Sample { 420 | floattype value; 421 | uint time; 422 | 423 | bool operator<(const Sample &o) const {return value < o.value;} 424 | bool operator>(const Sample &o) const {return value > o.value;} 425 | }; 426 | // wrapper over the monowedge streaming version 427 | class monowedgewrap : public minmaxfilter { 428 | public: 429 | monowedgewrap(std::vector & array, uint width) 430 | : maxvalues(array.size() - width + 1), 431 | minvalues(array.size() - width + 1) { 432 | std::deque max_wedge; 433 | std::deque min_wedge; 434 | for (uint i = 0; i < width - 1; ++i) { 435 | Sample sample = {array[i], i}; 436 | mono_wedge::max_wedge_update(max_wedge, sample); 437 | mono_wedge::min_wedge_update(min_wedge, sample); 438 | } 439 | for (uint i = width; i < array.size(); ++i) { 440 | Sample sample = {array[i], i}; 441 | mono_wedge::max_wedge_update(max_wedge, sample); 442 | mono_wedge::min_wedge_update(min_wedge, sample); 443 | while (max_wedge.front().time <= i-width) max_wedge.pop_front(); 444 | while (min_wedge.front().time <= i-width) min_wedge.pop_front(); 445 | maxvalues[i - width + 1] = max_wedge.front().value; 446 | minvalues[i - width + 1] = min_wedge.front().value; 447 | } 448 | } 449 | std::vector & getmaxvalues() { 450 | return maxvalues; 451 | } 452 | std::vector & getminvalues() { 453 | return minvalues; 454 | } 455 | std::vector maxvalues; 456 | std::vector minvalues; 457 | }; 458 | 459 | 460 | /** 461 | * implementation of the bitmap-based streaming algorithm 462 | */ 463 | class lemirebitmapmaxmin : public minmaxfilter { 464 | public: 465 | // TODO: make the code portable to non-GCC-like compilers 466 | // TODO: extend beyond 64-bit to include 128-bit 467 | lemirebitmapmaxmin(std::vector & array, const uint width) 468 | : maxvalues(array.size() - width + 1), 469 | minvalues(array.size() - width + 1) { 470 | assert(width <= sizeof(unsigned long) * 8); 471 | unsigned long maxfifo = 0; 472 | unsigned long minfifo = 0; 473 | for (uint i = 1; i < width; ++i) { 474 | if (array[i] > array[i - 1]) { // overshoot 475 | minfifo |= 1; 476 | minfifo <<= 1; 477 | maxfifo <<= 1; 478 | while (maxfifo != 0) { 479 | const long t = maxfifo & -maxfifo; 480 | const int bitpos = __builtin_popcountl(t - 1); 481 | if (array[i] <= array[i - bitpos]) { 482 | break; 483 | } 484 | maxfifo ^= t; 485 | } 486 | } else { 487 | maxfifo |= 1; 488 | minfifo <<= 1; 489 | maxfifo <<= 1; 490 | while (minfifo != 0) { 491 | const long t = minfifo & -minfifo; 492 | const int bitpos = __builtin_popcountl(t - 1); 493 | if (array[i] >= array[i - bitpos]) { 494 | break; 495 | } 496 | minfifo ^= t; 497 | } 498 | } 499 | } 500 | unsigned long mask = ~0l; 501 | if (width < sizeof(unsigned long) * 8) { 502 | mask = (1UL << width) - 1; 503 | } 504 | for (uint i = width; i < array.size(); ++i) { 505 | maxfifo &= mask; 506 | minfifo &= mask; 507 | if (maxfifo == 0) 508 | maxvalues[i - width] = array[i - 1]; 509 | else { 510 | maxvalues[i - width] = array[i - (sizeof(unsigned long) * 8 - 511 | __builtin_clzl(maxfifo))]; 512 | } 513 | if (minfifo == 0) 514 | minvalues[i - width] = array[i - 1]; 515 | else { 516 | minvalues[i - width] = array[i - (sizeof(unsigned long) * 8 - 517 | __builtin_clzl(minfifo))]; 518 | } 519 | if (array[i] > array[i - 1]) { // overshoot 520 | minfifo |= 1; 521 | minfifo <<= 1; 522 | maxfifo <<= 1; 523 | while (maxfifo != 0) { 524 | const long t = maxfifo & -maxfifo; 525 | const int bitpos = __builtin_popcountl(t - 1); 526 | if (array[i] <= array[i - bitpos]) { 527 | break; 528 | } 529 | maxfifo ^= t; 530 | } 531 | } else { 532 | maxfifo |= 1; 533 | maxfifo <<= 1; 534 | minfifo <<= 1; 535 | while (minfifo != 0) { 536 | const long t = minfifo & -minfifo; 537 | const int bitpos = __builtin_popcountl(t - 1); 538 | if (array[i] >= array[i - bitpos]) { 539 | break; 540 | } 541 | minfifo ^= t; 542 | } 543 | } 544 | } 545 | maxfifo = maxfifo & mask; 546 | minfifo = minfifo & mask; 547 | if (maxfifo == 0) 548 | maxvalues[array.size() - width] = array[array.size() - 1]; 549 | else 550 | maxvalues[array.size() - width] = 551 | array[array.size() - 552 | (sizeof(unsigned long) * 8 - __builtin_clzl(maxfifo))]; 553 | if (minfifo == 0) 554 | minvalues[array.size() - width] = array[array.size() - 1]; 555 | else 556 | minvalues[array.size() - width] = 557 | array[array.size() - 558 | (sizeof(unsigned long) * 8 - __builtin_clzl(minfifo))]; 559 | } 560 | std::vector & getmaxvalues() { 561 | return maxvalues; 562 | } 563 | std::vector & getminvalues() { 564 | return minvalues; 565 | } 566 | std::vector maxvalues; 567 | std::vector minvalues; 568 | }; 569 | 570 | /** 571 | * simplest implementation (pseudocode-like) 572 | */ 573 | class simplelemiremaxmin : public minmaxfilter { 574 | public: 575 | simplelemiremaxmin(std::vector & array, uint width) 576 | : maxvalues(array.size() - width + 1), 577 | minvalues(array.size() - width + 1) { 578 | std::deque maxfifo, minfifo; 579 | maxfifo.push_back(0); 580 | minfifo.push_back(0); 581 | for (uint i = 1; i < width; ++i) { 582 | if (array[i] > array[i - 1]) { // overshoot 583 | maxfifo.pop_back(); 584 | while (!maxfifo.empty()) { 585 | if (array[i] <= array[maxfifo.back()]) 586 | break; 587 | maxfifo.pop_back(); 588 | } 589 | } else { 590 | minfifo.pop_back(); 591 | while (!minfifo.empty()) { 592 | if (array[i] >= array[minfifo.back()]) 593 | break; 594 | minfifo.pop_back(); 595 | } 596 | } 597 | maxfifo.push_back(i); 598 | minfifo.push_back(i); 599 | } 600 | for (uint i = width; i < array.size(); ++i) { 601 | maxvalues[i - width] = array[maxfifo.front()]; 602 | minvalues[i - width] = array[minfifo.front()]; 603 | if (array[i] > array[i - 1]) { // overshoot 604 | maxfifo.pop_back(); 605 | while (!maxfifo.empty()) { 606 | if (array[i] <= array[maxfifo.back()]) 607 | break; 608 | maxfifo.pop_back(); 609 | } 610 | } else { 611 | minfifo.pop_back(); 612 | while (!minfifo.empty()) { 613 | if (array[i] >= array[minfifo.back()]) 614 | break; 615 | minfifo.pop_back(); 616 | } 617 | } 618 | maxfifo.push_back(i); 619 | minfifo.push_back(i); 620 | if (i == width + maxfifo.front()) 621 | maxfifo.pop_front(); 622 | else if (i == width + minfifo.front()) 623 | minfifo.pop_front(); 624 | } 625 | maxvalues[array.size() - width] = array[maxfifo.front()]; 626 | minvalues[array.size() - width] = array[minfifo.front()]; 627 | } 628 | std::vector & getmaxvalues() { 629 | return maxvalues; 630 | } 631 | std::vector & getminvalues() { 632 | return minvalues; 633 | } 634 | std::vector maxvalues; 635 | std::vector minvalues; 636 | }; 637 | 638 | #endif 639 | -------------------------------------------------------------------------------- /unit.cpp: -------------------------------------------------------------------------------- 1 | #include "runningmaxmin.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | bool compare(std::vector & a, std::vector & b) { 9 | if (a.size() != b.size()) 10 | return false; 11 | for (uint k = 0; k < a.size(); ++k) 12 | if (a[k] != b[k]) 13 | return false; 14 | return true; 15 | } 16 | bool compare(minmaxfilter & a, minmaxfilter & b) { 17 | return static_cast((compare(a.getmaxvalues(), b.getmaxvalues())) & 18 | static_cast(compare(a.getminvalues(), 19 | b.getminvalues()))) != 0; 20 | } 21 | 22 | void display(minmaxfilter & a) { 23 | display(a.getmaxvalues()); 24 | display(a.getminvalues()); 25 | } 26 | 27 | void test(std::vector data, uint width = 3) { 28 | 29 | slowmaxmin A(data, width); 30 | lemiremaxmin C(data, width); 31 | lemiremaxminwrap Cw(data, width); 32 | simplelemiremaxmin G(data, width); 33 | GilKimmel D(data, width); 34 | vanHerkGilWermanmaxmin B(data, width); 35 | lemirebitmapmaxmin E(data, width); 36 | monowedgewrap M(data,width); 37 | if (!compare(A, M)) { 38 | std::cout << "array " << std::endl; 39 | display(data); 40 | std::cout << "solution " << std::endl; 41 | display(A); 42 | std::cout << "monowedgewrap " << std::endl; 43 | display(M); 44 | } 45 | if (!compare(A, Cw)) { 46 | std::cout << "array " << std::endl; 47 | display(data); 48 | std::cout << "solution " << std::endl; 49 | display(A); 50 | std::cout << "lemirew " << std::endl; 51 | display(Cw); 52 | } 53 | 54 | if (!compare(A, D)) { 55 | std::cout << "array " << std::endl; 56 | display(data); 57 | std::cout << "solution " << std::endl; 58 | display(A); 59 | std::cout << "lemiremaxmin " << std::endl; 60 | display(D); 61 | } 62 | if (!compare(A, C)) { 63 | std::cout << "array " << std::endl; 64 | display(data); 65 | std::cout << "solution " << std::endl; 66 | display(A); 67 | std::cout << "lemiremaxmin " << std::endl; 68 | display(C); 69 | } 70 | if (!compare(A, B)) { 71 | std::cout << "array " << std::endl; 72 | display(data); 73 | std::cout << "solution " << std::endl; 74 | display(A); 75 | std::cout << "vanHerk " << std::endl; 76 | display(B); 77 | } 78 | assert(compare(A, Cw)); 79 | 80 | assert(compare(A, C)); 81 | assert(compare(A, B)); 82 | assert(compare(A, D)); 83 | assert(compare(A, G)); 84 | assert(compare(A, E)); 85 | } 86 | 87 | void unit() { 88 | uint size = 20; 89 | std::vector data(size); 90 | for (uint k = 0; k < size; ++k) 91 | data[k] = size - k; 92 | test(data, 6); 93 | test(data, 2); 94 | test(data, 3); 95 | test(data, 4); 96 | test(data, 5); 97 | for (uint k = 0; k < size; ++k) 98 | data[k] = k; 99 | test(data, 2); 100 | test(data, 3); 101 | test(data, 4); 102 | test(data, 5); 103 | for (uint j = 0; j < 1000; ++j) { 104 | for (uint k = 0; k < size; ++k) 105 | data[k] = rand(); 106 | test(data, 2); 107 | test(data, 3); 108 | test(data, 4); 109 | test(data, 5); 110 | } 111 | } 112 | 113 | int main() { 114 | unit(); 115 | std::cout << "Code appears ok." << std::endl; 116 | return 0; 117 | } 118 | --------------------------------------------------------------------------------