예제 #1
0
 public bool Equals(PrecursorTextId other)
 {
     return(PrecursorMz.Equals(other.PrecursorMz) &&
            Equals(IonMobility, other.IonMobility) &&
            Equals(Target, other.Target) &&
            Extractor == other.Extractor);
 }
예제 #2
0
        public SpectrumFilterPair(PrecursorTextId precursorTextId, Color peptideColor, int id, double?minTime, double?maxTime,
                                  double highEnergyIonMobilityValueOffset, bool highAccQ1, bool highAccQ3)
        {
            Id = id;
            ModifiedSequence = precursorTextId.Target;
            PeptideColor     = peptideColor;
            Q1              = precursorTextId.PrecursorMz;
            Extractor       = precursorTextId.Extractor;
            MinTime         = minTime;
            MaxTime         = maxTime;
            IonMobilityInfo = precursorTextId.IonMobility;
            HighEnergyIonMobilityValueOffset = highEnergyIonMobilityValueOffset;
            MinIonMobilityValue = IonMobilityInfo.IsEmpty ? null : IonMobilityInfo.IonMobility.Mobility - (IonMobilityInfo.IonMobilityExtractionWindowWidth ?? 0) / 2;
            MaxIonMobilityValue = IonMobilityInfo.IsEmpty ? null : MinIonMobilityValue + (IonMobilityInfo.IonMobilityExtractionWindowWidth ?? 0);
            HighAccQ1           = highAccQ1;
            HighAccQ3           = highAccQ3;

            Ms1ProductFilters = SimProductFilters = Ms2ProductFilters = EMPTY_FILTERS;

            if (Q1 == 0)
            {
                Ms1ProductFilters = new[] { new SpectrumProductFilter(SignedMz.ZERO, 0) };
                SimProductFilters = Ms1ProductFilters;  // We want TIC and BPC for all scans, even if they have narrow machine settings and look like SIM
            }
        }
예제 #3
0
        public SpectrumFilterPair(PrecursorTextId precursorTextId, Color peptideColor, int id, double?minTime, double?maxTime,
                                  bool highAccQ1, bool highAccQ3)
        {
            Id = id;
            ModifiedSequence = precursorTextId.Target;
            PeptideColor     = peptideColor;
            Q1        = precursorTextId.PrecursorMz;
            Extractor = precursorTextId.Extractor;

            if (minTime.HasValue && maxTime.HasValue)
            {
                _filterByTime = true;
                _minTime      = minTime.Value;
                _maxTime      = maxTime.Value;
            }
            else
            {
                // If not min and max, then it should be neither. Asymmetric limits not supported.
                Assume.IsTrue(!minTime.HasValue && !maxTime.HasValue);
            }
            IonMobilityInfo     = precursorTextId.IonMobility;
            MinIonMobilityValue = IonMobilityInfo.IsEmpty ? null : IonMobilityInfo.IonMobility.Mobility - (IonMobilityInfo.IonMobilityExtractionWindowWidth ?? 0) / 2;
            MaxIonMobilityValue = IonMobilityInfo.IsEmpty ? null : MinIonMobilityValue + (IonMobilityInfo.IonMobilityExtractionWindowWidth ?? 0);
            HighAccQ1           = highAccQ1;
            HighAccQ3           = highAccQ3;

            Ms1ProductFilters = SimProductFilters = Ms2ProductFilters = EMPTY_FILTERS;

            if (Q1 == 0)
            {
                Ms1ProductFilters = new[] { new SpectrumProductFilter(SignedMz.ZERO, 0, 0) };
                SimProductFilters = Ms1ProductFilters;  // We want TIC and BPC for all scans, even if they have narrow machine settings and look like SIM
            }
        }
예제 #4
0
        public SpectrumFilterPair(PrecursorTextId precursorTextId, Color peptideColor, int id, double?minTime, double?maxTime,
                                  double?minDriftTimeMsec, double?maxDriftTimeMsec, double highEnergyDriftTimeOffsetMsec, bool highAccQ1, bool highAccQ3)
        {
            Id = id;
            ModifiedSequence = precursorTextId.TextId;
            PeptideColor     = peptideColor;
            Q1               = precursorTextId.PrecursorMz;
            Extractor        = precursorTextId.Extractor;
            MinTime          = minTime;
            MaxTime          = maxTime;
            MinDriftTimeMsec = minDriftTimeMsec;
            MaxDriftTimeMsec = maxDriftTimeMsec;
            HighEnergyDriftTimeOffsetMsec = highEnergyDriftTimeOffsetMsec;
            HighAccQ1 = highAccQ1;
            HighAccQ3 = highAccQ3;

            Ms1ProductFilters = SimProductFilters = Ms2ProductFilters = EMPTY_FILTERS;

            if (Q1 == 0)
            {
                Ms1ProductFilters = new[] { new SpectrumProductFilter(0, 0) };
            }
        }
