//////////////////////////////////////////////////////////
// share_array の連結等の操作
// 通信は行わない
//////////////////////////////////////////////////////////
#ifndef SLICE_H
 #define SLICE_H
////////////////////////////////////////////
// a[i] := b[j]
////////////////////////////////////////////
static void share_setshare(share_array a, int i, share_array b, int j)
{
  //if (_party >  2) return;
  if (a == NULL || b == NULL) {
    printf("share_setshare: a = %p b = %p\n", a, b);
    return;
  }
  if (_party > max_partyid(a)) return;

  if (i < 0 || i >= a->n) {
    printf("share_setshare a: n %d i %d\n", a->n, i);
    exit(1);
  }
  if (j < 0 || j >= b->n) {
    printf("share_setshare b: n %d j %d\n", b->n, j);
    exit(1);
  }
  if (a->q != b->q) {
    printf("share_setshare a->q %d b->q %d\n", (int)a->q, (int)b->q);
    exit(1);
  }
  if (a->A != NULL && b->A != NULL) pa_set(a->A,i, pa_get(b->A,j));
}
#define _setshare share_setshare

////////////////////////////////////////////
// a[is:ie) := b[js:je)
////////////////////////////////////////////
static void share_setshares(share_array a, int is, int ie, share_array b, int js)
{
//  if (_party >  2) return;
  if (a == NULL || b == NULL) {
    printf("share_setshares: a = %p b = %p\n", a, b);
    return;
  }
  if (_party > max_partyid(a)) return;

  if (is < 0 || is >= a->n) {
    printf("share_setshares a: n %d is %d\n", a->n, is);
    exit(1);
  }
  if (ie > a->n) {
    printf("share_setshares a: n %d ie %d\n", a->n, ie);
    exit(1);
  }
  if (js < 0 || js >= b->n) {
    printf("share_setshares b: n %d js %d\n", b->n, js);
    exit(1);
  }
  if (js + (ie-is) > b->n) {
    printf("share_setshares b: n %d is %d ie %d js %d\n", b->n, is, ie, js);
    exit(1);
  }
  if (a->q != b->q) {
    printf("share_setshares a->q %d b->q %d\n", (int)a->q, (int)b->q);
    exit(1);
  }
  //pa_iter itr_a = pa_iter_new(a->A);
  pa_iter itr_b = pa_iter_pos_new(b->A, js);
  if (a->A != NULL && b->A != NULL) {
    for (int i = 0; i < ie-is; i++) {
      //pa_set(a->A,is + i, pa_get(b->A,js + i));
      //pa_iter_set(itr_a, pa_iter_get(itr_b));
      pa_set(a->A,is + i, pa_iter_get(itr_b));
    }
  }
  //pa_iter_flush(itr_a); 
  pa_iter_free(itr_b);
}
#define _setshares share_setshares

static share_array share_concat(share_array a, share_array b)
{
  if (a == NULL || b == NULL) {
    printf("share_concat: a = %p b = %p\n", a, b);
    return NULL;
  }
  //if (_party >  2) return NULL;
  if (a->q != b->q) {
    printf("share_concat a->q %d b->q %d\n", (int)a->q, (int)b->q);
  }
  NEWT(share_array, ans);
  ans->n = a->n + b->n;
  ans->q = a->q;
  ans->type = a->type;
  ans->irr_poly = a->irr_poly;
  ans->A = NULL;
  if (_party > max_partyid(a)) return ans;
  ans->A = pa_new_type(ans->n, a->A->w, a->A->type);
  for (int i=0; i<a->n; i++) pa_set(ans->A,i,pa_get(a->A,i)); // 要高速化
  for (int i=0; i<b->n; i++) pa_set(ans->A,a->n + i, pa_get(b->A,i));
  return ans;
}
#define _concat share_concat

static void share_concat_(share_array a, share_array b)
{
  if (a == NULL || b == NULL) {
    printf("share_concat_: a = %p b = %p\n", a, b);
    return;
  }
  //if (_party >  2) return;
  share_array tmp = share_concat(a, b);
  pa_free(a->A);  *a = *tmp;  free(tmp);
}
#define _concat_ share_concat_



static share_array share_insert_head(share_array a, share_t x)
{
  if (a == NULL) {
    printf("share_insert_head: a = %p\n", a);
    return NULL;
  }
  //if (_party >  2) return NULL;
  NEWT(share_array, ans);
  ans->n = a->n + 1;
  ans->q = a->q;
  ans->type = a->type;
  ans->irr_poly = a->irr_poly; // !!
  ans->A = NULL;
  if (_party > max_partyid(a)) return ans;
  ans->A = pa_new_type(ans->n, a->A->w, a->A->type);
  if (_party < 2) {
    pa_set(ans->A, 0, x);
  } else {
    pa_set(ans->A, 0, 0);
  }
  for (int i=1; i<ans->n; i++) pa_set(ans->A, i, pa_get(a->A, i-1));
  return ans;
}
#define _insert_head share_insert_head

