WiscSort / gensort-1.5 / gensort.c
gensort.c
Raw
/* gensort.c - Generator program for sort benchmarks.
 *
 * $Date: 2013/03/15 19:03:37 $
 * Chris Nyberg <chris.nyberg@ordinal.com>
 *
 * Copyright (C) 2009 - 2013
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of Version 2 of the GNU General Public
 * License as published by the Free Software Foundation.
 * 
 * This program 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.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
char *Version = "1.5";

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "rand16.h"
#include <zlib.h>   /* use crc32() function in zlib */

#if defined(SUMP_PUMP)
# include "sump.h"
# include <fcntl.h>

/* Number of records to be generated per output block */
# define BLK_RECS       100000

/* "instruction" structure that is passed to sump pump threads */
struct gen_instruct
{
    u16         starting_rec;   /* starting record number */
    u8          num_recs;       /* the number of records to generate */
};

#endif

#define REC_SIZE       100
#define SKEW_BYTES     6
#define HEX_DIGIT(x) ((x) >= 10 ? 'A' + (x) - 10 : '0' + (x))

/* Structure for a 10-deep queue of random numbers.  This queue is used
 * to cheaply create records where every byte is psuedo random, while
 * only creating one 128-bit number per record.  The 10-byte keys that
 * begin each record are generated using the top 10 bytes of a random
 * number (this is exactly the same as was done in the original gensort
 * program).  The next 90 bytes of each record are broken into 9 10-byte
 * parts.  Each part is generated using subsequent random numbers in the
 * queue xor'ed with a constant that is particular to that part. 
 */
#define QUEUE_SIZE      10
#define get_queue_rand(rq, index) (rq->rand[(index + rq->head_index) % QUEUE_SIZE])
typedef struct
{
    int         head_index;             /* index of head of queue in rand[] */
    u16         curr_rec_number;        /* current record number */
    u16         rand[QUEUE_SIZE];       /* circular queue of random numbers */
    int         skew_index;             /* index into key skews; approximately
                                         * log2(current_rec_number) + 1 */
} rand_queue;
#define RQ(rq, i) (rq->rand[rq->head_index + i - (rq->head_index + i >= QUEUE_SIZE ? QUEUE_SIZE : 0)])
#define ASSIGN_10_BYTES(rec_buf, rand) \
    (rec_buf)[0] = (rand.hi8 >> 56) & 0xFF; \
    (rec_buf)[1] = (rand.hi8 >> 48) & 0xFF; \
    (rec_buf)[2] = (rand.hi8 >> 40) & 0xFF; \
    (rec_buf)[3] = (rand.hi8 >> 32) & 0xFF; \
    (rec_buf)[4] = (rand.hi8 >> 24) & 0xFF; \
    (rec_buf)[5] = (rand.hi8 >> 16) & 0xFF; \
    (rec_buf)[6] = (rand.hi8 >>  8) & 0xFF; \
    (rec_buf)[7] = (rand.hi8 >>  0) & 0xFF; \
    (rec_buf)[8] = (rand.lo8 >> 56) & 0xFF; \
    (rec_buf)[9] = (rand.lo8 >> 48) & 0xFF

int         Print_checksum;     /* boolean to produce record checksum */
u16         Sum16;              /* record checksum */
void        (*Gen)(unsigned char *buf, rand_queue *rq); /* ptr to generator */
int         Skip_output;        /* boolean to skip output */

