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

#include "cakit.h"

@implementation CAObject

+ (STR) cakitRevision
{
  return __cakit_revision__;
}


- _setUpRefCount
{
  refCount = 1;
  return self;
}

- new
{
  return [self notImplemented:_cmd];
}

+ new
{
  return [[super new] _setUpRefCount];
}

+ str:(STR)aString
{
  return [self notImplemented:_cmd];
}

+ int:(int)intValue
{
  return [self notImplemented:_cmd];
}

+ stringValue:(const char *)aString
{
  return [self str:(STR) aString];
}

- clone
{
  self = [super copy];

  refCount = 1;

  _str = NULL;
  return self;
}

- copy
{
  self = [super copy];

  /* could invoke copyFromZone
   * invokes shallowCopy and deepen on GNU ObjC (GNU runtime)
   */

  refCount = 1;

  _str = NULL;
  return self;
}
- deepCopy
{
  self = [super copy];

  /* invokes copyFromZone: on 
   * invokes shallowCopy and deepen on GNU ObjC (GNU runtime)
   */

  refCount = 1;

  _str = NULL;
  return self;
}
- cheapCopy
{
  refCount++;
  return self;
}
- (BOOL) isCheapCopy
{
  return refCount > 1;
}

- checkRefCountOne
{
  if (refCount == 1) {
    return self;
  } else {
    return [self error:"Error: Object was created by -cheapCopy"];
  }
}

- check
{
  assert(refCount >= 1);
  return self;
}

- lock
{
  return self;
}

- unlock
{
  return self;
}

- invalidate
{
  assert(refCount == 1);
  if (_str)
    OC_Free(_str);
  assert([self check]);		/* now we must be ok again */
  return self;
}

- clear
{
  assert(refCount == 0);
  if (_str)
    OC_Free(_str);
  return self;
}

- free
{
  BOOL destroy;

  if (refCount < 1)
    [self error:"error in -free : refCount < 1 !"];

  /* if two threads will use -free, refCount must be >= 2
   * ie. the number of threads that attempt free > refCount
   */

  --refCount;
  destroy = (refCount == 0);

  if (destroy) {
    [self clear];
    return [super free];
  } else {
    /* the object might not even exist any more if a different thread
     * has concurrently executed free
     */
    return nil;
  }
}


- set
{
  return nil;
}

- (BOOL) sameClass:a
{
#if defined(__STPSTN__) || defined(__PORTABLE_OBJC__)
  return isa == [a class];
#else
  return isa == ((CAObject *) a)->isa;
#endif
}

- (BOOL) differentClass:a
{
  return [self sameClass:a] == NO;
}

- errorSetEqual
{
  return [self error:"Objects different class"];
}

- checkSetEqual:a
{
  if (self == nil || a == nil) {
    fprintf(stderr, "Object is *nil*.\n");
    abort();
  }
  if ([self differentClass:a]) {
    fprintf(stderr, "Objects different class : %s (%s) and %s (%s).\n", [self str], [self name], [a str], [a name]);
    abort();
  }
  return self;
}

- checkSameClass:a
{
  if (self == nil || a == nil) {
    fprintf(stderr, "Object is *nil*.\n");
    abort();
  }
  if ([self differentClass:a]) {
    fprintf(stderr, "Objects different class : %s (%s) and %s (%s).\n", [self str], [self name], [a str], [a name]);
    abort();
  }
  return self;
}

- (BOOL) isKindOfSequence
{
  return NO;
}

- class_vector
{
  return (id) [obj_vector class];
}

- class_varspsrecdegsps_polynomial
{
  return (id) [obj_varspsrecdegsps_polynomial class];
}

- class_varspsrecdegdns_polynomial
{
  return [self error:"No Variable Sparse, Recursive and Degree Dense Polynomials Yet"];
  /* return [obj_varspsrecdegdns_polynomial class]; */
}

- class_varspsexpdegsps_polynomial
{
  return (id) [obj_varspsexpdegsps_polynomial class];
}

- class_varspsexpdegdns_polynomial
{
  return [self error:"No Variable Sparse, Expanded and Degree Dense Polynomials Yet"];
  /* return [obj_varspsexpdegdns_polynomial class]; */
}

