@@ -10,65 +10,88 @@ interface
1010 { $ENDIF}
1111 Classes,
1212 SysUtils,
13- Generics.Collections
13+ Generics.Collections,
14+ Math
1415 { $IFDEF DEBUG}
1516, Stopwatch
1617 { $ENDIF}
17- ;
18+ ;
1819
1920type
2021 // Create a record of temperature stats
2122 TStat = record
2223 var
23- min: single;
24- max: single;
25- sum: single;
26- count: word;
24+ min: int64;
25+ max: int64;
26+ sum: int64;
27+ Count: int64;
28+ private
29+ function RoundEx (x: double): double; // Borrowed from the baseline program
30+ function PascalRound (x: double): double; // Borrowed from the baseline program
2731 public
28- constructor Create(newMin: single;
29- newMax: single;
30- newSum: single;
31- newCount: word);
32+ constructor Create(newMin: int64; newMax: int64; newSum: int64; newCount: int64);
3233 function ToString : string;
34+
3335 end ;
3436
3537type
3638 // Create a dictionary
3739 TWeatherDictionary = specialize TDictionary<string, TStat>;
3840
39-
40- { // A helper function to add a city temperature into the TWeatherDictionary
41- procedure AddCityTemperature(cityName: string;
42- newTemp: single;
43- var weatherDictionary: TWeatherDictionary);
44- }
45-
4641// The main algorithm to process the temp measurements from various weather station
4742procedure ProcessTempMeasurements (filename: string);
4843
4944
5045implementation
5146
52- constructor TStat.Create(newMin: single;
53- newMax: single;
54- newSum: single;
55- newCount: word);
47+ function TStat.RoundEx (x: double): double;
48+ begin
49+ Result := PascalRound(x * 10.0 ) / 10.0 ;
50+ end ;
51+
52+ function TStat.PascalRound (x: double): double;
53+ var
54+ t: double;
55+ begin
56+ // round towards positive infinity
57+ t := Trunc(x);
58+ if (x < 0.0 ) and (t - x = 0.5 ) then
59+ begin
60+ // Do nothing
61+ end
62+ else if Abs(x - t) >= 0.5 then
63+ begin
64+ t := t + Math.Sign(x);
65+ end ;
66+
67+ if t = 0.0 then
68+ Result := 0.0
69+ else
70+ Result := t;
71+ end ;
72+
73+ constructor TStat.Create(newMin: int64; newMax: int64; newSum: int64; newCount: int64);
5674begin
5775 self.min := newMin;
5876 self.max := newMax;
5977 self.sum := newSum;
60- self.count := newCount;
78+ self.Count := newCount;
6179end ;
6280
6381function TStat.ToString : string;
82+ var
83+ minR, meanR, maxR: double; // Store the rounded values prior saving to TStringList.
6484begin
6585 { $IFDEF DEBUG}
6686 Result := Format(' Min: %.1f; Mean: %.1f; Maxp: %.1f; Sum: %.1f; Count %d' ,
6787 [self.min, (self.sum / self.Count), self.max,
6888 self.sum, self.Count]);
6989 { $ENDIF DEBUG}
7090 // Result := Format('%.1f/%.1f/%.1f', [self.min, (self.sum / self.count), self.max]);
71- Result := FormatFloat(' 0.0' , self.min) + ' /' + FormatFloat(' 0.0' , (self.sum/self.count)) + ' /' + FormatFloat(' 0.0' , self.max)
91+ minR := RoundEx(self.min / 10 );
92+ maxR := RoundEx(self.max / 10 );
93+ meanR := RoundEx(self.sum / self.Count / 10 );
94+ Result := FormatFloat(' 0.0' , minR) + ' /' + FormatFloat(' 0.0' , meanR) + ' /' + FormatFloat(' 0.0' , maxR);
7295
7396end ;
7497
@@ -78,10 +101,11 @@ function TStat.ToString: string;
78101 The following procedure Written by Székely Balázs for the 1BRC for Object Pascal.
79102 URL: https://github.com/gcarreno/1brc-ObjectPascal/tree/main
80103}
81- function CustomTStringListComparer (AList: TStringList; AIndex1, AIndex2: Integer): Integer;
104+ function CustomTStringListComparer (AList: TStringList;
105+ AIndex1, AIndex2: integer): integer;
82106var
83- Pos1, Pos2: Integer ;
84- Str1, Str2: String ;
107+ Pos1, Pos2: integer ;
108+ Str1, Str2: string ;
85109begin
86110 Result := 0 ;
87111 Str1 := AList.Strings[AIndex1];
@@ -92,13 +116,12 @@ function CustomTStringListComparer(AList: TStringList; AIndex1, AIndex2: Integer
92116 begin
93117 Str1 := Copy(Str1, 1 , Pos1 - 1 );
94118 Str2 := Copy(Str2, 1 , Pos2 - 1 );
95- Result := CompareStr(Str1, Str2);
119+ Result := CompareStr(Str1, Str2);
96120 end ;
97121end ;
98122
99- procedure AddCityTemperature (cityName: string;
100- newTemp: single;
101- var weatherDictionary: TWeatherDictionary);
123+
124+ procedure AddCityTemperature (cityName: string; newTemp: int64; var weatherDictionary: TWeatherDictionary);
102125var
103126 stat: TStat;
104127begin
@@ -142,11 +165,11 @@ procedure AddCityTemperature(cityName: string;
142165procedure ProcessTempMeasurements (filename: string);
143166var
144167 wd: TWeatherDictionary;
145- line, ws: string;
146- lineSeparated: array of string;
168+ line, ws, recordedTemp: string;
147169 weatherStationList: TStringList;
148170 textFile: System.TextFile;
149171 isFirstKey: boolean = True;
172+ delimiterPos: integer;
150173begin
151174
152175 // Start a timer
@@ -172,12 +195,26 @@ procedure ProcessTempMeasurements(filename: string);
172195 begin
173196 // Read a line
174197 ReadLn(textFile, line);
198+
175199 // If the line start with #, then continue/skip.
176200 if (line[1 ] = ' #' ) then continue;
177201
202+ // Get position of the delimiter
203+ delimiterPos := Pos(' ;' , line);
204+
178205 // Else, add an entry into the dictionary.
179- lineSeparated := line.Split([' ;' ]);
180- AddCityTemperature(lineSeparated[0 ], StrToFloat(lineSeparated[1 ]), wd);
206+ // Get the weather station name
207+ // Using Copy ans POS - as suggested by Gemini AI.
208+ // This made the program 3 mins fater when processing 1 billion rows.
209+ ws := Copy(line, 1 , delimiterPos - 1 );
210+
211+ // Get the temperature recorded, as string, remove '.' from string float
212+ // because we want to save it as int64.
213+ recordedTemp := Copy(line, delimiterPos + 1 , Length(line));
214+ recordedTemp := StringReplace(recordedTemp, ' .' , ' ' , [rfReplaceAll, rfIgnoreCase]);
215+
216+ // Add the weather station and the recorded temp (as int64) in the TDictionary
217+ AddCityTemperature(ws, StrToInt64(recordedTemp), wd);
181218
182219 end ; // end while loop reading line at a time
183220
@@ -190,6 +227,7 @@ procedure ProcessTempMeasurements(filename: string);
190227 end ; // End of file reading ////////////////////////////////////////////////
191228
192229 // Format and sort weather station by name and temp stat ///////////////////
230+ ws:=' ' ;
193231 for ws in wd.Keys do
194232 begin
195233 weatherStationList.Add(ws + ' =' + wd[ws].ToString);
0 commit comments