Newressu(4.03): in search for randomness. is ressu one time pad material, possible heart to terttu, new utility terttuutil1 (v0.16) to query terttu format files, “bad for randomness” -bugfix, new source for randomness ressutwist, copy-reverse rewrite, sudokues everywhere

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

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

Edit: korjailen raporttia vielä seuraavien viikkojen aikana

Edit: olen jatkanut ressun tutkimista ja lisäilen illan mittaan uusia kappaleita raporttiin. Lisätty kaksi uutta tilastoohjelmaa, uudelleenkirjoituskappaleet stat_line funktioista ja sample() rutiinista. Toinen tilastoohjelma sisältää tertun ytimelle toivottavasti sopivan kenttien ja tietueiden hallinnan pohjan.

Edit Lisätty loppuun kappaleita exfat bugin korjaamisesta. Bugi korjautui osittain lisäämällä tulostettavaan tiedostoon fflush ja sync.

Edit: vähennetty stat_line:n “nykimistä”. Numerokentät (readable) ovat nyt useimmiten neljän pituisia.

Edit: Lisää exfat ongelman tutkimista. Lisätty ohjelma newressutest12, joka kirjoittaa kiinteän pituisia tietueita ja lukee ja tarkistaa tietueet. (vrt newressutest10)

Edit: Muutettu komentoriviparametrejä samankaltaisiksi. Ennen parametrit olivat esimerkiksi -b, –bytes, –lines, ne olivat erilaisia joka ohjelmassa. Nyt ne on samanlaisia eli –filesize 1g, –binsize 10, –linesize 21, –lines 1m, –blocksize 1024, –blocks 1m jne. Muutenkin näiden parametrien laskentaa ja tarkistusta on järkeistetty. Huomaa, että raportin komentoesimerkeissä vanhat versiot eivät enää toimi. Myös ,/newressu –sample –filesize 1g ja muut koko-parametrit toimivat.

Edit: Lisätty stream_open -funktion avaimen muodostukseen stream_keyn lisäksi cvar. Tämä tekee uudesta stream_key:stä satunnaisemman. Näin samoja stream_key avaimia ei kierretä uudelleen ja uudelleen. Jos jostain syystä päädymme samaan stream_key:in toistamiseen, seuraavan avaimen laskemiseen käytetään erilaista cvar kenttää, jolloin saman sarjan läpikäyminen loppuu heti.

Edit: lisätty newressu ohjelmaan –columns kytkin, jolla saa sarakenumerot sarakkeiden yläpuolelle.

Edit Vähennetty lisää stat_line:n nykimistä. Nyt myös left ja end kentät heiluvat vähemmän.

Edit: lisätty sample() satunnaislukutiedoston muodostukseen grep:illä tulostettava automaattinen ongelmien lista.

Edit: Lisätty –som komento-optio, jolla tulostetaan somalian merkkijonoja. Ensimmäisessä versiossa ei

Edit: Lisätty integrity testi, jossa aina (DEBUG72) ./newressu ajon alussa kirjoitetaan useimmilla –kytkimillä rivejä newressutest14.rnd* tiedostoon. Jokaiselle päivälle luodaan oma tiedostonsa.

Edit: Vielä listaus ressun ytimestä ilman copy/reverseä ja tilastollisia laskelmia, jotta nopeasti ymmärrät mikä tämä ressun sydän on. Seuraavassa on yksi kahdeksan bitin kierros ressua. Ydintä ajetaan kunnes kaikki tilastolliset satunnaisbittien määrän arvioinnit täyttyvät:

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

void ressu_genbytes_single_do(int size, unsigned char *buffer) // JariK 2013
{
  int c, d;
  unsigned char e;
  static int f = 0;
  
  for(c = 0; c < 8; c++) {
    for(d = 0; d < size; d++) {
      e = buffer[d];
      e = RL8(e, 1); // rotate byte left 1 bits
      //e = RL8(e, 3); // rotate byte left 3 bits
      //e = RR8(e, 1); // rotate byte right 1 bits
      buffer[d] = e ^ ressu_clockbyte();
    }
    for(d = 0; d < size; d++) {
      f = (f + buffer[d] + 2) % size; // + 2 JariK 2022
      e = buffer[d];
      buffer[d] = buffer[f];
      buffer[f] = e;
    }
  }
}

Asiasta toiseen tuo edellisen koodin + 2 on ns + 2 bugi, joka korjataan tässä raportissa. Tuo clockbyte() palauttaa käytettävän kellojonon, sisältää copy/reverse koodin ja perus tilastojen laskennan koodin. Kutsuvassa rutiinissa taas on teoreettisten satunnaisbittien laskennan toiminnot ja vertailut.

Edit: lisäsin flags rakenteen tähän alkuun, jotta se löytyisi raportista helpommin:

struct idflags { // 202307 JariK
  unsigned char *id;
  unsigned char *flags;
} idsflags[] = {
  { "🇫🇮", "🇫🇮: fi, fin, Finland, EU, NORTHERNEUROPE, EUROPE, EURASIA, NORDIC, FINLAND" },
  { "🇦🇽", "🇦🇽: ax, ala, Åland, aland, NORDIC, FINLAND" },
  { "🇸🇪", "🇸🇪: se, swe, Sweden, EU, NORTHERNEUROPE, EUROPE, EURASIA, NORDIC" },
  { "🇳🇴", "🇳🇴: no, nor, Norway, NORTHERNEUROPE, EUROPE, EURASIA, NORDIC" },
  { "🇩🇰", "🇩🇰: dk, dnk, Denmark, EU, NORTHERNEUROPE, EUROPE, EURASIA, NORDIC, DENMARK" },
  { "🇫🇴", "🇫🇴: fo, fro, FaroeIslands, NORDIC, DENMARK" },
  { "🇮🇸", "🇮🇸: is, isl, Iceland, NORTHERNEUROPE, EUROPE, EURASIA, NORDIC" },
  { "🇬🇱", "🇬🇱: gl, grl, Greenland, NORTHAMERICA, NORDIC, DENMARK" },
  
  { "🇪🇪", "🇪🇪: ee, est, Estonia, EU, NORTHERNEUROPE, EUROPE, EURASIA, BALTIC" },
  { "🇱🇻", "🇱🇻: lv, lva, Latvia, EU, NORTHERNEUROPE, EUROPE, EURASIA, BALTIC" },
  { "🇱🇹", "🇱🇹: lt, ltu, Lithuania, EU, NORTHERNEUROPE, EUROPE, EURASIA, BALTIC" },
  
  { "🇺🇦", "🇺🇦: ua, ukr, Ukraine, EASTERNEUROPE, EUROPE, EURASIA" },
  { "🇦🇱", "🇦🇱: al, alb, Albania, EASTERNEUROPE, EUROPE, EURASIA" },
  { "🇦🇩", "🇦🇩: ad, and, Andorra, WESTERNEUROPE, EUROPE, EURASIA" },
  { "🇦🇲", "🇦🇲: am, arm, Armenia, EASTERNEUROPE, EUROPE, EURASIA, WESTASIA, ASIA" },
  { "🇦🇹", "🇦🇹: at, aut, Austria, EU, WESTERNEUROPE, EUROPE, EURASIA" },
  { "🇦🇿", "🇦🇿: az, aze, Azerbaijan, EASTERNEUROPE, EUROPE, WESTASIA, ASIA, EURASIA" },
  { "🇧🇾", "🇧🇾: by, blr, Belarus, EASTERNEUROPE, EUROPE, EURASIA" },
  { "🇧🇪", "🇧🇪: be, bel, Belgium, EU, WESTERNEUROPE, EUROPE, EURASIA" },
  { "🇧🇦", "🇧🇦: ba, bih, BosniaandHerzegovina, EASTERNEUROPE, EUROPE" }, // Bosnia and Herzegovina
  { "🇧🇬", "🇧🇬: bg, bgr, Bulgaria, EU, EASTERNEUROPE, EUROPE, EURASIA" },
  { "🇭🇷", "🇭🇷: hr, hrv, Croatia, EU, EASTERNEUROPE, EUROPE, EURASIA" },
  { "🇨🇾", "🇨🇾: cy, cyp, Cyprus, EU, SOUTHERNEUROPE, EUROPE, EURASIA, WESTASIA, ASIA" },
  { "🇨🇿", "🇨🇿: cz, cze, CzechRepublic, EU, EASTERNEUROPE, EUROPE, EURASIA" },  // Czech Republic Czechia
  { "🇫🇷", "🇫🇷: fr, fra, France, EU, WESTERNEUROPE, EUROPE, EURASIA" },
  { "🇬🇪", "🇬🇪: ge, geo, Georgia, EASTERNEUROPE, EUROPE, EURASIA, WESTASIA, ASIA" },
  { "🇩🇪", "🇩🇪: de, deu, Germany, EU, WESTERNEUROPE, EUROPE, EURASIA" },
  { "🇬🇷", "🇬🇷: gr, grc, Greece, EU, SOUTHERNEUROPE, EUROPE, EURASIA" },
  { "🇭🇺", "🇭🇺: hu, hun, Hungary, EU, EASTERNEUROPE, EUROPE, EURASIA" },
  { "🇮🇪", "🇮🇪: ie, irl, Ireland, UK, EU, WESTERNEUROPE, EUROPE, EURASIA" },
  { "🇮🇹", "🇮🇹: it, ita, Italy, EU, SOUTHERNEUROPE, EUROPE, EURASIA" },
  { "🇻🇦", "🇻🇦: va, vat, Vatican, SOUTHERNEUROPE, EUROPE, EURASIA" },
  { "🇰🇿", "🇰🇿: kz, kaz, Kazakhstan, EUROPE, EURASIA, CENTRALASIA, ASIA" },
  { "🇱🇮", "🇱🇮: li, lie, Liechtenstein, WESTERNEUROPE, EUROPE, EURASIA" },
  { "🇱🇺", "🇱🇺: lu, lux, Luxembourg, EU, WESTERNEUROPE, EUROPE, EURASIA" },
  { "🇲🇹", "🇲🇹: mt, mlt, Malta, EU, SOUTHERNEUROPE, EUROPE, EURASIA" },
  { "🇲🇩", "🇲🇩: md, mda, Moldova, EASTERNEUROPE, EUROPE, EURASIA" },
  { "🇲🇪", "🇲🇪: me, mne, Montenegro, EASTERNEUROPE, EUROPE, EURASIA" },
  { "🇲🇨", "🇲🇨: mc, mco, Monaco, WESTERNEUROPE, EUROPE, EURASIA" },
  { "🇲🇪", "🇲🇪: me, mne, Montenegro, EUROPE, EURASIA" },
  { "🇳🇱", "🇳🇱: nl, nld, Netherlands, EU, WESTERNEUROPE, EUROPE, EURASIA" },
  { "🇲🇰", "🇲🇰: mk, mkd, Northmacedonia, EASTERNEUROPE, EUROPE, EURASIA" }, // North Macedonia
  { "🇵🇱", "🇵🇱: pl, pol, Poland, EU, EASTERNEUROPE, EUROPE, EURASIA" },
  { "🇵🇹", "🇵🇹: pt, prt, Portugal, EU, SOUTHERNEUROPE, EUROPE, EURASIA" },
  { "🇷🇴", "🇷🇴: ro, rou, Romania, EU, EASTERNEUROPE, EUROPE, EURASIA" },
  { "🇷🇺", "🇷🇺: ru, rus, Russia, EASTERNEUROPE, EUROPE, EURASIA, NORTHASIA, ASIA, BRICS" },
  { "🇸🇲", "🇸🇲: sm, smr, SanMarino, SOUTHERNEUROPE, EUROPE, EURASIA" }, // San Marino
  { "🇷🇸", "🇷🇸: rs, srb, Serbia, EASTERNEUROPE, EUROPE, EURASIA" },
  { "🇸🇰", "🇸🇰: sk, svk, Slovakia, EU, EASTERNEUROPE, EUROPE, EURASIA" },
  { "🇸🇮", "🇸🇮: si, svn, Slovenia, EU, EASTERNEUROPE, EUROPE, EURASIA" },
  { "🇪🇸", "🇪🇸: es, esp, Spain, EU, SOUTHERNEUROPE, EUROPE, EURASIA" },
  { "🇨🇭", "🇨🇭: ch, che, Switzerland, EU, WESTERNEUROPE, EUROPE, EURASIA" },
  { "🇹🇷", "🇹🇷: tr, tur, Turkey, SOUTHERNEUROPE, EUROPE, EURASIA, WESTASIA, ASIA" },
  
  { "🇬🇧", "🇬🇧: gb, gbr, UnitedKingdom, UK, WESTERNEUROPE, EUROPE, EURASIA" },
  { "🇮🇪", "🇮🇪: gb, gbr, England, UK" },
  { "🇮🇪", "🇮🇪: gb, gbr, Scotland, UK" },
  { "🇮🇪", "🇮🇪: gb, gbr, Wales, UK" },

  { "🇬🇮", "🇬🇮: gi, gib, Gibraltar, UK" },

  { "🇸🇯", "🇸🇯: sj, sjm, JanMayen, NORWAY" }, // Jan Mayen
  
  { "🇦🇮", "🇦🇮: ai, aia, Anguilla, NORTHAMERICA, UK" },
  { "🇦🇬", "🇦🇬: ag, atg, AntiguaAndBarbuda, NORTHAMERICA" }, // Antigua and Barbuda 
  { "🇦🇼", "🇦🇼: aw, sbw, Aruba, NORTHAMERICA" }, 
  { "🇧🇸", "🇧🇸: bs, bhs, Bahamas, NORTHAMERICA" },
  { "🇧🇧", "🇧🇧: bb, brb, Barbados, NORTHAMERICA" },
  { "🇧🇿", "🇧🇿: bz, blz, Belize, NORTHAMERICA" },
  { "🇧🇲", "🇧🇲: bm, bmu, Bermuda, UK, NORTHAMERICA" },
  { "🇧🇶", "🇧🇶: bq, bes, Bonaire, NETHERLANDS, NORTHAMERICA" },
  { "🇻🇬", "🇻🇬: vg, vgb, BritishVirginIslands, UK, NORTHAMERICA" }, // British Virgin Islands
  { "🇨🇦", "🇨🇦: ca, can, Canada, NORTHAMERICA" },
  { "🇰🇾", "🇰🇾: ky, cym, CaymanIslands, UK, NORTHAMERICA" },
  { "🇫🇷", "🇫🇷: fr, fra, ClippertonIsland, FRANCE, NORTHAMERICA" },
  { "🇨🇷", "🇨🇷: cr, cri, CostaRica, NORTHAMERICA" },
  { "🇨🇺", "🇨🇺: cu, cub, Cuba, NORTHAMERICA" },
  { "🇨🇼", "🇨🇼: cw, cuw, Curaçao, curacao, NORTHAMERICA" },
  { "🇩🇲", "🇩🇲: dm, dma, Dominica, NORTHAMERICA" },
  { "🇩🇴", "🇩🇴: do, dom, DominicanRepublic, NORTHAMERICA" }, // Dominican Republic
  { "🇸🇻", "🇸🇻: sv, slv, ElSalvador, NORTHAMERICA" },
  { "🇬🇱", "🇬🇱: gl, grl, Greenland, DENMARK, NORTHAMERICA" },
  { "🇬🇩", "🇬🇩: gd, grd, Grenada, NORTHAMERICA" },
  { "🇬🇵", "🇬🇵: gp, glp, Guadeloupe, FRANCE, NORTHAMERICA" },
  { "🇬🇹", "🇬🇹: gt, gtm, Guatemala, NORTHAMERICA" },
  { "🇭🇹", "🇭🇹: ht, hti, Haiti, NORTHAMERICA" },
  { "🇭🇳", "🇭🇳: hn, hnd, Honduras, NORTHAMERICA" },
  { "🇯🇲", "🇯🇲: jm, jam, Jamaica, NORTHAMERICA" },
  { "🇲🇶", "🇲🇶: mq, mtq, Martinique, FRANCE, NORTHAMERICA" },
  { "🇲🇽", "🇲🇽: mx, mex, Mexico, NORTHAMERICA" },
  { "🇲🇸", "🇲🇸: ms, msr, Montserrat, UK, NORTHAMERICA" },
  { "🇳🇮", "🇳🇮: ni, nic, Nicaragua, NORTHAMERICA" },
  { "🇵🇦", "🇵🇦: pa, pan, Panama, NORTHAMERICA" },
  { "🇵🇷", "🇵🇷: pr, pri, PuertoRico, NORTHAMERICA" },
  { "🇧🇶", "🇧🇶: bq, bes, Saba, NORTHAMERICA" },
  { "🇧🇱", "🇧🇱: bl, blm, SaintBarthélemy, saintbarthelemy, NORTHAMERICA" }, // Saint Barthélemy
  { "🇰🇳", "🇰🇳: kn, kna, SaintKitts, NORTHAMERICA" }, // Saint Kitts and Nevis
  { "🇱🇨", "🇱🇨: lc, lca, SaintLucia, NORTHAMERICA" }, // Saint Lucia
  { "🇲🇫", "🇲🇫: mf, maf, SaintMartin, NORTHAMERICA" }, // Collectivity of Saint Martin
  { "🇸🇽", "🇸🇽: sx, sxm, SintMaarten, NORTHAMERICA" }, // Sint Maarten
  { "🇹🇹", "🇹🇹: tt, tto, TrinidadandTobago, NORTHAMERICA" }, // Trinidad and Tobago
  { "🇹🇨", "🇹🇨: tc, tca, TurksandCaicos, UK, NORTHAMERICA" }, // Turks and Caicos Islands
  { "🇵🇲", "🇵🇲: pm, spm, SaintPierre, NORTHAMERICA" }, // Saint Pierre and Miquelon
  { "🇻🇨", "🇻🇨: vc, vct, SaintVincent, NORTHAMERICA" }, // Saint Vincent and the Grenadines
  { "🇺🇸", "🇺🇸: us, usa, UnitedStatesofAmerica, USA, NORTHAMERICA" }, // United States of America
  { "🇻🇮", "🇻🇮: vi, vir, VirginIslands, USA, NORTHAMERICA" }, // United States Virgin Islands

  { "🇧🇻", "🇧🇻: bv, bvt, BouvetIsland" }, // Bouvet Island
  { "🇺🇲", "🇺🇲: um, umi, Navassa" },
  { "🇸🇭", "🇸🇭: sh, shn, SaintHelena" }, // Saint Helena
  
  { "🇦🇷", "🇦🇷: ar, arg, Argentina, SOUTHAMERICA" },
  { "🇧🇴", "🇧🇴: bo, bol, Bolivia, SOUTHAMERICA" },
  { "🇧🇷", "🇧🇷: br, bra, Brazil, SOUTHAMERICA, BRICS" },
  { "🇨🇱", "🇨🇱: cl, chl, Chile, SOUTHAMERICA" },
  { "🇨🇴", "🇨🇴: co, col, Colombia, SOUTHAMERICA" },
  { "🇪🇨", "🇪🇨: ec, ecu, Ecuador, SOUTHAMERICA" },
  { "🇫🇰", "🇫🇰: fk, flk, FalklandIslands, UK, SOUTHAMERICA" }, // Falkland Islands
  { "🇬🇫", "🇬🇫: gf, guf, FrenchGuiana, SOUTHAMERICA, FRANCE" }, // French Guiana
  { "🇬🇾", "🇬🇾: gy, guy, Guyana, SOUTHAMERICA" },
  { "🇵🇾", "🇵🇾: py, pry, Paraguay, SOUTHAMERICA" },
  { "🇵🇪", "🇵🇪: pe, per, Peru, SOUTHAMERICA" },
  { "🇬🇸", "🇬🇸: gs, sgs, SouthGeorgia, SOUTHAMERICA" }, // South Georgia and the South Sandwich Islands
  { "🇸🇷", "🇸🇷: sr, sur, Suriname, SOUTHAMERICA" },
  { "🇺🇾", "🇺🇾: uy, ury, Uruguay, SOUTHAMERICA" },
  { "🇻🇪", "🇻🇪: ve, ven, Venezuela, SOUTHAMERICA" },

  { "🇦🇪", "🇦🇪: ae, are, UnitedArabEmirates, WESTASIA, ASIA, EURASIA" }, // United Arab Emirates
  { "🇦🇫", "🇦🇫: af, afg, Afghanistan, SOUTHASIA, ASIA, EURASIA" },
  { "🇧🇩", "🇧🇩: bd, bgd, Bangladesh, SOUTHASIA, ASIA, EURASIA" },
  { "🇧🇭", "🇧🇭: bh, bhr, Bahrain, EASTASIA, ASIA, EURASIA" },
  { "🇧🇳", "🇧🇳: bn, brn, Brunei, ASIA, EURASIA" },
  { "🇧🇹", "🇧🇹: bt, btn, Bhutan, SOUTHASIA, ASIA, EURASIA" },
  { "🇨🇳", "🇨🇳: cn, chn, China, EASTASIA, ASIA, EURASIA, BRICS" },
  { "🇭🇰", "🇭🇰: hk, hkg, HongKong, EASTASIA, ASIA, EURASIA" },
  { "🇮🇩", "🇮🇩: id, idn, Indonesia, ASIA, EURASIA" },
  { "🇮🇱", "🇮🇱: il, isr, Israel, WESTASIA, ASIA, EURASIA" },
  { "🇮🇳", "🇮🇳: in, ind, India, SOUTHASIA, ASIA, EURASIA, BRICS" },
  { "🇮🇶", "🇮🇶: iq, irq, Iraq, WESTASIA, ASIA, EURASIA" },
  { "🇮🇷", "🇮🇷: ir, irn, Iran, WESTASIA, ASIA, EURASIA" },
  { "🇯🇴", "🇯🇴: jo, jor, Jordan, WESTASIA, ASIA, EURASIA" },
  { "🇯🇵", "🇯🇵: jp, jpn, Japan, EASTASIA, ASIA, EURASIA" },
  { "🇰🇬", "🇰🇬: kg, kgz, Kyrgyzstan, CENTRALASIA, ASIA, EURASIA" },
  { "🇰🇭", "🇰🇭: kh, khm, Cambodia, ASIA, EURASIA" },
  { "🇰🇷", "🇰🇷: kr, kor, SouthKorea, EASTASIA, ASIA, EURASIA" },
  { "🇰🇼", "🇰🇼: kw, kwt, Kuwait, WESTASIA, ASIA, EURASIA" },
  { "🇱🇦", "🇱🇦: la, lao, Laos, ASIA, EURASIA" },
  { "🇱🇧", "🇱🇧: lb, lbn, Lebanon, WESTASIA, ASIA, EURASIA" },
  { "🇱🇰", "🇱🇰: lk, lka, SriLanka, SOUTHASIA, ASIA, EURASIA" },
  { "🇲🇳", "🇲🇳: mn, mng, Mongolia, EASTASIA, ASIA, EURASIA" },
  { "🇲🇴", "🇲🇴: mo, mac, Macao, EASTASIA, ASIA, EURASIA" },
  { "🇲🇻", "🇲🇻: mv, mdv, Maldives, SOUTHASIA, ASIA, EURASIA" },
  { "🇲🇲", "🇲🇲: mm, mmr, Myanmar, ASIA, EURASIA" },
  { "🇲🇾", "🇲🇾: my, mys, Malaysia, ASIA, EURASIA" },
  { "🇰🇵", "🇰🇵: kp, prk, NorthKorea, EASTASIA, ASIA, EURASIA" },
  { "🇳🇵", "🇳🇵: np, npl, Nepal, SOUTHASIA, ASIA, EURASIA" },
  { "🇴🇲", "🇴🇲: om, omn, Oman, WESTASIA, ASIA, EURASIA" },
  { "🇵🇭", "🇵🇭: ph, phl, Philippines, ASIA, EURASIA" },
  { "🇵🇰", "🇵🇰: pk, pak, Pakistan, SOUTHASIA, ASIA, EURASIA" },
  { "🇵🇸", "🇵🇸: ps, pse, Palestine, WESTASIA, ASIA, EURASIA" },
  { "🇶🇦", "🇶🇦: qa, qat, Qatar, WESTASIA, ASIA, EURASIA" },
  { "🇸🇦", "🇸🇦: sa, sau, SaudiArabia, WESTASIA, ASIA, EURASIA" },
  { "🇸🇬", "🇸🇬: sg, sgb, Singapore, ASIA, EURASIA" },
  { "🇸🇾", "🇸🇾: sy, syr, Syria, WESTASIA, ASIA, EURASIA" },
  { "🇹🇭", "🇹🇭: th, tha, Thailand, ASIA, EURASIA" },
  { "🇹🇯", "🇹🇯: tj, tjk, Tajikistan, CENTRALASIA, ASIA, EURASIA" },
  { "🇹🇱", "🇹🇱: tl, tls, EastTimor, ASIA, EURASIA" },
  { "🇹🇲", "🇹🇲: tm, tkm, Turkmenistan, CENTRALASIA, ASIA, EURASIA" },
  { "🇹🇼", "🇹🇼: tw, twn, Taiwan, EASTASIA, ASIA, EURASIA" },
  { "🇺🇿", "🇺🇿: uz, uzb, Uzbekistan, CENTRALASIA, ASIA, EURASIA" },
  { "🇻🇳", "🇻🇳: vn, vnm, Vietnam, ASIA, EURASIA" },
  { "🇾🇪", "🇾🇪: ye, yem, Yemen, WESTASIA, ASIA, EURASIA" },

  { "🇳🇪", "🇳🇪: ne, ner, Nigeria, AFRICA, WESTAFRICA" },
  { "🇪🇹", "🇪🇹: et, eth, Ethiopia, AFRICA, AFRICA, EASTAFRICA" },
  { "🇪🇷", "🇪🇷: er, eri, Eritrea, AFRICA, EASTAFRICA" },
  { "🇨🇬", "🇨🇬: cg, cog, Congo, AFRICA, CENTRALAFRICA" }, // Rebublic of the Congo
  { "🇨🇩", "🇨🇩: cd, cod, DemocraticCongo, AFRICA, CENTRALAFRICA" }, // Democratic Rebublic of the Congo
  { "🇹🇿", "🇹🇿: tz, tza, Tanzania, AFRICA, EASTAFRICA" },
  { "🇿🇦", "🇿🇦: za, zaf, SouthAfrica, AFRICA, SOUTHAFRICA, BRICS" },
  { "🇰🇪", "🇰🇪: ke, ken, Kenya, AFRICA, EASTAFRICA" },
  { "🇺🇬", "🇺🇬: ug, uga, Uganda, AFRICA, EASTAFRICA" },
  { "🇸🇸", "🇸🇸: ss, ssd, SouthSudan, AFRICA, EASTAFRICA" },
  { "🇸🇩", "🇸🇩: sd, sdn, Sudan, AFRICA, EASTAFRICA" },
  { "🇩🇿", "🇩🇿: dz, dza, Algeria, AFRICA, NORTHAFRICA" },
  { "🇪🇬", "🇪🇬: eg, egy, Egypt, AFRICA, NORTHAFRICA, WESTASIA, ASIA, EURASIA" },
  { "🇱🇾", "🇱🇾: ly, lby, Libya, AFRICA, NORTHAFRICA" },
  { "🇱🇾", "🇱🇾: ly, lby, Madeira, AFRICA, NORTHAFRICA" },
  { "🇲🇦", "🇲🇦: ma, mar, Morocco, AFRICA, NORTHAFRICA" },
  { "🇦🇴", "🇦🇴: ao, ago, Angola, AFRICA, CENTRALAFRICA" },
  { "🇬🇭", "🇬🇭: gh, gha, Ghana, AFRICA, WESTAFRICA" },
  { "🇲🇿", "🇲🇿: mz, moz, Mozambique, AFRICA" },
  { "🇲🇬", "🇲🇬: mg, mdg, Madagascar, AFRICA, EASTAFRICA" },
  { "🇾🇹", "🇾🇹: yt, myt, Mayotte, FRANCE, AFRICA, EASTAFRICA" },
  { "🇨🇮", "🇨🇮: ci, civ, IvoryCoast, AFRICA, WESTAFRICA" },
  { "🇨🇲", "🇨🇲: cm, cmr, Cameroon, AFRICA, CENTRALAFRICA" },
  { "🇳🇪", "🇳🇪: ne, ner, Niger, AFRICA, WESTAFRICA" },
  { "🇧🇫", "🇧🇫: bf, bfa, BurkinaFaso, AFRICA, WESTAFRICA" }, // Burkina Faso
  { "🇲🇱", "🇲🇱: ml, mli, Mali, AFRICA, WESTAFRICA" },
  { "🇲🇼", "🇲🇼: mw, mwi, Malawi, AFRICA, EASTAFRICA" },
  { "🇿🇲", "🇿🇲: zm, zmb, Zambia, AFRICA, EASTAFRICA" },
  { "🇹🇩", "🇹🇩: td, tcd, Chad, AFRICA, CENTRALAFRICA" },
  { "🇸🇴", "🇸🇴: so, som, Somalia, AFRICA, EASTAFRICA" },
  { "🇸🇳", "🇸🇳: sn, sen, Senegal, AFRICA, WESTAFRICA" },
  { "🇿🇼", "🇿🇼: zw, zwe, Zimbabwe, AFRICA, EASTAFRICA" },
  { "🇬🇳", "🇬🇳: gn, gin, Guinea, AFRICA, WESTAFRICA" },
  { "🇷🇪", "🇷🇪: re, reu, Réunion, reunion, FRANCE, AFRICA, EASTAFRICA" }, // Réunion
  { "🇷🇼", "🇷🇼: rw, rwa, Rwanda, AFRICA, EASTAFRICA" },
  { "🇧🇯", "🇧🇯: bj, ben, Benin, AFRICA, WESTAFRICA" },
  { "🇧🇮", "🇧🇮: bi, bdi, Burundi, AFRICA, EASTAFRICA" },
  { "🇹🇳", "🇹🇳: tn, tun, Tunisia, AFRICA, NORTHAFRICA" },
  { "🇹🇬", "🇹🇬: tg, tgo, Togo, AFRICA, WESTAFRICA" },
  { "🇸🇱", "🇸🇱: sl, sle, SierraLeone, AFRICA, WESTAFRICA" }, // Sierra Leone
  { "🇨🇩", "🇨🇩: cd, cod, Congo, AFRICA, CENTRALAFRICA" },
  { "🇨🇫", "🇨🇫: cf, caf, CentralAfrican, CENTRALAFRICA" }, // Central African Republic
  { "🇱🇷", "🇱🇷: lr, lbr, Liberia, AFRICA, WESTAFRICA" },
  { "🇲🇷", "🇲🇷: mr, mrt, Mauritania, AFRICA, WESTAFRICA" },
  { "🇪🇷", "🇪🇷: er, eri, Eritrea, AFRICA, EASTAFRICA" },
  { "🇬🇲", "🇬🇲: gm, gmb, Gambia, AFRICA, WESTAFRICA" },
  { "🇧🇼", "🇧🇼: bw, bwa, Botswana, AFRICA, SOUTHAFRICA" },
  { "🇳🇦", "🇳🇦: na, nam, Namibia, AFRICA, SOUTHAFRICA" },
  { "🇬🇦", "🇬🇦: ga, gab, Gabon, AFRICA, CENTRALAFRICA" },
  { "🇱🇸", "🇱🇸: ls, lso, Lesotho, AFRICA, SOUTHAFRICA" },
  { "🇬🇼", "🇬🇼: gw, gnb, GuineaBissau, AFRICA, WESTAFRICA" }, // Guinea-Bissau
  { "🇬🇶", "🇬🇶: gq, gnq, EquatorialGuinea, CENTRALAFRICA" }, // Equatorial Guinea
  { "🇲🇺", "🇲🇺: mu, mus, Mauritius, AFRICA, EASTAFRICA" },
  { "🇲🇺", "🇲🇺: mz, moz, Mozambique, AFRICA, EASTAFRICA" },
  { "🇸🇿", "🇸🇿: sz, swz, Eswatini, AFRICA, SOUTHAFRICA" },
  { "🇩🇯", "🇩🇯: dj, dji, Djibouti, AFRICA, EASTAFRICA" },
  { "🇰🇲", "🇰🇲: km, com, Comoros, AFRICA" },
  { "🇨🇻", "🇨🇻: cv, cpv, CapeVerde, AFRICA, WESTAFRICA" },
  { "🇪🇭", "🇪🇭: eh, esh, WesternSahara, SPAIN, AFRICA, NORTHAFRICA" }, // Western Sahara
  { "🇸🇹", "🇸🇹: st, stp, SãoToméandPríncipe, saotomeandprincipe, CENTRALAFRICA" }, // São Tomé and Príncipe
  { "🇸🇨", "🇸🇨: sc, syc, Seychelles, AFRICA, EASTAFRICA" },

  { "🇸🇭", "🇸🇭: sh, shn, TristandaCunha, UK" }, // Tristan da Cunha
  
  { "🇦🇺", "🇦🇺: au, aus, Australia, AUSTRALASIA, OCEANIA" },
  { "🇨🇽", "🇨🇽: cx, cxr, Christmasisland, AUSTRALIA" }, // Christmas Island

  { "🇨🇨", "🇨🇨: cc, cck, CocosIslands, AUSTRALIA" }, // Cocos (Keeling) Islands
  
  { "🇮🇲", "🇮🇲: im, imn, ManIsle, UK" }, // Isle of Man
  
  { "🇰🇮", "🇰🇮: ki, kir, Kiribati, MICRONESIA, OCEANIA" },
  { "🇳🇷", "🇳🇷: nr, nru, Nauru, MICRONESIA, OCEANIA" },
  { "🇳🇨", "🇳🇨: nc, ncl, NewCaledonia, FRANCE, OCEANIA" }, // New Caledonia
  { "🇫🇯", "🇫🇯: fj, fji, Fiji, MELANESIA, OCEANIA" },
  { "🇫🇲", "🇫🇲: fm, fsm, Micronesia, MICRONESIA, OCEANIA" },
  { "🇵🇬", "🇵🇬: pg, png, PapuanewGuinea, MELANESIA, OCEANIA" }, // Papua New Guinea
  { "🇻🇺", "🇻🇺: vu, vut, Vanuatu, MELANESIA, OCEANIA" },
  { "🇸🇧", "🇸🇧: sb, slb, SolomonIslands, MELANESIA, OCEANIA" }, // Solomon Islands
  { "🇬🇺", "🇬🇺: gu, gum, Guam, USA, MICRONESIA, OCEANIA" },
  { "🇲🇭", "🇲🇭: mh, mhl, MarshallIslands, MICRONESIA, OCEANIA" }, // Marshall Islands
  { "🇲🇵", "🇲🇵: mp, mnp, NorthernMariana, MICRONESIA, OCEANIA" }, // Northern Mariana Islands
  { "🇵🇫", "🇵🇫: pf, pyf, FrenchPolynesia, FRANCE, POLYNESIA, OCEANIA" }, // French Polynesia
  { "🇼🇸", "🇼🇸: ws, wsm, Samoa, POLYNESIA, OCEANIA" },
  { "🇼🇫", "🇼🇫: wf, wlf, WallisandFutuna, FRANCE, OCEANIA" }, // Wallis and Futuna
  { "🇹🇻", "🇹🇻: tv, tuv, Tuvalu, OCEANIA" },
  { "🇦🇸", "🇦🇸: as, asm, Americansamoa, USA, POLYNESIA, OCEANIA" }, // American Samoa
  { "🇵🇳", "🇵🇳: pn, pcn, Pitcairn, UK, POLYNESIA, OCEANIA" }, // Pitcairn Islands
  { "🇵🇼", "🇵🇼: pw, plw, Palau, MICRONESIA, OCEANIA" },

  { "🇯🇪", "🇯🇪: je, jey, Jersey, FRANCE" },
  
  { "🇦🇶", "🇦🇶: aq, ata, Antarctica, ANTARCTICA" },

  { "🇳🇫", "🇳🇫: nf, nfk, NorFolkIsland, AUSTRALASIA, POLYNESIA, OCEANIA" }, // Norfolk Island
  { "🇨🇰", "🇨🇰: ck, cok, Cookisland, NEWZEALAND, POLYNESIA, OCEANIA" }, // Cook Islands
  { "🇳🇿", "🇳🇿: nz, nzl, NewZealand, NEWZEALAND, AUSTRALASIA, OCEANIA" }, // New Zealand
  { "🇳🇺", "🇳🇺: nu, niu, Niue, NEWZEALAND, POLYNESIA, OCEANIA" },
  { "🇹🇰", "🇹🇰: tk, tkl, Tokelau, NEWZEALAND, POLYNESIA, OCEANIA" },
  { "🇹🇴", "🇹🇴: to, ton, Tonga, POLYNESIA, OCEANIA" },
  
  // still missing
  
  { "🇮🇴", "🇮🇴: io, iot, BritishIndianOcean" }, // British Indian Ocean Territory
  { "🇹🇫", "🇹🇫: tf, atf, FrenchSouthernandaAtarctic" }, // French Southern and Antarctic Lands
  { "🇬🇬", "🇬🇬: gg, ggy, Guernsey, GUERNSEY" },
  { "🇭🇲", "🇭🇲: hm, hmd, HeardandMacDonaldsIslands" }, // Heard Island and McDonald Islands
  { "🇺🇲", "🇺🇲: um, umi, UsMinorOutlyingIslands, US" }, // United States Minor Outlying Islands
};

Edit: Nämä liput taitaa olla suurempi projeksi. Lisätty tähän alkuun lippujen DEBUG77 tuloste, että sekin löytyy helposti.

area:FINLAND, Finland(fi), Åland(ax), count:2
area:DENMARK, Denmark(dk), FaroeIslands(fo), Greenland(gl), Greenland(gl), count:4
area:NORDIC, Finland(fi), Åland(ax), Sweden(se), Norway(no), Denmark(dk), FaroeIslands(fo), Iceland(is), Greenland(gl), count:8
area:BALTIC, Estonia(ee), Latvia(lv), Lithuania(lt), count:3
area:FRANCE, ClippertonIsland(fr), Guadeloupe(gp), Martinique(mq), FrenchGuiana(gf), Mayotte(yt), Réunion(re), NewCaledonia(nc), FrenchPolynesia(pf), WallisandFutuna(wf), Jersey(je), count:10
area:UK, Ireland(ie), UnitedKingdom(gb), England(gb), Scotland(gb), Wales(gb), Gibraltar(gi), Anguilla(ai), Bermuda(bm), BritishVirginIslands(vg), CaymanIslands(ky), Montserrat(ms), TurksandCaicos(tc), FalklandIslands(fk), TristandaCunha(sh), ManIsle(im), Pitcairn(pn), count:16
area:SPAIN, WesternSahara(eh), count:1
area:EU, Finland(fi), Sweden(se), Denmark(dk), Estonia(ee), Latvia(lv), Lithuania(lt), Austria(at), Belgium(be), Bulgaria(bg), Croatia(hr), Cyprus(cy), CzechRepublic(cz), France(fr), Germany(de), Greece(gr), Hungary(hu), Ireland(ie), Italy(it), Luxembourg(lu), Malta(mt), Netherlands(nl), Poland(pl), Portugal(pt), Romania(ro), Slovakia(sk), Slovenia(si), Spain(es), Switzerland(ch), count:28
area:NORTHERNEUROPE, Finland(fi), Sweden(se), Norway(no), Denmark(dk), Iceland(is), Estonia(ee), Latvia(lv), Lithuania(lt), count:8
area:EASTERNEUROPE, Ukraine(ua), Albania(al), Armenia(am), Azerbaijan(az), Belarus(by), BosniaandHerzegovina(ba), Bulgaria(bg), Croatia(hr), CzechRepublic(cz), Georgia(ge), Hungary(hu), Moldova(md), Montenegro(me), Northmacedonia(mk), Poland(pl), Romania(ro), Russia(ru), Serbia(rs), Slovakia(sk), Slovenia(si), count:20
area:SOUTHERNEUROPE, Cyprus(cy), Greece(gr), Italy(it), Vatican(va), Malta(mt), Portugal(pt), SanMarino(sm), Spain(es), Turkey(tr), count:9
area:WESTERNEUROPE, Andorra(ad), Austria(at), Belgium(be), France(fr), Germany(de), Ireland(ie), Liechtenstein(li), Luxembourg(lu), Monaco(mc), Netherlands(nl), Switzerland(ch), UnitedKingdom(gb), count:12
area:EUROPE, Finland(fi), Sweden(se), Norway(no), Denmark(dk), Iceland(is), Estonia(ee), Latvia(lv), Lithuania(lt), Ukraine(ua), Albania(al), Andorra(ad), Armenia(am), Austria(at), Azerbaijan(az), Belarus(by), Belgium(be), BosniaandHerzegovina(ba), Bulgaria(bg), Croatia(hr), Cyprus(cy), CzechRepublic(cz), France(fr), Georgia(ge), Germany(de), Greece(gr), Hungary(hu), Ireland(ie), Italy(it), Vatican(va), Kazakhstan(kz), Liechtenstein(li), Luxembourg(lu), Malta(mt), Moldova(md), Montenegro(me), Monaco(mc), Montenegro(me), Netherlands(nl), Northmacedonia(mk), Poland(pl), Portugal(pt), Romania(ro), Russia(ru), SanMarino(sm), Serbia(rs), Slovakia(sk), Slovenia(si), Spain(es), Switzerland(ch), Turkey(tr), UnitedKingdom(gb), count:51
area:EURASIA, Finland(fi), Sweden(se), Norway(no), Denmark(dk), Iceland(is), Estonia(ee), Latvia(lv), Lithuania(lt), Ukraine(ua), Albania(al), Andorra(ad), Armenia(am), Austria(at), Azerbaijan(az), Belarus(by), Belgium(be), Bulgaria(bg), Croatia(hr), Cyprus(cy), CzechRepublic(cz), France(fr), Georgia(ge), Germany(de), Greece(gr), Hungary(hu), Ireland(ie), Italy(it), Vatican(va), Kazakhstan(kz), Liechtenstein(li), Luxembourg(lu), Malta(mt), Moldova(md), Montenegro(me), Monaco(mc), Montenegro(me), Netherlands(nl), Northmacedonia(mk), Poland(pl), Portugal(pt), Romania(ro), Russia(ru), SanMarino(sm), Serbia(rs), Slovakia(sk), Slovenia(si), Spain(es), Switzerland(ch), Turkey(tr), UnitedKingdom(gb), UnitedArabEmirates(ae), Afghanistan(af), Bangladesh(bd), Bahrain(bh), Brunei(bn), Bhutan(bt), China(cn), HongKong(hk), Indonesia(id), Israel(il), India(in), Iraq(iq), Iran(ir), Jordan(jo), Japan(jp), Kyrgyzstan(kg), Cambodia(kh), SouthKorea(kr), Kuwait(kw), Laos(la), Lebanon(lb), SriLanka(lk), Mongolia(mn), Macao(mo), Maldives(mv), Myanmar(mm), Malaysia(my), NorthKorea(kp), Nepal(np), Oman(om), Philippines(ph), Pakistan(pk), Palestine(ps), Qatar(qa), SaudiArabia(sa), Singapore(sg), Syria(sy), Thailand(th), Tajikistan(tj), EastTimor(tl), Turkmenistan(tm), Taiwan(tw), Uzbekistan(uz), Vietnam(vn), Yemen(ye), Egypt(eg), count:96
area:NORTHASIA, Russia(ru), count:1
area:EASTASIA, Bahrain(bh), China(cn), HongKong(hk), Japan(jp), SouthKorea(kr), Mongolia(mn), Macao(mo), NorthKorea(kp), Taiwan(tw), count:9
area:CENTRALASIA, Kazakhstan(kz), Kyrgyzstan(kg), Tajikistan(tj), Turkmenistan(tm), Uzbekistan(uz), count:5
area:SOUTHASIA, Afghanistan(af), Bangladesh(bd), Bhutan(bt), India(in), SriLanka(lk), Maldives(mv), Nepal(np), Pakistan(pk), count:8
area:WESTASIA, Armenia(am), Azerbaijan(az), Cyprus(cy), Georgia(ge), Turkey(tr), UnitedArabEmirates(ae), Israel(il), Iraq(iq), Iran(ir), Jordan(jo), Kuwait(kw), Lebanon(lb), Oman(om), Palestine(ps), Qatar(qa), SaudiArabia(sa), Syria(sy), Yemen(ye), Egypt(eg), count:19
area:ASIA, Armenia(am), Azerbaijan(az), Cyprus(cy), Georgia(ge), Kazakhstan(kz), Russia(ru), Turkey(tr), UnitedArabEmirates(ae), Afghanistan(af), Bangladesh(bd), Bahrain(bh), Brunei(bn), Bhutan(bt), China(cn), HongKong(hk), Indonesia(id), Israel(il), India(in), Iraq(iq), Iran(ir), Jordan(jo), Japan(jp), Kyrgyzstan(kg), Cambodia(kh), SouthKorea(kr), Kuwait(kw), Laos(la), Lebanon(lb), SriLanka(lk), Mongolia(mn), Macao(mo), Maldives(mv), Myanmar(mm), Malaysia(my), NorthKorea(kp), Nepal(np), Oman(om), Philippines(ph), Pakistan(pk), Palestine(ps), Qatar(qa), SaudiArabia(sa), Singapore(sg), Syria(sy), Thailand(th), Tajikistan(tj), EastTimor(tl), Turkmenistan(tm), Taiwan(tw), Uzbekistan(uz), Vietnam(vn), Yemen(ye), Egypt(eg), count:53
area:NORTHAFRICA, Algeria(dz), Egypt(eg), Libya(ly), Madeira(ly), Morocco(ma), Tunisia(tn), WesternSahara(eh), count:7
area:EASTAFRICA, Ethiopia(et), Eritrea(er), Tanzania(tz), Kenya(ke), Uganda(ug), SouthSudan(ss), Sudan(sd), Madagascar(mg), Mayotte(yt), Malawi(mw), Zambia(zm), Somalia(so), Zimbabwe(zw), Réunion(re), Rwanda(rw), Burundi(bi), Eritrea(er), Mauritius(mu), Mozambique(mz), Djibouti(dj), Seychelles(sc), count:21
area:CENTRALAFRICA, Congo(cg), DemocraticCongo(cd), Angola(ao), Cameroon(cm), Chad(td), Congo(cd), CentralAfrican(cf), Gabon(ga), EquatorialGuinea(gq), SãoToméandPríncipe(st), count:10
area:SOUTHAFRICA, SouthAfrica(za), Botswana(bw), Namibia(na), Lesotho(ls), Eswatini(sz), count:5
area:WESTAFRICA, Nigeria(ne), Ghana(gh), IvoryCoast(ci), Niger(ne), BurkinaFaso(bf), Mali(ml), Senegal(sn), Guinea(gn), Benin(bj), Togo(tg), SierraLeone(sl), Liberia(lr), Mauritania(mr), Gambia(gm), GuineaBissau(gw), CapeVerde(cv), count:16
area:AFRICA, Nigeria(ne), Ethiopia(et), Eritrea(er), Congo(cg), DemocraticCongo(cd), Tanzania(tz), SouthAfrica(za), Kenya(ke), Uganda(ug), SouthSudan(ss), Sudan(sd), Algeria(dz), Egypt(eg), Libya(ly), Madeira(ly), Morocco(ma), Angola(ao), Ghana(gh), Mozambique(mz), Madagascar(mg), Mayotte(yt), IvoryCoast(ci), Cameroon(cm), Niger(ne), BurkinaFaso(bf), Mali(ml), Malawi(mw), Zambia(zm), Chad(td), Somalia(so), Senegal(sn), Zimbabwe(zw), Guinea(gn), Réunion(re), Rwanda(rw), Benin(bj), Burundi(bi), Tunisia(tn), Togo(tg), SierraLeone(sl), Congo(cd), Liberia(lr), Mauritania(mr), Eritrea(er), Gambia(gm), Botswana(bw), Namibia(na), Gabon(ga), Lesotho(ls), GuineaBissau(gw), Mauritius(mu), Mozambique(mz), Eswatini(sz), Djibouti(dj), Comoros(km), CapeVerde(cv), WesternSahara(eh), Seychelles(sc), count:58
area:NORTHAMERICA, Greenland(gl), Anguilla(ai), AntiguaAndBarbuda(ag), Aruba(aw), Bahamas(bs), Barbados(bb), Belize(bz), Bermuda(bm), Bonaire(bq), BritishVirginIslands(vg), Canada(ca), CaymanIslands(ky), ClippertonIsland(fr), CostaRica(cr), Cuba(cu), Curaçao(cw), Dominica(dm), DominicanRepublic(do), ElSalvador(sv), Greenland(gl), Grenada(gd), Guadeloupe(gp), Guatemala(gt), Haiti(ht), Honduras(hn), Jamaica(jm), Martinique(mq), Mexico(mx), Montserrat(ms), Nicaragua(ni), Panama(pa), PuertoRico(pr), Saba(bq), SaintBarthélemy(bl), SaintKitts(kn), SaintLucia(lc), SaintMartin(mf), SintMaarten(sx), TrinidadandTobago(tt), TurksandCaicos(tc), SaintPierre(pm), SaintVincent(vc), UnitedStatesofAmerica(us), VirginIslands(vi), count:44
area:SOUTHAMERICA, Argentina(ar), Bolivia(bo), Brazil(br), Chile(cl), Colombia(co), Ecuador(ec), FalklandIslands(fk), FrenchGuiana(gf), Guyana(gy), Paraguay(py), Peru(pe), SouthGeorgia(gs), Suriname(sr), Uruguay(uy), Venezuela(ve), count:15
area:USA, UnitedStatesofAmerica(us), VirginIslands(vi), Guam(gu), Americansamoa(as), count:4
area:OCEANIA, Australia(au), Kiribati(ki), Nauru(nr), NewCaledonia(nc), Fiji(fj), Micronesia(fm), PapuanewGuinea(pg), Vanuatu(vu), SolomonIslands(sb), Guam(gu), MarshallIslands(mh), NorthernMariana(mp), FrenchPolynesia(pf), Samoa(ws), WallisandFutuna(wf), Tuvalu(tv), Americansamoa(as), Pitcairn(pn), Palau(pw), NorFolkIsland(nf), Cookisland(ck), NewZealand(nz), Niue(nu), Tokelau(tk), Tonga(to), count:25
area:AUSTRALASIA, Australia(au), NorFolkIsland(nf), NewZealand(nz), count:3
area:MELANESIA, Fiji(fj), PapuanewGuinea(pg), Vanuatu(vu), SolomonIslands(sb), count:4
area:MICRONESIA, Kiribati(ki), Nauru(nr), Micronesia(fm), Guam(gu), MarshallIslands(mh), NorthernMariana(mp), Palau(pw), count:7
area:POLYNESIA, FrenchPolynesia(pf), Samoa(ws), Americansamoa(as), Pitcairn(pn), NorFolkIsland(nf), Cookisland(ck), Niue(nu), Tokelau(tk), Tonga(to), count:9
area:BRICS, Russia(ru), Brazil(br), China(cn), India(in), SouthAfrica(za), count:5
area:ALL, Finland(fi), Åland(ax), Sweden(se), Norway(no), Denmark(dk), FaroeIslands(fo), Iceland(is), Greenland(gl), Estonia(ee), Latvia(lv), Lithuania(lt), Ukraine(ua), Albania(al), Andorra(ad), Armenia(am), Austria(at), Azerbaijan(az), Belarus(by), Belgium(be), BosniaandHerzegovina(ba), Bulgaria(bg), Croatia(hr), Cyprus(cy), CzechRepublic(cz), France(fr), Georgia(ge), Germany(de), Greece(gr), Hungary(hu), Ireland(ie), Italy(it), Vatican(va), Kazakhstan(kz), Liechtenstein(li), Luxembourg(lu), Malta(mt), Moldova(md), Montenegro(me), Monaco(mc), Montenegro(me), Netherlands(nl), Northmacedonia(mk), Poland(pl), Portugal(pt), Romania(ro), Russia(ru), SanMarino(sm), Serbia(rs), Slovakia(sk), Slovenia(si), Spain(es), Switzerland(ch), Turkey(tr), UnitedKingdom(gb), England(gb), Scotland(gb), Wales(gb), Gibraltar(gi), JanMayen(sj), Anguilla(ai), AntiguaAndBarbuda(ag), Aruba(aw), Bahamas(bs), Barbados(bb), Belize(bz), Bermuda(bm), Bonaire(bq), BritishVirginIslands(vg), Canada(ca), CaymanIslands(ky), ClippertonIsland(fr), CostaRica(cr), Cuba(cu), Curaçao(cw), Dominica(dm), DominicanRepublic(do), ElSalvador(sv), Greenland(gl), Grenada(gd), Guadeloupe(gp), Guatemala(gt), Haiti(ht), Honduras(hn), Jamaica(jm), Martinique(mq), Mexico(mx), Montserrat(ms), Nicaragua(ni), Panama(pa), PuertoRico(pr), Saba(bq), SaintBarthélemy(bl), SaintKitts(kn), SaintLucia(lc), SaintMartin(mf), SintMaarten(sx), TrinidadandTobago(tt), TurksandCaicos(tc), SaintPierre(pm), SaintVincent(vc), UnitedStatesofAmerica(us), VirginIslands(vi), BouvetIsland(bv), Navassa(um), SaintHelena(sh), Argentina(ar), Bolivia(bo), Brazil(br), Chile(cl), Colombia(co), Ecuador(ec), FalklandIslands(fk), FrenchGuiana(gf), Guyana(gy), Paraguay(py), Peru(pe), SouthGeorgia(gs), Suriname(sr), Uruguay(uy), Venezuela(ve), UnitedArabEmirates(ae), Afghanistan(af), Bangladesh(bd), Bahrain(bh), Brunei(bn), Bhutan(bt), China(cn), HongKong(hk), Indonesia(id), Israel(il), India(in), Iraq(iq), Iran(ir), Jordan(jo), Japan(jp), Kyrgyzstan(kg), Cambodia(kh), SouthKorea(kr), Kuwait(kw), Laos(la), Lebanon(lb), SriLanka(lk), Mongolia(mn), Macao(mo), Maldives(mv), Myanmar(mm), Malaysia(my), NorthKorea(kp), Nepal(np), Oman(om), Philippines(ph), Pakistan(pk), Palestine(ps), Qatar(qa), SaudiArabia(sa), Singapore(sg), Syria(sy), Thailand(th), Tajikistan(tj), EastTimor(tl), Turkmenistan(tm), Taiwan(tw), Uzbekistan(uz), Vietnam(vn), Yemen(ye), Nigeria(ne), Ethiopia(et), Eritrea(er), Congo(cg), DemocraticCongo(cd), Tanzania(tz), SouthAfrica(za), Kenya(ke), Uganda(ug), SouthSudan(ss), Sudan(sd), Algeria(dz), Egypt(eg), Libya(ly), Madeira(ly), Morocco(ma), Angola(ao), Ghana(gh), Mozambique(mz), Madagascar(mg), Mayotte(yt), IvoryCoast(ci), Cameroon(cm), Niger(ne), BurkinaFaso(bf), Mali(ml), Malawi(mw), Zambia(zm), Chad(td), Somalia(so), Senegal(sn), Zimbabwe(zw), Guinea(gn), Réunion(re), Rwanda(rw), Benin(bj), Burundi(bi), Tunisia(tn), Togo(tg), SierraLeone(sl), Congo(cd), CentralAfrican(cf), Liberia(lr), Mauritania(mr), Eritrea(er), Gambia(gm), Botswana(bw), Namibia(na), Gabon(ga), Lesotho(ls), GuineaBissau(gw), EquatorialGuinea(gq), Mauritius(mu), Mozambique(mz), Eswatini(sz), Djibouti(dj), Comoros(km), CapeVerde(cv), WesternSahara(eh), SãoToméandPríncipe(st), Seychelles(sc), TristandaCunha(sh), Australia(au), Christmasisland(cx), CocosIslands(cc), ManIsle(im), Kiribati(ki), Nauru(nr), NewCaledonia(nc), Fiji(fj), Micronesia(fm), PapuanewGuinea(pg), Vanuatu(vu), SolomonIslands(sb), Guam(gu), MarshallIslands(mh), NorthernMariana(mp), FrenchPolynesia(pf), Samoa(ws), WallisandFutuna(wf), Tuvalu(tv), Americansamoa(as), Pitcairn(pn), Palau(pw), Jersey(je), Antarctica(aq), NorFolkIsland(nf), Cookisland(ck), NewZealand(nz), Niue(nu), Tokelau(tk), Tonga(to), BritishIndianOcean(io), FrenchSouthernandaAtarctic(tf), Guernsey(gg), HeardandMacDonaldsIslands(hm), UsMinorOutlyingIslands(um), count:262

Edit: lisätty uusi satunnaisuuden lähde –twist (ressu with twist). Siitä haettavien bittien pitäisi olla otp kelpoisia. Eli jos ressu on one time pad kelpoinen, ja se säilyy otp kelpoisena kun siihen summataan toinen generaattori (tässä pseudoressu), –twist:in tulos on otp kelpoinen.

Edit: korjattu “bad for randomness” bugi ressu_genbytes():ssä.

Edit: lisätty test_bytes() funktio, johon voit kirjoittaa oman satunnaislukugeneraattorisi, ja ajaa sitä newressun –test komentoriviparametrillä.

Edit: lisätty ressun satunnaisuutta käyttävä ohjelma newressusudoku, jolla voi etsiä sudokuja.

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

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

Ensiksi alkuperäinen kellojonon luku.

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

Sitten ns kiinteämittaista kellojonoa tarjoava rutiini:

#define FIXEDCLOCKCHAINLENGTH 80
unsigned int fixedclockchainlength = FIXEDCLOCKCHAINLENGTH;

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

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

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

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

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

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

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

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

  return(rando);
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

#define aDEBUG5 2

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

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

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

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

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

  ressu_genbytes_single_do(size, buffer);

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

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

    fprintf(stderr, "rounds:1");

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

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

#define aDEBUG7 2
#define aDEBUG2 2

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

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

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

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

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

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

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

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

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

	ressu_genbytes_single_do(ressut_bytes, ressut);

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

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

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

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

	// and average

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

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

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

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

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

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

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

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

	lim2 = lim;

	// calculate rndbits1

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

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

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

	//
	//  calculate rndbits4
	//

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

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

	//
	//  calculate rndbits5
	//

#define aDEBUG10 2
	
	rndbits5 = 0;

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

	//
	//  calculate rndbits6
	//

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

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

	//
	//  calculate rndbits7
	//

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

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

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

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

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

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

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

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

#ifdef DEBUG10

      rndbits5 = 0;

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

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

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

	  double l2;

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

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

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

#endif
      
      if(stats) {

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

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

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

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

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

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

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

#endif
  
  genbytes += size;
}

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

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

Edit: toisaalta, jos pseudoressun tulosjono summataan (xor) ressun tulosjonon kanssa yhteinen tulos on mielestäni taas one time pad kelpoinen. Siis ressun tuloste on one time pad materiaalia, vaikka siihen summataan mitä, summaamisen jälkeen se on edelleen one time pad materiaalia.

Edit: seuraavassa esimerkki, jossa on listattuna yhdellä and:ätyt puskurien alut: (ensimmäisellä rivillä vain nollabitti, eli arvot 0 ja 1. Seuraavalla rivillä lisätty ykkösbitti eli arvo 2 (tällöin arvot ovat 0 1 2 ja 3). Seuraavalla kakkosbitti (4) eli arvot 0, 1, 2, 3, 4, 5, 6 ja 7. Rivejä on tässä 8 kappaletta eli oma rivi jokaiselle uudelle bitille, viimeisen bitin arvo on 80 eli 128d. Seuraavan listan joissa on single 1 rivillä single_do()-funktion shift ja xor ja single 2 rivillä swap saat DEBUG5:lla. Lista on heksana että voidaan tarkistaa että viimeinen rivi on todella varsinaisella satunnaislukulistalla rivi 1. Huomaa ensimmäisen rivin swap:it. Kun swappailemme merkkejä joissa on vain ykköstä ja nollaa, ilmeni että niissä molemmissa on epäsymmetriaa verrattuna muihin merkkeihin.

$ ./newressu --single -x --fixedclock5| more
single 1   01 01 01 01 01 00 00 00 00 00 01 01 01 01 01 00 00 00 00 00 01 01 01 01 01 00 00 00 00 00 01 01
single 2   01 01 01 01 00 00 00 00 00 01 01 01 01 01 00 00 00 00 00 01 01 01 01 01 00 00 00 00 00 01 01 01

single 1   02 03 02 02 00 00 01 01 01 03 03 02 02 02 00 00 01 01 01 03 02 03 02 02 00 01 00 01 01 03 03 02
single 2   02 02 01 00 00 01 01 00 01 03 01 01 00 02 01 02 02 01 03 00 00 01 03 03 02 03 00 02 00 02 01 02
single 1   04 05 02 01 01 03 03 00 02 06 02 03 00 05 03 05 05 02 06 00 00 02 07 07 05 07 01 04 00 04 02 05
single 2   07 07 01 06 06 03 05 04 05 03 03 06 00 06 02 02 03 02 06 06 03 00 07 00 02 04 02 00 05 05 04 04
single 1   0f 0f 03 0c 0c 06 0a 08 0a 07 07 0d 01 0d 04 04 06 04 0d 0d 07 01 0f 00 04 08 04 00 0b 0a 08 09
single 2   0b 0f 09 01 01 0b 04 03 05 09 09 08 0d 07 09 05 0a 09 04 0b 04 0f 03 04 0b 04 08 09 08 06 02 0f
single 1   16 1e 12 02 02 17 09 07 0b 13 12 10 1a 0e 12 0b 15 13 09 16 08 1e 06 08 17 09 11 13 11 0c 04 1e
single 2   0f 1f 00 0e 02 15 12 15 09 16 19 16 10 0f 07 19 13 0a 16 1e 05 1b 01 15 08 08 19 0f 1a 0a 16 0a
single 1   1f 3f 01 1d 05 2a 24 2a 12 2c 33 2d 21 1f 0f 32 26 14 2c 3c 0b 37 03 2b 11 10 32 1e 34 14 2d 15
single 2   17 01 28 33 16 19 2b 1f 1f 15 28 2b 35 0f 37 16 32 18 0d 28 27 1f 11 11 3e 0a 3f 10 3b 26 0a 30
single 1   2f 03 50 66 2c 32 56 3f 3f 2b 51 57 6a 1e 6e 2c 64 31 1b 51 4f 3f 22 22 7c 14 7e 21 77 4d 15 61
single 2   56 55 2b 3b 3d 70 22 52 79 6d 52 57 3e 29 24 1e 50 5c 36 13 2a 5b 00 54 43 0f 18 5e 13 5d 21 70
single 1   ad ab 57 76 7a e0 44 a4 f3 db a5 af 7d 52 48 3c a0 b8 6d 27 55 b7 01 a8 86 1e 30 bc 27 bb 43 e1
single 2   41 b1 01 14 dc a8 a2 36 48 a8 6a 97 85 13 62 3f 0b e0 b5 34 8d b9 c4 fa 65 bc 8c 93 b8 f9 f2 ef
00000 41b10114dca8a23648a86a978513623f0be0b5348db9c4fa65bc8c93b8f9f2ef
00001 119f2a30b1a5b7737d92f5ca7d55b2991bca8da85db795278876a0cc66cd7fee
00002 40fe7e3eb56ee10680053fb69f00aa9e05b470490270875180bf545609d55db4
00003 af82f8756838045d6d8b8bf2ada39b43456690bd7ca56cba3fa592ec660680a2
00004 6f4370a240850ad1951fe555c1ef377b0b0fa20af3b1cd5381ac4a2916764eb2
00005 5b59d45d9d19280b9115405c92a25a75cd4c53b051d5df45a0cd4b39163c5060
00006 f4524aad74b97cab5bb6b6ae4baff0b609562aab71ddbd10b9473fb4f0be6940
00007 587979249184244f17740e5a9e4a5047fd46c94b1721fad3ff964363a313b8b3
00008 49724491c0ab1b9933c3a2f83caed2c97ea5482d5f0e5dfc775effde59a40bd4
00009 2a558f2f09776189a4b812fa741b423172ad619853a5eddbc21674a1564c5701

Nolla toimi seuraavalla tavalla: swapissähän f muuttujaan eli swapin kohteeseen lisätään merkki swapin puskurin d:n kohdasta. Jos siis puskuri d:n sisältö (bufferd) on nolla, f ei kasva, ja koko nollia sisältävä ketju jää “ripottelematta” puskuriin. Seuraavassa d käy läpi koko puskurin, f ei muutu mihinkään, ja näin koko nollaketju jää samanlaiseksi. Kun nollaketju loppuu swap:pi palaa takaisin normaaliin toimintaan.

ressu 1    00 00 00 00 00 01 01 01 01 01 00 00 00 00 01 00 01 01 01 01 00 00 00 00 00 01 01 01 01 01 00 00
d:0, f:0, bufferd:0, bufferf:0
d:1, f:0, bufferd:0, bufferf:0
d:2, f:0, bufferd:0, bufferf:0
d:3, f:0, bufferd:0, bufferf:0
d:4, f:0, bufferd:0, bufferf:0
d:5, f:0, bufferd:1, bufferf:0
d:6, f:1, bufferd:1, bufferf:1
d:7, f:2, bufferd:1, bufferf:1
d:8, f:3, bufferd:1, bufferf:1
d:9, f:4, bufferd:1, bufferf:1
d:10, f:5, bufferd:0, bufferf:1
d:11, f:5, bufferd:0, bufferf:0
d:12, f:5, bufferd:0, bufferf:0

Ykkösellä ongelma oli seuraava: d kulkee taas puskurin alusta puskurin loppuun ja f:ää kasvatetaan d:n sisällöllä (bufferd), siis f:ää kasvatetaan yhdellä, tällöin laitetaan ykkönen seuraavaan f:ään, joka on tietenkin seuraava d:n sisältö. Näin f kasvaa joka kierroksella yhdellä kunnes ykkönen on siirretty puskurin viimeiseen merkkiin. Ykkösversio ei siis palaa normaaliksi ykkösten jälkeen vaan jatkuu puskurin loppuun.

ressu 1    01 01 01 01 01 00 00 00 00 00 01 01 01 01 00 01 00 00 00 01 00 01 01 01 01 00 00 00 00 00 01 01
d:0, f:0, bufferd:1, bufferf:1
d:1, f:1, bufferd:1, bufferf:1
d:2, f:2, bufferd:1, bufferf:1
d:3, f:3, bufferd:1, bufferf:1
d:4, f:4, bufferd:1, bufferf:1
d:5, f:5, bufferd:1, bufferf:1
d:6, f:6, bufferd:1, bufferf:1
d:7, f:7, bufferd:1, bufferf:1
d:8, f:8, bufferd:1, bufferf:1
d:9, f:9, bufferd:1, bufferf:1
d:10, f:10, bufferd:1, bufferf:1
d:11, f:11, bufferd:1, bufferf:1
d:12, f:12, bufferd:1, bufferf:1
d:13, f:13, bufferd:1, bufferf:1

Korjaus on tietenkin ohittaa nämä tapaukset eli kasvattaa f:ää d:n sisällön lisäksi kakkosella. (d muuttujaa ei voi muuttaa, varsinaista swäppiä ei voi muuttaa, datan perusteella ei voi tehdä logiikkaa (esim if data = “1”), joten ainoa vaihtoehto on muuttaa f:ää) Seuraavassa uusi swap:pi osuus:

Ongelma ennen 0 ja 1 muutostakaan ei ollut tilastollisesti kovin yleinen. Suurin osa korjautui sillä että seuraavalla xor:lla nuo nollabitit ja ykkösbitit muuttuvat toisiksi ja taas swappaytyvät normaalisti. Periaatteessa puskurin viimeinen merkki oli hieman useammin ykkönen, joten siitä olisi voinut tehdä tunnistuksen käytetystä ressusta. +2 muutoksen jälkeen tunnistus ei tietenkään onnistu, nyt kaikki dataarvot toimivat uudessa versiossa symmetrisesti. Asiaa on tietysti tutkittava, ja osin uudelleen koska “sydän” (ressu_genbytes_single_do()) on muuttunut.

#define aDEBUG5A 2
#define aDEBUG5B 2
#define aDEBUG5C 2
#define aLOWCLOCKBITONLY 2 // off by default //

void ressu_genbytes_single_do(int size, unsigned char *buffer) // JariK 2013
...
    for(d = 0; d < size; d++) {
#ifdef DEBUG5B
      fprintf(stdout,"d:%d", d);
      fprintf(stdout,", f:%d", f);
      fprintf(stdout,", bufferd:%d", buffer[d]);
      fprintf(stdout,", bufferf:%d", buffer[f]);
      fprintf(stdout,"\n");
#endif
      f = (f + buffer[d] + 2) % size; // +2 JariK 2022
      e = buffer[d];
      buffer[d] = buffer[f];
      buffer[f] = e;
    }
...
}

F:än kasvattaminen yhdellä ei riitä, sillä sitten nolla toimii kuten yksi äsken eli kulkee kaikkien puskuripaikkojen läpi puskurin loppuun:

ressu 1    00 00 00 00 00 01 01 01 01 01 00 00 00 00 01 00 01 01 01 01 00 00 00 00 00 01 01 01 01 01 00 00
d:0, f:0, bufferd:0, bufferf:0
d:1, f:1, bufferd:0, bufferf:0
d:2, f:2, bufferd:0, bufferf:0
d:3, f:3, bufferd:0, bufferf:0
d:4, f:4, bufferd:0, bufferf:0
d:5, f:5, bufferd:0, bufferf:0
d:6, f:6, bufferd:0, bufferf:0
d:7, f:7, bufferd:0, bufferf:0
d:8, f:8, bufferd:0, bufferf:0
d:9, f:9, bufferd:0, bufferf:0
d:10, f:10, bufferd:0, bufferf:0
d:11, f:11, bufferd:0, bufferf:0
d:12, f:12, bufferd:0, bufferf:0
d:13, f:13, bufferd:0, bufferf:0

Lista, jossa aiempi ensimmäinen swappirivi oli melkein muuttumaton, tässä se näyttää jo kohtuulliselta (+2 version): Tässä on otettava huomioon että tämä on ensimmäinen swappi puskurille joka sisältää pelkästään ykkösiä ja nollia pitkinä jonoina. Seuraavat swap:it lisäävät tietysti satunnaisuutta.

$ ./newressu --single -x
ressu 1    00 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 00 00 00 00 00 00 00 00 00 00
ressu 2    00 01 01 01 01 00 01 00 01 00 00 01 01 00 01 01 00 00 01 00 01 01 01 01 00 00 00 01 01 01 00 01
ressu 1    01 02 02 02 02 00 02 00 02 01 01 03 03 01 03 03 01 01 03 01 03 03 03 03 01 01 01 03 03 03 01 03
ressu 2    02 00 01 02 03 02 02 01 01 03 01 03 03 00 01 03 01 03 00 01 01 01 01 03 01 03 01 03 01 00 01 02
ressu 1    04 00 02 04 06 05 05 03 03 07 03 07 07 01 03 07 03 07 01 03 03 03 03 07 03 07 03 07 03 01 03 05
ressu 2    00 02 00 06 03 03 03 00 06 00 06 02 01 01 04 02 02 05 01 00 04 03 04 02 01 07 06 05 01 06 02 06
ressu 1    00 04 00 0c 06 06 06 00 0c 00 0c 04 02 02 08 04 04 0b 03 01 09 07 09 05 03 0f 0d 0b 03 0d 05 0d
ressu 2    09 03 0e 03 07 0a 00 08 0b 0f 0a 08 07 0e 08 0c 08 07 0a 0d 05 05 08 06 02 0b 01 04 07 0c 0d 0d
ressu 1    13 07 1d 07 0f 15 01 11 17 1f 15 11 0f 1d 11 19 11 0f 15 1b 0b 0b 11 0d 05 17 03 09 0f 19 1b 1b
ressu 2    15 1b 0d 05 0f 1c 04 0c 01 1f 1b 05 07 0d 18 19 04 04 10 06 08 18 14 08 0a 0e 1c 1e 04 15 15 1a
ressu 1    2a 36 1a 0a 1e 38 08 18 02 3f 37 0b 0f 1b 31 33 09 09 21 0d 11 31 29 11 15 1d 39 3d 09 2b 2b 35
ressu 2    27 0f 16 25 24 05 02 0b 37 0d 36 20 1b 0c 08 2e 3b 22 29 30 27 01 28 16 02 05 2b 1c 08 2b 0a 33
ressu 1    4e 1e 2c 4a 48 0a 04 16 6e 1a 6c 40 36 18 10 5c 76 44 52 60 4e 02 50 2d 05 0b 57 39 11 57 15 67
ressu 2    7f 5c 6a 0a 47 3b 40 21 2e 5a 29 0e 3f 12 60 23 4b 45 37 73 77 53 3e 42 70 68 20 49 52 25 58 57
ressu 1    fe b8 d4 14 8e 76 80 42 5c b4 52 1d 7f 25 c1 47 97 8b 6f e7 ef a7 7d 85 e1 d1 41 93 a5 4b b1 af
ressu 2    87 ee b4 41 69 fc 4e 70 0a b3 d6 46 73 40 a0 0c 01 76 64 73 7e 98 0c e3 9a e3 3e b1 c2 b4 88 e2
00000 87eeb44169fc4e700ab3d6467340a00c017664737e980ce39ae33eb1c2b488e2
00001 1a6ea6583d89530e1e1455442446f3cf1ef119d28334f674db7ac30757fd0691
00002 b777adfc58cf9d4bdf8029a6df98ffd44aa4adfc52675306e72a38193a337da7
00003 c184bde3d90783c51df7ae206a245449f4c9ad78f2fa052c2113543c69753cf0
00004 c600776f4299a0729aabac97cc07956a6c3b616b499430c7c6165bbba81febaf
00005 e817ad5bece64ffcb8a7ca1b1d85a3b2eebbec925403d802c11612e99914531e
00006 619a2e82b7b321608dbf28d67044b542e2bdc3428d098430662f639c5af26963
00007 e76d4bf95ddd078a958bd537deb70bce2d6df7f8a0b49aa326565e64161acbbb
00008 736d9bbcae75d5037da8c545b78ff0042ee45b6a337384b8c389276ab7db962f
00009 94f2f4ef5cd97591af58c3e4669f64386d05f8cc890eb49946ed451372278168

Koko ensimmäinen swap:ätty blokki DEBUG5c päällä:

ressu 3    01 00 01 01 01 01 00 00 01 01 00 00 00 00 01 01 01 01 01 01 01 01 00 01 00 01 01 01 00 01 01 00
ressu 3    01 01 00 00 00 01 01 00 00 00 00 01 01 01 00 00 00 00 00 01 01 00 00 01 00 00 01 00 01 01 00 01
ressu 3    00 01 01 01 01 00 00 00 00 00 01 00 00 00 00 00 00 01 00 00 01 00 01 00 01 01 00 01 00 01 00 01
ressu 3    01 00 00 00 00 00 01 00 01 01 00 00 00 01 01 01 01 01 01 01 01 01 01 01 00 00 01 00 01 01 00 01
ressu 3    01 01 01 01 01 01 01 01 01 01 01 01 01 00 01 01 00 01 01 00 01 01 00 00 01 01 01 01 01 01 01 01
ressu 3    01 01 01 01 01 01 00 00 00 00 01 00 00 01 00 01 01 01 01 01 01 00 01 01 01 01 00 01 00 01 01 00
ressu 3    00 00 00 00 01 01 00 00 00 00 01 01 01 00 00 01 01 01 00 01 00 00 00 00 00 00 00 00 01 00 00 01
ressu 3    00 00 01 00 01 00 01 00 01 00 00 01 00 00 00 00 01 00 00 01 00 00 01 00 01 01 01 01 00 00 01 00
ressu 3    01 00 01 00 00 00 00 00 00 01 00 01 01 00 00 01 01 01 00 01 00 01 01 00 01 00 01 00 01 00 00 01
ressu 3    00 01 01 00 00 01 00 01 00 01 00 01 00 00 00 00 00 00 00 00 00 01 00 01 00 00 01 01 00 01 01 00
ressu 3    01 00 01 00 01 01 01 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 01 00 01 00 01 01 01
ressu 3    00 00 00 00 00 00 00 00 00 01 01 01 00 01 00 01 00 01 00 01 00 01 00 00 00 00 00 00 00 00 00 00
ressu 3    00 01 00 00 01 00 00 00 01 00 01 01 01 01 00 01 00 01 01 00 01 00 00 01 01 01 01 00 01 01 01 01
ressu 3    01 01 00 00 01 01 01 01 01 01 01 01 00 01 01 01 01 01 00 00 01 01 00 01 01 01 00 01 01 01 00 00
ressu 3    01 00 01 01 01 01 01 00 01 01 01 01 01 00 01 00 01 00 01 01 01 00 01 00 00 00 00 00 01 01 01 00
ressu 3    01 01 00 01 00 01 00 01 01 01 00 00 00 01 01 00 01 00 00 00 01 01 00 00 01 01 00 01 00 01 00 01
ressu 3    00 00 00 01 01 00 01 00 00 00 01 01 00 01 00 00 00 00 00 01 01 01 01 01 01 01 00 00 00 00 01 01
ressu 3    00 01 01 01 00 00 00 00 01 01 00 01 01 01 01 00 00 00 01 00 00 01 01 01 01 00 00 00 01 01 00 00
ressu 3    01 01 00 00 01 00 01 01 00 00 01 01 00 01 01 00 01 01 00 01 01 00 00 01 01 01 00 00 00 00 01 00
ressu 3    00 00 01 01 00 00 00 01 00 00 00 01 00 00 00 01 00 01 01 00 00 00 01 00 00 00 01 00 00 00 00 01
ressu 3    01 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 01 01 00 01 00 00 01 01 01
ressu 3    00 01 01 01 01 00 01 01 01 01 01 00 01 01 01 01 01 01 01 01 01 01 01 01 01 01 00 00 00 00 00 01
ressu 3    01 01 01 01 01 01 01 01 01 01 00 01 00 00 00 00 00 00 00 00 00 01 01 01 01 00 01 00 01 01 01 01
ressu 3    01 00 01 00 00 00 01 01 01 01 01 01 01 01 01 00 01 01 01 01 01 01 00 01 00 00 00 00 00 00 00 00
ressu 3    01 00 00 00 01 01 01 01 01 01 01 00 01 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 01 01 01 00
ressu 3    01 01 01 01 01 01 00 00 00 00 00 01 01 01 01 01 00 00 01 01 01 01 01 01 00 00 00 00 00 01 01 01
ressu 3    01 00 00 00 00 00 00 01 00 00 00 00 01 00 00 01 00 01 01 00 01 01 00 00 00 01 01 01 01 01 00 00
ressu 3    00 01 01 01 01 00 00 00 00 00 01 01 01 01 01 01 01 01 01 01 00 00 00 00 00 00 01 01 01 01 00 00
ressu 3    00 00 00 00 01 01 01 00 01 00 00 00 00 00 01 01 01 01 01 01 00 00 00 00 01 01 01 01 00 01 00 00
ressu 3    00 01 00 01 01 01 01 00 00 00 01 01 01 01 00 00 00 00 00 00 00 01 01 01 00 00 01 00 00 00 00 01
ressu 3    00 00 00 00 01 00 00 00 00 01 01 00 00 01 01 01 01 00 00 00 00 00 01 00 00 01 00 00 01 01 00 00
ressu 3    01 01 00 00 01 00 00 01 00 01 00 00 01 00 00 00 01 00 00 00 01 01 00 00 00 00 00 00 00 00 00 01

Edellistä DEBUG5C:llä tehtyä blokkia voi tietenkin verrata seuraavaan ressulla tehtyyn blokkiin:

$ ./newressu -w32 -l32 --lim2 -x
00000  0 0 0 1 0 0 0 0 0 1 0 1 1 0 1 0 1 0 0 1 0 1 0 1 1 0 1 0 0 1 1 0
00001  1 0 0 1 1 0 0 1 0 1 0 1 0 1 0 0 0 1 0 1 0 1 1 0 1 0 1 0 1 1 0 0
00002  1 0 1 0 1 0 0 1 1 0 0 1 0 1 1 0 0 0 1 0 1 0 0 0 0 0 0 1 0 1 1 1
00003  1 1 0 1 0 1 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 1 1 1 0
00004  1 0 0 0 0 0 0 1 1 1 1 0 0 0 0 0 0 1 0 0 0 1 1 1 1 1 1 0 1 1 0 1
00005  1 0 1 0 1 1 0 1 1 0 1 0 1 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 0 0
00006  0 1 0 1 0 1 0 1 1 1 1 1 1 1 0 0 1 1 1 1 1 0 1 0 0 0 0 1 1 1 1 0
00007  0 1 1 1 0 0 1 0 0 1 1 1 0 0 1 0 0 0 0 0 0 1 0 1 0 0 0 1 1 1 0 1
00008  0 0 1 1 0 1 1 0 1 0 0 1 0 1 1 0 0 1 1 0 1 0 1 1 0 0 1 0 1 0 0 0
00009  1 1 1 0 0 1 0 0 0 0 1 0 0 0 0 1 1 0 0 0 0 0 1 0 1 1 1 1 0 0 0 1
00010  0 1 0 0 0 0 1 0 1 1 0 0 0 0 1 0 1 1 0 1 0 0 0 1 1 0 0 1 1 0 0 1
00011  0 1 1 0 0 1 1 1 0 1 0 0 0 0 1 1 1 0 1 1 0 1 1 0 0 0 1 0 0 1 0 0
00012  0 1 0 0 1 1 1 1 0 1 1 0 0 0 1 0 1 1 0 0 0 1 1 0 0 1 0 0 0 0 1 1
00013  0 1 0 0 1 1 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0 1 1 1 0 1 1 1 1
00014  1 0 0 0 0 0 1 0 1 1 1 1 1 1 0 0 1 0 1 0 1 0 0 0 1 1 0 0 1 1 1 1
00015  1 1 1 0 1 1 1 1 0 0 1 0 0 0 0 1 0 1 1 0 0 1 1 0 0 1 1 1 1 0 0 1
00016  0 0 1 0 1 1 0 0 0 1 1 1 0 0 0 1 1 0 0 0 0 1 0 0 0 1 1 1 1 0 0 0
00017  0 0 1 1 0 1 1 1 1 0 0 1 1 1 0 0 0 0 1 1 1 0 0 1 1 0 1 1 0 0 0 1
00018  1 0 0 1 1 1 1 0 1 0 0 1 0 0 0 1 0 0 0 1 0 1 0 1 0 1 1 0 0 0 0 0
00019  0 1 1 1 0 1 1 1 1 0 0 1 1 0 1 0 1 0 0 1 1 1 1 1 1 1 1 0 0 1 0 0
00020  0 1 0 0 0 1 1 1 0 0 1 0 1 0 0 0 0 1 0 0 0 0 0 0 1 0 1 1 0 0 0 0
00021  1 1 1 1 0 0 1 1 1 1 0 1 0 0 0 1 1 1 1 0 0 1 1 1 0 0 0 0 0 0 1 0
00022  1 0 0 0 1 0 1 0 0 0 1 1 1 1 1 0 1 0 1 0 0 0 1 0 0 0 0 0 1 0 1 1
00023  0 0 1 0 1 1 1 1 1 1 0 1 0 0 1 1 1 0 0 1 0 1 1 1 1 1 1 1 0 1 0 0
00024  0 1 0 1 0 0 0 1 0 1 0 0 0 1 1 1 1 1 1 1 0 0 1 0 1 1 0 0 0 0 1 0
00025  0 1 1 1 1 0 0 1 0 0 1 1 0 0 0 1 0 1 0 1 1 0 0 1 1 1 1 1 1 0 0 1
00026  1 1 0 0 0 1 0 0 1 1 1 0 1 1 0 0 1 0 1 1 0 1 1 0 0 0 0 0 0 1 1 0
00027  0 0 1 0 1 1 1 1 1 1 1 0 1 1 1 0 1 1 1 1 0 0 1 0 1 1 0 1 1 1 0 0
00028  0 1 0 0 1 1 1 0 0 0 1 1 1 1 0 0 0 0 1 0 0 1 0 1 0 0 1 0 0 1 1 0
00029  0 0 1 1 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 1 0 0 0 0 1 1 1 1 1 0 0 0
00030  0 1 0 0 0 1 0 1 0 0 0 1 0 1 1 0 1 0 1 1 1 1 1 1 0 1 1 0 1 0 0 1
00031  1 0 1 1 1 1 1 0 0 0 0 1 0 1 0 0 1 1 0 0 0 0 0 0 0 1 1 1 1 0 1 1

Vihdoinkin pseudoressu:n koodi:

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

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

static void pseudoressu_internalbytes(unsigned char *digest)
{
  HashCtx hash;

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

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

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

#define aDEBUG23 2

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

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

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

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

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

  while(size > 0) {

    if(topup_counter <= 0) {
      if(init) {
	ressu_genbytes(sizeof(pseudoressu_key), pseudoressu_key); // set first key

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

	init = 0;
      }
      pseudoressu_topup(); // add randomness to key
#ifdef TOPUP_TWICE
      pseudoressu_topup(); // add randomness to key
#endif
      
      topup_counter = TOPUP_BYTES;
      blockbytes = 0;
    } // end of if(topup_counter <= 0)
    
    pseudoressu_internalbytes(digest); // get random bits using the key

    n = (size < sizeof(digest) ? size : sizeof(digest));

#ifdef DEBUG23
    ressu_dump("olddata", n, buffer, 32);
    ressu_dump("bytes", n, digest, 32);
#endif
    
    for(int c = 0; c < n; c++)
      buffer[c] ^= digest[c];

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

    blockbytes += n;
    if(blockbytes >= PSEUDORESSU_LIMIT && size > 0) {
      pseudoressu_internalbytes(pseudoressu_key); // replace key with new one

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

      blockbytes = 0;
    }

    topup_counter -= n;
  } // end of while(size>0)
  
  pseudoressu_internalbytes(pseudoressu_key); // replace key with new one

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

Satunnaislukuja käyttäen pseudoressua:

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

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

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

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

Sitten komentorivikytkimet ohjelman parametreista:

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

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

#endif
...
}

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

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

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

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

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

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

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

  return(ok);
}

#endif

#ifdef USE_RDRAND

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

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

  return(ok);
}

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

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

  return(int) ret;
}

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

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

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

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

      if(ret == 0)
	break;

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

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

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

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

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

  return(ret);
}

#endif // #ifdef USE_RDRAND

#ifdef USE_RDSEED

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

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

  return(ok);
}

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

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

  return(int) ret;
}

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

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

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

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

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

      if(ret == 0)
	break;

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

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

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

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

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

#endif // #ifdef USE_RDSEED

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

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

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

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

Lisätty hindi satunnaislukuaakkosiin kieliin:

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

Vielä koodi newressuun:

...
      } else if(!strcmp("--ind", argv[c]) // not ready
	     || !strcmp("--hin", argv[c])) { // Hindi alphabet
	//unsigned char *signs = "ऀँंःऺऻ़ऽािीुूृॄॅॆेैॉॊोौ्ॎॏॕॖॗॢॣ॒॑॓॔";
	digits =
	  "ऄअआइईउऊऋऌऍऎएऐऑऒओऔकखगघङचछजझञटठडढण"
	  "तथदधनऩपफबभमयरऱलळऴवशषसहऽॐक़ख़ग़ज़ड़"
	  "ढ़फ़य़ॠॡ।॥०१२३४५६७८९॰ॱॲॳॴॵॶॷॸॹॺॻॼॽॾ"
	  "ॿ"; // 97 characters

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

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

Lisätty vielä somalian ensimmäinen versio (seuraavassa versiossa ei vielä kaksoiskirjaimia: KH, SH, DH): (mukana myös columns rivit, joista lisää seuraavassa kappaleessa:)

$ ./newressu --som --columns
---------1111111111222222222233333333334444444444555555555566666666667
1234567890123456789012345678901234567890123456789012345678901234567890
======================================================================
00000 GDSBGHQOOAOHRJNLYGARTSELJDJLAJYYOTLEWEBWRXDNLQWTTMRYFOTJHGTWTYNF
00001 TCDCJXCNLCJJLCJORFYNALEYNTQNTKGTKAJNJFACAECQTJWKJLFJTXMLFMMYQCCC
00002 CWCYJKNEMGNQXMNRQYYAYQXDXSQEWWNSESTNGEGMBMKHCROKBMKCLAOSHCOJMGFA
00003 ARBTQCJNJOQOBRRGCXTMHNTOETNJXTOSRGSRFAOLKXGOLXDHGTCTBCCNTJFQCKAS
00004 WXGKLSXHEXXTXLKQRQSDYRWRTHXQBBKGTADDEXNGCXFBSROJXEHSDQEBLSCYCQQR
00005 HGOOEEKYHHEFGYGLQLCOXSNMSTGOKBQGJKHJJYJTOJOWNDGBGELFFFXWWHQGCGTG
00006 SJTQFSHOEYARAHOQSFBKSMXYOFXAEHFATSTYRAEWTNOSDQDATJNAGTXCYFCKCQHX
$

Vielä koodi somalian ensimmäisen version kirjaimiin:

      } else if(!strcmp("--som", argv[c]) ||
		!strcmp("--SOM", argv[c]) ||
		!strcmp("--SO", argv[c])) { // Somalia alphabet

#ifdef KOK
	// A, B, T, J, X, KH, D, R, S, SH, DH, C, G, F, Q, K, L, M, N, W, H, Y, E, and O
	digitsstrings = "A", "B", "T", "J", "X", "KH", "D", "R", "S", "SH", "DH", "C", "G", "F", "Q", "K", "L", "M", "N", "W", "H", "Y", "E", "O", "";

	int first = 1;
	unsigned char **pp;
	pp = digitsstrings;
	for(c = 0; strcmp(*pp, ""); c++) {
	  if(!first)
	    fprintf(stdout,", ");
	  fprintf(stdout,"%s", *pp);
	  first = 0;
	  pp++;
	}
	// missing KH SH DH
#endif
	digits = "ABTJXDRSCGFQKLMNWHYEO";
	charspaces = 0;
	charwidth = 1;
	size = 8;
	type = 0;

Lisätty –columns komentoriviparametri, jolla voidaan tulostaa sarakkeittain sarakkeen numero: ajo esimerkki

---------11111111112222222222333333333344444444445555555555666666666677
12345678901234567890123456789012345678901234567890123456789012345678901
=======================================================================
00000 22258382806296729238178486166796544611755807164847927666922883010
00001 28418563167711867913647921768757420667110694576793471041747264318
00002 99804558335755889341259131631520927482583107862745929217530498421
00003 51760111177576244043527804917085407731383726282400768462117274375
00004 89648866862627378375043043971820779839978322370961568999705923913
00005 29058447822637409075394304381942641680239167678291358293770305216
00006 02068139412914507088518193191859344515532139033119514656041976727

Ja koodi: (huomaa reilusti yli menevä maksimi sarakkeiden määrä, ylin rivi kertoo miljoonat. Toki vasta ensimmäinen rivi joka tarvitaan tulostetaan. mallilistassa ensimmäisellä rivillä on kymmenet)

  //
  // look thru command line parameters
  //
  
  for(c = 1; c < argc; c++) {
...
     } else if(!strcmp("--columns", argv[c])) {
	columns = !columns; // print column numbers
...
     }
 }
...
if(columns) {

    //---------11111111112222222222333333333344444444445555555555666666666677
    //12345678901234567890123456789012345678901234567890123456789012345678901
    //=======================================================================
    //00000 86747100388687087416829400778878989202760594993130062980826178851
    //00001 36862623069143814543525935285460059703805218323534266522497684582

    int digit, clines = 0, nonzero;
    for(c = 1000000; c >= 1; c /= 10) { // lines, 100000's, 10000's, 1000's ... 1's
      if(pchars < c)
	continue;
      nonzero = 0;
      for(d = 1; d <= pchars; d++) { // column numbers
	digit = (d / c) % 10;
	if(digit != 0)
	  nonzero = 1;
	if(nonzero)
	  fprintf(stdout,"%d", digit); // nonzero digits after first nonzero
	else
	  fprintf(stdout,"-"); // zeroes in beginning of row
      }
      fprintf(stdout,"\n");
      clines++;
    }
    for(d = 1; d <= pchars; d++)
      fprintf(stdout,"=");
    fprintf(stdout,"\n");
    clines++;
    lines -= clines
  } // if(columns

Muutettu komentoriviparametrien tiedoston kokoa määrittelevät parametrit samankaltaisiksi kaikissa testi ja newressu.c ohjelmissa: aluksi newressu.c:n tarvitsemat muuttujat: _set loppuiset parametrit muutetaan ykkösiksi jos ko parametri määritellään.

unsigned long long filesize = FILESIZE, blocks = BLOCKS;
unsigned int blocksize = BLOCKSIZE;
int filesize_set = 0, blocks_set = 0, blocksize_set = 0;

Sitten komentoriviparametri iffit: ensimmäinen kappale määrittelee –filesize parametrin, toinen kappale –blocksize parametrin ja viimeinen –blocks kappale määrittelee –blocks parametrin (blokkien lukumäärä).

      } else if(!strncmp("--filesize", argv[c], 10)) {
	if(*(argv[c] + 10) != '\0') {
	  filesize = getlonglong(argv[c] + 10);
	} else if(c + 1 < argc) {
	  filesize = getlonglong(argv[c + 1]);
	  c++;
	}
	filesize_set = 1;

      } else if(!strncmp("--blocksize", argv[c], 11)) {
	if(*(argv[c] + 11) != '\0') {
	  blocksize = getlonglong(argv[c] + 11);
	} else if(c + 1 < argc) {
	  blocksize = getlonglong(argv[c + 1]);
	  c++;
	}
	blocksize_set = 1;
	
      } else if(!strncmp("--blocks", argv[c], 8)) {
	if(*(argv[c] + 8) != '\0') {
	  blocks = getlonglong(argv[c] + 8);
	} else if(c + 1 < argc) {
	  blocks = getlonglong(argv[c + 1]);
	  c++;
	}
	blocks_set = 1;

Parametrien tarkistus ja puuttuvien laskenta: huomaa exfat korjaus, jossa tarkastetaan että blokin koko ei ylitä megaa merkkejä, ja jos ylittää se pyöristetään megaan ja blokkien määrä ja tiedoston koko lasketaan uudelleen. Ensimmäinen kappale laskee puuttuvan parametrin, ensimmäisenä lasketaan tiedoston koko (filesize), jos sitä ei ole parametreissa, sitten blokin koko ja viimeisenä blokkien lukumäärä, jos niitä ei ole annettu parametrinä.

Toinen kappale laskee miljoonaa merkkiä suuremman blokin koon miljoonaan, ja laskee uudelleen blokkien määrän ja tiedoston koon (kun blokin koko laskee, myös blokkien määrä ja tiedoiston koko muuttuu).

Kolmannessa kappaleessa tarkistetaan tiedoston koko (filesize) parametrin ylivuoto ja että blokkeja on vähintään yksi ja blokin koko on ainakin yksi.

Neljättä kappaletta tarvititaan silloin kun tarkistetaan että tiedoston koko(filesize), blokin koko (blocksize) ja blokkien määrä (blocks) parametrit on annettu oikein, kun ne on kaikki syötetty. Toki se vielä lopputarkastaa myös lasketut parametrit.

  // calculate missing file size parameters
  
  if(!filesize_set)
    filesize = blocks * blocksize;
  else if(!blocksize_set) {
    blocksize = (filesize + blocks - 1) / blocks; // round up
    filesize = blocks * blocksize;
  } else if(!blocks_set) {
    blocks = (filesize + blocksize - 1)/ blocksize; // round up
    filesize = blocks * blocksize;
  }

#define aEXFAT_FIX 2 // on for now
  
#ifdef EXFAT_FIX

  if(blocksize > KILO * KILO) { // max "blocksize" 1m (exfat)
    blocksize = KILO * KILO;
    blocks = (filesize + blocksize - 1) / blocksize;
    filesize = blocks * blocksize;
  }

#endif
  
  if(filesize != blocks * blocksize ||
     blocks < 1 || blocksize < 1) {
    fflush(stdout);
    fprintf(stderr,"%s: sample(): mismatched parameters", procname);
    fprintf(stderr,", blocks:%llu(", blocks);
    readablelonglong(stderr, blocks);
    fprintf(stderr,")");
    fprintf(stderr,", blocksize:%u(", blocksize);
    readablelonglong(stderr, blocksize);
    fprintf(stderr,")");
    fprintf(stderr,", filesize:%llu(", filesize);
    readablelonglong(stderr, filesize);
    fprintf(stderr,")");
    fprintf(stderr,"\n");
    fflush(stderr);
    exit(1);
  }
  
  if(filesize / blocks != blocksize) {
    fflush(stdout);
    fprintf(stderr,"%s: sample(): parameter overflow", procname);
    fprintf(stderr,", blocks:%llu(", blocks);
    readablelonglong(stderr, blocks);
    fprintf(stderr,")");
    fprintf(stderr,", blocksize:%u(", blocksize);
    readablelonglong(stderr, blocksize);
    fprintf(stderr,")");
    fprintf(stderr,", filesize:%llu(", filesize);
    readablelonglong(stderr, filesize);
    fprintf(stderr,")");
    fprintf(stderr,"\n");
    fflush(stderr);
    exit(1);
  }

Parametrien käsittelyssä käytetty getlonglong() funktio: getlonglong() käyttää getdigit():iä lukeakseen yhden numeromuotoisen merkin. Tässä numero voi mikä tahansa numero 0-9 tai kirjain a-z. Käytössä on vain binääri “01”, oktaali “01234567”, desimaali “0123456789”, heksa “0123456789abcdef”.

getlonglong():n ensimmäinen osuus lukee asiakkaan parametrin numerojärjestelmän (0b…, 0o…, 0d… tai 0x…), (esimerkiksi (0b1111 on 15, 0o10 on 8, 0xff on 255). Jos numerojärjestelmää ei ole annettu oletuksena on desimaali.

Toinen osuus laskee yhteen asiakkaan parametrin numeromerkit (esimerkiksi 8192) ja laskee ne yhteen huomioiden asiakkaan antaman numerojärjestelmin (binääri jne).

Kolmas osuus lukee asiakkaan antaman kertoimen (kilo, mega, giga, tera, peta tai eksa) esimerkiksi 1k, 1m, 2t. 8 merkin pituiseen kokonaislukuun mahtuu 15 eksaa ja rapiat.

Neljäs osuus tarkistaa ennettavan parametrin ylivuodon ja ynnää samaan parametriin liittyvät numero osuudet yhteen (esimerkiksi –filesize1g1m). Lopuksi poistetaan b tai ‘B’ lauseista kuten ‘100kb’ tai ‘1gb’.

int getdigit(unsigned char *p)
{
  int digit;
  
  if(*p >= '0' && *p <= '9')
    digit = *p - '0';
  else if(*p >= 'a' && *p <= 'z')
    digit = (*p - 'a') + 10;
  else if(*p >= 'A' && *p <= 'Z')
    digit = (*p - 'A') + 10;
  else
    digit = -1; // not found, illegal

  return(digit);
}

#define DEBUG32

//#define KILO 1000
#define KILO 1024

unsigned long long getlonglong(unsigned char *p2)
{
  int digit, base = 10;
  unsigned char *p = p2;
  unsigned long long totll, ll, prevll, multiplier;
  
  totll = 0;

  while(*p != '\0') { // works also: 1g100m & 1m20k and 1t1t etc...

    unsigned char *prevp = p;
    
    if(!strncmp("0x", p, 2)) {
      base = 16;
      p += 2;
      
    } else if(!strncmp("0d", p, 2)) {
      base = 10;
      p += 2;
      
    } else if(!strncmp("0o", p, 2)) {
      base = 8;
      p += 2;
      
    } else if(!strncmp("0b", p, 2)) {
      base = 2;
      p += 2;
      
    }
    
    ll = 0;
    while((digit = getdigit(p)) != -1 && digit < base) {
      ll = ll * base + digit;
      p++;
    }
    
    multiplier = 1;
    
    if(*p == 'k' || *p == 'K') {
      multiplier = KILO;
      p++;
      
    } else if(*p == 'm' || *p == 'M') {
      multiplier = (KILO * KILO);
      p++;
      
    } else if(*p == 'g' || *p == 'G') {
      multiplier = (KILO * KILO * KILO);
      p++;
      
    } else if(*p == 't' || *p == 'T') {
      multiplier = ((unsigned long long)KILO * KILO * KILO * KILO);
      p++;
      
    } else if(*p == 'p' || *p == 'P') {
      multiplier = ((unsigned long long)KILO * KILO * KILO * KILO * KILO);
      p++;
      
    } else if(*p == 'e' || *p == 'E') {
      multiplier = ((unsigned long long)KILO * KILO * KILO * KILO * KILO * KILO);
      p++;
      
    }
    
    prevll = ll;
    ll *= multiplier;
    if(ll / multiplier != prevll) {
      fflush(stdout);
      fprintf(stderr,"%s: multiply overflow", procname);
      fprintf(stderr,", string:'%s'", p2);
      fprintf(stderr,", digit:'");
      fprintfcharacter(stderr, p);
      fprintf(stderr,"'");
      fprintf(stderr,", value: %d", digit);
      fprintf(stderr,", base: %d", base);
      fprintf(stderr,", ll: %llu", prevll);
      fprintf(stderr,", multiplier: %llu", multiplier);
      fprintf(stderr,"\n");
      fflush(stderr);
    }
  
    if(*p == 'b' || *p == 'B') // remove last b (for bytes in 1tb, 1gb or 1mb)
      p++;
    
    totll += ll;

#ifdef DEBUG32
    fprintf(stderr,"string:'%s'", p2);
    fprintf(stderr,", base:%d(", base);
    readablelonglong(stderr, base);
    fprintf(stderr,")");
    fprintf(stderr,", multiplier:%llu(", multiplier);
    readablelonglong(stderr, multiplier);
    fprintf(stderr,")");
    fprintf(stderr,", prevll:%llu(", prevll);
    readablelonglong(stderr, prevll);
    fprintf(stderr,")");
    fprintf(stderr,", ll:%llu(", ll);
    readablelonglong(stderr, ll);
    fprintf(stderr,")");
    fprintf(stderr,", totll:%llu(", totll);
    readablelonglong(stderr, totll);
    fprintf(stderr,")");
    fprintf(stderr,"\n");
#endif
    
    if(prevp == p) // no progress
      break;
  }
  
  if(*p != '\0') {
    fflush(stdout);
    fprintf(stderr,"%s: illegal digit", procname);
    fprintf(stderr,", string:'%s'", p2);
    fprintf(stderr,", digit:'");
    fprintfcharacter(stderr, p);
    fprintf(stderr,"'");
    fprintf(stderr,", value: %d", digit);
    fprintf(stderr,", base: %d(", base);
    readablelonglong(stderr, base);
    fprintf(stderr,")");
    fprintf(stderr,"\n");
    fflush(stderr);
  }

  return(totll);
}

DEBUG32 on oletuksena päällä ja se tulostaa rivin näistä parametrien laskennoista: string kentässä on käsiteltävä parametri merkkijonona base on käyttäjän antama numerojärjestelmä, multiplier on käyttäjän antama kerroin (k, m, g, t, p, e), prevll on asiakkaan antama numero, ll on kerroin * numero, totll on samaan numeroon liittyvät numerot yhteensä.

$ ./newressu --sample --filesize 1g100m --blocksize1k
string:'1g100m', base:10(10B), multiplier:1073741824(1G), prevll:1(1B), ll:1073741824(1G), totll:1073741824(1G)
string:'1g100m', base:10(10B), multiplier:1048576(1M), prevll:100(100B), ll:104857600(100M), totll:1178599424(~1G)
string:'1k', base:10(10B), multiplier:1024(1K), prevll:1(1B), ll:1024(1K), totll:1024(1K)
blocksize:1024(1K), blocks:1150976(~1M), filesize:1178599424(~1G)
...
$ 

Edellisessä kuvassa on haluttu yhden gigan ja sadan megan kokoinen satunnaisbittitiedosto, jossa blokin koko (fwrite:n koko) on 8192. Ensimmäinen string: rivi edellisssä sisältää 1g tiedoston koosta, toinen rivi 100m tiedoston koosta, ja kolmas rivi 8192 blokin koon. viimeinen blocksize: rivi kertoo vielä yhteenvedon asetetuista tiedostokoon parametreista.

Vielä aiemmissa ohjelmissa käytetyt aliohjelmat: readablelonglong() tulostaa desimaalikokonaisluvun ja sen suuruusluokan (100g, 10m, 2t jne): toinen aliohjelma fprintfcharacter() tulostaa ufh merkin kokonaisuudessaan:

void readablelonglong(FILE *fp1, unsigned long long ll2)
{
  int c;
  unsigned long long multiplier, ll = ll2;
  
  // 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";

  c = 0;
  multiplier = 1;
  while(ll >= KILO) {
    ll /= KILO;
    multiplier *= KILO;
    c++;
  }

  if(ll * multiplier != ll2)
    fprintf(fp1,"~"); // approximately

  fprintf(fp1,"%llu%c", ll, units[c]);
}

void fprintfcharacter(FILE *fp1, unsigned char *p)
{
  fputc(*p, fp1); // print first char
  if(*p > 0xbf) { // first char utf8
    p++;
    for(;;) { // print rest of the utf8 chars
      if(*p > 0xbf || // new utf8 character
	 *p < 0x80 || // ascii character
	 *p == '\0') // end of string
	break;
      fputc(*p, fp1);
      p++;
    }
  }
}

Newressuun on lisätty uusi merkkijono salausmalli (stream cipher) –stream, jolla voidaan tehdä satunnaisbittejä merkkijonosta: seuraavat bitit on tehty merkkijonosta “kalakala”. Jatkossa merkkijonosalausta käytetään tiedoston kirjoituksen tarkastamiseen.

$ ./newressu --stream --key kalakala
./newressu: stream_open(): randomness from key, key=kalakala
00000 097007647538070452095222876773364032014848877614885432576274114682
00001 069076802144253754847369390425580418083695093228828897799956369200
00002 166962649730107510677372501914913551333963649770546607920656167530
00003 123872208527077508554473908037757655294059571650948600601580541243
00004 959069120976810662076442907983660853450311026021709027187795181219
00005 958500289994828533504137325466090300723020468339411775941540872433
00006 103918486603641932851463892918552658439592999940967471423412165257
00007 067647133783366171502688214398736241529441281236178360416684844342
00008 827011090709785699618541893769244994414243921218781696849617463710
00009 826005683785268736980725472626588977843361269856648811013296399762
jarik@jarik-HP-250-G8:~/f/ressurngd$

Lisätty integriteettitarkistus aina ./newressun ajon alkuun (DEBUG72). Sitä varten on lisätty seuraavat koodinpätkät.

#define DEBUG72 // default is on
		     
#ifdef DEBUG72	
  int flagint = 1;
#endif
....
  for(c = 1; c < argc; c++) {
...
#ifdef DEBUG72	
      } else if(!strcmp("--int", argv[c])) {
	flagint = !flagint;
	//fprintf(stdout,"Integrity test skipped...");
#endif
...
  }
....
#ifdef DEBUG72
  if(flagint) {
    unsigned char command[128];
    fprintf(stderr,"%s: Running integrity test(s)...",procname);
    fflush(stderr);
    sprintf(command,"./newressutest14.sh >newressutest14.rnd.`date +%%Y%%m%%d`");
    system(command);
    fprintf(stderr," done.\n");
    fflush(stderr);
  }
#endif

Lisäksi tuo edellisessä kappaleessa käynnistetty scripti newressutest.sh:

/bin/newressu --int --single --space2 --newline 5 -l1 $@
/bin/newressu --int --single --space3 --newline 5 -l1 $@
/bin/newressu --int --single --space4 --newline 5 -l1 $@
/bin/newressu --int --single --space5 --newline 5 -l1 $@
/bin/newressu --int --single --space6 --newline 5 -l1 $@
/bin/newressu --int --single --6bits --space -s1 -l1 $@
/bin/newressu --int --single --6bits --space -s2 -l1 $@
/bin/newressu --int --single --6bits --space -s3 -l1 $@
/bin/newressu --int --single --6bits --space -s4 -l1 $@
/bin/newressu --int --single --6bits --space -s5 -l1 $@
/bin/newressu --int --single --6bits --space -s6 -l1 $@
/bin/newressu --int --single --6bits --space -s7 -l1 $@
/bin/newressu --int --single --6bits --space -s8 -l1 $@
/bin/newressu --int --single --6bits --space -s9 -l1 $@
/bin/newressu --int --single --6bits --space -s10 -l1 $@
/bin/newressu --int --single --rand -l1 $@
/bin/newressu --int --single --alnum -l1 $@
/bin/newressu --int --single --alpha -l1 $@
/bin/newressu --int --single --graph -l1 $@
/bin/newressu --int --single --lower -l1 $@
/bin/newressu --int --single --punct -l1 $@
/bin/newressu --int --single --upper -l1 $@
/bin/newressu --int --single -d --lim41 -w7 --zero -l1 --lotto$@
/bin/newressu --int --single --1bit -l1 $@
/bin/newressu --int --single --2bits -l1 $@
/bin/newressu --int --single --3bits -l1 $@
/bin/newressu --int --single --oct -l1 $@
/bin/newressu --int --single --4bits -l1 $@
/bin/newressu --int --single --hex -l1 $@
/bin/newressu --int --single --HEX -l1 $@
/bin/newressu --int --single --5bits -l1 $@
/bin/newressu --int --single --6bits -l1 $@
/bin/newressu --int --single --dnk -l1 $@
/bin/newressu --int --single --nor -l1 $@
/bin/newressu --int --single --fin -l1 $@
/bin/newressu --int --single --swe -l1 $@
/bin/newressu --int --single --rus -l1 $@
/bin/newressu --int --single --est -l1 $@
/bin/newressu --int --single --ltu -l1 $@
/bin/newressu --int --single --lva -l1 $@
/bin/newressu --int --single --fra -l1 $@
/bin/newressu --int --single --gbr -l1 $@
/bin/newressu --int --single --usa -l1 $@
/bin/newressu --int --single --ita -l1 $@
/bin/newressu --int --single --eng -l1 $@
/bin/newressu --int --single --deu -l1 $@
/bin/newressu --int --single --grc -l1 $@
/bin/newressu --int --single --jp -l1 $@
/bin/newressu --int --single --jp1 -l1 $@
/bin/newressu --int --single --hir -l1 $@
/bin/newressu --int --single --jp2 -l1 $@
/bin/newressu --int --single --kat -l1 $@
/bin/newressu --int --single --jp3 -l1 $@
/bin/newressu --int --single --cn -l1 $@
/bin/newressu --int --single --kan -l1 $@
/bin/newressu --int --single --kor -l1 $@
/bin/newressu --int --single --ind -l1 $@
/bin/newressu --int --single --hin -l1 $@
/bin/newressu --int --single --som -l1 $@
/bin/newressu --int --single --SOM -l1 $@
/bin/newressu --int --single --SO -l1 $@
/bin/newressu --int --single --braille -l1 $@
/bin/newressu --int --single --dna -l1 $@
/bin/newressu --int --single --DNA -l1 $@

Raportti, joka tässä tapauksessa kirjoitettiin newressutest14.rnd.* tiedostoon on seuraavankaltainen:

00000   43547 02424  77426 40520  24483 85853  60125 09060  27766 91983
00000   90226 00953 26685  66371 98917 68085  20508 58887 33858  26510
00000   36046 40837 05103 72057  02162 62838 77719 36899  29862 14697
00000   36556 28423 75766 79658 12445  83960 32923 60357 04934 69935
00000   29071 78822 29171 57055 83957 60222  54011 66685 51620 90995
00000  x a d N 6 9 1 1 f A X t R p s B Z p B I Z Q E x N R l - f I d y j
00000  Z3 TR -s UJ dF bm L1 Uf iC w0 _m sK 2k bg vA LU W8 83 3R tA if x2
00000  F7K Ypn cRV fqT Sgd -nH xCg lfx mKa 1bO AQE OFH pfU Ytq xpH tw6
00000  -s0J cK8N FZpP rd2c tqQn 3gKJ 4vqB 6mC6 yA97 0S7P 3EBM 30vu LBP5
00000  2_lY0 N5uo4 Bf_uu EAEaU vyXiT Nm5BD 4004V 8sHqa fTjlL rdgiV IwJQo
00000  8-2Zwx gVbEem 0Mj1Eg yuUJfx perUOH TGNBnp jaNiOK RulLWh IpfY9P
00000  K-GfdFW wAsO75R xjhpfXE HxMtnkr sLY1620 zJimadt xYbLTvQ LOqwtmy
00000  jjDtKyoH MV2tEJ4p 8SywDwv- svDXLQbu Img1Xe-4 vfFnoIQg 81354mXg
00000  -V7ui7PEs xczU8mdRB LuRUcHPGt XzbIR427y yyDMjByDH v5fDDGYL8
00000  KNU_kIR7_g R_G2vivdDi urouyAaGdT YdtjjAl-Hj aCJCUgUdnL mx6j3PdM3Z
00000   41712 25645  39695 88861  41948 33840  36298 59751  77600 74349
00000 UBWuGDDcGpFkKNo244N5TVrH8TwEsl2eKypdBOIyBDkMoCaKwkT5IwTZo2yYum2dg
00000 kknksuixobxgyelguaxyknlbkotypickbqfxqcafuyshzflujousbwiykgyaxvfdy
00000 6EoMqaSuRdDe7m*xZ4-}ND%T';6MmD@KK~sZ`8^Lv*w2$,dv9(SC(.;Nx:6.vib(@
00000 stshrhhiupnmovnaovkzuifoohqivxyzrahmprolgqhjfbaaeigvueeuoqtqvyofk
00000 {%~')`%,%#;\<"\#}{#'<?]<)>>_!(:]~;~>=$+|<=!~-!:$,{]:+~.&}))^=!-`|
00000 HJGGZIRMQRWPXNELYARMBNDJLXCJMIOHEKVCKBQDUAGWJXAYMJTNPMTWNIQTKQMHA
00000  03 04 14 18 24 29 36
00000 1110110011100100110111111111011010110010111011110011011001000000
00000 3102022001303221331223300321321003220321212030231122110223131121
00000 742074745673401760037715534423267357677510113274147215471160147115
00000 154564406714644140021565367601517766620126222517076751422000524201
00000 2e5408c76d6619cfe329f7d133647d9252a5c207fa6591f863998a6711e85570
00000 04d2c38c643fa8f8307658a931256b3dc878adf7ea1ef6df9387351fe4eabb05
00000 CC807E1CB27065460191405263BDE3102150881F7E34A5190EE04E69EC6AF271
00000 D8TV7UC8P601U16IBA6TKUTECL49GV1LLPLAJ5I8G7KDEQ0BOUIUFKIEUCNVHL6K
00000 j7YBjWvXCovQdzVhONSoCArNTWzg5Gv0OX6jjMbzRi2ZUXHtazO85ZynzF6V-UwB
00000 zjDzoquNXiaxPTqØUDCifØqaGwÆnChKzfVXoKNDøxsoÅNmpoCpCAkRqiCÅTyÅLnw
00000 hoÅNMEhehæSYxktZNhCxvBÅZLkÅULXLyePDkobFQOÆcjmuizyUmGrKCAØuvvRNII
00000 VEåRÖtUObVVNoyDCÄjQWOälVäwtÅBAQNWqnYhJCAåGÖXqHyWäåXfåYJöSöRNCXEi
00000 jäHGrryvocÄoBVhwAÖÄHKTnMfOzUkwÄlZlxUöåhllÄnuÄkjCnLuEryjUiKNVSååt
00000 йЪлцЙощьрцСсЙьдрьжтсОфйхнЪгэрОйЭыЗФфЭеьЩэСлсиЕМоаЦеЫтХёлыЛЭЬаМиО
00000 UmTNPqcnötMmOCLUjZPŠmaKrqTiõSKCqŽÖmLdüHäöõmFjÜFeÜlÄjEÜWMgwyejÕqS
00000 keųzotDcVČųCŪyPmgcCinvueeįčšPIGUČIdųsEŪŪzjMSŲTŲfEFbĮIŽčsžBnūągČg
00000 uMĀĀbPģVBĢbAĀŪmrDhsnhĢhāUPžĪkzPAFdĀeķļčĶChēljmaaGIRŠĪŪaĪŽļķJjbtF
00000 foRubnHSkBtkPRptozSvDkwElsbHIIXerpLqfwzuigBxnSMoUQefFxkCOfXWYnSK
00000 wDasgHAgMzdATKKUyYiltXbvLgVPgHilhklyyNUNGYTAyBHXYJpktfBUmFVBfnPp
00000 zTTpcRUDmJLElleTngPqqznspGXOjlibUeafTYPrfhtmOTLvyVgIQlTECHlosAVc
00000 hzyItLFHUjOfeoiOEqwrkKNLwFjfMzHsGqnQcygUIdJQqgAcqsbjcQfDOOdqJKnr
00000 BFPJszetXmOVAoloouHdLmrdyePkRnucovlhqWYAKIPeYOoqcqLiMWBcgtFdWXzi
00000 zQrfpGßTVSöBzRYWyRDbieFcNOQniXßäTHRtYöwPiuaJGQẞzäEÜsSLOänTOaKItp
00000 ΠΤΑΚκιΥνυΗΧξΗΗΚαΨχΨαΟυΚεΠβΓΔΟΙδΔΟΚΠΓκΕαφΤζψΘΙΒνθμπαδΥΦΗθφΣραΖΔΝρ
00000 萳示柍璭忾驒蘼崰橚吅廊負稽郱鿠億唍沪瞵瀩駤靿睘虶驚蒡滫恻埤鞇朿郖
00000 ゕゖいにっげもぐんぃりくぇすでひゅょぉゖもえゑあざりはのえゎゃう
00000 せゕほへへうはじさげうべぴめれたぉをわばあんぞゎひきへゖょだむゎ
00000 チ・デペカヵヹヶゼペ・チヵヒヿニクレカニラュ゠エラセジナヰパロナ
00000 ヮェタヨ゠グゴャヺパウキジホゾダーパワズチフュョェサスヾペペゼル
00000 啊鯡螟酠晜嬕饅鈿岚勂戍來顕憠奠芍榠耍榬太闳媀軳嫔藇嚦璨散寠分蓪螛
00000 茴夲邛嵸詠乧過仧窅惸懸究昱堿猗橦碁縺佅畞碜鄫俩潚罇綰荸曛胐蒀芜饎
00000 灧僕蘀鴐暍項縅篸焜埾宧蝗鿉乪醩鰆僧襅歹珛捫釧賄馂穩祘虓栏财果睊芧
00000 얆뺖귄톹쭅쑣찰깃뤢텀횾첮죏옩춺퇄셱텧볾짣궭픒흽좪쯏듖헕홲뉡둵혧쯣
00000 ॷएॼऎ९एधऄॺॼक़९पॴॶॠऍॾमतॴआॽॡझषऱॳॠ॥औॴ१ॠ६खॷळओढ।७५ऴॸऱञऄअव८भॵऱलहतऴ५ऍग़६९॥
00000 ॶ।ॐब३थएढडखॵहकभमतऎङऩऔऽवअसग़ऐझखॹ७ॷॱकउयड़ञगआॻ७ॱओढ़ॱ८॥ऋ।ऱफ़७ॿरग़३ऴखछ६ॐझऱड़
00000 YNEETLLWFJHRFRCWQOESWRTJHQQHXLWFTYWBDOGKGFBBREGEOBJXFCRBBHMHAMOX
00000 MRJYXJENQXKMANSBYAWOKSSYLHXNWKDGCHFGKXQLHJKWYLWDSABFALDDJKBYBLFM
00000 FOXQWXXNEACXWNSCLBLYETARFTLGMCQKRDYEQCYGXAQRHNOOWADYGTTMAGKAHXNK
00000 ⣓⣕⢋⠀⣉⠄⢭⡞⢅⠑⢜⢙⠭⠾⣖⢵⡎⡩⡜⣾⢢⡳⡳⠡⢸⢨⡆⣠⡐⣲⢩⡔⠻⢄⠌⡹⡗⡕⢏⢛⠈⠾⣦⠾⡹⢔⠻⢱⡴⡰⢪⡬⡜⡔⠷⣆⣼⣬⡦⢏⢢⢚⡻⡛
00000 cactttgttggactatttcaagtggcgatcatctctttctgagaatctgaggttggcggagcat
00000 TAAAACACCCAGAATCCGACCATCTATGGTCGGCACACTAAAATAATACCCTCAAAAATTGAGA

Lisätty pistekirjoitus (–braille) satunnaislukujen raportti “kieleksi”, tässä mallituloste:

$ ./newressu --braille
00000 ⡚⡤⣤⣒⡯⣃⡝⣾⡎⣂⣭⡕⣹⠸⢗⢋⠇⡛⡎⡹⣝⣑⠅⠰⡵⠑⠝⠙⢗⡞⡤⣎⣉⡫⡺⠥⣇⠣⡟⣺⣃⠊⠞⢰⡝⠋⢅⡞⢀⣋⢷⠐⢄⢯⢰⠛⠳⣡⠽⢋⠃⢌⢺⢒
00001 ⠇⣤⣌⢚⣅⢯⠐⡄⡮⠅⢺⣸⠛⡞⡋⣋⢿⢌⡸⠢⢠⡋⡕⣼⠀⢾⡉⠆⡗⣋⢖⠞⢌⣤⣶⠦⠟⣛⠩⢼⣌⢛⠥⠤⡭⠉⡈⢜⢄⡑⣟⠯⡓⢺⣈⡅⠔⡿⠞⠍⢩⢸⣭⠛
00002 ⣥⠚⡓⢵⢎⡶⡕⡦⣹⠞⡜⡼⢳⣆⡇⣎⡭⣱⣽⡒⡉⡙⠌⢲⡄⡲⣸⣒⢷⠜⣧⠊⢺⠫⢚⠦⡷⠑⢬⢓⣲⢧⠼⠏⡃⡰⣀⢱⠚⣨⣓⣌⣌⢜⡣⣐⡝⡠⡉⣎⣃⣫⢍⢁
00003 ⡝⠞⢃⢡⣬⣾⢡⣁⠈⣐⡡⢆⡩⣉⡓⠃⣃⣡⡤⡊⢕⡯⣽⠋⣅⠞⡛⣠⢰⣏⠎⣱⡣⣄⡀⠂⣏⣶⠠⢜⣦⢦⣸⣴⠏⣬⢩⡗⡳⣬⣊⡙⠁⠜⢆⠃⢒⣤⣐⠞⡞⠹⣞⠦
00004 ⡷⠎⢩⠊⡎⡔⡁⠶⢞⡡⢧⡑⠿⠃⢀⡌⠰⠹⡐⢘⠔⢞⠶⠚⠁⢜⢸⣬⣋⢦⣩⢱⢺⠢⣯⡂⢁⠡⠕⣟⠏⢠⡧⡻⢾⠂⠰⡫⡘⢦⢍⠨⢰⠽⡑⢝⠩⠛⢕⡾⡰⣕⣪⣩
00005 ⢻⣞⡯⢫⢹⠳⠗⡚⢻⢒⣪⡞⢑⣀⡙⠀⡺⡯⡥⣯⠊⣭⢋⠼⠇⡝⢿⣀⡢⡞⢉⢫⠨⢕⢕⠳⣶⡥⠙⠐⠐⡭⡎⣜⣭⠆⡋⣤⡕⡨⡮⠙⣷⢧⡞⠛⡝⢤⢒⠊⢉⣡⠗⡧
00006 ⣀⣚⡌⡌⢫⣎⢭⢰⣿⣠⡖⣯⢖⢞⡿⠼⢘⠚⠫⠏⢺⠯⠌⠖⠠⣥⠓⠋⠓⠫⡮⣱⡙⡻⣴⠰⠺⣺⣙⢕⢞⠛⣗⠘⡻⡑⠛⢛⠎⣋⢌⡣⠙⢮⠒⢾⡢⢸⠭⠈⠙⡘⡚⢽
00007 ⣜⣇⢺⣨⡮⡞⡧⢰⣲⡑⢵⡕⡄⠜⠴⣎⡂⢷⡕⢓⣤⡥⠠⡉⣼⠼⣗⢥⡨⠏⣕⠯⣰⣙⡾⠗⡬⡐⢈⠓⢳⠻⡺⢩⠺⣼⢯⠐⣠⢰⡆⠍⣏⣌⢕⠉⡖⣸⡗⠄⡌⢔⢗⡀
00008 ⣓⢪⣙⠔⣣⡦⠊⢖⢪⢌⣍⠶⢑⡗⡟⠔⡊⡖⢒⡚⣜⠧⣧⠚⣕⢩⣀⣚⠥⢎⢪⢫⢙⡉⠬⣤⠯⡒⡉⠝⢠⡅⣷⢠⣬⡁⠂⠼⢕⣪⢕⡍⣸⡞⡨⢼⠙⡪⣳⢄⢱⡫⢪⢗
00009 ⡌⣟⠠⡵⡛⠍⣙⡤⡻⠽⢈⢗⢄⢯⣒⢳⠘⢺⣸⠚⠘⢍⠜⢊⣿⢅⢗⡀⢩⠶⠴⡰⠎⡁⡽⣦⢉⣍⠽⡿⣲⠞⢩⠤⠜⡌⢰⣒⢬⢹⢨⣭⣆⢠⠽⢿⡄⠊⣳⡎⠈⣺⡲⣺
$

Ja koodi braillen tulostamiseen:

...
      } else if(!strcmp("--braille", argv[c])) { // Braille alphabet
	digits = // 0x2800 - 0x2900
	  "⠀⠁⠂⠃⠄⠅⠆⠇⠈⠉⠊⠋⠌⠍⠎⠏⠐⠑⠒⠓⠔⠕⠖⠗⠘⠙⠚⠛⠜⠝⠞⠟⠠⠡⠢⠣⠤⠥⠦⠧⠨⠩⠪⠫⠬⠭⠮⠯⠰⠱⠲⠳⠴⠵⠶⠷⠸⠹⠺⠻⠼⠽⠾⠿"
	  "⡀⡁⡂⡃⡄⡅⡆⡇⡈⡉⡊⡋⡌⡍⡎⡏⡐⡑⡒⡓⡔⡕⡖⡗⡘⡙⡚⡛⡜⡝⡞⡟⡠⡡⡢⡣⡤⡥⡦⡧⡨⡩⡪⡫⡬⡭⡮⡯⡰⡱⡲⡳⡴⡵⡶⡷⡸⡹⡺⡻⡼⡽⡾⡿"
	  "⢀⢁⢂⢃⢄⢅⢆⢇⢈⢉⢊⢋⢌⢍⢎⢏⢐⢑⢒⢓⢔⢕⢖⢗⢘⢙⢚⢛⢜⢝⢞⢟⢠⢡⢢⢣⢤⢥⢦⢧⢨⢩⢪⢫⢬⢭⢮⢯⢰⢱⢲⢳⢴⢵⢶⢷⢸⢹⢺⢻⢼⢽⢾⢿"
	  "⣀⣁⣂⣃⣄⣅⣆⣇⣈⣉⣊⣋⣌⣍⣎⣏⣐⣑⣒⣓⣔⣕⣖⣗⣘⣙⣚⣛⣜⣝⣞⣟⣠⣡⣢⣣⣤⣥⣦⣧⣨⣩⣪⣫⣬⣭⣮⣯⣰⣱⣲⣳⣴⣵⣶⣷⣸⣹⣺⣻⣼⣽⣾⣿";
	charspaces = 0;
	charwidth = 1;
	size = 8;
	type = 0;
...

Lisätty newressu:un uusi komentorivikytkin –utf8, joka tulostaa listan utf8 merkeistä, lista on seuraavanlainen:

      ----------------111111111111111122222222222222223333333333333333
      0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
      ================================================================
    0 --- control characters ---       !"#$%&'()*+,-./0123456789:;<=>?
   40 @ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
   80 --- utf8 characters ---         --- utf8 characters ---         
   c0 --- utf8 characters ---         --- utf8 characters ---         
  100 ĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿ
  140 ŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſ
  180 ƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐƑƒƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƻƼƽƾƿ
  1c0 ǀǁǂǃDŽDždžLJLjljNJNjnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZDzdzǴǵǶǷǸǹǺǻǼǽǾǿ
  200 ȀȁȂȃȄȅȆȇȈȉȊȋȌȍȎȏȐȑȒȓȔȕȖȗȘșȚțȜȝȞȟȠȡȢȣȤȥȦȧȨȩȪȫȬȭȮȯȰȱȲȳȴȵȶȷȸȹȺȻȼȽȾȿ
  240 ɀɁɂɃɄɅɆɇɈɉɊɋɌɍɎɏɐɑɒɓɔɕɖɗɘəɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿ
  280 ʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯʰʱʲʳʴʵʶʷʸʹʺʻʼʽʾʿ
  2c0 ˀˁ˂˃˄˅ˆˇˈˉˊˋˌˍˎˏːˑ˒˓˔˕˖˗˘˙˚˛˜˝˞˟ˠˡˢˣˤ˥˦˧˨˩˪˫ˬ˭ˮ˯˰˱˲˳˴˵˶˷˸˹˺˻˼˽˾˿
  300 ̀́̂̃̄̅̆̇̈̉
  340 ͇͈͉̀́͂̓̈́͆ͅͰͱͲͳʹ͵Ͷͷ͸͹ͺͻͼͽ;Ϳ
  380 ΀΁΂΃΄΅Ά·ΈΉΊ΋Ό΍ΎΏΐΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ΢ΣΤΥΦΧΨΩΪΫάέήίΰαβγδεζηθικλμνξο
  3c0 πρςστυφχψωϊϋόύώϏϐϑϒϓϔϕϖϗϘϙϚϛϜϝϞϟϠϡϢϣϤϥϦϧϨϩϪϫϬϭϮϯϰϱϲϳϴϵ϶ϷϸϹϺϻϼϽϾϿ
  400 ЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмноп
  440 рстуфхцчшщъыьэюяѐёђѓєѕіїјљњћќѝўџѠѡѢѣѤѥѦѧѨѩѪѫѬѭѮѯѰѱѲѳѴѵѶѷѸѹѺѻѼѽѾѿ
  480 Ҁҁ҂҃҄҅҆҇҈҉ҊҋҌҍҎҏҐґҒғҔҕҖҗҘҙҚқҜҝҞҟҠҡҢңҤҥҦҧҨҩҪҫҬҭҮүҰұҲҳҴҵҶҷҸҹҺһҼҽҾҿ
  4c0 ӀӁӂӃӄӅӆӇӈӉӊӋӌӍӎӏӐӑӒӓӔӕӖӗӘәӚӛӜӝӞӟӠӡӢӣӤӥӦӧӨөӪӫӬӭӮӯӰӱӲӳӴӵӶӷӸӹӺӻӼӽӾӿ
  500 ԀԁԂԃԄԅԆԇԈԉԊԋԌԍԎԏԐԑԒԓԔԕԖԗԘԙԚԛԜԝԞԟԠԡԢԣԤԥԦԧԨԩԪԫԬԭԮԯ԰ԱԲԳԴԵԶԷԸԹԺԻԼԽԾԿ
  540 ՀՁՂՃՄՅՆՇՈՉՊՋՌՍՎՏՐՑՒՓՔՕՖ՗՘ՙ՚՛՜՝՞՟ՠաբգդեզէըթժիլխծկհձղճմյնշոչպջռսվտ

Vielä koodi, joka tulostaa edellisen listan:

#ifdef DEBUG59

  if(utf8) {
    fprintf(stdout,"%5s ","");
    fprintf(stdout,"----------------");
    fprintf(stdout,"1111111111111111");
    fprintf(stdout,"2222222222222222");
    fprintf(stdout,"3333333333333333");
    fprintf(stdout,"\n");
    fprintf(stdout,"%5s ","");
    fprintf(stdout,"0123456789abcdef");
    fprintf(stdout,"0123456789abcdef");
    fprintf(stdout,"0123456789abcdef");
    fprintf(stdout,"0123456789abcdef");
    fprintf(stdout,"\n");
    fprintf(stdout,"%5s ","");
    fprintf(stdout,"================");
    fprintf(stdout,"================");
    fprintf(stdout,"================");
    fprintf(stdout,"================");
    fprintf(stdout,"\n");

    int utf8output = 0;
    for(c = 0; c < 65535*4; c++) { // skip control chars
      //for(c = 0x2800; c < 0x2900; c++) { // braille
      unsigned char buffer10[10];
      if(c % 64 == 0) {
	if(c > 0) {
	  fprintf(stdout,"\n");
	  utf8output = 0;
	}
	fprintf(stdout,"%5x ",c);
	utf8output = 1; 
     }
      if(c >= 0 && c < 32) {
	c += 31;
	fprintf(stdout, "--- control characters ---      ");
	utf8output = 1;
      } else if(c >= 128 && c < 256) {
	c += 31;
	fprintf(stdout, "--- utf8 characters ---         ");
	utf8output = 1;
      } else {
	codetoutf8(buffer10, c);
	fprintf(stdout,"%s",buffer10);
	utf8output = 1;
      }
    }
    if(utf8output)
      fprintf(stdout,"\n");
    fflush(stdout);
    return(0);
  }
#endif

Koodaamisen lisäksi tutkin ressun tuottamaa satunnaisuutta parilla ohjelmalla (koodasin ne tietysti myös). Tässä ensimmäinen: Ennen se nimi oli cstat, uusi nimi on newressutest7.

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

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

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

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

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

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

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

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

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

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

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

	all = !all;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  unsigned long int singles = 0;

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

  // read input file and calculate
  // statistics

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

    if(verbose) {
      fputc(ch, stdout);
    }

    lastch = ch;
    
    // calculate character counts for
    // character statistics
    
    charcounts[ch]++;

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

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

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

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

      m1counts[c]++;
    }

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

    // calculate m2 multiple
    // counts
    
    if(first) {
      prevch = ch;
      m2length = 1;
      first = 0;
    } else {
      if(prevch == ch) {
	m2length++;
      } else {
	prevch = ch;
	m2counts[m2length - 1]++;
	m2length = 1;
      }
    }
    
    singles++;

  } // end of while((ch = fgetc(fp1)) != EOF)

  if(singles > 0)
    m2counts[m2length - 1]++; // add last m2 multiple

  if(lastch != '\n') // last char not lf, print lf
    fputc('\n', stdout);

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

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

  // print character counts if needed
  
  long int min, max;
  long int total;
  
  if(chars) {

    min = LONG_MAX;
    max = LONG_MIN;
    total = 0;
    
    fprintf(stdout,"chars     ");
    first = 1;

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

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

	long int temp = charcounts[c];
	
	fprintf(stdout,":%lu", temp);
	
	total += temp;

	if(min > temp)
	  min = temp;
	if(max < temp)
	  max = temp;

	first = 0;
      }
    }
    fprintf(stdout,", total:%ld", total);
    fprintf(stdout,", min:%ld", min);
    fprintf(stdout,", max:%ld", max);
    fprintf(stdout,"\n");
    fflush(stdout);
  } // end of if(chars)

  // calculate character statistics

  unsigned long int ctotal = 0, count = 0, average = 0,
    characters = 0, utf8first = 0, utf8other = 0,
    asciicnt = 0, ctrlcnt = 0;

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

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

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

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

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

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

  double entropy = 0;

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

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

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

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

  // calculate average
  
  average = ctotal / count;

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

    min = LONG_MAX;
    max = LONG_MIN;
    long int total = 0;
    
    first = 1;

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

      if(c <= 0x20)
	continue;

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

	long int temp;
	temp = average - charcounts[c];

	fprintf(stdout,":%ld", temp);

	total += temp;

	if(min > temp)
	  min = temp;
	if(max < temp)
	  max = temp;

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

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

  // print statistics on m1 multiples

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

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

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

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

  // difference between statistics and
  // real m1 counts

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

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

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

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

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

  // expected m2 multiple statistics

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

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

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

    min = LONG_MAX;
    max = LONG_MIN;    
    long int diffall = 0;

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

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

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

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

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

#define aDEBUG87 2

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Seuraavassa ajossa newressua ajetaan useita kertoja, ja tulostetaan raportti ensimmäisen merkin satunnaisuudesta:

$ cat newressuruns.sh
#!/bin/bash
echo "Statistics for first character on multiple runs of newressu: (./newressu --single --bin $@ -w1 -s1 --lineno -l1)"
rm -f newressuruns.rnd; for ((c=0;c<10000;c++)); do ./newressu --single --bin $@ -w1 -s1 --lineno -l1 >>newressuruns.rnd; done; cat newressuruns.rnd | ./newressutest7; rm -f newressuruns.rnd
$ ./newressuruns.sh
Statistics for first character on multiple runs of newressu: (./newressu --single --bin  -w1 -s1 --lineno -l1)
ctrl      LF:10000
chars     0:5046, 1:4954, total:10000, min:4954, max:5046
cdiff     0:-46, 1:46, total:0, min:-46, max:46
cstats    min:4954, max:5046, total:10000, count:2, average:5000, characters:10000, utf8first:0, utf8other:0, ascii:10000, ctrl:10000, entropy:0.999939
multiple1 singles:10000, twins:4950, triplets:2457, quadruplets:1236, quintuplets:608, sixlets:308, 7lets:150, 8lets:71, 9lets:36, 10lets:18, 11lets:7, 12lets:1, total:19842
m1expect  singles:10000, twins:5000, triplets:2500, quadruplets:1250, quintuplets:625, sixlets:312, 7lets:156, 8lets:78, 9lets:39, 10lets:19, 11lets:9, 12lets:4, 13lets:2, 14lets:1, total:19995
m1diff    singles:0, twins:50, triplets:43, quadruplets:14, quintuplets:17, sixlets:4, 7lets:6, 8lets:7, 9lets:3, 10lets:1, 11lets:2, 12lets:3, 13lets:2, 14lets:1, total:153, min:0, max:50
multiple2 singles:2557, twins:1272, triplets:593, quadruplets:328, quintuplets:142, sixlets:79, 7lets:44, 8lets:17, 9lets:7, 10lets:5, 11lets:5, 12lets:1, total:10000
m2expect  singles:2500, twins:1250, triplets:625, quadruplets:312, quintuplets:156, sixlets:78, 7lets:39, 8lets:19, 9lets:9, 10lets:4, 11lets:2, 12lets:1, total:9951
m2diff    singles:-57, twins:-22, triplets:32, quadruplets:-16, quintuplets:14, sixlets:-1, 7lets:-5, 8lets:2, 9lets:2, 10lets:-1, 11lets:-3, 12lets:0, total:-55, min:-57, max:32
m2dist    singles:3, twins:7, triplets:16, quadruplets:30, quintuplets:70, sixlets:126, 7lets:227, 8lets:588, 9lets:1428, 10lets:2000, 11lets:2000, 12lets:10000, total:10000
$

Raportin rivit puhuvat sen puolesta että tilastot toteutuvat myös eri ajot ylittävissä raporteissa. Tässä raportti jakautuu puoliksi nolliin ja ykkösiin, ja se tietysti johtaa siihen että myös datan monikot ovat tilaston mukaisia. Jos meillä on datana nolla, todennäköisyys on 50% että sen perään tulee toinen nolla, ja kahden nollan perään tulee nolla 50% tapauksista. Ressuhan satunnaistaa koko puskurin ennen ensimmäisen merkin palauttamista.

Toinen ohjelma jonka kirjoitin: tässä ideana on etsiä satunnaisjonoista tuplia. Tuplien maksimipituuden ja määrän pitäisi korreloida tiedoston tietueiden määrän kanssa. Esimerkiksi 64 merkkiä pitkiä tuplia ei pitäisi tulla, niiden todennäköisyys on niin pieni. –stat kytkimellä on aloiteltu tilaston laskemista, se on kuitenkin kesken. Ohjelma on nimetty uudelleen newressutest11:ksi ja komentoriviparametrejä on muutettu selkeämmäksi. Toki ohjelman mallikomennot jatkussa tarvitsevat uudet parametrit.

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

unsigned char *procname;
static unsigned char *programname = "newressutest11/newressutest version 0.5 ©";
static unsigned char *copyright = "Copyright (c) 2022-2023 Jari Kuivaniemi, Helsinki, Finland. Kaikki oikeudet pidätetään!";

#define FILESIZE 210
#define BINSIZE 10 // bytes per line
#define LINESIZE 21 // BINSIZE * 2 + 1
#define LINES 10
#define BLOCKSIZE 1024

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

#define INPUT_RESSU 0
#define INPUT_PSEUDORESSU 1
#define INPUT_FASTRESSU 2
#define INPUT_SINGLERESSU 3
#define INPUT_RDRAND 4
#define INPUT_RDSEED 5
#define INPUT_URANDOM 6
#define INPUT_RANDOM 7

#define USE_RDRAND 2
#define USE_RDSEED 2

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

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

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

int input = 0;

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

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

int ressu_genbyte()
{
  unsigned char ch;

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

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

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

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

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

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

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

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

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

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

  return(ch);
}

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

  if(lastlimit != limit) { // if limit changes, calculate new highlimit and bytes
    lastlimit = limit;
    if(limit <= 0x100) {
      // highest multiplier of limit that fits to needed bytes
      highlimit = (0x100 / limit) * limit;
      // number of bytes needed
      bytes = 1;

    } else if(limit <= 0x10000) {
      highlimit = (0x10000 / limit) * limit;
      bytes = 2;

    } else if(limit <= 0x1000000) {
      highlimit = (0x1000000 / limit) * limit;
      bytes = 3;

    } else if(limit <= 0x100000000) {
      highlimit = (0x100000000 / limit) * limit;
      bytes = 4;

    } else if(limit <= 0x10000000000) {
      highlimit = (0x10000000000 / limit) * limit;
      bytes = 5;

    } else if(limit <= 0x1000000000000) {
      highlimit = (0x1000000000000 / limit) * limit;
      bytes = 6;

    } else if(limit <= 0x100000000000000) {
      highlimit = (0x100000000000000 / limit) * limit;      
      bytes = 7;

    } else { // if(limit <= 0xffffffffffffffff) {
      highlimit = (0xffffffffffffffff / limit) * limit;      
      bytes = 8;

    }
  } // if(lastlimit != limit)

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

  word %= limit;
  
  return(word);
}

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

//#define KILO 1000
#define KILO 1024

int stat = 0;

#ifdef NOTUSED

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

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

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

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

#endif

#ifdef KOK

double fact(long n)
{
  unsigned long l;
  double result = 1;

  for(l = 1; l <= n; l++) {
    result = result * l;
  }
  return(result);
}

#endif

void fprintfcharacter(FILE *fp1, unsigned char *p)
{
  fputc(*p, fp1); // print first char
  if(*p > 0xbf) { // first char utf8
    p++;
    for(;;) { // print rest of the utf8 chars
      if(*p > 0xbf || // new utf8 character
	 *p < 0x80 || // ascii character
	 *p == '\0') // end of string
	break;
      fputc(*p, fp1);
      p++;
    }
  }
}

int getdigit(unsigned char *p)
{
  int digit;
  
  if(*p >= '0' && *p <= '9')
    digit = *p - '0';
  else if(*p >= 'a' && *p <= 'z')
    digit = (*p - 'a') + 10;
  else if(*p >= 'A' && *p <= 'Z')
    digit = (*p - 'A') + 10;
  else
    digit = -1; // not found, illegal

  return(digit);
}

void readablelonglong(FILE *fp1, unsigned long long ll2)
{
  int c;
  unsigned long long multiplier, ll = ll2;

  // 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";

  c = 0;
  multiplier = 1;
  while(ll >= KILO) {
    ll /= KILO;
    multiplier *= KILO;
    c++;
  }

  if(ll * multiplier != ll2)
    fprintf(fp1,"~"); // approximately

  fprintf(fp1,"%llu%c", ll, units[c]);
}

#define DEBUG45 2

unsigned long long getlonglong(unsigned char *p2)
{
  int digit, base = 10;
  unsigned char *p = p2;
  unsigned long long totll, ll, prevll, multiplier;
  
  totll = 0;

  while(*p != '\0') { // works also: 1g100m & 1m20k and 1t1t etc...

    unsigned char *prevp = p;
    
    if(!strncmp("0x", p, 2)) {
      base = 16;
      p += 2;
      
    } else if(!strncmp("0d", p, 2)) {
      base = 10;
      p += 2;
      
    } else if(!strncmp("0o", p, 2)) {
      base = 8;
      p += 2;
      
    } else if(!strncmp("0b", p, 2)) {
      base = 2;
      p += 2;
      
    }
    
    ll = 0;
    while((digit = getdigit(p)) != -1 && digit < base) {
      ll = ll * base + digit;
      p++;

    }
    
    multiplier = 1;
    
    if(*p == 'k' || *p == 'K') {
      multiplier = KILO;
      p++;
      
    } else if(*p == 'm' || *p == 'M') {
      multiplier = (KILO * KILO);
      p++;
      
    } else if(*p == 'g' || *p == 'G') {
      multiplier = (KILO * KILO * KILO);
      p++;
      
    } else if(*p == 't' || *p == 'T') {
      multiplier = ((unsigned long long)KILO * KILO * KILO * KILO);
      p++;
      
    } else if(*p == 'p' || *p == 'P') {
      multiplier = ((unsigned long long)KILO * KILO * KILO * KILO * KILO);
      p++;
      
    } else if(*p == 'e' || *p == 'E') {
      multiplier = ((unsigned long long)KILO * KILO * KILO * KILO * KILO * KILO);
      p++;
      
    }
    
    prevll = ll;
    ll *= multiplier;
    if(ll / multiplier != prevll) {
      fflush(stdout);
      fprintf(stderr,"%s: multiply overflow", procname);
      fprintf(stderr,", string:'%s'", p2);
      fprintf(stderr,", digit:'");
      fprintfcharacter(stderr, p);
      fprintf(stderr,"'");
      fprintf(stderr,", value:%d", digit);
      fprintf(stderr,", base:%u(", base);
      readablelonglong(stderr, base);
      fprintf(stderr,")");
      fprintf(stderr,", ll:%llu(", prevll);
      readablelonglong(stderr, prevll);
      fprintf(stderr,")");
      fprintf(stderr,", multiplier:%llu(", multiplier);
      readablelonglong(stderr, multiplier);
      fprintf(stderr,")");
      fprintf(stderr,"\n");
      fflush(stderr);
      exit(1);

    }
  
    if(*p == 'b' || *p == 'B') // remove last b (for bytes in 1tb)
      p++;
    
    totll += ll;

#ifdef DEBUG45
    fprintf(stderr,"string:'%s'", p2);
    fprintf(stderr,", base:%u(", base);
    readablelonglong(stderr, base);
    fprintf(stderr,")");
    fprintf(stderr,", multiplier:%llu(", multiplier);
    readablelonglong(stderr, multiplier);
    fprintf(stderr,")");
    fprintf(stderr,", prevll:%llu(", prevll);
    readablelonglong(stderr, prevll);
    fprintf(stderr,")");
    fprintf(stderr,", ll:%llu(", ll);
    readablelonglong(stderr, ll);
    fprintf(stderr,")");
    fprintf(stderr,", totll:%llu(", totll);
    readablelonglong(stderr, totll);
    fprintf(stderr,")");
    fprintf(stderr,"\n");
#endif
    
    if(prevp == p) // no progress
      break;
  }
  
  if(*p != '\0') {
    fflush(stdout);
    fprintf(stderr,"%s: illegal digit", procname);
    fprintf(stderr,", string:'%s'", p2);
    fprintf(stderr,", digit:'");
    fprintfcharacter(stderr, p);
    fprintf(stderr,"'");
    fprintf(stderr,", value:%d", digit);
    fprintf(stderr,", base:%u(", base);
    readablelonglong(stderr, base);
    fprintf(stderr,")");
    fprintf(stderr,"\n");
    fflush(stderr);
    exit(1);
  }

  return(totll);
}

#define FILE_HASH 2 // on by default

#ifdef FILE_HASH

void hashfinal2string(unsigned char *hashstring, unsigned char *final)
{
  for(int c = 0; c < HashLen; c++) {
    sprintf(hashstring + 2 * c, "%02x", final[c]);
  }
}

#endif

#define DEBUG79 2

extern unsigned int fixedclockchainlength;
int clockmode; // 0 = normal, 1 = fixed
void ressu_setclock(int);

int main(int argc, char *argv[])
{
  int c, percentageline = 1, prevpros, pros, quiet = 0, help = 0;
  int filesize_set = 0, binsize_set = 0, linesize_set = 0, lines_set = 0;
  unsigned long long filesize = FILESIZE, lines = LINES;
  unsigned int binsize = BINSIZE, linesize = LINESIZE;
  int stdoutflag = 0;
  unsigned long long plines = 0;
  unsigned char *buffer, filename[128] = "";
  FILE *fp1;
  
  procname = argv[0];

#ifdef KOK
  long l, l2;
  for(long l = 1; l < 10; l++) {
    fprintf(stderr,"c:%ld", l);
    l2 = fact(l);
    fprintf(stderr,", result:%ld", l2);
    fprintf(stderr,"\n");
  }
#endif
  
  for(c = 1; c < argc; c++) {

    if(c == argc - 1 && !strcmp(argv[c], "-")) { // last option hyphen
      stdoutflag = 1; // output to stdout
      continue;
    }
    
    if(strncmp("-", argv[c], 1)) { // filename not expected
      fprintf(stderr,"%s: filename not expected", procname);
      fprintf(stderr,", parameter:%s", argv[c]);
      fprintf(stderr,"\n");
      exit(1);
    }
    
    if(!strncmp("-", argv[c], 1)) { // option starting with hyphen
      
      if(!strncmp("-o", argv[c], 2)) { // output filename
	if(*(argv[c] + 2) != '\0') {
	  strncpy(filename, argv[c] + 2, sizeof(filename));
	} else if(c + 1 < argc) {
	  strncpy(filename, argv[c + 1], sizeof(filename));
	  c++;
	}
	stdoutflag = 0;
	
      } else if(!strcmp("--stdout", argv[c])) { // output stdout
	stdoutflag = 1; // output to stdout
	filename[0] = '\0';
	
      } else if(!strcmp("--help", argv[c]) ||
	 !strcmp("-?", argv[c]) ) {
	help = 1;

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

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

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

      } else if(!strncmp("--filesize", argv[c], 10)) {
	if(*(argv[c] + 10) != '\0') {
	  filesize = getlonglong(argv[c] + 10);
	} else if(c + 1 < argc) {
	  filesize = getlonglong(argv[c + 1]);
	  c++;
	}
	filesize_set = 1;

      } else if(!strncmp("--linesize", argv[c], 10)) {
	if(*(argv[c] + 10) != '\0') {
	  linesize = getlonglong(argv[c] + 10);
	} else if(c + 1 < argc) {
	  linesize = getlonglong(argv[c + 1]);
	  c++;
	}
	linesize_set = 1;
	
      } else if(!strncmp("--lines", argv[c], 7)) {
	if(*(argv[c] + 7) != '\0') {
	  lines = getlonglong(argv[c] + 7);
	} else if(c + 1 < argc) {
	  lines = getlonglong(argv[c + 1]);
	  c++;
	}
	lines_set = 1;

      } else if(!strncmp("--binsize", argv[c], 9)) {
	if(*(argv[c] + 9) != '\0') {
	  binsize = getlonglong(argv[c] + 9);
	} else if(c + 1 < argc) {
	  binsize = getlonglong(argv[c + 1]);
	  c++;
	}
	binsize_set = 1;

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

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

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

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

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

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

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

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

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

#endif
      } else if(!strncmp("--fixedclock", argv[c], 12)) {
	if(*(argv[c] + 12)!='\0' && atoi(argv[c] + 12) > 1) {
	  fixedclockchainlength = atoi(argv[c] + 12);
	  clockmode = 1;
	} else if(c + 1 < argc && atoi(argv[c + 1]) > 1) {
	  fixedclockchainlength = atoi(argv[c + 1]);
	  clockmode = 1;
	  c++;
	} else {
	  clockmode = !clockmode;
	}

	if(fixedclockchainlength < 5 ||
	   fixedclockchainlength > 255)
	  fixedclockchainlength = 50;

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

  if(!linesize_set)
    linesize = binsize * 2 + 1;
  else if(!binsize_set)
    binsize = (linesize - 1) / 2;

  if(!filesize_set)
    filesize = lines * linesize;
  else if(!lines_set) {
    lines = (filesize + linesize - 1) / linesize; // round up
    filesize = lines * linesize;
  } else if(!linesize_set) {
    linesize = (filesize + lines - 1) / lines; // round up
    if((linesize & 1) == 0) // up to x * 2 + 1
      linesize++;
    binsize = (linesize - 1) / 2;
    filesize = lines * linesize;
  }

#define aEXFAT_FIX 2
  
#ifdef EXFAT_FIX
  
  if(linesize >= KILO * KILO) { // max linesize 1m (exfat)
    linesize = KILO * KILO - 1;
    binsize = (linesize - 1) / 2;
    lines = (filesize + linesize - 1) / linesize;
    filesize = lines * linesize;
  }

#endif
  
  if(linesize != binsize * 2 + 1 ||
     linesize < 3 || binsize < 1) {
    fflush(stdout);
    fprintf(stderr,"%s: mismatched parameters", procname);
    fprintf(stderr,", linesize:%u(", linesize);
    readablelonglong(stderr, linesize);
    fprintf(stderr,")");
    fprintf(stderr,", binsize:%u(", binsize);
    readablelonglong(stderr, binsize);
    fprintf(stderr,")");
    fprintf(stderr,"\n");
    fflush(stderr);
    exit(1);
  }

  if(filesize != lines * linesize ||
     lines < 1 || linesize < 3) {
    fflush(stdout);
    fprintf(stderr,"%s: mismatched parameters", procname);
    fprintf(stderr,", lines:%llu(", lines);
    readablelonglong(stderr, lines);
    fprintf(stderr,")");
    fprintf(stderr,", linesize:%u(", linesize);
    readablelonglong(stderr, linesize);
    fprintf(stderr,")");
    fprintf(stderr,", filesize:%llu(", filesize);
    readablelonglong(stderr, filesize);
    fprintf(stderr,")");
    fprintf(stderr,"\n");
    fflush(stderr);
    exit(1);
  }

  if(filesize / lines != linesize) { // test overflow too
    fflush(stdout);
    fprintf(stderr,"%s: parameter overflow", procname);
    fprintf(stderr,", lines:%llu(", lines);
    readablelonglong(stderr, lines);
    fprintf(stderr,")");
    fprintf(stderr,", linesize:%u(", linesize);
    readablelonglong(stderr, linesize);
    fprintf(stderr,")");
    fprintf(stderr,", filesize:%llu(", filesize);
    readablelonglong(stderr, filesize);
    fprintf(stderr,")");
    fprintf(stderr,"\n");
    fflush(stderr);
    exit(1);
  }

  // print help message if needed
  
  if(help) {
    fprintf(stderr,"%s", procname);
    fprintf(stderr,"\n\t[-o filename]");
    //fprintf(stderr," [--stdout]");
    fprintf(stderr,"\n\t[--filesize base-number-multiplier]"); 
    fprintf(stderr,"\n\t[--lines base-number-multiplier]");
    fprintf(stderr," [--linesize base-number-multiplier]");
    fprintf(stderr,"\n\t[--binsize base-number-multiplier]");
    fprintf(stderr,"\n\t[--ressu]");
    fprintf(stderr," [--pseudoressu]");
    fprintf(stderr," [--fastressu]");
    fprintf(stderr," [--singleressu]");
#ifdef INPUT_RDRAND
    fprintf(stderr," [--rdrand]");
#endif
#ifdef INPUT_RDSEED
    fprintf(stderr," [--rdseed]");
#endif
    fprintf(stderr," [--urandom]");
#ifdef USE_RANDOM
    fprintf(stderr," [--random]");
#endif
    fprintf(stderr," [--fixedclock]");
    fprintf(stderr,"\n\t[--help]");
    fprintf(stderr," [--copyright]");
    fprintf(stderr," [--version]");
    fprintf(stderr," [--stat]");
    fprintf(stderr," [--verbose]");
    fprintf(stderr," [--quiet]");
    fprintf(stderr,"\n");
    fprintf(stderr,"Examples:\n");
    fprintf(stderr,"\t$ %s --single --filesize1g\n", procname);
    fprintf(stderr,"\t$ %s --single --filesize1g --lines1m\n", procname);
    fprintf(stderr,"\t$ %s --single --filesize1g --linesize25\n", procname);
    fprintf(stderr,"\t$ %s --single --filesize1g --binsize12\n", procname);
    exit(1);
  } // end of if(help)

#ifdef DEBUG79

  // print filesize parameters
  
  fprintf(stderr,"input:%d", input);
  fprintf(stderr,", lines:%llu(", lines);
  readablelonglong(stderr, lines);
  fprintf(stderr,")");
  fprintf(stderr,", binsize:%u(", binsize);
  readablelonglong(stderr, binsize);
  fprintf(stderr,")");
  fprintf(stderr,", linesize:%u(", linesize);
  readablelonglong(stderr, linesize);
  fprintf(stderr,")");
  fprintf(stderr,", filesize:%llu(", filesize);
  readablelonglong(stderr, filesize);
  fprintf(stderr,")");
  fprintf(stderr,"\n");
  fflush(stderr);
  
#endif
  
  if(!stdoutflag && filename[0] == '\0') {

    // find first available filename,
    // or empty "slot"

    for(c = 1; c <= 99999; c++) {
      sprintf(filename, "newressutest11.%d.rnd", c);
      if((fp1 = fopen(filename, "r")) != NULL) {
	fclose(fp1);
	continue;
      }

#ifdef FILE_HASH
      unsigned char filename2[138];
      sprintf(filename2,"%s.sha256", filename);
      if((fp1 = fopen(filename2, "r")) != NULL) {
	fclose(fp1);
	continue;
      }
#endif

      break;
    }
  }

  if(stdoutflag) {
    if(isatty(STDOUT_FILENO)) { // 0=stdin, 1=stdout, 2=stderr
      percentageline = 0;
    }
  }
    
  if(stdoutflag) 
    fprintf(stderr,"filename:-\n");
  else
    fprintf(stderr,"filename:%s\n", filename);
    
  if(stdoutflag == 1) {
    fp1 = stdout;
  } else if((fp1 = fopen(filename, "w")) == NULL) {
    fprintf(stderr,"%s: cannot open file", procname);
    fprintf(stderr,", filename:%s", filename);
    fprintf(stderr,"\n");
    exit(1);
  }
  
  if(stat) {
  }

#ifdef FILE_HASH
  
  unsigned char digest[HashLen];
  HashCtx hash;

  HashInit(&hash); // initialize hash

#endif
  
  if((buffer = malloc(linesize + 1)) == NULL) { // space for '\0' too
    fprintf(stderr,"%s: cannot allocate memory (buffer)\n", procname);
    exit(1);
  }

  memset(buffer, 0, linesize + 1);

#ifdef EXFAT_FIX
  
  int mbinlines = (KILO * KILO) / linesize;
  if(mbinlines == 0)
    mbinlines = 1;
  fprintf(stderr,"mbinlines:%d\n", mbinlines);

#endif
  
  prevpros = -1;
  
  // fill first hex string

  for(c = 0; c < binsize; c++)
    sprintf(buffer + c * 2, "%02x", (unsigned int)ressu_gen_limit(256));
  buffer[linesize - 1] = '\n';
  buffer[linesize] = '\0';
  
  for(;;) {

    // bug! this program produces fixed length records
    // and for some reason records end up being variable
    // length records when they reach my removable usb
    // drive (exfat). I am hoping this is silly one.
    // next if() checks record length, and prints stars for
    // variable length records. Havent seen stars yet...
    // seems that fflush(), sync() fixes the problem.
    
    if(linesize != strlen(buffer)) {
      fprintf(stderr,"*");
      fflush(stderr);
    }
    
#ifdef FILE_HASH
    HashUpdate(&hash, buffer, linesize); // calculate hash
#endif
      
    if(fwrite(buffer, 1, linesize, fp1) < linesize) {
      fprintf(stderr,"%s:", procname);
      fprintf(stderr," cannot write file");
      fprintf(stderr,"\n");
      exit(1);      
    }
    
#ifdef EXFAT_FIX

    if(plines > 0 && plines % mbinlines == 0) { // fflush and sync (EXFAT)
      fflush(stdout);
      sync();
    }

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

    // remove first byte

    memmove(buffer, buffer + 2, linesize - 1);

    // add new last byte

    sprintf(buffer + linesize - 3, "%02x", (unsigned int)ressu_gen_limit(256));

    buffer[linesize - 1] = '\n';
    buffer[linesize] = '\0';
    
    if(percentageline) { // && !quiet) {
      pros = (int)((double) plines / lines * 1000);
      if(prevpros != pros) {
	fflush(stdout);
	prevpros = pros;
	fprintf(stderr,"\rDone:%d.%d%%", pros / 10, pros % 10);
	fflush(stderr);
      }	
    }
  }

#ifdef FILE_HASH
  HashFinal(digest, &hash); // calculate hash
#endif
  
  if(percentageline) { // && !quiet) {
    fprintf(stderr,"\rDone:100.0%%  \n");
  }

#ifdef EXFAT_FIX
  
  fflush(stdout); // fflush and sync (EXFAT)
  sync();

#endif
  
  if(!stdoutflag)
    fclose(fp1);
  
#ifdef FILE_HASH

  unsigned char hashstring[2 * HashLen + 1];
  
  hashfinal2string(hashstring, digest);
  
  fprintf(stderr,"%s: hashed file", procname);

  if(filename[0] != '\0') {
    fprintf(stderr,", filename:%s", filename);

    unsigned char filename2[138];

    sprintf(filename2,"%s.sha256", filename);

    fprintf(stderr,", hashfilename:%s", filename2);

    if((fp1 = fopen(filename2, "w")) != NULL) {
      fprintf(fp1,"%s\n", hashstring);
      fclose(fp1);
    }
  } // end of if(filename[0] != '\0'
  
  fprintf(stderr,", sha256:%s\n", hashstring);

  if(!stdoutflag) {
    fprintf(stderr,"%s: checking sha256", procname);
    fprintf(stderr,", filename:%s\n", filename);
    unsigned char command[1024];
    sprintf(command,"sha256sum %s", filename);
    system(command);
  }
  
#endif
  
  free(buffer);
  
  return(0);
}

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

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

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

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

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

#define BUFLEN 1024

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

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

Esimerkkejä tarkistusohjelman ajosta: ensimmäinen esimerkki

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

Toinen esimerkki, joka tarkastaa sortin:

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

Kolmas. jossa tarkistetaan tehty tiedosto:

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

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

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

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

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

Tässä on ajettu newressutest11:sta exfat levyllä. Tässä versiossa on jo fflush ja sync kutsut. 256 gigainen tiedosto on ajettu onnistuneesti. Ajossa löytyi 4 tuplaa 32, 55, 77 ja 97 alkuiset. Kaikki tuplat ovat 16 pitkiä, eikä niissä ole samoja merkkejö 16 merkin jälkeen.

media$ ./newressutest11 --single --filesize256g | sort -T./temp/ | uniq -D --check-chars=16
string:'256g', base:10, multiplier:1073741824, prevll:256, ll:274877906944, totll:274877906944
input:3, lines:13089424141(~12G), binsize:10(10B), linesize:21(21B), filesize:274877906961(~256G)
filename:-
done:100.0%  
./newressutest11: hashed file, sha256:f459507a66824e6e83b98d044c49bb39243bb320fb475158d9a6acedb8e34bcd
32630a8e6ddcc9406ba7
32630a8e6ddcc940be5d
5548db9879bbd10c5499
5548db9879bbd10c62c2
777432efb42ea3a89ca3
777432efb42ea3a8ce06
971fd08fa651e6980065
971fd08fa651e6985c9c
media$ 

Kolmas ohjelma tulostaa raportin, josta voi vertailla monikkojen eri merkkien esiintymistä. Chars rivi on kuten ennenkin yksittäisten eri merkkien esiintymät yhteensä. Chars rivillä on myös keskiarvo kokonaislukuna. Diff(eillä) rivillä on ero tilastolliseen määrään. m1digits singles rivillä on yksösten määrät (erikseen ‘0’, ‘1’, … ‘f’) tällä tulosteella (–hex). Vastaavasti m1digdiff rivillä on erot tilastoon. Erojen pitäisi olla sekä positiivisia että negatiivisia suhteellisen pieniä arvoja. dexp kentässä on odotettu määrä. Twins, triplets, quadruplets ja quintuplets rivejä luetaan samalla tavalla kun singles rivejä. Multiple1, m1exp, m1diff, multiple2, m2exp ja m2diff rivit ovat samankaltaisia kun edellisellä tulosteella.

$ ./newressu --lineno --space --hex -l10000 | ./newressutest8
ctrl      LF:10000, space:130000
chars     0:35215, 1:34828, 2:34811, 3:34884, 4:34798, 5:35748, 6:35354, 7:34477, 8:35022, 9:34711, a:35015, b:34997, c:35401, d:34890, e:34756, f:35093, total:560000, count:16, average:35000, total:560000, min:34477, max:35748
cdiff     0:-215, 1:172, 2:189, 3:116, 4:202, 5:-748, 6:-354, 7:523, 8:-22, 9:289, a:-15, b:3, c:-401, d:110, e:244, f:-93, total:0, min:-748, max:523
multiple1 singles:560000, twins:35117, triplets:2606, quadruplets:328, quintuplets:39, sixlets:3, total:598093
m1exp     singles:560000, twins:35000, triplets:2187, quadruplets:136, quintuplets:8, sixlets:0, total:597331
m1diff    singles:0, twins:-117, triplets:-419, quadruplets:-192, quintuplets:-31, sixlets:-3, total:-762
m1digits  singles 0:35215, 1:34828, 2:34811, 3:34884, 4:34798, 5:35748, 6:35354, 7:34477, 8:35022, 9:34711, a:35015, b:34997, c:35401, d:34890, e:34756, f:35093, dexp:35000, total:560000, texp:560000, min:34477, max:35748
m1digdiff singles 0:-215, 1:172, 2:189, 3:116, 4:202, 5:-748, 6:-354, 7:523, 8:-22, 9:289, a:-15, b:3, c:-401, d:110, e:244, f:-93, total:0, min:-748, max:523
m1digits  twins 0:2219, 1:2285, 2:2248, 3:2223, 4:2128, 5:2185, 6:2244, 7:2049, 8:2216, 9:2263, a:2142, b:2137, c:2233, d:2226, e:2189, f:2130, dexp:2187, total:35117, texp:35000, min:2049, max:2285
m1digdiff twins 0:-32, 1:-98, 2:-61, 3:-36, 4:59, 5:2, 6:-57, 7:138, 8:-29, 9:-76, a:45, b:50, c:-46, d:-39, e:-2, f:57, total:-125, min:-98, max:138
m1digits  triplets 0:167, 1:178, 2:180, 3:163, 4:128, 5:154, 6:153, 7:160, 8:161, 9:170, a:136, b:162, c:173, d:182, e:192, f:147, dexp:136, total:2606, texp:2187, min:128, max:192
m1digdiff triplets 0:-31, 1:-42, 2:-44, 3:-27, 4:8, 5:-18, 6:-17, 7:-24, 8:-25, 9:-34, a:0, b:-26, c:-37, d:-46, e:-56, f:-11, total:-430, min:-56, max:8
m1digits  quadruplets 0:24, 1:27, 2:27, 3:21, 4:10, 5:13, 6:22, 7:19, 8:21, 9:17, a:20, b:15, c:23, d:24, e:29, f:16, dexp:8, total:328, texp:136, min:10, max:29
m1digdiff quadruplets 0:-16, 1:-19, 2:-19, 3:-13, 4:-2, 5:-5, 6:-14, 7:-11, 8:-13, 9:-9, a:-12, b:-7, c:-15, d:-16, e:-21, f:-8, total:-200, min:-21, max:-2
m1digits  quintuplets 0:6, 1:3, 2:2, 3:2, 4:1, 6:4, 7:3, 8:3, a:4, c:3, d:2, e:5, f:1, dexp:0, total:39, texp:8, min:1, max:6
m1digdiff quintuplets 0:-6, 1:-3, 2:-2, 3:-2, 4:-1, 6:-4, 7:-3, 8:-3, a:-4, c:-3, d:-2, e:-5, f:-1, total:-39, min:-6, max:-1
m1digits  sixlets 0:1, a:1, e:1, dexp:0, total:3, texp:0, min:1, max:1
m1digdiff sixlets 0:-1, a:-1, e:-1, total:-3, min:-1, max:-1
multiple2 singles:492372, twins:30233, triplets:1989, quadruplets:253, quintuplets:33, sixlets:3, total:560000
m2exp     singles:492187, twins:30761, triplets:1922, quadruplets:120, quintuplets:7, sixlets:0, total:559990
m2diff    singles:-185, twins:528, triplets:-67, quadruplets:-133, quintuplets:-26, sixlets:-3, total:-10
m2digits  singles 0:30944, 1:30436, 2:30495, 3:30601, 4:30670, 5:31532, 6:31019, 7:30539, 8:30751, 9:30355, a:30867, b:30885, c:31108, d:30620, e:30570, f:30980, dexp:30761, total:492372, texp:492187, min:30355, max:31532
m2digdiff singles 0:-183, 1:325, 2:266, 3:160, 4:91, 5:-771, 6:-258, 7:222, 8:10, 9:406, a:-106, b:-124, c:-347, d:141, e:191, f:-219, total:-196, min:-771, max:406
m2digits  twins 0:1909, 1:1956, 2:1915, 3:1918, 4:1882, 5:1890, 6:1960, 7:1748, 8:1915, 9:1940, a:1890, b:1828, c:1910, d:1886, e:1834, f:1852, dexp:1922, total:30233, texp:30761, min:1748, max:1960
m2digdiff twins 0:13, 1:-34, 2:7, 3:4, 4:40, 5:32, 6:-38, 7:174, 8:7, 9:-18, a:32, b:94, c:12, d:36, e:88, f:70, total:519, min:-38, max:174
m2digits  triplets 0:125, 1:127, 2:128, 3:123, 4:109, 5:128, 6:113, 7:125, 8:122, 9:136, a:100, b:132, c:130, d:136, e:139, f:116, dexp:120, total:1989, texp:1922, min:100, max:139
m2digdiff triplets 0:-5, 1:-7, 2:-8, 3:-3, 4:11, 5:-8, 6:7, 7:-5, 8:-2, 9:-16, a:20, b:-12, c:-10, d:-16, e:-19, f:4, total:-69, min:-19, max:20
m2digits  quadruplets 0:13, 1:21, 2:23, 3:17, 4:8, 5:13, 6:14, 7:13, 8:15, 9:17, a:13, b:15, c:17, d:20, e:20, f:14, dexp:7, total:253, texp:120, min:8, max:23
m2digdiff quadruplets 0:-6, 1:-14, 2:-16, 3:-10, 4:-1, 5:-6, 6:-7, 7:-6, 8:-8, 9:-10, a:-6, b:-8, c:-10, d:-13, e:-13, f:-7, total:-141, min:-16, max:-1
m2digits  quintuplets 0:4, 1:3, 2:2, 3:2, 4:1, 6:4, 7:3, 8:3, a:2, c:3, d:2, e:3, f:1, dexp:0, total:33, texp:7, min:1, max:4
m2digdiff quintuplets 0:-4, 1:-3, 2:-2, 3:-2, 4:-1, 6:-4, 7:-3, 8:-3, a:-2, c:-3, d:-2, e:-3, f:-1, total:-33, min:-4, max:-1
m2digits  sixlets 0:1, a:1, e:1, dexp:0, total:3, texp:0, min:1, max:1
m2digdiff sixlets 0:-1, a:-1, e:-1, total:-3, min:-1, max:-1
$ 

Tässä vielä bash:iin perustuva näkemys tuplista: (en ole varma näkyvätkö tuplat jossain selaimessa, minulla ne näkyvät bashissa punaisella)

$./newressu | grep "00\|11\|22\|33\|44\|55\|66\|77\|88\|99"
00000 50601770965858790287746351436604015693184573567799066517653601656
00001 48694546975015201968596666905183123871308274139328912966079898679
00002 79811004632452834690694925920237574068709382185064173509186665896
00003 67948756258916859329418821550901897658164278464642949542074825264
00004 33624858011285146428020109871734597275263223498277932780645159687
00005 17971792316063297502277999536969560242964320788109401452364923409
00006 35291566680695002181737688811100316149439988651827176360642157605
00007 11370134868289458957910751111498131857275527242659407122148439815
00008 26708050136430661831450323981723744901224836837055153985157327353
00009 26529141654263551049396906874524955288851596692074733373992155155
$

Vielä ohjelma:

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

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

void fprintfmultipletype(FILE *fp1, int c)
{
  switch(c) {
    case 0: fprintf(fp1, "singles"); break;
    case 1: fprintf(fp1, "twins"); break;
    case 2: fprintf(fp1, "triplets"); break;
    case 3: fprintf(fp1, "quadruplets"); break;
    case 4: fprintf(fp1, "quintuplets"); break;
    case 5: fprintf(fp1, "sixlets"); break;
    default: fprintf(fp1, "%dlets", c + 1); break;
  }
}

int main(int argc, char *argv[])
{
  int c, d, first;
  int verbose = 0, help = 0, all = 1,
    ctrl = 1, chars = 1, cdiff = 1,
    multiple1 = 1, m1exp = 1, m1diff = 1,
    m1digits = 1, m1digdiff = 1,
    multiple2 = 1, m2exp = 1, m2diff = 1,
    m2digits = 1, m2digdiff = 1;
  char filename[128] = "-"; // default stdin
  
  FILE *fp1;
  
  procname = argv[0];

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

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

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

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

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

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

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

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

	all = !all;

	ctrl = all;
	chars = all;
	cdiff = all;
	multiple1 = all;
	m1exp = all;
	m1diff = all;
	m1digits = all;
	m1digdiff = all;
	multiple2 = all;
	m2exp = all;
	m2diff = all;
	m2digits = all;
	m2digdiff = all;
	
      } else if(!strcmp("--ctrl", argv[c])) {
	ctrl = !ctrl;

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

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

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

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

      } else if(!strcmp("--m2", argv[c])) {
	multiple2 = !multiple2;
	m2exp = !m2exp;
	m2diff = !m2diff;
	m2digits = !m2digits;
	m2digdiff = !m2digdiff;
		
      } else if(!strcmp("--m2dig", argv[c])) {
	m2digits = !m2digits;
	m2digdiff = !m2digdiff;
		
      } else if(!strcmp("--multiple2", argv[c])) {
	multiple2 = !multiple2;

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

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

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

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

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

#ifdef DEBUG17
  fprintf(stdout,"multiple1:%d", multiple1);
  fprintf(stdout,", m1exp:%d", m1exp);
  fprintf(stdout,", m1diff:%d", m1diff);
  fprintf(stdout,"\n");
#endif

  // print help message if needed
  
  if(help) {
    fprintf(stderr,"%s",procname);
    fprintf(stderr," [-]");
    fprintf(stderr,"/[filename]");
    fprintf(stderr," [--verbose]");
    fprintf(stderr," [--help]");
    fprintf(stderr," [--copyright]");
    fprintf(stderr," [--version]");
    fprintf(stderr," [--all]");
    fprintf(stderr," [--ctrl]");
    fprintf(stderr," [--chars]");
    fprintf(stderr," [--cdiff]");
    fprintf(stderr," [--multiple1]");
    fprintf(stderr," [--m1exp]");
    fprintf(stderr," [--m1diff]");
    fprintf(stderr," [--m1digits]");
    fprintf(stderr," [--m1digdiff]");
    fprintf(stderr," [--multiple2]");
    fprintf(stderr," [--m2exp]");
    fprintf(stderr," [--m2diff]");
    fprintf(stderr," [--m2digits]");
    fprintf(stderr," [--m2digdiff]");
    fprintf(stderr,"\n");
    exit(1);
  } // end of if(help)

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

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

  unsigned long int singles = 0;

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

  unsigned char buffer[1024];
  unsigned char *ifile = NULL;
  unsigned long long ifilelength = 0;
  
  // read input file to buffer

  while(fgets(buffer,sizeof(buffer),fp1)!=NULL) {
    if(verbose) {
      fprintf(stdout,"%s",buffer);
    }

    int bytes = 0;

    if(ifile != NULL)
      bytes += strlen(ifile);
    bytes += strlen(buffer);
    bytes += 1;

    if(ifilelength < bytes) {
      ifilelength = bytes;
      if((ifile = realloc(ifile, ifilelength)) == NULL) {
	fprintf(stderr,"%s:",procname);
	fprintf(stderr," realloc(): cannot allocate memory");
	fprintf(stderr,", %lld bytes",ifilelength);
	fprintf(stderr,"\n");
	exit(2);
      }
    }
    strcat(ifile, buffer);
  }

  if(verbose)
    fflush(stdout);
  
  unsigned char *p;

  p = ifile;

  while((ch = *p++) != '\0') {

    // calculate character counts for
    // character statistics
    
    charcounts[ch]++;

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

    singles++;
  } // end of for(c = 0; c < ifilelength; c++)

  if(singles == 0) {
    fprintf(stderr,"%s: no data\n", procname);
    exit(2);
  }

  int charnum[MAXCOUNT];
  int charascii[MAXCOUNT];

  for(c = 0; c < MAXCOUNT; c++) {
    charnum[c] = -1;
    charascii[c] = -1;
  }

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

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

    if(charcounts[c] != 0) {
      charnum[c] = num;
      charascii[num] = c;
      num++;
    }      
  }

#define aDEBUG25 2

#ifdef DEBUG25

  fprintf(stdout,"charnum  ");
  for(c = 0; c < MAXCOUNT; c++) {
    if(charnum[c] != -1)
      fprintf(stdout," %d:%d", c, charnum[c]);
  }
  fprintf(stdout,"\n");
  fflush(stdout);
  fprintf(stdout,"charascii");
  for(c = 0; c < MAXCOUNT; c++) {
    if(charascii[c] != -1)
      fprintf(stdout," %d:%d", c, charascii[c]);
  }
  fprintf(stdout,"\n");
  fflush(stdout);

#endif
  
  unsigned long int count = 0, ctotal = 0;

  for(c = 0; c < MAXCOUNT; c++) {
    if(charcounts[c] > 0) {
  
      if(c <= 0x20) // ctrl or space, skip
	continue;

      ctotal += charcounts[c];
      count++;

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

  long m1multiplecounts[MAX][count];
  long m2multiplecounts[MAX][count];

  for(c = 0; c < MAX; c++) {
    for(d = 0; d < count; d++) {
      m1multiplecounts[c][d] = 0;
      m2multiplecounts[c][d] = 0;
    }
  }

  p = ifile;
  first = 1;
  m2length = 0;
  
  while((ch = *p++) != '\0') {

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

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

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

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

      m1counts[c]++; 
      m1multiplecounts[c][charnum[ch]]++;
    }

    if(++currentchar >= MAX)
      currentchar -= MAX;
    
    // calculate m2 multiple
    // counts
    
    if(first) {
      prevch = ch;
      m2length = 1;
      first = 0;
    } else {
      if(prevch == ch) {
	m2length++;
      } else {
	m2counts[m2length - 1]++;
	m2multiplecounts[m2length - 1][charnum[prevch]]++;
	m2length = 1;
	prevch = ch;
      }
    }
  } // end of for(c = 0; c < ifilelength; c++)
  
  if(m2length > 0) {
    m2counts[m2length - 1]++; // add last m2 multiple
    m2multiplecounts[m2length - 1][charnum[prevch]]++;
  }

#ifdef DEBUG46
  for(c = 0; c < MAX; c++) {
    for(d = 0; d < count; d++) {
      fprintf(stdout," %lu",m2multiplecounts[c][d]);
    }
    fprintf(stdout,"\n");
  }
#endif

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

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

  // calculate average

  unsigned long int average;
  
  average = ctotal / count;
  
  long int min;
  long int max;
  
  min = LONG_MAX;
  max = LONG_MIN;
  
  // print character counts if needed
  
  if(chars) {
    long int allchars;

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

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

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

	allchars += charcounts[c];

	if(min > charcounts[c])
	  min = charcounts[c];
	if(max < charcounts[c])
	  max = charcounts[c];

	first = 0;
      }
    }
    fprintf(stdout,", total:%ld", allchars);
    fprintf(stdout,", count:%ld", count);
    fprintf(stdout,", average:%ld", average);
    fprintf(stdout,", total:%ld", allchars);
    fprintf(stdout,", min:%ld", min);
    fprintf(stdout,", max:%ld", max);
    fprintf(stdout,"\n");
    fflush(stdout);
  } // end of if(chars)

  // calculate and print character
  // differences

  min = LONG_MAX;
  max = LONG_MIN;
  
  if(cdiff) {
    fprintf(stdout,"cdiff     ");

    long int chardiff = 0;
    long int chardifftotal = 0;
    
    first = 1;

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

      if(c <= 0x20)
	continue;

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

	chardiff = (average - charcounts[c]);

	chardifftotal += chardiff;

	if(min > chardiff)
	  min = chardiff;
	if(max < chardiff)
	  max = chardiff;

	first = 0;
      } // end of if(charcounts[c] > 0)
    } // end of for(c = 0; c < MAXCOUNT; c++)
    
    fprintf(stdout,", total:%ld", chardifftotal);
    fprintf(stdout,", min:%ld", min);
    fprintf(stdout,", max:%ld", max);
    fprintf(stdout,"\n");
    fflush(stdout);
  } // end of if(cdiff)
  
  // print statistics on m1 multiples,
  // multiple1, m1exp and m1diff

  for(int e = 0; e < 3; e++) {
    if(e==0) {
      if(!multiple1)
	continue;
      fprintf(stdout, "multiple1 ");
    } else if(e==1) {
      if(!m1exp)
	continue;
      fprintf(stdout, "m1exp     ");
    } else {
      if(!m1diff)
	continue;
      fprintf(stdout, "m1diff    ");
    }
    long int total = 0;
    long int expect = singles;
    first = 1;
    for(c = 0; c < MAX; c++) {
      if(m1counts[c] > 0) {
	if(!first)
	  fprintf(stdout,", ");
	fprintfmultipletype(stdout, c);
	
	long int temp;
	
	if(e == 0)
	  temp = m1counts[c];
	else if(e == 1)
	  temp = expect;
	else
	  temp = expect - m1counts[c];
	
	total += temp;
	
	fprintf(stdout,":%ld", temp);
      }
      expect /= count;
      first = 0;
    }
    fprintf(stdout,", total:%ld", total);
    fprintf(stdout,"\n");
    fflush(stdout);
  } // end of for(int e = 0; e < 3; e++)

  // print statistics on m1 multiples,
  // m1digits and m1digdiff
  
  long int m1expected = singles;

  for(c = 0; c < MAX; c++) {
    int print = 0;
    for(d = 0; d < count; d++) {
      if(m1multiplecounts[c][d] != 0)
	print = 1;
    }
    if(print) {
      for(int e = 0; e < 2; e++) { // 0 -> digits, 1 -> diff
	if(e==0) {
	  if(!m1digits)
	    continue;
	  fprintf(stdout,"m1digits  ");
	} else if(e==1) {
	  if(!m1digdiff)
	    continue;
	  fprintf(stdout,"m1digdiff ");
	}
	min = LONG_MAX;
	max = LONG_MIN;

	fprintfmultipletype(stdout, c);
	
	unsigned long total = 0;
	
	first = 1;
	
	for(d = 0; d < count; d++) {
	  if(m1multiplecounts[c][d] != 0) {
	    unsigned char ch = charascii[d];
	    if(!first)
	      fprintf(stdout,",");	    
	    fprintf(stdout," ");
	    if(isprint(ch) && ch > ' ')
	      fprintf(stdout,"%c", ch);
	    else
	      fprintf(stdout,"%02x", ch);

	    long temp;
	    if(e == 0)
	      temp = m1multiplecounts[c][d];
	    else if(e == 1)
	      temp = m1expected / count - m1multiplecounts[c][d];
	    fprintf(stdout,":%ld", temp);
	    total += temp;
	    first = 0;

	    if(min > temp)
	      min = temp;
	    if(max < temp)
	      max = temp;
	  }
	}
	if(!first) {
	  if(e == 0)
	    fprintf(stdout,", dexp:%ld", m1expected / count);
	  fprintf(stdout,", total:%ld", total);
	  if(e == 0)
	    fprintf(stdout,", texp:%ld", m1expected);
	  fprintf(stdout,", min:%ld", min);
	  fprintf(stdout,", max:%ld", max);
	}
	fprintf(stdout,"\n");
      } // end of for(int e = 0; e < 2; e++)
    }
    m1expected /= count;
  }

  unsigned long int m2expects[MAX];
  double base;
  
  for(c = 0; c < MAX; c++) {
    m2expects[c] = 0;
  }
    
  base = (((double)count - 1) / count)  * (((double)count - 1) / count);
  for(c = 0; c < MAX; c++) {
    if(base * ctotal > 0) {
      m2expects[c] = (unsigned long int) (base * ctotal);
      base *= ((double)1 / count);
    }
  }
  
  // print statistics on m2 multiples
  // multiple1 m2exp and m2diff
  
  for(int e = 0; e < 3; e++) {
    if(e==0) {
      if(!multiple2)
	continue;
      fprintf(stdout,"multiple2 ");
    } else if(e==1) {
      if(!m2exp)
	continue;
      fprintf(stdout,"m2exp     ");
    } else {
      if(!m2diff)
	continue;
      fprintf(stdout,"m2diff    ");
    }
    
    unsigned long int total = 0;
    
    first = 1;
    
    for(c = 0; c < MAX; c++) {
      if(m2counts[c] > 0) {
	if(first == 0)
	  fprintf(stdout,", ");

	fprintfmultipletype(stdout, c);
	
	long int temp;
	
	if(e == 0) {
	  temp = m2counts[c];
	  //total += temp * (c + 1);
	} else if(e == 1) {
	  temp = m2expects[c];
	  //total += temp * (c + 1);
	} else {
	  temp = m2expects[c] - m2counts[c];
	  //total += temp;
	}	
	fprintf(stdout,":%ld", temp);
	total += temp * (c + 1);
	first = 0;
      }
    } // end of for(c = 1; c < MAX; c++)
    
    fprintf(stdout,", total:%ld",total);
    fprintf(stdout,"\n");
    fflush(stdout);
  } // end if for(int e = 0; e < 3; e++)

  // print statistics on m2 multiples
  // m2digits and m2digdiff
  
  double m2expectedbase = (((double)count - 1) / count)  * (((double)count - 1) / count);

  for(c = 0; c < MAX; c++) {
    int print = 0;
    for(d = 0; d < count; d++) {
      if(m2multiplecounts[c][d] != 0)
	print = 1;
    }
    if(print) {
      unsigned long m2exp = m2expectedbase * ctotal;
      for(int e = 0; e < 2; e++) { // 0 -> digits, 1 -> diff
	if(e==0) {
	  if(!m2digits)
	    continue;
	  fprintf(stdout,"m2digits  ");
	} else {
	  if(!m2digdiff)
	    continue;
	  fprintf(stdout,"m2digdiff ");
	}
	min = LONG_MAX;
	max = LONG_MIN;

	fprintfmultipletype(stdout, c);
	
	first = 1;
	
	long int total = 0;
	
	for(d = 0; d < count; d++) {
	  if(m2multiplecounts[c][d] != 0) {
	    unsigned char ch = charascii[d];
	    if(!first)
	      fprintf(stdout,",");
	    fprintf(stdout," ");
	    if(isprint(ch) && ch > ' ')
	      fprintf(stdout,"%c", ch);
	    else
	      fprintf(stdout,"%02x", ch);

	    long temp;
	    if(e==0)
	      temp = m2multiplecounts[c][d];
	    else
	      temp = m2exp / count - m2multiplecounts[c][d];

	    fprintf(stdout,":%ld", temp);
	    total += temp;

	    if(min > temp)
	      min = temp;
	    if(max < temp)
	      max = temp;

	    first = 0;
	  }
	}
	if(!first) {	
	  if(e == 0)
	    fprintf(stdout,", dexp:%lu", (unsigned long) m2exp / count);
	  fprintf(stdout,", total:%ld", total);
	  if(e == 0)
	    fprintf(stdout,", texp:%lu", (unsigned long) m2exp);
	  fprintf(stdout,", min:%ld", min);
	  fprintf(stdout,", max:%ld", max);
	}
	fprintf(stdout,"\n");
      } // end of for(int e = 0; e < 2; e++)
      m2expectedbase *= ((double)1 / count);
    }
  }
}

Neljäs ohjelma jonka kirjoitin tekee edelleen tilastoja satunnaisuudesta: tällä kertaa tilastoidaan newressu tulosteen sanoja (words), eli välilyönneillä eroteltuja satunnaismerkkisarjoja. Seuraavassa raportissa sanan pituus on 1, jolloin tuotettu raportti on vastaa aiemmin laskettua chars riviä. Tällä voi debukata words toimintoa. Raportin –set toiminnolla voidaan tulostaa tietue, johon words rakenne lasketaan (vrt terttu).

$ ./newressu --bin -s1 --lineno --space -l1000| ./newressutest9 --set
set       '0' = "18112", '1' = "17888"
ctrl      LF:1000, space:35000
chars     0:18112, 1:17888, words:2, total:36000, average:18000, min:17888, max:18112
cdiff     0:-112, 1:112, total:0, min:-112, max:112
words     0:18112, 1:17888, words:2, total:36000, average:18000, min:17888, max:18112
wdiff     0:-112, 1:112, total:0, min:-112, max:112
$ 

Varsinainen toiminta on kuitenkin siinä että verrataan useampimerkkisiä sanoja:

$ ./newressu --bin -s2 --lineno --space -l1000 | ./newressutest9 --set
set       '00' = "6061", '01' = "5916", '10' = "6009", '11' = "6014"
ctrl      LF:1000, space:23000
chars     0:24047, 1:23953, words:2, total:48000, average:24000, min:23953, max:24047
cdiff     0:-47, 1:47, total:0, min:-47, max:47
words     00:6061, 01:5916, 10:6009, 11:6014, words:4, total:24000, average:6000, min:5916, max:6061
wdiff     00:-61, 01:84, 10:-9, 11:-14, total:0, min:-61, max:84
$ 

Edellisessä on listattu binääriset 2 bittiset sanat, jotka ovat 00, 01, 10 ja 11. Koska vaihtoehtoja on neljä, yhden yhdistelmän todennäköisyys on 1/4 eli 25%.

Listaan poikkeuksellisesti ensimmäisenä newressutest9:n tietorakenteen hallinnan, koska se on tavallaan erillinen kokonaisuus. En kuitenkaan vielä laittanut sitä omaan tiedostoon:

unsigned char *db5_set = NULL;

unsigned char *db5_get_set()
{
  return(db5_set);
}

void db5_skipwhite(unsigned char **p)
{
  while(isblank(**p))
    (*p)++;
}

void db5_get_element(unsigned int *namelen, unsigned char **name, unsigned int *valuelen, unsigned char **value, unsigned char **p2) // 2023 JariK
{
  unsigned char *p;

  p = *p2;
  
  db5_skipwhite(&p);
  if(*p == '\'') { // name
    p++;
    *name = p;
    *namelen = 0;
    while(*p != '\'' && *p != '\0') {
      p++;
      (*namelen)++;
    }
    if(*p == '\'')
      p++;
  }

  db5_skipwhite(&p);
  if(*p == '=') {
    p++;
  }

  db5_skipwhite(&p);
  if(*p == '\"') { // value
    p++;
    *value = p;
    *valuelen = 0;
    while(*p != '\"' && *p != '\0') {
      p++;
      (*valuelen)++;
    }
    if(*p == '\"')
      p++;
  }

  db5_skipwhite(&p);

  *p2 = p;
}

#define aDEBUG9 2
#define aDEBUG10 2
#define aDEBUG11 2

int db5_get(unsigned char *name, int valuesize, unsigned char *value) // 2023 JariK
{
  int firstelement, retval = 0;
  unsigned char *p;

  unsigned int namelen = strlen(name);

  unsigned int namelen2, valuelen2;
  unsigned char *name2, *value2;

#ifdef DEBUG9
  fprintf(stdout,"********** get\n");
#endif
  
  value[0] = '\0';

  if(db5_set != NULL) {

    firstelement = 1;
    p = db5_set;

    // read thru elements element by element
    
    while(*p != '\0') {

      db5_skipwhite(&p);

      if(!firstelement) {
	if(*p == ',')
	  p++;
	db5_skipwhite(&p);
      }
      
      db5_get_element(&namelen2, &name2, &valuelen2, &value2, &p);

#ifdef DEBUG9
      fprintf(stdout,"name:%s", name);
      fprintf(stdout,", name2:%.*s(%d)", namelen2, name2, namelen2);
      fprintf(stdout,", value2:%.*s(%d)", valuelen2, value2, valuelen2);
      fprintf(stdout,"\n");
#endif

      // we reached equal name
      
      if(namelen == namelen2 && !strncmp(name, name2, namelen)) {
	strncpy(value, value2, valuesize);
	if(valuesize >= valuelen2)
	  value[valuelen2] = '\0';
	else
	  value[valuesize - 1] = '\0';
	retval = strlen(value);
	break;
      }
      firstelement = 0;
    } // end of while(*p != '\0')
  } // end of if(db5_set != NULL)
  return(retval);
}

#define SORTED 2

void db5_put(unsigned char *name, unsigned char *value) // 2023 JariK
{
  int found = 0, firstelement, lastelement;
  unsigned char *p, *currentelement = NULL;
  unsigned char *thiselement = NULL, *nextelement = NULL;

  unsigned int namelen = strlen(name);
  unsigned int valuelen = strlen(value);

  unsigned int namelen2, valuelen2;
  unsigned char *name2, *value2;

#ifdef DEBUG9
  fprintf(stdout,"********** put\n");
#endif
  
  firstelement = 1;
  lastelement = 0;

  if(db5_set != NULL) {

    p = db5_set;
 
    // read thru elements element by element
    
    while(*p != '\0') {

      db5_skipwhite(&p);
      
      currentelement = p; // save beginning of element

      if(!firstelement) {
	if(*p == ',')
	  p++;
	db5_skipwhite(&p);
      }

      db5_get_element(&namelen2, &name2, &valuelen2, &value2, &p);

#ifdef DEBUG9
      fprintf(stdout,"name:%s", name);
      fprintf(stdout,", name2:%.*s(%d)", namelen2, name2, namelen2);
      fprintf(stdout,", value2:%.*s(%d)", valuelen2, value2, valuelen2);
      fprintf(stdout,"\n");
#endif

      // we reached equal name
      
      if(namelen == namelen2 &&
	 !strncmp(name, name2, namelen) ) {
	found = 1;
	thiselement = currentelement;
	nextelement = p; // beginning of next element
	break;
      }

#ifdef SORTED

      // we reached greater name

      if(namelen == namelen2 &&
	 strncmp(name, name2, namelen) < 0) { // equal lengths less (aaaa, bbbb)
	thiselement = currentelement;
	nextelement = currentelement;
	break;

      } else {

	// common beginning of strings
	
	int comblen = namelen;
	if(comblen > namelen2)
	  comblen = namelen2;
	
	int cmp = strncmp(name, name2, comblen);
	if((cmp < 0) || // first characters less (aaaa, b)
	   (!cmp && namelen2 > namelen) ) { // first characters equal but string longer (a, aaaa)
	  thiselement = currentelement;
	  nextelement = currentelement;
	  break;
	}
      }
    
#endif
      
      if(*p == '\0')
	lastelement = 1;
  
      firstelement = 0;
    } // end of while(*p != '\0')

  } else { // else of if(db5_set != NULL)

    lastelement = 1;
    firstelement = 1;

  } // end of if(db5_set != NULL)

  // make new element

  long count;
  static long elementsize = 0;
  static unsigned char *element = NULL;
  unsigned char *elementfmt;

  // figure out needed commas

  if((firstelement && found) || // first and existing
     (firstelement && lastelement) ) // first element in a new set (first and last)
    elementfmt = "'%s' = \"%s\""; // --> no commas (first existing or only)
  else if(firstelement) // first and not existing element
    elementfmt = "'%s' = \"%s\", "; // --> comma in the end (first new)
  else
    elementfmt = ", '%s' = \"%s\""; // ..> comma in the beginning (other)

  // print element

  count = snprintf(element, elementsize, elementfmt, name, value) + 1;
  if(elementsize < count) {
    elementsize = count;
    if((element = realloc(element, elementsize)) == NULL) {
      fprintf(stderr, "%s: realloc(): cannot allocate memory\n", procname);
      exit(1);
    }
    count = snprintf(element, elementsize, elementfmt, name, value) + 1;
  }
#ifdef DEBUG22
  fprintf(stdout,", set: %s", db5_set);
  fprintf(stdout,"\n");
#endif
  // calculate change in length

  long oldcount = 0;
  
  count = 0;
  if(db5_set != NULL) {
    count += strlen(db5_set);
    oldcount = count + 1; // + '\0'
  }
  if(found) // change in value only
    count += valuelen - valuelen2; // no punctuation marks
  else
    count += strlen(element);
  count++; // + '\0'

#ifdef DEBUG11
  if(oldcount != count) {
    fprintf(stdout, "old length:%ld %s\n", oldcount, db5_set);
    fflush(stdout);
  }
#endif  

  if(oldcount != count) { // size changed, 

    // reallocate set according to new length

    unsigned char *tempdb5_set = db5_set;
    if((db5_set = realloc(db5_set, count)) == NULL) {
      fprintf(stderr,"%s: realloc(): cannot allocate memory\n", procname);
      exit(1);
    }

    // adjust these too
    
    if(thiselement != NULL)
      thiselement = db5_set + (thiselement - tempdb5_set);
    if(nextelement != NULL)
      nextelement = db5_set + (nextelement - tempdb5_set);

    if(tempdb5_set == NULL)
      db5_set[0] = '\0';
  }

  
  // adjust to end of set if not present
  
  if(thiselement == NULL)
    thiselement = db5_set + strlen(db5_set);
  if(nextelement == NULL)
    nextelement = db5_set + strlen(db5_set);

  // move end of the set

  memmove(thiselement + strlen(element), nextelement, strlen(nextelement) + 1); // end of string too

  // add new element
  
  memmove(thiselement, element, strlen(element));

#ifdef DEBUG11
  if(oldcount != count) {
    fprintf(stdout,"new length:%ld %s\n", count, db5_set);
    fflush(stdout);
  }
#endif  
  
#ifdef DEBUG10
  fprintf(stdout,", set      %s(%ld)", db5_set, strlen(db5_set));
  fprintf(stdout,"\n");
  fflush(stdout);
#endif
}

Edellinen koodi listaa db5_put():in db5_get():in, joilla voisi olla potentiaalia myös tertun ytimeen.

Seuraavassa lista, josta näkee selvemmin datarakenteen:

$ cat newressudb5test.sh
#!/bin/bash
for ((c=0;c<5;c++)); do ./newressu --single --bin -s1 --spaces $@ --lineno -l1000 | ./newressutest9 --all; done;
for ((c=0;c<5;c++)); do ./newressu --single --oct -s1 --spaces $@ --lineno -l1000 | ./newressutest9 --all; done;
for ((c=0;c<5;c++)); do ./newressu --single --dec -s1 --spaces $@ --lineno -l1000 | ./newressutest9 --all; done;
for ((c=0;c<5;c++)); do ./newressu --single --hex -s1 --spaces $@ --lineno -l1000 | ./newressutest9 --all; done;
$ ./newressudb5test.sh
set       '0' = "17983", '1' = "18017"
set       '0' = "17935", '1' = "18065"
set       '0' = "17851", '1' = "18149"
set       '0' = "18034", '1' = "17966"
set       '0' = "18086", '1' = "17914"
set       '0' = "4507", '1' = "4508", '2' = "4524", '3' = "4466", '4' = "4438", '5' = "4542", '6' = "4538", '7' = "4477"
set       '0' = "4535", '1' = "4541", '2' = "4467", '3' = "4589", '4' = "4449", '5' = "4510", '6' = "4390", '7' = "4519"
set       '0' = "4528", '1' = "4502", '2' = "4445", '3' = "4507", '4' = "4574", '5' = "4444", '6' = "4516", '7' = "4484"
set       '0' = "4442", '1' = "4493", '2' = "4505", '3' = "4394", '4' = "4600", '5' = "4531", '6' = "4494", '7' = "4541"
set       '0' = "4569", '1' = "4346", '2' = "4472", '3' = "4464", '4' = "4447", '5' = "4585", '6' = "4551", '7' = "4566"
set       '0' = "3572", '1' = "3571", '2' = "3573", '3' = "3633", '4' = "3596", '5' = "3616", '6' = "3743", '7' = "3492", '8' = "3622", '9' = "3582"
set       '0' = "3620", '1' = "3650", '2' = "3567", '3' = "3610", '4' = "3561", '5' = "3622", '6' = "3698", '7' = "3556", '8' = "3553", '9' = "3563"
set       '0' = "3598", '1' = "3616", '2' = "3594", '3' = "3732", '4' = "3587", '5' = "3603", '6' = "3511", '7' = "3598", '8' = "3562", '9' = "3599"
set       '0' = "3577", '1' = "3575", '2' = "3640", '3' = "3568", '4' = "3648", '5' = "3591", '6' = "3570", '7' = "3577", '8' = "3682", '9' = "3572"
set       '0' = "3584", '1' = "3643", '2' = "3621", '3' = "3696", '4' = "3522", '5' = "3698", '6' = "3526", '7' = "3611", '8' = "3630", '9' = "3469"
set       '0' = "2344", '1' = "2323", '2' = "2156", '3' = "2296", '4' = "2314", '5' = "2249", '6' = "2275", '7' = "2220", '8' = "2289", '9' = "2202", 'a' = "2227", 'b' = "2262", 'c' = "2240", 'd' = "2177", 'e' = "2211", 'f' = "2215"
set       '0' = "2235", '1' = "2151", '2' = "2361", '3' = "2218", '4' = "2214", '5' = "2218", '6' = "2266", '7' = "2251", '8' = "2281", '9' = "2192", 'a' = "2227", 'b' = "2313", 'c' = "2259", 'd' = "2236", 'e' = "2337", 'f' = "2241"
set       '0' = "2209", '1' = "2197", '2' = "2289", '3' = "2343", '4' = "2260", '5' = "2253", '6' = "2298", '7' = "2195", '8' = "2224", '9' = "2197", 'a' = "2205", 'b' = "2308", 'c' = "2254", 'd' = "2221", 'e' = "2266", 'f' = "2281"
set       '0' = "2234", '1' = "2270", '2' = "2310", '3' = "2151", '4' = "2271", '5' = "2259", '6' = "2295", '7' = "2199", '8' = "2318", '9' = "2207", 'a' = "2230", 'b' = "2206", 'c' = "2275", 'd' = "2275", 'e' = "2253", 'f' = "2247"
set       '0' = "2314", '1' = "2290", '2' = "2202", '3' = "2300", '4' = "2133", '5' = "2218", '6' = "2229", '7' = "2292", '8' = "2208", '9' = "2251", 'a' = "2224", 'b' = "2239", 'c' = "2289", 'd' = "2248", 'e' = "2279", 'f' = "2284"
$ 

Listan alussa on laskettu binääri merkkejä (5 riviä), seuraavaksi oktaalimerkkejä, desimaalimerkkejä ja heksamerkkejä.

Datarakenteessa on tässä sanat (words) ja samanlaisten sanojen lukumäärä, mutta kun sanan tilalle laittaa kentän nimen ja lukumäärän tilalle kentän arvon, voidaan alkaa miettimään esimerkiksi appia tilaus ja siihen liittyviä kenttia ja tiedostoja (tilaus, asiakas, tilausrivit, tuote, asiakasmyynti, toimitus jne.). Edit: Tietenkin ilman kovakoodattuja sarakkeita, tauluja ja appeja.

Ja koodi kokonaisuudessaan: (huomaa että ohjelmassa on TEST80 kappale, joka ristiintarkistaa chars rivin ja words rivin luvut, se on nyt päällä)

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

static unsigned char *procname;
static unsigned char *programname = "newressutest9 version 0.4 ©";
static unsigned char *copyright = "Copyright (c) 1998-2023 Jari Kuivaniemi, Helsinki, Finland. Kaikki oikeudet pidätetään!";

unsigned char *db5_set = NULL;

unsigned char *db5_get_set()
{
  return(db5_set);
}

void db5_skipwhite(unsigned char **p)
{
  while(isblank(**p))
    (*p)++;
}

void db5_get_element(unsigned int *namelen, unsigned char **name, unsigned int *valuelen, unsigned char **value, unsigned char **p2) // 2023 JariK
{
  unsigned char *p;

  p = *p2;
  
  db5_skipwhite(&p);
  if(*p == '\'') { // name
    p++;
    *name = p;
    *namelen = 0;
    while(*p != '\'' && *p != '\0') {
      p++;
      (*namelen)++;
    }
    if(*p == '\'')
      p++;
  }

  db5_skipwhite(&p);
  if(*p == '=') {
    p++;
  }

  db5_skipwhite(&p);
  if(*p == '\"') { // value
    p++;
    *value = p;
    *valuelen = 0;
    while(*p != '\"' && *p != '\0') {
      p++;
      (*valuelen)++;
    }
    if(*p == '\"')
      p++;
  }

  db5_skipwhite(&p);

  *p2 = p;
}

#define aDEBUG9 2
#define aDEBUG10 2
#define aDEBUG11 2

int db5_get(unsigned char *name, int valuesize, unsigned char *value) // 2023 JariK
{
  int firstelement, retval = 0;
  unsigned char *p;

  unsigned int namelen = strlen(name);

  unsigned int namelen2, valuelen2;
  unsigned char *name2, *value2;

#ifdef DEBUG9
  fprintf(stdout,"********** get\n");
#endif
  
  value[0] = '\0';

  if(db5_set != NULL) {

    firstelement = 1;
    p = db5_set;

    // read thru elements element by element
    
    while(*p != '\0') {

      db5_skipwhite(&p);

      if(!firstelement) {
	if(*p == ',')
	  p++;
	db5_skipwhite(&p);
      }
      
      db5_get_element(&namelen2, &name2, &valuelen2, &value2, &p);

#ifdef DEBUG9
      fprintf(stdout,"get name:%s", name);
      fprintf(stdout,", name2:%.*s(%d)", namelen2, name2, namelen2);
      fprintf(stdout,", value2:%.*s(%d)", valuelen2, value2, valuelen2);
      fprintf(stdout,"\n");
#endif

      db5_skipwhite(&p);
      
      // we reached equal name
      
      if(namelen == namelen2 && !strncmp(name, name2, namelen)) {
	strncpy(value, value2, valuesize);
	if(valuesize >= valuelen2)
	  value[valuelen2] = '\0';
	else
	  value[valuesize - 1] = '\0';
	retval = strlen(value);
	break;
      }
      firstelement = 0;
    } // end of while(*p != '\0')
  } // end of if(db5_set != NULL)
  return(retval);
}

#define SORTED 2

void db5_put(unsigned char *name, unsigned char *value) // 2023 JariK
{
  int found = 0, firstelement, lastelement;
  unsigned char *p, *currentelement = NULL;
  unsigned char *thiselement = NULL, *nextelement = NULL;

  unsigned int namelen = strlen(name);
  unsigned int valuelen = strlen(value);

  unsigned int namelen2, valuelen2;
  unsigned char *name2, *value2;

#ifdef DEBUG9
  fprintf(stdout,"********** put\n");
#endif
  
  firstelement = 1;
  lastelement = 0;

  if(db5_set != NULL) {

    p = db5_set;
 
    // read thru elements element by element
    
    while(*p != '\0') {

      db5_skipwhite(&p);
      
      currentelement = p; // save beginning of element

      if(!firstelement) {
	if(*p == ',')
	  p++;
	db5_skipwhite(&p);
      }

      db5_get_element(&namelen2, &name2, &valuelen2, &value2, &p);

      db5_skipwhite(&p);
      
#ifdef DEBUG9
      fprintf(stdout,"put name:%s", name);
      fprintf(stdout,", name2:%.*s(%d)", namelen2, name2, namelen2);
      fprintf(stdout,", value2:%.*s(%d)", valuelen2, value2, valuelen2);
      fprintf(stdout,"\n");
#endif

      // we reached equal name
      
      if(namelen == namelen2 &&
	 !strncmp(name, name2, namelen) ) {
	found = 1;
	thiselement = currentelement;
	nextelement = p; // beginning of next element
	break;
      }

#ifdef SORTED

      // we reached greater name

      if(namelen == namelen2 &&
	 strncmp(name, name2, namelen) < 0) { // equal lengths less (aaaa, bbbb)
	thiselement = currentelement;
	nextelement = currentelement;
	break;

      } else {

	// common beginning of strings
	
	int comblen = namelen;
	if(comblen > namelen2)
	  comblen = namelen2;
	
	int cmp = strncmp(name, name2, comblen);
	if((cmp < 0) || // first characters less (aaaa, b)
	   (!cmp && namelen2 > namelen) ) { // first characters equal but string longer (a, aaaa)
	  thiselement = currentelement;
	  nextelement = currentelement;
	  break;
	}
      }
    
#endif
      
      if(*p == '\0')
	lastelement = 1;
  
      firstelement = 0;
    } // end of while(*p != '\0')

  } else { // else of if(db5_set != NULL)

    lastelement = 1;
    firstelement = 1;

  } // end of if(db5_set != NULL)

  // make new element

  long count;
  static long elementsize = 0;
  static unsigned char *element = NULL;
  unsigned char *elementfmt;

  // figure out needed commas

  if((firstelement && found) || // first and existing
     (firstelement && lastelement) ) // first element in a new set (first and last)
    elementfmt = "'%s' = \"%s\""; // --> no commas (first existing or only)
  else if(firstelement) // first and not existing element
    elementfmt = "'%s' = \"%s\", "; // --> comma in the end (first new)
  else
    elementfmt = ", '%s' = \"%s\""; // ..> comma in the beginning (other)

  // print element

  count = snprintf(element, elementsize, elementfmt, name, value) + 1;
  if(elementsize < count) {
    elementsize = count;
    if((element = realloc(element, elementsize)) == NULL) {
      fprintf(stderr, "%s: realloc(): cannot allocate memory\n", procname);
      exit(1);
    }
    count = snprintf(element, elementsize, elementfmt, name, value) + 1;
  }
#ifdef DEBUG22
  fprintf(stdout,", set: %s", db5_set);
  fprintf(stdout,"\n");
#endif
  // calculate change in length

  long oldcount = 0;
  
  count = 0;
  if(db5_set != NULL) {
    count += strlen(db5_set);
    oldcount = count + 1; // + '\0'
  }
  if(found) // change in value only
    count += valuelen - valuelen2; // no punctuation marks
  else
    count += strlen(element);
  count++; // + '\0'

#ifdef DEBUG11
  if(oldcount != count) {
    fprintf(stdout, "old length:%ld %s\n", oldcount, db5_set);
    fflush(stdout);
  }
#endif  

  if(oldcount != count) { // size changed, 

    // reallocate set according to new length

    unsigned char *tempdb5_set = db5_set;
    if((db5_set = realloc(db5_set, count)) == NULL) {
      fprintf(stderr,"%s: realloc(): cannot allocate memory\n", procname);
      exit(1);
    }

    // adjust these too
    
    if(thiselement != NULL)
      thiselement = db5_set + (thiselement - tempdb5_set);
    if(nextelement != NULL)
      nextelement = db5_set + (nextelement - tempdb5_set);

    if(tempdb5_set == NULL)
      db5_set[0] = '\0';
  }

  
  // adjust to end of set if not present
  
  if(thiselement == NULL)
    thiselement = db5_set + strlen(db5_set);
  if(nextelement == NULL)
    nextelement = db5_set + strlen(db5_set);

  // move end of the set

  memmove(thiselement + strlen(element), nextelement, strlen(nextelement) + 1); // end of string too

  // add new element
  
  memmove(thiselement, element, strlen(element));

#ifdef DEBUG11
  if(oldcount != count) {
    fprintf(stdout,"new length:%ld %s\n", count, db5_set);
    fflush(stdout);
  }
#endif  
  
#ifdef DEBUG10
  fprintf(stdout,", set      %s(%ld)", db5_set, strlen(db5_set));
  fprintf(stdout,"\n");
  fflush(stdout);
#endif
}

#define aDEBUG34 2

void increment(unsigned char *name)
{
  unsigned long long count;
  unsigned char buffer[32];

#ifdef DEBUG9
  fprintf(stdout, "********** increment\n");
#endif

  count = 0;
  if(db5_get(name, sizeof(buffer), buffer) != 0)
    count = atoll(buffer);
  count++;
  sprintf(buffer,"%lld", count);
  db5_put(name, buffer);
#ifdef DEBUG34
  fprintf(stdout, ", '%s', \"%s\"",name, buffer);
  fflush(stdout);
#endif
}

int main(int argc, char *argv[])
{
  int c, d, first;
  int verbose = 0, help = 0, all = 1, set = 0,
    ctrl = 1, chars = 1, cdiff = 1,
    words = 1, wdiff = 1;
  char filename[128] = "-"; // default stdin
  
  FILE *fp1;
  
  procname = argv[0];

#define aTEST39 2 // default is off
  
#ifdef TEST39

  // tests for earlier db5 routines
  
  db5_set = "'kala' = \"kele\", 'kala2' = \"kele2\", 'kala3' = \"kele3\"";

  unsigned int valuelen = 1024, valuelen2;
  unsigned char value[1024];

  // dbget
  
  fprintf(stdout,"db5\n");
  fflush(stdout);
  db5_get("kala", valuelen, value);
  valuelen2 = strlen(value);
  fprintf(stdout,"*value:%.*s(%d)\n",
	  valuelen2, value, valuelen2);
  fflush(stdout);
  db5_get("kala2", valuelen, value);
  valuelen2 = strlen(value);
  fprintf(stdout,"*value:%.*s(%d)\n",
	  valuelen2, value, valuelen2);
  fflush(stdout);
  db5_get("kala3", valuelen, value);
  valuelen2 = strlen(value);
  fprintf(stdout,"*value:%.*s(%d)\n",
	  valuelen2, value, valuelen2);
  fflush(stdout);

  // dbput and sorting
  
  db5_set = NULL;
  db5_put("kala", "kele");
  db5_put("kala1", "kele1");
  db5_put("kala2", "kele2");
  db5_put("kala21", "kele211111");
  db5_put("kala3", "kele3");
  db5_put("kala2", "kele2b");
  db5_put("kala21", "kele21");
  db5_put("kala21", "kele21111");

  // test increment
  
  free(db5_set);
  db5_set = NULL;
  increment("a");
  increment("b");
  increment("b");
  increment("c");
  increment("c");
  increment("c");

  // utf8 characters
  
  free(db5_set);
  db5_set = NULL;
  increment("⚀");  
  increment("⚁");  
  increment("⚂");  
  increment("⚃");  
  increment("⚄");  
  increment("⚅");  

  // sorting with different lengths of name
  
  free(db5_set);
  db5_set = NULL;
  db5_put("ccccc", "ccccc");
  db5_put("a", "a");
  db5_put("bbbb", "bbbb");
  db5_put("b", "b");
  db5_put("cccc", "cccc");
  db5_put("c", "c");
  db5_put("aaaaa", "aaaaa");
  db5_put("aa", "aa");
  db5_put("bb", "bb");
  db5_put("aaa", "aaa");
  db5_put("bbbbb", "bbbbb");
  db5_put("cc", "cc");
  db5_put("aaaaa", "aaaaa");
  db5_put("aaaa", "aaaa");
  db5_put("bbb", "bbb");
  db5_put("ccc", "ccc");

  fprintf(stdout,"db5_set:%s\n", db5_set);
  free(db5_set);
  db5_set = NULL;

  //exit(1);

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

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

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

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

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

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

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

      } else if(!strcmp("--set", argv[c])) { // display db5_set
	set = !set;

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

	all = !all;

	ctrl = all;
	chars = all;
	cdiff = all;
	words = all;
	wdiff = all;
	
      } else if(!strcmp("--ctrl", argv[c])) {
	ctrl = !ctrl;

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

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

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

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

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

  // print help message if needed
  
  if(help) {
    fprintf(stderr,"%s", procname);
    fprintf(stderr," [-]");
    fprintf(stderr,"/[filename]");
    fprintf(stderr," [--verbose]");
    fprintf(stderr," [--help]");
    fprintf(stderr," [--copyright]");
    fprintf(stderr," [--version]");
    fprintf(stderr," [--all]");
    fprintf(stderr," [--ctrl]");
    fprintf(stderr," [--chars]");
    fprintf(stderr," [--cdiff]");
    fprintf(stderr," [--words]");
    fprintf(stderr," [--wdiff]");
    fprintf(stderr,"\n");
    exit(1);
  } // end of if(help)

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

  long int charcounts[MAXCOUNT];
  
  for(c = 0; c < MAXCOUNT; c++)
    charcounts[c] = 0;
  
#define MAX 1024
  
  unsigned long int singles = 0;

  int ch;

  first = 1;

  unsigned char buffer[1024];
  unsigned char *ifile = NULL; // input file
  unsigned long long ifilelength = 0;
  
  // read input file to buffer

  while(fgets(buffer, sizeof(buffer), fp1) != NULL) {
    if(verbose) {
      fprintf(stdout,"%s", buffer);
    }

    int bytes = 0;

    if(ifile != NULL)
      bytes += strlen(ifile);
    bytes += strlen(buffer);
    bytes += 1;

    if(ifilelength < bytes) {
      unsigned char *tempifile = ifile;
      ifilelength = bytes;
      if((ifile = realloc(ifile, ifilelength)) == NULL) {
	fprintf(stderr,"%s: realloc(): cannot allocate memory\n", procname);
	exit(2);
      }
      if(tempifile == NULL)
	ifile[0] = '\0';
    }
    strcat(ifile, buffer);
  }

  if(verbose)
    fflush(stdout);
  
  unsigned char *p;
  
  p = ifile;

  while((ch = *p++) != '\0') {

    // calculate character counts for
    // character statistics
    
    charcounts[ch]++;

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

    singles++;

  } // end of for(c = 0; c < ifilelength; c++)

  if(singles == 0) {
    fprintf(stderr,"%s: no data\n",procname);
    exit(2);
  }
  
  int wcount = 0; // word count
  
  p = ifile;

  while(*p != '\0') {

    while(*p <= 0x20 && *p != '\0') // skip control && handle end of file
      p++;

    int count;
    unsigned char *w, word[128];

    // get word
    
    w = word;
    count = 0;
    while(*p > 0x20 && *p != '\0') {
      if(++count < sizeof(word))
	*w++ = *p;
      p++;
    }
    *w = '\0';

    if(count > 0) {
      increment(word);
      wcount++;
    }
  } // end of while(*p != '\0')

  if(set) {
    fprintf(stdout,"set       %s\n", db5_set);
    fflush(stdout);
  }
  
  unsigned long int count = 0, ctotal = 0;

  for(c = 0; c < MAXCOUNT; c++) {
    if(charcounts[c] > 0) {
  
      if(c <= 0x20) // ctrl or space, skip
	continue;

      ctotal += charcounts[c];
      count++;

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

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

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

  unsigned long int caverage;

  // calculate average for character statistics
  
  caverage = ctotal / count;

  long int min;
  long int max;
  
  // print character statistics

  for(int e = 0; e < 2; e++) {
    long int total = 0;
    min = LONG_MAX;
    max = LONG_MIN;

    if(e == 0) {
      if(!chars)
	continue;
      fprintf(stdout,"chars     ");
    } else {
      if(!cdiff)
	continue;
      fprintf(stdout,"cdiff     ");
    }
    first = 1;

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

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

	long int temp;
	
	if(e == 0)
	  temp = charcounts[c];
	else
	  temp = caverage - charcounts[c];

	fprintf(stdout,":%ld", temp);

	total += temp;
	if(min > temp)
	  min = temp;
	if(max < temp)
	  max = temp;

	first = 0;
      }
    } // end of if(charcounts[c] > 0)

    if(e == 0) {
      fprintf(stdout,", words:%ld", count);
      fprintf(stdout,", total:%ld", total);
      fprintf(stdout,", average:%ld", caverage);
    } else {
      fprintf(stdout,", total:%ld", total);
    }
    fprintf(stdout,", min:%ld", min);
    fprintf(stdout,", max:%ld", max);
    fprintf(stdout,"\n");
    fflush(stdout);
  } // end of for(int e = 0; e < 2: e++)

  unsigned char *iset = db5_get_set();

  unsigned int namelen, valuelen;
  unsigned char *name, *value;
  
#define TEST80 2 // default on (for now)
  
#ifdef TEST80

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

    if(c <= 0x20)
      continue;

    long int tcount = 0;
    
    p = iset;

    while(*p != '\0') {
      db5_skipwhite(&p);
      db5_get_element(&namelen, &name, &valuelen, &value, &p);
      db5_skipwhite(&p);
      if(*p == ',') {
	p++;
	db5_skipwhite(&p);
	if(*p == ',') {
	  p++;
	  db5_skipwhite(&p);
	  fprintf(stderr,"%s: excess comma, set:%s\n", procname, iset);
	  fflush(stderr);
	  exit(1);
	}
      }
      long int temp;
      unsigned char tempbuffer[32];
      
      strncpy(tempbuffer, value, valuelen);
      tempbuffer[valuelen] = '\0';
      temp = atol(tempbuffer);

      for(d = 0; d < namelen; d++) {
	if(name[d] == c)
	  tcount += temp;
      }
    } // end of while(*p != '\0')

#define aDEBUG84 2
    
#ifdef DEBUG84
    if(charcounts[c] != 0 || tcount != 0) {
      fprintf(stderr,"%s: test", procname);
      fprintf(stderr,", c:%d", c);
      fprintf(stderr,", charcount:%ld", charcounts[c]);
      fprintf(stderr,", tcount:%ld\n", tcount);
    }
#endif
    if(charcounts[c] != tcount) {
      fprintf(stderr,"%s: internal error:", procname);
      fprintf(stderr,"wordcounts and charcounts do not match");
      fprintf(stderr,", char:%c(0x%02x)", c, c);
      fprintf(stderr,", charcount:%ld", charcounts[c]);
      fprintf(stderr,", tcount:%ld", tcount);
      fprintf(stderr,"\n");
      //exit(2);
    }
  } // for(c = 0; c < MAXCOUNT; c++)	

#endif
  
  unsigned long wtotal = 0;
  unsigned long wordscnt = 0;
  
  // calculate words statistics average

  // first words total and count
  
  p = iset;

  // read thru elements element by element

  while(*p != '\0') {
    db5_skipwhite(&p);
    db5_get_element(&namelen, &name, &valuelen, &value, &p);
    db5_skipwhite(&p);
    if(*p == ',') {
      p++;
      db5_skipwhite(&p);
      if(*p == ',') {
	p++;
	db5_skipwhite(&p);
	fprintf(stderr,"%s: excess comma, set:%s\n", procname, iset);
	fflush(stderr);
	exit(1);
      }
    }

    long int temp;
    unsigned char tempbuffer[32];

    strncpy(tempbuffer, value, valuelen);
    tempbuffer[valuelen] = '\0';
    temp = atol(tempbuffer);
    wtotal += temp;

    wordscnt++;

  } // end of while(*set != '\0')

  // average (words statistics)
  
  long int waverage = wtotal / wordscnt;

  // print words statistics
  
  for(int e = 0; e < 2; e++) {
    unsigned long total = 0;
    
    min = LONG_MAX;
    max = LONG_MIN;
    
    if(e == 0) {
      if(!words)
	continue;
      fprintf(stdout,"words     ");
    } else {
      if(!wdiff)
	continue;
      fprintf(stdout,"wdiff     ");
    }

    unsigned char *iset = db5_get_set();
    
    // read thru elements element by element

    first = 1;
    p = iset;

    while(*p != '\0') {
      if(!first)
	fprintf(stdout, ", ");
      
      db5_skipwhite(&p);
      db5_get_element(&namelen, &name, &valuelen, &value, &p);
      db5_skipwhite(&p);
      if(*p == ',') {
	p++;
	db5_skipwhite(&p);
	if(*p == ',') {
	  p++;
	  db5_skipwhite(&p);
	  fprintf(stderr,"%s: excess comma, set:%s\n", procname, iset);
	  fflush(stderr);
	  exit(1);
	}
      }

      unsigned char tempbuffer[32];
      strncpy(tempbuffer, value, valuelen);
      tempbuffer[valuelen] = '\0';
      
      long int temp;
      if(e == 0)
	temp = atol(tempbuffer);
      else
	temp = waverage - atol(tempbuffer);
      
      fprintf(stdout,"%.*s:", namelen, name);
      fprintf(stdout,"%ld", temp);
      
      if(min > temp)
	min = temp;
      if(max < temp)
	max = temp;
      total += temp;

      first = 0;
    } // end of while(*set != '\0')

    if(e == 0) {
      fprintf(stdout,", words:%ld", wordscnt);
      fprintf(stdout,", total:%ld", total);
      fprintf(stdout,", average:%ld", waverage);
    } else {
      fprintf(stdout,", total:%ld", total);
    }

    fprintf(stdout,", min:%ld", min);
    fprintf(stdout,", max:%ld", max);
    fprintf(stdout,"\n");
    fflush(stdout);
  } //end of for(e = 0;e < 2; e++)
}

Tässä vielä kuva DEBUG11:n tulostamasta listasta: Listalle tulee kaksi riviä (old ja new) silloin kun set:n koko muuttuu, eli käydään muuttamassa tietueen kokoa realloc:issa.

$ ./newressu --dec --space --lineno -s1 -l10 | ./newressutest9 --set | more
old length:0 (null)
new length:10 '9' = "1"
old length:10 '9' = "1"
new length:21 '0' = "1", '9' = "1"
old length:21 '0' = "1", '9' = "1"
new length:32 '0' = "1", '7' = "1", '9' = "1"
old length:32 '0' = "1", '7' = "1", '9' = "1"
new length:43 '0' = "1", '2' = "1", '7' = "1", '9' = "1"
old length:43 '0' = "1", '2' = "1", '7' = "1", '9' = "1"
new length:54 '0' = "1", '2' = "1", '4' = "1", '7' = "1", '9' = "1"
old length:54 '0' = "1", '2' = "1", '4' = "1", '7' = "1", '9' = "1"
new length:65 '0' = "1", '2' = "1", '3' = "1", '4' = "1", '7' = "1", '9' = "1"
old length:65 '0' = "1", '2' = "1", '3' = "1", '4' = "1", '7' = "1", '9' = "1"
new length:76 '0' = "1", '2' = "1", '3' = "1", '4' = "1", '5' = "1", '7' = "1", '9' = "1"
old length:76 '0' = "2", '2' = "1", '3' = "1", '4' = "1", '5' = "1", '7' = "2", '9' = "1"
new length:87 '0' = "2", '1' = "1", '2' = "1", '3' = "1", '4' = "1", '5' = "1", '7' = "2", '9' = "1"
old length:87 '0' = "5", '1' = "3", '2' = "1", '3' = "2", '4' = "1", '5' = "7", '7' = "3", '9' = "4"
new length:98 '0' = "5", '1' = "3", '2' = "1", '3' = "2", '4' = "1", '5' = "7", '7' = "3", '8' = "1", '9' = "4"
old length:98 '0' = "5", '1' = "5", '2' = "1", '3' = "3", '4' = "5", '5' = "9", '7' = "3", '8' = "3", '9' = "5"
new length:109 '0' = "5", '1' = "5", '2' = "1", '3' = "3", '4' = "5", '5' = "9", '6' = "1", '7' = "3", '8' = "3", '9' = "5"
old length:109 '0' = "5", '1' = "5", '2' = "1", '3' = "4", '4' = "5", '5' = "9", '6' = "1", '7' = "3", '8' = "3", '9' = "5"
new length:110 '0' = "5", '1' = "5", '2' = "1", '3' = "4", '4' = "5", '5' = "10", '6' = "1", '7' = "3", '8' = "3", '9' = "5"
old length:110 '0' = "8", '1' = "9", '2' = "2", '3' = "5", '4' = "6", '5' = "14", '6' = "4", '7' = "4", '8' = "9", '9' = "6"
new length:111 '0' = "8", '1' = "9", '2' = "2", '3' = "5", '4' = "6", '5' = "14", '6' = "4", '7' = "4", '8' = "10", '9' = "6"
old length:111 '0' = "8", '1' = "9", '2' = "2", '3' = "5", '4' = "6", '5' = "14", '6' = "4", '7' = "4", '8' = "10", '9' = "6"
new length:112 '0' = "8", '1' = "10", '2' = "2", '3' = "5", '4' = "6", '5' = "14", '6' = "4", '7' = "4", '8' = "10", '9' = "6"
old length:112 '0' = "9", '1' = "15", '2' = "4", '3' = "6", '4' = "6", '5' = "15", '6' = "5", '7' = "5", '8' = "14", '9' = "6"
new length:113 '0' = "10", '1' = "15", '2' = "4", '3' = "6", '4' = "6", '5' = "15", '6' = "5", '7' = "5", '8' = "14", '9' = "6"
old length:113 '0' = "11", '1' = "20", '2' = "6", '3' = "9", '4' = "8", '5' = "16", '6' = "6", '7' = "8", '8' = "17", '9' = "6"
new length:114 '0' = "11", '1' = "20", '2' = "6", '3' = "10", '4' = "8", '5' = "16", '6' = "6", '7' = "8", '8' = "17", '9' = "6"
old length:114 '0' = "13", '1' = "20", '2' = "6", '3' = "10", '4' = "9", '5' = "17", '6' = "8", '7' = "8", '8' = "18", '9' = "7"
new length:115 '0' = "13", '1' = "20", '2' = "6", '3' = "10", '4' = "10", '5' = "17", '6' = "8", '7' = "8", '8' = "18", '9' = "7"
old length:115 '0' = "13", '1' = "20", '2' = "7", '3' = "11", '4' = "11", '5' = "21", '6' = "8", '7' = "9", '8' = "19", '9' = "8"
new length:116 '0' = "13", '1' = "20", '2' = "7", '3' = "11", '4' = "11", '5' = "21", '6' = "8", '7' = "10", '8' = "19", '9' = "8"
old length:116 '0' = "15", '1' = "22", '2' = "7", '3' = "11", '4' = "12", '5' = "21", '6' = "9", '7' = "10", '8' = "19", '9' = "9"
new length:117 '0' = "15", '1' = "22", '2' = "7", '3' = "11", '4' = "12", '5' = "21", '6' = "9", '7' = "10", '8' = "19", '9' = "10"
old length:117 '0' = "15", '1' = "22", '2' = "8", '3' = "11", '4' = "12", '5' = "22", '6' = "9", '7' = "10", '8' = "20", '9' = "10"
new length:118 '0' = "15", '1' = "22", '2' = "8", '3' = "11", '4' = "12", '5' = "22", '6' = "10", '7' = "10", '8' = "20", '9' = "10"
old length:118 '0' = "20", '1' = "25", '2' = "9", '3' = "14", '4' = "14", '5' = "25", '6' = "14", '7' = "15", '8' = "23", '9' = "11"
new length:119 '0' = "20", '1' = "25", '2' = "10", '3' = "14", '4' = "14", '5' = "25", '6' = "14", '7' = "15", '8' = "23", '9' = "11"
set       '0' = "36", '1' = "43", '2' = "26", '3' = "28", '4' = "35", '5' = "44", '6' = "31", '7' = "40", '8' = "47", '9' = "30"
ctrl      LF:10, space:350
chars     0:36, 1:43, 2:26, 3:28, 4:35, 5:44, 6:31, 7:40, 8:47, 9:30, words:10, total:360, average:36, min:26, max:47
cdiff     0:0, 1:-7, 2:10, 3:8, 4:1, 5:-8, 6:5, 7:-4, 8:-11, 9:6, total:0, min:-11, max:10
words     0:36, 1:43, 2:26, 3:28, 4:35, 5:44, 6:31, 7:40, 8:47, 9:30, words:10, total:360, average:36, min:26, max:47
wdiff     0:0, 1:-7, 2:10, 3:8, 4:1, 5:-8, 6:5, 7:-4, 8:-11, 9:6, total:0, min:-11, max:10
$ 

Uudelleenkirjoitin myös newressun stat_line rutiinit: uudessa versiossa ideana on että tilarivi tulostetaan vain, jos se on muuttunut. Uudessa versiossa tilarivi kootaan stat_line muuttujaan ja end() rutiinissa koko rivi tulostetaan, jos se on muuttunut. End() rutiini tallettaa stat_line muuttujan prev_stat_line muuttujaan seuraavaa vertailua varten. cursor_remove() ja cursor_start() kutsut on siirretty _end() rutiiniin.

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

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

void stat_line_cursor()
{
  long int secs;

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

void stat_line_cursor_remove()
{
  if(cursor_on) {
    fprintf(stderr, "\b \b");
    fflush(stderr);
    prev_secs = -1;
  }
  cursor_on = 0;
}

#include <stdarg.h>

#define aDEBUG31 2

unsigned char *procname = NULL;

static unsigned char *prev_stat_line = NULL;
static unsigned char *stat_line = NULL;
static size_t stat_line_length = 0;

static void stat_line_begin()
{
  if(stat_line != NULL)
    stat_line[0] = '\0';
}

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

  va_start(args, format);
  count = vsnprintf(buffer, buffer_length, format, args) + 1;
  va_end(args);

  if(buffer_length < count) {
    buffer_length = count;
    buffer = realloc(buffer, buffer_length);
    va_start(args, format);
    count = vsnprintf(buffer, buffer_length, format, args) + 1;
    va_end(args);
  }

  count = 0;
  if(stat_line != NULL)
    count += strlen(stat_line);
  count += strlen(buffer);
  count++;
  
  if(stat_line_length < count) {
    unsigned char *stat_line2 = stat_line;
    stat_line_length = count;
    stat_line = realloc(stat_line, stat_line_length);
    prev_stat_line = realloc(prev_stat_line, stat_line_length);
    if(stat_line2 == NULL) {
      stat_line[0] = '\0';
      prev_stat_line[0] = '\0';
    }
  }
  strcat(stat_line, buffer);
}

#define READABLE_NUMBER_BIN 2

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

#define READABLE_NUMBER_WIDTH 32

static void stat_line_get_readable(unsigned char buf10[10], unsigned long length)
{
  int c, low;
  double length2;

  // B = byte
  // K = kilo   10^3   2^10
  // M = mega   10^6   2^20
  // G = giga   10^9   2^30
  // T = tera   10^12  2^40
  // P = peta   10^15  2^50
  // E = exa    10^18  2^60
  // Z = zetta  10^21  2^70
  // Y = yotta  10^24  2^80

  char units[] = "BKMGTPEZY";

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

  for(c = 0; length >= low &&
      c < sizeof(units) - 1; c++) {
    if(length >= low &&
       length <= READABLE_NUMBER_HIGH) {
      if(units[c] == 'B')
        sprintf(buf10, "%ld", length);
      else if(units[c] == 'K' ||
	      units[c] == 'M' ||
	      units[c] == 'G' ||
	      units[c] == 'T')
	sprintf(buf10, "%.3f", length2);
      else if(length == length2)
	sprintf(buf10, "%ld", length);
      else
	sprintf(buf10, "%.1f", length2);

      if(strchr(buf10, '.') != NULL) {
	int d;
	d = strlen(buf10) - 1;
	while(d > 3 && isdigit(buf10[d])) {
	  buf10[d] = '\0';
	  d--;
	}
	if(buf10[strlen(buf10)-1] == '.')
	  buf10[strlen(buf10)-1] ='\0';
      }

      if(units[c] != 'B') {
	char unit[10];
	sprintf(unit,"%cB",units[c]);
	strcat(buf10, unit);
      }
      break;
    }
    length2 = (double)length / READABLE_NUMBER_DIVIDER;
    length /= READABLE_NUMBER_DIVIDER;
    low = 1;
  }
}

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

void stat_line_end()
{
  int c, d;

  if(stat_line != NULL && strcmp(prev_stat_line, stat_line)) { // status line changed
    stat_line_cursor_remove();
#ifdef DEBUG31
    fprintf(stderr, "\n");
#else
    fprintf(stderr, "\r");
#endif  

    fprintf(stderr,"%s", stat_line);
    fflush(stderr);

    // previous line longer than this one,
    // overwrite with spaces and backspace to
    // end
    
    d = strlen(prev_stat_line) - strlen(stat_line) + 1; // cursor too

    if(d > 0) { // previous line longer
      for(c = 0; c < d; c++) {
#ifdef DEBUG31
	fprintf(stderr, "*");
#else
	fprintf(stderr, " "); // print spaces
#endif
      }
#ifndef DEBUG31
      for(c = 0; c < d; c++)
	fprintf(stderr, "\b"); // and backspaces
#endif
    }
    strcpy(prev_stat_line, stat_line);
    if(strlen(stat_line) > 0)
      stat_line_cursor_start();
  }
}

sample() rutiini eli tiedoston muodostaminen satunnaisbiteistä sai myös uudelleenkirjoitusta. Aiemmin koodissa oli kaikkia clim ja dlim muuttujia, uudessa versiossa on vain tiedoston pituus merkkeinä ja blokin koko. Lisäksi aiemmat cursor_remove ja cursor_start rutiinit on poistettu niiden stat_line rutiiniin lisäämisen takia. Tiedoston nimeen on lisätty sarjanumero, joten sample ei poista aiempia tuloksia. Pilkkujen käyttöä tilarivissä on muutettu. Dieharder-satunnaislukuanalyysin voi myös ajaa automaattisesti sample ajon jälkeen. (–dieharder komentorivitoiminto). Die tulee ilmeisesti sanasta noppa.. Sample rutiinissa on ajon aikainen tilarivi, siinä on esimerkki aiempien rutiinien käytöstä.

static int sample = 0, dieharder = 0; // randomness analysis

// try randomness analysis with: $ dieharder -a -g 201 -f newressusample1.rnd > newressusample1.rnd.dieharder

#include <signal.h>

typedef void (*sighandler_t)(int);

void sample_handler()
{
  static int times = 0;

  times++;

  fprintf(stderr,"Ctrl-c pressed %d times\n", times);
  if(times >= 10) {
    signal(SIGINT, SIG_DFL);
    exit(1);
  }
}

#define DEBUG39

#define FILESIZE 1 * 1024 * 1024 * 1024
#define BLOCKSIZE 128 * 1024
#define BLOCKS 8 * 1024

unsigned long long filesize = FILESIZE, blocks = BLOCKS;
unsigned int blocksize = BLOCKSIZE;
int filesize_set = 0, blocks_set = 0, blocksize_set = 0;

void dump_sample() // & dieharder analysis
{
#define STAT_LINE_SHOW_BLOCK 2 // on by default
#define STAT_LINE_SHOW_WRITTEN 2 // on by default
#define STAT_LINE_SHOW_SPEED 2 // on by default
#define STAT_LINE_SHOW_NOW 2 // on by default
#define STAT_LINE_SHOW_LEFT 2 // on by default
#define STAT_LINE_SHOW_READY 2 // on by default

#define STAT_LINE_LESS_WOBBLE 2 // on by default

#define TIMEFORMAT "%H:%M %Z"
#define TIMEFORMAT2 "%a %H:%M %Z"
#define DATEFORMAT "%a %d %b %Y"
    
  unsigned long long c;
  unsigned char *buffer, filename[128], command[1024];
  
  FILE *fp1;
  
  // find first available filename,
  // or empty "fileslot"
  
  for(c = 1; c <= 99999; c++) {
    sprintf(filename, samplefilename, c);
    if((fp1 = fopen(filename, "r")) != NULL) {
      fclose(fp1);
      continue;
    }
    unsigned char filename2[138];
    sprintf(filename2,"%s.dieharder",filename);
    if((fp1 = fopen(filename2, "r")) != NULL) {
      fclose(fp1);
      continue;
    }
#ifdef SAMPLE_HASH
    sprintf(filename2,"%s.sha256",filename);
    if((fp1 = fopen(filename2, "r")) != NULL) {
      fclose(fp1);
      continue;
    }
#endif
    break;
  }

  // calculate missing file size parameters
  
  if(!filesize_set)
    filesize = blocks * blocksize;
  else if(!blocksize_set) {
    blocksize = (filesize + blocks - 1) / blocks; // round up
    filesize = blocks * blocksize;
  } else if(!blocks_set) {
    blocks = (filesize + blocksize - 1)/ blocksize; // round up
    filesize = blocks * blocksize;
  }

#define aEXFAT_FIX 2 // on for now
  
#ifdef EXFAT_FIX

  if(blocksize > KILO * KILO) { // max "blocksize" 1m (exfat)
    blocksize = KILO * KILO;
    blocks = (filesize + blocksize - 1) / blocksize;
    filesize = blocks * blocksize;
  }

#endif
  
  if(filesize != blocks * blocksize ||
     blocks < 1 || blocksize < 1) {
    fflush(stdout);
    fprintf(stderr,"%s: sample(): mismatched parameters", procname);
    fprintf(stderr,", blocks:%llu(", blocks);
    readablelonglong(stderr, blocks);
    fprintf(stderr,")");
    fprintf(stderr,", blocksize:%u(", blocksize);
    readablelonglong(stderr, blocksize);
    fprintf(stderr,")");
    fprintf(stderr,", filesize:%llu(", filesize);
    readablelonglong(stderr, filesize);
    fprintf(stderr,")");
    fprintf(stderr,"\n");
    fflush(stderr);
    exit(1);
  }
  
  if(filesize / blocks != blocksize) {
    fflush(stdout);
    fprintf(stderr,"%s: sample(): parameter overflow", procname);
    fprintf(stderr,", blocks:%llu(", blocks);
    readablelonglong(stderr, blocks);
    fprintf(stderr,")");
    fprintf(stderr,", blocksize:%u(", blocksize);
    readablelonglong(stderr, blocksize);
    fprintf(stderr,")");
    fprintf(stderr,", filesize:%llu(", filesize);
    readablelonglong(stderr, filesize);
    fprintf(stderr,")");
    fprintf(stderr,"\n");
    fflush(stderr);
    exit(1);
  }

#ifdef DEBUG39
  fprintf(stderr,"blocksize:%u(", blocksize);
  readablelonglong(stderr, blocksize);
  fprintf(stderr,")");
  fprintf(stderr,", blocks:%llu(", blocks);
  readablelonglong(stderr, blocks);
  fprintf(stderr,")");
  fprintf(stderr,", filesize:%llu(", filesize);
  readablelonglong(stderr, filesize);
  fprintf(stderr,")\n");
  fflush(stderr);
#endif

  if((buffer = malloc(blocksize)) == NULL) {
    fprintf(stderr,"%s: sample(): cannot allocate memory (buffer)\n", procname);
    exit(1);
  }
  
#define DEBUG40 2 // default on (for now)
    
#ifdef DEBUG40
  fprintf(stderr,"%s: sample(): writing file", procname);
  fprintf(stderr,", filename:%s", filename);
#ifndef SAMPLE_WRITE
  fprintf(stderr,", no write");
#endif
  fprintf(stderr,"\n");
#endif
  
#ifdef SAMPLE_HASH
  unsigned char digest[HashLen];
  HashCtx hash;
  
  HashInit(&hash); // calculate hash
#endif

  // variables for stat line fields

  time_t start, now, left, end;
  unsigned char speed[10], printspeed[10] = "";

#ifdef STAT_LINE_LESS_WOBBLE

  time_t prevend = -1;
  int countspeed = 0, countend = 0;
  unsigned char prevspeed[10] = "";
  unsigned int calculateleft;

#endif

  time_t printleft = -1, printend = -1;
  int leftprinted = 0;
  
  if((fp1 = fopen(filename, "a")) != NULL) {

    // starting time
    
    start = time(NULL);
    
    unsigned long long clim = (unsigned long long) filesize / blocksize;

#ifdef EXFAT_FIX
    int mbinblocks = (1 * KILO * KILO) / blocksize;
    if(mbinblocks == 0)
      mbinblocks = 1;
    fprintf(stderr,"mbinblocks:%d\n", mbinblocks);
#endif
    
    // you have to press ctrl-c 10 times

    signal(SIGINT, sample_handler);
    
    for(c = 0; c < clim; c++) {

      // current time
      
      now = time(NULL);

      // estimate for ending time
      
      end = (time_t)start + ((((double)now - start) / c) * clim);

#ifdef STAT_LINE_LESS_WOBBLE

      calculateleft = 0; 
      if(prevend / 60 == end / 60 &&
	 ++countend >= 30) { // 30 times same minutes --> minutes are real

	printend = end;
	calculateleft = 1;
      } else if(prevend / 60 != end / 60) { // minutes different
	prevend = end;
	countend = 0;
      }

#else
      printend = end;
#endif

      // estimate for time left

      left = printend - now;

#ifdef STAT_LINE_LESS_WOBBLE
      
      static int onlyseconds = 0;
      static long long cstart, cleft;
      
      if(onlyseconds || (left > 0 && left < 60)) {
	if(!onlyseconds) {
	  cstart = c;
	  cleft = clim - c;
	  onlyseconds = 1;
	}

	left = (time_t) 60 - ((((double) c - cstart) / cleft) * 60);
      }

      if(calculateleft)
	printleft = left;
      
#else
      printleft = left;
#endif

      // speed
      
      if(now - start > 1.0)
	stat_line_get_readable(speed, (unsigned long long)((double)c * blocksize / (now - start)) );
      else
	stat_line_get_readable(speed, (unsigned long long)((double)c * blocksize));

#ifdef STAT_LINE_LESS_WOBBLE

      if(!strcmp(prevspeed, speed) &&
	 ++countspeed >= 3) { // three times same speed --> speed is real
	strcpy(printspeed, speed);
      } else if(strcmp(prevspeed, speed)) {
	strcpy(prevspeed, speed);
	countspeed = 0;
      }

#else
      strcpy(printspeed, speed);
#endif
      
      // 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();
      
      int print = 0;
#ifdef STAT_LINE_SHOW_BLOCK
      
      stat_line_printf("%s", randomgen[input]);
      
      // print block from 0 to 8192
      
      stat_line_printf("block %d", c);
      print = 1;
#endif
      
#ifdef STAT_LINE_SHOW_WRITTEN
      
      // print written
      
      if(print)
	stat_line_printf(", ");
      
      stat_line_printf("written ");
      stat_line_readable((unsigned long)c * blocksize);
      print = 1;
#endif	  
      if(c > 0) {
	
#ifdef STAT_LINE_SHOW_SPEED
	
	// print speed
	
	if(printspeed[0] != '\0') {
	  if(print)
	    stat_line_printf(", ");

	  stat_line_printf("%s/sec", printspeed);
	  print = 1;
	}
#endif
	
#if defined STAT_LINE_SHOW_NOW ||			\
  defined STAT_LINE_SHOW_READY
	char timebuf[128];
#endif
	
#ifdef STAT_LINE_SHOW_READY
	char timebuf2[128];
#endif
	
#ifdef STAT_LINE_SHOW_NOW
	
	// print now
	
	if(print)
	  stat_line_printf(", ");
	
	stat_line_printf("now");
	strftime(timebuf, sizeof(timebuf), TIMEFORMAT2,
		 localtime((time_t *)&now));
	stat_line_printf(" %s", timebuf);
	print = 1;
#endif
	
#ifdef STAT_LINE_SHOW_LEFT
	
	// print left
	
	if(printleft > 0) {

	  unsigned long int left2;
	  unsigned long int temp;
	  int timeprinted = 0;
	  
	  if(print)
	    stat_line_printf(", ");

	  left2 = printleft;
	  stat_line_printf("left");
	  
	  temp = left2 / (24 * 3600); // days
	  if(temp > 0) {
	    timeprinted = 1;
	    stat_line_printf(" %dd", temp);
	    left2 -= temp * (24 * 3600);
	  }
	  
	  temp = left2 / 3600; // hours
	  if(temp > 0 || timeprinted) {
	    timeprinted = 1;
	    //hoursprinted = 1;
	    stat_line_printf(" %02dh", temp);
	    left2 -= temp * 3600;
	  }
	  
	  temp = left2 / 60; // minutes
	  if(temp > 0 || timeprinted) {
	    timeprinted = 1;
	    stat_line_printf(" %02dm", temp);
	    left2 -= temp * 60;
	  }
	  
	  temp = left2; // seconds
	  if(!timeprinted) {
	    stat_line_printf(" %d seconds", temp);
	  }

	  leftprinted = 1;
	  print = 1;
	}
#endif
	
#ifdef STAT_LINE_SHOW_READY
	
	//unsigned long int end = (int)start + ((((double)now - start) / c) * clim);

	if(printend > 0 && leftprinted) {
	  if(print)
	    stat_line_printf(", ");
	  
	  stat_line_printf("ready at");
	  strftime(timebuf, sizeof(timebuf), DATEFORMAT,
		   localtime((time_t *) & end));
	  strftime(timebuf2, sizeof(timebuf2), DATEFORMAT,
		   localtime((time_t *) & now));
	  if(strcmp(timebuf, timebuf2)) {
	    stat_line_printf(" %s", timebuf);
	  }
	  
	  // print end time
	  
	  strftime(timebuf, sizeof(timebuf), TIMEFORMAT,
		   localtime((time_t *) & printend));
	  stat_line_printf(" %s", timebuf);
	  
	  print = 1;
	}
#endif
      }
      stat_line_end();
      stat_line_cursor_start();
      
      // do the work
      
      stat_line_cursor();
      
      memset(buffer, 0, blocksize);
      
      if(input == INPUT_RESSU) // ressu prod
	ressu_genbytes(blocksize, buffer);
      
      else if(input == INPUT_PSEUDORESSU) // pseudoressu
	pseudoressu_bytes(blocksize, buffer);
      
      else if(input == INPUT_FASTRESSU) // ressu fast
	ressu_genbytes_fast(blocksize, buffer);
      
      else if(input == INPUT_SINGLERESSU) // ressu single
	ressu_genbytes_single(blocksize, buffer);
      
      else if(input == INPUT_STREAM) // ressu stream
	stream_bytes(blocksize, buffer);
      
#ifdef FORT
      else if(input == INPUT_FORT) // ressu fort
	fort_random_data(blocksize, buffer);
      
      else if(input == INPUT_FORTXOR) // ressu fort
	fort_random_data_xor(blocksize, buffer);
      
#endif
#ifdef USE_RDRAND
      else if(input == INPUT_RDRAND) // intel rdrand
	rdrand_bytes(blocksize, buffer);
      
#endif
#ifdef USE_RDSEED
      else if(input == INPUT_RDSEED) // intel rdseed
	rdseed_bytes(blocksize, buffer);
      
#endif
      else if(input == INPUT_URANDOM) // urandom
	readfile_xor(blocksize, buffer, urandomfilename);
      
#ifdef USE_RANDOM
      else if(input == INPUT_RANDOM) // random
	readfile_xor(blocksize, buffer, randomfilename);
      
#endif
#ifdef USE_DUMMY
      else if(input == INPUT_DUMMY) {
	// no generator
      }
#endif
      else {
	fprintf(stdout,"\n%s: mode '%d'(%s) not available\n",
		procname, input, randomgen[input]);
	exit(2);
      }
#ifdef SAMPLE_HASH
      HashUpdate(&hash, buffer, blocksize); // calculate hash
#endif
#ifdef SAMPLE_WRITE
      if(input != INPUT_DUMMY) {
	fwrite(buffer, 1, blocksize, fp1);
	
#ifdef SAMPLE_SYNC

#ifdef EXFAT_FIX
	if(c % mbinblocks == 0) { // fflush and sync (EXFAT)
	  fflush(fp1);
	  sync();
	  //fprintf(stderr,"*\n");
	}
#endif

#endif
      } // end of if(input != INPUT_DUMMY
#endif
    } // end of for(c = 0; c < clim; c++
#ifdef SAMPLE_HASH
    HashFinal(digest, &hash); // calculate hash
#endif

    fflush(fp1); // fflush and sync (EXFAT)
    sync();
    
    fclose(fp1);
    
  } else { // if((fp1=fopen
    fprintf(stderr,"%s:", procname);
    fprintf(stderr," sample(): cannot open file");
    fprintf(stderr,", filename: %s", filename);
    fprintf(stderr,"\n");
    exit(2);
  }
  
  // remove last status line

  stat_line_cursor_remove();
  
  stat_line_begin();
  
  stat_line_printf("\rwrote ");
  stat_line_readable((unsigned long)c * blocksize);
  
  stat_line_printf(", ");
  if(now - start >= 1.0)
    stat_line_readable((unsigned long)((double)c * blocksize / (now - start)) );
  else
    stat_line_readable((unsigned long)c * blocksize);
  stat_line_printf("/sec");
  
  stat_line_printf(", done!");
  stat_line_end();
  stat_line_cursor_remove();
  fprintf(stdout,"\n");
  fflush(stdout);

#ifdef SAMPLE_HASH
  unsigned char filename2[138];
  unsigned char hashstring[2 * HashLen + 1];
  
  hashfinal2string(hashstring, digest);

  sprintf(filename2,"%s.sha256", filename);
  
  fprintf(stderr,"%s: sample(): hashing file", procname);
  fprintf(stderr,", filename:%s", filename);
  fprintf(stderr,", hashfilename:%s", filename2);
  fprintf(stderr,", sha256:%s\n", hashstring);
  
  if((fp1 = fopen(filename2, "a")) != NULL) {
    fprintf(fp1,"%s\n",hashstring);
    fclose(fp1);
  }
  fprintf(stderr,"%s: sample(): checking sha256", procname);
  fprintf(stderr,", filename:%s\n", filename);
  sprintf(command,"sha256sum %s", filename);
  system(command);
#endif

  signal(SIGINT, SIG_DFL);

  free(buffer);
  
  if(dieharder) { // randomness analysis
    
    sprintf(command,"dieharder -a -g 201 -f %s > %s.dieharder 2>&1", filename, filename);
    //sprintf(command,"dieharder -a -g 201 -f %s > %s.dieharder", filename, filename);
    fprintf(stderr,"%s: sample(): running dieharder \"%s\"\n", procname, command);
    system(command);
    sprintf(command,"grep \"WEAK\\|FAILED\" %s.dieharder", filename);
    fprintf(stderr,"%s: sample(): grepping diehard problems \"%s\"\n", procname, command);
    system(command);
    sprintf(command,"grep rewound %s.dieharder | tail -n1", filename);
    fprintf(stderr,"%s: sample(): grepping last rewound count \"%s\"\n", procname, command);
    system(command);
    exit(0);
  }
}

Seuraavassa ajetaan sample() ja –dieharder analyysi: Dieharder on antanut yleensä muutamia WEAK:keja:

$ ./newressu --single --sample --dieharder
./newressu: sample: writing file, filename:newressusample1.rnd
wrote 16.0GB, 4.1MB/sec, done!                                                                           
./newressu: dieharder: running dieharder "dieharder -a -g 201 -f newressusample1.rnd > newressusample1.rnd.dieharder"
# The file file_input_raw was rewound 1 times
# The file file_input_raw was rewound 1 times
# The file file_input_raw was rewound 1 times
# The file file_input_raw was rewound 1 times
# The file file_input_raw was rewound 1 times
# The file file_input_raw was rewound 1 times
# The file file_input_raw was rewound 1 times
# The file file_input_raw was rewound 1 times
# The file file_input_raw was rewound 1 times
# The file file_input_raw was rewound 1 times
# The file file_input_raw was rewound 1 times
# The file file_input_raw was rewound 1 times
# The file file_input_raw was rewound 1 times
# The file file_input_raw was rewound 1 times
# The file file_input_raw was rewound 1 times
# The file file_input_raw was rewound 1 times
# The file file_input_raw was rewound 1 times
# The file file_input_raw was rewound 1 times
# The file file_input_raw was rewound 2 times
# The file file_input_raw was rewound 2 times
# The file file_input_raw was rewound 2 times
# The file file_input_raw was rewound 2 times
# The file file_input_raw was rewound 3 times
# The file file_input_raw was rewound 3 times
# The file file_input_raw was rewound 3 times
# The file file_input_raw was rewound 3 times
# The file file_input_raw was rewound 4 times
# The file file_input_raw was rewound 4 times
# The file file_input_raw was rewound 5 times
# The file file_input_raw was rewound 5 times
# The file file_input_raw was rewound 6 times
# The file file_input_raw was rewound 6 times
# The file file_input_raw was rewound 7 times
# The file file_input_raw was rewound 7 times
# The file file_input_raw was rewound 8 times
# The file file_input_raw was rewound 8 times
# The file file_input_raw was rewound 9 times
# The file file_input_raw was rewound 9 times
# The file file_input_raw was rewound 10 times
# The file file_input_raw was rewound 11 times
# The file file_input_raw was rewound 12 times
# The file file_input_raw was rewound 12 times
# The file file_input_raw was rewound 13 times
# The file file_input_raw was rewound 14 times
# The file file_input_raw was rewound 14 times
# The file file_input_raw was rewound 14 times
# The file file_input_raw was rewound 14 times
# The file file_input_raw was rewound 14 times
# The file file_input_raw was rewound 14 times
# The file file_input_raw was rewound 14 times

$ fgrep WEAK newressusample1.rnd.dieharder 
      rgb_lagged_sum|  18|   1000000|     100|0.99605238|   WEAK   
media$ fgrep FAILED newressusample1.rnd.dieharder 
media$

Edellisessä ajossakin tuli pari heikosti suoriuduttua riviä.

Poistettu ./newressu –sample –dieharder ohjelmasta nuo aiemman esimerkin “The file” rivit ja listattu grepillä loppuun –dieharder:in ongelmat:

Bugivälike: Tästä raportista tuli varsinainen bugiraportti, ensin ressussa löytyi tuo +2 ongelma, sitten ongelmia usb levyn kanssa (talletettu tiedosto ei ollut fixed length). Näistä +2 bugi sai korjauksen, joten jäljelle jäi usb ongelma. Kokeilin ajaa –sample –dieharderin usb levyllä, ja se meni läpi ihan ok: tällä kertaa yksi WEAK:ki. Huomaa myös että ajo on tehty –single moodilla..

media$ ./newressu --single --sample --dieharder
./newressu: sample: writing file, filename:newressusample1.rnd
wrote 16.0GB, 4.1MB/sec, done!                                                                           
./newressu: dieharder: running dieharder "dieharder -a -g 201 -f newressusample1.rnd > newressusample1.rnd.dieharder"
# The file file_input_raw was rewound 1 times
# The file file_input_raw was rewound 1 times
# The file file_input_raw was rewound 1 times
# The file file_input_raw was rewound 1 times
# The file file_input_raw was rewound 1 times
# The file file_input_raw was rewound 1 times
# The file file_input_raw was rewound 1 times
# The file file_input_raw was rewound 1 times
# The file file_input_raw was rewound 1 times
# The file file_input_raw was rewound 1 times
# The file file_input_raw was rewound 1 times
# The file file_input_raw was rewound 1 times
# The file file_input_raw was rewound 1 times
# The file file_input_raw was rewound 1 times
# The file file_input_raw was rewound 1 times
# The file file_input_raw was rewound 1 times
# The file file_input_raw was rewound 1 times
# The file file_input_raw was rewound 1 times
# The file file_input_raw was rewound 2 times
# The file file_input_raw was rewound 2 times
# The file file_input_raw was rewound 2 times
# The file file_input_raw was rewound 2 times
# The file file_input_raw was rewound 3 times
# The file file_input_raw was rewound 3 times
# The file file_input_raw was rewound 3 times
# The file file_input_raw was rewound 3 times
# The file file_input_raw was rewound 4 times
# The file file_input_raw was rewound 4 times
# The file file_input_raw was rewound 5 times
# The file file_input_raw was rewound 5 times
# The file file_input_raw was rewound 6 times
# The file file_input_raw was rewound 6 times
# The file file_input_raw was rewound 7 times
# The file file_input_raw was rewound 7 times
# The file file_input_raw was rewound 8 times
# The file file_input_raw was rewound 8 times
# The file file_input_raw was rewound 9 times
# The file file_input_raw was rewound 9 times
# The file file_input_raw was rewound 10 times
# The file file_input_raw was rewound 11 times
# The file file_input_raw was rewound 12 times
# The file file_input_raw was rewound 12 times
# The file file_input_raw was rewound 13 times
# The file file_input_raw was rewound 14 times
# The file file_input_raw was rewound 14 times
# The file file_input_raw was rewound 14 times
# The file file_input_raw was rewound 14 times
# The file file_input_raw was rewound 14 times
# The file file_input_raw was rewound 14 times
# The file file_input_raw was rewound 14 times

media$ fgrep WEAK newressusample1.rnd.dieharder 
      rgb_lagged_sum|  18|   1000000|     100|0.99605238|   WEAK   
media$ fgrep FAILED newressusample1.rnd.dieharder 
media$

.rnd tiedostossa oli myös oikea koko. Itse käytän usb levyjä koko ajan ja ajattelinkin ihmetellä sitä vielä hetken.. Sample():n tulos on tietysti jatkuva tiedosto, joten muutoksia siinä ei näe. Lisättyä tietoa ei ilmeisesti (koon perusteella) ole.

Tässä vielä tuloste uusimmasta –sample versiosta, joka ei tulosta aiempia “The file” -rivejä vaan vain FAIL ja WEAK rivit ja useammista tiedoston läpikäynneistä. Seuraavassa listauksessa materiaali on käyty läpi 229m kertaa ja löytyi 1 WEAK:ki. Ainakin osa weakeista aiheutuu mielestäni liian pienestä materiaalista. Huomaa myös että tässä on käytetty –single toimintoa.

$ ./newressu --single --sample --dieharder --filesize1g
string:'1g', base:10(10B), multiplier:1073741824(1G), prevll:1(1B), ll:1073741824(1G), totll:1073741824(1G)
blocksize:131072(128K), blocks:8192(8K), filesize:1073741824(1G)
./newressu: sample(): writing file, filename:newressusample432.rnd
wrote 1.00GB, 4.67MB/sec, done!                                                                            
./newressu: sample(): hashing file, filename:newressusample432.rnd, hashfilename:newressusample432.rnd.sha256, sha256:c9b1f180f9c72d2549ed29d7b4c62106192efb04a199e9344feef46376923a1d
./newressu: sample(): checking sha256, filename:newressusample432.rnd
c9b1f180f9c72d2549ed29d7b4c62106192efb04a199e9344feef46376923a1d  newressusample432.rnd
./newressu: sample(): running dieharder "dieharder -a -g 201 -f newressusample432.rnd > newressusample432.rnd.dieharder 2>&1"
./newressu: sample(): grepping diehard problems "grep "WEAK\|FAILED" newressusample432.rnd.dieharder"
          sts_serial|  16|    100000|     100|0.99953101|   WEAK   
./newressu: sample(): grepping last rewound count "grep rewound newressusample432.rnd.dieharder | tail -n1"
# The file file_input_raw was rewound 229 times
$ 

Kirjoitin uuden ohjelman levynkirjoitusongelmaa varten. Se kirjoittaa levylle halutun pituisen tiedoston ja tarkistaa että tieto tuli kirjoitettua oikein. Ongelmaa varten kirjoitin merkkijonosalaus (stream cipher) tyyppisen ohjelman. Näissä tuotetaan satunnaisbittejä siten että kaikki satunnaisuus saadaan avaimesta, ja sen perusteella annetaan näennäissatunnaisia merkkejä. Näin voidaan tuottaa täysin sama satunnaismerkkejä sisältävä merkkijono kaksi kertaa, ensin kirjoittamiseen ja sitten tarkistukseen. Tässä aluksi tuo merkkijonosalaus:

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

#define aDEBUG8 2

static void stream_internalbytes(unsigned char *digest)
{
  HashCtx hash;

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

#ifdef DEBUG8
  fprintf(stderr,"stream_key:");
  dumpline(stderr, sizeof(stream_key), stream_key);
  fprintf(stderr,", cvar:");
  dumpline(stderr, cvarsize + 1, cvar);
  fprintf(stderr,", digest:");
  dumpline(stderr, HashLen, digest);
  fprintf(stderr,"\n");
#endif
}

#define aDEBUG11 2

static int streamt_pos = 0;

#define STREAM_FASTER 2 // default is on

#ifdef STREAM_FASTER
static int streamt_rekey_rounds = 0;
#define STREAM_REKEY_ROUNDS 1024 // 1 secure, more than one faster
#endif

void stream_bytes(int size, unsigned char *buffer)
{
  int c;
  static unsigned char streamt[HashLen]; // 256 bits
  
  for(c = 0; c < size; c++) {

    if(streamt_pos == 0) {
      stream_internalbytes(streamt); // read next data bytes

#ifdef DEBUG11
      dump(stderr, "streamt", sizeof(streamt), streamt, 32);
#endif

#ifndef STREAM_FASTER
      stream_internalbytes(stream_key); // change key
#ifdef DEBUG11
        dump(stderr, "rekey", 32, stream_key, 32);
#endif
#else
      if(++streamt_rekey_rounds >= STREAM_REKEY_ROUNDS) {
	stream_internalbytes(stream_key); // change key
#ifdef DEBUG11
	dump(stderr, "rekey", 32, stream_key, 32);
#endif
	streamt_rekey_rounds = 0;
      }
#endif
    }

    buffer[c] = streamt[streamt_pos];
    streamt_pos = (streamt_pos + 1) % sizeof(streamt);
  } // end of for(c = 0; c < size; c++)

#ifdef DEBUG11
  dump(stderr, "buffer", size, buffer, 32);
#endif
}

#define aDEBUG19 2
#define DEBUG20 2

#define STREAM_KEY_ROUNDS 1024 // should be fast enough and slow enough, now 1024

void stream_open(int size, unsigned char *key) // size = 0 --> zero terminated string
{
  int c;
  HashCtx hash;

  clearcvar();
  streamt_pos = 0;  

#ifdef STREAM_FASTER
  streamt_rekey_rounds = 0;
#endif
  
  if(size == 0)
    size = strlen(key);
  
  HashInit(&hash);
  HashUpdate(&hash, key, size);
  HashUpdate(&hash, (unsigned char *) &cvar, cvarsize + 1);
  inccvar();

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

#ifdef DEBUG19
    fprintf(stderr,"prev:");
    dumpline(stderr, sizeof(stream_key), stream_key);
#endif

    HashFinal(stream_key, &hash);

#ifdef DEBUG19
    fprintf(stderr,", next:");
    dumpline(stderr, sizeof(stream_key), stream_key);
    fprintf(stderr,"\n");
#endif 

    HashInit(&hash);
    HashUpdate(&hash, stream_key, sizeof(stream_key));
    HashUpdate(&hash, (unsigned char *) &cvar, cvarsize + 1);
    inccvar();
  }

  HashFinal(stream_key, &hash);

#ifdef DEBUG19
  fprintf(stderr,"stream_key:");
  dumpline(stderr, sizeof(stream_key), stream_key);
  fprintf(stderr,"\n");
#endif

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

#ifdef DEBUG20
  fflush(stdout);
  fprintf(stderr,"%s: stream_open():", procname);
  fprintf(stderr," key:");
  dumpbin(stderr, size, key);

  //dumpline(stderr, sizeof(key), strlen(key));
  fprintf(stderr,", stream_key:");
  dumpline(stderr, sizeof(stream_key), stream_key);
  fprintf(stderr,"\n");
  fflush(stderr);
#endif
}

Edellisessä ohjelmassa avaimen käsittely stream_open:issa on aika lähellä käyttäjäsalasanan salakirjoitusta. Siinä tehtävät useammat kierrokset (tässä 1024) vaikuttavat toisaalta siihen kuinka nopeasti käyttäjän lokkaantuminen toimii, ja toisaalta siihen kuinka nopeasti hyökkääjä voi kokeilla sanalistansa sanoja. Tietysti vielä tarvitaan mukaan satunnainen salt. Salthan on satunnainen merkkijono, joka talletetaan selväkielisenä salakirjoitetun salasanan kanssa ja sitä käytetään lisänä salasanan salakirjoitetuksessa. Salt varmistaa että sama salasana ei ole aina sama salakirjoitettuna. Tietenkin hyökkääjä tarvitsee tätä hyökkäystä varten salasanan salakirjoitettuna.

Tiedostonkirjoitustestiohjelma tekee tälläisen tulosteen: ajossa merkkijonosalauksen merkkijono (tai avain tai salasana) on tässä “kalakala”. (merkkijonosalauksista lisää hakusanoilla “stream cipher”)

Ensimmäisellä kierroksella kirjoitetaan ja varmistetaan tiedosto. Varmistuksessa (–verify) kirjoitettu tiedosto luetaan uudestaan ja verrataan välittömästi. Tiedostosta lasketaan tiiviste (hash). Tiivisteitä voidaan vertailla “hashing file” -rivillä ja “checking sha256” jälkeisellä rivillä. Voit tietysti tarkastaa hashin myös $ sha256sum newressutest10.106.rnd komennolla (vaihda tiedoston nimi).

Toisella kierroksella luetaan tiedosto ja vertaillaan taas “kalakala” salasanalla saatuun merkkijonoon. Exfat:in aiemmat ongelmat saa pois lisäämällä –sync ja –flush optiot. Minulle jäi kuitenkin tutina että exfat:issa on vielä joku muu ongelma. Jos haluat ihmetellä tätä voit ajaa newressutest10, newressutest ja newressu –sample ohjelmaa itse. (en varmaan ole aiemmin sanonut tätä mutta ajamasi ohjelmat tietysti pitää tarkistaa että ne tekevät suurinpiirtein mitä sanovat, ja että niissä ei ole haitallista materiaalia). Jos ajat ohjelmia exfat levyllä ja jätät –sync ja –flush optiot pois tulee erilaisia virheilmoituksia. Mielestäni ongelmat esiintyvät vain exfat:issa suuremmilla tiedostoilla (>128g). Tavallisilla levyillä en ole niitä havainnut.

Jos käytät –verify optiota, se tarvitsee myös –flush option.

$ ./newressutest10 --bytes1g --flush --verify --sync
bytes:1073741824(0x40000000)
filename:newressutest10.106.rnd
mbinblocks:1024
./newressutest10: stream_open(): key:kalakala, stream_key:448cd1f8dea2f75d469e1f265cbc2658426121577335348a11c5ec7c25c07bf1
Done:100.0% write and verify done.
./newressutest10: sample: hashing file, filename:newressutest10.106.rnd, hashfilename:newressutest10.106.rnd.sha256, sha256:8a8780a51695ae69c8e3d93e940583b42fbc9bd0f7fcf3513444d1c233da6629
./newressutest10: sample: checking sha256, filename:newressutest10.106.rnd
8a8780a51695ae69c8e3d93e940583b42fbc9bd0f7fcf3513444d1c233da6629  newressutest10.106.rnd
./newressutest10: stream_open(): key:kalakala, stream_key:448cd1f8dea2f75d469e1f265cbc2658426121577335348a11c5ec7c25c07bf1
Done:100.0% read and compare done.
$ 

Seuraavassa ohjelman newressutest10 lähdekoodi: (uusi versio, jossa on muutettu merkkijonosalakirjoitusrutiineja ja comentoriviparametreja)

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "sha256.h"

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

#define FILESIZE 1 * 1024 * 1024 * 1024
#define BLOCKSIZE 1024
#define BLOCKS FILESIZE / BLOCKSIZE

void dumpbin(FILE *fp1, int len, unsigned char *buf)
{
  int c;

  for(c = 0; c < len; c++)
    if(isprint(buf[c]) && buf[c]!= ' ')
      fputc(buf[c], stderr);
    else
      fprintf(stderr,"\\%02x", buf[c]);
}

void dumpline(FILE *fp1, int len, unsigned char *buf)
{
  int c;

  for(c = 0; c < len; c++)
    fprintf(fp1,"%02x", buf[c]);
}

void dump(FILE *fp1, unsigned char *header, int len, unsigned char *buf, int linelen)
{
  int c;

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

void compareblocks(FILE *fp1, int size, unsigned char *buffer, unsigned char *buffer2)
{
  int c, count;
  count = 0;
  for(c = 0; c < size; c++) {
    if(count > 31) {
      fprintf(fp1,"\n");
      count = 0;
    }
    if(buffer[c] == buffer2[c]) {
      fprintf(fp1," %02x", buffer[c]);
      count++;
    } else {
      fprintf(fp1," %02x/%02x", buffer[c], buffer2[c]);
      count +=2;
    }
  }
}

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

void inccvar()
{
  int c;

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

  if(cvarsize < c)
    cvarsize = c;
}

void clearcvar()
{
  int c;

  cvarsize = 0;

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

  for(c = 0; c < sizeof(cvar); c++)
    if(cvar[c] != 0)
      cvarsize = c;
}

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

#define aDEBUG8 2

static void stream_internalbytes(unsigned char *digest)
{
  HashCtx hash;

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

#ifdef DEBUG8
  fprintf(stderr,"stream_key:");
  dumpline(stderr, sizeof(stream_key), stream_key);
  fprintf(stderr,", cvar:");
  dumpline(stderr, cvarsize + 1, cvar);
  fprintf(stderr,", digest:");
  dumpline(stderr, HashLen, digest);
  fprintf(stderr,"\n");
#endif
}

#define aDEBUG11 2

static int streamt_pos = 0;

#define STREAM_FASTER 2

#ifdef STREAM_FASTER
static int streamt_rekey_rounds = 0;
#define STREAM_REKEY_ROUNDS 1024 // 1 --> secure, more than one --> faster
#endif

void stream_bytes(int size, unsigned char *buffer)
{
  int c;
  static unsigned char streamt[HashLen]; // 256 bits
  
  for(c = 0; c < size; c++) {

    if(streamt_pos == 0) {
      stream_internalbytes(streamt); // read next data bytes

#ifdef DEBUG11
      dump(stderr, "streamt", 32, streamt, 32);
#endif
      
#ifndef STREAM_FASTER

      stream_internalbytes(stream_key); // change key

#ifdef DEBUG11
      dump(stderr, "rekey", 32, stream_key, 32);
#endif

#else
      if(++streamt_rekey_rounds >= STREAM_REKEY_ROUNDS) {
	stream_internalbytes(stream_key); // change key
#ifdef DEBUG11
	dump(stderr, "rekey", 32, stream_key, 32);
#endif
	streamt_rekey_rounds = 0;
      }
#endif
    } // end of if(streamt_pos == 0

    buffer[c] = streamt[streamt_pos];
    streamt_pos = (streamt_pos + 1) % sizeof(streamt);
  } // end of for(c = 0; c < size; c++)

#ifdef DEBUG11
  dump(stderr, "buffer", size, buffer, 32);
#endif
}

#define aDEBUG19 2
#define DEBUG20 2

#define STREAM_KEY_ROUNDS 1024 // should be fast enough and slow enough, now 1024

void stream_open(int size, unsigned char *key) // size = 0 --> zero terminated string
{
  int c;
  HashCtx hash;

  clearcvar();
  streamt_pos = 0;

#ifdef STREAM_FASTER
  streamt_rekey_rounds = 0;
#endif

  if(size == 0)
    size = strlen(key);
  
  HashInit(&hash);
  HashUpdate(&hash, key, size);
  HashUpdate(&hash, (unsigned char *) &cvar, cvarsize + 1);
  inccvar();

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

#ifdef DEBUG19
    fprintf(stderr,"prev:");
    dumpline(stderr, sizeof(stream_key), stream_key);
#endif

    HashFinal(stream_key, &hash);

#ifdef DEBUG19
    fprintf(stderr,", next:");
    dumpline(stderr, sizeof(stream_key), stream_key);
    fprintf(stderr,"\n");
#endif 

    HashInit(&hash);
    HashUpdate(&hash, stream_key, sizeof(stream_key));
    HashUpdate(&hash, (unsigned char *) &cvar, cvarsize + 1);
    inccvar();
  }

  HashFinal(stream_key, &hash);

#ifdef DEBUG19
  fprintf(stderr,"stream_key:");
  dumpline(stderr, sizeof(stream_key), stream_key);
  fprintf(stderr,"\n");
#endif

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

#ifdef DEBUG20
  fflush(stderr);
  fprintf(stderr,"%s: stream_open():", procname);
  fprintf(stderr," key:");
  dumpbin(stderr, size, key);
  fprintf(stderr,", stream_key:");
  dumpline(stderr, sizeof(stream_key), stream_key);
  fprintf(stderr,"\n");
  fflush(stderr);
#endif
}

void fprintfcharacter(FILE *fp1, unsigned char *p)
{
  fputc(*p, fp1); // print first char
  if(*p > 0xbf) { // first char utf8
    p++;
    for(;;) { // print rest of the utf8 chars
      if(*p > 0xbf || // new utf8 character
	 *p < 0x80 || // ascii character
	 *p == '\0') // end of string
	break;
      fputc(*p, fp1);
      p++;
    }
  }
}

int getdigit(unsigned char *p)
{
  int digit;
  
  if(*p >= '0' && *p <= '9')
    digit = *p - '0';
  else if(*p >= 'a' && *p <= 'z')
    digit = (*p - 'a') + 10;
  else if(*p >= 'A' && *p <= 'Z')
    digit = (*p - 'A') + 10;
  else
    digit = -1; // not found, illegal

  return(digit);
}

//#define KILO 1000
#define KILO 1024

void readablelonglong(FILE *fp1, unsigned long long ll2)
{
  int c;
  unsigned long long multiplier, ll = ll2;
  
  // 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";

  c = 0;
  multiplier = 1;
  while(ll >= KILO) {
    ll /= KILO;
    multiplier *= KILO;
    c++;
  }

  if(ll * multiplier != ll2)
    fprintf(fp1,"~"); // approximately

  fprintf(fp1,"%llu%c", ll, units[c]);
}

#define DEBUG32

unsigned long long getlonglong(unsigned char *p2)
{
  int digit, base = 10;
  unsigned char *p = p2;
  unsigned long long totll, ll, prevll, multiplier;
  
  totll = 0;

  while(*p != '\0') { // works also: 1g100m & 1m20k and 1t1t etc...

    unsigned char *prevp = p;
    
    if(!strncmp("0x", p, 2)) {
      base = 16;
      p += 2;
      
    } else if(!strncmp("0d", p, 2)) {
      base = 10;
      p += 2;
      
    } else if(!strncmp("0o", p, 2)) {
      base = 8;
      p += 2;
      
    } else if(!strncmp("0b", p, 2)) {
      base = 2;
      p += 2;
      
    }
    
    ll = 0;
    while((digit = getdigit(p)) != -1 && digit < base) {
      ll = ll * base + digit;
      p++;

    }
    
    multiplier = 1;
    
    if(*p == 'k' || *p == 'K') {
      multiplier = KILO;
      p++;
      
    } else if(*p == 'm' || *p == 'M') {
      multiplier = (KILO * KILO);
      p++;
      
    } else if(*p == 'g' || *p == 'G') {
      multiplier = (KILO * KILO * KILO);
      p++;
      
    } else if(*p == 't' || *p == 'T') {
      multiplier = ((unsigned long long)KILO * KILO * KILO * KILO);
      p++;
      
    } else if(*p == 'p' || *p == 'P') {
      multiplier = ((unsigned long long)KILO * KILO * KILO * KILO * KILO);
      p++;
      
    } else if(*p == 'e' || *p == 'E') {
      multiplier = ((unsigned long long)KILO * KILO * KILO * KILO * KILO * KILO);
      p++;
      
    }
    
    prevll = ll;
    ll *= multiplier;
    if(ll / multiplier != prevll) {
      fflush(stdout);
      fprintf(stderr,"%s: multiply overflow", procname);
      fprintf(stderr,", string:'%s'", p2);
      fprintf(stderr,", digit:'");
      fprintfcharacter(stderr, p);
      fprintf(stderr,"'");
      fprintf(stderr,", value: %d", digit);
      fprintf(stderr,", base: %d(", base);
      readablelonglong(stderr, base);
      fprintf(stderr,")");
      fprintf(stderr,", ll: %llu(", prevll);
      readablelonglong(stderr, prevll);
      fprintf(stderr,")");
      fprintf(stderr,", multiplier: %llu(", multiplier);
      readablelonglong(stderr, multiplier);
      fprintf(stderr,")");
      fprintf(stderr,"\n");
      fflush(stderr);

    }
  
    if(*p == 'b' || *p == 'B') // remove last b (for bytes in 1tb)
      p++;
    
    totll += ll;

#ifdef DEBUG32
    fprintf(stderr,"string:'%s'", p2);
    fprintf(stderr,", base:%d(", base);
    readablelonglong(stderr, base);
    fprintf(stderr,")");
    fprintf(stderr,", multiplier:%llu(", multiplier);
    readablelonglong(stderr, multiplier);
    fprintf(stderr,")");
    fprintf(stderr,", prevll:%llu(", prevll);
    readablelonglong(stderr, prevll);
    fprintf(stderr,")");
    fprintf(stderr,", ll:%llu(", ll);
    readablelonglong(stderr, ll);
    fprintf(stderr,")");
    fprintf(stderr,", totll:%llu(", totll);
    readablelonglong(stderr, totll);
    fprintf(stderr,")");
    fprintf(stderr,"\n");
#endif
    
    if(prevp == p) // no progress
      break;
  }
  
  if(*p != '\0') {
    fflush(stdout);
    fprintf(stderr,"%s: illegal digit", procname);
    fprintf(stderr,", string:'%s'", p2);
    fprintf(stderr,", digit:'");
    fprintfcharacter(stderr, p);
    fprintf(stderr,"'");
    fprintf(stderr,", value: %d", digit);
    fprintf(stderr,", base: %d(", base);
    readablelonglong(stderr, base);
    fprintf(stderr,")");
    fprintf(stderr,"\n");
    fflush(stderr);

  }

  return(totll);
}

#define FILE_HASH 2 // on by default

#ifdef FILE_HASH

void hashfinal2string(unsigned char *hashstring, unsigned char *final)
{
  for(int c = 0; c < HashLen; c++) {
    sprintf(hashstring + 2 * c, "%02x",final[c]);
  }
}

#endif

unsigned char filename[128] = "";

void check_firstblock()
{
  int count;
  FILE *fp1;
  static unsigned char *block = NULL;
  static unsigned char *check_block = NULL;

  if(block == NULL) {
    if((block = malloc(BLOCKSIZE)) == NULL) {
      fprintf(stderr,"%s: check_firstblock(): cannot allocate memory for block\n", procname);
      fflush(stderr);
      exit(1);
    }
  }

  if((fp1 = fopen(filename, "r")) == NULL) {
    fprintf(stderr,"%s: check_firstblock(): cannot fopen file\n", procname);
    fflush(stderr);
    exit(1);
  }
  count = fread(block, 1, BLOCKSIZE, fp1);
  fclose(fp1);

  if(count < BLOCKSIZE) { // not a full block
    return;
  
  } else if(check_block == NULL) {
    if((check_block = malloc(BLOCKSIZE)) == NULL) {
      fprintf(stderr,"%s: check_firstblock(): cannot allocate memory for check_block\n", procname);
      fflush(stderr);
      exit(1);
    }
    memcpy(check_block, block, BLOCKSIZE);
    dump(stderr, "block1", BLOCKSIZE, block, 32);

  } else {
    
    if(memcmp(block, check_block, BLOCKSIZE)) {
      fprintf(stderr,"%s: check_firstblock(): first block changed\n", procname);
      dump(stderr, "block", BLOCKSIZE, block, 32);
      exit(1);
    }
  }
}

#define KILO 1024
#define aDEBUG65 2
#define DEBUG68

int main(int argc, char *argv[])
{
  int c, percentageline = 1, blocksize_set = 0, blocks_set = 0, filesize_set = 0;
  unsigned long long ll, lllim, blocks = BLOCKS, filesize = FILESIZE;
  unsigned int blocksize = BLOCKSIZE;
  int errcount = 0, pros, prevpros, help = 0, stdoutflag = 0;
  int writeflag = 1, verifyflag = 0, flushflag = 0, syncflag = 0, checkflag = 1, block1flag = 0;
  unsigned char *buffer, *buffer2;
  unsigned char key[128] = "kalakala";
  FILE *fp1, *fp2;

  procname = argv[0];

#define aDEBUG46
  
#ifdef DEBUG46
  stream_open(0, "⚀⚁⚂⚃⚄⚅"); // utf8  
  stream_open(0, "Hasta la vista, baby (Arnold Schwarzenegger, T2)"); // pass phrase
  stream_open(0, "abcdefghijklmnopqrstuvwxyz" // long password (key)
	      "abcdefghijklmnopqrstuvwxyz"
	      "abcdefghijklmnopqrstuvwxyz"
	      "abcdefghijklmnopqrstuvwxyz"
	      "abcdefghijklmnopqrstuvwxyz"); 
#endif

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

    if(c == argc - 1 && !strcmp(argv[c], "-")) {// last option hyphen
      stdoutflag = 1; // output to stdout
      continue;
    }
    
    if(!strncmp("-",argv[c], 1)) {

      if(!strncmp("-o", argv[c], 2)) { // output filename
	if(*(argv[c] + 2) != '\0') {
	  strncpy(filename, argv[c] + 2, sizeof(filename));
	} else if(c + 1 < argc) {
	  strncpy(filename, argv[c + 1], sizeof(filename));
	  c++;
	}

      } else if(!strcmp("--stdout", argv[c])) { // output stdout
	stdoutflag = 1; // output to stdout
	
      } else if(!strcmp("--help", argv[c]) ||
	 !strcmp("-?", argv[c]) ) {
	help = 1;

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

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

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

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

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

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

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

      } else if(!strncmp("--key", argv[c], 5)) {
	if(*(argv[c] + 5) != '\0') {
	  strcpy(key, argv[c] + 5);
	} else if(c + 1 < argc) {
	  strcpy(key, argv[c + 1]);
	  c++;
	}
	fprintf(stderr,"key:%s", key);
	fprintf(stderr,"\n");
	fflush(stderr);

      } else if(!strncmp("--filesize", argv[c], 10)) {
	if(*(argv[c] + 10) != '\0') {
	  filesize = getlonglong(argv[c] + 10);
	} else if(c + 1 < argc) {
	  filesize = getlonglong(argv[c + 1]);
	  c++;
	}
	filesize_set = 1;

      } else if(!strncmp("--blocksize", argv[c], 11)) {
	if(*(argv[c] + 11) != '\0') {
	  blocksize = getlonglong(argv[c] + 11);
	} else if(c + 1 < argc) {
	  blocksize = getlonglong(argv[c + 1]);
	  c++;
	}
	blocksize_set = 1;
	
      } else if(!strncmp("--blocks", argv[c], 8)) {
	if(*(argv[c] + 8) != '\0') {
	  blocks = getlonglong(argv[c] + 8);
	} else if(c + 1 < argc) {
	  blocks = getlonglong(argv[c + 1]);
	  c++;
	}
	blocks_set = 1;

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

      }
    }
  }

  if(!filesize_set)
    filesize = blocks * blocksize;
  else if(!blocks_set) {
    blocks = (filesize + blocksize - 1) / blocksize; // round up
    filesize = blocks * blocksize;
  } else if(!blocksize_set) {
    blocksize = (filesize + blocks - 1) / blocks; // round up
    filesize = blocks * blocksize;
  }

#define EXFAT_FIX 2 // on for now
  
#ifdef EXFAT_FIX

  if(blocksize > KILO * KILO) { // max "blocksize" 1m (exfat)
    blocksize = KILO * KILO;
    blocks = (filesize + blocksize - 1) / blocksize;
    filesize = blocks * blocksize;
  }

#endif
  
  if(filesize != blocks * blocksize ||
     blocks < 1 || blocksize < 1) {
    fflush(stdout);
    fprintf(stderr,"%s: mismatched parameters", procname);
    fprintf(stderr,", blocks:%llu(", blocks);
    readablelonglong(stderr, blocks);
    fprintf(stderr,")");
    fprintf(stderr,", blocksize:%u(", blocksize);
    readablelonglong(stderr, blocksize);
    fprintf(stderr,")");
    fprintf(stderr,", filesize:%llu(", filesize);
    readablelonglong(stderr, filesize);
    fprintf(stderr,")");
    fprintf(stderr,"\n");
    fflush(stderr);
    exit(1);
  }

  if(filesize / blocks != blocksize) { // overflow too
    fflush(stdout);
    fprintf(stderr,"%s: parameter overflow", procname);
    fprintf(stderr,", blocks:%llu(", blocks);
    readablelonglong(stderr, blocks);
    fprintf(stderr,")");
    fprintf(stderr,", blocksize:%u(", blocksize);
    readablelonglong(stderr, blocksize);
    fprintf(stderr,")");
    fprintf(stderr,", filesize:%llu(", filesize);
    readablelonglong(stderr, filesize);
    fprintf(stderr,")");
    fprintf(stderr,"\n");
    fflush(stderr);
    exit(1);
  }

#ifdef DEBUG68
  
  fprintf(stderr,"blocksize:%u(", blocksize);
  readablelonglong(stderr, blocksize);
  fprintf(stderr,")");
  fprintf(stderr,", blocks:%llu(", blocks);
  readablelonglong(stderr, blocks);
  fprintf(stderr,")");
  fprintf(stderr,", filesize:%llu(", filesize);
  readablelonglong(stderr, filesize);
  fprintf(stderr,")\n");
  fflush(stderr);

#endif
  
  // print help message if needed
  
  if(help) {
    fprintf(stderr,"%s", procname);
    fprintf(stderr," [-o filename]");
    fprintf(stderr," [--stdout]");
    fprintf(stderr," [--help]");
    fprintf(stderr," [--copyright]");
    fprintf(stderr," [--version]");
    fprintf(stderr,"\n\t[--filesize base-number-multiplier]");
    fprintf(stderr," [--blocksize base-number-multiplier]");
    fprintf(stderr,"\n\t[--blocks base-number-multiplier]");
    fprintf(stderr,"\n\t");
    fprintf(stderr,"[--write]");
    fprintf(stderr," [--verify]");
    fprintf(stderr," [--flush]");
    fprintf(stderr," [--sync]");
    fprintf(stderr," [--check]");
    fprintf(stderr," [--block1]");
    fprintf(stderr,"\n");
    fprintf(stderr,"Options:\n");
    fprintf(stderr,"\tWrite and check are on.\n");
    fprintf(stderr,"\tVerify, flush, sync and block1 are off.\n");
    fprintf(stderr,"\tOptions turn switch, so single --write means write off.");
    fprintf(stderr,"\n\n");
    fprintf(stderr,"Examples:\n");
    fprintf(stderr,"\n");
    fprintf(stderr,"Typical use:\n"); 
    fprintf(stderr,"\t$ %s --filesize 128g (for regular drives)\n", procname);
    fprintf(stderr,"\t$ %s --filesize 128g --flush --sync (for exfat)\n", procname);
    fprintf(stderr,"\n");
    fprintf(stderr,"Other use:\n");
    fprintf(stderr,"\t$ %s --filesize 1g\n", procname);
    fprintf(stderr,"\t$ %s --filesize 8g --verify\n", procname);
    fprintf(stderr,"\t$ %s --filesize 64g --verify --flush\n", procname);
    fprintf(stderr,"\t$ %s --filesize 128g --verify --flush\n", procname);
    fprintf(stderr,"\t$ %s --filesize 128g --verify --flush --sync --block1\n", procname);
    fprintf(stderr,"\t$ %s -o newressutest10.1.rnd --write --filesize 128g\n", procname);
    exit(1);
  } // end of if(help)

#define aDEBUG52
  
#ifdef DEBUG52
  unsigned char buffer128[128];
  stream_open(0, key);
  stream_bytes(sizeof(buffer128), buffer128);
  dump(stderr, "stream", sizeof(buffer128), buffer128, 32);
#endif

  
  if((buffer = malloc(blocksize)) == NULL) {
    fprintf(stderr,"%s: cannot allocate memory (buffer)\n", procname);
    exit(1);

  }

  if((buffer2 = malloc(blocksize)) == NULL) {
    fprintf(stderr,"%s: cannot allocate memory (buffer2)\n", procname);
    exit(1);

  }
  
  if(!stdoutflag && filename[0] == '\0') {

    // find first available filename,
    // or empty "slot"

    for(c = 1; c <= 99999; c++) {
      sprintf(filename, "newressutest10.%d.rnd", c);
      if((fp1 = fopen(filename, "r")) != NULL) {
	fclose(fp1);
	continue;
      }

#ifdef FILE_HASH
      unsigned char filename2[138];
      sprintf(filename2,"%s.sha256", filename);
      if((fp1 = fopen(filename2, "r")) != NULL) {
	fclose(fp1);
	continue;
      }
#endif

      break;
    }
  }

  if(stdoutflag) {
    if(isatty(STDOUT_FILENO)) { // 0=stdin, 1=stdout, 2=stderr
      percentageline = 0;
    }
  }
  
  if(stdoutflag)
    fprintf(stderr,"filename:-");
  else
    fprintf(stderr,"filename:%s",filename);
  fprintf(stderr,"\n");
  
#ifdef FILE_HASH
  
  unsigned char digest[HashLen];
  HashCtx hash;

  HashInit(&hash); // initialize hash

#endif

  if(writeflag) {
    if(stdoutflag == 1)
      fp1 = stdout;
    else if((fp1 = fopen(filename, "w")) == NULL) { // created datafile
      fprintf(stderr,"%s:", procname);
      fprintf(stderr," cannot open file");
      fprintf(stderr,", filename:%s", filename);
      fprintf(stderr,"\n");
      exit(1);
    }
      
    if(verifyflag) {
      if((fp2 = fopen(filename, "r")) == NULL) { // for verify file
	fprintf(stderr,"%s:", procname);
	fprintf(stderr," cannot open file");
	fprintf(stderr,", filename:%s", filename);
	fprintf(stderr,"\n");
	verifyflag = 0;
      }
    }
    
#ifdef EXFAT_FIX

    int mbinblocks = (1 * KILO * KILO) / blocksize;
    if(mbinblocks == 0)
      mbinblocks = 1;
    fprintf(stderr,"mbinblocks:%d\n", mbinblocks);

#endif
    
    prevpros = -1;

    stream_open(0, key);
    
    lllim = (long long) filesize / blocksize;

    for(ll = 0; ll < lllim; ll++) {
      stream_bytes(blocksize, buffer);
      
#ifdef DEBUG65   
      fprintf(stderr,"buffer    ");
      dumpline(stderr, 32, buffer);
      fprintf(stderr,", block:%llu", ll);
      fprintf(stderr,"\n");
      fflush(stderr);
#endif
      
#ifdef FILE_HASH
      HashUpdate(&hash, buffer, blocksize); // calculate hash
#endif
      
      if(fwrite(buffer, 1, blocksize, fp1) < blocksize) {
	fprintf(stderr,"%s:", procname);
	fprintf(stderr," cannot write file");
	fprintf(stderr,"\n");
	exit(1);
      }
      
      int flushed = 0;

#ifdef EXFAT_FIX
      
      if(syncflag && (ll % mbinblocks == 0)) { // sync every 1m
	if(!flushed && flushflag) {
	  fflush(fp1);
	  flushed = 1;
	}
	sync();
	//fsync(fileno(fp1));
      }

#else

      if(syncflag) { // sync every 1m
	if(!flushed && flushflag) {
	  fflush(fp1);
	  flushed = 1;
	}
	sync();
	//fsync(fileno(fp1));
      }

#endif
      
      if(verifyflag) {
	if(!flushed && flushflag) {
	  fflush(fp1);
	  flushed = 1;
	}
	if(fread(buffer2, 1, blocksize, fp2) < blocksize) {
	  fprintf(stderr,"%s:", procname);
	  fprintf(stderr," cannot read file");
	  fprintf(stderr,"\n");
	  exit(1);
	}	
#ifdef DEBUG65
	fprintf(stderr,"buffer2   ");
	dumpline(stderr, 32, buffer2);
	fprintf(stderr,", block:%llu", ll);
	fprintf(stderr,"\n");
	fflush(stderr);
#endif
	
	if(memcmp(buffer, buffer2, blocksize)) {
	  fflush(stderr);
	  fprintf(stderr,"%s: verify error", procname);
	  fprintf(stderr,", block:%llu", ll);
	  fprintf(stderr,", offset:%llu(0x%llx)", ll * BLOCKSIZE, ll * BLOCKSIZE);
	  fprintf(stderr,", is:");
	  dumpline(stderr, 16, buffer2);
	  fprintf(stderr,", should be:");
	  dumpline(stderr, 16, buffer);
	  fprintf(stderr,"\n");
	  fflush(stderr);
	  if(++errcount > 9)
	    exit(1);
	}	 
      } // end of if(verifyflag)
      
      if(block1flag)
	check_firstblock();
      
      if(percentageline) {
	pros = (int)((double) ll / lllim * 1000);
	if(prevpros != pros) {
	  fflush(stdout);
	  prevpros = pros;
	  fprintf(stderr,"\rDone:%d.%d%%", pros / 10, pros % 10);
	  fflush(stderr);
	}
      }
    } // end of for(ll = 0; ll < lllim; ll++)
      
#ifdef FILE_HASH
    HashFinal(digest, &hash); // calculate hash
#endif

    if(syncflag) {
      fflush(fp1);
      sync();
    }

    if(!stdoutflag)
      fclose(fp1);

    if(syncflag) {
      sync();
    }
    
    if(verifyflag)
      fclose(fp2);
    
    fprintf(stderr,"\rDone:100.0%%");
    fprintf(stderr," write");
    if(verifyflag)
      fprintf(stderr," and verify");
    fprintf(stderr," done.");
    fprintf(stderr,"\n");
    
#ifdef FILE_HASH
    unsigned char hashstring[2 * HashLen + 1];
    
    hashfinal2string(hashstring, digest);
    
    fprintf(stderr,"%s: hashed file", procname);
    
    if(filename[0] != '\0') {
      fprintf(stderr,", filename:%s", filename);
      
      unsigned char filename2[138];
      
      sprintf(filename2,"%s.sha256", filename);
      
      fprintf(stderr,", hashfilename:%s", filename2);
      
      if((fp1 = fopen(filename2, "w")) != NULL) {
	fprintf(fp1,"%s\n",hashstring);
	fclose(fp1);
      }
    }      
    fprintf(stderr,", sha256:%s\n", hashstring);
    
    if(filename[0] != '\0') {
      fprintf(stderr,"%s: checking sha256", procname);
      fprintf(stderr,", filename:%s\n", filename);
      unsigned char command[1024];
      sprintf(command,"sha256sum %s", filename);
      system(command);
    }
#endif
  } // if(writeflag)
  
  if(!stdoutflag && checkflag) {
    if((fp2 = fopen(filename, "r")) != NULL) { // for reread

      prevpros = -1;
      lllim = (long long) filesize / blocksize;

      stream_open(0, key);
    
      for(ll = 0; ll < lllim; ll++) {
	stream_bytes(blocksize, buffer);

	if(fread(buffer2, 1, blocksize, fp2) < blocksize) {
	  fprintf(stderr,"%s:", procname);
	  fprintf(stderr," cannot read file");
	  fprintf(stderr,"\n");
	  exit(1);
	}

#ifdef DEBUG65
	fflush(stdout);
	fprintf(stderr,"bbuffer   ");
	dumpline(stderr, 32, buffer);
	fprintf(stderr,", block:%llu", ll);
	fprintf(stderr,"\n");
	fprintf(stderr,"bbuffer2  ");
	dumpline(stderr, 32, buffer2);
	fprintf(stderr,", block:%llu", ll);
	fprintf(stderr,"\n");
	fflush(stderr);
#endif

	if(memcmp(buffer, buffer2, blocksize)) {
	  fflush(stdout);
	  fprintf(stderr,"%s: check error", procname);
	  fprintf(stderr,", block:%llu", ll);
	  fprintf(stderr,", offset:%llu(0x%llu)", ll * BLOCKSIZE, ll * BLOCKSIZE);
	  fprintf(stderr,", data:");
	  dumpline(stderr, 32, buffer2);
	  fprintf(stderr,", should be:");
	  dumpline(stderr, 32, buffer);
	  fprintf(stderr,"\n");
	  compareblocks(stderr, blocksize, buffer, buffer2);
	  fprintf(stderr,"\n");
	  fflush(stderr);
	  if(++errcount > 9)
	    exit(1);
	}

	if(block1flag)
	  check_firstblock();
	
	pros = (int)((double) ll / lllim * 1000);
	if(prevpros != pros) {
	  fflush(stdout);
	  prevpros = pros;
	  fprintf(stderr,"\rDone:%d.%d%%", pros / 10, pros % 10);
	  fflush(stderr);
	}	
      } // end of for(ll = 0; ll < lllim; ll++)
      
      fclose(fp2);
      fflush(stdout);
      fprintf(stderr,"\rDone:100.0%%"); 
      fprintf(stderr," read and compare");
      fprintf(stderr," done.");
      fprintf(stderr,"\n");
    } // end of if((fp2 = fopen(filename, "r")) != NULL)
  } // end of if(checkflag)
}

Myös joihinkin postin aiempiin testailuohjelmiin (newressutest9 ja newressutest) on lisätty flush ja sync, päivitän niiden versiot tässä postissa uudempiin.

Lisätty uusi testiohjelma newressutest12, joka kirjoittaa ja lukee kiinteänpituisia tietueita. Tässä ohjelman tuloste: Tässä tulostetaan 1048576 (1m) riviä jotka sisältävät 10 merkkiä tietoa. Rivit tulostetaan heksajonona, joten yksi tulostettava rivi on 10 * 2 + 1 = 21 merkkiä pitkä. Tiedostosta tulee 1m * 21 = 21m kokoinen. Tässä tiedosto on kirjoitettu ja varmistettu (vaihe 1) ja luettu ja tarkistettu (vaihe 2). Muista lisätä –flush ja –sync exfat levyille.

$ ./newressutest12 --bytes10 --lines1m --verify --flush
base:10, multiplier:1, prevll:10, ll:10, totll:10
base:10, multiplier:1048576, prevll:1, ll:1048576, totll:1048576
lines:1048576(1M), bytes:10(10B), linesize:21(21B), filesize:22020096(21M)
filename:newressutest12.108.rnd
mbinlines:49932
./newressutest12: stream_open(): key:kalakala, stream_key:448cd1f8dea2f75d469e1f265cbc2658426121577335348a11c5ec7c25c07bf1
Done:100.0% write and verify done.
./newressutest12: sample: hashing file, filename:newressutest12.108.rnd, hashfilename:newressutest12.108.rnd.sha256, sha256:a328ce8d2c4d98bb4ebf8ee3f8ac6d79e561722105789ceea68ae8837b974072
./newressutest12: sample: checking sha256, filename:newressutest12.108.rnd
a328ce8d2c4d98bb4ebf8ee3f8ac6d79e561722105789ceea68ae8837b974072  newressutest12.108.rnd
./newressutest12: stream_open(): key:kalakala, stream_key:448cd1f8dea2f75d469e1f265cbc2658426121577335348a11c5ec7c25c07bf1
Done:100.0% read and compare done.
$ 

Seuraavassa pätkä tulostettua tiedostoa: Tässä on tulostettu 10 merkin pituisia tietueita 10 kappaletta.

$ ./newressutest12 --bytes10 --lines10
base:10, multiplier:1, prevll:10, ll:10, totll:10
base:10, multiplier:1, prevll:10, ll:10, totll:10
lines:10(10B), bytes:10(10B), linesize:21(21B), filesize:210(210B)
filename:newressutest12.109.rnd
mbinlines:49932
./newressutest12: stream_open(): key:kalakala, stream_key:448cd1f8dea2f75d469e1f265cbc2658426121577335348a11c5ec7c25c07bf1
Done:100.0% write done.
./newressutest12: sample: hashing file, filename:newressutest12.109.rnd, hashfilename:newressutest12.109.rnd.sha256, sha256:d57ad8172aa54ba800829370a98a241a19777aa6759401e6ff8b5577fcae7729
./newressutest12: sample: checking sha256, filename:newressutest12.109.rnd
d57ad8172aa54ba800829370a98a241a19777aa6759401e6ff8b5577fcae7729  newressutest12.109.rnd
./newressutest12: stream_open(): key:kalakala, stream_key:448cd1f8dea2f75d469e1f265cbc2658426121577335348a11c5ec7c25c07bf1
Done:100.0% read and compare done.
$ cat newressutest12.109.rnd
c0d1bc849d6fbb01fe79
507f611bfb4b0f739177
f574c12575e932b44ae3
29c32527df32c830651d
eb4880d6183b3cf1b3c2
a8dd883ba01e469d575c
cd8686453516ecf6251f
82adacdc30d74b31560a
7ccc5e834de655424463
c1e0bf91dd1c2ff14998
$ 

Seuraavalla voi testata piipun (‘|’) toimivuutta: datasta lasketun tiivisteen pitää mätsätä $sha256sum komennon tulostaman tiivisteen kanssa (c255de-alkuinen merkkijono).

$ ./newressutest12 --bytes16 --lines40g --stdout | sha256sum
string:'16', base:10, multiplier:1, prevll:16, ll:16, totll:16
string:'40g', base:10, multiplier:1073741824, prevll:40, ll:42949672960, totll:42949672960
lines:42949672960(40G), bytes:16(16B), linesize:33(33B), filesize:1417339207680(~1T)
filename:
mbinlines:31775
./newressutest12: stream_open(): key:kalakala, stream_key:448cd1f8dea2f75d469e1f265cbc2658426121577335348a11c5ec7c25c07bf1
Done:100.0% write done.
./newressutest12: sample: hashing file, filename:, hashfilename:.sha256, sha256:c255de89c865c957bccc648ab7fbd9327eec338f36dfb96112ca3801fbbf2b71
c255de89c865c957bccc648ab7fbd9327eec338f36dfb96112ca3801fbbf2b71  -
$

Ja varsinainen ohjelma newressutest12.c:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "sha256.h"

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

#define FILESIZE 81 * 1024 * 1024
#define BINSIZE 40 // bytes per line
#define LINESIZE 81 // BINSIZE * 2 + 1
#define LINES 1 * 1024 * 1024

#define BLOCKSIZE 1024

int help = 0;

void dumpbin(FILE *fp1, int len, unsigned char *buf)
{
  int c;

  for(c = 0; c < len; c++)
    if(isprint(buf[c]) && buf[c]!= ' ')
      fputc(buf[c], stderr);
    else
      fprintf(stderr,"\\%02x", buf[c]);
}

void dumpline(FILE *fp1, int len, unsigned char *buf)
{
  int c;

  for(c = 0; c < len; c++)
    fprintf(fp1,"%02x", buf[c]);
}

void dump(FILE *fp1, unsigned char *header, int len, unsigned char *buf, int linelen)
{
  int c;

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

void compareblocks(FILE *fp1, int size, unsigned char *buffer, unsigned char *buffer2)
{
  int c, count;

  count = 0;
  for(c = 0; c < size; c++) {
    if(count > 31) {
      fprintf(fp1,"\n");
      count = 0;
    }
    if(buffer[c] == buffer2[c]) {
      fprintf(fp1," %02x", buffer[c]);
      count++;
    } else {
      fprintf(fp1," %02x/%02x", buffer[c], buffer2[c]);
      count +=2;
    }
  }
}

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

void inccvar()
{
  int c;

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

  if(cvarsize < c)
    cvarsize = c;
}

void clearcvar()
{
  int c;

  cvarsize = 0;

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

  for(c = 0; c < sizeof(cvar); c++)
    if(cvar[c] != 0)
      cvarsize = c;
}

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

#define aDEBUG8 2

static void stream_internalbytes(unsigned char *digest)
{
  HashCtx hash;

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

#ifdef DEBUG8
  fprintf(stderr,"stream_key:");
  dumpline(stderr, sizeof(stream_key), stream_key);
  fprintf(stderr,", cvar:");
  dumpline(stderr, cvarsize + 1, cvar);
  fprintf(stderr,", digest:");
  dumpline(stderr, HashLen, digest);
  fprintf(stderr,"\n");
#endif
}

#define aDEBUG11 2

static int streamt_pos = 0;

#define STREAM_FASTER 2 // default is on

#ifdef STREAM_FASTER
static int streamt_rekey_rounds = 0;
#define STREAM_REKEY_ROUNDS 1024 // 1 secure, more than one faster
#endif

void stream_bytes(int size, unsigned char *buffer)
{
  int c;
  static unsigned char streamt[HashLen]; // 256 bits
  
  for(c = 0; c < size; c++) {

    if(streamt_pos == 0) {
      stream_internalbytes(streamt); // read next data bytes

#ifdef DEBUG11
      dump(stderr, "streamt", sizeof(streamt), streamt, 32);
#endif

#ifndef STREAM_FASTER

      stream_internalbytes(stream_key); // change key

#ifdef DEBUG11
      dump(stderr, "rekey", 32, stream_key, 32);
#endif

#else
      if(++streamt_rekey_rounds >= STREAM_REKEY_ROUNDS) {
	stream_internalbytes(stream_key); // change key

#ifdef DEBUG11
	dump(stderr, "rekey", 32, stream_key, 32);
#endif

	streamt_rekey_rounds = 0;
      }
#endif
    } // end of if(streamt_pos == 0

    buffer[c] = streamt[streamt_pos];
    streamt_pos = (streamt_pos + 1) % sizeof(streamt);
  } // end of for(c = 0; c < size; c++)

#ifdef DEBUG11
  dump(stderr, "buffer", size, buffer, 32);
#endif
}

#define aDEBUG19 2
#define DEBUG20 2

#define STREAM_KEY_ROUNDS 1024 // should be fast enough and slow enough, now 1024

void stream_open(int size, unsigned char *key) // size = 0 --> zero terminated string
{
  int c;
  HashCtx hash;

  clearcvar();
  streamt_pos = 0;  

#ifdef STREAM_FASTER
  streamt_rekey_rounds = 0;
#endif
  
  if(size == 0)
    size = strlen(key);
  
  HashInit(&hash);
  HashUpdate(&hash, key, size);
  HashUpdate(&hash, (unsigned char *) &cvar, cvarsize + 1);
  inccvar();

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

#ifdef DEBUG19
    fprintf(stderr,"prev:");
    dumpline(stderr, sizeof(stream_key), stream_key);
#endif

    HashFinal(stream_key, &hash);

#ifdef DEBUG19
    fprintf(stderr,", next:");
    dumpline(stderr, sizeof(stream_key), stream_key);
    fprintf(stderr,"\n");
#endif 

    HashInit(&hash);
    HashUpdate(&hash, stream_key, sizeof(stream_key));
    HashUpdate(&hash, (unsigned char *) &cvar, cvarsize + 1);
    inccvar();
  }

  HashFinal(stream_key, &hash);

#ifdef DEBUG19
  fprintf(stderr,"stream_key:");
  dumpline(stderr, sizeof(stream_key), stream_key);
  fprintf(stderr,"\n");
#endif

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

#ifdef DEBUG20
  fflush(stdout);
  fprintf(stderr,"%s: stream_open():", procname);
  fprintf(stderr," key:");
  dumpbin(stderr, size, key);

  fprintf(stderr,", stream_key:");
  dumpline(stderr, sizeof(stream_key), stream_key);
  fprintf(stderr,"\n");
  fflush(stderr);
#endif
}

void fprintfcharacter(FILE *fp1, unsigned char *p)
{
  fputc(*p, fp1); // print first char
  if(*p > 0xbf) { // first char utf8
    p++;
    for(;;) { // print rest of the utf8 chars
      if(*p > 0xbf || // new utf8 character
	 *p < 0x80 || // ascii character
	 *p == '\0') // end of string
	break;
      fputc(*p, fp1);
      p++;
    }
  }
}

int getdigit(unsigned char *p)
{
  int digit;
  
  if(*p >= '0' && *p <= '9')
    digit = *p - '0';
  else if(*p >= 'a' && *p <= 'z')
    digit = (*p - 'a') + 10;
  else if(*p >= 'A' && *p <= 'Z')
    digit = (*p - 'A') + 10;
  else
    digit = -1; // not found, illegal

  return(digit);
}

//#define KILO 1000
#define KILO 1024

void readablelonglong(FILE *fp1, unsigned long long ll2)
{
  int c;
  unsigned long long multiplier, ll = ll2;
  
  // 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";

  c = 0;
  multiplier = 1;
  while(ll >= KILO) {
    ll /= KILO;
    multiplier *= KILO;
    c++;
  }
  
  if(ll * multiplier != ll2)
    fprintf(fp1,"~"); // approximately

  fprintf(fp1,"%llu%c", ll, units[c]);
}

#define DEBUG32 2

unsigned long long getlonglong(unsigned char *p2)
{
  int digit, base = 10;
  unsigned char *p = p2;
  unsigned long long totll, ll, prevll, multiplier;
  
  totll = 0;

  while(*p != '\0') { // works also: 1g100m & 1m20k and 1t1t etc...

    unsigned char *prevp = p;
    
    if(!strncmp("0x", p, 2)) {
      base = 16;
      p += 2;
      
    } else if(!strncmp("0d", p, 2)) {
      base = 10;
      p += 2;
      
    } else if(!strncmp("0o", p, 2)) {
      base = 8;
      p += 2;
      
    } else if(!strncmp("0b", p, 2)) {
      base = 2;
      p += 2;
      
    }
    
    ll = 0;
    while((digit = getdigit(p)) != -1 && digit < base) {
      ll = ll * base + digit;
      p++;

    }
    
    multiplier = 1;
    
    if(*p == 'k' || *p == 'K') {
      multiplier = KILO;
      p++;
      
    } else if(*p == 'm' || *p == 'M') {
      multiplier = (KILO * KILO);
      p++;
      
    } else if(*p == 'g' || *p == 'G') {
      multiplier = (KILO * KILO * KILO);
      p++;
      
    } else if(*p == 't' || *p == 'T') {
      multiplier = ((unsigned long long)KILO * KILO * KILO * KILO);
      p++;
      
    } else if(*p == 'p' || *p == 'P') {
      multiplier = ((unsigned long long)KILO * KILO * KILO * KILO * KILO);
      p++;
      
    } else if(*p == 'e' || *p == 'E') {
      multiplier = ((unsigned long long)KILO * KILO * KILO * KILO * KILO * KILO);
      p++;
      
    }
    
    prevll = ll;
    ll *= multiplier;
    if(ll / multiplier != prevll) {
      fflush(stdout);
      fprintf(stderr,"%s: multiply overflow", procname);
      fprintf(stderr,", string:'%s'", p2);
      fprintf(stderr,", digit:'");
      fprintfcharacter(stderr, p);
      fprintf(stderr,"'");
      fprintf(stderr,", value:%d", digit);
      fprintf(stderr,", base:%d(", base);
      readablelonglong(stderr, base);
      fprintf(stderr,")");
      fprintf(stderr,", prevll:%llu(", prevll);
      readablelonglong(stderr, prevll);
      fprintf(stderr,")");
      fprintf(stderr,", ll:%llu(", ll);
      readablelonglong(stderr, ll);
      fprintf(stderr,")");
      fprintf(stderr,", multiplier:%llu(", multiplier);
      readablelonglong(stderr, multiplier);
      fprintf(stderr,")");
      fprintf(stderr,"\n");
      fflush(stderr);
      exit(1);
    }

    if(*p == 'b' || *p == 'B') // remove last b (for bytes in 1tb)
      p++;
    
    totll += ll;

#ifdef DEBUG32
    fprintf(stderr,"string:'%s'", p2);
    fprintf(stderr,", base:%d(", base);
    readablelonglong(stderr, base);
    fprintf(stderr,")");
    fprintf(stderr,", multiplier:%llu(", multiplier);
    readablelonglong(stderr, multiplier);
    fprintf(stderr,")");
    fprintf(stderr,", prevll:%llu(", prevll);
    readablelonglong(stderr, prevll);
    fprintf(stderr,")");
    fprintf(stderr,", ll:%llu(", ll);
    readablelonglong(stderr, ll);
    fprintf(stderr,")");
    fprintf(stderr,", totll:%llu(", totll);
    readablelonglong(stderr, totll);
    fprintf(stderr,")");
    fprintf(stderr,"\n");
#endif
    
    if(prevp == p) // no progress
      break;
  }
  
  if(*p != '\0') {
    fflush(stdout);
    fprintf(stderr,"%s: illegal digit", procname);
    fprintf(stderr,", string:'%s'", p2);
    fprintf(stderr,", digit:'");
    fprintfcharacter(stderr, p);
    fprintf(stderr,"'");
    fprintf(stderr,", value:%d", digit);
    fprintf(stderr,", base:%d(", base);
    readablelonglong(stderr, base);
    fprintf(stderr,")");
    fprintf(stderr,"\n");
    fflush(stderr);
    exit(1);
  }

  return(totll);
}

#define FILE_HASH 2 // on by default

#ifdef FILE_HASH

void hashfinal2string(unsigned char *hashstring, unsigned char *final)
{
  for(int c = 0; c < HashLen; c++) {
    sprintf(hashstring + 2 * c, "%02x", final[c]);
  }
}

#endif

unsigned char filename[128] = "";

void check_firstblock()
{
  int count;
  FILE *fp1;
  static unsigned char *block = NULL;
  static unsigned char *check_block = NULL;

  if(block == NULL) {
    if((block = malloc(BLOCKSIZE)) == NULL) {
      fprintf(stderr,"%s: check_firstblock(): cannot allocate memory for block\n", procname);
      fflush(stderr);
      exit(1);
    }
  }

  if((fp1 = fopen(filename, "r")) == NULL) {
    fprintf(stderr,"%s: check_firstblock(): cannot fopen file\n", procname);
    fflush(stderr);
    exit(1);
  }
  count = fread(block, 1, BLOCKSIZE, fp1);
  fclose(fp1);

  if(count < BLOCKSIZE) { // not a full block
    return;

  } else if(check_block == NULL) { // read and save original block
    if((check_block = malloc(BLOCKSIZE)) == NULL) {
      fprintf(stderr,"%s: check_firstblock(): cannot allocate memory for check_block\n", procname);
      fflush(stderr);
      exit(1);
    }
    memcpy(check_block, block, BLOCKSIZE);
    dump(stderr, "block1", BLOCKSIZE, block, 32);
    
  } else { // compare to original block
    if(memcmp(block, check_block, BLOCKSIZE)) {
      fprintf(stderr,"%s: check_firstblock(): first block changed\n", procname);
      dump(stderr, "block", BLOCKSIZE, block, 32);
      exit(1);
    }
  }
}

#define aDEBUG65 2
#define DEBUG69 2

int main(int argc, char *argv[])
{
  int c, percentageline = 1;
  int filesize_set = 0, binsize_set = 0, linesize_set = 0, lines_set = 0;
  unsigned long long ll, filesize = FILESIZE, lines = LINES;
  unsigned int binsize = BINSIZE, linesize = LINESIZE;
  int errcount = 0, pros, prevpros;
  int writeflag = 1, stdoutflag = 0, verifyflag = 0, flushflag = 0;
  int syncflag = 0, checkflag = 1, block1flag = 0;
  unsigned char *binbuffer, *buffer, *buffer2;
  unsigned char key[128] = "kalakala";
  FILE *fp1, *fp2;

  procname = argv[0];

#define aDEBUG46 2

#ifdef DEBUG46
  ll = 1;
  for(;;) {
    fprintf(stderr,"ll:%llu", ll);
    fprintf(stderr,", readablelonglong:");
    readablelonglong(stderr, ll);
    fprintf(stderr,"\n");
    unsigned long long prevll = ll;
    ll *= 2;
    if(ll / 2 != prevll)
      break;
  }
#endif

#define aDEBUG48 2

#ifdef DEBUG48
  stream_open(0, "⚀⚁⚂⚃⚄⚅"); // utf8  
  stream_open(0, "Hasta la vista, baby (Arnold Schwarzenegger, T2)"); // pass phrase
  stream_open(0, "abcdefghijklmnopqrstuvwxyz" // long password (key)
	      "abcdefghijklmnopqrstuvwxyz"
	      "abcdefghijklmnopqrstuvwxyz"
	      "abcdefghijklmnopqrstuvwxyz"
	      "abcdefghijklmnopqrstuvwxyz"); 
#endif

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

    if(c == argc - 1 && !strcmp(argv[c], "-")) { // last option hyphen
      stdoutflag = 1; // output to stdout
      continue;
    }
    
    if(!strncmp("-",argv[c], 1)) { // option starting with hyphen

      if(!strncmp("-o", argv[c], 2)) { // output filename
	if(*(argv[c] + 2) != '\0') {
	  strncpy(filename, argv[c] + 2, sizeof(filename));
	} else if(c + 1 < argc) {
	  strncpy(filename, argv[c + 1], sizeof(filename));
	  c++;
	}

      } else if(!strcmp("--stdout", argv[c])) { // output stdout
	stdoutflag = 1; // output to stdout
	
      } else if(!strcmp("--help", argv[c]) ||
	 !strcmp("-?", argv[c]) ) {
	help = 1;

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

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

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

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

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

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

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

      } else if(!strncmp("--key", argv[c], 5)) {
	if(*(argv[c] + 5) != '\0') {
	  strcpy(key, argv[c] + 5);
	} else if(c + 1 < argc) {
	  strcpy(key, argv[c +1]);
	}
	fprintf(stderr,"key:%s", key);
	fprintf(stderr,"\n");
	fflush(stderr);

      } else if(!strncmp("--filesize", argv[c], 10)) {
	if(*(argv[c] + 10) != '\0') {
	  filesize = getlonglong(argv[c] + 10);
	} else if(c + 1 < argc) {
	  filesize = getlonglong(argv[c + 1]);
	  c++;
	}
	filesize_set = 1;

      } else if(!strncmp("--linesize", argv[c], 10)) {
	if(*(argv[c] + 10) != '\0') {
	  linesize = getlonglong(argv[c] + 10);
	} else if(c + 1 < argc) {
	  linesize = getlonglong(argv[c + 1]);
	  c++;
	}
	linesize_set = 1;
	
      } else if(!strncmp("--lines", argv[c], 7)) {
	if(*(argv[c] + 7) != '\0') {
	  lines = getlonglong(argv[c] + 7);
	} else if(c + 1 < argc) {
	  lines = getlonglong(argv[c + 1]);
	  c++;
	}
	lines_set = 1;

      } else if(!strncmp("--binsize", argv[c], 9)) {
	if(*(argv[c] + 9) != '\0') {
	  binsize = getlonglong(argv[c] + 9);
	} else if(c + 1 < argc) {
	  binsize = getlonglong(argv[c + 1]);
	  c++;
	}
	binsize_set = 1;

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

      }
    } // end of if(!strncmp("-",argv[c], 1
  } // end of for(c = 1; c < argc; c++

  if(!linesize_set)
    linesize = binsize * 2 + 1;
  else if(!binsize_set)
    binsize = (linesize - 1) / 2;

  if(linesize != binsize * 2 + 1 ||
     linesize < 3 || binsize < 1) {
    fflush(stdout);
    fprintf(stderr,"%s: mismatched parameters", procname);
    fprintf(stderr,", linesize:%u(", linesize);
    readablelonglong(stderr, linesize);
    fprintf(stderr,")");
    fprintf(stderr,", binsize:%u(", binsize);
    readablelonglong(stderr, binsize);
    fprintf(stderr,")");
    fprintf(stderr,"\n");
    fflush(stderr);
    exit(1);
  }
    
  if(!filesize_set)
    filesize = lines * linesize;
  else if(!lines_set) {
    lines = (filesize + linesize - 1) / linesize; // round up
    filesize = lines * linesize;
  } else if(!linesize_set) {
    linesize = (filesize + lines - 1) / lines; // round up
    filesize = lines * linesize;
  }

#define aEXFAT_FIX 2

#ifdef EXFAT_FIX
  
  if(linesize >= KILO * KILO) { // max linesize 1m (exfat)
    linesize = KILO * KILO - 1;
    binsize = (linesize - 1) / 2;
    lines = (filesize + linesize - 1) / linesize;
    filesize = lines * linesize;
  }

#endif
  
  if(filesize != lines * linesize ||
     lines < 1 || linesize < 3) {
    fflush(stdout);
    fprintf(stderr,"%s: mismatched parameters", procname);
    fprintf(stderr,", lines:%llu(", lines);
    readablelonglong(stderr, lines);
    fprintf(stderr,")");
    fprintf(stderr,", linesize:%u(", linesize);
    readablelonglong(stderr, linesize);
    fprintf(stderr,")");
    fprintf(stderr,", filesize:%llu(", filesize);
    readablelonglong(stderr, filesize);
    fprintf(stderr,")");
    fprintf(stderr,"\n");
    fflush(stderr);
    exit(1);
  }

  if(filesize / lines != linesize) { // check for overflow too
    fflush(stdout);
    fprintf(stderr,"%s: parameter overflow", procname);
    fprintf(stderr,", lines:%llu(", lines);
    readablelonglong(stderr, lines);
    fprintf(stderr,")");
    fprintf(stderr,", linesize:%u(", linesize);
    readablelonglong(stderr, linesize);
    fprintf(stderr,")");
    fprintf(stderr,", filesize:%llu(", filesize);
    readablelonglong(stderr, filesize);
    fprintf(stderr,")");
    fprintf(stderr,"\n");
    fflush(stderr);
    exit(1);
  }

  // print help message if needed
  
  if(help) {
    fprintf(stderr,"%s", procname);
    fprintf(stderr," [--help]");
    fprintf(stderr," [--copyright]");
    fprintf(stderr," [--version]");
    fprintf(stderr," [-o filename]");
    fprintf(stderr," [--stdout]");
    fprintf(stderr,"\n\t[--filesize base-number-multiplier]"); 
    fprintf(stderr," [--linesize base-number-multiplier]");
    fprintf(stderr,"\n\t[--lines base-number-multiplier]");
    fprintf(stderr," [--binsize base-number-multiplier]");
    fprintf(stderr,"\n\t[--write]");
    fprintf(stderr," [--verify]");
    fprintf(stderr," [--flush]");
    fprintf(stderr," [--sync]");
    fprintf(stderr," [--check]");
    fprintf(stderr," [--block1]");
    fprintf(stderr,"\n");
    fprintf(stderr,"Options:\n");
    fprintf(stderr,"\tWrite and check are on.\n");
    fprintf(stderr,"\tVerify, flush, sync and block1 are off.\n");
    fprintf(stderr,"\tOptions turn switch, so single --write means write off.");
    fprintf(stderr,"\n\n");
    fprintf(stderr,"Examples:\n");
    fprintf(stderr,"\n");
    fprintf(stderr,"Typical use:\n"); 
    fprintf(stderr,"\t$ %s --lines 128m (for regular drives)\n", procname);
    fprintf(stderr,"\t$ %s --lines 128m --flush --sync (for exfat)\n", procname);
    fprintf(stderr,"\n");
    fprintf(stderr,"Other use:\n");
    fprintf(stderr,"\t$ %s --lines 1m\n", procname);
    fprintf(stderr,"\t$ %s --lines 8m --verify\n", procname);
    fprintf(stderr,"\t$ %s --lines 64m --verify --flush\n", procname);
    fprintf(stderr,"\t$ %s --lines 128m --verify --flush\n", procname);
    fprintf(stderr,"\t$ %s --lines 128m --verify --flush --sync --block1\n", procname);
    fprintf(stderr,"\t$ %s newressutest12.1.rnd --write --lines 128m\n", procname);
    exit(1);
  } // end of if(help)

#ifdef DEBUG69

  // print filesize parameters
  
  fprintf(stderr,"lines:%llu(", lines);
  readablelonglong(stderr, lines);
  fprintf(stderr,")");
  fprintf(stderr,", binsize:%u(", binsize);
  readablelonglong(stderr, binsize);
  fprintf(stderr,")");
  fprintf(stderr,", linesize:%u(", linesize);
  readablelonglong(stderr, linesize);
  fprintf(stderr,")");
  fprintf(stderr,", filesize:%llu(", filesize);
  readablelonglong(stderr, filesize);
  fprintf(stderr,")\n");
  fflush(stderr);

#endif
    
  
  unsigned int dumplinelength = 32;
  if(dumplinelength > linesize)
    dumplinelength = linesize;

  if((binbuffer = malloc(binsize)) == NULL) {
    fprintf(stderr,"%s: cannot allocate memory (binbuffer)\n", procname);
    exit(1);
  }

  if((buffer = malloc(linesize)) == NULL) {
    fprintf(stderr,"%s: cannot allocate memory (buffer)\n", procname);
    exit(1);
  }

  if((buffer2 = malloc(linesize)) == NULL) {
    fprintf(stderr,"%s: cannot allocate memory (buffer2)\n", procname);
    exit(1);
  }

#define aDEBUG52
  
#ifdef DEBUG52
  unsigned char buffer128[128];
  stream_open(0, key);
  stream_bytes(sizeof(buffer128), buffer128);
  dump(stderr, "stream", sizeof(buffer128), buffer128, 32);
#endif
  
  if(!stdoutflag && filename[0] == '\0') {

    // find first available filename,
    // or empty "slot"

    for(c = 1; c <= 99999; c++) {
      sprintf(filename, "newressutest12.%d.rnd", c);
      if((fp1 = fopen(filename, "r")) != NULL) {
	fclose(fp1);
	continue;
      }

#ifdef FILE_HASH
      unsigned char filename2[138];
      sprintf(filename2,"%s.sha256", filename);
      if((fp1 = fopen(filename2, "r")) != NULL) {
	fclose(fp1);
	continue;
      }
#endif

      break;
    }
  }

  if(stdoutflag) {
    if(isatty(STDOUT_FILENO)) { // 0=stdin, 1=stdout, 2=stderr
      percentageline = 0;
    }
  }
  
  if(stdoutflag)
    fprintf(stderr,"filename:-");
  else
    fprintf(stderr,"filename:%s",filename);
  fprintf(stderr,"\n");
  
#ifdef FILE_HASH
  
  unsigned char digest[HashLen];
  HashCtx hash;

  HashInit(&hash); // initialize hash

#endif

  if(writeflag) {
    if(stdoutflag == 1)
      fp1 = stdout;
    else if((fp1 = fopen(filename, "w")) == NULL) { // created datafile
      fprintf(stderr,"%s:", procname);
      fprintf(stderr," cannot open file");
      fprintf(stderr,", filename:%s", filename);
      fprintf(stderr,"\n");
      exit(1);
    }

    if(verifyflag) {
      if((fp2 = fopen(filename, "r")) == NULL) { // for verify file
	fprintf(stderr,"%s:", procname);
	fprintf(stderr," cannot open file");
	fprintf(stderr,", filename:%s", filename);
	fprintf(stderr,"\n");
	verifyflag = 0;
      }
    }
    
#ifdef EXFAT_FIX

    int mbinlines = (KILO * KILO) / linesize;
    if(mbinlines == 0)
      mbinlines = 1;
    fprintf(stderr,"mbinlines:%d\n", mbinlines);

#endif
    
    prevpros = -1;

    stream_open(0, key);
    
    for(ll = 0; ll < lines; ll++) {
      stream_bytes(binsize, binbuffer);
      
      for(c = 0; c < binsize; c++)
	sprintf(buffer + c * 2, "%02x", binbuffer[c]);
      buffer[linesize - 1] = '\n';
      
#ifdef DEBUG65   
      fprintf(stderr,"buffer    ");
      dumpline(stderr, dumplinelength, buffer);
      fprintf(stderr,", block:%llu", ll);
      fprintf(stderr,"\n");
      fflush(stderr);
#endif
      
#ifdef FILE_HASH
      HashUpdate(&hash, buffer, linesize); // calculate hash
#endif
      
      if(fwrite(buffer, 1, linesize, fp1) < linesize) {
	fprintf(stderr,"%s:", procname);
	fprintf(stderr," cannot write file");
	fprintf(stderr,"\n");
	exit(1);
      }
      
      int flushed = 0;
      
#ifdef EXFAT_FIX
      if(syncflag && (ll % mbinlines == 0)) { // sync every 1m (exfat)
	if(!flushed && flushflag) {
	  fflush(fp1);
	  flushed = 1;
	}
	sync();
	//fsync(fileno(fp1));
      }
#else
      if(flushflag) {
	fflush(fp1);
	flushed = 1;
      }
      if(syncflag) {
	sync();
      }
#endif
      if(verifyflag) {
	if(!flushed && flushflag) {
	  fflush(fp1);
	  flushed = 1;
	}
	if(fread(buffer2, 1, linesize, fp2) < linesize) {
	  fprintf(stderr,"%s:", procname);
	  fprintf(stderr," cannot read file");
	  fprintf(stderr,"\n");
	  exit(1);
	}
	
#ifdef DEBUG65
	fprintf(stderr,"buffer2   ");
	dumpline(stderr, dumplinelength, buffer2);
	fprintf(stderr,", block:%llu", ll);
	fprintf(stderr,"\n");
	fflush(stderr);
#endif
	
	if(memcmp(buffer, buffer2, linesize)) {
	  fflush(stdout);
	  fprintf(stderr,"%s: verify error", procname);
	  fprintf(stderr,", lines:%llu", ll);
	  fprintf(stderr,", offset:%llu(0x%llx)", ll * LINESIZE, ll * LINESIZE);
	  fprintf(stderr,", is:");
	  dumpline(stderr, dumplinelength, buffer2);
	  fprintf(stderr,", should be:");
	  dumpline(stderr, dumplinelength, buffer);
	  fprintf(stderr,"\n");
	  fflush(stderr);
	  if(++errcount > 9)
	    exit(1);
	}	 
      } // end of if(verifyflag)
      
      if(block1flag)
	check_firstblock();

      if(percentageline) {
	pros = (int)((double) ll / lines * 1000);
	if(prevpros != pros) {
	  fflush(stdout);
	  prevpros = pros;
	  fprintf(stderr,"\rDone:%d.%d%%", pros / 10, pros % 10);
	  fflush(stderr);
	}
      }
    } // end of for(ll = 0; ll < lines; ll++)
    
#ifdef FILE_HASH
    HashFinal(digest, &hash); // calculate hash
#endif
    
    fprintf(stderr,"\rDone:100.0%%");
    fprintf(stderr," write");
    if(verifyflag)
      fprintf(stderr," and verify");
    fprintf(stderr," done.");
    fprintf(stderr,"\n");
    fflush(stderr);
    
    fflush(fp1);
    sync();
    if(!stdoutflag)
      fclose(fp1);
    
    if(verifyflag)
      fclose(fp2);
    
#ifdef FILE_HASH
    unsigned char hashstring[2 * HashLen + 1];
    
    hashfinal2string(hashstring, digest);
    
    fprintf(stderr,"%s: hashed file", procname);

    if(filename[0] != '\0') {
      fprintf(stderr,", filename:%s", filename);

      unsigned char filename2[138];

      sprintf(filename2,"%s.sha256", filename);
    
      fprintf(stderr,", hashfilename:%s", filename2);

      if((fp1 = fopen(filename2, "w")) != NULL) {
	fprintf(fp1,"%s\n", hashstring);
	fclose(fp1);
      }
    }
    fprintf(stderr,", sha256:%s\n", hashstring);

    if(filename[0] != '\0') {
      fprintf(stderr,"%s: checking sha256", procname);
      fprintf(stderr,", filename:%s\n", filename);
      unsigned char command[1024];
      sprintf(command,"sha256sum %s", filename);
      system(command);
    }
#endif
  } // if(writeflag)
  
  if(!stdoutflag && checkflag) {
    if((fp2 = fopen(filename, "r")) != NULL) { // for reread

      prevpros = -1;

      stream_open(0, key);
    
      for(ll = 0; ll < lines; ll++) {
	stream_bytes(binsize, binbuffer);
	
	for(c = 0; c < binsize; c++)
	  sprintf(buffer + c * 2, "%02x", binbuffer[c]);
	buffer[linesize - 1] = '\n';
	
	if(fread(buffer2, 1, linesize, fp2) < linesize) {
	  fprintf(stderr,"%s:", procname);
	  fprintf(stderr," cannot read file");
	  fprintf(stderr,"\n");
	  exit(1);
	}

#ifdef DEBUG65
	fflush(stderr);
	fprintf(stderr,"bbuffer   ");
	dumpline(stderr, dumplinelength, buffer);
	fprintf(stderr,", line:%llu", ll);
	fprintf(stderr,"\n");
	fprintf(stderr,"bbuffer2  ");
	dumpline(stderr, dumplinelength, buffer2);
	fprintf(stderr,", line:%llu", ll);
	fprintf(stderr,"\n");
	fflush(stderr);
#endif

	if(memcmp(buffer, buffer2, linesize)) {
	  fflush(stdout);
	  fprintf(stderr,"%s: check error", procname);
	  fprintf(stderr,", line:%llu", ll);
	  fprintf(stderr,", offset:%llu(0x%llx)", ll * linesize, ll * linesize);
	  fprintf(stderr,", data:");
	  dumpline(stderr, dumplinelength, buffer2);
	  fprintf(stderr,", should be:");
	  dumpline(stderr, dumplinelength, buffer);
	  fprintf(stderr,"\n");
	  compareblocks(stderr, linesize, buffer, buffer2);
	  fprintf(stderr,"\n");
	  fflush(stderr);
	  if(++errcount > 9)
	    exit(1);
	}

	if(block1flag)
	  check_firstblock();
	
	pros = (int)((double) ll / lines * 1000);
	if(prevpros != pros) {
	  fflush(stdout);
	  prevpros = pros;
	  fprintf(stderr,"\rDone:%d.%d%%", pros / 10, pros % 10);
	  fflush(stderr);
	}
      } // end of for(ll = 0; ll < lllim; ll++)
      
      fclose(fp2);
      fflush(stdout);
      fprintf(stderr,"\rDone:100.0%%"); 
      fprintf(stderr," read and compare");
      fprintf(stderr," done.");
      fprintf(stderr,"\n");
      fflush(stderr);
    } // end of if((fp2 = fopen(filename, "r")) != NULL)
  } // end of if(checkflag)
}

Lisätty save_path, exists_path ja set_path, joiden käytöstä voit katsoa esimerkin DEBUG72 koodista. Tässä kuitenkin vielä koodit niille:

#define DEBUG25

void save_path(int path_length, unsigned char *path)
{
  if((getcwd(path, path_length)) == NULL) {
    fprintf(stdout,"%s: save_path(): cannot get working directory (getcwd)\n",
	    procname);
  }
#ifdef DEBUG25
  fprintf(stdout,"%s: save_path(): saved current path (getcwd), path = %s\n",
	  procname,path);
#endif
}

int exists_path(unsigned char *path)
{
  FILE *fp1;
  
  if((fp1 = fopen(path,"r")) != NULL) {
    fclose(fp1);
    return(1);
  } else
    return(0);
}

void set_path(unsigned char *path)
{
 if(chdir(path) != 0) {
    fprintf(stdout,"%s: set_path(): cannot change directory (chdir())\n",
	    procname);
  }
#ifdef DEBUG25
  fprintf(stdout,"%s: set_path(): set path (chdir()), path = %s\n",
	  procname,path);
#endif
}

Ja vielä esimerkki käytöstä:

#ifdef DEBUG72
  if(flagint) {
    unsigned char savepath[128] = "./";
    //fprintf(stderr,"%s: Running integrity test(s)...", procname);
    fflush(stderr);
    save_path(sizeof(savepath), savepath);

    if(!strncmp(procname, "/bin/newressu", 13) && exists_path("/var/ressu")) {
      set_path("/var/ressu");
      fprintf(stderr,"%s: procname '/bin/newressu', path exists '%s', path set\n",
	      procname, "/var/ressu");
      fflush(stderr);

    } else if(exists_path("/var/ressu")) {
      set_path("/var/ressu");
      fprintf(stderr,"%s: path exists '%s', set\n",
	      procname,"/var/ressu");
      fflush(stderr);

    } else if(exists_path("/var/newressu")) {
      set_path("/var/newressu");
      fprintf(stderr,"%s: path exists '%s', set\n",
	      procname,"/var/newressu");
      fflush(stderr);

    } else if(exists_path("dkala")) {
      set_path("dkala");
      fprintf(stderr,"%s: path exists '%s', set\n",
	      procname, "./dkala");
      fflush(stderr);

    } else if(exists_path(".ressu")) {
      set_path(".ressu");
      fprintf(stderr,"%s: path exists '%s', set\n",
	      procname, ".ressu");
      fflush(stderr);

    } else if(exists_path("./")) {
      set_path("./");
      fprintf(stderr,"%s: path exists '%s', set\n",
	      procname, "./");
      fflush(stderr);

    } else {
      set_path("./");
      fprintf(stderr,"%s: path set '%s'\n",
	      procname, "./");
      fflush(stderr);

    }

    unsigned char command[160], date[16], filename[64];
    command_readline("date +%Y%m%d%H", sizeof(date), date);
    //command_readline("date +%Y%m%d%H%M", sizeof(date), date);
    fprintf(stdout,"readline date '%s'\n",date);
    sprintf(filename,"newressutest14.rnd.%s",date);
    if(!exists_path(filename)) {
      sprintf(command,"./newressutest14.sh >%s",filename);
      system(command);
    }
    //fprintf(stderr," done.\n");
    fflush(stderr);

    set_path(savepath);
  }
#endif

Aloitin uuden ohjelman, jolla voi hakea terttu (tai sakki) muotoisesta tiedostosta haluamillaan avaimilla. Tätä voi käyttää esimerkiksi palvelimen lokin valikoituun tulostukseen.

Lisätty uuteen terttuutil1 ohjelmaan pätkä, joka parsii komentomerkkijonon, ja muodostaa siitä terttu-yhteensopivan kyselyn: esimerkkejä input kyselystä ‘a,b’, ‘a=1,b’ ‘a,b,c’ näistä tehdään terttu muotoinen kysely ‘a’, ‘b’ tai ‘a’ = “1”, ‘b’ tai ‘a’, ‘b’, ‘c’ jne.

#define aDEBUG59 2

int db6_parsequery(int querylen, unsigned char *query, unsigned char *command)
{
  unsigned char *token, *p, *q;
  int mode = 0, copymode = 0, count, valuetoo, exitcount = 0;
  
  p = command;
  q = query;
  mode = 20;
  count = 0;
  
  while(*p != '\0') {

    unsigned char *prevp = NULL;
    unsigned char *prevq = NULL;    
    
    prevp = p;
    prevq = q;

    if(count < querylen)
      *q = '\0';

    switch(mode) {

    case 10:

      token = " ";
      if(count + strlen(token) < querylen) {
	strcat (q, token);
	q += strlen(q);
      }
      count += strlen(token);
      mode = 20;
      
    case 20:

      token = "'";
      if(count + strlen(token) < querylen) {
	strcat (q, token);
	q += strlen(q);
      }	
      count += strlen(token);

      copymode = 0; // 0 = isalpha, 1 = within quotes
      db5_skipwhite(&p);
      if(*p == '\'') { // skip possible first quote
	copymode = 1;
	p++;
	//db5_skipwhite(&p);
      }

      token = " "; // placeholder for one character
      while((copymode == 0 && *p != ' ' && *p != ',' && *p != '=' && *p != '\0') ||
	    (copymode == 1 && *p != '\'' && *p != '\0')) {
	if(count + strlen(token) < querylen) {
	  *q++ = *p;
	  *q = '\0';
	}
	p++;
	count++;
      }

      if(copymode) { // skip possible second quote
	if(*p == '\'') {
	  p++;
	}
	db5_skipwhite(&p); // skip space after quote
      }

      token = "'"; // add single quote to set
      if(count + strlen(token) < querylen) {
	strcat (q, token);
	q += strlen(q);
      }
      count += strlen(token);

      mode = 30;

      break;

    case 30:

      valuetoo = 0;
      
      db5_skipwhite(&p);
      if(*p == '=') { // skip possible equals sign
	p++;
	db5_skipwhite(&p);
	valuetoo = 1;
      }

      if(*p == '"')
	valuetoo = 1;
      
      if(isalpha(*p) || isdigit(*p) || *p == '_')
	valuetoo = 1;
      
      if(valuetoo) {
	token = " = \""; // add equals to set
	if(count + strlen(token) < querylen) {
	  strcat (q, token);
	  q += strlen(q);
	}
	count += strlen(token);
	
	copymode = 0; // 0 = isalpha, 1 = within quotes
	db5_skipwhite(&p);
	if(*p == '"') { // skip possible first quote
	  copymode = 1;
	  p++;
	  //db5_skipwhite(&p); // no skipwhite after first quote
	}
	
	token = " ";
	while((copymode == 0 && *p != ' ' && *p != ',' && *p != '\0') ||
	      (copymode == 1 && *p != '"' && *p != '\0')) {
	  if(count + strlen(token) < querylen) {
	    *q++ = *p;
	    *q = '\0';
	  }
	  p++;
	  count++;
	}
	
	if(copymode) { // skip possible second quote
	  //db5_skipwhite(&p); // no skipwhite before second quote
	  if(*p == '\"')
	    p++;
	  db5_skipwhite(&p); // skip space after quote
	}

	token = "\""; // add quote to set
	if(count + strlen(token) < querylen) {
	  strcat (q, token);
	  q += strlen(q);
	}
	count += strlen(token);
      } // if(valuetoo
      
      mode = 40;
      break;
      
    case 40:

      db5_skipwhite(&p);
      if(*p == ',') {
	p++;
	db5_skipwhite(&p);
      }

      token = ","; // add comma to set
      if(count + strlen(token) < querylen) {
	strcat (q, token);
	q += strlen(token);
      }
      count += strlen(token);

      mode = 10;
      break;

    }

#ifdef DEBUG59
    fprintf(stderr,"command:`%s`", p);
    fprintf(stderr,", query:`%s`", query);
    fprintf(stderr,", mode:%d", mode);
    fprintf(stderr,"\n");
#endif
    
    if(*p == '\0')
      break;
    if(prevp == p && prevq == q) {
      fprintf(stderr,"%s: illegal character '", procname);
      fprintfcharacter(stderr, p);
      fprintf(stderr,"'");
      fprintf(stderr,", mode:%d", mode);
      fprintf(stderr,"\n");
      fflush(stderr);
      if(++ exitcount == 10) {
	break;
      }
    }
  }

  return(count);
}

Lisätty koodi, joka sivuttaa tulostetta sivuittain:

#include <termios.h>

struct termios old, new;

void startprompt()
{
  setvbuf(stdout, NULL, _IONBF, 0);
  tcgetattr(0, &old);
  new = old;
  new.c_lflag &= ~(ICANON | ECHO);
  //new.c_lflag &= ~(ISIG | ICANON | ECHO);
  new.c_cc[VMIN] = 1;
  new.c_cc[VTIME] = 2;
  tcsetattr(0, TCSANOW, &new);
}

void endprompt()
{
  tcsetattr(0, TCSANOW, &old);
}

void util_backspacechars(int chars)
{
  int c;
  
  for(c = 0; c < chars; c++)
    fprintf(stdout,"\b");
  for(c = 0; c < chars; c++)
    fprintf(stdout," ");
  for(c = 0; c < chars; c++)
    fprintf(stdout,"\b");
}

int screenlinesleft = 0, screenmaxchars, screenmaxlines;
int screenchars = 0;
int quitcmd = 0;

void more_wait()
{
  int c;
  unsigned char *moretext = "---more---";
  
  startprompt();
  fprintf(stdout,"%s", moretext);

  // wait until something below is pressed
  
  for(;;) {
    c = getchar();
    if(c == ' ') {
      screenlinesleft = screenmaxlines; // full screen
      break;
    } else if(c == '\n') {
      screenlinesleft = 1; // one line
      break;
    } else if(c == 'q') { // quit
      quitcmd = 1;
      break;
    }
  }
  
  util_backspacechars(strlen(moretext));
  
  endprompt();
}

#include <stdarg.h>

void more_printf(const char *format, ...)
{
  int count;
  va_list args;
  unsigned char *p;
  static char *printbuf=NULL;
  static int printbuf_len=0;

  va_start(args, format);
  count=vsnprintf(printbuf, printbuf_len, format, args) + 1;
  va_end(args);

  if(printbuf_len < count) {
    printbuf_len = count;
    printbuf=realloc(printbuf, printbuf_len);
    va_start(args, format);
    count=vsnprintf(printbuf, printbuf_len, format, args) + 1;
    va_end(args);
  }

  // print all characters from printbuf.
  // prompt more for full screen pages.
  
  p=printbuf;
  while(*p!='\0') {
    putchar(*p);
    if(*p=='\n' || ++screenchars==screenmaxchars) {
      screenchars=0;
      if(--screenlinesleft==0) {
	more_wait();
      }
    }
    p++;
  }
}

Lisätty koodi, jolla valitaan kyselyä vastaavat tietueet:

int db6_match(unsigned char *set, unsigned char *match)
{
  int ok, ok2, first1, first2;
  
  unsigned char *s, *m;

  unsigned int namelen, valuelen;
  unsigned char *name, *value;
  unsigned int namelen2, valuelen2;
  unsigned char *name2, *value2;

#ifdef DEBUG72
  fprintf(stdout,"match:%s", match);
  fprintf(stdout,", set:%s", set);
  fprintf(stdout,"\n");
  fflush(stdout);
#endif
  
  m = match;
  first2 = 1;
  ok = 1;
  while(*m != '\0') {
    db5_skipwhite(&m);
    if(!first2) {
      if(*m == ',')
	m++;
      db5_skipwhite(&m);
    }
    first2 = 0;
    
    db5_get_element(&namelen2, &name2, &valuelen2, &value2, &m); // match
    db5_skipwhite(&m);

#ifdef DEBUG72
    fprintf(stdout,"db6_get_element2: ");
    fprintf(stdout,"name2:%.*s(%d)", namelen2, name2, namelen2);
    fprintf(stdout,", value2:%.*s(%d)", valuelen2, value2, valuelen2);
    fprintf(stdout,"\n");
    fflush(stdout);
#endif    

    s = set;
    first1 = 1;
    ok2 = 0;
    while(*s != '\0') {
      db5_skipwhite(&s);
      if(!first1) {
	if(*s == ',')
	  s++;
	db5_skipwhite(&s);
      }
      first1 = 0;

      db5_get_element(&namelen, &name, &valuelen, &value, &s); // set
      db5_skipwhite(&s);

#ifdef DEBUG72
      fprintf(stdout,"db6_get_element: ");
      fprintf(stdout,"name:%.*s(%d)", namelen, name, namelen);
      fprintf(stdout,", value:%.*s(%d)", valuelen, value, valuelen);
      fprintf(stdout,"\n");
      fflush(stdout);
#endif
      
      if((name2 != NULL && namelen == namelen2 && !strncmp(name, name2, namelen)) &&
	 (value2 == NULL || (valuelen == valuelen2 && !strncmp(value, value2, valuelen))) ) {
	ok2 = 1;
	break;
      }
    }
    if(ok2 == 0)
      ok = 0;
  }
#ifdef DEBUG72
  fprintf(stdout,"db6_match done");
  fprintf(stdout,", ok:%d", ok);
  fprintf(stdout,"\n");
  fflush(stdout);
#endif
  return(ok);
}

Lisätty koodi project, joka palauttaa rivistä vain kyselyn sisältämät sarakkeet:

void db6_project(unsigned int *projectlen, unsigned char **project, unsigned char *set, unsigned char *query)
{
  unsigned char *q, *s;

  unsigned int namelen, valuelen;
  unsigned char *name, *value;
  unsigned int namelen2, valuelen2;
  unsigned char *name2, *value2;

  if(db5_set != NULL) {
    free(db5_set);
    db5_set = NULL;
  }

  q = query;
  while(*q != '\0') {
    db5_skipwhite(&q);
    db5_get_element(&namelen, &name, &valuelen, &value, &q);
    db5_skipwhite(&q);
    if(*q == ',') {
      q++;
      db5_skipwhite(&q);
    }

    s = set;
    while(*s != '\0') {
      db5_skipwhite(&s);
      db5_get_element(&namelen2, &name2, &valuelen2, &value2, &s);
      db5_skipwhite(&s);
      if(*s == ',') {
	s++;
	db5_skipwhite(&s);
      }

      if((name != NULL && namelen == namelen2 && !strncmp(name, name2, namelen)) &&
	 (value == NULL || (valuelen == valuelen2 && !strncmp(value, value2, valuelen))) ) {

	unsigned char name3[64];
	unsigned char value3[128];

	memset(name3,0,sizeof(name3));
	memset(value3,0,sizeof(value3));

	strncpy(name3, name2, namelen2);
	strncpy(value3, value2, valuelen2);

	db5_put(name3, value3);
	
      }
    } // while(*s != '\0'
  } // while(*q != '\0'

  if(db5_set != NULL) {
    if(*projectlen < strlen(db5_set) + 1) {
      unsigned char *prevproject = *project;
      *projectlen = strlen(db5_set) + 1;
      *project = realloc(*project, *projectlen);
      if(prevproject == NULL)
	**project = '\0';
    }
    strcpy(*project, db5_set);
  } else
    *project = '\0';
}

Kirjoitettu rutiini, joka voi lukea tiedostosta merkkijonon, jonka pituutta ei tiedetä etukäteen. Ohjelma vain suurentaa muistipuskuria tarvittaessa:

unsigned char *util_fgets(int *commandlen, unsigned char **command, FILE *fp1)
{
  int count, retval;
  unsigned char buffer[64];
  unsigned char *buf = *command;

  count = 0;

  if(buf != NULL)
    buf[0] = '\0';

  retval = 1;

  for(;;) {
    if(fgets(buffer, sizeof(buffer), fp1) == NULL) {
      retval = 0;
      break;
    }

    //fprintf(stderr," %s",buffer);
    count = 0;
    if(buf != NULL)
      count += strlen(buf);
    count += strlen(buffer);

    if(*commandlen < count) {
      unsigned char *prevbuf = buf;
      *commandlen = count;
      buf = realloc(buf, *commandlen);
      if(prevbuf == NULL)
	buf[0] = '\0';
    }
    
    strcat(buf, buffer);

    if(buf[strlen(buf)-1] == '\n') {
      buf[strlen(buf)-1] = '\0';
      break;
    }
  }
  *command = buf;

  if(retval == 0)
    return(NULL);
  else
    return(*command);
}

Seuraava pätkä tekee kyselyn, ja tulostaa vastauksen:

int util_doquery(unsigned char *query, unsigned char *filename)
{
  FILE *fp1;
  static unsigned char *line = NULL;
  static unsigned int linelen = 0;
  static unsigned char *project = NULL;
  static unsigned int projectlen = 0;
  
  if((fp1 = fopen(filename,"r"))== NULL) {
    fprintf(stderr,"%s: cannot open file `%s`\n", procname, filename);
    return(1);
  }

  db6_setheaderflags("project", 0); // standard: sorted, no duplicates
  db6_setheaderquery("project", query);
  db6_deletesets("project");

  while(util_fgets(&linelen, &line, fp1) != NULL) {
    if(quitcmd)
      break;
    if(line[strlen(line)-1] == '\n')
      line[strlen(line)-1] = '\0';
    if(db6_match(line, query)) {
      db6_project(&projectlen, &project, line, query);
      db6_putset("project", project);
    }
  }
  fclose(fp1);

  int c = 0;
  for(;;) {
    unsigned char *set;
    if(quitcmd)
      break;
    set = db6_getset("project", c);
    if(set == NULL)
      break;
    more_printf("%d*`%s`\n", c, set);
    c++;
  }
}

Lisätty db6_putset koodi lajittelulle (ja tuplille):

void db6_putset(unsigned char *setname, unsigned char *set)
{
  int add;
  struct setheader *sh;

  sh = db6_setheader(setname);

  struct setline **ppsetline;

  add = 1;
  ppsetline = &sh->first;
  while(*ppsetline != NULL) { // duplicates
    if((sh->flags & DB6_DUP) == 0 &&
       !strcmp(set, (*ppsetline)->set)) {
      add = 0;
      break;
    }
    if((sh->flags & DB6_NOSORT) == 0 && // sort
       strcmp(set, (*ppsetline)->set) < 0) {
      add = 1;
      break;
    }
    ppsetline = &((*ppsetline)->next);
  }

  if(add) {
    struct setline *nsl;
    nsl = malloc(sizeof(struct setline));
    nsl->set = db6_set(set);
#ifdef DEBUG66
    more_printf("Adding chain `%s`, set `%s`\n", setname, nsl->set);
#endif
    nsl->next = *ppsetline;
    *ppsetline = nsl;
  }
}

Seuraava kappale on pääohjelmassa (main) oleva rutiini, jolla voidaan tehdä testiaineistoa:

#define TEST_SETS 9
#define TEST_ITEMS 3
  
  if(testflag) {
    // 'a'='1', 'b'='1', 'c'='1'
    // 'a'='2', 'b'='1', 'c'='1'
    // 'a'='3', 'b'='2', 'c'='1'
    // 'a'='4', 'b'='2', 'c'='2'
    // 'a'='5', 'b'='3', 'c'='2'
    // 'a'='6', 'b'='3', 'c'='2'
    // 'a'='7', 'b'='4', 'c'='3'
    // 'a'='8', 'b'='4', 'c'='3'
    // 'a'='9', 'b'='5', 'c'='3'
    for(c = 0; c < TEST_SETS; c++) {
      first = 1;
      for(d = 0; d < TEST_ITEMS ; d++) {
	if(!first)
	  fprintf(stdout,", ");
	fprintf(stdout,"'%c'", 'a' + d);
	fprintf(stdout,"=");
	fprintf(stdout,"'%d'", c / (d + 1) + 1);
	first = 0;
      }
      fprintf(stdout,"\n");
    }
  }

Seuraava rutiini mahdollistaa lajittelun haussa. Lajittelu tehdään kenttien kyselyn järjestyksessä:

void db6_putset(unsigned char *setname, unsigned char *set)
{
  int add;
  struct setheader *sh;

  sh = db6_setheader(setname);

  struct setline **ppsetline;

  add = 1;
  ppsetline = &sh->first;
  while(*ppsetline != NULL) { // duplicates
    if((sh->flags & DB6_DUP) == 0 &&
       !strcmp(set, (*ppsetline)->set)) {
      add = 0;
      break;
    }
    if((sh->flags & DB6_NOSORT) == 0 && // sort
       strcmp(set, (*ppsetline)->set) < 0) {
      add = 1;
      break;
    }
    ppsetline = &((*ppsetline)->next);
  }

  if(add) {
    struct setline *thissetline;
    thissetline = malloc(sizeof(struct setline));
    thissetline->set = db6_set(set);
    sh->count++;
#ifdef DEBUG66
    more_printf("Adding chain `%s`, set `%s`\n", setname, thissetline->set);
#endif
    thissetline->next = *ppsetline;
    *ppsetline = thissetline;
  }
}

Seuraava rutiini siirtää käsiteltävän kyselyn tuloksen (setheader) aina listan alkuun: Näin usein käytettävät kyselyt löytyvät nopeammin.

static struct setheader *db6_setheader(unsigned char *setname)
{
  struct setheader **ppsetheader, *thissetheader;

  ppsetheader = &firstsetheader;
  while(*ppsetheader != NULL) {
    if(!strcmp(setname, (*ppsetheader)->setname))
      break;
    ppsetheader = &((*ppsetheader)->next);
  }

  if(*ppsetheader == NULL) { // does not exist, create
    thissetheader = malloc(sizeof(struct setheader));
    thissetheader->setname = malloc(strlen(setname) + 1);
    strcpy(thissetheader->setname, setname);
    thissetheader->flags = 0;
    thissetheader->query = NULL;
    thissetheader->count = 0;
    thissetheader->first = NULL;
    // put to first
    thissetheader->next = firstsetheader;
    firstsetheader = thissetheader;
#ifdef DEBUG66
    fprintf(stdout,"added set `%s` as first set\n",setname);
#endif
  } else {
    // remove from list
    thissetheader = *ppsetheader;
    *ppsetheader = thissetheader->next;
    // add back to 1st of list
    thissetheader->next = firstsetheader;
    firstsetheader = thissetheader;
#ifdef DEBUG66
    fprintf(stdout,"added set `%s` as first set\n",setname);
#endif
  }
  return(thissetheader);
}

Seuraava rutiini poistaa kyselyn set:in. Samalla lasketaan viittauslaskuria (count), ja jos set:iin ei ole enää viittauksia, se poistetaan muistista:

void db6_deleteset(unsigned char *set)
{
  int found;
  struct set **ppset;

  found = 0;
  ppset = &firstset;
  while(*ppset != NULL) {
    if(!strcmp((*ppset)->set, set)) {
      found = 1;
      break;
    }
    ppset = &((*ppset)->next);
  }
  
  if(found) {
    // set found
    // decrement reference counter
    (*ppset)->count--;
    // if no references delete set
    if((*ppset)->count == 0) { 
      struct set *prevppset;
      prevppset = *ppset;
#ifdef DEBUG66
      more_printf("Set `%s` deleted\n", (*ppset)->set);
#endif
      (*ppset) = (*ppset)->next;
      free(prevppset->set);
      free(prevppset);
    }
  }
}

Seuraavassa on ohjelma kokonaisuudessaan:

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

#include "sha256.h"

unsigned char *procname = NULL;
static unsigned char *programname = "Terttuutil1 version 0.17 ©";
static unsigned char *copyright = "Copyright (c) 1998-2023 Jari Kuivaniemi (moijari.com), Helsinki, Finland. Kaikki oikeudet pidätetään!";

int newressu_output = 0;

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

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

void dumpbin(FILE *fp1, int len, unsigned char *buf)
{
  int c;

  for(c = 0; c < len; c++)
    if(isprint(buf[c]) && buf[c]!= ' ')
      fputc(buf[c], stderr);
    else
      fprintf(stderr,"\\%02x", buf[c]);
}

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

void inccvar()
{
  int c;

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

  if(cvarsize < c)
    cvarsize = c;

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

#define aCVARRANDOMSTART 2 // off by default

void clearcvar()
{
  int c;

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

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

#define GENT_SIZE 1024

static unsigned char gent[GENT_SIZE]; // orig 1024 
static unsigned int gent_pos = 0;

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

static void stream_internalbytes(unsigned char *digest)
{
  HashCtx hash;

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

}

static int streamt_pos = 0;

void stream_bytes(int size, unsigned char *buffer) // JariK 2023
{
  int c;
  static unsigned char streamt[HashLen]; // 256 bits
  
  for(c = 0; c < size; c++) {

    if(streamt_pos == 0) {
      stream_internalbytes(streamt); // read next random bytes

      stream_internalbytes(stream_key); // change key

    } // end of if(streamt_pos == 0

    buffer[c] = streamt[streamt_pos];
    streamt_pos = (streamt_pos + 1) % sizeof(streamt);

  } // end of for(c = 0; c < size; c++)
}

#define STREAM_KEY_ROUNDS 1024 // should be fast enough and slow enough, now 1024

void stream_open(int size, unsigned char *key) // size = 0 --> zero terminated string
{
  int c;
  HashCtx hash;

  clearcvar();
  streamt_pos = 0;  

  if(size == 0)
    size = strlen(key);
  
  HashInit(&hash);
  HashUpdate(&hash, key, size);
  HashUpdate(&hash, (unsigned char *) &cvar, cvarsize + 1);
  inccvar();

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

    HashFinal(stream_key, &hash);

    HashInit(&hash);
    HashUpdate(&hash, stream_key, sizeof(stream_key));
    HashUpdate(&hash, (unsigned char *) &cvar, cvarsize + 1);
    inccvar();
  }

  HashFinal(stream_key, &hash);

#define DEBUG21 2

#ifdef DEBUG21
  fflush(stdout);
  fprintf(stderr,"%s: stream_open():", procname);
  fprintf(stderr," randomness from key, key=");
  dumpbin(stderr, size, key);
  fprintf(stderr,"\n");
  fflush(stderr);
#endif
  
  memset(&hash, 0, sizeof(hash)); // forget hash

}

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

int stream_genbyte()
{
  unsigned char ch;

  if(gent_pos == 0) {
#ifdef DEBUG23
    ressu_dump("oldgent", 32, gent, 32); // first 32 bytes
#endif
    gent_clear();
    stream_bytes(sizeof(gent), gent);
#ifdef DEBUG23
    ressu_dump("newgent", 32, gent, 32); // first 32 bytes
#endif 
 } // if(gent_pos==0
  ch = gent[gent_pos];
  gent_pos = (gent_pos + 1) % sizeof(gent);

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

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

  if(lastlimit != limit) { // if limit changes, calculate new highlimit and bytes
    lastlimit = limit;
    if(limit <= 0x100) { // one byte
      // highest multiplier of limit that fits to needed bytes
      highlimit = (0x100 / limit) * limit;
      // number of bytes needed
      bytes = 1;

    } else if(limit <= 0x10000) { // two bytes
      highlimit = (0x10000 / limit) * limit;
      bytes = 2;

    } else if(limit <= 0x1000000) { // three bytes
      highlimit = (0x1000000 / limit) * limit;
      bytes = 3;

    } else if(limit <= 0x100000000) { // four bytes
      highlimit = (0x100000000 / limit) * limit;
      bytes = 4;

    } else if(limit <= 0x10000000000) { // five bytes
      highlimit = (0x10000000000 / limit) * limit;
      bytes = 5;

    } else if(limit <= 0x1000000000000) { // six bytes
      highlimit = (0x1000000000000 / limit) * limit;
      bytes = 6;

    } else if(limit <= 0x100000000000000) { // seven bytes
      highlimit = (0x100000000000000 / limit) * limit;      
      bytes = 7;

    } else { // if(limit <= 0xffffffffffffffff) {
      highlimit = (0xffffffffffffffff / limit) * limit;      
      bytes = 8;

    }
  } // if(lastlimit != limit)

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

  word %= limit;
  
  return(word);
}

#include <termios.h>

struct termios old, new;

void startprompt()
{
  setvbuf(stdout, NULL, _IONBF, 0);
  tcgetattr(0, &old);
  new = old;
  new.c_lflag &= ~(ICANON | ECHO);
  //new.c_lflag &= ~(ISIG | ICANON | ECHO);
  new.c_cc[VMIN] = 1;
  new.c_cc[VTIME] = 2;
  tcsetattr(0, TCSANOW, &new);
}

void endprompt()
{
  tcsetattr(0, TCSANOW, &old);
}

void util_backspacechars(int chars)
{
  int c;
  
  for(c = 0; c < chars; c++)
    fprintf(stdout,"\b");
  for(c = 0; c < chars; c++)
    fprintf(stdout," ");
  for(c = 0; c < chars; c++)
    fprintf(stdout,"\b");
}

int screenlinesleft = 0, screenmaxchars, screenmaxlines;
int screenchars = 0;
int quitcmd = 0;

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

int more = 1;

void more_init()
{
  struct winsize w;
  ioctl(0, TIOCGWINSZ, &w);
  screenmaxchars = w.ws_col;
  screenmaxlines = w.ws_row-1;
  screenlinesleft = screenmaxlines;
  
  quitcmd = 0;
}

void more_wait()
{
  int c;
  unsigned char *moretext = "---more---";
  
  startprompt();
  fprintf(stderr,"%s", moretext);

  // wait until something below is pressed
  
  for(;;) {
    c = getchar();
    if(c == ' ') {
      screenlinesleft = screenmaxlines; // full screen
      break;
    } else if(c == '\n') {
      screenlinesleft = 1; // one line
      break;
    } else if(c == 'q') { // quit
      quitcmd = 1;
      break;
    }
  }
  
  util_backspacechars(strlen(moretext));
  
  endprompt();
}

#include <stdarg.h>

void more_printf(FILE *fp1, const char *format, ...)
{
  int count;
  va_list args;
  unsigned char *p;
  static char *printbuf=NULL;
  static int printbuf_len=0;

  va_start(args, format);
  count=vsnprintf(printbuf, printbuf_len, format, args) + 1;
  va_end(args);

  if(printbuf_len < count) {
    printbuf_len = count;
    printbuf=realloc(printbuf, printbuf_len);
    va_start(args, format);
    count=vsnprintf(printbuf, printbuf_len, format, args) + 1;
    va_end(args);
  }

  // print all characters from printbuf.
  // prompt more for full screen pages.
  
  p=printbuf;
  while(*p!='\0') {
    fputc(*p, fp1);
    if(*p=='\n' || ++screenchars==screenmaxchars) {
      screenchars=0;
      if(more && --screenlinesleft==0) {
	more_wait();
      }
    }
    p++;
  }
}

unsigned char *db5_set = NULL;

unsigned char *db5_get_set()
{
  return(db5_set);
}

void db5_skipwhite(unsigned char **p)
{
  while(isblank(**p))
    (*p)++;
}

#define aDEBUG8 2
#define aDEBUG9 2
#define aDEBUG10 2
#define aDEBUG11 2

void db5_get_element(unsigned int *namelen, unsigned char **name, unsigned int *valuelen, unsigned char **value, unsigned char **p2) // 2023 JariK
{
  unsigned char *p;

  p = *p2;
  
  db5_skipwhite(&p);
  if(*p == '\'') { // name
    p++;
    *name = p;
    *namelen = 0;
    while(*p != '\'' && *p != '\0') {
      p++;
      (*namelen)++;
    }
    if(*p == '\'')
      p++;
  } else
    *name = NULL;

  db5_skipwhite(&p);
  if(*p == '=') {
    p++;
  }

  db5_skipwhite(&p);
  if(*p == '\"') { // value
    p++;
    *value = p;
    *valuelen = 0;
    while(*p != '\"' && *p != '\0') {
      p++;
      (*valuelen)++;
    }
    if(*p == '\"')
      p++;
  } else
    *value = NULL;

  db5_skipwhite(&p);

#ifdef DEBUG8
  fprintf(stdout,"db5_get_element():");
  fprintf(stdout," name:%.*s(%d)", *namelen, *name, *namelen);
  fprintf(stdout,", value:%.*s(%d)", *valuelen, *value, *valuelen);
  fprintf(stdout,", next:%s",p);
  fprintf(stdout,"\n");
  fflush(stdout);
#endif
  
  *p2 = p;
}

int db5_get(unsigned char *name, int valuesize, unsigned char *value) // 2023 JariK
{
  int firstelement, retval = 0;
  unsigned char *p;

  unsigned int namelen = strlen(name);

  unsigned int namelen2, valuelen2;
  unsigned char *name2, *value2;

#ifdef DEBUG9
  fprintf(stdout,"********** get\n");
#endif
  
  value[0] = '\0';

  if(db5_set != NULL) {

    firstelement = 1;
    p = db5_set;

    // read thru elements element by element
    
    while(*p != '\0') {

      db5_skipwhite(&p);

      if(!firstelement) {
	if(*p == ',')
	  p++;
	db5_skipwhite(&p);
      }
      
      db5_get_element(&namelen2, &name2, &valuelen2, &value2, &p);

#ifdef DEBUG9
      fprintf(stdout,"get name:%s", name);
      fprintf(stdout,", name2:%.*s(%d)", namelen2, name2, namelen2);
      fprintf(stdout,", value2:%.*s(%d)", valuelen2, value2, valuelen2);
      fprintf(stdout,"\n");
#endif

      db5_skipwhite(&p);
      
      // we reached equal name
      
      if(namelen == namelen2 && !strncmp(name, name2, namelen)) {
	strncpy(value, value2, valuesize);
	if(valuesize >= valuelen2)
	  value[valuelen2] = '\0';
	else
	  value[valuesize - 1] = '\0';
	retval = strlen(value);
	break;
      }
      firstelement = 0;
    } // end of while(*p != '\0')
  } // end of if(db5_set != NULL)
  return(retval);
}

#define aSORTED 2

void db5_put(unsigned char *name, unsigned char *value) // 2023 JariK
{
  int found = 0, firstelement, lastelement;
  unsigned char *p, *currentelement = NULL;
  unsigned char *thiselement = NULL, *nextelement = NULL;

  unsigned int namelen = strlen(name);
  unsigned int valuelen = strlen(value);

  unsigned int namelen2, valuelen2;
  unsigned char *name2, *value2;

#ifdef DEBUG9
  fprintf(stdout,"********** put\n");
#endif
  
  firstelement = 1;
  lastelement = 0;

  if(db5_set != NULL) {

    p = db5_set;
 
    // read thru elements element by element
    
    while(*p != '\0') {

      db5_skipwhite(&p);
      
      currentelement = p; // save beginning of element

      if(!firstelement) {
	if(*p == ',')
	  p++;
	db5_skipwhite(&p);
      }

      db5_get_element(&namelen2, &name2, &valuelen2, &value2, &p);

      db5_skipwhite(&p);
      
#ifdef DEBUG9
      fprintf(stdout,"put name:%s", name);
      fprintf(stdout,", name2:%.*s(%d)", namelen2, name2, namelen2);
      fprintf(stdout,", value2:%.*s(%d)", valuelen2, value2, valuelen2);
      fprintf(stdout,"\n");
#endif

      // we reached equal name
      
      if(namelen == namelen2 &&
	 !strncmp(name, name2, namelen) ) {
	found = 1;
	thiselement = currentelement;
	nextelement = p; // beginning of next element
	break;
      }

#ifdef SORTED

      // we reached greater name

      if(namelen == namelen2 &&
	 strncmp(name, name2, namelen) < 0) { // equal lengths less (aaaa, bbbb)
	thiselement = currentelement;
	nextelement = currentelement;
	break;

      } else {

	// common beginning of strings
	
	int comblen = namelen;
	if(comblen > namelen2)
	  comblen = namelen2;
	
	int cmp = strncmp(name, name2, comblen);
	if((cmp < 0) || // first characters less (aaaa, b)
	   (!cmp && namelen2 > namelen) ) { // first characters equal but string longer (a, aaaa)
	  thiselement = currentelement;
	  nextelement = currentelement;
	  break;
	}
      }
    
#endif
      
      if(*p == '\0')
	lastelement = 1;
  
      firstelement = 0;
    } // end of while(*p != '\0')

  } else { // else of if(db5_set != NULL)

    lastelement = 1;
    firstelement = 1;

  } // end of if(db5_set != NULL)

  // make new element

  long count;
  static long elementsize = 0;
  static unsigned char *element = NULL;
  unsigned char *elementfmt;

  // figure out needed commas

  if((firstelement && found) || // first and existing
     (firstelement && lastelement) ) // first element in a new set (first and last)
    elementfmt = "'%s' = \"%s\""; // --> no commas (first existing or only)
  else if(firstelement) // first and not existing element
    elementfmt = "'%s' = \"%s\", "; // --> comma in the end (first new)
  else
    elementfmt = ", '%s' = \"%s\""; // ..> comma in the beginning (other)

  // print element

  count = snprintf(element, elementsize, elementfmt, name, value) + 1;
  if(elementsize < count) {
    elementsize = count;
    if((element = realloc(element, elementsize)) == NULL) {
      fprintf(stderr, "%s: realloc(): cannot allocate memory\n", procname);
      exit(1);
    }
    count = snprintf(element, elementsize, elementfmt, name, value) + 1;
  }

  // calculate change in length

  long oldcount = 0;
  
  count = 0;
  if(db5_set != NULL) {
    count += strlen(db5_set);
    oldcount = count + 1; // + '\0'
  }
  if(found) // change in value only
    count += valuelen - valuelen2; // no punctuation marks
  else
    count += strlen(element);
  count++; // + '\0'

#ifdef DEBUG11
  if(oldcount != count) {
    fprintf(stdout, "old length:%ld %s\n", oldcount, db5_set);
    fflush(stdout);
  }
#endif  

  if(oldcount != count) { // size changed, 

    // reallocate set according to new length

    unsigned char *tempdb5_set = db5_set;
    if((db5_set = realloc(db5_set, count)) == NULL) {
      fprintf(stderr,"%s: realloc(): cannot allocate memory\n", procname);
      exit(1);
    }

    // adjust these too
    
    if(thiselement != NULL)
      thiselement = db5_set + (thiselement - tempdb5_set);
    if(nextelement != NULL)
      nextelement = db5_set + (nextelement - tempdb5_set);

    if(tempdb5_set == NULL)
      db5_set[0] = '\0';
  }

  
  // adjust to end of set if not present
  
  if(thiselement == NULL)
    thiselement = db5_set + strlen(db5_set);
  if(nextelement == NULL)
    nextelement = db5_set + strlen(db5_set);

  // move end of the set

  memmove(thiselement + strlen(element), nextelement, strlen(nextelement) + 1); // end of string too

  // add new element
  
  memmove(thiselement, element, strlen(element));

#ifdef DEBUG11
  if(oldcount != count) {
    fprintf(stdout,"new length:%ld %s\n", count, db5_set);
    fflush(stdout);
  }
#endif  
  
#ifdef DEBUG10
  fprintf(stdout,", set      %s(%ld)", db5_set, strlen(db5_set));
  fprintf(stdout,"\n");
  fflush(stdout);
#endif
}

#define aDEBUG72 2

int db6_match(unsigned char *set, unsigned char *match)
{
  int ok, ok2, first1, first2;
  
  unsigned char *s, *m;

  unsigned int namelen, valuelen;
  unsigned char *name, *value;
  unsigned int namelen2, valuelen2;
  unsigned char *name2, *value2;

#ifdef DEBUG72
  fprintf(stdout,"match:%s", match);
  fprintf(stdout,", set:%s", set);
  fprintf(stdout,"\n");
  fflush(stdout);
#endif
  
  m = match;
  first2 = 1;
  ok = 1;
  while(*m != '\0') {
    db5_skipwhite(&m);
    if(!first2) {
      if(*m == ',')
	m++;
      db5_skipwhite(&m);
    }
    first2 = 0;
    
    db5_get_element(&namelen2, &name2, &valuelen2, &value2, &m); // match
    db5_skipwhite(&m);

#ifdef DEBUG72
    fprintf(stdout,"db6_get_element2: ");
    fprintf(stdout,"name2:%.*s(%d)", namelen2, name2, namelen2);
    fprintf(stdout,", value2:%.*s(%d)", valuelen2, value2, valuelen2);
    fprintf(stdout,"\n");
    fflush(stdout);
#endif    

    s = set;
    first1 = 1;
    ok2 = 0;
    while(*s != '\0') {
      db5_skipwhite(&s);
      if(!first1) {
	if(*s == ',')
	  s++;
	db5_skipwhite(&s);
      }
      first1 = 0;

      db5_get_element(&namelen, &name, &valuelen, &value, &s); // set
      db5_skipwhite(&s);

#ifdef DEBUG72
      fprintf(stdout,"db6_get_element: ");
      fprintf(stdout,"name:%.*s(%d)", namelen, name, namelen);
      fprintf(stdout,", value:%.*s(%d)", valuelen, value, valuelen);
      fprintf(stdout,"\n");
      fflush(stdout);
#endif
      
      if((name2 != NULL && namelen == namelen2 && !strncmp(name, name2, namelen)) &&
	 (value2 == NULL || (valuelen == valuelen2 && !strncmp(value, value2, valuelen))) ) {
	ok2 = 1;
	break;
      }
    }
    if(ok2 == 0)
      ok = 0;
  }
#ifdef DEBUG72
  fprintf(stdout,"db6_match done");
  fprintf(stdout,", ok:%d", ok);
  fprintf(stdout,"\n");
  fflush(stdout);
#endif
  return(ok);
}

#define aDEBUG50 2
#define aDEBUG51 2

void db6_project(unsigned int *projectlen, unsigned char **project, unsigned char *set, unsigned char *query)
{
  unsigned char *q, *s;

  unsigned int namelen, valuelen;
  unsigned char *name, *value;
  unsigned int namelen2, valuelen2;
  unsigned char *name2, *value2;

  if(db5_set != NULL) {
    free(db5_set);
    db5_set = NULL;
  }

  q = query;
  while(*q != '\0') {
    db5_skipwhite(&q);
    db5_get_element(&namelen, &name, &valuelen, &value, &q);
    db5_skipwhite(&q);
    if(*q == ',') {
      q++;
      db5_skipwhite(&q);
    }

    s = set;
    while(*s != '\0') {
      db5_skipwhite(&s);
      db5_get_element(&namelen2, &name2, &valuelen2, &value2, &s);
      db5_skipwhite(&s);
      if(*s == ',') {
	s++;
	db5_skipwhite(&s);
      }

      if((name != NULL && namelen == namelen2 && !strncmp(name, name2, namelen)) &&
	 (value == NULL || (valuelen == valuelen2 && !strncmp(value, value2, valuelen))) ) {

	unsigned char name3[64];
	unsigned char value3[128];

	memset(name3,0,sizeof(name3));
	memset(value3,0,sizeof(value3));

	strncpy(name3, name2, namelen2);
	strncpy(value3, value2, valuelen2);

	db5_put(name3, value3);
	
      }
    } // while(*s != '\0'
  } // while(*q != '\0'

  if(db5_set != NULL) {
    if(*projectlen < strlen(db5_set) + 1) {
      unsigned char *prevproject = *project;
      *projectlen = strlen(db5_set) + 1;
      *project = realloc(*project, *projectlen);
      if(prevproject == NULL)
	**project = '\0';
    }
    strcpy(*project, db5_set);
  } else
    *project = '\0';
}

void fprintfcharacter(FILE *fp1, unsigned char *p)
{
  fputc(*p, fp1); // print first char
  if(*p > 0xbf) { // first char utf8
    p++;
    for(;;) { // print rest of the utf8 chars
      if(*p > 0xbf || // new utf8 character
	 *p < 0x80 || // ascii character
	 *p == '\0') // end of string
	break;
      fputc(*p, fp1);
      p++;
    }
  }
}

#define aDEBUG59 2

int db6_parsequery(int querylen, unsigned char *query, unsigned char *command)
{
  unsigned char *token, *p, *q;
  int mode = 0, copymode = 0, count, valuetoo, exitcount = 0;
  
  p = command;
  q = query;
  mode = 20;
  count = 0;
  
  while(*p != '\0') {

    unsigned char *prevp = NULL;
    unsigned char *prevq = NULL;    
    
    prevp = p;
    prevq = q;

    if(count < querylen)
      *q = '\0';

    switch(mode) {

    case 10:

      token = " ";
      if(count + strlen(token) < querylen) {
	strcat (q, token);
	q += strlen(q);
      }
      count += strlen(token);
      mode = 20;
      
    case 20:

      token = "'";
      if(count + strlen(token) < querylen) {
	strcat (q, token);
	q += strlen(q);
      }	
      count += strlen(token);

      copymode = 0; // 0 = isalpha, 1 = within quotes
      db5_skipwhite(&p);
      if(*p == '\'') { // skip possible first quote
	copymode = 1;
	p++;
	//db5_skipwhite(&p);
      }

      token = " "; // placeholder for one character
      while((copymode == 0 && *p != ' ' && *p != ',' && *p != '=' && *p != '\0') ||
	    (copymode == 1 && *p != '\'' && *p != '\0')) {
	if(count + strlen(token) < querylen) {
	  *q++ = *p;
	  *q = '\0';
	}
	p++;
	count++;
      }

      if(copymode) { // skip possible second quote
	if(*p == '\'') {
	  p++;
	}
	db5_skipwhite(&p); // skip space after quote
      }

      token = "'"; // add single quote to set
      if(count + strlen(token) < querylen) {
	strcat (q, token);
	q += strlen(q);
      }
      count += strlen(token);

      if(*p == ',') // no value
	mode = 40; // skip to comma
      else
	mode = 30; // go thru value
      break;

    case 30:

      valuetoo = 0;
      
      db5_skipwhite(&p);
      if(*p == '=') { // skip possible equals sign
	p++;
	db5_skipwhite(&p);
	valuetoo = 1;
      }

      if(*p == '"')
	valuetoo = 1;
      
      if(isalpha(*p) || isdigit(*p) || *p == '_')
	valuetoo = 1;
      
      if(valuetoo) {
	token = " = \""; // add equals to set
	if(count + strlen(token) < querylen) {
	  strcat (q, token);
	  q += strlen(q);
	}
	count += strlen(token);
	
	copymode = 0; // 0 = isalpha, 1 = within quotes
	db5_skipwhite(&p);
	if(*p == '"') { // skip possible first quote
	  copymode = 1;
	  p++;
	  //db5_skipwhite(&p); // no skipwhite after first quote
	}
	
	token = " ";
	while((copymode == 0 && *p != ' ' && *p != ',' && *p != '\0') ||
	      (copymode == 1 && *p != '"' && *p != '\0')) {
	  if(count + strlen(token) < querylen) {
	    *q++ = *p;
	    *q = '\0';
	  }
	  p++;
	  count++;
	}
	
	if(copymode) { // skip possible second quote
	  //db5_skipwhite(&p); // no skipwhite before second quote
	  if(*p == '\"')
	    p++;
	  db5_skipwhite(&p); // skip space after quote
	}

	token = "\""; // add quote to set
	if(count + strlen(token) < querylen) {
	  strcat (q, token);
	  q += strlen(q);
	}
	count += strlen(token);
      } // if(valuetoo
      
      mode = 40;
      break;
      
    case 40:

      db5_skipwhite(&p);
      if(*p == ',') {
	p++;
	db5_skipwhite(&p);
      }

      token = ","; // add comma to set
      if(count + strlen(token) < querylen) {
	strcat (q, token);
	q += strlen(token);
      }
      count += strlen(token);

      mode = 10;
      break;

    }

#ifdef DEBUG59
    fprintf(stderr,"command:`%s`", p);
    fprintf(stderr,", query:`%s`", query);
    fprintf(stderr,", mode:%d", mode);
    fprintf(stderr,"\n");
#endif
    
    if(*p == '\0')
      break;
    if(prevp == p && prevq == q) {
      fprintf(stderr,"%s: illegal character '", procname);
      fprintfcharacter(stderr, p);
      fprintf(stderr,"'");
      fprintf(stderr,", mode:%d", mode);
      fprintf(stderr,"\n");
      fflush(stderr);
      if(++ exitcount == 10) {
	break;
      }
    }
  }

  return(count);
}

#define aDEBUG66 2

#define DB6_NOSORT 1
#define DB6_DUP 2

struct setheader {
  unsigned char *setname;
  int flags;
  unsigned char *query;
  int count;
  struct setheader *next;
  struct setline *first;
};

struct setheader *firstsetheader = NULL;

struct setline {
  unsigned char *set;
  struct setline *next;
};

struct set {
  unsigned char *set;
  int count;
  struct set *next;
};

struct set *firstset = NULL;

static struct setheader *db6_setheader(unsigned char *setname)
{
  struct setheader **ppsetheader, *thissetheader;

  ppsetheader = &firstsetheader;
  while(*ppsetheader != NULL) {
    if(!strcmp(setname, (*ppsetheader)->setname))
      break;
    ppsetheader = &((*ppsetheader)->next);
  }

  if(*ppsetheader == firstsetheader) {
#ifdef DEBUG66
    more_printf(stderr, "set `%s` is allready the first set\n",setname);
#endif
  }
  if(*ppsetheader == NULL) { // does not exist, create
    thissetheader = malloc(sizeof(struct setheader));
    thissetheader->setname = malloc(strlen(setname) + 1);
    strcpy(thissetheader->setname, setname);
    thissetheader->flags = 0;
    thissetheader->query = NULL;
    thissetheader->count = 0;
    thissetheader->first = NULL;
    // put to first
    thissetheader->next = firstsetheader;
    firstsetheader = thissetheader;
#ifdef DEBUG66
    more_printf(stderr, "added set `%s` as first set\n",setname);
#endif
  } else {
    // remove from list
    thissetheader = *ppsetheader;
    *ppsetheader = thissetheader->next;
    // add back to 1st of list
    thissetheader->next = firstsetheader;
    firstsetheader = thissetheader;
#ifdef DEBUG66
    more_printf(stderr, "added set `%s` as first set\n",setname);
#endif
  }
  return(thissetheader);
}

static struct setheader *db6_setheaderflags(unsigned char *setname, int flags)
{
  struct setheader *sh;

  sh = db6_setheader(setname);

  sh->flags = flags;
}

static struct setheader *db6_setheaderquery(unsigned char *setname, unsigned char *query)
{
  struct setheader *sh;

  sh = db6_setheader(setname);

  if(sh->query != NULL)
    free(sh->query);

  sh->query = malloc(strlen(query) + 1);
  strcpy(sh->query, query);
}

void db6_dumpsetheader(struct setheader *thissetheader)
{
  more_printf(stderr, "setheader");
  more_printf(stderr, " %p", thissetheader);
  more_printf(stderr, " setname:`%s`", thissetheader->setname);
  more_printf(stderr, ", flags:%02x", thissetheader->flags);
  more_printf(stderr, ", query:`%s`", thissetheader->query);
  more_printf(stderr, ", count:%d", thissetheader->count);
  more_printf(stderr, ", next:%p", thissetheader->next);
  more_printf(stderr, ", first:%p", thissetheader->first);
  more_printf(stderr, "\n");
}

void db6_dumpsetheaders()
{
  struct setheader *sh;
  sh = firstsetheader;
  while(sh != NULL) {
    db6_dumpsetheader(sh);
    sh=sh->next;
  }
}

void db6_dumpsetline(struct setline *thissetline)
{
  more_printf(stderr, "set");
  more_printf(stderr, " %p", thissetline);
  more_printf(stderr, " set:%s", thissetline->set);
  more_printf(stderr, ", next:%p", thissetline->next);
  more_printf(stderr, "\n");
}

unsigned char *db6_set(unsigned char *set)
{
  int found;
  struct set *s;

  found = 0;
  s = firstset;
  while(s != NULL) {
    if(!strcmp(s->set, set)) {
      found = 1;
      break;
    }
    s = s->next;
  }
  if(!found) {
    struct set *thisset;
    thisset = malloc(sizeof(struct set));
    thisset->set = malloc(strlen(set) + 1);
    strcpy(thisset->set, set);
    thisset->count = 1;
    //more_printf(stdout, "2:`%s`", set);
    thisset->next = firstset;
    firstset = thisset;
    s = thisset;
  } else {
    s->count++;
  }
  return(s->set);
}

void db6_putset(unsigned char *setname, unsigned char *set)
{
  int add;
  struct setheader *sh;

  sh = db6_setheader(setname);

  struct setline **ppsetline;

  add = 1;
  ppsetline = &sh->first;
  while(*ppsetline != NULL) { // duplicates
    if((sh->flags & DB6_DUP) == 0 &&
       !strcmp(set, (*ppsetline)->set)) {
      add = 0;
      break;
    }
    if((sh->flags & DB6_NOSORT) == 0 && // sort
       strcmp(set, (*ppsetline)->set) < 0) {
      add = 1;
      break;
    }
    ppsetline = &((*ppsetline)->next);
  }

  if(add) {
    struct setline *thissetline;
    thissetline = malloc(sizeof(struct setline));
    thissetline->set = db6_set(set);
    sh->count++;
#ifdef DEBUG66
    more_printf(stderr, "Adding chain `%s`, set `%s`\n", setname, thissetline->set);
#endif
    thissetline->next = *ppsetline;
    *ppsetline = thissetline;
  }
}

void db6_dumpset(struct set *thisset)
{
  more_printf(stderr, "set");
  more_printf(stderr, " %p", thisset);
  more_printf(stderr, " set:`%s`", thisset->set);
  more_printf(stderr, " count:%d", thisset->count);
  more_printf(stderr, ", next:%p", thisset->next);
  more_printf(stderr, "\n");
}

void db6_dumpsets()
{
  struct set *s;
  s = firstset;
  while(s != NULL) {
    db6_dumpset(s);
    s = s->next;
  }
}


void db6_deleteset(unsigned char *set)
{
  int found;
  struct set **ppset;

  found = 0;
  ppset = &firstset;
  while(*ppset != NULL) {
    if(!strcmp((*ppset)->set, set)) {
      found = 1;
      break;
    }
    ppset = &((*ppset)->next);
  }
  
  if(found) {
    // set found
    // decrement reference counter
    (*ppset)->count--;
    // if no references delete set
    if((*ppset)->count == 0) { 
      struct set *prevppset;
      prevppset = *ppset;
#ifdef DEBUG66
      more_printf(stderr, "Set `%s` deleted\n", (*ppset)->set);
#endif
      (*ppset) = (*ppset)->next;
      free(prevppset->set);
      free(prevppset);
    }
  }
}


#ifdef OLD1

void db6_deleteset(unsigned char *set)
{
  int found;
  struct set *s;

  found = 0;
  s = firstset;
  while(s != NULL) {
    if(!strcmp(s->set, set)) {
      found = 1;
      break;
    }
    s = s->next;
  }
  if(found) {
    s->count--;
  }
}

#endif

void db6_deletesets(unsigned char *setname)
{
  int c, found;
  struct setheader *sh;
  struct setline *sl, *sl2;
  
  sh = db6_setheader(setname);

#ifdef DEBUG66
  more_printf(stderr, "Deleting chain `%s`\n",setname);
#endif
  sl = sh->first;
  while(sl != NULL) {
#ifdef DEBUG66
    more_printf(stderr, "Deleting:%s\n", sl->set);
#endif
    db6_deleteset(sl->set);
    sh->count--;
    sl2 = sl;
    sl = sl2->next;
    free(sl2);
  }
#ifdef DEBUG66
  more_printf(stderr, "Chain `%s` deleted\n",setname);
#endif
  sh->first = NULL;
}

unsigned char *db6_getset(unsigned char *setname, int lineno)
{
  int c;
  struct setheader *sh;
  
  sh = db6_setheader(setname);
  //db6_dumpsetheader(sh);

  struct setline *sl;
  
  sl = sh->first;
  while(sl != NULL && lineno > 0) {
    //db6_dumpsetline(sl);
    sl = sl->next;
    lineno--;
  }
  if(sl == NULL)
    return(NULL);
  else {
#ifdef DEBUG66
    more_printf(stderr, "Restoring chain `%s`, set `%s`\n", setname, sl->set);
#endif
    return(sl->set);
  }
}

void util_parsequery(int *querylen, unsigned char **query, unsigned char *command)
{
  int count;
  
  count = db6_parsequery(*querylen, *query, command) + 1;
  if(*querylen < count) {
    *querylen = count;
    *query = realloc(*query, *querylen);
    count = db6_parsequery(*querylen, *query, command) + 1;
  }
  more_printf(stderr, "command:`%s`", command);
  more_printf(stderr, ", query:`%s`", *query);
  more_printf(stderr, "\n");
}

#define aDEBUG83 2

unsigned char *util_fgets(int *commandlen, unsigned char **command, FILE *fp1)
{
  int count, retval;
  unsigned char buffer[4];
  unsigned char *buf = *command;

  count = 0;

  if(buf != NULL)
    buf[0] = '\0';

  retval = 1;

  for(;;) {
    if(fgets(buffer, sizeof(buffer), fp1) == NULL) {
      retval = 0;
      break;
    }

#ifdef DEBUG83

    more_printf(stderr, "/`%s`",buffer);

#endif
    
    count = 0;
    if(buf != NULL)
      count += strlen(buf);
    count += strlen(buffer) + 1;
    
    if(*commandlen < count) {
      unsigned char *prevbuf = buf;
      *commandlen = count;
      buf = realloc(buf, *commandlen);
      if(prevbuf == NULL)
	buf[0] = '\0';
    }
    
    strcat(buf, buffer);

    if(buf[strlen(buf)-1] == '\n') {
      buf[strlen(buf)-1] = '\0';
      break;
    }
  }
  *command = buf;

#ifdef DEBUG83

  more_printf(stderr, "\n");

#endif
    
  
#ifdef DEBUG83

  more_printf(stderr, "`%s`(%d)", buf, strlen(buf));

#endif
  
  if(retval == 0)
    return(NULL);
  else
    return(*command);
}

int util_doquery(unsigned char *query, unsigned char *filename)
{
  FILE *fp1;
  static unsigned char *line = NULL;
  static unsigned int linelen = 0;
  static unsigned char *project = NULL;
  static unsigned int projectlen = 0;
  
  if((fp1 = fopen(filename,"r"))== NULL) {
    fprintf(stderr,"%s: cannot open file `%s`\n", procname, filename);
    return(1);
  }

  //db6_setheaderflags("project", DB6_NOSORT | DB6_DUP);
  //db6_setheaderflags("project", DB6_NOSORT);
  //db6_setheaderflags("project", DB6_DUP);
  db6_setheaderflags("project", 0);
  //db6_setheaderflags("project", DB6_NOSORT | DB6_DUP);
  db6_setheaderquery("project", query);
  db6_deletesets("project");

  while(util_fgets(&linelen, &line, fp1) != NULL) {
    if(quitcmd)
      break;
    if(line[strlen(line)-1] == '\n')
      line[strlen(line)-1] = '\0';
    if(db6_match(line, query)) {
      db6_project(&projectlen, &project, line, query);
      db6_putset("project", project);
    }
  }
  fclose(fp1);

  int c = 0;
  for(;;) {
    unsigned char *set;
    if(quitcmd)
      break;
    set = db6_getset("project", c);
    if(set == NULL)
      break;
    more_printf(stdout, "%d*`%s`\n", c, set);
    c++;
  }
}

void terttuutilversion()
{
  fprintf(stdout,"%s\n", programname);
  fprintf(stdout,"%s\n", copyright);
  fflush(stdout);
}

#define aDEBUG96 2

int main(int argc, char *argv[])
{
  int c, d, first, printcomma = 0, queryflag = 0, testflag = 0, test2flag = 0;
  unsigned char command[4096], *p;
  int commandlen = 4096;
#define aDEBUG98 2

  more = 0;
  if(isatty(STDOUT_FILENO))
    more = 1;
  
#ifdef DEBUG98
  db5_set = "'a' = \"1\", 'b' = \"2\", 'c' = \"3\"";
  db6_match(db5_set, "'a' = \"1\"");
  db6_match(db5_set, "'b' = \"2\"");
  db6_match(db5_set, "'c' = \"3\"");
  db6_match(db5_set, "'a'");
  db6_match(db5_set, "'b'");
  db6_match(db5_set, "'c'");
  db6_match(db5_set, "'a' = \"a\"");
  db6_match(db5_set, "'b' = \"b\"");
  db6_match(db5_set, "'c' = \"c\"");
#endif

  procname = argv[0];
  
  first = 1;
  command[0] = '\0';

  int help = 0, stat = 1;
  unsigned char filename[128] = "terttuutil1.3.skk";;
  
  //
  // look thru command line parameters
  //
  
  for(c = 1; c < argc; c++) {

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

      if(!strcmp("--help", argv[c]) ||
	 !strcmp("-?", argv[c])) {
	help = 1;
	
      } else if(!strncmp("--filename", argv[c], 10)) {
	if(*(argv[c] + 10) != '\0') {
	  strncpy(filename, argv[c] + 10, sizeof(filename));
	} else if(c + 1 < argc) {
	  strncpy(filename, argv[c + 1], sizeof(filename));
	  c++;
	}
	if(printcomma)
	  fprintf(stderr,", ");
	else
	  printcomma = 0;

      } else if(!strcmp("--copyright", argv[c]) ||
	 !strcmp("--version", argv[c])) {
	terttuutilversion();
#ifdef SHA256
	fprintf(stderr,"\nsha256(");
	for(int c = 0; c < HashLen; c++) {
	  fprintf(stderr, "%02x", programfiledigest[c]);
	}
	fprintf(stderr, ")\n\n");
#endif
	help = 0;
	exit(1);

      } else if(!strcmp("--stat", argv[c])) {
	stat = !stat;
	
      } else if(!strcmp("--query", argv[c])) {
	queryflag = !queryflag;
	
      } else if(!strcmp("--test", argv[c])) {
	testflag = !testflag;
	
      } else if(!strcmp("--test2", argv[c])) {
	test2flag = !test2flag;
	
      }
    } else {
      if(!first)
	strcat(command, " ");
      strcat(command, argv[c]);
      first = 0;
    }
  }

  db6_setheaderflags("headerfields", 0);
  db6_setheaderflags("linesfields", 0);
  db6_setheaderflags("header", 0);
  db6_setheaderflags("lines", 0);
  db6_setheaderflags("user", 0);
  db6_setheaderflags("log", 0);
  db6_setheaderflags("session", 0);

  more_init();

#define TEST_SETS 8192
#define TEST_ITEMS 5
  //#define TEST_SETS 9
  //#define TEST_ITEMS 3
  
  if(testflag) {
    // 'a'='1', 'b'='1', 'c'='1'
    // 'a'='2', 'b'='1', 'c'='1'
    // 'a'='3', 'b'='2', 'c'='1'
    // 'a'='4', 'b'='2', 'c'='2'
    // 'a'='5', 'b'='3', 'c'='2'
    // 'a'='6', 'b'='3', 'c'='2'
    // 'a'='7', 'b'='4', 'c'='3'
    // 'a'='8', 'b'='4', 'c'='3'
    // 'a'='9', 'b'='5', 'c'='3'
    for(c = 0; c < TEST_SETS; c++) {
      first = 1;
      for(d = 0; d < TEST_ITEMS ; d++) {
	if(!first)
	  fprintf(stdout,", ");
	fprintf(stdout,"'%c'", 'a' + d);
	fprintf(stdout," = ");
	fprintf(stdout,"\"%d\"", c / (d + 1) + 1);
	first = 0;
      }
      fprintf(stdout,"\n");
    }
  }
  
  if(test2flag) {
#define SEQUENTIAL 1
#define RANDOM 2
    
    struct field {
      unsigned char *fieldname;
      int fieldtype;
      int lowlimit;
      int highlimit;
      unsigned char *valuestring;
    } fields[] = {
      { "asno", SEQUENTIAL, 0, 0,"10%05d" },
      { "asnimi", SEQUENTIAL, 0,0, "nimi: %d" },
      { "asoso", SEQUENTIAL, 0, 0, "osoite: %d" },
      { "aspono", RANDOM, 0, 99999, "postinumero%d"},
      { "aspotmp", RANDOM, 0, 50, "postitmp %d"},
      { "asry", RANDOM, 0, 10, "1%d"},
      { "asmail", RANDOM, 0, 10, "mail: %d"},
    };

    stream_open(0, "kalakala");
    
    for(c = 0; c < TEST_SETS; c++) {
      if(quitcmd)
	break;

      first = 1;

      for(d = 0; d < sizeof(fields) / sizeof(fields[0]); d++) {
	if(!first)
	  more_printf(stdout,", ");
	more_printf(stdout,"'%s'", fields[d].fieldname);
	more_printf(stdout," = ");
	more_printf(stdout,"\"");
	if(fields[d].fieldtype = RANDOM && (fields[d].lowlimit != 0 || fields[d].highlimit != 0)) {
	  unsigned int e = stream_gen_limit(fields[d].highlimit - fields[d].lowlimit) + fields[d].lowlimit;
	  more_printf(stdout, fields[d].valuestring, e);
	} else {
	  more_printf(stdout, fields[d].valuestring, c);
	}
	more_printf(stdout,"\"");
	first = 0;
      }
      more_printf(stdout,"\n");
    }
    more_printf(stdout,"\n");
  }
  
  if(printcomma)
    fprintf(stderr,", ");
  else
    printcomma = 0;
  
  int count;
  static unsigned char *query = NULL;
  static int querylen = 0;
  unsigned char *q;

  //querylen = 1;
  //query = realloc(query, querylen);;
  querylen = 0;
  query = NULL;

  if(!queryflag && strlen(command) != 0) {
    util_parsequery(&querylen, &query, command);
    util_doquery(query, filename);
  }

  int quit = 0;

  unsigned char *command2 = NULL;
  int command2len = 0;
  
  if(queryflag) {
    while(!quit) {

      more_init();

      fprintf(stdout,"skk>");
      util_fgets(&command2len, &command2, stdin);
      if(command2[strlen(command) - 1] == '\n')
	command2[strlen(command) - 1] = '\0';
      if(strlen(command2)==0)
	continue;

      more_printf(stdout, "commandlen:%d\n", command2len);

      if(!strcmp(command2,"quit"))
	quit = 1;
      else if(!strcmp(command2,"exit"))
	quit = 1;
      else if(!strcmp(command2,"dumpsh"))
	db6_dumpsetheaders();
      else if(!strcmp(command2,"dumps"))
	db6_dumpsets();
      else {
	util_parsequery(&querylen, &query, command2);
	util_doquery(query, filename);
      }
    }
  }
}

Tässä vielä esimerkkejä terttuutil1:n ajosta: (ensimmäisessä ilman komentoriviparametreja ajettu ajo antaa tulokseksi koko testimateriaalin (poista , line: ja `:t datasta).

$ ./terttuutil1
stat, command:``, query: ``
, line: `'a' = "1", 'b' = "1", 'c' = "1"`
, line: `'a' = "2", 'b' = "1", 'c' = "1"`
, line: `'a' = "3", 'b' = "2", 'c' = "1"`
, line: `'a' = "4", 'b' = "2", 'c' = "1"`
, line: `'a' = "5", 'b' = "3", 'c' = "2"`
, line: `'a' = "6", 'b' = "3", 'c' = "2"`
, line: `'a' = "7", 'b' = "4", 'c' = "2"`
, line: `'a' = "8", 'b' = "4", 'c' = "2"`

$ ./terttuutil1 a=1
stat, command:`a=1`, added:, query: `'a' = "1"`
, line: `'a' = "1", 'b' = "1", 'c' = "1"`

$ ./terttuutil1 a=2
stat, command:`a=2`, added:, query: `'a' = "2"`
, line: `'a' = "2", 'b' = "1", 'c' = "1"`

$ ./terttuutil1 a=3
stat, command:`a=3`, added:, query: `'a' = "3"`
, line: `'a' = "3", 'b' = "2", 'c' = "1"`

$ ./terttuutil1 a=4
stat, command:`a=4`, added:, query: `'a' = "4"`
, line: `'a' = "4", 'b' = "2", 'c' = "1"`

$ ./terttuutil1 b=1
stat, command:`b=1`, added:, query: `'b' = "1"`
, line: `'a' = "1", 'b' = "1", 'c' = "1"`
, line: `'a' = "2", 'b' = "1", 'c' = "1"`

$ ./terttuutil1 b=2
stat, command:`b=2`, added:, query: `'b' = "2"`
, line: `'a' = "3", 'b' = "2", 'c' = "1"`
, line: `'a' = "4", 'b' = "2", 'c' = "1"`

$ ./terttuutil1 b=3
stat, command:`b=3`, added:, query: `'b' = "3"`
, line: `'a' = "5", 'b' = "3", 'c' = "2"`
, line: `'a' = "6", 'b' = "3", 'c' = "2"`

$ ./terttuutil1 b=4
stat, command:`b=4`, added:, query: `'b' = "4"`
, line: `'a' = "7", 'b' = "4", 'c' = "2"`
, line: `'a' = "8", 'b' = "4", 'c' = "2"`

$ ./terttuutil1 c=1
stat, command:`c=1`, added:, query: `'c' = "1"`
, line: `'a' = "1", 'b' = "1", 'c' = "1"`
, line: `'a' = "2", 'b' = "1", 'c' = "1"`
, line: `'a' = "3", 'b' = "2", 'c' = "1"`
, line: `'a' = "4", 'b' = "2", 'c' = "1"`

$ ./terttuutil1 c=2
stat, command:`c=2`, added:, query: `'c' = "2"`
, line: `'a' = "5", 'b' = "3", 'c' = "2"`
, line: `'a' = "6", 'b' = "3", 'c' = "2"`
, line: `'a' = "7", 'b' = "4", 'c' = "2"`
, line: `'a' = "8", 'b' = "4", 'c' = "2"`
$ 

Lisätty terttuutil1:een –query toiminto, joka on hieman interaktiivisempi versio siitä: tässä esimerkki ajosta.

$ ./terttuutil1 --query
command:``, query:`(null)`, command:``, count:0
**********querylen:0, count:1, query:`(null)`
, query:``, command:``, count:0
**********querylen:1, count:1, query:``
`'a' = "1", 'b' = "1", 'c' = "1"`
`'a' = "2", 'b' = "1", 'c' = "1"`
`'a' = "3", 'b' = "2", 'c' = "1"`
`'a' = "4", 'b' = "2", 'c' = "1"`
`'a' = "5", 'b' = "3", 'c' = "2"`
`'a' = "6", 'b' = "3", 'c' = "2"`
`'a' = "7", 'b' = "4", 'c' = "2"`
`'a' = "8", 'b' = "4", 'c' = "2"`
skk>a=1
`a=1`
, query:``, command:``, count:9
**********querylen:1, count:10, query:``
, query:`'a' = "1"`, command:``, count:9
**********querylen:10, count:10, query:`'a' = "1"`
`'a' = "1", 'b' = "1", 'c' = "1"`
skk>a=2
`a=2`
, query:`'a' = "2"`, command:``, count:9
**********querylen:10, count:10, query:`'a' = "2"`
`'a' = "2", 'b' = "1", 'c' = "1"`
skk>a=3
`a=3`
, query:`'a' = "3"`, command:``, count:9
**********querylen:10, count:10, query:`'a' = "3"`
`'a' = "3", 'b' = "2", 'c' = "1"`
skk>a=4
`a=4`
, query:`'a' = "4"`, command:``, count:9
**********querylen:10, count:10, query:`'a' = "4"`
`'a' = "4", 'b' = "2", 'c' = "1"`
skk>a=5
`a=5`
, query:`'a' = "5"`, command:``, count:9
**********querylen:10, count:10, query:`'a' = "5"`
`'a' = "5", 'b' = "3", 'c' = "2"`
skk>b=1
`b=1`
, query:`'b' = "1"`, command:``, count:9
**********querylen:10, count:10, query:`'b' = "1"`
`'a' = "1", 'b' = "1", 'c' = "1"`
`'a' = "2", 'b' = "1", 'c' = "1"`
skk>b=2
`b=2`
, query:`'b' = "2"`, command:``, count:9
**********querylen:10, count:10, query:`'b' = "2"`
`'a' = "3", 'b' = "2", 'c' = "1"`
`'a' = "4", 'b' = "2", 'c' = "1"`
skk>b=3
`b=3`
, query:`'b' = "3"`, command:``, count:9
**********querylen:10, count:10, query:`'b' = "3"`
`'a' = "5", 'b' = "3", 'c' = "2"`
`'a' = "6", 'b' = "3", 'c' = "2"`
skk>b=4
`b=4`
, query:`'b' = "4"`, command:``, count:9
**********querylen:10, count:10, query:`'b' = "4"`
`'a' = "7", 'b' = "4", 'c' = "2"`
`'a' = "8", 'b' = "4", 'c' = "2"`
skk>quit
`quit`
, query:`'quit'`, command:``, count:6
**********querylen:10, count:7, query:`'quit'`
$ 

Seuraavassa vielä uudemman terttuutil1:n tuloste, tällä kertaa DEBUG66:lla

$ ./terttuutil1 --query
added set `headerfields`
added set `linesfields`
added set `header`
added set `lines`
added set `user`
added set `log`
added set `session`
skk>a
commandlen:2
command:`a`, query:`'a'`
added set `project`
Deleting chain `project`
Chain `project` deleted
Adding chain `project`, set `'a' = "1"`
Adding chain `project`, set `'a' = "2"`
Adding chain `project`, set `'a' = "3"`
Adding chain `project`, set `'a' = "4"`
Adding chain `project`, set `'a' = "5"`
Adding chain `project`, set `'a' = "6"`
Adding chain `project`, set `'a' = "7"`
Adding chain `project`, set `'a' = "8"`
Restoring chain `project`, set `'a' = "8"`
0*`'a' = "8"`
Restoring chain `project`, set `'a' = "7"`
1*`'a' = "7"`
Restoring chain `project`, set `'a' = "6"`
2*`'a' = "6"`
Restoring chain `project`, set `'a' = "5"`
3*`'a' = "5"`
Restoring chain `project`, set `'a' = "4"`
4*`'a' = "4"`
Restoring chain `project`, set `'a' = "3"`
5*`'a' = "3"`
Restoring chain `project`, set `'a' = "2"`
6*`'a' = "2"`
Restoring chain `project`, set `'a' = "1"`
7*`'a' = "1"`
skk>b
commandlen:2
command:`b`, query:`'b'`
Deleting chain `project`
Deleting:'a' = "8"
Deleting:'a' = "7"
Deleting:'a' = "6"
Deleting:'a' = "5"
Deleting:'a' = "4"
Deleting:'a' = "3"
Deleting:'a' = "2"
Deleting:'a' = "1"
Chain `project` deleted
Adding chain `project`, set `'b' = "1"`
Adding chain `project`, set `'b' = "2"`
Adding chain `project`, set `'b' = "3"`
Adding chain `project`, set `'b' = "4"`
Restoring chain `project`, set `'b' = "4"`
0*`'b' = "4"`
Restoring chain `project`, set `'b' = "3"`
1*`'b' = "3"`
Restoring chain `project`, set `'b' = "2"`
2*`'b' = "2"`
Restoring chain `project`, set `'b' = "1"`
3*`'b' = "1"`
skk>c
commandlen:2
command:`c`, query:`'c'`
Deleting chain `project`
Deleting:'b' = "4"
Deleting:'b' = "3"
Deleting:'b' = "2"
Deleting:'b' = "1"
Chain `project` deleted
Adding chain `project`, set `'c' = "1"`
Adding chain `project`, set `'c' = "2"`
Restoring chain `project`, set `'c' = "2"`
0*`'c' = "2"`
Restoring chain `project`, set `'c' = "1"`
1*`'c' = "1"`
skk>     

Ja vielä mallirivejä ilman DEBUG66:sta: Näistä huomaa helposti, että tuplatietueiden poisto toimii, mutta lajittelu (sort) ei vielä toimi.

$ ./terttuutil1 --query
skk>a
commandlen:2
command:`a`, query:`'a'`
0*`'a' = "8"`
1*`'a' = "7"`
2*`'a' = "6"`
3*`'a' = "5"`
4*`'a' = "4"`
5*`'a' = "3"`
6*`'a' = "2"`
7*`'a' = "1"`
skk>b
commandlen:2
command:`b`, query:`'b'`
0*`'b' = "4"`
1*`'b' = "3"`
2*`'b' = "2"`
3*`'b' = "1"`
skk>c
commandlen:2
command:`c`, query:`'c'`
0*`'c' = "2"`
1*`'c' = "1"`
skk>a=1
commandlen:4
command:`a=1`, query:`'a' = "1"`
0*`'a' = "1"`
skk>b=2
commandlen:4
command:`b=2`, query:`'b' = "2"`
0*`'b' = "2"`
skk>c=3
commandlen:4
command:`c=3`, query:`'c' = "3"`
skk>b=1
commandlen:4
command:`b=1`, query:`'b' = "1"`
0*`'b' = "1"`
skk>b=2
commandlen:4
command:`b=2`, query:`'b' = "2"`
0*`'b' = "2"`
skk>b=3
commandlen:4
command:`b=3`, query:`'b' = "3"`
0*`'b' = "3"`
skk>exit

Tässä vielä uudempi testimateriaalien hakuja sisältävä lista, jossa lajittelukin toimii:

$ ./terttuutil1 --query --filenameterttuutil1.skk
skk>a
commandlen:3
command:`a`, query:`'a'`
0*`'a' = "1"`
1*`'a' = "2"`
2*`'a' = "3"`
3*`'a' = "4"`
4*`'a' = "5"`
5*`'a' = "6"`
6*`'a' = "7"`
7*`'a' = "8"`
skk>b
commandlen:3
command:`b`, query:`'b'`
0*`'b' = "1"`
1*`'b' = "2"`
2*`'b' = "3"`
3*`'b' = "4"`
skk>c
commandlen:3
command:`c`, query:`'c'`
0*`'c' = "1"`
1*`'c' = "2"`
skk>exit
commandlen:6
$ 

Seuraavaksi jatkan satunnaisten lippujen arpomista. Käyn läpi newressu koodin lippujen osalta: Ensimmäisenä pätkä lippujen talletusrakenteesta. Seuraava kuva listaa maailman maiden liput, ja niiden erilaiset valintakriteerit. Jos esimerkiksi haluamme “arvontaan” suomen lipun voimme tehdä sen newressun komentorivioptioilla –fi, –fin, –finland, –Fi, –Fin, –Finland, –FI, –FIN, –FINLAND. Toivon että myös –🇫🇮 tulee olemaan mahdollinen. Huomaa että isolla kirjoitetut ovat suurempia ryhmiä, joihin kuuluu useampia maita, esimerkiksi suomen –FINLAND tapauksessa ryhmään kuuluu ahvenanmaa (åland). Muita muita ryhmiä, joihin suomi tässä yhteydessä kuuluu ovat –EU, –EUROPE ja –NORDIC. Jos halutaan kaikki ryhmään kuuluvat kirjoitetaan komentorivioptioksi ryhmän nimi. Tässä myös muita kuin standardia asciita sisältävät maat on kirjoitettu pelkällä asciilla, kuten åland = aland ja Curaçao = curacao.

Ensimmäinen kaksi merkkinen ascii “sana” (esimerkiksi suomen tapauksessa fi) on kaksi merkkinen isokoodi, ja kolmemerkkinen sana (suomen tapauksessa fin) on kolmemerkkinen isokoodi. Seuraavana iso-koodien jälkeen tuleva merkkijono on maan nimi ilman välilyöntejä (finland), ja loput isolla kirjoitetut ovat ryhmiä, joihin rivin maa kuuluu. Tässä lopun isoilla kirjoitetulla FINLAND sanalla suomi yhdistetään ahvenanmaahan. Lopussa on vielä ryhmät EU, EUROPE ja NORDIC.

struct idflags { // 202307 JariK
  unsigned char *id;
  unsigned char *flags;
} idsflags[] = {
  { "🇫🇮", "🇫🇮: fi, fin, finland, EU, EUROPE, NORDIC, FINLAND" },
  { "🇦🇽", "🇦🇽: ax, ala, åland, aland, EU, EUROPE, NORDIC, FINLAND" },
  { "🇸🇪", "🇸🇪: se, swe, sweden, EU, EUROPE, NORDIC" },
  { "🇳🇴", "🇳🇴: no, nor, norway, EU, EUROPE, NORDIC" },
  { "🇩🇰", "🇩🇰: dk, dnk, denmark, EU, EUROPE, NORDIC, DENMARK" },
  { "🇫🇴", "🇫🇴: fo, fro, FaroeIslands, EU, EUROPE, NORDIC, DENMARK" },
  { "🇮🇸", "🇮🇸: is, isl, iceland, EU, EUROPE, NORDIC" },
  { "🇪🇪", "🇪🇪: ee, est, estonia, EU, EUROPE, BALTIC" },
  { "🇱🇻", "🇱🇻: lv, lva, latvia, EU, EUROPE, BALTIC" },
  { "🇱🇹", "🇱🇹: lt, ltu, lithuania, EU, EUROPE, BALTIC" },
...
// you can find full structure in the
// beginning of this article or in the
// full source of newressu in the end
// of the article.
...
  { "🇨🇻", "🇨🇻: cv, cpv, CapeVerde, AFRICA" },
  { "🇪🇭", "🇪🇭: eh, esh, WesternSahara, AFRICA" },
  { "🇸🇹", "🇸🇹: st, stp, SãoToméandPríncipe, saotomeandprincipe, AFRICA" },
  { "🇸🇨", "🇸🇨: sc, syc, Seychelles, AFRICA" },

  { "🇦🇺", "🇦🇺: au, aus, Australia" },
};

Tässä esimerkiksi komento suomen ja ruotsin lippujen arpomiseksi: (komento ei vielä tätä kirjoittaessani arponut lippuja oikeasti, mutta nyt kun luet tätä olen varmasti koodannut arpomisen loppuun…)

$ ./newressu --fi --se --flags
00000 🇫🇮🇫🇮🇫🇮🇸🇪🇸🇪🇫🇮🇫🇮🇸🇪🇫🇮🇸🇪🇫🇮🇫🇮🇸🇪🇸🇪🇸🇪🇫🇮🇫🇮🇸🇪🇸🇪🇸🇪🇫🇮🇫🇮🇸🇪🇸🇪🇸🇪🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮
00001 🇫🇮🇫🇮🇸🇪🇸🇪🇸🇪🇫🇮🇫🇮🇸🇪🇸🇪🇸🇪🇫🇮🇸🇪🇫🇮🇸🇪🇸🇪🇫🇮🇫🇮🇫🇮🇫🇮🇸🇪🇫🇮🇫🇮🇫🇮🇸🇪🇸🇪🇸🇪🇸🇪🇫🇮🇸🇪🇸🇪
00002 🇫🇮🇸🇪🇫🇮🇫🇮🇸🇪🇸🇪🇸🇪🇸🇪🇸🇪🇸🇪🇫🇮🇫🇮🇸🇪🇫🇮🇸🇪🇸🇪🇫🇮🇫🇮🇸🇪🇸🇪🇫🇮🇫🇮🇸🇪🇸🇪🇸🇪🇸🇪🇫🇮🇫🇮🇫🇮🇸🇪
00003 🇫🇮🇸🇪🇸🇪🇸🇪🇸🇪🇫🇮🇸🇪🇫🇮🇫🇮🇸🇪🇫🇮🇫🇮🇫🇮🇫🇮🇸🇪🇫🇮🇫🇮🇸🇪🇫🇮🇸🇪🇫🇮🇫🇮🇫🇮🇫🇮🇸🇪🇸🇪🇫🇮🇸🇪🇸🇪🇸🇪
00004 🇫🇮🇸🇪🇸🇪🇫🇮🇸🇪🇫🇮🇫🇮🇸🇪🇫🇮🇸🇪🇫🇮🇫🇮🇸🇪🇸🇪🇸🇪🇫🇮🇫🇮🇫🇮🇫🇮🇸🇪🇸🇪🇫🇮🇫🇮🇫🇮🇸🇪🇫🇮🇫🇮🇸🇪🇫🇮🇸🇪
00005 🇸🇪🇸🇪🇸🇪🇸🇪🇸🇪🇫🇮🇫🇮🇸🇪🇸🇪🇸🇪🇸🇪🇸🇪🇫🇮🇫🇮🇸🇪🇫🇮🇫🇮🇫🇮🇫🇮🇸🇪🇸🇪🇫🇮🇸🇪🇸🇪🇸🇪🇫🇮🇫🇮🇫🇮🇫🇮🇸🇪
00006 🇫🇮🇸🇪🇸🇪🇸🇪🇸🇪🇫🇮🇸🇪🇸🇪🇫🇮🇸🇪🇫🇮🇫🇮🇫🇮🇫🇮🇸🇪🇫🇮🇸🇪🇸🇪🇫🇮🇸🇪🇫🇮🇫🇮🇫🇮🇸🇪🇸🇪🇸🇪🇫🇮🇸🇪🇫🇮🇸🇪
00007 🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇸🇪🇸🇪🇸🇪🇸🇪🇫🇮🇫🇮🇸🇪🇸🇪🇫🇮🇫🇮🇫🇮🇸🇪🇸🇪🇫🇮🇸🇪🇸🇪🇫🇮🇸🇪🇸🇪🇸🇪🇸🇪🇸🇪🇸🇪
00008 🇸🇪🇫🇮🇫🇮🇸🇪🇸🇪🇫🇮🇸🇪🇫🇮🇫🇮🇸🇪🇸🇪🇫🇮🇸🇪🇸🇪🇸🇪🇫🇮🇫🇮🇸🇪🇸🇪🇸🇪🇫🇮🇫🇮🇫🇮🇫🇮🇸🇪🇫🇮🇸🇪🇸🇪🇫🇮🇸🇪
00009 🇫🇮🇫🇮🇸🇪🇸🇪🇸🇪🇫🇮🇫🇮🇸🇪🇫🇮🇸🇪🇫🇮🇸🇪🇸🇪🇸🇪🇸🇪🇫🇮🇫🇮🇫🇮🇫🇮🇸🇪🇫🇮🇸🇪🇸🇪🇫🇮🇸🇪🇫🇮🇸🇪🇫🇮🇸🇪🇸🇪
$

Vielä pohjoismaiden (–NORDIC) maiden komentoesimerkki:

$ ./newressu --nordic --flags
00000 🇮🇸🇫🇴🇳🇴🇳🇴🇳🇴🇦🇽🇳🇴🇳🇴🇸🇪🇫🇴🇫🇴🇦🇽🇫🇴🇳🇴🇳🇴🇬🇱🇮🇸🇳🇴🇳🇴🇳🇴🇬🇱🇳🇴🇳🇴🇮🇸🇳🇴🇬🇱🇫🇮🇸🇪🇸🇪🇮🇸
00001 🇫🇮🇳🇴🇩🇰🇳🇴🇳🇴🇫🇮🇬🇱🇩🇰🇸🇪🇳🇴🇫🇮🇸🇪🇩🇰🇫🇮🇮🇸🇦🇽🇫🇴🇳🇴🇮🇸🇩🇰🇫🇴🇮🇸🇮🇸🇸🇪🇳🇴🇸🇪🇦🇽🇬🇱🇦🇽🇬🇱
00002 🇩🇰🇫🇮🇮🇸🇫🇮🇩🇰🇬🇱🇮🇸🇮🇸🇬🇱🇬🇱🇸🇪🇮🇸🇦🇽🇦🇽🇬🇱🇫🇴🇫🇴🇳🇴🇬🇱🇮🇸🇦🇽🇦🇽🇦🇽🇫🇴🇬🇱🇸🇪🇩🇰🇫🇮🇩🇰🇮🇸
00003 🇩🇰🇬🇱🇫🇮🇫🇴🇳🇴🇸🇪🇳🇴🇫🇴🇮🇸🇫🇴🇫🇮🇩🇰🇮🇸🇫🇮🇸🇪🇬🇱🇬🇱🇩🇰🇩🇰🇦🇽🇸🇪🇬🇱🇫🇴🇫🇴🇮🇸🇸🇪🇦🇽🇫🇴🇬🇱🇮🇸
00004 🇦🇽🇳🇴🇫🇴🇩🇰🇸🇪🇳🇴🇩🇰🇳🇴🇦🇽🇫🇴🇸🇪🇮🇸🇬🇱🇳🇴🇫🇴🇸🇪🇫🇴🇩🇰🇮🇸🇬🇱🇫🇴🇦🇽🇩🇰🇫🇮🇳🇴🇫🇮🇫🇴🇮🇸🇫🇴🇳🇴
00005 🇫🇴🇦🇽🇬🇱🇩🇰🇫🇴🇫🇮🇦🇽🇫🇮🇦🇽🇳🇴🇦🇽🇦🇽🇫🇴🇦🇽🇮🇸🇦🇽🇬🇱🇳🇴🇬🇱🇬🇱🇸🇪🇫🇴🇦🇽🇬🇱🇫🇴🇬🇱🇸🇪🇬🇱🇮🇸🇳🇴
00006 🇸🇪🇸🇪🇩🇰🇩🇰🇮🇸🇫🇮🇫🇮🇸🇪🇮🇸🇬🇱🇮🇸🇸🇪🇫🇴🇦🇽🇸🇪🇸🇪🇮🇸🇩🇰🇫🇴🇦🇽🇫🇮🇳🇴🇫🇮🇩🇰🇬🇱🇫🇮🇮🇸🇬🇱🇦🇽🇬🇱
00007 🇮🇸🇦🇽🇫🇮🇮🇸🇩🇰🇬🇱🇫🇴🇮🇸🇫🇮🇮🇸🇬🇱🇸🇪🇩🇰🇸🇪🇫🇴🇮🇸🇸🇪🇫🇴🇩🇰🇩🇰🇫🇮🇳🇴🇦🇽🇫🇴🇸🇪🇳🇴🇳🇴🇮🇸🇳🇴🇩🇰
00008 🇫🇴🇬🇱🇦🇽🇬🇱🇫🇴🇸🇪🇬🇱🇦🇽🇦🇽🇫🇴🇫🇴🇦🇽🇫🇮🇫🇮🇫🇴🇬🇱🇬🇱🇬🇱🇸🇪🇸🇪🇬🇱🇫🇮🇬🇱🇳🇴🇳🇴🇳🇴🇳🇴🇬🇱🇸🇪🇬🇱
00009 🇫🇮🇳🇴🇮🇸🇮🇸🇬🇱🇦🇽🇬🇱🇮🇸🇦🇽🇮🇸🇩🇰🇦🇽🇦🇽🇫🇴🇸🇪🇫🇮🇫🇮🇬🇱🇸🇪🇦🇽🇫🇴🇩🇰🇦🇽🇬🇱🇳🇴🇫🇴🇳🇴🇬🇱🇩🇰🇬🇱
$ 

Tässä vielä eu:n lippuesimerkki:

$ ./newressu --eu --flags
00000 🇱🇺🇦🇹🇵🇱🇪🇪🇸🇰🇩🇪🇸🇪🇧🇪🇮🇹🇧🇪🇧🇪🇵🇹🇨🇿🇵🇱🇩🇪🇷🇴🇫🇷🇱🇻🇲🇹🇪🇪🇳🇱🇲🇹🇬🇷🇪🇸🇵🇹🇫🇮🇲🇹🇵🇹🇸🇰🇳🇱
00001 🇭🇷🇧🇪🇸🇪🇨🇾🇵🇱🇩🇪🇮🇹🇸🇮🇭🇺🇩🇰🇫🇮🇬🇷🇲🇹🇧🇪🇮🇹🇫🇮🇮🇪🇮🇹🇫🇷🇳🇱🇸🇰🇩🇪🇫🇷🇧🇪🇬🇷🇵🇹🇸🇰🇭🇷🇧🇬🇫🇷
00002 🇸🇮🇸🇮🇫🇷🇸🇰🇱🇺🇵🇹🇬🇷🇸🇮🇸🇮🇵🇹🇲🇹🇷🇴🇱🇻🇮🇪🇧🇬🇪🇸🇨🇾🇮🇪🇱🇺🇱🇹🇲🇹🇸🇰🇫🇷🇭🇺🇩🇰🇭🇺🇱🇻🇱🇹🇩🇰🇮🇹
00003 🇫🇮🇸🇪🇵🇱🇸🇰🇮🇹🇫🇷🇳🇱🇸🇰🇭🇷🇭🇺🇸🇮🇩🇰🇮🇪🇧🇪🇫🇷🇨🇾🇲🇹🇭🇺🇩🇰🇮🇹🇭🇷🇫🇮🇧🇪🇮🇹🇧🇪🇷🇴🇱🇻🇫🇮🇧🇪🇪🇸
00004 🇬🇷🇸🇰🇱🇺🇧🇬🇮🇪🇩🇪🇫🇮🇸🇰🇷🇴🇮🇪🇨🇾🇬🇷🇭🇺🇸🇪🇨🇾🇩🇰🇵🇱🇱🇹🇮🇹🇱🇺🇭🇺🇸🇪🇪🇸🇪🇸🇪🇸🇵🇱🇱🇺🇬🇷🇸🇮🇷🇴
00005 🇩🇰🇭🇷🇸🇪🇸🇪🇸🇮🇪🇪🇮🇹🇨🇾🇳🇱🇵🇹🇫🇮🇮🇪🇪🇸🇸🇰🇨🇿🇪🇪🇫🇮🇳🇱🇮🇹🇲🇹🇪🇸🇫🇮🇫🇮🇧🇪🇩🇰🇸🇪🇧🇪🇪🇪🇱🇹🇨🇿
00006 🇵🇱🇨🇿🇸🇮🇮🇹🇨🇾🇫🇮🇩🇪🇪🇪🇳🇱🇪🇪🇭🇷🇷🇴🇱🇹🇩🇪🇸🇮🇱🇻🇱🇻🇱🇻🇭🇷🇵🇹🇨🇾🇭🇺🇵🇹🇬🇷🇪🇪🇪🇸🇩🇰🇱🇻🇦🇹🇭🇺
00007 🇪🇸🇸🇮🇮🇹🇫🇷🇨🇿🇧🇬🇧🇬🇬🇷🇸🇮🇭🇷🇬🇷🇪🇪🇮🇹🇷🇴🇸🇰🇸🇪🇨🇾🇳🇱🇦🇹🇸🇪🇬🇷🇸🇮🇪🇪🇪🇪🇦🇹🇪🇸🇪🇪🇭🇺🇪🇪🇲🇹
00008 🇷🇴🇲🇹🇸🇰🇸🇮🇪🇪🇮🇹🇪🇪🇵🇹🇩🇰🇸🇰🇨🇾🇱🇻🇬🇷🇳🇱🇫🇷🇪🇸🇵🇹🇩🇪🇨🇾🇸🇰🇮🇹🇭🇷🇸🇮🇮🇹🇦🇹🇭🇷🇨🇿🇦🇹🇸🇮🇫🇷
00009 🇸🇪🇦🇹🇳🇱🇸🇮🇪🇸🇸🇮🇩🇰🇫🇮🇸🇪🇱🇻🇫🇮🇦🇹🇩🇰🇱🇺🇫🇷🇵🇹🇳🇱🇲🇹🇸🇰🇵🇱🇨🇾🇭🇷🇫🇷🇸🇮🇪🇪🇳🇱🇪🇸🇮🇹🇨🇾🇷🇴
$

Ja vielä esimerkki lipun kuvalla:

$ ./newressu --🇫🇮 --flags
00000 🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮
00001 🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮
00002 🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮
00003 🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮
00004 🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮
00005 🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮
00006 🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮
00007 🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮
00008 🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮
00009 🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮🇫🇮
$

AFRICA:n lippuja: (nyt on ruotsin lippu alarivillä eksynyt. se oli itseasiassa komennossa…)

$ ./newressu --AFRICA --flags
00000 🇺🇬🇹🇬🇲🇿🇨🇮🇪🇹🇸🇱🇧🇮🇧🇮🇲🇿🇬🇳🇲🇺🇾🇹🇱🇷🇱🇾🇲🇿🇿🇦🇸🇱🇺🇬🇩🇿🇬🇲🇪🇹🇬🇼🇨🇩🇺🇬🇱🇸🇩🇿🇬🇦🇸🇱🇲🇺🇲🇷
00001 🇦🇴🇷🇼🇿🇼🇲🇺🇲🇼🇨🇩🇸🇳🇨🇻🇪🇹🇲🇷🇨🇩🇪🇹🇳🇪🇿🇦🇿🇲🇳🇪🇲🇺🇬🇦🇸🇳🇸🇳🇧🇮🇬🇳🇪🇷🇪🇬🇱🇸🇹🇿🇩🇯🇨🇻🇨🇮🇬🇭
00002 🇨🇩🇱🇾🇰🇲🇱🇷🇱🇾🇧🇯🇲🇷🇹🇬🇸🇩🇪🇭🇸🇸🇲🇺🇹🇩🇪🇷🇨🇮🇿🇲🇱🇸🇧🇯🇪🇷🇺🇬🇳🇦🇪🇬🇨🇩🇾🇹🇲🇿🇺🇬🇸🇳🇨🇻🇹🇩🇩🇿
00003 🇲🇺🇧🇯🇳🇦🇨🇮🇦🇴🇷🇪🇨🇩🇪🇹🇷🇼🇰🇪🇿🇦🇧🇯🇨🇲🇹🇬🇩🇯🇸🇱🇿🇲🇨🇩🇨🇮🇨🇩🇨🇲🇦🇴🇱🇾🇸🇸🇰🇪🇬🇭🇱🇸🇲🇺🇲🇱🇸🇸
00004 🇰🇲🇬🇭🇲🇿🇩🇯🇬🇭🇿🇲🇹🇬🇩🇿🇧🇼🇱🇷🇧🇮🇬🇦🇩🇿🇲🇿🇹🇿🇸🇨🇱🇷🇿🇼🇲🇱🇷🇪🇷🇪🇧🇫🇸🇱🇬🇳🇦🇴🇲🇺🇨🇻🇨🇲🇹🇿🇹🇳
00005 🇲🇿🇷🇪🇪🇬🇺🇬🇧🇯🇨🇻🇹🇩🇪🇷🇲🇿🇩🇯🇨🇩🇰🇪🇨🇩🇨🇬🇨🇩🇸🇩🇲🇺🇱🇾🇲🇺🇪🇷🇧🇼🇾🇹🇸🇸🇲🇺🇹🇳🇿🇲🇸🇩🇸🇨🇷🇪🇩🇿
00006 🇲🇺🇬🇼🇸🇸🇧🇮🇲🇺🇪🇷🇪🇹🇲🇷🇨🇬🇲🇺🇬🇳🇲🇦🇸🇩🇰🇪🇸🇳🇪🇬🇸🇿🇿🇼🇬🇼🇬🇭🇷🇼🇸🇴🇪🇷🇧🇯🇬🇲🇨🇲🇨🇮🇸🇨🇰🇪🇲🇷
00007 🇪🇬🇸🇳🇬🇦🇬🇭🇸🇿🇱🇾🇦🇴🇨🇮🇨🇩🇹🇳🇪🇬🇬🇲🇲🇿🇹🇬🇬🇲🇬🇲🇱🇸🇬🇳🇧🇫🇩🇿🇸🇱🇲🇬🇱🇾🇱🇷🇨🇩🇪🇷🇸🇳🇱🇷🇪🇹🇹🇬
00008 🇲🇼🇱🇾🇸🇴🇲🇿🇿🇼🇲🇬🇱🇸🇨🇻🇸🇸🇦🇴🇸🇸🇷🇼🇬🇦🇷🇪🇸🇸🇨🇬🇱🇾🇲🇦🇱🇾🇧🇼🇸🇱🇨🇲🇬🇼🇳🇪🇸🇨🇳🇪🇲🇱🇪🇷🇷🇪🇬🇳
00009 🇪🇷🇸🇸🇬🇭🇨🇩🇨🇻🇬🇲🇲🇷🇲🇺🇸🇨🇲🇷🇹🇩🇿🇦🇸🇱🇺🇬🇨🇲🇸🇸🇨🇮🇷🇪🇲🇺🇰🇲🇲🇷🇳🇪🇧🇯🇳🇪🇨🇲🇸🇸🇹🇩🇨🇻🇸🇸🇸🇸
$

Etelä afrikka: (SOUTHAFRICA)

$ ./newressu --SOUTHAFRICA --flags
00000 🇱🇸🇧🇼🇱🇸🇳🇦🇸🇿🇧🇼🇿🇦🇿🇦🇳🇦🇸🇿🇸🇿🇸🇿🇸🇿🇿🇦🇸🇿🇿🇦🇧🇼🇱🇸🇱🇸🇳🇦🇧🇼🇧🇼🇧🇼🇸🇿🇱🇸🇧🇼🇧🇼🇿🇦🇿🇦🇱🇸
00001 🇳🇦🇧🇼🇳🇦🇸🇿🇱🇸🇸🇿🇳🇦🇸🇿🇧🇼🇳🇦🇿🇦🇳🇦🇿🇦🇧🇼🇧🇼🇸🇿🇱🇸🇧🇼🇳🇦🇸🇿🇿🇦🇧🇼🇧🇼🇧🇼🇳🇦🇿🇦🇳🇦🇧🇼🇿🇦🇱🇸
00002 🇿🇦🇸🇿🇧🇼🇱🇸🇸🇿🇳🇦🇿🇦🇧🇼🇱🇸🇳🇦🇿🇦🇸🇿🇧🇼🇿🇦🇸🇿🇳🇦🇱🇸🇱🇸🇱🇸🇱🇸🇳🇦🇧🇼🇿🇦🇱🇸🇸🇿🇱🇸🇱🇸🇸🇿🇿🇦🇸🇿
00003 🇳🇦🇳🇦🇧🇼🇸🇿🇳🇦🇳🇦🇸🇿🇱🇸🇱🇸🇱🇸🇧🇼🇧🇼🇳🇦🇸🇿🇱🇸🇸🇿🇸🇿🇧🇼🇱🇸🇸🇿🇸🇿🇳🇦🇱🇸🇳🇦🇳🇦🇱🇸🇧🇼🇸🇿🇸🇿🇸🇿
00004 🇿🇦🇿🇦🇧🇼🇸🇿🇱🇸🇳🇦🇧🇼🇿🇦🇳🇦🇸🇿🇱🇸🇱🇸🇧🇼🇱🇸🇸🇿🇱🇸🇿🇦🇿🇦🇱🇸🇱🇸🇿🇦🇱🇸🇳🇦🇳🇦🇳🇦🇳🇦🇿🇦🇧🇼🇳🇦🇳🇦
00005 🇿🇦🇱🇸🇱🇸🇧🇼🇧🇼🇿🇦🇱🇸🇧🇼🇳🇦🇧🇼🇿🇦🇿🇦🇸🇿🇧🇼🇱🇸🇳🇦🇧🇼🇸🇿🇱🇸🇸🇿🇸🇿🇱🇸🇳🇦🇳🇦🇱🇸🇳🇦🇿🇦🇿🇦🇧🇼🇧🇼
00006 🇿🇦🇿🇦🇱🇸🇿🇦🇳🇦🇿🇦🇳🇦🇧🇼🇸🇿🇳🇦🇳🇦🇱🇸🇸🇿🇱🇸🇧🇼🇸🇿🇱🇸🇿🇦🇿🇦🇧🇼🇧🇼🇧🇼🇿🇦🇧🇼🇧🇼🇧🇼🇧🇼🇱🇸🇸🇿🇱🇸
00007 🇿🇦🇧🇼🇸🇿🇸🇿🇳🇦🇱🇸🇿🇦🇧🇼🇳🇦🇳🇦🇳🇦🇳🇦🇸🇿🇱🇸🇳🇦🇧🇼🇳🇦🇸🇿🇱🇸🇸🇿🇿🇦🇱🇸🇧🇼🇳🇦🇳🇦🇳🇦🇧🇼🇳🇦🇿🇦🇧🇼
00008 🇿🇦🇿🇦🇧🇼🇱🇸🇱🇸🇿🇦🇱🇸🇿🇦🇸🇿🇱🇸🇱🇸🇿🇦🇿🇦🇱🇸🇸🇿🇿🇦🇳🇦🇿🇦🇿🇦🇧🇼🇸🇿🇱🇸🇳🇦🇿🇦🇸🇿🇳🇦🇳🇦🇸🇿🇸🇿🇳🇦
00009 🇸🇿🇳🇦🇸🇿🇸🇿🇸🇿🇱🇸🇳🇦🇳🇦🇧🇼🇧🇼🇿🇦🇱🇸🇳🇦🇸🇿🇸🇿🇿🇦🇱🇸🇿🇦🇧🇼🇸🇿🇱🇸🇱🇸🇱🇸🇱🇸🇱🇸🇳🇦🇸🇿🇱🇸🇸🇿🇧🇼
$

Vielä lippuja –all optiolla: huomaa EquatorialGuinea:n lippu “cq”, joka tarkoittaa että isokoodilla cq ei löytynyt utf8 lippua. Lisätty muutamia puuttuvia maita.

$ ./newressu --all --flags
00000 🇯🇪🇨🇷🇲🇽🇨🇿🇰🇼🇨🇴🇵🇫🇸🇸🇬🇳🇹🇰🇻🇳🇰🇵🇧🇾🇱🇾🇺🇾🇰🇪🇪🇪🇵🇪🇦🇸🇵🇹🇬🇺🇸🇱🇬🇱🇰🇿🇲🇺🇫🇯🇬🇹🇷🇸🇹🇯🇨🇮
00001 🇫🇷🇻🇪🇰🇮🇩🇯🇬🇪🇪🇬🇸🇲🇴🇲🇰🇮🇰🇵🇸🇬🇵🇼🇸🇧🇻🇮🇩🇴🇨🇷🇵🇸🇱🇷🇩🇪🇧🇫🇾🇪🇬🇲🇪🇬🇨🇼🇬🇸🇪🇨🇫🇷🇰🇮🇾🇪🇨🇶
00002 🇹🇿🇦🇫🇵🇷🇨🇳🇲🇺🇲🇶🇸🇻🇾🇹🇵🇼🇳🇦🇯🇪🇹🇿🇧🇼🇳🇺🇭🇰🇹🇲🇲🇷🇵🇳🇨🇾🇾🇪🇧🇸🇰🇮🇲🇺🇧🇻🇷🇸🇸🇮🇾🇪🇰🇵🇨🇫🇮🇷
00003 🇮🇪🇬🇮🇰🇼🇵🇷🇸🇷🇬🇳🇫🇰🇸🇾🇩🇿🇧🇫🇻🇮🇿🇼🇫🇷🇭🇰🇱🇷🇦🇹🇹🇬🇸🇳🇳🇵🇧🇲🇹🇿🇧🇦🇦🇹🇱🇰🇷🇼🇹🇯🇳🇨🇧🇸🇧🇻🇨🇱
00004 🇰🇵🇮🇶🇰🇼🇰🇬🇸🇴🇨🇫🇵🇫🇫🇷🇸🇴🇪🇸🇱🇸🇪🇭🇨🇩🇦🇹🇶🇦🇩🇲🇬🇳🇲🇻🇨🇷🇪🇸🇰🇪🇰🇾🇱🇻🇮🇳🇬🇺🇨🇶🇧🇶🇰🇲🇳🇷🇹🇩
00005 🇺🇿🇩🇲🇵🇳🇨🇿🇧🇮🇧🇴🇪🇪🇲🇹🇬🇾🇸🇾🇸🇧🇵🇸🇱🇷🇨🇦🇬🇩🇻🇨🇮🇷🇲🇶🇵🇭🇸🇪🇵🇹🇸🇮🇬🇬🇱🇾🇲🇿🇵🇲🇺🇾🇼🇸🇸🇷🇬🇪
00006 🇱🇮🇫🇲🇾🇹🇪🇹🇨🇿🇻🇪🇮🇩🇵🇪🇲🇱🇨🇷🇵🇹🇱🇸🇲🇿🇨🇴🇨🇶🇳🇿🇦🇪🇬🇷🇪🇨🇸🇽🇸🇪🇨🇩🇪🇨🇼🇫🇮🇷🇱🇧🇩🇰🇮🇷🇻🇪🇧🇧
00007 🇵🇾🇨🇬🇺🇾🇲🇹🇸🇦🇨🇱🇨🇩🇹🇱🇵🇾🇳🇺🇬🇮🇸🇳🇫🇰🇿🇦🇦🇼🇲🇰🇪🇬🇦🇴🇧🇧🇹🇩🇯🇵🇺🇾🇸🇪🇹🇩🇬🇵🇮🇩🇸🇴🇪🇸🇺🇾🇨🇲
00008 🇬🇵🇲🇿🇹🇨🇹🇱🇹🇹🇧🇼🇾🇪🇮🇷🇲🇱🇰🇪🇹🇫🇬🇩🇱🇮🇰🇿🇾🇪🇨🇾🇨🇼🇬🇺🇳🇺🇦🇶🇹🇿🇲🇩🇲🇿🇲🇦🇸🇳🇸🇻🇫🇷🇲🇺🇹🇳🇩🇯
00009 🇲🇪🇲🇫🇳🇪🇭🇳🇫🇷🇵🇳🇲🇽🇷🇸🇨🇩🇲🇻🇧🇯🇰🇮🇰🇪🇵🇪🇸🇻🇵🇸🇱🇮🇫🇰🇲🇱🇯🇵🇵🇪🇸🇽🇷🇴🇮🇪🇸🇲🇲🇲🇮🇴🇾🇹🇨🇮🇺🇸
$

Seuraavassa rutiini, jolle kaikki komentoriviparametrit annetaan, ja funktio merkkaa ne id:t (tässä tapauksessa liput), jotka komentoriviparametri valitsee. Esimerkiksi ‘fi’ kääntää suomen kytkimen, samalla tavoin kun aiemminkin komentorivikytkimet ovat toimineet. Jos komentorivillä on useampia kytkimiä, jokaisella niistä kutsutaan tätä erikseen. Jos ensin ensimmäinen kytkin valitsee ‘finland’ lipun, se merkitään ensimmäisellä kutsulla, ja jos sen jälkeen valitaan ‘sweden’, se merkitään toisella kutsulla. Näin sekä finland ja sweden tulevat valituiksi. Jos komentoriviparametreissä on –all optio, se käynnistää “ALL” option joka kääntää kaikki “liput” vastakkaisiksi.

Rutiini tarkistaa myös että optio on kokonainen sana, eli että sanan jälkeen on välilyönti, pilkku, kaksoispiste (alun lipunkuva) tai merkkijonon loppu. Samoin merkkijoa pitää edeltää merkkijonon alku, välilyönti, pilkku tai kaksoispiste.

Varsinaiset objektikohtaiset liput ovat flagids kentässä. Siinä on tilaa jokaiselle lipulle (1 merkki/lippu, arvot 1/0, tulostetaan/ei tulosteta).

int newressu_toggleflag(unsigned char *flag)
{
  int c, ok;
  unsigned char *p, *q;
  
  if(!strcmp(flag, "ALL")) {
    idsall = !idsall;
    for(c = 0; c < sizeof(idsflags) / sizeof(idsflags[0]); c++) {
      flagids[c] = idsall;
    }
    flagidflags = 1; // display flags
    ok = 1; // one flag toggled
  } else {
    ok = 0;
    for(c = 0; c < sizeof(idsflags) / sizeof(idsflags[0]); c++) {
      p = strstr(idsflags[c].flags, flag);
      q = idsflags[c].flags;
      int fl = strlen(flag);
      if( p != NULL && // string found
	  (*(p + fl) == ' ' ||  *(p + fl) == ',' || *(p + fl) == ':' || *(p + fl) == '\0') && // character after string
	  ( (p == q) || ( *(p - 1) == ' ' || *(p - 1) == ',' || *(p - 1) == ':' ) ) ) { // character before string
	flagids[c] = !flagids[c];
	//fprintf(stderr,"idsflags[%d].flags %s, toggled, value:%d\n", c, idsflags[c].flags, flagids[c]);
	fflush(stderr);
	flagidflags = 1; // display flags
	ok = 1; // one flag toggled
      }
    }
    //if(ok)
    //  fprintf(stdout,"\n");
  }
  return(ok);
}

int main(int argc, char *argv[])
{
....
  //
  // look thru command line parameters
  //

  for(c = 1; c < argc; c++) {

    int ok = 1;
    
    if(!strncmp("-", argv[c], 1)) {
....
      } else if(!strcmp("--flags", argv[c])) { // not ready

	// intentionally left empty
	
      } else if(!strcmp("--all", argv[c])) { // not ready
	if(flagsinit) {
	  idsall = 1;
	  newressu_toggleflag("ALL");
	  flagsinit = 0;
	}
	
      } else {
	ok = 0;
      }

      unsigned char buffer[32];
      int ok2 = 0;
      if(!strncmp(argv[c], "--", 2)) {
	if(flagsinit) {
	  idsall = 0;
	  newressu_toggleflag(buffer);
	  flagsinit = 0;
	}
	strcpy(buffer, argv[c] + 2); // as is
	if(newressu_toggleflag(buffer) == 1)
	  ok2 = 1;
	else {
	  strtolower(buffer); // all letters lowercase
	  if(newressu_toggleflag(buffer) == 1)
	    ok2 = 1;
	  else {
	    buffer[0] = toupper(buffer[0]); // first letter uppercase
	    if(newressu_toggleflag(buffer) == 1)
	      ok2 = 1;
	    else {
	      strtoupper(buffer); // all letters uppercase
	      if(newressu_toggleflag(buffer) == 1)
		ok2 = 1;
	    }
	  }
	}
      }
      if( (!flagflags && ok == 0) ||
	  (flagflags && ok == 0 && ok2 == 0)) {
	fprintf(stderr,"%s: invalid option %s\n", procname, argv[c]);
	exit(1);
      }
    } // if(!strncmp
  } // for(c = 0

  if(flagflags) {
    int first = 1;

    for(d = 0; d < sizeof(idsflags) / sizeof(idsflags[0]); d++) {
      if(flagids[d] == 1) {
	if(first) {
	  fprintf(stdout,"flags: ");
	  first = 0;
	}
	fprintf(stdout," %s",idsflags[d].id);
      }
    }
    
    if(!first)
      fprintf(stdout,"\n");
    
    digits = "🇦🇧🇨🇩🇪🇫🇬🇭🇮🇯🇰🇱🇲🇳🇴🇵🇶🇷🇸🇹🇺🇻🇼🇽🇾🇿";
    //digits = "🇫🇮🇸🇪🇳🇴🇩🇰🇪🇪🇱🇻🇱🇹";
    
    charspaces = 0;
    charwidth = 1;
    size = 5;
    type = 0;
    
  }		       

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

Lisätty uusi debukki DEBUG77, joka tulostaa yhteenvedon eri alueiden lipuista: tässä listaus sen tulostamasta raportista:

area:FINLAND, finland, åland, count:2
area:DENMARK, denmark, FaroeIslands, greenland, count:3
area:NORDIC, finland, åland, sweden, norway, denmark, FaroeIslands, iceland, greenland, count:8
area:BALTIC, estonia, latvia, lithuania, count:3
area:EU, finland, sweden, denmark, estonia, latvia, lithuania, austria, belgium, bulgaria, croatia, cyprus, czechrepublic, france, germany, Greece, Hungary, Ireland, Italy, Luxembourg, Malta, Netherlands, Poland, Portugal, Romania, Slovakia, Slovenia, Spain, count:27
area:EUROPE, finland, sweden, norway, denmark, iceland, estonia, latvia, lithuania, ukraine, albania, albania, Armenia, austria, Azerbaijan, belarus, belgium, bulgaria, croatia, cyprus, czechrepublic, france, Georgia, germany, Greece, Hungary, Ireland, Italy, Vatican, Kazakhstan, Liechtenstein, Luxembourg, Malta, moldova, monaco, montenegro, Netherlands, Poland, Portugal, Romania, Russia, sanmarino, serbia, Slovakia, Slovenia, Turkey, Spain, UnitedKingdom, count:47
area:ASIA, Armenia, Azerbaijan, cyprus, Georgia, Kazakhstan, Russia, Turkey, UnitedArabEmirates, Afghanistan, Bangladesh, Bahrain, Brunei, Bhutan, China, HongKong, Indonesia, Israel, India, Iraq, Iran, Jordan, Japan, Kyrgyzstan, Cambodia, SouthKorea, Kuwait, Laos, Lebanon, SriLanka, Mongolia, Macao, Maldives, Myanmar, Malaysia, NorthKorea, Nepal, Oman, Philippines, Pakistan, Palestine, Qatar, SaudiArabia, Singapore, Syria, Thailand, Tajikistan, EastTimor, Turkmenistan, Taiwan, Uzbekistan, Vietnam, Yemen, Egypt, count:53
area:AFRICA, Nigeria, Ethiopia, Eritrea, Congo, DemocraticCongo, Tanzania, SouthAfrica, Kenya, Uganda, SouthSudan, sudan, Algeria, Egypt, Libya, Madeira, Morocco, Angola, Ghana, Mozambique, Madagascar, mayotte, IvoryCoast, Cameroon, Niger, BurkinaFaso, Mali, Malawi, Zambia, Chad, Somalia, Senegal, Zimbabwe, Guinea, Rwanda, Benin, Burundi, Tunisia, Togo, SierraLeone, Congo, Liberia, Mauritania, Eritrea, Gambia, Botswana, Namibia, Gabon, Lesotho, GuineaBissau, Mauritius, Mozambique, Eswatini, Djibouti, Comoros, CapeVerde, WesternSahara, Seychelles, Réunion, count:58
area:NORTHAFRICA, Algeria, Egypt, Libya, Madeira, Morocco, Tunisia, WesternSahara, count:7
area:EASTAFRICA, Ethiopia, Eritrea, Tanzania, Kenya, Uganda, SouthSudan, sudan, Madagascar, mayotte, Malawi, Zambia, Somalia, Zimbabwe, Rwanda, Burundi, Eritrea, Mauritius, Mozambique, Djibouti, Seychelles, Réunion, count:21
area:CENTRALAFRICA, Congo, DemocraticCongo, Angola, Cameroon, Chad, CentralAfrican, Gabon, EquatorialGuinea, SãoToméandPríncipe, count:9
area:SOUTHAFRICA, SouthAfrica, Botswana, Namibia, Lesotho, Eswatini, count:5
area:WESTAFRICA, Ghana, IvoryCoast, Niger, BurkinaFaso, Mali, Senegal, Guinea, Benin, Togo, SierraLeone, Liberia, Mauritania, Gambia, GuineaBissau, CapeVerde, count:15
area:SOUTHAMERICA, Argentina, Bolivia, Brazil, Chile, Colombia, Ecuador, FalklandIslands, FrenchGuiana, Guyana, Paraguay, Peru, Suriname, Uruguay, Venezuela, count:14
area:NORTHAMERICA, greenland, Anguilla, AntiguaAndBarbuda, Aruba, Bahamas, Barbados, Belize, Bermuda, Bonaire, BritishVirginIslands, Canada, CaymanIslands, ClippertonIsland, CostaRica, Cuba, Curaçao, Dominica, DominicanRepublic, ElSalvador, Greenland, Grenada, Guadeloupe, Guatemala, Haiti, Honduras, Jamaica, Martinique, Mexico, Montserrat, Nicaragua, Panama, PuertoRico, Saba, SaintBarthélemy, SaintKitts, SaintLucia, SaintMartin, SaintPierre, SaintVincent, TrinidadandTobago, VirginIslands, count:41
area:OCEANIA, kiribati, count:1
area:ALL, finland, åland, sweden, norway, denmark, FaroeIslands, iceland, greenland, estonia, latvia, lithuania, ukraine, albania, albania, Armenia, austria, Azerbaijan, belarus, belgium, bosniaandherzegovina, bulgaria, croatia, cyprus, czechrepublic, france, Georgia, germany, Greece, Hungary, Ireland, Italy, Vatican, Kazakhstan, Liechtenstein, Luxembourg, Malta, moldova, monaco, montenegro, Netherlands, northmacedonia, Poland, Portugal, Romania, Russia, sanmarino, serbia, Slovakia, Slovenia, Turkey, Spain, UnitedKingdom, England, Scotland, Wales, Anguilla, AntiguaAndBarbuda, Argentina, Aruba, Bahamas, Barbados, Belize, Bermuda, Bolivia, Bonaire, BouvetIsland, Brazil, BritishVirginIslands, Canada, CaymanIslands, Chile, ClippertonIsland, Colombia, CostaRica, Cuba, Curaçao, Dominica, DominicanRepublic, Ecuador, ElSalvador, FalklandIslands, FrenchGuiana, Greenland, Grenada, Guadeloupe, Guatemala, Guyana, Haiti, Honduras, Jamaica, Martinique, Mexico, Montserrat, Navassa, Nicaragua, Panama, Paraguay, Peru, PuertoRico, Saba, SaintBarthélemy, SaintKitts, SaintLucia, SaintMartin, SaintPierre, SaintVincent, SintMaarten, SouthGeorgia, Suriname, TrinidadandTobago, TurksandCaicos, UnitedStatesofAmerica, VirginIslands, Uruguay, Venezuela, UnitedArabEmirates, Afghanistan, Bangladesh, Bahrain, Brunei, Bhutan, China, HongKong, Indonesia, Israel, India, Iraq, Iran, Jordan, Japan, Kyrgyzstan, Cambodia, SouthKorea, Kuwait, Laos, Lebanon, SriLanka, Mongolia, Macao, Maldives, Myanmar, Malaysia, NorthKorea, Nepal, Oman, Philippines, Pakistan, Palestine, Qatar, SaudiArabia, Singapore, Syria, Thailand, Tajikistan, EastTimor, Turkmenistan, Taiwan, Uzbekistan, Vietnam, Yemen, Nigeria, Ethiopia, Eritrea, Congo, DemocraticCongo, Tanzania, SouthAfrica, Kenya, Uganda, SouthSudan, sudan, Algeria, Egypt, Libya, Madeira, Morocco, Angola, Ghana, Mozambique, Madagascar, mayotte, IvoryCoast, Cameroon, Niger, BurkinaFaso, Mali, Malawi, Zambia, Chad, Somalia, Senegal, Zimbabwe, Guinea, Rwanda, Benin, Burundi, Tunisia, Togo, SierraLeone, Congo, CentralAfrican, Liberia, Mauritania, Eritrea, Gambia, Botswana, Namibia, Gabon, Lesotho, GuineaBissau, EquatorialGuinea, Mauritius, Mozambique, Eswatini, Djibouti, Comoros, CapeVerde, WesternSahara, SãoToméandPríncipe, Seychelles, Australia, kiribati, antarctica, cookislands, newzealand, niue, tokelau, americansamoa, britishindianocean, christmasisland, cocosislands, fiji, frenchpolynesia, frenchsouthernandantarctic, gibraltar, guam, guernsey, heardandmacdonaldsislands, manisle, jersey, liechtenstein, marshallislands, micronesia, nauru, newcaledonia, norfolkisland, northernmariana, paiau, papuanewguinea, pitcairn, Réunion, tristandacunha, samoa, solomonislands, janmayen, tuvalu, usminoroutlyingislands, vanuatu, wallisandfutuna, count:259
00000 23933476602239466377815183666336410526913255853507880902531575913
00001 75892311131489151051584635035296694319912096485236112089206322461
00002 38592554073360093998514241158337970540330801205676600268884077746
00003 13319205731345237480638193592296597610207468715353376895830326942
00004 35590860392770168574790082985573286375551823885389108732528064216
00005 25219754152834136322488110178605362616714455236854036050793549832
00006 45176182884339628322847596264906590850829829071656985062184052439
00007 51278261352573525446779932273640595147314743083454039849513229535
00008 44658224189508110992349860032329909065106543682283883425608435040
00009 48669205961987714566615256512928279472410821451443832019885646062

Vielä 77-debukin koodi:

#define DEBUG77

#ifdef DEBUG77

  unsigned char *areaflags = "FINLAND, DENMARK, NORDIC, BALTIC, EU, EUROPE, ASIA, AFRICA, SOUTHAMERICA, NORTHAMERICA, OCEANIA, ALL", *a;
  unsigned char name[1024];
  
  a = areaflags;
  while(*a != '\0' && newressu_get_name(sizeof(name), name, &a)) {
    fprintf(stdout,"area: %s",name);
    count = 0;
    for(c = 0; c < sizeof(idsflags) / sizeof(idsflags[0]); c++) {
      if(!strcmp(name, "ALL")) {
	count++;
	q = idsflags[c].flags;
	unsigned char name2[1024];
	newressu_get_name(sizeof(name2), name2, &q);
	newressu_get_name(sizeof(name2), name2, &q);
	newressu_get_name(sizeof(name2), name2, &q);
	newressu_get_name(sizeof(name2), name2, &q);
	fprintf(stdout,", %s",name2);
      } else {	
	p = strstr(idsflags[c].flags, name);
	q = idsflags[c].flags;
	int fl = strlen(name);
	if( p != NULL && // string found
	    ( isblank(*(p + fl)) || ispunct(*(p + fl)) || *(p + fl) == '\0' ) && // character after string
	    ( (p == q) || isblank(*(p - 1)) || ispunct(*(p - 1)) ) ) { // character before string
	  count++;
	  q = idsflags[c].flags;
	  unsigned char name2[1024];
	  newressu_get_name(sizeof(name2), name2, &q);
	  newressu_get_name(sizeof(name2), name2, &q);
	  newressu_get_name(sizeof(name2), name2, &q);
	  newressu_get_name(sizeof(name2), name2, &q);
	  fprintf(stdout,", %s",name2);
	}
      }
    }
    fprintf(stdout,", count:%d",count);
    fprintf(stdout,"\n");
  }
#endif

Edellisen debukin käyttämä get_name rutiini:

int newressu_get_name(int length, unsigned char *name, unsigned char **p2)
{
  int len, ok = 0;
  unsigned char *n, *p;

  p = *p2;
  n = name;
  len = 0;
  
  while(ispunct(*p) || isblank(*p)) {
    p++;
  }

  while(*p != '\0' && !ispunct(*p) && !isblank(*p)) {
    if(len < length) {
      *n++ = *p;
      ok = 1;
    }
    p++;
    len++;
  }
  *n = '\0';
  
  *p2 = p;

  return(ok);
}

Lisätty terttuutil1:een uusi tapa tuottaa testimateriaalia. Tässä testimateriaalia luodaan edeltä määritellyn rakenteen mukaisesti: rakenteessa luetellaan kentät, joita tarvitaan ja niiden arvojoukot ja tulostuksen formatointi.

    struct field {
      unsigned char *fieldname;
      int fieldtype;
      int lowlimit;
      int highlimit;
      unsigned char *valuestring;
    } fields[] = {
      { "asno", SEQUENTIAL, 0, 0,"10%05d" },
      { "asnimi", SEQUENTIAL, 0,0, "nimi: %d" },
      { "asoso", SEQUENTIAL, 0, 0, "osoite: %d" },
      { "aspono", RANDOM, 0, 99999, "postinumero%d"},
      { "aspotmp", RANDOM, 0, 50, "postitmp %d"},
      { "asry", RANDOM, 0, 10, "1%d"},
      { "asmail", RANDOM, 0, 10, "mail: %d"},
    };

Tässä pätkä edellisen mukaan luotua testimateriaalia: Tässä on testailun helpottamiseksi käytetty lyhyempiä kenttien nimiä.

'asno' = "1000000", 'asnimi' = "nimi: 0", 'asoso' = "osoite: 0", 'aspono' = "postinumero54863", 'aspotmp' = "postitmp 20", 'asry' = "10", 'asmail' = "mail: 7"
'asno' = "1000001", 'asnimi' = "nimi: 1", 'asoso' = "osoite: 1", 'aspono' = "postinumero58039", 'aspotmp' = "postitmp 25", 'asry' = "13", 'asmail' = "mail: 8"
'asno' = "1000002", 'asnimi' = "nimi: 2", 'asoso' = "osoite: 2", 'aspono' = "postinumero53052", 'aspotmp' = "postitmp 44", 'asry' = "15", 'asmail' = "mail: 2"
'asno' = "1000003", 'asnimi' = "nimi: 3", 'asoso' = "osoite: 3", 'aspono' = "postinumero49398", 'aspotmp' = "postitmp 22", 'asry' = "12", 'asmail' = "mail: 2"
'asno' = "1000004", 'asnimi' = "nimi: 4", 'asoso' = "osoite: 4", 'aspono' = "postinumero73740", 'aspotmp' = "postitmp 7", 'asry' = "17", 'asmail' = "mail: 3"

Ja koodi, jolla materiaali luodaan: Tässä on kenttien arvojen tekemiseen käytetty aiempaa stream_cipher rutiinia: stream_open() ja stream_bytes(). Näin on saatu data-arvot pysymään samoina, jos vain kenttien määrä ja kenttien arvot pysyvät samoina.

  if(test2flag) {
#define SEQUENTIAL 1
#define RANDOM 2
    
    struct field {
      unsigned char *fieldname;
      int fieldtype;
      int lowlimit;
      int highlimit;
      unsigned char *valuestring;
    } fields[] = {
      { "asno", SEQUENTIAL, 0, 0,"10%05d" },
      { "asnimi", SEQUENTIAL, 0,0, "nimi: %d" },
      { "asoso", SEQUENTIAL, 0, 0, "osoite: %d" },
      { "aspono", RANDOM, 0, 99999, "postinumero%d"},
      { "aspotmp", RANDOM, 0, 50, "postitmp %d"},
      { "asry", RANDOM, 0, 10, "1%d"},
      { "asmail", RANDOM, 0, 10, "mail: %d"},
    };

    stream_open(0, "kalakala");
    
    for(c = 0; c < TEST_SETS; c++) {
      if(quitcmd)
	break;

      first = 1;

      for(d = 0; d < sizeof(fields) / sizeof(fields[0]); d++) {
	if(!first)
	  more_printf(stdout,", ");
	more_printf(stdout,"'%s'", fields[d].fieldname);
	more_printf(stdout," = ");
	more_printf(stdout,"\"");
	if(fields[d].fieldtype = RANDOM && (fields[d].lowlimit != 0 || fields[d].highlimit != 0)) {
	  unsigned int e = stream_gen_limit(fields[d].highlimit - fields[d].lowlimit) + fields[d].lowlimit;
	  more_printf(stdout, fields[d].valuestring, e);
	} else {
	  more_printf(stdout, fields[d].valuestring, c);
	}
	more_printf(stdout,"\"");
	first = 0;
      }
      more_printf(stdout,"\n");
    }
    more_printf(stdout,"\n");
  }

Vielä aiemmin käytetty stream_gen_limit() rutiini:

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

  if(lastlimit != limit) { // if limit changes, calculate new highlimit and bytes
    lastlimit = limit;
    if(limit <= 0x100) { // one byte
      // highest multiplier of limit that fits to needed bytes
      highlimit = (0x100 / limit) * limit;
      // number of bytes needed
      bytes = 1;

    } else if(limit <= 0x10000) { // two bytes
      highlimit = (0x10000 / limit) * limit;
      bytes = 2;

    } else if(limit <= 0x1000000) { // three bytes
      highlimit = (0x1000000 / limit) * limit;
      bytes = 3;

    } else if(limit <= 0x100000000) { // four bytes
      highlimit = (0x100000000 / limit) * limit;
      bytes = 4;

    } else if(limit <= 0x10000000000) { // five bytes
      highlimit = (0x10000000000 / limit) * limit;
      bytes = 5;

    } else if(limit <= 0x1000000000000) { // six bytes
      highlimit = (0x1000000000000 / limit) * limit;
      bytes = 6;

    } else if(limit <= 0x100000000000000) { // seven bytes
      highlimit = (0x100000000000000 / limit) * limit;      
      bytes = 7;

    } else { // if(limit <= 0xffffffffffffffff) {
      highlimit = (0xffffffffffffffff / limit) * limit;      
      bytes = 8;

    }
  } // if(lastlimit != limit)

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

  word %= limit;
  
  return(word);
}

Ja -_limit rutiinissa käytetty stream_genbyte():

int stream_genbyte()
{
  unsigned char ch;

  if(gent_pos == 0) {
    gent_clear();
    stream_bytes(sizeof(gent), gent);
  } // if(gent_pos==0
  ch = gent[gent_pos];
  gent_pos = (gent_pos + 1) % sizeof(gent);

  return(ch);
}

Ja kaikki stream cipher rutiinit:

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

static void stream_internalbytes(unsigned char *digest)
{
  HashCtx hash;

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

}

static int streamt_pos = 0;

void stream_bytes(int size, unsigned char *buffer) // JariK 2023
{
  int c;
  static unsigned char streamt[HashLen]; // 256 bits
  
  for(c = 0; c < size; c++) {

    if(streamt_pos == 0) {
      stream_internalbytes(streamt); // read next random bytes

      stream_internalbytes(stream_key); // change key

    } // end of if(streamt_pos == 0

    buffer[c] = streamt[streamt_pos];
    streamt_pos = (streamt_pos + 1) % sizeof(streamt);

  } // end of for(c = 0; c < size; c++)
}

#define STREAM_KEY_ROUNDS 1024 // should be fast enough and slow enough, now 1024

void stream_open(int size, unsigned char *key) // size = 0 --> zero terminated string
{
  int c;
  HashCtx hash;

  clearcvar();
  streamt_pos = 0;  

  if(size == 0)
    size = strlen(key);
  
  HashInit(&hash);
  HashUpdate(&hash, key, size);
  HashUpdate(&hash, (unsigned char *) &cvar, cvarsize + 1);
  inccvar();

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

    HashFinal(stream_key, &hash);

    HashInit(&hash);
    HashUpdate(&hash, stream_key, sizeof(stream_key));
    HashUpdate(&hash, (unsigned char *) &cvar, cvarsize + 1);
    inccvar();
  }

  HashFinal(stream_key, &hash);

#define DEBUG21 2

#ifdef DEBUG21
  fflush(stdout);
  fprintf(stderr,"%s: stream_open():", procname);
  fprintf(stderr," randomness from key, key=");
  dumpbin(stderr, size, key);
  fprintf(stderr,"\n");
  fflush(stderr);
#endif
  
  memset(&hash, 0, sizeof(hash)); // forget hash
}

Ja vielä stream_cipher:in käyttämät cvar rutiinit:

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

void inccvar()
{
  int c;

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

  if(cvarsize < c)
    cvarsize = c;

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

#define aCVARRANDOMSTART 2 // off by default

void clearcvar()
{
  int c;

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

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

Koko koodi terttuutil1:een oli aiemmin artikkelissa.

Aiemmin artikkelissa sanoin, että ressu on one time pad materiaalia, ja sillä perusteella kirjoitinkin ohjelman, jolla voi luoda uniikkia one time pad materiaalia, jos numeeristen tietokenttien pitää olla uniikkeja. Tässä pätkä mallitiedostosta: en kerro tässä miten materiaalia käytetään, mutta youtube:ssa on videoita aiheesta (hakusana one time pad).

$ ./newressuonetimepad
00000  22983 29088 27522 06071 83152 08423 04809 75707 72840 83759
00001  86262 02471 71623 56655 70705 45228 74332 59496 49090 79319
00002  87718 95655 33822 66347 41853 60517 65396 19485 13898 95025
00003  80611 58219 13708 85033 79649 76119 20896 26169 59084 38831
00004  18935 82852 05160 52485 01070 62471 51691 27036 56925 84451
00005  81595 77010 72735 56554 46639 59640 98532 50613 19521 75984
00006  58259 83748 30984 76252 81888 50448 59479 95014 43388 53501
00007  27187 69156 44641 13931 53178 46699 73574 26870 61794 93634
00008  45352 47203 46311 69134 03390 76745 88596 56731 59709 76978
00009  52048 41207 13038 04123 02288 20990 29157 44586 14704 11880
Ctrl-c
$

Ja pieni ohjelma:

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

unsigned char *procname = NULL;
static unsigned char *programname = "newressuonetimepad version 0.02 ©";
static unsigned char *copyright = "Copyright (c) 2013-2024 Jari Kuivaniemi (moijari.com), Helsinki, Finland. Kaikki oikeudet pidätetään!";

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

#define USE_PSEUDORESSU 2

void ressu_genbytes(int size, unsigned char *buffer);
#ifdef USE_PSEUDORESSU
void pseudoressu_bytes(int size, unsigned char *buffer);
void ressutwist_bytes(int size, unsigned char *buffer);
#endif

#define INPUT_RESSU 0
#define INPUT_RESSUTWIST 2

int input = INPUT_RESSU;

int newressu_genbyte()
{
  unsigned char ch;

  if(gent_pos == 0) {
    memset(gent, 0, sizeof(gent));
    if(input == INPUT_RESSU)
      ressu_genbytes(sizeof(gent), gent);
    else if(input == INPUT_RESSUTWIST)
      ressutwist_bytes(sizeof(gent), gent);
  } // if(gent_pos==0
  ch = gent[gent_pos];
  gent_pos = (gent_pos + 1) % sizeof(gent);

  return(ch);
}

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

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

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

  word %= limit;
  
  return(word);
}

#define CODECHARS 5
#define CODES 100000

int main(int argc, char *argv[])
{
  int c, d, e, f, help = 0, mode = 1;
  int data[CODES];

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

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

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

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

      } else if(!strcmp("--ressutwist", argv[c]) // JariK 2023
		|| !strcmp("--ressuwtwist", argv[c])
	        || !strcmp("--twist", argv[c]) ) {
	input = INPUT_RESSUTWIST;

      } else if(!strcmp("--mode1", argv[c])) {
	mode = 1;
	
      } else if(!strcmp("--mode2", argv[c])) {
	mode = 2;

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

      } else {
	fprintf(stderr,"%s: invalid option %s\n", procname, argv[c]);
	exit(1);
      }
    }
  } // end of for(c = 1; c < argc; c++)

  // print help message if needed
  
  if(help) {
    fprintf(stderr,"%s: %s", procname, procname);
    fprintf(stderr," [--help]");
    fprintf(stderr," [--copyright]");
    fprintf(stderr," [--version]");
    fprintf(stderr," [--mode1]");
    fprintf(stderr," [--mode2]");
    fprintf(stderr," [--mode3]");
    fprintf(stderr,"\n");
    exit(1);
  } // end of if(help)

  fprintf(stdout,"mode:%d", mode);
  fprintf(stdout,", input:%d", input);
  fprintf(stdout,"\n");
  if(mode == 1) { // simple, not unique. you can also use ./newressu --otp5n -w10

    for(c = 0; c < CODES; c++) {
      data[c] = newressu_gen_limit(CODES);
    }

  } else if(mode == 2) { // unique faster

    for(c = 0; c < CODES; c++) {
      data[c] = c;
    }
    
    for(c = 0; c < CODES; c++) {
      e = newressu_gen_limit(CODES);
      f = data[e];
      data[e] = data[c];
      data[c] = f;
    }

  } else if (mode == 3) { // unique slower

    for(c = 0; c < CODES; c++) {
      int ok = 0;
      while(!ok) {
	e = newressu_gen_limit(CODES);
	ok = 1;
	for(d = 0; d < c; d++) {
	  if(data[d] == e) {
	    ok = 0;
	    break;
	  }
	}
      }
      data[c] = e;
    }  
  }

  int lineno = 0;

  for(c = 0; c < CODES; c++) {
    if(c % 10 == 0) {
      if(c > 0)
	fprintf(stdout,"\n");
      fprintf(stdout,"%05d ", lineno);
      lineno++;
    }
    fprintf(stdout," %0*d", CODECHARS, data[c]);
  }
  fprintf(stdout,"\n");

  exit(0);
}

Lisätty lippuihin australasia, melanesia, micronesia ja polynesia. Yksinkertaistettu lippufunktioita ja lisätty DEBUG77:an kaksimerkkinen iso koodi maan nimen perään: tässä DEBUG77 muutokset:

#define DEBUG77

#ifdef DEBUG77

  unsigned char *areaflags = "FINLAND, DENMARK, NORDIC, BALTIC, EU, EUROPE, ASIA, AFRICA, NORTHAFRICA, EASTAFRICA, CENTRALAFRICA, SOUTHAFRICA, WESTAFRICA SOUTHAMERICA, NORTHAMERICA, OCEANIA, AUSTRALASIA, MELANESIA, MICRONESIA, POLYNESIA, ALL", *a;
  unsigned char name[128];
  
  a = areaflags;
  while(*a != '\0' && newressu_getname(sizeof(name), name, &a)) {
    fprintf(stdout,"area:%s",name);
    int count = 0;
    for(c = 0; c < sizeof(idsflags) / sizeof(idsflags[0]); c++) {
      if(!strcmp(name, "ALL")) {
	count++;
	unsigned char *q = idsflags[c].flags;
	unsigned char name2[128], name3[128];
	newressu_getname(sizeof(name2), name2, &q); // flag
	newressu_getname(sizeof(name2), name2, &q); // 2 char isocode
	newressu_getname(sizeof(name3), name3, &q); // 3 char isocode
	newressu_getname(sizeof(name3), name3, &q); // name
	fprintf(stdout,", %s(%s)",name3, name2); // name and isocode2 needed
      } else {	

	if(newressu_strstr(idsflags[c].flags, name)!=NULL) {
	  count++;
	  unsigned char *q = idsflags[c].flags;
	  unsigned char name2[128], name3[128];
	  newressu_getname(sizeof(name2), name2, &q); // flag
	  newressu_getname(sizeof(name2), name2, &q); // 2 char isocode
	  newressu_getname(sizeof(name3), name3, &q); // 3 char isocode
	  newressu_getname(sizeof(name3), name3, &q); // name
	  fprintf(stdout,", %s(%s)", name3, name2); // name and isocode2 needed
	}
      }
    }
    fprintf(stdout,", count:%d",count);
    fprintf(stdout,"\n");
  }
#endif

Tässä getname funktio:

int newressu_getname(int length, unsigned char *name, unsigned char **p2)
{
  int len, ok = 0;
  unsigned char *n, *p;

  p = *p2;
  n = name;
  len = 0;
  
  while(ispunct(*p) || isblank(*p)) {
    p++;
  }

  while(*p != '\0' && !ispunct(*p) && !isblank(*p)) {
    if(len < length) {
      *n++ = *p;
      ok = 1;
    }
    p++;
    len++;
  }
  *n = '\0';
  
  *p2 = p;

  return(ok);
}

Ja uusi newressu_strstr funktio: standardiin funktioon on lisätty se, että etsittävän merkkijonon pitää alkaa ja loppua sanarajalla.

unsigned char *newressu_strstr(unsigned char *haystack, unsigned char *needle)
{
  int fl;
  unsigned char *p = haystack;

  while(*p != '\0') {
    fl = strlen(needle);
    if(!strncmp(p, needle, strlen(needle)) &&
       ( isblank(*(p + fl)) || ispunct(*(p + fl)) || *(p + fl) == '\0' ) && // character after string
       ( (p == haystack) || isblank(*(p - 1)) || ispunct(*(p - 1)) ) ) { // character before string
      return(p);
    }
    p++;
  }
  return(NULL);
}

Olen aiemmin sanonut että satunnaislukujen luomiseen kannattaa käyttää useampia satunnaisbittigeneraattoreita summattuna. Tässä uusi satunnaislukulähde ressutwist (Ressu with twist), jossa ressuun lisätään pseudoressu. Molemmat vaikuttavat olevan hyviä generaattoreita, ja niiden yhdiste olisi tietenkin vaikeampi murtaa.

Seuraavassa twistin koodi: Aluksi koodi newressu_genbyte:stä:

int newressu_genbyte()
{
  unsigned char ch;

  if(gent_pos == 0) {
#ifdef DEBUG23
    ressu_dump("oldgent", 32, gent, 32); // first 32 bytes
#endif

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

#ifdef SHA256
    else if(input == INPUT_PSEUDORESSU) // pseudoressu
      pseudoressu_bytes(sizeof(gent), gent);
    
#endif
#ifdef SHA256
    else if(input == INPUT_RESSUTWIST) {// ressu w twist
      ressu_genbytes(sizeof(gent), gent);
      pseudoressu_bytes(sizeof(gent), gent);
    }
#endif
    else if(input == INPUT_FASTRESSU) // ressu_fast
      ressu_genbytes_fast(sizeof(gent), gent);

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

Koodi sample():sta:

      //memset(buffer, 0, blocksize);
      
      if(input == INPUT_RESSU) // ressu prod
	ressu_genbytes(blocksize, buffer);
      
      else if(input == INPUT_PSEUDORESSU) // pseudoressu
	pseudoressu_bytes(blocksize, buffer);
      
      else if(input == INPUT_RESSUTWIST) { // ressu w twist
	ressu_genbytes(blocksize, buffer);
	pseudoressu_bytes(blocksize, buffer);
      
      } else if(input == INPUT_FASTRESSU) // ressu fast
	ressu_genbytes_fast(blocksize, buffer);
      
      else if(input == INPUT_SINGLERESSU) // ressu single
	ressu_genbytes_single(blocksize, buffer);
     

Koodi komentoparametrien käsittelystä:

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

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

      } else if(!strcmp("--ressutwist", argv[c]) // JariK 2023
		|| !strcmp("--ressuwtwist", argv[c])
	        || !strcmp("--twist", argv[c]) ) {
	input = INPUT_RESSUTWIST;

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

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

Jos ajatteluni pitää paikkansa twist:in pitäisi myös olla one time pad kelpoinen. Siinä on siis ressu, joka on one time pad kelpoinen ja kun one time pad kelpoisiin bitteihin lisätään joku muu satunnaisgeneraattori (tässä pseudoressu), se on vieläkin one time pad kelpoinen.

Sitten ns “bad for randomness” -korjaus: Alkuperäinen eli viallinen koodi on merkitty #ifdef OLD1 iffeillä ja korjattu koodi on kaksi riviä ennen niitä. Alkuperäisenä ajatuksena oli satunnaistaa kehitetyn satunnaispuskurin merkkien järjestystä lisää, mutta se todellisuudessa vähentää sitä. Luulisin että ongelma johtuu siitä että todellisuudessa vanhoilla koodiriveillä samoista merkeistä kopioidaan vain osa ja niitäkin eri määrä. Yhtä merkkiä kopioidaan kolme kertaa, seuraavaa kaksi, seuraavaa yksi ja seuraavaa ei kopioida ollenkaan. Alkuperäinen idea vanhoilla riveillä oli, että kun valitaan seuraavista 256 merkistä satunnainen, niin se sisältää keskimäärin kaikki vaihtoehdot 0-255.

void ressu_genbytes(int size, unsigned char *buffer) // 6.5.2021 JariK
{
---
    } // if(ressut_pos == 0)
    buffer[c] ^= ressut[ressut_pos];
    ressut_pos = (ressut_pos + 1) % ressut_bytes;
#ifdef OLD1
    ressut_f = (ressut_f + ressut[ressut_pos] + 2) % ressut_bytes; // bad for randomness... JariK 2023
    buffer[c] ^= ressut[ressut_f];
    ressut_pos = (ressut_pos + 1) % ressut_bytes;
#endif
  } // for(c = 0; c < size; c++)
...
}

“bad for randomness” bugin esittelyä varten tein sample() ajoista seuraavan kaltaisen, terttu tyylisen raportin: Raportilla satunnaisuuden lähde, tiedoston koko, tiedoston nimi, WEAK-rivit ja FAILED-rivit.

'input' = "pseudoressu", 'filesize' = "1080m", 'filename' = "newressusample801.rnd", 'weak' = "5", 'failed' = "3"
'input' = "pseudoressu", 'filesize' = "1g", 'filename' = "newressusample802.rnd", 'weak' = "1", 'failed' = "0"
'input' = "urandom", 'filesize' = "1g200m", 'filename' = "newressusample836.rnd", 'weak' = "4", 'failed' = "0"
'input' = "ressu", 'filesize' = "1g200m", 'filename' = "newressusample837.rnd", 'weak' = "7", 'failed' = "0"
'input' = "twist", 'filesize' = "1g200m", 'filename' = "newressusample838.rnd", 'weak' = "1", 'failed' = "0"
'input' = "ressubfr1", 'filesize' = "10g", 'filename' = "newressusample897.rnd", 'weak' = "12", 'failed' = "31"
'input' = "ressubfr1", 'filesize' = "20g", 'filename' = "newressusample832.rnd", 'weak' = "8", 'failed' = "34"
'input' = "ressubfr1", 'filesize' = "30g", 'filename' = "newressusample860.rnd", 'weak' = "9", 'failed' = "33"

Raportin koodi:

void dump_sample() // & dieharder analysis
{
...
    FILE *fp2;
    if((fp2 = fopen("newressudieharder.skk", "a")) != NULL) { //
      fprintf(fp2,"'input' = \"%s\"", input_str);
      if(filesize_str != NULL)
	fprintf(fp2,", 'filesize' = \"%s\"", filesize_str);
      if(blocksize_str != NULL)
	fprintf(fp2,", 'blocksize' = \"%s\"", blocksize_str);
      if(blocks_str != NULL)
	fprintf(fp2,"'blocks' = \"%s\"", blocks_str);
      fprintf(fp2,", 'filename' = \"%s\"", filename);
      fprintf(fp2,", 'weak' = \"%lld\"", weak);
      fprintf(fp2,", 'failed' = \"%lld\"", failed);
      fprintf(fp2,"\n");
      fclose(fp2);
    }
...
}

Ajoin –sample –dieharder rutiinia seuraavankaltaisilla shellscript:eillä:

$ cat newressidieharder7.sh
#!/bin/bash -x
./newressu --sample --dieharder --filesize 500m $@
./newressu --sample --dieharder --filesize 600m $@
./newressu --sample --dieharder --filesize 700m $@
./newressu --sample --dieharder --filesize 800m $@
./newressu --sample --dieharder --filesize 900m $@
./newressu --sample --dieharder --filesize 1g $@
./newressu --sample --dieharder --filesize 1g100m $@
./newressu --sample --dieharder --filesize 1g200m $@
./newressu --sample --dieharder --filesize 1g300m $@
./newressu --sample --dieharder --filesize 1g400m $@
./newressu --sample --dieharder --filesize 1g500m $@
./newressu --sample --dieharder --filesize 1g600m $@
./newressu --sample --dieharder --filesize 1g700m $@
./newressu --sample --dieharder --filesize 1g800m $@
./newressu --sample --dieharder --filesize 1g900m $@
./newressu --sample --dieharder --filesize 2g $@
./newressu --sample --dieharder --filesize 3g $@
./newressu --sample --dieharder --filesize 4g $@
./newressu --sample --dieharder --filesize 5g $@
./newressu --sample --dieharder --filesize 6g $@
$ chmod +x newressudieharder7.sh
$ ./newressudieharder7.sh --single
....

Single riveillä raportti näyttää hyvältä: failed rivejä on vain alle 1g tiedostoissa ja weak rivejä on vain muutamia tiedoston koosta huolimatta:

$ grep single newressudieharder7.sh
'input' = "single", 'filesize' = "1g", 'filename' = "newressusample913.rnd", 'weak' = "1", 'failed' = "0"
'input' = "single", 'filesize' = "1g100m", 'filename' = "newressusample1028.rnd", 'weak' = "5", 'failed' = "0"
'input' = "single", 'filesize' = "1g200m", 'filename' = "newressusample914.rnd", 'weak' = "1", 'failed' = "0"
'input' = "single", 'filesize' = "1g300m", 'filename' = "newressusample915.rnd", 'weak' = "1", 'failed' = "0"
'input' = "single", 'filesize' = "1g400m", 'filename' = "newressusample916.rnd", 'weak' = "3", 'failed' = "0"
'input' = "single", 'filesize' = "1g500m", 'filename' = "newressusample917.rnd", 'weak' = "2", 'failed' = "0"
'input' = "single", 'filesize' = "1g600m", 'filename' = "newressusample918.rnd", 'weak' = "3", 'failed' = "0"
'input' = "single", 'filesize' = "1g700m", 'filename' = "newressusample919.rnd", 'weak' = "6", 'failed' = "0"
'input' = "single", 'filesize' = "10g", 'filename' = "newressusample921.rnd", 'weak' = "0", 'failed' = "0"
'input' = "single", 'filesize' = "500m", 'filename' = "newressusample1022.rnd", 'weak' = "4", 'failed' = "1"
'input' = "single", 'filesize' = "600m", 'filename' = "newressusample1023.rnd", 'weak' = "7", 'failed' = "1"
'input' = "single", 'filesize' = "700m", 'filename' = "newressusample1024.rnd", 'weak' = "2", 'failed' = "0"
'input' = "single", 'filesize' = "800m", 'filename' = "newressusample1025.rnd", 'weak' = "6", 'failed' = "1"
'input' = "single", 'filesize' = "900m", 'filename' = "newressusample1026.rnd", 'weak' = "3", 'failed' = "3"
'input' = "single", 'filesize' = "1g800m", 'filename' = "newressusample1080.rnd", 'weak' = "4", 'failed' = "0"
'input' = "single", 'filesize' = "1g900m", 'filename' = "newressusample1081.rnd", 'weak' = "7", 'failed' = "0"
'input' = "single", 'filesize' = "2g", 'filename' = "newressusample1082.rnd", 'weak' = "2", 'failed' = "0"
'input' = "single", 'filesize' = "3g", 'filename' = "newressusample1083.rnd", 'weak' = "2", 'failed' = "0"
'input' = "single", 'filesize' = "4g", 'filename' = "newressusample1084.rnd", 'weak' = "2", 'failed' = "0"
'input' = "single", 'filesize' = "5g", 'filename' = "newressusample1085.rnd", 'weak' = "1", 'failed' = "0"
'input' = "single", 'filesize' = "6g", 'filename' = "newressusample1086.rnd", 'weak' = "2", 'failed' = "0"
$

Alkuperäisen “bad for randomness” koodin lista näyttää seuraavalta: (sekä weak että failed rivejä on paljon.

$ grep ressubfr1 newressudieharder.skk
'input' = "ressubfr1", 'filesize' = "10g", 'filename' = "newressusample897.rnd", 'weak' = "12", 'failed' = "31"
'input' = "ressubfr1", 'filesize' = "20g", 'filename' = "newressusample832.rnd", 'weak' = "8", 'failed' = "34"
'input' = "ressubfr1", 'filesize' = "30g", 'filename' = "newressusample860.rnd", 'weak' = "9", 'failed' = "33"
'input' = "ressubfr1", 'filesize' = "500m", 'filename' = "newressusample873.rnd", 'weak' = "11", 'failed' = "18"
'input' = "ressubfr1", 'filesize' = "600m", 'filename' = "newressusample874.rnd", 'weak' = "14", 'failed' = "18"
'input' = "ressubfr1", 'filesize' = "700m", 'filename' = "newressusample875.rnd", 'weak' = "12", 'failed' = "20"
'input' = "ressubfr1", 'filesize' = "800m", 'filename' = "newressusample876.rnd", 'weak' = "6", 'failed' = "21"
'input' = "ressubfr1", 'filesize' = "900m", 'filename' = "newressusample878.rnd", 'weak' = "15", 'failed' = "19"
'input' = "ressubfr1", 'filesize' = "1g", 'filename' = "newressusample880.rnd", 'weak' = "8", 'failed' = "18"
'input' = "ressubfr1", 'filesize' = "1g100m", 'filename' = "newressusample881.rnd", 'weak' = "9", 'failed' = "20"
'input' = "ressubfr1", 'filesize' = "1g200m", 'filename' = "newressusample882.rnd", 'weak' = "13", 'failed' = "31"
'input' = "ressubfr1", 'filesize' = "1g300m", 'filename' = "newressusample884.rnd", 'weak' = "14", 'failed' = "30"
'input' = "ressubfr1", 'filesize' = "1g400m", 'filename' = "newressusample885.rnd", 'weak' = "6", 'failed' = "32"
'input' = "ressubfr1", 'filesize' = "1g500m", 'filename' = "newressusample886.rnd", 'weak' = "9", 'failed' = "32"
'input' = "ressubfr1", 'filesize' = "1g600m", 'filename' = "newressusample889.rnd", 'weak' = "10", 'failed' = "19"
'input' = "ressubfr1", 'filesize' = "1g700m", 'filename' = "newressusample890.rnd", 'weak' = "7", 'failed' = "21"
'input' = "ressubfr1", 'filesize' = "1g800m", 'filename' = "newressusample891.rnd", 'weak' = "8", 'failed' = "20"
'input' = "ressubfr1", 'filesize' = "1g900m", 'filename' = "newressusample892.rnd", 'weak' = "8", 'failed' = "17"
'input' = "ressubfr1", 'filesize' = "2g", 'filename' = "newressusample899.rnd", 'weak' = "6", 'failed' = "21"
'input' = "ressubfr1", 'filesize' = "3g", 'filename' = "newressusample900.rnd", 'weak' = "9", 'failed' = "23"
'input' = "ressubfr1", 'filesize' = "4g", 'filename' = "newressusample901.rnd", 'weak' = "13", 'failed' = "17"
'input' = "ressubfr1", 'filesize' = "5g", 'filename' = "newressusample902.rnd", 'weak' = "9", 'failed' = "18"
'input' = "ressubfr1", 'filesize' = "6g", 'filename' = "newressusample903.rnd", 'weak' = "4", 'failed' = "18"
$

“bad for randomness” koodin +2 versio: tässäkin weak ja failed rivejä on paljon.

$ grep ressubfr2 newressudieharder.skk
'input' = "ressubfr2", 'filesize' = "1g200m", 'filename' = "newressusample932.rnd", 'weak' = "8", 'failed' = "22"
'input' = "ressubfr2", 'filesize' = "1g300m", 'filename' = "newressusample933.rnd", 'weak' = "6", 'failed' = "20"
'input' = "ressubfr2", 'filesize' = "1g400m", 'filename' = "newressusample934.rnd", 'weak' = "9", 'failed' = "17"
'input' = "ressubfr2", 'filesize' = "1g500m", 'filename' = "newressusample935.rnd", 'weak' = "5", 'failed' = "18"
'input' = "ressubfr2", 'filesize' = "1g600m", 'filename' = "newressusample936.rnd", 'weak' = "10", 'failed' = "20"
'input' = "ressubfr2", 'filesize' = "1g700m", 'filename' = "newressusample949.rnd", 'weak' = "13", 'failed' = "18"
'input' = "ressubfr2", 'filesize' = "10g", 'filename' = "newressusample950.rnd", 'weak' = "4", 'failed' = "22"
'input' = "ressubfr2", 'filesize' = "20g", 'filename' = "newressusample961.rnd", 'weak' = "9", 'failed' = "16"
'input' = "ressubfr2", 'filesize' = "30g", 'filename' = "newressusample962.rnd", 'weak' = "9", 'failed' = "17"
'input' = "ressubfr2", 'filesize' = "500m", 'filename' = "newressusample904.rnd", 'weak' = "12", 'failed' = "17"
'input' = "ressubfr2", 'filesize' = "600m", 'filename' = "newressusample980.rnd", 'weak' = "9", 'failed' = "18"
'input' = "ressubfr2", 'filesize' = "700m", 'filename' = "newressusample981.rnd", 'weak' = "14", 'failed' = "19"
'input' = "ressubfr2", 'filesize' = "800m", 'filename' = "newressusample982.rnd", 'weak' = "12", 'failed' = "21"
'input' = "ressubfr2", 'filesize' = "900m", 'filename' = "newressusample983.rnd", 'weak' = "13", 'failed' = "21"
'input' = "ressubfr2", 'filesize' = "1g", 'filename' = "newressusample984.rnd", 'weak' = "9", 'failed' = "20"
'input' = "ressubfr2", 'filesize' = "1g100m", 'filename' = "newressusample985.rnd", 'weak' = "12", 'failed' = "21"
'input' = "ressubfr2", 'filesize' = "1g600m", 'filename' = "newressusample986.rnd", 'weak' = "11", 'failed' = "18"
'input' = "ressubfr2", 'filesize' = "1g700m", 'filename' = "newressusample987.rnd", 'weak' = "9", 'failed' = "18"
'input' = "ressubfr2", 'filesize' = "1g800m", 'filename' = "newressusample988.rnd", 'weak' = "8", 'failed' = "20"
'input' = "ressubfr2", 'filesize' = "1g900m", 'filename' = "newressusample989.rnd", 'weak' = "11", 'failed' = "18"
'input' = "ressubfr2", 'filesize' = "2g", 'filename' = "newressusample990.rnd", 'weak' = "8", 'failed' = "18"
'input' = "ressubfr2", 'filesize' = "3g", 'filename' = "newressusample991.rnd", 'weak' = "8", 'failed' = "17"
'input' = "ressubfr2", 'filesize' = "4g", 'filename' = "newressusample992.rnd", 'weak' = "8", 'failed' = "21"
'input' = "ressubfr2", 'filesize' = "5g", 'filename' = "newressusample993.rnd", 'weak' = "8", 'failed' = "17"
$ 

Tämä lista on tehty korjatulla “bad for randomness” -rutiinilla: “Vihdoinkin” ressurivitkin näyttävät hyviltä, muutama weak ja ei failed rivejä suurissa tiedostoissa.

$ grep \"ressu\" newressudieharder.skk
'input' = "ressu", 'filesize' = "1g200m", 'filename' = "newressusample837.rnd", 'weak' = "7", 'failed' = "0"
'input' = "ressu", 'filesize' = "500m", 'filename' = "newressusample1042.rnd", 'weak' = "6", 'failed' = "1"
'input' = "ressu", 'filesize' = "600m", 'filename' = "newressusample1043.rnd", 'weak' = "6", 'failed' = "1"
'input' = "ressu", 'filesize' = "700m", 'filename' = "newressusample1044.rnd", 'weak' = "7", 'failed' = "1"
'input' = "ressu", 'filesize' = "800m", 'filename' = "newressusample1045.rnd", 'weak' = "4", 'failed' = "0"
'input' = "ressu", 'filesize' = "900m", 'filename' = "newressusample1046.rnd", 'weak' = "6", 'failed' = "0"
'input' = "ressu", 'filesize' = "1g", 'filename' = "newressusample1047.rnd", 'weak' = "3", 'failed' = "0"
'input' = "ressu", 'filesize' = "1g100m", 'filename' = "newressusample994.rnd", 'weak' = "5", 'failed' = "0"
'input' = "ressu", 'filesize' = "1g300m", 'filename' = "newressusample1003.rnd", 'weak' = "4", 'failed' = "0"
'input' = "ressu", 'filesize' = "1g400m", 'filename' = "newressusample1004.rnd", 'weak' = "2", 'failed' = "0"
'input' = "ressu", 'filesize' = "1g500m", 'filename' = "newressusample1005.rnd", 'weak' = "2", 'failed' = "0"
'input' = "ressu", 'filesize' = "1g600m", 'filename' = "newressusample1006.rnd", 'weak' = "2", 'failed' = "0"
'input' = "ressu", 'filesize' = "1g700m", 'filename' = "newressusample1007.rnd", 'weak' = "3", 'failed' = "0"
'input' = "ressu", 'filesize' = "1g800m", 'filename' = "newressusample1036.rnd", 'weak' = "6", 'failed' = "0"
'input' = "ressu", 'filesize' = "1g900m", 'filename' = "newressusample1037.rnd", 'weak' = "5", 'failed' = "0"
'input' = "ressu", 'filesize' = "2g", 'filename' = "newressusample1038.rnd", 'weak' = "3", 'failed' = "0"
$

Ressun fast version lista:

$ grep fast newressudieharder.skk
'input' = "fast", 'filesize' = "500m", 'filename' = "newressusample1078.rnd", 'weak' = "2", 'failed' = "2"
'input' = "fast", 'filesize' = "600m", 'filename' = "newressusample1079.rnd", 'weak' = "7", 'failed' = "2"
'input' = "fast", 'filesize' = "700m", 'filename' = "newressusample1103.rnd", 'weak' = "3", 'failed' = "0"
'input' = "fast", 'filesize' = "800m", 'filename' = "newressusample1144.rnd", 'weak' = "7", 'failed' = "1"
'input' = "fast", 'filesize' = "900m", 'filename' = "newressusample1145.rnd", 'weak' = "3", 'failed' = "4"
'input' = "fast", 'filesize' = "1g", 'filename' = "newressusample1146.rnd", 'weak' = "4", 'failed' = "0"
'input' = "fast", 'filesize' = "1g100m", 'filename' = "newressusample1147.rnd", 'weak' = "2", 'failed' = "1"
'input' = "fast", 'filesize' = "1g200m", 'filename' = "newressusample1148.rnd", 'weak' = "3", 'failed' = "0"
'input' = "fast", 'filesize' = "1g300m", 'filename' = "newressusample1149.rnd", 'weak' = "3", 'failed' = "0"
'input' = "fast", 'filesize' = "1g400m", 'filename' = "newressusample1150.rnd", 'weak' = "4", 'failed' = "0"
'input' = "fast", 'filesize' = "1g500m", 'filename' = "newressusample1151.rnd", 'weak' = "5", 'failed' = "0"
'input' = "fast", 'filesize' = "1g600m", 'filename' = "newressusample1152.rnd", 'weak' = "5", 'failed' = "0"
'input' = "fast", 'filesize' = "1g700m", 'filename' = "newressusample1153.rnd", 'weak' = "2", 'failed' = "1"
'input' = "fast", 'filesize' = "1g800m", 'filename' = "newressusample1154.rnd", 'weak' = "3", 'failed' = "0"
'input' = "fast", 'filesize' = "1g900m", 'filename' = "newressusample1155.rnd", 'weak' = "3", 'failed' = "0"
'input' = "fast", 'filesize' = "2g", 'filename' = "newressusample1156.rnd", 'weak' = "2", 'failed' = "0"
'input' = "fast", 'filesize' = "3g", 'filename' = "newressusample1157.rnd", 'weak' = "3", 'failed' = "0"
'input' = "fast", 'filesize' = "4g", 'filename' = "newressusample1158.rnd", 'weak' = "0", 'failed' = "0"
'input' = "fast", 'filesize' = "5g", 'filename' = "newressusample1159.rnd", 'weak' = "5", 'failed' = "0"
'input' = "fast", 'filesize' = "6g", 'filename' = "newressusample1160.rnd", 'weak' = "5", 'failed' = "0"
$

Tässä vielä pseudoressu:sta samanlainen lista: Pseudoressulista näyttää kanssa hyvältä, se on avaimia lukuunottamatta SHA256:n tulos. Avaimethan tulevat ressusta.

$ grep \"pseudoressu\" newressudieharder.skk
'input' = "pseudoressu", 'filesize' = "1g", 'filename' = "newressusample802.rnd", 'weak' = "1", 'failed' = "0"
'input' = "pseudoressu", 'filesize' = "500m", 'filename' = "newressusample905.rnd", 'weak' = "7", 'failed' = "1"
'input' = "pseudoressu", 'filesize' = "600m", 'filename' = "newressusample906.rnd", 'weak' = "8", 'failed' = "0"
'input' = "pseudoressu", 'filesize' = "700m", 'filename' = "newressusample907.rnd", 'weak' = "11", 'failed' = "1"
'input' = "pseudoressu", 'filesize' = "800m", 'filename' = "newressusample908.rnd", 'weak' = "2", 'failed' = "1"
'input' = "pseudoressu", 'filesize' = "900m", 'filename' = "newressusample909.rnd", 'weak' = "5", 'failed' = "2"
'input' = "pseudoressu", 'filesize' = "1g", 'filename' = "newressusample910.rnd", 'weak' = "3", 'failed' = "0"
'input' = "pseudoressu", 'filesize' = "1g100m", 'filename' = "newressusample911.rnd", 'weak' = "4", 'failed' = "0"
'input' = "pseudoressu", 'filesize' = "1g200m", 'filename' = "newressusample912.rnd", 'weak' = "3", 'failed' = "0"
'input' = "pseudoressu", 'filesize' = "1g300m", 'filename' = "newressusample922.rnd", 'weak' = "2", 'failed' = "1"
'input' = "pseudoressu", 'filesize' = "1g400m", 'filename' = "newressusample824.rnd", 'weak' = "2", 'failed' = "0"
'input' = "pseudoressu", 'filesize' = "1g500m", 'filename' = "newressusample923.rnd", 'weak' = "1", 'failed' = "0"
'input' = "pseudoressu", 'filesize' = "1g600m", 'filename' = "newressusample924.rnd", 'weak' = "6", 'failed' = "0"
'input' = "pseudoressu", 'filesize' = "1g700m", 'filename' = "newressusample925.rnd", 'weak' = "4", 'failed' = "0"
'input' = "pseudoressu", 'filesize' = "1g800m", 'filename' = "newressusample926.rnd", 'weak' = "0", 'failed' = "0"
'input' = "pseudoressu", 'filesize' = "1g900m", 'filename' = "newressusample927.rnd", 'weak' = "4", 'failed' = "0"
'input' = "pseudoressu", 'filesize' = "2g", 'filename' = "newressusample928.rnd", 'weak' = "2", 'failed' = "0"
'input' = "pseudoressu", 'filesize' = "3g", 'filename' = "newressusample929.rnd", 'weak' = "2", 'failed' = "0"
'input' = "pseudoressu", 'filesize' = "4g", 'filename' = "newressusample930.rnd", 'weak' = "4", 'failed' = "0"
'input' = "pseudoressu", 'filesize' = "5g", 'filename' = "newressusample943.rnd", 'weak' = "1", 'failed' = "0"
'input' = "pseudoressu", 'filesize' = "6g", 'filename' = "newressusample944.rnd", 'weak' = "3", 'failed' = "0"
$ 

Stream lähteen tuloskin on hyvä lista: tässä listassa ei oikeasti ole mitään satunnaista, kaikki merkit tulevat avaimesta ‘kalakala’.

$ grep \"stream\" newressudieharder.skk
'input' = "stream", 'filesize' = "500m", 'filename' = "newressusample955.rnd", 'weak' = "2", 'failed' = "2"
'input' = "stream", 'filesize' = "600m", 'filename' = "newressusample956.rnd", 'weak' = "6", 'failed' = "0"
'input' = "stream", 'filesize' = "700m", 'filename' = "newressusample957.rnd", 'weak' = "4", 'failed' = "1"
'input' = "stream", 'filesize' = "800m", 'filename' = "newressusample958.rnd", 'weak' = "4", 'failed' = "0"
'input' = "stream", 'filesize' = "900m", 'filename' = "newressusample959.rnd", 'weak' = "8", 'failed' = "1"
'input' = "stream", 'filesize' = "1g", 'filename' = "newressusample960.rnd", 'weak' = "2", 'failed' = "0"
'input' = "stream", 'filesize' = "1g100m", 'filename' = "newressusample963.rnd", 'weak' = "7", 'failed' = "0"
'input' = "stream", 'filesize' = "1g200m", 'filename' = "newressusample964.rnd", 'weak' = "6", 'failed' = "0"
'input' = "stream", 'filesize' = "1g300m", 'filename' = "newressusample965.rnd", 'weak' = "3", 'failed' = "1"
'input' = "stream", 'filesize' = "1g500m", 'filename' = "newressusample966.rnd", 'weak' = "5", 'failed' = "1"
'input' = "stream", 'filesize' = "1g600m", 'filename' = "newressusample967.rnd", 'weak' = "2", 'failed' = "0"
'input' = "stream", 'filesize' = "1g700m", 'filename' = "newressusample968.rnd", 'weak' = "2", 'failed' = "0"
'input' = "stream", 'filesize' = "1g800m", 'filename' = "newressusample969.rnd", 'weak' = "3", 'failed' = "0"
'input' = "stream", 'filesize' = "1g900m", 'filename' = "newressusample970.rnd", 'weak' = "2", 'failed' = "0"
'input' = "stream", 'filesize' = "2g", 'filename' = "newressusample971.rnd", 'weak' = "4", 'failed' = "0"
'input' = "stream", 'filesize' = "3g", 'filename' = "newressusample972.rnd", 'weak' = "7", 'failed' = "0"
'input' = "stream", 'filesize' = "4g", 'filename' = "newressusample973.rnd", 'weak' = "7", 'failed' = "0"
'input' = "stream", 'filesize' = "5g", 'filename' = "newressusample974.rnd", 'weak' = "4", 'failed' = "0"
'input' = "stream", 'filesize' = "6g", 'filename' = "newressusample975.rnd", 'weak' = "3", 'failed' = "0"
$ 

Uusi ajo –stream optiolla: kuten odotin, lista näyttää samalta edellisen stream listan kanssa.

$ grep stream2 newressudieharder.skk
'input' = "stream2", 'filesize' = "500m", 'filename' = "newressusample1124.rnd", 'weak' = "2", 'failed' = "2"
'input' = "stream2", 'filesize' = "600m", 'filename' = "newressusample1125.rnd", 'weak' = "6", 'failed' = "0"
'input' = "stream2", 'filesize' = "700m", 'filename' = "newressusample1126.rnd", 'weak' = "4", 'failed' = "1"
'input' = "stream2", 'filesize' = "800m", 'filename' = "newressusample1127.rnd", 'weak' = "4", 'failed' = "0"
'input' = "stream2", 'filesize' = "900m", 'filename' = "newressusample1128.rnd", 'weak' = "8", 'failed' = "1"
'input' = "stream2", 'filesize' = "1g", 'filename' = "newressusample1129.rnd", 'weak' = "2", 'failed' = "0"
'input' = "stream2", 'filesize' = "1g100m", 'filename' = "newressusample1130.rnd", 'weak' = "7", 'failed' = "0"
'input' = "stream2", 'filesize' = "1g200m", 'filename' = "newressusample1131.rnd", 'weak' = "6", 'failed' = "0"
'input' = "stream2", 'filesize' = "1g300m", 'filename' = "newressusample1132.rnd", 'weak' = "3", 'failed' = "1"
'input' = "stream2", 'filesize' = "1g400m", 'filename' = "newressusample1133.rnd", 'weak' = "3", 'failed' = "1"
'input' = "stream2", 'filesize' = "1g500m", 'filename' = "newressusample1134.rnd", 'weak' = "5", 'failed' = "1"
'input' = "stream2", 'filesize' = "1g600m", 'filename' = "newressusample1135.rnd", 'weak' = "2", 'failed' = "0"
'input' = "stream2", 'filesize' = "1g700m", 'filename' = "newressusample1136.rnd", 'weak' = "2", 'failed' = "0"
'input' = "stream2", 'filesize' = "1g800m", 'filename' = "newressusample1137.rnd", 'weak' = "3", 'failed' = "0"
'input' = "stream2", 'filesize' = "1g900m", 'filename' = "newressusample1138.rnd", 'weak' = "2", 'failed' = "0"
'input' = "stream2", 'filesize' = "2g", 'filename' = "newressusample1139.rnd", 'weak' = "4", 'failed' = "0"
'input' = "stream2", 'filesize' = "3g", 'filename' = "newressusample1140.rnd", 'weak' = "7", 'failed' = "0"
'input' = "stream2", 'filesize' = "4g", 'filename' = "newressusample1141.rnd", 'weak' = "7", 'failed' = "0"
'input' = "stream2", 'filesize' = "5g", 'filename' = "newressusample1142.rnd", 'weak' = "4", 'failed' = "0"
'input' = "stream2", 'filesize' = "6g", 'filename' = "newressusample1143.rnd", 'weak' = "3", 'failed' = "0"
$ 

Rdrand, eli intel prosessorin tarjoama satunnaisuus on kanssa –dieharder mielessä ok.

$ grep \"rdrand\" newressudieharder.skk
'input' = "rdrand", 'filesize' = "500m", 'filename' = "newressusample995.rnd", 'weak' = "5", 'failed' = "2"
'input' = "rdrand", 'filesize' = "600m", 'filename' = "newressusample996.rnd", 'weak' = "3", 'failed' = "4"
'input' = "rdrand", 'filesize' = "700m", 'filename' = "newressusample997.rnd", 'weak' = "3", 'failed' = "1"
'input' = "rdrand", 'filesize' = "800m", 'filename' = "newressusample998.rnd", 'weak' = "7", 'failed' = "0"
'input' = "rdrand", 'filesize' = "900m", 'filename' = "newressusample999.rnd", 'weak' = "4", 'failed' = "1"
'input' = "rdrand", 'filesize' = "1g", 'filename' = "newressusample1000.rnd", 'weak' = "5", 'failed' = "0"
'input' = "rdrand", 'filesize' = "1g100m", 'filename' = "newressusample1001.rnd", 'weak' = "5", 'failed' = "0"
'input' = "rdrand", 'filesize' = "1g200m", 'filename' = "newressusample827.rnd", 'weak' = "2", 'failed' = "0"
'input' = "rdrand", 'filesize' = "1g300m", 'filename' = "newressusample1039.rnd", 'weak' = "4", 'failed' = "1"
'input' = "rdrand", 'filesize' = "1g400m", 'filename' = "newressusample862.rnd", 'weak' = "2", 'failed' = "0"
'input' = "rdrand", 'filesize' = "1g500m", 'filename' = "newressusample1040.rnd", 'weak' = "5", 'failed' = "0"
'input' = "rdrand", 'filesize' = "1g600m", 'filename' = "newressusample1041.rnd", 'weak' = "5", 'failed' = "0"
'input' = "rdrand", 'filesize' = "1g700m", 'filename' = "newressusample1048.rnd", 'weak' = "4", 'failed' = "1"
'input' = "rdrand", 'filesize' = "1g800m", 'filename' = "newressusample1049.rnd", 'weak' = "1", 'failed' = "0"
'input' = "rdrand", 'filesize' = "1g900m", 'filename' = "newressusample1050.rnd", 'weak' = "0", 'failed' = "0"
'input' = "rdrand", 'filesize' = "2g", 'filename' = "newressusample1051.rnd", 'weak' = "3", 'failed' = "0"
'input' = "rdrand", 'filesize' = "3g", 'filename' = "newressusample1052.rnd", 'weak' = "3", 'failed' = "1"
'input' = "rdrand", 'filesize' = "4g", 'filename' = "newressusample1053.rnd", 'weak' = "3", 'failed' = "0"
'input' = "rdrand", 'filesize' = "5g", 'filename' = "newressusample1073.rnd", 'weak' = "2", 'failed' = "0"
'input' = "rdrand", 'filesize' = "6g", 'filename' = "newressusample1074.rnd", 'weak' = "2", 'failed' = "0"
$ 

Ja toinen intel tuote –rdseed: –dieharder ok. Näistä löydät keskustelua netistä. Ne ovat newressussa valinnaisia.

$ grep \"rdseed\" newressudieharder.skk
'input' = "rdseed", 'filesize' = "500m", 'filename' = "newressusample937.rnd", 'weak' = "7", 'failed' = "2"
'input' = "rdseed", 'filesize' = "600m", 'filename' = "newressusample938.rnd", 'weak' = "6", 'failed' = "0"
'input' = "rdseed", 'filesize' = "700m", 'filename' = "newressusample939.rnd", 'weak' = "12", 'failed' = "0"
'input' = "rdseed", 'filesize' = "800m", 'filename' = "newressusample940.rnd", 'weak' = "2", 'failed' = "0"
'input' = "rdseed", 'filesize' = "900m", 'filename' = "newressusample941.rnd", 'weak' = "9", 'failed' = "0"
'input' = "rdseed", 'filesize' = "1g", 'filename' = "newressusample942.rnd", 'weak' = "3", 'failed' = "0"
'input' = "rdseed", 'filesize' = "1g100m", 'filename' = "newressusample1087.rnd", 'weak' = "6", 'failed' = "0"
'input' = "rdseed", 'filesize' = "1g200m", 'filename' = "newressusample835.rnd", 'weak' = "4", 'failed' = "0"
'input' = "rdseed", 'filesize' = "1g300m", 'filename' = "newressusample1088.rnd", 'weak' = "8", 'failed' = "0"
'input' = "rdseed", 'filesize' = "1g400m", 'filename' = "newressusample863.rnd", 'weak' = "4", 'failed' = "0"
'input' = "rdseed", 'filesize' = "1g500m", 'filename' = "newressusample1089.rnd", 'weak' = "3", 'failed' = "0"
'input' = "rdseed", 'filesize' = "1g600m", 'filename' = "newressusample1090.rnd", 'weak' = "1", 'failed' = "0"
'input' = "rdseed", 'filesize' = "1g700m", 'filename' = "newressusample1091.rnd", 'weak' = "4", 'failed' = "0"
'input' = "rdseed", 'filesize' = "1g800m", 'filename' = "newressusample1092.rnd", 'weak' = "1", 'failed' = "0"
'input' = "rdseed", 'filesize' = "1g900m", 'filename' = "newressusample1093.rnd", 'weak' = "3", 'failed' = "0"
'input' = "rdseed", 'filesize' = "2g", 'filename' = "newressusample1094.rnd", 'weak' = "8", 'failed' = "0"
'input' = "rdseed", 'filesize' = "3g", 'filename' = "newressusample1095.rnd", 'weak' = "0", 'failed' = "0"
'input' = "rdseed", 'filesize' = "4g", 'filename' = "newressusample1096.rnd", 'weak' = "1", 'failed' = "0"
'input' = "rdseed", 'filesize' = "5g", 'filename' = "newressusample1097.rnd", 'weak' = "3", 'failed' = "0"
'input' = "rdseed", 'filesize' = "6g", 'filename' = "newressusample1098.rnd", 'weak' = "0", 'failed' = "0"
$

Edellinen lista edellisestä ressutwist generaattorista:

'input' = "ressutwist", 'filesize' = "500m", 'filename' = "newressusample1751.rnd", 'weak' = "5", 'failed' = "1"
'input' = "ressutwist", 'filesize' = "600m", 'filename' = "newressusample1752.rnd", 'weak' = "8", 'failed' = "1"
'input' = "ressutwist", 'filesize' = "700m", 'filename' = "newressusample1753.rnd", 'weak' = "6", 'failed' = "2"
'input' = "ressutwist", 'filesize' = "800m", 'filename' = "newressusample1754.rnd", 'weak' = "7", 'failed' = "0"
'input' = "ressutwist", 'filesize' = "900m", 'filename' = "newressusample1755.rnd", 'weak' = "3", 'failed' = "2"
'input' = "ressutwist", 'filesize' = "1g", 'filename' = "newressusample1756.rnd", 'weak' = "2", 'failed' = "0"
'input' = "ressutwist", 'filesize' = "1g100m", 'filename' = "newressusample1757.rnd", 'weak' = "3", 'failed' = "1"
'input' = "ressutwist", 'filesize' = "1g200m", 'filename' = "newressusample1758.rnd", 'weak' = "5", 'failed' = "0"
'input' = "ressutwist", 'filesize' = "1g300m", 'filename' = "newressusample1759.rnd", 'weak' = "3", 'failed' = "1"
'input' = "ressutwist", 'filesize' = "1g400m", 'filename' = "newressusample1760.rnd", 'weak' = "4", 'failed' = "0"
'input' = "ressutwist", 'filesize' = "1g500m", 'filename' = "newressusample1761.rnd", 'weak' = "5", 'failed' = "0"
'input' = "ressutwist", 'filesize' = "1g600m", 'filename' = "newressusample1762.rnd", 'weak' = "7", 'failed' = "0"
'input' = "ressutwist", 'filesize' = "1g700m", 'filename' = "newressusample1763.rnd", 'weak' = "1", 'failed' = "0"
'input' = "ressutwist", 'filesize' = "1g800m", 'filename' = "newressusample1764.rnd", 'weak' = "6", 'failed' = "0"
'input' = "ressutwist", 'filesize' = "1g900m", 'filename' = "newressusample1773.rnd", 'weak' = "4", 'failed' = "1"
'input' = "ressutwist", 'filesize' = "2g", 'filename' = "newressusample1774.rnd", 'weak' = "4", 'failed' = "0"
'input' = "ressutwist", 'filesize' = "3g", 'filename' = "newressusample1775.rnd", 'weak' = "1", 'failed' = "0"
'input' = "ressutwist", 'filesize' = "4g", 'filename' = "newressusample1776.rnd", 'weak' = "2", 'failed' = "0"
'input' = "ressutwist", 'filesize' = "5g", 'filename' = "newressusample1777.rnd", 'weak' = "1", 'failed' = "0"
'input' = "ressutwist", 'filesize' = "6g", 'filename' = "newressusample1778.rnd", 'weak' = "3", 'failed' = "0"

Lisätty newressu:un test_bytes() -funktio, johon voit kirjoittaa oman satunnaisbittigeneraattorisi, ja ajaa sitä –test komentorivioptiolla. Tällöin kaikki newressu:un toiminnot ja newressutest? ohjelmat ovat käytössäsi generaattorin testaamiseen. Tällä hetkellä test_bytes() funktio palauttaa koko puskurin täynnä nollia. –dieharder ei jostain syystä toimi kunnolla jos satunnaismerkit ovat pelkkiä nollia.

Test koodi:

void test_bytes(int size, unsigned char *buffer) // JariK 2023
{
  // insert your generator here, run it with --test command parameter
  
  memset(buffer, 0, size);
}

int newressu_genbyte()
{
....
    else if(input == INPUT_TEST) // test generator
      test_bytes(sizeof(gent), gent);

----
}

void dump_sample() // & dieharder analysis
{
....
      else if(input == INPUT_TEST) // test generator
	test_bytes(blocksize, buffer);
....
}

int main(int argc, char *argv[])
{
....
      } else if(!strcmp("--test", argv[c])) { // test generator
	input = INPUT_TEST;
	input_str = argv[c] + 2;
      }
....
  if(input == INPUT_TEST) {
    fprintf(stderr,"%s: randomness from your generator (test_bytes)\n", procname);
  }
....
}

Tässä vielä satunnaisuutta –test oletuskoodilla:

$ ./newressu --test
./newressu: randomness from your generator (test_bytes)
00000 00000000000000000000000000000000000000000000000000000000000000000
00001 00000000000000000000000000000000000000000000000000000000000000000
00002 00000000000000000000000000000000000000000000000000000000000000000
00003 00000000000000000000000000000000000000000000000000000000000000000
00004 00000000000000000000000000000000000000000000000000000000000000000
00005 00000000000000000000000000000000000000000000000000000000000000000
00006 00000000000000000000000000000000000000000000000000000000000000000
00007 00000000000000000000000000000000000000000000000000000000000000000
00008 00000000000000000000000000000000000000000000000000000000000000000
00009 00000000000000000000000000000000000000000000000000000000000000000
$ 

Lisätty test_bytes testirutiiniin ressun, pseudoressun ja /dev/urandom:in yhdistelmä:

void test_bytes(int size, unsigned char *buffer) // JariK 2023
{
  // insert your generator here, run it with
  // --test command line parameter (source for
  // randomness)

  ressutwist_bytes(size, buffer);
  urandom_bytes(size, buffer);
  
  //memset(buffer, 0, size);

  //for(int c = 0; c < size; c++)
  //  buffer[c] = c & 0xff;
}

Ja ajettu edellinen testiscripti test generaattorilla: ressu-pseudoressu-urandom yhdistelmälläkin tulos on –dieharder kelpoinen.

'input' = "test", 'filesize' = "500m", 'filename' = "newressusample2665.rnd", 'weak' = "4", 'failed' = "0"
'input' = "test", 'filesize' = "600m", 'filename' = "newressusample2666.rnd", 'weak' = "6", 'failed' = "3"
'input' = "test", 'filesize' = "700m", 'filename' = "newressusample2667.rnd", 'weak' = "10", 'failed' = "1"
'input' = "test", 'filesize' = "800m", 'filename' = "newressusample2668.rnd", 'weak' = "3", 'failed' = "2"
'input' = "test", 'filesize' = "900m", 'filename' = "newressusample2669.rnd", 'weak' = "3", 'failed' = "1"
'input' = "test", 'filesize' = "1g", 'filename' = "newressusample2670.rnd", 'weak' = "3", 'failed' = "1"
'input' = "test", 'filesize' = "1g100m", 'filename' = "newressusample2671.rnd", 'weak' = "2", 'failed' = "0"
'input' = "test", 'filesize' = "1g200m", 'filename' = "newressusample2672.rnd", 'weak' = "4", 'failed' = "0"
'input' = "test", 'filesize' = "1g300m", 'filename' = "newressusample2673.rnd", 'weak' = "2", 'failed' = "1"
'input' = "test", 'filesize' = "1g400m", 'filename' = "newressusample2674.rnd", 'weak' = "2", 'failed' = "0"
'input' = "test", 'filesize' = "1g500m", 'filename' = "newressusample2675.rnd", 'weak' = "6", 'failed' = "0"
'input' = "test", 'filesize' = "1g600m", 'filename' = "newressusample2676.rnd", 'weak' = "5", 'failed' = "0"
'input' = "test", 'filesize' = "1g700m", 'filename' = "newressusample2677.rnd", 'weak' = "2", 'failed' = "0"
'input' = "test", 'filesize' = "1g800m", 'filename' = "newressusample2678.rnd", 'weak' = "6", 'failed' = "0"
'input' = "test", 'filesize' = "1g900m", 'filename' = "newressusample2679.rnd", 'weak' = "1", 'failed' = "0"
'input' = "test", 'filesize' = "2g", 'filename' = "newressusample2680.rnd", 'weak' = "4", 'failed' = "0"
'input' = "test", 'filesize' = "3g", 'filename' = "newressusample2681.rnd", 'weak' = "5", 'failed' = "0"
'input' = "test", 'filesize' = "4g", 'filename' = "newressusample2682.rnd", 'weak' = "4", 'failed' = "0"
'input' = "test", 'filesize' = "5g", 'filename' = "newressusample2683.rnd", 'weak' = "3", 'failed' = "0"
'input' = "test", 'filesize' = "6g", 'filename' = "newressusample2684.rnd", 'weak' = "4", 'failed' = "0"

Lisätty uusi komentoriviparametri –base, jolla voi tulostaa satunnaislukuja numerojärjestelmän kantaluvulla: esimerkiksi –base2 tulostaa binäärisiä lukuja:

$ ./newressu --base2 --space
00000  11000100 01111111 11001101 00011101 00010110 11100110 11010111
00001  01001011 10110000 10001001 00100010 00111100 00100010 00111001
00002  01001110 01110111 10001111 01111110 10000100 01100011 01000001
00003  01110111 11001010 00011001 11110001 01101000 11111000 00011000
00004  10000101 10001100 00010101 00010100 10100010 11111000 11111100
00005  11100110 10111110 01000000 01001011 01011000 01101000 10000111
00006  11000101 01100011 11000011 01010010 01100101 01101000 01110100
00007  10001001 10110101 01000100 10100010 10100101 00111101 01110010
00008  00101000 00011010 11000000 00011011 11101100 11110011 01110001
00009  00111100 11101110 01101100 00100011 10100010 10101011 01110010
$ 

–base8 Tulostaa oktaalisia lukuja:

$ ./newressu --base8 --space
00000  555 577 571 453 417 713 524 041 350 116 445 542 630 601 101 063
00001  110 137 621 616 464 522 350 133 141 516 440 030 276 666 025 041
00002  722 142 746 011 005 570 526 245 065 113 063 012 037 734 450 664
00003  055 652 736 117 031 130 673 271 700 500 573 752 344 545 137 664
00004  707 527 155 352 163 075 363 256 061 763 374 541 372 452 550 745
00005  350 435 256 756 356 763 454 472 170 523 442 513 662 022 002 442
00006  627 647 504 635 317 431 241 253 347 641 640 102 436 255 202 137
00007  450 066 722 262 331 446 336 750 607 650 143 172 522 105 424 207
00008  012 004 751 651 634 314 721 755 462 450 507 014 306 236 653 053
00009  211 057 450 474 524 047 017 346 211 071 235 501 626 474 715 175
$ 

–base10 tulostaa desimaalisia lukuja:

$ ./newressu --base10 --space
00000  95115 37937 89506 93199 97183 10901 23222 55074 57325 64779 14185
00001  51276 53941 32097 46818 39443 36891 04676 65278 82499 99531 14515
00002  83199 80506 17322 72150 41682 57004 48748 82340 77278 23999 48218
00003  60605 83877 91724 87016 18583 08774 07189 93432 28636 75338 33974
00004  30782 37979 14311 64447 42103 93364 15366 28464 50557 93526 37781
00005  48320 15913 14301 24410 93099 97241 65875 61581 43909 32427 94536
00006  96746 50411 50162 12843 31785 30160 37329 05794 66353 91398 38261
00007  75387 17734 87459 97700 02426 44844 07538 93280 86564 39398 35084
00008  92139 68175 16288 09362 65059 77951 77617 16583 71273 09692 13384
00009  06691 06553 33780 39081 64955 58401 92144 49439 40880 35723 89259
$ 

–base16 tulostaa heksadesimaalilukuja:

$ ./newressu --base16 --space
00000  4c37 8c70 bc04 6810 05b8 ed49 3177 19f7 17cc adb2 29d0 f020 621a
00001  b172 b32c 1ab5 a032 9d63 bc53 f412 0037 eefc d3d7 c8b1 978c 2099
00002  c22c fbed f611 32ad d5fe d4c4 3ba2 7e37 ab79 2342 5c69 aef0 7599
00003  29cc 01ec 005b 037f 53ab 8961 1140 35e3 3719 0580 ab56 fe80 2177
00004  d9df 80b6 3701 2682 d4e4 880c 030c f534 e84e 7017 379a f30e 0480
00005  371f 2b47 59cf 8ac2 4380 64d1 dd7b f1f7 a838 b2ad d3f9 8a03 0573
00006  3f1e 6f0c 0514 ef54 7312 a380 0d3c 6150 cdaa 15cd dace 4395 5d4b
00007  e898 84e8 c85f 39e7 5e50 013a da26 dc00 c447 7487 3b08 8dde c410
00008  f10a 14c7 128d fa4d 6732 f56e 5ea7 f77e 970f d164 daaf dc6b f255
00009  19f4 0c41 1ca1 ce7c 7466 0422 0943 af04 72c5 007a 480a 6dc6 7804
$ 

Edellä olivat perus numerojärjestelmät binääri, oktaali, desimaali ja heksadesimaali, mutta –base toimii myös muilla kantaluvuilla (2-64): esimerkiksi –base36 tulostaa 36 kantaisen numerojärjestelmän lukuja:

$ ./newressu --base36 --space
00000  vw6t4707 wmzejmor jnacj3wx pbytkend szssa2dt u29rofy4 7ddvaowo
00001  gxyyprag w2m2t8il h0rcz7zz 0a8j9z2p mrczccyl qj592k9w lj3obm93
00002  6oh3zd8d fpluix1l rcyjaeqt oynw60up 2763qwu1 891zbfls egwb6lvq
00003  x14rnc4f kuv2kz3p et1topzo 3imwi0li 7jzzf2w4 ks06zhuk 2xo1rog8
00004  d2gdqkfx v4bsh2w0 7uxk55wp ey2l6f7u ixcl2sab ue8tj2wy mxeuqsx0
00005  s51oe413 8zr2iifo xo93c33h 8iveqio1 zbmwvpnc clzy8vng 6bz994j4
00006  8b3z7870 7jzekf1v btzmmq4w 02v09ecd 2ic974kw 3w99jvxy 30z5z9yj
00007  p10k2f02 jqp7xc7f 0mjc41q5 molcvqmy goxbzcat p4e75z9h 16lm6mgr
00008  szn5d1v8 leepjby0 j08fhhf3 bn1hrd12 nji2qc6l 5oszvwxv uzufl14b
00009  rws9u11f x33fapt2 oub6phs2 91g8vl7p eas829wa b5q7z29s k724s4p9
$

36-kantaisessa numerojärjestelmässä on numerot ja kaikki pienet kirjaimet. ([0-9]+[a-z] = 10 + 26 = 36). Seuraavassa koodi:

static unsigned char *basedigits = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-";
....
int main(int argc, char *argv[])
{
....
      } else if(!strncmp("--base", argv[c], 6)) {

	if(*(argv[c] + 6) != '\0') {
	  base = atoll(argv[c] + 6);
	} else if(c + 1 < argc) {
	  base = atoll(argv[c + 1]);
	  c++;
	}
	if(base < 2 || base > 64)
	  base = -1;
	
      } else if(!strncmp("--BASE", argv[c], 6)) {

	if(*(argv[c] + 6) != '\0') {
	  base = atoll(argv[c] + 6);
	  uppercase = 1;
	} else if(c + 1 < argc) {
	  base = atoll(argv[c + 1]);
	  uppercase = 1;
	  c++;
	}
	if(base < 2 || base > 64) {
	  base = -1;
	  uppercase = -1;
	}   
....'
      } else if(!strcmp("-b", argv[c]) ||
		!strcmp("--bin", argv[c]) ||
		!strcmp("--1bit", argv[c]) ||
		!strcmp("--1bits", argv[c])) {

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

	base = 4;

      } else if(!strcmp("-o", argv[c]) ||
		!strcmp("--oct", argv[c]) ||
		!strcmp("--3bits", argv[c]) ) {

	base = 8;

      } else if(!strcmp("-d", argv[c]) ||
		!strcmp("--dec", argv[c])) {

	base = 10;

      } else if(!strcmp("-x", argv[c]) ||
		!strcmp("--hex", argv[c]) ||
		!strcmp("--4bits", argv[c])) {

	base = 16;

      } else if(!strcmp("-X", argv[c]) ||
		!strcmp("--HEX", argv[c]) ||
		!strcmp("--4BITS", argv[c])) {

	base = 16;
	uppercase = 1;

      } else if(!strcmp("-5", argv[c]) ||
		!strcmp("--5bits", argv[c])) {

	base = 32;
	
      } else if(!strcmp("--5BITS", argv[c])) {

	base = 32;
	uppercase = 1;
	
      } else if(!strcmp("-2", argv[c]) ||
		!strcmp("--6bits", argv[c])) {

	base = 64;
	
....
  if(size == -1) {
    if(base == 2)
      size = 8;
    else if(base == 8)
      size = 3;
    else if(base == 10)
      size = 5;
    else if(base == 16)
      size = 4;
    else
      size = 8;
  }

  if(base == -1) {
    base = 10;
  } else {
    digits = malloc(base + 1);
    strncpy(digits, basedigits, base);
    if(uppercase == 1 && base <= 36) { // if lowercase only ([0-9] + [a-z], 10 + 26
      for(int c = 0; c < base; c++) { // to uppercase
        if(digits[c] >= 'a' && digits[c] <= 'z')
          digits[c] = digits[c] - 'a' + 'A';
        else if(digits[c] >= 'A' && digits[c] <= 'Z')
          digits[c] = digits[c] - 'A' + 'a';
      }
    }

    digits[base] = '\0';

    digitsext = digits;
    
    charspaces = 0;
    charwidth = 1;
  }
....
}

Vielä –base toiminnon tarkistamiseen käytettyjä debukkeja:

#define aDEBUG67

#ifdef DEBUG67
  unsigned long long big = 0xffffffffffffffff;
  fprintf(stdout,"\n0x%llx\n", big);
  fprintfbase(stdout, big, 2);
  fprintf(stdout,"\n0o%llo\n", big);
  fprintfbase(stdout, big, 8);
  fprintf(stdout,"\n0d%llu\n", big);
  fprintfbase(stdout, big, 10);
  fprintf(stdout,"\n0x%llx\n", big);
  fprintfbase(stdout, big, 16);
  fprintf(stdout,"\n");

  //for(unsigned long long ll = 4096; ll > 0; ll+=4096) {
  for(unsigned long long ll = 0; ll < 1047576; ll++) {
  //for(unsigned long long ll = 1; ll != 0; ll *= 2) {
    fprintf(stdout,"%llu:", ll);
    fflush(stdout);
    for(int base = 2; base <= 64; base++) {
      fprintf(stdout," ");
      fprintfbase(stdout, ll, base);
      fflush(stdout);
    }
    fprintf(stdout,"\n");
  }
#endif

Edellinen debukki suoritetaan ennen newressu:n alkua main() rutiinissa. Se tulostaa seuraavanlaisen listan: Vasemmassa reunassa on desimaalijärjestelmän numero ja siitä oikealle on eri numerojärjestelmien numerot 2-järjestelmästä (binääri) 64 järjestelmään. Binääriluvut, oktaaliluvut desimaaliluvut ja heksadesimaaliluvut on aloitettu totutuilla prefix:eillä 0b, 0o, 0d ja 0x.

0: 0b0 0 0 0 0 0 0o0 0 0d0 0 0 0 0 0 0x0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
1: 0b1 1 1 1 1 1 0o1 1 0d1 1 1 1 1 1 0x1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
2: 0b10 2 2 2 2 2 0o2 2 0d2 2 2 2 2 2 0x2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
3: 0b11 10 3 3 3 3 0o3 3 0d3 3 3 3 3 3 0x3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3
4: 0b100 11 10 4 4 4 0o4 4 0d4 4 4 4 4 4 0x4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4
5: 0b101 12 11 10 5 5 0o5 5 0d5 5 5 5 5 5 0x5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
6: 0b110 20 12 11 10 6 0o6 6 0d6 6 6 6 6 6 0x6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
7: 0b111 21 13 12 11 10 0o7 7 0d7 7 7 7 7 7 0x7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7
8: 0b1000 22 20 13 12 11 0o10 8 0d8 8 8 8 8 8 0x8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8
9: 0b1001 100 21 14 13 12 0o11 10 0d9 9 9 9 9 9 0x9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9
10: 0b1010 101 22 20 14 13 0o12 11 0d10 a a a a a 0xa a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a
11: 0b1011 102 23 21 15 14 0o13 12 0d11 10 b b b b 0xb b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b
12: 0b1100 110 30 22 20 15 0o14 13 0d12 11 10 c c c 0xc c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c
13: 0b1101 111 31 23 21 16 0o15 14 0d13 12 11 10 d d 0xd d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d
14: 0b1110 112 32 24 22 20 0o16 15 0d14 13 12 11 10 e 0xe e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e
15: 0b1111 120 33 30 23 21 0o17 16 0d15 14 13 12 11 10 0xf f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f
16: 0b10000 121 100 31 24 22 0o20 17 0d16 15 14 13 12 11 0x10 g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g
17: 0b10001 122 101 32 25 23 0o21 18 0d17 16 15 14 13 12 0x11 10 h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h
18: 0b10010 200 102 33 30 24 0o22 20 0d18 17 16 15 14 13 0x12 11 10 i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i
19: 0b10011 201 103 34 31 25 0o23 21 0d19 18 17 16 15 14 0x13 12 11 10 j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j
20: 0b10100 202 110 40 32 26 0o24 22 0d20 19 18 17 16 15 0x14 13 12 11 10 k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k
21: 0b10101 210 111 41 33 30 0o25 23 0d21 1a 19 18 17 16 0x15 14 13 12 11 10 l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l
22: 0b10110 211 112 42 34 31 0o26 24 0d22 20 1a 19 18 17 0x16 15 14 13 12 11 10 m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m
23: 0b10111 212 113 43 35 32 0o27 25 0d23 21 1b 1a 19 18 0x17 16 15 14 13 12 11 10 n n n n n n n n n n n n n n n n n n n n n n n n n n n n n n n n n n n n n n n n n
24: 0b11000 220 120 44 40 33 0o30 26 0d24 22 20 1b 1a 19 0x18 17 16 15 14 13 12 11 10 o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o
25: 0b11001 221 121 100 41 34 0o31 27 0d25 23 21 1c 1b 1a 0x19 18 17 16 15 14 13 12 11 10 p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p
26: 0b11010 222 122 101 42 35 0o32 28 0d26 24 22 20 1c 1b 0x1a 19 18 17 16 15 14 13 12 11 10 q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q
27: 0b11011 1000 123 102 43 36 0o33 30 0d27 25 23 21 1d 1c 0x1b 1a 19 18 17 16 15 14 13 12 11 10 r r r r r r r r r r r r r r r r r r r r r r r r r r r r r r r r r r r r r
28: 0b11100 1001 130 103 44 40 0o34 31 0d28 26 24 22 20 1d 0x1c 1b 1a 19 18 17 16 15 14 13 12 11 10 s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s
29: 0b11101 1002 131 104 45 41 0o35 32 0d29 27 25 23 21 1e 0x1d 1c 1b 1a 19 18 17 16 15 14 13 12 11 10 t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t
30: 0b11110 1010 132 110 50 42 0o36 33 0d30 28 26 24 22 20 0x1e 1d 1c 1b 1a 19 18 17 16 15 14 13 12 11 10 u u u u u u u u u u u u u u u u u u u u u u u u u u u u u u u u u u
31: 0b11111 1011 133 111 51 43 0o37 34 0d31 29 27 25 23 21 0x1f 1e 1d 1c 1b 1a 19 18 17 16 15 14 13 12 11 10 v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v
32: 0b100000 1012 200 112 52 44 0o40 35 0d32 2a 28 26 24 22 0x20 1f 1e 1d 1c 1b 1a 19 18 17 16 15 14 13 12 11 10 w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w
33: 0b100001 1020 201 113 53 45 0o41 36 0d33 30 29 27 25 23 0x21 1g 1f 1e 1d 1c 1b 1a 19 18 17 16 15 14 13 12 11 10 x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x
34: 0b100010 1021 202 114 54 46 0o42 37 0d34 31 2a 28 26 24 0x22 20 1g 1f 1e 1d 1c 1b 1a 19 18 17 16 15 14 13 12 11 10 y y y y y y y y y y y y y y y y y y y y y y y y y y y y y y
35: 0b100011 1022 203 120 55 50 0o43 38 0d35 32 2b 29 27 25 0x23 21 1h 1g 1f 1e 1d 1c 1b 1a 19 18 17 16 15 14 13 12 11 10 z z z z z z z z z z z z z z z z z z z z z z z z z z z z z

Toinen debukki on jo vanha, mutta sitä on hiukan päivitetty: se on DEBUG24 seuraavassa gen_limit() rutiinissa:

#define DEBUG24 2

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

  if(lastlimit != limit) { // if limit changes, calculate new highlimit and bytes
    lastlimit = limit;
    if(limit <= 0x100) { // one byte
      // highest multiplier of limit that fits to needed bytes
      highlimit = (0x100 / limit) * limit;
      // number of bytes needed
      bytes = 1;

    } else if(limit <= 0x10000) { // two bytes
      highlimit = (0x10000 / limit) * limit;
      bytes = 2;

    } else if(limit <= 0x1000000) { // three bytes
      highlimit = (0x1000000 / limit) * limit;
      bytes = 3;

    } else if(limit <= 0x100000000) { // four bytes
      highlimit = (0x100000000 / limit) * limit;
      bytes = 4;

    } else if(limit <= 0x10000000000) { // five bytes
      highlimit = (0x10000000000 / limit) * limit;
      bytes = 5;

    } else if(limit <= 0x1000000000000) { // six bytes
      highlimit = (0x1000000000000 / limit) * limit;
      bytes = 6;

    } else if(limit <= 0x100000000000000) { // seven bytes
      highlimit = (0x100000000000000 / limit) * limit;      
      bytes = 7;

    } else { // if(limit <= 0xffffffffffffffff) {
      highlimit = (0xffffffffffffffff / limit) * limit;      
      bytes = 8;

    }
  } // if(lastlimit != limit

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

  word %= limit;
  
#ifdef DEBUG24
  fprintf(stdout,"/");
  fprintf(stdout,"%d bytes: ", bytes);
  fprintf(stdout,"[");
  if(base == -1)
    fprintfbase(stdout, word, 16);
  else
    fprintfbase(stdout, word, base);
  fprintf(stdout,"]");
  fprintf(stdout,"/");
  fflush(stdout);
#endif

  return(word);
}

DEBUG24 tulostaa seuraavan listan: Listalla hakasulkeiden välissä on satunnaisluku halutussa numerojärjestelmässä ja kauttaviivojen välissä on newressu:n tulostama osa. Debukkailussa vertaillaan vaan näitä lukuja keskenään, jos ne poikkeavat, kysymys voi olla bugista.

$ ./newressu --base36
/6 bytes: [s8mgcnh3]/00000 s8mgcnh3/6 bytes: [9k2ib3e1]/9k2ib3e1/6 bytes: [9wp3bkxs]/9wp3bkxs/6 bytes: [518vdcat]/518vdcat/6 bytes: [ngdb4pp7]/ngdb4pp7/6 bytes: [rpttb929]/rpttb929/6 bytes: [lwo0fh64]/lwo0fh64/6 bytes: [hnb2u25m]/hnb2u25m
/6 bytes: [skbzruoy]/00001 skbzruoy/6 bytes: [sm3zjnoj]/sm3zjnoj/6 bytes: [8oypbwn7]/8oypbwn7/6 bytes: [41gq1eiw]/41gq1eiw/6 bytes: [z3o8q3sp]/z3o8q3sp/6 bytes: [tmtmov02]/tmtmov02/6 bytes: [9t0wdd6k]/9t0wdd6k/6 bytes: [4493jrr9]/4493jrr9
/6 bytes: [lfjc14j4]/00002 lfjc14j4/6 bytes: [q1eg504x]/q1eg504x/6 bytes: [l4pj6fo6]/l4pj6fo6/6 bytes: [mg5b9kwl]/mg5b9kwl/6 bytes: [zfk08not]/zfk08not/6 bytes: [srd8rz6v]/srd8rz6v/6 bytes: [m1zf881h]/m1zf881h/6 bytes: [51gvzg98]/51gvzg98
/6 bytes: [qdwjg75r]/00003 qdwjg75r/6 bytes: [6xgb7i7]/06xgb7i7/6 bytes: [jhqu1f3p]/jhqu1f3p/6 bytes: [fwyo6fal]/fwyo6fal/6 bytes: [n6dgl1wy]/n6dgl1wy/6 bytes: [mvco358n]/mvco358n/6 bytes: [siqxwadz]/siqxwadz/6 bytes: [bm64i7dy]/bm64i7dy
/6 bytes: [brgihpa2]/00004 brgihpa2/6 bytes: [ysj25oc3]/ysj25oc3/6 bytes: [9fls8bwv]/9fls8bwv/6 bytes: [zfjklzhm]/zfjklzhm/6 bytes: [exeky1m2]/exeky1m2/6 bytes: [mqi11o2o]/mqi11o2o/6 bytes: [b7hzs0ip]/b7hzs0ip/6 bytes: [2dt4wq9d]/2dt4wq9d
/6 bytes: [6sori6y4]/00005 6sori6y4/6 bytes: [l4iv2qgr]/l4iv2qgr/6 bytes: [9yc39f25]/9yc39f25/6 bytes: [ijtan82p]/ijtan82p/6 bytes: [u03ert1n]/u03ert1n/6 bytes: [hf2cy6qf]/hf2cy6qf/6 bytes: [o936snch]/o936snch/6 bytes: [6pyqcy9t]/6pyqcy9t
/6 bytes: [d17dc4fh]/00006 d17dc4fh/6 bytes: [1z2dtov8]/1z2dtov8/6 bytes: [ncw5v2ry]/ncw5v2ry/6 bytes: [lg90wern]/lg90wern/6 bytes: [v8k27knh]/v8k27knh/6 bytes: [i5gpv5rr]/i5gpv5rr/6 bytes: [a401wuzc]/a401wuzc/6 bytes: [qb872hiu]/qb872hiu
/6 bytes: [3e28gqkv]/00007 3e28gqkv/6 bytes: [90gvp2pb]/90gvp2pb/6 bytes: [wa449h7o]/wa449h7o/6 bytes: [r0yo8qnw]/r0yo8qnw/6 bytes: [ohettmrn]/ohettmrn/6 bytes: [zkznxobg]/zkznxobg/6 bytes: [o8tsp19w]/o8tsp19w/6 bytes: [adlj1ab0]/adlj1ab0
/6 bytes: [33fwjh4v]/00008 33fwjh4v/6 bytes: [m695umdx]/m695umdx/6 bytes: [50bi59u0]/50bi59u0/6 bytes: [cabcjlyh]/cabcjlyh/6 bytes: [ont9hx9s]/ont9hx9s/6 bytes: [m4olw55]/0m4olw55/6 bytes: [wmegho9f]/wmegho9f/6 bytes: [600aj7k5]/600aj7k5
/6 bytes: [d711s29h]/00009 d711s29h/6 bytes: [8vcucf6y]/8vcucf6y/6 bytes: [x2ekuqo8]/x2ekuqo8/6 bytes: [msmqzwue]/msmqzwue/6 bytes: [kgdm7xyw]/kgdm7xyw/6 bytes: [rmeff22k]/rmeff22k/6 bytes: [wa6xtfdw]/wa6xtfdw/6 bytes: [lmhmb5ti]/lmhmb5ti
$ 

Vielä debukkauksessa käytetty rutiini, joka tulostaa long long:in ll base lukujärjestelmässä:

void fprintfbase(FILE *fp1, unsigned long long ll2, int base) // JariK 2023
{
  int count;
  unsigned long long ll, divider;

  if(base == 2)
    fprintf(fp1,"0b");
  else if(base == 8)
    fprintf(fp1,"0o");
  else if(base == 10)
    fprintf(fp1,"0d");
  else if(base == 16)
    fprintf(fp1,"0x");
  
  ll = ll2;
  divider = 1;
  count = 0;

  for(;;) { // count digits
    ll /= base;
    count++;
    if(ll == 0)
      break;
    divider *= base; // do not count last digit
  }

  while(count > 0) { // print digits
    fprintf(fp1,"%c", basedigits[(ll2 / divider) % base]);
    divider /= base;
    count--;
  }
}

Lisätty kenttiä ressu_nonrandom() rutiiniin. Näin pyritään varmistamaan, että eri prosessissa(getpid) ja eri kellonaikaan(tv.tc_sec ja tv.tv_usec) generoidut satunnaisluvut ovat varmasti erilaisia. Lisäksi on lisätty satunnaisuutta prosessin kuluttaman ajan lisäämisellä (clock).

Periaatteessa ressun pääluuppi riittää kelvollisiin satunnaislukuihin, mutta tapauksessa jossa koneen kellojono on liian säännöllinen tarvitaan copy-reverse:n tuomaa lisää. Se että kellonaika tuodaan mukaan copy-reverse:en siirtää satunnaisuuden generoinnin uudelle tielle siinä tapauksessa että kello on asetettu. Tapauksessa, jossa kellonaikaa ei ole jäädään kellojonon uniikkisuuden varaan. Jatkossa vielä yksi testiajo, jossa tutkitaan kellojonon uniikkisuutta (newressutest16).

Tähän nonrandom:iin voisi vielä lisätä prosessorin id:n yksilöimään konetta, mutta sellaista ei ole saatavilla (onneksi..). Toki sen voisi korvata prosessorin tekemällä satunnaisluvulla (intel:in tapauksessa rdrand), mutta sitä en kiistanalaisuuden vuoksi tehnyt.

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

  gettimeofday(&tv, NULL);

  rando = rando +
    //genbytes +
    //time(NULL) +
    //ressu_useconds() +
    //ressu_clock() +
    //buf.tms_utime + 
    //buf.tms_stime +
    getpid() +
    clockbytes +
    tv.tv_usec +
    tv.tv_sec +
    clock() / CLOCKS_PER_SEC +
    clock() % CLOCKS_PER_SEC +
    ch * ch;

#ifdef DEBUG04
  count[rando % ressuct_bytes]++;
#endif
  
#ifdef DEBUG2C
  fprintf(stderr," %u", rando % ressuct_bytes);
  newressu_output = 1;
#endif
  
  return(rando);
}

Jo edellisessä rutiinissa olevalla DEBUG04 osuudella on tarkoitus tarkastella nonrandom:in hajontaa puskurissa. Seuraavassa muut (ja edellinen) DEBUG04:n osat:

....
#define DEBUG04 2

#ifdef DEBUG04

unsigned long count[RESSUT_BYTES];

#endif
....
#ifdef DEBUG04
  count[rando % ressuct_bytes]++;
#endif
....
#ifdef DEBUG04

  for(int c = 0; c < RESSUT_BYTES; c++)
    count[c] = 0;

#endif
....
#ifdef DEBUG04
  int bufferlen = 0;
  for(int c = 0; c < ressuct_bytes; c++) {
    unsigned char buffer[16];
    sprintf(buffer, "%lu", count[c]);
    if(bufferlen < strlen(buffer))
      bufferlen = strlen(buffer);
  }
  for(int c = 0; c < ressuct_bytes; c++) {
    if(c % 16 == 0) {
      if(c > 0)
	fprintf(stdout,"\n");
      fprintf(stdout,"%05u ", c);
    }
    fprintf(stdout,"%*lu", bufferlen + 1, count[c]);
  }
  fprintf(stdout,"\n");
#endif
....

DEBUG04:n tulostaa seuraavan kaltaisen raportin: Tällä kertaa en tarkemmin tutkinut muuta kuin sen että kaikkia puskurin merkkejä käytetään tasaisesti.

$ ./newressu -l100000 --quiet
00000  1005  956  956 1011  967  987  975 1039  981  998 1047 1022  982  943 1009  952
00016   961 1061 1005  923 1014 1026 1020 1032  954  962  983  995 1000 1029  904  947
00032   967  950 1011 1017 1019 1022  922 1005 1026 1002  976 1002  987  984 1005  980
00048  1054  999  998  974  997  953  987 1028  977  947 1010  985  977  973  915  975
00064   944 1042  940  954 1019  915 1003  968  988 1018  994  942 1005  974  973 1011
00080   987  947 1009  937  987  973  956  940 1005  971  972 1004  988  921 1006 1040
00096  1016  964 1008  982 1003  989 1010  988 1021 1005 1019  955  972  963  965  979
00112   970  972 1039 1005  983 1008 1010  965  999 1044  969  939  994  961  986 1024
00128   941  943 1019  950  999  934 1021  945 1035  978  951  945 1009  967 1019 1022
00144   995  992  992  984 1025  989 1013 1037  970  989  986  997  993  994  930  980
00160  1012  981  977 1013  993  980 1009  929 1017 1037  977  922  956 1068  974  976
00176  1014  986  993  958 1010  975  989  970  933  985  980 1025  918  949 1034  923
00192   974 1015  957  990  985  985  991  998 1034 1029  949  971 1030  997  991  946
00208   961  988  986  960  950  985  980  950  969 1032 1011  982  940  965  989 1002
00224  1067 1026  973  965 1001  989  987  951  964  971  951  954  929 1066  999  975
00240   967 1025  928  989 1025  982  979 1002  943 1007  957 1010  965  990  950 1001
00256   996 1019 1004  976 1062  953 1023 1043  956  991  966  997  963 1036  947  974
00272   980  931  945  935 1013 1020  944  967  987 1018  977 1010  967  951  990  922
00288  1015  976 1008  992  985  930  998  994  970  995 1067  994  949  997  987 1008
00304  1030 1095 1034  997 1014 1064 1024 1029  988  948  988  970 1056 1035  983 1002
00320   989  994  989 1007  995 1047  964  988 1014  975 1062 1016  992 1009  981  957
00336  1000  960  992  963  951 1004  964  999 1000 1023  971  956  986  961 1015  980
00352   989  982  971  959  968  980  923  974  994 1011 1006  994 1002 1008  954  974
00368  1038  950  980  999  957  999  972  921 1012 1015 1042  984  988  970  960  989
00384   987  958  978  956 1042 1051  967 1004  963  987  968  987 1001 1011 1013 1018
00400   961 1023 1027  976  965  969  891 1010  941 1005  954 1046 1035  995 1003  961
00416   995 1003 1017 1012 1028  970  980  973 1019  986  979 1006  976  998  994  952
00432  1024  981  931  972  974  977  971 1008 1028  973 1011  977  977 1009  907  963
00448  1017 1008  936  984  959  986 1036 1055  998 1087 1014  975  989  932  935 1063
00464  1028 1021 1026 1006  988  961 1028  956  980  999  986  992  984 1003 1016  977
00480   968  982 1034  981 1019  982  946  982  987  993  999  978  987 1034 1020 1052
00496   969  989 1042  988  994 1022  991  926  967  963  942  994 1012 1010  948  967
00512  1045 1029 1016  995  963  968 1001 1013  986 1008  977 1034  968 1010 1028  982
00528   965 1036  984 1011  974 1013 1021  971 1015 1045  973  984  960  927 1045  994
00544   946  961  974 1012 1018 1025  960  996  969  989 1019 1009  998 1008  944 1014
00560   963  979 1015  981 1021  968  986  986  978 1000 1050  948  961  993  982  960
00576  1025 1018 1008 1048  944  975 1013  936 1009  972 1007  960 1020 1017  997 1035
00592   968 1000 1009  975 1006 1026  980 1018 1021  967  933  933  978  985 1008  985
00608   991  940  942  945  995 1011  991  974 1016 1004  960  945 1005  953  999 1016
00624  1050  996  984 1035  995  961 1011 1021 1001  984  981 1022  988  939  962 1003
00640  1023  953  927  974  996  977  965 1009  964 1028  975 1059 1020  996 1017  965
00656  1021 1001  939  988  982  999 1001 1005  959  961  953 1030  948  949  991  981
00672   971  947  961  983 1030  951  962  968  968 1009  968  937  915  967 1009 1014
00688   979 1000  921 1040  967 1039  989  951  957 1004  981  989  992 1013  891  946
00704   956  995 1035  970 1000  992  962  999 1004  999  980 1060  994 1018  989 1007
00720   971  983  991  977  942 1016 1011  918 1020  973  957  983  973 1022 1006  969
00736  1004  994  983  930  975 1011  993  974  986  990 1027  996  998 1005  987 1006
00752  1054  954  985  977  954 1027 1001 1026 1002 1011 1003  996  943  993  992  941
00768  1036  954 1012  954 1001 1066  987 1006 1026  988  986 1055  937 1005  995 1000
00784   993  952  962  983  975  937  977  989 1011 1006 1012  992 1024  933  980 1013
00800   960  991  969  961 1017 1007  943  979  920 1055  973  962 1012  952  915 1008
00816  1027  985 1022  989  950  977  974  987  973 1003  981  992  929 1015  965  971
00832   994  983 1020  955 1024 1035  956 1055  990  957  932 1015 1013  996 1028 1002
00848   953 1008  978  986  997  970  985 1014  979  957 1002 1011 1011  993  994  970
00864   987  991 1035 1066  996  973  989  975  998  981 1052  983  934  960 1012  968
00880   976  955  976 1032  969  920  959  929  987  969 1000  979  974  986  984  982
00896  1026  910 1010 1054 1014  910 1009  967 1027  997  994  953  947 1035  984  959
00912  1026  976 1000 1002 1008  975 1015  952  979 1008  957  944 1032 1040 1014  957
00928  1045  999 1000 1004  963  960  979  957  992  953  991 1025  970  920  980  961
00944   983  930  943  898 1000 1075 1049  960 1014 1051 1027  937 1018  975  977  964
00960  1009  993 1019  986 1007  997  981  992  967 1000 1022  964  995  949  918  950
00976  1017 1017  957  973  981  896  979  975 1002  998  970  990 1044 1002 1018  970
00992   969 1003  980  970 1002  982 1012 1045  985  996  991 1026  998 1046  924  992
01008  1026 1009  976  936  987 1008  977  985 1021  999 1017  985 1075 1041  953  978
01024   947 1011  955  978  954  947  947  985 1020 1007 1005  983 1002  975  954  922
01040  1034  995 1017  950 1030  989  974  945  932  992  969 1005  993  932  940  985
01056  1009  989  957  934 1019 1043  991  980  938  993 1004 1003  962  955 1000  949
01072   946  947 1005  979 1019 1008 1039  985 1031  971  973  943  929  957  950 1041
01088   964  973 1025  994  952  929  967 1040 1019 1015 1052  959 1018 1021  943 1013
01104   911 1020  999  998  947 1004  964  952  956  998 1012  997  964  984 1013  984
01120   930 1003  973  946  977 1007  940 1062  928 1012 1009  995 1000 1007  999  983
01136   952 1017  961  954  937 1013  983  993  956  931 1063  944  974 1066 1015 1003
01152   990  973 1009  965  960 1020  978  986 1039  971  970  983 1004  989  958  940
01168   994 1009  948  974  995  911 1013 1021  973 1037  994 1021  998  968  997  964
01184  1018 1010 1025  964 1013  957  970 1028  994 1012 1014  937  983 1016 1006 1015
01200   977  961  971 1034  954  976 1034  994  990  963  947 1024  996 1044  935  965
01216  1013  984  971  997 1023 1032 1023  949  962 1050  997  992  958  982  948 1011
01232  1008  973  973  997  893 1003  997 1010  931  979  950  979  964  957  953  970
01248   959  980  922  980  996  965  963  948  945 1014 1054 1034  971  948 1002  964
01264  1077 1038  989  966  952  951 1027  947 1032  980  993 1034  979  953 1026  988
01280  1022  975 1004 1003 1016  964  985  986 1009 1008 1018  990 1058  936 1000  994
01296  1038 1014  941  927 1046  963 1055  954  950 1042  991 1014  956 1012 1006 1038
01312   965  992 1041  998 1022  965  905  944  950 1003  973  973 1053  964 1018  988
01328   990 1000  985  982  972  974 1015 1007  964  963  967  989  950  994  985  974
01344  1027 1026  950 1018 1017  991  963  961 1027  970  938  998  925 1024  973 1004
01360   996  955 1018  972  991  971 1016  993  962  998 1044  991  990 1008  987  947
01376  1002 1028  942  983  982  961 1003 1039  978 1007 1032  961  981  966  987 1050
01392   930  966  967 1006 1040 1000  990  978  946  931 1000  960  977 1019  960  980
01408   937 1023 1027 1051 1015  983 1008  949  951  956  961  977  993  986  951  930
01424  1024  999  979 1046  952  943  973 1054  987  995 1009  985 1028  945  992  963
01440  1031  967  999  989 1020  966  937  959 1034  952 1010 1023  961 1015  963 1011
01456  1026 1035 1051  963  937  924  973  959 1028  997 1048 1053  987  913  926 1026
01472   953  964  975  986  919  962 1012  995  952 1056  970 1002 1032  994  889  960
01488   948  910  992 1017  955  988 1002 1020  891 1027 1000  962  989 1017 1010 1045
01504  1033 1021  960 1014  973  988 1038  990 1023  986  987  961  936 1003  952 1001
01520   999  908 1026  976  989  971  964  986 1008 1040  957  971  954  991  968  979
01536  1001  948 1005  976  975  999 1017  930  999 1006 1028  982  994  986  949 1028
01552  1022 1000  923  956 1014  934 1068 1013  985  963  959 1014  927  984 1064 1074
01568   952 1010 1035 1033  995  929  976  985  984  983  984  951  992  994  994 1003
01584   946 1024  978 1059  962 1008 1023  943  982  984  978 1028 1025  978  960  929
01600   999 1030 1023  992 1006 1024  979  965 1028  958 1037  997  972  972 1056  929
01616  1025  995 1006 1004  993  991  953  970  985  955 1020 1024  972  949  968  972
01632   978  983 1026 1041 1000 1021 1013  985 1009  937  980  902  997  940  988  988
01648   991  974 1037  974  982  966  983  963  976  987  953 1015  990  992  964 1045
01664   954  960  941 1004 1034  995 1010  952 1015  955  985 1000 1012 1019  949 1045
01680   971  998 1016 1008  969  965  951  916  958  996  954 1035  968  955  955  970
01696  1023  982 1004  986  993  964 1000  966 1024  959  939  954  976  948 1011 1020
01712   964  972 1000  954 1002  971  963  997  964 1012  959 1008  963  991 1005  957
01728  1015  955  998  932 1044  948  975 1006 1000 1032  964  972  950  948  960 1045
01744  1022  990  957  970  991  985  954 1016  919  939 1014  957  933  971 1039  982
01760   989  991  910  997 1033 1036  969 1016  951 1016  981  967  982  942  960  959
01776  1015 1026 1007  995 1006  957 1023  968 1068  990  972  978  908  979  982  944
01792  1038 1036  984  990  974  987  968  970  974 1038  979  936  930  957  962 1014
01808  1011  968  992 1039  959 1042  933  987 1046 1008 1009 1017  988 1040  941 1023
01824   979  982  970 1019  959  945  976 1019 1045 1041  956  953 1029  986  930  981
01840   982  959  991  979  952 1015  963  993 1014 1015  989  966 1037  975 1003 1005
01856   994  997  969  953  962  999  923  989  970  903  983 1053  907  972 1047 1013
01872   987 1041  968  963  979 1019 1002 1061 1008 1015  989 1027 1042  910  954  971
01888   941  979  995 1059 1036  993  967 1000  937  967  945 1035 1011  904  923  960
01904  1007 1016  983 1035  977  959  982  941  930  942 1000  921  947 1026  958 1016
01920   966  996 1082  931  990  933 1016 1037  979  967 1042 1009  952  980 1015 1002
01936   963  962  966 1014  947  976 1003  965 1005 1046  974  973 1078  924  983 1027
01952   991  971  968 1015  916 1032  994  990 1021 1001  972 1033  987 1040 1015  987
01968   932 1061  979  948 1016  978  947  892 1019 1015 1026  997  994  968  958  990
01984   928  962 1011 1043  966 1023  978 1058  943 1007 1009 1019  974  995  969  974
02000   935 1023 1028  998  985  993 1032  958 1006 1017  950  986  992  983 1000  910
02016  1038 1025 1008  971  900  926  983 1001  943  954  974 1002  997 1051  976 1002
02032  1010  989  950  991 1022  985  957 1008  972  955  930 1004  994  981  946  958

Seuraavassa vielä kellojonon uniikkiuden tarkasteluun pieni ohjelma. Ohjelma pakkaa kellojonon siten että yhdestä ketjusta tulostetaan aina peräkkäin arvo ja pituus. Seuraavassa esimerkkitietue: raportin luvut ovat kaksimerkkisiä heksalukuja. Ensimmäinen merkki on nolla ja niitä on $5 kappaletta, seuraava merkki on $1 ja niitä on $1 kappale, jne…

$ ./newressutest16 --filesize1600 -
string:'1600', base:10(10B), multiplier:1(1B), prevll:1600(~1K), ll:1600(~1K), totll:1600(~1K)
clockstream, lines:1(1B), clocksize:1600(~1K), linesize:1601(~1K), filesize:1601(~1K)
filename:-
00050101020103080411051106130714081309110a140b140c130d140e130f141014110f121113131414151416141714181419121a131b141c141d141e141f1420142112221423142414251426142714281429122a142b132c142d142e142f1430143113321333133414351436143714381439123a133b143c143d133e143f1440144111421443144413451446144714481349124a134b144c144d144e144f1350145113521453145413551456145714581359135a135b145c135d145e145f1360146111621363146413651466136714681469126a136b136c146d146e136f1470147112721473137414751376147714781379127a137b137c147d137e147f1380138113821283138413851486138714881389128a138b138c148d108e148f1390149112921393149413951496139714981399129a139b139c149d139e139f14a013a111a212a313a413a513a614a713a813a912aa12ab14ac13ad13ae13af14b013b112b214b313b413b513b613b714b813b911ba13bb13bc13bd14be13bf13c013c112c213c312c413c513c613c713c813c912ca12cb13cc13cd13ce13cf13d013d111d213d313d413d513d614d713d813d911da12db13dc13dd13de13df13e013e111e213e313e413e513e613e713e813e911ea12eb13ec12ed13ee14ef12f013f113f212f313f413f512f614f712f813f911fa13fb13fc13fd13fe13ff130013010c020f03110411051206130712081209100a120b120c120d120e130f1210121111121313131412151316131712181319101a111b121c121d131e131f1320132110221223122412251326132713281329102a112b112c122d132e132f1230133111321233133413351336123713381339103a133b113c133d123e133f1240134111421143124412451346134713481249104a124b134c124d134e134f1350125111521353135412551356125713581259115a125b135c125d135e135f126013610f620e63126411651266126713681269116a116b126c126d136e126f1370127112721273137412751376127713781279107a117b137c127d127e137f1280138110821183118412851286128713881289118a118b128c138d128e128f13
./newressutest16: hashed file, sha256:661922921ecb992b816a89aa619fbb639b215104b51a4b3f3531ca7cb45362a3
jarik@jarik-HP14dv2002:~/f/ressurngd$ 

Seuraavassa ohjelmaa on ajettu siten, että sen tuloste lajitellaan ja vertaillaan 1500 ensimmäistä merkkiä lajitellun tiedoston peräkkäisistä riveistä. Jos ne ovat samat, se on ongelma ja tietueet tulostetaan. Jos ne poikkeavat, ne ovat molemmat uniikkeja, eikä mitään tulosteta: Eli kaikki on kunnossa, jos ohjelma ei tulosta mitään.

$ ./newressutest16 --filesize100g --stdout | sort | uniq -D --check-chars=1500
string:'100g', base:10(10B), multiplier:1073741824(1G), prevll:100(100B), ll:107374182400(100G), totll:107374182400(100G)
clockstream, lines:67066948(~63M), clocksize:1600(~1K), linesize:1601(~1K), filesize:107374183748(~100G)
filename:-
Done:100.0%  
./newressutest16: hashed file, sha256:485573cfe61b732c01722be1cddfd12adf769055e0395f89baad0ad6aa62857a
$

Ja varsinainen ohjelma:

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

#include "sha256.h"

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

#define FILESIZE 16010
#define CLOCKSIZE 1600 // bytes per line
#define LINESIZE 1601 // CLOCKSIZE + 1
#define LINES 10
#define BLOCKSIZE 1024

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

#include <sys/time.h>

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

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

//#define KILO 1000
#define KILO 1024

int stat = 0;

void fprintfcharacter(FILE *fp1, unsigned char *p)
{
  fputc(*p, fp1); // print first char
  if(*p > 0xbf) { // first char utf8
    p++;
    for(;;) { // print rest of the utf8 chars
      if(*p > 0xbf || // new utf8 character
	 *p < 0x80 || // ascii character
	 *p == '\0') // end of string
	break;
      fputc(*p, fp1);
      p++;
    }
  }
}

int getdigit(unsigned char *p) // JariK 2023
{
  int digit;
  
  if(*p >= '0' && *p <= '9')
    digit = *p - '0';
  else if(*p >= 'a' && *p <= 'z')
    digit = (*p - 'a') + 10;
  else if(*p >= 'A' && *p <= 'Z')
    digit = (*p - 'A') + 10;
  else
    digit = -1; // not found, illegal

  return(digit);
}

void readablelonglong(FILE *fp1, unsigned long long ll2)
{
  int c;
  unsigned long long multiplier, ll = ll2;

  // 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";

  c = 0;
  multiplier = 1;
  while(ll >= KILO) {
    ll /= KILO;
    multiplier *= KILO;
    c++;
  }

  if(ll * multiplier != ll2)
    fprintf(fp1,"~"); // approximately

  fprintf(fp1,"%llu%c", ll, units[c]);
}

#define DEBUG45 2

unsigned long long getlonglong(unsigned char *p2) // JariK 2023
{
  int digit, base = 10;
  unsigned char *p = p2;
  unsigned long long totll, ll, prevll, multiplier;
  
  totll = 0;

  while(*p != '\0') { // works also: 1g100m & 1m20k and 1t1t etc...

    unsigned char *prevp = p;
    
    if(!strncmp("0x", p, 2)) {
      base = 16;
      p += 2;
      
    } else if(!strncmp("0d", p, 2)) {
      base = 10;
      p += 2;
      
    } else if(!strncmp("0o", p, 2)) {
      base = 8;
      p += 2;
      
    } else if(!strncmp("0b", p, 2)) {
      base = 2;
      p += 2;
      
    }
    
    ll = 0;
    while((digit = getdigit(p)) != -1 && digit < base) {
      ll = ll * base + digit;
      p++;

    }
    
    multiplier = 1;
    
    if(*p == 'k' || *p == 'K') {
      multiplier = KILO;
      p++;
      
    } else if(*p == 'm' || *p == 'M') {
      multiplier = (KILO * KILO);
      p++;
      
    } else if(*p == 'g' || *p == 'G') {
      multiplier = (KILO * KILO * KILO);
      p++;
      
    } else if(*p == 't' || *p == 'T') {
      multiplier = ((unsigned long long)KILO * KILO * KILO * KILO);
      p++;
      
    } else if(*p == 'p' || *p == 'P') {
      multiplier = ((unsigned long long)KILO * KILO * KILO * KILO * KILO);
      p++;
      
    } else if(*p == 'e' || *p == 'E') {
      multiplier = ((unsigned long long)KILO * KILO * KILO * KILO * KILO * KILO);
      p++;
      
    }
    
    prevll = ll;
    ll *= multiplier;
    if(ll / multiplier != prevll) {
      fflush(stdout);
      fprintf(stderr,"%s: multiply overflow", procname);
      fprintf(stderr,", string:'%s'", p2);
      fprintf(stderr,", digit:'");
      fprintfcharacter(stderr, p);
      fprintf(stderr,"'");
      fprintf(stderr,", value:%d", digit);
      fprintf(stderr,", base:%u(", base);
      readablelonglong(stderr, base);
      fprintf(stderr,")");
      fprintf(stderr,", ll:%llu(", prevll);
      readablelonglong(stderr, prevll);
      fprintf(stderr,")");
      fprintf(stderr,", multiplier:%llu(", multiplier);
      readablelonglong(stderr, multiplier);
      fprintf(stderr,")");
      fprintf(stderr,"\n");
      fflush(stderr);
      exit(1);

    }
  
    if(*p == 'b' || *p == 'B') // remove last b (for bytes in 1tb)
      p++;
    
    totll += ll;

#ifdef DEBUG45
    fprintf(stderr,"string:'%s'", p2);
    fprintf(stderr,", base:%u(", base);
    readablelonglong(stderr, base);
    fprintf(stderr,")");
    fprintf(stderr,", multiplier:%llu(", multiplier);
    readablelonglong(stderr, multiplier);
    fprintf(stderr,")");
    fprintf(stderr,", prevll:%llu(", prevll);
    readablelonglong(stderr, prevll);
    fprintf(stderr,")");
    fprintf(stderr,", ll:%llu(", ll);
    readablelonglong(stderr, ll);
    fprintf(stderr,")");
    fprintf(stderr,", totll:%llu(", totll);
    readablelonglong(stderr, totll);
    fprintf(stderr,")");
    fprintf(stderr,"\n");
#endif
    
    if(prevp == p) // no progress
      break;
  }
  
  if(*p != '\0') {
    fflush(stdout);
    fprintf(stderr,"%s: illegal digit", procname);
    fprintf(stderr,", string:'%s'", p2);
    fprintf(stderr,", digit:'");
    fprintfcharacter(stderr, p);
    fprintf(stderr,"'");
    fprintf(stderr,", value:%d", digit);
    fprintf(stderr,", base:%u(", base);
    readablelonglong(stderr, base);
    fprintf(stderr,")");
    fprintf(stderr,"\n");
    fflush(stderr);
    exit(1);
  }

  return(totll);
}

#define FILE_HASH 2 // on by default

#ifdef FILE_HASH

void hashfinal2string(unsigned char *hashstring, unsigned char *final)
{
  for(int c = 0; c < HashLen; c++) {
    sprintf(hashstring + 2 * c, "%02x", final[c]);
  }
}

#endif

#define DEBUG79 2

int main(int argc, char *argv[])
{
  int c, d, percentageline = 1, prevpros, pros, quiet = 0, help = 0;
  int filesize_set = 0, clocksize_set = 0, linesize_set = 0, lines_set = 0;
  unsigned long long filesize = FILESIZE, lines = LINES;
  unsigned int clocksize = CLOCKSIZE, linesize = LINESIZE;
  int stdoutflag = 0;
  unsigned long long plines = 0;
  unsigned char *buffer, filename[128] = "";
  FILE *fp1;
  
  procname = argv[0];

  // look thru command line parameters

  for(c = 1; c < argc; c++) {

    if(c == argc - 1 && !strcmp(argv[c], "-")) { // last option hyphen
      stdoutflag = 1; // output to stdout
      continue;
    }
    
    if(strncmp("-", argv[c], 1)) { // filename not expected
      fprintf(stderr,"%s: filename not expected", procname);
      fprintf(stderr,", parameter:%s", argv[c]);
      fprintf(stderr,"\n");
      exit(1);
    }
    
    if(!strncmp("-", argv[c], 1)) { // option starting with hyphen
      
      if(!strncmp("-o", argv[c], 2)) { // output filename
	if(*(argv[c] + 2) != '\0') {
	  strncpy(filename, argv[c] + 2, sizeof(filename));
	} else if(c + 1 < argc) {
	  strncpy(filename, argv[c + 1], sizeof(filename));
	  c++;
	}
	stdoutflag = 0;
	
      } else if(!strcmp("--stdout", argv[c])) { // output stdout
	stdoutflag = 1; // output to stdout
	filename[0] = '\0';
	
      } else if(!strcmp("--help", argv[c]) ||
	 !strcmp("-?", argv[c]) ) {
	help = 1;

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

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

      } else if(!strncmp("--filesize", argv[c], 10)) {
	if(*(argv[c] + 10) != '\0') {
	  filesize = getlonglong(argv[c] + 10);
	} else if(c + 1 < argc) {
	  filesize = getlonglong(argv[c + 1]);
	  c++;
	}
	filesize_set = 1;

      } else if(!strncmp("--linesize", argv[c], 10)) {
	if(*(argv[c] + 10) != '\0') {
	  linesize = getlonglong(argv[c] + 10);
	} else if(c + 1 < argc) {
	  linesize = getlonglong(argv[c + 1]);
	  c++;
	}
	linesize_set = 1;
	
      } else if(!strncmp("--lines", argv[c], 7)) {
	if(*(argv[c] + 7) != '\0') {
	  lines = getlonglong(argv[c] + 7);
	} else if(c + 1 < argc) {
	  lines = getlonglong(argv[c + 1]);
	  c++;
	}
	lines_set = 1;

      } else if(!strncmp("--clocksize", argv[c], 11)) {
	if(*(argv[c] + 11) != '\0') {
	  clocksize = getlonglong(argv[c] + 11);
	} else if(c + 1 < argc) {
	  clocksize = getlonglong(argv[c + 1]);
	  c++;
	}
	clocksize_set = 1;

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

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

  if(!linesize_set)
    linesize = clocksize + 1;
  else if(!clocksize_set)
    clocksize = linesize - 1;

  if(!filesize_set)
    filesize = lines * linesize;
  else if(!lines_set) {
    lines = (filesize + linesize - 1) / linesize; // round up
    filesize = lines * linesize;
  } else if(!linesize_set) {
    linesize = (filesize + lines - 1) / lines; // round up
    clocksize = (linesize - 1);
    filesize = lines * linesize;
  }

  if(linesize != clocksize + 1 ||
     linesize < 5 || clocksize < 4) {
    fflush(stdout);
    fprintf(stderr,"%s: mismatched parameters", procname);
    fprintf(stderr,", linesize:%u(", linesize);
    readablelonglong(stderr, linesize);
    fprintf(stderr,")");
    fprintf(stderr,", clocksize:%u(", clocksize);
    readablelonglong(stderr, clocksize);
    fprintf(stderr,")");
    fprintf(stderr,"\n");
    fflush(stderr);
    exit(1);
  }

  if(filesize != lines * linesize ||
     lines < 1 || linesize < 5) {
    fflush(stdout);
    fprintf(stderr,"%s: mismatched parameters", procname);
    fprintf(stderr,", lines:%llu(", lines);
    readablelonglong(stderr, lines);
    fprintf(stderr,")");
    fprintf(stderr,", linesize:%u(", linesize);
    readablelonglong(stderr, linesize);
    fprintf(stderr,")");
    fprintf(stderr,", filesize:%llu(", filesize);
    readablelonglong(stderr, filesize);
    fprintf(stderr,")");
    fprintf(stderr,"\n");
    fflush(stderr);
    exit(1);
  }

  if(filesize / lines != linesize) { // test overflow too
    fflush(stdout);
    fprintf(stderr,"%s: parameter overflow", procname);
    fprintf(stderr,", lines:%llu(", lines);
    readablelonglong(stderr, lines);
    fprintf(stderr,")");
    fprintf(stderr,", linesize:%u(", linesize);
    readablelonglong(stderr, linesize);
    fprintf(stderr,")");
    fprintf(stderr,", filesize:%llu(", filesize);
    readablelonglong(stderr, filesize);
    fprintf(stderr,")");
    fprintf(stderr,"\n");
    fflush(stderr);
    exit(1);
  }

  // print help message if needed
  
  if(help) {
    fprintf(stderr,"%s", procname);
    fprintf(stderr,"\n\t[-o filename]");
    //fprintf(stderr," [--stdout]");
    fprintf(stderr,"\n\t[--filesize base-number-multiplier]"); 
    fprintf(stderr,"\n\t[--lines base-number-multiplier]");
    fprintf(stderr," [--linesize base-number-multiplier]");
    fprintf(stderr,"\n\t[--clocksize base-number-multiplier]");
    fprintf(stderr,"\n\t[--help]");
    fprintf(stderr," [--copyright]");
    fprintf(stderr," [--version]");
    fprintf(stderr," [--stat]");
    fprintf(stderr," [--verbose]");
    fprintf(stderr," [--quiet]");
    fprintf(stderr,"\n");
    fprintf(stderr,"Examples:\n");
    fprintf(stderr,"\t$ %s --single --filesize1g\n", procname);
    fprintf(stderr,"\t$ %s --single --filesize1g --lines1m\n", procname);
    fprintf(stderr,"\t$ %s --single --filesize1g --linesize25\n", procname);
    fprintf(stderr,"\t$ %s --single --filesize1g --clocksize12\n", procname);
    exit(1);
  } // end of if(help)

#ifdef DEBUG79

  // print filesize parameters
  
  fprintf(stderr,"clockstream");
  fprintf(stderr,", lines:%llu(", lines);
  readablelonglong(stderr, lines);
  fprintf(stderr,")");
  fprintf(stderr,", clocksize:%u(", clocksize);
  readablelonglong(stderr, clocksize);
  fprintf(stderr,")");
  fprintf(stderr,", linesize:%u(", linesize);
  readablelonglong(stderr, linesize);
  fprintf(stderr,")");
  fprintf(stderr,", filesize:%llu(", filesize);
  readablelonglong(stderr, filesize);
  fprintf(stderr,")");
  fprintf(stderr,"\n");
  fflush(stderr);
  
#endif
  
  if(!stdoutflag && filename[0] == '\0') {

    // find first available filename,
    // or empty "slot"

    for(c = 1; c <= 99999; c++) {
      sprintf(filename, "newressutest16.%d.rnd", c);
      if((fp1 = fopen(filename, "r")) != NULL) {
	fclose(fp1);
	continue;
      }

#ifdef FILE_HASH
      unsigned char filename2[138];
      sprintf(filename2,"%s.sha256", filename);
      if((fp1 = fopen(filename2, "r")) != NULL) {
	fclose(fp1);
	continue;
      }
#endif

      break;
    }
  }

  if(stdoutflag) {
    if(isatty(STDOUT_FILENO)) { // 0=stdin, 1=stdout, 2=stderr
      percentageline = 0;
    }
  }
    
  if(stdoutflag) 
    fprintf(stderr,"filename:-\n");
  else
    fprintf(stderr,"filename:%s\n", filename);
    
  if(stdoutflag == 1) {
    fp1 = stdout;
  } else if((fp1 = fopen(filename, "w")) == NULL) {
    fprintf(stderr,"%s: cannot open file", procname);
    fprintf(stderr,", filename:%s", filename);
    fprintf(stderr,"\n");
    exit(1);
  }
  
#ifdef FILE_HASH
  
  unsigned char digest[HashLen];
  HashCtx hash;

  HashInit(&hash); // initialize hash

#endif
  
  if((buffer = malloc(linesize + 1)) == NULL) { // space for '\0' too
    fprintf(stderr,"%s: cannot allocate memory (buffer)\n", procname);
    exit(1);
  }

  memset(buffer, 0, linesize + 1);

  prevpros = -1;
  
  int count, prevusec = -1, usec;
  unsigned long long diff0;

  for(;;) {

#define DEBUG87

#ifdef DEBUG87
    prevusec = useconds();
    while((usec = useconds()) == prevusec);
    diff0 = usec;
#else
    diff0 = 0;
#endif
    *buffer = '\0';
    
    count = 0;
    prevusec = -1;
    
    for(;;) {
      usec = (useconds() - diff0) & 0xff;
      if(prevusec == -1)
	prevusec = usec;
      else if(usec != prevusec) {
	unsigned char buffer10[10];
	sprintf(buffer10,"%02x%02x", prevusec, count);
	if(strlen(buffer) + strlen(buffer10) < linesize)
	  strcat(buffer, buffer10);
	else
	  break;
	prevusec = usec;
	count = 0;
      }
      count++;
    }
    strcat(buffer, "\n");
    
#ifdef FILE_HASH
    HashUpdate(&hash, buffer, strlen(buffer)); // calculate hash
#endif
      
    if(fwrite(buffer, 1, strlen(buffer), fp1) < strlen(buffer)) {
      fprintf(stderr,"%s:", procname);
      fprintf(stderr," cannot write file");
      fprintf(stderr,"\n");
      exit(1);      
    }
    
    plines++;
    if(plines >= lines)
      break;

  }

#ifdef FILE_HASH
  HashFinal(digest, &hash); // calculate hash
#endif
  
  if(!stdoutflag)
    fclose(fp1);
  
#ifdef FILE_HASH

  unsigned char hashstring[2 * HashLen + 1];
  
  hashfinal2string(hashstring, digest);
  
  fprintf(stderr,"%s: hashed file", procname);

  if(filename[0] != '\0') {
    fprintf(stderr,", filename:%s", filename);

    unsigned char filename2[138];

    sprintf(filename2,"%s.sha256", filename);

    fprintf(stderr,", hashfilename:%s", filename2);

    if((fp1 = fopen(filename2, "w")) != NULL) {
      fprintf(fp1,"%s\n", hashstring);
      fclose(fp1);
    }
  } // end of if(filename[0] != '\0'
  
  fprintf(stderr,", sha256:%s\n", hashstring);

  if(!stdoutflag) {
    fprintf(stderr,"%s: checking sha256", procname);
    fprintf(stderr,", filename:%s\n", filename);
    unsigned char command[1024];
    sprintf(command,"sha256sum %s", filename);
    system(command);
  }
  
#endif
  
  free(buffer);
  
  return(0);
}

Copy-reverse on uudelleenkirjoitettu siten, että kellojonoon lisätään aikaista enemmän satunnaisuutta: Uudessa versiossa on neljä (DEPTH) tasoa, joissa kussakin on oma lohkoihin jako. Näin ennen copy reverse käsitteli kellojonon vain yhtenä copy reverse puskurina ja nyt niitä on neljä.

#define DEPTH 4

unsigned int copyreverse = 1;
unsigned long cblocks = 0;
unsigned char lens = 4;

static unsigned char ressu_copyreverse(int depth)
{
  static int reverse[DEPTH] = {0}; // 0 = copy, 1 = reverse, 28.10.2022 JariK
  static unsigned int len[DEPTH] = {0};
  static unsigned int count[DEPTH] = {0};
  static unsigned int low[DEPTH] = {0};
  static unsigned int high[DEPTH] = {0};
  static unsigned char bytes[DEPTH][257] = {0};
  
  if(depth == 0) {
#ifdef DEBUG2D
    int c = ressu_clock();
    fprintf(stdout,", ch2:%02x",c);
    return(c);
#else
    return(ressu_clock());
#endif
  }

  depth--;

  // dividing clock stream into blocks
  // and reversing every other block

  // copy next block to bytes
  
  if(count[depth] == 0) { // previous block ended
    reverse[depth] = !reverse[depth];
    len[depth] = ressuct[ressu_nonrandom() % ressuct_bytes] + 2; // block size
    //len[depth] = (ressuct[ressu_nonrandom() % ressuct_bytes] & 3 ) + 2; // block size
    //len[depth] = --lens + 2; // block size
    //len[depth] = lens++ + 2; // block size
    count[depth] = len[depth];
    low[depth] = 0;
    high[depth] = len[depth];
    rndbits2 += 2;
#ifdef DEBUG2A
    fprintf(stdout,"\nblk:");
    fprintf(stdout," depth:%d", depth);
    fprintf(stdout,", reverse:%d", reverse[depth]);
    fprintf(stdout,", len:%d", len[depth]);
    fprintf(stdout,", count:%d", count[depth]);
    fprintf(stdout,", low:%d", low[depth]);
    fprintf(stdout,", high:%d", high[depth]);
    fflush(stdout);
#endif
    for(int c = 0; c < len[depth]; c++) {
      bytes[depth][c] = ressu_copyreverse(depth); // block bytes
#ifdef DEBUG2A
      fprintf(stdout,", b%d:%02x", depth, bytes[depth][c]);
      newressu_output = 1;
#endif
    }
  }
  if(reverse[depth]) { // reverse
    
    // get reversed byte to return
    ch = bytes[depth][--high[depth]]; // reverse
#ifdef DEBUG2E
    fprintf(stdout,", rev%d:%02x", depth, ch);
    fflush(stdout);
#endif
    --count[depth];

  } else { // copy
    
    // get copied byte to return
    ch = bytes[depth][low[depth]++];
#ifdef DEBUG2E
    fprintf(stdout,", copy%d:%02x", depth, ch);
    fflush(stdout);
#endif
    --count[depth];
  }

  return(ch);
}

Seuraava rutiini kutsuu edellistä:

static unsigned char ressu_clockbyte() /* JariK 2013 */
{
  if(copyreverse) {
    ch = ressu_copyreverse(DEPTH);
  } else {
    ch = ressu_clock();
  } // end of if(copyreverse)

#ifdef DEBUG2F
  static unsigned int cols = 0;
  if(cols % 32 == 0) {
    if(cols > 0)
      fprintf(stdout,"\n");
    fprintf(stdout,"%05u ",cols);
  }
  if(cols % 4 == 0)
    fprintf(stdout," ");
  fprintf(stdout,"%02x",ch);
  cols++;
#endif
  
  // statistics for theoretical
  // random bits calculation (rndbits?)
  
  static int prevbyte = -1, clockcount = 0;
  
  if(prevbyte == -1)
    prevbyte = ch;
  if(prevbyte != ch) {
    periods[clockcount]++;
    clockbytes += clockcount;
#ifdef DEBUG2B
    fprintf(stdout," %d", clockcount);
    newressu_output = 1;
#endif
    clockcount = 0;
    prevbyte = ch;
  }
  clockcount++;
  
  return(ch);
}

Vielä loppukevennykseksi uusi ressun satunnaisuutta käyttävä ohjelma, joka etsii sudokuja (newressusudoku).

Seuraavassa haetaan yksi sudoku:

$ ./newressusudoku 
    A   B   C   D   E   F   G   H   I  
  +-----------+-----------+-----------+
A |           |     3     |           |
B | 4         |     6     |     5     |
C |         9 |         5 |           |
  +-----------+-----------+-----------+
D | 9   8     |           | 1   2     |
E | 7       5 | 1   2   8 |           |
F | 3         |     4     |           |
  +-----------+-----------+-----------+
G | 5         |         2 | 7   9     |
H | 6         | 8         | 5       4 |
I |           |         6 |         8 |
  +-----------+-----------+-----------+
  s:0 steps:0 rounds:4 digits:27
$ 

Sudokuja on niin paljon erilaisia, että kaikkien tulostettujen sudokujen pitäisi olla ainutlaatuisia. Ohjelman ei pitäisi tulostaa samaa sudokua kahta kertaa. Tätä on omalta osaltaan takaamassa pseudoressu, jolta newressusudoku hakee satunnaisbittinsä (sudoku_make()).

Sudokun ulkomuotoon voi vaikuttaa –board -kytkimellä. –board:iin voi lisätä numeron, jolla valitaan miltä sudoku näyttää. Ensimmäisenä –board1, joka on oletusarvo: Tämä on sopiva muoto sudokun tulostukseen paperille ja ratkaistavaksi kynällä. Voit muotoilla sudokut tekstinkäsittelyllä esimerkiksi neljään sarakkeeseen, joissa on kolme sudokua, tai muuhun muotoon. Eniten merkintöjä paperille mahtuu kun tulostat nämä 1 sudoku / a4 lomake. Seuraavassa erilaiset –board kytkimet ja niiden tulosteet: –board0 kytkimellä voit jättää tehtäväruudukon tulostamatta. Ilman –board:in jälkeistä numeroa kytkin vaihdetaan vastakkaiseksi (0=false, 1/muu=true).

Ratkaisun tulostusmuotoon voi vaikuttaa –solution -kytkimellä, joka toimii samalla tavoin kun –board kytkin.

$ ./newressusudoku --board1
    A   B   C   D   E   F   G   H   I  
  +-----------+-----------+-----------+
A |     1   5 | 8         |         2 |
B |         7 | 1         |     3     |
C | 9       4 |           |           |
  +-----------+-----------+-----------+
D |         3 |         2 |           |
E |           | 9   6     | 1   4     |
F | 8         |     1     |         9 |
  +-----------+-----------+-----------+
G |     6     |         5 | 8   2     |
H |     7     |           |         5 |
I |         1 | 6         | 9         |
  +-----------+-----------+-----------+
  s:0 steps:0 rounds:4 digits:27
$ 

–board2 tulostaa seuraavanlaisen listan nämä pienemmät peliruudukot ovat hyviä ratkaisun tulostukseen:

$ ./newressusudoku --board2
  ABC DEF GHI 
 +---+---+---+
A|2  |3  |9 8|
B|  5|   |  2|
C|   |7  |   |
 +---+---+---+
D|   |9 4| 5 |
E|6  |   | 8 |
F|5  | 31| 64|
 +---+---+---+
G|36 | 5 | 2 |
H|45 |  7|8  |
I|12 |   |   |
 +---+---+---+
  s:0 steps:0 rounds:5 digits:27
$ 

Jatkossa loput –board tulosteet

$ ./newressusudoku --board3
  ABCDEFGHI 
 +---------+
A|   3 9  2|
B|769  23  |
C|    71  9|
D|4  1   8 |
E|  8 4    |
F|1 7      |
G| 25  8  1|
H| 136     |
I|   5  6  |
 +---------+
  s:0 steps:0 rounds:4 digits:27
$ 

$ ./newressusudoku --board4
  ABCDEFGHI
 +---------
A| 7 3     
B|   6  47 
C|4    12  
D|3   4   7
E|8  59 1 6
F|5      8 
G|9   8   2
H|12   7 4 
I|       51
  s:0 steps:0 rounds:3 digits:27

$ ./newressusudoku --board5
 ABCDEFGHI 
A  1 43   
B3 6 5918 
C95   7   
D6  8  7  
E  8    3 
F4     8 2
G23     9 
H         
I7   2 6 1
  s:0 steps:0 rounds:6 digits:27

$ ./newressusudoku --board6
1        
    38   
4   5 3  
72  1    
9 6  58  
   76 4 2
       39
 7 3   81
  3 2 64 
  s:0 steps:0 rounds:5 digits:27

$

$ ./newressusudoku --board10
AA8 AB1456 AC4569 AD7 AE369 AF36 AG2 AH56 AI3456
BA24569 BB4567 BC245679 BD249 BE2369 BF8 BG1 BH56 BI3456
CA246 CB3 CC246 CD1 CE26 CF5 CG468 CH7 CI9
DA26 DB9 DC2678 DD28 DE5 DF4 DG678 DH3 DI12678
EA23456 EB4567 EC245678 ED289 EE1 EF2367 EG46789 EH2689 EI24678
FA2346 FB467 FC1 FD289 FE236789 FF2367 FG5 FH2689 FI24678
GA7 GB1456 GC4569 GD258 GE28 GF12 GG3 GH125689 GI12568
HA159 HB2 HC3 HD6 HE78 HF17 HG789 HH4 HI1578
IA156 IB8 IC56 ID3 IE4 IF9 IG67 IH1256 II12567
  s:0 steps:0 rounds:5 digits:27

$ ./newressusudoku --board11
392  8               29  5  1 85  27 5  1    9   62 814 1 7 3  76           8 7  
  s:0 steps:0 rounds:4 digits:27

$ ./newressusudoku --board20
'AC' = "6", 'AD' = "8", 'AE' = "2", 'AH' = "5", 'BB' = "5", 'BG' = "8", 'BH' = "9", 'CD' = "1", 'CG' = "3", 'CH' = "7", 'DC' = "5", 'DE' = "6", 'DI' = "9", 'EB' = "8", 'EC' = "3", 'EF' = "1", 'FA' = "2", 'FC' = "4", 'FI' = "3", 'GB' = "6", 'GD' = "3", 'GI' = "7", 'HA' = "1", 'HC' = "2", 'HG' = "5", 'IA' = "9", 'IH' = "2"
  s:0 steps:0 rounds:3 digits:27

$ ./newressusudoku --board21
'AA' = "4589", 'AB' = "6", 'AC' = "28", 'AD' = "2489", 'AE' = "48", 'AF' = "1", 'AG' = "47", 'AH' = "2347", 'AI' = "2457", 'BA' = "7", 'BB' = "128", 'BC' = "3", 'BD' = "248", 'BE' = "5", 'BF' = "2468", 'BG' = "146", 'BH' = "9", 'BI' = "1246", 'CA' = "1459", 'CB' = "1259", 'CC' = "12", 'CD' = "3", 'CE' = "46", 'CF' = "7", 'CG' = "146", 'CH' = "8", 'CI' = "12456", 'DA' = "189", 'DB' = "1789", 'DC' = "4", 'DD' = "1578", 'DE' = "2", 'DF' = "58", 'DG' = "1789", 'DH' = "6", 'DI' = "3", 'EA' = "2", 'EB' = "1378", 'EC' = "18", 'ED' = "6", 'EE' = "9", 'EF' = "348", 'EG' = "5", 'EH' = "147", 'EI' = "1478", 'FA' = "6", 'FB' = "13789", 'FC' = "5", 'FD' = "1478", 'FE' = "1348", 'FF' = "348", 'FG' = "2", 'FH' = "147", 'FI' = "14789", 'GA' = "1358", 'GB' = "12358", 'GC' = "1268", 'GD' = "12458", 'GE' = "7", 'GF' = "9", 'GG' = "1468", 'GH' = "124", 'GI' = "12468", 'HA' = "1358", 'HB' = "12358", 'HC' = "9", 'HD' = "12458", 'HE' = "13468", 'HF' = "234568", 'HG' = "14678", 'HH' = "1247", 'HI' = "124678", 'IA' = "18", 'IB' = "4", 'IC' = "7", 'ID' = "128", 'IE' = "168", 'IF' = "268", 'IG' = "3", 'IH' = "5", 'II' = "12689"
  s:0 steps:0 rounds:5 digits:27
$

–board20 ja –board21 ovat terttu muodossa, että sitäkin tulee ajateltua.

Jos haluat ratkaisun tulosteen käytä –solution -kytkintä:

$ ./newressusudoku --solution
    A   B   C   D   E   F   G   H   I  
  +-----------+-----------+-----------+
A |     3   9 |     5   2 |           |
B |           |     8     |           |
C | 2       4 |           |     6     |
  +-----------+-----------+-----------+
D |         5 | 1         |           |
E | 1         |           |     3     |
F |           | 5   4   7 | 8       2 |
  +-----------+-----------+-----------+
G |           |     7   9 |         3 |
H | 7         | 4       3 | 5         |
I |     8     | 2         |         7 |
  +-----------+-----------+-----------+
  s:0 steps:0 rounds:5 digits:27
    A   B   C   D   E   F   G   H   I  
  +-----------+-----------+-----------+
A | 8   3   9 | 6   5   2 | 1   7   4 |
B | 6   1   7 | 3   8   4 | 2   5   9 |
C | 2   5   4 | 7   9   1 | 3   6   8 |
  +-----------+-----------+-----------+
D | 4   2   5 | 1   3   8 | 7   9   6 |
E | 1   7   8 | 9   2   6 | 4   3   5 |
F | 9   6   3 | 5   4   7 | 8   1   2 |
  +-----------+-----------+-----------+
G | 5   4   1 | 8   7   9 | 6   2   3 |
H | 7   9   2 | 4   6   3 | 5   8   1 |
I | 3   8   6 | 2   1   5 | 9   4   7 |
  +-----------+-----------+-----------+
  s:0
$ 

Seuraavassa neljä aloittelijalle sopivaa sudokua:

$ ./newressusudoku --easy45 --sudokues4
    A   B   C   D   E   F   G   H   I  
  +-----------+-----------+-----------+
A | 4         |     8   3 |     2   7 |
B | 9       2 |         4 |         1 |
C | 8   3   5 | 7   2   1 | 4   6   9 |
  +-----------+-----------+-----------+
D |     4   7 | 5   9     |     1     |
E |         8 |         6 |     5     |
F | 1       6 |         7 |           |
  +-----------+-----------+-----------+
G |     8     |           | 1   9   5 |
H | 6         | 8       5 | 3   7     |
I | 5       3 |     7   9 | 6   8   2 |
  +-----------+-----------+-----------+
  s:0 steps:0 rounds:2 digits:45
    A   B   C   D   E   F   G   H   I  
  +-----------+-----------+-----------+
A |     5   7 | 3       9 |         4 |
B | 6         |         1 | 9   2   5 |
C |           |     5     |     3     |
  +-----------+-----------+-----------+
D |         8 | 6         | 5   1   2 |
E |     7     | 9         |     8     |
F | 4   2   1 |           | 7       6 |
  +-----------+-----------+-----------+
G |           | 1   4   5 |     7   9 |
H |     6     | 8   9   3 | 2   5   1 |
I | 9       5 | 2   6   7 | 3   4   8 |
  +-----------+-----------+-----------+
  s:1 steps:0 rounds:3 digits:45
    A   B   C   D   E   F   G   H   I  
  +-----------+-----------+-----------+
A | 2         |         7 |         4 |
B | 3   1   7 | 2   5   4 |     8   9 |
C | 5       6 |     9     | 3   7     |
  +-----------+-----------+-----------+
D | 7       3 | 1       6 | 9         |
E | 1       9 |     8   3 | 7   4   6 |
F |           |         2 | 8       1 |
  +-----------+-----------+-----------+
G | 9         |           |     6   3 |
H |         2 | 3       5 |         7 |
I | 4   3   5 |     6   9 |     1   8 |
  +-----------+-----------+-----------+
  s:2 steps:0 rounds:2 digits:45
    A   B   C   D   E   F   G   H   I  
  +-----------+-----------+-----------+
A |           | 8   7     |     6   2 |
B | 5   7     |     1   6 | 8         |
C | 2   8   6 | 4   9     | 3         |
  +-----------+-----------+-----------+
D |           |     3     |         6 |
E | 9       7 |           | 1       8 |
F |     2   1 |     5     | 9   3   4 |
  +-----------+-----------+-----------+
G | 3   6   5 |     8   4 | 7   2   1 |
H | 7   9   4 | 5       1 | 6         |
I | 8       2 |     6     |     9     |
  +-----------+-----------+-----------+
  s:3 steps:0 rounds:2 digits:45
$ 

Hiukan vaikeampia 33:n vihjeen sudokuja:

$ ./newressusudoku --digits33 --sudokues4
    A   B   C   D   E   F   G   H   I  
  +-----------+-----------+-----------+
A |         3 | 1       9 | 4         |
B | 2       6 |         5 |           |
C |           | 7         | 8   6     |
  +-----------+-----------+-----------+
D |     1     | 5         |     2     |
E | 3         |           |           |
F |         8 | 4         | 6   5     |
  +-----------+-----------+-----------+
G | 8         |     5   7 |         9 |
H | 7   9   2 | 3       4 | 5   8     |
I | 4       5 |         8 |     1     |
  +-----------+-----------+-----------+
  s:0 steps:0 rounds:3 digits:33
    A   B   C   D   E   F   G   H   I  
  +-----------+-----------+-----------+
A | 6         | 3         | 9   1   2 |
B |           |     1   9 |     6     |
C | 1         |         6 | 8       7 |
  +-----------+-----------+-----------+
D |         4 | 6   9   5 |           |
E | 9   8   2 | 1       3 |           |
F |           |     8     |           |
  +-----------+-----------+-----------+
G |     7     |         2 |         1 |
H |         3 |     6   1 |     8   5 |
I |         6 | 7   5     |           |
  +-----------+-----------+-----------+
  s:1 steps:0 rounds:4 digits:33
    A   B   C   D   E   F   G   H   I  
  +-----------+-----------+-----------+
A | 9   5   2 | 7   8     |           |
B |           |     2     |           |
C |           | 4       3 |     5     |
  +-----------+-----------+-----------+
D |     6   7 | 9         | 8         |
E | 1       9 | 3         | 4       6 |
F |     3   4 | 6         |           |
  +-----------+-----------+-----------+
G |         5 | 8         |         3 |
H |     9     |     3   7 | 5       1 |
I | 8       3 |           |     9   2 |
  +-----------+-----------+-----------+
  s:2 steps:0 rounds:3 digits:33
    A   B   C   D   E   F   G   H   I  
  +-----------+-----------+-----------+
A | 5         |           | 4   1     |
B | 1       3 | 4   2     |     9   8 |
C |         8 |     6   9 |     5     |
  +-----------+-----------+-----------+
D |     8     |         2 |           |
E | 4         | 5   9   3 | 2   8     |
F | 3         |           | 6         |
  +-----------+-----------+-----------+
G |           |           |     7     |
H |         1 | 9         | 5       4 |
I | 2         | 8   3   4 | 9         |
  +-----------+-----------+-----------+
  s:3 steps:0 rounds:3 digits:33
$ 

27 vihjeisiä helppoja sudokuja:

    A   B   C   D   E   F   G   H   I  
  +-----------+-----------+-----------+
A |           |           |     5   9 |
B | 7         |           | 3         |
C | 4         | 9   8     |         2 |
  +-----------+-----------+-----------+
D |           |     1   2 | 7   3   5 |
E | 3         |           | 2   4     |
F | 6         | 4   3     |           |
  +-----------+-----------+-----------+
G |           |           |     1     |
H | 5       8 | 7         |           |
I |     4     |         5 |     2   3 |
  +-----------+-----------+-----------+
  s:0 steps:0 rounds:4 digits:27
    A   B   C   D   E   F   G   H   I  
  +-----------+-----------+-----------+
A |         3 |     9     | 6   4     |
B | 2   6     | 3   7     |     1     |
C | 7       5 |           |           |
  +-----------+-----------+-----------+
D | 6         |         3 |     8   9 |
E |           |           |           |
F |     7     |     8     | 2   5     |
  +-----------+-----------+-----------+
G |           |         4 |     2     |
H |           | 8   5     |         4 |
I | 8         |     1   7 |           |
  +-----------+-----------+-----------+
  s:1 steps:0 rounds:6 digits:27
    A   B   C   D   E   F   G   H   I  
  +-----------+-----------+-----------+
A |         8 |         6 | 2         |
B |     5   4 |     1     | 6         |
C |     1     | 2         |         5 |
  +-----------+-----------+-----------+
D |           | 3   7   5 |           |
E |           |           |           |
F | 7   4     | 9       2 |           |
  +-----------+-----------+-----------+
G |         6 |           | 5   4   3 |
H |           |         7 | 8   9     |
I |     3     | 6       8 |           |
  +-----------+-----------+-----------+
  s:2 steps:0 rounds:4 digits:27
    A   B   C   D   E   F   G   H   I  
  +-----------+-----------+-----------+
A |     1     |           |           |
B |     7     | 1         |         5 |
C |         4 |     8   6 |     1   3 |
  +-----------+-----------+-----------+
D | 6         | 9         |         2 |
E | 9       8 |           |           |
F | 4   5     |     3     | 8         |
  +-----------+-----------+-----------+
G |           | 8       2 |     3     |
H | 3   4     | 5         | 9       8 |
I |           |           | 7         |
  +-----------+-----------+-----------+
  s:3 steps:0 rounds:5 digits:27
$

Ja tässä 23 vihjeisiä helppoja sudokuja:

  $ ./newressusudoku --digits23 --sudokues4
    A   B   C   D   E   F   G   H   I  
  +-----------+-----------+-----------+
A |     1     | 6   8     |           |
B |           |     4     | 9   3   5 |
C | 9         | 2         |           |
  +-----------+-----------+-----------+
D | 1         |           | 3         |
E | 6         | 9         | 5       2 |
F |           |         7 |           |
  +-----------+-----------+-----------+
G |           |           |     6     |
H |         4 |           | 7   5     |
I | 2         |           | 8       9 |
  +-----------+-----------+-----------+
  s:0 steps:0 rounds:7 digits:23
    A   B   C   D   E   F   G   H   I  
  +-----------+-----------+-----------+
A |         8 |           |     7     |
B |           |         2 |     8     |
C |     3     |     4   9 |           |
  +-----------+-----------+-----------+
D |     4     |     9   3 |           |
E | 6         |           |     1   4 |
F |     8     |         5 |           |
  +-----------+-----------+-----------+
G |           |     6   4 | 9   5     |
H |     9     |         7 |           |
I |         3 |           |         6 |
  +-----------+-----------+-----------+
  s:1 steps:0 rounds:4 digits:23
    A   B   C   D   E   F   G   H   I  
  +-----------+-----------+-----------+
A |     8     |         2 |           |
B | 1         |         6 |         2 |
C | 5         |           |         1 |
  +-----------+-----------+-----------+
D |     3     |         1 |           |
E |         4 |     2     |     7     |
F |         9 | 3       8 |           |
  +-----------+-----------+-----------+
G |         2 |     8     |         4 |
H | 8       5 |     7     | 9         |
I |           |           |         5 |
  +-----------+-----------+-----------+
  s:2 steps:0 rounds:6 digits:23
    A   B   C   D   E   F   G   H   I  
  +-----------+-----------+-----------+
A | 1         |           |           |
B |     9     | 7   6     |     3     |
C |           |         3 |         6 |
  +-----------+-----------+-----------+
D | 4       1 |     2   8 |           |
E |     6     |           | 9         |
F |         2 |           |         5 |
  +-----------+-----------+-----------+
G |           |         1 |         8 |
H |           | 5         | 7   1   2 |
I |         5 |     3     |           |
  +-----------+-----------+-----------+
  s:3 steps:0 rounds:6 digits:23
$

Jos haluat näitä kauttaviiva (slashes) sudokuja:

$ ./newressusudoku --slashes --digits27
    A   B   C   D   E   F   G   H   I  
  +-----------+-----------+-----------+
A |         5 |         1 |         8 |
B |     9     |     4     |     3     |
C | 1         | 7         | 5         |
  +-----------+-----------+-----------+
D |         1 |         8 |         4 |
E |     6     |     2     |     7     |
F | 7         | 3         | 6         |
  +-----------+-----------+-----------+
G |         3 |         7 |         5 |
H |     8     |     5     |     9     |
I | 9         | 8         | 3         |
  +-----------+-----------+-----------+
  s:0 steps:0 rounds:5 digits:27
$ 

Kauttaviivasudokut voidaan sekoittaa (mix()) siten että kuitenkin rivit sarakkeet ja blokit säilyvät samanlaisina:

$ ./newressusudoku --slashes --mix --digits27
    A   B   C   D   E   F   G   H   I  
  +-----------+-----------+-----------+
A |     6     |         1 |         5 |
B | 5         | 6         |     2     |
C |         7 |     5     | 1         |
  +-----------+-----------+-----------+
D |     7     |         5 |         9 |
E |         5 |     4     | 8         |
F | 2         | 8         |     1     |
  +-----------+-----------+-----------+
G |         8 |     7     | 4         |
H |     9     |         6 |         2 |
I | 3         | 4         |     7     |
  +-----------+-----------+-----------+
  s:0 steps:0 rounds:4 digits:27
$ 

Seuraavassa kauttaviivasudoku ja sen sekoitettu versio, joista huomataan että rivit, sarakkeet ja laatikot pysyvät samoina:

$ ./newressusudoku --slashes --mix --verbose --digits27
    A   B   C   D   E   F   G   H   I  
  +-----------+-----------+-----------+
A |         2 |         6 |         4 |
B |     8     |     2     |     3     |
C | 7         | 8         | 5         |
  +-----------+-----------+-----------+
D |         1 |         5 |         8 |
E |     4     |     8     |     7     |
F | 6         | 3         | 2         |
  +-----------+-----------+-----------+
G |         9 |         4 |         3 |
H |     3     |     7     |     2     |
I | 8         | 5         | 4         |
  +-----------+-----------+-----------+
    A   B   C   D   E   F   G   H   I  
  +-----------+-----------+-----------+
A |         2 | 4         | 6         |
B |     8     |         3 |     2     |
C | 7         |     5     |         8 |
  +-----------+-----------+-----------+
D | 8         |     4     |         5 |
E |         9 | 3         | 4         |
F |     3     |         2 |     7     |
  +-----------+-----------+-----------+
G | 6         |     2     |         3 |
H |     4     |         7 |     8     |
I |         1 | 8         | 5         |
  +-----------+-----------+-----------+
  s:0 steps:0 rounds:5 digits:27
$

27 vihjeisiä slashes sudokuja:

$ ./newressusudoku --digits27 --slashes --mix --sudokues4
A   B   C   D   E   F   G   H   I  
  +-----------+-----------+-----------+
A |         9 |         8 |         1 |
B |     2     |     3     |     6     |
C | 5         | 2         | 4         |
  +-----------+-----------+-----------+
D |         4 |         9 |         7 |
E |     1     |     2     |     4     |
F | 9         | 7         | 6         |
  +-----------+-----------+-----------+
G |         8 |         6 |         2 |
H |     4     |     9     |     7     |
I | 7         | 3         | 5         |
  +-----------+-----------+-----------+
  s:0 steps:0 rounds:5 digits:27
    A   B   C   D   E   F   G   H   I  
  +-----------+-----------+-----------+
A |         3 |     8     | 6         |
B |     7     |         6 |         9 |
C | 8         | 7         |     1     |
  +-----------+-----------+-----------+
D |         8 |     3     | 5         |
E | 2         | 5         |     9     |
F |     1     |         9 |         8 |
  +-----------+-----------+-----------+
G | 1         | 3         |     8     |
H |         2 |     1     | 7         |
I |     8     |         4 |         6 |
  +-----------+-----------+-----------+
  s:1 steps:0 rounds:5 digits:27
    A   B   C   D   E   F   G   H   I  
  +-----------+-----------+-----------+
A |         6 |         9 |         1 |
B |     2     | 6         | 9         |
C | 3         |     8     |     2     |
  +-----------+-----------+-----------+
D |     4     | 3         | 7         |
E |         5 |         7 |         3 |
F | 9         |     5     |     6     |
  +-----------+-----------+-----------+
G |     5     | 8         | 2         |
H |         7 |         2 |         4 |
I | 8         |     9     |     3     |
  +-----------+-----------+-----------+
  s:2 steps:0 rounds:5 digits:27
    A   B   C   D   E   F   G   H   I  
  +-----------+-----------+-----------+
A | 8         |     3     | 7         |
B |         5 |         9 |         4 |
C |     2     | 1         |     9     |
  +-----------+-----------+-----------+
D |         7 |         2 |         5 |
E | 1         |     9     | 4         |
F |     5     | 4         |     7     |
  +-----------+-----------+-----------+
G | 7         |     5     | 9         |
H |         8 |         6 |         1 |
I |     3     | 9         |     4     |
  +-----------+-----------+-----------+
  s:3 steps:0 rounds:8 digits:27
$

26 vihjeisiä helppoja slashes sudokuja:

$ ./newressusudoku --digits26 --slashes --mix --sudokues4
    A   B   C   D   E   F   G   H   I  
  +-----------+-----------+-----------+
A |         2 | 8         | 3         |
B |     7     |     2     |     1     |
C | 3         |         9 |         5 |
  +-----------+-----------+-----------+
D | 9         |         1 |         2 |
E |     5     |     3     |     8     |
F |         4 | 5         | 1         |
  +-----------+-----------+-----------+
G | 5         |           |         8 |
H |     1     |     7     |     9     |
I |         7 | 6         | 5         |
  +-----------+-----------+-----------+
  s:0 steps:0 rounds:4 digits:26
    A   B   C   D   E   F   G   H   I  
  +-----------+-----------+-----------+
A |     4     |     7     |         2 |
B | 5         | 4         | 8         |
C |         7 |         8 |     6     |
  +-----------+-----------+-----------+
D |     9     |     2     |         3 |
E | 1         | 8         | 7         |
F |         3 |         4 |     9     |
  +-----------+-----------+-----------+
G |     1     |     6     |         7 |
H |         2 |         3 |     1     |
I | 3         | 1         |           |
  +-----------+-----------+-----------+
  s:1 steps:0 rounds:6 digits:26
    A   B   C   D   E   F   G   H   I  
  +-----------+-----------+-----------+
A | 7         | 4         |     1     |
B |         3 |     7     | 5         |
C |     5     |         9 |         8 |
  +-----------+-----------+-----------+
D | 6         | 3         |     2     |
E |     9     |         1 |         6 |
F |         5 |     8     | 3         |
  +-----------+-----------+-----------+
G |     7     |         8 |         2 |
H | 9         |           |     4     |
I |         4 |     2     | 9         |
  +-----------+-----------+-----------+
  s:2 steps:0 rounds:4 digits:26
    A   B   C   D   E   F   G   H   I  
  +-----------+-----------+-----------+
A | 2         |     8     | 1         |
B |         4 | 9         |         2 |
C |     1     |         7 |     9     |
  +-----------+-----------+-----------+
D |     5     |         4 |     8     |
E | 8         |     6     | 4         |
F |         3 | 2         |         6 |
  +-----------+-----------+-----------+
G | 6         |     4     | 5         |
H |         5 |           |         7 |
I |     2     |         1 |     6     |
  +-----------+-----------+-----------+
  s:3 steps:0 rounds:5 digits:26
$

23 vihjeisiä helppoja slashes sudokuja:

$ ./newressusudoku --digits23 --slashes --mix --sudokues4
    A   B   C   D   E   F   G   H   I  
  +-----------+-----------+-----------+
A | 2         |           | 1         |
B |         9 | 8         |     3     |
C |     6     |         4 |         5 |
  +-----------+-----------+-----------+
D |     4     |         3 |         6 |
E | 9         |     2     |           |
F |         8 | 6         |     2     |
  +-----------+-----------+-----------+
G |           |     4     | 8         |
H |     5     |           |         2 |
I |         7 | 3         |     5     |
  +-----------+-----------+-----------+
  s:0 steps:0 rounds:6 digits:23
    A   B   C   D   E   F   G   H   I  
  +-----------+-----------+-----------+
A |         8 |         5 |         6 |
B |     1     |           |     3     |
C | 4         | 2         | 9         |
  +-----------+-----------+-----------+
D |         7 |         6 |         8 |
E |     3     |     1     |     4     |
F |           | 3         | 7         |
  +-----------+-----------+-----------+
G |         3 |           |         9 |
H |     9     |     4     |     5     |
I | 1         | 7         |           |
  +-----------+-----------+-----------+
  s:1 steps:0 rounds:5 digits:23
    A   B   C   D   E   F   G   H   I  
  +-----------+-----------+-----------+
A |         1 | 9         |         2 |
B |     7     |     5     | 6         |
C | 9         |         6 |     5     |
  +-----------+-----------+-----------+
D |           |         5 |     3     |
E |     8     |     1     |           |
F |           |           |         5 |
  +-----------+-----------+-----------+
G |     5     |     4     | 8         |
H |         9 | 2         |         6 |
I | 4         |         7 |     9     |
  +-----------+-----------+-----------+
  s:2 steps:0 rounds:7 digits:23
    A   B   C   D   E   F   G   H   I  
  +-----------+-----------+-----------+
A | 7         |     3     | 4         |
B |         4 |         2 |         1 |
C |     1     | 6         |     2     |
  +-----------+-----------+-----------+
D |           |         4 |         7 |
E | 4         |           |           |
F |     2     | 5         |     6     |
  +-----------+-----------+-----------+
G | 6         |     5     | 8         |
H |         9 |         7 |         3 |
I |     5     |           |     9     |
  +-----------+-----------+-----------+
  s:3 steps:0 rounds:5 digits:23
$

Edellä haetut olivat ns helppoja sudokuja, jotka ratkeavat varmasti päättelemällä ruutu ruudulta, teknisesti ne ratkeavat aina simplesolve() menetelmällä. Seuraavassa hakuun käytetään solve() menettelyä: Alarivin muuttujat kertovat taustatietoa sudokusta. S: kenttä kertoo sudokun numeron tässä haussa, rounds: kertoo kuinka monta simple() menetelmän kierrosta tarvitaan sudokun ratkaisussa. steps: kertoo kuinka monta solve():n askelta tarvitaan sudokun raatkaisussa koneellisesti. Edellisesiä voi käyttää sudokun vaikeuden arvioinnissa. Seuraava sudoku on helppo, koska sekä steps ja rounds ovat nollia.

$ ./newressusudoku --solve
    A   B   C   D   E   F   G   H   I  
  +-----------+-----------+-----------+
A |           | 1   6     |     7   5 |
B |           |         8 |           |
C | 8       9 |     7     | 1   3     |
  +-----------+-----------+-----------+
D |           |           |           |
E | 7         |     5     | 2       6 |
F |         8 | 7         |     4     |
  +-----------+-----------+-----------+
G |         1 | 8   2     | 7         |
H |         2 |         7 |           |
I | 3         |         5 |     9   2 |
  +-----------+-----------+-----------+
  s:0 steps:0 rounds:0 digits:27
$

Oletan että ohjelman steps arvolla voidaan arvioida sudokun vaikeutta, ja olenkin etsinyt malliksi mielestäni todella vaikeita sudokuja:

    A   B   C   D   E   F   G   H   I
  +-----------+-----------+-----------+
A |           |           |         3 |
B | 1         | 8         | 6   7     |
C |           |     5     |           |
  +-----------+-----------+-----------+
D | 3   9     |         8 | 1   2     |
E |           |         5 |         4 |
F |           | 6         |           |
  +-----------+-----------+-----------+
G |     4     |     3   2 | 7   1     |
H | 2         | 1         |         8 |
I | 9   1     | 5   8     |           |
  +-----------+-----------+-----------+
  s:0 steps:638 rounds:0 digits:26
    A   B   C   D   E   F   G   H   I
  +-----------+-----------+-----------+
A | 1   2     |     9     |         4 |
B | 4         |         8 |     7     |
C | 8   3     | 2         |           |
  +-----------+-----------+-----------+
D | 6         |     2     |           |
E | 2         | 1   6     | 4   3   5 |
F |           |           | 7         |
  +-----------+-----------+-----------+
G |           |     8     | 2   4     |
H |           |           | 5       3 |
I |           |     4     |           |
  +-----------+-----------+-----------+
  s:0 steps:512 rounds:0 digits:25
    A   B   C   D   E   F   G   H   I
  +-----------+-----------+-----------+
A |     2     |         3 | 5         |
B |     1     |     9     |           |
C |     3   9 | 6         |           |
  +-----------+-----------+-----------+
D |         7 |           | 3         |
E |         8 |     7     |         2 |
F |           |         1 |     7   6 |
  +-----------+-----------+-----------+
G |         3 |         6 | 2         |
H | 6   9     |     3   5 |     8     |
I |           |           |           |
  +-----------+-----------+-----------+
  s:0 steps:489 rounds:0 digits:24
    A   B   C   D   E   F   G   H   I
  +-----------+-----------+-----------+
A |           |     5   8 |     2   9 |
B |     8     |     2   1 |         6 |
C |     2     |     7   3 |           |
  +-----------+-----------+-----------+
D |           |           |           |
E |           |         4 | 5         |
F |     6     | 2       9 |           |
  +-----------+-----------+-----------+
G | 2   1     | 3   9   7 |         8 |
H |     7   4 |           | 9         |
I | 3         |           |     7     |
  +-----------+-----------+-----------+
  s:0 steps:309 rounds:0 digits:27
    A   B   C   D   E   F   G   H   I
  +-----------+-----------+-----------+
A | 5         | 9       2 |         6 |
B |           |     7     | 5         |
C | 6         |     5     |           |
  +-----------+-----------+-----------+
D | 7       1 |     9   3 |     6     |
E | 4   2     | 6       7 | 3       8 |
F |         6 | 5         |         7 |
  +-----------+-----------+-----------+
G | 1       8 |         9 |         2 |
H |     7     |         5 |           |
I |           |           | 9   7     |
  +-----------+-----------+-----------+
  s:0 steps:178 rounds:0 digits:30

Tässä mielestäni vaikeita slashes sudokuja:

    A   B   C   D   E   F   G   H   I  
  +-----------+-----------+-----------+
A |     4     |         6 |         5 |
B | 9         |     2     |           |
C |           | 5         |     4     |
  +-----------+-----------+-----------+
D |         9 | 8         |     1     |
E |     3     |         9 |         4 |
F |           |     6     | 9         |
  +-----------+-----------+-----------+
G |     1     |         5 |         8 |
H |         8 | 1         |     2     |
I |           |     4     | 3         |
  +-----------+-----------+-----------+
  s:0 steps:413 rounds:0 digits:23
    A   B   C   D   E   F   G   H   I  
  +-----------+-----------+-----------+
A |           | 8         |           |
B |           |         1 |         7 |
C |     9     |     7     | 6         |
  +-----------+-----------+-----------+
D | 8         | 1         |     3     |
E |         1 |         4 |         2 |
F |     6     |     2     | 1         |
  +-----------+-----------+-----------+
G |         3 |         7 |         8 |
H | 6         | 2         |     9     |
I |     2     |     4     | 5         |
  +-----------+-----------+-----------+
  s:0 steps:553 rounds:0 digits:24
    A   B   C   D   E   F   G   H   I  
  +-----------+-----------+-----------+
A |           |     3     |         5 |
B | 4         |         1 |     2     |
C |         2 | 9         | 4         |
  +-----------+-----------+-----------+
D |         5 | 1         | 3         |
E | 8         |         3 |     7     |
F |           |     9     |         1 |
  +-----------+-----------+-----------+
G |         4 | 5         | 2         |
H |     3     |     4     |         6 |
I | 6         |         8 |     4     |
  +-----------+-----------+-----------+
  s:0 steps:371 rounds:0 digits:25
    A   B   C   D   E   F   G   H   I  
  +-----------+-----------+-----------+
A |         2 | 1         |     9     |
B |     1     |     9     |         3 |
C | 9         |         4 | 1         |
  +-----------+-----------+-----------+
D | 8         |         5 | 6         |
E |     7     |     1     |           |
F |         9 | 8         |     2     |
  +-----------+-----------+-----------+
G |         6 | 2         |     1     |
H | 4         |         1 | 3         |
I |     9     |     5     |         8 |
  +-----------+-----------+-----------+
  s:0 steps:197 rounds:0 digits:26

Myös simplesolven rounds: yrittää kuvata sudokun vaikeutta, mutta siitä huolimatta nämä ratkeavat yksinkertaisilla menetelmillä (katso simplesolve): seuraavassa muutamia:

    A   B   C   D   E   F   G   H   I
  +-----------+-----------+-----------+
A |     7     |     9     | 5       2 |
B | 8         | 5       1 |           |
C |           |           |         1 |
  +-----------+-----------+-----------+
D |           |           | 3   7     |
E | 2         |     8     |           |
F |         4 |           |         9 |
  +-----------+-----------+-----------+
G |           |         7 | 6         |
H |     9   5 |         3 | 8         |
I |         8 | 4         |     1     |
  +-----------+-----------+-----------+
  s:0 steps:0 rounds:12 digits:23
    A   B   C   D   E   F   G   H   I
  +-----------+-----------+-----------+
A |         7 | 9       5 |           |
B |           | 8       1 |           |
C |           | 4   6     |     9   7 |
  +-----------+-----------+-----------+
D | 8         |           |     6     |
E |         6 |           | 7       3 |
F |           | 6   8     | 9       4 |
  +-----------+-----------+-----------+
G | 1         | 3         |     4     |
H | 6       3 | 2       9 |           |
I |         2 |           |     5     |
  +-----------+-----------+-----------+
  s:0 steps:0 rounds:12 digits:27
    A   B   C   D   E   F   G   H   I
  +-----------+-----------+-----------+
A | 9         |         6 | 5         |
B |           | 1         |     6     |
C | 3   6     |     7     | 8         |
  +-----------+-----------+-----------+
D |         4 | 6       8 |     7     |
E | 7       5 | 2   1   4 | 3       6 |
F |     8     |           | 1         |
  +-----------+-----------+-----------+
G | 4         | 3       2 |           |
H |           |           |     3   9 |
I |           |     6     | 2         |
  +-----------+-----------+-----------+
  s:0 steps:0 rounds:12 digits:29

Seuraavassa ohjelman osien läpikäyminen, ensimmäisenä rutiinit,joilla sudokuetsin hakee satunnaisbitit: ensimmäinen palauttaa yhden merkin satunnaismerkin, ja jos sisäisen puskurin (gent) merkit loppuvat, hakee seuraavan puskurillisen pseudoressu_bytes() rutiinilla. Toinen rutiini palauttaa satunnaisen kokonaisluvun nollan ja halutun kokonaisluvun välillä. Funktion parametrinä oleva yläraja ei kuulu joukkoon, vaan maksimissaan palautetaan yhtä pienempi arvo.

int newressu_genbyte()
{
  unsigned char ch;

  if(gent_pos == 0) {
    memset(gent, 0, sizeof(gent));
    pseudoressu_bytes(sizeof(gent), gent);
  } // if(gent_pos == 0
  ch = gent[gent_pos];
  gent_pos = (gent_pos + 1) % sizeof(gent);

  return(ch);
}

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

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

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

  word %= limit;

  return(word);
}

Seuraava rutiini palauttaa tämän sudokun (b) solun (cell) mahdolliset vaihtoehdot (choices2)

void sudoku_get_choices(unsigned char b[82], int cell, char choices2[10])
{
  int c, d, lin, col, box;
  char choices[10], digit[2], digits[]="123456789";

  lin = cell / 9;
  col = cell % 9;
  box = lin / 3 * 27 + col / 3 * 3;
  
  choices[0] = '\0';
  
  for(c = 0; c < 9; c++) {
    if(b[lin * 9 + c] != ' ' && strchr(choices, b[lin * 9 + c]) == NULL) {
      digit[0] = b[lin * 9 + c];
      digit[1] = '\0';
      strcat(choices, digit);
    }
  }
  
  for(c = 0; c < 9; c++) {
    if(b[c * 9 + col] != ' ' && strchr(choices, b[c * 9 + col]) == NULL) {
      digit[0] = b[c * 9 + col];
      digit[1] = '\0';
      strcat(choices, digit);
    }
  }
  
  for(c = 0; c < 3; c++) {
    for(d = 0; d < 3; d++) {
      if(b[box + c * 9 + d] != ' ' && strchr(choices, b[box + c * 9 + d]) == NULL) {
	digit[0] = b[box + c * 9 + d];
	digit[1] = '\0';
	strcat(choices, digit);
      }
    }
  }
  
  choices2[0] = '\0';

  for(c = 0; c < 9; c++) {
    if(strchr(choices, digits[c]) == NULL) {
      digit[0] = digits[c];
      digit[1] = '\0';
      strcat(choices2, digit);
    }
  }
}

Seuraavilla sudoku_boardx() rutiineilla tulostetaan sudokuruudukot eri muodoissaan: seuraavana sudoku_board, joka tulostaa oletusmuotoilun mukaisen ruudukon (–board1).

void sudoku_board(unsigned char *s, unsigned char b[82])
{
  int c;
  unsigned char line[128];
  
  *s='\0';

  strcat(s, "    A   B   C   D   E   F   G   H   I  \n");
  for(c = 0; c < 81; c++) {
    if(c > 0 && c % 9 == 0) {
      strcat(s,"|\n");
    }
    if(c % 27 == 0)
      strcat(s,"  +-----------+-----------+-----------+\n");
    if(c % 9 == 0) {
      sprintf(line,"%c ",'A' + c / 9);
      strcat(s, line);
    }
    if(c % 3 == 0) {
      strcat(s, "|");
    } else strcat(s, " ");
    if(b[c] != ' ' && b[c] != '0')
      sprintf(line," %c ", b[c]);
    else
      sprintf(line,"   ");
    strcat(s, line);
  }
  strcat(s, "|\n  +-----------+-----------+-----------+");
  strcat(s, "\n");
}

Seuraavana sudoku ruudukon tulostava rutiini, joka tulostaa halutun muotoisen sudokun:

void sudoku_print(unsigned char *b, int board)
{
  unsigned char string[2048];

  string[0] = '\0';
  
  /* zero -> no board */
  if(board == 1) 
    sudoku_board(string, b);
  else if(board == 2) 
    sudoku_board2(string, b);
  else if(board == 3) 
    sudoku_board3(string, b);
  else if(board == 4) 
    sudoku_board4(string, b);
  else if(board == 5) 
    sudoku_board5(string, b);
  else if(board == 6) 
    sudoku_board6(string, b);
  else if(board == 10) 
    sudoku_board10(string, b);
  else if(board == 11) 
    sudoku_board11(string, b);
  else if(board == 20) 
    sudoku_board20(string, b);
  else if(board == 21) 
    sudoku_board21(string, b);

  fprintf(stdout,"%s", string);
}

Seuraavassa pienet rutiinit, joilla voidaan tarkastaa onko sudoku täynnä (valmis), tai tyhjentää sudoku:

int sudoku_full(unsigned char b[82])
{
  for(int c = 0; c < 81; c++) {
    if(b[c] == ' ')
      return(0);
  }
  return(1);
}

void sudoku_clear(unsigned char b[82])
{
  int c;
  for(c = 0; c < 81; c++) {
    b[c] = ' ';
  }
  b[c] = '\0';
}

Seuraavaksi solve() rutiinin aliohjelma, jolla voidaan tarkastaa onko sudokuruudun (r) tietylle riville (i) ja tiettyyn sarakkeeseen (j) tietty numero mahdollinen.

int sudoku_isallowed(unsigned char b[82], int i, int j, int e)
{
  int k, l, base;

  for(k = 0; k < 9; k++) {
    if(b[i * 9 + k] == e)
      return(0);
  }
  for(k = 0; k < 9; k++) {
    if(b[9 * k + j] == e)
      return(0);
  }
  base = i / 3 * 27 + j / 3 * 3;
  for(k = 0; k < 3; k++) {
    for(l = 0; l < 3; l++) {
      if(b[base + k * 9 + l] == e)
	return(0);
    }
  }
  return(1);
}

Seuraavalla rutiinilla solve() hakee seuraavan solun, johon tarvitaan numero: eli palautetaan ensimmäisen tyhjän ruudun rivi ja sarake.

int sudoku_getnextemptycell(char *b, int *i, int *j)
{
  int i2, j2;
  
  for(i2 = 0; i2 < 9; i2++) {
    for(j2 = 0; j2 < 9; j2++) {
      if(*(b + i2 * 9 + j2) == ' ') {
	*i = i2;
	*j = j2;
	return(1);
      }
    }
  }
  *i = -1;
  *j = -1;
  return(0);
}

Seuraavassa solve() rutiini. Solve2() on sen alirutiini, joka tekee varsinaisen rekursiivisen taulukon ratkaisun:

int sudoku_solve2(unsigned char *sudoku, unsigned char *solution, long long *solutions)
{
  int c, i, j;
  
  if(sudoku_getnextemptycell(sudoku, &i, &j) == 0) {
    (*solutions)++;
    if(solve_verbose) {
      if(*solutions % 1000000000 == 0)
	fprintf(stdout,"%lld: %s\n", *solutions, sudoku);
    }
    if(*solutions == 1)
      strcpy(solution, sudoku);

#ifdef SOLVE_QUICK_EXIT
    if(solve_quick_exit && *solutions > 1) // quick exit && multiple solutions, fail
      return(1);
    else
#endif
      return(0);
  }

  for(c = '1'; c <= '9'; c++) {
    if(sudoku_isallowed(sudoku, i, j, c) == 1) {
#ifdef SOLVE_USESIMPLE
      char savesudoku[82];
      if(solve_usesimple)
	strcpy(savesudoku, sudoku);
#endif
      
      *(sudoku + i * 9 + j) = c;
      if(solve_verbose)
	fprintf(stdout,"%s\n", sudoku);
      
#ifdef SOLVE_USESIMPLE
      if(solve_usesimple)
	sudoku_simplesolve(sudoku);
#endif
      
      solve_steps++;
#ifdef DEBUG57
      if(solve_verbose)
	fprintf(stdout,"%s\n", sudoku);
#endif
      if(sudoku_solve2(sudoku, solution, solutions) == 1)
	return(1);
#ifdef SOLVE_USESIMPLE
      if(solve_usesimple)
	strcpy(sudoku, savesudoku);
#endif
      *(sudoku + i * 9 + j) = ' ';
    }
  }      
  return(0);
}

int sudoku_solve(unsigned char *sudoku)
{
  unsigned char tsudoku[82], solution[82];

  solve_steps = 0;
  solutions = 0;
  memcpy(tsudoku, sudoku, sizeof(tsudoku));
  sudoku_simplesolve(tsudoku);
  sudoku_solve2(tsudoku, solution, &solutions);
  if(solutions == 1) { // single solution
    memcpy(sudoku, solution, sizeof(solution));
    if(solve_verbose) {
      fprintf(stdout,"%s\n", sudoku);
    }
    return(1);
  } else {
    return(0);
  }
}

Seuraavassa simplesolven eri versiot: simplesolve1 ratkaisee tapaukset, joissa sudokuruudulla on vain yksi mahdollinen vaihtoehto:

int sudoku_simplesolve1(unsigned char b[82])
{
  int c, changes = 0;
  unsigned char choices[10];

  for(c = 0; c < 81; c++) {
    if(newb[c] == ' ') { // empty cell
      choices[0] = '\0';

      sudoku_get_choices(newb, c, choices);

      int lenchoices = strlen(choices);
      if(lenchoices == 0) {
	break;
      } else if(lenchoices == 1) {
	if(simple_verbose) {
#ifdef DEBUG32
	  fprintf(stdout,"simplesolve1");
#endif
	  fprintf(stdout," %c%c%c", c / 9 + 'A', c % 9 + 'A', *choices);
#ifdef DEBUG32
	  fprintf(stdout,"\n");
	  sudoku_print(newb, 1);
#endif
	}
	newb[c] = *choices;
	if(simple_verbose) {
	  simple_count++;
	  simple_output = 1;
	}
#ifdef DEBUG32
	if(simple_verbose) {
	  fprintf(stdout,"\n");
	  sudoku_print(newb, 1);
	}
#endif
	changes++;
      } // end of if(strlen(choices)
    } // end of if(b[c] == ' ') { // empty cell
  } // end of for(c

  return(changes);
}

Simplesolve2 ratkaisee tapaukset, joissa rivillä numero sopii vain yhteen soluun:

int sudoku_simplesolve2(unsigned char b[82]) {
  int c, h, i, j, count, cell, base, changes = 0;
  char choices[81][10];
  
  for(c = '1'; c <= '9'; c++) { // digits
    for(i = 0; i < 9; i++) { // rows
      count = 0;
      cell = -1;
      for(j = 0; j < 9; j++) { // columns
	if(newb[i * 9 + j] == ' ' && sudoku_isallowed(newb, i, j, c) == 1) {
	  count++;
	  cell = i * 9 + j;
	}
      }
      if(count == 1 && cell != -1 && newb[cell] == ' ') {
	if(simple_verbose) {
#ifdef DEBUG34
	  fprintf(stdout,"simplesolve2: rows");
#endif
	  fprintf(stdout," %c%c%c", cell / 9 + 'A', cell % 9 + 'A', c);
#ifdef DEBUG34
	  fprintf(stdout,"\n");
	  sudoku_print(newb, 1);
#endif
	}
	newb[cell] = c;
	if(simple_verbose) {
	  simple_count++;
	  simple_output = 1;
	}
#ifdef DEBUG34
	if(simple_verbose) {
	  fprintf(stdout,"\n");
	  sudoku_print(newb, 1);
	}
#endif
	changes++;
	//sudoku_get_all_choices(b, choices);
      }
    }
  }

  return(changes);
}

Simplesolve3 ratkaisee tapaukset, joissa sarakkeessa on vain yksi paikka, johon voi laittaa tietyn numeron:

int sudoku_simplesolve3(unsigned char b[82]) {
  int c, h, i, j, count, cell, base, changes = 0;
  char choices[81][10];
  
  sudoku_get_all_choices(b, choices);

  for(c = '1'; c <= '9'; c++) { // digits
    for(j = 0; j < 9; j++) { // columns
      count = 0;
      cell = -1;
      for(i = 0; i < 9; i++) { // rows
	if(newb[i * 9 + j] == ' ' && sudoku_isallowed(newb, i, j, c) == 1) {
	  count++;
	  cell = i * 9 + j;
	}
      }
      if(count == 1 && cell != -1 && newb[cell] == ' ') {
	if(simple_verbose) {
#ifdef DEBUG36
	  fprintf(stdout,"simplesolve3: columns");
#endif
	  fprintf(stdout," %c%c%c", cell / 9 + 'A', cell % 9 + 'A', c);
	  simple_output = 1;
#ifdef DEBUG36
	  fprintf(stdout,"\n");
	  sudoku_print(newb, 1);
#endif
	}
	newb[cell] = c;
	simple_count++;

#ifdef DEBUG36
	if(simple_verbose) {
	  fprintf(stdout,"\n");
	  sudoku_print(newb, 1);
	}
#endif
	changes++;
	//sudoku_get_all_choices(b, choices);
      }
    }
  }

  return(changes);
}

Simplesolve4 ratkaisee tapaukset joissa numero voidaan laittaa vain yhteen laatikon soluun:

int sudoku_simplesolve4(unsigned char b[82]) {
  int c, h, i, j, basei, basej, count, cell, changes = 0;
  char choices[81][10];
  
  for(basei = 0; basei < 9; basei += 3) {
    for(basej = 0; basej < 9; basej += 3) {
      for(c = '1'; c <= '9'; c++) { // digits
	count = 0;
	cell = -1;
	for(i = 0; i < 3; i++) {
	  for(j = 0; j < 3; j++) {
#ifdef KOK
	    fprintf(stdout,"basei:%d", basei);
	    fprintf(stdout," basej:%d", basej);
	    fprintf(stdout," i:%d", i);
	    fprintf(stdout," j:%d", j);
	    fprintf(stdout," basei+i:%d", basei + i);
	    fprintf(stdout," basej+j:%d", basej + j);
	    fprintf(stdout," c:%d", c);
	    fprintf(stdout," cell:%d", cell);
	    fprintf(stdout,"\n");
#endif
	    if(newb[(basei + i) * 9 + basej + j] == ' ' && sudoku_isallowed(newb, basei + i, basej + j, c) == 1) {
	      cell = (basei + i) * 9 + j + basej;
	      count++;
	    }
	  }
	}
	if(count == 1 && cell != -1 && newb[cell] == ' ') {
	  if(simple_verbose) {
#ifdef DEBUG38
	    fprintf(stdout,"simplesolve4: boxes");
#endif
	    fprintf(stdout," %c%c%c", cell / 9 + 'A', cell % 9 + 'A', c);
#ifdef DEBUG38
	    fprintf(stdout,"\n");
	    sudoku_print(newb, 1);
#endif
	  }
	  newb[cell] = c;
	  
	  if(simple_verbose) {
	    simple_count++;
	    simple_output = 1;
	  }
#ifdef DEBUG38
	  if(simple_verbose) {
	    fprintf(stdout,"\n");
	    sudoku_print(newb, 1);
	  }
#endif
	  changes++;
	  //sudoku_get_all_choices(b, choices);
	} // end of if(count
      } // end of for(c
    } // end of for(basej
  } // end of for(basei

  return(changes);
}

Lisätty ohjelmaan ensimmäinen kaavailu DB8 liittymästä.

....
#define aOUTPUTDB 2
#define OUTPUTFILE 2

#ifdef OUTPUTDB
#include "db8.h"
#endif
....
#ifdef OUTPUTDB

  unsigned char temp[16];
  db8_set_filename(sudokuesdb);
  db8_put_element("sudoku", 0, "board", 0, b);
  db8_put_element("sudoku", 0, "solution", 0, s);
  db8_put_element("sudoku", 0, "cboard", 0, cboard);
  sprintf(temp,"%d",digits);
  db8_put_element("sudoku", 0, "digits", 0, temp);
  sprintf(temp,"%d",empty);
  db8_put_element("sudoku", 0, "empty", 0, temp);
  if(gmode == 0) {
    sprintf(temp,"%d", digitsa);
    db8_put_element("sudoku", 0, "digitsa", 0, temp);
    sprintf(temp,"%d", digitsb);
    db8_put_element("sudoku", 0, "digitsb", 0, temp);
  } else {
    sprintf(temp,"%d", digitsa);
    db8_put_element("sudoku", 0, "digitsa", 0, temp);
  }
  sprintf(temp,"%d", mode);
  db8_put_element("sudoku", 0, "mode", 0, temp);
  sprintf(temp,"%d", gmode);
  db8_put_element("sudoku", 0, "gmode", 0, temp);
  db8_save("sudoku");
  db8_clear("sudoku");
#endif
....

Vielä newressusudoku ohjelman kokonainen sorsa: ($ cc -DSHA256 newressusudoku.c newressu.c sha256.c -o newressusudoku -lm)

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

#define aOUTPUTDB 2
#define OUTPUTFILE 2

#ifdef OUTPUTDB
#include "db8.h"
#endif

unsigned char *procname;
static unsigned char *programname = "newressusudoku version 0.04 ©";
static unsigned char *copyright = "Copyright (c) 1998-2024 Jari Kuivaniemi (moijari.com), Helsinki, Finland. Kaikki oikeudet pidätetään!";

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

int sudoku_output = 0;

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

int newressu_genbyte()
{
  unsigned char ch;

  if(gent_pos == 0) {
    memset(gent, 0, sizeof(gent));
    pseudoressu_bytes(sizeof(gent), gent);
  } // if(gent_pos == 0
  ch = gent[gent_pos];
  gent_pos = (gent_pos + 1) % sizeof(gent);

  return(ch);
}

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

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

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

#define aDEBUG4 2

#ifdef DEBUG4
  fprintf(stdout,"limit:%lu", limit);
  fprintf(stdout,", highlimit:%lu", highlimit);
  fprintf(stdout,", bytes:%d", bytes);
  fprintf(stdout,", word:%lu", word);
  fprintf(stdout,", word%%limit:%lu\n", (unsigned long) word % limit);
#endif
  
  word %= limit;

  return(word);
}

int digitsa = -1;
int digitsb = -1;

unsigned char *cross =
  "111222111"
  "111222111"
  "111222111"
  "222111222"
  "222111222"
  "222111222"
  "111222111"
  "111222111"
  "111222111";

unsigned char *plus =
  "222111222"
  "222111222"
  "222111222"
  "111111111"
  "111111111"
  "111111111"
  "222111222"
  "222111222"
  "222111222";

unsigned char *slashes =
  "001001001"
  "010010010"
  "100100100"
  "001001001"
  "010010010"
  "100100100"
  "001001001"
  "010010010"
  "100100100";

unsigned char *full =
  "111111111"
  "111111111"
  "111111111"
  "111111111"
  "111111111"
  "111111111"
  "111111111"
  "111111111"
  "111111111";

unsigned char *sudoku_grid1 = NULL;

void sudoku_get_choices(unsigned char b[82], int cell, char choices2[10])
{
  int c, d, lin, col, box;
  char choices[10], digit[2], digits[]="123456789";

  lin = cell / 9;
  col = cell % 9;
  box = lin / 3 * 27 + col / 3 * 3;
  
  choices[0] = '\0';
  
  for(c = 0; c < 9; c++) {
    if(b[lin * 9 + c] != ' ' && strchr(choices, b[lin * 9 + c]) == NULL) {
      digit[0] = b[lin * 9 + c];
      digit[1] = '\0';
      strcat(choices, digit);
    }
  }
  
  for(c = 0; c < 9; c++) {
    if(b[c * 9 + col] != ' ' && strchr(choices, b[c * 9 + col]) == NULL) {
      digit[0] = b[c * 9 + col];
      digit[1] = '\0';
      strcat(choices, digit);
    }
  }
  
  for(c = 0; c < 3; c++) {
    for(d = 0; d < 3; d++) {
      if(b[box + c * 9 + d] != ' ' && strchr(choices, b[box + c * 9 + d]) == NULL) {
	digit[0] = b[box + c * 9 + d];
	digit[1] = '\0';
	strcat(choices, digit);
      }
    }
  }
  
  choices2[0] = '\0';

  for(c = 0; c < 9; c++) {
    if(strchr(choices, digits[c]) == NULL) {
      digit[0] = digits[c];
      digit[1] = '\0';
      strcat(choices2, digit);
    }
  }
}

/*
    A   B   C   D   E   F   G   H   I  
  +-----------+-----------+-----------+
A |           |     2     |         7 |
B |           |     6     |           |
C | 5   3     | 8   4   7 | 2         |
  +-----------+-----------+-----------+
D | 2         |         8 |     1   9 |
E |           |           |           |
F | 1       8 |     9     | 7   6   3 |
  +-----------+-----------+-----------+
G |     9     | 7         |           |
H |           | 9   1     |         5 |
I |     4     |         6 | 9         |
  +-----------+-----------+-----------+
                              steps:209
*/

void sudoku_board(unsigned char *s, unsigned char b[82])
{
  int c;
  unsigned char line[128];
  
  *s='\0';

  strcat(s, "    A   B   C   D   E   F   G   H   I  \n");
  for(c = 0; c < 81; c++) {
    if(c > 0 && c % 9 == 0) {
      strcat(s,"|\n");
    }
    if(c % 27 == 0)
      strcat(s,"  +-----------+-----------+-----------+\n");
    if(c % 9 == 0) {
      sprintf(line,"%c ",'A' + c / 9);
      strcat(s, line);
    }
    if(c % 3 == 0) {
      strcat(s, "|");
    } else strcat(s, " ");
    if(b[c] != ' ' && b[c] != '0')
      sprintf(line," %c ", b[c]);
    else
      sprintf(line,"   ");
    strcat(s, line);
  }
  strcat(s, "|\n  +-----------+-----------+-----------+");
  strcat(s, "\n");
}

/*
  ABC DEF GHI 
 +---+---+---+
A|   |   |   |
B|   | 26|   |
C|8 7|13 |6 2|
 +---+---+---+
D|7  |  3|1  |
E| 52|6 7|9  |
F|   |   |   |
 +---+---+---+
G|3  |7 8|49 |
H|4 9|   |  7|
I|   |  1| 36|
 +---+---+---+
 */
void sudoku_board2(char *s, unsigned char b[82])
{
  int c;
  unsigned char line[128];
  
  *s='\0';

  strcat(s, "  ABC DEF GHI \n");
  for(c = 0; c < 81; c++) {
    if(c > 0 && c % 9 == 0) {
      strcat(s,"|\n");
    }
    if(c % 27 == 0)
      strcat(s, " +---+---+---+\n");
    if(c % 9 == 0) {
      sprintf(line,"%c",'A' + c / 9);
      strcat(s, line);
    }
    if(c % 3 == 0) {
      strcat(s, "|");
    }
    if(b[c] != ' ' && b[c] != '0')
      sprintf(line,"%c", b[c]);
    else
      sprintf(line," ");
    strcat(s, line);
  }
  strcat(s, "|\n +---+---+---+");
  strcat(s, "\n");
}

/*
  ABCDEFGHI 
 +---------+
A|573842619|
B|219675348|
C|684319275|
D|821453967|
E|937168524|
F|456297831|
G|145936782|
H|392781456|
I|768524193|
 +---------+
  */
void sudoku_board3(unsigned char *s, unsigned char b[82])
{
  int c;
  unsigned char line[128];
  
  *s='\0';

  strcat(s, "  ABCDEFGHI \n");
  strcat(s," +---------+\n");
  for(c = 0; c < 81; c++) {
    if(c > 0 && c % 9 == 0) {
      strcat(s,"|\n");
    }
    if(c % 9 == 0) {
      sprintf(line,"%c",'A' + c / 9);
      strcat(s, line);
    }
    if(c % 9 == 0) {
      strcat(s, "|");
    }
    if(b[c] != ' ' && b[c] != '0')
      sprintf(line,"%c", b[c]);
    else
      sprintf(line," ");
    strcat(s, line);
  }
  strcat(s, "|\n +---------+");
  strcat(s, "\n");
}

/*
  ABCDEFGHI
 +---------
A|695217483
B|138564792
C|724893156
D|572946318
E|463185279
F|981732645
G|357429861
H|846351927
I|219678534
 */
void sudoku_board4(unsigned char *s, unsigned char b[82])
{
  int c;
  unsigned char line[128];
  
  *s='\0';

  strcat(s, "  ABCDEFGHI\n");
  strcat(s," +---------\n");
  for(c = 0; c < 81; c++) {
    if(c > 0 && c % 9 == 0)
      strcat(s,"\n");

    if(c % 9 == 0) {
      sprintf(line,"%c",'A' + c / 9);
      strcat(s, line);
    }
    if(c % 9 == 0)
      strcat(s, "|");

    if(b[c] != ' ' && b[c] != '0')
      sprintf(line,"%c", b[c]);
    else
      sprintf(line," ");
    strcat(s, line);
  }
  strcat(s, "\n");
}

/*
 ABCDEFGHI 
A287935641
B615482379
C943761258
D324519867
E596874123
F178623495
G432197586
H761258934
I859346712
 */
void sudoku_board5(unsigned char *s, unsigned char b[82])
{
  int c;
  unsigned char line[128];
  
  *s='\0';

  strcat(s, " ABCDEFGHI \n");
  for(c = 0; c < 81; c++) {
    if(c > 0 && c % 9 == 0)
      strcat(s,"\n");

    if(c % 9 == 0) {
      sprintf(line,"%c",'A' + c / 9);
      strcat(s, line);
    }

    if(b[c] != ' ' && b[c] != '0')
      sprintf(line,"%c", b[c]);
    else
      sprintf(line," ");

    strcat(s, line);
  }
  strcat(s, "\n");
}

/*
435189627
691275384
872436591
349852176
168947235
257613849
914368752
723591468
586724913
 */
void sudoku_board6(unsigned char *s, unsigned char b[82])
{
  int c;
  unsigned char line[128];
  
  *s='\0';

  for(c = 0; c < 81; c++) {
    if(c > 0 && c % 9 == 0) {
      strcat(s,"\n");
    }
    if(b[c] != ' ' && b[c] != '0')
      sprintf(line,"%c", b[c]);
    else
      sprintf(line," ");
    strcat(s, line);
  }
  strcat(s, "\n");
}

/*
AA5 AB28 AC3 AD1 AE9 AF278 AG4 AH367 AI678
BA6 BB248 BC7 BD23 BE23 BF28 BG389 BH1 BI589
CA138 CB9 CC13 CD367 CE4 CF5 CG2 CH367 CI678
DA13479 DB14567 DC2 DD69 DE16 DF149 DG369 DH8 DI4679
EA1479 EB1467 EC1469 ED269 EE8 EF3 EG5 EH2679 EI24679
FA3489 FB468 FC3469 FD5 FE7 FF249 FG369 FH2369 FI1
GA12479 GB12457 GC1459 GD8 GE1235 GF6 GG19 GH259 GI259
HA1279 HB3 HC1569 HD279 HE125 HF1279 HG1689 HH4 HI25689
IA129 IB1256 IC8 ID4 IE125 IF129 IG7 IH2569 II3
 */
void sudoku_board10(unsigned char *s, unsigned char b[82])
{
  int c, first;
  unsigned char choices[10], line[128];
  
  *s='\0';
  first = 1;
  for(c = 0; c < 81; c++) {
    if(c > 0 && c % 9 == 0) {
      strcat(s,"\n");
      first = 1;
    }
    if(!first)
      strcat(s, " ");
    sprintf(line,"%c%c",'A' + c / 9, 'A' + c % 9);
    strcat(s, line);

    if(b[c] >= '1' && b[c] <= '9') {
      unsigned char digit[3];
      digit[0] = b[c];
      digit[1] = '\0';
      strcat(s, digit);
    } else {
      sudoku_get_choices(b, c, choices);
      strcat(s, choices);
    }
    first = 0;
  }
  strcat(s, "\n");
}

/*
    7   37     1 88     95  78  1 4 21      595 6   3 4  23     6   7 1      5 9 
 */
void sudoku_board11(unsigned char *s, unsigned char b[82])
{
  int c;
  unsigned char digit[3];

  *s='\0';
  for(c = 0; c < 81; c++) {
    if(b[c] >= '1' && b[c] <= '9') {
      digit[0] = b[c];
      digit[1] = '\0';
      strcat(s, digit);
    } else
      strcat(s, " ");
  }
  strcat(s, "\n");
}

/*
'AA' = "4", 'AB' = "9", 'AE' = "7", 'BA' = "5", 'CB' = "8", 'CE' = "2", 'CF' = "4", 'CG' = "3", 'DC' = "9", 'DE' = "4", 'DH' = "6", 'EB' = "6", 'EE' = "5", 'FD' = "8", 'FF' = "3", 'FH' = "1", 'GA' = "3", 'GC' = "4", 'GE' = "9", 'GI' = "2", 'HC' = "6", 'HG' = "8", 'IA' = "9", 'IB' = "5", 'ID' = "2", 'IF' = "6", 'II' = "3"
 */
void sudoku_board20(unsigned char *s, unsigned char b[82])
{
  int c, first = 1;
  unsigned char column[128], digit[3];

  *s='\0';
  for(c = 0; c < 81; c++) {
    if(b[c] >= '1' && b[c] <= '9') {
      column[0] = '\0';
      if(!first)
	strcat(column, ", ");
	
      sprintf(column + strlen(column), "'%c%c'",'A' + c / 9, 'A' + c % 9);
      digit[0] = b[c];
      digit[1] = '\0';
      strcat(column, " = \"");
      strcat(column, digit);
      strcat(column, "\"");
      first = 0;
      strcat(s, column);
    }
  }
  strcat(s, "\n");
}

/*
'AA' = "278", 'AB' = "1", 'AC' = "2358", 'AD' = "29", 'AE' = "278", 'AF' = "4", 'AG' = "237", 'AH' = "2357", 'AI' = "6", 'BA' = "24678", 'BB' = "245678", 'BC' = "2458", 'BD' = "26", 'BE' = "2678", 'BF' = "3", 'BG' = "247", 'BH' = "1", 'BI' = "9", 'CA' = "9", 'CB' = "23467", 'CC' = "234", 'CD' = "126", 'CE' = "5", 'CF' = "267", 'CG' = "2347", 'CH' = "8", 'CI' = "37", 'DA' = "28", 'DB' = "289", 'DC' = "7", 'DD' = "23569", 'DE' = "1", 'DF' = "2569", 'DG' = "2389", 'DH' = "4", 'DI' = "35", 'EA' = "5", 'EB' = "2489", 'EC' = "6", 'ED' = "239", 'EE' = "237", 'EF' = "279", 'EG' = "123789", 'EH' = "2379", 'EI' = "137", 'FA' = "3", 'FB' = "29", 'FC' = "129", 'FD' = "259", 'FE' = "4", 'FF' = "8", 'FG' = "6", 'FH' = "2579", 'FI' = "157", 'GA' = "478", 'GB' = "345789", 'GC' = "34589", 'GD' = "345", 'GE' = "3", 'GF' = "1", 'GG' = "379", 'GH' = "6", 'GI' = "2", 'HA' = "1267", 'HB' = "235679", 'HC' = "12359", 'HD' = "8", 'HE' = "236", 'HF' = "256", 'HG' = "1379", 'HH' = "379", 'HI' = "4", 'IA' = "1246", 'IB' = "2346", 'IC' = "1234", 'ID' = "7", 'IE' = "9", 'IF' = "26", 'IG' = "5", 'IH' = "3", 'II' = "8"
 */
void sudoku_board21(unsigned char *s, unsigned char b[82])
{
  int c, first = 1;
  unsigned char column[128], digit[3], choices[10];

  *s='\0';
  for(c = 0; c < 81; c++) {
    column[0] = '\0';
    if(!first)
      strcat(column, ", ");
	
    sprintf(column + strlen(column), "'%c%c' = ", 'A' + c / 9, 'A' + c % 9);

    if(b[c] >= '1' && b[c] <= '9') {
      digit[0] = b[c];
      digit[1] = '\0';
      strcat(column, "\"");
      strcat(column, digit);
      strcat(column, "\"");
    } else {
      sudoku_get_choices(b, c, choices);
      strcat(column, "\"");
      strcat(column, choices);
      strcat(column, "\"");
    }
    first = 0;
    strcat(s, column);
  }
  strcat(s, "\n");
}

void sudoku_print(unsigned char *b, int board)
{
  unsigned char string[2048];

  string[0] = '\0';
  
  /* zero -> no board */
  if(board == 1) 
    sudoku_board(string, b);
  else if(board == 2) 
    sudoku_board2(string, b);
  else if(board == 3) 
    sudoku_board3(string, b);
  else if(board == 4) 
    sudoku_board4(string, b);
  else if(board == 5) 
    sudoku_board5(string, b);
  else if(board == 6) 
    sudoku_board6(string, b);
  else if(board == 10) 
    sudoku_board10(string, b);
  else if(board == 11) 
    sudoku_board11(string, b);
  else if(board == 20) 
    sudoku_board20(string, b);
  else if(board == 21) 
    sudoku_board21(string, b);

  fprintf(stdout,"%s", string);
}

void sudoku_get_all_choices(unsigned char *b, char choices[81][10]) {
  int c;
  
  for(c = 0; c < 81; c++) {
    choices[c][0] = '\0';
    if(*(b + c) == ' ') {
      sudoku_get_choices(b, c, choices[c]);
    } else {
      choices[c][0] = *(b + c);
      choices[c][1] = '\0';
    }
  }
}


int sudoku_full(unsigned char b[82])
{
  for(int c = 0; c < 81; c++) {
    if(b[c] == ' ')
      return(0);
  }
  return(1);
}

void sudoku_clear(unsigned char b[82])
{
  int c;
  for(c = 0; c < 81; c++) {
    b[c] = ' ';
  }
  b[c] = '\0';
}

void sudoku_cleargrid(unsigned char b[82], unsigned char grid[82], char choice)
{
  int c;

  for(c = 0; c < 81; c++) {
    if(grid[c] == choice)
      b[c] = ' ';
  }
}


int sudoku_isallowed(unsigned char b[82], int i, int j, int e)
{
  int k, l, base;

  for(k = 0; k < 9; k++) {
    if(b[i * 9 + k] == e)
      return(0);
  }
  for(k = 0; k < 9; k++) {
    if(b[9 * k + j] == e)
      return(0);
  }
  base = i / 3 * 27 + j / 3 * 3;
  for(k = 0; k < 3; k++) {
    for(l = 0; l < 3; l++) {
      if(b[base + k * 9 + l] == e)
	return(0);
    }
  }
  return(1);
}

int sudoku_verbose = 0;
int simple_verbose = 0, simple_output = 0, simple_count = 0;
int solve_verbose = 0;
int simple_rounds = 0, save_rounds = 0, save_steps = 0;
int simple_errors = 0;
unsigned char newb[82];

#define aDEBUG32 2

int sudoku_simplesolve1(unsigned char b[82])
{
  int c, changes = 0;
  unsigned char choices[10];

  for(c = 0; c < 81; c++) {
    if(newb[c] == ' ') { // empty cell
      choices[0] = '\0';

      sudoku_get_choices(newb, c, choices);

      int lenchoices = strlen(choices);
      if(lenchoices == 0) {
	break;
      } else if(lenchoices == 1) {
	if(simple_verbose) {
#ifdef DEBUG32
	  fprintf(stdout,"simplesolve1");
#endif
	  fprintf(stdout," %c%c%c", c / 9 + 'A', c % 9 + 'A', *choices);
#ifdef DEBUG32
	  fprintf(stdout,"\n");
	  sudoku_print(newb, 1);
#endif
	}
	newb[c] = *choices;
	if(simple_verbose) {
	  simple_count++;
	  simple_output = 1;
	}
#ifdef DEBUG32
	if(simple_verbose) {
	  fprintf(stdout,"\n");
	  sudoku_print(newb, 1);
	}
#endif
	changes++;
      } // end of if(strlen(choices)
    } // end of if(b[c] == ' ') { // empty cell
  } // end of for(c

  return(changes);
}

#define aDEBUG34 2

int sudoku_simplesolve2(unsigned char b[82]) {
  int c, i, j, count, cell, changes = 0;
  
  for(c = '1'; c <= '9'; c++) { // digits
    for(i = 0; i < 9; i++) { // rows
      count = 0;
      cell = -1;
      for(j = 0; j < 9; j++) { // columns
	if(newb[i * 9 + j] == ' ' && sudoku_isallowed(newb, i, j, c) == 1) {
	  count++;
	  cell = i * 9 + j;
	}
      }
      if(count == 1 && cell != -1 && newb[cell] == ' ') {
	if(simple_verbose) {
#ifdef DEBUG34
	  fprintf(stdout,"simplesolve2: rows");
#endif
	  fprintf(stdout," %c%c%c", cell / 9 + 'A', cell % 9 + 'A', c);
#ifdef DEBUG34
	  fprintf(stdout,"\n");
	  sudoku_print(newb, 1);
#endif
	}
	newb[cell] = c;
	if(simple_verbose) {
	  simple_count++;
	  simple_output = 1;
	}
#ifdef DEBUG34
	if(simple_verbose) {
	  fprintf(stdout,"\n");
	  sudoku_print(newb, 1);
	}
#endif
	changes++;
	//sudoku_get_all_choices(b, choices);
      }
    }
  }

  return(changes);
}

#define aDEBUG36 2

int sudoku_simplesolve3(unsigned char b[82]) {
  int c, i, j, count, cell, changes = 0;
  char choices[81][10];
  
  sudoku_get_all_choices(b, choices);

  for(c = '1'; c <= '9'; c++) { // digits
    for(j = 0; j < 9; j++) { // columns
      count = 0;
      cell = -1;
      for(i = 0; i < 9; i++) { // rows
	if(newb[i * 9 + j] == ' ' && sudoku_isallowed(newb, i, j, c) == 1) {
	  count++;
	  cell = i * 9 + j;
	}
      }
      if(count == 1 && cell != -1 && newb[cell] == ' ') {
	if(simple_verbose) {
#ifdef DEBUG36
	  fprintf(stdout,"simplesolve3: columns");
#endif
	  fprintf(stdout," %c%c%c", cell / 9 + 'A', cell % 9 + 'A', c);
	  simple_output = 1;
#ifdef DEBUG36
	  fprintf(stdout,"\n");
	  sudoku_print(newb, 1);
#endif
	}
	newb[cell] = c;
	simple_count++;

#ifdef DEBUG36
	if(simple_verbose) {
	  fprintf(stdout,"\n");
	  sudoku_print(newb, 1);
	}
#endif
	changes++;
	//sudoku_get_all_choices(b, choices);
      }
    }
  }

  return(changes);
}

#define aDEBUG38 2

int sudoku_simplesolve4(unsigned char b[82]) {
  int c, i, j, basei, basej, count, cell, changes = 0;
  
  for(basei = 0; basei < 9; basei += 3) {
    for(basej = 0; basej < 9; basej += 3) {
      for(c = '1'; c <= '9'; c++) { // digits
	count = 0;
	cell = -1;
	for(i = 0; i < 3; i++) {
	  for(j = 0; j < 3; j++) {
#ifdef KOK
	    fprintf(stdout,"basei:%d", basei);
	    fprintf(stdout," basej:%d", basej);
	    fprintf(stdout," i:%d", i);
	    fprintf(stdout," j:%d", j);
	    fprintf(stdout," basei+i:%d", basei + i);
	    fprintf(stdout," basej+j:%d", basej + j);
	    fprintf(stdout," c:%d", c);
	    fprintf(stdout," cell:%d", cell);
	    fprintf(stdout,"\n");
#endif
	    if(newb[(basei + i) * 9 + basej + j] == ' ' && sudoku_isallowed(newb, basei + i, basej + j, c) == 1) {
	      cell = (basei + i) * 9 + j + basej;
	      count++;
	    }
	  }
	}
	if(count == 1 && cell != -1 && newb[cell] == ' ') {
	  if(simple_verbose) {
#ifdef DEBUG38
	    fprintf(stdout,"simplesolve4: boxes");
#endif
	    fprintf(stdout," %c%c%c", cell / 9 + 'A', cell % 9 + 'A', c);
#ifdef DEBUG38
	    fprintf(stdout,"\n");
	    sudoku_print(newb, 1);
#endif
	  }
	  newb[cell] = c;
	  
	  if(simple_verbose) {
	    simple_count++;
	    simple_output = 1;
	  }
#ifdef DEBUG38
	  if(simple_verbose) {
	    fprintf(stdout,"\n");
	    sudoku_print(newb, 1);
	  }
#endif
	  changes++;
	  //sudoku_get_all_choices(b, choices);
	} // end of if(count
      } // end of for(c
    } // end of for(basej
  } // end of for(basei

  return(changes);
}

int simple1 = 1, simple2 = 1, simple3 = 1, simple4 = 1;

int sudoku_simplesolve(unsigned char *b)
{
  int changes, changes2 = 0;

  simple_count = 0;
  simple_rounds = 0;
  simple_errors = 0;
  
  changes = 1;
  while(simple_errors == 0 && changes) {

    memcpy(newb, b, sizeof(newb));
    
    changes = 0;

    if(simple1)
      changes += sudoku_simplesolve1(b);
    if(simple2)
      changes += sudoku_simplesolve2(b);
    if(simple3)
      changes += sudoku_simplesolve3(b);
    if(simple4)
      changes += sudoku_simplesolve4(b);
    
    changes2 += changes;
    simple_rounds++;

    memcpy(b, newb, sizeof(newb));
  }
  if(simple_verbose && simple_output) {
    fprintf(stdout,", moves:%d\n", simple_count);
    simple_output = 0;
  }
  return(changes2);
}

int sudoku_getnextemptycell(char *b, int *i, int *j)
{
  int i2, j2;
  
  for(i2 = 0; i2 < 9; i2++) {
    for(j2 = 0; j2 < 9; j2++) {
      if(*(b + i2 * 9 + j2) == ' ') {
	*i = i2;
	*j = j2;
	return(1);
      }
    }
  }
  *i = -1;
  *j = -1;
  return(0);
}

int sudoku_boardsmatch(unsigned char *board, unsigned char *board2)
{
  int c, ok;

  ok = 1;
  for(c = 0;c < 81; c++) {
    if(*(board + c) != ' ' &&
       *(board2 + c) != ' ' &&
       *(board + c) != *(board2 + c)) {
      ok = 0;
      break;
    }
  }
  return(ok);
}

int solve_steps = 0, solve_exact = -1, solve_greater = -1;
int rounds_greater = -1;

int solve_usesimple = 1;

#define SOLVE_QUICK_EXIT 2 // on for now

#define aDEBUG57 2

int solve_quick_exit = 1;
int solve_calculate_steps = 0;

unsigned long long solutions;

unsigned char prevsolution[82];

int sudoku_solve2(unsigned char *sudoku, unsigned char *solution, unsigned long long *solutions)
{
  int c, i, j;
  
  if(sudoku_getnextemptycell(sudoku, &i, &j) == 0) {
    (*solutions)++;
    if(solve_verbose) {
      if(*solutions % 1000000 == 0) {
	FILE *fp1;
	if((fp1 = fopen("newressusudokusolutions.txt", "a")) != NULL) {
	  fprintf(fp1,"%llu: %s\n", *solutions, sudoku);
	  fclose(fp1);
	}
      }
    }
    if(*solutions == 1)
      strcpy(solution, sudoku);

#ifdef SOLVE_QUICK_EXIT
    if(solve_quick_exit && *solutions > 1) // quick exit && multiple solutions, fail
      return(1);
    else
#endif
      return(0);
  }

  for(c = '1'; c <= '9'; c++) {
    if(sudoku_isallowed(sudoku, i, j, c) == 1) {
      char savesudoku[82];
      if(solve_usesimple)
	strcpy(savesudoku, sudoku);
      
      *(sudoku + i * 9 + j) = c;
#ifdef DEBUG57
      if(solve_verbose)
	fprintf(stdout,"%s\n", sudoku);
#endif      
      if(solve_usesimple)
	sudoku_simplesolve(sudoku);

#ifdef OLD1
      solve_steps++;
#endif

      if(solve_verbose)
	fprintf(stdout,"%s",sudoku);
      if(solve_calculate_steps && sudoku_boardsmatch(sudoku, prevsolution)) {
	solve_steps++;
	if(solve_verbose)
	  fprintf(stdout,"*****");
      }
      if(solve_verbose)
	fprintf(stdout,"\n");

#ifdef DEBUG57
      if(solve_verbose)
	fprintf(stdout,"%s\n", sudoku);
#endif
      if(sudoku_solve2(sudoku, solution, solutions) == 1)
	return(1);

      if(solve_usesimple)
	strcpy(sudoku, savesudoku);

      *(sudoku + i * 9 + j) = ' ';
    }
  }      
  return(0);
}

int sudoku_solve(unsigned char *sudoku)
{
  unsigned char tsudoku[82], solution[82];

  solve_steps = 0;
  solutions = 0;
  if(solve_verbose)
    fprintf(stdout,"%s\n",prevsolution);
  memcpy(tsudoku, sudoku, sizeof(tsudoku));
  sudoku_simplesolve(tsudoku);
  sudoku_solve2(tsudoku, solution, &solutions);
  if(solutions == 1) { // single solution
    memcpy(sudoku, solution, sizeof(solution));
    if(solve_verbose) {
      fprintf(stdout,"%s\n", sudoku);
    }
    return(1);
  }
  return(0);
}

unsigned char items[6][3] = {
  {  0,1,2 },
  { 0,2,1 },
  { 1,0,2 },
  { 1,2,0 },
  { 2,0,1 },
  { 2,1,0 }
};

void sudoku_mix(unsigned char *sudoku)
{
  int c, d, e, f, g, h, temp;

  for(c = 0; c < 3; c++) { // from stacks (bands) 0 - 3

    d = newressu_gen_limit(3); // to stack (band)) 0 - 3
    e = newressu_gen_limit(6); // order of columns (rows) in stack (band)

    for(f = 0; f < 3; f++) { // order of columns (rows) in from stack (band)
      g = items[e][f]; // order of columns (rows) in stack (band)

      for(h = 0; h < 9; h++) { // swap columns in stack

	temp = *(sudoku + c * 3 + f + h * 9);
	*(sudoku + c * 3 + f + h * 9) = *(sudoku + d * 3 + g + h * 9);
	*(sudoku + d * 3 + g + h * 9) = temp;

      }

      for(h = 0; h < 9; h++) { // swap rows in band

	temp = *(sudoku + c * 27 + f * 9 + h);
	*(sudoku + c * 27 + f * 9 + h) = *(sudoku + d * 27 + g * 9 + h);
	*(sudoku + d * 27 + g * 9 + h) = temp;

      }
    }
  }
  
}

int sudoku_makegrid(unsigned char *b, unsigned char *grid, char choice, unsigned int count)
{
  int c, cell, digit;
  unsigned char choices[10];
  
  for(c = 0; c < count; c++) {
    for(;;) {

       // get cell number from pseudoressutwist
      cell = newressu_gen_limit(81);

      if(*(grid + cell) != choice)
	continue;

      if(*(b + cell) != ' ') // filled cell
	continue;

      sudoku_get_choices(b, cell, choices);

      int choiceslen = strlen(choices);
      if(choiceslen == 0)
	return(1); // no choices, fail
      else if(choiceslen == 1)
	digit = choices[0]; // one choice only, use it
      else
	digit = choices[newressu_gen_limit(choiceslen)]; // multiple choices, get digit choise from pseudoressu
      
      *(b + cell) = digit;
      break;
    } // end of for(;;)
  } // end of for(c = 0; c
  
  return(0); // ok
}

#define SUDOKU_PHASE2_ROUNDS 30 // avgtries 2156

long long sumtries = 0, counttries = 0;
int mode = 0, gmode = 1;
int mix = 0;
unsigned char *sudokuesfile = "newressusudoku.skk";
unsigned char *sudokuesdb = "newressusudoku.dbskk";

int sudoku_make(unsigned char *b, unsigned char *s, unsigned char *m)
{
  int tries = 0, changes = 0, ok;
  unsigned char board[82], solution[82];

  for(;;) {

    prevsolution[0] = 0;
    solve_quick_exit = 1;
    solutions = 0;
    save_rounds = 0;
    save_steps = 0;

#define aDEBUG63 2
  
#ifdef DEBUG63
    fprintf(stdout,".");
    sudoku_output = 1;
    fflush(stdout);
#endif

    sudoku_clear(board);
    tries++;
    
    if(gmode == 0) {
      if(tries % SUDOKU_PHASE2_ROUNDS == 0) {
	ok = 0;
	while(!ok) {
	  ok = 1;
	  sudoku_clear(board);
	  if(sudoku_makegrid(board, sudoku_grid1, '1',  digitsa) == 1)
	    ok = 0;
	}
      }
      if(sudoku_makegrid(board, sudoku_grid1, '2', digitsb) == 1) {
	sudoku_cleargrid(board, sudoku_grid1, '2');
	continue;
      }
    } else if(gmode == 1) {
      ok = 0;
      while(!ok) {
	ok = 1;
	sudoku_clear(board);
	if(sudoku_makegrid(board, sudoku_grid1, '1',  digitsa) == 1)
	  ok = 0;
      }
    }

    *m = '\0';
    if(mix) {
      strcpy(m, board);
      sudoku_mix(board);
    }    

    memcpy(b, board, sizeof(board));

    if(mode == 0) { // easy
      sudoku_simplesolve(b);
      save_rounds = simple_rounds;
    } else if(mode == 1) { // hard
      changes = sudoku_simplesolve(b);
      save_rounds = simple_rounds;
      if(changes > 1 && changes < 10 && !sudoku_full(b))
	sudoku_solve(b);
      else
	continue;

    } else if(mode == 2) { // harder
      changes = sudoku_simplesolve(b);
      save_rounds = simple_rounds;
      if(changes == 1 && !sudoku_full(b))
	sudoku_solve(b);
      else
	continue;
      
    } else if(mode == 3) { // hardest
      if((changes = sudoku_simplesolve(b)) == 0 &&
	 !sudoku_full(b)) {
	save_rounds = simple_rounds;
	sudoku_solve(b);
      } else
	continue;

    } else if(mode == 10) { // solve only
      solve_quick_exit = 1;
      sudoku_solve(b);
      memcpy(prevsolution, b, sizeof(prevsolution));
    }

    if(solutions > 1)
      continue;
    
    if(!sudoku_full(b))
      continue;

    if(rounds_greater != -1 &&
       save_rounds <= rounds_greater)
      continue;

    if(solve_exact != -1 ||
       solve_greater != -1) {

      // calculate solve steps using full
      // solve
      solve_calculate_steps = 1;
      solve_quick_exit = 0;
      memcpy(b, board, sizeof(board));
      sudoku_solve(b);
      save_steps = solve_steps;

      if((solve_exact != -1 &&
	  solve_exact != solve_steps) ||
	 (solve_greater != -1 &&
	  save_steps <= solve_greater) )
	continue;
    }

    memcpy(solution, b, sizeof(solution));

    break;
  }

  sumtries += tries;
  counttries++;
  
  memcpy(b, board, sizeof(board));
  memcpy(s, solution, sizeof(solution));

  int digits = 0;
  unsigned char cboard[82], *p;
  p = cboard;
  for(int c = 0; c < sizeof(board); c++) {
    if(board[c] >= '1' && board[c] <= '9') {
      *p++ = board[c];
      digits++;
    }
  }
  int empty = 81 - digits;
  
#ifdef OUTPUTDB

  unsigned char temp[16];
  db8_set_filename(sudokuesdb);
  db8_put_element("sudoku", 0, "board", 0, b);
  db8_put_element("sudoku", 0, "solution", 0, s);
  db8_put_element("sudoku", 0, "cboard", 0, cboard);
  sprintf(temp,"%d",digits);
  db8_put_element("sudoku", 0, "digits", 0, temp);
  sprintf(temp,"%d",empty);
  db8_put_element("sudoku", 0, "empty", 0, temp);
  if(gmode == 0) {
    sprintf(temp,"%d", digitsa);
    db8_put_element("sudoku", 0, "digitsa", 0, temp);
    sprintf(temp,"%d", digitsb);
    db8_put_element("sudoku", 0, "digitsb", 0, temp);
  } else {
    sprintf(temp,"%d", digitsa);
    db8_put_element("sudoku", 0, "digitsa", 0, temp);
  }
  sprintf(temp,"%d", mode);
  db8_put_element("sudoku", 0, "mode", 0, temp);
  sprintf(temp,"%d", gmode);
  db8_put_element("sudoku", 0, "gmode", 0, temp);
  db8_save("sudoku");
  db8_clear("sudoku");

#endif
#ifdef OUTPUTFILE

  FILE *fp1;
  if((fp1 = fopen(sudokuesfile, "a"))!=NULL) {
    *p = '\0';
    fprintf(fp1,"'board' = \"%s\"", b);
    fprintf(fp1,", 'solution' = \"%s\"", s);
    fprintf(fp1,", 'cboard' = \"%s\"", cboard);
    fprintf(fp1,", 'digits' = \"%d\"", digits);
    fprintf(fp1,", 'empty' = \"%d\"", empty);
    if(gmode == 0) {

      fprintf(fp1,", 'digitsa' = \"%d\"", digitsa);
      fprintf(fp1,", 'digitsb' = \"%d\"", digitsb);
    } else if(gmode == 1) {
      fprintf(fp1,", 'digitsa' = \"%d\"", digitsa);
    }
    fprintf(fp1,", 'mode' = \"%d\"", mode);
    fprintf(fp1,", 'gmode' = \"%d\"", gmode);
    fprintf(fp1,"\n");
    fclose(fp1);
  }

#endif

  return(0);
}

unsigned char *sudoku_fgets(int *linelen, unsigned char **line, FILE *fp1)
{
  int count, retval;
  unsigned char buffer[128];
  unsigned char *buf = *line;

  count = 0;

  if(buf != NULL)
    buf[0] = '\0';

  retval = 1;

  for(;;) {
    if(fgets(buffer, sizeof(buffer), fp1) == NULL) {
      retval = 0;
      break;
    }

    count = 0;
    if(buf != NULL)
      count += strlen(buf);
    count += strlen(buffer) + 1;
    
    if(*linelen < count) {
      unsigned char *prevbuf = buf;
      *linelen = count;
      buf = realloc(buf, *linelen);
      if(prevbuf == NULL)
	buf[0] = '\0';
    }
    
    strcat(buf, buffer);

    if(buf[strlen(buf)-1] == '\n') {
      buf[strlen(buf)-1] = '\0';
      break;
    }
  }
  *line = buf;

  if(retval == 0)
    return(NULL);
  else
    return(*line);
}

int help = 0, quiet = 0, psolution = 0, pboard = 1, avgtries = 0, sudokues = 1;

int main(int argc, char *argv[])
{
  int c;
  unsigned char board[82], sboard[82], tboard[82], mixboard[82];

  procname = argv[0];

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

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

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

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

      } else if(!strcmp("--avgtries", argv[c])) {
	avgtries = !avgtries;
	
      } else if(!strncmp("--sudokues", argv[c], 10)) {
	if(*(argv[c] + 10) != '\0') {
	  sudokues = atoi(argv[c] + 10);
	} else if(c + 1 < argc) {
	  sudokues = atoi(argv[c + 1]);
	  c++;
	}

      } else if(!strncmp("--solution", argv[c], 10)) {
	if(*(argv[c] + 10) != '\0') {
	  psolution = atoi(argv[c] + 10);
	} else if(c + 1 < argc && isdigit(*(argv[c + 1]))) {
	  psolution = atoi(argv[c + 1]);
	  c++;
	} else
	  psolution = !psolution;

      } else if(!strncmp("--board", argv[c], 7)) {
	if(*(argv[c] + 7) != '\0') {
	  pboard = atoi(argv[c] + 7);
	} else if(c + 1 < argc && isdigit(*(argv[c + 1]))) {
	  pboard = atoi(argv[c + 1]);
	  c++;
	} else 
	  pboard = !pboard;
	
      } else if(!strcmp("--verbose", argv[c])) {
	sudoku_verbose = !sudoku_verbose;

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

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

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

      } else if(!strncmp("--digits", argv[c], 8)) {
	if(*(argv[c] + 8) != '\0') {
	  digitsa = atoi(argv[c] + 8);
	} else if(c + 1 < argc) {
	  digitsa = atoi(argv[c + 1]);
	  c++;
	}
	digitsb = 0;

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

      } else if(!strncmp("--digitsb", argv[c], 9)) {
	if(*(argv[c] + 9) != '\0') {
	  digitsb = atoi(argv[c] + 9);
	} else if(c + 1 < argc) {
	  digitsb = atoi(argv[c + 1]);
	  c++;
	}
	
      } else if(!strncmp("--stepsexact", argv[c], 12)) {
	if(*(argv[c] + 12) != '\0') {
	  solve_exact = atoi(argv[c] + 12);
	} else if(c + 1 < argc) {
	  solve_exact = atoi(argv[c + 1]);
	  c++;
	}
	
      } else if(!strncmp("--stepsgreater", argv[c], 14)) {
	if(*(argv[c] + 14) != '\0') {
	  solve_greater = atoi(argv[c] + 14);
	} else if(c + 1 < argc) {
	  solve_greater = atoi(argv[c + 1]);
	  c++;
	}
	fprintf(stdout,"solve_greater:%d\n",solve_greater);

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

      } else if(!strcmp("--gmode1", argv[c]) ) {
	gmode = 1;
	
      } else if(!strcmp("--gmode0", argv[c]) ) {
	gmode = 0;
	
      } else if(!strcmp("--mode0", argv[c]) ) {
	mode = 0;
	
      } else if(!strcmp("--mode1", argv[c]) ) {
	mode = 1;
	
      } else if(!strcmp("--mode2", argv[c]) ) {
	mode = 2;
	
      } else if(!strcmp("--mode3", argv[c]) ) {
	mode = 3;
	
      } else if(!strcmp("--mode10", argv[c]) ) {
	mode = 10;
	
      } else if(!strcmp("--full", argv[c])) {
	sudoku_grid1 = full;
	gmode = 1;
	mode = 0; // easy

      } else if(!strcmp("--easy", argv[c])) {
	gmode = 1;
	mode = 0; // easy
	
      } else if(!strcmp("--easy38", argv[c])) {
	digitsa = 38;
	sudoku_grid1 = full;
	gmode = 1;
	mode = 0; // easy
	
      } else if(!strcmp("--easy1", argv[c])) {
	digitsa = 25;
	sudoku_grid1 = full;
	gmode = 1;
	mode = 0; // easy
	
      } else if(!strcmp("--easy2", argv[c])) {
	digitsa = 27;
	sudoku_grid1 = full;
	gmode = 1;
	mode = 0; // easy
	
      } else if(!strcmp("--easy3", argv[c])) {
	digitsa = 29;
	sudoku_grid1 = full;
	gmode = 1;
	mode = 0; // easy
	
      } else if(!strcmp("--easy4", argv[c])) {
	digitsa = 31;
	sudoku_grid1 = full;
	gmode = 1;
	mode = 0; // easy
	
      } else if(!strcmp("--easy38", argv[c])) {
	digitsa = 38;
	sudoku_grid1 = full;
	gmode = 1;
	mode = 0; // easy
	
      } else if(!strcmp("--easy41", argv[c])) {
	digitsa = 41;
	sudoku_grid1 = full;
	gmode = 1;
	mode = 0; // easy
	
      } else if(!strcmp("--easy45", argv[c])) {
	digitsa = 45;
	sudoku_grid1 = full;
	gmode = 1;
	mode = 0; // easy
	
      } else if(!strcmp("--hard", argv[c])) {
	sudoku_grid1 = full;
	gmode = 1;
	mode = 1; // hard
	
      } else if(!strcmp("--hard1", argv[c])) {
	digitsa = 23;
	sudoku_grid1 = full;
	gmode = 1;
	mode = 1; // hard
	
      } else if(!strcmp("--hard24", argv[c])) {
	digitsa = 24;
	sudoku_grid1 = full;
	gmode = 1;
	mode = 1; // hard
	
      } else if(!strcmp("--hard2", argv[c])) {
	digitsa = 26;
	sudoku_grid1 = full;
	gmode = 1;
	mode = 1; // hard
	
      } else if(!strcmp("--hard3", argv[c])) {
	digitsa = 29;
	sudoku_grid1 = full;
	gmode = 1;
	mode = 1; // hard
	
      } else if(!strcmp("--hard4", argv[c])) {
	digitsa = 33;
	sudoku_grid1 = full;
	gmode = 1;
	mode = 1; // hard
	
      } else if(!strcmp("--harder", argv[c])) {
	sudoku_grid1 = full;
	gmode = 1;
	mode = 2; // harder

      } else if(!strcmp("--harder1", argv[c])) {
	digitsa = 23;
	sudoku_grid1 = full;
	gmode = 1;
	mode = 2; // harder
	
      } else if(!strcmp("--harder5", argv[c])) {
	digitsa = 34;
	sudoku_grid1 = full;
	gmode = 1;
	mode = 2; // harder
	
      } else if(!strcmp("--hardest", argv[c])) {
	sudoku_grid1 = full;
	gmode = 1;
	mode = 3; // hardest
	
      } else if(!strcmp("--hardest1", argv[c])) {
	digitsa = 23;
	sudoku_grid1 = full;
	gmode = 1;
	mode = 3; // hardest
	
      } else if(!strcmp("--hardest2", argv[c])) {
	digitsa = 27;
	sudoku_grid1 = full;
	gmode = 1;
	mode = 3; // hardest
	
      } else if(!strcmp("--hardest3", argv[c])) {
	digitsa = 31;
	sudoku_grid1 = full;
	gmode = 1;
	mode = 3; // hardest
	
      } else if(!strcmp("--hardest4", argv[c])) {
	digitsa = 31;
	sudoku_grid1 = full;
	gmode = 1;
	mode = 3; // hardest
	
      } else if(!strcmp("--hardest5", argv[c])) {
	digitsa = 33;
	sudoku_grid1 = full;
	gmode = 1;
	mode = 3; // hardest
	
      } else if(!strcmp("--solve", argv[c])) {
	sudoku_grid1 = full;
	gmode = 1;
	mode = 10; // solve
	
      } else if(!strcmp("--cross1", argv[c])) {
	digitsa = 25;
	digitsb = 0;
	sudoku_grid1 = cross;
	gmode = 0;

      } else if(!strcmp("--cross2", argv[c])) {
	digitsa = 30;
	digitsb = 0;
	sudoku_grid1 = cross;
	gmode = 0;

      } else if(!strcmp("--cross3", argv[c])) {
	digitsa = 35;
	digitsb = 0;
	sudoku_grid1 = cross;
	gmode = 0;

      } else if(!strcmp("--cross4", argv[c])) {
	digitsa = 40;
	digitsb = 0;
	sudoku_grid1 = cross;
	gmode = 0;

      } else if(!strcmp("--cross5", argv[c])) {
	digitsa = 45;
	digitsb = 0;
	sudoku_grid1 = cross;
	gmode = 0;

      } else if(!strcmp("--cross", argv[c])) {
	sudoku_grid1 = cross;
	gmode = 0;

      } else if(!strcmp("--plus1", argv[c])) {
	digitsa = 25;
	digitsb = 10;
	sudoku_grid1 = plus;
	gmode = 0;
	
      } else if(!strcmp("--plus2", argv[c])) {
	digitsa = 30;
	digitsb = 10;
	sudoku_grid1 = plus;
	gmode = 0;
	
      } else if(!strcmp("--plus3", argv[c])) {
	digitsa = 35;
	digitsb = 10;
	sudoku_grid1 = plus;
	gmode = 0;
	
      } else if(!strcmp("--plus4", argv[c])) {
	digitsa = 40;
	digitsb = 10;
	sudoku_grid1 = plus;
	gmode = 0;
	
      } else if(!strcmp("--plus5", argv[c])) {
	digitsa = 45;
	digitsb = 10;
	sudoku_grid1 = plus;
	gmode = 0;
	
      } else if(!strcmp("--plus", argv[c])) {
	sudoku_grid1 = plus;
	gmode = 0;
	
      } else if(!strcmp("--slashes", argv[c])) {	
	sudoku_grid1 = slashes;
	gmode = 0;

      } else if(!strcmp("--slashes1", argv[c])) {
	digitsa = 23;
	digitsb = 0;	
	sudoku_grid1 = slashes;
	gmode = 0;
	
      } else if(!strcmp("--slashes2", argv[c])) {
	digitsa = 24;
	digitsb = 0;
	sudoku_grid1 = slashes;
	gmode = 0;

      } else if(!strcmp("--slashes3", argv[c])) {
	digitsa = 25;
	digitsb = 0;
	sudoku_grid1 = slashes;
	gmode = 0;

      } else if(!strcmp("--slashes4", argv[c])) {
	digitsa = 26;
	digitsb = 0;
	sudoku_grid1 = slashes;
	gmode = 0;

      } else if(!strcmp("--slashes5", argv[c])) {
	digitsa = 27;
	digitsb = 0;
	sudoku_grid1 = slashes;
	gmode = 0;

      } else if(!strcmp("--test3", argv[c])) {
	simple1 = 1;
	simple2 = 1;
	digitsa = 9;
	digitsb = 25;
	
      } else {
	fprintf(stderr,"%s: invalid option %s\n", procname, argv[c]);
	exit(1);
      }
    }
  } // end of for(c = 1; c < argc; c++)
  
  if(help) {
    fprintf(stderr,"%s: %s", procname, procname);
    fprintf(stderr," [--help]");
    fprintf(stderr," [--copyright]");
    fprintf(stderr," [--version]");
    fprintf(stderr," [--solution]");
    fprintf(stderr,"\n");
    exit(1);
  } // end of if(help)
  
  if(digitsa == -1)
    digitsa = 27;
  if(digitsb == -1)
    digitsb = 10;

  solve_quick_exit = 1;
  
#define aDEBUG90 2
  
#ifdef DEBUG90
unsigned char *empty_sudoku =
  "         "
  "         "
  "         "
  "         "
  "         "
  "         "
  "         "
  "         "
  "         ";
#ifdef KOK
  "123456789"
  "789123456"
  "456789123"
  "214365897"
  "365897214"
  "897214365"
  "531642978"
  "648971532"
  "972538641";
#endif
 
  fprintf(stdout,"Counting all solutions...\n");
  fflush(stdout);
  simple_verbose = 0;
  solve_quick_exit = 0;
  solve_verbose = sudoku_verbose;
  memcpy(tboard, empty_sudoku, sizeof(tboard));
  sudoku_solve(tboard);
  fprintf(stdout,", solutions:%lld", solutions);
  sudoku_grid1 = full;
  simple_verbose = 0;
  solve_verbose = 0;
  fprintf(stdout,", Done\n");
  fflush(stdout);
#endif
  
#define aDEBUG94 2
  
#ifdef DEBUG94
  unsigned char sudokuaietana[82] = "1    7 9  3  2   8  96  5    53  9   1  8   26    4   3      1  4      7  7   3  ";
  /*
    A   B   C   D   E   F   G   H   I  
  +-----------+-----------+-----------+
A | 1   2     |     9     |         4 |
B | 4         |         8 |     7     |
C | 8   3     | 2         |           |
  +-----------+-----------+-----------+
D | 6         |     2     |           |
E | 2         | 1   6     | 4   3   5 |
F |           |           | 7         |
  +-----------+-----------+-----------+
G |           |     8     | 2   4     |
H |           |           | 5       3 |
I |           |     4     |           |
  +-----------+-----------+-----------+
  s:0 steps:512 rounds:0 digits:25
  */
  unsigned char hard1[82] =
    "12  9   4"
    "4    8 7 "
    "83 2     "
    "6   2    "
    "2  16 435"
    "      7  "
    "    8 24 "
    "      5 3"
    "    4    ";
  unsigned char solution[82];

  sudoku_clear(solution);
  memcpy(board, hard1, sizeof(board));
  sudoku_print(board, pboard);

  simple_verbose = 1;
  memcpy(tboard, board, sizeof(board));
  sudoku_simplesolve(tboard);
  sudoku_print(tboard, pboard);

  simple_verbose = 0;
  solve_verbose = 1;
  memcpy(tboard, board, sizeof(board)); 
  solve_quick_exit = 0;
  solve_usesimple = 1;
  sudoku_solve(tboard);
  if(solutions > 1)
    fprintf(stdout,"multiple solutions\n");
  fprintf(stdout,"solvesteps:%d\n", solve_steps);
  sudoku_print(tboard, 1);
  fprintf(stdout,"**********\n");

#endif

  unsigned char simplesolution[82], solvesolution[82];
  unsigned char solvesolutions[82];
  int id = 0;
  
  for(int boards = 0; boards < sudokues; boards++) {

    solve_usesimple = 1;

    solve_quick_exit = 1;
    
    simple_verbose = 0;
    solve_verbose = 0;
    sudoku_clear(board);
    sudoku_clear(sboard);
    sudoku_make(board, sboard, mixboard);

    if(sudoku_output) {
      fprintf(stdout,"\n");
      fflush(stdout);
      sudoku_output = 0;
    }

    // simplesolve for verbose
    
    memcpy(tboard, board, sizeof(board));
    simple_verbose = sudoku_verbose;
    sudoku_simplesolve(tboard);
    memcpy(simplesolution, tboard, sizeof(simplesolution));
    save_rounds = simple_rounds;
    simple_verbose = 1; // *****

    // solve for verbose
    
    memcpy(tboard, board, sizeof(board));
    solve_verbose = sudoku_verbose;
    solve_usesimple = 1;
    solve_quick_exit = 0;
    simple_verbose = 0;
    sudoku_solve(tboard);
    memcpy(solvesolution, tboard, sizeof(solvesolution));
    save_steps = solve_steps;
    solve_verbose = sudoku_verbose;

    // solve without usesimple
    
    solve_usesimple = 0;
    simple_verbose = 0;

    sudoku_solve(tboard);
    memcpy(solvesolutions, tboard, sizeof(solvesolution));
    
    if(mix && sudoku_verbose)
      sudoku_print(mixboard, pboard);
    
    sudoku_print(board, pboard);

    int digits = 0;
    for(c = 0;c < sizeof(board); c++)
      if(board[c] >= '0' && board[c] <= '9')
	digits++;
    
    fprintf(stdout,"  s:%d", id);
#ifdef SOLVE_QUICK_EXIT
    fprintf(stdout," steps:%d", save_steps);
#endif
    fprintf(stdout," rounds:%d", save_rounds);
    fprintf(stdout," digits:%d", digits);
    fprintf(stdout,"\n");
    if(psolution) {;
      sudoku_print(solvesolution, psolution);
      fprintf(stdout,"  s:%d", id);
      fprintf(stdout,"\n");
    }
    id++;
    
    //if(sudoku_verbose) {
    
    solve_verbose = sudoku_verbose;
    
    if(sudoku_boardsmatch(board, simplesolution) &&
       sudoku_boardsmatch(board, solvesolution) &&

       sudoku_boardsmatch(board, solvesolutions) &&

       sudoku_boardsmatch(simplesolution, solvesolution) &&
       sudoku_full(solvesolution)) {
    } else {
      fprintf(stdout,"board:    %s\n", board);
      fprintf(stdout,"simple:   %s\n", simplesolution);

      fprintf(stdout,"solvewsi: %s\n", solvesolutions);

      fprintf(stdout,"solve:    %s\n", solvesolution);
      fprintf(stdout,"board and solutions differ\n");
    }

    if(sudoku_verbose)
      fprintf(stdout,"solvesteps:%d\n", save_steps);
    fflush(stdout);
  }
  if(avgtries)
    fprintf(stdout,"avgtries:%f\n",(double) sumtries / counttries);
  return(0);
}

Suurin osa tästä postista on ollut exfat debukkailua ja satunnaisuus testiajoja, mutta taas kerran tuntuu siltä että “pasianssi” menee läpi. Eli että nuo db_get ja db_put rutiinit toimivat tosiaan sovelluksen koodauksessa. Tällä hetkellä mielessäni on idea että sovellukseen tarvittavat kyselyt ja rivit nimetään sovelluksen “sisäisillä” nimillä (header, lines, headerfields, linesfields, session, log jne…) ja niille lisätään järjestysnumero. Järjestysnumerolla voidaan lukea useampirivisen kyselyn halutun rivin kenttä. Esimerkiksi tilausrivit, taulukon sarakkeiden nimet, otsakkeen kenttien nimet. Onkohan jo aika 🍾&🥂, aika näyttää…

Olen miettinyt, että tästä saattaisi olla mahdollista tehdä vielä yksi uusi ammatti (/ansaintatapa), ja kyselenkin, koetteko saavanne näiden artikkeleiden lukemisesta niin paljon arvoa että voitte tukea niitä. Lahjoituksen suomessa voi tehdä, mutta ei voi sitä pyytää. Yritän keksiä lahjoitukselle jonkin vastalahjan. Tällä hetkellä ajattelen muistitikkua tai dvd-levyä, jolla on lähdekoodeja. Lahjoitus kattaa tämän ja edellisten artikkeleiden viihdearvon. Arvon, jota ajattelen raporttien voivan antaa ovat tietysti henkilökohtaisia. Päällisimpänä ajattelen viihdearvoa, seuraavina uutuusarvo, käyttöarvo, julkistusarvo jne… Toisaalta joku voi ajatella tämän arvoa hyvänä hack:inä (vanha merkitys, nykyinen termi ~eettinen hack). Lahjoitussumman ajattelin korvaavan levyjen valmistuksen kopiointikulut, hintaa en voi vielä miettiä hmmm? (Haaga-Helia Laiho, n.1997-1998). Toinen vaihtoehto on tietysti miettiä summaa, joka mahdollistaisi koodaamisen ja ei veisi koodaamiselta liiaksi aikaa. Toisaalta vastalahjalla pitäisi olla muutakin arvoa kun ohjelmistot, ehkäpä joku “laatutikku”, tai toisaalta mahdollinen dvd-levyn “taiteellinen” etiketti. Lahjoitus voisi voisi olla 1000€. Ensimmäisen valmistuksessa aina tietenkin kestää… Toisaalta voisin jatkaa yliopistolla ja tehdä lopputyön näistä.

Vastalahja tikku on edistynyt sen verran että sisältö on vaatimaton edistymisraportti, joka sisältää artikkelin lähdekoodeja ja niiden varmuuskopioita artikkelin kirjoituksen ajalta. Hakusessa on vielä saisiko artikkelin pdf:n mukaan tikulle.

Vielä joukko entistäkin parempia satunnaislukuja newressulla (+ 2 korjaus, copy/reverse muutos ja “bad for randomness” -korjaus):

$ ./newressu
00000 77458582497443145904566934443436246480701130620326935991456610973
00001 94507419393879258178180749627076833108928192114356128593175144808
00002 69955332005176379096756541035011728926172026506046261674970499492
00003 10041144250791764746028281923017387934850354308811311475334133566
00004 35481673992214936870774513280004290499190496268994268401165338970
00005 82463289343086188404499348759211815912057510425970342155995679459
00006 57920773023068190680028846995762779195455248238525716464305873857
00007 86630133084381732337741142532649045059766988279545483298107240233
00008 01015986958233860186920327673324049941187364720513073490914507087
00009 28508727880161886875910325608456711423282083816715925889000039535
$

Vielä ressun ja pseudoressu:n yhdistelmä ressutwist: tämän pitäisi olla kestävämpi kun pelkkä ressu.

$ ./newressu --ressutwist
00000 38169523754911197205187055060318737263295581333071814524589708402
00001 72357785313131012189080233848055894425568528104200838183302063376
00002 46313582383678766598338369217460575659675989837848215915983938840
00003 42087445607719393940612257351025832890546631293746186346013303374
00004 12111436258786575204528802582398706615613192232411446341863086139
00005 72046192140160144744229764463489240213143808100563106362594903710
00006 69877913434074772919675327159017485427086810711562559812792448662
00007 32295749372024209206376009222244012624606168682788316244901158666
00008 63384464324225511781931177910571924012544413758040773312731575605
00009 58904257885745995006916810801142432472067950180960633658250684053
$ 

Sitten newressu sorsa kokonaisuudessaan, ensin Makefile:

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

all:		newressu sha256

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

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

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

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

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

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

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

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

newressu.c

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

//#define SHA256 2 // in Makefile

#include "sha256.h"

//#define MAIN 2 // in Makefile

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

int newressu_output = 0;

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

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

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

int verbose = 0;
int size = -1, type = 10;

#define USE_RANDOM 2

#define ALL_SLICE 16 // 16 // orig 64
#define RESSUT_SLICES 128 // 128 // orig 32
#define RESSUT_BYTES (RESSUT_SLICES * ALL_SLICE) // 2048 16 * 128 // (32 * 64)

#define RESSU_MIN_ROUNDS 2
#define RESSU_MIN_CLOCKBYTES 16 * 1024

#define GENT_SLICES 128 // 32 // orig 16
#define GENT_SIZE (GENT_SLICES * ALL_SLICE) // 1024 (16 * 64)

static int ressut_bytes = (RESSUT_SLICES * ALL_SLICE); // orig 2048
static int ressut_bits1_needed = RESSUT_SLICES * 2; // orig 256
static int ressut_bits2_needed = RESSUT_SLICES * 8; // orig 1024
static int ressut_bits3_needed = RESSUT_SLICES * 16; // orig 2048
static int ressut_bits4_needed = RESSUT_SLICES * 16; // orig 2048
static int ressut_bits5_needed = RESSUT_SLICES * 16; // orig 2048
static int ressut_bits6_needed = RESSUT_SLICES * 32; // orig 2048 * 2
static int ressut_bits7_needed = RESSUT_SLICES * 128; // orig 2048 * 8

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

static int stats = 0;

#define DEBUG_SORTED 2

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

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

#define FIXEDCLOCKCHAINLENGTH 50 // orig 50
unsigned int fixedclockchainlength = FIXEDCLOCKCHAINLENGTH;

int fixedclock = 0; // 0 = normal, 1 = fixed

static unsigned char ressu_clock()
{
  static unsigned int clockbyte = 0, counter = 0;

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

#define aDEBUG04 2

#ifdef DEBUG04

unsigned long count[RESSUT_BYTES];

#endif

#include <sys/types.h>
#include <unistd.h>

#define aDEBUG2A 2
#define aDEBUG2B 2
#define aDEBUG2C 2
#define aDEBUG2D 2
#define aDEBUG2E 2
#define aDEBUG2F 2

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

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

  gettimeofday(&tv, NULL);

  rando = rando +
    //genbytes +
    //time(NULL) +
    //ressu_useconds() +
    //ressu_clock() +
    //buf.tms_utime + 
    //buf.tms_stime +
    getpid() +
    clockbytes +
    tv.tv_sec +
    tv.tv_usec +
    clock() / CLOCKS_PER_SEC +
    clock() % CLOCKS_PER_SEC +
    ch * ch;

#ifdef DEBUG04
  count[rando % ressuct_bytes]++;
#endif
  
#ifdef DEBUG2C
  fprintf(stdout," %u", rando % ressuct_bytes);
  newressu_output = 1;
#endif
  
  return(rando);
}

#define DEPTH 4

unsigned int copyreverse = 1;
unsigned long cblocks = 0;
unsigned char lens = 4;

static unsigned char ressu_copyreverse(int depth)
{
  static int reverse[DEPTH] = {0}; // 0 = copy, 1 = reverse, 28.10.2022 JariK
  static unsigned int len[DEPTH] = {0};
  static unsigned int count[DEPTH] = {0};
  static unsigned int low[DEPTH] = {0};
  static unsigned int high[DEPTH] = {0};
  static unsigned char bytes[DEPTH][257] = {0};
  
  if(depth == 0) {
#ifdef DEBUG2D
    int c = ressu_clock();
    fprintf(stdout,", ch2:%02x",c);
    return(c);
#else
    return(ressu_clock());
#endif
  }

  depth--;

  // dividing clock stream into blocks
  // and reversing every other block

  // copy next block to bytes
  
  if(count[depth] == 0) { // previous block ended
    reverse[depth] = !reverse[depth];
    len[depth] = ressuct[ressu_nonrandom() % ressuct_bytes] + 2; // block size
    //len[depth] = (ressuct[ressu_nonrandom() % ressuct_bytes] & 3 ) + 2; // block size
    //len[depth] = --lens + 2; // block size
    //len[depth] = lens++ + 2; // block size
    count[depth] = len[depth];
    low[depth] = 0;
    high[depth] = len[depth];
    rndbits2 += 2;
#ifdef DEBUG2A
    fprintf(stdout,"\nblk:");
    fprintf(stdout," depth:%d", depth);
    fprintf(stdout,", reverse:%d", reverse[depth]);
    fprintf(stdout,", len:%d", len[depth]);
    fprintf(stdout,", count:%d", count[depth]);
    fprintf(stdout,", low:%d", low[depth]);
    fprintf(stdout,", high:%d", high[depth]);
    fflush(stdout);
#endif
    for(int c = 0; c < len[depth]; c++) {
      bytes[depth][c] = ressu_copyreverse(depth); // block bytes
#ifdef DEBUG2A
      fprintf(stdout,", b%d:%02x", depth, bytes[depth][c]);
      newressu_output = 1;
#endif
    }
  }
  if(reverse[depth]) { // reverse
    
    // get reversed byte to return
    ch = bytes[depth][--high[depth]]; // reverse
#ifdef DEBUG2E
    fprintf(stdout,", rev%d:%02x", depth, ch);
    fflush(stdout);
#endif
    --count[depth];

  } else { // copy
    
    // get copied byte to return
    ch = bytes[depth][low[depth]++];
#ifdef DEBUG2E
    fprintf(stdout,", copy%d:%02x", depth, ch);
    fflush(stdout);
#endif
    --count[depth];
  }

  return(ch);
}

static unsigned char ressu_clockbyte() /* JariK 2013 */
{
  if(copyreverse) {
    ch = ressu_copyreverse(DEPTH);
  } else {
    ch = ressu_clock();
  } // end of if(copyreverse)

#ifdef DEBUG2F
  static unsigned int cols = 0;
  if(cols % 32 == 0) {
    if(cols > 0)
      fprintf(stdout,"\n");
    fprintf(stdout,"%05u ",cols);
  }
  if(cols % 4 == 0)
    fprintf(stdout," ");
  fprintf(stdout,"%02x",ch);
  cols++;
#endif
  
  // statistics for theoretical
  // random bits calculation (rndbits?)
  
  static int prevbyte = -1, clockcount = 0;
  
  if(prevbyte == -1)
    prevbyte = ch;
  if(prevbyte != ch) {
    periods[clockcount]++;
    clockbytes += clockcount;
#ifdef DEBUG2B
    fprintf(stdout," %d", clockcount);
    newressu_output = 1;
#endif
    clockcount = 0;
    prevbyte = ch;
  }
  clockcount++;
  
  return(ch);
}

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

#define aDEBUG5A 2
#define aDEBUG5B 2
#define aDEBUG5C 2
#define aLOWCLOCKBITONLY 2 // off by default

void ressu_genbytes_single_do(int size, unsigned char *buffer) // JariK 2013
{
  int c, d;
  unsigned char e;
  static int f = 0;
  
  for(c = 0; c < 8; c++) {
    for(d = 0; d < size; d++) { // shift and xor
      e = buffer[d];
      e = RL8(e, 1); // rotate byte left 1 bits
      //e = RL8(e, 3); // rotate byte left 3 bits
      //e = RR8(e, 1); // rotate byte right 1 bits

#ifdef LOWCLOCKBITONLY
      buffer[d] = e ^ (ressu_clockbyte() & 1);
#else
      buffer[d] = e ^ ressu_clockbyte();
#endif
    }
#ifdef DEBUG5A
    if(newressu_output)
      fprintf(stdout, "\n");
    ressu_dump("ressu 1", 32, buffer, 32);  // first 32 bytes
    fflush(stdout);
#endif
    for(d = 0; d < size; d++) { // swap
#ifdef DEBUG5B
      fprintf(stdout,"d:%d", d);
      fprintf(stdout,", f:%d", f);
      fprintf(stdout,", bufferd:%d", buffer[d]);
      fprintf(stdout,", bufferf:%d", buffer[f]);
      fprintf(stdout,"\n");
#endif
      f = (f + buffer[d] + 2) % size; // + 2 JariK 2022
      e = buffer[d];
      buffer[d] = buffer[f];
      buffer[f] = e;
    }
#ifdef DEBUG5A
    if(newressu_output)
      fprintf(stdout,"\n");
    ressu_dump("ressu 2", 32, buffer, 32); // first 32 bytes
    fflush(stdout);
#endif
#ifdef DEBUG5C
    fprintf(stdout,"\n");
    ressu_dump("ressu 3", size, buffer, 32); // whole buffer
    fflush(stdout);
#endif
  }
}

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

  rndbits2 = 0;
  clockbytes = 0;

  cblocks = 0;

  ressu_genbytes_single_do(size, buffer);

  //
  //  display statistics
  //

  if(stats) {

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

    fprintf(stderr, "rounds:1");

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

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

#endif
  
  genbytes += size;
}

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

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

  rndbits2 = 0;
  clockbytes = 0;

  cblocks = 0;
  
  for(d = 0; d < RESSU_MIN_ROUNDS ||
	(copyreverse == 1 && rndbits2 < ressut_bits2_needed) ||
	clockbytes < RESSU_MIN_CLOCKBYTES; d++) {
    ressu_genbytes_single_do(size, buffer);
  }

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

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

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

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

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

    int g = 0;

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

#ifdef DEBUG7

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

#endif
  
  genbytes += size;
}

#define aDEBUG3B 2

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

  unsigned long prevperiods[1024];

  ressuct = ressut;  // ressu random buffer lookup
  ressuct_bytes = ressut_bytes; // table used in ressu_genbytes_single_do

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

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

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

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

      int rndbits3high;
      int rndbits4highdigits;
      int rndbits6high;
      int rndbits6pos;
      int rndbits7high;
      
      for(d = 0;
	  rndbits1 < ressut_bits1_needed ||
	  (copyreverse == 1 && rndbits2 < ressut_bits2_needed) ||
	  rndbits3 < ressut_bits3_needed ||
	  rndbits4 < ressut_bits4_needed ||
	  rndbits5 < ressut_bits5_needed ||
	  rndbits6 < ressut_bits6_needed ||
	  rndbits7 < ressut_bits7_needed ||
	  d < RESSU_MIN_ROUNDS ||
	  clockbytes < RESSU_MIN_CLOCKBYTES; d++) {

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

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

#ifdef DEBUG3B
	fprintf(stdout,"rndbits1:%d", rndbits1);
	fprintf(stdout,", rndbits2:%d", rndbits2);
	fprintf(stdout,", rndbits3:%d", rndbits3);
	fprintf(stdout,", rndbits4:%d", rndbits4);
	fprintf(stdout,", rndbits5:%d", rndbits5);
	fprintf(stdout,", rndbits6:%d", rndbits6);
	fprintf(stdout,", rndbits7:%d", rndbits6);
	fprintf(stdout,"\n");
#endif
	ressu_genbytes_single_do(ressut_bytes, ressut);

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

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

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

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

	// and average

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

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

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

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

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

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

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

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

	lim2 = lim;

	// calculate rndbits1

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

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

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

	//
	//  calculate rndbits4
	//

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

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

	//
	//  calculate rndbits5
	//

#define aDEBUG14E 2
	
	rndbits5 = 0;

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

	//
	//  calculate rndbits6
	//

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

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

	//
	//  calculate rndbits7
	//

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

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

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

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

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

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

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

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

#ifdef DEBUG14E

      rndbits5 = 0;

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

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

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

	  double l2;

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

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

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

#endif
      
      if(stats) {

	if(newressu_output == 1)
	  fprintf(stderr,"\n");
	newressu_output = 0;
	
	//
	//  display statistics
	//
	fprintf(stderr, "rounds:%d", d);
	long chains = 0;
    
	for(e = 0; e < 1024; e++) {
	  if(periods[e] > 0) {
	    fprintf(stderr, " %d:%lu", e, periods[e]);
	    chains += periods[e];
	  }
	}

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

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

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

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

#endif
  
  genbytes += size;
}

#define aDEBUG20 2

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

void inccvar()
{
  int c;

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

  if(cvarsize < c)
    cvarsize = c;

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

#define aCVARRANDOMSTART 2 // off by default

void clearcvar()
{
  int c;

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

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

#ifdef SHA256

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

unsigned long topup_bytes = TOPUP_BYTES;

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

static void pseudoressu_internalbytes(unsigned char *digest)
{
  HashCtx hash;

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

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

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

#define aDEBUG23 2

static void pseudoressu_topup()
{
  unsigned char topup[TOPUP_SIZE]; // 32 bytes, 256 bits

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

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

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

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

  while(size > 0) {

    if(topup_counter == 0) {
      if(initneeded) {
	ressu_genbytes(sizeof(pseudoressu_key), pseudoressu_key); // set first key
#ifdef DEBUG23
	ressu_dump("first key", sizeof(pseudoressu_key), pseudoressu_key, 32);
#endif
	initneeded = 0;
      }

      pseudoressu_topup(); // add randomness to key
#ifdef TOPUP_TWICE
      pseudoressu_topup(); // add randomness to key
#endif

      topup_counter = topup_bytes;
      blockbytes = 0;
    } // end of if(topup_counter <= 0

    pseudoressu_internalbytes(digest); // get random bits using the key

    // n = smallest from size, digest and topup_counter
    
    n = (size < sizeof(digest) ? size : sizeof(digest));
    n = (topup_counter < n ? topup_counter : n);
    //fprintf(stderr,", n:%d",n);

    for(int c = 0; c < n; c++)
      buffer[c] ^= digest[c];

#ifdef DEBUG23
    ressu_dump("newdata", n, buffer, 32);
#endif

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

    buffer += n;
    size -= n;
    topup_counter -= n;
    
    blockbytes += n;

    if(blockbytes >= PSEUDORESSU_LIMIT && size > 0) {

      pseudoressu_internalbytes(pseudoressu_key); // replace key with new random one
#ifdef DEBUG23
      ressu_dump("rekey", sizeof(pseudoressu_key), pseudoressu_key, 32);
#endif
      blockbytes = 0;
    }

  } // end of while(size > 0
  
  pseudoressu_internalbytes(pseudoressu_key); // replace key with new random one
#ifdef DEBUG23
  ressu_dump("new key", sizeof(pseudoressu_key), pseudoressu_key, 32);
#endif
}

void ressutwist_bytes(int size, unsigned char *buffer) // JariK 2023
{
  ressu_genbytes(size, buffer);
  pseudoressu_bytes(size, buffer);
}

#endif // end of #ifdef SHA256

#ifdef SHA256

void dumpbin(FILE *fp1, int len, unsigned char *buf)
{
  int c;

  for(c = 0; c < len; c++)
    if(isprint(buf[c]) && buf[c]!= ' ')
      fputc(buf[c], stderr);
    else
      fprintf(stderr,"\\%02x", buf[c]);
}

void dumpline(FILE *fp1, int len, unsigned char *buf)
{
  int c;

  for(c = 0; c < len; c++)
    fprintf(fp1,"%02x", buf[c]);
}

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

static void stream_internalbytes(unsigned char *digest)
{
  HashCtx hash;

  HashInit(&hash);
  HashUpdate(&hash, stream_key, sizeof(stream_key));
  HashUpdate(&hash, (unsigned char *) &cvar, cvarsize + 1);
  inccvar();
  HashFinal(digest, &hash);

  memset(&hash, 0, sizeof(hash)); // forget hash
}

static int streamt_pos = 0;

void stream_bytes(int size, unsigned char *buffer) // JariK 2023
{
  int c;
  static unsigned char streamt[HashLen]; // 32 bytes, 256 bits
  
  for(c = 0; c < size; c++) {

    if(streamt_pos == 0) {
      stream_internalbytes(streamt); // read next random bytes
      stream_internalbytes(stream_key); // change key
    } // end of if(streamt_pos == 0

    buffer[c] = streamt[streamt_pos];
    streamt_pos = (streamt_pos + 1) % sizeof(streamt);

  } // end of for(c = 0; c < size; c++
}

#define STREAM_KEY_ROUNDS 4096 // should be fast enough and slow enough, now 4096

void stream_open(int size, unsigned char *key) // size = 0 --> zero terminated string
{
  int c;
  HashCtx hash;

  clearcvar();
  streamt_pos = 0;  

  if(size == 0)
    size = strlen(key);
  
  HashInit(&hash);
  HashUpdate(&hash, key, size);
  HashUpdate(&hash, (unsigned char *) &cvar, cvarsize + 1);
  inccvar();

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

    HashFinal(stream_key, &hash);

    HashInit(&hash);
    HashUpdate(&hash, stream_key, sizeof(stream_key));
    HashUpdate(&hash, (unsigned char *) &cvar, cvarsize + 1);
    inccvar();
  }

  HashFinal(stream_key, &hash);

#define DEBUG21 2

#ifdef DEBUG21
  fflush(stdout);
  fprintf(stderr,"%s: stream_open():", procname);
  fprintf(stderr," randomness from key, key=");
  dumpbin(stderr, size, key);
  fprintf(stderr,"\n");
  fflush(stderr);
#endif
  
  memset(&hash, 0, sizeof(hash)); // forget hash
}

#endif // end of #ifdef SHA256

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

  if((fp1 = fopen(filename, "rb")) == NULL) {
    fprintf(stderr,"%s: fopen(): cannot open file %s\n",
	    procname, filename);
    exit(1);
  }
  while(size > 0) {
    n = (size < sizeof(temp)) ? size : sizeof(temp);
    if(fread(temp, 1, n, fp1) < n) {
      fprintf(stderr,"%s: fread(): cannot read file %s\n",
	      procname, filename);
      exit(1);
    }
    for(c = 0; c < n; c++)
      buffer[c] ^= temp[c];
    size -= n;
    buffer += n;
  }
  fclose(fp1);
  
  memset(temp, 0, sizeof(temp)); // forget temp
}

static unsigned char urandomfilename[128] = "/dev/urandom";

void urandom_bytes(int size, unsigned char *buffer)
{
  readfile_xor(size, buffer, urandomfilename);
}

#ifdef USE_RANDOM

static unsigned char randomfilename[128] = "/dev/random";

void random_bytes(int size, unsigned char *buffer)
{
  readfile_xor(size, buffer, randomfilename);
}

#endif

void test_bytes(int size, unsigned char *buffer) // JariK 2023
{
  // insert your generator here, run it with
  // --test command line parameter (source for
  // randomness)

  ressutwist_bytes(size, buffer);
  urandom_bytes(size, buffer);
  
  //memset(buffer, 0, size);

  //for(int c = 0; c < size; c++)
  //  buffer[c] = c & 0xff;
}

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

#ifdef MAIN

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

#include "newressu.h"

#define SAMPLE_WRITE 2 // on by default
#define SAMPLE_HASH 2 // on by default (for now)
#define SAMPLE_SYNC 2 // on by default (for now (exfat))
#define USE_TIMER 2
#define aUSE_DUMMY 2

void command_readline(unsigned char *command, int linelen, unsigned char *line)
{
  FILE *fp1;

  line[0] = '\0';
  
  if((fp1 = popen(command, "r")) == NULL) {
    fprintf(stdout,"%s: popen(): cannot open process '%s'\n",
	    procname, command);
  } else  {
    fgets(line, linelen, fp1);
    if(line[strlen(line)-1] == '\n')
      line[strlen(line)-1] = '\0';
  }
}

long long command_countlines(unsigned char *command)
{
  unsigned char buffer[1024];
  long long ll = 0;
  FILE *fp1;

  if((fp1 = popen(command, "r")) == NULL) {
    fprintf(stdout,"%s: popen(): cannot open process '%s'\n",
	    procname, command);
  } else  {
    while(fgets(buffer, sizeof(buffer), fp1) != NULL)
      ll++;
  }
  return(ll);
}

#define aDEBUG25 // off by default
#define aDEBUG19 // off by default

#ifdef DEBUG19

void save_path(int path_length, unsigned char *path)
{
  if((getcwd(path, path_length)) == NULL) {
    fprintf(stdout,"%s: save_path(): cannot get working directory (getcwd())\n",
	    procname);
  }
#ifdef DEBUG25
  fprintf(stderr,"%s: save_path(): saved current path (getcwd()), path = %s\n",
	  procname, path);
#endif
}

int exists_path(unsigned char *path)
{
  FILE *fp1;
  
  if((fp1 = fopen(path, "r")) != NULL) {
    fclose(fp1);
    //fprintf(stderr,"path %s ok\n", path);
    return(1);
  } else
    return(0);
}

void set_path(unsigned char *path)
{
  if(chdir(path) != 0) {
    fprintf(stdout,"%s: set_path(): cannot change directory (chdir())\n",
	    procname);
  }
#ifdef DEBUG25
  fprintf(stderr,"%s: set_path(): set path (chdir()), path = %s\n",
	  procname, path);
#endif
}

#endif // end of #ifdef DEBUG19

unsigned char *newressu_strstr(unsigned char *haystack, unsigned char *needle)
{
  int nlen = strlen(needle);;
  unsigned char *p = haystack;

  while(*p != '\0') {
    if(!strncmp(p, needle, nlen) &&
       ( isblank(*(p + nlen)) || ispunct(*(p + nlen)) || *(p + nlen) == '\0' ) && // character after string
       ( (p == haystack) || isblank(*(p - 1)) || ispunct(*(p - 1)) ) ) // character before string
      return(p);
    p++;
  }
  return(NULL);
}

#ifdef OLD1

unsigned char *newressu_strstr(unsigned char *haystack, unsigned char *needle)
{
  int fl;
  unsigned char *p = haystack;

  while(*p != '\0') {
    fl = strlen(needle);
    if(!strncmp(p, needle, strlen(needle)) &&
       ( isblank(*(p + fl)) || ispunct(*(p + fl)) || *(p + fl) == '\0' ) && // character after string
       ( (p == haystack) || isblank(*(p - 1)) || ispunct(*(p - 1)) ) ) { // character before string
      return(p);
    }
    p++;
  }
  return(NULL);
}

#endif

/*
 *  code for flags functions
 */
/*
🇦
🇧
🇨
🇩
🇪
🇫
🇬
🇭
🇮
🇯
🇰
🇱
🇲
🇳
🇴
🇵
🇶
🇷
🇸
🇹
🇺
🇻
🇼
🇽
🇾
🇿
*/
/*
  🇦🇧🇨🇩🇪🇫🇬🇭🇮🇯🇰🇱🇲🇳🇴🇵🇶🇷🇸🇹🇺🇻🇼🇽🇾🇿
*/

struct idflags { // 202307 JariK
  unsigned char *id;
  unsigned char *flags;
} idsflags[] = {
  { "🇫🇮", "🇫🇮: fi, fin, Finland, EU, NORTHERNEUROPE, EUROPE, EURASIA, NORDIC, FINLAND" },
  { "🇦🇽", "🇦🇽: ax, ala, Åland, aland, NORDIC, FINLAND" },
  { "🇸🇪", "🇸🇪: se, swe, Sweden, EU, NORTHERNEUROPE, EUROPE, EURASIA, NORDIC" },
  { "🇳🇴", "🇳🇴: no, nor, Norway, NORTHERNEUROPE, EUROPE, EURASIA, NORDIC" },
  { "🇩🇰", "🇩🇰: dk, dnk, Denmark, EU, NORTHERNEUROPE, EUROPE, EURASIA, NORDIC, DENMARK" },
  { "🇫🇴", "🇫🇴: fo, fro, FaroeIslands, NORDIC, DENMARK" },
  { "🇮🇸", "🇮🇸: is, isl, Iceland, NORTHERNEUROPE, EUROPE, EURASIA, NORDIC" },
  { "🇬🇱", "🇬🇱: gl, grl, Greenland, NORTHAMERICA, NORDIC, DENMARK" },
  
  { "🇪🇪", "🇪🇪: ee, est, Estonia, EU, NORTHERNEUROPE, EUROPE, EURASIA, BALTIC" },
  { "🇱🇻", "🇱🇻: lv, lva, Latvia, EU, NORTHERNEUROPE, EUROPE, EURASIA, BALTIC" },
  { "🇱🇹", "🇱🇹: lt, ltu, Lithuania, EU, NORTHERNEUROPE, EUROPE, EURASIA, BALTIC" },
  
  { "🇺🇦", "🇺🇦: ua, ukr, Ukraine, EASTERNEUROPE, EUROPE, EURASIA" },
  { "🇦🇱", "🇦🇱: al, alb, Albania, EASTERNEUROPE, EUROPE, EURASIA" },
  { "🇦🇩", "🇦🇩: ad, and, Andorra, WESTERNEUROPE, EUROPE, EURASIA" },
  { "🇦🇲", "🇦🇲: am, arm, Armenia, EASTERNEUROPE, EUROPE, EURASIA, WESTASIA, ASIA" },
  { "🇦🇹", "🇦🇹: at, aut, Austria, EU, WESTERNEUROPE, EUROPE, EURASIA" },
  { "🇦🇿", "🇦🇿: az, aze, Azerbaijan, EASTERNEUROPE, EUROPE, WESTASIA, ASIA, EURASIA" },
  { "🇧🇾", "🇧🇾: by, blr, Belarus, EASTERNEUROPE, EUROPE, EURASIA" },
  { "🇧🇪", "🇧🇪: be, bel, Belgium, EU, WESTERNEUROPE, EUROPE, EURASIA" },
  { "🇧🇦", "🇧🇦: ba, bih, BosniaandHerzegovina, EASTERNEUROPE, EUROPE" }, // Bosnia and Herzegovina
  { "🇧🇬", "🇧🇬: bg, bgr, Bulgaria, EU, EASTERNEUROPE, EUROPE, EURASIA" },
  { "🇭🇷", "🇭🇷: hr, hrv, Croatia, EU, EASTERNEUROPE, EUROPE, EURASIA" },
  { "🇨🇾", "🇨🇾: cy, cyp, Cyprus, EU, SOUTHERNEUROPE, EUROPE, EURASIA, WESTASIA, ASIA" },
  { "🇨🇿", "🇨🇿: cz, cze, CzechRepublic, EU, EASTERNEUROPE, EUROPE, EURASIA" },  // Czech Republic Czechia
  { "🇫🇷", "🇫🇷: fr, fra, France, EU, WESTERNEUROPE, EUROPE, EURASIA" },
  { "🇬🇪", "🇬🇪: ge, geo, Georgia, EASTERNEUROPE, EUROPE, EURASIA, WESTASIA, ASIA" },
  { "🇩🇪", "🇩🇪: de, deu, Germany, EU, WESTERNEUROPE, EUROPE, EURASIA" },
  { "🇬🇷", "🇬🇷: gr, grc, Greece, EU, SOUTHERNEUROPE, EUROPE, EURASIA" },
  { "🇭🇺", "🇭🇺: hu, hun, Hungary, EU, EASTERNEUROPE, EUROPE, EURASIA" },
  { "🇮🇪", "🇮🇪: ie, irl, Ireland, UK, EU, WESTERNEUROPE, EUROPE, EURASIA" },
  { "🇮🇹", "🇮🇹: it, ita, Italy, EU, SOUTHERNEUROPE, EUROPE, EURASIA" },
  { "🇻🇦", "🇻🇦: va, vat, Vatican, SOUTHERNEUROPE, EUROPE, EURASIA" },
  { "🇰🇿", "🇰🇿: kz, kaz, Kazakhstan, EUROPE, EURASIA, CENTRALASIA, ASIA" },
  { "🇱🇮", "🇱🇮: li, lie, Liechtenstein, WESTERNEUROPE, EUROPE, EURASIA" },
  { "🇱🇺", "🇱🇺: lu, lux, Luxembourg, EU, WESTERNEUROPE, EUROPE, EURASIA" },
  { "🇲🇹", "🇲🇹: mt, mlt, Malta, EU, SOUTHERNEUROPE, EUROPE, EURASIA" },
  { "🇲🇩", "🇲🇩: md, mda, Moldova, EASTERNEUROPE, EUROPE, EURASIA" },
  { "🇲🇪", "🇲🇪: me, mne, Montenegro, EASTERNEUROPE, EUROPE, EURASIA" },
  { "🇲🇨", "🇲🇨: mc, mco, Monaco, WESTERNEUROPE, EUROPE, EURASIA" },
  { "🇲🇪", "🇲🇪: me, mne, Montenegro, EUROPE, EURASIA" },
  { "🇳🇱", "🇳🇱: nl, nld, Netherlands, EU, WESTERNEUROPE, EUROPE, EURASIA" },
  { "🇲🇰", "🇲🇰: mk, mkd, Northmacedonia, EASTERNEUROPE, EUROPE, EURASIA" }, // North Macedonia
  { "🇵🇱", "🇵🇱: pl, pol, Poland, EU, EASTERNEUROPE, EUROPE, EURASIA" },
  { "🇵🇹", "🇵🇹: pt, prt, Portugal, EU, SOUTHERNEUROPE, EUROPE, EURASIA" },
  { "🇷🇴", "🇷🇴: ro, rou, Romania, EU, EASTERNEUROPE, EUROPE, EURASIA" },
  { "🇷🇺", "🇷🇺: ru, rus, Russia, EASTERNEUROPE, EUROPE, EURASIA, NORTHASIA, ASIA, BRICS" },
  { "🇸🇲", "🇸🇲: sm, smr, SanMarino, SOUTHERNEUROPE, EUROPE, EURASIA" }, // San Marino
  { "🇷🇸", "🇷🇸: rs, srb, Serbia, EASTERNEUROPE, EUROPE, EURASIA" },
  { "🇸🇰", "🇸🇰: sk, svk, Slovakia, EU, EASTERNEUROPE, EUROPE, EURASIA" },
  { "🇸🇮", "🇸🇮: si, svn, Slovenia, EU, EASTERNEUROPE, EUROPE, EURASIA" },
  { "🇪🇸", "🇪🇸: es, esp, Spain, EU, SOUTHERNEUROPE, EUROPE, EURASIA" },
  { "🇨🇭", "🇨🇭: ch, che, Switzerland, EU, WESTERNEUROPE, EUROPE, EURASIA" },
  { "🇹🇷", "🇹🇷: tr, tur, Turkey, SOUTHERNEUROPE, EUROPE, EURASIA, WESTASIA, ASIA" },
  
  { "🇬🇧", "🇬🇧: gb, gbr, UnitedKingdom, UK, WESTERNEUROPE, EUROPE, EURASIA" },
  { "🇮🇪", "🇮🇪: gb, gbr, England, UK" },
  { "🇮🇪", "🇮🇪: gb, gbr, Scotland, UK" },
  { "🇮🇪", "🇮🇪: gb, gbr, Wales, UK" },

  { "🇬🇮", "🇬🇮: gi, gib, Gibraltar, UK" },

  { "🇸🇯", "🇸🇯: sj, sjm, JanMayen, NORWAY" }, // Jan Mayen
  
  { "🇦🇮", "🇦🇮: ai, aia, Anguilla, NORTHAMERICA, UK" },
  { "🇦🇬", "🇦🇬: ag, atg, AntiguaAndBarbuda, NORTHAMERICA" }, // Antigua and Barbuda 
  { "🇦🇼", "🇦🇼: aw, sbw, Aruba, NORTHAMERICA" }, 
  { "🇧🇸", "🇧🇸: bs, bhs, Bahamas, NORTHAMERICA" },
  { "🇧🇧", "🇧🇧: bb, brb, Barbados, NORTHAMERICA" },
  { "🇧🇿", "🇧🇿: bz, blz, Belize, NORTHAMERICA" },
  { "🇧🇲", "🇧🇲: bm, bmu, Bermuda, UK, NORTHAMERICA" },
  { "🇧🇶", "🇧🇶: bq, bes, Bonaire, NETHERLANDS, NORTHAMERICA" },
  { "🇻🇬", "🇻🇬: vg, vgb, BritishVirginIslands, UK, NORTHAMERICA" }, // British Virgin Islands
  { "🇨🇦", "🇨🇦: ca, can, Canada, NORTHAMERICA" },
  { "🇰🇾", "🇰🇾: ky, cym, CaymanIslands, UK, NORTHAMERICA" },
  { "🇫🇷", "🇫🇷: fr, fra, ClippertonIsland, FRANCE, NORTHAMERICA" },
  { "🇨🇷", "🇨🇷: cr, cri, CostaRica, NORTHAMERICA" },
  { "🇨🇺", "🇨🇺: cu, cub, Cuba, NORTHAMERICA" },
  { "🇨🇼", "🇨🇼: cw, cuw, Curaçao, curacao, NORTHAMERICA" },
  { "🇩🇲", "🇩🇲: dm, dma, Dominica, NORTHAMERICA" },
  { "🇩🇴", "🇩🇴: do, dom, DominicanRepublic, NORTHAMERICA" }, // Dominican Republic
  { "🇸🇻", "🇸🇻: sv, slv, ElSalvador, NORTHAMERICA" },
  { "🇬🇱", "🇬🇱: gl, grl, Greenland, DENMARK, NORTHAMERICA" },
  { "🇬🇩", "🇬🇩: gd, grd, Grenada, NORTHAMERICA" },
  { "🇬🇵", "🇬🇵: gp, glp, Guadeloupe, FRANCE, NORTHAMERICA" },
  { "🇬🇹", "🇬🇹: gt, gtm, Guatemala, NORTHAMERICA" },
  { "🇭🇹", "🇭🇹: ht, hti, Haiti, NORTHAMERICA" },
  { "🇭🇳", "🇭🇳: hn, hnd, Honduras, NORTHAMERICA" },
  { "🇯🇲", "🇯🇲: jm, jam, Jamaica, NORTHAMERICA" },
  { "🇲🇶", "🇲🇶: mq, mtq, Martinique, FRANCE, NORTHAMERICA" },
  { "🇲🇽", "🇲🇽: mx, mex, Mexico, NORTHAMERICA" },
  { "🇲🇸", "🇲🇸: ms, msr, Montserrat, UK, NORTHAMERICA" },
  { "🇳🇮", "🇳🇮: ni, nic, Nicaragua, NORTHAMERICA" },
  { "🇵🇦", "🇵🇦: pa, pan, Panama, NORTHAMERICA" },
  { "🇵🇷", "🇵🇷: pr, pri, PuertoRico, NORTHAMERICA" },
  { "🇧🇶", "🇧🇶: bq, bes, Saba, NORTHAMERICA" },
  { "🇧🇱", "🇧🇱: bl, blm, SaintBarthélemy, saintbarthelemy, NORTHAMERICA" }, // Saint Barthélemy
  { "🇰🇳", "🇰🇳: kn, kna, SaintKitts, NORTHAMERICA" }, // Saint Kitts and Nevis
  { "🇱🇨", "🇱🇨: lc, lca, SaintLucia, NORTHAMERICA" }, // Saint Lucia
  { "🇲🇫", "🇲🇫: mf, maf, SaintMartin, NORTHAMERICA" }, // Collectivity of Saint Martin
  { "🇸🇽", "🇸🇽: sx, sxm, SintMaarten, NORTHAMERICA" }, // Sint Maarten
  { "🇹🇹", "🇹🇹: tt, tto, TrinidadandTobago, NORTHAMERICA" }, // Trinidad and Tobago
  { "🇹🇨", "🇹🇨: tc, tca, TurksandCaicos, UK, NORTHAMERICA" }, // Turks and Caicos Islands
  { "🇵🇲", "🇵🇲: pm, spm, SaintPierre, NORTHAMERICA" }, // Saint Pierre and Miquelon
  { "🇻🇨", "🇻🇨: vc, vct, SaintVincent, NORTHAMERICA" }, // Saint Vincent and the Grenadines
  { "🇺🇸", "🇺🇸: us, usa, UnitedStatesofAmerica, USA, NORTHAMERICA" }, // United States of America
  { "🇻🇮", "🇻🇮: vi, vir, VirginIslands, USA, NORTHAMERICA" }, // United States Virgin Islands

  { "🇧🇻", "🇧🇻: bv, bvt, BouvetIsland" }, // Bouvet Island
  { "🇺🇲", "🇺🇲: um, umi, Navassa" },
  { "🇸🇭", "🇸🇭: sh, shn, SaintHelena" }, // Saint Helena
  
  { "🇦🇷", "🇦🇷: ar, arg, Argentina, SOUTHAMERICA" },
  { "🇧🇴", "🇧🇴: bo, bol, Bolivia, SOUTHAMERICA" },
  { "🇧🇷", "🇧🇷: br, bra, Brazil, SOUTHAMERICA, BRICS" },
  { "🇨🇱", "🇨🇱: cl, chl, Chile, SOUTHAMERICA" },
  { "🇨🇴", "🇨🇴: co, col, Colombia, SOUTHAMERICA" },
  { "🇪🇨", "🇪🇨: ec, ecu, Ecuador, SOUTHAMERICA" },
  { "🇫🇰", "🇫🇰: fk, flk, FalklandIslands, UK, SOUTHAMERICA" }, // Falkland Islands
  { "🇬🇫", "🇬🇫: gf, guf, FrenchGuiana, SOUTHAMERICA, FRANCE" }, // French Guiana
  { "🇬🇾", "🇬🇾: gy, guy, Guyana, SOUTHAMERICA" },
  { "🇵🇾", "🇵🇾: py, pry, Paraguay, SOUTHAMERICA" },
  { "🇵🇪", "🇵🇪: pe, per, Peru, SOUTHAMERICA" },
  { "🇬🇸", "🇬🇸: gs, sgs, SouthGeorgia, SOUTHAMERICA" }, // South Georgia and the South Sandwich Islands
  { "🇸🇷", "🇸🇷: sr, sur, Suriname, SOUTHAMERICA" },
  { "🇺🇾", "🇺🇾: uy, ury, Uruguay, SOUTHAMERICA" },
  { "🇻🇪", "🇻🇪: ve, ven, Venezuela, SOUTHAMERICA" },

  { "🇦🇪", "🇦🇪: ae, are, UnitedArabEmirates, WESTASIA, ASIA, EURASIA" }, // United Arab Emirates
  { "🇦🇫", "🇦🇫: af, afg, Afghanistan, SOUTHASIA, ASIA, EURASIA" },
  { "🇧🇩", "🇧🇩: bd, bgd, Bangladesh, SOUTHASIA, ASIA, EURASIA" },
  { "🇧🇭", "🇧🇭: bh, bhr, Bahrain, EASTASIA, ASIA, EURASIA" },
  { "🇧🇳", "🇧🇳: bn, brn, Brunei, ASIA, EURASIA" },
  { "🇧🇹", "🇧🇹: bt, btn, Bhutan, SOUTHASIA, ASIA, EURASIA" },
  { "🇨🇳", "🇨🇳: cn, chn, China, EASTASIA, ASIA, EURASIA, BRICS" },
  { "🇭🇰", "🇭🇰: hk, hkg, HongKong, EASTASIA, ASIA, EURASIA" },
  { "🇮🇩", "🇮🇩: id, idn, Indonesia, ASIA, EURASIA" },
  { "🇮🇱", "🇮🇱: il, isr, Israel, WESTASIA, ASIA, EURASIA" },
  { "🇮🇳", "🇮🇳: in, ind, India, SOUTHASIA, ASIA, EURASIA, BRICS" },
  { "🇮🇶", "🇮🇶: iq, irq, Iraq, WESTASIA, ASIA, EURASIA" },
  { "🇮🇷", "🇮🇷: ir, irn, Iran, WESTASIA, ASIA, EURASIA" },
  { "🇯🇴", "🇯🇴: jo, jor, Jordan, WESTASIA, ASIA, EURASIA" },
  { "🇯🇵", "🇯🇵: jp, jpn, Japan, EASTASIA, ASIA, EURASIA" },
  { "🇰🇬", "🇰🇬: kg, kgz, Kyrgyzstan, CENTRALASIA, ASIA, EURASIA" },
  { "🇰🇭", "🇰🇭: kh, khm, Cambodia, ASIA, EURASIA" },
  { "🇰🇷", "🇰🇷: kr, kor, SouthKorea, EASTASIA, ASIA, EURASIA" },
  { "🇰🇼", "🇰🇼: kw, kwt, Kuwait, WESTASIA, ASIA, EURASIA" },
  { "🇱🇦", "🇱🇦: la, lao, Laos, ASIA, EURASIA" },
  { "🇱🇧", "🇱🇧: lb, lbn, Lebanon, WESTASIA, ASIA, EURASIA" },
  { "🇱🇰", "🇱🇰: lk, lka, SriLanka, SOUTHASIA, ASIA, EURASIA" },
  { "🇲🇳", "🇲🇳: mn, mng, Mongolia, EASTASIA, ASIA, EURASIA" },
  { "🇲🇴", "🇲🇴: mo, mac, Macao, EASTASIA, ASIA, EURASIA" },
  { "🇲🇻", "🇲🇻: mv, mdv, Maldives, SOUTHASIA, ASIA, EURASIA" },
  { "🇲🇲", "🇲🇲: mm, mmr, Myanmar, ASIA, EURASIA" },
  { "🇲🇾", "🇲🇾: my, mys, Malaysia, ASIA, EURASIA" },
  { "🇰🇵", "🇰🇵: kp, prk, NorthKorea, EASTASIA, ASIA, EURASIA" },
  { "🇳🇵", "🇳🇵: np, npl, Nepal, SOUTHASIA, ASIA, EURASIA" },
  { "🇴🇲", "🇴🇲: om, omn, Oman, WESTASIA, ASIA, EURASIA" },
  { "🇵🇭", "🇵🇭: ph, phl, Philippines, ASIA, EURASIA" },
  { "🇵🇰", "🇵🇰: pk, pak, Pakistan, SOUTHASIA, ASIA, EURASIA" },
  { "🇵🇸", "🇵🇸: ps, pse, Palestine, WESTASIA, ASIA, EURASIA" },
  { "🇶🇦", "🇶🇦: qa, qat, Qatar, WESTASIA, ASIA, EURASIA" },
  { "🇸🇦", "🇸🇦: sa, sau, SaudiArabia, WESTASIA, ASIA, EURASIA" },
  { "🇸🇬", "🇸🇬: sg, sgb, Singapore, ASIA, EURASIA" },
  { "🇸🇾", "🇸🇾: sy, syr, Syria, WESTASIA, ASIA, EURASIA" },
  { "🇹🇭", "🇹🇭: th, tha, Thailand, ASIA, EURASIA" },
  { "🇹🇯", "🇹🇯: tj, tjk, Tajikistan, CENTRALASIA, ASIA, EURASIA" },
  { "🇹🇱", "🇹🇱: tl, tls, EastTimor, ASIA, EURASIA" },
  { "🇹🇲", "🇹🇲: tm, tkm, Turkmenistan, CENTRALASIA, ASIA, EURASIA" },
  { "🇹🇼", "🇹🇼: tw, twn, Taiwan, EASTASIA, ASIA, EURASIA" },
  { "🇺🇿", "🇺🇿: uz, uzb, Uzbekistan, CENTRALASIA, ASIA, EURASIA" },
  { "🇻🇳", "🇻🇳: vn, vnm, Vietnam, ASIA, EURASIA" },
  { "🇾🇪", "🇾🇪: ye, yem, Yemen, WESTASIA, ASIA, EURASIA" },

  { "🇳🇪", "🇳🇪: ne, ner, Nigeria, AFRICA, WESTAFRICA" },
  { "🇪🇹", "🇪🇹: et, eth, Ethiopia, AFRICA, AFRICA, EASTAFRICA" },
  { "🇪🇷", "🇪🇷: er, eri, Eritrea, AFRICA, EASTAFRICA" },
  { "🇨🇬", "🇨🇬: cg, cog, Congo, AFRICA, CENTRALAFRICA" }, // Rebublic of the Congo
  { "🇨🇩", "🇨🇩: cd, cod, DemocraticCongo, AFRICA, CENTRALAFRICA" }, // Democratic Rebublic of the Congo
  { "🇹🇿", "🇹🇿: tz, tza, Tanzania, AFRICA, EASTAFRICA" },
  { "🇿🇦", "🇿🇦: za, zaf, SouthAfrica, AFRICA, SOUTHAFRICA, BRICS" },
  { "🇰🇪", "🇰🇪: ke, ken, Kenya, AFRICA, EASTAFRICA" },
  { "🇺🇬", "🇺🇬: ug, uga, Uganda, AFRICA, EASTAFRICA" },
  { "🇸🇸", "🇸🇸: ss, ssd, SouthSudan, AFRICA, EASTAFRICA" },
  { "🇸🇩", "🇸🇩: sd, sdn, Sudan, AFRICA, EASTAFRICA" },
  { "🇩🇿", "🇩🇿: dz, dza, Algeria, AFRICA, NORTHAFRICA" },
  { "🇪🇬", "🇪🇬: eg, egy, Egypt, AFRICA, NORTHAFRICA, WESTASIA, ASIA, EURASIA" },
  { "🇱🇾", "🇱🇾: ly, lby, Libya, AFRICA, NORTHAFRICA" },
  { "🇱🇾", "🇱🇾: ly, lby, Madeira, AFRICA, NORTHAFRICA" },
  { "🇲🇦", "🇲🇦: ma, mar, Morocco, AFRICA, NORTHAFRICA" },
  { "🇦🇴", "🇦🇴: ao, ago, Angola, AFRICA, CENTRALAFRICA" },
  { "🇬🇭", "🇬🇭: gh, gha, Ghana, AFRICA, WESTAFRICA" },
  { "🇲🇿", "🇲🇿: mz, moz, Mozambique, AFRICA" },
  { "🇲🇬", "🇲🇬: mg, mdg, Madagascar, AFRICA, EASTAFRICA" },
  { "🇾🇹", "🇾🇹: yt, myt, Mayotte, FRANCE, AFRICA, EASTAFRICA" },
  { "🇨🇮", "🇨🇮: ci, civ, IvoryCoast, AFRICA, WESTAFRICA" },
  { "🇨🇲", "🇨🇲: cm, cmr, Cameroon, AFRICA, CENTRALAFRICA" },
  { "🇳🇪", "🇳🇪: ne, ner, Niger, AFRICA, WESTAFRICA" },
  { "🇧🇫", "🇧🇫: bf, bfa, BurkinaFaso, AFRICA, WESTAFRICA" }, // Burkina Faso
  { "🇲🇱", "🇲🇱: ml, mli, Mali, AFRICA, WESTAFRICA" },
  { "🇲🇼", "🇲🇼: mw, mwi, Malawi, AFRICA, EASTAFRICA" },
  { "🇿🇲", "🇿🇲: zm, zmb, Zambia, AFRICA, EASTAFRICA" },
  { "🇹🇩", "🇹🇩: td, tcd, Chad, AFRICA, CENTRALAFRICA" },
  { "🇸🇴", "🇸🇴: so, som, Somalia, AFRICA, EASTAFRICA" },
  { "🇸🇳", "🇸🇳: sn, sen, Senegal, AFRICA, WESTAFRICA" },
  { "🇿🇼", "🇿🇼: zw, zwe, Zimbabwe, AFRICA, EASTAFRICA" },
  { "🇬🇳", "🇬🇳: gn, gin, Guinea, AFRICA, WESTAFRICA" },
  { "🇷🇪", "🇷🇪: re, reu, Réunion, reunion, FRANCE, AFRICA, EASTAFRICA" }, // Réunion
  { "🇷🇼", "🇷🇼: rw, rwa, Rwanda, AFRICA, EASTAFRICA" },
  { "🇧🇯", "🇧🇯: bj, ben, Benin, AFRICA, WESTAFRICA" },
  { "🇧🇮", "🇧🇮: bi, bdi, Burundi, AFRICA, EASTAFRICA" },
  { "🇹🇳", "🇹🇳: tn, tun, Tunisia, AFRICA, NORTHAFRICA" },
  { "🇹🇬", "🇹🇬: tg, tgo, Togo, AFRICA, WESTAFRICA" },
  { "🇸🇱", "🇸🇱: sl, sle, SierraLeone, AFRICA, WESTAFRICA" }, // Sierra Leone
  { "🇨🇩", "🇨🇩: cd, cod, Congo, AFRICA, CENTRALAFRICA" },
  { "🇨🇫", "🇨🇫: cf, caf, CentralAfrican, CENTRALAFRICA" }, // Central African Republic
  { "🇱🇷", "🇱🇷: lr, lbr, Liberia, AFRICA, WESTAFRICA" },
  { "🇲🇷", "🇲🇷: mr, mrt, Mauritania, AFRICA, WESTAFRICA" },
  { "🇪🇷", "🇪🇷: er, eri, Eritrea, AFRICA, EASTAFRICA" },
  { "🇬🇲", "🇬🇲: gm, gmb, Gambia, AFRICA, WESTAFRICA" },
  { "🇧🇼", "🇧🇼: bw, bwa, Botswana, AFRICA, SOUTHAFRICA" },
  { "🇳🇦", "🇳🇦: na, nam, Namibia, AFRICA, SOUTHAFRICA" },
  { "🇬🇦", "🇬🇦: ga, gab, Gabon, AFRICA, CENTRALAFRICA" },
  { "🇱🇸", "🇱🇸: ls, lso, Lesotho, AFRICA, SOUTHAFRICA" },
  { "🇬🇼", "🇬🇼: gw, gnb, GuineaBissau, AFRICA, WESTAFRICA" }, // Guinea-Bissau
  { "🇬🇶", "🇬🇶: gq, gnq, EquatorialGuinea, CENTRALAFRICA" }, // Equatorial Guinea
  { "🇲🇺", "🇲🇺: mu, mus, Mauritius, AFRICA, EASTAFRICA" },
  { "🇲🇺", "🇲🇺: mz, moz, Mozambique, AFRICA, EASTAFRICA" },
  { "🇸🇿", "🇸🇿: sz, swz, Eswatini, AFRICA, SOUTHAFRICA" },
  { "🇩🇯", "🇩🇯: dj, dji, Djibouti, AFRICA, EASTAFRICA" },
  { "🇰🇲", "🇰🇲: km, com, Comoros, AFRICA" },
  { "🇨🇻", "🇨🇻: cv, cpv, CapeVerde, AFRICA, WESTAFRICA" },
  { "🇪🇭", "🇪🇭: eh, esh, WesternSahara, SPAIN, AFRICA, NORTHAFRICA" }, // Western Sahara
  { "🇸🇹", "🇸🇹: st, stp, SãoToméandPríncipe, saotomeandprincipe, CENTRALAFRICA" }, // São Tomé and Príncipe
  { "🇸🇨", "🇸🇨: sc, syc, Seychelles, AFRICA, EASTAFRICA" },

  { "🇸🇭", "🇸🇭: sh, shn, TristandaCunha, UK" }, // Tristan da Cunha
  
  { "🇦🇺", "🇦🇺: au, aus, Australia, AUSTRALASIA, OCEANIA" },
  { "🇨🇽", "🇨🇽: cx, cxr, Christmasisland, AUSTRALIA" }, // Christmas Island

  { "🇨🇨", "🇨🇨: cc, cck, CocosIslands, AUSTRALIA" }, // Cocos (Keeling) Islands
  
  { "🇮🇲", "🇮🇲: im, imn, ManIsle, UK" }, // Isle of Man
  
  { "🇰🇮", "🇰🇮: ki, kir, Kiribati, MICRONESIA, OCEANIA" },
  { "🇳🇷", "🇳🇷: nr, nru, Nauru, MICRONESIA, OCEANIA" },
  { "🇳🇨", "🇳🇨: nc, ncl, NewCaledonia, FRANCE, OCEANIA" }, // New Caledonia
  { "🇫🇯", "🇫🇯: fj, fji, Fiji, MELANESIA, OCEANIA" },
  { "🇫🇲", "🇫🇲: fm, fsm, Micronesia, MICRONESIA, OCEANIA" },
  { "🇵🇬", "🇵🇬: pg, png, PapuanewGuinea, MELANESIA, OCEANIA" }, // Papua New Guinea
  { "🇻🇺", "🇻🇺: vu, vut, Vanuatu, MELANESIA, OCEANIA" },
  { "🇸🇧", "🇸🇧: sb, slb, SolomonIslands, MELANESIA, OCEANIA" }, // Solomon Islands
  { "🇬🇺", "🇬🇺: gu, gum, Guam, USA, MICRONESIA, OCEANIA" },
  { "🇲🇭", "🇲🇭: mh, mhl, MarshallIslands, MICRONESIA, OCEANIA" }, // Marshall Islands
  { "🇲🇵", "🇲🇵: mp, mnp, NorthernMariana, MICRONESIA, OCEANIA" }, // Northern Mariana Islands
  { "🇵🇫", "🇵🇫: pf, pyf, FrenchPolynesia, FRANCE, POLYNESIA, OCEANIA" }, // French Polynesia
  { "🇼🇸", "🇼🇸: ws, wsm, Samoa, POLYNESIA, OCEANIA" },
  { "🇼🇫", "🇼🇫: wf, wlf, WallisandFutuna, FRANCE, OCEANIA" }, // Wallis and Futuna
  { "🇹🇻", "🇹🇻: tv, tuv, Tuvalu, OCEANIA" },
  { "🇦🇸", "🇦🇸: as, asm, Americansamoa, USA, POLYNESIA, OCEANIA" }, // American Samoa
  { "🇵🇳", "🇵🇳: pn, pcn, Pitcairn, UK, POLYNESIA, OCEANIA" }, // Pitcairn Islands
  { "🇵🇼", "🇵🇼: pw, plw, Palau, MICRONESIA, OCEANIA" },

  { "🇯🇪", "🇯🇪: je, jey, Jersey, FRANCE" },
  
  { "🇦🇶", "🇦🇶: aq, ata, Antarctica, ANTARCTICA" },

  { "🇳🇫", "🇳🇫: nf, nfk, NorFolkIsland, AUSTRALASIA, POLYNESIA, OCEANIA" }, // Norfolk Island
  { "🇨🇰", "🇨🇰: ck, cok, Cookisland, NEWZEALAND, POLYNESIA, OCEANIA" }, // Cook Islands
  { "🇳🇿", "🇳🇿: nz, nzl, NewZealand, NEWZEALAND, AUSTRALASIA, OCEANIA" }, // New Zealand
  { "🇳🇺", "🇳🇺: nu, niu, Niue, NEWZEALAND, POLYNESIA, OCEANIA" },
  { "🇹🇰", "🇹🇰: tk, tkl, Tokelau, NEWZEALAND, POLYNESIA, OCEANIA" },
  { "🇹🇴", "🇹🇴: to, ton, Tonga, POLYNESIA, OCEANIA" },
  
  // still missing
  
  { "🇮🇴", "🇮🇴: io, iot, BritishIndianOcean" }, // British Indian Ocean Territory
  { "🇹🇫", "🇹🇫: tf, atf, FrenchSouthernandaAtarctic" }, // French Southern and Antarctic Lands
  { "🇬🇬", "🇬🇬: gg, ggy, Guernsey, GUERNSEY" },
  { "🇭🇲", "🇭🇲: hm, hmd, HeardandMacDonaldsIslands" }, // Heard Island and McDonald Islands
  { "🇺🇲", "🇺🇲: um, umi, UsMinorOutlyingIslands, US" }, // United States Minor Outlying Islands
};

/*
 *  flags changed
 */
static int flagidflags = 0; // dont display ids

static int idsall = 1;

/*
 *  flag for each id
 */
static char flagids[sizeof(idsflags) / sizeof(idsflags[0])];

int newressu_toggleflag(unsigned char *flag)
{
  int c, ok;
  
  if(!strcmp(flag, "ALL")) {
    idsall = !idsall;
    for(c = 0; c < sizeof(idsflags) / sizeof(idsflags[0]); c++) {
      flagids[c] = idsall;
    }
    flagidflags = 1; // display flags
    ok = 1; // one flag toggled
  } else {
    ok = 0;
    for(c = 0; c < sizeof(idsflags) / sizeof(idsflags[0]); c++) {
      if(newressu_strstr(idsflags[c].flags, flag)!=NULL) {
	flagids[c] = !flagids[c];
	flagidflags = 1; // display flags
	ok = 1; // one flag toggled
      }
    }
    //if(ok)
    //  fprintf(stdout,"\n");
  }
  return(ok);
}

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

void clock_bytes(int size, unsigned char *buffer) // JariK 2024
{
  int c;
  
  for(c = 0; c < size; c++)
    buffer[c] = ressu_clock();
}

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

int input = INPUT_RESSU;
unsigned char *input_str = "ressu";

int base = -1;

static unsigned char gent[GENT_SIZE]; // orig 1024 
static unsigned int gent_pos = 0;

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

#define aDEBUG18 2

int newressu_genbyte()
{
  unsigned char ch;

  if(gent_pos == 0) {
#ifdef DEBUG23
    ressu_dump("oldgent", 32, gent, 32); // first 32 bytes
#endif

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

#ifdef SHA256
    else if(input == INPUT_PSEUDORESSU) // pseudoressu
      pseudoressu_bytes(sizeof(gent), gent);
    
#endif
#ifdef SHA256
    else if(input == INPUT_RESSUTWIST) // ressu w twist
      ressutwist_bytes(sizeof(gent), gent);

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

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

    else if(input == INPUT_STREAM) // stream
      stream_bytes(sizeof(gent), gent);

    else if(input == INPUT_TEST) // test generator
      test_bytes(sizeof(gent), gent);

#ifdef FORT
    else if(input == INPUT_FORT) // ressu fort
      fort_random_data(sizeof(gent), gent);

    else if(input == INPUT_FORTXOR) // ressu fort
      fort_random_data_xor(sizeof(gent), gent);

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

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

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

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

#endif
    else if(input == INPUT_CLOCK) // clockstream
      clock_bytes(sizeof(gent), gent);

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

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

static unsigned char *basedigits = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-"; // 64 chars

void fprintfbase(FILE *fp1, unsigned long long ll2, int base) // JariK 2023
{
  int count;
  unsigned long long ll, divider;

  if(base == 2)
    fprintf(fp1,"0b");
  else if(base == 8)
    fprintf(fp1,"0o");
  else if(base == 10)
    fprintf(fp1,"0d");
  else if(base == 16)
    fprintf(fp1,"0x");
  
  ll = ll2;
  divider = 1;
  count = 0;

  for(;;) { // count digits
    ll /= base;
    count++;
    if(ll == 0)
      break;
    divider *= base; // do not count last digit
  }

  while(count > 0) { // print digits
    fprintf(fp1,"%c", basedigits[(ll2 / divider) % base]);
    divider /= base;
    count--;
  }
}

#define aDEBUG24 2
  
unsigned long newressu_gen_limit(unsigned long limit) // JariK 2022
{
  int c;
  unsigned long word;
  static unsigned long lastlimit = 0, highlimit;
  static int bytes;

  if(lastlimit != limit) { // if limit changes, calculate new highlimit and bytes
    lastlimit = limit;
    if(limit <= 0x100) { // one byte
      // highest multiplier of limit that fits to needed bytes
      highlimit = (0x100 / limit) * limit;
      // number of bytes needed
      bytes = 1;

    } else if(limit <= 0x10000) { // two bytes
      highlimit = (0x10000 / limit) * limit;
      bytes = 2;

    } else if(limit <= 0x1000000) { // three bytes
      highlimit = (0x1000000 / limit) * limit;
      bytes = 3;

    } else if(limit <= 0x100000000) { // four bytes
      highlimit = (0x100000000 / limit) * limit;
      bytes = 4;

    } else if(limit <= 0x10000000000) { // five bytes
      highlimit = (0x10000000000 / limit) * limit;
      bytes = 5;

    } else if(limit <= 0x1000000000000) { // six bytes
      highlimit = (0x1000000000000 / limit) * limit;
      bytes = 6;

    } else if(limit <= 0x100000000000000) { // seven bytes
      highlimit = (0x100000000000000 / limit) * limit;      
      bytes = 7;

    } else { // if(limit <= 0xffffffffffffffff) {
      highlimit = (0xffffffffffffffff / limit) * limit;      
      bytes = 8;

    }
  } // if(lastlimit != limit

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

#define aDEBUG34

#ifdef DEBUG34
  fprintf(stdout,"limit:%lu", limit);
  fprintf(stdout,", highlimit:%lu", highlimit);
  fprintf(stdout,", bytes:%d", bytes);
  fprintf(stdout,", word:%lu", word);
  fprintf(stdout,", word%%limit:%lu", (unsigned long) word % limit);
  fprintf(stdout,", base:");
  if(base == -1)
    fprintfbase(stdout, word % limit, 16);
  else
    fprintfbase(stdout, word % limit, base);
  fprintf(stdout,", base:0o%lo", word % limit);
  fprintf(stdout,", base:0d%lu", word % limit);
  fprintf(stdout,", base:0x%lx", word % limit);
  fprintf(stdout,"\n");
#endif
  
  word %= limit;
  
#ifdef DEBUG24
  fprintf(stdout,"/");
  fprintf(stdout,"%d bytes: ", bytes);
  fprintf(stdout,"[");
  if(base == -1)
    fprintfbase(stdout, word, 16);
  else
    fprintfbase(stdout, word, base);
  fprintf(stdout,"]");
  fprintf(stdout,"/");
  fflush(stdout);
#endif

  return(word);
}

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

void stat_line_cursor_start()
{
  fprintf(stderr,"%c", cursor[crs]);
  fflush(stderr);
  //prev_secs = -1;
  cursor_on = 1;
}

void stat_line_cursor()
{
  long int secs;

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

void stat_line_cursor_remove()
{
  if(cursor_on) {
    fprintf(stderr, "\b \b");
    fflush(stderr);
    //prev_secs = -1;
  }
  cursor_on = 0;
}

#include <stdarg.h>

#define aDEBUG31 2

unsigned char *procname = NULL;

static unsigned char *prev_stat_line = NULL;
static unsigned char *stat_line = NULL;
static size_t stat_line_length = 0;

static void stat_line_begin()
{
  if(stat_line != NULL)
    stat_line[0] = '\0';
}

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

  va_start(args, format);
  count = vsnprintf(buffer, buffer_length, format, args) + 1;
  va_end(args);

  if(buffer_length < count) {
    buffer_length = count;
    buffer = realloc(buffer, buffer_length);
    va_start(args, format);
    count = vsnprintf(buffer, buffer_length, format, args) + 1;
    va_end(args);
  }

  count = 0;
  if(stat_line != NULL)
    count += strlen(stat_line);
  count += strlen(buffer);
  count++;
  
  if(stat_line_length < count) {
    unsigned char *stat_line2 = stat_line;
    stat_line_length = count;
    stat_line = realloc(stat_line, stat_line_length);
    prev_stat_line = realloc(prev_stat_line, stat_line_length);
    if(stat_line2 == NULL) {
      stat_line[0] = '\0';
      prev_stat_line[0] = '\0';
    }
  }
  strcat(stat_line, buffer);
}

#define READABLE_NUMBER_BIN 2

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

#define READABLE_NUMBER_WIDTH 32

static void stat_line_get_readable(unsigned char buf10[10], unsigned long long length)
{
  int c, low;
  double length2;

  // B = byte
  // K = kilo   10^3   2^10
  // M = mega   10^6   2^20
  // G = giga   10^9   2^30
  // T = tera   10^12  2^40
  // P = peta   10^15  2^50
  // E = exa    10^18  2^60
  // Z = zetta  10^21  2^70
  // Y = yotta  10^24  2^80

  char units[] = "BKMGTPEZY";

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

  for(c = 0; length >= low &&
      c < sizeof(units) - 1; c++) {
    if(length >= low &&
       length <= READABLE_NUMBER_HIGH) {
      if(units[c] == 'B')
        sprintf(buf10, "%lld", length);
      else if(units[c] == 'K' ||
	      units[c] == 'M' ||
	      units[c] == 'G' ||
	      units[c] == 'T')
	sprintf(buf10, "%.3f", length2);
      else if(length == length2)
	sprintf(buf10, "%lld", length);
      else
	sprintf(buf10, "%.1f", length2);

      if(strchr(buf10, '.') != NULL) {
	int d;
	d = strlen(buf10) - 1;
	while(d > 3 && isdigit(buf10[d])) {
	  buf10[d] = '\0';
	  d--;
	}
	if(buf10[strlen(buf10)-1] == '.')
	  buf10[strlen(buf10)-1] = '\0';
      }

      if(units[c] != 'B') {
	char unit[10];
	sprintf(unit,"%cB",units[c]);
	strcat(buf10, unit);
      }
      break;
    }
    length2 = (double)length / READABLE_NUMBER_DIVIDER;
    length /= READABLE_NUMBER_DIVIDER;
    low = 1;
  }
}

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

static void stat_line_dhms(unsigned long long printleft)
{
  unsigned long int left2;
  unsigned long int temp;
  int timeprinted = 0;
  
  left2 = printleft;
  temp = left2 / (24 * 3600); // days
  if(temp > 0) {
    timeprinted = 1;
    stat_line_printf(" %dd", temp);
    left2 -= temp * (24 * 3600);
  }
  
  temp = left2 / 3600; // hours
  if(temp > 0 || timeprinted) {
    timeprinted = 1;
    //hoursprinted = 1;
    stat_line_printf(" %02dh", temp);
    left2 -= temp * 3600;
  }
  
  temp = left2 / 60; // minutes
  if(temp > 0 || timeprinted) {
    timeprinted = 1;
    stat_line_printf(" %02dm", temp);
    left2 -= temp * 60;
  }
  
  temp = left2; // seconds
  if(!timeprinted) {
    stat_line_printf(" %d seconds", temp);
  }
  
}

void stat_line_end()
{
  int c, d;

  if(stat_line != NULL && strcmp(prev_stat_line, stat_line)) { // status line changed
    stat_line_cursor_remove();
#ifdef DEBUG31
    fprintf(stderr, "\n");
#else
    fprintf(stderr, "\r");
#endif  

    fprintf(stderr,"%s", stat_line);
    fflush(stderr);

    // previous line longer than this one,
    // overwrite with spaces and backspace to
    // end
    
    d = strlen(prev_stat_line) - strlen(stat_line) + 1; // cursor too

    if(d > 0) { // previous line longer
      for(c = 0; c < d; c++) {
#ifdef DEBUG31
	fprintf(stderr, "*");
#else
	fprintf(stderr, " "); // print spaces
#endif
      }
#ifndef DEBUG31
      for(c = 0; c < d; c++)
	fprintf(stderr, "\b"); // and backspaces
#endif
    }
    strcpy(prev_stat_line, stat_line);
    //if(strlen(stat_line) > 0)
    //stat_line_cursor_start();
    //stat_line_cursor();
  }
}

#define KILO 1024

void readablelonglong(FILE *fp1, unsigned long long ll2) // 2023 JariK
{
  int c;
  unsigned long long multiplier, ll = ll2;
  
  // 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";

  c = 0;
  multiplier = 1;
  while(ll >= KILO) {
    ll /= KILO;
    multiplier *= KILO;
    c++;
  }

  if(ll * multiplier != ll2)
    fprintf(fp1,"~"); // approximately

  fprintf(fp1,"%llu%c", ll, units[c]);
}

void fprintfcharacter(FILE *fp1, unsigned char *p) // 2023 JariK
{
  fputc(*p, fp1); // print first char
  if(*p > 0xbf) { // first char utf8
    p++;
    for(;;) { // print rest of the utf8 chars
      if(*p > 0xbf || // new utf8 character
	 *p < 0x80 || // ascii character
	 *p == '\0') // end of string
	break;
      fputc(*p, fp1);
      p++;
    }
  }
}

int getdigit(unsigned char *p) // 2023 JariK
{
  int digit;
  
  if(*p >= '0' && *p <= '9')
    digit = *p - '0';
  else if(*p >= 'a' && *p <= 'z')
    digit = (*p - 'a') + 10;
  else if(*p >= 'A' && *p <= 'Z')
    digit = (*p - 'A') + 10;
  else
    digit = -1; // not found, illegal

  return(digit);
}

#define DEBUG32

//#define KILO 1000
#define KILO 1024

unsigned long long getlonglong(unsigned char *p2) // 2023 JariK
{
  int digit, base = 10;
  unsigned char *p = p2;
  unsigned long long totll, ll, prevll, multiplier;
  
  totll = 0;

  while(*p != '\0') { // works also: 1g100m & 1m20k and 1t1t etc...

    unsigned char *prevp = p;
    
    if(!strncmp("0x", p, 2)) {
      base = 16;
      p += 2;
      
    } else if(!strncmp("0d", p, 2)) {
      base = 10;
      p += 2;
      
    } else if(!strncmp("0o", p, 2)) {
      base = 8;
      p += 2;
      
    } else if(!strncmp("0b", p, 2)) {
      base = 2;
      p += 2;
      
    }
    
    ll = 0;
    while((digit = getdigit(p)) != -1 && digit < base) {
      ll = ll * base + digit;
      p++;
    }
    
    multiplier = 1;
    
    if(*p == 'k' || *p == 'K') {
      multiplier = KILO;
      p++;
      
    } else if(*p == 'm' || *p == 'M') {
      multiplier = (KILO * KILO);
      p++;
      
    } else if(*p == 'g' || *p == 'G') {
      multiplier = (KILO * KILO * KILO);
      p++;
      
    } else if(*p == 't' || *p == 'T') {
      multiplier = ((unsigned long long)KILO * KILO * KILO * KILO);
      p++;
      
    } else if(*p == 'p' || *p == 'P') {
      multiplier = ((unsigned long long)KILO * KILO * KILO * KILO * KILO);
      p++;
      
    } else if(*p == 'e' || *p == 'E') {
      multiplier = ((unsigned long long)KILO * KILO * KILO * KILO * KILO * KILO);
      p++;
      
    }
    
    prevll = ll;
    ll *= multiplier;
    if(ll / multiplier != prevll) {
      fflush(stdout);
      fprintf(stderr,"%s: multiply overflow", procname);
      fprintf(stderr,", string:'%s'", p2);
      fprintf(stderr,", digit:'");
      fprintfcharacter(stderr, p);
      fprintf(stderr,"'");
      fprintf(stderr,", value: %d", digit);
      fprintf(stderr,", base: %d", base);
      fprintf(stderr,", ll: %llu", prevll);
      fprintf(stderr,", multiplier: %llu", multiplier);
      fprintf(stderr,"\n");
      fflush(stderr);
    }
  
    if(*p == 'b' || *p == 'B') // remove last b (for bytes in 1tb, 1gb or 1mb)
      p++;
    
    totll += ll;

#ifdef DEBUG32
    fprintf(stderr,"string:'%s'", p2);
    fprintf(stderr,", base:%d(", base);
    readablelonglong(stderr, base);
    fprintf(stderr,")");
    fprintf(stderr,", multiplier:%llu(", multiplier);
    readablelonglong(stderr, multiplier);
    fprintf(stderr,")");
    fprintf(stderr,", prevll:%llu(", prevll);
    readablelonglong(stderr, prevll);
    fprintf(stderr,")");
    fprintf(stderr,", ll:%llu(", ll);
    readablelonglong(stderr, ll);
    fprintf(stderr,")");
    fprintf(stderr,", totll:%llu(", totll);
    readablelonglong(stderr, totll);
    fprintf(stderr,")");
    fprintf(stderr,"\n");
#endif
    
    if(prevp == p) // no progress
      break;
  }
  
  if(*p != '\0') {
    fflush(stdout);
    fprintf(stderr,"%s: illegal digit", procname);
    fprintf(stderr,", string:'%s'", p2);
    fprintf(stderr,", digit:'");
    fprintfcharacter(stderr, p);
    fprintf(stderr,"'");
    fprintf(stderr,", value: %d", digit);
    fprintf(stderr,", base: %d(", base);
    readablelonglong(stderr, base);
    fprintf(stderr,")");
    fprintf(stderr,"\n");
    fflush(stderr);
  }

  return(totll);
}

static long long lines = 10, plines = 0, ptotallines = 0;

void printcolumns(int offset, int base, int startwith, int columns, int *clines) // 2023 JariK
{
  //---------11111111112222222222333333333344444444445555555555666666666677
  //12345678901234567890123456789012345678901234567890123456789012345678901
  //=======================================================================
  //00000 86747100388687087416829400778878989202760594993130062980826178851
  //00001 36862623069143814543525935285460059703805218323534266522497684582
  
  int c, d, digit, nonzero;
  unsigned char digits[0x10] = "0123456789abcdef";

  *clines = 0;

  if(base == 2)
    c = 1 << 12;
  else if(base == 8)
    c = 01000000;
  else if(base == 10)
    c = 1000000;
  else if(base == 16)
    c = 0x1000000;
  else {
    fprintf(stdout,"%s: printcolumns():", procname);
    fprintf(stdout," illegal base %d", base);
    fprintf(stdout,"\n");
    exit(1);
  }

  for(;c >= 1; c/= base) { // lines, 100000's, 10000's, 1000's ... 1's
    if(columns < c)
      continue;

    for(d = 0; d < offset; d++)
      fprintf(stdout," "); // spaces in beginning of row

    nonzero = 0;
    
    for(d = startwith; d < columns + startwith; d++) { // column numbers
      digit = (d / c) % base;
      if(digit != 0)
	nonzero = 1;
      if(nonzero || c == 1)
	fprintf(stdout,"%c", digits[digit]); // nonzero digits after first nonzero
      else
	fprintf(stdout,"-"); // zeroes in beginning of row
    }
    fprintf(stdout,"\n");
    (*clines)++;
  }
  for(d = 0; d < offset; d++)
    fprintf(stdout," "); // spaces in beginning of row
  for(d = startwith; d < columns + startwith; d++)
    fprintf(stdout,"=");
  fprintf(stdout,"\n");
  (*clines)++;
}

#ifdef SAMPLE_HASH

void hashfinal2string(unsigned char *hashstring, unsigned char *final)
{
  for(int c = 0; c < HashLen; c++) {
    sprintf(hashstring + 2 * c, "%02x",final[c]);
  }
}

#endif

static int sample = 0, dieharder = 0; // randomness analysis

// try randomness analysis with: $ dieharder -a -g 201 -f newressusample1.rnd > newressusample1.rnd.dieharder

#include <signal.h>

typedef void (*sighandler_t)(int);

void sample_handler()
{
  static int times = 0;

  times++;

  fprintf(stderr,"Ctrl-c pressed %d times\n", times);
  if(times >= 10) {
    signal(SIGINT, SIG_DFL);
    exit(2);
  }
}

#define DEBUG39

#define FILESIZE 3 * 1024 * 1024 * 1024
#define BLOCKSIZE 384 * 1024
#define BLOCKS 8192

unsigned long long filesize = (long long)FILESIZE, blocks = BLOCKS;
unsigned int blocksize = BLOCKSIZE;
int filesize_set = 0, blocks_set = 0, blocksize_set = 0;
int purge = 1;
int purge_set = 0;
unsigned char *filesize_str = NULL, *blocks_str = NULL, *blocksize_str = NULL;


void dump_sample() // & dieharder analysis
{
#define STAT_LINE_SHOW_BLOCK 2 // on by default
#define STAT_LINE_SHOW_WRITTEN 2 // on by default
#define STAT_LINE_SHOW_SPEED 2 // on by default
#define STAT_LINE_SHOW_NOW 2 // on by default
#define STAT_LINE_SHOW_LEFT 2 // on by default
#define STAT_LINE_SHOW_READY 2 // on by default

#define STAT_LINE_LESS_WOBBLE 2 // on by default

#define TIMEFORMAT "%H:%M %Z"
#define TIMEFORMAT2 "%a %H:%M %Z"
#define DATEFORMAT "%a %d %b %Y"
    
  unsigned long long c;
  unsigned char *buffer, filename[128], command[1024];
  
  FILE *fp1;
  
  // find first available filename,
  // or empty "fileslot"
  
  for(c = 1; c <= 99999; c++) {

    sprintf(filename, samplefilename, c);
    
    if((fp1 = fopen(filename, "r")) != NULL) {
      fclose(fp1);
      continue;
    }

    unsigned char filename2[138];

    sprintf(filename2,"%s.dieharder",filename);
    if((fp1 = fopen(filename2, "r")) != NULL) {
      fclose(fp1);
      continue;
    }
#ifdef SAMPLE_HASH
    sprintf(filename2,"%s.sha256",filename);
    if((fp1 = fopen(filename2, "r")) != NULL) {
      fclose(fp1);
      continue;
    }
#endif

    break;
  }

  
  // calculate missing file size parameters
  
  if(!filesize_set)
    filesize = blocks * blocksize;
  else if(!blocksize_set) {
    blocksize = (filesize + blocks - 1) / blocks; // round up
    filesize = blocks * blocksize;
  } else if(!blocks_set) {
    blocks = (filesize + blocksize - 1)/ blocksize; // round up
    filesize = blocks * blocksize;
  }

#define EXFAT_FIX 2 // on for now
  
#ifdef EXFAT_FIX

  if(blocksize > KILO * KILO) { // max "blocksize" 1m (exfat)
    blocksize = KILO * KILO;
    blocks = (filesize + blocksize - 1) / blocksize;
    filesize = blocks * blocksize;
  }

#endif
  
  if(filesize != blocks * blocksize ||
     blocks < 1 || blocksize < 1) {
    fflush(stdout);
    fprintf(stderr,"%s: sample(): mismatched parameters", procname);
    fprintf(stderr,", blocks:%llu(", blocks);
    readablelonglong(stderr, blocks);
    fprintf(stderr,")");
    fprintf(stderr,", blocksize:%u(", blocksize);
    readablelonglong(stderr, blocksize);
    fprintf(stderr,")");
    fprintf(stderr,", filesize:%llu(", filesize);
    readablelonglong(stderr, filesize);
    fprintf(stderr,")");
    fprintf(stderr,"\n");
    fflush(stderr);
    exit(1);
  }
  
  if(filesize / blocks != blocksize) {
    fflush(stdout);
    fprintf(stderr,"%s: sample(): parameter overflow", procname);
    fprintf(stderr,", blocks:%llu(", blocks);
    readablelonglong(stderr, blocks);
    fprintf(stderr,")");
    fprintf(stderr,", blocksize:%u(", blocksize);
    readablelonglong(stderr, blocksize);
    fprintf(stderr,")");
    fprintf(stderr,", filesize:%llu(", filesize);
    readablelonglong(stderr, filesize);
    fprintf(stderr,")");
    fprintf(stderr,"\n");
    fflush(stderr);
    exit(1);
  }

#ifdef DEBUG39
  fprintf(stderr,"blocksize:%u(", blocksize);
  readablelonglong(stderr, blocksize);
  fprintf(stderr,")");
  fprintf(stderr,", blocks:%llu(", blocks);
  readablelonglong(stderr, blocks);
  fprintf(stderr,")");
  fprintf(stderr,", filesize:%llu(", filesize);
  readablelonglong(stderr, filesize);
  fprintf(stderr,")\n");
  fflush(stderr);
#endif

  if((buffer = malloc(blocksize)) == NULL) {
    fprintf(stderr,"%s: sample(): cannot allocate memory (buffer)\n", procname);
    exit(1);
  }
  
#define DEBUG40 2 // default on (for now)
    
#ifdef DEBUG40
  fprintf(stderr,"%s: sample(): writing file", procname);
  fprintf(stderr,", filename:%s", filename);
#ifndef SAMPLE_WRITE
  fprintf(stderr,", no write");
#endif
  fprintf(stderr,"\n");
#endif
  
#ifdef SAMPLE_HASH
  unsigned char digest[HashLen];
  HashCtx hash;
  
  HashInit(&hash); // calculate hash
#endif

  // variables for stat line fields

  time_t start, now, left, end, took;
  unsigned char speed[10], printspeed[10] = "";

#ifdef STAT_LINE_LESS_WOBBLE

  time_t prevend = -1;
  int countspeed = 0, countend = 0;
  unsigned char prevspeed[10] = "";
  unsigned int calculateleft;

#endif

  time_t printleft = -1, printend = -1;
  int leftprinted = 0;

  unsigned long long clim = 0;
  
  if((fp1 = fopen(filename, "a")) != NULL) {

    // starting time
    
    start = time(NULL);
    
    clim = (unsigned long long) filesize / blocksize;

#ifdef EXFAT_FIX
    int mbinblocks = (1 * KILO * KILO) / blocksize;
    if(mbinblocks == 0)
      mbinblocks = 1;
    fprintf(stderr,"mbinblocks:%d\n", mbinblocks);
#endif
    
    // you have to press ctrl-c 10 times

    signal(SIGINT, sample_handler);
    
    for(c = 0; c < clim; c++) {

      // current time
      
      now = time(NULL);

      // estimate for ending time
      
      end = (time_t)start + ((((double)now - start) / c) * clim);

#ifdef STAT_LINE_LESS_WOBBLE

      calculateleft = 0; 
      if(prevend / 60 == end / 60 &&
	 ++countend >= 30) { // 30 times same minutes --> minutes are real

	printend = end;
	calculateleft = 1;
      } else if(prevend / 60 != end / 60) { // minutes different
	prevend = end;
	countend = 0;
      }

#else
      printend = end;
#endif

      // estimate for time left

      left = printend - now;

#ifdef STAT_LINE_LESS_WOBBLE
      
      static int onlyseconds = 0;
      static long long cstart, cleft, cseconds;
      
      if(onlyseconds || (left > 0 && left < 60)) {
	if(!onlyseconds) {
	  cstart = c;
	  cleft = clim - c;
	  cseconds = left;
	  onlyseconds = 1;
	}

	left = (double) cseconds - ((((double) c - cstart) / cleft) * cseconds);
      }

      if(calculateleft)
	printleft = left;
      
#else
      printleft = left;
#endif

      // speed
      
      if(now - start > 1.0)
	stat_line_get_readable(speed, (unsigned long long)((double)c * blocksize / (now - start)) );
      else
	stat_line_get_readable(speed, (unsigned long long)((double)c * blocksize));

#ifdef STAT_LINE_LESS_WOBBLE

      if(!strcmp(prevspeed, speed) &&
	 ++countspeed >= 3) { // three times same speed --> speed is real
	strcpy(printspeed, speed);
      } else if(strcmp(prevspeed, speed)) {
	strcpy(prevspeed, speed);
	countspeed = 0;
      }

#else
      strcpy(printspeed, speed);
#endif
      
      // 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();
      
      int print = 0;
#ifdef STAT_LINE_SHOW_BLOCK
      
      stat_line_printf("%s", input_str);
      
      // print block from 0 to 8192
      
      stat_line_printf("block %d", c);
      print = 1;
#endif
      
#ifdef STAT_LINE_SHOW_WRITTEN
      
      // print written
      
      if(print)
	stat_line_printf(", ");
      
      stat_line_printf("written ");
      stat_line_readable((unsigned long)c * blocksize);
      print = 1;
#endif	  
      if(c > 0) {
	
#ifdef STAT_LINE_SHOW_SPEED
	
	// print speed
	
	if(printspeed[0] != '\0') {
	  if(print)
	    stat_line_printf(", ");

	  stat_line_printf("%s/sec", printspeed);
	  print = 1;
	}
#endif
	
#if defined STAT_LINE_SHOW_NOW ||			\
  defined STAT_LINE_SHOW_READY
	char timebuf[128];
#endif
	
#ifdef STAT_LINE_SHOW_READY
	char timebuf2[128];
#endif
	
#ifdef STAT_LINE_SHOW_NOW
	
	// print now
	
	if(print)
	  stat_line_printf(", ");
	
	stat_line_printf("now");
	strftime(timebuf, sizeof(timebuf), TIMEFORMAT2,
		 localtime((time_t *)&now));
	stat_line_printf(" %s", timebuf);
	print = 1;
#endif
	
#ifdef STAT_LINE_SHOW_LEFT
	
	// print left
	
	if(printleft > 0) {

	  if(print)
	    stat_line_printf(", ");

	  stat_line_printf("left");
	  stat_line_dhms(printleft);

	  leftprinted = 1;
	  print = 1;
	}
#endif
	
#ifdef STAT_LINE_SHOW_READY
	
	//unsigned long int end = (int)start + ((((double)now - start) / c) * clim);

	if(printend > 0 && leftprinted) {
	  if(print)
	    stat_line_printf(", ");
	  
	  stat_line_printf("ready at");
	  strftime(timebuf, sizeof(timebuf), DATEFORMAT,
		   localtime((time_t *) & end));
	  strftime(timebuf2, sizeof(timebuf2), DATEFORMAT,
		   localtime((time_t *) & now));
	  if(strcmp(timebuf, timebuf2)) {
	    stat_line_printf(" %s", timebuf);
	  }
	  
	  // print end time
	  
	  strftime(timebuf, sizeof(timebuf), TIMEFORMAT,
		   localtime((time_t *) & printend));
	  stat_line_printf(" %s", timebuf);
	  
	  print = 1;
	}
#endif
      }
      stat_line_end();
      stat_line_cursor_start();
      
      // do the work
      
      stat_line_cursor();

      memset(buffer, 0, blocksize);
            
      if(input == INPUT_RESSU) // ressu prod
	ressu_genbytes(blocksize, buffer);
      
      else if(input == INPUT_PSEUDORESSU) // pseudoressu
	pseudoressu_bytes(blocksize, buffer);
      
      else if(input == INPUT_RESSUTWIST) // ressu w twist
	ressutwist_bytes(blocksize, buffer);
	
      else if(input == INPUT_FASTRESSU) // ressu fast
	ressu_genbytes_fast(blocksize, buffer);
      
      else if(input == INPUT_SINGLERESSU) // ressu single
	ressu_genbytes_single(blocksize, buffer);
      
      else if(input == INPUT_STREAM) // ressu stream
	stream_bytes(blocksize, buffer);
      
      else if(input == INPUT_TEST) // test generator
	test_bytes(blocksize, buffer);
      
#ifdef FORT
      else if(input == INPUT_FORT) // ressu fort
	fort_random_data(blocksize, buffer);
      
      else if(input == INPUT_FORTXOR) // ressu fort
	fort_random_data_xor(blocksize, buffer);
      
#endif
#ifdef USE_RDRAND
      else if(input == INPUT_RDRAND) // intel rdrand
	rdrand_bytes(blocksize, buffer);
      
#endif
#ifdef USE_RDSEED
      else if(input == INPUT_RDSEED) // intel rdseed
	rdseed_bytes(blocksize, buffer);
      
#endif
      else if(input == INPUT_URANDOM) // urandom
	urandom_bytes(blocksize, buffer);
      
#ifdef USE_RANDOM
      else if(input == INPUT_RANDOM) // random
	random_bytes(blocksize, buffer);
      
#endif
#ifdef USE_DUMMY
      else if(input == INPUT_DUMMY) {
	// no generator
      }
#endif
      else {
	fprintf(stdout,"\n%s: mode '%d'(%s) not available\n",
		procname, input, input_str);
	exit(2);
      }
#ifdef SAMPLE_HASH
      HashUpdate(&hash, buffer, blocksize); // calculate hash
#endif
#ifdef SAMPLE_WRITE
      if(input != INPUT_DUMMY) {
	fwrite(buffer, 1, blocksize, fp1);
	
#ifdef SAMPLE_SYNC

#ifdef EXFAT_FIX
	if(c % mbinblocks == 0) { // fflush and sync (EXFAT)
	  fflush(fp1);
	  //sync();
	  fsync(fileno(fp1));
	}
#endif

#endif
      } // end of if(input != INPUT_DUMMY
#endif
    } // end of for(c = 0; c < clim; c++
#ifdef SAMPLE_HASH
    HashFinal(digest, &hash); // calculate hash
#endif

    fflush(fp1); // fflush and sync (EXFAT)
    //sync();
    fsync(fileno(fp1));
    
    fclose(fp1);
    
  } else { // if((fp1=fopen
    fprintf(stderr,"%s:", procname);
    fprintf(stderr," sample(): cannot open file");
    fprintf(stderr,", filename: %s", filename);
    fprintf(stderr,"\n");
    exit(2);
  }
  
  // remove last status line

  //stat_line_cursor_remove();
  
  stat_line_begin();
  
  stat_line_printf("\rwrote ");
  stat_line_readable((unsigned long)c * blocksize);
  
  stat_line_printf(", ");
  if(now - start >= 1.0)
    stat_line_readable((unsigned long)((double)c * blocksize / (now - start)) );
  else
    stat_line_readable((unsigned long)c * blocksize);
  stat_line_printf("/sec");

  end = time(NULL);
  took = end - start;
  stat_line_printf(", took");
  stat_line_dhms(took);
  
  //stat_line_printf(", done!");
  stat_line_end();
  stat_line_cursor_remove();
  fprintf(stderr,"\n");
  fflush(stderr);

#ifdef SAMPLE_HASH
  unsigned char filename2[138];
  unsigned char hashstring[2 * HashLen + 1];
  
  hashfinal2string(hashstring, digest);

  sprintf(filename2,"%s.sha256", filename);
  
  fprintf(stderr,"%s: sample(): hashing file", procname);
  fprintf(stderr,", filename:%s", filename);
  fprintf(stderr,", hashfilename:%s", filename2);
  fprintf(stderr,", sha256:%s\n", hashstring);
  
  if((fp1 = fopen(filename2, "a")) != NULL) {
    fprintf(fp1,"%s\n",hashstring);
    fclose(fp1);
  }
  sprintf(command,"sha256sum %s", filename);
  fprintf(stderr,"%s: sample(): checking sha256", procname);
  fprintf(stderr,", filename:%s", filename);
  fprintf(stderr,", command:'%s'\n", command);
  system(command);
#endif

  signal(SIGINT, SIG_DFL);

  free(buffer);
  
  if(dieharder) { // randomness analysis
    
    sprintf(command,"dieharder -a -g 201 -f %s > %s.dieharder 2>&1",
	    filename, filename);
    //sprintf(command,"dieharder -a -g 201 -f %s > %s.dieharder", filename, filename);
    fprintf(stderr,"%s: sample(): running dieharder \"%s\"\n",
	    procname, command);
    system(command);
    sprintf(command,"grep \"WEAK\\|FAILED\" %s.dieharder",
	    filename);
    fprintf(stderr,"%s: sample(): grepping dieharder problems \"%s\"\n"
	    , procname, command);
    system(command);

    long long weak;
    sprintf(command,"grep WEAK %s.dieharder", filename);
    weak = command_countlines(command);
    fprintf(stderr,"%s: sample(): calculating WEAK lines", procname);
    fprintf(stderr, ", lines = %lld", weak);
    fprintf(stderr,", \"%s\"\n", command);

    long long failed;
    sprintf(command,"grep FAILED %s.dieharder", filename);
    failed = command_countlines(command);
    fprintf(stderr,"%s: sample(): calculating FAILED lines", procname);
    fprintf(stderr, ", lines = %lld", failed);
    fprintf(stderr,", \"%s\"\n", command);

    sprintf(command,"grep rewound %s.dieharder | tail -n1",
	    filename);
    fprintf(stderr,"%s: sample(): grepping last rewound count \"%s\"\n",
	    procname, command);
    system(command);

    FILE *fp2;
    if((fp2 = fopen("newressudieharder.skk", "a")) != NULL) { //
      fprintf(fp2,"'input' = \"%s\"", input_str);
      if(filesize_str != NULL)
	fprintf(fp2,", 'filesize' = \"%s\"", filesize_str);
      if(blocksize_str != NULL)
	fprintf(fp2,", 'blocksize' = \"%s\"", blocksize_str);
      if(blocks_str != NULL)
	fprintf(fp2,"'blocks' = \"%s\"", blocks_str);
      fprintf(fp2,", 'filename' = \"%s\"", filename);
      fprintf(fp2,", 'weak' = \"%lld\"", weak);
      fprintf(fp2,", 'failed' = \"%lld\"", failed);
      fprintf(fp2,"\n");
      fclose(fp2);
    }
    
    if(purge) {
      fprintf(stderr,"%s: sample(): removing file", procname);
      fprintf(stderr," %s\n", filename);
      unlink(filename);
    }
    exit(0);
  }
}

static unsigned long limit, word, uppercase = 0;
static unsigned char *digits = "0123456789", character[32];
static unsigned char digitstemp[256], *digitsext = NULL;
static unsigned char linenobuf[1024];
static unsigned char *stream_default_key  = "kalakala";
static unsigned char *key = NULL;
static int print_statline = 0, columns = 0, flagdigits = 0;

int help = 0;

//
//  utf8 functions
//

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

  if(flagdigits == 1) // semicolon separated (flags)
    return(characters / 2);
  else
    return(characters);
}

int utf8strings(unsigned char *buf)
{
  int strings = 0;

  unsigned char *p = buf;
  while(*p != '\0') {
    if(*p == ';')
      strings++;
    p++;
  }

  return(strings);
}

#define aDEBUG47 2

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

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

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

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

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

int utf8stringlengths(unsigned char *buf)
{
  int lengths = -1, len;
  unsigned char *p = buf;

  // return(0);
  
  len = 0;
  while(*p != '\0') {
    if(*p == ';') {
      p++;
      if(lengths == -1)
	lengths = len;
      else if(lengths != len)
	lengths = 0;
      len = 0;
    }
    p++;
    len++;
  }

  if(lengths == -1)
    lengths = 0;
  else
    lengths++; // count semicolon also

  return(lengths);
}

#define aDEBUG42 2

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

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

#ifdef DEBUG42
  int c, print;

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

  fprintf(stdout,", bin:");

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

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

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

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

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

#ifdef NOTUSED

#define aDEBUG43 2

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

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

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

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

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

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

#endif 

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

#define aDEBUG55 2

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

  // find first byte of character

  if(characterlengths != 0)
    p += characterlengths * n;
  else {
    if(flagdigits) { // semicolon separated (flags)
      while(*p != '\0') {
	if(d == n)
	  break;
	if(*p == ';') {
	  d++;
	}
	p++;
      }
    } else { // else if(flagdigits)
      while(*p != '\0') {
	if(*p < 0x80 || // ascii char
	   *p > 0xbf) { // first utf8 char
	  if(d == n)
	    break;
	  d++;
	}
	p++;
      }
    }
  }
  //fprintf(stdout,"%d***%s\n",d,p);

  // copy first byte and rest
  // of character

  if(flagdigits) { // semicolon separated (flags)
    while(*p != '\0') {
      if(*p == ';')
	break;
      *q++ = *p++; // copy first byte
    }
  } else {
    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';
  //fprintf(stdout,"character: `%s`\n", buf);

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

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

  // find first byte of character

  if(characterlengths != 0)
    p += characterlengths * n;
  else {
    while(*p != '\0') {
      if(*p < 0x80 || // ascii char
	 *p > 0xbf) { // first utf8 char
	if(d == n)
	  break;
	d++;
      }
      p++;
    }
  }
  //fprintf(stdout,"%d***%s\n",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 DEBUG56
  fprintf(stdout,"%s: utf8getcharacter:", procname);
  fprintf(stdout," string: %s", string);
  fprintf(stdout,", n: %d", n);
  fprintf(stdout,", character: %s", buf);
  fprintf(stdout,", characterlengths: %d", characterlengths);
  fprintf(stdout,"\n");
#endif
}

#define aDEBUG56 2

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

  // find first byte of character

  p += n;

  // copy first byte and rest
  // of character
  
  if(*p != '\0') {
    *q++ = *p; // copy first byte
    if(*p > 0xbf) { // if first is utf8 char
      p++;
      for(;;) {
	if(*p > 0xbf || // first utf8 char
	   *p < 0x80 || // ascii char
	   *p == '\0')  // end of file
	  break;
	*q++ = *p++; // copy rest of the bytes
      }
    }
  }
  *q = '\0';
  
#ifdef DEBUG56
  fprintf(stdout,"%s: utf8getcharacter:", procname);
  fprintf(stdout," string: %s", string);
  fprintf(stdout,", n: %d", n);
  fprintf(stdout,", character: %s", buf);
  fprintf(stdout,", characterlengths: %d", characterlengths);
  fprintf(stdout,"\n");
#endif
}

#define aDEBUG45 2

int digitscount = 10;

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

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

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

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

#ifdef DEBUG45
  fprintf(stdout,"]\n%s: out_word a:", procname);
  fprintf(stdout," orig string: %s", string);
#endif
  
  // reverse string

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

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

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

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

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

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

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

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

  *word = 0;
  d = utf8characters(buf);
  if(flagdigits) // semicolon separated (flags)
    f = utf8strings(digits);
  else // not
    f = utf8characters(digits);
    
  for(c = 0; c < d; c++) {
    utf8getcharacter(sizeof(character2), character2, c, buf);
    ok = 0;
    for(e = 0; e < f; e++) {
      utf8getcharactersemi(sizeof(character), character, e, digits); // semi added
      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);
    }
  }

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

}

//
// lotto functions
//

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

#define DEBUG59 2 // default is on for now

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

#ifdef DEBUG59
static int utf8 = 0;
#endif

#ifdef DEBUG51
static int limitsize = 0;
#endif

#define aDEBUG51 2

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

//
// stdout printing routines
//

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

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

static int calc_spaces()
{
  int spaces;

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

  return(spaces);
}

static void print_spaces()
{
  int sp;

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

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

#ifdef SHA256

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

#endif

#define aDEBUG55 2
#define aDEBUG57 2

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

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

#ifdef DEBUG57
  fprintf(stdout,"digitssize:%d\n", digitssize);
  fflush(stdout);
#endif

  unsigned char *prevdigits = *digits;
  *digits = realloc(*digits, digitssize + 1);
  if(prevdigits == NULL)
    **digits = '\0';

  strcat(*digits, str);
#ifdef DEBUG55
  fprintf(stdout,"\nnewdigits %s", *digits);
  fprintf(stdout,"\n");
#endif
}

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

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

#ifdef DEBUG57
  fprintf(stdout,"digitssize:%d\n", digitssize);
  fflush(stdout);
#endif
  
  unsigned char *prevdigits = *digits;
  *digits = realloc(*digits, digitssize + 1);
  if(prevdigits == NULL)
    **digits = '\0';

  for(c = start; c <= end; c++) {
    codetoutf8(buffer, c);
    strcat(*digits, buffer);
  }
#ifdef DEBUG55
  fprintf(stdout,"\nnewdigits %s", *digits);
  fprintf(stdout,"\n");
#endif
}

void strtolower(unsigned char *buf)
{
  while(*buf != '\0') {
    *buf = tolower(*buf);
    buf++;
  }
}

void strtoupper(unsigned char *buf)
{
  while(*buf != '\0') {
    *buf = toupper(*buf);
    buf++;
  }
}

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

int newressu_getname(int length, unsigned char *name, unsigned char **p2)
{
  int len, ok = 0;
  unsigned char *n, *p;

  p = *p2;
  n = name;
  len = 0;
  
  while(ispunct(*p) || isblank(*p)) {
    p++;
  }

  while(*p != '\0' && !ispunct(*p) && !isblank(*p)) {
    if(len < length) {
      *n++ = *p;
      ok = 1;
    }
    p++;
    len++;
  }
  *n = '\0';
  
  *p2 = p;

  return(ok);
}

void newressu_base(int base2)
{
  base = base2;
  
  digits = malloc(base + 1);
  strncpy(digits, basedigits, base);
  if(uppercase == 1 && base <= 36) { // if lowercase only ([0-9] + [a-z], 10 + 26
    for(int c = 0; c < base; c++) { // to uppercase
      if(digits[c] >= 'a' && digits[c] <= 'z')
	digits[c] = digits[c] - 'a' + 'A';
      else if(digits[c] >= 'A' && digits[c] <= 'Z')
	digits[c] = digits[c] - 'A' + 'a';
    }
  }
  if(digitsext != NULL)
    free(digitsext);
  digitsext = digits;
  
  digits[base] = '\0';
}

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

#define aDEBUG50 2
#define aDEBUG71 2

void call_main(char *);

#define aCALL_MAIN 2

#ifdef CALL_MAIN

#define DEBUG99 2

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

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

  size = 0;

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

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

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

#endif // #ifdef DEBUG99

  main2(count, argv);
}

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

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

#endif // #ifdef CALL_MAIN

#ifdef CALL_MAIN

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

#else // #ifdef CALL_MAIN

static unsigned char programfiledigest[HashLen];

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

#ifdef DEBUG04

  for(int c = 0; c < RESSUT_BYTES; c++)
    count[c] = 0;

#endif
  
#define aDEBUG67 2

#ifdef DEBUG67
  unsigned long long big = 0xffffffffffffffff;
  fprintf(stdout,"0x%llx\n", big);
  fprintf(stdout,"0o%llo\n", big);
  fprintf(stdout,"0d%llu\n", big);
  fprintf(stdout,"0x%llx\n", big);
  for(int base = 2; base <= 64; base++) {
    fprintf(stdout,"base:%d, ", base);
    fprintfbase(stdout, big, base);
    fprintf(stdout,"\n");
  }
  fprintf(stdout,"number");
  for(int base = 2; base <= 64; base++) {
    fprintf(stdout," %d",base);
  }
  fprintf(stdout,"\n");
  for(unsigned long long ll = 0; ll < 1048576; ll++) {
  //for(unsigned long long ll = 4096; ll > 0; ll+=4096) {
  //for(unsigned long long ll = 1; ll != 0; ll *= 2) {
    fprintf(stdout,"%llu:", ll);
    for(int base = 2; base <= 64; base++) {
      fprintf(stdout," ");
      fprintfbase(stdout, ll, base);
    }
    fprintf(stdout,"\n");
  }
  fflush(stdout);
#endif
  
#ifdef DEBUG31
  for(c = 20; c > 1; c--) {
    stat_line_begin();
    for(d = 0; d < c; d++) {
      stat_line_printf("+");
    }
    stat_line_end();
  }
#endif

#define aDEBUG69
  
#ifdef DEBUG69
  stream_open(0, "⚀⚁⚂⚃⚄⚅"); // utf8  
  stream_open(0, "Hasta la vista, baby (Arnold Schwarzenegger, T2)"); // pass phrase
  stream_open(0, "abcdefghijklmnopqrstuvwxyz" // long password (key)
	      "abcdefghijklmnopqrstuvwxyz"
	      "abcdefghijklmnopqrstuvwxyz"
	      "abcdefghijklmnopqrstuvwxyz"
	      "abcdefghijklmnopqrstuvwxyz"); 
  stream_open(5, "\x00\x01\x02\x03\x04"); // binary string with length
#endif
  
#define aDEBUG73

#ifdef DEBUG73

  unsigned char buf[128];
  command_readline("date +%Y%m%d%H%M", sizeof(buf), buf);
  fprintf(stdout,"readline date '%s'\n",buf);

#endif
  
#define aDEBUG74

#ifdef DEBUG74

  char *ds[] = { "A", "B", "T", "J", "X", "KH", "D", "R", "S", "SH",
		 "DH", "C", "G", "F", "Q", "K", "L", "M", "N", "W",
		 "H", "Y", "E", "O" }; // --som
  for(c= 0; c < sizeof(ds) / sizeof(ds[0]); c++)
    fprintf(stdout," `%s`",ds[c]);
  fprintf(stdout,"\n");

#endif

  unsigned char *flagchars[] =
    { "🇦", "🇧", "🇨", "🇩", "🇪", "🇫", "🇬", "🇭", "🇮", "🇯",
      "🇰", "🇱", "🇲", "🇳", "🇴", "🇵", "🇶", "🇷", "🇸", "🇹",
      "🇺", "🇻", "🇼", "🇽", "🇾", "🇿" };
  
#define DEBUG70

#ifdef DEBUG70

  for(c = 0; c < sizeof(idsflags) / sizeof(idsflags[0]); c++) {
    unsigned char buffer20[20];
    unsigned char buffer128[128];
    sprintf(buffer128," %s", idsflags[c].flags);
    unsigned char *p = buffer128;
    if(idsflags[c].id[0] !=  '\0')
      continue;
    buffer20[0] = '\0';
    for(d = 0; *p != '\0'; d++) {
      if(isalpha(*p) == 0 &&
	 islower(*(p + 1)) != 0 &&
	 islower(*(p + 2)) != 0 &&
	 isalpha(*(p + 3)) == 0) {
	strcat(buffer20, flagchars[toupper(*(p + 1)) - 'A']);
	strcat(buffer20, flagchars[toupper(*(p + 2)) - 'A']);
      }
      p++;
    }
    fprintf(stdout,"  { \"%s\"", buffer20);
    fprintf(stdout,", \"%s: %s\" },\n", buffer20, idsflags[c].flags);
    fflush(stdout);
  }
  
#endif

#define aDEBUG68 // default is off

#ifdef DEBUG68

  unsigned char *hindi = malloc(8192);
  digits_add_limits(&hindi,0x900,0x97f);   // Hindi (0900-097f)
  unsigned char *h;
  int first;

  fprintf(stdout,"hindi1:");
  h = hindi;
  first = 1;
  while(*h != '\0') {
    if(!first)
      fprintf(stdout, " ");
    fputc(*h, stdout); // print first char
    if(*h > 0xbf) { // first char utf8
      h++;
      for(;;) { // print rest of the utf8 chars
	if(*h > 0xbf || // new utf8 character
	   *h < 0x80 || // ascii character
	   *h == '\0') // end of string
	  break;
	fputc(*h, stdout);
	h++;
      }
    }
    first = 0;
  }
  fprintf(stdout,"\n");
  free(hindi);
  
#endif

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

  gent_clear();

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

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

#define aDEBUG72 // default is on
		     
#ifdef DEBUG19	
  int flagint = 1;
#endif

  idsall = 1;
  newressu_toggleflag("ALL");
  int flagsinit = 0;
		     
  //
  // look thru command line parameters
  // for flags option
  //

  int flagflags = 0;
  for(c = 1; c < argc; c++) {
    if(!strcmp("--flags", argv[c])) { // not ready
      flagflags = 1;
    }
  }

  //
  // look thru command line parameters
  //

  for(c = 1; c < argc; c++) {

    int ok = 1;

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

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

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

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

      } else if(!strcmp("--columns", argv[c])) {
	columns = !columns; // print column numbers
	
      } else if(!strncmp("--space", argv[c], 7)) {
	if(*(argv[c] + 7) != '\0' && atoi(argv[c] + 7) > 1) {
	  sspace = atoi(argv[c] + 7);
	} else if(c + 1 < argc && atoi(argv[c + 1]) > 1) {
	  sspace = atoi(argv[c + 1]);
	  c++;
	} else {
	  sspace = !sspace;
	}

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

      } else if(!strcmp("--sample", argv[c])) {
	sample = !sample;
	
      } else if(!strcmp("--dieharder", argv[c])) {
	dieharder = !dieharder;
	
      } else if(!strcmp("--purge", argv[c])) {
	purge = !purge;
	purge_set = 1;
	
      } else if(!strcmp("--save", argv[c])) {
	purge = 1;
	purge_set = 1;
	
#ifdef DEBUG59
      } else if(!strcmp("--utf8", argv[c])) {
	utf8 = !utf8;
	
#endif
      } else if(!strncmp("--filesize", argv[c], 10)) {
	if(*(argv[c] + 10) != '\0') {
	  filesize_str = argv[c] + 10;
	  filesize = getlonglong(filesize_str);
	} else if(c + 1 < argc) {
	  filesize_str = argv[c + 1];
	  filesize = getlonglong(filesize_str);
	  c++;
	}
	filesize_set = 1;

      } else if(!strncmp("--blocksize", argv[c], 11)) {
	if(*(argv[c] + 11) != '\0') {
	  blocksize_str = argv[c] + 10;
	  blocksize = getlonglong(blocksize_str);
	} else if(c + 1 < argc) {
	  blocksize_str = argv[c + 1];
	  blocksize = getlonglong(blocksize_str);
	  c++;
	}
	blocksize_set = 1;
	
      } else if(!strncmp("--blocks", argv[c], 8)) {
	if(*(argv[c] + 8) != '\0') {
	  blocks_str = argv[c] + 10;
	  blocks = getlonglong(blocks_str);
	} else if(c + 1 < argc) {
	  blocks_str = argv[c + 1];
	  blocks = getlonglong(blocks_str);
	  c++;
	}
	blocks_set = 1;

      } else if(!strcmp("--copyright", argv[c]) ||
	 !strcmp("--version", argv[c])) {
	newressu_version();
#ifdef SHA256
	fprintf(stderr,"\nsha256(");
	for(int c = 0; c < HashLen; c++) {
	  fprintf(stderr, "%02x", programfiledigest[c]);
	}
#endif
	fprintf(stderr, ")\n\n");
	help = 0;
	exit(1);
	
      } else if(!strcmp("--webversion", argv[c])) {
	fprintf(stderr, "%s", programname); // touch these outside MAIN
	fprintf(stderr,", sha256(");
	for(int c = 0; c < HashLen; c++) {
	  fprintf(stderr, "%02x", programfiledigest[c]);
	}
	fprintf(stderr,")\n");
	exit(0);

      } else if(!strcmp("--lineno", argv[c])) {
	slineno = !slineno; // print line number
	
      } else if(!strcmp("--crlf", argv[c])) {
	scrlf = !scrlf;
	slineno = 0;

#ifdef DEBUG19	
      } else if(!strcmp("--int", argv[c])) {
	flagint = !flagint;

#endif
      } else if(!strcmp("--rand", argv[c])) {
	digits = "0123456789";
	sspace = 2;
	snewline = 5;
	slineno = 1; // print line number
	words = 10;
	chars = 0;
	limit = 100000;
	//size = 5;
	//lines = 20000;
	
      } else if(!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(!strncmp("--lim", argv[c], 5)) { // limit of word
	if(*(argv[c] + 5) != '\0') {
	  in_word(&limit, digits, argv[c] + 5);
	} else if(c+1 < argc) {
	  in_word(&limit, digits, argv[c + 1]);
	  c++;
	}
	//fprintf(stdout,"limit:%lu, %ld bytes\n", limit, sizeof(limit));
	if(sspace < 1 ) // 23.6.2021
	  sspace = 1;

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

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

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

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

      } else if(!strncmp("--words", argv[c], 7)) { // words per line
	if(*(argv[c] + 7) != '\0') {
	  words = atoi(argv[c] + 7);
	} else if(c + 1 < argc) {
	  words = atoi(argv[c + 1]);
	  c++;
	}
	if(words < 1)
	  words = 1;
	chars = 0;
	screen = 0;
	
      } else if(!strncmp("-w", argv[c], 2)) { // words per line
	if(*(argv[c] + 2) != '\0') {
	  words = atoi(argv[c] + 2);
	} else if(c + 1 < argc) {
	  words = atoi(argv[c + 1]);
	  c++;
	}
	if(words < 1)
	  words = 1;
	chars = 0;
	screen = 0;
	
      } else if(!strncmp("-c*", argv[c], 3)) {
	struct winsize w;
	ioctl(0, TIOCGWINSZ, &w);
	chars = w.ws_col;
	words = 0;

      } else if(!strncmp("-l*", argv[c], 3)) {
	struct winsize w;
	ioctl(0, TIOCGWINSZ, &w);
	lines = w.ws_row-1;

      } else if(!strncmp("--screen", argv[c],8)) { // screenful
	struct winsize w;
	ioctl(0, TIOCGWINSZ, &w);
	chars = w.ws_col;
	lines = w.ws_row-1;
	words = 0;
	screen = 1;
	
      } else if(!strncmp("--chars", argv[c], 7)) {  // characters per line
	if(*(argv[c] + 7) != '\0') {
	  chars = atoi(argv[c] + 7);
	} else if(c + 1 < argc) {
	  chars = atoi(argv[c + 1]);
	  c++;
	}
	if(chars < 1)
	  chars = 72;
	words = 0;
	screen = 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++;
	}
	if(chars < 1)
	  chars = 72;
	words = 0;
	screen = 0;
	
      } else if(!strncmp("--lines",argv[c], 7)) { // lines
	if(*(argv[c] + 7) != '\0') {
	  lines = atoll(argv[c] + 7);
	} else if(c + 1 < argc) {
	  lines = atoll(argv[c + 1]);
	  c++;
	}
	screen = 0;
	
      } else if(!strncmp("-l",argv[c], 2)) { // lines
	if(*(argv[c] + 2) != '\0') {
	  lines = atoll(argv[c] + 2);
	} else if(c + 1 < argc) {
	  lines = atoll(argv[c + 1]);
	  c++;
	}
	screen = 0;
	
      } else if(!strcmp("--otp1", argv[c]) || // see: one time pad
		!strcmp("--otp5n", argv[c]) || // digits length 5
		!strcmp("--onetimepad1", argv[c])) {		
	digits = "0123456789";
	sspace = 1;
	charspaces = 0;
	charwidth = 1;
	size = 5;
	type = 0;
	
      } else if(!strcmp("--otp2", argv[c]) || // uppercase alpha length 5
		!strcmp("--otp5a", argv[c]) ||		
		!strcmp("--onetimepad2", argv[c]) ||
		!strcmp("--diana", argv[c]) ) { // see: diana cipher, diana table
	digits = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
	sspace = 1;
	charspaces = 0;
	charwidth = 1;
	size = 5;
	type = 0;
	
      } else if(!strcmp("--otp3", argv[c]) || // uppercase alpha length 4
		!strcmp("--otp4a", argv[c]) ||		
		!strcmp("--onetimepad3", argv[c])) {
	digits = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
	sspace = 1;
	charspaces = 0;
	charwidth = 1;
	size = 4;
	type = 0;

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

	if(*(argv[c] + 6) != '\0') {
	  newressu_base(atoi(argv[c] + 6));
	} else if(c + 1 < argc) {
	  newressu_base(atoi(argv[c + 1]));
	  c++;
	}
	if(base < 2 || base > 64) {
	  fprintf(stderr,"%s: base must be from 2 to 64 (%s)\n", procname, argv[c]);
	  exit(2);
	}
      } else if(!strncmp("--BASE", argv[c], 6)) {

	if(*(argv[c] + 6) != '\0') {
	  uppercase = 1;
	  newressu_base(atoi(argv[c] + 6));
	} else if(c + 1 < argc) {
	  uppercase = 1;
	  newressu_base(atoi(argv[c + 1]));
	  c++;
	}
	if(base < 2 || base > 64) {
	  fprintf(stderr,"%s: base must be from 2 to 64 (%s)\n", procname, argv[c]);
	  exit(2);
	}
	
      } else if(!strcmp("-b", argv[c]) ||
		!strcmp("--bin", argv[c]) ||
		!strcmp("--1bit", argv[c]) ||
		!strcmp("--1bits", argv[c])) {

	newressu_base(2);
	
      } else if(!strcmp("--2bits", argv[c])) {

	newressu_base(4);

      } else if(!strcmp("-o", argv[c]) ||
		!strcmp("--oct", argv[c]) ||
		!strcmp("--3bits", argv[c]) ) {

	newressu_base(8);

      } else if(!strcmp("-d", argv[c]) ||
		!strcmp("--dec", argv[c])) {

	newressu_base(10);

      } else if(!strcmp("-x", argv[c]) ||
		!strcmp("--hex", argv[c]) ||
		!strcmp("--4bits", argv[c])) {

	newressu_base(16);

      } else if(!strcmp("-X", argv[c]) ||
		!strcmp("--HEX", argv[c]) ||
		!strcmp("--4BITS", argv[c])) {

	newressu_base(16);
	uppercase = 1;

      } else if(!strcmp("-5", argv[c]) ||
		!strcmp("--5bits", argv[c])) {

	newressu_base(32);
	base = 32;
	
      } else if(!strcmp("--5BITS", argv[c])) {

	newressu_base(32);
	uppercase = 1;
	
      } else if(!strcmp("-2", argv[c]) ||
		!strcmp("--6bits", argv[c])) {

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

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

      } else if(!strcmp("--numeric", argv[c]) ||
		!strcmp("-11", argv[c])) {
	digits =
	  "0123456789";

	charspaces = 0;
	charwidth = 1;
	size = 5;
	type = 10;

      } else if(// !strcmp("--ALPHA", argv[c]) ||
		!strcmp("--LATIN", argv[c]) ||
		!strcmp("-12", argv[c]) ) {
	digits =
	  "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

	charspaces = 0;
	charwidth = 1;
	size = 5;

      } else if( //!strcmp("--alpha", argv[c]) ||
		!strcmp("--latin", argv[c]) ||
		!strcmp("-13", argv[c])) {
	digits =
	  "abcdefghijklmnopqrstuvwxyz";

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

      } else if(!strcmp("-13v1", argv[c])) {
	digits =
	  "abcdefghijklmnopqrstuvwxyz" \
	  "aeiouy"; // extra vowels

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

      } else if(!strcmp("-13v2", argv[c])) {
	digits =
	  "abcdefghijklmnopqrstuvwxyz"
	  "aeiouy" // extra vowels
	  "aeiouy"; // extra vowels

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

      } else if(!strcmp("-13v3", argv[c])) {
	digits =
	  "abcdefghijklmnopqrstuvwxyz"
	  "aeiouy" // extra vowels
	  "aeiouy" // extra vowels
	  "aeiouy"; // extra vowels

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

      } else if(!strcmp("-13v4", argv[c])) {
	digits =
	  "abcdefghijklmnopqrstuvwxyz"
	  "aeiouy" // extra vowels
	  "aeiouy" // extra vowels
	  "aeiouy" // extra vowels
	  "aeiouy"; // extra vowels

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

      } else if(!strcmp("--ind", argv[c]) // not ready
	     || !strcmp("--hin", argv[c])) { // Hindi alphabet
	//unsigned char *signs = "ऀँंःऺऻ़ऽािीुूृॄॅॆेैॉॊोौ्ॎॏॕॖॗॢॣ॒॑॓॔";
	digits =
	  "ऄअआइईउऊऋऌऍऎएऐऑऒओऔकखगघङचछजझञटठडढण"
	  "तथदधनऩपफबभमयरऱलळऴवशषसहऽॐक़ख़ग़ज़ड़"
	  "ढ़फ़य़ॠॡ।॥०१२३४५६७८९॰ॱॲॳॴॵॶॷॸॹॺॻॼॽॾ"
	  "ॿ"; // 97 characters

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

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

      } else if(!strcmp("--som", argv[c]) ||
		!strcmp("--SOM", argv[c]) ||
		!strcmp("--SO", argv[c])) { // Somalia alphabet

#define aNEWSOM 2
	
#ifdef NEWSOM
	digits = NULL;
	// A, B, T, J, X, KH, D, R, S, SH, DH, C, G, F, Q, K, L, M, N, W, H, Y, E, and O
	digits_add_string(&digits, "A;");
	digits_add_string(&digits, "B;");
	digits_add_string(&digits, "T;");
	digits_add_string(&digits, "J;");
	digits_add_string(&digits, "X;");
	digits_add_string(&digits, "KH;");
	digits_add_string(&digits, "D;");
	digits_add_string(&digits, "R;");
	digits_add_string(&digits, "S;");
	digits_add_string(&digits, "SH;");
	digits_add_string(&digits, "DH;");
	digits_add_string(&digits, "C;");
	digits_add_string(&digits, "G;");
	digits_add_string(&digits, "F;");
	digits_add_string(&digits, "Q;");
	digits_add_string(&digits, "K;");
	digits_add_string(&digits, "L;");
	digits_add_string(&digits, "M;");
	digits_add_string(&digits, "N;");
	digits_add_string(&digits, "W;");
	digits_add_string(&digits, "H;");
	digits_add_string(&digits, "Y;");
	digits_add_string(&digits, "E;");
	digits_add_string(&digits, "O;");

	flagdigits = 2; // semicolon separated (flags)
	
#else
	// missing KH SH DH
	digits = "ABTJXDRSCGFQKLMNWHYEO";
#endif
	charspaces = 0;
	charwidth = 1;
	size = 8;
	type = 0;

      } else if(!strcmp("--braille", argv[c])) { // Braille alphabet
	digits = // 0x2800 - 0x2900
	  "⠀⠁⠂⠃⠄⠅⠆⠇⠈⠉⠊⠋⠌⠍⠎⠏⠐⠑⠒⠓⠔⠕⠖⠗⠘⠙⠚⠛⠜⠝⠞⠟⠠⠡⠢⠣⠤⠥⠦⠧⠨⠩⠪⠫⠬⠭⠮⠯⠰⠱⠲⠳⠴⠵⠶⠷⠸⠹⠺⠻⠼⠽⠾⠿"
	  "⡀⡁⡂⡃⡄⡅⡆⡇⡈⡉⡊⡋⡌⡍⡎⡏⡐⡑⡒⡓⡔⡕⡖⡗⡘⡙⡚⡛⡜⡝⡞⡟⡠⡡⡢⡣⡤⡥⡦⡧⡨⡩⡪⡫⡬⡭⡮⡯⡰⡱⡲⡳⡴⡵⡶⡷⡸⡹⡺⡻⡼⡽⡾⡿"
	  "⢀⢁⢂⢃⢄⢅⢆⢇⢈⢉⢊⢋⢌⢍⢎⢏⢐⢑⢒⢓⢔⢕⢖⢗⢘⢙⢚⢛⢜⢝⢞⢟⢠⢡⢢⢣⢤⢥⢦⢧⢨⢩⢪⢫⢬⢭⢮⢯⢰⢱⢲⢳⢴⢵⢶⢷⢸⢹⢺⢻⢼⢽⢾⢿"
	  "⣀⣁⣂⣃⣄⣅⣆⣇⣈⣉⣊⣋⣌⣍⣎⣏⣐⣑⣒⣓⣔⣕⣖⣗⣘⣙⣚⣛⣜⣝⣞⣟⣠⣡⣢⣣⣤⣥⣦⣧⣨⣩⣪⣫⣬⣭⣮⣯⣰⣱⣲⣳⣴⣵⣶⣷⣸⣹⣺⣻⣼⣽⣾⣿";
	charspaces = 0;
	charwidth = 1;
	size = 8;
	type = 0;

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

      } else if(!strcmp("---champagne", argv[c])) { // Gothic alphabet
        digits = "🍾🥂🥂🥂🥂"; // 🥤🥤🥤🥤🥤🥤🥤🥤"; // 
	charspaces = 0;
	charwidth = 2;
        size = 8;
	type = 0;

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

	charspaces = 1;
	charwidth = 2;
	size = 5;
	type = 0;

      } else if(!strcmp("--cardsuits", argv[c])) {
	digits=
	  "♠♡♢♣♤♥♦♧";

	charspaces = 1;
	charwidth = 2;
	size = 5;
	type = 0;

      } else if(!strcmp("--chessmen", argv[c])) {
	digits=
	  "♔♕♖♗♘♙♚♛♜♝♞♟";

	charspaces = 1;
	charwidth = 2;
	size = 5;
	type = 0;

      } else if(!strcmp("--dice", argv[c])) {
	digits=
	  "⚀⚁⚂⚃⚄⚅";

	charspaces = 1;
	charwidth = 2;
	size = 5;
	type = 0;

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

	charspaces = 1;
	charwidth = 2;
	size = 5;
	type = 0;

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

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

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

      } else if(!strcmp("--pattern2", argv[c])) {
	digits=
	  "◜◝◞◟";

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

      } else if(!strcmp("--pattern3", argv[c])) {
	digits=
	  "\\/";

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

      } else if(!strcmp("--pattern4", argv[c])) {
	digits=
	  "◸◹◺◿";

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

      } else if(!strcmp("--pattern5", argv[c])) {
	digits=
	  "▧▨";

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

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

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

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

#endif
      } else if(!strcmp("--clockstream", argv[c])) { // clockstream
	input = INPUT_CLOCK;
	input_str = argv[c] + 2;
	
      } else if(!strcmp("--test", argv[c])) { // test generator
	input = INPUT_TEST;
	input_str = argv[c] + 2;
	
      } else if(!strcmp("--ressu", argv[c])) {
	input = INPUT_RESSU;
	input_str = argv[c] + 2;
	
      } else if(!strcmp("--pseudoressu", argv[c])) {
	input = INPUT_PSEUDORESSU;
	input_str = argv[c] + 2;

      } else if(!strcmp("--ressutwist", argv[c]) // JariK 2023
		|| !strcmp("--ressuwtwist", argv[c])
	        || !strcmp("--twist", argv[c]) ) {
	input = INPUT_RESSUTWIST;
	input_str = argv[c] + 2;

      } else if(!strcmp("--fast", argv[c])
		|| !strcmp("--fastressu", argv[c]) ) {
	input = INPUT_FASTRESSU;
	input_str = argv[c] + 2;

      } else if(!strcmp("--single", argv[c])
		|| !strcmp("--singleressu", argv[c]) ) {
	input = INPUT_SINGLERESSU;
	input_str = argv[c] + 2;

      } else if(!strcmp("--stream", argv[c])) {
	input = INPUT_STREAM;
	input_str = argv[c] + 2;
	
      } else if(!strncmp("--key", argv[c], 5)) {
	key = NULL;
	if(*(argv[c] + 5) != '\0') {
	  key = argv[c] + 5;
	} else if(c + 1 < argc) {
	  key = argv[c + 1];
	  c++;
	}
	if(key == NULL || utf8characters(key) < 1) {
	  fprintf(stderr,"%s: not enough key characters \"%s\"\n", procname, argv[c]);
	  exit(1);
	}
	//size = 1;

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

	if(fixedclockchainlength < 5 ||
	   fixedclockchainlength > 255)
	  fixedclockchainlength = FIXEDCLOCKCHAINLENGTH;

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

#ifdef FORT
      } else if(!strcmp("--fort", argv[c])) {
	input = INPUT_FORT;
	input_str = argv[c] + 2;

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

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

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

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

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

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

#endif
      } else if(!strcmp("--dummy", argv[c])) {
	input = INPUT_DUMMY;
	input_str = argv[c] + 2;

      } else if(!strcmp("--flags", argv[c])) { // not ready

	charspaces = 0;
	charwidth = 2;
	size = 8;
	type = 0;
	flagdigits = 1; // semicolon separated (flags)
	
	// code after command line parameter loop
	
      } else if(!strcmp("--all", argv[c])) { // not ready
	if(flagsinit) {
	  idsall = 1;
	  newressu_toggleflag("ALL");
	  flagsinit = 0;
	}
	flagdigits = 1; // semicolon separated (flags)
	
      } else {
	ok = 0;
      }

      unsigned char buffer[32];

      int ok2 = 0;
      
      // command line parameters for flags
      
      if(!strncmp(argv[c], "--", 2)) {
	if(flagsinit) {
	  idsall = 0;
	  newressu_toggleflag(buffer);
	  flagsinit = 0;
	}
	strcpy(buffer, argv[c] + 2); // as is, skip dashes
	if(newressu_toggleflag(buffer) == 1)
	  ok2 = 1;
	else {
	  strtolower(buffer); // all letters lowercase
	  if(newressu_toggleflag(buffer) == 1)
	    ok2 = 1;
	  else {
	    buffer[0] = toupper(buffer[0]); // first letter uppercase
	    if(newressu_toggleflag(buffer) == 1)
	      ok2 = 1;
	    else {
	      strtoupper(buffer); // all letters uppercase
	      if(newressu_toggleflag(buffer) == 1)
		ok2 = 1;
	    }
	  }
	}
      }
      if(flagflags && ok == 0 && ok2 == 0) {
	fprintf(stderr,"%s: invalid option %s\n", procname, argv[c]);
	exit(1);
      }
      if(!flagflags && ok == 0 && ok2 == 0) {
	fprintf(stderr,"%s: invalid option %s\n", procname, argv[c]);
	exit(1);
      }
      if(!flagflags && ok == 0 && ok2 == 1) {
	fprintf(stderr,"%s: invalid option '%s' without --flags\n", procname, argv[c]);
	exit(1);
      }
    } // if(!strncmp
  } // for(c = 0

  if(size == -1 && base != -1) {
    if(base == 2)
      size = 8;
    else if(base == 8)
      size = 3;
    else if(base == 10)
      size = 5;
    else if(base == 16)
      size = 4;
    else
      size = 8;
  }

  if(size == -1 && base == -1)
    size = 5;
  
  if(base != -1) {
    digitsext = digits;
    charspaces = 0;
    charwidth = 1;
  }
  
  if(flagflags) {

    digits = NULL;
    flagdigits = 1; // semicolon separated (flags)

    for(d = 0; d < sizeof(idsflags) / sizeof(idsflags[0]); d++) {
      unsigned char buffer[32];
      if(flagids[d] == 1) {
	strcpy(buffer,idsflags[d].id);
	strcat(buffer,";");
	digits_add_string(&digits, buffer);
      }
    }

    if(digits == NULL)
      digits = "🇫🇮;";
    else
      digitsext = digits;

    //digits = "🇦🇧🇨🇩🇪🇫🇬🇭🇮🇯🇰🇱🇲🇳🇴🇵🇶🇷🇸🇹🇺🇻🇼🇽🇾🇿";
    //digits = "🇫🇮🇸🇪🇳🇴🇩🇰🇪🇪🇱🇻🇱🇹";
    
  }		       

#ifdef DEBUG59

  if(utf8) {

    int xlines;
    printcolumns(6, 0x10, 0, 64, &xlines);
    
    int utf8output = 0;
    for(c = 0; c < 65535*4; c++) { // skip control chars
      //for(c = 0x2800; c < 0x2900; c++) { // braille
      unsigned char buffer10[10];
      if(c % 64 == 0) {
	if(c > 0) {
	  fprintf(stdout,"\n");
	  utf8output = 0;
	}
	fprintf(stdout,"%5x ",c);
	utf8output = 1; 
     }
      if(c >= 0 && c < 32) {
	c += 31;
	fprintf(stdout, "--- control characters ---      ");
	utf8output = 1;
      } else if(c >= 128 && c < 256) {
	c += 31;
	fprintf(stdout, "--- utf8 characters ---         ");
	utf8output = 1;
      } else {
	codetoutf8(buffer10, c);
	fprintf(stdout,"%s",buffer10);
	utf8output = 1;
      }
    }
    if(utf8output)
      fprintf(stdout,"\n");
    fflush(stdout);
    return(0);
  }

#endif

#define aDEBUG76
#ifdef DEBUG76

  if(flagidflags) {
    int first = 1;
    for(c = 0; c < sizeof(idsflags) / sizeof(idsflags[0]); c++) {
      if(flagids[c] == 1) {
	if(first) {
	  fprintf(stdout,"flags: ");
	  first = 0;
	}
	fprintf(stdout," %s",idsflags[c].id);
      }
    }
    if(!first)
      fprintf(stdout,"\n");
  }

#endif
  
#define aDEBUG60 2

#ifdef DEBUG60
    int xlines;
    printcolumns(6, 2, 0, 64, &xlines);
    printcolumns(6, 8, 0, 64, &xlines);
    printcolumns(6, 10, 0, 64, &xlines);
    printcolumns(6, 16, 0, 64, &xlines);
#endif

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

#ifdef USE_TIMER
  timerstart = getseconds();
#endif

  if(flagdigits) // semicolon separated (flags)
    digitscount = utf8strings(digits);
  else // not
    digitscount = utf8characters(digits);

  //digitscount = utf8characters(digits);
  
  if(flagdigits) // semicolon separated (flags)
    characterlengths = utf8stringlengths(digits);
  else
    characterlengths = utf8lengths(digits);

  //characterlengths = utf8lengths(digits);

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

  if(input == INPUT_STREAM) {
    if(key == NULL)
      key = stream_default_key;

    stream_open(0, key);

  } else if(key != NULL) {
    fprintf(stderr,"%s: cannot --key without --stream\n", procname);
    exit(2);

  }

  if(fixedclock && !copyreverse) {
    fprintf(stderr,"%s: cannot --fixedclock without --copyreverse\n", procname);
    exit(2);

  }
  if(dieharder && !sample) {
    fprintf(stderr,"%s: cannot --dieharder without --sample\n", procname);
    exit(2);
  }

  if(purge_set && (!dieharder || !sample)) {
    fprintf(stderr,"%s: cannot --purge or --save without --sample and --dieharder\n", procname);
    exit(2);
  }

  if((filesize_set || blocksize_set || blocks_set) && !sample) {
    fprintf(stderr,"%s: cannot --filesize, --blocks or --blocksize without --sample\n", procname);
    exit(2);
  }

  if(input == INPUT_TEST) {
    fprintf(stderr,"%s: randomness from your generator (test_bytes)\n", procname);
  }

  if(sample) { // & dieharder
    dump_sample();
    exit(status);
  } // if(sample)

#ifdef DEBUG19

  if(flagint) {
    unsigned char savepath[128];

    save_path(sizeof(savepath), savepath);

    if(!strncmp(procname, "/bin/newressu", 13) && exists_path("/var/ressu"))
      set_path("/var/ressu");
    else if(!strncmp(procname, "./newressu", 10) && exists_path(".ressu"))
      set_path(".ressu");
    else if(exists_path("/var/ressu"))
      set_path("/var/ressu");
    else if(exists_path("/var/newressu"))
      set_path("/var/newressu");
    else if(exists_path(".ressu"))
      set_path(".ressu");
    else
      set_path("./");

    unsigned char command[160], date[16], filename[64];
    command_readline("date +%Y%m%d%H", sizeof(date), date);
    //command_readline("date +%Y%m%d%H%M", sizeof(date), date);

    sprintf(filename,"newressutest14.rnd.%s", date);
    sprintf(command,"./newressutest14.sh >%s",filename);
    //fprintf(stdout,"date:%s, filename:%s\n", date, filename);

    if(!exists_path(filename)) {
      int error;
      if((error = system(command)) != 0) {
	fprintf(stderr,"%s: system(): cannot execute command '%s'\n", procname, command);
      }
    }
    //fprintf(stderr," done.\n");
    fflush(stderr);

    set_path(savepath);
  } // if(flagint

#endif
  
  // print used digits
  
  if(flagprintdigits) {
    int count = 0;
    unsigned char *p;

    fprintf(stdout,"digits:");
    
    p = digits;
    while(*p != '\0') {
      if(*p < 0x80 || // ascii char
	 *p > 0xbf) { // first utf8 byte
	count++;
#ifdef KOK
	if((charwidth == 2 && count >= 32) ||
	   (charwidth == 1 && count >= 72) )
	  break;
#endif
      }
      fprintf(stdout,"%c", *p);
      p++;
    }
    if(*p != '\0')
      fprintf(stdout,"...");
    fprintf(stdout,"\n");
  }

  // get linenumber length in digits

  plinesdigits = line_number_length();

  // get data length in digits

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

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

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

  // count words

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

    pchars += calc_spaces();
    pchars += (size*charwidth);
    pwords++;

  }

  if(columns) {
    int xlines;
    printcolumns(0, 10, 0, pchars, &xlines);
    lines -= xlines;
    if(lines < 1)
      lines = 1;
  }

  // there has to be atleast one data word
  
  if(linew < 1)
    linew = 1;

  unsigned long wordvalues=1, wordvaluesold;

  for(c = 0; c < size; c++) {
    wordvaluesold = wordvalues;
    wordvalues *= digitscount;
    if(wordvalues / digitscount != wordvaluesold) {
      wordvalues = 0;
      break;
    }
  }

  if(limit > 0) {
    if(sort==1 && limit-!zero < words) {
      fprintf(stderr, "%s: limit is less than words (zero)", procname);
      fprintf(stderr, ", limit:%lu", limit);
      fprintf(stderr, ", words:%d", words);
      fprintf(stderr, ", zero:%d", zero);
      fprintf(stderr, "\n");
      
      lines = 0;
    }
  } else {
    if(sort == 1 && size > 0 && wordvalues > 0 && wordvalues < linew) {
      fprintf(stderr, "%s:", procname);
      fprintf(stderr, " digits is less than calculated words");
      fprintf(stderr, ", digits:%d(%lu)", digitscount, wordvalues);
      fprintf(stderr, ", words:%d\n", linew);

      lines = 0;
    }
  }

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

#ifdef TEST
  if(flagint && base != -1) {
    fprintf(stderr,"Last word: ");
    fprintfbase(stderr, limit - 1, base);
    fprintf(stderr,"\n");
  }
#endif

  if(stats) {
    fprintf(stderr, "randomsource: %s", input_str);
    fprintf(stderr, ", fixedclock: %d", fixedclock);
    if(fixedclock == 1)
      fprintf(stderr, ", fixedclockchainlength: %d", fixedclockchainlength);
    fprintf(stderr, ", copyreverse: %d", copyreverse);
    fprintf(stderr, ", size: %d", size);
    fprintf(stderr, ", lines: %llu", lines);
    fprintf(stderr, ", linew: %d", linew);
    fprintf(stderr, ", pchars: %d", pchars);
    fprintf(stderr, ", pwords: %d", pwords);
    fprintf(stderr, ", sspace: %d", sspace);
    fprintf(stderr, ", tchars: %llu", (unsigned long long)size * linew * lines);
    fprintf(stderr, ", digitscount: %d", digitscount);
    fprintf(stderr, ", characterlengths: %d", characterlengths);
    fprintf(stderr, ", strlen(digits): %ld", strlen(digits));
    if(wordvalues > 0)
      fprintf(stderr, ", wordvalues: %lu", wordvalues);
    if(limit > 0) {
      fprintf(stderr, ", limit: %lu", limit);
#ifdef DEBUG51
      fprintf(stderr, ", type: %d", type);
      fprintf(stderr, ", limitsize%d: %d", type, limitsize);
#endif
    }
    fprintf(stderr, ", pid: %d", getpid());
    fprintf(stderr, "\n");
  }

  if(help) {
    struct helpline {
      char *command;
      char *text;
    } helplines[] = {
      { "newressu --help/-?", "this help page" },
      { "newressu -d/--dec", "print random decimal digits (default)" },
      { "newressu -o/--oct", "octal digits" },
      { "newressu -x/--hex", "hexadecimal digits" },
      { "newressu -b/--bin", "binary digits" },
      { "newressu -d --space", "decimal digits, with spaces between \"words\"" },
      { "newressu -b --space", "binary digits, with spaces between \"words\"" },
      { "newressu -d -l30", "decimal digits, 30 lines" },
      { "newressu -d -l30 --lineno", "decimal digits, 30 lines, no linenumbers"  },
      { "newressu --base2", "base 2 digits =decimal (0-1)" },
      { "newressu --base8", "base 8 digits =octal (0-7)" },
      { "newressu --base10", "base 10 digits =decimal (0-9)" },
      { "newressu --base16", "base 16 digits =hexadecimal (0-F)" },
      { "newressu --base62", "base 62 digits (0-9, A-Z, a-z)" },
      { "newressu --rand", "numbers in rand style listing"  },
      { "newressu --space 2", "numbers in 2 number groups"  },
      { "newressu --newline 5", "print numbers in 5 line groups"  },
      { "newressu -l*", "fill all screen lines"  },
      { "newressu -c*", "fill all screen columns"  },
      { "newressu --screen", "fill screen columns and lines"  },
      { "newressu -d -c128", "print decimal digits, 128 characters per line"  },
      { "newressu -d --lim10", "print decimal numbers between 0-9" },
      { "newressu -d --lim10 -x","print hexadecimal numbers with decimal limit" },
      { "newressu --isupper --limBAAAAA","print five letter uppercase words" },
      { "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 --alnum", "alphanumeric characters" },
      { "newressu --alpha", "alphabetic characters" },
      { "newressu --graph", "printables excluding space" },
      { "newressu --lower", "lowercase characters" },
      { "newressu --punct", "punctuation characters" },
      { "newressu --upper", "uppercase characters" },
      { "newressu -1", "material for passwords" },
      { "newressu --dnk/--nor", "danish/norwegian alphabet" },
      { "newressu --fin/--swe", "finnish/norwegian alphabet" },
      { "newressu --rus", "russian alphabet" },
      { "newressu --est", "estonian alphabet" },
      { "newressu --ltu", "lithuanian alphabet" },
      { "newressu --lva", "latvian alphabet" },
      { "newressu --fra/--gbr/--usa/--ita", "french/gb/us/italian alphabet" },
      { "newressu --deu", "deutsch alphabet" }, 
      { "newressu --grc", "greek alphabet" }, 
      { "newressu --jp", "japan alphabet (hiragana, katakana, kanji) " }, 
      { "newressu --jp1/--hir", "japan alphabet (hiragana) " }, 
      { "newressu --jp2/--kat", "japan alphabet (katakana) " }, 
      { "newressu --jp3/--kan", "japan alphabet (kanji) " }, 
      { "newressu --cn", "chinese alphabet" }, 
      { "newressu --kor", "korean alphabet" }, 
      { "newressu --got", "gothic alphabet" }, 
      { "newressu --otp5n/--otp1", "\"one time pad\" (otp) with 5 numeric characters" }, 
      { "newressu --otp5a/--otp2", "otp with 5 uppercase letters" }, 
      { "newressu --otp4a/--otp3", "otp with 4 uppercase letters" }, 
      { "newressu --cards", "playing cards" }, 
      { "newressu --cardsuits", "playing card suits" }, 
      { "newressu --chessmen", "chess men" }, 
      { "newressu --dice", "print dice" },
      { "newressu --mahjong", "print mahjong pieces" },
      { "newressu -i▌▙▄ / --pattern1", "print pattern1" },
      { "newressu -i◜◝◞◟ / --pattern2", "print pattern2" },
      { "newressu -i\\\\/ / --pattern3", "print pattern2" },
      { "newressu -i▧▨ / --pattern5", "print pattern2" },
      { "newressu --ressu", "use randomness from ressu (default)" },
      { "newressu --pseudoressu", "use randomness from pseudo random ressu" },
      { "newressu --fast/--fastressu", "use randomness from fast ressu" },
      { "newressu --single/--singleressu", "use randomness from single round of ressu" },
#ifdef FORT
      { "newressu --fort", "use randomness from fort" },
#endif
#ifdef USE_RDRAND
      { "newressu --rdrand", "use randomness from intel rdrand" },
#endif
#ifdef USE_RDSEED
      { "newressu --rdseed", "use randomness from intel rdseed" },
#endif
      { "newressu --urandom", "use randomness from /dev/urandom" },
#ifdef USE_RANDOM
      { "newressu --random", "use randomness from /dev/random" },
#endif
    };
    
    for(c = 0; c < sizeof(helplines) / sizeof(helplines[0]); c++) {
      fprintf(stdout, "%-50s", helplines[c].text);
      fprintf(stdout, "%-25s", helplines[c].command);
      fprintf(stdout, "\n");
    }

#define DEBUG77 2

#ifdef DEBUG77

    unsigned char *areaflags = "FINLAND, DENMARK, NORDIC, BALTIC, FRANCE, UK, SPAIN, EU, NORTHERNEUROPE, EASTERNEUROPE, SOUTHERNEUROPE, WESTERNEUROPE, EUROPE, EURASIA, NORTHASIA, EASTASIA, CENTRALASIA, SOUTHASIA, WESTASIA, ASIA, NORTHAFRICA, EASTAFRICA, CENTRALAFRICA, SOUTHAFRICA, WESTAFRICA, AFRICA, NORTHAMERICA, SOUTHAMERICA, USA, OCEANIA, AUSTRALASIA, MELANESIA, MICRONESIA, POLYNESIA, BRICS, ALL", *a;
    unsigned char name[128];
    
    a = areaflags;
    while(*a != '\0' && newressu_getname(sizeof(name), name, &a)) {
      fprintf(stdout,"area:%s",name);
      int count = 0;
      for(c = 0; c < sizeof(idsflags) / sizeof(idsflags[0]); c++) {
	if(!strcmp(name, "ALL")) {
	  count++;
	  unsigned char *q = idsflags[c].flags;
	  unsigned char name2[128], name3[128];
	  newressu_getname(sizeof(name2), name2, &q); // flag
	  newressu_getname(sizeof(name2), name2, &q); // 2 char isocode
	  newressu_getname(sizeof(name3), name3, &q); // 3 char isocode
	  newressu_getname(sizeof(name3), name3, &q); // name
	  fprintf(stdout,", %s(%s)",name3, name2); // name and isocode2 needed
	} else {	
	  
	  if(newressu_strstr(idsflags[c].flags, name)!=NULL) {
	    count++;
	    unsigned char *q = idsflags[c].flags;
	    unsigned char name2[128], name3[128];
	    newressu_getname(sizeof(name2), name2, &q); // flag
	    newressu_getname(sizeof(name2), name2, &q); // 2 char isocode
	    newressu_getname(sizeof(name3), name3, &q); // 3 char isocode
	    newressu_getname(sizeof(name3), name3, &q); // name
	    fprintf(stdout,", %s(%s)", name3, name2); // name and isocode2 needed
	  }
	}
      }
      fprintf(stdout,", count:%d",count);
      fprintf(stdout,"\n");
    }
#endif

    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(status);

#define aDEBUG78 2
    
#ifdef DEBUG78

    for(c = 0; c < sizeof(flagchars) / sizeof(flagchars[0]); c++) {
      int first = 1;
      for(d = 0; d < sizeof(flagchars) / sizeof(flagchars[0]); d++) {
	char buffer[32];
	buffer[0] = '\0';
	if(!first)
	  strcat(buffer," ");
	strcat(buffer, flagchars[c]);
	strcat(buffer, flagchars[d]);
	fprintf(stdout,"%s",buffer);
	first = 0;
      }
      fprintf(stdout,"\n");
    }
#endif
  
  int linecnt = 0;
  unsigned char *line = NULL;
  unsigned char wordbuf[1025];
  
  for(;;) {

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

      pwords = 0;

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

	if(!quiet) {

	  // in beginning of line, print line number

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

	  print_spaces();
	
	  // print word

	  print_word(wordbuf);

	  newressu_output = 1;
	}

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

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

      // fetch and save words on this row

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

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

      pwords = 0;

      // print words on this row

      while(pwords < linew) {

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

	if(!quiet) {

	  // in beginning of line, print line number

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

	  // print word
	
	  print_word(wordbuf);

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

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

    plines++;
    ptotallines++;

    // print empty line

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

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

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

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

	  bytes = (((unsigned long long)size * linew * plines) / STATDIV) * STATDIV;
	  stat_line_get_readable(buf10, bytes);
	  
#ifdef DEBUG97
	  fprintf(stderr, "%lld", (unsigned long long)size * linew * plines);
	  fprintf(stderr, ", %lld", bytes);
	  fprintf(stderr, ", %s", buf10);
	  fprintf(stderr, "\n");
#endif
	  stat_line_begin();
	  stat_line_printf("%s characters written:", input_str);
	  stat_line_readable((unsigned long) size * linew * plines);
	  stat_line_printf(", %d%%", pros);
	  //stat_line_printf(", lines:%lld",lines);
	  //stat_line_printf(", plines:%lld",plines);
	  stat_line_end();
	}
	stat_line_cursor();
      }
    } // if(print_statline)
  } // for(;;)

  if(print_statline) { // 0=stdin, 1=stdout, 2=stderr
    stat_line_begin();
    stat_line_end();
    stat_line_cursor_remove();
  }

#ifdef DEBUG04

  int bufferlen = 0;
  for(int c = 0; c < ressuct_bytes; c++) {
    unsigned char buffer[16];
    sprintf(buffer, "%lu", count[c]);
    if(bufferlen < strlen(buffer))
      bufferlen = strlen(buffer);
  }
  for(int c = 0; c < ressuct_bytes; c++) {
    if(c % 16 == 0) {
      if(c > 0)
	fprintf(stdout,"\n");
      fprintf(stdout,"%05u ", c);
    }
    fprintf(stdout,"%*lu", bufferlen + 1, count[c]);
  }
  fprintf(stdout,"\n");

#endif
  
#ifdef USE_TIMER

  if(timer) {
    double timertook=getseconds()-timerstart;
    fprintf(stderr, "%s run time %f seconds", input_str, timertook);
    fprintf(stderr, ", %lld lines", plines);
    fprintf(stderr, ", %f useconds/line", timertook * 1000000 / plines);
    fprintf(stderr, ", %d characters/line", pwords * size);
    fprintf(stderr, ", %f useconds/character",
	    (timertook * 1000000 / plines) / (pwords * size) );
    fprintf(stderr, "\n");
  }

#endif

  fflush(stdout);

  if(digitsext != NULL)
    free(digitsext);
  
#ifdef FORT

  if(input == INPUT_FORT ||
    input == INPUT_FORTXOR) {
    fort_save();
  }

#endif

  exit(status);
}

#endif // MAIN

newressutest14.sh

/bin/newressu --int --single --space2 --newline 5 -l1 $@
/bin/newressu --int --single --space3 --newline 5 -l1 $@
/bin/newressu --int --single --space4 --newline 5 -l1 $@
/bin/newressu --int --single --space5 --newline 5 -l1 $@
/bin/newressu --int --single --space6 --newline 5 -l1 $@
/bin/newressu --int --single --6bits --space -s1 -l1 $@
/bin/newressu --int --single --6bits --space -s2 -l1 $@
/bin/newressu --int --single --6bits --space -s3 -l1 $@
/bin/newressu --int --single --6bits --space -s4 -l1 $@
/bin/newressu --int --single --6bits --space -s5 -l1 $@
/bin/newressu --int --single --6bits --space -s6 -l1 $@
/bin/newressu --int --single --6bits --space -s7 -l1 $@
/bin/newressu --int --single --6bits --space -s8 -l1 $@
/bin/newressu --int --single --6bits --space -s9 -l1 $@
/bin/newressu --int --single --6bits --space -s10 -l1 $@
/bin/newressu --int --single --rand -l1 $@
/bin/newressu --int --single --alnum -l1 $@
/bin/newressu --int --single --alpha -l1 $@
/bin/newressu --int --single --graph -l1 $@
/bin/newressu --int --single --lower -l1 $@
/bin/newressu --int --single --punct -l1 $@
/bin/newressu --int --single --upper -l1 $@
/bin/newressu --int --single -d --lim41 -w7 --zero -l1 --lotto$@
/bin/newressu --int --single --1bit -l1 $@
/bin/newressu --int --single --2bits -l1 $@
/bin/newressu --int --single --3bits -l1 $@
/bin/newressu --int --single --oct -l1 $@
/bin/newressu --int --single --4bits -l1 $@
/bin/newressu --int --single --hex -l1 $@
/bin/newressu --int --single --HEX -l1 $@
/bin/newressu --int --single --5bits -l1 $@
/bin/newressu --int --single --6bits -l1 $@
/bin/newressu --int --single --dnk -l1 $@
/bin/newressu --int --single --nor -l1 $@
/bin/newressu --int --single --fin -l1 $@
/bin/newressu --int --single --swe -l1 $@
/bin/newressu --int --single --rus -l1 $@
/bin/newressu --int --single --est -l1 $@
/bin/newressu --int --single --ltu -l1 $@
/bin/newressu --int --single --lva -l1 $@
/bin/newressu --int --single --fra -l1 $@
/bin/newressu --int --single --gbr -l1 $@
/bin/newressu --int --single --usa -l1 $@
/bin/newressu --int --single --ita -l1 $@
/bin/newressu --int --single --eng -l1 $@
/bin/newressu --int --single --deu -l1 $@
/bin/newressu --int --single --grc -l1 $@
/bin/newressu --int --single --jp -l1 $@
/bin/newressu --int --single --jp1 -l1 $@
/bin/newressu --int --single --hir -l1 $@
/bin/newressu --int --single --jp2 -l1 $@
/bin/newressu --int --single --kat -l1 $@
/bin/newressu --int --single --jp3 -l1 $@
/bin/newressu --int --single --cn -l1 $@
/bin/newressu --int --single --kan -l1 $@
/bin/newressu --int --single --kor -l1 $@
/bin/newressu --int --single --ind -l1 $@
/bin/newressu --int --single --hin -l1 $@
/bin/newressu --int --single --som -l1 $@
/bin/newressu --int --single --SOM -l1 $@
/bin/newressu --int --single --SO -l1 $@
/bin/newressu --int --single --braille -l1 $@
/bin/newressu --int --single --dna -l1 $@
/bin/newressu --int --single --DNA -l1 $@

newressu.h

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

#define INPUT_RESSU 0
#define INPUT_PSEUDORESSU 1
#define INPUT_FASTRESSU 3
#define INPUT_SINGLERESSU 4
#define INPUT_FIXED 5

#define INPUT_STREAM 10
#define INPUT_FORT 11
#define INPUT_FORTXOR 12
#define INPUT_RDRAND 13
#define INPUT_RDSEED 14
#define INPUT_URANDOM 15
#define INPUT_RANDOM 16
#define INPUT_DUMMY 17

extern int verbose;

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

fort.c

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

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

#include <openssl/ssl.h>

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

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

#define FORT_INTERNAL_EVENTS 2

#define aDEBUG10 2
#define aDEBUG18 2

#define FORT_XOR_VERSION 2
#define FORT_ORIGINAL_VERSION 2

#include "fort.h"

int fort_events = 1;
int fort_use_web = 0;

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

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

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

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

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

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

#define FORT_64_POOLS 2

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

void fort_mix();

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

  inccvar();  
}

#define aDEBUG8 2

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

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

static IUTIME gettenths()
{
  struct timeval tv;

  gettimeofday(&tv, NULL);

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

static IUTIME getmicroseconds()
{
  struct timeval tv;

  gettimeofday(&tv, NULL);

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

static IUTIME getseconds()
{
  struct timeval tv;

  gettimeofday(&tv, NULL);

  return((IUTIME)tv.tv_sec);
}

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

static struct fort_pool fort_pools[FORT_POOLS];

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

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

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

  switch(mode) {

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

  case 3:
    break; // caller decides next pool

  }
}

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

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

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

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

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

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

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

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

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

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

#define aDEBUG18 2

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

  HashInit(hash);

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

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

  HashUpdate(hash, data, len);

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

void hash_final(unsigned char digest[HashLen], HashCtx *hash)
{
  FORT_INTERNAL_EVENTS_START(14)

  HashFinal(digest, hash);

#ifdef DEBUG18
  fprintf(stdout,"final     ");
  for(int d = 0; d < HashLen; d++)
    fprintf(stdout,"%02x", digest[d]);
  fprintf(stdout,"\n");
#endif
  
  FORT_INTERNAL_EVENTS_END(15)
}

static unsigned char fort_key[HashLen];

void fort_rekey(unsigned char *digest)
{
  HashCtx hash;

  FORT_INTERNAL_EVENTS_START(16)

  hash_init(&hash);
  hash_update(&hash, fort_key, sizeof(fort_key));
  hash_update_cvar(&hash);
  hash_final(digest, &hash);

  // Forget hash

  memset(&hash, 0, sizeof(hash));

  FORT_INTERNAL_EVENTS_END(17)
}

#define FORT_PSEUDO_LIMIT 1048576

// use 
// $ ./newressu --fort -x -w32 -s2 -l2000 --space | more
// to debug...

// choose one of
//     60 (1 minute)
//     300 (5 minutes)
//     600 (10 minutes)
//     900 (15 minutes)
//     1200 (20 minutes)
//     1800 (30 minutes)
//     3600 (hour)
//     7200 (2 hours)
//     10800 (3 hours)
//     14400 (4 hours)
//     21600 (6 hours)
//     28800 (8 hours)
//     43200 (12 hours)
//     86400 (24 hours)
// to get readable report..

#define FORT_SECONDS_BETWEEN_SAVES 10 * 60
static IUTIME fort_next_save = 0;

#define TIMEFORMAT "%Z%Y%m%d%H%M%S"

void fort_save();

static char current_timezone[10];

#define aDEBUG22 2

void fort_reseed(int len, unsigned char *buf)
{
  HashCtx hash;

  FORT_INTERNAL_EVENTS_START(22)
  
  hash_init(&hash);
  hash_update(&hash, fort_key, sizeof(fort_key));
  hash_update_cvar(&hash);
  hash_update(&hash, buf, len);

#ifdef DEBUG22
  fprintf(stdout,"\nprevkey   ");
  for(int d = 0; d < sizeof(fort_key); d++)
    fprintf(stdout,"%02x", fort_key[d]);
  fprintf(stdout,"\ncvar      ");
  for(int d = 0; d < cvarsize + 1; d++)
    fprintf(stdout,"%02x", cvar[d]);
  fprintf(stdout,"\ndata      ");
  for(int d = 0; d < HashLen; d++)
    fprintf(stdout,"%02x", buf[d]);
  fprintf(stdout,"\ndata asc  ");
  for(int d = 0; d < len; d++) {
    if(isprint(buf[d]))
      fprintf(stdout,"%c", buf[d]);
    else
      fprintf(stdout,"\\%02x", buf[d]);
  }
#endif
  
  hash_final(fort_key, &hash);

#ifdef DEBUG22
  fprintf(stdout,"\nnewkey    ");
  for(int d = 0; d < sizeof(fort_key); d++)
    fprintf(stdout,"%02x", fort_key[d]);
  fprintf(stdout,"\n");
#endif
  
  memset(&hash, 0, sizeof(hash));

  if(fort_next_save != 0) {
    IUTIME seconds;
    seconds = getseconds();
    
    char timezone[10];
    strftime(timezone, sizeof(timezone), "%Z",
             localtime((time_t *)&seconds) );
    if(strcmp(current_timezone, timezone)) {
      fort_next_save = 1;
      strcpy(current_timezone, timezone);
    }
    
    if(fort_next_save != 0 &&
       seconds >= fort_next_save) {
      
      IUTIME diff=
        timegm(localtime((time_t *)&seconds)) - seconds;
      
      fort_next_save = seconds -
        ((seconds + diff) %
         FORT_SECONDS_BETWEEN_SAVES) +
        FORT_SECONDS_BETWEEN_SAVES;
      
      fort_save();
    }
  }

  FORT_INTERNAL_EVENTS_END(23)
}

static FORT_COUNTER fort_reseed_count = 0;
static IUTIME fort_next_reseed = 0;

#define aDEBUG28 2

#ifdef FORT_ORIGINAL_VERSION

void fort_pseudo_random_data(int len, 
    unsigned char *buf)
{
  unsigned char digest[HashLen];
  unsigned int n, blockbytes;
#ifdef DEBUG28
  int d = 0;
#endif

  FORT_INTERNAL_EVENTS_START(18)

  blockbytes = 0;

  while(len != 0) {
    FORT_INTERNAL_EVENTS_START(19)

    fort_rekey(digest);
    n = (len < HashLen) ? len : HashLen;

#ifdef DEBUG28
    
    fprintf(stdout,"\nbuf(%02x):  ",d++);
    for(int c=0;c<n;c++)
      fprintf(stdout,"%02x",buf[c]);
    fprintf(stdout,"\ndigest:   ");
    for(int c=0;c<n;c++)
      fprintf(stdout,"%02x",digest[c]);
    fprintf(stdout,"\nsum:      ");
    for(int c=0;c<n;c++)
      fprintf(stdout,"%02x",buf[c]^digest[c]);
    fprintf(stdout,"\n");

#endif // #ifdef DEBUG18

    memcpy(buf, digest, n);

    buf += n;
    len -= n;
    blockbytes += n;

    // rekey every 1048576 bytes if data left

    if(blockbytes >= FORT_PSEUDO_LIMIT &&
        len > 0) {                                                                                                              
      fort_rekey(fort_key);
      blockbytes = 0;
    }

    FORT_INTERNAL_EVENTS_END(20)
  }

  fort_rekey(fort_key);

  // Forget last bytes

  memset(digest, 0, sizeof(digest));

  FORT_INTERNAL_EVENTS_END(21)
}

#define aDEBUG34 2

void fort_random_data(int len, unsigned char *buf)
{
  int c;
  IUTIME tenths;
  HashCtx hash;
  unsigned char digest[HashLen];

  FORT_INTERNAL_EVENTS_START(24)

  tenths = gettenths();

  if( (fort_next_reseed == 0) ||
      (fort_next_reseed <= tenths &&
      fort_pools[0].length >=
      FORT_MIN_POOL_SIZE) ) {

    fort_mix();
    
    // next tenth of a second

    fort_next_reseed = tenths + 1;
    fort_reseed_count++;

    hash_init(&hash);
    c = 0;

    while(c < FORT_POOLS && (fort_reseed_count
        % (1 << c)) == 0) {

      FORT_INTERNAL_EVENTS_START(25)

      hash_final(digest, &fort_pools[c].pool);

#ifdef DEBUG34
      fprintf(stdout,"\ndata   %2d ",c);
      for(int d = 0; d < HashLen; d++)
	fprintf(stdout,"%02x",digest[d]);
#endif

      hash_update(&hash, digest, sizeof(digest));
      fort_pools[c].length = 0;
      HashInit(&fort_pools[c].pool);

      // save earlier pool to new one

      hash_update(&fort_pools[c].pool,
        digest, sizeof(digest));
      c++;

      FORT_INTERNAL_EVENTS_END(26)
    }
    hash_update_cvar(&hash);
    hash_final(digest, &hash);

#ifdef DEBUG34
    fprintf(stdout,"\ndigest    ");
    for(int d = 0; d < HashLen; d++)
      fprintf(stdout,"%02x", digest[d]);
    fprintf(stdout,"\n");
#endif

    fort_reseed(sizeof(digest), digest);

    // Forget hash context

    memset(&hash, 0, sizeof(hash));

    // Forget reseed key

    memset(digest, 0, sizeof(digest));
  }

  fort_pseudo_random_data(len, buf);

  FORT_INTERNAL_EVENTS_END(27)
}

#endif // #ifdef FORT_ORIGINAL_VERSION

#ifdef FORT_XOR_VERSION

void fort_pseudo_random_data_xor(int len, 
    unsigned char *buf)
{
  unsigned char digest[HashLen];
  unsigned int n, blockbytes;
#ifdef DEBUG28
  int d = 0;
#endif

  FORT_INTERNAL_EVENTS_START(18)

  blockbytes = 0;

  while(len != 0) {
    FORT_INTERNAL_EVENTS_START(19)
      
    fort_rekey(digest);
    n = (len < HashLen) ? len : HashLen;

#ifdef DEBUG28

    fprintf(stdout,"\nbuf(%02x):  ", d++);
    for(int c = 0; c < n; c++)
      fprintf(stdout,"%02x", buf[c]);
    fprintf(stdout,"\ndigest:   ");
    for(int c = 0; c < n; c++)
      fprintf(stdout,"%02x", digest[c]);
    fprintf(stdout,"\nsum:      ");
    for(int c = 0; c < n; c++)
      fprintf(stdout,"%02x", buf[c] ^ digest[c]);
    fprintf(stdout,"\n");

#endif
    
    for(int c = 0; c < n; c++)
      buf[c] ^= digest[c];

    buf += n;
    len -= n;
    blockbytes += n;

    // rekey every 1048576 bytes if data left

    if(blockbytes >= FORT_PSEUDO_LIMIT &&
        len > 0) {                                                                                                              
      fort_rekey(fort_key);
      blockbytes = 0;
    }

    FORT_INTERNAL_EVENTS_END(20)
  }

  fort_rekey(fort_key);

  // Forget last bytes

  memset(digest, 0, sizeof(digest));

  FORT_INTERNAL_EVENTS_END(21)
}

void fort_random_data_xor(int len, unsigned char *buf)
{
  int c;
  IUTIME tenths;
  HashCtx hash;
  unsigned char digest[HashLen];

  FORT_INTERNAL_EVENTS_START(24)

  tenths = gettenths();

  if( (fort_next_reseed == 0) ||
      (fort_next_reseed <= tenths &&
      fort_pools[0].length >=
      FORT_MIN_POOL_SIZE) ) {

    fort_mix();

    // next tenth of a second

    fort_next_reseed = tenths + 1;
    fort_reseed_count++;

    hash_init(&hash);
    c = 0;

    while(c < FORT_POOLS && (fort_reseed_count
        % (1 << c)) == 0) {

      FORT_INTERNAL_EVENTS_START(25)

      hash_final(digest, &fort_pools[c].pool);

#ifdef DEBUG34
      fprintf(stdout,"\ndata   %2d ", c);
      for(int d = 0; d < HashLen; d++)
	fprintf(stdout,"%02x", digest[d]);
#endif
      
      hash_update(&hash, digest, sizeof(digest));
      fort_pools[c].length = 0;
      HashInit(&fort_pools[c].pool);

      // save earlier pool to new one

      hash_update(&fort_pools[c].pool,
        digest, sizeof(digest));
      c++;

      FORT_INTERNAL_EVENTS_END(26)
    }
    hash_update_cvar(&hash);
    hash_final(digest, &hash);

#ifdef DEBUG34
    fprintf(stdout,"\ndigest    ");
    for(int d = 0; d < HashLen; d++)
      fprintf(stdout,"%02x", digest[d]);
    fprintf(stdout,"\n");
#endif
    
    fort_reseed(sizeof(digest), digest);

    // Forget hash context

    memset(&hash, 0, sizeof(hash));

    // Forget reseed key

    memset(digest, 0, sizeof(digest));
  }

  fort_pseudo_random_data_xor(len, buf);

  FORT_INTERNAL_EVENTS_END(27)
}

#endif // #ifdef FORT_XOR_VERSION

#define FORTCNT 128

static unsigned int fort_cnt = FORTCNT;
static unsigned char fort_bytes[FORTCNT];
static int fort_byte = 999999999;

int fort_random_data_byte()
{
  if(fort_byte >= fort_cnt) {
    fort_random_data(fort_cnt, fort_bytes);
    fort_byte = 0;
  }
  return(fort_bytes[fort_byte++]);
}

void fort_clear()
{
  memset(fort_bytes, 0, fort_cnt);
  fort_byte = 999999998;
}

int fort_random_data_byte_limit(int limit)
{
  int c;

  while((c = fort_random_data_byte())>=
      (256 / limit) * limit);

  return(c % limit);
}

void fort_random_data_buffer(int size,
    unsigned char *buffer)
{
  int c;

  for(c = 0; c < size; c++)
    buffer[c] ^= fort_random_data_byte();
}

#define aDEBUG53 2

void fort_reseed_file(unsigned char *filename)
{
  int c, cnt, bytes, characters;
#ifdef DEBUG53
  unsigned char *p;
#endif
  FILE *fp1;
  unsigned char buffer[1024], digest[HashLen];

  HashCtx hash;

  bytes = 0;
  characters = 0;
  
  if((fp1 = fopen(filename, "r")) != NULL) {
    HashInit(&hash);
    while(fgets(buffer, sizeof(buffer), fp1) != NULL) {
      cnt = strlen(buffer);
      HashUpdate(&hash, buffer, cnt);
      bytes += cnt;
      for(c = 0; c < cnt; c++) {
	if(buffer[c] < 0x80 || buffer[c] > 0xbf)
	  characters++;
      }
#ifdef DEBUG53
      p = buffer;
      while(*p != '\0') {
	if(*p == '\\')
	  fprintf(stdout, "\\\\");
	else if(isprint(*p) || *p == '\n')
	  fputc(*p, stdout);
	else
	  fprintf(stdout,"\\%02x", *p);
	p++;
      }
#endif
    }
    hash_update_cvar(&hash);
    HashFinal(digest, &hash);

    fort_reseed(sizeof(digest), digest);

    fclose(fp1);
  }
#ifdef DEBUG53
  //if(verbose) {
  fprintf(stdout,"fort_reseed_file %s", filename);
  fprintf(stdout,", %d bytes read", bytes);
  fprintf(stdout,", %d characters read", characters);
  fprintf(stdout,", sha256: ");
  for(int c = 0; c < HashLen; c++) {
    fprintf(stdout,"%02x", digest[c]);
  }
  fprintf(stdout,"\n");
  //}
#endif
}

#define FORT_SAVE_SIZE 1024

#define aDEBUG57

void fort_save()
{
  FILE *fp1;
  unsigned char buffer[FORT_SAVE_SIZE];

  memset(buffer, 0, sizeof(buffer));

  if((fp1 = fopen(fort_random_file, "w")) != NULL) {
    fort_pseudo_random_data(sizeof(buffer), buffer);

#ifdef DEBUG57
    for(int c = 0; c < sizeof(buffer); c++) {
      if(c % 32 == 0) {
	if(c != 0)
	  fprintf(stdout,"\n");
	fprintf(stdout,"save      %05d ", c);
      }
      fprintf(stdout," %02x", buffer[c]);
    }
    fprintf(stdout,"\n");
#endif
    fwrite(buffer, 1, sizeof(buffer), fp1);
    memset(buffer, 0, sizeof(buffer));
    fclose(fp1);
  }
}

void fort_restore()
{
  int d;
#ifdef DEBUG57
  int random;
#endif
  FILE *fp1;
  unsigned char buffer[FORT_SAVE_SIZE];

  memset(buffer, 0, sizeof(buffer));
  
  if((fp1 = fopen(fort_random_file, "r"))
      != NULL) {
    d = fread(buffer, 1, sizeof(buffer), fp1);
    fclose(fp1);
#ifdef DEBUG57
    random = 0;
#endif
  } else {
    fort_pseudo_random_data(sizeof(buffer), buffer);
    ressu_genbytes(sizeof(buffer), buffer);
    d = sizeof(buffer);
#ifdef DEBUG57
    random = 1;
#endif
  }
#ifdef DEBUG57
  for(int c = 0; c < sizeof(buffer); c++) {
    if(c % 32 == 0) {
      if(c != 0)
	fprintf(stdout,"\n");
      fprintf(stdout,"restore");
      if(random)
	fprintf(stdout,"*");
      else
	fprintf(stdout," ");
      fprintf(stdout,"  %05d ", c);
    }
    fprintf(stdout," %02x", buffer[c]);
  }
  fprintf(stdout,"\n");
#endif
  fort_reseed(d, buffer);
  memset(buffer, 0, sizeof(buffer));

  fort_save();
}

#if defined FORT_USE_URANDOM || \
    defined FORT_USE_RANDOM

static void fort_readfile_xor(int len,
    unsigned char *buf,
    unsigned char *filename)
{
  int c, n, n2;
  unsigned char temp[64];
  FILE *fp1;

  if((fp1 = fopen(filename, "rb"))
      != NULL) {
    while(len != 0) {
      n = (len < sizeof(temp)) ?
          len : sizeof(temp);
      n2 = fread(temp, 1, n, fp1);
      for(c = 0; c < n2; c++)
        buf[c] ^= temp[c];
      len -= n2;
      buf += n2;
    }
    fclose(fp1);
  }
}

#endif

#ifdef FORT_USE_WEB

static void parse_string(unsigned char *string, int size, unsigned char **p2)
{
  int count;
  unsigned char *p, *q;

  p = *p2;
  q = string;
  count = 0;

  // uppercase & lowercase letters, '.'
  // or utf-8

  while(isalnum(*p) || *p == '.' || *p > 0x80) {
    if(++count < size)
      *q++ = *p;
    p++;
  }
  *q = '\0';
  *p2 = p;
}

#endif // #ifdef FORT_USE_WEB

#ifdef FORT_USE_WEB

static int check_string(unsigned char *string, unsigned char *p)
{
  int ok;

  ok = 0;
  if(!strncmp(string, p, strlen(string))) {
    ok = 1;
  }

  return(ok);
}

#endif // #ifdef FORT_USE_WEB

void fort_internal_random_data_1(int size, char *buf)
{
  ressu_genbytes(size, buf);
#ifdef FORT_XOR_VERSION
  fort_pseudo_random_data_xor(size, buf);
#endif
}

void fort_internal_random_data_2(int size, char *buf)
{
#ifdef FORT_XOR_VERSION
  fort_pseudo_random_data_xor(size, buf);
#else
  fort_pseudo_random_data(size, buf);
#endif
  ressu_genbytes(size, buf);
}

void fort_internal_random_data_3(int size, char *buf)
{
#ifdef FORT_XOR_VERSION
  fort_random_data_xor(size, buf);
#else
  fort_random_data(size, buf);
#endif
}

static IUTIME fort_next_localmix = 0;
//#define FORT_SECONDS_BETWEEN_LOCALMIXES 60*60
#define FORT_SECONDS_BETWEEN_LOCALMIXES 60*60

#ifdef FORT_USE_WEB  
static IUTIME fort_next_webmix = 0;
//#define FORT_SECONDS_BETWEEN_WEBMIXES 12*3600
#define FORT_SECONDS_BETWEEN_WEBMIXES 12*3600
#endif

#define RANDOMNESS_IN_ALPHABETS 2

#define DEBUG65 2

void fort_strrepl(unsigned char *dest,unsigned char *oldstring, unsigned char *newstring)
{
  unsigned char *stringstart;
#ifdef DEBUG65
  if(verbose) {  
    fprintf(stdout,"fort_strrepl(): oldstring:%s", oldstring);
    fprintf(stdout,", newstring:%s", newstring);
    fprintf(stdout,", olddest:%s", dest);
  }
#endif
  if((stringstart = strstr(dest, oldstring)) != NULL) {
    memmove(stringstart + strlen(newstring),
	    stringstart + strlen(oldstring),
	    strlen(stringstart + strlen(oldstring)) + 1);
    memmove(stringstart, newstring, strlen(newstring));
  }
#ifdef DEBUG65
  if(verbose) {  
    fprintf(stdout,", newdest:%s", dest);
    fprintf(stdout,"\n");
  }
#endif
}

void fort_mix()
{
  int webmix, localmix;
  unsigned char temp[64];

  IUTIME seconds;

  seconds = getseconds();
  
  webmix = 0;
  localmix = 0;

#ifdef FORT_USE_WEB  
  if(fort_next_webmix == 0 ||
     fort_next_webmix <= seconds) {
    fort_next_webmix = seconds + FORT_SECONDS_BETWEEN_WEBMIXES;
    fort_next_localmix = seconds + FORT_SECONDS_BETWEEN_LOCALMIXES;
    webmix = 1;
    localmix = 1;
  }
#endif
  
  if(fort_next_localmix == 0 ||
     fort_next_localmix <= seconds) {
    fort_next_localmix = seconds + FORT_SECONDS_BETWEEN_LOCALMIXES;
    localmix = 1;
  }

  if(!webmix && !localmix)
    return;
  
  if(verbose) {  
    char timebuf[128];
    strftime(timebuf, sizeof(timebuf), TIMEFORMAT,
	     localtime((time_t *)&seconds));
    fprintf(stdout,"Fort mix time:      %s\n", timebuf);

#ifdef FORT_USE_WEB
    if(fort_use_web) {
      strftime(timebuf, sizeof(timebuf), TIMEFORMAT,
	       localtime((time_t *)&fort_next_webmix));
      fprintf(stdout,"Next fort webmix:   %s\n", timebuf);
    }
#endif

    strftime(timebuf, sizeof(timebuf), TIMEFORMAT,
	     localtime((time_t *)&fort_next_localmix));
    fprintf(stdout,"Next fort localmix: %s\n", timebuf);
  }
  
  fort_internal_random_data_1(sizeof(temp), temp);
  fort_reseed(sizeof(temp), temp);
  
#if defined FORT_USE_WEB || \
  defined USE_RDRAND || \
  defined USE_RDSEED || \
  defined USE_NEWRESSU_COMMAND
  unsigned char digest[HashLen];
#endif    
  
#ifdef FORT_USE_WEB
  if(webmix && fort_use_web) {
    char *webpages[] = {
      "https://moijari.com:5001/",
      "https://moijari.com:5005/",
      "https://moijari.com:5006/",
    };

    for(int c = 0; c < sizeof(webpages) / sizeof(webpages[0]); c++) {
      unsigned char *p;
      
      unsigned char scheme[32];
      unsigned char host[32];
      unsigned char port[10];
      unsigned char rest[128];
      
      p = webpages[c];
      parse_string(scheme, sizeof(scheme), &p);
      
      if(check_string("://", p)) {
	p += 3;
	parse_string(host, sizeof(host), &p);
      } else {
	strcpy(host, scheme);
	strcpy(scheme,"http");
      }
      
      if(check_string(":", p)) {
	p++;
	parse_string(port, sizeof(port), &p);
      } else {
	strcpy(port,"80");
      }
      
      if(*p != '/') 
	strcpy(rest, "/");
      else
	strcpy(rest, p);
      
      if(verbose) {    
	fprintf(stdout,"URL:%s ", webpages[c]);
	fprintf(stdout,"[scheme:%s]", scheme);
	fprintf(stdout,"[host:%s]", host);
	fprintf(stdout,"[port:%s]", port);
	fprintf(stdout,"[rest:%s]", p);
	fflush(stdout);
      }
      
      if(!strcmp(scheme,"http")) {
	fort_hash_http_page(host, port, p, digest);
      } else if(!strcmp(scheme,"https")) {
	fort_hash_https_page(host, port, p, digest);
      }
      fort_reseed(sizeof(digest), digest);
    }
  }
#endif // #ifdef FORT_USE_WEB
    
  if(localmix) {    
#ifdef FORT_USE_URANDOM
    memset(temp, 0, sizeof(temp));
    fort_readfile_xor(sizeof(temp), temp,
		      "/dev/urandom");
    fort_reseed(sizeof(temp), temp);
#endif
  
#ifdef FORT_USE_RANDOM
    memset(temp, 0, sizeof(temp));
    fort_readfile_xor(sizeof(temp), temp,
		      "/dev/random");
    fort_reseed(sizeof(temp), temp);
#endif

#if defined USE_RDRAND || \
  defined USE_RDSEED
    HashCtx hash;
    unsigned char digest[HashLen];
#endif
    
#ifdef USE_RDRAND

    memset(temp, 0, sizeof(temp));
    if(rdrand_bytes(sizeof(temp), temp)) {
      
      HashInit(&hash);
      
      hash_update_cvar(&hash);
      hash_update_ressu(&hash);

      HashUpdate(&hash, temp, sizeof(temp));
      HashFinal(digest, &hash);
      
      if(verbose) {
	fprintf(stdout,", data: ");
	for(int c = 0; c < sizeof(temp); c++) {
	  fprintf(stdout,"%02x", temp[c]);
	}
	fprintf(stdout,", sha256: ");
	for(int c = 0; c < HashLen; c++) {
	  fprintf(stdout,"%02x", digest[c]);
	}
	fprintf(stdout,"\n");
	fflush(stdout);

	newressu_output = 0;
      }
      
      fort_reseed(sizeof(digest), digest);
    }  
    
#endif // #ifdef USE_RDRAND
  
#ifdef USE_RDSEED
  
    memset(temp, 0, sizeof(temp));
    if(rdseed_bytes(sizeof(temp), temp)) {
      
      HashInit(&hash);

      hash_update_cvar(&hash);
      hash_update_ressu(&hash);
      
      HashUpdate(&hash, temp, sizeof(temp));
      HashFinal(digest, &hash);
      
      if(verbose) {
	fprintf(stdout,", data: ");
	for(int c = 0; c < sizeof(temp); c++) {
	  fprintf(stdout,"%02x", temp[c]);
	}
	fprintf(stdout,", sha256: ");
	for(int c = 0; c < HashLen; c++) {
	  fprintf(stdout,"%02x", digest[c]);
	}
	fprintf(stdout,"\n");
	fflush(stdout);

	newressu_output = 0;
      }
      
      fort_reseed(sizeof(digest), digest);
    }

#endif // #ifdef USE_RDSEED

#ifdef FORT_USE_NEWRESSU_COMMAND

    char *commands[] = {
      "$newressu -2 -s8 -w8 -l1 --lineno",
      "$newressu -2 -s8 -w8 -l1 --lineno --fast",
      "$newressu -2 -s8 -w8 -l1 --lineno --urandom",
#ifdef USE_RDRAND
      "$newressu -2 -s8 -w8 -l1 --lineno --rdrand",
#endif
#ifdef USE_RDSEED
      "$newressu -2 -s8 -w8 -l1 --lineno --rdseed",
#endif
      "$newressu -2 -s8 -w8 -l1 --lineno --pseudoressu",
#ifdef RANDOMNESS_IN_ALPHABETS
      "$newressu --cn -s8 -w4 -l1 --lineno --single",
      "$newressu --deu -s8 -w8 -l1 --lineno --single",
      "$newressu --dnk -s8 -w8 -l1 --lineno --single",
      "$newressu --eng -s8 -w8 -l1 --lineno --single",
      "$newressu --est -s8 -w8 -l1 --lineno --single",
      "$newressu --fin -s8 -w8 -l1 --lineno --single",
      "$newressu --fra -s8 -w8 -l1 --lineno --single",
      "$newressu --gbr -s8 -w8 -l1 --lineno --single",
      "$newressu --got -s8 -w8 -l1 --lineno --single",
      "$newressu --grc -s8 -w8 -l1 --lineno --single",
      "$newressu --ita -s8 -w8 -l1 --lineno --single",
      "$newressu --jp -s8 -w4 -l1 --lineno --single",
      "$newressu --jp1 -s8 -w4 -l1 --lineno --single",
      "$newressu --jp2 -s8 -w4 -l1 --lineno --single",
      "$newressu --jp3 -s8 -w4 -l1 --lineno --single",
      "$newressu --kor -s8 -w4 -l1 --lineno --single",
      "$newressu --ltu -s8 -w8 -l1 --lineno --single",
      "$newressu --lva -s8 -w8 -l1 --lineno --single",
      "$newressu --nor -s8 -w8 -l1 --lineno --single",
      "$newressu --rus -s8 -w8 -l1 --lineno --single",
      "$newressu --swe -s8 -w8 -l1 --lineno --single",
#endif
    };

    for(int c = 0; c < sizeof(commands) / sizeof(commands[0]); c++) {
      if(strstr(commands[c], "--fort")) {
	fprintf(stderr,"%s: fort_mix(): cannot call fort recursively \"%s\"\n",
		procname, commands[c]);
      } else {
	unsigned char command[256];
	strcpy(command, commands[c]);

	// string:$newressu, newstring:/bin/newressu2, olddest:$newressu -2 -s8 -w8 -l1 --lineno, newdest:/bin/newressu2 -2 -s8 -w8 -l1 --lineno

	if(strstr(procname,"newressu") && strlen(procname) < 32)
	  fort_strrepl(command, "$newressu", procname);
	else
	  fort_strrepl(command, "$newressu", "/bin/newressu");

	fort_hash_command(command, digest);
	fort_reseed(sizeof(digest), digest);
      }
    }
#endif // #ifdef FORT_USE_NEWRESSU_COMMAND
  }
}

void file_digest(char *filename,unsigned char temp[HashLen])
{
  int c;
  unsigned char buffer[1024];
  FILE *fp1;
  HashCtx ctx;
  
  HashInit(&ctx);
  if((fp1 = fopen(filename, "rb")) != NULL) {
    while((c = fread(buffer, 1, sizeof(buffer), fp1)) > 0)
      HashUpdate(&ctx, buffer, c);
    fclose(fp1);
  }
  HashFinal(temp, &ctx);
}

#define DEBUG88

void fort_init()
{
  int c;
  unsigned char temp32[32];

  memset(temp32, 0, sizeof(temp32));
  memset(fort_bytes, 0, sizeof(fort_bytes));
  memset(fort_key, 0, sizeof(fort_key));
  
  if(verbose) {
    fprintf(stdout,"hash: %s",
	    HashName);
    fprintf(stdout,", pools: %d*%ld", FORT_POOLS,
	    sizeof(struct fort_pool));
    fprintf(stdout,", HashCtx: %ld",
	    sizeof(HashCtx));
    fprintf(stdout,", hashsize: %d",
	    HashLen);
    fprintf(stdout,"\n");
  }
  
  unsigned int save_fort_internal_events;
  save_fort_internal_events = fort_internal_events;
  fort_internal_events = 1;

  fort_next_save = 0;
  
  clearcvar();

  // Initialize buffers

  for(c = 0; c < FORT_POOLS; c++) {
    fort_pools[c].length = 0;
    HashInit(&fort_pools[c].pool);
  }
  
  // first key to fort_key
  
  fort_internal_random_data_1(sizeof(fort_key), fort_key);

#ifdef ADDITIONAL_SECRETS  

  // additional constant randomness from program

  fort_reseed(strlen(copyright), copyright);
  fort_reseed(strlen(programname), programname);

  unsigned char filedigest[HashLen];
  file_digest("/proc/self/exe", filedigest);
  fort_reseed(strlen(filedigest), filedigest);
  
  fort_reseed(strlen(program_secret), program_secret);

  // additional constant randomness
  // from you (one more secret adversary
  // has to guess)

  FILE *fp1;
  if((fp1 = fopen(fort_secret_file, "r")) != NULL) {
    fclose(fp1);
  } else {
    if((fp1 = fopen(fort_secret_file, "w")) != NULL) {
      fprintf(fp1,"Your secret additional constant data to fort:\n");
      fclose(fp1);
      unsigned char temp2[160];
      sprintf(temp2,"%s -2 -l1 >>%s", procname, fort_secret_file);
      system(temp2);
      fprintf(stdout,"File %s written\n", fort_secret_file);
    }
  }
  fort_reseed_file(fort_secret_file);

#endif
  
#ifdef DEBUG10

  FILE *fp1;
  
  // Empty events file

  if((fp1 = fopen(fort_events_file, "w"))!=NULL)
    fclose(fp1);
  event_id = 0;

#endif // #ifdef DEBUG10

  // Mix fort key with web + local
  // random numbers
  
  fort_mix();

  for(c = 0; c < FORT_POOLS; c++) {
    FORT_INTERNAL_EVENTS_START(31)
    fort_internal_random_data_2(sizeof(temp32), temp32);
    hash_update(&fort_pools[c].pool,
      temp32, sizeof(temp32));
    FORT_INTERNAL_EVENTS_END(32)
  }

#ifdef FORT_INTERNAL_EVENTS

  if(fort_internal_events) {

    // Create some internal events

    for(c=0; c<32; c++) {
      FORT_INTERNAL_EVENTS_START(34)
      fort_internal_random_data_3(sizeof(temp32), temp32);
      FORT_INTERNAL_EVENTS_END(35)
    }
  }

#endif // #ifdef FORT_INTERNAL_EVENTS

  fort_reseed_count = 0;
  fort_next_reseed = 0;

  // Reseed fort_key with new events

  fort_internal_random_data_3(sizeof(temp32), temp32);
  fort_reseed(sizeof(temp32), temp32);

  fort_restore();

  // Forget temp

  memset(temp32, 0, sizeof(temp32));

  fort_internal_events = save_fort_internal_events;

  //fort_reseed_count = 0;
  //fort_next_reseed = 0;

  fort_next_save = 1;
}

void fort_version()
{
  fprintf(stderr,"%s", programname); // touch these outside MAIN
  fprintf(stderr,", %s\n", copyright);
}

#define aMAIN 2
#ifdef MAIN

unsigned char *procname;

int main(int argc, char *argv[])
{
  procname = argv[0];
  fort_version();
  fort_init();
  return(0);
}

#endif // #ifdef MAIN

fort.h

/* Perustuu Bruce Schneierin kirjassa esittämään fortuna järjestelmään.
 * Written by Jari Kuivaniemi
 */

#ifndef SHA256_H
#include "sha256.h"
#endif

#define aUSE_RDRAND 2
#define aUSE_RDSEED 2

typedef unsigned long long IUTIME;

extern int fort_verbose;
extern int fort_partial_line;
extern int fort_use_web;

extern unsigned char fort_random_file[128];
extern unsigned char fort_secret_file[128];
extern unsigned char fort_pools_file[128];
#ifdef DEBUG10
static char fort_events_file[128] = "fortevents.deb";
#endif
unsigned char cvar[16];
int cvarsize;

void inccvar();
void clearcvar();
void hash_update_cvar(HashCtx *hash);

void fort_add_random_event(int *pool, int source, int mode, int len, unsigned char *buf);
void fort_add_random_event_timer_start(IUTIME *micros);
void fort_add_random_event_timer_do(int *pool, int source, int mode, IUTIME *millis);
void fort_add_random_event_split(int *pool, int source, int mode, int len, unsigned char *buf,int size);
void fort_add_random_event_time(int *pool, int source, int mode);
void fort_rekey(unsigned char *buf);
void fort_pseudo_random_data(int len,unsigned char *buf);
void fort_pseudo_random_data_xor(int len,unsigned char *buf);
void fort_reseed(int len,unsigned char *buf);
void fort_random_data(int len,unsigned char *buf);
void fort_random_data_xor(int len,unsigned char *buf);
int fort_random_data_byte();
void fort_clear();
int fort_random_data_byte_limit(int limit);
void fort_random_data_buffer(int size, unsigned char *buffer);
void fort_save();
void fort_read_file();
void fort_mix();
void fort_init();
void hash_init(HashCtx *hash);
void hash_update(HashCtx *hash, unsigned char *data, int len);
void hash_final(unsigned char digest[HashLen], HashCtx *hash);

void fort_hash_http_page(unsigned char *host,unsigned char *port, unsigned char *page, unsigned char *hash);
void fort_hash_https_page(unsigned char *host,unsigned char *port, unsigned char *page, unsigned char *hash);

void fort_hash_command(unsigned char *command, unsigned char *hash);

int rdrand_bytes(int buflen, unsigned char *buf);
int rdseed_bytes(int buflen, unsigned char *buf);

#ifdef FORT_INTERNAL_EVENTS
#define FORT_INTERNAL_EVENTS_START(source) \
  IUTIME micros;                           \
  static int \
    pool=0, pool2=0; \
  if(fort_internal_events) { \
    fort_add_random_event_time(&pool, \
    source, fort_internal_event_mode); \
    fort_add_random_event_timer_start(&micros); \
  }
#else
#define FORT_INTERNAL_EVENTS_START(source)
#endif

#ifdef FORT_INTERNAL_EVENTS
#define FORT_INTERNAL_EVENTS_END(source) \
  if(fort_internal_events) \
    fort_add_random_event_timer_do(&pool2, \
      source, fort_internal_event_mode, \
      &micros);
#else
#define FORT_INTERNAL_EVENTS_END(source)
#endif

#ifdef FORT_EVENTS
#define FORT_EVENTS_START(source) \
  IUTIME micros;                           \
  static int \
    pool=0, pool2=0; \
  if(fort_events) { \
    fort_add_random_event_time(&pool, \
    source, fort_event_mode); \
    fort_add_random_event_timer_start(&micros); \
  }
#else
#define FORT_EVENTS_START(source)
#endif

#ifdef FORT_EVENTS
#define FORT_EVENTS_END(source) \
  if(fort_events) \
    fort_add_random_event_timer_do(&pool2, \
      source, fort_event_mode, \
      &micros);
#else
#define FORT_EVENTS_END(source)
#endif

sha256.c

/* Written from SHA256 documents by Jari Kuivaniemi, read "http://en.wikipedia.org/wiki/SHA-2" */
#include <stdio.h>
#include <memory.h>
#include <string.h>

#include "sha256.h"

extern unsigned char *procname;

//#ifdef MAIN // in Makefile

unsigned char *sha_name = "SHA256 v1.0";

void SHA256Init(SHA256_CONTEXT *sha256)
{
  sha256->state[0] = 0x6a09e667;
  sha256->state[1] = 0xbb67ae85;
  sha256->state[2] = 0x3c6ef372;
  sha256->state[3] = 0xa54ff53a;
  sha256->state[4] = 0x510e527f;
  sha256->state[5] = 0x9b05688c;
  sha256->state[6] = 0x1f83d9ab;
  sha256->state[7] = 0x5be0cd19;
  
  sha256->count = 0;
}

IUWORD k256[64] = {
  0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
  0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
  0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
  0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
  0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
  0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
  0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
  0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
};

#define RR32(word,bits) ( ((word) >> (bits)) | ((word) << (32 - (bits))) )
#define RS32(word,bits) ( ((word) >> (bits)) )

void SHA256Transform(IUWORD state[8], IUBYTE buffer[64])
{
  int i;
  
  IUWORD w[64], s0;
  IUWORD a, b, c, d, e, f, g, h;
  IUWORD maj, t2, s1;
  IUWORD ch, t1;
  
  for(i = 0; i < 16; i++) {
    w[i] =
      (IUWORD)buffer[i * 4 + 0] << 24 |
      (IUWORD)buffer[i * 4 + 1] << 16 |
      (IUWORD)buffer[i * 4 + 2] << 8  |
      (IUWORD)buffer[i * 4 + 3];
  }
  
  for(i=16;i<64;i++) {
    s0 = (RR32(w[i - 15], 7)) ^ (RR32(w[i - 15], 18)) ^ (RS32(w[i - 15], 3));
    s1 = (RR32(w[i - 2], 17)) ^ (RR32(w[i - 2], 19)) ^ (RS32(w[i - 2], 10));
    w[i] = w[i - 16] + s0 + w[i - 7] + s1;
  }
  
  a = state[0];
  b = state[1];
  c = state[2];
  d = state[3];
  e = state[4];
  f = state[5];
  g = state[6];
  h = state[7];
  
  for(i=0;i<64;i++) {
    s0 = (RR32(a, 2)) ^ (RR32(a, 13)) ^ (RR32(a, 22));
    maj = (a & b) ^ (a & c) ^ (b & c);
    t2 = s0 + maj;
    s1 = (RR32(e, 6)) ^ (RR32(e, 11)) ^ (RR32(e, 25));
    ch = (e & f) ^ ((~ e) & g);
    t1 = h + s1 + ch + k256[i] + w[i];
    
    h = g;
    g = f;
    f = e;
    e = d + t1;
    d = c;
    c = b;
    b = a;
    a = t1 + t2;
  }
  
  state[0] = state[0] + a;
  state[1] = state[1] + b;
  state[2] = state[2] + c;
  state[3] = state[3] + d;
  state[4] = state[4] + e;
  state[5] = state[5] + f;
  state[6] = state[6] + g;
  state[7] = state[7] + h;
}

void SHA256Update(SHA256_CONTEXT *sha256, unsigned char *data, int len)
{
  int i, j;
  
  j = (int)sha256->count & 63;
  
  sha256->count += len;
  
  if((j + len) > 63) {
    memcpy(&sha256->buffer[j], data, 64 - j); /* last bytes of next block */
    
    SHA256Transform(sha256->state, sha256->buffer);
    for (i = 64 - j ; i + 63 < len; i += 64) {
      SHA256Transform(sha256->state, &data[i]);
    }
    j = 0;
  } else
    i = 0;
  
  memcpy(&sha256->buffer[j], &data[i], len - i);
}

void SHA256Final(unsigned char digest[32], SHA256_CONTEXT *sha256)
{
  int i, j;
  unsigned char filelenght[8];
  IULONG count;
  
  count = sha256->count << 3;
  SHA256Update(sha256, "\200", 1);
  
  while((sha256->count & 63) != 56)
    SHA256Update(sha256, "\0", 1);
  
  filelenght[0] = (unsigned char)(count >> 56) & 0xff;
  filelenght[1] = (unsigned char)(count >> 48) & 0xff;
  filelenght[2] = (unsigned char)(count >> 40) & 0xff;
  filelenght[3] = (unsigned char)(count >> 32) & 0xff;
  filelenght[4] = (unsigned char)(count >> 24) & 0xff;
  filelenght[5] = (unsigned char)(count >> 16) & 0xff;
  filelenght[6] = (unsigned char)(count >> 8 ) & 0xff;
  filelenght[7] = (unsigned char)(count & 0xff);
  
  SHA256Update(sha256, filelenght, sizeof(filelenght));
  
  for(i = 0, j = 0; i < 8; i++) {
    digest[j++] = (unsigned char) (sha256->state[i] >> 24) & 0xff;
    digest[j++] = (unsigned char) (sha256->state[i] >> 16) & 0xff;
    digest[j++] = (unsigned char) (sha256->state[i] >> 8) & 0xff;
    digest[j++] = (unsigned char) (sha256->state[i] ) & 0xff;
  }
}

#ifdef MAIN

void printhex(char *name, unsigned char *digest, int len)
{
  int c;

  fprintf(stdout,"%s=", name);
  for(c = 0; c < len; c++) {
    fprintf(stdout,"%02x", digest[c]);
  }
  fflush(stdout);
}

int shahexdigit(int c)
{
  if(c >= '0' && c <= '9') return(c - '0');
  else if(c >= 'a' && c <= 'f') return(c - 'a' + 10);
  else if(c >= 'A' && c <= 'F') return(c - 'A' + 10);
  return(0);
}

void sha_test()
{
  int c, d, e, ok, allok;
  SHA256_CONTEXT sha256;
  unsigned char result[32], *p;
  
  fprintf(stdout,"%s", sha_name);
  fprintf(stdout,", Context size: %ld", sizeof(SHA256_CONTEXT));
  fprintf(stdout,", Hash size: 32");
  fprintf(stdout,", IUBYTE size: %ld", sizeof(IUBYTE));
  fprintf(stdout,", IUWORD size: %ld", sizeof(IUWORD));
  fprintf(stdout,", IULONG size: %ld", sizeof(IULONG));

  struct test {
    unsigned char *string;
    int count;
    unsigned char *result;
  } tests[] = {
    { "abc", 1, "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad" },
    { "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", 1, "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1" },
    { "a", 1000000, "cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0" },
    { "The quick brown fox jumps over the lazy dog", 1, "d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592" },
  };

  allok = 1;
  for(c = 0; c < sizeof(tests) / sizeof(tests[0]); c++) {
    SHA256Init(&sha256);
    for(d = 0; d < tests[c].count; d++)
      SHA256Update(&sha256, tests[c].string, strlen(tests[c].string));
    SHA256Final(result, &sha256);
    p = tests[c].result;
    d = 0;
    ok = 1;
    while(*p != '\0') {
      if(*p != '\0')
	e = shahexdigit(*p++);
      if(*p!='\0')
	e = e * 16 + shahexdigit(*p++);
      if(e != result[d])
	ok = 0;
      d++;
    }
    if(ok) {
      fprintf(stdout,"\nsha256 test ");
      fprintf(stdout,"string: %s", tests[c].string);
      fprintf(stdout,", count: %d, ", tests[c].count);
      printhex("tmp32", result, sizeof(result));
      fprintf(stdout,", result: %s", tests[c].result);
      fprintf(stdout," ok!!!!");
      fflush(stdout);
    } else {
      fprintf(stdout,"\nsha256 test ");
      fprintf(stdout,"string: %s", tests[c].string);
      fprintf(stdout,", count: %d, ", tests[c].count);
      printhex("tmp32", result, sizeof(result));
      fprintf(stdout,", result: %s", tests[c].result);
      fprintf(stdout," failed!!!!");
      fflush(stdout);
      allok = 0;
    }
  }
  if(allok) {
    fprintf(stdout,", sha256 tests ok!!!!\n");
  } else {
    fprintf(stdout,", sha256 tests failed!!!!\n");
  }
  fflush(stdout);
}

int main(int argc, char *argv[])
{
  sha_test();
}

#endif

sha256.h

#ifndef SHA256_H
#define SHA256_H

#define HashName   "SHA256"
#define HashInit   SHA256Init
#define HashUpdate SHA256Update
#define HashFinal  SHA256Final
#define HashLen    32
#define HashCtx    SHA256_CONTEXT

typedef unsigned char IUBYTE;
typedef unsigned int IUWORD;
typedef unsigned long long IULONG;

typedef struct {
    IUWORD state[8];
    IULONG count;
    IUBYTE buffer[64];
} SHA256_CONTEXT;

void SHA256Init(SHA256_CONTEXT *sha256);
void SHA256Update(SHA256_CONTEXT* sha256, unsigned char *data, int len);
void SHA256Final(unsigned char digest[32], SHA256_CONTEXT *sha256);

void sha_test();

#endif

intelrandom.c

#include <stdio.h>
#include <memory.h>

#include "fort.h"
#include "newressu.h"

#if defined USE_RDRAND ||			\
    defined USE_RDSEED

// see: https://software.intel.com/content/www/us/en/develop/articles/intel-digital-random-number-generator-drng-software-implementation-guide.html

void _cpuid(unsigned int leaf, unsigned int subleaf,
	    unsigned int *a, unsigned int *b, unsigned int *c, unsigned int *d)
{
  asm volatile("cpuid"
	       : "=a" (*a), "=b" (*b), "=c" (*c), "=d" (*d)
	       : "a" (leaf), "c" (subleaf) );
}

int _is_cpu_vendor(unsigned char *cpuvendor)
{
  int ok = 0;
  unsigned int a, b, c, d;

  _cpuid(0, 0, &a, &b, &c, &d);

  if(memcmp((char *)(&b), cpuvendor,4) == 0 &&
     memcmp((char *)(&d), cpuvendor+4,4) == 0 &&
     memcmp((char *)(&c), cpuvendor+8,4) == 0)
    ok = 1;

  return(ok);
}

#endif

#ifdef USE_RDRAND

int _has_rdrand()
{
  int ok = 0;
  unsigned int a, b, c, d;

  _cpuid(1, 0, &a, &b, &c, &d);
  if((c & 0x40000000) == 0x40000000) {
    ok = 1;
  }

  return(ok);
}

int _rdrand_long(unsigned long *therand)
{
  unsigned char ret;

  asm volatile("rdrand %0; setc %1"
	       : "=r" (*therand), "=qm" (ret) );

  return(int) ret;
}

int rdrand_bytes(int buflen, unsigned char *buf)
{
  int c, n, ret = 0;
  unsigned long l;

  if(verbose) {
    if(newressu_output == 1)
      fprintf(stdout,"\n");
    newressu_output = 0;
  }
  
  if(_is_cpu_vendor("GenuineIntel") && _has_rdrand()) {
    if(verbose)
      fprintf(stdout,"Intel rdrand");
    ret = 1;
  } else if(_is_cpu_vendor("AuthenticAMD") && _has_rdrand()) {
    if(verbose)
      fprintf(stdout,"AMD rdrand");
    ret = 1;
  }
  
  if(ret) {
    while(buflen > 0) {
      int tries = 0;

      while(++tries < 100) {
	if((ret = _rdrand_long(&l)) == 1) { // 1 ok, 0 fail
	  break;
	}
	//fprintf(stdout," %016lu",l);
      }

      if(verbose) {
	fprintf(stdout,", t:%d ", tries);
	newressu_output = 1;
      }

      if(ret == 0)
	break;

      n = (buflen < sizeof(l) ? buflen : sizeof(l));

      if(verbose) {
	for(c = 0; c < sizeof(l); c++)
	  fprintf(stdout,"%02x", ((unsigned char *)&l)[c]);
	newressu_output = 1;
      }

      memcpy(buf, (unsigned char *)&l, n);

      buf += n;
      buflen -= n;
    }
  }

  if(verbose && newressu_output) {
    fprintf(stdout, "\n");
    newressu_output = 0;
  }

  return(ret);
}

#endif // #ifdef USE_RDRAND

#ifdef USE_RDSEED

int _has_rdseed()
{
  int ok = 0;
  unsigned int a, b, c, d;

  _cpuid(7, 0, &a, &b, &c, &d);
  if((b & 0x40000) == 0x40000) {
    ok = 1;
  }

  return(ok);
}

int _rdseed_long(unsigned long *therand)
{
  unsigned char ret;

  asm volatile("rdseed %0; setc %1"
	       : "=r" (*therand), "=qm" (ret) );

  return(int) ret;
}

int rdseed_bytes(int buflen, unsigned char *buf)
{
  int c, n, ret = 0;
  unsigned long l;

  if(verbose) {
    if(newressu_output == 1)
      fprintf(stdout,"\n");
    newressu_output = 0;
  }
  
  if(_is_cpu_vendor("GenuineIntel") && _has_rdseed()) {
    if(verbose)
      fprintf(stdout,"Intel rdseed");
    ret = 1;
  } else if(_is_cpu_vendor("AuthenticAMD") && _has_rdseed()) {
    if(verbose)
      fprintf(stdout,"AMD rdseed");
    ret = 1;
  }

  if(ret) {
    while(buflen > 0) {
      int tries = 0;

      while(++tries < 1000) {
	if((ret = _rdseed_long(&l)) == 1) { // 1 ok, 0 fail
	  break;
	}
	//fprintf(stdout," %lu",l);
      }

      if(verbose) {
	fprintf(stdout,", t:%d ", tries);
      }

      if(ret == 0)
	break;

      n = (buflen < sizeof(l) ? buflen : sizeof(l));

      if(verbose) {
	for(c = 0; c < sizeof(l); c++)
	  fprintf(stdout,"%02x",((unsigned char *)&l)[c]);
	newressu_output = 1;
      }

      memcpy(buf, (unsigned char *)&l, n);

      buf += n;
      buflen -= n;
    }
  }

  if(verbose && newressu_output) {
    fprintf(stdout,"\n");
    newressu_output = 0;
  }
  
  return(ret);
}

#endif // #ifdef USE_RDSEED

webrandom.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <memory.h>
#include <errno.h>

#include <openssl/ssl.h>
#include <openssl/err.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>

#include "sha256.h"
#include "fort.h"

extern unsigned char *procname;
extern int newressu_output; 
extern int verbose;

#include <stdarg.h>

void dbs_printf(unsigned char **buf, size_t *buf_length, const unsigned char *format, ...)
{
  int count;
  va_list args;;
  
  va_start(args, format);
  count = vsnprintf(*buf, *buf_length, format, args) + 1;
  va_end(args);
  if(*buf_length < count) {
    *buf_length = count;
    *buf = realloc(*buf, *buf_length);
    va_start(args, format);
    count = vsnprintf(*buf, *buf_length, format, args) + 1;
    va_end(args);
  }
}

int fort_connect(unsigned char *host, unsigned char *port)
{
  struct addrinfo hints, *res, *resp;
  int s, status;
  
  memset(&hints, 0, sizeof(hints));
  hints.ai_family = AF_UNSPEC;
  hints.ai_socktype = SOCK_STREAM;
  hints.ai_flags = AI_PASSIVE;
  
  if((status = getaddrinfo(host, port, &hints, &res)) != 0) {
    fprintf(stderr,"\n%s: getaddrinfo", procname);
    fprintf(stderr,", status %d", status);
    fprintf(stderr,", gai_strerror(): %s", gai_strerror(status));
    fprintf(stderr,", errno %d\n", errno);
    fflush(stderr);
  }
  
  for(resp = res; resp != NULL; resp = resp->ai_next) {
    if((s = socket(resp->ai_family, resp->ai_socktype, resp->ai_protocol)) < 0)
      continue;
    if(connect(s, resp->ai_addr, resp->ai_addrlen) == 0)
      break;
    close(s);
  }

  freeaddrinfo(res);

  return(s);
}

#define aDEBUG27 2

void fort_hash_http_page(unsigned char *host,unsigned char *port, unsigned char *page, unsigned char *hash) // 202011 JariK
{
  int c, cnt, s, status, bytes, characters;

  if((s = fort_connect(host, port))<0) {
    fprintf(stderr,"\n%s: cannot fort_connect()", procname);
    fprintf(stderr,", status: %d", s);
    fprintf(stderr,", errno: %d" , errno);
    perror("fort_connect");
    fflush(stderr);
  }

  // GET / HTTP/1.0\r\nHost: moijari.com:5006\r\n
    
  unsigned char *format =
    "GET %s HTTP/1.0\r\n" \
    "Host: %s:%s\r\n" \
    "User-Agent: %s\r\n";
  static unsigned char *msg = NULL;
  static size_t msg_length = 0;

  extern unsigned char *fortuseragent;
  
  dbs_printf(&msg, &msg_length, format, page, host, port, fortuseragent);

  if((status = write(s, msg, strlen(msg))) < 0) {
    fprintf(stderr, "\n%s: write(), error: %d\n", procname, errno);
    perror("write");
    fflush(stderr);
  }

  HashCtx ctx;
  unsigned char buffer[1024];

  HashInit(&ctx);

  bytes = 0;
  characters = 0;

  while((cnt = read(s, buffer, sizeof(buffer))) > 0) {
#ifdef DEBUG27
    write(1, buffer, cnt);
    fflush(stdout);
#endif
    HashUpdate(&ctx, buffer, cnt);
    bytes += cnt;
    for(c = 0; c < cnt; c++) {
      if(buffer[c] < 0x80 || buffer[c] > 0xbf)
	characters++;
    }
  }

  hash_update_cvar(&ctx);
  HashFinal(hash, &ctx);
  if(verbose) {
    fprintf(stdout,"\n, %d bytes read", bytes);
    fprintf(stdout,", %d characters read", characters);
    fprintf(stdout,", sha256: ");
    for(int c = 0;c < HashLen; c++) {
      fprintf(stdout,"%02x", hash[c]);
    }
    fprintf(stdout,"\n");
    newressu_output = 0; 
  }

  close(s);
}

#define aDEBUG49 2
#define aDEBUG50 2

void fort_hash_https_page(unsigned char *host,unsigned char *port, unsigned char *page, unsigned char *hash) // 202011 JariK
{
  int c, cnt, s, status, bytes, characters;

  SSL_METHOD *method = NULL;
  SSL_CTX *ctx = NULL;
  SSL *ssl;

  SSL_library_init(); //see: http://h30266.www3.hpe.com/odl/axpos/opsys/vmsos84/BA554_90007/ch04s03.html
  OpenSSL_add_ssl_algorithms();
  SSL_load_error_strings();

#ifdef DEBUG49
  fprintf(stdout,"SSLv23_client_method()\n");
#endif
  if((method = (SSL_METHOD *)    
      SSLv23_client_method()) == NULL) {
    fprintf(stderr,"\n%s: cannot SSLv3_server_method()", procname);
    fflush(stderr);
  }

#ifdef DEBUG49
  fprintf(stdout,"SSL_CTX_new()\n");
#endif
  if((ctx = SSL_CTX_new(method)) == NULL) {
    fprintf(stderr,"\n%s: cannot SSL_CTX_new()", procname);
    fflush(stderr);
  }

#ifdef DEBUG49
  fprintf(stdout,"SSL_new()\n");
#endif
  if((ssl = SSL_new(ctx)) == NULL) {
    fprintf(stderr,"\n%s: cannot SSL_new()", procname);
    fflush(stderr);
  }

#ifdef DEBUG49
  fprintf(stdout,"fort_connect()\n");
#endif 
 if((s = fort_connect(host, port)) < 0) {
    fprintf(stderr,"\n%s: cannot fort_connect()", procname);
    fprintf(stderr,", status: %d", s);
    fprintf(stderr,", errno: %d" , errno);
    perror("fort_connect");
    fflush(stderr);
  }

  SSL_set_fd(ssl, s);

#ifdef DEBUG49
  fprintf(stdout,"SSL_connect(ssl)\n");
#endif 
  if((status = SSL_connect(ssl)) <= 0) {
    fprintf(stderr,"\n%s: cannot SSL_connect()", procname);
    fprintf(stderr,", status: %d", status);
    fprintf(stderr,", errno: %d" , errno);
    fprintf(stderr,", SSL_get_error(): %d\n", SSL_get_error(ssl,status));
    perror("SSL_connect");
    fflush(stderr);
  }

  unsigned char *format =
    "GET %s HTTP/1.0\r\n" \
    "Host: %s:%s\r\n" \
    "User-Agent: %s\r\n";
  static unsigned char *msg = NULL;
  static size_t msg_length = 0;

  extern unsigned char *fortuseragent;

  dbs_printf(&msg, &msg_length, format, page, host, port, fortuseragent);

#ifdef DEBUG49
  fprintf(stdout,"SSL_write(), msg_length:%ld, msg=\"%s\"\n", msg_length,msg);
#endif 
  if((status = SSL_write(ssl, msg, strlen(msg))) < 0) {
    fprintf(stderr,"\n%s: SSL_write()", procname);
    fprintf(stderr,", status: %d", status);
    fprintf(stderr,", errno: %d", errno);
    fprintf(stderr,", SSL_get_error(): %d", SSL_get_error(ssl,status));
    perror("SSL_write");
    fflush(stderr);
  }
  fflush(stdout);
  
  HashCtx hashctx;
  unsigned char buffer[2048];

  HashInit(&hashctx);

  bytes = 0;
  characters = 0;

#ifdef DEBUG49
  fprintf(stdout, "SSL_read()\n");
#endif
  int bytesneeded = 0, bytesheader = 0, bytescontent = 0;
  
  do {
    cnt = SSL_read(ssl, buffer, sizeof(buffer));
    if(cnt < 0) {
      int err, err2;
      err = SSL_get_error(ssl, cnt);
      err2 = ERR_get_error();

      fprintf(stderr, "\n%s: cannot SSL_read()", procname);
      fprintf(stderr, ", retval: %d", cnt);
      fprintf(stderr, ", errno: %d", errno);
      fprintf(stderr,", SSL_get_error() %d", err);
      fprintf(stderr,", ERR_get_error() %d\n", err2);
      perror("SSL_read");
      fflush(stderr);
      break;
    }

#ifdef DEBUG50
    write(1, buffer, cnt);
#endif
    HashUpdate(&hashctx, buffer, cnt);
    bytes += cnt;

    for(c = 0; c < cnt; c++) {
      if(buffer[c] < 0x80 || buffer[c] > 0xbf)
	characters++;
    }
    if(bytesneeded == 0) {
      char *p;
      p = buffer;
      int count = 0;
      while(*p != '\0') {
	if(!strncmp(p, "\r\nContent-Length: ", 18)) {
	  bytescontent = atoi(p + 18);
	}
	if(!strncmp(p, "\r\n\r\n", 4)) {
	  bytesheader = count;
	}
	p++;
	count++;
      }
      bytesneeded = bytescontent + bytesheader + 4;
    }
  } while(SSL_pending(ssl) || bytes < bytesneeded);

  if(verbose) {
    fprintf(stdout,"\nbytescontent:%d", bytescontent);
    fprintf(stdout,", bytesheader:%d", bytesheader);
    fprintf(stdout,", bytesneeded:%d", bytesneeded);
    fflush(stdout);
  }
#ifdef OLD1

#ifdef DEBUG49
  fprintf(stdout,"SSL_read()\n");
#endif 
  while((cnt = SSL_read(ssl, buffer, sizeof(buffer))) > 0) {
#ifdef DEBUG49
    fprintf(stdout,"SSL_read() round\n");
#endif 
    
#ifdef DEBUG50
    write(1, buffer, cnt);
    fflush(stdout);
#endif
    HashUpdate(&hashctx, buffer, cnt);
    bytes += cnt;
    for(c = 0; c < cnt; c++) {
      if(buffer[c] < 0x80 || buffer[c] > 0xbf)
	characters++;
    }
  }
  fflush(stdout);
  if(cnt < 0) {
    fprintf(stderr, "\n%s: SSL_read()", procname);
    fprintf(stderr, ", status: %d", status);
    fprintf(stderr, ", errno: %d", errno);
    fprintf(stderr,", SSL_get_error(): %d", SSL_get_error(ssl,status));
    perror("SSL_read");
    fflush(stderr);
  }
  
#endif
  
  fflush(stdout);

  hash_update_cvar(&hashctx);
  HashFinal(hash, &hashctx);

  if(verbose) {
    fprintf(stdout,"\n, %d bytes read", bytes);
    fprintf(stdout,", %d characters read", characters);
    fprintf(stdout,", sha256: ");
    for(int c = 0;c < HashLen; c++) {
      fprintf(stdout,"%02x", hash[c]);
    }
    fprintf(stdout,"\n");
    newressu_output = 0; 
 }
#ifdef NOTUSED
  
#ifdef DEBUG49
  fprintf(stdout,"SSL_shutdown()\n");
#endif 
  SSL_shutdown(ssl);
#endif
  
#ifdef DEBUG49
  fprintf(stdout,"SSL_free()\n");
#endif 
  SSL_free(ssl);
#ifdef DEBUG49
  fprintf(stdout,"SSL_CTX_free()\n");
#endif 
  SSL_CTX_free(ctx);
  close(s);
}

commandrandom.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <memory.h>
#include <errno.h>

#include <openssl/ssl.h>
#include <openssl/err.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>

#include "sha256.h"
#include "fort.h"

extern unsigned char *procname;
extern int newressu_output; 
extern int verbose;

#include <stdarg.h>

void dbs_printf(unsigned char **buf, size_t *buf_length, const unsigned char *format, ...)
{
  int count;
  va_list args;;
  
  va_start(args, format);
  count = vsnprintf(*buf, *buf_length, format, args) + 1;
  va_end(args);
  if(*buf_length < count) {
    *buf_length = count;
    *buf = realloc(*buf, *buf_length);
    va_start(args, format);
    count = vsnprintf(*buf, *buf_length, format, args) + 1;
    va_end(args);
  }
}

int fort_connect(unsigned char *host, unsigned char *port)
{
  struct addrinfo hints, *res, *resp;
  int s, status;
  
  memset(&hints, 0, sizeof(hints));
  hints.ai_family = AF_UNSPEC;
  hints.ai_socktype = SOCK_STREAM;
  hints.ai_flags = AI_PASSIVE;
  
  if((status = getaddrinfo(host, port, &hints, &res)) != 0) {
    fprintf(stderr,"\n%s: getaddrinfo", procname);
    fprintf(stderr,", status %d", status);
    fprintf(stderr,", gai_strerror(): %s", gai_strerror(status));
    fprintf(stderr,", errno %d\n", errno);
    fflush(stderr);
  }
  
  for(resp = res; resp != NULL; resp = resp->ai_next) {
    if((s = socket(resp->ai_family, resp->ai_socktype, resp->ai_protocol)) < 0)
      continue;
    if(connect(s, resp->ai_addr, resp->ai_addrlen) == 0)
      break;
    close(s);
  }

  freeaddrinfo(res);

  return(s);
}

#define aDEBUG27 2

void fort_hash_http_page(unsigned char *host,unsigned char *port, unsigned char *page, unsigned char *hash) // 202011 JariK
{
  int c, cnt, s, status, bytes, characters;

  if((s = fort_connect(host, port))<0) {
    fprintf(stderr,"\n%s: cannot fort_connect()", procname);
    fprintf(stderr,", status: %d", s);
    fprintf(stderr,", errno: %d" , errno);
    perror("fort_connect");
    fflush(stderr);
  }

  // GET / HTTP/1.0\r\nHost: moijari.com:5006\r\n
    
  unsigned char *format =
    "GET %s HTTP/1.0\r\n" \
    "Host: %s:%s\r\n" \
    "User-Agent: %s\r\n";
  static unsigned char *msg = NULL;
  static size_t msg_length = 0;

  extern unsigned char *fortuseragent;
  
  dbs_printf(&msg, &msg_length, format, page, host, port, fortuseragent);

  if((status = write(s, msg, strlen(msg))) < 0) {
    fprintf(stderr, "\n%s: write(), error: %d\n", procname, errno);
    perror("write");
    fflush(stderr);
  }

  HashCtx ctx;
  unsigned char buffer[1024];

  HashInit(&ctx);

  bytes = 0;
  characters = 0;

  while((cnt = read(s, buffer, sizeof(buffer))) > 0) {
#ifdef DEBUG27
    write(1, buffer, cnt);
    fflush(stdout);
#endif
    HashUpdate(&ctx, buffer, cnt);
    bytes += cnt;
    for(c = 0; c < cnt; c++) {
      if(buffer[c] < 0x80 || buffer[c] > 0xbf)
	characters++;
    }
  }

  hash_update_cvar(&ctx);
  HashFinal(hash, &ctx);
  if(verbose) {
    fprintf(stdout,"\n, %d bytes read", bytes);
    fprintf(stdout,", %d characters read", characters);
    fprintf(stdout,", sha256: ");
    for(int c = 0;c < HashLen; c++) {
      fprintf(stdout,"%02x", hash[c]);
    }
    fprintf(stdout,"\n");
    newressu_output = 0; 
  }

  close(s);
}

#define aDEBUG49 2
#define aDEBUG50 2

void fort_hash_https_page(unsigned char *host,unsigned char *port, unsigned char *page, unsigned char *hash) // 202011 JariK
{
  int c, cnt, s, status, bytes, characters;

  SSL_METHOD *method = NULL;
  SSL_CTX *ctx = NULL;
  SSL *ssl;

  SSL_library_init(); //see: http://h30266.www3.hpe.com/odl/axpos/opsys/vmsos84/BA554_90007/ch04s03.html
  OpenSSL_add_ssl_algorithms();
  SSL_load_error_strings();

#ifdef DEBUG49
  fprintf(stdout,"SSLv23_client_method()\n");
#endif
  if((method = (SSL_METHOD *)    
      SSLv23_client_method()) == NULL) {
    fprintf(stderr,"\n%s: cannot SSLv3_server_method()", procname);
    fflush(stderr);
  }

#ifdef DEBUG49
  fprintf(stdout,"SSL_CTX_new()\n");
#endif
  if((ctx = SSL_CTX_new(method)) == NULL) {
    fprintf(stderr,"\n%s: cannot SSL_CTX_new()", procname);
    fflush(stderr);
  }

#ifdef DEBUG49
  fprintf(stdout,"SSL_new()\n");
#endif
  if((ssl = SSL_new(ctx)) == NULL) {
    fprintf(stderr,"\n%s: cannot SSL_new()", procname);
    fflush(stderr);
  }

#ifdef DEBUG49
  fprintf(stdout,"fort_connect()\n");
#endif 
 if((s = fort_connect(host, port)) < 0) {
    fprintf(stderr,"\n%s: cannot fort_connect()", procname);
    fprintf(stderr,", status: %d", s);
    fprintf(stderr,", errno: %d" , errno);
    perror("fort_connect");
    fflush(stderr);
  }

  SSL_set_fd(ssl, s);

#ifdef DEBUG49
  fprintf(stdout,"SSL_connect(ssl)\n");
#endif 
  if((status = SSL_connect(ssl)) <= 0) {
    fprintf(stderr,"\n%s: cannot SSL_connect()", procname);
    fprintf(stderr,", status: %d", status);
    fprintf(stderr,", errno: %d" , errno);
    fprintf(stderr,", SSL_get_error(): %d\n", SSL_get_error(ssl,status));
    perror("SSL_connect");
    fflush(stderr);
  }

  unsigned char *format =
    "GET %s HTTP/1.0\r\n" \
    "Host: %s:%s\r\n" \
    "User-Agent: %s\r\n";
  static unsigned char *msg = NULL;
  static size_t msg_length = 0;

  extern unsigned char *fortuseragent;

  dbs_printf(&msg, &msg_length, format, page, host, port, fortuseragent);

#ifdef DEBUG49
  fprintf(stdout,"SSL_write(), msg_length:%ld, msg=\"%s\"\n", msg_length,msg);
#endif 
  if((status = SSL_write(ssl, msg, strlen(msg))) < 0) {
    fprintf(stderr,"\n%s: SSL_write()", procname);
    fprintf(stderr,", status: %d", status);
    fprintf(stderr,", errno: %d", errno);
    fprintf(stderr,", SSL_get_error(): %d", SSL_get_error(ssl,status));
    perror("SSL_write");
    fflush(stderr);
  }
  fflush(stdout);
  
  HashCtx hashctx;
  unsigned char buffer[2048];

  HashInit(&hashctx);

  bytes = 0;
  characters = 0;

#ifdef DEBUG49
  fprintf(stdout, "SSL_read()\n");
#endif
  int bytesneeded = 0, bytesheader = 0, bytescontent = 0;
  
  do {
    cnt = SSL_read(ssl, buffer, sizeof(buffer));
    if(cnt < 0) {
      int err, err2;
      err = SSL_get_error(ssl, cnt);
      err2 = ERR_get_error();

      fprintf(stderr, "\n%s: cannot SSL_read()", procname);
      fprintf(stderr, ", retval: %d", cnt);
      fprintf(stderr, ", errno: %d", errno);
      fprintf(stderr,", SSL_get_error() %d", err);
      fprintf(stderr,", ERR_get_error() %d\n", err2);
      perror("SSL_read");
      fflush(stderr);
      break;
    }

#ifdef DEBUG50
    write(1, buffer, cnt);
#endif
    HashUpdate(&hashctx, buffer, cnt);
    bytes += cnt;

    for(c = 0; c < cnt; c++) {
      if(buffer[c] < 0x80 || buffer[c] > 0xbf)
	characters++;
    }
    if(bytesneeded == 0) {
      char *p;
      p = buffer;
      int count = 0;
      while(*p != '\0') {
	if(!strncmp(p, "\r\nContent-Length: ", 18)) {
	  bytescontent = atoi(p + 18);
	}
	if(!strncmp(p, "\r\n\r\n", 4)) {
	  bytesheader = count;
	}
	p++;
	count++;
      }
      bytesneeded = bytescontent + bytesheader + 4;
    }
  } while(SSL_pending(ssl) || bytes < bytesneeded);

  if(verbose) {
    fprintf(stdout,"\nbytescontent:%d", bytescontent);
    fprintf(stdout,", bytesheader:%d", bytesheader);
    fprintf(stdout,", bytesneeded:%d", bytesneeded);
    fflush(stdout);
  }
#ifdef OLD1

#ifdef DEBUG49
  fprintf(stdout,"SSL_read()\n");
#endif 
  while((cnt = SSL_read(ssl, buffer, sizeof(buffer))) > 0) {
#ifdef DEBUG49
    fprintf(stdout,"SSL_read() round\n");
#endif 
    
#ifdef DEBUG50
    write(1, buffer, cnt);
    fflush(stdout);
#endif
    HashUpdate(&hashctx, buffer, cnt);
    bytes += cnt;
    for(c = 0; c < cnt; c++) {
      if(buffer[c] < 0x80 || buffer[c] > 0xbf)
	characters++;
    }
  }
  fflush(stdout);
  if(cnt < 0) {
    fprintf(stderr, "\n%s: SSL_read()", procname);
    fprintf(stderr, ", status: %d", status);
    fprintf(stderr, ", errno: %d", errno);
    fprintf(stderr,", SSL_get_error(): %d", SSL_get_error(ssl,status));
    perror("SSL_read");
    fflush(stderr);
  }
  
#endif
  
  fflush(stdout);

  hash_update_cvar(&hashctx);
  HashFinal(hash, &hashctx);

  if(verbose) {
    fprintf(stdout,"\n, %d bytes read", bytes);
    fprintf(stdout,", %d characters read", characters);
    fprintf(stdout,", sha256: ");
    for(int c = 0;c < HashLen; c++) {
      fprintf(stdout,"%02x", hash[c]);
    }
    fprintf(stdout,"\n");
    newressu_output = 0; 
 }
#ifdef NOTUSED
  
#ifdef DEBUG49
  fprintf(stdout,"SSL_shutdown()\n");
#endif 
  SSL_shutdown(ssl);
#endif
  
#ifdef DEBUG49
  fprintf(stdout,"SSL_free()\n");
#endif 
  SSL_free(ssl);
#ifdef DEBUG49
  fprintf(stdout,"SSL_CTX_free()\n");
#endif 
  SSL_CTX_free(ctx);
  close(s);
}