// // DeepThought: This package provides functions for multiplication and // other computations in finitely generated nilpotent groups based on the // Deep Thought algorithm. //
static Obj FuncDTP_Binomial(Obj self, Obj N, Obj K)
{ // handle some special cases if (K == INTOBJ_INT(1)) // K=1 is the most frequent case for us, so check it first return N;
// restrict input if (!IS_INT(N) || !IS_INT(K)) return Fail;
if (K == INTOBJ_INT(0)) return INTOBJ_INT(1); if (IS_NEGATIVE(K)) return INTOBJ_INT(0);
// from now on, k >= 2
if (K == N) return INTOBJ_INT(1);
if (IS_NEGATIVE(N)) return Fail; // TODO: implement this (does it happen?)
if (LtInt(N, K)) return INTOBJ_INT(0);
// We only support immediate integers for k. Anything else at this point // would lead to output that's far too big for storage anyway. if (!IS_INTOBJ(K)) return Fail;
Int k = INT_INTOBJ(K); Int i;
Obj res = N; Int quot = 1;
// "unroll" the first few operations, to avoid repeated divisions, while // hopefully keeping res small enough to be represented as an immediate // (if N wasn't too big) for (i = 2; i <= k && i <= 6; ++i) {
N = DiffInt(N, INTOBJ_INT(1));
res = ProdInt(res, N);
quot *= i;
}
res = QuoInt(res, INTOBJ_INT(quot));
// now the general case for (; i <= k; ++i) {
N = DiffInt(N, INTOBJ_INT(1));
res = ProdInt(res, N);
res = QuoInt(res, INTOBJ_INT(i));
}
return res;
}
static Obj FuncDTP_SequenceLetter(Obj self, Obj letter, Obj seq)
{ if (!IS_PREC(letter))
ErrorMayQuit("DTP_SequenceLetter: must be a plain record (not a %s)",
(Int)TNAM_OBJ(letter), 0L);
if (!IS_PLIST(seq))
ErrorMayQuit("DTP_SequenceLetter: must be a plain list (not a %s)",
(Int)TNAM_OBJ(seq), 0L);
if (IsbPRec(letter, RNleft))
FuncDTP_SequenceLetter(self, ElmPRec(letter, RNleft), seq);
if (IsbPRec(letter, RNright))
FuncDTP_SequenceLetter(self, ElmPRec(letter, RNright), seq);
UInt len = LEN_PLIST(seq);
AssPlist(seq, len+1, letter);
return 0;
}
static Obj FuncDTP_Seq_i(Obj self, Obj letter, Obj i)
{ if (!IS_PREC(letter))
ErrorMayQuit("DTP_Seq_I: must be a plain record (not a %s)",
(Int)TNAM_OBJ(letter), 0L);
if (!IS_INT(i))
ErrorMayQuit("DTP_Seq_i: must be an integer (not a %s)",
(Int)TNAM_OBJ(letter), 0L);
// We only support immediate integers for i. Anything else at this point // would lead to output that's far too big for storage anyway. if (!IS_INTOBJ(i)) return Fail;
while( ElmPRec(letter, RNlength) > i ){
Obj left = ElmPRec(letter, RNleft); if( ElmPRec(left, RNlength) >= i )
{
letter = left;
} else {
i = DiffInt( i, ElmPRec(left, RNlength) );
letter = ElmPRec(letter, RNright);
}
}
static Obj FuncDTP_AreAlmostEqual(Obj self, Obj letter1, Obj letter2)
{ if (!IS_PREC(letter1))
ErrorMayQuit("DTP_AreAlmostEqual: must be a plain record (not a %s)",
(Int)TNAM_OBJ(letter1), 0L);
if (!IS_PREC(letter2))
ErrorMayQuit("DTP_AreAlmostEqual: must be a plain record (not a %s)",
(Int)TNAM_OBJ(letter2), 0L);
// We must have letter1.num = letter2.num // and both must have the same length if( !EQ( ElmPRec(letter1, RNnum), ElmPRec(letter2, RNnum) ) ||
!EQ( ElmPRec(letter1, RNlength), ElmPRec(letter2, RNlength) ) )
{ returnFalse;
}
// check whether both are atoms / non-atoms:
UInt is_atom = IsbPRec(letter1, RNside); // 1 <=> letter1 is atom if( is_atom == IsbPRec(letter2, RNside) ){ if( is_atom == 1) // both are atoms
{ if( EQ( ElmPRec(letter1, RNside), ElmPRec(letter2, RNside) ) ){ returnTrue;
} else { returnFalse;
}
} else { // both are non-atoms if( EQ( ElmPRec(letter1, RNleft), ElmPRec(letter2, RNleft) ) &&
EQ( ElmPRec(letter1, RNright), ElmPRec(letter2, RNright) ) ){ returnTrue;
} else { returnFalse;
}
}
} else { // one is an atom, the other one a non-atom returnFalse;
}
}
// Table of functions to export static StructGVarFunc GVarFuncs [] = {
GVAR_FUNC(DTP_Binomial, 2, "n, k"),
GVAR_FUNC(DTP_SequenceLetter, 2, "letter, seq"),
GVAR_FUNC(DTP_Seq_i, 2, "letter, i"),
GVAR_FUNC(DTP_AreAlmostEqual, 2, "letter1, letter2"),
{ 0 } /* Finish with an empty entry */