Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
| Download
GAP 4.8.9 installation with standard packages -- copy to your CoCalc project to get it
Project: cocalc-sagemath-dev-slelievre
Views: 418346/****************************************************************************1**2*W dt.c GAP source Wolfgang Merkwitz3**4**5*Y Copyright (C) 1996, Lehrstuhl D für Mathematik, RWTH Aachen, Germany6*Y (C) 1998 School Math and Comp. Sci., University of St Andrews, Scotland7*Y Copyright (C) 2002 The GAP Group8**9** This file implements the part of the deep thought package which deals10** with computing the deep thought polynomials.11**12** Deep Thought deals with trees. A tree <tree> is a concatenation of13** several nodes where each node is a 5-tuple of immediate integers. If14** <tree> is an atom it contains only one node, thus it is itself a15** 5-tuple. If <tree> is not an atom we obtain its list representation by16**17** <tree> := topnode(<tree>) concat left(<tree>) concat right(<tree>) .18**19** Let us denote the i-th node of <tree> by (<tree>, i) and the tree rooted20** at (<tree>, i) by tree(<tree>, i). Let <a> be tree(<tree>, i)21** The first entry of (<tree>, i) is pos(a),22** and the second entry is num(a). The third entry of (<tree>, i) gives a23** mark.(<tree>, i)[3] = 1 means that (<tree>, i) is marked,24** (<tree>, i)[3] = 0 means that (<tree>, i) is not marked. The fourth entry25** of (<tree>, i) contains the number of knodes of tree(<tree>, i). The26** fifth entry of (<tree>, i) finally contains a boundary for27** pos( tree(<tree>, i) ). (<tree>, i)[5] <= 0 means that28** pos( tree(<tree>, i) ) is unbounded. If tree(<tree>, i) is an atom we29** already know that pos( tree(<tree>, i) ) is unbound. Thus we then can30** use the fifth component of (<tree>, i) to store the side. In this case31** (<tree>, i)[5] = -1 means that tree(<tree>, i) is an atom from the32** right hand word, and (<tree>, i)[5] = -2 means that tree(<tree>, i) is33** an atom from the left hand word.34**35** A second important data structure deep thought deals with is a deep36** thought monomial. A deep thought monomial g_<tree> is a product of37** binomial coefficients with a coefficient c. Deep thought monomials38** are represented in this implementation by formula39** vectors, which are lists of integers. The first entry of a formula40** vector is 0, to distinguish formula vectors from trees. The second41** entry is the coefficient c, and the third and fourth entries are42** num( left(tree) ) and num( right(tree) ). The remaining part of the43** formula vector is a concatenation of pairs of integers. A pair (i, j)44** with i > 0 represents binomial(x_i, j). A pair (0, j) represents45** binomial(y_gen, j) when word*gen^power is calculated.46**47** Finally deep thought has to deal with pseudorepresentatives. A48** pseudorepresentative <a> is stored in list of length 4. The first entry49** stores left( <a> ), the second entry contains right( <a> ), the third50** entry contains num( <a> ) and the last entry finally gives a boundary51** for pos( <b> ) for all trees <b> which are represented by <a>.52*/53#include "system.h"54555657#include "gasman.h" /* garbage collector */58#include "objects.h" /* objects */59#include "scanner.h" /* scanner */60#include "bool.h" /* booleans */61#include "calls.h" /* generic call mechanism */62#include "gap.h" /* error handling, initialisation */63#include "gvars.h" /* global variables */64#include "integer.h" /* integers */6566#include "dt.h" /* deep thought */6768#include "records.h" /* generic records */69#include "precord.h" /* plain records */7071#include "lists.h" /* generic lists */72#include "listfunc.h" /* functions for generic lists */73#include "plist.h" /* plain lists */74#include "string.h" /* strings */7576#include "code.h" /* coder */77#include "thread.h" /* threads */78#include "tls.h" /* thread-local storage */798081/****************************************************************************82**8384*F DT_POS(tree, index) . . . . . . . . . . . . . position of (<tree>, index)85**86** 'DT_POS' returns pos(<a>) where <a> is the subtree of <tree> rooted at87** (<tree>, index). <index> has to be a positive integer less or equal than88** the number of nodes of <tree>.89*/90#define DT_POS(tree, index) \91(ELM_PLIST(tree, (index-1)*5 + 1 ) )929394/***************************************************************************95**96*F SET_DT_POS(tree, index, obj) . . . assign the position of(<tree>, index)97**98** 'SET_DT_POS sets pos(<a>) to the object <obj>, where <a> is the subtree99** of <tree>, rooted at (<tree>, index). <index> has to be an positive100** integer less or equal to the number of nodes of <tree>101*/102#define SET_DT_POS(tree, index, obj) \103SET_ELM_PLIST(tree, (index-1)*5 + 1, obj)104105106/***************************************************************************107**108*F DT_GEN(tree, index) . . . . . . . . . . . . . generator of (<tree>, index)109**110** 'DT_GEN' returns num(<a>) where <a> is the subtree of <tree> rooted at111** (<tree>, index). <index> has to be a positive integer less or equal than112** the number of nodes of <tree>.113*/114#define DT_GEN(tree, index) \115(ELM_PLIST(tree, (index-1)*5 + 2) )116117118/**************************************************************************119**120*F SET_DT_GEN(tree, index, obj) . . . assign the generator of(<tree>, index)121**122** 'SET_DT_GEN sets num(<a>) to the object <obj>, where <a> is the subtree123** of <tree>, rooted at (<tree>, index). <index> has to be an positive124** integer less or equal to the number of nodes of <tree>125*/126#define SET_DT_GEN(tree, index, obj) \127(SET_ELM_PLIST(tree, (index-1)*5 + 2, obj) )128129130/**************************************************************************131**132*F DT_IS_MARKED(tree, index) . . . . . . tests if (<tree>, index) is marked133**134** 'DT_IS_MARKED' returns 1 (as C integer) if (<tree>, index) is marked, and135** 0 otherwise. <index> has to be a positive integer less or equal to the136** number of nodes of <tree>.137*/138#define DT_IS_MARKED(tree, index) \139(INT_INTOBJ (ELM_PLIST(tree, (index-1)*5 + 3) ) )140141142/**************************************************************************143**144*F DT_MARK(tree, index) . . . . . . . . . . . . . . . . . . . . mark a node145**146** 'DT_MARK' marks the node (<tree>, index). <index> has to be a positive147** integer less or equal to the number of nodes of <tree>.148*/149#define DT_MARK(tree, index) \150SET_ELM_PLIST(tree, (index-1)*5 + 3, INTOBJ_INT(1) )151152153/**************************************************************************154**155*F DT_UNMARK(tree, index) . . . . . . . . . . . remove the mark from a node156**157** 'DT_UNMARK' removes the mark from the node (<tree>, index). <index> has158** has to be a positive integer less or equal to the number of nodes of159** <tree>.160*/161#define DT_UNMARK(tree, index) \162SET_ELM_PLIST(tree, (index-1)*5 + 3, INTOBJ_INT(0) )163164165/****************************************************************************166**167*F DT_RIGHT(tree, index) . . . .determine the right subnode of (<tree>, index)168*F DT_LEFT(tree, index) . . . . determine the left subnode of (<tree>, index)169**170** 'DT_RIGHT' returns the right subnode of (<tree>, index). That means if171** DT_RIGHT(tree, index) = index2, then (<tree>, index2) is the right172** subnode of (<tree>, index).173**174** 'DT_LEFT' returns the left subnode of (<tree>, index). That means if175** DT_LEFT(tree, index) = index2, then (<tree>, index2) is the left176** subnode of (<tree>, index).177**178** Before calling 'DT_RIGHT' or 'DT_LEFT' it should be ensured, that179** (<tree>, index) is not an atom. <index> has to be a positive integer180** less or equal to the number of nodes of <tree>.181*/182#define DT_RIGHT(tree, index) \183( INT_INTOBJ(ELM_PLIST(tree, index*5 + 4) ) + index + 1)184#define DT_LEFT(tree, index) \185( index + 1 )186187188/****************************************************************************189**190*F DT_SIDE(tree, index) . . . . . . . determine the side of (<tree>, index)191*V RIGHT. . . . . . . . . . . . . . . integer describing "right"192*V LEFT . . . . . . . . . . . . . . . integer describing "left"193**194** 'DT_SIDE' returns 'LEFT' if (<tree>, index) is an atom from the Left-hand195** word, and 'RIGHT' if (<tree>, index) is an atom of the Right-hand word.196** Otherwise 'DT_SIDE' returns an integer bigger than 1. <index> has to be197** a positive integer less or equal to the number of nodes of <tree>.198*/199#define RIGHT -1200#define LEFT -2201#define DT_SIDE(tree, index) \202(INT_INTOBJ( ELM_PLIST(tree, (index-1)*5 + 5 ) ) )203204205/****************************************************************************206**207*F DT_LENGTH(tree, index) . . . . . . . . number of nodes of (<tree>, index)208**209** 'DT_LENGTH' returns the number of nodes of (<tree>, index). <index> has210** to be a positive integer less or equal to the number of nodes of <tree>.211*/212#define DT_LENGTH(tree, index) \213( INT_INTOBJ(ELM_PLIST(tree, (index-1)*5 + 4) ) )214215216/***************************************************************************217**218*F DT_MAX(tree, index) . . . . . . . . . . . . . . . . boundary of a node219**220** 'DT_MAX(tree, index)' returns a boundary for 'DT_POS(tree, index)'.221** 'DT_MAX(tree, index) = 0 ' means that 'DT_POS(tree, index)' is unbound.222** <index> has to be a positive integer less or equal to the number of nodes223** of tree.224*/225#define DT_MAX(tree, index) \226(ELM_PLIST(tree, (index-1)*5 + 5 ) )227228229/****************************************************************************230**231*F CELM(list, pos) . . . . . . . . . . element of a plain list as C integer232**233** 'CELM' returns the <pos>-th element of the plain list <list>. <pos> has234** to be a positive integer less or equal to the physical length of <list>.235** Before calling 'CELM' it should be ensured that the <pos>-th entry of236** <list> is an immediate integer object.237*/238#define CELM(list, pos) ( INT_INTOBJ(ELM_PLIST(list, pos) ) )239240241/****************************************************************************242**243*V Dt_add244**245** Dt_add is used to store the library function dt_add.246*/247248Obj Dt_add;249extern Obj ShallowCopyPlist( Obj list );250251/****************************************************************************252**253*F UnmarkTree( <tree> ) . . . . . . . remove the marks of all nodes of <tree>254**255** 'UnmarkTree' removes all marks of all nodes of the tree <tree>.256*/257void UnmarkTree(258Obj tree )259{260UInt i, len; /* loop variable */261262len = DT_LENGTH(tree, 1);263for (i=1; i <= len; i++ )264DT_UNMARK(tree, i);265}266267268/****************************************************************************269**270*F FuncUnmarkTree(<self>, <tree>) . . remove the marks of all nodes of <tree>271**272** 'FuncUnmarkTree' implements the internal function 'UnmarkTree'.273**274** 'UnmarkTree( <tree> )'275**276** 'UnmarkTree' removes all marks of all nodes of the tree <tree>.277*/278Obj FuncUnmarkTree(279Obj self,280Obj tree )281{282UnmarkTree(tree);283return 0;284}285286287/*****************************************************************************288**289*F Mark(<tree>, <reftree>, <index>) . . . . . . . . find all nodes of <tree>290** which are almost equal291** to (<reftree>, index)292**293** 'Mark' determines all nodes of the tree <tree>, rooting subtrees almost294** equal to the tree rooted at (<reftree>, index). 'Mark' marks these nodes295** and returns the number of different nodes among these nodes. Since it296** is assumed that the set {pos(a) | a almost equal to (<reftree>, index) }297** is equal to {1,...,n} for a positive integer n, 'Mark' actually returns298** the Maximum of {pos(a) | a almost equal to (<reftree>, index)}.299*/300UInt Mark(301Obj tree,302Obj reftree,303Int indexx )304{305UInt i, /* loop variable */306m, /* integer to return */307len;308Obj refgen;309310m = 0;311i = 1;312len = DT_LENGTH(tree, 1);313refgen = DT_GEN(reftree, indexx);314while ( i <= len )315{316/* skip all nodes (<tree>, i) with317** num(<tree>, i) > num(<reftree>, indexx) */318while( i < len &&319DT_GEN(tree, i) > refgen )320i++;321if ( AlmostEqual(tree, i, reftree, indexx) )322{323DT_MARK(tree, i);324if ( m < INT_INTOBJ( DT_POS(tree, i) ) )325m = INT_INTOBJ( DT_POS(tree, i) );326}327/* Since num(a) < num(b) holds for all subtrees <a> of an arbitrary328** tree <b> we can now skip the whole tree rooted at (<tree>, i).329** If (<tree>, i) is the left subnode of another node we can even330** skip the tree rooted at that node, because of331** num( right(a) ) < num( left(a) ) for all trees <a>.332** Note that (<tree>, i) is the left subnode of another node, if and333** only if the previous node (<tree>, i-1) is not an atom. in this334** case (<tree>, i) is the left subnode of (<tree>, i-1). */335if ( DT_LENGTH(tree, i-1) == 1 )336/* skip the tree rooted at (<tree>, i). */337i = i + DT_LENGTH(tree, i);338else339/* skip the tree rooted at (<tree>, i-1) */340i = i - 1 + DT_LENGTH(tree, i-1);341}342return m;343}344345346/****************************************************************************347**348*F AmostEqual(<tree1>,<index1>,<tree2>,<index2>) . . test of almost equality349**350** 'AlmostEqual' tests if tree(<tree1>, index1) is almost equal to351** tree(<tree2>, index2). 'AlmostEqual' returns 1352** if these trees are almost equal, and 0 otherwise. <index1> has to be353** a positive integer less or equal to the number of nodes of <tree1>,354** and <index2> has to be a positive integer less or equal to the number of355** nodes of <tree2>.356*/357Int AlmostEqual(358Obj tree1,359Int index1,360Obj tree2,361Int index2 )362{363UInt k, schranke; /* loop variable */364/* First the two top nodes of tree(<tree1>, index1) and365** tree(<tree2>, index2) (that are (<tree1>, index1) and366** (<tree2, index2) ) are compared by testing the equality of the 2-nd,367** 5-th and 6-th entries the nodes. */368if ( DT_GEN(tree1, index1) != DT_GEN(tree2, index2) )369return 0;370if ( DT_SIDE(tree1, index1) != DT_SIDE(tree2, index2) )371return 0;372if ( DT_LENGTH(tree1, index1) != DT_LENGTH(tree2, index2) )373return 0;374/* For the comparison of the remaining nodes of tree(<tree1>, index1)375** and tree(<tree2>, index2) it is also necessary to compare the first376** entries of the nodes. Note that we know at this point, that377** tree(<tree1>, index1) and tree(<tree2>, index2) have the same number378** of nodes */379schranke = index1 + DT_LENGTH(tree1, index1);380for (k = index1 + 1; k < schranke; k++ )381{382if ( DT_GEN(tree1, k) != DT_GEN(tree2, k + index2 - index1 ) )383return 0;384if ( DT_POS(tree1, k) != DT_POS(tree2, k + index2 - index1 ) )385return 0;386if ( DT_SIDE(tree1, k) !=387DT_SIDE(tree2, k + index2 - index1) )388return 0;389if ( DT_LENGTH(tree1, k) != DT_LENGTH(tree2, k + index2 - index1) )390return 0;391}392return 1;393}394395396/*****************************************************************************397**398*F Equal(<tree1>,<index1>,<tree2>,<index2>) . . . . . . . . test of equality399**400** 'Equal' tests if tree(<tree1>, index1) is equal to401** tree(<tree2>, index2). 'Equal' returns 1402** if these trees are equal, and 0 otherwise. <index1> has to be403** a positive integer less or equal to the number of nodes of <tree1>,404** and <index2> has to be a positive integer less or equal to the number of405** nodes of <tree2>.406*/407Int Equal(408Obj tree1,409Int index1,410Obj tree2,411Int index2 )412{413UInt k, schranke; /* loop variable */414415/* Each node of tree(<tree1>, index1) is compared to the corresponding416** node of tree(<tree2>, index2) by testing the equality of the 1-st,417** 2-nd, 5-th and 6-th nodes. */418schranke = index1 + DT_LENGTH(tree1, index1);419for (k=index1; k < schranke; k++)420{421if ( DT_GEN(tree1, k) != DT_GEN(tree2, k + index2 - index1 ) )422return 0;423if ( DT_POS(tree1, k) != DT_POS(tree2, k + index2 - index1 ) )424return 0;425if ( DT_SIDE(tree1, k) !=426DT_SIDE(tree2, k + index2 - index1) )427return 0;428if ( DT_LENGTH(tree1, k) != DT_LENGTH(tree2, k + index2 - index1) )429return 0;430}431return 1;432}433434435/****************************************************************************436**437*F Mark2(<tree>,<index1>,<reftree>,<index2>) . . find all subtrees of438** tree(<tree>, index1) which439** are almost equal to440** tree(<reftree>, index2)441**442** 'Mark2' determines all subtrees of tree(<tree>, index1) that are almost443** equal to tree(<reftree>, index2). 'Mark2' marks the top nodes of these444** trees and returns a list of lists <list> such that <list>[i]445** for each subtree <a> of <tree> which is almost equal to446** tree(<reftree>, index2) and for which pos(<a>) = i holds contains an447** integer describing the position of the top node of <a> in <tree>.448** For example <list>[i] = [j, k] means that tree(<tree>, j) and449** tree(<tree>, k) are almost equal to tree(<reftree>, index2) and450** that pos(tree(<tree>, j) = pos(tree(<tree>, k) = i holds.451**452** <index1> has to be a positive integer less or equal to the number of nodes453** of <tree>, and <index2> has to be a positive integer less or equal to454** the number of nodes of <reftree>.455*/456Obj Mark2(457Obj tree,458Int index1,459Obj reftree,460Int index2 )461{462UInt i, /* loop variable */463len;464Obj new,465list, /* list to return */466refgen;467468/* initialize <list> */469list = NEW_PLIST(T_PLIST, 0);470SET_LEN_PLIST(list, 0);471i = index1;472len = index1 + DT_LENGTH(tree, index1) - 1;473refgen = DT_GEN(reftree, index2);474while( i <= len )475{476/* skip all nodes (<tree>, i) with477** num(<tree>, i) > num(<reftree>, index) */478while( i < len &&479DT_GEN(tree, i) > refgen )480i++;481if ( AlmostEqual(tree, i, reftree, index2) )482{483DT_MARK(tree, i);484/* if <list> is too small grow it appropriately */485if ( LEN_PLIST(list) < INT_INTOBJ( DT_POS(tree, i) ) )486{487GROW_PLIST(list, INT_INTOBJ( DT_POS(tree, i) ) );488SET_LEN_PLIST(list, INT_INTOBJ( DT_POS(tree, i) ) );489}490/* if <list> has no entry at position pos(tree(<tree>, i))491** create a new list <new>, assign it to list at position492** pos(tree(<tree>, i)), and add i to <new> */493if ( ELM_PLIST(list, INT_INTOBJ( DT_POS(tree, i) ) ) == 0)494{495new = NEW_PLIST( T_PLIST, 1);496SET_LEN_PLIST(new, 1);497SET_ELM_PLIST(new, 1, INTOBJ_INT(i) );498SET_ELM_PLIST(list, INT_INTOBJ( DT_POS(tree, i) ), new);499/* tell gasman that list has changed */500CHANGED_BAG(list);501}502/* add i to <list>[ pos(tree(<tree>, i)) ] */503else504{505new = ELM_PLIST(list, INT_INTOBJ( DT_POS(tree, i) ) );506GROW_PLIST(new, LEN_PLIST(new) + 1);507SET_LEN_PLIST(new, LEN_PLIST(new) + 1);508SET_ELM_PLIST(new, LEN_PLIST(new), INTOBJ_INT(i) );509/* tell gasman that new has changed */510CHANGED_BAG(new);511}512}513/* Since num(a) < num(b) holds for all subtrees <a> of an arbitrary514** tree <b> we can now skip the whole tree rooted at (<tree>, i).515** If (<tree>, i) is the left subnode of another node we can even516** skip the tree rooted at that node, because of517** num( right(a) ) < num( left(a) ) for all trees <a>.518** Note that (<tree>, i) is the left subnode of another node, if and519** only if the previous node (<tree>, i-1) is not an atom. In this520** case (<tree>, i) is the left subnode of (<tree>, i-1). */521if ( DT_LENGTH(tree, i-1) == 1 )522/* skip tree(<tree>, i) */523i = i + DT_LENGTH(tree, i);524else525/* skip tree(<tree>, i-1) */526i = i - 1 + DT_LENGTH(tree, i-1);527}528return list;529}530531532/*****************************************************************************533**534*F FindTree(<tree>, <index>)535**536** 'FindTree' looks for a subtree <a> of tree(<tree>, index) such that537** the top node of538** <a> is not marked but all the other nodes of <a> are marked. It is539** assumed that if the top node of a subtree <b> of tree(<tree>, index)540** is marked, all541** nodes of of <b> are marked. Hence it suffices to look for a subtree <a>542** of <tree> such that the top node of <a> is unmarked and the left and the543** right node of <a> are marked. 'FindTree' returns an integer <i> such544** that tree(<tree> ,i) has the properties mentioned above. If such a tree545** does not exist 'Findtree' returns 0 (as C integer). Note that this holds546** if and only if tree(<tree>, index) is marked.547*/548UInt FindTree(549Obj tree,550Int indexx )551{552UInt i; /* loop variable */553554/* return 0 if (<tree>, indexx) is marked */555if ( DT_IS_MARKED(tree, indexx) )556return 0;557i = indexx;558/* loop over all nodes of tree(<tree>, indexx) to find a tree with the559** properties described above. */560while( i < indexx + DT_LENGTH(tree, indexx) )561{562/* skip all nodes that are unmarked and rooting non-atoms */563while( !( DT_IS_MARKED(tree, i) ) && DT_LENGTH(tree, i) > 1 )564i++;565/* if (<tree>, i) is unmarked we now know that tree(<tree>, i) is566** an atom and we can return i. Note that an unmarked atom has the567** desired properties. */568if ( !( DT_IS_MARKED(tree, i) ) )569return i;570/* go to the previous node */571i--;572/* If the right node of tree(<tree>, i) is marked return i.573** Else go to the right node of tree(<tree>, i). */574if ( DT_IS_MARKED(tree, DT_RIGHT(tree, i) ) )575return i;576i = DT_RIGHT(tree, i);577}578return 0;579}580581582/****************************************************************************583**584*F MakeFormulaVector(<tree>, <pr>) . . . . . . . . . compute the polynomial585** g_<tree> for <tree>586**587** 'MakeFormulaVector' returns the polynomial g_<tree> for a tree <tree>588** and a pc-presentation <pr> of a nilpotent group. This polynomial g_<tree>589** is a product of binomial coefficients with a coefficient c ( see the590** header of this file ).591**592** For the calculation of the coefficient c the top node of <tree> is ignored593** because it can happen that trees are equal except for the top node.594** Hence it suffices to compute the formula vector for one of these trees.595** Then we get the "correct" coefficient for the polynomial for each <tree'>596** of those trees by multiplying the coefficient given by the formula vector597** with c_( num(left(<tree'>)), num(right(<tree'>)); num(<tree'>) ). This598** is also the reason for storing num(left(<tree>)) and num(right(<tree>))599** in the formula vector.600**601** 'MakeFormulaVector' only returns correct results if all nodes of <tree>602** are unmarked.603*/604Obj MakeFormulaVector(605Obj tree,606Obj pr )607{608UInt i, /* denominator of a binomial coefficient */609j, /* loop variable */610u; /* node index */611Obj rel, /* stores relations of <pr> */612vec, /* stores formula vector to return */613prod,/* stores the product of two integers */614gen;615616/* initialize <vec> and set the first four elements */617vec = NEW_PLIST(T_PLIST, 4);618SET_LEN_PLIST(vec, 4);619SET_ELM_PLIST(vec, 1, INTOBJ_INT(0) );620SET_ELM_PLIST(vec, 2, INTOBJ_INT(1) );621SET_ELM_PLIST(vec, 3, DT_GEN(tree, DT_LEFT(tree, 1) ) );622SET_ELM_PLIST(vec, 4, DT_GEN(tree, DT_RIGHT(tree, 1) ) );623/* loop over all almost equal classes of subtrees of <tree> except for624** <tree> itself. */625u = FindTree(tree, 1);626while( u > 1 )627{628/* mark all subtrees of <tree> almost equal to tree(<tree>, u) and629** get the number of different trees in this almost equal class */630i = Mark(tree, tree, u);631/* if tree(<tree>, u) is an atom from the Right-hand word append632** [ 0, i ] to <vec> */633if ( DT_SIDE(tree, u) == RIGHT )634{635GROW_PLIST(vec, LEN_PLIST(vec)+2);636SET_LEN_PLIST(vec, LEN_PLIST(vec)+2);637SET_ELM_PLIST(vec, LEN_PLIST(vec)-1, INTOBJ_INT(0) );638SET_ELM_PLIST(vec, LEN_PLIST(vec), INTOBJ_INT(i) );639}640/* if tree(<tree>, u) is an atom from the Left-hand word append641** [ num(tree(<tree>, u)), i ] to <vec> */642else if ( DT_SIDE(tree, u) == LEFT)643{644GROW_PLIST(vec, LEN_PLIST(vec)+2);645SET_LEN_PLIST(vec, LEN_PLIST(vec)+2);646SET_ELM_PLIST(vec, LEN_PLIST(vec)-1, DT_GEN(tree, u) );647SET_ELM_PLIST(vec, LEN_PLIST(vec), INTOBJ_INT(i) );648}649/* if tree(<tree>, u) is not an atom multiply650** <vec>[2] with binomial(d, i) where651** d = c_(num(left(<tree>,u)), num(right(<tree>,u)); num(<tree>,u)) */652else653{654j = 3;655rel = ELM_PLIST( ELM_PLIST(pr, INT_INTOBJ( DT_GEN(tree,656DT_LEFT(tree, u) ) ) ),657INT_INTOBJ( DT_GEN(tree, DT_RIGHT(tree, u) ) ) );658gen = DT_GEN(tree, u);659while ( 1 )660{661if ( ELM_PLIST(rel, j) == gen )662{663prod = ProdInt(ELM_PLIST(vec, 2),664binomial(ELM_PLIST(rel, j+1),665INTOBJ_INT(i) ) );666SET_ELM_PLIST(vec, 2, prod);667/* tell gasman that vec has changed */668CHANGED_BAG(vec);669break;670}671j+=2;672}673}674u = FindTree(tree, 1);675}676return vec;677}678679680/**************************************************************************681**682*F FuncMakeFormulaVector(<self>,<tree>,<pr>) . . . . . compute the formula683** vector for <tree>684**685** 'FuncMakeFormulaVector' implements the internal function686** 'MakeFormulaVector(<tree>, <pr>)'.687**688** 'MakeFormulaVector(<tree>, <pr>)'689**690** 'MakeFormulaVector' returns the formula vector for the tree <tree> and691** the pc-presentation <pr>.692*/693Obj FuncMakeFormulaVector(694Obj self,695Obj tree,696Obj pr )697{698if (LEN_PLIST(tree) == 5)699ErrorReturnVoid("<tree> has to be a non-atom", 0L, 0L,700"you can 'return;'");701return MakeFormulaVector(tree, pr);702}703704705/*****************************************************************************706**707*F binomial(<n>, <k>) . . . . . . . . . binomial coefficient of <n> and <k>708**709** 'binomial' returns the binomial coefficient of the integers <n> and <k>.710*/711Obj binomial( Obj n,712Obj k )713{714UInt j, kc;715Obj bin, help;716717if ( LtInt( INTOBJ_INT(0), n) && LtInt(n, k) )718return INTOBJ_INT(0);719if ( IS_INTOBJ(n) && n == k )720return INTOBJ_INT(1);721kc = INT_INTOBJ(k);722bin = INTOBJ_INT(1);723help = DiffInt(n, k);724for (j=1; j<=kc; j++)725bin = ProdInt( bin, SumInt(help, INTOBJ_INT(j) ) );726for (j=1; j<=kc; j++)727bin = QuoInt(bin, INTOBJ_INT(j) );728return bin;729}730731732733/****************************************************************************734**735*F Leftof(<tree1>,<index1>,<tree2>,<index2>) . . . . test if one tree is left736** of another tree737**738** 'Leftof' returns 1 if tree(<tree1>, index1) is left of tree(<tree2>,index2)739** in the word being collected at the first instance, that740** tree(<tree1>, index1) and tree(<tree2>, index2) both occur. It is assumed741** that tree(<tree1>, index1) is not equal to tree(<tree2>, index2).742*/743Int Leftof(744Obj tree1,745Int index1,746Obj tree2,747Int index2 )748{749if ( DT_LENGTH(tree1, index1) == 1 && DT_LENGTH(tree2, index2) == 1 ) {750if (DT_SIDE(tree1, index1) == LEFT && DT_SIDE(tree2, index2) == RIGHT)751return 1;752else if (DT_SIDE(tree1, index1) == RIGHT &&753DT_SIDE(tree2, index2) == LEFT )754return 0;755else if (DT_GEN(tree1, index1) == DT_GEN(tree2, index2) )756return ( DT_POS(tree1, index1) < DT_POS(tree2, index2) );757else758return ( DT_GEN(tree1, index1) < DT_GEN(tree2, index2) );759}760if ( DT_LENGTH(tree1, index1) > 1 &&761DT_LENGTH(tree2, index2) > 1 &&762Equal( tree1, DT_RIGHT(tree1, index1) ,763tree2, DT_RIGHT(tree2, index2) ) )764{765if ( Equal( tree1, DT_LEFT(tree1, index1),766tree2, DT_LEFT(tree2, index2) ) ) {767if ( DT_GEN(tree1, index1) == DT_GEN(tree2, index2) )768return ( DT_POS(tree1, index1) < DT_POS(tree2, index2) );769else770return ( DT_GEN(tree1, index1) < DT_GEN(tree2, index2) );771}772}773if( Earlier(tree1, index1, tree2, index2) )774return !Leftof2( tree2, index2, tree1, index1);775else776return Leftof2( tree1, index1, tree2, index2);777}778779780/*****************************************************************************781**782*F Leftof2(<tree1>,<index1>,<tree2>,<index2>) . . . . . test if one tree is783** left of another tree784**785** 'Leftof2' returns 1 if tree(<tree1>, index1) is left of786** tree(<tree2>,index2)in the word being collected at the first instance,787** that tree(<tree1>, index1) and tree(<tree2>, index2) both occur. It is788** assumed that tree(<tree2>, index2) occurs earlier than789** tree(<tree1>,index1). Furthermore it is assumed that if both790** tree(<tree1>, index1) and tree(<tree2>, index2) are non-atoms, then their791** right trees and their left trees are not equal.792*/793Int Leftof2(794Obj tree1,795Int index1,796Obj tree2,797Int index2 )798{799if ( DT_GEN(tree2, index2) < DT_GEN(tree1, DT_RIGHT(tree1, index1) ) )800return 0;801else if (Equal(tree1, DT_RIGHT(tree1, index1), tree2, index2 ) )802return 0;803else if (DT_GEN(tree2, index2) == DT_GEN(tree1, DT_RIGHT(tree1, index1)) )804return Leftof(tree1, DT_RIGHT(tree1, index1), tree2, index2 );805else if (Equal(tree1, DT_LEFT(tree1, index1), tree2, index2) )806return 0;807else808return Leftof(tree1, DT_LEFT(tree1, index1), tree2, index2);809}810811812/****************************************************************************813**814*F Earlier(<tree1>,<index1>,<tree2>,<index2>) . . . test if one tree occurs815** earlier than another816**817** 'Earlier' returns 1 if tree(<tree1>, index1) occurs strictly earlier than818** tree(<tree2>, index2). It is assumed that at least one of these trees819** is a non-atom. Furthermore it is assumed that if both of these trees are820** non-atoms, right(tree(<tree1>, index1) ) does not equal821** right(tree(<tree2>, index2) ) or left(tree(<tree1>, index1) ) does not822** equal left(tree(<tree2>, index2) ).823*/824Int Earlier(825Obj tree1,826Int index1,827Obj tree2,828Int index2 )829{830if ( DT_LENGTH(tree1, index1) == 1 )831return 1;832if ( DT_LENGTH(tree2, index2) == 1 )833return 0;834if ( Equal(tree1, DT_RIGHT(tree1, index1),835tree2, DT_RIGHT(tree2, index2) ) )836return Leftof(tree1, DT_LEFT(tree2, index2),837tree2, DT_LEFT(tree1, index1) );838if ( DT_GEN(tree1, DT_RIGHT(tree1, index1) ) ==839DT_GEN(tree2, DT_RIGHT(tree2, index2) ) )840return Leftof( tree1, DT_RIGHT(tree1, index1) ,841tree2, DT_RIGHT(tree2, index2) );842return (DT_GEN(tree1, DT_RIGHT(tree1, index1) ) <843DT_GEN(tree2, DT_RIGHT(tree2, index2) ) );844}845846847/****************************************************************************848**849** GetPols( <list>, <pr>, <pols> )850**851** GetPols computes all representatives which are represented by the852** pseudorepresentative <list>, converts them all into the corresponding853** deep thought monomial and stores all these monomials in the list <pols>.854*/855856/* See below: */857void GetReps( Obj list, Obj reps );858void FindNewReps2( Obj tree, Obj reps, Obj pr);859860void GetPols(861Obj list,862Obj pr,863Obj pols )864{865Obj lreps,866rreps,867tree,868tree1;869UInt i,j,k,l, lenr, lenl, len;870871lreps = NEW_PLIST(T_PLIST, 2);872rreps = NEW_PLIST(T_PLIST, 2);873SET_LEN_PLIST(lreps, 0);874SET_LEN_PLIST(rreps, 0);875/* get the representatives that are represented by <list>[1] and those876** which are represented by <list>[2]. */877GetReps( ELM_PLIST(list, 1), lreps );878GetReps( ELM_PLIST(list, 2), rreps );879lenr = LEN_PLIST(rreps);880lenl = LEN_PLIST(lreps);881for (i=1; i<=lenl; i++)882for (j=1; j<=lenr; j++)883{884/* now get all representatives, which can be constructed from885** <lreps>[<i>] and <rreps>[<j>] and add the corresponding886** deep thought monomials to <pols> */887k = LEN_PLIST( ELM_PLIST(lreps, i) )888+ LEN_PLIST( ELM_PLIST(rreps, j) ) + 5;/* m"ogliche Inkom-*/889tree = NEW_PLIST(T_PLIST, k); /* patibilit"at nach*/890SET_LEN_PLIST(tree, k); /*"Anderung der Datenstruktur */891SET_ELM_PLIST(tree, 1, INTOBJ_INT(1) );892SET_ELM_PLIST(tree, 2, ELM_PLIST( list, 3) );893SET_ELM_PLIST(tree, 3, INTOBJ_INT(0) );894SET_ELM_PLIST(tree, 4, INTOBJ_INT((int)(k/5)) );895SET_ELM_PLIST(tree, 5, INTOBJ_INT(0) );896tree1 = ELM_PLIST(lreps, i);897len = LEN_PLIST( tree1 );898for (l=1; l<=len; l++)899SET_ELM_PLIST(tree, l+5, ELM_PLIST(tree1, l) );900k = LEN_PLIST(tree1) + 5;901tree1 = ELM_PLIST(rreps, j);902len = LEN_PLIST( tree1 );903for (l=1; l<=len; l++)904SET_ELM_PLIST(tree, l+k, ELM_PLIST(tree1, l) );905UnmarkTree(tree);906FindNewReps2(tree, pols, pr);907}908}909910911912/****************************************************************************913**914*F FuncGetPols( <self>, <list>, <pr>, <pols> )915**916** FuncGetPols implements the internal function GetPols.917*/918919Obj FuncGetPols(920Obj self,921Obj list,922Obj pr,923Obj pols )924{925if (LEN_PLIST(list) != 4)926ErrorReturnVoid("<list> must be a generalised representative not a tree"927,0L, 0L, "you can 'return;'");928GetPols(list, pr, pols);929return (Obj) 0;930}931932933934/****************************************************************************935**936*F GetReps( <list>, <reps> )937**938** GetReps computes all representatives which are represented by the939** pseudorepresentative <list> and adds them to the list <reps>.940*/941942/* See below: */943void FindNewReps1( Obj tree, Obj reps);944945void GetReps(946Obj list,947Obj reps )948{949Obj lreps,950rreps,951tree,952tree1;953UInt i,j,k,l, lenr, lenl, len;;954955if ( LEN_PLIST(list) != 4 )956{957SET_ELM_PLIST(reps, 1, list);958SET_LEN_PLIST(reps, 1);959return;960}961lreps = NEW_PLIST(T_PLIST, 2);962rreps = NEW_PLIST(T_PLIST, 2);963SET_LEN_PLIST(lreps, 0);964SET_LEN_PLIST(rreps, 0);965/* now get all representatives which are represented by <list>[1] and966** all representatives which are represented by <list>[2]. */967GetReps( ELM_PLIST(list, 1), lreps );968GetReps( ELM_PLIST(list, 2), rreps );969lenl = LEN_PLIST( lreps );970lenr = LEN_PLIST( rreps );971for (i=1; i<=lenl; i++)972for (j=1; j<=lenr; j++)973{974/* compute all representatives which can be constructed from975** <lreps>[<i>] and <rreps>[<j>] and add them to <reps>. */976k = LEN_PLIST( ELM_PLIST(lreps, i) )977+ LEN_PLIST( ELM_PLIST(rreps, j) ) + 5;/* m"ogliche Inkom-*/978tree = NEW_PLIST(T_PLIST, k); /* patibilit"at nach*/979SET_LEN_PLIST(tree, k); /*"Anderung der Datenstruktur */980SET_ELM_PLIST(tree, 1, INTOBJ_INT(1) );981SET_ELM_PLIST(tree, 2, ELM_PLIST( list, 3) );982SET_ELM_PLIST(tree, 3, INTOBJ_INT(0) );983SET_ELM_PLIST(tree, 4, INTOBJ_INT((int)(k/5)) );984if ( TNUM_OBJ( ELM_PLIST(list, 4) ) == T_INT &&985CELM(list, 4) < 100 &&986CELM(list, 4) > 0 )987SET_ELM_PLIST(tree, 5, ELM_PLIST(list, 4) );988else989SET_ELM_PLIST(tree, 5, INTOBJ_INT(0) );990tree1 = ELM_PLIST(lreps, i);991len = LEN_PLIST( tree1 );992for (l=1; l<=len; l++)993SET_ELM_PLIST(tree, l+5, ELM_PLIST(tree1, l) );994k = LEN_PLIST(tree1) + 5;995tree1 = ELM_PLIST(rreps, j);996len = LEN_PLIST( tree1 );997for (l=1; l<=len; l++)998SET_ELM_PLIST(tree, l+k, ELM_PLIST(tree1, l) );999UnmarkTree(tree);1000FindNewReps1(tree, reps);1001}1002}100310041005/**************************************************************************1006**1007*F FindNewReps(<tree>,<reps>,<pr>,<max>) . . construct new representatives1008**1009** 'FindNewReps' constructs all trees <tree'> with the following properties.1010** 1) left(<tree'>) is equivalent to left(<tree>).1011** right(<tree'>) is equivalent to right(<tree>).1012** num(<tree'>) = num(<tree>)1013** 2) <tree'> is the least tree in its equivalence class.1014** 3) for each marked node of (<tree>, i) of <tree> tree(<tree>, i) is equal1015** to tree(<tree'>, i).1016** There are three versions of FindNewReps. FindNewReps1 adds all found1017** trees to the list <reps>. This version is called by GetReps.1018** FindNewReps2 computes for each found tree the corresponding deep thought1019** monomial adds these deep thought monomials to <reps>. This version1020** is called from GetPols.1021** The third version FindNewReps finally assumes that <reps> is the list of1022** pseudorepresentatives. This Version adds all found trees to <reps> and1023** additionally all trees, that fulfill 1), 2) and 3) except for1024** num(<tree'>) = num(<tree>). This version is called from the library1025** function calrepsn.1026** It is assumed that both left(<tree>) and right(<tree>) are the least1027** elements in their equivalence class.1028*/10291030/* See below: */1031void FindSubs1( Obj tree, Int x, Obj list1, Obj list2, Obj a, Obj b,1032Int al, Int ar, Int bl, Int br, Obj reps );10331034void FindNewReps1(1035Obj tree,1036Obj reps1037)1038{1039Obj y, /* stores a copy of <tree> */1040lsubs, /* stores pos(<subtree>) for all subtrees of1041** left(<tree>) in a given almost equal class */10421043rsubs, /* stores pos(<subtree>) for all subtrees of1044** right(<tree>) in the same almost equal class */10451046llist, /* stores all elements of an almost equal class1047** of subtrees of left(<tree>) */10481049rlist; /* stores all elements of the same almost equal1050** class of subtrees of right(<tree>) */1051Int a, /* stores a subtree of right((<tree>) */1052n, /* Length of lsubs */1053m, /* Length of rsubs */1054i; /* loop variable */10551056/* get a subtree of right(<tree>) which is unmarked but whose1057** subtrees are all marked */1058a = FindTree(tree, DT_RIGHT(tree, 1) );1059/* If we do not find such a tree we at the bottom of the recursion.1060** If leftof(left(<tree>), right(<tree>) ) holds we add all <tree>1061** to <reps>. */1062if ( a == 0 )1063{1064if ( Leftof(tree, DT_LEFT(tree, 1), tree, DT_RIGHT(tree, 1) ) )1065{1066y = ShallowCopyPlist(tree);1067GROW_PLIST(reps, LEN_PLIST(reps) + 1);1068SET_LEN_PLIST(reps, LEN_PLIST(reps) + 1);1069SET_ELM_PLIST(reps, LEN_PLIST(reps), y);1070/* tell gasman that <reps> has changed */1071CHANGED_BAG(reps);1072}1073return;1074}1075/* get all subtrees of left(<tree>) which are almost equal to1076** tree(<tree>, a) and mark them */1077llist = Mark2(tree, DT_LEFT(tree, 1), tree, a);1078/* get all subtrees of right(<tree>) which are almost equal to1079** tree(<tree>, a) and mark them */1080rlist = Mark2(tree, DT_RIGHT(tree, 1), tree, a);1081n = LEN_PLIST(llist);1082m = LEN_PLIST(rlist);1083/* if no subtrees of left(<tree>) almost equal to1084** tree(<tree>, a) have been found there is no possibility1085** to change the pos-argument in the trees stored in llist and1086** rlist, so call FindNewReps without changing any pos-arguments.1087*/1088if ( n == 0 )1089{1090FindNewReps1(tree, reps);1091/* unmark all top nodes of the trees stored in rlist */1092UnmarkAEClass(tree, rlist);1093return;1094}1095/* store all pos-arguments that occur in the trees of llist.1096** Note that the set of the pos-arguments in llist actually1097** equals {1,...,n}. */1098lsubs = NEW_PLIST( T_PLIST, n );1099SET_LEN_PLIST(lsubs, n);1100for (i=1; i<=n; i++)1101SET_ELM_PLIST(lsubs, i, INTOBJ_INT(i) );1102/* store all pos-arguments that occur in the trees of rlist.1103** Note that the set of the pos-arguments in rlist actually1104** equals {1,...,m}. */1105rsubs = NEW_PLIST( T_PLIST, m );1106SET_LEN_PLIST(rsubs, m);1107for (i=1; i<=m; i++)1108SET_ELM_PLIST(rsubs, i, INTOBJ_INT(i) );1109/* find all possibilities for lsubs and rsubs such that1110** lsubs[1] < lsubs[2] <...<lsubs[n],1111** rsubs[1] < rsubs[2] <...<rsubs[n],1112** and set(lsubs concat rsubs) equals {1,...,k} for a positiv1113** integer k. For each found lsubs and rsubs 'FindSubs' changes1114** pos-arguments of the subtrees in llist and rlist accordingly1115** and then calls 'FindNewReps' with the changed tree <tree>.1116*/1117FindSubs1(tree, a, llist, rlist, lsubs, rsubs, 1, n, 1, m, reps);1118/* Unmark the subtrees of <tree> in llist and rlist and reset1119** pos-arguments to the original state. */1120UnmarkAEClass(tree, rlist);1121UnmarkAEClass(tree, llist);1122}11231124/* See below: */1125void FindSubs2( Obj tree, Int x, Obj list1, Obj list2, Obj a, Obj b,1126Int al, Int ar, Int bl, Int br, Obj reps, Obj pr );11271128void FindNewReps2(1129Obj tree,1130Obj reps,1131Obj pr /* pc-presentation for a1132** nilpotent group <G> */1133)1134{1135Obj lsubs, /* stores pos(<subtree>) for all subtrees of1136** left(<tree>) in a given almost equal class */11371138rsubs, /* stores pos(<subtree>) for all subtrees of1139** right(<tree>) in the same almost equal class */11401141llist, /* stores all elements of an almost equal class1142** of subtrees of left(<tree>) */11431144rlist; /* stores all elements of the same almost equal1145** class of subtrees of right(<tree>) */1146Int a, /* stores a subtree of right((<tree>) */1147n, /* Length of lsubs */1148m, /* Length of rsubs */1149i; /* loop variable */11501151/* get a subtree of right(<tree>) which is unmarked but whose1152** subtrees are all marked */1153a = FindTree(tree, DT_RIGHT(tree, 1) );1154/* If we do not find such a tree we at the bottom of the recursion.1155** If leftof(left(<tree>), right(<tree>) ) holds we convert <tree>1156** into the corresponding deep thought monomial and add that to1157** <reps>. */1158if ( a == 0 )1159{1160if ( Leftof(tree, DT_LEFT(tree, 1), tree, DT_RIGHT(tree, 1) ) )1161{1162/* get the formula vector of tree and add it to1163** reps[ rel[1] ]. */1164UnmarkTree(tree);1165tree = MakeFormulaVector( tree, pr);1166CALL_3ARGS(Dt_add, tree, reps, pr);1167}1168return;1169}1170/* get all subtrees of left(<tree>) which are almost equal to1171** tree(<tree>, a) and mark them */1172llist = Mark2(tree, DT_LEFT(tree, 1), tree, a);1173/* get all subtrees of right(<tree>) which are almost equal to1174** tree(<tree>, a) and mark them */1175rlist = Mark2(tree, DT_RIGHT(tree, 1), tree, a);1176n = LEN_PLIST(llist);1177m = LEN_PLIST(rlist);1178/* if no subtrees of left(<tree>) almost equal to1179** tree(<tree>, a) have been found there is no possibility1180** to change the pos-argument in the trees stored in llist and1181** rlist, so call FindNewReps without changing any pos-arguments.1182*/1183if ( n == 0 )1184{1185FindNewReps2(tree, reps, pr);1186/* unmark all top nodes of the trees stored in rlist */1187UnmarkAEClass(tree, rlist);1188return;1189}1190/* store all pos-arguments that occur in the trees of llist.1191** Note that the set of the pos-arguments in llist actually1192** equals {1,...,n}. */1193lsubs = NEW_PLIST( T_PLIST, n );1194SET_LEN_PLIST(lsubs, n);1195for (i=1; i<=n; i++)1196SET_ELM_PLIST(lsubs, i, INTOBJ_INT(i) );1197/* store all pos-arguments that occur in the trees of rlist.1198** Note that the set of the pos-arguments in rlist actually1199** equals {1,...,m}. */1200rsubs = NEW_PLIST( T_PLIST, m );1201SET_LEN_PLIST(rsubs, m);1202for (i=1; i<=m; i++)1203SET_ELM_PLIST(rsubs, i, INTOBJ_INT(i) );1204/* find all possibilities for lsubs and rsubs such that1205** lsubs[1] < lsubs[2] <...<lsubs[n],1206** rsubs[1] < rsubs[2] <...<rsubs[n],1207** and set(lsubs concat rsubs) equals {1,...,k} for a positiv1208** integer k. For each found lsubs and rsubs 'FindSubs' changes1209** pos-arguments of the subtrees in llist and rlist accordingly1210** and then calls 'FindNewReps' with the changed tree <tree>.1211*/1212FindSubs2(tree, a, llist, rlist, lsubs, rsubs, 1, n, 1, m, reps, pr);1213/* Unmark the subtrees of <tree> in llist and rlist and reset1214** pos-arguments to the original state. */1215UnmarkAEClass(tree, rlist);1216UnmarkAEClass(tree, llist);1217}121812191220void FindNewReps(1221Obj tree,1222Obj reps,1223Obj pr, /* pc-presentation for a1224** nilpotent group <G> */12251226Obj max /* every generator <g_i> of <G> with1227** i > max lies in the center of <G> */1228)1229{1230Obj y, /* stores a copy of <tree> */1231lsubs, /* stores pos(<subtree>) for all subtrees of1232** left(<tree>) in a given almost equal class */12331234rsubs, /* stores pos(<subtree>) for all subtrees of1235** right(<tree>) in the same almost equal class */12361237llist, /* stores all elements of an almost equal class1238** of subtrees of left(<tree>) */12391240rlist, /* stores all elements of the same almost equal1241** class of subtrees of right(<tree>) */1242list1, /* stores a sublist of <reps> */1243rel; /* stores a commutator relation from <pr> */1244Int a; /* stores a subtree of right((<tree>) */1245UInt n, /* Length of lsubs */1246m, /* Length of rsubs */1247i, lenrel; /* loop variable */12481249/* get a subtree of right(<tree>) which is unmarked but whose1250** subtrees are all marked */1251a = FindTree(tree, DT_RIGHT(tree, 1) );1252/* If we do not find such a tree we at the bottom of the recursion.1253** If leftof(left(<tree>), right(<tree>) ) holds we add all trees1254** <tree'> with left(<tree'>) = left(<tree>),1255** right(<tree'>) = right(<tree>) to <reps>, and <tree'> is the1256** least element in its equivalence calss. Note that for such a1257** tree we have pos(<tree'>) = 1 and num(<tree'>) = j where j is a1258** positive integer for which1259** c_( num(left(<tree>), num(right(<tree>)), j ) does not equal1260** 0. These integers are contained in the list1261** pr[ num(left(<tree>)) ][ num(right(<tree>)) ]. */1262if ( a == 0 )1263{1264if ( Leftof(tree, DT_LEFT(tree, 1), tree, DT_RIGHT(tree, 1) ) )1265{1266/* get pr[ num(left(<tree>)) ][ num(right(<tree>)) ] */1267rel = ELM_PLIST( ELM_PLIST(pr, INT_INTOBJ( DT_GEN(tree,1268DT_LEFT(tree, 1)))) ,1269INT_INTOBJ( DT_GEN(tree, DT_RIGHT(tree, 1) ) ) );1270if ( ELM_PLIST(rel, 3) > max )1271{1272UnmarkTree(tree);1273tree = MakeFormulaVector(tree, pr);1274list1 = ELM_PLIST(reps, CELM(rel, 3) );1275GROW_PLIST(list1, LEN_PLIST(list1) + 1 );1276SET_LEN_PLIST(list1, LEN_PLIST(list1) + 1 );1277SET_ELM_PLIST(list1, LEN_PLIST(list1), tree);1278CHANGED_BAG(list1);1279}1280else1281{1282y = ShallowCopyPlist(tree);1283lenrel = LEN_PLIST(rel);1284for ( i=3;1285i < lenrel &&1286ELM_PLIST(rel, i) <= max;1287i+=2 )1288{1289list1 = ELM_PLIST(reps, CELM(rel, i) );1290GROW_PLIST(list1, LEN_PLIST(list1) + 1);1291SET_LEN_PLIST(list1, LEN_PLIST(list1) + 1);1292SET_ELM_PLIST(list1, LEN_PLIST(list1), y);1293/* tell gasman that <list1> has changed */1294CHANGED_BAG(list1);1295}1296}1297}1298return;1299}1300/* get all subtrees of left(<tree>) which are almost equal to1301** tree(<tree>, a) and mark them */1302llist = Mark2(tree, DT_LEFT(tree, 1), tree, a);1303/* get all subtrees of right(<tree>) which are almost equal to1304** tree(<tree>, a) and mark them */1305rlist = Mark2(tree, DT_RIGHT(tree, 1), tree, a);1306n = LEN_PLIST(llist);1307m = LEN_PLIST(rlist);1308/* if no subtrees of left(<tree>) almost equal to1309** tree(<tree>, a) have been found there is no possibility1310** to change the pos-argument in the trees stored in llist and1311** rlist, so call FindNewReps without changing any pos-arguments.1312*/1313if ( n == 0 )1314{1315FindNewReps(tree, reps, pr, max);1316/* unmark all top nodes of the trees stored in rlist */1317UnmarkAEClass(tree, rlist);1318return;1319}1320/* store all pos-arguments that occur in the trees of llist.1321** Note that the set of the pos-arguments in llist actually1322** equals {1,...,n}. */1323lsubs = NEW_PLIST( T_PLIST, n );1324SET_LEN_PLIST(lsubs, n);1325for (i=1; i<=n; i++)1326SET_ELM_PLIST(lsubs, i, INTOBJ_INT(i) );1327/* store all pos-arguments that occur in the trees of rlist.1328** Note that the set of the pos-arguments in rlist actually1329** equals {1,...,m}. */1330rsubs = NEW_PLIST( T_PLIST, m );1331SET_LEN_PLIST(rsubs, m);1332for (i=1; i<=m; i++)1333SET_ELM_PLIST(rsubs, i, INTOBJ_INT(i) );1334/* find all possibilities for lsubs and rsubs such that1335** lsubs[1] < lsubs[2] <...<lsubs[n],1336** rsubs[1] < rsubs[2] <...<rsubs[n],1337** and set(lsubs concat rsubs) equals {1,...,k} for a positiv1338** integer k. For each found lsubs and rsubs 'FindSubs' changes1339** pos-arguments of the subtrees in llist and rlist accordingly1340** and then calls 'FindNewReps' with the changed tree <tree>.1341*/1342FindSubs(tree, a, llist, rlist, lsubs, rsubs, 1, n, 1, m, reps, pr, max);1343/* Unmark the subtrees of <tree> in llist and rlist and reset1344** pos-arguments to the original state. */1345UnmarkAEClass(tree, rlist);1346UnmarkAEClass(tree, llist);1347}134813491350/***************************************************************************1351**1352*F FuncFindNewReps(<self>,<args>) . . . . . . construct new representatives1353**1354** 'FuncFindNewReps' implements the internal function 'FindNewReps'.1355*/13561357Obj FuncFindNewReps(1358Obj self,1359Obj tree,1360Obj reps,1361Obj pr,1362Obj max )1363{13641365/* test if <tree> is really a tree */1366/* TestTree(tree); */1367if ( LEN_PLIST(tree) < 15 )1368ErrorReturnVoid("<tree> must be a tree not a plain list", 0L, 0L,1369"you can 'return;'");1370FindNewReps(tree, reps, pr, max);1371return 0;1372}137313741375/***************************************************************************1376**1377*F TestTree(<obj>) . . . . . . . . . . . . . . . . . . . . . . test a tree1378**1379** 'TestTree' tests if <tree> is a tree. If <tree> is not a tree 'TestTree'1380** signals an error.1381*/1382void TestTree(1383Obj tree)1384{13851386if ( TNUM_OBJ(tree) != T_PLIST || LEN_PLIST(tree) % 7 != 0)1387ErrorReturnVoid("<tree> must be a plain list, whose length is a multiple of 7", 0L, 0L, "you can 'return;'");1388if ( DT_LENGTH(tree, 1) != LEN_PLIST(tree)/7 )1389ErrorReturnVoid("<tree> must be a tree, not a plain list.", 0L, 0L,1390"you can 'return;'");1391if ( DT_SIDE(tree, 1) >= DT_LENGTH(tree, 1) )1392ErrorReturnVoid("<tree> must be a tree, not a plain list.", 0L, 0L,1393"you can 'return;'");1394if ( DT_LENGTH(tree, 1) == 1)1395{1396if ( DT_SIDE(tree, 1) != LEFT && DT_SIDE(tree, 1) != RIGHT )1397ErrorReturnVoid("<tree> must be a tree, not a plain list.", 0L, 0L,1398"you can 'return;'");1399return;1400}1401if ( DT_SIDE(tree, 1) <= 1 )1402ErrorReturnVoid("<tree> must be a tree, not a plain list.", 0L, 0L,1403"you can 'return;'");1404if (DT_LENGTH(tree, 1) !=1405DT_LENGTH(tree, DT_LEFT(tree, 1)) +1406DT_LENGTH(tree, DT_RIGHT(tree, 1)) +14071 )1408ErrorReturnVoid("<tree> must be a tree, not a plain list.", 0L, 0L,1409"you can 'return;'");1410if ( DT_SIDE(tree, 1) != DT_LENGTH(tree, DT_LEFT(tree, 1) ) + 1 )1411ErrorReturnVoid("<tree> must be a tree, not a plain list.", 0L, 0L,1412"you can 'return;'");1413TestTree( Part(tree, (DT_LEFT(tree, 1) - 1)*7,1414(DT_RIGHT(tree, 1) - 1)*7 ) );1415TestTree( Part(tree, (DT_RIGHT(tree, 1) - 1)*7, LEN_PLIST(tree) ) );1416}141714181419/****************************************************************************1420**1421*F Part(<list>, <pos1>, <pos2> . . . . . . . . . . . . return a part of list1422**1423** 'Part' returns <list>{ [<pos1>+1 .. <pos2>] }.1424*/1425Obj Part(1426Obj list,1427Int pos1,1428Int pos2 )1429{1430Int i, length;1431Obj part;14321433length = pos2 - pos1;1434part = NEW_PLIST(T_PLIST, length);1435SET_LEN_PLIST(part, length);1436for (i=1; i <= length; i++)1437{1438SET_ELM_PLIST(part, i, ELM_PLIST(list, pos1+i) );1439}1440return part;1441}144214431444/***************************************************************************1445**1446*F FindSubs(<tree>,<x>,<list1>,<list2>,<a>,<b>,<al>,<ar>,<bl>,<br>,<reps>,1447** <pr>,<max> ) . . . . . . . . . find possible pos-arguments for1448** the trees in <list1> and <list2>1449**1450** 'FindSubs' finds all possibilities for a and b such that1451** 1) a[1] < a[2] <..< a[ ar ]1452** b[1] < b[2] <..< b[ br ]1453** 2) set( a concat b ) = {1,..,k} for a positiv integer k.1454** 3) a[1],...,a[ al-1 ] and b[1],..,b[ bl-1 ] remain unchanged.1455** For each found possibility 'FindSubs' sets the pos-arguments in the1456** trees of <list1> and <list2> according to the entries of <a> and1457** <b>. Then it calls 'FindNewReps' with the changed tree <tree> as1458v** argument.1459**1460** It is assumed that the conditions 1) and 2) hold for a{ [1..al-1] } and1461** b{ [1..bl-1] }.1462**1463** There are three versions of FindSubs according to the three versions of1464** FindNewReps. FindSubs1 is called from FindNewReps1 and calls1465** FindNewReps1. FindSubs2 is called from FindNewReps2 and calls1466** FindNewReps2. FindSubs is called from FindNewReps and calls FindNewReps.1467*/14681469void FindSubs1(1470Obj tree,1471Int x, /* subtree of <tree> */1472Obj list1, /* list containing all subtrees of1473** left(<tree>) almost equal to1474** tree(<tree>, x) */14751476Obj list2, /* list containing all subtrees of1477** right(<tree>) almost equal to1478** tree(<tree>, x) */14791480Obj a, /* list to change, containing the1481** pos-arguments of the trees in list1 */14821483Obj b, /* list to change, containing tthe1484** pos-arguments of the trees in list2 */1485Int al,1486Int ar,1487Int bl,1488Int br,1489Obj reps /* list of representatives for all trees */1490)1491{1492Int i; /* loop variable */14931494/* if <al> > <ar> or <bl> > <br> nothing remains to change. */1495if ( al > ar || bl > br )1496{1497/* Set the pos-arguments of the trees in <list1> and <list2>1498** according to the entries of <a> and <b>. */1499SetSubs( list1, a, tree);1500SetSubs( list2, b, tree);1501FindNewReps1(tree, reps);1502return;1503}1504/* If a[ ar] is bigger or equal to the boundary of pos(tree(<tree>, x)1505** the execution of the statements in the body of this if-statement1506** would have the consequence that some subtrees of <tree> in <list1>1507** would get a pos-argument bigger than the boundary of1508** pos(tree<tree>, x). But since the trees in <list1> are almost1509** equal to tree(<tree>, x) they have all the same boundary for their1510** pos-argument as tree(<tree>, x). So these statements are only1511** executed when <a>[ar] is less than the boundary of1512** pos(tree(<tree>, x).1513*/1514if ( INT_INTOBJ( DT_MAX(tree, x) ) <= 0 ||1515ELM_PLIST(a, ar) < DT_MAX(tree, x) )1516{1517for (i=al; i<=ar; i++)1518SET_ELM_PLIST(a, i, INTOBJ_INT( CELM(a,i) + 1 ) );1519FindSubs1(tree, x, list1, list2, a, b, al, ar, bl+1, br, reps);1520for (i=al; i<=ar; i++)1521SET_ELM_PLIST(a, i, INTOBJ_INT( CELM(a, i) - 1 ) );1522}1523FindSubs1(tree, x, list1, list2, a, b, al+1, ar, bl+1, br, reps);1524/* If b[ br] is bigger or equal to the boundary of pos(tree(<tree>, x)1525** the execution of the statements in the body of this if-statement1526** would have the consequence that some subtrees of <tree> in <list2>1527** would get a pos-argument bigger than the boundary of1528** pos(tree<tree>, x). But since the trees in <list2> are almost1529** equal to tree(<tree>, x) they have all the same boundary for their1530** pos-argument as tree(<tree>, x). So these statements are only1531** executed when <b>[br] is less than the boundary of1532** pos(tree(<tree>, x).1533*/1534if ( INT_INTOBJ( DT_MAX(tree, x) ) <= 0 ||1535ELM_PLIST(b, br) < DT_MAX(tree, x) )1536{1537for (i=bl; i<=br; i++)1538SET_ELM_PLIST(b, i, INTOBJ_INT( CELM(b, i) + 1 ) );1539FindSubs1(tree, x, list1, list2, a, b, al+1, ar, bl, br, reps);1540for (i=bl; i<=br; i++)1541SET_ELM_PLIST(b, i, INTOBJ_INT( CELM(b, i) - 1 ) );1542}1543}154415451546void FindSubs2(1547Obj tree,1548Int x, /* subtree of <tree> */1549Obj list1, /* list containing all subtrees of1550** left(<tree>) almost equal to1551** tree(<tree>, x) */15521553Obj list2, /* list containing all subtrees of1554** right(<tree>) almost equal to1555** tree(<tree>, x) */15561557Obj a, /* list to change, containing the1558** pos-arguments of the trees in list1 */15591560Obj b, /* list to change, containing the1561** pos-arguments of the trees in list2 */1562Int al,1563Int ar,1564Int bl,1565Int br,1566Obj reps, /* list of representatives for all trees */1567Obj pr /* pc-presentation */1568)1569{1570Int i; /* loop variable */15711572/* if <al> > <ar> or <bl> > <br> nothing remains to change. */1573if ( al > ar || bl > br )1574{1575/* Set the pos-arguments of the trees in <list1> and <list2>1576** according to the entries of <a> and <b>. */1577SetSubs( list1, a, tree);1578SetSubs( list2, b, tree);1579FindNewReps2(tree, reps, pr);1580return;1581}1582/* If a[ ar] is bigger or equal to the boundary of pos(tree(<tree>, x)1583** the execution of the statements in the body of this if-statement1584** would have the consequence that some subtrees of <tree> in <list1>1585** would get a pos-argument bigger than the boundary of1586** pos(tree<tree>, x). But since the trees in <list1> are almost1587** equal to tree(<tree>, x) they have all the same boundary for their1588** pos-argument as tree(<tree>, x). So these statements are only1589** executed when <a>[ar] is less than the boundary of1590** pos(tree(<tree>, x).1591*/1592if ( INT_INTOBJ( DT_MAX(tree, x) ) <= 0 ||1593ELM_PLIST(a, ar) < DT_MAX(tree, x) )1594{1595for (i=al; i<=ar; i++)1596SET_ELM_PLIST(a, i, INTOBJ_INT( CELM(a,i) + 1 ) );1597FindSubs2(tree, x, list1, list2, a, b, al, ar, bl+1, br, reps, pr);1598for (i=al; i<=ar; i++)1599SET_ELM_PLIST(a, i, INTOBJ_INT( CELM(a, i) - 1 ) );1600}1601FindSubs2(tree, x, list1, list2, a, b, al+1, ar, bl+1, br, reps, pr);1602/* If b[ br] is bigger or equal to the boundary of pos(tree(<tree>, x)1603** the execution of the statements in the body of this if-statement1604** would have the consequence that some subtrees of <tree> in <list2>1605** would get a pos-argument bigger than the boundary of1606** pos(tree<tree>, x). But since the trees in <list2> are almost1607** equal to tree(<tree>, x) they have all the same boundary for their1608** pos-argument as tree(<tree>, x). So these statements are only1609** executed when <b>[br] is less than the boundary of1610** pos(tree(<tree>, x).1611*/1612if ( INT_INTOBJ( DT_MAX(tree, x) ) <= 0 ||1613ELM_PLIST(b, br) < DT_MAX(tree, x) )1614{1615for (i=bl; i<=br; i++)1616SET_ELM_PLIST(b, i, INTOBJ_INT( CELM(b, i) + 1 ) );1617FindSubs2(tree, x, list1, list2, a, b, al+1, ar, bl, br, reps, pr);1618for (i=bl; i<=br; i++)1619SET_ELM_PLIST(b, i, INTOBJ_INT( CELM(b, i) - 1 ) );1620}1621}162216231624void FindSubs(1625Obj tree,1626Int x, /* subtree of <tree> */1627Obj list1, /* list containing all subtrees of1628** left(<tree>) almost equal to1629** tree(<tree>, x) */16301631Obj list2, /* list containing all subtrees of1632** right(<tree>) almost equal to1633** tree(<tree>, x) */16341635Obj a, /* list to change, containing the1636** pos-arguments of the trees in list1 */16371638Obj b, /* list to change, containing the1639** pos-arguments of the trees in list2 */1640Int al,1641Int ar,1642Int bl,1643Int br,1644Obj reps, /* list of representatives for all trees */1645Obj pr, /* pc-presentation */1646Obj max /* needed to call 'FindNewReps' */1647)1648{1649Int i; /* loop variable */16501651/* if <al> > <ar> or <bl> > <br> nothing remains to change. */1652if ( al > ar || bl > br )1653{1654/* Set the pos-arguments of the trees in <list1> and <list2>1655** according to the entries of <a> and <b>. */1656SetSubs( list1, a, tree);1657SetSubs( list2, b, tree);1658FindNewReps(tree, reps, pr, max);1659return;1660}1661/* If a[ ar] is bigger or equal to the boundary of pos(tree(<tree>, x)1662** the execution of the statements in the body of this if-statement1663** would have the consequence that some subtrees of <tree> in <list1>1664** would get a pos-argument bigger than the boundary of1665** pos(tree<tree>, x). But since the trees in <list1> are almost1666** equal to tree(<tree>, x) they have all the same boundary for their1667** pos-argument as tree(<tree>, x). So these statements are only1668** executed when <a>[ar] is less than the boundary of1669** pos(tree(<tree>, x).1670*/1671if ( INT_INTOBJ( DT_MAX(tree, x) ) <= 0 ||1672ELM_PLIST(a, ar) < DT_MAX(tree, x) )1673{1674for (i=al; i<=ar; i++)1675SET_ELM_PLIST(a, i, INTOBJ_INT( CELM(a,i) + 1 ) );1676FindSubs(tree, x, list1, list2, a, b, al, ar, bl+1, br, reps, pr, max);1677for (i=al; i<=ar; i++)1678SET_ELM_PLIST(a, i, INTOBJ_INT( CELM(a, i) - 1 ) );1679}1680FindSubs(tree, x, list1, list2, a, b, al+1, ar, bl+1, br, reps, pr, max);1681/* If b[ br] is bigger or equal to the boundary of pos(tree(<tree>, x)1682** the execution of the statements in the body of this if-statement1683** would have the consequence that some subtrees of <tree> in <list2>1684** would get a pos-argument bigger than the boundary of1685** pos(tree<tree>, x). But since the trees in <list2> are almost1686** equal to tree(<tree>, x) they have all the same boundary for their1687** pos-argument as tree(<tree>, x). So these statements are only1688** executed when <b>[br] is less than the boundary of1689** pos(tree(<tree>, x).1690*/1691if ( INT_INTOBJ( DT_MAX(tree, x) ) <= 0 ||1692ELM_PLIST(b, br) < DT_MAX(tree, x) )1693{1694for (i=bl; i<=br; i++)1695SET_ELM_PLIST(b, i, INTOBJ_INT( CELM(b, i) + 1 ) );1696FindSubs(tree, x, list1, list2, a, b, al+1, ar, bl, br, reps, pr, max);1697for (i=bl; i<=br; i++)1698SET_ELM_PLIST(b, i, INTOBJ_INT( CELM(b, i) - 1 ) );1699}1700}170117021703/****************************************************************************1704**1705*F SetSubs(<list>, <a>, <tree>) . . . . . . . . . . .. . set pos-arguments1706**1707** 'SetSubs' sets the pos-arguments of the subtrees of <tree>, contained1708** in <list> according to the entries in the list <a>.1709*/1710void SetSubs(1711Obj list,1712Obj a,1713Obj tree )1714{1715UInt i,j; /* loop variables */1716UInt len, len2;17171718len = LEN_PLIST(list);1719for (i=1; i <= len; i++)1720{1721len2 = LEN_PLIST( ELM_PLIST(list, i) );1722for (j=1; j <= len2; j++)1723SET_DT_POS(tree, CELM( ELM_PLIST(list, i), j), ELM_PLIST(a, i) );1724}1725}172617271728/****************************************************************************1729**1730*F UnmarkAEClass(<tree>, <list>) . . . . . . . . . . . . reset pos-arguments1731**1732** 'UnmarkAEClass' resets the pos arguments of the subtrees of <tree>,1733** contained in <list> to the original state. Furthermore it unmarks the1734** top node of each of those trees.1735*/17361737void UnmarkAEClass(1738Obj tree,1739Obj list )1740{1741UInt i,j, len, len2;17421743len = LEN_PLIST(list);1744for (i=1; i <= len; i++)1745{1746len2 = LEN_PLIST( ELM_PLIST(list, i) );1747for (j=1; j <= len2; j++)1748{1749DT_UNMARK(tree, CELM( ELM_PLIST(list, i), j) );1750SET_DT_POS(tree, CELM( ELM_PLIST(list, i), j), INTOBJ_INT(i) );1751}1752}1753}175417551756/****************************************************************************1757**1758*F FuncDT_evaluation( <self>, <vector> )1759**1760** FuncDT_evaluation implements the internal function1761**1762** DT_evaluation( <vector> ).1763**1764** DT_evaluation returns a positive integer which is used to sort the deep1765** monomials. DT_evaluation is called from the library function dt_add.1766*/17671768Obj FuncDT_evaluation(Obj self,1769Obj vector)1770{1771UInt res,i;17721773res = CELM(vector, 6)*CELM(vector, 6);1774for (i=7; i < LEN_PLIST(vector); i+=2)1775res += CELM(vector, i)*CELM(vector, i+1)*CELM(vector, i+1);1776return INTOBJ_INT(res);1777}1778177917801781/****************************************************************************1782**17831784*F * * * * * * * * * * * * * initialize package * * * * * * * * * * * * * * *1785*/178617871788/****************************************************************************1789**17901791*V GVarFuncs . . . . . . . . . . . . . . . . . . list of functions to export1792*/1793static StructGVarFunc GVarFuncs [] = {17941795{ "MakeFormulaVector", 2, "tree, presentation",1796FuncMakeFormulaVector, "src/dt.c:MakeFormulaVector" },17971798{ "FindNewReps", 4, "tree, representatives, presentation, maximum",1799FuncFindNewReps, "src/dt.c:FindNewReps" },18001801{ "UnmarkTree", 1, "tree",1802FuncUnmarkTree, "src/dt.c:UnmarkTree" },18031804{ "GetPols", 3, "list, presentation, polynomial",1805FuncGetPols, "src/dt.c:GetPols" },18061807{ "DT_evaluation", 1, "vector",1808FuncDT_evaluation, "src/dt.c:DT_evaluation" },18091810{ 0 }18111812};181318141815/****************************************************************************1816**18171818*F InitKernel( <module> ) . . . . . . . . initialise kernel data structures1819*/1820static Int InitKernel (1821StructInitInfo * module )1822{1823InitFopyGVar( "Dt_add" , &Dt_add );18241825/* init filters and functions */1826InitHdlrFuncsFromTable( GVarFuncs );18271828/* return success */1829return 0;1830}183118321833/****************************************************************************1834**1835*F InitLibrary( <module> ) . . . . . . . initialise library data structures1836*/1837static Int InitLibrary (1838StructInitInfo * module )1839{1840/* init filters and functions */1841InitGVarFuncsFromTable( GVarFuncs );18421843/* return success */1844return 0;1845}184618471848/****************************************************************************1849**1850*F InitInfoDeepThought() . . . . . . . . . . . . . . table of init functions1851*/1852static StructInitInfo module = {1853MODULE_BUILTIN, /* type */1854"dt", /* name */18550, /* revision entry of c file */18560, /* revision entry of h file */18570, /* version */18580, /* crc */1859InitKernel, /* initKernel */1860InitLibrary, /* initLibrary */18610, /* checkInit */18620, /* preSave */18630, /* postSave */18640 /* postRestore */1865};18661867StructInitInfo * InitInfoDeepThought ( void )1868{1869return &module;1870}187118721873/****************************************************************************1874**18751876*E dt.c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ends here1877**1878*/187918801881