Beispiel #1
0
        public static ATOM_Record FindAtomInsideSingularInteractionsChain(ProteinChainListContainer singularAaToAaInteractions, int chainIndex, int residueSequenceIndex)
        {
            if (ParameterValidation.IsProteinChainListContainerNullOrEmpty(singularAaToAaInteractions))
            {
                return(null);
            }

            if (ParameterValidation.IsChainIndexInvalid(chainIndex))
            {
                throw new ArgumentOutOfRangeException(nameof(chainIndex));
            }

            if (ParameterValidation.IsResidueSequenceIndexInvalid(residueSequenceIndex, true))
            {
                throw new ArgumentOutOfRangeException(nameof(residueSequenceIndex));
            }

            // Loop through atoms in specified chain to find atom with given residue sequence index
            for (int atomIndex = 0; atomIndex < singularAaToAaInteractions.ChainList[chainIndex].AtomList.Count; atomIndex++)
            {
                ATOM_Record atom = singularAaToAaInteractions.ChainList[chainIndex].AtomList[atomIndex];

                if (ProteinDataBankFileOperations.NullableTryParseInt32(atom.resSeq.FieldValue) == residueSequenceIndex)
                {
                    return(atom);
                }
            }

            return(null);
        }
Beispiel #2
0
        public static ATOM_Record FindAtomInsidePdbFileChain(ProteinChainListContainer pdbFileChains, int chainIndex, int residueSequenceIndex)
        {
            if (ParameterValidation.IsProteinChainListContainerNullOrEmpty(pdbFileChains))
            {
                return(null);
            }

            if (ParameterValidation.IsChainIndexInvalid(chainIndex))
            {
                throw new ArgumentOutOfRangeException(nameof(chainIndex));
            }

            if (ParameterValidation.IsResidueSequenceIndexInvalid(residueSequenceIndex, true))
            {
                throw new ArgumentOutOfRangeException(nameof(residueSequenceIndex));
            }

            for (int memberIndex = 0; memberIndex < pdbFileChains.ChainList[chainIndex].AtomList.Count; memberIndex++)
            {
                ATOM_Record atom = pdbFileChains.ChainList[chainIndex].AtomList[memberIndex];

                if (ProteinDataBankFileOperations.NullableTryParseInt32(atom.resSeq.FieldValue) == residueSequenceIndex)
                {
                    return(atom);
                }
            }

            return(null);
        }
        /// <summary>
        ///     Get the lowest (minimum) and highest (maximum) residue sequence index (as found in the pdb file) in an interaction
        ///     proteinInterface.
        /// </summary>
        /// <param name="proteinInterface"></param>
        /// <param name="singularAaToAaInteractions"></param>
        /// <param name="chainIndex"></param>
        /// <returns></returns>
        public static MinMax MinMaxResidueSequenceIndex(ClusteringFullResultListContainer.Chain.Stage.Cluster proteinInterface, ProteinChainListContainer singularAaToAaInteractions, int chainIndex)
        {
            if (ParameterValidation.IsClusterNullOrEmpty(proteinInterface))
            {
                throw new ArgumentNullException(nameof(proteinInterface));
            }

            if (ParameterValidation.IsProteinChainListContainerNullOrEmpty(singularAaToAaInteractions))
            {
                throw new ArgumentNullException(nameof(singularAaToAaInteractions));
            }

            if (ParameterValidation.IsChainIndexInvalid(chainIndex))
            {
                throw new ArgumentOutOfRangeException(nameof(chainIndex));
            }

            int proteinInterfaceMin = 0;
            int proteinInterfaceMax = 0;

            for (int memberIndex = 0; memberIndex < proteinInterface.AtomIndexList.Count; memberIndex++)
            {
                int member = proteinInterface.AtomIndexList[memberIndex];

                ATOM_Record atom = singularAaToAaInteractions.ChainList[chainIndex].AtomList[member];

                var residueSequenceIndex = ProteinDataBankFileOperations.NullableTryParseInt32(atom.resSeq.FieldValue);

                if (residueSequenceIndex == null)
                {
                    continue;
                }

                if (memberIndex == 0 || residueSequenceIndex < proteinInterfaceMin)
                {
                    proteinInterfaceMin = residueSequenceIndex.Value;
                }

                if (memberIndex == 0 || residueSequenceIndex > proteinInterfaceMax)
                {
                    proteinInterfaceMax = residueSequenceIndex.Value;
                }
            }

            return(new MinMax(proteinInterfaceMin, proteinInterfaceMax));
        }
