#############################################################################
##
##  This file is part of GAP, a system for computational discrete algebra.
##
##  SPDX-License-Identifier: GPL-2.0-or-later
##
##  Copyright of GAP belongs to its developers, whose names are too numerous
##  to list here. Please refer to the COPYRIGHT file for details.
##

############################################################################
#
# This file is a sample implementation for new style vectors and matrices.
# It stores matrices as dense lists of lists with wrapping.
#

############################################################################
# Constructors:
############################################################################


############################################################################
##
#F  MakeIsPlistVectorRep( <basedomain>, <list>, <check> )
##
##  Construct a new vector in the filter 'IsPlistVectorRep' with base domain
##  <basedomain> and entries in the list <list> (without copying).
##
##  If <check> is set to 'true' *and* 'ValueOption( "check" )' is 'true',
##  then it is checked that the entries of <list> are in <basedomain>.
##  So whenever you know that the input is guaranteed to be in <basedomain>,
##  pass 'false' for <check> to omit these (potentially costly) consistency
##  checks.
##
BindGlobal( "MakeIsPlistVectorRep",
  function( basedomain, list, check )
    local efam, fam, filter, types, typ;

    efam := ElementsFamily( FamilyObj( basedomain ) );
    fam := FamilyObj( basedomain );

    # we store the types in the base domain if the filter carries
    # information specific to it
    if IsBound( basedomain!.PlistVectorRepTypes ) then
      types := basedomain!.PlistVectorRepTypes;
    elif IsBound( fam!.PlistVectorRepTypes ) and not IsIntegers(basedomain) then
      types := fam!.PlistVectorRepTypes;
    else
      # initialize type cache
      # TODO: make this thread safe for HPC-GAP
      filter := IsPlistVectorRep;
      if CanEasilyCompareElementsFamily( efam ) then
        filter := filter and CanEasilyCompareElements;
      fi;
      if IsIntegers(basedomain) then
        filter := filter and IsIntVector;
      elif IsFFECollection(basedomain) then
        filter := filter and IsFFEVector;
      fi;
      types := [
          NewType( fam, filter ),
          NewType( fam, filter and IsMutable ),
      ];
      if IsIntegers(basedomain) then
        basedomain!.PlistVectorRepTypes := types;
      else
        fam!.PlistVectorRepTypes := types;
      fi;
    fi;
    if IsMutable( list ) then
      typ := types[2];
    else
      typ := types[1];
    fi;

    if check and ValueOption( "check" ) <> false then
      if not IsSubset( basedomain, list ) then
        Error( "the elements in <list> must lie in <basedomain>" );
      elif not IsPlistRep( list ) then
        Error( "<list> must be in 'IsPlistRep'" );
      fi;
    fi;

    return Objectify(typ, [ basedomain, list ]);
  end );


############################################################################
##
#F  MakeIsPlistMatrixRep( <basedomain>, <emptyvector>, <ncols>, <list>,
#F                        <check> )
##
##  Construct a new matrix in the filter 'IsPlistMatrixRep' with base domain
##  <basedomain> and rows given by the list <list>, whose entries must be
##  'IsPlistVectorObj' vectors with base domain <basedomain> and length <ncols>.
##  <emptyvector> must be an 'IsPlistVectorObj' vector of length zero and with
##  base domain <basedomain>.
##
##  If <check> is set to 'true' *and* 'ValueOption( "check" )' is 'true',
##  then it is checked that the entries of <list> are 'IsPlistVectorObj'
##  vectors with base domain identical with <basedomain> and length equal to
##  <ncols>.
##  So whenever you know that the input satisfies these conditions,
##  pass 'false' for <check> to omit these (potentially costly) consistency
##  checks.
##
##  (It is *not* checked whether for each of the vectors in <list>,
##  the entries lie in <basedomain>; this check is assumed to belong to the
##  creation of the vectors.)
##
BindGlobal( "MakeIsPlistMatrixRep",
  function( basedomain, emptyvector, ncols, list, check )
    local efam, fam, filter, typ, row;

    efam := ElementsFamily( FamilyObj( basedomain ) );
    fam := CollectionsFamily( FamilyObj( basedomain ) );

    # Currently there is no special handling depending on 'basedomain',
    # the types are always cached in 'fam'.
    if not IsBound( fam!.PlistMatrixRepTypes ) then
      # initialize type cache
      # TODO: make this thread safe for HPC-GAP
      filter := IsPlistMatrixRep;
      if CanEasilyCompareElementsFamily( efam ) then
        filter := filter and CanEasilyCompareElements;
      fi;
      fam!.PlistMatrixRepTypes := [
          NewType( fam, filter ),
          NewType( fam, filter and IsMutable ),
      ];
    fi;
    if IsMutable( list ) then
      typ := fam!.PlistMatrixRepTypes[2];
    else
      typ := fam!.PlistMatrixRepTypes[1];
    fi;

    if check and ValueOption( "check" ) <> false then
      if not IsPlistVectorRep( emptyvector ) then
        Error( "<emptyvector> must be in 'IsPlistVectorRep'" );
      elif not IsIdenticalObj( basedomain, emptyvector![BDPOS] ) then
        Error( "<emptyvector> must have the given base domain" );
      fi;
      for row in list do
        if not IsPlistVectorRep( row ) then
          Error( "the entries of <list> must be in 'IsPlistVectorRep'" );
        elif Length( row![ELSPOS] ) <> ncols then
          Error( "the entries of <list> must have length <ncols>" );
        elif not IsIdenticalObj( basedomain, row![BDPOS] ) then
          Error( "the entries of <list> must have the given base domain" );
        fi;
      od;
    fi;

    return Objectify( typ, [ basedomain, emptyvector, ncols, list ] );
  end );


############################################################################
# Constructor methods:
############################################################################

InstallTagBasedMethod( NewVector,
  IsPlistVectorRep,
  function( filter, basedomain, list )
    return MakeIsPlistVectorRep(basedomain, PlainListCopy( list ), true);
  end );

InstallTagBasedMethod( NewZeroVector,
  IsPlistVectorRep,
  function( filter, basedomain, len )
    local list;
    list := ListWithIdenticalEntries(len, Zero(basedomain));
    return MakeIsPlistVectorRep(basedomain, list, false);
  end );

