diff --git a/mzLib/Omics/IBioPolymerWithSetMods.cs b/mzLib/Omics/IBioPolymerWithSetMods.cs index eb1ed78c..47186b89 100644 --- a/mzLib/Omics/IBioPolymerWithSetMods.cs +++ b/mzLib/Omics/IBioPolymerWithSetMods.cs @@ -44,29 +44,6 @@ public interface IBioPolymerWithSetMods : IHasChemicalFormula, IEquatable BaseSequence[zeroBasedIndex]; IBioPolymer Parent { get; } - /// - /// Default Equals Method for IBioPolymerWithSetMods - /// - /// - /// - /// - /// Different parent but same sequence and digestion condition => are equal - /// Different Digestion agent but same sequence => are not equal (this is for multi-protease analysis in MetaMorpheus) - /// - bool IEquatable.Equals(IBioPolymerWithSetMods? other) - { - if (other is null) return false; - if (ReferenceEquals(this, other)) return true; - if (other.GetType() != GetType()) return false; - - // for those constructed from sequence and mods only - if (Parent is null && other.Parent is null) - return FullSequence.Equals(other.FullSequence); - - return FullSequence == other.FullSequence - && Equals(DigestionParams?.DigestionAgent, other.DigestionParams?.DigestionAgent); - } - public void Fragment(DissociationType dissociationType, FragmentationTerminus fragmentationTerminus, List products); diff --git a/mzLib/Proteomics/ProteolyticDigestion/PeptideWithSetModifications.cs b/mzLib/Proteomics/ProteolyticDigestion/PeptideWithSetModifications.cs index 1e7399fd..d3ca8dc2 100644 --- a/mzLib/Proteomics/ProteolyticDigestion/PeptideWithSetModifications.cs +++ b/mzLib/Proteomics/ProteolyticDigestion/PeptideWithSetModifications.cs @@ -16,7 +16,7 @@ namespace Proteomics.ProteolyticDigestion [Serializable] public class PeptideWithSetModifications : ProteolyticPeptide, IBioPolymerWithSetMods, IEquatable { - public string FullSequence { get; private set; } //sequence with modifications + public string FullSequence { get; init; } //sequence with modifications public int NumFixedMods { get; } // Parameter to store the full sequence of the corresponding Target or Decoy peptide // If the peptide in question is a decoy, this pairs it to the target it was generated from @@ -884,6 +884,11 @@ public override string ToString() return FullSequence + string.Join("\t", AllModsOneIsNterminus.Select(m => m.ToString())); } + #region IEquatable + + /// + /// Peptides are equal if they have the same full sequence, parent, and digestion agent + /// public override bool Equals(object obj) { if (obj is PeptideWithSetModifications peptide) @@ -893,12 +898,29 @@ public override bool Equals(object obj) return false; } + /// + /// Peptides are equal if they have the same full sequence, parent, and digestion agent + /// + public bool Equals(IBioPolymerWithSetMods other) => Equals(other as PeptideWithSetModifications); + + /// + /// Peptides are equal if they have the same full sequence, parent, and digestion agent + /// public bool Equals(PeptideWithSetModifications other) { - // interface equals first because it does null and reference checks - return (this as IBioPolymerWithSetMods).Equals(other) + if (other is null) return false; + if (ReferenceEquals(this, other)) return true; + if (other.GetType() != GetType()) return false; + + // for those constructed from sequence and mods only + if (Parent is null && other.Parent is null) + return FullSequence.Equals(other.FullSequence); + + return FullSequence == other.FullSequence + && Equals(DigestionParams?.DigestionAgent, other.DigestionParams?.DigestionAgent) + // These last two are important for parsimony in MetaMorpheus && OneBasedStartResidue == other!.OneBasedStartResidue - && Equals(Parent, other.Parent); + && Equals(Parent?.Accession, other.Parent?.Accession); } public override int GetHashCode() @@ -917,6 +939,8 @@ public override int GetHashCode() return hash.ToHashCode(); } + #endregion + /// /// This should be run after deserialization of a PeptideWithSetModifications, in order to set its Protein and Modification objects, which were not serialized /// diff --git a/mzLib/Test/TestPeptideWithSetMods.cs b/mzLib/Test/TestPeptideWithSetMods.cs index 438395c2..c2e19749 100644 --- a/mzLib/Test/TestPeptideWithSetMods.cs +++ b/mzLib/Test/TestPeptideWithSetMods.cs @@ -71,6 +71,8 @@ public static void TestPeptideOligoEquality() Assert.That(!peptide.Equals(oligo)); Assert.That(!((IBioPolymerWithSetMods)oligo).Equals(peptide)); Assert.That(!((IBioPolymerWithSetMods)peptide).Equals(oligo)); + Assert.That(!((object)oligo).Equals(peptide)); + Assert.That(!((object)peptide).Equals(oligo)); } [Test] @@ -1194,6 +1196,45 @@ public static void TestPeptideWithSetModsNoParentProtein() Assert.AreEqual('-', last.NextResidue); } + [Test] + public static void TestPeptideWithSetModsEquals() + { + // Create two proteins + Protein protein1 = new Protein("SEQUENCEK", "accession1"); + Protein protein2 = new Protein("SEQUENCEK", "accession2"); + + // Create digestion parameters + DigestionParams digestionParams = new DigestionParams(protease: "trypsin", maxMissedCleavages: 0, initiatorMethionineBehavior: InitiatorMethionineBehavior.Retain); + + // Digest the proteins to get peptides + PeptideWithSetModifications peptide1 = protein1.Digest(digestionParams, new List(), new List()).First(); + PeptideWithSetModifications peptide2 = protein2.Digest(digestionParams, new List(), new List()).First(); + + // Test equality - same peptide + Assert.IsTrue(peptide1.Equals(peptide1)); + + // different peptide + Assert.IsTrue(!peptide1.Equals(peptide2)); + Assert.IsTrue(!peptide1.Equals((object)peptide2)); + Assert.IsTrue(!peptide1.Equals((IBioPolymerWithSetMods)peptide2)); + Assert.AreNotEqual(peptide1.GetHashCode(), peptide2.GetHashCode()); + + // Test inequality with different start residue + PeptideWithSetModifications peptide3 = new PeptideWithSetModifications(protein1, digestionParams, 2, 9, CleavageSpecificity.Full, "", 0, new Dictionary(), 0); + Assert.IsFalse(peptide1.Equals(peptide3)); + + // Test inequality with different parent accession + PeptideWithSetModifications peptide4 = new PeptideWithSetModifications(protein2, digestionParams, 1, 9, CleavageSpecificity.Full, "", 0, new Dictionary(), 0); + Assert.IsFalse(peptide1.Equals(peptide4)); + + // all fail on null + Assert.That(!peptide1.Equals(null)); + Assert.That(!peptide1.Equals((object)null)); + Assert.That(!peptide1.Equals((PeptideWithSetModifications)null)); + } + + + [Test] public static void TestIBioPolymerWithSetModsModificationFromFullSequence() { diff --git a/mzLib/Test/Transcriptomics/TestOligoWithSetMods.cs b/mzLib/Test/Transcriptomics/TestOligoWithSetMods.cs index ceeaf3b7..2da62943 100644 --- a/mzLib/Test/Transcriptomics/TestOligoWithSetMods.cs +++ b/mzLib/Test/Transcriptomics/TestOligoWithSetMods.cs @@ -91,43 +91,55 @@ public static void TestEquality() .Digest(new RnaDigestionParams(), [], []) .ElementAt(1); - Assert.That(oligoWithSetMods, Is.EqualTo(oligoWithSetMods2)); + // same oligos + Assert.That(oligoWithSetMods.Equals(oligoWithSetMods2)); + Assert.That(oligoWithSetMods.Equals((object)oligoWithSetMods2)); + Assert.That(oligoWithSetMods.Equals((OligoWithSetMods)oligoWithSetMods2)); + Assert.That(oligoWithSetMods.Equals(oligoWithSetMods)); + Assert.That(oligoWithSetMods.Equals((object)oligoWithSetMods)); + Assert.That(oligoWithSetMods.Equals((OligoWithSetMods)oligoWithSetMods)); Assert.That(oligoWithSetMods.GetHashCode(), Is.EqualTo(oligoWithSetMods2.GetHashCode())); - Assert.That(oligoWithSetMods.Equals((object)oligoWithSetMods2)); // Test the Equals(Object obj) method + + // all fail on null Assert.That(!oligoWithSetMods2.Equals(null)); + Assert.That(!oligoWithSetMods2.Equals((object)null)); + Assert.That(!oligoWithSetMods2.Equals((OligoWithSetMods)null)); // Null parent checks oligoWithSetMods = new(oligoWithSetMods.FullSequence, modDict.ToDictionary(p => p.Value.First().IdWithMotif, p => p.Value.First())); oligoWithSetMods2 = new OligoWithSetMods(oligoWithSetMods.FullSequence, modDict.ToDictionary(p => p.Value.First().IdWithMotif, p => p.Value.First())); var oligoWithSetMods3 = new OligoWithSetMods(oligoWithSetMods.FullSequence + "AGAUA", modDict.ToDictionary(p => p.Value.First().IdWithMotif, p => p.Value.First())); - Assert.That(oligoWithSetMods, Is.EqualTo(oligoWithSetMods2)); - Assert.That(oligoWithSetMods, Is.EqualTo((object)oligoWithSetMods2)); - Assert.That(oligoWithSetMods, Is.EqualTo((OligoWithSetMods)oligoWithSetMods2)); - Assert.That(oligoWithSetMods, Is.Not.EqualTo(oligoWithSetMods3)); - Assert.That(oligoWithSetMods, Is.Not.EqualTo((object)oligoWithSetMods3)); - Assert.That(oligoWithSetMods, Is.Not.EqualTo((IBioPolymerWithSetMods)oligoWithSetMods3)); + // same oligo null parent + Assert.That(oligoWithSetMods.Equals(oligoWithSetMods2)); + Assert.That(oligoWithSetMods.Equals((object)oligoWithSetMods2)); + Assert.That(oligoWithSetMods.Equals((OligoWithSetMods)oligoWithSetMods2)); + + // different oligo null parent + Assert.That(!oligoWithSetMods.Equals(oligoWithSetMods3)); + Assert.That(!oligoWithSetMods.Equals((object)oligoWithSetMods3)); + Assert.That(!oligoWithSetMods.Equals((IBioPolymerWithSetMods)oligoWithSetMods3)); } [Test] [TestCase("GUACUG", "GUACUGGUACUG", "RNase A")] [TestCase("GUAGGAG", "GUAGCAG", "RNase A")] - public static void TestEquality_DifferentParentSameDigestionProduct(string sequence1, string sequence2, string enzyme) + public static void TestInequality_DifferentParentSameDigestionProduct(string sequence1, string sequence2, string enzyme) { var digestionParams = new RnaDigestionParams(rnase: enzyme, minLength: 1, maxMissedCleavages: 0); - var oligo1 = new RNA(sequence1) + var oligo1 = new RNA(sequence1, "", "rna1", "", "") .Digest(digestionParams, [], []) .First(); - var oligo2 = new RNA(sequence2) + var oligo2 = new RNA(sequence2, "", "rna3", "", "") .Digest(digestionParams, [], []) .First(); - Assert.That(oligo1, Is.EqualTo(oligo2)); + Assert.That(oligo1, Is.Not.EqualTo(oligo2)); Assert.That(oligo1.Equals(oligo1)); - Assert.That(oligo1, Is.EqualTo((object)oligo2)); - Assert.That(oligo1.GetHashCode(), Is.EqualTo(oligo2.GetHashCode())); + Assert.That(oligo1, Is.Not.EqualTo((object)oligo2)); + Assert.That(oligo1.GetHashCode(), Is.Not.EqualTo(oligo2.GetHashCode())); } /// diff --git a/mzLib/Transcriptomics/Digestion/OligoWithSetMods.cs b/mzLib/Transcriptomics/Digestion/OligoWithSetMods.cs index 6d35d80a..6455e209 100644 --- a/mzLib/Transcriptomics/Digestion/OligoWithSetMods.cs +++ b/mzLib/Transcriptomics/Digestion/OligoWithSetMods.cs @@ -215,6 +215,11 @@ public void Fragment(DissociationType dissociationType, FragmentationTerminus fr products.AddRange(GetNeutralFragments(type, sequence)); } + #region IEquatable + + /// + /// Oligos are equal if they have the same full sequence, parent, and digestion agent, and terminal caps + /// public override bool Equals(object? obj) { if (obj is OligoWithSetMods oligo) @@ -224,9 +229,31 @@ public override bool Equals(object? obj) return false; } + /// + /// Oligos are equal if they have the same full sequence, parent, and digestion agent, and terminal caps + /// + public bool Equals(IBioPolymerWithSetMods? other) => Equals(other as OligoWithSetMods); + + /// + /// Oligos are equal if they have the same full sequence, parent, and digestion agent, and terminal caps + /// public bool Equals(OligoWithSetMods? other) { - return (this as IBioPolymerWithSetMods).Equals(other); + if (other is null) return false; + if (ReferenceEquals(this, other)) return true; + if (other.GetType() != GetType()) return false; + + // for those constructed from sequence and mods only + if (Parent is null && other.Parent is null) + return FullSequence.Equals(other.FullSequence); + + return FullSequence == other.FullSequence + && Equals(DigestionParams?.DigestionAgent, other.DigestionParams?.DigestionAgent) + && _fivePrimeTerminus.Equals(other._fivePrimeTerminus) + && _threePrimeTerminus.Equals(other._threePrimeTerminus) + // These last two are important for parsimony in MetaMorpheus + && OneBasedStartResidue == other!.OneBasedStartResidue + && Equals(Parent?.Accession, other.Parent?.Accession); } public override int GetHashCode() @@ -242,9 +269,13 @@ public override int GetHashCode() { hash.Add(DigestionParams.DigestionAgent); } + hash.Add(FivePrimeTerminus); + hash.Add(ThreePrimeTerminus); return hash.ToHashCode(); } + #endregion + /// /// Generates theoretical internal fragments for given dissociation type for this peptide. /// The "products" parameter is filled with these fragments. diff --git a/mzLib/Transcriptomics/RNA.cs b/mzLib/Transcriptomics/RNA.cs index 41e3a64e..a7a5a480 100644 --- a/mzLib/Transcriptomics/RNA.cs +++ b/mzLib/Transcriptomics/RNA.cs @@ -23,7 +23,7 @@ public RNA(string sequence, IHasChemicalFormula? fivePrimeTerm = null, IHasChemi /// /// /// - /// + /// /// /// /// @@ -33,12 +33,12 @@ public RNA(string sequence, IHasChemicalFormula? fivePrimeTerm = null, IHasChemi /// /// /// - public RNA(string sequence, string name, string identifier, string organism, string databaseFilePath, + public RNA(string sequence, string name, string accession, string organism, string databaseFilePath, IHasChemicalFormula? fivePrimeTerminus = null, IHasChemicalFormula? threePrimeTerminus = null, IDictionary>? oneBasedPossibleModifications = null, bool isContaminant = false, bool isDecoy = false, List> geneNames = null, Dictionary? databaseAdditionalFields = null) - : base(sequence, name, identifier, organism, databaseFilePath, fivePrimeTerminus, threePrimeTerminus, + : base(sequence, name, accession, organism, databaseFilePath, fivePrimeTerminus, threePrimeTerminus, oneBasedPossibleModifications, isContaminant, isDecoy, geneNames, databaseAdditionalFields) { diff --git a/mzLib/mzLib.sln.DotSettings b/mzLib/mzLib.sln.DotSettings index 6c67babc..69660cbf 100644 --- a/mzLib/mzLib.sln.DotSettings +++ b/mzLib/mzLib.sln.DotSettings @@ -8,6 +8,7 @@ True True True + True True True True