#include "cakit.h"

#define VAL_MIN -32500
#define VAL_MAX +32500

static int 
bigintobj_check(bigintobj_t self, bigintobj_args args)
{
  assert(args.proto);
  [args.proto check];

  assert(VAL_MIN == -VAL_MAX);	/* for opposite tests */
  assert(VAL_MAX * VAL_MAX + VAL_MAX * VAL_MAX < INT_MAX);
  assert(VAL_MIN * VAL_MAX - VAL_MIN * VAL_MAX > INT_MIN);

  if (self->hit) {
    assert(VAL_MIN <= self->val && self->val <= VAL_MAX);
    if (self->obj) {
      id obj = [args.proto intValue:self->val];

      assert([obj isEqual:self->obj]);
      [obj free];
    }
  } else {
    int val;
    id obj = self->obj;

    assert([obj check]);
    val = [obj intValue];
    assert(val < VAL_MIN || VAL_MAX < val);
  }

  return 1;
}

/*
 * return new reference to object or create it if necessary
 */

static id 
bigintobj_obj(bigintobj_t self, bigintobj_args args)
{
  assert(bigintobj_check(self, args));

  if (self->obj) {
    return [self->obj cheapCopy];
  } else {
    id obj;

    assert(self->hit);
    obj = [args.proto intValue:self->val];
    assert([obj intValue] == self->val);
    return obj;
  }
}

/*
 * works inplace on "self"; creates object if it doesn't exists; returns it.
 */

static id 
bigintobj_makeobj(bigintobj_c * self, bigintobj_args args)
{
  assert(bigintobj_check(self, args));

  if (self->obj) {
    return self->obj;
  } else {
    assert(self->hit);
    return self->obj = [args.proto intValue:self->val];
  }
}

/*
 * initializing "c" by object
 */

static void 
bigintobj_ooobj(bigintobj_c * c, id obj, bigintobj_args args)
{
  int val = [obj intValue];

  if (VAL_MIN <= val && val <= VAL_MAX) {
    c->hit = 1;
    c->val = val;
    c->obj = obj;
  } else {
    c->hit = 0;
    c->obj = obj;
  }
  assert(bigintobj_check(c, args));
}

/*
 * replacing "c" by object result of inplace operation
 * used like :
 *
 *              bigintobj_makeobj(c,args);
 *              bigintobj_inobj(c,[c->obj negateSelf],args)
 */

static void 
bigintobj_inobj(bigintobj_c * c, id obj, bigintobj_args args)
{
  int val = [obj intValue];

  /* c->obj is invalid reference now... replace it by result inplace op
   * we cannot check "c" here (as the object is invalid...)
   */
  assert(c->obj != nil);
  if (VAL_MIN <= val && val <= VAL_MAX) {
    c->hit = 1;
    c->val = val;
    c->obj = obj;
  } else {
    c->hit = 0;
    c->obj = obj;
  }
  assert(bigintobj_check(c, args));
}

/*
 * initializing "c" by int value
 */

static void 
bigintobj_ooval(bigintobj_c * c, int val, bigintobj_args args)
{
  if (VAL_MIN <= val && val <= VAL_MAX) {
    c->hit = 1;
    c->val = val;
    c->obj = nil;
  } else {
    c->hit = 0;
    c->obj = [args.proto intValue:val];
  }
  assert(bigintobj_check(c, args));
}

/*
 * replacing "c" by int value
 */

static void 
bigintobj_inval(bigintobj_c * c, int val, bigintobj_args args)
{
  assert(bigintobj_check(c, args));
  if (VAL_MIN <= val && val <= VAL_MAX) {
    c->hit = 1;
    c->val = val;
    if (c->obj)
      c->obj = [c->obj free];
  } else {
    c->hit = 0;
    if (c->obj)
      c->obj = [c->obj free];
    c->obj = [args.proto intValue:val];
  }
  assert(bigintobj_check(c, args));
}

static void 
bigintobj_oozero(bigintobj_c * c, bigintobj_args args)
{
  c->hit = 1;
  c->val = 0;
  c->obj = nil;
  assert(bigintobj_check(c, args));
}

static void 
bigintobj_copy(bigintobj_c * c, bigintobj_t a, bigintobj_args args)
{
  c->hit = a->hit;
  c->val = a->val;
  c->obj = (a->obj) ? [a->obj cheapCopy] : nil;
  assert(bigintobj_check(c, args) && bigintobj_check(a, args));
}

static void 
bigintobj_deepcopy(bigintobj_c * c, bigintobj_t a, bigintobj_args args)
{
  c->hit = a->hit;
  c->val = a->val;
  c->obj = (a->obj) ? [a->obj copy] : nil;
  assert(bigintobj_check(c, args) && bigintobj_check(a, args));
}

static void 
bigintobj_move(bigintobj_c * c, bigintobj_c * a, bigintobj_args args)
{
  assert(bigintobj_check(c, args));
  c->hit = a->hit;
  c->val = a->val;
  a->val = SHRT_MAX;
  c->obj = a->obj;
  a->obj = nil;
  assert(bigintobj_check(c, args));
}

static void 
bigintobj_clear(bigintobj_c * c, bigintobj_args args)
{
  c->val = SHRT_MAX;
  if (c->obj)
    c->obj = [c->obj free];
}

static bigintobj_args 
bigintobj_getargs(id self)
{
  bigintobj_args args;

  args.proto = self;
  return args;
}

static int 
bigintobj_size(bigintobj_args args)
{
  return 1;			/* pointer increment */
}

static int 
bigintobj_bsize(bigintobj_args args)
{
  return sizeof(bigintobj_c);	/* byte size carrier */
}

#if 0
static bigintobj_t 
bigintobj_id2t(id object)
{				/* not needed */
}
static bigintobj_c *
bigintobj_id2t(id object)
{				/* not needed */
}

#endif

/*
 * initialize scratch space "ref" with a read-only value 
 */

static bigintobj_t 
bigintobj_id2ref(id object, bigintobj_c * ref)
{
  bigintobj_args args = bigintobj_getargs(object);	/* use object as proto */

  bigintobj_ooobj(ref, object, args);
  return ref;
}

/*
 * return a new object, containing a copy of "t"
 */

static id 
bigintobj_t2id(id proto, bigintobj_t t)
{
  bigintobj_args args = bigintobj_getargs(proto);

  return bigintobj_obj(t, args);
}

/*
 * return a new object, containing "t" itself (moved into object)
 */

static id 
bigintobj_c2id(id proto, bigintobj_c * c)
{
  id res;
  bigintobj_args args = bigintobj_getargs(proto);

  res = bigintobj_obj(c, args);
  bigintobj_clear(c, args);

  return res;
}

static unsigned 
bigintobj_hash(bigintobj_t a, bigintobj_args args)
{
  return (a->hit) ? a->val : [a->obj hash];
}

static int 
bigintobj_iseq(bigintobj_t a, bigintobj_t b, bigintobj_args args)
{
  if (a == b) {
    return YES;
  } else {
    if (a->hit) {
      return (b->hit) ? a->val == b->val : NO;
    } else {
      return (b->hit) ? NO : [a->obj isEqual:b->obj];
    }
  }
}

static int 
bigintobj_sgn(bigintobj_t a, bigintobj_args args)
{
  if (a->hit) {
    short int sgn = a->val;

    return (sgn == 0) ? 0 : ((sgn < 0) ? -1 : +1);
  } else {
    return [a->obj sign];
  }
}

static int 
bigintobj_cmp(bigintobj_t a, bigintobj_t b, bigintobj_args args)
{
  int cmp;

  if (a == b) {
    cmp = 0;
  } else {
    if (a->hit && b->hit) {
      cmp = a->val - b->val;
      cmp = (cmp == 0) ? 0 : ((cmp < 0) ? -1 : +1);
    } else {
      id A = bigintobj_obj(a, args);
      id B = bigintobj_obj(b, args);

      cmp = [A compare:B];
      [A free];
      [B free];
    }
  }

  return cmp;
}

static int 
bigintobj_iszero(bigintobj_t a, bigintobj_args args)
{
  int iszero = (a->hit && a->val == 0);

  assert(iszero || (a->hit && a->val != 0) || (a->hit == 0 && [a->obj notZero]));
  return iszero;
}

static int 
bigintobj_isop(bigintobj_t a, bigintobj_t b, bigintobj_args args)
{
  if (a == b) {
    return NO;
  } else {
    if (a->hit) {
      assert(VAL_MAX + VAL_MIN == 0);
      assert(VAL_MIN <= a->val && a->val <= VAL_MAX);
      assert(VAL_MIN <= b->val && b->val <= VAL_MAX);
      return (b->hit) ? a->val + b->val == 0 : NO;
    } else {
      return (b->hit) ? NO : [a->obj isOpposite:b->obj];
    }
  }
}

