-
Notifications
You must be signed in to change notification settings - Fork 3
/
TSPLib.hs
305 lines (243 loc) · 8.48 KB
/
TSPLib.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE FlexibleInstances #-}
module TSPLib (
Node,
Edge,
Path,
TSPAlgorithm,
distance,
xRange,
yRange,
pathLength,
edgeLength,
pathToEdges,
tracePath,
tracePath',
tracePath1,
tracePath1',
replace,
compEdgeDist,
nearestEdgeTo,
furthestEdgeTo,
findEnds,
wrapEnds,
wrapPathEnds,
tupleFromIntegral,
convexHull,
insertBetween,
combinations,
trigCartProd,
PathRing,
pathToRing,
ringToPath,
ringInsertBefore,
getRingNeighbors,
getNeighbors,
mergeRings,
mergePaths,
rotateRingTo,
reverseRing,
roundPath,
stripPath,
tupleToList,
deleteEdge,
traceEdges,
parseString,
parseStdin,
parseFile,
outputForGraph
) where
import Data.Functor
import Data.Function
import Data.List
import Control.Arrow ((>>>), (&&&), (***), second)
import Data.Tuple
import Debug.Trace
-- node
type Node = (Int, Int)
type Edge = (Node, Node)
type Path = [Node]
distance :: Node -> Node -> Double
distance (x1,y1) (x2,y2) = sqrt (fromIntegral $ (x2-x1)*(x2-x1) + (y2-y1)*(y2-y1))
xRange :: [Node] -> (Int, Int)
xRange = (foldl1' min &&& foldl1' max) . map fst
yRange :: [Node] -> (Int, Int)
yRange = (foldl1' min &&& foldl1' max) . map snd
type TSPAlgorithm = [Node] -> [Edge]
pathLength :: Path -> Double
pathLength xs = foldl1' (+) $ zipWith distance (init xs) (tail xs)
edgeLength :: Edge -> Double
edgeLength = uncurry distance
pathToEdges :: Path -> [Edge]
pathToEdges [] = []
pathToEdges xs = zip (init xs) (tail xs)
tracePath :: [Edge] -> Node -> Path
tracePath [] n = [n]
tracePath es n = case length edges of
0 -> [n]
1 -> let followingE = head edges
nextN = snd followingE
restEdges = es \\ [followingE, swap followingE]
in n : tracePath restEdges nextN
_ -> error "more than one choice"
where edges = filter ((==n) . fst) (es ++ map swap es)
tracePath1 :: [Edge] -> Path
tracePath1 es = tracePath es (fst $ head es)
-- this version of trace path follow
tracePath' :: [Edge] -> Node -> Path
tracePath' [] n = [n]
tracePath' es n = case length edges of
0 -> [n]
_ -> let followingE = head edges
nextN = snd followingE
restEdges = es \\ [followingE, swap followingE]
in n : tracePath' restEdges nextN
where edges = filter ((==n) . fst) (es ++ map swap es)
tracePath1' :: [Edge] -> Path
tracePath1' es = tracePath' es (fst $ head es)
replace :: (Eq a) => a -> [a] -> [a] -> [a]
replace x sub xs = newXs
where index = elemIndex x xs
makeHole i = second tail . splitAt i
connect (a,b) = a ++ sub ++ b
newXs = maybe xs (connect . flip makeHole xs) index
compEdgeDist :: Edge -> Edge -> Ordering
compEdgeDist = compare `on` edgeLength
nearestEdgeTo :: Node -> [Node] -> Edge
nearestEdgeTo n ms = (n, minimumBy (compare `on` distance n) ms)
furthestEdgeTo :: Node -> [Node] -> Edge
furthestEdgeTo n ms = (n, maximumBy (compare `on` distance n) ms)
findEnds :: [Edge] -> (Node, Node)
findEnds es = (head &&& last) $ map head $ filter (odd . length) ns'
where ns = map fst es ++ map snd es
ns' = group $ sort ns
wrapEnds :: [Edge] -> [Edge]
wrapEnds [] = []
wrapEnds es = findEnds es : es
wrapPathEnds :: Path -> Path
wrapPathEnds [] = []
wrapPathEnds (x:xs) = (x:xs) ++ [x]
tupleFromIntegral :: (Num c, Num d, Integral a, Integral b) =>
(a, b) -> (c, d)
tupleFromIntegral = fromIntegral *** fromIntegral
convexHull :: [Node] -> [Node]
convexHull [] = []
convexHull [x] = [x]
convexHull ns = lower ++ upper
where sorted = sort ns
lower = chain sorted
upper = chain (reverse sorted)
chain = go []
go (a:b:rs) (x:xs) = if ccw b a x
then go (b:rs) (x:xs)
else go (x:a:b:rs) xs
go rs (x:xs) = go (x:rs) xs
go rs [] = reverse $ tail rs
ccw (x1,y1) (x2,y2) (x3,y3) = (x2-x1)*(y3-y1) - (y2-y1)*(x3-x1) <= 0
insertBetween :: Path -> Node -> Node -> Node -> Path
insertBetween [] _ _ _ = []
insertBetween [a] _ _ _ = [a]
insertBetween (a:b:xs) p q n
| (a == p && b == q) ||
(a == q && b == p) = a : n : b : xs
| otherwise = a : insertBetween (b:xs) p q n
combinations :: Int -> [a] -> [[a]]
combinations 0 _ = [[]]
combinations n xs = [ y:ys | y:xs' <- tails xs
, ys <- combinations (n-1) xs']
trigCartProd :: [a] -> [(a,a)]
trigCartProd = map (head &&& last) . combinations 2
data PathRing = PathRing {
prLength :: Int,
prPath :: Path
}
pathToRing :: Path -> PathRing
pathToRing p = PathRing { prLength = length p, prPath = cycle p }
ringToPath :: PathRing -> Path
ringToPath PathRing {..} = take prLength prPath
getRingNeighbors :: PathRing -> Node -> (Node, Node)
getRingNeighbors PathRing {..} n = foo prPath
where foo (a:b:c:ns) = if n == b then (a,c) else foo (b:c:ns)
foo _ = error "invalid input"
getNeighbors :: Path -> Node -> (Node, Node)
getNeighbors p n = foo $ concat $ replicate 2 p
where foo (a:b:c:xs) = if b == n then (a,c) else foo (b:c:xs)
ringInsertBefore :: PathRing -> Node -> [Node] -> PathRing
ringInsertBefore PathRing {..} n ns = PathRing { prLength = newPrLength
, prPath = newPrPath
}
where newPrLength = prLength + length ns
newPrPath = foo prPath
foo (a:as) = if a == n
then cycle $ ns ++ [a] ++ take (prLength - 1) as
else foo as
foo _ = error "invalid input"
reverseRing :: PathRing -> PathRing
reverseRing = pathToRing . reverse . ringToPath
rotateRingTo :: PathRing -> Node -> PathRing
rotateRingTo PathRing {..} n = PathRing { prLength = prLength
, prPath = dropWhile (/= n) prPath
}
mergeRings :: PathRing -> PathRing -> (Node, Node) -> (Node, Node) -> PathRing
mergeRings pr qr pe qe = newPr
where newPr = ringInsertBefore pr (fst pe) (ringToPath rotatedQr)
rotatedQr = reverseRing $ rotateRingTo qr (snd qe)
deleteEdge :: [Edge] -> Edge -> [Edge]
deleteEdge [] _ = []
deleteEdge ((a,b):xs) (c,d)
| a == c && b == d = xs
| a == d && b == c = xs
| otherwise = (a,b) : deleteEdge xs (c,d)
stripPath :: Path -> Path
stripPath [] = []
stripPath (x:xs) = if last xs == x then xs else x:xs
mergePaths :: Path -> Path -> Edge -> Edge -> Path
mergePaths p1 p2 e1 e2 = rsltP
where p1' = deleteEdge (pathToEdges p1) e1
p2' = deleteEdge (pathToEdges p2) e2
newEs = [(fst e1, fst e2), (snd e1, snd e2)]
rslt = newEs ++ p1' ++ p2'
rsltP = stripPath $ tracePath' rslt (fst e1)
roundPath :: Path -> Path
roundPath [] = error "wrong path input"
roundPath (x:xs) = (x:xs) ++ [x]
tupleToList :: (a,a) -> [a]
tupleToList (a,b) = [a,b]
dumpEdges :: [Edge] -> String
dumpEdges es = intercalate "\n" (map showEdge es) ++ "\n"
where showEdge ((a,b), (c,d)) = show a ++ " " ++ show b ++ " " ++
show c ++ " " ++ show d
dumpNodes :: [Node] -> String
dumpNodes ns = intercalate "\n" (map showNode ns) ++ "\n"
where showNode (a,b) = show a ++ " " ++ show b
extractNodes :: [Edge] -> [Node]
extractNodes = concatMap tupleToList
traceEdges :: [Edge] -> [Edge]
traceEdges es = trace content es
where prefix = show (length nodes) ++ " " ++ show (length es) ++ "\n"
suffix = "\n"
content = prefix ++ dumpNodes nodes ++ dumpEdges es ++ suffix
nodes = nub $ extractNodes es
parseString :: String -> [Node]
parseString =
lines >>>
filter (not . ("#" `isPrefixOf`)) >>>
filter (not . null) >>>
map words >>>
map (map read) >>>
filter (\x -> length x == 2) >>>
map (\[a,b] -> (a,b))
parseStdin :: IO [Node]
parseStdin = parseString <$> getContents
parseFile :: FilePath -> IO [Node]
parseFile path = parseString <$> readFile path
outputForGraph :: [Node] -> [Edge] -> IO ()
outputForGraph ns es = do
putStrLn $ show (length ns) ++ " " ++ show (length es)
mapM_ (putStrLn . showNode) ns
mapM_ (putStrLn . showEdge) es
where showNode (a,b) = show a ++ " " ++ show b
showEdge ((a,b),(c,d)) = show a ++ " " ++ show b ++ " " ++
show c ++ " " ++ show d
main :: IO ()
main = parseStdin >>= flip outputForGraph []