```module Numeric.Limp.Solvers.Cbc.MatrixRepr where
import Numeric.Limp.Canon
import Numeric.Limp.Rep

import Data.Either (isLeft)

import qualified Data.Map as M
import qualified Data.Set as S

import qualified Data.Vector.Storable as V
import Data.Vector.Storable (Vector)
import Numeric.Limp.Solvers.Cbc.Internal.Wrapper

-- | A half-sparse, half-dense matrix (tableau?) representation of a linear program.
-- Columns are variables, and constraints are rows.
data MatrixRepr
= MatrixRepr
{ _starts   :: Vector Int
-- ^ starting indices segment descriptor for indsvals.
-- one per row (constraint).
-- but for some reason, there's an extra one at the end.
-- starts ! 0 == 0 && starts ! (length starts - 1) == length indsvals
-- (I think)
--
, _inds     :: Vector Int
, _vals     :: Vector Double
-- ^ segmented array. each pair is an index of the column (variable), and the coefficient.
-- the segment number is the row (constraint).
-- ^ Coefficients of the constraints

, _colLs    :: Vector Double
, _colUs    :: Vector Double
-- ^ Lower and upper bound for each variable

, _obj      :: Vector Double
-- ^ Coefficients of each variable in the objective function

, _rowLs    :: Vector Double
, _rowUs    :: Vector Double
-- ^ Lower and upper bound for each constraint

, _ints     :: Vector Int
-- ^ Indices of each integer variable.
}
deriving Show

matrixReprOfProgram :: (Ord z, Ord r) => Program z r IntDouble -> MatrixRepr
matrixReprOfProgram p
= MatrixRepr starts inds vals colLs colUs obj rowLs rowUs ints
where

starts
= V.fromList
\$ scanl (+) 0
\$ map length nestedindvals

inds = V.fromList \$ map fst \$ concat nestedindvals
vals = V.fromList \$ map snd \$ concat nestedindvals

nestedindvals = map getIndVals \$ S.toList vs

colLs = V.fromList \$ map (fst . boundsOfVar) \$ S.toList vs
colUs = V.fromList \$ map (snd . boundsOfVar) \$ S.toList vs

obj
= case _objective p of
Linear m
-> V.fromList
\$ map (maybe 0 unwrapR . flip M.lookup m) \$ S.toList vs

rowLs = V.fromList \$ map (fst . boundsOfCon) cons
rowUs = V.fromList \$ map (snd . boundsOfCon) cons

ints = V.fromList \$ map snd \$ filter (isLeft . fst) \$ S.toList vs `zip` [0..]

vs = varsOfProgram p
-- nvars = S.size vs
-- ncons = length cons
cons = case _constraints p of
Constraint cs -> cs

getIndVals v
= concatMap (\(C1 _ (Linear m) _, ix) ->
case M.lookup v m of
Nothing -> []
Just mul -> [(ix, unwrapR mul)]) (cons `zip` [0..])

boundsOfVar v
= case M.lookup v \$ _bounds p of
Nothing    -> (lower Nothing, upper Nothing)
Just (l,u) -> (lower l,       upper u)

boundsOfCon (C1 l _ u) = (lower l, upper u)

upper = maybe   getCoinDblMax  unwrapR
lower = maybe (-getCoinDblMax) unwrapR

makeAssignment :: (Ord z, Ord r) => Program z r IntDouble -> Vector Double -> Assignment z r IntDouble
makeAssignment p values
= go M.empty M.empty
\$ S.toList (varsOfProgram p) `zip` V.toList   values
where
go mz mr []
= Assignment mz mr

go mz mr ((Left z, val) : rest)
= go (M.insert z (Z \$ truncate val) mz) mr rest

go mz mr ((Right r, val) : rest)
= go mz (M.insert r (R \$ val) mr) rest

```