diff --git a/CHANGES b/CHANGES
index 2a18ca9..92feebd 100644
--- a/CHANGES
+++ b/CHANGES
@@ -3,6 +3,7 @@ This file describes changes in the orb package.
5.x.y (2026-MM-DD)
- Once again allow using this package without its kernel extension
(this was accidentally broken in version 4.9.1)
+ - Add hash functions for non-compressed finite-field vectors and matrices
5.0.1 (2025-06-20)
- Various janitorial changes
diff --git a/doc/hash.xml b/doc/hash.xml
index c239b37..da6a14a 100644
--- a/doc/hash.xml
+++ b/doc/hash.xml
@@ -89,9 +89,7 @@ Oper="ChooseHashFunction"/>.
a record
This method is for compressed vectors over the field GF(2) of
-two elements. Note that there is no hash function for non-compressed
-vectors over GF(2) because those objects cannot efficiently
-be recognised from their type.
+two elements.
Note that you can only use the resulting hash functions for vectors
of the same length.
@@ -103,9 +101,7 @@ of the same length.
a record
This method is for compressed vectors over a finite field with up
-to 256 elements. Note that there is no hash function for
-non-compressed such vectors because those objects cannot efficiently be
-recognised from their type.
+to 256 elements.
Note that you can only use the resulting hash functions for vectors
of the same length.
@@ -117,9 +113,7 @@ of the same length.
a record
This method is for compressed matrices over the field GF(2) of
-two elements. Note that there is no hash function for non-compressed
-matrices over GF(2) because those objects cannot efficiently
-be recognised from their type.
+two elements.
Note that you can only use the resulting hash functions for matrices
of the same size.
@@ -131,9 +125,33 @@ of the same size.
a record
This method is for compressed matrices over a finite field with up
-to 256 elements. Note that there is no hash function for
-non-compressed such vectors because those objects cannot efficiently be
-recognised from their type.
+to 256 elements.
+
+Note that you can only use the resulting hash functions for matrices
+of the same size.
+
+
+
+
+
+ a record
+
+This method is for non-compressed vectors of finite field elements.
+It uses the smallest field containing all entries of the vector, so
+equal vectors involving entries from subfields get the same hash.
+
+Note that you can only use the resulting hash functions for vectors
+of the same length.
+
+
+
+
+
+ a record
+
+This method is for non-compressed matrices of finite field elements.
+It hashes the rows using the method for non-compressed vectors of
+finite field elements.
Note that you can only use the resulting hash functions for matrices
of the same size.
diff --git a/gap/hash.gd b/gap/hash.gd
index aa8f68c..73f6281 100644
--- a/gap/hash.gd
+++ b/gap/hash.gd
@@ -45,6 +45,7 @@ DeclareGlobalFunction( "ORB_HashFunctionForShortGF2Vectors" );
DeclareGlobalFunction( "ORB_HashFunctionForShort8BitVectors" );
DeclareGlobalFunction( "ORB_HashFunctionForGF2Vectors" );
DeclareGlobalFunction( "ORB_HashFunctionFor8BitVectors" );
+DeclareGlobalFunction( "ORB_HashFunctionForFFEVectors" );
DeclareGlobalFunction( "ORB_HashFunctionForCompressedMats" );
DeclareGlobalFunction( "ORB_HashFunctionForIntegers" );
DeclareGlobalFunction( "ORB_HashFunctionForMemory" );
diff --git a/gap/hash.gi b/gap/hash.gi
index 3a6b474..c0a1db7 100644
--- a/gap/hash.gi
+++ b/gap/hash.gi
@@ -531,6 +531,20 @@ function(v,data)
return HashKeyBag(v,101,3*GAPInfo.BytesPerVariable,data[2]) mod data[1] + 1;
end );
+InstallGlobalFunction( ORB_HashFunctionForFFEVectors,
+function(v,data)
+ local f,x;
+ if not IsRowVector(v) or not IsFFECollection(v) then
+ return fail;
+ fi;
+ f := BaseDomain(v);
+ x := NumberFFVector(v, Size(f));
+ if x = fail then
+ return fail;
+ fi;
+ return x mod data[1] + 1;
+end );
+
InstallMethod( ChooseHashFunction, "failure method",
[IsObject,IsInt],
function(p,hashlen)
@@ -540,7 +554,7 @@ InstallMethod( ChooseHashFunction, "failure method",
# Now the choosing methods for compressed vectors:
InstallMethod( ChooseHashFunction, "for compressed gf2 vectors",
- [IsGF2VectorRep and IsList,IsInt],
+ [IsRowVector and IsFFECollection and IsGF2VectorRep,IsInt],
function(p,hashlen)
local bytelen;
bytelen := QuoInt(Length(p),8);
@@ -557,7 +571,7 @@ InstallMethod( ChooseHashFunction, "for compressed gf2 vectors",
end );
InstallMethod( ChooseHashFunction, "for compressed 8bit vectors",
- [Is8BitVectorRep and IsList,IsInt],
+ [IsRowVector and IsFFECollection and Is8BitVectorRep,IsInt],
function(p,hashlen)
local bytelen,i,q,qq;
q := Q_VEC8BIT(p);
@@ -595,7 +609,7 @@ function(x,data)
end );
InstallMethod( ChooseHashFunction, "for compressed gf2 matrices",
- [IsGF2MatrixRep and IsList,IsInt],
+ [IsMatrix and IsFFECollColl and IsGF2MatrixRep and IsList,IsInt],
function(p,hashlen)
local data;
data := [hashlen,ChooseHashFunction(p[1],hashlen),
@@ -605,7 +619,7 @@ InstallMethod( ChooseHashFunction, "for compressed gf2 matrices",
end );
InstallMethod( ChooseHashFunction, "for compressed 8bit matrices",
- [Is8BitMatrixRep and IsList,IsInt],
+ [IsMatrix and IsFFECollColl and Is8BitMatrixRep and IsList,IsInt],
function(p,hashlen)
local data,q;
q := Q_VEC8BIT(p[1]);
@@ -782,17 +796,32 @@ InstallMethod( ChooseHashFunction, "for lists of matrices",
end );
InstallMethod( ChooseHashFunction,
- "for finite field vectors over big finite fields",
- [IsList, IsInt],
+ "for finite field vectors",
+ [IsRowVector and IsFFECollection, IsInt],
function( l, hashlen )
- local f,q;
- if NestingDepthA(l) = 1 and Length(l) > 0 and IsFFE(l[1]) then
- f := Field(l);
- q := Size(f);
- return rec( func := ORB_HashFunctionForShort8BitVectors,
- data := [hashlen,q] );
+ if IsGF2VectorRep(l) or Is8BitVectorRep(l) then
+ TryNextMethod();
fi;
- TryNextMethod();
+ return rec( func := ORB_HashFunctionForFFEVectors,
+ data := [hashlen] );
+ end );
+
+InstallMethod( ChooseHashFunction,
+ "for finite field matrices",
+ [IsMatrix and IsFFECollColl, IsInt],
+ function( m, hashlen )
+ local r;
+ if Length(m) = 0 then
+ r := rec( func := ORB_HashFunctionForFFEVectors,
+ data := [hashlen] );
+ else
+ r := ChooseHashFunction( m[1], hashlen );
+ if r = fail then
+ return fail;
+ fi;
+ fi;
+ return rec( func := ORB_HashFunctionForMatList,
+ data := [101,hashlen,r] );
end );
if IsBound(HASH_FUNC_FOR_PPERM) then
diff --git a/tst/bugfix.tst b/tst/bugfix.tst
index d674ad3..64e15c6 100644
--- a/tst/bugfix.tst
+++ b/tst/bugfix.tst
@@ -64,5 +64,32 @@ gap> hf.func(w, hf.data);
gap> hf.func(v, hf.data);
446
+# verify plain finite field vectors and matrices can be hashed using the
+# minimal field generated by their entries
+gap> v := [Z(2)^0, 0*Z(2), Z(4)];;
+gap> w := [Z(2^2)^0, 0*Z(2), Z(4)];;
+gap> v = w;
+true
+gap> hf := ChooseHashFunction(v, 1009);;
+gap> IsRecord(hf);
+true
+gap> hf.func(v, hf.data) = hf.func(w, hf.data);
+true
+gap> m := IdentityMat(2, GF(1024));;
+gap> n := IdentityMat(2, GF(2));;
+gap> m = n;
+true
+gap> hf := ChooseHashFunction(m, 1009);;
+gap> IsRecord(hf);
+true
+gap> hf.func(m, hf.data) = hf.func(n, hf.data);
+true
+gap> m := [[Z(3)^0, 0*Z(3)], [0*Z(3), Z(3)^0]];;
+gap> hf := ChooseHashFunction(m, 1009);;
+gap> IsRecord(hf);
+true
+gap> IsInt(hf.func(m, hf.data));
+true
+
#
gap> STOP_TEST("Orb package: bugfix.tst", 0);