module Chiasma.Ui.Measure.Balance where

import Data.List (zipWith3)
import qualified Data.List.NonEmpty as NonEmpty

import Chiasma.Ui.Measure.Weights (
  amendAndNormalizeWeights,
  normalizeWeights,
  )

zipWith3NE :: (a -> b -> c -> d)  ->  NonEmpty a -> NonEmpty b -> NonEmpty c -> NonEmpty d
zipWith3NE :: forall a b c d.
(a -> b -> c -> d)
-> NonEmpty a -> NonEmpty b -> NonEmpty c -> NonEmpty d
zipWith3NE a -> b -> c -> d
z ~(a
a :| [a]
as) ~(b
b :| [b]
bs) ~(c
c :| [c]
cs) =
  a -> b -> c -> d
z a
a b
b c
c d -> [d] -> NonEmpty d
forall a. a -> [a] -> NonEmpty a
:| (a -> b -> c -> d) -> [a] -> [b] -> [c] -> [d]
forall a b c d. (a -> b -> c -> d) -> [a] -> [b] -> [c] -> [d]
zipWith3 a -> b -> c -> d
z [a]
as [b]
bs [c]
cs

data Balance =
  Balance {
    Balance -> NonEmpty Float
balanceMin :: NonEmpty Float,
    Balance -> NonEmpty (Maybe Float)
balanceMax :: NonEmpty (Maybe Float),
    Balance -> NonEmpty Float
balanceWeights :: NonEmpty Float,
    Balance -> NonEmpty Bool
balanceMinimized :: NonEmpty Bool,
    Balance -> Float
balanceTotal :: Float
  }

reverseWeights :: NonEmpty Float -> NonEmpty Float
reverseWeights :: NonEmpty Float -> NonEmpty Float
reverseWeights NonEmpty Float
weights =
  Float -> Float
rev (Float -> Float) -> NonEmpty Float -> NonEmpty Float
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> NonEmpty Float
r
  where
    rev :: Float -> Float
rev Float
a =
      Float -> Maybe Float -> Float
forall a. a -> Maybe a -> a
fromMaybe Float
a (Float
a Float -> Float -> Maybe Float
forall a. (Eq a, Fractional a) => a -> a -> Maybe a
/ Float
norm)
    r :: NonEmpty Float
r =
      (Float -> Float) -> NonEmpty Float -> NonEmpty Float
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Float
1 Float -> Float -> Float
forall a. Num a => a -> a -> a
-) NonEmpty Float
weights
    norm :: Float
norm =
      NonEmpty Float -> Float
forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum NonEmpty Float
r

cutSizes :: Balance -> NonEmpty Float
cutSizes :: Balance -> NonEmpty Float
cutSizes (Balance NonEmpty Float
minSizes NonEmpty (Maybe Float)
_ NonEmpty Float
weights NonEmpty Bool
_ Float
total) =
  Float -> Float
addNegatives (Float -> Float) -> NonEmpty Float -> NonEmpty Float
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> NonEmpty Float
truncatedWeighted
  where
    surplus :: Float
surplus =
      NonEmpty Float -> Float
forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum NonEmpty Float
minSizes Float -> Float -> Float
forall a. Num a => a -> a -> a
- Float
total
    surplusDistWeighted :: NonEmpty Float
surplusDistWeighted =
      (Float -> Float) -> NonEmpty Float -> NonEmpty Float
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Float
surplus Float -> Float -> Float
forall a. Num a => a -> a -> a
*) (NonEmpty Float -> NonEmpty Float
reverseWeights NonEmpty Float
weights)
    truncatedWeighted :: NonEmpty Float
truncatedWeighted =
      ((Float, Float) -> Float)
-> NonEmpty (Float, Float) -> NonEmpty Float
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((Float -> Float -> Float) -> (Float, Float) -> Float
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry (-)) (NonEmpty Float -> NonEmpty Float -> NonEmpty (Float, Float)
forall a b. NonEmpty a -> NonEmpty b -> NonEmpty (a, b)
NonEmpty.zip NonEmpty Float
minSizes NonEmpty Float
surplusDistWeighted)
    negOrZero :: a -> a
negOrZero a
a =
      if a
a a -> a -> Bool
forall a. Ord a => a -> a -> Bool
< a
0 then a
a else a
0
    neg :: NonEmpty Float
neg =
      (Float -> Float) -> NonEmpty Float -> NonEmpty Float
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Float -> Float
forall {a}. (Ord a, Num a) => a -> a
negOrZero NonEmpty Float
truncatedWeighted
    negTotal :: Float
negTotal =
      NonEmpty Float -> Float
forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum NonEmpty Float
neg
    negCount :: Int
negCount =
      [Float] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length ((Float -> Bool) -> NonEmpty Float -> [Float]
forall a. (a -> Bool) -> NonEmpty a -> [a]
NonEmpty.filter (Float -> Float -> Bool
forall a. Ord a => a -> a -> Bool
< Float
0) NonEmpty Float
neg)
    negativesDist :: Float
negativesDist =
      Float -> Maybe Float -> Float
forall a. a -> Maybe a -> a
fromMaybe Float
0 (Float
negTotal Float -> Float -> Maybe Float
forall a. (Eq a, Fractional a) => a -> a -> Maybe a
/ Int -> Float
forall a b. (Real a, Fractional b) => a -> b
realToFrac (NonEmpty Float -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length NonEmpty Float
minSizes Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
negCount))
    addNegatives :: Float -> Float
addNegatives Float
a =
      if Float
a Float -> Float -> Bool
forall a. Ord a => a -> a -> Bool
< Float
0 then Float
0 else Float
a Float -> Float -> Float
forall a. Num a => a -> a -> a
+ Float
negativesDist

distributeOnUnbounded :: Balance -> NonEmpty Float
distributeOnUnbounded :: Balance -> NonEmpty Float
distributeOnUnbounded (Balance NonEmpty Float
min' NonEmpty (Maybe Float)
max' NonEmpty Float
weights NonEmpty Bool
_ Float
total) =
  (Float -> Float -> Float)
-> NonEmpty Float -> NonEmpty Float -> NonEmpty Float
forall a b c.
(a -> b -> c) -> NonEmpty a -> NonEmpty b -> NonEmpty c
NonEmpty.zipWith Float -> Float -> Float
addWeights NonEmpty Float
initial NonEmpty Float
newWeights
  where
    initial :: NonEmpty Float
initial = (Float -> Maybe Float -> Float)
-> NonEmpty Float -> NonEmpty (Maybe Float) -> NonEmpty Float
forall a b c.
(a -> b -> c) -> NonEmpty a -> NonEmpty b -> NonEmpty c
NonEmpty.zipWith Float -> Maybe Float -> Float
forall a. a -> Maybe a -> a
fromMaybe NonEmpty Float
min' NonEmpty (Maybe Float)
max'
    newWeights :: NonEmpty Float
newWeights = NonEmpty Float -> NonEmpty Float
normalizeWeights (NonEmpty Float -> NonEmpty Float)
-> NonEmpty Float -> NonEmpty Float
forall a b. (a -> b) -> a -> b
$ (Float -> Maybe Float -> Float)
-> NonEmpty Float -> NonEmpty (Maybe Float) -> NonEmpty Float
forall a b c.
(a -> b -> c) -> NonEmpty a -> NonEmpty b -> NonEmpty c
NonEmpty.zipWith Float -> Maybe Float -> Float
forall {b} {a}. Num b => b -> Maybe a -> b
weightOrZeroIfMax NonEmpty Float
weights NonEmpty (Maybe Float)
max'
    addWeights :: Float -> Float -> Float
addWeights Float
i Float
w = Float
i Float -> Float -> Float
forall a. Num a => a -> a -> a
+ (Float
w Float -> Float -> Float
forall a. Num a => a -> a -> a
* Float
surplus)
    surplus :: Float
surplus = Float
total Float -> Float -> Float
forall a. Num a => a -> a -> a
- NonEmpty Float -> Float
forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum NonEmpty Float
initial
    weightOrZeroIfMax :: b -> Maybe a -> b
weightOrZeroIfMax b
w = b -> (a -> b) -> Maybe a -> b
forall b a. b -> (a -> b) -> Maybe a -> b
maybe b
w (b -> a -> b
forall a b. a -> b -> a
const b
0)

