{-|
v 0.2.1
Welcome to GDB Online.
GDB online is an online compiler and debugger tool for C, C++, Python, Java, PHP, Ruby, Perl,
C#, VB, Swift, Pascal, Fortran, Haskell, Objective-C, Assembly, HTML, CSS, JS, SQLite, Prolog.
Code, Compile, Run and Debug online from anywhere in world.
TP todo:
- different solutions should be possible
Whenever it is possible to assign more than one operation,
user should be asked, which operation should be assigned.
It can be achieved by the solve function to return either a new
solution, or a choice of operations. (One of many ways...)
Beware: the code was written in a hurry. It's not pretty, and shouldn't be
regarded as something exemplary in any way (both Haskell, and ALB).
-}
import Data.List as DL
import Debug.Trace as T
-- Op <id/number> <time> "succs"|"preds" [<id of related operations]
problem :: [Op]
problem = [
Op 1 1 "succs" [3, 4],
Op 2 2 "succs" [5],
Op 3 1 "succs" [6],
Op 4 5 "succs" [6],
Op 5 2 "succs" [6, 7],
Op 6 2 "succs" [9, 10],
Op 7 2 "succs" [10],
Op 8 4 "succs" [10],
Op 9 5 "succs" [11, 12],
Op 10 2 "succs" [12],
Op 11 4 "preds" [9],
Op 12 3 "succs" [13],
Op 13 5 "preds" [12]
]
cycle_time = 8
-- rpwWeights, wetWeights, nopWeights, noipWeights,
-- nofWeights, noifWeights
-- 8 = cycle time
sol = solve1 tmp_problem rpwWeights cycle_time
data OpTime
data OpId
data OpWeight
data OpTimeStart
data OpTimeEnd
-- The integer type.
data IntVal a
= IntVal
Int deriving (Eq, Show)
data RelOps
= Succs [IntVal OpId]
| Preds [IntVal OpId]
data Operation = Operation {
oid :: IntVal OpId,
time :: IntVal OpTime,
relops :: RelOps
type BLMProblem = [Operation]
opId
:: Int -> IntVal OpId
opId = IntVal
opTime
:: Int -> IntVal OpTime
opTime = IntVal
opWeight
:: Int -> IntVal OpWeight
opWeight = IntVal
opTimeStart
:: Int -> IntVal OpTimeStart
opTimeStart = IntVal
opTimeEnd
:: Int -> IntVal OpTimeEnd
opTimeEnd = IntVal
operation :: Op -> Operation
operation (Op op_id op_time op_rel op_id_lst) =
let o_id = opId op_id
o_time = opTime op_time
id
_lst
= map opId op
_id
_lst
constructor =
case op_rel of
"succs" -> Succs
_ -> Preds
in Operation o_id o_time $ constructor id_lst
--in case op_rel of
-- "succs" -> Operation o_id o_time $ Succs id_lst
-- _ -> Operation o_id o_time $ Preds id_lst
blmProblem :: [Op] -> BLMProblem
blmProblem
= map operation
type Weight = (IntVal OpId, IntVal OpWeight)
type WeightFunc = BLMProblem -> [Weight]
noifWeights :: WeightFunc
noifWeights problem
= map noifWeight problem
where
noifWeight :: Operation -> Weight
noifWeight op
= (oid op
, opWeight
$ length $ immediateSuccs problem op
)
noipWeights :: WeightFunc
noipWeights problem
= map noipWeight problem
where
noipWeight :: Operation -> Weight
noipWeight op
= (oid op
, opWeight
$ length $ immediatePreds problem op
)
wetWeights :: WeightFunc
wetWeights problem
= map wetWeight problem
where
wetWeight :: Operation -> Weight
wetWeight
(Operation
id (IntVal op
_time
) _rel
) =
nextWeights :: BLMProblem -> [Weight] -> [Operation]
nextWeights blm
_problem wgts
= filter (canCalcWeight blm
_problem wgts
) blm
_problem
where
canCalcWeight
:: BLMProblem
-> [Weight
] -> Operation
-> Bool canCalcWeight blm_problem wgts op =
let succs = immediateSuccs blm_problem op
can
_calc
= all (\op
_id
-> elem op
_id wgts
_ids
) succs
in can
_calc
&& (not $ elem (oid op
) wgts
_ids
)
type WeightsOperationFunc = ([Weight] -> Operation -> Weight)
rpwWeights :: WeightFunc
rpwWeights problem = calcWeights problem (rpwWeight problem) []
nofWeights :: WeightFunc
nofWeights problem
= map (nofWeight problem
) problem
nopWeights :: WeightFunc
nopWeights problem
= map (nopWeight problem
) problem
calcWeights :: BLMProblem -> WeightsOperationFunc -> [Weight] -> [Weight]
calcWeights problem weight_func wgts =
let op_ids = nextWeights problem wgts
add
_wgts
= map (weight
_func wgts
) op
_ids
new
_wgts
= concat [wgts
, add
_wgts
] in if allWeightsKnown problem new_wgts
then new_wgts
else calcWeights problem weight_func new_wgts
allWeightsKnown
:: BLMProblem
-> [Weight
] -> BoolallWeightsKnown problem wgts =
let problem
_op
_ids
= map oid problem
in all (\op
_id
-> elem op
_id wgts
_op
_ids
) problem
_op
_ids
rpwWeight :: BLMProblem -> WeightsOperationFunc
rpwWeight problem wgts the_op@(Operation op_id op_time _) =
let succs = immediateSuccs problem the_op
in case succs of
[] -> let (IntVal tm) = op_time
in (op_id, opWeight tm)
_ -> let the
_wgts
= filter (\
(op
_id
, _) -> elem op
_id succs
) wgts
int
_wgts
= map (\
(_, IntVal w
) -> w
) the
_wgts
(IntVal tm) = time the_op
in (oid the_op, opWeight $ the_weight + tm)
nofWeight :: BLMProblem -> Operation -> Weight
nofWeight blm_problem the_op =
let w
= length $ allRels immSuccsId blm
_problem the
_op
in (oid the_op, opWeight (w-1))
nopWeight :: BLMProblem -> Operation -> Weight
nopWeight blm_problem the_op =
let w
= length $ allRels immPredsId blm
_problem the
_op
in (oid the_op, opWeight (w-1))
operationWeight :: [Weight] -> Operation -> Weight
operationWeight weights op =
head $ filter (\
(w
_op
, _) -> w
_op
== (oid op
)) weights
allRels :: (BLMProblem -> IntVal OpId -> [IntVal OpId]) -> BLMProblem -> Operation -> [IntVal OpId]
allRels func_id blm_problem (Operation op_id _ _) = calcRels op_id ([], [])
where
calcRels :: IntVal OpId -> ([IntVal OpId], [IntVal OpId]) -> [IntVal OpId]
calcRels op_id (counted, to_count) =
then countNext counted to_count
else
case func_id blm_problem op_id of
[] -> countNext (op_id:counted) to_count
succs ->
let new_counted = op_id:counted
new
_to
_count
= concat [to
_count
, succs
] in countNext new_counted new_to_count
countNext :: [IntVal OpId] -> [IntVal OpId] -> [IntVal OpId]
countNext counted to_count =
case to_count of
[] -> counted
_ ->
let op_id:rest = to_count
in calcRels op_id (counted, rest)
immSuccsId :: BLMProblem -> IntVal OpId -> [IntVal OpId]
immSuccsId = immRelsId immediateSuccs
--immSuccsId blm_problem op_id =
-- let the_op = head $ filter (\op -> oid op == op_id) blm_problem
-- in immediateSuccs blm_problem the_op
immPredsId :: BLMProblem -> IntVal OpId -> [IntVal OpId]
immPredsId = immRelsId immediatePreds
immRelsId :: (BLMProblem -> Operation -> [IntVal OpId]) -> BLMProblem -> IntVal OpId -> [IntVal OpId]
immRelsId func blm_problem op_id =
let the
_op
= head $ filter (\op
-> oid op
== op
_id
) blm
_problem
in func blm_problem the_op
immediateSuccs :: BLMProblem -> Operation -> [IntVal OpId]
immediateSuccs blm_problem (Operation op_id _ op_rels) =
case op_rels of
Succs rels -> rels
Preds rels -> immSuccs blm_problem op_id
immediatePreds :: BLMProblem -> Operation -> [IntVal OpId]
immediatePreds blm_problem (Operation op_id _ op_rels) =
case op_rels of
Preds rels -> rels
Succs rels -> immPreds blm_problem op_id
immSuccs :: BLMProblem -> IntVal OpId -> [IntVal OpId]
immSuccs blm_problem op_id =
immPreds :: BLMProblem -> IntVal OpId -> [IntVal OpId]
immPreds blm_problem op_id =
-- find all the ops, for which op_id is one of succs
-- those are THE operations
--let succ_ops = filter (hasSucc op_id) blm_problem
-- in map oid succ_ops
hasPred
:: IntVal OpId
-> Operation
-> BoolhasPred op_id op =
case (relops op) of
Preds preds ->
let ops
= filter ((==) op
_id
) preds
in case ops of
[_] -> True
_ -> False
_ -> False
hasSucc
:: IntVal OpId
-> Operation
-> BoolhasSucc op_id op =
case (relops op) of
Succs succs ->
let ops
= filter ((==) op
_id
) succs
in case ops of
[_] -> True
_ -> False
_ -> False
-- 1) policz wagi
-- 2) otwórz stację roboczą
-- 3) wybierz operację do przypisania
-- a) relacje kolejnościowe
-- b) czas stacji
-- c) wagi
-- 4) brak operacji? goto 2
-- 5) wiele operacji? wybierz dowolną
-- 6) przypisz operację do stacji; goto 3
type OpScheduled = (IntVal OpId, IntVal OpTimeStart, IntVal OpTimeEnd)
type Station = [OpScheduled]
type Solution1 = [Station]
data ErrOpsTodo
= ErrNoOps
| ErrNoTime
| ErrNoWeightWTF
-- Yep, I do realise, that 1113 is VERY ugly.
-- Let's get serious, though.
-- How many students are ACTUALLY going to make up so complex examples,
-- that 1113 is not enough?
-- Therefore, I'm going to leave the number here.
solve1
:: BLMProblem
-> WeightFunc
-> Int -> Solution1
solve1 blm
_problem w
_func
cycle = solve
1113 [] where
weights = w_func blm_problem
solve
:: Int -> Solution1
-> Solution1
solve n sol =
if n > 0
then if solutionComplete blm_problem sol
then sol
else case (nextSolution sol) of
Nothing -> sol
Just new_sol -> solve (n-1) new_sol
else sol
solutionComplete
:: BLMProblem
-> Solution1
-> Bool solutionComplete blm_problem sol =
case blm_problem of
[] -> True
(op:rest) ->
if opDone sol (oid op)
then solutionComplete rest sol
else False
nextSolution
:: Solution1
-> Maybe Solution1
nextSolution old_sol =
let e_ops = availableOps blm_problem weights old_sol
in case (e_ops, old_sol) of
(Left ErrNoTime, _) -> Just $ []:old_sol -- open a new station
(Left err
, []) -> T
.trace
(show err
) Nothing
-- WTF? (Right ops, []) -> -- schedule the first operation
(IntVal op_time) = time the_op
op_id = oid the_op
sch_op = (op_id, opTimeStart 0, opTimeEnd op_time)
in Just [[sch_op]]
(Right ops, (st:rest)) ->
case st of
[] -> -- schedule the first operation
(IntVal op_time) = time the_op
op_id = oid the_op
sch_op = (op_id, opTimeStart 0, opTimeEnd op_time)
in Just $ [sch_op]:rest
(last_op:_) -> -- schedule the next operation
(IntVal op_time) = time the_op
op_id = oid the_op
(_, _, IntVal time_start) = last_op
sch_op = (op_id, opTimeStart time_start, opTimeEnd (op_time + time_start))
in Just $ (sch_op:st):rest
availableOps
:: BLMProblem
-> [Weight
] -> Solution1
-> Either ErrOpsTodo
[Operation
] availableOps blm_problem weights solution =
canBeDoneRels blm_problem solution
>>= canBeDoneTime blm
_problem solution
cycle >>= bestWeight weights
processWeights
:: [Weight
] -> Maybe ([Operation
], Weight
) -> Operation
-> Maybe ([Operation
], Weight
) processWeights weights mb_op op =
let weight = operationWeight weights op
in
case mb_op of
Nothing -> Just ([op], weight)
Just (old_ops, old_w) ->
let (_, IntVal old_w_int) = old_w
(_, IntVal w_int) = weight
in
if old_w_int < w_int
then Just ([op], weight)
else if old_w_int == w_int
then Just (op:old_ops, weight)
else mb_op
bestWeight
:: [Weight
] -> [Operation
] -> Either ErrOpsTodo
[Operation
] bestWeight weights ops =
let result
= foldl (processWeights weights
) Nothing ops
in case result of
Nothing -> Left ErrNoWeightWTF
Just (ops, _) -> Right ops
canBeDoneTime
:: BLMProblem
-> Solution1
-> Int -> [Operation
] -> Either ErrOpsTodo
[Operation
] canBeDoneTime blm
_problem solution
cycle ops
= let st_time = currentStationTime solution
rem
_time
= cycle - st
_time
ops
_within
_time
= foldl (processOpTime blm
_problem rem
_time
) [] ops
in case ops_within_time of
[] -> Left ErrNoTime
the_ops -> Right the_ops
processOpTime
:: BLMProblem
-> Int -> [Operation
] -> Operation
-> [Operation
] processOpTime blm_problem st_time acc op =
let (IntVal op_time) = time op
in if op_time <= st_time
then op:acc
else acc
currentStationTime
:: Solution1
-> Int currentStationTime sol =
case sol of
[] -> 0
(st:_) ->
case st of
[] -> 0
sch_ops ->
let end
_times
= map (\
(_,_, IntVal end
_time
) -> end
_time
) sch
_ops
canBeDoneRels
:: BLMProblem
-> Solution1
-> Either ErrOpsTodo
[Operation
] canBeDoneRels blm_problem solution =
let ops
_todo1
= filter (notScheduled solution
) blm
_problem
ops
_todo2
= filter (opCanBeDoneRel blm
_problem solution
) ops
_todo1
in case ops_todo2 of
[] -> Left ErrNoOps
_ -> Right ops_todo2
-- lista operacji BEZ tych, które już są przypisane,
-- lista operacji, które mogą zostać wykonane (rel)
notScheduled
:: Solution1
-> Operation
-> Bool notScheduled sol op
= not $ opDone sol
$ oid op
opCanBeDoneRel
:: BLMProblem
-> Solution1
-> Operation
-> Bool opCanBeDoneRel blm_problem solution op =
-- find the (immediate) predecessors of operation
let op_preds = immediatePreds blm_problem op
in case op_preds of
[] -> True -- no predecessors = operation can be done
_ -> -- if all of them are done, operation can be done
let preds
_not
_done
= filter (not . opDone solution
) op
_preds
in case preds_not_done of
[] -> True
_ -> False
opDone
:: Solution1
-> IntVal OpId
-> Bool opDone sol op_id =
--let all_scheduled = concat sol
-- ops_scheduled = map (\(x, _, _) -> x) all_scheduled
-- the_op = filter ((==) op_id) ops_scheduled
in case the_op of
[_] -> True
_ -> False
minCycle
:: BLMProblem
-> Int -> IntminCycle ops num_stations =
let op
_times
= map time ops
times
_int
= map (\
(IntVal t
) -> t
) op
_times
showSolution
:: Solution1
-> StringshowSolution sol
= DL
.intercalate
"\n" $ map (\
(s
, n
) -> DL
.intercalate
" " ["ST" ++ (show n
), s
]) $ zip lst
_stations
[1..] where
showStation
:: Station
-> String showStation st
= DL
.intercalate
" " $ reverse $ map showOp st
showOp
:: OpScheduled
-> String showOp (IntVal op_id, IntVal time_start, IntVal time_end) =
DL
.intercalate
" " ["O" ++ (show op
_id
), "(" ++ (show time
_start
) ++ "-" ++ (show time
_end
++ ")")]
---------------------------------------------------------------------
-- Stats
-- Assumptions:
-- - in the solution1, the stations order os always from the last one, to the first one
-- - the order of operations is also reversed, for each station
stTime ops
_scheduled
= maximum $ map (\
(_, _, IntVal t
) -> t
) ops
_scheduled
timeLine
:: Int -> Solution1
-> Int
timeLineWG
:: Int -> Solution1
-> Int let last
_station
= head sol
in cycle * (len
- 1) + stTime last
_station
lineEfficiency
:: Int -> Solution1
-> FloatlineEfficiency
cycle sol
= let st
_times
= map stTime sol
in num * 100 / den
smoothnessIndex
:: Solution1
-> FloatsmoothnessIndex sol =
let st
_times
= map stTime sol
diffs
= map (\st
_time
-> mx
- st
_time
) st
_times
squares
= map (\x
-> x
* x
) diffs
stats sol = DL.intercalate "\n" [
DL
.intercalate
"" ["T = ", show $ timeLine cycle
_time sol
], DL
.intercalate
"" ["(T = ", show $ timeLineWG cycle
_time sol
, ")"], DL
.intercalate
"" ["LE = ", show $ lineEfficiency cycle
_time sol
], DL
.intercalate
"" ["SI = ", show $ smoothnessIndex sol
] ]
---------------------------------------------------------------------
-- The Gantt Chart (wip)
axis
= DL
.intercalate
" " $ concat [["---"], numList
(cycle
_time
+ 1)]
numList mx
= map showPlus
$ take mx
[0..]
showPlus n =
if n == 0
0 ->
showTaskNum n =
if n < 10
else [Char.chr
$ 64 + n
- 9]
task (IntVal num, IntVal start, IntVal end) =
let time = (end - start - 1) * 2
before
_num
= time `
div`
2 after
_num
= (time `
div`
2) + (time `
mod`
2) before
= concat $ replicate before
_num
"-" after
= concat $ replicate after
_num
"-" in DL.intercalate "" [before, showTaskNum num, after]
ganttStation
(n
, ops
_scheduled
) = DL
.concat ["ST", show n
, " |", (DL
.intercalate
"|" $ map task
$ reverse ops
_scheduled
), "|"]
gantt sol
= DL
.intercalate
"\n" $ map ganttStation
$ zip [1..] $ reverse sol
minCycleInfo num_stations =
DL.intercalate " " [
"For", show num
_stations
, "stations the minimal cycle time is", show $ minCycle tmp
_problem num
_stations
]
tmp_problem = blmProblem problem
sol_str = showSolution sol
putStrLn "(Experimental stuff follows...)"