// 以下の関数は遅いので使うべきではない
static void share_insert_head_(share_array a, share_t x)
{
  if (a == NULL) {
    printf("share_insert_head_: a = %p\n", a);
    return;
  }
  //if (_party >  2) return;
  share_array tmp = share_insert_head(a, x);
  pa_free(a->A);  *a = *tmp;  free(tmp);
}
#define _insert_head_ share_insert_head_

static share_array share_insert_tail(share_array a, share_t x)
{
  if (a == NULL) {
    printf("share_insert_tail: a = %p\n", a);
    return NULL;
  }
  //if (_party >  2) return NULL;
  NEWT(share_array, ans);
  *ans = *a;
  ans->n = a->n + 1;
  ans->q = a->q;
  //if (ans->A == NULL) return ans;
  ans->irr_poly = a->irr_poly; // !!
  ans->A = NULL;
  if (_party > max_partyid(a)) return ans;
  ans->A = pa_new_type(ans->n, a->A->w, a->A->type);
  if (_party < 2) {
    pa_set(ans->A, ans->n-1, x);
  } else {
    pa_set(ans->A, ans->n-1, 0);
  }
  for (int i=0; i<ans->n-1; i++) pa_set(ans->A, i, pa_get(a->A, i));
  return ans;
}
#define _insert_tail share_insert_tail

// 以下の関数は遅いので使うべきではない
static void share_insert_tail_(share_array a, share_t x)
{
  if (a == NULL) {
    printf("share_insert_tail_: a = %p\n", a);
    return;
  }
  //if (_party >  2) return;
  share_array tmp = share_insert_tail(a, x);
  pa_free(a->A);  *a = *tmp;  free(tmp);
}
#define _insert_tail_ share_insert_tail_

_ rshift(_ v, share_t z)
{
  if (_party >  2) return NULL;
  int n = len(v);
  _ ans = _dup(v);
  _setshares(ans, 1, n, v, 0);
  _setpublic(ans, 0, z);
  return ans;
}

void rshift_(_ v, share_t z)
{
  if (_party >  2) return;
  _ tmp = rshift(v, z);
  pa_free(v->A);  *v = *tmp;  free(tmp);
}

_ lshift(_ v, share_t z)
{
  if (_party >  2) return NULL;
  int n = len(v);
  _ ans = _dup(v);
  _setshares(ans, 0, n-1, v, 1);
  _setpublic(ans, n-1, z);
  return ans;
}

void lshift_(_ v, share_t z)
{
  if (_party >  2) return;
  _ tmp = lshift(v, z);
  pa_free(v->A);  *v = *tmp;  free(tmp);
}

_ rrotate(_ v)
{
  if (_party >  2) return NULL;
  int n = len(v);
  _ ans = _dup(v);
  _setshares(ans, 1, n, v, 0);
  _setshare(ans, 0, v, n-1);
  return ans;
}

void rrotate_(_ v)
{
  if (_party >  2) return;
  _ tmp = rrotate(v);
  pa_free(v->A);  *v = *tmp;  free(tmp);
}

_ lrotate(_ v)
{
  if (_party >  2) return NULL;
  int n = len(v);
  _ ans = _dup(v);
  _setshares(ans, 0, n-1, v, 1);
  _setshare(ans, n-1, v, 0);
  return ans;
}

void lrotate_(_ v)
{
  if (_party >  2) return;
  _ tmp = lrotate(v);
  pa_free(v->A);  *v = *tmp;  free(tmp);
}

/////////////////////////////////////////////////////////////
// abc => aaabbbccc (l個ずつコピー)
/////////////////////////////////////////////////////////////
share_array extend_share_array_offset(int l, share_array x, int offset, share_t diff) 
{
  int n = len(x);
  share_t q = order(x);
  share_array ans = share_const(n * l, 0, q);
  pa_iter itr_ans = pa_iter_new(ans->A);
  pa_iter itr_x = pa_iter_pos_new(x->A, offset);
  for (int i = 0; i < n; ++i) {
    share_t z = pa_iter_get(itr_x);
    z = (z + diff) % q;
    for (int j = 0; j < l; ++j) {
      //share_setshare(ans, l * i + j, x, i);
      pa_iter_set(itr_ans, z);
    }
    if ((offset > 0) && (i + offset + 1 == n)) {
      pa_iter_free(itr_x);
      itr_x = pa_iter_new(x->A);
    }
  }
  pa_iter_flush(itr_ans);
  pa_iter_free(itr_x);
    
  return ans;
}
#define extend_share_array(l, x) extend_share_array_offset(l, x, 0, 0)