unsigned char Skew_binary[129][SKEW_BYTES] = {
    /*   0 */ { 0x4a, 0x69, 0x6d, 0x47, 0x72, 0x61 },
    /*   1 */ { 0x95, 0xe0, 0xe4, 0x82, 0x62, 0xb3 },
    /*   2 */ { 0x45, 0x97, 0x93, 0x53, 0xdb, 0xed },
    /*   3 */ { 0x88, 0x2a, 0x02, 0xc3, 0x15, 0x36 },
    /*   4 */ { 0x5c, 0x90, 0xab, 0x38, 0xae, 0x52 },
    /*   5 */ { 0x72, 0xdc, 0x0c, 0xa5, 0x1e, 0x33 },
    /*   6 */ { 0x10, 0x43, 0x1a, 0xf6, 0xa0, 0xd8 },
    /*   7 */ { 0x5e, 0xfc, 0x4a, 0xbf, 0xac, 0xa2 },
    /*   8 */ { 0x44, 0xf7, 0x8c, 0x8b, 0x40, 0xbf },
    /*   9 */ { 0x84, 0xc0, 0x99, 0x2f, 0x3b, 0x94 },
    /*  10 */ { 0xb3, 0xe9, 0x68, 0x9d, 0xe1, 0x6b },
    /*  11 */ { 0xf8, 0xf6, 0x42, 0x63, 0xfd, 0x0b },
    /*  12 */ { 0xda, 0x7a, 0x45, 0xa1, 0x82, 0xde },
    /*  13 */ { 0x9b, 0x6b, 0x48, 0x25, 0xe2, 0x51 },
    /*  14 */ { 0xdc, 0x68, 0x2a, 0x00, 0x64, 0x7e },
    /*  15 */ { 0xf2, 0x5b, 0xd1, 0x54, 0x39, 0xd1 },
    /*  16 */ { 0xf2, 0xfa, 0x42, 0xed, 0x18, 0x72 },
    /*  17 */ { 0x6a, 0x59, 0x45, 0x1b, 0xe8, 0xd0 },
    /*  18 */ { 0x27, 0x29, 0xb9, 0x77, 0x14, 0x71 },
    /*  19 */ { 0x87, 0x9b, 0x2f, 0xb7, 0xbb, 0x35 },
    /*  20 */ { 0x68, 0xd0, 0xcc, 0x3c, 0x19, 0x99 },
    /*  21 */ { 0x27, 0xd8, 0x08, 0x79, 0xd7, 0x9e },
    /*  22 */ { 0xb0, 0x79, 0x50, 0x11, 0xb7, 0x82 },
    /*  23 */ { 0x46, 0x4f, 0xb8, 0x4a, 0xb8, 0x48 },
    /*  24 */ { 0x21, 0xf0, 0x3e, 0xe8, 0xac, 0x41 },
    /*  25 */ { 0xe7, 0x96, 0x1c, 0x0d, 0x82, 0x7f },
    /*  26 */ { 0x84, 0xd9, 0x04, 0x45, 0x7a, 0x61 },
    /*  27 */ { 0x53, 0x59, 0xd3, 0x5d, 0xa8, 0x84 },
    /*  28 */ { 0x4e, 0x38, 0x54, 0x66, 0x52, 0x5c },
    /*  29 */ { 0x87, 0x0f, 0xa6, 0x45, 0x90, 0x11 },
    /*  30 */ { 0xff, 0x00, 0x46, 0x3a, 0xdf, 0xc8 },
    /*  31 */ { 0x89, 0xca, 0x67, 0xc2, 0x9c, 0x93 },
    /*  32 */ { 0x75, 0x50, 0x90, 0xc0, 0x17, 0x7d },
    /*  33 */ { 0xeb, 0x4d, 0x81, 0xa5, 0xc9, 0xea },
    /*  34 */ { 0x8a, 0x85, 0x68, 0xb3, 0x08, 0x6f },
    /*  35 */ { 0x5d, 0xa6, 0x9a, 0x3d, 0x86, 0x67 },
    /*  36 */ { 0x6a, 0x97, 0x43, 0x59, 0xea, 0xab },
    /*  37 */ { 0x63, 0xb6, 0x04, 0x4b, 0x8e, 0x78 },
    /*  38 */ { 0x33, 0x41, 0x49, 0x12, 0xcb, 0x67 },
    /*  39 */ { 0x22, 0x6d, 0xf2, 0xb7, 0x9c, 0x9b },
    /*  40 */ { 0x1e, 0x58, 0x39, 0x6c, 0x59, 0x9a },
    /*  41 */ { 0x4d, 0x67, 0x60, 0x91, 0xdc, 0xfe },
    /*  42 */ { 0xc9, 0x8f, 0x25, 0x9b, 0x15, 0x0d },
    /*  43 */ { 0xa8, 0x27, 0xdc, 0x9a, 0xff, 0x7e },
    /*  44 */ { 0x06, 0x96, 0xc9, 0xa1, 0xba, 0x3b },
    /*  45 */ { 0x6d, 0x16, 0xe3, 0x38, 0xd7, 0x77 },
    /*  46 */ { 0xac, 0x35, 0xa4, 0x3b, 0xa6, 0x62 },
    /*  47 */ { 0x7e, 0xe1, 0xe4, 0x00, 0x71, 0x63 },
    /*  48 */ { 0xa1, 0x6b, 0x6f, 0xa9, 0xf1, 0xea },
    /*  49 */ { 0x2c, 0xb7, 0xa1, 0xbb, 0x93, 0x62 },
    /*  50 */ { 0x2f, 0x4b, 0x08, 0x26, 0x7c, 0xe7 },
    /*  51 */ { 0x86, 0xd1, 0x92, 0xc5, 0x41, 0x84 },
    /*  52 */ { 0xf6, 0xe4, 0x14, 0x3f, 0xde, 0xaa },
    /*  53 */ { 0x45, 0x83, 0x69, 0xe8, 0x3c, 0xb9 },
    /*  54 */ { 0x6c, 0x15, 0xf7, 0x0f, 0x81, 0x76 },
    /*  55 */ { 0xc0, 0xb4, 0x87, 0x02, 0x6b, 0x7f },
    /*  56 */ { 0xae, 0x90, 0x31, 0xf8, 0x7d, 0x14 },
    /*  57 */ { 0x6b, 0x25, 0xdc, 0x59, 0xe0, 0x9e },
    /*  58 */ { 0x88, 0x38, 0x23, 0x62, 0x42, 0x4b },
    /*  59 */ { 0xaf, 0xb9, 0x6f, 0x95, 0xd3, 0x2b },
    /*  60 */ { 0xc1, 0xd4, 0xfc, 0xf5, 0x77, 0xdb },
    /*  61 */ { 0xc6, 0x8d, 0x66, 0xd1, 0x84, 0x53 },
    /*  62 */ { 0x74, 0xfe, 0x19, 0xdc, 0x52, 0x68 },
    /*  63 */ { 0x8b, 0x6a, 0xe0, 0x36, 0x71, 0x3b },
    /*  64 */ { 0x33, 0xd5, 0xb5, 0xb1, 0x5c, 0x70 },
    /*  65 */ { 0x5e, 0x46, 0xf5, 0x43, 0x2f, 0x2c },
    /*  66 */ { 0x26, 0x55, 0x46, 0x25, 0xdd, 0x68 },
    /*  67 */ { 0xf6, 0xed, 0xf4, 0xe3, 0xba, 0xfd },
    /*  68 */ { 0xcf, 0x9f, 0xb7, 0x8a, 0xa3, 0xca },
    /*  69 */ { 0x08, 0x14, 0x09, 0x8c, 0x2d, 0x9a },
    /*  70 */ { 0xea, 0x1c, 0xfc, 0x70, 0xfb, 0x3f },
    /*  71 */ { 0x68, 0xed, 0xe8, 0x28, 0xd4, 0xc5 },
    /*  72 */ { 0x86, 0x67, 0xc9, 0xb9, 0xbb, 0x8c },
    /*  73 */ { 0xe7, 0xaf, 0xa5, 0x12, 0x6f, 0x3d },
    /*  74 */ { 0xd0, 0x01, 0x02, 0xa1, 0xc5, 0x10 },
    /*  75 */ { 0xf9, 0x54, 0x9b, 0x14, 0x3a, 0x9e },
    /*  76 */ { 0xda, 0x0f, 0x75, 0x54, 0xe7, 0x9e },
    /*  77 */ { 0xca, 0x16, 0xea, 0x9b, 0x71, 0xf0 },
    /*  78 */ { 0xf9, 0x5a, 0x03, 0x5a, 0x6b, 0xe8 },
    /*  79 */ { 0xf3, 0xf0, 0x37, 0x8f, 0x70, 0x43 },
    /*  80 */ { 0xbb, 0x4d, 0x8a, 0x4f, 0xd7, 0x6c },
    /*  81 */ { 0xc9, 0x4a, 0x04, 0x75, 0x3d, 0xfc },
    /*  82 */ { 0x30, 0x9a, 0x89, 0x71, 0x88, 0x29 },
    /*  83 */ { 0xdd, 0xa5, 0x70, 0x75, 0xdf, 0x7a },
    /*  84 */ { 0xa6, 0x61, 0xcd, 0xc3, 0x16, 0x22 },
    /*  85 */ { 0xc5, 0x96, 0x93, 0x15, 0x25, 0x8c },
    /*  86 */ { 0x3a, 0x16, 0x93, 0xac, 0x95, 0x3b },
    /*  87 */ { 0xe9, 0x0e, 0x58, 0x7d, 0xf6, 0x9f },
    /*  88 */ { 0x8f, 0xc9, 0x47, 0x45, 0xb2, 0xfd },
    /*  89 */ { 0xa7, 0x6f, 0xd6, 0xfc, 0x71, 0x78 },
    /*  90 */ { 0x4c, 0x67, 0x4c, 0xe2, 0x3a, 0x86 },
    /*  91 */ { 0xf0, 0x05, 0xc4, 0x06, 0x15, 0x58 },
    /*  92 */ { 0x2a, 0x90, 0xa6, 0x7e, 0x8c, 0x6c },
    /*  93 */ { 0x5a, 0xdc, 0xee, 0x8c, 0xa7, 0x09 },
    /*  94 */ { 0xff, 0x81, 0xed, 0x50, 0xd5, 0x78 },
    /*  95 */ { 0xed, 0x44, 0x53, 0x6c, 0x44, 0x16 },
    /*  96 */ { 0x64, 0x8e, 0x48, 0x56, 0x64, 0x1a },
    /*  97 */ { 0xa4, 0x47, 0x3f, 0x64, 0xf9, 0xd0 },
    /*  98 */ { 0x6e, 0x45, 0xfb, 0x3d, 0x1c, 0x2c },
    /*  99 */ { 0x3c, 0xb4, 0x46, 0xe3, 0x07, 0x0c },
    /* 100 */ { 0x0a, 0x25, 0xa9, 0x9a, 0xf4, 0x39 },
    /* 101 */ { 0x2c, 0xb5, 0xa1, 0xdc, 0xef, 0x47 },
    /* 102 */ { 0x0e, 0x4d, 0x9c, 0xd4, 0x57, 0xae },
    /* 103 */ { 0x3b, 0x86, 0x6f, 0x4a, 0x1a, 0xef },
    /* 104 */ { 0x3e, 0x98, 0xbe, 0xe5, 0xfd, 0xf5 },
    /* 105 */ { 0x99, 0x9a, 0x6d, 0x40, 0xa4, 0x3f },
    /* 106 */ { 0xf7, 0xe8, 0xb4, 0x8b, 0xaa, 0xf9 },
    /* 107 */ { 0xef, 0xe5, 0x08, 0x20, 0x54, 0x1e },
    /* 108 */ { 0xf7, 0xd1, 0x98, 0x23, 0x53, 0x67 },
    /* 109 */ { 0x21, 0xa5, 0x8b, 0xdd, 0x20, 0x20 },
    /* 110 */ { 0xed, 0x59, 0xb7, 0x23, 0xb1, 0x6e },
    /* 111 */ { 0x20, 0xd1, 0xef, 0x94, 0x2f, 0x79 },
    /* 112 */ { 0x8f, 0x23, 0x46, 0xa3, 0x2a, 0xf7 },
    /* 113 */ { 0xb0, 0x98, 0x61, 0xcc, 0x8b, 0x8a },
    /* 114 */ { 0xb5, 0xe2, 0x63, 0x33, 0x3a, 0x0d },
    /* 115 */ { 0x63, 0xc1, 0xb7, 0xe7, 0x2b, 0x41 },
    /* 116 */ { 0xaf, 0x90, 0x85, 0x9b, 0x1c, 0xa9 },
    /* 117 */ { 0x9a, 0x52, 0x5e, 0x2f, 0x33, 0xbf },
    /* 118 */ { 0xc2, 0x83, 0xea, 0x63, 0xf3, 0x00 },
    /* 119 */ { 0x02, 0x0d, 0xe5, 0x60, 0x00, 0xf6 },
    /* 120 */ { 0x55, 0xcf, 0xe9, 0xd4, 0x3d, 0x64 },
    /* 121 */ { 0xb5, 0xd7, 0x69, 0x82, 0x36, 0x39 },
    /* 122 */ { 0xe6, 0x29, 0xca, 0xb5, 0x3c, 0xa1 },
    /* 123 */ { 0x9c, 0xbf, 0xeb, 0x07, 0x9d, 0xde },
    /* 124 */ { 0xa0, 0xba, 0x1e, 0xd1, 0xea, 0x79 },
    /* 125 */ { 0x0b, 0xe5, 0x49, 0xa5, 0x12, 0xd3 },
    /* 126 */ { 0x78, 0x70, 0xde, 0x1f, 0xc5, 0x61 },
    /* 127 */ { 0x98, 0xa2, 0x54, 0x2f, 0xd2, 0x3d },
    /* 128 */ { 0xe1, 0xdc, 0x46, 0xb6, 0x45, 0xc4 } };

