### qaos.k - Interfacing the QaoS database from KASH
##
## Copyright (C) 2005 by Sebastian Freundt, Sebastian Pauli
## Authors: Sebastian Freundt <freundt@math.tu-berlin.de>
##          Sebastian Pauli <pauli@math.tu-berlin.de>
## Created: 2005/03/01
## Keywords: 
##
## This program is free software; you can redistribute it and/or modify it
## under a BSD-like licence.
##
## 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 Technical University of Berlin 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.
## 
## Code:


### global variables
_PackageName := "QaoS";
_PackageVersion := "1.0";
_PackageShortDescription := "Interfacing Algebraic Databases";
_PackageHint := "Type `?Qaos' for help.";


## operations
_QaosOperations :=
  function(arg)
    local desc,recf,printf;

    if Length(arg) < 1 or Length(arg) > 2 then
      desc := "object";
      recf := i->i.("base");
    elif Length(arg) = 1 then
      if IsFunc(arg[1]) then
        printf := arg[1];
      else
        desc := arg[1];
        recf := i->i.("base");
        printf :=
          function(r)
            Print("<",desc," from database: ",recf(r), ">");
          end;
      fi;
    elif Length(arg) = 2 then
      desc := arg[1];
      if IsFunc(arg[2]) then
        recf := arg[2];
      else
        recf := i->i.(arg[2]);
      fi;
      printf :=
        function(r)
          Print("<",desc," from database: ",recf(r), ">");
        end;
    fi;
    return rec(Print := printf);
  end;


_QaosBase := "http://www.math.tu-berlin.de/cgi-bin/kant/qaos/";
_QaosBaseS := "https://www.math.tu-berlin.de/cgi-bin/kant/qaos/";

_QaosModules := Alist(["query","query.scm"]);
_QaosQueryActions := Alist(["query","Go"],["help","Help"],["count","Count"]);
_QaosQueryTypes :=
  Alist(
    ["anf",rec(
      basefield := "generator",
      description := "rational field extension",
      ops := _QaosOperations(~[2].description))],
    ["trnsg",rec(
      basefield := "name",
      description := "transitive group",
      ops := _QaosOperations(~[2].description))],
    ["finf",rec(
      basefield := "generator",
      description := "finite field extension",
      ops := _QaosOperations(~[2].description))]);


## auxiliary funs

UrlEncode:=
  function(string)
    local tmp, new, mpos, j, length, tmpstring, startpos, endpos;
    tmp:=ShallowCopy(string);
    mpos := StringMatchPositions("[^ 0-9A-Za-z]",tmp);

    ## the trivial case
    if Length(mpos) = 0 then
      Replace_(tmp," ","+");
      return tmp;
    fi;

    new := "";
    for j in [1..Length(mpos)] do
      ## copy the non-matching string
      if j>1 then
        startpos := mpos[j-1][2];
      else
        startpos := 1;
      fi;
      endpos := mpos[j][1];
      length := endpos-startpos;
      tmpstring := Substring(tmp, startpos, length);
      new := new + tmpstring;

      ## now copy and transform the matched string
      startpos := mpos[j][1];
      endpos := mpos[j][2];
      length := endpos-startpos;
      tmpstring := Substring(tmp, startpos, length);
      new := new + "%" + Hex(tmpstring);
    od;

    ## copy unmatched data at the end
    endpos:=mpos[Length(mpos)][2];
    length:=Length(tmp)+1-endpos;
    if length>0 then
      new := new + Substring(tmp,endpos,length);
    fi;

    ## now wipe spaces
    Replace_(new," ","+");
    return new;
  end;



InstallDocumentation(
  rec(
    kind := "KEYWORD",
    name := "QaoS",
    short := "",
    see := [DocHash("QaosNumberField(string)"),
            DocHash("QaosTransitiveGroup(string)")]),
  rec(ForceAdd:=TRUE));



#############################################################################
##
#F Qaos generic funs
##
QaosGenericQueryResult:=
  function(arg)
    local query,optarg,limit,offset,uri,buf,pip,result,
      type, action, basefield, ops, colgroups;

    CheckArgs(arg,["query","offset","limit","optarg"],[,,,rec()]);
    query := __ARGREC.("query");
    limit := __ARGREC.("limit");
    offset := __ARGREC.("offset");
    optarg := __ARGREC.("optarg");
    type := _GetEntry_rec_string(optarg,"Type",rec(Fail:="anf"));
    action := _GetEntry_rec_string(optarg,"Action",rec(Fail:="query"));
    colgroups := _GetEntry_rec_string(optarg,"ColGroups",
                   rec(Fail:=["cgall","size","struct"]));

    uri:=
      _QaosBase + Assoc(_QaosModules,"query") +
      "?type="+type+
      "&mode=keyword"+
      "&query="+
      UrlEncode(query)+
      "&action="+Assoc(_QaosQueryActions, action)+
      Mapconcat(i->"&"+i+"=on", colgroups, "")+
      "&offset=" + SPrint(offset)+
      "&limit=" + SPrint(limit)+
      "&output=kash"; 

    result:=Pipe("curl -k -s \""+uri+"\"","");
    return result;
  end;
QaosGenericQuery:=
  function(arg)
    local query,optarg,limit,offset, result,L,l,stime,
      type, typeval, basefield, ops;

    CheckArgs(arg,["query","offset","limit","optarg"],[,,,rec()]);
    query := __ARGREC.("query");
    limit := __ARGREC.("limit");
    offset := __ARGREC.("offset");
    optarg := __ARGREC.("optarg");

    type := _GetEntry_rec_string(optarg,"Type",rec(Fail:="anf"));
    typeval := Assoc(_QaosQueryTypes,type);
    basefield := _GetEntry_rec_string(optarg,"BaseField",
                   rec(Fail:=typeval.basefield));
    ops := _GetEntry_rec_string(optarg,"Operations",
             rec(Fail:=typeval.ops));

    optarg.("type") := type;

    stime := UTime();
    result := QaosGenericQueryResult(query,offset,limit,optarg);

    L := EvalString(result);
    if not IsRec(L) then
      L := rec(base:=L);
    fi;

    if Type(L) = void then
      result := ""; ## do nothing
    elif not Type(L)=string and IsList(L) then
      for l in L do
        l.("base") := l.(basefield);
        l.("operations") := ops;
        l.("id") := type + "_id";
        if IsBound(l.("field_alist")) then
          l.("field_alist") := L.("field_alist");
        fi;
      od;
      ops :=
        _QaosOperations(
          function(r)
            if Length(r)<1 then
              Print("<collection from database: no matches; "+
                     "\""+query+"\">");
            elif Length(r)=1 then
              Print("<collection from database: 1 "+typeval.description+"; "+
                     "\""+query+"\">");
            else 
              Print("<collection from database: ",Length(r),
                " "+typeval.description+"s"+"; "+
                "\""+query+"\">");
            fi;
          end);
    else
      ops :=
        _QaosOperations(
          function(r)
            if Base(r)<1 then
              Print("no "+typeval.description+"s satisfy "+
                     "\""+query+"\"");
            elif Base(r)=1 then
              Print("1 "+typeval.description+" satisfies "+
                     "\""+query+"\"");
            else 
              Print(Base(r)," "+typeval.description+"s satisfy "+
                     "\""+query+"\"");
            fi;
          end);
    fi;

    ## for timing purposes
    Print("Time: ",UTime()-stime," s\n");

    L.query := query;
    L.optarg := optarg;
    L.operations := ops;

    return L;
  end;



#############################################################################
##
#F  QaosNumberField(<string> [, optarg])
##
if not IsBound(QaosNumberFieldDefaultLimit) then
  QaosNumberFieldDefaultLimit := 25;
fi;
InstallDocumentation(
  rec(
    kind := "FUNCTION",
    name := "QaosNumberField",
    sin := [[string, "query"]],
    opt := [[elt-ord^rat,"Limit",
            "Determines how many fields may be retrieved maximally",
            rec(Default:=QaosNumberFieldDefaultLimit)],
            [elt-ord^rat,"Offset",
            "Determines an offset of fields",
            rec(Default:=0)],
            [string,"Action",
            "Determines which action to perform on the query string. "+
            "Possible values are `query' and `count'.",
            rec(Default:="query")],
            [list,"ColGroups",
            "Determines which information to return. "+
            "This is a list of column group specifiers.",
            rec(Default:=["cgall"])]],
    sou := [[list, "L"]],
    short :=
"Searches the Algebraic Objects Database in Berlin.  "+
"The query string equals the keyword search method in the web surface.\n"+
"See `"+_QaosBase+Assoc(_QaosModules,"query")+
  "?type=anf&action="+Assoc(_QaosQueryActions,"help")+"' "+
  "for more information about the syntax and keywords.\n\n"+

"Note: You must have `curl' (see http://curl.haxx.se) installed and "+
"properly configured in order to use QaoS from within KASH."),
  rec(ForceAdd:=TRUE));
QaosNumberField:=
  function(arg)
    local query,optarg,offset,limit,action,colgroups;

    CheckArgs(arg,["query","optarg"],
              [,rec(Limit:=QaosNumberFieldDefaultLimit)]);
    query := __ARGREC.("query");
    optarg := __ARGREC.("optarg");
    limit := _GetEntry_rec_string(optarg,"Limit",
               rec(Fail:=QaosNumberFieldDefaultLimit));
    offset := _GetEntry_rec_string(optarg,"Offset", rec(Fail:=0));
    action := _GetEntry_rec_string(optarg,"Action", rec(Fail:="query"));
    colgroups := _GetEntry_rec_string(optarg,"ColGroups",
                   rec(Fail:=["cgall"]));

    optarg.Type := "anf";
    optarg.Action := action;
    optarg.ColGroups := colgroups;

    return QaosGenericQuery(query,offset,limit,optarg);
  end;



#############################################################################
##
#F  QaosFunctionField(<string> [, optarg])
##
QaosPolynomialAlgebra:=
  function(baseobj,elmlist)
    if not IsList(elmlist) then
      return baseobj;
    elif IsList(elmlist) and Length(elmlist)=0 then
      return PolynomialAlgebra(QaosPolynomialAlgebra(baseobj,0));
    else
      return PolynomialAlgebra(QaosPolynomialAlgebra(baseobj,elmlist[1]));
    fi;
  end;
QaosPolynomial:=
  function(baseobj,elmlist)
    local reduced_elm, polyalg, len;
    if not IsList(elmlist) then
      return Element(baseobj,elmlist);
    elif IsList(elmlist) and Length(elmlist)=0 then
      return Element(baseobj,0);
    else
      polyalg := QaosPolynomialAlgebra(baseobj,elmlist);
      len := Length(elmlist);
      return
        polyalg.1^len+Sum(Apply([1..len],
          i->polyalg.1^(len-i)*QaosPolynomial(baseobj,elmlist[i])));
    fi;
  end;
if not IsBound(QaosFunctionFieldDefaultLimit) then
  QaosFunctionFieldDefaultLimit := 25;
fi;
InstallDocumentation(
  rec(
    kind := "FUNCTION",
    name := "QaosFunctionField",
    sin := [[string, "query"]],
    opt := [[elt-ord^rat,"Limit",
            "Determines how many fields may be retrieved maximally",
            rec(Default:=QaosNumberFieldDefaultLimit)],
            [elt-ord^rat,"Offset",
            "Determines an offset of fields",
            rec(Default:=0)],
            [string,"Action",
            "Determines which action to perform on the query string. "+
            "Possible values are `query' and `count'.",
            rec(Default:="query")],
            [list,"ColGroups",
            "Determines which information to return. "+
            "This is a list of column group specifiers.",
            rec(Default:=["cgall"])]],
    sou := [[list, "L"]],
    short :=
"Searches the Algebraic Objects Database in Berlin.  "+
"The query string equals the keyword search method in the web surface.\n"+
"See `"+_QaosBase+Assoc(_QaosModules,"query")+
  "?type=anf&action="+Assoc(_QaosQueryActions,"help")+"' "+
  "for more information about the syntax and keywords.\n\n"+

"Note: You must have `curl' (see http://curl.haxx.se) installed and "+
"properly configured in order to use QaoS from within KASH."),
  rec(ForceAdd:=TRUE));
QaosFunctionField:=
  function(arg)
    local query,optarg,offset,limit,action,colgroups,
      result, base_to_poly, find_prime_field, i;

    CheckArgs(arg,["query","optarg"],
              [,rec(Limit:=QaosFunctionFieldDefaultLimit)]);
    query := __ARGREC.("query");
    optarg := __ARGREC.("optarg");
    limit := _GetEntry_rec_string(optarg,"Limit",
               rec(Fail:=QaosFunctionFieldDefaultLimit));
    offset := _GetEntry_rec_string(optarg,"Offset", rec(Fail:=0));
    action := _GetEntry_rec_string(optarg,"Action", rec(Fail:="query"));
    colgroups := _GetEntry_rec_string(optarg,"ColGroups",
                   rec(Fail:=["cgall"]));

    optarg.Type := "finf";
    optarg.Action := action;
    optarg.ColGroups := colgroups;

    result := QaosGenericQuery(query,offset,limit,optarg);

    find_prime_field:=
      function(dbobj)
        local constf;
        constf:=Last(dbobj.tree);
        return FiniteField(constf[1]^constf[2]);
      end;
    base_to_poly:=
      function(dbobj)
        local constf;
        constf:=find_prime_field(dbobj);
        dbobj.base:=QaosPolynomial(constf,dbobj.generator);
        dbobj.disc:=QaosPolynomial(constf,dbobj.disc);
        return dbobj;
      end;

#    ## now re-assign the correct base
#    if action="query" then
#      for i in result do
#        i.base := i.tree.base;
#      od;
#    fi;

    return result;
  end;



#############################################################################
##
#F QaosTransitiveGroup(<string> [, optarg])
##
if not IsBound(QaosTransitiveGroupDefaultLimit) then
  QaosTransitiveGroupDefaultLimit := 25;
fi;
InstallDocumentation(
  rec(
    kind := "FUNCTION",
    name := "QaosTransitiveGroup",
    sin := [[string, "query"]],
    opt := [[elt-ord^rat,"Limit",
            "Determines how many groups may be retrieved maximally",
            rec(Default:=QaosTransitiveGroupDefaultLimit)],
            [elt-ord^rat,"Offset",
            "Determines an offset of groups",
            rec(Default:=0)],
            [string,"Action",
            "Determines which action to perform on the query string. "+
            "Possible values are `query' and `count'.",
            rec(Default:="query")],
            [list,"ColGroups",
            "Determines which information to return. "+
            "This is a list of column group specifiers.",
            rec(Default:=["cgall"])]],
    sou := [[list, "L"]],
    short :=
"Searches the Algebraic Objects Database in Berlin.  "+
"The query string equals the keyword search method in the web surface.\n"+
"See `"+_QaosBase+Assoc(_QaosModules,"query")+
  "?type=trnsg&action="+Assoc(_QaosQueryActions,"help")+"' "+
  "for more information about the syntax and keywords.\n\n"+

"Note: You must have `curl' (see http://curl.haxx.se) installed and "+
"properly configured in order to use QaoS from within KASH."),
  rec(ForceAdd:=TRUE));
QaosTransitiveGroup:=
  function(arg)
    local query,optarg,offset,limit,action,colgroups;

    CheckArgs(arg,["query","optarg"],
      [,rec(Limit:=QaosTransitiveGroupDefaultLimit)]);
    query := __ARGREC.("query");
    optarg := __ARGREC.("optarg");
    offset := _GetEntry_rec_string(optarg,"Offset", rec(Fail:=0));
    limit := _GetEntry_rec_string(optarg,"Limit",
               rec(Fail:=QaosTransitiveGroupDefaultLimit));
    action := _GetEntry_rec_string(optarg,"Action", rec(Fail:="query"));
    colgroups := _GetEntry_rec_string(optarg,"ColGroups",
                   rec(Fail:=["cgall"]));

    optarg.Type := "trnsg";
    optarg.Action := action;
    optarg.ColGroups := colgroups;

    return QaosGenericQuery(query,offset,limit,optarg);
  end;




#############################################################################
##
#F  Accessor Funs
##
InstallDocumentation(
  rec(
    kind := "FUNCTION",
    name := "QaosResult",
    sin := [[list, "collection"]],
    sou := [[list, "L"]],
    short :=
"Return the actual list of objects in `collection'."),
  rec(ForceAdd:=TRUE));
QaosResult:=
  function(coll)
    return coll.base;
  end;

if not IsBound(_Qaos_PreviousClassNumber) then
  _Qaos_PreviousClassNumber := ClassNumber;
fi;
ClassNumber := function( arg )
  if Length(arg) = 1 then
    if IsRec(arg[1]) then
      if IsBound(arg[1].class_num) then
        return arg[1].class_num;
      fi;
    fi;
    return _Qaos_PreviousClassNumber(arg[1]);
  elif Length(arg) = 2 then
    return _Qaos_PreviousClassNumber(arg[1],arg[2]);
  else
    Error("ClassNumber: wrong number of arguments");
  fi;
end;

if not IsBound(_Qaos_PreviousClassGroup) then
  _Qaos_PreviousClassGroup := ClassGroup;
fi;
ClassGroup := function( arg )
  if Length(arg) = 1 then
    if IsRec(arg[1]) then
      if IsBound(arg[1].class_group) then
        return AbelianGroup(arg[1].class_group);
      fi;
    fi;
    return _Qaos_PreviousClassGroup(arg[1]);
  elif Length(arg) = 2 then
    return _Qaos_PreviousClassGroup(arg[1],arg[2]);
  else
    Error("ClassGroup: wrong number of arguments");
  fi;
end;


if not IsBound(_Qaos_PreviousUnitGroup) then
  _Qaos_PreviousUnitGroup := UnitGroup;
fi;
UnitGroup := function( arg )
  local ug,fug;
  if Length(arg) = 1 then
    if IsRec(arg[1]) then
      if IsBound(arg[1].unit_group) then
        ug := arg[1].unit_group;
        fug := FreeAbelianGroup(ug[2]+1);
        return Quotient(fug, fug.1*ug[1]);
      fi;
    fi;
    return _Qaos_PreviousUnitGroup(arg[1]);
  elif Length(arg) = 2 then
    return _Qaos_PreviousUnitGroup(arg[1],arg[2]);
  else
    Error("UnitGroup: wrong number of arguments");
  fi;
end;


if not IsBound(_Qaos_PreviousDiscrimininant) then
  _Qaos_PreviousDiscrimininant := Discriminant;
fi;
Discriminant := function( arg )
  if Length(arg) = 1 then
    if IsRec(arg[1]) then
      if IsBound(arg[1].disc) then
        return arg[1].disc;
      fi;
    fi;
    return _Qaos_PreviousDiscrimininant(arg[1]);
  elif Length(arg) = 2 then
    return _Qaos_PreviousDiscrimininant(arg[1],arg[2]);
  else
    Error("Discriminant: wrong number of arguments");
  fi;
end;


if not IsBound(_Qaos_PreviousGalois) then
  _Qaos_PreviousGalois := Galois;
fi;
Galois := function( arg )
local g;
  if Length(arg) = 1 then
    if IsRec(arg[1]) then
      if IsBound(arg[1].galois_group) then
        if arg[1].galois_group <> FAILURE then
          return arg[1].galois_group;
        else
          # if galgrp is not set, compute it 
          g :=  _Qaos_PreviousGalois(arg[1]);
          arg[1].galois_group := g.base;
          return g;
        fi;
      fi;
    fi;
    return _Qaos_PreviousGalois(arg[1]);
  elif Length(arg) = 2 then
    return _Qaos_PreviousGalois(arg[1],arg[2]);
  else
    Error("Galois: wrong number of arguments");
  fi;
end;


if not IsBound(_Qaos_PreviousRegulator) then
  _Qaos_PreviousRegulator := Regulator;
fi;
Regulator := function( arg )
  if Length(arg) = 1 then
    if IsRec(arg[1]) then
      if IsBound(arg[1].reg) then
        return arg[1].reg;
      fi;
    else
      return _Qaos_PreviousRegulator(arg[1]);
    fi;
  elif Length(arg) = 2 then
    return _Qaos_PreviousRegulator(arg[1],arg[2]);
  else
    Error("Regulator: wrong number of arguments");
  fi;
end;

if not IsBound(_Qaos_PreviousSignature) then
  _Qaos_PreviousSignature := Signature;
fi;
Signature := function( arg )
  if Length(arg) = 1 then
    if IsRec(arg[1]) then
      if IsBound(arg[1].sig_re) and IsBound(arg[1].sig_im) then
        return [arg[1].sig_re, arg[1].sig_im];
      fi;
    else
      return _Qaos_PreviousSignature(arg[1]);
    fi;
  elif Length(arg) = 2 then
    return _Qaos_PreviousSignature(arg[1],arg[2]);
  else
    Error("Signature: wrong number of arguments");
  fi;