InstallTagBasedMethod( NewMatrix,
  IsPlistMatrixRep,
  function( filter, basedomain, ncols, list )
    local nd, filterVectors, m, e, i;

    # If applicable then replace a flat list 'list' by a nested list
    # of lists of length 'ncols'.
    if Length( list ) > 0 and not IsVectorObj( list[1] ) then
      nd := NestingDepthA( list );
      if nd < 2 or nd mod 2 = 1 then
        if Length( list ) mod ncols <> 0 then
          Error( "NewMatrix: Length of <list> is not a multiple of <ncols>" );
        fi;
        list := List([0, ncols .. Length( list )-ncols],
                     i -> list{[i+1..i+ncols]});
      fi;
    fi;

    filterVectors := IsPlistVectorRep;
    m := 0*[1..Length( list )];
    for i in [1..Length( list )] do
        if IsVectorObj( list[i] ) and filterVectors( list[i] ) then
            m[i] := ShallowCopy( list[i] );
        else
            m[i] := NewVector( filterVectors, basedomain, list[i] );
        fi;
    od;
    e := NewVector(filterVectors, basedomain, []);
    return MakeIsPlistMatrixRep( basedomain, e, ncols, m, true );
  end );

# This is faster than the default method.
InstallTagBasedMethod( NewZeroMatrix,
  IsPlistMatrixRep,
  function( filter, basedomain, rows, cols )
    local m,i,e,filter2;
    filter2 := IsPlistVectorRep;
    m := 0*[1..rows];
    e := NewVector(filter2, basedomain, []);
    for i in [1..rows] do
        m[i] := ZeroVector( cols, e );
    od;
    return MakeIsPlistMatrixRep( basedomain, e, cols, m, false );
  end );


############################################################################
# Printing and viewing methods:
############################################################################

InstallMethod( ViewObj, [ "IsPlistVectorRep" ],
  function( v )
    if not IsMutable(v) then
        Print("<immutable ");
    else
        Print("<");
    fi;
    Print("plist vector over ",v![BDPOS]," of length ",Length(v![ELSPOS]),">");
  end );

InstallMethod( PrintObj, [ "IsPlistVectorRep" ],
  function( v )
    Print("NewVector(IsPlistVectorRep");
    if IsFinite(v![BDPOS]) and IsField(v![BDPOS]) then
        Print(",GF(",Size(v![BDPOS]),"),",v![ELSPOS],")");
    else
        Print(",",String(v![BDPOS]),",",v![ELSPOS],")");
    fi;
  end );

InstallMethod( String, [ "IsPlistVectorRep" ],
  function( v )
    local st;
    st := "NewVector(IsPlistVectorRep";
    if IsFinite(v![BDPOS]) and IsField(v![BDPOS]) then
        Append(st,Concatenation( ",GF(",String(Size(v![BDPOS])),"),",
                                 String(v![ELSPOS]),")" ));
    else
        Append(st,Concatenation( ",",String(v![BDPOS]),",",
                                 String(v![ELSPOS]),")" ));
    fi;
    return st;
  end );

InstallMethod( Display, [ "IsPlistVectorRep" ],
  function( v )
    Print( "<a " );
    Print( "plist vector over ",BaseDomain(v),":\n");
    Print(v![ELSPOS],"\n>\n");
  end );

InstallMethod( CompatibleVectorFilter, ["IsPlistMatrixRep"],
  M -> IsPlistVectorRep );

############################################################################
############################################################################
# Vectors:
############################################################################
############################################################################


############################################################################
# The basic attributes:
############################################################################

InstallMethod( BaseDomain, [ "IsPlistVectorRep" ],
  v -> v![BDPOS] );

InstallMethod( Length, [ "IsPlistVectorRep" ],
  v -> Length( v![ELSPOS] ) );


############################################################################
# Representation preserving constructors:
############################################################################

InstallMethod( ZeroVector,
  [ "IsInt", "IsPlistVectorRep" ],
  { len, v } -> NewZeroVector( IsPlistVectorRep, v![BDPOS], len ) );

InstallMethod( ZeroVector,
  [ "IsInt", "IsPlistMatrixRep" ],
  { len, M } -> NewZeroVector( IsPlistVectorRep, M![BDPOS], len ) );

InstallMethod( Vector,
  [ "IsList and IsPlistRep", "IsPlistVectorRep" ],
  function( list, v )
    # wrap the given list without copying it (this is documented behavior)
    return MakeIsPlistVectorRep( v![BDPOS], list, true );
  end );

InstallMethod( Vector,
  [ "IsList", "IsPlistVectorRep" ],
  function( list, v )
    local m;
    m := IsMutable(list);
    list := PlainListCopy(list);
    if not m then
        MakeImmutable(list);
    fi;
    return MakeIsPlistVectorRep( v![BDPOS], list, true );
  end );


############################################################################
# A selection of list operations:
############################################################################

InstallMethod( \[\],
  [ "IsPlistVectorRep", "IsPosInt" ],
  { v, p } -> v![ELSPOS][p] );

InstallMethod( \[\]\:\=,
  [ "IsPlistVectorRep", "IsPosInt", "IsObject" ],
  function( v, p, ob )
    if ValueOption( "check" ) <> false and Length( v![ELSPOS] ) < p then
      Error( "<p> is out of bounds" );
    fi;
    v![ELSPOS][p] := ob;
  end );

InstallMethod( \{\},
  [ "IsPlistVectorRep", "IsList" ],
  { v, list } -> MakeIsPlistVectorRep( v![BDPOS], v![ELSPOS]{ list }, false ) );

InstallMethod( PositionNonZero, [ "IsPlistVectorRep" ],
  v -> PositionNonZero( v![ELSPOS] ) );

InstallOtherMethod( PositionNonZero,
  [ "IsPlistVectorRep", "IsInt" ],
  { v, s } -> PositionNonZero( v![ELSPOS], s ) );

InstallMethod( PositionLastNonZero, [ "IsPlistVectorRep" ],
  function( v )
    local els,i;
    els := v![ELSPOS];
    i := Length(els);
    while i > 0 and IsZero(els[i]) do i := i - 1; od;
    return i;
  end );

