/// <summary>
		/// Creates a new practitioner that is the result of merging the two specified practitioners.
		/// </summary>
		/// <param name="right"></param>
		/// <param name="left"></param>
		/// <param name="name"></param>
		/// <param name="licenseNumber"></param>
		/// <param name="billingNumber"></param>
		/// <param name="extendedProperties"></param>
		/// <param name="defaultContactPoint"></param>
		/// <param name="deactivatedContactPoints"></param>
		/// <param name="contactPointReplacements"></param>
		/// <returns></returns>
		public static ExternalPractitioner MergePractitioners(
			ExternalPractitioner right,
			ExternalPractitioner left,
			PersonName name,
			string licenseNumber,
			string billingNumber,
			IDictionary<string, string> extendedProperties,
			ExternalPractitionerContactPoint defaultContactPoint,
			ICollection<ExternalPractitionerContactPoint> deactivatedContactPoints,
			IDictionary<ExternalPractitionerContactPoint, ExternalPractitionerContactPoint> contactPointReplacements)
		{
			// sanity check
			if (Equals(right, left))
				throw new WorkflowException("Cannot merge a practitioner with itself.");
			if (right.Deactivated || left.Deactivated)
				throw new WorkflowException("Cannot merge a practitioner that is de-activated.");
			if (right.IsMerged || left.IsMerged)
				throw new WorkflowException("Cannot merge a practitioner that has already been merged.");
			if (defaultContactPoint != null && defaultContactPoint.IsMerged)
				throw new WorkflowException("Cannot assigned a merged contact point as default");

			// update properties on result record
			var result = new ExternalPractitioner { Name = name, LicenseNumber = licenseNumber, BillingNumber = billingNumber };

			ExtendedPropertyUtils.Update(result.ExtendedProperties, extendedProperties);

			// construct the set of retained contact points
			var retainedContactPoints = new HashedSet<ExternalPractitionerContactPoint>();
			retainedContactPoints.AddAll(contactPointReplacements.Values);

			// some of the replacement contact points are merged.  This should not be allowed.
			if (CollectionUtils.Contains(contactPointReplacements.Values, cp => cp.IsMerged))
				throw new WorkflowException("Cannot replace a contact point with another that has already been merged.");

			// add any existing contact point that was not in the replacement list (because it is implicitly being retained)
			foreach (var contactPoint in CollectionUtils.Concat(right.ContactPoints, left.ContactPoints))
			{
				// No need to retain a merged contact point.  Because its replacement would already be retained.
				if (!contactPointReplacements.ContainsKey(contactPoint) && !contactPoint.IsMerged)
					retainedContactPoints.Add(contactPoint);
			}

			// for all retained contact points, create a copy attached to the result practitioner,
			// and mark the original as having been merged into the copy
			foreach (var original in retainedContactPoints)
			{
				var copy = original.CreateCopy(result);
				result.ContactPoints.Add(copy);

				copy.IsDefaultContactPoint = original.Equals(defaultContactPoint);
				copy.MarkDeactivated(original.Deactivated || deactivatedContactPoints.Contains(original));
				original.SetMergedInto(copy);
			}

			// for all replaced contact points, mark the original as being merged into the 
			// copy of the replacement
			foreach (var kvp in contactPointReplacements)
			{
				kvp.Key.SetMergedInto(kvp.Value.MergedInto);
			}

			// mark both left and right as edited and merged
			foreach (var practitioner in new[] { right, left })
	{
				practitioner.MarkEdited();
				practitioner.SetMergedInto(result);
			}

			// mark the result as being edited
			result.MarkEdited();
			return result;
		}
        /// <summary>
        /// Creates a new practitioner that is the result of merging the two specified practitioners.
        /// </summary>
        /// <param name="right"></param>
        /// <param name="left"></param>
        /// <param name="name"></param>
        /// <param name="licenseNumber"></param>
        /// <param name="billingNumber"></param>
        /// <param name="extendedProperties"></param>
        /// <param name="defaultContactPoint"></param>
        /// <param name="deactivatedContactPoints"></param>
        /// <param name="contactPointReplacements"></param>
        /// <returns></returns>
        public static ExternalPractitioner MergePractitioners(
            ExternalPractitioner right,
            ExternalPractitioner left,
            PersonName name,
            string licenseNumber,
            string billingNumber,
            IDictionary <string, string> extendedProperties,
            ExternalPractitionerContactPoint defaultContactPoint,
            ICollection <ExternalPractitionerContactPoint> deactivatedContactPoints,
            IDictionary <ExternalPractitionerContactPoint, ExternalPractitionerContactPoint> contactPointReplacements)
        {
            // sanity check
            if (Equals(right, left))
            {
                throw new WorkflowException("Cannot merge a practitioner with itself.");
            }
            if (right.Deactivated || left.Deactivated)
            {
                throw new WorkflowException("Cannot merge a practitioner that is de-activated.");
            }
            if (right.IsMerged || left.IsMerged)
            {
                throw new WorkflowException("Cannot merge a practitioner that has already been merged.");
            }
            if (defaultContactPoint != null && defaultContactPoint.IsMerged)
            {
                throw new WorkflowException("Cannot assigned a merged contact point as default");
            }

            // update properties on result record
            var result = new ExternalPractitioner {
                Name = name, LicenseNumber = licenseNumber, BillingNumber = billingNumber
            };

            ExtendedPropertyUtils.Update(result.ExtendedProperties, extendedProperties);

            // construct the set of retained contact points
            var retainedContactPoints = new HashedSet <ExternalPractitionerContactPoint>();

            retainedContactPoints.AddAll(contactPointReplacements.Values);

            // some of the replacement contact points are merged.  This should not be allowed.
            if (CollectionUtils.Contains(contactPointReplacements.Values, cp => cp.IsMerged))
            {
                throw new WorkflowException("Cannot replace a contact point with another that has already been merged.");
            }

            // add any existing contact point that was not in the replacement list (because it is implicitly being retained)
            foreach (var contactPoint in CollectionUtils.Concat(right.ContactPoints, left.ContactPoints))
            {
                // No need to retain a merged contact point.  Because its replacement would already be retained.
                if (!contactPointReplacements.ContainsKey(contactPoint) && !contactPoint.IsMerged)
                {
                    retainedContactPoints.Add(contactPoint);
                }
            }

            // for all retained contact points, create a copy attached to the result practitioner,
            // and mark the original as having been merged into the copy
            foreach (var original in retainedContactPoints)
            {
                var copy = original.CreateCopy(result);
                result.ContactPoints.Add(copy);

                copy.IsDefaultContactPoint = original.Equals(defaultContactPoint);
                copy.MarkDeactivated(original.Deactivated || deactivatedContactPoints.Contains(original));
                original.SetMergedInto(copy);
            }

            // for all replaced contact points, mark the original as being merged into the
            // copy of the replacement
            foreach (var kvp in contactPointReplacements)
            {
                kvp.Key.SetMergedInto(kvp.Value.MergedInto);
            }

            // mark both left and right as edited and merged
            foreach (var practitioner in new[] { right, left })
            {
                practitioner.MarkEdited();
                practitioner.SetMergedInto(result);
            }

            // mark the result as being edited
            result.MarkEdited();
            return(result);
        }