Ressu 2.3 Little rewrite, changes to –sample, some new alphabets, new INPUT definations, change in –space option, new –newline option, rewrite to output routine

Kaikki oikeudet pidätetään. Tässä versiossa ressusta on uusi yksinkertaisempi keskiarvoon perustuva tapa luokitella ketjut laskettaviin ja ei laskettaviin. Lisäksi –sample kytkimeen on lisätty status rivi, joka kertoo tällä hetkellä kirjoitettavan 1MB blokin, generaattorin nopeuden, tämän hetkisen kellonajan, jäljellä olevan ajoajan ja valmistumisajan. Lisäksi ohjelmaan on lisätty “naapureiden” ja muiden kirjainmerkistöjä.

Edit: lisätty kappaleita postin loppuun, ennen kokonaista sorsaa. Endedit.

Aloitetaan tärkeimmästä, eli varsinaisesta generaattorista: Ressu_clockbyte() antaa generaattorille kellojonon, josta satunnaisuus luodaan.

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

Seuraavana ressun ydin ressu_genbytes_single(), joka luo varsinnaisen satunnaisbittipuskurin: Rutiinin nimi on muuten vaihdettu _fast-loppuisesta _single-loppuiseen fast ja single toimintojen selventämiseksi.

Satunnaisbitit luodaan kahdessa vaiheessa, ensimmäisessä xorataan koko puskuri kellobiteillä, ja toisessa vaihdetaan puskurin merkkejä satunnaisesti. Satunnaisuus itseasiassa muodostuu siitä, missä puskurin paikassa merkki on kun sitä xorataan kellojonolla.

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

  for(c=0; c<8; c++) {
    for(d=0; d<size; d++) {
      e = buffer[d];
      e = ((e&0x80)>>7) | ((e&0x7f)<<1); // rotate left 1
      //e = ((e&0xe0)>>5) | ((e&0x1f)<<3); // rotate left 3
      //e = ((e&0xfe)>>1) | ((e&0x1)<<7);  // rotate right 1
      byte = ressu_clockbyte();
      if(prevbyte==-1)
	prevbyte=byte;
      buffer[d] = e^byte;
      if(prevbyte!=byte) {
	periods[count]++;
	clockbytes+=count;
	count=0;
	prevbyte=byte;
      }
      count++;
    }
    for(d=0; d<size; d++) {
      f = (f + buffer[d]) % size;
      e = buffer[d];
      buffer[d] = buffer[f];
      buffer[f] = e;
    }
  }
}

Seuraava rutiini on varsinainen asiakkaan kutsuttava rutiini, ja se yrittää löytää tarvittavien bittien määrän perusteella oikean määrän edellisen _single rutiinin kutsukertoja. Olennaisia asioita ovat se, että _single suorituksia on tarpeeksi että tulee mahdottomaksi arvata kellojonoa, tai sen perusteella satunnaisbittijonoa. Toinen asia on se, että tämä rutiini erottaa satunnaislukupuskurin asiakkaasta, että asiakas ei saa puskurin rakennetta, eikä näin voi arvata kellojonoa tai puskuria.

Ressun –stat riveillä on nyt ketjut myös lajitellussa (sorted) listassa, jolloin eri limitit on helpompi paikallistaa.

void ressu_genbytes(int size, unsigned char *buffer) // 6.5.2021 JariK
{
  int c, d, e, f;
  static int ressut_first=1,
    ressut_pos = 0,
    ressut_f = 0;
  static unsigned char ressut[RESSUT_BYTES];
  unsigned long prevperiods[1024];
  
  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;
      }
      
      int rndbits=0;
      int lim, lim1=0, lim2=0;
      int lim1a, lim1b;
      int high1, high2;
      
      for(d=0; rndbits<ressu_bits_needed ||
	  d<RESSU_MIN_ROUNDS ||
	  clockbytes < RESSU_MIN_CLOCKBYTES; d++) {

	// save previous round

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

	ressu_genbytes_single(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 bits
		
	rndbits=0;
	for(e=0;e<1024;e++) {
	  if(periods[e]>0 && periods[e]<lim) {
	    rndbits+=periods[e];
	  }
	}
      } // for(d=0;
      if(stats) {
	fprintf(stdout,"rounds: %d",d);
	for(e=0;e<1024;e++) {
	  if(periods[e]>0)
	    fprintf(stdout," %d:%lu",e,periods[e]);
	}

#ifdef DEBUG_SORTED
	
	fprintf(stdout,", 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(stdout," %d",g);
	}

#endif
	fprintf(stdout,", high1:%d",high1);
	fprintf(stdout,", high2:%d",high2);
	fprintf(stdout,", lim1a:%d",lim1a);
	fprintf(stdout,", lim1b:%d",lim1b);
	fprintf(stdout,", lim1:%d",lim1);
	fprintf(stdout,", lim2:%d",lim2);
	fprintf(stdout,", div:%f",(double)lim2/lim1);
	fprintf(stdout,", clockbytes:%ld",clockbytes);
	fprintf(stdout,", rndbits:%d",rndbits);
	fprintf(stdout,"\n");
	fflush(stdout);
      }
    } // 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++)

  genbytes+=size;
}

Seuraavassa apurutiinit –sample option tilarivin tulostukseen. stat_line_begin() aloittaa tilarivin tulostuksen, stat_line_printf() tulostaa tilarivin elementin, stat_line_readable() tulostaa luvun pyöristettynä merkkeihin, kiloihin, megoihin tai gigoihin stat_line_end() lopettaa rivin tulostuksen ja poistaa edellisen rivin jäämät rivin lopusta.

#include <stdarg.h>

#define aDEBUG31

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

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;
  static size_t stat_line_buf_length;

  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_readable(unsigned long length)
{
  int c, low;
  double length2;
  unsigned char buf10[10];
  // 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;
  }
  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;
    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);
}

Ensin esimerkki tilarivin tulostuksesta: (rullaa loppurivin nähdäksesi oikealle). Olen lisännyt väleihin crlf:iä saadakseni näkyviin useampia rivejä. Rivien loppuihin on jäänyt tuo animoitu kursori.

./newressu --fast --sample
blk 0, written 0 |/
blk 1, written 1MB, 1MB/sec, now Sat 20:50 UTC, left 2h 16m, ready at 23:06 UTC /-
blk 327, written 327MB, 860KB/sec, now Sat 20:56 UTC, left 2h 35m, ready at 23:32 UTC \|/
blk 667, written 667MB, 854KB/sec, now Sat 21:03 UTC, left 2h 30m, ready at 23:33 UTC \|/
blk 945, written 945MB, 856KB/sec, now Sat 21:09 UTC, left 2h 24m, ready at 23:33 UTC /-
blk 1024, written 1GB, 855KB/sec, now Sat 21:10 UTC, left 2h 22m, ready at 23:33 UTC    \|
blk 1025, written 1.0GB, 856KB/sec, now Sat 21:10 UTC, left 2h 22m, ready at 23:33 UTC |/
blk 1685, written 1.6GB, 858KB/sec, now Sat 21:23 UTC, left 2h 9m, ready at 23:33 UTC |/
blk 2048, written 2GB, 860KB/sec, now Sat 21:30 UTC, left 2h 1m, ready at 23:32 UTC   \|
blk 2229, written 2.2GB, 862KB/sec, now Sat 21:34 UTC, left 1h 58m, ready at 23:32 UTC -\
blk 2725, written 2.7GB, 866KB/sec, now Sat 21:43 UTC, left 1h 47m, ready at 23:31 UTC /-
blk 3133, written 3.1GB, 869KB/sec, now Sat 21:51 UTC, left 1h 39m, ready at 23:31 UTC /-
blk 3569, written 3.5GB, 871KB/sec, now Sat 22:00 UTC, left 1h 30m, ready at 23:30 UTC \|
blk 4096, written 4GB, 869KB/sec, now Sat 22:10 UTC, left 1h 20m, ready at 23:31 UTC   \|
blk 4226, written 4.1GB, 870KB/sec, now Sat 22:13 UTC, left 1h 17m, ready at 23:30 UTC |/
blk 4864, written 4.8GB, 873KB/sec, now Sat 22:25 UTC, left 1h 5m, ready at 23:30 UTC /-\
blk 5380, written 5.3GB, 874KB/sec, now Sat 22:35 UTC, left 54m, ready at 23:30 UTC -\|
blk 5482, written 5.4GB, 874KB/sec, now Sat 22:37 UTC, left 52m, ready at 23:30 UTC /-
blk 5992, written 5.9GB, 875KB/sec, now Sat 22:47 UTC, left 42m, ready at 23:30 UTC -\
blk 6110, written 6.0GB, 876KB/sec, now Sat 22:49 UTC, left 40m, ready at 23:29 UTC \|
blk 6379, written 6.2GB, 876KB/sec, now Sat 22:54 UTC, left 35m, ready at 23:29 UTC /-
blk 7117, written 7.0GB, 873KB/sec, now Sat 23:09 UTC, left 20m, ready at 23:30 UTC /-
blk 7854, written 7.7GB, 870KB/sec, now Sat 23:24 UTC, left 6m, ready at 23:30 UTC -\
blk 8144, written 8.0GB, 870KB/sec, now Sat 23:30 UTC, left 56 seconds, ready at 23:30 UTC /-\
blk 8171, written 8.0GB, 869KB/sec, now Sat 23:30 UTC, left 24 seconds, ready at 23:31 UTC -\
blk 8187, written 8.0GB, 869KB/sec, now Sat 23:30 UTC, left 5 seconds, ready at 23:31 UTC /-
blk 8188, written 8.0GB, 869KB/sec, now Sat 23:30 UTC, left 4 seconds, ready at 23:31 UTC -\
blk 8189, written 8.0GB, 869KB/sec, now Sat 23:30 UTC, left 3 seconds, ready at 23:31 UTC \|
blk 8190, written 8.0GB, 869KB/sec, now Sat 23:30 UTC, left 2 seconds, ready at 23:31 UTC |/-
blk 8191, written 8.0GB, 869KB/sec, now Sat 23:31 UTC, left 1 seconds, ready at 23:31 UTC -\
Done!

Seuraavassa tulostetaan –sample kytkimellä satunnaislukutiedosto. Edellisestä versiosta on lisätty tilarivi animoituva kursori, kytkimet eri satunnaislukulähteille (–ressu, –debug. –single, –fast, –urandom, –random). Lisäksi sample rivillä on kenttä, jossa on bittigeneraattorin nopeus (esim 700Kb/sec).

.  if(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;
#ifdef STAT_LINE_ANIM
    long int secs, prev_secs=-1;
    int crs=0;
#endif

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

      secondsstart=time(NULL);

      for(c=0;c<CLIM;c++) {
	secondsnow=time(NULL);
	
	// print status line:
	// blk 8171, written 8.0GB, 869KB/sec, now Sat 23:30 UTC, left 24 seconds, ready at 23:31 UTC
	
	stat_line_begin();
	stat_line_printf("\rblk %d, written ",c);
	stat_line_readable((unsigned long)c*DLIM*BLKSIZE);
	
	if(c>0) {
	  stat_line_printf(", ");
	  stat_line_readable((unsigned long)((double)c*DLIM*BLKSIZE/(secondsnow-secondsstart)) );
	  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 different
	  
	  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
	
#ifdef STAT_LINE_ANIM
	unsigned char cursor[4] = { '|', '/', '-', '\\' };

	fprintf(stderr," ");
#endif
	for(d=0;d<DLIM;d++) {
#ifdef STAT_LINE_ANIM
	  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;
	  }
#endif
	  if(input==0) // ressu prod
	    ressu_genbytes(sizeof(buffer),buffer);
	  else if(input==1) // ressu debug
	    ressu_genbytes_debug(sizeof(buffer),buffer);
	  else if(input==2) { // ressu fast
	    clockbytes=0;
	    for(e=0; e<RESSU_MIN_ROUNDS ||
		  clockbytes < RESSU_MIN_CLOCKBYTES;e++) {
	      ressu_genbytes_single(sizeof(buffer),buffer);
	    }
	  } else if(input==3) // ressu single
	    ressu_genbytes_single(sizeof(buffer),buffer);
	  else if(input==8) // urandom
	    readfile_xor(sizeof(buffer),buffer,urandomfilename);
	  else if(input==9) // random
	    readfile_xor(sizeof(buffer),buffer,randomfilename);
	    
#ifdef WRITE_SAMPLE
	  fwrite(buffer,1,sizeof(buffer),fp1);
#endif
	}
#ifdef STAT_LINE_ANIM
	fprintf(stderr,"\b \b");
	prev_secs=-1;
#endif
      }
      fclose(fp1);
    }
    // remove last status line
    
    stat_line_begin();
    stat_line_printf("Done!");
    stat_line_end();
    fprintf(stdout,"\n");
    fflush(stdout);
    exit(0);
  }

Vielä joukko “naapureiden” ja muiden merkistöjä:

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

      } else if(!strcmp("--dnk",argv[c]) || // danish alphabet
		!strcmp("--nor",argv[c])) { // norwegian alphabet
	digits=
	  "ABCDEFGHIJKLMNOPQRSTUVWXYZÆØÅ" \
	  "abcdefghijklmnopqrstuvwxyzæøå";
	size=8;

      } else if(!strcmp("--fin",argv[c]) || // finnish alphabet
		!strcmp("--swe",argv[c])) { // swedish alphabet
	digits=
	  "ABCDEFGHIJKLMNOPQRSTUVWXYZÅÄÖ" \
	  "abcdefghijklmnopqrstuvwxyzåäö";
	size=8;

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

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

      } 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ž";
	size=8;

      } 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
		) {
	digits=
	  "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
	  "abcdefghijklmnopqrstuvwxyz";
	size=8;

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

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

      }

      ...