unsigned char Skew_ascii[129][SKEW_BYTES] = {
    /*   0 */ { 'A', 's', 'f', 'A', 'G', 'H' },
    /*   1 */ { '~', 's', 'H', 'd', '0', 'j' },
    /*   2 */ { 'u', 'I', '^', 'E', 'Y', 'm' },
    /*   3 */ { 'Q', ')', 'J', 'N', ')', 'R' },
    /*   4 */ { 'o', '4', 'F', 'o', 'B', 'k' },
    /*   5 */ { '*', '}', '-', 'W', 'z', '1' },
    /*   6 */ { '0', 'f', 's', 's', 'x', '}' },
    /*   7 */ { 'm', 'z', '4', 'V', 'C', 'N' },
    /*   8 */ { 'm', 'y', '+', '=', '5', 'r' },
    /*   9 */ { '5', 'H', 'A', '\\', 'z', '%' },
    /*  10 */  { '`', 'P', 'k', 'X', 'Q', '<' },
    /*  11 */  { 'G', 'L', 'S', 'n', 'l', 'm' },
    /*  12 */  { 's', 'w', 'B', 'z', 'Q', '#' },
    /*  13 */  { '9', 'S', 'C', '<', 'z', ' ' },
    /*  14 */  { 'o', '7', '~', 'd', 'r', 's' },
    /*  15 */  { 'K', '~', '%', 'q', '4', ']' },
    /*  16 */  { 'g', 'j', '<', 'm', '5', 'S' },
    /*  17 */  { '&', '~', '2', 'C', 'h', '{' },
    /*  18 */  { 'B', ']', 'a', 'z', '\'', '~' },
    /*  19 */  { ',', '2', 'K', ',', 'D', 'K' },
    /*  20 */  { '}', '[', 'v', '>', 'z', 'y' },
    /*  21 */  { 'k', 'X', 'r', 'G', '^', 'e' },
    /*  22 */  { 'v', '%', '^', 'R', 'G', '^' },
    /*  23 */  { 'v', 'i', '@', '>', '8', 'j' },
    /*  24 */  { '}', 'd', 'A', '7', '*', '<' },
    /*  25 */  { '>', 'Y', '$', 'K', 'c', ')' },
    /*  26 */  { 'u', 'F', '?', 'T', ';', '*' },
    /*  27 */  { '~', '%', '.', '@', '@', ',' },
    /*  28 */  { 'J', 'P', 'r', 'A', 'K', 'c' },
    /*  29 */  { '*', 'i', '@', 'l', 'F', '3' },
    /*  30 */  { '9', 'U', 'H', '>', '%', 'F' },
    /*  31 */  { '-', '^', '1', '~', '=', 'a' },
    /*  32 */  { '.', '\\', 'K', 'q', 'T', '&' },
    /*  33 */  { '9', 'R', 't', 's', 'P', 'a' },
    /*  34 */  { 'F', 'q', 'y', '~', ']', 'O' },
    /*  35 */  { 'W', 'j', '-', '%', '?', 'e' },
    /*  36 */  { 'R', 'v', ']', 'Q', 'H', 'o' },
    /*  37 */  { '1', ' ', 'e', '!', 'o', 'g' },
    /*  38 */  { 'N', ',', 'g', 'k', '>', '3' },
    /*  39 */  { 's', '.', '7', '2', 'Y', '.' },
    /*  40 */  { 'j', '*', '0', '`', 'g', 'c' },
    /*  41 */  { 'R', '+', 'S', 'h', '=', 'K' },
    /*  42 */  { 'x', ']', 'w', ']', '=', 'D' },
    /*  43 */  { 'm', 'a', 'U', '!', 'U', 'o' },
    /*  44 */  { 's', 'm', 'N', '$', 'i', ' ' },
    /*  45 */  { 'T', '}', '#', ' ', '`', '9' },
    /*  46 */  { 'G', '2', 'o', 'M', 'V', 'H' },
    /*  47 */  { '(', 't', 'v', '1', '\'', '>' },
    /*  48 */  { 'R', 'E', '%', 'Z', '{', 'o' },
    /*  49 */  { '\'', '%', ':', 'Y', 'Y', '?' },
    /*  50 */  { 'N', '(', 'J', 'u', 'G', '%' },
    /*  51 */  { '`', 'E', '$', ',', 'x', 'R' },
    /*  52 */  { 'j', 'z', 'u', '$', 'F', 'n' },
    /*  53 */  { '|', 'x', 'a', 'P', 'O', '\\' },
    /*  54 */  { 'G', 'C', 'U', '4', 'g', 'f' },
    /*  55 */  { 'J', 'U', '9', 's', '0', '3' },
    /*  56 */  { 'D', '@', 'v', '9', '"', '"' },
    /*  57 */  { 'O', 'f', 'f', 'B', '8', '(' },
    /*  58 */  { '-', '_', ')', ' ', 'R', '\'' },
    /*  59 */  { '\'', 'z', '4', 'P', '|', 'n' },
    /*  60 */  { '[', '!', 'N', 'b', 'x', '&' },
    /*  61 */  { 'p', '?', '"', '\\', '\\', '6' },
    /*  62 */  { '+', '2', 'A', '^', 'x', 'P' },
    /*  63 */  { '-', 'c', 'u', 'J', 'v', 'y' },
    /*  64 */  { 'i', 'x', '^', 'T', '<', 'S' },
    /*  65 */  { 'E', 'v', 't', 'L', '^', '`' },
    /*  66 */  { '\\', 'E', '=', 'u', 'j', '=' },
    /*  67 */  { 'C', 'U', '7', 'l', '~', '"' },
    /*  68 */  { ',', 'v', 'W', 'e', '=', '|' },
    /*  69 */  { 'k', '|', 'M', '.', 'e', 'l' },
    /*  70 */  { 'u', '<', '#', 'x', '_', 'G' },
    /*  71 */  { 'K', ' ', '&', '>', 'Q', '-' },
    /*  72 */  { 'E', '(', 'g', '$', '5', '-' },
    /*  73 */  { '~', '\\', 'p', 't', 's', 'n' },
    /*  74 */  { 'Y', 'h', 'l', 'P', 'k', '`' },
    /*  75 */  { 'W', 'B', 'z', 'u', 'l', ',' },
    /*  76 */  { 'q', ')', 'e', 'p', 'J', 's' },
    /*  77 */  { '`', 'W', 'V', 'k', 'W', '&' },
    /*  78 */  { 'H', 'X', 'g', '#', 'Z', '^' },
    /*  79 */  { '-', '&', '8', '$', 'l', 'c' },
    /*  80 */  { '^', 'S', 'p', 'q', 'w', 'o' },
    /*  81 */  { 'P', '-', '6', 'Y', 'b', 'w' },
    /*  82 */  { 'a', '`', ':', ';', 'y', 'd' },
    /*  83 */  { 'd', 'O', ']', 'G', '~', '-' },
    /*  84 */  { 'W', 'M', '^', ';', 'r', 'v' },
    /*  85 */  { '-', 'W', 'w', 'U', '4', 'B' },
    /*  86 */  { 'y', '\'', 'w', '/', 'P', 'Z' },
    /*  87 */  { '#', 'j', 'A', '`', 'l', '-' },
    /*  88 */  { ':', '!', 'W', '.', '=', 'v' },
    /*  89 */  { '"', '!', 'r', '4', 'u', 'g' },
    /*  90 */  { 'O', '?', '(', '|', '_', 'd' },
    /*  91 */  { 'x', 'w', 'W', 'R', 'j', 'k' },
    /*  92 */  { 'T', 'e', ' ', '(', '9', '@' },
    /*  93 */  { '(', 'c', 'b', '6', '2', '1' },
    /*  94 */  { 'h', 'g', '5', 'x', ' ', 's' },
    /*  95 */  { 'w', '5', 'n', 'n', '`', 'w' },
    /*  96 */  { 'q', 'e', 'Q', 'N', 'o', 'B' },
    /*  97 */  { '6', 'n', '>', 'i', 'M', 'b' },
    /*  98 */  { 'u', 'G', 'z', '7', 'S', '=' },
    /*  99 */  { 'r', 'd', ')', 'x', '<', '>' },
    /* 100 */  { 'x', 'E', 'w', 'a', '^', ':' },
    /* 101 */  { '\'', ':', ']', '$', '}', 'b' },
    /* 102 */  { 'v', 'Z', 'l', '"', 'J', 'i' },
    /* 103 */  { 'o', '%', ',', '!', 'f', 'S' },
    /* 104 */  { '*', '+', 'o', '@', '+', 't' },
    /* 105 */  { '(', 'C', 'o', 'K', 'U', 'h' },
    /* 106 */  { 'I', '>', ':', '9', 'J', 'Q' },
    /* 107 */  { 'e', 'S', 'z', 'U', '!', 'X' },
    /* 108 */  { '[', 'Q', 'Z', '{', 'a', '&' },
    /* 109 */  { '?', ':', 'Y', 'p', '4', '%' },
    /* 110 */  { '\'', '_', '>', '/', ' ', '4' },
    /* 111 */  { 'K', 'r', 'N', 'W', '.', '!' },
    /* 112 */  { 'j', '1', '7', 'a', '3', 'a' },
    /* 113 */  { ')', 'o', '-', 'Q', 'L', 'H' },
    /* 114 */  { 'a', 'l', ';', ':', 'O', 'E' },
    /* 115 */  { 'I', '@', 'L', '*', '`', 'H' },
    /* 116 */  { '<', '!', 'p', 'S', 'W', 'F' },
    /* 117 */  { '1', 'w', '@', 'T', 'Z', 'u' },
    /* 118 */  { 'V', 'h', '%', '|', 'K', 'i' },
    /* 119 */  { 'h', '{', 'y', 'd', 'A', 'P' },
    /* 120 */  { ']', 'V', '9', 'h', 'C', 'o' },
    /* 121 */  { '.', 'T', 'K', '[', '[', 'l' },
    /* 122 */  { 'F', 'k', 't', 'H', 's', 'd' },
    /* 123 */  { 'K', 'i', '|', '$', '-', 'M' },
    /* 124 */  { '\\', 'g', 'W', 'C', '3', '[' },
    /* 125 */  { '.', '"', '^', '}', ',', ' ' },
    /* 126 */  { 'k', ' ', 'V', 'C', '?', 'n' },
    /* 127 */  { '}', ']', 'K', 'k', '&', ':' },
    /* 128 */  { '1', '2', 'N', 'S', 'K', '|' } };
    
