program RangeCombo;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils, System.Math, Generics.Defaults, Generics.Collections;
type
Func<TResult> = reference to function(): TResult;
Func<T, TResult> = reference to function(const Arg1: T): TResult;
Func<T1, T2, TResult> = reference to function(const Arg1: T1; const Arg2: T2): TResult;
// various algorithms
Alg = record
class function Aggregate<T, TAccumulate>(const Input: TArray<T>; const F: Func<TAccumulate, T, TAccumulate>): TAccumulate; overload; static;
class function Aggregate<T, TAccumulate>(const Input: TArray<T>; const InitialValue: TAccumulate; const F: Func<TAccumulate, T, TAccumulate>): TAccumulate; overload; static;
class function Sort<T>(const Input: TArray<T>): TArray<T>; overload; static;
end;
{ Alg }
class function Alg.Aggregate<T, TAccumulate>(const Input: TArray<T>;
const F: Func<TAccumulate, T, TAccumulate>): TAccumulate;
begin
result := Aggregate<T, TAccumulate>(Input, Default(TAccumulate), F);
end;
class function Alg.Aggregate<T, TAccumulate>(const Input: TArray<T>;
const InitialValue: TAccumulate;
const F: Func<TAccumulate, T, TAccumulate>): TAccumulate;
var
elm: T;
begin
result := InitialValue;
for elm in Input do
begin
result := F(result, elm);
end;
end;
class function Alg.Sort<T>(const Input: TArray<T>): TArray<T>;
begin
result := Copy(Input);
TArray.Sort<T>(result, TComparer<T>.Default);
end;
type
Range = record
First: integer;
Last: integer;
class operator Implicit(const Value: integer): Range;
end;
function IsSingularRange(const R: Range): boolean;
begin
result := (R.First = R.Last);
end;
{ Range }
class operator Range.Implicit(const Value: integer): Range;
begin
result.First := Value;
result.Last := Value;
end;
function CombineRanges(const Input: TArray<integer>): TArray<Range>;
var
sortedInput: TArray<integer>;
begin
sortedInput := Alg.Sort<integer>(Input);
result := Alg.Aggregate<integer, TArray<Range>>(sortedInput,
function(const Accumulator: TArray<Range>; const Value: integer): TArray<Range>
var
r: Range;
begin
if (Length(Accumulator) = 0) then
begin
result := [Value];
exit;
end;
r := Accumulator[High(Accumulator)];
if (Value > (r.Last + 1)) then
begin
// need a new range
result := Accumulator + [Value];
end
else
begin
// extend last range
result := Accumulator;
result[High(result)].Last := System.Math.Max(r.Last, Value);
end
end
);
end;
function ExpandRanges(const Input: TArray<Range>): TArray<integer>;
begin
result := Alg.Aggregate<Range, TArray<integer>>(Input,
function(const Accumulator: TArray<integer>; const Value: Range): TArray<integer>
var
i: integer;
begin
result := Accumulator;
for i := Value.First to Value.Last do
begin
result := result + [i];
end;
end
);
end;
var
input: TArray<integer>;
ranges: TArray<Range>;
r: Range;
values: TArray<integer>;
i: integer;
begin
input := [1,2,3,4,6,7,8,13,14];
try
ranges := CombineRanges(input);
for r in ranges do
begin
if (IsSingularRange(r)) then
WriteLn(r.First)
else
WriteLn(r.First, '-', r.Last);
end;
WriteLn;
WriteLn;
values := ExpandRanges(ranges);
for i in values do
begin
WriteLn(i);
end;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
WriteLn('done');
ReadLn;
end.