예제 #5
0
        public SpectrumFilterPair(PrecursorTextId precursorTextId, Color peptideColor, int id, double?minTime, double?maxTime,
                                  double?minIonMobilityValue, double?maxIonMobilityValue, IonMobilityAndCCS ionMobilityInfo, bool highAccQ1, bool highAccQ3)
        {
            Id = id;
            ModifiedSequence = precursorTextId.Target;
            PeptideColor     = peptideColor;
            Q1                  = precursorTextId.PrecursorMz;
            Extractor           = precursorTextId.Extractor;
            MinTime             = minTime;
            MaxTime             = maxTime;
            MinIonMobilityValue = minIonMobilityValue;
            MaxIonMobilityValue = maxIonMobilityValue;
            IonMobilityInfo     = ionMobilityInfo ?? IonMobilityAndCCS.EMPTY;
            HighAccQ1           = highAccQ1;
            HighAccQ3           = highAccQ3;

            Ms1ProductFilters = SimProductFilters = Ms2ProductFilters = EMPTY_FILTERS;

            if (Q1 == 0)
            {
                Ms1ProductFilters = new[] { new SpectrumProductFilter(SignedMz.ZERO, 0) };
                SimProductFilters = Ms1ProductFilters;  // We want TIC and BPC for all scans, even if they have narrow machine settings and look like SIM
            }
        }