end;





#############################################################################
##
#F  Data Completer Funs
##
QaosObjectNF :=
  function(object)
    if not IsBound(object.NF) then
      if Degree(object)>1 then
        object.NF := NumberField(object);
      else
        object.NF := FAILURE;
      fi;
    fi;
    return object.NF;
  end;

QaosObjectFF :=
  function(object)
    if not IsBound(object.FF) then
      if Degree(object)>1 then
        object.FF := FunctionField(object);
      else
        object.FF := FAILURE;
      fi;
    fi;
    return object.FF;
  end;

QaosObjectMO :=
  function(object)
    if not IsBound(object.MO) then
      if Type(object)=elt-alg^pol then
        object.MO := MaximalOrder(object);
      elif Type(object)=elt-alg^pol/fld^fin then
        if Degree(object)>1 then
          object.MO := MaximalOrderFinite(QaosObjectFF(object));
        else
          object.MO := FAILURE;
        fi;
      fi;
    fi;
    return object.MO;
  end;

QaosObjectCG :=
  function(object)
    if not IsBound(object.CG) then
      if Degree(object)>1 then
        object.CG := ClassGroup(QaosObjectMO(object));
      else
        object.CG := FAILURE;
      fi;
    fi;
    return object.CG;
  end;

QaosObjectIU :=
  function(object)
    if not IsBound(object.IU) then
      object.IU := IndependentUnits(QaosObjectMO(object));
    fi;
    return object.IU;
  end;
    
QaosCompute\:num_ind_fund_un:=
  function(object)
    if not IsBound(object.UG) then
      object.UG := UnitGroup(QaosObjectNF(object));
    fi;
    return TorsionFreeRank(object.UG);
  end;
QaosCompute\:num_ind_fund_un:=
  function(object)
    return TorsionFreeRank(QaosObjectIU(object));
  end;
QaosCompute\:num_roots_un:=
  function(object)
    if not IsBound(object.UG) then
      object.UG := UnitGroup(QaosObjectNF(object));
    fi;
    return Order(TorsionSubgroup(object.UG));
  end;
QaosCompute\:num_roots_un:=
  function(object)
    return Order(TorsionSubgroup(QaosObjectIU(object)));
  end;
QaosCompute\:unit_group:=
  function(object)
    return [QaosCompute\:num_roots_un(object),
            QaosCompute\:num_ind_fund_un(object)];
  end;

QaosCompute\:galois_group_id:=
  function(object)
    if not IsBound(object.GG) then
      object.GG := _Qaos_PreviousGalois(object);
    fi;
    return "get_group_id_by_tn("+SPrint(object.GG.("ext1"))+","+
      SPrint(object.GG.("ext2"))+")";
  end;
QaosCompute\:galois_group:=
  function(object)
    if not IsBound(object.GG) then
      object.GG := _Qaos_PreviousGalois(object);
    fi;
    ## This string is stored elsewhere, no need to recompute it
    return FAILURE; ## Base(object.GG);
  end;
QaosCompute\:gal_grp_ord:=
  function(object)
    if not IsBound(object.GG) then
      object.GG := _Qaos_PreviousGalois(object);
    fi;
    ## object.GG is a string actually, not a group
    return FAILURE; ##Order(object.GG);
  end;

QaosCompute\:class_num :=
  function(object)
    if Degree(object)>1 then
      return Order(QaosObjectCG(object));
    else
      return FAILURE;
    fi;
  end;
QaosCompute\:class_group :=
  function(object)
    if Degree(object)>1 then
      return AbelianInvariants(QaosObjectCG(object));
    else
      return FAILURE;
    fi;
  end;

QaosCompute\:disc_f :=
  function(object)
    if IsZero(object.disc) then
      return FAILURE;
    else
      return Factorisation(object.disc);
    fi;
  end;


QaosComputeMissingDataAlist:=
  Alist(
    ["galois_group_id",QaosCompute\:galois_group_id],
    ##["galois_group",QaosCompute\:galois_group],
    ##["gal_grp_ord",QaosCompute\:gal_grp_ord],
    ["class_num",QaosCompute\:class_num],
    ["class_group",QaosCompute\:class_group],
    ["num_ind_fund_un",QaosCompute\:num_ind_fund_un],
    ["num_roots_un",QaosCompute\:num_roots_un],
    ["disc_f",QaosCompute\:disc_f]);

