open-axiom repository from github
/* Released under the Modified BSD license attached to Axiom sources.1* TeX Display Math Mode Line Breaking Program2*3* Author: Robert S. Sutor4*5* Date: 19916*7* Change History:8*9* 01/19/92 RSS Change to use \[ \] instead of $$ $$10*11* 09/01/92 RSS Format and fix =-.12*13* Operation:14*15* This program reads standard input and writes to standard output. Display math16* mode starts with \[ at the beginning of a line and ends with \]. All lines17* not in display math mode are simply printed on standard output. The18* expressions in display math mode are broken so that they fit on a page19* better (line breaking).20*21* The array stuff is being converted to use the array node type.22*23* Restrictions:24*25* 1. Assume \[ and \] start in column 1 and are the only things on the lines.26*27* 2. Comments in display math mode are not preserved.28*29* 3. This is meant to deal with output from the AXIOM computer algebra system.30* Unpredictable results may occur if used with hand-generated TeX code or31* TeX code generated by other programs.32*/3334/*35* Include files and #defines.36*/37#include "useproto.h"38#include <ctype.h>39#include <stdio.h>40#include <stdlib.h>41#include <string.h>4243#define MATHBUFLEN 8*819244#define MAXMATHTOKEN 8045#define MAXCHARSINLINE 6046#define FATDELIMMULT 24748#ifndef max49#define max(a,b) (((a) > (b)) ? (a) : (b))50#endif51#ifndef min52#define min(a,b) (((a) < (b)) ? (a) : (b))53#endif5455#define TRUE 156#define FALSE 05758#define STRCHREQ(str,char) (str[0] == char)5960/*61* Type declarations.62*/6364enum nodeTypes {65N_NODE,66N_TEXT,67N_ARRAY68};6970typedef struct listNodeStruct {71struct listNodeStruct *nextListNode;72struct listNodeStruct *prevListNode;73enum nodeTypes nodeType;74long width;75long realWidth;76union {77char *text;78struct listNodeStruct *node;79struct arrayNodeStruct *array;80} data;81} listNode;8283typedef struct arrayNodeStruct {84int cols;85listNode *argsNode;86listNode *entries; /* linked list of nodes, each pointing to a87* row */88} arrayNode;899091/*92* Global Variables.93*/9495char line[1024];96char blanks[] = " ";97char lastPrinted = '\0';98int indent = 0;99char mathBuffer[MATHBUFLEN], mathToken[MAXMATHTOKEN];100char bufout[2*MATHBUFLEN];101int lineLen, mathBufferLen, mathBufferPtr, mathTokenLen;102listNode *mathList;103int charsOut, fatDelimiter;104int maxLineWidth = 4500; /* 4.5 inches * 1000 */105int maxLineSlop = 0; /* 0.0 inches * 1000 */106int charTable[256];107int avgCharWidth;108int spaceWidths[5], extraOverWidth;109int arrayDepth = 0, arrayMaxDepth = 3;110int charMultNum, charMultDenom;111int sinWidth, cosWidth, tanWidth, erfWidth;112long outLineNum = 0L;113114/*115* Function Prototypes.116*/117#ifndef _NO_PROTO118void arrayTooDeep();119int breakFracProd(listNode *, long, char *, char *, int);120int breakFunction(listNode *, long);121int breakList(listNode *, long);122int breakMathList(listNode *, long);123int breakNumber(listNode *, long);124int breakPMEB(listNode *, long);125int breakParen(listNode *, long);126int bufferMathLines();127void buildMathList();128int charWidth(char);129void computeNodeWidth(listNode *);130long computeWidth(listNode *);131void displaySplitMsg(char *, int);132void error(char *, char *);133void freeMathList(listNode *);134void getOptions(int, char **);135void initCharTable();136listNode *insertStringAfter(char *, listNode *);137listNode *insertStringAtBack(char *, listNode *);138listNode *insertStringAtFront(char *, listNode *);139int newLineIfNecessary(int);140listNode *newListNode(enum nodeTypes);141int nextMathToken();142int printChar(char);143int printMathList(listNode *, int);144int printString(char *);145void putcBuf(char, char []);146void putsBuf(char [], char []);147void resetCharMults();148listNode *string2NodeList(char *, listNode *);149void ttCharMults();150#else151void arrayTooDeep();152int breakFracProd();153int breakFunction();154int breakList();155int breakMathList();156int breakNumber();157int breakPMEB();158int breakParen();159int bufferMathLines();160void buildMathList();161int charWidth();162void computeNodeWidth();163long computeWidth();164void displaySplitMsg();165void error();166void freeMathList();167void getOptions();168void initCharTable();169listNode *insertStringAfter();170listNode *insertStringAtBack();171listNode *insertStringAtFront();172int newLineIfNecessary();173listNode *newListNode();174int nextMathToken();175int printChar();176int printMathList();177int printString();178void putsBuf();179void putcBuf();180void resetCharMults();181listNode *string2NodeList();182void ttCharMults();183#endif184185186/*187* Function Definitions.188*/189190int191#ifndef _NO_PROTO192texbreak(char bufinp[])193#else194texbreak(bufinp)195char bufinp[];196#endif197{198initCharTable();199strcpy(bufout,"");200mathBufferLen=strlen(bufinp);201if (mathBufferLen > MATHBUFLEN) {202fprintf(stderr, "mathBuffer too small. Need: %d\n", mathBufferLen);203return 0;204};205mathBufferPtr = 0;206strcpy(mathBuffer,bufinp);207/* fprintf(stderr, "bufinp: %s\n", mathBuffer); */208fatDelimiter = 1;209arrayDepth = 0;210mathList = newListNode(N_NODE);211buildMathList(mathList);212resetCharMults();213computeWidth(mathList);214/* if (mathList->width > maxLineWidth)215fprintf(stderr, "Width = %ld units, Output line = %ld.\n", mathList->width, outLineNum); */216breakMathList(mathList, maxLineWidth);217charsOut = 0;218printMathList(mathList->data.node, TRUE);219freeMathList(mathList);220return 1;221}222223/*224* breakFracProd:225*226* Arguments:227*228* n : the starting node at which we are to try to break229*230* lineWidth : the maximum width of a line231*232* match : either "\\over" or "\\ "233*234* label : either "quotient" or "product"235*236* paren : add parentheses (TRUE or FALSE)237*238*239* Returns: TRUE or FALSE, depending on whether the expression was broken240*241*242* Function: Tries to break up a quotient t1 \over t2 or a product t1 \ t2 by243* splitting and parenthesizing the numerator and/or the denominator.244*/245246int247#ifndef _NO_PROTO248breakFracProd(listNode * n, long lineWidth, char *match, char *label, int paren)249#else250breakFracProd(n,lineWidth,match,label,paren)251listNode * n;252long lineWidth;253char *match;254char *label;255int paren;256#endif257{258259listNode *rootNode, *lastNode, *t1, *t2;260int ok;261long workWidth1, workWidth2;262263if (n->nodeType != N_NODE)264return FALSE;265266rootNode = n;267268ok = FALSE;269t1 = n = rootNode->data.node;270n = n->nextListNode;271if (n) {272if ((n->nodeType == N_TEXT) &&273(0 == strcmp(n->data.text, match))) {274t2 = n->nextListNode;275if (t2 && (NULL == t2->nextListNode))276ok = TRUE;277}278}279280displaySplitMsg(label, ok);281282if (ok) {283284/* for products, determine rough widths for the two factors */285286if (0 == strcmp(label, "product")) {287computeNodeWidth(t1);288computeNodeWidth(t2);289workWidth1 = lineWidth - charWidth(' ');290291if (workWidth1 / 2 > t1->realWidth) {292workWidth2 = workWidth1 - t1->realWidth;293workWidth1 = t1->realWidth;294}295else if (workWidth1 / 2 > t2->realWidth) {296workWidth1 = workWidth1 - t2->realWidth;297workWidth2 = t2->realWidth;298}299else300workWidth1 = workWidth2 = workWidth1 / 2;301302if (paren) {303if (t1->realWidth > workWidth1)304workWidth1 = workWidth1 - 4 * FATDELIMMULT * charWidth('(');305if (t2->realWidth > workWidth2)306workWidth2 = workWidth2 - 4 * FATDELIMMULT * charWidth('(');307}308}309else /* "quotient" */310workWidth1 = workWidth2 =311lineWidth - paren * 4 * FATDELIMMULT * charWidth('(');312313if ((t1->nodeType == N_NODE) && (t1->realWidth > workWidth1)) {314t1->width = t1->realWidth;315316if (breakMathList(t1, workWidth1) && paren) {317/* insert the \left( */318lastNode = insertStringAtFront("\\left(", t1);319320while (lastNode->nextListNode)321lastNode = lastNode->nextListNode;322323/* insert the \right) */324insertStringAtBack("\\right)", lastNode);325}326}327328if ((t2->nodeType == N_NODE) && (t2->realWidth > workWidth2)) {329t2->width = t2->realWidth;330331if (breakMathList(t2, workWidth2) && paren) {332/* insert the \left( */333lastNode = insertStringAtFront("\\left(", t2);334335while (lastNode->nextListNode)336lastNode = lastNode->nextListNode;337338/* insert the \right) */339insertStringAtBack("\\right)", lastNode);340}341}342343return TRUE;344}345return FALSE;346}347348349int350#ifndef _NO_PROTO351breakFunction(listNode * n, long lineWidth)352#else353breakFunction(n,lineWidth)354listNode * n;355long lineWidth;356#endif357{358listNode *rootNode, *tmpNode, *lastNode, *t1, *t2, *t3;359int ok = FALSE;360long workWidth, maxWidth = 0;361362if (n->nodeType != N_NODE)363return FALSE;364365n = n->data.node;366367if (n->nodeType == N_NODE)368return FALSE;369370if ((0 == strcmp(n->data.text, "\\sin")) ||371(0 == strcmp(n->data.text, "\\cos")) ||372(0 == strcmp(n->data.text, "\\tan")) ||373(0 == strcmp(n->data.text, "\\log")) ||374(0 == strcmp(n->data.text, "\\arctan")) ||375(0 == strcmp(n->data.text, "\\erf"))) {376computeNodeWidth(n);377ok = TRUE;378}379380displaySplitMsg("function", ok);381382if (ok) {383t2 = newListNode(N_NODE);384t2->data.node = n->nextListNode;385t2->prevListNode = n;386n->nextListNode = t2;387ok = breakMathList(t2, lineWidth - n->realWidth);388}389390return ok;391}392393/*394* breakList:395*396* Arguments:397*398* n : the starting node at which we are to try to break399*400* lineWidth : the maximum width of a line401*402*403* Returns: TRUE or FALSE, depending on whether the expression was broken404*405*406* Function: Tries to split an expression that is bracketed by \left[ and407* \right] (or \left\{ and \right\} and contains at least one comma.408*/409410int411#ifndef _NO_PROTO412breakList(listNode * n, long lineWidth)413#else414breakList(n,lineWidth)415listNode * n;416long lineWidth;417#endif418{419listNode *rootNode, *tmpNode, *lastNode, *t1, *t2, *t3;420int ok, comma;421long workWidth, maxWidth = 0;422423if (n->nodeType != N_NODE)424return FALSE;425426rootNode = n;427428t1 = n = rootNode->data.node;429comma = ok = FALSE;430431if ((t1->nodeType == N_TEXT) &&432(0 == strcmp(t1->data.text, "\\left")) &&433(t1->nextListNode) &&434(t1->nextListNode->nodeType == N_TEXT) &&435((0 == strcmp(t1->nextListNode->data.text, "[")) ||436(0 == strcmp(t1->nextListNode->data.text, "\\{")))) {437438t1 = t1->nextListNode->nextListNode;439440/*441* Check for a special case: sometimes the whole body of the list is442* a node. Flatten this, if possible.443*/444445if ((t1->nodeType == N_NODE) &&446(t1->nextListNode->nodeType == N_TEXT) &&447(0 == strcmp(t1->nextListNode->data.text, "\\right"))) {448tmpNode = t1->prevListNode;449t2 = t1->nextListNode;450t3 = t1->data.node;451tmpNode->nextListNode = t3;452t3->prevListNode = tmpNode;453while (t3->nextListNode)454t3 = t3->nextListNode;455t3->nextListNode = t2;456t2->prevListNode = t3;457free(t1);458t1 = tmpNode->nextListNode;459}460461while (t1->nextListNode && !ok) {462if ((t1->nodeType == N_TEXT) &&463(0 == strcmp(t1->data.text, ",")))464comma = TRUE;465else if ((t1->nodeType == N_TEXT) &&466(0 == strcmp(t1->data.text, "\\right")) &&467(t1->nextListNode->nodeType == N_TEXT) &&468((0 == strcmp(t1->nextListNode->data.text, "]")) ||469(0 == strcmp(t1->nextListNode->data.text, "\\}"))) &&470(NULL == t1->nextListNode->nextListNode)) {471ok = comma;472tmpNode = t1->nextListNode;473}474t1 = t1->nextListNode;475}476}477478displaySplitMsg("list", ok);479480if (ok) {481if (arrayDepth >= arrayMaxDepth) {482arrayTooDeep();483return FALSE;484}485486/*487* Create array environment488*/489490lastNode = insertStringAtFront("\\begin{array}{@{}l}\\displaystyle", rootNode);491arrayDepth++;492insertStringAtBack("\\end{array}", tmpNode);493494/*495* Now break at best place short of width. Start after the496* environment begins and after the \left(497*/498499n = lastNode->nextListNode->nextListNode->nextListNode;500501/*502* try to split the first expression if too big503*/504505tmpNode = n->nextListNode;506if (breakMathList(n, lineWidth)) {507workWidth = n->width;508n = tmpNode;509}510else511workWidth = n->width;512maxWidth = workWidth;513514while (n->nextListNode) {515if ((n->nodeType == N_TEXT) &&516((0 == strcmp(n->data.text, ",")) ||517(0 == strcmp(n->data.text, "\\:"))) &&518(workWidth + n->nextListNode->width > lineWidth)) {519maxWidth = max(maxWidth, workWidth);520n = insertStringAfter("\\right. \\\\ \\\\ \\displaystyle \\left.", n);521522/*523* try to split the next expression if too big524*/525526tmpNode = n->nextListNode;527if (breakMathList(n, lineWidth)) {528workWidth = n->width;529n = tmpNode;530}531else532workWidth = n->width;533}534else {535workWidth += n->nextListNode->width;536n = n->nextListNode;537}538}539540rootNode->width = rootNode->realWidth =541rootNode->data.node->width = rootNode->data.node->realWidth =542maxWidth;543arrayDepth--;544545return TRUE;546}547548return FALSE;549}550551/*552* breakNumber:553*554* Arguments:555*556* rootNode : the starting node at which we are to try to break557*558* lineWidth : the maximum width of a line559*560*561* Returns: TRUE or FALSE, depending on whether the expression was broken562*563*564* Function: Tries to break an expression that contains only digits and possibly565* a decimal point.566*/567568int569#ifndef _NO_PROTO570breakNumber(listNode * rootNode, long lineWidth)571#else572breakNumber(rootNode,lineWidth)573listNode * rootNode;574long lineWidth;575#endif576{577int ok = TRUE;578listNode *n, *arrNode, *rowNode, *colNode;579long workWidth, maxWidth = 0;580581if (rootNode->nodeType != N_NODE)582return FALSE;583584n = rootNode->data.node;585while (n && ok) {586if ((n->nodeType == N_TEXT) &&587(n->data.text[1] == '\0') &&588(isdigit(n->data.text[0]) || ('.' == n->data.text[0]))) {589n = n->nextListNode;590}591else592ok = FALSE;593}594595displaySplitMsg("number", ok);596597if (ok) {598if (arrayDepth >= arrayMaxDepth) {599arrayTooDeep();600return FALSE;601}602603arrayDepth++;604arrNode = newListNode(N_ARRAY);605arrNode->data.array->entries = rowNode = newListNode(N_NODE);606arrNode->data.array->cols = 1;607arrNode->data.array->argsNode = newListNode(N_NODE);608string2NodeList("{@{}l}", arrNode->data.array->argsNode);609610n = rootNode->data.node;611computeWidth(n);612maxWidth = workWidth = n->width;613rowNode->data.node = colNode = newListNode(N_NODE);614colNode->data.node = n;615n = n->nextListNode;616617while (n) {618computeWidth(n);619620if (workWidth + n->width > lineWidth) {621maxWidth = max(maxWidth, workWidth);622623/*624* time to start a new row625*/626627n->prevListNode->nextListNode = NULL;628n->prevListNode = NULL;629workWidth = n->width;630rowNode->nextListNode = newListNode(N_NODE);631rowNode = rowNode->nextListNode;632rowNode->data.node = colNode = newListNode(N_NODE);633colNode->data.node = n;634}635else636workWidth += (n->nextListNode) ? n->nextListNode->width :0 ;637638n = n->nextListNode;639}640641rootNode->data.node = arrNode;642rootNode->width = rootNode->realWidth =643arrNode->width = arrNode->realWidth = maxWidth;644arrayDepth--;645646return TRUE;647}648649return FALSE;650}651652void653#ifndef _NO_PROTO654resetWidths(listNode * n)655#else656resetWidths(n)657listNode * n;658#endif659{660if (n) {661n->width = -1;662n->realWidth = 0;663if (n->nodeType == N_NODE)664resetWidths(n->data.node);665resetWidths(n->nextListNode);666}667}668669/*670* breakParen:671*672* Arguments:673*674* n : the starting node at which we are to try to break675*676* lineWidth : the maximum width of a line677*678*679* Returns: TRUE or FALSE, depending on whether the expression was broken680*681*682* Function: Tries to split an expression that is bracketed by left( and \right)683* (e.g., a factor).684*/685686int687#ifndef _NO_PROTO688breakParen(listNode * n, long lineWidth)689#else690breakParen(n,lineWidth)691listNode * n;692long lineWidth;693#endif694{695listNode *tmpNode, *workNode;696int ok = FALSE;697698if (n->nodeType != N_NODE)699goto say_msg;700701tmpNode = n->data.node;702703/*704* check for \left705*/706707if ((tmpNode == NULL) ||708(tmpNode->nodeType == N_NODE) ||709(0 != strcmp(tmpNode->data.text, "\\left")))710goto say_msg;711712/*713* check for '('714*/715716tmpNode = tmpNode->nextListNode;717718if ((tmpNode == NULL) ||719(tmpNode->nodeType == N_NODE) ||720('(' != tmpNode->data.text[0]))721goto say_msg;722723/*724* now move to the end725*/726727tmpNode = tmpNode->nextListNode;728729if (tmpNode != NULL) {730while (tmpNode->nextListNode)731tmpNode = tmpNode->nextListNode;732tmpNode = tmpNode->prevListNode;733}734735/*736* check for \right737*/738739if ((tmpNode == NULL) ||740(tmpNode->nodeType == N_NODE) ||741(0 != strcmp(tmpNode->data.text, "\\right")))742goto say_msg;743744/*745* check for ')'746*/747748tmpNode = tmpNode->nextListNode;749750if ((tmpNode == NULL) ||751(tmpNode->nodeType == N_NODE) ||752(')' != tmpNode->data.text[0]))753goto say_msg;754755ok = TRUE;756757say_msg:758displaySplitMsg("parenthesized expression", ok);759760if (ok) {761762/*763* nest the whole inside if necessary, i.e., there is more than one764* term between the ( and the \right765*/766767if (tmpNode->prevListNode->prevListNode !=768n->data.node->nextListNode->nextListNode) {769workNode = newListNode(N_NODE);770workNode->data.node = n->data.node->nextListNode->nextListNode;771n->data.node->nextListNode->nextListNode = workNode;772tmpNode->prevListNode->prevListNode->nextListNode = NULL;773tmpNode->prevListNode->prevListNode = workNode;774workNode->prevListNode = n->data.node->nextListNode;775workNode->nextListNode = tmpNode->prevListNode;776resetWidths(workNode);777computeWidth(workNode);778}779780return breakMathList(n->data.node->nextListNode->nextListNode,781lineWidth - 4 * FATDELIMMULT * charWidth('('));782}783784return FALSE;785}786787/*788* breakPMEB:789*790* Arguments:791*792* n : the starting node at which we are to try to break793*794* lineWidth : the maximum width of a line795*796*797* Returns: TRUE or FALSE, depending on whether the expression was broken798*799*800* Function: Tries to split an expression that contains only +, -, = or \ as801* operators. The split occurs after the operator.802*/803804int805#ifndef _NO_PROTO806breakPMEB(listNode * n, long lineWidth)807#else808breakPMEB(n,lineWidth)809listNode * n;810long lineWidth;811#endif812{813char *s;814listNode *rootNode, *tmpNode, *lastNode;815int ok, op;816long workWidth, maxWidth = 0;817818if (n->nodeType != N_NODE)819return FALSE;820821if (n->width <= lineWidth + maxLineSlop) /* allow a little slop here */822return FALSE;823824rootNode = n;825tmpNode = n = n->data.node;826ok = TRUE;827op = FALSE;828829while (n && ok) {830if (n->nodeType == N_TEXT) {831s = n->data.text;832if (STRCHREQ(s, '+') || STRCHREQ(s, '-') || STRCHREQ(s, '=') ||833(0 == strcmp(s, "\\ ")))834op = TRUE;835else if ((0 == strcmp(s, "\\left")) ||836(0 == strcmp(s, "\\right")) ||837(0 == strcmp(s, "\\over")) ||838STRCHREQ(s, ',')) {839ok = FALSE;840break;841}842}843tmpNode = n;844n = n->nextListNode;845}846ok = ok & op;847848displaySplitMsg("(+,-,=, )-expression", ok);849850851if (ok) {852if (arrayDepth >= arrayMaxDepth) {853arrayTooDeep();854return FALSE;855}856857/*858* Create array environment859*/860861lastNode = insertStringAtFront("\\begin{array}{@{}l}\\displaystyle", rootNode);862arrayDepth++;863insertStringAtBack("\\end{array}", tmpNode);864865/*866* Now break at best place short of width. Start after the867* environment begins.868*/869870n = lastNode->nextListNode;871872/*873* try to split the first expression if too big874*/875876tmpNode = n->nextListNode;877if (breakMathList(n, lineWidth)) {878workWidth = n->width;879n = tmpNode;880}881else882workWidth = n->width;883maxWidth = workWidth;884885while (n->nextListNode) {886loop_top:887if ((n->nodeType == N_TEXT) &&888(STRCHREQ(n->data.text, '+') || STRCHREQ(n->data.text, '-') ||889STRCHREQ(n->data.text, '=') ||890(0 == strcmp(n->data.text, "\\ "))) &&891(workWidth > 24) && /* avoid - or + on their own line */892(workWidth + n->nextListNode->width > lineWidth)) {893894if ((workWidth < lineWidth / 3) &&895(breakMathList(n->nextListNode, lineWidth - workWidth))) {896n->nextListNode->width = -1;897n->nextListNode->realWidth = 0;898computeNodeWidth(n->nextListNode);899goto loop_top;900}901902/*903* \ means multiplication. Use a \cdot to make this clearer904*/905906if (0 == strcmp(n->data.text, "\\ "))907n = insertStringAfter("\\cdot \\\\ \\\\ \\displaystyle", n);908else909n = insertStringAfter("\\\\ \\\\ \\displaystyle", n);910maxWidth = max(maxWidth, workWidth);911912/*913* try to split the next expression if too big914*/915916tmpNode = n->nextListNode;917if (breakMathList(n, lineWidth)) {918workWidth = n->width;919n = tmpNode;920}921else922workWidth = n->width;923}924else {925workWidth += n->nextListNode->width;926n = n->nextListNode;927}928}929930rootNode->width = rootNode->realWidth =931rootNode->data.node->width = rootNode->data.node->realWidth =932maxWidth;933arrayDepth--;934935return TRUE;936}937938return FALSE;939}940941/*942* breakMathList:943*944* Arguments:945*946* n : the starting node at which we are to try to break947*948* lineWidth : the maximum width of a line949*950*951* Returns: TRUE or FALSE, depending on whether the expression was broken952*953*954* Function: Tries various methods to break the expression up into multiple955* lines if the expression is too big.956*/957958int959#ifndef _NO_PROTO960breakMathList(listNode * n, long lineWidth)961#else962breakMathList(n,lineWidth)963listNode * n;964long lineWidth;965#endif966{967int split = FALSE;968969/*970* Don't do anything if already short enough.971*/972973if (n->width <= lineWidth)974return FALSE;975976/*977* Can't split strings, so just return.978*/979980if (n->nodeType == N_TEXT)981return FALSE;982983blanks[indent] = ' ';984indent += 2;985blanks[indent] = '\0';986987/*988* We know we have a node, so see what we can do.989*/990991/*992* Case 1: a product: t1 \ t2993*/994995if (split = breakFracProd(n, lineWidth, "\\ ", "product", FALSE))996goto done;997998/*999* Case 2: a sequence of tokens separated by +, - or =1000*/10011002if (split = breakPMEB(n, lineWidth))1003goto done;10041005/*1006* Case 3: a fraction of terms: t1 \over t21007*/10081009if (split = breakFracProd(n, lineWidth, "\\over", "quotient", TRUE))1010goto done;10111012/*1013* Case 4: a list of terms bracketed by \left[ and \right] with a comma1014*/10151016if (split = breakList(n, lineWidth))1017goto done;10181019/*1020* Case 5: a list of digits, possibly with one "."1021*/10221023if (split = breakNumber(n, lineWidth))1024goto done;10251026/*1027* Case 6: a parenthesized expression (e.g., a factor)1028*/10291030if (split = breakParen(n, lineWidth))1031goto done;10321033/*1034* Case 7: a function application1035*/10361037if (split = breakFunction(n, lineWidth))1038goto done;10391040done:1041blanks[indent] = ' ';1042indent -= 2;1043blanks[indent] = '\0';10441045return split;1046}10471048void1049#ifndef _NO_PROTO1050buildMathList(listNode * oldNode)1051#else1052buildMathList(oldNode)1053listNode * oldNode;1054#endif1055{1056listNode *curNode, *tmpNode;10571058curNode = NULL;1059while (nextMathToken()) {1060if (mathToken[0] == '}')1061break;1062if (mathToken[0] == '{') {1063tmpNode = newListNode(N_NODE);1064buildMathList(tmpNode);1065}1066else {1067tmpNode = newListNode(N_TEXT);1068tmpNode->data.text = strdup(mathToken);1069}1070if (curNode == NULL) {1071oldNode->data.node = tmpNode;1072}1073else {1074tmpNode->prevListNode = curNode;1075curNode->nextListNode = tmpNode;1076}1077curNode = tmpNode;1078}10791080/*1081* leave with one level of nesting, e.g., {{{x}}} --> {x}1082*/10831084tmpNode = oldNode->data.node;1085while ( tmpNode && (tmpNode->nodeType == N_NODE) &&1086(tmpNode->nextListNode == NULL) ) {1087oldNode->data.node = tmpNode->data.node;1088free(tmpNode);1089tmpNode = oldNode->data.node;1090}1091}10921093void1094#ifndef _NO_PROTO1095computeNodeWidth(listNode * n)1096#else1097computeNodeWidth(n)1098listNode * n;1099#endif1100{1101char *s;1102int i;1103listNode *tmp;11041105if (n->width != -1) /* only = -1 if unprocessed */1106return;11071108n->realWidth = 0;11091110if (n->nodeType == N_TEXT) {1111s = n->data.text;1112if (s[0] == '\\') {1113if (s[2] == '\0') {1114switch (s[1]) {1115case ' ':1116n->width = spaceWidths[0];1117break;1118case ',':1119n->width = spaceWidths[1];1120break;1121case '!':1122n->width = spaceWidths[2];1123break;1124case ':':1125n->width = spaceWidths[3];1126break;1127case ';':1128n->width = spaceWidths[4];1129break;1130default:1131n->width = avgCharWidth;1132}1133n->realWidth = n->width;1134}1135else if ((0 == strcmp(s, "\\displaystyle")) ||1136(0 == strcmp(s, "\\bf")) ||1137(0 == strcmp(s, "\\sf")) ||1138(0 == strcmp(s, "\\tt")) ||1139(0 == strcmp(s, "\\rm")) ||1140(0 == strcmp(s, "\\hbox")) ||1141(0 == strcmp(s, "\\mbox")) ||1142(0 == strcmp(s, "\\overline")) ||1143(0 == strcmp(s, "\\textstyle")) ||1144(0 == strcmp(s, "\\scriptstyle")) ||1145(0 == strcmp(s, "\\scriptscriptstyle"))) {1146n->width = 0;1147}1148else if (0 == strcmp(s, "\\ldots"))1149n->width = 3 * charWidth('.');1150else if (0 == strcmp(s, "\\left")) {1151tmp = n->nextListNode;1152if (tmp->nodeType != N_TEXT)1153error("unusual token following \\left", "");1154n->realWidth = n->width = (tmp->data.text[0] == '.')1155? 01156: charWidth(tmp->data.text[0]);1157tmp->width = 0;1158fatDelimiter = 1;1159}1160else if (0 == strcmp(s, "\\over")) {11611162/*1163* have already added in width of numerator1164*/1165computeNodeWidth(n->nextListNode);1166n->realWidth = extraOverWidth + max(n->prevListNode->width, n->nextListNode->width);1167n->width = n->realWidth - n->prevListNode->width;1168n->nextListNode->width = 0;1169fatDelimiter = FATDELIMMULT;1170}1171else if (0 == strcmp(s, "\\right")) {1172tmp = n->nextListNode;1173if (tmp->nodeType != N_TEXT)1174error("unusual token following \\right", "");1175n->realWidth = n->width = fatDelimiter *1176((tmp->data.text[0] == '.') ? 0 : charWidth(tmp->data.text[0]));1177tmp->width = 0;1178fatDelimiter = 1;1179}1180else if (0 == strcmp(s, "\\root")) {1181computeNodeWidth(n->nextListNode); /* which root */1182n->nextListNode->nextListNode->width = 0; /* \of */1183tmp = n->nextListNode->nextListNode->nextListNode;1184computeNodeWidth(tmp); /* root of */1185n->realWidth = n->width = tmp->width + (avgCharWidth / 2) +1186max(avgCharWidth, n->nextListNode->width);1187n->nextListNode->width = 0;1188tmp->width = 0;1189}1190else if (0 == strcmp(s, "\\sqrt")) {1191computeNodeWidth(n->nextListNode);1192n->realWidth = n->width =1193avgCharWidth + (avgCharWidth / 2) + n->nextListNode->width;1194n->nextListNode->width = 0;1195}1196else if (0 == strcmp(s, "\\zag")) {1197computeNodeWidth(n->nextListNode);1198computeNodeWidth(n->nextListNode->nextListNode);1199n->realWidth = n->width = avgCharWidth + max(n->nextListNode->width,1200n->nextListNode->nextListNode->width);1201n->nextListNode->width = 0;1202n->nextListNode->nextListNode->width = 0;1203fatDelimiter = FATDELIMMULT;1204}1205else if ((0 == strcmp(s, "\\alpha")) ||1206(0 == strcmp(s, "\\beta")) ||1207(0 == strcmp(s, "\\pi"))) {1208n->realWidth = n->width = avgCharWidth;1209}1210else if (0 == strcmp(s, "\\sin"))1211/* should use table lookup here */1212n->realWidth = n->width = sinWidth;1213else if (0 == strcmp(s, "\\cos"))1214n->realWidth = n->width = cosWidth;1215else if (0 == strcmp(s, "\\tan"))1216n->realWidth = n->width = tanWidth;1217else if (0 == strcmp(s, "\\erf"))1218n->realWidth = n->width = erfWidth;12191220/*1221* otherwise just compute length of token after \1222*/1223else {1224n->width = 0;1225for (i = 1; i < strlen(s); i++)1226n->width += charWidth(s[i]);1227n->realWidth = n->width;1228}1229}1230else if (s[1] == '\0')1231switch (s[0]) {1232case '^':1233case '_':1234tmp = n->nextListNode;1235computeNodeWidth(tmp);1236n->width = n->width = tmp->width;1237tmp->width = 0;1238break;1239default:1240n->realWidth = n->width = charWidth(s[0]);1241}1242else {1243n->width = 0;1244for (i = 0; i < strlen(s); i++)1245n->width += charWidth(s[i]);1246n->realWidth = n->width;1247}1248}1249else {1250n->realWidth = n->width = computeWidth(n->data.node);1251}1252}12531254long1255#ifndef _NO_PROTO1256computeWidth(listNode * n)1257#else1258computeWidth(n)1259listNode * n;1260#endif1261{1262long w = 0;12631264while (n != NULL) {1265if (n->width == -1) {1266computeNodeWidth(n);1267w += n->width;1268}1269n = n->nextListNode;1270}1271return w;1272}12731274/*1275* displaySplitMsg:1276*1277* Arguments:1278*1279* s : a string describing the kind of expression we are trying to split.1280*1281* ok : whether we can split it (TRUE or FALSE)1282*1283*1284* Returns: nothing1285*1286*1287* Function: Displays a message on stderr about whether a particular method of1288* line breaking will be successful.1289*/12901291void1292#ifndef _NO_PROTO1293displaySplitMsg(char *s, int ok)1294#else1295displaySplitMsg(s,ok)1296char *s;1297int ok;1298#endif1299{1300/* fprintf(stderr, "%sCan split %s: %s\n", blanks, s, ok ? "TRUE" : "FALSE"); */1301}13021303void1304arrayTooDeep()1305{1306fprintf(stderr, "%s->Array nesting too deep!\n", blanks);1307}13081309void1310#ifndef _NO_PROTO1311error(char *msg, char *insert)1312#else1313error(msg,insert)1314char *msg;1315char *insert;1316#endif1317{1318fputs("Error (texbreak): ", stderr);1319fputs(msg, stderr);1320fputs(insert, stderr);1321fputc('\n', stderr);13221323fputs("% Error (texbreak): ", stdout);1324fputs(msg, stdout);1325fputs(insert, stdout);1326fputc('\n', stdout);1327exit(1);1328}13291330void1331#ifndef _NO_PROTO1332freeMathList(listNode * n)1333#else1334freeMathList(n)1335listNode * n;1336#endif1337{1338listNode *tmpNode;13391340while (n != NULL) {1341if (n->nodeType == N_NODE)1342freeMathList(n->data.node);1343else if (n->nodeType == N_TEXT)1344free(n->data.text);1345else {1346freeMathList(n->data.array->argsNode);1347freeMathList(n->data.array->entries);1348free(n->data.array);1349}1350tmpNode = n->nextListNode;1351free(n);1352n = tmpNode;1353}1354}13551356listNode *1357#ifndef _NO_PROTO1358insertStringAfter(char *s, listNode * n)1359#else1360insertStringAfter(s,n)1361char *s;1362listNode * n;1363#endif1364{13651366/*1367* returns node after inserted string1368*/1369listNode *workNode, *lastNode;13701371workNode = newListNode(N_NODE);1372lastNode = string2NodeList(s, workNode);13731374n->nextListNode->prevListNode = lastNode;1375lastNode->nextListNode = n->nextListNode;1376n->nextListNode = workNode->data.node;1377workNode->data.node->prevListNode = n;13781379free(workNode);1380return lastNode->nextListNode;1381}13821383listNode *1384#ifndef _NO_PROTO1385insertStringAtBack(char *s, listNode * n)1386#else1387insertStringAtBack(s,n)1388char *s;1389listNode * n;1390#endif1391{13921393/*1394* Breaks s up into a list of tokens and appends them onto the end of n.1395* n must be non-NULL.1396*/13971398listNode *workNode, *lastNode;13991400workNode = newListNode(N_NODE);1401lastNode = string2NodeList(s, workNode);1402n->nextListNode = workNode->data.node;1403workNode->data.node->prevListNode = n;1404free(workNode);14051406return lastNode;1407}14081409listNode *1410#ifndef _NO_PROTO1411insertStringAtFront(char *s, listNode * n)1412#else1413insertStringAtFront(s,n)1414char *s;1415listNode * n;1416#endif1417{14181419/*1420* Breaks s up into a list of tokens and appends them onto the front of1421* n. n must be a node.1422*/14231424listNode *workNode, *lastNode;14251426workNode = newListNode(N_NODE);1427lastNode = string2NodeList(s, workNode);1428lastNode->nextListNode = n->data.node;1429n->data.node->prevListNode = lastNode;1430n->data.node = workNode->data.node;1431free(workNode);14321433return lastNode;1434}14351436int1437#ifndef _NO_PROTO1438newLineIfNecessary(int lastWasNewLine)1439#else1440newLineIfNecessary(lastWasNewLine)1441int lastWasNewLine;1442#endif1443{1444if (!lastWasNewLine || (charsOut > 0)) {1445putcBuf('\n', bufout);1446outLineNum++;1447charsOut = 0;1448}1449return TRUE;1450}14511452listNode *1453#ifndef _NO_PROTO1454newListNode(enum nodeTypes nt)1455#else1456newListNode(nt)1457enum nodeTypes nt;1458#endif1459{1460listNode *n;14611462n = (listNode *) malloc(sizeof(listNode));1463n->nextListNode = n->prevListNode = NULL;1464n->nodeType = nt;1465n->width = -1;1466n->realWidth = -1;1467if (nt == N_NODE)1468n->data.node = NULL;1469else if (nt == N_TEXT)1470n->data.text = NULL;1471else {1472n->data.array = (arrayNode *) malloc(sizeof(arrayNode));1473n->data.array->argsNode = NULL;1474n->data.array->entries = NULL;1475n->data.array->cols = 0;1476}1477return n;1478}14791480int1481nextMathToken()1482{148314841485/*1486* Sets mathToken. Returns 1 if ok, 0 if no more tokens.1487*/14881489char curChar, errChar[2];14901491errChar[1] = '\0';1492mathToken[0] = '\0';1493mathTokenLen = 0;14941495/*1496* Kill any blanks.1497*/14981499while ((mathBufferPtr < mathBufferLen) && (mathBuffer[mathBufferPtr] == ' '))1500mathBufferPtr++;15011502/*1503* If at end, exit saying so.1504*/15051506if (mathBufferPtr >= mathBufferLen)1507return 0;15081509mathToken[mathTokenLen++] = curChar = mathBuffer[mathBufferPtr++];15101511if (curChar == '\\') {1512curChar = mathBuffer[mathBufferPtr++];1513switch (curChar) {1514case '\0': /* at end of buffer */1515mathToken[mathTokenLen++] = ' ';1516goto done;1517case '\\':1518case ' ':1519case '!':1520case '#':1521case '$':1522case '%':1523case '&':1524case ',':1525case ':':1526case ';':1527case '^':1528case '_':1529case '{':1530case '}':1531mathToken[mathTokenLen++] = curChar;1532goto done;1533}1534if (isalpha(curChar) || (curChar == '@')) {1535mathToken[mathTokenLen++] = curChar;1536while ((curChar = mathBuffer[mathBufferPtr]) &&1537(isalpha(curChar) || (curChar == '@'))) {1538mathToken[mathTokenLen++] = curChar;1539mathBufferPtr++;1540}1541}1542else {1543errChar[0] = curChar;1544errChar[1] = '\0';1545error("strange character following \\: ", errChar);1546}1547}1548else if (isdigit(curChar)) /* digits are individual tokens */1549;1550else if (isalpha(curChar)) {1551while ((curChar = mathBuffer[mathBufferPtr]) &&1552(isalpha(curChar))) {1553mathToken[mathTokenLen++] = curChar;1554mathBufferPtr++;1555}1556}1557else if (curChar == '"') { /* handle strings */1558while ((curChar = mathBuffer[mathBufferPtr]) &&1559(curChar != '"')) {1560mathToken[mathTokenLen++] = curChar;1561mathBufferPtr++;1562}1563mathToken[mathTokenLen++] = '"';1564mathBufferPtr++;1565}15661567done:1568mathToken[mathTokenLen--] = '\0';15691570/*1571* Some translations.1572*/1573if (0 == strcmp(mathToken, "\\sp")) {1574mathToken[0] = '^';1575mathToken[1] = '\0';1576mathTokenLen = 1;1577}1578else if (0 == strcmp(mathToken, "\\sb")) {1579mathToken[0] = '_';1580mathToken[1] = '\0';1581mathTokenLen = 1;1582}15831584return 1;1585}15861587int1588#ifndef _NO_PROTO1589printChar(char c)1590#else1591printChar(c)1592char c;1593#endif1594{1595if ((charsOut > MAXCHARSINLINE) &&1596isdigit(lastPrinted) && isdigit(c)) {1597putcBuf('\n', bufout);1598outLineNum++;1599charsOut = 0;1600}16011602putcBuf(c, bufout);1603lastPrinted = c;1604charsOut++;16051606/*1607* break lines after following characters1608*/16091610if ((charsOut > MAXCHARSINLINE) && strchr("+- ,_^", c)) {1611putcBuf('\n', bufout);1612outLineNum++;1613charsOut = 0;1614lastPrinted = '\0';1615return TRUE;1616}1617return FALSE;1618}16191620int1621#ifndef _NO_PROTO1622printMathList(listNode * n, int lastWasNewLine)1623#else1624printMathList(n,lastWasNewLine)1625listNode * n;1626int lastWasNewLine;1627#endif1628{1629listNode *tmpNode, *rowNode, *colNode;1630int begin, group, r, c;16311632while (n != NULL) {1633if (n->nodeType == N_NODE) {1634lastWasNewLine = printChar('{');1635lastWasNewLine = printMathList(n->data.node, lastWasNewLine);1636lastWasNewLine = printChar('}');1637}1638else if (n->nodeType == N_ARRAY) {1639lastWasNewLine = newLineIfNecessary(lastWasNewLine);1640lastWasNewLine = printString("\\begin{array}");1641lastWasNewLine = printMathList(n->data.array->argsNode, lastWasNewLine);1642lastWasNewLine = printString("\\displaystyle");1643lastWasNewLine = newLineIfNecessary(lastWasNewLine);16441645rowNode = n->data.array->entries; /* node pointing to first row */1646while (rowNode) {1647colNode = rowNode->data.node;1648while (colNode) {1649if (colNode->prevListNode) { /* if not first column */1650lastWasNewLine = printString(" & ");1651lastWasNewLine = newLineIfNecessary(lastWasNewLine);1652}1653lastWasNewLine = printMathList(colNode->data.node, lastWasNewLine);1654colNode = colNode->nextListNode;1655}1656if (rowNode->nextListNode) /* if not last row */1657lastWasNewLine = printString(" \\\\");16581659lastWasNewLine = newLineIfNecessary(lastWasNewLine);1660rowNode = rowNode->nextListNode;1661}16621663lastWasNewLine = printString("\\end{array}");1664lastWasNewLine = newLineIfNecessary(lastWasNewLine);1665}1666else if (n->nodeType == N_TEXT) {16671668/*1669* handle keywords that might appear in math mode1670*/16711672if ((0 == strcmp(n->data.text, "by")) ||1673(0 == strcmp(n->data.text, "if")) ||1674(0 == strcmp(n->data.text, "then")) ||1675(0 == strcmp(n->data.text, "else"))) {1676lastWasNewLine = printString(" \\hbox{ ");1677lastWasNewLine = printString(n->data.text);1678lastWasNewLine = printString(" } ");1679}16801681/*1682* handle things that should be in a special font1683*/16841685else if ((0 == strcmp(n->data.text, "true")) ||1686(0 == strcmp(n->data.text, "false")) ||1687(0 == strcmp(n->data.text, "table")) ||1688(0 == strcmp(n->data.text, "Aleph"))1689) {1690lastWasNewLine = printString(" \\mbox{\\rm ");1691lastWasNewLine = printString(n->data.text);1692lastWasNewLine = printString("} ");1693}16941695/*1696* handle things that should always be on their own line1697*/16981699else if ((0 == strcmp(n->data.text, "\\\\")) ||1700(0 == strcmp(n->data.text, "\\displaystyle"))) {1701lastWasNewLine = newLineIfNecessary(lastWasNewLine);1702lastWasNewLine = printString(n->data.text);1703lastWasNewLine = newLineIfNecessary(lastWasNewLine);1704}17051706/*1707* handle phrases that should be on their own line.1708*/17091710else if ((0 == strcmp(n->data.text, "\\begin")) ||1711(0 == strcmp(n->data.text, "\\end"))) {1712lastWasNewLine = newLineIfNecessary(lastWasNewLine);1713lastWasNewLine = printString(n->data.text);1714begin = (n->data.text[1] == 'b') ? TRUE : FALSE;17151716n = n->nextListNode; /* had better be a node */1717tmpNode = n->data.node;1718lastWasNewLine = printChar('{');1719lastWasNewLine = printMathList(tmpNode, lastWasNewLine);1720lastWasNewLine = printChar('}');17211722if (begin) {17231724/*1725* if array, print the argument.1726*/17271728if (0 == strcmp(tmpNode->data.text, "array")) {1729n = n->nextListNode; /* had better be a node */1730lastWasNewLine = printChar('{');1731lastWasNewLine = printMathList(n->data.node, lastWasNewLine);1732lastWasNewLine = printChar('}');1733}1734}1735lastWasNewLine = newLineIfNecessary(FALSE);1736}17371738/*1739* handle everything else, paying attention as to whether we1740* should include a trailing blank.1741*/17421743else {1744group = 0;1745/* guess whether next word is part of a type */1746if ((strlen(n->data.text) > 2) &&1747('A' <= n->data.text[0]) &&1748('Z' >= n->data.text[0])) {1749group = 1;1750lastWasNewLine = printString("\\hbox{\\axiomType{");1751}1752lastWasNewLine = printString(n->data.text);1753if (group) {1754lastWasNewLine = printString("}\\ }");1755group = 0;1756}1757tmpNode = n->nextListNode;1758if ((n->data.text[0] == '_') ||1759(n->data.text[0] == '^') ||1760(n->data.text[0] == '.') ||1761(n->data.text[0] == '(') ||1762(0 == strcmp(n->data.text, "\\left")) ||1763(0 == strcmp(n->data.text, "\\right")) ||1764(0 == strcmp(n->data.text, "\\%")));1765else if (tmpNode && (tmpNode->nodeType == N_TEXT)) {1766if (((isdigit(n->data.text[0])) &&1767(isdigit(tmpNode->data.text[0]))) ||1768((isdigit(n->data.text[0])) &&1769(',' == tmpNode->data.text[0])) ||1770(tmpNode->data.text[0] == '\'') ||1771(tmpNode->data.text[0] == '_') ||1772(tmpNode->data.text[0] == '^') ||1773(tmpNode->data.text[0] == '.') ||1774(tmpNode->data.text[0] == ')'));1775else1776lastWasNewLine = printChar(' ');1777}1778}1779}1780n = n->nextListNode;1781}1782return lastWasNewLine;1783}17841785int1786#ifndef _NO_PROTO1787printString(char *s)1788#else1789printString(s)1790char *s;1791#endif1792{1793if (s[0]) {1794if (!s[1])1795return printChar(s[0]);1796else {1797putsBuf(s,bufout);1798charsOut += strlen(s);1799}1800}1801return FALSE;1802}18031804void1805#ifndef _NO_PROTO1806putsBuf(char s[], char buf[])1807#else1808putsBuf(s,buf)1809char s[], buf[]1810#endif1811{1812strcat(buf,s);1813}18141815void1816#ifndef _NO_PROTO1817putcBuf(char c,char buf[])1818#else1819putcBuf(c,buf)1820char c, buf[]1821#endif1822{1823char s[2];18241825s[0] = c;1826s[1] = '\0';1827strcat(buf,s);1828}18291830listNode *1831#ifndef _NO_PROTO1832string2NodeList(char *s, listNode * n)1833#else1834string2NodeList(s,n)1835char *s;1836listNode * n;1837#endif1838{18391840/*1841* First argument is string to be broken up, second is a node. Return1842* value is last item in list.1843*/18441845mathBufferPtr = 0;1846strcpy(mathBuffer, s);1847mathBufferLen = strlen(s);1848buildMathList(n);1849n = n->data.node;1850while (n->nextListNode) {18511852/*1853* set width to 0: other funs will have to set for real1854*/1855n->width = 0;1856n = n->nextListNode;1857}1858n->width = 0;1859return n;1860}18611862void1863resetCharMults()1864{18651866/*1867* this is a ratio by which the standard \mit should be multiplied to get1868* other fonts, roughly1869*/18701871charMultNum = charMultDenom = 1;1872}18731874void1875ttCharMults()1876{18771878/*1879* this is a ratio by which the standard \mit should be multiplied to get1880* the \tt font, roughly1881*/18821883charMultNum = 11;1884charMultDenom = 10;1885}18861887int1888#ifndef _NO_PROTO1889charWidth(char c)1890#else1891charWidth(c)1892char c;1893#endif1894{1895return (charMultNum * charTable[c]) / charMultDenom;1896}18971898void1899#ifndef _NO_PROTO1900#else1901#endif1902initCharTable()1903{1904int i;19051906avgCharWidth = 95; /* where 1000 = 1 inch */19071908spaceWidths[0] = 51; /* \ */1909spaceWidths[1] = 25; /* \, */1910spaceWidths[2] = -25; /* \! */1911spaceWidths[3] = 37; /* \: */1912spaceWidths[4] = 42; /* \; */19131914extraOverWidth = 33; /* extra space in fraction bar */19151916sinWidth = 186; /* width of \sin */1917cosWidth = 203;1918tanWidth = 219;1919erfWidth = 185;19201921for (i = 0; i < 256; i++)1922charTable[i] = avgCharWidth;19231924charTable['!'] = 42;1925charTable['"'] = 76;1926charTable['%'] = 126;1927charTable['('] = 59;1928charTable[')'] = 59;1929charTable['+'] = 185;1930charTable[','] = 42;1931charTable['-'] = 185;1932charTable['.'] = 42;1933charTable['/'] = 76;1934charTable['0'] = 76;1935charTable['1'] = 76;1936charTable['2'] = 76;1937charTable['3'] = 76;1938charTable['4'] = 76;1939charTable['5'] = 76;1940charTable['6'] = 76;1941charTable['7'] = 76;1942charTable['8'] = 76;1943charTable['9'] = 76;1944charTable[':'] = 42;1945charTable[';'] = 42;1946charTable['<'] = 202;1947charTable['='] = 202;1948charTable['>'] = 202;1949charTable['A'] = 114;1950charTable['B'] = 123;1951charTable['C'] = 119;1952charTable['D'] = 130;1953charTable['E'] = 121;1954charTable['F'] = 119;1955charTable['G'] = 119;1956charTable['H'] = 138;1957charTable['I'] = 79;1958charTable['J'] = 99;1959charTable['K'] = 140;1960charTable['L'] = 103;1961charTable['M'] = 164;1962charTable['N'] = 138;1963charTable['O'] = 120;1964charTable['P'] = 118;1965charTable['Q'] = 120;1966charTable['R'] = 116;1967charTable['S'] = 102;1968charTable['T'] = 110;1969charTable['U'] = 120;1970charTable['V'] = 122;1971charTable['W'] = 164;1972charTable['X'] = 137;1973charTable['Y'] = 122;1974charTable['Z'] = 114;1975charTable['['] = 42;1976charTable[']'] = 42;1977charTable['a'] = 80;1978charTable['b'] = 65;1979charTable['c'] = 66;1980charTable['d'] = 79;1981charTable['e'] = 71;1982charTable['f'] = 91;1983charTable['g'] = 78;1984charTable['h'] = 87;1985charTable['i'] = 52;1986charTable['j'] = 71;1987charTable['k'] = 84;1988charTable['l'] = 48;1989charTable['m'] = 133;1990charTable['n'] = 91;1991charTable['o'] = 73;1992charTable['p'] = 76;1993charTable['q'] = 73;1994charTable['r'] = 73;1995charTable['s'] = 71;1996charTable['t'] = 55;1997charTable['u'] = 87;1998charTable['v'] = 79;1999charTable['w'] = 113;2000charTable['x'] = 87;2001charTable['y'] = 80;2002charTable['z'] = 77;2003charTable['{'] = 76;2004charTable['|'] = 42;2005charTable['}'] = 76;2006}200720082009