- class_vardnsrecdegsps_polynomial
{
  return (id) [obj_vardnsrecdegsps_polynomial class];
}

- class_vardnsrecdegdns_polynomial
{
  return (id) [obj_vardnsrecdegdns_polynomial class];
}

- class_vardnsexpdegsps_polynomial
{
  return (id) [obj_vardnsexpdegsps_polynomial class];
}

- class_vardnsexpdegdns_polynomial
{
  return [self error:"No Variable Dense, Expanded and Degree Dense Polynomials Yet"];
  /* return [obj_vardnsexpdegdns_polynomial class]; */
}


static int 
fileLen(FILE * aFile)
{
  int c = 0;

  rewind(aFile);
  while (feof(aFile) == 0) {
    fgetc(aFile);
    c++;
  }
  return c;
}

static void 
fileCpy(STR s, FILE * aFile)
{
  rewind(aFile);
  while (feof(aFile) == 0) {
    *s++ = fgetc(aFile);
  }
  *s++ = 0;
}

- (STR) str
{
  [self lock];
  if (_str) {
    [self unlock];
    return _str;
  } else {
    FILE *aFile = tmpfile();

    [self printOn:aFile];
    _str = (STR) OC_Malloc(fileLen(aFile) + 1);
    fileCpy(_str, aFile);
    fclose(aFile);
    [self unlock];
    return _str;
  }
}

- (const char *) stringValue
{
  return (const char *) [self str];
}

- str:(STR)aString
{
  return [self subclassResponsibility:_cmd];
}

- (int) intValue
{
  [self subclassResponsibility:_cmd];
  return 0;
}

- intValue:(int)i
{
  [self subclassResponsibility:_cmd];
  return nil;
}

- asModp:(unsigned short)p
{
  return [self subclassResponsibility:_cmd];
}

- (BOOL) isFloatingPoint
{
  return NO;
}

- asNumerical
{
  return [self subclassResponsibility:_cmd];
}

- (float) floatValue
{
  [self subclassResponsibility:_cmd];
  return 0;
}

- floatValue:(float)f
{
  [self subclassResponsibility:_cmd];
  return nil;
}

- asTotalFraction
{
  return [Fraction totalFraction:[self cheapCopy]];
}

- asScalar
{
  return [self notImplemented:_cmd];
}

- scalar:aScalar
{
  return [self notImplemented:_cmd];
}

- asSymbol
{
  return [self notImplemented:_cmd];
}

- symbol:aSymbol
{
  return nil;
}


- (BOOL) isEqual:a
{
  return self == a;
}

- (BOOL) notEqual:a
{
  return [self isEqual:a] == NO;
}


- (BOOL) inOrderedSet
{
  [self subclassResponsibility:_cmd];
  return NO;
}

- (int) compare:b
{
  [self subclassResponsibility:_cmd];
  return NO;
}

- (int) sign
{
  [self subclassResponsibility:_cmd];
  return 0;
}

- (BOOL) isLess:a
{
  return (self == a) ? NO : [self compare:a] < 0;
}

- (BOOL) isGreater:a
{
  return (self == a) ? NO : [self compare:a] > 0;
}

- (BOOL) isLessEqual:a
{
  return (self == a) ? YES : [self compare:a] <= 0;
}

- (BOOL) isGreaterEqual:a
{
  return (self == a) ? YES : [self compare:a] >= 0;
}

- absValue
{
  return ([self sign] < 0) ? [self negate] : [self cheapCopy];
}

- absValueSelf
{
  return ([self sign] > 0) ? self : [self negateSelf];
}


- (BOOL) inAdditiveSemiGroup
{
  [self subclassResponsibility:_cmd];
  return NO;
}

- (BOOL) inAdditiveMonoid
{
  [self subclassResponsibility:_cmd];
  return NO;
}

- (BOOL) inAdditiveGroup
{
  [self subclassResponsibility:_cmd];
  return NO;
}

- (BOOL) isZero
{
  [self subclassResponsibility:_cmd];
  return NO;
}

- (BOOL) notZero
{
  return [self isZero] == NO;
}

- (BOOL) isOpposite:b
{
  [self subclassResponsibility:_cmd];
  return NO;
}

