diff --git a/CHANGES.md b/CHANGES.md index f575b28..71e9ecd 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,10 @@ # CHANGES log for the 'Utils' package +## Version 0.99 for GAP 4.16.1 (09/09/26) + * (09/09/26) since now using AutoDoc to extract tests from the manual, + many of the original tests are no longer needed, so removed. + * (08/09/26) extensive changes to the Download function + ## Version 0.98 for GAP 4.16.0 (04/08/26) * (04/08/26) changed Fitting series examples to just show StructureDescription diff --git a/doc/download.xml b/doc/download.xml index cf8548c..138f9f9 100644 --- a/doc/download.xml +++ b/doc/download.xml @@ -69,7 +69,31 @@ The following components are supported. and the function writes the downloaded contents to this file; the returned record does not have a result component in this case.

- If the download fails then this file is not left behind. + If the download fails then a file created by the attempt is not left + behind, unless resume is set. A file that was already there + before the call is not removed, since it is not ours to delete – + but note that a failed attempt may well have overwritten it, so its old + contents are not guaranteed either. + +resume + + If this component is bound and has the value true, + and target is given, + then a partially downloaded file is continued rather than fetched again, + and it is kept if the download fails again. +

+ Methods that cannot resume decline the request, since they would discard + the partial file that a method which can resume needs; currently the + methods based on the external programs curl and wget resume. + If none is available the download fails, and the caller can retry without + resume. +

+ It is the caller's responsibility that an existing target really is + a prefix of what url delivers; otherwise the two get concatenated. +

+ If the server does not support range requests then no data is lost: + wget fetches the file again from the start, and curl fails + and leaves the partial file alone. verifyCert diff --git a/doc/groups.xml b/doc/groups.xml index e253ca7..35f0cf2 100644 --- a/doc/groups.xml +++ b/doc/groups.xml @@ -401,24 +401,21 @@ The first of these is the zero map, the last is the identity.

gens := [ (1,2,3,4), (1,2)(3,4) ];; -gap> d8 := Group( gens );; -gap> SetName( d8, "d8" ); +gap> d8 := Group( gens );; SetName( d8, "d8" ); gap> c2 := Subgroup( d8, [ (2,4) ] );; gap> SortedList( IdempotentEndomorphismsWithImage( gens, c2 ) ); [ [ (), (2,4) ], [ (2,4), () ] ] gap> data:= IdempotentEndomorphismsData( d8 );; -gap> data.images[1]; -[ [ (), () ] ] -gap> List( data.images, Length ); -[ 1, 2, 2, 2, 2, 1 ] +gap> Length( data.images ); +6 gap> all:= IdempotentEndomorphisms( d8 );; -gap> Length( all ); -10 -gap> all[1]; -[ (1,2,3,4), (1,2)(3,4) ] -> [ (), () ] -gap> Size( Image( all[1] ) ); -1 -gap> Last( all ) = IdentityMapping( d8 ); +gap> allim := List( all, m -> MappingGeneratorsImages(m)[2] );; +gap> ## the list of 10 sets of images sorted: +gap> allimpairs := +> [ [ (), () ], [ (), (2,4) ], [ (2,4), () ], [ (), (1,3) ], [ (1,3), () ], +> [ (), (1,2)(3,4) ], [ (1,2)(3,4), (1,2)(3,4) ], [ (), (1,4)(2,3) ], +> [ (1,4)(2,3), (1,4)(2,3) ], [ (1,2,3,4), (1,2)(3,4) ] ];; +gap> ForAll( allim, m -> ( m in allimpairs ) ); true ]]> diff --git a/doc/intro.xml b/doc/intro.xml index 3556f41..071a406 100644 --- a/doc/intro.xml +++ b/doc/intro.xml @@ -2,7 +2,7 @@ - + @@ -78,8 +78,16 @@ may be rebuilt as follows: gap> ReadPackage( "utils", "makedoc.g" ); ]]>

-It is possible to check that the package has been installed correctly -by running the test files (which terminates the ⪆ session): + +This package now uses the AutoDoc package +to extract test files from the examples in the manual. +Many of the original test files, +which duplicated these examples, have now been deleted. +The user can ensure that the full set of test files exists on their system +by rebuilding the manual, as described above. +

+It is then possible to check that the package has been installed correctly +by running the test suite (this terminates the ⪆ session):

ReadPackage( "utils", "tst/testall.g" ); diff --git a/doc/iterator.xml b/doc/iterator.xml index 9eba053..ea4589a 100644 --- a/doc/iterator.xml +++ b/doc/iterator.xml @@ -2,7 +2,7 @@ - + @@ -54,6 +54,11 @@ gap> AllIsomorphisms( G, s3 ); gap> iter := AllIsomorphismsIterator( G, s3 );; gap> for h in iter do Print( ImageElm( h, G.1 ) = (6,7), ", " ); od; true, false, false, true, false, false, +gap> s4 := Group( (1,2),(2,3),(3,4) );; +gap> AllIsomorphismsNumber( s3, s4 ); +0 +gap> AllIsomorphisms( s3, s4 ); +[ ] ]]> @@ -156,6 +161,24 @@ gap> NextIterator( pairs4 ); gap> IsDoneIterator( pairs4 ); true ]]> + +Some trivial cases: + iter0 := IteratorList( [ ] );; +gap> iter4 := IteratorList( [ 4 ] );; +gap> cart := CartesianIterator( iter0, iter0 );; +gap> IsDoneIterator( cart ); +true +gap> cart := CartesianIterator( iter0, iter4 );; +gap> IsDoneIterator( cart ); +true +gap> cart := CartesianIterator( iter4, iter0 );; +gap> IsDoneIterator( cart ); +true +gap> pairs0 := UnorderedPairsIterator( iter0 );; +gap> IsDoneIterator( pairs0 ); +true +]]> diff --git a/doc/matrix.xml b/doc/matrix.xml index 13f7433..894c044 100644 --- a/doc/matrix.xml +++ b/doc/matrix.xml @@ -2,7 +2,7 @@ - + @@ -83,6 +83,14 @@ gap> Display( M8 ); gap> L8 := DirectSumDecompositionMatrices( M8 );; gap> Length( L8 ); 16 + +gap> M3 := [ [0,0,7,0,0,0,0], [0,0,0,8,0,0,0], [0,0,0,0,9,0,0] ];; +gap> Display( M3 ); +[ [ 0, 0, 7, 0, 0, 0, 0 ], + [ 0, 0, 0, 8, 0, 0, 0 ], + [ 0, 0, 0, 0, 9, 0, 0] ] +gap> L3 := DirectSumDecompositionMatrices( M3 ); +[ [ [ [ 0, 0, 7 ] ], [ [ 8 ] ], [ [ 9, 0, 0 ] ] ] ] ]]>

diff --git a/lib/download.gi b/lib/download.gi index 4b9a6ce..de3fa60 100644 --- a/lib/download.gi +++ b/lib/download.gi @@ -36,15 +36,29 @@ Add( Download_Methods, rec( download:= function( url, opt ) local res; + if IsBound( opt.resume ) and opt.resume = true and + IsBound( opt.target ) and IsString( opt.target ) then + # Declining matters: this method would truncate the partial file that a + # method which can resume needs. + return rec( success:= false, error:= "no support for resuming" ); + fi; + opt:= ShallowCopy( opt ); if not IsBound( opt.failOnError ) then opt.failOnError:= true; fi; - # 'DownloadURL' handles the options 'verifyCert' and 'maxTime'. + # 'DownloadURL' handles the options 'verifyCert' and 'maxTime'. Newer + # versions can write the body to a file themselves, which keeps it out of + # memory; they call that option 'targetFile'. + if IsBound( opt.target ) and IsString( opt.target ) then + opt.targetFile:= opt.target; + fi; res:= ValueGlobal( "DownloadURL" )( url, opt ); if res.success = true and - IsBound( opt.target ) and IsString( opt.target ) then + IsBound( opt.target ) and IsString( opt.target ) and + IsBound( res.result ) then + # an older 'DownloadURL' ignored 'targetFile' and returned the contents FileString( opt.target, res.result ); Unbind( res.result ); fi; @@ -61,6 +75,11 @@ Add( Download_Methods, rec( return rec( success:= false, error:= "protocol is not http" ); elif IsBound( opt.maxTime ) and opt.maxTime <> 0 then return rec( success:= false, error:= "no support for given timeout" ); + elif IsBound( opt.resume ) and opt.resume = true and + IsBound( opt.target ) and IsString( opt.target ) then + # No range request, so this would overwrite the partial file that a + # method which can resume needs. + return rec( success:= false, error:= "no support for resuming" ); fi; # Split the URL after 'http://' into the authority and HTTP request target. @@ -164,6 +183,10 @@ Add( Download_Methods, rec( else args:= [ "--quiet", "-O", "-", url ]; fi; + if IsBound( opt.resume ) and opt.resume = true and + IsBound( opt.target ) and IsString( opt.target ) then + Add( args, "-c" ); + fi; if IsBound( opt.verifyCert ) and opt.verifyCert = false then Add( args, "--no-check-certificate" ); fi; @@ -173,11 +196,6 @@ Add( Download_Methods, rec( code:= Process( DirectoryCurrent(), exec, InputTextNone(), outstream, args ); CloseStream( outstream ); if code <> 0 then - # wget may have created the target file; try to remove it - if IsBound( opt.target ) and IsString( opt.target ) and - IsExistingFile( opt.target ) and RemoveFile( opt.target ) <> true then - Error( "Download cannot remove unwanted file ", opt.target ); - fi; return rec( success:= false, error:= Concatenation( "Process returned ", String( code ) ) ); elif not ( IsBound( opt.target ) and IsString( opt.target ) ) then @@ -211,6 +229,11 @@ Add( Download_Methods, rec( else Add( args, "-" ); fi; + if IsBound( opt.resume ) and opt.resume = true and + IsBound( opt.target ) and IsString( opt.target ) then + Add( args, "-C" ); + Add( args, "-" ); + fi; if IsBound( opt.maxTime ) and IsPosInt( opt.maxTime ) then Add( args, "--max-time" ); Add( args, opt.maxTime ); @@ -245,7 +268,7 @@ InstallMethod( Download, InstallMethod( Download, [ "IsString", "IsRecord" ], function( url, opt ) - local timeout, errors, r, res; + local timeout, hadTarget, errors, r, res; # Do not modify the caller's record when filling in the defaults below. opt:= ShallowCopy( opt ); @@ -264,6 +287,11 @@ InstallMethod( Download, fi; fi; + # Whether the caller brought the target file, as opposed to a method + # creating it below. We remove only what we created. + hadTarget:= IsBound( opt.target ) and IsString( opt.target ) and + IsExistingFile( opt.target ); + # Run over the methods. errors:= []; for r in Download_Methods do @@ -275,8 +303,12 @@ InstallMethod( Download, fi; # A failed method may have left a partial or bogus target file behind. # Remove it here, so that the guarantee holds for every method, - # including ones added to 'Download_Methods' from outside. - if IsBound( opt.target ) and IsString( opt.target ) and + # including ones added to 'Download_Methods' from outside. Two + # files are not ours to delete: one the caller brought, and the + # partial one that a resumed download continues from. + if not hadTarget and + not ( IsBound( opt.resume ) and opt.resume = true ) and + IsBound( opt.target ) and IsString( opt.target ) and IsExistingFile( opt.target ) then RemoveFile( opt.target ); fi; diff --git a/tst/download.tst b/tst/download.tst index 02856d3..e80c402 100644 --- a/tst/download.tst +++ b/tst/download.tst @@ -1,4 +1,4 @@ -#@local meths, i, urls, pair, url, expected, res1, good1, n, file, res2, good2, contents, r, res3, good3, bad, server, baseurl, iometh, opt, oldpref +#@local meths, i, urls, pair, url, expected, res1, good1, n, file, res2, good2, contents, r, res3, good3, bad, server, baseurl, iometh, opt, oldpref, resumers, name ############################################################################ ## #W download.tst Utils Package Thomas Breuer @@ -164,6 +164,66 @@ true gap> IsExistingFile( file ); false +## 'resume' continues a partial file rather than fetching it again. The +## test server answers a Range request with the remainder in upper case, so +## a resumed download is distinguishable from a restarted one. +gap> resumers:= Filtered( meths, r -> r.name in [ "via curl", "via wget" ] );; +gap> for r in resumers do +> FileString( file, "abcde" );; +> res1:= r.download( Concatenation( baseurl, "/resumable" ), +> rec( target:= file, resume:= true ) ); +> if res1.success <> true then +> Print( "resume failed for ", r.name, ": ", res1.error, "\n" ); +> elif StringFile( file ) <> "abcdeFGHIJKLMNOPQRST" then +> Print( "did not resume for ", r.name, ": ", StringFile( file ), "\n" ); +> fi; +> RemoveFile( file ); +> od; + +## Without 'resume' the target is replaced, not appended to. +gap> FileString( file, "abcde" );; +gap> res1:= Download( Concatenation( baseurl, "/resumable" ), +> rec( target:= file ) );; +gap> StringFile( file ); +"abcdefghijklmnopqrst" + +## Methods that cannot resume decline, rather than discarding the partial +## file that a method which can resume needs. +gap> iometh.download( Concatenation( baseurl, "/resumable" ), +> rec( target:= file, resume:= true ) ).error; +"no support for resuming" + +## With 'resume', a failed download keeps the partial file to continue from. +## Without this, the clean-up in 'Download' would throw away the very thing +## the next attempt is meant to continue. +gap> FileString( file, "abcde" );; +gap> res1:= Download( Concatenation( baseurl, "/missing" ), +> rec( target:= file, resume:= true ) );; +gap> res1.success; +false +gap> StringFile( file ); +"abcde" +gap> RemoveFile( file );; + +## 'resume' is only declined when it is actually requested: a method must +## not be skipped merely because the component is present. +gap> res1:= Download( Concatenation( baseurl, "/file" ), +> rec( target:= file, resume:= false ) );; +gap> res1.success; +true +gap> RemoveFile( file );; + +## A file that was already there is not removed by a failed download: +## 'Download' only cleans up after itself. +gap> FileString( file, "mine" );; +gap> res1:= Download( Concatenation( baseurl, "/missing" ), +> rec( target:= file ) );; +gap> res1.success; +false +gap> IsExistingFile( file ); +true +gap> RemoveFile( file );; + ## test errors and redirects gap> res1:= Download( Concatenation( baseurl, "/missing" ) );; gap> res1.success = false; diff --git a/tst/gslp.tst b/tst/gslp.tst index 5d4f7bf..b2ec1a9 100644 --- a/tst/gslp.tst +++ b/tst/gslp.tst @@ -5,6 +5,9 @@ ## #Y Copyright (C) 2026, The GAP Group ## +## +## some tests other than those in the manual + gap> START_TEST( "gslp.tst" ); # diff --git a/tst/http-server.g b/tst/http-server.g index 40a532a..ad2b0b4 100644 --- a/tst/http-server.g +++ b/tst/http-server.g @@ -4,7 +4,7 @@ ## BindGlobal( "UTILS_HandleHTTPTestRequest", function( listener, socket ) - local connection, line, parts, uri, body, status, location; + local connection, line, parts, uri, body, status, location, range, from; IO_close( listener ); connection:= IO_WrapFD( socket, IO.DefaultBufSize, IO.DefaultBufSize ); @@ -16,8 +16,12 @@ BindGlobal( "UTILS_HandleHTTPTestRequest", function( listener, socket ) fi; uri:= parts[2]; + range:= fail; repeat line:= IO_ReadLine( connection ); + if IsString( line ) and StartsWith( LowercaseString( line ), "range:" ) then + range:= line; + fi; until line = fail or line = "" or line = "\n" or line = "\r\n"; body:= "download test response\n"; @@ -32,6 +36,27 @@ BindGlobal( "UTILS_HandleHTTPTestRequest", function( listener, socket ) body:= ""; status:= "302 Found"; location:= "Location: /success\r\n"; + elif StartsWith( uri, "/resumable" ) then + # Answer a 'Range: bytes=N-' request with the remainder, but in upper + # case, so that a test can tell a resumed download from a restarted one. + body:= "abcdefghijklmnopqrst"; + if range <> fail then + from:= Int( Filtered( range, c -> c in "0123456789" ) ); + if from <> fail and 0 < from and from < Length( body ) then + IO_Write( connection, + "HTTP/1.1 206 Partial Content\r\n", + "Content-Type: text/plain\r\n", + "Content-Range: bytes ", String( from ), "-", + String( Length( body ) - 1 ), "/", String( Length( body ) ), + "\r\n", + "Content-Length: ", String( Length( body ) - from ), "\r\n", + "Connection: close\r\n\r\n", + UppercaseString( body{ [ from+1 .. Length( body ) ] } ) ); + IO_Flush( connection ); + IO_Close( connection ); + IO_exit( 0 ); + fi; + fi; fi; IO_Write( connection, diff --git a/tst/iterator.tst b/tst/iterator.tst deleted file mode 100644 index 36a8799..0000000 --- a/tst/iterator.tst +++ /dev/null @@ -1,100 +0,0 @@ -#@local c3c3, cart, G, h, it1, it2, iter, iter0, iter4, iterL -#@local L, n, pairs0, pairs4, pairsL, s3, s4 - -############################################################################## -## -#W iterator.tst Utils Package -## -#Y Copyright (C) 2015-2022, The GAP Group -## - -gap> ReadPackage( "utils", "tst/loadall.g" );; -gap> UtilsLoadingComplete; -true - -## SubSection 7.1.1 -gap> G := DihedralGroup(6);; -gap> s3 := Group( (5,6), (6,7) );; -gap> iter := AllIsomorphismsIterator( G, s3 );; -gap> NextIterator( iter ); -[ f1, f2 ] -> [ (6,7), (5,6,7) ] -gap> n := AllIsomorphismsNumber( G, s3 ); -6 -gap> AllIsomorphisms( G, s3 ); -[ [ f1, f2 ] -> [ (6,7), (5,6,7) ], [ f1, f2 ] -> [ (5,7), (5,6,7) ], - [ f1, f2 ] -> [ (5,6), (5,7,6) ], [ f1, f2 ] -> [ (6,7), (5,7,6) ], - [ f1, f2 ] -> [ (5,7), (5,7,6) ], [ f1, f2 ] -> [ (5,6), (5,6,7) ] ] -gap> iter := AllIsomorphismsIterator( G, s3 );; -gap> for h in iter do Print( ImageElm( h, G.1 ) = (6,7), ", " ); od; -true, false, false, true, false, false, -gap> s4 := Group( (1,2),(2,3),(3,4) );; -gap> AllIsomorphismsNumber( s3, s4 ); -0 -gap> AllIsomorphisms( s3, s4 ); -[ ] - -## SubSection 7.1.2 -gap> c3c3 := Group( (1,2,3), (4,5,6) );; -gap> iter := AllSubgroupsIterator( c3c3 ); - -gap> while not IsDoneIterator(iter) do Print(NextIterator(iter),"\n"); od; -Group( () ) -Group( [ (4,5,6) ] ) -Group( [ (1,2,3) ] ) -Group( [ (1,2,3)(4,5,6) ] ) -Group( [ (1,3,2)(4,5,6) ] ) -Group( [ (4,5,6), (1,2,3) ] ) - -## Subsection 7.2.1 -gap> it1 := Iterator( [ 1, 2, 3 ] );; -gap> it2 := Iterator( [ 4, 5, 6 ] );; -gap> iter := CartesianIterator( it1, it2 );; -gap> while not IsDoneIterator(iter) do Print(NextIterator(iter),"\n"); od; -[ 1, 4 ] -[ 1, 5 ] -[ 1, 6 ] -[ 2, 4 ] -[ 2, 5 ] -[ 2, 6 ] -[ 3, 4 ] -[ 3, 5 ] -[ 3, 6 ] -gap> ## try some trivial cases -gap> iter0 := IteratorList( [ ] );; -gap> iter4 := IteratorList( [ 4 ] );; -gap> cart := CartesianIterator( iter0, iter0 );; -gap> IsDoneIterator( cart ); -true -gap> cart := CartesianIterator( iter0, iter4 );; -gap> IsDoneIterator( cart ); -true -gap> cart := CartesianIterator( iter4, iter0 );; -gap> IsDoneIterator( cart ); -true - -## Subsection 6.2.2 -gap> L := [6,7,8,9];; -gap> iterL := IteratorList( L );; -gap> pairsL := UnorderedPairsIterator( iterL );; -gap> while not IsDoneIterator(pairsL) do Print(NextIterator(pairsL),"\n"); od; -[ 6, 6 ] -[ 6, 7 ] -[ 7, 7 ] -[ 6, 8 ] -[ 7, 8 ] -[ 8, 8 ] -[ 6, 9 ] -[ 7, 9 ] -[ 8, 9 ] -[ 9, 9 ] -gap> iter4 := IteratorList( [ 4 ] ); - -gap> pairs4 := UnorderedPairsIterator(iter4); - -gap> NextIterator( pairs4 ); -[ 4, 4 ] -gap> IsDoneIterator( pairs4 ); -true -gap> pairs0 := UnorderedPairsIterator( iter0 );; -gap> IsDoneIterator( pairs0 ); -true diff --git a/tst/lists.tst b/tst/lists.tst deleted file mode 100644 index 6407a69..0000000 --- a/tst/lists.tst +++ /dev/null @@ -1,109 +0,0 @@ -#@local C, c4, d16, gens, i, J, K, L, n, P, trans - -############################################################################## -## -#W lists.tst Utils Package -## -#Y Copyright (C) 2015-2022, The GAP Group -## - -gap> ReadPackage( "utils", "tst/loadall.g" );; -gap> UtilsLoadingComplete; -true - -## SubSection 3.1.1 -gap> List( [1..12], n->n^3 ); -[ 1, 8, 27, 64, 125, 216, 343, 512, 729, 1000, 1331, 1728 ] -gap> DifferencesList( last ); -[ 7, 19, 37, 61, 91, 127, 169, 217, 271, 331, 397 ] -gap> DifferencesList( last ); -[ 12, 18, 24, 30, 36, 42, 48, 54, 60, 66 ] -gap> DifferencesList( last ); -[ 6, 6, 6, 6, 6, 6, 6, 6, 6 ] -gap> L := [ 0 ];; -gap> DifferencesList( L ); -[ ] -gap> L := [ ];; -gap> DifferencesList( L ); -[ ] - -## SubSection 3.1.2 -gap> List( [0..10], n -> Factorial(n) ); -[ 1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800 ] -gap> QuotientsList( last ); -[ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] -gap> L := [ 1, 3, 5, -1, -3, -5 ];; -gap> QuotientsList( L ); -[ 3, 5/3, -1/5, 3, 5/3 ] -gap> FloatQuotientsList( L ); -[ 3., 1.66667, -0.2, 3., 1.66667 ] -gap> QuotientsList( [ 2, 1, 0, -1, -2 ] ); -[ 1/2, 0, fail, 2 ] -gap> FloatQuotientsList( [1..10] ); -[ 2., 1.5, 1.33333, 1.25, 1.2, 1.16667, 1.14286, 1.125, 1.11111 ] -gap> Product( last ); -10. - -## SubSection 3.1.3 -gap> L := [1..20];; L[1]:=13;; -gap> for i in [1..19] do -> if IsOddInt(L[i]) then L[i+1]:=3*L[i]+1; else L[i+1]:=L[i]/2; fi; -> od; -gap> L; -[ 13, 40, 20, 10, 5, 16, 8, 4, 2, 1, 4, 2, 1, 4, 2, 1, 4, 2, 1, 4 ] -gap> SearchCycle( L ); -[ 1, 4, 2 ] -gap> n := 1;; L := [n];; -gap> for i in [1..100] do n:=(n^2+1) mod 1093; Add(L,n); od; -gap> L; -[ 1, 2, 5, 26, 677, 363, 610, 481, 739, 715, 795, 272, 754, 157, 604, 848, - 1004, 271, 211, 802, 521, 378, 795, 272, 754, 157, 604, 848, 1004, 271, - 211, 802, 521, 378, 795, 272, 754, 157, 604, 848, 1004, 271, 211, 802, 521, - 378, 795, 272, 754, 157, 604, 848, 1004, 271, 211, 802, 521, 378, 795, 272, - 754, 157, 604, 848, 1004, 271, 211, 802, 521, 378, 795, 272, 754, 157, 604, - 848, 1004, 271, 211, 802, 521, 378, 795, 272, 754, 157, 604, 848, 1004, - 271, 211, 802, 521, 378, 795, 272, 754, 157, 604, 848, 1004 ] -gap> C := SearchCycle( L ); -[ 157, 604, 848, 1004, 271, 211, 802, 521, 378, 795, 272, 754 ] -gap> P := Positions( L, 157 ); -[ 14, 26, 38, 50, 62, 74, 86, 98 ] -gap> Length( C ); DifferencesList( P ); -12 -[ 12, 12, 12, 12, 12, 12, 12 ] - -## SubSection 3.1.4 -## this manual example not tested as answers vary from run to run -## gap> RandomCombination( [1..49], 6 ); -## [ 3, 5, 21, 24, 27, 31 ] - - -## SubSection 3.2.1 -gap> J := [ [1,2,3], [3,4], [3,4], [1,2,4] ];; -gap> DistinctRepresentatives( J ); -[ 1, 3, 4, 2 ] -gap> K := [ [3,4], [1,2], [2,3], [2,3,4] ];; -gap> CommonRepresentatives( J, K ); -[ [ 3, 3, 3, 1 ], [ 1, 3, 4, 2 ] ] -gap> d16 := DihedralGroup( IsPermGroup, 16 ); SetName( d16, "d16" ); -Group([ (1,2,3,4,5,6,7,8), (2,8)(3,7)(4,6) ]) -gap> c4 := Subgroup( d16, [ d16.1^2 ] ); SetName( c4, "c4" ); -Group([ (1,3,5,7)(2,4,6,8) ]) -gap> RightCosets( d16, c4 ); -[ RightCoset(c4,()), RightCoset(c4,(2,8)(3,7)(4,6)), RightCoset(c4,(1,8,7,6,5, - 4,3,2)), RightCoset(c4,(1,8)(2,7)(3,6)(4,5)) ] -gap> trans := CommonTransversal( d16, c4 ); -[ (), (2,8)(3,7)(4,6), (1,2,3,4,5,6,7,8), (1,2)(3,8)(4,7)(5,6) ] -gap> IsCommonTransversal( d16, c4, trans ); -true - -## SubSection 3.3.1 -gap> gens := GeneratorsOfGroup( DihedralGroup(12) ); -[ f1, f2, f3 ] -gap> String( gens ); -"[ f1, f2, f3 ]" -gap> BlankFreeString( gens ); -"[f1,f2,f3]" - -############################################################################# -## -#E lists.tst . . . . . . . . . . . . . . . . . . . . . . . . . . . ends here diff --git a/tst/manual.g b/tst/manual.g new file mode 100755 index 0000000..e8aedd6 --- /dev/null +++ b/tst/manual.g @@ -0,0 +1,24 @@ +## utils.g +LoadPackage("utils"); +Print( "running utils01.tst\n" ); +Test( "tst/utils01.tst", rec(compareFunction := "uptowhitespace") ); +Print( "running utils02.tst\n" ); +Test( "tst/utils02.tst", rec(compareFunction := "uptowhitespace") ); +Print( "running utils03.tst\n" ); +Test( "tst/utils03.tst", rec(compareFunction := "uptowhitespace") ); +Print( "running utils04.tst\n" ); +Test( "tst/utils04.tst", rec(compareFunction := "uptowhitespace") ); +Print( "running utils05.tst\n" ); +Test( "tst/utils05.tst", rec(compareFunction := "uptowhitespace") ); +Print( "running utils06.tst\n" ); +Test( "tst/utils06.tst", rec(compareFunction := "uptowhitespace") ); +Print( "running utils07.tst\n" ); +Test( "tst/utils07.tst", rec(compareFunction := "uptowhitespace") ); +Print( "running utils08.tst\n" ); +Test( "tst/utils08.tst", rec(compareFunction := "uptowhitespace") ); +Print( "running utils09.tst\n" ); +Test( "tst/utils09.tst", rec(compareFunction := "uptowhitespace") ); +Print( "running utils10.tst\n" ); +Test( "tst/utils10.tst", rec(compareFunction := "uptowhitespace") ); +Print( "running utils11.tst\n" ); +Test( "tst/utils11.tst", rec(compareFunction := "uptowhitespace") ); diff --git a/tst/matrix.tst b/tst/matrix.tst deleted file mode 100644 index 78da41d..0000000 --- a/tst/matrix.tst +++ /dev/null @@ -1,80 +0,0 @@ -#@local M6, L6, M4, L4, M8, L8, L, A, M3, L3, M5, L5; -############################################################################## -## -#W matrix.tst Utils Package -## -#Y Copyright (C) 2015-2023, The GAP Group -## - -gap> ReadPackage( "utils", "tst/loadall.g" );; -gap> UtilsLoadingComplete; -true - -## SubSection 9.1.1 -gap> M6 := [ [1,2,0,0,0,0], [3,4,0,0,0,0], [5,6,0,0,0,0], -> [0,0,9,0,0,0], [0,0,0,1,2,3], [0,0,0,4,5,6] ];; -gap> Display( M6 ); -[ [ 1, 2, 0, 0, 0, 0 ], - [ 3, 4, 0, 0, 0, 0 ], - [ 5, 6, 0, 0, 0, 0 ], - [ 0, 0, 9, 0, 0, 0 ], - [ 0, 0, 0, 1, 2, 3 ], - [ 0, 0, 0, 4, 5, 6 ] ] -gap> L6 := DirectSumDecompositionMatrices( M6 ); -[ [ [ [ 1, 2 ], [ 3, 4 ], [ 5, 6 ] ], [ [ 9 ] ], [ [ 1, 2, 3 ], [ 4, 5, 6 ] ] - ] ] - -gap> M4 := [ [0,3,0,0], [0,0,0,0], [0,0,0,0], [0,0,4,0] ];; -gap> Display( M4 ); -[ [ 0, 3, 0, 0 ], - [ 0, 0, 0, 0 ], - [ 0, 0, 0, 0 ], - [ 0, 0, 4, 0 ] ] -gap> L4 := DirectSumDecompositionMatrices( M4 ); -[ [ [ [ 0, 3 ] ], [ [ 0, 0 ], [ 0, 0 ], [ 4, 0 ] ] ], - [ [ [ 0, 3 ], [ 0, 0 ] ], [ [ 0, 0 ], [ 4, 0 ] ] ], - [ [ [ 0, 3 ], [ 0, 0 ], [ 0, 0 ] ], [ [ 4, 0 ] ] ] ] -gap> for L in L4 do -> A := DirectSumMat( L );; -> if ( A = M4 ) then Print( "yes, A = M4\n" ); fi; -> od; -yes, A = M4 -yes, A = M4 -yes, A = M4 - -gap> M8 := DirectSumMat( M4, M4 );; -gap> Display( M8 ); -[ [ 0, 3, 0, 0, 0, 0, 0, 0 ], - [ 0, 0, 0, 0, 0, 0, 0, 0 ], - [ 0, 0, 0, 0, 0, 0, 0, 0 ], - [ 0, 0, 4, 0, 0, 0, 0, 0 ], - [ 0, 0, 0, 0, 0, 3, 0, 0 ], - [ 0, 0, 0, 0, 0, 0, 0, 0 ], - [ 0, 0, 0, 0, 0, 0, 0, 0 ], - [ 0, 0, 0, 0, 0, 0, 4, 0 ] ] -gap> L8 := DirectSumDecompositionMatrices( M8 );; -gap> Length( L8 ); -16 - -gap> M3 := [ [0,0,0,7,0,0,0], [0,0,0,0,8,0,0], [0,0,0,0,0,9,0] ];; -gap> Display( M3 ); -[ [ 0, 0, 0, 7, 0, 0, 0 ], - [ 0, 0, 0, 0, 8, 0, 0 ], - [ 0, 0, 0, 0, 0, 9, 0 ] ] -gap> L3 := DirectSumDecompositionMatrices( M3 ); -[ [ [ [ 0, 0, 0, 7 ] ], [ [ 8 ] ], [ [ 9, 0 ] ] ] ] - -gap> M5 := [ [1,2,0,0,0], [3,4,0,0,0], [0,0,0,0,0], -> [0,0,0,6,7], [0,0,0,8,9] ];; -gap> Display(M5); -[ [ 1, 2, 0, 0, 0 ], - [ 3, 4, 0, 0, 0 ], - [ 0, 0, 0, 0, 0 ], - [ 0, 0, 0, 6, 7 ], - [ 0, 0, 0, 8, 9 ] ] -gap> L5 := DirectSumDecompositionMatrices( M5 ); -[ [ [ [ 1, 2 ], [ 3, 4 ] ], [ [ 0 ] ], [ [ 6, 7 ], [ 8, 9 ] ] ] ] - -############################################################################# -## -#E matrix.tst . . . . . . . . . . . . . . . . . . . . . . . . . . ends here diff --git a/tst/number.tst b/tst/number.tst deleted file mode 100644 index b61c7b8..0000000 --- a/tst/number.tst +++ /dev/null @@ -1,80 +0,0 @@ -#@local i, iter, n, p, sum - -############################################################################## -## -#W number.tst Utils Package Stefan Kohl -## -#Y Copyright (C) 2015-2022, The GAP Group -## - -gap> ReadPackage( "utils", "tst/loadall.g" );; -gap> UtilsLoadingComplete; -true - -## SubSection 4.1.1 -gap> AllSmoothIntegers( 3, 1000 ); -[ 1, 2, 3, 4, 6, 8, 9, 12, 16, 18, 24, 27, 32, 36, 48, 54, 64, 72, 81, 96, - 108, 128, 144, 162, 192, 216, 243, 256, 288, 324, 384, 432, 486, 512, 576, - 648, 729, 768, 864, 972 ] -gap> AllSmoothIntegers( [5,11,17], 1000 ); -[ 1, 5, 11, 17, 25, 55, 85, 121, 125, 187, 275, 289, 425, 605, 625, 935 ] -gap> Length( last ); -16 -gap> List( [3..20], n -> Length( AllSmoothIntegers( [5,11,17], 10^n ) ) ); -[ 16, 29, 50, 78, 114, 155, 212, 282, 359, 452, 565, 691, 831, 992, 1173, - 1374, 1595, 1843 ] - -## SubSection 4.1.2 -gap> AllProducts([1..4],3); -[ 1, 2, 3, 4, 2, 4, 6, 8, 3, 6, 9, 12, 4, 8, 12, 16, 2, 4, 6, 8, 4, 8, 12, - 16, 6, 12, 18, 24, 8, 16, 24, 32, 3, 6, 9, 12, 6, 12, 18, 24, 9, 18, 27, - 36, 12, 24, 36, 48, 4, 8, 12, 16, 8, 16, 24, 32, 12, 24, 36, 48, 16, 32, - 48, 64 ] -gap> Set(last); -[ 1, 2, 3, 4, 6, 8, 9, 12, 16, 18, 24, 27, 32, 36, 48, 64 ] -gap> AllProducts( [(1,2,3),(2,3,4)], 2 ); -[ (2,4,3), (1,2)(3,4), (1,3)(2,4), (1,3,2) ] - -## SubSection 4.1.3 -gap> RestrictedPartitions( 20, [4..10] ); -[ [ 4, 4, 4, 4, 4 ], [ 5, 5, 5, 5 ], [ 6, 5, 5, 4 ], [ 6, 6, 4, 4 ], - [ 7, 5, 4, 4 ], [ 7, 7, 6 ], [ 8, 4, 4, 4 ], [ 8, 6, 6 ], [ 8, 7, 5 ], - [ 8, 8, 4 ], [ 9, 6, 5 ], [ 9, 7, 4 ], [ 10, 5, 5 ], [ 10, 6, 4 ], - [ 10, 10 ] ] -gap> RestrictedPartitionsWithoutRepetitions( 20, [4..10] ); -[ [ 10, 6, 4 ], [ 9, 7, 4 ], [ 9, 6, 5 ], [ 8, 7, 5 ] ] -gap> RestrictedPartitionsWithoutRepetitions( 10^2, List([1..10], n->n^2 ) ); -[ [ 100 ], [ 64, 36 ], [ 49, 25, 16, 9, 1 ] ] - -## SubSection 4.1.4 -gap> n := 2^251;; -gap> NextProbablyPrimeInt( n ); -3618502788666131106986593281521497120414687020801267626233049500247285301313 - -## SubSection 4.1.6 -gap> iter := PrimeNumbersIterator();; -gap> for i in [1..100] do p := NextIterator(iter); od; -gap> p; -541 -gap> sum := 0;; -gap> ## "prime number race" 1 vs. 3 mod 4 -gap> for p in PrimeNumbersIterator() do -> if p <> 2 then sum := sum + E(4)^(p-1); fi; -> if sum > 0 then break; fi; -> od; -gap> p; -26861 - -## this final example takes quite a while: use examples/number.g -## gap> sum := 0;; -## gap> ## "prime number race" 1 vs. 5 mod 8 -## gap> for p in PrimeNumbersIterator() do -## > if p mod 8 in [1,5] then sum := sum + E(4)^((p-1)/2); fi; -## > if sum > 0 then break; fi; -## > od; -## gap> p; -## 588067889 - -############################################################################# -## -#E number.tst . . . . . . . . . . . . . . . . . . . . . . . . . . ends here diff --git a/tst/others.tst b/tst/others.tst deleted file mode 100644 index 4a07480..0000000 --- a/tst/others.tst +++ /dev/null @@ -1,79 +0,0 @@ -#@local a, F2, f, g, relq8, q8, M, s1, n1, n2, N, s2; -############################################################################ -## -#W others.tst Utils Package -## -#Y Copyright (C) 2015-2025, The GAP Group -## - -gap> ReadPackage( "utils", "tst/loadall.g" );; -gap> UtilsLoadingComplete; -true - -## SubSection 10.1.1 -## this manual example is not tested to avoid creating files triv.* -## gap> LogTo( "triv.log" ); -## gap> a := 33^5; -## 39135393 -## gap> LogTo(); -## gap> Log2HTML( "triv.log" ); - -## SubSection 10.2.1 -gap> IntOrInfinityToLaTeX( 10^3 ); -"1000" -gap> IntOrInfinityToLaTeX( infinity ); -"\\infty" - -## SubSection 10.2.2 -gap> LaTeXStringFactorsInt( Factorial(12) ); -"2^{10} \\cdot 3^5 \\cdot 5^2 \\cdot 7 \\cdot 11" - -## SubSection 10.3.1 -gap> ## permutation groups -gap> ConvertToMagmaInputString( Group( (1,2,3,4,5), (3,4,5) ) ); -"PermutationGroup<5|(1,2,3,4,5),\n(3,4,5)>;\n" -gap> ConvertToMagmaInputString( Group( (1,2,3,4,5) ), "c5" ); -"c5 := PermutationGroup<5|(1,2,3,4,5)>;\n" -gap> ## pc-group -gap> ConvertToMagmaInputString( DihedralGroup( IsPcGroup, 10 ) ); -"PolycyclicGroup< f1,f2 |\nf1^2,\nf2^5,\nf2^f1 = f2^4\n>;\n" -gap> ## fp-group -gap> F2 := FreeGroup( 2 );; -gap> f := F2.1;; g := F2.2;; -gap> relq8 := [ f^4, g^4, f*g*f*g^-1, f^2*g^2 ];; -gap> q8 := F2/relq8;; -gap> ConvertToMagmaInputString( q8 ); -no conversion function yet available for fp-groups -fail -gap> ## matrix group -gap> M := GL(2,5);; Size(M); -480 -gap> s1 := ConvertToMagmaInputString( M ); -"F := GF(5);\nP := GL(2,F);\ngens := [\nP![2,0,0,1],\nP![4,1,4,0]\n];\nsub

;\n" -gap> Print( s1 ); -F := GF(5); -P := GL(2,F); -gens := [ -P![2,0,0,1], -P![4,1,4,0] -]; -sub

; -gap> n1 := [ [ Z(9)^0, Z(9)^0 ], [ Z(9)^0, Z(9) ] ];; -gap> n2 := [ [ Z(9)^0, Z(9)^3 ], [ Z(9)^4, Z(9)^2 ] ];; -gap> N := Group( n1, n2 );; Size( N ); -5760 -gap> s2 := ConvertToMagmaInputString( N, "gpN" );; -gap> Print( s2 ); -F := GF(3^2); -P := GL(2,F); -w := PrimitiveElement(F); -gens := [ -P![ 1, 1, 1,w^1], -P![ 1,w^3, 2,w^2] -]; -gpN := sub

; - -############################################################################# -## -#E others.tst . . . . . . . . . . . . . . . . . . . . . . . . . . ends here diff --git a/tst/print.tst b/tst/print.tst deleted file mode 100644 index 5a8a57b..0000000 --- a/tst/print.tst +++ /dev/null @@ -1,48 +0,0 @@ -#@local L, s5 - -############################################################################## -## -#W print.tst Utils Package -## -#Y Copyright (C) 2015-2022, The GAP Group -## - -gap> ReadPackage( "utils", "tst/loadall.g" );; -gap> UtilsLoadingComplete; -true - -## SubSection 2.1.1 -gap> L := List( [1..20], n -> n^5 );; -gap> PrintSelection( L, [18..20] ); -18 : 1889568 -19 : 2476099 -20 : 3200000 -gap> PrintSelection( L, 2, 9 ); -2 : 32 -11 : 161051 -20 : 3200000 -gap> PrintSelection( L, 2, 3, 11 ); -2 : 32 -5 : 3125 -8 : 32768 -11 : 161051 -gap> s5 := SymmetricGroup( 5 );; -gap> PrintSelection( s5, [30,31,100,101] ); -30 : (1,5)(3,4) -31 : (1,5,2) -100 : (1,4,3) -101 : (1,4)(3,5) -gap> PrintSelection( s5, 1, 30 ); -1 : () -31 : (1,5,2) -61 : (1,2,3) -91 : (1,3,5,2,4) -gap> PrintSelection( s5, 9, 11, 43 ); -9 : (2,5,3) -20 : (2,4) -31 : (1,5,2) -42 : (1,5,2,3,4) - -############################################################################# -## -#E print.tst . . . . . . . . . . . . . . . . . . . . . . . . . . . ends here diff --git a/tst/record.tst b/tst/record.tst deleted file mode 100644 index 8799aef..0000000 --- a/tst/record.tst +++ /dev/null @@ -1,65 +0,0 @@ -#@local r, defaults, PrintDimensions, mydim - -############################################################################## -## -#W record.tst Utils Package -## -#Y Copyright (C) 2015-2022, The GAP Group -## - -gap> ReadPackage( "utils", "tst/loadall.g" );; -gap> UtilsLoadingComplete; -true - -## SubSection 8.1.1 -gap> r := rec( a := 1, b := 2, c := 3 );; -gap> AssignGlobals( r ); -The following global variables have been assigned: -[ "a", "b", "c" ] -gap> [ a, b, c ]; -[ 1, 2, 3 ] - -## SubSection 8.2.1 -gap> defaults := rec( a := 1, b := 2, c := 3 );; -gap> OptionRecordWithDefaults( defaults, rec( a := 6) ); -rec( a := 6, b := 2, c := 3 ) -gap> OptionRecordWithDefaults( defaults, rec( b := 7, c := 8 ) ); -rec( a := 1, b := 7, c := 8 ) -gap> OptionRecordWithDefaults( defaults, [ ] ); -rec( a := 1, b := 2, c := 3 ) -gap> OptionRecordWithDefaults( defaults, [ rec( c := 8 ) ] ); -rec( a := 1, b := 2, c := 8 ) -gap> OptionRecordWithDefaults( defaults, rec( d := 9 ) ); -Error, Unknown option: d -gap> OptionRecordWithDefaults( defaults, [ rec( b := 7 ), rec( c := 8 ) ] ); -Error, Too many arguments for function -gap> OptionRecordWithDefaults( defaults, 5 ); -Error, Options should be a record -gap> OptionRecordWithDefaults( defaults, [6,7,8] ); -Error, Too many arguments for function - -gap> PrintDimensions := function( arg ) -> local nargs, dim, order, V, L, len, K, i; -> nargs := Length( arg ); -> dim := [ arg[1]!.height, arg[1]!.width, arg[1]!.depth ]; -> order := rec( h := 1, w := 2, d := 3 ); -> V := [ "height", "width", "depth" ]; -> if ( nargs > 1 ) and IsRecord( arg[2] ) then -> order := OptionRecordWithDefaults( order, arg[2] ); -> fi; -> L := [ order!.h, order!.w, order!.d ]; -> len := Length( L ); -> K := [ 1..len ]; -> SortParallel( L, K ); -> Print( "dimensions: " ); -> Print( V[K[1]], " = ", dim[K[1]], ", " ); -> Print( V[K[2]], " = ", dim[K[2]], ", " ); -> Print( V[K[3]], " = ", dim[K[3]], "\n" ); -> end;; - -gap> mydim := rec( height := 45, width := 31, depth := 17 ); -rec( depth := 17, height := 45, width := 31 ) -gap> PrintDimensions( mydim ); -dimensions: height = 45, width = 31, depth = 17 -gap> PrintDimensions( mydim, rec( h:=3, w:=1, d:=2 ) ); -dimensions: width = 31, depth = 17, height = 45