Skip to main content

kas_core/layout/
size_rules.rs

1// Licensed under the Apache License, Version 2.0 (the "License");
2// you may not use this file except in compliance with the License.
3// You may obtain a copy of the License in the LICENSE-APACHE file or at:
4//     https://www.apache.org/licenses/LICENSE-2.0
5
6//! [`SizeRules`] type
7
8use smallvec::SmallVec;
9use std::iter::Sum;
10
11use super::Stretch;
12use crate::cast::{Cast, Conv};
13
14// for doc use
15#[allow(unused)] use super::FrameRules;
16#[allow(unused)] use crate::theme::SizeCx;
17
18/// Widget sizing information
19///
20/// This is the return value of [`crate::Layout::size_rules`] and is used to
21/// describe size and margin requirements for widgets. This type only concerns
22/// size requirements along a *single* axis.
23///
24/// All units are in pixels. Sizes usually come directly from [`SizeCx`]
25/// or from a fixed quantity multiplied by [`SizeCx::scale_factor`].
26///
27/// ### Sizes
28///
29/// The widget size model is simple: a rectangular box, plus a margin on each
30/// side. The `SizeRules` type represents expectations along a single axis:
31///
32/// -   The minimum acceptable size (almost always met)
33/// -   The ideal size (often the same size; this distinction is most useful for
34///     scrollable regions which are *ideally* large enough not to require
35///     scrolling, but can be much smaller)
36/// -   A [`Stretch`] priority, used to prioritize allocation of excess space
37///
38/// Note that `Stretch::None` does not *prevent* stretching, but simply states
39/// that it is undesired (lowest priority). Actually preventing stretching
40/// requires alignment.
41///
42/// ### Margins
43///
44/// Required margin sizes are handled separately for each side of a widget.
45/// Since [`SizeRules`] concerns only one axis, it stores only two margin sizes:
46/// "pre" (left/top) and "post" (right/bottom). These are stored as `u16` values
47/// on the assumption that no margin need exceed 65536.
48///
49/// When widgets are placed next to each other, their margins may be combined;
50/// e.g. if a widget with margin of 6px is followed by another with margin 2px,
51/// the required margin between the two is the maximum, 6px.
52///
53/// Only the layout engine and parent widgets need consider margins (beyond
54/// their specification). For these cases, one needs to be aware that due to
55/// margin-merging behaviour, one cannot simply "add" two `SizeRules`. Instead,
56/// when placing one widget next to another, use [`SizeRules::append`] or
57/// [`SizeRules::appended`]; when placing a widget within a frame, use
58/// [`FrameRules::surround`].
59/// When calculating the size of a sequence of
60/// widgets, one may use the [`Sum`] implementation (this assumes that the
61/// sequence is in left-to-right or top-to-bottom order).
62///
63/// ### Alignment
64///
65/// `SizeRules` concerns calculations of size requirements, which the layout
66/// engine uses to assign each widget a [`Rect`]; it is up to the widget itself
67/// to either fill this rect or align itself within the given space.
68/// See [`crate::Layout::set_rect`] for more information.
69///
70/// For widgets with a stretch priority of [`Stretch::None`], it is still
71/// possible for layout code to assign a size larger than the preference. It is
72/// up to the widget to align itself within this space: see
73/// [`crate::Layout::set_rect`] and [`crate::layout::AlignHints`].
74///
75/// [`Rect`]: crate::geom::Rect
76#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
77pub struct SizeRules {
78    // minimum good size
79    a: i32,
80    // ideal size; b >= a
81    b: i32,
82    // (pre, post) margins
83    m: (u16, u16),
84    stretch: Stretch,
85}
86
87impl SizeRules {
88    /// Empty (zero size) widget
89    ///
90    /// Warning: appending another size to `EMPTY` *does* include margins
91    /// even though `EMPTY` itself has zero size. However, `EMPTY` itself has
92    /// zero-size margins, so this only affects appending an `EMPTY` with a
93    /// non-empty `SizeRules`.
94    pub const EMPTY: Self = SizeRules::empty(Stretch::None);
95
96    /// Empty space with the given stretch priority
97    ///
98    /// See warning on [`SizeRules::EMPTY`].
99    #[inline]
100    pub const fn empty(stretch: Stretch) -> Self {
101        SizeRules {
102            a: 0,
103            b: 0,
104            m: (0, 0),
105            stretch,
106        }
107    }
108
109    /// Construct for a fixed size
110    ///
111    /// Margins are zero-sized by default; use [`Self::with_margin`] or
112    /// [`Self::with_margins`] to set.
113    #[inline]
114    pub fn fixed(size: i32) -> Self {
115        debug_assert!(size >= 0);
116        SizeRules {
117            a: size,
118            b: size,
119            m: (0, 0),
120            stretch: Stretch::None,
121        }
122    }
123
124    /// Construct with custom rules
125    ///
126    /// Region size should meet the given `min`-imum size and has a given
127    /// `ideal` size, plus a given `stretch` priority.
128    ///
129    /// Expected: `ideal >= min` (if not, ideal is clamped to min).
130    ///
131    /// Margins are zero-sized by default; use [`Self::with_margin`] or
132    /// [`Self::with_margins`] to set.
133    #[inline]
134    pub fn new(min: i32, ideal: i32, stretch: Stretch) -> Self {
135        debug_assert!(0 <= min && 0 <= ideal);
136        SizeRules {
137            a: min,
138            b: ideal.max(min),
139            m: (0, 0),
140            stretch,
141        }
142    }
143
144    /// Set both margins (symmetric)
145    ///
146    /// Both margins are set to the same value. By default these are 0.
147    #[inline]
148    pub fn with_margin(mut self, margin: u16) -> Self {
149        self.m = (margin, margin);
150        self
151    }
152
153    /// Set both margins (top/left, bottom/right)
154    ///
155    /// By default these are 0.
156    #[inline]
157    pub fn with_margins(mut self, (first, second): (u16, u16)) -> Self {
158        self.m = (first, second);
159        self
160    }
161
162    /// Set stretch factor, inline
163    #[inline]
164    pub fn with_stretch(mut self, stretch: Stretch) -> Self {
165        self.stretch = stretch;
166        self
167    }
168
169    /// Get the minimum size
170    #[inline]
171    pub fn min_size(self) -> i32 {
172        self.a
173    }
174
175    /// Get the ideal size
176    #[inline]
177    pub fn ideal_size(self) -> i32 {
178        self.b
179    }
180
181    /// Get the `(pre, post)` margin sizes
182    #[inline]
183    pub fn margins(self) -> (u16, u16) {
184        self.m
185    }
186
187    /// Get the `(pre, post)` margin sizes, cast to `i32`
188    #[inline]
189    pub fn margins_i32(self) -> (i32, i32) {
190        (self.m.0.into(), self.m.1.into())
191    }
192
193    /// Get the stretch priority
194    #[inline]
195    pub fn stretch(self) -> Stretch {
196        self.stretch
197    }
198
199    /// Set the stretch priority
200    #[inline]
201    pub fn set_stretch(&mut self, stretch: Stretch) {
202        self.stretch = stretch;
203    }
204
205    /// Set margins
206    #[inline]
207    pub fn set_margins(&mut self, margins: (u16, u16)) {
208        self.m = margins;
209    }
210
211    /// Use the maximum size of `self` and `rhs`.
212    #[must_use = "method does not modify self but returns a new value"]
213    pub fn max(self, rhs: Self) -> SizeRules {
214        SizeRules {
215            a: self.a.max(rhs.a),
216            b: self.b.max(rhs.b),
217            m: (self.m.0.max(rhs.m.0), self.m.1.max(rhs.m.1)),
218            stretch: self.stretch.max(rhs.stretch),
219        }
220    }
221
222    /// Set `self = self.max(rhs);`
223    #[inline]
224    pub fn max_with(&mut self, rhs: Self) {
225        *self = self.max(rhs);
226    }
227
228    /// Multiply the `(min, ideal)` size, including internal margins
229    ///
230    /// E.g. given `margin = margins.0 + margins.1` and factors `(2, 5)`, the
231    /// minimum size is set to `min * 2 + margin` and the ideal to
232    /// `ideal * 5 + 4 * margin`.
233    ///
234    /// Panics if either factor is 0.
235    pub fn multiply_with_margin(&mut self, min_factor: i32, ideal_factor: i32) {
236        let margin = i32::from(self.m.0).max(i32::from(self.m.1));
237        assert!(min_factor > 0);
238        assert!(ideal_factor > 0);
239        self.a = min_factor * self.a + (min_factor - 1) * margin;
240        self.b = ideal_factor * self.b + (ideal_factor - 1) * margin;
241    }
242
243    /// Append the rules for `rhs` to self
244    ///
245    /// This implies that `rhs` rules concern an element to the right of or
246    /// below self. Note that order matters since margins may be combined.
247    ///
248    /// Note also that appending [`SizeRules::EMPTY`] does include interior
249    /// margins (those between `EMPTY` and the other rules) within the result.
250    pub fn append(&mut self, rhs: SizeRules) {
251        let c: i32 = self.m.1.max(rhs.m.0).into();
252        self.a += rhs.a + c;
253        self.b += rhs.b + c;
254        self.m.1 = rhs.m.1;
255        self.stretch = self.stretch.max(rhs.stretch);
256    }
257
258    /// Return the rules for self appended by `rhs`
259    ///
260    ///
261    /// This implies that `rhs` rules concern an element to the right of or
262    /// below self. Note that order matters since margins may be combined.
263    ///
264    /// Note also that appending [`SizeRules::EMPTY`] does include interior
265    /// margins (those between `EMPTY` and the other rules) within the result.
266    #[must_use = "method does not modify self but returns a new value"]
267    pub fn appended(self, rhs: SizeRules) -> Self {
268        let c: i32 = self.m.1.max(rhs.m.0).into();
269        SizeRules {
270            a: self.a + rhs.a + c,
271            b: self.b + rhs.b + c,
272            m: (self.m.0, rhs.m.1),
273            stretch: self.stretch.max(rhs.stretch),
274        }
275    }
276
277    /// Return the result of appending all given ranges
278    pub fn sum(range: &[SizeRules]) -> SizeRules {
279        range.iter().sum()
280    }
281
282    /// Return the result of appending all given ranges (min only)
283    ///
284    /// This is a specialised version of sum: only the minimum is calculated
285    pub fn min_sum(range: &[SizeRules]) -> SizeRules {
286        if range.is_empty() {
287            return SizeRules::EMPTY;
288        }
289
290        let mut rules = range[0];
291        for r in &range[1..] {
292            rules.a += i32::from(rules.m.1.max(r.m.0)) + r.a;
293        }
294        rules.b = rules.a;
295        rules.m.1 = range[range.len() - 1].m.1;
296        rules
297    }
298
299    /// Set self to `self - x + y`, clamped to 0 or greater
300    ///
301    /// This is a specialised operation to join two spans, subtracing the
302    /// common overlap (`x`), thus margins are `self.m.0` and `y.m.1`.
303    pub fn sub_add(&mut self, x: Self, y: Self) {
304        self.a = (self.a - x.a + y.a).max(0);
305        self.b = (self.b - x.b + y.b).max(0);
306        self.m.1 = y.m.1;
307        self.stretch = self.stretch.max(y.stretch);
308    }
309
310    /// Reduce the minimum size
311    ///
312    /// If `min` is greater than the current minimum size, this has no effect.
313    #[inline]
314    pub fn reduce_min_to(&mut self, min: i32) {
315        self.a = self.a.min(min);
316    }
317
318    /// Solve a sequence of rules with even distribution
319    ///
320    /// Given a sequence of width (or height) `rules` from children and a
321    /// `target` size, find an appropriate size for each child.
322    /// The method attempts to ensure the following, in order of priority:
323    ///
324    /// 1.  All widths are at least their minimum size requirement
325    /// 2.  The sum of widths plus margins between items equals `target`
326    /// 3.  No width exceeds its ideal size while other widths are below their
327    ///     own ideal size
328    /// 4.  No item with a [`Stretch`] priority less than the highest in `rules`
329    ///     exceeds its ideal size
330    /// 5.  When extra space is available and some widgets are below their ideal
331    ///     size, extra space is divided evenly between these widgets until they
332    ///     have reached their ideal size
333    /// 6.  If all rules use `Stretch::None`, then widths are not increased over
334    ///     their ideal size.
335    /// 7.  Extra space (after all widths are at least their ideal size) is
336    ///     shared equally between all widgets with the highest stretch priority
337    ///
338    /// Input requirements: `rules.len() == widths.len()`.
339    ///
340    /// This method is idempotent: if input widths already match the above
341    /// requirements then they will not be modified.
342    /// Moreover, this method attempts to ensure that if `target`
343    /// is increased, then decreased back to the previous value, this will
344    /// revert to the previous solution. (The reverse may not hold if widths
345    /// had previously been affected by a different agent.)
346    #[inline]
347    pub fn solve_widths(widths: &mut [i32], rules: &[Self], target: i32) {
348        let total = SizeRules::sum(rules);
349        Self::solve_widths_with_total(widths, rules, total, target);
350    }
351
352    /// Solve a sequence of rules
353    ///
354    /// This is the same as [`SizeRules::solve_widths`] except that the rules' sum
355    /// is passed explicitly.
356    ///
357    /// Input requirements:
358    /// - `rules.len() == widths.len()`
359    /// - `SizeRules::sum(rules) == total`
360    pub fn solve_widths_with_total(widths: &mut [i32], rules: &[Self], total: Self, target: i32) {
361        Self::solve_widths_impl(widths, rules, total, target, Even)
362    }
363
364    /// Solve a sequence of rules with priority distribution
365    ///
366    /// Given a sequence of width (or height) `rules` from children and a
367    /// `target` size, find an appropriate size for each child.
368    /// The method attempts to ensure that:
369    ///
370    /// 1.  All widths are at least their minimum size requirement
371    /// 2.  The sum of widths plus margins between items equals `target`
372    /// 3.  No width exceeds its ideal size while other widths are below their
373    ///     own ideal size
374    /// 4.  No item with a [`Stretch`] priority less than the highest in `rules`
375    ///     exceeds its ideal size
376    /// 5.  When extra space is available and some widgets are below their ideal
377    ///     size, extra space is divided evenly between these widgets until they
378    ///     have reached their ideal size
379    /// 6.  If all rules use `Stretch::None`, then widths are not increased over
380    ///     their ideal size.
381    /// 7.  Extra space (after all widths are at least their ideal size) is
382    ///     allocated to the first (or `last`) item with the highest stretch
383    ///     priority
384    ///
385    /// Input requirements: `rules.len() == widths.len()`.
386    ///
387    /// This method is idempotent: given satisfactory input widths, these will
388    /// be preserved. Moreover, this method attempts to ensure that if target
389    /// is increased, then decreased back to the previous value, this will
390    /// revert to the previous solution. (The reverse may not hold if widths
391    /// had previously been affected by a different agent.)
392    #[allow(clippy::collapsible_if, clippy::needless_return)]
393    pub fn solve_widths_with_priority(widths: &mut [i32], rules: &[Self], target: i32, last: bool) {
394        let total = SizeRules::sum(rules);
395        Self::solve_widths_impl(widths, rules, total, target, Prioritize { last })
396    }
397
398    #[allow(
399        clippy::comparison_chain,
400        clippy::needless_range_loop,
401        clippy::needless_return
402    )]
403    #[inline(always)]
404    fn solve_widths_impl(
405        out: &mut [i32],
406        rules: &[Self],
407        total: Self,
408        target: i32,
409        pri: impl SolvePriority,
410    ) {
411        #[allow(non_snake_case)]
412        let N = out.len();
413        assert_eq!(rules.len(), N);
414        if N == 0 {
415            return;
416        }
417        #[cfg(debug_assertions)]
418        {
419            assert!(out.iter().all(|w| *w >= 0));
420            let mut sum = SizeRules::sum(rules);
421            sum.m = total.m; // external margins are unimportant here
422            assert_eq!(sum, total);
423        }
424
425        if target <= total.a {
426            // Below minimum size: ignore target and use minimum sizes.
427            for n in 0..N {
428                out[n] = rules[n].a;
429            }
430            return;
431        }
432        let highest_stretch = total.stretch;
433
434        // All minimum sizes can be met.
435        out[0] = out[0].max(rules[0].a);
436        let mut margin_sum = 0;
437        let mut sum = out[0];
438        let mut dist_under_b = (rules[0].b - out[0]).max(0);
439        let mut dist_over_b = (out[0] - rules[0].b).max(0);
440        let mut dist_over_b_lower_stretch = 0;
441        if rules[0].stretch < highest_stretch {
442            dist_over_b_lower_stretch = dist_over_b;
443        }
444        for i in 1..N {
445            out[i] = out[i].max(rules[i].a);
446            margin_sum += i32::from((rules[i - 1].m.1).max(rules[i].m.0));
447            sum += out[i];
448            dist_under_b += (rules[i].b - out[i]).max(0);
449            let over = (out[i] - rules[i].b).max(0);
450            dist_over_b += over;
451            if rules[i].stretch < highest_stretch {
452                dist_over_b_lower_stretch += over;
453            }
454        }
455        let target = target - margin_sum;
456
457        // Shrink: sum - target
458        // + dist_under_b because this lets us increase targets under b
459        let mut to_shrink = sum + dist_under_b - target;
460        if dist_over_b <= to_shrink {
461            // Ensure nothing exceeds the ideal and consider shrinking anything over min:
462            let mut targets = Targets::new();
463            sum = 0;
464            for i in 0..N {
465                out[i] = out[i].min(rules[i].b);
466                sum += out[i];
467                if out[i] > rules[i].a {
468                    targets.push(i.cast());
469                }
470            }
471
472            if sum > target {
473                let avail = target + margin_sum - total.a;
474                reduce_targets(out, &mut targets, |i| rules[i].a, avail);
475                debug_assert_eq!(target, (0..N).fold(0, |x, i| x + out[i]));
476                return;
477            }
478        } else if dist_over_b_lower_stretch >= to_shrink {
479            // Ensure only highest-stretch-priority items exceed the ideal:
480            sum = 0;
481            for i in 0..N {
482                if rules[i].stretch < highest_stretch {
483                    out[i] = out[i].min(rules[i].b);
484                }
485                sum += out[i];
486            }
487        } else if to_shrink > 0 {
488            // we do not go below ideal, and will keep at least one above
489            if let Some(prioritize_last) = pri.priority() {
490                if !prioritize_last {
491                    for i in 0..N {
492                        if out[i] > rules[i].b {
493                            let decr = to_shrink.min((out[i] - rules[i].b).max(0));
494                            to_shrink -= decr;
495                            out[i] -= decr;
496                        }
497                    }
498                } else {
499                    for i in (0..N).rev() {
500                        if out[i] > rules[i].b {
501                            let decr = to_shrink.min((out[i] - rules[i].b).max(0));
502                            to_shrink -= decr;
503                            out[i] -= decr;
504                        }
505                    }
506                }
507                debug_assert_eq!(to_shrink, 0);
508            } else {
509                // calculate distance over for each stretch priority
510                const MAX_STRETCH: usize = Stretch::Maximize as usize + 1;
511                let mut dists = [0; MAX_STRETCH];
512                for i in 0..N {
513                    dists[rules[i].stretch as usize] += (out[i] - rules[i].b).max(0);
514                }
515                let mut accum = 0;
516                let mut highest_affected = 0;
517                for i in 0..MAX_STRETCH {
518                    highest_affected = i;
519                    dists[i] += accum;
520                    accum = dists[i];
521                    if accum >= to_shrink {
522                        break;
523                    }
524                }
525
526                sum = 0;
527                let mut avail = 0;
528                let mut targets = Targets::new();
529                for i in 0..N {
530                    let stretch = rules[i].stretch as usize;
531                    if out[i] > rules[i].b {
532                        let over = (out[i] - rules[i].b).max(0);
533                        if stretch < highest_affected {
534                            out[i] = rules[i].b;
535                        } else if stretch == highest_affected {
536                            avail += over;
537                            targets.push(i.cast());
538                        }
539                    }
540                    sum += out[i];
541                }
542                let to_shrink = sum + dist_under_b - target;
543                if 0 < to_shrink && to_shrink <= avail {
544                    avail -= to_shrink;
545                    reduce_targets(out, &mut targets, |i| rules[i].b, avail);
546                    sum -= to_shrink;
547                }
548            }
549        }
550
551        if sum < target {
552            if target - sum >= dist_under_b {
553                // We can increase all sizes to their ideal. Since this may
554                // not be enough, we also count the number with highest
555                // stretch factor and how far these are over their ideal.
556                // If highest stretch is None, do not expand beyond ideal.
557                sum = 0;
558                if let Some(prioritize_last) = pri.priority() {
559                    let mut pick = None;
560                    for i in 0..N {
561                        out[i] = out[i].max(rules[i].b);
562                        sum += out[i];
563                        if highest_stretch > Stretch::None && rules[i].stretch == highest_stretch {
564                            if prioritize_last || pick.is_none() {
565                                pick = Some(i);
566                            }
567                        }
568                    }
569
570                    if let Some(i) = pick {
571                        out[i] += target - sum;
572                    }
573                } else {
574                    let mut targets = Targets::new();
575                    let mut over = 0;
576                    for i in 0..N {
577                        out[i] = out[i].max(rules[i].b);
578                        sum += out[i];
579                        if highest_stretch > Stretch::None && rules[i].stretch == highest_stretch {
580                            over += out[i] - rules[i].b;
581                            targets.push(i.cast());
582                        }
583                    }
584
585                    let avail = target - sum + over;
586                    increase_targets(out, &mut targets, |i| rules[i].b, avail);
587                }
588
589                debug_assert!(target >= (0..N).fold(0, |x, i| x + out[i]));
590                return;
591            } else {
592                // We cannot increase sizes as far as their ideal: instead
593                // increase over minimum size and under ideal
594                let mut targets = Targets::new();
595                let mut over = 0;
596                for i in 0..N {
597                    if out[i] < rules[i].b {
598                        over += out[i] - rules[i].a;
599                        targets.push(i.cast());
600                    }
601                }
602
603                let avail = target - sum + over;
604                increase_targets(out, &mut targets, |i| rules[i].a, avail);
605            }
606        }
607
608        debug_assert_eq!(
609            target,
610            (0..N).fold(0, |x, i| x + out[i]),
611            "widths {out:?} not equal to target {target}"
612        );
613    }
614
615    /// Ensure at least one of `rules` has stretch priority at least as high as self
616    ///
617    /// The stretch policies are increased according to the highest `scores`.
618    /// Required: `rules.len() == scores.len()`.
619    pub(crate) fn distribute_stretch_over_by(self, rules: &mut [Self], scores: &[u32]) {
620        assert_eq!(rules.len(), scores.len());
621        if rules.iter().any(|r| r.stretch >= self.stretch) {
622            return;
623        }
624
625        let highest = scores.iter().cloned().max().unwrap_or(0);
626        for i in 0..rules.len() {
627            if scores[i] == highest {
628                rules[i].stretch = self.stretch;
629            }
630        }
631    }
632
633    /// Adjust a sequence of `rules` to ensure that the total is at least `self`
634    ///
635    /// This is a cheap hack used by grids to ensure that cell spans are sufficiently large.
636    pub(crate) fn distribute_span_over(self, rules: &mut [Self]) {
637        let len = rules.len();
638        assert!(len > 0);
639        let len1 = len - 1;
640        let sum: SizeRules = rules.iter().sum();
641
642        rules[0].m.0 = rules[0].m.0.max(self.m.0);
643        rules[len1].m.1 = rules[len1].m.1.max(self.m.1);
644
645        let excess_a = (self.a - sum.a).max(0);
646        let excess_b = (self.b - sum.b).max(0);
647        if excess_a == 0 && excess_b == 0 {
648            return;
649        }
650
651        let highest_stretch = sum.stretch;
652        let count = i32::conv(
653            (0..len)
654                .filter(|i| rules[*i].stretch == highest_stretch)
655                .count(),
656        );
657        let a_per_elt = excess_a / count;
658        let b_per_elt = excess_b / count;
659        let mut extra_a = excess_a - count * a_per_elt;
660        let mut extra_b = excess_b - count * b_per_elt;
661        for rules in rules.iter_mut() {
662            if rules.stretch == highest_stretch {
663                rules.a += a_per_elt;
664                rules.b += b_per_elt;
665                if extra_a > 0 {
666                    rules.a += 1;
667                    extra_a -= 1;
668                }
669                if extra_b > 0 {
670                    rules.b += 1;
671                    extra_b -= 1;
672                }
673                if highest_stretch < self.stretch {
674                    rules.stretch = self.stretch;
675                }
676            }
677        }
678    }
679}
680
681trait SolvePriority: std::fmt::Debug {
682    /// Are we giving priority to some widget when increasing/decreasing size?
683    ///
684    /// - `None` means even distribution
685    /// - `Some(true)` means give priority to the *last* item
686    /// - `Some(false)` means give priority to the *first* item
687    fn priority(&self) -> Option<bool>;
688}
689
690#[derive(Debug)]
691struct Even;
692impl SolvePriority for Even {
693    #[inline]
694    fn priority(&self) -> Option<bool> {
695        None
696    }
697}
698
699#[derive(Debug)]
700struct Prioritize {
701    last: bool,
702}
703impl SolvePriority for Prioritize {
704    #[inline]
705    fn priority(&self) -> Option<bool> {
706        Some(self.last)
707    }
708}
709
710/// Return the sum over a sequence of rules, assuming these are ordered
711///
712/// Uses [`SizeRules::appended`] on all rules in sequence.
713impl Sum for SizeRules {
714    fn sum<I: Iterator<Item = Self>>(mut iter: I) -> Self {
715        if let Some(first) = iter.next() {
716            iter.fold(first, |x, y| x.appended(y))
717        } else {
718            SizeRules::EMPTY
719        }
720    }
721}
722
723/// Return the sum over a sequence of rules, assuming these are ordered
724///
725/// Uses [`SizeRules::appended`] on all rules in sequence.
726impl<'a> Sum<&'a Self> for SizeRules {
727    fn sum<I: Iterator<Item = &'a Self>>(mut iter: I) -> Self {
728        if let Some(first) = iter.next() {
729            iter.fold(*first, |x, y| x.appended(*y))
730        } else {
731            SizeRules::EMPTY
732        }
733    }
734}
735
736type Targets = SmallVec<[i32; 16]>;
737
738fn increase_targets(
739    out: &mut [i32],
740    targets: &mut Targets,
741    base: impl Fn(usize) -> i32,
742    mut avail: i32,
743) {
744    if targets.is_empty() {
745        return;
746    }
747
748    // Calculate ceiling above which sizes will not be increased
749    let mut any_removed = true;
750    while any_removed {
751        any_removed = false;
752        let count = i32::conv(targets.len());
753        let ceil = (avail + count - 1) / count; // round up
754        let mut t = 0;
755        while t < targets.len() {
756            let i = usize::conv(targets[t]);
757            if out[i] >= base(i) + ceil {
758                avail -= out[i] - base(i);
759                targets.remove(t);
760                any_removed = true;
761                continue;
762            }
763            t += 1;
764        }
765        if targets.is_empty() {
766            return;
767        }
768    }
769
770    // Since no more are removed by a ceiling, all remaining
771    // targets will be (approx) equal. Arbitrarily distribute
772    // rounding errors to the first ones.
773    let count = i32::conv(targets.len());
774    let per_elt = avail / count;
775    let extra = usize::conv(avail - per_elt * count);
776    assert!(extra < targets.len());
777    for t in 0..extra {
778        let i = usize::conv(targets[t]);
779        out[i] = base(i) + per_elt + 1;
780    }
781    for t in extra..targets.len() {
782        let i = usize::conv(targets[t]);
783        out[i] = base(i) + per_elt;
784    }
785}
786
787/// Reduce targets such that the sum over `i in targets` of `out[i] + base(i)` is `avail`.
788///
789/// Assumption: for `i in targets`, each `out[i] >= base(i)`.
790fn reduce_targets<F: Fn(usize) -> i32>(
791    out: &mut [i32],
792    targets: &mut Targets,
793    base: F,
794    mut avail: i32,
795) {
796    debug_assert!(avail >= 0);
797    // We can ignore everything below the floor
798    let mut any_removed = true;
799    while any_removed {
800        any_removed = false;
801        let floor = avail / i32::conv(targets.len());
802        let mut t = 0;
803        while t < targets.len() {
804            let i = usize::conv(targets[t]);
805            let base = base(i);
806            debug_assert!(out[i] >= base);
807            if out[i] <= base + floor {
808                avail -= out[i] - base;
809                targets.remove(t);
810                any_removed = true;
811                continue;
812            }
813            t += 1;
814        }
815    }
816
817    // All targets remaining must be reduced to floor, bar rounding errors
818    let floor = avail / i32::conv(targets.len());
819    let extra = usize::conv(avail) - usize::conv(floor) * targets.len();
820    assert!(extra < targets.len());
821    for t in 0..extra {
822        let i = usize::conv(targets[t]);
823        out[i] = base(i) + floor + 1;
824    }
825    for t in extra..targets.len() {
826        let i = usize::conv(targets[t]);
827        out[i] = base(i) + floor;
828    }
829}