\documentclass{article}
\usepackage{axiom}

\title{\File{src/interp/i-coerfn.boot} Pamphlet}
\author{The Axiom Team}

\begin{document}
\maketitle
\begin{abstract}
\end{abstract}
\eject
\tableofcontents
\eject

\begin{verbatim}
Special coercion routines

This is the newly revised set of coercion functions to work with
the new library and the new runtime system.

coerceByTable is driven off $CoerceTable which is used to match
the top-level constructors of the source and object types.  The
form of $CoerceTable is an alist where the "properties" are the
source top-level constructors and the values are triples
                target-domain coercion-type function
where target-domain is the top-level constructor of the target,
coercion-type is one of 'total, 'partial or 'indeterm, and
function is the name of the function to call to handle the
coercion. coercion-type is used by canCoerce and friends: 'total
means that a coercion can definitely be performed, 'partial means
that one cannot tell whether a coercion can be performed unless
you have the actual data (like telling whether a Polynomial Integer
can be coerced to an Integer: you have to know whether it is a
constant polynomial), and 'indeterm means that you might be able
to tell without data, but you need to call the function with the
argument "$fromCoerceable$" for a response of true or false. As an
example of this last kind, you may be able to coerce a list to a
vector but you have to know what the underlying types are. So
List Integer is coerceable to Vector Integer but List Float is
not necessarily coerceable to Vector Integer.

The functions always take three arguments:
     value                this is the unwrapped source object
     source-type          this is the type of the source
     target-type          this is the requested type of the target
For ethical reasons and to avoid eternal damnation, we try to use
library functions to perform a lot of the structure manipulations.
However, we sometimes cheat for efficiency reasons, particularly to
avoid intermediate instantiations.

the following are older comments:

This file  contains the special  coercion routines that  convert from
one  datatype to  another  in the  interpreter.   The  choice of  the
primary special routine is made by  the function coerceByTable.  Note
that not all coercions use these functions, as some are done via SPAD
algebra code  and controlled by  the function coerceByFunction.   See
the file COERCE BOOT for more information.

some assumption about the call of commute and embed functions:
embed functions are called for one level embedding only,
  e.g. I to P I, but not I to P G I
commute functions are called for two types which differ only in the
  permutation of the two top type constructors
  e.g. G P RN to P G RN, but not G P I to P G RN or
    P[x] G RN to G P RN

all functions in this file should call canCoerce and coerceInt, as
  opposed to canCoerceFrom and coerceInteractive

all these coercion functions have the following result:
1. if u=$fromCoerceable$, then TRUE or NIL
2. if the coercion succeeds, the coerced value (this may be NIL)
3. if the coercion fails, they throw to a catch point in
     coerceByTable

\end{verbatim}
\section{License}
<<license>>=
-- Copyright (c) 1991-2002, The Numerical ALgorithms Group Ltd.
-- All rights reserved.
--
-- 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.
--
--     - Neither the name of The Numerical ALgorithms Group Ltd. nor the
--       names of its contributors may be used to endorse or promote products
--       derived from this software without specific prior written permission.
--
-- 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.

@
<<*>>=
<<license>>

SETANDFILEQ($coerceFailure,GENSYM())

position1(x,y) ==
  -- this is used where we want to assume a 1-based index
  1 + position(x,y)

--% Direct Product, New and Old