/* init_rand_queue - initialize a queue of random numbers 
 */
void init_rand_queue(rand_queue *rq, u16 starting_rec_number)
{
    int         i;
    
    rq->head_index = 0;
    rq->curr_rec_number = starting_rec_number;
    rq->rand[0] = next_rand(skip_ahead_rand(rq->curr_rec_number));
    for (i = 1; i < QUEUE_SIZE; i++)
        rq->rand[i] = next_rand(rq->rand[i - 1]);
    rq->skew_index = 0;
}


/* bump_queue - progress random queue to next random number and record number.
 */
void bump_queue(rand_queue *rq)
{
    int tail_index;

    /* head_index is the head of the queue.  find the tail index. */
    tail_index = rq->head_index - 1;
    if (tail_index < 0)
        tail_index = QUEUE_SIZE - 1;

    /* make a new tail entry where the current head is */
    rq->rand[rq->head_index] = next_rand(rq->rand[tail_index]);

    /* bump the head_index to make a new head of the queue */
    if (++rq->head_index == QUEUE_SIZE)
        rq->head_index = 0;

    /* bump the current record number */
    if (++rq->curr_rec_number.lo8 == 0)
        ++rq->curr_rec_number.hi8;
}


/* gen_rec - generate a "binary" record suitable for all sort
 *              benchmarks *except* PennySort.
 */
