#######################################################################
##
#W  omput.gi          GAP OpenMath Package           Andrew Solomon
##
#H  @(#)$Id: omput.gi,v 1.18 2006/01/27 15:01:24 gap Exp $
##
#Y  Copyright (C)  1997,  Lehrstuhl D fuer Mathematik,  RWTH Aachen,  Germany
#Y  (C) 1998 School Math and Comp. Sci., University of St.  Andrews, Scotland
##
##
## 
Revision.("openmath/gap/omput.gi") := 
"@(#)$Id: omput.gi,v 1.18 2006/01/27 15:01:24 gap Exp $";



# The Gap function AppendTo (used by OMWriteLine) uses
# PrintFormattingStatus and, before Gap 4.4.7, PrintFormattingStatus was
# defined only for text streams.

if not CompareVersionNumbers( VERSION, "4.4.7" )  then
    InstallOtherMethod( PrintFormattingStatus, "for non-text output stream",
      [IsOutputStream],
      function ( str )
        if IsOutputTextStream( str )  then
            TryNextMethod();
        fi;
        return false;
    end);
fi;



#######################################################################
##  
#F  OMWriteLine( <stream>, <list> )
##
##  Input : List of arguments to print
##  Output: \t ^ OMIndent, arguments
##

InstallGlobalFunction(OMWriteLine,
function(stream, alist)
	local i;

	if IsClosedStream( stream )  then
		Error( "closed stream" );
	fi;

	if IsOutputTextStream( stream )  then
		SetPrintFormattingStatus( stream, false );
	fi;

	# do the indentation
	for i in [ 1 .. OMIndent] do
		AppendTo(stream, "\t");
	od;

	for i in alist do
		AppendTo(stream, i);
	od;
	AppendTo(stream, "\n");
end);


#######################################################################
##
#F  OMPutSymbol( <stream>, <cd>, <name> )
##
##  Input : cd, name as strings
##  Output: <OMS cd="<cd>" name="<name>" />
##
InstallGlobalFunction(OMPutSymbol,
function(stream, cd, name)
  OMWriteLine(stream, ["<OMS cd=\"", cd, "\" name=\"", name, "\"/>"]);
end);


#######################################################################
##
#F  OMPutVar( <stream>, <name> )
##
##  Input : name as string
##  Output: <OMV name="<name>" />
##
InstallGlobalFunction(OMPutVar,
function(stream, name)
  OMWriteLine(stream, ["<OMV name=\"", name, "\"/>"]);
end);


#######################################################################
##
#F  OMIrredMatEntryPut( <stream>, <entry>, <data> )
##
##  <entry> is a (possibly unknown) cyclotomic
##  <data> is the record of information about names and values
##  used to substitute for complicated irreducible expressions.
##
##  This borrows heavily from Thomas Breuer's 
##  CharacterTableDisplayStringEntryDefault
##
BindGlobal("OMIrredMatEntryPut",
function(stream, entry, data)
	local val, irrstack, irrnames, name, ll, i, letters, n;

  # OMPut(stream,entry);
	if IsCyc( entry ) and not IsInt( entry ) then
      # find shorthand for cyclo
      irrstack:= data.irrstack;
      irrnames:= data.irrnames;
      for i in [ 1 .. Length( irrstack ) ] do
        if entry = irrstack[i] then
          OMPutVar(stream, irrnames[i]);
					return;
        elif entry = -irrstack[i] then
					OMWriteLine(stream, ["<OMA>"]);
					OMIndent := OMIndent +1;
					OMPutSymbol(stream, "arith1", "unary_minus");
          OMPutVar(stream, irrnames[i]);
					OMIndent := OMIndent -1;
					OMWriteLine(stream, ["</OMA>"]);
					return;
        fi;
        val:= GaloisCyc( irrstack[i], -1 );
        if entry = val then
					OMWriteLine(stream, ["<OMA>"]);
					OMIndent := OMIndent +1;
					OMPutSymbol(stream, "complex1", "conjugate");
          OMPutVar(stream, irrnames[i]);
					OMIndent := OMIndent -1;
					OMWriteLine(stream, ["</OMA>"]);
					return;
        elif entry = -val then
					OMWriteLine(stream, ["<OMA>"]);
					OMIndent := OMIndent +1;
					OMPutSymbol(stream, "arith1", "unary_minus");
					OMWriteLine(stream, ["<OMA>"]);
					OMIndent := OMIndent +1;
					OMPutSymbol(stream, "complex1", "conjugate");
          OMPutVar(stream, irrnames[i]);
					OMIndent := OMIndent -1;
					OMWriteLine(stream, ["</OMA>"]);
					OMIndent := OMIndent -1;
					OMWriteLine(stream, ["</OMA>"]);
					return;
        fi;
        val:= StarCyc( irrstack[i] );
        if entry = val then
					OMWriteLine(stream, ["<OMA>"]);
					OMIndent := OMIndent +1;
					OMPutSymbol(stream, "algnums", "star");
          OMPutVar(stream, irrnames[i]);
					OMIndent := OMIndent -1;
					OMWriteLine(stream, ["</OMA>"]);
					return;
        elif -entry = val then
					OMWriteLine(stream, ["<OMA>"]);
					OMIndent := OMIndent +1;
					OMPutSymbol(stream, "arith1", "unary_minus");
					OMWriteLine(stream, ["<OMA>"]);
					OMIndent := OMIndent +1;
					OMPutSymbol(stream, "algnums", "star");
          OMPutVar(stream, irrnames[i]);
					OMIndent := OMIndent -1;
					OMWriteLine(stream, ["</OMA>"]);
					OMIndent := OMIndent -1;
					OMWriteLine(stream, ["</OMA>"]);
					return;
        fi;
        i:= i+1;
      od;
      Add( irrstack, entry );

      # Create a new name for the irrationality.
      name:= "";
      n:= Length( irrstack );
      letters:= data.letters;
      ll:= Length( letters );
      while 0 < n do
        name:= Concatenation( letters[(n-1) mod ll + 1], name );
        n:= QuoInt(n-1, ll);
      od;
      Add( irrnames, name );
      OMPutVar(stream, irrnames[ Length( irrnames ) ]);
			return;

		elif IsUnknown( entry ) then
			OMPutVar(stream, "?"); 
			return;
		else
			OMPut(stream, entry);
			return;
		fi;

end);

#######################################################################
##
#F  OMPutIrredMat( <stream>, <x> )
##
##  <x> is a character table
##
##  This borrows heavily from Thomas Breuer's 
##  character table Display routines -- see lib/ctbl.gi
##
BindGlobal("OMPutIrredMat",
function(stream, x)
	local r,i, irredmat, data;

	data := CharacterTableDisplayStringEntryDataDefault( x );
  # irreducibles matrix
  irredmat :=  List(Irr(x), ValuesOfClassFunction);

	# OMPut(stream,irredmat);

  OMWriteLine(stream, ["<OMA>"]);
  OMIndent := OMIndent +1;

  OMWriteLine(stream, ["<OMS cd=\"linalg2\" name=\"matrix\"/>"]);
  for r in irredmat do
    OMWriteLine(stream, ["<OMA>"]);
    OMIndent := OMIndent +1;

    OMWriteLine(stream, ["<OMS cd=\"linalg2\" name=\"matrixrow\"/>"]);

    for i in r do
      OMIrredMatEntryPut(stream, i, data);
    od;

    OMIndent := OMIndent -1;
    OMWriteLine(stream, ["</OMA>"]);


  od;

  OMIndent := OMIndent -1;
  OMWriteLine(stream, ["</OMA>"]);

	# Now output the list of (variable = value) pairs
	OMWriteLine(stream, ["<OMA>"]);
	OMIndent := OMIndent +1;
	OMPutSymbol(stream, "list1", "list");
	for i in [1 .. Length(data.irrstack)] do
		OMWriteLine(stream, ["<OMA>"]);
		OMIndent := OMIndent +1;
		OMPutSymbol(stream, "relation1", "eq");
		OMPutVar(stream, data.irrnames[i]);
		OMPut(stream, data.irrstack[i]);
		OMIndent := OMIndent -1;
		OMWriteLine(stream, ["</OMA>"]);
	od;
	OMIndent := OMIndent -1;
	OMWriteLine(stream, ["</OMA>"]);

end);




	
#######################################################################
##
#F  OMPutObject( <stream>, <obj> ) 
##
##
InstallGlobalFunction(OMPutObject,
function(stream, x)
	OMIndent := 0;
	OMWriteLine(stream, ["<OMOBJ>"]);
	OMIndent:= OMIndent+1;
	OMPut(stream, x);
	OMIndent:=OMIndent-1;
	OMWriteLine(stream, ["</OMOBJ>"]);
end);

#######################################################################
##
#F  OMPrint( <obj> ) .......   Print <obj> as OpenMath object
##
##
InstallGlobalFunction(OMPrint,
function(x)
	local str, outstream;

	str := "";
	outstream := OutputTextString(str, true);
	OMPutObject(outstream, x);
	CloseStream(outstream);
	Print(str);
end);

#######################################################################
##
#M  OMPut(<stream>, <string> )  
##
##  specified in the standard
## 
InstallMethod(OMPut, "for a string", true,
[IsOutputStream, IsString],0,
function(stream, x)
	if IsEmpty(x) and not IsEmptyString(x) then
		TryNextMethod();
	fi;

  # convert XML escaped chars
  x := ReplacedString( x, "&", "&amp;" );
  x := ReplacedString( x, "<", "&lt;" );

	OMWriteLine(stream, ["<OMSTR>",x,"</OMSTR>"]);
end);


#######################################################################
##
#M  OMPut( <stream>, <list> )  
##
##  Printing for lists or collection. Prints them as lists.
##
## 

InstallMethod(OMPut, "for a list or collection", true,
[IsOutputStream, IsListOrCollection],0,
function(stream, x)
  local i;

  OMWriteLine(stream, ["<OMA>"]);
  OMIndent := OMIndent +1;

    OMWriteLine(stream, ["<OMS cd=\"list1\" name=\"list\"/>"]);
    for i in x do
      OMPut(stream, i);
    od;

    OMIndent := OMIndent -1;
    OMWriteLine(stream, ["</OMA>"]);
end);


#######################################################################
##
#M  OMPut( <stream>, <bool> )  
##
##  Printing for booleans: specified in CD nums # now logic1
## 
InstallMethod(OMPut, "for a boolean", true,
[IsOutputStream, IsBool],0,
function(stream, x)
    if not x in [ true, false]  then
        TryNextMethod();
    fi;
		
	OMWriteLine(stream, ["<OMS cd=\"logic1\" name=\"", x, "\" />"]);
end);


#######################################################################
##
#M  OMPut( <stream>, <int> )  
##
##  Printing for integers: specified in the standard
## 
InstallMethod(OMPut, "for an integer", true,
[IsOutputStream, IsInt],0,
function(stream, x)
	OMWriteLine(stream, ["<OMI> ", x, "</OMI>"]);
end);

#######################################################################
##
#M  OMPut( <stream>, <int> )  
##
##  Printing for rationals
## 
InstallMethod(OMPut, "for a rational", true,
[IsOutputStream, IsRat],0,
function(stream, x)
	OMWriteLine(stream, ["<OMA>"]);
	OMIndent := OMIndent+1;
	OMWriteLine(stream, ["<OMS cd=\"nums1\" name=\"rational\" />"]);
	OMPut(stream, NumeratorRat(x));
	OMPut(stream, DenominatorRat(x));
	OMIndent := OMIndent-1;
	OMWriteLine(stream, ["</OMA>"]);
end);

#######################################################################
##
#M  OMPut( <stream>, <int> )  
##
##  Printing for cyclotomics
## 
InstallMethod(OMPut, "for a proper cyclotomic", true,
[IsOutputStream, IsCyc],0,
function(stream, x)
	local
                real,
                imaginary,

		n, # Length(powlist)
		i,
		clist; # x = Sum_i clist[i]*E(n)^(i-1)

    if IsGaussRat( x )  then

        real := x -> (x + ComplexConjugate( x )) / 2;
        imaginary := x -> (x - ComplexConjugate( x )) * -1 / 2 * E( 4 );

        OMWriteLine(stream, ["<OMA>"]);
        OMIndent := OMIndent+1;

            OMWriteLine(stream, ["<OMS cd=\"complex1\" name=\"complex_cartesian\"/>"]);
            OMPut(stream, real(x));
            OMPut(stream, imaginary(x));

        OMIndent := OMIndent-1;
        OMWriteLine(stream, ["</OMA>"]);

    else

	n := Conductor(x);
	clist := CoeffsCyc(x, n);

	OMWriteLine(stream, ["<OMA>"]);
	OMIndent := OMIndent+1;
		OMWriteLine(stream, ["<OMS cd=\"arith1\" name=\"plus\" />"]);
		for i in [1 .. n] do
			if clist[i] <> 0 then
				OMWriteLine(stream, ["<OMA>"]); # times
				OMIndent := OMIndent+1;
					OMWriteLine(stream, ["<OMS cd=\"arith1\" name=\"times\" />"]);
					OMPut(stream, clist[i]);
					OMWriteLine(stream, ["<OMA>"]);
					OMIndent := OMIndent+1;
						OMWriteLine(stream, ["<OMS cd=\"algnums\" name=\"NthRootOfUnity\" />"]);
						OMPut(stream, n);
						OMPut(stream, (i-1));
					OMIndent := OMIndent-1;
					OMWriteLine(stream, ["</OMA>"]);
				OMIndent := OMIndent-1;
				OMWriteLine(stream, ["</OMA>"]); #times
			fi;
		od;
	OMIndent := OMIndent-1;
	OMWriteLine(stream, ["</OMA>"]);

    fi;
end);

#######################################################################
##
#M  OMPut( <stream>, <perm> )  
##
##  Printing for permutations: specified in permut1.ocd 
## 
InstallMethod(OMPut, "for a permutation", true,
[IsOutputStream, IsPerm],0,
function(stream, x)
	local i;

	OMWriteLine(stream, ["<OMA>"]);
	OMIndent := OMIndent +1;

	OMWriteLine(stream, ["<OMS cd=\"permut1\" name=\"Permutation\"/>"]);
	for i in ListPerm(x) do
		OMPut(stream, i);
	od;

	OMIndent := OMIndent -1;
	OMWriteLine(stream, ["</OMA>"]);
end);



#######################################################################
##
#M  OMPut( <stream>, <group> )  
##
##  Printing for groups: specified in group1.ocd 
## 
InstallMethod(OMPut, "for a group", true,
[IsOutputStream, IsGroup],0,
function(stream, x)
	local i;

	OMWriteLine(stream, ["<OMA>"]);
	OMIndent := OMIndent +1;

		OMWriteLine(stream, ["<OMS cd=\"group1\" name=\"Group\"/>"]);
		for i in GeneratorsOfGroup(x) do
			OMPut(stream, i);
		od;

		OMIndent := OMIndent -1;
		OMWriteLine(stream, ["</OMA>"]);
end);


#######################################################################
##
#M  OMPut( <stream>,  <matrixrow> )  
##
##  Printing for matrixrows: specified in linalg2.ocd
##
InstallMethod(OMPut, "for a row vector", true,
[IsOutputStream, IsRowVector],0,
function(stream, x)
  local  i;

  OMWriteLine(stream, ["<OMA>"]);
  OMIndent := OMIndent +1;

	OMWriteLine(stream, ["<OMS cd=\"linalg2\" name=\"vector\"/>"]);

	for i in x do
		OMPut(stream, i);
	od;

	OMIndent := OMIndent -1;
	OMWriteLine(stream, ["</OMA>"]);
end);



#######################################################################
##
#M  OMPut( <matrix> )  
##
##  Printing for matrices: specified in linalg2.ocd
##
InstallMethod(OMPut, "for a matrix", true,
[IsOutputStream, IsMatrix],0,
function(stream, x)
  local r, i;

  OMWriteLine(stream, ["<OMA>"]);
  OMIndent := OMIndent +1;

	OMWriteLine(stream, ["<OMS cd=\"linalg2\" name=\"matrix\"/>"]);
	for r in x do
		# OMPut(r); 
		# unfortunately it sometimes might get vectors 
		# and sets confused so we hardwire it.
		OMWriteLine(stream, ["<OMA>"]);
		OMIndent := OMIndent +1;

		OMWriteLine(stream, ["<OMS cd=\"linalg2\" name=\"matrixrow\"/>"]);

		for i in r do
			OMPut(stream, i);
		od;

		OMIndent := OMIndent -1;
		OMWriteLine(stream, ["</OMA>"]);

		
	od;

	OMIndent := OMIndent -1;
	OMWriteLine(stream, ["</OMA>"]);
end);