DP2DP(u,source is [.,n,S],target is [.,m,T]) ==
  n ^= m => nil
  u = '_$fromCoerceable_$ => canCoerce(S,T)
  null (u' := coerceInt(objNewWrap(u,['Vector,S]),['Vector,T])) =>
    coercionFailure()
  objValUnwrap u'

--% Distributed Multivariate Polynomials, New and Old

Dmp2Dmp(u,source is [dmp,v1,S], target is [.,v2,T]) ==
  -- the variable lists must share some variables, or u is a constant
  u = '_$fromCoerceable_$ =>
    v:= intersection(v1,v2)
    v and
      w2:= SETDIFFERENCE(v2,v)
      t1:= if w1 then [dmp,w1,S] else S
      t2:= if w2 then [dmp,w2,T] else T
      canCoerce(t1,t2)
  null u => domainZero(target)
  u is [[e,:c]] and e=LIST2VEC [0 for v in v1] =>
    z:= coerceInt(objNewWrap(c,S),target) => objValUnwrap(z)
    coercionFailure()
  v:= intersection(v1,v2) =>
    w1:= SETDIFFERENCE(v1,v) =>
      coerceDmp1(u,source,target,v,w1)
    coerceDmp2(u,source,target)
  coercionFailure()

coerceDmp1(u,source is [.,v1,S],target is [.,v2,T],v,w) ==
  -- coerces one Dmp to another, where v1 is not a subset of v2
  -- v is the intersection, w the complement of v1 and v2
  t:= ['DistributedMultivariatePolynomial,w,S]
  x:= domainZero(target)
  one:= domainOne(T)
  plusfunc:= getFunctionFromDomain('_+,target,[target,target])
  multfunc:= getFunctionFromDomain('_*,target,[target,target])
  pat1:= [member(x,v) for x in v1]
  pat2:= [member(x,w) for x in v1]
  pat3:= [member(x,v) and POSN1(x,v) for x in v2]
  for [e,:c] in u until not z repeat
    exp:= LIST2VEC [y for x in pat2 for y in VEC2LIST e | x]
    z:= coerceInt(objNewWrap([CONS(exp,c)],t),target) =>
      li:= [y for x in pat1 for y in VEC2LIST e | x]
      a:= [CONS(LIST2VEC [if x then li.x else 0 for x in pat3],one)]
      x:= SPADCALL(x,SPADCALL(objValUnwrap(z),a,multfunc),plusfunc)
  z => x
  coercionFailure()

coerceDmp2(u,source is [.,v1,S],target is [.,v2,T]) ==
  -- coerces one Dmp to another, where v1 is included in v2
  x:= domainZero(target)
  one:= domainOne(T)
  plusfunc:= getFunctionFromDomain('_+,target,[target,target])
  multfunc:= getFunctionFromDomain('_*,target,[target,target])
  pat:= [member(x,v1) and POSN1(x,v1) for x in v2]
  for [e,:c] in u until not z repeat
    z:= coerceInt(objNewWrap(c,S),target) =>
      li:= VEC2LIST e
      a:= [CONS(LIST2VEC [if x then li.x else 0 for x in pat],one)]
      x:= SPADCALL(x,SPADCALL(objValUnwrap(z),a,multfunc),plusfunc)
    NIL
  z => x
  coercionFailure()

Dmp2Expr(u,source is [dmp,vars,S], target is [Expr,T]) ==
    u = '_$fromCoerceable_$ => canCoerce(S, target)

    null vars =>
        [[., :c]] := u
        not (c := coerceInt(objNewWrap(c, S), target)) => coercionFailure()
        objValUnwrap(c)

    syms := [objValUnwrap coerceInt(objNewWrap(var, $Symbol), target) for
                var in vars]
    sum := domainZero(target)

    plus := getFunctionFromDomain("+",  target, [target, target])
    mult := getFunctionFromDomain("*",  target, [target, target])
    expn := getFunctionFromDomain("**", target, [target, $Integer])

    for [e, :c] in u repeat
        not (c := coerceInt(objNewWrap(c, S), target)) => coercionFailure()
        c := objValUnwrap(c)
        term := domainOne(target)
        for i in 0.. for sym in syms repeat
            exp := e.i
            e.i > 0 => term := SPADCALL(term, SPADCALL(sym, e.i, expn), mult)
        sum := SPADCALL(sum, SPADCALL(c, term, mult), plus)

    sum

Dmp2Mp(u, source is [dmp, x, S], target is [mp, y, T]) ==
  source' := [dmp,y,T]
  u = '_$fromCoerceable_$ =>
    x = y => canCoerce(S,T)
    canCoerce(source',target)
  null u => domainZero(target)  -- 0 dmp is = nil
  x ^= y =>
    (u' := coerceInt(objNewWrap(u,source),source')) or coercionFailure()
    (u' := coerceInt(u',target)) or coercionFailure()
    objValUnwrap(u')

  -- slight optimization for case #u = 1, x=y , #x =1 and S=T
  -- I know it's pathological, but it may avoid an instantiation
  (x=y) and (1 = #u) and (1 = #x) and (S = T) =>
    [1,1,[(CAAR u).0,0,:CDAR u]]

  (u' := coerceDmpCoeffs(u,S,T)) = 'failed =>
    coercionFailure()
  plusfunc := getFunctionFromDomain("+",target,[target,target])
  u'' := genMpFromDmpTerm(u'.0, 0)
  for i in 1..(#u' - 1) repeat
    u'' := SPADCALL(u'',genMpFromDmpTerm(u'.i, 0),plusfunc)
  u''

coerceDmpCoeffs(u,S,T) ==
  -- u is a dmp, S is domain of coeffs, T is domain to coerce coeffs to
  S = T => u
  u' := nil
  bad := nil
  for [e,:c] in u repeat
    bad => nil
    null (c' := coerceInt(objNewWrap(c,S),T)) => return (bad := true)
    u' := [[e,:objValUnwrap(c')],:u']
  bad => 'failed
  nreverse u'

sortAndReorderDmpExponents(u,vl) ==
  vl' := reverse MSORT vl
  n := (-1) + #vl
  pos := LIST2VEC LZeros (n+1)
  for i in 0..n repeat pos.i := position(vl.i,vl')
  u' := nil
  for [e,:c] in u repeat
    e' := LIST2VEC LZeros (n+1)
    for i in 0..n repeat e'.(pos.i) := e.i
    u' := [[e',:c],:u']
  reverse u'

domain2NDmp(u, source, target is [., y, T]) ==
  target' := ['DistributedMultivariatePolynomial,y,T]
  u = '_$fromCoerceable_$ => canCoerce(source,target')
  (u' := coerceInt(objNewWrap(u,source),target')) =>
    (u'' := coerceInt(u',target)) =>
      objValUnwrap(u'')
    coercionFailure()
  coercionFailure()

Dmp2NDmp(u,source is [dmp,x,S],target is [ndmp,y,T]) ==
  -- a null DMP = 0
  null u => domainZero(target)
  target' := [dmp,y,T]
  u = '_$fromCoerceable_$ => Dmp2Dmp(u,source,target')
  (u' := Dmp2Dmp(u,source,target')) => addDmpLikeTermsAsTarget(u',target)
  coercionFailure()

addDmpLikeTermsAsTarget(u,target) ==
  u' := domainZero(target)
  func := getFunctionFromDomain("+",target,[target,target])
  for t in u repeat u' := SPADCALL(u',[t],func)
  u'

-- rewrite ?
Dmp2P(u, source is [dmp,vl, S], target is [.,T]) ==
  -- a null DMP = 0
  null u => domainZero(target)
  u = '_$fromCoerceable_$ =>
    t := canCoerce(S,T)
    null t => canCoerce(S,target)
    t

  S is ['Polynomial,.] =>
    mp := coerceInt(objNewWrap(u,source),['MultivariatePolynomial,vl,S])
      or coercionFailure()
    p := coerceInt(mp,target) or coercionFailure()
    objValUnwrap p

  -- slight optimization for case #u = 1, #vl =1 and S=T
  -- I know it's pathological, but it may avoid an instantiation
  (1 = #u) and (1 = #vl) and (S = T) =>
    (lexp:= (CAAR u).0) = 0 => [1,:CDAR u]
    [1,vl.0,[lexp,0,:CDAR u]]

  vl' := reverse MSORT vl
  source' := [dmp,vl',S]
  target' := ['MultivariatePolynomial,vl',S]
  u' := sortAndReorderDmpExponents(u,vl)
  u' := coerceInt(objNewWrap(u',source'),target')
  if u' then
    u' := translateMpVars2PVars (objValUnwrap(u'),vl')
    u' := coerceInt(objNewWrap(u',['Polynomial,S]),target)
  u' => objValUnwrap(u')
  -- get drastic. create monomials
  source' := [dmp,vl,T]
  u' := domainZero(target)
  oneT := domainOne(T)
  plusfunc := getFunctionFromDomain("+",target,[target,target])
  multfunc := getFunctionFromDomain("*",target,[target,target])
  for [e,:c] in u repeat
    (c' := coerceInt(objNewWrap(c,S),target)) or coercionFailure()
    (e' := coerceInt(objNewWrap([[e,:oneT]],source'),target)) or
      coercionFailure()
    t := SPADCALL(objValUnwrap(e'),objValUnwrap(c'),multfunc)
    u' := SPADCALL(u',t,plusfunc)
  coercionFailure()

translateMpVars2PVars (u, vl) ==
  u is [ =1, v, :termlist] =>
    [ 1, vl.(v-1),
      :[[e,:translateMpVars2PVars(c,vl)] for [e,:c] in termlist]]
  u

Dmp2Up(u, source is [dmp,vl,S],target is [up,var,T]) ==
  null u =>    -- this is true if u = 0
    domainZero(target)

  u = '_$fromCoerceable_$ =>
    member(var,vl) =>
      vl' := remove(vl,var)
      null vl' =>         -- no remaining variables
        canCoerce(S,T)
      null rest vl' =>    -- one remaining variable
        canCoerce([up,first vl',S],T)
      canCoerce([dmp,vl',S], T)
    canCoerce(source,T)

  -- check constant case
  (null rest u) and (first(u) is [e,:c]) and
    ( and/[(0 = e.i) for i in 0..(-1 + #vl)] ) =>
      (x := coerceInt(objNewWrap(c,S),target)) or coercionFailure()
      objValUnwrap(x)

  -- check non-member case
  null member(var,vl) =>
    (u' := coerceInt(objNewWrap(u,source),T)) or coercionFailure()
    [[0,:objValUnwrap u']]

  vl' := remove(vl,var)

  -- only one variable in DMP case
  null vl' =>
    u' := nreverse SORTBY('CAR,[[e.0,:c] for [e,:c] in u])
    (u' := coerceInt(objNewWrap(u',[up,var,S]),target)) or
      coercionFailure()
    objValUnwrap u'

  S1 := [dmp,vl',S]
  plusfunc:= getFunctionFromDomain('_+,T,[T,T])
  zero := getConstantFromDomain('(Zero),T)
  x := NIL
  pos:= POSN1(var,vl)
  for [e,:c] in u until not y repeat
    exp:= e.pos
    e1:= removeVectorElt(e,pos)
    y:= coerceInt(objNewWrap([[e1,:c]],S1),T) =>
      -- need to be careful about zeros
      p:= ASSQ(exp,x) =>
        c' := SPADCALL(CDR p,objValUnwrap(y),plusfunc)
        c' = zero => x := REMALIST(x,exp)
        RPLACD(p,c')
      zero = objValUnwrap(y) => 'iterate
      x := CONS(CONS(exp,objValUnwrap(y)),x)
  y => nreverse SORTBY('CAR,x)
  coercionFailure()

removeVectorElt(v,pos) ==
  -- removes the pos'th element from vector v
  LIST2VEC [x for x in VEC2LIST v for y in 0.. | not (y=pos)]

removeListElt(l,pos) ==
  pos = 0 => CDR l
  [CAR l, :removeListElt(CDR l,pos-1)]

NDmp2domain(u,source is [ndmp,x,S],target) ==
  -- a null NDMP = 0
  null u => domainZero(target)
  dmp := 'DistributedMultivariatePolynomial
  source' := [dmp,x,S]
  u = '_$fromCoerceable_$ => canCoerce(source',target)
  u' := addDmpLikeTermsAsTarget(u,source')
  (u'' := coerceInt(objNewWrap(u',source'),target)) =>
    objValUnwrap(u'')
  coercionFailure()

NDmp2NDmp(u,source is [ndmp,x,S],target is [.,y,T]) ==
  -- a null NDMP = 0
  null u => domainZero(target)
  dmp := 'DistributedMultivariatePolynomial
  source' := [dmp,x,S]
  target' := [dmp,y,T]
  u = '_$fromCoerceable_$ => canCoerce(source',target')
  u' := addDmpLikeTermsAsTarget(u,source')
  (u'' := coerceInt(objNewWrap(u',source'),target')) =>
    addDmpLikeTermsAsTarget(objValUnwrap(u''),target)
  coercionFailure()

--% Expression

Expr2Complex(u,source is [.,S], target is [.,T]) ==
    u = '_$fromCoerceable_$ => nil   -- can't tell, in general

    not member(S, [$Integer, $Float, $DoubleFloat]) => coercionFailure()
    not member(T, [$Float, $DoubleFloat]) => coercionFailure()

    complexNumeric := getFunctionFromDomain("complexNumeric", ['Numeric, S], [source])

    -- the following might fail
    cf := SPADCALL(u,complexNumeric)  -- returns a Float
    T = $DoubleFloat =>
        null (z := coerceInt(objNewWrap(cf, ['Complex, $Float]), ['Complex, $DoubleFloat])) =>
            coercionFailure()
        objValUnwrap z
    cf

Expr2Dmp(u,source is [Expr,S], target is [dmp,v2,T]) ==
    u = '_$fromCoerceable_$ => canCoerce(source, T)

    null v2 =>
        not (z := coerceInt(objNewWrap(u, source), T)) => coercionFailure()
        [[LIST2VEC NIL, :objValUnwrap z]]

    obj := objNewWrap(u, source)
    univ := coerceInt(obj, ['UnivariatePolynomial, first v2, T])
    not univ =>
        T = source => coercionFailure()
        not (z := coerceInt(obj, [dmp, v2, source])) =>
            coercionFailure()
        z := objValUnwrap z
        for term in z repeat
            [., :c] := term
            not (c := coerceInt(objNewWrap(c, source), T)) => coercionFailure()
            RPLACD(term, objValUnwrap c)
        z

    univ := objValUnwrap univ

    -- only one variable

    null rest v2 =>
        for term in univ repeat
            RPLACA(term, VECTOR CAR term)
        univ

    -- more than one variable

    summands := nil
    for [e,:c] in univ repeat
        summands := Expr2Dmp1(summands,
            LIST2VEC [e, :[0 for v in rest v2]], c, T, 1, rest v2, T)

    plus := getFunctionFromDomain("+", target, [target, target])
    sum  := domainZero target
    for summand in summands repeat
        sum := SPADCALL([summand], sum, plus)
    sum

Expr2Dmp1(summands, vec, c, source, index, varList, T) ==
    if null varList then
        if not (source = T) then
            not (c := coerceInt(objNewWrap(c, source), T)) => coercionFailure()
            c := objValUnwrap c
        summands := [[vec, :c], :summands]
    else
        univ := coerceInt(objNewWrap(c, source),
            ['UnivariatePolynomial, first varList, T])
        univ := objValUnwrap univ

        for [e,:c] in univ repeat
            vec := COPY_-SEQ vec
            vec.index := e
            summands := Expr2Dmp1(summands, vec, c, T, index+1, rest varList, T)
    summands

Expr2Mp(u,source is [Expr,S], target is [.,v2,T]) ==
    u = '_$fromCoerceable_$ => canCoerce(source, T)

    dmp := ['DistributedMultivariatePolynomial,v2,T]
    d   := Expr2Dmp(u,source, dmp)
    not (m := coerceInt(objNewWrap(d, dmp), target)) => coercionFailure()
    objValUnwrap m

Expr2Up(u,source is [Expr,S], target is [.,var,T]) ==
    u = '_$fromCoerceable_$ => canCoerce(source, T)
    kernelFunc := getFunctionFromDomain("kernels", source, [source])
    kernelDom  := ['Kernel, source]
    nameFunc   := getFunctionFromDomain("name", kernelDom, [kernelDom])
    kernels    := SPADCALL(u,kernelFunc)
    v1         := [SPADCALL(kernel, nameFunc) for kernel in kernels]

    not member(var, v1) => coercionFailure()

    -- variable is a kernel

    varKernel  := kernels.(POSN1(var, v1))
    univFunc   := getFunctionFromDomain("univariate", source, [source, kernelDom])
    sup        := ['SparseUnivariatePolynomial, source]

    fracUniv   := SPADCALL(u, varKernel, univFunc)
    denom      := CDR fracUniv

    not equalOne(denom, sup) => coercionFailure()

    numer      := CAR fracUniv
    uniType := ['UnivariatePolynomial, var, source]
    (z := coerceInt(objNewWrap(numer, uniType), target)) => objValUnwrap z
    coercionFailure()

--% Kernels over Expr

Ker2Ker(u,source is [.,S], target is [.,T]) ==
  u = '_$fromCoerceable_$ => canCoerce(S, T)
  not (m := coerceInt(objNewWrap(u, source), S)) => coercionFailure()
  u' := objValUnwrap m
  not (m' := coerceInt(objNewWrap(u', S), T)) => coercionFailure()
  u'' := objValUnwrap m'
  not (m'' := coerceInt(objNewWrap(u'', T), target)) => coercionFailure()
  objValUnwrap m''

Ker2Expr(u,source is [.,S], target) ==
  u = '_$fromCoerceable_$ => canCoerce(S, target)
  not (m := coerceByFunction(objNewWrap(u, source), S)) => coercionFailure()
  u':= objValUnwrap m
  not (m' := coerceInt(objNewWrap(u', S), target)) => coercionFailure()
  objValUnwrap m'


--% Factored objects

Factored2Factored(u,oldmode,newmode) ==
  [.,oldargmode,:.]:= oldmode
  [.,newargmode,:.]:= newmode
  u = '_$fromCoerceable_$ => canCoerce(oldargmode,newargmode)
  u' := unwrap u
  unit' := coerceInt(objNewWrap(first u',oldargmode),newargmode)
  null unit' => coercionFailure()
  factors := KDR u'
  factors' := [(coerceFFE(x,oldargmode,newargmode)) for x in factors]
  member('failed,factors') => coercionFailure()
  [objValUnwrap(unit'),:factors']

coerceFFE(ffe, oldmode, newmode) ==
  fac' := coerceInt(objNewWrap(ffe.1,oldmode),newmode)
  null fac' => 'failed
  LIST2VEC [ffe.0,objValUnwrap(fac'),ffe.2]

--% Complex

Complex2underDomain(u,[.,S],target) ==
  u = '_$fromCoerceable_$ => nil
  [r,:i] := u
  i=domainZero(S) =>
    [r',.,.]:= coerceInt(objNewWrap(r,S),target) or
      coercionFailure()
    r'
  coercionFailure()

Complex2FR(u,S is [.,R],target is [.,T]) ==
  u = '_$fromCoerceable_$ =>
    S ^= T => nil
    R = $Integer => true
    nil
  S ^= T => coercionFailure()
  package :=
    R = $Integer => ['GaussianFactorizationPackage]
    coercionFailure()
  factor := getFunctionFromDomain('factor,package,[S])
  SPADCALL(u,factor)

Complex2Expr(u, source is [.,S], target is [., T]) ==
  u = '_$fromCoerceable_$ =>
    T is ['Complex, T1] and canCoerceFrom(S, T1) or coercionFailure()
  E := defaultTargetFE source
  negOne := coerceInt(objNewWrap(-1, $Integer), E)
  null negOne => coercionFailure()
  sqrtFun := getFunctionFromDomain('sqrt, E, [E])
  i := SPADCALL(objValUnwrap negOne, sqrtFun)
  realFun := getFunctionFromDomain('real, source, [source])
  imagFun := getFunctionFromDomain('imag, source, [source])
  real := SPADCALL(u, realFun)
  imag := SPADCALL(u, imagFun)
  realExp := coerceInt(objNewWrap(real, S), E)
  null realExp => coercionFailure()
  imagExp := coerceInt(objNewWrap(imag, S), E)
  null imagExp => coercionFailure()
  timesFun := getFunctionFromDomain('_*, E, [E, E])
  plusFun  := getFunctionFromDomain('_+, E, [E, E])
  newVal := SPADCALL(objValUnwrap(realExp),
             SPADCALL(i, objValUnwrap imagExp, timesFun), plusFun)
  newObj := objNewWrap(newVal, E)
  finalObj := coerceInt(newObj, target)
  finalObj => objValUnwrap finalObj
  coercionFailure()

--% Integer

I2EI(n,source,target) ==
  n = '_$fromCoerceable_$ => nil
  if not ODDP(n) then n else coercionFailure()

I2OI(n,source,target) ==
  n = '_$fromCoerceable_$ => nil
  if ODDP(n) then n else coercionFailure()

I2PI(n,source,target) ==
  n = '_$fromCoerceable_$ => nil
  if n > 0 then n else coercionFailure()

I2NNI(n,source,target) ==
  n = '_$fromCoerceable_$ => nil
  if n >= 0 then n else coercionFailure()

--% List

L2Tuple(val, source is [.,S], target is [.,T]) ==
    val = '_$fromCoerceable_$ => canCoerce(S,T)
    null (object := coerceInt1(mkObjWrap(val,source), ['List, T])) =>
      coercionFailure()
    asTupleNew0 objValUnwrap object

L2DP(l, source is [.,S], target is [.,n,T]) ==
  -- need to know size of the list
  l = '_$fromCoerceable_$ => nil
  n ^= SIZE l => coercionFailure()
  (v := coerceInt(objNewWrap(LIST2VEC l,['Vector,S]),['Vector,T])) or
    coercionFailure()
  V2DP(objValUnwrap v, ['Vector, T], target)

V2DP(v, source is [.,S], target is [.,n,T]) ==
  -- need to know size of the vector
  v = '_$fromCoerceable_$ => nil
  n ^= SIZE v => coercionFailure()
  (v1 := coerceInt(objNewWrap(v,source),['Vector,T])) or
    coercionFailure()
  dpFun  := getFunctionFromDomain('directProduct, target, [['Vector,T]])
  SPADCALL(objValUnwrap v1, dpFun)

L2V(l, source is [.,S], target is [.,T]) ==
  l = '_$fromCoerceable_$ => canCoerce(S,T)
  (v := coerceInt(objNewWrap(LIST2VEC l,['Vector,S]),target)) or
    coercionFailure()
  objValUnwrap(v)

V2L(v, source is [.,S], target is [.,T]) ==
  v = '_$fromCoerceable_$ => canCoerce(S,T)
  (l := coerceInt(objNewWrap(VEC2LIST v,['List,S]),target)) or
    coercionFailure()
  objValUnwrap(l)

L2M(u,[.,D],[.,R]) ==
  u = '_$fromCoerceable_$ => nil
  D is ['List,E] and isRectangularList(u,#u,# first u) =>
    u' := nil
    for x in u repeat
      x' := nil
      for y in x repeat
        (y' := coerceInt(objNewWrap(y,E),R)) or coercionFailure()
        x' := [objValUnwrap(y'),:x']
      u' := [LIST2VEC reverse x',:u']
    LIST2VEC reverse u'
  coercionFailure()

L2Record(l,[.,D],[.,:al]) ==
  l = '_$fromCoerceable_$ => nil
  #l = #al =>
    v:= [u for x in l for [":",.,D'] in al] where u ==
      T:= coerceInt(objNewWrap(x,D),D') or return 'failed
      objValUnwrap(T)
    v = 'failed => coercionFailure()
    #v = 2 => [v.0,:v.1]
    LIST2VEC v
  coercionFailure()

L2Rm(u,source is [.,D],target is [.,n,m,R]) ==
  u = '_$fromCoerceable_$ => nil
  D is ['List,E] and isRectangularList(u,n,m) =>
    L2M(u,source,['Matrix,R])
  coercionFailure()

L2Sm(u,source is [.,D],[.,n,R]) ==
  u = '_$fromCoerceable_$ => nil
  D is ['List,E] and isRectangularList(u,n,n) =>
    L2M(u,source,['Matrix,R])
  coercionFailure()

L2Set(x,source is [.,S],target is [.,T]) ==
  x = '_$fromCoerceable_$ => canCoerce(S,T)
  -- call library function  brace  to get a set
  target' := ['Set,S]
  u := objNewWrap(
    SPADCALL(x,getFunctionFromDomain('brace,target',[source])),
      target')
  (u := coerceInt(u,target)) or coercionFailure()
  objValUnwrap u

Set2L(x,source is [.,S],target is [.,T]) ==
  x = '_$fromCoerceable_$ => canCoerce(S,T)
  -- call library function  destruct  to get a list
  u := objNewWrap(
    SPADCALL(x,getFunctionFromDomain('destruct,source,[source])),
      ['List,S])
  (u := coerceInt(u,target)) or coercionFailure()
  objValUnwrap u

Agg2Agg(x,source is [agg1,S],target is [.,T]) ==
  x = '_$fromCoerceable_$ => canCoerce(S,T)
  S = T => coercionFailure()         -- library function
  target' := [agg1,T]
  (u := coerceInt(objNewWrap(x,source),target')) or coercionFailure()
  (u := coerceInt(u,target)) or coercionFailure()
  objValUnwrap u

Agg2L2Agg(x,source is [.,S],target) ==
  -- tries to use list as an intermediate type
  mid := ['List,S]
  x = '_$fromCoerceable_$ =>
    canCoerce(source,mid) and canCoerce(mid,target)
  (u := coerceInt(objNewWrap(x,source),mid)) or coercionFailure()
  (u := coerceInt(u,target)) or coercionFailure()
  objValUnwrap u

isRectangularList(x,p,q) ==
  p=0 or p=#x =>
    n:= #first x
    and/[n=#y for y in rest x] => p=0 or q=n

--% Matrix

M2L(x,[.,S],target) ==
  mid := ['Vector,['Vector,S]]
  x = '_$fromCoerceable_$ => canCoerce(mid,target)
  (u := coerceInt(objNewWrap(x,mid),target)) or coercionFailure()
  objValUnwrap u

M2M(x,[.,R],[.,S]) ==
  x = '_$fromCoerceable_$ => canCoerce(R,S)
  n := # x
  m := # x.0
  v := nil
  for i in 0..(n-1) repeat
    u := nil
    for j in 0..(m-1) repeat
      y := x.i.j
      (y' := coerceInt(objNewWrap(y,R),S)) or coercionFailure()
      u := [objValUnwrap y',:u]
    v := [LIST2VEC reverse u,:v]
  LIST2VEC reverse v

M2Rm(x,source is [.,R],[.,p,q,S]) ==
  x = '_$fromCoerceable_$ => nil
  n:= #x
  m:= #x.0
  n=p and m=q => M2M(x,source,[nil,S])
  coercionFailure()

M2Sm(x,source is [.,R],[.,p,S]) ==
  x = '_$fromCoerceable_$ => nil
  n:= #x
  m:= #x.(0)
  n=m and m=p => M2M(x,source,[nil,S])
  coercionFailure()

M2V(x,[.,S],target) ==
  mid := ['Vector,['Vector,S]]
  x = '_$fromCoerceable_$ =>  canCoerce(mid,target)
  (u := coerceInt(objNewWrap(x,mid),target)) or coercionFailure()
  objValUnwrap u

--% Multivariate Polynomial

Mp2Dmp(u, source is [., x, S], target is [dmp, y, T]) ==
  -- Change the representation to a DMP with the same variables and
  -- coerce.
  target' := [dmp,x,S]
  u = '_$fromCoerceable_$ => canCoerce(target',target)

  -- check if we have a constant
  u is [ =0,:c] =>
    null (u' := coerceInt(objNewWrap(c,S),target)) =>
      coercionFailure()
    objValUnwrap(u')

  plus := getFunctionFromDomain('_+,target',[target',target'])
  mult := getFunctionFromDomain('_*,target',[target',target'])
  one := domainOne(S)
  zero := domainZero(S)
  (u' := coerceInt(objNewWrap(Mp2SimilarDmp(u,S,#x,plus,mult,one,zero),
    target'),target)) or coercionFailure()
  objValUnwrap(u')

Mp2SimilarDmp(u,S,n,plus,mult,one,zero) ==
  u is [ =0,:c] =>
    c = zero => NIL  -- zero for dmp
    [[LIST2VEC LZeros n,:c]]
  u is [ =1,x,:terms] =>
    u' := NIL  -- zero for dmp
    for [e,:c] in terms repeat
      e' := LIST2VEC LZeros n
      e'.(x-1) := e
      t := [[e',:one]]
      t := SPADCALL(t,Mp2SimilarDmp(c,S,n,plus,mult,one,zero),mult)
      u' := SPADCALL(u',t,plus)
    u'

Mp2Expr(u,source is [mp,vars,S], target is [Expr,T]) ==
    u = '_$fromCoerceable_$ => canCoerce(S, target)

    dmp := ['DistributedMultivariatePolynomial, vars, S]
    not (d := coerceInt(objNewWrap(u, source), dmp)) => coercionFailure()
    Dmp2Expr(objValUnwrap d, dmp, target)

Mp2FR(u,S is [.,vl,R],[.,T]) ==
  u = '_$fromCoerceable_$ =>
    S ^= T => nil
    R in '((Integer) (Fraction (Integer))) => true
    nil
  S ^= T => coercionFailure()
  package :=
    R = $Integer =>
      ovl := ['OrderedVariableList, vl]
      ['MultivariateFactorize,ovl, ['IndexedExponents, ovl],R,S]
    R is ['Fraction, D] =>
      ovl := ['OrderedVariableList, vl]
      package := ['MRationalFactorize,['IndexedExponents, ovl], ovl, D, S]
    coercionFailure()
  factor := getFunctionFromDomain('factor,package,[S])
  SPADCALL(u,factor)

Mp2Mp(u,source is [mp,x,S], target is [.,y,T]) ==
  -- need not deal with case of x = y (coerceByMapping)
  common := intersection(y,x)
  x' := SETDIFFERENCE(x,common)
  y' := SETDIFFERENCE(y,common)

  u = '_$fromCoerceable_$ =>
    x = y => canCoerce(S,T)
    null common => canCoerce(source,T)
    null x' => canCoerce(S,target)
    null y' => canCoerce([mp,x',S],T)
    canCoerce([mp,x',S],[mp,y',T])

  -- first check for constant case
  u is [ =0,:c] =>
    (u' := coerceInt(objNewWrap(c,S),target)) or coercionFailure()
    objValUnwrap(u')

  plus  := getFunctionFromDomain('_+,target,[target,target])

  -- now no-common-variables case

  null common =>
    times := getFunctionFromDomain('_*,target,[target,target])
    expn  := getFunctionFromDomain('_*_*,target,
      [target,$NonNegativeInteger])
    Mp2MpAux0(u,S,target,x,plus,times,expn)

  -- if source vars are all in target
  null x' =>
    monom := getFunctionFromDomain('monomial,target,
      [target,['OrderedVariableList,y],$NonNegativeInteger])
    Mp2MpAux1(u,S,target,x,y,plus,monom)

  -- if target vars are all in source
  null y' =>    -- change source to MP[common] MP[x'] S
    univariate := getFunctionFromDomain('univariate,
      source,[source,['OrderedVariableList,x]])
    u' := Mp2MpAux2(u,x,common,x',common,x',univariate,S,NIL)
    (u' := coerceInt(objNewWrap(u', [mp,common,[mp,x',S]]),target)) or
      coercionFailure()
    objValUnwrap(u')

  -- we have a mixture
  (u' := coerceInt(objNewWrap(u,source),[mp,common,[mp,x',S]])) or
    coercionFailure()
  (u' := coerceInt(u',target)) or coercionFailure()
  objValUnwrap(u')

Mp2MpAux0(u,S,target,vars,plus,times,expn) ==
  -- for case when no common variables
  u is [ =0,:c] =>
    (u' := coerceInt(objNewWrap(c,S),target)) or coercionFailure()
    objValUnwrap(u')
  [.,var,:terms] := u
  [mp,.,T] := target
  x := coerceInt(objNewWrap(vars.(var-1),['Variable,vars.(var-1)]),
    [mp,vars,$Integer]) or coercionFailure()
  (x := coerceInt(x,T)) or coercionFailure()
  x := [0,:objValUnwrap x]
  sum := domainZero(target)
  for [e,:c] in terms repeat
    prod := SPADCALL(SPADCALL(x,e,expn),
      Mp2MpAux0(c,S,target,vars,plus,times,expn),times)
    sum := SPADCALL(sum,prod,plus)
  sum

Mp2MpAux1(u,S,target,varl1,varl2,plus,monom) ==
  -- for case when source vars are all in target
  u is [ =0,:c] =>
    (u' := coerceInt(objNewWrap(c,S),target)) or coercionFailure()
    objValUnwrap(u')
  [.,var,:terms] := u
  sum := domainZero(target)
  for [e,:c] in terms repeat
    mon := SPADCALL( Mp2MpAux1(c,S,target,varl1,varl2,plus,monom),
      position1(varl1.(var-1), varl2),e,monom)
    sum := SPADCALL(sum,mon,plus)
  sum

Mp2MpAux2(u,x,oldcomm,oldrest,common,restvars,univariate,S,isUnder) ==
  -- target vars are all in source
  mp2 := ['MultivariatePolynomial,oldcomm,['MultivariatePolynomial,
    oldrest,S]]
  common =>
    u is [ =0,:c] =>
      (u' := coerceInt(objNewWrap(c,S),mp2)) or coercionFailure()
      objValUnwrap(u')
    [var,:common] := common
    u' := SPADCALL(u,position1(var,x),univariate)
    null(rest(u')) and (first(first(u')) = 0) =>
      Mp2MpAux2(u,x,oldcomm,oldrest,common,restvars,univariate,S,isUnder)
    [1,position1(var,oldcomm),:[[e,:Mp2MpAux2(c,x,oldcomm,oldrest,
      common,restvars,univariate,S,isUnder)] for [e,:c] in u']]
  null isUnder =>
    [0,:Mp2MpAux2(u,x,oldcomm,oldrest,common,restvars,univariate,S,true)]
  -- just treat like elt of [mp,x',S]
  u is [ =0,:c] => u
  [var,:restvars] := restvars
  u' := SPADCALL(u,position1(var,x),univariate)
  null(rest(u')) and (first(first(u')) = 0) =>
    Mp2MpAux2(u,x,oldcomm,oldrest,common,restvars,univariate,S,isUnder)
  [1,position1(var,oldrest),:[[e,:Mp2MpAux2(c,x,oldcomm,oldrest,
    common,restvars,univariate,S,isUnder)] for [e,:c] in u']]

genMpFromDmpTerm(u, oldlen) ==

  -- given one term of a DMP representation of a polynomial, this creates
  -- the corresponding MP term.

  patlen := oldlen
  [e,:c] := u
  numexps := # e
  patlen >= numexps => [0, :c]
  for i in patlen..(numexps - 1) repeat
    e.i = 0 => patlen := patlen + 1
    return nil
  patlen >= numexps => [0, :c]
  [1, 1+patlen, [e.patlen,:genMpFromDmpTerm(u,patlen+1)]]

Mp2P(u, source is [mp,vl, S], target is [p,R]) ==
  u = '_$fromCoerceable_$ => canCoerce(S,target)
  S is ['Polynomial,.] => MpP2P(u,vl,S,R)
  vl' := REVERSE MSORT vl
  -- if Mp2Mp fails, a THROW will occur
  u' := Mp2Mp(u,source,[mp,vl',S])
  u' := translateMpVars2PVars (u',vl')
  (u' := coerceInt(objNewWrap(u',[p,S]),target)) or coercionFailure()
  objValUnwrap(u')

MpP2P(u,vl,PS,R) ==
  -- u has type MP(vl,PS). Want to coerce to P R.
  PR := ['Polynomial,R]
  u is [ =0,:c] =>
    (u' :=coerceInt(objNewWrap(c,PS),PR)) or
      coercionFailure()
    objValUnwrap u'
  [ .,pos,:ec] := u
  multivariate := getFunctionFromDomain('multivariate,
    PR,[['SparseUnivariatePolynomial,PR],$Symbol])
  sup := [[e,:MpP2P(c,vl,PS,R)] for [e,:c] in ec]
  p := SPADCALL(sup,vl.(pos-1),multivariate)
  --(p' :=coerceInt(objNewWrap(p,PS),['Polynomial,R])) or coercionFailure()
  --objValUnwrap(p')

Mp2Up(u,source is [mp,vl,S],target is [up,x,T]) ==
  u = '_$fromCoerceable_$ =>
    member(x,vl) =>
      vl = [x] => canCoerce(S,T)
      canCoerce([mp,delete(x,vl),S],T)
    canCoerce(source,T)

  u is [ =0,:c] =>      -- constant polynomial?
    (u' := coerceInt(objNewWrap(c,S),target)) or coercionFailure()
    objValUnwrap u'

  null member(x,vl) =>
    (u' := coerceInt(objNewWrap(u,source),T)) or coercionFailure()
    [[0,:objValUnwrap(u')]]

  vl = [x] =>
    u' := [[e,:c] for [e,.,:c] in CDDR u]
    (u' := coerceInt(objNewWrap(u',[up,x,S]),target))
      or coercionFailure()
    objValUnwrap u'

  -- do a univariate to transform u to a UP(x,P S) and then coerce again
  var := position1(x,vl)
  UPP := ['UnivariatePolynomial,x,source]
  univariate := getFunctionFromDomain('univariate,
    source,[source,['OrderedVariableList,vl]])
  upU := SPADCALL(u,var,univariate)  -- we may assume this has type UPP
  (u' := coerceInt(objNewWrap(upU,UPP),target)) or coercionFailure()
  objValUnwrap u'

--% OrderedVariableList

OV2OV(u,source is [.,svl], target is [.,tvl]) ==
  svl = intersection(svl,tvl) =>
    u = '_$fromCoerceable_$ => true
    position1(svl.(u-1),tvl)
  u = '_$fromCoerceable_$ => nil
  coercionFailure()

OV2P(u,source is [.,svl], target is [.,T]) ==
  u = '_$fromCoerceable_$ => true
  v := svl.(unwrap(u)-1)
  [1,v,[1,0,:domainOne(T)]]

OV2poly(u,source is [.,svl], target is [p,vl,T]) ==
  u = '_$fromCoerceable_$ =>
    p = 'UnivariatePolynomial => (# svl = 1) and (p = svl.0)
    and/[member(v,vl) for v in svl]
  v := svl.(unwrap(u)-1)
  val' := [1,:domainOne(T)]
  p = 'UnivariatePolynomial =>
    v ^= vl => coercionFailure()
    [[1,:domainOne(T)]]
  null member(v,vl) => coercionFailure()
  val' := [[1,:domainOne(T)]]
  source' := ['UnivariatePolynomial,v,T]
  (u' := coerceInt(objNewWrap(val',source'),target)) or
    coercionFailure()
  objValUnwrap(u')

OV2SE(u,source is [.,svl], target) ==
  u = '_$fromCoerceable_$ => true
  svl.(unwrap(u)-1)

OV2Sy(u,source is [.,svl], target) ==
  u = '_$fromCoerceable_$ => true
  svl.(unwrap(u)-1)

--% Polynomial

varsInPoly(u) ==
  u is [ =1, v, :termlist] =>
    [v,:varsInPoly(c) for [e,:c] in termlist]
  nil

P2FR(u,S is [.,R],[.,T]) ==
  u = '_$fromCoerceable_$ =>
    S ^= T => nil
    R in '((Integer) (Fraction (Integer))) => true
    nil
  S ^= T => coercionFailure()
  package :=
    R = $Integer =>
      ['MultivariateFactorize,$Symbol,['IndexedExponents, $Symbol],R,S]
    R is ['Fraction, D] =>
      package := ['MRationalFactorize,['IndexedExponents, $Symbol],$Symbol,
                 D, S]
    coercionFailure()
  factor := getFunctionFromDomain('factor,package,[S])
  SPADCALL(u,factor)

P2Dmp(u, source is [., S], target is [., y, T]) ==
  u = '_$fromCoerceable_$ =>
    -- might be able to say yes
    canCoerce(source,T)
  u is [ =0,:c] =>       -- polynomial is a constant
    (u' := coerceInt(objNewWrap(c,S),target)) or coercionFailure()
    objValUnwrap(u')
  univariate := getFunctionFromDomain('univariate,
    source,[source,$Symbol])
  plus := getFunctionFromDomain("+",target,[target,target])
  monom := getFunctionFromDomain('monomial,target,
    [target,['OrderedVariableList,y],$NonNegativeInteger])
  P2DmpAux(u,source,S,target,copy y,y,T,univariate,plus,monom)

P2Expr(u, source is [.,S], target is [., T]) ==
  u = '_$fromCoerceable_$ =>
    canCoerce(S, T)
  S = T => coercionFailure()
  newS := ['Polynomial, T]
  val := coerceInt(objNewWrap(u, source), newS)
  null val => coercionFailure()
  val := coerceInt(val, target)
  null val => coercionFailure()
  objValUnwrap val

P2DmpAux(u,source,S,target,varlist,vars,T,univariate,plus,monom) ==
  u is [ =0,:c] =>       -- polynomial is a constant
    (u' := coerceInt(objNewWrap(c,S),target)) or coercionFailure()
    objValUnwrap(u')

  -- if no variables left, try to go to underdomain of target (T)
  null vars =>
    (u' := coerceInt(objNewWrap(u,source),T)) or coercionFailure()
    -- if successful, embed
    (u' := coerceByFunction(u',target)) or coercionFailure()
    objValUnwrap(u')

  -- there are variables, so get them out of u
  [x,:vars] := vars
  sup := SPADCALL(u,x,univariate)  -- this is a SUP P S
  null sup =>           -- zero? unlikely.
    domainZero(target)
  -- degree 0 polynomial? (variable did not occur)
  null(rest(sup)) and first(sup) is [ =0,:c] =>
    -- call again, but with one less var
    P2DmpAux(c,source,S,target,varlist,vars,T,univariate,plus,monom)
  var := position1(x,varlist)
  u' := domainZero(target)
  for [e,:c] in sup repeat
    u'' := SPADCALL(
      P2DmpAux(c,source,S,target,varlist,vars,T,univariate,plus,monom),
        var,e,monom)
    u' := SPADCALL(u',u'',plus)
  u'

P2Mp(u, source is [., S], target is [., y, T]) ==
  u = '_$fromCoerceable_$ =>
    -- might be able to say yes
    canCoerce(source,T)
  univariate := getFunctionFromDomain('univariate,
    source,[source,$Symbol])
  P2MpAux(u,source,S,target,copy y,y,T,univariate)

P2MpAux(u,source,S,target,varlist,vars,T,univariate) ==
  u is [ =0,:c] =>       -- polynomial is a constant
    (u' := coerceInt(objNewWrap(c,S),target)) or
      coercionFailure()
    objValUnwrap(u')

  -- if no variables left, try to go to underdomain of target (T)
  null vars =>
    (u' := coerceInt(objNewWrap(u,source),T)) or
      coercionFailure()
    -- if successful, embed
    [ 0,:objValUnwrap(u')]

  -- there are variables, so get them out of u
  [x,:vars] := vars
  sup := SPADCALL(u,x,univariate)  -- this is a SUP P S
  null sup =>           -- zero? unlikely.
    domainZero(target)
  -- degree 0 polynomial? (variable did not occur)
  null(rest(sup)) and first(sup) is [ =0,:c] =>
    -- call again, but with one less var
    P2MpAux(c,source,S,target,varlist,vars,T,univariate)
  terms := [[e,:P2MpAux(c,source,S,target,varlist,vars,T,univariate)] for
    [e,:c] in sup]
  [1, position1(x,varlist), :terms]

varIsOnlyVarInPoly(u, var) ==
  u is [ =1, v, :termlist] =>
    v ^= var => nil
    and/[varIsOnlyVarInPoly(c,var) for [e,:c] in termlist]
  true

P2Up(u,source is [.,S],target is [.,x,T]) ==
  u = '_$fromCoerceable_$ => canCoerce(source,T)
  u is [ =0,:c] =>
    (u' := coerceInt(objNewWrap(c,S),target)) or coercionFailure()
    objValUnwrap(u')

  -- see if the target var is the polynomial vars
  varsFun := getFunctionFromDomain('variables,source,[source])
  vars := SPADCALL(u,varsFun)
  not member(x,vars) =>
    (u' := coerceInt(objNewWrap(u,source),T)) or coercionFailure()
    [[0,:objValUnwrap(u')]]

  -- do a univariate to transform u to a UP(x,P S) and then coerce again
  UPP := ['UnivariatePolynomial,x,source]
  univariate := getFunctionFromDomain('univariate,
    source,[source,$Symbol])
  upU := SPADCALL(u,x,univariate)  -- we may assume this has type UPP
  (u' := coerceInt(objNewWrap(upU,UPP),target)) or coercionFailure()
  objValUnwrap(u')

--% Fraction

Qf2PF(u,source is [.,D],target) ==
  u = '_$fromCoerceable_$ => canCoerce(D,target)
  [num,:den] := u
  num':= coerceInt(objNewWrap(num,D),target) or
    coercionFailure()
  num' := objValUnwrap num'
  den':= coerceInt(objNewWrap(den,D),target) or
    coercionFailure()
  den' := objValUnwrap den'
  equalZero(den', target) => throwKeyedMsg("S2IA0001",NIL)
  SPADCALL(num',den', getFunctionFromDomain("/",target,[target,target]))

Qf2F(u,source is [.,D,:.],target) ==
  D = $Integer =>
    u = '_$fromCoerceable_$ => true
    Rn2F(u,source,target)
  u = '_$fromCoerceable_$ => canCoerce(D,target)
  [num,:den] := u
  [.,:num']:= coerceInt(objNewWrap(num,D),target) or
    coercionFailure()
  [.,:den']:= coerceInt(objNewWrap(den,D),target) or
    coercionFailure()
  (unwrap num') * 1.0 / (unwrap den')

Rn2F(rnum, source, target) ==
  float(CAR(rnum)/CDR(rnum))

-- next function is needed in RN algebra code
--Rn2F([a,:b],source,target) ==
--  al:=if LINTP a then QLENGTHCODE a else 4
--  bl:=if LINTP b then QLENGTHCODE b else 4
--  MAX(al,bl) < 36 => FLOAT a / FLOAT b
--  sl:=0
--  if al>32 then
--     sl:=35*(al-32)/4
--     a:=a/2**sl
--  if bl>32 then
--     sbl:=35*(bl-32)/4
--     b:=b/2**sbl
--     sl:=sl-sbl
--  ans:=FLOAT a /FLOAT b
--  sl=0 => ans
--  ans*2**sl

Qf2domain(u,source is [.,D],target) ==
  -- tests whether it is an element of the underlying domain
  useUnder := (ut := underDomainOf target) and canCoerce(source,ut)
  u = '_$fromCoerceable_$ => useUnder
  not (containsPolynomial(D) and containsPolynomial(target)) and
    useUnder => coercionFailure()    -- let other mechanism handle it
  [num, :den] := u
  (num' := coerceInt(objNewWrap(num,D),target)) or coercionFailure()
  num' := objValUnwrap(num')
  equalOne(den,D) => num'
  (target is [.,[=$QuotientField,T]]) or
    (target is [.,.,[=$QuotientField,T]]) =>
      (den' := coerceInt(objNewWrap(den,D),T)) or coercionFailure()
      den' := [domainOne(T),:objValUnwrap(den')]
      timesfunc:= getFunctionFromDomain('_*,target,
        [[$QuotientField,T],target])
      SPADCALL(den',num',timesfunc)
  coercionFailure()

Qf2EF(u,[.,S],target) ==
  u = '_$fromCoerceable_$ => canCoerce(S,target)
  [num,:den] := u
  (num' := coerceInt(objNewWrap(num,S),target)) or
    coercionFailure()
  (den' := coerceInt(objNewWrap(den,S),target)) or
    coercionFailure()
  divfun := getFunctionFromDomain("/",target,[target,target])
  SPADCALL(objValUnwrap(num'),objValUnwrap(den'),divfun)

Qf2Qf(u0,[.,S],target is [.,T]) ==
  u0 = '_$fromCoerceable_$ =>
    S = ['Polynomial, [$QuotientField, $Integer]] and
      T = '(Polynomial (Integer)) => true
    canCoerce(S,T)
  [a,:b] := u0
  S = ['Polynomial, [$QuotientField, $Integer]] and
    T = '(Polynomial (Integer)) =>
      (a' := coerceInt(objNewWrap(a,S),target)) =>
        (b' := coerceInt(objNewWrap(b,S),target)) =>
          divfunc:= getFunctionFromDomain('_/,target,[target,target])
          SPADCALL(objValUnwrap(a'),objValUnwrap(b'),divfunc)
        coercionFailure()
      coercionFailure()
  (a' := coerceInt(objNewWrap(a,S),T)) =>
    (b' := coerceInt(objNewWrap(b,S),T)) =>
      [objValUnwrap(a'),:objValUnwrap(b')]
    coercionFailure()
  coercionFailure()

-- partOf(x,i) ==
--   VECP x => x.i
--   i=0 => first x
--   i=1 => rest x
--   systemError '"partOf"

--% RectangularMatrix

Rm2L(x,[.,.,.,R],target) == M2L(x,['Matrix,R],target)

Rm2M(x,[.,.,.,R],target is [.,S]) == M2M(x,[nil,R],target)

Rm2Sm(x,[.,n,m,S],[.,p,R]) ==
  x = '_$fromCoerceable_$ => n=m and m=p and canCoerce(S,R)
  n=m and m=p =>
    M2M(x,[nil,S],[nil,R])
  coercionFailure()

Rm2V(x,[.,.,.,R],target) == M2V(x,['Matrix,R],target)

--% Script

Scr2Scr(u, source is [.,S], target is [.,T]) ==
  u = '_$fromCoerceable_$ => canCoerce(S,T)
  null (v := coerceInt(objNewWrap(CDR u,S),T)) =>
    coercionFailure()
  [CAR u, :objValUnwrap(v)]

--% SparseUnivariatePolynomialnimial

SUP2Up(u,source is [.,S],target is [.,x,T]) ==
  u = '_$fromCoerceable_$ => canCoerce(source,T) or canCoerce(S,T)
  null u => u
  S = T => u
  -- try to go underneath first
  null (u' := coerceInt(objNewWrap(u,source),T)) =>
    -- must be careful in case any of the coeffs come back 0
    u' := NIL
    zero := getConstantFromDomain('(Zero),T)
    for [e,:c] in u repeat
      c' := objValUnwrap (coerceInt(objNewWrap(c,S),T) or
        coercionFailure())
      c' = zero => 'iterate
      u' := [[e,:c'],:u']
    nreverse u'
  [[0,:objValUnwrap u']]

--% SquareMatrix

Sm2L(x,[.,.,R],target) == M2L(x,['Matrix,R],target)

Sm2M(x,[.,n,R],target is [.,S]) == M2M(x,[nil,R],target)

Sm2PolyType(u,source is [sm,n,S], target is [pol,vl,T]) ==
  -- only really handles cases like:
  --      SM[2] P I -> P[x,y] SM[2] P I
  -- works for UP, MP, DMP and NDMP
  u = '_$fromCoerceable_$ => canCoerce(source,T)
  -- first want to check case S is Polynomial
  S is ['Polynomial,S'] =>
    -- check to see if variable occurs in any of the terms
    if ATOM vl
      then vl' := [vl]
      else vl' := vl
    novars := true
    for i in 0..(n-1) while novars repeat
      for j in 0..(n-1) while novars repeat
        varsUsed := varsInPoly u.i.j
        or/[member(x,varsUsed) for x in vl'] => novars := nil
    novars => coercionFailure()
    source' := [sm,n,[pol,vl,S]]
    null (u' := coerceInt(objNewWrap(u,source),source')) =>
      coercionFailure()
    null (u' := coerceInt(u',target)) =>
      coercionFailure()
    objValUnwrap(u')
  -- let other cases be handled by standard machinery
  coercionFailure()

Sm2Rm(x,[.,n,R],[.,p,q,S]) ==
  x = '_$fromCoerceable_$ => p=q and p=n and canCoerce(R,S)
  p=q and p=n =>
    M2M(x,[nil,R],[nil,S])
  coercionFailure()

Sm2V(x,[.,.,R],target) == M2V(x,['Matrix,R],target)

--% Symbol

Sy2OV(u,source,target is [.,vl]) ==
  u = '_$fromCoerceable_$ => nil
  position1(u,vl)

Sy2Dmp(u,source,target is [dmp,vl,S]) ==
  u = '_$fromCoerceable_$ => canCoerce(source,S)
  len:= #vl
  -1^=(n:= position(u,vl)) =>
    u:= wrap LIST [LIST2VEC [(n=i => 1; 0) for i in 0..len-1],:1]
    objValUnwrap(coerceInt(objNew(u,[dmp,vl,$Integer]),target))
  (u := coerceInt(objNewWrap(u,source),S)) or coercionFailure()
  [[Zeros len,:objValUnwrap u]]

Sy2Mp(u,source,target is [mp,vl,S]) ==
  u = '_$fromCoerceable_$ => canCoerce(source,S)
  (n:= position1(u,vl)) ^= 0 =>
    [1,n,[1,0,:domainOne(S)]]
  (u := coerceInt(objNewWrap(u,source),S)) or coercionFailure()
  [0,:objValUnwrap(u)]

Sy2NDmp(u,source,target is [ndmp,vl,S]) ==
  u = '_$fromCoerceable_$ => canCoerce(source,S)
  len:= #vl
  -1^=(n:= position(u,vl)) =>
    u:= wrap LIST [LIST2VEC [(n=i => 1; 0) for i in 0..len-1],:1]
    objValUnwrap(coerceInt(objNew(u,[ndmp,vl,$Integer]),target))
  (u := coerceInt(objNewWrap(u,source),S)) or coercionFailure()
  [[Zeros len,:objValUnwrap(u)]]

Sy2P(u,source,target is [poly,S]) ==
  u = '_$fromCoerceable_$ => true
  -- first try to get it into an underdomain
  if (S ^= $Integer) then
    u' := coerceInt(objNewWrap(u,source),S)
    if u' then return [0,:objValUnwrap(u')]
  -- if that failed, return it as a polynomial variable
  [1,u,[1,0,:domainOne(S)]]

Sy2Up(u,source,target is [up,x,S]) ==
  u = '_$fromCoerceable_$ => canCoerce(source,S)
  u=x => [[1,:domainOne(S)]]
  (u := coerceInt(objNewWrap(u,source),S)) or coercionFailure()
  [[0,:objValUnwrap u]]

Sy2Var(u,source,target is [.,x]) ==
  u = '_$fromCoerceable_$ => NIL
  u=x => u
  coercionFailure()

--% Univariate Polynomial

Up2Dmp(u,source is ['UnivariatePolynomial,var,S],
 target is ['DistributedMultivariatePolynomial,vl,T]) ==
  -- var must be a member of vl, or u is a constant
  u = '_$fromCoerceable_$ => member(var,vl) and canCoerce(S,target)
  null u => domainZero(target)
  u is [[e,:c]] and e=0 =>
    z:= coerceInt(objNewWrap(c,S),target) => objValUnwrap(z)
    coercionFailure()
  member(var,vl) =>
    x:= domainZero(target)
    one:= domainOne(T)
    plusfunc:= getFunctionFromDomain('_+,target,[target,target])
    multfunc:= getFunctionFromDomain('_*,target,[target,target])
    n:= #vl ; p:= POSN1(var,vl)
    l1:= not (p=0) and [0 for m in 1..p]
    l2:= not (p=n-1) and [0 for m in p..n-2]
    for [e,:c] in u until not z repeat
      z:= coerceInt(objNewWrap(c,S),target) =>
        y:= SPADCALL(objValUnwrap(z),
          [[LIST2VEC [:l1,e,:l2],:one]],multfunc)
        x:= SPADCALL(x,y,plusfunc)
    z => x
    coercionFailure()
  coercionFailure()

Up2Expr(u,source is [up,var,S], target is [Expr,T]) ==
    u = '_$fromCoerceable_$ => canCoerce(S, target)

    null u => domainZero(target)

    u is [[e,:c]] and e=0 =>
        (z := coerceInt(objNewWrap(c, S), target)) => objValUnwrap(z)
        coercionFailure()

    sym := objValUnwrap coerceInt(objNewWrap(var, $Symbol), target)

    plus := getFunctionFromDomain("+",  target, [target, target])
    mult := getFunctionFromDomain("*",  target, [target, target])
    expn := getFunctionFromDomain("**", target, [target, $Integer])

    -- coerce via Horner's rule

    [e1, :c1] := first u
    if not (S = target) then
        not (c1 := coerceInt(objNewWrap(c1, S), target)) => coercionFailure()
        c1 := objValUnwrap(c1)

    for [e2, :c2] in rest u repeat
        coef :=
            e1 - e2 = 1 => sym
            SPADCALL(sym, e1-e2, expn)
        if not (S = target) then
            not (c2 := coerceInt(objNewWrap(c2, S), target)) =>
                coercionFailure()
            c2 := objValUnwrap(c2)
        coef := SPADCALL(SPADCALL(c1, coef, mult), c2, plus)
        e1 := e2
        c1 := coef

    e1 = 0 => c1
    e1 = 1 => SPADCALL(sym, c1, mult)
    SPADCALL(SPADCALL(sym, e1, expn), c1, mult)

Up2FR(u,S is [.,x,R],target is [.,T]) ==
  u = '_$fromCoerceable_$ =>
    S ^= T => nil
    R in '((Integer) (Fraction (Integer))) => true
    nil
  S ^= T => coercionFailure()
  package :=
    R = $Integer => ['UnivariateFactorize,S]
    R = $RationalNumber => package := ['RationalFactorize,S]
    coercionFailure()
  factor := getFunctionFromDomain('factor,package,[S])
  SPADCALL(u,factor)

Up2Mp(u,source is [.,x,S], target is [.,vl,T]) ==
  u = '_$fromCoerceable_$ =>
    member(x,vl) => canCoerce(S,T)
    canCoerce(source,T)

  null u => domainZero(target)

  null(rest(u)) and (first(u) is [e,:c]) and e=0 =>
    x:= coerceInt(objNewWrap(c,S),target) => objValUnwrap(x)
    coercionFailure()

  null member(x,vl) =>
    (x := coerceInt(objNewWrap(u,source),T)) or coercionFailure()
    [0,:objValUnwrap(x)]

  plus  := getFunctionFromDomain('_+,target,[target,target])
  monom := getFunctionFromDomain('monomial,target,
    [target,['OrderedVariableList,vl],$NonNegativeInteger])
  sum := domainZero(target)
  pos := position1(x,vl)

  for [e,:c] in u repeat
    (p := coerceInt(objNewWrap(c,S),target)) or coercionFailure()
    mon := SPADCALL(objValUnwrap(p),pos,e,monom)
    sum := SPADCALL(sum,mon,plus)
  sum

Up2P(u,source is [.,var,S],target is [.,T]) ==
  u = '_$fromCoerceable_$ => canCoerce(S,target)
  null u => domainZero(target)
  u is [[e,:c]] and e=0 =>
    x:= coerceInt(objNewWrap(c,S),target) => objValUnwrap(x)
    coercionFailure()
  pol:= domainZero(target)
  one:= domainOne(T)
  plusfunc := getFunctionFromDomain("+",target,[target,target])
  multfunc := getFunctionFromDomain("*",target,[target,target])
  for [e,:c] in u until not x repeat
    x:= coerceInt(objNewWrap(c,S),target) =>
      term:= SPADCALL([1,var,[e,0,:one]],objValUnwrap(x),multfunc)
      pol:= SPADCALL(pol,term,plusfunc)
    coercionFailure()
  x => pol
  coercionFailure()

Up2SUP(u,source is [.,x,S],target is [.,T]) ==
  u = '_$fromCoerceable_$ => canCoerce(source,T) or canCoerce(S,T)
  null u => u
  S = T => u
  -- try to go underneath first
  null (u' := coerceInt(objNewWrap(u,source),T)) =>
    u' := NIL
    zero := getConstantFromDomain('(Zero),T)
    for [e,:c] in u repeat
      c' := objValUnwrap (coerceInt(objNewWrap(c,S),T) or
        coercionFailure())
      c' = zero => 'iterate
      u' := [[e,:c'],:u']
    nreverse u'
  [[0,:objValUnwrap u']]

Up2Up(u,source is [.,v1,S], target is [.,v2,T]) ==
  -- if v1 = v2 then this is handled by coerceIntByMap
  -- this only handles case where poly is a constant
  u = '_$fromCoerceable_$ =>
    v1=v2 => canCoerce(S,T)
    canCoerce(source,T)
  null u => u
  u is [[e,:c]] and e=0 =>
    x:= coerceInt(objNewWrap(c,S),target) => objValUnwrap(x)
    coercionFailure()
  coercionFailure()

insertAlist(a,b,l) ==
  null l => [[a,:b]]
  a = l.0.0 => (RPLAC(CDAR l,b);l)
  _?ORDER(l.0.0,a) => [[a,:b],:l]
  (fn(a,b,l);l) where fn(a,b,l) ==
    null rest l => RPLAC(rest l,[[a,:b]])
    a = l.1.0 => RPLAC(rest l.1,b)
    _?ORDER(l.1.0,a) => RPLAC(rest l,[[a,:b],:rest l])
    fn(a,b,rest l)

--% Union

Un2E(x,source,target) ==
  ['Union,:branches] := source
  x = '_$fromCoerceable_$ =>
    and/[canCoerce(t, target) for t in branches | ^ STRINGP t]
  coerceUn2E(x,source)

--% Variable

Var2OV(u,source,target is [.,vl]) ==
  sym := CADR source
  u = '_$fromCoerceable_$ => member(sym,vl)
  member(sym,vl) => position1(sym,vl)
  coercionFailure()

Var2Dmp(u,source,target is [dmp,vl,S]) ==
  sym := CADR source
  u = '_$fromCoerceable_$ => member(sym,vl) or canCoerce(source,S)

  len := #vl
  -1 ^= (n:= position(sym,vl)) =>
    LIST [LIST2VEC [(n=i => 1; 0) for i in 0..len-1],
      :getConstantFromDomain('(One),S)]
  (u := coerceInt(objNewWrap(u,source),S)) or coercionFailure()
  [[Zeros len,:objValUnwrap u]]

Var2Gdmp(u,source,target is [dmp,vl,S]) ==
  sym := CADR source
  u = '_$fromCoerceable_$ => member(sym,vl) or canCoerce(source,S)

  len := #vl
  -1 ^= (n:= position(sym,vl)) =>
    LIST [LIST2VEC [(n=i => 1; 0) for i in 0..len-1],
      :getConstantFromDomain('(One),S)]
  (u := coerceInt(objNewWrap(u,source),S)) or coercionFailure()
  [[Zeros len,:objValUnwrap u]]

Var2Mp(u,source,target is [mp,vl,S]) ==
  sym := CADR source
  u = '_$fromCoerceable_$ => member(sym,vl) or canCoerce(source,S)
  (n:= position1(u,vl)) ^= 0 =>
    [1,n,[1,0,:getConstantFromDomain('(One),S)]]
  (u := coerceInt(objNewWrap(u,source),S)) or coercionFailure()
  [0,:objValUnwrap u]

Var2NDmp(u,source,target is [ndmp,vl,S]) ==
  sym := CADR source
  u = '_$fromCoerceable_$ => member(sym,vl) or canCoerce(source,S)

  len:= #vl
  -1^=(n:= position(u,vl)) =>
    LIST [LIST2VEC [(n=i => 1; 0) for i in 0..len-1],
      :getConstantFromDomain('(One),S)]
  (u := coerceInt(objNewWrap(u,source),S)) or coercionFailure()
  [[Zeros len,:objValUnwrap(u)]]

Var2P(u,source,target is [poly,S]) ==
  sym := CADR source
  u = '_$fromCoerceable_$ => true

  -- first try to get it into an underdomain
  if (S ^= $Integer) then
    u' := coerceInt(objNewWrap(u,source),S)
    if u' then return [0,:objValUnwrap(u')]
  -- if that failed, return it as a polynomial variable
  [1,sym,[1,0,:getConstantFromDomain('(One),S)]]

Var2QF(u,source,target is [qf,S]) ==
  u = '_$fromCoerceable_$ => canCoerce(source,S)

  S = $Integer => coercionFailure()
  sym := CADR source
  (u' := coerceInt(objNewWrap(u,source),S)) or coercionFailure()
  [objValUnwrap u',:getConstantFromDomain('(One),S)]

Var2FS(u,source,target is [fs,S]) ==
  u = '_$fromCoerceable_$ => true
  (v := coerceInt(objNewWrap(u,source),['Polynomial,S])) or
    coercionFailure()
  (v := coerceInt(v,target)) or coercionFailure()
  objValUnwrap v

Var2Up(u,source,target is [up,x,S]) ==
  sym := CADR source
  u = '_$fromCoerceable_$ => (sym = x) or canCoerce(source,S)

  x=sym => [[1,:getConstantFromDomain('(One),S)]]
  (u := coerceInt(objNewWrap(u,source),S)) or coercionFailure()
  [[0,:objValUnwrap u]]

Var2SUP(u,source,target is [sup,S]) ==
  sym := CADR source
  u = '_$fromCoerceable_$ => (sym = "?") or canCoerce(source,S)

  sym = "?" => [[1,:getConstantFromDomain('(One),S)]]
  (u := coerceInt(objNewWrap(u,source),S)) or coercionFailure()
  [[0,:objValUnwrap u]]

Var2UpS(u,source,target is [ups,x,S]) ==
  sym := CADR source
  u = '_$fromCoerceable_$ => (sym = x) or canCoerce(source,S)

  mid := ['UnivariatePolynomial,x,S]
  x = sym =>
    u := Var2Up(u,source,mid)
    (u := coerceInt(objNewWrap(u,mid),target)) or coercionFailure()
    objValUnwrap u
  (u := coerceInt(objNewWrap(u,source),S)) or coercionFailure()
  (u := coerceInt(u,target)) or coercionFailure()
  objValUnwrap u

Var2OtherPS(u,source,target is [.,x,S]) ==
  sym := CADR source
  mid := ['UnivariatePowerSeries,x,S]
  u = '_$fromCoerceable_$ =>
    (sym = x) or (canCoerce(source,mid) and canCoerce(mid,target))
  u := Var2UpS(u,source,mid)
  (u := coerceInt(objNewWrap(u,mid),target)) or coercionFailure()
  objValUnwrap u

--% Vector

V2M(u,[.,D],[.,R]) ==
  u = '_$fromCoerceable_$ =>
    D is ['Vector,:.] => nil  -- don't have data
    canCoerce(D,R)
  -- first see if we are coercing a vector of vectors
  D is ['Vector,E] and
    isRectangularVector(u,MAXINDEX u,MAXINDEX u.0) =>
      LIST2VEC
        [LIST2VEC [objValUnwrap(coerceInt(objNewWrap(x.j,E),R))
           for j in 0..MAXINDEX(x:=u.i)] for i in 0..MAXINDEX u]
  -- if not, try making it into a 1 by n matrix
  coercionFailure()
--LIST2VEC [LIST2VEC [objValUnwrap(coerceInt(objNewWrap(u.i,D),R))
--  for i in 0..MAXINDEX(u)]]

V2Rm(u,[.,D],[.,n,m,R]) ==
  u = '_$fromCoerceable_$ => nil
  D is [.,E,:.] and isRectangularVector(u,n-1,m-1) =>
    LIST2VEC
      [LIST2VEC [objValUnwrap(coerceInt(objNewWrap(x.j,E),R))
         for j in 0..MAXINDEX(x:=u.i)] for i in 0..MAXINDEX u]
  coercionFailure()

V2Sm(u,[.,D],[.,n,R]) ==
  u = '_$fromCoerceable_$ => nil
  D is [.,E,:.] and isRectangularVector(u,n-1,n-1) =>
    LIST2VEC
      [LIST2VEC [objValUnwrap(coerceInt(objNewWrap(x.j,E),R))
         for j in 0..MAXINDEX(x:=u.i)] for i in 0..MAXINDEX u]
  coercionFailure()

isRectangularVector(x,p,q) ==
  MAXINDEX x = p =>
    and/[q=MAXINDEX x.i for i in 0..p]

-- Polynomial and Expression to Univariate series types

P2Uts(u, source, target) ==
  P2Us(u,source, target, 'taylor)

P2Uls(u, source, target) ==
  P2Us(u,source, target, 'laurent)

P2Upxs(u, source, target) ==
  P2Us(u,source, target, 'puiseux)

P2Us(u, source is [.,S], target is [.,T,var,cen], type) ==
  u = '_$fromCoerceable_$ =>
    -- might be able to say yes
    canCoerce(S,T)
  T isnt ['Expression, :.] => coercionFailure()
  if S ^= '(Float) then S := $Integer
  obj := objNewWrap(u, source)
  E := ['Expression, S]
  newU := coerceInt(obj, E)
  null newU => coercionFailure()
  EQtype := ['Equation, E]
  eqfun := getFunctionFromDomain('_=, EQtype, [E,E])
  varE := coerceInt(objNewWrap(var, '(Symbol)), E)
  null varE => coercionFailure()
  cenE := coerceInt(objNewWrap(cen, T), E)
  null cenE => coercionFailure()
  eq := SPADCALL(objValUnwrap(varE), objValUnwrap(cenE), eqfun)
  package := ['ExpressionToUnivariatePowerSeries, S, E]
  func := getFunctionFromDomain(type, package, [E, EQtype])
  newObj := SPADCALL(objValUnwrap(newU), eq, func)
  newType := CAR newObj
  newVal  := CDR newObj
  newType = target => newVal
  finalObj := coerceInt(objNewWrap(newVal, newType), target)
  null finalObj => coercionFailure()
  objValUnwrap finalObj


--% General Coercion Commutation Functions

-- general commutation functions are called with 5 values
--     u           object of type source
--     source      type of u
--     S           underdomain of source
--     target      coercion target type
--     T           underdomain of T
-- Because of checking, can always assume S and T have underdomains.

--% Complex

commuteComplex(u,source,S,target,T) ==
  u = '_$fromCoerceable_$ =>
    canCoerce(S,target) and canCoerce(T,target)
  [real,:imag] := u
  (real := coerceInt(objNewWrap(real,S),target)) or coercionFailure()
  (imag := coerceInt(objNewWrap(imag,S),target)) or coercionFailure()
  T' := underDomainOf T
  i := [domainZero(T'),
       :domainOne(T')]
  (i := coerceInt(objNewWrap(i,T),target)) or coercionFailure()
  f := getFunctionFromDomain("*",target,[target,target])
  i := SPADCALL(objValUnwrap i, objValUnwrap imag, f)
  f := getFunctionFromDomain("+",target,[target,target])
  SPADCALL(objValUnwrap real,i,f)

--% Quaternion

commuteQuaternion(u,source,S,target,T) ==
  u = '_$fromCoerceable_$ =>
    canCoerce(S,target) and canCoerce(T,target)
  c := [objValUnwrap(coerceInt(objNewWrap(x,S),target)
    or coercionFailure()) for x in VEC2LIST u]
  q := '(Quaternion (Integer))
  e := [[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]]
  e := [(coerceInt(objNewWrap(LIST2VEC x,q),T)
    or coercionFailure()) for x in e]
  e :=[objValUnwrap(coerceInt(x,target) or coercionFailure()) for x in e]
  u' := domainZero(target)
  mult := getFunctionFromDomain("*",target,[target,target])
  plus := getFunctionFromDomain("+",target,[target,target])
  for x in c for y in e repeat
    u' := SPADCALL(u',SPADCALL(x,y,mult),plus)
  u'

--% Fraction

commuteFraction(u,source,S,target,T) ==
  u = '_$fromCoerceable_$ =>
    ofCategory(target,'(Field)) => canCoerce(S,target)
    canCoerce(S,T) and canCoerce(T,target)
  [n,:d] := u
  ofCategory(target,'(Field)) =>
    -- see if denominator can go over to target
    (d' := coerceInt(objNewWrap(d,S),target)) or coercionFailure()
    -- if so, try to invert it
    inv := getFunctionFromDomain('inv,target,[target])
    d' := SPADCALL(objValUnwrap d',inv)
    -- now coerce to target
    (n' := coerceInt(objNewWrap(n,S),target)) or coercionFailure()
    multfunc := getFunctionFromDomain("*",target,[target,target])
    SPADCALL(d',objValUnwrap n',multfunc)
  -- see if denominator can go over to QF part of target
  (d' := coerceInt(objNewWrap(d,S),T)) or coercionFailure()
  -- if so, try to invert it
  inv := getFunctionFromDomain('inv,T,[T])
  d' := SPADCALL(objValUnwrap d',inv)
  -- now coerce to target
  (d' := coerceInt(objNewWrap(d',T),target)) or coercionFailure()
  (n' := coerceInt(objNewWrap(n,S),target)) or coercionFailure()
  multfunc := getFunctionFromDomain("*",target,[target,target])
  SPADCALL(objValUnwrap d',objValUnwrap n',multfunc)

--% SquareMatrix

commuteSquareMatrix(u,source,S,target,T) ==
  u = '_$fromCoerceable_$ =>
    canCoerce(S,target) and canCoerce(T,target)
  -- commuting matrices of matrices should be a no-op
  S is ['SquareMatrix,:.] =>
     source=target => u
     coercionFailure()
  u' := domainZero(target)
  plusfunc := getFunctionFromDomain("+",target,[target,target])
  multfunc := getFunctionFromDomain("*",target,[target,target])
  zero := domainZero(S)
  [sm,n,:.] := source
  S' := [sm,n,$Integer]
  for i in 0..(n-1) repeat
    for j in 0..(n-1) repeat
      (e := u.i.j) = zero => 'iterate
      (e' := coerceInt(objNewWrap(e,S),target)) or coercionFailure()
      (Eij := coerceInt(objNewWrap(makeEijSquareMatrix(i,j,n),S'),T)) or
        coercionFailure()
      (Eij := coerceInt(Eij,target)) or coercionFailure()
      e' := SPADCALL(objValUnwrap(e'),objValUnwrap(Eij),multfunc)
      u' := SPADCALL(e',u',plusfunc)
  u'

makeEijSquareMatrix(i, j, dim) ==
  -- assume using 0 based scale, makes a dim by dim matrix with a
  -- 1 in the i,j position, zeros elsewhere
  LIST2VEC [LIST2VEC [((i=r) and (j=c) => 1; 0)
    for c in 0..(dim-1)] for r in 0..(dim-1)]

--% Univariate Polynomial and Sparse Univariate Polynomial

commuteUnivariatePolynomial(u,source,S,target,T) ==
  commuteSparseUnivariatePolynomial(u,source,S,target,T)

commuteSparseUnivariatePolynomial(u,source,S,target,T) ==
  u = '_$fromCoerceable_$ =>
    canCoerce(S,target) and canCoerce(T,target)

  u' := domainZero(target)
  null u => u'

  T'  := underDomainOf T
  one := domainOne(T')
  monom := getFunctionFromDomain('monomial,T,[T',$NonNegativeInteger])
  plus  := getFunctionFromDomain("+",target,[target,target])
  times := getFunctionFromDomain("*",target,[target,target])

  for [e,:c] in u repeat
    (c := coerceInt(objNewWrap(c,S),target)) or coercionFailure()
    m := SPADCALL(one,e,monom)
    (m := coerceInt(objNewWrap(m,T),target)) or coercionFailure()
    c := objValUnwrap c
    m := objValUnwrap m
    u' := SPADCALL(u',SPADCALL(c,m,times),plus)
  u'

--% Multivariate Polynomials

commutePolynomial(u,source,S,target,T) ==
  commuteMPolyCat(u,source,S,target,T)

commuteMultivariatePolynomial(u,source,S,target,T) ==
  commuteMPolyCat(u,source,S,target,T)

commuteDistributedMultivariatePolynomial(u,source,S,target,T) ==
  commuteMPolyCat(u,source,S,target,T)

commuteNewDistributedMultivariatePolynomial(u,source,S,target,T) ==
  commuteMPolyCat(u,source,S,target,T)

commuteMPolyCat(u,source,S,target,T) ==
  u = '_$fromCoerceable_$ => canCoerce(S,target)
  -- check constant case
  isconstfun := getFunctionFromDomain("ground?",source,[source])
  SPADCALL(u,isconstfun) =>
    constfun := getFunctionFromDomain("ground",source,[source])
    c := SPADCALL(u,constfun)
    (u' := coerceInt(objNewWrap(c,S),target)) or coercionFailure()
    objValUnwrap(u')

  lmfun := getFunctionFromDomain('leadingMonomial,source,[source])
  lm := SPADCALL(u,lmfun)    -- has type source, is leading monom

  lcfun := getFunctionFromDomain('leadingCoefficient,source,[source])
  lc := SPADCALL(lm,lcfun)    -- has type S, is leading coef
  (lc' := coerceInt(objNewWrap(lc,S),target)) or coercionFailure()

  pmfun := getFunctionFromDomain('primitiveMonomials,source,[source])
  lm := first SPADCALL(lm,pmfun) -- now we have removed the leading coef
  (lm' := coerceInt(objNewWrap(lm,source),T)) or coercionFailure()
  (lm' := coerceInt(lm',target)) or coercionFailure()

  rdfun := getFunctionFromDomain('reductum,source,[source])
  rd := SPADCALL(u,rdfun)    -- has type source, is reductum
  (rd' := coerceInt(objNewWrap(rd,source),target)) or coercionFailure()

  lc' := objValUnwrap lc'
  lm' := objValUnwrap lm'
  rd' := objValUnwrap rd'

  plusfun := getFunctionFromDomain("+",target,[target,target])
  multfun := getFunctionFromDomain("*",target,[target,target])
  SPADCALL(SPADCALL(lc',lm',multfun),rd',plusfun)

------------------------------------------------------------------------
-- Format for alist member is:  domain  coercionType function
--   here coercionType can be one of 'total, 'partial or 'indeterm
--   (indeterminant - cannot tell in a simple way).
--
-- In terms of canCoerceFrom, 'total implies true, 'partial implies
--   false (just cannot tell without actual data) and 'indeterm means
--   to call the function with the data = "$fromCoerceable$" for a
--   response of true or false.
------------------------------------------------------------------------
-- There are no entries here for RationalNumber or RationalFunction.
-- These should have been changed to QF I and QF P, respectively, by
-- a function like deconstructTower.  RSS 8-1-85
------------------------------------------------------------------------

SETANDFILEQ($CoerceTable, '(                                          _
  (Complex . ( _
    (Expression                       indeterm   Complex2Expr) _
    (Factored                         indeterm   Complex2FR) _
    (Integer                          partial    Complex2underDomain) _
    (PrimeField                       partial    Complex2underDomain) _
    ))_
  (DirectProduct . (                                                  _
    (DirectProduct                        partial    DP2DP)           _
   ))                                                                 _
  (DistributedMultivariatePolynomial . (                              _
    (DistributedMultivariatePolynomial    indeterm   Dmp2Dmp) _
    (Expression                           indeterm   Dmp2Expr) _
    (Factored                             indeterm   Mp2FR) _
    (HomogeneousDistributedMultivariatePolynomial indeterm   Dmp2NDmp) _
    (MultivariatePolynomial               indeterm   Dmp2Mp) _
    (Polynomial                           indeterm   Dmp2P) _
    (UnivariatePolynomial                 indeterm   Dmp2Up) _
    ))_
  (Expression . (
    (Complex                                         partial    Expr2Complex) _
    (DistributedMultivariatePolynomial               indeterm   Expr2Dmp) _
    (HomogeneousDistributedMultivariatePolynomial    indeterm   Expr2Dmp) _
    (MultivariatePolynomial                          indeterm   Expr2Mp) _
    (UnivariateLaurentSeries                         indeterm   P2Uls) _
    (UnivariatePolynomial                            indeterm   Expr2Up) _
    (UnivariatePuiseuxSeries                         indeterm   P2Upxs) _
    (UnivariateTaylorSeries                          indeterm   P2Uts) _
    )) _

  (Kernel . ( _
    (Kernel                                          indeterm   Ker2Ker) _
    (Expression                                      indeterm   Ker2Expr) _
     )) _

  (Factored . ( _
    (Factored                             indeterm   Factored2Factored) _
    ))_
  (Fraction . ( _
    (DistributedMultivariatePolynomial    partial    Qf2domain) _
    (ElementaryFunction                   indeterm   Qf2EF) _
    (Expression                           indeterm   Qf2EF) _
    (Fraction                             indeterm   Qf2Qf) _
    (HomogeneousDistributedMultivariatePolynomial partial    Qf2domain) _
    (Integer                              partial    Qf2domain) _
    (MultivariatePolynomial               partial    Qf2domain) _
    (Polynomial                           partial    Qf2domain) _
    (PrimeField                           indeterm   Qf2PF) _
    (UnivariateLaurentSeries              indeterm   P2Uls) _
    (UnivariatePolynomial                 partial    Qf2domain) _
    (UnivariatePuiseuxSeries              indeterm   P2Upxs) _
    (UnivariateTaylorSeries               indeterm   P2Uts) _
    ))_
  (Int . ( _
    (Expression                           total      ncI2E) _
    (Integer                              total      ncI2I) _
  ))_
  (Baby . ( _
    (Expression                           total      ncI2E) _
    (Integer                              total      ncI2I) _
  ))_
  (Integer . ( _
    (Baby                                 total      I2ncI) _
    (EvenInteger                          partial    I2EI) _
    (Int                                  total      I2ncI) _
    (NonNegativeInteger                   partial    I2NNI) _
    (OddInteger                           partial    I2OI) _
    (PositiveInteger                      partial    I2PI) _
    ))_
  (List . ( _
    (DirectProduct                        indeterm   L2DP) _
    (Matrix                               partial    L2M) _
    (Record                               partial    L2Record) _
    (RectangularMatrix                    partial    L2Rm) _
    (Set                                  indeterm   L2Set) _
    (SquareMatrix                         partial    L2Sm) _
    (Stream                               indeterm   Agg2Agg) _
    (Tuple                                indeterm   L2Tuple) _
    (Vector                               indeterm   L2V) _
    ))_
  ))

SETANDFILEQ($CoerceTable,NCONC($CoerceTable,'( _
  (Matrix . ( _
    (List                                 indeterm   M2L) _
    (RectangularMatrix                    partial    M2Rm) _
    (SquareMatrix                         partial    M2Sm) _
    (Vector                               indeterm   M2L) _
    ))_
  (MultivariatePolynomial . ( _
    (DistributedMultivariatePolynomial    indeterm   Mp2Dmp) _
    (Expression                           indeterm   Mp2Expr) _
    (Factored                             indeterm   Mp2FR) _
    (HomogeneousDistributedMultivariatePolynomial indeterm   domain2NDmp) _
    (MultivariatePolynomial               indeterm   Mp2Mp) _
    (Polynomial                           indeterm   Mp2P) _
    (UnivariatePolynomial                 indeterm   Mp2Up) _
    ))_
  (HomogeneousDirectProduct . ( _
    (HomogeneousDirectProduct             indeterm   DP2DP) _
   ))_
  (HomogeneousDistributedMultivariatePolynomial . ( _
    (Complex                              indeterm   NDmp2domain) _
    (DistributedMultivariatePolynomial    indeterm   NDmp2domain) _
    (Expression                           indeterm   Dmp2Expr) _
    (Factored                             indeterm   Mp2FR) _
    (Fraction                             indeterm   NDmp2domain) _
    (HomogeneousDistributedMultivariatePolynomial indeterm   NDmp2NDmp) _
    (MultivariatePolynomial               indeterm   NDmp2domain) _
    (Polynomial                           indeterm   NDmp2domain) _
    (Quaternion                           indeterm   NDmp2domain) _
    (UnivariatePolynomial                 indeterm   NDmp2domain) _
    ))_
  (OrderedVariableList . ( _
    (DistributedMultivariatePolynomial    indeterm   OV2poly) _
    (HomogeneousDistributedMultivariatePolynomial indeterm   OV2poly) _
    (MultivariatePolynomial               indeterm   OV2poly) _
    (OrderedVariableList                  indeterm   OV2OV) _
    (Polynomial                           total      OV2P) _
    (Symbol                               total      OV2Sy) _
    (UnivariatePolynomial                 indeterm   OV2poly) _
    ))_
  (Polynomial . ( _
    (DistributedMultivariatePolynomial    indeterm   P2Dmp) _
    (Expression                           indeterm   P2Expr) _
    (Factored                             indeterm   P2FR) _
    (HomogeneousDistributedMultivariatePolynomial partial    domain2NDmp) _
    (MultivariatePolynomial               indeterm   P2Mp) _
    (UnivariateLaurentSeries              indeterm   P2Uls) _
    (UnivariatePolynomial                 indeterm   P2Up) _
    (UnivariatePuiseuxSeries              indeterm   P2Upxs) _
    (UnivariateTaylorSeries               indeterm   P2Uts) _
    ))_
  (Set . ( _
    (List                                 indeterm   Set2L) _
    (Vector                               indeterm   Agg2L2Agg) _
    ))_
  (RectangularMatrix . ( _
    (List                                 indeterm   Rm2L) _
    (Matrix                               indeterm   Rm2M) _
    (SquareMatrix                         indeterm   Rm2Sm) _
    (Vector                               indeterm   Rm2V) _
    ))_
  (SparseUnivariatePolynomial . ( _
    (UnivariatePolynomial                       indeterm   SUP2Up) _
    ))_
  (SquareMatrix . (
    -- ones for polys needed for M[2] P I -> P[x,y] M[2] P I, say
    (DistributedMultivariatePolynomial            partial    Sm2PolyType) _
    (HomogeneousDistributedMultivariatePolynomial partial    Sm2PolyType) _
    (List                                         indeterm   Sm2L) _
    (Matrix                                       indeterm   Sm2M) _
    (MultivariatePolynomial                       partial    Sm2PolyType) _
    (RectangularMatrix                            indeterm   Sm2Rm) _
    (UnivariatePolynomial                         indeterm   Sm2PolyType) _
    (Vector                                       indeterm   Sm2V) _
    ) ) _
  (Symbol . ( _
    (DistributedMultivariatePolynomial            indeterm   Sy2Dmp) _
    (HomogeneousDistributedMultivariatePolynomial indeterm   Sy2NDmp) _
    (MultivariatePolynomial                       indeterm   Sy2Mp) _
    (OrderedVariableList                          partial    Sy2OV) _
    (Polynomial                                   total      Sy2P) _
    (UnivariatePolynomial                         indeterm   Sy2Up) _
    (Variable                                     indeterm   Sy2Var) _
    ) ) _
  (UnivariatePolynomial . ( _
    (DistributedMultivariatePolynomial            indeterm   Up2Dmp) _
    (Expression                                   indeterm   Up2Expr) _
    (Factored                                     indeterm   Up2FR) _
    (HomogeneousDistributedMultivariatePolynomial indeterm   domain2NDmp) _
    (MultivariatePolynomial                       indeterm   Up2Mp) _
    (Polynomial                                   indeterm   Up2P) _
    (SparseUnivariatePolynomial                   indeterm   Up2SUP) _
    (UnivariatePolynomial                         indeterm   Up2Up) _
    ) ) _
  (Variable . ( _
    (AlgebraicFunction                            total      Var2FS) _
    (ContinuedFractionPowerSeries                 indeterm   Var2OtherPS) _
    (DistributedMultivariatePolynomial            indeterm   Var2Dmp) _
    (ElementaryFunction                           total      Var2FS) _
    (Fraction                                     indeterm   Var2QF) _
    (FunctionalExpression                         total      Var2FS) _
    (GeneralDistributedMultivariatePolynomial     indeterm   Var2Gdmp) _
    (HomogeneousDistributedMultivariatePolynomial indeterm   Var2NDmp) _
    (LiouvillianFunction                          total      Var2FS) _
    (MultivariatePolynomial                       indeterm   Var2Mp) _
    (OrderedVariableList                          indeterm   Var2OV) _
    (Polynomial                                   total      Var2P) _
    (SparseUnivariatePolynomial                   indeterm   Var2SUP) _
    (Symbol                                       total      Identity) _
    (UnivariatePolynomial                         indeterm   Var2Up) _
    (UnivariatePowerSeries                        indeterm   Var2UpS) _
    ) ) _
  (Vector . ( _
    (DirectProduct                        indeterm   V2DP) _
    (List                                 indeterm   V2L) _
    (Matrix                               indeterm   V2M) _
    (RectangularMatrix                    indeterm   V2Rm) _
    (Set                                  indeterm   Agg2L2Agg) _
    (SquareMatrix                         indeterm   V2Sm) _
    (Stream                               indeterm   Agg2Agg) _
    ) ) _
  ) ) )

-- this list is too long for the parser, so it has to be split into parts
-- specifies the commute functions
-- commute stands for partial commute function
--SETANDFILEQ($CommuteTable, '(                                           _
--  (DistributedMultivariatePolynomial . (                                _
--    (DistributedMultivariatePolynomial    commute    commuteMultPol)    _
--    (Complex                              commute    commuteMultPol)    _
--    (MultivariatePolynomial               commute    commuteMultPol)    _
--    (NewDistributedMultivariatePolynomial commute    commuteMultPol)    _
--    (Polynomial                           commute    commuteMultPol)    _
--    (Quaternion                           commute    commuteMultPol)    _
--    (Fraction                             commute    commuteMultPol)    _
--    (SquareMatrix                         commute    commuteMultPol)    _
--    (UnivariatePolynomial                 commute    commuteMultPol)    _
--    ))                                                                  _
--  (Complex . (                                                         _
--    (DistributedMultivariatePolynomial    commute    commuteG2)         _
--    (MultivariatePolynomial               commute    commuteG2)         _
--    (NewDistributedMultivariatePolynomial commute    commuteG2)         _
--    (Polynomial                           commute    commuteG1)         _
--    (Fraction                             commute    commuteG1)         _
--    (SquareMatrix                         commute    commuteG2)         _
--    (UnivariatePolynomial                 commute    commuteG2)         _
--    ))                                                                  _
--  (MultivariatePolynomial . (                                           _
--    (DistributedMultivariatePolynomial    commute    commuteMultPol)    _
--    (Complex                             commute    commuteMultPol)    _
--    (MultivariatePolynomial               commute    commuteMultPol)    _
--    (NewDistributedMultivariatePolynomial commute    commuteMultPol)    _
--    (Polynomial                           commute    commuteMultPol)    _
--    (Quaternion                           commute    commuteMultPol)    _
--    (Fraction                        commute    commuteMultPol)    _
--    (SquareMatrix                         commute    commuteMultPol)    _
--    (UnivariatePolynomial                       commute    commuteMultPol)    _
--    ))                                                                  _
--  (Polynomial . (                                                       _
--    (DistributedMultivariatePolynomial    commute    commuteMultPol)    _
--    (Complex                              commute    commuteMultPol)    _
--    (MultivariatePolynomial               commute    commuteMultPol)    _
--    (NewDistributedMultivariatePolynomial commute    commuteMultPol)    _
--    (Polynomial                           commute    commuteMultPol)    _
--    (Quaternion                           commute    commuteMultPol)    _
--    (Fraction                             commute    commuteMultPol)    _
--    (SquareMatrix                         commute    commuteMultPol)    _
--    (UnivariatePolynomial                 commute    commuteMultPol)    _
--    ))                                                                  _
--  (Quaternion . (                                                       _
--    (DistributedMultivariatePolynomial    commute    commuteQuat2)      _
--    (MultivariatePolynomial               commute    commuteQuat2)      _
--    (NewDistributedMultivariatePolynomial commute    commuteQuat2)      _
--    (Polynomial                           commute    commuteQuat1)      _
--    (SquareMatrix                         commute    commuteQuat2)      _
--    (UnivariatePolynomial                       commute    commuteQuat2)      _
--    ))                                                                  _
--  (SquareMatrix . (                                                     _
--    (DistributedMultivariatePolynomial    commute    commuteSm2)        _
--    (Complex                             commute    commuteSm1)        _
--    (MultivariatePolynomial               commute    commuteSm2)        _
--    (NewDistributedMultivariatePolynomial commute    commuteSm2)        _
--    (Polynomial                           commute    commuteSm1)        _
--    (Quaternion                           commute    commuteSm1)        _
--    (SparseUnivariatePolynomial           commute    commuteSm1)        _
--    (UnivariatePolynomial                 commute    commuteSm2)        _
--    ))                                                                  _
--  (UnivariatePolynomial . (                                                   _
--    (DistributedMultivariatePolynomial    commute    commuteUp2)        _
--    (Complex                              commute    commuteUp1)        _
--    (MultivariatePolynomial               commute    commuteUp2)        _
--    (NewDistributedMultivariatePolynomial commute    commuteUp2)        _
--    (Polynomial                           commute    commuteUp1)        _
--    (Quaternion                           commute    commuteUp1)        _
--    (Fraction                        commute    commuteUp1)        _
--    (SparseUnivariatePolynomial           commute    commuteUp1)        _
--    (SquareMatrix                         commute    commuteUp2)        _
--    (UnivariatePolynomial                 commute    commuteUp2)        _
--    ))                                                                  _
--  ))

SETANDFILEQ($CommuteTable, '(                                           _
  (Complex . (                                                         _
    (DistributedMultivariatePolynomial    commute    commuteG2)         _
    (MultivariatePolynomial               commute    commuteG2)         _
    (HomogeneousDistributedMultivariatePolynomial commute    commuteG2) _
    (Polynomial                           commute    commuteG1)         _
    (Fraction                             commute    commuteG1)         _
    (SquareMatrix                         commute    commuteG2)         _
    (UnivariatePolynomial                 commute    commuteG2)         _
    ))                                                                  _
  (Polynomial . (                                                       _
    (Complex                              commute    commuteMultPol)    _
    (MultivariatePolynomial               commute    commuteMultPol)    _
    (HomogeneousDistributedMultivariatePolynomial commute    commuteMultPol)_
    (Polynomial                           commute    commuteMultPol)    _
    (Quaternion                           commute    commuteMultPol)    _
    (Fraction                             commute    commuteMultPol)    _
    (SquareMatrix                         commute    commuteMultPol)    _
    (UnivariatePolynomial                 commute    commuteMultPol)    _
    ))                                                                  _
  (SquareMatrix . (                                                     _
    (DistributedMultivariatePolynomial    commute    commuteSm2)        _
    (Complex                              commute    commuteSm1)        _
    (MultivariatePolynomial               commute    commuteSm2)        _
    (HomogeneousDistributedMultivariatePolynomial commute    commuteSm2)_
    (Polynomial                           commute    commuteSm1)        _
    (Quaternion                           commute    commuteSm1)        _
    (SparseUnivariatePolynomial           commute    commuteSm1)        _
    (UnivariatePolynomial                 commute    commuteSm2)        _
    ))                                                                  _
  ))

@
\eject
\begin{thebibliography}{99}
\bibitem{1} nothing
\end{thebibliography}
\end{document}