xref: /haiku/src/kits/interface/layouter/ComplexLayouter.cpp (revision b671e9bbdbd10268a042b4f4cc4317ccd03d105e)
1 /*
2  * Copyright 2007, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
3  * All rights reserved. Distributed under the terms of the MIT License.
4  */
5 
6 #include "ComplexLayouter.h"
7 
8 #include <math.h>
9 #include <stdio.h>
10 #include <string.h>
11 
12 #include <new>
13 
14 #include <OS.h>
15 #include <Size.h>
16 
17 #include <AutoDeleter.h>
18 
19 #include "LayoutOptimizer.h"
20 #include "SimpleLayouter.h"
21 
22 
23 //#define TRACE_COMPLEX_LAYOUTER	1
24 #if TRACE_COMPLEX_LAYOUTER
25 #	define TRACE(format...)	printf(format);
26 #	define TRACE_ONLY(x)	x
27 #else
28 #	define TRACE(format...)
29 #	define TRACE_ONLY(x)
30 #endif
31 
32 using std::nothrow;
33 
34 
35 // MyLayoutInfo
36 class ComplexLayouter::MyLayoutInfo : public LayoutInfo {
37 public:
38 	MyLayoutInfo(int32 elementCount, int32 spacing)
39 		: fCount(elementCount),
40 		  fSpacing(spacing)
41 	{
42 		// We also store the location of the virtual elementCountth element.
43 		// Thus fLocation[i + 1] - fLocation[i] is the size of the ith element
44 		// (not considering spacing).
45 		fLocations = new(nothrow) int32[elementCount + 1];
46 	}
47 
48 	~MyLayoutInfo()
49 	{
50 		delete[] fLocations;
51 	}
52 
53 	void InitFromSizes(int32* sizes)
54 	{
55 		fLocations[0] = 0;
56 		for (int32 i = 0; i < fCount; i++)
57 			fLocations[i + 1] = fLocations[i] + sizes[i] + fSpacing;
58 	}
59 
60 	virtual float ElementLocation(int32 element)
61 	{
62 		if (element < 0 || element >= fCount)
63 			return 0;
64 
65 		return fLocations[element];
66 	}
67 
68 	virtual float ElementSize(int32 element)
69 	{
70 		if (element < 0 || element >= fCount)
71 			return -1;
72 
73 		return fLocations[element + 1] - fLocations[element] - 1
74 			- fSpacing;
75 	}
76 
77 	virtual float ElementRangeSize(int32 position, int32 length)
78 	{
79 		if (position < 0 || length < 0 || position + length > fCount)
80 			return -1;
81 
82 		return fLocations[position + length] - fLocations[position] - 1
83 			- fSpacing;
84 	}
85 
86 	void Dump()
87 	{
88 		printf("ComplexLayouter::MyLayoutInfo(): %ld elements:\n", fCount);
89 		for (int32 i = 0; i < fCount + 1; i++)
90 			printf("  %2ld: location: %4ld\n", i, fLocations[i]);
91 	}
92 
93 public:
94 	int32	fCount;
95 	int32	fSpacing;
96 	int32*	fLocations;
97 };
98 
99 
100 // Constraint
101 struct ComplexLayouter::Constraint {
102 	Constraint(int32 start, int32 end, int32 min, int32 max)
103 		: start(start),
104 		  end(end),
105 		  min(min),
106 		  max(max),
107 		  next(NULL)
108 	{
109 		if (min > max)
110 			max = min;
111 		effectiveMax = max;
112 	}
113 
114 	void Restrict(int32 newMin, int32 newMax)
115 	{
116 		if (newMin > min)
117 			min = newMin;
118 		if (newMax < max)
119 			max = newMax;
120 		if (min > max)
121 			max = min;
122 		effectiveMax = max;
123 	}
124 
125 	bool IsSatisfied(int32* sumValues) const
126 	{
127 		int32 value = sumValues[end] - sumValues[start - 1];
128 		return (value >= min && value <= max);
129 	}
130 
131 	int32		start;
132 	int32		end;
133 	int32		min;
134 	int32		max;
135 	int32		effectiveMax;
136 	Constraint*	next;
137 };
138 
139 
140 // SumItem
141 struct ComplexLayouter::SumItem {
142 	int32	min;
143 	int32	max;
144 	bool	minDirty;
145 	bool	maxDirty;
146 };
147 
148 
149 // SumItemBackup
150 struct ComplexLayouter::SumItemBackup {
151 	int32	min;
152 	int32	max;
153 };
154 
155 
156 // #pragma mark - ComplexLayouter
157 
158 
159 // constructor
160 ComplexLayouter::ComplexLayouter(int32 elementCount, float spacing)
161 	: fElementCount(elementCount),
162 	  fSpacing((int32)spacing),
163 	  fConstraints(new(nothrow) Constraint*[elementCount]),
164 	  fWeights(new(nothrow) float[elementCount]),
165 	  fSums(new(nothrow) SumItem[elementCount + 1]),
166 	  fSumBackups(new(nothrow) SumItemBackup[elementCount + 1]),
167 	  fOptimizer(new(nothrow) LayoutOptimizer(elementCount)),
168 	  fUnlimited(B_SIZE_UNLIMITED / (elementCount == 0 ? 1 : elementCount)),
169 	  fMinMaxValid(false),
170 	  fOptimizerConstraintsAdded(false)
171 {
172 	if (fConstraints)
173 		memset(fConstraints, 0, sizeof(Constraint*) * fElementCount);
174 
175 	if (fWeights) {
176 		for (int32 i = 0; i < fElementCount; i++)
177 			fWeights[i] = 1.0f;
178 	}
179 }
180 
181 
182 // destructor
183 ComplexLayouter::~ComplexLayouter()
184 {
185 	for (int32 i = 0; i < fElementCount; i++) {
186 		Constraint* constraint = fConstraints[i];
187 		fConstraints[i] = NULL;
188 		while (constraint != NULL) {
189 			Constraint* next = constraint->next;
190 			delete constraint;
191 			constraint = next;
192 		}
193 	}
194 
195 	delete[] fConstraints;
196 	delete[] fWeights;
197 	delete[] fSums;
198 	delete[] fSumBackups;
199   	delete fOptimizer;
200 }
201 
202 
203 // InitCheck
204 status_t
205 ComplexLayouter::InitCheck() const
206 {
207 	if (!fConstraints || !fWeights || !fSums || !fSumBackups || !fOptimizer)
208 		return B_NO_MEMORY;
209 	return fOptimizer->InitCheck();
210 }
211 
212 
213 // AddConstraints
214 void
215 ComplexLayouter::AddConstraints(int32 element, int32 length,
216 	float _min, float _max, float _preferred)
217 {
218 	if (element < 0 || length <= 0 || element + length > fElementCount)
219 		return;
220 
221 	TRACE("%p->ComplexLayouter::AddConstraints(%ld, %ld, %ld, %ld, %ld)\n",
222 		this, element, length, (int32)_min, (int32)_max, (int32)_preferred);
223 
224 	int32 spacing = fSpacing * (length - 1);
225 	int32 min = (int32)_min + 1 - spacing;
226 	int32 max = (int32)_max + 1 - spacing;
227 
228 	if (min < 0)
229 		min = 0;
230 	if (max > fUnlimited)
231 		max = fUnlimited;
232 
233 	int32 end = element + length - 1;
234 	Constraint** slot = fConstraints + end;
235 	while (*slot != NULL && (*slot)->start > element)
236 		slot = &(*slot)->next;
237 
238 	if (*slot != NULL && (*slot)->start == element) {
239 		// previous constraint exists -- use stricter values
240 		(*slot)->Restrict(min, max);
241 	} else {
242 		// no previous constraint -- create new one
243 		Constraint* constraint = new(nothrow) Constraint(element, end, min,
244 			max);
245 		if (!constraint)
246 			return;
247 		constraint->next = *slot;
248 		*slot = constraint;
249 	}
250 
251 	fMinMaxValid = false;
252 }
253 
254 
255 // SetWeight
256 void
257 ComplexLayouter::SetWeight(int32 element, float weight)
258 {
259 	if (element < 0 || element >= fElementCount)
260 		return;
261 
262 	fWeights[element] = max_c(weight, 0);
263 }
264 
265 
266 // MinSize
267 float
268 ComplexLayouter::MinSize()
269 {
270 	_ValidateLayout();
271 	return fMin;
272 }
273 
274 
275 // MaxSize
276 float
277 ComplexLayouter::MaxSize()
278 {
279 	_ValidateLayout();
280 	return fMax;
281 }
282 
283 
284 // PreferredSize
285 float
286 ComplexLayouter::PreferredSize()
287 {
288 	return fMin;
289 }
290 
291 
292 // CreateLayoutInfo
293 LayoutInfo*
294 ComplexLayouter::CreateLayoutInfo()
295 {
296 	MyLayoutInfo* layoutInfo = new(nothrow) MyLayoutInfo(fElementCount,
297 		fSpacing);
298 	if (layoutInfo && !layoutInfo->fLocations) {
299 		delete layoutInfo;
300 		return NULL;
301 	}
302 
303 	return layoutInfo;
304 }
305 
306 
307 // Layout
308 void
309 ComplexLayouter::Layout(LayoutInfo* _layoutInfo, float _size)
310 {
311 	TRACE("%p->ComplexLayouter::Layout(%ld)\n", this, (int32)_size);
312 
313 	if (fElementCount == 0)
314 		return;
315 
316 	_ValidateLayout();
317 
318 	MyLayoutInfo* layoutInfo = (MyLayoutInfo*)_layoutInfo;
319 
320 	int32 min = fSums[fElementCount].min;
321 	int32 max = fSums[fElementCount].max;
322 
323 	int32 size = (int32)_size + 1 - (fElementCount - 1) * fSpacing;
324 	if (size < min)
325 		size = min;
326 	if (size > max)
327 		size = max;
328 
329 	SumItem sums[fElementCount + 1];
330 	memcpy(sums, fSums, (fElementCount + 1) * sizeof(SumItem));
331 
332 	sums[fElementCount].min = size;
333 	sums[fElementCount].max = size;
334 	sums[fElementCount].minDirty = (size != min);
335 	sums[fElementCount].maxDirty = (size != max);
336 
337 	// propagate the size
338 	_PropagateChangesBack(sums, fElementCount - 1, NULL);
339 	_PropagateChanges(sums, fElementCount - 1, NULL);
340 
341 #if TRACE_COMPLEX_LAYOUTER
342 	TRACE("Layout(%ld)\n", size);
343 	for (int32 i = 0; i < fElementCount; i++) {
344 		SumItem& sum = sums[i + 1];
345 		TRACE("[%ld] minc = %4ld,  maxc = %4ld\n", i + 1, sum.min, sum.max);
346 	}
347 #endif
348 
349 	int32 sizes[fElementCount];
350 	if (!_Layout(size, sums, sizes)) {
351 	}
352 
353 	layoutInfo->InitFromSizes(sizes);
354 }
355 
356 
357 // CloneLayouter
358 Layouter*
359 ComplexLayouter::CloneLayouter()
360 {
361 	ComplexLayouter* layouter
362 		= new(nothrow) ComplexLayouter(fElementCount, fSpacing);
363 	ObjectDeleter<ComplexLayouter> layouterDeleter(layouter);
364 	if (!layouter || layouter->InitCheck() != B_OK
365 		|| !layouter->fOptimizer->AddConstraintsFrom(fOptimizer)) {
366 		return NULL;
367 	}
368 
369 	// clone the constraints
370 	for (int32 i = 0; i < fElementCount; i++) {
371 		Constraint* constraint = fConstraints[i];
372 		Constraint** end = layouter->fConstraints + i;
373 		while (constraint) {
374 			*end = new(nothrow) Constraint(constraint->start, constraint->end,
375 				constraint->min, constraint->max);
376 			if (!*end)
377 				return NULL;
378 
379 			end = &(*end)->next;
380 			constraint = constraint->next;
381 		}
382 	}
383 
384 	// copy the other stuff
385 	memcpy(layouter->fWeights, fWeights, fElementCount * sizeof(float));
386 	memcpy(layouter->fSums, fSums, (fElementCount + 1) * sizeof(SumItem));
387 	memcpy(layouter->fSumBackups, fSumBackups,
388 		(fElementCount + 1) * sizeof(SumItemBackup));
389 	layouter->fMin = fMin;
390 	layouter->fMax = fMax;
391 	layouter->fMinMaxValid = fMinMaxValid;
392 
393 	return layouterDeleter.Detach();
394 }
395 
396 
397 // _Layout
398 bool
399 ComplexLayouter::_Layout(int32 size, SumItem* sums, int32* sizes)
400 {
401 	// prepare the desired solution
402 	SimpleLayouter::DistributeSize(size, fWeights, sizes, fElementCount);
403 	if (_SatisfiesConstraints(sizes)) {
404 		// The desired solution already satisfies all constraints.
405 		return true;
406 	}
407 
408 	double realSizes[fElementCount];
409 	for (int32 i = 0; i < fElementCount; i++)
410 		realSizes[i] = sizes[i];
411 
412 	if (!_AddOptimizerConstraints())
413 		return false;
414 
415 
416 	// prepare a feasible solution (the minimum)
417 	double values[fElementCount];
418 	for (int32 i = 0; i < fElementCount; i++)
419 		values[i] = sums[i + 1].min - sums[i].min;
420 
421 #if TRACE_COMPLEX_LAYOUTER
422 	TRACE("feasible solution vs. desired solution:\n");
423 	for (int32 i = 0; i < fElementCount; i++)
424 		TRACE("%8.4f   %8.4f\n", values[i], realSizes[i]);
425 #endif
426 
427 	// solve
428 	TRACE_ONLY(bigtime_t time = system_time();)
429 	if (!fOptimizer->Solve(realSizes, size, values))
430 		return false;
431 	TRACE_ONLY(time = system_time() - time;)
432 
433 	// compute integer solution
434 	// The basic strategy is to floor() the sums. This guarantees that the
435 	// difference between two rounded sums remains in the range of floor()
436 	// and ceil() of their real value difference. Since the constraints have
437 	// integer values, the integer solution will therefore satisfy all
438 	// constraints the real solution satisfied.
439 	TRACE("computed solution in %lld us:\n", time);
440 
441 	double realSum = 0;
442 	double previousSum = 0;
443 	for (int32 i = 0; i < fElementCount; i++) {
444 		realSum += values[i];
445 		double roundedRealSum = floor(realSum);
446 		if (fuzzy_equals(realSum, roundedRealSum + 1))
447 			realSum = roundedRealSum + 1;
448 		sizes[i] = int32(roundedRealSum - previousSum);
449 		previousSum = roundedRealSum;
450 
451 		TRACE("x[%ld] = %8.4f   %4ld\n", i, values[i], sizes[i]);
452 	}
453 
454 	return _SatisfiesConstraints(sizes);
455 }
456 
457 
458 // _AddOptimizerConstraints
459 bool
460 ComplexLayouter::_AddOptimizerConstraints()
461 {
462 	if (fOptimizerConstraintsAdded)
463 		return true;
464 
465 	fOptimizer->RemoveAllConstraints();
466 
467 	// add constraints
468 	for (int32 i = 0; i < fElementCount; i++) {
469 		SumItem& sum = fSums[i + 1];
470 
471 		Constraint* constraint = fConstraints[i];
472 		while (constraint != NULL) {
473 			SumItem& base = fSums[constraint->start];
474 			int32 sumMin = base.min + constraint->min;
475 			int32 baseMax = sum.max - constraint->min;
476 			bool minRedundant = (sumMin < sum.min && baseMax > base.max);
477 
478 			int32 sumMax = base.max + constraint->effectiveMax;
479 			int32 baseMin = sum.min - constraint->effectiveMax;
480 			bool maxRedundant = (sumMax > sum.max && baseMin < base.min);
481 
482 			if (!minRedundant || !maxRedundant) {
483 				bool success = true;
484 				if (constraint->min == constraint->effectiveMax) {
485 					// min and max equal -- add an equality constraint
486 					success = fOptimizer->AddConstraint(constraint->start - 1,
487 						constraint->end, constraint->min, true);
488 				} else {
489 					// min and max not equal -- add them individually,
490 					// unless redundant
491 					if (!minRedundant) {
492 						success |= fOptimizer->AddConstraint(
493 							constraint->start - 1, constraint->end,
494 							constraint->min, false);
495 					}
496 					if (!maxRedundant) {
497 						success |= fOptimizer->AddConstraint(constraint->end,
498 							constraint->start - 1,
499 							-constraint->effectiveMax, false);
500 					}
501 				}
502 
503 				if (!success)
504 					return false;
505 			}
506 
507 			constraint = constraint->next;
508 		}
509 	}
510 
511 	fOptimizerConstraintsAdded = true;
512 	return true;
513 }
514 
515 
516 // _SatisfiesConstraints
517 bool
518 ComplexLayouter::_SatisfiesConstraints(int32* sizes) const
519 {
520 	int32 sumValues[fElementCount + 1];
521 	sumValues[0] = 0;
522 	for (int32 i = 0; i < fElementCount; i++)
523 		sumValues[i + 1] = sumValues[i] + sizes[i];
524 
525 	return _SatisfiesConstraintsSums(sumValues);
526 }
527 
528 
529 // _SatisfiesConstraintsSums
530 bool
531 ComplexLayouter::_SatisfiesConstraintsSums(int32* sumValues) const
532 {
533 	for (int32 i = 0; i < fElementCount; i++) {
534 		Constraint* constraint = fConstraints[i];
535 		while (constraint) {
536 			if (!constraint->IsSatisfied(sumValues))
537 				return false;
538 
539 			constraint = constraint->next;
540 		}
541 	}
542 
543 	return true;
544 }
545 
546 
547 // _ValidateLayout
548 void
549 ComplexLayouter::_ValidateLayout()
550 {
551 	// The general idea for computing the min and max for the given constraints
552 	// is that we rewrite the problem a little. Instead of considering the
553 	// x_1, ... x_n (n = fElementCount) and the constraints of the form
554 	//   x_i + ... + x_{i+j} >= min[i,j] and
555 	//   x_i + ... + x_{i+j} >= max[i,j], with i >= 1, j >= 0, i + j <= n
556 	//   and min[i,j], max[i,j] >= 0
557 	// we define
558 	//   c[0] = 0
559 	//   c[i] = \sum_{k=1}^i x_k, for all i, 1 <= i <= n
560 	// and thus the constraints read:
561 	//   c[i+j] - c[i-1] >= min[i,j]
562 	//   c[i+j] - c[i-1] <= max[i,j]
563 	//
564 	// Let minc[i] and maxc[i] the limits imposed by the given constraints, i.e.
565 	//   minc[i] <= c[i] <= maxc[i] for any tuple of (c[i])_i satisfying the
566 	// constraints (minc[i] and maxc[i] are unique), then we gain:
567 	//   minc[i+j] >= c[i-1] + min[i,j]
568 	//   maxc[i+j] <= c[i-1] + min[i,j]
569 	//   minc[i-1] >= minc[i+j] - max[i,j]
570 	//   maxc[i-1] >= maxc[i+j] - min[i,j]
571 	// We can compute the minc[i] and maxc[i] in an iterative process,
572 	// propagating the first to kinds of constraints forward and the other two
573 	// backwards. First we start considering all min constraints only. They
574 	// can't contradict each other and are usually to be enforced over max
575 	// constraints. Afterwards we add the max constraints one by one. For each
576 	// one of them we propagate resulting changes back and forth. In case of
577 	// a conflict, we relax the max constraint as much as necessary to yield
578 	// a consistent set of constraints. After all constraints have been
579 	// incorporated, the resulting minc[n] and maxc[n] are the min and max
580 	// limits we wanted to compute.
581 
582 	if (fMinMaxValid)
583 		return;
584 
585 	fSums[0].min = 0;
586 	fSums[0].max = 0;
587 
588 	int32 maxSum = 0;
589 	for (int32 i = 0; i < fElementCount; i++) {
590 		SumItem& sum = fSums[i + 1];
591 		sum.min = 0;
592 		sum.max = maxSum += fUnlimited;
593 		sum.minDirty = false;
594 		sum.maxDirty = false;
595 	}
596 
597 	// apply min constraints forward:
598 	//   minc[i+j] >= minc[i-1] + min[i,j]
599 	for (int32 i = 0; i < fElementCount; i++) {
600 		SumItem& sum = fSums[i + 1];
601 
602 		Constraint* constraint = fConstraints[i];
603 		while (constraint != NULL) {
604 			int32 minSum = fSums[constraint->start].min + constraint->min;
605 			if (minSum > sum.min) {
606 				sum.min = minSum;
607 			} else {
608 				TRACE("min constraint is redundant: x%ld + ... + x%ld >= %ld\n",
609 					constraint->start, constraint->end, constraint->min);
610 			}
611 
612 			constraint = constraint->next;
613 		}
614 	}
615 
616 	// apply min constraints backwards:
617 	//   maxc[i-1] <= maxc[i+j] - min[i,j]
618 	for (int32 i = fElementCount - 1; i >= 0; i--) {
619 		SumItem& sum = fSums[i + 1];
620 
621 		Constraint* constraint = fConstraints[i];
622 		while (constraint != NULL) {
623 			SumItem& base = fSums[constraint->start];
624 			int32 baseMax = sum.max - constraint->min;
625 			if (baseMax < base.max)
626 				base.max = baseMax;
627 
628 			constraint = constraint->next;
629 		}
630 	}
631 
632 	// apply max constraints
633 	for (int32 i = 0; i < fElementCount; i++) {
634 		Constraint* constraint = fConstraints[i];
635 		while (constraint != NULL) {
636 			_ApplyMaxConstraint(constraint, i);
637 
638 			constraint = constraint->next;
639 		}
640 	}
641 
642 #if TRACE_COMPLEX_LAYOUTER
643 	for (int32 i = 0; i < fElementCount; i++) {
644 		SumItem& sum = fSums[i + 1];
645 		TRACE("[%ld] minc = %4ld,  maxc = %4ld\n", i + 1, sum.min, sum.max);
646 	}
647 #endif
648 
649 	if (fElementCount == 0) {
650 		fMin = -1;
651 		fMax = B_SIZE_UNLIMITED;
652 	} else {
653 		int32 spacing = (fElementCount - 1) * fSpacing;
654 		fMin = fSums[fElementCount].min + spacing - 1;
655 		fMax = fSums[fElementCount].max + spacing - 1;
656 		if (fMax >= fUnlimited)
657 			fMax = B_SIZE_UNLIMITED;
658 	}
659 
660 	fOptimizerConstraintsAdded = false;
661 	fMinMaxValid = true;
662 }
663 
664 
665 // _ApplyMaxConstraint
666 void
667 ComplexLayouter::_ApplyMaxConstraint(Constraint* currentConstraint, int32 index)
668 {
669 	SumItem& sum = fSums[index + 1];
670 	SumItem& base = fSums[currentConstraint->start];
671 
672 	// We want to apply:
673 	//   c[i+j] <= c[i-1] + max[i,j]
674 	//
675 	// This has the following direct consequences (let k = i + j):
676 	// (1) maxc[k] <= maxc[i-1] + max[i,j]
677 	// (2) minc[i-1] >= minc[k] - max[i,j]
678 	//
679 	// If maxc[k] or minc[i-i] changed, those changes have to be propagated
680 	// back.
681 
682 	// apply (1) maxc[k] <= maxc[i-1] + max[i,j]
683 	int32 max = currentConstraint->effectiveMax;
684 	int32 sumMax = base.max + max;
685 
686 	// enforce maxc[i+j] >= minc[i+j]
687 	if (sumMax < sum.min) {
688 		sumMax = sum.min;
689 		max = sumMax - base.max;
690 	}
691 
692 	// apply (2) minc[i-1] >= minc[k] - max[i,j]
693 	// and check minc[i-1] <= maxc[i-1]
694 	int32 baseMin = sum.min - max;
695 	if (baseMin > base.max) {
696 		baseMin = base.max;
697 		max = sum.min - baseMin;
698 		sumMax = base.max + max;
699 	}
700 
701 	if (currentConstraint->effectiveMax != max) {
702 		TRACE("relaxing conflicting max constraint (1): "
703 			"x%ld + ... + x%ld <= %ld -> %ld\n", currentConstraint->start,
704 			currentConstraint->end, currentConstraint->effectiveMax, max);
705 	}
706 	currentConstraint->effectiveMax = max;
707 
708 	if (baseMin <= base.min && sumMax >= sum.max) {
709 		TRACE("max constraint is redundant: x%ld + ... + x%ld <= %ld\n",
710 			currentConstraint->start, currentConstraint->end,
711 			currentConstraint->effectiveMax);
712 		return;
713 	}
714 
715 	// backup old values, in case we detect a conflict later
716 	_BackupValues(index);
717 
718 	int32 diff;
719 	do {
720 		// apply the changes
721 		int32 changedIndex = currentConstraint->start;
722 
723 		if (baseMin > base.min) {
724 			base.min = baseMin;
725 			base.minDirty = true;
726 		}
727 
728 		if (sumMax < sum.max) {
729 			changedIndex = index;
730 			sum.max = sumMax;
731 			sum.maxDirty = true;
732 		}
733 
734 		// propagate the changes
735 		_PropagateChangesBack(fSums, changedIndex, currentConstraint);
736 		_PropagateChanges(fSums, index, currentConstraint);
737 
738 		// check the new constraint again -- if it doesn't hold, it
739 		// conflicts with the other constraints
740 		diff = 0;
741 
742 		// check (1) maxc[k] <= maxc[i-1] + max[i,j]
743 		max = currentConstraint->effectiveMax;
744 		sumMax = base.max + max;
745 		if (sumMax < sum.max)
746 			diff = sum.max - sumMax;
747 
748 		// check (2) minc[i-1] >= minc[k] - max[i,j]
749 		baseMin = sum.min - max;
750 		if (baseMin > base.min)
751 			diff = max_c(diff, baseMin - base.min);
752 
753 		// clear the dirty flags
754 		for (int32 i = 0; i <= changedIndex; i++) {
755 			SumItem& sum = fSums[i + 1];
756 			sum.minDirty = false;
757 			sum.maxDirty = false;
758 		}
759 
760 		// if we've got a conflict, we relax the constraint and try again
761 		if (diff > 0) {
762 			max += diff;
763 			TRACE("relaxing conflicting max constraint (2): "
764 				"x%ld + ... + x%ld <= %ld -> %ld\n", currentConstraint->start,
765 				currentConstraint->end, currentConstraint->effectiveMax, max);
766 			currentConstraint->effectiveMax = max;
767 
768 			_RestoreValues(index);
769 
770 			sumMax = base.max + max;
771 			baseMin = sum.min - max;
772 
773 			if (baseMin <= base.min && sumMax >= sum.max)
774 				return;
775 		}
776 	} while (diff > 0);
777 }
778 
779 
780 // _PropagateChanges
781 /*!	Propagate changes forward using min and max constraints. Max constraints
782 	Beyond \a toIndex or at \a to toIndex after (and including)
783 	\a lastMaxConstraint will be ignored. To have all constraints be
784 	considered pass \c fElementCount and \c NULL.
785 */
786 void
787 ComplexLayouter::_PropagateChanges(SumItem* sums, int32 toIndex,
788 	Constraint* lastMaxConstraint)
789 {
790 	for (int32 i = 0; i < fElementCount; i++) {
791 		SumItem& sum = sums[i + 1];
792 
793 		bool ignoreMaxConstraints = (i > toIndex);
794 
795 		Constraint* constraint = fConstraints[i];
796 		while (constraint != NULL) {
797 			SumItem& base = sums[constraint->start];
798 
799 			if (constraint == lastMaxConstraint)
800 				ignoreMaxConstraints = true;
801 
802 			// minc[k] >= minc[i-1] + min[i,j]
803 			if (base.minDirty) {
804 				int32 sumMin = base.min + constraint->min;
805 				if (sumMin > sum.min) {
806 					sum.min = sumMin;
807 					sum.minDirty = true;
808 				}
809 			}
810 
811 			// maxc[k] <= maxc[i-1] + max[i,j]
812 			if (base.maxDirty && !ignoreMaxConstraints) {
813 				int32 sumMax = base.max + constraint->effectiveMax;
814 				if (sumMax < sum.max) {
815 					sum.max = sumMax;
816 					sum.maxDirty = true;
817 				}
818 			}
819 
820 			constraint = constraint->next;
821 		}
822 
823 		if (sum.minDirty || sum.maxDirty) {
824 			if (sum.min > sum.max) {
825 				// TODO: Can this actually happen?
826 				TRACE("adjusted max in propagation phase: index: "
827 					"%ld: %ld -> %ld\n", i, sum.max, sum.min);
828 				sum.max = sum.min;
829 				sum.maxDirty = true;
830 			}
831 		}
832 	}
833 }
834 
835 
836 // _PropagateChangesBack
837 void
838 ComplexLayouter::_PropagateChangesBack(SumItem* sums, int32 changedIndex,
839 	Constraint* lastMaxConstraint)
840 {
841 	for (int32 i = changedIndex; i >= 0; i--) {
842 		SumItem& sum = sums[i + 1];
843 
844 		bool ignoreMaxConstraints = false;
845 
846 		Constraint* constraint = fConstraints[i];
847 		while (constraint != NULL) {
848 			SumItem& base = sums[constraint->start];
849 
850 			if (constraint == lastMaxConstraint)
851 				ignoreMaxConstraints = true;
852 
853 			// minc[i-1] >= minc[k] - max[i,j]
854 			if (sum.minDirty && !ignoreMaxConstraints) {
855 				int32 baseMin = sum.min - constraint->effectiveMax;
856 				if (baseMin > base.min) {
857 					if (baseMin > base.max) {
858 						TRACE("min above max in back propagation phase: index: "
859 							"(%ld -> %ld), min: %ld, max: %ld\n", i,
860 							constraint->start, baseMin, base.max);
861 					}
862 					base.min = baseMin;
863 					base.minDirty = true;
864 				}
865 			}
866 
867 			// maxc[i-1] <= maxc[k] - min[i,j]
868 			if (sum.maxDirty) {
869 				int32 baseMax = sum.max - constraint->min;
870 				if (baseMax < base.max) {
871 					if (baseMax < base.min) {
872 						TRACE("max below min in back propagation phase: index: "
873 							"(%ld -> %ld), max: %ld, min: %ld\n", i,
874 							constraint->start, baseMax, base.min);
875 					}
876 					base.max = baseMax;
877 					base.maxDirty = true;
878 				}
879 			}
880 
881 			constraint = constraint->next;
882 		}
883 	}
884 }
885 
886 
887 // _BackupValues
888 void
889 ComplexLayouter::_BackupValues(int32 maxIndex)
890 {
891 	for (int32 i = 0; i <= maxIndex; i++) {
892 		SumItem& sum = fSums[i + 1];
893 		fSumBackups[i + 1].min = sum.min;
894 		fSumBackups[i + 1].max = sum.max;
895 	}
896 }
897 
898 
899 // _RestoreValues
900 void
901 ComplexLayouter::_RestoreValues(int32 maxIndex)
902 {
903 	for (int32 i = 0; i <= maxIndex; i++) {
904 		SumItem& sum = fSums[i + 1];
905 		sum.min = fSumBackups[i + 1].min;
906 		sum.max = fSumBackups[i + 1].max;
907 	}
908 }
909 
910