- (BOOL) notOpposite:b
{
  return [self isOpposite:b] == NO;
}

- zero
{
  [self subclassResponsibility:_cmd];
  return nil;
}

- negate
{
  [self subclassResponsibility:_cmd];
  return nil;
}

- negateSelf
{
  return idReplace(self, [self negate]);
}

- _negate:(int)v
{
  assert(v == +1 || v == -1);
  return (v == +1) ? [self cheapCopy] : [self negate];
}

- _negateSelf:(int)v
{
  assert(v == +1 || v == -1);
  return (v == +1) ? self : [self negateSelf];
}

- add:b
{
  [self subclassResponsibility:_cmd];
  return nil;
}

- addSelf:b
{
  return idReplace(self, [self add:b]);
}

- _add:(int)v:b:(int)w
{
  switch (v) {
    case +1:return (w == +1) ? [self add : b]:[self subtract:b];
    case -1:return (w == +1) ? [b subtract : self]:[[self add:b] negateSelf];
  }
  return nil;
}

- _addSelf:(int)v:b:(int)w
{
  return idReplace(self, [self _add:v:b:w]);
}

- subtract:b
{
  [self subclassResponsibility:_cmd];
  return nil;
}

- subtractSelf:b
{
  return idReplace(self, [self subtract:b]);
}

- subtractSelfLeft:b
{
  return idReplace(self, [b subtract:self]);
}

- increment
{
  id one = [self one];

  self = [self add:one];
  [one free];
  return self;
}

- incrementSelf
{
  return idReplace(self, [self increment]);
}

- decrement
{
  id one = [self one];

  self = [self subtract:one];
  [one free];
  return self;
}

- decrementSelf
{
  return idReplace(self, [self decrement]);
}

- multiplyIntValue:(int)b
{
  id res = [self intValue:b];

  return idReplace(res, [self multiply:res]);
}
- multiplySelfIntValue:(int)b
{
  return idReplace(self, [self multiplyIntValue:b]);
}

- double
{
  [self subclassResponsibility:_cmd];
  return nil;
}

- doubleSelf
{
  return idReplace(self, [self double]);
}

- _double:(int)v
{
  return (v == +1) ? [self double] : [[self double] negateSelf];
}

- _doubleSelf:(int)v
{
  return idReplace(self, [self _double:v]);
}

- quadruple
{
  [self subclassResponsibility:_cmd];
  return nil;
}

- quadrupleSelf
{
  [self subclassResponsibility:_cmd];
  return nil;
}

- divideIntValue:(int)b
{
  [self subclassResponsibility:_cmd];
  return nil;
}

- divideSelfIntValue:(int)b
{
  return idReplace(self, [self divideIntValue:b]);
}

- half
{
  [self subclassResponsibility:_cmd];
  return nil;
}

- halfSelf
{
  [self subclassResponsibility:_cmd];
  return nil;
}

- quarter
{
  [self subclassResponsibility:_cmd];
  return nil;
}

- quarterSelf
{
  [self subclassResponsibility:_cmd];
  return nil;
}


- (BOOL) commutes
{
  [self subclassResponsibility:_cmd];
  return NO;
}

- (BOOL) commutesWith:b
{
  [self subclassResponsibility:_cmd];
  return NO;
}

- (BOOL) inSemiGroup
{
  [self subclassResponsibility:_cmd];
  return NO;
}

- (BOOL) inMonoid
{
  [self subclassResponsibility:_cmd];
  return NO;
}

- (BOOL) inGroup
{
  [self subclassResponsibility:_cmd];
  return NO;
}

- one
{
  [self subclassResponsibility:_cmd];
  return nil;
}

- minusOne
{
  return [[self one] negateSelf];
}

- (BOOL) isOne
{
  [self subclassResponsibility:_cmd];
  return NO;
}

- (BOOL) notOne
{
  return [self isOne] == NO;
}

- (BOOL) isMinusOne
{
  [self subclassResponsibility:_cmd];
  return NO;
}

- (BOOL) notMinusOne
{
  return [self isMinusOne] == NO;
}

- square
{
  [self subclassResponsibility:_cmd];
  return nil;
}

