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

#include "cakit.h"

@implementation Monomial
- check
{
  assert(termOne != nil && scalar != nil && terms != nil);
  [termOne check];
  [scalar check];
  [terms checkRefCountOne];
  [terms check];
  return [super check];
}


+ new
{
  return [self error:"+new: Cannot create monomial without scalar"];
}

- _setUpScalar:aScalar terms:aCarrier
{
  scalar = aScalar;
  termOne = [[aCarrier termOne] cheapCopy];
  terms = aCarrier;

  assert([self check]);
  return self;
}

+ scalar:aScalar terms:aCarrier
{
  return [[super new] _setUpScalar:aScalar terms:aCarrier];
}

+ scalar:aScalar
{
  id aCarrier = [varsps_monomial scalarOne:[aScalar one]];

  return [self scalar:aScalar terms:aCarrier];
}

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

+ scalar:aScalar symbols:aCltn
{
  if ([aCltn isEmpty]) {
    return [self error:"Monomial: Cltn must not be empty"];
  } else {
    id aCarrier = [vardns_monomial scalarOne:[aScalar one] symbols:aCltn];

    return [self scalar:aScalar terms:aCarrier];
  }
}

- clone
{
  self = [super clone];
  termOne = [termOne cheapCopy];
  scalar = nil;
  terms = nil;
  _leadingTerm = nil;
  _symbols = nil;
  return self;
}

- scalar:aScalar terms:theTerms
{
  self = [self clone];
  scalar = aScalar;
  terms = theTerms;
  assert([self check]);
  return self;
}

- empty
{
  return [self scalar:[scalar one] terms:[terms empty]];
}

- copy
{
  return [self scalar:[scalar cheapCopy] terms:[terms copy]];
}

- deepCopy
{
  return [self scalar:[scalar deepCopy] terms:[terms deepCopy]];
}

- clear
{
  termOne = [termOne free];
  terms = [terms free];
  scalar = [scalar free];
  if (_leadingTerm)
    _leadingTerm = [_leadingTerm free];
  if (_symbols)
    _symbols = [_symbols free];
  return [super clear];
}

- invalidate
{
  if (_leadingTerm)
    _leadingTerm = [_leadingTerm free];
  if (_symbols)
    _symbols = [_symbols free];
  return [super invalidate];
}


- asNumerical
{
  id s = [scalar asNumerical];

  return [self scalar:s terms:[terms copy]];
}

- asModp:(unsigned short)p
{
  id s = [scalar asModp:p];

  return [self scalar:s terms:[terms copy]];
}

- (int) intValue
{
  if ([self numTerms]) {
    [self error:"-intValue: monomial not intValue"];
    return 0;
  } else {
    return [scalar intValue];
  }
}

- intValue:(int)aValue
{
  id c = [scalar intValue:aValue];

  return (c) ? [self scalar : c terms:[terms empty]]:nil;
}

- (float) floatValue
{
  if ([self numTerms]) {
    [self error:"-floatValue: monomial not floatValue"];
    return 0;
  } else {
    return [scalar floatValue];
  }
}

- floatValue:(float)aValue
{
  id c = [scalar floatValue:aValue];

  return (c) ? [self scalar : c terms:[terms empty]]:nil;
}

- (BOOL) isScalar
{
  return ([self numTerms] == 0);
}

- asScalar
{
  return ([self numTerms] == 0) ? [scalar cheapCopy] : nil;
}

- scalar:aScalar
{
  assert([scalar sameClass:aScalar]);
  return [self scalar:aScalar terms:[terms empty]];
}

- termsAsSymbol
{
  if ([self numTerms] == 1) {
    id lt = [self leadingTerm];

    return (lt) ? [lt asSymbol] : nil;
  } else {
    return nil;
  }
}

- termsSymbol:aSymbol
{
  if ([self isVariableDense] && [[terms symbols] offsetOf:aSymbol] == -1) {
    return nil;
  } else {
    id term = [termOne symbol:aSymbol];

    assert([termOne symbol] == nil && term);	/* never fails */
    return [[terms empty] insertTerm:term];
  }
}

- asSymbol
{
  return ([scalar isOne]) ? [self termsAsSymbol] : nil;
}

- symbol:aSymbol
{
  id t = [self termsSymbol:aSymbol];

  return (t) ? [self scalar : [scalar one] terms:t]:nil;
}

- (BOOL)sameClass:b
{
  return isa == [b class] && [scalar sameClass:[b scalar]] && [terms sameClass:[b terms]];
}

- (BOOL)inAdditiveSemiGroup		 {
  return YES;
}
- (BOOL)inAdditiveMonoid		 {
  return YES;
}
- (BOOL)inAdditiveGroup			 {
  return YES;
}

- (BOOL)inSemiGroup			 {
  return YES;
}
- (BOOL)inMonoid			 {
  return YES;
}
- (BOOL)inGroup				 {
  return NO;
}

