)if false
\documentclass{article}
\usepackage{axiom, amsthm, amsmath, amssymb}
\newtheorem{ToDo}{ToDo}[section]
\begin{document}
\title{fffg.spad}
\author{Martin Rubey}
\maketitle
\begin{abstract}
  The packages defined in this file provide fast fraction free rational
  interpolation algorithms.
\end{abstract}
\tableofcontents
\section{package FAMR2 FiniteAbelianMonoidRingFunctions2}
)endif

)abbrev package FAMR2 FiniteAbelianMonoidRingFunctions2
++ Author: Martin Rubey
++ Description:
++ This package provides a mapping function for \spadtype{FiniteAbelianMonoidRing}
FiniteAbelianMonoidRingFunctions2(E : OrderedAbelianMonoid,
                                  R1 : Ring,
                                  A1 : FiniteAbelianMonoidRing(R1, E),
                                  R2 : Ring,
                                  A2 : FiniteAbelianMonoidRing(R2, E)) _
                                  : Exports == Implementation where
  Exports == with

    map : (R1 -> R2, A1) -> A2
      ++ \spad{map}(f, a) applies the map f to each coefficient in a. It is
      ++ assumed that f maps 0 to 0

  Implementation == add

    map(f : R1 -> R2, a : A1) : A2 ==
      if zero? a then 0$A2
      else
        monomial(f leadingCoefficient a, degree a)$A2 + map(f, reductum a)

)if false

\section{package FFFG FractionFreeFastGaussian}
)endif

)abbrev package FFFG FractionFreeFastGaussian
++ Author: Martin Rubey
++ Description:
++ This package implements the interpolation algorithm proposed in Beckermann,
++ Bernhard and Labahn, George, Fraction-free computation of matrix rational
++ interpolants and matrix GCDs, SIAM Journal on Matrix Analysis and
++ Applications 22.
FractionFreeFastGaussian(D, V) : Exports == Implementation where
  D : Join(IntegralDomain, GcdDomain)
  V : AbelianMonoidRing(D, NonNegativeInteger) -- for example, SUP D

  SUP  ==> SparseUnivariatePolynomial

  cFunction ==> (NonNegativeInteger, Vector SUP D) -> D

  CoeffAction ==> (NonNegativeInteger, NonNegativeInteger, V) -> D

  Exports == with

    fffg : (List D, cFunction, Vector Integer, NonNegativeInteger) -> Matrix SUP D
      ++ \spad{fffg(C, c, vd, K)} is the general algorithm as proposed
      ++ by Beckermann and Labahn.
      ++
      ++ The first argument is the list of c_{i, i}.  These are the only values
      ++ of C explicitly needed in \spad{fffg}.
      ++
      ++ The second argument c, computes c_k(M), i.e., c_k(.) is the dual basis
      ++ of the vector space V, but also knows about the special multiplication
      ++ rule as described in Equation (2).  Note that the information about f
      ++ is therefore encoded in c.
      ++
      ++ vd is modified by the routine, on input it is the vector of degree
      ++ bounds n, as introduced in Definition 2.1.  On output it is
      ++ vector of defects (degree bound minus degree of solution).
      ++
      ++ K is requested order of solution.

    fffg  : (List D, cFunction, List NonNegativeInteger) -> Matrix SUP D
      ++ \spad{fffg(C, c, eta)} is version of fffg which uses sum
      ++ of eta as order

    interpolate : (List D, List D, NonNegativeInteger) -> Fraction SUP D
      ++ \spad{interpolate(xlist, ylist, deg)} returns the rational
      ++ function with
      ++ numerator degree at most \spad{deg} and denominator degree at most
      ++ \spad{#xlist-deg-1}  that interpolates the given points using
      ++ fraction free arithmetic. Note that rational interpolation does not
      ++ guarantee that all given points are interpolated correctly:
      ++ unattainable points may make this impossible.

)if false
\begin{ToDo}
  The following function could be moved to [[FFFGF]], parallel to
  [[generalInterpolation]].  However, the reason for moving
  [[generalInterpolation]] for fractions to a separate package was the need of
  a generic signature, hence the extra argument [[VF]] to [[FFFGF]].  In the
  special case of rational interpolation, this extra argument is not necessary,
  since we are always returning a fraction of [[SUP]]s, and ignore [[V]].  In
  fact, [[V]] is not needed for [[fffg]] itself, only if we want to specify a
  [[CoeffAction]].

  Thus, maybe it would be better to move [[fffg]] to a separate package?
\end{ToDo}
)endif

    interpolate : (List Fraction D, List Fraction D, NonNegativeInteger)
                -> Fraction SUP D
      ++ \spad{interpolate(xlist, ylist, deg)} returns the rational
      ++ function with
      ++ numerator degree \spad{deg} that interpolates the given points using
      ++ fraction free arithmetic.

    generalInterpolation : (List D, CoeffAction,
                           Vector V, List NonNegativeInteger) -> Matrix SUP D
      ++ \spad{generalInterpolation(C, CA, f, eta)} performs Hermite-Pade
      ++ approximation using the given action CA of polynomials on the elements
      ++ of f. The result is guaranteed to be correct up to order
      ++ |eta|-1. Given that eta is a "normal" point, the degrees on the
      ++ diagonal are given by eta. The degrees of column i are in this case
      ++ eta + e.i - [1, 1, ..., 1], where the degree of zero is -1.
      ++
      ++ The first argument C is the list of coefficients c_{k, k} in the
      ++ expansion <x^k> z g(x) = sum_{i=0}^k c_{k, i} <x^i> g(x).
      ++
      ++ The second argument, CA(k, l, f), should return the coefficient of x^k
      ++ in z^l f(x).

    generalInterpolation : (List D, CoeffAction,  Vector V,
               Vector Integer, NonNegativeInteger) -> Matrix SUP D
      ++ \spad{generalInterpolation(C, CA, f, vd, K)} is like
      ++ \spad{generalInterpolation(C, CA, f, eta)} but solves up to
      ++ order K and modifies vd to return defects of solutions

    generalCoefficient : (CoeffAction, Vector V,
                         NonNegativeInteger, Vector SUP D) -> D
      ++ \spad{generalCoefficient(action, f, k, p)} gives the coefficient of
      ++ x^k in p(z)\dot f(x), where the action of z^l on a polynomial in x is
      ++ given by action, i.e., action(k, l, f) should return the coefficient
      ++ of x^k in z^l f(x).

    ShiftAction : (NonNegativeInteger, NonNegativeInteger, V) -> D
      ++ \spad{ShiftAction(k, l, g)} gives the coefficient of x^k in z^l g(x),
      ++ where \spad{z*(a+b*x+c*x^2+d*x^3+...) = (b*x+2*c*x^2+3*d*x^3+...)}. In
      ++ terms of sequences, z*u(n)=n*u(n).

    ShiftC : NonNegativeInteger -> List D
      ++ \spad{ShiftC} gives the coefficients c_{k, k} in the expansion <x^k> z
      ++ g(x) = sum_{i=0}^k c_{k, i} <x^i> g(x), where z acts on g(x) by
      ++ shifting. In fact, the result is [0, 1, 2, ...]

    DiffAction : (NonNegativeInteger, NonNegativeInteger, V) -> D
      ++ \spad{DiffAction(k, l, g)} gives the coefficient of x^k in z^l g(x),
      ++ where z*(a+b*x+c*x^2+d*x^3+...) = (a*x+b*x^2+c*x^3+...), i.e.,
      ++ multiplication with x.

    DiffC : NonNegativeInteger -> List D
      ++ \spad{DiffC} gives the coefficients c_{k, k} in the expansion <x^k> z
      ++ g(x) = sum_{i=0}^k c_{k, i} <x^i> g(x), where z acts on g(x) by
      ++ shifting. In fact, the result is [0, 0, 0, ...]

    qShiftAction : (D, NonNegativeInteger, NonNegativeInteger, V) -> D
      ++ \spad{qShiftAction(q, k, l, g)} gives the coefficient of x^k in z^l
      ++ g(x), where z*(a+b*x+c*x^2+d*x^3+...) =
      ++ (a+q*b*x+q^2*c*x^2+q^3*d*x^3+...). In terms of sequences,
      ++ z*u(n)=q^n*u(n).

    qShiftC : (D, NonNegativeInteger) -> List D
      ++ \spad{qShiftC} gives the coefficients c_{k, k} in the expansion <x^k> z
      ++ g(x) = sum_{i=0}^k c_{k, i} <x^i> g(x), where z acts on g(x) by
      ++ shifting. In fact, the result is [1, q, q^2, ...]

    genVectorStream : (NonNegativeInteger, NonNegativeInteger, _
                NonNegativeInteger) -> Stream List NonNegativeInteger
      ++ \spad{genVectorStream(sumEta, maxEta, k)} generates stream
      ++ of all possible non-increasing lists \spad{eta}
      ++ with maximal entry \spad{maxEta} and sum of entries at most
      ++ \spad{sumEta}.

    genVectorStream2 : (NonNegativeInteger, NonNegativeInteger, _
                NonNegativeInteger) -> Stream List NonNegativeInteger
      ++ like genVectorStream, but skips every second vector.

  Implementation ==> add

