private Match GetAnonymizedMatch(Match match, IAnonymizer anonymizer)
        {
            if (anonymizer == null)
            {
                return(match);
            }

            return(new Match
            {
                MatchTestAdminDisplayName = anonymizer.GetAnonymizedName(match.MatchTestAdminDisplayName),
                MatchTestDisplayName = anonymizer.GetAnonymizedName(match.MatchTestDisplayName),
                TestGuid = anonymizer.GetAnonymizedGuid(match.TestGuid),
                SharedCentimorgans = match.SharedCentimorgans,
                SharedSegments = match.SharedSegments,
                LongestBlock = match.LongestBlock,
                TreeType = match.TreeType,
                TreeUrl = match.TreeUrl == null ? null : "https://invalid",
                TreeSize = match.TreeSize,
                HasCommonAncestors = match.HasCommonAncestors,
                CommonAncestors = match.CommonAncestors?.Select(commonAncestor => anonymizer.GetAnonymizedName(commonAncestor)).ToList(),
                Starred = match.Starred,
                HasHint = match.HasHint,
                Note = null,
                TagIds = match.TagIds,
                IsFather = match.IsFather,
                IsMother = match.IsMother,
            });
        }
        public async Task <(string, List <IClusterableMatch>, List <Tag>)> LoadClusterableMatchesAsync(string savedData, double minCentimorgansToCluster, double minCentimorgansInSharedMatches, IAnonymizer anonymizer, ProgressData progressData)
        {
            progressData.Description = "Loading data...";

            var serializedMatchesReaders = _serializedMatchesReaders.Where(reader => reader.IsSupportedFileType(savedData)).ToList();

            if (serializedMatchesReaders.Count == 0)
            {
                MessageBox.Show("Unsupported file type.");
                return(null, null, null);
            }

            Serialized input        = null;
            string     errorMessage = null;

            foreach (var serializedMatchesReader in serializedMatchesReaders)
            {
                string thisErrorMessage;
                (input, thisErrorMessage) = await serializedMatchesReader.ReadFileAsync(savedData, progressData);

                if (input != null)
                {
                    break;
                }
                if (errorMessage == null)
                {
                    errorMessage = thisErrorMessage;
                }
            }

            if (input == null)
            {
                MessageBox.Show(errorMessage);
                return(null, null, null);
            }

            return(await Task.Run(() =>
            {
                var strongMatches = input.Matches.Where(match => match.SharedCentimorgans >= minCentimorgansToCluster).ToList();
                var maxMatchIndex = strongMatches.Count + 1;
                var maxIcwIndex = Math.Min(maxMatchIndex, input.Matches.Count(match => match.SharedCentimorgans >= minCentimorgansInSharedMatches) + 1);
                maxIcwIndex = Math.Min(maxIcwIndex, input.Matches.Count - 1);
                var strongMatchesGuids = new HashSet <string>(strongMatches.Select(match => match.TestGuid), StringComparer.OrdinalIgnoreCase);
                var icw = input.Icw
                          .Where(kvp => strongMatchesGuids.Contains(kvp.Key))
                          .OrderBy(kvp => input.MatchIndexes.TryGetValue(kvp.Key, out var index) ? index : input.MatchIndexes.Count)
                          .ToDictionary(
                    kvp => kvp.Key,
                    kvp => kvp.Value.Where(index => index <= maxIcwIndex).ToList()
                    );
                var matchesDictionary = strongMatches.ToDictionary(match => match.TestGuid);
                var clusterableMatches = icw
                                         .AsParallel().AsOrdered()
                                         .Select((kvp, index) =>
                {
                    var match = matchesDictionary[kvp.Key];
                    match = GetAnonymizedMatch(match, anonymizer);
                    return (IClusterableMatch) new ClusterableMatch(index, match, kvp.Value);
                }
                                                 )
                                         .ToList();

                clusterableMatches = MaybeFilterMassivelySharedMatches(clusterableMatches);

                var testTakerTestId = anonymizer?.GetAnonymizedGuid(input.TestTakerTestId) ?? input.TestTakerTestId;
                var tags = anonymizer == null ? input.Tags : input.Tags?.Select((tag, index) => new Tag {
                    TagId = tag.TagId, Color = tag.Color, Label = $"Group{index}"
                }).ToList();
                return (testTakerTestId, clusterableMatches, tags);
            }));
        }