void gen_rec(unsigned char *rec_buf, rand_queue *rq)
{
    u16         rand;
    
    /* generate the 10-byte key using the high 10 bytes of the 1st 128-bit
     * random number
     */
    rand = RQ(rq, 0);
    ASSIGN_10_BYTES(rec_buf + 0, rand);

    /* generate next 10 bytes using 2nd random number, xor with a constant
     * that is specific to the 2nd ten bytes.  This could allow a
     * a rogue entrant to compress the sort input 10 : 1 using these
     * same xor constants, but this would violate the sort contest rules.
     * The psuedo-random records should make ineffective any inadvertent
     * compression at the hardware or lower levels of the network protocols.
     */
    rand = RQ(rq, 1);
    rand.hi8 ^= 0xF0E8E4E2E1D8D4D2LL;   rand.lo8 ^= 0xD1CC000000000000LL;
    ASSIGN_10_BYTES(rec_buf + 10, rand);

    /* get next 10 bytes using 3rd random number, xor with specific constant
     */
    rand = RQ(rq, 2);
    rand.hi8 ^= 0xCAC9C6C5C3B8B4B2LL;   rand.lo8 ^= 0xB1AC000000000000LL;
    ASSIGN_10_BYTES(rec_buf + 20, rand);

    /* get next 10 bytes using 4th random number, xor with specific constant
     */
    rand = RQ(rq, 3);
    rand.hi8 ^= 0xAAA9A6A5A39C9A99LL;   rand.lo8 ^= 0x9695000000000000LL;
    ASSIGN_10_BYTES(rec_buf + 30, rand);

    /* get next 10 bytes using 5th random number, xor with specific constant
     */
    rand = RQ(rq, 4);
    rand.hi8 ^= 0x938E8D8B87787472LL;   rand.lo8 ^= 0x716C000000000000LL;
    ASSIGN_10_BYTES(rec_buf + 40, rand);

    /* get next 10 bytes using 6th random number, xor with specific constant
     */
    rand = RQ(rq, 5);
    rand.hi8 ^= 0x6A696665635C5A59LL;   rand.lo8 ^= 0x5655000000000000LL;
    ASSIGN_10_BYTES(rec_buf + 50, rand);

    /* get next 10 bytes using 7th random number, xor with specific constant
     */
    rand = RQ(rq, 6);
    rand.hi8 ^= 0x534E4D4B473C3A39LL;   rand.lo8 ^= 0x3635000000000000LL;
    ASSIGN_10_BYTES(rec_buf + 60, rand);

    /* get next 10 bytes using 8th random number, xor with specific constant
     */
    rand = RQ(rq, 7);
    rand.hi8 ^= 0x332E2D2B271E1D1BLL;   rand.lo8 ^= 0x170F000000000000LL;
    ASSIGN_10_BYTES(rec_buf + 70, rand);

    /* get next 10 bytes using 9th random number, xor with specific constant
     */
    rand = RQ(rq, 8);
    rand.hi8 ^= 0xC8C4C2C198949291LL;   rand.lo8 ^= 0x8CE0000000000000LL;
    ASSIGN_10_BYTES(rec_buf + 80, rand);

    /* get last 10 bytes using 10th random number, xor with specific constant
     */
    rand = RQ(rq, 9);
    rand.hi8 ^= 0x170F332E2D2B271ELL;   rand.lo8 ^= 0x1D1B000000000000LL;
    ASSIGN_10_BYTES(rec_buf + 90, rand);
}


/* get_skew_index - get the skew index for the current record number.
 *                  The skew number is the number of bits of relevance
 *                  in the current record number, or roughly log2(rec_num).
 *                  Examples:
 *                             current_rec_number  -->   skew_index
 *                            hi8              lo8
 *                     0000000000000000 0000000000000000     0
 *                     0000000000000000 0000000000000001     1
 *                     0000000000000000 0000000000000002     2
 *                     0000000000000000 0000000000000003     2
 *                     0000000000000000 0000000000000004     3
 *                     0000000000000000 0000000000000005     3
 *                     0000000000000000 0000000000000006     3
 *                     0000000000000000 0000000000000007     3
 *                     0000000000000000 0000000000000008     4
 *                     0000000000000000 0000000000000009     4
 *                     0000000000000000 000000000000000A     4
 *                     0000000000000000 000000000000000B     4
 *                     0000000000000000 000000000000000C     4
 *                     0000000000000000 000000000000000D     4
 *                     0000000000000000 000000000000000E     4
 *                     0000000000000000 000000000000000F     4
 *                     0000000000000000 0000000000000010     5
 *                     0000000000000000 8000000000000000    64
 *                     0000000000000000 FFFFFFFFFFFFFFFF    64
 *                     0000000000000001 0000000000000000    65
 *                     0000000000000001 0000000000000001    65
 *                     8000000000000000 0000000000000000   128
 *                     FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF   128
 */