#######################################################################
##
#M  OMPut( <stream>,  <set> )  
##
##  Printing for set: specified in set1.ocd
##
InstallMethod(OMPut, "for a set", true,
[IsOutputStream, IsSSortedList],0,
function(stream, x)
  local i;

  if IsString(x) and Length(x)>0 or IsEmptyString(x)  then 
  # this doesn't include the empty list
    TryNextMethod();
  fi;

  OMWriteLine(stream, ["<OMA>"]);
  OMIndent := OMIndent +1;

    OMWriteLine(stream, ["<OMS cd=\"set1\" name=\"set\"/>"]);
    for i in x do
      OMPut(stream, i);
    od;

    OMIndent := OMIndent -1;
    OMWriteLine(stream, ["</OMA>"]);
end);


#######################################################################
##
#M  OMPut( <stream>, <character table> )  
##
##
InstallMethod(OMPut, "for a character table", true,
[IsOutputStream, IsCharacterTable],0,
function(stream, c)
	local
		centralizersizes,
		centralizerindices,
		centralizerprimes,
		ordersclassreps,
		sizesconjugacyclasses,
		classnames,
		powmap;



  # the centralizer primes
  centralizersizes := SizesCentralizers(c);
  centralizerprimes := AsSSortedList(Factors(Product(centralizersizes)));

  # the indices which define the factorisation of the
  # centralizer orders
  centralizerindices := List(centralizersizes, z->
    List(centralizerprimes, x->Size(Filtered(Factors(z), y->y=x))));

	# ordersclassreps - every element of a conjugacy class has
	# the same order.
  ordersclassreps := OrdersClassRepresentatives( c );

	# SizesConjugacyClasses
	sizesconjugacyclasses := SizesConjugacyClasses( c );

  # the classnames
  classnames := ClassNames(c);

  # the powermap
  powmap := List(centralizerprimes,
    x->List(PowerMap(c, x),z->ClassNames(c)[z]));

  # irreducibles matrix
  # irredmat :=  List(Irr(c), ValuesOfClassFunction);



  OMWriteLine(stream, ["<OMA>"]);
  OMIndent := OMIndent +1;
    OMWriteLine(stream, ["<OMS cd=\"group1\" name=\"CharacterTable\"/>"]);
		OMPutList(stream, classnames);
		OMPutList(stream, centralizersizes);
		OMPutList(stream, centralizerprimes);
		OMPutList(stream, centralizerindices); 
		OMPutList(stream, powmap);
		OMPutList(stream, sizesconjugacyclasses);
		OMPutList(stream, ordersclassreps);
		# OMPut(stream, irredmat); # previous cd version
		OMPutIrredMat(stream, c);
	OMIndent := OMIndent -1;
	OMWriteLine(stream, ["</OMA>"]);
end);


#######################################################################
##
#M  OMPutList( <stream>, <list> )  
##
##
InstallMethod(OMPutList, "for a list of any type", true,
[IsOutputStream, IsList],0,
function(stream, x)
  local i;

  OMWriteLine(stream, ["<OMA>"]);
  OMIndent := OMIndent +1;

	OMWriteLine(stream, ["<OMS cd=\"list1\" name=\"list\"/>"]);
	for i in x do
		if IsString(i) then
			OMPut(stream, i); # no such thing as characters in OpenMath
		else
			OMPutList(stream, i);
		fi;
	od;

	OMIndent := OMIndent -1;
	OMWriteLine(stream, ["</OMA>"]);
end);

#######################################################################
##
#F  OMPutListVar( <stream>, <list> )  
##
##
BindGlobal("OMPutListVar", 
function(stream, x)
  local i;

  OMWriteLine(stream, ["<OMA>"]);
  OMIndent := OMIndent +1;

	OMWriteLine(stream, ["<OMS cd=\"list1\" name=\"list\"/>"]);
	for i in x do
		OMPutVar(stream, i); 
	od;

	OMIndent := OMIndent -1;
	OMWriteLine(stream, ["</OMA>"]);
end);


#######################################################################
##
#M  OMPutList( <stream>, <list> )  
##
##
InstallMethod(OMPutList, "when we can find no way of regarding it as a list", 
true, [IsOutputStream, IsObject],0,
function(stream, x)
	OMPut(stream, x);
end);
######################################################################
##
#E