- squareSelf
{
  return idReplace(self, [self square]);
}

- _square:(int)w
{
  return (w == +1) ? [self square] : [[self square] negateSelf];
}

- _squareSelf:(int)w
{
  return (w == +1) ? [self squareSelf] : [[self squareSelf] negateSelf];
}

- multiply:b
{
  [self subclassResponsibility:_cmd];
  return nil;
}

- multiplySelf:b
{
  return idReplace(self, [self multiply:b]);
}

- multiplySelfLeft:b
{
  return idReplace(self, [b multiply:self]);
}

- _multiply:b:(int)w
{
  id res = [self multiply:b];

  return (w == +1) ? res : [res negateSelf];
}

- _multiplySelf:b:(int)w
{
  self = [self multiplySelf:b];
  return (w == +1) ? self : [self negateSelf];
}

- power:(int)n
{
  id o, e;

  if (n < 0) {
    id i = [self inverse];

    return (i) ? [i powerSelf : -n]:nil;
  }
  switch (n) {
    case 0: {
      return [self one];
    }
  case 1:{
      return [self cheapCopy];
    }
  case 2:{
      return [self square];
    }
  }
  if ([self inMonoid] && [self isOne])
    return [self cheapCopy];
  if ([self inAdditiveMonoid]) {
    if ([self isZero]) {
      return [self cheapCopy];
    }
    if ([self isMinusOne]) {
      return ODD(n) ? [self cheapCopy] : [self one];
    }
  }
  o = [self one];
  e = [self cheapCopy];
  while (n > 1) {
    if (n & 1)
      o = [o multiplySelf:e];
    e = [e squareSelf];
    n >>= 1;
  }

  e = [e multiplySelf:o];
  o = [o free];
  return e;
}

- powerSelf:(int)n
{
  return idReplace(self, [self power:n]);
}

- inverse
{
  [self subclassResponsibility:_cmd];
  return nil;
}

- inverseSelf
{
  return idReplace(self, [self inverse]);
}

- (BOOL) isUnit
{
  id inv = [self inverse];

  return (inv) ? ([inv free], YES) : NO;
}

- (BOOL) notUnit
{
  return NO == [self isUnit];
}

- divide:b
{
  [self subclassResponsibility:_cmd];
  return nil;
}

- divideSelf:b
{
  return idReplace(self, [self divide:b]);
}

- divideSelfLeft:b
{
  return idReplace(self, [b divide:self]);
}


- remainder:b quotient:(id *)q
{
  [self subclassResponsibility:_cmd];
  return nil;
}
- remainderSelf:b quotient:(id*)q
{
  return idReplace(self, [self remainder:b quotient:q]);
}

- remainder:b
{
  return [self remainder:b quotient:NULL];
}
- remainderSelf:b
{
  return [self remainderSelf:b quotient:NULL];
}

- quotient:b
{
  id q, r;

  if (r = [self remainder:b quotient:&q]) {
    [r free];
    return q;
  } else {
    return nil;
  }
}
- quotientSelf:b
{
  return idReplace(self, [self quotient:b]);
}


- (BOOL) inEuclideanDomain
{
  [self subclassResponsibility:_cmd];
  return NO;
}

- (BOOL) isCoprime:b
{
  id g;
  BOOL res;

  g = [self gcd:b];
  res = [g isUnit];
  [g free];
  return res;
}

- (BOOL) notCoprime:b
{
  return [self isCoprime:b] == NO;
}

- (BOOL) isGcd:a:b
{
  id g;
  BOOL res;

  g = [a gcd:b];
  res = [g isEqual:self];
  [g free];
  return res;
}

- (BOOL) isLcm:a:b
{
  id g;
  BOOL res;

  g = [a lcm:b];
  res = [g isEqual:self];
  [g free];
  return res;
}

- gcd:b
{
  return [self subclassResponsibility:_cmd];
}

- gcdSelf:b
{
  return idReplace(self, [self gcd:b]);
}

- bezout:b gcd:(id *)gcd
{
  return [self subclassResponsibility:_cmd];
}