-------------------------------------------------------------------------------
-- Shift Operator
-------------------------------------------------------------------------------

-- ShiftAction(k, l, f) is the CoeffAction appropriate for the shift operator.

    ShiftAction(k : NonNegativeInteger, l : NonNegativeInteger, f : V) : D ==
      k^l*coefficient(f, k)


    ShiftC(total : NonNegativeInteger) : List D ==
      [i::D for i in 0..total-1]

-------------------------------------------------------------------------------
-- q-Shift Operator
-------------------------------------------------------------------------------

-- q-ShiftAction(k, l, f) is the CoeffAction appropriate for the q-shift operator.

    qShiftAction(q : D, k : NonNegativeInteger, l : NonNegativeInteger, f : V) : D ==
      q^(k*l)*coefficient(f, k)


    qShiftC(q : D, total : NonNegativeInteger) : List D ==
      [q^i for i in 0..total-1]

-------------------------------------------------------------------------------
-- Differentiation Operator
-------------------------------------------------------------------------------

-- DiffAction(k, l, f) is the CoeffAction appropriate for the differentiation
-- operator.

    DiffAction(k : NonNegativeInteger, l : NonNegativeInteger, f : V) : D ==
      if k < l then 0 else coefficient(f, (k-l)::NonNegativeInteger)


    DiffC(total : NonNegativeInteger) : List D ==
      [0 for i in 1..total]

-------------------------------------------------------------------------------
-- general - suitable for functions f
-------------------------------------------------------------------------------

-- get the coefficient of z^k in the scalar product of p and f, the action
-- being defined by coeffAction

    generalCoefficient(coeffAction : CoeffAction, f : Vector V,
                       k : NonNegativeInteger, p : Vector SUP D) : D ==
      res : D := 0
      for i in 1..#f repeat

-- Defining a and b and summing only over those coefficients that might be
-- nonzero makes a huge difference in speed
        a := f.i
        b := p.i
        for l in minimumDegree b..degree b repeat
            if not zero? coefficient(b, l)
            then res := res + coefficient(b, l) * coeffAction(k, l, a)
      res


    generalInterpolation(C : List D, coeffAction : CoeffAction,
                         f : Vector V,
                         eta : List NonNegativeInteger) : Matrix SUP D ==

      c : cFunction := (x, y) +-> generalCoefficient(coeffAction, f,
                                         (x - 1)::NonNegativeInteger, y)
      fffg(C, c, eta)

    generalInterpolation(C : List D, coeffAction : CoeffAction,
                         f : Vector V, vd : Vector Integer,
                         K : NonNegativeInteger) : Matrix SUP D ==

      c : cFunction := (x, y) +-> generalCoefficient(coeffAction, f,
                                         (x - 1)::NonNegativeInteger, y)
      fffg(C, c, vd, K)