static void 
bigintobj_ooneg(bigintobj_c * c, bigintobj_t a, bigintobj_args args)
{
  if (a->hit) {
    int C = -a->val;

    bigintobj_ooval(c, C, args);
  } else {
    bigintobj_ooobj(c, [a->obj negate], args);
  }

  assert(bigintobj_isop(c, a, args));
}

static void 
bigintobj_inneg(bigintobj_c * c, bigintobj_args args)
{
  if (c->hit) {
    int C = -c->val;

    bigintobj_inval(c, C, args);
  } else {
    bigintobj_inobj(c, [c->obj negateSelf], args);
  }
}

static void 
bigintobj_oodbl(bigintobj_c * c, bigintobj_t a, int v, bigintobj_args args)
{
  if (a->hit) {
    int C = 2 * a->val;

    C = (v == +1) ? +C : -C;
    bigintobj_ooval(c, C, args);
  } else {
    bigintobj_ooobj(c, [a->obj _double:v], args);
  }
}

static void 
bigintobj_indbl(bigintobj_c * c, int v, bigintobj_args args)
{
  if (c->hit) {
    int C = 2 * c->val;

    C = (v == +1) ? +C : -C;
    bigintobj_inval(c, C, args);
  } else {
    bigintobj_inobj(c, [c->obj _doubleSelf:v], args);
  }
}

static void 
bigintobj_ooadd(bigintobj_c * c, bigintobj_t a, int v, bigintobj_t b, int w, bigintobj_args args)
{
  if (a->hit && b->hit) {
    int C = a->val * v + b->val * w;

    bigintobj_ooval(c, C, args);
  } else {
    id A, B, C;

    A = bigintobj_obj(a, args);
    B = bigintobj_obj(b, args);
    C = [A _add:v:B:w];
    [A free];
    [B free];
    bigintobj_ooobj(c, C, args);
  }
}

static void 
bigintobj_inadd(bigintobj_c * c, int v, bigintobj_t b, int w, bigintobj_args args)
{
  if (c->hit && b->hit) {
    int C = c->val * v + b->val * w;

    bigintobj_inval(c, C, args);
  } else {
    id B = bigintobj_obj(b, args);

    bigintobj_makeobj(c, args);
    bigintobj_inobj(c, [c->obj _addSelf:v:B:w], args);
    [B free];
  }
}

static int 
bigintobj_isone(bigintobj_t a, bigintobj_args args)
{
  return (a->hit && a->val == +1);
}

static int 
bigintobj_isminusone(bigintobj_t a, bigintobj_args args)
{
  return (a->hit && a->val == -1);
}

static void 
bigintobj_oosqr(bigintobj_c * c, bigintobj_t a, bigintobj_args args)
{
  if (a->hit) {
    int C = a->val * a->val;

    bigintobj_ooval(c, C, args);
  } else {
    bigintobj_ooobj(c, [a->obj square], args);
  }
}

static void 
bigintobj_insqr(bigintobj_c * c, bigintobj_args args)
{
  if (c->hit) {
    int C = c->val * c->val;

    bigintobj_inval(c, C, args);
  } else {
    bigintobj_inobj(c, [c->obj squareSelf], args);
  }
}

static void 
bigintobj_oomul(bigintobj_c * c, bigintobj_t a, bigintobj_t b, bigintobj_args args)
{
  if (a->hit && b->hit) {
    int C = a->val * b->val;

    bigintobj_ooval(c, C, args);
  } else {
    id A, B, C;

    A = bigintobj_obj(a, args);
    B = bigintobj_obj(b, args);
    C = [A multiply:B];
    [A free];
    [B free];
    bigintobj_ooobj(c, C, args);
  }
}

static void 
bigintobj_inmul(bigintobj_c * c, bigintobj_t b, bigintobj_args args)
{
  if (c->hit && b->hit) {
    int C = c->val * b->val;

    bigintobj_inval(c, C, args);
  } else {
    id B = bigintobj_obj(b, args);

    bigintobj_makeobj(c, args);
    bigintobj_inobj(c, [c->obj multiplySelf:B], args);
    [B free];
  }
}

static void 
bigintobj_oomulv(bigintobj_c * c, bigintobj_t a, bigintobj_t b, int v, bigintobj_args args)
{
  if (a->hit && b->hit) {
    int C = a->val * b->val * v;

    bigintobj_ooval(c, C, args);
  } else {
    id A, B, C;

    A = bigintobj_obj(a, args);
    B = bigintobj_obj(b, args);
    C = [A _multiply:B:v];
    [A free];
    [B free];
    bigintobj_ooobj(c, C, args);
  }
}

static void 
bigintobj_inmulv(bigintobj_c * c, bigintobj_t b, int v, bigintobj_args args)
{
  if (c->hit && b->hit) {
    int C = c->val * b->val * v;

    bigintobj_inval(c, C, args);
  } else {
    id B = bigintobj_obj(b, args);

    bigintobj_makeobj(c, args);
    bigintobj_inobj(c, [c->obj _multiplySelf:B:v], args);
    [B free];
  }
}

static void 
bigintobj_ooaddmul(bigintobj_c * c, bigintobj_t a, int v, bigintobj_t b, bigintobj_t t, int w, bigintobj_args args)
{
  if (a->hit && b->hit && t->hit) {
    int C = a->val * v + b->val * t->val * w;

    bigintobj_ooval(c, C, args);
  } else {
    id A, B, T, C;

    A = bigintobj_obj(a, args);
    B = bigintobj_obj(b, args);
    T = bigintobj_obj(t, args);
    C = [A _add:v:B multiply:T:w];
    [A free];
    [B free];
    [T free];
    bigintobj_ooobj(c, C, args);
  }
}

static void 
bigintobj_inaddmul(bigintobj_c * c, int v, bigintobj_t b, bigintobj_t t, int w, bigintobj_args args)
{
  if (c->hit && b->hit && t->hit) {
    int C = c->val * v + b->val * t->val * w;

    bigintobj_inval(c, C, args);
  } else {
    id B = bigintobj_obj(b, args);
    id T = bigintobj_obj(t, args);

    bigintobj_makeobj(c, args);
    bigintobj_inobj(c, [c->obj _addSelf:v:B multiply:T:w], args);
    [B free];
    [T free];
  }
}

static void 
bigintobj_oomuladd(bigintobj_c * c, bigintobj_t a, bigintobj_t s, int v, bigintobj_t b, int w, bigintobj_args args)
{
  if (a->hit && s->hit && b->hit) {
    int C = a->val * s->val * v + b->val * w;

    bigintobj_ooval(c, C, args);
  } else {
    id A, B, S, C;

    A = bigintobj_obj(a, args);
    B = bigintobj_obj(b, args);
    S = bigintobj_obj(s, args);
    C = [A _multiply:S:v add:B:w];
    [A free];
    [B free];
    [S free];
    bigintobj_ooobj(c, C, args);
  }
}

static void 
bigintobj_inmuladd(bigintobj_c * c, bigintobj_t s, int v, bigintobj_t b, int w, bigintobj_args args)
{
  if (c->hit && s->hit && b->hit) {
    int C = c->val * s->val * v + b->val * w;

    bigintobj_inval(c, C, args);
  } else {
    id B = bigintobj_obj(b, args);
    id S = bigintobj_obj(s, args);

    bigintobj_makeobj(c, args);
    bigintobj_inobj(c, [c->obj _multiplySelf:S:v add:B:w], args);
    [B free];
    [S free];
  }
}

static void 
bigintobj_oomuladdmul(bigintobj_c * c, bigintobj_t a, bigintobj_t s, int v, bigintobj_t b, bigintobj_t t, int w, bigintobj_args args)
{
  if (a->hit && s->hit && b->hit && t->hit) {
    int C = a->val * s->val * v + b->val * t->val * w;

    bigintobj_ooval(c, C, args);
  } else {
    id A, B, S, T, C;

    A = bigintobj_obj(a, args);
    B = bigintobj_obj(b, args);
    S = bigintobj_obj(s, args);
    T = bigintobj_obj(t, args);
    C = [A _multiply:S:v add:B multiply:T:w];
    [A free];
    [B free];
    [S free];
    [T free];
    bigintobj_ooobj(c, C, args);
  }
}

static void 
bigintobj_inmuladdmul(bigintobj_c * c, bigintobj_t s, int v, bigintobj_t b, bigintobj_t t, int w, bigintobj_args args)
{
  if (c->hit && s->hit && b->hit && t->hit) {
    int C = c->val * s->val * v + b->val * t->val * w;

    bigintobj_inval(c, C, args);
  } else {
    id B = bigintobj_obj(b, args);
    id S = bigintobj_obj(s, args);
    id T = bigintobj_obj(t, args);

    bigintobj_makeobj(c, args);
    bigintobj_inobj(c, [c->obj _multiplySelf:S:v add:B multiply:T:w], args);
    [B free];
    [S free];
    [T free];
  }
}

static void 
bigintobj_ooaddsqr(bigintobj_c * c, bigintobj_t a, int v, bigintobj_t b, int w, bigintobj_args args)
{
  if (a->hit && b->hit) {
    int C = a->val * v + b->val * b->val * w;

    bigintobj_ooval(c, C, args);
  } else {
    id A, B, C;

    A = bigintobj_obj(a, args);
    B = bigintobj_obj(b, args);
    C = [A _addSquare:v:B:w];
    [A free];
    [B free];
    bigintobj_ooobj(c, C, args);
  }
}

static void 
bigintobj_inaddsqr(bigintobj_c * c, int v, bigintobj_t b, int w, bigintobj_args args)
{
  if (c->hit && b->hit) {
    int C = c->val * v + b->val * b->val * w;

    bigintobj_inval(c, C, args);
  } else {
    id B = bigintobj_obj(b, args);

    bigintobj_makeobj(c, args);
    bigintobj_inobj(c, [c->obj _addSelfSquare:v:B:w], args);
    [B free];
  }
}

static BOOL 
bigintobj_oodiv(bigintobj_c * c, bigintobj_t a, bigintobj_t b, bigintobj_args args)
{
  if (a->hit && b->hit) {
    int C;

    if (b->val == 0 || a->val % b->val != 0) {
      return FAILURE;
    } else {
      C = a->val / b->val;
    }
    bigintobj_ooval(c, C, args);
  } else {
    id A, B, C;

    A = bigintobj_obj(a, args);
    B = bigintobj_obj(b, args);
    C = [A divide:B];
    [A free];
    [B free];
    if (C == nil)
      return FAILURE;
    bigintobj_ooobj(c, C, args);
  }

  return SUCCESS;
}

static void 
bigintobj_indiv(bigintobj_c * c, bigintobj_t b, bigintobj_args args)
{
  if (c->hit && b->hit) {
    int C;

    if (b->val == 0 || c->val % b->val != 0) {
      fprintf(stderr, "-exact division failed\n");
      abort();
    } else {
      C = c->val / b->val;
    }
    bigintobj_inval(c, C, args);
  } else {
    id C, B = bigintobj_obj(b, args);

    bigintobj_makeobj(c, args);
    C = [c->obj divideSelf:B];
    if (C == nil) {
      fprintf(stderr, "-exact division failed\n");
      abort();
    }
    bigintobj_inobj(c, C, args);
    [B free];
  }
}

static void 
bigintobj_args_fileOut(id aFile, bigintobj_args * args)
{
}
static void 
bigintobj_args_fileIn(id aFile, bigintobj_args * args)
{
}

static void 
bigintobj_fileOut(id aFiler, bigintobj_c * a, bigintobj_args args)
{
  assert(bigintobj_check(a, args));
  [aFiler fileOut:&a->hit type:'S'];
  if (a->hit) {
    [aFiler fileOut:&a->val type:'s'];	/* signed ! */
  } else {
    [aFiler fileOut:&a->obj type:'@'];
  }
}

static void 
bigintobj_fileIn(id aFiler, bigintobj_c * c, bigintobj_args args)
{
  [aFiler fileIn:&c->hit type:'S'];
  if (c->hit) {
    [aFiler fileIn:&c->val type:'s'];
    c->obj = nil;
  } else {
    [aFiler fileIn:&c->obj type:'@'];
  }
}


/*
 * Computer Algebra Kit (c) 1993,98 by Comp.Alg.Objects.  All Rights Reserved.
 * $Id: ivectorc.m,v 1.7 1998/11/13 09:45:31 stes Exp $
 */

#if bigintobj_pervalue
static bigintobj_t 
bigintobjseq_tval(bigintobjseq_t self)
{
  return *self;
}
static bigintobj_c *
bigintobjseq_cval(bigintobjseq_t self)
{
  return self;
}
#else
static bigintobj_t 
bigintobjseq_tval(bigintobjseq_t self)
{
  return self;
}
static bigintobj_c *
bigintobjseq_cval(bigintobjseq_t self)
{
  return self;
}
#endif

static int 
bigintobjseq_check(bigintobjseq_t self, int n, bigintobj_args args)
{
  while (n--) {
    bigintobj_check(bigintobjseq_tval(self), args);
    self += bigintobj_size(args);
  }
  return 1;
}

static bigintobjseq_t 
bigintobjseq_alloc(int n, bigintobj_args args)
{
  return (bigintobjseq_t) OC_Malloc(n * bigintobj_bsize(args));
}

static bigintobjseq_t 
bigintobjseq_realloc(bigintobjseq_t self, int n, bigintobj_args args)
{
  return (bigintobjseq_t) OC_Realloc(self, n * bigintobj_bsize(args));
}

static bigintobjseq_t 
bigintobjseq_free(bigintobjseq_t self, bigintobj_args args)
{
  OC_Free(self);
  return NULL;
}

static void 
bigintobjseq_oozero(bigintobjseq_t c, int n, bigintobj_args args)
{
  while (n--) {
    bigintobj_oozero(bigintobjseq_cval(c), args);
    c += bigintobj_size(args);
  }
}

static void 
bigintobjseq_copy(bigintobjseq_t c, bigintobjseq_t a, int n, bigintobj_args args)
{
  assert(n >= 0);
  while (n--) {
    bigintobj_copy(bigintobjseq_cval(c), bigintobjseq_tval(a), args);
    c += bigintobj_size(args);
    a += bigintobj_size(args);
  }
}

static void 
bigintobjseq_deepcopy(bigintobjseq_t c, bigintobjseq_t a, int n, bigintobj_args args)
{
  assert(n >= 0);
  while (n--) {
    bigintobj_deepcopy(bigintobjseq_cval(c), bigintobjseq_tval(a), args);
    c += bigintobj_size(args);
    a += bigintobj_size(args);
  }
}

#if bigintobj_isvalue
static void 
bigintobjseq_clear(bigintobjseq_t self, int n, bigintobj_args args)
{
  /* nothing to clear */
}

#else
static void 
bigintobjseq_clear(bigintobjseq_t self, int n, bigintobj_args args)
{
  while (n--) {
    bigintobj_clear(self, args);
    self += bigintobj_size(args);
  }
}
#endif				/* bigintobj_isvalue */

static int 
bigintobjseq_iseq(bigintobjseq_t a, bigintobjseq_t b, int n, bigintobj_args args)
{
  assert(n >= 0 && a != b);
  while (n--) {
    if (!bigintobj_iseq(bigintobjseq_tval(a), bigintobjseq_tval(b), args)) {
      return 0;
    } else {
      a += bigintobj_size(args);
      b += bigintobj_size(args);
    }
  }
  return 1;
}

static unsigned 
bigintobjseq_hash(bigintobjseq_t a, int n, bigintobj_args args)
{
  assert(n >= 0);
  return (n) ? bigintobj_hash(bigintobjseq_tval(a), args) : 0;
}

static void 
bigintobjseq_place(bigintobjseq_t self, bigintobj_t a, int i, bigintobj_args args)
{
  bigintobj_c *c = bigintobjseq_cval(self + i * bigintobj_size(args));

  bigintobj_clear(c, args);
  bigintobj_copy(c, a, args);
}

static int 
bigintobjseq_insertlast(bigintobjseq_t self, bigintobj_t a, int n, bigintobj_args args)
{
  bigintobj_c *c;

  assert(n >= 0);
  c = bigintobjseq_cval(self + n * bigintobj_size(args));
  bigintobj_copy(c, a, args);
  return ++n;
}

static int 
bigintobjseq_insertfirst(bigintobjseq_t self, bigintobj_t a, int n, bigintobj_args args)
{
  int j;

  assert(n > 0);
  self += n * bigintobj_size(args);
  j = n;
  while (j--) {
    bigintobjseq_t t;

    t = self - bigintobj_size(args);
    bigintobj_move(bigintobjseq_cval(self), bigintobjseq_cval(t), args);
    self = t;
  }
  bigintobj_copy(bigintobjseq_cval(self), a, args);
  return ++n;
}

static int 
bigintobjseq_insertat(bigintobjseq_t self, bigintobj_t a, int i, int n, bigintobj_args args)
{
  assert(0 <= i && i < n);
  self += i * bigintobj_size(args);
  return i + bigintobjseq_insertfirst(self, a, n - i, args);
}

static int 
bigintobjseq_freelast(bigintobjseq_t self, int n, bigintobj_args args)
{
  bigintobj_c *c;

  assert(0 < n);
  --n;
  c = bigintobjseq_cval(self + n * bigintobj_size(args));
  bigintobj_clear(c, args);
  return n;
}

static int 
bigintobjseq_freefirst(bigintobjseq_t self, int n, bigintobj_args args)
{
  int j;

  assert(0 < n);
  bigintobj_clear(bigintobjseq_cval(self), args);
  --n;
  j = n;
  while (j--) {
    bigintobjseq_t t;

    t = self + bigintobj_size(args);
    bigintobj_move(bigintobjseq_cval(self), bigintobjseq_cval(t), args);
    self = t;
  }
  return n;
}

