-- Converts a list of key/values into a map, aggregating values 
-- e.g. [(1, "one"), (2, "two"), (1, "uno")] -> Map {1: ["one", "uno"], 2: ["two"]}  
keyValuesToMap 
::  ( Eq  k
,  Ord  k
,  Ord  v
)  =>  [ ( k
,  v
) ]  ->  M
. Map k 
[ v
] keyValuesToMap =  M. fromList .  groupValues
    where 
        groupValues 
::  ( Eq  k
,  Ord  k
,  Ord  v
)  =>  [ ( k
,  v
) ]  ->  [ ( k
,  [ v
] ) ]         groupValues 
=  map  foldValues 
.  groupBy 
( \
( x
,  _ )  ( y
,  _ )  ->  x 
==  y
)  .  sort 
 
        -- Combines values of the same key into a list 
        -- e.g. [(1, 2), (1, 4), (1, 5)] -> (1, [2, 4, 5])  
        -- Precondition: the key must be the same for all values in the input list 
        foldValues 
::  ( Eq  k
,  Ord  k
,  Ord  v
)  =>  [ ( k
,  v
) ]  ->  ( k
,  [ v
] )         foldValues xs 
=  foldr  accumulateSameKeyValues 
( key
,  [ ] )  xs
            where 
 
                accumulateSameKeyValues 
::  ( Eq  k
,  Ord  k
,  Ord  v
)  =>  ( k
,  v
)  ->  ( k
,  [ v
] )  ->  ( k
,  [ v
] )                 accumulateSameKeyValues ( key1,  value)  ( key2,  values) 
                    |  key1 ==  key2 =  ( key1,  value:values) 
LS0gQ29udmVydHMgYSBsaXN0IG9mIGtleS92YWx1ZXMgaW50byBhIG1hcCwgYWdncmVnYXRpbmcgdmFsdWVzCi0tIGUuZy4gWygxLCAib25lIiksICgyLCAidHdvIiksICgxLCAidW5vIildIC0+IE1hcCB7MTogWyJvbmUiLCAidW5vIl0sIDI6IFsidHdvIl19IAprZXlWYWx1ZXNUb01hcCA6OiAoRXEgaywgT3JkIGssIE9yZCB2KSA9PiBbKGssIHYpXSAtPiBNLk1hcCBrIFt2XQprZXlWYWx1ZXNUb01hcCA9IE0uZnJvbUxpc3QgLiBncm91cFZhbHVlcwogICAgd2hlcmUKICAgICAgICBncm91cFZhbHVlcyA6OiAoRXEgaywgT3JkIGssIE9yZCB2KSA9PiBbKGssIHYpXSAtPiBbKGssIFt2XSldCiAgICAgICAgZ3JvdXBWYWx1ZXMgPSBtYXAgZm9sZFZhbHVlcyAuIGdyb3VwQnkgKFwoeCwgXykgKHksIF8pIC0+IHggPT0geSkgLiBzb3J0IAoKICAgICAgICAtLSBDb21iaW5lcyB2YWx1ZXMgb2YgdGhlIHNhbWUga2V5IGludG8gYSBsaXN0CiAgICAgICAgLS0gZS5nLiBbKDEsIDIpLCAoMSwgNCksICgxLCA1KV0gLT4gKDEsIFsyLCA0LCA1XSkgCiAgICAgICAgLS0gUHJlY29uZGl0aW9uOiB0aGUga2V5IG11c3QgYmUgdGhlIHNhbWUgZm9yIGFsbCB2YWx1ZXMgaW4gdGhlIGlucHV0IGxpc3QKICAgICAgICBmb2xkVmFsdWVzIDo6IChFcSBrLCBPcmQgaywgT3JkIHYpID0+IFsoaywgdildIC0+IChrLCBbdl0pCiAgICAgICAgZm9sZFZhbHVlcyB4cyA9IGZvbGRyIGFjY3VtdWxhdGVTYW1lS2V5VmFsdWVzIChrZXksIFtdKSB4cwogICAgICAgICAgICB3aGVyZQogICAgICAgICAgICAgICAga2V5ID0gZnN0IC4gaGVhZCAkIHhzCgogICAgICAgICAgICAgICAgYWNjdW11bGF0ZVNhbWVLZXlWYWx1ZXMgOjogKEVxIGssIE9yZCBrLCBPcmQgdikgPT4gKGssIHYpIC0+IChrLCBbdl0pIC0+IChrLCBbdl0pCiAgICAgICAgICAgICAgICBhY2N1bXVsYXRlU2FtZUtleVZhbHVlcyAoa2V5MSwgdmFsdWUpIChrZXkyLCB2YWx1ZXMpCiAgICAgICAgICAgICAgICAgICAgfCBrZXkxID09IGtleTIgPSAoa2V5MSwgdmFsdWU6dmFsdWVzKQogICAgICAgICAgICAgICAgICAgIHwgb3RoZXJ3aXNlID0gZXJyb3IgIkNhbm5vdCBncm91cCB2YWx1ZXMgZm9yIGRpZmZlcmVudCBrZXlzLiI=