- (BOOL)inEuclideanDomain		 {
  return NO;
}
- (BOOL)inIntegralDomain		 {
  return NO;
}
- (BOOL)inField				 {
  return NO;
}

- (BOOL)inOrderedSet			 {
  return NO;
}
- (int)characteristic			 {
  return [scalar characteristic];
}


- scalar
{
  return scalar;
}

- termOne
{
  return termOne;
}

- terms
{
  return terms;
}

- (BOOL) isMonic
{
  return [scalar isOne];
}

+ classTerms
{
  return [self doesNotRecognize:_cmd];
}

- (BOOL) isVariableSparse
{
  return [terms isVariableSparse];
}

- (BOOL) isVariableDense
{
  return [terms isVariableDense];
}

- (int) degree
{
  return [terms degree];
}

- (int) numTerms
{
  return [terms numTerms];
}

- (BOOL) isTerm
{
  return [terms numTerms] == 1;
}

- (BOOL) isEqual:aMonomial
{
  if (self == aMonomial) {
    return YES;
  } else {
    BOOL cmp;
    id aScalar = [aMonomial scalar];
    id aTerms = [aMonomial terms];

    cmp = [terms isEqual:aTerms];
    assert(cmp == NO || [terms compare:aTerms] == 0);
    return [scalar isEqual:aScalar] && cmp;
  }
}

- (unsigned) hash
{
  return ([scalar hash] << 24) + [terms hash];
}


- addSymbolsTo:aCltn
{
  id term, seq = [self eachTerm];

  while ((term = [seq next]))
    [term addSymbolsTo:aCltn];
  [seq free];
  return aCltn;
}

- symbols
{
  if ([self isVariableDense]) {
    return [terms symbols];
  } else {
    [self lock];
    if (_symbols == nil) {
      _symbols = [self addSymbolsTo:[CACollection new]];
      [[_symbols sort] reverse];
    }
    [self unlock];
    return _symbols;
  }
}

- (BOOL) isOrderDegreeCompatible
{
  return [self isVariableDense] && [[self symbols] size] == 1;
}

- (BOOL) isOrderReverseDegreeCompatible
{
  return NO;
}

- (int) compareTerms:aMonomial
{
  return [terms compare:[aMonomial terms]];
}


- removeTerm
{
  id lt;

  [self checkRefCountOne];
  assert([self check]);
  lt = [terms removeTerm];
  [self invalidate];
  assert([self check]);
  return lt;
}
- insertTerm:aTerm
{
  [self checkRefCountOne];
  assert([self check]);
  if ([aTerm isZero]) {
    [aTerm free];
    scalar = idReplace(scalar, [scalar zero]);
    terms = idReplace(terms, [terms empty]);
  } else {
    id c = [aTerm coefficient];

    if ([c isOne]) {
      terms = [terms insertTerm:aTerm];
    } else {
      id new = [aTerm coefficient:[c one] exponent:[aTerm exponent]];

      scalar = [scalar multiplySelf:c];
      [aTerm free];
      terms = [terms insertTerm:new];
    }
  }
  assert([self check]);
  return [self invalidate];
}


- eachTerm
{
  return [terms eachTerm];
}

- leadingTerm
{
  [self lock];
  if (_leadingTerm == nil && [self numTerms]) {
    id aTerm, aSequence = [self eachTerm];

    while ((aTerm = [aSequence next])) {
      if ([aTerm exponent]) {
	_leadingTerm = [aTerm cheapCopy];
	break;
      }
    }
    aSequence = [aSequence free];
  }
  [self unlock];
  return _leadingTerm;
}


- zero
{
  return [self scalar:[scalar zero] terms:[terms empty]];
}

- (BOOL) isZero
{
  return [scalar isZero];
}

- negate
{
  id s = [scalar negate];
  id t = [terms copy];

  return [self scalar:s terms:t];
}

- double
{
  id s = [scalar double];
  id t = [terms copy];

  return [self scalar:s terms:t];
}

- add:b
{
  if ([self compareTerms:b]) {
    return nil;
  } else {
    id s = [scalar add:[b scalar]];
    id t = [terms copy];

    return [self scalar:s terms:t];
  }
}

- subtract:b
{
  if ([self compareTerms:b]) {
    return nil;
  } else {
    id s = [scalar subtract:[b scalar]];
    id t = [terms copy];

    return [self scalar:s terms:t];
  }
}


- one
{
  return [self scalar:[scalar one] terms:[terms empty]];
}

- (BOOL) isOne
{
  return [scalar isOne] && [terms isEmpty];
}

- (BOOL) isMinusOne
{
  return [scalar isMinusOne] && [terms isEmpty];
}

- square
{
  return [self scalar:[scalar square] terms:[terms square]];
}