- lcm:b
{
  id lcm, gcd;

  if ([self inAdditiveSemiGroup]) {
    if ([self isZero])
      return [b cheapCopy];
    if ([b isZero])
      return [self cheapCopy];
  }
  gcd = [self gcd:b];
  lcm = [self multiply:b];
  lcm = [lcm divideSelf:gcd];
  [gcd free];
  return lcm;
}

- lcmSelf:b
{
  return idReplace(self, [self lcm:b]);
}


- modulo:m
{
  id r = [self remainder:m];

  if ([self inOrderedSet] && [r sign] < 0)
    r = [r addSelf:m];
  return r;
}

- moduloSelf:m
{
  self = [self remainderSelf:m];
  if ([self inOrderedSet] && [self sign] < 0)
    self = [self addSelf:m];
  return self;
}

- multiply:b modulo:m
{
  return [[self multiply:b] moduloSelf:m];
}

- multiplySelf:b modulo:m
{
  return [[self multiplySelf:b] moduloSelf:m];
}

- squareModulo:m
{
  return [[self square] moduloSelf:m];
}

- squareSelfModulo:m
{
  return [[self squareSelf] moduloSelf:m];
}

- power:(int)n modulo:m
{
  id o, e;

  if (n < 0)
    return [self error:"Negative Exponent"];

  switch (n) {
    case 0: {
      return ([self isZero]) ? nil : [self one];
    }
  case 1:{
      return [self modulo:m];
    }
  case 2:{
      return [self squareModulo:m];
    }
  }

  if ([self isOne]) {
    return [self cheapCopy];
  }
  if ([self isZero]) {
    return [self cheapCopy];
  }
  if ([self isMinusOne]) {
    return ODD(n) ? [self cheapCopy] : [self one];
  }
  o = [self one];
  e = [self cheapCopy];
  while (n > 1) {
    if (n & 1)
      o = [o multiplySelf:e modulo:m];
    e = [e squareSelfModulo:m];
    n >>= 1;
  }

  e = [e multiplySelf:o modulo:m];
  o = [o free];
  return e;
}

- powerSelf:(int)n modulo:m
{
  return idReplace(self, [self power:n modulo:m]);
}

- inverseModulo:m
{
  return [self subclassResponsibility:_cmd];
}

- inverseSelfModulo:m
{
  return idReplace(self, [self inverseModulo:m]);
}


- random
{
  return [self subclassResponsibility:_cmd];
}


- (int) characteristic
{
  [self subclassResponsibility:_cmd];
  return 0;
}

- (BOOL) isCharacteristicZero
{
  return [self characteristic] == 0;
}

- (BOOL) notCharacteristicZero
{
  return [self characteristic] != 0;
}

- (BOOL) isCharacteristicTwo
{
  return [self characteristic] == 2;
}

- (BOOL) notCharacteristicTwo
{
  return [self characteristic] != 2;
}

- frobenius
{
  return [self subclassResponsibility:_cmd];
}

- frobeniusSelf
{
  return idReplace(self, [self frobenius]);
}

- frobeniusInverse
{
  return [self subclassResponsibility:_cmd];
}

- frobeniusInverseSelf
{
  return idReplace(self, [self frobeniusInverse]);
}

- (int) dimensionOverPrimeField
{
  [self subclassResponsibility:_cmd];
  return 0;
}


- (BOOL) inRing
{
  return [self inAdditiveGroup] && [self inMonoid];
}

- (BOOL) inIntegralDomain
{
  [self subclassResponsibility:_cmd];
  return NO;
}

- (BOOL) inField
{
  return [self inAdditiveGroup] && [self inGroup];
}

- (BOOL) inFieldOfFractions
{
  return NO;
}

- _addSquare:(int)v:b:(int)w
{
  id t = [b square];

  self = [self _add:v:t:w];
  [t free];
  return self;
}

- _addSelfSquare:(int)v:b:(int)w
{
  id t = [b square];

  self = [self _addSelf:v:t:w];
  [t free];
  return self;
}

- _add:(int)v:B multiply:b:(int)w
{
  id t = [B multiply:b];

  self = [self _add:v:t:w];
  [t free];
  return self;
}

- _addSelf:(int)v:B multiply:b:(int)w
{
  id t = [B multiply:b];

  self = [self _addSelf:v:t:w];
  [t free];
  return self;
}