예제 #6
0
        public IsotopeDistInfo(MassDistribution massDistribution,
                               TypedMass monoisotopicMass,
                               Adduct adduct,
                               Func <double, double> calcFilterWindow,
                               double massResolution,
                               double minimumAbundance)
        {
            _monoisotopicMass = monoisotopicMass;
            _adduct           = adduct.Unlabeled; // Don't reapply explicit isotope labels

            // Get peak center of mass values for the given resolution
            var q1FilterValues = MassDistribution.NewInstance(massDistribution, massResolution, 0).Keys.ToList();
            // Find the monoisotopic m/z and make sure it is exactly the expected number
            double monoMz        = _adduct.MzFromNeutralMass(_monoisotopicMass);
            double monoMzDist    = monoMz;
            int    monoMassIndex = 0;

            for (int i = 0; i < q1FilterValues.Count; i++)
            {
                double peakCenterMz = q1FilterValues[i];
                double filterWindow = calcFilterWindow(peakCenterMz);
                double startMz      = peakCenterMz - filterWindow / 2;
                double endMz        = startMz + filterWindow;
                if (startMz < monoMz && monoMz < endMz)
                {
                    monoMzDist        = q1FilterValues[i];
                    q1FilterValues[i] = monoMz;
                    monoMassIndex     = i;
                    break;
                }
            }
            // Insert a M-1 peak, even if it is not expected in the isotope mass distribution
            if (monoMassIndex == 0 && q1FilterValues.Count > 1)
            {
                // Use the delta from the original distribution monoMz to the next peak
                q1FilterValues.Insert(0, monoMz + monoMzDist - q1FilterValues[1]);
                monoMassIndex++;
            }

            if (!q1FilterValues.Any())
            {
                // This should never happen, but just in case it does happen, we safely exit the constructor
                ExpectedPeaks = ImmutableList.Singleton(new MzRankProportion(monoMz, 0, 1.0f));
                MonoMassIndex = BaseMassIndex = 0;
                return;
            }

            var signedQ1FilterValues = q1FilterValues.Select(q => new SignedMz(q, adduct.AdductCharge < 0)).ToList();

            // Use the filtering algorithm that will be used on real data to determine the
            // expected proportions of the mass distribution that will end up filtered into
            // peaks
            // CONSIDER: Mass accuracy information is not calculated here
            var key    = new PrecursorTextId(signedQ1FilterValues[monoMassIndex], null, null, ChromExtractor.summed);
            var filter = new SpectrumFilterPair(key, PeptideDocNode.UNKNOWN_COLOR, 0, null, null, false, false);

            filter.AddQ1FilterValues(signedQ1FilterValues, calcFilterWindow);

            var expectedSpectrum = filter.FilterQ1SpectrumList(new[] { new MsDataSpectrum
                                                                       {
                                                                           Mzs = massDistribution.Keys.ToArray(), Intensities = massDistribution.Values.ToArray(), NegativeCharge = (adduct.AdductCharge < 0)
                                                                       } });

            int startIndex = expectedSpectrum.Intensities.IndexOf(inten => inten >= minimumAbundance);

            if (startIndex == -1)
            {
                // This can happen if the amino acid modifications are messed up,
                // and the peptide mass is negative or something.
                ExpectedPeaks = ImmutableList.Singleton(new MzRankProportion(monoMz, 0, 1.0f));
                MonoMassIndex = BaseMassIndex = 0;
                return;
            }
            // Always include the M-1 peak, even if it is expected to have zero intensity
            if (startIndex > monoMassIndex - 1)
            {
                startIndex = monoMassIndex - 1;
            }
            if (startIndex < 0)
            {
                startIndex = 0;
            }
            int endIndex              = expectedSpectrum.Intensities.LastIndexOf(inten => inten >= minimumAbundance) + 1;
            int countPeaks            = endIndex - startIndex;
            var listProportionIndices = new List <KeyValuePair <float, int> >(countPeaks);

            for (int i = 0; i < countPeaks; i++)
            {
                listProportionIndices.Add(new KeyValuePair <float, int>(
                                              expectedSpectrum.Intensities[i + startIndex], i));
            }
            // Sort proportions descending.
            listProportionIndices.Sort((p1, p2) => Comparer.Default.Compare(p2.Key, p1.Key));

            // Set proportions and ranks back in the original locations
            var expectedProportionRanks = new KeyValuePair <float, int> [countPeaks];

            for (int i = 0; i < countPeaks; i++)
            {
                expectedProportionRanks[listProportionIndices[i].Value] =
                    new KeyValuePair <float, int>(listProportionIndices[i].Key, i + 1);
            }

            MonoMassIndex = monoMassIndex - startIndex;

            // Find the base peak and fill in the masses and proportions
            var expectedPeaks = new List <MzRankProportion>();

            for (int i = 0; i < countPeaks; i++)
            {
                float expectedProportion = expectedProportionRanks[i].Key;
                int   rank = expectedProportionRanks[i].Value;
                expectedPeaks.Add(new MzRankProportion(q1FilterValues[i + startIndex], rank, expectedProportion));
                if (expectedProportion > expectedProportionRanks[BaseMassIndex].Key)
                {
                    BaseMassIndex = i;
                }
            }
            ExpectedPeaks = expectedPeaks;
        }
