Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/GAP/pkg/simpcomp/lib/   (Algebra von RWTH Aachen Version 4.15.1©)  Datei vom 18.2.2022 mit Größe 48 kB image not shown  

Quelle  io.gi   Sprache: unbekannt

 
################################################################################
##
##  simpcomp / io.gi
##
##  simpcomp IO functions 
##
##  $Id$
##
################################################################################

#make list to dense string (not containing any spaces
SCIntFunc.ListToDenseString:=function(item)
 if(IsStringRep(item)) then
  return Concatenation(["\"",item,"\""]);
 elif(IsList(item) and not IsStringRep(item)) then
  return Concatenation("[",
      JoinStringsWithSeparator(List(item,SCIntFunc.ListToDenseString),","),"]");
 else
  return String(item);
 fi;
end;




#parsing variables
SCIntFunc.parseDelim:=',';
SCIntFunc.parseOpenstruct:='[';
SCIntFunc.parseClosestruct:=']';
SCIntFunc.parseArray:=['0','1','2','3','4','5','6','7','8','9',' ',',',
  '[',']','\'','\"'];
SCIntFunc.parseChar:='\'';
SCIntFunc.parseString:='\"';
SCIntFunc.parseEscape:='\\';

#reader functions for reading xml (pc)data 
SCIntFunc.ReadInteger:=
function(string)
 if IsEmptyString(string) or not ForAll(string,x->IsDigitChar(x) or x='-') then
  Info(InfoSimpcomp,1,"SCIntFunc.ReadInteger: error reading from string \"",
      string,"\".");
  return fail;
 else
  return Int(string);
 fi;
end;

SCIntFunc.ReadBoolean:=
function(string)
 local b;
 b:=SCIntFunc.ReadInteger(string);
 if(b<>fail) then
  if(b=0) then
   return false;
  elif(b=1) then
   return true;
  fi;
 fi;
 Info(InfoSimpcomp,1,"SCIntFunc.ReadBoolean: error reading from string \"",
    string,"\".");
 return fail;
end;

SCIntFunc.BooleanToString:=
function(b)
 if(b=false) then
  return "0";
 elif(b=true) then
  return "1";
 else
  return "2";
 fi;
end;

SCIntFunc.ReadString:=
function(string)
 return String(string);
end;


SCIntFunc.ReadArray:=
function(string)
 local p,ret,ReadArrayInteger,ReadArrayString,ReadArrayArray;

 ReadArrayInteger:=function(arr,pos)
  local p,int;

  int:=[];

  for p in [pos..Length(arr)] do
   if(IsDigitChar(arr[p]) or arr[p]='-') then
    Add(int,arr[p]);
   else
    break;
   fi;
  od;

  if(int=[] or (p=Length(arr) and not arr[Length(arr)]=']')) then
   Info(InfoSimpcomp,1,"ReadArrayInteger: error reading from string \"",
        arr,"\" at position ",pos,".");
   return [fail,pos];
  else
   return [Int(int),p];
  fi;
 end;

 ReadArrayString:=function(arr,pos)
  local p,int,delim,escape,str;

  str:=[];
  escape:=false;
  delim:=arr[pos];
  for p in [pos+1..Length(arr)] do
   if(arr[p]=SCIntFunc.parseEscape) then
    escape:=true;
    continue;
   fi;

   if(escape) then
    Add(str,arr[p]);
    escape:=false;
   elif(arr[p]=delim) then
    return [str,p+1];
   else
    Add(str,arr[p]);
   fi;
  od;

  Info(InfoSimpcomp,1,"ReadArrayString: error reading from string \"",
      arr,"\" at position ",pos,".");
  return [fail,pos];
 end;

 ReadArrayArray:=function(arr,pos)
  local p,int,ret,a,range;

  a:=[];
  range:=false;
  p:=pos+1;
  while p<=Length(arr) do
   if(arr[p]=' ' or arr[p]=',') then
    p:=p+1;
    continue;
   fi;

   if(range and not (IsDigitChar(arr[p]) or arr[p]='-')) then
    return fail;
   fi;

   if(not range and arr[p]=']') then
    return [a,p+1];
   elif(arr[p]='[') then
    ret:=ReadArrayArray(arr,p);
   elif(IsDigitChar(arr[p]) or arr[p]='-') then
    ret:=ReadArrayInteger(arr,p);
   elif(arr[p]='\'' or arr[p]='\"') then
    ret:=ReadArrayString(arr,p);
   elif(arr[p]='.' and p<Length(arr) and arr[p+1]='.') then
    range:=true;
    p:=p+2;
    continue;
   else
    Info(InfoSimpcomp,1,"ReadArrayArray: unknown type in string \"",
          arr,"\" at position ",pos,".");
    ret:=fail;
   fi;

   if(ret=fail or ret[1]=fail) then
    return fail;
   fi;

   if(not range) then
    Add(a,ret[1]);
    p:=ret[2];
   else
    range:=[a[Length(a)]..ret[1]];
    a[Length(a)]:=range;
    p:=ret[2];
    range:=false;
   fi;
  od;

  return fail;
 end;


 for p in [1..Length(string)] do
  if(string[p]=' ') then
   continue;
  fi;

  if(string[p]='[') then
   ret:=ReadArrayArray(string,p);
   if(ret=fail) then
    return fail;
   else
    return ret[1];
   fi;
  else
   break;
  fi;
 od;

 Info(InfoSimpcomp,1,"SCIntFunc.ReadArray: no starting sequence found ",
    "in string \"",string,"\".");
 return fail;
end;

SCIntFunc.ReadPerm:=
function(arr)
 local a;
 a:=SCIntFunc.ReadArray(arr);
 if(a<>fail) then
  return PermList(a);
 else
  return fail;
 fi;
end;

SCIntFunc.PermToString:=
function(p)
 return SCIntFunc.ListToDenseString(ListPerm(p));
end;


SCIntFunc.ReadPermGroup:=
function(arr)
 local a,g;
 a:=SCIntFunc.ReadArray(arr);

 if(a<>fail) then
  if a <> [] and a<>[[]] then
   if(IsString(a[1]) and a[1]<>[]) then
    if(a[2]<>[]) then
     g:=Group(List(a[2],x->PermList(x)));
    else
     g:=Group(());
    fi;
    SetStructureDescription(g,a[1]);
    SetName(g,a[1]);
    return g;
   else
    return Group(List(a,x->PermList(x)));
   fi;
  else
   return Group(());
  fi;
 else
  return fail;
 fi;
end;

SCIntFunc.PermGroupToString:=
function(g)
 if(HasName(g) and Name(g)<>"") then
  return String([Name(g),List(GeneratorsOfGroup(g),x->ListPerm(x))]);
 else
  return String([StructureDescription(g),
      List(GeneratorsOfGroup(g),x->ListPerm(x))]);
 fi;
end;





#position object (gets splitted into elements when serialized to xml, 
#as opposed to list)
SCPositionalObjectFamily:=NewFamily("SCPositionalObjectFamily",
  SCIsPositionalObject and IsPositionalObjectRep and IsMutable);
SCPositionalObjectType:=NewType(SCPositionalObjectFamily,SCIsPositionalObject);

#create positional object from list
SCIntFunc.SCPositionalObjectFromList:=
function(list)
 return Objectify(SCPositionalObjectType,rec(data:=list));
end;

#methods for positional object
InstallMethod(
 \[\],"for SCPositionalObject",
 [SCIsPositionalObject,IsInt],
function(po, pos)
  return po!.data[pos];
end);

InstallMethod(
 IsBound\[\],"for SCPositionalObject",
 [SCIsPositionalObject,IsInt],
function(po, pos)
 return IsBound(po!.data[pos]);
end);

InstallMethod(
 \[\]\:\=,"for SCPositionalObject",
 [SCIsPositionalObject and IsMutable,IsInt,IsObject],
function(po, pos, val)
 po!.data[pos]:=val;
end);