- _multiply:a:(int)v add:B:(int)w
{
  return [[self multiply:a] _addSelf:v:B:w];
}

- _multiplySelf:a:(int)v add:B:(int)w
{
  return [[self multiplySelf:a] _addSelf:v:B:w];
}

- _multiply:a:(int)v add:B multiply:b:(int)w
{
  return [[self multiply:a] _addSelf:v:B multiply:b:w];
}

- _multiplySelf:a:(int)v add:B multiply:b:(int)w
{
  return [[self multiplySelf:a] _addSelf:v:B multiply:b:w];
}

- _multiply:a:(int)v divide:d
{
  return [[self _multiply:a:v] divideSelf:d];
}

- _multiplySelf:a:(int)v divide:d
{
  return [[self _multiplySelf:a:v] divideSelf:d];
}

- _multiply:a:(int)v add:B multiply:b:(int)w divide:d
{
  return [[self _multiply:a:v add:B multiply:b:w] divideSelf:d];
}

- _multiplySelf:a:(int)v add:B multiply:b:(int)w divide:d
{
  return [[self _multiplySelf:a:v add:B multiply:b:w] divideSelf:d];
}


- scalarZero
{
  [self subclassResponsibility:_cmd];
  return nil;
}
- scalarContent
{
  [self subclassResponsibility:_cmd];
  return nil;
}

- divideScalarContent
{
  id new, c;

  c = [self scalarContent];
  new = ([c isZero]) ? [self cheapCopy] : [self divideScalar:c];
  [c free];
  return new;
}

- divideSelfScalarContent
{
  return idReplace(self, [self divideScalarContent]);
}

- multiplyScalar:s
{
  [self subclassResponsibility:_cmd];
  return nil;
}

- multiplySelfScalar:s
{
  return idReplace(self, [self multiplyScalar:s]);
}

- _multiplyScalar:s:(int)v
{
  id res = [self multiplyScalar:s];

  return (v == +1) ? res : [res negateSelf];
}

- _multiplySelfScalar:s:(int)v
{
  self = [self multiplySelfScalar:s];
  return (v == +1) ? self : [self negateSelf];
}

- divideScalar:s
{
  [self subclassResponsibility:_cmd];
  return nil;
}

- divideSelfScalar:s
{
  return idReplace(self, [self divideScalar:s]);
}

- addScalar:s
{
  [self subclassResponsibility:_cmd];
  return nil;
}

- addSelfScalar:s
{
  return idReplace(self, [self addScalar:s]);
}

- subtractScalar:s
{
  [self subclassResponsibility:_cmd];
  return nil;
}

- subtractSelfScalar:s
{
  return idReplace(self, [self subtractScalar:s]);
}

- _add:(int)v:B multiplyScalar:b:(int)w
{
  id t = [B multiplyScalar:b];

  self = [self _add:v:t:w];
  [t free];
  return self;
}

- _addSelf:(int)v:B multiplyScalar:b:(int)w
{
  id t = [B multiplyScalar:b];

  self = [self _addSelf:v:t:w];
  [t free];
  return self;
}

- _multiplyScalar:a:(int)v add:B:(int)w
{
  return [[self multiplyScalar:a] _addSelf:v:B:w];
}

- _multiplySelfScalar:a:(int)v add:B:(int)w
{
  return [[self multiplySelfScalar:a] _addSelf:v:B:w];
}

- _multiplyScalar:a:(int)v add:B multiplyScalar:b:(int)w
{
  return [[self multiplyScalar:a] _addSelf:v:B multiplyScalar:b:w];
}

- _multiplySelfScalar:a:(int)v add:B multiplyScalar:b:(int)w
{
  return [[self multiplySelfScalar:a] _addSelf:v:B multiplyScalar:b:w];
}

- _multiplyScalar:a:(int)v divideScalar:d
{
  return [[self _multiplyScalar:a:v] divideSelfScalar:d];
}

- _multiplySelfScalar:a:(int)v divideScalar:d
{
  return [[self _multiplySelfScalar:a:v] divideSelfScalar:d];
}

- _multiplyScalar:a:(int)v add:B multiplyScalar:b:(int)w divideScalar:d
{
  return [[self _multiplyScalar:a:v add:B multiplyScalar:b:w] divideSelfScalar:d];
}

