source: git/Singular/LIB/fpaprops.lib @ 8307d2d

spielwiese
Last change on this file since 8307d2d was 8307d2d, checked in by Karim Abou Zeid <karim23697@…>, 5 years ago
remove lpGkDim from LIB
  • Property mode set to 100644
File size: 38.1 KB
Line 
1////////////////////////////////////////////////////////////////
2version="version fpaprops.lib 4.1.2.0 Feb_2019 "; // $Id$
3category="Noncommutative";
4info="
5LIBRARY: fpaprops.lib   Algorithmic ring-theoretic properties of finitely presented algebras (Letterplace)
6AUTHORS: Karim Abou Zeid,       karim.abou.zeid at rwth-aachen.de
7
8Support: Project II.6 in the transregional collaborative research centre
9SFB-TRR 195 'Symbolic Tools in Mathematics and their Application' of the German DFG
10
11OVERVIEW:
12In this library, algorithms for computing various ring-theoretic properties of
13finitely presented algebras are implemented.
14Applicability: Letterplace rings.
15
16REFERENCES:
17Huishi Li: Groebner bases in ring theory. World Scientific, 2010.
18
19KEYWORDS: finitely presented algebra; ring theory; Letterplace Groebner basis;
20growth of algebra; Gelfand-Kirillov dimension; global homological dimension; semi-prime ideal; Ufnarovski graph
21
22SEE ALSO:  fpadim_lib, freegb_lib
23
24PROCEDURES:
25  lpNoetherian(<GB>);     check whether A/<LM(GB)> is (left/right) Noetherian
26  lpIsSemiPrime(<GB>);    check whether A/<LM(GB)> is semi-prime
27  lpIsPrime(<GB>);        check whether A/<LM(GB)> is prime
28  lpGlDimBound(<GB>);     compute an upper bound for the global dimension of A/<GB>
29  lpSubstitute();         substitute a variable with polynomials (ring homomorphism)
30  lpUfGraph(<GB>);        constructs the Ufnarovskij graph for <LM(GB)>
31  lpCalcSubstDegBound(); utility for lpSubstitute
32";
33
34LIB "fpadim.lib";
35
36/* very fast and cheap test of consistency and functionality
37  DO NOT make it static !
38  after adding the new proc, add it here */
39proc tstfpaprops()
40{
41    example lpNoetherian;
42    example lpIsSemiPrime;
43    example lpIsPrime;
44    example lpGlDimBound;
45    example lpSubstitute;
46    example lpUfGraph;
47    example lpCalcSubstDegBound;
48};
49
50
51////////////////////////////////////////////////////////////////////
52proc lpNoetherian(ideal G)
53"USAGE: lpNoetherian(G); G an ideal in a Letterplace ring
54RETURN: int
55      0 not Noetherian
56      1 left Noetherian
57      2 right Noetherian
58      3 Noetherian
59      4 weak Noetherian
60PURPOSE: Check whether the monomial algebra A/<LM(G)> is (left/right) noetherian
61ASSUME: - basering is a Letterplace ring
62      - G is a Groebner basis
63THEORY: lpNoetherian works with the monomial algebra A/<LM(G)>.
64If it gives an affirmative answer for one of the properties, then it
65holds for both A/<LM(G)> and A/<G>. However, a negative answer applies
66only to A/<LM(G)> and not necessarily to A/<G>.
67NOTE: Weak Noetherian means that two-sided ideals in A/<LM(G)> satisfy
68the acc (ascending chain condition).
69"
70{
71  G = lead(G);
72  G = simplify(G, 2+4+8);
73
74  // check special case 1
75  int l = 0;
76  for (int i = 1; i <= size(G); i++) {
77    // find the max degree in G
78    int d = deg(G[i]);
79    if (d > l) {
80      l = d;
81    }
82
83    // also if G is the whole ring
84    if (leadmonom(G[i]) == 1) {
85      ERROR("noetherianity not defined for 0-ring")
86    }
87    kill d;
88  } kill i;
89  // if longest word has length 1 we handle it as a special case
90  if (l == 1) {
91    int n = lpVarBlockSize(basering); // variable count
92    int k = size(G);
93    if (k == n) { // only the field left
94      return(3); // every field is noetherian
95    }
96    if (k == n-1) { // V = {1} with loop
97      return(3);
98    }
99    if (k <= n-2) { // V = {1} with more than one loop
100      return(0);
101    }
102  }
103
104  intmat UG = lpUfGraph(G);
105
106  // check special case 2
107  intmat zero[nrows(UG)][ncols(UG)];
108  if (UG == zero) {
109    return (3);
110  }
111
112  if (!imHasLoops(UG) && imIsUpRightTriangle(topologicalSort(UG))) {
113    // UG is a DAG
114    return (3);
115  }
116
117  // DFS from every vertex, if cycle is found, check every vertex for incomming/outcom
118  intvec visited;
119  visited[ncols(UG)] = 0;
120  int inFlag, outFlag, inOutFlag;
121  for (int v = 1; v <= ncols(UG) && (inFlag + outFlag) != 3; v++) {
122    int inOutFlags = inOutCommingEdgesInCycles(UG, v, visited, 0);
123    if (inOutFlags == 1) {
124      inFlag = 1;
125    }
126    if (inOutFlags == 2) {
127      outFlag = 1;
128    }
129    if (inOutFlags == 3) {
130      inFlag = 1;
131      outFlag = 1;
132    }
133    if (inOutFlags == 4) {
134      inOutFlag = 1;
135    }
136    if (inOutFlags == 5) {
137      inFlag = 1;
138      inOutFlag = 1;
139    }
140    if (inOutFlags == 6) {
141      outFlag = 1;
142      inOutFlag = 1;
143    }
144    if (inOutFlags == 7) {
145      inFlag = 1;
146      outFlag = 1;
147      inOutFlag = 1;
148    }
149    kill inOutFlags;
150  } kill v;
151  int noetherian = 3 - 1*inFlag - 2*outFlag;
152  if (noetherian == 0) {
153    return (4 - 4*inOutFlag); // weak noetherian
154  }
155  return (noetherian);
156}
157example
158{
159  "EXAMPLE:"; echo = 2;
160  ring r = 0,(x,y),dp;
161  def R = freeAlgebra(r, 5);
162  setring R;
163  ideal G = x*x, y*x; // K<x,y>/<xx,yx> is right noetherian
164  lpNoetherian(G);
165}
166
167static proc inOutCommingEdgesInCycles(intmat G, int v, intvec visited, intvec path) {
168  // Mark the current vertex as visited
169  visited[v] = 1;
170
171  // Store the current vertex in path
172  if (path[1] == 0) {
173    path[1] = v;
174  } else {
175    path[size(path) + 1] = v;
176  }
177
178  int inFlag, outFlag, inOutFlag;
179
180  for (int w = 1; w <= ncols(G) && (inFlag + outFlag) != 3; w++) {
181    if (G[v,w] == 1) {
182      if (visited[w] == 1) { // new cycle
183        int tmpInFlag;
184        int tmpOutFlag;
185        if (v == w) { // cycle is a loop
186          for (int u = 1; u <= ncols(G); u++) {
187            if (G[v,u] && u != v) {
188              outFlag = 1;
189              tmpOutFlag = 1;
190            }
191            if (G[u,v] && u != v) {
192              inFlag = 1;
193              tmpInFlag = 1;
194            }
195          } kill u;
196        } else {
197          for (int i = size(path); i >= 1; i--) { // for each vertex in the path
198            // check for neighbors not directly next or prev in cycle
199            for (int u = 1; u <= ncols(G); u++) {
200              if (G[path[i],u] == 1) { // there is an edge to u
201                if (path[i] != v) {
202                  if (u != path[i+1]) { // and u is not the next element in the cycle
203                    outFlag = 1;
204                    tmpOutFlag = 1;
205                  }
206                } else {
207                  if (u != w) {
208                    outFlag = 1;
209                    tmpOutFlag = 1;
210                  }
211                }
212              }
213              if (G[u,path[i]] == 1) { // there is an edge from u
214                if (path[i] != w) {
215                  if (u != path[i-1]) { // and u is not the previous element in the cylce
216                    inFlag = 1;
217                    tmpInFlag = 1;
218                  }
219                } else {
220                  if (u != v) {
221                    inFlag = 1;
222                    tmpInFlag = 1;
223                  }
224                }
225              }
226            } kill u;
227            if (path[i] == w) {
228              break;
229            }
230          } kill i;
231        }
232        if (tmpInFlag > 0 && tmpOutFlag > 0) {
233          // there are both in and outcomming edges in this cycle
234          inOutFlag = 1;
235        }
236        kill tmpInFlag;
237        kill tmpOutFlag;
238      } else {
239        int inOutFlags = inOutCommingEdgesInCycles(G, w, visited, path);
240        if (inOutFlags == 1) {
241          inFlag = 1;
242        }
243        if (inOutFlags == 2) {
244          outFlag = 1;
245        }
246        if (inOutFlags == 3) {
247          inFlag = 1;
248          outFlag = 1;
249        }
250        if (inOutFlags == 4) {
251          inOutFlag = 1;
252        }
253        if (inOutFlags == 5) {
254          inFlag = 1;
255          inOutFlag = 1;
256        }
257        if (inOutFlags == 6) {
258          outFlag = 1;
259          inOutFlag = 1;
260        }
261        if (inOutFlags == 7) {
262          inFlag = 1;
263          outFlag = 1;
264          inOutFlag = 1;
265        }
266        kill inOutFlags;
267      }
268    }
269  } kill w;
270
271  return (1*inFlag + 2*outFlag + 4*inOutFlag);
272}
273
274proc lpIsSemiPrime(ideal G)
275"USAGE: lpIsSemiPrime(G); G an ideal in a Letterplace ring
276RETURN: boolean
277PURPOSE: Check whether A/<LM(G)> is semi-prime ring,
278alternatively whether <LM(G)> is a semi-prime ideal in A.
279ASSUME: - basering is a Letterplace ring
280      - G is a Groebner basis
281THEORY: A (two-sided) ideal I in the ring A is semi-prime, if for any a in A one has
282aAa subseteq I implies a in I.
283NOTE: lpIsSemiPrime works with the monomial algebra A/<LM(G)>.
284A positive answer holds for both A/<LM(G)> and A/<G>, while
285a negative answer applies only to A/<LM(G)> and not necessarily to
286A/<G>.
287"
288{
289    // old theory part: that is when p * (A/<LM(G)>) * p != 0 for all p in (A/<LM(G)> - {0}).
290  G = lead(G);
291  G = simplify(G, 2+4+8);
292
293  // check special case 1
294  int l = 0;
295  for (int i = 1; i <= size(G); i++) {
296    // find the max degree in G
297    int d = deg(G[i]);
298    if (d > l) {
299      l = d;
300    }
301
302    // also if G is the whole ring
303    if (leadmonom(G[i]) == 1) {
304      ERROR("primeness not defined for 0-ring")
305    }
306    kill d;
307  } kill i;
308  // if longest word has length 1 we handle it as a special case
309  if (l == 1) {
310    return(1);
311  }
312
313  list VUG = lpUfGraph(G, 1);
314  intmat UG = VUG[1]; // the Ufnarovskij graph
315  ideal V = VUG[2]; // the vertices of UG (standard words with length = l-1)
316
317  list LG = lpId2ivLi(G);
318  list SW = ivStandardWordsUpToLength(LG, maxDeg(G));
319  list LV = lpId2ivLi(V);
320
321  // delete the 0 in SW
322  int indexofzero = ivIndexOf(SW, 0);
323  if (indexofzero > 0) { // should be always true when |SW| > 0
324    SW = delete(SW, indexofzero);
325  }
326
327  // check if each monomial in SW is cyclic
328  for (int i = 1; i <= size(SW); i++) {
329    if (!isCyclicInUfGraph(UG, LV, SW[i])) {
330      return (0);
331    }
332  } kill i;
333
334  return (1);
335}
336example
337{
338  "EXAMPLE:"; echo = 2;
339  ring r = 0,(x1,x2),dp;
340  def R = freeAlgebra(r, 5);
341  setring R;
342  ideal G = x1*x2, x2*x1; // K<x1,x2>/<x1*x2,x2*x1> is semi prime
343  lpIsSemiPrime(G);
344}
345
346// checks whether a monomial is a cyclic monomial
347static proc isCyclicInUfGraph(intmat UG, list LV, intvec u)
348{
349  if (ncols(UG) == 0) {return (0);} // UG is empty
350  if (u == 0) {return (0);} // 0 is never cyclic
351
352  int l = size(LV[1]) + 1;
353
354  int s = size(u);
355  if (s <= l - 1) {
356    for (int i = 1; i <= size(LV); i++) {
357      // for all vertices where u is a suffix
358      if(isSF(u, LV[i])) {
359        if (existsRoute(UG, i, i)) {
360          return (1);
361        }
362      }
363    } kill i;
364  } else { // size(u) > l - 1
365    int m = s - l + 1;
366
367    // there must be a route from v0 to vm
368    intvec v0 = u[1..(l-1)]; // first in route of u
369    intvec vm = u[m+1..m+(l-1)]; // last in route of u
370
371    int iv0 = ivIndexOf(LV, v0);
372    int ivm = ivIndexOf(LV, vm);
373    if (iv0 <= 0 || ivm <= 0) {
374      ERROR("u is not a standard word");
375    }
376
377    return (existsRoute(UG, ivm, iv0));
378  }
379
380  return (0);
381}
382
383proc lpIsPrime(ideal G)
384"USAGE: lpIsPrime(G); G an ideal in a Letterplace ring
385RETURN: boolean
386PURPOSE: Check whether A/<LM(G)> is prime ring,
387alternatively whether <LM(G)> is a prime ideal in A.
388ASSUME: - basering is a Letterplace ring
389      - G is a Groebner basis
390THEORY: A (two-sided) ideal I in the ring A is prime, if for any a,b in A one has
391aAb subseteq I implies a in I or b in I.
392NOTE: lpIsPrime works with the monomial algebra A/<LM(G)>.
393A positive answer holds for both A/<LM(G)> and A/<G>, while
394a negative answer applies only to A/<LM(G)> and not necessarily to A/<G>.
395"
396{
397    // old theory part: that is when p1 * (A/<LM(G)>) * p2 != 0 for all p1, p2 in (A/<LM(G)> - {0}).
398  G = lead(G);
399  G = simplify(G, 2+4+8);
400
401  // check special case 1
402  int l = 0;
403  for (int i = 1; i <= size(G); i++) {
404    // find the max degree in G
405    int d = deg(G[i]);
406    if (d > l) {
407      l = d;
408    }
409
410    // also if G is the whole ring
411    if (leadmonom(G[i]) == 1) {
412      ERROR("primeness not defined for 0-ring")
413    }
414    kill d;
415  } kill i;
416  // if longest word has length 1 we handle it as a special case
417  if (l == 1) {
418    return(1);
419  }
420
421  list VUG = lpUfGraph(G, 1);
422  intmat UG = VUG[1]; // the Ufnarovskij graph
423  ideal V = VUG[2]; // the vertices of UG (standard words with length = l-1)
424
425  list LG = lpId2ivLi(G);
426  list LV = lpId2ivLi(V);
427
428  int n = ncols(UG);
429
430  // 1) for each vi vj there exists a route from vi to vj (means UG is connected)
431  for (int i = 1; i <= n; i++) {
432    for (int j = 1; j <= n; j++) {
433      if (!existsRoute(UG, i, j)) {
434        return (0);
435      }
436    } kill j;
437  } kill i;
438
439  // 2) any standard word with length < l-1 is a suffix of a vertex
440  list SW = ivStandardWordsUpToLength(LG, maxDeg(G) - 2); // < maxDeg - 1
441  if (size(SW) > 0 && size(LV) == 0) {return (0);}
442  for (int i = 1; i <= size(SW); i++) {
443    // check if SW[i] is a suffix of some LV
444    for (int j = 1; j <= size(LV); j++) {
445      if (!isSF(SW[i], LV[j])) {
446        if (j == size(LV)) {
447          return (0);
448        }
449      } else {
450        break;
451      }
452    }
453  } kill i;
454
455  return (1);
456}
457example
458{
459  "EXAMPLE:"; echo = 2;
460  ring r = 0,(x,y),dp;
461  def R = freeAlgebra(r, 5);
462  setring R;
463  ideal G = x*x, y*y; // K<x,y>/<xx,yy> is prime
464  lpIsPrime(G);
465}
466
467static proc existsRoute(intmat G, int v, int u, list #)
468"USAGE: existsRoute(G,v,u); G a graph, v and u vertices
469NOTE: don't pass anything to # (internal use for recursion)
470routes always have at least one edge
471"
472{
473  int n = ncols(G);
474
475  // init visited
476  intvec visited;
477  if (size(#) > 0) {
478    if (v == u) {return (1);} // don't check on first call so |route| >= 1 holds
479    visited = #[1];
480  } else { // first call
481    visited[n] = 0;
482  }
483
484  // mark current vertex as visited
485  visited[v] = 1;
486
487  // recursive DFS
488  for (int i = 1; i <= n; i++) {
489    if (G[v,i] && (!visited[i] || i == u)) { // i == u to allow routes from u to u
490      if (existsRoute(G, i, u, visited)) {
491        return (1);
492      }
493    }
494  } kill i;
495
496  return (0);
497}
498
499static proc UfGraphURTNZDGrowth(intmat UG) {
500  // URTNZD = upper right triangle non zero diagonal
501  for (int i = 1; i <= ncols(UG); i++) {
502    UG[i,i] = 0; // remove all loops
503  } kill i;
504  intmat UGk = UG;
505  intmat zero[nrows(UGk)][ncols(UGk)];
506  int k = 1;
507  while (UGk != zero) {
508    UGk = UGk * UG;
509    k++;
510  }
511  return (k);
512}
513
514static proc imIsUpRightTriangle(intmat M) {
515  for (int i = 1; i <= nrows(M); i++) {
516    for (int j = 1; j < i; j++) {
517      if(M[i,j] != 0) { return (0); }
518    } kill j;
519  } kill i;
520  return (1);
521}
522
523static proc eliminateZerosUpTriangle(intmat G) {
524  // G is expected to be an upper triangle matrix
525  for (int i = ncols(G); i >= 1; i--) { // loop order is important because we delete entries
526    if (G[i,i] == 0) { // i doesn't have a cycle
527      for (int j = 1; j < i; j++) {
528        if (G[j,i] == 1) { // j has an edge to i
529          for (int k = i + 1; k <= nrows(G); k++) {
530            if (G[i,k] == 1) {
531              G[j,k] = G[i,k]; // give j all edges from i
532            }
533          } kill k;
534        }
535      } kill j;
536      G = imDelRowCol(G,i,i); // remove vertex i
537    }
538  } kill i;
539  return (G);
540}
541
542static proc imDelRowCol(intmat M, int row, int col) {
543  // row and col are expected to be > 0
544  int nr = nrows(M);
545  int nc = ncols(M);
546  intmat Mdel[nr - 1][nc - 1];
547  for (int i = 1; i <= nr; i++) {
548    for (int j = 1; j <= nc; j++) {
549      if(i != row && j != col) {
550        int newi = i;
551        int newj = j;
552        if (i > row) { newi = i - 1; }
553        if (j > col) { newj = j - 1; }
554        Mdel[newi,newj] = M[i,j];
555        kill newi; kill newj;
556      }
557    } kill j;
558  } kill i;
559  return (Mdel);
560}
561
562static proc topologicalSort(intmat G) {
563  // NOTE: ignores loops
564  // NOTE: this takes O(|V^3|), can be optimized
565  int n = ncols(G);
566  for (int i = 1; i <= n; i++) { // only use the submat at i
567    // find a vertex v in the submat at i with no incoming edges
568    int v;
569    for (int j = i; j <= n; j++) {
570      int incoming = 0;
571      for (int k = i; k <= n; k++) {
572        if (k != j && G[k,j] == 1) {
573          incoming = 1;
574        }
575      } kill k;
576      if (incoming == 0) {
577        v = j;
578        kill incoming;
579        break;
580      } else {
581        if (j == n) {
582          // G contains at least one cycle, abort
583          return (G);
584        }
585      }
586      kill incoming;
587    } kill j;
588
589    // swap v and i
590    if (v != i) {
591      G = imPermcol(G, v, i);
592      G = imPermrow(G, v, i);
593    }
594    kill v;
595  } kill i;
596  return (G);
597}
598
599static proc imPermcol (intmat A, int c1, int c2)
600{
601  intmat B = A;
602  int k = nrows(B);
603  B[1..k,c1] = A[1..k,c2];
604  B[1..k,c2] = A[1..k,c1];
605  return (B);
606}
607
608static proc imPermrow (intmat A, int r1, int r2)
609{
610  intmat B = A;
611  int k = ncols(B);
612  B[r1,1..k] = A[r2,1..k];
613  B[r2,1..k] = A[r1,1..k];
614  return (B);
615}
616
617static proc UfGraphGrowth(intmat UG)
618{
619  int n = ncols(UG); // number of vertices
620  // iterate through all vertices
621
622  intvec visited = 0:n;
623  intvec cyclic = 0:n;
624  intvec countedCycles = -2:n;
625
626  int maxCycleCount = 0;
627  for (int v = 1; v <= n; v++) {
628    countedCycles = countCycles(UG, v, visited, cyclic, 0, countedCycles);
629    dbprint("counted " + string(countedCycles[v]) + " cycles from vertex " + string(v) + "/" + string(n) + " (cache: " + string(countedCycles) + ")");
630    if (countedCycles[v] == -1) {
631      return(-1);
632    }
633    if (countedCycles[v] > maxCycleCount) {
634      maxCycleCount = countedCycles[v];
635    }
636  } kill v;
637  return (maxCycleCount);
638}
639
640static proc countCycles(intmat G, int v, intvec visited, intvec cyclic, intvec path, intvec countedCycles)
641"USAGE: countCycles(G, v, visited, cyclic, path); G a Graph, v the vertex to
642start. The parameter visited, cyclic and path should be 0.
643RETURN: int
644     Maximal number of distinct cycles
645PURPOSE: Calculate the maximal number of distinct cycles in a single path starting at v
646ASSUME: Basering is a Letterplace ring
647"
648{
649  if (countedCycles[v] > -2) {
650    return (countedCycles);
651  }
652  // Mark the current vertex as visited
653  visited[v] = 1;
654
655  // Store the current vertex in path
656  if (path[1] == 0) {
657    path[1] = v;
658  } else {
659    path[size(path) + 1] = v;
660  }
661
662  int cycles = 0;
663  for (int w = 1; w <= ncols(G); w++) {
664    if (G[v,w] == 1) {
665      if (visited[w] == 1) { // found new cycle
666        // 1. for all vertices in path until w, check if they are cyclic
667        for (int j = size(path); j >= 1; j--) {
668          if(cyclic[path[j]] == 1) {
669            // 1.1 if yes, return -1
670            countedCycles[v] = -1;
671            return (countedCycles);
672          }
673          if (path[j] == w) {
674            break;
675          }
676        } kill j;
677
678        // 2. otherwise cycles++
679        for (int j = size(path); j >= 1; j--) {
680          // 2.2 remove the edges from that cycle and mark the vertices as cyclic
681          if (j == size(path)) { // special case in the first iteration
682            cyclic[v] = 1;
683            G[v, w] = 0;
684          } else {
685            cyclic[path[j]] = 1;
686            G[path[j], path[j+1]] = 0;
687          }
688          if (path[j] == w) {
689            break;
690          }
691        } kill j;
692
693        // 3. countCycles() on all these vertices
694        int maxCycleCount = 0;
695        for (int j = size(path); j >= 1; j--) {
696          countedCycles = countCycles(G, path[j], visited, cyclic, path, countedCycles);
697          if(countedCycles[path[j]] == -1) {
698            countedCycles[v] = -1;
699            return (countedCycles);
700          }
701          if (countedCycles[path[j]] > maxCycleCount) {
702            maxCycleCount = countedCycles[path[j]];
703          }
704          if (path[j] == w) {
705            break;
706          }
707        } kill j;
708        if (maxCycleCount >= cycles) {
709          cycles = maxCycleCount + 1;
710        }
711        kill maxCycleCount;
712      } else {
713        countedCycles = countCycles(G, w, visited, cyclic, path, countedCycles);
714        if (countedCycles[w] == -1) {
715          countedCycles[v] = -1;
716          return (countedCycles);
717        }
718        if (countedCycles[w] > cycles) {
719          cycles = countedCycles[w];
720        }
721      }
722    }
723  } kill w;
724  countedCycles[v] = cycles;
725  return (countedCycles);
726}
727
728proc lpUfGraph(ideal G, list #)
729"USAGE: lpUfGraph(G); G a set of monomials in a letterplace ring.
730RETURN: intmat or list
731NOTE: lpUfGraph(G); returns intmat. lpUfGraph(G,1); returns list L with L[1] an intmat and L[2] an ideal.
732      The intmat is the Ufnarovskij Graph and the ideal contains the vertices.
733PURPOSE: Constructs the Ufnarovskij graph induced by G
734      the adjacency matrix of the Ufnarovskij graph induced by G
735ASSUME: - basering is a Letterplace ring
736      - G are the leading monomials of a Groebner basis
737"
738{
739  dbprint("computing maxDeg");
740  int l = maxDeg(G);
741  if (l - 1 == 0) {
742    // TODO: how should the graph look like when l - 1 = 0 ?
743    ERROR("Ufnarovskij graph not implemented for l = 1");
744  }
745  int lV = lpVarBlockSize(basering);
746  // TODO: what if l <= 0?
747  dbprint("computing standard words");
748  ideal SW = lpStandardWords(G, l - 1); // vertices
749  int n = ncols(SW);
750  dbprint("n = " + string(n));
751  intmat UG[n][n]; // Ufnarovskij graph
752  for (int i = 1; i <= n; i++) {
753    for (int j = 1; j <= n; j++) {
754      dbprint("Ufnarovskii graph: " + string((i-1)*n + j) + "/" + string(n*n) + " entries");
755      // [Studzinski page 76]
756      poly v = SW[i];
757      poly w = SW[j];
758      intvec v_overlap;
759      intvec w_overlap;
760      if (l - 1 > 1) {
761        v_overlap = leadexp(v);
762        w_overlap = leadexp(w);
763        v_overlap = v_overlap[(lV+1) .. (l-1)*lV];
764        w_overlap = w_overlap[1 .. (l-2)*lV];
765      }
766      if (v_overlap == w_overlap && !lpLmDivides(G, v * lpVarAt(w, l - 1))) {
767        UG[i,j] = 1;
768      }
769      kill v; kill w; kill v_overlap; kill w_overlap;
770    } kill j;
771  } kill i;
772  if (size(#) > 0) {
773    if (typeof(#[1]) == "int") {
774      if (#[1] != 0) {
775        list ret = UG;
776        ret[2] = SW; // the vertices
777        return (ret);
778      }
779    }
780  }
781  return (UG);
782}
783example
784{
785  "EXAMPLE:"; echo = 2;
786  ring r = 0,(x,y,z),dp;
787  def R = freeAlgebra(r, 5); // constructs a Letterplace ring
788  setring R; // sets basering to Letterplace ring
789  ideal I = x*y, x*z, z*y, z*z;
790  lpUfGraph(I);
791  lpUfGraph(I,1);
792}
793
794static proc maxDeg(ideal G)
795{
796  int l = 0;
797  for (int i = 1; i <= size(G); i++) { // find the max degree in G
798    int d = deg(G[i]);
799    if (d > l) {
800      l = d;
801    }
802    kill d;
803  } kill i;
804  return (l);
805}
806
807static proc lpStandardWords(ideal G, int length)
808"ASSUME: G is simplified
809"
810{
811  if (length < 0) {
812    return (delete(ideal(0), 1)); // no standard words
813  }
814
815  ideal words = maxideal(length);
816  for (int i = ncols(words); i >= 1; i--) {
817    if (lpLmDivides(G, words[i])) {
818      words = delete(words, i);
819    }
820  } kill i;
821  return (words);
822}
823
824static proc ivStandardWords(list G, int length)
825"ASSUME: G is simplified
826"
827{
828  if (length <= 0) {
829    list words;
830    if (length == 0 && !ivdivides(G,0)) {
831      words[1] = 0; // iv = 0 means monom = 1
832    }
833    return (words); // no standard words
834  }
835  int lV = lpVarBlockSize(basering); // variable count
836  list prevWords = ivStandardWords(G, length - 1);
837  list words;
838  for (int i = 1; i <= lV; i++) {
839    for (int j = 1; j <= size(prevWords); j++) {
840      intvec word = prevWords[j];
841      word[length] = i;
842      // assumes that G is simplified!
843      if (!ivdivides(G, word)) {
844        words = insert(words, word);
845      }
846      kill word;
847    } kill j;
848  } kill i;
849  return (words);
850}
851
852static proc ivStandardWordsUpToLength(list G, int length)
853"ASSUME: G is simplified
854"
855{
856  list words = ivStandardWords(G,0);
857  if (size(words) == 0) {return (words)}
858  for (int i = 1; i <= length; i++) {
859    words = words + ivStandardWords(G, i);
860  } kill i;
861  return (words);
862}
863
864static proc ivdivides(list G, intvec iv) {
865  for (int k = 1; k <= size(G); k++) {
866    if (isIF(G[k], iv)) {
867      return (1);
868    } else {
869      if (k == size(G)) {
870        return (0);
871      }
872    }
873  } kill k;
874  return (0);
875}
876
877// lpGkDim now implemented in dynamic module
878/* proc lpGkDim(ideal G) */
879/* "USAGE: lpGkDim(G); G an ideal in a letterplace ring */
880/* RETURN: int */
881/* PURPOSE: Determines the Gelfand Kirillov dimension of A/<G> */
882/*        -1 means positive infinite */
883/* ASSUME: - basering is a Letterplace ring */
884/*       - G is a Groebner basis */
885/* " */
886/* { */
887/*   G = lead(G); */
888/*   G = simplify(G, 2+4+8); */
889
890/*   // check special case 1 */
891/*   int l = 0; */
892/*   for (int i = 1; i <= size(G); i++) { */
893/*     // find the max degree in G */
894/*     int d = deg(G[i]); */
895/*     if (d > l) { */
896/*       l = d; */
897/*     } */
898
899/*     // also if G is the whole ring return minus infinity */
900/*     if (leadmonom(G[i]) == 1) { */
901/*       ERROR("GK-Dim not defined for 0-ring") */
902/*     } */
903/*     kill d; */
904/*   } kill i; */
905/*   // if longest word has length 1, or G is the zero ideal, we handle it as a special case */
906/*   if (l == 1 || size(G) == 0) { */
907/*     int n = lpVarBlockSize(basering); // variable count */
908/*     int k = size(G); */
909/*     if (k == n) { // V = {1} no edges */
910/*       return(0); */
911/*     } */
912/*     if (k == n-1) { // V = {1} with loop */
913/*       return(1); */
914/*     } */
915/*     if (k <= n-2) { // V = {1} with more than one loop */
916/*       return(-1); */
917/*     } */
918/*   } */
919
920/*   dbprint("computing Ufnarovskii graph"); */
921/*   intmat UG = lpUfGraph(G); */
922/*   if (printlevel >= voice + 1) { */
923/*     UG; */
924/*   } */
925
926/*   // check special case 2 */
927/*   intmat zero[nrows(UG)][ncols(UG)]; */
928/*   if (UG == zero) { */
929/*     return (0); */
930/*   } */
931
932/*   // check special case 3 */
933/*   dbprint("topological sorting of Ufnarovskii graph"); */
934/*   UG = topologicalSort(UG); */
935/*   if (printlevel >= voice + 1) { */
936/*     UG; */
937/*   } */
938
939/*   dbprint("check if Ufnarovskii graph is DAG"); */
940/*   if (imIsUpRightTriangle(UG)) { */
941/*     UG = eliminateZerosUpTriangle(UG); */
942/*     if (ncols(UG) == 0 || nrows(UG) == 0) { // when the diagonal was zero */
943/*       return (0) */
944/*     } */
945/*     dbprint("DAG detected, using URTNZD growth alg"); */
946/*     return(UfGraphURTNZDGrowth(UG)); */
947/*   } */
948
949/*   // otherwise count cycles in the Ufnarovskij Graph */
950/*   dbprint("not a DAG, using regular growth alg"); */
951/*   return(UfGraphGrowth(UG)); */
952/* } */
953/* example */
954/* { */
955/*   "EXAMPLE:"; echo = 2; */
956/*   ring r = 0,(x,y,z),dp; */
957/*   def R = freeAlgebra(r, 5); // constructs a Letterplace ring */
958/*   R; */
959/*   setring R; // sets basering to Letterplace ring */
960/*   ideal I = z;//an example of infinite GK dimension */
961/*   lpGkDim(I); */
962/*   I = x,y,z; // gkDim = 0 */
963/*   lpGkDim(I); */
964/*   I = x*y, x*z, z*y, z*z;//gkDim = 2 */
965/*   lpGkDim(I); */
966/*   ideal G = y*x - x*y, z*x - x*z, z*y - y*z; G = std(G); */
967/*   G; */
968/*   lpGkDim(G); // 3, as expected for K[x,y,z] */
969/* } */
970
971proc lpGlDimBound(ideal G)
972"USAGE: lpGlDimBound(I); I an ideal
973RETURN: int, an upper bound for the global dimension, -1 means infinity
974PURPOSE: computing an upper bound for the global dimension
975ASSUME: - basering is a Letterplace ring, G is a reduced Groebner Basis
976EXAMPLE: example lpGlDimBound; shows example
977NOTE: if I = LM(I), then the global dimension is equal the Gelfand
978    Kirillov dimension if it is finite
979    Global dimension should be 0 for A/G = K and 1 for A/G = K<x1...xn>
980"
981{
982  G = simplify(G,2); // remove zero generators
983  // NOTE: Gl should be 0 for A/G = K and 1 for A/G = K<x1...xn>
984  // G1 contains generators with single variable in LM
985  ideal G1;
986  for (int i = 1; i <= size(G); i++) {
987    if (ord(G[i]) < 2) { // single variable in LM
988      G1 = insertGenerator(G1,G[i]);
989    }
990  } kill i;
991  G1 = simplify(G1,2); // remove zero generators
992
993  // G = NF(G,G1)
994  for (int i = 1; i <= ncols(G); i++) { // do not use size() here
995    G[i] = lpNF(G[i],G1);
996  } kill i;
997  G = simplify(G,2); // remove zero generators
998
999  // delete variables in LM(G1) from the ring
1000  def save = basering;
1001  def R = basering;
1002  if (size(G1) > 0) {
1003    while (size(G1) > 0) {
1004      if (lpVarBlockSize(R) > 1) {
1005        def @R = R - string(G1[1]);
1006        R = @R;
1007        kill @R;
1008        setring R;
1009        /* ring R = lpDelVar(lp2iv(G1[1])[1]); // TODO replace with proper method */
1010        ideal G1 = imap(save,G1);
1011        G1 = simplify(G1, 2); // remove zero generators
1012      } else {
1013        // only the field is left (no variables)
1014        return(0);
1015      }
1016    }
1017    ideal G = imap(save, G); // put this here, because when save == R this call would make G = 0
1018  }
1019
1020  // Li p. 184 if G = LM(G), then I = LM(I) and thus glDim = gkDim if it's finite
1021  for (int i = 1; i <= size(G); i++) {
1022    if (G[i] != lead(G[i])) {
1023      break;
1024    } else {
1025      if (i == size(G)) { // if last iteration
1026        int gkDim = lpGkDim(G);
1027        if (gkDim >= 0) {
1028          return (gkDim);
1029        }
1030        kill gkDim;
1031      }
1032    }
1033  } kill i;
1034
1035  intmat GNC = lpGraphOfNChains(G);
1036
1037  // assuming GNC is connected
1038
1039  // TODO: maybe loop+cycle checking could be done more efficiently?
1040  if (!imHasLoops(GNC) && imIsUpRightTriangle(topologicalSort(GNC))) {
1041    // GNC is a DAG
1042    intmat GNCk = GNC;
1043    intmat zero[1][ncols(GNCk)];
1044    int k = 1;
1045    // while first row isn't empty
1046    while (GNCk[1,1..(ncols(GNCk))] != zero[1,1..(ncols(zero))]) {
1047      GNCk = GNCk * GNC;
1048      k++;
1049    }
1050    // k-1 = number of edges in longest path starting from 1
1051    return (k-1);
1052  } else {
1053    // GNC contains loops/cycles => there is always an n-chain
1054    return (-1); // infinity
1055  }
1056}
1057example
1058{
1059  "EXAMPLE:"; echo = 2;
1060  ring r = 0,(x,y),dp;
1061  def R = freeAlgebra(r, 5); // constructs a Letterplace ring
1062  setring R; // sets basering to Letterplace ring
1063  ideal G = x*x, y*y,x*y*x; // it is a monomial Groebner basis
1064  lpGlDimBound(G);
1065  ideal H = y*x - x*y; H = std(H); // H is a Groebner basis
1066  lpGlDimBound(H); // gl dim of K[x,y] is 2, as expected
1067}
1068
1069static proc imHasLoops(intmat A) {
1070  int n = ncols(A);
1071  for (int i = 1; i <= n; i++) {
1072    if (A[i,i] == 1) {
1073      return (1);
1074    }
1075  } kill i;
1076  return (0);
1077}
1078
1079static proc lpGraphOfNChains(ideal G) // G must be reduced
1080{
1081  list LG = lpId2ivLi(lead(G));
1082  int n = lpVarBlockSize(basering);
1083  int degbound = lpDegBound(basering);
1084
1085  list V;
1086  for (int i = 0; i <= n; i++) {
1087    V[i+1] = i; // add 1 and all variables
1088  } kill i;
1089  for (int i = 1; i <= size(LG); i++) {
1090    intvec u = LG[i];
1091    for (int j = 2; j <= size(u); j++) {
1092      intvec v = u[j..size(u)];
1093      if (!contains(V, v)) {
1094        V = insert(V, v, size(V)); // add subword j..size
1095      }
1096      kill v;
1097    } kill j;
1098    kill u;
1099  } kill i;
1100  int nV = size(V);
1101  intmat GNC[nV][nV]; // graph of n-chains
1102
1103  // for vertex 1
1104  for (int i = 2; i <= n + 1; i++) {
1105    GNC[1,i] = 1; // 1 has an edge to all variables
1106  } kill i;
1107
1108  // for the other vertices
1109  for (int i = 2; i <= nV; i++) {
1110    for (int j = 2; j <= nV; j++) {
1111      intvec uv = V[i],V[j];
1112
1113      if (contains(LG, uv)) {
1114        GNC[i,j] = 1;
1115      } else {
1116        // Li p. 177
1117        // search for a right divisor 'w' of uv in G
1118        // then check if G doesn't divide the subword uv-1
1119
1120        // look for a right divisor in LG
1121        for (int k = 1; k <= size(LG); k++) {
1122          if (isSF(LG[k], uv)) {
1123            // w = LG[k]
1124            if(!ivdivides(LG, uv[1..(size(uv)-1)])) {
1125              // G doesn't divide uv-1
1126              GNC[i,j] = 1;
1127              break;
1128            }
1129          }
1130        } kill k;
1131      }
1132      kill uv;
1133    } kill j;
1134  } kill i;
1135
1136  return(GNC);
1137}
1138
1139static proc contains(list L, def item)
1140{
1141  for (int i = 1; i <= size(L); i++) {
1142    if (L[i] == item) {
1143      return (1);
1144    }
1145  } kill i;
1146  return (0);
1147}
1148
1149proc lpSubstitute(poly f, ideal s1, ideal s2, list #)
1150"USAGE: lpSubstitute(f,s1,s2[,G]); f poly, s1 list (ideal) of variables
1151  to replace, s2 list (ideal) of polynomials to replace with, G optional ideal to
1152  reduce with.
1153RETURN: poly, the substituted polynomial
1154ASSUME: - basering is a Letterplace ring
1155      - s1 contains a subset of the set of variables
1156      - s2 and s1 are of the same size
1157      - G is a Groebner basis,
1158      - the current ring has a sufficient degbound (which also can be calculated with lpCalcSubstDegBound())
1159NOTE: the procedure implements the image of a polynomial f
1160under an endomorphism of a free algebra, defined by s1 and s2:
1161variables, not present in s1, are left unchanged;
1162variable s1[k] is mapped to a polynomial s2[k].
1163- An optional ideal G extends the endomorphism as above to the morphism into the factor algebra K<X>/G.
1164EXAMPLE: example lpSubstitute; shows examples
1165"
1166{
1167  ideal G;
1168  if (size(#) > 0) {
1169    if (typeof(#[1])=="ideal") {
1170      G = #[1];
1171    }
1172  }
1173
1174  poly fs;
1175  for (int i = 1; i <= size(f); i++) {
1176    poly fis = leadcoef(f[i]);
1177    intvec ivfi = lp2iv(f[i]);
1178    for (int j = 1; j <= size(ivfi); j++) {
1179      int varindex = ivfi[j];
1180      if (varindex > 0) {
1181        int subindex = lpIndexOf(s1, var(varindex));
1182        if (subindex > 0) {
1183          s2[subindex] = lpNF(s2[subindex],G);
1184          fis = fis * s2[subindex];
1185        } else {
1186          fis = fis * lpNF(iv2lp(varindex),G);
1187        }
1188        /*fis = lpNF(fis,G);*/
1189        kill subindex;
1190      }
1191      kill varindex;
1192    } kill j;
1193    kill ivfi;
1194    fs = fs + fis;
1195    kill fis;
1196  }
1197  kill i;
1198  fs = lpNF(fs, G);
1199  return (fs);
1200}
1201example
1202{
1203  "EXAMPLE:"; echo = 2;
1204  ring r = 0,(x,y,z),dp;
1205  def R = freeAlgebra(r, 4);
1206  setring R;
1207  ideal G = x*y; // optional
1208  poly f = 3*x*x+y*x;
1209  ideal s1 = x, y;
1210  ideal s2 = y*z*z, x; // i.e. x --> yzz and y --> x
1211  // the substitution probably needs a higher degbound
1212  int minDegBound = lpCalcSubstDegBound(f,s1,s2);
1213  minDegBound; // thus the bound needs to be increased
1214  setring r; // back to original r
1215  def R1 = freeAlgebra(r, minDegBound);
1216  setring R1;
1217  lpSubstitute(imap(R,f), imap(R,s1), imap(R,s2));
1218  // the last parameter is optional; above it was G=<xy>
1219  // the output will be reduced with respect to G
1220  lpSubstitute(imap(R,f), imap(R,s1), imap(R,s2), imap(R,G));
1221}
1222
1223// another example:
1224/*
1225  //////// EXAMPLE B ////////
1226  ring r = 0,(x,y,z),dp;
1227  def R = freeAlgebra(r, 4);
1228  setring R;
1229
1230  poly f = 3*x*x+y*x;
1231  poly g = z*x+y;
1232  poly h = 7*x*z+x;
1233  ideal I = f,g,h;
1234  ideal s1 = x, y;
1235  ideal s2 = y*z*z, x;
1236
1237  int minDegBound = lpCalcSubstDegBound(I,s1,s2);
1238  setring r;
1239  def R1 = freeAlgebra(r, minDegBound);
1240  setring R1;
1241
1242  ideal I = imap(R,I);
1243  ideal s1 = imap(R,s1);
1244  ideal s2 = imap(R,s2);
1245  for (int i = 1; i <= size(I); i++) {
1246    lpSubstitute(I[i], s1, s2);
1247  }
1248*/
1249
1250static proc lpIndexOf(ideal I, poly p) {
1251  for (int i = 1; i <= size(I); i++) {
1252    if (I[i] == p) {
1253      return (i);
1254    }
1255  } kill i;
1256  return (-1);
1257}
1258
1259static proc ivIndexOf(list L, intvec iv) {
1260  for (int i = 1; i <= size(L); i++) {
1261    if (L[i] == iv) {
1262      return (i);
1263    }
1264  } kill i;
1265  return (-1);
1266}
1267
1268
1269static proc lpCalcSubstDegBoundSingle(poly f, ideal s1, ideal s2)
1270"USAGE: lpCalcSubstDegBoundSingle(f,s1,s2); f letterplace polynomial, s1 list (ideal) of variables
1271  to replace, s2 list (ideal) of polynomials to replace with
1272RETURN: int, the min degbound required to perform the substitution
1273ASSUME: - basering is a Letterplace ring
1274EXAMPLE: example lpCalcSubstDegBoundSingle; shows examples
1275"
1276{
1277  int maxDegBound = 0;
1278  for (int i = 1; i <= size(f); i++) {
1279    intvec ivfi = lp2iv(f[i]);
1280    int tmpDegBound;
1281    for (int j = 1; j <= size(ivfi); j++) {
1282      int varindex = ivfi[j];
1283      if (varindex > 0) {
1284        int subindex = lpIndexOf(s1, var(varindex));
1285        if (subindex > 0) {
1286          tmpDegBound = tmpDegBound + deg(s2[subindex]);
1287        } else {
1288          tmpDegBound = tmpDegBound + 1;
1289        }
1290        kill subindex;
1291      }
1292      kill varindex;
1293    } kill j;
1294    if (tmpDegBound > maxDegBound) {
1295      maxDegBound = tmpDegBound;
1296    }
1297    kill ivfi; kill tmpDegBound;
1298  } kill i;
1299
1300  // increase degbound by 50% when ideal is provided
1301  // needed for lpNF
1302  maxDegBound = maxDegBound + (maxDegBound div 2);
1303
1304  return (maxDegBound);
1305}
1306example
1307{
1308  // see lpCalcSubstDegBound()
1309}
1310
1311proc lpCalcSubstDegBound(ideal I, ideal s1, ideal s2)
1312"USAGE: lpCalcSubstDegBound(I,s1,s2); I ideal of polynomials, s1 ideal of variables to replace, s2 ideal of polynomials to replace with
1313RETURN: int, the min degbound required to perform all of the substitutions
1314ASSUME: - basering is a Letterplace ring
1315EXAMPLE: example lpCalcSubstDegBound; shows examples
1316NOTE: convenience method
1317"
1318{
1319  int maxDegBound = 0;
1320  for (int i = 1; i <= size(I); i++) {
1321    int tmpDegBound = lpCalcSubstDegBoundSingle(I[i], s1, s2, #);
1322    if (tmpDegBound > maxDegBound) {
1323      maxDegBound = tmpDegBound;
1324    }
1325    kill tmpDegBound;
1326  } kill i;
1327  return (maxDegBound);
1328}
1329example
1330{
1331  "EXAMPLE:"; echo = 2;
1332  ring r = 0,(x,y,z),dp;
1333  def R = freeAlgebra(r, 4);
1334  setring R;
1335  ideal I = 3*x*x+y*x, x*y*x - z;
1336  ideal s1 = x, y; // z --> z
1337  ideal s2 = y*z*z, x; // i.e. x --> yzz and y --> x
1338  // the substitution probably needs a higher degbound
1339  lpCalcSubstDegBound(I,s1,s2);
1340  lpCalcSubstDegBound(I[1],s1,s2);
1341}
1342
1343static proc isSF(intvec S, intvec I)
1344"
1345PURPOSE:
1346checks, if a word S is a suffix of another word I
1347"
1348{
1349  int n = size(S);
1350  if (n <= 0 || S == 0) {return(1);}
1351  int m = size(I);
1352  if (m < n) {return(0);}
1353  intvec IS = I[(m-n+1)..m];
1354  if (IS == S) {return(1);}
1355  else {return(0);}
1356}
1357
1358static proc isIF(intvec IF, intvec I)
1359"
1360PURPOSE:
1361checks, if a word IF is an infix of another word I
1362"
1363{
1364  int n = size(IF);
1365  int m = size(I);
1366
1367  if (n <= 0 || IF == 0) {return(1);}
1368  if (m < n) {return(0);}
1369
1370  for (int i = 0; (n + i) <= m; i++){
1371    intvec IIF = I[(1 + i)..(n + i)];
1372    if (IIF == IF) {
1373      return(1);
1374    }
1375    kill IIF;
1376  } kill i;
1377  return(0);
1378}
1379
1380// no longer working with new interface and new orderings
1381/* // TODO: use original ring attrib to create a new letterplace ring */
1382/* // removes a variable from a letterplace ring (a bit of a hack) */
1383/* static proc lpDelVar(int index) { */
1384/*   int lV = lpVarBlockSize(basering); // number of variables in the main block */
1385/*   int d = lpDegBound(basering); // degree bround */
1386/*   list LR = ringlist(basering); */
1387
1388/*   if (!(index >= 1 && index <= lV)) { return (basering); } // invalid index */
1389
1390/*   // remove frome the variable list */
1391/*   for (int i = (d-1)*lV + index; i >= 1; i = i - lV) { */
1392/*     LR[2] = delete(LR[2], i); */
1393/*   } kill i; */
1394
1395/*   // remove from a ordering */
1396/*   intvec aiv = LR[3][1][2]; */
1397/*   aiv = aiv[1..(d*lV-d)]; */
1398/*   LR[3][1][2] = aiv; */
1399
1400/*   // remove block orderings */
1401/*   int del = (lV - index); */
1402/*   int cnt = -1; */
1403/*   for (int i = size(LR[3]); i >= 2; i--) { */
1404/*     if (LR[3][i][2] != 0) { */
1405/*       for (int j = size(LR[3][i][2]); j >= 1; j--) { */
1406/*         cnt++; // next 1 */
1407/*         if (cnt%lV == del) { */
1408/*           // delete */
1409/*           if (size(LR[3][i][2]) > 1) { // if we have more than one element left, delete one */
1410/*             LR[3][i][2] = delete(LR[3][i][2],j); */
1411/*           } else { // otherwise delete the whole block */
1412/*             LR[3] = delete(LR[3], i); */
1413/*             break; */
1414/*           } */
1415/*         } */
1416/*       } kill j; */
1417/*     } */
1418/*   } kill i; */
1419
1420/*   def R = setLetterplaceAttributes(ring(LR),d,lV-1); */
1421/*   return (R); */
1422/* } */
1423/* example */
1424/* { */
1425/*   "EXAMPLE:"; echo = 2; */
1426/*   ring r = 0,(x,y,z),dp; */
1427/*   def A = freeAlgebra(r, 3); */
1428/*   setring A; A; */
1429/*   def R = lpDelVar(2); setring R; R; */
1430/* } */
Note: See TracBrowser for help on using the repository browser.