## maps for kash ##

InstallDocumentation(
rec(
name := "Maps",
kind := "KEYWORD",
short:= "In KASH3 maps are functions with additional information, namely "+
        "domain, codomain, and in some cases a map for computing preimages.",
see   := [
DocHash("map"),
DocHash("Map(any,any,func)"),
DocHash("Map(any,any,func,func)"),
DocHash("Domain(map())"),
DocHash("Codomain(map())"),
DocHash("Image(any,map())"),
DocHash("Preimage(any,map())"),
DocHash("HasPreimage(any,map())"),
DocHash("Composition(map(),map())")
]
)
);

__Print_map := function(phi)
local rf;
  rf := RecFields(phi);
  Print("Mapping ");
  if "domain" in rf then
    Print("from ",Type(phi.domain),": ",Name(phi.domain));
  fi;
  if "codomain" in rf then
    Print(" to ",Type(phi.codomain),": ",Name(phi.codomain));
  fi;
  if "inv" in rf then
    Print(" with inverse");
  fi;
end;


InstallMethod(
rec(
name := "Composition",
kind := "FUNCTION",
sin  := [[map(),"phi"],[map(),"psi"]],
sou  := [[map()]],
short:= "The composition 'phi*psi' of the maps 'phi' and 'psi'.",
ex    := [
"x_add_with_inv := function(a)\n"+
"# this function returns a map that adds 'a'\n"+
"local phi, psi;\n"+
"  phi := function(b) return b+a; end;\n"+
"  psi := function(c) return c-a; end;\n"+
"  return Map(Z,Z,phi,psi);\n"+
"end;\n\n"+
"x_f := x_add_with_inv(5);\n"+
"x_f(2);\n"+
"x_Z7 := Quotient(Z,7);\n"+
"x_g := Composition(x_Z7.ext1,x_f);\n"+
"x_g(1);\n"+
"Preimage(Coerce(x_Z7,8),x_g);\n"],
see   := [
DocHash("map"),
DocHash("Map(any,any,func)"),
DocHash("Map(any,any,func,func)"),
DocHash("Domain(map())"),
DocHash("Codomain(map())"),
DocHash("Image(any,map())"),
DocHash("Preimage(any,map())")
]
),
function(phi,psi)
local tau,sigma;

  if not IsRec(phi) and not IsRec(psi) then
    return phi*psi;
  fi;

  if Domain(phi) <> Codomain(psi) then
    Error("Composition(phi,psi): Domain(phi) must equal Codomain(psi).");
  fi;

  tau := rec(base := function(a) return phi(psi(a)); end,
             domain := Domain(psi),
             codomain := Codomain(phi),
             type := map(Type(Domain(psi)),Type(Codomain(phi))),
             operations := rec(Print := __Print_map, \*:=Composition)
             );
  if IsRec(phi) and IsRec(psi) then  
    if "inv" in RecFields(phi) and "inv" in RecFields(psi) then
      sigma := function(a) return Preimage(Preimage(a,phi),psi); end;
      tau.inv := sigma;
    fi;
  elif IsRec(phi) then
    if "inv" in RecFields(phi) then
      sigma := function(a) return Preimage(Preimage(a,phi),psi); end;
      tau.inv := sigma;
    fi;
  else
    if "inv" in RecFields(psi) then
      sigma := function(a) return Preimage(Preimage(a,phi),psi); end;
      tau.inv := sigma;
    fi;
  fi;
  return tau;
end
);


