forked from VolunteerComputingHelp/boinc-app-eah-brp
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathrngmed.c
341 lines (290 loc) · 10.9 KB
/
rngmed.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
/*----------------------------------------------------------------------------
*
* File Name: rngmed.c
* Authors: B. Machenschalk
*
*
* Description: efficient computation of a running median
*
* This implements https://dcc.ligo.org/LIGO-T030168/public
* Soumya Mohanty "Efficient Algorithm for Computing a Running Median" (2003/4)
*
*----------------------------------------------------------------------------
*/
/* compilation flags */
#define NEWCHECKPOINT /* add a checkpoint for the median */
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include "rngmed.h"
/*Used in running qsort*/
static int rngmed_qsortindex(const void *elem1, const void *elem2)
{
struct qsnode
{
float value;
unsigned int index;
};
const struct qsnode *A = (const struct qsnode*) elem1;
const struct qsnode *B = (const struct qsnode*) elem2;
if (B->value > A->value)
return -1;
else if (A->value > B->value)
return 1;
else if (A->index > B->index)
return -1;
else
return 1;
}
void rngmed(const float *input, const unsigned int length, const unsigned int bsize, float *medians)
{
/* a single "node"
lesser points to the next node with less or equal value
greater points to the next node with greater or equal value
an index == blocksize is an end marker
*/
struct node
{
float value;
unsigned int lesser;
unsigned int greater;
};
/* a node of the quicksort array */
struct qsnode
{
float value;
unsigned int index;
};
const unsigned int nil = bsize; /* invalid index used as end marker */
const int isodd = bsize&1; /* bsize is odd = median is a single element */
struct node* nodes; /* array of nodes, will be of size blocksize */
struct qsnode* qsnodes; /* array of indices for initial qsort */
unsigned int* checkpts; /* array of checkpoints */
unsigned int ncheckpts,stepchkpts; /* checkpoints: number and distance between */
unsigned int oldestnode; /* index of "oldest" node */
unsigned int i; /* loop counter (up to input length) */
int j; /* loop counter (might get negative) */
unsigned int nmedian; /* current median, outer loop counter */
unsigned int midpoint; /* index of middle node in sorting order */
unsigned int mdnnearest; /* checkpoint "nearest" to the median */
unsigned int nextnode; /* node after an insertion point,
also used to find a median */
unsigned int prevnode; /* node before an insertion point */
unsigned int rightcheckpt; /* checkpoint 'right' of an insertion point */
float oldvalue,newvalue; /* old + new value of the node being replaced */
unsigned int oldlesser,oldgreater; /* remember the pointers of the replaced node */
/* create nodes array */
nodes = (struct node*)calloc(bsize, sizeof(struct node));
/* determine checkpoint positions */
stepchkpts = sqrt(bsize);
/* the old form
ncheckpts = bsize/stepchkpts;
caused too less checkpoints at the end, leading to break the
cost calculation */
ncheckpts = ceil((float)bsize/(float)stepchkpts);
/* set checkpoint nearest to the median and offset of the median to it */
midpoint = (bsize+(bsize&1)) / 2 - 1;
#ifndef NEWCHECKPOINT
mdnnearest = floor(midpoint / stepchkpts);
mdnoffset = midpoint - mdnnearest * stepchkpts;
#else
/* this becomes the median checkpoint */
/* Hint: Bsize = 18, 32 */
mdnnearest = ceil((float)midpoint / (float)stepchkpts);
#endif
#ifdef NEWCHECKPOINT
/* add a checkpoint for the median if necessary */
if (ceil((float)midpoint / (float)stepchkpts) != (float)midpoint / (float)stepchkpts)
ncheckpts++;
#endif
/* create checkpoints array */
checkpts = (unsigned int*)calloc(ncheckpts,sizeof(unsigned int));
/* create array for qsort */
qsnodes = (struct qsnode*)calloc(bsize, sizeof(struct qsnode));
/* init qsort array
the nodes get their values from the input,
the indices are only identities qi[0]=0,qi[1]=1,... */
for(i = 0; i < bsize; i++)
{
qsnodes[i].value = input[i];
qsnodes[i].index = i;
}
/* sort qsnodes by value and index(!) */
qsort(qsnodes, bsize, sizeof(struct qsnode), rngmed_qsortindex);
/* init nodes array */
for(i = 0; i < bsize; i++)
nodes[i].value = input[i];
for(i = 1; i < bsize - 1; i++)
{
nodes[qsnodes[i-1].index].greater = qsnodes[i].index;
nodes[qsnodes[i+1].index].lesser = qsnodes[i].index;
}
nodes[qsnodes[0].index].lesser = nil; /* end marker */
nodes[qsnodes[1].index].lesser = qsnodes[0].index;
nodes[qsnodes[bsize-2].index].greater = qsnodes[bsize-1].index;
nodes[qsnodes[bsize-1].index].greater = nil; /* end marker */
/* setup checkpoints */
#ifndef NEWCHECKPOINT
for(i = 0; i < ncheckpts; i++)
checkpts[i] = qsnodes[i*stepchkpts].index;
#else
/* j is the current checkpoint
i is the stepping
they are out of sync after a median checkpoint has been added */
for(i = 0, j = 0; j < ncheckpts; j++, i++)
{
if (j == mdnnearest) {
checkpts[j] = qsnodes[midpoint].index;
if (i*stepchkpts != midpoint)
j++;
}
checkpts[j] = qsnodes[i*stepchkpts].index;
}
#endif
/* don't need the qsnodes anymore */
free(qsnodes);
/* find first median */
nextnode = checkpts[mdnnearest];
#ifndef NEWCHECKPOINT
for(i=0; i<mdnoffset; i++)
nextnode = nodes[nextnode].greater;
#endif
if(isodd)
medians[0] = nodes[nextnode].value;
else
medians[0] = (nodes[nextnode].value + nodes[nodes[nextnode].greater].value) / 2.0;
/* the "oldest" node (first in sequence) is the one with index 0 */
oldestnode = 0;
/* outer loop: find a median with each iteration */
for(nmedian = 1; nmedian < length - bsize + 1; nmedian++)
{
/* remember value of sample to be deleted */
oldvalue = nodes[oldestnode].value;
/* get next value to be inserted from input */
newvalue = input[nmedian+bsize-1];
/** find point of insertion: **/
/* find checkpoint greater or equal newvalue */
/* possible optimisation: use bisectional search instaed of linear */
for(rightcheckpt = 0; rightcheckpt < ncheckpts; rightcheckpt++)
if(newvalue <= nodes[checkpts[rightcheckpt]].value)
break;
/* assume we are inserting at the beginning: */
prevnode = nil;
if (rightcheckpt == 0)
/* yes, we are */
nextnode = checkpts[0];
else
{
/* we're beyond the first checkpoint, find the node we're
inserting at: */
nextnode = checkpts[rightcheckpt-1]; /* this also works if we found no
checkpoint > newvalue, as
then rightcheckpt == ncheckpts */
/* the following loop is always ran at least once, as
nodes[checkpts[rightcheckpt-1]].value < newvalue
after 'find checkpoint' loop */
while((nextnode != nil) && (newvalue > nodes[nextnode].value))
{
prevnode = nextnode;
nextnode = nodes[nextnode].greater;
}
}
/* ok, we have:
- case 1: insert at beginning: prevnode == nil, nextnode ==
smallest node
- case 2: insert at end: nextnode == nil (terminated loop),
prevnode == last node
- case 3: ordinary insert: insert between prevnode and nextnode
*/
/* insertion deletion and shifting are unnecessary if we are replacing
at the same pos */
if ((oldestnode != prevnode) && (oldestnode != nextnode))
{
/* delete oldest node from list */
if (nodes[oldestnode].lesser == nil)
{
/* case 1: at beginning */
nodes[nodes[oldestnode].greater].lesser = nil;
/* this shouldn't be necessary, but doesn't harm */
checkpts[0] = nodes[oldestnode].greater;
}
else if (nodes[oldestnode].greater == nil)
/* case 2: at end */
nodes[nodes[oldestnode].lesser].greater = nil;
else
{
/* case 3: anywhere else */
nodes[nodes[oldestnode].lesser].greater = nodes[oldestnode].greater;
nodes[nodes[oldestnode].greater].lesser = nodes[oldestnode].lesser;
}
/* remember the old links for special case in shifting below */
oldgreater = nodes[oldestnode].greater;
oldlesser = nodes[oldestnode].lesser;
/* insert new node - actually we reuse the oldest one */
/* the value is set outside the outer "if" */
nodes[oldestnode].lesser = prevnode;
nodes[oldestnode].greater = nextnode;
if (prevnode != nil)
nodes[prevnode].greater = oldestnode;
if (nextnode != nil)
nodes[nextnode].lesser = oldestnode;
/* shift checkpoints */
/* if there is a sequence of identical values, new values are
inserted
always at the left end. Thus, the oldest value has to be the rightmost
of such a sequence. This requires proper init.
This makes shifting of the checkpoints rather easy:
if (oldvalue < newvalue), all checkpoints with
oldvalue <(=) chkptvalue < newvalue are shifted,
if (newvalue <= oldvalue), all checkpoints with
newvalue <= chkptvalue <= oldvalue are shifted.
<(=) means that only a checkpoint at the deleted node must be
shifted, no other accidently pointing to the same value.
Care is needed if a checkpoint to shift is the node we just deleted
We start at the checkpoint we know to be closest to the new node
satifying the above condition:
rightcheckpt-1 if (oldvalue < newvalue)
rightcheckpt othewise
and proceed in the direction towards the deleted node
*/
if (oldvalue < newvalue)
{
/* we shift towards larger values */
for(j = rightcheckpt - 1; (j > 0) && (nodes[checkpts[j]].value >= oldvalue); j--)
if (nodes[checkpts[j]].value > oldvalue)
checkpts[j] = nodes[checkpts[j]].greater;
else if (checkpts[j] == oldestnode)
checkpts[j] = oldgreater;
}
else /* newvalue <= oldvalue */
/* we shift towards smaller values */
for(i = rightcheckpt; (i < ncheckpts) && (nodes[checkpts[i]].value <= oldvalue); i++)
if (checkpts[i] == oldestnode)
checkpts[i] = oldlesser;
else
checkpts[i] = nodes[checkpts[i]].lesser;
} /* if ((oldestnode != prevnode) && (oldestnode != nextnode)) */
/* in any case set new value */
nodes[oldestnode].value = newvalue;
/* find median */
if (newvalue == oldvalue)
medians[nmedian] = medians[nmedian-1];
else
{
nextnode = checkpts[mdnnearest];
#ifndef NEWCHECKPOINT
for(i = 0; i < mdnoffset; i++)
nextnode = nodes[nextnode].greater;
#endif
if(isodd)
medians[nmedian] = nodes[nextnode].value;
else
medians[nmedian] = (nodes[nextnode].value + nodes[nodes[nextnode].greater].value) / 2.0;
}
/* next oldest node */
oldestnode = (oldestnode + 1) % bsize; /* wrap around */
} /* for (nmedian...) */
/* cleanup */
free(checkpts);
free(nodes);
}