File: EagonResolution.m2

package info (click to toggle)
macaulay2 1.21%2Bds-3
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 133,096 kB
  • sloc: cpp: 110,377; ansic: 16,306; javascript: 4,193; makefile: 3,821; sh: 3,580; lisp: 764; yacc: 590; xml: 177; python: 140; perl: 114; lex: 65; awk: 3
file content (1598 lines) | stat: -rw-r--r-- 51,296 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
newPackage(
        "EagonResolution",
        Version => "1.0", 
        Date => "September 23, 2020",
        Authors => {{Name => "David Eisenbud", 
                  Email => "de@msri.org", 
                  HomePage => "http://www.msri.org/~de"},
	          {Name => "Mike Stillman", 
                  Email => "mike@math.cornell.edu", 
                  HomePage => "http://pi.math.cornell.edu/~mike"}},
        Headline => "Compute the Eagon Resolution of the residue field",
	Keywords => {"Commutative Algebra"},
        DebuggingMode => false
        )


export {
    "eagon", 
    "eagonResolution", -- use res EagonData as a synonym
    "golodBetti",   -- the betti table "as if" the module is Golod.
    "verticalStrand", --make a vertical strand of the Eagon complex
    "horizontalStrand", --make a vertical strand of the Eagon complex    
    "eagonBeta", -- the maps responsible for any non-minimality.
    "picture", -- compressed display of labeled matrices; various options
    "mapComponent",
--Symbols:
    "EagonData", -- the result of the eagon computation
    "Transpose", -- option for picture
    "DisplayBlocks", --option for picture
    "Display", -- option for picture and for eagonBeta
    "CompressBeta" --option for eagon
    }
protect EagonLength

--EagonData--
EagonData = new Type of HashTable
net EagonData := E -> net ("EagonData in <ring>.cache computed to length "| toString(E.EagonLength))