static int 
bigintobjseq_freeat(bigintobjseq_t self, int i, int n, bigintobj_args args)
{
  assert(0 <= i && i < n);
  self += i * bigintobj_size(args);
  return i + bigintobjseq_freefirst(self, n - i, args);
}

static bigintobj_t 
bigintobjseq_at(bigintobjseq_t self, int i, bigintobj_args args)
{
  bigintobj_t res;

  assert(0 <= i);
  res = bigintobjseq_tval(self + i * bigintobj_size(args));
  assert(bigintobj_check(res, args));
  return res;
}

static bigintobj_t 
bigintobjseq_last(bigintobjseq_t self, int n, bigintobj_args args)
{
  return bigintobjseq_at(self, n - 1, args);
}

static bigintobj_c *
bigintobjseq_cvalat(bigintobjseq_t self, int i, bigintobj_args args)
{
  assert(0 <= i);
  return bigintobjseq_cval(self + i * bigintobj_size(args));
}

static int 
bigintobjseq_iszero(bigintobjseq_t a, int n, bigintobj_args args)
{
  assert(n >= 0);
  while (n--) {
    if (bigintobj_iszero(bigintobjseq_tval(a), args)) {
      a += bigintobj_size(args);
    } else {
      return 0;
    }
  }
  return 1;
}

static int 
bigintobjseq_isop(bigintobjseq_t a, bigintobjseq_t b, int n, bigintobj_args args)
{
  assert(n >= 0 && a != b);
  while (n--) {
    if (!bigintobj_isop(bigintobjseq_tval(a), bigintobjseq_tval(b), args)) {
      return 0;
    } else {
      a += bigintobj_size(args);
      b += bigintobj_size(args);
    }
  }
  return 1;
}

static void 
bigintobjseq_ooneg(bigintobjseq_t c, bigintobjseq_t a, int n, bigintobj_args args)
{
  assert(n >= 0);
  while (n--) {
    bigintobj_ooneg(bigintobjseq_cval(c), bigintobjseq_tval(a), args);
    c += bigintobj_size(args);
    a += bigintobj_size(args);
  }
}

static void 
bigintobjseq_inneg(bigintobjseq_t a, int n, bigintobj_args args)
{
  assert(n >= 0);
  while (n--) {
    bigintobj_inneg(bigintobjseq_cval(a), args);
    a += bigintobj_size(args);
  }
}

static void 
bigintobjseq_oonegv(bigintobjseq_t c, bigintobjseq_t a, int v, int n, bigintobj_args args)
{
  assert(v * v == 1);
  if (v == -1)
    bigintobjseq_ooneg(c, a, n, args);
  else
    bigintobjseq_copy(c, a, n, args);
}

static void 
bigintobjseq_innegv(bigintobjseq_t a, int v, int n, bigintobj_args args)
{
  assert(v * v == 1);
  if (v == -1)
    bigintobjseq_inneg(a, n, args);
}

static void 
bigintobjseq_oodbl(bigintobjseq_t c, bigintobjseq_t a, int v, int n, bigintobj_args args)
{
  assert(n >= 0 && v * v == 1);
  while (n--) {
    bigintobj_oodbl(bigintobjseq_cval(c), bigintobjseq_tval(a), v, args);
    c += bigintobj_size(args);
    a += bigintobj_size(args);
  }
}

static void 
bigintobjseq_indbl(bigintobjseq_t a, int v, int n, bigintobj_args args)
{
  assert(n >= 0 && v * v == 1);
  while (n--) {
    bigintobj_indbl(bigintobjseq_cval(a), v, args);
    a += bigintobj_size(args);
  }
}

static void 
bigintobjseq_ooadd(bigintobjseq_t c, bigintobjseq_t a, int v, bigintobjseq_t b, int w, int n, bigintobj_args args)
{
  assert(n >= 0 && a != b && v * v == 1 && w * w == 1);
  while (n--) {
    bigintobj_ooadd(bigintobjseq_cval(c), bigintobjseq_tval(a), v, bigintobjseq_tval(b), w, args);
    c += bigintobj_size(args);
    a += bigintobj_size(args);
    b += bigintobj_size(args);
  }
}

static void 
bigintobjseq_inadd(bigintobjseq_t a, int v, bigintobjseq_t b, int w, int n, bigintobj_args args)
{
  assert(n >= 0 && a != b && v * v == 1 && w * w == 1);
  while (n--) {
    bigintobj_inadd(bigintobjseq_cval(a), v, bigintobjseq_tval(b), w, args);
    a += bigintobj_size(args);
    b += bigintobj_size(args);
  }
}

static void 
bigintobjseq_oomulsc(bigintobjseq_t c, bigintobjseq_t a, bigintobj_t s, int n, bigintobj_args args)
{
  assert(n >= 0);
  while (n--) {
    bigintobj_oomul(bigintobjseq_cval(c), bigintobjseq_tval(a), s, args);
    c += bigintobj_size(args);
    a += bigintobj_size(args);
  }
}

static void 
bigintobjseq_inmulsc(bigintobjseq_t a, bigintobj_t s, int n, bigintobj_args args)
{
  assert(n >= 0);
  while (n--) {
    bigintobj_inmul(bigintobjseq_cval(a), s, args);
    a += bigintobj_size(args);
  }
}

static void 
bigintobjseq_oomulscv(bigintobjseq_t c, bigintobjseq_t a, bigintobj_t s, int v, int n, bigintobj_args args)
{
  assert(n >= 0 && v * v == 1);
  while (n--) {
    bigintobj_oomulv(bigintobjseq_cval(c), bigintobjseq_tval(a), s, v, args);
    c += bigintobj_size(args);
    a += bigintobj_size(args);
  }
}

static void 
bigintobjseq_inmulscv(bigintobjseq_t a, bigintobj_t s, int v, int n, bigintobj_args args)
{
  assert(n >= 0 && v * v == 1);
  while (n--) {
    bigintobj_inmulv(bigintobjseq_cval(a), s, v, args);
    a += bigintobj_size(args);
  }
}

static void 
bigintobjseq_ooaddmulsc(bigintobjseq_t c, bigintobjseq_t a, int v, bigintobjseq_t b, bigintobj_t t, int w, int n, bigintobj_args args)
{
  assert(n >= 0 && v * v == 1 && w * w == 1);
  while (n--) {
    bigintobj_ooaddmul(bigintobjseq_cval(c), bigintobjseq_tval(a), v, bigintobjseq_tval(b), t, w, args);
    a += bigintobj_size(args);
    b += bigintobj_size(args);
    c += bigintobj_size(args);
  }
}

static void 
bigintobjseq_oomulscadd(bigintobjseq_t c, bigintobjseq_t a, bigintobj_t s, int v, bigintobjseq_t b, int w, int n, bigintobj_args args)
{
  assert(n >= 0 && v * v == 1 && w * w == 1);
  while (n--) {
    bigintobj_oomuladd(bigintobjseq_cval(c), bigintobjseq_tval(a), s, v, bigintobjseq_tval(b), w, args);
    a += bigintobj_size(args);
    b += bigintobj_size(args);
    c += bigintobj_size(args);
  }
}

static void 
bigintobjseq_oomulscaddmulsc(bigintobjseq_t c, bigintobjseq_t a, bigintobj_t s, int v, bigintobjseq_t b, bigintobj_t t, int w, int n, bigintobj_args args)
{
  assert(n >= 0 && v * v == 1 && w * w == 1);
  while (n--) {
    bigintobj_oomuladdmul(bigintobjseq_cval(c), bigintobjseq_tval(a), s, v, bigintobjseq_tval(b), t, w, args);
    a += bigintobj_size(args);
    b += bigintobj_size(args);
    c += bigintobj_size(args);
  }
}

static void 
bigintobjseq_inaddmulsc(bigintobjseq_t a, int v, bigintobjseq_t b, bigintobj_t t, int w, int n, bigintobj_args args)
{
  assert(n >= 0 && v * v == 1 && w * w == 1);
  while (n--) {
    bigintobj_inaddmul(bigintobjseq_cval(a), v, bigintobjseq_tval(b), t, w, args);
    a += bigintobj_size(args);
    b += bigintobj_size(args);
  }
}

static void 
bigintobjseq_inmulscadd(bigintobjseq_t a, bigintobj_t s, int v, bigintobjseq_t b, int w, int n, bigintobj_args args)
{
  assert(n >= 0 && v * v == 1 && w * w == 1);
  while (n--) {
    bigintobj_inmuladd(bigintobjseq_cval(a), s, v, bigintobjseq_tval(b), w, args);
    a += bigintobj_size(args);
    b += bigintobj_size(args);
  }
}

static void 
bigintobjseq_inmulscaddmulsc(bigintobjseq_t a, bigintobj_t s, int v, bigintobjseq_t b, bigintobj_t t, int w, int n, bigintobj_args args)
{
  assert(n >= 0 && v * v == 1 && w * w == 1);
  while (n--) {
    bigintobj_inmuladdmul(bigintobjseq_cval(a), s, v, bigintobjseq_tval(b), t, w, args);
    a += bigintobj_size(args);
    b += bigintobj_size(args);
  }
}

static BOOL 
bigintobjseq_oodivsc(bigintobjseq_t c, bigintobjseq_t a, bigintobj_t s, int n, bigintobj_args args)
{
  int i;
  BOOL failed;
  bigintobjseq_t orgc = c;

  assert(n >= 0);

  for (i = 0; i < n; i++) {

    failed = bigintobj_oodiv(bigintobjseq_cval(c), bigintobjseq_tval(a), s, args);

    if (failed) {
      bigintobjseq_clear(orgc, i - 1, args);
      return FAILURE;
    } else {
      c += bigintobj_size(args);
      a += bigintobj_size(args);
    }
  }

  return SUCCESS;
}

static void 
bigintobjseq_indivsc(bigintobjseq_t a, bigintobj_t s, int n, bigintobj_args args)
{
  assert(n >= 0);
  while (n--) {
    bigintobj_indiv(bigintobjseq_cval(a), s, args);
    a += bigintobj_size(args);
  }
}

static void 
bigintobjseq_oodotsqr(bigintobj_c * c, bigintobjseq_t a, int n, bigintobj_args args)
{
  assert(n >= 0);
  bigintobj_oozero(c, args);
  while (n--) {
    bigintobj_inaddsqr(c, +1, bigintobjseq_tval(a), +1, args);
    a += bigintobj_size(args);
  }
}

static void 
bigintobjseq_oodotmul(bigintobj_c * c, bigintobjseq_t a, bigintobjseq_t b, int n, bigintobj_args args)
{
  assert(n >= 0);
  bigintobj_oozero(c, args);
  while (n--) {
    bigintobj_inaddmul(c, +1, bigintobjseq_tval(a), bigintobjseq_tval(b), +1, args);
    a += bigintobj_size(args);
    b += bigintobj_size(args);
  }
}

static void 
bigintobjseq_args_fileOut(id aFiler, bigintobj_args * args)
{
  bigintobj_args_fileOut(aFiler, args);
}
static void 
bigintobjseq_args_fileIn(id aFiler, bigintobj_args * args)
{
  bigintobj_args_fileIn(aFiler, args);
}

static void 
bigintobjseq_fileOut(id aFiler, bigintobjseq_t self, int n, bigintobj_args args)
{
  assert(n >= 0);
  while (n--) {
    bigintobj_fileOut(aFiler, bigintobjseq_cval(self), args);
    self += bigintobj_size(args);
  }
}

static void 
bigintobjseq_fileIn(id aFiler, bigintobjseq_t self, int n, bigintobj_args args)
{
  assert(n >= 0);
  while (n--) {
    bigintobj_fileIn(aFiler, bigintobjseq_cval(self), args);
    self += bigintobj_size(args);
  }
}

static bigintobjvec_args 
bigintobjvec_getargs(id self)
{
  bigintobjvec_args args;

  args.sargs = bigintobj_getargs([self scalarZero]);
  return args;
}

static int 
bigintobjvec_check(bigintobjvec_t self, bigintobjvec_args args)
{
  assert(0 <= self->n && self->n <= self->c);
  bigintobjseq_check(self->ptr, self->n, args.sargs);
  return 1;
}

static bigintobjvec_t 
bigintobjvec_id2t(id object)
{
  return [object bigintobjvec_value];
}

static bigintobjvec_c *
bigintobjvec_id2c(id object)
{
  return [object bigintobjvec_reference];
}

static id 
bigintobjvec_t2id(id proto, bigintobjvec_t f)
{
  return [proto bigintobjvec_value:f];
}

static id 
bigintobjvec_c2id(id proto, bigintobjvec_c * r)
{
  return [proto bigintobjvec_reference:r];
}

static void 
bigintobjvec_init(bigintobjvec_t self, int n, bigintobjvec_args args)
{
  self->n = n;
  self->c = n;
  self->ptr = bigintobjseq_alloc(n, args.sargs);
}

static void 
bigintobjvec_initcount(bigintobjvec_t c, int n, bigintobjvec_args args)
{
  bigintobjvec_init(c, n, args);
  bigintobjseq_oozero(c->ptr, n, args.sargs);
}

static void 
bigintobjvec_initcapacity(bigintobjvec_t self, int n, bigintobjvec_args args)
{
  self->n = 0;
  self->c = n;
  self->ptr = bigintobjseq_alloc(n, args.sargs);
}

static void 
bigintobjvec_copy(bigintobjvec_t c, bigintobjvec_t a, bigintobjvec_args args)
{
  bigintobjvec_init(c, a->n, args);
  bigintobjseq_copy(c->ptr, a->ptr, a->n, args.sargs);
}

static void 
bigintobjvec_deepcopy(bigintobjvec_t c, bigintobjvec_t a, bigintobjvec_args args)
{
  bigintobjvec_init(c, a->n, args);
  bigintobjseq_deepcopy(c->ptr, a->ptr, a->n, args.sargs);
}

static void 
bigintobjvec_move(bigintobjvec_t c, bigintobjvec_t a, bigintobjvec_args args)
{
  c->n = a->n;
  c->c = a->c;
  c->ptr = a->ptr;
  a->ptr = NULL;
}

static void 
bigintobjvec_destroy(bigintobjvec_t self, bigintobjvec_args args)
{
  /*
   * clearing memory allocated by bigintobjvec_init after a failed operation
   * can't do a bigintobjseq_clear in that case as the handler doesnt know
   * what elements have been allocated
   */

  self->ptr = bigintobjseq_free(self->ptr, args.sargs);
}

static void 
bigintobjvec_clear(bigintobjvec_t self, bigintobjvec_args args)
{
  bigintobjseq_clear(self->ptr, self->n, args.sargs);
  bigintobjvec_destroy(self, args);
}

static int 
bigintobjvec_count(bigintobjvec_t self)
{
  return self->n;
}

static unsigned 
bigintobjvec_hash(bigintobjvec_t a, bigintobjvec_args args)
{
  return bigintobjseq_hash(a->ptr, a->n, args.sargs);
}

static int 
bigintobjvec_iseq(bigintobjvec_t a, bigintobjvec_t b, bigintobjvec_args args)
{
  assert(a != b);
  return (a->n == b->n && bigintobjseq_iseq(a->ptr, b->ptr, a->n, args.sargs));
}

static void 
bigintobjvec_expand(bigintobjvec_t self, bigintobjvec_args args)
{
  assert(self->n == self->c);
  self->c = self->n + self->c + 1;
  self->ptr = bigintobjseq_realloc(self->ptr, self->c, args.sargs);
  assert(self->n < self->c);
}

static void 
bigintobjvec_insertfirst(bigintobjvec_t self, bigintobj_t a, bigintobjvec_args args)
{
  assert(self->n <= self->c);
  if (self->n == self->c)
    bigintobjvec_expand(self, args);
  self->n = bigintobjseq_insertlast(self->ptr, a, self->n, args.sargs);
}

static void 
bigintobjvec_insertat(bigintobjvec_t self, bigintobj_t a, int i, bigintobjvec_args args)
{
  assert(0 < i && i <= self->n && self->n <= self->c);
  if (self->n == self->c)
    bigintobjvec_expand(self, args);

  if (i == self->n) {
    self->n = bigintobjseq_insertfirst(self->ptr, a, self->n, args.sargs);
  } else {
    self->n = bigintobjseq_insertat(self->ptr, a, self->n - i - 1, self->n, args.sargs);
  }
}

static void 
bigintobjvec_place(bigintobjvec_t self, bigintobj_t a, int i, bigintobjvec_args args)
{
  assert(0 <= i && i < self->n);
  bigintobjseq_place(self->ptr, a, self->n - i - 1, args.sargs);
}

static void 
bigintobjvec_freefirst(bigintobjvec_t self, bigintobjvec_args args)
{
  assert(self->n > 0);
  self->n = bigintobjseq_freelast(self->ptr, self->n, args.sargs);
}

static void 
bigintobjvec_freeat(bigintobjvec_t self, int i, bigintobjvec_args args)
{
  assert(0 < i && i < self->n);
  self->n = bigintobjseq_freeat(self->ptr, self->n - i - 1, self->n, args.sargs);
}


static bigintobj_t 
bigintobjvec_at(bigintobjvec_t self, int ix, bigintobjvec_args args)
{
  assert(0 <= ix && ix < self->n);
  return bigintobjseq_at(self->ptr, self->n - ix - 1, args.sargs);
}