InstallMethod( ListOp, [ "IsPlistVectorRep" ],
  v -> v![ELSPOS]{ [ 1 .. Length( v![ELSPOS] ) ] } );

InstallMethod( ListOp,
  [ "IsPlistVectorRep", "IsFunction" ],
  { v, f } -> List( v![ELSPOS], f ) );

InstallMethod( Unpack,
  [ "IsPlistVectorRep" ],
  v -> ShallowCopy( v![ELSPOS] ) );

############################################################################
# Standard operations for all objects:
############################################################################

InstallMethod( ShallowCopy, [ "IsPlistVectorRep" ],
  v -> MakeIsPlistVectorRep( v![BDPOS], ShallowCopy( v![ELSPOS] ), false ) );

# StructuralCopy works automatically

InstallMethod( PostMakeImmutable, [ "IsPlistVectorRep" ],
  function( v )
    MakeImmutable( v![ELSPOS] );
  end );


############################################################################
# Arithmetical operations:
############################################################################

InstallMethod( \+,
  [ "IsPlistVectorRep", "IsPlistVectorRep" ],
  function( a, b )
    if ValueOption( "check" ) <> false and
       not IsIdenticalObj( a![BDPOS], b![BDPOS] ) then
      Error( "<a> and <b> are not compatible" );
    fi;
    return MakeIsPlistVectorRep(a![BDPOS],
               SUM_LIST_LIST_DEFAULT(a![ELSPOS],b![ELSPOS]), false);
  end );

InstallMethod( \-,
  [ "IsPlistVectorRep", "IsPlistVectorRep" ],
  function( a, b )
    if ValueOption( "check" ) <> false and
       not IsIdenticalObj( a![BDPOS], b![BDPOS] ) then
      Error( "<a> and <b> are not compatible" );
    fi;
    return MakeIsPlistVectorRep(a![BDPOS],
               DIFF_LIST_LIST_DEFAULT(a![ELSPOS],b![ELSPOS]), false);
  end );

InstallMethod( \=,
  [ "IsPlistVectorRep", "IsPlistVectorRep" ],
  { a, b } -> EQ_LIST_LIST_DEFAULT( a![ELSPOS], b![ELSPOS] ) );

InstallMethod( \<,
  [ "IsPlistVectorRep", "IsPlistVectorRep" ],
  { a, b } -> LT_LIST_LIST_DEFAULT( a![ELSPOS], b![ELSPOS] ) );

InstallMethod( AddRowVector,
  [ "IsPlistVectorRep and IsMutable", "IsPlistVectorRep" ],
  function( a, b )
    if ValueOption( "check" ) <> false and
       not IsIdenticalObj( a![BDPOS], b![BDPOS] ) then
      Error( "<a> and <b> are not compatible" );
    fi;
    ADD_ROW_VECTOR_2( a![ELSPOS], b![ELSPOS] );
  end );

# Better method for integer vectors:
InstallMethod( AddRowVector,
  [ "IsPlistVectorRep and IsMutable and IsIntVector",
    "IsPlistVectorRep and IsIntVector" ],
  function( a, b )
    if ValueOption( "check" ) <> false and
       not IsIdenticalObj( a![BDPOS], b![BDPOS] ) then
      Error( "<a> and <b> are not compatible" );
    fi;
    ADD_ROW_VECTOR_2_FAST( a![ELSPOS], b![ELSPOS] );
  end );

InstallMethod( AddRowVector,
  [ "IsPlistVectorRep and IsMutable", "IsPlistVectorRep", "IsObject" ],
  function( a, b, s )
    ADD_ROW_VECTOR_3( a![ELSPOS], b![ELSPOS], s );
    if ValueOption( "check" ) <> false then
      if not IsIdenticalObj( a![BDPOS], b![BDPOS] ) then
        Error( "<a> and <b> are not compatible" );
      elif not IsSubset( a![BDPOS], a![ELSPOS] ) then
        Error( "<a> is not defined over its base domain" );
      fi;
    fi;
  end );

# Better method for integer vectors:
InstallMethod( AddRowVector,
  [ "IsPlistVectorRep and IsIntVector and IsMutable",
    "IsPlistVectorRep and IsIntVector", "IsInt" ],
  function( a, b, s )
    if ValueOption( "check" ) <> false then
      if not IsIdenticalObj( a![BDPOS], b![BDPOS] ) then
        Error( "<a> and <b> are not compatible" );
      fi;
    fi;
    if IsSmallIntRep(s) then
        ADD_ROW_VECTOR_3_FAST( a![ELSPOS], b![ELSPOS], s );
    else
        ADD_ROW_VECTOR_3( a![ELSPOS], b![ELSPOS], s );
    fi;
  end );

InstallMethod( AddRowVector,
  [ "IsPlistVectorRep and IsMutable", "IsPlistVectorRep",
    "IsObject", "IsPosInt", "IsPosInt" ],
  function( a, b, s, from, to )
    ADD_ROW_VECTOR_5( a![ELSPOS], b![ELSPOS], s, from, to );
    if ValueOption( "check" ) <> false then
      if not IsIdenticalObj( a![BDPOS], b![BDPOS] ) then
        Error( "<a> and <b> are not compatible" );
      elif not IsSubset( a![BDPOS], a![ELSPOS] ) then
        Error( "<a> is not defined over its base domain" );
      fi;
    fi;
  end );

# Better method for integer vectors:
InstallMethod( AddRowVector,
  [ "IsPlistVectorRep and IsIntVector and IsMutable",
    "IsPlistVectorRep and IsIntVector", "IsInt", "IsPosInt", "IsPosInt" ],
  function( a, b, s, from, to )
    if ValueOption( "check" ) <> false and
       not IsIdenticalObj( a![BDPOS], b![BDPOS] ) then
      Error( "<a> and <b> are not compatible" );
    fi;
    if IsSmallIntRep(s) then
        ADD_ROW_VECTOR_5_FAST( a![ELSPOS], b![ELSPOS], s, from, to );
    else
        ADD_ROW_VECTOR_5( a![ELSPOS], b![ELSPOS], s, from, to );
    fi;
  end );

