/* For information on usage and redistribution, and for a DISCLAIMER OF ALL
* WARRANTIES, see the file, "LICENSE.txt," in this distribution.

iem_dp written by IOhannes m zmoelnig, Thomas Musil, Copyright (c) IEM KUG Graz Austria 1999 - 2007 */
/* double precision library */

#include "m_pd.h"
#include "iemlib.h"
#include "iem_dp.h"


/* -------------------------- sub~~ ------------------------------ */
/* based on miller's -~ which is part of pd */

static t_class *sub_tilde_tilde_class, *scalarsub_tilde_tilde_class;

typedef struct _sub_tilde_tilde
{
  t_object  x_obj;
  t_float   x_f;
  t_sample  *x_in1_coarse;
  t_sample  *x_in1_fine;
  t_sample  *x_in2_coarse;
  t_sample  *x_in2_fine;
  t_sample  *x_out_coarse;
  t_sample  *x_out_fine;
} t_sub_tilde_tilde;

typedef struct _scalarsub_tilde_tilde
{
  t_object  x_obj;
  t_float   x_f;
  t_sample  *x_in1_coarse;
  t_sample  *x_in1_fine;
  t_sample  *x_out_coarse;
  t_sample  *x_out_fine;
  t_sample  x_in2_coarse;
  t_sample  x_in2_fine;
} t_scalarsub_tilde_tilde;

static void *sub_tilde_tilde_new(t_symbol *s, int argc, t_atom *argv)
{
  if(((argc==1)&&(argv->a_type == A_FLOAT))||(argc>=2)&&(argv->a_type == A_FLOAT)&&((argv+1)->a_type == A_FLOAT))
  {
    t_scalarsub_tilde_tilde *x = (t_scalarsub_tilde_tilde *)pd_new(scalarsub_tilde_tilde_class);

    inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);
    floatinlet_new(&x->x_obj, &x->x_in2_coarse);
    floatinlet_new(&x->x_obj, &x->x_in2_fine);
    x->x_in2_coarse = atom_getfloatarg(0, argc, argv);
    if(argc>=2)
      x->x_in2_fine = atom_getfloatarg(1, argc, argv);
    else
      x->x_in2_fine = 0;
    outlet_new(&x->x_obj, &s_signal);
    outlet_new(&x->x_obj, &s_signal);
    x->x_f = 0;
    return (x);
  }
  else /* either no arguments or symbols */
  {
    t_sub_tilde_tilde *x = (t_sub_tilde_tilde *)pd_new(sub_tilde_tilde_class);

    inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);
    inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);
    inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);
    outlet_new(&x->x_obj, &s_signal);
    outlet_new(&x->x_obj, &s_signal);
    x->x_f = 0;
    return (x);
  }
}

static t_int *sub_tilde_tilde_perform(t_int *w)
{
  t_sub_tilde_tilde *x = (t_sub_tilde_tilde *)(w[1]);
  int n = (int)(w[2]);
  t_sample *in1c = x->x_in1_coarse;
  t_sample *in1f = x->x_in1_fine;
  t_sample *in2c = x->x_in2_coarse;
  t_sample *in2f = x->x_in2_fine;
  t_sample *outc = x->x_out_coarse;
  t_sample *outf = x->x_out_fine;
  double d;
  t_float f;

  while(n--)
  {
    d = iem_dp_calc_sum(*in1c++, *in1f++) - iem_dp_calc_sum(*in2c++, *in2f++);
    f = iem_dp_cast_to_float(d);
    *outf++ = iem_dp_calc_residual(d, f);
        *outc++ = f;
  }
  return(w+3);
}

