Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit5acf299

Browse files
committed
Merge branch 'feature/56-add-integer-range-adv-record' into develop
Fixes#56
2 parents5eb4162 +36f7128 commit5acf299

File tree

9 files changed

+1629
-8
lines changed

9 files changed

+1629
-8
lines changed

‎collection/706.dat‎

Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
type
2+
TIntegerRange = record
3+
strict private
4+
var
5+
fLowerBound: Integer;
6+
fUpperBound: Integer;
7+
function GetLowerBound: Integer;
8+
function GetUpperBound: Integer;
9+
function IsSubrangeOf(const ARange: TIntegerRange): Boolean;
10+
public
11+
// Constructs a range whose bounds are A and B. The lowest of the two
12+
// parameters is taken as the lower bound of the range with the other
13+
// parameter taken as the upper bound.
14+
// Valid bounds must fall in the range -MaxInt..MaxInt. An
15+
// EArgumentException exception is raised otherwise.
16+
constructor Create(const A, B: Integer);
17+
18+
// Constructs an empty range.
19+
class function CreateEmpty: TIntegerRange; static;
20+
21+
// Checks if the range is empty.
22+
function IsEmpty: Boolean;
23+
24+
// Returns the length of the range, i.e. the number of integers in the range.
25+
function Length: Cardinal;
26+
27+
// Constrains AValue to fall within this range. If the value lies within the
28+
// range it is returned unchanged. If it is outside the range then either
29+
// LowerBound or UpperBound is returned, depending on whether the value
30+
// falls below or above the range, respectively.
31+
// An EInvalidOpException exception is raised if the range is empty.
32+
function Constrain(const AValue: Integer): Integer;
33+
34+
// Checks if this range overlaps with ARange, i.e. the interection of the
35+
// ranges is non empty. Empty ranges cannot overlap with any range.
36+
function OverlapsWith(const ARange: TIntegerRange): Boolean;
37+
38+
// Checks if this range is immediately adjacent to ARange, with no overlap.
39+
// Empty ranges are never contiguous with other ranges or themselves.
40+
function IsContiguousWith(const ARange: TIntegerRange): Boolean;
41+
42+
// Checks if the set of all values in this range and ARange form a
43+
// continuous sequence. This implies that a range is continuous with itself.
44+
// Since adding an empty range to a non-empty range doesn't change the
45+
// non-empty range we define empty ranges to be continuous with any range.
46+
function IsContinuousWith(const ARange: TIntegerRange): Boolean;
47+
48+
// Checks if ranges A and B are the same
49+
class operator Equal(const A, B: TIntegerRange): Boolean;
50+
51+
// Checks if ranges A and B are not the same
52+
class operator NotEqual(const A, B: TIntegerRange): Boolean;
53+
54+
// Checks if range A is contained in, or is the same as, range B.
55+
// An empty range is deemed to be contained in any other range.
56+
class operator LessThanOrEqual(const A, B: TIntegerRange): Boolean;
57+
58+
// Checks if range A is contains, or is the same as, range B.
59+
// A non-empty range is never contained in an empty range.
60+
class operator GreaterThanOrEqual(const A, B: TIntegerRange): Boolean;
61+
62+
// Combine two ranges, A and B. The result is the smallest range that
63+
// contains both A and B.
64+
// If A and B are not continuous the resulting range will contain values
65+
// that were not in either A or B.
66+
// Combining any range either with itself or with an empty range is a no-op.
67+
class operator Add(const A, B: TIntegerRange): TIntegerRange;
68+
69+
// Returns a range that is the intersection of ranges A and B.
70+
// Returns an empty range if A and B do not overlap.
71+
class operator Multiply(const A, B: TIntegerRange): TIntegerRange;
72+
73+
// Checks if integer AValue is contained within range ARange.
74+
class operator In(const AValue: Integer; const ARange: TIntegerRange):
75+
Boolean;
76+
77+
// Implicitly casts ARange to a string. If ARange is non-empty the string
78+
// has format [X..Y], where X and Y are the lower and upper bounds of
79+
// ARange respectively. If ARange is empty then [] is returned.
80+
// This means that ARange can be assigned directly to a string.
81+
class operator Implicit(const ARange: TIntegerRange): string;
82+
83+
// Explicitly casts ARange to a string. If ARange is non-empty the string
84+
// has format [X..Y], where X and Y are the lower and upper bounds of
85+
// ARange respectively. If ARange is empty then [] is returned.
86+
// This means that ARange can be explicitly cast to a string using
87+
// string(ARange).
88+
class operator Explicit(const ARange: TIntegerRange): string;
89+
90+
// The lower bound of a non-empty range.
91+
// EInvalidOpException is raised if the property is read when the range is
92+
// empty.
93+
property LowerBound: Integer read GetLowerBound;
94+
95+
// The upper bound of a non-empty range.
96+
// EInvalidOpException is raised if the property is read when the range is
97+
// empty.
98+
property UpperBound: Integer read GetUpperBound;
99+
end;
100+
101+
class operator TIntegerRange.Add(const A, B: TIntegerRange): TIntegerRange;
102+
begin
103+
if A.IsEmpty then
104+
Exit(B);
105+
if B.IsEmpty then
106+
Exit(A);
107+
Result := TIntegerRange.Create(
108+
Math.Min(A.fLowerBound, B.fLowerBound),
109+
Math.Max(A.fUpperBound, B.fUpperBound)
110+
);
111+
end;
112+
113+
function TIntegerRange.Constrain(const AValue: Integer): Integer;
114+
begin
115+
if IsEmpty then
116+
raise Sysutils.EInvalidOpException.Create(
117+
'TIntegerRange.Constrain not valid for an empty range.'
118+
);
119+
Result := Math.EnsureRange(AValue, fLowerBound, fUpperBound);
120+
end;
121+
122+
constructor TIntegerRange.Create(const A, B: Integer);
123+
begin
124+
// Normalise range so that smallest parameter is the lower bound
125+
fLowerBound := Math.Min(A, B);
126+
fUpperBound := Math.Max(A, B);
127+
if fLowerBound = Low(Integer) then
128+
// This restriction is required to prevent the Length method's Cardinal
129+
// return value from wrapping around / overflowing
130+
raise SysUtils.EArgumentException.CreateFmt(
131+
'TIntegerRange.Create: Arguments must be greater than %d', [Low(Integer)]
132+
);
133+
end;
134+
135+
class function TIntegerRange.CreateEmpty: TIntegerRange;
136+
begin
137+
Result.fLowerBound := High(Integer);
138+
Result.fUpperBound := Low(Integer);
139+
end;
140+
141+
class operator TIntegerRange.Equal(const A, B: TIntegerRange): Boolean;
142+
begin
143+
if A.IsEmpty or B.IsEmpty then
144+
Exit(A.IsEmpty and B.IsEmpty);
145+
Result := (A.fLowerBound = B.fLowerBound) and (A.fUpperBound = B.fUpperBound);
146+
end;
147+
148+
class operator TIntegerRange.Explicit(const ARange: TIntegerRange): string;
149+
begin
150+
if ARange.IsEmpty then
151+
Exit('[]');
152+
Result := SysUtils.Format(
153+
'[%d..%d]', [ARange.fLowerBound, ARange.fUpperBound]
154+
);
155+
end;
156+
157+
function TIntegerRange.GetLowerBound: Integer;
158+
begin
159+
if IsEmpty then
160+
raise Sysutils.EInvalidOpException.Create(
161+
'TIntegerRange.LowerBound not valid for an empty range.'
162+
);
163+
Result := fLowerBound;
164+
end;
165+
166+
function TIntegerRange.GetUpperBound: Integer;
167+
begin
168+
if IsEmpty then
169+
raise Sysutils.EInvalidOpException.Create(
170+
'TIntegerRange.LowerBound not valid for an empty range.'
171+
);
172+
Result := fUpperBound;
173+
end;
174+
175+
class operator TIntegerRange.GreaterThanOrEqual(const A, B: TIntegerRange):
176+
Boolean;
177+
begin
178+
Result := B.IsSubrangeOf(A);
179+
end;
180+
181+
class operator TIntegerRange.Implicit(const ARange: TIntegerRange): string;
182+
begin
183+
Result := string(ARange); // calls Explicit cast operator
184+
end;
185+
186+
class operator TIntegerRange.In(const AValue: Integer;
187+
const ARange: TIntegerRange): Boolean;
188+
begin
189+
if ARange.IsEmpty then
190+
Exit(False);
191+
Result := (AValue >= ARange.fLowerBound) and (AValue <= ARange.fUpperBound);
192+
end;
193+
194+
function TIntegerRange.IsContiguousWith(const ARange: TIntegerRange): Boolean;
195+
begin
196+
if Self.IsEmpty or ARange.IsEmpty then
197+
Exit(False);
198+
Result := (Self + ARange).Length = (Self.Length + ARange.Length);
199+
end;
200+
201+
function TIntegerRange.IsContinuousWith(const ARange: TIntegerRange): Boolean;
202+
begin
203+
if Self.IsEmpty or ARange.IsEmpty then
204+
// Empty ranges are only continuous with other empty ranges
205+
Exit(True);
206+
Result := IsContiguousWith(ARange) or OverlapsWith(ARange);
207+
end;
208+
209+
function TIntegerRange.IsEmpty: Boolean;
210+
begin
211+
Result := fLowerBound > fUpperBound;
212+
end;
213+
214+
function TIntegerRange.IsSubrangeOf(const ARange: TIntegerRange): Boolean;
215+
begin
216+
if ARange.IsEmpty then
217+
Exit(Self.IsEmpty);
218+
Result := (Self.fLowerBound >= ARange.fLowerBound)
219+
and (Self.fUpperBound <= ARange.fUpperBound)
220+
or Self.IsEmpty
221+
end;
222+
223+
function TIntegerRange.Length: Cardinal;
224+
begin
225+
if IsEmpty then
226+
Exit(0);
227+
Result := fUpperBound - fLowerBound + 1
228+
end;
229+
230+
class operator TIntegerRange.LessThanOrEqual(const A, B: TIntegerRange):
231+
Boolean;
232+
begin
233+
Result := A.IsSubrangeOf(B);
234+
end;
235+
236+
class operator TIntegerRange.Multiply(const A, B: TIntegerRange): TIntegerRange;
237+
var
238+
Up, Lo: Integer;
239+
begin
240+
if A.IsEmpty or B.IsEmpty then
241+
Exit(TIntegerRange.CreateEmpty);
242+
Lo := Math.Max(A.fLowerBound, B.fLowerBound);
243+
Up := Math.Min(A.fUpperBound, B.fUpperBound);
244+
if Lo <= Up then
245+
Result := TIntegerRange.Create(Lo, Up)
246+
else
247+
Result := TIntegerRange.CreateEmpty;
248+
end;
249+
250+
class operator TIntegerRange.NotEqual(const A, B: TIntegerRange): Boolean;
251+
begin
252+
if A.IsEmpty or B.IsEmpty then
253+
Exit(A.IsEmpty <> B.IsEmpty);
254+
Result := (A.fLowerBound <> B.fLowerBound)
255+
or (A.fUpperBound <> B.fUpperBound);
256+
end;
257+
258+
function TIntegerRange.OverlapsWith(const ARange: TIntegerRange): Boolean;
259+
begin
260+
Result := not (Self * ARange).IsEmpty;
261+
end;