- _multiplySelfScalar:a:(int)v add:B multiplyScalar:b:(int)w divideScalar:d
{
  return [[self _multiplySelfScalar:a:v add:B multiplyScalar:b:w] divideSelfScalar:d];
}

- subtractSelf:r multiplyScalar:q
{
  return [self _addSelf:+1:r multiplyScalar:q:-1];
}

- multiplySelfScalar:p subtract:r multiplyScalar:q divideScalar:d
{
  return [self _multiplySelfScalar:p:+1 add:r multiplyScalar:q:-1 divideScalar:d];
}

- multiplySelfScalar:p divideScalar:d
{
  return [self _multiplySelfScalar:p:+1 divideScalar:d];
}

- multiplyCoefficient:s
{
  [self subclassResponsibility:_cmd];
  return nil;
}

- multiplySelfCoefficient:s
{
  return idReplace(self, [self multiplyCoefficient:s]);
}

- _multiplyCoefficient:s:(int)v
{
  id res = [self multiplyCoefficient:s];

  return (v == +1) ? res : [res negateSelf];
}

- _multiplySelfCoefficient:s:(int)v
{
  self = [self multiplySelfCoefficient:s];
  return (v == +1) ? self : [self negateSelf];
}

- divideCoefficient:s
{
  [self subclassResponsibility:_cmd];
  return nil;
}

- divideSelfCoefficient:s
{
  return idReplace(self, [self divideCoefficient:s]);
}

- _add:(int)v:B multiplyCoefficient:b:(int)w
{
  id t = [B multiplyCoefficient:b];

  self = [self _add:v:t:w];
  [t free];
  return self;
}

- _addSelf:(int)v:B multiplyCoefficient:b:(int)w
{
  id t = [B multiplyCoefficient:b];

  self = [self _addSelf:v:t:w];
  [t free];
  return self;
}

- _multiplyCoefficient:a:(int)v add:B:(int)w
{
  return [[self multiplyCoefficient:a] _addSelf:v:B:w];
}

- _multiplySelfCoefficient:a:(int)v add:B:(int)w
{
  return [[self multiplySelfCoefficient:a] _addSelf:v:B:w];
}

- _multiplyCoefficient:a:(int)v add:B multiplyCoefficient:b:(int)w
{
  return [[self multiplyCoefficient:a] _addSelf:v:B multiplyCoefficient:b:w];
}

- _multiplySelfCoefficient:a:(int)v add:B multiplyCoefficient:b:(int)w
{
  return [[self multiplySelfCoefficient:a] _addSelf:v:B multiplyCoefficient:b:w];
}

- _multiplyCoefficient:a:(int)v divideCoefficient:d
{
  return [[self _multiplyCoefficient:a:v] divideSelfCoefficient:d];
}

- _multiplySelfCoefficient:a:(int)v divideCoefficient:d
{
  return [[self _multiplySelfCoefficient:a:v] divideSelfCoefficient:d];
}

- _multiplyCoefficient:a:(int)v add:B multiplyCoefficient:b:(int)w divideCoefficient:d
{
  return [[self _multiplyCoefficient:a:v add:B multiplyCoefficient:b:w] divideSelfCoefficient:d];
}

- _multiplySelfCoefficient:a:(int)v add:B multiplyCoefficient:b:(int)w divideCoefficient:d
{
  return [[self _multiplySelfCoefficient:a:v add:B multiplyCoefficient:b:w] divideSelfCoefficient:d];
}
- multiplyTerm:s
{
  [self subclassResponsibility:_cmd];
  return nil;
}

- multiplySelfTerm:s
{
  return idReplace(self, [self multiplyTerm:s]);
}

- _multiplyTerm:s:(int)v
{
  id res = [self multiplyTerm:s];

  return (v == +1) ? res : [res negateSelf];
}

- _multiplySelfTerm:s:(int)v
{
  self = [self multiplySelfTerm:s];
  return (v == +1) ? self : [self negateSelf];
}

- divideTerm:s
{
  [self subclassResponsibility:_cmd];
  return nil;
}

