/// <summary>
        /// Объединяет параллелепипеды, если возможно
        /// </summary>
        public static SpatialBox <TContext> Merge <TContext>(this SpatialBox <TContext> instance, SpatialBox <TContext> other)
            where TContext : class
        {
            var areOffersEqual  = instance.OfferInterval.EqualsTo(other.OfferInterval);
            var areNightsEqual  = instance.NightInterval.EqualsTo(other.NightInterval);
            var areSeasonsEqual = instance.SeasonInterval.EqualsTo(other.SeasonInterval);

            var offerMerged  = instance.OfferInterval.Merge(other.OfferInterval, (x, i) => x.AddDays(i));
            var nightMerged  = instance.NightInterval.Merge(other.NightInterval, (x, i) => (short)(x + i));
            var seasonMerged = instance.SeasonInterval.Merge(other.SeasonInterval, (x, i) => x.AddDays(i));

            if (offerMerged != null && areNightsEqual && areSeasonsEqual)
            {
                return(instance.Copy(offerInterval: offerMerged));
            }
            if (areOffersEqual && nightMerged != null && areSeasonsEqual)
            {
                return(instance.Copy(nightInterval: nightMerged));
            }
            if (areOffersEqual && areNightsEqual && seasonMerged != null)
            {
                return(instance.Copy(seasonInterval: seasonMerged));
            }

            return(null);
        }
        /// <summary>
        /// Находит остатки от пересечения текущего куба с посторонним кубом
        /// </summary>
        public static List <SpatialBox <TContext> > Difference <TContext>(this SpatialBox <TContext> instance, SpatialBox <TContext> other)
            where TContext : class
        {
            var intersection = instance.Intersect(other, (x, y) => null); // we do not need context of intersection

            if (intersection == null)
            {
                return new List <SpatialBox <TContext> > {
                           instance
                }
            }
            ;

            var result = new List <SpatialBox <TContext> >();

            result.AddRange(
                instance.OfferInterval.Difference(intersection.OfferInterval, (x, i) => x.AddDays(i))
                .Select(x => instance.Copy(offerInterval: x)).ToList());
            result.AddRange(
                instance.NightInterval.Difference(intersection.NightInterval, (x, i) => (short)(x + i))
                .Select(x => instance.Copy(offerInterval: intersection.OfferInterval, nightInterval: x)).ToList());
            result.AddRange(
                instance.SeasonInterval.Difference(intersection.SeasonInterval, (x, i) => x.AddDays(i))
                .Select(x => instance.Copy(offerInterval: intersection.OfferInterval, nightInterval: intersection.NightInterval, seasonInterval: x)).ToList());

            return(result);
        }
        /// <summary>
        /// Находит пересечение текущего куба и постороннего куба
        /// </summary>
        public static SpatialBox <TContext> Intersect <TContext>(this SpatialBox <TContext> instance, SpatialBox <TContext> other, Func <TContext, TContext, TContext> combiner)
            where TContext : class
        {
            var offerIntersect  = instance.OfferInterval.Intersect(other.OfferInterval);
            var nightIntersect  = instance.NightInterval.Intersect(other.NightInterval);
            var seasonIntersect = instance.SeasonInterval.Intersect(other.SeasonInterval);

            if (offerIntersect == null || nightIntersect == null || seasonIntersect == null)
            {
                return(null);
            }

            return(new SpatialBox <TContext>(offerIntersect, nightIntersect, seasonIntersect, combiner(instance.Context, other.Context)));
        }
 /// <summary>
 /// Находит остатки от пересечения текущего списка непересекающихся кубов с посторонним кубом
 /// </summary>
 public static List <SpatialBox <TContext> > Difference <TContext>(this List <SpatialBox <TContext> > instances, SpatialBox <TContext> other)
     where TContext : class
 {
     return(instances.SelectMany(x => x.Difference(other)).Where(x => x != null).ToList());
 }
 /// <summary>
 /// Находит пересечение текущего куба со списком посторонних различающихся кубов
 /// </summary>
 public static List <SpatialBox <TContext> > Intersect <TContext>(this SpatialBox <TContext> instance, List <SpatialBox <TContext> > others, Func <TContext, TContext, TContext> combiner)
     where TContext : class
 {
     return(others.Intersect(instance, combiner));
 }
 /// <summary>
 /// Находит пересечение текущего списка различающихся кубов с посторонним кубом
 /// </summary>
 public static List <SpatialBox <TContext> > Intersect <TContext>(this List <SpatialBox <TContext> > instances, SpatialBox <TContext> other, Func <TContext, TContext, TContext> combiner)
     where TContext : class
 {
     return(instances.Select(x => x.Intersect(other, combiner)).Where(x => x != null).ToList());
 }
        /// <summary>
        /// Объединяет непересекающиеся параллелепипеды
        /// </summary>
        public static List <SpatialBox <TContext> > Join <TContext>(this List <SpatialBox <TContext> > instances, SpatialBox <TContext> other, Func <TContext, TContext, TContext> combiner)
            where TContext : class
        {
            var differences   = instances.Difference(other);
            var intersections = instances.Intersect(other, combiner);

            var otherParts = new List <SpatialBox <TContext> > {
                other
            };

            foreach (var intersection in intersections)
            {
                otherParts = otherParts.Difference(intersection);
            }

            var result = differences.Concat(intersections).Concat(otherParts).ToList();

            return(result);
        }
 /// <summary>
 /// Находит остатки от пересечения текущего кубоа с посторонним списком непересекающихся кубов
 /// </summary>
 public static List <SpatialBox <TContext> > Difference <TContext>(this SpatialBox <TContext> instance, List <SpatialBox <TContext> > others)
     where TContext : class
 {
     return(others.Aggregate(instance.ToList(), (x, y) => x.Difference(y)).ToList());
 }