-------------------------------------------------------------------------------
-- general - suitable for functions f - trying all possible degree combinations
-------------------------------------------------------------------------------


    -- The following function returns the lexicographically next vector with
    -- non-negative components smaller than [[p]] with the same sum as [[v]].

    nextVector!(p : NonNegativeInteger, v : List NonNegativeInteger)
                : Union("failed", List NonNegativeInteger) ==
      n := #v
      pos := position(x +-> x < p, v)
      zero? pos => return "failed"
      if pos = 1 then
        sum : Integer := v.1
        for i in 2..n repeat
          if v.i < p and sum > 0 then
            v.i := v.i + 1
            sum := sum - 1
            for j in 1..i-1 repeat
              if sum > p then
                v.j := p
                sum := sum - p
              else
                v.j := sum::NonNegativeInteger
                sum := 0
            return v
          else sum := sum + v.i
        return "failed"
      else
        v.pos     := v.pos + 1
        v.(pos-1) := (v.(pos-1) - 1)::NonNegativeInteger

      v

)if false

The following function returns the stream of all possible degree vectors,
beginning with [[v]], where the degree vectors are sorted in reverse
lexicographic order. Furthermore, the entries are all less or equal to [[p]]
and their sum equals the sum of the entries of [[v]]. We assume that the
entries of [[v]] are also all less or equal to [[p]].

)endif

    vectorStream(p : NonNegativeInteger, v : List NonNegativeInteger)
                 : Stream List NonNegativeInteger == delay
      next := nextVector!(p, copy v)
      (next case "failed") => empty()$Stream(List NonNegativeInteger)
      cons(next, vectorStream(p, next))

    -- [[vectorStream2]] skips every second entry of [[vectorStream]].

    vectorStream2(p : NonNegativeInteger, v : List NonNegativeInteger)
                  : Stream List NonNegativeInteger == delay
      next := nextVector!(p, copy v)
      (next case "failed") => empty()$Stream(List NonNegativeInteger)
      next2 := nextVector!(p, copy next)
      (next2 case "failed") => cons(next, empty())
      cons(next2, vectorStream2(p, next2))

)if false