--verticalStrand--
verticalStrand = method()
verticalStrand (EagonData, ZZ) := ChainComplex =>  (E,n) -> (
    	   Kemax :=select(keys E, k -> (k_0 === "dVert" and E#k !=0));
           maxn := max apply(Kemax, k->k_1);
	   if n>maxn then error "That vertical strand wasn't defined";
    	   Ke := select(keys E, k -> (k_0 === 0 and k_1 === n and E#k !=0));
	   b := max (Ke/(k-> k_2));
           chainComplex(apply(b, i-> E#{"dVert", n,i+1})))
       
--horizontalStrand--       
horizontalStrand = method()
horizontalStrand (EagonData, ZZ) := ChainComplex =>  (E,i) -> (
    	   Kemax :=select(keys E, k -> (k_0 === "dHor" and E#k !=0));
           maxi := max apply(Kemax, k->k_2);
	   if i>maxi then error "That horizontal strand wasn't defined";
    	   Ke := select(keys E, k -> (k_0 === 0 and k_2 === i and E#k !=0));
	   b := max (Ke/(k-> k_1));
           chainComplex(apply(b-1, n -> E#{"dHor", n+1,i})))
    
///
restart
loadPackage("EagonResolution", Reload => true)
///

--tensoring a list of modules together:
tensorL = (R,L) -> (if L === {} then return R^1;
                              if #L === 1 then return L_0;
			      L_0**tensorL(R,drop(L,1)))
--golodBetti--			  
golodBetti = method()
golodBetti (ChainComplex, ChainComplex, ZZ) := BettiTally => (F,G,b) ->(
    --F,G finite free complexes (resolutions) over a ring S.
    --Compute the Betti table of what should be the Eagon resolution of 
    --the module resolved by G over the ring resolved by F
    --up to step b.
    symbs := apply(b+1, n->eagonSymbols(n,0));
    mods := apply(symbs, s -> 
	directSum apply(#s, 
	    i-> G_(s_i_0)**tensorL(ring F, apply(s_i_1, j->F_(j)))));
   betti chainComplex apply(b,i->map(mods_i,mods_(i+1),0))
   )

golodBetti (Module,ZZ) := BettiTally => (M,b) ->(
    --case where M is a module over a factor ring R = S/I,
    --MS is the same module over S
    --F = res I
    --K = res MS
    R := ring M;
    p := presentation R;
    S := ring p;
    phi1 := substitute(presentation M, S);
    phi := phi1 | target phi1 ** p;
    MS := prune coker phi;
    K := res MS;
    F := res coker p;
    golodBetti(F,K,b)
    )

///
restart
loadPackage("EagonResolution", Reload =>true)
S = ZZ/101[x,y,z]
R = S/ideal(x^2,y^3)
M = coker random(R^2, R^{-2,-2,-3})
b = 5
golodBetti(M,5)
///


homologyCover = method()
homologyCover(ChainComplex,ZZ) := Matrix => (C,i) ->(
    --map from a free module to C_i giving a basis of HH_i C.
    --Note that HH_i C must be of finite length, or there's an error.
    B := basis HH_i C;
    (gens target B)*matrix B)

homologyCover(ZZ, ChainComplex) := List => (b,C) -> 
    apply(b, i-> homologyCover(C,i))

homologyCover(ChainComplex) := List => C -> homologyCover(1+length C, C)

homologyIsomorphism = method()
homologyIsomorphism(Module, ChainComplex, ZZ) := Matrix => (M,C,i) ->(
    --If M is isomorphic to HH_i C then the matrix returned is a map
    -- target presentation M --> C_i 
    --inducing the isomorphism.
    --else the function throws and error.
    Hi := HH_i C;
    H := prune Hi;
    p := H.cache.pruningMap; -- iso from H to HH_i C
    f := degreeZeroSurjection(M,H);
    if f === null then error "non-iso modules that should be iso";
--    g := degreeZeroSurjection(H, M); -- this is just a check; get rid of it eventually
    map(C_i,HH_i C, gens Hi)*p*f
)

labeler = (L,F) -> directSum(1:(L=>F));

tensorWithComponents = method()
tensorWithComponents(Module, Module, Function) := Module => (F, G, combineIndices) -> (
    if F == 0 or G == 0 then return (ring F)^0;
    (compsF, indicesF) := componentsAndIndices F;
    (compsG, indicesG) := componentsAndIndices G;
    comps := flatten for f from 0 to #compsF-1 list (
        for g from 0 to #compsG-1 list (
            newindex := if indicesF#f === null or indicesG#g === null
	       then null else combineIndices(indicesF#f, indicesG#g);
            newindex => directSum(1:(newindex=>(compsF#f ** compsG#g)))
            )
        );
    if #comps == 0 then (ring F)^0 else directSum comps
    )
tensorWithComponents(Module, Module) := Module => (F, G) -> tensorWithComponents(F, G, (a,b) -> a|b)
tensorWithComponents(Matrix, Module, Function) := Matrix => (phi, G, combineIndices) -> (
                          src :=  tensorWithComponents(source phi, G, combineIndices);
                          tar :=  tensorWithComponents(target phi, G, combineIndices);			  
			  map(tar,src,phi**G))

eTensor = method()
eTensor(Module,Module) := Module => (F, G) -> tensorWithComponents(F, G, (a,b) ->(a#0+b#0,a#1|b#1))
eTensor(Matrix,Module) := Matrix => (phi,G) -> tensorWithComponents(phi, G, (a,b) ->(a#0+b#0,a#1|b#1))


trimWithLabel = method()
trimWithLabel ZZ := ZZ => n -> n
trimWithLabel Symbol := Symbol => s -> s

trimWithLabel Module := Module => M ->(
    if M == 0 then return (ring M)^0;
    ci := componentsAndIndices M;
    pci := positions(ci_0,M -> M!=0);
    if #pci ===0 then return (ring M)^0;
    if #pci === 1 then 
        labeler(ci_1_pci_0, ci_0_pci_0) 
    else
        directSum apply(pci, i->labeler(ci_1_i,ci_0_i))
)

trimWithLabel Matrix := Matrix => f ->(
    S := source f;
    T := target f;
    S':= trimWithLabel S;
    T':= trimWithLabel T;
    map(T',T,id_T')*f*map(S,S',id_S)
    )

trimWithLabel HashTable := HashTable => E -> hashTable apply(keys E, k-> (k,trimWithLabel E#k))

trimWithLabel ChainComplex := ChainComplex => F -> chainComplex apply(
                              length F, i-> trimWithLabel(F.dd_(i+1))
			      )

///
restart
loadPackage("EagonResolution", Reload =>true)
S = ZZ/101[x,y,z]
R = S/ideal"x2,y2,z2"
E = eagon(R,5)
E
eagonResolution E

resolution E
res E
eagon(R,4)
eagon(R,-1)
///

--eagon--
eagon = method(Options => {CompressBeta => true, Verbose => false})
eagon(Ring, ZZ) := EagonData => o -> (R,b) ->(
    --compute the Eagon configuration up to and including column b; and thus
    --also the "Eagon Resolution"
    --Y^b_0 \to...\to Y^1_0 \to Y^0_0. 

    --Let X_i be the free module R**H_i(K), where K is the Koszul complex on the variables of R.
        --We count X_i as having homological degree i+1.
    --The module Y^n_i = Eagon#{0,n,i} is described in Gulliksen-Levin as:
    --Y^0 = koszul vars R
    --Y^(n+1)_0 = Y^n_1; and 
    --for i>0, Y^(n+1)_i = Y^n_(i+1) ++ Y^n_0**X_i

    --Note that Y^n_i == 0 for i>1+length koszul vars R, so we will carry computations out to that length.

    --Each Y^n is a complex whose i-th homology is H_i(Y^n) = H_0(Y^n)^0**X_i (proved in Gulliksen-Levin).
    --assuming that the differential of Y^n and the maps Y^n --> Y^(n-1) are known
    --To construct the differential of Y^(n+1) and the map Y^(n+1) \to Y^n, 
    --this isomorphism must be made explicit.

    if b == -1 then (
	remove(R.cache,symbol EagonData);  
	<<"EagonData removed from "<<R<<".cache"<<endl;
	return null);    
    
    if R.cache.?EagonData  and R.cache.EagonData.EagonLength >= b then return R.cache.EagonData;
    
    Eagon := new MutableHashTable;        

    g := numgens R;
    K0 := koszul vars R;
    Eagon#"numgens" = g;

    --now label the modules in the Koszul complex    
    K := chainComplex(for i from 1 to length K0 list 
	map(labeler((i-1,{}), K0_(i-1)),
	    labeler((i,{}), K0_(i)),
	    K0.dd_i));
    
    homologyCover' := (K,i) -> (
	--Returns the map from X(i) to K_i
        phi := homologyCover(K,i); 
        Xi := labeler((0,{i}),source phi); 
        map(K_i, Xi, phi)
        );
    
    ebasis := memoize homologyCover';
    X := i -> if i<=g and (s := source ebasis(K,i))!=0 then  
             labeler((0,{i}),s) else R^0; -- X(i) is the X_i of Gulliksen-Levin.
    --we made it a function so that it would be available for all integers i.
        pd := 0; while X(pd)!=0 do pd = pd+1; pd = pd-1; -- max i such that X(i)!=0
    	Eagon#"pd" = pd;
    
    --first make the free modules Y^n_i = Eagon#{0,n,i}. 
    --The maps Y^(n+1)_j \to Y^n_j will be Eagon#{"dHor",n+1,j}
    west := "dHor";
    --The differential verticaldiff of Y^n is the sum of maps eagon#{"dVert",n,i} and eagon#{"NW",n,i}.
    north := "dVert";
    eagonBeta := "eagonBeta";

    --Make the free modules Eagon#{0,n,i}. 
    --two special cases:
    for i from 0 to g+1 do (
	Eagon#{0,0,i} = K_i;-- print Eagon#{0,0,i}.cache.components);
      for n from 0 to b do(
           Eagon#{0,n,g+2} = directSum(1:R^0);-- R^0++R^0; 
       	                 ));
    -- cases:
    for n from 1 to b do (
    for i from 0 to g+1 do(	
       if i == 0 then (
	   Eagon#{0,n,i} = Eagon#{0,n-1,1} ;
	)
        else (
        Eagon#{0,n,i} = Eagon#{0,n-1,i+1}++eTensor(Eagon#{0,n-1,0},X(i));
	     )
    ));

    --Now make the northward maps; the maps of the complexes Y^n = E#{0,n,*}
    --Note that the highest term in Y^n is in place b-n, so the top interesting homology is H_(b-n-1)
    --initialize:
    for i from 0 to g+2 do Eagon#{north,0,i} = K.dd_i;
	       
--Make the maps for n=1:
    --three special cases:
    Eagon#{north, 1, g+2} = map(Eagon#{0,1,g+1}, Eagon#{0,1,g+2},0);
    Eagon#{west,1,0} = Eagon#{north, 0,1};

    Eagon#{north,1,1} = (Eagon#{north,0,2})*(Eagon#{0,1,1})^[0] +
	                    ebasis(K,1)*(Eagon#{0,1,1})^[1];
    Eagon#{eagonBeta,1,1} = ebasis(K,1);			    
    Eagon#{west,1,1} = K.dd_2 | ebasis(K,1);
    
    for i from 2 to g+1 do(
	Eagon#{north,1,i} = (Eagon#{0,1,i-1})_[0]*
	                    (
	                    (Eagon#{north,0,i+1})*(Eagon#{0,1,i})^[0] +
	                    ebasis(K,i)*(Eagon#{0,1,i})^[1]
			    );
        Eagon#{eagonBeta,1,i} = ebasis(K,i);			    
	Eagon#{west,1,i} = K.dd_(i+1) | ebasis(K,i);
			);
    		
--now the induction, assuming that the Y^m have been defined for m<n:
    for n from 2 to b do(
       Eagon#{north, n, g+2} = map(Eagon#{0,n,g+1}, Eagon#{0,n,g+2},0);
       Eagon#{west,n,0} = Eagon#{north, n-1,1};
       Eagon#{eagonBeta,n,0} = Eagon#{eagonBeta, n-1,1};       
    	    	    
    for i from 1 to g+1 do(

--for eagonBeta:
        toLift := -(if #components Eagon#{0,n-2,i} ===1 then 
	   id_(Eagon#{0,n-2,i}) else Eagon#{0,n-2,i}_[0])*
             Eagon#{eagonBeta,n-1,i}*
             eTensor(Eagon#{north, n-2,1},X(i));
        M := Eagon#{north,n-2,i+1};
     
--idea: when o.CompresBeta == true, then 
--try the initial segments of blocks until lifting becomes possible; then
--set M = that lifting.  We
--know that a lifting is eventually possible.

     ind := indices flattenBlocks source M;
     if o.CompressBeta == true and ind =!=null then(
       numInd := #ind;
       if numInd != 0 then scan(numInd, p-> (
		 M' := extractBlocks(M,toList(0..p));
		 if toLift % M' == 0 then(
		     (M = M';
		      if o.Verbose == true then 
		         <<"Used "<<p+1<<" of "<<numInd<<" blocks of eagonBeta "<<(n,i)<<endl;
		      break)))));

     Eagon#{eagonBeta,n,i} = toLift//M;
 

     Eagon#{west,n,i} = Eagon#{0,n-1,i}_[0]*Eagon#{eagonBeta,n,i}*(Eagon#{0,n,i})^[1]+
	                   Eagon#{0,n-1,i}_[1]* (Eagon#{west,n-1,0}**X(i))  *(Eagon#{0,n,i})^[1]+
	                   (if Eagon#?{west,n-1,i+1} then 
			            Eagon#{0,n-1,i}_[0]*Eagon#{west,n-1,i+1}*Eagon#{0,n,i}^[0] 
				                     else 0);

     if i == 1 then 
         Eagon#{north,n,i} = -- special case because Y^n_0 is not a tensor product with Y^(n-1)_0
	                    (
	                    (Eagon#{north,n-1,i+1})*((Eagon#{0,n,i})^[0])+
	                    Eagon#{0,n-1,i}_[0]*Eagon#{eagonBeta,n,i}*(Eagon#{0,n,i})^[1]+
			    Eagon#{0,n-1,i}_[1]*(Eagon#{north, n-2,1}**X(i))*(Eagon#{0,n,i}^[1])
			    )
     else
         Eagon#{north,n,i} =
	    Eagon#{0,n,i-1}_[0]*
	                    (
	                    (Eagon#{north,n-1,i+1})*((Eagon#{0,n,i})^[0])+
	                    Eagon#{0,n-1,i}_[0]*Eagon#{eagonBeta,n,i}*(Eagon#{0,n,i})^[1]+
			    Eagon#{0,n-1,i}_[1]*(Eagon#{north, n-2,1}**X(i))*(Eagon#{0,n,i}^[1])
			    );
  ));

  H := trimWithLabel hashTable prepend((symbol EagonLength, b), pairs Eagon);
  E :=new EagonData from H;

  R.cache.EagonData = E;
  E
)

--eagonBeta--
eagonBeta = method(Options => {Display => "picture", Verbose => false})
--There are no maps eagonBeta(E,0) or eagonBeta(E,1); the display starts with eagonBeta(E,2).

eagonBeta(EagonData, ZZ) := o -> (E,n) -> (
         if o.Display == "picture" then 
             picture(E#{"eagonBeta",n,0},Verbose => o.Verbose)
         else if o.Display == "DisplayBlocks" then 
	     displayBlocks E#{"eagonBeta",n,0} 
         else 
	     E#{"eagonBeta",n,0}
         )

eagonBeta EagonData := List => o-> E -> (
    b := max apply(select(keys E, k-> k_0 === 0 and k_2 === 0), k->k_1);
    netList apply(b-1, n -> eagonBeta(E,n+2,Display => o.Display, Verbose => o.Verbose))
    )

extractBlocks = method()
extractBlocks(Matrix, List) := Matrix => (phi, L) -> (
    --the map map(target phi, source phi, phi_[L]*(source phi)^[L], where L is a list
    --of integers representing blocks in source phi.
    phi1 := flattenBlocks phi;
    src := source phi1;
    ind := indices src;
    sum(L, i->phi1_[ind_i]*src^[ind_i])
    )

--eagonResolution--
eagonResolution = method()
eagonResolution EagonData := ChainComplex => E ->(
    b := max apply( select(keys E, k-> k_0 === 0 and k_2 === 0), k->k_1);
    chainComplex(apply(b,n->E#{"dHor",n+1,0}))
    )    

eagonResolution(Ring,ZZ) := ChainComplex => (R,b) ->(
    eagonResolution eagon(R,b)
    )

resolution EagonData := opts->E -> eagonResolution E
--res--
--resolution--

///
restart
loadPackage("EagonResolution", Reload=>true)
S = ZZ/101[a,b,c]
I = ideal"a3,b3,c3"
R = S/I
E = eagon(R,6)
eagonResolution E -- works
--resolution EagonData := E -> eagonResolution E
resolution E -- fails
eagonResolution E
res R.cache.EagonData
eagonResolution E
///

eagonSymbols = method()
eagonSymbols(ZZ,ZZ) := List => (n,i) ->(
    --symbol of the module Y^n_i, as a list of pairs, defined inductively from n-1,i+1 and n-1,0
    --assumes large number of vars and pd.
    if n === 0 then return {(i,{})};
    if i === 0 then return eagonSymbols(n-1,1);
    e' := eagonSymbols (n-1,0);
    e'1 := apply (e', L -> L_1|{i});
    eagonSymbols(n-1,i+1)|apply (#e', j-> (e'_j_0,e'1_j))
    )

componentsAndIndices = (F) -> (
    if not F.cache.?components then (
        -- F has no components
        ({F}, {null})
        )
    else if #F.cache.components == 1 then (
        if F.cache.?indices then 
            ({F}, F.cache.indices)
        else 
            ({F}, {null})
        )
    else (
        a := for f in F.cache.components list componentsAndIndices f;
        (flatten(a/first), flatten(a/last))
        )
    )

flattenBlocks = method()
flattenBlocks Module := (F) -> (
    if not isFreeModule F then error "expected a free module";
    (comps, inds) := componentsAndIndices F;
    compsLabelled := for i from 0 to #comps-1 list (
        inds#i => comps#i
        );
    directSum compsLabelled
    )

flattenBlocks Matrix := (M) -> (
    F := flattenBlocks target M;
    G := flattenBlocks source M;
    map(F,G,matrix M)
    )

--displayBlocks--
displayBlocks = method()
displayBlocks Matrix := (M1) -> (
    M := flattenBlocks M1;
    src := select(indices source M, i-> i =!= null);
    tar := select(indices target M, i-> i =!= null);
    netList (prepend(
        prepend("", src),
        for t in tar list prepend(t, for s in src list (
                mts := M^[t]_[s];
                h := if mts == 0 then "." else if (numrows mts == numcols mts and mts == 1) then "1" else net mts
                ))
        ), Alignment=>Center)
    )
displayBlocks ChainComplex := List => C -> apply(length C, i -> displayBlocks(C.dd_(i+1)))
displayBlocks EagonData := List => E -> displayBlocks eagonResolution E

    
--pictureList--
pictureList = method(Options => {Verbose => false, Display => "picture", Transpose => false})
pictureList Matrix := o -> (M1) -> (
    M := flattenBlocks M1;
    src := indices source M;
    tar := indices target M;
    kkk := ring M/(ideal gens ring M);
    L := prepend(
        prepend("", src),
        for t in tar list prepend(t, for s in src list (
                mts := M^[t]_[s];
		nums := toString(numrows mts,numcols mts);
		cont := ideal M^[t]_[s];
		if o.Verbose == false then (
		  h := if mts == 0 then 
                          "." 
                       else if (numrows mts == numcols mts and mts == 1) then 
                          "id" 
                       else if cont == ideal(1_(ring mts)) then
		          toString numrows M^[t]_[s]|","|toString rank(kkk**M^[t]_[s]) 
                       else "*"
                  )
                else (
		  h = if mts == 0 then 
                          nums|" ." 
                      else if (numrows mts == numcols mts and mts == 1) then 
                          nums|" id" 
                      else if cont == ideal(1_(ring mts)) then 
                          nums|","|toString rank(kkk**M^[t]_[s]) 
                      else "*"
                  )
         )));
    if o.Transpose then transpose L else L
    )

pictureList ChainComplex := List => o -> C -> apply(length C, i-> pictureList(C.dd_(i+1), o))
pictureList EagonData := List => o -> E -> pictureList (eagonResolution E, o)

--picture--
picture = method(Options => options pictureList)
picture Matrix := o -> (M1) -> (
    if o.Display === "DisplayBlocks" then return displayBlocks M1;
    netList (pictureList(M1,o), Alignment => Center)
    )  
picture ChainComplex := Net => o -> C -> netList apply(length C, i-> picture(C.dd_(i+1), o))
picture EagonData := Net => o -> E -> picture (eagonResolution E, o)

--mapComponent--
mapComponent = method()
mapComponent(Matrix, Sequence, Sequence) := Matrix => (M,tar,src) -> (
    --Matrix should be one with labeled components, such as produced by
    --E = eagon(R,n)
    --M = E#{"dVert",4,1}
    --or
    --M = (eagonResolution(R,n)).dd_4
    M1 := flattenBlocks M;
    --use "member" and "componentsAndIndices" to check reasonableness? or try evaluating and catch error.
    try (M2 := M1^[tar]_[src]) then 
       M2
    else (
       <<endl<<"*** bad source or target symbol; use `picture M1' to check ***"<<endl<<endl; 
       error()
       )
    )

degreeZeroSurjection = method()
degreeZeroSurjection(Module,Module) := Matrix => (A,B) -> ( -- null if no surjection
    --creates a random degree 0 map f:A --> B and tests surjectivity. returns the map if true, else null.
    A' := prune A;
    pruningMapA := A'.cache.pruningMap; --from A' to A
    B' := prune B;
    pruningMapB := B'.cache.pruningMap;   
    H := Hom(A',B');
    B0 := basis(0,H); -- this seems to be total degree 0 in case of degreeLength>1
    f := homomorphism(B0*random(source B0, (ring B0)^1));
    t := coker f == 0;
    if t then pruningMapB * f * (pruningMapA)^-1 else null
)

beginDocumentation()

-*
restart
debug loadPackage("EagonResolution", Reload => true)
uninstallPackage "EagonResolution"
restart
installPackage "EagonResolution"
check "EagonResolution"
viewHelp EagonResolution
*-

--docEagonResolution
doc ///
Key
  EagonResolution
Headline
 Construct the Eagon double complex, which contains a resolution of the residue field
Description
  Text
   This package implements Eagon's algorithm for producing a not-necessarily minimal resolution of
   the residue field of a ring R = S/I where S is a polynomial ring and I is an ideal.
   The resolution constructed is minimal if and only if R is Golod. The resolution
   constructed is sometimes called the Golod or Shamash or Eagon resolution.
   
   This resolution was described, in the special case where it is minimal, by
   E.S. Golod: Homology of some local rings, Uspekhi Mat. Nauk 33 (1978), no. 5(203), 177–178.
   A general construction was described by Jack Shamash:
   The Poincaré series of a local ring II, J. Algebra 17 (1971), 1–18
   and, perhaps around the same time, by Jack Eagon.
   Eagon's construction, superficially different than Shamash'
   was not published by him, but is described in Ch. 4 of the notes
   by Gulliksen and Levin: Homology of local rings,
   Queen's Paper in Pure and Applied Mathematics, No. 20 Queen's University, Kingston, Ont. 1969.  
   
   To get a glimpse of the construction, consider the first steps. Let 
   K be the Koszul complex of S, which is the minimal S-free resolution
   of the residue field k. If numgens S = n, this begins 
   
   K_1 = S^n -> K_0 = S -> k.
   
   Let F be the minimal S-free resolution of R.
   by the right-exactness of the tensor product, the complex
   
   R**K_1 -> R**K_0 -> k 
   
   is a presentation of k, and of course R**K_2 maps to the kernel of
   R**K_1 -> R**K_0. But there are new elements of the kernel, obtained by
   writing the generators of I, which correspond to the generators of F_1,
   in terms of the generators of the maximal ideal. Thus we must add a map
   R**F_1 -> R**K_1, and it is easy to show that the resulting complex
   
   R**F_1 ++ R**K_2 -> R**K_1 -> R**K_0 -> k
   
   is exact. There are three important points to note:
   
   1) F_0 does not occur
   
   2) F_1 occurs in homological degree 2

   3) There is a map F_1 -> K_1 that must be introduced and that does not
      come from either the complex F nor the complex K.
      
   Eagon showed how this complex can be continued to a resolution.
   The underlying graded
   module of the complex is K ** T(F'), where F' is the complex F, shifted by
   1 in homological degree so that F_i is in homological degree i+1, and truncated
   by dropping F_0; and T(F') denotes the tensor algebra on the graded module F'.

   The differentials of the complex come from the differentials in the Koszul
   complex and various maps identifying the homology, at successive stages of the 
   construction, with tensor products of modules already constructed.
   These are also the ingredients of
   the "Massey products" from topology, used by Golod to construct the complex
   in the special case where there are ``trivial Massey products'', and the resolution is therefore minimal.
   
   The command  @TO eagon@ produces a type of hashTable called an @TO EagonData@, defined in the package.
   It contains all the data produced in
   Eagon's construction of the resolution: a double complex Y^n_i, and some internal 
   maps. The vertical differential is called dVert: Y^n_i -> Y^n_{i+1} and the horizontal
   differential is dHor: Y^n_i -> Y^{n-1}_i. 

   Thus for example if $R$ is a factor ring of a polynomial ring S, then
   
   E = eagon(R,5)
   @TO eagonResolution@ E

   or simply 

   res EE

   produces the first 5 steps of a 
   (not necessarily minimal) R-free resolution of the residue field of R.  The function picture gives
   alternate ways of viewing the innards of the resolution.
   
   
  Example
   S = ZZ/101[a,b,c]
   I = ideal(a,b,c)*ideal(b,c)
   R = S/I
   E = eagon(R,5)
   F = eagonResolution E
   assert(F == res E)
  Text
   As stated above, F = K\otimes T(F'), and one can see the maps between 
   each pair of summands. We label the summand 
   K_i**F_{j_1}**..**F_{j_m} with the symbol (i,\{j_1,..,j_m\}), and we can write out
   the differentials in block form with the function picture, 
   with the option Display => "DisplayBlocks", including the labels:
  Example
   F.dd_3
   picture(F.dd_3, Display => "DisplayBlocks")
  Text
   Since the matrices can be very large, it is sometimes better to know just whether
   a given block is zero or not, and this can be obtained with the function @TO picture@,
   with the default option Display => "picture".
  Example   
   picture F.dd_3
   picture (F, Verbose => true)
   picture (F, Verbose => true, Transpose => true)
SeeAlso
   eagon
   picture
   eagonResolution
   DisplayBlocks
   Transpose

///

--docEagonData
doc ///
   Key
    EagonData
   Headline
    HashTable storing output of eagon
   Usage
    E = eagon(R,b)
   Inputs
    R:Ring
    b:ZZ
   Outputs
    E:EagonData
   Description
    Text
     The command E = eagon(R,b) puts the EagonData E in R.cache so that E== R.cache.EagonData,
     and causes the message "EagonData in <ring>.cache computed to length b"
    Example
     R = ZZ/101[x,y]/ideal"x2,xy,y2"
     E = eagon(R,3)
     E === R.cache.EagonData
   SeeAlso
    eagon
///

--doceagon
doc///
   Key 
    eagon
    (eagon, Ring, ZZ)
    [eagon,CompressBeta]
    [eagon,Verbose]
   Headline 
    compute the Eagon double complex
   Usage
    E = eagon(R,b)
   Inputs
    R:Ring
    b:ZZ
     how far to carry the computation; -1 means "delete EagonData"
   Outputs
    E:EagonData
   Description
    Text
     eagon(R,b) computes the first b columns of the Eagon double complex Y^*_* of R, 
     and caches them in a HashTable of class EagonData in of R.cache.EagonData. 
     (The command eagon(R,-1) removes this.) 
     
     Following
     Gulliksen-Levin we think of Y^n_* as the n-th column, and Y^*_i as the i-th row. The columns
     Y^n are not acyclic.
     The i-th row is a resolution of the i-th module of boundaries in the Koszul complex K
     of the variables of R; in particular, the
     "Eagon Resolution" is the 0-th row,
     
     Y^b_0 \to...\to Y^1_0 \to Y^0_0. 
     
     Let X_i be the free module R**H_i(K), which is also the R**F_i, where F is a minimal free
     resolution of R as a module over the polynomial ring on the same set of variables.
     
     We count X_i as having homological degree i+1.
     With this convention, Y^*_0 has the form K\otimes T(F'), where T denotes the tensor algebra
     and F' is the F_1++F_2++... .
     
     The module Y^n_i = Eagon#{0,n,i} is described in Gulliksen-Levin as:
     Y^0 = koszul vars R
     Y^{n+1}_0 = Y^n_1; and 
     for i>0, Y^{n+1}_i = Y^n_{i+1} ++ Y^n_0**X_i

     Note that Y^n_i == 0 for i>1+length koszul vars R - n, 
     
     The i-th homology of Y^n_* is H_i(Y^n) = H_0(Y^n_*)**X_i (proved in Gulliksen-Levin). Part of the
     inductive construction will be a map inducing this isomorphism
     
     alpha^n_i = eagonBeta^n_i + dHor^n_0**1: Y^n_0**X_i \to Y^{n-1}_{i+1} ++ Y^{n-1}_0**X_i = Y^n
     
     
     Assume that the differential of Y^n and the maps dVert^n and alpha^n are known. We take
     
     dHor^{n+1}_0: Y^{n+1}_0 = Y^n_1 -> Y^n_0 to be dVert^n_1. 
     
     The remaining horizontal differentials dHor^{n+1}_i: Y^{n+1} \to Y^n have source and target as follows:
     
     Y^{n+1}_i = Y^n_{i+1} ++ Y^n_0**X_i -> Y^n_i = Y^{n-1}_{i+1} ++ Y^{n-1}_0**X_i.
     
     We take dHor^{n+1}_i to be the sum of two maps: 
     
     dVert^n_{i+1}  Y^n_{i+1} -> Y^n_i ++ Y^{n-1}_0**X_i.
     
     and alpha^{n+1}_i = eagonBeta^{n+1}_i + dHor^n_0**1:  Y^n_0**X_i \to  Y^n_i ++ Y^{n-1}_0**X(i).
     
     It remains to define eagonBeta^{n+1}_i; we take this to be
     the negative of
     
     a lifting along the map from Y^{n+1}_{i-1} \subset Y^n_i to Y^n_{i-1} of the composite
     
     dVert^{n+1}_{i-1} *  (dHor^n_0 ** X_i): Y^n_0**X_i -> Y^{n-1}_0.
     
    Example
     S = ZZ/101[a,b,c]
     I = ideal(a,b)*ideal"a3,b3,c3"
     R = S/I
     needsPackage "DGAlgebras"; isGolod R
     E = eagon(R,6)
    Text
     We can see the vertical and horizontal strands, and the eagonBeta maps
    Example
     verticalStrand(E,3)
     horizontalStrand(E,2)
     horizontalStrand (E,0)
     F = eagonResolution E     
     eagonBeta E     
    Text
     With the default option CompressBeta => true, only a subset of the components of Y^{n+1}_{i-1} are used.
     To see the effect of CompressBeta => true, consider:
    Example     
     eagon(R,-1)
     E = eagon(R,6, Verbose =>true)
     eagon(R,-1)
     En = eagon(R,6,CompressBeta => false)
     eagonBeta (E,4), eagonBeta(E,5)
     eagonBeta (En,4), eagonBeta(En,5)
    Text
     There are also ways to investigate the components of dVert, dHor, and eagonBeta; see 
     @TO picture@, @TO DisplayBlocks@, and @TO mapComponent@.
   SeeAlso
    verticalStrand
    horizontalStrand
///

--doceagonResolution
doc ///
   Key
    eagonResolution
    (eagonResolution, Ring, ZZ)
    (eagonResolution, EagonData)
   Headline
    computes a resolution of the residue field
   Usage
    F = eagonResolution(R,n)
    F = eagonResolution E
   Inputs
    R:Ring
     factor ring of a polynomial ring
    n:ZZ
     number of maps to compute
    E:EagonData
     computed by eagon(R,n)
   Outputs
    F:ChainComplex
     possibly non-minimal R-free resolution of R/(ideal vars R)    
   Description
    Text
     computes the Eagon resolution
    Example
     S = ZZ/101[a,b,c]
     I = ideal(a,b,c)*ideal(b,c)
     R = S/I
     eagonResolution(R,5)
   SeeAlso
     eagon
///

--docpicture
doc ///
   Key
    picture
    (picture, Matrix)
    (picture, ChainComplex)    
    (picture, EagonData)        
    [picture, Display]
    [picture, Verbose]
    [picture, Transpose]    
   Headline
    information about components of a labeled Matrix or ChainComplex
   Usage
    N = picture M
    L = picture C
    L = picture E
   Inputs
    M:Matrix
    C:ChainComplex
    E:EagonData
     produced by eagon; picture E is equivalent to picture @TO eagonResolution@ E and to picture res E
   Outputs
    N:Net
    L:List
     List of Nets, one for each map in the complex
   Description
    Text
     The free modules that are the sources and targets of the matrices defined in the EagonData eagon(R,b) 
     generally have many components. These can be analyzed with the functions
     picture, and @TO mapComponent@. Each summand of one of these free modules has
     a label of the form (i, \{u_1..u_s\}) representing the tensor product K_i ** X_{u_1}**..**X_{u_s},
     where 0\leq i \leq numvars R and 1\leq u_t \leq projective dimension over S of R.
     Thus a block is identified by a pair of such symbols in the order target, source.

     For any labeled matrix M, picture M (with the default option Display => "picture") 
     prints a net showing which blocks of the matrix are 
     0 (represented by .); or
     nonzero and in the maximal ideal, represented by *; or
     contain a unit entry, represented by a pair of numbers, which are
     the rank of the target of the matrix and the rank of the matrix tensored with the residue field
     (the "nonminimal part").
     
     Options:
     The default option is Display => "picture".
     With the option Display=>"DisplayBlocks", picture prints the matrices in each block.
     With any other assignment such as Display =>"", Display prints the whole matrix, without showing 
     the block structure.
     With the option Verbose => true, picture prints (numrows,numcols) for each block.
     With the option Transpose => true, picture prints the data for the transposed matrix 
     (possibly useful if there are many columns in the matrix).
     
     Applied to a complex of labeled matrices such as that produced by (res, EagonData) or applied to 
     an instance of EagonData, 
     picture prints a netList of the pictures of the maps in the complex

    Example
     S = ZZ/101[a,b,c]
     I = ideal(a,b)*ideal(a,b,c)
     R = S/I
     E = eagon(R,4);
     picture E
     picture E#{"eagonBeta",3,0}
     picture E
     picture verticalStrand(E,1)

   SeeAlso
    eagon
    "eagonBeta"
    eagonResolution
    DisplayBlocks
    mapComponent
///


--docdisplayBlocks
doc ///
   Key
    DisplayBlocks
   Headline
    Display => "DisplayBlocks" option for picture
   Usage
    N = picture(M, Display => "DisplayBlocks")
   Inputs
    M:Matrix
   Outputs
    N:Net
     prints a "picture" -- a net -- showing information about the blocks
   Description
    Text
     The free modules that are the sources and targets of the matrices defined in the EagonData eagon(R,b) 
     generally have many components. These can be analyzed with the functions
     @TO picture@, and @TO mapComponent@. Each summand of one of these free modules has
     a label of the form (i, {u_1..u_s}) representing the tensor product K_i ** X_{u_1}**..**X_{u_s},
     where 0\leq i \leq numvars R and 1\leq u_t \leq projective dimension R.
     Thus a block is identified by a pair of such symbols, representing source and target.
     
     Display => "picture"; with this option, @TO picture@ does not actually print
     the entries of the matrices. But picture(M, Display => "DisplayBlocks") prints a net 
     with the matrices themselves.
    Example
     S = ZZ/101[a,b,c]
     I = ideal(a,b)*ideal(a,b,c)
     R = S/I
     E = eagon(R,4);
     C = horizontalStrand(E,0)
     picture C
     picture(C, Display => "DisplayBlocks")
   SeeAlso
    eagon
    eagonResolution
    picture
    mapComponent
    horizontalStrand
    verticalStrand
///


--doceagonBeta
doc///
   Key
    eagonBeta
    (eagonBeta,EagonData)
    (eagonBeta,EagonData,ZZ)
    [eagonBeta, Display]
    [eagonBeta, Verbose]
   Headline
    print the eagonBeta maps in the Eagon resolution
   Usage
    N = eagonBeta E
    N = eagonBeta(E,n)
   Inputs
    E:EagonData
     created by eagon(R,b)
    n:ZZ
     which eagonBeta to show
   Outputs
    N:Net
     either a "Display" display (with Display => "picture", the default) or a "displayBlocks" display
     with Display => "DisplayBlocks" or a plain matrix if Display => <anything else>.
     With Verbose => true, the display includes (rank target eagonBeta,rank source eagonBeta)
   Description
    Text
     The eagonBeta maps are the components of the Eagon resolution,
     starting from the 2nd differential that may or may not be minimal, 
     and are therefore most interesting. With the default option 
     
     Display => "picture"
     
     the pictures (which blocks are 0,nonzero, nonminimal) are shown; or
     the displayBlocks output
     with Display => "DisplayBlocks" or a plain matrix if Display => <any other string>.
     
     In the notes of Gulliksen-Levin it is proven that R is Golod if and only if the maps eagonBeta can be
     taken with values in the Koszul complex; thus in particular, if R is Golod, then 
     there are no "new" eagonBetas after eagonBeta(E,numgens R+1). Since R is Golod iff all the eagonBeta matrices have
     all entries in the maximal ideal, this proves in particular that R is Golod if and only if
     the Betti numbers of the resolution of coker vars R agree up to the step numgens R with 
     the Betti numbers of the Eagon resolution.
    Example
     S = ZZ/101[a,b,c,d]
     I = ideal(a,b,c)*ideal(a,b,c,d)
     I = ideal"a3,b3,c3"
     R = S/I
     E = eagon(R,4);
     eagonBeta(E,4)
     eagonBeta(E,4,Display => "DisplayBlocks")
     eagonBeta(E,4,Display => "")     
     eagonBeta E
   SeeAlso
    eagon
    picture
    DisplayBlocks
///

--dochorizontalStrand
doc///
   Key
    horizontalStrand
    (horizontalStrand, EagonData, ZZ)
   Headline
    extracts one horizontal strand from an Eagon double complex
   Usage
    F = horizontalStrand(E,i)
   Inputs
    E:EagonData
     produced by eagon(R,b)
    i:ZZ
     which strand
   Outputs
    F:ChainComplex
     beginning of the free resolution of the i-th boundary module of the Koszul complex
   Description
    Text
     The 0-th strand is a possibly non-minimal resolution of the residue field.
     More generally, the i-th strand resolves the i-th boundary module in the Koszul complex of R. These
     resolutions are
     all minimal iff R is Golod.
    Example
     S = ZZ/101[x,y,z]
     R = S/((ideal(x,y))^2+ideal(z^3))
     E = eagon(R,5);
     F = horizontalStrand(E,2)
     picture F
   SeeAlso
    verticalStrand
    eagon
    picture
///

--docverticalStrand
doc ///
   Key
    verticalStrand
    (verticalStrand, EagonData, ZZ)
   Headline
    extracts one vertical strand from an Eagon double complex
   Usage
    F = verticalStrand(E,i)
   Inputs
    E:EagonData
     produced by eagon(R,b)
    i:ZZ
     which strand
   Outputs
    F:ChainComplex
     beginning of the free resolution of the i-th boundary module of the Koszul complex
   Description
    Text
     The 0-th vertical strand is the Koszul complex of R. The vertical strands are
     never resolutions unless R is regular. The key lemma in Eagon's treatment identifies
     the i-th homology H_i of the n-th vertical strand with H_0**X_i.
    Example
     S = ZZ/101[x,y,z]
     R = S/((ideal(x,y))^2+ideal(z^3))
     E = eagon(R,5);
     F = verticalStrand(E,3)
     picture F
   SeeAlso
    horizontalStrand
    eagon
    picture
///

--docmapComponent
doc ///
   Key
    mapComponent
    (mapComponent, Matrix, Sequence, Sequence)
   Headline
    extract a single component from a labeled map
   Usage
    N = mapComponent(M,tar,src)
   Inputs
    M:Matrix
     labeled map from eagon(R,b)
    tar:Sequence
     symbol of a free module components of the Eagon resolution
    src:Sequence
     symbol of a free module components of the Eagon resolution
   Outputs
    N:Matrix
   Description
    Text
     The source and target of a a map in the Eagon double complex, such as
     dVert, dHor, and eagonBeta, are direct sums of tensor products of the form
     K_i**X_{u_1}**..**X_{u_s} where K_i is a term of the Koszul complex and X_i
     is a term of the S-free resolution of R, all tensored with R.
     This tensor product is represented by a symbol that is a two element Sequence
     
     (i, \{u_1..u_s\})

     The block structure of the matrix, together with the source and
     target Sequences, can be seen from 
     picture M.
     
     The function mapComponent returns a single block.
    Example
     S = ZZ/101[a,b,c,d,e]
     R = S/(ideal(e^2,d*e^4)+(ideal"ab,ac")^2) --a non-Golod ring, generators in different degrees
     E = eagon (R,5);
     picture E#{"dHor",3,0}
     mapComponent(E#{"dHor",3,0}, (0,{1}),(1,{1}))
     picture E#{"dVert",3,1}
     mapComponent(E#{"dVert",3,1}, (0,{2}),(0,{1,1})) 
     picture E#{"eagonBeta",3,1}
     mapComponent(E#{"eagonBeta",3,1}, (0,{2}),(0,{1,1})) 
   SeeAlso
    picture
    DisplayBlocks
    eagon
///

--docgolodBetti
doc ///
   Key
    golodBetti
    (golodBetti, ChainComplex, ChainComplex, ZZ)
    (golodBetti, Module, ZZ)    
   Headline
    list the ranks of the free modules in the resolution of a Golod module
   Usage
    B = golodBetti(F,K,b)
    B = golodBetti(M,b)    
   Inputs
    F:ChainComplex
     resolution, typically of (R = S/I)^1 over S
    K:ChainComplex
     resolution, typically of an R-module M over S
    M:Module
     R-module
    b:ZZ
     homological degree to which to carry the computation
   Outputs
    B:BettiTally
     This would be betti table of the free res of M over R, if M were a Golod module over R
   Description
    Text
     Let S be a standard graded polynomial ring. A module M over R = S/I is Golod if
     the resolution H of M has maximal betti numbers given the
     betti numbers of the S-free resolutions F of R and K of M. This resolution, H,
     has underlying graded module H = R**K**T(F'), where F' is the truncated resolution
     F_1 <- F_2... and T(F') is the tensor algebra.
     
     Since the component modules of H are given, the computation only requires the computation of
     the minimal S-free resolution of M, and then is purely numeric;
     the differentials in the R-free resolution of M are not computed.
     
     In case M = coker vars R, the result is the Betti table of the Golod-Shamash-Eagon 
     resolution of the residue field.
     
     We say that M is a Golod module (over R) if the ranks of the free modules in a minimal R-free resolution
     of M are equal to the numbers produced by golodBetti. Theorems of Levin and Lescot assert that if
     R has a Golod module, then R is a Golod ring; and that if R is Golod, then the d-th syzygy
     of any R-module M is Golod for all d greater than or equal to the projective dimension
     of M as an S-module (more generally, the co-depth of M) (Avramov, 6 lectures, 5.3.2).
     
    Example
     S = ZZ/101[a,b,c]
     I = (ideal(a,b,c^2))^2
     F = res(S^1/I)
     K = res coker vars S
     R = S/I
     E = eagon(R,6);
     golodBetti(F,K,6)
     betti res (coker vars R, LengthLimit => 6)
     betti eagonResolution E     
   SeeAlso
    eagon
    eagonResolution
///
-*
--doceagonSymbols
doc ///
   Key
    eagonSymbols
    (eagonSymbols, ZZ, ZZ)
   Headline
    symbols of the components of a module in the Eagon double complex    
   Usage
    L = eagonSymbols(n,i)
   Inputs
    n:ZZ
     column indix
    i:ZZ
     row index
   Outputs
    L:List
     list of symbols
   Description
    Text
     Each module in the Eagon double complex is a direct sum of tensor products of
     a component of the Koszul complex and a list of modules X_i. Such
     a tensor product is represented by a symbol that is a two element Sequence
     of the form
     
     (i, {u_1..u_s})
     
     representing K_i ** X_{u_1} ** .. ** X_{u_s}. 
     
     The function eagonSymbols(n,i) produces the list of symbols of the summands
     of the module Y^n_i in the n-th column, i-th row of the double complex.
     This is done arithmetically, that is,  without computing the double complex.
    Example
     L = eagonSymbols(3,2)
    Text
     This is the list of symbols associated to the source of (for example)
     the 2-nd differential in the 3-rd vertical strand of the double complex.
    Example
     S = ZZ/101[a,b,c,d,e]
     R = S/(ideal vars S)^2 --a Golod ring
     E = eagon(R,3);
     V = verticalStrand(E,3)
     picture (V.dd_2)
    Text
     Compare the top row with
    Example
     L
   SeeAlso
    eagon
    picture
    DisplayBlocks
    mapComponent
///
*-

--docDisplay
doc ///
   Key
    Display
   Headline
    Option for eagonBeta, default is "picture"
   Usage
    eagonBeta(E,Display => "picture")
   Inputs
    E:EagonData
   Description
    Text
     if Display=>"picture" then @TO picture@ is invoked; if Display =>"DisplayBlocks" 
     then a net with the matrices (the "blocks") is produced.
    Example
     R = ZZ/101[x,y,z]/ideal"x3,y3,z3"
     E = eagon(R,5);
     eagonBeta(E,3)
     eagonBeta(E,3,Display =>"DisplayBlocks")
   SeeAlso
    eagon
///
--docCompressBeta
doc ///
   Key
    CompressBeta
   Headline
    CompressBeta is an option for eagon, default is true
   Usage
    E = eagon(R,b,CompressBeta =>bool1, Verbose =>bool2)
   Inputs
    R:Ring
    b:ZZ
    bool1: Boolean
    bool2: Boolean
   Outputs
    E:EagonData
   Description
    Text
     eagonBeta(E,i) := E#{"eagonBeta",n,0} 
     is defined by lifting another map along the map dVert^n_i. If
     CompressBeta => true, the default, then the lifting uses
     the smallest initial subsequence of the blocks of source dVert as possible,
     and thus the columns of picture eagonBeta(E,i) will have fewer nonzero entries.
     if Verbose =>true, then data about the usage is printed.
    Example
     R = ZZ/101[x,y,z]/ideal"x3,y3,z3"
     E = eagon(R,4,CompressBeta =>true, Verbose =>true);
   SeeAlso
    eagon
    eagonBeta
///
doc ///
   Key
    Transpose
   Headline
    Transpose => false, default option for picture
   Usage
    N = picture(M, Transpose => b)
   Inputs
    M:Matrix
     labeled matrix
    b:Boolean
   Outputs
    N: Net
   Description
    Text
     With the option Transpose => true, picture prints the picture of the transposed matrix; when 
     the matrix has many more columns than rows this makes it easier to read.
    Example
     S = ZZ/101[a,b]
     R = S/ideal"a2,b2"
     E = eagon(R,3)
     picture res E
     picture(res E, Transpose => true)
   SeeAlso
    DisplayBlocks
    Verbose
///



doc ///
   Key
    (net, EagonData)
   Headline
    prints the homological degree to which the EagonData has been computed
   Usage
    s = net E
   Inputs
    E:EagonData
   Outputs
    s:Net
   Description
    Text
     prints the homological degree to which the EagonData has been computed
    Example
     R = ZZ/101[a,b]/ideal"a2,b2"
     E = eagon(R,4)
     net E
   SeeAlso
    EagonData
///
doc ///
   Key
    (resolution, EagonData)
   Headline
    outputs the resolution that is the 0th row of the Eagon double complex
   Usage
    C = resolution E
   Inputs
    E:EagonData
   Outputs
    C:ChainComplex
   Description
    Text
     This command is equivalent to @TO (eagonResolution, EagonData)@.
    Example
     R = ZZ/101[a,b]/ideal"a2,b2"
     E = eagon(R,4)
     C = resolution E
     picture C
   SeeAlso
    eagon
    EagonData
    eagonResolution
    picture
///

----TESTS
-*
restart
uninstallPackage "EagonResolution"
installPackage "EagonResolution"
check EagonResolution
viewHelp EagonResolution
loadPackage("EagonResolution", Reload => true)
*-

TEST///
     S = ZZ/101[a,b,c]
     I = ideal(a,b)*ideal"a3,b3,c3"
     R = S/I
     E = eagon(R,6,CompressBeta =>true);
     En = eagon(R,6,CompressBeta => false);
     F = eagonResolution E
     Fn= eagonResolution En
     assert(F.dd^2 == 0)
     assert(Fn.dd^2 == 0)
     assert(all(5, i->prune HH_(i+1) F == 0))
     assert(all(5, i->prune HH_(i+1) Fn == 0))
///

TEST///
debug EagonResolution
S = ZZ/101[a,b,c]/ideal(b^2,c^2) -- complete intersection
B = 3
E = eagon(S,B);
F = eagonResolution E
M = F.dd_3
M' = extractBlocks(M,{0,2})
M'' = map(target M', source M', matrix {{c, 0, -b, 0, 0, 0, 0, 0}, {-b, 0, 0, -c, 0, 0, 0, 0}, {a, 0, 0, 0, 0, -c, b, 0}, {0, 0, a, 0, b,
      0, c, 0}, {0, 0, 0, a, 0, b, 0, c}})
assert(M' == M'')
///

TEST///
S = ZZ/101[a,b,c]
R = S/(ideal"ab,ac")^2 --a simple Golod ring on which to try this
b = 6
E = eagon(R,b);
F = eagonResolution E
G = res (coker vars R, LengthLimit => b)
assert(betti F == betti G)
assert(F.dd^2== 0)
assert(all(b-1, i-> prune (HH_(i+1) F) == 0))
///

TEST///
debug EagonResolution
S = ZZ/101[a,b,c,d,e]
A = S^1
B = S^2
A' = labeler((0,{1}),A)
indices A'
B' = labeler((0,{2}),B)
assert(indices eTensor(A',B') == {(0, {1, 2})})
assert(indices (T = eTensor(B',A')) == {(0, {2, 1})})
assert(indices trimWithLabel T ==  {(0, {2, 1})})
///

TEST/// -- test of eagon
S = ZZ/101[a,b,c]
R = S/(ideal"ab,ac")^2 --a simple Golod ring on which to try this
bound = 6
E = eagon(R,bound);
Y = apply(bound, n-> verticalStrand(E,n))
assert(all(#Y, n->((Y_n).dd)^2 == 0))
assert all(#Y, n->isHomogeneous Y_n)
F = eagonResolution(R,bound)
F = eagonResolution E
assert isHomogeneous F
assert all(bound-1,i-> prune HH_(i+1) F == 0)
assert(betti res(coker vars R,LengthLimit => bound) == betti F)

S = ZZ/101[a,b,c,d,e]
R = S/(ideal(e^2,d*e^4)+(ideal"ab,ac")^2) --a non-Golod ring, generators in different degrees
E = eagon(R,5);
Y = apply(5, n -> verticalStrand(E,n));
assert(all(#Y, n->((Y_n).dd)^2 == 0))
assert all(#Y, n->isHomogeneous Y_n)
F = eagonResolution(R,5)
assert isHomogeneous F
assert all(4,i-> prune HH_(i+1) F == 0)
///


TEST///
debug needsPackage "EagonResolution"
assert(eagonSymbols(1,2) == {(3, {}), (0, {2})})
assert (eagonSymbols(2,1) == eagonSymbols(3,0))
assert(eagonSymbols(1,3) == {(4, {}), (0, {3})})
assert(eagonSymbols(2,2) == {(4, {}), (0, {3}), (1, {2})})
assert(eagonSymbols(2,0) == {(2, {}), (0, {1})})
assert(eagonSymbols(3,1) == {(4, {}), (0, {3}), (1, {2}), (2, {1}), (0, {1, 1})})
assert(eagonSymbols(3,2) == {(5, {}), (0, {4}), (1, {3}), (2, {2}), (0, {1, 2})})
///

///
restart
loadPackage("EagonResolution",Reload => true)
needsPackage "DGAlgebras"
///
TEST///
S = ZZ/101[x,y,z]
I = trim(ideal(x,y)*ideal"x,y2,z")
R = S/I
F = res I
G = res coker vars S
b = 6
H = res(coker vars R,LengthLimit =>6)
E = eagon(R,b);
assert(betti eagonResolution E == betti H)
assert(golodBetti(F,G,b) == betti H)
assert (golodBetti (coker vars R,b) == betti H)
///


-- TODO: PLACE INTO M2 CORE
--SOME USEFUL INTERNAL FUNCTIONS
--    "compositions"
--    "isDegreeZeroSurjection",
--    "isIsomorphic"
-*
compositions(ZZ,ZZ,ZZ) := (nparts, k, maxelem) -> (
    -- nparts is the number of terms
    -- k is the sum of the elements
    -- each element is between 0 and maxelem.
     compositionn := (n,k) -> (
	  if n===0 or k < 0 then {}
	  else if k===0 then {toList(n:0)}
	  else (
          set1 := apply(compositionn(n-1,k), s -> s | {0});
          set2 := apply(compositionn(n,k-1), s -> s + (toList(n-1:0) | {1}));
          set2 = select(set2, s -> s#(n-1) <= maxelem);
          join(set1, set2)
          )
      );
     compositionn = memoize compositionn;
     result := compositionn(nparts,k);
     compositionn = null;
     result
     );

isDegreeZeroSurjection := method()
isDegreeZeroSurjection(Module,Module) := Boolean => (A,B) -> (
    --tests a random degree 0 map f:A --> B to see whether its a surjection, 
    --and returns the answer. If "true" and  o.Verbose == true then returns f.
    H := Hom(A,B);
    B0 := basis(0,H); -- this seems to be total degree 0 in case of degreeLength>1
    f := homomorphism(B0*random(source B0, (ring B0)^1));
    coker f == 0
)

-- the following function works for modules in the graded case.
-- and produces (only) graded isomorphisms.
isIsomorphic = method()
isIsomorphic(Module,Module) := (A,B) -> (
    --tests random degree 0 maps A->B, B->A and returns true
    --if both are surjective.
    if not(isHomogeneous A and isHomogeneous B) then 
	  error"not implemented for inhomogeneous modules";
    Ap := prune A;
    Bp := prune B;
    dA := set flatten degrees source gens Ap;
    dB := set flatten degrees source gens Bp;
    if dA =!= dB then return false;
    degreeZeroSurjection(Ap,Bp) =!= null and degreeZeroSurjection(Bp,Ap) =!= null
    )
*-

TEST/// --test of degreeZeroSurjection
debug needsPackage "EagonResolution"--degreeZeroSurjection is not exported.
S = ZZ/101[a,b]
A = S^{1,0}; B = S^{0,1};B1 = B/((ideal a)*B)
f = degreeZeroSurjection(A,B)
assert(source f == A)
assert(target f == B)
assert(coker f == 0)

f = degreeZeroSurjection(A,B1)
assert(source f == A)
assert(target f == B1)
assert(coker f == 0)

f = degreeZeroSurjection(B1,A)
assert(f===null)
///

end--
------------------------------------
restart
uninstallPackage "EagonResolution"
restart
installPackage "EagonResolution"
check EagonResolution

viewHelp EagonResolution