blob: 8b873a34dfb87901526efc83fa43f31f4fb551da [file] [log] [blame]
tanjent@gmail.comf67ce942011-03-14 09:11:18 +00001#include "Platform.h"
tanjent@gmail.com7e5c3632010-11-02 00:50:04 +00002#include "hashes.h"
tanjent@gmail.comad4b3632010-11-05 01:20:58 +00003#include "KeysetTest.h"
4#include "SpeedTest.h"
5#include "AvalancheTest.h"
6#include "DifferentialTest.h"
tanjent@gmail.com7e5c3632010-11-02 00:50:04 +00007
tanjent@gmail.comf67ce942011-03-14 09:11:18 +00008#include <stdio.h>
tanjent@gmail.comad4b3632010-11-05 01:20:58 +00009#include <time.h>
tanjent@gmail.comad4b3632010-11-05 01:20:58 +000010
11bool g_testAll = false;
12
tanjent@gmail.comad4b3632010-11-05 01:20:58 +000013bool g_testSanity = false;
14bool g_testSpeed = false;
15bool g_testDiff = false;
16bool g_testAvalanche = false;
17bool g_testCyclic = false;
18bool g_testSparse = false;
19bool g_testPermutation = false;
20bool g_testWindow = false;
21bool g_testText = false;
22bool g_testZeroes = false;
23bool g_testSeed = false;
tanjent@gmail.comad4b3632010-11-05 01:20:58 +000024
tanjent@gmail.comf67ce942011-03-14 09:11:18 +000025//-----------------------------------------------------------------------------
tanjent@gmail.com31a9e8e2010-11-09 20:29:19 +000026
27int64_t g_hashcount = 0;
28int64_t g_bytecount = 0;
29
30void counterhash ( const void * , const int len, const uint32_t , void * out )
31{
32 g_hashcount++;
33 g_bytecount += len;
34
35 *(uint32_t*)out = rand_u32();
36}
37
tanjent@gmail.comad4b3632010-11-05 01:20:58 +000038//-----------------------------------------------------------------------------
39
40struct HashInfo
41{
42 pfHash hash;
43 int hashbits;
44 const char * name;
45 const char * desc;
46};
47
48HashInfo g_hashes[] =
49{
tanjent@gmail.com31a9e8e2010-11-09 20:29:19 +000050 { counterhash, 32, "count", "Counts how many times the hash function is called" },
tanjent@gmail.comad4b3632010-11-05 01:20:58 +000051 { randhash_32, 32, "rand32", "Random number generator, 32-bit" },
52 { randhash_64, 64, "rand64", "Random number generator, 64-bit" },
53 { randhash_128, 128, "rand128", "Random number generator, 128-bit" },
54
55 { crc32, 32, "crc32", "CRC-32" },
56 { DoNothingHash, 32, "donothing32", "Do-Nothing Function (only valid for speed test comparison)" },
57
58 { md5_32, 32, "md5_32a", "MD5, first 32 bits of result" },
59 { sha1_32a, 32, "sha1_32a", "SHA1, first 32 bits of result" },
60
61 { FNV, 32, "FNV", "Fowler-Noll-Vo hash, 32-bit" },
62 { lookup3_test, 32, "lookup3", "Bob Jenkins' lookup3" },
63 { SuperFastHash, 32, "superfast", "Paul Hsieh's SuperFastHash" },
tanjent@gmail.combabb5532011-02-28 06:03:12 +000064 { MurmurOAAT, 32, "MurmurOAAT", "Murmur one-at-a-time" },
tanjent@gmail.comad4b3632010-11-05 01:20:58 +000065
66 // MurmurHash2
67
68 { MurmurHash2_test, 32, "Murmur2", "MurmurHash2 for x86, 32-bit" },
69 { MurmurHash2A_test, 32, "Murmur2A", "MurmurHash2A for x86, 32-bit" },
70 { MurmurHash64A_test, 64, "Murmur2B", "MurmurHash2 for x64, 64-bit" },
71 { MurmurHash64B_test, 64, "Murmur2C", "MurmurHash2 for x86, 64-bit" },
72
73 // MurmurHash3
74
75 { MurmurHash3_x86_32, 32, "Murmur3A", "MurmurHash3 for x86, 32-bit" },
tanjent@gmail.comad4b3632010-11-05 01:20:58 +000076 { MurmurHash3_x86_128, 128, "Murmur3C", "MurmurHash3 for x86, 128-bit" },
tanjent@gmail.comad4b3632010-11-05 01:20:58 +000077 { MurmurHash3_x64_128, 128, "Murmur3F", "MurmurHash3 for x64, 128-bit" },
78
79};
80
81HashInfo * findHash ( const char * name )
82{
83 for(int i = 0; i < sizeof(g_hashes) / sizeof(HashInfo); i++)
84 {
85 if(_stricmp(name,g_hashes[i].name) == 0) return &g_hashes[i];
86 }
87
88 return NULL;
89}
tanjent@gmail.com7e5c3632010-11-02 00:50:04 +000090
91//----------------------------------------------------------------------------
92
93template < typename hashtype >
94void test ( hashfunc<hashtype> hash, const char * hashname )
95{
tanjent@gmail.comad4b3632010-11-05 01:20:58 +000096 const int hashbits = sizeof(hashtype) * 8;
tanjent@gmail.com7e5c3632010-11-02 00:50:04 +000097
tanjent@gmail.comad4b3632010-11-05 01:20:58 +000098 printf("-------------------------------------------------------------------------------\n");
99 printf("--- Testing %s\n\n",hashname);
tanjent@gmail.com7e5c3632010-11-02 00:50:04 +0000100
tanjent@gmail.comad4b3632010-11-05 01:20:58 +0000101 //-----------------------------------------------------------------------------
102 // Sanity tests
tanjent@gmail.com7e5c3632010-11-02 00:50:04 +0000103
tanjent@gmail.comad4b3632010-11-05 01:20:58 +0000104 if(g_testSanity || g_testAll)
105 {
106 printf("[[[ Sanity Tests ]]]\n\n");
tanjent@gmail.com7e5c3632010-11-02 00:50:04 +0000107
tanjent@gmail.comad4b3632010-11-05 01:20:58 +0000108 QuickBrownFox(hash,hashbits);
109 SanityTest(hash,hashbits);
tanjent@gmail.comad4b3632010-11-05 01:20:58 +0000110 AppendedZeroesTest(hash,hashbits);
111 printf("\n");
112 }
tanjent@gmail.com7e5c3632010-11-02 00:50:04 +0000113
tanjent@gmail.comad4b3632010-11-05 01:20:58 +0000114 //-----------------------------------------------------------------------------
115 // Speed tests
tanjent@gmail.com7e5c3632010-11-02 00:50:04 +0000116
tanjent@gmail.comad4b3632010-11-05 01:20:58 +0000117 if(g_testSpeed || g_testAll)
118 {
119 printf("[[[ Speed Tests ]]]\n\n");
tanjent@gmail.com7e5c3632010-11-02 00:50:04 +0000120
tanjent@gmail.comad4b3632010-11-05 01:20:58 +0000121 BulkSpeedTest(hash);
122 printf("\n");
tanjent@gmail.com7e5c3632010-11-02 00:50:04 +0000123
tanjent@gmail.combabb5532011-02-28 06:03:12 +0000124 for(int i = 1; i < 32; i++)
125 {
126 double cycles;
127
128 TinySpeedTest(hash,sizeof(hashtype),i,true,cycles);
129 }
130
131 for(int i = 32; i <= 2048; i += 32)
132 {
133 double cycles;
134
135
136 TinySpeedTest(hash,sizeof(hashtype),i,true,cycles);
137 }
138
tanjent@gmail.comad4b3632010-11-05 01:20:58 +0000139 printf("\n");
140 }
tanjent@gmail.com7e5c3632010-11-02 00:50:04 +0000141
tanjent@gmail.comad4b3632010-11-05 01:20:58 +0000142 //-----------------------------------------------------------------------------
143 // Differential tests
tanjent@gmail.com7e5c3632010-11-02 00:50:04 +0000144
tanjent@gmail.comad4b3632010-11-05 01:20:58 +0000145 if(g_testDiff || g_testAll)
146 {
147 printf("[[[ Differential Tests ]]]\n\n");
tanjent@gmail.com7e5c3632010-11-02 00:50:04 +0000148
tanjent@gmail.comad4b3632010-11-05 01:20:58 +0000149 bool result = true;
150 bool dumpCollisions = false;
tanjent@gmail.com7e5c3632010-11-02 00:50:04 +0000151
tanjent@gmail.comad4b3632010-11-05 01:20:58 +0000152 result &= DiffTest< Blob<64>, hashtype >(hash,5,1000,dumpCollisions);
153 result &= DiffTest< Blob<128>, hashtype >(hash,4,1000,dumpCollisions);
154 result &= DiffTest< Blob<256>, hashtype >(hash,3,1000,dumpCollisions);
155
156 if(!result) printf("*********FAIL*********\n");
157 printf("\n");
158 }
159
160 //-----------------------------------------------------------------------------
161 // Avalanche tests.
162
163 // 2 million reps is enough to measure bias down to ~0.25%
164
165 if(g_testAvalanche || g_testAll)
166 {
167 printf("[[[ Avalanche Tests ]]]\n\n");
168
tanjent@gmail.combabb5532011-02-28 06:03:12 +0000169 //const int hashbits = sizeof(hashtype) * 8;
tanjent@gmail.comad4b3632010-11-05 01:20:58 +0000170 bool result = true;
171
tanjent@gmail.combabb5532011-02-28 06:03:12 +0000172 result &= AvalancheTest< Blob< 32>, hashtype > (hash,300000);
173 result &= AvalancheTest< Blob< 40>, hashtype > (hash,300000);
174 result &= AvalancheTest< Blob< 48>, hashtype > (hash,300000);
175 result &= AvalancheTest< Blob< 56>, hashtype > (hash,300000);
176
177 result &= AvalancheTest< Blob< 64>, hashtype > (hash,300000);
178 result &= AvalancheTest< Blob< 72>, hashtype > (hash,300000);
179 result &= AvalancheTest< Blob< 80>, hashtype > (hash,300000);
180 result &= AvalancheTest< Blob< 88>, hashtype > (hash,300000);
181 result &= AvalancheTest< Blob< 96>, hashtype > (hash,300000);
182 result &= AvalancheTest< Blob<104>, hashtype > (hash,300000);
183 result &= AvalancheTest< Blob<112>, hashtype > (hash,300000);
184 result &= AvalancheTest< Blob<120>, hashtype > (hash,300000);
185
186 result &= AvalancheTest< Blob<128>, hashtype > (hash,300000);
187 result &= AvalancheTest< Blob<136>, hashtype > (hash,300000);
188 result &= AvalancheTest< Blob<144>, hashtype > (hash,300000);
189 result &= AvalancheTest< Blob<152>, hashtype > (hash,300000);
190 result &= AvalancheTest< Blob<160>, hashtype > (hash,300000);
191 result &= AvalancheTest< Blob<168>, hashtype > (hash,300000);
192 result &= AvalancheTest< Blob<176>, hashtype > (hash,300000);
193 result &= AvalancheTest< Blob<184>, hashtype > (hash,300000);
194 result &= AvalancheTest< Blob<192>, hashtype > (hash,300000);
195 result &= AvalancheTest< Blob<200>, hashtype > (hash,300000);
196 result &= AvalancheTest< Blob<208>, hashtype > (hash,300000);
197 result &= AvalancheTest< Blob<216>, hashtype > (hash,300000);
198 result &= AvalancheTest< Blob<224>, hashtype > (hash,300000);
199 result &= AvalancheTest< Blob<232>, hashtype > (hash,300000);
200 result &= AvalancheTest< Blob<240>, hashtype > (hash,300000);
201 result &= AvalancheTest< Blob<248>, hashtype > (hash,300000);
202
203 result &= AvalancheTest< Blob<256>, hashtype > (hash,300000);
204
205 //result &= AvalancheTest< Blob<hashbits * 2>, hashtype > (hash,200000);
206 //result &= AvalancheTest< Blob<768>, hashtype > (hash,2000000);
tanjent@gmail.comad4b3632010-11-05 01:20:58 +0000207
208 // The bit independence test is slow and not particularly useful...
209 //result &= BicTest < Blob<hashbits * 2>, hashtype > ( hash, 1000000 );
210
211 if(!result) printf("*********FAIL*********\n");
212 printf("\n");
213 }
214
215 //-----------------------------------------------------------------------------
216 // Keyset 'Cyclic'
217
218 if(g_testCyclic || g_testAll)
219 {
220 printf("[[[ Keyset 'Cyclic' Tests ]]]\n\n");
221
222 bool result = true;
223 bool drawDiagram = false;
224
225 result &= CyclicKeyTest<hashtype>(hash,sizeof(hashtype)+0,8,10000000,drawDiagram);
226 result &= CyclicKeyTest<hashtype>(hash,sizeof(hashtype)+1,8,10000000,drawDiagram);
227 result &= CyclicKeyTest<hashtype>(hash,sizeof(hashtype)+2,8,10000000,drawDiagram);
228 result &= CyclicKeyTest<hashtype>(hash,sizeof(hashtype)+3,8,10000000,drawDiagram);
229 result &= CyclicKeyTest<hashtype>(hash,sizeof(hashtype)+4,8,10000000,drawDiagram);
230
231 if(!result) printf("*********FAIL*********\n");
232 printf("\n");
233 }
234
235 //-----------------------------------------------------------------------------
236 // Keyset 'Sparse'
237
238 if(g_testSparse || g_testAll)
239 {
240 printf("[[[ Keyset 'Sparse' Tests ]]]\n\n");
241
242 bool result = true;
243 bool drawDiagram = false;
244
245 result &= SparseKeyTest< 32,hashtype>(hash,6,true,true,true,drawDiagram);
246 result &= SparseKeyTest< 40,hashtype>(hash,6,true,true,true,drawDiagram);
247 result &= SparseKeyTest< 48,hashtype>(hash,5,true,true,true,drawDiagram);
248 result &= SparseKeyTest< 56,hashtype>(hash,5,true,true,true,drawDiagram);
249 result &= SparseKeyTest< 64,hashtype>(hash,5,true,true,true,drawDiagram);
250 result &= SparseKeyTest< 96,hashtype>(hash,4,true,true,true,drawDiagram);
251 result &= SparseKeyTest< 256,hashtype>(hash,3,true,true,true,drawDiagram);
252 result &= SparseKeyTest<2048,hashtype>(hash,2,true,true,true,drawDiagram);
253
254 if(!result) printf("*********FAIL*********\n");
255 printf("\n");
256 }
257
258 //-----------------------------------------------------------------------------
259 // Keyset 'Permutation'
260
261 if(g_testPermutation || g_testAll)
262 {
tanjent@gmail.comad4b3632010-11-05 01:20:58 +0000263 {
tanjent@gmail.combabb5532011-02-28 06:03:12 +0000264 // This one breaks lookup3, surprisingly
tanjent@gmail.comad4b3632010-11-05 01:20:58 +0000265
tanjent@gmail.combabb5532011-02-28 06:03:12 +0000266 printf("[[[ Keyset 'Combination Lowbits' Tests ]]]\n\n");
tanjent@gmail.comad4b3632010-11-05 01:20:58 +0000267
tanjent@gmail.combabb5532011-02-28 06:03:12 +0000268 bool result = true;
269 bool drawDiagram = false;
tanjent@gmail.comad4b3632010-11-05 01:20:58 +0000270
tanjent@gmail.combabb5532011-02-28 06:03:12 +0000271 uint32_t blocks[] =
272 {
273 0x00000000,
274
275 0x00000001, 0x00000002, 0x00000003, 0x00000004, 0x00000005, 0x00000006, 0x00000007,
276 };
277
278 result &= CombinationKeyTest<hashtype>(hash,8,blocks,sizeof(blocks) / sizeof(uint32_t),true,true,drawDiagram);
279
280 if(!result) printf("*********FAIL*********\n");
281 printf("\n");
282 }
283
284 {
285 printf("[[[ Keyset 'Combination Highbits' Tests ]]]\n\n");
286
287 bool result = true;
288 bool drawDiagram = false;
289
290 uint32_t blocks[] =
291 {
292 0x00000000,
293
294 0x20000000, 0x40000000, 0x60000000, 0x80000000, 0xA0000000, 0xC0000000, 0xE0000000
295 };
296
297 result &= CombinationKeyTest<hashtype>(hash,8,blocks,sizeof(blocks) / sizeof(uint32_t),true,true,drawDiagram);
298
299 if(!result) printf("*********FAIL*********\n");
300 printf("\n");
301 }
302
303 {
304 printf("[[[ Keyset 'Combination 0x8000000' Tests ]]]\n\n");
305
306 bool result = true;
307 bool drawDiagram = false;
308
309 uint32_t blocks[] =
310 {
311 0x00000000,
312
313 0x80000000,
314 };
315
316 result &= CombinationKeyTest<hashtype>(hash,20,blocks,sizeof(blocks) / sizeof(uint32_t),true,true,drawDiagram);
317
318 if(!result) printf("*********FAIL*********\n");
319 printf("\n");
320 }
321
322 {
323 printf("[[[ Keyset 'Combination 0x0000001' Tests ]]]\n\n");
324
325 bool result = true;
326 bool drawDiagram = false;
327
328 uint32_t blocks[] =
329 {
330 0x00000000,
331
332 0x00000001,
333 };
334
335 result &= CombinationKeyTest<hashtype>(hash,20,blocks,sizeof(blocks) / sizeof(uint32_t),true,true,drawDiagram);
336
337 if(!result) printf("*********FAIL*********\n");
338 printf("\n");
339 }
340
341 {
342 printf("[[[ Keyset 'Combination Hi-Lo' Tests ]]]\n\n");
343
344 bool result = true;
345 bool drawDiagram = false;
346
347 uint32_t blocks[] =
348 {
349 0x00000000,
350
351 0x00000001, 0x00000002, 0x00000003, 0x00000004, 0x00000005, 0x00000006, 0x00000007,
352
353 0x80000000, 0x40000000, 0xC0000000, 0x20000000, 0xA0000000, 0x60000000, 0xE0000000
354 };
355
356 result &= CombinationKeyTest<hashtype>(hash,6,blocks,sizeof(blocks) / sizeof(uint32_t),true,true,drawDiagram);
357
358 if(!result) printf("*********FAIL*********\n");
359 printf("\n");
360 }
361
362 //----------
363
364 /*
365 {
366 printf("[[[ Keyset 'Permutation' Tests ]]]\n\n");
367
368 bool result = true;
369 bool drawDiagram = false;
370
371 // This very sparse set of blocks is particularly hard for SuperFastHash
372
373 uint32_t blocks[] =
374 {
375 0x00000000,
376 0x00000001,
377 0x00000002,
378
379 0x00000400,
380 0x00008000,
381
382 0x00080000,
383 0x00200000,
384
385 0x20000000,
386 0x40000000,
387 0x80000000,
388 };
389
390 result &= PermutationKeyTest<hashtype>(hash,blocks,sizeof(blocks) / sizeof(uint32_t),true,true,drawDiagram);
391
392 if(!result) printf("*********FAIL*********\n");
393 printf("\n");
394 }
395 */
tanjent@gmail.comad4b3632010-11-05 01:20:58 +0000396 }
397
398 //-----------------------------------------------------------------------------
399 // Keyset 'Window'
400
401 // Skip distribution test for these - they're too easy to distribute well,
402 // and it generates a _lot_ of testing
403
404 if(g_testWindow || g_testAll)
405 {
406 printf("[[[ Keyset 'Window' Tests ]]]\n\n");
407
408 bool result = true;
409 bool testCollision = true;
410 bool testDistribution = false;
411 bool drawDiagram = false;
412
413 result &= WindowedKeyTest< Blob<hashbits*2>, hashtype > ( hash, 20, testCollision, testDistribution, drawDiagram );
414
415 if(!result) printf("*********FAIL*********\n");
416 printf("\n");
417 }
418
419 //-----------------------------------------------------------------------------
420 // Keyset 'Text'
421
422 if(g_testText || g_testAll)
423 {
424 printf("[[[ Keyset 'Text' Tests ]]]\n\n");
425
426 bool result = true;
427 bool drawDiagram = false;
428
429 const char * alnum = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
430
431 result &= TextKeyTest( hash, "Foo", alnum,4, "Bar", drawDiagram );
432 result &= TextKeyTest( hash, "FooBar", alnum,4, "", drawDiagram );
433 result &= TextKeyTest( hash, "", alnum,4, "FooBar", drawDiagram );
434
435 if(!result) printf("*********FAIL*********\n");
436 printf("\n");
437 }
438
439 //-----------------------------------------------------------------------------
440 // Keyset 'Zeroes'
441
442 if(g_testZeroes || g_testAll)
443 {
444 printf("[[[ Keyset 'Zeroes' Tests ]]]\n\n");
445
446 bool result = true;
447 bool drawDiagram = false;
448
449 result &= ZeroKeyTest<hashtype>( hash, drawDiagram );
450
451 if(!result) printf("*********FAIL*********\n");
452 printf("\n");
453 }
454
455 //-----------------------------------------------------------------------------
456 // Keyset 'Seed'
457
458 if(g_testSeed || g_testAll)
459 {
460 printf("[[[ Keyset 'Seed' Tests ]]]\n\n");
461
462 bool result = true;
463 bool drawDiagram = false;
464
465 result &= SeedTest<hashtype>( hash, 1000000, drawDiagram );
466
467 if(!result) printf("*********FAIL*********\n");
468 printf("\n");
469 }
tanjent@gmail.com7e5c3632010-11-02 00:50:04 +0000470}
471
472//-----------------------------------------------------------------------------
473
tanjent@gmail.comad4b3632010-11-05 01:20:58 +0000474void testHash ( const char * name )
475{
476 HashInfo * pInfo = findHash(name);
tanjent@gmail.com7e5c3632010-11-02 00:50:04 +0000477
tanjent@gmail.comad4b3632010-11-05 01:20:58 +0000478 if(pInfo == NULL)
479 {
480 printf("Invalid hash '%s' specified\n",name);
481 return;
482 }
483 else
484 {
485 if(pInfo->hashbits == 32)
486 {
487 test<uint32_t>( pInfo->hash, pInfo->desc );
488 }
489 else if(pInfo->hashbits == 64)
490 {
491 test<uint64_t>( pInfo->hash, pInfo->desc );
492 }
493 else if(pInfo->hashbits == 128)
494 {
495 test<uint128_t>( pInfo->hash, pInfo->desc );
496 }
tanjent@gmail.combabb5532011-02-28 06:03:12 +0000497 else if(pInfo->hashbits == 256)
498 {
499 test<uint256_t>( pInfo->hash, pInfo->desc );
500 }
tanjent@gmail.comad4b3632010-11-05 01:20:58 +0000501 else
502 {
503 printf("Invalid hash bit width %d for hash '%s'",pInfo->hashbits,pInfo->name);
504 }
505 }
506}
507//-----------------------------------------------------------------------------
508
tanjent@gmail.comad4b3632010-11-05 01:20:58 +0000509int main ( int argc, char ** argv )
tanjent@gmail.com7e5c3632010-11-02 00:50:04 +0000510{
tanjent@gmail.comf67ce942011-03-14 09:11:18 +0000511 SetAffinity(2);
tanjent@gmail.com7e5c3632010-11-02 00:50:04 +0000512
513 int a = clock();
514
tanjent@gmail.comad4b3632010-11-05 01:20:58 +0000515 g_testAll = true;
516
tanjent@gmail.comad4b3632010-11-05 01:20:58 +0000517 //g_testSanity = true;
518 //g_testSpeed = true;
519 //g_testAvalanche = true;
tanjent@gmail.combabb5532011-02-28 06:03:12 +0000520 //g_testCyclic = true;
tanjent@gmail.comad4b3632010-11-05 01:20:58 +0000521 //g_testDiff = true;
522 //g_testSparse = true;
523 //g_testPermutation = true;
tanjent@gmail.combabb5532011-02-28 06:03:12 +0000524 //g_testZeroes = true;
tanjent@gmail.comad4b3632010-11-05 01:20:58 +0000525
tanjent@gmail.com31a9e8e2010-11-09 20:29:19 +0000526 //testHash("count");
tanjent@gmail.com31a9e8e2010-11-09 20:29:19 +0000527 //printf("Called the hash function %I64d times, %I64d bytes hashed\n",g_hashcount,g_bytecount);
528
tanjent@gmail.comf67ce942011-03-14 09:11:18 +0000529 testHash("murmur3f");
tanjent@gmail.comad4b3632010-11-05 01:20:58 +0000530
tanjent@gmail.com7e5c3632010-11-02 00:50:04 +0000531 //----------
532
tanjent@gmail.com7e5c3632010-11-02 00:50:04 +0000533 int b = clock();
534
535 printf("time %d\n",b-a);
tanjent@gmail.comad4b3632010-11-05 01:20:58 +0000536
537 return 0;
tanjent@gmail.combabb5532011-02-28 06:03:12 +0000538}