Seuraavassa satunnaismerkkejä suomalaisella merkistöllä:

$ newressu --fin
00000 gaClJUiÖLFyyzKOKShäIwhpäyckfsITLjpIåCbPjZgfoNMqpKäuwäYyJYeqYaWBO
00001 CuhaxfbcefEsCwdAHuTMrcLqhkzÄSäCNicgDsNÄqekVläÅStgZiKuDVNÅkfhuaåT
00002 häOhuzNOQuBSoCAjPufäAlRAhtRGzbGMoöäPSgÅzöBWQAGiÖSctäTSuLwxTuxhff
00003 yryQRXiDQVåkKÅbivTBulywRzGÅpUaTåJäcboZCqÅvskCcÄUCtnbJLHOWeÄÅxÅGV
00004 MÅöVxnCbÄäböSmjjGVDpÄTFSEUCVÄxMRÅiqAäzpctsuaNvkbgkLTUXwpbNhMblLh
00005 oLUwEgvÅVVSÅåJvhmZDKUäKksÄjaåwRdzEhPeöqUyZYPDöezupmÅägkHmzoHÖbIc
00006 xNYbbKucMktDQlWcPxDJåYEOaHMoäfyQLEUdQxkIjKcXydQVJPjRJdUshdZjÅBur
00007 pWvxTCqAbnjsTJhkQtSDphJumoUogeIhKmVåAYSrLqÖoHsVrKrDFOmåPRÄiRRTbD
00008 GiöDRÖÄezäwFgäwuFcpApInTdXYbMvOpwscbQyrZPdmBbCRVqgkeZBpALsxQPäeG
00009 rTrMöxjWöuFMZASgGjrlrÖÅäoZrvAWvTpaöVIEIZxPFPHzFyGeIMäBrVlIQsOhFF

Ja vielä satunnaismerkkejä kreikkalaisella merkistöllä:

$ ./newressu --grc
00000 ΧΙμΘΗζζηΓλΨΥλΔυσΤΒΒηλΨγτΕσΦθΜκΣμψψδιΒψΗωρβΚΟΡΦΔΟΗσαλΤΑΧΧρωεγΜΥΒΓ
00001 ΤβΚΒΘΣΚγΞΦηΗγοΦοφγΒζβπΣΜδΙζπψδψεΞηΖΒΔΖηοΜνΠΒτγΠΗρηΜΣξηρΤΡνΖΞοφαλ
00002 ΒΔΗαορΙΚΦΥηοφΤηΦΝρΜΙμΞπΡβδδΝβΩΚυΙοΝΗιΡΖΓΤΥππΘΔλΥΙΣΩΛΒζξλθΛχΒηψΨΛ
00003 σΧΛΥγξΠΠΒριΚιΔπΘΨΓαΣΤεεξΝοΗπΔΨνΠνΜηΠλφΒπΣΞξβρΑΩΨΙΠρθδπαξψθωΟΠΦΝλ
00004 ΚΨΝτπβσαΥΨΚηδΔΖβΩΦΜμψλΤΔεχΞΑυΡνΓεκιΙαβΧμΕβΤεΩηβΝεΩΘΣμΕΟΙεΘαφΑΡυθ
00005 υΨχνΔΥΜΝκηωφΙΜΑΜΧΧΕρυξυΝτωλΞρΟθψρχΩωΚΜΒΧνΓΗΔΒαυΟνψΗωμσδΗσρσΞΔπΩΥ
00006 εγΜτζγσμΕΝΟγφνδΔμΙλξκΞζδιΓηλΘθζυοΗΦψπδΠΦΗηΗψΜωθαιΣπΘυψΥΤλΣμΝΦοΓυ
00007 ΤΕεΛΙυΔΤμΟΗωΡΔτππΜΔΑΔογγκσλΠΒΛΟΚραΓΔΛμπβξωξΚζΣοηποΝχΗωΡγΨθωΞΚΡηγ
00008 ΝΓΘφΓτΣλΠιαΨεΨΛΚΑΗφβζΠΦΩΜφφΨΞαΣξδθΠΕΑωζΑΛΜζωωΓξΩπΘΟδθδΓΑμλΥΞΡΚχΝ
00009 εΠΙβΒΚηΘΣΚΨτβΨΗΧΑΚΦαξΨιααδΘιμνΖπΦΒνΔχρζΓομγγζυγΔζΘΑπΔΝμΛΟοΚΕΗΒΦξ

Edit: Lisätty #define määritykset eri input tyypeille:

#define INPUT_RESSU 0
#define INPUT_DEBUG 1
#define INPUT_FAST 2
#define INPUT_SINGLE 3
#define INPUT_URANDOM 8
#define INPUT_RANDOM 9

static int input=0;

char *randomgen[] = { "ressu", "debug","fast","single","4","5","6","7","urandom","random" };

Eri input:tien käsittely parametreissa:

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

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

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

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

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

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

#endif

Eri inputtien käsittely Ressu_genbyte():ssä:

int ressu_genbyte()
{
  static unsigned char gent[GENT_SIZE];
  static unsigned int gent_pos=0;
  unsigned char ch;

  if(input==INPUT_RESSU) { // ressu prod
    ressu_genbytes(sizeof(ch), &ch);
  } else if(input==INPUT_DEBUG) { // ressu debug
    ressu_genbytes_debug(sizeof(ch), &ch);
  } else {
    if(gent_pos==0) {
      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);
      } 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
      }
    } // if(gent_pos==0
    ch=gent[gent_pos];
    gent_pos=(gent_pos+1)%sizeof(gent);
  }
  return(ch);
}

Inputtien käsittely –sample rutiinissa:

...
	  if(input==INPUT_RESSU) // ressu prod
	    ressu_genbytes(sizeof(buffer),buffer);
	  else if(input==INPUT_DEBUG) // ressu debug
	    ressu_genbytes_debug(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);
	  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
...

–space parametria muutettu siten että sillä voidaan ryhmitellä rivit kuinka monen sanan ryhmäksi tahansa. Lisätty myös –newline parametri, jolla kerrotaan kuinka monen rivin välin tulostetaan tyhjä rivi. Aluksi –space ja –newline parametrin käsittely:

      } 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)>1) {
	  snewline=atoi(argv[c]+9);
	} else if(c+1<argc && atoi(argv[c+1])>1) {
	  snewline=atoi(argv[c+1]);
	  c++;
	} else {
	  snewline=!snewline;
	}