- multiply:b
{
  id s, t;

  [self checkSameClass:b];
  s = [scalar multiply:[b scalar]];
  t = [terms multiply:[b terms]];
  return [self scalar:s terms:t];
}

- multiplyScalar:s
{
  return [self scalar:[scalar multiply:s] terms:[terms copy]];
}

- divideScalar:s
{
  id c = [scalar divide:s];

  return (c) ? [self scalar : c terms:[terms copy]]:nil;
}

- divide:aMonomial
{
  id t;

  [self checkSameClass:aMonomial];
  if ((t = [terms divide:[aMonomial terms]])) {
    id s = [scalar divide:[aMonomial scalar]];

    return (s) ? [self scalar : s terms:t]:([t free], nil);
  } else {
    return nil;
  }
}

- divideTerms:aMonomial
{
  id t;

  [self checkSameClass:aMonomial];
  assert([self check] && [aMonomial check]);
  t = [terms divide:[aMonomial terms]];
  return (t) ? [self scalar : [scalar cheapCopy] terms:t]:nil;
}


- gcd:aMonomial
{
  id gcd = [terms gcd:[aMonomial terms]];

  return [self scalar:[scalar one] terms:gcd];
}

- lcm:aMonomial
{
  id lcm = [terms lcm:[aMonomial terms]];

  return [self scalar:[scalar one] terms:lcm];
}


- frobenius
{
  id aTerm, aSequence;
  id m = [terms empty];
  id s = [scalar frobenius];

  aSequence = [self eachTerm];
  while ((aTerm = [aSequence next]))
    [m insertTerm:[aTerm frobenius]];
  [aSequence free];

  return [self scalar:s terms:m];
}

- frobeniusInverse
{
  id s = [scalar frobeniusInverse];

  if (s) {
    id m = [terms empty];
    id aTerm, aSequence = [self eachTerm];

    while (aTerm = [aSequence next]) {
      aTerm = [aTerm frobeniusInverse];
      if (aTerm)
	[m insertTerm:aTerm];
      else {
	[m free];
	[s free];
	return nil;
      }
    }
    [aSequence free];

    return [self scalar:s terms:m];
  } else {
    return nil;
  }
}

- changeRepresentationTo:aPrototype
{
  id new, seq, term;

  assert([aPrototype isExpanded]);

  new = [aPrototype scalar:[scalar cheapCopy]];
  seq = [self eachTerm];
  while (term = [seq next]) {
    id x = [term changeRepresentationTo:aPrototype];

    new = [new multiplySelf:x];
    [x free];
  }
  seq = [seq free];

  return new;
}


- (BOOL) printsLeadingSign
{
  /* the structure of this method must match printing ! */

  if ([scalar isZero])
    return NO;
  if ([scalar isOne])
    return NO;
  if ([scalar isMinusOne])
    return YES;

  if ([terms isEmpty]) {
    return [scalar printsLeadingSign];
  } else {
    return ([scalar printsSum]) ? NO : [scalar printsLeadingSign];
  }
}

- (BOOL) printsSum
{
  /* the structure of this method must match printing ! */

  if ([scalar isZero])
    return NO;
  if ([scalar isOne])
    return NO;
  if ([scalar isMinusOne])
    return NO;

  if ([terms isEmpty]) {
    return [scalar printsSum];
  } else {
    return NO;
  }
}

- (BOOL) printsProduct
{
  /* the structure of this method must match printing ! */

  if ([scalar isZero])
    return NO;
  if ([scalar isOne])
    return [terms numTerms] > 0;
  if ([scalar isMinusOne])
    return [terms numTerms] > 0;

  if ([terms isEmpty]) {
    return [scalar printsProduct];
  } else {
    return YES;
  }
}

- printTermsToFile:(FILE *)aFile
{
  assert([self check]);

  if ([terms isEmpty]) {
    fprintf(aFile, "1");
  } else {
    id term, seq;

    seq = [self eachTerm];
    do {
      term = [seq next];
    } while ([term exponent] == 0);
    [term printOn:aFile];
    while ((term = [seq next])) {
      if ([term exponent]) {
	fprintf(aFile, " ");
	[term printOn:aFile];
      }
    }
    seq = [seq free];
  }

  return self;
}

- printOn:(IOD)aFile
{
  assert([self check]);

  if ([scalar isZero]) {
    fprintf(aFile, "0");
    return self;
  }
  if ([scalar isOne]) {
    return [self printTermsToFile:aFile];
  }
  if ([scalar isMinusOne]) {
    fprintf(aFile, "-");
    return [self printTermsToFile:aFile];
  }
  [scalar printOn:aFile];

  if ([terms isEmpty]) {
    return self;
  } else {
    fprintf(aFile, " ");

    if ([scalar printsSum]) {
      fprintf(aFile, "(");
      [self printTermsToFile:aFile];
      fprintf(aFile, ")");
    } else {
      [self printTermsToFile:aFile];
    }

    return self;
  }
}

@end