/////////////////////////////////////////////////////////////
// abc => abcabcabc (l 回コピー)
/////////////////////////////////////////////////////////////
share_array extend_cyclic_share_array_offset(int l, share_array x, int offset, share_t diff) 
{
  int n = len(x);
  share_t q = order(x);
  share_array ans = share_const(n * l, 0, q);
  pa_iter itr_ans = pa_iter_new(ans->A);
  for (int j = 0; j < l; ++j) {
    pa_iter itr_x = pa_iter_new(x->A);
    for (int i = 0; i < n; ++i) {
      share_t z = pa_iter_get(itr_x);
      z = (z + diff) % q;
      pa_iter_set(itr_ans, z);
      if ((offset > 0) && (i + offset + 1 == n)) {
        pa_iter_free(itr_x);
        itr_x = pa_iter_new(x->A);
      }
    }
    pa_iter_free(itr_x);
  }
  pa_iter_flush(itr_ans);
    
  return ans;
}
#define extend_cyclic_share_array(l, x) extend_cyclic_share_array_offset(l, x, 0, 0)

/////////////////////////////////////////////////////////////
// share_array の連結
/////////////////////////////////////////////////////////////
share_array serialize_share_arrays(int l, share_array *x) {
    int n = len(x[0]);
    share_t q = order(x[0]);
    share_array ans = share_const(l * n, 0, q);
    for (int i = 0; i < l; ++i) {
        for (int j = 0; j < n; ++j) {
            share_setshare(ans, n * i, x[i], j);
        }
    }
    return ans;
}

/////////////////////////////////////////
// [start, end-1] の範囲を切り出す
// end は含まないことに注意（Python風）
/////////////////////////////////////////
static share_array share_slice_raw(share_array a, int start, int end)
{
  if (a == NULL) {
    printf("share_slice_raw: a = %p\n", a);
    return NULL;
  }
//  if (_party >  2) return NULL;
  if (start < 0) start = a->n + start;
  if (end <= 0) end = a->n + end;
  if (start < 0 || start > a->n) {
    printf("share_slice n %d start %d\n", a->n, start);
  }
  if (end < 0 || end > a->n) {
    printf("share_slice n %d end %d\n", a->n, end);
  }
  //  printf("share_slice n %d start %d\n", a->n, start);
  //  printf("share_slice n %d end %d\n", a->n, end);
  NEWT(share_array, ans);
  *ans = *a;
  ans->n = end - start;
  ans->q = a->q;
  ans->type = a->type; // !!
  ans->irr_poly = a->irr_poly; // !!
  ans->A = NULL;
  if (_party > max_partyid(a)) return ans;
  ans->A = pa_new_type(ans->n, a->A->w, a->A->type);
#if 0
  for (int i=0; i<ans->n; i++) pa_set(ans->A,i,pa_get(a->A,start+i));
#else
  pa_iter itr_ans = pa_iter_new(ans->A);
  pa_iter itr = pa_iter_pos_new(a->A, start);
  for (int i=0; i<ans->n; i++) pa_iter_set(itr_ans, pa_iter_get(itr));
  pa_iter_flush(itr_ans);
  pa_iter_free(itr);


//  printf("slice ans "); _print_debug(ans);
//  _ tmp = _dup(ans);
//  for (int i=0; i<tmp->n; i++) pa_set(tmp->A,i,pa_get(a->A,start+i));
//  printf("slice tmp "); _print_debug(tmp);

#endif
  return ans;
}
#define _slice_raw share_slice_raw
#define share_slice share_slice_raw
#define _slice share_slice_raw

static void share_slice_(share_array a, int start, int end)
{
  if (a == NULL) {
    printf("share_slice_: a = %p\n", a);
    return;
  }
//  if (_party >  2) return;
  share_array tmp = share_slice(a, start, end);
  pa_free(a->A);  *a = *tmp;  free(tmp);
}
#define _slice_ share_slice_

static share_t debug_get(share_array a, int i)
{
  _ tmp = share_slice(a, i, i+1);
  _ tmp2 = _reconstruct(tmp);
  share_t ans = share_getraw(tmp2, 0);
  _free(tmp); _free(tmp2);
  return ans;
}



#endif // SLICE_H
