|
Packit |
67cb25 |
/* movstat/medacc.c
|
|
Packit |
67cb25 |
*
|
|
Packit |
67cb25 |
* Copyright (C) 2018 Patrick Alken
|
|
Packit |
67cb25 |
*
|
|
Packit |
67cb25 |
* This program is free software; you can redistribute it and/or modify
|
|
Packit |
67cb25 |
* it under the terms of the GNU General Public License as published by
|
|
Packit |
67cb25 |
* the Free Software Foundation; either version 3 of the License, or (at
|
|
Packit |
67cb25 |
* your option) any later version.
|
|
Packit |
67cb25 |
*
|
|
Packit |
67cb25 |
* This program is distributed in the hope that it will be useful, but
|
|
Packit |
67cb25 |
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
67cb25 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit |
67cb25 |
* General Public License for more details.
|
|
Packit |
67cb25 |
*
|
|
Packit |
67cb25 |
* You should have received a copy of the GNU General Public License
|
|
Packit |
67cb25 |
* along with this program; if not, write to the Free Software
|
|
Packit |
67cb25 |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
Packit |
67cb25 |
*
|
|
Packit |
67cb25 |
* Original copyright notice:
|
|
Packit |
67cb25 |
* Copyright (c) 2011 ashelly.myopenid.com under <http://www.opensource.org/licenses/mit-license>
|
|
Packit |
67cb25 |
*/
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
#include <config.h>
|
|
Packit |
67cb25 |
#include <stdlib.h>
|
|
Packit |
67cb25 |
#include <math.h>
|
|
Packit |
67cb25 |
#include <gsl/gsl_math.h>
|
|
Packit |
67cb25 |
#include <gsl/gsl_errno.h>
|
|
Packit |
67cb25 |
#include <gsl/gsl_movstat.h>
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
#define ItemLess(a,b) ((a)<(b))
|
|
Packit |
67cb25 |
#define ItemMean(a,b) (((a)+(b))/2)
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
#define minCt(m) (((m)->ct-1)/2) /* count of items in minheap */
|
|
Packit |
67cb25 |
#define maxCt(m) (((m)->ct)/2) /* count of items in maxheap */
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
typedef double medacc_type_t;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
typedef struct
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
int n; /* window size */
|
|
Packit |
67cb25 |
int idx; /* position in circular queue */
|
|
Packit |
67cb25 |
int ct; /* count of items in queue */
|
|
Packit |
67cb25 |
medacc_type_t *data; /* circular queue of values, size k */
|
|
Packit |
67cb25 |
int *pos; /* index into `heap` for each value, size 2*k */
|
|
Packit |
67cb25 |
int *heap; /* max/median/min heap holding indices into `data` */
|
|
Packit |
67cb25 |
} medacc_state_t;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
static size_t medacc_size(const size_t n);
|
|
Packit |
67cb25 |
static int medacc_init(const size_t n, void * vstate);
|
|
Packit |
67cb25 |
static int medacc_insert(const medacc_type_t x, void * vstate);
|
|
Packit |
67cb25 |
static int medacc_delete(void * vstate);
|
|
Packit |
67cb25 |
static int medacc_get(void * params, medacc_type_t * result, const void * vstate);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
static int mmless(const medacc_state_t * state, const int i, const int j);
|
|
Packit |
67cb25 |
static int mmexchange(medacc_state_t * state, const int i, const int j);
|
|
Packit |
67cb25 |
static int mmCmpExch(medacc_state_t * state, const int i, const int j);
|
|
Packit |
67cb25 |
static void minSortDown(medacc_state_t * state, int i);
|
|
Packit |
67cb25 |
static void maxSortDown(medacc_state_t * state, int i);
|
|
Packit |
67cb25 |
static int minSortUp(medacc_state_t * state, int i);
|
|
Packit |
67cb25 |
static int maxSortUp(medacc_state_t * state, int i);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
static size_t
|
|
Packit |
67cb25 |
medacc_size(const size_t n)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
size_t size = 0;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
size += sizeof(medacc_state_t);
|
|
Packit |
67cb25 |
size += n * sizeof(medacc_type_t);
|
|
Packit |
67cb25 |
size += 2 * n * sizeof(int);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
return size;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
static int
|
|
Packit |
67cb25 |
medacc_init(const size_t n, void * vstate)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
medacc_state_t * state = (medacc_state_t *) vstate;
|
|
Packit |
67cb25 |
int k = (int) n;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
state->n = n;
|
|
Packit |
67cb25 |
state->ct = 0;
|
|
Packit |
67cb25 |
state->idx = 0;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
state->data = (medacc_type_t *) ((unsigned char *) vstate + sizeof(medacc_state_t));
|
|
Packit |
67cb25 |
state->pos = (int *) ((unsigned char *) state->data + n * sizeof(medacc_type_t));
|
|
Packit |
67cb25 |
state->heap = state->pos + n + (n/2); /* points to middle of storage */
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* set up initial heap fill pattern: median,max,min,max,... */
|
|
Packit |
67cb25 |
while (k--)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
state->pos[k] = ((k + 1)/2) * ((k & 1) ? -1 : 1);
|
|
Packit |
67cb25 |
state->heap[state->pos[k]] = k;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
return GSL_SUCCESS;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
static int
|
|
Packit |
67cb25 |
medacc_insert(const medacc_type_t x, void * vstate)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
medacc_state_t * state = (medacc_state_t *) vstate;
|
|
Packit |
67cb25 |
int isNew = (state->ct < (int) state->n);
|
|
Packit |
67cb25 |
int p = state->pos[state->idx];
|
|
Packit |
67cb25 |
medacc_type_t old = state->data[state->idx];
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
state->data[state->idx] = x;
|
|
Packit |
67cb25 |
state->idx = (state->idx + 1) % state->n;
|
|
Packit |
67cb25 |
state->ct += isNew;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (p > 0) /* new item is in minHeap */
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
if (!isNew && ItemLess(old, x))
|
|
Packit |
67cb25 |
minSortDown(state, p * 2);
|
|
Packit |
67cb25 |
else if (minSortUp(state, p))
|
|
Packit |
67cb25 |
maxSortDown(state, -1);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
else if (p < 0) /* new item is in maxHeap */
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
if (!isNew && ItemLess(x, old))
|
|
Packit |
67cb25 |
maxSortDown(state, p * 2);
|
|
Packit |
67cb25 |
else if (maxSortUp(state, p))
|
|
Packit |
67cb25 |
minSortDown(state, 1);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
else /* new item is at median */
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
if (maxCt(state))
|
|
Packit |
67cb25 |
maxSortDown(state, -1);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (minCt(state))
|
|
Packit |
67cb25 |
minSortDown(state, 1);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
return GSL_SUCCESS;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
static int
|
|
Packit |
67cb25 |
medacc_delete(void * vstate)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
medacc_state_t * state = (medacc_state_t *) vstate;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (state->ct > 0)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
int p = state->pos[(state->idx - state->ct + state->n) % state->n];
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (p > 0) /* oldest item is in minHeap */
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
mmexchange(state, p, minCt(state));
|
|
Packit |
67cb25 |
--(state->ct);
|
|
Packit |
67cb25 |
minSortDown(state, 2 * p);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
else if (p < 0) /* oldest item is in maxHeap */
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
mmexchange(state, p, maxCt(state));
|
|
Packit |
67cb25 |
--(state->ct);
|
|
Packit |
67cb25 |
maxSortDown(state, 2 * p);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
else if (p == 0) /* oldest item is at median */
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
return GSL_SUCCESS;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* returns median (or average of 2 when item count is even) */
|
|
Packit |
67cb25 |
static int
|
|
Packit |
67cb25 |
medacc_get(void * params, medacc_type_t * result, const void * vstate)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
const medacc_state_t * state = (const medacc_state_t *) vstate;
|
|
Packit |
67cb25 |
medacc_type_t median = state->data[state->heap[0]];
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
(void) params;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if ((state->ct & 1) == 0)
|
|
Packit |
67cb25 |
median = ItemMean(median, state->data[state->heap[-1]]);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
*result = median;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
return GSL_SUCCESS;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* returns 1 if heap[i] < heap[j] */
|
|
Packit |
67cb25 |
static int
|
|
Packit |
67cb25 |
mmless(const medacc_state_t * state, const int i, const int j)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
return ItemLess(state->data[state->heap[i]], state->data[state->heap[j]]);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* swaps items i and j in heap, maintains indexes */
|
|
Packit |
67cb25 |
static int
|
|
Packit |
67cb25 |
mmexchange(medacc_state_t * state, const int i, const int j)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
int t = state->heap[i];
|
|
Packit |
67cb25 |
state->heap[i] = state->heap[j];
|
|
Packit |
67cb25 |
state->heap[j] = t;
|
|
Packit |
67cb25 |
state->pos[state->heap[i]] = i;
|
|
Packit |
67cb25 |
state->pos[state->heap[j]] = j;
|
|
Packit |
67cb25 |
return 1;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* swaps items i and j if i < j; returns true if swapped */
|
|
Packit |
67cb25 |
static int
|
|
Packit |
67cb25 |
mmCmpExch(medacc_state_t * state, const int i, const int j)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
return (mmless(state, i, j) && mmexchange(state , i, j));
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* maintains minheap property for all items below i/2. */
|
|
Packit |
67cb25 |
static void
|
|
Packit |
67cb25 |
minSortDown(medacc_state_t * state, int i)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
for (; i <= minCt(state); i *= 2)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
if (i > 1 && i < minCt(state) && mmless(state, i + 1, i))
|
|
Packit |
67cb25 |
++i;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (!mmCmpExch(state, i, i / 2))
|
|
Packit |
67cb25 |
break;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* maintains maxheap property for all items below i/2. (negative indexes) */
|
|
Packit |
67cb25 |
static void
|
|
Packit |
67cb25 |
maxSortDown(medacc_state_t * state, int i)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
for (; i >= -maxCt(state); i *= 2)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
if (i < -1 && i > -maxCt(state) && mmless(state, i, i - 1))
|
|
Packit |
67cb25 |
--i;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (!mmCmpExch(state, i / 2, i))
|
|
Packit |
67cb25 |
break;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* maintains minheap property for all items above i, including median
|
|
Packit |
67cb25 |
returns true if median changed */
|
|
Packit |
67cb25 |
static int
|
|
Packit |
67cb25 |
minSortUp(medacc_state_t * state, int i)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
while (i > 0 && mmCmpExch(state, i, i / 2))
|
|
Packit |
67cb25 |
i /= 2;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
return (i == 0);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* maintains maxheap property for all items above i, including median
|
|
Packit |
67cb25 |
returns true if median changed */
|
|
Packit |
67cb25 |
static int
|
|
Packit |
67cb25 |
maxSortUp(medacc_state_t * state, int i)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
while (i<0 && mmCmpExch(state, i / 2, i))
|
|
Packit |
67cb25 |
i /= 2;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
return (i == 0);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
static const gsl_movstat_accum median_accum_type =
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
medacc_size,
|
|
Packit |
67cb25 |
medacc_init,
|
|
Packit |
67cb25 |
medacc_insert,
|
|
Packit |
67cb25 |
NULL, /* XXX FIXME */
|
|
Packit |
67cb25 |
medacc_get
|
|
Packit |
67cb25 |
};
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
const gsl_movstat_accum *gsl_movstat_accum_median = &median_accum_type;
|