InstallMethod(
rec(
name  := "Map",
kind  := "FUNCTION", 
sin   := [[any,"domain"],[any,"codomain"],[func,"phi"],[func,"psi"]],
sou   := [[map()]],
short := "Create a map with domain 'domain' and codomain 'codomain' "
        +"from the function 'phi'.  "
        + "The function 'psi' is used for the computation of preimages.",
ex    := [
"x_add_with_inv := function(a)\n"+
"# this function returns a map that adds 'a'\n"+
"local phi, psi;\n"+
"  phi := function(b) return b+a; end;\n"+
"  psi := function(c) return c-a; end;\n"+
"  return Map(Z,Z,phi,psi);\n"+
"end;\n\n"+
"x_f := x_add_with_inv(5);\n"+
"x_f(2);\n"+
"Image(3,x_f);\n"+
"Preimage(8,x_f);\n"+
"Preimage(x_f(900),x_f);"],
see   := [
DocHash("map"),
DocHash("Maps"),
DocHash("Map(any,any,func)"),
DocHash("Domain(map())"),
DocHash("Codomain(map())"),
DocHash("Image(any,map())"),
DocHash("Preimage(any,map())"),
DocHash("Composition(map(),map())")
]
),
function(domain,codomain,phi,psi)
return
rec(
base := phi,
inv  := psi,
domain := domain,
codomain := codomain,
type := map(Type(domain),Type(codomain)),
operations := 
rec(Print:=__Print_map, \*:=Composition
)
);
end
);

InstallMethod(
rec(
name  := "Map",
kind  := "FUNCTION", 
sin   := [[any,"domain"],[any,"codomain"],[func,"phi"]],
sou   := [[map()]],
short := "Create a map with domain 'domain' and codomain 'codomain' "
        +"from the function 'phi'.  ",
ex    := [
"x_mult := function(a)\n"+
"# this function returns a map that multiplies by 'I*a'\n"+
"local phi;\n"+
"  phi := function(b) return b*a*I; end;\n"+
"  return Map(R,C,phi);\n"+
"end;\n\n"+
"x_f := x_mult(5);\n"+
"x_f(2);\n"+
"Image(3.1,x_f);\n"+
"Domain(x_f);\n"+
"Codomain(x_f);"],
see   := [
DocHash("map"),
DocHash("Maps"),
DocHash("Map(any,any,func,func)"),
DocHash("Domain(map())"),
DocHash("Codomain(map())"),
DocHash("Image(any,map())"),
DocHash("Preimage(any,map())"),
DocHash("Composition(map(),map())")
]
),
function(domain,codomain,phi)
return
rec(
base := phi,
domain := domain,
codomain := codomain,
type := map(Type(domain),Type(codomain)),
operations := 
rec(Print:= __Print_map
)
);
end
);

######################################################################
# replace the internal map functions by generalizations
######################################################################

_Image := Image;
Unbind(Image);
_Preimage := Preimage;
Unbind(Preimage);
_Domain := Domain;
Unbind(Domain);
_Codomain := Codomain;
Unbind(Codomain);

Image := function(a,phi)

  if Is(Type(phi),map()) then
    return phi(a);
  fi;
  if Is(Type(a),map()) then
    return a(phi);
  fi;
  Error("Image: second argument must be a map.");
end;

Preimage := function(a,phi)

  if not IsRec(phi) then
    return _Preimage(a,phi);
  fi;
  if Is(Type(phi),map()) then
    if not "inv" in RecFields(phi) then
      Error("Preimage: cannot compute preimages under this map.");
    fi;
    return phi.inv(a);
  fi;
  Error("Preimage: second argument must be a map.");
end;

Domain := function(phi)

  if not IsRec(phi) then
    return _Domain(phi);
  fi;
  if Is(Type(phi),map()) then
    if not "domain" in RecFields(phi) then
      Error("Domain: no domain has been assigned to this map.");
    fi;
    return phi.domain;
  fi;
  Error("Domain: argument must be a map.");
end;


Codomain := function(phi)

  if not IsRec(phi) then
    return _Codomain(phi);
  fi;
  if not "codomain" in RecFields(phi) then
    Error("Codomain: no codomain has been assigned to this map.");
  fi;
  if Is(Type(phi),map()) then
    return phi.codomain;
  fi;
  Error("Codomain: argument must be a map.");
end;


