Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  io.gi   Sprache: unbekannt

 
#############################################################################
##
##  tools/io.gi
##  Copyright (C) 2013-2022                              James D. Mitchell
##
##  Licensing information can be found in the README file of this package.
##
#############################################################################
##

#############################################################################
# Internal functions - for reading and writing generators to a file
#############################################################################

# This function sets the component <decoder> of the IO file object <f> to a
# function that can be called on the file object to decode its contents. This
# is not called before anything is read from <f>.
SEMIGROUPS.FileDecoder := function(f)
  local char;

  if not IsBound(f!.decoder) then
    Assert(1, IsBound(f!.rpos) and f!.rpos = 1);
    char := IO_ReadBlock(f, 1);
    if char in ["t", "p", "b"] then
      # Use old style decoding
      f!.decoder := x -> SEMIGROUPS.ReadGeneratorsLine(Chomp(IO_ReadLine(x)));
    else
      # Use IO pickling
      f!.decoder := IO_Unpickle;
    fi;
    # Rewind, this is probably a bad idea.
    f!.rpos := 1;
    f!.rdata := f!.rdata + 1;
  fi;
  return f!.decoder;
end;

# This function converts a line in an old style "generators" file into the
# corresponding object.
SEMIGROUPS.ReadGeneratorsLine := function(line)
  local i, k, out, m, deg, f, j;

  i := 2;
  k := 0;
  out := [];

  while i < Length(line) do
    m := Int([line[i]]);                                       # blocksize
    deg := Int(NormalizedWhitespace(line{[i + 1 .. m + i]}));  # max domain
    f := line{[m + i + 1 .. i + m * (deg + 1)]};
    k := k + 1;
    out[k] := EmptyPlist(deg);
    for j in [1 .. deg] do
      Add(out[k], Int(NormalizedWhitespace(f{[(j - 1) * m + 1 .. j * m]})));
    od;
    i := i + m * (deg + 1) + 1;
  od;

  if IsEmpty(line) then
    return IO_Nothing;
  elif line[1] = 't' then  # transformations
    Apply(out, TransformationNC);
  elif line[1] = 'p' then  # partial perms
    Apply(out, DensePartialPermNC);
  elif line[1] = 'b' then  # bipartitions
    Apply(out, BIPART_NC);
  fi;

  return out;
end;

# This function converts a transformation/partial perm/bipartition collection
# <coll> in a line of an old style "generators" file and writes that line to
# the file <file>, using IO_WriteLine.
SEMIGROUPS.WriteGeneratorsLine := function(file, coll)
  local append, line, deg, nrdigits, x, i;

  append := function(str, pt, m)
    local i, j;

    i := String(pt);
    for j in [1 .. m - Length(i)] do
      Append(str, " ");
    od;
    Append(str, i);
    return str;
  end;

  if IsTransformationCollection(coll) then
    line := "t";
    for x in coll do
      deg := String(DegreeOfTransformation(x));
      nrdigits := Length(deg);
      Append(line, String(nrdigits));
      Append(line, deg);
      for i in [1 .. DegreeOfTransformation(x)] do
        append(line, i ^ x, nrdigits);
      od;
    od;
  elif IsPartialPermCollection(coll) then
    line := "p";
    for x in coll do
      deg := String(DegreeOfPartialPerm(x));
      nrdigits := Length(String(Maximum(DegreeOfPartialPerm(x),
                                        CodegreeOfPartialPerm(x))));
      Append(line, String(nrdigits));
      append(line, deg, nrdigits);
      for i in [1 .. DegreeOfPartialPerm(x)] do
        append(line, i ^ x, nrdigits);
      od;
    od;
  elif IsBipartitionCollection(coll) then
    line := "b";
    for x in coll do
      deg := String(2 * DegreeOfBipartition(x));
      nrdigits := Length(deg);
      Append(line, String(nrdigits));
      Append(line, deg);
      for i in IntRepOfBipartition(x) do
        append(line, i, nrdigits);
      od;
    od;
  fi;
  if IO_WriteLine(file, line) = fail then
    # Cannot test this line
    return IO_Error;
  else
    return IO_OK;
  fi;