‎collection/structs.ini‎

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ FPC=Y
5757
DescEx="<p>Constructs and returns a <var>TRange</var> record with bounds <var>A</var> and <var>B</var>.</p><p>The smaller of <var>A</var> and <var>B</var> is used as the lower bound and the larger as the upper bound. If both values are equal then the range will be empty.</p>"
5858
Depends=TRange
5959
SeeAlso=TRange
60-
SeeAlso=TRange,TRangeEx
60+
SeeAlso=TRange,TRangeEx,TIntegerRange
6161
Snip=580.dat
6262
Delphi7=Y
6363
Delphi2005Win32=Y
@@ -122,6 +122,20 @@ Delphi10S=Y
122122
Delphi12A=Y
123123
FPC=Y
124124

125+
[TIntegerRange]
126+
DisplayName=TIntegerRange
127+
DescEx="<p>An advanced record that encapsulates an integer range along with operations on it.</p><p>The range is immutable, so the constructor must be used to instantiate a non-empty range.</p>"
128+
Extra="<p><var>TIntegerRange</var> supports various operator overloads, which operate in a way similar to the Pascal <strong>set</strong> operators:</p><p><mono>=</mono> compares two ranges for equality.</p><p><mono>&lt;&gt;</mono> compares two ranges for inequality.</p><p><mono>&lt;=</mono> checks if the left operand is wholly contained in, or is equal to the right operand (c.f. Pascal subset operator).</p><p><mono>&gt;=</mono> checks if the left operand wholly contains, or is equal to, the right hand operand (c.f. Pascal proper superset operator).</p><p><mono>+</mono> creates the union of the two ranges specified by the operands, i.e. a new range that is the smallest range that contains both operands. For example, representing a range as <mono>[A..B]</mono>, <mono>[1..3] + [7..10] = [1..10]</mono> (c.f. Pascal union operator).</p><p><mono>*</mono> creates a new range that the largest range contained in both ranges specified by the operands, or an empty range if the operands do not overlap. For example, <mono>[1..3] * [2..7] = [2..3]</mono> while <mono>[1..3] * [6..9] is an empty range.</mono> (c.f. Pascal intersection operator).</p><p><strong><mono>in</mono></strong> checks if the integer specified in the left hand operand is contained in the range specified by the right hand operand (c.f. Pascal membership operator).</p>"
129+
Kind=class
130+
Units=SysUtils,Math
131+
SeeAlso=TRange,TRangeEx
132+
TestInfo=advanced
133+
AdvancedTest.Level=unit-tests
134+
AdvancedTest.URL="https://github.com/delphidabbler/code-snippets/tree/master/tests/Cat-Structs"
135+
Snip=706.dat
136+
DelphiXE=Y
137+
Delphi12A=Y
138+
125139
[TPointF]
126140
Kind=type
127141
DescEx="<p>Encapsulates a point with double precision floating point coordinates.</p>"
@@ -150,7 +164,7 @@ FPC=Y
150164
Kind=type
151165
DescEx="<p>Encapsulates the upper and lower bounds of a range of values.</p>"
152166
SeeAlso=Range
153-
SeeAlso=Range,TRangeEx
167+
SeeAlso=Range,TRangeEx,TIntegerRange
154168
Snip=579.dat
155169
Delphi7=Y
156170
Delphi2005Win32=Y
@@ -195,7 +209,7 @@ FPC=Y
195209
Kind=class
196210
DescEx="<p>Encapsulates a range of integers with a methods to test whether a value falls within the range and to adjust the value to fit.</p>"
197211
Units=Math
198-
SeeAlso=Range,TRange
212+
SeeAlso=Range,TRange,TIntegerRange
199213
Snip=578.dat
200214
Delphi2=N
201215
Delphi3=N
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
program TestCatStructsXE;
2+
{
3+
4+
Delphi DUnit Test Project
5+
-------------------------
6+
This project contains the DUnit test framework and the GUI/Console test runners.
7+
Add "CONSOLE_TESTRUNNER" to the conditional defines entry in the project options
8+
to use the console test runner. Otherwise the GUI test runner will be used by
9+
default.
10+
11+
}
12+
13+
{$IFDEF CONSOLE_TESTRUNNER}
14+
{$APPTYPE CONSOLE}
15+
{$ENDIF}
16+
17+
uses
18+
Forms,
19+
TestFramework,
20+
GUITestRunner,
21+
TextTestRunner,
22+
TestUStructCatSnippetsin'TestUStructCatSnippets.pas',
23+
UStructCatSnippetsin'UStructCatSnippets.pas';
24+
25+
{$R *.RES}
26+
27+
begin
28+
Application.Initialize;
29+
if IsConsolethen
30+
with TextTestRunner.RunRegisteredTestsdo
31+
Free
32+
else
33+
GUITestRunner.RunRegisteredTests;
34+
end.
35+

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp