{-# LANGUAGE TupleSections #-}
module Math.Elbow
( findElbow
, findElbowList
, getRotationAngle
, MinMax (..)
) where
import Safe (headMay, atMay)
import qualified Numeric.LinearAlgebra as H
data MinMax = Min | Max deriving (Read, Show)
listToTuple :: [a] -> Maybe (a, a)
listToTuple [x, y] = Just (x, y)
listToTuple _ = Nothing
getRotationAngle :: (RealFloat a, H.Numeric a) => H.Matrix a -> Maybe a
getRotationAngle = fmap (uncurry atan2)
. listToTuple
. reverse
. fmap (\x -> H.maxElement x - H.minElement x)
. H.toColumns
findElbow :: (RealFloat a, H.Numeric a)
=> MinMax -> H.Matrix a -> Maybe (Int, (a, a))
findElbow minMax m = do
theta <- getRotationAngle m
let co = cos theta
si = sin theta
rot = (2 H.>< 2) [co, -si, si, co]
rotated = m H.<> rot
findMinMax Max = H.maxIndex
findMinMax Min = H.minIndex
idx <- fmap (findMinMax minMax) . flip atMay 1 . H.toColumns $ rotated
fmap (idx,) . listToTuple . H.toList $ m H.! idx
findElbowList :: (RealFloat a, H.Numeric a)
=> MinMax -> [[a]] -> Maybe (Int, (a, a))
findElbowList minMax = findElbow minMax . H.fromColumns . fmap H.fromList