1 module cassowary.SimplexSolver;
2 
3 import std.array;
4 import std.conv;
5 import std.stdio;
6 import std.math;
7 
8 import cassowary.Tableau;
9 import cassowary.AbstractVariable;
10 import cassowary.Constraint;
11 import cassowary.Variable;
12 import cassowary.Strength;
13 import cassowary.Point;
14 import cassowary.LinearExpression;
15 import cassowary.LinearInequality;
16 import cassowary.ObjectiveVariable;
17 import cassowary.set;
18 import cassowary.EditInfo;
19 import cassowary.Cl;
20 import cassowary.Error;
21 import cassowary.EditConstraint;
22 import cassowary.StayConstraint;
23 import cassowary.SlackVariable;
24 import cassowary.DummyVariable;
25 import cassowary.SymbolicWeight;
26 
27 
28 class ClSimplexSolver : ClTableau
29 {
30 	// Ctr initializes the fields, and creates the objective row
31 	this()
32 	{
33 		_resolve_pair = [0, 0];
34 		_objective = new ClObjectiveVariable("Z");
35 
36 		auto e = new ClLinearExpression();
37 		_rows[_objective] = e;
38 		_stkCedcns = [0];
39 
40 		traceprint("objective expr == " ~ rowExpression(_objective).toString());
41 	}
42 
43 	// Convenience function for creating a linear inequality constraint
44 	final ClSimplexSolver addLowerBound(ClAbstractVariable v, double lower)
45 	{
46 		auto cn = new ClLinearInequality(v, InequalityType.GEQ, new ClLinearExpression(lower));
47 		return addConstraint(cn);
48 	}
49 
50 	// Convenience function for creating a linear inequality constraint
51 	final ClSimplexSolver addUpperBound(ClAbstractVariable v, double upper)
52 	{
53 		auto cn = new ClLinearInequality(v, InequalityType.LEQ, new ClLinearExpression(upper));
54 		return addConstraint(cn);
55 	}
56 
57 	// Convenience function for creating a pair of linear inequality constraint
58 	final ClSimplexSolver addBounds(ClAbstractVariable v, double lower, double upper)
59 	{
60 		addLowerBound(v, lower); addUpperBound(v, upper); return this;
61 	}
62 
63 	// Add constraint "cn" to the solver
64 	final ClSimplexSolver addConstraint(ClConstraint cn)
65 	{
66 		fnenterprint("addConstraint: " ~ cn.toString());
67 
68 		ClAbstractVariable[] eplus_eminus;
69 		double prevEConstant = 0;
70 		ClLinearExpression expr = newExpression(cn,         /* output to: */
71 												eplus_eminus, prevEConstant);
72 		bool fAddedOkDirectly = false;
73 
74 		try
75 		{
76 			fAddedOkDirectly = tryAddingDirectly(expr);
77 			if (!fAddedOkDirectly)
78 			{
79 				// could not add directly
80 				addWithArtificialVariable(expr);
81 			}
82 		}
83 		catch (ClErrorRequiredFailure err)
84 		{
85 			///try {
86 			///        removeConstraint(cn); // FIXGJB
87 			//      } catch (ClErrorConstraintNotFound errNF) {
88 			// This should not possibly happen
89 			/// System.err.println("ERROR: could not find a constraint just added\n");
90 			///}
91 			throw err;
92 		}
93 
94 		_fNeedsSolving = true;
95 
96 		if (cn.isEditConstraint())
97 		{
98 			auto i = _editVarMap.length;
99 			ClEditConstraint cnEdit = cast(ClEditConstraint) cn;
100 			ClSlackVariable clvEplus = cast(ClSlackVariable) eplus_eminus[0];
101 			ClSlackVariable clvEminus = cast(ClSlackVariable) eplus_eminus[1];
102 
103 			_editVarMap[cnEdit.variable()] = new ClEditInfo(cnEdit, clvEplus, clvEminus,
104 															prevEConstant, i);
105 		}
106 
107 		if (autosolve)
108 		{
109 			optimize(_objective);
110 			setExternalVariables();
111 		}
112 
113 		return this;
114 	}
115 
116 	// Same as addConstraint, except returns false if the constraint
117 	// resulted in an unsolvable system (instead of throwing an exception)
118 	final bool addConstraintNoException(ClConstraint cn)
119 	{
120 		fnenterprint("addConstraintNoException: " ~ cn.toString());
121 
122 		try
123 		{
124 			addConstraint(cn);
125 			return true;
126 		}
127 		catch (ClErrorRequiredFailure e)
128 		{
129 			return false;
130 		}
131 	}
132 
133 	// Add an edit constraint for "v" with given strength
134 	final ClSimplexSolver addEditVar(ClVariable v, const ClStrength strength = ClStrength.strong)
135 	{
136 		try
137 		{
138 			ClEditConstraint cnEdit = new ClEditConstraint(v, strength);
139 			return addConstraint(cnEdit);
140 		}
141 		catch (ClErrorRequiredFailure e)
142 		{
143 			// should not get this
144 			throw new ClErrorInternal("Required failure when adding an edit variable");
145 		}
146 	}
147 
148 	// Remove the edit constraint previously added for variable v
149 	final ClSimplexSolver removeEditVar(ClVariable v)
150 	{
151 		ClEditInfo cei = _editVarMap[v];
152 		ClConstraint cn = cei.Constraint();
153 		removeConstraint(cn);
154 		return this;
155 	}
156 
157 	// beginEdit() should be called before sending
158 	// resolve() messages, after adding the appropriate edit variables
159 	final ClSimplexSolver beginEdit()
160 	{
161 		assert(_editVarMap.length > 0, "_editVarMap.length() > 0");
162 		// may later want to do more in here
163 		_infeasibleRows.clear();
164 		resetStayConstants();
165 		_stkCedcns ~= _editVarMap.length;
166 		return this;
167 	}
168 
169 	// endEdit should be called after editing has finished
170 	// for now, it just removes all edit variables
171 	final ClSimplexSolver endEdit()
172 	{
173 		assert(_editVarMap.length > 0, "_editVarMap.length() > 0");
174 		resolve();
175 		_stkCedcns.popBack();
176 		size_t n = _stkCedcns[$-1];
177 		removeEditVarsTo(n);
178 		// may later want to do more in here
179 		return this;
180 	}
181 
182 	// removeAllEditVars() just eliminates all the edit constraints
183 	// that were added
184 	final ClSimplexSolver removeAllEditVars()
185 	{
186 		return removeEditVarsTo(0);
187 	}
188 
189 	// remove the last added edit vars to leave only n edit vars left
190 	final ClSimplexSolver removeEditVarsTo(size_t n)
191 	{
192 		try
193 		{
194 			foreach(ClVariable v, ClEditInfo cei; _editVarMap)
195 			{
196 				if (cei.Index() >= n)
197 				{
198 					removeEditVar(v);
199 				}
200 			}
201 			assert(_editVarMap.length == n, "_editVarMap.length() == n");
202 
203 			return this;
204 		}
205 		catch (ClErrorConstraintNotFound e)
206 		{
207 			// should not get this
208 			throw new ClErrorInternal("Constraint not found in removeEditVarsTo");
209 		}
210 	}
211 
212 	// Add weak stays to the x and y parts of each point. These have
213 	// increasing weights so that the solver will try to satisfy the x
214 	// and y stays on the same point, rather than the x stay on one and
215 	// the y stay on another.
216 	final ClSimplexSolver addPointStays(ClPoint[] listOfPoints)
217 	{
218 		fnenterprint("addPointStays" ~ listOfPoints.to!string());
219 		double weight = 1.0;
220 		double multiplier = 2.0;
221 		foreach(p; listOfPoints)
222 		{
223 			addPointStay(p, weight);
224 			weight *= multiplier;
225 		}
226 		return this;
227 	}
228 
229 	final ClSimplexSolver addPointStay(ClVariable vx, ClVariable vy, double weight = 1)
230 	{
231 		addStay(vx, ClStrength.weak, weight);
232 		addStay(vy, ClStrength.weak, weight);
233 		return this;
234 	}
235 
236 	final ClSimplexSolver addPointStay(ClPoint clp, double weight = 1)
237 	{
238 		return addPointStay(clp.X(), clp.Y(), weight);
239 	}
240 
241 	// Add a stay of the given strength (default to weak) of v to the tableau
242 	final ClSimplexSolver addStay(ClVariable v, const ClStrength strength = ClStrength.weak, double weight = 1)
243 	{
244 		auto cn = new ClStayConstraint(v, strength, weight);
245 		return addConstraint(cn);
246 	}
247 
248 	// Remove the constraint cn from the tableau
249 	// Also remove any error variable associated with cn
250 	final ClSimplexSolver removeConstraint(ClConstraint cn)
251 	{
252 		fnenterprint("removeConstraint: " ~ cn.toString());
253 		traceprint(this.toString());
254 
255 		_fNeedsSolving = true;
256 
257 		resetStayConstants();
258 
259 		ClLinearExpression zRow = rowExpression(_objective);
260 
261 		auto eVars = _errorVars.get(cn, null);
262 		traceprint("eVars == " ~ eVars.to!string());
263 
264 		if (eVars !is null)
265 		{
266 			foreach(ClAbstractVariable clv; eVars)
267 			{
268 				ClLinearExpression expr = rowExpression(clv);
269 				if (expr is null )
270 				{
271 					zRow.addVariable(clv, -cn.weight() *
272 									 cn.strength().symbolicWeight.asDouble(),
273 									 _objective, this);
274 				}
275 				else                 // the error variable was in the basis
276 				{
277 					zRow.addExpression(expr, -cn.weight() *
278 									   cn.strength().symbolicWeight.asDouble(),
279 									   _objective, this);
280 				}
281 			}
282 		}
283 
284 		ClAbstractVariable marker = _markerVars.get(cn, null);
285 
286 		if (marker is null)
287 		{
288 			throw new ClErrorConstraintNotFound();
289 		}
290 
291 		_markerVars.remove(cn);
292 
293 		traceprint("Looking to remove var " ~ marker.toString());
294 
295 		if (rowExpression(marker) is null )
296 		{
297 			// not in the basis, so need to do some work
298 			auto col = _columns[marker];
299 
300 			traceprint("Must pivot -- columns are " ~ col.toString());
301 
302 			ClAbstractVariable exitVar = null;
303 			double minRatio = 0.0;
304 			foreach(ClAbstractVariable v; col)
305 			{
306 				if (v.isRestricted() )
307 				{
308 					ClLinearExpression expr = rowExpression( v);
309 					double coeff = expr.coefficientFor(marker);
310 					traceprint("Marker " ~ marker.to!string() ~ "'s coefficient in " ~ expr.toString() ~ " is " ~ coeff.to!string());
311 					if (coeff < 0.0)
312 					{
313 						double r = -expr.constant() / coeff;
314 						if (exitVar is null || r < minRatio)
315 						{
316 							minRatio = r;
317 							exitVar = v;
318 						}
319 					}
320 				}
321 			}
322 			if (exitVar is null )
323 			{
324 				traceprint("exitVar is still null");
325 				foreach (ClAbstractVariable v; col)
326 				{
327 					if (v.isRestricted() )
328 					{
329 						ClLinearExpression expr = rowExpression(v);
330 						double coeff = expr.coefficientFor(marker);
331 						double r = expr.constant() / coeff;
332 						if (exitVar is null || r < minRatio)
333 						{
334 							minRatio = r;
335 							exitVar = v;
336 						}
337 					}
338 				}
339 			}
340 
341 			if (exitVar is null)
342 			{
343 				// exitVar is still null
344 				if (col.length == 0)
345 				{
346 					removeColumn(marker);
347 				}
348 				else
349 				{
350 					exitVar = col.anyElement;
351 				}
352 			}
353 
354 			if (exitVar !is null)
355 			{
356 				pivot(marker, exitVar);
357 			}
358 		}
359 
360 		if (rowExpression(marker) !is null )
361 		{
362 			ClLinearExpression expr = removeRow(marker);
363 			expr = null;
364 		}
365 
366 		if (eVars !is null)
367 		{
368 			foreach(ClAbstractVariable v; eVars)
369 			{
370 				// FIXGJBNOW != or equals?
371 				if ( v != marker )
372 				{
373 					removeColumn(v);
374 					v = null;
375 				}
376 			}
377 		}
378 
379 		if (cn.isStayConstraint())
380 		{
381 			if (eVars !is null)
382 			{
383 				for (int i = 0; i < _stayPlusErrorVars.length; i++)
384 				{
385 					eVars.remove(_stayPlusErrorVars[i]);
386 					eVars.remove(_stayMinusErrorVars[i]);
387 				}
388 			}
389 		}
390 		else if (cn.isEditConstraint())
391 		{
392 			assert(eVars !is null, "eVars != null");
393 			ClEditConstraint cnEdit = cast(ClEditConstraint) cn;
394 			ClVariable clv = cnEdit.variable();
395 			ClEditInfo cei = _editVarMap[clv];
396 			ClSlackVariable clvEditMinus = cei.ClvEditMinus();
397 			//      ClSlackVariable clvEditPlus = cei.ClvEditPlus();
398 			// the clvEditPlus is a marker variable that is removed elsewhere
399 			removeColumn( clvEditMinus );
400 			_editVarMap.remove(clv);
401 		}
402 
403 		// FIXGJB do the remove at top
404 		if (eVars !is null)
405 		{
406 			_errorVars.remove(cn);
407 		}
408 		marker = null;
409 
410 		if (autosolve)
411 		{
412 			optimize(_objective);
413 			setExternalVariables();
414 		}
415 
416 		return this;
417 	}
418 
419 	// Re-initialize this solver from the original constraints, thus
420 	// getting rid of any accumulated numerical problems.  (Actually, we
421 	// haven't definitely observed any such problems yet)
422 	final void reset()
423 	{
424 		fnenterprint("reset");
425 		throw new ClErrorInternal("reset not implemented");
426 	}
427 
428 	// Re-solve the current collection of constraints for new values for
429 	// the constants of the edit variables.
430 	// DEPRECATED:  use suggestValue(...) then resolve()
431 	// If you must use this, be sure to not use it if you
432 	// remove an edit variable (or edit constraint) from the middle
433 	// of a list of edits and then try to resolve with this function
434 	// (you'll get the wrong answer, because the indices will be wrong
435 	// in the ClEditInfo objects)
436 	final void resolve(double[] newEditConstants)
437 	{
438 		fnenterprint("resolve" ~ newEditConstants.to!string());
439 		foreach(ClVariable v, ClEditInfo cei; _editVarMap)
440 		{
441 			auto i = cei.Index();
442 			try
443 			{
444 				if (i < newEditConstants.length)
445 					suggestValue(v, newEditConstants[i]);
446 			}
447 			catch (ClError err)
448 			{
449 				throw new ClErrorInternal("Error during resolve");
450 			}
451 		}
452 		resolve();
453 	}
454 
455 	// Convenience function for resolve-s of two variables
456 	final void resolve(double x, double y)
457 	{
458 		_resolve_pair[0] = x;
459 		_resolve_pair[1] = y;
460 		resolve(_resolve_pair);
461 	}
462 
463 	// Re-solve the cuurent collection of constraints, given the new
464 	// values for the edit variables that have already been
465 	// suggested (see suggestValue() method)
466 	final void resolve()
467 	{
468 		fnenterprint("resolve()");
469 		dualOptimize();
470 		setExternalVariables();
471 		_infeasibleRows.clear();
472 		resetStayConstants();
473 	}
474 
475 	// Suggest a new value for an edit variable
476 	// the variable needs to be added as an edit variable
477 	// and beginEdit() needs to be called before this is called.
478 	// The tableau will not be solved completely until
479 	// after resolve() has been called
480 	final ClSimplexSolver suggestValue(ClVariable v, double x)
481 	{
482 		fnenterprint("suggestValue(" ~ v.toString() ~ ", " ~ x.to!string() ~ ")");
483 		ClEditInfo cei = _editVarMap.get(v, null);
484 		if (cei is null)
485 		{
486 			writeln("suggestValue for variable " ~ v.toString() ~ ", but var is not an edit variable\n");
487 			throw new ClError();
488 		}
489 
490 		ClSlackVariable clvEditPlus = cei.ClvEditPlus();
491 		ClSlackVariable clvEditMinus = cei.ClvEditMinus();
492 		double delta = x - cei.PrevEditConstant();
493 		cei.SetPrevEditConstant(x);
494 		deltaEditConstant(delta, clvEditPlus, clvEditMinus);
495 		return this;
496 	}
497 
498 	// If autosolving has been turned off, client code needs
499 	// to explicitly call solve() before accessing variables
500 	// values
501 	final ClSimplexSolver solve()
502 	{
503 		if (_fNeedsSolving)
504 		{
505 			optimize(_objective);
506 			setExternalVariables();
507 		}
508 		return this;
509 	}
510 
511 	ClSimplexSolver setEditedValue(ClVariable v, double n)
512 	{
513 		if (!containsVariable(v))
514 		{
515 			v.change_value(n);
516 			return this;
517 		}
518 
519 		if (!approxEqual(n, v.value()))
520 		{
521 			addEditVar(v);
522 			beginEdit();
523 			try
524 			{
525 				suggestValue(v, n);
526 			}
527 			catch (ClError e)
528 			{
529 				// just added it above, so we shouldn't get an error
530 				throw new ClErrorInternal("Error in setEditedValue");
531 			}
532 			endEdit();
533 		}
534 		return this;
535 	}
536 
537 	final bool containsVariable(ClVariable v)
538 	{
539 		return columnsHasKey(v) || (rowExpression(v) !is null);
540 	}
541 
542 	ClSimplexSolver addVar(ClVariable v)
543 	{
544 		if (!containsVariable(v))
545 		{
546 			try
547 			{
548 				addStay(v);
549 			}
550 			catch (ClErrorRequiredFailure e)
551 			{
552 				// cannot have a required failure, since we add w/ weak
553 				throw new ClErrorInternal("Error in addVar -- required failure is impossible");
554 			}
555 			traceprint("added initial stay on " ~ v.toString());
556 		}
557 		return this;
558 	}
559 
560 	// Originally from Michael Noth <noth@cs>
561 	override string getInternalInfo()
562 	{
563 		string res = super.getInternalInfo();
564 		res ~= "\nSolver info:\n";
565 		res ~= "Stay Error Variables: ";
566 		res ~= (_stayPlusErrorVars.length - _stayMinusErrorVars.length).to!string();
567 		res ~= " (" ~ _stayPlusErrorVars.length.to!string() ~ " +, ";
568 		res ~= _stayMinusErrorVars.length.to!string() ~ " -)\n";
569 		res ~= "Edit Variables: " ~ _editVarMap.length.to!string();
570 		res ~= "\n";
571 		return res;
572 	}
573 
574 	final string getDebugInfo()
575 	{
576 		string res = toString();
577 		res ~= getInternalInfo();
578 		return res;
579 	}
580 
581 	override string toString() const
582 	{
583 		string res = super.toString();
584 		res ~= "\n_stayPlusErrorVars: ";
585 		res ~= _stayPlusErrorVars.to!string();
586 		res ~= "\n_stayMinusErrorVars: ";
587 		res ~= _stayMinusErrorVars.to!string();
588 
589 		res ~= "\n";
590 		return res;
591 	}
592 
593 	auto getConstraintMap()
594 	{
595 		return _markerVars;
596 	}
597 
598 	//// END PUBLIC INTERFACE
599 
600 	// Add the constraint expr=0 to the inequality tableau using an
601 	// artificial variable.  To do this, create an artificial variable
602 	// av and add av=expr to the inequality tableau, then make av be 0.
603 	// (Raise an exception if we can't attain av=0.)
604 	protected final void addWithArtificialVariable(ClLinearExpression expr)
605 	{
606 		fnenterprint("addWithArtificialVariable: " ~ expr.toString());
607 
608 		ClSlackVariable av = new ClSlackVariable(++_artificialCounter, "a");
609 		ClObjectiveVariable az = new ClObjectiveVariable("az");
610 		ClLinearExpression azRow = cast(ClLinearExpression) expr.clone();
611 
612 		traceprint("before addRows:\n" ~ toString());
613 
614 		addRow(az, azRow);
615 		addRow(av, expr);
616 
617 		traceprint("after addRows:\n" ~ toString());
618 		optimize(az);
619 
620 		ClLinearExpression azTableauRow = rowExpression(az);
621 
622 		traceprint("azTableauRow.constant() == " ~ azTableauRow.constant().to!string());
623 
624 		if (!approxEqual(azTableauRow.constant(), 0.0))
625 		{
626 			removeRow(az);
627 			removeColumn(av);
628 			throw new ClErrorRequiredFailure();
629 		}
630 
631 		// See if av is a basic variable
632 		ClLinearExpression e = rowExpression(av);
633 
634 		if (e !is null)
635 		{
636 			// find another variable in this row and pivot,
637 			// so that av becomes parametric
638 			if (e.isConstant())
639 			{
640 				// if there isn't another variable in the row
641 				// then the tableau contains the equation av=0 --
642 				// just delete av's row
643 				removeRow(av);
644 				removeRow(az);
645 				return;
646 			}
647 			ClAbstractVariable entryVar = e.anyPivotableVariable();
648 			pivot( entryVar, av);
649 		}
650 		assert(rowExpression(av) is null, "rowExpression(av) is null");
651 		removeColumn(av);
652 		removeRow(az);
653 	}
654 
655 	// We are trying to add the constraint expr=0 to the appropriate
656 	// tableau.  Try to add expr directly to the tableax without
657 	// creating an artificial variable.  Return true if successful and
658 	// false if not.
659 	protected final bool tryAddingDirectly(ClLinearExpression expr)
660 	{
661 		fnenterprint("tryAddingDirectly: " ~ expr.toString() );
662 		ClAbstractVariable subject = chooseSubject(expr);
663 		if (subject is null )
664 		{
665 			fnexitprint("returning false");
666 			return false;
667 		}
668 		expr.newSubject( subject);
669 		if (columnsHasKey( subject))
670 		{
671 			substituteOut( subject, expr);
672 		}
673 		addRow( subject, expr);
674 		fnexitprint("returning true");
675 		return true;         // successfully added directly
676 	}
677 
678 	// We are trying to add the constraint expr=0 to the tableaux.  Try
679 	// to choose a subject (a variable to become basic) from among the
680 	// current variables in expr.  If expr contains any unrestricted
681 	// variables, then we must choose an unrestricted variable as the
682 	// subject.  Also, if the subject is new to the solver we won't have
683 	// to do any substitutions, so we prefer new variables to ones that
684 	// are currently noted as parametric.  If expr contains only
685 	// restricted variables, if there is a restricted variable with a
686 	// negative coefficient that is new to the solver we can make that
687 	// the subject.  Otherwise we can't find a subject, so return nil.
688 	// (In this last case we have to add an artificial variable and use
689 	// that variable as the subject -- this is done outside this method
690 	// though.)
691 	//
692 	// Note: in checking for variables that are new to the solver, we
693 	// ignore whether a variable occurs in the objective function, since
694 	// new slack variables are added to the objective function by
695 	// 'newExpression:', which is called before this method.
696 	protected final ClAbstractVariable chooseSubject(ClLinearExpression expr)
697 	{
698 		fnenterprint("chooseSubject: " ~ expr.toString());
699 		ClAbstractVariable subject = null;         // the current best subject, if any
700 
701 		bool foundUnrestricted = false;
702 		bool foundNewRestricted = false;
703 
704 		auto terms = expr.terms();
705 
706 		foreach(ClAbstractVariable v, double c; terms)
707 		{
708 			if (foundUnrestricted)
709 			{
710 				if (!v.isRestricted())
711 				{
712 					if (!columnsHasKey(v))
713 						return v;
714 				}
715 			}
716 			else
717 			{
718 				// we haven't found an restricted variable yet
719 				if (v.isRestricted())
720 				{
721 					if (!foundNewRestricted && !v.isDummy() && c < 0.0)
722 					{
723 						auto col = _columns.get(v, null);
724 						if (col is null ||
725 							(col.length == 1 && columnsHasKey(_objective)))
726 						{
727 							subject = v;
728 							foundNewRestricted = true;
729 						}
730 					}
731 				}
732 				else
733 				{
734 					subject = v;
735 					foundUnrestricted = true;
736 				}
737 			}
738 		}
739 
740 		if (subject !is null)
741 			return subject;
742 
743 		double coeff = 0.0;
744 
745 		foreach(ClAbstractVariable v, double c; terms)
746 		{
747 			if (!v.isDummy())
748 				return null;                 // nope, no luck
749 			if (!columnsHasKey(v))
750 			{
751 				subject = v;
752 				coeff = c;
753 			}
754 		}
755 
756 		if (!approxEqual(expr.constant(), 0.0))
757 		{
758 			throw new ClErrorRequiredFailure();
759 		}
760 		if (coeff > 0.0)
761 		{
762 			expr.multiplyMe(-1);
763 		}
764 		return subject;
765 	}
766 
767 	// Each of the non-required edits will be represented by an equation
768 	// of the form
769 	//    v = c + eplus - eminus
770 	// where v is the variable with the edit, c is the previous edit
771 	// value, and eplus and eminus are slack variables that hold the
772 	// error in satisfying the edit constraint.  We are about to change
773 	// something, and we want to fix the constants in the equations
774 	// representing the edit constraints.  If one of eplus and eminus is
775 	// basic, the other must occur only in the expression for that basic
776 	// error variable.  (They can't both be basic.)  Fix the constant in
777 	// this expression.  Otherwise they are both nonbasic.  Find all of
778 	// the expressions in which they occur, and fix the constants in
779 	// those.  See the UIST paper for details.
780 	// (This comment was for resetEditConstants(), but that is now
781 	// gone since it was part of the screwey vector-based interface
782 	// to resolveing. --02/16/99 gjb)
783 	protected final void deltaEditConstant(double delta,
784 										   ClAbstractVariable plusErrorVar,
785 										   ClAbstractVariable minusErrorVar)
786 	{
787 		fnenterprint("deltaEditConstant :" ~ delta.to!string() ~ ", " ~ plusErrorVar.toString() ~ ", " ~ minusErrorVar.toString());
788 		ClLinearExpression exprPlus = rowExpression(plusErrorVar);
789 		if (exprPlus !is null )
790 		{
791 			exprPlus.incrementConstant(delta);
792 
793 			if (exprPlus.constant() < 0.0)
794 			{
795 				_infeasibleRows ~= plusErrorVar;
796 			}
797 			return;
798 		}
799 
800 		ClLinearExpression exprMinus = rowExpression(minusErrorVar);
801 		if (exprMinus !is null)
802 		{
803 			exprMinus.incrementConstant(-delta);
804 			if (exprMinus.constant() < 0.0)
805 			{
806 				_infeasibleRows ~= minusErrorVar;
807 			}
808 			return;
809 		}
810 
811 		auto columnVars = _columns[minusErrorVar];
812 
813 		foreach(ClAbstractVariable basicVar; columnVars)
814 		{
815 			ClLinearExpression expr = rowExpression(basicVar);
816 			//assert(expr != null, "expr != null" );
817 			double c = expr.coefficientFor(minusErrorVar);
818 			expr.incrementConstant(c * delta);
819 			if (basicVar.isRestricted() && expr.constant() < 0.0)
820 			{
821 				_infeasibleRows ~= basicVar;
822 			}
823 		}
824 	}
825 
826 	// We have set new values for the constants in the edit constraints.
827 	// Re-optimize using the dual simplex algorithm.
828 	protected final void dualOptimize()
829 	{
830 		fnenterprint("dualOptimize:");
831 		ClLinearExpression zRow = rowExpression(_objective);
832 		while (!_infeasibleRows.isEmpty())
833 		{
834 			ClAbstractVariable exitVar = _infeasibleRows.anyElement;
835 			_infeasibleRows.remove(exitVar);
836 			ClAbstractVariable entryVar = null;
837 			ClLinearExpression expr = rowExpression(exitVar);
838 			if (expr !is null )
839 			{
840 				if (expr.constant() < 0.0)
841 				{
842 					double ratio = double.max;
843 					foreach(ClAbstractVariable v, double c; expr.terms())
844 					{
845 						if (c > 0.0 && v.isPivotable())
846 						{
847 							double zc = zRow.coefficientFor(v);
848 							double r = zc/c;                             // FIXGJB r:= zc/c or zero, as ClSymbolicWeight-s
849 							if (r < ratio)
850 							{
851 								entryVar = v;
852 								ratio = r;
853 							}
854 						}
855 					}
856 					if (ratio == double.max)
857 					{
858 						throw new ClErrorInternal("ratio == nil (MAX_VALUE) in dualOptimize");
859 					}
860 					pivot( entryVar, exitVar);
861 				}
862 			}
863 		}
864 	}
865 
866 	// Make a new linear expression representing the constraint cn,
867 	// replacing any basic variables with their defining expressions.
868 	// Normalize if necessary so that the constant is non-negative.  If
869 	// the constraint is non-required give its error variables an
870 	// appropriate weight in the objective function.
871 	protected final ClLinearExpression newExpression(ClConstraint cn,
872 													 ref ClAbstractVariable[] eplus_eminus,
873 													 ref double prevEConstant)
874 	{
875 		fnenterprint("newExpression: " ~ cn.toString());
876 		traceprint("cn.isInequality() == " ~ cn.isInequality().to!string());
877 		traceprint("cn.isRequired() == " ~ cn.isRequired().to!string());
878 
879 		ClLinearExpression cnExpr = cn.expression();
880 		ClLinearExpression expr = new ClLinearExpression(cnExpr.constant());
881 		ClSlackVariable slackVar = new ClSlackVariable();
882 		ClDummyVariable dummyVar = new ClDummyVariable();
883 		ClSlackVariable eminus = new ClSlackVariable();
884 		ClSlackVariable eplus = new ClSlackVariable();
885 		foreach(ClAbstractVariable v, double c; cnExpr.terms())
886 		{
887 			ClLinearExpression e = rowExpression(v);
888 			if (e is null)
889 				expr.addVariable(v, c);
890 			else
891 				expr.addExpression(e, c);
892 		}
893 
894 		import std.stdio;
895 
896 		if (cn.isInequality())
897 		{
898 			++_slackCounter;
899 			slackVar = new ClSlackVariable(_slackCounter, "s");
900 			expr.setVariable(slackVar, -1);
901 			_markerVars[cn] = slackVar;
902 			if (!cn.isRequired())
903 			{
904 				++_slackCounter;
905 				eminus = new ClSlackVariable(_slackCounter, "em");
906 				expr.setVariable(eminus, 1.0);
907 				ClLinearExpression zRow = rowExpression(_objective);
908 				ClSymbolicWeight sw = cn.strength().symbolicWeight.times(cn.weight());
909 				zRow.setVariable( eminus, sw.asDouble());
910 				insertErrorVar(cn, eminus);
911 				noteAddedVariable(eminus, _objective);
912 			}
913 		}
914 		else
915 		{
916 			// cn is an equality
917 			if (cn.isRequired())
918 			{
919 				++_dummyCounter;
920 				dummyVar = new ClDummyVariable(_dummyCounter, "d");
921 				expr.setVariable(dummyVar, 1.0);
922 				_markerVars[cn] = dummyVar;
923 				traceprint("Adding dummyVar == d" ~ _dummyCounter.to!string());
924 			}
925 			else
926 			{
927 				++_slackCounter;
928 
929 				eplus = new ClSlackVariable(_slackCounter, "ep");
930 				eminus = new ClSlackVariable(_slackCounter, "em");
931 
932 				expr.setVariable(eplus, -1.0);
933 				expr.setVariable(eminus, 1.0);
934 
935 				_markerVars[cn] = eplus;
936 				ClLinearExpression zRow = rowExpression(_objective);
937 				ClSymbolicWeight sw = cn.strength().symbolicWeight.times(cn.weight());
938 				double swCoeff = sw.asDouble();
939 
940 				if (swCoeff == 0)
941 				{
942 					traceprint("sw == " ~ sw.to!string());
943 					traceprint("cn == " ~ cn.to!string());
944 					traceprint("adding " ~ eplus.to!string() ~ " and " ~ eminus.to!string() ~ " with swCoeff == " ~ swCoeff.to!string());
945 				}
946 
947 				zRow.setVariable(eplus, swCoeff);
948 				noteAddedVariable(eplus, _objective);
949 				zRow.setVariable(eminus, swCoeff);
950 
951 				noteAddedVariable(eminus, _objective);
952 
953 				insertErrorVar(cn, eminus);
954 
955 				insertErrorVar(cn, eplus);
956 
957 				if (cn.isStayConstraint())
958 				{
959 					_stayPlusErrorVars ~= eplus;
960 					_stayMinusErrorVars ~= eminus;
961 				}
962 				else if (cn.isEditConstraint())
963 				{
964 					eplus_eminus ~= eplus;
965 					eplus_eminus ~= eminus;
966 					prevEConstant = cnExpr.constant();
967 				}
968 			}
969 		}
970 
971 		if (expr.constant() < 0)
972 			expr.multiplyMe(-1);
973 
974 		fnexitprint("returning " ~ expr.to!string());
975 		return expr;
976 	}
977 
978 	// Minimize the value of the objective.  (The tableau should already
979 	// be feasible.)
980 	protected final void optimize(ClObjectiveVariable zVar)
981 	{
982 		fnenterprint("optimize: " ~ zVar.toString());
983 		traceprint(this.toString());
984 
985 		ClLinearExpression zRow = rowExpression(zVar);
986 		assert(zRow !is null, "zRow != null");
987 		ClAbstractVariable entryVar = null;
988 		ClAbstractVariable exitVar = null;
989 		while (true)
990 		{
991 			double objectiveCoeff = 0;
992 			foreach(ClAbstractVariable v, double c; zRow.terms())
993 			{
994 				if (v.isPivotable() && c < objectiveCoeff)
995 				{
996 					objectiveCoeff = c;
997 					entryVar = v;
998 				}
999 			}
1000 			if (objectiveCoeff >= -_epsilon || entryVar is null)
1001 				return;
1002 			traceprint("entryVar == " ~ entryVar.to!string() ~ ", objectiveCoeff == " ~ objectiveCoeff.to!string());
1003 
1004 			double minRatio = double.max;
1005 			double r = 0.0;
1006 			foreach(ClAbstractVariable v; _columns[entryVar])
1007 			{
1008 				traceprint("Checking " ~ v.toString());
1009 				if (v.isPivotable())
1010 				{
1011 					ClLinearExpression expr = rowExpression(v);
1012 					double coeff = expr.coefficientFor(entryVar);
1013 					traceprint("pivotable, coeff = " ~ coeff.to!string());
1014 					if (coeff < 0.0)
1015 					{
1016 						r = -expr.constant() / coeff;
1017 						if (r < minRatio)
1018 						{
1019 							traceprint("New minratio == " ~ r.to!string());
1020 							minRatio = r;
1021 							exitVar = v;
1022 						}
1023 					}
1024 				}
1025 			}
1026 			if (minRatio == double.max)
1027 			{
1028 				throw new ClErrorInternal("Objective function is unbounded in optimize");
1029 			}
1030 			pivot(entryVar, exitVar);
1031 			traceprint(this.toString());
1032 		}
1033 	}
1034 
1035 	// Do a pivot.  Move entryVar into the basis (i.e. make it a basic variable),
1036 	// and move exitVar out of the basis (i.e., make it a parametric variable)
1037 	protected final void pivot(ClAbstractVariable entryVar,
1038 							   ClAbstractVariable exitVar)
1039 	{
1040 		fnenterprint("pivot: " ~ entryVar.toString() ~ ", " ~ exitVar.toString());
1041 
1042 		// the entryVar might be non-pivotable if we're doing a removeConstraint --
1043 		// otherwise it should be a pivotable variable -- enforced at call sites,
1044 		// hopefully
1045 
1046 		ClLinearExpression pexpr = removeRow(exitVar);
1047 
1048 		pexpr.changeSubject(exitVar, entryVar);
1049 		substituteOut(entryVar, pexpr);
1050 		addRow(entryVar, pexpr);
1051 	}
1052 
1053 	// Each of the non-required stays will be represented by an equation
1054 	// of the form
1055 	//     v = c + eplus - eminus
1056 	// where v is the variable with the stay, c is the previous value of
1057 	// v, and eplus and eminus are slack variables that hold the error
1058 	// in satisfying the stay constraint.  We are about to change
1059 	// something, and we want to fix the constants in the equations
1060 	// representing the stays.  If both eplus and eminus are nonbasic
1061 	// they have value 0 in the current solution, meaning the previous
1062 	// stay was exactly satisfied.  In this case nothing needs to be
1063 	// changed.  Otherwise one of them is basic, and the other must
1064 	// occur only in the expression for that basic error variable.
1065 	// Reset the constant in this expression to 0.
1066 	protected final void resetStayConstants()
1067 	{
1068 		fnenterprint("resetStayConstants");
1069 
1070 		for (int i = 0; i < _stayPlusErrorVars.length; i++)
1071 		{
1072 			ClLinearExpression expr = rowExpression(_stayPlusErrorVars[i]);
1073 			if (expr is null )
1074 				expr = rowExpression(_stayMinusErrorVars[i]);
1075 			if (expr !is null)
1076 				expr.set_constant(0.0);
1077 		}
1078 	}
1079 
1080 	// Set the external variables known to this solver to their appropriate values.
1081 	// Set each external basic variable to its value, and set each
1082 	// external parametric variable to 0.  (It isn't clear that we will
1083 	// ever have external parametric variables -- every external
1084 	// variable should either have a stay on it, or have an equation
1085 	// that defines it in terms of other external variables that do have
1086 	// stays.  For the moment I'll put this in though.)  Variables that
1087 	// are internal to the solver don't actually store values -- their
1088 	// values are just implicit in the tableu -- so we don't need to set
1089 	// them.
1090 	protected final void setExternalVariables()
1091 	{
1092 		fnenterprint("setExternalVariables:");
1093 		traceprint(this.toString());
1094 
1095 		foreach(ClAbstractVariable v; _externalParametricVars)
1096 		{
1097 			if (rowExpression(v) !is null)
1098 			{
1099 				writeln("Error: variable" ~ v.toString() ~
1100 						" in _externalParametricVars is basic");
1101 				continue;
1102 			}
1103 			(cast(ClVariable)v).change_value(0.0);
1104 		}
1105 
1106 		foreach(ClAbstractVariable v; _externalRows)
1107 		{
1108 			ClLinearExpression expr = rowExpression(v);
1109 			debugprint("v == " ~ v.toString());
1110 			debugprint("expr == " ~ expr.toString());
1111 			(cast(ClVariable)v).change_value(expr.constant());
1112 		}
1113 
1114 		_fNeedsSolving = false;
1115 	}
1116 
1117 	// Protected convenience function to insert an error variable into
1118 	// the _errorVars set, creating the mapping with put as necessary
1119 	protected final void insertErrorVar(ClConstraint cn, ClAbstractVariable var)
1120 	{
1121 		fnenterprint("insertErrorVar:" ~ cn.toString() ~ ", " ~ var.toString());
1122 
1123 		auto cnset = _errorVars.get(cn, null);
1124 		if (cnset is null)
1125 		{
1126 			cnset = new typeof(cnset)();
1127 			_errorVars[cn] = cnset;
1128 		}
1129 		cnset ~= var;
1130 	}
1131 
1132 	// Control whether optimization and setting of external variables
1133 	// is done automatically or not.  By default it is done
1134 	// automatically and solve() never needs to be explicitly
1135 	// called by client code; if autosolve is put to false,
1136 	// then solve() needs to be invoked explicitly before using
1137 	// variables' values
1138 	// (Turning off autosolve while adding lots and lots of
1139 	// constraints [ala the addDel test in ClTests] saved
1140 	// about 20% in runtime, from 68sec to 54sec for 900 constraints,
1141 	// with 126 failed adds)
1142 	bool autosolve = true;
1143 
1144 	//// BEGIN PRIVATE INSTANCE FIELDS
1145 
1146 	// the arrays of positive and negative error vars for the stay constraints
1147 	// (need both positive and negative since they have only non-negative values)
1148 	private ClAbstractVariable[] _stayMinusErrorVars;
1149 	private ClAbstractVariable[] _stayPlusErrorVars;
1150 
1151 	// give error variables for a non required constraint,
1152 	// maps to ClSlackVariable-s
1153 	private Set!ClAbstractVariable[ClConstraint] _errorVars;     // map ClConstraint to Set (of ClVariable)
1154 
1155 
1156 	// Return a lookup table giving the marker variable for each
1157 	// constraint (used when deleting a constraint).
1158 	private ClAbstractVariable[ClConstraint] _markerVars;     // map ClConstraint to ClVariable
1159 
1160 	private ClObjectiveVariable _objective;
1161 
1162 	// Map edit variables to ClEditInfo-s.
1163 	// ClEditInfo instances contain all the information for an
1164 	// edit constraint (the edit plus/minus vars, the index [for old-style
1165 	// resolve(Vector...) interface], and the previous value.
1166 	// (ClEditInfo replaces the parallel vectors from the Smalltalk impl.)
1167 	private ClEditInfo[ClVariable] _editVarMap;     // map ClVariable to a ClEditInfo
1168 
1169 	private long _slackCounter = 0;
1170 	private long _artificialCounter = 0;
1171 	private long _dummyCounter = 0;
1172 
1173 	private double[] _resolve_pair;
1174 
1175 	private enum double _epsilon = 1e-8;
1176 
1177 	private bool _fNeedsSolving = false;
1178 
1179 	private size_t[] _stkCedcns;
1180 }