예제 #7
0
        public SpectrumFilter(SrmDocument document, MsDataFileUri msDataFileUri, IFilterInstrumentInfo instrumentInfo,
                              IRetentionTimePredictor retentionTimePredictor = null, bool firstPass = false)
        {
            _fullScan          = document.Settings.TransitionSettings.FullScan;
            _instrument        = document.Settings.TransitionSettings.Instrument;
            _acquisitionMethod = _fullScan.AcquisitionMethod;
            if (instrumentInfo != null)
            {
                _isWatersFile = instrumentInfo.IsWatersFile;
            }
            IsFirstPass = firstPass;

            var comparer = PrecursorTextId.PrecursorTextIdComparerInstance;
            var dictPrecursorMzToFilter = new SortedDictionary <PrecursorTextId, SpectrumFilterPair>(comparer);

            if (EnabledMs || EnabledMsMs)
            {
                if (EnabledMs)
                {
                    _isHighAccMsFilter = !Equals(_fullScan.PrecursorMassAnalyzer,
                                                 FullScanMassAnalyzerType.qit);

                    if (!firstPass)
                    {
                        var key = new PrecursorTextId(SignedMz.ZERO, null, ChromExtractor.summed);  // TIC
                        dictPrecursorMzToFilter.Add(key, new SpectrumFilterPair(key, PeptideDocNode.UNKNOWN_COLOR, dictPrecursorMzToFilter.Count,
                                                                                _instrument.MinTime, _instrument.MaxTime, null, null, 0, _isHighAccMsFilter, _isHighAccProductFilter));
                        key = new PrecursorTextId(SignedMz.ZERO, null, ChromExtractor.base_peak);   // BPC
                        dictPrecursorMzToFilter.Add(key, new SpectrumFilterPair(key, PeptideDocNode.UNKNOWN_COLOR, dictPrecursorMzToFilter.Count,
                                                                                _instrument.MinTime, _instrument.MaxTime, null, null, 0, _isHighAccMsFilter, _isHighAccProductFilter));
                    }
                }
                if (EnabledMsMs)
                {
                    _isHighAccProductFilter = !Equals(_fullScan.ProductMassAnalyzer,
                                                      FullScanMassAnalyzerType.qit);

                    if (_fullScan.AcquisitionMethod == FullScanAcquisitionMethod.DIA &&
                        _fullScan.IsolationScheme.IsAllIons)
                    {
                        if (instrumentInfo != null)
                        {
                            _isWatersMse  = _isWatersFile;
                            _isAgilentMse = instrumentInfo.IsAgilentFile;
                        }
                        _mseLevel = 1;
                    }
                }

                Func <double, double> calcWindowsQ1 = _fullScan.GetPrecursorFilterWindow;
                Func <double, double> calcWindowsQ3 = _fullScan.GetProductFilterWindow;
                _minTime = _instrument.MinTime;
                _maxTime = _instrument.MaxTime;
                bool canSchedule = CanSchedule(document, retentionTimePredictor);
                // TODO: Figure out a way to turn off time sharing on first SIM scan so that
                //       times can be shared for MS1 without SIM scans
                _isSharedTime = !canSchedule;

                // If we're using bare measured drift times from spectral libraries, go get those now
                var libraryIonMobilityInfo = document.Settings.PeptideSettings.Prediction.UseLibraryDriftTimes
                    ? document.Settings.GetIonMobilities(msDataFileUri)
                    : null;

                foreach (var nodePep in document.Molecules)
                {
                    if (firstPass && !retentionTimePredictor.IsFirstPassPeptide(nodePep))
                    {
                        continue;
                    }

                    foreach (TransitionGroupDocNode nodeGroup in nodePep.Children)
                    {
                        if (nodeGroup.Children.Count == 0)
                        {
                            continue;
                        }

                        double?       minTime = _minTime, maxTime = _maxTime;
                        double?       startDriftTimeMsec = null, endDriftTimeMsec = null;
                        double        windowDT;
                        double        highEnergyDriftTimeOffsetMsec = 0;
                        DriftTimeInfo centerDriftTime               = document.Settings.PeptideSettings.Prediction.GetDriftTime(
                            nodePep, nodeGroup, libraryIonMobilityInfo, out windowDT);
                        if (centerDriftTime.DriftTimeMsec(false).HasValue)
                        {
                            startDriftTimeMsec            = centerDriftTime.DriftTimeMsec(false) - windowDT / 2; // Get the low energy drift time
                            endDriftTimeMsec              = startDriftTimeMsec + windowDT;
                            highEnergyDriftTimeOffsetMsec = centerDriftTime.HighEnergyDriftTimeOffsetMsec;
                        }

                        if (canSchedule)
                        {
                            if (RetentionTimeFilterType.scheduling_windows == _fullScan.RetentionTimeFilterType)
                            {
                                double?centerTime = null;
                                double windowRT   = 0;
                                if (retentionTimePredictor != null)
                                {
                                    centerTime = retentionTimePredictor.GetPredictedRetentionTime(nodePep);
                                }
                                else
                                {
                                    var prediction = document.Settings.PeptideSettings.Prediction;
                                    if (prediction.RetentionTime == null || !prediction.RetentionTime.IsAutoCalculated)
                                    {
                                        centerTime = document.Settings.PeptideSettings.Prediction.PredictRetentionTimeForChromImport(
                                            document, nodePep, nodeGroup, out windowRT);
                                    }
                                }
                                // Force the center time to be at least zero
                                if (centerTime.HasValue && centerTime.Value < 0)
                                {
                                    centerTime = 0;
                                }
                                if (_fullScan.RetentionTimeFilterLength != 0)
                                {
                                    windowRT = _fullScan.RetentionTimeFilterLength * 2;
                                }
                                if (centerTime != null)
                                {
                                    double startTime = centerTime.Value - windowRT / 2;
                                    double endTime   = startTime + windowRT;
                                    minTime = Math.Max(minTime ?? 0, startTime);
                                    maxTime = Math.Min(maxTime ?? double.MaxValue, endTime);
                                }
                            }
                            else if (RetentionTimeFilterType.ms2_ids == _fullScan.RetentionTimeFilterType)
                            {
                                var times = document.Settings.GetBestRetentionTimes(nodePep, msDataFileUri);
                                if (times.Length > 0)
                                {
                                    minTime = Math.Max(minTime ?? 0, times.Min() - _fullScan.RetentionTimeFilterLength);
                                    maxTime = Math.Min(maxTime ?? double.MaxValue, times.Max() + _fullScan.RetentionTimeFilterLength);
                                }
                            }
                        }

                        SpectrumFilterPair filter;
                        string             textId = nodePep.RawTextId; // Modified Sequence for peptides, or some other string for custom ions
                        var mz  = new SignedMz(nodeGroup.PrecursorMz, nodeGroup.PrecursorCharge < 0);
                        var key = new PrecursorTextId(mz, textId, ChromExtractor.summed);
                        if (!dictPrecursorMzToFilter.TryGetValue(key, out filter))
                        {
                            filter = new SpectrumFilterPair(key, nodePep.Color, dictPrecursorMzToFilter.Count, minTime, maxTime,
                                                            startDriftTimeMsec, endDriftTimeMsec, highEnergyDriftTimeOffsetMsec,
                                                            _isHighAccMsFilter, _isHighAccProductFilter);
                            dictPrecursorMzToFilter.Add(key, filter);
                        }

                        if (!EnabledMs)
                        {
                            filter.AddQ3FilterValues(from TransitionDocNode nodeTran in nodeGroup.Children
                                                     select nodeTran.Mz, calcWindowsQ3);
                        }
                        else if (!EnabledMsMs)
                        {
                            filter.AddQ1FilterValues(GetMS1MzValues(nodeGroup), calcWindowsQ1);
                        }
                        else
                        {
                            filter.AddQ1FilterValues(GetMS1MzValues(nodeGroup), calcWindowsQ1);
                            filter.AddQ3FilterValues(from TransitionDocNode nodeTran in nodeGroup.Children
                                                     where !nodeTran.IsMs1
                                                     select nodeTran.Mz, calcWindowsQ3);
                        }
                    }
                }
                _filterMzValues = dictPrecursorMzToFilter.Values.ToArray();

                var listChromKeyFilterIds = new List <ChromKey>();
                foreach (var spectrumFilterPair in _filterMzValues)
                {
                    spectrumFilterPair.AddChromKeys(listChromKeyFilterIds);
                }
                _productChromKeys = listChromKeyFilterIds.ToArray();

                // Sort a copy of the filter pairs by maximum retention time so that we can detect when
                // filters are no longer active.
                _filterRTValues = new SpectrumFilterPair[_filterMzValues.Length];
                Array.Copy(_filterMzValues, _filterRTValues, _filterMzValues.Length);
                Array.Sort(_filterRTValues, CompareByRT);
            }

            InitRTLimits();
        }
