home *** CD-ROM | disk | FTP | other *** search
/ Math Solutions 1995 October / Math_Solutions_CD-ROM_Walnut_Creek_October_1995.iso / pc / mac / discrete / lib / string.g < prev    next >
Encoding:
Text File  |  1993-05-05  |  19.3 KB  |  668 lines

  1. #############################################################################
  2. ##
  3. #A  string.g                    GAP library                      Frank Celler
  4. ##
  5. #A  @(#)$Id: string.g,v 3.17 1993/02/05 08:53:05 martin Rel $
  6. ##
  7. #Y  Copyright 1990-1992,  Lehrstuhl D fuer Mathematik,  RWTH Aachen,  Germany
  8. ##
  9. ##  This file contains the string functions.
  10. ##
  11. #H  $Log: string.g,v $
  12. #H  Revision 3.17  1993/02/05  08:53:05  martin
  13. #H  fixed 'PrintArray', used 'Flat' for a matrix of strings
  14. #H
  15. #H  Revision 3.16  1992/02/20  14:07:27  fceller
  16. #H  'PrintArry' now allows list args.
  17. #H
  18. #H  Revision 3.15  1992/01/29  09:49:16  martin
  19. #H  added 'StringFFE'
  20. #H
  21. #H  Revision 3.14  1992/01/13  13:23:23  martin
  22. #H  changed 'CoeffsCyc' to 'COEFFSCYC'
  23. #H
  24. #H  Revision 3.13  1992/01/09  16:13:47  martin
  25. #H  improved 'PrintRec' to support '~'
  26. #H
  27. #H  Revision 3.12  1992/01/02  10:13:11  martin
  28. #H  removed 'RecField'
  29. #H
  30. #H  Revision 3.11  1991/12/19  13:02:20  martin
  31. #H  renamed 'SupportPerm' to 'LargestMovedPointPerm'
  32. #H
  33. #H  Revision 3.10  1991/10/14  10:54:43  martin
  34. #H  added 'Ordinal'
  35. #H
  36. #H  Revision 3.9  1991/10/09  13:16:13  martin
  37. #H  added 'PrintRec'
  38. #H
  39. #H  Revision 3.8  1991/10/09  13:01:10  martin
  40. #H  added 'StringRat' and 'StringCyc'
  41. #H
  42. #H  Revision 3.7  1991/08/12  11:31:56  fceller
  43. #H  add 'StringRec'
  44. #H
  45. #H  Revision 3.6  1991/08/09  12:24:48  fceller
  46. #H  Changed 'Quo|Abs|Log' into '...Int'
  47. #H
  48. #H  Revision 3.5  1991/06/25  10:56:35  fceller
  49. #H  A small mistake in 'StringPP', 13*13 was printed as 2^13.
  50. #H
  51. #H  Revision 3.4  1991/06/21  16:41:16  fceller
  52. #H  'StringPP' for prime powers string added.
  53. #H
  54. #H  Revision 3.3  1991/05/31  13:36:57  fceller
  55. #H  'IdAgWord' adapted.
  56. #H
  57. #H  Revision 3.2  1991/05/10  11:23:50  fceller
  58. #H  General identity of agwords removed.
  59. #H
  60. #H  Revision 3.1  1991/05/06  11:23:50  fceller
  61. #H  Initial revision
  62. #H
  63. ##
  64.  
  65.  
  66. #############################################################################
  67. ##
  68. #F  StringInt( <n> )  . . . . . . . . . . . convert integer <n> into a string
  69. ##
  70. StringInt := function( n )
  71.     local   str,  num,  digits;
  72.  
  73.     # Construct the string without sign.
  74.     num    := AbsInt( n );
  75.     digits := [ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ];
  76.     str    := "";
  77.     repeat
  78.         str := ConcatenationString( digits[ (num mod 10) + 1 ], str );
  79.         num := QuoInt( num, 10 );
  80.     until num = 0;
  81.  
  82.     # Add the sign and return.
  83.     if n < 0  then
  84.         str := ConcatenationString( "-", str );
  85.     fi;
  86.     return str;
  87.  
  88. end;
  89.  
  90.  
  91. #############################################################################
  92. ##
  93. #F  StringRat( <rat> )  . . . . . . . . . .  convert a rational into a string
  94. ##
  95. StringRat := function ( rat )
  96.     local   str;
  97.  
  98.     str := StringInt( Numerator(rat) );
  99.     if Denominator(rat) <> 1  then
  100.         str := ConcatenationString( str, "/", StringInt(Denominator(rat)) );
  101.     fi;
  102.  
  103.     return str;
  104. end;
  105.  
  106.  
  107. #############################################################################
  108. ##
  109. #F  StringCyc( <cyc> )  . . . . . . . . . .  convert cyclotomic into a string
  110. ##
  111. StringCyc := function( cyc )
  112.     local i, j, En, coeffs, str;
  113.  
  114.     # get the coefficients
  115.     coeffs := COEFFSCYC( cyc );
  116.  
  117.     # get the root as a string
  118.     En := ConcatenationString( "E(", StringInt( Length( coeffs ) ), ")" );
  119.  
  120.     # print the first non zero coefficient
  121.     i := 1;
  122.     while coeffs[i] = 0 do i:= i+1; od;
  123.     if i = 1  then
  124.         str := StringRat( coeffs[1] );
  125.     elif coeffs[i] = -1 then
  126.         str := ConcatenationString( "-", En );
  127.     elif coeffs[i] = 1 then
  128.         str := En;
  129.     else
  130.         str := ConcatenationString( StringRat( coeffs[i] ), "*", En );
  131.     fi;
  132.     if 2 < i  then
  133.         str := ConcatenationString( str, "^", StringInt(i-1) );
  134.     fi;
  135.  
  136.     # print the other coefficients
  137.     for j  in [i+1..Length(coeffs)]  do
  138.         if   coeffs[j] =  1  then
  139.             str := ConcatenationString(str,"+",En);
  140.         elif coeffs[j] = -1  then
  141.             str := ConcatenationString(str,"-",En);
  142.         elif coeffs[j] >  0  then
  143.             str := ConcatenationString(str,"+",StringRat(coeffs[j]),"*",En);
  144.         elif coeffs[j] <  0  then
  145.             str := ConcatenationString(str,StringRat(coeffs[j]),"*",En);
  146.         fi;
  147.         if 2 < j  and coeffs[j] <> 0  then
  148.             str := ConcatenationString(str,"^",StringInt(j-1));
  149.         fi;
  150.     od;
  151.  
  152.     # return the string
  153.     return str;
  154. end;
  155.  
  156.  
  157. #############################################################################
  158. ##
  159. #F  StringFFE( <ffe> )  . . . .  convert a finite field element into a string
  160. ##
  161. StringFFE := function ( ffe )
  162.     local   str;
  163.     if   ffe = 0 * ffe  then
  164.         str := ConcatenationString("0*Z(",StringInt(CharFFE(ffe)),")");
  165.     else
  166.         str := ConcatenationString("Z(",StringInt(CharFFE(ffe)));
  167.         if DegreeFFE(ffe) <> 1  then
  168.             str := ConcatenationString(str,"^",StringInt(DegreeFFE(ffe)));
  169.         fi;
  170.         str := ConcatenationString(str,")");
  171.         if LogFFE(ffe) <> 1  then
  172.             str := ConcatenationString(str,"^",StringInt(LogFFE(ffe)));
  173.         fi;
  174.     fi;
  175.     return str;
  176. end;
  177.  
  178.  
  179. #############################################################################
  180. ##
  181. #F  StringPerm( <perm> )  . . . . . . . . . convert permutation into a string
  182. ##
  183. StringPerm := function( perm )
  184.     local   str,  i,  j;
  185.  
  186.     if perm = perm ^ 0 then
  187.         str := "()";
  188.     else
  189.         str := "";
  190.         for i  in [ 1 .. LargestMovedPointPerm( perm ) ]  do
  191.             j := i ^ perm;
  192.             while j > i  do j := j ^ perm;  od;
  193.             if j = i and i ^ perm <> i  then
  194.                 str := ConcatenationString( str, "(", StringInt( i ) );
  195.                 j := i ^ perm;
  196.                 while j > i do
  197.                     str := ConcatenationString( str, ",", StringInt( j ) );
  198.                     j := j ^ perm;
  199.                 od;
  200.                 str := ConcatenationString( str, ")" );
  201.             fi;
  202.         od;
  203.     fi;
  204.     return str;
  205.  
  206. end;
  207.  
  208.  
  209. #############################################################################
  210. ##
  211. #F  StringAgWord( <g> ) . . . . . . . . . . . . convert an agword to a string
  212. ##
  213. StringAgWord := function( g )
  214.     local   i,  str,  names,  exps,  sparse;
  215.  
  216.     # There is no good way to print the identity,  so we use "IdAgWord".
  217.     if g = g^0  then
  218.         return "IdAgWord";
  219.     fi;
  220.  
  221.     # Use  the  names returned by 'InformationAgWord' to construct the string
  222.     # of  the  given  <g>.  'ExponentsAgWord'  yields the exponent vector for
  223.     # these generators.
  224.     names  := InformationAgWord( g ).names;
  225.     str    := "";
  226.     exps   := ExponentsAgWord( g );
  227.     sparse := [];
  228.     for i  in [ 1 .. Length( exps ) ]  do
  229.         if exps[ i ] <> 0  then
  230.             Add( sparse, [ i, exps[ i ] ] );
  231.         fi;
  232.     od;
  233.     for i  in [ 1 .. Length( sparse ) ]  do
  234.         if i > 1  then
  235.             str := ConcatenationString( str, "*" );
  236.         fi;
  237.         str := ConcatenationString( str, names[ sparse[ i ][ 1 ] ] );
  238.         if sparse[ i ][ 2 ] > 1  then
  239.             str := ConcatenationString( str, "^", StringInt(sparse[i][2]) );
  240.         fi;
  241.     od;
  242.     return str;
  243.  
  244. end;
  245.  
  246.  
  247. #############################################################################
  248. ##
  249. #F  StringBool( <bool> )  . . . . . . . . . . . convert boolean into a string
  250. ##
  251. StringBool := function( bool )
  252.  
  253.     if bool  then
  254.         return "true";
  255.     else
  256.         return "false";
  257.     fi;
  258.  
  259. end;
  260.  
  261.  
  262. #############################################################################
  263. ##
  264. #F  StringList( <list> )  . . . . . . . . . . . .  convert list into a string
  265. ##
  266. StringList := function( list )
  267.     local   str, i;
  268.  
  269.     str := "[ ";
  270.     for i in [ 1 .. Length( list ) ]  do
  271.         if IsBound( list[ i ] )  then
  272.             str := ConcatenationString( str, String( list[ i ] ) );
  273.         fi;
  274.         if i <> Length( list )  then
  275.             str := ConcatenationString( str, ", " );
  276.         fi;
  277.     od;
  278.     return ConcatenationString( str, " ]" );
  279.  
  280. end;
  281.  
  282.  
  283. #############################################################################
  284. ##
  285. #F  StringRec( <rec> )    . . . . . . . . . . . .  convert record into a string
  286. ##
  287. StringRec := function( record )
  288.     local   str,  nam,  com;
  289.  
  290.     if IsBound(record.operations) and IsBound(record.operations.String)  then
  291.         str := record.operations.String( record );
  292.     else
  293.         str := "rec( ";
  294.         com := false;
  295.         for nam  in RecFields( record )  do
  296.             if com  then
  297.                 str := ConcatenationString( str, ", " );
  298.             else
  299.                 com := true;
  300.             fi;
  301.             str := ConcatenationString( str, nam, " := ",
  302.                                         String( record.(nam) ) );
  303.         od;
  304.         str := ConcatenationString( str, " )" );
  305.     fi;
  306.     return str;
  307.  
  308. end;
  309.  
  310.  
  311. #############################################################################
  312. ##
  313. #F  String( <obj>, <width> ) . . . . . . . . . . convert object into a string
  314. ##
  315. ##  Main dispatcher for 'StringType( <obj> )', where <Type> is  the  type  of
  316. ##  <obj>.
  317. ##
  318. String := function( arg )
  319.     local   str, blanks, obj, fill;
  320.  
  321.     # Check the arguments.
  322.     if Length( arg ) < 1
  323.        or Length( arg ) > 2
  324.        or ( Length( arg ) = 2 and not IsInt( arg[ 2 ] ) )
  325.     then
  326.         Error( "usage: String( <obj> ) or String( <obj>, <width> )" );
  327.     fi;
  328.     obj := arg[ 1 ];
  329.  
  330.     # Dispatcher.
  331.     if   IsInt( obj )     then str := StringInt   ( obj );
  332.     elif IsRat( obj )     then str := StringRat   ( obj );
  333.     elif IsCyc( obj )     then str := StringCyc   ( obj );
  334.     elif IsFFE( obj )     then str := StringFFE   ( obj );
  335.     elif IsPerm( obj )    then str := StringPerm  ( obj );
  336.     elif IsAgWord( obj )  then str := StringAgWord( obj );
  337.     elif IsBool( obj )    then str := StringBool  ( obj );
  338.     elif IsString( obj )  then str := obj;
  339.     elif IsList( obj )    then str := StringList  ( obj );
  340.     elif IsRec( obj )     then str := StringRec   ( obj );
  341.     else Error( "String: cannot convert ", obj, " into a string" );
  342.     fi;
  343.  
  344.     # If no <width> is given or it is to small, return.
  345.     if Length( arg ) = 1 or LengthString( str ) >= AbsInt( arg[ 2 ] )  then
  346.         return str;
  347.     fi;
  348.  
  349.     # If <width> is positive, blanks are filled in from the left.
  350.     blanks := "                                                 ";
  351.     if arg[ 2 ] > 0  then
  352.         fill := arg[ 2 ] - LengthString( str );
  353.         while fill > 0  do
  354.             if fill >= LengthString( blanks )  then
  355.                 str := ConcatenationString( blanks, str );
  356.             else
  357.                 str := ConcatenationString( SubString( blanks,1,fill ),str );
  358.             fi;
  359.             fill := arg[ 2 ] - LengthString( str );
  360.         od;
  361.     else
  362.         fill :=  - arg[ 2 ] - LengthString( str );
  363.         while fill > 0  do
  364.             if fill >= LengthString( blanks )  then
  365.                 str := ConcatenationString( str, blanks );
  366.             else
  367.                 str := ConcatenationString( str,SubString( blanks,1,fill ) );
  368.             fi;
  369.             fill :=  - arg[ 2 ] - LengthString( str );
  370.         od;
  371.     fi;
  372.     return str;
  373.  
  374. end;
  375.  
  376.  
  377. #############################################################################
  378. ##
  379. #F  PrintArray( <array> ) . . . . . . . . . . . . . . . . pretty print matrix
  380. ##
  381. PrintArray := function( array )
  382.     local   arr,  max,  l,  k;
  383.  
  384.     if array = [[]]  then
  385.         Print( "[ [ ] ]\n" );
  386.     elif array = []  then
  387.     Print( "[ ]\n" );
  388.     elif not ForAll( array, IsList )  then
  389.     arr := List( array, x -> String( x ) );
  390.     max := Maximum( List( arr, LengthString ) );
  391.     Print( "[ ", String( arr[ 1 ], max + 1 ) );
  392.     for l  in [ 2 .. Length( arr ) ]  do
  393.         Print( ", ", String( arr[ l ], max + 1 ) );
  394.     od;
  395.     Print( " ]\n" );
  396.     else
  397.         arr := List( array, x -> List( x, String ) );
  398.         max := Maximum( List( arr, x -> Maximum( List(x,LengthString) ) ) );
  399.         Print( "[ " );
  400.         for l  in [ 1 .. Length( arr ) ]  do
  401.             if l > 1  then
  402.                 Print( "  " );
  403.             fi;
  404.             Print( "[ " );
  405.             for k  in [ 1 .. Length( arr[ l ] ) ]  do
  406.                 Print( String( arr[ l ][ k ], max + 1 ) );
  407.                 if k = Length( arr[ l ] )  then
  408.                     Print( " ]" );
  409.                 else
  410.                     Print( ", " );
  411.                 fi;
  412.             od;
  413.             if l = Length( arr )  then
  414.                 Print( " ]\n" );
  415.             else
  416.                 Print( ",\n" );
  417.             fi;
  418.         od;
  419.     fi;
  420.  
  421. end;
  422.  
  423.  
  424. #############################################################################
  425. ##
  426. #F  PrintRec(<record>)  . . . . . . . . . . . . . . . . . . .  print a record
  427. ##
  428. ##  'PrintRec' must  call 'Print'  so that 'Print'   assigns  the record   to
  429. ##  '~' and  prints for  example 'rec( a := ~  )'  in this  form and does not
  430. ##  go into an  infinite loop 'rec( a  := rec(  a := ...'.   To make  'Print'
  431. ##  do the right   thing, we  assign to '<record>.operation'  the  operations
  432. ##  record 'RecordOps', which contains the appropriate 'Print' function.
  433. ##
  434. PrintRecIgnore := [ "operations", "parent" ];
  435.  
  436. PrintRecIndent := "  ";
  437.  
  438. RecordOps := rec();
  439.  
  440. RecordOps.Print := function ( record )
  441.     local  len, i, nam, lst, printRecIndent;
  442.     len  := 0;
  443.     for nam in RecFields( record )  do
  444.         if len < LengthString( nam )  then
  445.             len := LengthString( nam );
  446.         fi;
  447.         lst := nam;
  448.     od;
  449.     Print( "rec(\n" );
  450.     for nam  in RecFields( record )  do
  451.         if not nam in PrintRecIgnore then
  452.             if not IsRec( record.(nam) )  then
  453.                 Print( PrintRecIndent, nam );
  454.                 for i  in [LengthString(nam)..len]  do
  455.                     Print( " " );
  456.                 od;
  457.                 Print( ":= ", record.(nam) );
  458.                 if nam <> lst  then  Print( ",\n" );  fi;
  459.             else
  460.                 Print( PrintRecIndent, nam );
  461.                 for i  in [LengthString(nam)..len]  do
  462.                     Print( " " );
  463.                 od;
  464.                 Print( ":= " );
  465.                 printRecIndent := PrintRecIndent;
  466.                 PrintRecIndent := ConcatenationString(PrintRecIndent,"  ");
  467.                 PrintRec( record.(nam) );
  468.                 if nam <> lst  then Print( ",\n" );  fi;
  469.                 PrintRecIndent := printRecIndent;
  470.             fi;
  471.         else
  472.             Print( PrintRecIndent, nam );
  473.             for i  in [LengthString(nam)..len]  do
  474.                 Print( " " );
  475.             od;
  476.             Print( ":= ..." );
  477.             if nam <> lst  then Print( ",\n" );  fi;
  478.         fi;
  479.     od;
  480.     Print( " )" );
  481. end;
  482.  
  483. PrintRec := function ( record )
  484.     local   operations;
  485.     if IsBound( record.operations )  then
  486.         operations := record.operations;
  487.     fi;
  488.     record.operations := RecordOps;
  489.     Print( record );
  490.     if IsBound( operations )  then
  491.         record.operations := operations;
  492.     else
  493.         Unbind( record.operations );
  494.     fi;
  495. end;
  496.  
  497.  
  498. #############################################################################
  499. ##
  500. #F  StringDate( <date> )  . . . . . . . . convert date into a readable string
  501. #F  WeekDay( <date> ) . . . . . . . . . . . . . . . . . . . weekday of a date
  502. #F  DMYDay( <day> ) . . .  convert days since 01-Jan-1970 into day-month-year
  503. #F  DayDMY( <dmy> ) . . .  convert day-month-year into days since 01-Jan-1970
  504. #F  DaysInYear( <year> )  . . . . . . . . .  days in a year, knows leap-years
  505. #F  DaysInMonth( <month>, <year> )  . . . . days in a month, knows leap-years
  506. ##
  507. DaysInYear := function ( year )
  508.     if year mod 4 in [1,2,3]  or year mod 400 in [100,200,300]  then
  509.         return 365;
  510.     else
  511.         return 366;
  512.     fi;
  513. end;
  514.  
  515. DaysInMonth := function ( month, year )
  516.     if month in [ 1, 3, 5, 7, 8, 10, 12 ]  then
  517.         return 31;
  518.     elif month in [ 4, 6, 9, 11 ]  then
  519.         return 30;
  520.     elif year mod 4 in [1,2,3]  or year mod 400 in [100,200,300]  then
  521.         return 28;
  522.     else
  523.         return 29;
  524.     fi;
  525. end;
  526.  
  527. DMYDay := function ( day )
  528.     local  year, month;
  529.     year := 1970;
  530.     while DaysInYear(year) <= day  do
  531.         day   := day - DaysInYear(year);
  532.         year  := year + 1;
  533.     od;
  534.     month := 1;
  535.     while DaysInMonth(month,year) <= day  do
  536.         day   := day - DaysInMonth(month,year);
  537.         month := month + 1;
  538.     od;
  539.     return [ day+1, month, year ];
  540. end;
  541.  
  542. DayDMY := function ( dmy )
  543.     local  year, month, day;
  544.     day   := dmy[1]-1;
  545.     month := dmy[2];
  546.     year  := dmy[3];
  547.     while 1 < month  do
  548.         month := month - 1;
  549.         day   := day + DaysInMonth( month, year );
  550.     od;
  551.     while 1970 < year  do
  552.         year  := year - 1;
  553.         day   := day + DaysInYear( year );
  554.     od;
  555.     return day;
  556. end;
  557.  
  558. NameWeekDay := [ "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" ];
  559. WeekDay := function ( date )
  560.     if IsList( date )  then date := DayDMY( date );  fi;
  561.     return NameWeekDay[ (date + 3) mod 7 + 1 ];
  562. end;
  563.  
  564. NameMonth :=  [ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  565.                 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ];
  566. StringDate := function ( date )
  567.     if IsInt( date )  then date := DMYDay( date );  fi;
  568.     return ConcatenationString(
  569.         String(date[1],2), "-", NameMonth[date[2]], "-", String(date[3],4) );
  570. end;
  571.  
  572.  
  573. #############################################################################
  574. ##
  575. #F  StringTime( <time> )  . convert hour-min-sec-milli into a readable string
  576. #F  HMSMSec( <sec> )  . . . . . . . .  convert seconds into hour-min-sec-mill
  577. #F  SecHMSM( <hmsm> ) . . . . . . . . convert hour-min-sec-milli into seconds
  578. ##
  579. HMSMSec := function ( sec )
  580.     local  hour, minute, second, milli;
  581.     hour   := QuoInt( sec, 3600000 );
  582.     minute := QuoInt( sec,   60000 ) mod 60;
  583.     second := QuoInt( sec,    1000 ) mod 60;
  584.     milli  :=         sec            mod 1000;
  585.     return [ hour, minute, second, milli ];
  586. end;
  587.  
  588. SecHMSM := function ( hmsm )
  589.     return 3600000*hmsm[1] + 60000*hmsm[2] + 1000*hmsm[3] + hmsm[4];
  590. end;
  591.  
  592. StringTime := function ( time )
  593.     if IsInt( time )  then time := HMSMSec( time );  fi;
  594.     return ConcatenationString(
  595.         String(time[1],2), ":", String(time[2],2), ":",
  596.         String(time[3],2), ".", String(time[4],3) );
  597. end;
  598.  
  599.  
  600. #############################################################################
  601. ##
  602. #F  StringPP( <int> ) . . . . . . . . . . . . . . . . . . . . P1^E1 ... Pn^En
  603. ##
  604. StringPP := function( n )
  605.     local   l, p, e, i, prime, str;
  606.  
  607.     if n = 1  then
  608.         return "1";
  609.     elif n = -1  then
  610.         return "-1";
  611.     elif n = 0  then
  612.         return "0";
  613.     elif n < 0  then
  614.         l := Factors( -n );
  615.     str := "-";
  616.     else
  617.         l := Factors( n );
  618.     str := "";
  619.     fi;
  620.     p := [];
  621.     e := [];
  622.     for prime  in Set( l )  do
  623.         Add( p, prime );
  624.         Add( e, Length( Filtered( l, x -> prime = x ) ) );
  625.     od;
  626.  
  627.     if e[ 1 ] = 1   then
  628.         str := ConcatenationString( str, String( p[ 1 ] ) );
  629.     else
  630.         str := ConcatenationString( str, String( p[ 1 ] ),
  631.                                      "^", String( e[ 1 ] ) );
  632.     fi;
  633.  
  634.     for i  in [ 2 .. Length( p ) ]  do
  635.         if e[ i ] = 1  then
  636.         str := ConcatenationString( str, "*", String( p[ i ] ) );
  637.         else
  638.         str := ConcatenationString( str, "*", String( p[ i ] ),
  639.                                          "^", String( e[ i ] ) );
  640.         fi;
  641.     od;
  642.  
  643.     return str;
  644.  
  645. end;
  646.  
  647.  
  648. #############################################################################
  649. ##
  650. #F  Ordinal(<n>)  . . . . . . . . . . . . . . ordinal of an integer as string
  651. ##
  652. Ordinal := function ( n )
  653.     local   str;
  654.     if   n mod 10 = 1  and n <> 11  then
  655.         str := ConcatenationString( StringInt(n), "st" );
  656.     elif n mod 10 = 2  and n <> 12  then
  657.         str := ConcatenationString( StringInt(n), "nd" );
  658.     elif n mod 10 = 3  and n <> 13  then
  659.         str := ConcatenationString( StringInt(n), "rd" );
  660.     else
  661.         str := ConcatenationString( StringInt(n), "th" );
  662.     fi;
  663.     return str;
  664. end;
  665.  
  666.  
  667.  
  668.