static bigintobj_t 
bigintobjvec_first(bigintobjvec_t self, bigintobjvec_args args)
{
  assert(self->n > 0);
  return bigintobjseq_last(self->ptr, self->n, args.sargs);
}

static bigintobj_t 
bigintobjvec_elt(bigintobjvec_t self, bigintobjvec_ixt ix, bigintobjvec_args args)
{
  assert(ix->n == self->n);
  return bigintobjvec_at(self, ix->i, args);
}

static int 
bigintobjvec_iszero(bigintobjvec_t self, bigintobjvec_args args)
{
  return bigintobjseq_iszero(self->ptr, self->n, args.sargs);
}

static int 
bigintobjvec_isop(bigintobjvec_t a, bigintobjvec_t b, bigintobjvec_args args)
{
  assert(a != b);
  return (a->n == b->n && bigintobjseq_isop(a->ptr, b->ptr, a->n, args.sargs));
}

static void 
bigintobjvec_ooneg(bigintobjvec_t c, bigintobjvec_t a, bigintobjvec_args args)
{
  bigintobjvec_init(c, a->n, args);
  bigintobjseq_ooneg(c->ptr, a->ptr, a->n, args.sargs);
}

static void 
bigintobjvec_inneg(bigintobjvec_t a, bigintobjvec_args args)
{
  bigintobjseq_inneg(a->ptr, a->n, args.sargs);
}

static void 
bigintobjvec_oodbl(bigintobjvec_t c, bigintobjvec_t a, int v, bigintobjvec_args args)
{
  bigintobjvec_init(c, a->n, args);
  bigintobjseq_oodbl(c->ptr, a->ptr, v, a->n, args.sargs);
}

static void 
bigintobjvec_indbl(bigintobjvec_t a, int v, bigintobjvec_args args)
{
  bigintobjseq_indbl(a->ptr, v, a->n, args.sargs);
}

static void 
bigintobjvec_checkcount(int a, int b)
{
  if (a != b) {
    fprintf(stderr, "Vector : Dimensions Not Equal\n");
    abort();
  }
}

static void 
bigintobjvec_ooadd(bigintobjvec_t c, bigintobjvec_t a, int v, bigintobjvec_t b, int w, bigintobjvec_args args)
{
  assert(a != b);
  bigintobjvec_checkcount(a->n, b->n);
  bigintobjvec_init(c, a->n, args);
  bigintobjseq_ooadd(c->ptr, a->ptr, v, b->ptr, w, a->n, args.sargs);
}

static void 
bigintobjvec_inadd(bigintobjvec_t a, int v, bigintobjvec_t b, int w, bigintobjvec_args args)
{
  assert(a != b);
  bigintobjvec_checkcount(a->n, b->n);
  bigintobjseq_inadd(a->ptr, v, b->ptr, w, a->n, args.sargs);
}

static void 
bigintobjvec_oomulsc(bigintobjvec_t c, bigintobjvec_t a, bigintobj_t s, bigintobjvec_args args)
{
  bigintobjvec_init(c, a->n, args);
  bigintobjseq_oomulsc(c->ptr, a->ptr, s, a->n, args.sargs);
}

static void 
bigintobjvec_inmulsc(bigintobjvec_t a, bigintobj_t s, bigintobjvec_args args)
{
  bigintobjseq_inmulsc(a->ptr, s, a->n, args.sargs);
}

static void 
bigintobjvec_ooaddmulsc(bigintobjvec_t c, bigintobjvec_t a, int v, bigintobjvec_t b, bigintobj_t s, int w, bigintobjvec_args args)
{
  assert(a != b);
  bigintobjvec_checkcount(a->n, b->n);
  bigintobjvec_init(c, a->n, args);
  bigintobjseq_ooaddmulsc(c->ptr, a->ptr, v, b->ptr, s, w, a->n, args.sargs);
}

static void 
bigintobjvec_inaddmulsc(bigintobjvec_t a, int v, bigintobjvec_t b, bigintobj_t s, int w, bigintobjvec_args args)
{
  assert(a != b);
  bigintobjvec_checkcount(a->n, b->n);
  bigintobjseq_inaddmulsc(a->ptr, v, b->ptr, s, w, a->n, args.sargs);
}

static void 
bigintobjvec_oomulscadd(bigintobjvec_t c, bigintobjvec_t a, bigintobj_t s, int v, bigintobjvec_t b, int w, bigintobjvec_args args)
{
  assert(a != b);
  bigintobjvec_checkcount(a->n, b->n);
  bigintobjvec_init(c, a->n, args);
  bigintobjseq_oomulscadd(c->ptr, a->ptr, s, v, b->ptr, w, a->n, args.sargs);
}

static void 
bigintobjvec_inmulscadd(bigintobjvec_t a, bigintobj_t s, int v, bigintobjvec_t b, int w, bigintobjvec_args args)
{
  assert(a != b);
  bigintobjvec_checkcount(a->n, b->n);
  bigintobjseq_inmulscadd(a->ptr, s, v, b->ptr, w, a->n, args.sargs);
}

static void 
bigintobjvec_oomulscaddmulsc(bigintobjvec_t c, bigintobjvec_t a, bigintobj_t s, int v, bigintobjvec_t b, bigintobj_t t, int w, bigintobjvec_args args)
{
  assert(a != b);
  bigintobjvec_checkcount(a->n, b->n);
  bigintobjvec_init(c, a->n, args);
  bigintobjseq_oomulscaddmulsc(c->ptr, a->ptr, s, v, b->ptr, t, w, a->n, args.sargs);
}

static void 
bigintobjvec_inmulscaddmulsc(bigintobjvec_t a, bigintobj_t s, int v, bigintobjvec_t b, bigintobj_t t, int w, bigintobjvec_args args)
{
  assert(a != b);
  bigintobjvec_checkcount(a->n, b->n);
  bigintobjseq_inmulscaddmulsc(a->ptr, s, v, b->ptr, t, w, a->n, args.sargs);
}

static BOOL 
bigintobjvec_oodivsc(bigintobjvec_t c, bigintobjvec_t a, bigintobj_t s, bigintobjvec_args args)
{
  BOOL failed;

  bigintobjvec_init(c, a->n, args);
  failed = bigintobjseq_oodivsc(c->ptr, a->ptr, s, a->n, args.sargs);
  if (failed)
    bigintobjvec_destroy(c, args);

  return failed;
}

static void 
bigintobjvec_indivsc(bigintobjvec_t a, bigintobj_t s, bigintobjvec_args args)
{
  bigintobjseq_indivsc(a->ptr, s, a->n, args.sargs);
}

static void 
bigintobjvec_oodotmul(bigintobj_c * c, bigintobjvec_t a, bigintobjvec_t b, bigintobjvec_args args)
{
  assert(a != b);
  bigintobjvec_checkcount(a->n, b->n);
  bigintobjseq_oodotmul(c, a->ptr, b->ptr, a->n, args.sargs);
}

static void 
bigintobjvec_oodotsqr(bigintobj_c * c, bigintobjvec_t a, bigintobjvec_args args)
{
  bigintobjseq_oodotsqr(c, a->ptr, a->n, args.sargs);
}

static void 
bigintobjvec_args_fileOut(id aFiler, bigintobjvec_args * args)
{
  bigintobjseq_args_fileOut(aFiler, &args->sargs);
}
static void 
bigintobjvec_args_fileIn(id aFiler, bigintobjvec_args * args)
{
  bigintobjseq_args_fileIn(aFiler, &args->sargs);
}

static void 
bigintobjvec_fileOut(id aFiler, bigintobjvec_t self, bigintobjvec_args args)
{
  [aFiler fileOut:&self->n type:'i'];
  bigintobjseq_fileOut(aFiler, self->ptr, self->n, args.sargs);
}

static void 
bigintobjvec_fileIn(id aFiler, bigintobjvec_t self, bigintobjvec_args args)
{
  int n;

  [aFiler fileIn:&n type:'i'];
  bigintobjvec_init(self, n, args);
  bigintobjseq_fileIn(aFiler, self->ptr, self->n, args.sargs);
}

static void 
bigintobjvec_ix_init(bigintobjvec_ixt ix, id self)
{
  bigintobjvec_t v = [self bigintobjvec_value];

  ix->n = v->n;
}

static void 
bigintobjvec_ix_copy(bigintobjvec_ixt c, bigintobjvec_ixt a)
{
  c->i = a->i;
  c->n = a->n;
}

static void 
bigintobjvec_ix_clear(bigintobjvec_ixt ix)
{
  /* nothing to clear */
}

static int 
bigintobjvec_ix_count(bigintobjvec_ixt ix)
{
  return ix->n;
}

static int 
bigintobjvec_ix_isempty(bigintobjvec_ixt ix)
{
  return ix->n == 0;
}