InstallMethod( MultVectorLeft,
  [ "IsPlistVectorRep and IsMutable", "IsObject" ],
  function( v, s )
    MULT_VECTOR_LEFT_2(v![ELSPOS],s);
    if ValueOption( "check" ) <> false and not IsSubset( v![BDPOS], v![ELSPOS] ) then
      Error( "<v> is not defined over its base domain" );
    fi;
  end );

InstallMethod( MultVectorRight,
  [ "IsPlistVectorRep and IsMutable", "IsObject" ],
  function( v, s )
    MULT_VECTOR_RIGHT_2(v![ELSPOS],s);
    if ValueOption( "check" ) <> false and not IsSubset( v![BDPOS], v![ELSPOS] ) then
      Error( "<v> is not defined over its base domain" );
    fi;
  end );

InstallOtherMethod( MultVectorLeft,
  [ "IsPlistVectorRep and IsIntVector and IsMutable", "IsSmallIntRep" ],
  function( v, s )
    MULT_VECTOR_2_FAST(v![ELSPOS],s);
  end );

# The four argument version of MultVectorLeft / ..Right uses the generic
# implementation in matobj.gi

InstallMethod( \*,
  [ "IsPlistVectorRep", "IsScalar" ],
  { v, s } -> MakeIsPlistVectorRep( v![BDPOS],
                  PROD_LIST_SCL_DEFAULT( v![ELSPOS], s ), true ) );

InstallMethod( \*,
  [ "IsScalar", "IsPlistVectorRep" ],
  { s, v } -> MakeIsPlistVectorRep( v![BDPOS],
                  PROD_SCL_LIST_DEFAULT( s, v![ELSPOS] ), true ) );

InstallMethod( \/,
  [ "IsPlistVectorRep", "IsScalar" ],
  function( v, s )
    local basedomain, w;

    basedomain:= v![BDPOS];
    w:= PROD_LIST_SCL_DEFAULT( v![ELSPOS], s^-1 );
    return MakeIsPlistVectorRep(basedomain, w, true);
  end );

InstallMethod( AdditiveInverseSameMutability,
  [ "IsPlistVectorRep" ],
  v -> MakeIsPlistVectorRep( v![BDPOS],
           AdditiveInverseSameMutability( v![ELSPOS] ), false ) );

InstallMethod( AdditiveInverseImmutable,
  [ "IsPlistVectorRep" ],
  v -> MakeIsPlistVectorRep( v![BDPOS],
           AdditiveInverseImmutable( v![ELSPOS] ), false ) );

InstallMethod( AdditiveInverseMutable,
  [ "IsPlistVectorRep" ],
  v -> MakeIsPlistVectorRep( v![BDPOS],
           AdditiveInverseMutable( v![ELSPOS] ), false ) );

InstallMethod( ZeroSameMutability, [ "IsPlistVectorRep" ],
  v -> MakeIsPlistVectorRep( v![BDPOS],
           ZeroSameMutability( v![ELSPOS] ), false ) );

InstallMethod( ZeroImmutable, [ "IsPlistVectorRep" ],
  function( v )
    v:= MakeIsPlistVectorRep( v![BDPOS],
            ZeroImmutable( v![ELSPOS] ), false );
    SetIsZero( v, true );
    return v;
  end );

InstallMethod( ZeroMutable, [ "IsPlistVectorRep" ],
  v -> MakeIsPlistVectorRep( v![BDPOS], ZeroMutable( v![ELSPOS] ), false ) );

InstallMethod( IsZero, [ "IsPlistVectorRep" ],
  v -> IsZero( v![ELSPOS] ) );

InstallMethodWithRandomSource( Randomize,
  "for a random source and a mutable plist vector",
  [ IsRandomSource, IsPlistVectorRep and IsMutable ],
  function( rs, v )
    local bd,i;
    bd := v![BDPOS];
    for i in [1..Length(v![ELSPOS])] do
        v![ELSPOS][i] := Random( rs, bd );
    od;
    return v;
  end );

InstallMethod( CopySubVector,
  [ "IsPlistVectorRep", "IsPlistVectorRep and IsMutable", "IsList", "IsList" ],
  function( a,b,pa,pb )
    if ValueOption( "check" ) <> false and
       not IsIdenticalObj( a![BDPOS], b![BDPOS] ) then
      Error( "<a> and <b> are not compatible" );
    fi;
    # The following should eventually go into the kernel:
    b![ELSPOS]{pb} := a![ELSPOS]{pa};
  end );


############################################################################
############################################################################
# Matrices:
############################################################################
############################################################################


############################################################################
# The basic attributes:
############################################################################

InstallMethod( BaseDomain,
  [ "IsPlistMatrixRep" ],
  M -> M![BDPOS] );

InstallMethod( NumberRows,
  [ "IsPlistMatrixRep" ],
  M -> Length( M![ROWSPOS] ) );

InstallMethod( NumberColumns,
  [ "IsPlistMatrixRep" ],
  M -> M![RLPOS] );


############################################################################
# Representation preserving constructors:
############################################################################

InstallMethod( ZeroMatrix,
  [ "IsInt", "IsInt", "IsPlistMatrixRep" ],
  function( nrows, ncols, M )
    local t, list;
    t := M![EMPOS];
    list := List([1..nrows],i->ZeroVector(ncols,t));
    return MakeIsPlistMatrixRep( M![BDPOS], t, ncols, list, false );
  end );

InstallMethod( IdentityMatrix,
  [ "IsInt", "IsPlistMatrixRep" ],
  function( nrows, M )
    local t, list, o, i;
    t := M![EMPOS];
    list := List([1..nrows],i->ZeroVector(nrows,t));
    o := One(M![BDPOS]);
    for i in [1..nrows] do
        list[i][i] := o;
    od;
    return MakeIsPlistMatrixRep( M![BDPOS], t, nrows, list, false );
  end );

