Current section

Files

Jump to
mmath c_src aggr_nif.c
Raw

c_src/aggr_nif.c

#include "erl_nif.h"
#include "mmath.h"
#include <stdio.h>
#include <math.h>
typedef ffloat (*aggr_func) (ffloat, ffloat);
typedef ffloat (*emit_func) (ffloat, double);
typedef ffloat (*threshold_func) (ffloat*, uint32_t, double);
void print(ffloat* vs, uint32_t count) {
for (uint32_t i = 0; i < count; i++) {
printf("%.2f(%.2f) ", vs[i].value, vs[i].confidence);
};
printf("\n\r");
}
static int
load(ErlNifEnv* env, void** priv, ERL_NIF_TERM load_info)
{
return 0;
}
static int
upgrade(ErlNifEnv* env, void** priv, void** old_priv, ERL_NIF_TERM load_info)
{
return 0;
}
static ERL_NIF_TERM
aggr2(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[], aggr_func f, emit_func g)
{
ErlNifBinary bin;
ErlNifSInt64 chunk; // size to be compressed
ERL_NIF_TERM r;
ffloat* vs;
ffloat* target;
ffloat aggr; // Aggregator
double confidence;
uint32_t target_i = 0; // target position
uint32_t count;
uint32_t pos = 0;
uint32_t target_size;
if (argc != 2)
return enif_make_badarg(env);
GET_CHUNK(chunk);
GET_BIN(0, bin, count, vs);
target_size = ceil((double) count / chunk) * sizeof(ffloat);
if (! (target = (ffloat*) enif_make_new_binary(env, target_size, &r)))
return enif_make_badarg(env); // TODO return propper error
if (count > 0) {
aggr = vs[0];
confidence = aggr.confidence;
pos = 1;
//We will be overwriting the confidence generated by dec_add because
//it would give a false impression based on the later values having
//a higher influence.
for (uint32_t i = 1; i < count; i++, pos++) {
if (pos == chunk) {
aggr.confidence = confidence / chunk;
target[target_i] = g(aggr, chunk);
target_i++;
aggr = vs[i];
confidence = aggr.confidence;
pos = 0;
} else {
confidence += vs[i].confidence;
aggr = f(aggr, vs[i]);
}
}
if (count % chunk) {
for (uint32_t i = 0; i < (chunk - (count % chunk)); i++) {
aggr = f(aggr, vs[count-1]);
}
}
aggr.confidence = confidence / chunk;
target[target_i] = g(aggr, chunk);
}
return r;
}
static ERL_NIF_TERM
aggr(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[], aggr_func f)
{
return aggr2(env, argc, argv, f, float_const);
}
static ERL_NIF_TERM
min(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
return aggr(env, argc, argv, float_min);
}
static ERL_NIF_TERM
max(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
return aggr(env, argc, argv, float_max);
}
static ERL_NIF_TERM
sum(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
return aggr(env, argc, argv, float_add);
}
static ERL_NIF_TERM
avg(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
return aggr2(env, argc, argv, float_add, float_divc);
}
int comp (const void * elem1, const void * elem2)
{
double f = ((ffloat*)elem1) ->value;
double s = ((ffloat*)elem2) ->value;
if (f > s) return 1;
if (f < s) return -1;
return 0;
}
void sort(ffloat* vs, uint32_t count) {
qsort(vs, count, sizeof(ffloat), comp);
}
// TODO: investigate quickselect https://en.wikipedia.org/wiki/Quickselect
static ERL_NIF_TERM
percentile(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
ErlNifBinary bin;
ErlNifSInt64 chunk; // size to be compressed
double p;
ERL_NIF_TERM r;
ffloat* vs;
ffloat* target;
uint32_t count;
uint32_t pos = 0;
uint32_t target_size;
if (argc != 3)
return enif_make_badarg(env);
if (!enif_get_double(env, argv[1], &p) || p < 0 || p > 1)
return enif_make_badarg(env);
if (!enif_get_int64(env, argv[2], &chunk) || chunk < 1)
return enif_make_badarg(env);
GET_BIN(0, bin, count, vs);
target_size = ceil((double) count / chunk) * sizeof(ffloat);
if (! (target = (ffloat*) enif_make_new_binary(env, target_size, &r)))
return enif_make_badarg(env); // TODO return propper error
uint32_t offset;
for (offset = 0; (offset + chunk) <= count; offset += chunk) {
uint32_t n = (uint32_t)((chunk - 1) * p);
sort(&vs[offset], chunk);
target[pos] = vs[offset + n];
target[pos].confidence = 0;
for (uint32_t i = 0; i < chunk; i ++) {
target[pos].confidence += vs[offset + i].confidence;
}
target[pos].confidence /= chunk;
pos++;
}
// if the last chunk is incomplete
uint32_t leftover = count % chunk;
if (leftover) {
uint32_t n = (uint32_t)((leftover - 1) * p);
sort(&vs[offset], leftover);
target[pos].value = vs[offset + n].value;
target[pos].confidence = 0;
for (uint32_t i = 0; i < leftover; i ++) {
double c = vs[offset + i].confidence;
target[pos].confidence += c;
}
target[pos].confidence /= leftover;
}
return r;
}
static ERL_NIF_TERM
threshold(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[], threshold_func f)
{
ErlNifBinary bin;
ErlNifSInt64 chunk; // size to be compressed
double level;
ERL_NIF_TERM r;
ffloat* vs;
ffloat* target;
uint32_t count;
uint32_t pos = 0;
uint32_t target_size;
if (argc != 3)
return enif_make_badarg(env);
if (!enif_get_double(env, argv[1], &level))
return enif_make_badarg(env);
if (!enif_get_int64(env, argv[2], &chunk) || chunk < 1)
return enif_make_badarg(env);
GET_BIN(0, bin, count, vs);
target_size = ceil((double) count / chunk) * sizeof(ffloat);
if (! (target = (ffloat*) enif_make_new_binary(env, target_size, &r)))
return enif_make_badarg(env); // TODO return propper error
uint32_t offset;
for (offset = 0; (offset + chunk) <= count; offset += chunk) {
target[pos] = f(&vs[offset], chunk, level);
pos++;
}
// if the last chunk is incomplete
uint32_t leftover = count % chunk;
if (leftover) {
target[pos] = f(&vs[offset], leftover, level);
}
return r;
}
ffloat first_below(ffloat* vs, uint32_t count, double level)
{
uint32_t i = 0;
double confidence = 0;
while(i < count) {
if (vs[i].value < level) {
return (ffloat) {
.value = vs[i].value,
.confidence = confidence / (i + 1)
};
}
confidence += vs[i].confidence;
i++;
}
return (ffloat) {.confidence = 0, .value = 0};
}
static ERL_NIF_TERM
nif_first_below(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
return threshold(env, argc, argv, first_below);
}
ffloat first_above(ffloat* vs, uint32_t count, double level)
{
uint32_t i = 0;
double confidence = 0;
while(i < count) {
if (vs[i].value > level) {
return (ffloat) {
.value = vs[i].value,
.confidence = confidence / (i + 1)
};
}
confidence += vs[i].confidence;
i++;
}
return (ffloat) {.confidence = 0, .value = 0};
}
static ERL_NIF_TERM
nif_first_above(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
return threshold(env, argc, argv, first_above);
}
ffloat last_below(ffloat* vs, uint32_t count, double level)
{
int32_t i = count - 1;
double confidence = 0;
while(i >= 0) {
if (vs[i].value < level) {
return (ffloat) {
.value = vs[i].value,
.confidence = confidence / (i + 1)
};
}
confidence += vs[i].confidence;
i--;
}
return (ffloat) {.confidence = 0, .value = 0};
}
static ERL_NIF_TERM
nif_last_below(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
return threshold(env, argc, argv, last_below);
}
ffloat last_above(ffloat* vs, uint32_t count, double level)
{
int32_t i = count - 1;
double confidence = 0;
while(i >= 0) {
if (vs[i].value > level) {
return (ffloat) {
.value = vs[i].value,
.confidence = confidence / (i + 1)
};
}
confidence += vs[i].confidence;
i--;
}
return (ffloat) {.confidence = 0, .value = 0};
}
static ERL_NIF_TERM
nif_last_above(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
return threshold(env, argc, argv, last_above);
}
ffloat count_above(ffloat* vs, uint32_t count, double level)
{
uint32_t counter = 0;
double confidence = 0;
for (uint32_t i = 0; i < count; i ++) {
if (vs[i].value > level) {
counter++;
}
confidence += vs[i].confidence;
}
if (counter) {
return (ffloat) {
.confidence = confidence / count,
.value = counter
};
} else {
return (ffloat) {.confidence = 0, .value = 0};
}
}
static ERL_NIF_TERM
nif_count_above(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
return threshold(env, argc, argv, count_above);
}
ffloat count_below(ffloat* vs, uint32_t count, double level)
{
uint32_t counter = 0;
double confidence = 0;
for (uint32_t i = 0; i < count; i ++) {
if (vs[i].value < level) {
counter++;
}
confidence += vs[i].confidence;
}
if (counter) {
return (ffloat) {
.confidence = confidence / count,
.value = counter
};
} else {
return (ffloat) {.confidence = 0, .value = 0};
}
}
static ERL_NIF_TERM
nif_count_below(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
return threshold(env, argc, argv, count_below);
}
ffloat first_above_conf(ffloat* vs, uint32_t count, double level)
{
uint32_t i = 0;
while(i < count) {
if (vs[i].confidence > level) {
return vs[i];
}
i++;
}
return (ffloat) {.confidence = 0, .value = 0};
}
static ERL_NIF_TERM
nif_first_above_conf(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
return threshold(env, argc, argv, first_above_conf);
}
ffloat first_below_conf(ffloat* vs, uint32_t count, double level)
{
uint32_t i = 0;
while(i < count) {
if (vs[i].confidence < level) {
return vs[i];
}
i++;
}
return (ffloat) {.confidence = 0, .value = 0};
}
static ERL_NIF_TERM
nif_first_below_conf(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
return threshold(env, argc, argv, first_below_conf);
}
ffloat last_above_conf(ffloat* vs, uint32_t count, double level)
{
int32_t i = count - 1;
while(i >= 0) {
if (vs[i].confidence > level) {
return vs[i];
}
i--;
}
return (ffloat) {.confidence = 0, .value = 0};
}
static ERL_NIF_TERM
nif_last_above_conf(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
return threshold(env, argc, argv, last_above_conf);
}
ffloat last_below_conf(ffloat* vs, uint32_t count, double level)
{
int32_t i = count - 1;
double confidence = 0;
while(i >= 0) {
if (vs[i].confidence < level) {
return vs[i];
}
i--;
}
return (ffloat) {.confidence = 0, .value = 0};
}
static ERL_NIF_TERM
nif_last_below_conf(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
return threshold(env, argc, argv, last_below_conf);
}
ffloat count_above_conf(ffloat* vs, uint32_t count, double level)
{
uint32_t counter = 0;
double confidence = 0;
for (uint32_t i = 0; i < count; i ++) {
if (vs[i].confidence > level) {
counter++;
}
confidence += vs[i].confidence;
}
if (counter) {
return (ffloat) {
.confidence = confidence / count,
.value = counter
};
} else {
return (ffloat) {.confidence = 0, .value = 0};
}
}
static ERL_NIF_TERM
nif_count_above_conf(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
return threshold(env, argc, argv, count_above_conf);
}
ffloat count_below_conf(ffloat* vs, uint32_t count, double level)
{
uint32_t counter = 0;
double confidence = 0;
for (uint32_t i = 0; i < count; i ++) {
if (vs[i].confidence < level) {
counter++;
}
confidence += vs[i].confidence;
}
if (counter) {
return (ffloat) {
.confidence = confidence / count,
.value = counter
};
} else {
return (ffloat) {.confidence = 0, .value = 0};
}
}
static ERL_NIF_TERM
nif_count_below_conf(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
return threshold(env, argc, argv, count_below_conf);
}
static ErlNifFunc nif_funcs[] = {
{"min", 2, min},
{"max", 2, max},
{"sum", 2, sum},
{"avg", 2, avg},
{"percentile", 3, percentile},
{"first_below", 3, nif_first_below},
{"first_above", 3, nif_first_above},
{"last_below", 3, nif_last_below},
{"last_above", 3, nif_last_above},
{"count_below", 3, nif_count_below},
{"count_above", 3, nif_count_above},
{"first_below_conf", 3, nif_first_below_conf},
{"first_above_conf", 3, nif_first_above_conf},
{"last_below_conf", 3, nif_last_below_conf},
{"last_above_conf", 3, nif_last_above_conf},
{"count_below_conf", 3, nif_count_below_conf},
{"count_above_conf", 3, nif_count_above_conf}
};
// Initialize this NIF library.
//
// Args: (MODULE, ErlNifFunc funcs[], load, reload, upgrade, unload)
// Docs: http://erlang.org/doc/man/erl_nif.html#ERL_NIF_INIT
ERL_NIF_INIT(mmath_aggr, nif_funcs, &load, NULL, &upgrade, NULL);