QaosComputeMissingData:=
  function(object,recfield)
    local cfun;
    cfun := Assoc(QaosComputeMissingDataAlist,recfield);
    if IsFunc(cfun) then
      object.(recfield) := cfun(object);
    fi;
    return object.(recfield);
  end;

QaosMissingData:=
  function(object)
    if IsRec(object) then
      return Filtered(i->object.(i)=FAILURE,RecFields(object));
    else
      Error("Stupid, heh?");
    fi;
  end;



#############################################################################
##
#F  SQL-Retransformer Funs
##
QaosConvertSQL\:class_group :=
  function(object)
    return "ARRAY["+Mapconcat(i->SPrint(i), List(object.("class_group")), ",")+"]";
  end;
QaosConvertSQL\:disc_f :=
  function(object)
    local poly_to_sql, factor_to_sql, over_finf, trans_deg;
    over_finf := Last(object.tree[2]);
    trans_deg := object.tree[1][4]-1;
    poly_to_sql :=
      function(p)
        p := Butfirst(Reverse(ElementToSequence(p)));
        return "finfs_element_vector_to_finf_elms_id"+
          "(" + SPrint(over_finf) + "," + SPrint(trans_deg) + "," +
          "ARRAY["+Mapconcat(i->SPrint(i), List(p), ",")+"])";
      end;
    factor_to_sql :=
      function(f)
        return "ARRAY["+poly_to_sql(f[1])+","+SPrint(f[2])+"]";
      end;
    return "ARRAY[" + Mapconcat(i->factor_to_sql(i), List(object.disc_f), ",") + "]";
  end;

QaosConvertSQLAlist:=
  Alist(
    ["class_group", QaosConvertSQL\:class_group],
    ["disc_f", QaosConvertSQL\:disc_f]);
QaosObjectRecfield\-\>SQL :=
  function(object, recfield)
    local cfun;
    cfun := Assoc(QaosConvertSQLAlist, recfield);
    if cfun = FAILURE then
      return SPrint(object.(recfield));
    else
      return cfun(object);
    fi;
  end;

_QaosMissingDatum\-\>SQL :=
  function(object, recfield)
    if not object.(recfield) = FAILURE then
      return
        Assoc(object.field_alist,recfield).column+
        "="+QaosObjectRecfield\-\>SQL(object,recfield);
    else
      return "";
    fi;
  end;
_QaosSqlUpdateSkel :=
  function(object,recfield)
    return "UPDATE "+Assoc(object.field_alist,recfield).table+" SET";
  end;
_QaosMissingData\-\>SQL :=
  function(object, missing_list)
    if Length(missing_list) > 0 then
      return
        _QaosSqlUpdateSkel(object,object.("id"))+" "+
          Mapconcat(i->_QaosMissingDatum\-\>SQL(object,i), missing_list, ", ")+
          " WHERE "+object.("id")+" = "+SPrint(object.(object.("id")))+";";
    else
      return VOID;
    fi;
  end;
QaosMissingData\-\>SQL :=
  function(object)
    local missing_before, missing_after, missing_diff;

    missing_before := QaosMissingData(object);

    ## now compute some of the missing things
    Mapc(i->QaosComputeMissingData(object,i),missing_before);

    missing_after := QaosMissingData(object);
    missing_diff := DryDifference(missing_before,missing_after);

    return _QaosMissingData\-\>SQL(object, missing_diff);
  end;

QaosCollection\-\>SQL:=
  function(object_collection)
    local QMD;
    QMD:=
      function(obj)
        local val;
        val := QaosMissingData\-\>SQL(obj);
        if val = VOID then
          return "";
        else
          return val;
        fi;
      end;
    return Mapconcat(i->QMD(i),object_collection,"\n");
  end;


QaosLPolyToSQLbottommost :=
  function(p,over_finf,trans_deg)
    local arr;
    arr := Mapconcat(i->SPrint(i), List(p), ",");
    if Length(arr)=0 then
      return "finfs_element_vector_to_finf_elms_id"+
        "(" + SPrint(over_finf) + "," + SPrint(trans_deg) + "," +
        "'{}'::bigint[])";
    else
      return "finfs_element_vector_to_finf_elms_id"+
        "(" + SPrint(over_finf) + "," + SPrint(trans_deg) + "," +
        "ARRAY["+arr+"])";
    fi;
  end;
