```{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE ScopedTypeVariables #-}

module Data.Linear (
Linear,
integerToLinear,
constToLinear,
termToLinear,
--  linearOpLinear,
--  linearOpLinears,
linearToConst,
linearToTerm,
linearMultiply,
linearMult,
linearToList, linearToListEx,
getCoef,
) where

import qualified Data.Map as Map
import Data.Map(Map)

data (Ord t, Num v) => Linear t v = Linear v (Map t v)

deriving instance (Num v, Eq v, Ord t, Eq t) => Eq (Linear t v)
deriving instance (Num v, Ord v, Ord t, Eq t) => Ord (Linear t v)
deriving instance (Num v, Show v, Ord t, Show t) => Show (Linear t v)

termToLinear :: (Num v, Ord t) => t -> Linear t v
termToLinear x = Linear 0 \$ Map.singleton x 1

integerToLinear :: (Num v, Ord t) => Integer -> Linear t v
integerToLinear = constToLinear . fromInteger

constToLinear :: (Num v, Ord t) => v -> Linear t v
constToLinear x = Linear x Map.empty

-- linearOpLinear :: (Num v, Ord t) => v -> Linear t v -> v -> Linear t v -> Linear t v
-- linearOpLinear a (Linear ac am) b (Linear bc bm) = Linear (a*ac+b*bc) \$ Map.filter (/=0) \$ Map.unionWith (\ax bx -> ax*a+bx*b) am bm

-- linearOpLinears :: (Num v, Ord t) => [(v,Linear t v)] -> Linear t v
-- linearOpLinears l = foldr (\(c,t) a -> linearOpLinear 1 a c t) (integerToLinear 0) l

linearToList :: (Ord t, Num v) => Linear t v -> [(Maybe t,v)]
linearToList (Linear c m) = [(Nothing,c)] ++ (map (\(a,b) -> (Just a,b)) \$ Map.toList m)

linearToListEx :: (Ord t, Num v) => Linear t v -> (v,[(t,v)])
linearToListEx (Linear c m) = (c,Map.toList m)

getCoef :: (Num v, Ord t) => Maybe t -> Linear t v -> v
getCoef Nothing (Linear c _) = c
getCoef (Just t) (Linear _ m) = Map.findWithDefault 0 t m

linearMult :: (Num v, Eq v, Ord t) => v -> Linear t v -> Linear t v
linearMult m (Linear ac am) = Linear (m*ac) \$ if (m==0) then Map.empty else Map.filter (/=0) \$ Map.map (m*) am

linearMultiply :: (Num v, Eq v, Ord t) => Linear t v -> Linear t v -> Maybe (Linear t v)
linearMultiply (Linear ac am) bl | (Map.null am) = Just \$ linearMult ac bl
linearMultiply bl (Linear ac am) | (Map.null am) = Just \$ linearMult ac bl
linearMultiply _ _ = Nothing

linearToConst :: (Num v, Ord t) => Linear t v -> Maybe v
linearToConst (Linear c m) | Map.null m = Just c
linearToConst _ = Nothing

linearToTerm :: (Num v, Eq v, Ord t) => Linear t v -> Maybe t
linearToTerm (Linear c m) | (c==0 && (Map.size m)==1) =
let (t,v) = Map.findMin m
in if (v==1) then Just t else Nothing
linearToTerm _ = Nothing

instance (Num v, Eq v, Ord t, Eq t, Show t) => Num (Linear t v) where
(Linear ac am) + (Linear bc bm) = Linear (ac+bc) \$ Map.filter (/=0) \$ Map.unionWith (+) am bm
(Linear ac am) - (Linear bc bm) = Linear (ac-bc) \$ Map.filter (/=0) \$ Map.unionWith (+) am \$ Map.map negate bm
a * b = case linearMultiply a b of Just x -> x; Nothing -> error "Cannot multiply generic linear expressions"
negate (Linear ac am) = Linear (-ac) \$ Map.map negate am
abs (Linear ac am) | (Map.null am) = Linear (abs ac) Map.empty
abs _ = error "Cannot take abs of generic linear expressions"
signum (Linear ac am) | (Map.null am) = Linear (signum ac) Map.empty
signum _ = error "Cannot take signum of generic linear expressions"
fromInteger x = integerToLinear x
```