Ressu 2.1 Lottonumerot ja muuta

Kaikki oikeudet pidätetään. Edellisessä Ressu 2.0 postissa esiteltyyn toiminnallisuuteen on tehty seuraavia muutoksia: asiallinen lottonumeroiden tulostus, tuotantoversio ressusta (ilman debukkailuja), debukkailuversio ressusta erikseen, digits merkkijonon perusteella tehtävän tulosteen nopeutus, virheiden korjauksia, ja yleistä luettavuuden parantamista. Koko ohjelma on kopypastattavissa postin lopussa.

Käyn seuraavassa läpi valitut osat uudesta ohjelmasta. Ensin ns tuotantoversio ressusta. Tuotantoversiossa on vielä –stat komentoriviparametrillä toimiva debug rivi, mutta lähemmäksi kuljetaan: Toisaalta tuotantoversiossa ei enää ole noita aloituspaikan varmistamisen debug while luuppeja, eikä se luo enää tiedostoa. –debug versiosta luupit ja tiedostot vielä löytyvät.

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

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

int ressu_bits_needed=RESSU_BITS_NEEDED;
int ressut_bytes = RESSUT_BYTES;

int stats=0;

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

Edellinen ensimmäinen rutiini ressu_clockbyte() palauttaa kellon millisekuntien alimmat bitit kutsujalle (seuraava rutiini).

Seuraavaksi ressun ydin, eli yhden ressu kierroksen ajo: siltä varalta ettet ole vielä tästä lukenut, se koostuu kahdesta vaiheesta, joista ensimmäisessä (for(d=0)) puskurin jokaisen merkin bittejä siirretään ja xorataan puskurin merkki kellojonolla (edellinen ressu_clockbyte() rutiini). Toisessa vaiheessa (toinen for(d=0)) käydään uudelleen puskuri kokonaan läpi ja vaihdetaan jokainen merkki satunnaisen merkin kanssa. Nämä kaksi vaihetta tehdään kahdeksan kertaa (for(c=0)), eli jokaisella puskurin merkin bitillä. If prevbyte -iffillä kerätään tilastoa ns vaihtelusta, ja tilaston perusteella lasketaan seuraavassa vaiheessa teoreettisia satunnaisbittejä. Teoreettisten satunnaisbittien perusteella lasketaan kuinka monta kierrosta (round) tarvitaan puskurin satunnaistamiseen.

void ressu_genbytes_fast(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;
    }
  }
}

Seuraavalla rutiinilla täytetään asiakkaan toimittama puskuri satunnaisbiteillä. Satunnaisbitit palautetaan ressut-taulusta. Näin asiakas ei saa koko satunnaisbittigeneraattorin rakennetta kyselyllään. Kun ressut taulu on käytetty if(ressut_pos) iffillä haetaan ressut taulu uudestaan “täyteen”. Taulun täyttämiseksi suoritetaan ressu_genbytes_fast() rutiinia niin monta kertaa että tilastossa palautettujen “pienien” ketjujen määrä ylittää tarvittavan bittimäärän. Tässä tuotantoversiossa fast() rutiinia ajetaan vähintään kaksi kertaa. Lisäksi suorituskierroksia kasvattaa minimikellojonon pituus, jolla saadaan uniikkeja puskureita.

void ressu_genbytes(int size, unsigned char *buffer) // 6.5.2021 JariK
{
  int c,d,e;
  static int ressut_first=1,
    ressut_pos = 0,
    ressut_f = 0;
  static unsigned char ressut[RESSUT_BYTES];
  
  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, rounds=0;
      int lim, lim1, lim2;

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

	ressu_genbytes_fast(ressut_bytes,ressut);

	lim=1;
	
	int ok=1, f, prevf=-1;

	while(ok==1) {
	  ok=0;
	  f=-1;
	  for(e=0;e<1024;e++) {
	    if(periods[e]>lim && (f==-1 || periods[e]<f)) {
	      f=periods[e];
	    }
	  }
	  if(prevf==-1 || (f!=-1 && f<=(double)prevf*1.6)) {
	    lim=f;
	    prevf=f;
	    ok=1;
	  }
	} // while(ok==1)

	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];
	}
	lim=f;
	lim2=lim;
	
	int prev_rndbits=rndbits;

	rndbits=0;
	for(e=0;e<1024;e++) {
	  if(periods[e]>0 && periods[e]<lim) {
	    rndbits+=periods[e];
	  }
	}
	if(rndbits==0 || prev_rndbits == rndbits) { // restart
	  clockbytes=0;
	  for(e=0;e<1024;e++)
	    periods[e]=0;
	  rndbits=0;
	}
      } // for(d=0;
      if(stats) {
	fprintf(stderr,"round: %d",d);
	for(e=0;e<1024;e++) {
	  if(periods[e]>0)
	    fprintf(stderr," %d:%lu",e,periods[e]);
	}
	fprintf(stderr," lim:%d",lim1);
	fprintf(stderr," lim2:%d",lim2);
	fprintf(stderr," clockbytes:%ld",clockbytes);
	fprintf(stderr," rndbits:%d",rndbits);
	fprintf(stderr,"\n");
      }
    } // 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;
}

Seuraavana debukkiversio jossa on nuo aloituspaikan varmentavat debug while:t ja puskurin ja tilastojen tulostus tiedostoon. Näitä tarvitaan sen takia että tuon uniikkeja puskureita tuottavan kellojonon pituus on vielä hakusessa. Jos siihen tulisi joku ratkaisu kierrosmäärää voisi ehkä vähentää. Ennen debukkiversiota rivi newressu.deb tiedostosta:

4db49f6a76c06d972a7588c443cfc43e16c2d9c69013e98ccc28b26b2c49a0c0, ressut_bytes: 4096, ressu_bits_needed: 128, ressu_min_rounds: 2, clockbytes: 131061, fluctuations: 1:13 2:14 3:18 4:12 5:13 6:12 7:14 8:21 9:9 10:25 11:3325 12:424 13:1275 14:5139, total: 131061, limit1: 25, limit2: 424, limit: 424, skipped: 11:3325 12:424 13:1275 14:5139, counted: 1:13 2:14 3:18 4:12 5:13 6:12 7:14 8:21 9:9 10:25, rndbits: 151, rounds: 4, small chains: 4 10 * 7 5 * 1 5 * 3 4 * 7 * 4 * 10 9 * 6 3 * 10 * 8 * 10 * 5 3 * 9 3 * 10 8 * 2 2 * 10 * 7 * 1 10 * 10 * 2 1 * 3 3 * 5 1 * 10 * 4 3 * 5 9 * 7 * 8 8 * 8 9 * 9 3 * 2 4 * 4 5 * 5 5 * 9 1 8 * 7 1 * 3 6 * 2 1 10 * 6 8 * 5 10 * 10 2 * 1 8 * 10 * 3 7 * 10 * 10 1 9 * 1 9 * 7 7 * 3 * 5 3 * 8 * 2 * 2 3 * 7 6 * 8 2 * 10 5 * 6 * 3 * 4 6 * 3 * 6 7 * 6 10 * 10 4 * 10 4 * 10 4 * 10 3 * 3 * 4 * 8 8 * 4 2 * 10 * 2 10 * 5 8 * 2 * 1 1 * 6 10 * 5 * 7 7 * 8 8 * 2 * 1 10 * 4 2 * 8 6 * 8 7 * 8 6 * 8 8 * 6 8 * 7 9 *, count: 151

Ensimmäistä heksamerkkijonoa käytetään tuplapuskureiden etsimiseen, se on dumppi puskurin alusta. Lopun tähtimerkkijonossa listataan käytetyt pienet ketjut. Tuplapuskureita löytyy ainakin 1024 merkkiä pitkällä puskurilla jos bittien määrä on 1 ja minimikierrosten määrä on 1. Eli ainakin bittien määrän on oltava kohtuullisen iso.

Ja debukkiversio (käytetään –debug kytkimellä):

#define DEBUG4 2

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

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_fast_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;
  static int ressut_first=1,
    ressut_pos = 0,
    ressut_f = 0;
  static unsigned char ressut[RESSUT_BYTES];
  
  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, rounds=0;
      int lim, lim1, lim2;

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

	rounds++;
	
	ressu_genbytes_fast_debug(ressut_bytes,ressut);

	lim=1;
	
	int ok=1, f, prevf=-1;

	while(ok==1) {
	  ok=0;
	  f=-1;
	  for(e=0;e<1024;e++) {
	    if(periods[e]>lim && (f==-1 || periods[e]<f)) {
	      f=periods[e];
	    }
	  }
	  if(prevf==-1 || (f!=-1 && f<=(double)prevf*1.6)) {
	    lim=f;
	    prevf=f;
	    ok=1;
	  }
	} // while(ok==1)

	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];
	}
	lim=f;
	lim2=lim;
	
	int prev_rndbits=rndbits;

	rndbits=0;
	for(e=0;e<1024;e++) {
	  if(periods[e]>0 && periods[e]<lim) {
	    rndbits+=periods[e];
	  }
	}

	if(rndbits==0 || prev_rndbits == rndbits) { // restart
	  clockbytes=0;
	  for(e=0;e<1024;e++)
	    periods[e]=0;
	  ccp=cc;
	  cc_bytes=0;
	  rndbits=0;
	}
      } // for(d=0;

#ifdef DEBUG4
      FILE *fp1;
      if((fp1=fopen("newressu.deb","a"))!=NULL) {

	for(d=0;d<32;d++)
	  fprintf(fp1,"%02x",ressut[d]);

	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(d=0;d<1024;d++) {
	  if(periods[d]!=0) {
	    fprintf(fp1," %d:%lu",d,periods[d]);
	    total+=(periods[d]*d);
	  }
	}
	fprintf(fp1,", total: %lu",total);
	fprintf(fp1,", limit1: %d",lim1);
	fprintf(fp1,", limit2: %d",lim2);
	fprintf(fp1,", limit: %d",lim);

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

	int prevbyte=-1, count=0, count2=0;
	unsigned char byte, small=-1;
	fprintf(fp1,", small chains:");
	for(d=0;d<cc_bytes;d++) {
	  byte=cc[d];
	  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;
}

Seuraavassa lottonumeroiden tulostukseen tai satunnaislukurivin lajitteluun liittyvät rutiinit: clear() rutiini tyhjentää lajittelupuskurina käytetyn merkkijonon. add() lisää sanan (word) merkkijonoon. Lisäyksessä varmistetaan että sana on uniikki ja että se lajitellaan. Seuraavalla get() rutiinilla luetaan lajitellut sanat sana kerrallaan.

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

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

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

int line_dump(unsigned char *string)
{
  int e,n,ok,count;
  unsigned char *p,*q;

  p=string;
  n=0;
  while(*p!='\0') {
    fprintf(stdout," %d:",n++);
    while(*p!=' ' && *p!='\0') // find next space
      putchar(*p++);
    while(*p==' ' && *p!='\0') // find next non space
      p++;
    e++;
  }
  fprintf(stdout,"\n");
}

Seuraavaksi valittuja uudistuksia pääohjelmasta: –lotto –unique ja –sort kytkimet pääohjelmasta. Unique:ta ei ole vielä toteutettu. –lotto ja –sort ovat samanlaisia.

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

Seuraava laskee kuinka monta sanaa (word) riville tulostetaan:

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

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

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

    linew++;
    
    if(sspace && pchars>0 & !quiet) {
      if(sspace==3 && pwords%2==0)
	pchars++;
      pchars++;
    }
	
    pchars+=size;
    pwords++;
  }

Seuraava laskee kuinka monta erilaista arvoa sana voi saada. Tätä tarvitaan virheilmoituksia varten:

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

Tässä optimointi, joka korvaa lyhyen sanan useamman char_limit() haun yhdellä gen_limit() toiminnolla.

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

Seuraava on ei lotto versio tulostuksesta:

      pchars=0;
      pwords=0;

      while(pwords<linew) {
	if(limit!=0) {
	  word=0;
	  
	  if(zero) {
	    word=ressu_gen_limit(limit); // include zeroes
	    //fprintf(stdout,"(%lo)",word);
	  } else if(limit>=1) {
	    while((word=ressu_gen_limit(limit))==0); // skip zeroes
	  }
	  
	  out_word(sizeof(wordbuf3),wordbuf3,digits,word);
	  
	  // fill leading zeroes
	  
	  wordbuf[0]='\0';
	  utf8getchar(sizeof(character),character,0,digits);
	  for(d=size-utf8len(wordbuf3);d>0;d--) {
	    strcat(wordbuf,character);
	  }
	  
	  // rest of the number
	  
	  strcat(wordbuf,wordbuf3);
	  
	} else if(digits!=NULL) {
	  int digitslen;
	  
	  wordbuf[0]='\0';
	  digitslen=utf8len(digits);
	  
	  // fill whole word digit by digit
	  
	  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(wordbuf3),wordbuf3,e,digits);
	    //fprintf(stdout,"(%s)",wordbuf3);
	    strcat(wordbuf,wordbuf3);
	  }
	}

	// in beginning of line, print line number

	if(pchars==0 && !quiet && slines) {
	  sprintf(wordbuf2,"%0*d",plinesdigits,plines);
	  fprintf(stdout,"%s",wordbuf2);
	  pchars +=strlen(wordbuf2);
	  fprintf(stdout," ");
	  pchars++;
	}
	
	// want to print spaces between "words"?
	
	if(sspace && pchars>0 && !quiet) {
	  if(sspace==3 && pwords%2==0) {
	    fprintf(stdout," ");
	    pchars++;
	  }
	  fprintf(stdout," ");
	  pchars++;
	}
	
	// print word
	
	if(size!=0) {
	  if(!quiet)
	    fprintf(stdout,"%*s",size,wordbuf);
	  pchars += size;
	  pwords++;
	} else {
	  if(!quiet)
	    fprintf(stdout,"%s",wordbuf);
	  pchars += strlen(wordbuf);
	  pwords++;
	}
      } // while(pwords<linew) {
    } else { // if(!sort)

Seuraavassa lotto versio tulostuksesta: erona on se, että ensin kerätään sanat, ja sitten toisessa vaiheessa ne tulostetaan: Edellisessä ei lotto versiossa oli vain yksi vaihe joka teki kaiken.

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

      // fetch and save words on this row
      while(pwords<linew) {
	if(limit!=0) {
	  word=0;
	  
	  if(zero) {
	    word=ressu_gen_limit(limit); // include zeroes
	  } else if(limit>=1) {
	    while((word=ressu_gen_limit(limit))==0); // skip zeroes
	  }
	  
	  out_word(sizeof(wordbuf3),wordbuf3,digits,word);
	  
	  // fill leading zeroes
	  
	  wordbuf[0]='\0';
	  utf8getchar(sizeof(character),character,0,digits);
	  for(d=size-utf8len(wordbuf3);d>0;d--) {
	    strcat(wordbuf,character);
	  }
	  
	  // rest of the number
	  
	  strcat(wordbuf,wordbuf3);
	  
	} else if(digits!=NULL) {
	  int digitslen;
	  
	  wordbuf[0]='\0';
	  digitslen=utf8len(digits);
	  
	  // fill whole word digit by digit
	  
	  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(wordbuf3),wordbuf3,e,digits);
	    strcat(wordbuf,wordbuf3);
	  }
	}

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

      pchars=0;
      pwords=0;

      // print words on this row
      while(pwords<linew) {

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

	// in beginning of line, print line number

	if(pchars==0 && !quiet && slines) {
	  sprintf(wordbuf2,"%0*d",plinesdigits,plines);
	  fprintf(stdout,"%s",wordbuf2);
	  pchars +=strlen(wordbuf2);
	  fprintf(stdout," ");
	  pchars++;
	}
	
	// want to print spaces between "words"?
	
	if(sspace && pchars>0 && !quiet) {
	  if(sspace==3 && pwords%2==0) {
	    fprintf(stdout," ");
	    pchars++;
	  }
	  fprintf(stdout," ");
	  pchars++;
	}
	
	// print word
	
	if(size!=0) {
	  if(!quiet)
	    fprintf(stdout,"%*s",size,wordbuf);
	  pchars += size;
	  pwords++;
	} else {
	  if(!quiet)
	    fprintf(stdout,"%s",wordbuf);
	  pchars += strlen(wordbuf);
	  pwords++;
	}
      } // while(pwords<linew) {

Vielä esimerkki suomalaisista lottoriveistä: –sort ja –lotto toiminnat ovat samanlaisia, voit lajitella mitkä tahansa rivit numerojärjestykseen.

./newressu --lim41 -w7 --zero --sort
00000  02 05 13 15 26 38 39
00001  04 13 15 24 28 36 39
00002  09 11 28 29 30 35 40
00003  04 06 13 15 20 21 36
00004  07 10 11 20 23 27 30
00005  02 03 11 15 25 32 36
00006  01 07 09 24 28 31 32
00007  06 13 15 19 30 32 35
00008  02 03 08 17 28 29 31
00009  17 23 24 26 27 36 39

Ja lopuksi ohjelma kokonaisuudessaan:

#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>

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

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

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

int ressu_bits_needed=RESSU_BITS_NEEDED;
int ressut_bytes = RESSUT_BYTES;

int stats=0;

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

void ressu_genbytes_fast(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(int size, unsigned char *buffer) // 6.5.2021 JariK
{
  int c,d,e;
  static int ressut_first=1,
    ressut_pos = 0,
    ressut_f = 0;
  static unsigned char ressut[RESSUT_BYTES];
  
  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, lim2;

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

	ressu_genbytes_fast(ressut_bytes,ressut);

	lim=1;
	
	int ok=1, f, prevf=-1;

	while(ok==1) {
	  ok=0;
	  f=-1;
	  for(e=0;e<1024;e++) {
	    if(periods[e]>lim && (f==-1 || periods[e]<f)) {
	      f=periods[e];
	    }
	  }
	  if(prevf==-1 || (f!=-1 && f<=(double)prevf*1.6)) {
	    lim=f;
	    prevf=f;
	    ok=1;
	  }
	} // while(ok==1)

	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];
	}
	lim=f;
	lim2=lim;
	
	int prev_rndbits=rndbits;

	rndbits=0;
	for(e=0;e<1024;e++) {
	  if(periods[e]>0 && periods[e]<lim) {
	    rndbits+=periods[e];
	  }
	}
	if(rndbits==0 || prev_rndbits == rndbits) { // restart
	  clockbytes=0;
	  for(e=0;e<1024;e++)
	    periods[e]=0;
	  rndbits=0;
	}
      } // for(d=0;
      if(stats) {
	fprintf(stderr,"round: %d",d);
	for(e=0;e<1024;e++) {
	  if(periods[e]>0)
	    fprintf(stderr," %d:%lu",e,periods[e]);
	}
	fprintf(stderr," lim:%d",lim1);
	fprintf(stderr," lim2:%d",lim2);
	fprintf(stderr," clockbytes:%ld",clockbytes);
	fprintf(stderr," rndbits:%d",rndbits);
	fprintf(stderr,"\n");
      }
    } // 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;
}

#define DEBUG4 2

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

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_fast_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;
  static int ressut_first=1,
    ressut_pos = 0,
    ressut_f = 0;
  static unsigned char ressut[RESSUT_BYTES];
  
  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, rounds=0;
      int lim, lim1, lim2;

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

	rounds++;
	
	ressu_genbytes_fast_debug(ressut_bytes,ressut);

	lim=1;
	
	int ok=1, f, prevf=-1;

	while(ok==1) {
	  ok=0;
	  f=-1;
	  for(e=0;e<1024;e++) {
	    if(periods[e]>lim && (f==-1 || periods[e]<f)) {
	      f=periods[e];
	    }
	  }
	  if(prevf==-1 || (f!=-1 && f<=(double)prevf*1.6)) {
	    lim=f;
	    prevf=f;
	    ok=1;
	  }
	} // while(ok==1)

	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];
	}
	lim=f;
	lim2=lim;
	
	int prev_rndbits=rndbits;

	rndbits=0;
	for(e=0;e<1024;e++) {
	  if(periods[e]>0 && periods[e]<lim) {
	    rndbits+=periods[e];
	  }
	}

	if(rndbits==0 || prev_rndbits == rndbits) { // restart
	  clockbytes=0;
	  for(e=0;e<1024;e++)
	    periods[e]=0;
	  ccp=cc;
	  cc_bytes=0;
	  rndbits=0;
	}
      } // for(d=0;

#ifdef DEBUG4
      FILE *fp1;
      if((fp1=fopen("newressu.deb","a"))!=NULL) {

	for(d=0;d<32;d++)
	  fprintf(fp1,"%02x",ressut[d]);

	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(d=0;d<1024;d++) {
	  if(periods[d]!=0) {
	    fprintf(fp1," %d:%lu",d,periods[d]);
	    total+=(periods[d]*d);
	  }
	}
	fprintf(fp1,", total: %lu",total);
	fprintf(fp1,", limit1: %d",lim1);
	fprintf(fp1,", limit2: %d",lim2);
	fprintf(fp1,", limit: %d",lim);

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

	int prevbyte=-1, count=0, count2=0;
	unsigned char byte, small=-1;
	fprintf(fp1,", small chains:");
	for(d=0;d<cc_bytes;d++) {
	  byte=cc[d];
	  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);
  }
}

