• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

HicServices / RDMP / 7102803153

05 Dec 2023 03:19PM UTC coverage: 56.338% (-0.4%) from 56.745%
7102803153

push

github

web-flow
Bump NUnit from 3.14.0 to 4.0.0 (#1686)

10562 of 20305 branches covered (0.0%)

Branch coverage included in aggregate %.

64 of 96 new or added lines in 21 files covered. (66.67%)

161 existing lines in 17 files now uncovered.

30382 of 52370 relevant lines covered (58.01%)

7337.5 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/Rdmp.Core/ReusableLibraryCode/Diff.cs
1
// Copyright (c) The University of Dundee 2018-2019
2
// This file is part of the Research Data Management Platform (RDMP).
3
// RDMP 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.
4
// RDMP 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.
5
// You should have received a copy of the GNU General Public License along with RDMP. If not, see <https://www.gnu.org/licenses/>.
6

7
using System;
8
using System.Collections;
9
using System.Text.RegularExpressions;
10

11
namespace Rdmp.Core.ReusableLibraryCode;
12

13
/// <summary>
14
/// This Class implements the Difference Algorithm published in
15
/// "An O(ND) Difference Algorithm and its Variations" by Eugene Myers
16
/// Algorithmica Vol. 1 No. 2, 1986, p 251.
17
/// 
18
/// <para>There are many C, Java, Lisp implementations public available but they all seem to come
19
/// from the same source (diffutils) that is under the (unfree) GNU public License
20
/// and cannot be reused as a sourcecode for a commercial application.
21
/// There are very old C implementations that use other (worse) algorithms.
22
/// Microsoft also published sourcecode of a diff-tool (windiff) that uses some tree data.
23
/// Also, a direct transfer from a C source to C# is not easy because there is a lot of pointer
24
/// arithmetic in the typical C solutions and i need a managed solution.
25
/// These are the reasons why I implemented the original published algorithm from the scratch and
26
/// make it available without the GNU license limitations.
27
/// I do not need a high performance diff tool because it is used only sometimes.
28
/// I will do some performance tweaking when needed.</para>
29
/// 
30
/// <para>The algorithm itself is comparing 2 arrays of numbers so when comparing 2 text documents
31
/// each line is converted into a (hash) number. See DiffText(). </para>
32
/// 
33
/// <para>Some changes to the original algorithm:
34
/// The original algorithm was described using a recursive approach and comparing zero indexed arrays.
35
/// Extracting sub-arrays and rejoining them is very performance and memory intensive so the same
36
/// (readonly) data arrays are passed around together with their lower and upper bounds.
37
/// This circumstance makes the LCS and SMS functions more complicate.
38
/// I added some code to the LCS function to get a fast response on sub-arrays that are identical,
39
/// completely deleted or inserted.</para>
40
/// 
41
/// <para>The result from a comparison is stored in 2 arrays that flag for modified (deleted or inserted)
42
/// lines in the 2 data arrays. These bits are then analysed to produce a array of Item objects.</para>
43
/// 
44
/// <para>Further possible optimizations:
45
/// (first rule: don't do it; second: don't do it yet)
46
/// The arrays DataA and DataB are passed as parameters, but are never changed after the creation
47
/// so they can be members of the class to avoid the parameter overhead.
48
/// In SMS is a lot of boundary arithmetic in the for-D and for-k loops that can be done by increment
49
/// and decrement of local variables.
50
/// The DownVector and UpVector arrays are always created and destroyed each time the SMS gets called.
51
/// It is possible to reuse them when transferring them to members of the class.</para>
52
/// 
53
/// <para>diff.cs: A port of the algorithm to C#
54
/// Created by Matthias Hertel, see http://www.mathertel.de
55
/// This work is licensed under a Creative Commons Attribution 2.0 Germany License.
56
/// see http://creativecommons.org/licenses/by/2.0/de/</para>
57
/// 
58
/// <para>Changes:
59
/// 2002.09.20 There was a "hang" in some situations.
60
/// Now I understand a little bit more of the SMS algorithm.
61
/// There have been overlapping boxes; that where analyzed partial differently.
62
/// One return-point is enough.
63
/// A assertion was added in CreateDiffs when in debug-mode, that counts the number of equal (no modified) lines in both arrays.
64
/// They must be identical.</para>
65
/// 
66
/// <para>2003.02.07 Out of bounds error in the Up/Down vector arrays in some situations.
67
/// The two vectors are now accessed using different offsets that are adjusted using the start k-Line.
68
/// A test case is added. </para>
69
/// 
70
/// <para>2006.03.05 Some documentation and a direct Diff entry point.</para>
71
/// 
72
/// <para>2006.03.08 Refactored the API to static methods on the Diff class to make usage simpler.
73
/// 2006.03.10 using the standard Debug class for self-test now.
74
///            compile with: csc /target:exe /out:diffTest.exe /d:DEBUG /d:TRACE /d:SELFTEST Diff.cs</para>
75
/// </summary>
76
public class Diff
77
{
78
    /// <summary>details of one difference.</summary>
79
    public struct Item
80
    {
81
        /// <summary>Start Line number in Data A.</summary>
82
        public int StartA;
83

84
        /// <summary>Start Line number in Data B.</summary>
85
        public int StartB;
86

87
        /// <summary>Number of changes in Data A.</summary>
88
        public int deletedA;
89

90
        /// <summary>Number of changes in Data A.</summary>
91
        public int insertedB;
92
    } // Item
93

94
    /// <summary>
95
    /// Shortest Middle Snake Return Data
96
    /// </summary>
97
    private struct SMSRD
98
    {
99
        internal int x, y;
100
        // internal int u, v;  // 2002.09.20: no need for 2 points
101
    }
102

103

104
    #region self-Test
105

106
#if (SELFTEST)
107
    /// <summary>
108
    /// start a self- / box-test for some diff cases and report to the debug output.
109
    /// </summary>
110
    /// <param name="args">not used</param>
111
    /// <returns>always 0</returns>
112
    public static int Main(string[] args) {
113
      StringBuilder ret = new StringBuilder();
114
      string a, b;
115

116
      System.Diagnostics.ConsoleTraceListener ctl = new System.Diagnostics.ConsoleTraceListener(false);
117
      System.Diagnostics.Debug.Listeners.Add(ctl);
118

119
      System.Console.WriteLine("Diff Self Test...");
120

121
      // test all changes
122
      a = "a,b,c,d,e,f,g,h,i,j,k,l".Replace(',', '\n');
123
      b = "0,1,2,3,4,5,6,7,8,9".Replace(',', '\n');
124
      System.Diagnostics.Debug.Assert(TestHelper(Diff.DiffText(a, b, false, false, false))
125
        == "12.10.0.0*",
126
        "all-changes test failed.");
127
      System.Diagnostics.Debug.WriteLine("all-changes test passed.");
128
      // test all same
129
      a = "a,b,c,d,e,f,g,h,i,j,k,l".Replace(',', '\n');
130
      b = a;
131
      System.Diagnostics.Debug.Assert(TestHelper(Diff.DiffText(a, b, false, false, false))
132
        == "",
133
        "all-same test failed.");
134
      System.Diagnostics.Debug.WriteLine("all-same test passed.");
135

136
      // test snake
137
      a = "a,b,c,d,e,f".Replace(',', '\n');
138
      b = "b,c,d,e,f,x".Replace(',', '\n');
139
      System.Diagnostics.Debug.Assert(TestHelper(Diff.DiffText(a, b, false, false, false))
140
        == "1.0.0.0*0.1.6.5*",
141
        "snake test failed.");
142
      System.Diagnostics.Debug.WriteLine("snake test passed.");
143

144
      // 2002.09.20 - repro
145
      a = "c1,a,c2,b,c,d,e,g,h,i,j,c3,k,l".Replace(',', '\n');
146
      b = "C1,a,C2,b,c,d,e,I1,e,g,h,i,j,C3,k,I2,l".Replace(',', '\n');
147
      System.Diagnostics.Debug.Assert(TestHelper(Diff.DiffText(a, b, false, false, false))
148
        == "1.1.0.0*1.1.2.2*0.2.7.7*1.1.11.13*0.1.13.15*",
149
        "repro20020920 test failed.");
150
      System.Diagnostics.Debug.WriteLine("repro20020920 test passed.");
151

152
      // 2003.02.07 - repro
153
      a = "F".Replace(',', '\n');
154
      b = "0,F,1,2,3,4,5,6,7".Replace(',', '\n');
155
      System.Diagnostics.Debug.Assert(TestHelper(Diff.DiffText(a, b, false, false, false))
156
        == "0.1.0.0*0.7.1.2*",
157
        "repro20030207 test failed.");
158
      System.Diagnostics.Debug.WriteLine("repro20030207 test passed.");
159

160
      // Muegel - repro
161
      a = "HELLO\nWORLD";
162
      b = "\n\nhello\n\n\n\nworld\n";
163
      System.Diagnostics.Debug.Assert(TestHelper(Diff.DiffText(a, b, false, false, false))
164
        == "2.8.0.0*",
165
        "repro20030409 test failed.");
166
      System.Diagnostics.Debug.WriteLine("repro20030409 test passed.");
167

168
    // test some differences
169
      a = "a,b,-,c,d,e,f,f".Replace(',', '\n');
170
      b = "a,b,x,c,e,f".Replace(',', '\n');
171
      System.Diagnostics.Debug.Assert(TestHelper(Diff.DiffText(a, b, false, false, false))
172
        == "1.1.2.2*1.0.4.4*1.0.6.5*",
173
        "some-changes test failed.");
174
      System.Diagnostics.Debug.WriteLine("some-changes test passed.");
175

176
      System.Diagnostics.Debug.WriteLine("End.");
177
      System.Diagnostics.Debug.Flush();
178

179
      return (0);
180
    }
181

182

183
    public static string TestHelper(Item []f) {
184
      StringBuilder ret = new StringBuilder();
185
      for (int n = 0; n < f.Length; n++) {
186
        ret.Append(f[n].deletedA.ToString() + "." + f[n].insertedB.ToString() + "." + f[n].StartA.ToString() + "." + f[n].StartB.ToString() + "*");
187
      }
188
      // Debug.Write(5, "TestHelper", ret.ToString());
189
      return (ret.ToString());
190
    }
191
#endif
192

193
    #endregion
194

195

196
    /// <summary>
197
    /// Find the difference in 2 texts, comparing by textlines.
198
    /// </summary>
199
    /// <param name="TextA">A-version of the text (usually the old one)</param>
200
    /// <param name="TextB">B-version of the text (usually the new one)</param>
201
    /// <returns>Returns a array of Items that describe the differences.</returns>
202
    public static Item[] DiffText(string TextA, string TextB) =>
UNCOV
203
        DiffText(TextA, TextB, false, false, false); // DiffText
×
204

205

206
    /// <summary>
207
    /// Find the difference in 2 text documents, comparing by textlines.
208
    /// The algorithm itself is comparing 2 arrays of numbers so when comparing 2 text documents
209
    /// each line is converted into a (hash) number. This hash-value is computed by storing all
210
    /// textlines into a common hashtable so i can find duplicates in there, and generating a
211
    /// new number each time a new textline is inserted.
212
    /// </summary>
213
    /// <param name="TextA">A-version of the text (usually the old one)</param>
214
    /// <param name="TextB">B-version of the text (usually the new one)</param>
215
    /// <param name="trimSpace">When set to true, all leading and trailing whitespace characters are stripped out before the comparison is done.</param>
216
    /// <param name="ignoreSpace">When set to true, all whitespace characters are converted to a single space character before the comparison is done.</param>
217
    /// <param name="ignoreCase">When set to true, all characters are converted to their lowercase equivalents before the comparison is done.</param>
218
    /// <returns>Returns a array of Items that describe the differences.</returns>
219
    public static Item[] DiffText(string TextA, string TextB, bool trimSpace, bool ignoreSpace, bool ignoreCase)
220
    {
221
        // prepare the input-text and convert to comparable numbers.
UNCOV
222
        var h = new Hashtable(TextA.Length + TextB.Length);
×
223

224
        // The A-Version of the data (original data) to be compared.
UNCOV
225
        var DataA = new DiffData(DiffCodes(TextA, h, trimSpace, ignoreSpace, ignoreCase));
×
226

227
        // The B-Version of the data (modified data) to be compared.
UNCOV
228
        var DataB = new DiffData(DiffCodes(TextB, h, trimSpace, ignoreSpace, ignoreCase));
×
229

UNCOV
230
        LCS(DataA, 0, DataA.Length, DataB, 0, DataB.Length);
×
UNCOV
231
        return CreateDiffs(DataA, DataB);
×
232
    } // DiffText
233

234

235
    /// <summary>
236
    /// Find the difference in 2 arrays of integers.
237
    /// </summary>
238
    /// <param name="ArrayA">A-version of the numbers (usually the old one)</param>
239
    /// <param name="ArrayB">B-version of the numbers (usually the new one)</param>
240
    /// <returns>Returns a array of Items that describe the differences.</returns>
241
    public static Item[] DiffInt(int[] ArrayA, int[] ArrayB)
242
    {
243
        // The A-Version of the data (original data) to be compared.
244
        var DataA = new DiffData(ArrayA);
×
245

246
        // The B-Version of the data (modified data) to be compared.
247
        var DataB = new DiffData(ArrayB);
×
248

249
        LCS(DataA, 0, DataA.Length, DataB, 0, DataB.Length);
×
250
        return CreateDiffs(DataA, DataB);
×
251
    } // Diff
252

253

254
    /// <summary>
255
    /// This function converts all textlines of the text into unique numbers for every unique textline
256
    /// so further work can work only with simple numbers.
257
    /// </summary>
258
    /// <param name="aText">the input text</param>
259
    /// <param name="h">This extern initialized hashtable is used for storing all ever used textlines.</param>
260
    /// <param name="trimSpace">ignore leading and trailing space characters</param>
261
    /// <param name="ignoreSpace">ignore whitespace differences e.g. double spaces vs single spaces</param>
262
    /// <param name="ignoreCase">ignore capitalisation in comparison</param>
263
    /// <returns>a array of integers.</returns>
264
    private static int[] DiffCodes(string aText, Hashtable h, bool trimSpace, bool ignoreSpace, bool ignoreCase)
265
    {
266
        // get all codes of the text
UNCOV
267
        var lastUsedCode = h.Count;
×
268

269
        // strip off all cr, only use lf as line separator.
UNCOV
270
        aText = aText.Replace("\r", "");
×
UNCOV
271
        var Lines = aText.Split('\n');
×
272

UNCOV
273
        var Codes = new int[Lines.Length];
×
274

UNCOV
275
        for (var i = 0; i < Lines.Length; ++i)
×
276
        {
UNCOV
277
            var s = Lines[i];
×
UNCOV
278
            if (trimSpace)
×
279
                s = s.Trim();
×
280

UNCOV
281
            if (ignoreSpace) s = Regex.Replace(s, "\\s+", " ");
×
282

UNCOV
283
            if (ignoreCase)
×
284
                s = s.ToLower();
×
285

UNCOV
286
            var aCode = h[s];
×
UNCOV
287
            if (aCode == null)
×
288
            {
UNCOV
289
                lastUsedCode++;
×
UNCOV
290
                h[s] = lastUsedCode;
×
UNCOV
291
                Codes[i] = lastUsedCode;
×
292
            }
293
            else
294
            {
295
                Codes[i] = (int)aCode;
×
296
            } // if
297
        } // for
298

UNCOV
299
        return Codes;
×
300
    } // DiffCodes
301

302

303
    /// <summary>
304
    /// This is the algorithm to find the Shortest Middle Snake (SMS).
305
    /// </summary>
306
    /// <param name="DataA">sequence A</param>
307
    /// <param name="LowerA">lower bound of the actual range in DataA</param>
308
    /// <param name="UpperA">upper bound of the actual range in DataA (exclusive)</param>
309
    /// <param name="DataB">sequence B</param>
310
    /// <param name="LowerB">lower bound of the actual range in DataB</param>
311
    /// <param name="UpperB">upper bound of the actual range in DataB (exclusive)</param>
312
    /// <returns>a MiddleSnakeData record containing x,y and u,v</returns>
313
    private static SMSRD SMS(DiffData DataA, int LowerA, int UpperA, DiffData DataB, int LowerB, int UpperB)
314
    {
UNCOV
315
        var MAX = DataA.Length + DataB.Length + 1;
×
316

UNCOV
317
        var DownK = LowerA - LowerB; // the k-line to start the forward search
×
UNCOV
318
        var UpK = UpperA - UpperB; // the k-line to start the reverse search
×
319

UNCOV
320
        var Delta = UpperA - LowerA - (UpperB - LowerB);
×
UNCOV
321
        var oddDelta = (Delta & 1) != 0;
×
322

323
        // vector for the (0,0) to (x,y) search
UNCOV
324
        var DownVector = new int[2 * MAX + 2];
×
325

326
        // vector for the (u,v) to (N,M) search
UNCOV
327
        var UpVector = new int[2 * MAX + 2];
×
328

329
        // The vectors in the publication accepts negative indexes. the vectors implemented here are 0-based
330
        // and are access using a specific offset: UpOffset UpVector and DownOffset for DownVektor
UNCOV
331
        var DownOffset = MAX - DownK;
×
UNCOV
332
        var UpOffset = MAX - UpK;
×
333

UNCOV
334
        var MaxD = (UpperA - LowerA + UpperB - LowerB) / 2 + 1;
×
335

336
        // Debug.Write(2, "SMS", String.Format("Search the box: A[{0}-{1}] to B[{2}-{3}]", LowerA, UpperA, LowerB, UpperB));
337

338
        // init vectors
UNCOV
339
        DownVector[DownOffset + DownK + 1] = LowerA;
×
UNCOV
340
        UpVector[UpOffset + UpK - 1] = UpperA;
×
341

UNCOV
342
        for (var D = 0; D <= MaxD; D++)
×
343
        {
344
            // Extend the forward path.
345
            SMSRD ret;
UNCOV
346
            for (var k = DownK - D; k <= DownK + D; k += 2)
×
347
            {
348
                // Debug.Write(0, "SMS", "extend forward path " + k.ToString());
349

350
                // find the only or better starting point
351
                int x;
UNCOV
352
                if (k == DownK - D)
×
353
                {
UNCOV
354
                    x = DownVector[DownOffset + k + 1]; // down
×
355
                }
356
                else
357
                {
UNCOV
358
                    x = DownVector[DownOffset + k - 1] + 1; // a step to the right
×
UNCOV
359
                    if (k < DownK + D && DownVector[DownOffset + k + 1] >= x)
×
360
                        x = DownVector[DownOffset + k + 1]; // down
×
361
                }
362

UNCOV
363
                var y = x - k;
×
364

365
                // find the end of the furthest reaching forward D-path in diagonal k.
UNCOV
366
                while (x < UpperA && y < UpperB && DataA.data[x] == DataB.data[y])
×
367
                {
368
                    x++;
×
369
                    y++;
×
370
                }
371

UNCOV
372
                DownVector[DownOffset + k] = x;
×
373

374
                // overlap ?
UNCOV
375
                if (oddDelta && UpK - D < k && k < UpK + D)
×
376
                    if (UpVector[UpOffset + k] <= DownVector[DownOffset + k])
×
377
                    {
378
                        ret.x = DownVector[DownOffset + k];
×
379
                        ret.y = DownVector[DownOffset + k] - k;
×
380
                        // ret.u = UpVector[UpOffset + k];      // 2002.09.20: no need for 2 points
381
                        // ret.v = UpVector[UpOffset + k] - k;
382
                        return ret;
×
383
                    } // if
384
                // if
385
            } // for k
386

387
            // Extend the reverse path.
UNCOV
388
            for (var k = UpK - D; k <= UpK + D; k += 2)
×
389
            {
390
                // Debug.Write(0, "SMS", "extend reverse path " + k.ToString());
391

392
                // find the only or better starting point
393
                int x;
UNCOV
394
                if (k == UpK + D)
×
395
                {
UNCOV
396
                    x = UpVector[UpOffset + k - 1]; // up
×
397
                }
398
                else
399
                {
UNCOV
400
                    x = UpVector[UpOffset + k + 1] - 1; // left
×
UNCOV
401
                    if (k > UpK - D && UpVector[UpOffset + k - 1] < x)
×
402
                        x = UpVector[UpOffset + k - 1]; // up
×
403
                } // if
404

UNCOV
405
                var y = x - k;
×
406

UNCOV
407
                while (x > LowerA && y > LowerB && DataA.data[x - 1] == DataB.data[y - 1])
×
408
                {
409
                    x--;
×
410
                    y--; // diagonal
×
411
                }
412

UNCOV
413
                UpVector[UpOffset + k] = x;
×
414

415
                // overlap ?
UNCOV
416
                if (!oddDelta && DownK - D <= k && k <= DownK + D)
×
UNCOV
417
                    if (UpVector[UpOffset + k] <= DownVector[DownOffset + k])
×
418
                    {
UNCOV
419
                        ret.x = DownVector[DownOffset + k];
×
UNCOV
420
                        ret.y = DownVector[DownOffset + k] - k;
×
421
                        // ret.u = UpVector[UpOffset + k];     // 2002.09.20: no need for 2 points
422
                        // ret.v = UpVector[UpOffset + k] - k;
UNCOV
423
                        return ret;
×
424
                    } // if
425
                // if
426
            } // for k
427
        } // for D
428

429
        throw new ApplicationException("the algorithm should never come here.");
×
430
    } // SMS
431

432

433
    /// <summary>
434
    /// This is the divide-and-conquer implementation of the longes common-subsequence (LCS)
435
    /// algorithm.
436
    /// The published algorithm passes recursively parts of the A and B sequences.
437
    /// To avoid copying these arrays the lower and upper bounds are passed while the sequences stay constant.
438
    /// </summary>
439
    /// <param name="DataA">sequence A</param>
440
    /// <param name="LowerA">lower bound of the actual range in DataA</param>
441
    /// <param name="UpperA">upper bound of the actual range in DataA (exclusive)</param>
442
    /// <param name="DataB">sequence B</param>
443
    /// <param name="LowerB">lower bound of the actual range in DataB</param>
444
    /// <param name="UpperB">upper bound of the actual range in DataB (exclusive)</param>
445
    private static void LCS(DiffData DataA, int LowerA, int UpperA, DiffData DataB, int LowerB, int UpperB)
446
    {
447
        // Debug.Write(2, "LCS", String.Format("Analyse the box: A[{0}-{1}] to B[{2}-{3}]", LowerA, UpperA, LowerB, UpperB));
448

449
        // Fast walkthrough equal lines at the start
UNCOV
450
        while (LowerA < UpperA && LowerB < UpperB && DataA.data[LowerA] == DataB.data[LowerB])
×
451
        {
452
            LowerA++;
×
453
            LowerB++;
×
454
        }
455

456
        // Fast walkthrough equal lines at the end
UNCOV
457
        while (LowerA < UpperA && LowerB < UpperB && DataA.data[UpperA - 1] == DataB.data[UpperB - 1])
×
458
        {
459
            --UpperA;
×
460
            --UpperB;
×
461
        }
462

UNCOV
463
        if (LowerA == UpperA)
×
464
        {
465
            // mark as inserted lines.
UNCOV
466
            while (LowerB < UpperB)
×
UNCOV
467
                DataB.modified[LowerB++] = true;
×
468
        }
UNCOV
469
        else if (LowerB == UpperB)
×
470
        {
471
            // mark as deleted lines.
UNCOV
472
            while (LowerA < UpperA)
×
UNCOV
473
                DataA.modified[LowerA++] = true;
×
474
        }
475
        else
476
        {
477
            // Find the middle snakea and length of an optimal path for A and B
UNCOV
478
            var smsrd = SMS(DataA, LowerA, UpperA, DataB, LowerB, UpperB);
×
479
            // Debug.Write(2, "MiddleSnakeData", String.Format("{0},{1}", smsrd.x, smsrd.y));
480

481
            // The path is from LowerX to (x,y) and (x,y) ot UpperX
UNCOV
482
            LCS(DataA, LowerA, smsrd.x, DataB, LowerB, smsrd.y);
×
UNCOV
483
            LCS(DataA, smsrd.x, UpperA, DataB, smsrd.y, UpperB); // 2002.09.20: no need for 2 points
×
484
        }
UNCOV
485
    } // LCS()
×
486

487

488
    /// <summary>Scan the tables of which lines are inserted and deleted,
489
    /// producing an edit script in forward order.
490
    /// </summary>
491
    /// dynamic array
492
    private static Item[] CreateDiffs(DiffData DataA, DiffData DataB)
493
    {
UNCOV
494
        var a = new ArrayList();
×
495

496
        int StartA, StartB;
497

UNCOV
498
        var LineA = 0;
×
UNCOV
499
        var LineB = 0;
×
UNCOV
500
        while (LineA < DataA.Length || LineB < DataB.Length)
×
UNCOV
501
            if (LineA < DataA.Length && !DataA.modified[LineA]
×
UNCOV
502
                                     && LineB < DataB.Length && !DataB.modified[LineB])
×
503
            {
504
                // equal lines
505
                LineA++;
×
506
                LineB++;
×
507
            }
508
            else
509
            {
510
                // maybe deleted and/or inserted lines
UNCOV
511
                StartA = LineA;
×
UNCOV
512
                StartB = LineB;
×
513

UNCOV
514
                while (LineA < DataA.Length && (LineB >= DataB.Length || DataA.modified[LineA]))
×
515
                    // while (LineA < DataA.Length && DataA.modified[LineA])
UNCOV
516
                    LineA++;
×
517

UNCOV
518
                while (LineB < DataB.Length && (LineA >= DataA.Length || DataB.modified[LineB]))
×
519
                    // while (LineB < DataB.Length && DataB.modified[LineB])
UNCOV
520
                    LineB++;
×
521

UNCOV
522
                if (StartA < LineA || StartB < LineB)
×
523
                    // store a new difference-item
UNCOV
524
                    a.Add(new Item
×
UNCOV
525
                    {
×
UNCOV
526
                        StartA = StartA,
×
UNCOV
527
                        StartB = StartB,
×
UNCOV
528
                        deletedA = LineA - StartA,
×
UNCOV
529
                        insertedB = LineB - StartB
×
UNCOV
530
                    }); // if
×
531
            } // if
532

533
        // while
UNCOV
534
        var result = new Item[a.Count];
×
UNCOV
535
        a.CopyTo(result);
×
536

UNCOV
537
        return result;
×
538
    }
539
} // class Diff
540

541
/// <summary>Data on one input file being compared.
542
/// </summary>
543
internal class DiffData
544
{
545
    /// <summary>Number of elements (lines).</summary>
546
    internal int Length;
547

548
    /// <summary>Buffer of numbers that will be compared.</summary>
549
    internal int[] data;
550

551
    /// <summary>
552
    /// Array of booleans that flag for modified data.
553
    /// This is the result of the diff.
554
    /// This means deletedA in the first Data or inserted in the second Data.
555
    /// </summary>
556
    internal bool[] modified;
557

558
    /// <summary>
559
    /// Initialize the Diff-Data buffer.
560
    /// </summary>
561
    /// <param name="initData">reference to the buffer</param>
UNCOV
562
    internal DiffData(int[] initData)
×
563
    {
UNCOV
564
        data = initData;
×
UNCOV
565
        Length = initData.Length;
×
UNCOV
566
        modified = new bool[Length + 2];
×
UNCOV
567
    } // DiffData
×
568
} // class DiffData
569

570
// namespace
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc