using System; using System.Collections.Generic; using System.Linq; using Medallion.Collections; namespace TMI_practicum { public static class SweepLineEffAlgorithm { public static IEnumerable Solve(IEnumerable circles) { var intersections = new Stack(); var events = new PriorityQueue(); var upperActive = new CircleTree(); var bottomActive = new CircleTree(); foreach (var circle in circles) { events.Enqueue(new Event(Event.EventType.Start, circle.X - circle.R, circle)); events.Enqueue(new Event(Event.EventType.End, circle.X + circle.R, circle)); } while (events.Count != 0) { Event e = events.Dequeue(); int hash = e.GetHashCode(); switch (e.Type) { case Event.EventType.Start: upperActive.Add(new SegmentKey(e.X, e.Circle.Y + e.Circle.R, SegmentKey.SegmentType.Upper, hash), e.Circle); bottomActive.Add(new SegmentKey(e.X, e.Circle.Y - e.Circle.R, SegmentKey.SegmentType.Bottom, hash), e.Circle); CheckIntersections(upperActive, e, intersections, true); CheckIntersections(bottomActive, e, intersections, false); break; case Event.EventType.End: upperActive.Remove(new SegmentKey(e.X, e.Circle.Y + e.Circle.R, SegmentKey.SegmentType.Upper, hash)); bottomActive.Remove(new SegmentKey(e.X, e.Circle.Y - e.Circle.R, SegmentKey.SegmentType.Bottom, hash)); break; default: throw new ArgumentOutOfRangeException(); } } return intersections; } private static void CheckIntersections(CircleTree active, Event e, Stack intersections, bool upper) { Circle other = null; Circle other2 = null; try { other = upper? active.FindSuccessor(new SegmentKey(e.X, e.Circle.Y, SegmentKey.SegmentType.Upper, e.GetHashCode())) : active.FindPredecessor(new SegmentKey(e.X, e.Circle.Y, SegmentKey.SegmentType.Bottom, e.GetHashCode())); other2 = upper? active.FindPredecessor(new SegmentKey(e.X, e.Circle.Y, SegmentKey.SegmentType.Upper, e.GetHashCode())) : active.FindSuccessor(new SegmentKey(e.X, e.Circle.Y, SegmentKey.SegmentType.Bottom, e.GetHashCode())); } catch (KeyNotFoundException) { } if (other != null) { var intersects = e.Circle.FindIntersections(other); if (intersects != null) { foreach (var intersection in intersects) { if (upper && e.Circle.Y == intersection.Y) continue; intersections.Push(intersection); } } } } private class CircleTree : SortedDictionary { public TValue FindSuccessor(TKey key) { var other = this.Keys.Where(s => this.Comparer.Compare(s, key) > 0).MinOrDefault(); return this[other]; } public TValue FindPredecessor(TKey key) { var other = this.Keys.Where(s => this.Comparer.Compare(s, key) < 0).MaxOrDefault(); return this[other]; } } private struct Event : IComparable { public EventType Type { get; } public Circle Circle { get; } public double X { get; } public enum EventType { Start, End } public Event(EventType type, double x, Circle circle) { Type = type; X = x; Circle = circle; } public int CompareTo(Event other) { return X.CompareTo(other.X); } } private struct SegmentKey : IComparable { private readonly double _x; private readonly double? _y; public double Y => _y ?? double.MinValue; private readonly SegmentType _type; private readonly int _identifier; public SegmentKey(double x, double y, SegmentType type, int identifier) { _y = y; _type = type; _identifier = identifier; _x = x; } public int CompareTo(SegmentKey other) { if (_identifier.Equals(other._identifier)) return _type.CompareTo(other._type); if (!_y.Equals(other._y)) return Y.CompareTo(other.Y); return _type.Equals(other._type) ? _x.CompareTo(other._x) : _type.CompareTo(other._type); } public override bool Equals(object obj) { if (obj == null || GetType() != obj.GetType()) return false; SegmentKey r = (SegmentKey) obj; return _identifier.Equals(r._identifier) && _type.Equals(r._type); } public override int GetHashCode() { return _identifier.GetHashCode() ^ _type.GetHashCode(); } public enum SegmentType { Bottom, Upper } } } public static class EnumerableExtensions { public static T MinOrDefault(this IEnumerable sequence) { return sequence.Any() ? sequence.Min() : default(T); } public static T MaxOrDefault(this IEnumerable sequence) { return sequence.Any() ? sequence.Max() : default(T); } } }