InstallMethod(
 Unbind\[\],"for SCPositionalObject",
 [SCIsPositionalObject and IsMutable,IsInt],
function(po, pos)
 Unbind(po!.data[pos]);
end);

InstallMethod(
 Length,"for SCPositionalObject",
 [SCIsPositionalObject],
function(po)
 return Length(po!.data);
end);

InstallMethod(
 ViewObj,"for SCPositionalObject",
 [SCIsPositionalObject],
function(po)
 ViewObj(po!.data);
end);

InstallMethod(
 PrintObj,"for SCPositionalObject",
 [SCIsPositionalObject],
function(po)
 PrintObj(po!.data);
end);


#serialize object to xml
SCIntFunc.SCObjectToXML:=
function(object,name,ident)
 local type,prefix,subitems,processed;

 if(object=fail) then
  return "";
 fi;

 processed:=false;
 for type in RecNames(SCIntFunc.SCXMLIOHandlers) do
  if(not SCIntFunc.SCXMLIOHandlers.(type)[1](object)) then
   continue;
  else
   prefix:=Concatenation(ListWithIdenticalEntries(ident,"\t"));

   if(SCIntFunc.SCXMLIOHandlers.(type)[4]=true) then
    #atomic type
    return Concatenation([prefix,"<",String(name)," type=\"",type,"\">",
          SCIntFunc.SCXMLIOHandlers.(type)[2](object),"</",name,">\n"]);
   else
    #compound type
    return Concatenation([prefix,"<",String(name)," type=\"",type,"\">\n",
          SCIntFunc.SCXMLIOHandlers.(type)[2](object,ident),prefix,"</",name,
          ">\n"]);
   fi;
  fi;
 od;

 Info(InfoSimpcomp,3,"SCIntFunc.SCObjectToXML: ignoring property ",name,
    " -- unknown type.");
 return "";
end;


#element reading handler for xml reader
SCIntFunc.SCXMLElementHandler:=
function(target,pos,root)
 local type,content,c,value,data,lpos;

 if(root.name="PCDATA" or root.name="XMLPI" or 
      root.name="XMLCOMMENT" or root.name="Ignore") then
  #ignore processing instructions & comments
  return 0;
 fi;

 #assume type string
 type:="SCString";
 value:=fail;

 if "attributes" in RecNames(root) and "type" in RecNames(root.attributes) then
  type:=root.attributes.type;
 fi;


 if(type="SCEmpty") then
  return 1;
 fi;


 if(not type in RecNames(SCIntFunc.SCXMLIOHandlers)) then
  Info(InfoSimpcomp,3,"SCIntFunc.SCXMLElementHandler: warning, ignoring ",
      "unknown type \"",type,"\".");
  return 0;
 fi;

 value:=fail;
 if(SCIntFunc.SCXMLIOHandlers.(type)[4]=true) then
  #atomic content
  content:=fail;
  for c in root.content do
   if c.name="PCDATA" then
    content:=c.content;
    break;
   fi;
  od;

  if(content=fail) then
   Info(InfoSimpcomp,1,"SCIntFunc.SCXMLElementHandler: getting content of ",
        "xml node ",root," failed.");
   return -1;
  fi;

  value:=SCIntFunc.SCXMLIOHandlers.(type)[3](content);
 else
  #xml content
  value:=SCIntFunc.SCXMLIOHandlers.(type)[3](root);
 fi;

 if(value=fail) then
  Info(InfoSimpcomp,1,"SCIntFunc.SCXMLElementHandler: I/O handler ",
      "for property ",root.name," failed.");
  return -1;
 fi;

 if(IsRecord(target)) then
  target.(root.name):=value;
 elif(IsList(target)) then
  target[pos]:=value;
 else
  Info(InfoSimpcomp,1,"SCIntFunc.SCXMLElementHandler: target object ",
      "neither of Record nor of List type. target=",target,".");
  return -1;
 fi;
 
 return 1;
end;

#deserialize xml to object
SCIntFunc.SCXMLToObject:=
function(xml)
 local n,tree,items,pos;

 if(IsEmptyString(xml)) then
  return [];
 fi;
 tree:=ParseTreeXMLString(xml);
 if(tree=fail) then
  Info(InfoSimpcomp,1,"SCIntFunc.SCXMLToObject: parsing xml ",
      "input string failed.");
  return fail;
 fi;
 if tree.name="WHOLEDOCUMENT" then
  #start processing
  items:=[];
  pos:=1;
  for n in tree.content do
   if(SCIntFunc.SCXMLElementHandler(items,pos,n)>0) then
    pos:=pos+1;
    
   fi;
  od;
  return items;
 fi;

 Info(InfoSimpcomp,1,"SCIntFunc.SCXMLToObject: xml document root not found.");
 return fail;
end;


#serialize record to xml
SCIntFunc.RecordToXML:=
function(object,ident)
 local key,subitems;
 
 if(not IsRecord(object)) then
  Info(InfoSimpcomp,1,"SCIntFunc.RecordToXML: first argument must be ",
      "of type Record.");
  return "";
 fi;
 
 subitems:="";
 for key in RecNames(object) do
  Append(subitems,SCIntFunc.SCObjectToXML(object.(key),key,ident+1));
 od;
 return subitems;
end;


#deserialize record from xml
SCIntFunc.RecordFromXML:=
function(root)
 local c,r;
 r:=rec();
 for c in root.content do
  SCIntFunc.SCXMLElementHandler(r,1,c);
 od;
 return r;
end;


#serialize positional object to xml
SCIntFunc.PositionalObjectToXML:=
function(object,ident)
 local key,subitems,prefix;
 subitems:="";

 if(not SCIsPositionalObject(object)) then
  Info(InfoSimpcomp,1,"SCIntFunc.PositionalObjectToXML: first argument ",
      "must be of type SCPositionalObject.");
  return "";
 fi;
 
 
 prefix:=Concatenation(ListWithIdenticalEntries(ident,"\t"));
 for key in [1..Length(object)] do
  if(not IsBound(object[key])) then
   Append(subitems,Concatenation(prefix,
        "\t<Entry type=\"SCEmpty\"></Entry>\n"));
  else
   Append(subitems,SCIntFunc.SCObjectToXML(object[key],"Entry",ident+1));
  fi;
 od;
 return subitems;
end;


#deserialize positional object from xml
SCIntFunc.PositionalObjectFromXML:=
function(root)
 local c,po,pos;
 po:=[];
 pos:=1;
 for c in root.content do
  if(SCIntFunc.SCXMLElementHandler(po,pos,c)>0) then
   pos:=pos+1;
  fi;
 od;
 return SCIntFunc.SCPositionalObjectFromList(po);
end;



#serialize fp group to xml
SCIntFunc.FpGroupToXML:=
function(object,ident)
 local key,subitems,prefix;
 subitems:="";

 if(not IsFpGroup(object)) then
  Info(InfoSimpcomp,1,"SCIntFunc.FpGroupToXML: first argument must be ",
      "of type FpGroup.");
  return "";
 fi;
 
 
 prefix:=Concatenation(ListWithIdenticalEntries(ident,"\t"));
 
 Append(subitems,Concatenation(prefix,"\t<Generators type=\"SCArray\">",
    String(List(GeneratorsOfGroup(object),String)),"</Generators>\n"));
 Append(subitems,Concatenation(prefix,"\t<Relators type=\"SCArray\">",
    String(List(RelatorsOfFpGroup(object),String)),"</Relators>\n"));
 
 #return subitems;
 return "";
end;


#deserialize fp group from xml
SCIntFunc.FpGroupFromXML:=
function(root)
 local c,po,pos;
 po:=[];
 pos:=1;
 for c in root.content do
  if(SCIntFunc.SCXMLElementHandler(po,pos,c)>0) then
   pos:=pos+1;
  fi;
 od;
 return [];
