{-# LANGUAGE TupleSections #-}
module Math.Elbow
( findElbow
, findElbowList
, getRotationAngle
, MaxMin (..)
) where
import Safe (headMay, atMay)
import qualified Numeric.LinearAlgebra as H
data MaxMin = Max | Min
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)
=> MaxMin -> H.Matrix a -> Maybe (Int, (a, a))
findElbow maxMin m = do
theta <- getRotationAngle m
let co = cos theta
si = sin theta
rot = (2 H.>< 2) [co, -si, si, co]
rotated = m H.<> rot
findMaxMin Max = H.maxIndex
findMaxMin Min = H.minIndex
idx <- fmap (findMaxMin maxMin) . flip atMay 1 . H.toColumns $ rotated
fmap (idx,) . listToTuple . H.toList $ m H.! idx
findElbowList :: (RealFloat a, H.Numeric a)
=> MaxMin -> [[a]] -> Maybe (Int, (a, a))
findElbowList maxMin = findElbow maxMin . H.fromColumns . fmap H.fromList