Muutos –rand toimintoon (ennen sspace asetettiin kolmoseksi, nyt kakkoseksi, eli kahden sanan ryhmät, ja newline asetetaan viitoseksi, tyhjä rivi viiden rivin välein):

      } else if(!strcmp("--rand",argv[c])) {
	digits = "0123456789";
	sspace=2;
	snewline=5;
	slineno=1;
	words=10;
	chars=0;
	limit=100000;
	//size=5;
	//lines=20000;

Seuraavassa esimerkkejä –space toiminnosta:

$ newressu --space2 -w12
00000   40576 19224  31061 56270  17558 40937  34594 10659  74339 12974  22074 55552
00001   16458 38377  11774 16585  22914 91334  07864 56857  67861 21625  60387 19236
00002   89247 64778  70347 88680  18056 41666  21943 54933  98467 47080  72261 75113
00003   85600 85785  21279 60961  44469 44224  07816 05428  12713 47709  00767 21351
00004   44610 12782  36115 18451  35002 32467  57140 45450  52562 19903  23200 42600
00005   41334 49165  43451 22812  35567 97697  33419 54424  99249 73074  78173 33494
00006   46004 49866  74092 88375  66402 06470  84535 42922  19028 54769  40706 56304
00007   23769 33368  34626 62956  76015 13668  63460 88936  16224 07373  95768 58904
00008   69892 32434  47599 52513  59030 96318  71762 83897  84475 66329  97716 37462
00009   49860 55143  28166 55053  24563 70116  81067 09653  68256 69821  35928 62961
$ newressu --space3 -w12
00000   17152 43421 73154  88356 08724 22361  98840 58719 38530  71759 99457 54797
00001   34274 64843 30530  09491 75975 09179  07263 28982 07386  42666 87893 42997
00002   79771 40056 80666  48071 95604 53112  11807 43640 97394  83588 99039 15051
00003   24446 20612 27304  39014 61616 12134  66611 26373 08435  10287 76092 84674
00004   51142 23682 70818  15506 33787 87524  27784 70951 40143  93128 05481 37800
00005   92343 56280 49333  63783 87591 69719  91171 03362 25838  51097 71197 07878
00006   37752 74753 85191  70998 41906 76664  03094 36603 26754  63766 35271 66881
00007   30308 55030 36917  91142 83198 72776  56947 07046 01347  91820 34930 46188
00008   76167 95789 56123  00234 68842 55135  01123 61807 36224  10612 53782 58296
00009   60449 49813 78428  13318 35303 92252  76849 68503 27659  21438 20675 24302
$ newressu --space4 -w12
00000   20742 22650 28906 31905  47341 04150 50584 13183  17049 80113 91663 72823
00001   18451 62550 03962 72838  70153 04558 04736 05078  32753 58747 59763 85999
00002   13546 21343 80647 86450  08986 70814 52073 89482  16833 69109 83168 74567
00003   43312 08071 10380 79643  05954 04207 79686 58144  32362 12612 49240 77737
00004   09080 40197 19063 83600  71642 53301 42849 13056  24062 84903 99760 74049
00005   58252 03578 26906 69699  54557 99905 97538 10684  48060 11384 36784 22614
00006   62784 41888 44974 92600  54416 74768 04900 40537  21423 48921 05873 87655
00007   79264 95285 31512 88833  95410 48463 18757 65606  47583 62909 61778 98167
00008   21430 53643 59374 10165  66253 18774 52714 97702  66947 83793 96488 74019
00009   19585 27211 43086 98500  78116 97613 11368 97086  07767 75589 49746 85101
$ newressu --space6 -w12
00000   54562 43657 61862 90326 06376 68543  13187 74217 34102 99570 00506 08494
00001   43796 71105 76359 36295 05082 17566  83453 67076 01490 85085 89075 90791
00002   96623 74591 02669 84605 35980 77577  48690 37794 52479 63557 22829 04179
00003   62797 50805 37389 27107 88850 70272  90188 75741 81854 59182 87981 04882
00004   13869 45007 30936 83500 77868 82829  94888 97847 91576 45645 82972 11360
00005   69896 87217 37225 57165 46607 05168  26226 57438 52827 74413 74600 41372
00006   13930 89230 03171 90853 79694 61314  08778 82186 92948 66862 70041 66197
00007   53014 51311 04614 80982 78052 99464  30259 68724 10193 26498 36534 33548
00008   36716 94076 88796 12946 17293 42641  89329 14410 66266 92296 49736 20468
00009   89022 22758 19994 67680 20271 33398  90060 85101 31617 11169 89386 62883

Seuraavassa esimerkkejä –newline toiminnosta:

$ newressu --newline2
00000 12813454512274517122425130115329366202515091666002172505025708446
00001 71096917961720806622398424293094570593626081571407468856782029942

00002 31047589039493088825514190891802272638960735378240661287270814933
00003 44089360986269804268430685930815151891798848432529601113180624795

00004 19569055111387376033524341992010566706684305745782920146962411455
00005 40925179314794580859485054809954191393015027380242681571601836439

00006 39004105241760250328315129737539505105940903109668904054251135855
00007 14587962370655169678732121574908360695089442013629060386702741659

00008 86993427583498077107114114430973231820885984160751028895695538981
00009 99373782264354982010189723728714999104059933148883694576500989637
$ newressu --newline3
00000 38623820473984903715490633923035337747665655115872184341824384094
00001 11547415184513070913520251785473334799146294338887127786659410999
00002 15410172629583358480662649136383040138410387647765016582268881965

00003 00863451030577367274360418081943523452940675438253783662606719532
00004 01250079675066865757517649726995679792198002260657201736023787837
00005 94042313276943376689259878147623444882366379288007965386275558839

00006 23468210585440511486572986161349224492165783119937326761815799604
00007 75580944804404621982050962301318881792685082315718928756343655877
00008 97838780101007189801544198322649565267631399973289857678700275243

00009 00363356898906130839164602585292709158234658394677097875319319258
$ newressu --newline4
00000 72786849533113500677581727520646514466992947738841761551460659422
00001 17424406153258926055361193709969183192241783314364747963252396187
00002 62974292705081577883093518402663586933467293523840132980383941609
00003 09632535989837189578312034432800371535312445316521384541985342710

00004 58019241328566825267006667886417218528388447275955778929489919053
00005 25858380015274227163548822248333717139214276930655496085958767633
00006 58319515459516655356787764450762739262081023905149877752114500182
00007 71846494587648754437205332298599954664694916723684381200029099175

00008 71730873837817666232939724815562175781809864537799316370685741021
00009 62465837514841138083445025234974607020150852582309301951684391272

Muotoiltu tulostus luuppia uudestaan, seuraavassa uusi koodi(aluksi apurutiinit): (vähennetty !quiet if lauseita, vähennetty pchars muuttelua)

static void print_spaces()
{
  if(sspace>=2 &&
       // rand space between double words
     ( (pwords>0 && pwords%sspace==0) ||
     // rand space after linenumber
       (slineno && pwords==0) ) )
    fprintf(stdout," ");

  if(sspace &&
       // space between words
     ( (pwords>0) ||
     // space after linenumber
       (slineno && pwords==0) ) ) 
    fprintf(stdout," ");
}

#ifdef OLD1
static void print_spaces()
{
  if(sspace && pchars>0 && !quiet) {
    if(sspace==3 && pwords%2==0) {
      fprintf(stdout," ");
      pchars++;
    }
    fprintf(stdout," ");
    pchars++;
  }
}
#endif

static void print_word(unsigned char *buf)
{
  if(size!=0) {
    fprintf(stdout,"%*s",size,buf);
  } else {
    fprintf(stdout,"%s",buf);
  }
}

Ja varsinainen selkiytetty/lyhennetty tulostusluuppi:

  for(;;) {

    if(!sort) { // also 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);
	}

	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);
	}
	pwords++;
      } // while(pwords<linew) {
    } // if(!sort)

    if(!quiet) {
      fprintf(stdout,"\n");
    }
    plines++;
    if(!quiet && snewline>1 &&
       plines<lines &&
       plines%snewline==0)
      fprintf(stdout,"\n");

    // all needed lines printed?

    if(plines >= lines)
      break;

  } // for(;;)

