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

#include "cakit.h"

@implementation Fraction
- check
{
  assert(numerator && denominator);
  [numerator check];
  [denominator check];
  if ([numerator sameClass:denominator]) {
    if ([numerator inIntegralDomain]) {
      assert([numerator isCoprime:denominator]);
    }
    if ([numerator inOrderedSet])
      assert([denominator sign] >= 0);
  }
  return [super check];
}


- totalFraction:aNumerator
{
  return [self notImplemented:_cmd];
}

+ totalFraction:aNumerator
{
  return [self numerator:aNumerator denominator:[aNumerator one]];
}

- _setUpNumerator:aNumerator denominator:aDenominator
{
  numerator = aNumerator;
  denominator = aDenominator;
  assert([self check]);
  return self;
}

+ numerator:aNumerator denominator:aDenominator
{
  return [[super new] _setUpNumerator:aNumerator denominator:aDenominator];
}

- copy
{
  self = [super copy];
  numerator = [numerator cheapCopy];
  denominator = [denominator cheapCopy];
  return self;
}

- deepCopy
{
  self = [super deepCopy];
  numerator = [numerator deepCopy];
  denominator = [denominator deepCopy];
  return self;
}

- clear
{
  [numerator free];
  [denominator free];
  return [super clear];
}

static id 
mulMap(id numerator, id denominator)
{
  return [numerator multiply:denominator];	/* A x S -> A currently only total fractions */
}

- numerator:n denominator:d
{
  assert(n && d);

  if ([numerator sameClass:denominator]) {
    if ([numerator inIntegralDomain]) {
      id gcd = [n gcd:d];

      if ([gcd notOne]) {
	n = [n divideSelf:gcd];
	d = [d divideSelf:gcd];
      }
      gcd = [gcd free];
    }
    if ([numerator inOrderedSet] && [d sign] < 0) {
      n = [n negateSelf];
      d = [d negateSelf];
    }
  }
  self = [self clone];
  numerator = n;
  denominator = d;
  return self;
}

- numerator:aNumerator
{
  return [self numerator:aNumerator denominator:[denominator cheapCopy]];
}

- denominator:aDenominator
{
  return [self numerator:[numerator cheapCopy] denominator:aDenominator];
}

- denominator:aNumerator numerator:aDenominator
{
  return [self numerator:aNumerator denominator:aDenominator];
}

- numerator:aNumerator numerator:aDenominator
{
  return [self numerator:aNumerator denominator:aDenominator];
}

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

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

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

- (BOOL)inEuclideanDomain		 {
  return YES;
}
- (BOOL)inIntegralDomain		 {
  return YES;
}
- (BOOL)inField				 {
  return YES;
}
- (BOOL)inFieldOfFractions		 {
  return YES;
}

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


- numerator
{
  return numerator;
}

- denominator
{
  return denominator;
}

- (unsigned) hash
{
  return ([numerator hash] << 16) + ([denominator hash] & 0x0000ffff);
}

- (BOOL) isEqual:b
{
  if (self == b) {
    return YES;
  } else {
    id bn = [b numerator];
    id bd = [b denominator];

    if ([numerator isEqual:bn] && [denominator isEqual:bd])
      return YES;
    if ([numerator inIntegralDomain]) {
      if ([numerator inOrderedSet]) {
	return NO;
      } else {
	if ([numerator isOpposite:bn] && [denominator isOpposite:bd])
	  return YES;
	return NO;
      }
    } else {
      BOOL eq;
      id t = [self subtract:b];

      eq = [t isZero];
      [t free];
      return eq;
    }
    return NO;
  }
}

- (int) compare:b
{
  if (self == b) {
    return 0;
  } else {
    int cmp;
    id ad = mulMap(numerator, [b denominator]);
    id cb = mulMap([b numerator], denominator);

    cmp = [ad compare:cb];
    [ad free];
    [cb free];
    return cmp;
  }
}


- asNumerical
{
  id r;
  id n = [numerator asNumerical];
  id d = [denominator asNumerical];

  r = [n divide:d];
  [n free];
  [d free];
  return r;
}

- asModp:(unsigned short)p
{
  id r;
  id n = [numerator asModp:p];
  id d = [denominator asModp:p];

  if ((r = [n divide:d])) {
    [n free];
    [d free];
    return r;
  } else {
    return [self error:"asModp: division failed"];
  }
}

- asIntegral
{
  if ([denominator isOne]) {
    return [numerator cheapCopy];
  } else {
    return [self error:"-asIntegral: denominator not one"];
  }
}


- zero
{
  return [self numerator:[numerator zero]];
}

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

- (BOOL) isOpposite:b
{
  if (self == b) {
    return [self isZero];
  } else {
    id bn = [b numerator];
    id bd = [b denominator];

    if ([numerator isOpposite:bn] && [denominator isEqual:bd])
      return YES;
    if ([numerator inIntegralDomain]) {
      if ([numerator inOrderedSet]) {
	return NO;
      } else {
	if ([numerator isEqual:bn] && [denominator isOpposite:bd])
	  return YES;
	return NO;
      }
    } else {
      BOOL eq;
      id t = [self add:b];

      eq = [t isZero];
      [t free];
      return eq;
    }
    return NO;
  }
}