end;


#serialize simplicial complex object to xml
SCIntFunc.SCSimplicialComplexToXML:=
function(object,ident)
 local key,subitems,prefix;
 subitems:="";
 
 if(not SCIsSimplicialComplex(object)) then
  Info(InfoSimpcomp,1,"SCIsSimplicialComplex: first argument must be of ",
      "type SCSimplicialComplex.");
  return "";
 fi;
 
 prefix:=Concatenation(ListWithIdenticalEntries(ident,"\t"));
 subitems:="";
 
 #legacy format v1
 #for key in SCPropertiesNames(object) do
 # Append(subitems,SCIntFunc.SCObjectToXML(SCPropertyByName(object,key),
  # key,ident+1));
 #od;
 
 #new format v2
 for key in KnownAttributesOfObject(object) do
  Append(subitems,SCIntFunc.SCObjectToXML(object!.(key),key,ident+1));
 od;
 
 return subitems;
end;


#deserialize simplicial complex object from xml, legacy format 1
SCIntFunc.SCSimplicialComplexFromXMLv1Legacy:=rec(
Facets:=SCFacetsEx,
Faces:=SCFaceLatticeEx,
VertexLabels:=SCFacetsEx,
StronglyConnected:=SCIsStronglyConnected,
Connected:=SCIsConnected,
Generators:=SCGeneratorsEx,
MinimalNonFaces:=SCMinimalNonFacesEx,
Pure:=SCIsPure,
PM:=SCIsPseudoManifold,
CentrallySymmetric:=SCIsCentrallySymmetric,
CohomologyBasis:=SCCohomologyBasis,
HomologyBasis:=SCHomologyBasis,
);

SCIntFunc.SCSimplicialComplexFromXMLv1:=
function(root)
 local props,key,fkey,sc;

 props:=SCIntFunc.RecordFromXML(root);
 sc:=SCIntFunc.SCNew();
 
 for key in RecNames(props) do
  if(IsBound(SCIntFunc.SCSimplicialComplexFromXMLv1Legacy.(key))) then
   fkey:=SCIntFunc.SCSimplicialComplexFromXMLv1Legacy.(key);
  elif(IsBound(SCIntFunc.SCPropertyHandlers.(key))) then
   fkey:=SCIntFunc.SCPropertyHandlers.(key);
  else
   fkey:=EvalString(key);
  fi;
  if(Setter(fkey)=false or Setter(fkey)=fail) then
   Info(InfoSimpcomp,2,"SCIntFunc.SCSimplicialComplexFromXMLv1: legacy ",
        "format, skipped loading attribute '",key,"'");
   continue;
  fi;
  Setter(fkey)(sc,props.(key));
 od;
 
 return sc;
end;


#deserialize simplicial complex object from xml, format 2
SCIntFunc.SCSimplicialComplexFromXMLv2:=
function(root)
 local props,key,sc;

 props:=SCIntFunc.RecordFromXML(root);
 sc:=SCIntFunc.SCNew();
 
 for key in RecNames(props) do
  Setter(EvalString(key))(sc,props.(key));
 od;
 
 return sc;
end;



#serialize library repository object to xml
SCIntFunc.SCLibraryRepositoryToXML:=
function(object,ident)
 local key,subitems,prefix;
 subitems:="";

 if(not SCIsLibRepository(object)) then
  Info(InfoSimpcomp,1,"SCIntFunc.SCLibraryRepositoryToXML: first argument ",
      "must be of type SCLibRepository.");
  return "";
 fi;

 prefix:=Concatenation(ListWithIdenticalEntries(ident,"\t"));
 subitems:="";
 for key in SCPropertiesNames(object) do
  if(key="Loaded") then continue; fi;
  Append(subitems,SCIntFunc.SCObjectToXML(SCPropertyByName(object,key),
      key,ident+1));
 od;
 return subitems;
end;


#deserialize library repository object from xml
SCIntFunc.SCLibraryRepositoryFromXML:=
function(root)
 local props;

 props:=SCIntFunc.RecordFromXML(root);

 if(props=fail) then
  return fail;
 fi;

 return SCIntFunc.LibRepositoryEmptyWithAttributes(props);
end;



################################################################################
##<#GAPDoc Label="SCLoad">
## <ManSection>
## <Func Name="SCLoad" Arg="filename"/>
## <Returns>simplicial complex of type <C>SCSimplicialComplex</C> upon 
## success, <K>fail</K> otherwise.</Returns>
## <Description>
## Loads a simplicial complex stored in a binary format (using 
## <C>IO_Pickle</C>) from a file specified in <Arg>filename</Arg> (as string). 
## If <Arg>filename</Arg> does not end in <C>.scb</C>, this suffix is 
## appended to the file name.
## <Example><![CDATA[
## gap> c:=SCBdSimplex(3);;
## gap> SCSave(c,"/tmp/bddelta3");
## true
## gap> d:=SCLoad("/tmp/bddelta3");
## gap> c=d;
## ]]></Example>
## </Description>
## </ManSection>
##<#/GAPDoc>
################################################################################
InstallGlobalFunction(SCLoad,
 function(filename)

 local lf,fh,c;

 if(not IsString(filename)) then
  Info(InfoSimpcomp,1,"SCLoad: first argument must be of type String.");
  return fail;
 fi;

 #try to detect format (xml,binary) by extension
 if(Length(filename)>2 and 
    filename{[Length(filename)-2..Length(filename)]}=".sc") then
  Info(InfoSimpcomp,2,"SCLoad: assuming XML format for file '",
      filename,"', falling back to SCLoadXML.");
  return SCLoadXML(filename);
 fi;
 
 lf:=ShallowCopy(filename);
 if(Length(filename)<4 or 
    filename{[Length(filename)-3..Length(filename)]}<>".scb") then
  Append(lf,".scb");
 fi;
 
 fh:=IO_File(lf,"r");
 
 if(fh=fail) then
  Info(InfoSimpcomp,1,"SCLoad: Error opening file '",lf,"' for reading.");
  return fail;
 fi;

 #unpickle
 c:=IO_Unpickle(fh);
 
 IO_Close(fh);

 if(c=IO_Error) then
  Info(InfoSimpcomp,1,"SCLoad: Error loading simplicial complex from file '",
      lf,"'.");
  return fail;
 else
  return c;
 fi;
 
end);


################################################################################
##<#GAPDoc Label="SCLoadXML">
## <ManSection>
## <Func Name="SCLoadXML" Arg="filename"/>
## <Returns>simplicial complex of type <C>SCSimplicialComplex</C> upon 
## success, <K>fail</K> otherwise.</Returns>
## <Description>
## Loads a simplicial complex stored in XML format from a file specified in 
## <Arg>filename</Arg> (as string). If <Arg>filename</Arg> does not end in 
## <C>.sc</C>, this suffix is appended to the file name.
## <Example><![CDATA[
## gap> c:=SCBdSimplex(3);;
## gap> SCSaveXML(c,"/tmp/bddelta3");
## true
## gap> d:=SCLoadXML("/tmp/bddelta3");
## gap> c=d;
## ]]></Example>
## </Description>
## </ManSection>
##<#/GAPDoc>
################################################################################
InstallGlobalFunction(SCLoadXML,
 function(filename)

 local data,obj,lf;

 if(not IsString(filename)) then
  Info(InfoSimpcomp,1,"SCLoadXML: first argument must be of type String.");
  return fail;
 fi;
 
 lf:=ShallowCopy(filename);
 if(Length(filename)<3 or 
    filename{[Length(filename)-2..Length(filename)]}<>".sc") then
  Append(lf,".sc");
 fi;

 data:=StringFile(lf);
 if(data=fail or IsEmptyString(data)) then
  Info(InfoSimpcomp,1,"SCLoadXML: error reading file \"",lf,"\".");
  return fail;
 fi;

 obj:=SCIntFunc.SCXMLToObject(data);

 if(obj=fail or Length(obj)<1 or not SCIsSimplicialComplex(obj[1])) then
  Info(InfoSimpcomp,1,"SCLoadXML: error loading simplicial complex from file.");
  return fail;
 fi;

 return obj[1];
end);



################################################################################
##<#GAPDoc Label="SCSave">
## <ManSection>
## <Func Name="SCSave" Arg="complex, filename"/>
## <Returns><K>true</K> upon success, <K>fail</K> otherwise.</Returns>
## <Description>
## Saves a simplicial complex in a binary format (using <C>IO_Pickle</C>) to 
## a file specified in <Arg>filename</Arg> (as string). If <Arg>filename</Arg> 
## does not end in <C>.scb</C>, this suffix is appended to the file name.
## <Example><![CDATA[
## gap> c:=SCBdSimplex(3);;
## gap> SCSave(c,"/tmp/bddelta3");
## true
## ]]></Example>
## </Description>
## </ManSection>
##<#/GAPDoc>
################################################################################
InstallGlobalFunction(SCSave,
 function(complex,filename)

 local fh,lf;

 if(not SCIsSimplicialComplex(complex) or not IsString(filename)) then
  Info(InfoSimpcomp,1,"SCSave: first argument must be of type ",
      "SCSimplicialComplex, second of type String.");
  return fail;
 fi;

 lf:=ShallowCopy(filename);
 if(Length(filename)>2 and 
    filename{[Length(filename)-2..Length(filename)]}=".sc") then
  Append(lf,"b");
 fi;

 if(Length(lf)<4 or lf{[Length(lf)-3..Length(lf)]}<>".scb") then
  Append(lf,".scb");
 fi;
 
 Info(InfoSimpcomp,3,"SCSave: Saving simplicial complex to file '",lf,"'."); 
 fh:=IO_File(lf,"w");
 
 if(fh=fail) then
  Info(InfoSimpcomp,1,"SCSave: Error opening file '",lf,"' for writing.");
  return fail;
 fi;

 #pickle
 if(IO_Pickle(fh,complex)<>IO_OK) then
  Info(InfoSimpcomp,1,"SCSave: Error saving simplicial complex to file '",
      lf,"'.");
  IO_Close(fh);
  return fail;
 else
  IO_Close(fh);
  return true;
 fi;
end);


################################################################################
##<#GAPDoc Label="SCSaveXML">
## <ManSection>
## <Func Name="SCSaveXML" Arg="complex, filename"/>
## <Returns><K>true</K> upon success, <K>fail</K> otherwise.</Returns>
## <Description>
## Saves a simplicial complex <Arg>complex</Arg> to a file specified by 
## <Arg>filename</Arg> (as string) in XML format. If <Arg>filename</Arg> does 
## not end in <C>.sc</C>, this suffix is appended to the file name.
## <Example><![CDATA[
## gap> c:=SCBdSimplex(3);;
## gap> SCSaveXML(c,"/tmp/bddelta3");
## true
## ]]></Example>
## </Description>
## </ManSection>
##<#/GAPDoc>
################################################################################
InstallGlobalFunction(SCSaveXML,
 function(complex,filename)

 local buf,lf;
 
 if(not SCIsSimplicialComplex(complex) or not IsString(filename)) then
  Info(InfoSimpcomp,1,"SCSaveXML: first argument must be of type ",
      "SCSimplicialComplex, second of type String.");
  return fail;
 fi;
 
 lf:=ShallowCopy(filename);

 if(Length(filename)>3 and 
    filename{[Length(filename)-3..Length(filename)]}=".scb") then
  lf:=lf{[1..Length(lf)-1]};
 fi;

 if(Length(lf)<3 or lf{[Length(lf)-2..Length(lf)]}<>".sc") then
  Append(lf,".sc");
 fi;

 Info(InfoSimpcomp,3,"SCSaveXML: saving simplicial complex to file '",lf,"'."); 

 buf:=Concatenation("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n",
 SCIntFunc.SCObjectToXML(complex,"SimplicialComplexV2",0));

 if(FileString(lf,buf)=fail) then
  Info(InfoSimpcomp,1,"SCSaveXML: file \"",filename,"\" not writeable!");
  return fail;
 else
  return true;
 fi;
end);


SCIntFunc.SCGetSimplicialComplexAttributesXMLEx:=
function(sc,attrs)
 local buf,pname,prop,ptype;

 if(Length(attrs)>0 and not ForAll(attrs,IsString)) then
  Info(InfoSimpcomp,1,"SCIntFunc.SCGetSimplicialComplexAttributesXMLEx: ",
      "invalid attribute list.");
  return fail;
 fi;

 buf:="";

 for pname in Intersection(SCPropertiesNames(sc),attrs) do
  prop:=SCPropertyByName(sc,pname);
  Append(buf,SCIntFunc.SCObjectToXML(prop,pname,1));
 od;

 return buf;
end;


SCIntFunc.SCGetSimplicialComplexAttributesXML:=
function(sc,attrs)
 local buf;

 buf:=SCIntFunc.SCGetSimplicialComplexAttributesXMLEx(sc,attrs);
 if(buf=fail) then return fail; fi;

 return Concatenation(["<SimplicialComplex>\n",buf,"</SimplicialComplex>\n\n"]);
end;

################################################################################
##<#GAPDoc Label="SCExportMacaulay2">
## <ManSection>
## <Func Name="SCExportMacaulay2" Arg="complex, ring,  filename [, alphalabels]"/>
## <Returns><K>true</K> upon success, <K>fail</K> otherwise.</Returns>
## <Description>
## Exports the facet list of a given simplicial complex <Arg>complex</Arg> in 
## <C>Macaulay2</C> format to a file specified by <Arg>filename</Arg>. The 
## argument <Arg>ring</Arg> can either be the ring of integers (specified by 
## <C>Integers</C>) or the ring of rationals (sepcified by <C>Rationals</C>). 
## The optional boolean argument <Arg>alphalabels</Arg> labels the complex 
## with characters from <M>a, \dots ,z</M> in the exported file if a value of 
## <K>true</K> is supplied, while the standard labeling of the vertices is 
## <M>v_1, \dots ,v_n</M> where <M>n</M> is the number of vertices of 
## <Arg>complex</Arg>. If <Arg>complex</Arg> has more than <M>26</M> 
## vertices, the argument <Arg>alphalabels</Arg> is ignored. 
## <Example><![CDATA[
## gap> c:=SCBdCrossPolytope(4);;
## gap> SCExportMacaulay2(c,Integers,"/tmp/bdbeta4.m2");
## true
## ]]></Example>
## </Description>
## </ManSection>
##<#/GAPDoc>
################################################################################
InstallGlobalFunction(SCExportMacaulay2,
function(arg)
 local complex,filename,ring,alpha,buf,i,s,v,verts,lut,facets;

 if(Length(arg)<3 or Length(arg)>4 or not SCIsSimplicialComplex(arg[1]) or 
      (not IsIntegers(arg[2]) and not IsRationals(arg[2])) or 
      not IsStringRep(arg[3])) then
   Info(InfoSimpcomp,1,"SCExportMacaulay2: invalid arguments, first",
        "argument must be of type SCSimplicialComplex, seconds a ring ",
        "(Integers or Rationals), third a string.");
   return fail;
 fi;
 
 complex:=arg[1];
 ring:=arg[2];
 filename:=arg[3];
 
 if(SCIsEmpty(complex)) then
  Info(InfoSimpcomp,1,"SCExportMacaulay2: empty complex, duh.");
  return true;
 fi;
 
 if(Length(arg)=4 and IsBool(arg[4]) and arg[4]=true) then
  alpha:=true;
 else
  alpha:=false;
 fi;
 
 
 buf:=["-- simpcomp export of complex ",SCName(complex),
    "\n\nloadPackage \"SimplicialComplexes\";\n\nR = "];
 
 if(IsIntegers(ring)) then
  Add(buf,"ZZ[");
 else
  Add(buf,"QQ[");
 fi;
 
 verts:=Union(SCSkelEx(complex,0));

 #dump variables
 if(Length(verts)<27 and alpha) then
  lut:=["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o",
      "p","q","r","s","t","u","v","w","x","y","z"];
  Add(buf,Concatenation("a..",lut[Length(verts)]));
 else
  lut:=[1..Length(verts)];
  for i in [1..Length(verts)] do
   Add(buf,Concatenation("v",String(i)));
   if(i<Length(verts)) then
    Add(buf,", ");
   fi;
  od;
 fi;
 Add(buf,"];\nFacets = {");
 
 facets:=SCFacetsEx(complex);
 for s in [1..Length(facets)] do
  for v in [1..Length(facets[s])] do
   if not alpha then
    Add(buf,Concatenation("v",String(lut[Position(verts,facets[s][v])])));
   else
    Add(buf,String(lut[Position(verts,facets[s][v])]));
   fi;
   
   if(v<Length(facets[s])) then
    Add(buf,"*");
   elif(s<Length(facets)) then
    Add(buf,", ");
   fi;
  od;
 od;
 Add(buf,"}\n");

 Add(buf,"complex = simplicialComplex Facets;\n\n-- end of export");

 if(FileString(filename,String(Concatenation(buf)))=fail) then
  Info(InfoSimpcomp,1,"SCExportMacaulay2: file \"",filename,
      "\" not writeable!");
  return fail;
 else
  return true;
 fi;

end);