If [[f]] consists of two elements only, we can skip every second degree vector:
note that [[fffg]], and thus also [[generalInterpolation]], returns a matrix
with [[#f]] columns, each corresponding to a solution of the interpolation
problem. More precisely, the $i$\textsuperscript{th} column is a solution with
degrees [[eta]]$-(1, 1, \dots, 1)+e_i$. Thus, in the case of $2\times 2$ matrices,
[[vectorStream]] would produce solutions corresponding to $(d, 0), (d-1, 1);
(d-1, 1), (d-2, 2); (d-2, 2), (d-3, 3)\dots$, i.e., every second matrix is
redundant.

Although some redundancy exists also for higher dimensional [[f]], the scheme
becomes much more complicated, thus we did not implement it.


We need to generate an initial degree vector, being the minimal element in
reverse lexicographic order, i.e., $m, m, \dots, m, k, 0, 0, \dots$, where $m$
is [[maxEta]] and $k$ is the remainder of [[sumEta]] divided by
[[maxEta]]. This is done by the following code:

)endif

    initialVector(sum : NonNegativeInteger, maxEta : NonNegativeInteger,
                 n : NonNegativeInteger)_
                  : List NonNegativeInteger ==
        entry : Integer
        [(if sum < maxEta _
            then (entry := sum; sum := 0) _
            else (entry := maxEta; sum := (sum - maxEta)::NonNegativeInteger); _
            entry::NonNegativeInteger) for i in 1..n]

    genVectorStream(sum : NonNegativeInteger, max : NonNegativeInteger,
                 k : NonNegativeInteger) : Stream List NonNegativeInteger ==
        eta := initialVector(sum, max, k)
        cons(eta, vectorStream(max, eta))

    genVectorStream2(sum : NonNegativeInteger, max : NonNegativeInteger,
                 k : NonNegativeInteger) : Stream List NonNegativeInteger ==
        eta := initialVector(sum, max, k)
        cons(eta, vectorStream2(max, eta))

-------------------------------------------------------------------------------
-- rational interpolation
-------------------------------------------------------------------------------

    interpolate(x : List Fraction D, y : List Fraction D, d : NonNegativeInteger)
                : Fraction SUP D ==
      gx := splitDenominator(x)$InnerCommonDenominator(D, Fraction D, _
                                                       List D, _
                                                       List Fraction D)
      gy := splitDenominator(y)$InnerCommonDenominator(D, Fraction D, _
                                                       List D, _
                                                       List Fraction D)
      r := interpolate(gx.num, gy.num, d)
      elt(numer r, monomial(gx.den, 1))/(gy.den*elt(denom r, monomial(gx.den, 1)))


    interpolate(x : List D, y : List D, d : NonNegativeInteger) : Fraction SUP D ==
-- compute interpolants of grade d and N-d-1
      if (N := #x) ~= #y then
        error "interpolate: number of points and values must match"
      if N <= d then
        error "interpolate: numerator degree must be smaller than number of data points"
      c : cFunction := (s, u) +-> y.s * elt(u.2, x.s) - elt(u.1, x.s)
      eta : List NonNegativeInteger := [d, (N-d)::NonNegativeInteger]
      M := fffg(x, c, eta)

      if zero?(M.(2, 1)) then M.(1, 2)/M.(2, 2)
                        else M.(1, 1)/M.(2, 1)

)if false

Because of Lemma~5.3, [[M.1.(2, 1)]] and [[M.1.(2, 2)]] cannot both vanish,
since [[eta_sigma]] is always $\sigma$-normal by Theorem~7.2 and therefore also
para-normal, see Definition~4.2.

Because of Lemma~5.1 we have that [[M.1.(*, 2)]] is a solution of the
interpolation problem, if [[M.1.(2, 1)]] vanishes.

-------------------------------------------------------------------------------
-- fffg
-------------------------------------------------------------------------------

[[recurrence]] computes the new matrix $M$, according to the following formulas
(cf. Table~2 in Beckermann and Labahn):
\begin{align*}
  &\text{Increase order}\\
  &\quad\quad\text{for $\ell=1\dots m$, $\ell\neq\pi$}\\
  &\quad\quad\quad\quad\mathbf M_{\sigma+1}^{(., \ell)} :=
        \left(\mathbf M_{\sigma}^{(., \ell)}r^{(\pi)}
            - \mathbf M_{\sigma}^{(., \pi)}r^{(\ell)}\right)/d_\sigma\\
  &\text{Increase order in column $\pi$}\\
  &\quad\quad\mathbf M_{\sigma+1}^{(., \pi)} :=
        \left(z-c_{\sigma, \sigma}\right)\mathbf M_{\sigma}^{(., \pi)}\\
  &\text{Adjust degree constraints : }\\
  &\quad\quad\mathbf M_{\sigma+1}^{(., \pi)} :=
        \left(\mathbf M_{\sigma+1}^{(., \pi)}r^{(\pi)}
            - \sum_{\ell\neq\pi}\mathbf M_{\sigma+1}^{(., \ell)}p^{(\ell)}
        \right)/d_\sigma
\end{align*}

Since we do not need the matrix $\mathbf M_{\sigma}$ anymore, we drop the index
and update the matrix destructively.  In the following, we write [[Ck]] for
$c_{\sigma, \sigma}$.

)endif