- negate
{
  if ([self isZero])
    return [self cheapCopy];
  return [self numerator:[numerator negate]];
}

- double
{
  if ([self isZero])
    return [self cheapCopy];
  return [self numerator:[numerator double]];
}

- add:b
{
  if (self == b) {
    return [self double];
  } else {
    if ([self isZero])
      return [b cheapCopy];
    if ([b isZero])
      return [self cheapCopy];
    if ([denominator isEqual:[b denominator]]) {
      return [self numerator:[numerator add:[b numerator]]];
    } else {
      id ad = mulMap(numerator, [b denominator]);
      id cb = mulMap([b numerator], denominator);

      ad = [ad addSelf:cb];
      [cb free];
      return [self numerator:ad denominator:[denominator multiply:[b denominator]]];
    }
  }
}

- subtract:b
{
  if (self == b) {
    return [self zero];
  } else {
    if ([self isZero])
      return [b negate];
    if ([b isZero])
      return [self cheapCopy];
    if ([denominator isEqual:[b denominator]]) {
      return [self numerator:[numerator subtract:[b numerator]]];
    } else {
      id ad = mulMap(numerator, [b denominator]);
      id cb = mulMap([b numerator], denominator);

      ad = [ad subtractSelf:cb];
      [cb free];
      return [self numerator:ad denominator:[denominator multiply:[b denominator]]];
    }
  }
}


- one
{
  return [self numerator:[numerator one]];
}

- (BOOL) isOne
{
  return [numerator isEqual:denominator];
}

- (BOOL) isMinusOne
{
  return [numerator isOpposite:denominator];
}

- square
{
  if ([self isZero])
    return [self zero];
  if ([self isOne] || [self isMinusOne])
    return [self one];
  return [self numerator:[numerator square] denominator:[denominator square]];
}

- inverse
{
  if ([self isZero])
    return nil;
  if ([self isOne] || [self isMinusOne])
    return [self cheapCopy];
  return [self denominator:[denominator cheapCopy] numerator:[numerator cheapCopy]];
}

- multiply:b
{
  if (self == b) {
    return [self square];
  } else {
    id n, d;

    if ([self isZero] || [b isZero])
      return [self zero];
    if ([self isOne])
      return [b cheapCopy];
    if ([b isOne])
      return [self cheapCopy];
    if ([self isMinusOne])
      return [b negate];
    if ([b isMinusOne])
      return [self negate];
    n = [numerator multiply:[b numerator]];
    d = [denominator multiply:[b denominator]];
    return [self numerator:n denominator:d];
  }
}

- divide:b
{
  if ([b isZero])
    return nil;
  if (self == b) {
    return [self one];
  } else {
    id n, d;

    if ([self isZero])
      return [self zero];
    if ([b isOne])
      return [self cheapCopy];
    if ([b isMinusOne])
      return [self negate];
    n = mulMap(numerator, [b denominator]);
    d = mulMap([b numerator], denominator);
    return [self numerator:n numerator:d];
  }
}

- power:(int)n
{
  if ([self isZero])
    return [self zero];
  if ([self isOne])
    return [self one];
  if ([self isMinusOne])
    return (ODD(n)) ? [self cheapCopy] : [self one];
  return [self numerator:[numerator power:n] denominator:[denominator power:n]];
}


- intValue:(int)intValue
{
  return [self numerator:[numerator intValue:intValue] denominator:[denominator one]];
}

- (int) intValue
{
  return [numerator intValue] / [denominator intValue];
}

- multiplyIntValue:(int)intValue
{
  switch (intValue) {
    case 0:return [self zero];
    case +1:return [self cheapCopy];
    case -1:return [self negate];
  default:{
      id n = [numerator multiplyIntValue:intValue];
      id d = [denominator cheapCopy];

      return [self numerator:n denominator:d];
    }
  }
}

- divideIntValue:(int)intValue
{
  switch (intValue) {
    case 0:return nil;
    case +1:return [self cheapCopy];
    case -1:return [self negate];
  default:{
      id n = [numerator cheapCopy];
      id d = [denominator multiplyIntValue:intValue];

      return [self numerator:n denominator:d];
    }
  }
}


- (BOOL) printsLeadingSign
{
  if ([denominator notOne]) {
    if ([numerator printsSum])
      return NO;
    return [numerator printsLeadingSign];
  } else {
    return [numerator printsLeadingSign];
  }
}

- (BOOL) printsSum
{
  if ([denominator notOne]) {
    return NO;
  } else {
    return [numerator printsSum];
  }
}

- (BOOL) printsProduct
{
  if ([denominator notOne]) {
    return NO;
  } else {
    return [numerator printsProduct];
  }
}

- printOn:(IOD)aFile
{
  if ([denominator notOne]) {
    if ([numerator printsSum]) {
      fprintf(aFile, "(");
      [numerator printOn:aFile];
      fprintf(aFile, ")");
    } else {
      [numerator printOn:aFile];
    }

    fprintf(aFile, "/");

    if ([denominator printsProduct] || [denominator printsSum]) {
      fprintf(aFile, "(");
      [denominator printOn:aFile];
      fprintf(aFile, ")");
    } else {
      [denominator printOn:aFile];
    }
  } else {
    [numerator printOn:aFile];
  }

  return self;
}

@end