################################################################################
##<#GAPDoc Label="SCExportPolymake">
## <ManSection>
## <Func Name="SCExportPolymake" Arg="complex, filename"/>
## <Returns><K>true</K> upon success, <K>fail</K> otherwise.</Returns>
## <Description>
## Exports the facet list with vertex labels of a given simplicial complex 
## <Arg>complex</Arg> in <C>polymake</C> format to a file specified by 
## <Arg>filename</Arg>. Currently, only the export in the format of 
## <C>polymake</C> version 2.3 is supported. 
## <Example><![CDATA[
## gap> c:=SCBdCrossPolytope(4);;
## gap> SCExportPolymake(c,"/tmp/bdbeta4.poly");
## true
## ]]></Example>
## </Description>
## </ManSection>
##<#/GAPDoc>
################################################################################
InstallGlobalFunction(SCExportPolymake,
function(complex,filename)
 local buf,i,s,v,verts;

 buf:=["_application topaz\n_version 2.3\n_type SimplicialComplex\n\nFACETS\n"];

 verts:=SCVertices(complex);

 for s in SCFacetsEx(complex) do
  Add(buf,"{ ");
  for v in s do
   Append(buf,[String(Position(verts,v)-1)," "]);
  od;
  Add(buf,"}\n");
 od;

 #print vertex labels
 Add(buf,"\nVERTEX_LABELS\n");

 for i in [1..Length(verts)] do
  Add(buf,SCIntFunc.ListToDenseString(verts[i]));
  if(i<Length(verts)) then Add(buf," "); fi;
 od;
 Add(buf,"\n\n");

 if(FileString(filename,String(Concatenation(buf)))=fail) then
  Info(InfoSimpcomp,1,"SCExportPolymake: file \"",filename,"\" not ",
      "writeable!");
  return fail;
 else
  return true;
 fi;

end);


################################################################################
##<#GAPDoc Label="SCExportRecognizer">
## <ManSection>
## <Func Name="SCExportRecognizer" Arg="complex, filename"/>
## <Returns><K>true</K> upon success, <K>fail</K> otherwise.</Returns>
## <Description>
## Exports the gluings of the tetrahedra of a given combinatorial 
## <M>3</M>-manifold <Arg>complex</Arg> in a format compatible with Matveev's 
## <M>3</M>-manifold software <C>Recognizer</C>.
## <Example><![CDATA[
## gap> c:=SCBdCrossPolytope(4);;
## gap> SCExportRecognizer(c,"/tmp/bdbeta4.mv");
## true
## ]]></Example>
## </Description>
## </ManSection>
##<#/GAPDoc>
################################################################################
InstallGlobalFunction(SCExportRecognizer,
function(complex,filename)
 local buf,i,dim,pm,facets,done,j,k,l,elm,trig,f3;

 dim := SCDim(complex);
 pm := SCIsPseudoManifold(complex);
 if dim <> 3 or pm <> true then
  Info(InfoSimpcomp,1,"SCExportRecognizer: input must be a 3-dimensional ",
      "weak pseudomanifold.");
  return fail;
 fi;

 buf:=["triangulation\n"];
 facets := SCFacetsEx(complex);
 if facets = fail then
  return fail;
 fi;
 f3 := Size(facets);
 done := List([1..f3],x->[false,false,false,false]);
 for i in [1..f3] do
  for j in [4,3,2,1] do
   if done[i][j] = true then continue; fi;
   elm := [1..4];
   Remove(elm,j);
   Append(buf,["t",String(i),"(",String(elm[1]),",",String(elm[2]),",",
        String(elm[3]),") - "]);
   trig := facets[i]{elm};
   for k in [i+1..f3] do
    if IsSubset(facets[k],trig) then
     elm := [Position(facets[k],trig[1]),Position(facets[k],trig[2]),
            Position(facets[k],trig[3])];
     Append(buf,["t",String(k),"(",String(elm[1]),",",String(elm[2]),",",
            String(elm[3]),"),\n"]);
     for l in [1..4] do
      if not l in elm then
       done[k][l] := true;
       break;
      fi;
     od;
     break;
    fi;
   od; 
  od;
 od;
 Add(buf,"end\n");
 if(FileString(filename,String(Concatenation(buf)))=fail) then
  Info(InfoSimpcomp,1,"SCExportRecognizer: file \"",filename,
      "\" not writeable!");
  return fail;
 else
  return true;
 fi;

end);

