implement helper function findMeanInt
This commit is contained in:
@@ -42,6 +42,51 @@ function findMinPositive<T>(items: T[], evaluate: (item: T) => number) {
|
||||
return bestItem;
|
||||
}
|
||||
|
||||
function findMeanInt(sum: number, constraints: { min: number, max: number }[]) {
|
||||
let mean = Math.floor(sum / constraints.length);
|
||||
while (true) {
|
||||
let actualSum = 0;
|
||||
let increasable = 0;
|
||||
let decreasable = 0;
|
||||
let low = -Infinity;
|
||||
let high = Infinity;
|
||||
for (const constraint of constraints) {
|
||||
const value = clamp(mean, constraint.min, constraint.max);
|
||||
actualSum += value;
|
||||
if (mean > constraint.min) {
|
||||
decreasable++;
|
||||
if (value > low) {
|
||||
low = value;
|
||||
}
|
||||
}
|
||||
if (mean < constraint.max) {
|
||||
increasable++;
|
||||
if (value < high) {
|
||||
high = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const oldMean = mean;
|
||||
const error = actualSum - sum;
|
||||
if (error > 0) {
|
||||
// need to decrease mean
|
||||
if (decreasable > 0) {
|
||||
mean = Math.floor(low - error / decreasable);
|
||||
}
|
||||
} else if (error < 0) {
|
||||
// need to increase mean
|
||||
if (increasable > 0) {
|
||||
mean = Math.floor(high - error / increasable);
|
||||
}
|
||||
}
|
||||
|
||||
if (mean === oldMean) {
|
||||
return mean;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function rectEquals(a: QmlRect, b: QmlRect) {
|
||||
return a.x === b.x &&
|
||||
a.y === b.y &&
|
||||
|
||||
93
src/tests/units/utils/findMean.ts
Normal file
93
src/tests/units/utils/findMean.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
tests.register("findMean", 1, () => {
|
||||
const testCases: {
|
||||
sum: number,
|
||||
constraints: { min: number, max: number }[],
|
||||
check: (result: number) => boolean,
|
||||
}[] = [
|
||||
{
|
||||
sum: 600,
|
||||
constraints: [
|
||||
{ min: 10, max: 600 },
|
||||
{ min: 10, max: 600 },
|
||||
],
|
||||
check: r => r === 300,
|
||||
},
|
||||
{
|
||||
sum: 600,
|
||||
constraints: [
|
||||
{ min: 10, max: 250 },
|
||||
{ min: 10, max: 500 },
|
||||
],
|
||||
check: r => r === 350,
|
||||
},
|
||||
{
|
||||
sum: 600,
|
||||
constraints: [
|
||||
{ min: 10, max: 250 },
|
||||
{ min: 400, max: 500 },
|
||||
],
|
||||
check: r => r === 200,
|
||||
},
|
||||
{
|
||||
sum: 765,
|
||||
constraints: [
|
||||
{ min: 10, max: 250 },
|
||||
{ min: 10, max: 254 },
|
||||
{ min: 10, max: 500 },
|
||||
],
|
||||
check: r => r === 261,
|
||||
},
|
||||
{
|
||||
sum: 600,
|
||||
constraints: [
|
||||
{ min: 10, max: 150 },
|
||||
{ min: 400, max: 500 },
|
||||
],
|
||||
check: r => r === 450,
|
||||
},
|
||||
{
|
||||
sum: 750,
|
||||
constraints: [
|
||||
{ min: 10, max: 250 },
|
||||
{ min: 10, max: 250 },
|
||||
{ min: 400, max: 500 },
|
||||
{ min: 10, max: 300 },
|
||||
],
|
||||
check: r => r === 116,
|
||||
},
|
||||
{
|
||||
sum: 750,
|
||||
constraints: [
|
||||
{ min: 10, max: 250 },
|
||||
{ min: 120, max: 250 },
|
||||
{ min: 400, max: 500 },
|
||||
{ min: 10, max: 300 },
|
||||
],
|
||||
check: r => r === 115,
|
||||
},
|
||||
{
|
||||
sum: 1200,
|
||||
constraints: [
|
||||
{ min: 10, max: 250 },
|
||||
{ min: 10, max: 500 },
|
||||
],
|
||||
check: r => r >= 500,
|
||||
},
|
||||
{
|
||||
sum: 5,
|
||||
constraints: [
|
||||
{ min: 10, max: 250 },
|
||||
{ min: 10, max: 500 },
|
||||
],
|
||||
check: r => r <= 10,
|
||||
},
|
||||
];
|
||||
|
||||
for (const testCase of testCases) {
|
||||
const result = findMeanInt(testCase.sum, testCase.constraints);
|
||||
Assert.assert(
|
||||
testCase.check(result),
|
||||
{ message: `got ${result} for test case ${JSON.stringify(testCase)}` },
|
||||
);
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user