// C++ Program for handling Nichols algebras via computing bilinear forms, // written by Eric Mueller 1998-1999, last change 16 June 1999. // (e-mail: emueller@rz.mathematik.uni-muenchen.de) // No kind of warranty is given for correctness and that the program serves // any purpose. Please feel free to report any kinds of errors to the author. // Usage and distribution of this program is free provided that either the // file is not changed or all changes are marked with the name and address // of the person who has done the changes and the changes are reported // below these lines. // In UNIX systems type the following to compile the program (assuming that the // program name should be 'nichols' and this source file is called 'nichols.cpp'): // CC -fast -o nichols nichols.cpp // or // g++ -O -o nichols nichols.cpp #include #include #include const int maxT=20; // maximal number of algebra generators of B(V) const int maxM=50; // Maximal length of monomials const int maxN=20; // maximal value of the order of the root of unity q typedef struct { int exponent; int permut[maxT];} grupp; typedef int* Zeiger; typedef enum {FALSE, TRUE} Boolean; typedef enum {Normal, Ausgabedoppelt, NurDateien} Dateistatus; typedef enum {FLUSH, ENDL} Manipulator; struct Manip {Manipulator eintrag;}; // Manipulatoren const Manip Flush ={FLUSH}; const Manip Endl = {ENDL}; // globale Variablen ifstream Eingabedatei; ofstream Ausgabedatei; unsigned long int AbbruchZahl=1000, LimitZahl=0; // maximale Anzahl an BLF-Summanden pro BLF-Berechnung unsigned long int Zaehler, ZweitZaehler; // Zaehlt, wie viele BLF-Summanden vorkommen Boolean Einfachausgabe=FALSE; // FALSE: Format fuer BLF-Wert 2+4q^3, TRUE: 2 0 0 4 Boolean Schrittanzahl=TRUE; // Angabe, wie viele Summanden fuer BLF-Wert vorkamen Boolean Quadratfrei=FALSE; // gibt an, ob Quadrate der Erzeuger immer verschwinden Boolean Fehlversuchsangabe=TRUE; // gibt an, ob Angaben zur Laenge und zu Fehlversuchen ausgegeben werden sollen Boolean GruppengradNull=FALSE; // gibt an, ob ausgegeben werden soll, dass Monom Gesamtgrad Null hat, d.h. auf jees Element trivial operiert int N,T; // Ordnung der Einheitswurzel, Anzahl der B(V)-Erzeuger Boolean Charakter=TRUE; // TRUE=Charakter oder FALSE=Bicharakter int zei[256]; // Zeichenumsetzungstabelle fuer B(V)-Erzeuger char erzeuger[maxT]; // Erzeuger von B(V) short int expo[maxT][maxT]; // Charakterwerte (Exponenten der Einheitswurzel) short int* expoeinfach = &expo[0][0]; // Hilfstabelle fuer einfachen Charakter short int a[maxT][maxT]; // a[j][i]: Element j wirkt auf Element i durch Konjugation short int erstes[maxM]; // erstes Monom short int* ErstMonom; // Zeiger auf erstes Monom fuer 'ausrechnen' short int zweites[maxM]; // zweites Monom short int* ZweitMonom; // zeiger auf zweites Monom fuer 'ausrechnen' short int naechst[maxT][maxM+2]; // naechst[i][j] (bzw. vor[i][j]) gibt an, an welcher ersten Stelle short int vor[maxT][maxM+2]; // hinter (vor) Stelle j das Zeichen i im ersten Monom vorkommt. short int naechstes[maxM+1]; // naechstes Vorkommen des Zeichens an Stelle i nach Stelle i im ersten Monom short int M; // Monomlaenge long int ergebnis[maxN]; // Ergebnisvektor short int permutation[maxM]; // Hilfsfeld mit Daten ueber passende Permutation Boolean TrivialWirkung=FALSE; // gibt an, ob Operation der Erzeuger trivial ist. short int monomgrad[maxT]; // Grad des ersten Monoms (Angabe, wie oft jeder Erzeuger vorkommt, nur bei trivialer Wirkung class EinAus { private: Dateistatus status; public: EinAus(Dateistatus estatus) {status=estatus;} public: EinAus() {status=Normal;} public: int Nurdateien() {return (status==NurDateien ? 1 : 0);} public: void Aendern(Dateistatus estatus) {status=estatus;} friend EinAus& operator << (EinAus& dies, char ch); friend EinAus& operator << (EinAus& dies, char* s); friend EinAus& operator << (EinAus& dies, int i); friend EinAus& operator << (EinAus& dies, long int i); friend EinAus& operator << (EinAus& dies, unsigned long int i); friend EinAus& operator << (EinAus& dies, short int i); friend EinAus& operator << (EinAus& dies, Manip manip); friend EinAus& operator << (EinAus& dies, short int* monom); friend EinAus& operator >> (EinAus& dies, char& ch); friend EinAus& operator >> (EinAus& dies, char* s); friend EinAus& operator >> (EinAus& dies, int& i); friend EinAus& operator >> (EinAus& dies, unsigned long int& i); }; EinAus InOut; EinAus& operator << (EinAus& dies, Manip manip) { if (dies.status!=Normal) if (manip.eintrag==FLUSH) Ausgabedatei << flush; else Ausgabedatei << endl; if (dies.status!=NurDateien) if (manip.eintrag==FLUSH) cout << flush; else cout << endl; return dies; } EinAus& operator << (EinAus& dies, short int* monom) { // Laenge M vorausgesetzt! int i; for (i=0; i> (EinAus& dies, char& ch) { char hilf; if (dies.status==NurDateien) { Eingabedatei >> ch; Eingabedatei.get(hilf);} else { cin >> ch; cin.get(hilf);} if (dies.status!=Normal) Ausgabedatei << ch << endl; return dies; } EinAus& operator >> (EinAus& dies, char* s) { if (dies.status==NurDateien) Eingabedatei >> s; else cin >> s; if (dies.status!=Normal) Ausgabedatei << s << endl; return dies; } int Zahllesen(istream& datei, int Zeilennummer); EinAus& operator >> (EinAus& dies, int& i) { if (dies.status==NurDateien) i=Zahllesen(Eingabedatei,0); else i=Zahllesen(cin,0); if (dies.status!=Normal) Ausgabedatei << i << endl; return dies; } EinAus& operator >> (EinAus& dies, unsigned long int& i) { if (dies.status==NurDateien) i=Zahllesen(Eingabedatei,0); else i=Zahllesen(cin,0); if (dies.status!=Normal) Ausgabedatei << i << endl; return dies; } int Fehlerbehandlung (int Fehlernummer, int Nummer, char Zeichen, int abbruch) { int i,j; i=1; j=0; cerr << "Error: "; if (Zeichen && Zeichen !='\n') cerr << "Character \"" << Zeichen << "\" "; if (Nummer) cerr << "in line " << Nummer << ' '; switch (Fehlernummer) { case 1: cerr << "First Parameter: name of input file!"; break; case 2: cerr << "Input file cannot be opened"; break; case 3: cerr << "no digit!"; break; case 4: cerr << "occurs twice or: a blank is not allowed!" ; break; case 5: cerr << "too many characters"; break; case 6: if (Zeichen=='\n') cerr << "must contain all generators of B(V)"; else cerr << "has to be generator of B(V)"; break; case 7: cerr << "- line too short"; break; case 8: cerr << "- line too long"; break; case 9: cerr << "has to be generator of the group!"; break; case 11: cerr << "number expected; value of number is set to 0"; break; case 12: cerr << "number too big or too low (minimal value 2, maximal value " << maxN << ')'; break; case 13: cerr << "value 1 (for character) or 2 (bicharacter) nothing else"; break; case 14: cerr << "monomial too long"; break; case 16: cerr << "Output file cannot be opened"; break; case 17: cerr << "wrong (only y/n)"; break; case 18: cerr << "wrong (only A/B/C)"; break; default: cerr << "Other kind of error."; break; } cerr << '\n'; if (abbruch) exit(Fehlernummer); return 0; } void Permutationsausgabe(short int* hilfsfeld) // erzeugt Permutationsangabe aus Ergebnissen der Durchlaeufe von 'ausrechnen' { int i,j; for (i=2; i<=M; i++) for (j=1; j='0' && ch<='9') {i*= 10; i+= (ch-'0');} else { Fehlerbehandlung(3, Zeilennummer, ch, 1);} }; if (ch=='\n' && j == 0) { Fehlerbehandlung(11, Zeilennummer, '\0', 0); i=0; } return i; } void Konvertieren() // konvertiert Ergebnisvektor, damit hoechste Stelle 0 ist. { int i; long int hilf; // Konvertieren des Ergebnisses if ((hilf=ergebnis[N-1])!=0) { ergebnis[N-1]=0; for (i=0; i0) InOut << '+'; if (ergebnis[i]!=0 && (i==0 || ergebnis[i]!=1)) { if (i>0 && ergebnis[i]==-1) InOut << (char) '-'; else InOut << (int) ergebnis[i]; ausgabe=TRUE; } if (i>0 && ergebnis[i]!=0) { InOut << 'q'; if (i>1) InOut << '^' << i; ausgabe=TRUE; } } if (!ausgabe) InOut << '0'; } InOut << Endl; } Boolean EinzelMonomEingabe(short int* ausgabe, short int& i, Boolean punkt) { // i: Laenge des Monoms; Ausgabewert: ob Eingabe mit Punkt beginnt. int nummer; Boolean punkt_kommt_vor=FALSE; char s[maxM+2]; char* zeiger=s; InOut >> s; i=0; if (punkt==TRUE && s[0]=='.') {punkt_kommt_vor=TRUE; zeiger++; } while(i 0) && (Zweitzaehler % 80) == 0) // nicht zu viele Punkte pro Zeile { InOut << Endl; } if (b) { InOut << ZweitMonom << '!' << Endl; } } return b; } int Quadratfinden(short int* Monom) // gibt kleinsten Index aus, an dem in Monom zwei gleiche Faktoren vorkommen // (bzw. Monomlaenge M, wenn es keinen solchen gibt): fuer Monom abc ist // Wert 3, fuer Monom aab ist Wert 1, fuer Monom baa ist Wert 2. { int i; for (i=1; i=M) stufe=Quadratfinden(Monom); if (TrivialWirkung==TRUE && stufe>=M) stufe=Passend(ErstesMonom,Monom); // Abfangen von zu grossem Wert, um Endlosschleife zu vermeiden if (stufe>M) stufe=M; } return TRUE; } void Tabellenausgabe() { int i,j; InOut << "Left generator of B(V) acts on the upper as ...; on the right: values of chi\n "; for (i=0; ipermut[i]=i; hilfg->exponent=0; } else { hilfg=gru[ch]; } for (j=0; jexponent=Zahllesen(datei,zeilennummer); j=T; // Schleifenende! } else if ((nummer=zei[ch])==-1) Fehlerbehandlung(6,zeilennummer,ch,1); else { // doppeltes Zeichen: for (k=0; kpermut[k] == nummer) Fehlerbehandlung(4,zeilennummer,ch,1); // alles richtig: hilfg->permut[j]=nummer; } } } } else { // B(V)-Erzeuger wird zusammengesetzt oder Bicharakterwerte nummer = zei[ch]; if (datei.get(ch) && ch != '\n') { if (Charakter==FALSE && zei[ch]>-1) expo[nummer][zei[ch]]=Zahllesen(datei,zeilennummer++); else { while(ch !='\n') { if (gru[ch]== 0) Fehlerbehandlung(9, zeilennummer, ch,1); else { zwischen=a[nummer]; for (j=0; jpermut[hilfe[j]]; if (Charakter) { expoeinfach[nummer]+= gru[ch]->exponent; expoeinfach[nummer]%= N; } } if (!datei.get(ch)) ch='\n'; // Schleife beenden, falls nichts mehr } } } } } } for (i=0; i<256; i++) if (gru[i]) delete(gru[i]); Tabellenausgabe(); TrivialWirkung=Trivialwirkung(); return 0; } Boolean ausrechnen(const int stufe, short int* alte_erste, int exponent, int stelle, int stelle_in_erstes) // benoetigt Vorbereitung in BLFberechnung // Achtung: aus historischen Gruenden bei ErstMonom Indizierung von 1 bis M, // bei ZweitMonom wie ueblich von 0 bis M-1. // Ergebniswert: TRUE, wenn nicht abgebrochen wegen zu hoher Schrittzahl { const int stufeminuseins=stufe-1; const int stufepluseins=stufe+1; short int neue_erste[maxM]; register short int j,k, RechteGrenze=M+1, NeuesElement=ZweitMonom[stufe], hilf, hilf1, hilf2; // register short int i=0; permutation[stufe]=stelle; { register short int i=0, hilf, hilf3; register short int * p=neue_erste; // Kopieren des ersten Teils von alte_erste auf neue_erste while(i0; j--) { if (j+1>=RechteGrenze) return TRUE; // in diesem Schleifendurchlauf // kann kein Treffer mehr gefunden werden, da RechteGrenze // zu niedrig und RechteGrenze-j monoton faellt. if ((hilf=naechst[NeuesElement][(hilf1=neue_erste[j-1])])0; j--) { if (j+1>=RechteGrenze) return TRUE; // in diesem Schleifendurchlauf // kann kein Treffer mehr gefunden werden, da RechteGrenze // zu niedrig und RechteGrenze-j monoton faellt if (naechst[NeuesElement][(hilf1=neue_erste[j-1])]= AbbruchZahl) { if (AbbruchZahl==1) { // Ausgabe der Permutation InOut << exponent; short int hilfsfeld[maxM+1]; for (k=1; k=AbbruchZahl) { if (AbbruchZahl==1) { // Ausgabe der Permutation InOut << exponent; short int hilfsfeld[maxM+1]; for (k=1; k=0 && naechst[nummer][j]>maxM; j--) naechst[nummer][j]=i; for (j=i+1; j<=maxM+1 && vor[nummer][j]M) return TRUE; // Bilinearform muss 0 sein, da letzte // Stelle des 2. Monoms nicht im ersten vorkommt. if (M==1) { ergebnis[0]=(Zweites[0]==Erstes[0]); return TRUE; } // Monomlaenge mindestens 2: Normales Ausrechnen ZweitMonom=Zweites; // Funktion 'ausrechnen' benoetigt globale Variable 'ZweitMonom' return ausrechnen(1,NULL,0,0,hilf); } void Monomeingabe() { char ch, SpecialMode; short int i; int maxstufe; int j,k, nummer; short int hilfslaenge; // Hilfsvariable: Laenge des 2. Monoms short int hilfszweit[maxM]; // 2. Monom Boolean Schleifenbeginn=TRUE, Schleifenfortfuehrung=FALSE; InOut << "Input of monomials, maximal length " << maxM << '\n'; while (Schleifenbeginn || Schleifenfortfuehrung) { InOut << "First monomial: "; Schleifenbeginn=EinzelMonomEingabe(erstes, M, TRUE); if (Schleifenfortfuehrung==TRUE && M==1) return; if (Schleifenbeginn) InOut << "The following input is considered repeatedly until first monomial has length 1.\n"; if (Schleifenbeginn) Schleifenfortfuehrung=TRUE; if (Schleifenbeginn || !Schleifenfortfuehrung) { InOut << "Second monomial (if of length 1: special modes): "; EinzelMonomEingabe(&zweites[0], hilfslaenge, FALSE); } if (M>1 && hilfslaenge==1) { // Sonderfunktionen: if (Schleifenbeginn || !Schleifenfortfuehrung) { InOut << "Special modes (A: comparison with all monomials\n"; InOut << "B: like A, but with limit\n"; InOut << "C: automatic search for big non-zero monomial): "; InOut >> SpecialMode; if (SpecialMode>='a' && SpecialMode<='c') SpecialMode= 'A'+(SpecialMode-'a'); // Umwandlung in Grossbuchstaben if (SpecialMode<'A' || SpecialMode>'C') Fehlerbehandlung(18,0,SpecialMode,1); } if (SpecialMode=='B' || SpecialMode=='C') { if (Schleifenbeginn || !Schleifenfortfuehrung) { InOut << "Limit of summands: "; InOut >> j; if (j>1 && j<=AbbruchZahl) { AbbruchZahl=j; LimitZahl=1; } if (j>AbbruchZahl) { LimitZahl=j/AbbruchZahl; if (LimitZahl*AbbruchZahl < j) InOut << "changed to " << LimitZahl*AbbruchZahl << '\n'; } } } if (SpecialMode=='A' || SpecialMode=='B') { if (Schleifenbeginn || !Schleifenfortfuehrung) { InOut << "Start value for the second monomial: "; EinzelMonomEingabe(hilfszweit, i, FALSE); if (i zweites; mit letzterem wird weiter gerechnet for (i=0; i< M; i++) zweites[i]=hilfszweit[i]; if (NaechsterWert(M+1, Quadratfrei, FALSE, erstes, &zweites[0]) && BLFberechnung(TRUE, erstes, &zweites[0]) && BLFungleichNull()) { InOut << zweites; Wertausgabe(); } // Berechnen, wie weit 'ausrechnen' gekommen ist. maxstufe=0; while(maxstufe> ch; if (ch!='y' && ch!='Y' && ch!= 'n' && ch!='N') Fehlerbehandlung(17,0,ch,1); if (ch=='n' || ch=='N') Monomlaengenangabe=FALSE; if (!TrivialWirkung) { InOut << "Indicate whether total degree is invariant (y/n)? "; InOut >> ch; if (ch!='y' && ch!='Y' && ch!= 'n' && ch!='N') Fehlerbehandlung(17,0,ch,1); if (ch=='n' || ch=='N') GruppengradNull=FALSE; else GruppengradNull=TRUE; } } while (1) { if (NaechsterWert(M+1, Quadratfrei, TRUE, erstes, erstes) && BLFberechnung(TRUE, erstes, erstes) && BLFungleichNull()) { if (Monomlaengenangabe) InOut << M << ' '; { InOut << erstes; if (GruppengradNull==TRUE && MonomGrad(erstes)) InOut << '.'; Wertausgabe(); } // naechster Wert: M++; if (M>maxM) { InOut << "Monomial too long. End." << Endl; break;} if (Quadratfrei && erstes[M-2]==0) erstes[M-1]=1; else erstes[M-1]=0; } else { if (Fehlversuchsangabe) InOut << ':' << Flush; if (!NaechsterWert(M, Quadratfrei, TRUE, erstes, erstes)) break; } } } } else { if (hilfslaenge!=M) InOut << '0' << Endl; else { BLFberechnung(TRUE, erstes, zweites); Wertausgabe(); } } } } main(int argc, char* argv[]) { char ch; int i,j,k,l; if (argc < 2 || argc > 3) { return Fehlerbehandlung(1,0,'\0',1); } // Test, ob Ausgabedatei vorhanden (fuer Eingaben!) if (argc==3) { Eingabedatei.open(argv[2], ios::in); if (Eingabedatei) { if (Eingabedatei.get(ch) && ch!=' ') InOut.Aendern(NurDateien); else InOut.Aendern(Ausgabedoppelt); Eingabedatei.close(); } else InOut.Aendern(Ausgabedoppelt); Ausgabedatei.open(argv[2], ios::app); if (!Ausgabedatei) {return Fehlerbehandlung(16,0,'\0',1); } } Eingabedatei.open(argv[1], ios::in); if (!Eingabedatei) { return Fehlerbehandlung(2,0,'\0',1); } // Einlesen der Eingabedatei // 1. Zeile: Wert von N N=Zahllesen(Eingabedatei,1); if (N>maxN || N<2) Fehlerbehandlung(12,1,'\0',1); InOut << " Order of the root of unity: " << N << '\n'; // 2. Zeile: Charakter 1 oder Bicharakter 2 if (Eingabedatei.get(ch) && ch!='\n') switch (ch) { case '1': Charakter=TRUE; break; case '2': Charakter=FALSE; break; default: Fehlerbehandlung(13,2,'\0',1); break; } // 2. Zeile: Quadrate Null if (ch!='\n' && Eingabedatei.get(ch) && ch!='\n') switch (ch) { case 'j': case 'J': case 'Y': case 'y': Quadratfrei=TRUE; break; case 'n': case 'N': Quadratfrei=FALSE; break; case ' ': AbbruchZahl=Zahllesen(Eingabedatei,2); ch='\n'; break; default: Fehlerbehandlung(17,2,ch,1); break; } if (Quadratfrei==TRUE) InOut << "Squares of the generators vanish.\n"; // 2. Zeile: Angabe der Schrittanzahl if (ch!='\n' && Eingabedatei.get(ch) && ch!='\n') switch (ch) { case 'j': case 'J': case 'Y': case 'y': Schrittanzahl=TRUE; break; case 'n': case 'N': Schrittanzahl=FALSE; break; case ' ': AbbruchZahl=Zahllesen(Eingabedatei,2); ch='\n'; break; default: Fehlerbehandlung(17,2,ch,1); break; } if (Schrittanzahl==TRUE) InOut << "The number of steps is indicated.\n"; // 2. Zeile: Angabe der Fehlversuche if (ch!='\n' && Eingabedatei.get(ch) && ch!='\n') switch (ch) { case 'j': case 'J': case 'Y': case 'y': Fehlversuchsangabe=TRUE; break; case 'n': case 'N': Fehlversuchsangabe=FALSE; break; case ' ': AbbruchZahl=Zahllesen(Eingabedatei,2); ch='\n'; break; default: Fehlerbehandlung(17,2,ch,1); break; } if (Fehlversuchsangabe==TRUE) InOut << "Information about lengths and aborted computations is given.\n"; // 2. Zeile: einfache Ausgabe if (ch!='\n' && Eingabedatei.get(ch) && ch!='\n') switch (ch) { case 'j': case 'J': case 'Y': case 'y': Einfachausgabe=TRUE; break; case 'n': case 'N': Einfachausgabe=FALSE; break; case ' ': AbbruchZahl=Zahllesen(Eingabedatei,2); ch='\n'; break; default: Fehlerbehandlung(17,2,ch,1); break; } if (Einfachausgabe==TRUE) InOut << "Simple output of values of bilinear form\n"; if (ch!='\n' && Eingabedatei.get(ch)) { if (ch==' ') { AbbruchZahl=Zahllesen(Eingabedatei,2); ch='\n'; } else if (ch!='\n') Fehlerbehandlung(8,2,'\0',1); } if (AbbruchZahl==0) AbbruchZahl=1; if (AbbruchZahl==1) InOut << "Indicate how the first element is shuffled to fit to the second.\n"; else InOut << "Print out a dot for each multiple of " << AbbruchZahl << " computed summands\n"; // 3. Zeile: zei initialisieren for (i=0; i<256; i++) zei[i]=-1; zei[' ']=0; // Leerzeichen darf nicht vorkommen zei['.']=0; // Punkt darf nicht vorkommen InOut << "Generators of B(V): "; while (Eingabedatei.get(ch) && ch != '\n') { if (zei[ch] <0) {zei[ch]=T; erzeuger[T]=ch; T++;} else Fehlerbehandlung(4, 3, ch,1); if (T>maxT) Fehlerbehandlung(5,3, '\0',1); InOut << ch; } InOut << '\n'; // ab 4. Zeile: Gruppen(Eingabedatei); Eingabedatei.close(); // Monomeingabe von Tastatur, Auswertung: if (InOut.Nurdateien()) { Eingabedatei.open(argv[2], ios::in); } Monomeingabe(); }