weightsWithoutMinimized :: Balance -> NonEmpty Float
weightsWithoutMinimized :: Balance -> NonEmpty Float
weightsWithoutMinimized (Balance NonEmpty Float
_ NonEmpty (Maybe Float)
_ NonEmpty Float
weights NonEmpty Bool
minimized Float
_) =
  NonEmpty Float -> NonEmpty Float
normalizeWeights NonEmpty Float
zeroIfMinimizedWeights
  where
    zeroIfMinimizedWeights :: NonEmpty Float
zeroIfMinimizedWeights = (Float -> Bool -> Float)
-> NonEmpty Float -> NonEmpty Bool -> NonEmpty Float
forall a b c.
(a -> b -> c) -> NonEmpty a -> NonEmpty b -> NonEmpty c
NonEmpty.zipWith Float -> Bool -> Float
forall {p}. Num p => p -> Bool -> p
zeroIfMinimized NonEmpty Float
weights NonEmpty Bool
minimized
    zeroIfMinimized :: p -> Bool -> p
zeroIfMinimized p
w Bool
m = if Bool
m then p
0 else p
w

trimWeights :: NonEmpty Bool -> NonEmpty Float -> NonEmpty Float
trimWeights :: NonEmpty Bool -> NonEmpty Float -> NonEmpty Float
trimWeights NonEmpty Bool
unsat NonEmpty Float
withoutMinimized =
  NonEmpty (Maybe Float) -> NonEmpty Float
amendAndNormalizeWeights NonEmpty (Maybe Float)
onlyUnsat
  where
    onlyUnsat :: NonEmpty (Maybe Float)
onlyUnsat = (Bool -> Float -> Maybe Float)
-> NonEmpty Bool -> NonEmpty Float -> NonEmpty (Maybe Float)
forall a b c.
(a -> b -> c) -> NonEmpty a -> NonEmpty b -> NonEmpty c
NonEmpty.zipWith Bool -> Float -> Maybe Float
forall {a}. Bool -> a -> Maybe a
weightIfUnsat NonEmpty Bool
unsat NonEmpty Float
withoutMinimized
    weightIfUnsat :: Bool -> a -> Maybe a
weightIfUnsat Bool
s a
w = if Bool
s then a -> Maybe a
forall a. a -> Maybe a
Just a
w else Maybe a
forall a. Maybe a
Nothing

distRest :: Balance -> Float -> NonEmpty Float -> NonEmpty Float -> NonEmpty Float
distRest :: Balance
-> Float -> NonEmpty Float -> NonEmpty Float -> NonEmpty Float
distRest Balance
balance Float
rest NonEmpty Float
sizes NonEmpty Float
effectiveMax =
  (Float -> Float -> Float)
-> NonEmpty Float -> NonEmpty Float -> NonEmpty Float
forall a b c.
(a -> b -> c) -> NonEmpty a -> NonEmpty b -> NonEmpty c
NonEmpty.zipWith Float -> Float -> Float
addRestWeights NonEmpty Float
sizes NonEmpty Float
restW
  where
    unsat :: NonEmpty Bool
unsat = (Float -> Float -> Bool)
-> NonEmpty Float -> NonEmpty Float -> NonEmpty Bool
forall a b c.
(a -> b -> c) -> NonEmpty a -> NonEmpty b -> NonEmpty c
NonEmpty.zipWith Float -> Float -> Bool
forall a. Ord a => a -> a -> Bool
(>) NonEmpty Float
effectiveMax NonEmpty Float
sizes
    unsatLeft :: Bool
unsatLeft = NonEmpty Bool -> Bool
forall (t :: * -> *). Foldable t => t Bool -> Bool
or NonEmpty Bool
unsat
    withoutMinimized :: NonEmpty Float
withoutMinimized = Balance -> NonEmpty Float
weightsWithoutMinimized Balance
balance
    restW :: NonEmpty Float
restW =
      if Bool
unsatLeft
      then NonEmpty Bool -> NonEmpty Float -> NonEmpty Float
trimWeights NonEmpty Bool
unsat NonEmpty Float
withoutMinimized
      else NonEmpty Float
withoutMinimized
    addRestWeights :: Float -> Float -> Float
addRestWeights Float
s Float
w = Float
s Float -> Float -> Float
forall a. Num a => a -> a -> a
+ Float
w Float -> Float -> Float
forall a. Num a => a -> a -> a
* Float
rest

saturate :: NonEmpty Float -> NonEmpty Float -> NonEmpty Float -> Float -> NonEmpty Float
saturate :: NonEmpty Float
-> NonEmpty Float -> NonEmpty Float -> Float -> NonEmpty Float
saturate NonEmpty Float
initial NonEmpty Float
max' NonEmpty Float
initialWeights Float
total =
  NonEmpty Float -> NonEmpty Float -> NonEmpty Float
loop NonEmpty Float
initial NonEmpty Float
initialWeights
  where
    loop :: NonEmpty Float -> NonEmpty Float -> NonEmpty Float
loop NonEmpty Float
current NonEmpty Float
weights =
      if NonEmpty Float
new NonEmpty Float -> NonEmpty Float -> Bool
forall a. Eq a => a -> a -> Bool
== NonEmpty Float
current Bool -> Bool -> Bool
|| Float
rest Float -> Float -> Bool
forall a. Ord a => a -> a -> Bool
<= Float
0 then NonEmpty Float
new else NonEmpty Float -> NonEmpty Float -> NonEmpty Float
loop NonEmpty Float
new NonEmpty Float
newWeights
      where
        rest :: Float
rest = Float
total Float -> Float -> Float
forall a. Num a => a -> a -> a
- NonEmpty Float -> Float
forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum NonEmpty Float
current
        unsatWeights :: NonEmpty Float
unsatWeights = (Float -> Float -> Float -> Float)
-> NonEmpty Float
-> NonEmpty Float
-> NonEmpty Float
-> NonEmpty Float
forall a b c d.
(a -> b -> c -> d)
-> NonEmpty a -> NonEmpty b -> NonEmpty c -> NonEmpty d
zipWith3NE (\Float
s Float
m Float
w -> if Float
s Float -> Float -> Bool
forall a. Ord a => a -> a -> Bool
>= Float
m then Float
0 else Float
w) NonEmpty Float
current NonEmpty Float
max' NonEmpty Float
weights
        newWeights :: NonEmpty Float
newWeights = NonEmpty Float -> NonEmpty Float
normalizeWeights NonEmpty Float
unsatWeights
        new :: NonEmpty Float
new = (Float -> Float -> Float -> Float)
-> NonEmpty Float
-> NonEmpty Float
-> NonEmpty Float
-> NonEmpty Float
forall a b c d.
(a -> b -> c -> d)
-> NonEmpty a -> NonEmpty b -> NonEmpty c -> NonEmpty d
zipWith3NE (\Float
l Float
h Float
w -> Float -> Float -> Float
forall a. Ord a => a -> a -> a
min (Float
l Float -> Float -> Float
forall a. Num a => a -> a -> a
+ Float
w Float -> Float -> Float
forall a. Num a => a -> a -> a
* Float
rest) Float
h) NonEmpty Float
current NonEmpty Float
max' NonEmpty Float
newWeights

distributeOnAll :: Balance -> NonEmpty Float
distributeOnAll :: Balance -> NonEmpty Float
distributeOnAll balance :: Balance
balance@(Balance NonEmpty Float
min' NonEmpty (Maybe Float)
max' NonEmpty Float
weights NonEmpty Bool
_ Float
total) =
  if Float
rest Float -> Float -> Bool
forall a. Ord a => a -> a -> Bool
<= Float
0 then NonEmpty Float
sizes else Balance
-> Float -> NonEmpty Float -> NonEmpty Float -> NonEmpty Float
distRest Balance
balance Float
rest NonEmpty Float
sizes NonEmpty Float
effectiveMax
  where
    effectiveMax :: NonEmpty Float
effectiveMax = Float -> Maybe Float -> Float
forall a. a -> Maybe a -> a
fromMaybe Float
1e6 (Maybe Float -> Float) -> NonEmpty (Maybe Float) -> NonEmpty Float
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> NonEmpty (Maybe Float)
max'
    sizes :: NonEmpty Float
sizes = NonEmpty Float
-> NonEmpty Float -> NonEmpty Float -> Float -> NonEmpty Float
saturate NonEmpty Float
min' NonEmpty Float
effectiveMax NonEmpty Float
weights Float
total
    rest :: Float
rest = Float
total Float -> Float -> Float
forall a. Num a => a -> a -> a
- NonEmpty Float -> Float
forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum NonEmpty Float
sizes

hasUnbounded :: Balance -> Bool
hasUnbounded :: Balance -> Bool
hasUnbounded =
  (Maybe Float -> Bool) -> NonEmpty (Maybe Float) -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any Maybe Float -> Bool
forall a. Maybe a -> Bool
isNothing (NonEmpty (Maybe Float) -> Bool)
-> (Balance -> NonEmpty (Maybe Float)) -> Balance -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Balance -> NonEmpty (Maybe Float)
balanceMax

distributeSizes :: Balance -> NonEmpty Float
distributeSizes :: Balance -> NonEmpty Float
distributeSizes Balance
balance =
  Balance -> NonEmpty Float
handler Balance
balance
  where
    handler :: Balance -> NonEmpty Float
handler =
      if (Float
maxTotal Float -> Float -> Bool
forall a. Ord a => a -> a -> Bool
< Balance -> Float
balanceTotal Balance
balance) Bool -> Bool -> Bool
&& Balance -> Bool
hasUnbounded Balance
balance
      then Balance -> NonEmpty Float
distributeOnUnbounded
      else Balance -> NonEmpty Float
distributeOnAll
    maxTotal :: Float
maxTotal = [Float] -> Float
forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum ([Maybe Float] -> [Float]
forall a. [Maybe a] -> [a]
catMaybes ([Maybe Float] -> [Float]) -> [Maybe Float] -> [Float]
forall a b. (a -> b) -> a -> b
$ NonEmpty (Maybe Float) -> [Maybe Float]
forall a. NonEmpty a -> [a]
NonEmpty.toList (NonEmpty (Maybe Float) -> [Maybe Float])
-> NonEmpty (Maybe Float) -> [Maybe Float]
forall a b. (a -> b) -> a -> b
$ Balance -> NonEmpty (Maybe Float)
balanceMax Balance
balance)

roundSizes :: NonEmpty Float -> NonEmpty Int
roundSizes :: NonEmpty Float -> NonEmpty Int
roundSizes (Float
h :| [Float]
t) =
  Int
roundedHead Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Float -> Int
forall a b. (RealFrac a, Integral b) => a -> b
round Float
surplus Int -> [Int] -> NonEmpty Int
forall a. a -> [a] -> NonEmpty a
:| [Int]
roundedTail
  where
    (Float
surplus, [Int]
roundedTail) = (Float -> Float -> (Float, Int))
-> Float -> [Float] -> (Float, [Int])
forall (t :: * -> *) s a b.
Traversable t =>
(s -> a -> (s, b)) -> s -> t a -> (s, t b)
mapAccumL Float -> Float -> (Float, Int)
forall {a} {b}. (RealFrac a, Integral b) => a -> a -> (a, b)
folder Float
diff0 [Float]
t
    (Int
roundedHead, Float
diff0) = Float -> (Int, Float)
forall {b} {a}. (RealFrac b, Integral a) => b -> (a, b)
diff Float
h
    folder :: a -> a -> (a, b)
folder a
z a
a =
      (a
z a -> a -> a
forall a. Num a => a -> a -> a
+ a
z1, b
a1)
      where
        (b
a1, a
z1) = a -> (b, a)
forall {b} {a}. (RealFrac b, Integral a) => b -> (a, b)
diff a
a
    diff :: b -> (a, b)
diff b
a = (b -> a
forall a b. (RealFrac a, Integral b) => a -> b
floor b
a, b
a b -> b -> b
forall a. Num a => a -> a -> a
- Int -> b
forall a b. (Integral a, Num b) => a -> b
fromIntegral (b -> Int
forall a b. (RealFrac a, Integral b) => a -> b
floor b
a :: Int))

-- |Tmux doesn't render panes smaller than two cells.
ensureMinimum2 :: NonEmpty Float -> NonEmpty Float
ensureMinimum2 :: NonEmpty Float -> NonEmpty Float
ensureMinimum2 NonEmpty Float
sizes =
  Float -> Float
choose (Float -> Float) -> NonEmpty Float -> NonEmpty Float
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> NonEmpty Float
positives
  where
    positive :: Float -> Float
positive =
      Float -> Float -> Float
forall a. Ord a => a -> a -> a
max Float
0
    positives :: NonEmpty Float
positives =
      Float -> Float
positive (Float -> Float) -> NonEmpty Float -> NonEmpty Float
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> NonEmpty Float
sizes
    overTwoCount :: Int
overTwoCount =
      [Float] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length ((Float -> Bool) -> NonEmpty Float -> [Float]
forall a. (a -> Bool) -> NonEmpty a -> [a]
NonEmpty.filter (Float -> Float -> Bool
forall a. Ord a => a -> a -> Bool
>= Float
2) NonEmpty Float
sizes)
    unders :: NonEmpty Float
unders =
      Float -> Float
amountUnderTwo (Float -> Float) -> NonEmpty Float -> NonEmpty Float
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> NonEmpty Float
positives
    -- If no sizes are larger than 2, nothing will have to be subtracted, all sizes will be clamped to 2
    amountUnderTwoDist :: Float
amountUnderTwoDist =
      Float -> Maybe Float -> Float
forall a. a -> Maybe a -> a
fromMaybe Float
0 (NonEmpty Float -> Float
forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum NonEmpty Float
unders Float -> Float -> Maybe Float
forall a. (Eq a, Fractional a) => a -> a -> Maybe a
/ Int -> Float
forall a b. (Real a, Fractional b) => a -> b
realToFrac Int
overTwoCount)
    amountUnderTwo :: Float -> Float
amountUnderTwo Float
a =
      Float -> Float
positive (Float
2 Float -> Float -> Float
forall a. Num a => a -> a -> a
- Float
a)
    choose :: Float -> Float
choose Float
a =
      Float -> Float -> Float
forall a. Ord a => a -> a -> a
max Float
2 (Float
a Float -> Float -> Float
forall a. Num a => a -> a -> a
- Float
amountUnderTwoDist)

rectifySizes :: NonEmpty Float -> NonEmpty Int
rectifySizes :: NonEmpty Float -> NonEmpty Int
rectifySizes =
  NonEmpty Float -> NonEmpty Int
roundSizes (NonEmpty Float -> NonEmpty Int)
-> (NonEmpty Float -> NonEmpty Float)
-> NonEmpty Float
-> NonEmpty Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. NonEmpty Float -> NonEmpty Float
ensureMinimum2

-- FIXME need to round manually, keeping track of the surplus, in order to achieve determinism
balanceSizes :: NonEmpty Float -> NonEmpty (Maybe Float) -> NonEmpty Float -> NonEmpty Bool -> Float -> NonEmpty Int
balanceSizes :: NonEmpty Float
-> NonEmpty (Maybe Float)
-> NonEmpty Float
-> NonEmpty Bool
-> Float
-> NonEmpty Int
balanceSizes NonEmpty Float
minSizes NonEmpty (Maybe Float)
maxSizes NonEmpty Float
weights NonEmpty Bool
minimized Float
total =
  NonEmpty Float -> NonEmpty Int
rectifySizes (Balance -> NonEmpty Float
fit Balance
balance)
  where
    fit :: Balance -> NonEmpty Float
fit = if NonEmpty Float -> Float
forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum NonEmpty Float
minSizes Float -> Float -> Bool
forall a. Ord a => a -> a -> Bool
> Float
total then Balance -> NonEmpty Float
cutSizes else Balance -> NonEmpty Float
distributeSizes
    balance :: Balance
balance = NonEmpty Float
-> NonEmpty (Maybe Float)
-> NonEmpty Float
-> NonEmpty Bool
-> Float
-> Balance
Balance NonEmpty Float
minSizes NonEmpty (Maybe Float)
maxSizes NonEmpty Float
weights NonEmpty Bool
minimized Float
total