################################################################################
##<#GAPDoc Label="SCExportJavaView">
## <ManSection>
## <Func Name="SCExportJavaView" Arg="complex, file, coords"/>
## <Returns><K>true</K> on success, <K>fail</K> otherwise.</Returns>
## <Description>
## Exports the 2-skeleton of the given simplicial complex <Arg>complex</Arg> 
## (or the facets if the complex is of dimension 2 or less) in <C>JavaView</C> 
## format (file name suffix <C>.jvx</C>) to a file specified by 
## <Arg>filename</Arg> (as string). The list <Arg>coords</Arg> must contain a 
## <M>3</M>-tuple of real coordinates for each vertex of <Arg>complex</Arg>, 
## either as tuple of length three containing the coordinates (Warning: 
## as &GAP; only has rudimentary support for floating point values, currently 
## only integer numbers can be used as coordinates when providing 
## <Arg>coords</Arg> as list of <M>3</M>-tuples) or as string of the form 
## <C>"x.x y.y z.z"</C> with decimal numbers <C>x.x</C>, <C>y.y</C>, 
## <C>z.z</C> for the three coordinates (i.e. <C>"1.0 0.0 0.0"</C>).
## <Example><![CDATA[
## gap> coords:=[[1,0,0],[0,1,0],[0,0,1]];;
## gap> SCExportJavaView(SCBdSimplex(2),"/tmp/triangle.jvx",coords);
## true
## ]]></Example>
## </Description>
## </ManSection>
##<#/GAPDoc>
################################################################################
InstallGlobalFunction(SCExportJavaView,
function(complex,filename,coords)
 local buf,v,f,verts,vertices,trigs,i,name,fl,dim;

 if(SCIsEmpty(complex)) then
  Info(InfoSimpcomp,1,"SCExportJavaView: empty complex, nothing to do.");
  return true;
 fi;

 buf:=[];
 dim:=SCDim(complex);
 if(dim=fail) then
  return fail;
 fi;
 
 if(dim<3) then
  trigs:=SCFacetsEx(complex);
 else
  trigs:=SCSkelEx(complex,2);
 fi;
 
 verts:=Union(SCSkelEx(complex,0));
 
 if(trigs=fail or verts=fail or Length(verts)<>Length(coords)) then
  if(Length(verts)<>Length(coords)) then
   Info(InfoSimpcomp,1,"SCExportJavaView: vertex count must match ",
        "coordinate count.");
  fi;
  return fail;
 fi;
 
 name:=SCPropertyByName(complex,"Name");
 
 if(name=fail) then
  name:="Unnamed simplcicial complex";
 fi;
 
 
 Append(buf,Concatenation("\
 <?xml version=\"1.0\" encoding=\"ISO-8859-1\" standalone=\"no\"?>\n\
 <!DOCTYPE jvx-model SYSTEM \"http://www.javaview.de/rsrc/jvx.dtd\">\n\
 <jvx-model>\n\
  <meta generator=\"<Package>simpcomp</Package>\"/>\n\
  <meta dtd=\"1.11.001\"/>\n\
  <meta date=\"\"/>\n\
  <version type=\"dump\">1.0</version>\n\
  <title>",name,"</title>\n\
  <geometries>\n"));
 
 Append(buf,Concatenation("\
 <geometry name=\"",String(verts),"\">\n\
  <pointSet point=\"show\" dim=\"3\">\n\
   <points num=\"",String(Length(verts)),"\">\n"));
 
 vertices:=[];
 for v in [1..Length(verts)] do
  if(not IsString(coords[v])) then
   #coordinates as list
   vertices[v]:=Concatenation("<p name=\"",String(verts[v]),"\">");
   
   #convert to floating point
   for i in [1..3] do
   
    if(not IsInt(coords[v][i])) then
     Info(InfoSimpcomp,1,"SCExportJavaView: currently only integer ",
            "coordinates are supported when passed as list of coordinate ",
            "tuples. Use list of strings instead.");
     return fail;
    fi;
    
    fl:=ShallowCopy(String(coords[v][i]));
    
    if(PositionSublist(fl,".")=fail) then
     Append(fl,".0");
    fi;
    
    Append(vertices[v],fl);
    
    if(i<3) then
     Append(vertices[v]," ");
    fi;
   od;
   
   Append(vertices[v],"</p>");
  else
   #coordinates as string
   vertices[v]:=Concatenation("<p name=\"",String(verts[v]),"\">",
        coords[v],"</p>");
  fi;
  Append(buf,Concatenation("\t\t\t",vertices[v],"\n"));
 od;


 Append(buf,Concatenation("\
    <thickness>4.0</thickness>\n\
    <color type=\"rgb\">255 0 0</color>\n\
    <colorTag type=\"rgb\">255 0 255</colorTag>\n\
    <labelAtt font=\"text\" horAlign=\"head\" name=\"SansSerif\" verAlign=\"middle\" style=\"plain\" visible=\"show\">\n\
     <xOffset>0</xOffset>\n\
     <yOffset>0</yOffset>\n\
     <size>20</size>\n\
     <color type=\"rgb\">0 0 0</color>\n\
    </labelAtt>\n\
   </points>\n\
  </pointSet>\n\
  <faceSet color=\"show\" edge=\"show\" face=\"show\">\n\
   <faces num=\"",String(Length(trigs)),"\">\n"));

 for f in trigs do
  Append(buf,"\t\t\t<f>");
  for v in [1..Length(f)] do
   if(v>1) then Append(buf," "); fi;
   Append(buf,String(Position(verts,f[v])-1));
  od;
  Append(buf,"</f>\n");
 od;
  
 Append(buf,"\
    <color type=\"rgb\">0 255 0</color>\n\
    <colorTag type=\"rgb\">255 0 255</colorTag>\n\
    <creaseAngle>3.2</creaseAngle>\n\
   </faces>\n\
   <neighbours num=\"1\">\n\
    <nb>-1 -1 -1</nb>\n\
   </neighbours>\n\
   <edges>\n\
    <thickness>1.0</thickness>\n\
    <color type=\"rgb\">0 0 0</color>\n\
    <colorTag type=\"rgb\">255 0 255</colorTag>\n\
   </edges>\n\
   <colors num=\"1\" type=\"rgb\">\n\
    <c>119 236 158</c>\n\
   </colors>\n\
  </faceSet>\n\
  <center visible=\"hide\">\n\
   <p>0.0 0.0 0.0</p>\n\
  </center>\n\
  <material>\n\
   <ambientIntensity>0.2</ambientIntensity>\n\
   <diffuse>\n\
    <color type=\"rgb\">204 204 204</color>\n\
   </diffuse>\n\
   <emissive>\n\
    <color type=\"rgb\">0 0 0</color>\n\
   </emissive>\n\
   <shininess>10.0</shininess>\n\
   <specular>\n\
    <color type=\"rgb\">255 255 255</color>\n\
   </specular>\n\
   <transparency visible=\"show\">0.5</transparency>\n\
  </material>\n\
 </geometry>\n\
 </geometries>\n\
 </jvx-model>\n");
 
 if(FileString(filename,buf)=fail) then
  Info(InfoSimpcomp,1,"SCExportJavaView: filename \"",filename,
      "\" not writeable!");
  return fail;
 else
  return true;
 fi;
 
end);

################################################################################
##<#GAPDoc Label="SCExportLatexTable">
## <ManSection>
## <Func Name="SCExportLatexTable" Arg="complex, filename, itemsperline"/>
## <Returns><K>true</K> on success, <K>fail</K> otherwise.</Returns>
## <Description>
## Exports the facet list of a given simplicial complex <Arg>complex</Arg> 
## (or any list given as first argument) in form of a &LaTeX; table to a file 
## specified by <Arg>filename</Arg>. The argument <Arg>itemsperline</Arg> 
## specifies how many columns the exported table should have. The faces are 
## exported in the format <M>\langle v_1,\dots,v_k \rangle</M>.  
## <Example><![CDATA[
## gap> c:=SCBdSimplex(5);;
## gap> SCExportLatexTable(c,"/tmp/bd5simplex.tex",5);
## ]]></Example>
## </Description>
## </ManSection>
##<#/GAPDoc>
################################################################################
InstallGlobalFunction(SCExportLatexTable,
function(complex,filename,itemsperline)
 local i,j,buf,facets;

 if(not SCIsSimplicialComplex(complex) and not IsList(complex)) then
  Info(InfoSimpcomp,1,"SCExportLatexTable: first argument must be of type ",
      "SCSimplicialComplex or a list of faces.");
  return fail;
 fi;
  
 if(SCIsSimplicialComplex(complex)) then
  facets:=SCFacets(complex);
 else
  facets:=complex;
 fi;

 if(facets=fail) then
  return fail;
 fi;

 buf:=Concatenation(["\\begin{tabular}{",
    Concatenation(ListWithIdenticalEntries(itemsperline,"l")),"}\n"]);

 for i in [1..Length(facets)] do
  Append(buf,"$\\langle ");

  for j in [1..Length(facets[i])-1] do
   Append(buf,
        Concatenation([SCIntFunc.ListToDenseString(facets[i][j]),"\\,"]));
  od;
  Append(buf,SCIntFunc.ListToDenseString(facets[i][Length(facets[i])]));


  Append(buf," \\rangle$");

  if(i=Length(facets)) then
   Append(buf,".");
   Append(buf,
        Concatenation(ListWithIdenticalEntries(-(i mod itemsperline) mod itemsperline,"&")));
   Append(buf,"\n");
   break;
  fi;

  if(i mod itemsperline=0) then
   Append(buf,",\\\\\n\n");
  else
   Append(buf,", &\n");
  fi;
 od;

 Append(buf,"\\end{tabular}");
 buf:=ReplacedString(buf,"\"","");

 if(FileString(filename,buf)=fail) then
  Info(InfoSimpcomp,1,"SCExportLatexTable: file \"",filename,
      "\" not writeable!");
  return fail;
 fi;

 return true;
end);



