Sindbad~EG File Manager
/* bblock processes a term into a 'bblocked term', suitable for passing
to 'display'
M. Beeson
Original date 6.1.90
modified 10.28.99
1.10.00 added if(!OBJECT(ARG(0,t))) to bblock_minus
3.30.00 changed paren() on '*'
6.17.00 added f != '=' in bblock_function to prevent blocking an equation
as a subscripted function.
6.17.04 removed conditional 16-bit compilation
8.29.04 Modified paren so FRACT as a child of '+' returns 0, not 1
Corrections at line 1549 and 1561
9.4.04 moved paren, my_gcvt, my_gcvt0 to pstring.c in parser.dll.
Removed set_decimalpoint.
10.7.04 modified bblock on FLOOR to get the width right
4.7.06 changed ltoa and itoa to sprintf
8.24.07 added a line for POLYGAMMA
9.2.07 moved include screen.h below include display.h
9.8.11 cast return value of strlen to int for 64-bit compilation
5.8.13 added include stddef.h and removed heaps.h
6.14.13 changed bblock_fraction to use in-line blocking in compound fractions and binomial coefficients
5.15.13 added BERNOULLI to bb()
5.21.13 added EULERNUMBER
6.3.13 modified bblock_fraction to always use in-line blocking in binomial (to fix binomial(-1/2,k), but will also affect binomial(6,4) etc.)
11.5.23 added EXCLUDEENGLISH so this file can link without "english"
11.6.23 added EXCLUDESERIES so it can link without bblock_series.c
11.10.23 moved functor_width and atom_height here from display.c
11.17.23 added assert(0) in bblock_object
11.17.23 modified bblocksum to use pluswidth
11.18.23 modified functor_width for eightpoint, as eightpointheight is now smaller.
11.26.23 introduced get_infixspace() at line 1438 so less space is left in a <= b.
11.26.23 parenwidth and parenspace in bblock_fraction (not charspace)
12.28.23 changed bblock_atom to make .wl smaller on EIGHTPOINT.
12.29.23 modified bblock_fraction to get 1/2 right in exponent
1.8.24 modified bblock_object and bblock_atom setting .wl
1.10.23 corrected bblock_function
1.11.24 changed functor_width
1.11.24 changed bblock_and and bblock_function
1.11.24 changed product_spacing
2.24.24 removed include readinit.h
6.21.24 modified bblock_atom on subscripted atoms
11.24.24 gave get_productspace() a size argument
1.13.25 in bb always allocate space for 5 args for INTEGRAL
2.16.25 removed unused 'flag' in bblock_and
*/
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <stddef.h> /* ptrdiff_t */
#include "terms.h" /* which gets cgraph which gets terms */
#include "display.h"
#include "coord.h" /* defines type coord */
#include "coord.h" /* defines type coord */
#include "display1.h"
#include "algaux.h" /* variablesin */
#include "english.h"
#include "mygcvt.h"
#include "trig.h"
#include "dispfunc.h"
#include "pstring.h" /* paren, my_gcvt, my_gcvt0 */
#include "defns.h"
#include "vaux.h" /* get_currentline */
#ifdef SVG
#include "termtoSVG.h" /* svg_text_width */
#include "textwidth.h"
#endif
#include "islinear.h"
static int extravars(term,term);
static char *compute_subscripted_varname(term expr);
static int bcontains(term t, unsigned short f);
/*______________________________________________________________*/
unsigned short atom_height(unsigned short f , short pointsize)
/* pointsize is TENPOINT or EIGHTPOINT. Return the
height in papyrus coordinates required to display this
atom, which is presumed to be a single character or
a predefined atom. */
{ return (unsigned short)(pointsize == TENPOINT ? 14 : 10);
}
/*_______________________________________________________________*/
int bb(term expr, term *newexpr , short sizein, short op, short dir);
/* 'sizein' is either TENPOINT or EIGHTPOINT, the size of the biggest
type to be used.
expr occurs as the daughter of a term with functor 'op';
'dir' is either RIGHT, LEFT, CENTRAL, NODIR, HI, or LO, telling the
direction of expr as a daughter of op. NODIR occurs only at the
top level when expr is not a daughter node. In that case 'op'
is passed as 0. */
#define US unsigned short
static int bblock_object(term,term *,US);
static int bblock_minus(term,term *,US,US,US);
static int bblock_sum(term,term *,US,US,US);
static int bblock_product(term,term *,US,US,US);
static int bblock_and(term,term*,US,US,US);
static int bblock_power(term,term *,US,US,US);
static int bblock_fraction(term,term *,US,US,US);
static int bblock_function(term,term *,US,US,US);
static int bblock_minus(term,term *,US,US,US);
static int interval_as_and(term); /* copied from prover.c */
static int bblock_binder(term,term *, US,US,US);
static int force_paren(unsigned short,unsigned short);
static int infix_exp(term, term);
/*_______________________________________________________________________*/
static int force_paren(unsigned short op1, unsigned short op2)
/* decides whether a product of two terms with operators op1 and op2
needs to have the op1 term parenthesized by force, despite what paren says.
Return 1 to force parentheses; return 0 to not force them..
*/
{ if (op2 == END)
return 0;
if (op1 == ATOM)
return 0;
if (op1 == SQRT || op1 == ROOT)
return 0; /* as in 3 \sqrt 7 y or \sqrt 3 i */
if (op2 == ATOM)
return (op1 < PREFIXBOUND); /* (ln 2) x */
if ((op1 < PREFIXBOUND) && (op2 >= PREFIXBOUND))
return 1; /* as in sin x sin y */
return 0;
}
/*_____________________________________________________________*/
int bblock( term expr, term *newexpr)
/* given expr, return the corresponding bblocked term in newexpr */
/* the top-level call */
/* return value 1 means out of memory;
return value 2 means term is too big to bblock */
{
return bb(expr,newexpr,TENPOINT,0,NODIR);
}
/*_____________________________________________________________*/
int bb(term expr, term *newexpr , short sizein, short op, short dir)
/* newexpr must point to something that will last long enough
to be displayed; in other words you call bblock(expr,&blockedterm)
and then display blockedterm */
/* further documented above */
/* return value as for bblock */
{ unsigned short f,arity;
int err,color;
block b;
term vlist;
color = COLOR(expr);
if (ISATOM(expr))
{ if(FUNCTOR(expr)==ILLEGAL) /* a void term, should not display */
/* this is used to create gaps in matrix display */
/* which is in turn used to display 'cases' */
/* it is also used in 'interval notation' a < x < b,
which is bblocked as a<x & void < b */
{ *newexpr = make_term(ILLEGAL,1);
b.w = 0;
b.h = 12; // less than a variable or number
b.wl = 6; // doesn't matter what we put here
b.bra = b.rightbra = b.leftbra = b.size = b.extra = 0;
b.size = sizein;
ARGREP(*newexpr,0,*(term *)(&b));
return 0;
}
if(FUNCTOR(expr) == 0)
{ assert(0); /* if we attempt to display garbage,
we will bomb out here. */
}
*newexpr = make_term(0,2);
SETCOLOR(*newexpr,color);
return bblock_atom(expr, newexpr,sizein); /* op, dir not used */
}
if (OBJECT(expr))
{ *newexpr = make_term(0,3);
/* one for the block, one for the printstring, one for the
original data. */
SETCOLOR(*newexpr,color);
return bblock_object(expr,newexpr,sizein); /* op, dir not used */
}
arity = ARITY(expr);
f = FUNCTOR(expr);
if(f==MATRIXINVERSE)
{ term inverse, minusone;
/* can't use global minusone or make_power, since these
aren't part of display.dll */
minusone = make_term('-', 1);
ARGREP(minusone,0,make_int(1L));
inverse = make_term('^',2);
ARGREP(inverse,0,ARG(0,expr));
ARGREP(inverse,1,minusone);
*newexpr = make_term('^',3);
return bblock_power(inverse,newexpr,sizein,op,dir);
}
if(f == LINEARSYSTEM)
*newexpr = make_term(LINEARSYSTEM,(unsigned short)(ARITY(ARG(0,expr))+2));
/* ARG(0,expr) is the linear system itself */
else if(f == CASES)
*newexpr = make_term(MATRIX,(unsigned short)(arity+2));
else if(f==MATRIX || f==VECTOR )
*newexpr = make_term(MATRIX,(unsigned short)(arity+2));
else if (f == AND && interval_as_and(expr))
{ *newexpr = make_term(AND,3);
}
else if (
(f==AND && get_anddisplay()) || /* prepare for matrix display of AND */
(f==OR && get_ordisplay())
)
*newexpr = make_term(MATRIX,(unsigned short)(arity+2));
else if (f == INTEGRAL)
{ *newexpr = make_term(f,5);
// use enough space for a definite integral
// necessary because definite integrals are bblocked first as indefinite integrals.
SETFUNCTOR(*newexpr, f, arity+1);
}
else
*newexpr = make_term(f,(unsigned short)(arity+1));
newexpr->info = expr.info;
SETCOLOR(*newexpr,color);
switch(f)
{ case '-' : return bblock_minus(expr,newexpr,sizein,op,dir);
case '+' : return bblock_sum(expr,newexpr,sizein,op,dir);
case '*' : return bblock_product(expr,newexpr,sizein,op,dir);
case '^' : return bblock_power(expr,newexpr,sizein,op,dir);
case '/' : return bblock_fraction(expr,newexpr,sizein,op,dir);
case MATRIX: return bblock_matrix(0,expr,newexpr,sizein,op,dir);
case CIS: assert(0);
case VECTOR: /* change vector(a,b) to matrix(vector(a),vector(b)) */
{ term matrix;
term arg;
int i;
matrix=make_term(MATRIX,arity);
for(i=0;i<arity;i++)
{ ARGREP(matrix,i,make_term(VECTOR,1));
arg = ARG(i,expr);
ARGREP(ARG(i,matrix),0,arg);
}
err= bb(matrix,newexpr,sizein,op,dir);
for(i=0;i<arity;i++)
RELEASE(ARG(i,matrix));
/* freed terms were allocated above by make_term(VECTOR,1) */
RELEASE(matrix); /* allocated above at make_term */
return err;
}
case ABSFUNCTOR: return bblock_abs(expr,newexpr,sizein); /* op, dir not used */
case DET: SETFUNCTOR(expr,ABSFUNCTOR,ARITY(expr));
SETFUNCTOR(*newexpr,ABSFUNCTOR,ARITY(*newexpr));
err = bblock_abs(expr,newexpr,sizein);
/* determinants are displayed as absolute values;
matrices don't get brackets as children of ABSFUNCTOR */
SETFUNCTOR(*newexpr,DET,ARITY(*newexpr));
return err;
case DIFF: if (arity == 2)
return bblock_diff(expr,newexpr,sizein,op,dir);
if (arity == 3)
return bblock_hi_diff(expr,newexpr,sizein,op,dir);
case INTEGRAL:
if (arity == 2)
return bblock_integral(expr,newexpr,sizein,op,dir);
if (arity == 4)
return bblock_def_integral(expr,newexpr,sizein,op,dir);
case LINEARSYSTEM: /* the input has an AND term for its zero-th arg,
and the second arg is another AND, listing the atoms to
be considered variables. The first arg is the system
to be displayed with its variables lined up. */
return bblock_linear_system(expr,newexpr,sizein,op,dir);
case AND :
if(get_anddisplay() == 0 || interval_as_and(expr))
{
return bblock_and(expr,newexpr,sizein,op,dir);
}
else if(
LINEUP(expr) /* this specifies to line up the variables */
&& !is_linear_system(expr,get_currentline(),&vlist)
/* is_linear_system returns 0 for success */
)
/* send a term with functor LINEARSYSTEM to be displayed */
{ term system = make_term(LINEARSYSTEM,2);
ARGREP(system,0,expr);
ARGREP(system,1,vlist);
expr = system;
return bb(system, newexpr,sizein, op, dir);
}
else /* display AND as a column matrix */
return bblock_as_matrix(expr,newexpr,sizein,op,dir);
case OR :
if(get_ordisplay()==0)
return bblock_and(expr,newexpr,sizein,op,dir);
/* "and" is not a mistake */
else /* display OR as a column matrix */
return bblock_as_matrix(expr,newexpr,sizein,op,dir);
case LIMIT:
return bblock_limit(expr,newexpr,sizein,op,dir);
case SQRT:
return bblock_sqrt(expr,newexpr,sizein,op,dir);
case SUM :
if(ARITY(expr) == 4)
return bblock_indexed_sum(expr,newexpr,sizein,op,dir); /* using sigma notation */
else
{ err = bblock_indexed_sum(expr,newexpr,sizein,op,dir);
if(err)
return 1;
#ifndef EXCLUDESERIES
err = bblock_series(expr,ARGPTR(*newexpr)+5,sizein,op,dir); /* using ... notation */
if(err)
return 1;
#endif
b = GETBLOCK(ARG(5,*newexpr));
/* set the block of newexpr to that of the sum */
/* but if the sum has parentheses, remove the parens
from the series term, otherwise we get double
parentheses */
b.leftbra = b.rightbra = 0;
SETBLOCK(*newexpr,b);
return 0;
}
case PRODUCT:
return bblock_indexed_sum(expr,newexpr,sizein,op,dir);
case ROOT:
return bblock_root(expr,newexpr,sizein,op,dir);
case BESSELJ: /* fall-through */
case BESSELY:
case BESSELI:
case BESSELK:
case POLYGAMMA:
case BERNOULLI:
case EULERNUMBER:
case CONSTANTOFINTEGRATION:
return bblock_subscripted_function(expr,newexpr,sizein,op,dir);
case LOGB:
return bblock_logb1(expr,newexpr,sizein,op,dir);
case FLOOR:
{ block b;
SETFUNCTOR(expr,ABSFUNCTOR,1);
err = bb(expr,newexpr,sizein,op,dir);
b = GETBLOCK(*newexpr);
b.leftbra = b.rightbra = 0;
b.size = sizein;
b.w -= 2 + 2 * get_parenspace(b.size); /* put in by bblock_abs */
b.w += 2 * get_floorwidth(); /* needed to display floor characters */
if(b.h >= get_charspace())
b.h += get_underfloor(); /* floor symbols in floor(g) are right at the bottom of the bounding boc of g, no space below. */
SETBLOCK(*newexpr,b);
SETFUNCTOR(*newexpr,FLOOR,1);
return err;
}
case BINOMIAL:
return bblock_fraction(expr,newexpr,sizein,op,dir);
case IF:
return 1; /* FINISH THIS */
case CASES:
return bblock_cases(expr,newexpr,sizein,op,dir);
case PR:
return bblock_prime(expr,newexpr,sizein,op,dir);
case NOT:
return bblock_minus(expr,newexpr,sizein,op,dir);
case ':':
return bblock_type(expr,newexpr,sizein,op,dir);
case EVAL: /* eval terms are of the form eval(u,x,lo,hi).
If u contains variables other than x, we want
to replace lo by x=lo before displaying. We
call evalat to avoid modifying the original
copy of expr. */
if(extravars(ARG(0,expr),ARG(1,expr)))
{ term y;
term eqn = make_term('=',2);
ARGREP(eqn,0,ARG(1,expr));
ARGREP(eqn,1,ARG(2,expr));
y = make_term(EVAL,4);
ARGREP(y,0,ARG(0,expr));
ARGREP(y,1,ARG(1,expr));
ARGREP(y,2,eqn);
ARGREP(y,3,ARG(3,expr));
expr = y;
}
/* Now proceed to bblock the term */
SETFUNCTOR(expr,INTEGRAL,4); /* just temporarily */
/* an eval term requires the same space approximately
as a definite integral, because the eval bar
replaces the integral sign (it comes at the end
instead of the beginning, but no matter). But,
a definite integral terminates with a 'dx', which
has no analogue in an eval term. Therefore we
have to subtract for that. */
err = bblock_def_integral(expr,newexpr,sizein,op,dir);
SETFUNCTOR(*newexpr,EVAL,5); /* restore the functor */
SETFUNCTOR(expr,EVAL,4);
b = GETBLOCK(*newexpr);
b.wl -= get_upperlimy();
b.h = (coord)( b.h - get_lowerlimy() - get_upperlimy());
/* in eval terms, the lower limits are not
vertically displaced like in definite integrals */
b.w -= 2*get_charspace() - get_productspace(b.size);
/* for the ' dx' at the end of a
definite integral, which doesn't occur with EVAL */
term lo = ARG(3,*newexpr);
term hi = ARG(4,*newexpr);
unsigned long for_integral = MAXIMUM( WIDTH(lo)+get_lowerlimx(), WIDTH(hi)+ get_upperlimx());
unsigned long for_eval = MAXIMUM(WIDTH(lo), WIDTH(hi)) + get_upperlimx();
b.w = b.w - for_integral + for_eval;
if(paren(op,EVAL,dir) && !paren(op,INTEGRAL,dir))
{ b.bra=PARENS;
b.leftbra=b.rightbra=1;
b.w += 2*get_parenspace(sizein) + 2*get_parenwidth(sizein);
}
SETBLOCK(*newexpr,b);
return err;
case LAMBDA: /* fall through */
case FORALL:
case EXISTS: /* fall through */
return bblock_binder(expr,newexpr,sizein,op,dir);
default:
return bblock_function(expr,newexpr,sizein,op,dir);
}
}
/*____________________________________________________________________*/
/* space has been allocated before calling this for 2 args of *newexpr */
/* ISATOM(expr) is checked before calling this */
/* An atom is somewhat different from other terms;
expr.args is a pointer not to the base of an array of terms, but to
a long (a value in the value list, 0.0 by default, or whatever value
has been assigned).
The bblocked atom will be a term with two args. The first one as
usual will be interpreted as a block. The second one
will be a string to be written. Note, it will be a (char *).
Although the bblocked term will actually have arity two, i.e. space will
be allocated for two args, we return it with arity 0, so 'display' can
tell it came from an atom. */
/* The height and width of a predefined or single-letter atom will
be found from precomputed metric information using atom_height and
atom_width. Often the font leaves a lot of internal leading space,
which we eliminate by using an atom_height smaller than 16. */
/* Atoms such as x3 will be printed with subscripts */
int bblock_atom(term expr,term *newexpr,US sizein)
{ block b;
char *printstring;
int charheight = sizein == TENPOINT ? get_charspace(): get_eightpointheight();
unsigned short f = FUNCTOR(expr);
if(f == PSEUDOATOM)
{ printstring = (char *) ARGPTR(expr);
b.w = svg_text_width(printstring, sizein);
b.h = (coord) charheight;
}
else if(f == FALSEFUNCTOR)
{
if(get_falsedisplay())
{
#ifdef EXCLUDEENGLISH
printstring = "No solution";
#else
printstring = (char *) english(411); /* No solution */
#endif
#ifdef SVG
b.w = svg_text_width(printstring, sizein);
#else
b.w = text_width(printstring,sizein);
#endif
b.h = (coord) charheight;
}
else
{
#ifdef EXCLUDEENGLISH
printstring = "false";
#else
printstring = (char *) english(978); /* false */
#endif
b.w = atom_width(f,sizein);
b.h = (coord) charheight;
}
}
else if(('a' <= f && f <= 'z') ||
('A' <= f && f <= 'Z') ||
PREDEFINED_ATOM(f)
)
{ b.w = atom_width(f,sizein);
b.h = atom_height(f,sizein);
printstring = atom_string(expr);
}
else if(SUBSCRIPT(f))
{ unsigned short g = VARNAME(f);
printstring = compute_subscripted_varname(expr);
#ifdef SVG
b.w = svg_text_width(printstring, EIGHTPOINT) + 2;
#else
b.w = text_width(printstring,sizein);
#endif
b.h = atom_height(g,sizein);
}
else
{ printstring = atom_string(expr);
#ifdef SVG
b.w = svg_text_width(printstring, sizein);
#else
b.w = text_width(printstring,sizein);
#endif
b.h =(coord) charheight;
}
if(printstring==NULL)
return 1;
if(sizein == TENPOINT)
b.wl = b.h-7;
else
b.wl = b.h -5;
if(SUBSCRIPT(f))
b.h += get_subscriptspace(); // but don't change waterlevel
b.size = sizein;
b.extra = 0;
b.leftbra=b.rightbra = 0; /* don't need parens */
SETBLOCK(*newexpr,b);
*((char **)(ARGPTR(*newexpr) + 1)) = printstring;
newexpr->info = expr.info;
SETFUNCTOR(*newexpr,f,0); /* see comments above */
return 0;
}
/*_____________________________________________________________________*/
/* OBJECT(expr) is checked before calling this */
/* space has been allocated before calling this for THREE
args of *newexpr. */
/* like for atoms, we make the second arg of newexpr be a (char *)
pointing to the (base of the) string to be written; then we
throw the actual data in at the third argument (second numbering
from zero) */
static int bblock_object(term expr,term *newexpr,US sizein)
{ block b;
char *printstring;
char buffer[36];
US type = TYPE(expr);
switch(type)
{ case INTEGER:
snprintf(buffer,sizeof(buffer),"%ld",INTDATA(expr));
printstring= callocate((int) strlen(buffer)+1,sizeof(char));
if(printstring==NULL)
nospace();
strcpy(printstring,buffer);
b.w = svg_text_width(printstring, sizein);
break;
case DOUBLE:
my_gcvt(DOUBLEDATA(expr),get_precision(),buffer);
printstring= callocate((int) strlen(buffer)+1,sizeof(char));
if(printstring==NULL)
nospace();
strcpy(printstring,buffer);
b.w = svg_text_width(printstring, sizein);
break;
case BIGNUM:
if(AE(expr)==0) /* then this is a "fake bignum" */
/* pointing to a display string already */
{ printstring = (char *) ARGPTR(expr);
break;
}
printstring = bignum_string(BIGNUMDATA(expr),get_separator());
if(printstring==NULL)
nospace();
b.w = bignum_text_width(printstring, sizein ,get_separator());
break;
default:
assert(0); // that's all the possible object types
}
b.h = atom_height('h',sizein);
b.size = sizein;
if(sizein == TENPOINT)
b.wl = b.h - 7;
else
b.wl = b.h - 5;
b.leftbra = b.rightbra = 0;
b.extra = 0;
SETBLOCK(*newexpr,b);
newexpr->info = expr.info;
/* sets the type info and sets HASARGS bit to zero (it was left 1
by the make_term call that created 3 args for newexpr) */
*((char **)(ARGPTR(*newexpr) + 1)) = printstring;
if(sizeof(bignum) > sizeof(term) ||
sizeof(long) > sizeof(term) ||
sizeof(double) > sizeof(term)
)
assert(0);
memcpy(ARGPTR(*newexpr)+2, ARGPTR(expr),sizeof(term));
/* actually we're copying a bignum (just the head), an int,
or a double. The size of each of these data items must
not exceed sizeof(term), a fact which is also
relied upon in the basic representation of objects */
return 0;
}
/*____________________________________________________________________*/
/* space has been allocated before calling this for the args of *newexpr */
/* also used for terms with functor NOT */
static int bblock_minus(term expr,
term *newexpr,
unsigned short sizein,
unsigned short op,
unsigned short dir
)
{ block b;
term arg;
int pneeded, err;
unsigned long width;
term *newargs; /* pointer to dynamic array of terms for new bblocked args*/
newargs = ARGPTR(*newexpr);
err = bb(ARG(0,expr),newargs + 1,sizein,'-',CENTRAL);
if(err)
return err;
arg = *(newargs+1);
b.h = HEIGHT(arg);
b.wl = LEVEL(arg);
b.size = SIZE(arg);
b.extra = 0;
/* do we need parentheses around this term? */
pneeded = (paren(op,'-',dir) || ((op == '+') && (dir == RIGHT)));
if(pneeded)
{ b.w = WIDTH(arg) + 2*get_parenwidth(sizein) + get_minuswidth(b.size);
/* two parens, one minus sign */
if(!OBJECT(ARG(0,expr)))
b.w += 2*get_parenspace(sizein) + get_unaryminusspace(sizein);
/* space between parens and expr, and after minus sign */
else
b.w += get_parenspace(sizein);
/* (-2) is printed without any extra space after the minus, and
only half as much between the parens and the expression.
*/
if (b.w >= MAXWIDTH - 5*get_charspace())
return 2; /* term too big */
b.leftbra = b.rightbra = 1;
b.bra = PARENS;
}
else
{ width = WIDTH(arg);
if(width >= (MAXWIDTH - get_unaryminusspace(sizein) -get_minuswidth(sizein)))
return 2; /* too big */
width += get_minuswidth(sizein); /* for the minus sign */
width += get_unaryminusspace(sizein);
if (width > MAXWIDTH)
return 2;
b.w = width;
b.leftbra = b.rightbra= 0;
}
SETBLOCK(*newexpr,b);
return 0;
}
/*____________________________________________________________________*/
coord product_spacing(term u, term v)
/* return the space to be left between u and v occurring as
adjacent args of a product (u before v). Just space enough
for a multiplication dot before an object or rational number;
or object to a power, or a factorial of a number.
2 sin x requires productspace between 2 and sin x,
but 2x has just one half productspace
Otherwise no space after an object, except just before a
ROOT term, to avoid confusion between exponents and root indices,
as in 2 root(3,x) versus 2^3 sqrt x.
Otherwise productspace.
The use of this function assumes that no space precedes or
follows a multiplication dot, so if you ever want that, just
increase dotspace and make display center the dot in dotspace.
*/
{ unsigned f = FUNCTOR(v);
if(BB_OBJECT(v) || f == DEG)
return get_dotspace();
if( f == '^' && BB_OBJECT(ARG(1,v)))
return get_dotspace();
if( f == '/' && BB_OBJECT(ARG(1,v)) && BB_OBJECT(ARG(2,v)))
return get_dotspace();
if(BB_OBJECT(u) && f == FACTORIAL && BB_OBJECT(ARG(1,v)))
return get_dotspace(); /* 8 \cdot 7! rather than 87! */
if(BB_OBJECT(u) && ATOMIC(v))
return get_productspace(SIZE(u))/2;
return get_productspace(SIZE(u));
}
/*____________________________________________________________________*/
/* space has been allocated before calling this for the args of *newexpr */
static int bblock_product(term expr,term *newexpr,US sizein, US op, US dir)
{ block b;
int err,i;
unsigned short arity = ARITY(expr);
unsigned long width = 0, height = 0, level=0;
unsigned short leftop,rightop,newdir;
long temp,temp2; /* these need to be signed */
term arg,u;
term *newargs; /* pointer to dynamic array of terms for new bblocked args*/
newargs = ARGPTR(*newexpr);
for(i=1;i<=arity;i++)
{ if(ISATOM(ARG(i-1,expr)))
leftop=ATOM;
else if OBJECT(ARG(i-1,expr))
leftop= NUMBR;
else
leftop = FUNCTOR(ARG(i-1,expr));
if(i < arity)
{ u = ARG(i,expr);
if(OBJECT(u) || FUNCTOR(u) == DEG)
rightop = NUMBR;
else if (FUNCTOR(u) == '^' && OBJECT(ARG(0,u)))
rightop = NUMBR; /* as in 2*3^2 */
else if (FUNCTOR(u) == '^' && ATOMIC(ARG(1,u)) &&
TRIGFUNCTOR(FUNCTOR(ARG(0,u)))
)
rightop = FUNCTOR(ARG(0,u));
else if (FUNCTOR(u) == '/' && INTEGERP(ARG(0,u)) && INTEGERP(ARG(1,u)))
rightop = NUMBR; /* as in 2*(1/2) */
else if ISATOM(ARG(i,expr))
rightop = ATOM;
else
rightop = FUNCTOR(u);
}
else
rightop=END;
if(force_paren(leftop,rightop))
newdir = FORCED;
else if (i==1)
newdir = LEFT;
else if (i< arity)
newdir = CENTRAL;
else
newdir = RIGHT;
err = bb(ARG(i-1,expr),newargs+i,sizein,'*',newdir);
if (err)
return err;
arg = *(newargs+i);
/* Now adjust the width for the term just processed and the
space required BEFORE that term */
width += WIDTH(arg);
if(i > 1)
width += product_spacing( newargs[i-1], newargs[i]);
/* Now adjust the height and level */
if(i==1)
{ height = HEIGHT(arg);
level = LEVEL(arg);
}
else
{ temp = (long) LEVEL(arg) - (long) (unsigned short) level;
temp2 = (long) HEIGHT(arg) - (long) LEVEL(arg) -
(height - level);
if(temp> 0) /* arg sticks out above water level */
height += temp;
if(temp2 > 0) /* arg sticks out below water level */
height += temp2;
if(temp>0)
level += temp;
if(level>MAXHEIGHT)
return 2;
/* water level of product is max of levels of args */
}
}
if(arity == 2 && op < PREFIXBOUND &&
INTEGERP(ARG(0,expr)) && ISATOM(ARG(1,expr))
)
/* sin 3 x, not sin(3 x) */
{ /* don't call paren and adjust b for parentheses */
b.leftbra = b.rightbra = 0;
}
else if(paren(op,'*',dir))
{ b.leftbra = b.rightbra = 1;
b.bra = PARENS;
width += 2*get_parenspace(sizein) + 2*get_parenwidth(sizein);
}
else
b.leftbra = b.rightbra= 0;
if ((width > MAXWIDTH) || (height > MAXHEIGHT))
return 2;
b.w = width;
b.wl = (US) level;
b.h = (US) height;
b.size = sizein;
b.extra = 0;
SETBLOCK(*newexpr,b);
return 0;
}
/*_______________________________________________________________________*/
/* used to bblock AND and OR both */
static int bblock_and(term expr,term *newexpr,US sizein,US op,US dir)
{ block b;
int err,i;
term c,t,empty;
unsigned short f,g;
unsigned short arity = ARITY(expr);
unsigned long width = 0, height = 0, level=0;
unsigned short newdir;
long temp,temp2;
term arg;
US func = FUNCTOR(expr);
term *newargs; /* pointer to dynamic array of terms for new bblocked args*/
newargs = ARGPTR(*newexpr);
for(i=1;i<=arity;i++)
{ newdir = (unsigned short)(i==1 ? LEFT : RIGHT);
if(i > 1)
f = (unsigned short)((ATOMIC(ARG((unsigned short)(i-2),expr)) ? 0 : FUNCTOR(ARG((unsigned short)(i-2),expr))));
else
f = 0;
g = (unsigned short)(ATOMIC(ARG(i-1,expr)) ? 0 : FUNCTOR(ARG(i-1,expr)));
/* test whether this is an inequality to be displayed
using 'interval notation', e.g. a < x < b */
if((f == '<' || f == LE) && (g == '<' || g == LE) && func == AND
&& equals(ARG(1,ARG(i-2,expr)),ARG(0,ARG(i-1,expr)))
) /* then it IS to be displayed using interval notation */
{ c = ARG(1,ARG(i-1,expr));
t = make_term(g,2);
SETFUNCTOR(empty,ILLEGAL,0); /* a void term, no display */
ARGREP(t,0,empty);
ARGREP(t,1,c);
err = bb(t,newargs +i, sizein,func,newdir);
if(err)
return err;
}
else
{ err = bb(ARG(i-1,expr),newargs+i,sizein,func,newdir);
if (err)
return err;
}
arg = *(newargs+i);
/* Now adjust the width for the term just processed and the
space required after that term */
width += WIDTH(arg);
if (i < arity) /* no space after the last term */
{
if (func==OR)
width += get_charspace(); /* "or" is 2 chars long */
/* Now adjust the height */
}
if(i==1)
{ level = LEVEL(arg);
height = HEIGHT(arg);
}
else
{ temp = (long) LEVEL(arg) - level;
temp2 = (long) HEIGHT(arg) - (long) LEVEL(arg) - (height-level);
if(temp> 0)
height += temp; /* arg sticks out above water level */
if(temp2 > 0) /* arg sticks out below water level as well as above */
height += temp2;
if(temp>0)
level += temp;
if(level>MAXHEIGHT)
return 2;
/* water level of product is max of levels of args */
}
}
if(paren(op,func,dir))
{ b.leftbra= b.rightbra = 1;
b.bra = PARENS;
width += 2*get_parenspace(sizein) + 2*get_parenwidth(sizein);
}
else
b.leftbra= b.rightbra= 0;
if ((width > MAXWIDTH) || (height > MAXHEIGHT))
return 2;
b.w = width;
b.wl = (US) level;
b.h = (US) height;
b.size = sizein;
b.extra = 0;
SETBLOCK(*newexpr,b);
return 0;
}
/*________________________________________________________________________*/
/* a term like +(a,-b,c) gets printed as a-b+c */
/* space has been allocated before calling this for the args of *newexpr */
static int bblock_sum(term expr,
term *newexpr,
unsigned short sizein,
unsigned short op,
unsigned short dir
)
{ block b;
int err,i;
int color;
int sign; /* for the sign of the current arg */
unsigned short arity = ARITY(expr);
unsigned long width = 0, height = 0, level=0;
unsigned short leftop,newdir;
long temp,temp2;
term arg;
term *minusterm,*argptr;
term *newargs; /* pointer to dynamic array of terms for new bblocked args*/
newargs = ARGPTR(*newexpr);
for(i=1;i<=arity;i++)
{ leftop = FUNCTOR(ARG(i-1,expr));
sign = leftop == '-' ? -1 : 1;
newdir = (unsigned short)( i==1 ? LEFT : RIGHT);
if(sign > 0)
{ err = bb(ARG(i-1,expr),newargs+i,sizein,'+',newdir);
if(err)
return err;
arg = *(newargs+i);
width += WIDTH(arg);
if (i > 1)
{ width += 2* get_sumspace(sizein) + get_pluswidth(sizein); // for the + sign
}
}
else
{ minusterm = (term *) mallocate(sizeof(term));
if(minusterm == NULL)
nospace();
*minusterm = make_term('-',2);
argptr = ARGPTR(*minusterm) + 1; /* pointer to where the bblocked
term to be subtracted goes */
err = bb(ARG(0,ARG(i-1,expr)),argptr,sizein,'-',CENTRAL);
if (err)
return err;
/* Now *minusterm itself is not properly bblocked,
because nothing has been put in for its first arg. */
b = GETBLOCK(*argptr);
b.w += get_minuswidth(sizein) + get_minusspace(sizein); /* for the minus sign and space after */
b.leftbra = b.rightbra = 0; /* No parens around the minus term */
SETBLOCK(*minusterm,b);
color = COLOR(ARG(i-1,expr));
if(color)
SETCOLOR(*minusterm,color);
ARGREP(*newexpr,i,*minusterm);
arg = *(newargs+i);
width += WIDTH(arg); // which includes the minus sign and minusspace after it but not before it
if(i > 1)
width += get_minusspace(sizein); // for the space before it
}
/* Now adjust the height */
if (i==1)
{ level = LEVEL(arg);
height = HEIGHT(arg);
}
else
{ temp = (long) LEVEL(arg) - level;
temp2 = (long) HEIGHT(arg) - (long) LEVEL(arg) -(height-level);
if(temp> 0)
{ height += temp; /* arg sticks out above water level */
level += temp; /* water level of sum is max of levels of args */
}
if(temp2 > 0) /* arg sticks out below water level as well as above */
height += temp2;
if(level > MAXHEIGHT)
return 2;
}
}
if(paren(op,'+',dir))
{ b.leftbra= b.rightbra= 1;
b.bra = PARENS;
width += 2*get_parenspace(sizein) + 2*get_parenwidth(sizein);
}
else
b.leftbra= b.rightbra= 0;
if((width > MAXWIDTH) || (height > MAXHEIGHT))
return 2;
b.w = width;
b.wl = (US) level;
b.h = (US) height;
b.size = sizein;
b.extra = 0;
SETBLOCK(*newexpr,b);
return 0;
}
/*________________________________________________________________________*/
static int powersize(term t)
/* return TENPOINT if t contains a LIMIT or INTEGRAL
or a fraction with a sum in the denominator that
contains an exponent;
else return EIGHTPOINT
*/
{ unsigned i,n;
unsigned f;
if(ATOMIC(t))
return EIGHTPOINT;
f = FUNCTOR(t);
if(f == LIMIT || f == INTEGRAL)
return TENPOINT;
n = ARITY(t);
for(i=0;i<n;i++)
{ if(powersize(ARG(i,t)) == TENPOINT)
return TENPOINT;
}
return EIGHTPOINT;
}
/*________________________________________________________________________*/
/* space has been allocated before calling this for the args of *newexpr */
static int bblock_power(term expr,term *newexpr,
unsigned short sizein,
unsigned short op,
unsigned short dir
)
{ int err;
term arg1,arg2;
block b;
int size;
err = bb(ARG(0,expr),&arg1,sizein,'^',LEFT);
if (err)
return err;
/* Now, should we use eightpoint type in the exponent?
powersize answers this question. */
size = powersize(ARG(1,expr));
err = bb(ARG(1,expr),&arg2,(short) size,'^',RIGHT);
b = GETBLOCK(arg2);
b.size = size;
SETBLOCK(arg2,b); /* b.size is used by display */
if (err)
return err;
err = bblock_power_aux(arg1,arg2,newexpr,op,dir);
if(err)
return err;
return 0;
}
/*_______________________________________________________________*/
static int bblock_power_aux2(term arg1, term arg2, term *newexpr,
unsigned short op, unsigned short dir
)
/* arg1 and arg2 are the bblocked base and exponent respectively */
/* prepare for printing as infix exponent like sin^2 x */
/* To enable Term Selection to work, this isn't used unless the
infix exponent will stick up above the arg, eg. sin^2 x^2 will be
displayed as (sin x^2)^2 instead. So this function can assume that
the arg will not stick up too high. It's also not used unless
the infix exponent is just one character high.
*/
{ block b;
unsigned long width;
b = GETBLOCK(arg1);
*(ARGPTR(*newexpr) + 1) = arg1;
*(ARGPTR(*newexpr) + 2) = arg2;
assert(HEIGHT(arg2) <= get_charspace());
assert(HEIGHT(arg1) <= get_charspace());
b.h = (coord)(HEIGHT(arg1) + LEVEL(arg2));
b.wl =(coord)(LEVEL(arg1) + LEVEL(arg2));
/* Now compute the width of the power term */
width = WIDTH(ARG(1,arg1)) + WIDTH(arg2) + 2*get_powerspace();
width = (unsigned long)(width + functor_width(FUNCTOR(arg1),(short) b.size));
b.leftbra=b.rightbra=0;
if (width >= MAXWIDTH)
return 2;
b.w = width;
/* now calculate the water level of the power term */
if(paren(op,FUNCTOR(arg1),dir))
{ b.leftbra= b.rightbra = 1;
b.bra = PARENS;
b.w += 2*get_parenspace(b.size) + 2*get_parenwidth(b.size);
}
else
b.leftbra = b.rightbra = 0;
b.size = SIZE(arg1);
SETBLOCK(*newexpr,b);
return 0;
}
/*_______________________________________________________________*/
int bblock_power_aux(term arg1, term arg2, term *newexpr,
unsigned short op, unsigned short dir
)
/* arg1 and arg2 are the bblocked base and exponent respectively */
/* this can't be static as it's called in break.c */
{ block b;
unsigned long width,height;
coord baselevel,exponentlevel,baseheight,basedepth,exponentdepth;
long shift;
/* Now: is this term going to be displayed with an infix exponent
like sin^2 x, or not? */
b = GETBLOCK(arg1);
if(infix_exp(arg1,arg2))
{ b.extra = 1;
if(b.bra)
{ assert(b.w > (unsigned)(2*(get_parenspace(b.size) + get_parenwidth(b.size))));
b.w = b.w - 2*(get_parenspace(b.size) + get_parenwidth(b.size));
/* bblock_function sets up function terms to be parenthesized
as children of '^', so we have to adjust it here */
b.bra = 0;
}
SETBLOCK(arg1,b);
return bblock_power_aux2(arg1,arg2,newexpr,op,dir);
}
b.extra = 0; /* don't use infix exponent */
SETBLOCK(arg1,b);
*(ARGPTR(*newexpr) + 1) = arg1;
*(ARGPTR(*newexpr) + 2) = arg2;
/* Now compute the height of the power term */
/* The exponent is placed so its water level is at the
top of the base, unless that would result in its bottom going below
the waterlevel of the base by more than 1/4 of the
depth of the base. (This last rule is taken from TeX.)
*/
baselevel = LEVEL(arg1);
exponentlevel = LEVEL(arg2);
baseheight = HEIGHT(arg1);
basedepth = (coord)(baseheight-baselevel);
exponentdepth = (coord)(HEIGHT(arg2)-exponentlevel);
shift = (long)(exponentdepth - (baselevel + 0.25 * basedepth));
/* shift is used to implement the TeX rule about the exponent not
descending too far. */
height = baseheight + exponentlevel;
if(shift > 0)
height += shift;
if(height >= MAXHEIGHT)
return 2;
b.h = (US) height;
/* Now compute the width of the power term */
width = WIDTH(arg1) + WIDTH(arg2) + get_powerspace();
if(FUNCTOR(arg1)==ILLEGAL) /* void term, no display of base */
width -= get_powerspace();
b.leftbra=b.rightbra=0;
if (width >= MAXWIDTH)
return 2;
b.w = width;
b.wl = (coord) (exponentlevel + baselevel + (shift > 0 ? shift: 0));
if(paren(op,'^',dir))
{ b.leftbra= b.rightbra = 1;
b.bra = PARENS;
b.w = (unsigned long)(b.w + 2*get_parenspace(b.size) + 2*get_parenwidth(b.size));
}
else
b.leftbra = b.rightbra = 0;
b.size = SIZE(arg1);
SETBLOCK(*newexpr,b);
return 0;
}
/*_________________________________________________________________________*/
/* space has been allocated before calling this for the args of *newexpr */
/* this is used in case expr has functor '/' or BINOMIAL */
/* if either arg is wider than 'maxfract' then it will be bblocked
for in-line display using '/' or "binomial" instead of vertical display */
/* Also if sizein==EIGHTPOINT, quotients of atomic terms are bblocked for
inline display */
static int bblock_fraction(term expr,term *newexpr,US sizein,US op,US dir)
{ block b;
b.size = sizein;
unsigned long width,height;
int err;
int sizedown;
term arg1,arg2;
unsigned short f = FUNCTOR(expr); /* might be BINOMIAL or '/' */
sizedown = (f==BINOMIAL) ? EIGHTPOINT : sizein;
err = bb(ARG(0,expr),ARGPTR(*newexpr) + 1,sizedown,'/',LEFT);
if (err)
return err;
err = bb(ARG(1,expr),ARGPTR(*newexpr) + 2,sizedown,'/',RIGHT);
if (err)
return err;
arg1 = ARG(1,*newexpr);
arg2 = ARG(2,*newexpr);
width = MAXIMUM(WIDTH(arg1), WIDTH(arg2));
if(paren(op,f,dir)) /* then fraction must be enclosed in parens */
{ width += 2*(get_parenwidth(sizein) + get_parenspace(sizein));
b.leftbra= b.rightbra = 1;
b.bra = PARENS;
}
else
b.leftbra= b.rightbra = 0;
if(width > MAXWIDTH)
return 2;
if( (get_maxfract() == 0 && sizein == TENPOINT && op != '/' && op != BINOMIAL) ||
(get_maxfract() == 0 && contains(ARG(1,expr),'+')) ||
/* fractions with sums in the denom should be vertically displayed
even in EIGHTPOINT or in compound fractions */
( width <= (unsigned long) get_maxfract() &&
(
(sizein == TENPOINT && op != '/' && op != BINOMIAL)
|| !ATOMIC(ARG(0,expr)) || !ATOMIC(ARG(1,expr))
)
)
)
/* then bblock for vertical display */
{ height = HEIGHT(arg1) + HEIGHT(arg2);
if (f == '/')
{ height += get_abovefractionspace() + get_belowfractionspace();
if(GETBLOCK(arg2).leftbra)
height += get_belowfractionspace();
/* additional space when denominator is bracketed */
}
if (height > MAXHEIGHT)
return 2;
else
b.h = (US) height;
b.w = width;
if(f == BINOMIAL)
b.wl = HEIGHT(arg1);
else
b.wl = (coord)(HEIGHT(arg1) + get_abovefractionspace()); /* water level at fraction bar */
}
else if (f == '/') /* bblock fraction for horizontal display */
/* this is tricky in that it requires different parenthesization
on the numerator and denominator than for vertical display.
*/
{ coord level,submerged,savespace;
SETFUNCTOR(*newexpr,FRACT,ARITY(*newexpr));
/* that tells 'display' to use horizontal display */
if( (FUNCTOR(arg1)==0 && ARITY(arg1)==2) /* a number */
|| ISATOM(arg1)
)
; /* do nothing, never parenthesize an atom or number in numerator */
else if(paren(FRACT,FUNCTOR(arg1),LEFT)) // && b.leftbra==0) 8.29.04
{ b = GETBLOCK(arg1);
b.leftbra= 1; b.rightbra= 1;
b.bra = PARENS;
savespace = (coord)(get_parenwidth(sizein) + get_parenspace(sizein));
b.w = (unsigned long)(b.w + 2*savespace);
SETBLOCK(arg1,b);
}
if( (FUNCTOR(arg2)==0 && ARITY(arg2)==2) /* a number */
|| ISATOM(arg2)
)
; /* do nothing, never parenthesize an atom or number in denominator */
else if(paren(FRACT,FUNCTOR(arg2),RIGHT)) // 8.29.04 && b.leftbra==0)
{ b = GETBLOCK(arg2);
savespace = (coord)(get_parenwidth(sizein) + get_parenspace(sizein));
b.leftbra= 1; b.rightbra= 1;
b.w += 2*savespace;
b.bra = PARENS;
b.extra = 0;
SETBLOCK(arg2,b);
}
level = MAXIMUM(LEVEL(arg1),LEVEL(arg2));
submerged = (coord)(MAXIMUM(HEIGHT(arg1)-LEVEL(arg1),HEIGHT(arg2)-LEVEL(arg2)));
height = level + submerged;
if(height > MAXHEIGHT)
return 2;
b.h = (US) height;
b.w = WIDTH(arg1) + WIDTH(arg2)
+ (sizein==EIGHTPOINT ? 5 : 5); /* for the fraction bar */
if (paren(op,FRACT,dir) )
{ b.leftbra=b.rightbra=1;
b.w += 2*(get_parenwidth(sizein)+get_parenspace(sizein));
b.bra = PARENS;
}
else
b.rightbra=b.leftbra=0;
b.wl = level;
}
else if (f == BINOMIAL)
{ coord level,submerged;
level = MAXIMUM(LEVEL(arg1),LEVEL(arg2));
submerged = (coord)(MAXIMUM(HEIGHT(arg1)-LEVEL(arg1),HEIGHT(arg2)-LEVEL(arg2)));
height = level + submerged;
if(height > MAXHEIGHT)
return 2;
b.h = (US) height;
b.w = WIDTH(arg1) + WIDTH(arg2) + 2*get_charspace(); /* for comma and space */
b.leftbra= b.rightbra= 0;
b.wl = level;
b.w += 2*(get_charspace() + get_parenspace(sizein));
}
b.extra = 0;
if(COLOR(expr))
SETCOLOR(*newexpr,COLOR(expr));
/* color may have been destroyed by SETFUNCTOR, above */
SETBLOCK(*newexpr,b);
return 0;
}
/*____________________________________________________________________*/
/* Functors not handled by bblock_function:
ABSFUNCTOR, DET, MATRIX,DIFF, INTEGRAL,SUM, PRODUCT, NOT,
SQRT, ROOT, LOGB, FLOOR, BINOMIAL, LIMIT, PR, CASES, EVAL
AND, IF, OR, */
static int bblock_function(term expr,term *newexpr,
US sizein,US op,US dir)
{ block b;
unsigned long width;
long height;
int err;
term arg;
int i;
coord adjustment=0;
coord charheight = sizein == TENPOINT ? get_charspace() : get_eightpointheight();
unsigned short f = FUNCTOR(expr);
unsigned short arity = ARITY(expr);
char fname[32];
if(f == '=' && SUBSCRIPTARGS(expr))
{ /* this bit is set sometimes on equations */
UNSET_FACTORME(expr); /* this macro unsets the SUBSCRIPTARGS bit */
UNSET_FACTORME(*newexpr);
}
if(SUBSCRIPTARGS(expr))
{ SET_SUBSCRIPTARGS(*newexpr);
return bblock_subscripted_function(expr,newexpr,sizein,op,dir);
}
for(i=0;i<arity;i++)
{ err = bb(ARG(i,expr),ARGPTR(*newexpr)+i+1,sizein,f,CENTRAL);
if (err)
return err;
}
arg = ARG(1,*newexpr);
b = GETBLOCK(arg);
width = b.w;
height = b.h;
if(!INFIX(f) && b.h <= charheight)
{ adjustment = (coord)(charheight - b.h);
b.h = charheight;
b.wl = (coord)(b.wl + adjustment);
}
functor_string(f,SCREEN,fname);
if(DEFINED_FUNCTION(f))
{
#ifdef SVG
assert(0);
// We're not going to support defined functions in WebMathXpert.
#else
width += text_width(f,sizein);
#endif
}
else
width += functor_width(f,sizein);
if( PREFIX(f) )
{ width += get_prefixspace(); /* log x , small space after 'log' */
/* infix exponents will be adjusted for by bblock_power */
if(paren(op,f,dir) &&
!(op == '*' && GETBLOCK(ARG(1,*newexpr)).rightbra)
/* sin(3x) e^x, not (sin(3x)) e^x */
)
{ b.leftbra= b.rightbra = 1;
b.bra = PARENS;
width += 2*get_parenspace(b.size) + 2*get_parenwidth(b.size);
}
else
b.leftbra = b.rightbra = 0;
}
else if( POSTFIX(f) )
{ /* for example (x + y)! No space, so don't change width */
if (paren(op,f,dir))
{ b.leftbra = b.rightbra = 1;
b.bra = PARENS;
width += 2*get_parenwidth(b.size);
if(height > get_charspace())
width += 2*get_parenspace(b.size);
}
else
b.leftbra = b.rightbra = 0;
}
else if( INFIX(f) )
{ /* for example x < y */
term arg2 = ARG(2,*newexpr);
int gap; /* how much space to leave on each side of the symbol */
/* spaces before and after relation symbol? */
/* not in subscripts or superscripts unless a minus sign
follows, e.g. a=b ; a = -b a > -b a>b */
width += WIDTH(arg2);
if(sizein==TENPOINT)
{ gap = get_infixspace();
// width has already been increased for the width of the relation symbol
width += 2*gap; // even if the left arg is ILLEGAL. Then it has zero width
// but we still need a gap before the inequality sign.
/* when displaying a < b < c we don't want to allocate space here
for before the second < */
}
else /* superscript or subscript */
{ gap = 2; /* You could easily change this */
width += 2*gap;
/* At present we leave NO extra space in EIGHTPOINT,
even if there's a minus sign on the right.
Display will handle whatever space we do leave by
putting the extra space equally on both sides of
the infix operation sign. */
}
if(paren(op,f,dir))
{ b.leftbra = b.rightbra = 1;
b.bra = PARENS;
width += 2*get_parenspace(b.size) + 2*get_parenwidth(b.size);
}
else
b.leftbra = b.rightbra = 0;
if(width > MAXWIDTH)
return 2;
b.w = width;
b.wl = MAXIMUM(LEVEL(arg),LEVEL(arg2));
height = b.wl + MAXIMUM(HEIGHT(arg)-LEVEL(arg),HEIGHT(arg2)-LEVEL(arg2));
if (height > MAXHEIGHT)
return 2;
b.h = (coord) height;
}
else /* f(x sin x), no space between parens and args*/
/* but we DO put a space between paren and args if the term
as a whole has height > get_charspace() */
/* f(sin x,y) No space after comma */
/* f(x,y) No space after comma */
{ width += 2*get_parenwidth(b.size); /* for parentheses */
b.leftbra = b.rightbra = 0; /* parentheses never required on f(x,y) */
if(!adjustment)
b.wl = LEVEL(arg); /* initialization, before we get to other args */
if(arity > 1)
for(i=1;i<arity;i++)
{ width += WIDTH(ARG(i+1,*newexpr)); /* for i-th arg of expr */
width += get_parenwidth(b.size); /* for comma */
if (LEVEL(ARG(i+1,*newexpr)) > b.wl)
{ long excessht, excessdepth; /* must be signed */
excessht = LEVEL(ARG(i+1,*newexpr)) -b.wl; /* above previous water level */
excessdepth = (HEIGHT(ARG(i+1,*newexpr)) - LEVEL(ARG(i+1,*newexpr)))
- (height - b.wl); /* below water level */
if(excessht > 0)
{ height += excessht;
if(b.wl + excessht > MAXHEIGHT)
return 2;
b.wl += (coord) excessht;
}
if(excessdepth >0)
height += excessdepth;
}
}
if( height > get_charspace())
width += 2*get_parenspace(b.size); /* spaces by parens */
}
if (height > MAXHEIGHT)
return 2;
if (width > MAXWIDTH)
return 2;
else
b.w = width;
b.size = sizein;
b.extra = 0;
SETBLOCK(*newexpr,b);
return 0;
}
/*_______________________________________________________________*/
static int infix_exp(term a, term b)
/* a and b are bblocked args of a power term. Determine whether it
should be displayed in infix-exponent form like sin^2 x.
If so return 1. If not return 0 */
/* Note that ISATOM works correctly on bblocked terms,
because block_atom sets the arity artificially to zero.
ISINTEGER does not, however, so instead
we use the following macro: */
#define ISBBLOCKEDINTEGER(x) (TYPE(x) == INTEGER && FUNCTOR(x)==0 && ARITY(x)==3)
/* To enable Term Selection to work, infix exponents are not used unless
the exponent will stick up above the arg, eg. sin^2 x^2 will be
displayed as (sin x^2)^2 instead. Since infix exponents aren't used when
the exponent is more than get_charspace() high, that means that the arg can't
stick up too far above its water level. Also they would look funny if
pieces stuck out BELOW the water level, so for simplicity we just
don't use infix exponents except when the arg has height <= get_charspace().
*/
{ unsigned f = FUNCTOR(a);
term u;
if(ATOMIC(a))
return 0;
if(f != SIN && f!= COS && f != TAN && f != SEC && f != CSC && f != COT &&
f != SINH && f != COSH && f != TANH && f != SECH && f != CSCH && f!= COTH
)
return 0; /* ln^2 x is wrong, (ln x)^2 is right */
u = ARG(1,a);
if(HEIGHT(u) > get_charspace())
return 0; /* see comments above */
if(ISBBLOCKEDINTEGER(b)) /* bignum exponents not allowed here */
return 1; /* sin^5430543243234 x looks too weird */
if(ISATOM(b) && !bcontains(ARG(1,a),FUNCTOR(b)))
return 1; /* sin^n n is wrong; sin^n nx is also wrong by this rule
though this is so unusual that common usage isn't clear. */
if(FUNCTOR(b) == '*' && ARITY(b) == 3 /* so original arity is 2 */
&& ISBBLOCKEDINTEGER(ARG(1,b)) && ISATOM(ARG(2,b))
&& !bcontains(ARG(1,a),FUNCTOR(ARG(2,b)))
)
return 1; /* sin^(2n) x is ok but not sin^(2n) n */
return 0;
}
/*___________________________________________________________________*/
static int interval_as_and(term u)
/* return 1 if u is an interval, that is, an AND of two inequalities
with the same middle term, as a < x && x < b. Return 0 if not. */
/* copied from prover.c to prevent overlay swapping */
{ unsigned f,g;
if(FUNCTOR(u) != AND)
return 0;
if(ARITY(u) != 2)
return 0;
f = FUNCTOR(ARG(0,u));
if(f != '<' && f != LE)
return 0;
g = FUNCTOR(ARG(1,u));
if(g != '<' && g != LE)
return 0;
return equals(ARG(1,ARG(0,u)),ARG(0,ARG(1,u)));
}
/*________________________________________________________________*/
static int extravars(term t, term x)
/* Return 1 if t has variables other than x */
/* Return 0 if not */
{ unsigned i,n,f;
if(OBJECT(t))
return 0;
if(ISATOM(t))
{ f = FUNCTOR(t);
if(f == PI_ATOM || f == 'e' ||
(f == 'i' && TYPE(t) != INTEGER) ||
f == FUNCTOR(x)
)
return 0;
return 1;
}
n = ARITY(t);
for(i=0;i<n;i++)
{ if(extravars(ARG(i,t),x))
return 1;
}
return 0;
}
/*______________________________________________________________________*/
/* You can't call destroy_term on a bblocked term without disaster.
Instead use this: */
void destroy_bblocked_term( term f)
{ unsigned i,j,n;
void *node;
if(
(ISATOM(f)
/* ISATOM works on bblocked terms because arity is
artificially set to zero */
|| (FUNCTOR(f)==0 && ARITY(f)==3) /* a bblocked OBJECT */
)
&& HASARGS(f) /* hasn't already been 'destroyed' */
)
{ /* ARG(1,f) is a pointer to char; the memory it points
to was allocated by bblock_object. Free that string: */
/* you can't just say free2(ARG(2,f)) or even free2( (char *) ARG(2,f))
because you can't cast a structure type to a pointer. You have
to use this arcane syntax: */
if( SUBSCRIPT(FUNCTOR(f)) || /* subscripted atom */
ARITY(f)==3
) /* don't do this for non-subscripted atoms, which have static
print strings; do it only for objects */
free2(* (char **)(ARGPTR(f)+1));
/* cf 2nd-to-last line in bblock_object */
RELEASE(f); /* three args have been allocated */
KILLARGS(f);
return;
}
if( ! HASARGS(f) )
return; /* don't free args if already freed */
if(FUNCTOR(f) == MATRIX || FUNCTOR(f) == LINEARSYSTEM)
{ unsigned m;
n = ARITY(f) -1; /* number of rows plus 1*/
m = ARITY(ARG(1,f)); /* number of columns + 1*/
node = *(unsigned short **)(ARGPTR(f)+n); /* pointer to array of column-width numbers,
see display.c line 2002 */
free2(node);
for(i=1;i<n;i++)
{ for(j=1;j<m;j++)
destroy_bblocked_term(ARG(j,ARG(i,f))); /* destroy the entries */
node = (ARGPTR(f) + i)->args; /* space for i-th row of matrix */
free2(node);
}
RELEASE(f);
}
else
{ n = ARITY(f);
for(i=1;i<n;i++)
destroy_bblocked_term(ARG(i,f));
/* don't destroy ARG(0,f) as it is a block, not a term ! */
RELEASE(f); /* two args have been allocated */
KILLARGS(f);
}
return;
}
/*___________________________________________________________________*/
static int bblock_binder(term expr,term *newexpr,
US sizein,US op,US dir)
/* bblock FORALL, EXISTS, LAMBDA */
{ block b;
unsigned long width;
int err;
term arg;
int i;
unsigned short f = FUNCTOR(expr);
unsigned short arity = ARITY(expr);
assert(arity == 2);
width = functor_width(f,sizein);
for(i=0;i<arity;i++)
{ err = bb(ARG(i,expr),ARGPTR(*newexpr)+i+1,sizein,f,(unsigned short)(i==0 ? LEFT : RIGHT));
if (err)
return err;
}
if(FUNCTOR(ARG(0,expr)) == AND)
{ /* as in all([x,y],P(x,y)); this prints as 'all xy P(x,y)' */
for(i=0;i<ARITY(ARG(0,expr));i++)
width += WIDTH(ARG(i+1,ARG(1,*newexpr)));
width += WIDTH(ARG(2,*newexpr));
}
else
width += WIDTH(ARG(1,*newexpr)) + WIDTH(ARG(2, *newexpr));
arg = ARG(2,*newexpr);
b = GETBLOCK(arg);
if (width > MAXWIDTH)
return 2;
else
b.w = width;
b.size = sizein;
b.leftbra = b.rightbra = 0; /* never parenthesize these terms */
SETBLOCK(*newexpr,b);
return 0;
}
/*____________________________________________________________*/
static char *compute_subscripted_varname(term expr)
/* compute the print string for a subscripted variable.
Return a string in space created by callocate.
There is another copy of this in vaux.c
*/
{ char buffer[32];
char *printstring;
term temp = expr;
int subscript;
unsigned f = FUNCTOR(expr);
SETFUNCTOR(temp,VARNAME(f),0);
strcpy(buffer,atom_string(temp));
subscript = SUBSCRIPT(f)-1;
int avail = sizeof(buffer)-(int)strlen(buffer);
snprintf(buffer+(int) strlen(buffer),avail,"%d",subscript);
/* Now the right string is in buffer */
printstring = callocate((int) strlen(buffer)+1,sizeof(char));
strcpy(printstring,buffer);
return printstring;
}
/*__________________________________________________________________*/
static int bcontains(term t, unsigned short f)
/* t is a bblocked term. Return 1 if t contains f, 0 if not.
This is like 'contains', but you can't use 'contains' on
a bblocked term. */
{ unsigned short i,n;
if(BB_OBJECT(t))
return 0;
if(FUNCTOR(t) == f)
return 1;
if(ISATOM(t))
return 0; /* note, ISATOM works on bblocked terms because
bblock_atom artificially sets the arity to zero */
n = ARITY(t);
for(i=1;i<n;i++)
/* Start the loop from 1 not zero since t is a bblocked term */
{ if(bcontains(ARG(i,t),f))
return 1;
}
return 0;
}
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists