233 lines
5.8 KiB
Plaintext
233 lines
5.8 KiB
Plaintext
|
{
|
||
|
Solutions to the Advent Of Code.
|
||
|
Copyright (C) 2023 Stefan Müller
|
||
|
|
||
|
This program is free software: you can redistribute it and/or modify it under
|
||
|
the terms of the GNU General Public License as published by the Free Software
|
||
|
Foundation, either version 3 of the License, or (at your option) any later
|
||
|
version.
|
||
|
|
||
|
This program is distributed in the hope that it will be useful, but WITHOUT
|
||
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||
|
|
||
|
You should have received a copy of the GNU General Public License along with
|
||
|
this program. If not, see <http://www.gnu.org/licenses/>.
|
||
|
}
|
||
|
|
||
|
unit UIntervals;
|
||
|
|
||
|
{$mode ObjFPC}{$H+}
|
||
|
{$modeswitch AdvancedRecords+}
|
||
|
|
||
|
interface
|
||
|
|
||
|
uses
|
||
|
Classes, SysUtils, Generics.Collections, Math;
|
||
|
|
||
|
type
|
||
|
|
||
|
{ TInterval }
|
||
|
|
||
|
TInterval = record
|
||
|
Start, Length: Cardinal;
|
||
|
function GetLast: Cardinal;
|
||
|
end;
|
||
|
|
||
|
TIntervals = specialize TList<TInterval>;
|
||
|
|
||
|
{ TIntervalSet }
|
||
|
|
||
|
TIntervalSet = class
|
||
|
private
|
||
|
FIntervals: TIntervals;
|
||
|
function GetCount: Cardinal;
|
||
|
procedure Intersect(const AInterval1, AInterval2: TInterval; out OIntersection: TInterval);
|
||
|
procedure Difference(const AInterval1, AInterval2: TInterval; out OBefore, OAfter: TInterval);
|
||
|
public
|
||
|
property Count: Cardinal read GetCount;
|
||
|
constructor Create;
|
||
|
constructor Create(constref AInterval: TInterval);
|
||
|
constructor Create(const AIntervals: TIntervals);
|
||
|
constructor Create(const AStart, ALength: Cardinal);
|
||
|
destructor Destroy; override;
|
||
|
function Contains(const AValue: Cardinal): Boolean;
|
||
|
function IsEmpty: Boolean;
|
||
|
function Intersect(const AOther: TIntervalSet): TIntervalSet;
|
||
|
function Difference(const AOther: TIntervalSet): TIntervalSet;
|
||
|
function Clone: TIntervalSet;
|
||
|
end;
|
||
|
|
||
|
implementation
|
||
|
|
||
|
{ TInterval }
|
||
|
|
||
|
function TInterval.GetLast: Cardinal;
|
||
|
begin
|
||
|
if Start <= Cardinal.MaxValue - Length + 1 then
|
||
|
Result := Start + Length - 1
|
||
|
else
|
||
|
Result := Cardinal.MaxValue;
|
||
|
end;
|
||
|
|
||
|
{ TIntervalSet }
|
||
|
|
||
|
function TIntervalSet.GetCount: Cardinal;
|
||
|
var
|
||
|
interval: TInterval;
|
||
|
begin
|
||
|
Result := 0;
|
||
|
for interval in FIntervals do
|
||
|
Inc(Result, interval.Length);
|
||
|
end;
|
||
|
|
||
|
procedure TIntervalSet.Intersect(const AInterval1, AInterval2: TInterval; out OIntersection: TInterval);
|
||
|
var
|
||
|
minLast: Cardinal;
|
||
|
begin
|
||
|
minLast := Min(AInterval1.GetLast, AInterval2.GetLast);
|
||
|
|
||
|
if minLast >= Max(AInterval1.Start, AInterval2.Start) then
|
||
|
begin
|
||
|
OIntersection.Start := Max(AInterval1.Start, AInterval2.Start);
|
||
|
OIntersection.Length := minLast - OIntersection.Start + 1;
|
||
|
end
|
||
|
else begin
|
||
|
OIntersection.Start := 0;
|
||
|
OIntersection.Length := 0;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
// Returns in OBefore the part of AInterval1 that lies before AInterval2, and in OAfter the part of AInterval1 that lies
|
||
|
// after AInterval2. Either could be empty. OBefore + OAfter would be equivalent to AInterval1 - AInterval2.
|
||
|
procedure TIntervalSet.Difference(const AInterval1, AInterval2: TInterval; out OBefore, OAfter: TInterval);
|
||
|
var
|
||
|
last1, last2: Cardinal;
|
||
|
begin
|
||
|
if AInterval2.Start > AInterval1.Start then
|
||
|
begin
|
||
|
OBefore.Start := AInterval1.Start;
|
||
|
OBefore.Length := Min(AInterval2.Start - AInterval1.Start, AInterval1.Length);
|
||
|
end
|
||
|
else begin
|
||
|
OBefore.Start := 0;
|
||
|
OBefore.Length := 0;
|
||
|
end;
|
||
|
|
||
|
last1 := AInterval1.GetLast;
|
||
|
last2 := AInterval2.GetLast;
|
||
|
if last1 > last2 then
|
||
|
begin
|
||
|
OAfter.Start := Max(last2 + 1, AInterval1.Start);
|
||
|
OAfter.Length := Min(last1 - last2, AInterval1.Length);
|
||
|
end
|
||
|
else begin
|
||
|
OAfter.Start := 0;
|
||
|
OAfter.Length := 0;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
constructor TIntervalSet.Create;
|
||
|
begin
|
||
|
FIntervals := TIntervals.Create;
|
||
|
end;
|
||
|
|
||
|
constructor TIntervalSet.Create(constref AInterval: TInterval);
|
||
|
begin
|
||
|
Create;
|
||
|
if AInterval.Length > 0 then
|
||
|
FIntervals.Add(AInterval);
|
||
|
end;
|
||
|
|
||
|
constructor TIntervalSet.Create(const AIntervals: TIntervals);
|
||
|
begin
|
||
|
Create;
|
||
|
FIntervals.AddRange(AIntervals);
|
||
|
end;
|
||
|
|
||
|
constructor TIntervalSet.Create(const AStart, ALength: Cardinal);
|
||
|
var
|
||
|
interval: TInterval;
|
||
|
begin
|
||
|
interval.Start := AStart;
|
||
|
interval.Length := ALength;
|
||
|
Create(interval);
|
||
|
end;
|
||
|
|
||
|
destructor TIntervalSet.Destroy;
|
||
|
begin
|
||
|
FIntervals.Free;
|
||
|
inherited Destroy;
|
||
|
end;
|
||
|
|
||
|
function TIntervalSet.Contains(const AValue: Cardinal): Boolean;
|
||
|
var
|
||
|
interval: TInterval;
|
||
|
begin
|
||
|
Result := False;
|
||
|
for interval in FIntervals do
|
||
|
if (interval.Start <= AValue) and (AValue <= interval.GetLast) then
|
||
|
begin
|
||
|
Result := True;
|
||
|
Exit;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
function TIntervalSet.IsEmpty: Boolean;
|
||
|
begin
|
||
|
Result := FIntervals.Count = 0;
|
||
|
end;
|
||
|
|
||
|
function TIntervalSet.Intersect(const AOther: TIntervalSet): TIntervalSet;
|
||
|
var
|
||
|
interval1, interval2, intersection: TInterval;
|
||
|
begin
|
||
|
Result := TIntervalSet.Create;
|
||
|
for interval2 in AOther.FIntervals do
|
||
|
for interval1 in FIntervals do
|
||
|
begin
|
||
|
Intersect(interval1, interval2, intersection);
|
||
|
if intersection.Length > 0 then
|
||
|
Result.FIntervals.Add(intersection);
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
function TIntervalSet.Difference(const AOther: TIntervalSet): TIntervalSet;
|
||
|
var
|
||
|
interval1, interval2, before, after: TInterval;
|
||
|
current, next, temp: TIntervals;
|
||
|
begin
|
||
|
current := TIntervals.Create(FIntervals);
|
||
|
next := TIntervals.Create;
|
||
|
for interval2 in AOther.FIntervals do
|
||
|
begin
|
||
|
for interval1 in current do
|
||
|
begin
|
||
|
Difference(interval1, interval2, before, after);
|
||
|
if before.Length > 0 then
|
||
|
next.Add(before);
|
||
|
if after.Length > 0 then
|
||
|
next.Add(after);
|
||
|
end;
|
||
|
current.Clear;
|
||
|
temp := current;
|
||
|
current := next;
|
||
|
next := temp;
|
||
|
end;
|
||
|
Result := TIntervalSet.Create(current);
|
||
|
current.Free;
|
||
|
next.Free;
|
||
|
end;
|
||
|
|
||
|
function TIntervalSet.Clone: TIntervalSet;
|
||
|
var
|
||
|
interval: TInterval;
|
||
|
begin
|
||
|
Result := TIntervalSet.Create;
|
||
|
for interval in FIntervals do
|
||
|
Result.FIntervals.Add(interval);
|
||
|
end;
|
||
|
|
||
|
end.
|
||
|
|