Lopuksi vielä koko ohjelma:

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

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

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

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

#define RESSUT_BYTES 1024
#define RESSU_BITS_NEEDED 128
#define RESSU_MIN_ROUNDS 2
#define RESSU_MIN_CLOCKBYTES 16384+1024

#define aWRITE_SAMPLE 2
#define USE_RANDOM 2
#define aDEBUG1
#define aDEBUG2 2
#define aDEBUG4 2
#define DEBUG_SORTED 2

static char samplefilename[128]="newressusample.rnd";
static char urandomfilename[128]="/dev/urandom";
static char randomfilename[128]="/dev/random";

#ifdef DEBUG4
static char debugfilename[128]="newressu.deb";
#endif

static int ressu_bits_needed = RESSU_BITS_NEEDED;
static int ressut_bytes = RESSUT_BYTES;

static int stats=0;

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

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

  for(c=0; c<8; c++) {
    for(d=0; d<size; d++) {
      e = buffer[d];
      e = ((e&0x80)>>7) | ((e&0x7f)<<1); // rotate left 1
      //e = ((e&0xe0)>>5) | ((e&0x1f)<<3); // rotate left 3
      //e = ((e&0xfe)>>1) | ((e&0x1)<<7);  // rotate right 1
      byte = ressu_clockbyte();
      if(prevbyte==-1)
	prevbyte=byte;
      buffer[d] = e^byte;
      if(prevbyte!=byte) {
	periods[count]++;
	clockbytes+=count;
	count=0;
	prevbyte=byte;
      }
      count++;
    }
    for(d=0; d<size; d++) {
      f = (f + buffer[d]) % size;
      e = buffer[d];
      buffer[d] = buffer[f];
      buffer[f] = e;
    }
  }
}

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

  clockbytes=0;
  for(c=0; c<RESSU_MIN_ROUNDS ||
	clockbytes < RESSU_MIN_CLOCKBYTES;c++) {
    ressu_genbytes_single(size,buffer);
  }
}

void ressu_genbytes(int size, unsigned char *buffer) // 6.5.2021 JariK
{
  int c, d, e, f;
  static int ressut_first=1,
    ressut_pos = 0,
    ressut_f = 0;
  static unsigned char ressut[RESSUT_BYTES];
  unsigned long prevperiods[1024];
  
  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;
      }
      
      int rndbits=0;
      int lim, lim1=0, lim2=0;
      int lim1a, lim1b;
      int high1, high2;
      
      for(d=0; rndbits<ressu_bits_needed ||
	  d<RESSU_MIN_ROUNDS ||
	  clockbytes < RESSU_MIN_CLOCKBYTES; d++) {

	// save previous round

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

	ressu_genbytes_single(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 bits
		
	rndbits=0;
	for(e=0;e<1024;e++) {
	  if(periods[e]>0 && periods[e]<lim) {
	    rndbits+=periods[e];
	  }
	}
      } // for(d=0;
      if(stats) {
	fprintf(stdout,"rounds: %d",d);
	for(e=0;e<1024;e++) {
	  if(periods[e]>0)
	    fprintf(stdout," %d:%lu",e,periods[e]);
	}

#ifdef DEBUG_SORTED
	
	fprintf(stdout,", 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(stdout," %d",g);
	}

#endif
	fprintf(stdout,", high1:%d",high1);
	fprintf(stdout,", high2:%d",high2);
	fprintf(stdout,", lim1a:%d",lim1a);
	fprintf(stdout,", lim1b:%d",lim1b);
	fprintf(stdout,", lim1:%d",lim1);
	fprintf(stdout,", lim2:%d",lim2);
	fprintf(stdout,", div:%f",(double)lim2/lim1);
	fprintf(stdout,", clockbytes:%ld",clockbytes);
	fprintf(stdout,", rndbits:%d",rndbits);
	fprintf(stdout,"\n");
	fflush(stdout);
      }
    } // 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++)

  genbytes+=size;
}

#ifdef DEBUG4
static unsigned char cc[256*1048576]; // clock_chain
static unsigned char *ccp;
static int cc_bytes;
#endif

static unsigned char ressu_clockbyte_debug() /* JariK 2013 */
{
  unsigned char c;
  struct timeval tv;
  gettimeofday(&tv, NULL);
  c=tv.tv_usec & 0xff;
#ifdef DEBUG4
  if(cc_bytes<sizeof(cc)) {
    *ccp++ = c;
    cc_bytes++;
  }
#endif
  return(c);
}

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

  for(c=0; c<8; c++) {
    for(d=0; d<size; d++) {
      e = buffer[d];
      e = ((e&0x80)>>7) | ((e&0x7f)<<1); // rotate left 1
      //e = ((e&0xe0)>>5) | ((e&0x1f)<<3); // rotate left 3
      //e = ((e&0xfe)>>1) | ((e&0x1)<<7);  // rotate right 1
      byte = ressu_clockbyte_debug();
      if(prevbyte==-1)
	prevbyte=byte;
      buffer[d] = e^byte;
      if(prevbyte!=byte) {
	periods[count]++;
	clockbytes+=count;
	count=0;
	prevbyte=byte;
      }
      count++;
    }
    for(d=0; d<size; d++) {
      f = (f + buffer[d]) % size;
      e = buffer[d];
      buffer[d] = buffer[f];
      buffer[f] = e;
    }
  }
}

