/*************************************************************************** ** *A cvec.c cvec-package Max Neunhoeffer ** ** Copyright (C) 2007 Max Neunhoeffer, Lehrstuhl D f. Math., RWTH Aachen ** This file is free software, see license information at the end. **
*/
#include <stdlib.h>
#include"gap_all.h"// GAP headers
#ifdef SYS_IS_64_BIT #include"gf2lib_64.c"
WORD myarena[(4096*1024+1024*1024)/8]; /* we assume 4MB of cache, aligned to 1MB */
WORD *arenastart; #else #include"gf2lib_32.c"
WORD myarena[(2048*1024+1024*1024)/4]; /* we assume 2MB of cache, aligned to 1MB */
WORD *arenastart; #endif
/* Our basic unit is a C unsigned long: */ typedefunsignedlong Word; /* Our basic unit for operations, 32 or 64 bits */ #define Word32 UInt4
#define WORDALLONE (~(0UL)) #define BYTESPERWORD sizeof(Word) #define CACHESIZE (512L*1024L) #define CACHELINE 64 /* If you want to change the following limit, please also change the
* corresponding value in cvec.gi in CVEC_NewCVecClass! */ #define MAXDEGREE 1024
/************************************/ /* Macros to access the data types: */ /************************************/
/* The following positions are exported into the record CVEC for use * within the GAP part: If you add a position, please document it
* in gap/cvec.gd and export it in InitLibrary! */
#ifndef DATA_TYPE #define DATA_TYPE(type) ELM_PLIST( type, 3 ) #endif #define DATA_OBJ(obj) DATA_TYPE(TYPE_DATOBJ(obj)) /* Note: The index 3 is a magic constant taken from the GAP library and kernel. Future GAP versions will provide the DATA_TYPE macro in the kernel headers, so that we do not need to rely on magic constants anymore.
*/
/* currently unused, see below: #define PREPARE(f) \ Word *wi = (Word *) (CHARS_STRING(ELM_PLIST(f,IDX_wordinfo))); \ register Word offset = wi[OFF_offset]; \ register Word mask = wi[OFF_mask]; \ register Int shift = INT_INTOBJ(ELM_PLIST(f,IDX_bitsperel))-1; \ register Int p = INT_INTOBJ(ELM_PLIST(f,IDX_p)) #define REDUCE(x) (x - (((x + offset) & mask) >> shift) * p)
*/
/* The following is used for arithmetic: */ #define PREPARE(f) \
Word *wi = (Word *) (CHARS_STRING(ELM_PLIST(f,IDX_wordinfo))); \ register Word offset = wi[OFF_offset]; \ register Word mask = wi[OFF_mask]; \ registerInt shift = INT_INTOBJ(ELM_PLIST(f,IDX_bitsperel))-1; \ registerInt ps = INT_INTOBJ(ELM_PLIST(f,IDX_p))*(mask>>shift) #define REDUCE(x) x - ((((x+offset)&mask) - (((x+offset)&mask)>>shift))&ps)
#define PREPARE_cl(v,cl) \
Obj cl = DATA_OBJ(v); #define PREPARE_clfi(v,cl,fi) \
Obj cl = DATA_OBJ(v); \
Obj fi = ELM_PLIST(cl,IDX_fieldinfo) #define PREPARE_p(fi) Int p = INT_INTOBJ(ELM_PLIST(fi,IDX_p)) #define PREPARE_d(fi) Int d = INT_INTOBJ(ELM_PLIST(fi,IDX_d)) #define PREPARE_q(fi) Int q = INT_INTOBJ(ELM_PLIST(fi,IDX_q)) #define PREPARE_cp(fi) Int *cp = \
(Int *)CHARS_STRING(ELM_PLIST(fi,IDX_conway)) #define PREPARE_bpe(fi) Int bitsperel = \
INT_INTOBJ(ELM_PLIST(fi,IDX_bitsperel)) #define PREPARE_epw(fi) Int elsperword = \
INT_INTOBJ(ELM_PLIST(fi,IDX_elsperword)) #define PREPARE_type(fi) Obj type = ELM_PLIST(fi,IDX_type) #define PREPARE_tab1(fi) Obj tab1 = ELM_PLIST(fi,IDX_tab1) #define PREPARE_tab2(fi) Obj tab2 = ELM_PLIST(fi,IDX_tab2) #define PREPARE_mask(fi) Word mask = \
((Word *)CHARS_STRING(ELM_PLIST(fi,IDX_wordinfo)))[OFF_mask] #define PREPARE_offset(fi) Word offset = \
((Word *)CHARS_STRING(ELM_PLIST(fi,IDX_wordinfo)))[OFF_offset] #define PREPARE_maskp(fi) Word maskp = \
((Word *)CHARS_STRING(ELM_PLIST(fi,IDX_wordinfo)))[OFF_maskp] #define PREPARE_cutmask(fi) Word cutmask = \
((Word *)CHARS_STRING(ELM_PLIST(fi,IDX_wordinfo)))[OFF_cutmask]
#define DATA_CVEC(cvec) ((Word *)((ADDR_OBJ(cvec))+1)) #define CONST_DATA_CVEC(cvec) ((const Word *)((CONST_ADDR_OBJ(cvec))+1))
/* The following macros is called IS_CVEC, but actually does not test * the complete type. It only does some cheap plausibility check. In
* addition, it also covers the case of big FF scalars (CSca). */
static Obj FuncCVEC_TEST_ASSUMPTIONS(Obj self) /* Result 0 is OK, otherwise something is wrong... */
{ /* Note in addition, that d * length of a vector must fit into a
* C Int! */ if (0UL - 1UL != WORDALLONE) return INTOBJ_INT(1); if ( WORDALLONE >> 0 != WORDALLONE ) return INTOBJ_INT(2); if ( sizeof(Word) != 8 && sizeof(Word) != 4) return INTOBJ_INT(3); #ifdef SYS_IS_64_BIT if (sizeof(Word) != 8) return INTOBJ_INT(5); #else if (sizeof(Word) != 4) return INTOBJ_INT(6); #endif if (sizeof(Word32) != 4) return INTOBJ_INT(7); #if !(__BYTE_ORDER == __LITTLE_ENDIAN) && !(__BYTE_ORDER == __BIG_ENDIAN) return INTOBJ_INT(4); #else return INTOBJ_INT(0); #endif
}
static Obj FuncCVEC_COEFF_LIST_TO_C(Obj self, Obj po, Obj s)
{ /* po is a list of (small) GAP integers, s a GAP string. */ Int l,i; Int *p;
l = LEN_PLIST(po);
GrowString(s,sizeof(Int)*l);
SET_LEN_STRING(s,sizeof(Int)*l);
p = (Int *) CHARS_STRING(s); for (i = 1;i <= l;i++) *p++ = INT_INTOBJ(ELM_PLIST(po,i)); return s;
}
static Obj FuncCVEC_FINALIZE_FIELDINFO(Obj self, Obj f)
{
Obj s;
Word *po;
Word w; Int j;
PREPARE_p(f);
PREPARE_bpe(f);
PREPARE_epw(f);
/* GARBAGE COLLECTION POSSIBLE */
s = NEW_STRING(sizeof(Word) * 4);
po = (Word *) CHARS_STRING(s); if (p & 1) { /* Odd characteristic */ for (w = 1UL,j = 1;j < elsperword;j++)
w = (w << bitsperel)+1UL;
po[OFF_mask] = w << (bitsperel-1);
po[OFF_offset] = po[OFF_mask] - w * p;
po[OFF_maskp] = (1UL << bitsperel)-1;
po[OFF_cutmask] = w * po[OFF_maskp];
} else { /* Characteristic 2 */
po[OFF_mask] = 0;
po[OFF_offset] = 0;
po[OFF_maskp] = 1;
po[OFF_cutmask] = WORDALLONE;
}
SET_ELM_PLIST(f,IDX_wordinfo,s);
CHANGED_BAG(f); return f;
}
static Obj FuncCVEC_INIT_SMALL_GFQ_TABS(Obj self, Obj pp, Obj cp, Obj tab1, Obj tab2)
{
UInt p = INT_INTOBJ(pp);
UInt d = LEN_PLIST(cp) - 1;
FF ff = FiniteField(p, d);
UInt q = SIZE_FF(ff);
UInt poly; /* Conway polynomial of extension */
UInt i, l, f, n, e; /* loop variables */
// convert the Conway polynomial
poly = 0; for ( i = 1, l = 1; i <= d; l *= p, i++ ) {
poly += l * INT_INTOBJ(ELM_PLIST(cp, i));
}
/* We already know that tab1 and tab2 are lists of length q. */
/* We want ELM_PLIST(tab1,VAL_FFE(fe)+1) to be the small integer * whose p-adic expansion corresponds to fe in F_p[x]/cp where * cp is the Conway polynomial. tab2 works the opposite way, that is,
* ELM_PLIST(tab2,e+1) = fe for the corresponding FFE. */
SET_ELM_PLIST(tab1,1,INTOBJ_INT(0));
SET_ELM_PLIST(tab2,1,NEW_FFE(ff,0)); for ( e = 1, n = 0; n < q-1; ++n ) {
SET_ELM_PLIST(tab1,n+2,INTOBJ_INT(e));
SET_ELM_PLIST(tab2,e+1,NEW_FFE(ff,n+1)); if ( p != 2 ) {
f = p * (e % (q/p)); l = (p - (e/(q/p))) % p; e = 0; for ( i = 1; i < q; i *= p )
e = e + i * ((f/i + l * (poly/i)) % p);
} else { if ( 2*e & q ) e = 2*e ^ poly ^ q; else e = 2*e;
}
} return 0L;
}
// Given a FFE o, perform lookup in tab1 staticinline Obj FFE_TO_INTOBJ(Obj tab1, Int q, Obj o)
{
FFV v = VAL_FFE(o); if (v == 0) return INTOBJ_INT(0);
typedefstruct SeqAcc { Int d; Int bitsperel; Int elsperword; Int pos; /* one based */
Word mask; /* to extract */ Int bitpos; Int offset; /* in words */
} seqaccess;
/* For the following macros sa is *seqaccess, v is *Word, off is Int, s is Int,
* which is a prime field scalar. */
/* Gets a prime field component: */ #define GET_VEC_ELM(sa,w,off) \
(((w)[(sa)->offset+off] & (sa)->mask) >> (sa)->bitpos)
/* Sets a prime field component: */ #define SET_VEC_ELM(sa,w,off,s) \
(((w)[((sa)->offset)+(off)])) = \
((((w)[((sa)->offset)+(off)]) & (~((sa)->mask))) | (s << ((sa)->bitpos)))
/* Initializes the sequential access struct, v is a cvec: */ staticinlinevoid INIT_SEQ_ACCESS(seqaccess *sa, Obj v, Int pos)
{
PREPARE_clfi(v,cl,fi);
PREPARE_d(fi);
PREPARE_bpe(fi);
PREPARE_epw(fi);
/* Initializes the sequential access struct, v is a cvec: */ #define MOVE_SEQ_ACCESS(sa,pos) \
(sa)->offset = (sa)->d*(((pos)-1) / (sa)->elsperword); \
(sa)->bitpos = (((pos)-1) % (sa)->elsperword) * (sa)->bitsperel; \
(sa)->mask = ((1UL << (sa)->bitsperel)-1) << (sa)->bitpos;
/*******************************************************/ /* Interfacing stuff for the objects to the GAP level: */ /*******************************************************/
si = sizeof(Word) * INT_INTOBJ(ELM_PLIST(cl,IDX_wordlen)); /* GARBAGE COLLECTION POSSIBLE */
v = NewBag( T_DATOBJ, sizeof( Obj ) + si );
SET_TYPE_DATOBJ(v, type); return v;
}
static Obj FuncCVEC_MAKEZERO(Obj self, Obj v)
{ if (!IS_CVEC(v)) { return OurErrorBreakQuit("CVEC_MAKEZERO: no cvec");
}
{ Int si;
PREPARE_cl(v,cl);
si = INT_INTOBJ(ELM_PLIST(cl,IDX_wordlen));
memset(DATA_CVEC(v),0,sizeof(Word)*si);
} return 0L;
}
static Obj FuncCVEC_COPY(Obj self, Obj v, Obj w)
{ if (!IS_CVEC(v) || !IS_CVEC(w)) { return OurErrorBreakQuit("CVEC_COPY: no cvec");
}
{ Int si,si2;
PREPARE_cl(v,cl);
PREPARE_cl(w,cl2);
si = INT_INTOBJ(ELM_PLIST(cl,IDX_len));
si2 = INT_INTOBJ(ELM_PLIST(cl2,IDX_len)); if (si != si2) { return OurErrorBreakQuit("CVEC_COPY: unequal length");
}
si = INT_INTOBJ(ELM_PLIST(cl,IDX_wordlen));
memcpy(DATA_CVEC(w),DATA_CVEC(v),sizeof(Word)*si); return 0L;
}
}
static Obj FuncCVEC_CVEC_TO_INTREP(Obj self,Obj v,Obj l) /* This function returns the vector in its integer representation. This * means, that for the case that q <= 65536 or d = 1 each integer * corresponds to one field entry (p-adic expansion for d>1). For bigger * q (and d), we fill a list of lists of length d containing d little * integers for each vector entry, giving the coefficients of the
* polynomial over the prime field representing the residue class. */
{ registerconst Word *pw; Int len; Int size;
if (!IS_CVEC(v)) { return OurErrorBreakQuit("CVEC_CVEC_TO_INTREP: no cvec");
} if (!IS_PLIST(l)) { return OurErrorBreakQuit("CVEC_CVEC_TO_INTREP: no plist");
}
{
PREPARE_clfi(v,cl,fi);
PREPARE_d(fi);
len = INT_INTOBJ(ELM_PLIST(cl,IDX_len));
size = INT_INTOBJ(ELM_PLIST(fi,IDX_size));
if (LEN_PLIST(l) != len) { return OurErrorBreakQuit("CVEC_CVEC_TO_INTREP: different lengths");
}
if (d == 1) { register Word y; register Word w = 0; Int i,ii; for (i = 1,ii = elsperword;i <= len;i++,ii++) { if (ii == elsperword) { w = *pw++; ii = 0; }
y = w & maskp;
w >>= bitsperel;
SET_ELM_PLIST(l,i,INTOBJ_INT((Int) y));
}
} else {
pw -= d; /* This is corrected at i==0! */ Int i; registerInt j; registerInt shift; register Word y; if (size <= 0) { for (i = 0;i < len;i++) {
shift = (i % elsperword) * bitsperel; if (shift == 0) pw += d;
y = 0; for (j = d - 1;j >= 0;j--)
y = y * p + ((pw[j] >> shift) & maskp);
SET_ELM_PLIST(l,i+1,INTOBJ_INT((Int) y));
}
} else { /* size >= 1, we write coefficient lists */ for (i = 0;i < len;i++) {
Obj oo = ELM_PLIST(l,i+1);
shift = (i % elsperword) * bitsperel; if (shift == 0) pw += d; for (j = 0;j < d;j++)
SET_ELM_PLIST(oo,j+1,
INTOBJ_INT((Int)((pw[j] >> shift) & maskp)));
}
}
}
}
} return 0L;
}
static Obj FuncCVEC_INTREP_TO_CVEC(Obj self,Obj l,Obj v) /* This function transfers data in integer representation to the vector. This * means, that for the case that q <= 65536 or d = 1, each integer * corresponds to one field entry (p-adic expansion for d > 1). For * bigger q (and d), we have a list of lists of length d of little * integers such that every d numbers give the coefficients of the * polynomial over the prime field representing the residue class. * The length of the list l must correspond to the length of v. As an * exception, the elements in integer representation may also be GAP
* FFEs, if those are in the same field or a subfield. */
{ register Word *pw;
if (!IS_CVEC(v)) { return OurErrorBreakQuit("CVEC_INTREP_TO_CVEC: no cvec");
}
{
PREPARE_clfi(v,cl,fi);
PREPARE_d(fi); Int len = INT_INTOBJ(ELM_PLIST(cl,IDX_len));
pw = DATA_CVEC(v);
/* Check lengths: */ if (!IS_PLIST(l) || LEN_PLIST(l) != len) { return OurErrorBreakQuit( "CVEC_INTREP_TO_CVEC: l must be a list of corresponding length to v");
}
if (d == 1) { register Word y; register Word w; registerInt j; register Obj o; Int i; for (i = 1;i <= len;i += elsperword) {
j = i+elsperword-1; if (j > len) j=len;
w = 0; while (j >= i) {
o = ELM_PLIST(l,j); if (IS_INTOBJ(o))
y = (Word) INT_INTOBJ(o); elseif (IS_FFE(o) && CHAR_FF(FLD_FFE(o)) == p &&
DegreeFFE(o) == 1) {
y = (Word) INT_INTOBJ(FFE_TO_INTOBJ(tab1, q, o));
} else { return OurErrorBreakQuit( "CVEC_INTREP_TO_CVEC: invalid object in list");
}
w = (w << bitsperel) | y;
j--;
}
*pw++ = w;
}
} else { /* First clear the space: */
memset(pw,0,sizeof(Word)*INT_INTOBJ(ELM_PLIST(cl,IDX_wordlen)));
pw -= d; /* This is corrected at i==0! */ Int i; registerInt j; registerInt shift; register Word y; register Obj o; for (i = 0;i < len;i++) {
shift = (i % elsperword) * bitsperel; if (shift == 0) pw += d;
o = ELM_PLIST(l,i+1); if (IS_INTOBJ(o)) {
y = (Word) INT_INTOBJ(o); for (j = 0;j < d;j++) {
pw[j] |= ((y % p) << shift);
y /= p;
}
} elseif (IS_FFE(o) && CHAR_FF(FLD_FFE(o)) == p &&
(d % DegreeFFE(o) == 0)) {
y = (Word) INT_INTOBJ(FFE_TO_INTOBJ(tab1, q, o)); for (j = 0;j < d;j++) {
pw[j] |= ((y % p) << shift);
y /= p;
}
} elseif (IS_PLIST(o) && LEN_PLIST(o) == d) { register Obj oo; for (j = 0;j < d;j++) {
oo = ELM_PLIST(o,j+1); if (IS_INTOBJ(oo))
pw[j] |= INT_INTOBJ(oo) << shift; /* This should better be between 0 and p-1! */ elseif (IS_FFE(oo) && CHAR_FF(FLD_FFE(oo)) == p &&
d == 1) { if (VAL_FFE(oo) != 0)
pw[j] |= INT_INTOBJ(ELM_PLIST(tab1,
(VAL_FFE(oo)+1))) << shift; /* We assume that tab1 is for GF(p) here! */
} else { return OurErrorBreakQuit( "CVEC_INTREP_TO_CVEC: invalid object in list");
}
}
} else { return OurErrorBreakQuit( "CVEC_INTREP_TO_CVEC: invalid object in list");
}
}
}
}
} return 0L;
}
static Obj FuncCVEC_INTLI_TO_FFELI(Obj self,Obj fi, Obj l) /* Transforms a list of integers between 0 and q-1 into FFEs. */
{ if (!IS_PLIST(l)) { return OurErrorBreakQuit( "CVEC_INTLI_TO_FFELI: Must be called with a field info and a plain list");
} Int size = INT_INTOBJ(ELM_PLIST(fi,IDX_size)); if (size <= 0) { Int len; Int i;
Obj e;
PREPARE_q(fi);
PREPARE_tab2(fi);
len = LEN_PLIST(l); for (i = 1;i <= len;i++) {
e = ELM_PLIST(l,i); if (!IS_INTOBJ(e) || INT_INTOBJ(e) < 0 || INT_INTOBJ(e) >= q) { return OurErrorBreakQuit("CVEC_INTLI_TO_FFELI: Elements of l " "must be integers between 0 and q-1");
}
e = ELM_PLIST(tab2,INT_INTOBJ(e)+1);
SET_ELM_PLIST(l,i,e);
}
} else { Int len; Int i;
Obj e;
PREPARE_p(fi);
PREPARE_tab2(fi);
len = LEN_PLIST(l); for (i = 1;i <= len;i++) {
e = ELM_PLIST(l,i); if (!IS_INTOBJ(e) || INT_INTOBJ(e) < 0 || INT_INTOBJ(e) >= p) { return OurErrorBreakQuit("CVEC_INTLI_TO_FFELI: Elements of l " "must be integers between 0 and p-1");
}
e = ELM_PLIST(tab2,INT_INTOBJ(e)+1);
SET_ELM_PLIST(l,i,e);
}
} return 0L;
}
static Obj FuncCVEC_FFELI_TO_INTLI(Obj self,Obj fi, Obj l) /* Transforms a list of FFEs into integers between 0 and q-1. */
{ if (!IS_PLIST(l)) { return OurErrorBreakQuit( "CVEC_FFELI_TO_INTLI: Must be called with a field info and a plain list");
}
{ Int len; Int i;
Obj e;
PREPARE_p(fi);
PREPARE_d(fi);
PREPARE_q(fi);
PREPARE_tab1(fi);
len = LEN_PLIST(l); for (i = 1;i <= len;i++) {
e = ELM_PLIST(l,i); if (!IS_FFE(e) || CHAR_FF(FLD_FFE(e)) != p ||
(d % DegreeFFE(e)) != 0) { return OurErrorBreakQuit("CVEC_FFELI_TO_INTLI: Elements of l " "must be finite field elements over " "the right field");
}
e = FFE_TO_INTOBJ(tab1, q, e);
SET_ELM_PLIST(l,i,e);
}
} return 0L;
}
static Obj FuncCVEC_CVEC_TO_NUMBERFFLIST(Obj self, Obj v, Obj l, Obj split)
{
PREPARE_clfi(v,cl,fi);
PREPARE_epw(fi);
PREPARE_bpe(fi);
PREPARE_maskp(fi);
PREPARE_p(fi); Int wordlen = INT_INTOBJ(ELM_PLIST(cl,IDX_wordlen)); const Word *vv = CONST_DATA_CVEC(v);
Word wo;
Word res; Int i,j; Int shift;
for (i = 1;i <= wordlen;i++) {
wo = *vv++;
res = 0;
shift = bitsperel * (elsperword-1); for (j = elsperword;j > 0;j--,shift -= bitsperel)
res = res * p + ((wo >> shift) & maskp); if (split == True) {
SET_ELM_PLIST(l,2*i-1,
INTOBJ_INT(res & ((1UL << (4*BYTESPERWORD)) - 1UL)));
SET_ELM_PLIST(l,2*i,
INTOBJ_INT(res >> (4*BYTESPERWORD)));
} else {
SET_ELM_PLIST(l,i,INTOBJ_INT((Int) res));
}
} return 0L;
}
static Obj FuncCVEC_NUMBERFFLIST_TO_CVEC(Obj self, Obj l, Obj v, Obj split)
{
PREPARE_clfi(v,cl,fi);
PREPARE_epw(fi);
PREPARE_bpe(fi);
PREPARE_p(fi); Int wordlen = INT_INTOBJ(ELM_PLIST(cl,IDX_wordlen));
Word *vv = DATA_CVEC(v);
Word wo;
Word res; Int i,j; Int shift;
for (i = 1;i <= wordlen;i++) { if (split == True) {
res = (Word) INT_INTOBJ(ELM_PLIST(l,2*i-1)) +
(((Word) INT_INTOBJ(ELM_PLIST(l,2*i))) << (4*BYTESPERWORD));
} else {
res = INT_INTOBJ(ELM_PLIST(l,i));
}
shift = 0;
wo = 0; for (j = elsperword;j > 0;j--,shift += bitsperel) {
wo |= (res % p) << shift;
res /= p;
}
*vv++ = wo;;
} return 0L;
}
static Obj FuncCVEC_GREASEPOS(Obj self, Obj v, Obj pivs)
{
PREPARE_clfi(v,cl,fi);
PREPARE_p(fi);
PREPARE_d(fi); Int i,j;
seqaccess sa; Int res; const Word *ww = CONST_DATA_CVEC(v);
i = LEN_PLIST(pivs);
INIT_SEQ_ACCESS(&sa,v,INT_INTOBJ(ELM_PLIST(pivs,i)));
res = 0; while (1) { /* we use break */ /* sa already points to position i! */ for (j = d-1;j >= 0;j--) res = res * p + GET_VEC_ELM(&sa,ww,j); if (--i <= 0) break;
MOVE_SEQ_ACCESS(&sa,INT_INTOBJ(ELM_PLIST(pivs,i)));
} return INTOBJ_INT(res+1);
}
/*******************/ /* The arithmetic: */ /*******************/
/* Now we have the real worker routines, they are meant to be called from */ /* the C level. */
/* For multiplication with scalars with non-prime fields we need some
* infrastructure: */ /* p-adic expansion of the scalar (mod Conway polynomial): */ /* (index 0 contains prime field entry) */ staticInt scbuf[MAXDEGREE+1]; /* Number of entries actually used in sc (beginning with 0): */ /* Is always at least 1. */ staticInt sclen; /* A buffer for elsperword consecutive field entries: */ static Word buf[MAXDEGREE+1];
/* */ /* First some internal inlined functions to do addition and */ /* scalar multiplication: */ /* */
staticinlinevoid ADD2_INL(Word *vv,const Word *ww,Obj f,long i) /* This internal inlined function adds i Words at *ww to the corresponding * Words at *vv. Characteristic 2 and odd characteristic are handled
* separately. */
{
PREPARE_p(f); register Word *vv_r = vv; registerconst Word *ww_r = ww; if (p == 2) { /* Characteristic 2: */ registerlong j = i; while (--j >= 0) *vv_r++ ^= *ww_r++;
} else { /* Odd characteristic: */ register Word wo; registerlong i_r = i;
PREPARE(f); while (--i_r >= 0) {
wo = *vv_r + *ww_r++;
*vv_r++ = REDUCE(wo);
}
}
}
staticinlinevoid ADD3_INL(Word *uu,const Word *vv,const Word *ww,Obj f,long i) /* This internal inlined function adds i Words at *ww to the corresponding * Words at *vv and stores them to *uu. Characteristic 2 and odd
* characteristic are handled separately. */
{
PREPARE_p(f); register Word *uu_r = uu; registerconst Word *vv_r = vv; registerconst Word *ww_r = ww; if (p == 2) { /* Characteristic 2: */ registerlong i_r = i; while (--i_r >= 0) *uu_r++ = (*vv_r++) ^ (*ww_r++);
} else { /* Odd characteristic: */ register Word wo; registerlong i_r = i;
PREPARE(f); while (--i_r >= 0) {
wo = *vv_r++ + *ww_r++;
*uu_r++ = REDUCE(wo);
}
}
}
staticinlinevoid MUL_INL(Word *vv,Obj f,Word s,long i) /* This interal inlined function multiplies i Words at *vv by the scalar * s from the prime field (0 <= s < p). Special cases 0, 1, p-1, and 2 * are handled especially fast. * This code is extremely similar to the code of MUL1_INL, MUL2_INL, ADDMUL_INL,
* and ADDMUL1_INL, so changes should be made accordingly. */
{
PREPARE_p(f); /* Note that the characteristic 2 case is handled properly, because
* s is either 0 or 1. */ /* Handle scalar 1: */ if (s == 1) return; /* Handle scalar 0: */ if (s == 0)
memset(vv,0,sizeof(Word) * i); elseif (s == p - 1) { /* Here we can calculate p-x for all entries x. */
PREPARE(f);
PREPARE_bpe(f); register Word pm1 = (mask >> (bitsperel - 1)) * p; registerlong i_r = i; register Word *vv_r = vv; register Word wo; while (--i_r >= 0) {
wo = pm1 - *vv_r;
*vv_r++ = REDUCE(wo);
}
} else { /* From here on we need some extra variables: */
PREPARE(f); register Word wo,res; register Word *vv_r = vv; registerlong i_r = i;
if (s == 2) { /* Here we can add v to v */ while (--i_r >= 0) {
wo = *vv_r << 1;
*vv_r++ = REDUCE(wo);
}
} else { /* Now to the ugly case: */ while (--i_r >= 0) { /* Handle one word: */ register Word ss = s;
wo = *vv_r;
res = 0; while (1) { if (ss & 1) {
res += wo;
res = REDUCE(res);
}
ss >>= 1; if (!ss) break;
wo <<= 1;
wo = REDUCE(wo);
}
*vv_r++ = res;
}
}
}
}
staticinline Word MUL1_INL(Word wo,Obj f,Word s) /* This interal inlined function multiplies 1 Word wo by the scalar * s from the prime field (0 <= s < p). Special cases 0, 1, p-1, and 2 * are handled especially fast. * This code is extremely similar to the code of MUL_INL, MUL2_INL, ADDMUL_INL,
* and ADDMUL1_INL, so changes should be made accordingly. */
{
PREPARE_p(f); /* Note that the characteristic 2 case is handled properly, because
* s is either 0 or 1. */ /* Handle scalar 1: */ if (s == 1) return wo; /* Handle scalar 0: */ elseif (s == 0) return (Word) 0UL; elseif (s == p - 1) { /* Here we can calculate p-x for all entries x. */
PREPARE(f);
PREPARE_bpe(f); register Word pm1 = (mask >> (bitsperel - 1)) * p;
wo = pm1 - wo; return REDUCE(wo);
} else { /* From here on we need some extra variables: */
PREPARE(f);
if (s == 2) { /* Here we can add v to v */
wo <<= 1; return REDUCE(wo);
} else { /* Now to the ugly case: */ register Word res = 0; while (1) { if (s & 1) {
res += wo;
res = REDUCE(res);
}
s >>= 1; if (!s) break;
wo <<= 1;
wo = REDUCE(wo);
} return res;
}
}
}
staticinlinevoid MUL2_INL(Word *vv,const Word *ww,Obj f,Word s,long i) /* This interal inlined function multiplies i Words at *ww by the scalar * s from the prime field (0 <= s < p) and stores the result to *vv. * Special cases 0, 1, p-1, and 2 are handled especially fast. * This code is extremely similar to the code of MUL_INL, MUL1_INL, ADDMUL_INL,
* and ADDMUL1_INL, so changes should be made accordingly. */
{
PREPARE_p(f); /* Note that the characteristic 2 case is handled properly, because
* s is either 0 or 1. */ /* Handle scalar 1: */ if (s == 1) memcpy(vv,ww,sizeof(Word) * i); /* Handle scalar 0: */ elseif (s == 0) memset(vv,0,sizeof(Word) * i); elseif (s == p - 1) {
PREPARE(f);
PREPARE_bpe(f); /* Here we can calculate p-x for all entries x. */ register Word pm1 = (mask >> (bitsperel - 1)) * p; registerlong i_r = i; registerconst Word *ww_r = ww; register Word *vv_r = vv; register Word wo; while (--i_r >= 0) {
wo = pm1 - *ww_r++;
*vv_r++ = REDUCE(wo);
}
} else { /* From here on we need some extra variables: */
PREPARE(f); register Word wo,res; registerlong i_r = i; registerconst Word *ww_r = ww; register Word *vv_r = vv;
if (s == 2) { /* Here we can add v to v */ while (--i_r >= 0) {
wo = *ww_r++ << 1;
*vv_r++ = REDUCE(wo);
}
} else { /* Now to the ugly case: */ while (--i_r >= 0) { /* Handle one word: */ register Word ss = s;
wo = *ww_r++;
res = 0; while (1) { if (ss & 1) {
res += wo;
res = REDUCE(res);
}
ss >>= 1; if (!ss) break;
wo <<= 1;
wo = REDUCE(wo);
}
*vv_r++ = res;
}
}
}
}
staticinlinevoid ADDMUL_INL(Word *vv,const Word *ww,Obj f,Word s,long i) /* This interal inlined function multiplies i Words at *ww by the scalar * s from the prime field (0 <= s < p) and adds the result to *vv. * Special cases 0, 1, p-1, and 2 are handled especially fast. * This code is extremely similar to the code of MUL_INL, MUL1_INL, MUL2_INL,
* and ADDMUL1_INL, so changes should be made accordingly. */
{
PREPARE_p(f); /* Note that the characteristic 2 case is handled properly, because s
* is either 0 or 1. */ /* Handle scalar 1: */ if (s == 1) ADD2_INL(vv,ww,f,i); /* Handle scalar 0: */ elseif (s == 0) return; elseif (s == p - 1) { /* Here we can calculate p-x for all entries x. */
PREPARE(f);
PREPARE_bpe(f); register Word pm1 = (mask >> (bitsperel - 1)) * p; register Word wo; registerlong i_r = i; registerconst Word *ww_r = ww; register Word *vv_r = vv; while (--i_r >= 0) {
wo = (pm1 - *ww_r++) + *vv_r;
*vv_r++ = REDUCE(wo);
}
} else { /* From here on we need some extra variables: */
PREPARE(f); register Word wo,res; registerlong i_r = i; registerconst Word *ww_r = ww; register Word *vv_r = vv;
if (s == 2) { /* Here we can add v to v */ while (--i_r >= 0) {
wo = (*ww_r++ << 1);
wo = REDUCE(wo) + *vv_r;
*vv_r++ = REDUCE(wo);
}
} else { /* Now to the ugly case: */ while (--i_r >= 0) { /* Handle one word: */ register Word ss = s;
wo = *ww_r++;
res = 0; while (1) { if (ss & 1) {
res += wo;
res = REDUCE(res);
}
ss >>= 1; if (!ss) break;
wo <<= 1;
wo = REDUCE(wo);
}
wo = *vv_r + res;
*vv_r++ = REDUCE(wo);
}
}
}
}
staticinline Word ADDMUL1_INL(Word vv,Word ww,Obj f,Word s) /* This interal inlined function multiplies one Word ww by the scalar * s from the prime field (0 <= s < p) and adds the result to the Word vv. * Special cases 0, 1, p-1, and 2 are handled especially fast. * This code is extremely similar to the code of MUL_INL, MUL1_INL, MUL2_INL,
* and ADDMUL_INL, so changes should be made accordingly. */
{
PREPARE_p(f); if (p == 2) { /* Even characteristic: */ return s == 1 ? (vv ^ ww) : vv;
} else { /* Odd characteristic: */
PREPARE(f); /* Handle scalar 1: */ if (s == 1) {
vv += ww; return REDUCE(vv);
} /* Handle scalar 0: */ elseif (s == 0) return vv; elseif (s == p - 1) { /* Here we can calculate p-x for all entries x. */
PREPARE_bpe(f); register Word pm1 = (mask >> (bitsperel - 1)) * p;
vv = (pm1 - ww) + vv; return REDUCE(vv);
} else { /* From here on we need some extra variables: */ register Word res;
if (s == 2) { /* Here we can add v to v */
ww = ww << 1;
vv = REDUCE(ww) + vv; return REDUCE(vv);
} else { /* Now to the ugly case: */
res = 0; while (1) { if (s & 1) {
res += ww;
res = REDUCE(res);
}
s >>= 1; if (!s) break;
ww <<= 1;
ww = REDUCE(ww);
}
vv += res; return REDUCE(vv);
}
}
}
}
/* * Element access, internally, we distinguish between prime field and * extension field. Also the cases for different representations of * scalars are distinguished. This is later used in the functions for the * GAP level:
*/
staticinlineInt CVEC_Itemp(Obj fi, const Word *v, Int i)
{
PREPARE_epw(fi);
PREPARE_bpe(fi);
PREPARE_maskp(fi);
i--; /* we are now 1 based */ return (Int) ((v[i/elsperword] >> (bitsperel * (i % elsperword))) & maskp);
}
staticinlinevoid CVEC_Itemq(Obj fi, const Word *v, Int i) /* Writes scalar into scbuf. */
{ Int *sc = scbuf; Int j;
i--; /* we are now 1 based */
PREPARE_epw(fi);
PREPARE_bpe(fi);
PREPARE_d(fi);
PREPARE_maskp(fi); Int shift = (i % elsperword) * bitsperel;
v += d * (i / elsperword);
sclen = 1; /* at least that */ for (j = d;j > 0;j--) {
*sc = ((*v++) >> shift) & maskp; if (*sc) sclen = (sc-scbuf)+1;
sc++;
}
}
staticinlinevoid CVEC_AssItemp(Obj fi, Word *v, Int i, Word sc)
{
PREPARE_epw(fi);
PREPARE_bpe(fi);
PREPARE_maskp(fi);
i--; /* we are now 1 based */
v += i / elsperword; registerInt shift = bitsperel * (i % elsperword);
*v = (*v & (WORDALLONE ^ (maskp << shift))) | (sc << shift);
}
staticinlinevoid CVEC_AssItemq(Obj fi, Word *v, Int i, Int *sc)
{
PREPARE_epw(fi);
PREPARE_bpe(fi);
PREPARE_maskp(fi);
PREPARE_d(fi); Int j;
i--; /* we are now 1 based */ Int shift = (i % elsperword) * bitsperel;
Word mask = WORDALLONE ^ (maskp << shift);
v += d * (i / elsperword); for (j = d;j > 0;j--,v++) {
*v = (*v & mask) | (((Word) (*sc++)) << shift);
}
}
staticinlineInt CVEC_Firstnzp(Obj fi, const Word *v, Int len) /* Returns the index of the first non-zero element in the vector or len+1
* if the vector is equal to zero. This is only for prime fields. */
{ register PREPARE_epw(fi); register PREPARE_bpe(fi); register PREPARE_maskp(fi); registerconst Word *p = v; registerInt i = 1; registerInt im = 0; /* i mod elsperword */ register Word w = 0;
while (i <= len) { if (im == 0) {
w = *p++; if (w == 0) {
i += elsperword; continue;
}
} if (w & maskp) return i;
w >>= bitsperel;
i++; if (++im == elsperword) im = 0;
} return len+1;
}
staticinlineInt CVEC_Firstnzq(Obj fi, const Word *v, Int len, Int wordlen) /* Returns the index of the first non-zero element in the vector or 0,
* if the vector is equal to zero. This is for extension fields. */
{
PREPARE_epw(fi);
PREPARE_bpe(fi);
PREPARE_d(fi); registerconst Word *p = v; registerInt i = 0; registerInt im = 0;
/* First look for the first non-vanishing word: */
im = wordlen; while (*p == 0 && i < im) { p++; i++; } if (i >= im) return len+1; /* Vector is zero */
/* Go back to beginning of the d Words: */
im = i % d;
p -= im;
i = ((i-im) / d) * elsperword + 1;
{ register PREPARE_maskp(fi); while (1) { for (im = d-1;im >= 0;im--) if (p[im] & maskp) return i;
maskp <<= bitsperel;
i++;
}
} /* Never reached: */ return 0;
}
staticinlineInt CVEC_Lastnzp(Obj fi, const Word *v, Int len) /* Returns the index of the last non-zero element in the vector or 0
* if the vector is equal to zero. This is only for prime fields. */
{
PREPARE_epw(fi);
PREPARE_bpe(fi);
PREPARE_maskp(fi); Int i = len-1; /* code used to be zero based */ const Word *p = v + i / elsperword; register Word w = *p--; register Word y = maskp << (bitsperel * (i % elsperword)); if (w == 0) {
i = i - i % elsperword - 1;
y = maskp << (bitsperel * (elsperword-1));
w = *p--;
} /* Quickly step over zeros: */ while (i >= 0 && w == 0) {
i -= elsperword;
w = *p--;
} while (i >= 0) { if ((y & w) != 0) return i+1; /* we use one based convention */
y >>= bitsperel; if (i % elsperword == 0) {
w = *p--;
y = maskp << (bitsperel * (elsperword-1));
}
i--;
} return 0;
}
staticinlineInt CVEC_Lastnzq(Obj fi, const Word *v, Int len, Int wordlen) /* Returns the index of the last non-zero element in the vector or 0,
* if the vector is equal to zero. This is for extension fields. */
{ register PREPARE_maskp(fi);
PREPARE_epw(fi);
PREPARE_bpe(fi);
PREPARE_d(fi); registerconst Word *p; registerInt i; registerInt im; register Word mask;
/* First look for the first non-vanishing word: */
i = wordlen-1;
p = v + i; /* The last word */ while (*p == 0 && i >= 0) { p--; i--; } if (i < 0) return 0; /* Vector is zero */
/* Go back to beginning of the d Words: */
im = i % d;
p -= im;
i = ((i-im) / d) * elsperword + elsperword - 1;
mask = maskp << ((elsperword-1) * bitsperel); while (1) { for (im = d-1;im >= 0;im--) if (p[im] & mask) return i+1;
mask >>= bitsperel;
i--;
} /* Never reached: */ return 0;
}
/****************************************/ /* Arithmetic for finite field scalars: */ /****************************************/
staticinlineInt invert_modp(Int s,Int p)
{ /* This is a standard extended Euclidean algorithm for p and s. * We start with x:=p and y:=s and assume p>s>0. We always keep * a,a',b,b' such that x = a'*p+a*s and y = b'*p+b*s. * As we are only interested in b at the time when y divides x, * we do not bother to calculate a' and b'.
* The function returns the result between 1 and p-1. */
ldiv_t qr; registerInt a = 0; registerInt b = 1; registerInt c; registerInt x = p; registerInt y = s; while (1) {
qr = ldiv(x,y); if (!qr.rem) { if (b < 0) return b+p; elsereturn b;
}
x = y;
y = qr.rem;
c = a-qr.quot*b;
a = b;
b = c;
} return 0; /* Never reached */
}
staticinlineInt mulmodp(Int a, Int b, Int p)
{ return (Int)( (((longlong)a) * b) % p);
}
/* Now some functions to put vector arithmetic up to the GAP level, here * is an overview. Note that scalar multiplication with non- * prime-field values is implemented only here! * For all of the following functions u,v, and w must be vectors over the * same field of equal length, already allocated: * CVEC_ADD2(u,v,fr,to) does u += v * CVEC_ADD3(u,v,w) does u = v+w * CVEC_MUL1(v,s,fr,to) does v *= s * CVEC_MUL2(u,v,s) does u = v*s * CVEC_ADDMUL(u,v,s,fr,to) does u += v*s * They all return nothing. Scalars s can be immediate integers or finite * field elements where appropriate. fr and to are hints, that all elements * outside the range [fr..to] in v are known to be zero, such that operations * can be left undone to save CPU time. The functions round fr and to to * word boundaries and apply the low level functions only within the bounds. * The copying functions do not have this feature, because they have to * copy anyway. Both fr and to can be 0, which indicates 1 and length of
* the vector respectively. */
staticinlineInt handle_hints(Obj cl, Obj fi, Obj fr, Obj to, Int *start, Int *end)
{ Int st,en;
PREPARE_epw(fi);
PREPARE_d(fi); /* A return value of zero indicates failure. */ if (!IS_INTOBJ(fr) || !IS_INTOBJ(to)) { return (Int) OurErrorBreakQuit( "CVEC_handle_hints: fr and to must be immediate integers");
}
st = INT_INTOBJ(fr); if (st == 0) st = 1;
en = INT_INTOBJ(to); if (en == 0) en = INT_INTOBJ(ELM_PLIST(cl,IDX_len)); if (en == -1) en = 1; /* a scalar! */
*start = ((st-1) / elsperword) * d;
*end = ((en+elsperword-1)/elsperword) * d; return 1;
}
static Obj FuncCVEC_ADD2(Obj self, Obj u, Obj v,Obj fr, Obj to)
{ if (!IS_CVEC(u) || !IS_CVEC(v)) { return OurErrorBreakQuit("CVEC_ADD2: no cvec");
}
{ /* The PREPAREs define new variables, so we want an extra block! */
PREPARE_clfi(u,ucl,ufi);
PREPARE_clfi(v,vcl,vfi); Int start = 0; /* Initialization to keep the compiler happy. */ Int end = 0;
if (ufi != vfi || ELM_PLIST(ucl,IDX_len) != ELM_PLIST(vcl,IDX_len)) { return OurErrorBreakQuit( "CVEC_ADD2: incompatible fields or lengths");
}
/* Handle hints: */ if (!handle_hints(ucl,ufi,fr,to,&start,&end)) return 0L;
static Obj FuncCVEC_ADD3(Obj self, Obj u, Obj v,Obj w)
{ if (!IS_CVEC(u) || !IS_CVEC(v) || !IS_CVEC(w)) { return OurErrorBreakQuit("CVEC_ADD3: no cvec");
}
{ /* The PREPAREs define new variables, so we want an extra block! */
PREPARE_clfi(u,ucl,ufi);
PREPARE_clfi(v,vcl,vfi);
PREPARE_clfi(w,wcl,wfi);
staticinlineInt *prepare_scalar(Obj fi, Obj s) /* A NULL pointer indicates failure. */
{ Int sc;
PREPARE_p(fi);
if (IS_FFE(s)) {
PREPARE_tab1(fi);
PREPARE_d(fi);
PREPARE_q(fi); if (CHAR_FF(FLD_FFE(s)) == p && (d % DegreeFFE(s)) == 0) {
sc = INT_INTOBJ(FFE_TO_INTOBJ(tab1, q, s));
} else { return (Int *) OurErrorBreakQuit( "prepare_scalar: scalar from wrong field");
}
} elseif (IS_INTOBJ(s)) {
sc = INT_INTOBJ(s); /* goes on below, case distinction! */
} elseif (IS_PLIST(s)) { /* Coefficients are either FFEs from the
prime field or integers < p */ /* Note that we assume that we only see FFEs in such a list, if * p < 65536 < q, such that tab1 and tab2 are created, but for the
* prime field for exactly this purpose here! */
PREPARE_tab1(fi);
PREPARE_d(fi); Int len = LEN_PLIST(s);
Obj ss;
sclen = 0; /* Note that the length can be 0 <= len <= d, which is <= MAXDEGREE */ if (len > d) { return (Int *) OurErrorBreakQuit( "prepare_scalar: coefficient list longer than d");
} if (len == 0) {
scbuf[0] = 0;
sclen = 1; return scbuf;
} while (sclen < len) {
ss = ELM_PLIST(s,sclen+1); if (IS_INTOBJ(ss)) scbuf[sclen++] = INT_INTOBJ(ss); elseif (IS_FFE(ss) && CHAR_FF(FLD_FFE(ss)) == p &&
DEGR_FF(FLD_FFE(ss)) == 1) { if (VAL_FFE(ss) == 0)
scbuf[sclen++] = 0L; else
scbuf[sclen++] = INT_INTOBJ(ELM_PLIST(tab1,VAL_FFE(ss)+1));
} else { return (Int *) OurErrorBreakQuit( "prepare_scalar: strange object in coefficient list");
}
} /* Find length of scalar: */ for (/* nothing here */;sclen > 1 && scbuf[sclen-1] == 0;sclen--); return scbuf;
} else { return (Int *) OurErrorBreakQuit( "CVEC_MUL*: strange object as scalar");
} /* Now the scalar is in sc as integer between 0..q-1 */ /* We write out the p-adic expansion into our buffer: */
sclen = 0; /* Length should be at least one. */ do {
scbuf[sclen++] = sc % p;
sc /= p;
} while(sc); return scbuf;
}
staticinlinevoid MUL1_INT(Obj u, Obj ucl, Obj ufi, Int d, Int *sc, Int start, Int end) /* This does the multiplication in place in the non-prime field case. * This is an internal function, called by MUL1 and possibly during * other primitive operations that are implemented in the kernel. * Note that this function is not exported to the GAP level and that
* the global variable sclen must contain the length of the scalar. */
{ register Word *vv; Int c = end-start; Int i; Int j; Int ss; register Word wo;
Word *bb;
PREPARE_cp(ufi);
/* Now the ugly case involving conway polynomials: */ for (i = 0,vv = DATA_CVEC(u)+start;i < c;i += d,vv += d) { /* Do one chunk of elsperword elements from F_q: */
ss = 0; /* This counts the nonzero entries in the scalar */
/* Make one copy for further processing: */ for (j = d,bb = buf;j > 0;j--) *bb++ = *vv++;
vv -= d; /* Go back to beginning. */ /* Now handle prime field component: */
MUL2_INL(vv,buf,ufi,*sc,d); while (++ss < sclen) { /* Now we have to multiply the content of the buffer by the * polynomial x modulo the conway polynomial. This means * shifting the Words in the buffer one up and multiplying * the Word falling out by the conway polynomial. We do that
* now. */
wo = buf[d - 1]; /* Keep this one */ for (j = d - 1; j > 0; j--) buf[j] = buf[j-1];
buf[0] = 0UL; for (j = 0,bb = buf;j < d;j++,bb++) {
*bb = ADDMUL1_INL(*bb,wo,ufi,cp[j]);
} /* Now add a multiple of that to the result: */
ADDMUL_INL(vv,buf,ufi,sc[ss],d);
}
}
}
static Obj FuncCVEC_MUL1(Obj self, Obj u, Obj s, Obj fr, Obj to)
{ if (!IS_CVEC(u)) { return OurErrorBreakQuit("CVEC_MUL1: no cvec");
}
{ /* The PREPAREs define new variables, so we want an extra block! */
PREPARE_clfi(u,ucl,ufi);
PREPARE_d(ufi); Int *sc; Int start = 0; /* Just to please modern C compilers */ Int end = 0;
/* Now handle the scalar: */
sc = prepare_scalar(ufi,s); if (!sc) return 0L;
/* Handle hints: */ if (!handle_hints(ucl,ufi,fr,to,&start,&end)) return 0L;
if (sclen == 1) { /* Good luck, scalar is in prime field! */
MUL_INL(DATA_CVEC(u)+start,ufi,*sc,end-start); return 0L;
}
MUL1_INT(u,ucl,ufi,d,sc,start,end);
} return 0L;
}
staticinlinevoid MUL2_INT(Obj u, Obj ucl, Obj ufi, Obj v, Int d, Int wordlen, Int *sc) /* This does the multiplication with result somewhere else in the non-prime * field case. * This is an internal function, called by MUL2 and possibly during * other primitive operations that are implemented in the kernel. * Note that this function is not exported to the GAP level and that
* the global variable sclen must contain the length of the scalar. */
{ register Word *uu; registerconst Word *vv; Int i; Int j; Int ss; register Word wo;
Word *bb;
PREPARE_cp(ufi);
/* Now the ugly case involving conway polynomials: */ for (i = 0,uu = DATA_CVEC(u),vv = CONST_DATA_CVEC(v);i < wordlen;
i += d,uu += d) { /* Do one chunk of elsperword elements from F_q: */
ss = 0; /* This counts the nonzero entries in the scalar */
/* Make one copy for further processing: */ for (j = d,bb = buf;j > 0;j--) *bb++ = *vv++; /* Now handle prime field component: */
MUL2_INL(uu,buf,ufi,*sc,d); while (++ss < sclen) { /* Now we have to multiply the content of the buffer by the * polynomial x modulo the conway polynomial. This means * shifting the Words in the buffer one up and multiplying * the Word falling out by the conway polynomial. We do that
* now. */
wo = buf[d - 1]; /* Keep this one */ for (j = d - 1; j > 0; j--) buf[j] = buf[j-1];
buf[0] = 0UL; for (j = 0,bb = buf;j < d;j++,bb++) {
*bb = ADDMUL1_INL(*bb,wo,ufi,cp[j]);
} /* Now add a multiple of that to the result: */
ADDMUL_INL(uu,buf,ufi,sc[ss],d);
}
}
}
static Obj FuncCVEC_MUL2(Obj self, Obj u, Obj v, Obj s, Obj fr, Obj to)
{ if (!IS_CVEC(u) || !IS_CVEC(v)) { return OurErrorBreakQuit("CVEC_MUL1: no cvec");
}
{ /* The PREPAREs define new variables, so we want an extra block! */
PREPARE_clfi(u,ucl,ufi);
PREPARE_d(ufi);
PREPARE_clfi(v,vcl,vfi); Int wordlen = INT_INTOBJ(ELM_PLIST(ucl,IDX_wordlen));
Int *sc;
/* Check fields and lengths: */ if (ufi != vfi ||
ELM_PLIST(ucl,IDX_len) != ELM_PLIST(vcl,IDX_len)) { return OurErrorBreakQuit( "CVEC_MUL2: incompatible fields or lengths");
}
/* Now handle the scalar: */
sc = prepare_scalar(ufi,s); if (!sc) return 0L;
if (sclen == 1) { /* Good luck, scalar is in prime field! */
MUL2_INL(DATA_CVEC(u),CONST_DATA_CVEC(v),ufi,*sc,wordlen); return 0L;
}
MUL2_INT(u,ucl,ufi,v,d,wordlen,sc);
} return 0L;
}
staticinlinevoid ADDMUL_INT(Obj u, Obj ucl, Obj ufi, Obj v, Int d, Int *sc, Int start, Int end) /* This does the multiplication plus addition to something else in the * non-prime field case. * This is an internal function, called by ADDMUL and possibly during * other primitive operations that are implemented in the kernel. * Note that this function is not exported to the GAP level and that
* the global variable sclen must contain the length of the scalar. */
{ register Word *uu; registerconst Word *vv; Int c = end-start; Int i; Int j; Int ss; register Word wo;
Word *bb;
PREPARE_cp(ufi);
/* Now the ugly case involving conway polynomials: */ for (i = 0,uu = DATA_CVEC(u)+start,vv = CONST_DATA_CVEC(v)+start;
i < c;i += d,uu += d) { /* Do one chunk of elsperword elements from F_q: */
ss = 0; /* This counts the nonzero entries in the scalar */
/* Make one copy for further processing: */ for (j = d,bb = buf;j > 0;j--) *bb++ = *vv++; /* Now handle prime field component: */
ADDMUL_INL(uu,buf,ufi,*sc,d); while (++ss < sclen) { /* Now we have to multiply the content of the buffer by the * polynomial x modulo the conway polynomial. This means * shifting the Words in the buffer one up and multiplying * the Word falling out by the conway polynomial. We do that
* now. */
wo = buf[d - 1]; /* Keep this one */ for (j = d - 1; j > 0; j--) buf[j] = buf[j-1];
buf[0] = 0UL; for (j = 0,bb = buf;j < d;j++,bb++) {
*bb = ADDMUL1_INL(*bb,wo,ufi,cp[j]);
} /* Now add a multiple of that to the result: */
ADDMUL_INL(uu,buf,ufi,sc[ss],d);
}
}
}
static Obj FuncCVEC_ADDMUL(Obj self, Obj u, Obj v, Obj s, Obj fr, Obj to)
{ if (!IS_CVEC(u) || !IS_CVEC(v)) { return OurErrorBreakQuit("CVEC_ADDMUL: no cvec");
}
{ /* The PREPAREs define new variables, so we want an extra block! */
PREPARE_clfi(u,ucl,ufi);
PREPARE_d(ufi);
PREPARE_clfi(v,vcl,vfi); Int *sc; Int start = 0; /* Just to please modern C compilers */ Int end = 0;
/* Check fields and lengths: */ if (ufi != vfi ||
ELM_PLIST(ucl,IDX_len) != ELM_PLIST(vcl,IDX_len)) { return OurErrorBreakQuit( "CVEC_ADDMUL: incompatible fields or lengths");
}
/* Now handle the scalar: */
sc = prepare_scalar(ufi,s); if (!sc) return 0L;
/* Handle hints: */ if (!handle_hints(ucl,ufi,fr,to,&start,&end)) return 0L;
if (sclen == 1) { /* Good luck, scalar is in prime field! */
ADDMUL_INL(DATA_CVEC(u)+start,CONST_DATA_CVEC(v)+start,ufi,*sc,end-start); return 0L;
}
ADDMUL_INT(u,ucl,ufi,v,d,sc,start,end);
} return 0L;
}
static Obj FuncCVEC_ASS_CVEC(Obj self, Obj v, Obj pos, Obj s)
{ Int i; Int *sc; if (!IS_CVEC(v)) { return OurErrorBreakQuit("CVEC_ASS_CVEC: no cvec");
} if (!IS_INTOBJ(pos)) { return OurErrorBreakQuit("CVEC_ASS_CVEC: no integer");
}
i = INT_INTOBJ(pos);
{
PREPARE_clfi(v,cl,fi);
PREPARE_d(fi); Int j;
/* Check bounds: */ if (i < 1 || i > INT_INTOBJ(ELM_PLIST(cl,IDX_len))) { return OurErrorBreakQuit("CVEC_ASS_CVEC: out of bounds");
}
/* Now handle the scalar: */
sc = prepare_scalar(fi,s); if (!sc) return 0L;
/* Fill unused space: */ for (j = sclen;j < d;scbuf[j++] = 0) ;
if (d == 1)
CVEC_AssItemp(fi, DATA_CVEC(v), i, (Word) (*sc)); else
CVEC_AssItemq(fi, DATA_CVEC(v), i, sc);
} return 0L;
}
static Obj FuncCVEC_ELM_CVEC(Obj self, Obj v, Obj pos)
{ Int i; if (!IS_CVEC(v)) { return OurErrorBreakQuit("CVEC_ELM_CVEC: no cvec");
} if (!IS_INTOBJ(pos)) { return OurErrorBreakQuit("CVEC_ELM_CVEC: no integer");
}
i = INT_INTOBJ(pos);
{ /* The following are all pointers to masterpointers and thus
* survive a garbage collection: */
PREPARE_clfi(v,cl,fi);
PREPARE_p(fi);
PREPARE_d(fi);
PREPARE_tab2(fi); Int size = INT_INTOBJ(ELM_PLIST(fi,IDX_size)); Int s;
Obj sca;
/* Check bounds: */ if (i < 1 || i > INT_INTOBJ(ELM_PLIST(cl,IDX_len))) { return OurErrorBreakQuit("CVEC_ELM_CVEC: out of bounds");
}
if (size >= 1 && d > 1) { /* The field has more than 65536 elements */ /* Let's allocate a new scalar first: */ /* GARBAGE COLLECTION POSSIBLE */
sca = NEW_PLIST( T_PLIST, d );
SET_LEN_PLIST( sca, d ); /* Here, a garbage collection might occur, however, all our * variables survive this, as they are pointers to master
* pointers! */
} else sca = 0L; /* just to please the compiler */
if (d == 1) {
s = CVEC_Itemp(fi, CONST_DATA_CVEC(v), i); if (p < 65536) /* we do GAP FFEs */ return ELM_PLIST(tab2,s+1); else return INTOBJ_INT(s);
} else {
CVEC_Itemq(fi, CONST_DATA_CVEC(v), i); if (size == 0) { registerInt i; for (s = 0,i = d-1;i >= 0;i--) s = s * p + scbuf[i]; return ELM_PLIST(tab2,s+1);
} else { if (p < 65536) { for (i = 0;i < d;i++)
SET_ELM_PLIST(sca, i+1, ELM_PLIST(tab2,scbuf[i]+1));
} else { for (i = 0;i < d;i++)
SET_ELM_PLIST(sca, i+1, INTOBJ_INT(scbuf[i]));
} return sca;
}
}
} return 0L;
}
static Obj FuncCVEC_CVEC_LT(Obj self, Obj u, Obj v)
{ if (!IS_CVEC(u) || !IS_CVEC(v)) { return OurErrorBreakQuit("CVEC_CVEC_LT: no cvecs");
}
{ /* The PREPAREs define new variables, so we want an extra block! */ registerInt wordlen; registerconst Word *p1; registerconst Word *p2;
PREPARE_clfi(u,ucl,ufi);
PREPARE_clfi(v,vcl,vfi);
/* Check fields and lengths: */ if (ufi != vfi ||
ELM_PLIST(ucl,IDX_len) != ELM_PLIST(vcl,IDX_len)) { return OurErrorBreakQuit( "CVEC_CVEC_LT: incompatible fields or lengths");
}
wordlen = INT_INTOBJ(ELM_PLIST(ucl,IDX_wordlen));
p1 = CONST_DATA_CVEC(u);
p2 = CONST_DATA_CVEC(v); while (wordlen > 0) { if (*p1 < *p2) returnTrue; elseif (*p1 > *p2) returnFalse;
p1++; p2++; wordlen--;
} returnFalse;
} /* Never reached: */ return 0L;
}
static Obj FuncCVEC_CVEC_EQ(Obj self, Obj u, Obj v)
{ if (!IS_CVEC(u) || !IS_CVEC(v)) { return OurErrorBreakQuit("CVEC_CVEC_EQ: no cvecs");
}
{ /* The PREPAREs define new variables, so we want an extra block! */ registerInt wordlen; registerconst Word *p1; registerconst Word *p2;
PREPARE_clfi(u,ucl,ufi);
PREPARE_clfi(v,vcl,vfi);
/* Check fields and lengths: */ if (ufi != vfi ||
ELM_PLIST(ucl,IDX_len) != ELM_PLIST(vcl,IDX_len)) { return OurErrorBreakQuit( "CVEC_CVEC_EQ: incompatible fields or lengths");
}
wordlen = INT_INTOBJ(ELM_PLIST(ucl,IDX_wordlen));
p1 = CONST_DATA_CVEC(u);
p2 = CONST_DATA_CVEC(v); while (wordlen > 0) { if (*p1 != *p2) returnFalse;
p1++; p2++; wordlen--;
} returnTrue;
} /* Never reached: */ return 0L;
}
static Obj FuncCVEC_CVEC_ISZERO(Obj self, Obj u)
{ if (!IS_CVEC(u)) { return OurErrorBreakQuit("CVEC_CVEC_EQ: no cvec");
}
{ /* The PREPAREs define new variables, so we want an extra block! */ registerInt wordlen; registerconst Word *p;
PREPARE_cl(u,ucl);
wordlen = INT_INTOBJ(ELM_PLIST(ucl,IDX_wordlen));
p = CONST_DATA_CVEC(u); while (wordlen > 0) { if (*p != 0) returnFalse;
p++; wordlen--;
} returnTrue;
} /* Never reached: */ return 0L;
}
static Obj FuncCVEC_EXTRACT(Obj self, Obj v, Obj ii, Obj ll) /* Extracts ll field elements from the vector self at position ii into * one Word in a convenient way, fitting to FILL_GREASE_TAB. * Here is an example with l = 3, d = 2: * (on a potential machine with 25 bits wide Words :-) ) * |-|---|---|---|333|222|111|---|---| lower memory addresses ^ * |-|---|---|---|666|555|444|---|---| higher memory addresses V * ==> * |-|---|---|666|555|444|333|222|111| * Here is an example where i is such that not all field elements are in * the same word: * |-|222|111|---|---|---|---|---|---| lower memory addresses ^ * |-|555|444|---|---|---|---|---|---| * |-|---|---|---|---|---|---|---|333| * |-|---|---|---|---|---|---|---|666| higher memory addresses V * ==> * |-|---|---|666|555|444|333|222|111| * Note that 111 and 444 encode the coefficients of the leftmost extension * field element in the vector, 222 and 555 the next one and 333 and 666 * the third.
*/
{ /* No checks here, because performance is mission critical! */
PREPARE_clfi(v,cl,fi);
PREPARE_bpe(fi);
PREPARE_epw(fi);
PREPARE_d(fi); Int overflow = 0; /* Set to 1 if we read over the end of the vector */ Int i = INT_INTOBJ(ii)-1; /* we are 1-based in GAP, here we want 0-based */ Int l = INT_INTOBJ(ll);
Word res = 0UL; const Word *p = CONST_DATA_CVEC(v) + (i / elsperword) * d; Int rest = i % elsperword; Int wordlen = INT_INTOBJ(ELM_PLIST(cl,IDX_wordlen));
if (((i+l-1)/elsperword)*d >= wordlen) overflow = 1; /* In that case we do not look over the last word, fortunately, this * can only happen in the ugly cases and we only have to skip the
* second step. */
/* First the prime field case: */ if (d == 1) { /* First decide, whether we are in the simple or the ugly case: */ if (rest + l <= elsperword) { /* Good luck, everything is in the same word! */
--> --------------------
--> maximum size reached
--> --------------------
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.43Angebot
Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.