################################################################################
##<#GAPDoc Label="SCImportPolymake">
## <ManSection>
## <Func Name="SCImportPolymake" Arg="filename"/>
## <Returns>simplicial complex of type <C>SCSimplicialComplex</C> upon 
## success, <K>fail</K> otherwise.</Returns>
## <Description>
## Imports the facet list of a <C>topaz</C> <C>polymake</C> file specified by 
## <Arg>filename</Arg> (discarding any vertex labels) and creates a 
## simplicial complex object from these facets.
## <Example><![CDATA[
## gap> c:=SCBdCrossPolytope(4);;
## gap> SCExportPolymake(c,"/tmp/bdbeta4.poly");
## gap> d:=SCImportPolymake("/tmp/bdbeta4.poly");
## gap> c=d;
## ]]></Example>
## </Description>
## </ManSection>
##<#/GAPDoc>
################################################################################
InstallGlobalFunction(SCImportPolymake,
function(filename)
 local file,lines,fpos,lidx,v,verts,s,facets,sc;
 
 file:=StringFile(filename);
 
 if(file=fail) then
  Info(InfoSimpcomp,1,"SCImportPolymake: error reading file \"",
      filename,"\".");
  return fail;
 fi;
 
 
 lines:=SplitString(file,"\n");
 Apply(lines,NormalizedWhitespace);
 
 fpos:=Position(lines,"VERTICES_IN_FACETS");
 if(fpos=fail) then
  fpos:=Position(lines,"FACETS");
 fi;
 
 
 if(fpos=fail) then
  Info(InfoSimpcomp,1,"SCImportPolymake: could neither find section ",
      "VERTICES_IN_FACETS nor section FACETS in polymake file.");
  return fail;
 fi;
 
 facets:=[];
 for lidx in [fpos+1..Length(lines)] do
  if(not IsBound(lines[lidx]) or IsEmptyString(lines[lidx]) or 
      lines[lidx]=" ") then
   break;
  fi;
  verts:=SplitString(lines[lidx]," ","{}");
  s:=[];
  for v in verts do
   if(IsEmpty(v)) then continue; fi;
   Add(s,Rat(v));
  od;
  Add(facets,s+1);
 od;
 
 sc:=SCFromFacets(facets);
 
 if(sc<>fail) then
  SCRename(sc,Concatenation("polymake import '",filename,"'"));
 fi;
 
 return sc; 
end);



#general io pickling function
SCIntFunc.GeneralPicklerIgnoreList:=[
"ComputedSCHomalgBoundaryMatricess", "ComputedSCHomalgCoboundaryMatricess", 
  "ComputedSCHomalgHomologys", "ComputedSCHomalgCohomologys" 
];
SCIntFunc.GeneralPickler:=
function(f,c,id)
 local p;
 
 # write SCSimplicialComplex tag
 IO_AddToPickled(c);
 if IO_Write(f,id) = fail then 
  IO_FinalizePickled();
  return IO_Error;
 fi;
 
 # write number of attributes
 if IO_WriteSmallInt(f,Length(KnownAttributesOfObject(c))) = IO_Error then
  IO_FinalizePickled();
  return IO_Error;
 fi;        
 
 # pickle all attributes
 for p in KnownAttributesOfObject(c) do
  if(p in SCIntFunc.GeneralPicklerIgnoreList) then
   Info(InfoSimpcomp,3,"SCIntFunc.GeneralPickler: ignoring attribute ",p,
        " as it is in ignore list (no pickler available).");
   continue;
  fi;
 
  # write attribute name
  if(IO_Pickle(f,p)<>IO_OK) then
   IO_FinalizePickled();
   return IO_Error;
  fi;

  # write attribute value
  if(IO_Pickle(f,c!.(p))<>IO_OK) then
   IO_FinalizePickled();
   return IO_Error;
  fi;
 od;

 IO_FinalizePickled();
 return IO_OK;
end;
 
#general io unpickling function
SCIntFunc.GeneralUnpickler:=
function(f,c)
    local i,len,name,ob;

    # read number of attributes
    len:=IO_ReadSmallInt(f);
    if len = IO_Error then
        Info(InfoSimpcomp,1,"SCIntFunc.GeneralUnpickler: Error during ",
          "unpicking of attribute name");
        return c;
    fi;

    # do unpickling
    IO_AddToUnpickled(c);
    for i in [1..len] do
        # read attribute name
     name:=IO_Unpickle(f);
        if name = IO_Error or not(IsString(name)) then
            Info(InfoSimpcomp,1,"SCIntFunc.GeneralUnpickler: Error while ",
                "unpicking attribute name");
        fi;
        
        # read attribute value
        ob:=IO_Unpickle(f);
        if IO_Result(ob) then
            if ob = IO_Error then
                Info(InfoSimpcomp,1,"SCIntFunc.GeneralUnpickler: Error while ",
                  "unpicking attribute value of '",name,"'");
            fi;
        else
     Setter(EvalString(name))(c,ob);
        fi;
    od;
    IO_FinalizeUnpickled();
end;


#pickler for SCSimplicialComplex
InstallMethod(IO_Pickle, "for SCSimplicialComplex",
[ IsFile, SCIsSimplicialComplex ],
function(f,c)
 return SCIntFunc.GeneralPickler(f,c,"SCSC");
end);


# unpickler for SCSimplicialComplex
IO_Unpicklers.SCSC:=
function(f)
    local c;
    
    # SCSimplicialComplex
    c:=SCIntFunc.SCNew();

    SCIntFunc.GeneralUnpickler(f,c);
    return c;
end;

# pickler for SCLibraryRepository
InstallMethod(IO_Pickle, "for SCLibRepository",
[ IsFile, SCIsLibRepository ],
function(f,lr)
 IO_AddToPickled(lr);

 # write SCLibRepository tag
 if IO_Write(f,"SCLR") = fail then 
  IO_FinalizePickled();
  return IO_Error;
 fi;
 
 if(IO_Pickle(f,lr!.Properties)<>IO_OK) then
  IO_FinalizePickled();
  return IO_Error;
 fi;
 
 IO_FinalizePickled();
 return IO_OK;
end);


# unpickler for SCLibraryRepository
IO_Unpicklers.SCLR:=
function(f)
    local prop;
 
    #get library properties    
    prop:=IO_Unpickle(f);
    
    if IO_Result(prop) then
     if prop = IO_Error then
      Info(InfoSimpcomp,1,"IO_Unpicklers.SCLR: Error while unpicking ",
          "library repository. Delete file complexes.idx and complexes.idxb ",
          "and recreate them with SCLibInit.");
      return IO_Error;
     fi;
    else
     return SCIntFunc.LibRepositoryEmptyWithAttributes(prop);
    fi;
end;

#handler functions for file load/save operations
SCIntFunc.SCXMLIOHandlers:=
rec(
 SCSimplicialComplex:=[SCIsSimplicialComplex,
    SCIntFunc.SCSimplicialComplexToXML,SCIntFunc.SCSimplicialComplexFromXMLv1,
    false],
 SCSimplicialComplexV2:=[SCIsSimplicialComplex,
    SCIntFunc.SCSimplicialComplexToXML,SCIntFunc.SCSimplicialComplexFromXMLv2,
    false],
 SCLibraryRepository:=[SCIsLibRepository,SCIntFunc.SCLibraryRepositoryToXML,
    SCIntFunc.SCLibraryRepositoryFromXML,false],
 SCRecord:=[IsRecord,SCIntFunc.RecordToXML,SCIntFunc.RecordFromXML,false],
 SCPositionalObject:=[SCIsPositionalObject,SCIntFunc.PositionalObjectToXML,
    SCIntFunc.PositionalObjectFromXML,false],
 SCInteger:=[IsInt,String,SCIntFunc.ReadInteger,true],
 SCBoolean:=[IsBool,SCIntFunc.BooleanToString,SCIntFunc.ReadBoolean,true],
 SCString:=[IsStringRep,String,SCIntFunc.ReadString,true],
 SCArray:=[IsList,SCIntFunc.ListToDenseString,SCIntFunc.ReadArray,true],
 SCPerm:=[IsPerm,SCIntFunc.PermToString,SCIntFunc.ReadPerm,true],
 SCPermGroup:=[IsPermGroup,SCIntFunc.PermGroupToString,SCIntFunc.ReadPermGroup,
    true],
 SCFpGroup:=[IsFpGroup,SCIntFunc.FpGroupToXML,SCIntFunc.FpGroupFromXML,false]
);