void ressu_genbytes_debug(int size, unsigned char *buffer) // 6.5.2021 JariK
{
  int c, d, e, f;
  static int ressut_first=1,
    ressut_pos = 0,
    ressut_f = 0;
  static unsigned char ressut[RESSUT_BYTES];
  unsigned long prevperiods[1024];
  
  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 DEBUG4
      ccp=cc;
      cc_bytes=0;

      while(ressu_clockbyte_debug()!=0);
      while(ressu_clockbyte_debug()==0);

      ccp=cc;
      cc_bytes=0;
#endif

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

      for(d=0; rndbits<ressu_bits_needed ||
	  d<RESSU_MIN_ROUNDS; d++) {

	// save previous round

        for(e=0;e<1024;e++) {
	  prevperiods[e]=periods[e];
	}
		
	ressu_genbytes_single_debug(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 bits
	
	rndbits=0;
	for(e=0;e<1024;e++) {
	  if(periods[e]>0 && periods[e]<lim) {
	    rndbits+=periods[e];
	  }
	}
	
	if(stats) {
	  fprintf(stdout," round: %d",d);
	  for(e=0;e<1024;e++) {
	    if(periods[e]>0)
	      fprintf(stdout," %d:%lu",e,periods[e]);
	  }

#ifdef DEBUG_SORTED

	  fprintf(stdout,", 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(stdout," %d",g);
	  }

#endif
	  
	  fprintf(stdout," high1:%d",high1);
	  fprintf(stdout," high2:%d",high2);
	  fprintf(stdout," lim1a:%d",lim1a);
	  fprintf(stdout," lim1b:%d",lim1b);
	  fprintf(stdout," lim1:%d",lim1);
	  fprintf(stdout," lim2:%d",lim2);
	  fprintf(stdout," clockbytes:%ld",clockbytes);
	  fprintf(stdout," rndbits:%d",rndbits);
	  fprintf(stdout,"\n");
	  fflush(stdout);
	}
      } // for(d=0;
      if(stats) {
	fprintf(stdout,"rounds: %d",d);
	for(e=0;e<1024;e++) {
	  if(periods[e]>0)
	    fprintf(stdout," %d:%lu",e,periods[e]);
	}

#ifdef DEBUG_SORTED

	fprintf(stdout,", 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(stdout," %d",g);
	}

#endif
	
	fprintf(stdout," high1:%d",high1);
	fprintf(stdout," high2:%d",high2);
	fprintf(stdout," lim1a:%d",lim1a);
	fprintf(stdout," lim1b:%d",lim1b);
	fprintf(stdout," lim1:%d",lim1);
	fprintf(stdout," lim2:%d",lim2);
	fprintf(stdout," clockbytes:%ld",clockbytes);
	fprintf(stdout," rndbits:%d",rndbits);
	fprintf(stdout,"\n");
	fflush(stdout);
      }
      
#ifdef DEBUG4
      FILE *fp1;
      if((fp1=fopen(debugfilename,"a"))!=NULL) {
	//if(lim1==-1 || lim2==-1 || lim1==lim2) {
	for(e=0;e<32;e++)
	  fprintf(fp1,"%02x",ressut[e]);

	fprintf(fp1,", ressut_bytes: %d",ressut_bytes);
	fprintf(fp1,", ressu_bits_needed: %d",ressu_bits_needed);
	fprintf(fp1,", ressu_min_rounds: %d",RESSU_MIN_ROUNDS);

	fprintf(fp1,", clockbytes: %ld",clockbytes);
	
	unsigned long total=0;	
	fprintf(fp1,", fluctuations:");
	for(e=0;e<1024;e++) {
	  if(periods[e]!=0) {
	    fprintf(fp1," %d:%lu",e,periods[e]);
	    total+=(periods[e]*e);
	  }
	}
	fprintf(fp1,", total: %lu",total);
	fprintf(fp1,", high1: %d",high1);
	fprintf(fp1,", high2: %d",high2);
	fprintf(fp1,", limit1: %d",lim1);
	fprintf(fp1,", limit2: %d",lim2);
	fprintf(fp1,", limit: %d",lim);

	fprintf(fp1,", skipped:");
	for(e=0;e<1024;e++) {
	  if(periods[e]>=lim) {
	    fprintf(fp1," %d:%lu",e,periods[e]);
	  }
	}
	
	fprintf(fp1,", counted:");
	for(e=0;e<1024;e++) {
	  if(periods[e]>0 && periods[e]<lim) {
	    fprintf(fp1," %d:%lu",e,periods[e]);
	  }
	}	
	fprintf(fp1,", rndbits: %d",rndbits);
	fprintf(fp1,", rounds: %d",d);

	int prevbyte=-1, count=0, count2=0;
	unsigned char byte, small=-1;

	fprintf(fp1,", small chains:");
	for(e=0;e<cc_bytes;e++) {
	  byte=cc[e];
	  if(prevbyte==-1) {
	    prevbyte=byte;
	    count=1;
	  }
	  if(prevbyte!=byte) {
	    if(periods[count]>=lim) {
	      if(small==1 || small==-1)
		fprintf(fp1," *");
	      small=0;
	    } else small=1;
	    if(small) {
	      fprintf(fp1," %d",count);
	      count2++;
	    }
	    count=0;
	    prevbyte=byte;
	  }
	  count++;
	}
	if(small) {
	  fprintf(fp1," %d",count);
	  count2++;
	}
	fprintf(fp1,", count: %d",count2);

	fprintf(fp1,"\n");
	fflush(fp1);
	//}
	fclose(fp1);
      } // if((fp1=fopen
#endif
    } // 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++)

  genbytes+=size;
}

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);
  }
}

#define INPUT_RESSU 0
#define INPUT_DEBUG 1
#define INPUT_FAST 2
#define INPUT_SINGLE 3
#define INPUT_URANDOM 8
#define INPUT_RANDOM 9

static int input=0;

#define GENT_SIZE 2048

#define aDEBUG9 2

char *randomgen[] = { "ressu", "debug","fast","single","4","5","6","7","urandom","random" };

int ressu_genbyte()
{
  static unsigned char gent[GENT_SIZE];
  static unsigned int gent_pos=0;
  unsigned char ch;

  if(input==INPUT_RESSU) { // ressu prod
    ressu_genbytes(sizeof(ch), &ch);
  } else if(input==INPUT_DEBUG) { // ressu debug
    ressu_genbytes_debug(sizeof(ch), &ch);
  } else {
    if(gent_pos==0) {
      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);
      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
    } // if(gent_pos==0
    ch=gent[gent_pos];
    gent_pos=(gent_pos+1)%sizeof(gent);
  }
  return(ch);
}

int ressu_genbyte_limit(int limit)
{
  int c;
  while((c=ressu_genbyte()) >= (256/limit)*limit);
  return(c%limit);
}

int ressu_genshort()
{
  return(ressu_genbyte() | ressu_genbyte()<<8);
}

int ressu_genshort_limit(int limit)
{
  int c;
  while((c=ressu_genshort()) >= (65536/limit)*limit);
  return(c%limit);
}

