void
JSummation::Render
	(
	const JExprRenderer& renderer,
	const JExprRectList& rectList
	)
	const
{
	// find ourselves in the list

	JIndex ourIndex;
	const JBoolean found = rectList.FindFunction(this, &ourIndex);
	assert( found );

	const JRect ourRect          = rectList.GetRect(ourIndex);
	const JCoordinate ourMidline = rectList.GetMidline(ourIndex);
	const JSize fontSize         = rectList.GetFontSize(ourIndex);

	// draw ourselves

	JCoordinate h          = ourRect.left;
	const JSize spaceWidth = renderer.GetStringWidth(fontSize, " ");

	const JSize argCount = GetArgCount();
	for (JIndex i=1; i<=argCount; i++)
		{
		const JFunction* f   = this;
		const JFunction* arg = GetArg(i);
		if (arg->GetType() == kJNegationType)
			{
			renderer.DrawString(h, ourMidline, fontSize, JPGetSubtractionString());
			f = arg;
			const JNegation* neg = dynamic_cast<const JNegation*>(arg);
			assert( neg != NULL );
			arg = neg->GetArg();
			}
		else if (i > 1)
			{
			renderer.DrawString(h, ourMidline, fontSize, JPGetAdditionString());
			}

		arg->Render(renderer, rectList);

		JIndex argIndex;
		const JBoolean found = rectList.FindFunction(arg, &argIndex);
		assert( found );
		const JRect argRect = rectList.GetRect(argIndex);
		h = argRect.right;

		if (ParenthesizeArgForRender(*f, *arg))
			{
			renderer.DrawParentheses(argRect);
			h += renderer.GetParenthesisWidth(argRect.height());
			}

		h += spaceWidth;
		}
}
void
JSummation::Print
	(
	ostream& output
	)
	const
{
	const JSize argCount = GetArgCount();
	for (JIndex i=1; i<=argCount; i++)
		{
		const JFunction* f   = this;
		const JFunction* arg = GetArg(i);
		if (arg->GetType() == kJNegationType)
			{
			output << JPGetSubtractionString();
			f = arg;
			const JNegation* neg = dynamic_cast(const JNegation*, arg);
			assert( neg != NULL );
			arg = neg->GetArg();
			}
		else if (i > 1)
void
JSummation::Print
	(
	ostream& output
	)
	const
{
	const JSize argCount = GetArgCount();
	for (JIndex i=1; i<=argCount; i++)
		{
		const JFunction* f   = this;
		const JFunction* arg = GetArg(i);
		if (arg->GetType() == kJNegationType)
			{
			output << JPGetSubtractionString();
			f = arg;
			const JNegation* neg = dynamic_cast<const JNegation*>(arg);
			assert( neg != NULL );
			arg = neg->GetArg();
			}
		else if (i > 1)
			{
			output << JPGetAdditionString();
			}

		if (ParenthesizeArgForPrint(*f, *arg))
			{
			output << '(';
			arg->Print(output);
			output << ')';
			}
		else
			{
			arg->Print(output);
			}
		}
}
JParseResult
JParseAsSummation
	(
	const JCharacter*		origExpr,
	const JSize				origLength,
	const JVariableList*	theVariableList,
	JFunction**				theFunction,
	const JBoolean			allowUIF
	)
{
	*theFunction = NULL;

	// remove enclosing parentheses

	const JCharacter* expr = origExpr;
	const JSize length     = JStripParentheses(&expr, origLength);

	// build a list of the locations of all visible + and - operators

	const JCharacter* plusStr  = JPGetAdditionString();
	const JSize plusLength     = JPGetAdditionStringLength();
	const JCharacter* minusStr = JPGetSubtractionString();
	const JSize minusLength    = JPGetSubtractionStringLength();

	JArray<JCharacter> opList;
	JArray<JSize> opOffsetList;
	JArray<JSize> argOffsetList;
	JSize lastOffset = 0;
	while (1)
		{
		JSize plusOffset;
		const JBoolean foundPlus =
			JFindFirstOperator(expr + lastOffset, length - lastOffset,
							   plusStr, &plusOffset);
		plusOffset += lastOffset;

		JSize minusOffset;
		const JBoolean foundMinus =
			JFindFirstOperator(expr + lastOffset, length - lastOffset,
							   minusStr, &minusOffset);
		minusOffset += lastOffset;

		JCharacter opType;
		JSize opOffset, argOffset;
		if (foundPlus && foundMinus && plusOffset < minusOffset)
			{
			opType = '+';
			opOffset  = plusOffset;
			argOffset = plusOffset + plusLength;
			}
		else if (foundPlus && foundMinus && minusOffset < plusOffset)
			{
			opType = '-';
			opOffset  = minusOffset;
			argOffset = minusOffset + minusLength;
			}
		else if (foundPlus)
			{
			opType = '+';
			opOffset  = plusOffset;
			argOffset = plusOffset + plusLength;
			}
		else if (foundMinus)
			{
			opType = '-';
			opOffset  = minusOffset;
			argOffset = minusOffset + minusLength;
			}
		else
			{
			break;
			}

		if (!JIsExponentSign(expr, opOffset, argOffset))
			{
			opList.AppendElement(opType);
			opOffsetList.AppendElement(opOffset);
			argOffsetList.AppendElement(argOffset);
			}

		lastOffset = argOffset;
		}

	if (opList.IsEmpty())
		{
		return kJNotMyProblem;
		}
	else if (opList.GetElementCount() == 1 && opOffsetList.GetElement(1) == 0)
		{
		const JSize argOffset = argOffsetList.GetElement(1);
		JFunction* arg = NULL;
		if (!JRecurseFunction(expr + argOffset, length - argOffset, theVariableList,
							  &arg, allowUIF))
			{
			return kJParseError;
			}

		if (opList.GetElement(1) == '-')
			{
			*theFunction = new JNegation(arg);
			assert( *theFunction != NULL );
			return kJParsedOK;
			}
		else	// we ignore leading +
			{
			*theFunction = arg;
			return kJParsedOK;
			}
		}
	else
		{
		JSummation* sum = new JSummation;
		assert( sum != NULL );

		// No operand in front means implied +

		if (opOffsetList.GetElement(1) > 0)
			{
			opList.PrependElement('+');
			opOffsetList.PrependElement(0);
			argOffsetList.PrependElement(0);
			}

		// We append an extra element at the end of the list
		// so our loop catches the last summand.

		opList.AppendElement('+');
		opOffsetList.AppendElement(length);
		argOffsetList.AppendElement(length);

		// Parse each summand

		const JSize opCount = opList.GetElementCount();
		for (JIndex i=1; i<opCount; i++)
			{
			const JCharacter opType = opList.GetElement(i);
			const JSize startOffset = argOffsetList.GetElement(i);
			const JSize endOffset   = opOffsetList.GetElement(i+1);

			JFunction* arg = NULL;
			if (!JRecurseFunction(expr + startOffset, endOffset - startOffset,
								  theVariableList, &arg, allowUIF))
				{
				delete sum;
				return kJParseError;
				}
			else if (opType == '+')
				{
				sum->SetArg(i, arg);
				}
			else	// opType == '-'
				{
				JNegation* negArg = new JNegation(arg);
				assert( negArg != NULL );
				sum->SetArg(i, negArg);
				}
			}

		*theFunction = sum;
		return kJParsedOK;
		}
}
JIndex
JSummation::PrepareToRender
	(
	const JExprRenderer&	renderer,
	const JPoint&			upperLeft,
	const JSize				fontSize,
	JExprRectList*			rectList
	)
{
	// intialize our rectangle and midline

	JPoint argUpperLeft = upperLeft;

	JRect ourRect(upperLeft, argUpperLeft);
	ourRect.bottom += renderer.GetLineHeight(fontSize);
	JCoordinate ourMidline = ourRect.ycenter();
	const JCoordinate origMidline = ourMidline;

	// get rectangle for each argument

	const JSize spaceWidth = renderer.GetStringWidth(fontSize, " ");
	const JSize plusWidth  = renderer.GetStringWidth(fontSize, JPGetAdditionString());
	const JSize minusWidth = renderer.GetStringWidth(fontSize, JPGetSubtractionString());

	const JSize argCount = GetArgCount();
	{
	for (JIndex i=1; i<=argCount; i++)
		{
		JFunction* f   = this;
		JFunction* arg = GetArg(i);
		if (arg->GetType() == kJNegationType)
			{
			argUpperLeft.x += minusWidth + spaceWidth;
			if (i > 1)
				{
				argUpperLeft.x += spaceWidth;
				}
			f = arg;
			JNegation* neg = dynamic_cast<JNegation*>(arg);
			assert( neg != NULL );
			arg = neg->GetArg();
			}
		else if (i > 1)
			{
			argUpperLeft.x += plusWidth + 2*spaceWidth;
			}

		const JIndex argIndex =
			arg->PrepareToRender(renderer, argUpperLeft, fontSize, rectList);
		JRect argRect = rectList->GetRect(argIndex);
		argUpperLeft.x = argRect.right;

		if (ParenthesizeArgForRender(*f, *arg))
			{
			const JSize parenWidth = renderer.GetParenthesisWidth(argRect.height());
			rectList->ShiftRect(argIndex, parenWidth, 0);
			argRect = rectList->GetRect(argIndex);
			argUpperLeft.x += 2*parenWidth;
			ourRect.right   = argRect.right + parenWidth;
			}

		ourRect = JCovering(ourRect, argRect);
		const JCoordinate argMidline = rectList->GetMidline(argIndex);
		if (argMidline > ourMidline)
			{
			ourMidline = argMidline;
			}
		}
	}

	// adjust the argument rectangles so all the midlines are the same
	// (ourMidline is guaranteed to stay constant)

	if (argCount > 1 && ourMidline > origMidline)
		{
		for (JIndex i=1; i<=argCount; i++)
			{
			const JFunction* arg = GetArg(i);
			if (arg->GetType() == kJNegationType)
				{
				const JNegation* neg = dynamic_cast<const JNegation*>(arg);
				assert( neg != NULL );
				arg = neg->GetArg();
				}

			JIndex argIndex;
			const JBoolean found = rectList->FindFunction(arg, &argIndex);
			assert( found );
			rectList->SetMidline(argIndex, ourMidline);
			ourRect = JCovering(ourRect, rectList->GetRect(argIndex));
			}
		}

	// save our rectangle

	return rectList->AddRect(ourRect, ourMidline, fontSize, this);
}