- divideSelfTerm:s
{
  return idReplace(self, [self divideTerm:s]);
}

- _add:(int)v:B multiplyTerm:b:(int)w
{
  id t = [B multiplyTerm:b];

  self = [self _add:v:t:w];
  [t free];
  return self;
}

- _addSelf:(int)v:B multiplyTerm:b:(int)w
{
  id t = [B multiplyTerm:b];

  self = [self _addSelf:v:t:w];
  [t free];
  return self;
}

- _multiplyCoefficient:a:(int)v add:B multiplyTerm:b:(int)w
{
  return [[self multiplyCoefficient:a] _addSelf:v:B multiplyTerm:b:w];
}

- _multiplySelfCoefficient:a:(int)v add:B multiplyTerm:b:(int)w
{
  return [[self multiplySelfCoefficient:a] _addSelf:v:B multiplyTerm:b:w];
}

- addSelf:r multiplyTerm:q
{
  return [self _addSelf:+1:r multiplyTerm:q:+1];
}

- subtractSelf:r multiplyTerm:q
{
  return [self _addSelf:+1:r multiplyTerm:q:-1];
}

- multiplySelfCoefficient:p subtract:r multiplyTerm:q
{
  return [self _multiplySelfCoefficient:p:+1 add:r multiplyTerm:q:-1];
}
- multiplyMonomial:s
{
  [self subclassResponsibility:_cmd];
  return nil;
}

- multiplySelfMonomial:s
{
  return idReplace(self, [self multiplyMonomial:s]);
}

- _multiplyMonomial:s:(int)v
{
  id res = [self multiplyMonomial:s];

  return (v == +1) ? res : [res negateSelf];
}

- _multiplySelfMonomial:s:(int)v
{
  self = [self multiplySelfMonomial:s];
  return (v == +1) ? self : [self negateSelf];
}

- divideMonomial:s
{
  [self subclassResponsibility:_cmd];
  return nil;
}

- divideSelfMonomial:s
{
  return idReplace(self, [self divideMonomial:s]);
}

- _add:(int)v:B multiplyMonomial:b:(int)w
{
  id t = [B multiplyMonomial:b];

  self = [self _add:v:t:w];
  [t free];
  return self;
}

- _addSelf:(int)v:B multiplyMonomial:b:(int)w
{
  id t = [B multiplyMonomial:b];

  self = [self _addSelf:v:t:w];
  [t free];
  return self;
}

- _multiplyScalar:a:(int)v add:B multiplyMonomial:b:(int)w
{
  return [[self multiplyScalar:a] _addSelf:v:B multiplyMonomial:b:w];
}

- _multiplySelfScalar:a:(int)v add:B multiplyMonomial:b:(int)w
{
  return [[self multiplySelfScalar:a] _addSelf:v:B multiplyMonomial:b:w];
}

- addSelf:r multiplyMonomial:q
{
  return [self _addSelf:+1:r multiplyMonomial:q:+1];
}

- subtractSelf:r multiplyMonomial:q
{
  return [self _addSelf:+1:r multiplyMonomial:q:-1];
}

- multiplySelfScalar:p subtract:r multiplyMonomial:q
{
  return [self _multiplySelfScalar:p:+1 add:r multiplyMonomial:q:-1];
}

- (BOOL) printsLeadingSign
{
  return NO;
}

- (BOOL) printsSum
{
  return NO;
}

- (BOOL) printsProduct
{
  return NO;
}

- printOn:(IOD)aFile
{
  [self subclassResponsibility:_cmd];
  return self;
}


- fileOut:(void *)value type:(char)typeDesc
{
  return [self notImplemented:_cmd];
}

- fileIn:(void *)value type:(char)typeDesc
{
  return [self notImplemented:_cmd];
}

- fileOutOn:aFiler
{
  [super fileOutOn:aFiler];
  [aFiler fileOut:&refCount type:'i'];
  return self;
}

- fileInFrom:aFiler
{
  [super fileInFrom:aFiler];
  [aFiler fileIn:&refCount type:'i'];
  return self;
}

- addReference
{
  return [self cheapCopy];
}

@end

id 
idReplace(id old, id new)
{
  return (new) ? ([old free], new) : nil;
}