################################################################################

################################################################################
##<#GAPDoc Label="SCExportSnapPy">
## <ManSection>
## <Func Name="SCExportSnapPy" Arg="complex, filename"/>
## <Returns><K>true</K> upon success, <K>fail</K> otherwise.</Returns>
## <Description>
## Exports the facet list and orientability of a given combinatorial 
## <M>3</M>-pseudomanifold <Arg>complex</Arg> in <C>SnapPy</C> format to a 
## file specified by <Arg>filename</Arg>.
## <Example><![CDATA[
## gap> SCLib.SearchByAttribute("Dim=3 and F=[8,28,56,28]");
## gap> c:=SCLib.Load(last[1][1]);;
## gap> SCExportSnapPy(c,"/tmp/M38.tri");
## true
## ]]></Example>
## </Description>
## </ManSection>
##<#/GAPDoc>
################################################################################
InstallGlobalFunction(SCExportSnapPy,
function(complex,filename)
 local curvertex,buf,dim,name,orient,lks,i,pmflag,lksconn,kleinb,torus,
    sphere,lktype,neighbors,gluings,cusps,j,k,trig,remoteidx,verts,facets;

 dim:=SCDim(complex);
 if dim=fail then
  return fail;
 fi;

 if dim<>3 then
  Info(InfoSimpcomp,1,"SCExportSnapPy: argument must be a 3-dimensional ",
      "simplicial complex.");
  return fail;
 fi;

 kleinb:=0;
 torus:=0;
 sphere:=0;

 if SCIsManifold(complex) then
  sphere:=SCNumFaces(complex,0);
  lktype:=ListWithIdenticalEntries(sphere,0);
 fi;

 if sphere=0 then

  lks:=SCLinks(complex,0);
  pmflag:=true;
  for i in [1..Size(lks)] do 
   if not SCIsManifold(lks[i]) then
    pmflag:=false;
    break;
   fi;
  od;
 
  if pmflag<>true then
   Info(InfoSimpcomp,1,"SCExportSnapPy: argument must be a combinatorial ",
        "3-pseudomanifold.");
   return fail;
  fi;

  lksconn:=true;
  for i in [1..Size(lks)] do 
   if not SCIsConnected(lks[i]) then
    lksconn:=false;
    break;
   fi;
  od;

  if lksconn<>true then
   Info(InfoSimpcomp,1,"SCExportSnapPy: argument must be a combinatorial ",
        "3-pseudomanifold with connected links.");
   return fail;
  fi;

  lktype:=[];
  for i in [1..Size(lks)] do
   if SCEulerCharacteristic(lks[i])=2 then
    lktype[i]:=0;
    sphere:=sphere+1;
   elif SCEulerCharacteristic(lks[i])=0 then
    if SCIsOrientable(lks[i]) then
     lktype[i]:=1;
     torus:=torus+1;
    else
     lktype[i]:=2;
     kleinb:=kleinb+1;
    fi;
   else
    Info(InfoSimpcomp,1,"SCExportSnapPy: argument must be a combinatorial ",
          "3-pseudomanifold with links of type S^2, T^2 or K^2.");
    return fail;
   fi;
  od;

 fi;

 name:=SCName(complex);
 if name=fail then
  return fail;
 fi;

 buf:=["% Triangulation\n"];
 Append(buf,[name,"\nunknown 0.0\n"]);

 orient:=SCIsOrientable(complex);
 if orient=fail then
  return fail;
 fi;

 if orient=true then
  Append(buf,["oriented_manifold\nCS_unknown\n\n"]);
 else
  Append(buf,["nonorientable_manifold\nCS_unknown\n\n"]);
 fi;

 verts:=SCVertices(complex);

 Append(buf,[String(torus)," ",String(kleinb),"\n"]);
 for i in [1..SCNumFaces(complex,0)] do
  if lktype[i]=0 then
   #Append(buf,["        internal   0 0\n"]);
   continue;
  elif lktype[i]=1 then
   Append(buf,["        torus   0 0\n"]);
  elif lktype[i]=2 then
   Append(buf,["        Klein bottle   0 0\n"]);
  fi;
 od;

 neighbors:=[];
 gluings:=[];
 cusps:=[];
 facets:=SCFacets(complex);
 if facets=fail then
  return fail;
 fi;
 for j in [1..Size(facets)] do
  neighbors[j]:=[];
  gluings[j]:=[];
  cusps[j]:=[];
  for trig in Combinations(facets[j],3) do
   curvertex:=Difference(facets[j],trig)[1];
   k:=Position(facets[j],curvertex);
   for i in [1..Size(facets)] do
    if facets[i] = facets[j] then continue; fi;
    if IsSubset(facets[i],trig) then
     remoteidx:=Position(facets[i],Difference(facets[i],trig)[1]);
     neighbors[j][k]:=i-1; 
     gluings[j][k]:=[];
     gluings[j][k][k]:=remoteidx-1;    
     gluings[j][k][Position(facets[j],trig[1])]:=
            Position(facets[i],trig[1])-1;
     gluings[j][k][Position(facets[j],trig[2])]:=
            Position(facets[i],trig[2])-1;
     gluings[j][k][Position(facets[j],trig[3])]:=
            Position(facets[i],trig[3])-1;
     cusps[j][k]:=curvertex-1;
     break;
    fi;
   od;
  od;
 od;

 Append(buf,["\n",String(SCNumFaces(complex,dim)),"\n"]);
 for i in [1..Size(facets)] do
  Append(buf,["    ",String(neighbors[i][1]),"    ",
      String(neighbors[i][2]),"    ",String(neighbors[i][3]),"    ",
      String(neighbors[i][4]),"\n"]);
  Append(buf,["    ",String(gluings[i][1][1]),String(gluings[i][1][2]),
      String(gluings[i][1][3]),String(gluings[i][1][4])," ",
      String(gluings[i][2][1]),String(gluings[i][2][2]),
      String(gluings[i][2][3]),String(gluings[i][2][4])," ",
      String(gluings[i][3][1]),String(gluings[i][3][2]),
      String(gluings[i][3][3]),String(gluings[i][3][4])," ",
      String(gluings[i][4][1]),String(gluings[i][4][2]),
      String(gluings[i][4][3]),String(gluings[i][4][4]),"\n"]);
  Append(buf,["    ",String(cusps[i][1]),"    ",String(cusps[i][2]),"    ",
      String(cusps[i][3]),"    ",String(cusps[i][4]),"\n"]);
  Append(buf,["    0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0\n"]);
  Append(buf,["    0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0\n"]);
  Append(buf,["    0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0\n"]);
  Append(buf,["    0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0\n"]);
  Append(buf,["    0  0\n\n"]);
 od;

 if(FileString(filename,String(Concatenation(buf)))=fail) then
  Info(InfoSimpcomp,1,"SCExportSnapPy: file \"",filename,"\" not writeable!");
  return fail;
 else
  return true;
 fi;

end);

[ Dauer der Verarbeitung: 0.42 Sekunden  (vorverarbeitet)  ]