Blame movstat/medacc.c

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;