Newressu version bumped to 3.0

Kaikki oikeudet pidätetään ©. Maailman parhaat satunnaisbitit: https://moijari.com:5006.

Versionumeron kasvatuksen yhteydessä käyn läpi kaikki uudet ja vanhat pääfunktiot.

Edit: korjailen raporttia vielä seuraavien viikkojen aikana

Ensimmäiset funktiot sisältävät varsinaisen kellon luvun ja kellon esikäsittelyn. Esikäsittely on uusi ominaisuus, jossa kellojono jaetaan satunnaisesti 1-256 merkkiä sisältäviin “blokkeihin”, ja joka toinen blokki palautetaan sellaisenaan (copy) ja joka toinen palautetaan käännetyssä järjestyksessä (reverse). Tällä tavalla sain kellojonon aiemman esikäsittelyn “symmetrisemmäksi”, ja koska kellojonon käsittelyssä ei enää ole aiemman version ohituksia (skip) myös kellojonon “tuhlaus” loppuu. Tämän esikäsittelyn pitäisi tehdä mahdottomaksi kellojonon arvaaminen.

Kellojonon käsittelyyn on myös lisätty uusi –fixedclock toiminto, jolla kellojonon ketjut ovat aina yhtä pitkiä. Tällä voidaan ensinnäkin miettiä muodostaako pelkkä copy-reverse ilman kellojonon vaihtelua tarvittavasti satunnaisuutta sellaisenaan, ja toisaalta taas sen ymmärtäminen, miten uusi copy-reverse vaikuttaa.

Ensiksi alkuperäinen kellojonon luku.

static unsigned char ressu_clockbyte2() /* JariK 2013 */
{
  struct timeval tv;
  gettimeofday(&tv, NULL);
  return(tv.tv_usec & 0xff);
}

Sitten ns kiinteämittaista kellojonoa tarjoava rutiini:

#define FIXEDCLOCKCHAINLENGTH 80
unsigned int fixedclockchainlength = FIXEDCLOCKCHAINLENGTH;

static unsigned char ressu_fixedclockbyte2() /* JariK 2022 */
{
  static unsigned int clockbyte = 0, counter = 0;

  if(counter == 0) {
    clockbyte++;
    counter += fixedclockchainlength;
  }
  counter--;
  return(clockbyte & 0xff);
}

Tässä fixed/normal valinnan tekevä rutiini. Ensimmäisen kerran näissä raporteissa osoite funktioon (pointer to function) tyyppinen muuttuja:

unsigned char (*clockfunc)()= &ressu_clockbyte2; // normal

void ressu_setclock(int clockmode)
{
  if(!clockmode)
    clockfunc = &ressu_clockbyte2; // normal
  else
    clockfunc = &ressu_fixedclockbyte2; // fixed
}

Seuraavana “mukamas” satunnaisia lukuja tekevä rutiini. Tulos on kuitenkin satunnainen, koska sitä käytetään indeksinä satunnaislukupuskuriin. Tämä funktio oli jo copy-skip versiossa, tässä se on ulkoistettu koska sitä käytetään kaksi kertaa, kerran copy:lle ja kerran reverse:lle.

static unsigned int ressu_nonrandom() // not really random
{
  static unsigned int rando = 0;

  rando = rando +
    clockbytes +
    genbytes +
    time(NULL) +
    clock() +
    get_useconds() +
    ch * ch;

  return(rando);
}

Tässä kellojonon esikäsittely joka kopioi tai kääntää seuraavan lohkon: (huomaa ch = (*clockfunc)() joka palauttaa joko tavallisen tai kiinteänmittaisen kellojonon).

unsigned char *ressuct; // ressu random buffer lookup
unsigned int ressuct_bytes;

static unsigned char ressu_clockbyte() /* JariK 2013 */
{
  static int reverse = 0; // 0 = copy, 1 = reverse, 28.10.2022 JariK
  static unsigned char reversebytes[256];
  static unsigned int count = 0;

  if(reverse) {
    if(count == 0) {
      count = ressuct[ressu_nonrandom() % ressuct_bytes] + 1;
      rndbits2 += 8;
#ifdef DEBUG2A
      fprintf(stdout,"rev:  %03x  ", count);
#endif
      for(int c = 0; c < count; c++) {

	reversebytes[c] = (*clockfunc)();
	//reversebytes[c] = ressu_clockbyte2();

#ifdef DEBUG2A
	fprintf(stdout," %02x", reversebytes[c]);
	newressu_output = 1;
#endif
      }
#ifdef DEBUG2A
      fprintf(stdout,"\n");
#endif
    }
    ch = reversebytes[--count];
#ifdef DEBUG2A
    fprintf(stdout," ch:%02x", ch);
#endif
    if(count == 0) {
      reverse = 0;
#ifdef DEBUG2A
      fprintf(stdout,"\n");
#endif
    }
  } else {
    if(count == 0) {
      count = ressuct[ressu_nonrandom() % ressuct_bytes] + 1;
      rndbits2 += 8;
#ifdef DEBUG2A
      fprintf(stdout,"copy: %03x  ", count);
#endif
    }

    ch = (*clockfunc)();
    //ch = ressu_clockbyte2();

#ifdef DEBUG2A
    fprintf(stdout," ch:%02x", ch);
    newressu_output = 1;
#endif

    if(--count == 0) {
      reverse = 1;
#ifdef DEBUG2A
      fprintf(stdout,"\n");
#endif
    }
  }
#ifdef DEBUG2A
  fflush(stdout);
#endif
  
  return(ch);
}

Tässä vielä edellinen ilman #ifdef DEBUG2A rivejä: (debukilla voi muuten katsella miten copy reverse käyttäytyy).

static unsigned char ressu_clockbyte() /* JariK 2013 */
{
  static int reverse = 0; // 0 = copy, 1 = reverse, 28.10.2022 JariK
  static unsigned char reversebytes[256];
  static unsigned int count = 0;

  if(reverse) {
    if(count == 0) {
      count = ressuct[ressu_nonrandom() % ressuct_bytes] + 1;
      rndbits2 += 8;
      for(int c = 0; c < count; c++) {
	reversebytes[c] = (*clockfunc)();
	//reversebytes[c] = ressu_clockbyte2();
      }
    }
    ch = reversebytes[--count];
    if(count == 0) {
      reverse = 0;
    }
  } else {
    if(count == 0) {
      count = ressuct[ressu_nonrandom() % ressuct_bytes] + 1;
      rndbits2 += 8;
    }
    ch = (*clockfunc)();
    //ch = ressu_clockbyte2();
    if(--count == 0) {
      reverse = 1;
    }
  }
  
  return(ch);
}

Seuraavassa fixed moodissa tehtyjä satunnaisbittejä : (–stat optio tuo tulosteeseen myös kaksi statistics riviä. Huomaa että pisin kellojono on tuo 80 merkkiä, kaikki muut ketjut ovat satunnaisista kohdista katkaistuja saman mittaisia ketjuja)

$ ./newressu --fixedclock --stat
randomsource: ressu, clockmode: 1, clockchainlength: 80, size: 5, lines: 10, linew: 13, pchars: 71, pwords: 13, tchars: 650, digitscount: 10, wordvalues: 100000, limit: 100000
rounds:10 1:38 2:37 3:25 4:20 5:30 6:37 7:30 8:27 9:25 10:29 11:32 12:19 13:28 14:36 15:31 16:35 17:19 18:31 19:18 20:28 21:28 22:24 23:31 24:25 25:30 26:38 27:29 28:29 29:39 30:27 31:26 32:29 33:24 34:22 35:26 36:24 37:44 38:25 39:32 40:32 41:28 42:21 43:40 44:23 45:23 46:21 47:24 48:27 49:27 50:23 51:33 52:26 53:26 54:31 55:23 56:23 57:28 58:19 59:29 60:22 61:17 62:28 63:20 64:27 65:22 66:29 67:23 68:16 69:24 70:20 71:21 72:23 73:30 74:27 75:26 76:18 77:15 78:27 79:26 80:1041, chains:3156, sorted: 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 35 36 37 38 39 40 44 1041, high1:1041, high2:44, lim1a:37, lim1b:38, lim1:37, lim2:38, div:1.027027, clockbytes:163760, rndbits1:1916, rndbits2:16488, rndbits3high:1041, rndbits3:2115, rndbits4highdigits:4, rndbits4:2115, rndbits5:3156, rndbits6high:1041, rndbits6:10576, rndbits7high:1041, rndbits7:21067
00000 22294132938409767835685350193923839292365996595976935862826433282
00001 48488719945788375230776214888393776008587235060334557977496406293
00002 80431275143976461305838453114822706361730974050477594023459017094
00003 93206417217628250656515986177836863340864892051316267057597113916
00004 61731938638333708609330161928363100282423290987243662193954403192
00005 94982923919430464067869481643568801481659685690996569554171263466
00006 73313649799714726811829474731299233472648891876654506268759611113
00007 71330815013503135793796350332802986281525959248136439352537631481
00008 80520675067022116837313859689861210963143997805965619140693304090
00009 27529324544421024520929292748015725933677643980607578142560612229

Sitten DEBUG2A tulostetta normal moodissa: (ch: on palautettava merkki, copy: sanan jälkeen tulee merkkien lukumäärä ja kopioitava blokki, rev: sanan jälkeen merkkien lukumäärä, merkit alkuperäisessä järjestyksessä ja sitten käännetyssä järjestyksessä).

copy: 015   ch:21 ch:21 ch:21 ch:21 ch:21 ch:21 ch:21 ch:21 ch:21 ch:21 ch:21 ch:21 ch:21 ch:21 ch:21 ch:21 ch:21 ch:21 ch:21 ch:21 ch:21
rev:  019   21 21 21 21 21 21 21 21 21 21 21 21 21 21 21 21 21 21 21 21 21 21 21 21 21
 ch:21 ch:21 ch:21 ch:21 ch:21 ch:21 ch:21 ch:21 ch:21 ch:21 ch:21 ch:21 ch:21 ch:21 ch:21 ch:21 ch:21 ch:21 ch:21 ch:21 ch:21 ch:21 ch:21 ch:21 ch:21
copy: 016   ch:21 ch:21 ch:21 ch:21 ch:21 ch:21 ch:21 ch:21 ch:21 ch:21 ch:21 ch:21 ch:21 ch:21 ch:21 ch:21 ch:21 ch:21 ch:21 ch:21 ch:21 ch:21
rev:  00a   21 21 21 21 21 21 21 22 22 22
 ch:22 ch:22 ch:22 ch:21 ch:21 ch:21 ch:21 ch:21 ch:21 ch:21
copy: 010   ch:22 ch:22 ch:22 ch:22 ch:22 ch:22 ch:22 ch:22 ch:22 ch:22 ch:22 ch:22 ch:22 ch:22 ch:22 ch:22
rev:  018   22 22 22 22 22 22 22 22 22 22 22 22 22 22 22 22 22 22 22 22 22 22 22 22
 ch:22 ch:22 ch:22 ch:22 ch:22 ch:22 ch:22 ch:22 ch:22 ch:22 ch:22 ch:22 ch:22 ch:22 ch:22 ch:22 ch:22 ch:22 ch:22 ch:22 ch:22 ch:22 ch:22 ch:22
copy: 014   ch:22 ch:22 ch:22 ch:22 ch:22 ch:22 ch:22 ch:22 ch:22 ch:22 ch:22 ch:22 ch:22 ch:22 ch:22 ch:22 ch:22 ch:22 ch:22 ch:22
rev:  00f   22 22 22 22 22 22 22 22 22 22 22 22 22 22 22
 ch:22 ch:22 ch:22 ch:22 ch:22 ch:22 ch:22 ch:22 ch:22 ch:22 ch:22 ch:22 ch:22 ch:22 ch:22

Mielenkiintoista on, voiko teoreettisten satunnaismerkkien laskennan (rndbits1, rndbits3 jne) jättää kokonaan pois kun copy-reverse on toiminnassa, sitä voi yrittää hahmottaa newressu –single komennolla.

$ ./newressu --stat --single
randomsource: single, clockmode: 0, size: 5, lines: 10, linew: 13, pchars: 71, pwords: 13, tchars: 650, digitscount: 10, wordvalues: 100000, limit: 100000
rounds:1 1:38 2:35 3:6 4:13 5:13 6:9 7:11 8:9 9:9 10:7 11:5 12:7 13:9 14:62 15:169 16:8 17:1 18:6 19:122 20:68, chains:607, clockbytes:8187, rndbits2:1128
00000 32262283161809391678657136366555756828967803578676610526738617216
00001 69511031437775242989469007588191015483302697394274894700778823040
00002 76140983583748585072353786938410170011199037634650689281079597904
00003 93729335401437288771146924781560699584385747294818636367091300176
00004 05265892091793398109092431528679114647250196564188868255370175310
00005 32662058346170613285279049952971968089134474619682155846073978431
00006 37542509108789762598062162668902832635630435392168038872003862287
00007 49867796710786537902924653466617634225652081735463098061052717754
00008 40532647329764438978005136772857284492053527665773437979327537731
00009 68916755542984513818470885899001010383458534877845952219318341042

Vielä fast versio, jossa oletetaan, että copy-reversen 8 bittiä blokki (rndbits2) on oikea satunnaisuuden määrä. Tässä ei siis testata noita muita teoreettisen satunnaisuuden kenttiä:

$ ./newressu --stat --fast
randomsource: fast, clockmode: 0, size: 5, lines: 10, linew: 13, pchars: 71, pwords: 13, tchars: 650, digitscount: 10, wordvalues: 100000, limit: 100000
rounds:3 1:18 2:19 3:19 4:13 5:11 6:23 7:16 8:18 9:22 10:10 11:18 12:14 13:12 14:11 15:14 16:13 17:14 18:16 19:11 20:16 21:27 22:301 23:99 24:16 25:10 26:13 27:10 28:11 29:7 30:9 31:5 32:72 33:181 34:16 35:11 36:5 37:6 40:3 41:3, chains:1113, sorted: 3 5 6 7 9 10 11 12 13 14 16 18 19 22 23 27 72 99 181 301, clockbytes:24556, rndbits2:1736
00000 68784755242708765294394273037319039572739737000724542275166407714
00001 06972592691192404285861495591371050958299735095969537656132751909
00002 67574262177835663992021063370107991617814973885686637190673088094
00003 23737673519966062499715155344625118562351368088477401742394627022
00004 03263216076372320938776167737005047546185162611650283796001101369
00005 51407508376549070991359975767652992476157609203411915065410641295
00006 12655893530487345584253510097815082335938955217832759445595250214
00007 24937072376561882973994015071952414930388304299234767961731321584
00008 05580710011089642452809116400909876953773223311014848926770989925
00009 80053600376650552642904242376127515525297384309817394907301522152

Joka tapauksessa perusversio: teoreettisten satunnaismerkkien laskemisineen ja copy-reverse:n kanssa on mielestäni versio korotuksen arvoinen. (Teiltä pitäisi kysyä onko se valmis…) Suorittelen kuitenkin, kuten aina, sen käyttämistä muiden generaattorien kanssa, ja viimeinen tietysti omassa ohjelmassa. Näistä omistani –pseudoressu tulee mieleen, siitä jatkossa.

$ ./newressu
00000 37623172794213955845654028970142043156650464162378618125059418215
00001 44770808876162322824064917059041951056355916863064355533513084242
00002 75485713017878047234034253933802355371072968018532606905129869680
00003 96551784242102846132000149967407451652050029473735516535627149942
00004 91453561873005997442161145845169261020298914882602023854201229781
00005 72203425783242175112239728887442139124665422626613641888668562931
00006 01828527840750137347400562529323569375142224887300242484404778626
00007 34038142372693997824692927661290471630937124920642848309126302336
00008 71422612084979552308234145197900652207559401420147297100214334402
00009 64387847336829223605559797922477255813426710680768085663528529936

Vielä ressun “ydin” ja liitännäistoiminnot. Uudelleen nimetty pääfunktio.

#define RR8(byte,bits) ( ((byte) >> (bits)) | ((byte) << (8 - (bits))) )
#define RL8(byte,bits) ( ((byte) >> (8 - (bits))) | ((byte) << (bits)) )

#define aDEBUG5 2

void ressu_genbytes_single_do(int size, unsigned char *buffer)
{
  int c, d;
  unsigned char e, byte;
  static int f = 0, prevbyte = -1, count = 0;

  //ressuct = buffer;  // ressu random buffer lookup
  //ressuct_bytes = size;
  
  for(c = 0; c < 8; c++) {
    for(d = 0; d < size; d++) {
      e = buffer[d];
      e = RL8(e, 1); // rotate byte left 1 bits
      //e = RL8(e, 3); // rotate byte left 3 bits
      //e = RR8(e, 1); // rotate byte right 1 bits
      byte = ressu_clockbyte();
      if(prevbyte == -1)
	prevbyte = byte;
      buffer[d] = e ^ byte;
      if(prevbyte != byte) {
	periods[count]++;
	clockbytes += count;
	count = 0;
	prevbyte = byte;
      }
      count++;
    }
#ifdef DEBUG5
    if(newressu_output)
      fprintf(stdout, "\n");
    ressu_dump("single 1", 32, buffer, 32);
#endif
    for(d = 0; d < size; d++) {
      f = (f + buffer[d]) % size;
      e = buffer[d];
      buffer[d] = buffer[f];
      buffer[f] = e;
    }
#ifdef DEBUG5
    if(newressu_output)
      fprintf(stdout,"\n");
    ressu_dump("single 2", 32, buffer, 32);
#endif
  }
}

Ja uusi versio ressu_genbytes_single() funktiosta, johon on lisättu –stat rivit, ja uusi genbytes_single_do() -kutsu:

Nuo ensimmäiset ressu_ct ja _bytes kertovat paikan josta uusi copy-reverse hakee blockin pituuden. Pituudet haetaan aina käsittelyn alla olevasta satunnaisbittipuskurista.

void ressu_genbytes_single(int size, unsigned char *buffer)
{
  ressuct = buffer;  // ressu random buffer lookup
  ressuct_bytes = size;
  
  rndbits2 = 0;
  clockbytes = 0;

  ressu_genbytes_single_do(size, buffer);

  //
  //  display statistics
  //
  if(stats) {

    if(newressu_output == 1)
      fprintf(stdout, "\n");
    newressu_output = 0;

    fprintf(stderr, "rounds:1");

    long chains = 0;
    
    for(int e = 0; e < 1024; e++) {
      if(periods[e] > 0) {
	fprintf(stderr, " %d:%lu", e, periods[e]);
	chains += periods[e];
      }
    }
    
    fprintf(stderr, ", chains:%ld", chains);
    fprintf(stderr, ", clockbytes:%ld", clockbytes);
    fprintf(stderr, ", rndbits2:%d", rndbits2);
    fprintf(stderr, "\n");
    fflush(stderr);
  }
}

Sitten ressu ressu_genbytes(): jossa lisätyt ressuct ja _bytes muuttujat kellojonon pituuden hakemista varten ja muutettu kutsu ressu_genbytes_single_do “ydintä” varten.

#define aDEBUG7 2
#define aDEBUG2 2

void ressu_genbytes(int size, unsigned char *buffer) // 6.5.2021 JariK
{
  int c, d, e, f;
  static unsigned char ressut[RESSUT_BYTES];
  static int ressut_first = 1,
    ressut_pos = 0,
    ressut_f = 0;
  unsigned long prevperiods[1024];

#ifdef DEBUG2
  static unsigned char counts[RESSUT_BYTES];
#endif
  
  ressuct = ressut;  // ressu random buffer lookup
  ressuct_bytes = ressut_bytes; // table used in ressu_genbytes_single_do

  for(c = 0; c < size; c++) {
    if(ressut_pos == 0) {
      if(ressut_first) {
	memset(ressut, 0, ressut_bytes);
	ressut_first = 0;
      }

      clockbytes = 0;
      for(d = 0; d < 1024; d++) {
	periods[d] = 0;
      }

#ifdef DEBUG2
      for(d = 0; d < size; d++)
	counts[d] = 0;
#endif
      
      rndbits1 = 0;
      rndbits2 = 0;
      rndbits3 = 0;
      rndbits4 = 0;
      rndbits5 = 0;
      rndbits6 = 0;
      rndbits7 = 0;

      int lim, lim1 = 0, lim2 = 0;
      int lim1a, lim1b;
      int high1, high2;

      int rndbits3high;
      int rndbits4highdigits;
      int rndbits6high;
      int rndbits6pos;
      int rndbits7high;
      
      for(d = 0;
	  rndbits1 < ressu_bits1_needed ||
	  rndbits2 < ressu_bits2_needed ||
	  rndbits3 < ressu_bits3_needed ||
	  rndbits4 < ressu_bits4_needed ||
	  rndbits5 < ressu_bits5_needed ||
	  rndbits6 < ressu_bits6_needed ||
	  rndbits7 < ressu_bits7_needed ||
	  d < RESSU_MIN_ROUNDS ||
	  clockbytes < RESSU_MIN_CLOCKBYTES; d++) {

#define aDEBUG6 2
	
	//
	//  calculate rndbits1
	//
	
	// save previous round

        for(e = 0; e < 1024; e++) {
	  prevperiods[e] = periods[e];
	}

	ressu_genbytes_single_do(ressut_bytes, ressut);

	// find new lim1
	
	lim = lim1;
	f = -1;
	for(e = 0; e < 1024; e++) {
	  if(prevperiods[e] > 0 && prevperiods[e] == lim) {
	    if(f == -1 || (periods[e]>0 && f<periods[e])) {
	      f  = periods[e];
	    }
	  }
	}

	if(f != -1)
	  lim = f;

	lim1a = lim;
	
	// find highest amount of chains
	
	high1 = -1;
	for(e = 0; e < 1024; e++) {
	  if(high1 == -1 || high1 < periods[e]) {
	    high1 = periods[e];
	  }
	}

	// and second highest
	
	high2 = -1;
	for(e = 0; e < 1024; e++) {
	  if( (high2 == -1 && periods[e] < high1) ||
	      (high2 < periods[e] && periods[e] < high1) ) {
	    high2 = periods[e];
	  }
	}

	// and average

	int psum = 0;
	int pcnt = 0;
	for(e = 0; e < 1024; e++) {
	  if(periods[e] > 0) {
	    psum += periods[e];
	    pcnt++;
	  }
	}

	lim1b = (int)((double)psum / pcnt);
	
	// find next smaller than average

	f = -1;
	for(e = 0; e < 1024; e++) {
	  if( (f == -1 && periods[e] < lim1b) ||
	      (f < periods[e] && periods[e] < lim1b) ) {
	    f = periods[e];
	  }
	}

	if(f != -1)
	  lim1b = f;

	if(lim == 0 || lim > lim1b)
	  lim = lim1b;

	// if lim greater that second highest,
	// set to second highest

	if(lim > high2) {
	  lim = high2;
	}
		
	lim1 = lim;

	// find next greater than lim
	
	f = -1;
	for(e = 0; e < 1024; e++) {
	  if(periods[e] > lim &&
	     (f == -1 || periods[e] < f))
	    f = periods[e];
	}
	if(f != -1)
	  lim = f;

	lim2 = lim;

	// calculate rndbits1

#define aDEBUG6 2
	
	rndbits1 = 0;
	for(e = 0; e < 1024; e++) {
	  if(periods[e] > 0 && periods[e] < lim) {
	    rndbits1 += periods[e];
	  }
	}
	
	//
	//  calculate rndbits3
	//

#define aDEBUG8 2
	
	rndbits3high = 0;
	for(e = 0; e < 1024; e++) {
	  if(rndbits3high < periods[e])
	    rndbits3high = periods[e];
	}

	rndbits3 = 0;
	for(e = 0; e < 1024; e++) {
	  if(rndbits3high > periods[e]) {
	    rndbits3 += periods[e];
	  }
	}

	//
	//  calculate rndbits4
	//

#define aDEBUG9 2
	
	rndbits4highdigits = 0;
	for(e = 0; e < 1024; e++) {
	  if(rndbits4highdigits < (int)log10((double)periods[e]) + 1) {
	    rndbits4highdigits = (int)log10((double)periods[e]) + 1;
	  }
	}

	rndbits4 = 0;
	for(e = 0; e < 1024; e++) {
	  if(rndbits4highdigits > (int)log10((double)periods[e]) + 1) {
	    rndbits4 += periods[e];
	  }
	}

	//
	//  calculate rndbits5
	//

#define aDEBUG10 2
	
	rndbits5 = 0;

	for(e = 0; e < 1024; e++) {
	  rndbits5 += periods[e];
	}

	//
	//  calculate rndbits6
	//

#define aDEBUG11 2
	
	rndbits6high = 0;
	rndbits6pos = 0;
	rndbits6 = 0;
	for(e = 0; e < 1024; e++) {
	  if(rndbits6high < periods[e]) {
	    rndbits6high = periods[e];
	    rndbits6pos = e;
	  }
	}

	for(e = 0; e < 1024; e++) {
	  if(rndbits6high > periods[e] && periodsl[e] > 0) {
	    if(rndbits6pos > e)
	      rndbits6 += log2((double)rndbits6pos - e) * periods[e];
	    else
	      rndbits6 += log2((double)e - rndbits6pos) * periods[e];
	  }
	}

	//
	//  calculate rndbits7
	//

#define aDEBUG12 2
	
	rndbits7high = 0;
	rndbits7 = 0;
	for(e = 0; e < 1024; e++) {
	  if(rndbits7high < periods[e])
	    rndbits7high = periods[e];
	}

	for(e = 0; e < 1024; e++) {
	  if(rndbits7high > periods[e] && periods[e] > 0) {
	    rndbits7 += log2((double)rndbits7high - periods[e]) * periods[e];
	  }
	}
      } // for(d=0;
      
      //
      // debug display for rndbits1
      //

#ifdef DEBUG6
	
      rndbits1 = 0;
      for(e = 0; e < 1024; e++) {
	if(periods[e] > 0 && periods[e] < lim) {
	  rndbits1 += periods[e];
	  fprintf(stderr,"e:%d", e);
	  fprintf(stderr,", periods:%ld", periods[e]);
	  fprintf(stderr,", rndbits1+prev:%d", rndbits1);
	  fprintf(stderr,"\n");
	}
      }

#endif
      
      //
      //  debug display for rndbits3
      //

#ifdef DEBUG8
	
      rndbits3 = 0;
      for(e = 0; e < 1024; e++) {
	if(rndbits3high > periods[e] &&
	   periods[e] > 0) {
	  rndbits3 += periods[e];
	  fprintf(stderr,"e:%d", e);
	  fprintf(stderr,", periods:%ld", periods[e]);
	  fprintf(stderr,", rndbits3+prev:%d", rndbits3);
	  fprintf(stderr,"\n");
	}
      }

#endif
      
      //
      //  debug display for rndbits4
      //

#ifdef DEBUG9
      
      rndbits4 = 0;
      for(e = 0; e < 1024; e++) {
	if(rndbits4highdigits > (int)log10((double)periods[e]) + 1 &&
	   periods[e] > 0) {
	  rndbits4 += periods[e];
	  fprintf(stderr,"e:%d", e);
	  fprintf(stderr,", periods:%ld", periods[e]);
	  fprintf(stderr,", rndbits4+prev:%d", rndbits4);
	  fprintf(stderr,"\n");
	}
      }

#endif
      
      //
      //  debug display for rndbits5
      //

#ifdef DEBUG10

      rndbits5 = 0;

      for(e = 0; e < 1024; e++) {
	if(periods[e] > 0) {
	  fprintf(stderr,"e:%d", e);
	  fprintf(stderr,", periods:%ld", periods[e]);
	  rndbits5 += periods[e];
	  fprintf(stderr,", rndbits5+prev:%d", rndbits5);
	  fprintf(stderr,"\n");
	}
      }

#endif
      
      //
      //  debug diplay for rndbits6
      //

#ifdef DEBUG11
	
      rndbits6 = 0;
      for(e = 0; e < 1024; e++) {
	if(rndbits6high > periods[e] &&
	   periods[e] > 0) {
	  fprintf(stderr,"e:%d", e);
	  fprintf(stderr,", periods:%lu", periods[e]);

	  double l2;

	  if(rndbits6pos > e) {
	    fprintf(stderr,", pos-e:%d", rndbits6pos - e);
	    l2 = log2((double)rndbits6pos - e);
	  } else {
	    fprintf(stderr,", e-pos:%d", e - rndbits6pos);
	    l2 = log2((double)e - rndbits6pos);
	  }
	  fprintf(stderr,", log2(prev):%f", l2);
	  fprintf(stderr,", periods*prev:%ld", (long int) ((double)periods[e] * l2));
	  rndbits6 += l2 * periods[e];
	  fprintf(stderr,", rndbits6+prev:%d", rndbits6);
	  fprintf(stderr,"\n");
	}
      }

#endif
      
      //
      //  debug diplay for rndbits7
      //

#ifdef DEBUG12
	
      rndbits7 = 0;
      for(e = 0; e < 1024; e++) {
	if(rndbits7high > periods[e] &&
	   periods[e] > 0) {
	  rndbits7 += log2((double)rndbits7high - periods[e]) * periods[e];
	  fprintf(stderr,"e:%d", e);
	  fprintf(stderr,", periods:%ld", periods[e]);
	  fprintf(stderr,", high-prev:%ld", rndbits7high-periods[e]);
	  double l2=log2((double)rndbits7high-periods[e]);
	  fprintf(stderr,", log2(prev):%f", l2);
	  fprintf(stderr,", periods*prev:%ld", (long int) ((double)periods[e] * l2));
	  fprintf(stderr,", rndbits7+prev:%d", rndbits7);
	  fprintf(stderr,"\n");
	}
      }

#endif
      
      if(stats) {

	if(newressu_output == 1)
	  fprintf(stderr,"\n");
	newressu_output = 0;
	
	//
	//  display statistics
	//
	fprintf(stderr, "rounds:%d", d);

	long chains = 0;
    
	for(e = 0; e < 1024; e++) {
	  if(periods[e] > 0) {
	    fprintf(stderr, " %d:%lu", e, periods[e]);
	    chains += periods[e];
	  }
	}

	fprintf(stderr, ", chains:%ld", chains);
	
#ifdef DEBUG_SORTED
	
	fprintf(stderr, ", sorted:");
	int g = 0;
	for(;;) {
	  f = -1;
	  for(e = 0; e < 1024; e++) {
	    if( (f == -1 && periods[e] > g) ||
		(periods[e] > g && f > periods[e]) )
	      f = periods[e];
	  }
	  if(f == -1)
	    break;

	  g = f;
	  fprintf(stderr," %d", g);
	}

#endif
	fprintf(stderr, ", high1:%d", high1);
	fprintf(stderr, ", high2:%d", high2);
	fprintf(stderr, ", lim1a:%d", lim1a);
	fprintf(stderr, ", lim1b:%d", lim1b);
	fprintf(stderr, ", lim1:%d", lim1);
	fprintf(stderr, ", lim2:%d", lim2);
	fprintf(stderr, ", div:%f", (double)lim2 / lim1);
	fprintf(stderr, ", clockbytes:%ld", clockbytes);
	fprintf(stderr, ", rndbits1:%d", rndbits1);
	fprintf(stderr, ", rndbits2:%d", rndbits2);
	fprintf(stderr, ", rndbits3high:%d", rndbits3high);
	fprintf(stderr, ", rndbits3:%d", rndbits3);
	fprintf(stderr, ", rndbits4highdigits:%d", rndbits4highdigits);
	fprintf(stderr, ", rndbits4:%d", rndbits4);
	fprintf(stderr, ", rndbits5:%d", rndbits5);
	fprintf(stderr, ", rndbits6high:%d", rndbits6high);
	fprintf(stderr, ", rndbits6:%d", rndbits6);
	fprintf(stderr, ", rndbits7high:%d", rndbits7high);
	fprintf(stderr, ", rndbits7:%d", rndbits7);
	fprintf(stderr, "\n");
	fflush(stderr);
      }
    } // if(ressut_pos == 0)
    ressut_f = (ressut_f + ressut[ressut_pos]) % ressut_bytes;
    buffer[c] ^= ressut[ressut_f];
    ressut_pos = (ressut_pos + 1) % ressut_bytes;
  } // for(c=0; c<size; c++)

  if(verbose) {
    fprintf(stdout,"ressu ");
    for(c = 0; c < size; c++) {
      if(c > 0 && c % 32 == 0)
	fprintf(stdout," ");
      fprintf(stdout,"%02x", buffer[c]);
      newressu_output = 1;
    }
    fprintf(stdout,"\n");
  }
  
#ifdef DEBUG7

  ressu_dump("genbytes", size, buffer, 32);

#endif
  
  genbytes += size;
}

Mielestäni ressu (./newressu –ressu tai ./newressu) on kelvollista jopa one time pad materiaaliksi (jokainen bitti irtonainen, ei ole laskennallista yhteyttä muihin bitteihin paitsi tilastollinen todennäköisyys), ja jos noin olisi voisimme tehdä nopeamman satunnaisgeneraattorin käyttämällä ressua satunnaisbittien lähteenä ja tehdä uusia satunnaisbittejä tiivistefunktiolla (tässä sha256) (vertaa fort). Tässä generattoriehdokas: (ehdokas ei tietenkään ole one time pad materiaalia sha256 funktion vuoksi).

Edit: Miksi sitten ressun bitit ovat riippumattomia. Helpoimmin päättelyni jäljille pääsee, jos kuvittelee että kellomerkit on and:atty ykkösellä. Tällöin vain alin bitti kellojonosta jää jäljelle. Kun and:atyllä kellojonolla xor:ataan puskuri vain sen alin bitti muuttuu. Shift:issä alin bitti siirretään talteen kakkosbittiin ja kakkosbitti kolmosbittiin jne. Merkkien paikkaa muutetaan satunnaisesti ja aloitetaan alusta. Merkkien paikkojen satunnainen muuttaminen poistaa yhteyden yksittäisten “samalla rivillä” olevien bittien välillä. Se että yhden merkin eri bitit otetaan eri kohdasta kellojonosta ja vaihdetusta puskurinpaikasta varmistaa että merkin biteille ei muodostu yhteyttä. (Huomaa että myös merkin paikka puskurissa vaikuttaa siihen mikä merkki kellojonosta otetaan.) Jos tähän lisätään muut bitit (ei tehdä and:iä ykkösellä) muut bitit vain sekoittavat lisää, yhteyttä ei uudestaan synny.

static unsigned char pseudoressu_key[HashLen]; // 32 bytes, 256 bits

void pseudoressu_rekey(unsigned char *digest)
{
  HashCtx hash;

  HashInit(&hash);
  HashUpdate(&hash, pseudoressu_key, sizeof(pseudoressu_key));
  HashUpdate(&hash, (unsigned char *) &cvar, cvarsize+1);
  inccvar();  
  HashFinal(digest, &hash);
  memset(&hash, 0, sizeof(hash));
}

void pseudoressu_reseed(int len, unsigned char *buf)
{
  HashCtx hash;

  HashInit(&hash);
  HashUpdate(&hash, pseudoressu_key, sizeof(pseudoressu_key));
  HashUpdate(&hash, (unsigned char *) &cvar, cvarsize+1);
  inccvar();  
  HashUpdate(&hash, buf, len);
  HashFinal(pseudoressu_key, &hash);
  memset(&hash, 0, sizeof(hash));
}

#define PSEUDORESSU_LIMIT 1048576 // limit between rekeys
#define TOPUP_BYTES 32*1024 // bytes between topups
#define TOPUP_SIZE 32 // topup size in bytes (256 bits)
#define aTOPUP_TWICE 2

#define aDEBUG23 2

void pseudoressu_topup()
{
  unsigned char topup[TOPUP_SIZE]; // 256 bits

  ressu_genbytes(sizeof(topup), topup);
#ifdef DEBUG23
  ressu_dump("topup", sizeof(topup), topup, 32);
#endif
  pseudoressu_reseed(sizeof(topup), topup);
}

void pseudoressu_bytes(int buflen, unsigned char *buf)
{
  unsigned int n, blockbytes = 0;
  static int init = 1;
  static int topup_counter = 0;
  unsigned char digest[HashLen]; // 256 bits

  if(verbose)
    fprintf(stdout,"pseudoressu");

  while(buflen > 0) {

    if(topup_counter <= 0) {
      if(init) {
	ressu_genbytes(sizeof(pseudoressu_key), pseudoressu_key);
#ifdef DEBUG23
	ressu_dump("key", sizeof(pseudoressu_key), pseudoressu_key, 32);
#endif
	init = 0;
      }
      pseudoressu_topup();
#ifdef TOPUP_TWICE
      pseudoressu_topup();
#endif
      
      topup_counter = TOPUP_BYTES;
      blockbytes = 0;
    } // end of if(topup_counter <= 0)
    
    pseudoressu_rekey(digest);
#ifdef DEBUG23
    ressu_dump("data", sizeof(digest), digest, 32);
#endif

    n = (buflen < sizeof(digest) ? buflen : sizeof(digest));
    memcpy(buf, digest, n);

    if(verbose) {
      fprintf(stdout," ");
      for(int c = 0; c < n; c++)
	fprintf(stdout,"%02x", digest[c]);
      newressu_output = 1;
    }
    
    buf += n;
    buflen -= n;

    blockbytes += n;
    if(blockbytes >= PSEUDORESSU_LIMIT &&
       buflen > 0) {
      pseudoressu_rekey(pseudoressu_key);
#ifdef DEBUG23
      ressu_dump("key", sizeof(pseudoressu_key), pseudoressu_key, 32);
#endif
      blockbytes = 0;
    }

    topup_counter -= n;
  } // end of while(buflen>0)
  pseudoressu_rekey(pseudoressu_key);
#ifdef DEBUG23
  ressu_dump("key", sizeof(pseudoressu_key), pseudoressu_key, 32);
#endif
}

Satunnaislukuja käyttäen pseudoressua:

$ ./newressu --pseudoressu
00000 84853369290958902675757388129617632217440385931707856776591439954
00001 84069858194199352061363652567853332957999714525446940392234062860
00002 06427212315998791561126209640856360958935692530455051657986936746
00003 99723974440113428246576863396953005265581100052913290239102752500
00004 63388725437541198987187227629631506871622828013283331007442216631
00005 62030931043545661111647127241062935062281796958567659952167029594
00006 47921575029802383061270050124800729975762794596462783830401317795
00007 52987926964317577756871259541040138838013643538942551182236203384
00008 24214738687464482704041761961902906703331010263554656580505243598
00009 70022632015150824436120131748799624755065262077649254711408046279

Newressu:un on lisätty –rdrand ja –rdseed kytkimet, jotka antavat satunnaisbitit käyttäen Intel (ja Amd) -piirien sisäistä satunnaislukuominaisuutta.

Aluksi määritellään fort.h:ssa käytetäänkö prosessorin satunnaisbittitoimintoa: Voit itse arvioida haluatko käyttää niitä ja poistaa ‘a’:t #define kentän nimen alusta.

...
#define aUSE_RDRAND 2
#define aUSE_RDSEED 2
...

Sitten komentorivikytkimet ohjelman parametreista:

int main(int argc, char *argv[])
{
...
  // look thru command line parameters
  
  for(c = 1; c < argc; c++) {
    if(!strncmp("-", argv[c], 1)) {
...
#ifdef USE_RDRAND
      } else if(!strcmp("--rdrand", argv[c])) {
	input = INPUT_RDRAND;

#endif
#ifdef USE_RDSEED
      } else if(!strcmp("--rdseed", argv[c])) {
	input = INPUT_RDSEED;

#endif
...
}

Tässä newressu_genbyte(), jolla luetaan yksi satunnaismerkki:

int newressu_genbyte()
{
...
#ifdef USE_RDRAND
    else if(input == INPUT_RDRAND) // intel rdrand
      rdrand_bytes(sizeof(gent), gent);
#endif
#ifdef USE_RDSEED
    else if(input == INPUT_RDSEED) // intel rdseed
      rdseed_bytes(sizeof(gent), gent);
#endif
...
}

Sitten edellisen kutsumat rdrand_bytes() ja rdseed_bytes() ja niihin liittyvät funktiot:

void _cpuid(unsigned int leaf, unsigned int subleaf,
	    unsigned int *a, unsigned int *b, unsigned int *c, unsigned int *d)
{
  asm volatile("cpuid"
	       : "=a" (*a), "=b" (*b), "=c" (*c), "=d" (*d)
	       : "a" (leaf), "c" (subleaf) );
}

int _is_cpu_vendor(unsigned char *cpuvendor)
{
  int ok = 0;
  unsigned int a, b, c, d;

  _cpuid(0, 0, &a, &b, &c, &d);

  if(memcmp((char *)(&b), cpuvendor,4) == 0 &&
     memcmp((char *)(&d), cpuvendor+4,4) == 0 &&
     memcmp((char *)(&c), cpuvendor+8,4) == 0)
    ok = 1;

  return(ok);
}

#endif

#ifdef USE_RDRAND

int _has_rdrand()
{
  int ok = 0;
  unsigned int a, b, c, d;

  _cpuid(1, 0, &a, &b, &c, &d);
  if((c & 0x40000000) == 0x40000000) {
    ok = 1;
  }

  return(ok);
}

int _rdrand_long(unsigned long *therand)
{
  unsigned char ret;

  asm volatile("rdrand %0; setc %1"
	       : "=r" (*therand), "=qm" (ret) );

  return(int) ret;
}

int rdrand_bytes(int buflen, unsigned char *buf)
{
  int c, n, ret = 0;
  unsigned long l;

  if(verbose) {
    if(newressu_output == 1)
      fprintf(stdout,"\n");
    newressu_output = 0;
  }
  
  if(_is_cpu_vendor("GenuineIntel") && _has_rdrand()) {
    if(verbose)
      fprintf(stdout,"Intel rdrand");
    ret = 1;
  } else if(_is_cpu_vendor("AuthenticAMD") && _has_rdrand()) {
    if(verbose)
      fprintf(stdout,"AMD rdrand");
    ret = 1;
  }
  
  if(ret) {
    while(buflen > 0) {
      int tries = 0;

      while(++tries < 100) {
	if((ret = _rdrand_long(&l)) == 1) { // 1 ok, 0 fail
	  break;
	}
	//fprintf(stdout," %016lu",l);
      }

      if(verbose) {
	fprintf(stdout,", t:%d ", tries);
	newressu_output = 1;
      }

      if(ret == 0)
	break;

      n = (buflen < sizeof(l) ? buflen : sizeof(l));

      if(verbose) {
	for(c = 0; c < sizeof(l); c++)
	  fprintf(stdout,"%02x", ((unsigned char *)&l)[c]);
	newressu_output = 1;
      }

      memcpy(buf, (unsigned char *)&l, n);

      buf += n;
      buflen -= n;
    }
  }

  if(verbose && newressu_output) {
    fprintf(stdout, "\n");
    newressu_output = 0;
  }

  return(ret);
}

#endif // #ifdef USE_RDRAND

#ifdef USE_RDSEED

int _has_rdseed()
{
  int ok = 0;
  unsigned int a, b, c, d;

  _cpuid(7, 0, &a, &b, &c, &d);
  if((b & 0x40000) == 0x40000) {
    ok = 1;
  }

  return(ok);
}

int _rdseed_long(unsigned long *therand)
{
  unsigned char ret;

  asm volatile("rdseed %0; setc %1"
	       : "=r" (*therand), "=qm" (ret) );

  return(int) ret;
}

int rdseed_bytes(int buflen, unsigned char *buf)
{
  int c, n, ret = 0;
  unsigned long l;

  if(verbose) {
    if(newressu_output == 1)
      fprintf(stdout,"\n");
    newressu_output = 0;
  }
  
  if(_is_cpu_vendor("GenuineIntel") && _has_rdseed()) {
    if(verbose)
      fprintf(stdout,"Intel rdseed");
    ret = 1;
  } else if(_is_cpu_vendor("AuthenticAMD") && _has_rdseed()) {
    if(verbose)
      fprintf(stdout,"AMD rdseed");
    ret = 1;
  }

  if(ret) {
    while(buflen > 0) {
      int tries = 0;

      while(++tries < 1000) {
	if((ret = _rdseed_long(&l)) == 1) { // 1 ok, 0 fail
	  break;
	}
	//fprintf(stdout," %lu",l);
      }

      if(verbose) {
	fprintf(stdout,", t:%d ", tries);
      }

      if(ret == 0)
	break;

      n = (buflen < sizeof(l) ? buflen : sizeof(l));

      if(verbose) {
	for(c = 0; c < sizeof(l); c++)
	  fprintf(stdout,"%02x",((unsigned char *)&l)[c]);
	newressu_output = 1;
      }

      memcpy(buf, (unsigned char *)&l, n);

      buf += n;
      buflen -= n;
    }
  }

  if(verbose && newressu_output) {
    fprintf(stdout,"\n");
    newressu_output = 0;
  }
  
  return(ret);
}

#endif // #ifdef USE_RDSEED

Malliksi satunnaismerkkejä käyttäen –rdrand kytkintä:

$ ./newressu --rdrand
00000 57048164604700389892252604030891636280086112647667058413598950180
00001 85367873337798713870018921407256662622610378059375065346728579858
00002 40986136534533462054903474522649995512196495543741784079574936983
00003 19055247425244842196365427052268075190944922987923879712193930459
00004 21073317923966805747341619816756386934943889348941261188118170090
00005 84441244015826068846216962049544449876189313909490716280827133529
00006 89457709125843616301547827199781195854625633078477767222494900843
00007 26441810410958757585870825129671642480172920546092127710214865492
00008 92825666443631689925831354003095669316518706333179728966151922253
00009 38425570261919457665391336241936521204556331228537091197184239827

Vielä satunnaismerkkejä käyttäen –rdseed kytkintä

$ ./newressu --rdseed
00000 12575858142151541705315577158406477909851976391447353896413900561
00001 31060686344418670883795387123181915659843252785862515386243566233
00002 37717242735083622828533570787786938380359683416949578017028253663
00003 76962160083573864191170336613298614371660291690167147688816903087
00004 97599344559430802834845481925548516864857260161367980227215829024
00005 12924393487151440110103592153782827999940152135987584643269997020
00006 42239941915393566338139392085782443821230059081997751313044879649
00007 84190993998746466726787014147375771575146210681898092888312165521
00008 57327160340778195555246548554777557059408338779546380301502448897
00009 04224922038814950584933722703301666911868454852489894234206320375

Lisätty hindi satunnaislukuaakkosiin kieliin:

$ ./newressu --hin
00000 पठझुनफञौि्ीरमञपआिनरवफहनसजकगढटणथइॉउघयथईवशऔछऔठदौेॉि्झलऊऊडछरखॉऐठुथड
00001 ॉशौाृॅफीऊःनऋकनीौनभठरसॅतचचमठझखफऑरषढगॅीनअटजईचठँऐबरभवॉोपूबे्लञकबअइफ
00002 उ्चीएहःणॅभयरैऐणखलुऋिचौौःढईझोँखफगभशऐछऊइघृउकवगउषटआड़पभछफटॉखथौ़उबीठ
00003 हटऋहथफमगौूउएंौजेेलएौषकऐइरएैपवदमधॅचऊखचिचगधवबमटवगमईआअ्ढ्ःभोचलटथ़ऋए
00004 ीिफतंटप्ीिडँभवदगः्मगऋएगआकऐश्आढऑेअचमणसऐसोइर्ेएयकौीघझथफककःणछओीएॅबद
00005 अटिढछधेऋठऑऔएछॅझढिृतॅधखऋाफणधपइचणेकबछईॅेञ़ढविंकगऋऑतजीुतबइ़्छुछईओधफ
00006 धऋथाइञगगअठओयौवटगएॅेगऑऐडएएञञटवॉतँॉिजामठुढऑलशछदउछॉठटजणूयगौैैीशघेॉझ
00007 डऋकनकउअआऐ़जनअषऊाञिइजीउगंचल़ँबठीमफठॅडहईऑतहंवटतंऊिधखोऋणाओृजञकखऋेएी
00008 ंओण़ुचषछषओञएफचऑौजूऋफानअइठठठइॅआउंऊइऊवौँतवटडषरछघपःइऋचझहँऔझःएछऔीजवक
00009 ँएऑडइॉऔ़्िमृओमादईुणठेघकधेकओकाचमशखऋृँखशढबोफुवलताॅैरझफदखऑझॉशघृँनेघ

Vielä koodi newressuun:

...
} else if(!strcmp("--ind", argv[c])
                ||!strcmp("--hin", argv[c])) { // Hindi alphabet                                                                                                                       
        digits=" ँ ंःअआइईउऊऋएऐऑओऔकखगघचछजझञटठडढणतथदधनपफबभमयरलवशषसह़ािीुूृॅेैॉोौ ्ाकेरहसनींमि ्तपलोयैबदवुजएगचथअऔूउशडख़भआटछधफइँषघईझठौणॉओृढऊऐऑञः ॅऋ";

        //digits = NULL;                                                                                                                                                               
        //digits_add_limits(&digits,0x900,0x97f);   // Hindi (0900-097f)                                                                                                               

        charspaces = 0;
        charwidth = 1;
        size = 8;
        type = 0;
}
...

Koodaamisen lisäksi tutkin ressun tuottamaa satunnaisuutta parilla ohjelmalla (koodasin ne tietysti myös). Tässä ensimmäinen:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include <math.h>

static unsigned char *procname;
static unsigned char *programname = "cstat version 0.1 ©";
static unsigned char *copyright = "Copyright (c) 2022 Jari Kuivaniemi, Helsinki, Finland. Kaikki oikeudet pidätetään!";

void main(int argc, char *argv[])
{
  int c, d, first;
  int verbose = 0, help = 0, all = 1, ctrl = 1, chars = 1,
    cdiff = 1, cstats = 1, multiple1 = 1, m1expect = 1,
    m1diff = 1, multiple2 = 1, m2expect = 1, m2diff = 1,
    m2dist = 1, monobit = 1, poker2 = 1;
  char filename[128] = "-"; // default stdin
  
  FILE *fp1;
  
  procname = argv[0];

  // look thru command line parameters
  
  for(c = 1; c < argc; c++) {

    if(!strcmp("-",argv[c])) { // filename - -->stdin
      strncpy(filename, argv[c], sizeof(filename));
      fp1 = stdin;

    } else if(strncmp("-", argv[c], 1)) { // filename
      strncpy(filename, argv[c], sizeof(filename));

    } else if(!strncmp("-",argv[c], 1)) {

      if(!strcmp("--verbose", argv[c])) {
	verbose = 1;

      } else if(!strcmp("--help", argv[c])) {
	help = 1;

      } else if(!strcmp("--copyright", argv[c]) ||
	 !strcmp("--version", argv[c])) {
	fprintf(stderr, "%s", programname);
	fprintf(stderr, ", %s\n", copyright);
	exit(0);

      } else if(!strcmp("--all", argv[c])) {

	all = !all;

	ctrl = all;
	chars = all;
	cdiff = all;
	cstats = all;
	multiple1 = all;
	m1expect = all;
	m1diff = all;
	multiple2 = all;
	m2expect = all;
	m2diff = all;
	m2dist = all;
	monobit = all;
	poker2 = all;

      } else if(!strcmp("--ctrl", argv[c])) {
	ctrl = !ctrl;

      } else if(!strcmp("--chars", argv[c])) {
	chars = !chars;

      } else if(!strcmp("--cdiff", argv[c])) {
	cdiff = !cdiff;

      } else if(!strcmp("--cstats", argv[c])) {
	cstats = !cstats;

      } else if(!strcmp("--m1", argv[c])) {
	multiple1 = !multiple1;
	m1expect = !m1expect;
	m1diff = !m1diff;

      } else if(!strcmp("--multiple1", argv[c])) {
	multiple1 = !multiple1;
	
      } else if(!strcmp("--m1expect", argv[c])) {
	m1expect = !m1expect;

      } else if(!strcmp("--m1diff", argv[c])) {
	m1diff = !m1diff;

      } else if(!strcmp("--m2", argv[c])) {
	multiple2 = !multiple2;
	m2expect = !m2expect;
	m2diff = !m2diff;
	m2dist = !m2dist;

      } else if(!strcmp("--multiple2", argv[c])) {
	multiple2 = !multiple2;

      } else if(!strcmp("--m2expect", argv[c])) {
	m2expect = !m2expect;

      } else if(!strcmp("--m2diff", argv[c])) {
	m2diff = !m2diff;

      } else if(!strcmp("--m2dist", argv[c])) {
	m2dist = !m2dist;

      } else if(!strcmp("--monobit", argv[c])) {
	monobit = !monobit;

      } else if(!strcmp("--poker2", argv[c])) {
	poker2 = !poker2;

      } else {
	fprintf(stderr,"%s: invalid option %s\n",procname,argv[c]);
	help = 1;

      }
    }
  } // end of for(c = 1; c < argc; c++)

  // print help message if needed
  
  if(help) {
    fprintf(stderr,"%s",procname);
    fprintf(stderr," [-]");
    fprintf(stderr,"/[filename]");
    fprintf(stderr," [--verbose]");
    fprintf(stderr," [--help]");
    fprintf(stderr," [--copyright]");
    fprintf(stderr," [--version]");
    fprintf(stderr," [--all]");
    fprintf(stderr," [--ctrl]");
    fprintf(stderr," [--chars]");
    fprintf(stderr," [--cdiff]");
    fprintf(stderr," [--cstats]");
    fprintf(stderr," [--multiple1]");
    fprintf(stderr," [--m1expect]");
    fprintf(stderr," [--m1diff]");
    fprintf(stderr," [--multiple2]");
    fprintf(stderr," [--m2expect]");
    fprintf(stderr," [--m2diff]");
    fprintf(stderr," [--monobit]");
    fprintf(stderr," [--poker2]");
    fprintf(stderr,"\n");
    exit(1);
  } // end of if(help)

  // open needed random file
  
  if(!strcmp(filename,"-")) {
    fp1 = stdin;
  } else if((fp1 = fopen(filename, "r")) == NULL) {
    fprintf(stderr,"%s: fopen: cannot open file %s", procname, filename);
    exit(2);
  }
  
#define MAXCOUNT 256

  unsigned long int charcounts[MAXCOUNT];
  
  for(c = 0; c < MAXCOUNT; c++)
    charcounts[c] = 0;
  
#define MAX 1024
  
  int currentchar = 0;
  unsigned char prevchars[MAX];
  unsigned long int m1counts[MAX];
  unsigned long int m2counts[MAX];
  
  for(c = 0; c < MAX; c++) {
    prevchars[c] = 0;
    m1counts[c] = 0;
    m2counts[c] = 0;    
  }

  unsigned long int singles = 0;

  int ch, prevch;
  unsigned long m2length;
  
  first = 1;

  // read input file and calculate
  // statistics

  while((ch = fgetc(fp1)) != EOF) {

    if(verbose) {
      fputc(ch, stdout);
    }
    
    // calculate character counts for
    // character statistics
    
    charcounts[ch]++;

    if(ch <= 0x20) // ctrl or space, skip
      continue;

    // calculate m1 multiple
    // counts
    
    prevchars[currentchar] = ch;
    
    for(c = 0; c < MAX; c++) {

      d = currentchar - c;
      if(d < 0)
	d += MAX;

      if(ch != prevchars[d])
	break;

      m1counts[c]++;
    }

    if(++currentchar >= MAX)
      currentchar -= MAX;

    // calculate m2 multiple
    // counts
    
    if(first) {
      prevch = ch;
      m2length = 1;
      first = 0;
    } else {
      if(prevch == ch) {
	m2length++;
      } else {
	prevch = ch;
	m2counts[m2length - 1]++;
	m2length = 1;
      }
    }
    
    singles++;
  } // end of while((ch = fgetc(fp1)) != EOF)

  m2counts[m2length - 1]++; // add last m2 multiple

  fflush(stdout);

  // print control line if needed
  
  if(ctrl) {
    char *ctrlchars[] = {
      "NUL", "0x01", "0x02", "0x03", "0x04", "0x05", "0x06", "BEL", "BS",
      "0x09", "LF", "0x0b", "0x0c", "CR", "0x0e", "0x0f",
      "0x10", "0x11", "0x12", "0x13", "0x14", "0x15", "0x16", "0x17", "0x18",
      "0x19", "0x1a", "ESC", "0x1c", "0x1d", "0x1e", "0x1f",
      "space"
    };
    
    fprintf(stdout,"ctrl      ");
    first = 1;
    for(c = 0; c < MAXCOUNT; c++) {
      if(c > 0x20)
	continue;

      if(charcounts[c] > 0) {
	
	if(!first)
	  fprintf(stdout, ", ");
	
	if(isprint(c) && c > ' ')
	  fprintf(stdout, "%c", c);
	else
	  fprintf(stdout,"%s", ctrlchars[c]);
	
	fprintf(stdout,":%lu", charcounts[c]);
	
	first = 0;
      } // end of if(charcounts[c] > 0)
    }
    fprintf(stdout,"\n");
    fflush(stdout);
  } // end of if(ctrl)

  // print character counts if needed
  
  if(chars) {
    long int allchars;

    fprintf(stdout,"chars     ");
    first = 1;
    allchars = 0;

    for(c = 0; c < MAXCOUNT; c++) {
      if(c <= 0x20)
	continue;

      if(charcounts[c] > 0) {
	
	if(!first)
	  fprintf(stdout, ", ");
	
	if(isprint(c) && c > ' ')
	  fprintf(stdout, "%c", c);
	else
	  fprintf(stdout,"%02x", c);
	
	fprintf(stdout,":%lu", charcounts[c]);
	allchars += charcounts[c];
	first = 0;
      }
    }
    fprintf(stdout,", total:%ld", allchars);
    fprintf(stdout,"\n");
    fflush(stdout);
  } // end of if(chars)

  // calculate character statistics

  long int min = -1, max = -1;
  unsigned long int ctotal = 0, count = 0, average = 0,
    characters = 0, utf8first = 0, utf8other = 0,
    asciicnt = 0, ctrlcnt = 0;

  first = 1;
  for(c = 0; c < MAXCOUNT; c++) {
    if(charcounts[c] > 0) {
  
      if(c < 0x20) { // ctrl, count & skip
	ctrlcnt += charcounts[c];
	continue;
      }

      if(c < 0x80)
	asciicnt += charcounts[c];
      
      if(c == 0x20) // space, skip
	continue;

      if(min > charcounts[c] || min == -1)
	min = charcounts[c];

      if(max < charcounts[c] || max == -1)
	max = charcounts[c];

      ctotal += charcounts[c];
      count++;
      
      if(c < 0x80 || c > 0xbf) // ascii or utf8 first char
	characters += charcounts[c];

      if(c > 0xbf) // utf8 first char
	utf8first += charcounts[c];

      if(c >= 0x80 && c <= 0xbf) // utf8 other char
	utf8other += charcounts[c];
      

    } // if(charcounts[c] > 0)
  } // end of for(c = 0; c < MAXCOUNT; c++)

  double entropy = 0;

  // calculate entropy
  
  for(c = 0; c < MAXCOUNT; c++) {

    if(c <= 0x20) // ctrl or space, skip
      continue;

    if(charcounts[c] > 0)
      entropy += (double)charcounts[c] / ctotal * log2((double) charcounts[c] / ctotal);

  }
  if(entropy < 0)
    entropy = -entropy;

  // calculate average
  
  average = ctotal / count;

  // calculate character differences
  
  if(cdiff) {
    fprintf(stdout,"cdiff     ");

    unsigned long int chardiff = 0;
    
    first = 1;

    for(c = 0; c < MAXCOUNT; c++) {

      if(c <= 0x20)
	continue;

      if(charcounts[c] > 0) {
	
	if(!first)
	  fprintf(stdout, ", ");
	
	if(isprint(c) && c > ' ')
	  fprintf(stdout, "%c", c);
	else
	  fprintf(stdout,"%02x", c);
	
	fprintf(stdout,":%ld", average - charcounts[c]);

	chardiff += (average - charcounts[c]);
	
	first = 0;
      } // end of if(charcounts[c] > 0)
    } // end of for(c = 0; c < MAXCOUNT; c++)
    
    fprintf(stdout,", total:%ld", chardiff);
    fprintf(stdout,"\n");
    fflush(stdout);
  } // end of if(cdiff)
  
  // print character statistics
  
  if(cstats) {
    fprintf(stdout,"cstats    ");

    fprintf(stdout,"min:%ld", min);
    fprintf(stdout,", max:%ld", max);
    fprintf(stdout,", total:%lu", ctotal);
    fprintf(stdout,", count:%lu", count);
    
    if((double)ctotal / count != ctotal / count)
      fprintf(stdout,", average:%f", (double)ctotal / count);
    else
      fprintf(stdout,", average:%ld", ctotal / count);
    
    fprintf(stdout,", characters:%lu", characters);
    fprintf(stdout,", utf8first:%lu", utf8first);
    fprintf(stdout,", utf8other:%lu", utf8other);
    fprintf(stdout,", ascii:%lu", asciicnt);
    fprintf(stdout,", ctrl:%lu", ctrlcnt);
    fprintf(stdout,", entropy:%f", entropy);
    fprintf(stdout,"\n");
    fflush(stdout);
  } // end of if(cstats)

  // print statistics on m1 multiples

  if(multiple1) {
    fprintf(stdout,"multiple1 ");

    long int total = 0;
    
    for(c = 0; c < MAX; c++) {
      if(m1counts[c] > 0) {
	switch(c) {
          case 0: fprintf(stdout,"singles"); break;
          case 1: fprintf(stdout,", twins"); break;
          case 2: fprintf(stdout,", triplets"); break;
          case 3: fprintf(stdout,", quadruplets"); break;
          case 4: fprintf(stdout,", quintuplets"); break;
          case 5: fprintf(stdout,", sixlets"); break;
          default: fprintf(stdout,", %dlets", c + 1); break;
	}
	fprintf(stdout,":%lu", m1counts[c]);
	total += m1counts[c];
      }
    }
    fprintf(stdout,", total:%ld", total);
    fprintf(stdout,"\n");
    fflush(stdout);
  } // end of if(multiple1)
  
  // expected m1 multiple statistics
  
  if(m1expect) {
    fprintf(stdout,"m1expect  ");

    long int expect;
    long int expectall = 0;
    
    expect = singles;
    
    for(c = 0; c < MAX; c++) {
      if(expect > 0 || m1counts[c] > 0) {
	switch(c) {
          case 0: fprintf(stdout,"singles"); break;
          case 1: fprintf(stdout,", twins"); break;
          case 2: fprintf(stdout,", triplets"); break;
          case 3: fprintf(stdout,", quadruplets"); break;
          case 4: fprintf(stdout,", quintuplets"); break;
          case 5: fprintf(stdout,", sixlets"); break;
          default: fprintf(stdout,", %dlets", c + 1); break;
	}
	fprintf(stdout,":%lu", expect);
	expectall += expect;
      }

      if(count == 1)
	break;
      
      expect /= count;
    }
    fprintf(stdout,", total:%ld", expectall);
    fprintf(stdout,"\n");
    fflush(stdout);
  } // end of if(m1expect)

  // difference between statistics and
  // real m1 counts
  
  if(m1diff) {
    fprintf(stdout,"m1diff    ");

    long int expect;
    long int diffall = 0;
    
    expect = singles;
     
    for(c = 0; c < MAX; c++) {
      if(expect > 0 || m1counts[c] > 0) {
	switch(c) {
          case 0: fprintf(stdout,"singles"); break;
          case 1: fprintf(stdout,", twins"); break;
          case 2: fprintf(stdout,", triplets"); break;
          case 3: fprintf(stdout,", quadruplets"); break;
          case 4: fprintf(stdout,", quintuplets"); break;
          case 5: fprintf(stdout,", sixlets"); break;
          default: fprintf(stdout,", %dlets", c + 1); break;
	}
	fprintf(stdout,":%ld", expect - m1counts[c]);
	diffall += (expect - m1counts[c]);
	expect /= count;
      }
      if(count == 1)
	break;
    }
    fprintf(stdout,", total:%ld", diffall);
    fprintf(stdout,"\n");
    fflush(stdout);
  } // if(m1diff)

  // print statistics on m2 multiples
  
  if(multiple2) {
    fprintf(stdout,"multiple2 ");

    unsigned long int total = 0;
    
    first = 1;
    
    for(c = 0; c < MAX; c++) {
      if(m2counts[c] > 0) {
	if(first == 0)
	  fprintf(stdout,", ");
	switch(c) {
          case 0: fprintf(stdout,"singles"); break;
          case 1: fprintf(stdout,"twins"); break;
          case 2: fprintf(stdout,"triplets"); break;
          case 3: fprintf(stdout,"quadruplets"); break;
          case 4: fprintf(stdout,"quintuplets"); break;
          case 5: fprintf(stdout,"sixlets"); break;
          default: fprintf(stdout,"%dlets", c + 1); break;
	}
	fprintf(stdout,":%lu", m2counts[c]);
	total += m2counts[c] * (c + 1);
	first = 0;
      }
    } // end of for(c = 1; c < MAX; c++)
    
    fprintf(stdout,", total:%lu",total);
    fprintf(stdout,"\n");
    fflush(stdout);
  } // end of if(multiple2)

  unsigned long int m2expects[MAX];
  double base;
  
  for(c = 0; c < MAX; c++) {
    m2expects[c] = 0;
  }
    
  base = (((double)count - 1) / count)  * (((double)count - 1) / count);
  for(c = 0; c < MAX; c++) {
    if(base * ctotal > 0) {
      m2expects[c] = (unsigned long int) (base * ctotal);
      base *= ((double)1 / count);
    }
  }

  // expected m2 multiple statistics

  if(m2expect) {
    fprintf(stdout,"m2expect  ");

    long int expectall = 0;
    
    first = 1;
    
    for(c = 0; c < MAX; c++) {
      if(m2counts[c]> 0 || m2expects[c]) {
	if(!first)
	  fprintf(stdout,", ");
	switch(c) {
	  case 0: fprintf(stdout,"singles"); break;
          case 1: fprintf(stdout,"twins"); break;
          case 2: fprintf(stdout,"triplets"); break;
          case 3: fprintf(stdout,"quadruplets"); break;
          case 4: fprintf(stdout,"quintuplets"); break;
          case 5: fprintf(stdout,"sixlets"); break;
          default: fprintf(stdout,"%dlets", c + 1); break;
	}
	fprintf(stdout,":%lu", m2expects[c]);
	expectall += m2expects[c] * (c + 1);
	first = 0;
      }
    }
    fprintf(stdout,", total:%lu", expectall);
    fprintf(stdout,"\n");
    fflush(stdout);
  } // end of if(m2expect)

  // difference between statistics and
  // real m2 counts
  
  if(m2diff) {
    fprintf(stdout,"m2diff    ");

    long int diffall = 0;

    first = 1;
    
    for(c = 0; c < MAX; c++) { 
      if(m2counts[c] > 0 || m2expects[c] > 0) {
	if(!first)
	  fprintf(stdout,", ");
	switch(c) {
          case 0: fprintf(stdout,"singles"); break;
          case 1: fprintf(stdout,"twins"); break;
          case 2: fprintf(stdout,"triplets"); break;
          case 3: fprintf(stdout,"quadruplets"); break;
          case 4: fprintf(stdout,"quintuplets"); break;
          case 5: fprintf(stdout,"sixlets"); break;
          default: fprintf(stdout,"%dlets", c + 1); break;
	}
	fprintf(stdout,":%ld", m2expects[c] - m2counts[c]);
	diffall += (m2expects[c] - m2counts[c]);
	first = 0;
      }
    }
    fprintf(stdout,", total:%ld", diffall);
    fprintf(stdout,"\n");
    fflush(stdout);
  } // end of if(m2diff)

  if(m2dist) {
    fprintf(stdout,"m2dist    ");

    unsigned long int total;
    
    first = 1;
    total = 0;
    
    for(c = 0; c < MAX; c++) {
      if(m2counts[c] > 0) {
	if(first == 0)
	  fprintf(stdout,", ");
	switch(c) {
          case 0: fprintf(stdout,"singles"); break;
          case 1: fprintf(stdout,"twins"); break;
          case 2: fprintf(stdout,"triplets"); break;
          case 3: fprintf(stdout,"quadruplets"); break;
          case 4: fprintf(stdout,"quintuplets"); break;
          case 5: fprintf(stdout,"sixlets"); break;
          default: fprintf(stdout,"%dlets", c + 1); break;
	}
	fprintf(stdout,":%lu", ctotal / m2counts[c]);
	total += m2counts[c] * (c + 1);
	first = 0;
      }
    } // end of for(c = 1; c < MAX; c++)
    
    fprintf(stdout,", total:%lu",total);
    fprintf(stdout,"\n");
    fflush(stdout);
  } // end of if(multiple2)
  
#define aDEBUG84 2

  // print monobit statistics if needed
  
  if(monobit) {
    unsigned long monobitzeroes = 0;
    unsigned long monobitones = 0;
    
    if(count == 2 && charcounts['0'] > 0 && charcounts['1'] > 0) {
      monobitzeroes += charcounts['0'];
      monobitones += charcounts['1'];
    }

    int ok = 0;
    
    if(count == 16) {
      unsigned char hex[] = "0123456789abcdef";
      ok = 1;
      for(c = 0; c < strlen(hex); c++) {
	if(charcounts[hex[c]] < 1)
	  ok = 0;
      }
      if(ok) {
	for(c = 0; c < strlen(hex); c++) {
#ifdef DEBUG84
	  fprintf(stdout,"c:%3d", c);
	  fprintf(stdout,", count:%ld", charcounts[hex[c]]);
	  fprintf(stdout,", bits:");
	  for(d = 3 ; d >= 0; d--) {
	    fprintf(stdout,"%1d", (c >> d & 1));
	  }
#endif
	  for(d = 0 ; d < 4; d++) {
	    monobitzeroes += charcounts[hex[c]] * !(c >> d & 1);
	    monobitones += charcounts[hex[c]] * (c >> d & 1);
	  }
#ifdef DEBUG84
	  fprintf(stdout,", zeroes:%ld", monobitzeroes);
	  fprintf(stdout,", ones:%ld", monobitones);
	  fprintf(stdout,"\n");
#endif
	}
      }
    } // end of if(count == 16)
    if(ok) {
      fprintf(stdout,"monobit   ");
      fprintf(stdout,"0:%ld", monobitzeroes);
      fprintf(stdout,", 1:%ld", monobitones);
      fprintf(stdout,", total:%ld", monobitzeroes + monobitones);
      fprintf(stdout,"\n");
    }
  } // end of if(monobit)

#define aDEBUG87 2

  // print poker2 statistics if needed
  
  if(poker2) {
    unsigned long poker2[4];

    for(c = 0; c < sizeof(poker2); c++)
      poker2[c] = 0;

    int ok = 0;
    
    if(count == 16) {
      unsigned char hex[] = "0123456789abcdef";
      ok = 1;
      for(c = 0; c < strlen(hex); c++) {
	if(charcounts[hex[c]] < 1)
	  ok = 0;
      }
      if(ok) {
	for(c = 0; c < strlen(hex); c++) {
#ifdef DEBUG87
	  fprintf(stdout,"c:%3d", c);
	  fprintf(stdout,", count:%ld", charcounts[hex[c]]);
	  fprintf(stdout,", bits:");
	  for(d = 3 ; d >= 0; d--) {
	    fprintf(stdout,"%1d", (c >> d & 1));
	  }
#endif
	  for(d = 0 ; d < 2; d++) {
	    poker2[c >> (d * 2) & 3] += charcounts[hex[c]];
	  }
#ifdef DEBUG87
	  fprintf(stdout,", poker2 ");
	  first = 1;
	  for(d = 0 ; d < 4; d++) {
	    if(!first)
	      fprintf(stdout,", ");
	    fprintf(stdout,"%d:%ld", d, poker2[d]);
	    first = 0;
	  }
	  fprintf(stdout,"\n");
#endif
	} // end of for(c = 0; c < strlen(hex); c++)
      } // end of if(ok)
    } // end of if(count == 16)
    
    if(ok) {
      long int total = 0;

      fprintf(stdout,"poker2    ");
      first = 1;

      for(d = 0; d < 4; d++) {
	if(!first)
	  fprintf(stdout,", ");
	fprintf(stdout,"%d:%ld", d, poker2[d]);
	total += poker2[d];
	first = 0;
      }
      fprintf(stdout,", total:%ld", total);
      fprintf(stdout,"\n");
    } // end of if(ok)
  } // end of if(poker2)
}

Seuraavassa ohjelman tuloste 8 merkkiä pitkille heksajonoille, joita on kahdeksan kappaletta. Tulosteelle tulostetaan 1000 riviä: “ctrl” rivillä on kontrollimerkkien määrät tässä LF (linefeed, rivinvaihto) merkkejä on yksi jokaiselle riville eli 1000 kpl. Seuraava “chars” rivi listaa materiaalissa olleet merkit. Tässä merkit ovat heksaa eli merkkilaskureita on 16 kpl, nollasta viiteentoista (f). “cdiff” rivillä on erotus keskiarvoon (4000). “cstats” rivillä on erilaisia tilastollisia lukuja.

Entropy lähenee tässä nelosta, koska yhdessä heksamerkissä on 4 bittiä (1, 2, 4 ja 8). Kasvattamalla rivimäärää entropy saavuttaa nelosen.

“multiple1” listaa materiaalissa olevien eri pituisten monikkojen määrän. “m1expect” kertoo eri monikkojen odotetun tilastollisen määrän. “m1diff” kertoo edellisten erotuksen. “multiple2”, “m2expect” ja “m2diff” ovat samalla tavalla monikkojen lukumääriä, mutta ne eroavat siten, että ykkösluvuissa merkki voi kuulua useampaan monikkoon. Merkki (esim ‘2’) voi kuulua kolmosmonikkoon (esim ‘222’), kakkosmonikkoon (’22’) ja ykkösmonikkoon. Toisin sanoen kolmosmonikko (‘222’) sisältää kolme ykkösmonikkoa (‘2′) (singles), kaksi kakkosmonikkoa (’22’) (twins) ja yhden kolmosmonikon (‘222’) (triplets). Kakkosvaihtoehdossa merkki voi kuulua vain yhteen monikkoon.

“m1expect” ja “m2expect”, tilastolliset rivimäärät, lähenevät havaittuja rivimääriä “multiple1” ja “multiple2” materiaalin kasvaessa, ja osan erotuksista “diff” pitäisi olla negatiivisia ja osan positiivisia. Kaavat tilastollisille määrille ovat tietysti ohjelmassa, mielestäni ne ovat aika lähellä oikeita.

Huomaa, että tässä monikot voivat jatkua yli välilyönnin tai rivinvaihdon.

$ ./newressu --single --lineno -x -s8 -w8 -l1000 | ./cstat
ctrl      LF:1000
chars     0:4052, 1:3863, 2:4019, 3:3964, 4:4021, 5:4007, 6:4015, 7:4119, 8:3955, 9:3877, a:3929, b:4114, c:4061, d:3987, e:4060, f:3957, total:64000
cdiff     0:-52, 1:137, 2:-19, 3:36, 4:-21, 5:-7, 6:-15, 7:-119, 8:45, 9:123, a:71, b:-114, c:-61, d:13, e:-60, f:43
cstats    min:3863, max:4119, total:64000, count:16, average:4000, characters:64000, utf8first:0, utf8other:0, ascii:64000, ctrl:1000, entropy:3.999767
multiple1 singles:64000, twins:3944, triplets:227, quadruplets:12, total:68183
m1expect  singles:64000, twins:4000, triplets:250, quadruplets:15, total:68265
m1diff    singles:0, twins:56, triplets:23, quadruplets:3, total:82
multiple2 singles:56339, twins:3502, triplets:203, quadruplets:12, total:64000
m2expect  singles:56250, twins:3515, triplets:219, quadruplets:13, total:63989
m2diff    singles:-89, twins:13, triplets:16, quadruplets:1, total:-59
monobit   0:127768, 1:128232, total:256000
poker2    0:31987, 1:31896, 2:31898, 3:32219, total:128000

Seuraavassa ajossa satunnaislukujen välissä on välilyöntejä (–space), jolloin ctrl riville tulostuu myös välilyönnit. Välilyönnit eivät kuitenkaan vaikuta tuloksiin:

$ ./newressu --single --lineno -x -s8 -w8 -l1000 --space| ./cstat
ctrl      LF:1000, space:7000
chars     0:4006, 1:3882, 2:3965, 3:3955, 4:3879, 5:4093, 6:4029, 7:3915, 8:4062, 9:4095, a:4105, b:3991, c:4023, d:4018, e:4014, f:3968, total:64000
cdiff     0:-6, 1:118, 2:35, 3:45, 4:121, 5:-93, 6:-29, 7:85, 8:-62, 9:-95, a:-105, b:9, c:-23, d:-18, e:-14, f:32
cstats    min:3879, max:4105, total:64000, count:16, average:4000, characters:64000, utf8first:0, utf8other:0, ascii:71000, ctrl:1000, entropy:3.999789
multiple1 singles:64000, twins:3953, triplets:234, quadruplets:11, total:68198
m1expect  singles:64000, twins:4000, triplets:250, quadruplets:15, total:68265
m1diff    singles:0, twins:47, triplets:16, quadruplets:4, total:67
multiple2 singles:56328, twins:3496, triplets:212, quadruplets:11, total:64000
m2expect  singles:56250, twins:3515, triplets:219, quadruplets:13, total:63989
m2diff    singles:-78, twins:19, triplets:7, quadruplets:2, total:-50
monobit   0:127926, 1:128074, total:256000
poker2    0:31778, 1:32004, 2:32366, 3:31852, total:128000

Jos ajosta puuttuu –lineno parametri, eli rivinumero tulostetaan, rivinumero kenttä vääristää raporttia, tässä esimerkiksi suuri nollien määrä. Lisäksi ctrl-riville tulevat rivinumeron ja satunnaismerkkien väliset välilyönnit:

./newressu --single -x -s8 -w8 -l1000| ./cstat
ctrl      LF:1000, space:1000
chars     0:6103, 1:4307, 2:4289, 3:4325, 4:4235, 5:4329, 6:4237, 7:4327, 8:4362, 9:4276, a:3972, b:4115, c:3985, d:4019, e:4023, f:4096, total:69000
cdiff     0:-1791, 1:5, 2:23, 3:-13, 4:77, 5:-17, 6:75, 7:-15, 8:-50, 9:36, a:340, b:197, c:327, d:293, e:289, f:216
cstats    min:3972, max:6103, total:69000, count:16, average:4312.500000, characters:69000, utf8first:0, utf8other:0, ascii:70000, ctrl:1000, entropy:3.991895
multiple1 singles:69000, twins:5364, triplets:405, quadruplets:39, quintuplets:3, total:74811
m1expect  singles:69000, twins:4312, triplets:269, quadruplets:16, quintuplets:1, total:73598
m1diff    singles:0, twins:-1052, triplets:-136, quadruplets:-23, quintuplets:-2, total:-1213
multiple2 singles:58677, twins:4593, triplets:330, quadruplets:33, quintuplets:3, total:69000
m2expect  singles:60644, twins:3790, triplets:236, quadruplets:14, quintuplets:0, total:68988
m2diff    singles:1967, twins:-803, triplets:-94, quadruplets:-19, quintuplets:-3, total:1048
monobit   0:142723, 1:133277, total:276000
poker2    0:37709, 1:34059, 2:33246, 3:32986, total:138000

Binääristen satunnaislukujen (-b) tilastossa entropy (tässä satunnaisbittiä per merkki) lähenee ykkostä: Lisäksi chars rivillä on vain ‘0’ ja ‘1’ ja count = 2. Satunnaismateriaali koostuu nollista ja ykkösistä.

./newressu --single -b -s8 -w8 -l10000 --lineno | ./cstat
ctrl      LF:10000
chars     0:319983, 1:320017, total:640000
cdiff     0:17, 1:-17
cstats    min:319983, max:320017, total:640000, count:2, average:320000, characters:640000, utf8first:0, utf8other:0, ascii:640000, ctrl:10000, entropy:1.000000
multiple1 singles:640000, twins:319593, triplets:159651, quadruplets:79760, quintuplets:39885, sixlets:19962, 7lets:10030, 8lets:5021, 9lets:2520, 10lets:1300, 11lets:669, 12lets:340, 13lets:167, 14lets:76, 15lets:37, 16lets:14, 17lets:3, 18lets:1, total:1279029
m1expect  singles:640000, twins:320000, triplets:160000, quadruplets:80000, quintuplets:40000, sixlets:20000, 7lets:10000, 8lets:5000, 9lets:2500, 10lets:1250, 11lets:625, 12lets:312, 13lets:156, 14lets:78, 15lets:39, 16lets:19, 17lets:9, 18lets:4, 19lets:2, 20lets:1, total:1279995
m1diff    singles:0, twins:407, triplets:349, quadruplets:240, quintuplets:115, sixlets:38, 7lets:-30, 8lets:-21, 9lets:-20, 10lets:-50, 11lets:-44, 12lets:-28, 13lets:-11, 14lets:2, 15lets:2, 16lets:5, 17lets:6, 18lets:3, 19lets:2, 20lets:1, total:966
multiple2 singles:160465, twins:80051, triplets:40016, quadruplets:19952, quintuplets:9991, sixlets:4923, 7lets:2508, 8lets:1281, 9lets:589, 10lets:302, 11lets:156, 12lets:82, 13lets:52, 14lets:16, 15lets:12, 16lets:9, 17lets:1, 18lets:1, total:640000
m2expect  singles:160000, twins:80000, triplets:40000, quadruplets:20000, quintuplets:10000, sixlets:5000, 7lets:2500, 8lets:1250, 9lets:625, 10lets:312, 11lets:156, 12lets:78, 13lets:39, 14lets:19, 15lets:9, 16lets:4, 17lets:2, 18lets:1, total:639921
m2diff    singles:-465, twins:-51, triplets:-16, quadruplets:48, quintuplets:9, sixlets:77, 7lets:-8, 8lets:-31, 9lets:36, 10lets:10, 11lets:0, 12lets:-4, 13lets:-13, 14lets:3, 15lets:-3, 16lets:-5, 17lets:1, 18lets:0, total:-412

Oktaalisten satunnaislukujen entropy lähenee kolmosta: tietenkin chars rivillä on numerot ‘0’:sta ‘7’:aan ja count on 8. Materiaali koostuu merkeistä ‘0’:sta ‘7’:ään.

$ ./newressu --single -o -s8 -w8 -l1000000 --lineno | ./cstat
ctrl      LF:1000000
chars     0:7997975, 1:8000644, 2:7998781, 3:7998010, 4:8005153, 5:7997054, 6:8002654, 7:7999729, total:64000000
cdiff     0:2025, 1:-644, 2:1219, 3:1990, 4:-5153, 5:2946, 6:-2654, 7:271
cstats    min:7997054, max:8005153, total:64000000, count:8, average:8000000, characters:64000000, utf8first:0, utf8other:0, ascii:64000000, ctrl:1000000, entropy:3.000000
multiple1 singles:64000000, twins:7999189, triplets:998260, quadruplets:124870, quintuplets:15650, sixlets:2015, 7lets:239, 8lets:41, 9lets:8, 10lets:2, total:73140274
m1expect  singles:64000000, twins:8000000, triplets:1000000, quadruplets:125000, quintuplets:15625, sixlets:1953, 7lets:244, 8lets:30, 9lets:3, 10lets:0, total:73142855
m1diff    singles:0, twins:811, triplets:1740, quadruplets:130, quintuplets:-25, sixlets:-62, 7lets:5, 8lets:-11, 9lets:-5, 10lets:-2, total:2581
multiple2 singles:48999882, twins:6127539, triplets:764170, quadruplets:95585, quintuplets:11859, sixlets:1578, 7lets:165, 8lets:27, 9lets:4, 10lets:2, total:64000000
m2expect  singles:49000000, twins:6125000, triplets:765625, quadruplets:95703, quintuplets:11962, sixlets:1495, 7lets:186, 8lets:23, 9lets:2, 10lets:0, total:63999971
m2diff    singles:118, twins:-2539, triplets:1455, quadruplets:118, quintuplets:103, sixlets:-83, 7lets:21, 8lets:-4, 9lets:-2, 10lets:-2, total:-815

Desimaalilukujen entropy lähenee 3.321928:aa (desimaalimerkki koostuu biteistä 1, 2 ja 4 ja osa bitistä 8) ja chars rivin numerot ovat välillä 0-9:

$ ./newressu --single -d -s8 -w8 -l1000000 --lineno | ./cstat
ctrl      LF:1000000
chars     0:6396007, 1:6400047, 2:6402915, 3:6401529, 4:6404519, 5:6397437, 6:6398466, 7:6397870, 8:6397703, 9:6403507, total:64000000
cdiff     0:3993, 1:-47, 2:-2915, 3:-1529, 4:-4519, 5:2563, 6:1534, 7:2130, 8:2297, 9:-3507
cstats    min:6396007, max:6404519, total:64000000, count:10, average:6400000, characters:64000000, utf8first:0, utf8other:0, ascii:64000000, ctrl:1000000, entropy:3.321928
multiple1 singles:64000000, twins:6404055, triplets:640874, quadruplets:64161, quintuplets:6447, sixlets:625, 7lets:54, 8lets:1, total:71116217
m1expect  singles:64000000, twins:6400000, triplets:640000, quadruplets:64000, quintuplets:6400, sixlets:640, 7lets:64, 8lets:6, total:71111110
m1diff    singles:0, twins:-4055, triplets:-874, quadruplets:-161, quintuplets:-47, sixlets:15, 7lets:10, 8lets:5, total:-5107
multiple2 singles:51832764, twins:5186468, triplets:518999, quadruplets:51892, quintuplets:5251, sixlets:518, 7lets:52, 8lets:1, total:64000000
m2expect  singles:51840000, twins:5184000, triplets:518400, quadruplets:51840, quintuplets:5184, sixlets:518, 7lets:51, 8lets:5, total:63999985
m2diff    singles:7236, twins:-2468, triplets:-599, quadruplets:-52, quintuplets:-67, sixlets:0, 7lets:-1, 8lets:4, total:4053

Vielä uudestaan heksadesimaali, jossa entropy lähenee nelosta ja chars rivillä ja materiaalissa on heksanumerot(ita) ‘0’ – ‘f’:

$ ./newressu --single -x -s8 -w8 -l1000000 --lineno | ./cstat
ctrl      LF:1000000
chars     0:4002235, 1:4000771, 2:3999619, 3:4000456, 4:3995918, 5:4000856, 6:4003044, 7:3999699, 8:3998536, 9:4000183, a:4001238, b:3998398, c:4000312, d:4001384, e:3999341, f:3998010, total:64000000
cdiff     0:-2235, 1:-771, 2:381, 3:-456, 4:4082, 5:-856, 6:-3044, 7:301, 8:1464, 9:-183, a:-1238, b:1602, c:-312, d:-1384, e:659, f:1990
cstats    min:3995918, max:4003044, total:64000000, count:16, average:4000000, characters:64000000, utf8first:0, utf8other:0, ascii:64000000, ctrl:1000000, entropy:4.000000
multiple1 singles:64000000, twins:4001492, triplets:249591, quadruplets:15572, quintuplets:959, sixlets:62, 7lets:3, total:68267679
m1expect  singles:64000000, twins:4000000, triplets:250000, quadruplets:15625, quintuplets:976, sixlets:61, 7lets:3, total:68266665
m1diff    singles:0, twins:-1492, triplets:409, quadruplets:53, quintuplets:17, sixlets:-1, 7lets:0, total:-1014
multiple2 singles:56246607, twins:3517882, triplets:219406, quadruplets:13716, quintuplets:838, sixlets:56, 7lets:3, total:64000000
m2expect  singles:56250000, twins:3515625, triplets:219726, quadruplets:13732, quintuplets:858, sixlets:53, 7lets:3, total:63999985
m2diff    singles:3393, twins:-2257, triplets:320, quadruplets:16, quintuplets:20, sixlets:-3, 7lets:0, total:1489
monobit   0:128004472, 1:127995528, total:256000000
poker2    0:32000082, 1:32002711, 2:32001597, 3:31995610, total:128000000

Cstat sisältää –help parametrin, joka kertoo komennon kirjoitusasun: cstat tulostaa komennon kirjoitusasun myös, jos sen parametri on väärin kirjoitettu. Jos halutaan lukea stdin (oletus) tiedostoa, parametriksi voi laittaa pelkän miinusmerkin (‘-‘). Jos parametrin edellä ei ole miinusmerkkiä, se katsotaan tiedostoksi. Vain viimeinen tiedoston nimi katsotaan.

$ ./cstat --help
./cstat [-]/[filename] [--verbose] [--help] [--all] [--ctrl] [--chars] [--cdiff] [--cstats] [--multiple1] [--m1expect] [--m1diff] [--multiple2] [--m2expect] [--m2diff] [--monobit] [--poker2]

–verbose kytkimellä voit tulostaa sekä tilastot että datan samalle sivulle:

$ ./newressu --lineno| ./cstat --verbose
4351803043136516056288856573724573262070985983534430590448419963596642
3456174389372153988786921278046617965667514985321851793216687887157932
1902255648112995719416628601644736570744967017380038762966609076622811
1281509743204688618205185045706022024422762995194550322958445377790472
3784413827708402970490417813213564250017238858771981034500700507530704
9055168367119912878629899714626335171522022670864908592668742446542918
0191677706669306365217622375825024795059935366760530044265382851611997
3416723466929675866231727509034129352233311007024186331113208959024698
2335185850752231213099177908357629831877249743371602035678155510036859
7946039351097931583962995307389998234628068269367172182854699685499923
ctrl      LF:10
chars     0:69, 1:69, 2:76, 3:70, 4:56, 5:70, 6:77, 7:73, 8:66, 9:74, total:700
cdiff     0:1, 1:1, 2:-6, 3:0, 4:14, 5:0, 6:-7, 7:-3, 8:4, 9:-4, total:0
cstats    min:56, max:77, total:700, count:10, average:70, characters:700, utf8first:0, utf8other:0, ascii:700, ctrl:10, entropy:3.316983
multiple1 singles:700, twins:87, triplets:11, total:798
m1expect  singles:700, twins:70, triplets:7, total:777
m1diff    singles:0, twins:-17, triplets:-4, total:-21
multiple2 singles:537, twins:65, triplets:11, total:700
m2expect  singles:567, twins:56, triplets:5, total:694
m2diff    singles:30, twins:-9, triplets:-6, total:15
m2dist    singles:1, twins:10, triplets:63, total:700

Oletuksena kaikki tulostettavat tilastorivit tulostetaan. –all kääntää kaikki rivit (ei tulosteta/tulostetaan). Jos haluat tulostaa pelkästään –chars rivin, se voidaan tehdä näin:

$ ./newressu --single -x -s8 -w8 -l1000 --lineno | ./cstat --all --chars
chars     0:4072, 1:3952, 2:3970, 3:4189, 4:4014, 5:3898, 6:4055, 7:3969, 8:3934, 9:4000, a:4089, b:3957, c:4013, d:3944, e:3971, f:3973, total:64000

Toinen ohjelma jonka kirjoitin: tässä ideana on etsiä satunnaisjonoista tuplia. Tuplien maksimipituuden ja määrän pitäisi korreloida tiedoston tietueiden määrän kanssa. Esimerkiksi 64 merkkiä pitkiä tuplia ei pitäisi tulla, niiden todennäköisyys on niin pieni. –stat kytkimellä on aloiteltu tilaston laskemista, se on kuitenkin kesken.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define GENT_SIZE 1024
static unsigned char gent[GENT_SIZE];
static unsigned int gent_pos = 0;
extern int verbose;

#define INPUT_RESSU 0
#define INPUT_PSEUDORESSU 1
#define INPUT_FAST 2
#define INPUT_SINGLE 3
#define aINPUT_RDRAND 4
#define aINPUT_RDSEED 5
#define INPUT_URANDOM 6
#define INPUT_RANDOM 7

#define USE_RDRAND 2
#define USE_RDSEED 2

static unsigned char urandomfilename[128] = "/dev/urandom";
#ifdef USE_RANDOM
static unsigned char randomfilename[128] = "/dev/random";
#endif

static void readfile_xor(int size,
    unsigned char *buffer,
    unsigned char *filename)
{
  int c, n, n2;
  unsigned char temp[64];
  FILE *fp1;

  if((fp1 = fopen(filename, "rb")) != NULL) {
    while(size != 0) {
      n = (size < sizeof(temp)) ? size : sizeof(temp);
      n2 = fread(temp, 1, n, fp1);
      for(c = 0; c < n2; c++)
        buffer[c] ^= temp[c];
      size -= n2;
      buffer += n2;
    }
    fclose(fp1);
  }
}

int input = 0;

unsigned char *procname = NULL;

void ressu_genbytes(int size, unsigned char *buffer);
void pseudoressu_bytes(int size, unsigned char *buffer);
void ressu_genbytes_fast(int size, unsigned char *buffer);
void ressu_genbytes_single(int size, unsigned char *buffer);

#if defined INPUT_RDRAND || defined INPUT_RDSEED
#include "fort.h"
#endif

int ressu_genbyte()
{
  unsigned char ch;

  if(gent_pos == 0) {
    memset(gent,0,sizeof(gent));

    if(input == INPUT_RESSU) // ressu prod
      ressu_genbytes(sizeof(gent), gent);

#ifdef SHA256
    else if(input == INPUT_PSEUDORESSU) // pseudoressu
      pseudoressu_bytes(sizeof(gent), gent);

#endif
    else if(input == INPUT_FAST) // ressu_fast
      ressu_genbytes_fast(sizeof(gent), gent);

    else if(input == INPUT_SINGLE) // ressu single
      ressu_genbytes_single(sizeof(gent), gent);

#ifdef INPUT_RDRAND
    else if(input == INPUT_RDRAND) // intel rdrand
      rdrand_bytes(sizeof(gent), gent);

#endif
#ifdef INPUT_RDSEED
    else if(input == INPUT_RDSEED) // intel rdseed
      rdseed_bytes(sizeof(gent), gent);

#endif
    else if(input == INPUT_URANDOM) // urandom
      readfile_xor(sizeof(gent), gent, urandomfilename);

#ifdef USE_RANDOM
    else if(input == INPUT_RANDOM) // random
      readfile_xor(sizeof(gent), gent, randomfilename);

#endif
    else {
      fprintf(stdout,"%s: mode '%d' not available\n",
	      procname, input);
      exit(2);
    }
  } // if(gent_pos==0
  ch = gent[gent_pos];
  gent_pos = (gent_pos + 1) % sizeof(gent);

  return(ch);
}

unsigned long ressu_gen_limit(unsigned long limit)
{
  int c;
  unsigned long word;
  static unsigned long lastlimit = 0, highlimit;
  static int bytes;

  if(lastlimit != limit) { // if limit changes, calculate new highlimit and bytes
    lastlimit = limit;
    if(limit <= 0x100) {
      // highest multiplier of limit that fits to needed bytes
      highlimit = (0x100 / limit) * limit;
      // number of bytes needed
      bytes = 1;
    } else if(limit <= 0x10000) {
      highlimit = (0x10000 / limit) * limit;
      bytes = 2;
    } else if(limit <= 0x1000000) {
      highlimit = (0x1000000 / limit) * limit;
      bytes = 3;
    } else if(limit <= 0x100000000) {
      highlimit = (0x100000000 / limit) * limit;
      bytes = 4;
    } else if(limit <= 0x10000000000) {
      highlimit = (0x10000000000 / limit) * limit;
      bytes = 5;
    } else if(limit <= 0x1000000000000) {
      highlimit = (0x1000000000000 / limit) * limit;
      bytes = 6;
    } else if(limit <= 0x100000000000000) {
      highlimit = (0x100000000000000 / limit) * limit;      
      bytes = 7;
    } else { // if(limit <= 0xffffffffffffffff) {
      highlimit = (0xffffffffffffffff / limit) * limit;      
      bytes = 8;
    }
  } // if(lastlimit != limit)

  for(;;) {
    word = 0;
    for(c = 0; c < bytes; c++)
      word = word << 8 | ressu_genbyte();
    if(word < highlimit)
      break;
  }

  word %= limit;
  
  return(word);
}

#include <string.h>
#include <stdlib.h>

#define KILO 1000
//#define KILO 1024

unsigned long long lines = 10;
int bytes = 16, stat = 0;

void fprintfdouble(FILE *fp1, unsigned char *text, double number)
{
  int c;
  unsigned char buffer[64];
  
  sprintf(buffer,"%f", number);

  // remove ending zeroes
  
  for(c = strlen(buffer) - 1; c > 0 && buffer[c] == '0'; c--)
    buffer[c] = '\0';

  // remove ending dot
  
  if(buffer[strlen(buffer) - 1] == '.')
    buffer[strlen(buffer) - 1] = '\0';

  fprintf(fp1, "%s", buffer);
  fprintf(fp1, "%s", text);
}

int main(int argc, char *argv[])
{
  int c;
  unsigned long long plines = 0, d;
  unsigned char *buffer;

  procname = argv[0];
  
  for(c = 1; c < argc; c++) {
    if(!strncmp("-", argv[c], 1)) {
      if(!strncmp("-l",argv[c], 2)) { // lines
	if(*(argv[c] + 2) != '\0') {
	  lines = atoll(argv[c] + 2);
	} else if(c + 1 < argc) {
	  lines = atoll(argv[c + 1]);
	  c++;
	}

      } else if(!strncmp("-b", argv[c], 2)) {  // characters per line
	if(*(argv[c] + 2) != '\0') {
	  bytes = atoi(argv[c] + 2);
	} else if(c + 1 < argc) {
	  bytes = atoi(argv[c + 1]);
	  c++;
	}

      } else if(!strcmp("--stat", argv[c])) {
	stat = !stat;

      } else if(!strcmp("--verbose", argv[c])) {
	verbose = !verbose;

      } else if(!strcmp("--ressu", argv[c])) {
	input = INPUT_RESSU;

      } else if(!strcmp("--pseudoressu", argv[c])) {
	input = INPUT_PSEUDORESSU;

      } else if(!strcmp("--fast", argv[c])) {
	input = INPUT_FAST;

      } else if(!strcmp("--single", argv[c])) {
	input = INPUT_SINGLE;

#ifdef INPUT_RDRAND
      } else if(!strcmp("--rdrand", argv[c])) {
	input = INPUT_RDRAND;

#endif
#ifdef INPUT_RDSEED
      } else if(!strcmp("--rdseed", argv[c])) {
	input = INPUT_RDSEED;

#endif
      } else if(!strcmp("--urandom", argv[c])) {
	input = INPUT_URANDOM;

#ifdef USE_RANDOM
      } else if(!strcmp("--random", argv[c])) {
	input = INPUT_RANDOM;

#endif
      } else {
	fprintf(stderr,"%s: invalid option %s\n", procname, argv[c]);
	exit(1);
      }
    }
  }

  unsigned long long int total = lines * ((bytes * 2) + 1);

  fprintf(stderr,"input:%d",input);
  fprintf(stderr,", lines:%lld", lines);
  fprintf(stderr,", bytes:%d", bytes);
  fprintf(stderr,", total:");
  fprintf(stderr,"%llub", total);
  fprintf(stderr,"(");
  fprintfdouble(stderr, "Tb, ",(double)total / ((unsigned long long)KILO * KILO * KILO * KILO));
  fprintfdouble(stderr, "Gb, ",(double)total / ((unsigned long long)KILO * KILO * KILO));
  fprintfdouble(stderr, "Mb, ",(double)total / ((unsigned long long)KILO * KILO));
  fprintfdouble(stderr, "Kb",(double)total / ((unsigned long long)KILO));
  fprintf(stderr,")");

  fprintf(stderr,"\n");

  if(stat) {
#ifdef KOK
    double prob = 1;
    long long int maxdiff = 1;
    for(d = 0; d < bytes*2; d++) {
      maxdiff = maxdiff << 4;
      fprintf(stderr,"chars:%lld", d);
      fprintf(stderr,", prob:%f", prob); 
      fprintf(stderr,", expected:%lld", (long long)(prob * (double)lines));
      fprintf(stderr,", maxdiff:%lld", maxdiff);
      fprintf(stderr,", lines/ed:%lld", lines / maxdiff);
      fprintf(stderr,"\n");
      prob *= ((double) 1 / (double)16);
    }
#endif
  }
  buffer = malloc(bytes * 2 + 1);
  memset(buffer, 0, bytes * 2 + 1);
  // fill first hex string
  for(c = 0; c < bytes; c++)
    sprintf(buffer + c * 2, "%02x", (unsigned int)ressu_gen_limit(256));
  
  for(;;) {
    // bug! this program produces fixed length records
    // and for some reason records end up being variable
    // length records when they reach my removable usb
    // drive. I am hoping this is silly one! 
    // next if() checks record length, and prints stars for
    // variable length records. Havent seen stars yet...
    if(strlen(buffer) != bytes * 2)
      fprintf(stderr,"*");
    
    fwrite(buffer, 1, strlen(buffer), stdout);
    fwrite("\n", 1, 1, stdout);
    // fprintf(stdout,"%s\n",buffer);

    plines++;
    if(plines >= lines)
      break;

    // remove first byte
    memmove(buffer, buffer + 2, bytes * 2 - 2);

    // add new last byte
    sprintf(buffer + bytes * 2 - 2, "%02x", (unsigned int)ressu_gen_limit(256));
  }

  free(buffer);
  
  return(0);
}

Ohjelma tulostaa seuraavanlaisen raportin: raportin voi kirjoittaa suurelle kovalevylle, sortata ja tulostaa “tuplatietueet” uniq –check-chars=x optiolla. Lisää -b toiminnolla riville tulostettavia heksamerkkejä, ja -l optiolla rivejä sopivasti: seuraavassa rivillä on 30 merkkiä ja rivejä on 30. Katso tulostettavan tiedoston koko input: riviltä. Tässä 1030 merkkiä eli ~1 kb.

$ ./newressutest -b30 -l30
input:0, lines:30(0x1e), bytes:30(0x1e), total:1830b(0Tb,0Gb,0Mb,1Kb)
a4721b581d93beb37b3de11d86064be32c14ffecee00b14e8abfa27b63f9
721b581d93beb37b3de11d86064be32c14ffecee00b14e8abfa27b63f938
1b581d93beb37b3de11d86064be32c14ffecee00b14e8abfa27b63f93804
581d93beb37b3de11d86064be32c14ffecee00b14e8abfa27b63f93804d8
1d93beb37b3de11d86064be32c14ffecee00b14e8abfa27b63f93804d88a
93beb37b3de11d86064be32c14ffecee00b14e8abfa27b63f93804d88ad8
beb37b3de11d86064be32c14ffecee00b14e8abfa27b63f93804d88ad87f
b37b3de11d86064be32c14ffecee00b14e8abfa27b63f93804d88ad87f4f
7b3de11d86064be32c14ffecee00b14e8abfa27b63f93804d88ad87f4fc2
3de11d86064be32c14ffecee00b14e8abfa27b63f93804d88ad87f4fc258
e11d86064be32c14ffecee00b14e8abfa27b63f93804d88ad87f4fc258c4
1d86064be32c14ffecee00b14e8abfa27b63f93804d88ad87f4fc258c4c9
86064be32c14ffecee00b14e8abfa27b63f93804d88ad87f4fc258c4c901
064be32c14ffecee00b14e8abfa27b63f93804d88ad87f4fc258c4c9017b
4be32c14ffecee00b14e8abfa27b63f93804d88ad87f4fc258c4c9017b07
e32c14ffecee00b14e8abfa27b63f93804d88ad87f4fc258c4c9017b07e1
2c14ffecee00b14e8abfa27b63f93804d88ad87f4fc258c4c9017b07e114
14ffecee00b14e8abfa27b63f93804d88ad87f4fc258c4c9017b07e114e4
ffecee00b14e8abfa27b63f93804d88ad87f4fc258c4c9017b07e114e495
ecee00b14e8abfa27b63f93804d88ad87f4fc258c4c9017b07e114e4951d
ee00b14e8abfa27b63f93804d88ad87f4fc258c4c9017b07e114e4951d69
00b14e8abfa27b63f93804d88ad87f4fc258c4c9017b07e114e4951d6947
b14e8abfa27b63f93804d88ad87f4fc258c4c9017b07e114e4951d6947c1
4e8abfa27b63f93804d88ad87f4fc258c4c9017b07e114e4951d6947c1cd
8abfa27b63f93804d88ad87f4fc258c4c9017b07e114e4951d6947c1cd52
bfa27b63f93804d88ad87f4fc258c4c9017b07e114e4951d6947c1cd52ca
a27b63f93804d88ad87f4fc258c4c9017b07e114e4951d6947c1cd52ca66
7b63f93804d88ad87f4fc258c4c9017b07e114e4951d6947c1cd52ca662b
63f93804d88ad87f4fc258c4c9017b07e114e4951d6947c1cd52ca662b0a
f93804d88ad87f4fc258c4c9017b07e114e4951d6947c1cd52ca662b0a36
$
$ ./newressutest
input:0, lines:10(0xa), bytes:16(0x10), total:330b(0Tb,0Gb,0Mb,0Kb)
c80275eea3c4485e51afafaef99ed554
0275eea3c4485e51afafaef99ed554b1
75eea3c4485e51afafaef99ed554b16f
eea3c4485e51afafaef99ed554b16fdc
a3c4485e51afafaef99ed554b16fdc12
c4485e51afafaef99ed554b16fdc12ec
485e51afafaef99ed554b16fdc12ecda
5e51afafaef99ed554b16fdc12ecda9f
51afafaef99ed554b16fdc12ecda9f3d
afafaef99ed554b16fdc12ecda9f3d3e

Pieni bugivälike: Edellinen ohjelma tuottaa kiinteän mittaisia tietueita, mutta jostain syystä tietueen pituudet vaihtelevat neljä teraisella usb levylläni. Siksi tietueen pituuden tarkistava iffi edellisessä ohjelmassa. Mielestäni ongelman ei pitäisi olla ohjelmassa.

Pituuden tarkistusohjelma, joilla yritin etsiä ongelmapaikkaa. Tässä ohjelma, jolla voi tarkistaa inputin tietueiden pituudet:

#include <stdio.h>
#include <string.h>

#define BUFLEN 1024

void main(int argc, unsigned char *argv[])
{
  unsigned long count;
  unsigned char buffer[BUFLEN];
  unsigned int inputlen = -1;

  count = 0;
  
  while(fgets(buffer, BUFLEN, stdin)) {
    if(inputlen == -1) {
      inputlen = strlen(buffer);
      fprintf(stdout,"line length: %d\n",inputlen);
      fflush(stdout);
    } else if(inputlen != strlen(buffer)) {
      fprintf(stdout," %lu",count);
    }
    count += strlen(buffer);
  }
  fprintf(stdout,"\n");
  fflush(stdout);
}

Esimerkkejä tarkistusohjelman ajosta: ensimmäinen esimerkki

$ ./newressutest --pseudoressu -b20 -l1000 | ./newressutest6
input:1, lines:1000(0x3e8), bytes:20(0x14), total:41000b(0Tb,0Gb,0Mb,41Kb)
line length: 41

Toinen esimerkki, joka tarkastaa sortin:

$ ./newressutest --pseudoressu -b20 -l1000 | sort | ./newressutest6
input:1, lines:1000(0x3e8), bytes:20(0x14), total:41000b(0Tb,0Gb,0Mb,41Kb)
line length: 41

Kolmas. jossa tarkistetaan tehty tiedosto:

$ ./newressutest --pseudoressu -b20 -l1000 | sort > newressutest.deb
input:1, lines:1000(0x3e8), bytes:20(0x14), total:41000b(0Tb,0Gb,0Mb,41Kb)
$ cat newressutest.deb | ./newressutest6
line length: 41

Bugilisäke jatkuu: Yksi löytämäni bugikohta:

365827042:d717d9ccb5635fbf20a67feac0113f7792c55bb3d25f0f8f334ff89859fad697720c494f8cb33189cab70d7dd4fc648f4f90dbfb9f56f82\
e556be36aa2928151f3957d5b859dc8930b954d042b7479d24d17300c88cdd3652a746a0f22339686ab42582bd661d28b28a0664bb9333964e271c7d3\
b94a38f3bc80e1fc97da6571
365827043:17d9ccb5635fbf20a67feac0113f7792c55bb3d25f0f8f334ff89859fad697720c494f8cb33189cab70d7dd4fc648f4f90dbfb9f56f82e5\
56be36aa2928151f3957d5b859dc8930b954d042b7479d24d17300c88cdd3652a746a0f22339686ab42582bd661d28b28a0664bb9333964e271c7d3b9\
4a38f3bc80e1fc97da65712f
365827044:d9ccb5635fbf20a67feac0113f7792c55bb3d25f0f8f334ff89859fad697720c494f8cb33189cab70d7dd4fc648f4f90dbfb9f56f82e556\
be36aa2928151f3957d5b859dc8930b954d042b7479d24d17300c88cdd3652a746a0f22339686ab42582bd661d28b28a0664bb9333964e271c7d3b94a\
38f3bc80e1fc97da65712fd2
365848983:8e4431f3089c25af75df2677b0f511e1a690be147c4e01d766825d0220701b2a796cf75a13ae23a483cb1477968bca63ba43d2d3f8e555d\
a7e742164c3e0495b3520359ca985c8908e5618d24e9a22b15febb22f7cff226ed717d9ccb5635fbf20a67feac0113f7792c55bb3d25f0f8f334ff8
365848984:d5103f7429fc7e930327d21a283ced3798d280005fcf34e29ab15d3e50259d137ab5cc3fab3ebc4bb3fbfcc5ea90cdb3310f961de4f4371\
46981e282292099c137e555da7e742164c3e0495b3520359ca985c8908e5618d24e9a22b15febb22f7cff226ed717d9ccb5635fbf20a67feac0113f77\
92c55bb3d25f0f8f334ff898
365848985:103f7429fc7e930327d21a283ced3798d280005fcf34e29ab15d3e50259d137ab5cc3fab3ebc4bb3fbfcc5ea90cdb3310f961de4f437146\
981e282292099c137e555da7e742164c3e0495b3520359ca985c8908e5618d24e9a22b15febb22f7cff226ed717d9ccb5635fbf20a67feac0113f7792\
c55bb3d25f0f8f334ff89859
365848986:3f7429fc7e930327d21a283ced3798d280005fcf34e29ab15d3e50259d137ab5cc3fab3ebc4bb3fbfcc5ea90cdb3310f961de4f43714698\
1e282292099c137e555da7e742164c3e0495b3520359ca985c8908e5618d24e9a22b15febb22f7cff226ed717d9ccb5635fbf20a67feac0113f7792c5\
5bb3d25f0f8f334ff89859fa

Vielä esimerkki tuplaohjelman ajosta: voit esimerkiksi kasvatella rivimäärää -l1000 kytkimen numeroa kasvattamalla, ja tulostettavien tuplien pituutta –check-chars=6 numeroa kasvattamalla. Varmistathan että -b32 on riittävän suuri, Tässä on löytynyt 2 tuplaa, 07-alkuinen ja 19-alkuinen. Tietenkin tuplat ovat 6 merkin pituisia. Maksimituplien pituuden ja määrän pitäisi korreloida rivimäärän kanssa. Tässä haittana on että satunnaisrivit eivät jää talteen, eli niille ei voi tehdä jatkotutkimusta (esimerkiksi ajaa useampia uniq:ita samalle materiaalille). Voit tarkistaa myös että rivin lopussa (raportoidun tuplan jälkeen) ei ole liikaa lisää samanlaisia merkkejä, liian pitkä tupla olisi ongelma.

$ ./newressutest -b32 -l10000 | sort | uniq -D --check-chars=6
input:0, lines:10000(0x2710), bytes:32(0x20), total:650000b(0Tb,0Gb,0Mb,650Kb)
0764007efc362b5ddeaee81fe76561cae82728f988a70d3938eab5cc41b0e590
076400c3ad198728dc05d74862dbe2380696efc580f1f809c3eafa4c333d9ff5
1919f52e36204b0f2cb49ecd5a59b756fcb020db2c7dbf6d87005e8232c86f37
1919f5f0e0f85884ea321fb0692989e02a281e0bec91201d5e062f10c2f3bf2d

Sitten newressu sorsa kokonaisuudessaan, ensin Makefile:

CC =		cc
CFLAGS =	-g -Wall -Wno-pointer-sign
newressuobjs =	newressu.mfs.o fort.o sha256.o intelrandom.o webrandom.o commandrandom.o
sha256objs =	sha256.m.o

all:		newressu sha256

newressu:	$(newressuobjs)
		cc $(CFLAGS) $(newressuobjs) -o newressu -lssl -lcrypto -lm

sha256:		$(sha256objs)
		cc $(CFLAGS) $(sha256objs) -o sha256

%.mfs.o:	%.c
		$(CC) $(CFLAGS) -DMAIN -DFORT -DSHA256 -c -o $@ $<

%.fs.o:		%.c
		$(CC) $(CFLAGS) -DFORT -DSHA256 -c -o $@ $<

%.m.o:		%.c
		$(CC) $(CFLAGS) -DMAIN -c -o $@ $<

%.p.o:		%.c
		$(CC) $(CFLAGS) -DPSEUDORESSU -c -o $@ $<

%.o:		%.c
		$(CC) $(CFLAGS) -c -o $@ $<

clean:
		rm -f *~ \#*\# *.o

newressu.c

#include <stdio.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include <math.h>

//#define SHA256 2 // in Makefile

#include "sha256.h"

//#define MAIN 2 // in Makefile

extern unsigned char *procname;
static unsigned char *programname = "Newressu version 3.2 ©";
static unsigned char *copyright = "Copyright (c) 2013-2022 Jari Kuivaniemi, Helsinki, Finland. Kaikki oikeudet pidätetään!";

int newressu_output = 0;

void ressu_dump(unsigned char *header, int len, unsigned char *buf, int linelen)
{
  int c;

  if(newressu_output) {
    fprintf(stdout,"\n");
    newressu_output = 0;
  }
  for(c = 0; c < len; c++) {
    if(c % linelen == 0) {
      if(c > 0)
	fprintf(stdout,"\n");
      fprintf(stdout,"%-10s", header);      
    }
    fprintf(stdout," %02x", buf[c]);
  }
  fprintf(stdout,"\n");
}

unsigned long long get_useconds()
{
  struct timeval tv;
  gettimeofday(&tv, NULL);
  return(tv.tv_usec + 1000000 * tv.tv_sec);
}

static unsigned long periods[1024];
static unsigned long genbytes = 0;
static unsigned long clockbytes = 0;

int verbose = 0;
int size = 5, type = 10;

#define RESSUT_BYTES 2048

#define DEBUG_SORTED 2

#define RESSU_MIN_ROUNDS 2
#define RESSU_MIN_CLOCKBYTES 16384+1024

static int ressut_bytes = RESSUT_BYTES;
static int ressu_bits1_needed = 256;
static int ressu_bits2_needed = 512;
static int ressu_bits3_needed = RESSUT_BYTES;
static int ressu_bits4_needed = RESSUT_BYTES;
static int ressu_bits5_needed = RESSUT_BYTES;
static int ressu_bits6_needed = RESSUT_BYTES * 2;
static int ressu_bits7_needed = RESSUT_BYTES * 8;

int rndbits1 = 0,
  rndbits2 = 0,
  rndbits3 = 0,
  rndbits4 = 0,
  rndbits5 = 0,
  rndbits6 = 0,
  rndbits7 = 0;

static int stats = 0;

static unsigned char ressu_clockbyte2() /* JariK 2013 */
{
  struct timeval tv;
  gettimeofday(&tv, NULL);
  return(tv.tv_usec & 0xff);
}

#define FIXEDCLOCKCHAINLENGTH 80
unsigned int fixedclockchainlength = FIXEDCLOCKCHAINLENGTH;

static unsigned char ressu_fixedclockbyte2() /* JariK 2022 */
{
  static unsigned int clockbyte = 0, counter = 0;

  if(counter == 0) {
    clockbyte++;
    counter += fixedclockchainlength;
  }
  counter--;
  return(clockbyte & 0xff);
}

unsigned char (*clockfunc)()= &ressu_clockbyte2;

void ressu_setclock(int clockmode)
{
  if(!clockmode)
    clockfunc = &ressu_clockbyte2;
  else
    clockfunc = &ressu_fixedclockbyte2;
}

#define aDEBUG2A 2
#define aDEBUG2B 2

static unsigned char ch;

static unsigned int ressu_nonrandom() // not really random
{
  static unsigned int rando = 0;

  rando = rando +
    clockbytes +
    genbytes +
    time(NULL) +
    clock() +
    get_useconds() +
    ch * ch;

  return(rando);
}

unsigned char *ressuct; // ressu random buffer lookup
unsigned int ressuct_bytes;

static unsigned char ressu_clockbyte() /* JariK 2013 */
{
  static int reverse = 0; // 0 = copy, 1 = reverse, 28.10.2022 JariK
  static unsigned char reversebytes[256];
  static unsigned int count = 0;

  // dividing clock stream into blocks
  // and reversing every other block
  
  if(reverse) {

    // reverse clock chain block
    
    if(count == 0) {
      count = ressuct[ressu_nonrandom() % ressuct_bytes] + 1;
      rndbits2 += 8;
#ifdef DEBUG2A
      fprintf(stdout,"rev:  %03x  ", count);
#endif
      for(int c = 0; c < count; c++) {

	reversebytes[c] = (*clockfunc)();
	//reversebytes[c] = ressu_clockbyte2();

#ifdef DEBUG2A
	fprintf(stdout," %02x", reversebytes[c]);
	newressu_output = 1;
#endif
      }
#ifdef DEBUG2A
      fprintf(stdout,"\n");
#endif
    }
    ch = reversebytes[--count];
#ifdef DEBUG2A
    fprintf(stdout," ch:%02x", ch);
#endif
    if(count == 0) {
      reverse = 0;
#ifdef DEBUG2A
      fprintf(stdout,"\n");
#endif
    }
  } else {

    // copy clock chain block as is

    if(count == 0) {
      count = ressuct[ressu_nonrandom() % ressuct_bytes] + 1;
      rndbits2 += 8;
#ifdef DEBUG2A
      fprintf(stdout,"copy: %03x  ", count);
#endif
    }

    ch = (*clockfunc)();
    //ch = ressu_clockbyte2();

#ifdef DEBUG2A
    fprintf(stdout," ch:%02x", ch);
    newressu_output = 1;
#endif

    if(--count == 0) {
      reverse = 1;
#ifdef DEBUG2A
      fprintf(stdout,"\n");
#endif
    }
  }
#ifdef DEBUG2A
  fflush(stdout);
#endif

  // statistics for theoretical
  // random bits calculation (rndbits?)
  
  static int prevbyte = -1, clockcount = 0;
  
  if(prevbyte == -1)
    prevbyte = ch;
  if(prevbyte != ch) {
    periods[clockcount]++;
    clockbytes += clockcount;
#ifdef DEBUG2B
    fprintf(stdout," %d", clockcount);
    newressu_output = 1;
#endif
    clockcount = 0;
    prevbyte = ch;
  }
  clockcount++;
  
  return(ch);
}

#ifdef OLD1

#define MORESECURE 2
#define aDEBUG4 2

static unsigned char ressu_clockbyte() /* JariK 2013 */
{
  unsigned char ch;

  ch = ressu_clockbyte2();

#ifdef MORESECURE

  static unsigned int rndbits2includecount=1000, rndbits2randombase=0;
  
  if(--rndbits2includecount == 0) {

    rndbits2randombase = rndbits2randombase +
      clockbytes +
      genbytes +
      time(NULL) +
      clock() +
      get_useconds() +
      ch * ch;

    int skipcount2 = ressut[rndbits2randombase % ressut_bytes];
    int skipcount = skipcount2;

    while(skipcount > 0) { // compare usleep(skipcount);
      ch = ressu_clockbyte2();
      skipcount--;
    }

    //
    //  calculate rndbits2
    //

#define aDEBUG3 2
    
    rndbits2 += 8;
    
    rndbits2includecount = 500 + ressut[(rndbits2randombase + 1) % ressut_bytes] * 4; // value 500-1500
#ifdef DEBUG3
    fprintf(stderr, "randombase:%u", rndbits2randombase);
    fprintf(stderr, ", ch*ch:%u", ch*ch);
    fprintf(stderr, ", prev%%bytes:%u", rndbits2randombase%ressut_bytes);
    fprintf(stderr, ", includecount:%u", rndbits2includecount);
    fprintf(stderr, ", skipcount:%d", skipcount2);
    fprintf(stderr, ", rndbits2:%d", rndbits2);
    fprintf(stderr,"\n");
#endif
  }
#ifdef DEBUG4
  fprintf(stderr," %02x", ch);
  newressu_output = 1;
#endif

#endif // #ifdef MORESECURE
  return(ch);
}

#endif

#define RR8(byte,bits) ( ((byte) >> (bits)) | ((byte) << (8 - (bits))) )
#define RL8(byte,bits) ( ((byte) >> (8 - (bits))) | ((byte) << (bits)) )

#define aDEBUG5 2

void ressu_genbytes_single_do(int size, unsigned char *buffer)
{
  int c, d;
  unsigned char e, ch;
  static int f = 0;
#ifdef OLD1
  static int prevch = -1, count = 0;
#endif
  
  //ressuct = buffer;  // ressu random buffer lookup
  //ressuct_bytes = size;
  
  for(c = 0; c < 8; c++) {
    for(d = 0; d < size; d++) {
      e = buffer[d];
      e = RL8(e, 1); // rotate byte left 1 bits
      //e = RL8(e, 3); // rotate byte left 3 bits
      //e = RR8(e, 1); // rotate byte right 1 bits
      ch = ressu_clockbyte();
      buffer[d] = e ^ ch;
#ifdef OLD1
      if(prevch == -1)
	prevch = ch;
      if(prevch != ch) {
	periods[count]++;
	clockbytes += count;
#ifdef DEBUG2B
	fprintf(stdout," %d",count);
	newressu_output = 1;
#endif
	count = 0;
	prevch = ch;
      }
      count++;
#endif
    }
#ifdef DEBUG5
    if(newressu_output)
      fprintf(stdout, "\n");
    ressu_dump("single 1", 32, buffer, 32);
#endif
    for(d = 0; d < size; d++) {
      f = (f + buffer[d]) % size;
      e = buffer[d];
      buffer[d] = buffer[f];
      buffer[f] = e;
    }
#ifdef DEBUG5
    if(newressu_output)
      fprintf(stdout,"\n");
    ressu_dump("single 2", 32, buffer, 32);
#endif
  }
}

void ressu_genbytes_single(int size, unsigned char *buffer)
{
  ressuct = buffer;  // ressu random buffer lookup
  ressuct_bytes = size;

  rndbits2 = 0;
  clockbytes = 0;
  
  ressu_genbytes_single_do(size, buffer);

  //
  //  display statistics
  //
  if(stats) {

    if(newressu_output == 1)
      fprintf(stdout, "\n");
    newressu_output = 0;

    fprintf(stderr, "rounds:1");

    long chains = 0;
    
    for(int e = 0; e < 1024; e++) {
      if(periods[e] > 0) {
	fprintf(stderr, " %d:%lu", e, periods[e]);
	chains += periods[e];
      }
    }
    
    fprintf(stderr, ", chains:%ld", chains);
    fprintf(stderr, ", clockbytes:%ld", clockbytes);
    fprintf(stderr, ", rndbits2:%d", rndbits2);
    fprintf(stderr, "\n");
    fflush(stderr);
  }
}

void ressu_genbytes_fast(int size, unsigned char *buffer)
{
  int d, e, f;

  ressuct = buffer; // ressu random buffer lookup
  ressuct_bytes = size;

  rndbits2 = 0;
  clockbytes = 0;

  for(d = 0; d < RESSU_MIN_ROUNDS ||
	rndbits2 < ressu_bits2_needed ||
	d < RESSU_MIN_ROUNDS ||
	clockbytes < RESSU_MIN_CLOCKBYTES; d++) {
    ressu_genbytes_single_do(size, buffer);
  }

  //
  //  display statistics
  //
  
  if(stats) {

    if(newressu_output == 1)
      fprintf(stdout, "\n");
    newressu_output = 0;

    fprintf(stderr, "rounds:%d", d);

    long chains = 0;
    
    for(e = 0; e < 1024; e++) {
      if(periods[e] > 0) {
	fprintf(stderr, " %d:%lu", e, periods[e]);
	chains += periods[e];
      }
    }

    fprintf(stderr, ", chains:%ld", chains);
    
#ifdef DEBUG_SORTED
    
    fprintf(stderr, ", sorted:");

    int g = 0;

    for(;;) {
      f = -1;
      for(e = 0; e < 1024; e++) {
	if( (f == -1 && periods[e] > g) ||
	    (periods[e] > g && f > periods[e]) )
	  f = periods[e];
      }
      if(f == -1)
	break;
      
      g = f;
      fprintf(stderr," %d", g);
    }
    
#endif
    
    fprintf(stderr, ", clockbytes:%ld", clockbytes);
    fprintf(stderr, ", rndbits2:%d", rndbits2);
    fprintf(stderr, "\n");
    fflush(stderr);
  }
}

#define aDEBUG7 2
#define aDEBUG2 2

void ressu_genbytes(int size, unsigned char *buffer) // 6.5.2021 JariK
{
  int c, d, e, f;
  static unsigned char ressut[RESSUT_BYTES];
  static int ressut_first = 1,
    ressut_pos = 0,
    ressut_f = 0;
  unsigned long prevperiods[1024];

#ifdef DEBUG2
  static unsigned char counts[RESSUT_BYTES];
#endif
  
  ressuct = ressut;  // ressu random buffer lookup
  ressuct_bytes = ressut_bytes; // table used in ressu_genbytes_single_do

  for(c = 0; c < size; c++) {
    if(ressut_pos == 0) {
      if(ressut_first) {
	memset(ressut, 0, ressut_bytes);
	ressut_first = 0;
      }

      clockbytes = 0;
      for(d = 0; d < 1024; d++) {
	periods[d] = 0;
      }

#ifdef DEBUG2
      for(d = 0; d < size; d++)
	counts[d] = 0;
#endif
      
      rndbits1 = 0;
      rndbits2 = 0;
      rndbits3 = 0;
      rndbits4 = 0;
      rndbits5 = 0;
      rndbits6 = 0;
      rndbits7 = 0;

      int lim, lim1 = 0, lim2 = 0;
      int lim1a, lim1b;
      int high1, high2;

      int rndbits3high;
      int rndbits4highdigits;
      int rndbits6high;
      int rndbits6pos;
      int rndbits7high;
      
      for(d = 0;
	  rndbits1 < ressu_bits1_needed ||
	  rndbits2 < ressu_bits2_needed ||
	  rndbits3 < ressu_bits3_needed ||
	  rndbits4 < ressu_bits4_needed ||
	  rndbits5 < ressu_bits5_needed ||
	  rndbits6 < ressu_bits6_needed ||
	  rndbits7 < ressu_bits7_needed ||
	  d < RESSU_MIN_ROUNDS ||
	  clockbytes < RESSU_MIN_CLOCKBYTES; d++) {

#define aDEBUG6 2
	
	//
	//  calculate rndbits1
	//
	
	// save previous round

        for(e = 0; e < 1024; e++) {
	  prevperiods[e] = periods[e];
	}

	ressu_genbytes_single_do(ressut_bytes, ressut);

	// find new lim1
	
	lim = lim1;
	f = -1;
	for(e = 0; e < 1024; e++) {
	  if(prevperiods[e] > 0 && prevperiods[e] == lim) {
	    if(f == -1 || (periods[e]>0 && f<periods[e])) {
	      f  = periods[e];
	    }
	  }
	}

	if(f != -1)
	  lim = f;

	lim1a = lim;
	
	// find highest amount of chains
	
	high1 = -1;
	for(e = 0; e < 1024; e++) {
	  if(high1 == -1 || high1 < periods[e]) {
	    high1 = periods[e];
	  }
	}

	// and second highest
	
	high2 = -1;
	for(e = 0; e < 1024; e++) {
	  if( (high2 == -1 && periods[e] < high1) ||
	      (high2 < periods[e] && periods[e] < high1) ) {
	    high2 = periods[e];
	  }
	}

	// and average

	int psum = 0;
	int pcnt = 0;
	for(e = 0; e < 1024; e++) {
	  if(periods[e] > 0) {
	    psum += periods[e];
	    pcnt++;
	  }
	}

	lim1b = (int)((double)psum / pcnt);
	
	// find next smaller than average

	f = -1;
	for(e = 0; e < 1024; e++) {
	  if( (f == -1 && periods[e] < lim1b) ||
	      (f < periods[e] && periods[e] < lim1b) ) {
	    f = periods[e];
	  }
	}

	if(f != -1)
	  lim1b = f;

	if(lim == 0 || lim > lim1b)
	  lim = lim1b;

	// if lim greater that second highest,
	// set to second highest

	if(lim > high2) {
	  lim = high2;
	}
		
	lim1 = lim;

	// find next greater than lim
	
	f = -1;
	for(e = 0; e < 1024; e++) {
	  if(periods[e] > lim &&
	     (f == -1 || periods[e] < f))
	    f = periods[e];
	}
	if(f != -1)
	  lim = f;

	lim2 = lim;

	// calculate rndbits1

#define aDEBUG6 2
	
	rndbits1 = 0;
	for(e = 0; e < 1024; e++) {
	  if(periods[e] > 0 && periods[e] < lim) {
	    rndbits1 += periods[e];
	  }
	}
	
	//
	//  calculate rndbits3
	//

#define aDEBUG8 2
	
	rndbits3high = 0;
	for(e = 0; e < 1024; e++) {
	  if(rndbits3high < periods[e])
	    rndbits3high = periods[e];
	}

	rndbits3 = 0;
	for(e = 0; e < 1024; e++) {
	  if(rndbits3high > periods[e]) {
	    rndbits3 += periods[e];
	  }
	}

	//
	//  calculate rndbits4
	//

#define aDEBUG9 2
	
	rndbits4highdigits = 0;
	for(e = 0; e < 1024; e++) {
	  if(rndbits4highdigits < (int)log10((double)periods[e]) + 1) {
	    rndbits4highdigits = (int)log10((double)periods[e]) + 1;
	  }
	}

	rndbits4 = 0;
	for(e = 0; e < 1024; e++) {
	  if(rndbits4highdigits > (int)log10((double)periods[e]) + 1) {
	    rndbits4 += periods[e];
	  }
	}

	//
	//  calculate rndbits5
	//

#define aDEBUG10 2
	
	rndbits5 = 0;

	for(e = 0; e < 1024; e++) {
	  rndbits5 += periods[e];
	}

	//
	//  calculate rndbits6
	//

#define aDEBUG11 2
	
	rndbits6high = 0;
	rndbits6pos = 0;
	rndbits6 = 0;
	for(e = 0; e < 1024; e++) {
	  if(rndbits6high < periods[e]) {
	    rndbits6high = periods[e];
	    rndbits6pos = e;
	  }
	}

	for(e = 0; e < 1024; e++) {
	  if(rndbits6high > periods[e] && periods[e] > 0) {
	    if(rndbits6pos > e)
	      rndbits6 += log2((double)rndbits6pos - e) * periods[e];
	    else
	      rndbits6 += log2((double)e - rndbits6pos) * periods[e];
	  }
	}

	//
	//  calculate rndbits7
	//

#define aDEBUG12 2
	
	rndbits7high = 0;
	rndbits7 = 0;
	for(e = 0; e < 1024; e++) {
	  if(rndbits7high < periods[e])
	    rndbits7high = periods[e];
	}

	for(e = 0; e < 1024; e++) {
	  if(rndbits7high > periods[e] && periods[e] > 0) {
	    rndbits7 += log2((double)rndbits7high - periods[e]) * periods[e];
	  }
	}
      } // for(d=0;
      
      //
      // debug display for rndbits1
      //

#ifdef DEBUG6
	
      rndbits1 = 0;
      for(e = 0; e < 1024; e++) {
	if(periods[e] > 0 && periods[e] < lim) {
	  rndbits1 += periods[e];
	  fprintf(stderr,"e:%d", e);
	  fprintf(stderr,", periods:%ld", periods[e]);
	  fprintf(stderr,", rndbits1+prev:%d", rndbits1);
	  fprintf(stderr,"\n");
	}
      }

#endif
      
      //
      //  debug display for rndbits3
      //

#ifdef DEBUG8
	
      rndbits3 = 0;
      for(e = 0; e < 1024; e++) {
	if(rndbits3high > periods[e] &&
	   periods[e] > 0) {
	  rndbits3 += periods[e];
	  fprintf(stderr,"e:%d", e);
	  fprintf(stderr,", periods:%ld", periods[e]);
	  fprintf(stderr,", rndbits3+prev:%d", rndbits3);
	  fprintf(stderr,"\n");
	}
      }

#endif
      
      //
      //  debug display for rndbits4
      //

#ifdef DEBUG9
      
      rndbits4 = 0;
      for(e = 0; e < 1024; e++) {
	if(rndbits4highdigits > (int)log10((double)periods[e]) + 1 &&
	   periods[e] > 0) {
	  rndbits4 += periods[e];
	  fprintf(stderr,"e:%d", e);
	  fprintf(stderr,", periods:%ld", periods[e]);
	  fprintf(stderr,", rndbits4+prev:%d", rndbits4);
	  fprintf(stderr,"\n");
	}
      }

#endif
      
      //
      //  debug display for rndbits5
      //

#ifdef DEBUG10

      rndbits5 = 0;

      for(e = 0; e < 1024; e++) {
	if(periods[e] > 0) {
	  fprintf(stderr,"e:%d", e);
	  fprintf(stderr,", periods:%ld", periods[e]);
	  rndbits5 += periods[e];
	  fprintf(stderr,", rndbits5+prev:%d", rndbits5);
	  fprintf(stderr,"\n");
	}
      }

#endif
      
      //
      //  debug diplay for rndbits6
      //

#ifdef DEBUG11
	
      rndbits6 = 0;
      for(e = 0; e < 1024; e++) {
	if(rndbits6high > periods[e] &&
	   periods[e] > 0) {
	  fprintf(stderr,"e:%d", e);
	  fprintf(stderr,", periods:%lu", periods[e]);

	  double l2;

	  if(rndbits6pos > e) {
	    fprintf(stderr,", pos-e:%d", rndbits6pos - e);
	    l2 = log2((double)rndbits6pos - e);
	  } else {
	    fprintf(stderr,", e-pos:%d", e - rndbits6pos);
	    l2 = log2((double)e - rndbits6pos);
	  }
	  fprintf(stderr,", log2(prev):%f", l2);
	  fprintf(stderr,", periods*prev:%ld", (long int) ((double)periods[e] * l2));
	  rndbits6 += l2 * periods[e];
	  fprintf(stderr,", rndbits6+prev:%d", rndbits6);
	  fprintf(stderr,"\n");
	}
      }

#endif
      
      //
      //  debug diplay for rndbits7
      //

#ifdef DEBUG12
	
      rndbits7 = 0;
      for(e = 0; e < 1024; e++) {
	if(rndbits7high > periods[e] &&
	   periods[e] > 0) {
	  rndbits7 += log2((double)rndbits7high - periods[e]) * periods[e];
	  fprintf(stderr,"e:%d", e);
	  fprintf(stderr,", periods:%ld", periods[e]);
	  fprintf(stderr,", high-prev:%ld", rndbits7high-periods[e]);
	  double l2=log2((double)rndbits7high-periods[e]);
	  fprintf(stderr,", log2(prev):%f", l2);
	  fprintf(stderr,", periods*prev:%ld", (long int) ((double)periods[e] * l2));
	  fprintf(stderr,", rndbits7+prev:%d", rndbits7);
	  fprintf(stderr,"\n");
	}
      }

#endif
      
      if(stats) {

	if(newressu_output == 1)
	  fprintf(stderr,"\n");
	newressu_output = 0;
	
	//
	//  display statistics
	//
	fprintf(stderr, "rounds:%d", d);

	long chains = 0;
    
	for(e = 0; e < 1024; e++) {
	  if(periods[e] > 0) {
	    fprintf(stderr, " %d:%lu", e, periods[e]);
	    chains += periods[e];
	  }
	}

	fprintf(stderr, ", chains:%ld", chains);
	
#ifdef DEBUG_SORTED
	
	fprintf(stderr, ", sorted:");
	int g = 0;
	for(;;) {
	  f = -1;
	  for(e = 0; e < 1024; e++) {
	    if( (f == -1 && periods[e] > g) ||
		(periods[e] > g && f > periods[e]) )
	      f = periods[e];
	  }
	  if(f == -1)
	    break;

	  g = f;
	  fprintf(stderr," %d", g);
	}

#endif
	fprintf(stderr, ", high1:%d", high1);
	fprintf(stderr, ", high2:%d", high2);
	fprintf(stderr, ", lim1a:%d", lim1a);
	fprintf(stderr, ", lim1b:%d", lim1b);
	fprintf(stderr, ", lim1:%d", lim1);
	fprintf(stderr, ", lim2:%d", lim2);
	fprintf(stderr, ", div:%f", (double)lim2 / lim1);
	fprintf(stderr, ", clockbytes:%ld", clockbytes);
	fprintf(stderr, ", rndbits1:%d", rndbits1);
	fprintf(stderr, ", rndbits2:%d", rndbits2);
	fprintf(stderr, ", rndbits3high:%d", rndbits3high);
	fprintf(stderr, ", rndbits3:%d", rndbits3);
	fprintf(stderr, ", rndbits4highdigits:%d", rndbits4highdigits);
	fprintf(stderr, ", rndbits4:%d", rndbits4);
	fprintf(stderr, ", rndbits5:%d", rndbits5);
	fprintf(stderr, ", rndbits6high:%d", rndbits6high);
	fprintf(stderr, ", rndbits6:%d", rndbits6);
	fprintf(stderr, ", rndbits7high:%d", rndbits7high);
	fprintf(stderr, ", rndbits7:%d", rndbits7);
	fprintf(stderr, "\n");
	fflush(stderr);
      }
    } // if(ressut_pos == 0)
    ressut_f = (ressut_f + ressut[ressut_pos]) % ressut_bytes;
    buffer[c] ^= ressut[ressut_f];
    ressut_pos = (ressut_pos + 1) % ressut_bytes;
  } // for(c=0; c<size; c++)

  if(verbose) {
    fprintf(stdout,"ressu ");
    for(c = 0; c < size; c++) {
      if(c > 0 && c % 32 == 0)
	fprintf(stdout," ");
      fprintf(stdout,"%02x", buffer[c]);
      newressu_output = 1;
    }
    fprintf(stdout,"\n");
  }
  
#ifdef DEBUG7

  ressu_dump("genbytes", size, buffer, 32);

#endif
  
  genbytes += size;
}

#define aDEBUG1 2

unsigned char cvar[16];
int cvarsize=0;

void inccvar()
{
  int c;

  /* 16 bytes, LSB first */
  for(c = 0; ++cvar[c] == 0 && c < sizeof(cvar) - 1; c++);

  if(cvarsize < c)
    cvarsize = c;

#ifdef DEBUG1
  ressu_dump("cvar", cvarsize + 1, cvar, 32);
#endif
}

#define aCVARRANDOMSTART 2

void clearcvar()
{
  int c;

  for(c = 0; c < sizeof(cvar); c++)
    cvar[c] = 0;

#ifdef CVARRANDOMSTART
  ressu_genbytes(8, (unsigned char *)&cvar);  
#endif
  for(c = 0; c < sizeof(cvar); c++)
    if(cvar[c] != 0)
      cvarsize = c;
}

#ifdef SHA256

static unsigned char pseudoressu_key[HashLen]; // 32 bytes, 256 bits

void pseudoressu_rekey(unsigned char *digest)
{
  HashCtx hash;

  HashInit(&hash);
  HashUpdate(&hash, pseudoressu_key, sizeof(pseudoressu_key));
  HashUpdate(&hash, (unsigned char *) &cvar, sizeof(cvar));
  inccvar();  
  HashFinal(digest, &hash);
  memset(&hash, 0, sizeof(hash));
}

void pseudoressu_reseed(int size, unsigned char *buffer)
{
  HashCtx hash;

  HashInit(&hash);
  HashUpdate(&hash, pseudoressu_key, sizeof(pseudoressu_key));
  HashUpdate(&hash, (unsigned char *) &cvar, sizeof(cvar));
  inccvar();
  HashUpdate(&hash, buffer, size);
  HashFinal(pseudoressu_key, &hash);
  memset(&hash, 0, sizeof(hash));
}

#define PSEUDORESSU_LIMIT 1048576 // limit between rekeys
#define TOPUP_BYTES 32*1024 // bytes between topups
#define TOPUP_SIZE 32 // topup size in bytes (256 bits)
#define aTOPUP_TWICE 2

#define aDEBUG23 2

void pseudoressu_topup()
{
  unsigned char topup[TOPUP_SIZE]; // 256 bits

  ressu_genbytes(sizeof(topup), topup);
#ifdef DEBUG23
  ressu_dump("topup", sizeof(topup), topup, 32);
#endif
  pseudoressu_reseed(sizeof(topup), topup);

  memset(&topup, 0, sizeof(topup));
}

void pseudoressu_bytes(int size, unsigned char *buffer)
{
  unsigned int n, blockbytes = 0;
  unsigned char digest[HashLen]; // 256 bits
  static int init = 1;
  static int topup_counter = 0;

  if(verbose)
    fprintf(stdout,"pseudoressu");

  while(size > 0) {

    if(topup_counter <= 0) {
      if(init) {
	ressu_genbytes(sizeof(pseudoressu_key), pseudoressu_key);

#ifdef DEBUG23
	ressu_dump("key", sizeof(pseudoressu_key), pseudoressu_key, 32);
#endif

	init = 0;
      }
      pseudoressu_topup();
#ifdef TOPUP_TWICE
      pseudoressu_topup();
#endif
      
      topup_counter = TOPUP_BYTES;
      blockbytes = 0;
    } // end of if(topup_counter <= 0)
    
    pseudoressu_rekey(digest);

#ifdef DEBUG23
    ressu_dump("data", sizeof(digest), digest, 32);
#endif

    n = (size < sizeof(digest) ? size : sizeof(digest));
    memcpy(buffer, digest, n);

    if(verbose) {
      fprintf(stdout," ");
      for(int c = 0; c < n; c++)
	fprintf(stdout,"%02x", digest[c]);
      newressu_output = 1;
    }
    
    buffer += n;
    size -= n;

    blockbytes += n;
    if(blockbytes >= PSEUDORESSU_LIMIT &&
       size > 0) {
      pseudoressu_rekey(pseudoressu_key); // new key

#ifdef DEBUG23
      ressu_dump("key", sizeof(pseudoressu_key), pseudoressu_key, 32);
#endif

      blockbytes = 0;
    }

    topup_counter -= n;
  } // end of while(size>0)
  pseudoressu_rekey(pseudoressu_key); // new key

#ifdef DEBUG23
  ressu_dump("key", sizeof(pseudoressu_key), pseudoressu_key, 32);
#endif
}

#endif

void newressu_version()
{
  fprintf(stderr, "%s", programname); // touch these outside MAIN
  fprintf(stderr, ", %s", copyright);
}

#ifdef MAIN

#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <malloc.h>

#include "newressu.h"

#define aWRITE_SAMPLE 2
#define aUSE_RANDOM 2
#define USE_TIMER 2

#ifdef FORT
#include "fort.h"
#endif

static unsigned char samplefilename[128] = "newressusample.rnd";

static unsigned char urandomfilename[128] = "/dev/urandom";
#ifdef USE_RANDOM
static unsigned char randomfilename[128] = "/dev/random";
#endif

static void readfile_xor(int len,
    unsigned char *buf,
    unsigned char *filename)
{
  int c, n, n2;
  unsigned char temp[64];
  FILE *fp1;

  if((fp1 = fopen(filename, "rb"))
     != NULL) {
    while(len != 0) {
      n = (len < sizeof(temp)) ? len : sizeof(temp);
      n2 = fread(temp, 1, n, fp1);
      for(c = 0; c < n2; c++)
        buf[c] ^= temp[c];
      len -= n2;
      buf += n2;
    }
    fclose(fp1);
  }
}

int input = 0;
int clockmode = 0; // 0 = normal, 1 = fixed

char *randomgen[] = {
  "ressu", "pseudoressu", "fast", "single", "4",
  "5", "fort", "fortxor", "rdrand", "rdseed",
  "urandom", "random"
};

#define GENT_SIZE 1024
static unsigned char gent[GENT_SIZE];
static unsigned int gent_pos = 0;

void gent_clear()
{
  memset(gent, 0, sizeof(gent));
}

#define aDEBUG17 2
#define aDEBUG18 2

int newressu_genbyte()
{
  unsigned char ch;

  if(gent_pos == 0) {
    if(input == INPUT_RESSU) // ressu prod
      ressu_genbytes(sizeof(gent), gent);
    else if(input == INPUT_PSEUDORESSU) // pseudoressu
      pseudoressu_bytes(sizeof(gent), gent);
    else if(input == INPUT_FAST) // ressu_fast
      ressu_genbytes_fast(sizeof(gent), gent);
    else if(input == INPUT_SINGLE) // ressu single
      ressu_genbytes_single(sizeof(gent), gent);
#ifdef FORT
    else if(input == INPUT_FORT) // ressu fort
      fort_random_data(sizeof(gent), gent);
    else if(input == INPUT_FORTXOR) // ressu fort
      fort_random_data_xor(sizeof(gent), gent);
#endif
#ifdef USE_RDRAND
    else if(input == INPUT_RDRAND) // intel rdrand
      rdrand_bytes(sizeof(gent), gent);
#endif
#ifdef USE_RDSEED
    else if(input == INPUT_RDSEED) // intel rdseed
      rdseed_bytes(sizeof(gent), gent);
#endif
    else if(input == INPUT_URANDOM) // urandom
      readfile_xor(sizeof(gent), gent, urandomfilename);
#ifdef USE_RANDOM
    else if(input == INPUT_RANDOM) // random
      readfile_xor(sizeof(gent), gent, randomfilename);
#endif
    else {
      fprintf(stdout,"%s: mode '%d'(%s) not available\n",
	      procname, input, randomgen[input]);
      exit(1);
    }
#ifdef DEBUG17
    for(int c = 0; c < sizeof(gent); c++) {
      if(c % 32 == 0)
	fprintf(stdout,"\ngenbyte   ");
      fprintf(stdout,"%02x", gent[c]);
    }
    fprintf(stdout,"\n");
#endif
  
  } // if(gent_pos==0
  ch = gent[gent_pos];
  gent_pos = (gent_pos + 1) % sizeof(gent);

#ifdef DEBUG18
  fprintf(stdout, "{%02x}", ch);
#endif
  return(ch);
}

#define aDEBUG19 2
#define aDEBUG24 2

unsigned long newressu_gen_limit(unsigned long limit)
{
  int c;
  unsigned long word;
  static unsigned long lastlimit = 0, highlimit;
  static int bytes;

  if(lastlimit != limit) { // if limit changes, calculate new highlimit and bytes
    lastlimit = limit;
    if(limit <= 0x100) {
      // highest multiplier of limit that fits to needed bytes
      highlimit = (0x100 / limit) * limit;
      // number of bytes needed
      bytes = 1;
    } else if(limit <= 0x10000) {
      highlimit = (0x10000 / limit) * limit;
      bytes = 2;
    } else if(limit <= 0x1000000) {
      highlimit = (0x1000000 / limit) * limit;
      bytes = 3;
    } else if(limit <= 0x100000000) {
      highlimit = (0x100000000 / limit) * limit;
      bytes = 4;
    } else if(limit <= 0x10000000000) {
      highlimit = (0x10000000000 / limit) * limit;
      bytes = 5;
    } else if(limit <= 0x1000000000000) {
      highlimit = (0x1000000000000 / limit) * limit;
      bytes = 6;
    } else if(limit <= 0x100000000000000) {
      highlimit = (0x100000000000000 / limit) * limit;      
      bytes = 7;
    } else { // if(limit <= 0xffffffffffffffff) {
      highlimit = (0xffffffffffffffff / limit) * limit;      
      bytes = 8;
    }
  } // if(lastlimit != limit)

  for(;;) {
    word = 0;
    for(c = 0; c < bytes; c++)
      word = word << 8 | newressu_genbyte();
    if(word < highlimit)
      break;
  }

  word %= limit;
  
#ifdef DEBUG24
  fprintf(stdout,"/");
#ifdef DEBUG19
  fprintf(stdout,"%d bytes: ", bytes);
#endif
  if(type == 2)
    fprintf(stdout,"%lx", word);
  else if(type == 8)
    fprintf(stdout,"%lo", word);
  else if(type == 10)
    fprintf(stdout,"%lu", word);
  else if(type == 16)
    fprintf(stdout,"%lx", word);
  else
    fprintf(stdout,"%lu", word);
  fprintf(stdout,"/");
  fflush(stdout);
#endif

  return(word);
}

#include <stdarg.h>

#define aDEBUG31

static int stat_line_chars = 0;
static int stat_line_edchars = 0;

unsigned char *procname = NULL;

static void stat_line_begin()
{
#ifdef DEBUG31
  fprintf(stderr, "\n");
#else
  fprintf(stderr, "\r");
#endif  
  stat_line_edchars = stat_line_chars;
  stat_line_chars = 0;
}

static void stat_line_printf(const unsigned char *format, ...)
{
  int count;
  va_list args;
  static unsigned char *stat_line_buf = NULL;
  static size_t stat_line_buf_length = 0;

  va_start(args, format);
  count = vsnprintf(stat_line_buf, stat_line_buf_length, format, args) + 1;
  va_end(args);
  if(stat_line_buf_length < count) {
    stat_line_buf_length = count;
    stat_line_buf = realloc(stat_line_buf, stat_line_buf_length);
    va_start(args, format);
    count = vsnprintf(stat_line_buf, stat_line_buf_length, format, args) + 1;
    va_end(args);
  }
  fprintf(stderr,"%s", stat_line_buf);
  stat_line_chars += strlen(stat_line_buf);
}

#define READABLE_NUMBER_BIN 2

#ifdef READABLE_NUMBER_BIN
#define READABLE_NUMBER_HIGH 1023
#define READABLE_NUMBER_DIVIDER 1024
#else
#define READABLE_NUMBER_HIGH 999
#define READABLE_NUMBER_DIVIDER 1000
#endif

#define READABLE_NUMBER_WIDTH 32

static void stat_line_get_readable(unsigned char buf10[10], unsigned long length)
{
  int c, low;
  double length2;
  // B = byte
  // K = kilo   10^3   2^10
  // M = mega   10^6   2^20
  // G = giga   10^9   2^30
  // T = tera   10^12  2^40
  // P = peta   10^15  2^50
  // E = exa    10^18  2^60
  // Z = zetta  10^21  2^70
  // Y = yotta  10^24  2^80
  char units[] = "BKMGTPEZY";

  strcpy(buf10, "***");
  low = 0;

  for(c = 0; length >= low &&
      c < sizeof(units) - 1; c++) {
    if(length >= low &&
       length <= READABLE_NUMBER_HIGH) {
      if(units[c] == 'B')
        sprintf(buf10, "%ld", length);
      else if(units[c] == 'K')
	sprintf(buf10, "%ld%cB", length, units[c]);
      else if(length == length2)
	sprintf(buf10, "%ld%cB", length, units[c]);
      else
	sprintf(buf10, "%.1f%cB", length2, units[c]);
	
      break;
    }
    length2 = (double)length / READABLE_NUMBER_DIVIDER;
    length /= READABLE_NUMBER_DIVIDER;
    low = 1;
  }
}

static void stat_line_readable(unsigned long length)
{
  unsigned char buf10[10];
  stat_line_get_readable(buf10, length);
  stat_line_printf("%s", buf10);
}

void stat_line_end()
{
  int c, d;

  // previous line longer than this one,
  // overwrite with spaces and backspace to
  // end
  
  if(stat_line_edchars > stat_line_chars) {
    d = stat_line_edchars - stat_line_chars + 1; // cursor too
    for(c = 0; c < d; c++) {
#ifdef DEBUG31
      fprintf(stderr, "*");
#else
      fprintf(stderr, " ");
#endif
    }
    for(c = 0; c < d; c++)
      fprintf(stderr, "\b");
  }
  fflush(stderr);
}

unsigned char cursor[4] = { '|', '/', '-', '\\' };
long int prev_secs = -1;
int crs = 0;

void stat_line_cursor_start()
{
  fprintf(stderr, " ");
  prev_secs = -1;
}

void stat_line_cursor()
{
  long int secs;
  
  secs = time(NULL) % sizeof(cursor);
  if(prev_secs != secs) {
    if(prev_secs != -1) 
      crs = (crs + 1) % sizeof(cursor);
    fprintf(stderr,"\b%c", cursor[crs]);
    fflush(stderr);
    prev_secs = secs;
  }
}

void stat_line_cursor_remove()
{
  fprintf(stderr, "\b \b");
  prev_secs = -1;
}
int help = 0;

static int utf8characters(unsigned char *buf)
{
  int characters;
  unsigned char *p;
  
  p = buf;
  characters = 0;
  while(*p != '\0') {
    if(*p < 0x80 || // ascii char
       *p > 0xbf) // first utf8 byte
      characters++;
    p++;
  }
  
  return(characters);
}

#define aDEBUG29 2

int utf8lengths(unsigned char *buf)
{
  int lengths, len, first = 1;
  unsigned char *p;

#ifdef DEBUG29 
  fprintf(stdout,"digits    %s", buf);
  fprintf(stdout,"\nlengths   ");
#endif
  
  p = buf;
  len = 0;
  lengths = -1;
  first = 1;

  while(*p != '\0') {
    if(!first &&
       (*p < 0x80 || // ascii char
	*p > 0xbf)) { // first utf8 byte
#ifdef DEBUG29
      fprintf(stdout,"%d", len);
#endif
      if(lengths == -1)
	lengths = len;
      else if(lengths != len)
	lengths = 0;
      len = 0;
    }
    p++;
    len++;
    first = 0;
  }

#ifdef DEBUG29
  fprintf(stdout,"%d", len);
#endif
  if(lengths == -1)
    lengths = len;
  else if(lengths != len)
    lengths = 0;

#ifdef DEBUG29
  fprintf(stdout,"\nlengths   %d\n\n", lengths);
#endif
  return(lengths);
}

#define aDEBUG42 2

static void codetoutf8(unsigned char *buf2, unsigned long code)
{
  unsigned char *buf;

  buf = buf2;
  if(code <= 0x7f) {
    *buf++ = code;
  } else if(code <= 0x7ff) {            // 110xxxxx 10xxxxxx
    *buf++ = (0xc0 | ((code >> 6) & 0x1f));
    *buf++ = (0x80 | (code & 0x3f));
  } else if(code <= 0xffff) {           // 1110xxxx 10xxxxxx 10xxxxxx
    *buf++ = (0xe0 | ((code >> 12) & 0x0f));
    *buf++ = (0x80 | ((code >> 6) & 0x3f));
    *buf++ = (0x80 | (code & 0x3f));
  } else if(code <= 0x10ffff) {         // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
    *buf++ = (0xf0 | ((code >> 18) & 0x07));
    *buf++ = (0x80 | ((code >> 12) & 0x3f));      
    *buf++ = (0x80 | ((code >> 6) & 0x3f));      
    *buf++ = (0x80 | ((code & 0x3f)));      
  } else if(code <= 0x3ffffff) {        // 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx // these not yet needed
    *buf++ = (0xf8 | ((code >> 24) & 0x03));
    *buf++ = (0x80 | ((code >> 18) & 0x3f));      
    *buf++ = (0x80 | ((code >> 12) & 0x3f));      
    *buf++ = (0x80 | ((code >> 6) & 0x3f));      
    *buf++ = (0x80 | ((code & 0x3f)));      
  } else if(code <= 0x7fffffff) {       // 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx // these not yet needed
    *buf++ = (0xfc | ((code >> 30) & 0x01));
    *buf++ = (0x80 | ((code >> 24) & 0x3f));      
    *buf++ = (0x80 | ((code >> 18) & 0x3f));      
    *buf++ = (0x80 | ((code >> 12) & 0x3f));      
    *buf++ = (0x80 | ((code >> 6) & 0x3f));      
    *buf++ = (0x80 | ((code & 0x3f)));      
  } else if(code <= 0xfffffffff) {      // 11111110 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx // these not yet needed
    *buf++ = (0xfe);
    *buf++ = (0x80 | ((code >> 30) & 0x3f));      
    *buf++ = (0x80 | ((code >> 24) & 0x3f));      
    *buf++ = (0x80 | ((code >> 18) & 0x3f));      
    *buf++ = (0x80 | ((code >> 12) & 0x3f));      
    *buf++ = (0x80 | ((code >> 6) & 0x3f));      
    *buf++ = (0x80 | ((code & 0x3f)));      
  } else if(code <= 0x3ffffffffff) {    // 11111111 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx // these not yet needed
    *buf++ = (0xff);
    *buf++ = (0x80 | ((code >> 36) & 0x3f));      
    *buf++ = (0x80 | ((code >> 30) & 0x3f));      
    *buf++ = (0x80 | ((code >> 24) & 0x3f));      
    *buf++ = (0x80 | ((code >> 18) & 0x3f));      
    *buf++ = (0x80 | ((code >> 12) & 0x3f));      
    *buf++ = (0x80 | ((code >> 6) & 0x3f));      
    *buf++ = (0x80 | ((code & 0x3f)));      
  }
  *buf = '\0';

#ifdef DEBUG42
  int c, print;

  fprintf(stdout,"codepoint: %lx", code);

  fprintf(stdout,", bin:");

  print = 0;
  for(c = (sizeof(code) * 8) - 1; c >= 0; c--) {
    int bit = (code >> c) & 1;
    if(bit != 0)
      print = 1;
    if(print) {
      fprintf(stdout,"%d", bit);
      if(c > 0 && c % 8 == 0)
	fprintf(stdout,"|");
    }
  }

  fprintf(stdout,", oct:");
  print = 0;
  fprintf(stdout,"%lo", code);

  fprintf(stdout,", utf8: ");
  for(c = 0; c < 8; c++) {
    if(buf2[c] == '\0')
      break;
    fprintf(stdout,"%02x", buf2[c]);
  }

  fprintf(stdout,", bin: ");
  for(c = 0; c < 8; c++) {
    if(buf2[c] == '\0')
      break;
    if(c > 0)
      fprintf(stdout,"|");
    for(int d = 7; d >= 0; d--) {
      fprintf(stdout,"%d", buf2[c] >> d & 1);
    }
  }

  fprintf(stdout,", oct:");
  for(c = 0; c < 8; c++) {
    if(buf2[c] == '\0')
      break;
    fprintf(stdout," %3o", buf2[c]);
  }
  
  fprintf(stdout,", character: %s", buf2);
  fprintf(stdout,"\n");
#endif
}

#ifdef NOTUSED

#define aDEBUG43 2

static void utf8tocode(unsigned long *code, unsigned char *buf2)
{
  unsigned char *buf;
  
  *code = 0;
  buf = buf2;
  
  if(*buf <= 0x7f) {                  // 0xxxxxxx
    *code = *buf;
  } else if(*buf < 0xc0) {            // 110xxxxx 10xxxxxx
    *code = 0;
  } else if(*buf < 0xe0) {            // 110xxxxx 10xxxxxx
    *code = *buf++ & 0x1f;
    *code = (*code << 6) + (*buf++ & 0x3f);
  } else if(*buf < 0xf0) {            // 1110xxxx 10xxxxxx 10xxxxxx
    *code = *buf++ & 0x0f;
    *code = (*code << 6) + (*buf++ & 0x3f);
    *code = (*code << 6) + (*buf++ & 0x3f);
  } else if(*buf<0xf8) {            // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
    *code = *buf++ & 0x07;
    *code = (*code << 6) + (*buf++ & 0x3f);
    *code = (*code << 6) + (*buf++ & 0x3f);
    *code = (*code << 6) + (*buf++ & 0x3f);
  } else if(*buf < 0xfc) {            // 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
    *code = *buf++ & 0x03;
    *code = (*code << 6) + (*buf++ & 0x3f);
    *code = (*code << 6) + (*buf++ & 0x3f);
    *code = (*code << 6) + (*buf++ & 0x3f);
    *code = (*code << 6) + (*buf++ & 0x3f);
  } else if(*buf < 0xfe) {            // 1111110x 10xxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
    *code = *buf++ & 0x01;
    *code = (*code << 6) + (*buf++ & 0x3f);
    *code = (*code << 6) + (*buf++ & 0x3f);
    *code = (*code << 6) + (*buf++ & 0x3f);
    *code = (*code << 6) + (*buf++ & 0x3f);
    *code = (*code << 6) + (*buf++ & 0x3f);
  } else if(*buf < 0xff) {            // 11111110 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
    buf++;
    *code = *buf++ & 0x3f;
    *code = (*code << 6) + (*buf++ & 0x3f);
    *code = (*code << 6) + (*buf++ & 0x3f);
    *code = (*code << 6) + (*buf++ & 0x3f);
    *code = (*code << 6) + (*buf++ & 0x3f);
    *code = (*code << 6) + (*buf++ & 0x3f);
  } else if(*buf == 0xff) {            // 11111111 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
    buf++;
    *code = *buf++ & 0x3f;
    *code = (*code << 6) + (*buf++ & 0x3f);
    *code = (*code << 6) + (*buf++ & 0x3f);
    *code = (*code << 6) + (*buf++ & 0x3f);
    *code = (*code << 6) + (*buf++ & 0x3f);
    *code = (*code << 6) + (*buf++ & 0x3f);
    *code = (*code << 6) + (*buf++ & 0x3f);
  }
#ifdef DEBUG43
  int c;

  fprintf(stdout, "character: %s", buf2);

  fprintf(stdout, ", utf8: ");
  for(c = 0; c < 8; c++) {
    if(buf2[c] == '\0')
      break;
    fprintf(stdout,"%02x", buf2[c]);
  }

  fprintf(stdout, ", bin: ");
  for(c = 0; c < 8; c++) {
    if(buf2[c] == '\0')
      break;
    if(c > 0)
      fprintf(stdout, "|");
    for(int d = 7; d >= 0; d--)
      fprintf(stdout, "%d", buf2[c] >> d & 1);
  }

  fprintf(stdout, ", oct:");
  for(c = 0; c < 8; c++) {
    if(buf2[c] == '\0')
      break;
    fprintf(stdout, " %3o", buf2[c]);
  }
  
  fprintf(stdout, ", codepoint: %lx", *code);
  fprintf(stdout, ", bin:");
  int print = 0;
  for(c = (sizeof(code) * 8) - 1; c >= 0; c--) {
    int bit = (*code >> c) & 1;
    if(bit != 0)
      print = 1;
    if(print) {
      fprintf(stdout, "%d", bit);
      if(c > 0 && c % 8 == 0)
	fprintf(stdout, "|");
    }
  }

  fprintf(stdout, ", oct:");
  fprintf(stdout, "%lo", *code);
  
  fprintf(stdout, "\n");
#endif
}

#endif 

int characterlengths = 0; // 0 multiple lengths, x lengths are same

#define aDEBUG30 2

static void utf8getcharacter(int size, unsigned char *buf, int n, unsigned char *string)
{
  int d;
  unsigned char *p, *q;
  
  d = 0;
  p = string;
  q = buf;

  // find first byte of character

  if(characterlengths != 0)
    p += characterlengths * n;
  else {
    while(*p != '\0') {
      if(*p < 0x80 || // ascii char
	 *p > 0xbf) { // first utf8 char
	if(d == n)
	  break;
	d++;
      }
      p++;
    }
  }

  // copy first byte and rest
  // of character
  
  if(*p != '\0') {
    *q++ = *p; // copy first byte
    if(*p > 0xbf) { // if first is utf8 char
      p++;
      for(;;) {
	if(*p > 0xbf || // first utf8 char
	   *p < 0x80 || // ascii char
	   *p == '\0')  // end of file
	  break;
	*q++ = *p++; // copy rest of the bytes
      }
    }
  }
  *q = '\0';

#ifdef DEBUG30
  fprintf(stdout,"%s: utf8getcharacter:", procname);
  fprintf(stdout," string: %s", string);
  fprintf(stdout,", n: %d", n);
  fprintf(stdout,", character: %s", buf);
  fprintf(stdout,", characterlengths: %d", characterlengths);
  fprintf(stdout,"\n");
#endif
}

static void utf8getcharacter1(int size, unsigned char *buf, int n, unsigned char *string)
{
  unsigned char *p, *q;
  
  p = string;
  q = buf;

  // find first byte of character

  p += n;

  // copy first byte and rest
  // of character
  
  if(*p != '\0') {
    *q++ = *p; // copy first byte
    if(*p > 0xbf) { // if first is utf8 char
      p++;
      for(;;) {
	if(*p > 0xbf || // first utf8 char
	   *p < 0x80 || // ascii char
	   *p == '\0')  // end of file
	  break;
	*q++ = *p++; // copy rest of the bytes
      }
    }
  }
  *q = '\0';

#ifdef DEBUG30
  fprintf(stdout,"%s: utf8getcharacter:", procname);
  fprintf(stdout," string: %s", string);
  fprintf(stdout,", n: %d", n);
  fprintf(stdout,", character: %s", buf);
  fprintf(stdout,", characterlengths: %d", characterlengths);
  fprintf(stdout,"\n");
#endif
}

#define aDEBUG45 2

int digitscount = 10;

// display word in "digits" base (random data)

static void out_word(int size, unsigned char *buf, unsigned char *digits, unsigned long word2) // 8.5.2021 JariK
{
  int c, d, len;
  unsigned long word;
  unsigned char string[132], character[32];

  //digitscount = utf8characters(digits);
  word = word2;
  len = 0;
  string[0] = '\0';

  if(word == 0 || digitscount < 2) {
    // zero
    utf8getcharacter(sizeof(character), character, 0, digits);
    if(len + strlen(character) < sizeof(string)) {
      strcat(string, character);
      len += strlen(character);
    }
  } else {
    // non zero
    while(word > 0) {
      utf8getcharacter(sizeof(character), character, word%digitscount, digits);
      if(len + strlen(character) < sizeof(string)) {
	strcat(string, character);
	len += strlen(character);
      }
      word /= digitscount;
    }
  }

  // reverse string
  
  *buf = '\0';
  len = 0;
  d = utf8characters(string);
  for(c = d - 1; c >= 0; c--) {
    utf8getcharacter(sizeof(character), character, c, string);
    if(len + strlen(character) < size) {
      strcat(buf, character);
      len += strlen(character);
    }
  }

#ifdef DEBUG45
  fprintf(stdout,"]\n%s: out_word: ", procname);
  fprintf(stdout," reverse string: %s", string);
  fprintf(stdout,", digits: %s", digits);
  fprintf(stdout,", int: %lu", word2);
  fprintf(stdout,"\n[");
#endif
}

// display word in decimal (10-base) (line number)

static void out_word_10(int size, unsigned char *buf, unsigned long word2) // 8.5.2021 JariK
{
  int c, d, len;
  unsigned long word;
  unsigned char string[132], character[32];
  unsigned char *digits = "0123456789";
  int digitscount = 10;
  
  //digitscount = utf8characters(digits);
  word = word2;
  len = 0;
  string[0] = '\0';

  if(word == 0 || digitscount < 2) {
    // zero
    utf8getcharacter1(sizeof(character), character, 0, digits);
    if(len+strlen(character) < sizeof(string)) {
      strcat(string, character);
      len += strlen(character);
    }
  } else {
    // non zero
    while(word > 0) {
      utf8getcharacter1(sizeof(character), character, word % digitscount, digits);
      if(len + strlen(character) < sizeof(string)) {
	strcat(string, character);
	len += strlen(character);
      }
      word /= digitscount;
    }
  }

  // reverse string
  
  *buf = '\0';
  len = 0;
  d = utf8characters(string);
  for(c = d - 1;c >= 0; c--) {
    utf8getcharacter1(sizeof(character), character, c, string);
    if(len + strlen(character) < size) {
      strcat(buf, character);
      len += strlen(character);
    }
  }

#ifdef DEBUG45
  fprintf(stdout,"]\n%s: out_word: ", procname);
  fprintf(stdout," reverse string: %s", string);
  fprintf(stdout,", digits: %s", digits);
  fprintf(stdout,", int: %lu", word2);
  fprintf(stdout,"\n[");
#endif
}

#define aDEBUG48 2

static void in_word(unsigned long *word, unsigned char *digits, unsigned char *buf)
{
  int c, d, e, f, ok;
  unsigned char character[32], character2[32];

  *word = 0;
  d = utf8characters(buf);
  f = utf8characters(digits);

  for(c = 0; c < d; c++) {
    utf8getcharacter(sizeof(character2), character2, c, buf);
    ok = 0;
    for(e = 0; e < f; e++) {
      utf8getcharacter(sizeof(character), character, e, digits);
      if(!strcmp(character, character2)) {
	ok = 1;
	break;
      }
    }
    if(ok) {
      *word = ((*word) * f) + e;
    } else {
      fprintf(stdout,"%s: in_word:", procname);
      fprintf(stdout," illegal digit '%s'\n", character2);
      help = 1;
    }
  }

#ifdef DEBUG48
  fprintf(stdout,"%s: in_word:", procname);
  fprintf(stdout," word: %lu", *word);
  fprintf(stdout,", digits: %s", digits);
  fprintf(stdout,", string: %s", buf);
  fprintf(stdout,"\n");
#endif
}

static void line_clear(int *len, unsigned char **buf)
{
  if(*len < 1) {
    *len = 129;
    *buf = realloc(*buf, *len);
  }
  **buf = '\0';
}

static int line_add_string_sort(int *len, unsigned char **buf, unsigned char *string)
{
  int cmp, count, add;
  unsigned char *p;

  p = *buf;
  add = 1;
  while(*p != '\0') {
    cmp = strncmp(string, p, strlen(string));
    if(cmp > 0) {
      while(*p != ' ' && *p != '\0') // find next space
	p++;
      while(*p == ' ' && *p != '\0') // find next non space
	p++;
    } else if(cmp == 0) {
      add = 0;
      break;
    } else {
      break;
    }
  }

  if(add) {
    count = strlen(*buf) + strlen(string) +1 +1;
    if(*buf == NULL || count > *len) {
      int diff = p - *buf;
      *len = *len + 128;
      *buf = realloc(*buf, *len);
      p = *buf + diff;
    }
    memmove(p + strlen(string) + 1, p, strlen(p) + 1);
    memmove(p, string, strlen(string));
    p += strlen(string);
    *p = ' ';
  }

  return(add);
}

static void line_get_string(int len, unsigned char *buf, int n, unsigned char *string)
{
  int e, ok, count;
  unsigned char *p, *q;

  ok = 0;
  p = string;
  e = 0;

  while(*p != '\0') {
    if(e == n) {
      ok = 1;
      break;
    }
    while(*p!=' ' && *p!='\0') // find next space
      p++;
    while(*p==' ' && *p!='\0') // find next non space
      p++;
    e++;
  }
  if(ok) {
    q = buf;
    count = 0;
    while(*q!=' ' && *q!='\0') {
      if(++count < len)
	*q++ = *p;
      p++;
    }
    *q = '\0';
  } else {
    buf[0] = '\0';
  }
}

static int zero = 1, sspace = 0, snewline = 0,
  scrlf = 1, chars = 72, pchars = 0, charwidth = 1,
  charspaces = 0, words = 0, pwords = 0,
  plinesdigits = 5, slineno = 1, screen = 0,
  quiet = 0, sort = 0, unique = 0, sample = 0;

#ifdef DEBUG51
static int limitsize = 0,
#endif

static unsigned long long lines = 10, plines = 0, ptotallines = 0;
static unsigned long limit, word;
static unsigned char *digits = "0123456789", character[32];
static unsigned char digitstemp[256], *digitsext = NULL;
static unsigned char linenobuf[1024];
static int print_statline = 0;
  

#define aDEBUG51 2

static void readword(unsigned char *buf)
{
  int d,e;
  unsigned char temp1024[1024];
  
  if(limit != 0) {
    word = 0;
    
    if(zero) {
      word = newressu_gen_limit(limit); // include zeroes
    } else if(limit >= 1) {
      while((word = newressu_gen_limit(limit)) == 0); // skip zeroes
    }
#ifdef DEBUG51
    if(type == 2)
      fprintf(stdout, "(%0*lx)", limitsize, word);
    else if(type == 8)
      fprintf(stdout, "(%0*lo)", limitsize, word);
    else if(type == 10)
      fprintf(stdout, "(%0*lu)", limitsize, word);
    else if(type == 16)
      fprintf(stdout, "(%0*lx)", limitsize, word);
    else {
      fprintf(stdout, "(%02lu)", word);
    }
#endif
	  
    out_word(sizeof(temp1024), temp1024, digits, word);
	  
    // fill leading zeroes
    
    buf[0] = '\0';
    utf8getcharacter(sizeof(character), character, 0, digits);
    for(d = size - utf8characters(temp1024); d > 0; d--) {
      strcat(buf, character);
    }
	  
    // rest of the number
	  
    strcat(buf, temp1024);
	  
  } else if(digits != NULL) {
	  
    buf[0] = '\0';
	  
#ifdef DEBUG51
    fprintf(stdout, "(");
#endif
    // fill whole word digit by digit
    for(d = 0; d < size; d++) {
      if(digits[0] == '0' && !zero)
	e = newressu_gen_limit(digitscount - 1) + 1;
      else
	e = newressu_gen_limit(digitscount);
      utf8getcharacter(sizeof(temp1024), temp1024, e, digits);
#ifdef DEBUG51
      fprintf(stdout,"%s", temp1024);
      if(charspaces == 1)
	fputc(' ', stdout);
#endif
      strcat(buf, temp1024);
    }
#ifdef DEBUG51
    fprintf(stdout, ")");
#endif
  }
}

static int line_number_length()
{
  int c;
  out_word_10(sizeof(linenobuf), linenobuf, lines-1);
  c = strlen(linenobuf);
  if(c < 5)
    c = 5;
  return(c);
}

static void print_line_number()
{
  unsigned char linenobuf[32];
  
  if(pwords == 0 && slineno) {
    if(newressu_output) {
      fprintf(stdout,"\n");
      newressu_output = 0;
    }
    sprintf(linenobuf,"%0*llu", plinesdigits, plines);
    fprintf(stdout,"%s", linenobuf);
    fprintf(stdout," ");
  }
}

static int calc_spaces()
{
  int spaces;

  spaces = 0;
  
  if(sspace >= 2 &&
     // space between word groups
     ( (pwords > 0 && pwords % sspace == 0) ||
       // space after linenumber
       // are not printed if
       // not printing linenumber
       (slineno && pwords == 0) ) )
    spaces++;
    
  if(sspace &&
     // space between words
     ( (pwords > 0) ||
       // space after linenumber
       (slineno && pwords == 0) ) ) 
    spaces++;

  return(spaces);
}

static void print_spaces()
{
  int sp;

  for(sp = calc_spaces(); sp > 0; sp--)
    fprintf(stdout, " ");
}

static void print_word(unsigned char *buf)
{
  int e;
  
  if(size != 0) {
    if(charspaces == 0) {
      fprintf(stdout, "%s", buf);
    } else {
      for(e = 0; e < size; e++) {
	unsigned char temp1024[1024];
	utf8getcharacter(sizeof(temp1024), temp1024, e, buf);
	fprintf(stdout, "%s", temp1024);
	fputc(' ', stdout);
      }
    }
  } else {
    fprintf(stdout, "%s", buf);
    if(charspaces == 1) {
      fputc(' ', stdout);
    }
  }
}

#ifdef SHA256

static void newressu_file_digest(char *filename,unsigned char *hash)
{
  int c;
  unsigned char buffer[1024];
  FILE *fp1;
  HashCtx ctx;
  
  HashInit(&ctx);
  if((fp1 = fopen(filename, "rb")) != NULL) {
    while((c = fread(buffer, 1, sizeof(buffer), fp1)) > 0)
      HashUpdate(&ctx, buffer, c);
    fclose(fp1);
  }
  HashFinal(hash, &ctx);  
}

#endif

#define aDEBUG55 2

void digits_add_string(unsigned char **digits, char *str)
{
  int digitssize;

#ifdef DEBUG55
  fprintf(stdout,"\nolddigits %s", *digits);
  fprintf(stdout,"\nadddigits %s", str);
#endif
  digitssize = 0;
  if(*digits != NULL)
    digitssize += strlen(*digits);
  digitssize += strlen(str);
  *digits = realloc(*digits, digitssize + 1);
  strcat(*digits, str);
#ifdef DEBUG55
  fprintf(stdout,"\nnewdigits %s", *digits);
  fprintf(stdout,"\n");
#endif
}

void digits_add_limits(unsigned char **digits, int start, int end)
{
  int c, digitssize;
  unsigned char buffer[10];

#ifdef DEBUG55
  fprintf(stdout,"\nolddigits %s", *digits);
  fprintf(stdout,"\nadddigits start:%d, end:%d", start, end);
#endif
  digitssize = 0;
  if(*digits != NULL)
    digitssize += strlen(*digits);
  
  for(c = start; c <= end; c++) {
    codetoutf8(buffer, c);
    digitssize += strlen(buffer);
  }
  *digits = realloc(*digits, digitssize + 1);
  for(c = start; c <= end; c++) {
    codetoutf8(buffer, c);
    strcat(*digits, buffer);
  }
#ifdef DEBUG55
  fprintf(stdout,"\nnewdigits %s", *digits);
  fprintf(stdout,"\n");
#endif
}

double getseconds()
{
  struct timeval tv;
  gettimeofday(&tv, NULL);
  return((double)tv.tv_sec + (double)tv.tv_usec / 1000000);
}

void dump_sample()
{
//sample file size = CLIM*DLIM*BLKSIZE
#define CLIM 8192 // outer loop
#define DLIM 256 // inner loop
#define BLKSIZE 4096 // block size

#define STAT_LINE_READY 2
#define STAT_LINE_NOW 2
#define STAT_LINE_ANIM 2
    
#define TIMEFORMAT "%H:%M %Z"
#define TIMEFORMAT2 "%a %H:%M %Z"
#define DATEFORMAT "%a %d %b %Y"
    
    unsigned int c, d;
    unsigned char buffer[BLKSIZE];
    FILE *fp1;
    time_t secondsstart, secondsnow;

    if((fp1 = fopen(samplefilename, "a")) != NULL) {

      secondsstart = time(NULL);

      for(c = 0; c < CLIM; c++) {
	secondsnow = time(NULL);
	
	// print status line:
	// blk 10, written 10MB, 269KB/sec, left 8 h 38 m, ready at Sat 17 Jul 2021 01:57:43 EEST, now Fri 17:19:32 EEST
	
	stat_line_begin();
	stat_line_printf("\r%s blk %d, written ", randomgen[input]);
	stat_line_readable((unsigned long)c * DLIM * BLKSIZE);
	
	if(c > 0) {
	  stat_line_printf(", ");
	  if(secondsnow - secondsstart > 1.0)
	    stat_line_readable((unsigned long)((double)c * DLIM * BLKSIZE / (secondsnow - secondsstart)) );
	  else
	    stat_line_readable((unsigned long)((double)c * DLIM * BLKSIZE));
	  stat_line_printf("/sec");

	  char timebuf[128], timebuf2[128];
	  
#ifdef STAT_LINE_NOW

	  // print now
	  
	  stat_line_printf(", now");
	  strftime(timebuf, sizeof(timebuf), TIMEFORMAT2,
		   localtime((time_t *)&secondsnow));
	  stat_line_printf(" %s", timebuf);
#endif

	  // print left
	  
	  long int secondsleft = (int)((((double)secondsnow - secondsstart) / c) * (CLIM - c) );
	  long int secondsleft2;
	  long int temp,timeprinted = 0;

	  secondsleft2 = secondsleft;
	  stat_line_printf(", left");
	  
	  temp = secondsleft2 / (24 * 3600);
	  if(temp > 0) {
	    timeprinted = 1;
	    stat_line_printf(" %dd", temp);
	    secondsleft2 -= temp * (24 * 3600);
	  }
	  temp = secondsleft2 / 3600;
	  if(temp > 0) {
	    timeprinted = 1;
	    stat_line_printf(" %dh", temp);
	    secondsleft2 -= temp * 3600;
	  }
	  temp = secondsleft2 / 60;
	  if(temp > 0) {
	    timeprinted = 1;
	    stat_line_printf(" %dm", temp);
	    secondsleft2 -= temp * 60;
	  }
	  temp = secondsleft2;
	  if(!timeprinted) {
	    stat_line_printf(" %d seconds", temp);
	  }

#ifdef STAT_LINE_READY

	  long int secondsend = (int)secondsstart + ((((double)secondsnow - secondsstart) / c) * CLIM);
	  
	  // print end date if different7
	  
	  stat_line_printf(", ready at");
	  strftime(timebuf, sizeof(timebuf), DATEFORMAT,
		   localtime((time_t *) & secondsend));
	  strftime(timebuf2, sizeof(timebuf2), DATEFORMAT,
		   localtime((time_t *) & secondsnow));
	  if(strcmp(timebuf, timebuf2)) {
	    stat_line_printf(" %s", timebuf);
	  }

	  // print end time
	  
	  strftime(timebuf, sizeof(timebuf), TIMEFORMAT,
		   localtime((time_t *) & secondsend));
	  stat_line_printf(" %s", timebuf);
#endif

	}

	stat_line_end();
	
	// do the work
	
	stat_line_cursor_start();

	for(d = 0; d < DLIM; d++) {
	  stat_line_cursor();
	  if(input == INPUT_RESSU) // ressu prod
	    ressu_genbytes(sizeof(buffer), buffer);
	  else if(input == INPUT_FAST) // ressu fast
	    ressu_genbytes_fast(sizeof(buffer), buffer);
	  else if(input == INPUT_SINGLE) // ressu single
	    ressu_genbytes_single(sizeof(buffer), buffer);
#ifdef FORT
	  else if(input == INPUT_FORT) // ressu fort
	    fort_random_data(sizeof(buffer), buffer);
	  else if(input == INPUT_FORTXOR) // ressu fort
	    fort_random_data_xor(sizeof(buffer), buffer);
#endif
#ifdef USE_RDRAND
	  else if(input == INPUT_RDRAND) // intel rdrand
	    rdrand_bytes(sizeof(buffer), buffer);
#endif
#ifdef USE_RDSEED
	  else if(input == INPUT_RDSEED) // intel rdseed
	    rdseed_bytes(sizeof(buffer), buffer);
#endif
	  else if(input == INPUT_URANDOM) // urandom
	    readfile_xor(sizeof(buffer), buffer, urandomfilename);
#ifdef USE_RANDOM
	  else if(input == INPUT_RANDOM) // random
	    readfile_xor(sizeof(buffer), buffer, randomfilename);
#endif
	  
#ifdef WRITE_SAMPLE
	  fwrite(buffer, 1, sizeof(buffer), fp1);
#endif
	} // for(d=0;
	stat_line_cursor_remove();
      }
      fclose(fp1);
    } // if((fp1=fopen

    // remove last status line
    
    stat_line_begin();

    stat_line_printf("\rwrote ");
    stat_line_readable((unsigned long)c * DLIM * BLKSIZE);

    stat_line_printf(", ");
    if(secondsnow - secondsstart >= 1.0)
      stat_line_readable((unsigned long)((double)c * DLIM * BLKSIZE / (secondsnow - secondsstart)) );
    else
      stat_line_readable((unsigned long)c * DLIM * BLKSIZE);
    stat_line_printf("/sec");
    
    stat_line_printf(", done!");
    stat_line_end();
    fprintf(stdout,"\n");
    fflush(stdout);
}

#include <sys/ioctl.h> // for TIOCGWINSZ

#define aDEBUG50 2

#define aDEBUG71 2

void call_main(char *);

#define aCALL_MAIN 2

#ifdef CALL_MAIN

#define DEBUG99 2

int main2(int argc, char *argv[]);

void call_main(char *cmd)
{
  int size, round, count, chars;
  unsigned char *p, *q, *argmem,*params;
  char **argv;

  size = 0;

  for(round = 0; round < 2; round++) {
    count = 0;
    p = cmd;
    while(*p != '\0') {
      while(*p == ' ' || *p == '\t')
	p++;
      q = p;
      chars = 0;
      while(*p != ' ' &&  *p != '\t' && *p != '\0') {
	p++;
	chars++;
      }
      if(round == 0) // calculate size & alloc
	size += chars + 1 + sizeof(char *);
      else { // move parameters to arg area
	strncpy(params, q, chars);
	*(argv+count) = params;
	params += (chars+1);
      }
      count++;
    }
    if(round == 0) { // allocate
      size += sizeof(char *); // ending NULL
      argmem = malloc(size);
      argv = (char **)argmem;
      params = argmem + sizeof(char *) * (count+1);
    }
  }

  *(argv+count) = NULL; // ending NULL

#ifdef DEBUG99
  
  fprintf(stdout, "size:%d,", size);
  fprintf(stdout, " argmem:");
  for(int c = 0; c < size; c++) {
    if(c > 0 && c % 8 == 0)
      fprintf(stdout, "|");
    if(*(argmem+c) == '\\')
      fprintf(stdout, "\\\\");
    if(isprint(*(argmem + c)))
      fprintf(stdout, "%c", *(argmem + c));
    else
      fprintf(stdout, "\\%02x", *(argmem + c));
  }
  fprintf(stdout, "\n");

#endif // #ifdef DEBUG99

  main2(count, argv);
}

int main(int argc, char *argv[])
{
  int first;
  char buffer[1024];

  first = 1;
  buffer[0]='\0';
  for(int c = 0; c < argc; c++) {
    if(!first)
      strcat(buffer, " ");
    strcat(buffer, argv[c]);
    first = 0;
  }
#ifdef DEBUG99
  fprintf(stdout,"main: cmd \"%s\"\n",buffer);
#endif
  
  call_main(buffer);
}

#endif // #ifdef CALL_MAIN

#ifdef CALL_MAIN

int main2(int argc, char *argv[])
{
#ifdef DEBUG99
  fprintf(stdout, "main2args:");  
  for(int c = 0; c < argc; c++) {
    fprintf(stdout, " %d:\"%s\"", c, argv[c]);
  }
  fprintf(stdout, "\n");
#endif // #ifdef DEBUG99

#else // #ifdef CALL_MAIN

static unsigned char programfiledigest[HashLen];
  
int main(int argc, char *argv[])
{
#endif // #ifdef CALL_MAIN
  
  int c, d, status = 0;

  procname = argv[0];
  limit = 0;
  newressu_file_digest("/proc/self/exe", programfiledigest);

  gent_clear();

  clockbytes = 0;
  for(d = 0; d < 1024; d++)
    periods[d] = 0;

#ifdef USE_TIMER
  int timer = 0;
  double timerstart;
#endif  

  // look thru command line parameters
  
  for(c = 1; c < argc; c++) {
    if(!strncmp("-", argv[c], 1)) {
      if(!strcmp("--lineno", argv[c])) {
	slineno = !slineno; // print line number
	
      } else if(!strcmp("--crlf", argv[c])) {
	scrlf = !scrlf;
	slineno = 0;

      } else if(!strcmp("--quiet", argv[c])) {
	quiet = !quiet;

      } else if(!strcmp("--stats", argv[c]) ||
	!strcmp("--stat", argv[c])) {
	stats = !stats;

      } else if(!strcmp("--rand", argv[c])) {
	digits = "0123456789";
	sspace = 2;
	snewline = 5;
	slineno = 1; // print line number
	words = 10;
	chars = 0;
	limit = 100000;
	//size = 5;
	//lines = 20000;
	
      } else if(!strncmp("--space", argv[c], 7)) {
	if(*(argv[c] + 7)!='\0' && atoi(argv[c] + 7) > 1) {
	  sspace = atoi(argv[c] + 7);
	} else if(c + 1 < argc && atoi(argv[c + 1]) > 1) {
	  sspace = atoi(argv[c + 1]);
	  c++;
	} else {
	  sspace = !sspace;
	}

      } else if(!strncmp("--newline", argv[c], 9)) {
	if(*(argv[c] + 9)!='\0' && atoi(argv[c] + 9) > 0) {
	  snewline = atoi(argv[c] + 9);
	} else if(c + 1 < argc && atoi(argv[c + 1]) > 0) {
	  snewline = atoi(argv[c + 1]);
	  c++;
	}

      } else if(!strcmp("--zero", argv[c])) {
	zero = !zero;

      } else if(!strcmp("--sort", argv[c])) {
	sort = !sort;
	if(sspace == 0)
	  sspace = 1;
		
      } else if(!strcmp("--unique", argv[c])) {
	unique =! unique;
	if(sspace == 0)
	  sspace = 1;
		
      } else if(!strcmp("--lotto", argv[c])) {
	sort = !sort;
	if(sspace == 0)
	  sspace = 1;
	//sort = 1;
	//unique = 1;

      } else if(!strcmp("--sample", argv[c])) {
	sample = !sample;
	
      } else if(!strcmp("--copyright", argv[c]) ||
	 !strcmp("--version", argv[c])) {
	newressu_version();
#ifdef SHA256
	fprintf(stderr,"\nsha256(");
	for(int c = 0; c < HashLen; c++) {
	  fprintf(stderr, "%02x", programfiledigest[c]);
	}
#endif
	fprintf(stderr, ")\n\n");
	help = 1;
	
      } else if(!strcmp("--webversion", argv[c])) {
	fprintf(stderr, "%s", programname); // touch these outside MAIN
	fprintf(stderr,", sha256(");
	for(int c = 0; c < HashLen; c++) {
	  fprintf(stderr, "%02x", programfiledigest[c]);
	}
	fprintf(stderr,")\n");
	exit(0);
      } else if(!strncmp("--lim", argv[c], 5)) {
	if(*(argv[c] + 5) != '\0') {
	  in_word(&limit, digits, argv[c] + 5);
	} else if(c+1 < argc) {
	  in_word(&limit, digits, argv[c + 1]);
	  c++;
	}
	//fprintf(stdout,"limit:%lu, %ld bytes\n", limit, sizeof(limit));
	if(sspace < 1 ) // 23.6.2021
	  sspace = 1;

      } else if(!strncmp("-s", argv[c], 2)) {
	if(*(argv[c] + 2)!='\0') {
	  size = atoi(argv[c] + 2);
	} else if(c + 1<argc) {
	  size = atoi(argv[c + 1]);
	  c++;
	}
	limit = 0; // 23.6.2021

      } else if(!strncmp("--bits", argv[c], 6)) {
	if(*(argv[c] + 6)!='\0') {
	  ressu_bits1_needed = atoi(argv[c] + 6);
	} else if(c + 1 < argc) {
	  ressu_bits1_needed = atoi(argv[c + 1]);
	  c++;
	}	

      } else if(!strncmp("--bytes", argv[c], 7)) {
	if(*(argv[c] + 7) != '\0') {
	  ressut_bytes = atoi(argv[c] + 7);
	} else if(c + 1 < argc) {
	  ressut_bytes = atoi(argv[c + 1]);
	  c++;
	}	

      } else if(!strncmp("-w", argv[c], 2)) { // words per line
	if(*(argv[c] + 2)!='\0') {
	  words = atoi(argv[c] + 2);
	} else if(c + 1 < argc) {
	  words = atoi(argv[c + 1]);
	  c++;
	}
	if(words < 1)
	  words = 1;
	chars = 0;
	screen = 0;
	
      } else if(!strncmp("-c*", argv[c], 3)) {
	struct winsize w;
	ioctl(0, TIOCGWINSZ, &w);
	chars = w.ws_col;
	words = 0;
#ifdef DEBUG71
	fprintf(stdout,"screencolumns:%d\n", chars);
#endif

      } else if(!strncmp("-l*", argv[c], 3)) {
	struct winsize w;
	ioctl(0, TIOCGWINSZ, &w);
	lines = w.ws_row-1;
#ifdef DEBUG71
	fprintf(stdout,"screenlines:%lld\n", lines);
#endif

      } else if(!strncmp("--screen", argv[c],8)) {
	struct winsize w;
	ioctl(0, TIOCGWINSZ, &w);
	chars = w.ws_col;
	lines = w.ws_row-1;
	words = 0;
	screen = 1;
#ifdef DEBUG71
	fprintf(stdout,"screencolumns:%d", chars);
	fprintf(stdout," screenlines:%lld\n", lines);
#endif
	
      } else if(!strncmp("-c", argv[c], 2)) {  // characters per line
	if(*(argv[c] + 2) != '\0') {
	  chars = atoi(argv[c] + 2);
	} else if(c + 1 < argc) {
	  chars = atoi(argv[c + 1]);
	  c++;
	}
	if(chars < 1)
	  chars = 72;
	words = 0;
	screen = 0;
	
      } else if(!strncmp("-l",argv[c], 2)) { // lines
	if(*(argv[c] + 2) != '\0') {
	  lines = atoll(argv[c] + 2);
	} else if(c + 1 < argc) {
	  lines = atoll(argv[c + 1]);
	  c++;
	}
	screen = 0;
	
      } else if(!strcmp("-x", argv[c]) ||
		!strcmp("--hex", argv[c])) {
	digits = "0123456789abcdef";
	charspaces = 0;
	charwidth = 1;
	size = 4;
	type = 16;

      } else if(!strcmp("-X", argv[c]) ||
		!strcmp("--HEX", argv[c])) {
	digits = "0123456789ABCDEF";
	charspaces = 0;
	charwidth = 1;
	size = 4;
	type = 16;

      } else if(!strcmp("-d", argv[c]) ||
		!strcmp("--dec", argv[c])) {
	digits = "0123456789";
	charspaces = 0;
	charwidth = 1;
	size = 5;
	type = 10;

      } else if(!strcmp("-o", argv[c]) ||
		!strcmp("--oct", argv[c])) {
	digits = "01234567";
	charspaces = 0;
	charwidth = 1;
	size = 3;
	type = 8;

      } else if(!strcmp("-b", argv[c]) ||
		!strcmp("--bin", argv[c])) {
	digits = "01";
	charspaces = 0;
	charwidth = 1;
	size = 8;
	type = 2;

      } else if(!strcmp("-1", argv[c])) {
	digits=
	  "0123456789" \
	  "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
	  "abcdefghijklmnopqrstuvwxyz";
	charspaces = 0;
	charwidth = 1;
	size = 1;
	type = 0;

      } else if(!strcmp("-11", argv[c])) {
	digits=
	  "0123456789";
	charspaces = 0;
	charwidth = 1;
	size = 1;
	type = 10;

      } else if(!strcmp("-12", argv[c])) {
	digits=
	  "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
	charspaces = 0;
	charwidth = 1;
	size = 1;

      } else if(!strcmp("-13", argv[c])) {
	digits=
	  "abcdefghijklmnopqrstuvwxyz";
	charspaces = 0;
	charwidth = 1;
	size = 1;
	type = 0;

      } else if(!strcmp("-13v1", argv[c])) {
	digits=
	  "abcdefghijklmnopqrstuvwxyz" \
	  "aeiouy";
	charspaces = 0;
	charwidth = 1;
	size = 1;
	type = 0;

      } else if(!strcmp("-13v2", argv[c])) {
	digits=
	  "abcdefghijklmnopqrstuvwxyz" \
	  "aeiouy" \
	  "aeiouy";
	charspaces = 0;
	charwidth = 1;
	size = 1;
	type = 0;

      } else if(!strcmp("-13v3", argv[c])) {
	digits=
	  "abcdefghijklmnopqrstuvwxyz" \
	  "aeiouy" \
	  "aeiouy" \
	  "aeiouy";
	charspaces = 0;
	charwidth = 1;
	size = 1;
	type = 0;

      } else if(!strcmp("-13v4", argv[c])) {
	digits=
	  "abcdefghijklmnopqrstuvwxyz" \
	  "aeiouy" \
	  "aeiouy" \
	  "aeiouy" \
	  "aeiouy";
	charspaces = 0;
	charwidth = 1;
	size = 1;
	type = 0;
 
      } else if(!strcmp("-2", argv[c])) {
	digits=
	  "0123456789" \
	  "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
	  "abcdefghijklmnopqrstuvwxyz" \
	  "_-"; // 6 bits
	charspaces = 0;
	charwidth = 1;
	size = 8;
	type = 0;

      } else if(!strcmp("-3", argv[c])) { // 9.5.2021 JariK
	digits=
	  "!\"#$%&'()*+,-./0123456789:;<=>?@" \
	  "ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`" \
	  "abcdefghijklmnopqrstuvwxyz{|}~";
	charspaces = 0;
	charwidth = 1;
	size = 8;
	type = 0;

      } else if(!strcmp("-5", argv[c])) {
	digits=
	  "0123456789" \
	  "ABCDEFGHIJKLMNOPQRSTUV"; // 5 bits
	charspaces = 0;
	charwidth = 1;
	size = 8;
	type = 0;

      } else if(!strcmp("-9", argv[c])) {
	digits=
	  "0123456789" \
	  "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
	  "abcdefghijklmnopqrstuvwxyz";
	charspaces = 0;
	charwidth = 1;
	size = 8;
	type = 0;

      } else if(!strcmp("--dnk", argv[c]) || // Danish alphabet
		!strcmp("--nor", argv[c])) { // Norwegian alphabet
	digits=
	  "ABCDEFGHIJKLMNOPQRSTUVWXYZÆØÅ" \
	  "abcdefghijklmnopqrstuvwxyzæøå";
	charspaces = 0;
	charwidth = 1;
	size = 8;
	type = 0;

      } else if(!strcmp("--fin", argv[c]) || // Finnish alphabet
		!strcmp("--swe", argv[c])) { // Swedish alphabet
	digits=
	  "ABCDEFGHIJKLMNOPQRSTUVWXYZÅÄÖ" \
	  "abcdefghijklmnopqrstuvwxyzåäö";
	charspaces = 0;
	charwidth = 1;
	size = 8;
	type = 0;

      } else if(!strcmp("--rus", argv[c])) { // Russian alphabet
	digits=
	  "АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ" \
	  "абвгдеёжзийклмнопрстуфхцчшщъыьэюя";
	charspaces = 0;
	charwidth = 1;
	size = 8;
	type = 0;

      } else if(!strcmp("--est", argv[c])) { // Estonian alphabet

	digits=
	  "ABCDEFGHIJKLMNOPQRSŠZŽTUVWÕÄÖÜXY" \
	  "abcdefghijklmnopqrsšzžtuvwõäöüxy";
	charspaces = 0;
	charwidth = 1;
	size = 8;
	type = 0;
  
      } else if(!strcmp("--ltu", argv[c])) { // Lithuanian alphabet
	digits=
	  "AĄBCČDEĘĖFGHIĮYJKLMNOPRSŠTUŲŪVZŽ" \
	  "aąbcčdeęėfghiįyjklmnoprsštuųūvzž";
	charspaces = 0;
	charwidth = 1;
	size = 8;
	type = 0;

      } else if(!strcmp("--lva", argv[c])) { // Latvian alphabet
	digits=
	  "AĀBCČDEĒFGĢHIĪJKĶLĻMNŅOPRSŠTUŪVZŽ" \
	  "aābcčdeēfgģhiījkķlļmnņoprsštuūvzž";
	charspaces = 0;
	charwidth = 1;
	size = 8;
	type = 0;

      } else if(!strcmp("--fra", argv[c]) // French alphabet
		||!strcmp("--gbr", argv[c]) // Great Britain alphabet
		||!strcmp("--usa", argv[c]) // United States alphabet
		||!strcmp("--ita", argv[c]) // Italian alphabet
		||!strcmp("--eng", argv[c]) // English alphabet
		) {
	digits=
	  "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
	  "abcdefghijklmnopqrstuvwxyz";
	charspaces = 0;
	charwidth = 1;
	size = 8;
	type = 0;

      } else if(!strcmp("--deu", argv[c])) { // Deutsch alphabet
	digits=
	  "ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÜẞ" \
	  "abcdefghijklmnopqrstuvwxyzäöüß";
	charspaces = 0;
	charwidth = 1;
	size = 8;
	type = 0;

      } else if(!strcmp("--grc", argv[c])) { // Greek alphabet
	digits=
	  "ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ" \
	  "αβγδεζηθικλμνξοπρστυφχψω";
	charspaces = 0;
	charwidth = 1;
	size = 8;
	type = 0;
	
      } else if(!strcmp("--jp", argv[c])) { // all Japanese alphabets

	digits = NULL;
	digits_add_string(&digits,
	    "ぁあぃいぅうぇえぉおかがきぎく"
	  "ぐけげこごさざしじすずせぜそぞた"
	  "だちぢっつづてでとどなにぬねのは"
	  "ばぱひびぴふぶぷへべぺほぼぽまみ"
	  "むめもゃやゅゆょよらりるれろゎわ"
	  "ゐゑをんゔゕゖ");
	digits_add_string(&digits,
	  "゠ァアィイゥウェエォオカガキギク"
	  "グケゲコゴサザシジスズセゼソゾタ"
	  "ダチヂッツヅテデトドナニヌネノハ"
	  "バパヒビピフブプヘベペホボポマミ"
	  "ムメモャヤュユョヨラリルレロヮワ"
	  "ヰヱヲンヴヵヶヷヸヹヺ・ーヽヾヿ");
	digits_add_limits(&digits,0x4e00,0x9fef);

	digitsext = digits;
	charspaces = 0;
	charwidth = 2;
	size = 8;
	type = 0;

	
      } else if(!strcmp("--jp1", argv[c]) // Japanese hiragana alphabet
		||!strcmp("--hir", argv[c])) { // Japanese hiragana alphabet
	digits=
	    "ぁあぃいぅうぇえぉおかがきぎく"
	  "ぐけげこごさざしじすずせぜそぞた"
	  "だちぢっつづてでとどなにぬねのは"
	  "ばぱひびぴふぶぷへべぺほぼぽまみ"
	  "むめもゃやゅゆょよらりるれろゎわ"
	  "ゐゑをんゔゕゖ";
	charspaces = 0;
	charwidth = 2;
	size = 8;
	type = 0;

      } else if(!strcmp("--jp2", argv[c]) // Japanese katakana alphabet
		||!strcmp("--kat", argv[c])) {  // Japanese katakana alphabet
	digits=
	  "゠ァアィイゥウェエォオカガキギク"
	  "グケゲコゴサザシジスズセゼソゾタ"
	  "ダチヂッツヅテデトドナニヌネノハ"
	  "バパヒビピフブプヘベペホボポマミ"
	  "ムメモャヤュユョヨラリルレロヮワ"
	  "ヰヱヲンヴヵヶヷヸヹヺ・ーヽヾヿ";
	charspaces = 0;
	charwidth = 2;
	size = 8;
	type = 0;

      } else if(!strcmp("--cn", argv[c]) // Chinese alphabet 
		||!strcmp("--jp3", argv[c]) // Japanese kanji alphabet
		||!strcmp("--kan", argv[c])) { // Japanese kanji alphabet

	digits = NULL;
	digits_add_limits(&digits,0x4e00,0x9fef);

	digitsext = digits;
	charspaces = 0;
	charwidth = 2;
	size = 8;
	type = 0;
	
      } else if(!strcmp("--kor", argv[c])) { // Korean alphabet 

	digits = NULL;
	digits_add_limits(&digits,0xac00,0xd7a3);   // Hangul Syllables (AC00–D7A3)
	//digits_add_limits(&digits,0x1100,0x11ff); // Hangul Jamo (1100–11FF)
	//digits_add_limits(&digits,0x3130,0x318f); // Hangul Compatibility Jamo (3130–318F)
	//digits_add_limits(&digits,0xa960,0xa97f); // Hangul Jamo Extended-A (A960–A97F)
	//digits_add_limits(&digits,0xd7b0,0xd7ff); //Hangul Jamo Extended-B (D7B0–D7FF)

	digitsext = digits;
	charspaces = 0;
	charwidth = 2;
	size = 8;
	type = 0;

      } else if(!strcmp("--ind", argv[c])
		||!strcmp("--hin", argv[c])) { // Hindi alphabet
	digits="ँंःअआइईउऊऋएऐऑओऔकखगघचछजझञटठडढणतथदधनपफबभमयरलवशषसह़ािीुूृॅेैॉोौ्ाकेरहसनींमि्तपलोयैबदवुजएगचथअऔूउशडख़भआटछधफइँषघईझठौणॉओृढऊऐऑञःॅऋ";

	//digits = NULL;
	//digits_add_limits(&digits,0x900,0x97f);   // Hindi (0900-097f)

	charspaces = 0;
	charwidth = 1;
	size = 8;
	type = 0;

      } else if(!strcmp("--got", argv[c])) { // Greek alphabet                                                                                                                          
        digits=
          "𐌰𐌱𐌲𐌳𐌴𐌵𐌶𐌷𐌸𐌹𐌺𐌻𐌼𐌽𐌾𐌿"
          "𐍀𐍁𐍂𐍃𐍄𐍅𐍆𐍇𐍈𐍉𐍊";
	charspaces = 0;
	charwidth = 1;
        size = 8;
	type = 0;

      } else if(!strcmp("--cards", argv[c])) {
	digits=
	  "🂡🂢🂣🂤🂥🂦🂧🂨🂩🂪🂫🂭🂮" \
	  "🂱🂲🂳🂴🂵🂶🂷🂸🂹🂺🂻🂽🂾" \
	  "🃁🃂🃃🃄🃅🃆🃇🃈🃉🃊🃋🃍🃎" \
	  "🃑🃒🃓🃔🃕🃖🃗🃘🃙🃚🃛🃝🃞";
	charspaces = 1;
	charwidth = 2;
	size = 5;
	type = 0;

      } else if(!strcmp("--cardsuits", argv[c])) {
	digits=
	  "♠♡♢♣♤♥♦♧";
	charspaces = 1;
	charwidth = 2;
	size = 5;
	type = 0;

      } else if(!strcmp("--chessmen", argv[c])) {
	digits=
	  "♔♕♖♗♘♙♚♛♜♝♞♟";
	charspaces = 1;
	charwidth = 2;
	size = 5;
	type = 0;

      } else if(!strcmp("--dice", argv[c])) {
	digits=
	  "⚀⚁⚂⚃⚄⚅";
	charspaces = 1;
	charwidth = 2;
	size = 5;
	type = 0;

      } else if(!strcmp("--mahjong", argv[c])) {
	digits= // removed 🀄 & 🀫
	  "🀀🀁🀂🀃🀅🀆" \
	  "🀇🀈🀉🀊🀋🀌🀍🀎🀏" \
	  "🀐🀑🀒🀓🀔🀕🀖🀗🀘" \
	  "🀙🀚🀛🀜🀝🀞🀟🀠🀡" \
	  "🀢🀣🀤🀥🀦🀧🀨🀩🀪";
	charspaces = 1;
	charwidth = 2;
	size = 5;
	type = 0;

      } else if(!strcmp("--dna", argv[c])) {
	digits=
	  "acgt";
	charspaces = 0;
	charwidth = 1;
	size = 4;
	type = 0;
	
      } else if(!strcmp("--DNA", argv[c])) {
	digits=
	  "ACGT";
	charspaces = 0;
	charwidth = 1;
	size = 4;
	type = 0;
	
      } else if(!strcmp("--pattern1", argv[c])) {
	digits=
	  "▌▙▄";
	charspaces = 0;
	charwidth = 1;
	size = 1;
	type = 0;

      } else if(!strcmp("--pattern2", argv[c])) {
	digits=
	  "◜◝◞◟";
	charspaces = 0;
	charwidth = 1;
	size = 1;
	type = 0;

      } else if(!strcmp("--pattern3", argv[c])) {
	digits=
	  "\\/";
	charspaces = 0;
	charwidth = 1;
	size = 1;
	type = 0;

      } else if(!strcmp("--pattern4", argv[c])) {
	digits=
	  "◸◹◺◿";
	charspaces = 1;
	charwidth = 2;
	size = 1;
	type = 0;

      } else if(!strcmp("--pattern5", argv[c])) {
	digits=
	  "▧▨";
	charspaces = 1;
	charwidth = 2;
	size = 1;
	type = 0;

      } else if(!strcmp("--isalnum", argv[c]) || // 9.5.2021 JariK
	        !strcmp("--isalpha", argv[c]) ||
	        !strcmp("--isdigit", argv[c]) ||
	        !strcmp("--isgraph", argv[c]) ||
	        !strcmp("--islower", argv[c]) ||
	        !strcmp("--isprint", argv[c]) ||
	        !strcmp("--ispunct", argv[c]) ||
	        !strcmp("--isupper", argv[c]) ||
		!strcmp("--isxdigit", argv[c])) {
	unsigned char *p=digitstemp;
	for(d=0;d<256;d++) {
	  if((!strcmp("--isalnum", argv[c]) && isalnum(d)) ||
	     (!strcmp("--isalpha", argv[c]) && isalpha(d)) ||
	     (!strcmp("--isdigit", argv[c]) && isdigit(d)) ||
	     (!strcmp("--isgraph", argv[c]) && isgraph(d)) ||
	     (!strcmp("--islower", argv[c]) && islower(d)) ||
	     (!strcmp("--isprint", argv[c]) && isprint(d)) ||
	     (!strcmp("--ispunct", argv[c]) && ispunct(d)) ||
	     (!strcmp("--isupper", argv[c]) && isupper(d)) ||
	     (!strcmp("--isxdigit", argv[c]) && isxdigit(d))) {
	    *p++ = d;
	  }
	}
	*p = '\0';
	digits = digitstemp;
	size = 8;
	type = 0;

      } else if(!strncmp("-i", argv[c], 2)) {
	digits = NULL;
	if(*(argv[c] + 2)!='\0') {
	  digits = argv[c] + 2;
	} else if(c + 1 < argc) {
	  digits = argv[c + 1];
	  c++;
	}
	if(digits == NULL || utf8characters(digits) < 2) {
	  fprintf(stderr,"%s: not enough digits \"%s\"\n", procname, argv[c]);
	  help = 1;
	}
	size = 1;

#ifdef USE_TIMER
      } else if(!strcmp("--timer", argv[c])) {
	timer = 1;

#endif
      } else if(!strcmp("--ressu", argv[c])) {
	input = INPUT_RESSU;

      } else if(!strcmp("--pseudoressu", argv[c])) {
	input = INPUT_PSEUDORESSU;

      } else if(!strcmp("--fast", argv[c])) {
	input = INPUT_FAST;

      } else if(!strcmp("--single", argv[c])) {
	input = INPUT_SINGLE;

      } else if(!strcmp("--fixedclock", argv[c])) {
	clockmode = !clockmode;
	ressu_setclock(clockmode);
 
      } else if(!strcmp("--verbose", argv[c])) {
	verbose = !verbose;
	
      } else if(!strcmp("--statline", argv[c])) {
	print_statline = !print_statline;
	
#ifdef FORT
      } else if(!strcmp("--fort", argv[c])) {
	input = INPUT_FORT;

      } else if(!strcmp("--fortxor", argv[c])) {
	input = INPUT_FORTXOR;

      } else if(!strcmp("--fortverbose", argv[c])) {
	verbose = !verbose;

      } else if(!strcmp("--fortuseweb", argv[c])) {
	fort_use_web = !fort_use_web;
	
#endif
#ifdef USE_RDRAND
      } else if(!strcmp("--rdrand", argv[c])) {
	input = INPUT_RDRAND;

#endif
#ifdef USE_RDSEED
      } else if(!strcmp("--rdseed", argv[c])) {
	input = INPUT_RDSEED;

#endif
      } else if(!strcmp("--urandom", argv[c])) {
	input = INPUT_URANDOM;

#ifdef USE_RANDOM
      } else if(!strcmp("--random", argv[c])) {
	input = INPUT_RANDOM;

#endif
      } else {
	fprintf(stderr,"%s: invalid option %s\n", procname, argv[c]);
	status = 1;
	help = 1;
      }
    } else {
      help = 1;

    } // if(!strncmp
  } // for(c=0

  if(size == 0)
    size = 1;
  else if(size > 1024)
    size = 1024;
  if(lines < 1)
    lines = 1;

#ifdef USE_TIMER
  timerstart = getseconds();
#endif
  
  digitscount = utf8characters(digits);
  characterlengths = utf8lengths(digits);

#ifdef FORT
  if(input == INPUT_FORT ||
     input == INPUT_FORTXOR ) {
    strcpy(fort_random_file, "newressufort.rnd");
    strcpy(fort_secret_file, "newressufortsecret.rnd");
    //fort_verbose = 0;
    fort_init();
  }
#endif
  
  if(sample) {
    dump_sample();
    exit(status);
  } // if(sample)

  // get linenumber length in digits

  plinesdigits = line_number_length();

  // get data length in digits

  if(limit != 0) {
    unsigned char wordbuf[128];
    out_word(sizeof(wordbuf), wordbuf, digits, limit-1);
    size = utf8characters(wordbuf);
  }

  pchars = 0;
  pwords = 0;
  int linew = 0;

  // in beginning of line, count printed line number
      
  if(!quiet && slineno) {
    sprintf(linenobuf,"%0*llu", plinesdigits, plines);
    pchars += strlen(linenobuf);
    pchars++;
  }

  // count words
  
  while((chars > 0 && pchars + size * charwidth + calc_spaces() <= chars) ||
	(words > 0 && pwords < words)) {
    
    linew++;

#ifdef DEBUG50
    fprintf(stdout,"spaces:%d", calc_spaces());
#endif
    
    pchars += calc_spaces();
    pchars += (size*charwidth);
    pwords++;

#ifdef DEBUG50
    fprintf(stdout,", chars:%d", chars);
    fprintf(stdout,", pchars:%d", pchars); 
    fprintf(stdout,", words:%d", words);
    fprintf(stdout,", pwords:%d", pwords);  
    fprintf(stdout,"\n");
#endif
  }

  // one data word needed
  
  if(linew < 1)
    linew = 1;

#define aDEBUG89 2
  
  unsigned long wordvalues=1, wordvaluesold;

  for(c = 0; c < size; c++) {
    wordvaluesold = wordvalues;
    wordvalues *= digitscount;
#ifdef DEBUG89 
   fprintf(stdout, "wordvaluesold:%ld", wordvaluesold);
    fprintf(stdout, ", digitscount:%d", digitscount);
    fprintf(stdout, ", wordvalues:%ld", wordvalues);
    fprintf(stdout, "\n");
#endif
    if(wordvalues / digitscount != wordvaluesold) {
      wordvalues = 0;
#ifdef DEBUG89
      fprintf(stdout, "wordvalues:%ld", wordvalues);
      fprintf(stdout, "\n");
#endif
      break;
    }
  }
  
  if(limit > 0) {
    if(sort==1 && limit-!zero < words) {
      fprintf(stderr, "%s: limit is less than words (zero)", procname);
      fprintf(stderr, ", limit:%lu", limit);
      fprintf(stderr, ", words:%d", words);
      fprintf(stderr, ", zero:%d", zero);
      fprintf(stderr, "\n");
      
      help = 1;
      lines = 0;
    }
  } else {
    if(sort == 1 && size > 0 && wordvalues > 0 && wordvalues < linew) {
      fprintf(stderr, "%s:", procname);
      fprintf(stderr, " digits is less than calculated words");
      fprintf(stderr, ", digits:%d(%lu)", digitscount, wordvalues);
      fprintf(stderr, ", words:%d\n", linew);
      help = 1;
      lines = 0;
    }
  }

  // "small" words as single newressu_gen_limit() call
  
  if(limit == 0 && wordvalues > 0)
    limit = wordvalues;

#ifdef DEBUG51
  unsigned char buffer10[10];
  if(type == 2)
    sprintf(buffer10, "%2lx", limit - 1);
  else if(type == 8)
    sprintf(buffer10, "%2lo", limit - 1);
  else if(type == 10)
    sprintf(buffer10, "%2ld", limit - 1);
  else if(type == 16)
    sprintf(buffer10, "%2lx", limit - 1);
  else
    sprintf(buffer10, "%2ld", limit - 1);
  limitsize = strlen(buffer10);
#endif  
  
  if(stats) {
    fprintf(stderr, "randomsource: %s", randomgen[input]);
    fprintf(stderr, ", clockmode: %d", clockmode);
    if(clockmode==1)
      fprintf(stderr, ", clockchainlength: %d", fixedclockchainlength);
    fprintf(stderr, ", size: %d", size);
    fprintf(stderr, ", lines: %llu", lines);
    fprintf(stderr, ", linew: %d", linew);
    fprintf(stderr, ", pchars: %d", pchars);
    fprintf(stderr, ", pwords: %d", pwords);
    fprintf(stderr, ", tchars: %llu", (unsigned long long)size * linew * lines);
    fprintf(stderr, ", digitscount: %d", digitscount);
    if(wordvalues > 0)
      fprintf(stderr, ", wordvalues: %lu", wordvalues);
    if(limit > 0) {
      fprintf(stderr, ", limit: %lu", limit);
#ifdef DEBUG51
      fprintf(stderr, ", type: %d", type);
      fprintf(stderr, ", limitsize%d: %d", type, limitsize);
#endif
    }
    fprintf(stderr, "\n");
  }
  
  if(help) {
    struct helpline {
      char *command;
      char *text;
    } helplines[] = {
      { "newressu -d", "print random decimal digits (default)" },
      { "newressu -o", "print octal digits" },
      { "newressu -x", "print hexadecimal digits" },
      { "newressu -b", "print binary digits" },
      { "newressu -d --space", "print decimal digits, with spaces between \"words\"" },
      { "newressu -b --space", "print binary digits, with spaces between \"words\"" },
      { "newressu -d -l30", "print decimal digits, 30 lines" },
      { "newressu -d -l30 --lineno", "print decimal digits, 30 lines, no linenumbers"  },
      { "newressu --rand", "print numbers in rand style listing"  },
      { "newressu --space 2", "print numbers in 2 number groups"  },
      { "newressu --newline 5", "print numbers in 5 line groups"  },
      { "newressu -l*", "fill all screen lines"  },
      { "newressu -c*", "fill all screen columns"  },
      { "newressu --screen", "fill screen columns and lines"  },
      { "newressu -d -c128", "print decimal digits, 128 characters per line"  },
      { "newressu -d --lim10", "print decimal numbers between 0-9" },
      { "newressu -d --lim10 -x","print hexadecimal numbers with decimal limit" },
      { "newressu -d -s10 --space","print decimal numbers with spaces and wordsize 10" },
      { "newressu -d --space --zero --lim7","print rollings of dice (1-6)" },
      { "newressu -b -s1 --space","print throws of coin (0,1)" },
      { "newressu -d --lim41 -w7 --zero --lotto","print lotto numbers for finnish lotto 7x(1-40)" },
      { "newressu -d -s5 --sort -w16","print 16 sorted decimal numbers from 0 to 99999" },
      { "newressu --isalnum", "print alphanumeric characters" },
      { "newressu --isalpha", "print alphabetic characters" },
      { "newressu --isgraph", "print printables excluding space" },
      { "newressu --islower", "print lowercase characters" },
      { "newressu --ispunct", "print punctuation characters" },
      { "newressu --isupper", "print uppercase characters" },
      { "newressu -1", "print material for passwords" },
      { "newressu --dnk/--nor", "danish/norwegian alphabet" },
      { "newressu --fin/--swe", "finnish/norwegian alphabet" },
      { "newressu --rus", "russian alphabet" },
      { "newressu --est", "estonian alphabet" },
      { "newressu --ltu", "lithuanian alphabet" },
      { "newressu --lva", "latvian alphabet" },
      { "newressu --fra/--gbr/--usa/--ita", "french/gb/us/italian alphabet" },
      { "newressu --deu", "deutsch alphabet" }, 
      { "newressu --grc", "greek alphabet" }, 
      { "newressu --jp", "japan alphabet (hiragana, katakana, kanji) " }, 
      { "newressu --jp1/--hir", "japan alphabet (hiragana) " }, 
      { "newressu --jp2/--kat", "japan alphabet (katakana) " }, 
      { "newressu --jp3/--kan", "japan alphabet (kanji) " }, 
      { "newressu --cn", "chinese alphabet" }, 
      { "newressu --kor", "korean alphabet" }, 
      { "newressu --got", "gothic alphabet" }, 
      { "newressu --cards", "playing cards" }, 
      { "newressu --cardsuits", "playing card suits" }, 
      { "newressu --chessmen", "chess men" }, 
      { "newressu --dice", "print dice" },
      { "newressu --mahjong", "print mahjong pieces" },
      { "newressu -i▌▙▄ / --pattern1", "print pattern1" },
      { "newressu -i\\\\/ / --pattern2", "print pattern2" },
      { "newressu --ressu", "use randomness from ressu (default)" },
      { "newressu --pseudoressu", "use randomness from pseudo random ressu" },
      { "newressu --fast", "use randomness from fast ressu" },
      { "newressu --single", "use randomness from single round of ressu" },
#ifdef FORT
      { "newressu --fort", "use randomness from fort" },
#endif
#ifdef USE_RDRAND
      { "newressu --rdrand", "use randomness from intel rdrand" },
#endif
#ifdef USE_RDSEED
      { "newressu --rdseed", "use randomness from intel rdseed" },
#endif
      { "newressu --urandom", "use randomness from /dev/urandom" },
#ifdef USE_RANDOM
      { "newressu --random", "use randomness from /dev/random" },
#endif
    };
    
    for(c = 0; c < sizeof(helplines) / sizeof(helplines[0]); c++) {
      fprintf(stderr, "%-50s", helplines[c].text);
      fprintf(stderr, "%-25s", helplines[c].command);
      fprintf(stderr, "\n");
    }
    for(c = 0; c < chars; c++)
      fprintf(stderr, "*");
    fprintf(stderr, "\n");
    if(lines > 1)
      lines = 1; // print one line below help as a sample
  }

  if(lines == 0)
    exit(status);

  int linecnt = 0;
  unsigned char *line = NULL;
  unsigned char wordbuf[1025];
  
  for(;;) {

    if(!sort) { // no sort, no lotto

      pwords = 0;

      while(pwords < linew) {
	readword(wordbuf);

	if(!quiet) {

	  // in beginning of line, print line number

	  print_line_number();
	
	  // want to print spaces between "words"?

	  print_spaces();
	
	  // print word

	  print_word(wordbuf);

	  newressu_output = 1;
	}

	pwords++;
      } // while(pwords<linew) {
    } else { // if(!sort)

      pwords = 0;
      
      line_clear(&linecnt, &line);

      // fetch and save words on this row

      while(pwords < linew) {
	readword(wordbuf);

	if(line_add_string_sort(&linecnt, &line, wordbuf)) {
	  pwords++;
	}
      } // while(pwords<linew) {

      pwords = 0;

      // print words on this row

      while(pwords < linew) {

	line_get_string(sizeof(wordbuf), wordbuf, pwords, line);

	if(!quiet) {

	  // in beginning of line, print line number

	  print_line_number();
	
	  // want to print spaces between "words"?
	
	  print_spaces();

	  // print word
	
	  print_word(wordbuf);

	  newressu_output = 1;
	}
	pwords++;
      } // while(pwords<linew) {
    } // if(!sort)

    if(!quiet) {
      fprintf(stdout, "\n");
    }

    plines++;
    ptotallines++;

    // print empty line

    if(!quiet &&
       snewline > 0 &&
       plines%snewline == 0) {
      // if screenfull printed, use
      // total lines (contains also
      // empty lines), if not
      // screenfull use printed
      // lines (without empty lines)
      if((screen && ptotallines < lines) ||
	 (!screen && plines < lines)) {
	fprintf(stdout, "\n");
	ptotallines++;
      }
    }

    //fflush(stdout);
    
    newressu_output = 0;
    
    // all needed lines printed?

    if(screen && snewline > 0 &&
       ptotallines >= lines)
      break;
    if(plines >= lines)
      break;

#define STATDIV (1024*1024*100)
    
    if(print_statline) {
      if(isatty(STDOUT_FILENO)) { // 0=stdin, 1=stdout, 2=stderr
	print_statline = 0;
      } else {
	static unsigned char prevbuf10[10];
	unsigned char buf10[10];
	unsigned long long bytes;
	bytes = (((unsigned long long)size * linew * plines) / STATDIV) * STATDIV;
	stat_line_get_readable(buf10, bytes);
	static int prevpros;
	int pros = (int)(((double)plines/lines)*100);

#ifdef DEBUG97
	fprintf(stderr, "%lld", (unsigned long long)size * linew * plines);
	fprintf(stderr, ", %lld", bytes);
	fprintf(stderr, ", %s", buf10);
	fprintf(stderr, ", %s", prevbuf10);
	fprintf(stderr, "\n");
#endif
	
	if(prevpros != pros ||
	   strcmp(prevbuf10, buf10)) {

	  strcpy(prevbuf10, buf10);
	  stat_line_begin();
	  //fprintf(stderr,"\n");
	  stat_line_printf("%s characters written:", randomgen[input]);
	  stat_line_readable((unsigned long) size * linew * plines);
	  stat_line_printf(", %d%%", pros);
	  //stat_line_printf(", lines:%lld",lines);
	  //stat_line_printf(", plines:%lld",plines);
	  stat_line_end();
	  stat_line_cursor_start();
	  if(pros > 0 && prevpros != pros && pros % 10 == 0)
	    fprintf(stderr,"\b \b\n");

	  prevpros = pros;
	}
	stat_line_cursor();
      }
    } // if(print_statline)
  } // for(;;)

  if(print_statline) { // 0=stdin, 1=stdout, 2=stderr
    stat_line_cursor_remove();
    stat_line_begin();
    //stat_line_printf("Done!");
    stat_line_end();
  }
    
#ifdef USE_TIMER
  if(timer) {
    // run time 1565 seconds, 0.000782 seconds/line (decimal)
    // run time 6538 seconds, 0.003269 seconds/line (--ch)
    // run time 1954 seconds, 0.000977 seconds/line (--ch digitscount)
    // run time 1814 seconds, 0.000907 seconds/line (--ch digitscount)
    // run time 1540 seconds, 0.000770 seconds/line (decimal)
    // run time 2196 seconds, 0.001098 seconds/line (--got)
    // run time 1536 seconds, 0.000768 seconds/line (decimal)   
    // run time 2008 seconds, 0.001004 seconds/line (--jp)
    // run time 2043 seconds, 0.001022 seconds/line (--jp)
    double timertook=getseconds()-timerstart;
    fprintf(stderr, "run time %f seconds", timertook);
    fprintf(stderr, ", %lld lines", plines);
    fprintf(stderr, ", %f useconds/line", timertook * 1000000 / plines);
    fprintf(stderr, ", %d characters/line", pwords * size);
    fprintf(stderr, ", %f useconds/character",
	    (timertook * 1000000 / plines) / (pwords * size) );
    fprintf(stderr, "\n");
  }
#endif
  fflush(stdout);

  if(digitsext != NULL)
    free(digitsext);
  
#ifdef FORT
  if(input == INPUT_FORT ||
    input == INPUT_FORTXOR) {
    fort_save();
  }
#endif
  exit(status);
}

#endif // MAIN

newressu.h

void ressu_genbytes(int size, unsigned char *buffer);
unsigned long ressu_gen_limit(unsigned long limit);
void pseudoressu_bytes(int buflen, unsigned char *buf);
void inccvar();
void clearcvar();

#define INPUT_RESSU 0
#define INPUT_PSEUDORESSU 1
#define INPUT_FAST 2
#define INPUT_SINGLE 3
#define INPUT_FORT 6
#define INPUT_FORTXOR 7
#define INPUT_RDRAND 8
#define INPUT_RDSEED 9
#define INPUT_URANDOM 10
#define INPUT_RANDOM 11

extern int verbose;

extern int input;
extern int newressu_output;
extern char *randomgen[];
extern void pseudoressu_bytes(int, unsigned char *);

fort.c

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <ctype.h>

#include <sys/time.h>
#include <errno.h>

#include <openssl/ssl.h>

#include "sha256.h"
#include "newressu.h"

extern unsigned char *procname;
static unsigned char *programname = "fort version 0.52 ©";
unsigned char *fortuseragent = "fort version 0.52 ©"; 
static unsigned char *copyright = "Copyright (c) 2020-2022 Jari Kuivaniemi, Helsinki, Finland. Kaikki oikeudet pidätetään!";

#define FORT_INTERNAL_EVENTS 2

#define aDEBUG10 2
#define aDEBUG18 2

#define FORT_XOR_VERSION 2
#define FORT_ORIGINAL_VERSION 2

#include "fort.h"

int fort_events = 1;
int fort_use_web = 0;

#define DEBUG 2
#define FORT_MIN_POOL_SIZE 64
static unsigned int fort_internal_events = 1;
static unsigned int fort_internal_event_mode = 1;

#define FORT_USE_WEB 2
#define FORT_USE_URANDOM 2
#define aFORT_USE_RANDOM 2
#define FORT_USE_NEWRESSU_COMMAND 2

unsigned char fort_random_file[128] = "fort.rnd";

#ifdef DEBUG10
static int event_id = 0;
static char fort_events_file[128] = "fortevents.deb";
#endif

// Additional secrets to fort
// (Adversary must guess these too)

#define ADDITIONAL_SECRETS 2
#ifdef ADDITIONAL_SECRETS
unsigned char fort_secret_file[128] = "fortsecret.rnd";
unsigned char *program_secret = "OHUwiVmwm8HlqNhKxPi9rdt_8lm4jXI8hi-FCntPAQN_UXOyt4s6zQnRo__ySAd1";  
#endif

#define FORT_64_POOLS 2

#ifdef FORT_64_POOLS
#define FORT_POOLS 64
typedef unsigned long FORT_COUNTER;
#else
#define FORT_POOLS 32
typedef unsigned int FORT_COUNTER;
#endif

void fort_mix();

void hash_update_cvar(HashCtx *hash)
{
  hash_update(hash, (unsigned char *)&cvar,
	      cvarsize + 1);

  inccvar();  
}

#define aDEBUG8 2

void hash_update_ressu(HashCtx *hash)
{
  unsigned char temp[16];
  ressu_genbytes(sizeof(temp), temp);

#ifdef DEBUG8
  fprintf(stdout,", ressu:");
  for(int c = 0; c < sizeof(temp); c++)
    fprintf(stdout,"%02x", temp[c]);
#endif
  
  hash_update(hash, temp, sizeof(temp));
}    

static IUTIME gettenths()
{
  struct timeval tv;

  gettimeofday(&tv, NULL);

  return((IUTIME)tv.tv_sec * 10 +
      (int)tv.tv_usec / 100000);
}

static IUTIME getmicroseconds()
{
  struct timeval tv;

  gettimeofday(&tv, NULL);

  return((IUTIME)tv.tv_sec * 1000000 +
      tv.tv_usec);
}

static IUTIME getseconds()
{
  struct timeval tv;

  gettimeofday(&tv, NULL);

  return((IUTIME)tv.tv_sec);
}

struct fort_pool {
  unsigned long length;
  HashCtx pool;
};

static struct fort_pool fort_pools[FORT_POOLS];

void fort_add_random_event(int *pool, int source, int mode, int len, unsigned char *buf)
{
  while(len > 1 && buf[len-1] == 0)
    len--;

  HashUpdate(&fort_pools[*pool].pool, buf, len);
  fort_pools[*pool].length += len; // (unsigned long)2097152*16384;

#ifdef DEBUG10
  if(event_id < 65536) {
    FILE *fp1;
    if((fp1 = fopen(fort_events_file, "a")) != NULL) {
      fprintf(fp1,"id=%d", event_id);
      fprintf(fp1,", source=%02d", source);
      fprintf(fp1,", pool=%02d", *pool);
      fprintf(fp1,", mode=%d", mode);
      fprintf(fp1,", len=%d", len);
      fprintf(fp1,", data=");
      for(int c=0;c<len;c++)
        fprintf(fp1,"%02x", buf[c]);
      fprintf(fp1,"\n");
      fflush(fp1);
      fclose(fp1);
      event_id++;
    }
  }
#endif

  switch(mode) {

  case 1:
    (*pool) = ((*pool) + 1) % FORT_POOLS;
    break;

  case 3:
    break; // caller decides next pool

  }
}

void fort_add_random_event_timer_start(IUTIME *micros)
{
  *micros = getmicroseconds();
}

void fort_add_random_event_timer_do(int *pool, int source, int mode, IUTIME *micros)
{
  unsigned char temp[2];
  IUTIME t;

  t = getmicroseconds() - *micros;
  temp[0] = t & 0xff;
  temp[1] = (t >> 8) & 0xff;

  fort_add_random_event(pool, source, mode,
			sizeof(temp), temp);
}

void fort_add_random_event_time(int *pool, int source, int mode)
{
  int len;
  unsigned char temp[2];
  IUTIME t;
  static int prev_second = -1;

  t = getmicroseconds();
  temp[0] = t & 0xff;
  temp[1] = (t >> 8) & 0xff;
  len = 2;

  if(prev_second == temp[1]) {
    len--;
  } else {
    prev_second = temp[1];
  }

  fort_add_random_event(pool, source, mode,
	       len, temp);
}

void fort_add_random_event_split(int *pool, int source, int mode, int len, unsigned char *buf, int size)
{
  int n;

  while(len != 0) {
    n = (size<len)? size:len;
    fort_add_random_event(pool, source,
        mode, n, buf);
    buf += n;
    len -= n;
  }
}

#define aDEBUG18 2

void hash_init(HashCtx *hash)
{
  FORT_INTERNAL_EVENTS_START(10)

  HashInit(hash);

#ifdef DEBUG18
  fprintf(stdout,"init\n");
#endif
  
  FORT_INTERNAL_EVENTS_END(11)
}

void hash_update(HashCtx *hash, unsigned char *data, int len)
{
  FORT_INTERNAL_EVENTS_START(12)

  HashUpdate(hash, data, len);

#ifdef DEBUG18
  fprintf(stdout,"update    ");
  for(int d = 0; d < len; d++)
    fprintf(stdout,"%02x",data[d]);
  fprintf(stdout,"\n");
#endif
  
  FORT_INTERNAL_EVENTS_END(13)
}

void hash_final(unsigned char digest[HashLen], HashCtx *hash)
{
  FORT_INTERNAL_EVENTS_START(14)

  HashFinal(digest, hash);

#ifdef DEBUG18
  fprintf(stdout,"final     ");
  for(int d = 0; d < HashLen; d++)
    fprintf(stdout,"%02x", digest[d]);
  fprintf(stdout,"\n");
#endif
  
  FORT_INTERNAL_EVENTS_END(15)
}

static unsigned char fort_key[HashLen];

void fort_rekey(unsigned char *digest)
{
  HashCtx hash;

  FORT_INTERNAL_EVENTS_START(16)

  hash_init(&hash);
  hash_update(&hash, fort_key, sizeof(fort_key));
  hash_update_cvar(&hash);
  hash_final(digest, &hash);

  // Forget hash

  memset(&hash, 0, sizeof(hash));

  FORT_INTERNAL_EVENTS_END(17)
}

#define FORT_PSEUDO_LIMIT 1048576

// use 
// $ ./newressu --fort -x -w32 -s2 -l2000 --space | more
// to debug...

// choose one of
//     60 (1 minute)
//     300 (5 minutes)
//     600 (10 minutes)
//     900 (15 minutes)
//     1200 (20 minutes)
//     1800 (30 minutes)
//     3600 (hour)
//     7200 (2 hours)
//     10800 (3 hours)
//     14400 (4 hours)
//     21600 (6 hours)
//     28800 (8 hours)
//     43200 (12 hours)
//     86400 (24 hours)
// to get readable report..

#define FORT_SECONDS_BETWEEN_SAVES 10 * 60
static IUTIME fort_next_save = 0;

#define TIMEFORMAT "%Z%Y%m%d%H%M%S"

void fort_save();

static char current_timezone[10];

#define aDEBUG22 2

void fort_reseed(int len, unsigned char *buf)
{
  HashCtx hash;

  FORT_INTERNAL_EVENTS_START(22)
  
  hash_init(&hash);
  hash_update(&hash, fort_key, sizeof(fort_key));
  hash_update_cvar(&hash);
  hash_update(&hash, buf, len);

#ifdef DEBUG22
  fprintf(stdout,"\nprevkey   ");
  for(int d = 0; d < sizeof(fort_key); d++)
    fprintf(stdout,"%02x", fort_key[d]);
  fprintf(stdout,"\ncvar      ");
  for(int d = 0; d < cvarsize + 1; d++)
    fprintf(stdout,"%02x", cvar[d]);
  fprintf(stdout,"\ndata      ");
  for(int d = 0; d < HashLen; d++)
    fprintf(stdout,"%02x", buf[d]);
  fprintf(stdout,"\ndata asc  ");
  for(int d = 0; d < len; d++) {
    if(isprint(buf[d]))
      fprintf(stdout,"%c", buf[d]);
    else
      fprintf(stdout,"\\%02x", buf[d]);
  }
#endif
  
  hash_final(fort_key, &hash);

#ifdef DEBUG22
  fprintf(stdout,"\nnewkey    ");
  for(int d = 0; d < sizeof(fort_key); d++)
    fprintf(stdout,"%02x", fort_key[d]);
  fprintf(stdout,"\n");
#endif
  
  memset(&hash, 0, sizeof(hash));

  if(fort_next_save != 0) {
    IUTIME seconds;
    seconds = getseconds();
    
    char timezone[10];
    strftime(timezone, sizeof(timezone), "%Z",
             localtime((time_t *)&seconds) );
    if(strcmp(current_timezone, timezone)) {
      fort_next_save = 1;
      strcpy(current_timezone, timezone);
    }
    
    if(fort_next_save != 0 &&
       seconds >= fort_next_save) {
      
      IUTIME diff=
        timegm(localtime((time_t *)&seconds)) - seconds;
      
      fort_next_save = seconds -
        ((seconds + diff) %
         FORT_SECONDS_BETWEEN_SAVES) +
        FORT_SECONDS_BETWEEN_SAVES;
      
      fort_save();
    }
  }

  FORT_INTERNAL_EVENTS_END(23)
}

static FORT_COUNTER fort_reseed_count = 0;
static IUTIME fort_next_reseed = 0;

#define aDEBUG28 2

#ifdef FORT_ORIGINAL_VERSION

void fort_pseudo_random_data(int len, 
    unsigned char *buf)
{
  unsigned char digest[HashLen];
  unsigned int n, blockbytes;
#ifdef DEBUG28
  int d = 0;
#endif

  FORT_INTERNAL_EVENTS_START(18)

  blockbytes = 0;

  while(len != 0) {
    FORT_INTERNAL_EVENTS_START(19)

    fort_rekey(digest);
    n = (len < HashLen) ? len : HashLen;

#ifdef DEBUG28
    
    fprintf(stdout,"\nbuf(%02x):  ",d++);
    for(int c=0;c<n;c++)
      fprintf(stdout,"%02x",buf[c]);
    fprintf(stdout,"\ndigest:   ");
    for(int c=0;c<n;c++)
      fprintf(stdout,"%02x",digest[c]);
    fprintf(stdout,"\nsum:      ");
    for(int c=0;c<n;c++)
      fprintf(stdout,"%02x",buf[c]^digest[c]);
    fprintf(stdout,"\n");

#endif // #ifdef DEBUG18

    memcpy(buf, digest, n);

    buf += n;
    len -= n;
    blockbytes += n;

    // rekey every 1048576 bytes if data left

    if(blockbytes >= FORT_PSEUDO_LIMIT &&
        len > 0) {                                                                                                              
      fort_rekey(fort_key);
      blockbytes = 0;
    }

    FORT_INTERNAL_EVENTS_END(20)
  }

  fort_rekey(fort_key);

  // Forget last bytes

  memset(digest, 0, sizeof(digest));

  FORT_INTERNAL_EVENTS_END(21)
}

#define aDEBUG34 2

void fort_random_data(int len, unsigned char *buf)
{
  int c;
  IUTIME tenths;
  HashCtx hash;
  unsigned char digest[HashLen];

  FORT_INTERNAL_EVENTS_START(24)

  tenths = gettenths();

  if( (fort_next_reseed == 0) ||
      (fort_next_reseed <= tenths &&
      fort_pools[0].length >=
      FORT_MIN_POOL_SIZE) ) {

    fort_mix();
    
    // next tenth of a second

    fort_next_reseed = tenths + 1;
    fort_reseed_count++;

    hash_init(&hash);
    c = 0;

    while(c < FORT_POOLS && (fort_reseed_count
        % (1 << c)) == 0) {

      FORT_INTERNAL_EVENTS_START(25)

      hash_final(digest, &fort_pools[c].pool);

#ifdef DEBUG34
      fprintf(stdout,"\ndata   %2d ",c);
      for(int d = 0; d < HashLen; d++)
	fprintf(stdout,"%02x",digest[d]);
#endif

      hash_update(&hash, digest, sizeof(digest));
      fort_pools[c].length = 0;
      HashInit(&fort_pools[c].pool);

      // save earlier pool to new one

      hash_update(&fort_pools[c].pool,
        digest, sizeof(digest));
      c++;

      FORT_INTERNAL_EVENTS_END(26)
    }
    hash_update_cvar(&hash);
    hash_final(digest, &hash);

#ifdef DEBUG34
    fprintf(stdout,"\ndigest    ");
    for(int d = 0; d < HashLen; d++)
      fprintf(stdout,"%02x", digest[d]);
    fprintf(stdout,"\n");
#endif

    fort_reseed(sizeof(digest), digest);

    // Forget hash context

    memset(&hash, 0, sizeof(hash));

    // Forget reseed key

    memset(digest, 0, sizeof(digest));
  }

  fort_pseudo_random_data(len, buf);

  FORT_INTERNAL_EVENTS_END(27)
}

#endif // #ifdef FORT_ORIGINAL_VERSION

#ifdef FORT_XOR_VERSION

void fort_pseudo_random_data_xor(int len, 
    unsigned char *buf)
{
  unsigned char digest[HashLen];
  unsigned int n, blockbytes;
#ifdef DEBUG28
  int d = 0;
#endif

  FORT_INTERNAL_EVENTS_START(18)

  blockbytes = 0;

  while(len != 0) {
    FORT_INTERNAL_EVENTS_START(19)
      
    fort_rekey(digest);
    n = (len < HashLen) ? len : HashLen;

#ifdef DEBUG28

    fprintf(stdout,"\nbuf(%02x):  ", d++);
    for(int c = 0; c < n; c++)
      fprintf(stdout,"%02x", buf[c]);
    fprintf(stdout,"\ndigest:   ");
    for(int c = 0; c < n; c++)
      fprintf(stdout,"%02x", digest[c]);
    fprintf(stdout,"\nsum:      ");
    for(int c = 0; c < n; c++)
      fprintf(stdout,"%02x", buf[c] ^ digest[c]);
    fprintf(stdout,"\n");

#endif
    
    for(int c = 0; c < n; c++)
      buf[c] ^= digest[c];

    buf += n;
    len -= n;
    blockbytes += n;

    // rekey every 1048576 bytes if data left

    if(blockbytes >= FORT_PSEUDO_LIMIT &&
        len > 0) {                                                                                                              
      fort_rekey(fort_key);
      blockbytes = 0;
    }

    FORT_INTERNAL_EVENTS_END(20)
  }

  fort_rekey(fort_key);

  // Forget last bytes

  memset(digest, 0, sizeof(digest));

  FORT_INTERNAL_EVENTS_END(21)
}

void fort_random_data_xor(int len, unsigned char *buf)
{
  int c;
  IUTIME tenths;
  HashCtx hash;
  unsigned char digest[HashLen];

  FORT_INTERNAL_EVENTS_START(24)

  tenths = gettenths();

  if( (fort_next_reseed == 0) ||
      (fort_next_reseed <= tenths &&
      fort_pools[0].length >=
      FORT_MIN_POOL_SIZE) ) {

    fort_mix();

    // next tenth of a second

    fort_next_reseed = tenths + 1;
    fort_reseed_count++;

    hash_init(&hash);
    c = 0;

    while(c < FORT_POOLS && (fort_reseed_count
        % (1 << c)) == 0) {

      FORT_INTERNAL_EVENTS_START(25)

      hash_final(digest, &fort_pools[c].pool);

#ifdef DEBUG34
      fprintf(stdout,"\ndata   %2d ", c);
      for(int d = 0; d < HashLen; d++)
	fprintf(stdout,"%02x", digest[d]);
#endif
      
      hash_update(&hash, digest, sizeof(digest));
      fort_pools[c].length = 0;
      HashInit(&fort_pools[c].pool);

      // save earlier pool to new one

      hash_update(&fort_pools[c].pool,
        digest, sizeof(digest));
      c++;

      FORT_INTERNAL_EVENTS_END(26)
    }
    hash_update_cvar(&hash);
    hash_final(digest, &hash);

#ifdef DEBUG34
    fprintf(stdout,"\ndigest    ");
    for(int d = 0; d < HashLen; d++)
      fprintf(stdout,"%02x", digest[d]);
    fprintf(stdout,"\n");
#endif
    
    fort_reseed(sizeof(digest), digest);

    // Forget hash context

    memset(&hash, 0, sizeof(hash));

    // Forget reseed key

    memset(digest, 0, sizeof(digest));
  }

  fort_pseudo_random_data_xor(len, buf);

  FORT_INTERNAL_EVENTS_END(27)
}

#endif // #ifdef FORT_XOR_VERSION

#define FORTCNT 128

static unsigned int fort_cnt = FORTCNT;
static unsigned char fort_bytes[FORTCNT];
static int fort_byte = 999999999;

int fort_random_data_byte()
{
  if(fort_byte >= fort_cnt) {
    fort_random_data(fort_cnt, fort_bytes);
    fort_byte = 0;
  }
  return(fort_bytes[fort_byte++]);
}

void fort_clear()
{
  memset(fort_bytes, 0, fort_cnt);
  fort_byte = 999999998;
}

int fort_random_data_byte_limit(int limit)
{
  int c;

  while((c = fort_random_data_byte())>=
      (256 / limit) * limit);

  return(c % limit);
}

void fort_random_data_buffer(int size,
    unsigned char *buffer)
{
  int c;

  for(c = 0; c < size; c++)
    buffer[c] ^= fort_random_data_byte();
}

#define aDEBUG53 2

void fort_reseed_file(unsigned char *filename)
{
  int c, cnt, bytes, characters;
#ifdef DEBUG53
  unsigned char *p;
#endif
  FILE *fp1;
  unsigned char buffer[1024], digest[HashLen];

  HashCtx hash;

  bytes = 0;
  characters = 0;
  
  if((fp1 = fopen(filename, "r")) != NULL) {
    HashInit(&hash);
    while(fgets(buffer, sizeof(buffer), fp1) != NULL) {
      cnt = strlen(buffer);
      HashUpdate(&hash, buffer, cnt);
      bytes += cnt;
      for(c = 0; c < cnt; c++) {
	if(buffer[c] < 0x80 || buffer[c] > 0xbf)
	  characters++;
      }
#ifdef DEBUG53
      p = buffer;
      while(*p != '\0') {
	if(*p == '\\')
	  fprintf(stdout, "\\\\");
	else if(isprint(*p) || *p == '\n')
	  fputc(*p, stdout);
	else
	  fprintf(stdout,"\\%02x", *p);
	p++;
      }
#endif
    }
    hash_update_cvar(&hash);
    HashFinal(digest, &hash);

    fort_reseed(sizeof(digest), digest);

    fclose(fp1);
  }
#ifdef DEBUG53
  //if(verbose) {
  fprintf(stdout,"fort_reseed_file %s", filename);
  fprintf(stdout,", %d bytes read", bytes);
  fprintf(stdout,", %d characters read", characters);
  fprintf(stdout,", sha256: ");
  for(int c = 0; c < HashLen; c++) {
    fprintf(stdout,"%02x", digest[c]);
  }
  fprintf(stdout,"\n");
  //}
#endif
}

#define FORT_SAVE_SIZE 1024

#define aDEBUG57

void fort_save()
{
  FILE *fp1;
  unsigned char buffer[FORT_SAVE_SIZE];

  memset(buffer, 0, sizeof(buffer));

  if((fp1 = fopen(fort_random_file, "w")) != NULL) {
    fort_pseudo_random_data(sizeof(buffer), buffer);

#ifdef DEBUG57
    for(int c = 0; c < sizeof(buffer); c++) {
      if(c % 32 == 0) {
	if(c != 0)
	  fprintf(stdout,"\n");
	fprintf(stdout,"save      %05d ", c);
      }
      fprintf(stdout," %02x", buffer[c]);
    }
    fprintf(stdout,"\n");
#endif
    fwrite(buffer, 1, sizeof(buffer), fp1);
    memset(buffer, 0, sizeof(buffer));
    fclose(fp1);
  }
}

void fort_restore()
{
  int d;
#ifdef DEBUG57
  int random;
#endif
  FILE *fp1;
  unsigned char buffer[FORT_SAVE_SIZE];

  memset(buffer, 0, sizeof(buffer));
  
  if((fp1 = fopen(fort_random_file, "r"))
      != NULL) {
    d = fread(buffer, 1, sizeof(buffer), fp1);
    fclose(fp1);
#ifdef DEBUG57
    random = 0;
#endif
  } else {
    fort_pseudo_random_data(sizeof(buffer), buffer);
    ressu_genbytes(sizeof(buffer), buffer);
    d = sizeof(buffer);
#ifdef DEBUG57
    random = 1;
#endif
  }
#ifdef DEBUG57
  for(int c = 0; c < sizeof(buffer); c++) {
    if(c % 32 == 0) {
      if(c != 0)
	fprintf(stdout,"\n");
      fprintf(stdout,"restore");
      if(random)
	fprintf(stdout,"*");
      else
	fprintf(stdout," ");
      fprintf(stdout,"  %05d ", c);
    }
    fprintf(stdout," %02x", buffer[c]);
  }
  fprintf(stdout,"\n");
#endif
  fort_reseed(d, buffer);
  memset(buffer, 0, sizeof(buffer));

  fort_save();
}

#if defined FORT_USE_URANDOM || \
    defined FORT_USE_RANDOM

static void fort_readfile_xor(int len,
    unsigned char *buf,
    unsigned char *filename)
{
  int c, n, n2;
  unsigned char temp[64];
  FILE *fp1;

  if((fp1 = fopen(filename, "rb"))
      != NULL) {
    while(len != 0) {
      n = (len < sizeof(temp)) ?
          len : sizeof(temp);
      n2 = fread(temp, 1, n, fp1);
      for(c = 0; c < n2; c++)
        buf[c] ^= temp[c];
      len -= n2;
      buf += n2;
    }
    fclose(fp1);
  }
}

#endif

#ifdef FORT_USE_WEB

static void parse_string(unsigned char *string, int size, unsigned char **p2)
{
  int count;
  unsigned char *p, *q;

  p = *p2;
  q = string;
  count = 0;

  // uppercase & lowercase letters, '.'
  // or utf-8

  while(isalnum(*p) || *p == '.' || *p > 0x80) {
    if(++count < size)
      *q++ = *p;
    p++;
  }
  *q = '\0';
  *p2 = p;
}

#endif // #ifdef FORT_USE_WEB

#ifdef FORT_USE_WEB

static int check_string(unsigned char *string, unsigned char *p)
{
  int ok;

  ok = 0;
  if(!strncmp(string, p, strlen(string))) {
    ok = 1;
  }

  return(ok);
}

#endif // #ifdef FORT_USE_WEB

void fort_internal_random_data_1(int size, char *buf)
{
  ressu_genbytes(size, buf);
#ifdef FORT_XOR_VERSION
  fort_pseudo_random_data_xor(size, buf);
#endif
}

void fort_internal_random_data_2(int size, char *buf)
{
#ifdef FORT_XOR_VERSION
  fort_pseudo_random_data_xor(size, buf);
#else
  fort_pseudo_random_data(size, buf);
#endif
  ressu_genbytes(size, buf);
}

void fort_internal_random_data_3(int size, char *buf)
{
#ifdef FORT_XOR_VERSION
  fort_random_data_xor(size, buf);
#else
  fort_random_data(size, buf);
#endif
}

static IUTIME fort_next_localmix = 0;
//#define FORT_SECONDS_BETWEEN_LOCALMIXES 60*60
#define FORT_SECONDS_BETWEEN_LOCALMIXES 60*60

#ifdef FORT_USE_WEB  
static IUTIME fort_next_webmix = 0;
//#define FORT_SECONDS_BETWEEN_WEBMIXES 12*3600
#define FORT_SECONDS_BETWEEN_WEBMIXES 12*3600
#endif

#define RANDOMNESS_IN_ALPHABETS 2

#define DEBUG65 2

void fort_strrepl(unsigned char *dest,unsigned char *oldstring, unsigned char *newstring)
{
  unsigned char *stringstart;
#ifdef DEBUG65
  if(verbose) {  
    fprintf(stdout,"fort_strrepl(): oldstring:%s", oldstring);
    fprintf(stdout,", newstring:%s", newstring);
    fprintf(stdout,", olddest:%s", dest);
  }
#endif
  if((stringstart = strstr(dest, oldstring)) != NULL) {
    memmove(stringstart + strlen(newstring),
	    stringstart + strlen(oldstring),
	    strlen(stringstart + strlen(oldstring)) + 1);
    memmove(stringstart, newstring, strlen(newstring));
  }
#ifdef DEBUG65
  if(verbose) {  
    fprintf(stdout,", newdest:%s", dest);
    fprintf(stdout,"\n");
  }
#endif
}

void fort_mix()
{
  int webmix, localmix;
  unsigned char temp[64];

  IUTIME seconds;

  seconds = getseconds();
  
  webmix = 0;
  localmix = 0;

#ifdef FORT_USE_WEB  
  if(fort_next_webmix == 0 ||
     fort_next_webmix <= seconds) {
    fort_next_webmix = seconds + FORT_SECONDS_BETWEEN_WEBMIXES;
    fort_next_localmix = seconds + FORT_SECONDS_BETWEEN_LOCALMIXES;
    webmix = 1;
    localmix = 1;
  }
#endif
  
  if(fort_next_localmix == 0 ||
     fort_next_localmix <= seconds) {
    fort_next_localmix = seconds + FORT_SECONDS_BETWEEN_LOCALMIXES;
    localmix = 1;
  }

  if(!webmix && !localmix)
    return;
  
  if(verbose) {  
    char timebuf[128];
    strftime(timebuf, sizeof(timebuf), TIMEFORMAT,
	     localtime((time_t *)&seconds));
    fprintf(stdout,"Fort mix time:      %s\n", timebuf);

#ifdef FORT_USE_WEB
    if(fort_use_web) {
      strftime(timebuf, sizeof(timebuf), TIMEFORMAT,
	       localtime((time_t *)&fort_next_webmix));
      fprintf(stdout,"Next fort webmix:   %s\n", timebuf);
    }
#endif

    strftime(timebuf, sizeof(timebuf), TIMEFORMAT,
	     localtime((time_t *)&fort_next_localmix));
    fprintf(stdout,"Next fort localmix: %s\n", timebuf);
  }
  
  fort_internal_random_data_1(sizeof(temp), temp);
  fort_reseed(sizeof(temp), temp);
  
#if defined FORT_USE_WEB || \
  defined USE_RDRAND || \
  defined USE_RDSEED || \
  defined USE_NEWRESSU_COMMAND
  unsigned char digest[HashLen];
#endif    
  
#ifdef FORT_USE_WEB
  if(webmix && fort_use_web) {
    char *webpages[] = {
      "https://moijari.com:5001/",
      "https://moijari.com:5005/",
      "https://moijari.com:5006/",
    };

    for(int c = 0; c < sizeof(webpages) / sizeof(webpages[0]); c++) {
      unsigned char *p;
      
      unsigned char scheme[32];
      unsigned char host[32];
      unsigned char port[10];
      unsigned char rest[128];
      
      p = webpages[c];
      parse_string(scheme, sizeof(scheme), &p);
      
      if(check_string("://", p)) {
	p += 3;
	parse_string(host, sizeof(host), &p);
      } else {
	strcpy(host, scheme);
	strcpy(scheme,"http");
      }
      
      if(check_string(":", p)) {
	p++;
	parse_string(port, sizeof(port), &p);
      } else {
	strcpy(port,"80");
      }
      
      if(*p != '/') 
	strcpy(rest, "/");
      else
	strcpy(rest, p);
      
      if(verbose) {    
	fprintf(stdout,"URL:%s ", webpages[c]);
	fprintf(stdout,"[scheme:%s]", scheme);
	fprintf(stdout,"[host:%s]", host);
	fprintf(stdout,"[port:%s]", port);
	fprintf(stdout,"[rest:%s]", p);
	fflush(stdout);
      }
      
      if(!strcmp(scheme,"http")) {
	fort_hash_http_page(host, port, p, digest);
      } else if(!strcmp(scheme,"https")) {
	fort_hash_https_page(host, port, p, digest);
      }
      fort_reseed(sizeof(digest), digest);
    }
  }
#endif // #ifdef FORT_USE_WEB
    
  if(localmix) {    
#ifdef FORT_USE_URANDOM
    memset(temp, 0, sizeof(temp));
    fort_readfile_xor(sizeof(temp), temp,
		      "/dev/urandom");
    fort_reseed(sizeof(temp), temp);
#endif
  
#ifdef FORT_USE_RANDOM
    memset(temp, 0, sizeof(temp));
    fort_readfile_xor(sizeof(temp), temp,
		      "/dev/random");
    fort_reseed(sizeof(temp), temp);
#endif

#if defined USE_RDRAND || \
  defined USE_RDSEED
    HashCtx hash;
    unsigned char digest[HashLen];
#endif
    
#ifdef USE_RDRAND

    memset(temp, 0, sizeof(temp));
    if(rdrand_bytes(sizeof(temp), temp)) {
      
      HashInit(&hash);
      
      hash_update_cvar(&hash);
      hash_update_ressu(&hash);

      HashUpdate(&hash, temp, sizeof(temp));
      HashFinal(digest, &hash);
      
      if(verbose) {
	fprintf(stdout,", data: ");
	for(int c = 0; c < sizeof(temp); c++) {
	  fprintf(stdout,"%02x", temp[c]);
	}
	fprintf(stdout,", sha256: ");
	for(int c = 0; c < HashLen; c++) {
	  fprintf(stdout,"%02x", digest[c]);
	}
	fprintf(stdout,"\n");
	fflush(stdout);

	newressu_output = 0;
      }
      
      fort_reseed(sizeof(digest), digest);
    }  
    
#endif // #ifdef USE_RDRAND
  
#ifdef USE_RDSEED
  
    memset(temp, 0, sizeof(temp));
    if(rdseed_bytes(sizeof(temp), temp)) {
      
      HashInit(&hash);

      hash_update_cvar(&hash);
      hash_update_ressu(&hash);
      
      HashUpdate(&hash, temp, sizeof(temp));
      HashFinal(digest, &hash);
      
      if(verbose) {
	fprintf(stdout,", data: ");
	for(int c = 0; c < sizeof(temp); c++) {
	  fprintf(stdout,"%02x", temp[c]);
	}
	fprintf(stdout,", sha256: ");
	for(int c = 0; c < HashLen; c++) {
	  fprintf(stdout,"%02x", digest[c]);
	}
	fprintf(stdout,"\n");
	fflush(stdout);

	newressu_output = 0;
      }
      
      fort_reseed(sizeof(digest), digest);
    }

#endif // #ifdef USE_RDSEED

#ifdef FORT_USE_NEWRESSU_COMMAND

    char *commands[] = {
      "$newressu -2 -s8 -w8 -l1 --lineno",
      "$newressu -2 -s8 -w8 -l1 --lineno --fast",
      "$newressu -2 -s8 -w8 -l1 --lineno --urandom",
#ifdef USE_RDRAND
      "$newressu -2 -s8 -w8 -l1 --lineno --rdrand",
#endif
#ifdef USE_RDSEED
      "$newressu -2 -s8 -w8 -l1 --lineno --rdseed",
#endif
      "$newressu -2 -s8 -w8 -l1 --lineno --pseudoressu",
#ifdef RANDOMNESS_IN_ALPHABETS
      "$newressu --cn -s8 -w4 -l1 --lineno --single",
      "$newressu --deu -s8 -w8 -l1 --lineno --single",
      "$newressu --dnk -s8 -w8 -l1 --lineno --single",
      "$newressu --eng -s8 -w8 -l1 --lineno --single",
      "$newressu --est -s8 -w8 -l1 --lineno --single",
      "$newressu --fin -s8 -w8 -l1 --lineno --single",
      "$newressu --fra -s8 -w8 -l1 --lineno --single",
      "$newressu --gbr -s8 -w8 -l1 --lineno --single",
      "$newressu --got -s8 -w8 -l1 --lineno --single",
      "$newressu --grc -s8 -w8 -l1 --lineno --single",
      "$newressu --ita -s8 -w8 -l1 --lineno --single",
      "$newressu --jp -s8 -w4 -l1 --lineno --single",
      "$newressu --jp1 -s8 -w4 -l1 --lineno --single",
      "$newressu --jp2 -s8 -w4 -l1 --lineno --single",
      "$newressu --jp3 -s8 -w4 -l1 --lineno --single",
      "$newressu --kor -s8 -w4 -l1 --lineno --single",
      "$newressu --ltu -s8 -w8 -l1 --lineno --single",
      "$newressu --lva -s8 -w8 -l1 --lineno --single",
      "$newressu --nor -s8 -w8 -l1 --lineno --single",
      "$newressu --rus -s8 -w8 -l1 --lineno --single",
      "$newressu --swe -s8 -w8 -l1 --lineno --single",
#endif
    };

    for(int c = 0; c < sizeof(commands) / sizeof(commands[0]); c++) {
      if(strstr(commands[c], "--fort")) {
	fprintf(stderr,"%s: fort_mix(): cannot call fort recursively \"%s\"\n",
		procname, commands[c]);
      } else {
	unsigned char command[256];
	strcpy(command, commands[c]);

	// string:$newressu, newstring:/bin/newressu2, olddest:$newressu -2 -s8 -w8 -l1 --lineno, newdest:/bin/newressu2 -2 -s8 -w8 -l1 --lineno

	if(strstr(procname,"newressu") && strlen(procname) < 32)
	  fort_strrepl(command, "$newressu", procname);
	else
	  fort_strrepl(command, "$newressu", "/bin/newressu");

	fort_hash_command(command, digest);
	fort_reseed(sizeof(digest), digest);
      }
    }
#endif // #ifdef FORT_USE_NEWRESSU_COMMAND
  }
}

void file_digest(char *filename,unsigned char temp[HashLen])
{
  int c;
  unsigned char buffer[1024];
  FILE *fp1;
  HashCtx ctx;
  
  HashInit(&ctx);
  if((fp1 = fopen(filename, "rb")) != NULL) {
    while((c = fread(buffer, 1, sizeof(buffer), fp1)) > 0)
      HashUpdate(&ctx, buffer, c);
    fclose(fp1);
  }
  HashFinal(temp, &ctx);
}

#define DEBUG88

void fort_init()
{
  int c;
  unsigned char temp32[32];

  memset(temp32, 0, sizeof(temp32));
  memset(fort_bytes, 0, sizeof(fort_bytes));
  memset(fort_key, 0, sizeof(fort_key));
  
  if(verbose) {
    fprintf(stdout,"hash: %s",
	    HashName);
    fprintf(stdout,", pools: %d*%ld", FORT_POOLS,
	    sizeof(struct fort_pool));
    fprintf(stdout,", HashCtx: %ld",
	    sizeof(HashCtx));
    fprintf(stdout,", hashsize: %d",
	    HashLen);
    fprintf(stdout,"\n");
  }
  
  unsigned int save_fort_internal_events;
  save_fort_internal_events = fort_internal_events;
  fort_internal_events = 1;

  fort_next_save = 0;
  
  clearcvar();

  // Initialize buffers

  for(c = 0; c < FORT_POOLS; c++) {
    fort_pools[c].length = 0;
    HashInit(&fort_pools[c].pool);
  }
  
  // first key to fort_key
  
  fort_internal_random_data_1(sizeof(fort_key), fort_key);

#ifdef ADDITIONAL_SECRETS  

  // additional constant randomness from program

  fort_reseed(strlen(copyright), copyright);
  fort_reseed(strlen(programname), programname);

  unsigned char filedigest[HashLen];
  file_digest("/proc/self/exe", filedigest);
  fort_reseed(strlen(filedigest), filedigest);
  
  fort_reseed(strlen(program_secret), program_secret);

  // additional constant randomness
  // from you (one more secret adversary
  // has to guess)

  FILE *fp1;
  if((fp1 = fopen(fort_secret_file, "r")) != NULL) {
    fclose(fp1);
  } else {
    if((fp1 = fopen(fort_secret_file, "w")) != NULL) {
      fprintf(fp1,"Your secret additional constant data to fort:\n");
      fclose(fp1);
      unsigned char temp2[160];
      sprintf(temp2,"%s -2 -l1 >>%s", procname, fort_secret_file);
      system(temp2);
      fprintf(stdout,"File %s written\n", fort_secret_file);
    }
  }
  fort_reseed_file(fort_secret_file);

#endif
  
#ifdef DEBUG10

  FILE *fp1;
  
  // Empty events file

  if((fp1 = fopen(fort_events_file, "w"))!=NULL)
    fclose(fp1);
  event_id = 0;

#endif // #ifdef DEBUG10

  // Mix fort key with web + local
  // random numbers
  
  fort_mix();

  for(c = 0; c < FORT_POOLS; c++) {
    FORT_INTERNAL_EVENTS_START(31)
    fort_internal_random_data_2(sizeof(temp32), temp32);
    hash_update(&fort_pools[c].pool,
      temp32, sizeof(temp32));
    FORT_INTERNAL_EVENTS_END(32)
  }

#ifdef FORT_INTERNAL_EVENTS

  if(fort_internal_events) {

    // Create some internal events

    for(c=0; c<32; c++) {
      FORT_INTERNAL_EVENTS_START(34)
      fort_internal_random_data_3(sizeof(temp32), temp32);
      FORT_INTERNAL_EVENTS_END(35)
    }
  }

#endif // #ifdef FORT_INTERNAL_EVENTS

  fort_reseed_count = 0;
  fort_next_reseed = 0;

  // Reseed fort_key with new events

  fort_internal_random_data_3(sizeof(temp32), temp32);
  fort_reseed(sizeof(temp32), temp32);

  fort_restore();

  // Forget temp

  memset(temp32, 0, sizeof(temp32));

  fort_internal_events = save_fort_internal_events;

  //fort_reseed_count = 0;
  //fort_next_reseed = 0;

  fort_next_save = 1;
}

void fort_version()
{
  fprintf(stderr,"%s", programname); // touch these outside MAIN
  fprintf(stderr,", %s\n", copyright);
}

#define aMAIN 2
#ifdef MAIN

unsigned char *procname;

int main(int argc, char *argv[])
{
  procname = argv[0];
  fort_version();
  fort_init();
  return(0);
}

#endif // #ifdef MAIN

fort.h

/* Perustuu Bruce Schneierin kirjassa esittämään fortuna järjestelmään.
 * Written by Jari Kuivaniemi
 */

#ifndef SHA256_H
#include "sha256.h"
#endif

#define aUSE_RDRAND 2
#define aUSE_RDSEED 2

typedef unsigned long long IUTIME;

extern int fort_verbose;
extern int fort_partial_line;
extern int fort_use_web;

extern unsigned char fort_random_file[128];
extern unsigned char fort_secret_file[128];
extern unsigned char fort_pools_file[128];
#ifdef DEBUG10
static char fort_events_file[128] = "fortevents.deb";
#endif
unsigned char cvar[16];
int cvarsize;

void inccvar();
void clearcvar();
void hash_update_cvar(HashCtx *hash);

void fort_add_random_event(int *pool, int source, int mode, int len, unsigned char *buf);
void fort_add_random_event_timer_start(IUTIME *micros);
void fort_add_random_event_timer_do(int *pool, int source, int mode, IUTIME *millis);
void fort_add_random_event_split(int *pool, int source, int mode, int len, unsigned char *buf,int size);
void fort_add_random_event_time(int *pool, int source, int mode);
void fort_rekey(unsigned char *buf);
void fort_pseudo_random_data(int len,unsigned char *buf);
void fort_pseudo_random_data_xor(int len,unsigned char *buf);
void fort_reseed(int len,unsigned char *buf);
void fort_random_data(int len,unsigned char *buf);
void fort_random_data_xor(int len,unsigned char *buf);
int fort_random_data_byte();
void fort_clear();
int fort_random_data_byte_limit(int limit);
void fort_random_data_buffer(int size, unsigned char *buffer);
void fort_save();
void fort_read_file();
void fort_mix();
void fort_init();
void hash_init(HashCtx *hash);
void hash_update(HashCtx *hash, unsigned char *data, int len);
void hash_final(unsigned char digest[HashLen], HashCtx *hash);

void fort_hash_http_page(unsigned char *host,unsigned char *port, unsigned char *page, unsigned char *hash);
void fort_hash_https_page(unsigned char *host,unsigned char *port, unsigned char *page, unsigned char *hash);

void fort_hash_command(unsigned char *command, unsigned char *hash);

int rdrand_bytes(int buflen, unsigned char *buf);
int rdseed_bytes(int buflen, unsigned char *buf);

#ifdef FORT_INTERNAL_EVENTS
#define FORT_INTERNAL_EVENTS_START(source) \
  IUTIME micros;                           \
  static int \
    pool=0, pool2=0; \
  if(fort_internal_events) { \
    fort_add_random_event_time(&pool, \
    source, fort_internal_event_mode); \
    fort_add_random_event_timer_start(&micros); \
  }
#else
#define FORT_INTERNAL_EVENTS_START(source)
#endif

#ifdef FORT_INTERNAL_EVENTS
#define FORT_INTERNAL_EVENTS_END(source) \
  if(fort_internal_events) \
    fort_add_random_event_timer_do(&pool2, \
      source, fort_internal_event_mode, \
      &micros);
#else
#define FORT_INTERNAL_EVENTS_END(source)
#endif

#ifdef FORT_EVENTS
#define FORT_EVENTS_START(source) \
  IUTIME micros;                           \
  static int \
    pool=0, pool2=0; \
  if(fort_events) { \
    fort_add_random_event_time(&pool, \
    source, fort_event_mode); \
    fort_add_random_event_timer_start(&micros); \
  }
#else
#define FORT_EVENTS_START(source)
#endif

#ifdef FORT_EVENTS
#define FORT_EVENTS_END(source) \
  if(fort_events) \
    fort_add_random_event_timer_do(&pool2, \
      source, fort_event_mode, \
      &micros);
#else
#define FORT_EVENTS_END(source)
#endif

sha256.c

/* Written from SHA256 documents by Jari Kuivaniemi, read "http://en.wikipedia.org/wiki/SHA-2" */
#include <stdio.h>
#include <memory.h>
#include <string.h>

#include "sha256.h"

extern unsigned char *procname;

//#ifdef MAIN // in Makefile

unsigned char *sha_name = "SHA256 v1.0";

void SHA256Init(SHA256_CONTEXT *sha256)
{
  sha256->state[0] = 0x6a09e667;
  sha256->state[1] = 0xbb67ae85;
  sha256->state[2] = 0x3c6ef372;
  sha256->state[3] = 0xa54ff53a;
  sha256->state[4] = 0x510e527f;
  sha256->state[5] = 0x9b05688c;
  sha256->state[6] = 0x1f83d9ab;
  sha256->state[7] = 0x5be0cd19;
  
  sha256->count = 0;
}

IUWORD k256[64] = {
  0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
  0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
  0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
  0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
  0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
  0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
  0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
  0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
};

#define RR32(word,bits) ( ((word) >> (bits)) | ((word) << (32 - (bits))) )
#define RS32(word,bits) ( ((word) >> (bits)) )

void SHA256Transform(IUWORD state[8], IUBYTE buffer[64])
{
  int i;
  
  IUWORD w[64], s0;
  IUWORD a, b, c, d, e, f, g, h;
  IUWORD maj, t2, s1;
  IUWORD ch, t1;
  
  for(i = 0; i < 16; i++) {
    w[i] =
      (IUWORD)buffer[i * 4 + 0] << 24 |
      (IUWORD)buffer[i * 4 + 1] << 16 |
      (IUWORD)buffer[i * 4 + 2] << 8  |
      (IUWORD)buffer[i * 4 + 3];
  }
  
  for(i=16;i<64;i++) {
    s0 = (RR32(w[i - 15], 7)) ^ (RR32(w[i - 15], 18)) ^ (RS32(w[i - 15], 3));
    s1 = (RR32(w[i - 2], 17)) ^ (RR32(w[i - 2], 19)) ^ (RS32(w[i - 2], 10));
    w[i] = w[i - 16] + s0 + w[i - 7] + s1;
  }
  
  a = state[0];
  b = state[1];
  c = state[2];
  d = state[3];
  e = state[4];
  f = state[5];
  g = state[6];
  h = state[7];
  
  for(i=0;i<64;i++) {
    s0 = (RR32(a, 2)) ^ (RR32(a, 13)) ^ (RR32(a, 22));
    maj = (a & b) ^ (a & c) ^ (b & c);
    t2 = s0 + maj;
    s1 = (RR32(e, 6)) ^ (RR32(e, 11)) ^ (RR32(e, 25));
    ch = (e & f) ^ ((~ e) & g);
    t1 = h + s1 + ch + k256[i] + w[i];
    
    h = g;
    g = f;
    f = e;
    e = d + t1;
    d = c;
    c = b;
    b = a;
    a = t1 + t2;
  }
  
  state[0] = state[0] + a;
  state[1] = state[1] + b;
  state[2] = state[2] + c;
  state[3] = state[3] + d;
  state[4] = state[4] + e;
  state[5] = state[5] + f;
  state[6] = state[6] + g;
  state[7] = state[7] + h;
}

void SHA256Update(SHA256_CONTEXT *sha256, unsigned char *data, int len)
{
  int i, j;
  
  j = (int)sha256->count & 63;
  
  sha256->count += len;
  
  if((j + len) > 63) {
    memcpy(&sha256->buffer[j], data, 64 - j); /* last bytes of next block */
    
    SHA256Transform(sha256->state, sha256->buffer);
    for (i = 64 - j ; i + 63 < len; i += 64) {
      SHA256Transform(sha256->state, &data[i]);
    }
    j = 0;
  } else
    i = 0;
  
  memcpy(&sha256->buffer[j], &data[i], len - i);
}

void SHA256Final(unsigned char digest[32], SHA256_CONTEXT *sha256)
{
  int i, j;
  unsigned char filelenght[8];
  IULONG count;
  
  count = sha256->count << 3;
  SHA256Update(sha256, "\200", 1);
  
  while((sha256->count & 63) != 56)
    SHA256Update(sha256, "\0", 1);
  
  filelenght[0] = (unsigned char)(count >> 56) & 0xff;
  filelenght[1] = (unsigned char)(count >> 48) & 0xff;
  filelenght[2] = (unsigned char)(count >> 40) & 0xff;
  filelenght[3] = (unsigned char)(count >> 32) & 0xff;
  filelenght[4] = (unsigned char)(count >> 24) & 0xff;
  filelenght[5] = (unsigned char)(count >> 16) & 0xff;
  filelenght[6] = (unsigned char)(count >> 8 ) & 0xff;
  filelenght[7] = (unsigned char)(count & 0xff);
  
  SHA256Update(sha256, filelenght, sizeof(filelenght));
  
  for(i = 0, j = 0; i < 8; i++) {
    digest[j++] = (unsigned char) (sha256->state[i] >> 24) & 0xff;
    digest[j++] = (unsigned char) (sha256->state[i] >> 16) & 0xff;
    digest[j++] = (unsigned char) (sha256->state[i] >> 8) & 0xff;
    digest[j++] = (unsigned char) (sha256->state[i] ) & 0xff;
  }
}

#ifdef MAIN

void printhex(char *name, unsigned char *digest, int len)
{
  int c;

  fprintf(stdout,"%s=", name);
  for(c = 0; c < len; c++) {
    fprintf(stdout,"%02x", digest[c]);
  }
  fflush(stdout);
}

int shahexdigit(int c)
{
  if(c >= '0' && c <= '9') return(c - '0');
  else if(c >= 'a' && c <= 'f') return(c - 'a' + 10);
  else if(c >= 'A' && c <= 'F') return(c - 'A' + 10);
  return(0);
}

void sha_test()
{
  int c, d, e, ok, allok;
  SHA256_CONTEXT sha256;
  unsigned char result[32], *p;
  
  fprintf(stdout,"%s", sha_name);
  fprintf(stdout,", Context size: %ld", sizeof(SHA256_CONTEXT));
  fprintf(stdout,", Hash size: 32");
  fprintf(stdout,", IUBYTE size: %ld", sizeof(IUBYTE));
  fprintf(stdout,", IUWORD size: %ld", sizeof(IUWORD));
  fprintf(stdout,", IULONG size: %ld", sizeof(IULONG));

  struct test {
    unsigned char *string;
    int count;
    unsigned char *result;
  } tests[] = {
    { "abc", 1, "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad" },
    { "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", 1, "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1" },
    { "a", 1000000, "cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0" },
    { "The quick brown fox jumps over the lazy dog", 1, "d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592" },
  };

  allok = 1;
  for(c = 0; c < sizeof(tests) / sizeof(tests[0]); c++) {
    SHA256Init(&sha256);
    for(d = 0; d < tests[c].count; d++)
      SHA256Update(&sha256, tests[c].string, strlen(tests[c].string));
    SHA256Final(result, &sha256);
    p = tests[c].result;
    d = 0;
    ok = 1;
    while(*p != '\0') {
      if(*p != '\0')
	e = shahexdigit(*p++);
      if(*p!='\0')
	e = e * 16 + shahexdigit(*p++);
      if(e != result[d])
	ok = 0;
      d++;
    }
    if(ok) {
      fprintf(stdout,"\nsha256 test ");
      fprintf(stdout,"string: %s", tests[c].string);
      fprintf(stdout,", count: %d, ", tests[c].count);
      printhex("tmp32", result, sizeof(result));
      fprintf(stdout,", result: %s", tests[c].result);
      fprintf(stdout," ok!!!!");
      fflush(stdout);
    } else {
      fprintf(stdout,"\nsha256 test ");
      fprintf(stdout,"string: %s", tests[c].string);
      fprintf(stdout,", count: %d, ", tests[c].count);
      printhex("tmp32", result, sizeof(result));
      fprintf(stdout,", result: %s", tests[c].result);
      fprintf(stdout," failed!!!!");
      fflush(stdout);
      allok = 0;
    }
  }
  if(allok) {
    fprintf(stdout,", sha256 tests ok!!!!\n");
  } else {
    fprintf(stdout,", sha256 tests failed!!!!\n");
  }
  fflush(stdout);
}

int main(int argc, char *argv[])
{
  sha_test();
}

#endif

sha256.h

#ifndef SHA256_H
#define SHA256_H

#define HashName   "SHA256"
#define HashInit   SHA256Init
#define HashUpdate SHA256Update
#define HashFinal  SHA256Final
#define HashLen    32
#define HashCtx    SHA256_CONTEXT

typedef unsigned char IUBYTE;
typedef unsigned int IUWORD;
typedef unsigned long long IULONG;

typedef struct {
    IUWORD state[8];
    IULONG count;
    IUBYTE buffer[64];
} SHA256_CONTEXT;

void SHA256Init(SHA256_CONTEXT *sha256);
void SHA256Update(SHA256_CONTEXT* sha256, unsigned char *data, int len);
void SHA256Final(unsigned char digest[32], SHA256_CONTEXT *sha256);

void sha_test();

#endif

intelrandom.c

#include <stdio.h>
#include <memory.h>

#include "fort.h"
#include "newressu.h"

#if defined USE_RDRAND ||			\
    defined USE_RDSEED

// see: https://software.intel.com/content/www/us/en/develop/articles/intel-digital-random-number-generator-drng-software-implementation-guide.html

void _cpuid(unsigned int leaf, unsigned int subleaf,
	    unsigned int *a, unsigned int *b, unsigned int *c, unsigned int *d)
{
  asm volatile("cpuid"
	       : "=a" (*a), "=b" (*b), "=c" (*c), "=d" (*d)
	       : "a" (leaf), "c" (subleaf) );
}

int _is_cpu_vendor(unsigned char *cpuvendor)
{
  int ok = 0;
  unsigned int a, b, c, d;

  _cpuid(0, 0, &a, &b, &c, &d);

  if(memcmp((char *)(&b), cpuvendor,4) == 0 &&
     memcmp((char *)(&d), cpuvendor+4,4) == 0 &&
     memcmp((char *)(&c), cpuvendor+8,4) == 0)
    ok = 1;

  return(ok);
}

#endif

#ifdef USE_RDRAND

int _has_rdrand()
{
  int ok = 0;
  unsigned int a, b, c, d;

  _cpuid(1, 0, &a, &b, &c, &d);
  if((c & 0x40000000) == 0x40000000) {
    ok = 1;
  }

  return(ok);
}

int _rdrand_long(unsigned long *therand)
{
  unsigned char ret;

  asm volatile("rdrand %0; setc %1"
	       : "=r" (*therand), "=qm" (ret) );

  return(int) ret;
}

int rdrand_bytes(int buflen, unsigned char *buf)
{
  int c, n, ret = 0;
  unsigned long l;

  if(verbose) {
    if(newressu_output == 1)
      fprintf(stdout,"\n");
    newressu_output = 0;
  }
  
  if(_is_cpu_vendor("GenuineIntel") && _has_rdrand()) {
    if(verbose)
      fprintf(stdout,"Intel rdrand");
    ret = 1;
  } else if(_is_cpu_vendor("AuthenticAMD") && _has_rdrand()) {
    if(verbose)
      fprintf(stdout,"AMD rdrand");
    ret = 1;
  }
  
  if(ret) {
    while(buflen > 0) {
      int tries = 0;

      while(++tries < 100) {
	if((ret = _rdrand_long(&l)) == 1) { // 1 ok, 0 fail
	  break;
	}
	//fprintf(stdout," %016lu",l);
      }

      if(verbose) {
	fprintf(stdout,", t:%d ", tries);
	newressu_output = 1;
      }

      if(ret == 0)
	break;

      n = (buflen < sizeof(l) ? buflen : sizeof(l));

      if(verbose) {
	for(c = 0; c < sizeof(l); c++)
	  fprintf(stdout,"%02x", ((unsigned char *)&l)[c]);
	newressu_output = 1;
      }

      memcpy(buf, (unsigned char *)&l, n);

      buf += n;
      buflen -= n;
    }
  }

  if(verbose && newressu_output) {
    fprintf(stdout, "\n");
    newressu_output = 0;
  }

  return(ret);
}

#endif // #ifdef USE_RDRAND

#ifdef USE_RDSEED

int _has_rdseed()
{
  int ok = 0;
  unsigned int a, b, c, d;

  _cpuid(7, 0, &a, &b, &c, &d);
  if((b & 0x40000) == 0x40000) {
    ok = 1;
  }

  return(ok);
}

int _rdseed_long(unsigned long *therand)
{
  unsigned char ret;

  asm volatile("rdseed %0; setc %1"
	       : "=r" (*therand), "=qm" (ret) );

  return(int) ret;
}

int rdseed_bytes(int buflen, unsigned char *buf)
{
  int c, n, ret = 0;
  unsigned long l;

  if(verbose) {
    if(newressu_output == 1)
      fprintf(stdout,"\n");
    newressu_output = 0;
  }
  
  if(_is_cpu_vendor("GenuineIntel") && _has_rdseed()) {
    if(verbose)
      fprintf(stdout,"Intel rdseed");
    ret = 1;
  } else if(_is_cpu_vendor("AuthenticAMD") && _has_rdseed()) {
    if(verbose)
      fprintf(stdout,"AMD rdseed");
    ret = 1;
  }

  if(ret) {
    while(buflen > 0) {
      int tries = 0;

      while(++tries < 1000) {
	if((ret = _rdseed_long(&l)) == 1) { // 1 ok, 0 fail
	  break;
	}
	//fprintf(stdout," %lu",l);
      }

      if(verbose) {
	fprintf(stdout,", t:%d ", tries);
      }

      if(ret == 0)
	break;

      n = (buflen < sizeof(l) ? buflen : sizeof(l));

      if(verbose) {
	for(c = 0; c < sizeof(l); c++)
	  fprintf(stdout,"%02x",((unsigned char *)&l)[c]);
	newressu_output = 1;
      }

      memcpy(buf, (unsigned char *)&l, n);

      buf += n;
      buflen -= n;
    }
  }

  if(verbose && newressu_output) {
    fprintf(stdout,"\n");
    newressu_output = 0;
  }
  
  return(ret);
}

#endif // #ifdef USE_RDSEED

webrandom.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <memory.h>
#include <errno.h>

#include <openssl/ssl.h>
#include <openssl/err.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>

#include "sha256.h"
#include "fort.h"

extern unsigned char *procname;
extern int newressu_output; 
extern int verbose;

#include <stdarg.h>

void dbs_printf(unsigned char **buf, size_t *buf_length, const unsigned char *format, ...)
{
  int count;
  va_list args;;
  
  va_start(args, format);
  count = vsnprintf(*buf, *buf_length, format, args) + 1;
  va_end(args);
  if(*buf_length < count) {
    *buf_length = count;
    *buf = realloc(*buf, *buf_length);
    va_start(args, format);
    count = vsnprintf(*buf, *buf_length, format, args) + 1;
    va_end(args);
  }
}

int fort_connect(unsigned char *host, unsigned char *port)
{
  struct addrinfo hints, *res, *resp;
  int s, status;
  
  memset(&hints, 0, sizeof(hints));
  hints.ai_family = AF_UNSPEC;
  hints.ai_socktype = SOCK_STREAM;
  hints.ai_flags = AI_PASSIVE;
  
  if((status = getaddrinfo(host, port, &hints, &res)) != 0) {
    fprintf(stderr,"\n%s: getaddrinfo", procname);
    fprintf(stderr,", status %d", status);
    fprintf(stderr,", gai_strerror(): %s", gai_strerror(status));
    fprintf(stderr,", errno %d\n", errno);
    fflush(stderr);
  }
  
  for(resp = res; resp != NULL; resp = resp->ai_next) {
    if((s = socket(resp->ai_family, resp->ai_socktype, resp->ai_protocol)) < 0)
      continue;
    if(connect(s, resp->ai_addr, resp->ai_addrlen) == 0)
      break;
    close(s);
  }

  freeaddrinfo(res);

  return(s);
}

#define aDEBUG27 2

void fort_hash_http_page(unsigned char *host,unsigned char *port, unsigned char *page, unsigned char *hash) // 202011 JariK
{
  int c, cnt, s, status, bytes, characters;

  if((s = fort_connect(host, port))<0) {
    fprintf(stderr,"\n%s: cannot fort_connect()", procname);
    fprintf(stderr,", status: %d", s);
    fprintf(stderr,", errno: %d" , errno);
    perror("fort_connect");
    fflush(stderr);
  }

  // GET / HTTP/1.0\r\nHost: moijari.com:5006\r\n
    
  unsigned char *format =
    "GET %s HTTP/1.0\r\n" \
    "Host: %s:%s\r\n" \
    "User-Agent: %s\r\n";
  static unsigned char *msg = NULL;
  static size_t msg_length = 0;

  extern unsigned char *fortuseragent;
  
  dbs_printf(&msg, &msg_length, format, page, host, port, fortuseragent);

  if((status = write(s, msg, strlen(msg))) < 0) {
    fprintf(stderr, "\n%s: write(), error: %d\n", procname, errno);
    perror("write");
    fflush(stderr);
  }

  HashCtx ctx;
  unsigned char buffer[1024];

  HashInit(&ctx);

  bytes = 0;
  characters = 0;

  while((cnt = read(s, buffer, sizeof(buffer))) > 0) {
#ifdef DEBUG27
    write(1, buffer, cnt);
    fflush(stdout);
#endif
    HashUpdate(&ctx, buffer, cnt);
    bytes += cnt;
    for(c = 0; c < cnt; c++) {
      if(buffer[c] < 0x80 || buffer[c] > 0xbf)
	characters++;
    }
  }

  hash_update_cvar(&ctx);
  HashFinal(hash, &ctx);
  if(verbose) {
    fprintf(stdout,"\n, %d bytes read", bytes);
    fprintf(stdout,", %d characters read", characters);
    fprintf(stdout,", sha256: ");
    for(int c = 0;c < HashLen; c++) {
      fprintf(stdout,"%02x", hash[c]);
    }
    fprintf(stdout,"\n");
    newressu_output = 0; 
  }

  close(s);
}

#define aDEBUG49 2
#define aDEBUG50 2

void fort_hash_https_page(unsigned char *host,unsigned char *port, unsigned char *page, unsigned char *hash) // 202011 JariK
{
  int c, cnt, s, status, bytes, characters;

  SSL_METHOD *method = NULL;
  SSL_CTX *ctx = NULL;
  SSL *ssl;

  SSL_library_init(); //see: http://h30266.www3.hpe.com/odl/axpos/opsys/vmsos84/BA554_90007/ch04s03.html
  OpenSSL_add_ssl_algorithms();
  SSL_load_error_strings();

#ifdef DEBUG49
  fprintf(stdout,"SSLv23_client_method()\n");
#endif
  if((method = (SSL_METHOD *)    
      SSLv23_client_method()) == NULL) {
    fprintf(stderr,"\n%s: cannot SSLv3_server_method()", procname);
    fflush(stderr);
  }

#ifdef DEBUG49
  fprintf(stdout,"SSL_CTX_new()\n");
#endif
  if((ctx = SSL_CTX_new(method)) == NULL) {
    fprintf(stderr,"\n%s: cannot SSL_CTX_new()", procname);
    fflush(stderr);
  }

#ifdef DEBUG49
  fprintf(stdout,"SSL_new()\n");
#endif
  if((ssl = SSL_new(ctx)) == NULL) {
    fprintf(stderr,"\n%s: cannot SSL_new()", procname);
    fflush(stderr);
  }

#ifdef DEBUG49
  fprintf(stdout,"fort_connect()\n");
#endif 
 if((s = fort_connect(host, port)) < 0) {
    fprintf(stderr,"\n%s: cannot fort_connect()", procname);
    fprintf(stderr,", status: %d", s);
    fprintf(stderr,", errno: %d" , errno);
    perror("fort_connect");
    fflush(stderr);
  }

  SSL_set_fd(ssl, s);

#ifdef DEBUG49
  fprintf(stdout,"SSL_connect(ssl)\n");
#endif 
  if((status = SSL_connect(ssl)) <= 0) {
    fprintf(stderr,"\n%s: cannot SSL_connect()", procname);
    fprintf(stderr,", status: %d", status);
    fprintf(stderr,", errno: %d" , errno);
    fprintf(stderr,", SSL_get_error(): %d\n", SSL_get_error(ssl,status));
    perror("SSL_connect");
    fflush(stderr);
  }

  unsigned char *format =
    "GET %s HTTP/1.0\r\n" \
    "Host: %s:%s\r\n" \
    "User-Agent: %s\r\n";
  static unsigned char *msg = NULL;
  static size_t msg_length = 0;

  extern unsigned char *fortuseragent;

  dbs_printf(&msg, &msg_length, format, page, host, port, fortuseragent);

#ifdef DEBUG49
  fprintf(stdout,"SSL_write(), msg_length:%ld, msg=\"%s\"\n", msg_length,msg);
#endif 
  if((status = SSL_write(ssl, msg, strlen(msg))) < 0) {
    fprintf(stderr,"\n%s: SSL_write()", procname);
    fprintf(stderr,", status: %d", status);
    fprintf(stderr,", errno: %d", errno);
    fprintf(stderr,", SSL_get_error(): %d", SSL_get_error(ssl,status));
    perror("SSL_write");
    fflush(stderr);
  }
  fflush(stdout);
  
  HashCtx hashctx;
  unsigned char buffer[2048];

  HashInit(&hashctx);

  bytes = 0;
  characters = 0;

#ifdef DEBUG49
  fprintf(stdout, "SSL_read()\n");
#endif
  int bytesneeded = 0, bytesheader = 0, bytescontent = 0;
  
  do {
    cnt = SSL_read(ssl, buffer, sizeof(buffer));
    if(cnt < 0) {
      int err, err2;
      err = SSL_get_error(ssl, cnt);
      err2 = ERR_get_error();

      fprintf(stderr, "\n%s: cannot SSL_read()", procname);
      fprintf(stderr, ", retval: %d", cnt);
      fprintf(stderr, ", errno: %d", errno);
      fprintf(stderr,", SSL_get_error() %d", err);
      fprintf(stderr,", ERR_get_error() %d\n", err2);
      perror("SSL_read");
      fflush(stderr);
      break;
    }

#ifdef DEBUG50
    write(1, buffer, cnt);
#endif
    HashUpdate(&hashctx, buffer, cnt);
    bytes += cnt;

    for(c = 0; c < cnt; c++) {
      if(buffer[c] < 0x80 || buffer[c] > 0xbf)
	characters++;
    }
    if(bytesneeded == 0) {
      char *p;
      p = buffer;
      int count = 0;
      while(*p != '\0') {
	if(!strncmp(p, "\r\nContent-Length: ", 18)) {
	  bytescontent = atoi(p + 18);
	}
	if(!strncmp(p, "\r\n\r\n", 4)) {
	  bytesheader = count;
	}
	p++;
	count++;
      }
      bytesneeded = bytescontent + bytesheader + 4;
    }
  } while(SSL_pending(ssl) || bytes < bytesneeded);

  if(verbose) {
    fprintf(stdout,"\nbytescontent:%d", bytescontent);
    fprintf(stdout,", bytesheader:%d", bytesheader);
    fprintf(stdout,", bytesneeded:%d", bytesneeded);
    fflush(stdout);
  }
#ifdef OLD1

#ifdef DEBUG49
  fprintf(stdout,"SSL_read()\n");
#endif 
  while((cnt = SSL_read(ssl, buffer, sizeof(buffer))) > 0) {
#ifdef DEBUG49
    fprintf(stdout,"SSL_read() round\n");
#endif 
    
#ifdef DEBUG50
    write(1, buffer, cnt);
    fflush(stdout);
#endif
    HashUpdate(&hashctx, buffer, cnt);
    bytes += cnt;
    for(c = 0; c < cnt; c++) {
      if(buffer[c] < 0x80 || buffer[c] > 0xbf)
	characters++;
    }
  }
  fflush(stdout);
  if(cnt < 0) {
    fprintf(stderr, "\n%s: SSL_read()", procname);
    fprintf(stderr, ", status: %d", status);
    fprintf(stderr, ", errno: %d", errno);
    fprintf(stderr,", SSL_get_error(): %d", SSL_get_error(ssl,status));
    perror("SSL_read");
    fflush(stderr);
  }
  
#endif
  
  fflush(stdout);

  hash_update_cvar(&hashctx);
  HashFinal(hash, &hashctx);

  if(verbose) {
    fprintf(stdout,"\n, %d bytes read", bytes);
    fprintf(stdout,", %d characters read", characters);
    fprintf(stdout,", sha256: ");
    for(int c = 0;c < HashLen; c++) {
      fprintf(stdout,"%02x", hash[c]);
    }
    fprintf(stdout,"\n");
    newressu_output = 0; 
 }
#ifdef NOTUSED
  
#ifdef DEBUG49
  fprintf(stdout,"SSL_shutdown()\n");
#endif 
  SSL_shutdown(ssl);
#endif
  
#ifdef DEBUG49
  fprintf(stdout,"SSL_free()\n");
#endif 
  SSL_free(ssl);
#ifdef DEBUG49
  fprintf(stdout,"SSL_CTX_free()\n");
#endif 
  SSL_CTX_free(ctx);
  close(s);
}

commandrandom.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <memory.h>
#include <errno.h>

#include <openssl/ssl.h>
#include <openssl/err.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>

#include "sha256.h"
#include "fort.h"

extern unsigned char *procname;
extern int newressu_output; 
extern int verbose;

#include <stdarg.h>

void dbs_printf(unsigned char **buf, size_t *buf_length, const unsigned char *format, ...)
{
  int count;
  va_list args;;
  
  va_start(args, format);
  count = vsnprintf(*buf, *buf_length, format, args) + 1;
  va_end(args);
  if(*buf_length < count) {
    *buf_length = count;
    *buf = realloc(*buf, *buf_length);
    va_start(args, format);
    count = vsnprintf(*buf, *buf_length, format, args) + 1;
    va_end(args);
  }
}

int fort_connect(unsigned char *host, unsigned char *port)
{
  struct addrinfo hints, *res, *resp;
  int s, status;
  
  memset(&hints, 0, sizeof(hints));
  hints.ai_family = AF_UNSPEC;
  hints.ai_socktype = SOCK_STREAM;
  hints.ai_flags = AI_PASSIVE;
  
  if((status = getaddrinfo(host, port, &hints, &res)) != 0) {
    fprintf(stderr,"\n%s: getaddrinfo", procname);
    fprintf(stderr,", status %d", status);
    fprintf(stderr,", gai_strerror(): %s", gai_strerror(status));
    fprintf(stderr,", errno %d\n", errno);
    fflush(stderr);
  }
  
  for(resp = res; resp != NULL; resp = resp->ai_next) {
    if((s = socket(resp->ai_family, resp->ai_socktype, resp->ai_protocol)) < 0)
      continue;
    if(connect(s, resp->ai_addr, resp->ai_addrlen) == 0)
      break;
    close(s);
  }

  freeaddrinfo(res);

  return(s);
}

#define aDEBUG27 2

void fort_hash_http_page(unsigned char *host,unsigned char *port, unsigned char *page, unsigned char *hash) // 202011 JariK
{
  int c, cnt, s, status, bytes, characters;

  if((s = fort_connect(host, port))<0) {
    fprintf(stderr,"\n%s: cannot fort_connect()", procname);
    fprintf(stderr,", status: %d", s);
    fprintf(stderr,", errno: %d" , errno);
    perror("fort_connect");
    fflush(stderr);
  }

  // GET / HTTP/1.0\r\nHost: moijari.com:5006\r\n
    
  unsigned char *format =
    "GET %s HTTP/1.0\r\n" \
    "Host: %s:%s\r\n" \
    "User-Agent: %s\r\n";
  static unsigned char *msg = NULL;
  static size_t msg_length = 0;

  extern unsigned char *fortuseragent;
  
  dbs_printf(&msg, &msg_length, format, page, host, port, fortuseragent);

  if((status = write(s, msg, strlen(msg))) < 0) {
    fprintf(stderr, "\n%s: write(), error: %d\n", procname, errno);
    perror("write");
    fflush(stderr);
  }

  HashCtx ctx;
  unsigned char buffer[1024];

  HashInit(&ctx);

  bytes = 0;
  characters = 0;

  while((cnt = read(s, buffer, sizeof(buffer))) > 0) {
#ifdef DEBUG27
    write(1, buffer, cnt);
    fflush(stdout);
#endif
    HashUpdate(&ctx, buffer, cnt);
    bytes += cnt;
    for(c = 0; c < cnt; c++) {
      if(buffer[c] < 0x80 || buffer[c] > 0xbf)
	characters++;
    }
  }

  hash_update_cvar(&ctx);
  HashFinal(hash, &ctx);
  if(verbose) {
    fprintf(stdout,"\n, %d bytes read", bytes);
    fprintf(stdout,", %d characters read", characters);
    fprintf(stdout,", sha256: ");
    for(int c = 0;c < HashLen; c++) {
      fprintf(stdout,"%02x", hash[c]);
    }
    fprintf(stdout,"\n");
    newressu_output = 0; 
  }

  close(s);
}

#define aDEBUG49 2
#define aDEBUG50 2

void fort_hash_https_page(unsigned char *host,unsigned char *port, unsigned char *page, unsigned char *hash) // 202011 JariK
{
  int c, cnt, s, status, bytes, characters;

  SSL_METHOD *method = NULL;
  SSL_CTX *ctx = NULL;
  SSL *ssl;

  SSL_library_init(); //see: http://h30266.www3.hpe.com/odl/axpos/opsys/vmsos84/BA554_90007/ch04s03.html
  OpenSSL_add_ssl_algorithms();
  SSL_load_error_strings();

#ifdef DEBUG49
  fprintf(stdout,"SSLv23_client_method()\n");
#endif
  if((method = (SSL_METHOD *)    
      SSLv23_client_method()) == NULL) {
    fprintf(stderr,"\n%s: cannot SSLv3_server_method()", procname);
    fflush(stderr);
  }

#ifdef DEBUG49
  fprintf(stdout,"SSL_CTX_new()\n");
#endif
  if((ctx = SSL_CTX_new(method)) == NULL) {
    fprintf(stderr,"\n%s: cannot SSL_CTX_new()", procname);
    fflush(stderr);
  }

#ifdef DEBUG49
  fprintf(stdout,"SSL_new()\n");
#endif
  if((ssl = SSL_new(ctx)) == NULL) {
    fprintf(stderr,"\n%s: cannot SSL_new()", procname);
    fflush(stderr);
  }

#ifdef DEBUG49
  fprintf(stdout,"fort_connect()\n");
#endif 
 if((s = fort_connect(host, port)) < 0) {
    fprintf(stderr,"\n%s: cannot fort_connect()", procname);
    fprintf(stderr,", status: %d", s);
    fprintf(stderr,", errno: %d" , errno);
    perror("fort_connect");
    fflush(stderr);
  }

  SSL_set_fd(ssl, s);

#ifdef DEBUG49
  fprintf(stdout,"SSL_connect(ssl)\n");
#endif 
  if((status = SSL_connect(ssl)) <= 0) {
    fprintf(stderr,"\n%s: cannot SSL_connect()", procname);
    fprintf(stderr,", status: %d", status);
    fprintf(stderr,", errno: %d" , errno);
    fprintf(stderr,", SSL_get_error(): %d\n", SSL_get_error(ssl,status));
    perror("SSL_connect");
    fflush(stderr);
  }

  unsigned char *format =
    "GET %s HTTP/1.0\r\n" \
    "Host: %s:%s\r\n" \
    "User-Agent: %s\r\n";
  static unsigned char *msg = NULL;
  static size_t msg_length = 0;

  extern unsigned char *fortuseragent;

  dbs_printf(&msg, &msg_length, format, page, host, port, fortuseragent);

#ifdef DEBUG49
  fprintf(stdout,"SSL_write(), msg_length:%ld, msg=\"%s\"\n", msg_length,msg);
#endif 
  if((status = SSL_write(ssl, msg, strlen(msg))) < 0) {
    fprintf(stderr,"\n%s: SSL_write()", procname);
    fprintf(stderr,", status: %d", status);
    fprintf(stderr,", errno: %d", errno);
    fprintf(stderr,", SSL_get_error(): %d", SSL_get_error(ssl,status));
    perror("SSL_write");
    fflush(stderr);
  }
  fflush(stdout);
  
  HashCtx hashctx;
  unsigned char buffer[2048];

  HashInit(&hashctx);

  bytes = 0;
  characters = 0;

#ifdef DEBUG49
  fprintf(stdout, "SSL_read()\n");
#endif
  int bytesneeded = 0, bytesheader = 0, bytescontent = 0;
  
  do {
    cnt = SSL_read(ssl, buffer, sizeof(buffer));
    if(cnt < 0) {
      int err, err2;
      err = SSL_get_error(ssl, cnt);
      err2 = ERR_get_error();

      fprintf(stderr, "\n%s: cannot SSL_read()", procname);
      fprintf(stderr, ", retval: %d", cnt);
      fprintf(stderr, ", errno: %d", errno);
      fprintf(stderr,", SSL_get_error() %d", err);
      fprintf(stderr,", ERR_get_error() %d\n", err2);
      perror("SSL_read");
      fflush(stderr);
      break;
    }

#ifdef DEBUG50
    write(1, buffer, cnt);
#endif
    HashUpdate(&hashctx, buffer, cnt);
    bytes += cnt;

    for(c = 0; c < cnt; c++) {
      if(buffer[c] < 0x80 || buffer[c] > 0xbf)
	characters++;
    }
    if(bytesneeded == 0) {
      char *p;
      p = buffer;
      int count = 0;
      while(*p != '\0') {
	if(!strncmp(p, "\r\nContent-Length: ", 18)) {
	  bytescontent = atoi(p + 18);
	}
	if(!strncmp(p, "\r\n\r\n", 4)) {
	  bytesheader = count;
	}
	p++;
	count++;
      }
      bytesneeded = bytescontent + bytesheader + 4;
    }
  } while(SSL_pending(ssl) || bytes < bytesneeded);

  if(verbose) {
    fprintf(stdout,"\nbytescontent:%d", bytescontent);
    fprintf(stdout,", bytesheader:%d", bytesheader);
    fprintf(stdout,", bytesneeded:%d", bytesneeded);
    fflush(stdout);
  }
#ifdef OLD1

#ifdef DEBUG49
  fprintf(stdout,"SSL_read()\n");
#endif 
  while((cnt = SSL_read(ssl, buffer, sizeof(buffer))) > 0) {
#ifdef DEBUG49
    fprintf(stdout,"SSL_read() round\n");
#endif 
    
#ifdef DEBUG50
    write(1, buffer, cnt);
    fflush(stdout);
#endif
    HashUpdate(&hashctx, buffer, cnt);
    bytes += cnt;
    for(c = 0; c < cnt; c++) {
      if(buffer[c] < 0x80 || buffer[c] > 0xbf)
	characters++;
    }
  }
  fflush(stdout);
  if(cnt < 0) {
    fprintf(stderr, "\n%s: SSL_read()", procname);
    fprintf(stderr, ", status: %d", status);
    fprintf(stderr, ", errno: %d", errno);
    fprintf(stderr,", SSL_get_error(): %d", SSL_get_error(ssl,status));
    perror("SSL_read");
    fflush(stderr);
  }
  
#endif
  
  fflush(stdout);

  hash_update_cvar(&hashctx);
  HashFinal(hash, &hashctx);

  if(verbose) {
    fprintf(stdout,"\n, %d bytes read", bytes);
    fprintf(stdout,", %d characters read", characters);
    fprintf(stdout,", sha256: ");
    for(int c = 0;c < HashLen; c++) {
      fprintf(stdout,"%02x", hash[c]);
    }
    fprintf(stdout,"\n");
    newressu_output = 0; 
 }
#ifdef NOTUSED
  
#ifdef DEBUG49
  fprintf(stdout,"SSL_shutdown()\n");
#endif 
  SSL_shutdown(ssl);
#endif
  
#ifdef DEBUG49
  fprintf(stdout,"SSL_free()\n");
#endif 
  SSL_free(ssl);
#ifdef DEBUG49
  fprintf(stdout,"SSL_CTX_free()\n");
#endif 
  SSL_CTX_free(ctx);
  close(s);
}
Published
Categorized as muuta

Leave a comment