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
32using std::nothrow;
33
34
35// MyLayoutInfo
36class ComplexLayouter::MyLayoutInfo : public LayoutInfo {
37public:
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(): %" 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
96public:
97	int32	fCount;
98	int32	fSpacing;
99	int32*	fLocations;
100};
101
102
103// Constraint
104struct ComplexLayouter::Constraint {
105	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
117	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
128	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
144struct ComplexLayouter::SumItem {
145	int32	min;
146	int32	max;
147	bool	minDirty;
148	bool	maxDirty;
149};
150
151
152// SumItemBackup
153struct ComplexLayouter::SumItemBackup {
154	int32	min;
155	int32	max;
156};
157
158
159// #pragma mark - ComplexLayouter
160
161
162// constructor
163ComplexLayouter::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
186ComplexLayouter::~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
207status_t
208ComplexLayouter::InitCheck() const
209{
210	if (!fConstraints || !fWeights || !fSums || !fSumBackups || !fOptimizer)
211		return B_NO_MEMORY;
212	return fOptimizer->InitCheck();
213}
214
215
216// AddConstraints
217void
218ComplexLayouter::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
259void
260ComplexLayouter::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
270float
271ComplexLayouter::MinSize()
272{
273	_ValidateLayout();
274	return fMin;
275}
276
277
278// MaxSize
279float
280ComplexLayouter::MaxSize()
281{
282	_ValidateLayout();
283	return fMax;
284}
285
286
287// PreferredSize
288float
289ComplexLayouter::PreferredSize()
290{
291	return fMin;
292}
293
294
295// CreateLayoutInfo
296LayoutInfo*
297ComplexLayouter::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
311void
312ComplexLayouter::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
361Layouter*
362ComplexLayouter::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
401bool
402ComplexLayouter::_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
462bool
463ComplexLayouter::_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
520bool
521ComplexLayouter::_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
533bool
534ComplexLayouter::_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
551void
552ComplexLayouter::_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
675void
676ComplexLayouter::_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*/
795void
796ComplexLayouter::_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
846void
847ComplexLayouter::_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
897void
898ComplexLayouter::_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
909void
910ComplexLayouter::_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