1/*
2 * Legal Notice
3 *
4 * This document and associated source code (the "Work") is a part of a
5 * benchmark specification maintained by the TPC.
6 *
7 * The TPC reserves all right, title, and interest to the Work as provided
8 * under U.S. and international laws, including without limitation all patent
9 * and trademark rights therein.
10 *
11 * No Warranty
12 *
13 * 1.1 TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, THE INFORMATION
14 * CONTAINED HEREIN IS PROVIDED "AS IS" AND WITH ALL FAULTS, AND THE
15 * AUTHORS AND DEVELOPERS OF THE WORK HEREBY DISCLAIM ALL OTHER
16 * WARRANTIES AND CONDITIONS, EITHER EXPRESS, IMPLIED OR STATUTORY,
17 * INCLUDING, BUT NOT LIMITED TO, ANY (IF ANY) IMPLIED WARRANTIES,
18 * DUTIES OR CONDITIONS OF MERCHANTABILITY, OF FITNESS FOR A PARTICULAR
19 * PURPOSE, OF ACCURACY OR COMPLETENESS OF RESPONSES, OF RESULTS, OF
20 * WORKMANLIKE EFFORT, OF LACK OF VIRUSES, AND OF LACK OF NEGLIGENCE.
21 * ALSO, THERE IS NO WARRANTY OR CONDITION OF TITLE, QUIET ENJOYMENT,
22 * QUIET POSSESSION, CORRESPONDENCE TO DESCRIPTION OR NON-INFRINGEMENT
23 * WITH REGARD TO THE WORK.
24 * 1.2 IN NO EVENT WILL ANY AUTHOR OR DEVELOPER OF THE WORK BE LIABLE TO
25 * ANY OTHER PARTY FOR ANY DAMAGES, INCLUDING BUT NOT LIMITED TO THE
26 * COST OF PROCURING SUBSTITUTE GOODS OR SERVICES, LOST PROFITS, LOSS
27 * OF USE, LOSS OF DATA, OR ANY INCIDENTAL, CONSEQUENTIAL, DIRECT,
28 * INDIRECT, OR SPECIAL DAMAGES WHETHER UNDER CONTRACT, TORT, WARRANTY,
29 * OR OTHERWISE, ARISING IN ANY WAY OUT OF THIS OR ANY OTHER AGREEMENT
30 * RELATING TO THE WORK, WHETHER OR NOT SUCH AUTHOR OR DEVELOPER HAD
31 * ADVANCE NOTICE OF THE POSSIBILITY OF SUCH DAMAGES.
32 *
33 * Contributors
34 * - Sergey Vasilevskiy
35 * - Doug Johnson
36 */
37
38#include "main/EGenTables_stdafx.h"
39
40using namespace TPCE;
41
42const UINT iUSACtryCode = 1; // must be the same as the code in country tax rates file
43const UINT iCanadaCtryCode = 2; // must be the same as the code in country tax rates file
44
45// Minimum and maximum to use when generating address street numbers.
46const int iStreetNumberMin = 100;
47const int iStreetNumberMax = 25000;
48
49// Some customers have an AD_LINE_2, some are NULL.
50const int iPctCustomersWithNullAD_LINE_2 = 60;
51
52// Of the customers that have an AD_LINE_2, some are
53// an apartment, others are a suite.
54const int iPctCustomersWithAptAD_LINE_2 = 75;
55
56// Minimum and maximum to use when generating apartment numbers.
57const int iApartmentNumberMin = 1;
58const int iApartmentNumberMax = 1000;
59
60// Minimum and maximum to use when generating suite numbers.
61const int iSuiteNumberMin = 1;
62const int iSuiteNumberMax = 500;
63
64// Number of RNG calls to skip for one row in order
65// to not use any of the random values from the previous row.
66const int iRNGSkipOneRowAddress = 10; // real number in 3.5: 7
67
68/*
69 * Constructor for the ADDRESS table class.
70 *
71 * PARAMETERS:
72 * IN inputFiles - input flat files loaded in memory
73 * IN iCustomerCount - number of customers to generate
74 * IN iStartFromCustomer - ordinal position of the first customer in
75 * the sequence (Note: 1-based) for whom to generate the addresses. Used if
76 * generating customer addresses only. IN bCustomerAddressesOnly - if true,
77 * generate only customer addresses if false, generate exchange, company, and
78 * customer addresses (always start from the first customer in this case)
79 */
80CAddressTable::CAddressTable(const DataFileManager &dfm, TIdent iCustomerCount, TIdent iStartFromCustomer,
81 bool bCustomerAddressesOnly, bool bCacheEnabled)
82 : TableTemplate<ADDRESS_ROW>(), m_companies(dfm.CompanyFile()), m_Street(dfm.StreetNameDataFile()),
83 m_StreetSuffix(dfm.StreetSuffixDataFile()), m_ZipCode(dfm.ZipCodeDataFile()),
84 m_iStartFromCustomer(iStartFromCustomer), m_iCustomerCount(iCustomerCount),
85 m_bCustomerAddressesOnly(bCustomerAddressesOnly), m_bCustomerAddress(bCustomerAddressesOnly),
86 m_bCacheEnabled(bCacheEnabled), INVALID_CACHE_ENTRY(-1) {
87 m_iExchangeCount = dfm.ExchangeDataFile().size(); // number of rows in Exchange
88 m_iCompanyCount = m_companies.GetConfiguredCompanyCount(); // number of configured companies
89
90 // Generate customer addresses only (used for CUSTOMER_TAXRATE)
91 if (bCustomerAddressesOnly) {
92 // skip exchanges and companies
93 m_iLastRowNumber = m_iExchangeCount + m_iCompanyCount + iStartFromCustomer - 1;
94
95 // This is not really a count, but the last address row to generate.
96 //
97 m_iTotalAddressCount = m_iLastRowNumber + m_iCustomerCount;
98 } else { // Generating not only customer, but also exchange and company
99 // addresses
100 m_iLastRowNumber = iStartFromCustomer - 1;
101
102 // This is not really a count, but the last address row to generate.
103 //
104 m_iTotalAddressCount = m_iLastRowNumber + m_iCustomerCount + m_iExchangeCount + m_iCompanyCount;
105 }
106
107 m_row.AD_ID = m_iLastRowNumber + iTIdentShift; // extend to 64 bits for address id
108
109 if (m_bCacheEnabled) {
110 m_iCacheSize = (int)iDefaultLoadUnitSize;
111 m_iCacheOffset = iTIdentShift + m_iExchangeCount + m_iCompanyCount + m_iStartFromCustomer;
112 m_CacheZipCode = new int[m_iCacheSize];
113 for (int i = 0; i < m_iCacheSize; i++) {
114 m_CacheZipCode[i] = INVALID_CACHE_ENTRY;
115 }
116 }
117}
118
119CAddressTable::~CAddressTable() {
120 if (m_bCacheEnabled) {
121 delete[] m_CacheZipCode;
122 }
123}
124
125/*
126 * Reset the state for the next load unit.
127 *
128 * PARAMETERS:
129 * none.
130 *
131 * RETURNS:
132 * none.
133 */
134void CAddressTable::InitNextLoadUnit() {
135 m_rnd.SetSeed(m_rnd.RndNthElement(RNGSeedTableDefault, (RNGSEED)m_iLastRowNumber * iRNGSkipOneRowAddress));
136
137 ClearRecord(); // this is needed for EGenTest to work
138
139 if (m_bCacheEnabled) {
140 m_iCacheOffset += iDefaultLoadUnitSize;
141 for (int i = 0; i < m_iCacheSize; i++) {
142 m_CacheZipCode[i] = INVALID_CACHE_ENTRY;
143 }
144 }
145}
146
147/*
148 * Generates the next A_ID value.
149 * It is stored in the internal record structure and also returned.
150 * The number of rows generated is incremented. This is why
151 * this function cannot be called more than once for a record.
152 *
153 * PARAMETERS:
154 * none.
155 *
156 * RETURNS:
157 * next address id.
158 */
159TIdent CAddressTable::GenerateNextAD_ID() {
160 // Reset RNG at Load Unit boundary, so that all data is repeatable.
161 //
162 if (m_iLastRowNumber > (m_iExchangeCount + m_iCompanyCount) &&
163 ((m_iLastRowNumber - (m_iExchangeCount + m_iCompanyCount)) % iDefaultLoadUnitSize == 0)) {
164 InitNextLoadUnit();
165 }
166
167 ++m_iLastRowNumber;
168 // Find out whether this next row is for a customer (so as to generate
169 // AD_LINE_2). Exchange and Company addresses are before Customer ones.
170 //
171 m_bCustomerAddress = m_iLastRowNumber >= m_iExchangeCount + m_iCompanyCount;
172
173 // update state info
174 m_bMoreRecords = m_iLastRowNumber < m_iTotalAddressCount;
175
176 m_row.AD_ID = m_iLastRowNumber + iTIdentShift;
177
178 return m_row.AD_ID;
179}
180
181/*
182 * Returns the address id of the customer specified by the customer id.
183 *
184 * PARAMETERS:
185 * IN C_ID - customer id (1-based)
186 *
187 * RETURNS:
188 * address id.
189 */
190TIdent CAddressTable::GetAD_IDForCustomer(TIdent C_ID) {
191 return m_iExchangeCount + m_iCompanyCount + C_ID;
192}
193
194/*
195 * Generate AD_LINE_1 and store it in the record structure.
196 * Does not increment the number of rows generated.
197 *
198 * PARAMETERS:
199 * none.
200 *
201 * RETURNS:
202 * none.
203 */
204void CAddressTable::GenerateAD_LINE_1() {
205 int iStreetNum = m_rnd.RndIntRange(iStreetNumberMin, iStreetNumberMax);
206 // int iStreetThreshold = m_rnd.RndIntRange(0,
207 // m_Street->GetGreatestKey()-2);
208 int iStreetThreshold = m_rnd.RndIntRange(0, m_Street.size() - 2);
209 int iStreetSuffixThreshold = m_rnd.RndIntRange(0, m_StreetSuffix.size() - 1);
210
211 snprintf(m_row.AD_LINE1, sizeof(m_row.AD_LINE1), "%d %s %s", iStreetNum, m_Street[iStreetThreshold].STREET_CSTR(),
212 m_StreetSuffix[iStreetSuffixThreshold].SUFFIX_CSTR());
213}
214
215/*
216 * Generate AD_LINE_2 and store it in the record structure.
217 * Does not increment the number of rows generated.
218 *
219 * PARAMETERS:
220 * none.
221 *
222 * RETURNS:
223 * none.
224 */
225void CAddressTable::GenerateAD_LINE_2() {
226 if (!m_bCustomerAddress || m_rnd.RndPercent(iPctCustomersWithNullAD_LINE_2)) { // Generate second address line
227 // only for customers (not
228 // companies)
229 m_row.AD_LINE2[0] = '\0';
230 } else {
231 if (m_rnd.RndPercent(iPctCustomersWithAptAD_LINE_2)) {
232 snprintf(m_row.AD_LINE2, sizeof(m_row.AD_LINE2), "Apt. %d",
233 m_rnd.RndIntRange(iApartmentNumberMin, iApartmentNumberMax));
234 } else {
235 snprintf(m_row.AD_LINE2, sizeof(m_row.AD_LINE2), "Suite %d",
236 m_rnd.RndIntRange(iSuiteNumberMin, iSuiteNumberMax));
237 }
238 }
239}
240
241/*
242 * For a given address id returns the same Threshold used to
243 * select the town, division, zip, and country.
244 * Needed to return a specific division/country for a given address id (for
245 * customer tax rates).
246 *
247 * PARAMETERS:
248 * IN ADID - address id
249 *
250 * RETURNS:
251 * none.
252 */
253int CAddressTable::GetTownDivisionZipCodeThreshold(TIdent ADID) {
254 RNGSEED OldSeed;
255 int iThreshold;
256
257 OldSeed = m_rnd.GetSeed();
258 m_rnd.SetSeed(m_rnd.RndNthElement(RNGSeedBaseTownDivZip, (RNGSEED)ADID));
259 iThreshold = m_rnd.RndIntRange(0, m_ZipCode.size() - 1);
260 m_rnd.SetSeed(OldSeed);
261 return (iThreshold);
262}
263
264/*
265 * Return the country code code for a given zip code.
266 *
267 * PARAMETERS:
268 * IN szZipCode - string with a US or Canada zip code
269 *
270 * RETURNS:
271 * country code.
272 */
273UINT CAddressTable::GetCountryCode(const char *szZipCode) {
274 if (('0' <= szZipCode[0]) && (szZipCode[0] <= '9')) {
275 // If the zip code starts with a number, then it's a USA code.
276 return (iUSACtryCode);
277 } else {
278 // If the zip code does NOT start with a number, than it's a Canadian
279 // code.
280 return (iCanadaCtryCode);
281 }
282}
283
284/*
285 * Return a certain division/country code (from the input file) for a given
286 * address id. Used in the loader to properly calculate tax on a trade.
287 *
288 * PARAMETERS:
289 * IN AD_ID - address id
290 * OUT iDivCode - division (state/province) code
291 * OUT iCtryCode - country (USA/CANADA) code
292 *
293 * RETURNS:
294 * none.
295 */
296void CAddressTable::GetDivisionAndCountryCodesForAddress(TIdent AD_ID, UINT &iDivCode, UINT &iCtryCode) {
297 // const TZipCodeInputRow* pZipCodeInputRow = NULL;
298
299 // We will sometimes get AD_ID values that are outside the current
300 // load unit (cached range). We need to check for this case
301 // and avoid the lookup (as we will segfault or get bogus data.)
302 TIdent index = AD_ID - m_iCacheOffset;
303 bool bCheckCache = (index >= 0 && index <= m_iCacheSize);
304
305 if (m_bCacheEnabled && bCheckCache && (INVALID_CACHE_ENTRY != m_CacheZipCode[index])) {
306 // Make use of the cache to get the data.
307 iDivCode = m_ZipCode[m_CacheZipCode[index]].DivisionTaxKey();
308 iCtryCode = GetCountryCode(m_ZipCode[m_CacheZipCode[index]].ZC_CODE_CSTR());
309
310 // We're done, so bail out.
311 return;
312 }
313
314 // The cache wasn't used so get the necessary value.
315 int iThreshold = GetTownDivisionZipCodeThreshold(AD_ID);
316
317 // If possible, cache the result in case we need it again.
318 if (m_bCacheEnabled && bCheckCache) {
319 m_CacheZipCode[index] = iThreshold;
320 }
321
322 // Get the data.
323 iDivCode = m_ZipCode[iThreshold].DivisionTaxKey();
324 iCtryCode = GetCountryCode(m_ZipCode[iThreshold].ZC_CODE_CSTR());
325}
326
327/*
328 * Generate zip code and country for the current address id
329 * and store them in the record structure.
330 * Does not increment the number of rows generated.
331 *
332 * PARAMETERS:
333 * none.
334 *
335 * RETURNS:
336 * none.
337 */
338void CAddressTable::GenerateAD_ZC_CODE_CTRY() {
339 int iThreshold;
340
341 iThreshold = GetTownDivisionZipCodeThreshold(m_row.AD_ID);
342 const ZipCodeDataFileRecord &dfr = m_ZipCode[iThreshold];
343
344 strncpy(m_row.AD_ZC_CODE, dfr.ZC_CODE_CSTR(), sizeof(m_row.AD_ZC_CODE));
345
346 if (iUSACtryCode == GetCountryCode(dfr.ZC_CODE_CSTR())) { // US state
347 strncpy(m_row.AD_CTRY, "USA", sizeof(m_row.AD_CTRY));
348 } else { // Canadian province
349 strncpy(m_row.AD_CTRY, "CANADA", sizeof(m_row.AD_CTRY));
350 }
351}
352
353/*
354 * Generate all column values for the next row
355 * and store them in the record structure.
356 * Increment the number of rows generated.
357 *
358 * PARAMETERS:
359 * none.
360 *
361 * RETURNS:
362 * TRUE, if there are more records in the ADDRESS table; FALSE
363 * othewise.
364 */
365bool CAddressTable::GenerateNextRecord() {
366 GenerateNextAD_ID();
367 GenerateAD_LINE_1();
368 GenerateAD_LINE_2();
369 GenerateAD_ZC_CODE_CTRY();
370
371 // Return false if all the rows have been generated
372 return (MoreRecords());
373}
374