-- a major part of the time is spent here
    recurrence(M : Matrix SUP D, pi : NonNegativeInteger,
               m : NonNegativeInteger, r : Vector D, d : D, z : SUP D,
               Ck : D, p : Vector D, vd : Vector Integer) : Matrix SUP D ==

        rPi : D := qelt(r, pi)
        polyf : SUP D := rPi * (z - Ck::SUP D)

        for i in 1..m repeat
            MiPi : SUP D    := qelt(M, i, pi)
            newMiPi : SUP D := polyf * MiPi

-- update columns ~= pi and calculate their sum
            for l in 1..m | l ~= pi and vd(l) >= 0 repeat
                rl : D  := qelt(r, l)
-- I need the coercion to SUP D, since exquo returns an element of
-- Union("failed", SUP D)...
                Mil : SUP D := ((qelt(M, i, l) * rPi - MiPi * rl) exquo d)::SUP D
                qsetelt!(M, i, l, Mil)

                pl : D  := qelt(p, l)
                newMiPi := newMiPi - pl * Mil

-- update column pi
            qsetelt!(M, i, pi, (newMiPi exquo d)::SUP D)

        M


    fffg(C : List D, c : cFunction, vd : Vector Integer,
         K : NonNegativeInteger) : Matrix SUP D ==
        z : SUP D := monomial(1, 1)
        m : NonNegativeInteger := #vd
        M : Matrix SUP D := scalarMatrix(m, 1)
        d : D := 1
        etak : Vector NonNegativeInteger := zero(m)
        r : Vector D := zero(m)
        p : Vector D := zero(m)
        lambdaMax : Integer
        lambda : NonNegativeInteger

        for k in 1..K repeat
-- k = sigma+1

            lambda := 0
            lambdaMax := -1
            for l in 1..m repeat
                (vdl := vd(l)) < 0 => iterate
                r(l) := c(k, column(M, l))
                r(l) = 0 => iterate
                if vdl > lambdaMax then
                    lambdaMax := vdl
                    lambda := l

-- if Lambda is empty, then M, d and etak remain unchanged. Otherwise, we look
-- for the next closest para-normal point.

            lambda = 0 => iterate

-- Calculate leading coefficients

            for l in 1..m | l ~= lambda repeat
                if etak(l) > 0 and vd(l) >= 0 then
                    p(l) := coefficient(M(l, lambda),
                                       (etak(l) - 1)::NonNegativeInteger)
                else
                    p(l) := 0

-- increase order and adjust degree constraints

            M := recurrence(M, lambda, m, r, d, z, C.k, p, vd)

            d := r.lambda
            etak(lambda) := etak(lambda) + 1
            vd(lambda) := vd(lambda) - 1

        M

    fffg(C : List D, c : cFunction, eta : List NonNegativeInteger
        ) : Matrix SUP D ==
       vd : Vector Integer := vector([ei::Integer for ei in eta])
       fffg(C, c, vd, reduce(_+, eta))

)if false
\section{package FFFGF FractionFreeFastGaussianFractions}
)endif

)abbrev package FFFGF FractionFreeFastGaussianFractions
++ Author: Martin Rubey
++ Description:
++ This package lifts the interpolation functions from
++ \spadtype{FractionFreeFastGaussian} to fractions.
FractionFreeFastGaussianFractions(D, V, VF) : Exports == Implementation where
  D : Join(IntegralDomain, GcdDomain)
  V : FiniteAbelianMonoidRing(D, NonNegativeInteger)
  VF : FiniteAbelianMonoidRing(Fraction D, NonNegativeInteger)

  F ==> Fraction D

  SUP  ==> SparseUnivariatePolynomial

  FFFG ==> FractionFreeFastGaussian

  FAMR2 ==> FiniteAbelianMonoidRingFunctions2

  cFunction ==> (NonNegativeInteger, Vector SUP D) -> D

  CoeffAction ==> (NonNegativeInteger, NonNegativeInteger, V) -> D