예제 #8
0
        public IsotopeDistInfo(MassDistribution massDistribution,
                               double monoisotopicMass,
                               bool isMassH, // Is monoisotopicMass M+H, or just M as in small molecule use?
                               int charge,
                               Func <double, double> calcFilterWindow,
                               double massResolution,
                               double minimumAbundance)
        {
            _monoisotopicMass = monoisotopicMass;
            _charge           = charge;
            _isMassH          = isMassH;

            // Get peak center of mass values for the given resolution
            var q1FilterValues = MassDistribution.NewInstance(massDistribution, massResolution, 0).Keys.ToList();
            // Find the monoisotopic m/z and make sure it is exactly the expected number
            double monoMz        = isMassH ? SequenceMassCalc.GetMZ(_monoisotopicMass, _charge) : BioMassCalc.CalculateIonMz(_monoisotopicMass, _charge);
            double monoMzDist    = monoMz;
            int    monoMassIndex = 0;

            for (int i = 0; i < q1FilterValues.Count; i++)
            {
                double peakCenterMz = q1FilterValues[i];
                double filterWindow = calcFilterWindow(peakCenterMz);
                double startMz      = peakCenterMz - filterWindow / 2;
                double endMz        = startMz + filterWindow;
                if (startMz < monoMz && monoMz < endMz)
                {
                    monoMzDist        = q1FilterValues[i];
                    q1FilterValues[i] = monoMz;
                    monoMassIndex     = i;
                    break;
                }
            }
            // Insert a M-1 peak, even if it is not expected in the isotope mass distribution
            if (monoMassIndex == 0 && q1FilterValues.Count > 1)
            {
                // Use the delta from the original distribution monoMz to the next peak
                q1FilterValues.Insert(0, monoMz + monoMzDist - q1FilterValues[1]);
                monoMassIndex++;
            }

            if (!q1FilterValues.Any())  // As is small molecule docs with mz values only, no formulas
            {
                return;
            }

            // Use the filtering algorithm that will be used on real data to determine the
            // expected proportions of the mass distribution that will end up filtered into
            // peaks
            // CONSIDER: Mass accuracy information is not calculated here
            var key    = new PrecursorTextId(q1FilterValues[monoMassIndex], null, ChromExtractor.summed);
            var filter = new SpectrumFilterPair(key, PeptideDocNode.UNKNOWN_COLOR, 0, null, null, null, null, 0, false, false);

            filter.AddQ1FilterValues(q1FilterValues, calcFilterWindow);

            var expectedSpectrum = filter.FilterQ1SpectrumList(new[] { new MsDataSpectrum
                                                                       {
                                                                           Mzs = massDistribution.Keys.ToArray(), Intensities = massDistribution.Values.ToArray()
                                                                       } });

            int startIndex = expectedSpectrum.Intensities.IndexOf(inten => inten >= minimumAbundance);

            if (startIndex == -1)
            {
                throw new InvalidOperationException(
                          string.Format(Resources.IsotopeDistInfo_IsotopeDistInfo_Minimum_abundance__0__too_high,
                                        minimumAbundance));
            }
            // Always include the M-1 peak, even if it is expected to have zero intensity
            if (startIndex > monoMassIndex - 1)
            {
                startIndex = monoMassIndex - 1;
            }
            if (startIndex < 0)
            {
                startIndex = 0;
            }
            int endIndex              = expectedSpectrum.Intensities.LastIndexOf(inten => inten >= minimumAbundance) + 1;
            int countPeaks            = endIndex - startIndex;
            var listProportionIndices = new List <KeyValuePair <float, int> >(countPeaks);

            for (int i = 0; i < countPeaks; i++)
            {
                listProportionIndices.Add(new KeyValuePair <float, int>(
                                              expectedSpectrum.Intensities[i + startIndex], i));
            }
            // Sort proportions descending.
            listProportionIndices.Sort((p1, p2) => Comparer.Default.Compare(p2.Key, p1.Key));

            // Set proportions and ranks back in the original locations
            var expectedProportionRanks = new KeyValuePair <float, int> [countPeaks];

            for (int i = 0; i < countPeaks; i++)
            {
                expectedProportionRanks[listProportionIndices[i].Value] =
                    new KeyValuePair <float, int>(listProportionIndices[i].Key, i + 1);
            }

            // TODO: Can this be discarded?
            // MassDistribution = massDistribution;

            MonoMassIndex = monoMassIndex - startIndex;

            // Find the base peak and fill in the masses and proportions
            var expectedPeaks = new List <MzRankProportion>();

            for (int i = 0; i < countPeaks; i++)
            {
                float expectedProportion = expectedProportionRanks[i].Key;
                int   rank = expectedProportionRanks[i].Value;
                expectedPeaks.Add(new MzRankProportion(q1FilterValues[i + startIndex], rank, expectedProportion));
                if (expectedProportion > expectedProportionRanks[BaseMassIndex].Key)
                {
                    BaseMassIndex = i;
                }
            }
            ExpectedPeaks = expectedPeaks;
        }
예제 #9
0
 public bool Equals(PrecursorTextId other)
 {
     return(PrecursorMz.Equals(other.PrecursorMz) &&
            string.Equals(TextId, other.TextId) &&
            Extractor == other.Extractor);
 }