From 44c5c43c255c5a8ce0cf88d85bdbe9678b5ec8dd Mon Sep 17 00:00:00 2001 From: Peter Fajdiga Date: Sat, 26 Oct 2024 18:23:32 +0200 Subject: [PATCH] fillSpace: distribute remainder --- src/lib/utils/fillSpace.ts | 36 +++++++++++++++++++++++++----- src/tests/units/utils/fillSpace.ts | 10 ++++----- 2 files changed, 36 insertions(+), 10 deletions(-) diff --git a/src/lib/utils/fillSpace.ts b/src/lib/utils/fillSpace.ts index 8ab05dc..6b7b4c2 100644 --- a/src/lib/utils/fillSpace.ts +++ b/src/lib/utils/fillSpace.ts @@ -1,12 +1,16 @@ function fillSpace(availableSpace: number, items: { min: number, max: number }[]) { + if (items.length === 0) { + return []; + } + const middleSize = findMiddleSize(availableSpace, items); - return items.map(item => clamp(middleSize, item.min, item.max)); + const sizes = items.map(item => clamp(middleSize, item.min, item.max)); + if (middleSize !== Math.floor(availableSpace / items.length)) { + distributeRemainder(availableSpace, middleSize, sizes, items); + } + return sizes; function findMiddleSize(availableSpace: number, items: { min: number, max: number }[]) { - if (items.length === 0) { - return 0; - } - const ranges = buildRanges(items); let requiredSpace = items.reduce((acc, item) => acc + item.min, 0); for (const range of ranges) { @@ -50,6 +54,28 @@ function fillSpace(availableSpace: number, items: { min: number, max: number }[] return array; } + function distributeRemainder(availableSpace: number, middleSize: number, sizes: number[], constraints: { max: number }[]) { + const indexes = Array.from(sizes.keys()) + .filter(i => sizes[i] === middleSize); + indexes.sort((a, b) => constraints[a].max - constraints[b].max); + + const requiredSpace = sum(...sizes); + let remainder = availableSpace - requiredSpace; + let n = indexes.length; + for (const i of indexes) { + if (remainder <= 0) { + break; + } + const enlargable = constraints[i].max - sizes[i]; + if (enlargable > 0) { + const enlarge = Math.min(enlargable, Math.ceil(remainder / n)); + sizes[i] += enlarge; + remainder -= enlarge; + } + n--; + } + } + type Range = { start: number, end: number, diff --git a/src/tests/units/utils/fillSpace.ts b/src/tests/units/utils/fillSpace.ts index a012eee..cce16d7 100644 --- a/src/tests/units/utils/fillSpace.ts +++ b/src/tests/units/utils/fillSpace.ts @@ -58,7 +58,7 @@ tests.register("fillSpace", 1, () => { { min: 400, max: 500 }, { min: 10, max: 300 }, ], - expected: [116, 116, 400, 116], + expected: [117, 117, 400, 116], }, { availableSpace: 750, @@ -98,7 +98,7 @@ tests.register("fillSpace", 1, () => { { min: 109, max: 800 }, { min: 10, max: 800 }, ], - expected: [114, 93, 93, 93, 93, 93, 110, 110], + expected: [114, 93, 93, 93, 93, 93, 111, 110], }, { availableSpace: 801, @@ -126,7 +126,7 @@ tests.register("fillSpace", 1, () => { { min: 109, max: 800 }, { min: 10, max: 95 }, ], - expected: [120, 93, 93, 93, 93, 93, 120, 95], + expected: [121, 93, 93, 93, 93, 93, 120, 95], }, { availableSpace: 799, @@ -140,7 +140,7 @@ tests.register("fillSpace", 1, () => { { min: 10, max: 91}, { min: 105, max: 800 }, ], - expected: [79, 107, 107, 107, 107, 107, 79, 105], + expected: [80, 107, 107, 107, 107, 107, 79, 105], }, { availableSpace: 1029, @@ -184,7 +184,7 @@ tests.register("fillSpace", 1, () => { { min: 10, max: 600 }, { min: 10, max: 600 }, ], - expected: [204, 199, 199, 199], + expected: [204, 200, 200, 199], }, { availableSpace: 900,