-- Copyright (c) 1991-2002, The Numerical ALgorithms Group Ltd.
-- All rights reserved.
-- Copyright (C) 2007-2011, Gabriel Dos Reis.
-- 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.


import i_-object
namespace BOOT

resolveTypeList u ==
  u is [a,:tail] =>

    -- if the list consists entirely of variables then keep it explicit
    allVars :=
      a is ['Variable,v] => [v]
      nil
    while allVars for b in tail repeat
        allVars :=
            b is ['Variable,v] => insert(v, allVars)
            nil
    allVars =>
        null rest allVars => ['Variable, first allVars]
        ['OrderedVariableList,reverse! allVars]

    for md in tail repeat
      a := resolveTT(md,a)
      null a => return nil
    a
  throwKeyedMsg("S2IR0002",nil)

-- resolveTT is in CLAMMED BOOT

resolveTypeListAny tl ==
  rt := resolveTypeList tl
  null rt => $Any
  rt

resolveTTAny(t1,t2) ==
  (t3 := resolveTT(t1, t2)) => t3
  $Any

resolveTT1(t1,t2) ==
  -- this is the main symmetric resolve
  -- first it looks for equal constructors on both sides
  -- then it tries to use a rewrite rule
  -- and finally it builds up a tower
  t1=t2 => t1
  (t1 = '$NoValueMode) or (t2 = '$NoValueMode) => nil
  (t1 = $Void) or (t2 = $Void) => $Void
  (t1 = $Any) or (t2 = $Any) => $Any
  t1 = $Exit => t2
  t2 = $Exit => t1
  t1 is ['Union,:.] => resolveTTUnion(t1,t2)
  t2 is ['Union,:.] => resolveTTUnion(t2,t1)
  string?(t1) =>
    t2 = $String => t2
    nil
  string?(t2) =>
    t1 = $String => t1
    nil
  null acceptableTypesToResolve(t1,t2) => nil
  if compareTT(t1,t2) then
     t := t1
     t1 := t2
     t2 := t
  (t := resolveTTSpecial(t1,t2)) and isValidType t => t
  (t := resolveTTSpecial(t2,t1)) and isValidType t => t
  isSubTowerOf(t1,t2) and canCoerceFrom(t1,t2) => t2
  isSubTowerOf(t2,t1) and canCoerceFrom(t2,t1) => t1
  t := resolveTTRed(t1,t2) => t
  t := resolveTTCC(t1,t2) => t
  (t := resolveTTEq(t1,t2)) and isValidType t => t
  [c1,:arg1] := deconstructT t1
  arg1 and
    [c2,:arg2] := deconstructT t2
    arg2 and
      t := resolveTT1(last arg1,last arg2)
      t and ( resolveTT2(c1,c2,arg1,arg2,t) or
        resolveTT2(c2,c1,arg2,arg1,t) )

acceptableTypesToResolve(t1,t2) ==
  -- this is temporary. It ensures that two types that have coerces
  -- that really should be converts don't automatically resolve.
  -- when the coerces go away, so will this.
  acceptableTypesToResolve1(t1,t2) and
    acceptableTypesToResolve1(t2,t1)

acceptableTypesToResolve1(t1,t2) ==
  t1 = $Integer =>
    t2 = $String => nil
    true
  t1 = $DoubleFloat or t1 = $Float =>
    t2 = $String => nil
    t2 = '(RationalNumber) => nil
    t2 = [$QuotientField, $Integer] => nil
    true
  true

resolveTT2(c1,c2,arg1,arg2,t) ==
  -- builds a tower and tests for all the necessary coercions
  t0 := constructM(c2,replaceLast(arg2,t))
  canCoerceFrom(t,t0) and
    t1 := constructM(c1,replaceLast(arg1,t0))
    canCoerceFrom(t0,t1) and t1

resolveTTUnion(t1 is ['Union,:doms],t2) ==
  unionDoms1 :=
    doms and first doms is [":",:.] =>
      tagged := true
      [t for [.,.,t] in doms]
    tagged := false
    doms
  member(t2,unionDoms1) => t1
  tagged => nil
  t2 isnt ['Union,:doms2] =>
    ud := nil
    bad := nil
    for d in doms while not bad repeat
      d is '"failed" => ud := [d,:ud]
      null (d' := resolveTT(d,t2)) => bad := true
      ud := [d',:ud]
    bad => nil
    ['Union,:removeDuplicates reverse ud]
  ud := nil
  bad := nil
  for d in doms2 while not bad repeat
    d is '"failed" => ud := append(ud,[d])
    null (d' := resolveTTUnion(t1,d)) => bad := true
    ud := append(ud,rest d')
  bad => nil
  ['Union,:removeDuplicates ud]

resolveTTSpecial(t1,t2) ==
  -- tries to resolve things that would otherwise get mangled in the
  -- rest of the resolve world. I'll leave it for Albi to fix those
  -- things. (RSS 1/-86)

  -- following is just an efficiency hack
  (t1 = $Symbol or t1 is ['OrderedVariableList,.]) and cons?(t2) and
    first(t2) in '(Polynomial RationalFunction) => t2

  (t1 = $Symbol) and ofCategory(t2, '(IntegerNumberSystem)) =>
    resolveTT1(['Polynomial, t2], t2)

  t1 = $AlgebraicNumber and (t2 = $Float or t2 = $DoubleFloat) =>
    ['Expression, t2]
  t1 = $AlgebraicNumber and (t2 = ['Complex, $Float] or t2 = ['Complex, $DoubleFloat]) =>
    ['Expression, second t2]

  t1 = $AlgebraicNumber and t2 is ['Complex,.] =>
    resolveTT1('(Expression (Integer)), t2)

  t1 is ['SimpleAlgebraicExtension,F,Rep,poly] =>
    t2 = Rep => t1
    t2 is ['UnivariatePolynomial,x,R] and (t3 := resolveTT(t1, R)) =>
      ['UnivariatePolynomial,x,t3]
    t2 is ['Variable,x] and (t3 := resolveTT(t1, F)) =>
      ['UnivariatePolynomial,x,t3]
    t2 is ['Polynomial,R] and (R' := resolveTT(Rep, t2)) =>
      R' = Rep => t1
      ['Polynomial,t1]
    canCoerceFrom(t2,F) => t1
    nil
  t1 = $PositiveInteger and ofCategory(t2,$Ring) =>
    resolveTT1($Integer,t2)
  t1 = $NonNegativeInteger and ofCategory(t2,$Ring) =>
    resolveTT1($Integer,t2)
  t1 is ['OrderedVariableList,[x]] => resolveTTSpecial(['Variable, x], t2)
  t1 is ['OrderedVariableList,vl] =>
    ofCategory(t2,$Ring) => resolveTT(['Polynomial,$Integer],t2)
    resolveTT($Symbol,t2)
  t1 is ['Variable,x] =>
    t2 is ["SimpleAlgebraicExtension",:.] => resolveTTSpecial(t2,t1)
    t2 is ['UnivariatePolynomial,y,S] =>
      x = y => t2
      resolveTT1(['UnivariatePolynomial,x,$Integer],t2)
    t2 is ['Variable,y] =>
      x = y => t1
--    ['OrderedVariableList, MSORT [x,y]]
      $Symbol
    t2 = $Symbol => t2
    t2 is ['Polynomial,.] => t2
    t2 is ['OrderedVariableList, vl] and member(x,vl) => t2
    isPolynomialMode t2 => nil
    ofCategory(t2, '(IntegerNumberSystem)) => resolveTT(['Polynomial, t2], t2)
    resolveTT(['Polynomial,$Integer],t2)
  t1 is ['FunctionCalled,f] and t2 is ['FunctionCalled,g] =>
    null (mf := get(f,'mode,$e)) => nil
    null (mg := get(g,'mode,$e)) => nil
    mf ~= mg => nil
    mf
  t1 is ['UnivariatePolynomial,x,S] =>
    t2 is ["Variable",:.] => resolveTTSpecial(t2,t1)
    t2 is ["SimpleAlgebraicExtension",:.] => resolveTTSpecial(t2,t1)
    t2 is ['UnivariatePolynomial,y,T] =>
      (x = y) and (U := resolveTT1(S,T)) and ['UnivariatePolynomial,x,U]
    nil
  t1 = '(Pi) =>
    t2 is ['Complex,d] => defaultTargetFE t2
    t2 is ['AlgebraicNumber] => defaultTargetFE t2
    t2 is ["Variable",:.] or t2 = $Symbol => defaultTargetFE($Symbol)
    t2 is ['Polynomial, .] or t2 is ['Fraction, ['Polynomial, .]] =>
      defaultTargetFE(t2)
    nil
  t1 is ['Polynomial,['Complex,u1]] and t2 is ['Complex,u2] =>
    resolveTT1(t1,u2)
  t1 is ['Polynomial,R] and t2 is ['Complex,S] =>
    containsPolynomial(S) => resolveTT1(['Polynomial,['Complex,R]],t2)
    ['Polynomial,['Complex,resolveTT1(R,S)]]
  t1 is ['Expression, R] and t2 is ['Complex,S] =>
    dom' := resolveTT(R, t2)
    null dom' => nil
    ['Expression, dom']
  t1 is ['Segment, dom] and t2 isnt ['Segment,.] =>
    dom' := resolveTT(dom, t2)
    null dom' => nil
    ['Segment, dom']
  member(t1,[$Domain,$Category]) and t2 = $Type => t2
  t1 = $Domain and t2 = $Category => $Type
  nil

resolveTTCC(t1,t2) ==
  -- tries to use canCoerceFrom information to see if types can be
  -- coerced to one another
  gt21 := GGREATERP(t2,t1)
  (c12 := canCoerceFrom(t1,t2)) and gt21 => t2
  c21 := canCoerceFrom(t2,t1)
  null (c12 or c21) => nil
  c12 and not c21 => t2
  c21 and not c12 => t1
  -- both are coerceable to each other
  if gt21 then t1 else t2

resolveTTEq(t1,t2) ==
  -- tries to find the constructor of t1 somewhere in t2 (or vice versa)
  -- and move the other guy to the top
  [c1,:arg1] := deconstructT t1
  [c2,:arg2] := deconstructT t2
  t := resolveTTEq1(c1,arg1,[c2,arg2]) => t
  t := ( arg1 and resolveTTEq2(c2,arg2,[c1,arg1]) ) => t
  arg2 and resolveTTEq2(c1,arg1,[c2,arg2])

resolveTTEq1(c1,arg1,TL is [c2,arg2,:.]) ==
  -- takes care of basic types and of types with the same constructor
  -- calls resolveTT1 on the arguments in the second case
  null arg1 and null arg2 =>
    canCoerceFrom(c1,c2) => constructTowerT(c2,CDDR TL)
    canCoerceFrom(c2,c1) and constructTowerT(c1,CDDR TL)
  c1=c2 and
    [c2,arg2,:TL] := bubbleType TL
    until null arg1 or null arg2 or not t repeat
      t := resolveTT1(first arg1,first arg2) =>
        arg := [t,:arg]
        arg1 := rest arg1
        arg2 := rest arg2
    t and null arg1 and null arg2 and
      t0 := constructM(c1,reverse! arg)
      constructTowerT(t0,TL)

resolveTTEq2(c1,arg1,TL is [c,arg,:.]) ==
  -- tries to resolveTTEq the type [c1,arg1] with the last argument
  -- of the type represented by TL
  [c2,:arg2] := deconstructT last arg
  TL := [c2,arg2,:TL]
  t := resolveTTEq1(c1,arg1,TL) => t
  arg2 and resolveTTEq2(c1,arg1,TL)

resolveTTRed(t1,t2) ==
  -- the same function as resolveTTEq, but instead of testing for
  -- constructor equality, it looks whether a rewrite rule can be applied
  t := resolveTTRed1(t1,t2,nil) => t
  [c1,:arg1] := deconstructT t1
  t := arg1 and resolveTTRed2(t2,last arg1,[c1,arg1]) => t
  [c2,:arg2] := deconstructT t2
  arg2 and resolveTTRed2(t1,last arg2,[c2,arg2])

resolveTTRed1(t1,t2,TL) ==
  -- tries to apply a reduction rule on (Resolve t1 t2)
  -- then it creates a type using the result and TL
  sameObject?(t,term1RW(t := ['Resolve,t1,t2],$Res)) and
    sameObject?(t,term1RW(t := ['Resolve,t2,t1],$Res)) => nil
  [c2,:arg2] := deconstructT t2
  [c2,arg2,:TL] := bubbleType [c2,arg2,:TL]
  t2 := constructM(c2,arg2)
  l := term1RWall(['Resolve,t1,t2],$Res)
  for t0 in l until t repeat t := resolveTTRed3 t0
  l and t => constructTowerT(t,TL)
  l := term1RWall(['Resolve,t2,t1],$Res)
  for t0 in l until t repeat t := resolveTTRed3 t0
  l and t and constructTowerT(t,TL)

resolveTTRed2(t1,t2,TL) ==
  -- tries to resolveTTRed t1 and t2 and build a type using TL
  t := resolveTTRed1(t1,t2,TL) => t
  [c2,:arg2] := deconstructT t2
  arg2 and resolveTTRed2(t1,last arg2,[c2,arg2,:TL])

resolveTTRed3(t) ==
  -- recursive resolveTTRed which handles all subterms of the form
  -- (Resolve t1 t2) or subterms which have to be interpreted
  t isnt [.,:.] => t
  t is ['Resolve,a,b] =>
    ( t1 := resolveTTRed3 a ) and ( t2 := resolveTTRed3 b ) and
      resolveTT1(t1,t2)
  t is ['Incl,a,b] => member(a,b) and b
  t is ['SetDiff,a,b] => intersection(a,b) and SETDIFFERENCE(a,b)
  t is ['SetComp,a,b] =>
    and/[member(x,a) for x in b] and SETDIFFERENCE(a,b)
  t is ['SetInter,a,b] => intersection(a,b)
  t is ['SetUnion,a,b] => union(a,b)
  t is ['VarEqual,a,b] => (a = b) and a
  t is ['SetEqual,a,b] =>
    (and/[member(x,a) for x in b] and "and"/[member(x,b) for x in a]) and a
  [(x isnt [.,:.] and x ) or ((not cs and x and not interpOp? x and x)
    or resolveTTRed3 x) or return nil
      for x in t for cs in getDualSignature t.op ]

interpOp?(op) ==
  cons?(op) and
    first(op) in '(Incl SetDiff SetComp SetInter SetUnion VarEqual SetEqual)

--% Resolve Type with Category

resolveTCat(t,c) ==
  -- this function attempts to find a type tc of category c such that
  -- t can be coerced to tc. nil returned for failure.
  -- Example:  t = Integer, c = Field ==> tc = RationalNumber

  -- first check whether t already belongs to c
  ofCategory(t,c) => t

  -- if t is built by a parametrized constructor and there is a
  -- condition on the parameter that matches the category, try to
  -- recurse. An example of this is (G I, Field) -> G RN

  rest(t) and (tc := resolveTCat1(t,c)) => tc

  -- now check some specific niladic categories
  member(c,'((Field) (EuclideanDomain))) and ofCategory(t,'(IntegralDomain))=>
    eqType [$QuotientField, t]

  c = $Field and t = $Symbol => ['RationalFunction,$Integer]

  c = $Ring and t is ['FactoredForm,t0] => ['FactoredRing,t0]

  sd := superType t => resolveTCat(sd,c)

  #(td := deconstructT t) ~= 2=> nil
  #(tc := deconstructT c) ~= 2 => nil
  ut := underDomainOf t
  null isValidType(uc := last tc) => nil
  null canCoerceFrom(ut,uc) => nil
  nt := constructT(first td,[uc])
  ofCategory(nt,c) => nt
  nil

resolveTCat1(t,c) ==
  -- does the hard work of looking at conditions on under domains
  -- if null (ut := getUnderModeOf(t)) then ut := last dt
  null (conds := getConditionsForCategoryOnType(t,c)) => nil
--rest(conds) => nil   -- will handle later
  cond := first conds
  cond isnt [.,["has", pat, c1],:.] => nil
  rest(c1) => nil      -- make it simple

  argN := 0
  t1 := nil

  for ut in rest t for i in 1.. while (argN = 0) repeat
    sharp := INTERNL('"#",STRINGIMAGE i)
    sharp = pat =>
      argN := i
      t1 := ut

  null t1 => nil
  null (t1' := resolveTCat(t1,c1)) => nil
  t' := copy t
  t'.argN := t1'
  t'

getConditionsForCategoryOnType(t,cat) ==
  getConditionalCategoryOfType(t,[nil],['ATTRIBUTE,cat])

getConditionalCategoryOfType(t,conditions,match) ==
  if cons? t then t := first t
  t in '(Union Mapping Record) => nil
  conCat := getConstructorCategory t
  removeDuplicates rest getConditionalCategoryOfType1(conCat,conditions,match,[nil])

getConditionalCategoryOfType1(cat,conditions,match,seen) ==
  cat is ['Join,:cs] or cat is ['CATEGORY,:cs] =>
    null cs => conditions
    getConditionalCategoryOfType1([first cat,:rest cs],
     getConditionalCategoryOfType1(first cs,conditions,match,seen),
       match,seen)
  cat is ['IF,., cond,.] =>
    matchUpToPatternVars(cond,match,nil) =>
      conditions.rest := [cat,:rest conditions]
      conditions
    conditions
  cat is [catName,:.] and (getConstructorKindFromDB catName = "category") =>
    member(cat, rest seen) => conditions
    seen.rest := [cat,:rest seen]
    subCat := getConstructorCategory catName
    -- substitute vars of cat into category
    for v in rest cat for vv in $TriangleVariableList repeat
      subCat := substitute(v,vv,subCat)
    getConditionalCategoryOfType1(subCat,conditions,match,seen)
  conditions

matchUpToPatternVars(pat,form,patAlist) ==
  -- tries to match pattern variables (of the # form) in pat
  -- against expressions in form. If one is found, it is checked
  -- against the patAlist to make sure we are using the same expression
  -- each time.
  pat = form => true
  isSharpVarWithNum(pat) =>
    -- see is pattern variable is in alist
    (p := assoc(pat,patAlist)) => form = rest p
    patAlist := [[pat,:form],:patAlist]
    true
  cons?(pat) =>
    form isnt [.,:.] => nil
    matchUpToPatternVars(first pat, first form,patAlist) and
      matchUpToPatternVars(rest pat, rest form,patAlist)
  nil

--% Resolve Type with Mode

-- only implemented for nullary control-L's (which stand for types)

resolveTMOrCroak(t,m) ==
  resolveTM(t,m) or throwKeyedMsg("S2IR0004",[t,m])

resolveTM(t,m) ==
  -- resolves a type with a mode which may be partially specified
  startTimingProcess 'resolve
  $Subst : local := nil
  $Coerce : local := 'T
  t := eqType t
  m := eqType substitute("**",$EmptyMode,m)
  tt := resolveTM1(t,m)
  result := tt and isValidType tt and eqType tt
  stopTimingProcess 'resolve
  result

resolveTM1(t,m) ==
  -- general resolveTM, which looks for a term variable
  -- otherwise it looks whether the type has the same top level
  -- constructor as the mode, looks for a rewrite rule, or builds up
  -- a tower
  t=m => t
  m is ['Union,:.] => resolveTMUnion(t,m)
  m = $Void => m
  m = $Any => m
  m = $Exit => t
  containsVars m =>
    isPatternVar m =>
      p := objectAssoc(m,$Subst) =>
        $Coerce =>
          tt := resolveTT1(t,rest p) => (p.rest := tt) and tt
          nil
        t=rest p and t
      $Subst := [[m,:t],:$Subst]
      t
    t isnt [.,:.] or m isnt [.,:.] => nil
    (t is ['Record,:tr]) and (m is ['Record,:mr]) and
      (tt := resolveTMRecord(tr,mr)) => tt
    t is ['Record,:.] or m is ['Record,:.] => nil
    t is ['Variable, .] and m is ['Mapping, :.] => m
    t is ['FunctionCalled, .] and m is ['Mapping, :.] => m
    if isEqualOrSubDomain(t, $Integer) then
      t := $Integer
    tt := resolveTMEq(t,m) => tt
    $Coerce and
      tt := resolveTMRed(t,m) => tt
      resolveTM2(t,m)
  $Coerce and canCoerceFrom(t,m) and m

resolveTMRecord(tr,mr) ==
  #tr ~= #mr => nil
  ok := true
  tt := nil
  for ta in tr for ma in mr while ok repeat
    -- element is [':,tag,mode]
    second(ta) ~= second(ma) => ok := nil      -- match tags
    ra := resolveTM1(third ta, third ma)   -- resolve modes
    null ra => ok := nil
    tt := [[first ta,second ta,ra],:tt]
  not ok => nil
  ['Record,reverse! tt]

resolveTMUnion(t, m is ['Union,:ums]) ==
  isTaggedUnion m => resolveTMTaggedUnion(t,m)
  -- resolves t with a Union type
  t isnt ['Union,:uts] =>
    ums := removeDuplicates spliceTypeListForEmptyMode([t],ums)
    ums' := nil
    success := nil
    for um in ums repeat
      (um' := resolveTM1(t,um)) =>
        success := true
        um' in '(T TRUE) => ums' := [um,:ums']
        ums' := [um',:ums']
      ums' := [um,:ums']
    -- remove any duplicate domains that might have been created
    m' := ['Union,:removeDuplicates reverse ums']
    success =>
      null CONTAINED('_*_*,m') => m'
      t = $Integer => nil
      resolveTM1($Integer,m')
    nil
  -- t is actually a Union if we got here
  ums := removeDuplicates spliceTypeListForEmptyMode(uts,ums)
  bad := nil
  doms := nil
  for ut in uts while not bad repeat
    (m' := resolveTMUnion(ut,['Union,:ums])) =>
      doms := append(rest m',doms)
    bad := true
  bad => nil
  ['Union,:removeDuplicates doms]

resolveTMTaggedUnion(t, m is ['Union,:ums]) ==
  nil

spliceTypeListForEmptyMode(tl,ml) ==
  -- splice in tl for occurrence of ** in ml
  null ml => nil
  ml is [m,:ml'] =>
    m = "**" => append(tl,spliceTypeListForEmptyMode(tl,ml'))
    [m,:spliceTypeListForEmptyMode(tl,ml')]

resolveTM2(t,m) ==
  -- resolves t with the last argument of m and builds up a tower
  [cm,:argm] := deconstructT m
  argm and
    tt := resolveTM1(t,last argm)
    tt and
      ttt := constructM(cm,replaceLast(argm,tt))
      ttt and canCoerceFrom(tt,ttt) and ttt

resolveTMEq(t,m) ==
  -- tests whether t and m have the same top level constructor, which,
  -- in the case of t, could be bubbled up
  (res := resolveTMSpecial(t,m)) => res
  [cm,:argm] := deconstructT m
  c := containsVars cm
  TL := nil
  until b or not t repeat
    [ct,:argt] := deconstructT t
    b :=
      c =>
        SL := resolveTMEq1(ct,cm)
        SL ~= 'failed
      ct=cm
    not b =>
      TL := [ct,argt,:TL]
      t := argt and last argt
  b and
    t := resolveTMEq2(cm,argm,[ct,argt,:TL])
    if t then for p in SL repeat $Subst := augmentSub(first p,rest p,$Subst)
    t

resolveTMSpecial(t,m) ==
  -- a few special cases
  t = $AnonymousFunction and m is ['Mapping,:.] => m
  t is ['Variable,x] and m is ['OrderedVariableList,le] =>
    isPatternVar le => ['OrderedVariableList,[x]]
    cons?(le) and member(x,le) => le
    nil
  t is ['Fraction, ['Complex, t1]] and m is ['Complex, m1] =>
    resolveTM1(['Complex, ['Fraction, t1]], m)
  t is ['Fraction, ['Polynomial, ['Complex, t1]]] and m is ['Complex, m1] =>
    resolveTM1(['Complex, ['Fraction, ['Polynomial, t1]]], m)
  t is ['Mapping,:lt] and m is ['Mapping,:lm] =>
    #lt ~= #lm => nil
    l := nil
    ok := true
    for at in lt for am in lm while ok repeat
      (ok := resolveTM1(at,am)) => l := [ok,:l]
    ok and ['Mapping,:reverse l]
  t is ['Segment,u] and m is ['UniversalSegment,.] =>
    resolveTM1(['UniversalSegment, u], m)
  nil

resolveTMEq1(ct,cm) ==
  -- ct and cm are type constructors
  -- tests for a match from cm to ct
  -- the result is a substitution or 'failed
  not (first ct=first cm) => 'failed
  SL := nil
  ct := rest ct
  cm := rest cm
  b := 'T
  while ct and cm and b repeat
    xt := first ct
    ct := rest ct
    xm := first cm
    cm := rest cm
    if cons? xm and first xm = ":"  --  i.e. Record
      and first xt = ":" and second xm = second xt then
        xm := third xm
        xt := third xt
    b :=
      xt=xm => 'T
      isPatternVar(xm) and
        p := objectAssoc(xm,$Subst) => xt=rest p
        p := objectAssoc(xm,SL) => xt=rest p
        SL := augmentSub(xm,xt,SL)
  b => SL
  'failed

resolveTMEq2(cm,argm,TL) ==
  -- [cm,argm] is a deconstructed mode,
  -- TL is a deconstructed type t
  [ct,argt,:TL] :=
    $Coerce => bubbleType TL
    TL
  null TL and
    null argm => constructM(ct,argt)
--  null argm => nil
    arg := nil
    while argt and argm until not tt repeat
      x1 := first argt
      argt := rest argt
      x2 := first argm
      argm := rest argm
      tt := resolveTM1(x1,x2) =>
        arg := [tt,:arg]
    null argt and null argm and tt and constructM(ct,reverse! arg)

resolveTMRed(t,m) ==
  -- looks for an applicable rewrite rule at any level of t and tries
  --   to bubble this constructor up to the top to t
  TL := nil
  until b or not t repeat
    [ct,:argt] := deconstructT t
    b := not sameObject?(t,term1RW(['Resolve,t,m],$ResMode)) and
      [c0,arg0,:TL0] := bubbleType [ct,argt,:TL]
      null TL0 and
        l := term1RWall(['Resolve,constructM(c0,arg0),m],$ResMode)
        for t0 in l until t repeat t := resolveTMRed1 t0
        l and t
    b or
      TL := [ct,argt,:TL]
      t := argt and last argt
  b and t

resolveTMRed1(t) ==
  -- recursive resolveTMRed which handles all subterms of the form
  -- (Resolve a b)
  t isnt [.,:.] => t
  t is ['Resolve,a,b] =>
    ( a := resolveTMRed1 a ) and ( b := resolveTMRed1 b ) and
      resolveTM1(a,b)
  t is ['Incl,a,b] => cons? b and member(a,b) and b
  t is ['Diff,a,b] => cons? a and member(b,a) and SETDIFFERENCE(a,[b])
  t is ['SetIncl,a,b] => cons? b and "and"/[member(x,b) for x in a] and b
  t is ['SetDiff,a,b] => cons? b and cons? b and
                         intersection(a,b) and SETDIFFERENCE(a,b)
  t is ['VarEqual,a,b] => (a = b) and b
  t is ['SetComp,a,b] => cons? a and cons? b and
    "and"/[member(x,a) for x in b] and SETDIFFERENCE(a,b)
  t is ['SimpleAlgebraicExtension,a,b,p] =>  -- this is a hack. RSS
    ['SimpleAlgebraicExtension, resolveTMRed1 a, resolveTMRed1 b,p]
  [(x isnt [.,:.] and x ) or resolveTMRed1 x or return nil for x in t]

--% Type and Mode Representation

eqType(t) ==
  -- looks for an equivalent but more simple type
  -- eg, eqType QF I = RN
  -- the new algebra orginization no longer uses these sorts of types
--  termRW(t,$TypeEQ)
  t

equiType(t) ==
  -- looks for an equivalent but expanded type
  -- eg, equiType RN == QF I
  -- the new algebra orginization no longer uses these sorts of types
--  termRW(t,$TypeEqui)
  t

getUnderModeOf d ==
  not cons? d => nil
--  n := LASSOC(first d,$underDomainAlist) => d.n ----> $underDomainAlist NOW always nil
  for a in rest d for m in rest destructT d repeat
    if m then return a

--deconstructM(t) ==
--  -- M is a type, which may contain type variables
--  -- results in a pair (type constructor . mode arguments)
--  rest t and constructor? first t =>
--    dt := destructT first t
--    args := [ x for d in dt for y in t | ( x := d and y ) ]
--    c := [ x for d in dt for y in t | ( x := not d and y ) ]
--    [c,:args]
--  [t,:nil]

deconstructT(t) ==
  -- M is a type, which may contain type variables
  -- results in a pair (type constructor . mode arguments)
  KDR t and (op := first t) and ident? op and constructor? op =>
    dt := destructT op
    args := [ x for d in dt for y in t | ( x := d and y ) ]
    c := [ x for d in dt for y in t | ( x := not d and y ) ]
    [c,:args]
  [t,:nil]

constructT(c,A) ==
  -- c is a type constructor, A a list of argument types
  A => [if d then POP A else POP c for d in destructT first c]
  c

constructM(c,A) ==
  -- replaces top level RE's or QF's by equivalent types, if possible
  containsVars(c) or containsVars(A) => nil
  -- collapses illegal FE's
  first(c) = $FunctionalExpression => eqType defaultTargetFE first A
  eqType constructT(c,A)

replaceLast(A,t) ==
  -- replaces the last element of the nonempty list A by t (constructively
  reverse! (reverse(A).first := t)

destructT(functor)==
  -- provides a list of booleans, which indicate whether the arguments
  -- to the functor are category forms or not
  getDualSignature opOf functor

constructTowerT(t,TL) ==
  -- t is a type, TL a list of constructors and argument lists
  -- t is embedded into TL
  while TL and t repeat
    [c,arg,:TL] := TL
    t0 := constructM(c,replaceLast(arg,t))
    t := canCoerceFrom(t,t0) and t0
  t

bubbleType(TL) ==
  -- tries to move the last constructor in TL upwards
  -- uses canCoerceFrom to test whether two constructors can be bubbled
  [c1,arg1,:T1] := TL
  null T1 or null arg1 => TL
  [c2,arg2,:T2] := T1
  t := last arg1
  t2 := constructM(c2,replaceLast(arg2,t))
  arg1 := replaceLast(arg1,t2)
  newCanCoerceCommute(c2,c1) or canCoerceCommute(c2, c1) =>
    bubbleType [c1,arg1,:T2]
  TL

bubbleConstructor(TL) ==
  -- TL is a nonempty list of type constructors and nonempty argument
  -- lists representing a deconstructed type
  -- then the lowest constructor is bubbled to the top
  [c,arg,:T1] := TL
  t := last arg
  until null T1 repeat
    [c1,arg1,:T1] := T1
    arg1 := replaceLast(arg1,t)
    t := constructT(c1,arg1)
  constructT(c,replaceLast(arg,t))

compareTT(t1,t2) ==
  -- 'T if type t1 is more nested than t2
  -- otherwise 'T if t1 is lexicographically greater than t2
  t1 is [=$QuotientField,:.] or
    symbolMember?(opOf t2,[$QuotientField, 'SimpleAlgebraicExtension]) => nil
    CGREATERP(PRIN2CVEC opOf t1,PRIN2CVEC opOf t2)