static int 
bigintobjvec_ix_ismatch(bigintobjvec_ixt ix)
{
  assert(-1 <= ix->i && ix->i <= ix->n);
  return 0 <= ix->i && ix->i < ix->n;
}

static void 
bigintobjvec_ix_tofirst(bigintobjvec_ixt ix)
{
  ix->i = -1;			/* next -> first */
}

static void 
bigintobjvec_ix_tolast(bigintobjvec_ixt ix)
{
  ix->i = ix->n;		/* prev -> last */
}

static void 
bigintobjvec_ix_to(bigintobjvec_ixt ix, int i)
{
  /* the actual position is n-i-1; conversion done in elting routine... */
  if (i < 0)
    ix->i = -1;
  else if (i > ix->n)
    ix->i = ix->n;
  else
    ix->i = i - 1;		/* next -> at i */
}

static void 
bigintobjvec_ix_next(bigintobjvec_ixt ix)
{
  ix->i = (ix->i < ix->n) ? ix->i + 1 : ix->n;
}

static void 
bigintobjvec_ix_prev(bigintobjvec_ixt ix)
{
  ix->i = (0 <= ix->i) ? ix->i - 1 : -1;
}

@implementation bigintobj_vector : vectorc

- check
{
  [super check];
  [scalarZero check];
  bigintobjvec_check(&value, bigintobjvec_getargs(self));
  return self;
}


- _setUpScalarZero:aScalarZero numScalars:(int)numScalars
{
  scalarZero = aScalarZero;
  bigintobjvec_initcount(&value, numScalars, bigintobjvec_getargs(self));
  assert([self check]);
  return self;
}

+ scalarZero:aScalarZero numScalars:(int)numScalars
{
  return [[super new] _setUpScalarZero:aScalarZero numScalars:numScalars];
}

- copy
{
  bigintobjvec_c c;

  assert([self check]);
  bigintobjvec_copy(&c, &value, bigintobjvec_getargs(self));
  return bigintobjvec_c2id(self, &c);
}

- deepCopy
{
  bigintobjvec_c c;

  assert([self check]);
  bigintobjvec_deepcopy(&c, &value, bigintobjvec_getargs(self));
  return bigintobjvec_c2id(self, &c);
}

- clear
{
  scalarZero = [scalarZero free];
  bigintobjvec_clear(&value, bigintobjvec_getargs(self));
  return [super clear];
}

- (bigintobjvec_t)bigintobjvec_value
{
  return &value;
}

- bigintobjvec_value:(bigintobjvec_t)aValue
{
  self = [self clone];
  bigintobjvec_copy(&value, aValue, bigintobjvec_getargs(self));
  assert([self check]);
  return self;
}

- (bigintobjvec_t)bigintobjvec_reference
{
  return &value;
}

- bigintobjvec_reference:(bigintobjvec_c *)aReference
{
  self = [self clone];
  bigintobjvec_move(&value, aReference, bigintobjvec_getargs(self));
  assert([self check]);
  return self;
}

- clone
{
  self = [super clone];
  scalarZero = [scalarZero cheapCopy];
  return self;
}

- capacity:(int)aCapacity
{
  bigintobjvec_c c;

  assert([self check]);
  bigintobjvec_initcapacity(&c, aCapacity, bigintobjvec_getargs(self));
  return bigintobjvec_c2id(self, &c);
}

- numScalars:(int)numScalars
{
  bigintobjvec_c c;

  assert([self check]);
  bigintobjvec_initcount(&c, numScalars, bigintobjvec_getargs(self));
  return bigintobjvec_c2id(self, &c);
}


- scalarZero
{
  return scalarZero;
}

- (int) numScalars
{
  assert([self check]);
  return bigintobjvec_count(&value);
}


- insertScalar:aScalar
{
  bigintobj_c c;

  assert([aScalar check]);
  [self checkRefCountOne];
  assert([self check]);
  bigintobjvec_insertfirst(&value, bigintobj_id2ref(aScalar, &c), bigintobjvec_getargs(self));
  assert([aScalar check]);
  [aScalar free];
  assert([self check]);
  [self invalidate];
  return self;
}

- insertScalar:aScalar at:(int)i
{
  bigintobj_c c;

  assert(aScalar);
  [self checkRefCountOne];
  assert([self check]);
  bigintobjvec_insertat(&value, bigintobj_id2ref(aScalar, &c), i, bigintobjvec_getargs(self));
  assert([aScalar check]);
  [aScalar free];
  assert([self check]);
  [self invalidate];
  return self;
}


- removeScalar
{
  if ([self numScalars]) {
    id res;
    bigintobj_t t = bigintobjvec_first(&value, bigintobjvec_getargs(self));

    assert([self check]);
    [self checkRefCountOne];
    res = bigintobj_t2id(scalarZero, t);
    bigintobjvec_freefirst(&value, bigintobjvec_getargs(self));
    assert([self check]);
    assert([res check]);
    [self invalidate];
    return res;
  } else {
    return nil;
  }
}

- removeScalarAt:(int)i
{
  id res;

  assert([self check]);
  [self checkRefCountOne];
  res = bigintobj_t2id(scalarZero, bigintobjvec_at(&value, i, bigintobjvec_getargs(self)));
  bigintobjvec_freeat(&value, i, bigintobjvec_getargs(self));
  assert([self check]);
  assert([res check]);
  [self invalidate];
  return res;
}


- placeScalar:aScalar at:(int)i
{
  bigintobj_c c;

  assert([aScalar check]);
  assert([self check]);
  [self checkRefCountOne];
  bigintobjvec_place(&value, bigintobj_id2ref(aScalar, &c), i, bigintobjvec_getargs(self));
  assert([aScalar check]);
  [aScalar free];
  assert([self check]);
  [self invalidate];
  return self;
}

- replaceScalarAt:(int)i with:aScalar
{
  id res;
  bigintobj_c c;
  bigintobj_t t = bigintobjvec_at(&value, i, bigintobjvec_getargs(self));

  assert([self check]);
  [self checkRefCountOne];
  res = bigintobj_t2id(scalarZero, t);
  bigintobjvec_place(&value, bigintobj_id2ref(aScalar, &c), i, bigintobjvec_getargs(self));
  assert([aScalar check]);
  [aScalar free];
  assert([self check]);
  assert([res check]);
  [self invalidate];
  return res;
}


- eachScalar
{
  id aSequence = [bigintobjvec_sequence content:[self cheapCopy]];

  return [CASequence over:aSequence];
}

- elt_bigintobjvec_ix:(bigintobjvec_ixt)ix
{
  if (bigintobjvec_ix_ismatch(ix)) {
    bigintobj_t t = bigintobjvec_elt(&value, ix, bigintobjvec_getargs(self));

    return bigintobj_t2id(scalarZero, t);
  } else {
    return nil;
  }
}


- dotMultiply:b
{
  bigintobj_c c;		/* carrier for scalar */

  bigintobjvec_oodotmul(&c, &value, [b bigintobjvec_value], bigintobjvec_getargs(self));
  /* now promote the carrier to object */
  /* all of this used to be very difficult in 0.6 */
  return bigintobj_c2id(scalarZero, &c);
}

- dotSquare
{
  bigintobj_c c;		/* carrier for scalar */

  bigintobjvec_oodotsqr(&c, &value, bigintobjvec_getargs(self));
  /* now promote the carrier to object */
  /* all of this used to be very difficult in 0.6 */
  return bigintobj_c2id(scalarZero, &c);
}


- fileOutOn:aFiler
{
  bigintobjvec_args args;

  assert([self check]);
  [super fileOutOn:aFiler];
  args = bigintobjvec_getargs(self);
  bigintobjvec_args_fileOut(aFiler, &args);
  bigintobjvec_fileOut(aFiler, &value, args);
  return self;
}

- fileInFrom:aFiler
{
  bigintobjvec_args args;

  [super fileInFrom:aFiler];
  bigintobjvec_args_fileIn(aFiler, &args);
  bigintobjvec_fileIn(aFiler, &value, args);
  return self;
}

- (unsigned) hash
{
  return bigintobjvec_hash(bigintobjvec_id2t(self), bigintobjvec_getargs(self));
}

- (BOOL) isEqual:b
{
  [self checkSameClass:b];
  if (self == b) {
    return YES;
  } else {
    return bigintobjvec_iseq(bigintobjvec_id2t(self), bigintobjvec_id2t(b), bigintobjvec_getargs(self));
  }
}

- (BOOL) notEqual:b
{
  [self checkSameClass:b];
  if (self == b) {
    return NO;
  } else {
    return !bigintobjvec_iseq(bigintobjvec_id2t(self), bigintobjvec_id2t(b), bigintobjvec_getargs(self));
  }
}

- (BOOL) isZero
{
  assert([self check]);
  return bigintobjvec_iszero(bigintobjvec_id2t(self), bigintobjvec_getargs(self));
}