unsigned int ressu_genint()
{
  return(ressu_genshort() | ressu_genshort()<<16);
}

unsigned int ressu_genint_limit(unsigned long limit)
{
  unsigned int c;
  while((c=ressu_genint()) >= (((unsigned long)65536*65536)/limit)*limit);
  return(c%limit);
}

unsigned long ressu_genlong()
{
  return(((unsigned long)ressu_genint()) | ((unsigned long)ressu_genint())<<32);
}

unsigned long ressu_genlong_limit(unsigned long limit)
{
  unsigned long c;
  while((c=ressu_genlong()) >= (((unsigned long)0xffffffffffffffff)/limit)*limit);
  return(c%limit);
}

unsigned long ressu_gen_limit(unsigned long limit)
{
  if(limit<=256)
    return(ressu_genbyte_limit(limit));
  else if(limit<=65536)
    return(ressu_genshort_limit(limit));
  else if(limit<=(unsigned long)65536*65536)
    return(ressu_genint_limit(limit));
  else if(limit<=(unsigned long)0xffffffffffffffff)
    return(ressu_genlong_limit(limit));
  else
    return(-1);
}

#define MAIN 2

#ifdef MAIN

#include <stdarg.h>

#define aDEBUG31

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

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;
  static size_t stat_line_buf_length;

  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_readable(unsigned long length)
{
  int c, low;
  double length2;
  unsigned char buf10[10];
  // 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;
  }
  stat_line_printf("%s",buf10);
}

#ifdef OLD1

static void stat_line_readable(unsigned long length)
{
  int c, low;
  unsigned char buf10[10];
  // 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%cB", length, units[c]);
      else
        sprintf(buf10,"%ld", length);
      break;
    }
    length/=READABLE_NUMBER_DIVIDER;
    low=1;
  }
  stat_line_printf("%s",buf10);
}

#endif

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;
    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);
}

int help=0;

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

#define aDEBUG38 2

static void utf8getchar(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
  
  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 DEBUG38
  fprintf(stdout,"%s: utf8getchar:",procname);
  fprintf(stdout," string: %s",string);
  fprintf(stdout,", n: %d",n);
  fprintf(stdout,", character: %s",buf);
  fprintf(stdout,"\n");
#endif
}

