/*
  Native calls for normalize

  @author Scott Fernandez
  @version $Id: normalize.c,v 1.1 2011/09/15 14:48:41 ntn Exp $
 */
#include "native.h"
#include <math.h>

void   scale_and_shift (float* in, double scale, double shift, int start, int end);
void   array_add       (float* in, double shift, int end);
void   array_multiply  (float* in, double scale, int end);

JNIEXPORT void JNICALL Java_nxm_dsp_prim_normalize_nativeScaleAndShift(JNIEnv *env,
    jobject jobj, jobject buf, jdouble scale, jdouble shift, jint start, jint end) {
  jfloat *in;
  in = GetArray(buf);
  scale_and_shift(in, scale, shift, start, end);
  ReleaseArray(buf, in);
}

void scale_and_shift (float* const in, double scale, double shift, int start, int end) {
  float* startP = &in[start];
  end -= start;

  if (scale == 1 || shift == 0) {
    /* scalarAddToArray and scalarMultiplyToArray just return if there's
           nothing to do, so at most one will run */
    array_add     (startP, shift, end);
    array_multiply(startP, scale, end);
    return;
  }
  /* Otherwise, do them both at once */
  static const int unroll = 4;

  float* endP = &in[end];
  float* unrolledEnd = endP - (end%unroll);

  while(startP != unrolledEnd) {
    startP[0] = startP[0]*scale + shift;
    startP[1] = startP[1]*scale + shift;
    startP[2] = startP[2]*scale + shift;
    startP[3] = startP[3]*scale + shift;
    startP += unroll;
  }
  while(startP != endP) {
    startP[0] = startP[0]*scale + shift;
    startP++;
  }
}

/*
    ================================
    |      AUXILARY FUNCTIONS      |
    ================================
 */
void array_add (float* in, double shift, int end) {
  /* Return if nothing to do */
  if (shift == 0) { return; }

  static const int unroll = 4;

  float* endP = &in[end];
  float* unrolledEnd = endP - (end%unroll);
  while(in != unrolledEnd) {
    in[0] += shift;
    in[1] += shift;
    in[2] += shift;
    in[3] += shift;
    in += 4;
  }
  while(in != endP) {
    (in++)[0] += shift;
  }
}

void array_multiply (float* in, double scale, int end) {
  /* Return if nothing to do */
  if (scale == 1) { return; }

  static const int unroll = 4;

  float* endP = &in[end];
  float* unrolledEnd = endP - (end%unroll);
  while(in != unrolledEnd) {
    in[0] *= scale;
    in[1] *= scale;
    in[2] *= scale;
    in[3] *= scale;
    in += 4;
  }
  while(in != endP) {
    (in++)[0] *= scale;
  }
}