int input=0;

#define GENT_SIZE 2048
#define USE_RANDOM 2

#define aDEBUG9 2

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

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

  if(input==0) { // ressu prod
    ressu_genbytes(sizeof(ch), &ch);
  } else if(input==1) { // ressu debug
    ressu_genbytes_debug(sizeof(ch), &ch);
  } else {
    if(gent_pos==0) {
      if(input==2) { // ressu_fast
	clockbytes=0;
	for(c=0; c<2 ||
	  clockbytes < RESSU_MIN_CLOCKBYTES;c++) {
	  ressu_genbytes_fast(sizeof(gent),gent);
	}
      } else if(input==3) { // ressu_single
	ressu_genbytes_fast(sizeof(gent),gent);
      } else if(input==8) { // urandom
	readfile_xor(sizeof(gent),gent,"/dev/urandom");
#ifdef USE_RANDOM
      } else if(input==9) { // random
	readfile_xor(sizeof(gent),gent,"/dev/random");
#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

int help=0;

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

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

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

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
}

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

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

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

void line_dump(unsigned char *string)
{
  int e,n;
  unsigned char *p;

  p=string;
  n=0;
  while(*p!='\0') {
    fprintf(stdout," %d:",n++);
    while(*p!=' ' && *p!='\0') // find next space
      putchar(*p++);
    while(*p==' ' && *p!='\0') // find next non space
      p++;
    e++;
  }
  fprintf(stdout,"\n");
}

#define aDEBUG86 2
#define aDEBUG87 2
#define aDEBUG88 2
#define aDEBUG89 2

int main(int argc, char *argv[])
{
  int c, d, e, size=5, sspace=0,
    chars=72, pchars=0, words=0, pwords=0,
    lines=10, plines=0, plinesdigits=5, slines=1,
    quiet=0, zero=1, sort=0, unique=0;
  unsigned char *digits="0123456789",
    digitstemp[256], character[32];
  unsigned long limit;

  procname=argv[0];

#ifdef KAL
  c=0;
  for(;;) {
    unsigned char buffer[2048];
    ressu_genbytes(sizeof(buffer),buffer);
  }
#endif
  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])) {
	slines=!slines;

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

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

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

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

      } 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(!strcmp("--rand",argv[c])) {
	digits = "0123456789";
	sspace=3;
	slines=1;
	words=10;
	chars=0;
	limit=100000;
	//size=5;
	//lines=20000;
	
      } 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=0;
      } else if(!strcmp("--debug",argv[c])) {
	input=1;
      } else if(!strcmp("--fast",argv[c])) {
	input=2;
      } else if(!strcmp("--single",argv[c])) {
	input=3;
      } else if(!strcmp("--urandom",argv[c])) {
	input=8;
#ifdef USE_RANDOM
      } else if(!strcmp("--random",argv[c])) {
	input=9;
#endif
      } else {
	fprintf(stderr,"%s: invalid option %s\n",procname,argv[c]);
	help = 1;
      }
    } else {
      help = 1;
    } // if(!strncmp
  } // for(c=0

  unsigned long word;
  unsigned char wordbuf[1024];
  unsigned char wordbuf2[1024];
  unsigned char wordbuf3[1024];

  // get linenumber length in digits

  out_word(sizeof(wordbuf),wordbuf,"0123456789",lines-1);
  plinesdigits=strlen(wordbuf);
  if(plinesdigits<5)
    plinesdigits=5;

  // 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 && slines) {
    sprintf(wordbuf2,"%0*d",plinesdigits,plines);
    pchars += strlen(wordbuf2);
    pchars++;
  }

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

    linew++;
    
    if(sspace && pchars>0 && !quiet) {
      if(sspace==3 && pwords%2==0)
	pchars++;
      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);
    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;
  
  for(;;) {

    if(!sort) { // also lotto

      pchars=0;
      pwords=0;

      while(pwords<linew) {
	if(limit!=0) {
	  word=0;
	  
	  if(zero) {
	    word=ressu_gen_limit(limit); // include zeroes
	    //fprintf(stdout,"(%lo)",word);
	  } else if(limit>=1) {
	    while((word=ressu_gen_limit(limit))==0); // skip zeroes
	  }
	  
	  out_word(sizeof(wordbuf3),wordbuf3,digits,word);
	  
	  // fill leading zeroes
	  
	  wordbuf[0]='\0';
	  utf8getchar(sizeof(character),character,0,digits);
	  for(d=size-utf8len(wordbuf3);d>0;d--) {
	    strcat(wordbuf,character);
	  }
	  
	  // rest of the number
	  
	  strcat(wordbuf,wordbuf3);
	  
	} else if(digits!=NULL) {
	  int digitslen;
	  
	  wordbuf[0]='\0';
	  digitslen=utf8len(digits);
	  
	  // fill whole word digit by digit
	  
	  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(wordbuf3),wordbuf3,e,digits);
	    //fprintf(stdout,"(%s)",wordbuf3);
	    strcat(wordbuf,wordbuf3);
	  }
	}

	// in beginning of line, print line number

	if(pchars==0 && !quiet && slines) {
	  sprintf(wordbuf2,"%0*d",plinesdigits,plines);
	  fprintf(stdout,"%s",wordbuf2);
	  pchars +=strlen(wordbuf2);
	  fprintf(stdout," ");
	  pchars++;
	}
	
	// want to print spaces between "words"?
	
	if(sspace && pchars>0 && !quiet) {
	  if(sspace==3 && pwords%2==0) {
	    fprintf(stdout," ");
	    pchars++;
	  }
	  fprintf(stdout," ");
	  pchars++;
	}
	
	// print word
	
	if(size!=0) {
	  if(!quiet)
	    fprintf(stdout,"%*s",size,wordbuf);
	  pchars += size;
	  pwords++;
	} else {
	  if(!quiet)
	    fprintf(stdout,"%s",wordbuf);
	  pchars += strlen(wordbuf);
	  pwords++;
	}
      } // while(pwords<linew) {
    } else { // if(!sort)

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

      // fetch and save words on this row
      while(pwords<linew) {
	if(limit!=0) {
	  word=0;
	  
	  if(zero) {
	    word=ressu_gen_limit(limit); // include zeroes
	  } else if(limit>=1) {
	    while((word=ressu_gen_limit(limit))==0); // skip zeroes
	  }
	  
	  out_word(sizeof(wordbuf3),wordbuf3,digits,word);
	  
	  // fill leading zeroes
	  
	  wordbuf[0]='\0';
	  utf8getchar(sizeof(character),character,0,digits);
	  for(d=size-utf8len(wordbuf3);d>0;d--) {
	    strcat(wordbuf,character);
	  }
	  
	  // rest of the number
	  
	  strcat(wordbuf,wordbuf3);
	  
	} else if(digits!=NULL) {
	  int digitslen;
	  
	  wordbuf[0]='\0';
	  digitslen=utf8len(digits);
	  
	  // fill whole word digit by digit
	  
	  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(wordbuf3),wordbuf3,e,digits);
	    strcat(wordbuf,wordbuf3);
	  }
	}

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

      pchars=0;
      pwords=0;

      // print words on this row
      while(pwords<linew) {

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

	// in beginning of line, print line number

	if(pchars==0 && !quiet && slines) {
	  sprintf(wordbuf2,"%0*d",plinesdigits,plines);
	  fprintf(stdout,"%s",wordbuf2);
	  pchars +=strlen(wordbuf2);
	  fprintf(stdout," ");
	  pchars++;
	}
	
	// want to print spaces between "words"?
	
	if(sspace && pchars>0 && !quiet) {
	  if(sspace==3 && pwords%2==0) {
	    fprintf(stdout," ");
	    pchars++;
	  }
	  fprintf(stdout," ");
	  pchars++;
	}
	
	// print word
	
	if(size!=0) {
	  if(!quiet)
	    fprintf(stdout,"%*s",size,wordbuf);
	  pchars += size;
	  pwords++;
	} else {
	  if(!quiet)
	    fprintf(stdout,"%s",wordbuf);
	  pchars += strlen(wordbuf);
	  pwords++;
	}
      } // while(pwords<linew) {
    } // if(!sort)

    // line full?
    
    if(!quiet)
      fprintf(stdout,"\n");
    plines++;
    if(sspace==3 && plines<lines && plines%5==0)
      fprintf(stdout,"\n");

    // all needed lines printed?
    
    if(plines >= lines)
      break;

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

#endif // MAIN