#define aDEBUG45

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

  digitslen = utf8len(digits);
  word=word2;
  len=0;
  string[0]='\0';

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

  // reverse string
  
  *buf='\0';
  len=0;
  d=utf8len(string);
  for(c=d-1;c>=0;c--) {
    utf8getchar(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 aDEBUG58 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=utf8len(buf);
  f=utf8len(digits);

  for(c=0;c<d;c++) {
    utf8getchar(sizeof(character2),character2,c,buf);
    ok=0;
    for(e=0;e<f;e++) {
      utf8getchar(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 DEBUG58
  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 size=5, zero=1, sspace=0, snewline=1,
  scrlf=1, chars=72, pchars=0, words=0, pwords=0,
  lines=10, plines=0, plinesdigits=5, slineno=1,
  quiet=0, sort=0, unique=0, sample=0;
static unsigned long limit, word;
static unsigned char *digits="0123456789", character[32];
static unsigned char digitstemp[256];
static unsigned char linenobuf[1024];

static void readword(unsigned char *buf)
{
  int d,e;
  unsigned char temp1024[1024];
  
  if(limit!=0) {
    word=0;
    
    if(zero) {
      word=ressu_gen_limit(limit); // include zeroes
      //fprintf(stdout,"(%02ld)",word);
    } else if(limit>=1) {
      while((word=ressu_gen_limit(limit))==0); // skip zeroes
    }
	  
    out_word(sizeof(temp1024),temp1024,digits,word);
	  
    // fill leading zeroes
    
    buf[0]='\0';
    utf8getchar(sizeof(character),character,0,digits);
    for(d=size-utf8len(temp1024);d>0;d--) {
      strcat(buf,character);
    }
	  
    // rest of the number
	  
    strcat(buf,temp1024);
	  
  } else if(digits!=NULL) {
    int digitslen;
	  
    buf[0]='\0';
    digitslen=utf8len(digits);
	  
    // fill whole word digit by digit
	  
    //fprintf(stdout,"[");
    for(d=0;d<size;d++) {
      if(digits[0]=='0' && !zero)
	e=ressu_gen_limit(digitslen-1)+1;
      else
	e=ressu_gen_limit(digitslen);
      utf8getchar(sizeof(temp1024),temp1024,e,digits);
      //fprintf(stdout,"%s",temp1024);
      strcat(buf,temp1024);
    }
    //fprintf(stdout,"]");
  }
}

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

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

static void print_spaces()
{
  if(sspace>=2 &&
       // rand space between double words
     ( (pwords>0 && pwords%sspace==0) ||
     // rand space after linenumber
       (slineno && pwords==0) ) )
    fprintf(stdout," ");

  if(sspace &&
       // space between words
     ( (pwords>0) ||
     // space after linenumber
       (slineno && pwords==0) ) ) 
    fprintf(stdout," ");
}

#ifdef OLD1
static void print_spaces()
{
  if(sspace && pchars>0 && !quiet) {
    if(sspace==3 && pwords%2==0) {
      fprintf(stdout," ");
      pchars++;
    }
    fprintf(stdout," ");
    pchars++;
  }
}
#endif

static void print_word(unsigned char *buf)
{
  if(size!=0) {
    fprintf(stdout,"%*s",size,buf);
  } else {
    fprintf(stdout,"%s",buf);
  }
}

int main(int argc, char *argv[])
{
  int c,d;
 
  procname=argv[0];

  limit=0;
#ifdef DEBUG4
  ccp=cc;
  cc_bytes=0;
#endif
  clockbytes=0;
  for(d=0;d<1024;d++)
    periods[d]=0;

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

      } 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;
	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)>1) {
	  snewline=atoi(argv[c]+9);
	} else if(c+1<argc && atoi(argv[c+1])>1) {
	  snewline=atoi(argv[c+1]);
	  c++;
	} else {
	  snewline=!snewline;
	}

     } 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])) {
	fprintf(stderr,"%s",programname);
	fprintf(stderr,", %s",copyright);
	fprintf(stderr,"\n\n");
	help=1;
	
      } 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++;
	}
	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
	if(size==0)
	  size=1;
	if(size>1024)
	  size=1024;

      } else if(!strncmp("--bits",argv[c],6)) {
	if(*(argv[c]+6)!='\0') {
	  ressu_bits_needed=atoi(argv[c]+6);
	} else if(c+1<argc) {
	  ressu_bits_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;
	
      } 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++;
	}
	words=0;
	
      } else if(!strncmp("-l",argv[c],2)) { // lines
	if(*(argv[c]+2)!='\0') {
	  lines=atoi(argv[c]+2);
	} else if(c+1<argc) {
	  lines=atoi(argv[c+1]);
	  c++;
	}
	if(lines<1)
	  lines=1;

      } else if(!strcmp("-x",argv[c])) {
	digits = "0123456789abcdef";
	size=4;

      } else if(!strcmp("-d",argv[c])) {
	digits = "0123456789";
	size=5;

      } else if(!strcmp("-o",argv[c])) {
	digits = "01234567";
	size=3;

      } else if(!strcmp("-b",argv[c])) {
	digits = "01";
	size=8;

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

      } else if(!strcmp("-11",argv[c])) {
	digits=
	  "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
	size=8;

      } else if(!strcmp("-12",argv[c])) {
	digits=
	  "0123456789";
	size=8;

      } else if(!strcmp("-2",argv[c])) {
	digits=
	  "0123456789" \
	  "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
	  "abcdefghijklmnopqrstuvwxyz" \
	  "_-";
	size=8;

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

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

      } else if(!strcmp("--dnk",argv[c]) || // danish alphabet
		!strcmp("--nor",argv[c])) { // norwegian alphabet
	digits=
	  "ABCDEFGHIJKLMNOPQRSTUVWXYZÆØÅ" \
	  "abcdefghijklmnopqrstuvwxyzæøå";
	size=8;

      } else if(!strcmp("--fin",argv[c]) || // finnish alphabet
		!strcmp("--swe",argv[c])) { // swedish alphabet
	digits=
	  "ABCDEFGHIJKLMNOPQRSTUVWXYZÅÄÖ" \
	  "abcdefghijklmnopqrstuvwxyzåäö";
	size=8;

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

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

      } 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ž";
	size=8;

      } 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
		) {
	digits=
	  "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
	  "abcdefghijklmnopqrstuvwxyz";
	size=8;

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

      } else if(!strcmp("--grc",argv[c])) { // greek alphabet
	digits=
	  "ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ" \
	  "αβγδεζηθικλμνξοπρστυφχψω";
	size=8;
   
      } else if(!strcmp("--cards",argv[c])) {
	digits=
	  "🂡🂢🂣🂤🂥🂦🂧🂨🂩🂪🂫🂭🂮" \
	  "🂱🂲🂳🂴🂵🂶🂷🂸🂹🂺🂻🂽🂾" \
	  "🃁🃂🃃🃄🃅🃆🃇🃈🃉🃊🃋🃍🃎" \
	  "🃑🃒🃓🃔🃕🃖🃗🃘🃙🃚🃛🃝🃞";
	size=1;

      } 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;

      } 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 || utf8len(digits)<2) {
	  fprintf(stderr,"%s: not enough digits \"%s\"\n",procname,argv[c]);
	  help = 1;
	}
	size=1;

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

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

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

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

      } 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]);
	help = 1;

      }
    } else {
      help = 1;

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

  if(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;
#ifdef STAT_LINE_ANIM
    long int secs, prev_secs=-1;
    int crs=0;
#endif

    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("\rblk %d, written ",c);
	stat_line_readable((unsigned long)c*DLIM*BLKSIZE);
	
	if(c>0) {
	  stat_line_printf(", ");
	  stat_line_readable((unsigned long)((double)c*DLIM*BLKSIZE/(secondsnow-secondsstart)) );
	  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
	
#ifdef STAT_LINE_ANIM
	unsigned char cursor[4] = { '|', '/', '-', '\\' };

	fprintf(stderr," ");
#endif
	for(d=0;d<DLIM;d++) {
#ifdef STAT_LINE_ANIM
	  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;
	  }
#endif
	  if(input==INPUT_RESSU) // ressu prod
	    ressu_genbytes(sizeof(buffer),buffer);
	  else if(input==INPUT_DEBUG) // ressu debug
	    ressu_genbytes_debug(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);
	  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;
#ifdef STAT_LINE_ANIM
	fprintf(stderr,"\b \b");
	prev_secs=-1;
#endif
      }
      fclose(fp1);
    } // if((fp1=fopen

    // remove last status line
    
    stat_line_begin();
    stat_line_printf("Done!");
    stat_line_end();
    fprintf(stdout,"\n");
    fflush(stdout);
    exit(0);
  } // 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=utf8len(wordbuf);
  }

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

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

  // count words
  
  while((chars>0 && pchars+size < chars) ||
	(words>0 && pwords < words)) {

    linew++;

    if(sspace>=2 &&
       ( (pwords>0 && pwords%sspace==0) ||
	 (slineno && pwords==0) ) )
      pchars++;
    
    if(sspace &&
       ( (pwords>0) ||
	 (slineno && pwords==0) ) ) 
      pchars++;

    pchars+=size;
    pwords++;
  }

  unsigned long wordvalues=1;

  for(c=0;c<size;c++) {
    if(wordvalues!=0 && wordvalues*utf8len(digits)<(unsigned long)65536*65536*65536) {
      wordvalues*=utf8len(digits);
    } else {
      wordvalues=0;
    }
  }

  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)",utf8len(digits),wordvalues);
      fprintf(stderr,", words:%d\n",linew);
      help=1;
      lines=0;
    }
  }

  // small words as single ressu_gen_limit()
  
  if(limit==0 && wordvalues>0)
    limit=wordvalues;

  if(stats) {
    fprintf(stdout,"randomsource: %s",randomgen[input]);
    fprintf(stdout,", size: %d",size);
    fprintf(stdout,", linew: %d",linew);
    fprintf(stdout,", pchars: %d",pchars);
    fprintf(stdout,", pwords: %d",pwords);
    fprintf(stdout,", digitslen: %d",utf8len(digits));
    if(wordvalues>0)
      fprintf(stdout,", wordvalues: %lu",wordvalues);
    if(limit>0)
      fprintf(stdout,", limit: %lu",limit);
    fprintf(stdout,"\n");
  }
  
  if(help) {
    struct helpline {
      char *command;
      char *text;
    } helplines[] = {
      { "newressu -d", "print random decimal digits" },
      { "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 -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 -iаэыуояеёюи", "print russian vowels" },
      { "newressu --ressu", "use randomness from ressu (default)" },
      { "newressu --debug", "use randomness from debugging ressu" },
      { "newressu --fast", "use randomness from fast ressu" },
      { "newressu --single", "use randomness from single round of ressu" },
      { "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(stdout,"*");
    fprintf(stdout,"\n");
    if(lines>1)
      lines=1; // print one line below help as a sample
  }

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

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

    if(!sort) { // also 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);
	}

	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);
	}
	pwords++;
      } // while(pwords<linew) {
    } // if(!sort)

    if(!quiet) {
      fprintf(stdout,"\n");
    }
    plines++;
    if(!quiet && snewline>1 &&
       plines<lines &&
       plines%snewline==0)
      fprintf(stdout,"\n");

    // all needed lines printed?

    if(plines >= lines)
      break;

  } // for(;;)
  fflush(stdout);
}

#endif // MAIN