- (BOOL) notZero
{
  assert([self check]);
  return !bigintobjvec_iszero(bigintobjvec_id2t(self), bigintobjvec_getargs(self));
}

- (BOOL) isOpposite:b
{
  assert([self checkSameClass:b] && self != b);
  return bigintobjvec_isop(bigintobjvec_id2t(self), bigintobjvec_id2t(b), bigintobjvec_getargs(self));
}

- (BOOL) notOpposite:b
{
  assert([self checkSameClass:b] && self != b);
  return !bigintobjvec_isop(bigintobjvec_id2t(self), bigintobjvec_id2t(b), bigintobjvec_getargs(self));
}

- negate
{
  bigintobjvec_c c;

  assert([self check]);
  bigintobjvec_ooneg(&c, bigintobjvec_id2t(self), bigintobjvec_getargs(self));
  return bigintobjvec_c2id(self, &c);
}

- negateSelf
{
  if (refCount > 1)
    return idReplace(self, [self negate]);
  assert([self check]);
  bigintobjvec_inneg(bigintobjvec_id2c(self), bigintobjvec_getargs(self));
  return [self invalidate];
}

- _double:(int)v
{
  bigintobjvec_c c;

  assert([self check]);
  bigintobjvec_oodbl(&c, bigintobjvec_id2t(self), v, bigintobjvec_getargs(self));
  return bigintobjvec_c2id(self, &c);
}

- _doubleSelf:(int)v
{
  if (refCount > 1)
    return idReplace(self, [self _double:v]);
  assert([self check]);
  bigintobjvec_indbl(bigintobjvec_id2c(self), v, bigintobjvec_getargs(self));
  return [self invalidate];
}

- double
{
  bigintobjvec_c c;

  assert([self check]);
  bigintobjvec_oodbl(&c, bigintobjvec_id2t(self), +1, bigintobjvec_getargs(self));
  return bigintobjvec_c2id(self, &c);
}

- doubleSelf
{
  if (refCount > 1)
    return idReplace(self, [self double]);
  assert([self check]);
  bigintobjvec_indbl(bigintobjvec_id2c(self), +1, bigintobjvec_getargs(self));
  return [self invalidate];
}

- _add:(int)v:b:(int)w
{
  bigintobjvec_c c;

  assert([self sameClass:b] && v * v == 1 && w * w == 1 && self != b);
  bigintobjvec_ooadd(&c, bigintobjvec_id2t(self), v, bigintobjvec_id2t(b), w, bigintobjvec_getargs(self));
  return bigintobjvec_c2id(self, &c);
}

- _addSelf:(int)v:b:(int)w
{
  assert([self sameClass:b] && v * v == 1 && w * w == 1 && self != b);
  if (refCount > 1)
    return idReplace(self, [self _add:v:b:w]);
  bigintobjvec_inadd(bigintobjvec_id2c(self), v, bigintobjvec_id2t(b), w, bigintobjvec_getargs(self));
  return [self invalidate];
}

- add:b
{
  bigintobjvec_c c;

  assert([self checkSameClass:b] && self != b);
  bigintobjvec_ooadd(&c, bigintobjvec_id2t(self), +1, bigintobjvec_id2t(b), +1, bigintobjvec_getargs(self));
  return bigintobjvec_c2id(self, &c);
}

- addSelf:b
{
  assert([self sameClass:b] && self != b);
  if (refCount > 1)
    return idReplace(self, [self add:b]);
  bigintobjvec_inadd(bigintobjvec_id2c(self), +1, bigintobjvec_id2t(b), +1, bigintobjvec_getargs(self));
  return [self invalidate];
}

- subtract:b
{
  bigintobjvec_c c;

  assert([self checkSameClass:b] && self != b);
  bigintobjvec_ooadd(&c, bigintobjvec_id2t(self), +1, bigintobjvec_id2t(b), -1, bigintobjvec_getargs(self));
  return bigintobjvec_c2id(self, &c);
}

- subtractSelf:b
{
  assert([self sameClass:b] && self != b);
  if (refCount > 1)
    return idReplace(self, [self subtract:b]);
  bigintobjvec_inadd(bigintobjvec_id2c(self), +1, bigintobjvec_id2t(b), -1, bigintobjvec_getargs(self));
  return [self invalidate];
}

- multiplyScalar:s
{
  bigintobjvec_c c;
  bigintobj_c sc;

  bigintobjvec_oomulsc(&c, bigintobjvec_id2t(self), bigintobj_id2ref(s, &sc), bigintobjvec_getargs(self));
  return bigintobjvec_c2id(self, &c);
}

- multiplySelfScalar:s
{
  bigintobj_c sc;

  if (refCount > 1)
    return idReplace(self, [self multiplyScalar:s]);
  bigintobjvec_inmulsc(bigintobjvec_id2c(self), bigintobj_id2ref(s, &sc), bigintobjvec_getargs(self));
  return [self invalidate];
}

- divideScalar:s
{
  bigintobjvec_c c;
  bigintobj_c sc;
  BOOL failed = bigintobjvec_oodivsc(&c, bigintobjvec_id2t(self), bigintobj_id2ref(s, &sc), bigintobjvec_getargs(self));

  return (failed) ? nil : bigintobjvec_c2id(self, &c);
}

- divideSelfScalar:s
{
  bigintobj_c sc;

  if (refCount > 1)
    return idReplace(self, [self divideScalar:s]);
  bigintobjvec_indivsc(bigintobjvec_id2c(self), bigintobj_id2ref(s, &sc), bigintobjvec_getargs(self));
  return [self invalidate];
}

- _add:(int)v:B multiplyScalar:b:(int)w
{
  bigintobjvec_c c;
  bigintobj_c bc;

  assert([self sameClass:B] && [scalarZero sameClass:b] && v * v == 1 && w * w == 1);
  bigintobjvec_ooaddmulsc(&c, bigintobjvec_id2t(self), v, bigintobjvec_id2t(B), bigintobj_id2ref(b, &bc), w, bigintobjvec_getargs(self));
  return bigintobjvec_c2id(self, &c);
}

- _addSelf:(int)v:B multiplyScalar:b:(int)w
{
  bigintobj_c bc;

  if (refCount > 1)
    return idReplace(self, [self _add:v:B multiplyScalar:b:w]);
  assert([self sameClass:B] && [scalarZero sameClass:b] && v * v == 1 && w * w == 1);
  bigintobjvec_inaddmulsc(bigintobjvec_id2c(self), v, bigintobjvec_id2t(B), bigintobj_id2ref(b, &bc), w, bigintobjvec_getargs(self));
  return [self invalidate];
}

- _multiplyScalar:a:(int)v add:B:(int)w
{
  bigintobjvec_c c;
  bigintobj_c ac;

  assert([scalarZero sameClass:a] && [self sameClass:B] && v * v == 1 && w * w == 1);
  bigintobjvec_oomulscadd(&c, bigintobjvec_id2t(self), bigintobj_id2ref(a, &ac), v, bigintobjvec_id2t(B), w, bigintobjvec_getargs(self));
  return bigintobjvec_c2id(self, &c);
}

- _multiplySelfScalar:a:(int)v add:B:(int)w
{
  bigintobj_c ac;

  if (refCount > 1)
    return idReplace(self, [self _multiplyScalar:a:v add:B:w]);
  assert([scalarZero sameClass:a] && [self sameClass:B] && v * v == 1 && w * w == 1);
  bigintobjvec_inmulscadd(bigintobjvec_id2c(self), bigintobj_id2ref(a, &ac), v, bigintobjvec_id2t(B), w, bigintobjvec_getargs(self));
  return [self invalidate];
}

- _multiplyScalar:a:(int)v add:B multiplyScalar:b:(int)w
{
  bigintobjvec_c c;
  bigintobj_c ac, bc;

  assert([scalarZero sameClass:a] && [self sameClass:B] && [scalarZero sameClass:b] && v * v == 1 && w * w == 1);
  bigintobjvec_oomulscaddmulsc(&c, bigintobjvec_id2t(self), bigintobj_id2ref(a, &ac), v, bigintobjvec_id2t(B), bigintobj_id2ref(b, &bc), w, bigintobjvec_getargs(self));
  return bigintobjvec_c2id(self, &c);
}

- _multiplySelfScalar:a:(int)v add:B multiplyScalar:b:(int)w
{
  bigintobj_c ac, bc;

  if (refCount > 1)
    return idReplace(self, [self _multiplyScalar:a:v add:B multiplyScalar:b:w]);
  assert([scalarZero sameClass:a] && [self sameClass:B] && [scalarZero sameClass:b] && v * v == 1 && w * w == 1);
  bigintobjvec_inmulscaddmulsc(bigintobjvec_id2c(self), bigintobj_id2ref(a, &ac), v, bigintobjvec_id2t(B), bigintobj_id2ref(b, &bc), w, bigintobjvec_getargs(self));
  return [self invalidate];
}
@end