InstallMethod( Matrix,
  [ "IsList", "IsInt", "IsPlistMatrixRep" ],
  function( list, ncols, M )
    local basedomain, check, i,l,nrrows,t;
    t := M![EMPOS];
    basedomain:= M![BDPOS];
    check:= ValueOption( "check" ) <> false;
    if Length( list ) > 0 then
        if IsVectorObj( list[1] ) and IsPlistVectorRep( list[1] ) then
            if check then
              for i in list do
                if not IsIdenticalObj( basedomain, BaseDomain( i ) ) then
                  Error( "not the same <basedomain>" );
                elif Length( i ) <> ncols then
                  Error( "incompatible lengths of vectors" );
                fi;
              od;
            fi;
            l := list;
        elif IsList( list[1] ) then
            l := ListWithIdenticalEntries( Length( list ), 0 );
            for i in [1..Length( list )] do
                l[i] := Vector( list[i], t );
                if check and Length( list[i] ) <> ncols then
                  Error( "incompatible lengths of vectors" );
                fi;
            od;
        else  # a flat initializer:
            nrrows := Length( list ) / ncols;
            l := ListWithIdenticalEntries(nrrows,0);
            for i in [1..nrrows] do
                l[i] := Vector( list{ [(i-1)*ncols+1..i*ncols] }, t );
            od;
        fi;
    else
        l := [];
    fi;
    # The result shall be mutable iff 'list' is mutable.
    if not IsMutable( list ) then
      MakeImmutable( l );
    fi;
    return MakeIsPlistMatrixRep( basedomain, t, ncols, l, false );
  end );


############################################################################
# A selection of list operations:
############################################################################

InstallOtherMethod( \[\],
#T Once the declaration of '\[\]' for 'IsMatrixObj' disappears,
#T we can use 'InstallMethod'.
  [ "IsPlistMatrixRep", "IsPosInt" ],
  { M, p } -> M![ROWSPOS][p] );

InstallMethod( \[\]\:\=,
  [ "IsPlistMatrixRep and IsMutable", "IsPosInt", "IsPlistVectorRep" ],
  function( M, p, v )
    if ValueOption( "check" ) <> false and
       ( not IsIdenticalObj( M![BDPOS], v![BDPOS] ) or
         Length( v ) <> M![RLPOS] ) then
      Error( "<M> and <v> are not compatible" );
    fi;
    M![ROWSPOS][p] := v;
  end );

InstallMethod( \{\},
  [ "IsPlistMatrixRep", "IsList" ],
  { M, list } -> MakeIsPlistMatrixRep( M![BDPOS], M![EMPOS], M![RLPOS],
                     M![ROWSPOS]{ list }, false ) );

InstallMethod( Add,
  [ "IsPlistMatrixRep and IsMutable", "IsPlistVectorRep" ],
  function( M, v )
    if ValueOption( "check" ) <> false and
       ( not IsIdenticalObj( M![BDPOS], v![BDPOS] ) or
         Length( v ) <> M![RLPOS] ) then
      Error( "<M> and <v> are not compatible" );
    fi;
    Add( M![ROWSPOS], v );
  end );

InstallMethod( Add,
  [ "IsPlistMatrixRep and IsMutable", "IsPlistVectorRep", "IsPosInt" ],
  function( M, v, p )
    if ValueOption( "check" ) <> false and
       ( not IsIdenticalObj( M![BDPOS], v![BDPOS] ) or
         Length( v ) <> M![RLPOS] ) then
      Error( "<M> and <v> are not compatible" );
    fi;
    Add( M![ROWSPOS], v, p );
  end );

InstallMethod( Remove,
  [ "IsPlistMatrixRep and IsMutable" ],
  M -> Remove( M![ROWSPOS] ) );

InstallMethod( Remove,
  [ "IsPlistMatrixRep and IsMutable", "IsPosInt" ],
  function( M, p )
    if p <= Length( M![ROWSPOS] ) then
      return Remove( M![ROWSPOS], p );
    fi;
  end );

InstallMethod( IsBound\[\],
  [ "IsPlistMatrixRep", "IsPosInt" ],
  { M, p } -> p <= Length( M![ROWSPOS] ) );

InstallMethod( Unbind\[\],
  [ "IsPlistMatrixRep and IsMutable", "IsPosInt" ],
  function( M, p )
    if p <> Length( M![ROWSPOS] ) then
        ErrorNoReturn("Unbind\\[\\]: Matrices must stay dense, you cannot Unbind here");
    fi;
    Unbind( M![ROWSPOS][p] );
  end );

InstallMethod( \{\}\:\=,
  [ "IsPlistMatrixRep and IsMutable", "IsList",
    "IsPlistMatrixRep" ],
  function( M, pp, N )
    if ValueOption( "check" ) <> false and
       ( not IsIdenticalObj( M![BDPOS], N![BDPOS] ) or
         M![RLPOS] <> N![RLPOS] ) then
      Error( "<M> and <N> are not compatible" );
    fi;
    M![ROWSPOS]{pp} := N![ROWSPOS];
  end );

InstallMethod( Append,
  [ "IsPlistMatrixRep and IsMutable", "IsPlistMatrixRep" ],
  function( M, N )
    if ValueOption( "check" ) <> false and
       ( not IsIdenticalObj( M![BDPOS], N![BDPOS] ) or
         M![RLPOS] <> N![RLPOS] ) then
      Error( "<M> and <N> are not compatible" );
    fi;
    Append( M![ROWSPOS], N![ROWSPOS] );
  end );

InstallMethod( ShallowCopy,
  [ "IsPlistMatrixRep" ],
  M -> MakeIsPlistMatrixRep( M![BDPOS], M![EMPOS], M![RLPOS],
           ShallowCopy( M![ROWSPOS] ), false ) );

InstallMethod( PostMakeImmutable,
  [ "IsPlistMatrixRep" ],
  function( M )
    MakeImmutable( M![ROWSPOS] );
  end );

InstallMethod( ListOp,
  [ "IsPlistMatrixRep" ],
  M -> List( M![ROWSPOS] ) );

InstallMethod( ListOp,
  [ "IsPlistMatrixRep", "IsFunction" ],
  { M, f } -> List( M![ROWSPOS], f ) );

InstallMethod( Unpack,
  [ "IsPlistMatrixRep" ],
  M -> List( M![ROWSPOS], v -> ShallowCopy( v![ELSPOS] ) ) );

InstallMethod( MutableCopyMatrix,
  [ "IsPlistMatrixRep" ],
  M -> MakeIsPlistMatrixRep( M![BDPOS], M![EMPOS], M![RLPOS],
           List( M![ROWSPOS], ShallowCopy ), false ) );

InstallMethod( ExtractSubMatrix,
  [ "IsPlistMatrixRep", "IsList", "IsList" ],
  function( M, rowspos, colspos )
    local i, list;
    list := M![ROWSPOS]{ rowspos };
    for i in [ 1 .. Length( list ) ] do
      list[i]:= MakeIsPlistVectorRep( list[i]![BDPOS],
                    list[i]![ELSPOS]{ colspos }, false );
    od;
    return MakeIsPlistMatrixRep( M![BDPOS], M![EMPOS], Length( colspos ),
               list, false );
  end );

InstallMethod( CopySubMatrix,
  [ "IsPlistMatrixRep", "IsPlistMatrixRep and IsMutable",
    "IsList", "IsList", "IsList", "IsList" ],
  function( M, N, srcrows, dstrows, srccols, dstcols )
    local i;
    if ValueOption( "check" ) <> false and
       not IsIdenticalObj( M![BDPOS], N![BDPOS] ) then
      Error( "<M> and <N> are not compatible" );
    fi;
    # This eventually should go into the kernel without creating
    # intermediate objects:
    for i in [1..Length(srcrows)] do
        N![ROWSPOS][dstrows[i]]![ELSPOS]{dstcols} :=
                  M![ROWSPOS][srcrows[i]]![ELSPOS]{srccols};
    od;
  end );

InstallOtherMethod( CopySubMatrix,
  "for two plists -- fallback in case of bad rep.",
  [ "IsPlistRep", "IsPlistRep and IsMutable",
    "IsList", "IsList", "IsList", "IsList" ],
  function( M, N, srcrows, dstrows, srccols, dstcols )
    local i;
    # in this representation all access probably has to go through the
    # generic method selection, so it is not clear whether there is an
    # improvement in moving this into the kernel.
    for i in [1..Length(srcrows)] do
        N[dstrows[i]]{dstcols}:= M[srcrows[i]]{srccols};
    od;
  end );
#T move to another file?

InstallMethod( MatElm,
  [ "IsPlistMatrixRep", "IsPosInt", "IsPosInt" ],
  { M, row, col } -> M![ROWSPOS][row]![ELSPOS][col] );

InstallMethod( SetMatElm,
  [ "IsPlistMatrixRep and IsMutable", "IsPosInt", "IsPosInt", "IsObject" ],
  function( M, row, col, ob )
    if ValueOption( "check" ) <> false then
      if not ob in BaseDomain( M ) then
        Error( "<ob> must lie in the base domain of <M>" );
      elif col > M![RLPOS] then
        Error( "<col> must be at most <M>![RLPOS]" );
      fi;
    fi;
    M![ROWSPOS][row]![ELSPOS][col] := ob;
  end );


############################################################################
# Printing and viewing methods:
############################################################################

InstallMethod( ViewObj, [ "IsPlistMatrixRep" ],
  function( M )
    Print("<");
    if not IsMutable(M) then Print("immutable "); fi;
    Print(Length(M![ROWSPOS]),"x",M![RLPOS],"-matrix over ",M![BDPOS],">");
  end );

InstallMethod( PrintObj, [ "IsPlistMatrixRep" ],
  function( M )
    Print("NewMatrix(IsPlistMatrixRep");
    if IsFinite(M![BDPOS]) and IsField(M![BDPOS]) then
        Print(",GF(",Size(M![BDPOS]),"),");
    else
        Print(",",String(M![BDPOS]),",");
    fi;
    Print(NumberColumns(M),",",Unpack(M),")");
  end );

InstallMethod( Display, [ "IsPlistMatrixRep" ],
  function( M )
    local i;
    Print("<");
    if not IsMutable(M) then Print("immutable "); fi;
    Print(Length(M![ROWSPOS]),"x",M![RLPOS],"-matrix over ",M![BDPOS],":\n");
    for i in [1..Length(M![ROWSPOS])] do
        if i = 1 then
            Print("[");
        else
            Print(" ");
        fi;
        Print(M![ROWSPOS][i]![ELSPOS],"\n");
    od;
    Print("]>\n");
  end );

InstallMethod( String, [ "IsPlistMatrixRep" ],
  function( M )
    local st;
    st := "NewMatrix(IsPlistMatrixRep";
    Add(st,',');
    if IsFinite(M![BDPOS]) and IsField(M![BDPOS]) then
        Append(st,"GF(");
        Append(st,String(Size(M![BDPOS])));
        Append(st,"),");
    else
        Append(st,String(M![BDPOS]));
        Append(st,",");
    fi;
    Append(st,String(NumberColumns(M)));
    Add(st,',');
    Append(st,String(Unpack(M)));
    Add(st,')');
    return st;
  end );

############################################################################
# Arithmetical operations:
############################################################################

InstallMethod( \+,
  [ "IsPlistMatrixRep", "IsPlistMatrixRep" ],
  function( a, b )
    if ValueOption( "check" ) <> false and
       ( not IsIdenticalObj( a![BDPOS], b![BDPOS] ) or
         a![RLPOS] <> b![RLPOS] ) then
      Error( "<a> and <b> are not compatible" );
    fi;
    return MakeIsPlistMatrixRep( a![BDPOS], a![EMPOS], a![RLPOS],
               SUM_LIST_LIST_DEFAULT( a![ROWSPOS], b![ROWSPOS] ), false );
  end );

InstallMethod( \-,
  [ "IsPlistMatrixRep", "IsPlistMatrixRep" ],
  function( a, b )
    if ValueOption( "check" ) <> false and
       ( not IsIdenticalObj( a![BDPOS], b![BDPOS] ) or
         a![RLPOS] <> b![RLPOS] ) then
      Error( "<a> and <b> are not compatible" );
    fi;
    return MakeIsPlistMatrixRep( a![BDPOS], a![EMPOS], a![RLPOS],
               DIFF_LIST_LIST_DEFAULT( a![ROWSPOS], b![ROWSPOS] ), false );
  end );

InstallMethod( \*,
  [ "IsPlistMatrixRep", "IsPlistMatrixRep" ],
  function( a, b )
    local i,j,l,v,w;
    if ValueOption( "check" ) <> false then
      if not a![RLPOS] = Length(b![ROWSPOS]) then
        ErrorNoReturn("\\*: Matrices do not fit together");
      elif not IsIdenticalObj(a![BDPOS],b![BDPOS]) then
        ErrorNoReturn("\\*: Matrices not over same base domain");
      fi;
    fi;
    l := ListWithIdenticalEntries(Length(a![ROWSPOS]),0);
    for i in [1..Length(l)] do
        if b![RLPOS] = 0 then
            l[i] := b![EMPOS];
        else
            v := a![ROWSPOS][i];
            w := ZeroVector(b![RLPOS],b![EMPOS]);
            for j in [1..a![RLPOS]] do
                AddRowVector(w,b![ROWSPOS][j],v[j]);
            od;
            l[i] := w;
        fi;
    od;
    if not IsMutable(a) and not IsMutable(b) then
        MakeImmutable(l);
    fi;
    return MakeIsPlistMatrixRep( a![BDPOS], a![EMPOS], b![RLPOS], l, false );
  end );

InstallMethod( \=,
  [ "IsPlistMatrixRep", "IsPlistMatrixRep" ],
  { a, b } -> EQ_LIST_LIST_DEFAULT( a![ROWSPOS], b![ROWSPOS] ) );

InstallMethod( \<,
  [ "IsPlistMatrixRep", "IsPlistMatrixRep" ],
  { a, b } -> LT_LIST_LIST_DEFAULT( a![ROWSPOS], b![ROWSPOS] ) );

# According to "Mutability Status and List Arithmetic":
# If the result is mutable then
# all its rows are mutable if the first row of 'M' is mutable,
# and all its rows are immutable otherwise.
InstallMethod( AdditiveInverseSameMutability,
  [ "IsPlistMatrixRep" ],
  function( M )
    local l;

    if not IsMutable( M ) then
      l:= MakeImmutable( List( M![ROWSPOS], AdditiveInverseImmutable ) );
    elif 0 < NumberRows( M ) and IsMutable( M![ROWSPOS][1] ) then
      l:= List( M![ROWSPOS], AdditiveInverseMutable );
    else
      l:= List( M![ROWSPOS], AdditiveInverseImmutable );
    fi;
    return MakeIsPlistMatrixRep( M![BDPOS], M![EMPOS], M![RLPOS], l, false );
  end );

InstallMethod( AdditiveInverseImmutable,
  [ "IsPlistMatrixRep" ],
  M -> MakeIsPlistMatrixRep( M![BDPOS], M![EMPOS], M![RLPOS],
           MakeImmutable( List( M![ROWSPOS], AdditiveInverseImmutable ) ),
           false ) );

# all rows mutable
InstallMethod( AdditiveInverseMutable,
  [ "IsPlistMatrixRep" ],
  M -> MakeIsPlistMatrixRep( M![BDPOS], M![EMPOS], M![RLPOS],
           List( M![ROWSPOS], AdditiveInverseMutable ), false ) );

InstallMethod( ZeroSameMutability,
  [ "IsPlistMatrixRep" ],
  function( M )
    local l;

    if not IsMutable( M ) then
      l:= MakeImmutable( List( M![ROWSPOS], ZeroImmutable ) );
    elif 0 < NumberRows( M ) and IsMutable( M![ROWSPOS][1] ) then
      l:= List( M![ROWSPOS], ZeroMutable );
    else
      l:= List( M![ROWSPOS], ZeroImmutable );
    fi;
    return MakeIsPlistMatrixRep( M![BDPOS], M![EMPOS], M![RLPOS], l, false );
  end );

InstallMethod( ZeroImmutable,
  [ "IsPlistMatrixRep" ],
  function( M )
    M:= MakeIsPlistMatrixRep( M![BDPOS], M![EMPOS], M![RLPOS],
            MakeImmutable( List( M![ROWSPOS], ZeroImmutable ) ), false );
    SetIsZero( M, true );
    return M;
  end );

InstallMethod( ZeroMutable,
  [ "IsPlistMatrixRep" ],
  M -> MakeIsPlistMatrixRep( M![BDPOS], M![EMPOS], M![RLPOS],
           List( M![ROWSPOS], ZeroMutable ), false ) );

InstallMethod( IsZero,
  [ "IsPlistMatrixRep" ],
  function( M )
    local i;
    for i in [1..Length(M![ROWSPOS])] do
        if not IsZero(M![ROWSPOS][i]) then
            return false;
        fi;
    od;
    return true;
  end );

InstallMethod( IsOne,
  [ "IsPlistMatrixRep" ],
  function( M )
    local n, i, row;
    if Length(M![ROWSPOS]) <> M![RLPOS] then
        #Error("IsOne: Matrix must be square");
        return false;
    fi;
    n := M![RLPOS];
    for i in [1..n] do
      row:= M![ROWSPOS][i];
      if PositionNonZero( row ) <> i or
         not IsOne( row![ELSPOS][i] ) or
         PositionNonZero( row, i ) <= n then
        return false;
      fi;
    od;
    return true;
  end );

InstallMethod( OneSameMutability,
  [ "IsPlistMatrixRep" ],
  function( M )
    local o, i;
    if M![RLPOS] <> Length(M![ROWSPOS]) then
        #Error("OneSameMutability: Matrix is not square");
        #return;
        return fail;
    fi;
    o := IdentityMatrix(M![RLPOS],M);
    if not IsMutable( M ) then
      # result immutable
      MakeImmutable( o );
      SetIsOne( o, true );
    elif 0 < NumberRows( M ) and IsMutable( M![ROWSPOS][1] ) then
      # all rows mutable
    else
      # mutable, all rows immutable
      M:= IdentityMatrix( M![RLPOS], M );
      for i in [ 1 .. NrRows( o ) ] do
        MakeImmutable( o![ROWSPOS][i] );
      od;
    fi;
    return o;
  end );

InstallMethod( OneMutable,
  [ "IsPlistMatrixRep" ],
  function( M )
    if M![RLPOS] <> Length(M![ROWSPOS]) then
        #Error("OneMutable: Matrix is not square");
        #return;
        return fail;
    fi;
    return IdentityMatrix(M![RLPOS],M);
  end );

InstallMethod( OneImmutable,
  [ "IsPlistMatrixRep" ],
  function( M )
    if M![RLPOS] <> Length(M![ROWSPOS]) then
        #Error("OneImmutable: Matrix is not square");
        #return;
        return fail;
    fi;
    M:= MakeImmutable( IdentityMatrix( M![RLPOS], M ) );
    SetIsOne( M, true );
    return M;
  end );

# For the moment we delegate to the fast kernel arithmetic for plain
# lists of plain lists:

InstallMethod( InverseMutable,
  [ "IsPlistMatrixRep" ],
  function( M )
    local n;
    if M![RLPOS] <> Length(M![ROWSPOS]) then
        #Error("InverseMutable: Matrix is not square");
        #return;
        return fail;
    fi;
    # Make a plain list of lists:
    n := List(M![ROWSPOS],x->x![ELSPOS]);
    n := InverseMutable(n);  # Invert!
    if n = fail then return fail; fi;
    return Matrix(n,Length(n),M);
  end );

InstallMethod( InverseImmutable,
  [ "IsPlistMatrixRep" ],
  function( M )
    local n;
    if M![RLPOS] <> Length(M![ROWSPOS]) then
        #Error("InverseMutable: Matrix is not square");
        #return;
        return fail;
    fi;
    # Make a plain list of lists:
    n := List(M![ROWSPOS],x->x![ELSPOS]);
    n := InverseImmutable(n);  # Invert!
    if n = fail then return fail; fi;
    return Matrix(n,Length(n),M);
  end );

InstallMethod( InverseSameMutability,
  [ "IsPlistMatrixRep" ],
  function( M )
    local n;
    if M![RLPOS] <> Length(M![ROWSPOS]) then
        #Error("InverseMutable: Matrix is not square");
        #return;
        return fail;
    fi;
    # Make a plain list of lists:
    n := List(M![ROWSPOS],x->x![ELSPOS]);
    if not IsMutable(M) then
        MakeImmutable(n);
    fi;
    n := InverseSameMutability(n);  # Invert!
    if n = fail then return fail; fi;
    return Matrix(n,Length(n),M);
#T does 'Matrix' respect the "mixed mutability rules" from "Mutability Status ..."?
  end );

InstallMethod( RankMat,
  [ "IsPlistMatrixRep" ],
  function( M )
    M:= M![ROWSPOS];
    if Length( M ) = 0 then
      return 0;
    fi;
    return RankMat( List( M, x -> x![ELSPOS] ) );
  end );

InstallMethodWithRandomSource( Randomize,
  "for a random source and a mutable plist matrix",
  [ IsRandomSource, IsPlistMatrixRep and IsMutable ],
  function( rs, M )
    local v;
    for v in M![ROWSPOS] do
        Randomize( rs, v );
    od;
    return M;
  end );

InstallMethod( TransposedMatMutable,
  [ "IsPlistMatrixRep" ],
  function( M )
    local i,n;
    n := ListWithIdenticalEntries(M![RLPOS],0);
    for i in [1..M![RLPOS]] do
        n[i]:= Vector(List(M![ROWSPOS],v->v![ELSPOS][i]),M![EMPOS]);
    od;
    return MakeIsPlistMatrixRep( M![BDPOS], M![EMPOS], Length(M![ROWSPOS]), n, false );
  end );

InstallMethod( \*,
  [ "IsPlistVectorRep", "IsPlistMatrixRep" ],
  function( v, M )
    local i,res,s;
    if ValueOption( "check" ) <> false and
       ( not IsIdenticalObj( v![BDPOS], M![BDPOS] ) or
         Length( v ) <> NumberRows( M ) ) then
      Error( "<v> and <M> are not compatible" );
    fi;
    res := ZeroVector(M![RLPOS],M![EMPOS]);
    for i in [1..Length(v![ELSPOS])] do
        s := v![ELSPOS][i];
        if not IsZero(s) then
            AddRowVector( res, M![ROWSPOS][i], s );
        fi;
    od;
    if not IsMutable(v) and not IsMutable(M) then
        MakeImmutable(res);
    fi;
    return res;
  end );

#InstallMethod( \^,
#  [ "IsPlistMatrixRep", "IsInt" ],
#  function( M, i )
#    local mi;
#    if M![RLPOS] <> Length(M![ROWSPOS]) then
#        #Error("\\^: Matrix must be square");
#        #return;
#        return fail;
#    fi;
#    if i = 0 then return OneSameMutability(M);
#    elif i > 0 then return POW_OBJ_INT(M,i);
#    else
#        mi := InverseSameMutability(M);
#        if mi = fail then return fail; fi;
#        return POW_OBJ_INT( mi, -i );
#    fi;
#  end );

InstallMethod( ConstructingFilter,
  [ "IsPlistVectorRep" ],
  v -> IsPlistVectorRep );

InstallMethod( ConstructingFilter,
  [ "IsPlistMatrixRep" ],
  M -> IsPlistMatrixRep );

InstallMethod( ChangedBaseDomain,
  [ "IsPlistVectorRep", "IsRing" ],
  function( v, r )
    r:= NewVector( IsPlistVectorRep, r, v![ELSPOS] );
    if not IsMutable( v ) then
      MakeImmutable( r );
    fi;
    return r;
  end );

InstallMethod( ChangedBaseDomain,
  [ "IsPlistMatrixRep", "IsRing" ],
  function( M, r )
    r:= NewMatrix( IsPlistMatrixRep, r, M![RLPOS],
                   List( M![ROWSPOS], x-> x![ELSPOS] ) );
    if not IsMutable( M ) then
      MakeImmutable( r );
    fi;
    return r;
  end );

# We know that 'CompatibleVectorFilter( M )' is 'IsPlistVectorRep'.
InstallMethod( CompatibleVector,
  [ "IsPlistMatrixRep" ],
  M -> NewZeroVector( IsPlistVectorRep, BaseDomain( M ), NumberRows( M ) ) );