-- coeffAction(k, l, f) is the coefficient of x^k in z^l f(x)

  Exports == with

    generalInterpolation : (List D, CoeffAction, Vector VF, List NonNegativeInteger)
                          -> Matrix SUP D
      ++ \spad{generalInterpolation(l, CA, f, eta)} performs Hermite-Pade
      ++ approximation using the given action CA of polynomials on the elements
      ++ of f. The result is guaranteed to be correct up to order
      ++ |eta|-1. Given that eta is a "normal" point, the degrees on the
      ++ diagonal are given by eta. The degrees of column i are in this case
      ++ eta + e.i - [1, 1, ..., 1], where the degree of zero is -1.

  Implementation == add

    multiplyRows!(v : Vector D, M : Matrix SUP D) : Matrix SUP D ==
      n := #v
      for i in 1..n repeat
        for j in 1..n repeat
          M.(i, j) := v.i*M.(i, j)

      M

    generalInterpolation(C : List D, coeffAction : CoeffAction,
                         f : Vector VF, eta : List NonNegativeInteger) : Matrix SUP D ==
      n := #f
      g : Vector V   := new(n, 0)
      den : Vector D := new(n, 0)

      for i in 1..n repeat
        c := coefficients(f.i)
        den.i := commonDenominator(c)$CommonDenominator(D, F, List F)
        g.i := map(x +-> retract(x*den.i)@D, f.i)
                  $FAMR2(NonNegativeInteger, Fraction D, VF, D, V)

      M := generalInterpolation(C, coeffAction, g, eta)$FFFG(D, V)

-- The following is necessary since I'm multiplying each row with a factor, not
-- each column. Possibly I could factor out gcd den, but I'm not sure whether
-- this is efficient.

      multiplyRows!(den, M)

)if false
\section{package NEWTON NewtonInterpolation}
)endif

)abbrev package NEWTON NewtonInterpolation
++ Description:
++ This package exports Newton interpolation for the special case where the
++ result is known to be in the original integral domain
NewtonInterpolation F : Exports == Implementation where
    F : IntegralDomain
    Exports == with

      newton : List F -> SparseUnivariatePolynomial F

      ++ \spad{newton}(l) returns the interpolating polynomial for the values
      ++ l, where the x-coordinates are assumed to be [1, 2, 3, ..., n] and the
      ++ coefficients of the interpolating polynomial are known to be in the
      ++ domain F. I.e., it is a very streamlined version for a special case of
      ++ interpolation.

    Implementation == add

      differences(yl : List F) : List F ==
        [y2-y1 for y1 in yl for y2 in rest yl]

      z : SparseUnivariatePolynomial(F) := monomial(1, 1)

-- we assume x=[1, 2, 3, ..., n]
      newtonAux(k : F, fact : F, yl : List F) : SparseUnivariatePolynomial(F) ==
        if empty? rest yl
        then ((yl.1) exquo fact)::F::SparseUnivariatePolynomial(F)
        else ((yl.1) exquo fact)::F::SparseUnivariatePolynomial(F)
             + (z-k::SparseUnivariatePolynomial(F)) _
               * newtonAux(k+1$F, fact*k, differences yl)


      newton yl == newtonAux(1$F, 1$F, yl)


--Copyright (c) 2006-2007, Martin Rubey <Martin.Rubey@univie.ac.at>
--
--Redistribution and use in source and binary forms, with or without
--modification, are permitted provided that the following conditions are
--met:
--
--    - Redistributions of source code must retain the above copyright
--      notice, this list of conditions and the following disclaimer.
--
--    - Redistributions in binary form must reproduce the above copyright
--      notice, this list of conditions and the following disclaimer in
--      the documentation and/or other materials provided with the
--      distribution.
--
--THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
--IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
--TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
--PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
--OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
--EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
--PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
--PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
--LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
--NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
--SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