int get_skew_index(rand_queue *rq)
{
    int         skew_index;

    /* Get last skew_index for previous record. Skew_index will be 0 if
     * this is the first record generated by this rand_queue.
     */
    skew_index = rq->skew_index;

    /* Given that either:
     * 1) this is first record generated from the rand_queue, and
     *    skew_index is 0; or
     * 2) this is not the first record generated from the rand_queue, and
     *    skew_index is the correct index of the previous record number,
     * then figure out what the skew_index is for the current record number.
     * In most cases (scenario 2 above), the skew_index for the current
     * record number is the same as for the previous record number, but we
     * need to verify that. In other scenario 2 cases, the skew_index will
     * need to be incremented by 1. In the scenario 1 case (first record
     * generated by this rand_queue), a linear search will need to be done
     * to find the correct skew_index (don't worry, it's logarithmic). 
     */
    if (rq->curr_rec_number.hi8 == 0)
    {
        if (rq->curr_rec_number.lo8 == 0)
            skew_index = 0;
        /* if highest order bit is set */
        else if (rq->curr_rec_number.lo8 & 0xF000000000000000LL)
            skew_index = 64;
        else
        {
            /* while 2**(skew_index) <= lo8 */
            while (((u8)0x1 << skew_index) <= rq->curr_rec_number.lo8)
                skew_index++;
        }
    }
    else
    {
        /* if highest order bit is set */
        if (rq->curr_rec_number.hi8 & 0xF000000000000000LL)
            skew_index = 128;
        else
        {
            if (skew_index < 64)
                skew_index = 64;
            /* while 2**(skew_index - 64) <= hi8 */
            while (((u8)0x1 << (skew_index - 64)) <= rq->curr_rec_number.hi8)
                skew_index++;
        }
    }
    /* remember index to speed up index calculation for next possible record */
    rq->skew_index = skew_index; 
    return (skew_index);
}


/* gen_skewed_rec - generate a "binary" skewed record suitable for 
 *                  alternate Daytona skewed data test.
 */
void gen_skewed_rec(unsigned char *rec_buf, rand_queue *rq)
{
    int                 skew_index;
    u16                 rand;
    unsigned char       mask;
    unsigned char       *skew_bytes;
    int                 skew_bits;
    
    /* first generate non-skewed record */
    gen_rec(rec_buf, rq);

    /* get skew index for current record */
    skew_index = get_skew_index(rq);

    /* use skew_index to get a pointer to the potential SKEW_BYTES key bytes
     * which may replace the high-order key bytes just generated by gen_rec().
     */
    skew_bytes = Skew_binary[skew_index];
    
    rand = RQ(rq, 0);

    /* get an index in the inclusive range 0 - 48 from 6 bits in lo8 */
    skew_bits = (int)(rand.lo8 >> 32) & 0x3F;
    if (skew_bits > 8 * SKEW_BYTES)
        skew_bits = 0;  /* each rec has 16/64 = .25 chance to not be skewed */

    /* replace the high-order "skew_bits" bits in record key with bits from
     * the skew bytes.
     */
    while (skew_bits >= 8) /* while there is a whole byte to replace */
    {
        *rec_buf = *skew_bytes;
        skew_bits -= 8;
        rec_buf++;
        skew_bytes++;
    }
    if (skew_bits > 0)
    {
        /* We know that skew_bits is at least 1 and no more than 7.
         * Replace the high-order "skew_bits" bits with the same bits from
         * the byte pointed to by skew_bytes. Leave the remain bits the same.
         */
        mask = (unsigned char)0xFF >> skew_bits;
        *rec_buf = (*skew_bytes & ~mask) | (*rec_buf & mask);
    }
}


/* gen_ascii_rec = generate an ascii record suitable for all sort
 *              benchmarks including PennySort.
 */
void gen_ascii_rec(unsigned char *rec_buf, rand_queue *rq)
{
    int         i;
    u16         rand = rq->rand[rq->head_index];
    u16         rec_number = rq->curr_rec_number;
    u8          temp;
    
    /* generate the 10-byte ascii key using mostly the high 64 bits.
     */
    temp = rand.hi8;
    rec_buf[0] = (unsigned char)(' ' + (temp % 95));
    temp /= 95;
    rec_buf[1] = (unsigned char)(' ' + (temp % 95));
    temp /= 95;
    rec_buf[2] = (unsigned char)(' ' + (temp % 95));
    temp /= 95;
    rec_buf[3] = (unsigned char)(' ' + (temp % 95));
    temp /= 95;
    rec_buf[4] = (unsigned char)(' ' + (temp % 95));
    temp /= 95;
    rec_buf[5] = (unsigned char)(' ' + (temp % 95));
    temp /= 95;
    rec_buf[6] = (unsigned char)(' ' + (temp % 95));
    temp /= 95;
    rec_buf[7] = (unsigned char)(' ' + (temp % 95));
    temp = rand.lo8;
    rec_buf[8] = (unsigned char)(' ' + (temp % 95));
    temp /= 95;
    rec_buf[9] = (unsigned char)(' ' + (temp % 95));
    temp /= 95;

    /* add 2 bytes of "break" */
    rec_buf[10] = ' ';
    rec_buf[11] = ' ';
    
    /* convert the 128-bit record number to 32 bits of ascii hexadecimal
     * as the next 32 bytes of the record.
     */
    for (i = 0; i < 16; i++)
        rec_buf[12 + i] =
            (unsigned char)(HEX_DIGIT((rec_number.hi8 >> (60 - 4 * i)) & 0xF));
    for (i = 0; i < 16; i++)
        rec_buf[28 + i] =
            (unsigned char)(HEX_DIGIT((rec_number.lo8 >> (60 - 4 * i)) & 0xF));

    /* add 2 bytes of "break" data */
    rec_buf[44] = ' ';
    rec_buf[45] = ' ';

    /* add 52 bytes of filler based on low 48 bits of random number */
    rec_buf[46] = rec_buf[47] = rec_buf[48] = rec_buf[49] = 
        (unsigned char)(HEX_DIGIT((rand.lo8 >> 48) & 0xF));
    rec_buf[50] = rec_buf[51] = rec_buf[52] = rec_buf[53] = 
        (unsigned char)(HEX_DIGIT((rand.lo8 >> 44) & 0xF));
    rec_buf[54] = rec_buf[55] = rec_buf[56] = rec_buf[57] = 
        (unsigned char)(HEX_DIGIT((rand.lo8 >> 40) & 0xF));
    rec_buf[58] = rec_buf[59] = rec_buf[60] = rec_buf[61] = 
        (unsigned char)(HEX_DIGIT((rand.lo8 >> 36) & 0xF));
    rec_buf[62] = rec_buf[63] = rec_buf[64] = rec_buf[65] = 
        (unsigned char)(HEX_DIGIT((rand.lo8 >> 32) & 0xF));
    rec_buf[66] = rec_buf[67] = rec_buf[68] = rec_buf[69] = 
        (unsigned char)(HEX_DIGIT((rand.lo8 >> 28) & 0xF));
    rec_buf[70] = rec_buf[71] = rec_buf[72] = rec_buf[73] = 
        (unsigned char)(HEX_DIGIT((rand.lo8 >> 24) & 0xF));
    rec_buf[74] = rec_buf[75] = rec_buf[76] = rec_buf[77] = 
        (unsigned char)(HEX_DIGIT((rand.lo8 >> 20) & 0xF));
    rec_buf[78] = rec_buf[79] = rec_buf[80] = rec_buf[81] = 
        (unsigned char)(HEX_DIGIT((rand.lo8 >> 16) & 0xF));
    rec_buf[82] = rec_buf[83] = rec_buf[84] = rec_buf[85] = 
        (unsigned char)(HEX_DIGIT((rand.lo8 >> 12) & 0xF));
    rec_buf[86] = rec_buf[87] = rec_buf[88] = rec_buf[89] = 
        (unsigned char)(HEX_DIGIT((rand.lo8 >>  8) & 0xF));
    rec_buf[90] = rec_buf[91] = rec_buf[92] = rec_buf[93] = 
        (unsigned char)(HEX_DIGIT((rand.lo8 >>  4) & 0xF));
    rec_buf[94] = rec_buf[95] = rec_buf[96] = rec_buf[97] = 
        (unsigned char)(HEX_DIGIT((rand.lo8 >>  0) & 0xF));

    /* add 2 bytes of "break" data */
    rec_buf[98] = '\r'; /* nice for Windows */
    rec_buf[99] = '\n';
}


/* gen_ascii_skewed_rec = generate an ascii skewed record suitable for
 *                        alternate Daytona skewed data test.
 */
void gen_ascii_skewed_rec(unsigned char *rec_buf, rand_queue *rq)
{
    int                 skew_index;
    u16                 rand;
    unsigned char       *skew_bytes;
    int                 skew_count;

    /* generate non-skewed record */
    gen_ascii_rec(rec_buf, rq);

    /* get skew index for current record */
    skew_index = get_skew_index(rq);

    /* use skew_index to get the SKEW_BYTES potential skew bytes for the 
     * high-order key bytes.
     */
    skew_bytes = Skew_ascii[skew_index];
    
    rand = RQ(rq, 0);

    /* get random number in the inclusive range 0 - SKEW_BYTES from
     * 3 bits in lo8.
     */
    skew_count = (int)(rand.lo8 >> 32) & 0x7;
    if (skew_count > SKEW_BYTES)
        skew_count = 0; /* each rec has 2/8 = .25 chance of being non-skewed */

    /* replace the high-order "skew_count" bytes of record key with bytes from
     * the skew bytes.
     */
    while (skew_count > 0) /* while there is another byte to replace */
    {
        *rec_buf = *skew_bytes;
        skew_count--;
        rec_buf++;
        skew_bytes++;
    }
}


#if defined(SUMP_PUMP)
/* gen_block -  pump function that reads a task instruction from the
 *              process's main thread.  The instruction consists of the
 *              beginning record number and the number of records to be
 *              generated.  The records are written to the task output.
 *              The sump pump infrastructure will run multiple instances
 *              of this function in parallel, and concatenate their
 *              outputs to form the output of the sump pump.
 */
int gen_block(sp_task_t t, void *unused)
{
    struct gen_instruct *ip;
    struct gen_instruct instruct;
    u8                  j;
    u16                 temp16 = {0LL, 0LL};
    u16                 sum16 = {0LL, 0LL};
    unsigned char       rec_buf[100];
    rand_queue          rq;

    /* read instruction from the thread's input */
    if (pfunc_get_rec(t, &ip) != sizeof(instruct))
        return (pfunc_error(t, "sp_get_rec() error"));
    instruct = *ip;
        
    init_rand_queue(&rq, instruct.starting_rec);
    
    for (j = 0; j < instruct.num_recs; j++)
    {
        (*Gen)(rec_buf, &rq);
        if (Print_checksum)
        {
            temp16.lo8 = crc32(0, rec_buf, REC_SIZE);
            sum16 = add16(sum16, temp16);
        }
        if (!Skip_output)
            pfunc_write(t, 0, rec_buf, REC_SIZE);
        bump_queue(&rq);
    }

    /* add the checksum for the block of records just generated to
     * the global checksum.
     *
     * this could be done without a mutex by outputing the checksum
     * to a second output stream that is read by the main thread,
     * but this way is much simpler.
     */
    pfunc_mutex_lock(t);
    Sum16 = add16(Sum16, sum16);  
    pfunc_mutex_unlock(t);
    
    return (SP_OK);
}
#endif


static char usage_str[] =
    "Gensort Sort Input Generator\n"
    "\n"
    "usage: gensort [-a] [-c] [-bSTARTING_REC_NUM] "
#if defined(SUMP_PUMP)
    "[-tN] NUM_RECS FILE_NAME[,opts]\n"
#else
    "NUM_RECS FILE_NAME\n"
#endif
    "-a        Generate ascii records required for PennySort or JouleSort.\n"
    "          These records are also an alternative input for the other\n"
    "          sort benchmarks.  Without this flag, binary records will be\n"
    "          generated that contain the highest density of randomness in\n"
    "          the 10-byte key.\n"
    "-c        Calculate the sum of the crc32 checksums of each of the\n"
    "          generated records and send it to standard error.\n"
    "-bN       Set the beginning record generated to N. By default the\n"
    "          first record generated is record 0.\n"
    "-s        Generate input records with skewed keys. If used with -a\n"
    "          option, then skewed ascii records are generated.\n"
#if defined(SUMP_PUMP)
    "-tN       Use N internal program threads to generate the records.\n"
#endif
    "NUM_RECS  The number of sequential records to generate.\n"