HasSublists :=
  function(l)
    if IsList(l) then
      return ForAny(i->IsList(i),l);
    else
      return false;
    fi;
  end;
QaosLPolyToSQL :=
  function(p,over_finf,trans_deg)
    if HasSublists(p) then
      return "ARRAY["+ Mapconcat(i->QaosLPolyToSQL(i,over_finf,trans_deg),
        List(p), ",") +"]";
    else
      return QaosLPolyToSQLbottommost(p,over_finf,trans_deg);
    fi;
  end;
QaosFunctionFieldToSQL :=
  function(obj)
    local gpoly, gpoly_l, cfield, cfield\[x\], bang_to_palg, p_to_l;

    cfield := ConstantField(obj);
    cfield\[x\] := PolynomialAlgebra(cfield);

    bang_to_palg := poly->Element(cfield\[x\], poly);
    p_to_l :=
      function(p)
        local banged;
        if p in cfield then
          return p;
        else
          banged :=
            Reversed(Apply(i->p_to_l(bang_to_palg(i)),
            List(ElementToSequence(p))));
          return Butfirst(banged);
        fi;
      end;

    gpoly := MinimalPolynomial(obj.1);
    gpoly_l := p_to_l(gpoly);

    return "INSERT INTO finfs_elms (finf_id, finf_elm, transcendency_degree) "+
      "VALUES (5, "+QaosLPolyToSQL(gpoly_l,5,0)+", 1)";
  end;


############################################################################
##
##  Identification functions
##
IsInQaosFinfs :=
  function(object)
    ## for finite fields
    local char,charq, q,qq, deg,degq;

    char := Base(Characteristic(object));
    charq := "c"+SPrint(char);

    q := Base(Size(object));
    if q=char then
      qq := "p";
    else
      qq := "q"+SPrint(q);
    fi;

    deg := Base(Degree(object));
    degq := "ad"+SPrint(deg);

    return Mapconcat(i->i,["td0",degq,charq]," ");
  end;

IsInQaosFinfsTD1 :=
  function(object)
    ## for function fields
    local char,charq, q,qq, deg,degq, fmo,disc,discq;

    char := Base(Characteristic(object));
    charq := "c"+SPrint(char);

    q := Base(Size(ConstantField(object)));
    if q=char then
      qq := "p";
    else
      qq := "q"+SPrint(q);
    fi;

    deg := Base(Degree(object));
    degq := "ad"+SPrint(deg);

    fmo:=MaximalOrderFinite(object);
    disc := Base(Discriminant(fmo));
    discq := "disc"+SPrint(disc);

    return Mapconcat(i->i,["td1",degq,charq,discq]," ");
  end;

IsInQaosRatfs :=
  function(object)
    ## for rational fields
    local deg,degq, sig_re,sig_req, disc,discq;

    deg := Base(Degree(object));
    degq := "d"+SPrint(deg);

    disc := Base(Discriminant(object));
    discq := "disc"+SPrint(disc);

    sig_re := Base(Signature(object));
    sig_req := "sr"+SPrint(sig_re);

    return Mapconcat(i->i,[degq, discq, sig_req]," ");
  end;

IsInQaos :=
  function(object)
    #"Test whether `object' is contained in QaoS. "+
    #"Therefore, generate a query out of object.";
    if Type(object) = fld^fin then
      return IsInQaosFinfs(object);
    elif (Type(object) = fld^rat) or (Type(object) = fld^num/fld^rat) then
      return IsInQaosRatfs(object);
    else
      Error("Object type not supported.");
    fi;
    return FALSE;
  end;



if (not IsBound(_QaosBannerShown) or not _QaosBannerShown) then
    _PackageBanner :=
      "\n## Loaded package " +_PackageName+" " +_PackageVersion+" " +
      "("+ _PackageShortDescription + ")\n" +
      "## by Sebastian Freundt (http://www.math.tu-berlin.de/~freundt/)\n"+
      "##    Sebastian Pauli (http://www.math.tu-berlin.de/~pauli/)\n"+
      "## " +_PackageHint+ "\n";
  _QaosBannerShown:=TRUE;
fi;

