-
Notifications
You must be signed in to change notification settings - Fork 0
/
bkacw.hs
130 lines (110 loc) · 4.6 KB
/
bkacw.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
import qualified Data.Set as Set
import qualified Data.List as List
import qualified Data.Ord as Ord
import System.Environment
import System.IO
import System.Random
import Data.CSV
import Text.ParserCombinators.Parsec
main = do
args <- getArgs
-- Read rawData in
oldFile <- parseFromFile csvFile $ head args
-- Create random number generator
g <- newStdGen
let ranndomGenerator = (randoms g :: [Double])
-- Easy way to output strings instead of object:
--Left err -> show err
newFile = case oldFile of
Left err -> return [show err]
Right rawData ->
let people = getPeople rawData
randoms = take (length people) ranndomGenerator
validPairs = getValidPairs rawData people
buckets = buildBuckets validPairs people
matches = pickMatches buckets randoms
partners = findPartners matches people
listAdder (l, partner) = l ++ [partner]
paddedData = padData rawData
in alphabetize $ map listAdder $ zip paddedData partners
writeFile (args !! 1) $ genCsvFile newFile
-- How to output string:
--putStrLn newFile
alphabetize :: [[String]] -> [[String]]
alphabetize input =
let compareNames left right =
(head left) `compare` (head right)
in List.sortBy compareNames input
padData :: [[String]] -> [[String]]
padData rawData =
let lengths = map length rawData
maxLength = List.maximum lengths
addBlanks row =
let deficiency = maxLength - (length row)
in row ++ (take deficiency $ repeat "")
in map addBlanks rawData
getPeople :: [[String]] -> [String]
getPeople rawData =
[person | person:partners <- rawData]
getPossiblePairs :: [[String]] -> [String] -> Set.Set (Set.Set String)
getPossiblePairs rawData people =
let cartesianProduct = [[x, y] | x <- people, y <- people, x /= y]
in Set.fromList $ map Set.fromList cartesianProduct
getPastPairs :: [String] -> Set.Set (Set.Set String)
getPastPairs (person:partners) =
Set.fromList [Set.fromList [person, partner] | partner <- partners]
getAllPastPairs :: [[String]] -> Set.Set (Set.Set String)
getAllPastPairs rawData =
let pastPairsList = map getPastPairs rawData
in foldl Set.union Set.empty pastPairsList
getValidPairs :: [[String]] -> [String] -> Set.Set (Set.Set String)
getValidPairs rawData people =
let possiblePairs = getPossiblePairs rawData people
pastPairs = getAllPastPairs rawData
in Set.difference possiblePairs pastPairs
-------------------------------------------------------------------------
-------------------------------------------------------------------------
-- Algorithm for picking new pairs
-- -- Needs to remove pairs as they are picked
-- -- Needs to garauntee that no one will ever run out of pairs
--
-- Build list of buckets, one per person
-- Sort by size
-- while there are buckets
-- -- Pick a pair
-- -- Remove bucket for each person
-- -- Remove every pair either person is in
-- IDK why this typedef breaks things but it does so I leave it out.
--compareSets :: Set.Set -> Set.Set -> Ordering
compareSets a b = (Set.size a) `compare` (Set.size b)
buildBuckets :: Set.Set (Set.Set String) -> [String] -> [Set.Set (Set.Set String)]
buildBuckets validPairs people =
let buildBucket person = Set.filter (Set.member person) validPairs
buckets = map buildBucket people
in List.sortBy compareSets buckets
pruneBuckets :: Set.Set String -> [Set.Set (Set.Set String)] -> [Set.Set (Set.Set String)]
pruneBuckets match buckets =
let doesntMatch pair = 1 > (Set.size $ Set.intersection match pair)
prunedBuckets = map (Set.filter doesntMatch) buckets
unsortedBuckets = filter (\x -> Set.size x > 0) prunedBuckets
in List.sortBy compareSets unsortedBuckets
pickMatches :: [Set.Set (Set.Set String)] -> [Double] -> [Set.Set String]
pickMatches buckets randoms =
if [] == filter (\x -> Set.size x > 0) buckets
then []
else
let headList = Set.toList $ head buckets
listLength = fromIntegral $ length headList
matchIndex = truncate $ listLength * (head randoms)
match = headList !! matchIndex
in match:(pickMatches (pruneBuckets match buckets) $ tail randoms)
findPartners :: [Set.Set String] -> [String] -> [String]
findPartners matches people =
map findPartner people
where findPartner person =
let filteredMatches = filter (Set.member person) matches
pair =
if [] == filteredMatches
then Set.fromList [person, ""]
else head filteredMatches
in head $ Set.toList $ Set.difference pair $ Set.singleton person