end;

#############################################################################
# User functions - for reading and writing generators to a file
#############################################################################

InstallGlobalFunction(ReadGenerators,
function(arg...)
  local name, line_nr, file, decoder, i, obj, out;

  if Length(arg) = 1 then
    name    := arg[1];
    line_nr := 0;      # Read all data
  elif Length(arg) = 2 then
    name    := arg[1];
    line_nr := arg[2];
  else
    ErrorNoReturn("there should be 1 or 2 arguments");
  fi;

  if IsString(name) then
    name := UserHomeExpand(name);
    file := IO_CompressedFile(name, "r");
    if file = fail then
      ErrorNoReturn("could not open the file ", name);
    fi;
  elif IsFile(name) then
    file := name;
  else
    ErrorNoReturn("the 1st argument is not a string or a file");
  fi;

  if not (IsInt(line_nr) and line_nr >= 0) then
    ErrorNoReturn("the 2nd argument is not a positive integer");
  fi;

  decoder := SEMIGROUPS.FileDecoder(file);

  if line_nr <> 0 then
    # When the first arg is a file this means get the line_nr-th entry from the
    # last read entry and not necessarily from the start of the file.
    i := 0;
    repeat
      i   := i + 1;
      obj := decoder(file);
      # This is potentially more costly than necessary, since we fully
      # decode every line, and then just throw it away.
    until i = line_nr or obj = IO_Nothing;

    if IsString(arg[1]) then
      IO_Close(file);
    fi;
    if obj = IO_Nothing then
      ErrorNoReturn("the file only has ", i - 1, " further entries");
    fi;
    return obj;
  else
    i   := 0;
    out := [];
    obj := decoder(file);
    while obj <> IO_Nothing do
      i      := i + 1;
      out[i] := obj;
      obj    := decoder(file);
    od;

    if IsString(arg[1]) then
      IO_Close(file);
    fi;
    return out;
  fi;
end);

InstallGlobalFunction(WriteGenerators,
function(arg...)
  local name, collcoll, mode, file, encoder, coll;

  if Length(arg) = 2 then
    name     := arg[1];
    collcoll := arg[2];
    mode     := "w";
    encoder  := fail;
  elif Length(arg) = 3 then
    if IsString(arg[3]) then
      name     := arg[1];
      collcoll := arg[2];
      mode     := arg[3];
      encoder  := fail;
    elif IsFunction(arg[3]) then
      name     := arg[1];
      collcoll := arg[2];
      mode     := "w";
      encoder  := arg[3];
    else
      ErrorNoReturn("the 3rd argument is not a string or a function");
    fi;
  elif Length(arg) = 4 then
    name     := arg[1];
    collcoll := arg[2];
    mode     := arg[3];
    encoder  := arg[4];
  else
    ErrorNoReturn("there should be 2, 3, or 4 arguments");
  fi;

  if mode <> "a" and mode <> "w" then
    ErrorNoReturn("the 3rd argument is not \"a\" or \"w\"");
  elif IsString(name) then
    name := UserHomeExpand(name);
    file := IO_CompressedFile(name, mode);
    if file = fail then
      # Cannot test this
      ErrorNoReturn("couldn't open the file ", name);
    fi;
  elif IsFile(name) then
    file := name;
  else
    ErrorNoReturn("the 1st argument is not a string or a file");
  fi;

  if not IsList(collcoll) or IsEmpty(collcoll) then
    if IsString(name) then
      IO_Close(file);
    fi;
    ErrorNoReturn("the 2nd argument is not a non-empty list");
  fi;

  if encoder = fail then
    encoder := IO_Pickle;
  elif not IsFunction(encoder) then
    if IsString(name) then
      IO_Close(file);
    fi;
    ErrorNoReturn("the 3rd or 4th argument is not a function");
  fi;

  # Check encoder is consistent with <coll>
  if encoder = SEMIGROUPS.WriteGeneratorsLine
      and ForAny(collcoll, x -> not (IsTransformationCollection(x)
                                     or IsPartialPermCollection(x)
                                     or IsBipartitionCollection(x))) then
    if IsString(name) then
      IO_Close(file);
    fi;
    ErrorNoReturn("the 2nd argument is incompatible with the file format");
  fi;

  for coll in collcoll do
    if IsSemigroup(coll)
        and not (IsReesMatrixSemigroup(coll)
                 or IsReesZeroMatrixSemigroup(coll)) then
      coll := GeneratorsOfSemigroup(coll);
      # We could use a smaller generating set (i.e. GeneratorsOfMonoid,
      # GeneratorsOfInverseSemigroup etc) but we have no way of knowing which
      # generators we wrote, so better always use GeneratorsOfSemigroup.
    fi;
    if encoder(file, coll) = IO_Error then
      # Cannot test this line
      return IO_Error;
    fi;
  od;

  if IsString(name) then
    IO_Close(file);
  fi;
  return IO_OK;
end);

InstallGlobalFunction(IteratorFromGeneratorsFile,
function(filename)
  local file, decoder, record;

  filename := UserHomeExpand(filename);
  file     := IO_CompressedFile(filename, "r");

  if file = fail then
    return fail;
  fi;

  decoder := SEMIGROUPS.FileDecoder(file);

  record := rec(file     := file,
                filename := filename,
                decoder  := decoder,
                current  := decoder(file));

  record.NextIterator := function(iter)
    local next;
    next := iter!.current;
    iter!.current := iter!.decoder(iter!.file);
    return next;
  end;

  record.IsDoneIterator := function(iter)
    if iter!.current = IO_Nothing then
      if not iter!.file!.closed then
        IO_Close(iter!.file);
      fi;
      return true;
    else
      return false;
    fi;
  end;

  record.ShallowCopy := function(iter)
    return rec(file     := IO_CompressedFile(iter!.filename, "r"),
               filename := iter!.filename,
               decoder  := iter!.decoder,
               current  := iter!.decoder(~.file));
  end;

  return IteratorByFunctions(record);
end);

################################################################################
# User functions for multiplication tables
################################################################################

InstallGlobalFunction(ReadMultiplicationTable,
function(arg...)
  local name, line_nr, file, ReadMultiplicationTableLine, i, line, lines;
  if Length(arg) = 1 then
    name    := arg[1];
    line_nr := 0;
  elif Length(arg) = 2 then
    name    := arg[1];
    line_nr := arg[2];
  else
    ErrorNoReturn("there should be 1 or 2 arguments");
  fi;
  if IsString(name) then
    name := UserHomeExpand(name);
    file := IO_CompressedFile(name, "r");
    if file = fail then
      ErrorNoReturn("could not open the file \"", name, "\"");
    fi;
  elif IsFile(name) then
    file := name;
  else
    ErrorNoReturn("the 1st argument is not a string or a file");
  fi;
  if not (IsInt(line_nr) and line_nr >= 0) then
    ErrorNoReturn("the 2nd argument is not a positive integer");
  fi;
  ReadMultiplicationTableLine := SEMIGROUPS.ReadMultiplicationTableLine;

  if line_nr <> 0 then
    i := 0;
    repeat
      i    := i + 1;
      line := IO_ReadLine(file);
    until i = line_nr or line = "";
    if IsString(arg[1]) then
      IO_Close(file);
    elif line = "" then
      ErrorNoReturn("the file only has ", i - 1, " lines");
    fi;
    return ReadMultiplicationTableLine(Chomp(line), RootInt(Length(line)));
  else
    lines := IO_ReadLines(file);
    if IsString(arg[1]) then
      IO_Close(file);
    fi;
    Apply(lines, line -> ReadMultiplicationTableLine(Chomp(line),
                                                   RootInt(Length(line))));
    return lines;
  fi;
end);

SEMIGROUPS.ReadMultiplicationTableLine := function(line, n)
  local table, vals, row, entry, i, j;
  table := EmptyPlist(n);
  vals := [0 .. n - 1];
  for i in vals do
    row := EmptyPlist(n);
    for j in vals do
      entry := IntChar(line[i * n + j + 1]);
      if entry = 0 then
        row[j + 1] := 10;
      else
        row[j + 1] := entry;
      fi;
    od;
    table[i + 1] := row;
  od;
  return table;
end;

InstallGlobalFunction(WriteMultiplicationTable,
function(arg...)
  local name, coll, mode, file, str, i, j, k, n;

  if Length(arg) = 2 then
    name := arg[1];
    coll := arg[2];
    mode := "w";
  elif Length(arg) = 3 then
    name := arg[1];
    coll := arg[2];
    mode := arg[3];
  else
    ErrorNoReturn("there must be 2 or 3 arguments");
  fi;

  if mode <> "a" and mode <> "w" then
    ErrorNoReturn("the 3rd argument is not \"a\" or \"w\"");
  elif IsString(name) then
    name := UserHomeExpand(name);
    file := IO_CompressedFile(name, mode);
    if file = fail then
      # Cannot test this
      ErrorNoReturn("couldn't open the file ", name);
    fi;
  elif IsFile(name) then
    file := name;
  else
    ErrorNoReturn("the 1st argument is not a string or a file");
  fi;

  for i in [1 .. Length(coll)] do
    n := Size(coll[i]);  # Don't assume all multiplication tables are same size.
    if not (IsRectangularTable(coll[i]) and IsInt(coll[i][1][1])) then
      if IsString(name) then
        IO_Close(file);
      fi;
      ErrorNoReturn("the 2nd argument is not a collection of rectangular ",
                    "tables containing only integers");
    elif not n < 256 then
      if IsString(name) then
        IO_Close(file);
      fi;
      ErrorNoReturn("the 2nd argument is not a collection of rectangular ",
                    "tables with at most 255 rows");
    fi;

    str := EmptyString(n ^ 2 + 1);  # + 1 for newline character
    for j in [1 .. n] do
      for k in [1 .. n] do
        if not (0 < coll[i][j][k] and coll[i][j][k] <= n) then
          if IsString(name) then
            IO_Close(file);
          fi;
          ErrorNoReturn("the 2nd argument is not a collection of ",
                        "rectangular tables with integer entries from [1, 2, ",
                        "..., n] (where n equals the number of rows of the ",
                        "table)");
        elif coll[i][j][k] = 10 then
          Add(str, '\000');  # We use CharInt(0) since CharInt(10) is newline.
        else
          Add(str, CharInt(coll[i][j][k]));
        fi;
      od;
    od;
    Add(str, '\n');

    if IO_Write(file, str) = fail then
      # Cannot test this line
      return IO_Error;
    fi;
  od;

  if IsString(name) then
    IO_Close(file);
  fi;
  return IO_OK;
end);

InstallGlobalFunction(IteratorFromMultiplicationTableFile,
function(str)
  local file, line, record, n;

  file := IO_CompressedFile(UserHomeExpand(str), "r");

  if file = fail then
    return fail;
  fi;

  line := IO_ReadLine(file);
  n := RootInt(Length(line));
  line := SEMIGROUPS.ReadMultiplicationTableLine(Chomp(line), n);
  record := rec(file := file, current := line);

  record.NextIterator := function(iter)
    local next;
    next := iter!.current;
    iter!.current := IO_ReadLine(iter!.file);
    if iter!.current <> "" then
      iter!.current
        := SEMIGROUPS.ReadMultiplicationTableLine(Chomp(iter!.current), n);
    fi;
    return next;
  end;

  record.IsDoneIterator := function(iter)
    if iter!.current = "" then
      if not iter!.file!.closed then
        IO_Close(iter!.file);
      fi;
      return true;
    else
      return false;
    fi;
  end;

  # TODO use iter here? Store things in the iterator?
  record.ShallowCopy := function(_)
    local file, line;
    file := IO_CompressedFile(UserHomeExpand(str), "r");
    line := SEMIGROUPS.ReadMultiplicationTableLine(Chomp(IO_ReadLine(file)), n);
    return rec(file := file, current := line);
  end;

  return IteratorByFunctions(record);
end);

[ Dauer der Verarbeitung: 0.29 Sekunden  (vorverarbeitet)  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge