public SubPhraseMatch(int startIndex, Part part)
			{
				StartIndex = startIndex;
				Part = part;
			}
		/// ------------------------------------------------------------------------------------
		/// <summary>
		/// Finds the longest phrase that is a sub-phrase of the specified part.
		/// </summary>
		/// <param name="part">The part.</param>
		/// <returns></returns>
		/// ------------------------------------------------------------------------------------
		private SubPhraseMatch FindSubPhraseMatch(Part part)
		{
			int partWordCount = part.m_words.Count;
			for (int subPhraseWordCount = partWordCount - 1; subPhraseWordCount > 1; subPhraseWordCount--)
			{
				Dictionary<Word, List<Part>> subPhraseTable;
				if (!m_partsTable.TryGetValue(subPhraseWordCount, out subPhraseTable))
					continue;

				for (int iWord = 0; iWord < partWordCount; iWord++)
				{
					Word word = part.m_words[iWord];
					if (iWord + subPhraseWordCount > partWordCount)
						break; // There aren't enough words left in this part to find a match
					List<Part> possibleSubParts;
					if (subPhraseTable.TryGetValue(word, out possibleSubParts))
					{
						foreach (Part possibleSubPart in possibleSubParts)
						{
							int iWordTemp = iWord + 1;
							int isubWord = 1;
							int possiblePartWordCount = possibleSubPart.m_words.Count;
							while (isubWord < possiblePartWordCount && possibleSubPart.m_words[isubWord] == part.m_words[iWordTemp++])
								isubWord++;
							if (isubWord == possiblePartWordCount)
								return new SubPhraseMatch(iWord, possibleSubPart);
						}
					}
				}
			}
			return null;
		}
		/// ------------------------------------------------------------------------------------
		/// <summary>
		/// Recalculates the part translation by considering all the owning phrases of the part
		/// and a probable translation based on what they have in common.
		/// </summary>
		/// <param name="part">The part.</param>
		/// <returns></returns>
		/// ------------------------------------------------------------------------------------
		private static IEnumerable<Part> RecalculatePartTranslation(Part part)
		{
			string originalTranslation = part.Translation;

			List<string> userTranslations = new List<string>();
			foreach (TranslatablePhrase phrase in part.OwningPhrases.Where(op => op.HasUserTranslation))
			{
				string toAdd = phrase.UserTransSansOuterPunctuation;
				foreach (IPhrasePart otherPart in phrase.GetParts().Where(otherPart => otherPart != part))
				{
					if (otherPart is KeyTermMatch)
					{
						foreach (string ktTrans in ((KeyTermMatch)otherPart).Renderings)
						{
							int ich = toAdd.IndexOf(ktTrans, StringComparison.Ordinal);
							if (ich >= 0)
							{
								toAdd = toAdd.Remove(ich, ktTrans.Length).Insert(ich, StringUtils.kszObject);
								break;
							}
						}
					}
					else
					{
						if (otherPart.Translation.Length > 0)
						{
							int ichMatch = toAdd.IndexOf(otherPart.Translation, StringComparison.Ordinal);
							if (ichMatch >= 0)
								toAdd = toAdd.Remove(ichMatch, otherPart.Translation.Length).Insert(ichMatch, StringUtils.kszObject);
						}
					}
				}
				if (!string.IsNullOrEmpty(toAdd))
					userTranslations.Add(toAdd);
			}

			string commonTranslation = GetBestCommonPartTranslation(userTranslations);
			if (commonTranslation != null)
				part.Translation = commonTranslation;
			if (originalTranslation.Length > 0 && (part.Translation.Length == 0 || originalTranslation.Contains(part.Translation)))
			{
				// The translation of the part has shrunk
				return part.OwningPhrases.Where(phr => phr.HasUserTranslation).SelectMany(otherPhrases => otherPhrases.TranslatableParts).Distinct();
			}
			return new Part[0];
		}
		/// ------------------------------------------------------------------------------------
		/// <summary>
		/// Gets or creates a part matching the given sub-phrase.
		/// </summary>
		/// <param name="words">The words of the sub-phrase.</param>
		/// <param name="owningPhraseOfPart">The owning phrase of the part to find or create.</param>
		/// <param name="tempWordCountOfPhraseBeingBroken">The temp word count of phrase being broken.</param>
		/// <returns>the newly created or found part</returns>
		/// ------------------------------------------------------------------------------------
		private Part GetOrCreatePart(IEnumerable<Word> words, TranslatablePhrase owningPhraseOfPart,
			int tempWordCountOfPhraseBeingBroken)
		{
			Debug.Assert(words.Any());
			Part part = null;

			Dictionary<Word, List<Part>> partsTable;
			List<Part> parts = null;
			if (m_partsTable.TryGetValue(words.Count(), out partsTable))
			{
				if (partsTable.TryGetValue(words.First(), out parts))
					part = parts.FirstOrDefault(x => x.Words.SequenceEqual(words));
			}
			else
				m_partsTable[words.Count()] = partsTable = new Dictionary<Word, List<Part>>();

			if (parts == null)
				partsTable[words.First()] = parts = new List<Part>();

			if (part == null)
			{
				Debug.Assert(tempWordCountOfPhraseBeingBroken != words.Count());
				part = new Part(words);
				parts.Add(part);
			}

			part.AddOwningPhrase(owningPhraseOfPart);

			return part;
		}