Beispiel #4
0
        /// <summary>
        ///     This method finds interactions between detected proteinInterfaces.  It is specific to dimers with exactly two chains.  [Chain A
        ///     ProteinInterface Index, Chain B ProteinInterface Index]
        /// </summary>
        /// <param name="cancellationToken"></param>
        /// <param name="pdbFilename"></param>
        /// <param name="pdbFileChains"></param>
        /// <param name="chainInteractingAtomLists"></param>
        /// <param name="fullClusteringResult"></param>
        /// <param name="proteinInterfacesClusteringResult"></param>
        /// <param name="detectedFinalStageIndexes"></param>
        /// <param name="pdbIdChainIdList"></param>
        /// <returns></returns>
        public static InteractionBetweenProteinInterfacesListContainer FindInteractionsBetweenAnyProteinInterfaces(
            CancellationToken cancellationToken,
            decimal maxAtomInterationDistance,
            string pdbFilename,
            Dictionary<string, List<string>> pdbIdChainIdList,
            ProteinChainListContainer pdbFileChains,
            ProteinChainListContainer chainInteractingAtomLists,
            ClusteringFullResultListContainer fullClusteringResult,
            ClusteringFullResultListContainer proteinInterfacesClusteringResult,
            int[] detectedFinalStageIndexes)
        {
            if (string.IsNullOrWhiteSpace(pdbFilename))
            {
                throw new ArgumentOutOfRangeException(nameof(pdbFilename));
            }

            if (!File.Exists(pdbFilename))
            {
                throw new FileNotFoundException("File not found", pdbFilename);
            }

            if (ParameterValidation.IsProteinChainListContainerNullOrEmpty(chainInteractingAtomLists))
            {
                throw new ArgumentOutOfRangeException(nameof(chainInteractingAtomLists));
            }

            if (ParameterValidation.IsClusteringFullResultListContainerNullOrEmpty(fullClusteringResult))
            {
                throw new ArgumentOutOfRangeException(nameof(fullClusteringResult));
            }

            if (ParameterValidation.IsClusteringFullResultListContainerNullOrEmpty(proteinInterfacesClusteringResult))
            {
                throw new ArgumentOutOfRangeException(nameof(proteinInterfacesClusteringResult));
            }

            if (ParameterValidation.IsIntArrayNullOrEmpty(detectedFinalStageIndexes))
            {
                throw new ArgumentOutOfRangeException(nameof(detectedFinalStageIndexes));
            }

            string proteinId = ProteinDataBankFileOperations.PdbIdFromPdbFilename(pdbFilename);

            var interactionBetweenProteinInterfacesListContainer = new InteractionBetweenProteinInterfacesListContainer();

            List<AtomPair> interactionList;

            if (pdbFileChains != null && pdbFileChains.ChainList != null && pdbFileChains.ChainList.Count > 0)
            {
                interactionList = SearchInteractions.FindInteractions(cancellationToken, maxAtomInterationDistance, proteinId, pdbIdChainIdList, pdbFileChains); //, false, -1, pdbFileChains);
            }
            else
            {
                interactionList = SearchInteractions.FindInteractions(cancellationToken, maxAtomInterationDistance, pdbFilename, pdbIdChainIdList);
            }

            var interactionInsideProteinInterfaceArray = new bool[interactionList.Count];

            ////////Console.WriteLine("");
            ////////Console.WriteLine("");
            ////////Console.WriteLine("------------------ START ------------------");
            //int c = 0;

            for (int chainIndexA = 0; chainIndexA < proteinInterfacesClusteringResult.ChainList.Count; chainIndexA++)
            {
                for (int chainIndexB = 0; chainIndexB < proteinInterfacesClusteringResult.ChainList.Count; chainIndexB++)
                {
                    if (chainIndexA == chainIndexB || chainIndexB < chainIndexA)
                    {
                        continue;
                    }

                    List<ClusteringFullResultListContainer.Chain.Stage.Cluster> proteinInterfaceListA = proteinInterfacesClusteringResult.ChainList[chainIndexA].StageList[detectedFinalStageIndexes[chainIndexA]].ClusterList;
                    List<ClusteringFullResultListContainer.Chain.Stage.Cluster> proteinInterfaceListB = proteinInterfacesClusteringResult.ChainList[chainIndexB].StageList[detectedFinalStageIndexes[chainIndexB]].ClusterList;

                    int realProteinInterfaceIndexA = -1;

                    for (int proteinInterfaceIndexA = 0; proteinInterfaceIndexA < proteinInterfaceListA.Count; proteinInterfaceIndexA++)
                    {
                        int realProteinInterfaceIndexB = -1;
                        List<int> proteinInterfaceMemberIndexListA = proteinInterfaceListA[proteinInterfaceIndexA].AtomIndexList;
                        List<ATOM_Record> proteinInterfaceAtomListA = proteinInterfaceMemberIndexListA.Select(proteinInterfaceMemberIndexA => chainInteractingAtomLists.ChainList[chainIndexA].AtomList[proteinInterfaceMemberIndexA]).ToList();
                        proteinInterfaceAtomListA = proteinInterfaceAtomListA.OrderBy(a => ProteinDataBankFileOperations.NullableTryParseInt32(a.resSeq.FieldValue)).ToList();
                        if (proteinInterfaceAtomListA.Count > 0)
                        {
                            realProteinInterfaceIndexA++;
                        }
                        else
                        {
                            continue;
                        }

                        for (int proteinInterfaceIndexB = 0; proteinInterfaceIndexB < proteinInterfaceListB.Count; proteinInterfaceIndexB++)
                        {
                            List<int> proteinInterfaceMemberIndexListB = proteinInterfaceListB[proteinInterfaceIndexB].AtomIndexList;
                            List<ATOM_Record> proteinInterfaceAtomListB = proteinInterfaceMemberIndexListB.Select(proteinInterfaceMemberIndexB => chainInteractingAtomLists.ChainList[chainIndexB].AtomList[proteinInterfaceMemberIndexB]).ToList();
                            proteinInterfaceAtomListB = proteinInterfaceAtomListB.OrderBy(b => ProteinDataBankFileOperations.NullableTryParseInt32(b.resSeq.FieldValue)).ToList();
                            if (proteinInterfaceAtomListB.Count > 0)
                            {
                                realProteinInterfaceIndexB++;
                            }
                            else
                            {
                                continue;
                            }

                            for (int proteinInterfaceAtomListIndexA = 0; proteinInterfaceAtomListIndexA < proteinInterfaceAtomListA.Count; proteinInterfaceAtomListIndexA++)
                            {
                                ATOM_Record atomA = proteinInterfaceAtomListA[proteinInterfaceAtomListIndexA];

                                for (int proteinInterfaceAtomListIndexB = 0; proteinInterfaceAtomListIndexB < proteinInterfaceAtomListB.Count; proteinInterfaceAtomListIndexB++)
                                {
                                    ATOM_Record atomB = proteinInterfaceAtomListB[proteinInterfaceAtomListIndexB];

                                    //c++;
                                    ////////Console.WriteLine(c.ToString().PadLeft(5) +
                                    //                  " Chain " + chainIndexA + " (" + proteinInterfaceListA.Count(a => a.AtomIndexList.Count > 0) + " proteinInterfaces) ProteinInterface " + realProteinInterfaceIndexA + " (" + proteinInterfaceAtomListA.Count + " atoms) <--->" +
                                    //                  " Chain " + chainIndexB + " (" + proteinInterfaceListB.Count(a => a.AtomIndexList.Count > 0) + " proteinInterfaces) ProteinInterface " + realProteinInterfaceIndexB + " (" + proteinInterfaceAtomListB.Count + " atoms)  --->" +
                                    //                  " chainID " + atomA.chainID.FieldValue + " resName " + atomA.resName.FieldValue + " resSeq " + atomA.resSeq.FieldValue + " <--->" +
                                    //                  " chainID " + atomB.chainID.FieldValue + " resName " + atomB.resName.FieldValue + " resSeq " + atomB.resSeq.FieldValue);

                                    for (int interactionIndex = 0; interactionIndex < interactionList.Count; interactionIndex++)
                                    {
                                        AtomPair interaction = interactionList[interactionIndex];

                                        if ((interaction.Atom1 == atomA && interaction.Atom2 == atomB) || (interaction.Atom1 == atomB && interaction.Atom2 == atomA))
                                        {
                                            interactionInsideProteinInterfaceArray[interactionIndex] = true;

                                            var interactionBetweenProteinInterfaces = new InteractionBetweenProteinInterfaces();
                                            interactionBetweenProteinInterfacesListContainer.InteractionBetweenProteinInterfacesList.Add(interactionBetweenProteinInterfaces);

                                            interactionBetweenProteinInterfaces.Atom1.Atom = atomA;
                                            interactionBetweenProteinInterfaces.Atom1.FullProteinInterfaceId.ProteinId = proteinId;
                                            interactionBetweenProteinInterfaces.Atom1.FullProteinInterfaceId.ChainId = chainIndexA;
                                            interactionBetweenProteinInterfaces.Atom1.FullProteinInterfaceId.ProteinInterfaceId = realProteinInterfaceIndexA;

                                            interactionBetweenProteinInterfaces.Atom2.Atom = atomB;
                                            interactionBetweenProteinInterfaces.Atom2.FullProteinInterfaceId.ProteinId = proteinId;
                                            interactionBetweenProteinInterfaces.Atom2.FullProteinInterfaceId.ChainId = chainIndexB;
                                            interactionBetweenProteinInterfaces.Atom2.FullProteinInterfaceId.ProteinInterfaceId = realProteinInterfaceIndexB;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }

            for (int interactionIndex = 0; interactionIndex < interactionInsideProteinInterfaceArray.Length; interactionIndex++)
            {
                bool interactionInsideProteinInterface = interactionInsideProteinInterfaceArray[interactionIndex];

                if (!interactionInsideProteinInterface)
                {
                    var interactionBetweenNonProteinInterfaces = new InteractionBetweenProteinInterfaces();
                    interactionBetweenProteinInterfacesListContainer.InteractionBetweenNonProteinInterfacesList.Add(interactionBetweenNonProteinInterfaces);

                    interactionBetweenNonProteinInterfaces.Atom1.Atom = interactionList[interactionIndex].Atom1;
                    interactionBetweenNonProteinInterfaces.Atom1.FullProteinInterfaceId.ProteinId = proteinId;
                    interactionBetweenNonProteinInterfaces.Atom1.FullProteinInterfaceId.ChainId = interactionList[interactionIndex].Atom1FullProteinInterfaceId.ChainId;
                    interactionBetweenNonProteinInterfaces.Atom1.FullProteinInterfaceId.ProteinInterfaceId = -1;

                    interactionBetweenNonProteinInterfaces.Atom2.Atom = interactionList[interactionIndex].Atom2;
                    interactionBetweenNonProteinInterfaces.Atom2.FullProteinInterfaceId.ProteinId = proteinId;
                    interactionBetweenNonProteinInterfaces.Atom2.FullProteinInterfaceId.ChainId = interactionList[interactionIndex].Atom2FullProteinInterfaceId.ChainId;
                    interactionBetweenNonProteinInterfaces.Atom2.FullProteinInterfaceId.ProteinInterfaceId = -1;
                }
            }

            ////////Console.WriteLine("------------------ END ------------------");

            // ensure sorted order
            interactionBetweenProteinInterfacesListContainer.InteractionBetweenProteinInterfacesList = interactionBetweenProteinInterfacesListContainer.InteractionBetweenProteinInterfacesList
                .OrderBy(a => a.Atom1.FullProteinInterfaceId.ChainId)
                .ThenBy(a => a.Atom1.FullProteinInterfaceId.ProteinInterfaceId)
                .ThenBy(a => ProteinDataBankFileOperations.NullableTryParseInt32(a.Atom1.Atom.resSeq.FieldValue))
                .ThenBy(a => a.Atom2.FullProteinInterfaceId.ChainId)
                .ThenBy(a => a.Atom2.FullProteinInterfaceId.ProteinInterfaceId)
                .ThenBy(a => ProteinDataBankFileOperations.NullableTryParseInt32(a.Atom2.Atom.resSeq.FieldValue))
                .ToList();

            interactionBetweenProteinInterfacesListContainer.InteractionBetweenNonProteinInterfacesList = interactionBetweenProteinInterfacesListContainer.InteractionBetweenNonProteinInterfacesList
                .OrderBy(a => a.Atom1.FullProteinInterfaceId.ChainId)
                .ThenBy(a => a.Atom1.FullProteinInterfaceId.ProteinInterfaceId)
                .ThenBy(a => ProteinDataBankFileOperations.NullableTryParseInt32(a.Atom1.Atom.resSeq.FieldValue))
                .ThenBy(a => a.Atom2.FullProteinInterfaceId.ChainId)
                .ThenBy(a => a.Atom2.FullProteinInterfaceId.ProteinInterfaceId)
                .ThenBy(a => ProteinDataBankFileOperations.NullableTryParseInt32(a.Atom2.Atom.resSeq.FieldValue))
                .ToList();

            // remove duplicates (as the list is sorted, duplicates will always be together in the list)
            for (int index = interactionBetweenProteinInterfacesListContainer.InteractionBetweenProteinInterfacesList.Count - 1; index > 0; index--)
            {
                InteractionBetweenProteinInterfaces lastInteractionBetweenProteinInterfaces = interactionBetweenProteinInterfacesListContainer.InteractionBetweenProteinInterfacesList[index - 1];
                InteractionBetweenProteinInterfaces thisInteractionBetweenProteinInterfaces = interactionBetweenProteinInterfacesListContainer.InteractionBetweenProteinInterfacesList[index];

                if (lastInteractionBetweenProteinInterfaces == null || thisInteractionBetweenProteinInterfaces == null)
                {
                    continue;
                }

                if (thisInteractionBetweenProteinInterfaces.Atom1.FullProteinInterfaceId.ProteinId == lastInteractionBetweenProteinInterfaces.Atom1.FullProteinInterfaceId.ProteinId &&
                    thisInteractionBetweenProteinInterfaces.Atom1.FullProteinInterfaceId.ChainId == lastInteractionBetweenProteinInterfaces.Atom1.FullProteinInterfaceId.ChainId &&
                    thisInteractionBetweenProteinInterfaces.Atom1.FullProteinInterfaceId.ProteinInterfaceId == lastInteractionBetweenProteinInterfaces.Atom1.FullProteinInterfaceId.ProteinInterfaceId &&
                    thisInteractionBetweenProteinInterfaces.Atom1.Atom == lastInteractionBetweenProteinInterfaces.Atom1.Atom &&
                    thisInteractionBetweenProteinInterfaces.Atom2.FullProteinInterfaceId.ProteinId == lastInteractionBetweenProteinInterfaces.Atom2.FullProteinInterfaceId.ProteinId &&
                    thisInteractionBetweenProteinInterfaces.Atom2.FullProteinInterfaceId.ChainId == lastInteractionBetweenProteinInterfaces.Atom2.FullProteinInterfaceId.ChainId &&
                    thisInteractionBetweenProteinInterfaces.Atom2.FullProteinInterfaceId.ProteinInterfaceId == lastInteractionBetweenProteinInterfaces.Atom2.FullProteinInterfaceId.ProteinInterfaceId &&
                    thisInteractionBetweenProteinInterfaces.Atom2.Atom == lastInteractionBetweenProteinInterfaces.Atom2.Atom)
                {
                    interactionBetweenProteinInterfacesListContainer.InteractionBetweenProteinInterfacesList.RemoveAt(index - 1);
                    //////Console.WriteLine("removed duplicate");
                }
            }

            for (int index = interactionBetweenProteinInterfacesListContainer.InteractionBetweenNonProteinInterfacesList.Count - 1; index > 0; index--)
            {
                InteractionBetweenProteinInterfaces lastInteractionBetweenProteinInterfaces = interactionBetweenProteinInterfacesListContainer.InteractionBetweenNonProteinInterfacesList[index - 1];
                InteractionBetweenProteinInterfaces thisInteractionBetweenProteinInterfaces = interactionBetweenProteinInterfacesListContainer.InteractionBetweenNonProteinInterfacesList[index];

                if (lastInteractionBetweenProteinInterfaces == null || thisInteractionBetweenProteinInterfaces == null)
                {
                    continue;
                }

                if (thisInteractionBetweenProteinInterfaces.Atom1.FullProteinInterfaceId.ProteinId == lastInteractionBetweenProteinInterfaces.Atom1.FullProteinInterfaceId.ProteinId &&
                    thisInteractionBetweenProteinInterfaces.Atom1.FullProteinInterfaceId.ChainId == lastInteractionBetweenProteinInterfaces.Atom1.FullProteinInterfaceId.ChainId &&
                    thisInteractionBetweenProteinInterfaces.Atom1.FullProteinInterfaceId.ProteinInterfaceId == lastInteractionBetweenProteinInterfaces.Atom1.FullProteinInterfaceId.ProteinInterfaceId &&
                    thisInteractionBetweenProteinInterfaces.Atom1.Atom == lastInteractionBetweenProteinInterfaces.Atom1.Atom &&
                    thisInteractionBetweenProteinInterfaces.Atom2.FullProteinInterfaceId.ProteinId == lastInteractionBetweenProteinInterfaces.Atom2.FullProteinInterfaceId.ProteinId &&
                    thisInteractionBetweenProteinInterfaces.Atom2.FullProteinInterfaceId.ChainId == lastInteractionBetweenProteinInterfaces.Atom2.FullProteinInterfaceId.ChainId &&
                    thisInteractionBetweenProteinInterfaces.Atom2.FullProteinInterfaceId.ProteinInterfaceId == lastInteractionBetweenProteinInterfaces.Atom2.FullProteinInterfaceId.ProteinInterfaceId &&
                    thisInteractionBetweenProteinInterfaces.Atom2.Atom == lastInteractionBetweenProteinInterfaces.Atom2.Atom)
                {
                    interactionBetweenProteinInterfacesListContainer.InteractionBetweenNonProteinInterfacesList.RemoveAt(index - 1);
                    //////Console.WriteLine("removed duplicate");
                }
            }

            return interactionBetweenProteinInterfacesListContainer;
        }
        public static int[] FindFinalProteinInterfaceStageIndexes(
            ProteinChainListContainer chainInteractingAtomLists,
            ClusteringFullResultListContainer clusters,
            ClusteringFullResultListContainer proteinInterfaces,
            List <List <int> > chainStageProteinInterfaceCount)
        {
            if (ParameterValidation.IsProteinChainListContainerNullOrEmpty(chainInteractingAtomLists))
            {
                throw new ArgumentOutOfRangeException(nameof(chainInteractingAtomLists));
            }

            if (ParameterValidation.IsClusteringFullResultListContainerNullOrEmpty(clusters))
            {
                throw new ArgumentOutOfRangeException(nameof(clusters));
            }

            if (ParameterValidation.IsClusteringFullResultListContainerNullOrEmpty(proteinInterfaces))
            {
                throw new ArgumentOutOfRangeException(nameof(proteinInterfaces));
            }

            if (ParameterValidation.IsListNullOrEmpty(chainStageProteinInterfaceCount))
            {
                throw new ArgumentOutOfRangeException(nameof(chainInteractingAtomLists));
            }

            int totalChains = chainInteractingAtomLists.ChainList.Count;
            var result      = new int[totalChains];

            for (int chainIndex = 0; chainIndex < totalChains; chainIndex++)
            {
                result[chainIndex] = -1;

                // Find the Max proteinInterfaces in proteinInterfaces list
                int chainMaxProteinInterfaceCount          = chainStageProteinInterfaceCount[chainIndex].Max();
                int chainMaxProteinInterfaceCountLastIndex = chainStageProteinInterfaceCount[chainIndex].LastIndexOf(chainMaxProteinInterfaceCount);

                for (int stageIndex = chainMaxProteinInterfaceCountLastIndex + 1; stageIndex < proteinInterfaces.ChainList[chainIndex].StageList.Count; stageIndex++)
                {
                    ClusteringFullResultListContainer.Chain.Stage proteinInterfacesLastStage = proteinInterfaces.ChainList[chainIndex].StageList[stageIndex - 1];
                    ClusteringFullResultListContainer.Chain.Stage proteinInterfacesThisStage = proteinInterfaces.ChainList[chainIndex].StageList[stageIndex];

                    // Check proteinInterfaces in proteinInterfacesThisStage contains the member of proteinInterfaces in proteinInterfacesLastStage
                    foreach (ClusteringFullResultListContainer.Chain.Stage.Cluster proteinInterfaceLastStage in proteinInterfacesLastStage.ClusterList)
                    {
                        foreach (int lastProteinInterfaceMember in proteinInterfaceLastStage.AtomIndexList)
                        {
                            bool lastProteinInterfaceMemberFound = false;

                            foreach (ClusteringFullResultListContainer.Chain.Stage.Cluster proteinInterfaceThisStage in proteinInterfacesThisStage.ClusterList)
                            {
                                if (proteinInterfaceThisStage.AtomIndexList.Contains(lastProteinInterfaceMember))
                                {
                                    lastProteinInterfaceMemberFound = true;
                                    break;
                                }
                            }

                            if (!lastProteinInterfaceMemberFound)
                            {
                                result[chainIndex] = stageIndex - 1;
                                break;
                            }
                        }

                        if (result[chainIndex] == stageIndex - 1)
                        {
                            break;
                        }
                    }

                    if (result[chainIndex] == stageIndex - 1)
                    {
                        break;
                    }
                }

                if (result[chainIndex] == -1)
                {
                    result[chainIndex] = chainMaxProteinInterfaceCountLastIndex;
                }
            }

            return(result);
        }
        /// <summary>
        ///     Load proteinInterface data from the PDB file based on a list of already detected proteinInterfaces.
        ///     The detected proteinInterfaces may be missing data such as other atoms or residues which are also in the proteinInterface but were not
        ///     directly interacting.
        ///     The positions and lengths of the proteinInterfaces are also calculated.
        /// </summary>
        /// <param name="pdbFilename"></param>
        /// <param name="pdbFileChains"></param>
        /// <param name="singularAaToAaInteractions"></param>
        /// <param name="proteinInterfacesClusteringResult"></param>
        /// <param name="detectedBestStages"></param>
        /// <param name="interactionBetweenProteinInterfacesContainer"></param>
        /// <returns></returns>
        public static List <ProteinInterfaceSequenceAndPositionData> AnalyseProteinInterfacesSequenceAndPositionData(
            string pdbFilename,
            Dictionary <string, List <string> > pdbIdChainIdList,
            ProteinChainListContainer pdbFileChains,
            ProteinChainListContainer singularAaToAaInteractions,
            ClusteringFullResultListContainer proteinInterfacesClusteringResult,
            int[] detectedBestStages,
            InteractionBetweenProteinInterfacesListContainer interactionBetweenProteinInterfacesContainer)
        {
            if (string.IsNullOrWhiteSpace(pdbFilename))
            {
                throw new ArgumentOutOfRangeException(nameof(pdbFilename));
            }

            if (!File.Exists(pdbFilename))
            {
                throw new FileNotFoundException("File not found", pdbFilename);
            }

            if (ParameterValidation.IsProteinChainListContainerNullOrEmpty(singularAaToAaInteractions))
            {
                throw new ArgumentOutOfRangeException(nameof(singularAaToAaInteractions));
            }

            if (ParameterValidation.IsClusteringFullResultListContainerNullOrEmpty(proteinInterfacesClusteringResult))
            {
                throw new ArgumentOutOfRangeException(nameof(proteinInterfacesClusteringResult));
            }

            if (ParameterValidation.IsIntArrayNullOrEmpty(detectedBestStages))
            {
                throw new ArgumentOutOfRangeException(nameof(detectedBestStages));
            }

            // ProteinInterfaces are clusters with non-proteinInterfaces removed.

            var    result      = new List <ProteinInterfaceSequenceAndPositionData>();
            string proteinId   = ProteinDataBankFileOperations.PdbIdFromPdbFilename(pdbFilename);
            int    totalChains = proteinInterfacesClusteringResult.ChainList.Count;

            for (int chainIndex = 0; chainIndex < totalChains; chainIndex++)
            {
                int    stageIndex    = detectedBestStages[chainIndex];
                string chainIdLetter = SpreadsheetFileHandler.AlphabetLetterRollOver(chainIndex);

                List <ClusteringFullResultListContainer.Chain.Stage.Cluster> proteinInterfaceList = proteinInterfacesClusteringResult.ChainList[chainIndex].StageList[stageIndex].ClusterList;

                List <ClusteringFullResultListContainer.Chain.Stage.Cluster> nonEmptyProteinInterfaceList = proteinInterfaceList.Where(a => a != null && a.AtomIndexList != null && a.AtomIndexList.Count > 0).ToList();

                // loop through each proteinInterface
                for (int proteinInterfaceIndex = 0; proteinInterfaceIndex < nonEmptyProteinInterfaceList.Count; proteinInterfaceIndex++)
                {
                    ClusteringFullResultListContainer.Chain.Stage.Cluster proteinInterface = nonEmptyProteinInterfaceList[proteinInterfaceIndex];

                    // Find min and max residue sequence index value in the proteinInterface

                    MinMax proteinInterfaceResidueSequenceIndexes = MinMaxResidueSequenceIndex(proteinInterface, singularAaToAaInteractions, chainIndex);
                    int    proteinInterfaceLength = CalculateProteinInterfaceLength(proteinInterfaceResidueSequenceIndexes.Min, proteinInterfaceResidueSequenceIndexes.Max);

                    string proteinInterfaceIdLetter = SpreadsheetFileHandler.AlphabetLetterRollOver(proteinInterfaceIndex);

                    var proteinInterfacePositionData = new ProteinInterfaceSequenceAndPositionData
                    {
                        FullProteinInterfaceId = new FullProteinInterfaceId(proteinId, chainIndex, proteinInterfaceIndex, proteinInterfaceResidueSequenceIndexes.Min, proteinInterfaceResidueSequenceIndexes.Max),
                        ChainIdLetter          = chainIdLetter,

                        ProteinInterfaceIdLetter = proteinInterfaceIdLetter,

                        StartPosition          = proteinInterfaceResidueSequenceIndexes.Min,
                        EndPosition            = proteinInterfaceResidueSequenceIndexes.Max,
                        ProteinInterfaceLength = CalculateProteinInterfaceLength(proteinInterfaceResidueSequenceIndexes.Min, proteinInterfaceResidueSequenceIndexes.Max)
                    };
                    proteinInterfacePositionData.AminoAcidSequenceAllResidueSequenceIndexes = new ProteinInterfaceAminoAcidMetaData[proteinInterfacePositionData.ProteinInterfaceLength];

                    proteinInterfacePositionData.AminoAcidSequenceAll1L             = "";
                    proteinInterfacePositionData.AminoAcidSequenceInteractionsAll1L = "";
                    proteinInterfacePositionData.AminoAcidSequenceInteractionsInsideProteinInterfacesOnly1L = "";
                    proteinInterfacePositionData.AminoAcidSequenceInteractionsNone1L = "";
                    proteinInterfacePositionData.AminoAcidSequenceInteractionsOutsideProteinInterfacesOnly1L = "";

                    proteinInterfacePositionData.AminoAcidSequenceAll3L             = "";
                    proteinInterfacePositionData.AminoAcidSequenceInteractionsAll3L = "";
                    proteinInterfacePositionData.AminoAcidSequenceInteractionsInsideProteinInterfacesOnly3L = "";
                    proteinInterfacePositionData.AminoAcidSequenceInteractionsNone3L = "";
                    proteinInterfacePositionData.AminoAcidSequenceInteractionsOutsideProteinInterfacesOnly3L = "";

                    //int foundAtomCount = 0;

                    const string placeholder1L = "_";
                    const string placeholder3L = "___";

                    for (int residueSequenceIndex = proteinInterfaceResidueSequenceIndexes.Min; residueSequenceIndex <= proteinInterfaceResidueSequenceIndexes.Max; residueSequenceIndex++)
                    {
                        /* questions
                         * 1. does this reside interact with another reside which is also part of a proteinInterface?
                         * 2. if not, does this reside interact at all?
                         */

                        var proteinInterfaceAminoAcidMetaData = new ProteinInterfaceAminoAcidMetaData();
                        proteinInterfacePositionData.AminoAcidSequenceAllResidueSequenceIndexes[proteinInterfacePositionData.AminoAcidSequenceAll1L.Length] = proteinInterfaceAminoAcidMetaData;

                        ATOM_Record foundAtomInsidePdbFile = AtomSearchMethods.FindAtomInsidePdbFileChain(pdbFileChains, chainIndex, residueSequenceIndex);

                        if (foundAtomInsidePdbFile == null)
                        {
                            // Non-CA atom is loaded here in case of missing CA atom to find the AA code for the resSeq index
                            var chainIdList = pdbIdChainIdList != null ? (pdbIdChainIdList.ContainsKey(proteinId) ? pdbIdChainIdList[proteinId].ToArray() : null) : null;

                            ProteinChainListContainer pdbFileChains2 = ProteinDataBankFileOperations.PdbAtomicChains(pdbFilename, chainIdList, -1, -1, false);
                            foundAtomInsidePdbFile = AtomSearchMethods.FindAtomInsidePdbFileChain(pdbFileChains2, chainIndex, residueSequenceIndex);
                        }

                        proteinInterfaceAminoAcidMetaData.PdbResidueSequenceIndex          = residueSequenceIndex;
                        proteinInterfaceAminoAcidMetaData.ArrayMemberIndex                 = pdbFileChains.ChainList[chainIndex].AtomList.IndexOf(foundAtomInsidePdbFile);
                        proteinInterfaceAminoAcidMetaData.OppoproteinInterfaceInteractions = new bool[proteinInterfaceLength];


                        if (foundAtomInsidePdbFile != null)
                        {
                            proteinInterfacePositionData.AminoAcidSequenceAll1L += AminoAcidConversions.AminoAcidNameToCode1L(foundAtomInsidePdbFile.resName.FieldValue);

                            proteinInterfacePositionData.AminoAcidSequenceAll3L += foundAtomInsidePdbFile.resName.FieldValue.PadRight(3, '_');
                        }
                        else
                        {
                            proteinInterfacePositionData.AminoAcidSequenceAll1L             += placeholder1L;
                            proteinInterfacePositionData.AminoAcidSequenceInteractionsAll1L += placeholder1L;
                            proteinInterfacePositionData.AminoAcidSequenceInteractionsInsideProteinInterfacesOnly1L  += placeholder1L;
                            proteinInterfacePositionData.AminoAcidSequenceInteractionsOutsideProteinInterfacesOnly1L += placeholder1L;
                            proteinInterfacePositionData.AminoAcidSequenceInteractionsNone1L += placeholder1L;

                            proteinInterfacePositionData.AminoAcidSequenceAll3L             += placeholder3L;
                            proteinInterfacePositionData.AminoAcidSequenceInteractionsAll3L += placeholder3L;
                            proteinInterfacePositionData.AminoAcidSequenceInteractionsInsideProteinInterfacesOnly3L  += placeholder3L;
                            proteinInterfacePositionData.AminoAcidSequenceInteractionsOutsideProteinInterfacesOnly3L += placeholder3L;
                            proteinInterfacePositionData.AminoAcidSequenceInteractionsNone3L += placeholder3L;

                            proteinInterfaceAminoAcidMetaData.ProteinInterfaceInteractionType = ProteinInterfaceInteractionType.NoInteractionFound;
                            proteinInterfaceAminoAcidMetaData.NonProteinInterfaceInteractionResidueNames1L += placeholder1L;
                            proteinInterfaceAminoAcidMetaData.NonProteinInterfaceInteractionResidueNames3L += placeholder3L;
                            continue;
                        }

                        List <ATOM_Record> foundAtomInteractingWithAnotherProteinInterface = AtomSearchMethods.FindAtomInteractingWithOtherProteinInterfaces(foundAtomInsidePdbFile, interactionBetweenProteinInterfacesContainer, FindAtomInteractingWithAnotherProteinInterfaceOptions.FindAtomsInteractingWithOtherProteinInterfaces);
                        List <ATOM_Record> foundAtomInteractingWithNonProteinInterface     = AtomSearchMethods.FindAtomInteractingWithOtherProteinInterfaces(foundAtomInsidePdbFile, interactionBetweenProteinInterfacesContainer, FindAtomInteractingWithAnotherProteinInterfaceOptions.FindAtomsInteractingWithNonProteinInterfaces);

                        proteinInterfaceAminoAcidMetaData.OppoproteinInterfaceInteractions = AminoAcidInteractionVector(singularAaToAaInteractions, proteinInterfacesClusteringResult, detectedBestStages, interactionBetweenProteinInterfacesContainer, chainIndex, proteinInterfaceIndex, residueSequenceIndex);

                        proteinInterfaceAminoAcidMetaData.ResidueName1L = AminoAcidConversions.AminoAcidNameToCode1L(foundAtomInsidePdbFile.resName.FieldValue);
                        proteinInterfaceAminoAcidMetaData.ResidueName3L = foundAtomInsidePdbFile.resName.FieldValue.PadRight(3, '_');

                        if (foundAtomInteractingWithAnotherProteinInterface != null)
                        {
                            foreach (ATOM_Record atom in foundAtomInteractingWithAnotherProteinInterface)
                            {
                                proteinInterfaceAminoAcidMetaData.ProteinInterfaceInteractionResidueNames1L += AminoAcidConversions.AminoAcidNameToCode1L(atom.resName.FieldValue);
                                proteinInterfaceAminoAcidMetaData.ProteinInterfaceInteractionResidueNames3L += atom.resName.FieldValue.PadRight(3, '_');
                            }
                        }

                        if (foundAtomInteractingWithNonProteinInterface != null)
                        {
                            foreach (ATOM_Record atom in foundAtomInteractingWithNonProteinInterface)
                            {
                                proteinInterfaceAminoAcidMetaData.NonProteinInterfaceInteractionResidueNames1L += AminoAcidConversions.AminoAcidNameToCode1L(atom.resName.FieldValue);
                                proteinInterfaceAminoAcidMetaData.NonProteinInterfaceInteractionResidueNames3L += atom.resName.FieldValue.PadRight(3, '_');
                            }
                        }

                        if (foundAtomInteractingWithAnotherProteinInterface != null && foundAtomInteractingWithAnotherProteinInterface.Count > 0)
                        {
                            proteinInterfacePositionData.AminoAcidSequenceInteractionsAll1L += AminoAcidConversions.AminoAcidNameToCode1L(foundAtomInsidePdbFile.resName.FieldValue);
                            proteinInterfacePositionData.AminoAcidSequenceInteractionsInsideProteinInterfacesOnly1L  += AminoAcidConversions.AminoAcidNameToCode1L(foundAtomInsidePdbFile.resName.FieldValue);
                            proteinInterfacePositionData.AminoAcidSequenceInteractionsOutsideProteinInterfacesOnly1L += placeholder1L;
                            proteinInterfacePositionData.AminoAcidSequenceInteractionsNone1L += placeholder1L;

                            proteinInterfacePositionData.AminoAcidSequenceInteractionsAll3L += foundAtomInsidePdbFile.resName.FieldValue.PadRight(3, '_');
                            proteinInterfacePositionData.AminoAcidSequenceInteractionsInsideProteinInterfacesOnly3L  += foundAtomInsidePdbFile.resName.FieldValue.PadRight(3, '_');
                            proteinInterfacePositionData.AminoAcidSequenceInteractionsOutsideProteinInterfacesOnly3L += placeholder3L;
                            proteinInterfacePositionData.AminoAcidSequenceInteractionsNone3L += placeholder3L;

                            proteinInterfaceAminoAcidMetaData.ProteinInterfaceInteractionType = ProteinInterfaceInteractionType.InteractionWithAnotherProteinInterface;

                            if (foundAtomInteractingWithNonProteinInterface != null && foundAtomInteractingWithNonProteinInterface.Count > 0)
                            {
                                proteinInterfaceAminoAcidMetaData.ProteinInterfaceInteractionType |= ProteinInterfaceInteractionType.InteractionWithNonProteinInterface;
                            }
                        }
                        else if (foundAtomInteractingWithNonProteinInterface != null && foundAtomInteractingWithNonProteinInterface.Count > 0)
                        {
                            proteinInterfacePositionData.AminoAcidSequenceInteractionsAll1L += AminoAcidConversions.AminoAcidNameToCode1L(foundAtomInsidePdbFile.resName.FieldValue);
                            proteinInterfacePositionData.AminoAcidSequenceInteractionsInsideProteinInterfacesOnly1L  += placeholder1L;
                            proteinInterfacePositionData.AminoAcidSequenceInteractionsOutsideProteinInterfacesOnly1L += AminoAcidConversions.AminoAcidNameToCode1L(foundAtomInsidePdbFile.resName.FieldValue);
                            proteinInterfacePositionData.AminoAcidSequenceInteractionsNone1L += placeholder1L;

                            proteinInterfacePositionData.AminoAcidSequenceInteractionsAll3L += foundAtomInsidePdbFile.resName.FieldValue.PadRight(3, '_');
                            proteinInterfacePositionData.AminoAcidSequenceInteractionsInsideProteinInterfacesOnly3L  += placeholder3L;
                            proteinInterfacePositionData.AminoAcidSequenceInteractionsOutsideProteinInterfacesOnly3L += foundAtomInsidePdbFile.resName.FieldValue.PadRight(3, '_');
                            proteinInterfacePositionData.AminoAcidSequenceInteractionsNone3L += placeholder3L;

                            proteinInterfaceAminoAcidMetaData.ProteinInterfaceInteractionType = ProteinInterfaceInteractionType.InteractionWithNonProteinInterface;
                        }
                        else
                        {
                            proteinInterfacePositionData.AminoAcidSequenceInteractionsAll1L += placeholder1L;
                            proteinInterfacePositionData.AminoAcidSequenceInteractionsInsideProteinInterfacesOnly1L  += placeholder1L;
                            proteinInterfacePositionData.AminoAcidSequenceInteractionsOutsideProteinInterfacesOnly1L += placeholder1L;
                            proteinInterfacePositionData.AminoAcidSequenceInteractionsNone1L += AminoAcidConversions.AminoAcidNameToCode1L(foundAtomInsidePdbFile.resName.FieldValue);

                            proteinInterfacePositionData.AminoAcidSequenceInteractionsAll3L += placeholder3L;
                            proteinInterfacePositionData.AminoAcidSequenceInteractionsInsideProteinInterfacesOnly3L  += placeholder3L;
                            proteinInterfacePositionData.AminoAcidSequenceInteractionsOutsideProteinInterfacesOnly3L += placeholder3L;
                            proteinInterfacePositionData.AminoAcidSequenceInteractionsNone3L += foundAtomInsidePdbFile.resName.FieldValue.PadRight(3, '_');

                            proteinInterfaceAminoAcidMetaData.ProteinInterfaceInteractionType = ProteinInterfaceInteractionType.NoInteractionFound;
                        }
                    }

                    result.Add(proteinInterfacePositionData);
                }
            }

            return(result);
        }
        /// <summary>
        ///     Removes groups from the cluster which do not meet the proteinInterface definition criteria.  Also provides a count of proteinInterfaces per
        ///     stage.
        /// </summary>
        /// <param name="proteinId"></param>
        /// <param name="chainInteractingAtomLists"></param>
        /// <param name="fullClusteringResult"></param>
        /// <param name="chainStageProteinInterfaceCount"></param>
        /// <param name="clusteringProteinInterfaceDensityDetectionOptions"></param>
        /// <param name="minimumProteinInterfaceAlphas"></param>
        /// <param name="maximumProteinInterfaceAlphas"></param>
        /// <param name="minimumProteinInterfaceDensity"></param>
        /// <param name="maximumProteinInterfaceDensity"></param>
        /// <returns></returns>
        public static ClusteringFullResultListContainer DetectProteinInterfaces(
            string proteinId,
            ProteinChainListContainer chainInteractingAtomLists,
            ClusteringFullResultListContainer fullClusteringResult,
            out List <List <int> > chainStageProteinInterfaceCount,
            ClusteringProteinInterfaceDensityDetectionOptions clusteringProteinInterfaceDensityDetectionOptions,// = ClusteringProteinInterfaceDensityDetectionOptions.ResidueSequenceIndex,
            decimal minimumProteinInterfaceDensity,
            decimal maximumProteinInterfaceDensity = -1,
            int minimumProteinInterfaceAlphas      = 4,
            int maximumProteinInterfaceAlphas      = -1
            )
        {
            if (ParameterValidation.IsProteinChainListContainerNullOrEmpty(chainInteractingAtomLists))
            {
                throw new ArgumentOutOfRangeException(nameof(chainInteractingAtomLists));
            }

            if (ParameterValidation.IsClusteringFullResultListContainerNullOrEmpty(fullClusteringResult))
            {
                throw new ArgumentOutOfRangeException(nameof(fullClusteringResult));
            }


            //var resultChainsStagesGroupsMembers = new List<List<List<List<int>>>>();
            var resultChainsStagesGroupsMembers = new ClusteringFullResultListContainer();

            chainStageProteinInterfaceCount = new List <List <int> >();

            for (int chainIndex = 0; chainIndex < fullClusteringResult.ChainList.Count; chainIndex++)
            {
                //var thisChain = new List<List<List<int>>>();

                var thisChain = new ClusteringFullResultListContainer.Chain();
                resultChainsStagesGroupsMembers.ChainList.Add(thisChain);

                var chainProteinInterfacesCount = new List <int>();
                chainStageProteinInterfaceCount.Add(chainProteinInterfacesCount);

                // Check every stage to see which has the lowest and highest number of suspected interaction proteinInterfaces.
                for (int stageIndex = 0; stageIndex < fullClusteringResult.ChainList[chainIndex].StageList.Count; stageIndex++)
                {
                    //var thisStage = new List<List<int>>();
                    var thisStage = new ClusteringFullResultListContainer.Chain.Stage();
                    thisChain.StageList.Add(thisStage);

                    int proteinInterfacesAtThisStage = 0;

                    // For every group, check if they have enough members (carbon alpha atoms) to meet the requirements of being an interaction proteinInterface.
                    for (int groupIndex = 0; groupIndex < fullClusteringResult.ChainList[chainIndex].StageList[stageIndex].ClusterList.Count; groupIndex++)
                    {
                        //var thisGroup = new List<int>();
                        var thisGroup = new ClusteringFullResultListContainer.Chain.Stage.Cluster();
                        thisStage.ClusterList.Add(thisGroup);

                        int groupTotalCarbonAlphas = fullClusteringResult.ChainList[chainIndex].StageList[stageIndex].ClusterList[groupIndex].AtomIndexList.Count;

                        if (((minimumProteinInterfaceAlphas > -1) && (groupTotalCarbonAlphas < minimumProteinInterfaceAlphas)) || ((maximumProteinInterfaceAlphas > -1) && groupTotalCarbonAlphas > maximumProteinInterfaceAlphas))
                        {
                            continue;
                        }

                        //var interactingGroup = clusters.ChainList[chainIndex].StageList[stageIndex].ClusterList[groupIndex].Select(member => chainInteractingAtomLists.ChainList[chainIndex].AtomList[member]).ToList();
                        var interactingGroup = new ProteinAtomListContainer
                        {
                            AtomList = fullClusteringResult.ChainList[chainIndex].StageList[stageIndex].ClusterList[groupIndex].AtomIndexList.Select(member => chainInteractingAtomLists.ChainList[chainIndex].AtomList[member]).ToList()
                        };

                        decimal totalProteinInterfaceLength;

                        switch (clusteringProteinInterfaceDensityDetectionOptions)
                        {
                        case ClusteringProteinInterfaceDensityDetectionOptions.ResidueSequenceIndex:
                            totalProteinInterfaceLength = Clustering.FindProteinInterfaceDistanceFromResidueSequenceIndex(interactingGroup);
                            break;

                        case ClusteringProteinInterfaceDensityDetectionOptions.ShortestRoutePath:
                            decimal bestPathDistance;
                            Clustering.BruteForceTravellingSalesmanProblemSolver(interactingGroup, out bestPathDistance);
                            totalProteinInterfaceLength = bestPathDistance;
                            break;

                        case ClusteringProteinInterfaceDensityDetectionOptions.LongestDistanceBetweenInteractions:
                            decimal longestDistance;
                            Clustering.FindLongestDistanceBetweenNodes(interactingGroup, out longestDistance);
                            totalProteinInterfaceLength = longestDistance;
                            break;

                        default:
                            throw new ArgumentOutOfRangeException();
                        }

                        // The length is the maximum distance between any two values/points or the total distance between them.
                        decimal interactionProteinInterfaceDensity = Math.Round((groupTotalCarbonAlphas / totalProteinInterfaceLength) * 100, 0);

                        if (((minimumProteinInterfaceDensity <= -1) || (interactionProteinInterfaceDensity >= minimumProteinInterfaceDensity)) && ((maximumProteinInterfaceDensity <= -1) || (interactionProteinInterfaceDensity <= maximumProteinInterfaceDensity)))
                        {
                            proteinInterfacesAtThisStage++;
                            //stageTotalProteinInterfaces[chainIndex].StageList[stageIndex]++;
                            for (int memberIndex = 0; memberIndex < fullClusteringResult.ChainList[chainIndex].StageList[stageIndex].ClusterList[groupIndex].AtomIndexList.Count; memberIndex++)
                            {
                                int thisMember = fullClusteringResult.ChainList[chainIndex].StageList[stageIndex].ClusterList[groupIndex].AtomIndexList[memberIndex];
                                thisGroup.AtomIndexList.Add(thisMember);
                            }
                            if (proteinInterfacesAtThisStage >= 4)
                            {
                                ////////Console.WriteLine(proteinInterfacesAtThisStage);
                            }
                        }
                    }

                    chainProteinInterfacesCount.Add(proteinInterfacesAtThisStage);
                }
            }

            return(resultChainsStagesGroupsMembers);
        }
        /// <summary>
        ///
        /// </summary>
        /// <param name="cancellationToken"></param>
        /// <param name="pdbFilename"></param>
        /// <param name="pdbIdChainIdList"></param>
        /// <param name="pdbFileChains"></param>
        /// <param name="singularAaToAaInteractions"></param>
        /// <param name="fullClusteringResult"></param>
        /// <returns></returns>
        public static ProteinInterfaceAnalysisResultData AnalyseProteinInterfaces(
            CancellationToken cancellationToken,
            decimal maxAtomInterationDistance,
            decimal minimumProteinInterfaceDensity,
            string pdbFilename,
            Dictionary <string, List <string> > pdbIdChainIdList,
            ProteinChainListContainer pdbFileChains,
            ProteinChainListContainer singularAaToAaInteractions,
            ClusteringFullResultListContainer fullClusteringResult)
        {
            if (ParameterValidation.IsLoadFilenameInvalid(pdbFilename))
            {
                throw new ArgumentOutOfRangeException(nameof(pdbFilename));
            }

            if (ParameterValidation.IsProteinChainListContainerNullOrEmpty(singularAaToAaInteractions))
            {
                throw new ArgumentOutOfRangeException(nameof(singularAaToAaInteractions));
            }

            if (ParameterValidation.IsClusteringFullResultListContainerNullOrEmpty(fullClusteringResult))
            {
                throw new ArgumentOutOfRangeException(nameof(fullClusteringResult));
            }

            string proteinId = ProteinDataBankFileOperations.PdbIdFromPdbFilename(pdbFilename);

            List <List <int> > chainStageProteinInterfaceCount;

            // Find how many proteinInterfaces at each stage.
            ClusteringFullResultListContainer proteinInterfacesClusteringResult = DetectProteinInterfaces(proteinId, singularAaToAaInteractions, fullClusteringResult, out chainStageProteinInterfaceCount, ClusteringProteinInterfaceDensityDetectionOptions.ResidueSequenceIndex, minimumProteinInterfaceDensity);

            // Find the last stage having required number of proteinInterfaces.
            int[] detectedBestClusterStagesIndexes = ProteinInterfaceTreeOptimalStageDetection.FindFinalProteinInterfaceStageIndexes(singularAaToAaInteractions, fullClusteringResult, proteinInterfacesClusteringResult, chainStageProteinInterfaceCount);

            int totalChains = singularAaToAaInteractions.ChainList.Count;

            var interactionProteinInterfaceClusteringHierarchyDataList = new List <InteractionProteinInterfaceClusteringHierarchyData>();

            int[] numberProteinInterfacesPerChain = FindNumberProteinInterfacesPerChain(proteinInterfacesClusteringResult, detectedBestClusterStagesIndexes);

            for (int chainIndex = 0; chainIndex < totalChains; chainIndex++)
            {
                int stageIndex = detectedBestClusterStagesIndexes[chainIndex];

                string chainIdLetter = SpreadsheetFileHandler.AlphabetLetterRollOver(chainIndex);

                var interactionProteinInterfaceClusteringHierarchyData = new InteractionProteinInterfaceClusteringHierarchyData(proteinId, chainIdLetter, numberProteinInterfacesPerChain[chainIndex], stageIndex + 1, fullClusteringResult.ChainList[chainIndex].StageList.Count);

                interactionProteinInterfaceClusteringHierarchyDataList.Add(interactionProteinInterfaceClusteringHierarchyData);
            }

            InteractionBetweenProteinInterfacesListContainer interactionBetweenProteinInterfacesContainer = CrossProteinInterfaceInteractions.FindInteractionsBetweenAnyProteinInterfaces(cancellationToken, maxAtomInterationDistance, pdbFilename, pdbIdChainIdList, pdbFileChains, singularAaToAaInteractions, fullClusteringResult, proteinInterfacesClusteringResult, detectedBestClusterStagesIndexes);

            List <ProteinInterfaceSequenceAndPositionData> analyseProteinInterfacesSequenceAndPositionData = AnalyseProteinInterfacesSequenceAndPositionData(pdbFilename, pdbIdChainIdList, pdbFileChains, singularAaToAaInteractions, proteinInterfacesClusteringResult, detectedBestClusterStagesIndexes, interactionBetweenProteinInterfacesContainer);

            var result = new ProteinInterfaceAnalysisResultData(
                detectedBestClusterStagesIndexes,
                proteinInterfacesClusteringResult,
                interactionProteinInterfaceClusteringHierarchyDataList,
                interactionBetweenProteinInterfacesContainer,
                analyseProteinInterfacesSequenceAndPositionData
                );

            return(result);
        }
        public static int[] FindNextProteinInterfaceGroupIndexes(
            ProteinChainListContainer chainInteractingAtomLists,
            ClusteringFullResultListContainer clusters,
            ClusteringFullResultListContainer proteinInterfaces,
            int[] stageIndexes,
            int[] lastIndexes)
        {
            if (ParameterValidation.IsProteinChainListContainerNullOrEmpty(chainInteractingAtomLists))
            {
                throw new ArgumentOutOfRangeException(nameof(chainInteractingAtomLists));
            }

            if (ParameterValidation.IsClusteringFullResultListContainerNullOrEmpty(clusters))
            {
                throw new ArgumentOutOfRangeException(nameof(clusters));
            }

            if (ParameterValidation.IsClusteringFullResultListContainerNullOrEmpty(proteinInterfaces))
            {
                throw new ArgumentOutOfRangeException(nameof(proteinInterfaces));
            }

            if (ParameterValidation.IsIntArrayNullOrEmpty(stageIndexes))
            {
                throw new ArgumentOutOfRangeException(nameof(stageIndexes));
            }

            if (ParameterValidation.IsIntArrayNullOrEmpty(lastIndexes))
            {
                throw new ArgumentOutOfRangeException(nameof(lastIndexes));
            }

            int totalChains = chainInteractingAtomLists.ChainList.Count;
            var nextProteinInterfaceIndexes = new int[totalChains];

            for (int chainIndex = 0; chainIndex < totalChains; chainIndex++)
            {
                nextProteinInterfaceIndexes[chainIndex] = -1;
                int lastIndex   = lastIndexes[chainIndex];
                int stageIndex  = stageIndexes[chainIndex];
                int totalGroups = proteinInterfaces.ChainList[chainIndex].StageList[stageIndex].ClusterList.Count;

                if (stageIndex > -1)
                {
                    for (int groupIndex = (lastIndex + 1); groupIndex < totalGroups; groupIndex++)
                    {
                        ClusteringFullResultListContainer.Chain.Stage.Cluster proteinInterface = proteinInterfaces.ChainList[chainIndex].StageList[stageIndex].ClusterList[groupIndex];
                        int totalAtoms = 0;

                        if (proteinInterface != null && proteinInterface.AtomIndexList != null && proteinInterface.AtomIndexList.Count > 0)
                        {
                            totalAtoms = proteinInterface.AtomIndexList.Count;
                        }

                        if (totalAtoms > 0)
                        {
                            nextProteinInterfaceIndexes[chainIndex] = groupIndex;
                            break;
                        }
                    }
                }
            }

            return(nextProteinInterfaceIndexes);
        }