static t_int *sub_tilde_tilde_perf8(t_int *w)
{
  t_sub_tilde_tilde *x = (t_sub_tilde_tilde *)(w[1]);
  int n = (int)(w[2]);
  t_sample *in1c = x->x_in1_coarse;
  t_sample *in1f = x->x_in1_fine;
  t_sample *in2c = x->x_in2_coarse;
  t_sample *in2f = x->x_in2_fine;
  t_sample *outc = x->x_out_coarse;
  t_sample *outf = x->x_out_fine;

  for(; n; n -= 8, in1c += 8, in1f += 8, in2c += 8, in2f += 8, outc += 8, outf += 8)
  {
    double d;
    t_float f;

    d = iem_dp_calc_sum(in1c[0], in1f[0]) - iem_dp_calc_sum(in2c[0], in2f[0]);
    f = iem_dp_cast_to_float(d);
    outf[0] = iem_dp_calc_residual(d, f);
        outc[0] = f;

    d = iem_dp_calc_sum(in1c[1], in1f[1]) - iem_dp_calc_sum(in2c[1], in2f[1]);
    f = iem_dp_cast_to_float(d);
    outf[1] = iem_dp_calc_residual(d, f);
        outc[1] = f;

    d = iem_dp_calc_sum(in1c[2], in1f[2]) - iem_dp_calc_sum(in2c[2], in2f[2]);
    f = iem_dp_cast_to_float(d);
    outf[2] = iem_dp_calc_residual(d, f);
        outc[2] = f;

    d = iem_dp_calc_sum(in1c[3], in1f[3]) - iem_dp_calc_sum(in2c[3], in2f[3]);
    f = iem_dp_cast_to_float(d);
    outf[3] = iem_dp_calc_residual(d, f);
    outc[3] = f;

    d = iem_dp_calc_sum(in1c[4], in1f[4]) - iem_dp_calc_sum(in2c[4], in2f[4]);
    f = iem_dp_cast_to_float(d);
    outf[4] = iem_dp_calc_residual(d, f);
    outc[4] = f;

    d = iem_dp_calc_sum(in1c[5], in1f[5]) - iem_dp_calc_sum(in2c[5], in2f[5]);
    f = iem_dp_cast_to_float(d);
    outf[5] = iem_dp_calc_residual(d, f);
    outc[5] = f;

    d = iem_dp_calc_sum(in1c[6], in1f[6]) - iem_dp_calc_sum(in2c[6], in2f[6]);
    f = iem_dp_cast_to_float(d);
    outf[6] = iem_dp_calc_residual(d, f);
    outc[6] = f;

    d = iem_dp_calc_sum(in1c[7], in1f[7]) - iem_dp_calc_sum(in2c[7], in2f[7]);
    f = iem_dp_cast_to_float(d);
    outf[7] = iem_dp_calc_residual(d, f);
    outc[7] = f;
  }
  return(w+3);
}

static t_int *scalarsub_tilde_tilde_perform(t_int *w)
{
  t_scalarsub_tilde_tilde *x = (t_scalarsub_tilde_tilde *)(w[1]);
  int n = (int)(w[2]);
  t_sample *inc = x->x_in1_coarse;
  t_sample *inf = x->x_in1_fine;
  t_sample *outc = x->x_out_coarse;
  t_sample *outf = x->x_out_fine;
  double d, g=iem_dp_calc_sum(x->x_in2_coarse, x->x_in2_fine);
  t_float f;

  while(n--)
  {
    d = iem_dp_calc_sum(*inc++, *inf++) - g;
    f = iem_dp_cast_to_float(d);
    *outf++ = iem_dp_calc_residual(d, f);
        *outc++ = f;
  }
  return(w+3);
}

