############################################################################
#
#       File:     polystuf.icn
#
#       Subject:  Procedures for manipulating polynomials
#
#       Author:   Erik Eid
#
#       Date:     May 23, 1994
#
############################################################################
#
#   This file is in the public domain.
#
############################################################################
#
#     These procedures are for creating and performing operations on single-
# variable polynomials (like ax^2 + bx + c).
#
#     poly (c1, e1, c2, e2, ...)  - creates a polynomial from the parameters
#                                   given as coefficient-exponent pairs:
#                                   c1x^e1 + c2x^e2 + ...
#     is_zero (n)                 - determines if n = 0
#     is_zero_poly (p)            - determines if a given polynomial is 0x^0
#     poly_add (p1, p2)           - returns the sum of two polynomials
#     poly_sub (p1, p2)           - returns the difference of p1 - p2
#     poly_mul (p1, p2)           - returns the product of two polynomials
#     poly_eval (p, x)            - finds the value of polynomial p when
#                                   evaluated at the given x.
#     term2string (c, e)          - converts one coefficient-exponent pair
#                                   into a string.
#     poly_string (p)             - returns the string representation of an
#                                   entire polynomial.
#
############################################################################

procedure poly (terms[])
local p, coef, expn
  if *terms % 2 = 1 then fail              # Odd number of terms means the 
                                           # list does not contain all
                                           # coefficient-exponent pairs.
  p := table()
  while *terms > 0 do {                    # A polynomial is stored as a
    coef := get(terms)                     # table in which the keys are
    expn := get(terms)                     # exponents and the elements are
                                           # coefficients.
    if numeric(coef) then if numeric(expn)
      then p[real(expn)] := coef           # If any part of pair is invalid,
                                           # discard it.  Otherwise, save
                                           # term with a real key (necessary
                                           # for consistency in sorting).
  }
  return p
end

procedure is_zero (n)
  if ((n = integer(n)) & (n = 0)) then return else fail
end

procedure is_zero_poly (p)
  if ((*p = 1) & is_zero(p[real(0)])) then return else fail
end

procedure poly_add (p1, p2)
local p3, z
  p3 := copy(p1)                           # Make a copy to start with.
  if is_zero_poly (p3) then delete (p3, real(0))
                                           # If first is zero, don't include
                                           # the 0x^0 term.
  every z := key(p2) do {                  # For every term in the second
    if member (p3, z) then p3[z] +:= p2[z] # polynomial, if one of its
      else p3[z] := p2[z]                  # exponent is in the third,
                                           # increment its coefficient.
                                           # Otherwise, create a new term.
    if is_zero(p3[z]) then delete (p3, z)       
                                           # Remove any term with coefficient
                                           # zero, since the term equals 0.
  }
  if *p3 = 0 then p3[real(0)] := 0         # Empty poly table indicates a
                                           # zero polynomial.
  return p3
end

procedure poly_sub (p1, p2)
local p3, z
  p3 := copy(p1)                           # Similar process to poly_add.
  if is_zero_poly (p3) then delete (p3, real(0))
  every z := key(p2) do {
    if member (p3, z) then p3[z] -:= p2[z]
      else p3[z] := -p2[z]
    if is_zero(p3[z]) then delete (p3, z)
  }
  if *p3 = 0 then p3[real(0)] := 0
  return p3
end

procedure poly_mul (p1, p2)
local p3, c, e, y, z
  p3 := table()
  every y := key(p1) do                    # Multiply every term in p1 by
    every z := key(p2) do {                # every term in p2 and add those
      c := p1[y] * p2[z]                   # results into p3 as in poly_add.
      e := y + z
      if member (p3, e) then p3[e] +:= c
        else p3[e] := c
      if is_zero(p3[e]) then delete (p3, e)
    }
  if *p3 = 0 then p3[real(0)] := 0
  return p3
end

procedure poly_eval (p, x)
local e, sum
  sum := 0
  every e := key(p) do                     # Increase sum by coef * x ^ exp.
    sum +:= p[e] * (x ^ e)                 # Note: this procedure does not
                                           # check in advance if x^e will
                                           # result in an error.
  return sum
end

procedure term2string (c, e)
local t
  t := ""
  if e = integer(e) then e := integer(e)   # Removes unnecessary ".0"
  if c ~= 1 then {
    if c = -1 then t ||:= "-" else t ||:= c
  }                                        # Use "-x" or "x," not "-1x" or 
                                           # "1x."
    else if e = 0 then t ||:= c            # Make sure to include a 
                                           # constant term.
  if e ~= 0 then {
    t ||:= "x"
    if e ~= 1 then t ||:= ("^" || e)       # Use "x," not "x^1."
  }
  return t
end

procedure poly_string (p)
local pstr, plist, c, e
  pstr := ""
  plist := sort(p, 3)                      # Sort table into key-value pairs.
  while *plist > 0 do {
    c := pull(plist)                       # Since sort is nondecreasing,
    e := pull(plist)                       # take terms in reverse order.
    pstr ||:= (term2string (c, e) || " + ")
  }
  pstr := pstr[1:-3]                       # Remove last " + " from end
  return pstr
end