#if defined(SUMP_PUMP)
    "FILE_NAME[,opts] The name of the file to write the records to.\n"
    "          File options may immediately follow the file name:\n"
    "          ,buf           Use buffered and synchronous file writes,\n"
    "                         instead of the default direct and asynchronous\n"
    "                         writes.\n"
    "          ,dir           Use direct and asynchronous file writes.\n"
    "                         The is the default.\n"
    "          ,trans=N[k,m,g] Sets the file write request size in bytes,\n"
    "                         kilobytes, megabytes or gigabytes.\n"
    "          ,count=N       Sets the maximum number of simultaneous\n"
    "                         asynchronous write requests allowed.\n"
#else
    "FILE_NAME The name of the file to write the records to.\n"
#endif
    "\n"
    "Example 1 - to generate 1000000 ascii records starting at record 0 to\n"
    "the file named \"pennyinput\":\n"
    "    gensort -a 1000000 pennyinput\n"
    "\n"
    "Example 2 - to generate 1000 binary records beginning with record 2000\n"
    "to the file named \"partition2\":\n"
    "    gensort -b2000 1000 partition2\n"
    "\n"
    "Copyright (C) 2009 - 2011, Chris Nyberg\n"
    "\n"
    "This program is free software; you can redistribute it and/or\n"
    "modify it under the terms of Version 2 of the GNU General Public\n"
    "License as published by the Free Software Foundation.\n"
    "\n"
    "This program is distributed in the hope that it will be useful,\n"
    "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
    "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
    "GNU General Public License for more details.\n"
    "\n"
    "You should have received a copy of the GNU General Public License\n"
    "along with this program; if not, write to the Free Software Foundation,\n"
    "Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.\n"
    ;

void usage(void)
{
    fprintf(stderr, usage_str);
    fprintf(stderr, "\nVersion %s, cvs $Revision: 1.14 $\n", Version);
#if defined(SUMP_PUMP)
    fprintf(stderr, "SUMP Pump version %s\n", sp_get_version());
#endif
    exit(1);
}


int main(int argc, char *argv[])
{
    u8                  j;                      /* should be a u16 someday */
    u16                 starting_rec_number;
    u16                 num_recs;
    unsigned char       rec_buf[REC_SIZE];
    FILE                *out;
    u16                 temp16 = {0LL, 0LL};
    char                sumbuf[U16_ASCII_BUF_SIZE];
    rand_queue          rq;
#if defined(SUMP_PUMP)
    int                 number_threads = 0;
    int                 ret;
    sp_t                sp_gen;         /* handle for sump pump */
    u8                  blk_recs;
    struct gen_instruct instruct;
#endif
    
    starting_rec_number.hi8 = 0;
    starting_rec_number.lo8 = 0;
    Gen = gen_rec;

    while (argc > 1 && argv[1][0] == '-')
    {
        if (argv[1][1] == 'a')
        {
            if (Gen == gen_rec)
                Gen = gen_ascii_rec;
            else if (Gen == gen_skewed_rec)
                Gen = gen_ascii_skewed_rec;
        }
        else if (argv[1][1] == 'b')
            starting_rec_number = dec_to_u16(argv[1] + 2);
        else if (argv[1][1] == 'c')
            Print_checksum = 1;
        else if (argv[1][1] == 's')
        {
            if (Gen == gen_rec)
                Gen = gen_skewed_rec;
            else if (Gen == gen_ascii_rec)
                Gen = gen_ascii_skewed_rec;
        }
#if defined(SUMP_PUMP)
        else if (argv[1][1] == 't')
            number_threads = atoi(argv[1] + 2);
#endif
        else
            usage();
        argc--;
        argv++;
    }
    if (argc != 3)
        usage();
    num_recs = dec_to_u16(argv[1]);
    Skip_output = (strcmp(argv[2], "/dev/null") == 0);

#if defined(SUMP_PUMP)
    if (number_threads != 1)
    {
        /* start a sump pump to generate records using the gen_block()
         * function.  Making the input buffer size the size of an
         * instruction structure insures that each sump pump task executes
         * exactly one instruction.  The output buffer size will hold the
         * maximum number of records that will be generated per instruction.
         */
        ret = sp_start(&sp_gen, gen_block,
                       "-IN_BUF_SIZE=%d -REC_SIZE=%d -OUT_BUF_SIZE[0]=%d "
                       "-OUT_FILE[0]=%s -THREADS=%d",
                       sizeof(struct gen_instruct),  /* input buf size */
                       sizeof(struct gen_instruct),  /* input record size */
                       BLK_RECS * REC_SIZE,          /* output buf size */
                       argv[2],                      /* file name */
                       number_threads);
        if (ret)
        {
            fprintf(stderr, "sp_start failed: %s\n",
                    sp_get_error_string(sp_gen, ret));
            return (ret);
        }

        /* Feed generate instruction structures to the sump pump threads.
         * Each instruction struct will handled by a sump pump thread
         * executing the gen_block() pump function.
         */
        instruct.starting_rec = starting_rec_number;
        for (j = 0; (j * BLK_RECS) < num_recs.lo8; j++)
        {
            /* set starting rec and number of recs to generate */
            /* instruct.starting_rec.lo8 = (j * BLK_RECS); */
            blk_recs = num_recs.lo8 - (j * BLK_RECS);
            if (blk_recs > BLK_RECS)
                blk_recs = BLK_RECS;
            instruct.num_recs = blk_recs;
            if (sp_write_input(sp_gen, &instruct, sizeof(instruct)) !=
                sizeof(instruct))
            {
                fprintf(stderr, "sp_write_input: %s\n",
                        sp_get_error_string(sp_gen, SP_WRITE_ERROR)), exit(1);
            }
            instruct.starting_rec.lo8 += BLK_RECS;
        }
        /* write EOF to sump pump so it will wind down */
        if (sp_write_input(sp_gen, NULL, 0) != 0)
            fprintf(stderr, "sp_write_input(0): %s\n",
                    sp_get_error_string(sp_gen, SP_WRITE_ERROR)), exit(1); 

        /* wait for sump pump to finish */
        if ((ret = sp_wait(sp_gen)) != SP_OK)
            fprintf(stderr, "sp_wait: %s\n",
                    sp_get_error_string(sp_gen, ret)), exit(1); 
    }
    else
#endif
    {
        /* use just this single thread */
        
        if ((out = fopen(argv[2], "w")) == NULL)
        {
            perror(argv[2]);
            exit(1);
        }
        
        init_rand_queue(&rq, starting_rec_number);
    
        for (j = 0; j < num_recs.lo8; j++)
        {
            (*Gen)(rec_buf, &rq);
            if (Print_checksum)
            {
                temp16.lo8 = crc32(0, rec_buf, REC_SIZE);
                Sum16 = add16(Sum16, temp16);
            }
            if (!Skip_output)
                fwrite(rec_buf, REC_SIZE, 1, out);
            bump_queue(&rq);
        }
    }
    
    if (Print_checksum)
        fprintf(stderr, "%s\n", u16_to_hex(Sum16, sumbuf));
    return (0);
}