static t_int *scalarsub_tilde_tilde_perf8(t_int *w)
{
  t_scalarsub_tilde_tilde *x = (t_scalarsub_tilde_tilde *)(w[1]);
  int n = (int)(w[2]);
  t_sample *inc = x->x_in1_coarse;
  t_sample *inf = x->x_in1_fine;
  t_sample *outc = x->x_out_coarse;
  t_sample *outf = x->x_out_fine;
  double g=iem_dp_calc_sum(x->x_in2_coarse, x->x_in2_fine);

  for(; n; n -= 8, inc += 8, inf += 8, outc += 8, outf += 8)
  {
    double d;
    t_float f;

    d = iem_dp_calc_sum(inc[0], inf[0]) - g;
    f = iem_dp_cast_to_float(d);
    outf[0] = iem_dp_calc_residual(d, f);
        outc[0] = f;

    d = iem_dp_calc_sum(inc[1], inf[1]) - g;
    f = iem_dp_cast_to_float(d);
    outf[1] = iem_dp_calc_residual(d, f);
        outc[1] = f;

    d = iem_dp_calc_sum(inc[2], inf[2]) - g;
    f = iem_dp_cast_to_float(d);
    outf[2] = iem_dp_calc_residual(d, f);
        outc[2] = f;

    d = iem_dp_calc_sum(inc[3], inf[3]) - g;
    f = iem_dp_cast_to_float(d);
    outf[3] = iem_dp_calc_residual(d, f);
    outc[3] = f;

    d = iem_dp_calc_sum(inc[4], inf[4]) - g;
    f = iem_dp_cast_to_float(d);
    outf[4] = iem_dp_calc_residual(d, f);
    outc[4] = f;

    d = iem_dp_calc_sum(inc[5], inf[5]) - g;
    f = iem_dp_cast_to_float(d);
    outf[5] = iem_dp_calc_residual(d, f);
    outc[5] = f;

    d = iem_dp_calc_sum(inc[6], inf[6]) - g;
    f = iem_dp_cast_to_float(d);
    outf[6] = iem_dp_calc_residual(d, f);
    outc[6] = f;

    d = iem_dp_calc_sum(inc[7], inf[7]) - g;
    f = iem_dp_cast_to_float(d);
    outf[7] = iem_dp_calc_residual(d, f);
    outc[7] = f;
  }
  return(w+3);
}

static void sub_tilde_tilde_dsp(t_sub_tilde_tilde *x, t_signal **sp)
{
  x->x_in1_coarse = sp[0]->s_vec;
  x->x_in1_fine = sp[1]->s_vec;
  x->x_in2_coarse = sp[2]->s_vec;
  x->x_in2_fine = sp[3]->s_vec;
  x->x_out_coarse = sp[4]->s_vec;
  x->x_out_fine = sp[5]->s_vec;
  if(sp[0]->s_n&7)
    dsp_add(sub_tilde_tilde_perform, 2, x, sp[0]->s_n);
  else
    dsp_add(sub_tilde_tilde_perf8, 2, x, sp[0]->s_n);
}

static void scalarsub_tilde_tilde_dsp(t_scalarsub_tilde_tilde *x, t_signal **sp)
{
  x->x_in1_coarse = sp[0]->s_vec;
  x->x_in1_fine = sp[1]->s_vec;
  x->x_out_coarse = sp[2]->s_vec;
  x->x_out_fine = sp[3]->s_vec;
  if(sp[0]->s_n&7)
    dsp_add(scalarsub_tilde_tilde_perform, 2, x, sp[0]->s_n);
  else
    dsp_add(scalarsub_tilde_tilde_perf8, 2, x, sp[0]->s_n);
}

void sub_tilde_tilde_setup(void)
{
    sub_tilde_tilde_class = class_new(gensym("-~~"), (t_newmethod)sub_tilde_tilde_new, 0,
        sizeof(t_sub_tilde_tilde), 0, A_GIMME, 0);
    class_addcreator((t_newmethod)sub_tilde_tilde_new, gensym("sub~~"), A_GIMME, 0);
    CLASS_MAINSIGNALIN(sub_tilde_tilde_class, t_sub_tilde_tilde, x_f);
    class_addmethod(sub_tilde_tilde_class, (t_method)sub_tilde_tilde_dsp, gensym("dsp"), A_CANT, 0);
    scalarsub_tilde_tilde_class = class_new(gensym("-~~"), 0, 0,
        sizeof(t_scalarsub_tilde_tilde), 0, 0);
    CLASS_MAINSIGNALIN(scalarsub_tilde_tilde_class, t_scalarsub_tilde_tilde, x_f);
    class_addmethod(scalarsub_tilde_tilde_class, (t_method)scalarsub_tilde_tilde_dsp, gensym("dsp"), A_CANT, 0);
}
