//////////////////////////////////////////////////
// GenCycle とその関連
//////////////////////////////////////////////////
#ifndef PROPAGATE_H
 #define PROPAGATE_H

#include "stablesort.h"



/************************************
def GenCycle(g):
  N = len(g)
  sigma = StableSort(g)
  t0 = vmul(Perm_ID(N), vneg(g))
  t1 = vmul(Perm_ID(N), g)
  u = AppInvPerm(t1, sigma)
  v = lshift(u, 0)
  y = AppPerm(v, sigma)
  pi = vadd(y, t0)
  w = rshift(u, 0)
  z = AppPerm(w, sigma)
  pi_inv = vadd(z, t0)
  pi_inv[0] = u[N-1]
  return (pi, pi_inv)
*************************************/
_pair GenCycle(_ g)
{
  if (_party >  2) {
    _pair ans = {NULL, NULL};
    return ans;
  }
  int n = len(g);
  _ gtmp = share_shrink(g, 2);
  _ sigma = StableSort2(gtmp);
  //printf("check sigma "); _print(sigma);
//  _check(sigma);
  //_ perm = Perm_ID(g);
  _ perm = Perm_ID2(len(sigma), order(sigma));
  //printf("perm "); _print(perm);
//  _check(perm);
  _ gneg = vneg(g);
  //printf("g    "); _print(g);
//  _check(g);
  //printf("gneg "); _print(gneg);
//  _check(gneg);
  _ gnegtmp = _shrink(gneg, 2);
  _ gnegtmp2 = B2A(gneg, order(perm));
  _ gtmp2 = B2A(gtmp, order(perm));
  _ t0 = vmul(perm, gnegtmp2);
  _ t1 = vmul(perm, gtmp2);
  _free(gnegtmp); _free(gnegtmp2);
  _free(gtmp); _free(gtmp2);
  //printf("t0 "); _print(t0);
//  _check(t0);
  //printf("t1 "); _print(t1);
//  _check(t1);
  _ u = AppInvPerm(t1, sigma);
  //printf("u "); _print(u);
//  _check(u);
  _ v = lshift(u, 0);
  _ y = AppPerm(v, sigma);
  _ pi = vadd(y, t0);
  _ w = rshift(u, 0);
  _ z = AppPerm(w, sigma);
  _ pi_inv = vadd(z, t0);
  _setshare(pi_inv, 0, u, n-1);

  _pair ans = {pi, pi_inv};

  _free(z);
  _free(w);
  _free(y);
  _free(v);
  _free(t0);
  _free(t1);
  _free(u);
  _free(gneg);
  _free(sigma);
  _free(perm);

  return ans;
}

_pair GenCycle2(_ g_)
{
  if (_party >  2) {
    _pair ans = {NULL, NULL};
    return ans;
  }
  int n = len(g_);
  _ sigma = StableSort2(g_);
//  _ g = B2A(g_, n);
  share_t k = blog(n-1)+1;
  share_t q = 1<<k;
  _ g = B2A(g_, q); // !!!
  _ perm = Perm_ID(g);
  _ gneg = vneg(g);
  _ t0 = vmul(perm, gneg);
  _ t1 = vmul(perm, g);
  _ u = AppInvPerm(t1, sigma);
  _ v = lshift(u, 0);
  _ y = AppPerm(v, sigma);
  _ pi = vadd(y, t0);
  _ w = rshift(u, 0);
  _ z = AppPerm(w, sigma);
  _ pi_inv = vadd(z, t0);
  _setshare(pi_inv, 0, u, n-1);

  _pair ans = {pi, pi_inv};

  _free(g);
  _free(z);
  _free(w);
  _free(y);
  _free(v);
  _free(t0);
  _free(t1);
  _free(u);
  _free(gneg);
  _free(sigma);
  _free(perm);

  return ans;
}

/********************************
def Propagate(g, v):
  (pi, pi_inv) = GenCycle(g)
  x = AppInvPerm(v, pi)
  y = x
  y[0] = 0
  z = PrefixSum(vsub(v, y))
  return z
********************************/
_ Propagate(_ g, _ v)
{
  if (_party >  2) return NULL;
  //printf("Propagate: g ");  _print_debug(g);
  _pair tmp = GenCycle(g);
  _ pi = tmp.x;
  //printf("Propagate: pi ");  _print_debug(pi);
  _free(tmp.y);

  _ x = AppInvPerm(v, pi);
  _setpublic(x, 0, 0);
  _ v2 = vsub(v, x);
  _ z = PrefixSum(v2);
  _free(pi);
  _free(x);
  _free(v2);
  return z;
}

_ Propagate2(_ g, _ v)
{
  //if (_party >  2) return NULL;
  if (_party > 2) {
    NEWT(_, ans);
    *ans = *v;
    ans->A = NULL;
    return ans;
  }
  _pair tmp = GenCycle2(g);
  _ pi = tmp.x;
  _free(tmp.y);
  //printf("Propagate2: g "); _print(g);
  //printf("Propagate2: pi "); _print(pi);

  _ x = AppInvPerm(v, pi);
  _setpublic(x, 0, 0);
  _ v2 = vsub(v, x);
  _ z = PrefixSum(v2);
  _free(pi);
  _free(x);
  _free(v2);
  return z;
}

_ Propagate3(_ g, _ X)
{
  if (_party > 2) {
    NEWT(_, ans);
    *ans = *X;
    ans->A = NULL;
    return ans;
  }

  int n = len(g);
  _ pi = StableSort2(g);
  _ x2 = AppInvPerm(X, pi);
  _ g2_ = AppInvPerm(g, pi);
  _ g2 = _extend(g2_, order(X));
  _ x3 = rshift(x2, 0);
  _ diff = vsub(x2, x3);
  _ diff2 = vmul(diff, g2);
  _ y0 = AppPerm(diff2, pi);
  _ z = PrefixSum(y0);
  _free(x2); _free(x3); _free(g2); _free(g2_); _free(diff); _free(diff2);
  _free(pi); _free(y0);
 
  return z;
}

_bits Propagate_bits(_ g, _bits v)
{
  if (_party >  2) return NULL;
  NEWT(_bits, ans);
  NEWA(ans->a, share_array, v->d);
  for (int i = 0; i<v->d; i++) {
    ans->a[i] = Propagate(g, v->a[i]);
  }
  return ans;
}

/*******************************
def GroupSum(g,v):
    (pi, pi_inv) = GenCycle(g)
    s = SuffixSum(v)
    t = [0] * len(v)
    t[1:] = s[1:]
    y = AppInvPerm(t, pi_inv)
    z = vsub(s, y)
    return z
*******************************/
_ GroupSum(_ g, _ v)
{
  if (_party >  2) return NULL;
  _pair tmp = GenCycle(g);
  _free(tmp.x);
  _ pi_inv = tmp.y;
  _ s = SuffixSum(v);
  _ t = _dup(s);
  _setpublic(t, 0, 0);
  _ y = AppInvPerm(t, pi_inv);
  vsub_(s, y);

  _free(pi_inv);
  _free(t);
  _free(y);

  return s;
}


/************************************************
def Grouping(V):
  n = len(V)
  G = [0]*n
  G[0] = 1
  for i in range(1,n):
    if V[i] != V[i-1]:
      G[i] = 1
  return G
************************************************/
_ Grouping(_ V)
{
  if (_party >  2) {
    NEWT(_, ans);
    *ans = *V;
    ans->q = 2;
    ans->A = NULL;
    return ans;
  }
  _ Vp = rshift(V, 1);
  _subshare(Vp, 0, V, 0);
  //_ ans = Equality2(V, Vp);
  _ ans = Equality(V, Vp);
  //_ ans = EqualityConst2_channel(V, Vp, 2, 0);
  vneg_(ans);
  _free(Vp);
  return ans;
}

_ Grouping_name(_ L, share_t q)
{
  if (_party >  2) return NULL;
  int n = len(L);
  _ V = _const(n, 0, q);
  for (int i=0; i<n; i++) {
    _setpublic(V, i, i);
  }
//  _ ans = Propagate(L, V);
  _ ans = Propagate2(L, V);
  _free(V);
  return ans;
}

_ Grouping_bit(_ V)
{
  if (_party >  2) return NULL;
  _ Vp = rshift(V, 1);
  _subshare(Vp, 0, V, 0);
  _ ans = Equality_bit(V, Vp);
  vneg_(ans);
  _free(Vp);
  return ans;
}

_ Grouping_bits(_bits V)
{
  if (_party >  2) return NULL;
  int d = V->d;
//  printf("i=0 V "); _print(V->a[0]);
  _ b = Grouping_bit(V->a[0]);
//  printf("i=0 b "); _print(b);
//  _check(b);
  for (int i=1; i<d; i++) {
    _ g = Grouping_bit(V->a[i]);
  //  printf("i=%d g ", i); _print(g);
  //  _check(g);
    _move_(b, OR(b, g));
  //  printf("i=%d b ", i); _print(b);
  //  _check(b);
    _free(g);
  }
  return b;
}

#endif
