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