SHARE
TWEET

Untitled

a guest Jun 13th, 2019 91 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // Copyright (c) 2009-2018, Andreas Wettstein
  2. // All rights reserved.
  3. //
  4. // Redistribution and use in source and binary forms, with or without
  5. // modification, are permitted provided that the following conditions are met:
  6. //
  7. //     - Redistributions of source code must retain the above copyright notice,
  8. //       this list of conditions and the following disclaimer.
  9. //     - Redistributions in binary form must reproduce the above copyright
  10. //       notice, this list of conditions and the following disclaimer in the
  11. //       documentation and/or other materials provided with the distribution.
  12. //
  13. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  14. // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  15. // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  16. // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
  17. // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  18. // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  19. // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  20. // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  21. // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  22. // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  23. // POSSIBILITY OF SUCH DAMAGE.
  24.  
  25. constexpr char opt_version[] = "1.267";
  26.  
  27. //--------------- src/konstanten.hh ---------------
  28. #ifndef BELEGUNGSOPTIMIERER_KONSTANTEN_H
  29. #define BELEGUNGSOPTIMIERER_KONSTANTEN_H
  30.  
  31. #include <string>
  32.  
  33. // Umlaute für die Verwendung in der Ausgabe; diese kann entweder in 8-Bit
  34. // Codierung (Latin-1) oder UTF-8-Codierung erfolgen.
  35.  
  36. #ifdef AUSGABE_8BIT
  37. #define strAe "\xE4"
  38. #define strAE "\xC4"
  39. #define strOe "\xF6"
  40. #define strUe "\xFC"
  41. #define strNBS "\xA0"
  42. #else
  43. #define strAe "\u00e4"
  44. #define strAE "\u00c4"
  45. #define strOe "\u00f6"
  46. #define strUe "\u00fc"
  47. #define strNBS "\u00A0"
  48. #endif // !AUSGABE_8BIT
  49.  
  50. // Ein primitives Schema, um beim Compiliern deutsche oder englische Ausgaben
  51. // zu wählen.
  52.  
  53. #ifdef ENGLISH
  54. #define SPRACHE(x,y) y
  55. #else
  56. #define SPRACHE(x,y) x
  57. #endif
  58.  
  59. #ifndef TASTENZAHL
  60. #define TASTENZAHL 35
  61. #endif // !TASTENZAHL
  62.  
  63. constexpr int nshift = 2;                 // Anzahl Shifttasten
  64. constexpr int ntaste = TASTENZAHL-nshift; // Anzahl Symboltasten
  65. constexpr int nebene = 2;                 // Anzahl Ebenen
  66. constexpr int nmaxkorpus = 10;
  67.  
  68. #ifdef OHNE2SHIFT
  69. constexpr int nebene2 = 1;
  70. #else
  71. constexpr int nebene2 = nebene;
  72. #endif
  73.  
  74. // Konstanten, um Zeilennummern zu bezeichnen.
  75. struct zeilen_t {
  76.    enum {
  77.       Zahlenreihe, Obere_Zeile, Mittelzeile, Untere_Zeile, Leerzeichenzeile,
  78.       nzeile
  79.    };
  80. };
  81.  
  82. // nzeile ist die Höhe und nspalte ist die Breite eines vollen Tastenfelds.
  83. constexpr int nzeile = zeilen_t::nzeile;
  84. constexpr int nspalte = 16;
  85.  
  86. // Anzahl der Finger (einschliesslich der Daumen)
  87. constexpr int nfinger = 10;
  88.  
  89. class finger_t {
  90. public:
  91.    // EinerDerDaumen ist ein Daumen, aber es ist nicht festgelegt, welcher.
  92.    enum {
  93.       KleinfingerLinks = -5,  RingfingerLinks = -4,
  94.       MittelfingerLinks = -3, ZeigefingerLinks = -2,
  95.       DaumenLinks = -1,
  96.       EinerDerDaumen = 0,
  97.       DaumenRechts = 1,
  98.       ZeigefingerRechts = 2,  MittelfingerRechts = 3,
  99.       RingfingerRechts = 4,   KleinfingerRechts = 5
  100.    } _;
  101.  
  102.    operator int() const { return _; }
  103.    explicit finger_t(int w) : _(static_cast<decltype(_)>(w)){}
  104.    finger_t(decltype(_) w) : _(w){}
  105.    finger_t() : _(EinerDerDaumen){}
  106. };
  107.  
  108. // Konstanten, um Bigrammkategorien zu bezeichnen.
  109. struct kategorie_t {
  110.    enum {
  111.       Handwechsel, Kollision, Doppeltanschlag,
  112.       Auswaerts, Einwaerts, MitUndefDaumen, nKategorie
  113.    } _;
  114.  
  115.    operator int() const { return _; }
  116.    explicit kategorie_t(int w) : _(static_cast<decltype(_)>(w)){}
  117.    kategorie_t(decltype(_) w) : _(w){}
  118.    kategorie_t() : _(nKategorie){}
  119. };
  120.  
  121. #endif // !BELEGUNGSOPTIMIERER_KONSTANTEN_H
  122.  
  123. //--------------- src/utfhilfe.hh ---------------
  124. #ifndef BELEGUNGSOPTIMIERER_UTFHILFE_H
  125. #define BELEGUNGSOPTIMIERER_UTFHILFE_H
  126.  
  127. #include <cstdint>
  128. #include <string>
  129.  
  130. std::string utf32_in_utf8(char32_t c);
  131. std::string utf32_in_utf8(const std::u32string& s);
  132. std::string utf32_in_utf8(uint64_t u);
  133.  
  134. char32_t utf8_in_utf32(const char*& s, bool& fehler);
  135.  
  136. std::u32string zahl_in_utf32(int z);
  137.  
  138. std::string utf32_in_ausgabe(char32_t);
  139. std::string utf32_in_ausgabe(const std::u32string&);
  140.  
  141. bool ist_ziffer(char32_t c);
  142. bool ist_zwischenraum(char32_t c);
  143.  
  144. constexpr uint64_t utf_maske = 0x1fffff;
  145.  
  146. #endif //!BELEGUNGSOPTIMIERER_UTFHILFE_H
  147.  
  148. //--------------- src/Unicode.hh ---------------
  149. #ifndef BELEGUNGSOPTIMIERER_UNICODE_H
  150. #define BELEGUNGSOPTIMIERER_UNICODE_H
  151.  
  152. #include <unordered_map>
  153. #include <unordered_set>
  154.  
  155. class Unicode final {
  156.    std::unordered_set<char32_t> buchstaben;
  157.    std::unordered_map<char32_t, char32_t> gross_in_klein;
  158.    Unicode(const Unicode& ) = delete;
  159.    Unicode();
  160. public:
  161.    static const Unicode& get(){ static const Unicode x; return x; }
  162.  
  163.    char32_t kleinbuchstabe(char32_t c) const;
  164.    bool ist_buchstabe(char32_t c) const;
  165. };
  166.  
  167. #endif // !BELEGUNGSOPTIMIERER_UNICODE_H
  168.  
  169. //--------------- src/Eingabestream.hh ---------------
  170. #ifndef BELEGUNGSOPTIMIERER_EINGABESTREAM_H
  171. #define BELEGUNGSOPTIMIERER_EINGABESTREAM_H
  172.  
  173. #include <fstream>
  174. #include <string>
  175.  
  176. class Eingabestream final {
  177.    std::string zeile, name;
  178.    std::u32string buffer;
  179.    std::ifstream f;
  180.    size_t l, p, maxl, izeile;
  181.    int errpos;
  182.    bool utf8ein, geaendert;
  183.  
  184.    bool fuellen();
  185.  
  186.    // Übergehen alle Leerzeichen und Tabulatoren, beginnend von der aktuellen
  187.    // Position, höchstens bis zum Ende der Zeile.
  188.    void zwischenraum_uebergehen();
  189. public:
  190.    // f: Geöffneter Stream; utf8: true wenn der Stream UTF-8 enthalten sollte,
  191.    // false, wenn er CP-1252 enthalten sollte.
  192.    Eingabestream(const std::string& nam, bool utf8,
  193.                  bool muss_existieren = true);
  194.  
  195.    ~Eingabestream();
  196.  
  197.    // Lies nächstes Zeichen (einschliesslich Zeilenenden '\n'); falls Eingabe
  198.    // erschöpft ist gibt Null zurück.
  199.    char32_t lieszeichen();
  200.  
  201.    // Falls aus der aktuellen Zeile schon Zeichen gelesen wurden, fange eine
  202.    // neue an.  Gib false zurück, wenn die Eingabe erschöpft ist.
  203.    bool neuezeile();
  204.  
  205.    // Wie neuezeile, übergeht aber Kommentar- und Leerzeilen.
  206.    bool echte_neuezeile();
  207.  
  208.    // Anzahl der Zeichen, die in der aktuellen Zeile noch verbleiben.
  209.    size_t restzeichen() const;
  210.  
  211.    // Ist aktuelles Zeichen in aktueller Zeile ein Leerzeichen oder Tabulator?
  212.    bool ist_zwischenraum() const;
  213.  
  214.    // Ist aktuelles Zeichen in aktueller Zeile eine Ziffer
  215.    bool ist_ziffer() const;
  216.  
  217.    // Ist aktuelles Zeichen in aktueller Zeile gleich dem übergebenen?
  218.    bool ist(char32_t c) const;
  219.  
  220.    // übergeht das aktuelle Zeichen, fängt aber nie eine neue Zeile an.
  221.    void uebergehen();
  222.  
  223.    // Liest nächstes Zeichen in aktueller Zeile; gibt 0 am Zeilenende;
  224.    char32_t lies_in_zeile();
  225.  
  226.    // Prüft, ob in Rest der Zeile höchstens noch Zwischenraum vorkommt und
  227.    // übergeht diesen.
  228.    bool zeilenende();
  229.  
  230.    // Übergeht führenden Zwischenraum und erwartet dahinter eine Zahl, die bis
  231.    // zum Zeilenende oder einem Zwischenraum reicht.  Falls das nicht so ist,
  232.    // wird false zurückgegeben. Im Erfolgsfall wird true zurückgegeben, der
  233.    // gelese Wert steht in wert.
  234.    bool hole_zahl(double& wert);
  235.  
  236.    // Übergeht führenden Zwischenraum und gibt das dahinter stehende Wort
  237.    // zurück, das durch Zwischenraum oder das Zeilenende begrenzt ist.  Falls
  238.    // die Zeile kein Wort mehr enthält, gib false zurück.  Falls man auf ein
  239.    // Kommentarzeichen # stösst gilt die Zeile als ohne beendet, kein Wort wird
  240.    // gefunden.
  241.    bool hole_wort(std::u32string& wert);
  242.  
  243.    // Übergeht führenden Zwischenraum und gibt das dahinter stehende Flag (+
  244.    // für true, - für false) in wert zurück, das durch Zwischenraum oder das
  245.    // Zeilenende begrenzt ist.  Falls kein Flag gefunden wird, gib false
  246.    // als Returnwert.
  247.    bool hole_flag(bool& wert);
  248.  
  249.    // übergeht Zwischenraum, und erwartet dann noch mindestens zwei Zeichen in
  250.    // der Zeile.  Das erste davon ist der Stringbegrenzer; die folgenden
  251.    // Zeichen bis zum nächsten Stringbegrenzer gehören zum String.  Kommt kein
  252.    // weiterer Stringbegrenzer ist das ein Fehler.
  253.    bool hole_string(std::u32string& wert);
  254.  
  255.    bool encoding_geaendert() const;
  256.  
  257.    [[noreturn]] void fehler(size_t off = 0) const;
  258.  
  259.    size_t aktuelle_zeile() const;
  260.    const std::string& aktuelles_file() const;
  261. };
  262.  
  263. double hole_zahl(Eingabestream& f, double rmin, double rmax);
  264.  
  265. void pruefe_leer_dann_N(Eingabestream& f, size_t N);
  266.  
  267. #endif // !BELEGUNGSOPTIMIERER_EINGABESTREAM_H
  268.  
  269. //--------------- src/typen.hh ---------------
  270. #ifndef BELEGUNGSOPTIMIERER_TYPEN_H
  271. #define BELEGUNGSOPTIMIERER_TYPEN_H
  272.  
  273. //#include "konstanten.hh"
  274. #include <memory>
  275. #include <string>
  276.  
  277. // Koordinate
  278. struct koordinate_t { double x, y; };
  279.  
  280. // Beschreibung einer einzelnen Taste.
  281. struct taste_t final {
  282.    std::u32string name;
  283.    double x, y, aufwand;
  284.    int spalte, zeile;
  285.    finger_t finger;
  286.    bool istGrundposition, seite;
  287. };
  288.  
  289. // Beschreibung eines n-Gramms.
  290. struct n_gramm_t final {
  291.    n_gramm_t(int T1, int T2, int T3, const std::u32string& N)
  292.       : t1(T1), t2(T2), t3(T3), name(N){}
  293.    int t1, t2, t3;
  294.    std::u32string name;
  295. };
  296.  
  297.  
  298. // zaehl_t ist der Datentyp, der zum Zählen von n-Grammen im Korpus verwendet
  299. // wird.  Double vermeidet Überläufe bei der Berechnung von Korrelationen und
  300. // ist daher der sinnvollste Typ.
  301. using zaehl_t = double;
  302. using zaehl_ta = std::unique_ptr<zaehl_t[]>;
  303.  
  304. // akkumuations_t wird zur Berechnung der Aufwände verwendet.
  305. using akkumuations_t = float;
  306.  
  307. // aufwand_t wird zur Tabellierung der Aufwände verwendet.  Um die Tabellen
  308. // klein zu halten, verwende ich hier nur float; ist allemal genau genug.
  309. using aufwand_t = float;
  310.  
  311. // haeufigkeit_t wird zur Tabellierung der Häufigkeiten verwendet, die in der
  312. // Optimierung verwendet werden.  Auch hier ein float, um die Tabellen klein zu
  313. // halten; die sieben Dezimale sollten mehr als ausreichend genau sein.
  314. using haeufigkeit_t = float;
  315.  
  316. // belegung_t ist eine Zeichenpermutation und repräsentiert Belegungen.
  317. using belegung_t = unsigned char[ntaste];
  318.  
  319. // Fingerbelastung durch die verschiedenen Korpora.
  320. using fingerbelastung_t = akkumuations_t[nmaxkorpus][nfinger+1];
  321.  
  322. #endif // !BELEGUNGSOPTIMIERER_TYPEN_H
  323.  
  324. //--------------- src/Naechstes_zeichen.hh ---------------
  325. #ifndef BELEGUNGSOPTIMIERER_NAECHSTES_ZEICHEN_H
  326. #define BELEGUNGSOPTIMIERER_NAECHSTES_ZEICHEN_H
  327.  
  328. //#include "Eingabestream.hh"
  329. #include <string>
  330.  
  331. class Naechstes_zeichen {
  332.    Eingabestream text;
  333. public:
  334.    Naechstes_zeichen(const std::string& name, bool utf8) : text(name, utf8){}
  335.    virtual ~Naechstes_zeichen(){}
  336.    virtual char32_t get(){ return text.lieszeichen(); }
  337.    bool encoding_geaendert() const { return text.encoding_geaendert(); }
  338. };
  339. #endif // !BELEGUNGSOPTIMIERER_NAECHSTES_ZEICHEN_H
  340.  
  341. //--------------- src/Haeufigkeit.hh ---------------
  342. #ifndef BELEGUNGSOPTIMIERER_HAEUFIGKEIT_H
  343. #define BELEGUNGSOPTIMIERER_HAEUFIGKEIT_H
  344.  
  345. //#include "typen.hh"
  346. #include <cassert>
  347. #include <unordered_map>
  348. #include <vector>
  349.  
  350. class Tastatur;
  351. class Kodierung;
  352.  
  353. class Haeufigkeit final {
  354. private:
  355.    haeufigkeit_t h3[ntaste][ntaste][ntaste][nebene];
  356.    haeufigkeit_t h2[ntaste][ntaste][nebene][nebene2];
  357.    haeufigkeit_t h1[ntaste][nebene], h1e[nmaxkorpus][ntaste][nebene];
  358.    haeufigkeit_t gewichte[nmaxkorpus];
  359.    haeufigkeit_t hunbekannt;
  360.    const int nkorpus;
  361.    bool trigramme;
  362.    const Tastatur& tastatur;
  363.    const Kodierung& kodierung;
  364.  
  365.    haeufigkeit_t *h11, *h21, *h22;
  366.    haeufigkeit_t *h11e[nmaxkorpus], *h11ee[nmaxkorpus], *h21e[nmaxkorpus];
  367.  
  368.    void akkumuliere(int korpus, double gewicht,
  369.                     const zaehl_t* uh1, const zaehl_t* uh2, const zaehl_t* uh3,
  370.                     const zaehl_t* uh11, const zaehl_t* uh12,
  371.                     const zaehl_t* uh22, const zaehl_t* uh1l,
  372.                     const zaehl_t* uh1s, const zaehl_t* uh2l,
  373.                     const zaehl_t* uh2s, zaehl_t ll, zaehl_t ls, zaehl_t ss,
  374.                     const std::unordered_map<char32_t, zaehl_t>& unbekannt,
  375.                     std::unordered_map<char32_t, zaehl_t>& summe_unbekannt);
  376.    haeufigkeit_t& ein(int p, int e)
  377.    { assert(p < ntaste && e < nebene); return h1[p][e]; }
  378.    haeufigkeit_t& ein_k(int korpus, int p, int e) {
  379.       assert(korpus < nkorpus && p < ntaste && e < nebene);
  380.       return h1e[korpus][p][e];
  381.    }
  382.    haeufigkeit_t& bi(int p1, int e1, int p2, int e2){
  383.       assert(p1 < ntaste && e1 < nebene && p2 < ntaste && e2 < nebene2);
  384.       return h2[p1][p2][e1][e2];
  385.    }
  386.    haeufigkeit_t& tri(int p1, int p2, int p3, int e3){
  387.       assert(p1 < ntaste && p2 < ntaste && p3 < ntaste && e3 < nebene);
  388.       return h3[p1][p2][p3][e3];
  389.    }
  390.  
  391.    haeufigkeit_t& ein_ein(int oi, int oj){
  392.       assert(h11 && oi < groesse1 && oj < groesse1);
  393.       return h11[sym_index(oi, oj)];
  394.    }
  395.    haeufigkeit_t& ein_k_ein_k(int korpus, int oi, int oj){
  396.       assert(korpus<nkorpus && h11ee[korpus] && oi < groesse1 && oj < groesse1);
  397.       return h11ee[korpus][sym_index(oi, oj)];
  398.    }
  399.    haeufigkeit_t& ein_ein_k(int korpus, int oi, int oj){
  400.       assert(korpus<nkorpus && h11e[korpus] && oi < groesse1 && oj < groesse1);
  401.       return h11e[korpus][index_bi_flach(oi, oj)];
  402.    }
  403.    haeufigkeit_t& ein_bi(int ok, int oij){
  404.       assert(h21 && ok < groesse1 && oij < groesse2);
  405.       return h21[index_tri21_flach(oij, ok)];
  406.    }
  407.    haeufigkeit_t& ein_k_bi(int korpus, int ok, int oij){
  408.       assert(korpus<nkorpus && h21e[korpus] && ok < groesse1 && oij < groesse2);
  409.       return h21e[korpus][index_tri21_flach(oij, ok)];
  410.    }
  411.    haeufigkeit_t& bi_bi(int oij, int okl){
  412.       assert(h22 && oij < groesse2 && okl < groesse2);
  413.       return h22[sym_index(oij, okl)];
  414.    }
  415.  
  416.    void varianzen_anlegen();
  417.  
  418. public:
  419.    Haeufigkeit(const Tastatur& tastatur, const Kodierung& kodierung,
  420.                const std::vector<std::string>& basis,
  421.                const std::vector<double>& gewicht,
  422.                const std::vector<bool>& t,
  423.                bool varianzen,
  424.                bool berichte_unbekanne_zeichen);
  425.    Haeufigkeit(const Haeufigkeit&) = delete;
  426.    // das int-Argument dient lediglich der Unterscheidung vom Copy-Konstruktor
  427.    Haeufigkeit(const Haeufigkeit&, int);
  428.  
  429.    void setze(const Haeufigkeit& h, const belegung_t p);
  430.    void swap(int p1, int p2);
  431.  
  432.    ~Haeufigkeit();
  433.  
  434.    haeufigkeit_t operator()(int p, int e) const
  435.    { assert(p < ntaste && e < nebene); return h1[p][e]; }
  436.    haeufigkeit_t operator()(int p1, int e1, int p2, int e2) const {
  437.       assert(p1 < ntaste && e1 < nebene && p2 < ntaste && e2 < nebene2);
  438.       return h2[p1][p2][e1][e2];
  439.    }
  440.    haeufigkeit_t operator()(int korpus, int p, int e) const {
  441.       assert(korpus < nkorpus && p < ntaste && e < nebene);
  442.       return h1e[korpus][p][e];
  443.    }
  444.    haeufigkeit_t tri(int p1, int p2, int p3, int e3) const {
  445.       assert(trigramme && p1 < ntaste && p2 < ntaste && p3 < ntaste &&
  446.              e3 < nebene);
  447.       return h3[p1][p2][p3][e3];
  448.    }
  449.    int num_korpus() const { return nkorpus; }
  450.    haeufigkeit_t gewicht(int korpus) const
  451.    { assert(korpus < nkorpus); return gewichte[korpus]; }
  452.    bool mit_trigrammen() const { return trigramme; }
  453.    bool mit_varianzen() const { return h11 != nullptr; }
  454.  
  455.    haeufigkeit_t ein_ein(int oi, int oj) const {
  456.       assert(h11 && oi < groesse1 && oj < groesse1);
  457.       return h11[sym_index(oi, oj)];
  458.    }
  459.    haeufigkeit_t ein_k_ein_k(int korpus, int oi, int oj) const {
  460.       assert(korpus<nkorpus && h11ee[korpus] && oi < groesse1 && oj < groesse1);
  461.       return h11ee[korpus][sym_index(oi, oj)];
  462.    }
  463.    haeufigkeit_t ein_ein_k(int korpus, int oi, int oj) const {
  464.       assert(korpus<nkorpus && h11e[korpus] && oi < groesse1 && oj < groesse1);
  465.       return h11e[korpus][index_bi_flach(oi, oj)];
  466.    }
  467.    haeufigkeit_t ein_bi(int ok, int oij) const {
  468.       assert(h21 && ok < groesse1 && oij < groesse2);
  469.       return h21[index_tri21_flach(oij, ok)];
  470.    }
  471.    haeufigkeit_t ein_k_bi(int korpus, int ok, int oij) const {
  472.       assert(korpus<nkorpus && h21e[korpus] && ok < groesse1 && oij < groesse2);
  473.       return h21e[korpus][index_tri21_flach(oij, ok)];
  474.    }
  475.    haeufigkeit_t bi_bi(int oi, int oj) const {
  476.       assert(h22 && oi < groesse2 && oj < groesse2);
  477.       return h22[sym_index(oi, oj)];
  478.    }
  479.    haeufigkeit_t unbekannt() const { return hunbekannt; }
  480.  
  481.    void statistik() const;
  482.  
  483.    // Ein paar Hilfsroutinen, um mehrdimensionale Felder flach umzunummerieren.
  484.    static int sym_index(int i1, int i2){
  485.       const int k = i1 < i2 ? i1 : i2;
  486.       const int g = i1 < i2 ? i2 : i1;
  487.       return (g*(g+1))/2+k;
  488.    }
  489.    static int index_ein_flach(int tastenindex, int ebenenindex)
  490.    { return tastenindex*nebene+ebenenindex; }
  491.    static int index_bi_flach(int idx_ein1, int idx_ein2)
  492.    { return groesse1*idx_ein1+idx_ein2; }
  493.    static int index_tri21_flach(int idx_bi, int idx_ein)
  494.    { return groesse1*idx_bi+idx_ein; }
  495.    static int index_tri_flach(int idx_ein1, int idx_ein2, int idx_ein3)
  496.    { return index_tri21_flach(index_bi_flach(idx_ein1, idx_ein2), idx_ein3); }
  497.  
  498.    static constexpr int groesse1 = ntaste*nebene;
  499.    static constexpr int groesse2 = groesse1*groesse1;
  500.    static constexpr int groesse3 = groesse1*groesse2;
  501.    static constexpr int groesse11 = (groesse1*(groesse1+1))/2;
  502.    static constexpr int groesse21 = groesse1*groesse2;
  503.    static constexpr int groesse22 = (groesse2*(groesse2+1))/2;
  504. };
  505.  
  506. #endif // !BELEGUNGSOPTIMIERER_HAEUFIGKEIT_H
  507.  
  508. //--------------- src/Kodierung.hh ---------------
  509. #ifndef BELEGUNGSOPTIMIERER_KODIERUNG_H
  510. #define BELEGUNGSOPTIMIERER_KODIERUNG_H
  511.  
  512. //#include "konstanten.hh"
  513. #include <string>
  514. #include <unordered_map>
  515. #include <utility>
  516. #include <vector>
  517.  
  518. class Kodierung final {
  519.    std::unordered_map<char32_t, std::pair<int, int>> invers;
  520.    std::unordered_map<char32_t, std::vector<std::pair<int, int>>> inversstr;
  521.    std::string strings[ntaste][nebene], glyphname[ntaste], platzhalterstr;
  522.    char32_t chars[ntaste][nebene];
  523.    unsigned char psenc[ntaste];
  524.  
  525. public:
  526.    Kodierung(const std::vector<std::u32string>& klartext,
  527.              const std::vector<std::u32string>& ersatzstring,
  528.              const std::vector<std::u32string>& glyph,
  529.              char32_t platzhalter);
  530.    bool ist_platzhalter(int i, int e) const;
  531.    const std::string& txt(int i, int e) const;
  532.    char32_t uchar(int i, int e) const;
  533.    const std::string& bevorzugt(int i) const;
  534.    const std::string& txt(int ie) const;
  535.    std::pair<int, int> position(char32_t z) const;
  536.    const std::vector<std::pair<int,int>>* ersatz(char32_t z) const;
  537.    int psencoding(int i) const;
  538.    std::string psencstr(int i) const;
  539.    const std::string& psglyphname(int i) const;
  540. };
  541.  
  542. #endif // !BELEGUNGSOPTIMIERER_KODIERUNG_H
  543.  
  544. //--------------- src/Tastatur.hh ---------------
  545. #ifndef BELEGUNGSOPTIMIERER_TASTATUR_H
  546. #define BELEGUNGSOPTIMIERER_TASTATUR_H
  547.  
  548. //#include "konstanten.hh"
  549. //#include "typen.hh"
  550. #include <map>
  551. #include <string>
  552. #include <unordered_map>
  553. #include <unordered_set>
  554. #include <vector>
  555.  
  556. class Tastatur final {
  557. public:
  558.    // Tests, die feststellen, ob eine Fingernummer ein bestimmter Finger
  559.    // (unabhängig von der Hand) ist.
  560.    static bool istDaumen(finger_t i);
  561.    static bool istKleinfinger(finger_t i);
  562.  
  563.    // Kurze Beschreibung der Kategorie.
  564.    static const char* kategorie_str(int kategorie);
  565.  
  566.    // Längere Beschreibung des Bigramms aus Taste i und j
  567.    std::string kategorie_lang(int i, int j) const;
  568.  
  569.    // Spaltennummer der Taste i.
  570.    int spalte(int i) const;
  571.    // Zeilennummer der Taste i.
  572.    int zeile(int i) const;
  573.  
  574.    // Finger, der Taste i betätigt; der Rückgabewert ist eine der oben
  575.    // definierten Konstanten in finger_t.
  576.    finger_t finger(int i) const;
  577.  
  578.    // Fingerindex zu Taste i; das ist ein Wert zwischen 0 und nfinger
  579.    // (einschliesslich).
  580.    int finger_index(int i) const;
  581.  
  582.    // Nummer der Taste, die die Shifttaste ist, die zusammen mit der
  583.    // Symboltaste i benutzt wird, das heisst, die Nummer der Shifttaste, die in
  584.    // der anderen Tastaturhälfte wie Taste i liegt.
  585.    int shifttaste(int i) const;
  586.  
  587.    // Fingerindex für den Finger, der die Shifttaste bedient, die zusammen mit
  588.    // der Symboltaste i benutzt wird.
  589.    int shift_finger_index(int i) const;
  590.  
  591.    // Taste in der der Finger, der Taste i bedient, seine Grundstellung hat.
  592.    int grundposition(int i) const;
  593.  
  594.    // Die Nummer der Taste in Zeile z und Spalte s.  Falls an dieser Position
  595.    // keine Taste liegt, wird -1 zurückgegeben.
  596.    int taste(int z, int s) const;
  597.  
  598.    // Name der Taste i.
  599.    const std::u32string& name(int i) const;
  600.  
  601.    // Tastenname -> Tastennummer, -1 falls es den Namen nicht gibt.
  602.    int taste(const std::u32string& n) const;
  603.  
  604.    // Kategorie, zu der das Tastenbigramm aus Taste i gefolgt von Taste j
  605.    // gehört; die Kategorie wird als eine der oben definierten Konstanten
  606.    // zurückgegeben.
  607.    kategorie_t kategorie(int i, int j) const;
  608.  
  609.    // Benutzerdefinierte Kategorien, zu dem das Bi- oder Trigramm gemäss der
  610.    // Tasten in i gehört.
  611.    const std::vector<int>& benutzerkategorie(const std::vector<int>& i) const;
  612.  
  613.    // Nummer eine Benutzerkategorie in Namen umwandeln.
  614.    const std::string& benutzerkategorie_name(int i) const;
  615.  
  616.    // Testet, ob das Tastenbigramm aus Taste i gefolgt von Taste j eine
  617.    // Handwiederholung ist (dafür gibt es keine eigene Kategorie).
  618.    bool istHandwiederholung(int i, int j) const;
  619.  
  620.    // Prüft, ob das Tastentrigramm aus Tasten i, j und k ein Wippbewegung ist.
  621.    bool istWippe(int i, int j, int k) const;
  622.  
  623.    // Koordinate der Taste i; die Längeneinheit des Koordinatensystems ist die
  624.    // Breite einer normalen Taste.  Die Koordinate bezeichnet eine geometrische
  625.    // Position.
  626.    koordinate_t tastenkoord(int i) const;
  627.  
  628.    // Distanz der Tasten i und j.
  629.    double distanz(int i, int j) const;
  630.  
  631.    // Teste, ob Finger mit Index i nur Tasten bedient, deren Belegung fest
  632.    // vorgegeben ist.
  633.    bool finger_fix(int i) const;
  634.  
  635.    // Anzahl der Tasten, die für Zeichen gedacht sind und deren Belegung nicht
  636.    // fest ist.
  637.    int nvariabel() const;
  638.  
  639.    // Lageaufwand der Taste i.
  640.    double lageaufwand(int i) const;
  641.  
  642.    Tastatur(const std::vector<taste_t>& tasten,
  643.             const std::unordered_set<std::u32string>& fixe_tasten);
  644.  
  645.    void neue_kategorien(const std::vector<n_gramm_t>& benutzerkategorie);
  646.  
  647. private:
  648.    std::u32string namen[ntaste+nshift];
  649.    int spalten[ntaste+nshift], zeilen[ntaste+nshift], nvar;
  650.    finger_t fingertab[ntaste+nshift];
  651.    int fingerind[ntaste+nshift];
  652.    int shifttast[ntaste+nshift], sfingerind[ntaste+nshift];
  653.    int grundpos[ntaste+nshift], tastennr[nzeile][nspalte];
  654.    kategorie_t tastenkategorie[ntaste+nshift][ntaste+nshift];
  655.    double x[ntaste+nshift], y[ntaste+nshift], aufwand[ntaste+nshift];
  656.    bool fix[nfinger+1];
  657.    std::unordered_map<std::u32string, int> namennr;
  658.    std::vector<std::string> kategorie_liste;
  659.    std::map<std::vector<int>, std::vector<int>> benutzerkat;
  660. };
  661.  
  662. #endif // !BELEGUNGSOPTIMIERER_TASTATUR_H
  663.  
  664. //--------------- src/Grafik.hh ---------------
  665. #ifndef BELEGUNGSOPTIMIERER_GRAFIK_H
  666. #define BELEGUNGSOPTIMIERER_GRAFIK_H
  667.  
  668. //#include "typen.hh"
  669. #include <fstream>
  670. #include <string>
  671.  
  672. class Haeufigkeit;
  673. class Kodierung;
  674. class Konfiguration;
  675. class Tastatur;
  676.  
  677. class Grafik final {
  678.    std::ofstream grafik;
  679.    int seite;
  680.    bool ungerade;
  681.    const Tastatur& tastatur;
  682.    const Kodierung& kodierung;
  683. public:
  684.    Grafik(const std::string& name, const Tastatur& tastatur,
  685.           const Kodierung& kodierung, const Haeufigkeit& h,
  686.           const Konfiguration& konfiguration);
  687.    ~Grafik();
  688.    void ausgabe(const belegung_t b);
  689. };
  690.  
  691. #endif // !BELEGUNGSOPTIMIERER_GRAFIK_H
  692.  
  693. //--------------- src/Statistik.hh ---------------
  694. #ifndef BELEGUNGSOPTIMIERER_STATISTIK_H
  695. #define BELEGUNGSOPTIMIERER_STATISTIK_H
  696.  
  697. //#include "konstanten.hh"
  698. //#include "typen.hh"
  699. #include <map>
  700. #include <string>
  701.  
  702. class Aufwandstabelle;
  703. class Haeufigkeit;
  704. class Kodierung;
  705. class Tastatur;
  706.  
  707. struct Statistik {
  708.    akkumuations_t aeinzel = 0;
  709.    haeufigkeit_t hpos[2][nzeile] = { { 0, 0, 0, 0, 0 },
  710.                                                { 0, 0, 0, 0, 0 } };
  711.    haeufigkeit_t hk[kategorie_t::nKategorie] = { 0 };
  712.    haeufigkeit_t hs[kategorie_t::nKategorie] = { 0 };
  713.    haeufigkeit_t hi0[kategorie_t::nKategorie] = { 0 };
  714.    haeufigkeit_t hi2[kategorie_t::nKategorie] = { 0 };
  715.    haeufigkeit_t hfinger[nfinger+1] = { 0 };
  716.    haeufigkeit_t hkollision1[nfinger] = { 0 }, hkollision2[nfinger] = { 0 };
  717.    haeufigkeit_t hskollision1[nshift] = { 0 }, hskollision2[nshift] = { 0 };
  718.    haeufigkeit_t hnachbar1[nfinger] = { 0 }, hnachbar2[nfinger] = { 0 };
  719.    haeufigkeit_t hsnachbar1[nshift] = { 0 }, hsnachbar2[nshift] = { 0 };
  720.    haeufigkeit_t h1tot = 0, h2tot = 0, hs2tot = 0, h3tot = 0;
  721.    haeufigkeit_t hlinks = 0,  hrechts = 0, hslinks = 0, hsrechts = 0;
  722.    haeufigkeit_t hnachbar = 0, hsnachbar = 0;
  723.    haeufigkeit_t hwippe = 0, hdoppelhw = 0, hkeinhw = 0;
  724.    haeufigkeit_t hrel[3][2] = { {0,0}, {0,0}, {0,0} };
  725.    bool mitfinger[nfinger+1] = { false };
  726.  
  727.    std::map<int, haeufigkeit_t> hs_benutzer, hk_benutzer, ht_benutzer;
  728.    std::multimap<haeufigkeit_t, std::string> ngramm[3][2];
  729.  
  730.    Statistik(const belegung_t b, const Tastatur& tastatur,
  731.              const Kodierung& kodierung, const Haeufigkeit& h,
  732.              const Aufwandstabelle& a, const double ngrammakkumlimit[3]);
  733. };
  734.  
  735. #endif // !BELEGUNGSOPTIMIERER_STATISTIK_H
  736.  
  737. //--------------- src/Konfiguration.hh ---------------
  738. #ifndef BELEGUNGSOPTIMIERER_KONFIGURATION_H
  739. #define BELEGUNGSOPTIMIERER_KONFIGURATION_H
  740.  
  741. //#include "konstanten.hh"
  742. #include <memory>
  743. #include <string>
  744. #include <vector>
  745.  
  746. class Tastatur;
  747. class Kodierung;
  748.  
  749. class Konfiguration final {
  750.    // Unnormierte Zielhäufigkeit für Finger von links nach rechts
  751.    double fh_unnorm[nfinger];
  752.    // Die Häufigkeiten der Tastendrücke werden auf 1 normiert, positive
  753.    // Abweichungen von den normierten Zielhäufigkeiten quadriert, mit folgenden
  754.    // Gewichten multipliziert und zur Gesamtaufwand addiert:
  755.    double mf_input[nfinger];
  756.    // Multiplikator für Shift-Bigramme.  Mit diesem Faktor werden die normalen
  757.    // Bigrammaufwände für die Bigramme aus erster und letzter Taste (Shift und
  758.    // zweite Symboltaste) in diesen Tastentrigrammen multipliziert.  Erster
  759.    // Wert für normale Bigramme mit positivem Gewicht, zweiter mit negativem
  760.    // Gewicht.
  761.    double mult_shiftindirekt[2];
  762.    // Multiplikator Trigramme, die aufgrund von Bigrammen aus erster und
  763.    // letzter Taste des Trigramms bewertet werden.  Erster Wert für normale
  764.    // Bigramme mit positivem Gewicht, zweiter mit negativem Gewicht.
  765.    double mult_indirekt[2];
  766.    // Der Bequemlichkeit halber haben wir für verschiedene Bigrammkategorien
  767.    // Vorfaktoren für den Aufwand eingeführt.
  768.    double mult_handwiederholung, mult_auswaerts, mult_handwechsel;
  769.    double mult_doppelkomp, mult_zeilenkomp[5];
  770.    // Für Bigramme ohne Hand-, aber mit Zeilenwechsel
  771.    double mult_schraegZS[2];
  772.    double mult_schraegYX[2];
  773.    double add_schraegDX[2];
  774.    // Fixer Anteil des Aufwand für jede Kollision, Daumen...Kleinfinger
  775.    double mult_kollision_konstant[5];
  776.    // Variabler Anteil, wird mit Tastenabstand multipliziert,
  777.    // Daumen...Kleinfinger
  778.    double mult_kollision_distanz[5];
  779.    // Nachbarstrafe Daumen/Zeigefinger, Zeige/Mittelfinger, Mittel/Ringfinger,
  780.    // Ringfinger/Kleinfinger
  781.    double nachbarstrafe[4];
  782.    // Mit diesen Parametern kann für Trigramme mit zwei Handwechseln
  783.    // bzw. solche ganz ohne Handwechsel einen zusätzlichen Aufwand anrechnen.
  784.    double mult_doppelwechsel, mult_doppelwiederholung, mult_wippe;
  785.    // Beliebige Bi- und Trigramme
  786.    double bigramm_roh[ntaste+nshift][ntaste+nshift];
  787.    double trigramm_roh[ntaste+nshift][ntaste+nshift][ntaste+nshift];
  788.    // Für Verwechslungspotenzial/Ähnlichkeit
  789.    double mult_kollision, mult_nachbar, mult_symmetrisch;
  790.    double mult_symmetrisch_gleichzeile, mult_hand_verschieden;
  791.    double verwechslungspotenzial_roh[ntaste+nshift][ntaste+nshift];
  792.    double aehnlichkeit_roh[ntaste][ntaste];
  793.    // Vorlieben
  794.    double vorliebe_roh[ntaste][ntaste];
  795.    double vorliebe_knick;
  796.    // Aufwand aufgrund nicht unterstützter Zeichen.
  797.    double aunbekannt;
  798.    // Die Schriftarten in Postscriptgrafiken.
  799.    std::string _zeichenfont, _beschreibungsfont;
  800. public:
  801.    Konfiguration(const std::vector<std::string>& namen,
  802.                  std::unique_ptr<const Tastatur>& tastatur,
  803.                  std::unique_ptr<const Kodierung>& kodierung);
  804.  
  805.    // Aufwand für Bigramm der Tasten i und j.
  806.    double bigrammaufwand(int i, int j, const Tastatur& tastatur) const;
  807.  
  808.    // Aufwand für Trigramm der Tasten i, j und k.
  809.    double trigrammaufwand(int i, int j, int k, const Tastatur& tastatur) const;
  810.  
  811.    // Das Risiko, Tasten i und j zu verwechseln, ausgedrückt als Aufwand.
  812.    double verwechslungspotenzial(int i, int j, const Tastatur& tastatur) const;
  813.  
  814.    double vorliebe(int i, int j) const;
  815.    double vorliebenknick() const;
  816.    double aehnlichkeit(int i, int j) const;
  817.    double shiftindirekt(double a) const;
  818.    double indirekt(double a) const;
  819.    double zielhaeufigkeit(int f) const;
  820.    double multfinger(int f) const;
  821.    double unbekannt() const;
  822.  
  823.    const std::string& zeichenfont() const ;
  824.    const std::string& beschreibungsfont() const;
  825. };
  826.  
  827. #endif // !BELEGUNGSOPTIMIERER_KONFIGURATION_H
  828.  
  829. //--------------- src/Aufwandstabelle.hh ---------------
  830. #ifndef BELEGUNGSOPTIMIERER_AUFWANDSTABELLE_H
  831. #define BELEGUNGSOPTIMIERER_AUFWANDSTABELLE_H
  832.  
  833. //#include "konstanten.hh"
  834. //#include "typen.hh"
  835. #include <cassert>
  836.  
  837. class Kodierung;
  838. class Konfiguration;
  839. class Tastatur;
  840.  
  841. class Aufwandstabelle final {
  842.    aufwand_t a3[ntaste][ntaste][ntaste][nebene];
  843.    aufwand_t va2[ntaste][ntaste], vl[ntaste][ntaste], vl_knick;
  844.    aufwand_t ae[ntaste][ntaste], a2[ntaste][ntaste][nebene][nebene2];
  845.    aufwand_t a1[ntaste][nebene], multfinger[nfinger], aunbekannt;
  846.    haeufigkeit_t finger_zielhaeufigkeit[nfinger];
  847.    const bool mit_trigrammen;
  848.    bool mit_vorlieben, mit_aehnlichkeit;
  849.  
  850.    const Tastatur& tastatur;
  851. public:
  852.    Aufwandstabelle(bool mit_trigrammen, const Tastatur&,
  853.                    const Konfiguration&);
  854.  
  855.    aufwand_t operator()(int p, int e) const
  856.    { assert(p < ntaste && e < nebene); return a1[p][e]; }
  857.    aufwand_t operator()(int p1, int e1, int p2, int e2) const {
  858.       assert(p1 < ntaste && e1 < nebene && p2 < ntaste && e2 < nebene2);
  859.       return a2[p1][p2][e1][e2];
  860.    }
  861.    aufwand_t tri(int p1, int p2, int p3, int e3) const {
  862.       assert(p1 < ntaste && p2 < ntaste && p3 < ntaste && e3 < nebene);
  863.       return a3[p1][p2][p3][e3];
  864.    }
  865.    akkumuations_t fingerabweichung(int i, akkumuations_t rel) const {
  866.       assert(i < nfinger);  assert(rel <= 1);
  867.       return rel-finger_zielhaeufigkeit[i];
  868.    }
  869.    aufwand_t mult_finger(int fi) const { return multfinger[fi]; }
  870.    aufwand_t unbekannt() const { return aunbekannt; }
  871.  
  872.    aufwand_t verwechslungspotenzial(int i, int j) const
  873.    { assert(i < ntaste && j < ntaste); return va2[i][j]; }
  874.    bool hat_aehnlichkeit() const { return mit_aehnlichkeit; }
  875.    aufwand_t aehnlichkeit(int i, int j) const
  876.    { assert(i < ntaste && j < ntaste); return ae[i][j]; }
  877.    bool hat_vorlieben() const { return mit_vorlieben; }
  878.    aufwand_t vorliebe(int z, int p) const
  879.    { assert(z < ntaste && p < ntaste); return vl[z][p]; }
  880.    void anzeigen(const Kodierung&, const Konfiguration&) const;
  881.  
  882.    aufwand_t vorliebenknick() const { return vl_knick; }
  883.    aufwand_t knick(aufwand_t v) const
  884.    { return v < vl_knick ? vl_knick : v; }
  885. };
  886.  
  887. #endif // !BELEGUNGSOPTIMIERER_AUFWANDSTABELLE_H
  888.  
  889. //--------------- src/string_in_belegung.hh ---------------
  890. #ifndef BELEGUNGSOPTIMIERER_STRING_IN_BELEGUNG_H
  891. #define BELEGUNGSOPTIMIERER_STRING_IN_BELEGUNG_H
  892.  
  893. //#include "typen.hh"
  894. #include <string>
  895.  
  896. class Kodierung;
  897.  
  898. void string_in_belegung(const std::u32string& z, belegung_t b, bool* fest,
  899.                         int nv, const Kodierung& kodierung);
  900.  
  901. #endif //!BELEGUNGSOPTIMIERER_STRING_IN_BELEGUNG_H
  902.  
  903. //--------------- src/html_markup.hh ---------------
  904. #ifndef BELEGUNGSOPTIMIERER_HTML_MARKUP_H
  905. #define BELEGUNGSOPTIMIERER_HTML_MARKUP_H
  906.  
  907. #include <string>
  908.  
  909. class Kodierung;
  910. class Tastatur;
  911.  
  912. void html_markup(const std::string& textfile,
  913.                  const Kodierung& kodierung,
  914.                  const Tastatur& tastatur,
  915.                  const std::string& referenztastatur);
  916.  
  917. #endif // !BELEGUNGSOPTIMIERER_HTML_MARKUP_H
  918.  
  919. //--------------- src/schreibe_belegung.hh ---------------
  920. #ifndef BELEGUNGSOPTIMIERER_SCHREIBE_BELEGUNG_H
  921. #define BELEGUNGSOPTIMIERER_SCHREIBE_BELEGUNG_H
  922.  
  923. //#include "typen.hh"
  924. #include <string>
  925. #include <unordered_map>
  926.  
  927. class Aufwandstabelle;
  928. class Haeufigkeit;
  929. class Kodierung;
  930. class Tastatur;
  931.  
  932. void
  933. schreibe_belegung(const belegung_t b, const Tastatur& tastatur,
  934.                   const Kodierung& kodierung, const Haeufigkeit& h,
  935.                   const Aufwandstabelle& a, const akkumuations_t A,
  936.                   const std::u32string& name,
  937.                   const double ngrammakkumlimit[3],
  938.                   const std::unordered_map<std::string, haeufigkeit_t>&
  939.                   wortliste,
  940.                   const akkumuations_t handeinsatzlimit,
  941.                   bool als_fixeszeichen);
  942.  
  943. void
  944. schreibe_belegung(const belegung_t b, double A, int nv,
  945.                   const Kodierung& kodierung,
  946.                   const std::u32string* name);
  947.  
  948. void
  949. schreibe_zyklen(const belegung_t b1, const belegung_t b2,
  950.                 const Kodierung& kodierung);
  951.  
  952. #endif // !BELEGUNGSOPTIMIERER_SCHREIBE_BELEGUNG_H
  953.  
  954. //--------------- src/trennen.hh ---------------
  955. #ifndef BELEGUNGSOPTIMIERER_TRENNEN_H
  956. #define BELEGUNGSOPTIMIERER_TRENNEN_H
  957.  
  958. //#include "Naechstes_zeichen.hh"
  959. #include <string>
  960. #include <unordered_map>
  961. #include <vector>
  962.  
  963. class hole_mit_trennung final : public Naechstes_zeichen {
  964.    std::unordered_map<std::u32string, std::vector<char>> trennmuster;
  965.    std::u32string wort;
  966.    std::vector<bool> trennstellen;
  967.    std::unordered_map<std::u32string, std::vector<bool>> cache;
  968.    size_t wort_i, maxlen;
  969.    bool naechste_ist_trennung;
  970.    char32_t rest;
  971.  
  972.    char32_t fuelle_buffer();
  973. public:
  974.    hole_mit_trennung(const std::string& name, const std::string& tmfile,
  975.                      bool utf8);
  976.    char32_t get() override;
  977. };
  978.  
  979. void markiere_alle_trennstellen(const std::string& ein, const std::string& aus,
  980.                                 const std::string& trennmuster);
  981.  
  982. #endif // !BELEGUNGSOPTIMIERER_TRENNEN_H
  983.  
  984. //--------------- src/wortliste.hh ---------------
  985. #ifndef BELEGUNGSOPTIMIERER_WORTLISTE_H
  986. #define BELEGUNGSOPTIMIERER_WORTLISTE_H
  987.  
  988. //#include "typen.hh"
  989. #include <string>
  990. #include <unordered_map>
  991.  
  992. class Kodierung;
  993.  
  994. void lies_wortliste(const std::string& name,
  995.                     std::unordered_map<std::u32string, zaehl_t>& wl,
  996.                     bool muss_existieren);
  997.  
  998. void lies_wortliste(const std::string name,
  999.                     std::unordered_map<std::string, haeufigkeit_t>& wortliste,
  1000.                     const Kodierung& kodierung);
  1001.  
  1002. #endif // !BELEGUNGSOPTIMIERER_WORTLISTE_H
  1003.  
  1004. //--------------- src/vollkorpus.hh ---------------
  1005. #ifndef BELEGUNGSOPTIMIERER_VOLLKORPUS_H
  1006. #define BELEGUNGSOPTIMIERER_VOLLKORPUS_H
  1007.  
  1008. //#include "typen.hh"
  1009. #include <cstdint>
  1010. #include <string>
  1011. #include <unordered_map>
  1012.  
  1013. void lies_vollkorpus(const std::string& name, const std::string* trennmuster,
  1014.                      std::unordered_map<std::u32string, zaehl_t>& wl,
  1015.                      std::unordered_map<uint64_t, zaehl_t> uh[3]);
  1016.  
  1017.  
  1018. #endif // !BELEGUNGSOPTIMIERER_VOLLKORPUS_H
  1019.  
  1020. //--------------- src/ngramme.hh ---------------
  1021. #ifndef BELEGUNGSOPTIMIERER_NGRAMME_H
  1022. #define BELEGUNGSOPTIMIERER_NGRAMME_H
  1023.  
  1024. #include <string>
  1025. #include <vector>
  1026.  
  1027. void erzeuge_ngrammtabellen(const std::vector<std::string>& namen);
  1028.  
  1029. #endif // !BELEGUNGSOPTIMIERER_NGRAMME_H
  1030.  
  1031. //--------------- src/berechnungen.hh ---------------
  1032. #ifndef BELEGUNGSOPTIMIERER_BERECHNUNGEN_H
  1033. #define BELEGUNGSOPTIMIERER_BERECHNUNGEN_H
  1034.  
  1035. //#include "typen.hh"
  1036. #include <unordered_map>
  1037.  
  1038. class Aufwandstabelle;
  1039. class Grafik;
  1040. class Haeufigkeit;
  1041. class Kodierung;
  1042. class Tastatur;
  1043.  
  1044. void
  1045. suche_optimum(const Tastatur& tastatur, const Kodierung& kodierung,
  1046.               const Haeufigkeit* korpus, const Aufwandstabelle& a,
  1047.               akkumuations_t minimum, bool schoene_tastatur, bool alle_guten,
  1048.               bool als_fixeszeichen, int saatwert, int iterationen,
  1049.               int lebenszeichen, const double ngrammakkumlimit[3],
  1050.               const std::unordered_map<std::string, haeufigkeit_t>& wortliste,
  1051.               akkumuations_t handeinsatzlimit, Grafik* grafik,
  1052.               akkumuations_t* globalesMinimum);
  1053.  
  1054. double anzahl_varianten(int N, int n);
  1055.  
  1056. void
  1057. erzeuge_variationen(const belegung_t ausgangsbelegung,
  1058.                     const Tastatur& tastatur, const Kodierung& kodierung,
  1059.                     Haeufigkeit& h, const Aufwandstabelle& a,
  1060.                     akkumuations_t A, int tiefe,
  1061.                     akkumuations_t limit, const bool* fest);
  1062.  
  1063. akkumuations_t
  1064. aufwandsvarianz(const belegung_t b1, const belegung_t* b2,
  1065.                 const Tastatur& tastatur,
  1066.                 const Haeufigkeit& h,
  1067.                 const Aufwandstabelle& a);
  1068.  
  1069. // Konstanter (belegungsunabhängiger) Aufwand.
  1070. akkumuations_t
  1071. konstanter_aufwand(const Haeufigkeit& h, const Aufwandstabelle& a);
  1072.  
  1073. // Variabler (belegungsabhängiger) Aufwand.
  1074. akkumuations_t
  1075. variabler_aufwand(const belegung_t b, const Haeufigkeit& h,
  1076.                   const Tastatur& tastatur, const Aufwandstabelle& a);
  1077.  
  1078. #endif // !BELEGUNGSOPTIMIERER_BERECHNUNGEN_H
  1079.  
  1080. //--------------- src/Konfiguration.cc ---------------
  1081. //#include "Konfiguration.hh"
  1082.  
  1083. //#include "Eingabestream.hh"
  1084. //#include "Kodierung.hh"
  1085. //#include "Tastatur.hh"
  1086. //#include "typen.hh"
  1087. //#include "utfhilfe.hh"
  1088. #include <cassert>
  1089. #include <cmath>
  1090. #include <iostream>
  1091. #include <limits>
  1092. #include <map>
  1093. #include <unordered_map>
  1094. #include <utility>
  1095.  
  1096. namespace {
  1097.  
  1098. using herkunft_t = std::pair<size_t, std::string>;
  1099. using str_herkunft_t = std::unordered_map<std::u32string, herkunft_t>;
  1100. using char_herkunft_t = std::unordered_map<char32_t, herkunft_t>;
  1101. using zs_herkunft_t =
  1102.               std::map<std::pair<int, int>, std::pair<std::u32string, int>>;
  1103. using gpos_herkunft_t = std::unordered_map<int, std::u32string>;
  1104.  
  1105. std::u32string hole_string(Eingabestream& f, size_t lmin = 0,
  1106.                            size_t lmax = std::numeric_limits<size_t>::max()){
  1107.    std::u32string w;
  1108.    if(f.hole_string(w)){
  1109.       if(w.length() >= lmin && w.length() <= lmax) return w;
  1110.       std::cerr << SPRACHE("Der String '", "The string '")
  1111.                 << utf32_in_ausgabe(w) << SPRACHE("' ist zu ", "' is too ")
  1112.                 << (w.length() < lmin
  1113.                     ? SPRACHE("kurz", "short") : SPRACHE("lang", "long"))
  1114.                 << (w.length() < lmin ? ". Minimal" : ". Maximal")
  1115.                 << SPRACHE("e L" strAe "nge: ", " length: ")
  1116.                 << (w.length() < lmin ? lmin : lmax) << std::endl;
  1117.    }else{
  1118.       std::cerr << SPRACHE("Ein String wurde erwartet, das abschliessende Anf"
  1119.                            strUe "hrungszeichen jedoch nicht gefunden.",
  1120.                            "A string has been expected, however, the closing "
  1121.                            "quotation mark has not been found.") << std::endl;
  1122.    }
  1123.    f.fehler();
  1124. }
  1125.  
  1126. std::u32string hole_wort(Eingabestream& f){
  1127.    std::u32string w;
  1128.    if(f.hole_wort(w)) return w;
  1129.    f.fehler();
  1130. }
  1131.  
  1132. void pruefe_tastenname(Eingabestream& f, const std::u32string& name,
  1133.                        const str_herkunft_t& tn_herkunft)
  1134. {
  1135.    if(tn_herkunft.find(name) == tn_herkunft.end()){
  1136.       std::cerr << SPRACHE("Die Taste '", "The key '") << utf32_in_ausgabe(name)
  1137.                 << SPRACHE("' ist unbekannt.", "' is not known.") << std::endl;
  1138.       f.fehler();
  1139.    }
  1140. }
  1141.  
  1142. std::vector<std::u32string>
  1143. hole_tastenliste(Eingabestream& f, const str_herkunft_t& tn_herkunft){
  1144.    std::vector<std::u32string> namen;
  1145.    std::u32string name;
  1146.    while(f.hole_wort(name)){
  1147.       pruefe_tastenname(f, name, tn_herkunft);
  1148.       namen.push_back(name);
  1149.    }
  1150.    if(namen.size() >= 1) return namen;
  1151.    std::cerr << SPRACHE("Die Liste von Tasten darf nicht leer sein.",
  1152.                         "The list of keys must not be empty.") << std::endl;
  1153.    f.fehler();
  1154. }
  1155.  
  1156. int hole_int(Eingabestream& f, int rmin = -std::numeric_limits<int>::max(),
  1157.              int rmax = std::numeric_limits<int>::max()){
  1158.    const double w = hole_zahl(f, rmin, rmax);
  1159.    const int i = static_cast<int>(w);
  1160.    if(static_cast<double>(i) != w){
  1161.       std::cerr << SPRACHE("Ganze Zahl erwartet, ",
  1162.                            "Expected an integer, found ") << w
  1163.                 << SPRACHE(" gefunden.", ".") << std::endl;
  1164.       f.fehler();
  1165.    }
  1166.    return i;
  1167. }
  1168.  
  1169. bool hole_flag(Eingabestream& f){
  1170.    bool w;
  1171.    if(f.hole_flag(w)) return w;
  1172.    std::cerr << SPRACHE(
  1173.       "Ein Flag ('-' oder '+') wurde erwartet, jedoch nicht gefunden.",
  1174.       "A flag ('-' or '+') has been expected, however, none was found.")
  1175.              << std::endl;
  1176.    f.fehler();
  1177. }
  1178.  
  1179. void das_wars(Eingabestream& f){
  1180.    if(!f.zeilenende()){
  1181.       if(f.ist(U'#')) f.uebergehen();
  1182.       else{
  1183.          std::cerr << SPRACHE(strUe "berf" strUe "ssiger Text am Zeilenende.",
  1184.                               "Excess input at end of the line.") << std::endl;
  1185.          f.fehler();
  1186.       }
  1187.    }
  1188. }
  1189.  
  1190. std::u32string hole_wort_oder_leer(Eingabestream& f){
  1191.    std::u32string w = U"";
  1192.    if(!f.zeilenende() && !f.ist(U'#')) f.hole_wort(w);
  1193.    return w;
  1194. }
  1195.  
  1196. std::u32string hole_string_oder_leer(Eingabestream& f){
  1197.    if(!f.zeilenende() && !f.ist(U'#')) return hole_string(f, 1);
  1198.    return U"";
  1199. }
  1200.  
  1201. int hole_flag_oder_leer(Eingabestream& f){
  1202.    if(!f.zeilenende() && !f.ist(U'#')) return hole_flag(f) ? 1 : -1;
  1203.    return 0;
  1204. }
  1205.  
  1206. void schonmal(Eingabestream& f, herkunft_t& h){
  1207.    std::cerr << SPRACHE("' wurde bereits in File ",
  1208.                         "' has already been introduced in file ")
  1209.              << h.second << SPRACHE(", Zeile ", ", line ")
  1210.              << h.first << SPRACHE(" eingef" strUe "hrt.", ".") << std::endl;
  1211. }
  1212.  
  1213. void tastenname_eindeutigkeit(Eingabestream& f, const std::u32string& name,
  1214.                               str_herkunft_t& tn_herkunft){
  1215.     const auto p = tn_herkunft.find(name);
  1216.     if(p != tn_herkunft.end()){
  1217.        std::cerr << SPRACHE("Der Tastenname '", "The key name '")
  1218.                  << utf32_in_ausgabe(name);
  1219.        schonmal(f, p->second);
  1220.        f.fehler();
  1221.     }
  1222.     tn_herkunft[name] = std::make_pair(f.aktuelle_zeile(), f.aktuelles_file());
  1223. }
  1224.  
  1225. void fixtasten_eindeutigkeit(Eingabestream& f, const std::u32string& name,
  1226.                              str_herkunft_t& herkunft){
  1227.    const auto p = herkunft.find(name);
  1228.    if(p != herkunft.end()){
  1229.       std::cerr << SPRACHE("Der Tastenname '", "The key name '")
  1230.                 << utf32_in_ausgabe(name)
  1231.                 << SPRACHE("' wurde in File ", "' has already been used for "
  1232.                            "'FixesZeichen' in file ")
  1233.                 << p->second.second << SPRACHE(", Zeile ", ", line ")
  1234.                 << p->second.first
  1235.                 << SPRACHE(" bereits f" strUe "r 'FixesZeichen' verwendet.",".")
  1236.                 << std::endl;
  1237.       f.fehler();
  1238.    }
  1239.    herkunft[name] = std::make_pair(f.aktuelle_zeile(), f.aktuelles_file());
  1240. }
  1241.  
  1242. void zs_eindeutigkeit(Eingabestream& f, int spalte, int zeile, int finger,
  1243.                       const std::u32string& name, zs_herkunft_t& zs_herkunft){
  1244.    const auto zs = std::make_pair(spalte, zeile);
  1245.    const auto p = zs_herkunft.find(zs);
  1246.    if(p != zs_herkunft.end() && p->second.second != finger){
  1247.       std::cerr << SPRACHE("Spalte ", "Column ") << spalte
  1248.                 << SPRACHE(", Zeile ", " row ") << zeile
  1249.                 << SPRACHE(" wurde bereits der Taste '",
  1250.                            " has already been assigned to the key '")
  1251.                 << utf32_in_ausgabe(p->second.first)
  1252.                 << SPRACHE("' zugewiesen, und diese ist Finger ",
  1253.                            "' which is associated to finger ")
  1254.                 << p->second.second
  1255.                 << SPRACHE(" statt Finger ", " instead of finger ")
  1256.                 << finger
  1257.                 << SPRACHE(" zugeordnet.", ".") << std::endl;
  1258.       f.fehler();
  1259.    }
  1260.    zs_herkunft[zs] = std::make_pair(name, finger);
  1261. }
  1262.  
  1263. void gpos_eindeutigkeit(Eingabestream& f, int finger,
  1264.                         const std::u32string& name,
  1265.                         gpos_herkunft_t& gpos_herkunft){
  1266.    const auto p = gpos_herkunft.find(finger);
  1267.    if(p != gpos_herkunft.end()){
  1268.       std::cerr << "Finger " << finger
  1269.                 << SPRACHE(" wurde bereits die Taste '",
  1270.                            " has been assigned the key '")
  1271.                 << utf32_in_ausgabe(p->second)
  1272.                 << SPRACHE("' als Grundposition zugewiesen.",
  1273.                            "' as rest position already.") << std::endl;
  1274.       f.fehler();
  1275.    }
  1276.    gpos_herkunft[finger] = name;
  1277. }
  1278.  
  1279. taste_t hole_taste(Eingabestream& f, str_herkunft_t& tn_herkunft,
  1280.                    zs_herkunft_t& zs_herkunft,
  1281.                    gpos_herkunft_t& gpos_herkunft, int minf,int maxf)
  1282. {
  1283.    const std::u32string name = hole_wort(f);
  1284.    tastenname_eindeutigkeit(f, name, tn_herkunft);
  1285.    const int spalte = hole_int(f, 0,nspalte-1);
  1286.    const int zeile = hole_int(f, 0, nzeile-1);
  1287.    const double x = hole_zahl(f, -5., 20.), y = hole_zahl(f, -5., 10.);
  1288.    const finger_t finger(hole_int(f, minf, maxf));
  1289.    zs_eindeutigkeit(f, spalte, zeile, finger, name, zs_herkunft);
  1290.    const bool gpos = hole_flag(f);
  1291.    if(gpos) gpos_eindeutigkeit(f, finger, name, gpos_herkunft);
  1292.    const double aufwand = hole_zahl(f, 0, 1e15);
  1293.    const int optss = hole_flag_oder_leer(f);
  1294.    const bool seite = optss ? optss > 0 : finger > 0;
  1295.    taste_t t{name, x,y, aufwand, spalte, zeile, finger, gpos, seite};
  1296.    das_wars(f);
  1297.    return t;
  1298. }
  1299.  
  1300. std::u32string hole_tastennamen(Eingabestream& f,
  1301.                                 const str_herkunft_t& tn_herkunft){
  1302.    const std::u32string name = hole_wort(f);
  1303.    pruefe_tastenname(f, name, tn_herkunft);
  1304.    return name;
  1305. }
  1306.  
  1307. void nur_bekannte_zeichen(Eingabestream& f, const std::u32string& s,
  1308.                           const char_herkunft_t& zeichen_herkunft){
  1309.    for(size_t i = 0; i < s.length(); ++i){
  1310.       if(zeichen_herkunft.find(s[i]) == zeichen_herkunft.end()){
  1311.          std::cerr << SPRACHE("Das Zeichen '", "The symbol '")
  1312.                    << utf32_in_ausgabe(s[i])
  1313.                    << SPRACHE("' ist unbekannt.", "' is unknown.") << std::endl;
  1314.          f.fehler(i+1);
  1315.       }
  1316.    }
  1317. }
  1318.  
  1319. void zeichen_eindeutigkeit(Eingabestream& f, const std::u32string& klartext,
  1320.                            char32_t platzhalter,
  1321.                            char_herkunft_t& zeichen_herkunft){
  1322.    for(size_t j = 0; j < klartext.length(); ++j){
  1323.       if(klartext[j] != platzhalter){
  1324.          const auto p = zeichen_herkunft.find(klartext[j]);
  1325.          if(p != zeichen_herkunft.end()){
  1326.             std::cerr << SPRACHE("Das Zeichen '", "The symbol '")
  1327.                       << utf32_in_ausgabe(klartext[j]);
  1328.             schonmal(f, p->second);
  1329.             f.fehler(j+1);
  1330.          }
  1331.       }else if(j || klartext.length() == 1u){
  1332.          std::cerr << SPRACHE("Das Platzhalter-Zeichen '",
  1333.                               "The placeholder symbol '")
  1334.                    << utf32_in_ausgabe(klartext[j])
  1335.                    << SPRACHE("' darf nur in Ebene 1 einer Taste mit "
  1336.                               "mehreren Ebenen erscheinen.",
  1337.                               "' must only appear on level 1 of a "
  1338.                               "key with multiple levels.") << std::endl;
  1339.          f.fehler();
  1340.       }
  1341.    }
  1342.    for(auto j : klartext)
  1343.       zeichen_herkunft[j] = std::make_pair(f.aktuelle_zeile(),
  1344.                                            f.aktuelles_file());
  1345. }
  1346.  
  1347. }
  1348.  
  1349.  
  1350.  
  1351. Konfiguration::
  1352. Konfiguration(const std::vector<std::string>& namen,
  1353.               std::unique_ptr<const Tastatur>& tastatur,
  1354.               std::unique_ptr<const Kodierung>& kodierung)
  1355. {
  1356.    struct stdaufwaende {
  1357.       double* tabelle;
  1358.       size_t n;
  1359.       double wmin, wmax;
  1360.    };
  1361.    std::unordered_map<std::u32string, stdaufwaende> aufwaende;
  1362.    aufwaende[U"Zielh\u00e4ufigkeit"]= { fh_unnorm, nfinger, 0., 1e15 };
  1363.    aufwaende[U"Fingerbelastung"]   = { mf_input, nfinger, 0., 1e15 };
  1364.    aufwaende[U"Shiftbigramm"]      = { mult_shiftindirekt, 2, 0., 1. };
  1365.    aufwaende[U"Indirekt"]          = { mult_indirekt, 2, 0., 1. };
  1366.    aufwaende[U"Handwiederholung"]  = { &mult_handwiederholung, 1, -1e15, 1e15 };
  1367.    aufwaende[U"Ausw\u00e4rts"]     = { &mult_auswaerts, 1, -1e15, 1e15 };
  1368.    aufwaende[U"Handwechsel"]       = { &mult_handwechsel, 1, -1e15, 1e15 };
  1369.    aufwaende[U"DoppeltRabatt"]     = { &mult_doppelkomp, 1, 0., 1. };
  1370.    aufwaende[U"Schr\u00e4gZS"]     = { mult_schraegZS, 2, -1e15, 1e15 };
  1371.    aufwaende[U"Schr\u00e4gYX"]     = { mult_schraegYX, 2, -1e15, 1e15 };
  1372.    aufwaende[U"Schr\u00e4gNenner0"]= { add_schraegDX, 2, 0, 1e15 };
  1373.    aufwaende[U"Doppelwechsel"]     = { &mult_doppelwechsel, 1, -1e15, 1e15 };
  1374.    aufwaende[U"Doppelwiederholung"]= { &mult_doppelwiederholung, 1, -1e15,1e15};
  1375.    aufwaende[U"Wippe"]             = { &mult_wippe, 1, -1e15, 1e15 };
  1376.    aufwaende[U"Fehlt"]             = { &aunbekannt, 1, 0, 1e15 };
  1377.    aufwaende[U"KollisionKonstant"] = { mult_kollision_konstant, 5, -1e15, 1e15};
  1378.    aufwaende[U"KollisionDistanz"]  = { mult_kollision_distanz, 5, -1e15, 1e15 };
  1379.    aufwaende[U"Nachbar"]           = { nachbarstrafe, 4, -1e15, 1e15 };
  1380.    aufwaende[U"VPKollision"]       = { &mult_kollision, 1, -1e15, 1e15 };
  1381.    aufwaende[U"VPNachbar"]         = { &mult_nachbar, 1, -1e15, 1e15 };
  1382.    aufwaende[U"VPHandwechsel"]     = { &mult_hand_verschieden, 1, -1e15, 1e15 };
  1383.    aufwaende[U"VPSymmetrisch"]     = { &mult_symmetrisch, 1, -1e15, 1e15 };
  1384.    aufwaende[U"VPSymmetrischGleicheZeile"]= { &mult_symmetrisch_gleichzeile,
  1385.                                               1, -1e15, 1e15 };
  1386.    aufwaende[U"ZeilenwiederholungRabatt"] = { mult_zeilenkomp, 5, 0., 1. };
  1387. #ifdef EXPERIMENTELL
  1388.    aufwaende[U"VorliebeKnick"] = { &vorliebe_knick, 1, -1e15, 1e15 };
  1389. #endif // !EXPERIMENTELL
  1390.    struct bigramm {
  1391.       bigramm(const std::u32string& T1, const std::u32string& T2,
  1392.               const std::u32string& N, double w)
  1393.          : t1(T1), t2(T2), name(N), a(w){}
  1394.       std::u32string t1, t2, name; double a;
  1395.    };
  1396.    struct verwechslungspotenzial {
  1397.       verwechslungspotenzial(const std::u32string& T1, const std::u32string& T2,
  1398.                              double w) : t1(T1), t2(T2), a(w){}
  1399.       std::u32string t1, t2; double a;
  1400.    };
  1401.    struct trigramm {
  1402.       trigramm(const std::u32string& T1, const std::u32string& T2,
  1403.                const std::u32string& T3, const std::u32string& N, double w)
  1404.          : t1(T1), t2(T2), t3(T3), name(N), a(w){}
  1405.       std::u32string t1, t2, t3, name; double a;
  1406.    };
  1407.    struct vorliebe {
  1408.       vorliebe(const std::u32string& Z, double w,
  1409.                const std::vector<std::u32string>& T) : z(Z), a(w), t(T){}
  1410.       std::u32string z; double a; std::vector<std::u32string> t;
  1411.    };
  1412.    std::vector<bigramm> bigramme;
  1413.    std::vector<trigramm> trigramme;
  1414.    std::vector<verwechslungspotenzial> verwechslungspot;
  1415.    std::vector<n_gramm_t> benutzerkategorien;
  1416.    std::vector<std::u32string> klartext, ersatzstring, glyphnamen;
  1417.    std::unordered_map<std::u32string, std::u32string> fixedtext;
  1418.    std::unordered_map<std::u32string, std::u32string> fixedglyphs;
  1419.    char_herkunft_t zeichen_herkunft;
  1420.    gpos_herkunft_t gpos_herkunft;
  1421.    zs_herkunft_t zs_herkunft;
  1422.    str_herkunft_t tn_herkunft, fixtasten;
  1423.    std::vector<taste_t> tasten;
  1424.    std::vector<std::pair<std::u32string, double>> aehnlichkeit;
  1425.    std::vector<vorliebe> vorlieben;
  1426.    std::vector<bool> utf8(namen.size(), true);
  1427.    taste_t ShiftL, ShiftR;
  1428.    herkunft_t platzhalter_herkunft;
  1429.    char32_t platzhalter;
  1430.    bool mitSL, mitSR, nochmal;
  1431.  
  1432.    do{
  1433.       for(auto& i : fh_unnorm) i = 1;
  1434.       for(auto& i : mult_shiftindirekt) i = 1;
  1435.       for(auto& i : mult_indirekt) i = 1;
  1436.       vorliebe_knick = 1e15;
  1437.       _zeichenfont = "Courier-Bold";
  1438.       _beschreibungsfont = "Courier-Bold";
  1439.  
  1440.       bigramme.clear(); trigramme.clear(); verwechslungspot.clear();
  1441.       klartext.clear(); glyphnamen.clear(); ersatzstring.clear();
  1442.       fixedtext.clear();  fixedglyphs.clear(); fixtasten.clear();
  1443.       tasten.clear();  benutzerkategorien.clear();
  1444.       zeichen_herkunft.clear();  tn_herkunft.clear();
  1445.       zs_herkunft.clear(); gpos_herkunft.clear();
  1446.       aehnlichkeit.clear();
  1447.       vorlieben.clear();
  1448.       mitSL = mitSR = nochmal = false;
  1449.       platzhalter = 0;
  1450.       for(size_t i = 0; !nochmal && i < namen.size(); ++i){
  1451.          Eingabestream f(namen[i], utf8[i]);
  1452.          while(f.echte_neuezeile()){
  1453.             std::u32string wort;
  1454.             if(!f.hole_wort(wort)) f.fehler();
  1455.             if(wort == U"Taste"){
  1456.                tasten.push_back(hole_taste(f, tn_herkunft, zs_herkunft,
  1457.                                            gpos_herkunft, -5, 5));
  1458.             }else if(wort == U"ShiftL"){
  1459.                ShiftL = hole_taste(f, tn_herkunft, zs_herkunft,
  1460.                                    gpos_herkunft, -5, -1);
  1461.                mitSL = true;
  1462.             }else if(wort == U"ShiftR"){
  1463.                ShiftR = hole_taste(f, tn_herkunft, zs_herkunft,
  1464.                                    gpos_herkunft, 1, 5);
  1465.                mitSR = true;
  1466.             }else if(wort == U"Platzhalter"){
  1467.                if(platzhalter){
  1468.                   std::cerr << "'Platzhalter";
  1469.                   schonmal(f, platzhalter_herkunft);
  1470.                   f.fehler();
  1471.                }
  1472.                if(klartext.size() || fixedtext.size()){
  1473.                   std::cerr << "'Platzhalter' "
  1474.                      SPRACHE("muss vor der ersten Zeichenfestlegung stehen.",
  1475.                              "must appear before the first symbol definition.")
  1476.                             << std::endl;
  1477.                   f.fehler();
  1478.                }
  1479.                platzhalter = hole_string(f, 1, 1)[0];
  1480.                platzhalter_herkunft =
  1481.                   std::make_pair(f.aktuelle_zeile(), f.aktuelles_file());
  1482.             }else if(wort == U"Zeichen"){
  1483.                const auto zeichen = hole_string(f, 1);
  1484.                zeichen_eindeutigkeit(f, zeichen, platzhalter, zeichen_herkunft);
  1485.                klartext.push_back(zeichen);
  1486.                glyphnamen.push_back(hole_wort_oder_leer(f));
  1487.                das_wars(f);
  1488.             }else if(wort == U"FixesZeichen"){
  1489.                const auto taste = hole_tastennamen(f, tn_herkunft);
  1490.                if((mitSL && taste == ShiftL.name) ||
  1491.                   (mitSR && taste == ShiftR.name)){
  1492.                   std::cerr << SPRACHE(
  1493.                      "Shifttasten d" strUe "rfen nicht in 'FixesZeichen' "
  1494.                      "verwendet werden.",
  1495.                      "Shift keys must not be used in 'FixesZeichen'.")
  1496.                             << std::endl;
  1497.                   f.fehler();
  1498.                }
  1499.                fixtasten_eindeutigkeit(f, taste, fixtasten);
  1500.                const auto zeichen = hole_string(f, 1);
  1501.                zeichen_eindeutigkeit(f, zeichen, platzhalter, zeichen_herkunft);
  1502.                const auto glyph = hole_wort_oder_leer(f);
  1503.                das_wars(f);
  1504.                fixedglyphs[taste] = glyph;
  1505.                fixedtext[taste] = zeichen;
  1506.             }else if(wort == U"Vorliebe"){
  1507.                const std::u32string s = hole_string(f, 1);
  1508.                nur_bekannte_zeichen(f, s, zeichen_herkunft);
  1509.                vorlieben.push_back(vorliebe{s, -hole_zahl(f, -1e15, 1e15),
  1510.                                             hole_tastenliste(f, tn_herkunft) });
  1511.             }else if(wort == U"Ersatz"){
  1512.                ersatzstring.push_back(hole_string(f, 2));
  1513.                das_wars(f);
  1514.             }else if(wort == U"\u00c4hnlich"){
  1515.                const std::u32string s = hole_string(f, 2);
  1516.                nur_bekannte_zeichen(f, s, zeichen_herkunft);
  1517.                aehnlichkeit
  1518.                   .push_back(std::make_pair(s, hole_zahl(f, 0, 1e15)));
  1519.                das_wars(f);
  1520.             }else if(wort == U"Bigramm"){
  1521.                const std::u32string t1 = hole_tastennamen(f, tn_herkunft);
  1522.                const std::u32string t2 = hole_tastennamen(f, tn_herkunft);
  1523.                const double aufwand = hole_zahl(f, -1e15, 1e15);
  1524.                const std::u32string name = hole_string_oder_leer(f);
  1525.                das_wars(f);
  1526.                bigramme.push_back(bigramm{t1, t2, name, aufwand});
  1527.             }else if(wort == U"Verwechslungspotenzial"){
  1528.                verwechslungspot.push_back
  1529.                   (verwechslungspotenzial{hole_tastennamen(f, tn_herkunft),
  1530.                                           hole_tastennamen(f,tn_herkunft),
  1531.                                           hole_zahl(f, -1e15, 1e15)});
  1532.                if(verwechslungspot.back().t1 == verwechslungspot.back().t2){
  1533.                   std::cerr << SPRACHE("Ein Verwechslungspotenzial gibt es nur "
  1534.                                              "zwischen verschiedenen Tasten.",
  1535.                                        "Confusability only exists between "
  1536.                                        "different keys.") << std::endl;
  1537.                   f.fehler();
  1538.                }
  1539.                das_wars(f);
  1540.             }else if(wort == U"Trigramm"){
  1541.                const std::u32string t1 = hole_tastennamen(f, tn_herkunft);
  1542.                const std::u32string t2 = hole_tastennamen(f, tn_herkunft);
  1543.                const std::u32string t3 = hole_tastennamen(f, tn_herkunft);
  1544.                const double aufwand = hole_zahl(f, -1e15, 1e15);
  1545.                const std::u32string name = hole_string_oder_leer(f);
  1546.                das_wars(f);
  1547.                trigramme.push_back(trigramm{t1, t2, t3, name, aufwand});
  1548.             }else if(wort == U"Zeichenfont"){
  1549.                _zeichenfont = utf32_in_ausgabe(hole_wort(f));
  1550.                das_wars(f);
  1551.             }else if(wort == U"Beschreibungsfont"){
  1552.                _beschreibungsfont = utf32_in_ausgabe(hole_wort(f));
  1553.                das_wars(f);
  1554.             }else{
  1555.                const auto k = aufwaende.find(wort);
  1556.                if(k == aufwaende.end()){
  1557.                   std::cerr << SPRACHE("Unbekanntes Schl" strUe "sselwort ",
  1558.                                        "Unknown keyword ")
  1559.                             << utf32_in_ausgabe(wort) << std::endl;
  1560.                   f.fehler();
  1561.                }else{
  1562.                   double* a = k->second.tabelle;
  1563.                   const size_t n = k->second.n;
  1564.                   const double wmin = k->second.wmin, wmax = k->second.wmax;
  1565.                   for(size_t j = 0; j < n; ++j) a[j] = hole_zahl(f, wmin, wmax);
  1566.                   das_wars(f);
  1567.                }
  1568.             }
  1569.          }
  1570.          if(f.encoding_geaendert()){
  1571.             nochmal = f.encoding_geaendert();
  1572.             utf8[i] = false;
  1573.          }
  1574.       }
  1575.    }while(nochmal);
  1576.  
  1577.    if(tasten.size() != ntaste){
  1578.       std::cerr << "'Taste' " SPRACHE("kommt ", "appears ")
  1579.                 << tasten.size() << SPRACHE(" mal vor, erwartet ist ",
  1580.                                             " times, expected is ")
  1581.                 << ntaste
  1582.                 << SPRACHE(" mal.  Sie k" strOe "nnen entweder " strAe "ndern, "
  1583.                            "wie oft 'Taste' vorkommt oder die erwartete "
  1584.                            "Tastenzahl mit der Compileroption",
  1585.                            " times.  You can either fix the number of "
  1586.                            "occurrences of 'Taste', or adjust the number of "
  1587.                            "expected keys by using the compiler option")
  1588.                            " '-DTASTENZAHL=" << tasten.size()+nshift
  1589.                 << SPRACHE("' einstellen.", "'.") << std::endl;
  1590.       exit(1);
  1591.    }
  1592.  
  1593.    // Die Zahl der Shifttasten festgelegen stimmen.
  1594.    if(nshift != (mitSL ? 1 : 0)+(mitSR ? 1 : 0)){
  1595.       if(!mitSL)
  1596.          std::cerr << SPRACHE("ShiftL wurde nicht festgelegt.",
  1597.                               "ShiftL has not been defined.") << std::endl;
  1598.       if(!mitSR)
  1599.          std::cerr << SPRACHE("ShiftR wurde nicht festgelegt.",
  1600.                               "ShiftR has not been defined.") << std::endl;
  1601.       if(mitSL && mitSR)
  1602.          std::cerr << SPRACHE("ShiftL und ShiftR wurden beide festgelegt.",
  1603.                               "ShiftL and ShiftR have both been defined.")
  1604.                    << std::endl;
  1605.       exit(1);
  1606.    }
  1607.  
  1608.    if(mitSL) tasten.push_back(ShiftL);
  1609.    if(mitSR) tasten.push_back(ShiftR);
  1610.  
  1611.    std::unordered_set<std::u32string> fixset;
  1612.    for(const auto& i : fixtasten) fixset.insert(i.first);
  1613.    Tastatur* neuetastatur = new Tastatur(tasten, fixset);
  1614.    tastatur = std::unique_ptr<const Tastatur>(neuetastatur);
  1615.  
  1616.    for(int i = tastatur->nvariabel(); i < ntaste; ++i){
  1617.       klartext.push_back(fixedtext[tastatur->name(i)]);
  1618.       glyphnamen.push_back(fixedglyphs[tastatur->name(i)]);
  1619.    }
  1620.    kodierung = std::unique_ptr<const Kodierung>
  1621.       (new Kodierung(klartext, ersatzstring, glyphnamen, platzhalter));
  1622.  
  1623.    for(const auto& i : bigramme){
  1624.       const int i1 = tastatur->taste(i.t1), i2 = tastatur->taste(i.t2);
  1625.       bigramm_roh[i1][i2] = i.a;
  1626.       if(i.name.length())
  1627.          benutzerkategorien.push_back(n_gramm_t{i1, i2, -1, i.name});
  1628.    }
  1629.    for(const auto& i : verwechslungspot){
  1630.       const int i1 = tastatur->taste(i.t1), i2 = tastatur->taste(i.t2);
  1631.       verwechslungspotenzial_roh[i1][i2] =
  1632.          verwechslungspotenzial_roh[i2][i1] = i.a;
  1633.    }
  1634.    for(const auto& i : trigramme){
  1635.       const int i1 = tastatur->taste(i.t1), i2 = tastatur->taste(i.t2);
  1636.       const int i3 = tastatur->taste(i.t3);
  1637.       trigramm_roh[i1][i2][i3] = i.a;
  1638.       if(i.name.length())
  1639.          benutzerkategorien.push_back(n_gramm_t{i1, i2, i3, i.name});
  1640.    }
  1641.    for(const auto& i : vorlieben){
  1642.       for(const auto& j : i.t){
  1643.          const int taste = tastatur->taste(j);
  1644.          const double aufwand = i.a;
  1645.          for(const auto& s : i.z){
  1646.             const auto z = kodierung->position(s);
  1647.             vorliebe_roh[z.first][taste] += aufwand;
  1648.          }
  1649.       }
  1650.    }
  1651.  
  1652.    neuetastatur->neue_kategorien(benutzerkategorien);
  1653.  
  1654.    for(const auto& i : aehnlichkeit){
  1655.       const std::u32string t = i.first;
  1656.       const double w = i.second;
  1657.       for(size_t j = 0; j+1 < t.length(); ++j){
  1658.          const auto p1 = kodierung->position(t[j]);
  1659.          for(size_t k = j+1; k < t.length(); ++k){
  1660.             const auto p2 = kodierung->position(t[k]);
  1661.             aehnlichkeit_roh[p2.first][p1.first] += w;
  1662.             aehnlichkeit_roh[p1.first][p2.first] += w;
  1663.          }
  1664.       }
  1665.    }
  1666. }
  1667.  
  1668. double
  1669. Konfiguration::
  1670. bigrammaufwand(int i, int j, const Tastatur& tastatur) const {
  1671.    const int zeile_i = tastatur.zeile(i);
  1672.    const int spalte_i = tastatur.spalte(i);
  1673.    const finger_t finger_i = tastatur.finger(i);
  1674.  
  1675.    const auto zeile_j = tastatur.zeile(j);
  1676.    const int spalte_j = tastatur.spalte(j);
  1677.    const finger_t finger_j = tastatur.finger(j);
  1678.    const int grundpos_j = tastatur.grundposition(j);
  1679.  
  1680.    if(tastatur.kategorie(i,j) == kategorie_t::Handwechsel){
  1681.       return mult_handwechsel+bigramm_roh[i][j];
  1682.    }else if(tastatur.kategorie(i,j) == kategorie_t::MitUndefDaumen){
  1683.       return bigramm_roh[i][j];
  1684.    }else if(tastatur.kategorie(i,j) == kategorie_t::Doppeltanschlag){
  1685.       // Wenn wir eine Taste abseits der Grundposition zweimal anschlagen,
  1686.       // müssen wir den Finger nur einmal dorthin bewegen, der Mehraufwand
  1687.       // gegenüber dem Anschlag auf der Grundposition entfällt somit beim
  1688.       // zweiten Anschlag.
  1689.       return mult_doppelkomp*
  1690.          (tastatur.lageaufwand(grundpos_j)-tastatur.lageaufwand(j))
  1691.          +bigramm_roh[i][j];
  1692.    }else if(tastatur.kategorie(i,j) == kategorie_t::Kollision){
  1693.       // Kollisionen sind umso schlimmer, je weiter der Finger springen muss.
  1694.       const int idx = std::abs(finger_i)-finger_t::DaumenRechts;
  1695.       return mult_handwiederholung+mult_kollision_konstant[idx]+
  1696.          mult_kollision_distanz[idx]*tastatur.distanz(i,j)+bigramm_roh[i][j];
  1697.    }else{
  1698.       // Verschiedene Finger auf derselben Hand.
  1699.       const bool auswaerts = tastatur.kategorie(i,j) == kategorie_t::Auswaerts;
  1700.       const double dspalte = std::abs(spalte_i-spalte_j);
  1701.       const double dx =
  1702.          std::abs(tastatur.tastenkoord(i).x-tastatur.tastenkoord(j).x);
  1703.       const double dy =
  1704.          std::abs(tastatur.tastenkoord(i).y-tastatur.tastenkoord(j).y);
  1705.       const double dfinger = std::abs(finger_i-finger_j);
  1706.       const double dzeile = std::abs(zeile_i-zeile_j);
  1707.       const int minfingerindex =
  1708.          std::min(std::abs(finger_i), std::abs(finger_j))
  1709.          -finger_t::DaumenRechts;
  1710.       const bool mitDaumen = (minfingerindex == 0);
  1711.       // Wenn wir eine Taste in einer Zeile abseits der Grundzeile anschlagen
  1712.       // bewegen wir die Hand dorthin, folgende Anschläge von anderen Tasten
  1713.       // in dieser Zeile wird leichter.
  1714.       const int dspaltem1 = std::abs(std::abs(spalte_i-spalte_j)-1);
  1715.       const double zkomp = mult_zeilenkomp[dspaltem1 < 5 ? dspaltem1 : 4];
  1716.       const double komp =
  1717.          (!mitDaumen && dzeile == 0 && zeile_i != zeilen_t::Mittelzeile &&
  1718.           (zeile_i != zeilen_t::Untere_Zeile ||
  1719.            !Tastatur::istKleinfinger(finger_i)))
  1720.          ? zkomp*(tastatur.lageaufwand(grundpos_j)-tastatur.lageaufwand(j))
  1721.          : 0;
  1722.  
  1723.       const double offset = add_schraegDX[finger_i > 0];
  1724.       const double nZS = dspalte+offset, nYX = dx+offset;
  1725.       const double zZS = mitDaumen
  1726.          ? 0. : mult_schraegZS[finger_i > 0]*dzeile;
  1727.       const double zYX = mitDaumen
  1728.          ? 0. : mult_schraegYX[finger_i > 0]*dy;
  1729.       const double zaehler = zZS*nYX+zYX*nZS, nenner = nZS*nYX;
  1730.       if((nZS == 0. && zZS != 0.) || (nYX == 0. && zYX != 0.)){
  1731.          std::cerr << SPRACHE(
  1732.             "Division durch Null f" strUe "r schr" strAe "ge Griffe.  "
  1733.             "Dieser Fehler kann auftreten, wenn verschiedene Finger "
  1734.             "Tasten in derselben Zeile bedienen.  'Schr" strAe
  1735.             "gNenner0' schafft Abhilfe.",
  1736.             "Division by zero for hand distorting digrams.  This "
  1737.             "error can occur if different fingers operate keys in "
  1738.             "the same column. 'Schr" strAe "gNenner0' can solve this.")
  1739.                    << std::endl;
  1740.          exit(1);
  1741.       }
  1742.       const double quotient = zaehler == 0. ? 0. : zaehler/nenner;
  1743.  
  1744.       return komp+
  1745.          // Aufwand für Handwiederholung.
  1746.          mult_handwiederholung
  1747.          // Zusatzaufwand für Auswärtsbewegung.
  1748.          +(auswaerts && !mitDaumen ? mult_auswaerts : 0)
  1749.          // Aufwand wenn die Tasten benachbart sind
  1750.          +(dfinger == 1 ? nachbarstrafe[minfingerindex] : 0)
  1751.          // Aufwand, wenn die Tasten in verschiedenen Zeilen liegen; je
  1752.          // schräger der Griff desto schlimmer.
  1753.          +quotient
  1754.          +bigramm_roh[i][j];
  1755.    }
  1756. }
  1757.  
  1758. double
  1759. Konfiguration::
  1760. trigrammaufwand(int i, int j, int k, const Tastatur& tastatur) const {
  1761.    // Sinnvoll sind Aufwände nur, wenn erste und letzte Taste auf derselben
  1762.    // Hand sind.
  1763.    if(!tastatur.istHandwiederholung(i,k)) return trigramm_roh[i][j][k];
  1764.  
  1765.    // Ansonsten bauen wir die Trigrammaufwände aus den Bigrammaufwänden.  bi
  1766.    // ist der Bigrammaufwand für die erste und dritte Taste:
  1767.    const double bi = bigrammaufwand(i, k, tastatur);
  1768.    const double ik = mult_indirekt[bi < 0]*bi;
  1769.    if(tastatur.istHandwiederholung(i,j)){
  1770.       // Drei Tasten mit derselben Hand.
  1771.       const double extra = trigramm_roh[i][j][k]+mult_doppelwiederholung+
  1772.          (tastatur.istWippe(i, j, k) ? mult_wippe : 0);
  1773.       if(ik <= 0) return extra;
  1774.       const double ij = bigrammaufwand(i, j, tastatur);
  1775.       const double jk = bigrammaufwand(j, k, tastatur);
  1776.       // Insgesamt sollte der Aufwand mindestens so hoch wie er gewesen
  1777.       // wäre, wenn die mittlere Taste auf der anderen Hand gelegen wäre.
  1778.       // Man könnte vielleicht in einigen Fällen mehr berechnen, aber sicher
  1779.       // nicht weniger.
  1780.       return ik > ij+jk ? extra+ik-ij-jk : extra;
  1781.    }else
  1782.       // Zwei Handwechsel.
  1783.       return ik+mult_doppelwechsel+trigramm_roh[i][j][k];
  1784. }
  1785.  
  1786. double
  1787. Konfiguration::
  1788. verwechslungspotenzial(int i, int j, const Tastatur& tastatur) const {
  1789.    const finger_t finger_i = tastatur.finger(i);
  1790.    const finger_t finger_j = tastatur.finger(j);
  1791.    if(!tastatur.istHandwiederholung(i,j)){
  1792.       if(!Tastatur::istDaumen(finger_i) &&
  1793.          std::abs(finger_i) == std::abs(finger_j)){
  1794.          const int zi = tastatur.zeile(i), zj = tastatur.zeile(j);
  1795.          return (zi == zj ? mult_symmetrisch_gleichzeile : mult_symmetrisch)
  1796.             +mult_hand_verschieden
  1797.             +verwechslungspotenzial_roh[i][j];
  1798.       }else return mult_hand_verschieden
  1799.                +verwechslungspotenzial_roh[i][j];
  1800.    }else if(tastatur.kategorie(i,j) == kategorie_t::Kollision){
  1801.       return mult_kollision+verwechslungspotenzial_roh[i][j];
  1802.    }else{
  1803.       return (std::abs(finger_i-finger_j) == 1 ? mult_nachbar : 0)
  1804.          +verwechslungspotenzial_roh[i][j];
  1805.    }
  1806. }
  1807.  
  1808. double
  1809. Konfiguration::vorliebe(int i, int j) const {
  1810.    assert(i < ntaste+nshift && j < ntaste+nshift);
  1811.    return vorliebe_roh[i][j];
  1812. }
  1813.  
  1814. double
  1815. Konfiguration::vorliebenknick() const
  1816. { return vorliebe_knick; }
  1817.  
  1818. double
  1819. Konfiguration::aehnlichkeit(int i, int j) const {
  1820.    assert(i < ntaste+nshift && j < ntaste+nshift);
  1821.    return aehnlichkeit_roh[i][j];
  1822. }
  1823.  
  1824. double
  1825. Konfiguration::shiftindirekt(double a) const
  1826. { return mult_shiftindirekt[a < 0]; }
  1827.  
  1828. double
  1829. Konfiguration::indirekt(double a) const
  1830. { return mult_indirekt[a < 0]; }
  1831.  
  1832. double
  1833. Konfiguration::zielhaeufigkeit(int f) const {
  1834.    assert(f < nfinger);
  1835.    return fh_unnorm[f];
  1836. }
  1837.  
  1838. double
  1839. Konfiguration::multfinger(int f) const {
  1840.    assert(f < nfinger);
  1841.    return mf_input[f];
  1842. }
  1843.  
  1844. double
  1845. Konfiguration::unbekannt() const
  1846. { return aunbekannt; }
  1847.  
  1848. const std::string&
  1849. Konfiguration::zeichenfont() const
  1850. { return _zeichenfont; }
  1851.  
  1852. const std::string&
  1853. Konfiguration::beschreibungsfont() const
  1854. { return _beschreibungsfont; }
  1855.  
  1856. //--------------- src/main.cc ---------------
  1857. //#include "Aufwandstabelle.hh"
  1858. //#include "Eingabestream.hh"
  1859. //#include "Grafik.hh"
  1860. //#include "Haeufigkeit.hh"
  1861. //#include "Kodierung.hh"
  1862. //#include "Konfiguration.hh"
  1863. //#include "Tastatur.hh"
  1864. //#include "berechnungen.hh"
  1865. //#include "copyright.hh"
  1866. //#include "html_markup.hh"
  1867. //#include "konstanten.hh"
  1868. //#include "ngramme.hh"
  1869. //#include "schreibe_belegung.hh"
  1870. //#include "string_in_belegung.hh"
  1871. //#include "trennen.hh"
  1872. //#include "typen.hh"
  1873. //#include "utfhilfe.hh"
  1874. //#include "wortliste.hh"
  1875. #include <algorithm>
  1876. #include <cassert>
  1877. #include <chrono>
  1878. #include <cmath>
  1879. #include <cstdint>
  1880. #include <cstdio>
  1881. #include <cstdlib>
  1882. #include <cstring>
  1883. #include <fstream>
  1884. #include <iomanip>
  1885. #include <iostream>
  1886. #include <limits>
  1887. #include <map>
  1888. #include <memory>
  1889. #include <random>
  1890. #include <sstream>
  1891. #include <string>
  1892. #ifdef MIT_THREADS
  1893. #include <thread>
  1894. #endif // MIT_THREADS
  1895. #include <unordered_map>
  1896. #include <unordered_set>
  1897. #include <vector>
  1898.  
  1899. template <typename S, typename Z>
  1900. void einfuegen(std::unordered_map<S, Z>& summe,
  1901.                std::unordered_map<S, Z>& summand)
  1902. {
  1903.    if(summe.size())
  1904.       for(const auto& k : summand) summe[k.first] += k.second;
  1905.    else summe.swap(summand);
  1906. }
  1907.  
  1908. void hilfe()
  1909. {
  1910. #ifdef ENGLISH
  1911.    std::cout <<
  1912. "Keyboard layout optimiser version " << opt_version << ", compiled for "
  1913.                                      << ntaste+nshift << " keys"
  1914. #ifdef OHNE2SHIFT
  1915. "\n(using option -DOHNE2SHIFT).\n\n"
  1916. #else
  1917. ".\n\n"
  1918. #endif
  1919. "opt [-2|-3 prefix][-A][-b upto][-g file][-G num][-H upto][-i maxiter]\n"
  1920. "    [-k][-K][-m max][-r file][-s seed][-t num][-T][-V num][-w file]\n\n"
  1921. "-2 prefix  Specifies prefix for UTF-8 encoded files with character frequencies,\n"
  1922. "           (suffix .1) and digram frequencies (suffix .2).\n"
  1923. "-3 prefix  As -2, additionally with trigram frequencies (suffix .3).\n"
  1924. "-A         Dump the efforts used.\n"
  1925. "-b upto    Create summary of digrams up to given cumulative frequency per hand.\n"
  1926. "-f         Displays layout as 'FixesZeichen'; no not use with option -k.\n"
  1927. "-g file    Create a PostScript file with a graphical evaluation of the layouts.\n"
  1928. "-G weight  Weight used for subsequent frequency files.\n"
  1929. "-H upto    Print one-handed sequences up to given cumulative frequency.\n"
  1930. "-i maxiter Number of local optima to compute.\n"
  1931. "-k         Output results in compact form (one line per layout).\n"
  1932. "-K file    Use the given configuration file.\n"
  1933. "-m max     If this option is present, all layouts with a total effort below the\n"
  1934. "           given threshold will be printed.\n"
  1935. "-M file    Displays how the file is entered, as HTML (must be used with -r).\n"
  1936. "-r file    Output layouts read from the file; do not perform an optimisation.\n"
  1937. "-s seed    Seed for random number generator, a positive number.\n"
  1938. #ifdef MIT_THREADS
  1939. "-t num     Use num threads (default 1).\n"
  1940. #endif // MIT_THREADS
  1941. "-T         Add soft hyphenation in corpus.\n"
  1942. "-V maxdiff Variation depth (must be used with -r).\n"
  1943. "-w file    Specifies a word list." << std::endl;
  1944. #else
  1945.    std::cout << "Belegungsoptimierer Version " << opt_version
  1946.              << ", " strUe "bersetzt f" strUe "r "
  1947.              << ntaste+nshift << " Tasten"
  1948. #ifdef OHNE2SHIFT
  1949. "\n(mit Option -DOHNE2SHIFT).\n\n"
  1950. #else
  1951. ".\n\n"
  1952. #endif
  1953. "opt [-2|-3 pr" strAe "fix][-A][-b biszu][-g file][-G num][-H upto][-i maxiter]\n"
  1954. "    [-k][-K][-m max][-r file][-s Saat][-t num][-T][-V num][-w file]\n\n"
  1955. "-2 pr" strAe "fix  Spezifiziert Pr" strAe "fix f" strUe "r UTF-8-kodierte Files mit\n"
  1956. "           Zeichenh" strAe "ufigkeiten (Suffix .1) und Bigrammh" strAe "ufigkeiten (Suffix .2).\n"
  1957. "-3 pr" strAe "fix  Wie -2, zus" strAe "tzlich mit Trigrammh" strAe "ufigkeiten (Suffix .3).\n"
  1958. "-A         Gib verwendete Aufw" strAe "nde aus.\n"
  1959. "-b biszu   Gib Bigrammaufstellung bis zu angegebener summierter H" strAe "ufigkeit\n"
  1960. "           pro Hand aus.\n"
  1961. "-f         Gibt Belegung als 'FixesZeichen' aus; nicht mit Option -k verwenden.\n"
  1962. "-g file    Erzeuge ein PostScript-File mit grafischer Auswertung\n"
  1963. "           der Belegungen.\n"
  1964. "-G gewicht Gewicht f" strUe "r folgende H" strAe "ufigkeitsfiles.\n"
  1965. "-H biszu   Kumulierte H" strAe "ufigkeit, bis zu der Handeins" strAe "tze ausgegeben werden.\n"
  1966. "-i maxiter Anzahl der zu berechnenden lokalen Optimierungen.\n"
  1967. "-k         Gibt Resultate in einer kompakten Form (eine Zeile pro Belegung) aus.\n"
  1968. "-K file    Benutze die Konfiguration im angegeben File.\n"
  1969. "-m max     Wenn diese Option angegeben wird, werden Belegungen mit\n"
  1970. "           Gesamtaufwand unter der angegbenen Schranke angezeigt.\n"
  1971. "-M file    Zeigt als HTML, wie das File eingegeben wird (in Verbindung mit -r).\n"
  1972. "-r file    Gibt Referenzbelegungen im File aus; es wird keine Optimierung\n"
  1973. "           durchgef" strUe "hrt.\n"
  1974. "-s Saat    Saat f" strUe "r den Zufallsgenerator, eine positive, ganze Zahl.\n"
  1975. #ifdef MIT_THREADS
  1976. "-t num     Benutze num Threads (Voreinstellung 1).\n"
  1977. #endif // MIT_THREADS
  1978. "-T         Weiche Trennzeichen in Korpus einf" strUe "gen.\n"
  1979. "-V maxdiff Variantentiefe (in Verbindung mit -r).\n"
  1980. "-w file    Spezifiziert eine Wortliste." << std::endl;
  1981. #endif
  1982.    exit(0);
  1983. }
  1984.  
  1985. const char* argument(int& argc, char* const*& argv)
  1986. {
  1987.    if(argc < 2){
  1988.       std::cerr << SPRACHE("Die Option '", "Option '") << *argv
  1989.                 << SPRACHE("' erwartet ein Argument.",
  1990.                            "' requires an argument.") << std::endl;
  1991.       exit(1);
  1992.    }
  1993.    argv++; argc--;
  1994.    return *argv;
  1995. }
  1996.  
  1997. double fargument(int& argc, char* const*& argv)
  1998. {
  1999.    const std::string opt = *argv;
  2000.    const char* c = argument(argc, argv);
  2001.    char* x;
  2002.    const double r = strtod(c, &x);
  2003.    if(*x){
  2004.       std::cerr << SPRACHE("Das Argument der Option '", "The argument of option '")
  2005.                   << opt << SPRACHE("' muss eine Zahl sein.", "' must be a number.")
  2006.                   << std::endl;
  2007.         exit(1);
  2008.     }
  2009.     return r;
  2010. }
  2011. int iargument(int& argc, char* const*& argv)
  2012. {
  2013.    const std::string opt = *argv;
  2014.    const char* c = argument(argc, argv);
  2015.    char* x;
  2016.    const long r = strtol(c, &x, 10);
  2017.    if(*x){
  2018.       std::cerr << SPRACHE("Das Argument der Option '",
  2019.                            "The argument of option '") << opt
  2020.                 << SPRACHE("' muss ganzzahlig sein.", "' must be an integer.")
  2021.                 << std::endl;
  2022.       exit(1);
  2023.    }
  2024.    if(std::abs(r) > std::numeric_limits<int>::max()){
  2025.       std::cerr << SPRACHE("Der Betrag des Arguments der Option '",
  2026.                            "The magnitude of the argument of option '") << opt
  2027.                 << SPRACHE("' muss muss kleiner oder gleich ",
  2028.                            "' must be less than or equal to ")
  2029.                 << std::numeric_limits<int>::max()
  2030.                 << SPRACHE(" sein.", ".") << std::endl;
  2031.       exit(1);
  2032.    }
  2033.    return r;
  2034. }
  2035.  
  2036. int main(int argc, char* const argv[])
  2037. {
  2038.    std::random_device rd;
  2039.  
  2040.    bool alle_guten = false, trigramm = false, korpusstatistik = false;
  2041.    bool schoene_tastatur = true, zyklen = false, aufwandstabelle = false;
  2042.    bool irgendeine_option = false, trennen = false, als_fixeszeichen = false;
  2043.    std::string referenztastatur, grafikname;
  2044.    int iterationen = std::numeric_limits<int>::max(), num_variierte_tasten = 0;
  2045.    int saatwert = rd(), nthreads = 1, iakkumlimit = 0;
  2046.    akkumuations_t minimum = 1e38, handeinsatzlimit = 95;
  2047.    double ngrammakkumlimit[3] = { -1, -1, -1 }, gewicht = 1, gewichtssumme = 0;
  2048.    std::vector<std::string> korpusnamen, freie_argumente;
  2049.    std::vector<std::string> konfigurationsfiles, wortlisten, markup;
  2050.    std::vector<double> gewichte;
  2051.    std::vector<bool> trigramme;
  2052.    std::unordered_map<std::string, haeufigkeit_t> wortliste;
  2053.  
  2054.    const std::string progname = *argv++;  argc--;
  2055.    for(; argc; argv++, argc--){
  2056.       const std::string option = *argv;
  2057.       bool ist_option = true;
  2058.  
  2059.       if(option == "-2" || option == "-3"){
  2060.          if(korpusnamen.size() >= nmaxkorpus){
  2061.             std::cerr << SPRACHE("Man kann maximal ",
  2062.                                  "You can specify at most ") << nmaxkorpus
  2063.                       << SPRACHE(" verschiedene Korpora angeben.  Im "
  2064.                                  "Sourcecode 'nmaxkorpus' erh" strOe "hen.",
  2065.                                  " different corpora.  In the source code, "
  2066.                                  "increase 'nmaxkorpus'.")
  2067.                       << std::endl;
  2068.             exit(1);
  2069.          }
  2070.          korpusnamen.emplace_back(argument(argc, argv));
  2071.          gewichte.push_back(gewicht);
  2072.          gewichtssumme += gewicht;
  2073.          trigramme.push_back(option == "-3");
  2074.          trigramm |= trigramme.back();
  2075.       }else if(option == "-A"){
  2076.          aufwandstabelle = true;
  2077.       }else if(option == "-b"){
  2078.          if(iakkumlimit < 3){
  2079.             ngrammakkumlimit[iakkumlimit] = fargument(argc, argv)/100.;
  2080.             if(ngrammakkumlimit[iakkumlimit] < 0){
  2081.                std::cerr << SPRACHE(
  2082.                   "Das Angument von -b darf nicht negativ sein.",
  2083.                   "The argument of -b must not be negative.") << std::endl;
  2084.                exit(1);
  2085.             }
  2086.             ++iakkumlimit;
  2087.          }
  2088.       }else if(option == "-f"){
  2089.          als_fixeszeichen = true;
  2090.       }else if(option == "-g"){
  2091.          grafikname = argument(argc, argv);
  2092.       }else if(option == "-G"){
  2093.          gewicht = fargument(argc, argv);
  2094.          if(gewicht <= 0.){
  2095.             std::cerr << SPRACHE(
  2096.                "Gewichte m" strUe "ssen positive Zahlen sein.",
  2097.                "Weights must be positive numbers.") << std::endl;
  2098.             exit(1);
  2099.          }
  2100.       }else if(option == "-h"){
  2101.          hilfe();
  2102.       }else if(option == "-H"){
  2103.          handeinsatzlimit = fargument(argc, argv);
  2104.       }else if(option == "-i"){
  2105.          iterationen = iargument(argc, argv);
  2106.       }else if(option == "-k"){
  2107.          schoene_tastatur = false;
  2108.       }else if(option == "-K"){
  2109.          konfigurationsfiles.emplace_back(argument(argc, argv));
  2110.       }else if(option == "-m"){
  2111.          minimum = fargument(argc, argv)/100.;
  2112.          alle_guten = true;
  2113.       }else if(option == "-M"){
  2114.          markup.emplace_back(argument(argc, argv));
  2115.       }else if(option == "-r"){
  2116.          if(referenztastatur.size()){
  2117.             std::cerr << SPRACHE(
  2118.                "Es darf nur ein Belegungsfile angegeben werden.",
  2119.                "You can specify only one layout file.") << std::endl;
  2120.             exit(1);
  2121.          }
  2122.          referenztastatur = argument(argc, argv);
  2123.       }else if(option == "-s"){
  2124.          saatwert = (iargument(argc, argv));
  2125.       }else if(option == "-S"){
  2126.          korpusstatistik = true;
  2127.       }else if(option == "-t"){
  2128.          nthreads = (iargument(argc, argv));
  2129.          if(nthreads < 1){
  2130.             std::cerr << SPRACHE("Die Anzahl Threads muss mindestens 1 sein.",
  2131.                                  "The number of threads must be at least 1.")
  2132.                       << std::endl;
  2133.             exit(1);
  2134.          }
  2135. #ifndef MIT_THREADS
  2136.          std::cerr << SPRACHE(
  2137.             "Die Option -t ist nicht unterst" strUe "tzt, denn der Optimierer "
  2138.             "wurde ohne die Option -DMIT_THREADS " strUe "bersetzt.  ",
  2139.             "The option -t is not supported, as the optimiser was compiled "
  2140.             "without using option -DMIT_THREADS.") << std::endl;
  2141. #endif // !MIT_THREADS
  2142.       }else if(option == "-T"){
  2143.          trennen = true;
  2144.          ist_option = false;
  2145.       }else if(option == "-V"){
  2146.          num_variierte_tasten = iargument(argc, argv);
  2147.       }else if(option == "-w"){
  2148.          wortlisten.emplace_back(argument(argc, argv));
  2149.       }else if(option == "-Z"){
  2150.          zyklen = true;
  2151.       }else{
  2152.          ist_option = false;
  2153.          freie_argumente.push_back(option);
  2154.       }
  2155.  
  2156.       irgendeine_option |= ist_option;
  2157.    }
  2158.  
  2159.    if(freie_argumente.size() || trennen){
  2160.       if(irgendeine_option){
  2161.          std::cerr << SPRACHE("Bei Erstellen von H" strAe "ufigkeitstabellen d"
  2162.                               strUe "rfen keine Optionen angegeben werden.",
  2163.                               "When creating frequency files, you must not "
  2164.                               "specify any options.")
  2165.                    << std::endl;
  2166.          exit(1);
  2167.       }
  2168.       if(trennen){
  2169.          if(freie_argumente.size() != 3){
  2170.             std::cerr << SPRACHE(
  2171.                "Zum Trennen genau drei Argumente angeben: "
  2172.                "Trennmuster, Eingabefile, Ausgabefile",
  2173.                "To hyphenate, provide exactly three arguments: "
  2174.                "hyphenation patterns, input file, output file") << std::endl;
  2175.             exit(1);
  2176.          }
  2177.          markiere_alle_trennstellen(freie_argumente[1], freie_argumente[2],
  2178.                                     freie_argumente[0]);
  2179.          exit(0);
  2180.       }else{
  2181.          erzeuge_ngrammtabellen(freie_argumente);
  2182.          exit(0);
  2183.       }
  2184.    }
  2185.  
  2186.    if(!konfigurationsfiles.size())
  2187.       konfigurationsfiles.push_back("standard.cfg");
  2188.  
  2189.    std::unique_ptr<const Kodierung> kodierung;
  2190.    std::unique_ptr<const Tastatur> tastatur;
  2191.    std::unique_ptr<Konfiguration> konfiguration
  2192.       (new Konfiguration(konfigurationsfiles, tastatur, kodierung));
  2193.    std::unique_ptr<const Aufwandstabelle> a
  2194.       (new Aufwandstabelle(trigramm, *tastatur, *konfiguration));
  2195.    if(aufwandstabelle){
  2196.       a->anzeigen(*kodierung, *konfiguration);
  2197.       exit(0);
  2198.    }
  2199.  
  2200.    if(markup.size()){
  2201.       if(!referenztastatur.size()){
  2202.          std::cerr << SPRACHE(
  2203.             "Mit Option -M muss auch Option -r verwendet werden.",
  2204.             "When using option -M, you must use option -r as well.")
  2205.                    << std::endl;
  2206.          exit(1);
  2207.       }
  2208.       for(auto& i : markup)
  2209.          html_markup(i, *kodierung, *tastatur, referenztastatur);
  2210.       exit(0);
  2211.    }
  2212.  
  2213.    if(!korpusnamen.size()){
  2214.       std::cerr << SPRACHE("Quellen f" strUe "r Buchstaben- und Bi/Trigrammh"
  2215.                            strAe "ufigkeiten m" strUe "ssen angegeben werden.",
  2216.                            "Sources for character and di/trigram frequencies "
  2217.                            "must be specified.") << std::endl;
  2218.       exit(1);
  2219.    }
  2220.  
  2221.    for(auto& i : gewichte) i /= gewichtssumme;
  2222.    std::unique_ptr<const Haeufigkeit> korpus
  2223.       (new Haeufigkeit(*tastatur, *kodierung, korpusnamen, gewichte, trigramme,
  2224.                        referenztastatur.size()&&schoene_tastatur && !trigramm,
  2225.                        a->unbekannt() > 0));
  2226.    if(korpusstatistik) korpus->statistik();
  2227.  
  2228.    std::unique_ptr<Grafik> grafik
  2229.       (grafikname.size()
  2230.        ? new Grafik(grafikname, *tastatur, *kodierung, *korpus, *konfiguration)
  2231.        : nullptr);
  2232.  
  2233.    for(const auto& i : wortlisten){
  2234.       std::unordered_map<std::string, haeufigkeit_t> ht;
  2235.       lies_wortliste(i, ht, *kodierung);
  2236.       einfuegen(wortliste, ht);
  2237.    }
  2238.  
  2239.    const char* vgl[2] = { SPRACHE("Gegen erste: ",  " Compared to first: "),
  2240.                           SPRACHE("Gegen vorige: ", " Compared to previous: ")};
  2241.  
  2242.    const akkumuations_t K = konstanter_aufwand(*korpus, *a);
  2243.    minimum -= K;
  2244.    if(referenztastatur.size()){
  2245.       constexpr bool utf8 = true;
  2246.       belegung_t b2[2];  b2[0][0] = b2[1][0] = ntaste;
  2247.       Eingabestream liste(referenztastatur, utf8);
  2248.       while(liste.echte_neuezeile()){
  2249.          std::u32string bs, uname;
  2250.          if(!liste.hole_wort(bs) || !liste.hole_wort(uname)){
  2251.             std::cerr << SPRACHE("Fehlerhaft formatiertes Belegungsfile ",
  2252.                                  "Incorrectly formatted layout file ")
  2253.                       << referenztastatur << std::endl;
  2254.             liste.fehler();
  2255.          }
  2256.  
  2257.          if(bs.size()){
  2258.             belegung_t b;
  2259.             bool fest[ntaste];
  2260.             string_in_belegung(bs, b, fest, tastatur->nvariabel(), *kodierung);
  2261.             if(zyklen){
  2262.                for(int z = 0; z < 2; ++z){
  2263.                   if(b2[z][0] >= ntaste) continue;
  2264.                   int ndiff = 0;
  2265.                   for(int i = 0; i < tastatur->nvariabel(); ++i)
  2266.                      if(b[i] != b2[z][i]) ++ndiff;
  2267.                   std::cout << vgl[z] << ndiff
  2268.                             << SPRACHE(" Tasten umbelegt, Zyklen:",
  2269.                                        " keys reassigned, cycles:");
  2270.                   schreibe_zyklen(b2[z], b, *kodierung);
  2271.                   std::cout << "\n";
  2272.                }
  2273.                std::cout << "\n";
  2274.             }
  2275.  
  2276.             std::unique_ptr<Haeufigkeit> arbeitskorpus
  2277.                (new Haeufigkeit(*korpus, 0));
  2278. #ifndef PERMUTATIONEN_ALT
  2279.             arbeitskorpus->setze(*korpus, b);
  2280. #endif // !PERMUTATIONEN_ALT
  2281.             const akkumuations_t A =
  2282.                variabler_aufwand(b, *arbeitskorpus, *tastatur, *a);
  2283.  
  2284.             if(num_variierte_tasten > 1){
  2285.                int numfrei = 0;
  2286.                for(int i = 0; i < tastatur->nvariabel(); ++i)
  2287.                   if(!fest[i]) ++numfrei;
  2288.                std::cerr << numfrei << SPRACHE(" freie Tasten, ",
  2289.                                                " variable keys, ")
  2290.                          << std::setprecision(16)
  2291.                          << anzahl_varianten(numfrei, num_variierte_tasten)
  2292.                          << SPRACHE(" Varianten", " variants") << std::endl;
  2293.  
  2294.                if(A < minimum) schreibe_belegung(b, A+K, tastatur->nvariabel(),
  2295.                                                  *kodierung, &uname);
  2296.  
  2297.                erzeuge_variationen(b, *tastatur, *kodierung, *arbeitskorpus, *a,
  2298.                                    A, num_variierte_tasten, minimum, fest);
  2299.             }else{
  2300.                if(schoene_tastatur){
  2301.                   schreibe_belegung(b, *tastatur, *kodierung, *korpus, *a, A+K,
  2302.                                     uname, ngrammakkumlimit, wortliste,
  2303.                                     handeinsatzlimit, als_fixeszeichen);
  2304.                   if(korpus->mit_varianzen() && b2[0][0] < ntaste){
  2305.                      std::cout << SPRACHE(
  2306.                         "Standardabweichung der " "Aufwandsdifferenz zur "
  2307.                         "ersten Belegung: ",
  2308.                         "Standard deviation of the difference of efforts "
  2309.                         "compared to the first layout: ")
  2310.                                << std::setprecision(9)
  2311.                                << 100*std::sqrt(
  2312.                                   aufwandsvarianz(b, &b2[0], *tastatur,
  2313.                                                   *korpus, *a))
  2314.                                << "\n";
  2315.                   }
  2316.                   if(korpus->mit_varianzen()){
  2317.                      std::cout << SPRACHE(
  2318.                         "Standardabweichung des Gesamtaufwands: ",
  2319.                         "Standard deviation of the total effort: ")
  2320.                                << std::setprecision(9)
  2321.                                << 100*std::sqrt(
  2322.                                   aufwandsvarianz(b, nullptr, *tastatur,
  2323.                                                   *korpus, *a))
  2324.                                << "\n";
  2325.                   }
  2326.                   std::cout << std::endl;
  2327.                }else{
  2328.                   schreibe_belegung(b, A+K, tastatur->nvariabel(), *kodierung,
  2329.                                     &uname);
  2330.                }
  2331.  
  2332.                if(grafik) grafik->ausgabe(b);
  2333.  
  2334.             }
  2335.  
  2336.             if(b2[0][0] >= ntaste)
  2337.                for(int i = 0; i < ntaste; ++i) b2[0][i] = b[i];
  2338.             if(zyklen)
  2339.                for(int i = 0; i < ntaste; ++i) b2[1][i] = b[i];
  2340.          }
  2341.       }
  2342.    }else{
  2343.       const auto startzeit = std::chrono::high_resolution_clock::now();
  2344.  
  2345.       const int lebenszeichen =
  2346.          (trigramm ? 1000 : 10000)*(schoene_tastatur ? 1 : 100);
  2347.  
  2348.       std::seed_seq saatgen{saatwert};
  2349.       std::vector<int> saaten(nthreads);
  2350.       saatgen.generate(saaten.begin(), saaten.end());
  2351.       akkumuations_t globalesMinimum = minimum;
  2352. #ifdef MIT_THREADS
  2353.       std::vector<std::thread> threads;
  2354.       for(int i = 1; i < nthreads; ++i){
  2355.          threads.push_back(
  2356.             std::thread(suche_optimum, *tastatur, *kodierung, korpus.get(), *a,
  2357.                         minimum, schoene_tastatur, alle_guten, als_fixeszeichen,
  2358.                         saaten[i], iterationen, std::numeric_limits<int>::max(),
  2359.                         ngrammakkumlimit, wortliste, handeinsatzlimit,
  2360.                         grafik.get(), &globalesMinimum));
  2361.       }
  2362. #endif // MIT_THREADS
  2363.       suche_optimum(*tastatur, *kodierung, korpus.get(), *a, minimum,
  2364.                     schoene_tastatur, alle_guten, als_fixeszeichen, saaten[0],
  2365.                     iterationen, lebenszeichen, ngrammakkumlimit, wortliste,
  2366.                     handeinsatzlimit, grafik.get(), &globalesMinimum);
  2367. #ifdef MIT_THREADS
  2368.       for(auto& t : threads) t.join();
  2369. #endif // MIT_THREADS
  2370.  
  2371.       const auto endzeit = std::chrono::high_resolution_clock::now();
  2372.       const std::chrono::nanoseconds zeitdifferenz = endzeit-startzeit;
  2373.       std::cerr << SPRACHE("Laufzeit: ", "Run time: ")
  2374.                 << std::fixed << std::setw(9)
  2375.                 << std::setprecision(9) << 1e-9*zeitdifferenz.count()
  2376.                 << SPRACHE(" Sekunden", " seconds") << std::endl;
  2377.    }
  2378.  
  2379.    return 0;
  2380. }
  2381.  
  2382. //--------------- src/berechnungen.cc ---------------
  2383. //#include "berechnungen.hh"
  2384.  
  2385. //#include "Aufwandstabelle.hh"
  2386. //#include "Grafik.hh"
  2387. //#include "Haeufigkeit.hh"
  2388. //#include "Kodierung.hh"
  2389. //#include "Tastatur.hh"
  2390. //#include "schreibe_belegung.hh"
  2391. //#include "utfhilfe.hh"
  2392. #include <cstring>
  2393. #include <iostream>
  2394. #ifdef MIT_THREADS
  2395. #include <mutex>
  2396. #endif // MIT_THREADS
  2397. #include <random>
  2398.  
  2399. namespace {
  2400.  
  2401. #ifdef PERMUTATIONEN_ALT
  2402. #define BELEGUNG(z, b, p) const int z = b[p]
  2403. #else
  2404. #define BELEGUNG(z, b, p) const int& z = p
  2405. #endif
  2406.  
  2407. // Der Zufallszahlengenerator kann Einfluss auf die Geschwindigkeit haben.
  2408. using rngengine_t = std::mt19937;
  2409. using uniform_t = std::uniform_int_distribution<char>;
  2410.  
  2411. #ifdef MIT_THREADS
  2412. std::mutex ausgabemutex;
  2413. #endif // MIT_THREADS
  2414.  
  2415. using fingerbelastung_t = akkumuations_t[nmaxkorpus][nfinger+1];
  2416.  
  2417. inline akkumuations_t
  2418. berechne_vorliebensumme(const belegung_t b, const Aufwandstabelle& a){
  2419.    if(!a.hat_vorlieben()) return 0;
  2420.    akkumuations_t vs = 0;
  2421.    for(int p = 0; p < ntaste; ++p) vs += a.vorliebe(b[p], p);
  2422.    return vs;
  2423. }
  2424.  
  2425. inline void
  2426. berechne_fingerbelastung(const belegung_t b,
  2427.                          const Haeufigkeit& h,
  2428.                          const Tastatur& tastatur,
  2429.                          fingerbelastung_t fh)
  2430. {
  2431.    for(int k = 0; k < h.num_korpus(); ++k){
  2432.       for(auto& i : fh[k]) i = 0;
  2433.       for(int p1 = 0; p1 < ntaste; ++p1){
  2434.          BELEGUNG(z1, b, p1);
  2435.          const int fi = tastatur.finger_index(p1);
  2436.          for(int e1 = 0; e1 < nebene; ++e1) fh[k][fi] += h(k,z1,e1);
  2437.          const int sf = tastatur.shift_finger_index(p1);
  2438.          fh[k][sf] += h(k,z1,1);
  2439.       }
  2440.    }
  2441. }
  2442.  
  2443. inline void zufallsbelegung(belegung_t b, int nv,
  2444.                             rngengine_t& rng, uniform_t& d){
  2445.    for(int j = 0; j < ntaste; ++j) b[j] = j;
  2446.    for(int j = nv-1; j ; --j){
  2447.       const int pos = d(rng, uniform_t::param_type(0, j));
  2448.       std::swap(b[j], b[pos]);
  2449.    }
  2450. }
  2451.  
  2452. // Berechne Aufwanderhöhung dadurch, dass man die Belegung der Plätze p1 und p2
  2453. // vertauscht.
  2454. akkumuations_t
  2455. diff_aufwand(int p1, int p2, const belegung_t b,
  2456.              const Tastatur& tastatur,
  2457.              const Haeufigkeit& h,
  2458.              const fingerbelastung_t fh_alt,
  2459. #ifdef EXPERIMENTELL
  2460.              const akkumuations_t vs_alt,
  2461. #endif // EXPERIMENTELL
  2462.              const Aufwandstabelle& a)
  2463. {
  2464.    assert(p1 < tastatur.nvariabel() && p2 < tastatur.nvariabel());
  2465.    BELEGUNG(z1, b, p1);  BELEGUNG(z2, b, p2);
  2466.    akkumuations_t summe = 0;
  2467.  
  2468.    for(int e1 = 0; e1 < nebene; ++e1){
  2469.       const akkumuations_t dh1 = (h(z2,e1)-h(z1,e1));
  2470.       const akkumuations_t da1 = a(p1,e1)-a(p2,e1);
  2471.       summe += da1*dh1;
  2472.  
  2473.       for(int e2 = 0; e2 < nebene2; ++e2){
  2474.          const akkumuations_t dh1 = h(z1,e1,z1,e2)-h(z1,e1,z2,e2);
  2475.          const akkumuations_t dh2 = h(z2,e1,z2,e2)-h(z2,e1,z1,e2);
  2476.          const akkumuations_t da1 = a(p1,e1,p1,e2)-a(p1,e1,p2,e2);
  2477.          const akkumuations_t da2 = a(p2,e1,p2,e2)-a(p2,e1,p1,e2);
  2478.          summe += (da1+da2)*(dh1+dh2);
  2479.       }
  2480.    }
  2481.    for(int p = 0; p < ntaste; ++p){
  2482.       BELEGUNG(z, b, p);
  2483.       for(int e1 = 0; e1 < nebene; ++e1){
  2484.          for(int e2 = 0; e2 < nebene2; ++e2){
  2485.             const akkumuations_t dh2_1 = h(z2,e1,z,e2)-h(z1,e1,z,e2);
  2486.             const akkumuations_t da2_1 = a(p1,e1,p,e2)-a(p2,e1,p,e2);
  2487.             const akkumuations_t dh2_2 = h(z,e1,z2,e2)-h(z,e1,z1,e2);
  2488.             const akkumuations_t da2_2 = a(p,e1,p1,e2)-a(p,e1,p2,e2);
  2489.             summe += da2_1*dh2_1+da2_2*dh2_2;
  2490.          }
  2491.       }
  2492.    }
  2493.  
  2494.    if(a.hat_vorlieben()){
  2495.       const int z1 = b[p1], z2 = b[p2];
  2496.       const akkumuations_t diff_vs =
  2497.          (a.vorliebe(z2,p1)+a.vorliebe(z1,p2))
  2498.          -(a.vorliebe(z1,p1)+a.vorliebe(z2,p2));
  2499. #ifdef EXPERIMENTELL
  2500.       const akkumuations_t vs_neu = vs_alt+diff_vs;
  2501.       summe += a.knick(vs_neu)-a.knick(vs_alt);
  2502. #else
  2503.       summe += diff_vs;
  2504. #endif // !EXPERIMENTELL
  2505.    }
  2506.  
  2507.    if(a.hat_aehnlichkeit()){
  2508.       const int z1v = b[p1], z2v = b[p2];
  2509.       for(int p = 0; p < ntaste; ++p){
  2510.          if(p == p1 || p == p2) continue;
  2511.          const int z = b[p];
  2512.          const akkumuations_t dh2_1 =
  2513.             a.aehnlichkeit(z2v,z)-a.aehnlichkeit(z1v,z);
  2514.          const akkumuations_t da2_1 =
  2515.             a.verwechslungspotenzial(p1,p)-a.verwechslungspotenzial(p2,p);
  2516.          summe += 2.*da2_1*dh2_1;
  2517.       }
  2518.    }
  2519.  
  2520.    if(h.mit_trigrammen()){
  2521.       for(int pi = 0; pi < ntaste; ++pi){
  2522.          if(pi == p1 || pi == p2) continue;
  2523.          BELEGUNG(zi, b, pi);
  2524.          for(int pj = 0; pj < ntaste; ++pj){
  2525.             if(pj == p1 || pj == p2) continue;
  2526.             BELEGUNG(zj, b, pj);
  2527.             for(int e = 0; e < nebene; ++e){
  2528.                const akkumuations_t dh3_1 = h.tri(zi,zj,z2,e)-h.tri(zi,zj,z1,e);
  2529.                const akkumuations_t da3_1 = a.tri(pi,pj,p1,e)-a.tri(pi,pj,p2,e);
  2530.                summe += da3_1*dh3_1;
  2531.                const akkumuations_t dh3_2 = h.tri(zi,z2,zj,e)-h.tri(zi,z1,zj,e);
  2532.                const akkumuations_t da3_2 = a.tri(pi,p1,pj,e)-a.tri(pi,p2,pj,e);
  2533.                summe += da3_2*dh3_2;
  2534.                const akkumuations_t dh3_3 = h.tri(z2,zi,zj,e)-h.tri(z1,zi,zj,e);
  2535.                const akkumuations_t da3_3 = a.tri(p1,pi,pj,e)-a.tri(p2,pi,pj,e);
  2536.                summe += da3_3*dh3_3;
  2537.             }
  2538.          }
  2539.       }
  2540.  
  2541.       for(int pi = 0; pi < ntaste; ++pi){
  2542.          if(pi == p1 || pi == p2) continue;
  2543.          BELEGUNG(zi, b, pi);
  2544.          for(int e = 0; e < nebene; ++e){
  2545.             const akkumuations_t dh3_1a = h.tri(zi,z2,z2,e)-h.tri(zi,z1,z1,e);
  2546.             const akkumuations_t da3_1a = a.tri(pi,p1,p1,e)-a.tri(pi,p2,p2,e);
  2547.             summe += da3_1a*dh3_1a;
  2548.             const akkumuations_t dh3_1b = h.tri(zi,z1,z2,e)-h.tri(zi,z2,z1,e);
  2549.             const akkumuations_t da3_1b = a.tri(pi,p2,p1,e)-a.tri(pi,p1,p2,e);
  2550.             summe += da3_1b*dh3_1b;
  2551.             const akkumuations_t dh3_2a = h.tri(z2,zi,z2,e)-h.tri(z1,zi,z1,e);
  2552.             const akkumuations_t da3_2a = a.tri(p1,pi,p1,e)-a.tri(p2,pi,p2,e);
  2553.             summe += da3_2a*dh3_2a;
  2554.             const akkumuations_t dh3_2b = h.tri(z1,zi,z2,e)-h.tri(z2,zi,z1,e);
  2555.             const akkumuations_t da3_2b = a.tri(p2,pi,p1,e)-a.tri(p1,pi,p2,e);
  2556.             summe += da3_2b*dh3_2b;
  2557.             const akkumuations_t dh3_3a = h.tri(z2,z2,zi,e)-h.tri(z1,z1,zi,e);
  2558.             const akkumuations_t da3_3a = a.tri(p1,p1,pi,e)-a.tri(p2,p2,pi,e);
  2559.             summe += da3_3a*dh3_3a;
  2560.             const akkumuations_t dh3_3b = h.tri(z1,z2,zi,e)-h.tri(z2,z1,zi,e);
  2561.             const akkumuations_t da3_3b = a.tri(p2,p1,pi,e)-a.tri(p1,p2,pi,e);
  2562.             summe += da3_3b*dh3_3b;
  2563.          }
  2564.       }
  2565.       for(int e = 0; e < nebene; ++e){
  2566.          const akkumuations_t dh3_a = h.tri(z2,z2,z2,e)-h.tri(z1,z1,z1,e);
  2567.          const akkumuations_t da3_a = a.tri(p1,p1,p1,e)-a.tri(p2,p2,p2,e);
  2568.          summe += da3_a*dh3_a;
  2569.          const akkumuations_t dh3_b = h.tri(z2,z2,z1,e)-h.tri(z1,z1,z2,e);
  2570.          const akkumuations_t da3_b = a.tri(p1,p1,p2,e)-a.tri(p2,p2,p1,e);
  2571.          summe += da3_b*dh3_b;
  2572.          const akkumuations_t dh3_c = h.tri(z2,z1,z2,e)-h.tri(z1,z2,z1,e);
  2573.          const akkumuations_t da3_c = a.tri(p1,p2,p1,e)-a.tri(p2,p1,p2,e);
  2574.          summe += da3_c*dh3_c;
  2575.          const akkumuations_t dh3_d = h.tri(z1,z2,z2,e)-h.tri(z2,z1,z1,e);
  2576.          const akkumuations_t da3_d = a.tri(p2,p1,p1,e)-a.tri(p1,p2,p2,e);
  2577.          summe += da3_d*dh3_d;
  2578.       }
  2579.    }
  2580.  
  2581.    const int f1 = tastatur.finger_index(p1), f2 = tastatur.finger_index(p2);
  2582.    if(f1 == f2) return summe;
  2583.  
  2584.    const int sf1 = tastatur.shift_finger_index(p1);
  2585.    const int sf2 = tastatur.shift_finger_index(p2);
  2586.    akkumuations_t fsgesamt = 0;
  2587.    for(int k = 0; k < h.num_korpus(); ++k){
  2588.       akkumuations_t fh_neu[nfinger+1];
  2589.       for(int i = 0; i < nfinger+1; ++i) fh_neu[i] = fh_alt[k][i];
  2590.       for(int e1 = 0; e1 < nebene; ++e1){
  2591.          const akkumuations_t dh = h(k,z1,e1)-h(k,z2,e1);
  2592.          fh_neu[f1] -= dh;
  2593.          fh_neu[f2] += dh;
  2594.       }
  2595.       if(sf1 != sf2){
  2596.          const akkumuations_t dh = h(k,z1,1)-h(k,z2,1);
  2597.          fh_neu[sf1] -= dh;
  2598.          fh_neu[sf2] += dh;
  2599.       }
  2600.  
  2601.       akkumuations_t fs = 0;
  2602.       for(int i = 0; i < nfinger; ++i){
  2603.          const akkumuations_t diff = (fh_neu[i]-fh_alt[k][i]);
  2604.          if(diff){
  2605.             const akkumuations_t d_neu = a.fingerabweichung(i, fh_neu[i]);
  2606.             const akkumuations_t d_alt = a.fingerabweichung(i, fh_alt[k][i]);
  2607.             if(d_neu > 0){
  2608.                if(d_alt > 0){
  2609.                   const akkumuations_t plus =
  2610.                      a.fingerabweichung(i, 0.5*(fh_neu[i]+fh_alt[k][i]));
  2611.                   fs += a.mult_finger(i)*plus*diff;
  2612.                }else fs += 0.5*a.mult_finger(i)*d_neu*d_neu;
  2613.             }else if(d_alt > 0)
  2614.                fs -= 0.5*a.mult_finger(i)*d_alt*d_alt;
  2615.          }
  2616.       }
  2617.       fsgesamt += h.gewicht(k)*fs;
  2618.    }
  2619.  
  2620.    return summe+2*fsgesamt;
  2621. }
  2622.  
  2623. // Berechne variablen Gesamtaufwand einer Belegung
  2624. akkumuations_t
  2625. variabler_aufwand(const belegung_t b, const Haeufigkeit& h,
  2626.         const fingerbelastung_t fh,
  2627.         const Aufwandstabelle& a)
  2628. {
  2629.    akkumuations_t summe = 0;
  2630.  
  2631.    for(int p1 = 0; p1 < ntaste; ++p1){
  2632.       BELEGUNG(z1, b, p1);
  2633.       for(int e1 = 0; e1 < nebene; ++e1){
  2634.          const akkumuations_t a1 = a(p1,e1), h1 = h(z1,e1);
  2635.          summe += a1*h1;
  2636.       }
  2637.    }
  2638.  
  2639.    for(int p1 = 0; p1 < ntaste; ++p1){
  2640.       BELEGUNG(z1, b, p1);
  2641.       for(int p2 = 0; p2 < ntaste; ++p2){
  2642.          BELEGUNG(z2, b, p2);
  2643.          for(int e1 = 0; e1 < nebene; ++e1){
  2644.             for(int e2 = 0; e2 < nebene2; ++e2){
  2645.                const akkumuations_t a2 = a(p1,e1,p2,e2), h2 = h(z1,e1,z2,e2);
  2646.                summe += a2*h2;
  2647.             }
  2648.          }
  2649.       }
  2650.    }
  2651.  
  2652.    if(a.hat_vorlieben()) summe += a.knick(berechne_vorliebensumme(b, a));
  2653.  
  2654.    if(a.hat_aehnlichkeit()){
  2655.       for(int p1 = 0; p1 < ntaste; ++p1){
  2656.          const int z1 = b[p1];
  2657.          for(int p2 = 0; p2 < p1; ++p2){
  2658.             const int z2 = b[p2];
  2659.             const akkumuations_t a2 = a.verwechslungspotenzial(p1,p2);
  2660.             const akkumuations_t h2 = a.aehnlichkeit(z1,z2);
  2661.             // Faktor 2, weil wir in der Doppelschleife Symmetrie ausnutzen.
  2662.             summe += 2.*a2*h2;
  2663.          }
  2664.       }
  2665.    }
  2666.  
  2667.    if(h.mit_trigrammen()){
  2668.       for(int p1 = 0; p1 < ntaste; ++p1){
  2669.          BELEGUNG(z1, b, p1);
  2670.          for(int p2 = 0; p2 < ntaste; ++p2){
  2671.             BELEGUNG(z2, b, p2);
  2672.             for(int p3 = 0; p3 < ntaste; ++p3){
  2673.                BELEGUNG(z3, b, p3);
  2674.                for(int e3 = 0; e3 < nebene; ++e3){
  2675.                   const akkumuations_t a3 = a.tri(p1,p2,p3,e3);
  2676.                   const akkumuations_t h3 = h.tri(z1,z2,z3,e3);
  2677.                   summe += a3*h3;
  2678.                }
  2679.             }
  2680.          }
  2681.       }
  2682.    }
  2683.  
  2684.    akkumuations_t fsgesamt = 0;
  2685.    for(int k = 0; k < h.num_korpus(); ++k){
  2686.       akkumuations_t fs = 0;
  2687.       for(int i = 0; i < nfinger; ++i){
  2688.          if(a.mult_finger(i) == 0) continue;
  2689.          const akkumuations_t diff = a.fingerabweichung(i, fh[k][i]);
  2690.          if(diff > 0) fs += a.mult_finger(i)*diff*diff;
  2691.       }
  2692.       fsgesamt += h.gewicht(k)*fs;
  2693.    }
  2694.    return summe+fsgesamt;
  2695. }
  2696.  
  2697. inline akkumuations_t
  2698. zufaelliger_abstieg(belegung_t b, const Tastatur& tastatur, Haeufigkeit& h,
  2699.                     const Aufwandstabelle& a, rngengine_t& rng, uniform_t& d)
  2700. {
  2701.    bool reduktion;
  2702.    belegung_t p;
  2703.    int probiert[ntaste][ntaste];
  2704.    int variante = 1;
  2705.    std::memset(probiert, 0, sizeof(probiert));
  2706.    fingerbelastung_t fh;
  2707.    berechne_fingerbelastung(b, h, tastatur, fh);
  2708. #ifndef NDEBUG
  2709.    akkumuations_t alt = variabler_aufwand(b, h, fh, a);
  2710. #endif // !NDEBUG
  2711. #ifdef EXPERIMENTELL
  2712.    akkumuations_t vs = berechne_vorliebensumme(b, a);
  2713. #endif // EXPERIMENTELL
  2714.    do{
  2715.       reduktion = false;
  2716.       zufallsbelegung(p, tastatur.nvariabel(), rng, d);
  2717.       for(int i = 0; i < tastatur.nvariabel()-1; ++i){
  2718.          const int pi = p[i];
  2719.          for(int j = i+1;  j < tastatur.nvariabel(); ++j){
  2720.             const int pj = p[j];
  2721.             if(probiert[pi][pj] == variante) continue;
  2722.             const akkumuations_t diff =
  2723. #ifdef EXPERIMENTELL
  2724.                diff_aufwand(pi, pj, b, tastatur, h, fh, vs, a);
  2725. #else
  2726.                diff_aufwand(pi, pj, b, tastatur, h, fh, a);
  2727. #endif // !EXPERIMENTELL
  2728.                if(diff < 0){
  2729.                   std::swap(b[pi], b[pj]);
  2730. #ifndef PERMUTATIONEN_ALT
  2731.                   h.swap(pi, pj);
  2732. #endif // !PERMUTATIONEN_ALT
  2733. #ifdef EXPERIMENTELL
  2734.                   vs = berechne_vorliebensumme(b, a);
  2735. #endif // EXPERIMENTELL
  2736.                   berechne_fingerbelastung(b, h, tastatur, fh);
  2737.                   reduktion = true;
  2738.                   ++variante;
  2739. #ifndef NDEBUG
  2740.                   const akkumuations_t neu = variabler_aufwand(b, h, fh, a);
  2741.                   if(std::abs((neu-alt)-diff) >= 1e-5*std::abs(neu+alt)){
  2742.                      std::cerr << SPRACHE("Relativer Fehler ","Relative error ")
  2743.                                << std::abs((neu-alt)-diff)/std::abs(neu+alt)
  2744.                                << std::endl;
  2745.                      exit(1);
  2746.                   }
  2747.                   alt = neu;
  2748. #endif // !NDEBUG
  2749.                }
  2750.                probiert[pi][pj] = probiert[pj][pi] = variante;
  2751.          }
  2752.       }
  2753.    }while(reduktion);
  2754.  
  2755.    return variabler_aufwand(b, h, fh, a);
  2756. }
  2757.  
  2758. struct zyklen_t {
  2759.    int pos[ntaste][ntaste];
  2760.    int idx[ntaste+1];
  2761.    int teilweise;
  2762. };
  2763.  
  2764.  
  2765.  
  2766.  
  2767. void variiere_tasten(belegung_t b, const Tastatur& tastatur,
  2768.                      const Kodierung& kodierung, Haeufigkeit& h,
  2769.                      const fingerbelastung_t& fh,
  2770. #ifdef EXPERIMENTELL
  2771.                      const akkumuations_t vs,
  2772. #endif // EXPERIMENTELL
  2773.                      const Aufwandstabelle& a, akkumuations_t A, int tiefe,
  2774.                      zyklen_t& zyklen, int ab, akkumuations_t limit,
  2775.                      const bool* fest)
  2776. {
  2777.    --tiefe;
  2778.    for(int p2 = ab; p2 < tastatur.nvariabel(); ++p2){
  2779.       if(fest[p2]) continue;
  2780.       int z = 0;
  2781.       for(; zyklen.idx[z]; ++z){
  2782.          const int zlaenge = zyklen.idx[z];
  2783.          if(zlaenge == 1) --zyklen.teilweise;
  2784.          zyklen.pos[z][zlaenge] = p2;
  2785.          if(tiefe) ++zyklen.idx[z];
  2786.  
  2787.          for(int j = 0; j < zlaenge; ++j){
  2788.             const int p1 = zyklen.pos[z][j];
  2789.  
  2790.             const akkumuations_t Aneu =
  2791. #ifdef EXPERIMENTELL
  2792.                A+diff_aufwand(p1, p2, b, tastatur, h, fh, vs, a);
  2793. #else
  2794.                A+diff_aufwand(p1, p2, b, tastatur, h, fh, a);
  2795. #endif // !EXPERIMENTELL
  2796.  
  2797.                std::swap(b[p1], b[p2]);
  2798.  
  2799.                if(zyklen.teilweise == 0 && Aneu < limit)
  2800.                   schreibe_belegung(b, Aneu+konstanter_aufwand(h, a),
  2801.                                     tastatur.nvariabel(), kodierung,0);
  2802.  
  2803.                if(tiefe && tiefe >= zyklen.teilweise &&
  2804.                   p2+1 < tastatur.nvariabel()){
  2805. #ifndef PERMUTATIONEN_ALT
  2806.                   h.swap(p1, p2);
  2807. #endif // !PERMUTATIONEN_ALT
  2808.                   fingerbelastung_t fh2;
  2809.                   berechne_fingerbelastung(b, h, tastatur, fh2);
  2810. #ifdef EXPERIMENTELL
  2811.                   const akkumuations_t vs2 = berechne_vorliebensumme(b, a);
  2812.                   variiere_tasten(b, tastatur, kodierung, h, fh2, vs2, a, Aneu,
  2813.                                   tiefe, zyklen, p2+1, limit, fest);
  2814. #else
  2815.                   variiere_tasten(b, tastatur, kodierung, h, fh2, a, Aneu,
  2816.                                   tiefe, zyklen, p2+1, limit, fest);
  2817. #endif // !EXPERIMENTELL
  2818.  
  2819. #ifndef PERMUTATIONEN_ALT
  2820.                   h.swap(p1, p2);
  2821. #endif // !PERMUTATIONEN_ALT
  2822.                }
  2823.                std::swap(b[p1], b[p2]);
  2824.          }
  2825.          zyklen.idx[z] = zlaenge;
  2826.          if(zlaenge == 1) ++zyklen.teilweise;
  2827.       }
  2828.  
  2829.       if(tiefe > zyklen.teilweise && p2+1 < tastatur.nvariabel()){
  2830.          zyklen.idx[z] = 1;
  2831.          zyklen.pos[z][0] = p2;
  2832.          ++zyklen.teilweise;
  2833. #ifdef EXPERIMENTELL
  2834.          variiere_tasten(b, tastatur, kodierung, h, fh, vs, a, A,
  2835.                          tiefe, zyklen, p2+1, limit, fest);
  2836. #else // !EXPERIMENTELL
  2837.          variiere_tasten(b, tastatur, kodierung, h, fh, a, A,
  2838.                          tiefe, zyklen, p2+1, limit, fest);
  2839. #endif // !EXPERIMENTELL
  2840.          zyklen.idx[z] = 0;
  2841.          --zyklen.teilweise;
  2842.       }
  2843.    }
  2844. }
  2845.  
  2846. } // Ende des namenlosen Namensraums.
  2847.  
  2848.  
  2849. void
  2850. suche_optimum(const Tastatur& tastatur, const Kodierung& kodierung,
  2851.               const Haeufigkeit* korpus, const Aufwandstabelle& a,
  2852.               akkumuations_t minimum, bool schoene_tastatur, bool alle_guten,
  2853.               bool als_fixeszeichen, int saatwert, int iterationen,
  2854.               int lebenszeichen, const double ngrammakkumlimit[3],
  2855.               const std::unordered_map<std::string, haeufigkeit_t>& wortliste,
  2856.               akkumuations_t handeinsatzlimit, Grafik* grafik,
  2857.               akkumuations_t* globalesMinimum)
  2858. {
  2859.    // Es ist nicht klar, dass diese beiden Kopien die Geschwindigkeit
  2860.    // verbessern.  Auf meiner Maschine tun sie es.
  2861.    std::unique_ptr<const Haeufigkeit> h(new Haeufigkeit(*korpus, 0));
  2862.    std::unique_ptr<const Aufwandstabelle> aufwand(new Aufwandstabelle(a));
  2863.  
  2864.    rngengine_t rng(saatwert);
  2865.    uniform_t uniform;
  2866.    std::unique_ptr<Haeufigkeit> arbeitskorpus(new Haeufigkeit(*h, 0));
  2867.  
  2868.    for(int j = 0; j++ < iterationen;){
  2869.       belegung_t b; zufallsbelegung(b, tastatur.nvariabel(), rng, uniform);
  2870.  
  2871. #ifndef PERMUTATIONEN_ALT
  2872.       arbeitskorpus->setze(*h, b);
  2873. #endif // !PERMUTATIONEN_ALT
  2874.       const akkumuations_t A = zufaelliger_abstieg(b, tastatur, *arbeitskorpus,
  2875.                                                    *aufwand, rng, uniform);
  2876.       if(A < minimum){
  2877. #ifdef MIT_THREADS
  2878.          std::lock_guard<std::mutex> sperre(ausgabemutex);
  2879. #endif // MIT_THREADS
  2880.          if(A < *globalesMinimum){
  2881.             if(!alle_guten) minimum = *globalesMinimum = A;
  2882.             const akkumuations_t K =
  2883.                konstanter_aufwand(*arbeitskorpus, *aufwand);
  2884.             if(schoene_tastatur)
  2885.                schreibe_belegung(b, tastatur, kodierung, *h, *aufwand, A+K,
  2886.                                  zahl_in_utf32(j), ngrammakkumlimit, wortliste,
  2887.                                  handeinsatzlimit, als_fixeszeichen);
  2888.             else
  2889.                schreibe_belegung(b, A+K, tastatur.nvariabel(), kodierung, 0);
  2890.  
  2891.             if(grafik) grafik->ausgabe(b);
  2892.          }else if(!alle_guten) minimum = *globalesMinimum;
  2893.       }
  2894.  
  2895.       if(j % lebenszeichen == 0)
  2896.          std::cerr << SPRACHE("Thread 0: Lokale Optimierung ",
  2897.                               "Thread 0: Local optimisation ")<< j << std::endl;
  2898.    }
  2899. }
  2900.  
  2901. double anzahl_varianten(int N, int n)
  2902. {
  2903.    double C = N;
  2904.    double sf2 = 1, sf1 = 0;
  2905.    double summe = 1;
  2906.    for(int i = 2; i <= n; ++i){
  2907.       C = (C*(N-(i-1)))/i;
  2908.       const double sf = (i-1)*(sf1+sf2);
  2909.       sf2 = sf1; sf1 = sf;
  2910.       summe += C*sf;
  2911.    }
  2912.    return summe;
  2913. }
  2914.  
  2915. void
  2916. erzeuge_variationen(const belegung_t ausgangsbelegung,
  2917.                     const Tastatur& tastatur, const Kodierung& kodierung,
  2918.                     Haeufigkeit& h, const Aufwandstabelle& a,
  2919.                     akkumuations_t A, int tiefe,
  2920.                     akkumuations_t limit, const bool* fest)
  2921. {
  2922.    belegung_t b;
  2923.    for(int i = 0; i < ntaste; ++i) b[i] = ausgangsbelegung[i];
  2924.    fingerbelastung_t fh;
  2925.    berechne_fingerbelastung(b, h, tastatur, fh);
  2926.    zyklen_t zyklen;
  2927.    for(int i = 0; i < tastatur.nvariabel()+1; ++i) zyklen.idx[i] = 0;
  2928.    zyklen.teilweise = 0;
  2929. #ifdef EXPERIMENTELL
  2930.    const akkumuations_t vs = berechne_vorliebensumme(b, a);
  2931.    variiere_tasten(b, tastatur, kodierung, h, fh, vs, a, A,
  2932.                    tiefe, zyklen, 0, limit, fest);
  2933. #else
  2934.    variiere_tasten(b, tastatur, kodierung, h, fh, a, A,
  2935.                    tiefe, zyklen, 0, limit, fest);
  2936. #endif // !EXPERIMENTELL
  2937. }
  2938.  
  2939. akkumuations_t
  2940. aufwandsvarianz(const belegung_t b1, const belegung_t* b2,
  2941.                 const Tastatur& tastatur,
  2942.                 const Haeufigkeit& h,
  2943.                 const Aufwandstabelle& a)
  2944. {
  2945.    assert(h.mit_varianzen());
  2946.    akkumuations_t summe = 0;
  2947.    // Umkehrung der Belegung(en)
  2948.    belegung_t ib1, ib2;
  2949.    for(int i = 0; i < ntaste; ++i) ib1[b1[i]] = ib2[b1[i]] = i;
  2950.    if(b2) for(int i = 0; i < ntaste; ++i) ib2[(*b2)[i]] = i;
  2951.  
  2952.    // Ableitung der (Differenz der) Aufwände für das Überschreiten der
  2953.    // Zielhäufigkeiten nach den Anschlagsshäufigkeiten, tabelliert nach Zeichen.
  2954.    akkumuations_t fhdiff[nmaxkorpus][ntaste][nebene];
  2955.    for(int k = 0; k < h.num_korpus(); ++k){
  2956.       akkumuations_t fh[2][nfinger+1];
  2957.       memset(fh, 0, sizeof(fh));
  2958.       for(int j = 0; j < (b2 ? 2 : 1); ++j){
  2959.          for(int z = 0; z < ntaste; ++z){
  2960.             const int p = j ? ib2[z] : ib1[z];
  2961.             const int fi = tastatur.finger_index(p);
  2962.             const int sf = tastatur.shift_finger_index(p);
  2963.             for(int e = 0; e < nebene; ++e) fh[j][fi] += h(k,z,e);
  2964.             fh[j][sf] += h(k,z,1);
  2965.          }
  2966.          for(int i = 0; i < nfinger; ++i){
  2967.             const akkumuations_t diff = a.fingerabweichung(i, fh[j][i]);
  2968.             fh[j][i] = diff > 0 ? 2.*h.gewicht(k)*a.mult_finger(i)*diff : 0;
  2969.          }
  2970.          fh[j][nfinger] = 0;
  2971.       }
  2972.       for(int z = 0; z < ntaste; ++z){
  2973.          const int p1 = ib1[z], p2 = ib2[z];
  2974.          const int f1 = tastatur.finger_index(p1);
  2975.          const int s1 = tastatur.shift_finger_index(p1);
  2976.          const int f2 = tastatur.finger_index(p2);
  2977.          const int s2 = tastatur.shift_finger_index(p2);
  2978.          for(int e = 0; e < nebene; ++e) fhdiff[k][z][e] = fh[0][f1]-fh[1][f2];
  2979.          fhdiff[k][z][1] += fh[0][s1]-fh[1][s2];
  2980.       }
  2981.    }
  2982.  
  2983.    akkumuations_t a1diff[Haeufigkeit::groesse1];
  2984.    for(int z1 = 0; z1 < ntaste; ++z1){
  2985.       for(int e = 0; e < nebene; ++e)
  2986.          a1diff[nebene*z1+e] = b2 ? a(ib1[z1],e)-a(ib2[z1],e) : a(ib1[z1],e);
  2987.    }
  2988.  
  2989.    for(int z1 = 0; z1 < ntaste; ++z1){
  2990.       const int p1_1 = ib1[z1], p1_2 = ib2[z1];
  2991.       for(int e1 = 0; e1 < nebene; ++e1){
  2992.          const int o1 = nebene*z1+e1;
  2993.          for(int z2 = 0; z2 < ntaste; ++z2){
  2994.             int p2_1 = ib1[z2], p2_2 = ib2[z2];
  2995.             for(int e2 = 0; e2 < nebene; ++e2){
  2996.                const int o2 = nebene*z2+e2;
  2997.                const haeufigkeit_t h11 = h.ein_ein(o1, o2);
  2998.                summe += a1diff[o1]*a1diff[o2]*h11;
  2999.  
  3000.                for(int k1 = 0; k1 < h.num_korpus(); ++k1){
  3001.                   const haeufigkeit_t h11k = h.ein_ein_k(k1, o1, o2);
  3002.                   summe += a1diff[o1]*fhdiff[k1][z2][e2]*h11k;
  3003.                   const haeufigkeit_t h11kk = h.ein_k_ein_k(k1, o1, o2);
  3004.                   summe += fhdiff[k1][z1][e1]*fhdiff[k1][z2][e2]*h11kk;
  3005.                }
  3006.  
  3007.                if(e2 >= nebene2) continue;
  3008.  
  3009.                const int o12 = Haeufigkeit::index_bi_flach(o1, o2);
  3010.                const akkumuations_t a12_1 =      a(p1_1, e1, p2_1, e2);
  3011.                const akkumuations_t a12_2 = b2 ? a(p1_2, e1, p2_2, e2) : 0;
  3012.                for(int z3 = 0; z3 < ntaste; ++z3){
  3013.                   int p3_1 = ib1[z3], p3_2 = ib2[z3];
  3014.                   for(int e3 = 0; e3 < nebene; ++e3){
  3015.                      const int o3 = nebene*z3+e3;
  3016.                      const haeufigkeit_t h21 = h.ein_bi(o3, o12);
  3017.                      summe += (a12_1-a12_2)*a1diff[o3]*h21;
  3018.  
  3019.                      for(int k2 = 0; k2 < h.num_korpus(); ++k2){
  3020.                         const haeufigkeit_t h21k = h.ein_k_bi(k2, o3, o12);
  3021.                         summe += (a12_1-a12_2)*fhdiff[k2][z3][e3]*h21k;
  3022.                      }
  3023.  
  3024.                      for(int z4 = 0; z4 < ntaste; ++z4){
  3025.                         int p4_1 = ib1[z4], p4_2 = ib2[z4];
  3026.                         for(int e4 = 0; e4 < nebene2; ++e4){
  3027.                            const int o4 = nebene*z4+e4;
  3028.                            const int o34 = Haeufigkeit::index_bi_flach(o3, o4);
  3029.                            const akkumuations_t a34_1 = a(p3_1, e3, p4_1, e4);
  3030.                            const akkumuations_t a34_2 =
  3031.                               b2 ? a(p3_2, e3, p4_2, e4 ) : 0;
  3032.                            const haeufigkeit_t h22 = h.bi_bi(o12, o34);
  3033.                            summe += (a12_1-a12_2)*(a34_1-a34_2)*h22;
  3034.                         }
  3035.                      }
  3036.                   }
  3037.                }
  3038.             }
  3039.          }
  3040.       }
  3041.    }
  3042.  
  3043.    return summe;
  3044. }
  3045.  
  3046. akkumuations_t
  3047. konstanter_aufwand(const Haeufigkeit& h, const Aufwandstabelle& a)
  3048. { return a.unbekannt()*h.unbekannt(); }
  3049.  
  3050. akkumuations_t
  3051. variabler_aufwand(const belegung_t b, const Haeufigkeit& h,
  3052.                   const Tastatur& tastatur, const Aufwandstabelle& a)
  3053. {
  3054.    fingerbelastung_t fh;
  3055.    berechne_fingerbelastung(b, h, tastatur, fh);
  3056.    return variabler_aufwand(b, h, fh, a);
  3057. }
  3058.  
  3059. //--------------- src/Haeufigkeit.cc ---------------
  3060. //#include "Haeufigkeit.hh"
  3061.  
  3062. //#include "Kodierung.hh"
  3063. //#include "Naechstes_zeichen.hh"
  3064. //#include "Tastatur.hh"
  3065. //#include "utfhilfe.hh"
  3066. #include <cstring>
  3067. #include <iomanip>
  3068. #include <iostream>
  3069. #include <string>
  3070.  
  3071. namespace {
  3072.  
  3073. bool lies_codierte_ngramme(const std::string& name, size_t N, size_t Nmax,
  3074.                            zaehl_t** uh,  const Kodierung& kodierung,
  3075.                            bool muss_existieren, bool& utf8ein,
  3076.                            std::unordered_map<char32_t, zaehl_t>* unbekannt)
  3077. {
  3078.    Eingabestream tabelle(name, utf8ein, muss_existieren);
  3079.    if(!tabelle.neuezeile()) return false;
  3080.  
  3081.    while(tabelle.echte_neuezeile()){
  3082.       const zaehl_t h = hole_zahl(tabelle, 0, 1e15);
  3083.       pruefe_leer_dann_N(tabelle, N);
  3084.       tabelle.uebergehen();
  3085.  
  3086.       size_t ende1 = 0, anfangN = 0;  bool ok = true;
  3087.       std::vector<std::pair<int, int>> alles;
  3088.       for(size_t i = 0; i < N; ++i){
  3089.          const char32_t z = tabelle.lies_in_zeile();
  3090.          const auto p = kodierung.position(z);
  3091.          anfangN = alles.size();
  3092.          if(p.first >= 0){
  3093.             alles.push_back(p);
  3094.          }else if(auto s = kodierung.ersatz(z)){
  3095.             for(const auto& j : *s) alles.push_back(j);
  3096.          }else{
  3097.             ok = false;
  3098.             if(unbekannt) (*unbekannt)[z] += h;
  3099.          }
  3100.          if(!i) ende1 = alles.size();
  3101.       }
  3102.  
  3103.       if(!ok) continue;
  3104.       for(size_t i = 0; i < ende1; ++i){
  3105.          int offset = 0;
  3106.          for(size_t j = 0; j < Nmax && i+j < alles.size(); ++j){
  3107.             const int p = alles[i+j].first, e = alles[i+j].second;
  3108.             offset = (offset*ntaste+p)*nebene+e;
  3109.             if(j+1 >= N && i+j >= anfangN) uh[j][offset] += h;
  3110.          }
  3111.       }
  3112.    }
  3113.  
  3114.    if(tabelle.encoding_geaendert()) utf8ein = false;
  3115.    return true;
  3116. }
  3117.  
  3118. int leerzeichenindex(const Kodierung& kodierung){
  3119.    const auto p = kodierung.position(U' ');
  3120.    if(p.first < 0){
  3121.       const auto ersatz = kodierung.ersatz(U' ');
  3122.       return ersatz ? (*ersatz)[0].first*nebene+(*ersatz)[0].second : -1;
  3123.    }else return p.first*nebene+p.second;
  3124. }
  3125.  
  3126. }
  3127.  
  3128. Haeufigkeit::Haeufigkeit(const Tastatur& tastatur, const Kodierung& kod,
  3129.                          const std::vector<std::string>& basis,
  3130.                          const std::vector<double>& gewicht,
  3131.                          const std::vector<bool>& tri,
  3132.                          bool varianzen, bool berichte_unbekanne_zeichen)
  3133.    : nkorpus(basis.size()), trigramme(false), tastatur(tastatur),
  3134.      kodierung(kod), h11(nullptr), h21(nullptr), h22(nullptr)
  3135. {
  3136.    memset(h3, 0, sizeof(h3));
  3137.    memset(h2, 0, sizeof(h2));
  3138.    memset(h1, 0, sizeof(h1));
  3139.    memset(h1e, 0, sizeof(h1e));
  3140.    memset(h11e, 0, sizeof(h11e));
  3141.    memset(h11ee, 0, sizeof(h11ee));
  3142.    memset(h21e, 0, sizeof(h21e));
  3143.  
  3144.   std::unordered_map<char32_t, zaehl_t> summe_unbekannt;
  3145.  
  3146.   for(size_t i = 0; i < basis.size(); ++i){
  3147.       std::unordered_map<char32_t, zaehl_t> unbekannt;
  3148.       zaehl_ta uh1(new zaehl_t[groesse1]), uh2(new zaehl_t[groesse2]);
  3149.       zaehl_ta uh3, uh11, uh21, uh22, uh1l, uh1s, uh2l, uh2s;
  3150.       const bool t = tri[i];
  3151.       if(t){
  3152.          trigramme = true;
  3153.          uh3 = zaehl_ta(new zaehl_t[groesse3]);
  3154.       }
  3155.       zaehl_t llvarianz = 0, lsvarianz = 0, ssvarianz = 0;
  3156.  
  3157.       zaehl_t* uh[3] = { uh1.get(), uh2.get(), uh3.get() };
  3158.  
  3159.       bool nochmal, utf8[3] = {true, true, true}, utf8a[3] = {true, true, true};
  3160.       do{
  3161.          unbekannt.clear();
  3162.          nochmal = false;
  3163.          memset(uh1.get(), 0, sizeof(zaehl_t)*groesse1);
  3164.          memset(uh2.get(), 0, sizeof(zaehl_t)*groesse2);
  3165.          if(uh3) memset(uh3.get(), 0, sizeof(zaehl_t)*groesse3);
  3166.  
  3167.          if(lies_codierte_ngramme(basis[i]+".1", 1, t ? 3 : 2, uh, kodierung,
  3168.                                   false, utf8[0], berichte_unbekanne_zeichen
  3169.                                   ? &unbekannt : nullptr)){
  3170.             lies_codierte_ngramme(basis[i]+".2", 2, t ? 3 : 2, uh, kodierung,
  3171.                                   true, utf8[1], nullptr);
  3172.             if(t) lies_codierte_ngramme(basis[i]+".3", 3, 3, uh, kodierung,
  3173.                                         true, utf8[2], nullptr);
  3174.             for(size_t j = 0; j < 3; ++j){
  3175.                nochmal |= (utf8a[j] != utf8[j]);
  3176.                utf8a[j] = utf8[j];
  3177.             }
  3178.          }else{
  3179.             if(varianzen && utf8[0]){// nur beim ersten Versuch anlegen/init.
  3180.                uh11 = zaehl_ta(new zaehl_t[groesse11]);
  3181.                uh21 = zaehl_ta(new zaehl_t[groesse21]);
  3182.                uh22 = zaehl_ta(new zaehl_t[groesse22]);
  3183.                uh1l = zaehl_ta(new zaehl_t[groesse1]);
  3184.                uh1s = zaehl_ta(new zaehl_t[groesse1]);
  3185.                uh2l = zaehl_ta(new zaehl_t[groesse2]);
  3186.                uh2s = zaehl_ta(new zaehl_t[groesse2]);
  3187.                memset(uh11.get(), 0, sizeof(zaehl_t)*groesse11);
  3188.                memset(uh21.get(), 0, sizeof(zaehl_t)*groesse21);
  3189.                memset(uh22.get(), 0, sizeof(zaehl_t)*groesse22);
  3190.                memset(uh1l.get(), 0, sizeof(zaehl_t)*groesse1);
  3191.                memset(uh1s.get(), 0, sizeof(zaehl_t)*groesse1);
  3192.                memset(uh2l.get(), 0, sizeof(zaehl_t)*groesse2);
  3193.                memset(uh2s.get(), 0, sizeof(zaehl_t)*groesse2);
  3194.             }
  3195.  
  3196.             std::unique_ptr<Naechstes_zeichen> nz
  3197.                (new Naechstes_zeichen(basis[i], utf8[0]));
  3198.  
  3199.             const int o_korrelationsende = leerzeichenindex(kodierung);
  3200.             std::u32string wort;
  3201.             zaehl_t nworte = 0;
  3202.             std::unordered_map<std::u32string, zaehl_t> worthaeufigkeit;
  3203.  
  3204.             // Zähle Wörter, sammle 1/2/3-Gramme
  3205.             int o1 = -1, o2 = -1;
  3206.             while(const char32_t z = nz->get()){
  3207.                const auto p = kodierung.position(z);
  3208.                std::vector<std::pair<int,int>> pos(1, p);
  3209.                if(p.first < 0){
  3210.                   const auto ersatz = kodierung.ersatz(z);
  3211.                   if(ersatz) pos = *ersatz;
  3212.                }
  3213.  
  3214.                for(const auto j : pos){
  3215.                   const int o3 = j.first < 0
  3216.                      ? -1 : index_ein_flach(j.first, j.second);
  3217.                   if(o3 >= 0){
  3218.                      wort.push_back(o3);
  3219.                      ++uh1[o3];
  3220.                      if(o2 >= 0){
  3221.                         ++uh2[index_bi_flach(o2, o3)];
  3222.                         if(t && o1 >= 0)
  3223.                            ++uh3[index_tri_flach(o1,o2,o3)];
  3224.                      }
  3225.                      o1 = o2; o2 = o3;
  3226.                   }else{
  3227.                      if(berichte_unbekanne_zeichen && z >= U' ') ++unbekannt[z];
  3228.                      o1 = o2 = -1;
  3229.                   }
  3230.  
  3231.                   if(o3 < 0 || o3 == o_korrelationsende){
  3232.                      if(varianzen && wort.size()){
  3233.                         ++worthaeufigkeit[wort];
  3234.                         ++nworte;
  3235.                      }
  3236.                      wort.clear();
  3237.                      if(o3 >= 0) wort.push_back(o3);
  3238.                   }
  3239.                }
  3240.             }
  3241.             if(varianzen && wort.size()){
  3242.                ++worthaeufigkeit[wort];
  3243.                ++nworte;
  3244.             }
  3245.  
  3246.             nochmal = nz->encoding_geaendert();
  3247.  
  3248.             if(nochmal){
  3249.                utf8[0] = false;
  3250.             }else if(varianzen){
  3251.                const double rez_nworte = 1./nworte;
  3252.                for(const auto& wort_h : worthaeufigkeit){
  3253.                   const std::u32string& wort = wort_h.first;
  3254.                   const size_t rohe_wortlaenge = wort.length();
  3255.                   double anschlaege = 0., wortlaenge = 0.;
  3256.  
  3257.                   // Zähle n-Gramme im Wort
  3258.                   int o2 = -1;
  3259.                   std::unordered_map<int, double> wh1, wh2;
  3260.                   for(size_t j = 0; j < rohe_wortlaenge; ++j){
  3261.                      const int o3 = wort[j];
  3262.                      // Jedes "o_korrelationsende" wurde in zwei Wörter
  3263.                      // aufgenommen, daher zählen wir sie hier nur halb.
  3264.                      const double plus = o3 != o_korrelationsende ? 1 : 0.5;
  3265.                      if(o3%nebene) anschlaege += plus;
  3266.                      wh1[o3] += plus;
  3267.                      anschlaege += plus;
  3268.                      wortlaenge += plus;
  3269.  
  3270.                      if(o2 >= 0) ++wh2[index_bi_flach(o2, o3)];
  3271.                      o2 = o3;
  3272.                   }
  3273.  
  3274.                   // Aus Wortzahl und n-Grammzahl pro Wort die
  3275.                   // n-Grammhäufigkeiten bestimmen, Varianzen (unter Annahme
  3276.                   // einer Binominalverteilung der Worte) berechnen.
  3277.                   const zaehl_t h = wort_h.second;
  3278.                   const double varianz = h*(1.-h*rez_nworte);
  3279.                   llvarianz += wortlaenge*wortlaenge*varianz;
  3280.                   lsvarianz += wortlaenge*anschlaege*varianz;
  3281.                   ssvarianz += anschlaege*anschlaege*varianz;
  3282.                   for(auto j = wh1.cbegin(); j != wh1.cend(); ++j){
  3283.                      const int oj = j->first;
  3284.                      const auto hj = j->second;
  3285.                      uh1l[oj] += varianz*hj*wortlaenge;
  3286.                      uh1s[oj] += varianz*hj*anschlaege;
  3287.                      for(auto k = j; k != wh1.cend(); ++k){
  3288.                         const int ok = k->first;
  3289.                         const auto hk = k->second;
  3290.                         uh11[sym_index(oj, ok)] += varianz*(hj*hk);
  3291.                      }
  3292.                      for(auto k = wh2.cbegin(); k != wh2.cend(); ++k){
  3293.                         const int ok = k->first;
  3294.                         const auto hk = k->second;
  3295.                         uh21[index_tri21_flach(ok, oj)] += varianz*(hj*hk);
  3296.                      }
  3297.                   }
  3298.                   for(auto j = wh2.cbegin(); j != wh2.cend(); ++j){
  3299.                      const int oj = j->first;
  3300.                      const auto hj = j->second;
  3301.                      uh2l[oj] += varianz*hj*wortlaenge;
  3302.                      uh2s[oj] += varianz*hj*anschlaege;
  3303.                      for(auto k = j; k != wh2.cend(); ++k){
  3304.                         const int ok = k->first;
  3305.                         const auto hk = k->second;
  3306.                         uh22[sym_index(ok, oj)] += varianz*(hj*hk);
  3307.                      }
  3308.                   }
  3309.                }
  3310.             }
  3311.          }
  3312.       }while(nochmal);
  3313.       akkumuliere(i, gewicht[i], uh1.get(), uh2.get(), uh3.get(),
  3314.                   uh11.get(), uh21.get(), uh22.get(),
  3315.                   uh1l.get(), uh1s.get(), uh2l.get(), uh2s.get(),
  3316.                   llvarianz, lsvarianz, ssvarianz,
  3317.                   unbekannt, summe_unbekannt);
  3318.       gewichte[i] = gewicht[i];
  3319.    }
  3320.  
  3321.    for(int i = 0; i < ntaste; ++i){
  3322.       for(int ei = 0; ei < nebene; ++ei){
  3323.          if(kodierung.ist_platzhalter(i, ei)) continue;
  3324.          const auto& txt = kodierung.txt(i, ei);
  3325.          if(h1[i][ei] == 0 && (ei == 0 || txt != kodierung.txt(i, 0)))
  3326.             std::cerr << SPRACHE("Zeichen '", "Symbol '") << txt
  3327.                       << SPRACHE("' tritt im Korpus nie auf.",
  3328.                                  "' does not appear in the corpus.")
  3329.                       << std::endl;
  3330.       }
  3331.    }
  3332.  
  3333.    hunbekannt = 0;
  3334.    zaehl_t hmax = 0; char32_t zmax = 0;
  3335.    for(const auto& zh : summe_unbekannt){
  3336.       hunbekannt += zh.second;
  3337.       if(zh.second > hmax){
  3338.          hmax = zh.second; zmax = zh.first;
  3339.       }
  3340.    }
  3341.    if(berichte_unbekanne_zeichen && hunbekannt > 0){
  3342.       std::cerr << summe_unbekannt.size()
  3343.                 << SPRACHE(" verschiedene unbekannte Zeichen, relative "
  3344.                            "Gesamth" strAe "ufigkeit ",
  3345.                            " distinct unknown symbols, relative total "
  3346.                            "frequency ")
  3347.                 << hunbekannt*100 << "%.  "
  3348.                 << SPRACHE("Das wichtigste dieser Zeichen ist '",
  3349.                            "The most important of these symbols is '")
  3350.                 << utf32_in_ausgabe(zmax) << "'.\n" << std::endl;
  3351.    }
  3352. }
  3353.  
  3354. // Kein copy-Konstruktor: Korrelationstabellen werden nicht kopiert.
  3355. Haeufigkeit::Haeufigkeit(const Haeufigkeit& h, int)
  3356.    : hunbekannt(h.hunbekannt), nkorpus(h.nkorpus),
  3357.      trigramme(h.trigramme), tastatur(h.tastatur), kodierung(h.kodierung),
  3358.      h11(nullptr) // nur für Destruktor
  3359. {
  3360.    for(int i = 0; i < nkorpus; ++i) gewichte[i] = h.gewichte[i];
  3361.    belegung_t b;
  3362.    for(int j = 0; j < ntaste; ++j) b[j] = j;
  3363.    setze(h, b);
  3364. }
  3365.  
  3366. Haeufigkeit::~Haeufigkeit(){
  3367.    if(!h11) return;
  3368.    delete []h11; delete []h21; delete []h22;
  3369.    for(int i = 0; i < nkorpus; ++i){
  3370.       delete []h11e[i]; delete []h11ee[i]; delete []h21e[i];
  3371.    }
  3372. }
  3373.  
  3374. void
  3375. Haeufigkeit::varianzen_anlegen(){
  3376.    if(mit_varianzen()) return;
  3377.    h11 = new haeufigkeit_t[groesse11];
  3378.    h21 = new haeufigkeit_t[groesse21];
  3379.    h22 = new haeufigkeit_t[groesse22];
  3380.    memset(h11, 0, sizeof(haeufigkeit_t)*groesse11);
  3381.    memset(h21, 0, sizeof(haeufigkeit_t)*groesse21);
  3382.    memset(h22, 0, sizeof(haeufigkeit_t)*groesse22);
  3383.    for(int i = 0; i < nkorpus; ++i){
  3384.       h11e[i]  = new haeufigkeit_t[groesse1*groesse1];
  3385.       memset(h11e[i],  0, sizeof(haeufigkeit_t)*groesse1*groesse1);
  3386.       h11ee[i] = new haeufigkeit_t[groesse11];
  3387.       memset(h11ee[i], 0, sizeof(haeufigkeit_t)*groesse11);
  3388.       h21e[i]  = new haeufigkeit_t[groesse21];
  3389.       memset(h21e[i],  0, sizeof(haeufigkeit_t)*groesse21);
  3390.    }
  3391. }
  3392.  
  3393. void
  3394. Haeufigkeit::setze(const Haeufigkeit& h, const belegung_t p)
  3395. {
  3396.    for(int p1 = 0; p1 < ntaste; ++p1){
  3397.       const int z1 = p[p1];
  3398.       for(int e1 = 0; e1 < nebene; ++e1)
  3399.          h1[p1][e1] = h.h1[z1][e1];
  3400.       for(int k = 0; k < nkorpus; ++k)
  3401.          for(int e1 = 0; e1 < nebene; ++e1)
  3402.             h1e[k][p1][e1] = h.h1e[k][z1][e1];
  3403.    }
  3404.    for(int p1 = 0; p1 < ntaste; ++p1){
  3405.       const int z1 = p[p1];
  3406.       for(int p2 = 0; p2 < ntaste; ++p2){
  3407.          const int z2 = p[p2];
  3408.          for(int e1 = 0; e1 < nebene; ++e1)
  3409.             for(int e2 = 0; e2 < nebene2; ++e2)
  3410.                h2[p1][p2][e1][e2] = h.h2[z1][z2][e1][e2];
  3411.       }
  3412.    }
  3413.    if(trigramme){
  3414.       for(int p1 = 0; p1 < ntaste; ++p1){
  3415.          const int z1 = p[p1];
  3416.          for(int p2 = 0; p2 < ntaste; ++p2){
  3417.             const int z2 = p[p2];
  3418.             for(int p3 = 0; p3 < ntaste; ++p3){
  3419.                const int z3 = p[p3];
  3420.                for(int e1 = 0; e1 < nebene; ++e1)
  3421.                   h3[p1][p2][p3][e1] = h.h3[z1][z2][z3][e1];
  3422.             }
  3423.          }
  3424.       }
  3425.    }
  3426. }
  3427.  
  3428. // Sollte mit C++11 funktionieren, Solaris Studio 12.4 packt es aber nicht:
  3429. // void Haeufigkeit::swap(int p1, int p2){
  3430. //    std::swap(h1[p1], h1[p2]);
  3431. //    for(int k = 0; k < nkorpus; ++k)
  3432. //       std::swap(h1e[k][p1], h1e[k][p2]);
  3433. //    std::swap(h2[p1], h2[p2]);
  3434. //    for(int p = 0; p < ntaste; ++p)
  3435. //       std::swap(h2[p][p1], h2[p][p2]);
  3436. //
  3437. //    if(trigramme){
  3438. //       std::swap(h3[p1], h3[p2]);
  3439. //       for(int pi = 0; pi < ntaste; ++pi)
  3440. //          std::swap(h3[pi][p1], h3[pi][p2]);
  3441. //       for(int pi = 0; pi < ntaste; ++pi)
  3442. //          for(int pj = 0; pj < ntaste; ++pj)
  3443. //             std::swap(h3[pi][pj][p1], h3[pi][pj][p2]);
  3444. //    }
  3445. // }
  3446.  
  3447. void Haeufigkeit::swap(int p1, int p2){
  3448.    for(int e1 = 0; e1 < nebene; ++e1){
  3449.       const haeufigkeit_t t = h1[p1][e1];
  3450.       h1[p1][e1] = h1[p2][e1];
  3451.       h1[p2][e1] = t;
  3452.    }
  3453.    for(int k = 0; k < nkorpus; ++k)
  3454.       for(int e1 = 0; e1 < nebene; ++e1){
  3455.          const haeufigkeit_t t = h1e[k][p1][e1];
  3456.          h1e[k][p1][e1] = h1e[k][p2][e1];
  3457.          h1e[k][p2][e1] = t;
  3458.       }
  3459.  
  3460.    for(int p = 0; p < ntaste; ++p)
  3461.       for(int e1 = 0; e1 < nebene; ++e1)
  3462.          for(int e2 = 0; e2 < nebene2; ++e2){
  3463.             const haeufigkeit_t t = h2[p1][p][e1][e2];
  3464.             h2[p1][p][e1][e2] = h2[p2][p][e1][e2];
  3465.             h2[p2][p][e1][e2] = t;
  3466.          }
  3467.    for(int p = 0; p < ntaste; ++p)
  3468.       for(int e1 = 0; e1 < nebene; ++e1)
  3469.          for(int e2 = 0; e2 < nebene2; ++e2){
  3470.             const haeufigkeit_t t = h2[p][p1][e1][e2];
  3471.             h2[p][p1][e1][e2] = h2[p][p2][e1][e2];
  3472.             h2[p][p2][e1][e2] = t;
  3473.          }
  3474.    if(trigramme){
  3475.       for(int pi = 0; pi < ntaste; ++pi)
  3476.          for(int pj = 0; pj < ntaste; ++pj)
  3477.             for(int e1 = 0; e1 < nebene; ++e1){
  3478.                const haeufigkeit_t t = h3[p1][pi][pj][e1];
  3479.                h3[p1][pi][pj][e1] = h3[p2][pi][pj][e1];
  3480.                h3[p2][pi][pj][e1] = t;
  3481.             }
  3482.       for(int pi = 0; pi < ntaste; ++pi)
  3483.          for(int pj = 0; pj < ntaste; ++pj)
  3484.             for(int e1 = 0; e1 < nebene; ++e1){
  3485.                const haeufigkeit_t t = h3[pi][p1][pj][e1];
  3486.                h3[pi][p1][pj][e1] = h3[pi][p2][pj][e1];
  3487.                h3[pi][p2][pj][e1] = t;
  3488.             }
  3489.       for(int pi = 0; pi < ntaste; ++pi)
  3490.          for(int pj = 0; pj < ntaste; ++pj)
  3491.             for(int e1 = 0; e1 < nebene; ++e1){
  3492.                const haeufigkeit_t t = h3[pi][pj][p1][e1];
  3493.                h3[pi][pj][p1][e1] = h3[pi][pj][p2][e1];
  3494.                h3[pi][pj][p2][e1] = t;
  3495.             }
  3496.    }
  3497. }
  3498.  
  3499. void Haeufigkeit::
  3500. akkumuliere(int korpus, double gewicht,
  3501.             const zaehl_t* uh1,  const zaehl_t* uh2, const zaehl_t* uh3,
  3502.             const zaehl_t* uh11, const zaehl_t* uh21, const zaehl_t* uh22,
  3503.             const zaehl_t* uh1l, const zaehl_t* uh1s,
  3504.             const zaehl_t* uh2l, const zaehl_t* uh2s,
  3505.             zaehl_t ll, zaehl_t ls, zaehl_t ss,
  3506.             const std::unordered_map<char32_t, zaehl_t>& unbekannt,
  3507.             std::unordered_map<char32_t, zaehl_t>& summe_unbekannt)
  3508. {
  3509.    zaehl_t total = 0, geshiftet = 0, mitfix = 0;
  3510.  
  3511.    bool shift_variabel = 0;
  3512.    for(int i = ntaste; i < ntaste+nshift; ++i)
  3513.       if(!tastatur.finger_fix(tastatur.finger_index(i))) shift_variabel = true;
  3514.  
  3515.    for(int i = 0; i < ntaste; ++i){
  3516.       for(int ei = 0; ei < nebene; ++ei){
  3517.          const int oi = index_ein_flach(i, ei);
  3518.          if(!tastatur.finger_fix(tastatur.finger_index(i))) total += uh1[oi];
  3519.          mitfix += uh1[oi];
  3520.          if(ei && shift_variabel) geshiftet += uh1[oi];
  3521.       }
  3522.    }
  3523.  
  3524.    const zaehl_t htot_k = total+geshiftet;
  3525.    const double w = gewicht/total, w2 = w*w;
  3526.    const double wf = 1./htot_k, wf2 = wf*wf, wfw = wf*w;
  3527.    const double mitfix_w = gewicht/mitfix;
  3528.  
  3529.    for(int i = 0; i < ntaste; ++i){
  3530.       for(int ei = 0; ei < nebene; ++ei){
  3531.          const int oi = index_ein_flach(i, ei);
  3532.          ein(i,ei) += uh1[oi]*w;
  3533.          ein_k(korpus,i,ei) = uh1[oi]*wf;
  3534.  
  3535.          for(int j = 0; j < ntaste; ++j){
  3536.             for(int ej = 0; ej < nebene2; ++ej){
  3537.                const int oj = index_ein_flach(j, ej);
  3538.                const int oij = index_bi_flach(oi, oj);
  3539.                bi(i,ei,j,ej) += uh2[oij]*w;
  3540.  
  3541.                // Zu dieser speziellen Summation siehe den Kommentar in
  3542.                // Aufwandstabelle::Aufwandstabelle
  3543.                if(uh3 && ej == 0){
  3544.                   for(int k = 0; k < ntaste; ++k){
  3545.                      for(int ek = 0; ek < nebene; ++ek){
  3546.                         const int ok = index_ein_flach(k, ek);
  3547.                         const int oijk = index_tri21_flach(oij, ok);
  3548.                         tri(i,j,k,ek) += uh3[oijk]*w;
  3549.                      }
  3550.                   }
  3551.                }
  3552.             }
  3553.          }
  3554.       }
  3555.    }
  3556.    if(uh11){
  3557.       const double wk = 1./total;
  3558.       varianzen_anlegen();
  3559.       for(int oi = 0; oi < groesse1; ++oi){
  3560.          const haeufigkeit_t hi = uh1[oi]*wk, hi_f = uh1[oi]*wf;
  3561.          for(int oj = 0; oj < groesse1; ++oj){
  3562.             const haeufigkeit_t hj = uh1[oj]*wk, hj_f = uh1[oj]*wf;
  3563.             const zaehl_t uhij = uh11[sym_index(oi, oj)];
  3564.             ein_ein_k(korpus, oi, oj) =
  3565.                (uhij-hj_f*uh1s[oi]-hi*uh1l[oj]+ls*hi*hj_f)*wfw;
  3566.  
  3567.             if(oj > oi) continue;
  3568.             ein_ein(oi, oj) +=
  3569.                (uhij-hj*uh1l[oi]-hi*uh1l[oj]+ll*hi*hj)*w2;
  3570.             ein_k_ein_k(korpus, oi, oj) =
  3571.                (uhij-hj_f*uh1s[oi]-hi_f*uh1s[oj]+ss*hi_f*hj_f)*wf2;
  3572.          }
  3573.       }
  3574.       for(int oij = 0; oij < groesse2; ++oij){
  3575.          const haeufigkeit_t hij = uh2[oij]*wk;
  3576.          for(int ok = 0; ok < groesse1; ++ok){
  3577.             const haeufigkeit_t hk = uh1[ok]*wk, hk_f = uh1[ok]*wf;
  3578.             const zaehl_t uhijk = uh21[index_tri21_flach(oij, ok)];
  3579.             ein_bi(ok, oij) +=
  3580.                (uhijk-hij*uh1l[ok]-hk*uh2l[oij]+ll*hij*hk)*w2;
  3581.             ein_k_bi(korpus, ok, oij) =
  3582.                (uhijk-hij*uh1l[ok]-hk_f*uh2s[oij]+ls*hij*hk_f)*wfw;
  3583.          }
  3584.          for(int okl = 0; okl <= oij; ++okl){
  3585.             const haeufigkeit_t hkl = uh2[okl]*wk;
  3586.             bi_bi(oij, okl) += (uh22[sym_index(oij, okl)]-hij*uh2l[okl]
  3587.                                 -hkl*uh2l[oij]+ll*hij*hkl)*w2;
  3588.          }
  3589.       }
  3590.    }
  3591.  
  3592.    for(const auto& zh : unbekannt){
  3593.       summe_unbekannt[zh.first] += mitfix_w*zh.second;
  3594.    }
  3595. }
  3596.  
  3597. void
  3598. Haeufigkeit::statistik() const {
  3599.    const Haeufigkeit& h = *this;
  3600.    haeufigkeit_t htot = 0, h2tot = 0, doppelt = 0, gross = 0;
  3601.    for(int i = 0; i < ntaste; ++i){
  3602.       htot += h(i,0)+h(i,1);
  3603.       gross += h(i,1);
  3604.       for(int ei = 0; ei < nebene; ++ei){
  3605.          for(int ej = 0; ej < nebene2; ++ej){
  3606.             doppelt += h(i,ei,i,ej);
  3607.             for(int j = 0; j < ntaste; ++j)
  3608.                h2tot += h(i,ei,j,ej);
  3609.          }
  3610.       }
  3611.    }
  3612.  
  3613.    right(std::cout);  fixed(std::cout);
  3614.    std::cout << std::setprecision(3)
  3615.              << SPRACHE("Zeichenh" strAe "ufigkeit klein/gross:\n\n",
  3616.                         "Symbol frequencies upper/lower case:\n\n");
  3617.    for(int i = 0; i < ntaste; ++i){
  3618.       if(i && i%4 == 0) std::cout << "\n";
  3619.       std::cout << kodierung.txt(i, 0) << kodierung.txt(i, 1) << " "
  3620.                 << std::setw(6) << 100.*h(i,0)/htot << "/"
  3621.                 << std::setw(5) << 100.*h(i,1)/htot << "   ";
  3622.    }
  3623.  
  3624.    std::cout << SPRACHE("\n\nGrossbuchstaben:  ", "\n\nCapital letters:  ")
  3625.              << std::setw(5) << 100*gross/htot
  3626.              << SPRACHE(" %\nDoppeltanschl" strAe "ge: ",
  3627.                         " %\nDouble strokes:   ")
  3628.              << std::setw(5) << 100*doppelt/h2tot << " %\n" << std::endl;
  3629. }
  3630.  
  3631. //--------------- src/Tastatur.cc ---------------
  3632. //#include "Tastatur.hh"
  3633.  
  3634. //#include "konstanten.hh"
  3635. //#include "utfhilfe.hh"
  3636. #include <cassert>
  3637. #include <cmath>
  3638. #include <iostream> // für Fehlerausgabe, besser wäre Exception
  3639.  
  3640. bool
  3641. Tastatur::istDaumen(finger_t i)
  3642. { return std::abs(i) <= finger_t::DaumenRechts; }
  3643.  
  3644. bool
  3645. Tastatur::istKleinfinger(finger_t i)
  3646. { return std::abs(i) == finger_t::KleinfingerRechts; }
  3647.  
  3648. const char*
  3649. Tastatur::kategorie_str(int kategorie){
  3650.    const char* kategorie_s[] = {
  3651.       SPRACHE("Handwechsel", "Hand alternation"),
  3652.       SPRACHE("Kollision", "Same finger repetition"),
  3653.       SPRACHE("Doppeltanschlag", "Double stroke"),
  3654.       SPRACHE("Ausw" strAe "rts", "Outwards"),
  3655.       SPRACHE("Einw" strAe "rts", "Inwards"),
  3656.       SPRACHE("Mit undefiniertem Daumen", "With undefined thumb") };
  3657.    return kategorie_s[kategorie];
  3658. }
  3659.  
  3660. std::string
  3661. Tastatur::kategorie_lang(int i, int j) const {
  3662.    assert(i < ntaste+nshift && j < ntaste+nshift);
  3663.    const int dzeile = std::abs(zeile(i)-zeile(j));
  3664.    const int fi = finger(i), fj = finger(j);
  3665.    std::string typ = kategorie_str(kategorie(i, j));
  3666.    if(istHandwiederholung(i,j)){
  3667.       if(std::abs(fi-fj) == 1)
  3668.          typ += SPRACHE(", Nachbarfinger", ", adjacent finger");
  3669.       if(dzeile == 1)
  3670.          typ += SPRACHE(", Zeilensprung", ", row jump");
  3671.       if(dzeile == 2)
  3672.          typ += SPRACHE(", doppelter Zeilensprung", ", double row jump");
  3673.    }
  3674.    return typ;
  3675. }
  3676.  
  3677. int
  3678. Tastatur::spalte(int i) const {
  3679.    assert(i < ntaste+nshift);
  3680.    return spalten[i];
  3681. }
  3682.  
  3683. int
  3684. Tastatur::zeile(int i) const {
  3685.    assert(i < ntaste+nshift);
  3686.    return zeilen[i];
  3687. }
  3688.  
  3689. finger_t
  3690. Tastatur::finger(int i) const {
  3691.    assert(i < ntaste+nshift);
  3692.    return fingertab[i];
  3693. }
  3694.  
  3695. int
  3696. Tastatur::finger_index(int i) const {
  3697.    assert(i < ntaste+nshift);
  3698.    return fingerind[i];
  3699. }
  3700.  
  3701. int
  3702. Tastatur::shifttaste(int i) const {
  3703.    assert(i < ntaste+nshift);
  3704.    return shifttast[i];
  3705. }
  3706.  
  3707. int
  3708. Tastatur::shift_finger_index(int i) const {
  3709.    assert(i < ntaste+nshift);
  3710.    return sfingerind[i];
  3711. }
  3712.  
  3713. int
  3714. Tastatur::grundposition(int i) const {
  3715.    assert(i < ntaste+nshift);
  3716.    return grundpos[i];
  3717. }
  3718.  
  3719. int
  3720. Tastatur::taste(int z, int s) const {
  3721.    if(z < 0 || z >= nzeile || s < 0 || s >= nspalte) return -1;
  3722.    return tastennr[z][s];
  3723. }
  3724.  
  3725. const std::u32string&
  3726. Tastatur::name(int i) const {
  3727.    assert(i < ntaste+nshift);
  3728.    return namen[i];
  3729. }
  3730.  
  3731. int
  3732. Tastatur::taste(const std::u32string& n) const {
  3733.    const auto i = namennr.find(n);
  3734.    return i == namennr.end() ? -1 : i->second;
  3735. }
  3736.  
  3737. kategorie_t
  3738. Tastatur::kategorie(int i, int j) const {
  3739.    assert(i < ntaste+nshift && j < ntaste+nshift);
  3740.    return tastenkategorie[i][j];
  3741. }
  3742.  
  3743. const std::vector<int>&
  3744. Tastatur::benutzerkategorie(const std::vector<int>& i) const {
  3745.    static const std::vector<int> leerervektor;
  3746.    const auto p = benutzerkat.find(i);
  3747.    return p == benutzerkat.end() ? leerervektor : p->second;
  3748. }
  3749.  
  3750. const std::string&
  3751. Tastatur::benutzerkategorie_name(int i) const
  3752. { return kategorie_liste[i]; }
  3753.  
  3754. bool
  3755. Tastatur::istHandwiederholung(int i, int j) const
  3756. { return kategorie(i,j) != kategorie_t::Handwechsel &&
  3757.       kategorie(i,j) != kategorie_t::MitUndefDaumen; }
  3758.  
  3759. bool
  3760. Tastatur::istWippe(int i, int j, int k) const {
  3761.    return (kategorie(i, j) == kategorie_t::Einwaerts &&
  3762.            kategorie(j, k) == kategorie_t::Auswaerts)
  3763.       || (kategorie(i, j) == kategorie_t::Auswaerts &&
  3764.           kategorie(j, k) == kategorie_t::Einwaerts);
  3765. }
  3766.  
  3767. koordinate_t
  3768. Tastatur::tastenkoord(int i) const
  3769. { return koordinate_t{x[i], y[i]}; }
  3770.  
  3771. double
  3772. Tastatur::distanz(int i, int j) const {
  3773.    const auto ki = tastenkoord(i), kj = tastenkoord(j);
  3774.    return std::hypot(ki.x-kj.x, ki.y-kj.y);
  3775. }
  3776.  
  3777. bool
  3778. Tastatur::finger_fix(int i) const
  3779. { return fix[i]; }
  3780.  
  3781. int
  3782. Tastatur::nvariabel() const
  3783. { return nvar; }
  3784.  
  3785. double
  3786. Tastatur::lageaufwand(int i) const
  3787. {
  3788.    assert(i < ntaste+nshift);
  3789.    return aufwand[i];
  3790. }
  3791.  
  3792. Tastatur::Tastatur(const std::vector<taste_t>& tasten,
  3793.                    const std::unordered_set<std::u32string>& fixe_tasten){
  3794.    nvar = ntaste-fixe_tasten.size();
  3795.    if(nvar < 2){
  3796.       std::cerr << SPRACHE("Alle Tasten sind fix belegt.",
  3797.                            "All keys have a fixed mapping.")
  3798.                 << std::endl;
  3799.       exit(1);
  3800.    }
  3801.  
  3802.    for(auto& i : tastennr) for(auto& j : i) j = -1;
  3803.  
  3804.    int gpos[nfinger+1];
  3805.    for(auto& i: gpos) i = -1;
  3806.    int ifix = nvar, ivar = 0;
  3807.    for(int j = 0; j < ntaste+nshift; ++j){
  3808.       const taste_t& t = tasten[j];
  3809.       const bool istFix = fixe_tasten.find(t.name) != fixe_tasten.end();
  3810.       const int i = istFix ? ifix++ : (j < ntaste ? ivar++ : j);
  3811.       namen[i] = t.name;
  3812.       namennr[t.name] = i;
  3813.       spalten[i] = t.spalte;
  3814.       zeilen[i] = t.zeile;
  3815.       // Wenn meherere Tasten in derselben Spalte und Zeile sind "gewinnt"
  3816.       // die erste nicht fixe.
  3817.       if(tastennr[t.zeile][t.spalte] == -1){
  3818.          tastennr[t.zeile][t.spalte] = i;
  3819.       }else{
  3820.          if(tastennr[t.zeile][t.spalte] >= nvar && !istFix)
  3821.             tastennr[t.zeile][t.spalte] = i;
  3822.       }
  3823.  
  3824.       fingertab[i] = t.finger;
  3825.       fingerind[i] = (t.finger < 0)
  3826.          ? t.finger+5
  3827.          : (t.finger > 0 ? t.finger+4 : nfinger);
  3828.  
  3829.       if(t.istGrundposition) gpos[fingerind[i]] = i;
  3830.       x[i] = t.x;  y[i] = t.y;
  3831.       aufwand[i] = t.aufwand;
  3832.  
  3833.       shifttast[i] = t.seite ? ntaste : ntaste+(nshift-1);
  3834.    }
  3835.  
  3836.    for(int i = 0; i < ntaste+nshift; ++i){
  3837.       sfingerind[i] = finger_index(shifttaste(i));
  3838.       const int fi = finger_index(i);
  3839.       grundpos[i] = gpos[fi] < 0 ? i : gpos[fi];
  3840.    }
  3841.  
  3842.    for(int i = 0; i < ntaste+nshift; ++i){
  3843.       const int finger_i = finger(i);
  3844.       for(int j = 0; j < ntaste+nshift; ++j){
  3845.          const int finger_j = finger(j);
  3846.  
  3847.          if(finger_i == finger_t::EinerDerDaumen ||
  3848.             finger_j == finger_t::EinerDerDaumen){
  3849.             tastenkategorie[i][j] = kategorie_t::MitUndefDaumen;
  3850.          }else if((finger_i^finger_j) < 0){
  3851.             tastenkategorie[i][j] = kategorie_t::Handwechsel;
  3852.          }else{
  3853.             if(spalte(i) == spalte(j) && zeile(i) == zeile(j)){
  3854.                tastenkategorie[i][j] = kategorie_t::Doppeltanschlag;
  3855.             }else if(finger_i == finger_j){
  3856.                tastenkategorie[i][j] = kategorie_t::Kollision;
  3857.             }else{
  3858.                const bool auswaerts =
  3859.                   (std::abs(finger_j) > std::abs(finger_i));
  3860.                tastenkategorie[i][j] = auswaerts
  3861.                   ? kategorie_t::Auswaerts
  3862.                   : kategorie_t::Einwaerts;
  3863.             }
  3864.          }
  3865.       }
  3866.    }
  3867.  
  3868.    bool mehrere_shift = false;
  3869.    for(auto& i : fix) i = true;
  3870.    for(int i = 0; i < nvar; ++i){
  3871.       fix[finger_index(i)] = false;
  3872.       if(shift_finger_index(i) != shift_finger_index(0))
  3873.          mehrere_shift = true;
  3874.    }
  3875.    if(mehrere_shift)
  3876.       for(int i = 0; i < nvar; ++i) fix[shift_finger_index(i)] = false;
  3877. }
  3878.  
  3879. void
  3880. Tastatur::neue_kategorien(const std::vector<n_gramm_t>& benutzerkategorie){
  3881.    for(const auto& i : benutzerkategorie){
  3882.       const std::string n = utf32_in_ausgabe(i.name);
  3883.       size_t p = 0;
  3884.       for(; p < kategorie_liste.size(); ++p)
  3885.          if(kategorie_liste[p] == n) break;
  3886.       if(p == kategorie_liste.size())
  3887.          kategorie_liste.push_back(n);
  3888.       std::vector<int> ngramm = { i.t1, i.t2 };
  3889.       if(i.t3 >= 0) ngramm.push_back(i.t3);
  3890.       benutzerkat[ngramm].push_back(p);
  3891.    }
  3892. }
  3893.  
  3894. //--------------- src/Kodierung.cc ---------------
  3895. //#include "Kodierung.hh"
  3896.  
  3897. //#include "Unicode.hh"
  3898. //#include "utfhilfe.hh"
  3899. #include <cassert>
  3900. #include <iomanip>
  3901. #include <iostream>
  3902. #include <sstream>
  3903.  
  3904. Kodierung::Kodierung(const std::vector<std::u32string>& klartext,
  3905.                      const std::vector<std::u32string>& ersatzstring,
  3906.                      const std::vector<std::u32string>& glyph,
  3907.                      char32_t platzhalter)
  3908.    : platzhalterstr(utf32_in_ausgabe(platzhalter))
  3909. {
  3910.    // DEL, dann kommen die hohen CTRLs, die können wir alle belegen.
  3911.    unsigned char freiercode = 127;
  3912.    if(klartext.size() != ntaste){
  3913.       std::cerr << SPRACHE("Die Anzahl der der Zeichen ist ",
  3914.                            "The number of symbols is ")
  3915.                 << klartext.size()
  3916.                 << SPRACHE(
  3917.                    ", sollte jedoch gleich der Anzahl der Zeichentasten ",
  3918.                    ", but it should be equal to the number of symbol keys ")
  3919.                 << ntaste << SPRACHE(" sein.", ".") << std::endl;
  3920.       exit(1);
  3921.    }
  3922.    for(int i = 0; i < ntaste; ++i){
  3923.       for(int e = 0; e < nebene; ++e) chars[i][e] = 0;
  3924.       psenc[i] = 0;
  3925.       for(int e = 0; e < nebene; ++e){
  3926.          const char32_t c =
  3927.             klartext[i].length() < nebene ? klartext[i][0] : klartext[i][e];
  3928.          if(c == platzhalter) continue;
  3929.          chars[i][e] = c;
  3930.          if(c < 256 &&
  3931.             (psenc[i] == 0 || psenc[i] == Unicode::get().kleinbuchstabe(c))){
  3932.             psenc[i] = c;
  3933.          }
  3934.          strings[i][e] = utf32_in_ausgabe(c);
  3935.          if(invers.find(c) == invers.end()) invers[c] = std::make_pair(i, e);
  3936.       }
  3937.       for(size_t e = nebene; e < klartext[i].length(); ++e){
  3938.          const char32_t extra = klartext[i][e];
  3939.          if(psenc[i] == 0 && extra < 256) psenc[i] = extra;
  3940.          if(invers.find(extra) == invers.end())
  3941.             invers[extra] = std::make_pair(i, nebene-1);
  3942.       }
  3943.  
  3944.       glyphname[i] = utf32_in_ausgabe(glyph[i]);
  3945.       if(psenc[i] == 0){
  3946.          psenc[i] = freiercode;
  3947.          if(freiercode == 9 || freiercode == 12){
  3948.             freiercode += 2;  // CR, LF übergehen
  3949.          }else if(freiercode == 159){
  3950.             // 160 ist nichtumbrechendes Leerzeichen, das überschreiben wir
  3951.             // nicht.  Fülle dann den CTRL-Bereich.
  3952.             freiercode = 1;
  3953.          }else ++freiercode;
  3954.  
  3955.          if(glyphname[i].length() == 0){
  3956.             const int codepoint =
  3957.                (chars[i][0] == 0 ||
  3958.                 Unicode::get().kleinbuchstabe(chars[i][1]) == chars[i][0])
  3959.                ? chars[i][1] : chars[i][0];
  3960.             const int laenge = codepoint < 0x10000 ? 4 : 5;
  3961.             const std::string u = codepoint < 0x10000 ? "uni" : "u";
  3962.             std::ostringstream os;
  3963.             os << u << std::setw(laenge) << std::setfill('0')
  3964.                << std::uppercase << std::hex << codepoint;
  3965.             glyphname[i] = os.str();
  3966.          }
  3967.       }
  3968.    }
  3969.  
  3970.    for(const auto& i : ersatzstring){
  3971.       if(i.length() == 0) continue;
  3972.       std::vector<std::pair<int, int>> ersatzliste;
  3973.       const char32_t n = i[0];
  3974.       std::u32string fehlt;
  3975.       for(size_t j = 1; j < i.length(); ++j){
  3976.          const auto p = position(i[j]);
  3977.          if(p.first < 0){
  3978.             const auto* pe = ersatz(i[j]);
  3979.             if(pe){
  3980.                for(const auto& pk : *pe) ersatzliste.push_back(pk);
  3981.             }else fehlt.push_back(i[j]);
  3982.          }else ersatzliste.push_back(p);
  3983.       }
  3984.       if(fehlt.size() == 0){
  3985.          inversstr[n] = ersatzliste;
  3986.       }else{
  3987.          std::cerr << "Ersatz '" << utf32_in_ausgabe(i) << "': "
  3988.                    << SPRACHE("Warnung: Das Zeichen '",
  3989.                               "Warning: The character '")
  3990.                    << utf32_in_ausgabe(n)
  3991.                    << SPRACHE(
  3992.                       "' kann nicht ersetzt werden weil das/die Zeichen '",
  3993.                       "' cannot be substituted because the character(s) '")
  3994.                    << utf32_in_ausgabe(fehlt)
  3995.                    << SPRACHE("' nicht in der Belegung sind.",
  3996.                               "' are not in the layout.")
  3997.                    << std::endl;
  3998.       }
  3999.    }
  4000. }
  4001. bool
  4002. Kodierung::ist_platzhalter(int i, int e) const
  4003. { assert(i < ntaste && e < nebene); return strings[i][e].length() == 0; }
  4004.  
  4005. const std::string&
  4006. Kodierung::txt(int i, int e) const
  4007. { return ist_platzhalter(i,e) ? platzhalterstr : strings[i][e]; }
  4008.  
  4009. char32_t
  4010. Kodierung::uchar(int i, int e) const
  4011. { assert(i < ntaste && e < nebene); return chars[i][e];}
  4012.  
  4013. const std::string&
  4014. Kodierung::bevorzugt(int i) const {
  4015.    assert(i < ntaste);
  4016.    return strings[i][0].length() ? strings[i][0] : strings[i][1];
  4017. }
  4018. const std::string&
  4019. Kodierung::txt(int ie) const
  4020. { assert(ie < ntaste*nebene);  return strings[ie/nebene][ie%nebene]; }
  4021.  
  4022. std::pair<int, int>
  4023. Kodierung::position(char32_t z) const {
  4024.    const auto i = invers.find(z);
  4025.    if(i == invers.end()) return std::make_pair(-1, -1);
  4026.    return i->second;
  4027. }
  4028.  
  4029. const std::vector<std::pair<int,int>>*
  4030. Kodierung::ersatz(char32_t z) const {
  4031.    const auto i = inversstr.find(z);
  4032.    return i == inversstr.end() ? nullptr : &i->second;
  4033. }
  4034.  
  4035. int
  4036. Kodierung::psencoding(int i) const
  4037. { assert(i < ntaste); return psenc[i]; }
  4038.  
  4039. std::string
  4040. Kodierung::psencstr(int i) const {
  4041.    std::string str;
  4042.    if(psenc[i] == '(' || psenc[i] == ')' || psenc[i] == '\\')
  4043.       str.push_back('\\');
  4044.    str.push_back(psenc[i]);
  4045.    return str;
  4046. }
  4047.  
  4048. const std::string&
  4049. Kodierung::psglyphname(int i) const
  4050. { assert(i < ntaste); return glyphname[i]; }
  4051.  
  4052. //--------------- src/schreibe_belegung.cc ---------------
  4053. //#include "schreibe_belegung.hh"
  4054.  
  4055. //#include "Aufwandstabelle.hh"
  4056. //#include "Haeufigkeit.hh"
  4057. //#include "Kodierung.hh"
  4058. //#include "Statistik.hh"
  4059. //#include "Tastatur.hh"
  4060. //#include "konstanten.hh"
  4061. //#include "utfhilfe.hh"
  4062. #include <cassert>
  4063. #include <cmath>
  4064. #include <iomanip>
  4065. #include <iostream>
  4066. #include <vector>
  4067.  
  4068. #define SP1 std::setprecision(1)
  4069. #define SP2 std::setprecision(2)
  4070. #define SP3 std::setprecision(3)
  4071. #define SP4 std::setprecision(4)
  4072. #define SW2 std::setw(2)
  4073. #define SW4 std::setw(4)
  4074. #define SW5 std::setw(5)
  4075. #define SW6 std::setw(6)
  4076. #define SW7 std::setw(7)
  4077. #define SW8 std::setw(8)
  4078.  
  4079. namespace {
  4080.  
  4081. std::string
  4082. decodiere(const std::string& seq, const Kodierung& kodierung)
  4083. {
  4084.    std::string resultat;
  4085.    for(const auto j : seq) resultat += kodierung.txt(j);
  4086.    return resultat;
  4087. }
  4088.  
  4089. void
  4090. handeinsaetze(const belegung_t b, const Tastatur& tastatur,
  4091.               const Kodierung& kodierung,
  4092.               const std::unordered_map<std::string, haeufigkeit_t>& wortliste,
  4093.               const akkumuations_t limit)
  4094. {
  4095.    bool seite[ntaste];
  4096.    for(int i = 0; i < ntaste; ++i) seite[b[i]] = (tastatur.finger(i) < 0);
  4097.  
  4098.    std::unordered_map<std::string, akkumuations_t> handeinsatz;
  4099.    akkumuations_t summe = 0, summe2 = 0, tot = 0, stot[2] = { 0, 0 };
  4100.    std::vector<akkumuations_t> prolaenge[2];
  4101.    for(auto& i : wortliste){
  4102.       const std::string& wort = i.first;
  4103.       const haeufigkeit_t h = i.second;
  4104.  
  4105.       bool warseite = seite[wort[0]/nebene];
  4106.       size_t start = 0;
  4107.       for(size_t j = 1; j <= wort.size(); ++j){
  4108.          if(j == wort.size() || warseite != seite[wort[j]/nebene]){
  4109.             const size_t len = j-start;
  4110.  
  4111.             handeinsatz[wort.substr(start, len)] += h;
  4112.  
  4113.             stot[warseite] += h;
  4114.             while(prolaenge[0].size() <= len){
  4115.                prolaenge[0].push_back(0);
  4116.                prolaenge[1].push_back(0);
  4117.             }
  4118.             assert(prolaenge[0].size() == prolaenge[1].size());
  4119.             prolaenge[warseite][len] += h;
  4120.  
  4121.             summe +=  len*h;
  4122.             summe2 += len*len*h;
  4123.             tot += h;
  4124.  
  4125.             warseite = !warseite;
  4126.             start = j;
  4127.          }
  4128.       }
  4129.    }
  4130.  
  4131.    std::multimap<akkumuations_t, std::string> sortiert;
  4132.    size_t laengster = 0;
  4133.    akkumuations_t hlaengster = 0;
  4134.    std::string tlaengster;
  4135.    for(const auto& i : handeinsatz){
  4136.       sortiert.insert(std::make_pair(i.second, i.first));
  4137.       size_t len = i.first.size();
  4138.       if(len > laengster || (len == laengster && hlaengster < i.second)){
  4139.          laengster = len;
  4140.          tlaengster = i.first;
  4141.          hlaengster = i.second;
  4142.       }
  4143.    }
  4144.  
  4145.    akkumuations_t mittel = summe/tot;
  4146.    std::cout << SPRACHE("\nInsgesamt ", "\nIn total ") << sortiert.size()
  4147.              << SPRACHE(
  4148.                 " verschiedene Handeins" strAe "tze, mittlere L" strAe "nge ",
  4149.                 " different one-handed sequences, average length ")
  4150.              << SP3 << mittel
  4151.              << SPRACHE("\n  Standardabweichung ",
  4152.                         "\n  standard deviation ")
  4153.              << SP3 << std::sqrt(summe2/tot-mittel*mittel)
  4154.              << SPRACHE(", maximale L" strAe "nge ",
  4155.                         ", maximum length ") << laengster
  4156.              << SPRACHE(" f" strUe "r ",
  4157.                         " for ") << decodiere(tlaengster, kodierung)
  4158.              << SPRACHE("\n\n#  % links  % summiert    % rechts % summiert    "
  4159.                         "% beide  % summiert\n",
  4160.                         "\n\n#  % left   % cumulative  % right  % cumulative  "
  4161.                         "% both   % cumulative\n");
  4162.  
  4163.    akkumuations_t procum[2] = { 0, 0 };
  4164.    for(size_t i = 1; i+1 < prolaenge[0].size(); ++i){
  4165.       procum[0] += prolaenge[0][i];
  4166.       procum[1] += prolaenge[1][i];
  4167.       std::cout << SW2 << i
  4168.                 << SP4 << SW8 << 100*(prolaenge[1][i]/stot[1]) << " ("
  4169.                 << SP4 << SW8 << 100*(procum[1]/stot[1]) << ")    "
  4170.                 << SP4 << SW8 << 100*(prolaenge[0][i]/stot[0]) << " ("
  4171.                 << SP4 << SW8 << 100*(procum[0]/stot[0]) << ")    "
  4172.                 << SP4 << SW8 << 100*((prolaenge[0][i]+prolaenge[1][i])/tot)
  4173.                 << " ("
  4174.                 << SP4 << SW8 << 100*((procum[0]+procum[1])/tot) << ")\n";
  4175.    }
  4176.  
  4177.    if(limit > 0){
  4178.       std::cout << SPRACHE("\n#    %       % summiert\n",
  4179.                            "\n#    %       % cumulative\n");
  4180.       akkumuations_t sum = 0;
  4181.       int n = 0;
  4182.       for(auto i = sortiert.crbegin(); i != sortiert.crend(); ++i){
  4183.          if(sum > limit) break;
  4184.          n++;
  4185.          const akkumuations_t h = 100*i->first/tot;
  4186.          sum += h;
  4187.          std::cout << SW4 << n
  4188.                    << SP3 << SW7 << h << " ("
  4189.                    << SP3 << SW7 << sum << ")  "
  4190.                    << decodiere(i->second, kodierung) << "\n";
  4191.       }
  4192.    }
  4193. }
  4194.  
  4195. }
  4196.  
  4197. void schreibe_belegung(const belegung_t b, const Tastatur& tastatur,
  4198.                        const Kodierung& kodierung, const Haeufigkeit& h,
  4199.                        const Aufwandstabelle& a, const akkumuations_t A,
  4200.                        const std::u32string& name,
  4201.                        const double ngrammakkumlimit[3],
  4202.                        const std::unordered_map<std::string, haeufigkeit_t>&
  4203.                           wortliste,
  4204.                        const akkumuations_t handeinsatzlimit,
  4205.                        bool als_fixeszeichen)
  4206. {
  4207.    std::string str[nzeile];
  4208.    for(int z = 0; z < nzeile; ++z){
  4209.       for(int s = 0; s < nspalte; ++s){
  4210.          const int t = tastatur.taste(z, s);
  4211.          str[z] += (t < 0 || t >= ntaste)
  4212.             ? " " : kodierung.bevorzugt(b[t]);
  4213.       }
  4214.    }
  4215.  
  4216.    Statistik S(b, tastatur, kodierung, h, a, ngrammakkumlimit);
  4217.  
  4218.    const std::string lspalte(nspalte, ' ');
  4219.  
  4220.    std::cout << utf32_in_ausgabe(name);
  4221.    for(int i = name.length(); i < nspalte+1; ++i) std::cout << " ";
  4222.    right(std::cout);  fixed(std::cout);
  4223.  
  4224.    std::cout << SP3 << SW7 << 100*A
  4225.              << SPRACHE(" Gesamtaufwand  "," total effort   ")
  4226.              << SW7 << 100*S.aeinzel
  4227.              << SPRACHE(" Lageaufwand        links rechts\n",
  4228.                         " positional effort    left right\n");
  4229.    std::cout << str[0] << " "
  4230.              << SP3 << SW7 << S.hk[kategorie_t::Kollision]
  4231.              << SPRACHE(" Kollisionen    ", " same finger rp ")
  4232.              << SW7 << S.hs[kategorie_t::Kollision]
  4233.              << SPRACHE(" Shift-Kollisionen  ob ", " shift same finger top ")
  4234.              << SP1 << SW4 << S.hpos[0][zeilen_t::Obere_Zeile] << " "
  4235.              << SW4 << S.hpos[1][zeilen_t::Obere_Zeile] << "\n";
  4236.    std::cout << str[1] << " "
  4237.              << SP3 << SW7 << S.hk[kategorie_t::Handwechsel]
  4238.              << SPRACHE(" Handwechsel    ", " hand alternat. ")
  4239.              << SW7 << S.hs[kategorie_t::Handwechsel]
  4240.              << SPRACHE(" Shift-Handwechsel  mi ", " shift hand alter. mid ")
  4241.              << SP1 << SW4 << S.hpos[0][zeilen_t::Mittelzeile] << " "
  4242.              << SW4 << S.hpos[1][zeilen_t::Mittelzeile] << "\n";
  4243.    std::cout << str[2] << " "
  4244.              << SP3 << SW7
  4245.              << S.hk[kategorie_t::Einwaerts]/S.hk[kategorie_t::Auswaerts]
  4246.              << SPRACHE(" Ein-/Ausw" strAe "rts  ", " inward/outward ")
  4247.              << SP3 << SW7 << (S.hk[kategorie_t::Einwaerts]+
  4248.                                S.hk[kategorie_t::Auswaerts])
  4249.              << SPRACHE(" Ein- oder ausw" strAe "rts un ",
  4250.                         " inward or outward bot ")
  4251.              << SP1 << SW4 << S.hpos[0][zeilen_t::Untere_Zeile] << " "
  4252.              << SW4 << S.hpos[1][zeilen_t::Untere_Zeile] << "\n";
  4253.    std::cout << str[3] << " " << SP3  << SW7 << S.hnachbar
  4254.              << SPRACHE(" benachbart     ", " adjacent       ")
  4255.              // Die Normierung von hnachbar ist fragwürdig.
  4256.              << SW7 << S.hsnachbar
  4257.              << SPRACHE(" Shift-benachbart  sum", " shift adjacent    sum")
  4258.              << SP1 << SW5 << S.hlinks << SW5 << S.hrechts << "\n";
  4259.    if(h.mit_trigrammen()){
  4260.       std::cout << str[4] << " " << SP3  << SW7 << S.hkeinhw
  4261.                 << SPRACHE(" kein Handwechs.", " no hand altern.")
  4262.                 << SP3 << SW7 << S.hdoppelhw
  4263.                 << SPRACHE(" zwei Handwechsel", " two hand altern.")
  4264.                 << "\n " << lspalte
  4265.                 << SP3  << SW7 << S.hwippe
  4266.                 << SPRACHE(" Wippe          ", " seesaw         ")
  4267.                 << SP3 << SW7 << S.hi2[kategorie_t::Kollision]
  4268.                 << SPRACHE(" IndirKollision", " indir same finger")
  4269.                 << "\n";
  4270.       str[4] = lspalte;
  4271.    }
  4272.  
  4273.    for(const auto& i : S.hk_benutzer){
  4274.       std::cout << str[4] << " " << SP3 << SW7 << i.second << " "
  4275.                 << tastatur.benutzerkategorie_name(i.first) << "\n";
  4276.       str[4] = lspalte;
  4277.    }
  4278.    for(const auto& i : S.hs_benutzer){
  4279.       std::cout << str[4] << " " << SP3 << SW7 << i.second << " Shift-"
  4280.                 << tastatur.benutzerkategorie_name(i.first) << "\n";
  4281.       str[4] = lspalte;
  4282.    }
  4283.    for(const auto& i : S.ht_benutzer){
  4284.       std::cout << str[4] << " " << SP3 << SW7 << i.second << " "
  4285.                 << tastatur.benutzerkategorie_name(i.first) << "\n";
  4286.       str[4] = lspalte;
  4287.    }
  4288.  
  4289.    std::cout << str[4];
  4290.    for(int i = 0; i < nfinger; ++i){
  4291.       if(S.mitfinger[i])
  4292.          std::cout << " " << SW4 << SP1 << S.hfinger[i];
  4293.       else
  4294.          std::cout << " --.-";
  4295.    }
  4296.    std::cout << " Sh" << SW5 << S.hslinks << SW5 << S.hsrechts << "\n";
  4297.  
  4298.    if(ngrammakkumlimit[0] >= 0){
  4299.       std::cout << SPRACHE("  Kollision/Fi. ", "  same fing/fi. ");
  4300.       for(int i = 0; i < nfinger; ++i){
  4301.          if(S.mitfinger[i])
  4302.             std::cout << " " << SP2 << SW4 << S.hkollision1[i];
  4303.          else
  4304.             std::cout << "     ";
  4305.       }
  4306.       std::cout << " Sh" << SP2;
  4307.       for(int s = 0; s < nshift; ++s) std::cout << SW5 << S.hskollision1[s];
  4308.  
  4309.       std::cout << SPRACHE("\n  \" \" Sprung>=2 ", "\n  \" \" jump >= 2 ");
  4310.       for(int i = 0; i < nfinger; ++i){
  4311.          if(S.mitfinger[i])
  4312.             std::cout << SP2 << SW5 << S.hkollision2[i];
  4313.          else
  4314.             std::cout << "     ";
  4315.       }
  4316.       std::cout << " Sh" << SP2;
  4317.       for(int s = 0; s < nshift; ++s) std::cout << SW5 << S.hskollision2[s];
  4318.  
  4319.       std::cout << SPRACHE("\n  benachbart/F.paar", "\n  adjacent/fin.pair");
  4320.       for(int i = 0; i < nfinger-1; ++i){
  4321.          if(S.mitfinger[i] && S.mitfinger[i+1])
  4322.             std::cout << SP2 << SW5 << S.hnachbar1[i];
  4323.          else
  4324.             std::cout << "     ";
  4325.       }
  4326.       std::cout << "   Sh" << SP2;
  4327.       for(int s = 0; s < nshift; ++s) std::cout << SW5 << S.hsnachbar1[s];
  4328.  
  4329.       std::cout << SPRACHE("\n  \" \" Ze.sprung>=2 ",
  4330.                            "\n  \" \" row jump >=2 ");
  4331.       for(int i = 0; i < nfinger-1; ++i){
  4332.          if(S.mitfinger[i] && S.mitfinger[i+1])
  4333.             std::cout << SP2 << SW5 << S.hnachbar2[i];
  4334.          else
  4335.             std::cout << "     ";
  4336.       }
  4337.       std::cout << "   Sh" << SP2;
  4338.       for(int s = 0; s < nshift; ++s) std::cout << SW5 << S.hsnachbar2[s];
  4339.       std::cout << "\n";
  4340.    }
  4341.  
  4342.    const haeufigkeit_t htot[3] = { S.h2tot, S.hs2tot, S.h3tot };
  4343.    for(int t = 0; t < 3; ++t){
  4344.       if(ngrammakkumlimit[t] <= 0) continue;
  4345.       for(int s = 0; s < 2; ++s){
  4346.          const auto& hwb = S.ngramm[t][s];
  4347.          if(hwb.size() == 0) continue;
  4348.          double sum = 0;
  4349.          std::cout << SPRACHE("\n  % alle  % ", "\n  % all   % ")
  4350.                    << (t < 2
  4351.                        ? (s ? SPRACHE("rechts","right ")
  4352.                             : SPRACHE("links "," left "))
  4353.                        : (s ? SPRACHE("2 H.w.","2 h.a.")
  4354.                             : SPRACHE("0 H.w.","0 h.a.")))
  4355.                    << SPRACHE(" summiert\n", " cumulative\n");
  4356.          for(auto i = hwb.crbegin();
  4357.              i != hwb.crend() && sum < S.hrel[t][s]*ngrammakkumlimit[t]; ++i){
  4358.             const double aa = 100*i->first;
  4359.             sum += i->first;
  4360.             std::cout << SP3 << SW7 << aa/htot[t] << " "
  4361.                       << SP3 << SW7 << aa/S.hrel[t][s]
  4362.                       << " ("<< SP3 << SW7 << 100*sum/S.hrel[t][s]
  4363.                       <<"): " << i->second << "\n";
  4364.          }
  4365.       }
  4366.    }
  4367.  
  4368.    if(handeinsatzlimit >= 0 && wortliste.size())
  4369.       handeinsaetze(b, tastatur, kodierung, wortliste, handeinsatzlimit);
  4370.    std::cout << std::endl;
  4371.  
  4372.    if(als_fixeszeichen){
  4373.       const std::string einfach = "'", doppelt = "\"";
  4374.  
  4375.       for(int i = 0; i < tastatur.nvariabel(); ++i){
  4376.          const std::string z0 = kodierung.txt(b[i], 0);
  4377.          const std::string z1 = kodierung.txt(b[i], 1);
  4378.          const std::string anf = z0 != einfach && z1 != einfach
  4379.             ? einfach
  4380.             : (z0 != doppelt && z1 != doppelt ? doppelt : "!");
  4381.          std::cout << "FixesZeichen " << utf32_in_ausgabe(tastatur.name(i))
  4382.                    << " " << anf << z0 << z1 << anf << std::endl;
  4383.       }
  4384.       std::cout << std::endl;
  4385.    }
  4386. }
  4387.  
  4388. void schreibe_belegung(const belegung_t b, double A, int nv,
  4389.                        const Kodierung& kodierung, const std::u32string* name)
  4390. {
  4391.    constexpr int zbreite = 9;
  4392.    char bewertung[zbreite+ntaste*4+2];
  4393.    sprintf(bewertung, "%#8.7g", 100*A);
  4394.    char* s = bewertung+(zbreite-1);
  4395.    *s++ = ' ';
  4396.    for(int i = 0; i < nv; ++i){
  4397.       const std::string& z = kodierung.bevorzugt(b[i]);
  4398.       for(const char* j = z.c_str(); *j;) *s++ = *j++;
  4399.    }
  4400.    if(name){
  4401.       *s = 0;
  4402.       std::cout << bewertung;
  4403.       std::cout << "   " << utf32_in_ausgabe(*name) << std::endl;
  4404.    }else{
  4405.       *s++ = '\n'; *s = 0;
  4406.       std::cout << bewertung;
  4407.    }
  4408. }
  4409.  
  4410. void
  4411. schreibe_zyklen(const belegung_t b1, const belegung_t b2,
  4412.                 const Kodierung& kodierung)
  4413. {
  4414.    belegung_t zyklus;
  4415.  
  4416.    for(int i = 0; i < ntaste; ++i){
  4417.       const unsigned char z1 = b1[i];
  4418.       zyklus[z1] = b2[i];
  4419.    };
  4420.  
  4421.    for(int i = 0; i < ntaste; ++i){
  4422.       const unsigned char z1 = zyklus[i];
  4423.       if(z1 == ntaste) continue; // Wurde schon abgehandelt.
  4424.       if(z1 == i) continue; // triviale Zyklen ignorieren.
  4425.       std::cout << "  ";
  4426.       unsigned char z = z1;
  4427.       do{
  4428.          const std::string& txt = kodierung.bevorzugt(z);
  4429.          std::cout << (txt == strNBS ? "_" : txt.c_str());
  4430.          const unsigned char z2 = zyklus[z];
  4431.          zyklus[z] = ntaste;
  4432.          z = z2;
  4433.       }while(z != z1);
  4434.    }
  4435. }
  4436.  
  4437. //--------------- src/vollkorpus.cc ---------------
  4438. //#include "vollkorpus.hh"
  4439.  
  4440. //#include "trennen.hh"
  4441. //#include "utfhilfe.hh"
  4442. #include <fstream>
  4443.  
  4444. void lies_vollkorpus(const std::string& name, const std::string* trennmuster,
  4445.                      std::unordered_map<std::u32string, zaehl_t>& wl,
  4446.                      std::unordered_map<uint64_t, zaehl_t> uh[3])
  4447. {
  4448.    bool utf8 = true, geaendert = false;
  4449.    do{
  4450.       for(size_t i = 0; i < 3; ++i) uh[i].clear();
  4451.       wl.clear();
  4452.  
  4453.       std::unique_ptr<Naechstes_zeichen> nz
  4454.          (trennmuster ? new hole_mit_trennung(name, *trennmuster, utf8)
  4455.                       : new Naechstes_zeichen(name, utf8));
  4456.  
  4457.       std::u32string w;  w.reserve(20);
  4458.       uint64_t z1 = 0, z2 = 0;
  4459.       while(const char32_t c = nz->get()){
  4460.          if(c < U' ' || c == U'\u00ad'){
  4461.             if(w.length()) ++wl[w];
  4462.             w.clear();
  4463.             z1 = z2 = 0;
  4464.             continue;
  4465.          }else if(c == U' '){
  4466.             if(w.length()) ++wl[w];
  4467.             w.clear();
  4468.          }else w += c;
  4469.  
  4470.          const uint64_t z3 = utf_maske & c;
  4471.          ++uh[0][z3];
  4472.          if(z2){
  4473.             z2 = (z2<<21) | z3;
  4474.             ++uh[1][z2];
  4475.             if(z1) ++uh[2][(z1<<21) | z3];
  4476.             z1 = z2;
  4477.          }
  4478.          z2 = z3;
  4479.       }
  4480.       if(w.length()) ++wl[w];
  4481.  
  4482.       geaendert = nz->encoding_geaendert();
  4483.       utf8 = false;
  4484.    }while(geaendert);
  4485. }
  4486.  
  4487. //--------------- src/wortliste.cc ---------------
  4488. //#include "wortliste.hh"
  4489.  
  4490. //#include "Eingabestream.hh"
  4491. //#include "Haeufigkeit.hh"
  4492. //#include "Kodierung.hh"
  4493. //#include "utfhilfe.hh"
  4494. #include <iostream>
  4495.  
  4496. namespace {
  4497.  
  4498. void wort_in_wortliste(
  4499.    const std::string wort, unsigned h,
  4500.    std::unordered_map<std::string, haeufigkeit_t>& wortliste)
  4501. {
  4502.    if(!wort.size()) return;
  4503.    const auto i = wortliste.find(wort);
  4504.    if(i == wortliste.end())
  4505.       wortliste[wort] = h;
  4506.    else i->second += h;
  4507. }
  4508.  
  4509. }
  4510.  
  4511. void lies_wortliste(const std::string& name,
  4512.                     std::unordered_map<std::u32string, zaehl_t>& wl,
  4513.                     bool muss_existieren){
  4514.    bool utf8 = true, geaendert = false;
  4515.    do{
  4516.       wl.clear();
  4517.       Eingabestream f(name, utf8, muss_existieren);
  4518.       while(f.echte_neuezeile()){
  4519.          const zaehl_t h = hole_zahl(f, 0, 1e15);
  4520.          const bool zwischen = f.ist_zwischenraum();
  4521.          const size_t rest = f.restzeichen();
  4522.          if(!(zwischen && rest > 1)){
  4523.             std::cerr << SPRACHE("Nach der Zahl werden ein Leerzeichen und "
  4524.                                  "mindestens ein weiteres Zeichen erwartet.",
  4525.                                  "After the number, one space character and at "
  4526.                                  "least one more character are expected.")
  4527.                       << std::endl;
  4528.             f.fehler();
  4529.          }
  4530.  
  4531.          f.uebergehen();
  4532.          std::u32string idx; idx.reserve(rest-1);
  4533.          for(size_t i = 1; i < rest; ++i)
  4534.             idx.push_back(f.lies_in_zeile());
  4535.          wl[idx] += h;
  4536.       }
  4537.       geaendert = f.encoding_geaendert();
  4538.       utf8 = false;
  4539.    }while(geaendert);
  4540. }
  4541.  
  4542. void lies_wortliste(const std::string name,
  4543.                     std::unordered_map<std::string, haeufigkeit_t>& wortliste,
  4544.                     const Kodierung& kodierung)
  4545. {
  4546.    std::unordered_map<std::u32string, zaehl_t> wl;
  4547.    lies_wortliste(name, wl, true);
  4548.    for(const auto& i : wl){
  4549.       const haeufigkeit_t h = i.second;
  4550.       std::string konvertiert;
  4551.       for(const char32_t z : i.first){
  4552.          const bool zwischenraum = ist_zwischenraum(z);
  4553.          const auto c = kodierung.position(z);
  4554.          if(c.first < 0 || zwischenraum){
  4555.             wort_in_wortliste(konvertiert, h, wortliste);
  4556.             konvertiert.erase();
  4557.          }else konvertiert
  4558.                   .push_back(Haeufigkeit::index_ein_flach(c.first, c.second));
  4559.       }
  4560.       wort_in_wortliste(konvertiert, h, wortliste);
  4561.    }
  4562. }
  4563.  
  4564. //--------------- src/Aufwandstabelle.cc ---------------
  4565. //#include "Aufwandstabelle.hh"
  4566.  
  4567. //#include "Kodierung.hh"
  4568. //#include "Konfiguration.hh"
  4569. //#include "Tastatur.hh"
  4570. //#include "utfhilfe.hh"
  4571. #include <iomanip>
  4572. #include <iostream>
  4573. #include <string>
  4574.  
  4575. Aufwandstabelle::
  4576. Aufwandstabelle(bool mittrigrammen, const Tastatur& tast,
  4577.                 const Konfiguration& A)
  4578.    : aunbekannt(A.unbekannt()), mit_trigrammen(mittrigrammen), tastatur(tast)
  4579. {
  4580.    // werden Trigramme mit berücksichtigt, steigt der Gesamtaufwand.  Daher
  4581.    // sollte das Gewicht für die Fingerbelastung auch entsprechend grösser
  4582.    // gewählt werden:
  4583.    const double mult_mf_tri = 1+A.indirekt(1e100);
  4584.    double fh_tot = 0;
  4585.    // Wenn einem Finger nur fest belegte Tasten zugeordnet sind legen wir für
  4586.    // ihn keine Zielhäufigkeit fest.
  4587.    for(int i = 0; i < nfinger; ++i)
  4588.       if(!tastatur.finger_fix(i)) fh_tot += A.zielhaeufigkeit(i);
  4589.    for(int i = 0; i < nfinger; ++i){
  4590.       finger_zielhaeufigkeit[i] = A.zielhaeufigkeit(i)/fh_tot;
  4591.       multfinger[i] = mit_trigrammen
  4592.          ? mult_mf_tri*A.multfinger(i) : A.multfinger(i);
  4593.       if(tastatur.finger_fix(i)) finger_zielhaeufigkeit[i] = multfinger[i] = 0;
  4594.    }
  4595.  
  4596.    for(int i = 0; i < ntaste; ++i){
  4597.       // Basisaufwand für Kleinbuchstaben:
  4598.       a1[i][0] = tastatur.lageaufwand(i);
  4599.       // Für Grossbuchstaben muss zusätzlich noch Shift gedrückt werden:
  4600.       const int shift_i = tastatur.shifttaste(i);
  4601.       a1[i][1] = a1[i][0]+tastatur.lageaufwand(tastatur.shifttaste(i))
  4602.          +A.bigrammaufwand(shift_i, i, tastatur);
  4603.    }
  4604.  
  4605.    // Wir bewerten n-Gramme bis zu drei Tasten, dabei wird Shift als Taste
  4606.    // mitgezählt.  Deshalb fallen Tasten-n-Gramme nicht mit Zeichen-n-Grammen
  4607.    // zusammen, und die Zählung wird verwickelt.  Shift ist zudem speziell: Zum
  4608.    // einen, weil die Shifttaste durch die zu shiftende Taste festgelegt ist,
  4609.    // zum anderen, weil Shift vor der zu shiftenden Taste angeschlagen und
  4610.    // gehalten wird, bis die zu shiftende Taste angeschlagen wurde.
  4611.    //
  4612.    // Schauen wir uns die Eingabe von drei Zeichen an.  Für jedes Zeichen muss
  4613.    // eine Zeichentaste (b) und gegebenenfalls Shift (s) angeschlagen werden.
  4614.    // Es gibt also acht Möglichkeiten, die man in die darin enthaltenen
  4615.    // Trigramme aufspalten kann:
  4616.    //
  4617.    // T1:   b   b   b  = bbb
  4618.    // T2: s-b   b   b  = sbb + bbb
  4619.    // T3:   b s-b   b  = bsb + sbb
  4620.    // T4: s-b s-b   b  = sbs + bsb + sbb
  4621.    // T5:   b   b s-b  = bbs + bsb
  4622.    // T6: s-b   b s-b  = sbb + bbs + bsb
  4623.    // T7:   b s-b s-b  = bsb + sbs + bsb
  4624.    // T8: s-b s-b s-b  = sbs + bsb + sbs + bsb
  4625.    //
  4626.    // Nur für bbb (in T1 und T2) und bbs (in T5 und T6) müssen wir tatsächlich
  4627.    // die Häufigkeiten von Zeichentrigrammen betrachten.  Für sbb, bsb und sbs
  4628.    // genügt die Betrachtung von Zeichenbigrammen.  Wir handeln sie daher mit
  4629.    // den Bigrammen ab.  Für Zeichenbigramme gibt es vier Möglichkeiten, und
  4630.    // wir können sie in die enthaltenen Tastenbi- und trigramme aufteilen:
  4631.    //
  4632.    // B1:   b   b = bb
  4633.    // B2: s-b   b = sbb + bb
  4634.    // B3:   b s-b = bsb + bs
  4635.    // B4: s-b s-b = sbs + bsb + bs
  4636.    //
  4637.    // wobei die Bigramme sb bereits weggelassen sind, denn das sind
  4638.    // verkappte 1-Gramme (Grossbuchstaben).
  4639.    //
  4640.    // Wenn wir die Zeichentrigrammhäufigkeiten für Trigramme der Form bbb
  4641.    // und bbs genauer betrachten sehen wir:
  4642.    // - Es spielt keine Rolle, ob der erste Buchstabe gross oder klein
  4643.    //   ist, denn ein allfälliges Shift liegt vor (also ausserhalb) des
  4644.    //   Trigramms.  Wir können die entsprechenden Häufigkeiten summieren.
  4645.    // - Der zweite Buchstabe ist immer klein.
  4646.    // - Für bbb ist der dritte Buchstabe klein, für bbs gross.
  4647.    //
  4648.    // Die Zeichenbigrammhäufigkeiten haben wir nach den vier
  4649.    // Klein/Gross-Kombinationen aufgespalten.
  4650.    //
  4651.    // Für die Bewertung ist noch zu bemerken, dass sbb und sbs nicht mit bbb,
  4652.    // bbs und bsb gleichzusetzen sind, weil das s am Anfang gehalten werden
  4653.    // muss, bis das folgende b getippt ist.  Wir nennen nennen Tastentrigramme
  4654.    // der Art sbb und sbs «Shift-Bigramme».
  4655.  
  4656.    // Fülle die Bigrammtabellen.
  4657.    for(int i = 0; i < ntaste; ++i){
  4658.       const int shift_i = tastatur.shifttaste(i);
  4659.       for(int j = 0; j < ntaste; ++j){
  4660.          // Basisbewertung: Bigramme zwischen normalen Tasten
  4661.          a2[i][j][0][0] = A.bigrammaufwand(i, j, tastatur);// bb
  4662.  
  4663.          // Aufwand, falls das erste Zeichen im Bigramm zusammen mit Shift
  4664.          // gedrückt wird.
  4665.          const double sa1 = A.bigrammaufwand(shift_i, j, tastatur);
  4666.          const double sf1 = A.shiftindirekt(sa1);
  4667.          a2[i][j][1][0]  = sf1*sa1                         // sbb
  4668.             +A.bigrammaufwand(i, j, tastatur);             // bb
  4669.  
  4670.          // Aufwand, falls das zweite Zeichen im Bigramm zusammen mit Shift
  4671.          // gedrückt wird.
  4672. #ifndef OHNE2SHIFT
  4673.          const int shift_j = tastatur.shifttaste(j);
  4674.          a2[i][j][0][1] =
  4675.             A.trigrammaufwand(i, shift_j, j, tastatur)    // bsb
  4676.             +A.bigrammaufwand(i, shift_j, tastatur);      // bs
  4677.  
  4678.          // Aufwand wenn beide Zeichen mit Shift gedrückt werden.
  4679.          const double sa12 = A.bigrammaufwand(shift_i, shift_j, tastatur);
  4680.          const double sf12 = A.shiftindirekt(sa12);
  4681.          a2[i][j][1][1] = sa12*sf12                       // sbs
  4682.             +A.trigrammaufwand(i, shift_j, j, tastatur)   // bsb
  4683.             +A.bigrammaufwand(i, shift_j, tastatur);      // bs
  4684. #endif
  4685.       }
  4686.    }
  4687.  
  4688.    bool mit_verwechslungspotenzial = false;
  4689.    mit_vorlieben = mit_aehnlichkeit = false;
  4690.    for(int i = 0; i < ntaste; ++i){
  4691.       for(int j = 0; j < ntaste; ++j){
  4692.          va2[i][j] = A.verwechslungspotenzial(i, j, tastatur);
  4693.          if(va2[i][j] != 0) mit_verwechslungspotenzial = true;
  4694.          ae[i][j] = A.aehnlichkeit(i, j);
  4695.          if(ae[i][j] != 0) mit_aehnlichkeit = true;
  4696.          vl[i][j] = A.vorliebe(i, j);
  4697.          if(vl[i][j] != 0) mit_vorlieben = true;
  4698.       }
  4699.    }
  4700.    mit_aehnlichkeit &= mit_verwechslungspotenzial;
  4701.    vl_knick = -A.vorliebenknick();
  4702.  
  4703.    if(!mit_trigrammen) return;
  4704.    // Fülle die Trigrammtabellen.
  4705.    for(int k = 0; k < ntaste; ++k){
  4706.       const int shift_k = tastatur.shifttaste(k);
  4707.       for(int i = 0; i < ntaste; ++i){
  4708.          for(int j = 0; j < ntaste; ++j){
  4709.             a3[i][j][k][0] =
  4710.                A.trigrammaufwand(i,j,k,tastatur);                    // bbb
  4711.             a3[i][j][k][1] =
  4712.                A.trigrammaufwand(i,j,shift_k,tastatur);              // bbs
  4713.          }
  4714.       }
  4715.    }
  4716. }
  4717.  
  4718. void
  4719. Aufwandstabelle::
  4720. anzeigen(const Kodierung& kodierung, const Konfiguration& A) const
  4721. {
  4722.    // Keine Übersetzung ins Englische, die Wörter hier sind Schlüsselwörter.
  4723.    const std::string einfachesanfuehrungszeichen = "'";
  4724.    for(int i = 0; i < ntaste+nshift; ++i){
  4725.       const std::string ni = utf32_in_ausgabe(tastatur.name(i));
  4726.       for(int j = 0; j < ntaste+nshift; ++j){
  4727.          const std::string nj = utf32_in_ausgabe(tastatur.name(j));
  4728.          const double b = A.bigrammaufwand(i, j, tastatur);
  4729.          if(b != 0.)
  4730.             std::cout << "Bigramm " << ni << " " << nj << " "
  4731.                       << std::setprecision(16) << b << "\n";
  4732.  
  4733.          if(mit_trigrammen){
  4734.             for(int k = 0; k < ntaste+nshift; ++k){
  4735.                const double t = A.trigrammaufwand(i, j, k, tastatur);
  4736.                if(t != 0.)
  4737.                   std::cout << "Trigramm " << ni << " " << nj << " "
  4738.                             << utf32_in_ausgabe(tastatur.name(k)) << " "
  4739.                             << std::setprecision(16) << t << "\n";
  4740.             }
  4741.          }
  4742.  
  4743.          if(i < ntaste && j < ntaste){
  4744.             const double v = verwechslungspotenzial(i, j);
  4745.             if(v != 0.)
  4746.                std::cout << "Verwechslungspotenzial " << ni << " " << nj << " "
  4747.                          << std::setprecision(16) << v << "\n";
  4748.             if(mit_vorlieben && vorliebe(i, j) != 0.){
  4749.                const std::string& z = kodierung.bevorzugt(i);
  4750.                const std::string a = z == einfachesanfuehrungszeichen
  4751.                   ? "\"" : einfachesanfuehrungszeichen;
  4752.                std::cout << "Vorliebe " << a << z << a << " "
  4753.                          << -vorliebe(i, j) << " " << nj << "\n";
  4754.             }
  4755.          }
  4756.       }
  4757.    }
  4758. #ifdef EXPERIMENTELL
  4759.    if(mit_vorlieben && A.vorliebenknick() > -1e15)
  4760.       std::cout << "VorliebeKnick " << A.vorliebenknick() << "\n";
  4761. #endif // EXPERIMENTELL
  4762. }
  4763.  
  4764. //--------------- src/ngramme.cc ---------------
  4765. //#include "ngramme.hh"
  4766.  
  4767. //#include "Eingabestream.hh"
  4768. //#include "typen.hh"
  4769. //#include "utfhilfe.hh"
  4770. //#include "vollkorpus.hh"
  4771. //#include "wortliste.hh"
  4772. #include <cstdint>
  4773. #include <fstream>
  4774. #include <iomanip>
  4775. #include <map>
  4776. #include <unordered_map>
  4777.  
  4778. namespace {
  4779.  
  4780. void lies_ngramme(const std::string& name, size_t N,
  4781.                   std::unordered_map<uint64_t, zaehl_t>& uh){
  4782.    bool utf8 = true, geaendert = false;
  4783.    do{
  4784.       uh.clear();
  4785.       Eingabestream tabelle(name, utf8, true);
  4786.       while(tabelle.echte_neuezeile()){
  4787.          const zaehl_t h = hole_zahl(tabelle, 0, 1e15);
  4788.          pruefe_leer_dann_N(tabelle, N);
  4789.          tabelle.uebergehen();
  4790.          uint64_t k = 0;
  4791.          for(size_t i = 0; i < N; ++i)
  4792.             k = (k<<21) | (tabelle.lies_in_zeile() & utf_maske);
  4793.          uh[k] += h;
  4794.       }
  4795.       geaendert = tabelle.encoding_geaendert();
  4796.       utf8 = false;
  4797.    }while(geaendert);
  4798. }
  4799.  
  4800. template <typename S>
  4801. void ngramme_ausgeben(const std::unordered_map<S, zaehl_t>& uh,
  4802.                       const std::string& name)
  4803. {
  4804.    if(uh.size() == 0) return;
  4805.    std::ofstream o(name.c_str(), std::ios_base::out);
  4806.    std::multimap<zaehl_t, std::string> g;
  4807.    for(const auto& i : uh)
  4808.       g.insert(std::make_pair(i.second, utf32_in_utf8(i.first)));
  4809.    for(auto i = g.crbegin(); i != g.crend(); ++i)
  4810.       o << std::setprecision(16) << i->first << " " << i->second << "\n";
  4811. }
  4812.  
  4813. }
  4814.  
  4815. void erzeuge_ngrammtabellen(const std::vector<std::string>& namen)
  4816. {
  4817.    std::unordered_map<std::u32string, zaehl_t> wl;
  4818.    std::unordered_map<uint64_t, zaehl_t> uh[3];
  4819.    const std::string ext[] = { ".1", ".2", ".3", ".wl" };
  4820.  
  4821.    if(namen.size() < 3){
  4822.       if(namen.size() == 1)
  4823.          lies_vollkorpus(namen[0], nullptr, wl, uh);
  4824.       else
  4825.          lies_vollkorpus(namen[1], &namen[0], wl, uh);
  4826.    }else{
  4827.       for(size_t i = 0; i+1 < namen.size(); ++i){
  4828.          for(int j = 0; j < 3; ++j){
  4829.             std::unordered_map<uint64_t, zaehl_t> uhj;
  4830.             lies_ngramme(namen[i]+ext[j], j+1, uhj);
  4831.             if(uh[j].size()){
  4832.                for(const auto& k : uhj) uh[j][k.first] += uhj[k.second];
  4833.             }else{
  4834.                uh[j].swap(uhj);
  4835.             }
  4836.          }
  4837.          std::unordered_map<std::u32string, zaehl_t> wli;
  4838.          lies_wortliste(namen[i]+ext[3], wli, false);
  4839.          if(wl.size()){
  4840.             for(const auto& k : wli) wl[k.first] += k.second;
  4841.          }else{
  4842.             wl.swap(wli);
  4843.          }
  4844.       }
  4845.    }
  4846.  
  4847.    const std::string base = namen.back();
  4848.    for(int j = 0; j < 3; ++j) ngramme_ausgeben(uh[j], base+ext[j]);
  4849.    ngramme_ausgeben(wl, base+ext[3]);
  4850. }
  4851.  
  4852. //--------------- src/html_markup.cc ---------------
  4853. //#include "html_markup.hh"
  4854.  
  4855. //#include "Eingabestream.hh"
  4856. //#include "Kodierung.hh"
  4857. //#include "Naechstes_zeichen.hh"
  4858. //#include "Tastatur.hh"
  4859. //#include "string_in_belegung.hh"
  4860. //#include "utfhilfe.hh"
  4861. #include <cmath>
  4862. #include <fstream>
  4863. #include <iostream>
  4864. #include <map>
  4865. #include <utility>
  4866. #include <vector>
  4867.  
  4868. namespace {
  4869.  
  4870. void add_bigramm_markup(int f, int vf, bool shift,
  4871.                         std::string& stil)
  4872. {
  4873.    char c = 0;
  4874.    if(vf == f){
  4875.       c = 'K';
  4876.    }else if(std::abs(vf-f) == 1){
  4877.       c = 'N';
  4878.    }else if(vf*f > 0){
  4879.       c = (std::abs(f) > std::abs(vf)) ? 'A' : 'E';
  4880.    }
  4881.    if(c){
  4882.       if(stil.length()) stil.push_back(' ');
  4883.       stil.push_back(shift ? ('a'-'A')+c : c);
  4884.    }
  4885. }
  4886.  
  4887. class Hole_mit_ersetzen {
  4888.    Naechstes_zeichen nz;
  4889.    const Kodierung& kodierung;
  4890.    const std::vector<std::pair<int, int>>* ersatz = nullptr;
  4891.    size_t ersatz_i = 0;
  4892. public:
  4893.    Hole_mit_ersetzen(const std::string& name, const Kodierung& kodierung,
  4894.                      bool utf8)
  4895.       : nz(name, utf8), kodierung(kodierung){}
  4896.  
  4897.    std::pair<char32_t, std::pair<int,int>> get() {
  4898.       if(ersatz && ersatz_i < ersatz->size()){
  4899.          const auto& ie = (*ersatz)[ersatz_i++];
  4900.          return std::make_pair(kodierung.uchar(ie.first, ie.second), ie);
  4901.       }
  4902.       const char32_t z = nz.get();
  4903.       const auto ie = kodierung.position(z);
  4904.       if(ie.first < 0){
  4905.          ersatz = kodierung.ersatz(z);
  4906.          ersatz_i = 0;
  4907.          if(ersatz) return get();
  4908.       }
  4909.       return std::make_pair(z, ie);
  4910.    }
  4911.  
  4912.    bool encoding_geaendert() const { return nz.encoding_geaendert(); }
  4913. };
  4914.  
  4915. }
  4916.  
  4917. void html_markup(const std::string& textfile, const Kodierung& kodierung,
  4918.                  const Tastatur& tastatur, const std::string& referenztastatur)
  4919. {
  4920.    const std::map<char32_t, std::string> escape =
  4921.       {{ U'<', "&lt;" },{ U'>', "&gt;" },{ U'&', "&amp;" },{ U'\u00A0', " " } };
  4922.  
  4923.    bool utf8 = true, nochmal;
  4924.    do{
  4925.       nochmal = false;
  4926.       Eingabestream liste(referenztastatur, true);
  4927.       std::ofstream html(textfile+".html");
  4928.       html << "<!DOCTYPE html><html><head>\n"
  4929.          "<meta http-equiv=\"Content-Type\" content=\"text/html; "
  4930.          "charset=utf-8\" />\n"
  4931.          "<style type=\"text/css\">\n"
  4932.          ".K{background-color: #FCC;}\n"
  4933.          ".k{text-decoration: underline; text-decoration-color: #F00;}\n"
  4934.          ".N{background-color: #FFA;}\n"
  4935.          ".n{text-decoration: underline; text-decoration-color: #A80;}\n"
  4936.          ".A{background-color: #DDF;}\n"
  4937.          ".a{text-decoration: underline; text-decoration-color: #00F;}\n"
  4938.          ".E{background-color: #DFD;}\n"
  4939.          ".e{text-decoration: underline; text-decoration-color: #0F0;}\n"
  4940.          ".L{color: #008;}\n"
  4941.          ".R{color: #080;}\n"
  4942.          ".G{font-weight: 600;}\n"
  4943.          ".B{font-family: monospace; background-color: #FFF; color: #000;}\n"
  4944.          "</style>\n"
  4945.          "</head><body>\n"
  4946.          SPRACHE("<p class=\"B\">"
  4947.                  "<a class=\"K\">Kollision</a>, "
  4948.                  "<a class=\"k\">Shift-Kollision</a>, "
  4949.                  "<a class=\"N\">Nachbaranschlag</a>, "
  4950.                  "<a class=\"n\">Shift-Nachbaranschlag</a>, "
  4951.                  "<a class=\"A\">Auswärts</a>, "
  4952.                  "<a class=\"a\">Shift-Auswärts</a>, "
  4953.                  "<a class=\"E\">Einwärts</a>, "
  4954.                  "<a class=\"e\">Shift-Einwärts</a>.\n"
  4955.                  ,
  4956.                  "<p class=\"B\">"
  4957.                  "<a class=\"K\">Same finger repetition</a>, "
  4958.                  "<a class=\"k\">Shift-same finger repetition</a>, "
  4959.                  "<a class=\"N\">adjacent finger stroke</a>, "
  4960.                  "<a class=\"n\">Shift-adjacent finger stroke</a>, "
  4961.                  "<a class=\"A\">outwards</a>, "
  4962.                  "<a class=\"a\">Shift-outwards</a>, "
  4963.                  "<a class=\"E\">inwards</a>, "
  4964.                  "<a class=\"e\">Shift-inwards</a>.\n"
  4965.             ) "</p>";
  4966.       while(liste.echte_neuezeile()){
  4967.          std::u32string bs, uname;
  4968.          if(!liste.hole_wort(bs) || !liste.hole_wort(uname)){
  4969.             std::cerr << SPRACHE("Fehlerhaft formatiertes Belegungsfile ",
  4970.                                  "Incorrectly formatted layout file ")
  4971.                       << referenztastatur << std::endl;
  4972.             liste.fehler();
  4973.          }
  4974.  
  4975.          if(!bs.size()) continue;
  4976.          bool fest[ntaste];
  4977.          belegung_t b, ib;
  4978.          string_in_belegung(bs, b, fest, tastatur.nvariabel(), kodierung);
  4979.          for(int i = 0; i < ntaste; ++i) ib[b[i]] = i;
  4980.  
  4981.          html << "<h1>" << utf32_in_utf8(uname) << "</h1>\n<p class=\"B\">";
  4982.          Hole_mit_ersetzen nz(textfile, kodierung, utf8);
  4983.          int vorige_pos = -1, vorige_ebene = -1;
  4984.          std::string voriger_stil;
  4985.          do{
  4986.             const auto zie = nz.get();
  4987.             const auto zeichen = zie.first;
  4988.             nochmal = nz.encoding_geaendert();
  4989.             if(!zeichen || nochmal) break;
  4990.             const auto i = zie.second.first, e = zie.second.second;
  4991.             std::string stil;
  4992.             if(i >= 0 && e >= 0){
  4993.                const auto pos = ib[i];
  4994.                const auto shift = tastatur.shifttaste(pos);
  4995.                const auto f = tastatur.finger(pos), sf = tastatur.finger(shift);
  4996.                if(vorige_pos >= 0){
  4997.                   const auto vshift = tastatur.shifttaste(vorige_pos);
  4998.                   const auto vf = tastatur.finger(vorige_pos);
  4999.                   const auto vsf = tastatur.finger(vshift);
  5000.                   if(vorige_pos != pos) add_bigramm_markup(f, vf, false, stil);
  5001.                   if(vorige_ebene > 0){
  5002.                      // Shift-Buchstabentaste oder Shift-Shift?
  5003.                      const auto nt = e ? shift : pos;
  5004.                      if(vshift != nt)
  5005.                         add_bigramm_markup(e ? sf : f, vsf, true, stil);
  5006.                   }
  5007.                }
  5008.                vorige_pos = pos;  vorige_ebene = e;
  5009.             }else{
  5010.                vorige_pos = vorige_ebene = -1;
  5011.             }
  5012.             if(stil != voriger_stil){
  5013.                if(voriger_stil.size()) html << "</a>";
  5014.                if(stil.size()) html << "<a class=\"" << stil << "\">";
  5015.                voriger_stil.swap(stil);
  5016.             }
  5017.             const auto iesc = escape.find(zeichen);
  5018.             if(iesc == escape.end()){
  5019.                html << utf32_in_utf8(zeichen);
  5020.             }else{
  5021.                html << iesc->second;
  5022.             }
  5023.          }while(true);
  5024.          if(voriger_stil.size()) html << "</a>";
  5025.          html << "</p>\n";
  5026.       }
  5027.       html << "</body></html>\n" << std::endl;
  5028.       utf8 = false;
  5029.    }while(nochmal);
  5030. }
  5031.  
  5032. //--------------- src/trennen.cc ---------------
  5033. //#include "trennen.hh"
  5034.  
  5035. //#include "Eingabestream.hh"
  5036. //#include "Unicode.hh"
  5037. //#include "utfhilfe.hh"
  5038. #include <cassert>
  5039. #include <memory>
  5040.  
  5041. namespace {
  5042.  
  5043. size_t
  5044. lies_trennmuster(const std::string& name,
  5045.                  std::unordered_map<std::u32string, std::vector<char>>& tabelle)
  5046. {
  5047.    size_t maxlen;
  5048.    bool utf8ein = true, nochmal = false;
  5049.    do{
  5050.       tabelle.clear();
  5051.       maxlen = 0;
  5052.       Eingabestream liste(name, utf8ein);
  5053.       while(liste.echte_neuezeile()){
  5054.          std::u32string muster;
  5055.          std::vector<char> trennstellen;
  5056.          bool warziffer = false;
  5057.          while(const char32_t zi = liste.lies_in_zeile()){
  5058.             if(ist_ziffer(zi)){
  5059.                trennstellen.push_back(zi-U'0');
  5060.                warziffer = true;
  5061.             }else{
  5062.                if(!warziffer) trennstellen.push_back(0);
  5063.                muster.append(1, zi);
  5064.                warziffer = false;
  5065.             }
  5066.          }
  5067.          if(!warziffer) trennstellen.push_back(0);
  5068.          assert(trennstellen.size() == muster.size()+1);
  5069.          tabelle[muster] = trennstellen;
  5070.          if(muster.size() > maxlen) maxlen = muster.size();
  5071.       }
  5072.       nochmal = liste.encoding_geaendert();
  5073.       utf8ein = false;
  5074.    }while(nochmal);
  5075.    return maxlen;
  5076. }
  5077.  
  5078. void
  5079. trenne(const std::u32string& wort1,
  5080.        const std::unordered_map<std::u32string, std::vector<char>>& tabelle,
  5081.        size_t maxlen, std::vector<bool>& resultat)
  5082. {
  5083.    static const size_t min_silbenlaenge = 2;
  5084.  
  5085.    // Die Punkte in der Trennmustertabelle markieren Wortanfang und Wortende.
  5086.    std::u32string wort(wort1.size()+2, U'.');
  5087.    for(size_t i = 0; i < wort1.size(); ++i)
  5088.       wort[i+1] = Unicode::get().kleinbuchstabe(wort1[i]);
  5089.    std::vector<char> trennstellen(wort.length()+1, 0);
  5090.    for(size_t i = 0; i < wort.size()-min_silbenlaenge; ++i){
  5091.       const size_t minlaenge = i ? min_silbenlaenge : min_silbenlaenge+1;
  5092.       const size_t len = wort.size()-i;
  5093.       const size_t maxlaenge = len > maxlen ? maxlen : len;
  5094.       // Effizienz spielt keine Rolle...
  5095.       std::u32string muster(wort, i, minlaenge-1);
  5096.       muster.reserve(maxlaenge);
  5097.       for(size_t j = minlaenge; j <= maxlaenge; ++j){
  5098.          muster.append(1, wort[i+j-1]);
  5099.          const auto p = tabelle.find(muster);
  5100.          if(p != tabelle.end()){
  5101.             const std::vector<char>& t = p->second;
  5102.             for(size_t k = 0; k < t.size(); ++k){
  5103.                if(t[k] > trennstellen[i+k]) trennstellen[i+k] = t[k];
  5104.             }
  5105.          }
  5106.       }
  5107.    }
  5108.  
  5109.    if(resultat.size() < wort1.size()) resultat.resize(wort1.size());
  5110.    for(size_t i = 2; i < trennstellen.size()-2; ++i){
  5111.       const bool innen = (i > min_silbenlaenge) &&
  5112.          (i < trennstellen.size()-1-min_silbenlaenge);
  5113.       resultat[i-2] = innen && (trennstellen[i] & 1);
  5114.    }
  5115.    resultat[wort1.size()-1] = true;
  5116. }
  5117.  
  5118. }
  5119.  
  5120.  
  5121. char32_t
  5122. hole_mit_trennung::fuelle_buffer(){
  5123.    wort_i = 0;
  5124.    wort.erase();
  5125.    char32_t c = Naechstes_zeichen::get();
  5126.    while(Unicode::get().ist_buchstabe(c)){
  5127.       wort.append(1, c);
  5128.       c = Naechstes_zeichen::get();
  5129.    }
  5130.    if(wort.size()){
  5131.       const auto i = cache.find(wort);
  5132.       if(i != cache.end())
  5133.          trennstellen = i->second;
  5134.       else{
  5135.          trenne(wort, trennmuster, maxlen, trennstellen);
  5136.          cache[wort] = trennstellen;
  5137.       }
  5138.    }
  5139.    return c;
  5140. }
  5141.  
  5142. hole_mit_trennung::
  5143. hole_mit_trennung(const std::string& name, const std::string& tmfile,
  5144.                   bool utf8)
  5145.    : Naechstes_zeichen(name, utf8)
  5146. {
  5147.    maxlen = lies_trennmuster(tmfile, trennmuster);
  5148.    naechste_ist_trennung = false;
  5149.    rest = fuelle_buffer();
  5150. }
  5151.  
  5152.  
  5153. char32_t
  5154. hole_mit_trennung::
  5155. get(){
  5156.    const char32_t trenner = U'\u00ad';
  5157.    if(naechste_ist_trennung){
  5158.       naechste_ist_trennung = false;
  5159.       return trenner;
  5160.    }
  5161.  
  5162.    if(wort_i < wort.size()){
  5163.       naechste_ist_trennung = trennstellen[wort_i];
  5164.       const char32_t c = wort[wort_i];
  5165.       wort_i++;
  5166.       return c;
  5167.    }
  5168.  
  5169.    if(rest){
  5170.       const char32_t alterrest = rest;
  5171.       rest = fuelle_buffer();
  5172.       naechste_ist_trennung = true;
  5173.       return alterrest;
  5174.    }
  5175.    return 0;
  5176. }
  5177.  
  5178.  
  5179. void markiere_alle_trennstellen(const std::string& ein, const std::string& aus,
  5180.                                 const std::string& trennmuster)
  5181. {
  5182.    bool utf8 = true, nochmal = false;
  5183.    do{
  5184.       std::unique_ptr<Naechstes_zeichen> nz
  5185.          (new hole_mit_trennung(ein, trennmuster, utf8));
  5186.       std::ofstream ausgabe(aus.c_str(), std::ios_base::out);
  5187.       while(char32_t c3 = nz->get()) ausgabe << utf32_in_utf8(c3);
  5188.       nochmal = nz->encoding_geaendert();
  5189.       utf8 = false;
  5190.    }while(nochmal);
  5191. }
  5192.  
  5193. //--------------- src/Grafik.cc ---------------
  5194. //#include "Grafik.hh"
  5195.  
  5196. //#include "Haeufigkeit.hh"
  5197. //#include "Kodierung.hh"
  5198. //#include "Konfiguration.hh"
  5199. //#include "Tastatur.hh"
  5200. #include <cmath>
  5201.  
  5202. Grafik::
  5203. Grafik(const std::string& name, const Tastatur& tast,
  5204.        const Kodierung& kod, const Haeufigkeit& h,
  5205.        const Konfiguration& konfiguration)
  5206.    : grafik(name.c_str(), std::ios_base::out), seite(0),
  5207.      ungerade(false), tastatur(tast), kodierung(kod)
  5208. {
  5209.    constexpr double sk = 1e6;
  5210.    const std::string& Beschreibungsfont = konfiguration.beschreibungsfont();
  5211.    const std::string& Zeichenfont = konfiguration.zeichenfont();
  5212.  
  5213.    // Vorspann
  5214.    grafik <<
  5215.       "%!PS-Adobe-3.0\n"
  5216.       "%%BoundingBox: 0 0 842 595\n"
  5217.       "%%DocumentFonts: " << Beschreibungsfont;
  5218.    if(Beschreibungsfont != Zeichenfont) grafik << " " << Zeichenfont;
  5219.    grafik <<
  5220. "\n%%DocumentData: Clean8Bit\n"
  5221. "%%EndComments\n"
  5222. "%%BeginProlog\n"
  5223. "/transparenz true def  % Verwende Transparenz (nach Konversion in PDF)\n"
  5224. "/alpha 0.5 def         % Grad der Transparenz (1: intranspanent)\n"
  5225. "/mitmini true def      % Zeige Minitastatur unter Buchstaben\n"
  5226. "/mitZahlen true def    % Zeige Haeufigkeiten zusaetzlich als Zahlenwerte an\n"
  5227. "/mitSummen true def    % Zeige summierte Haeufigkeiten mit an\n"
  5228. "/minFuerKurve 0.01 def % Mindestbigrammhaeufigkeit, ab der Kurve gemalt wird\n"
  5229. "/maxkurvendicke 0.25 def   % Maximale Dicke der Kurven\n"
  5230. "/differenzen false def % Zeige Haeufigkeitsdiffenzen fuer Belegungspaare\n"
  5231. "/fhkaestchen 0.25 def % Fingerhaeufigkeit: Haeufigkeit, die Kaestchen fuellt\n"
  5232. "/ghkaestchen 0.4 def  % Gruppenhaeufigkeit ''\n"
  5233. "/bikaestchen 0.02 def % Bigrammhaeufigkeit ''\n"
  5234. "/zhkaestchen 0.2 def  % Zeilenspruenge     ''\n"
  5235. "/kollschritt 1.25 sqrt def % Sprungdistanz-Einteilung bei Kollisionen\n"
  5236. "/beschrx 0.3 def      % X-Position der Beschreibung\n"
  5237. "/beschry -0.7 def     % X-Position der Beschreibung\n"
  5238. "/beschrgr 0.3 def     % Groesse der Beschreibung\n"
  5239. "%\n"
  5240. "/b{def}bind def/B{bind b}bind b"
  5241. "/h1a[";
  5242.  
  5243.    // Variabler Teil: Häufigkeiten, Zeichen, Tasten
  5244.    for(int i = 0; i < tastatur.nvariabel(); ++i)
  5245.       grafik << std::floor(0.5+(h(i,0)+h(i,1))*sk)
  5246.              << (i+1 < tastatur.nvariabel() ? " " : "]");
  5247.    grafik << "B/h1s[";
  5248.    for(int i = 0; i < tastatur.nvariabel(); ++i)
  5249.       grafik << std::floor(0.5+h(i,1)*sk)
  5250.              << (i+1 < tastatur.nvariabel() ? " " : "]");
  5251.    grafik << "B/h2a[";
  5252.    for(int i = 0; i < tastatur.nvariabel(); ++i){
  5253.       grafik << "[";
  5254.       for(int j = 0; j < tastatur.nvariabel(); ++j){
  5255.          double h2 = 0.;
  5256.          for(int ej = 0; ej < nebene2; ++ej)
  5257.             h2 += h(i,0,j,ej)+h(i,1,j,ej);
  5258.          grafik << std::floor(0.5+h2*sk)
  5259.                 << (j+1 < tastatur.nvariabel() ? " " : "]");
  5260.       }
  5261.    }
  5262.    grafik << "]B/h2s[";
  5263.    for(int i = 0; i < tastatur.nvariabel(); ++i){
  5264.       grafik << "[";
  5265.       for(int j = 0; j < tastatur.nvariabel(); ++j){
  5266.          double h2 = 0.;
  5267.          for(int ej = 0; ej < nebene2; ++ej)
  5268.             h2 += h(i,1,j,ej);
  5269.          grafik << std::floor(0.5+h2*sk)
  5270.                 << (j+1 < tastatur.nvariabel() ? " " : "]");
  5271.       }
  5272.    }
  5273.    grafik << "]B/klartext{(";
  5274.    for(int i = 0; i < tastatur.nvariabel(); ++i)
  5275.       grafik << kodierung.psencstr(i);
  5276.    grafik << ")}B/zeilen[";
  5277.    int maxzeile = 0, minzeile = 99;
  5278.    double maxx = -100, minx = 100, maxy = -100, miny = 100;
  5279.    double maxx1 = -100, minx1 = 100, maxy1 = -100, miny1 = 100;
  5280.    for(int i = 0; i < ntaste+nshift; ++i){
  5281.       if(i < ntaste && i >= tastatur.nvariabel()) continue;
  5282.  
  5283.       const auto k = tastatur.tastenkoord(i);
  5284.       const int zeile = zeilen_t::Leerzeichenzeile-tastatur.zeile(i);
  5285.  
  5286.       if(zeile > maxzeile) maxzeile = zeile;
  5287.       if(zeile < minzeile) minzeile = zeile;
  5288.  
  5289.       if(k.x > maxx) maxx = k.x;
  5290.       if(k.x < minx) minx = k.x;
  5291.       if(k.y > maxy) maxy = k.y;
  5292.       if(k.y < miny) miny = k.y;
  5293.  
  5294.       if(i < ntaste){
  5295.          if(k.x > maxx1) maxx1 = k.x;
  5296.          if(k.x < minx1) minx1 = k.x;
  5297.          if(k.y > maxy1) maxy1 = k.y;
  5298.          if(k.y < miny1) miny1 = k.y;
  5299.       }
  5300.  
  5301.       grafik << zeile;
  5302.       if(i+1 < ntaste+nshift) grafik << " ";
  5303.    }
  5304.  
  5305.    // Für die globale Skalierung:
  5306.    const double weite = maxx-minx+1.5;
  5307.    const double hoehe = zeilen_t::Leerzeichenzeile-miny;
  5308.    const double skala = std::min(12.5/weite, std::min(4./hoehe, 1.));
  5309.    // Für die Skalierung der Minitastaturen:
  5310.    const double miniweite = maxx1-minx1+1;
  5311.    const double minihoehe = maxy1-miny1+1;
  5312.    const double minixoff  = minx1-minx;
  5313.    const double miniyoff  = zeilen_t::Leerzeichenzeile-maxy1;
  5314.  
  5315.    grafik << "]B/koordinaten[";
  5316.    for(int i = 0; i < ntaste+nshift; ++i){
  5317.       if(i < ntaste && i >= tastatur.nvariabel()) continue;
  5318.       const auto k = tastatur.tastenkoord(i);
  5319.       double x = k.x-minx;
  5320.       // Verschiebe Daumentasten der Platzeinteilung zuliebe.
  5321.       if(zeilen_t::Leerzeichenzeile == tastatur.zeile(i)){
  5322.          if(tastatur.spalte(i) == 5){
  5323.             x -= 4;
  5324.          }else if(tastatur.spalte(i) == 9){
  5325.             x += 4;
  5326.          }
  5327.       }
  5328.       grafik << "[" << x << " " << zeilen_t::Leerzeichenzeile-k.y << "]";
  5329.    }
  5330.    grafik << "]B/fingertab[";
  5331.    for(int i = 0; i < ntaste+nshift; ++i){
  5332.       if(i < ntaste && i >= tastatur.nvariabel()) continue;
  5333.       grafik << tastatur.finger(i);
  5334.       if(i+1 < ntaste+nshift) grafik << " ";
  5335.    }
  5336.    grafik << "]B/shift[";
  5337.    for(int i = 0; i < tastatur.nvariabel(); ++i){
  5338.       grafik << tastatur.shifttaste(i)+tastatur.nvariabel()-ntaste;
  5339.       if(i+1 < tastatur.nvariabel()) grafik << " ";
  5340.    }
  5341.    grafik << "]B/minzeile " << minzeile
  5342.           << " b/maxzeile " << maxzeile
  5343.           << " b/skala " << skala
  5344.           << " b/minx " << minx
  5345.           << " b/minixoff " << -minixoff
  5346.           << " b/miniyoff " << -miniyoff
  5347.           << " b/minihoehe " << minihoehe
  5348.           << " b/miniweite " << miniweite
  5349.           << " b";
  5350.  
  5351.    std::string meinencoding = "ISOLatin1Encoding";
  5352.    bool umcodieren = false;
  5353.    for(int i = 0; i < tastatur.nvariabel(); ++i)
  5354.       if(kodierung.psglyphname(i).size()) umcodieren = true;
  5355.    if(umcodieren){
  5356.       meinencoding = "meinencoding";
  5357.       grafik << "/meinencoding[ISOLatin1Encoding aload pop]B\n";
  5358.       for(int i = 0; i < tastatur.nvariabel(); ++i){
  5359.          if(kodierung.psglyphname(i).size()){
  5360.             const int n = kodierung.psencoding(i);
  5361.             grafik << meinencoding << " " << n << "/"
  5362.                    << kodierung.psglyphname(i) << " put\n";
  5363.          }
  5364.       }
  5365.    }
  5366.    grafik <<
  5367.       "/schrift{findfont 0.3 scalefont setfont}B"
  5368.       "/neuerfont{findfont dup length dict begin{b}forall/Encoding "
  5369.           << meinencoding << " b currentdict end definefont pop}B"
  5370.       "/BSchrift/" << Beschreibungsfont << " neuerfont\n";
  5371.    if(Beschreibungsfont != Zeichenfont)
  5372.       grafik <<"/ZSchrift/" << Zeichenfont << " neuerfont\n";
  5373.  
  5374.    grafik <<
  5375. "/n " << tastatur.nvariabel() << " b/N " << tastatur.nvariabel()+nshift << " b"
  5376. "/zz 5 b" // zeilenzahl
  5377. "/mf 2 b" // innerster Finger
  5378. "/xf 5 b" // äusserster Finger
  5379. "/tmp 2 array B"
  5380. "/rot{3 2 roll}B"
  5381. "/-rot{3 1 roll}B"
  5382. "/over{1 index}B"
  5383. "/2dup{2 copy}B"
  5384. "/nip{exch pop}B"
  5385. "/tuck{exch over}B"
  5386. "/2drop{pop pop}B"
  5387. "/3drop{2drop pop}B"
  5388. "/2swap{4 2 roll}B"
  5389. "/l{1 sub 0 1 rot}B"
  5390. "/L{length l}B"
  5391. "/R{grestore}B"
  5392. "/S{gsave}B"
  5393. "/T{translate}B"
  5394. "/@{exch get}B"
  5395. "/?{ifelse}B"
  5396. "/max{2dup gt{pop}{nip}?}B"
  5397. "/min{2dup gt{nip}{pop}?}B"
  5398. "/hypot{dup mul exch dup mul add sqrt}B"
  5399. "/lf{dup L{over exch 0 put}for pop}B" // lösche Feld
  5400. "/lF{dup L{over @ lf}for pop}B"       // lösche Felder
  5401. "/arraymax{tuck L{2 index @ max}for nip}B"
  5402. "/arraysumme{tuck L{2 index @ add}for nip}B"
  5403. "/addinsarray{2 index 2 index get add put}B"
  5404. "/addarrays{dup L{2dup get 3 index -rot addinsarray}for 2drop}B"
  5405. "/submaxvonarray{2 index 2 index get exch sub 0 max put}B"
  5406. "/submaxarrays{dup L{2dup get 3 index -rot submaxvonarray}for 2drop}B"
  5407. "/submaxmatrizen{dup L{2dup get 3 index rot get exch submaxarrays}for 2drop}B"
  5408. "/switch{@ exec}B"
  5409. "/buchstabe{over length over le{2drop(Sh)}{1 getinterval}?}B"
  5410. "/zeile{zeilen @}B/finger{fingertab @}B"
  5411. "/buchstabentab[256{-1}repeat]B "
  5412. // Berechnet buchstabentab[buchstabe] = i
  5413. "n l{klartext over get buchstabentab -rot exch put}for"
  5414. "/koordinate{koordinaten @ aload pop}B"
  5415. "/gruppe{dup finger 0 lt{0}{zz}? exch zeile add}B"
  5416. "/klassifiziere{" // taste2 taste1--
  5417.     "2dup eq"
  5418.     "{2drop 1}"
  5419.     "{"
  5420.        "finger exch finger "
  5421.        "2dup mul 0 lt"
  5422.        "{2drop 0}"
  5423.        "{"
  5424.            "2dup eq"
  5425.            "{2drop 6}"
  5426.            "{"
  5427.                "abs exch abs 2dup lt"
  5428.                "{sub abs 1 eq{3}{5}?}"
  5429.                "{sub abs 1 eq{2}{4}?}?"
  5430.            "}?"
  5431.        "}?"
  5432.     "}?"
  5433. "}B"
  5434. "/klassifikationstab["
  5435.     "N l{"
  5436.        "[exch N l{over klassifiziere exch}for pop]"
  5437.     "}for"
  5438. "]B"
  5439. "/bigrammklasse{" // taste1 taste2--
  5440.     "exch klassifikationstab @ @"
  5441. "}B"
  5442. "/einmax 0 h1a arraymax b"
  5443. "/bimax 0 h2a L{h2a @ arraymax}for b"
  5444. "/einsumme 0 h1a arraysumme b"
  5445. "/bisumme 0 h2a L{h2a @ arraysumme}for h2s L{h2s @ arraysumme}for b"
  5446. "/ph1a N array B"
  5447. "/ph1A N array B"
  5448. "/ph1s n array B"
  5449. "/ph2a[N{n array}repeat]B"
  5450. "/ph2A[N{n array}repeat]B"
  5451. "/ph2s[n{n array}repeat]B"
  5452. // Weist TOS gemäss der Belegung permutiertes TOS+1 zu
  5453. "/perm1{" // belegung htab1 ptab1--
  5454.     "n l{"
  5455.        "over exch 4 index over get buchstabentab @ 4 index @ put"
  5456.     "}for 3drop"
  5457. "}B"
  5458. // Weist TOS gemäss der Belegung permutiertes TOS+1 zu
  5459. "/perm2{" // belegung htab2 ptab2
  5460.     "n l{"
  5461.        "3 index over get buchstabentab @ 3 index @ "
  5462.        "2 index rot get 4 index -rot perm1"
  5463.     "}for 3drop"
  5464. "}B"
  5465. "/setze{" // belegung 1a 2a--
  5466.     "rot 2 index 2 index "
  5467.     "2 index h2s ph2s perm2 "
  5468.     "2 index h2a rot perm2 "
  5469.     "over h1s ph1s perm1 "
  5470.     "h1a exch perm1 "
  5471.     "n 1 over " << nshift-1 << " add{"
  5472.        "2 index over 0 put "
  5473.        "over @ lf"
  5474.     "}for "
  5475.     "h1s L{" // 1a 2a i
  5476.        "shift over get 2dup exch "
  5477.        "ph1s @ 5 index -rot addinsarray "
  5478.        "2 index @ exch ph2s @ addarrays"
  5479.     "}for 2drop"
  5480. "}B"
  5481. "/zentriert{dup stringwidth pop -0.5 mul 0 rmoveto show}B"
  5482. "/zzahl{mitZahlen{(000000000000)cvs zentriert}{pop}?}B"
  5483. "/zeigebuchstabe{" // belegungsstring tastenindex
  5484.     "S "
  5485.     "pop 0.5 0.5 moveto "
  5486.    << ((Beschreibungsfont != Zeichenfont)
  5487.        ? "S/ZSchrift schrift zentriert R"
  5488.        : "zentriert")
  5489.    <<
  5490. " 0.2 dup scale "
  5491.     "tmp 1 get 2.5 2.1 moveto zzahl "
  5492.     "R"
  5493. "}B"
  5494. "/skgrau{sqrt 0.95 mul 0.95 exch sub setgray}B"
  5495. "/skrgb{"
  5496.     "4 3 roll sqrt 0.95 mul 0.05 add 4 1 roll "
  5497.     "3{1 sub 3 index mul 1 add rot}repeat "
  5498.     "setrgbcolor pop"
  5499. "}B"
  5500. "/Q{"
  5501.     "0.1 0.1 0.8 0.8 rectfill "
  5502.     "0 setgray tmp 1 get "
  5503.     "0.5 0.12 moveto zzahl"
  5504. "}B"
  5505. "/K{"
  5506.     "0.05 setlinewidth newpath "
  5507.     "0.1 0.1 moveto 0.9 0.9 lineto "
  5508.     "0.1 0.9 moveto 0.9 0.1 lineto stroke"
  5509. "}B"
  5510. "/minitastatur{" // t1 htab
  5511.     "S "
  5512.        "transparenz{/Normal .setblendmode alpha .setshapealpha}if "
  5513.        "0.03 0 0.94 dup minihoehe mul miniweite div rectfill "
  5514.     "R "
  5515.     "dup L{"
  5516.        "S 0.03 0 T "
  5517.        "0.94 miniweite div dup scale "
  5518.        "minixoff miniyoff T "
  5519.        "2dup get dup tmp 1 rot put "
  5520.        "bimax div "
  5521.        "over koordinate T "
  5522.        "3 index rot bigrammklasse"
  5523.        "["
  5524.            "{0 0.7 0 skrgb Q}"
  5525.            "{0 0.7 0 skrgb K}"
  5526.            "{0.9 0.7 0 skrgb Q}"
  5527.            "{0.9 0.9 0 skrgb Q}"
  5528.            "{0.2 0.9 0 skrgb Q}"
  5529.            "{0.2 0.9 0 skrgb Q}"
  5530.            "{0.9 0 0 skrgb Q}"
  5531.        "]switch "
  5532.        "R"
  5533.     "}for 2drop"
  5534. "}B"
  5535. "/taste{" // tastenindex belegung
  5536.     "over buchstabe over ph1a @ "
  5537.     "dup tmp 1 rot put "
  5538.     "einmax div skgrau "
  5539.     "over zeigebuchstabe "
  5540.     "mitmini{dup ph2a @ minitastatur}{pop}?"
  5541. "}B"
  5542. "/diffpkt{rot sub -rot exch sub exch}B"
  5543. "/skpunkt{rot over mul -rot mul}B"
  5544. "/kurve{"
  5545.     "0.4 -0.2 2dup hypot 0.15 exch div skpunkt newpath moveto "
  5546.     "dup 0.4 -0.2 rot skpunkt rot "
  5547.     "dup 0.6 -0.2 rot skpunkt rot "
  5548.     "0.4 -0.2 2dup hypot 0.15 exch div skpunkt -rot sub "
  5549.     "tmp 1 2 index put"
  5550.     "/Pattern setcolorspace"
  5551.     "<<"
  5552.        "/PatternType 2"
  5553.        "/Shading<<"
  5554.            "/ShadingType 2"
  5555.            "/ColorSpace/DeviceRGB"
  5556.            "/Coords[0 0 tmp 1 get 0]"
  5557.            "/Extend[true true]"
  5558.            "/Function<<"
  5559.                "/Domain[0 1]"
  5560.                "/FunctionType 0"
  5561.                "/Range[0 1 0 1 0 1]"
  5562.                "/DataSource tmp 0 get"
  5563.                "/BitsPerSample 8"
  5564.                "/Size[2]"
  5565.            ">>"
  5566.        ">>"
  5567.     ">>matrix makepattern setcolor "
  5568.     "exch curveto stroke"
  5569. "}B"
  5570. "/linien{" // htab tastenindex
  5571.     "S dup koordinate T 1 setlinecap "
  5572.     "transparenz{/Normal .setblendmode alpha .setshapealpha}if "
  5573.     "over L{"
  5574.        "2dup eq"
  5575.        "{pop}"
  5576.        "{"
  5577.            "2dup bigrammklasse"
  5578.            "[<FFFFFF00FFFF>"
  5579.             "<FFFFFFFFFFFF>"
  5580.             "<FFFFFF8000FF>"
  5581.             "<FFFFFF4000FF>"
  5582.             "<FFFFFF0080FF>"
  5583.             "<FFFFFF00E0FF>"
  5584.             "<FFFFFFFF00FF>"
  5585.            "]over get tmp 0 rot put "
  5586.            "0 eq"
  5587.            "{pop}"
  5588.            "{"
  5589.                "2 index over get bimax div "
  5590.                "dup minFuerKurve gt"
  5591.                "{"
  5592.                    "maxkurvendicke mul setlinewidth "
  5593.                    "over koordinate rot koordinate diffpkt 2dup exch atan "
  5594.                    "S "
  5595.                        "rotate hypot over finger 0 lt{1 -1 scale}if kurve "
  5596.                    "R"
  5597.                "}"
  5598.                "{2drop}?"
  5599.            "}?"
  5600.        "}?"
  5601.     "}for "
  5602.     "2drop R"
  5603. "}B"
  5604. "/gruppenzahl 2 zz mul b"
  5605. "/fsum 11 array B"
  5606. "/fsumA 11 array B"
  5607. "/gsum gruppenzahl array B"
  5608. "/gsumA gruppenzahl array B"
  5609. "/bsum[11{21 array}repeat]B"
  5610. "/bsumA[11{21 array}repeat]B"
  5611. "/zsum[gruppenzahl{zz array}repeat]B"
  5612. "/zsumA[gruppenzahl{zz array}repeat]B"
  5613. "/fingerhaeufigkeit{" // 1a fs
  5614.     "dup lf "
  5615.     "over L{"
  5616.        "2dup finger 5 add "
  5617.        "rot 4 index @ addinsarray"
  5618.     "}for 2drop"
  5619. "}B"
  5620. "/gruppenhaeufigkeit{" // 1a gs
  5621.     "dup lf "
  5622.     "over L{2dup gruppe rot 4 index @ addinsarray}for 2drop"
  5623. "}B"
  5624. "/unterklasse{" // t1 t2
  5625.     "2dup bigrammklasse dup 3 mul 4 1 roll"
  5626.     "["
  5627.        "{2drop 0}dup"
  5628.        "{zeile exch zeile sub abs}dup"
  5629.        "{2drop 0}dup"
  5630.        "{koordinate rot koordinate diffpkt hypot "
  5631.         "kollschritt div floor 2 min 0 max cvi}"
  5632.     "]switch"
  5633. "}B"
  5634. "/bigrammsumme1{" // bs 2a-1 t1
  5635.     "over L{"
  5636.        "2dup unterklasse "
  5637.        "4 index 4 3 roll get "
  5638.        "-rot over add 1 exch{"
  5639.            "over 5 index -rot addinsarray"
  5640.        "}for pop"
  5641.     "}for 3drop"
  5642. "}B"
  5643. "/bigrammhaeufigkeit{" // 2a gs
  5644.     "dup lF "
  5645.     "over L{"
  5646.        "2dup finger 5 add get "
  5647.        "over 4 index @ "
  5648.        "rot bigrammsumme1"
  5649.     "}for 2drop"
  5650. "}B"
  5651. "/zssumme1{" // zs-gr 2a-1 t1
  5652.     "finger "
  5653.     "over L{"
  5654.        "2dup finger mul 0 gt"
  5655.        "{2 index over get exch zeile 4 index -rot exch addinsarray}"
  5656.        "{pop}?"
  5657.     "}for "
  5658.     "3drop"
  5659. "}B"
  5660. "/zshaeufigkeit{" // 2a zs
  5661.     "dup lF "
  5662.     "over L{"
  5663.        "2dup gruppe get over 4 index @ rot zssumme1"
  5664.     "}for 2drop"
  5665. "}B"
  5666. "/umrandung{0.25 setgray 0.003 setlinewidth 0 0 0.8 0.12 rectstroke}B"
  5667. "/zahlimkaestchen{"
  5668.     "S "
  5669.     "0.25 setgray 0.4 0.017 moveto 0.4 dup scale zzahl "
  5670.     "R"
  5671. "}B"
  5672. "/hkaestchen{"
  5673.     "0.75 setgray "
  5674.     "over exch einsumme mul div 0.8 mul 0.12 0 0 2swap rectfill "
  5675.     "umrandung zahlimkaestchen"
  5676. "}B"
  5677. "/bigrammfarben["
  5678.     "[0 1 0][0 0 1][1 0 0]"
  5679.     "[]dup dup"
  5680.     "[1 1 0.5][1 0.85 0.25][1 0.7 0]"
  5681.     "[1 1 0.5][1 0.85 0.25][1 0.7 0]"
  5682.     "[]dup 2dup 2dup"
  5683.     "[1 0.75 0.75][1 0.4 0.4][1 0 0]"
  5684. "]B"
  5685. "/bkaestchen{" // haeufigkeiten a b
  5686.     "over 4 1 roll "
  5687.     "1 exch{"
  5688.        "bigrammfarben over get aload pop setrgbcolor "
  5689.        "over @ "
  5690.        "bisumme bikaestchen mul div 0.8 mul 0.12 0 0 2swap rectfill"
  5691.     "}for "
  5692.     "umrandung @ zahlimkaestchen"
  5693. "}B"
  5694. "/zsfarbe["
  5695.     "[[0.5 0.5 0.5][0.5 0.5 0.5][0.5 0.5 0.5][0.5 0.5 0.5][0.5 0.5 0.5]]"
  5696.     "[[0.5 0.5 0.5][0 1 0][1 0.7 0][1 0 0][1 0 0]]"
  5697.     "[[0.5 0.5 0.5][1 0.5 0][0 1 0][1 0.7 0][1 0 0]]"
  5698.     "[[0.5 0.5 0.5][1 0 0][1 0.5 0][0 1 0][1 0.7 0]]"
  5699.     "[[0.5 0.5 0.5][1 0 0][1 0 0][1 0.7 0][0 1 0]]"
  5700. "]B"
  5701. "/zkaestchen{" // haeufigkeiten farbtab
  5702.     "minzeile 1 maxzeile{"
  5703.        "2dup get aload pop setrgbcolor "
  5704.        "2 index over get bisumme zhkaestchen mul div 0.8 mul "
  5705.        "0.12 0 0 2swap rectfill "
  5706.        "umrandung 2 index @ zahlimkaestchen "
  5707.        "0.8 0.07 T"
  5708.     "}for 2drop"
  5709. "}B"
  5710. "/anzeigen{" // beschreibung belegung
  5711.     "S "
  5712.        "beschrx beschry moveto beschrgr dup scale"
  5713.        "{S show R 0 -0.27 rmoveto}forall "
  5714.     "R "
  5715.     "ph1a L{"
  5716.        "S "
  5717.        "0.4 -0.55 T ph2a over get exch linien "
  5718.        "R"
  5719.     "}for "
  5720.     "ph1a L{"
  5721.        "S "
  5722.        "dup koordinate 1 sub T 0.8 dup scale over taste "
  5723.        "R"
  5724.     "}for pop "
  5725.     "mitSummen{"
  5726.        "fsum L{"
  5727.            "dup 5 sub abs dup mf ge exch xf le and{"
  5728.                "S "
  5729.                "dup dup 5 lt{2}{1}? minx sub add -0.3 T "
  5730.                "dup fsum @ fhkaestchen hkaestchen "
  5731.                "0.15 -0.1 T 0.625 dup scale "
  5732.                "dup bsum @ dup 18 20 bkaestchen "
  5733.                "over dup 5 lt{0.4}{-0.4}? -0.15 T "
  5734.                "5 sub abs dup mf ne{over 9 11 bkaestchen}if "
  5735.                "rot 5 lt{-0.8}{0.8}? -0.08 T "
  5736.                "xf ne{6 8 bkaestchen}{pop}? "
  5737.                "R"
  5738.            "}{pop}?"
  5739.        "}for "
  5740.        "gsum L{"
  5741.            "dup zz mod dup minzeile ge exch maxzeile le and{"
  5742.                "S "
  5743.                "dup zz idiv " << weite << " mul 0.25 sub "
  5744.                "over zz mod 1 sub T "
  5745.                "90 rotate "
  5746.                "dup gsum @ ghkaestchen hkaestchen "
  5747.                "-0.1 -0.14 T 1.25 maxzeile minzeile sub 1 add div dup scale "
  5748.                "zsum over get exch zz mod zsfarbe @ zkaestchen "
  5749.                "R"
  5750.            "}{pop}?"
  5751.        "}for"
  5752.     "}if"
  5753. "}B"
  5754. "/adirekt{" // belegung beschreibung--
  5755.     "over ph1a ph2a setze "
  5756.     "mitSummen{"
  5757.        "ph1a fsum fingerhaeufigkeit "
  5758.        "ph1a gsum gruppenhaeufigkeit "
  5759.        "ph2a bsum bigrammhaeufigkeit "
  5760.        "ph2a zsum zshaeufigkeit"
  5761.     "}if "
  5762.     "anzeigen"
  5763. "}B"
  5764. "/adiff{"
  5765.     "ph1A ph2A setze over ph1a ph2a setze "
  5766.     "mitSummen{"
  5767.        "ph1a fsum fingerhaeufigkeit ph1a gsum gruppenhaeufigkeit "
  5768.        "ph2a bsum bigrammhaeufigkeit ph2a zsum zshaeufigkeit "
  5769.        "ph1A fsumA fingerhaeufigkeit ph1A gsumA gruppenhaeufigkeit "
  5770.        "ph2A bsumA bigrammhaeufigkeit ph2A zsumA zshaeufigkeit "
  5771.        "fsum fsumA submaxarrays gsum gsumA submaxarrays "
  5772.        "bsum bsumA submaxmatrizen zsum zsumA submaxmatrizen "
  5773.        "ph1a ph1A submaxarrays ph2a ph2A submaxmatrizen"
  5774.     "}if "
  5775.     "anzeigen"
  5776. "}B"
  5777. // Zwei Belegungen.  (belegung1 beschr1 belegung2 beschr2 --)
  5778. "/an{S adirekt 0 4 skala div T adirekt showpage R}B"
  5779. // Differenz zweier Belegungen (belegung1 beschr1 belegung2 beschr2 --)
  5780. "/ad{S over exch 4 index adiff 0 4 skala div T adiff showpage R}B"
  5781. // Belegung und Differenz (belegung1 beschr1 belegung2 beschr2 --)
  5782. "/M{S 3 index adiff 0 4 skala div T adirekt showpage R}B"
  5783. // Zwei Belegungen oder ihre Differenz.
  5784. "/A{differenzen{ad}{an}?}B"
  5785. // Einzelne Belegung
  5786. "/E{S 0 2 T adirekt showpage grestore}B\n"
  5787. "%%EndProlog\n"
  5788. "%%BeginSetup\n"
  5789. "<</PageSize[842 595]>> setpagedevice "
  5790. "65 skala mul dup scale 0.5 1.7 T"
  5791. "/BSchrift schrift\n"
  5792. "%%EndSetup" << std::endl;
  5793. };
  5794.  
  5795. void
  5796. Grafik::
  5797. ausgabe(const belegung_t b){
  5798.    ungerade = !ungerade;
  5799.    if(ungerade){
  5800.       seite++;
  5801.       grafik << "%%Page: " << seite << " " << seite << "\n";
  5802.    }
  5803.    grafik << "(";
  5804.    for(int i = 0; i < tastatur.nvariabel(); ++i)
  5805.       grafik << kodierung.psencstr(b[i]);
  5806.    grafik << ")[]";
  5807.  
  5808.    if(!ungerade) grafik << "A" << std::endl;
  5809. }
  5810.  
  5811. Grafik::
  5812. ~Grafik(){
  5813.    if(ungerade) grafik << "E\n";
  5814.    grafik << "%%EOF" << std::endl;
  5815. }
  5816.  
  5817. //--------------- src/string_in_belegung.cc ---------------
  5818. //#include "string_in_belegung.hh"
  5819.  
  5820. //#include "Kodierung.hh"
  5821. //#include "utfhilfe.hh"
  5822. #include <iostream>
  5823.  
  5824. void string_in_belegung(const std::u32string& z, belegung_t b, bool* fest,
  5825.                         int nv, const Kodierung& kodierung)
  5826. {
  5827.    char32_t gabs[ntaste];
  5828.    for(auto& i : gabs) i = 0;
  5829.    for(int i = 0; i < ntaste; ++i) b[i] = i;
  5830.  
  5831.    const int l = z.size();
  5832.    if(l < nv-1){
  5833.       std::cerr << SPRACHE("Die Belegung '", "The layout '")
  5834.                 << utf32_in_ausgabe(z)
  5835.                 << SPRACHE("' hat ", "' has ")
  5836.                 << l << SPRACHE(" Zeichen, erwartet werden ",
  5837.                                 " symbols, but expected are ")
  5838.                 << nv << "." << std::endl;
  5839.       exit(1);
  5840.    }
  5841.  
  5842.    int sum = (nv*(nv-1))/2;
  5843.    for(int i = 0; i < std::min(l, nv); ++i){
  5844.       const auto p = kodierung.position(z[i]);
  5845.       if(p.first == -1){
  5846.          std::cerr << SPRACHE("Die Belegung '", "The layout '")
  5847.                    << utf32_in_ausgabe(z)
  5848.                    << SPRACHE("' enth" strAe "lt das unbekannte Zeichen '",
  5849.                               "' contains the unknown symbol '")
  5850.                    << utf32_in_ausgabe(z[i]) << "'." << std::endl;
  5851.          exit(1);
  5852.       }else if(gabs[p.first]){
  5853.          std::cerr << SPRACHE("Die Belegung  '", "The layout '")
  5854.                    << utf32_in_ausgabe(z)
  5855.                    << SPRACHE("' enth" strAe "lt '", "' contains '")
  5856.                    << utf32_in_ausgabe(z[i])
  5857.                    << SPRACHE("' mehrfach. ", "' multiple times.");
  5858.          if(z[i] != gabs[p.first]){
  5859.             std::cerr << SPRACHE(" Vorher wurde das gleichwertige Zeichen '",
  5860.                                  " Previously, the equivalent symbol '")
  5861.                       << utf32_in_ausgabe(gabs[p.first])
  5862.                       << SPRACHE("' verwendet.", "' has been used.");
  5863.          }
  5864.  
  5865.          std::cerr << std::endl;
  5866.          exit(1);
  5867.       }else{
  5868.          b[i] = p.first;
  5869.          sum -= b[i];
  5870.          gabs[p.first] = z[i];
  5871.          fest[i] = (p.second != 0) && kodierung.txt(p.first, 0).length();
  5872.       }
  5873.    }
  5874.  
  5875.    if(l < nv){
  5876.       b[nv-1] = sum;
  5877.       fest[nv-1] = false;
  5878.    }
  5879. }
  5880.  
  5881. //--------------- src/Eingabestream.cc ---------------
  5882. //#include "Eingabestream.hh"
  5883.  
  5884. //#include "konstanten.hh"
  5885. //#include "utfhilfe.hh"
  5886. #include <iostream>
  5887.  
  5888. namespace {
  5889.  
  5890. constexpr char32_t cp1252extra[] =
  5891.    U"\u20ac \u201a\u0192\u201e\u2026\u2020\u2021\u02c6\u2030\u0160"
  5892.    U"\u2039\u0152 \u017d  \u2018\u2019\u201c\u201d\u2022\u2013\u2014"
  5893.    U"\u02dc\u2122\u0161\u203a\u0153 \u017e\u0178";
  5894.  
  5895. char32_t cp1252_in_utf32(const char*& s){
  5896.    if(*s == 0) return 0;
  5897.    const char32_t c = *s++ & 0xff;
  5898.    if(c < 0x80 || c >= 0xA0) return c;
  5899.    return cp1252extra[c-0x80];
  5900. }
  5901.  
  5902. double zehnhoch(unsigned n){
  5903.    double fak = 1., mult = 10.;
  5904.    while(n){
  5905.       if(n & 1) fak *= mult;
  5906.       n >>= 1;
  5907.       mult *= mult;
  5908.    }
  5909.    return fak;
  5910. }
  5911.  
  5912. constexpr char32_t BOM = U'\uFEFF', CR = U'\u000D';
  5913. }
  5914.  
  5915.  
  5916. Eingabestream::
  5917. Eingabestream(const std::string& nam, bool utf8,
  5918.               bool muss_existieren)
  5919.    : name(nam), l(0), p(0), maxl(0), izeile(0), errpos(-1),
  5920.      utf8ein(utf8), geaendert(false){
  5921.    f.open(name.c_str());
  5922.    if(muss_existieren && !f){
  5923.       std::cerr << "'" << name
  5924.                 << SPRACHE("' ist nicht lesbar.", "' is not readable")
  5925.                 << std::endl;
  5926.       exit(1);
  5927.    }
  5928. }
  5929.  
  5930. Eingabestream::
  5931. ~Eingabestream()
  5932. { f.close(); }
  5933.  
  5934. // Lies nächstes Zeichen (einschliesslich Zeilenenden '\n'); falls Eingabe
  5935. // erschöpft ist gibt Null zurück.
  5936. char32_t
  5937. Eingabestream::lieszeichen(){
  5938.    if(p < l) return buffer[p++];
  5939.    if(!fuellen()) return 0;
  5940.    return U'\n';
  5941. }
  5942.  
  5943. // Falls aus der aktuellen Zeile schon Zeichen gelesen wurden, fange eine
  5944. // neue an.  Gib false zurück, wenn die Eingabe erschöpft ist.
  5945. bool
  5946. Eingabestream::neuezeile(){
  5947.    errpos = -1;
  5948.    if(!p && l) return true;
  5949.    p = l;
  5950.    return fuellen();
  5951. }
  5952. // Wie neuezeile, übergeht aber Kommentar- und Leerzeilen.
  5953. bool
  5954. Eingabestream::echte_neuezeile(){
  5955.    while(neuezeile()){
  5956.       zwischenraum_uebergehen();
  5957.       if(p == l) continue;
  5958.       if(ist(U'#')){ p = l; continue; }
  5959.       return true;
  5960.    }
  5961.    return false;
  5962. }
  5963.  
  5964. size_t
  5965. Eingabestream::restzeichen() const
  5966. { return l-p; }
  5967.  
  5968. bool
  5969. Eingabestream::ist_zwischenraum() const
  5970. { return p < l && ::ist_zwischenraum(buffer[p]); }
  5971.  
  5972. bool
  5973. Eingabestream::ist_ziffer() const
  5974. { return p < l && ::ist_ziffer(buffer[p]); }
  5975.  
  5976. // Ist aktuelles Zeichen in aktueller Zeile gleich dem übergebenen?
  5977. bool Eingabestream::ist(char32_t c) const { return p < l && c == buffer[p]; }
  5978.  
  5979. void
  5980. Eingabestream::zwischenraum_uebergehen()
  5981. { while(ist_zwischenraum()) ++p; }
  5982.  
  5983. void
  5984. Eingabestream::uebergehen()
  5985. { if(p < l) ++p; }
  5986.  
  5987. char32_t
  5988. Eingabestream::lies_in_zeile()
  5989. { return p < l ?  buffer[p++] : 0; }
  5990.  
  5991. bool
  5992. Eingabestream::zeilenende(){
  5993.    zwischenraum_uebergehen();
  5994.    errpos = p;
  5995.    return p == l;
  5996. }
  5997.  
  5998. bool
  5999. Eingabestream::hole_wort(std::u32string& wert){
  6000.    wert.resize(0);
  6001.    zwischenraum_uebergehen();
  6002.    errpos = p;
  6003.    if(p >= l) return false;
  6004.    if(ist(U'#')){ p = l; return false; }
  6005.    while(p < l && !ist_zwischenraum()) wert.push_back(buffer[p++]);
  6006.    return true;
  6007. }
  6008.  
  6009. bool
  6010. Eingabestream::hole_flag(bool& wert){
  6011.    zwischenraum_uebergehen();
  6012.    errpos = p;
  6013.    if(p >= l) return false;
  6014.    wert = ist(U'+');
  6015.    if(wert || ist(U'-')){ ++p; return p == l || ist_zwischenraum(); }
  6016.    return false;
  6017. }
  6018.  
  6019. bool
  6020. Eingabestream::hole_string(std::u32string& wert){
  6021.    wert.resize(0);
  6022.    zwischenraum_uebergehen();
  6023.    errpos = p;
  6024.    if(p+1 >= l) return false;
  6025.    const char32_t ende = buffer[p++];
  6026.    while(p < l && buffer[p] != ende) wert.push_back(buffer[p++]);
  6027.    if(p >= l) return false;
  6028.    ++p;
  6029.    return true;
  6030. }
  6031.  
  6032. bool Eingabestream::encoding_geaendert() const { return geaendert; }
  6033.  
  6034. [[noreturn]] void
  6035. Eingabestream::fehler(size_t off) const {
  6036.    const std::string o = name+SPRACHE(", Zeile ", ", row ")+
  6037.       std::to_string(izeile)+": ";
  6038.    std::cerr << o << zeile << std::endl;
  6039.    if(errpos >=0){
  6040.       size_t n = o.length()+errpos+off;
  6041.       for(size_t i = 0; i < n; ++i)
  6042.          std::cerr << ".";
  6043.       std::cerr << "^" << std::endl;
  6044.    }
  6045.    exit(1);
  6046. }
  6047.  
  6048. size_t
  6049. Eingabestream::aktuelle_zeile() const
  6050. { return izeile; }
  6051.  
  6052. const std::string&
  6053. Eingabestream::aktuelles_file() const
  6054. { return name; }
  6055.  
  6056. bool
  6057. Eingabestream::hole_zahl(double& wert){
  6058.    zwischenraum_uebergehen();
  6059.    errpos = p;
  6060.    wert = 0;
  6061.    if(p >= l) return false;
  6062.  
  6063.    double sign = 1.;
  6064.    if(buffer[p] == U'-'){
  6065.       sign = -1.; ++p;
  6066.    }else if(buffer[p] == U'+') ++p;
  6067.  
  6068.    int nk = 0, ev = 0, es = 1;
  6069.    bool nachkomma = false, exponent = false, zf = false;
  6070.    for(; p < l && !ist_zwischenraum(); ++p){
  6071.       if(ist_ziffer()){
  6072.          const double val = buffer[p]-U'0';
  6073.          if(exponent){
  6074.             ev = ev*10+val;
  6075.          }else{
  6076.             wert = wert*10.+val;
  6077.             if(nachkomma) ++nk;
  6078.             zf = true;
  6079.          }
  6080.       }else if(buffer[p] == U'.' || buffer[p] == U','){
  6081.          if(exponent || nachkomma) return false;
  6082.          nachkomma = true;
  6083.       }else if(buffer[p] == U'e' || buffer[p] == U'E'){
  6084.          if(exponent) return false;
  6085.          if(p+1 == l) return false;
  6086.          if(buffer[p+1] == '-'){
  6087.             es = -1.; ++p;
  6088.          }else if(buffer[p+1] == '+') ++p;
  6089.          if(p+1 == l) return false;
  6090.          ++p;
  6091.          if(!ist_ziffer()) return false;
  6092.          ev =  buffer[p]-U'0';
  6093.          exponent = true;
  6094.       }else{
  6095.          return false;
  6096.       }
  6097.    }
  6098.    ev = es*ev-nk;
  6099.    const double e10 = sign*zehnhoch(std::abs(ev));
  6100.    wert = ev < 0 ? wert/e10 : wert*e10;
  6101.    return zf;
  6102. }
  6103.  
  6104. bool
  6105. Eingabestream::fuellen(){
  6106.    if(p < l) return true; // Buffer nicht leer.
  6107.    p = l = 0;
  6108.    buffer.resize(0);  buffer.reserve(maxl);
  6109.    if(!f || f.rdstate() & std::ios::eofbit) return false;
  6110.    // Das \n am Zeilenende ist nicht enthalten.
  6111.    std::getline(f, zeile);
  6112.    ++izeile;
  6113.  
  6114.    const char* cp = zeile.c_str();
  6115.    if(utf8ein){
  6116.       bool reinterpret = false;
  6117.       while(*cp){
  6118.          const char32_t c = utf8_in_utf32(cp, reinterpret);
  6119.          if(reinterpret){
  6120.             l = 0; buffer.resize(0);  buffer.reserve(maxl);
  6121.             geaendert = true;
  6122.             return false;
  6123.          }
  6124.          if(c != BOM && c != CR){  // wegen CP/M und dergleichen
  6125.             buffer.push_back(c);
  6126.             ++l;
  6127.          }
  6128.       }
  6129.    }else{
  6130.       while(*cp){
  6131.          buffer.push_back(cp1252_in_utf32(cp));
  6132.          ++l;
  6133.       }
  6134.    }
  6135.  
  6136.    if(l > maxl) maxl = l;
  6137.    return true;
  6138. }
  6139.  
  6140. double
  6141. hole_zahl(Eingabestream& f, double rmin, double rmax){
  6142.     double w;
  6143.     if(f.hole_zahl(w)){
  6144.        if(w < rmin || w > rmax){
  6145.           std::cerr << w
  6146.                     << SPRACHE(" ist ausserhalb des erlaubten Wertebereichs [",
  6147.                                " is not within the allowed range of values [")
  6148.                     << rmin << ", " << rmax << "]." << std::endl;
  6149.        }else return w;
  6150.     }else{
  6151.        std::cerr << SPRACHE("Eine Zahl wurde erwartet, jedoch nicht gefunden.",
  6152.                             "A number has been expected, but none was found.")
  6153.                   << std::endl;
  6154.     }
  6155.     f.fehler();
  6156. }
  6157.  
  6158. void
  6159. pruefe_leer_dann_N(Eingabestream& f, size_t N){
  6160.    const bool rest = f.restzeichen() == N+1;
  6161.    const bool zwischen = f.ist_zwischenraum();
  6162.    if(zwischen && rest) return;
  6163.  
  6164.    std::cerr << SPRACHE("Nach der Zahl werden ein Leerzeichen und genau ",
  6165.                         "After the number, one space character and exactly ")
  6166.              << N << SPRACHE(" weitere Zeichen erwartet.",
  6167.                              " additional characters are expected.")
  6168.              << std::endl;
  6169.    f.fehler();
  6170. }
  6171.  
  6172. //--------------- src/Statistik.cc ---------------
  6173. //#include "Statistik.hh"
  6174.  
  6175. //#include "Aufwandstabelle.hh"
  6176. //#include "Haeufigkeit.hh"
  6177. //#include "Kodierung.hh"
  6178. //#include "Tastatur.hh"
  6179.  
  6180. Statistik::
  6181. Statistik(const belegung_t b, const Tastatur& tastatur,
  6182.           const Kodierung& kodierung, const Haeufigkeit& h,
  6183.           const Aufwandstabelle& a, const double ngrammakkumlimit[3])
  6184. {
  6185.    for(int i = 0; i < ntaste; ++i){
  6186.       const int fi  = tastatur.finger(i);
  6187.       const int zi  = b[i], sti = tastatur.shifttaste(i);
  6188.       const int fx  = tastatur.finger_index(i);
  6189.       const int s   = fi > 0;
  6190.       const int sfi = tastatur.finger(sti);
  6191.       const int ss  = sfi > 0;
  6192.       const int sfx = tastatur.finger_index(sti);
  6193.       const int z   = tastatur.zeile(i);
  6194.       const int sz  = tastatur.zeile(sti);
  6195.  
  6196.       mitfinger[fx] = mitfinger[sfx] = true;
  6197.  
  6198.       haeufigkeit_t h01 = 0;
  6199.       for(int ei = 0; ei < nebene; ++ei){
  6200.          aeinzel += a(i,ei)*h(zi,ei);
  6201.          if(fx < nfinger) h01 += h(zi,ei);
  6202.       }
  6203.       const haeufigkeit_t h1 = sfx < nfinger ? h(zi,1) : 0;
  6204.       h1tot         += h01+h1;
  6205.       hpos[s][z]    += h01;
  6206.       hpos[ss][sz]  += h1;
  6207.       hfinger[fx]   += h01;
  6208.       hfinger[sfx]  += h1;
  6209.       hlinks        += (1-s)*h01+(1-ss)*h1;
  6210.       hslinks       += (1-ss)*h1;
  6211.       hrechts       += ss*h1+s*h01;
  6212.       hsrechts      += ss*h1;
  6213.  
  6214.       for(int j = 0; j < ntaste; ++j){
  6215.          if(tastatur.kategorie(i,j) == kategorie_t::MitUndefDaumen) continue;
  6216.          const int zj = b[j];
  6217.          const int stj= tastatur.shifttaste(j), sfy= tastatur.finger_index(stj);
  6218.          const int fy  = tastatur.finger_index(j);
  6219.          const int fj = tastatur.finger(j);
  6220.          const int f2[2] = { fj, tastatur.finger(stj) };
  6221.          const int t2[2] = { j, stj }, fi2[2] = { fy, sfy };
  6222.  
  6223.          // sbb und sbs (siehe den ganz langen Kommentar oben) sind ein
  6224.          // Mittelding zwischen Bigrammen und Trigammen.  Für die Ausgabe
  6225.          // weisen wir sie getrennt und mit der Vorsatz "Shift-" aus.
  6226.          for(int ej = 0; ej < nebene2; ++ej){
  6227.             // Shift-Bigramme (sbb und sbs)
  6228.             const haeufigkeit_t h2 = h(zi,1,zj,ej);
  6229.             hs[tastatur.kategorie(sti,t2[ej])] += h2;
  6230.             hs2tot += h2;
  6231.  
  6232.             const std::vector<int> sbi = { sti, t2[ej] };
  6233.             for(const int ub : tastatur.benutzerkategorie(sbi))
  6234.                hs_benutzer[ub] += h2;
  6235.  
  6236.             if(std::abs(sfi-f2[ej]) == 1){
  6237.                hsnachbar += h2;
  6238.                hsnachbar1[sti-ntaste] += h2;
  6239.                if(std::abs(sz-tastatur.zeile(t2[ej])) > 1)
  6240.                   hsnachbar2[sti-ntaste] += h2;
  6241.             }else if(tastatur.kategorie(sti,t2[ej]) == kategorie_t::Kollision){
  6242.                hskollision1[sti-ntaste] += h2;
  6243.                if(tastatur.distanz(sti,t2[ej]) >= 2)
  6244.                   hskollision2[sti-ntaste] += h2;
  6245.             }
  6246.  
  6247.             if(h2 > 0 && ngrammakkumlimit[1] > 0 &&
  6248.                tastatur.istHandwiederholung(sti,t2[ej])){
  6249.                std::string bg_typ = kodierung.txt(zi,1)+kodierung.txt(zj,ej)
  6250.                   +" : Shift-"+tastatur.kategorie_lang(sti, t2[ej]);
  6251.                hrel[1][ss] += h2;
  6252.                const std::pair<haeufigkeit_t, std::string> hw(h2, bg_typ);
  6253.                ngramm[1][ss].insert(hw);
  6254.             }
  6255.  
  6256.             for(int ei = 0; ei < nebene; ++ei){
  6257.                // Bigramme: bb in B1 und B2 (ej == 0), bs in B3 und B4 (ej == 1)
  6258.                const haeufigkeit_t h2 = h(zi,ei,zj,ej);
  6259.                hk[tastatur.kategorie(i,t2[ej])] += h2;
  6260.                h2tot+= h2;
  6261.                if(std::abs(f2[ej]-fi) == 1){
  6262.                   hnachbar += h2;
  6263.                   hnachbar1[(fx+fi2[ej]-1)/2] += h2;
  6264.                   if(std::abs(z-tastatur.zeile(t2[ej])) > 1)
  6265.                      hnachbar2[(fx+fi2[ej]-1)/2] += h2;
  6266.                }else if(tastatur.kategorie(i,t2[ej]) == kategorie_t::Kollision){
  6267.                   assert(fx < nfinger);
  6268.                   hkollision1[fx] += h2;
  6269.                   if(tastatur.distanz(i,t2[ej]) >= 2) hkollision2[fx] += h2;
  6270.                }
  6271.  
  6272.                const std::vector<int> bi = {i, t2[ej] };
  6273.                for(const int ub : tastatur.benutzerkategorie(bi))
  6274.                   hk_benutzer[ub] += h2;
  6275.  
  6276.                if(ej){
  6277.                   // bsb, vom Tippen her Trigramme.
  6278.                   const haeufigkeit_t h3 = h(zi,ei,zj,ej);
  6279.                   // Wir interessieren uns nicht für einfache Handwechsel, und
  6280.                   // kein Handwechsel kann hier nicht vorkommen.
  6281.                   if(tastatur.kategorie(i,stj) == kategorie_t::Handwechsel){
  6282.                      hi2[tastatur.kategorie(i,j)] += h3; // bsb in B3 und B4
  6283.                      hdoppelhw += h3;
  6284.                   }
  6285.                   h3tot += h3;
  6286.  
  6287.                   const std::vector<int> tri = {i, stj, j };
  6288.                   for(const int ub : tastatur.benutzerkategorie(tri))
  6289.                      ht_benutzer[ub] += h2;
  6290.                }
  6291.             }
  6292.  
  6293.             // bb und bs.  Die hatten wir oben schonmal, wir müssen hier aber
  6294.             // darauf achten, dass wir nicht unterscheiden, ob das erste b ein
  6295.             // Grossbuchstabe ist oder nicht, denn in beiden Fällen handelt es
  6296.             // sich um dasselbe Tastenbigramm (das unterscheidende Shift liegt
  6297.             // vor diesem).
  6298.             const haeufigkeit_t hij = h(zi,0,zj,ej)+h(zi,1,zj,ej);
  6299.             if(hij > 0 && ngrammakkumlimit[0] > 0 &&
  6300.                tastatur.istHandwiederholung(i, t2[ej])){
  6301.                // Für ej == 1 steht der Grossbuchstabe in diesem Bigramm für
  6302.                // die Shifttaste die man zu seine Eingabe braucht.
  6303.                std::string bg_typ = kodierung.txt(zi,0)+kodierung.txt(zj,ej)
  6304.                   +" : "+tastatur.kategorie_lang(i, t2[ej]);
  6305.                hrel[0][s] += hij;
  6306.                const std::pair<haeufigkeit_t, std::string> hw(hij, bg_typ);
  6307.                ngramm[0][s].insert(hw);
  6308.             }
  6309.          }
  6310.  
  6311.          if(h.mit_trigrammen()){
  6312.             for(int k = 0; k < ntaste; ++k){
  6313.                if(tastatur.finger(k) == finger_t::EinerDerDaumen) continue;
  6314.                const int zk = b[k];
  6315.  
  6316.                // Die normalen Trigramme.
  6317.                const int stk = tastatur.shifttaste(k), t3[2] = { k, stk };
  6318.                const haeufigkeit_t h3[2] = { h.tri(zi,zj,zk,0),
  6319.                                              h.tri(zi,zj,zk,1) };
  6320.                h3tot += h3[0]+h3[1];
  6321.  
  6322.                for(int ek = 0; ek < 2; ++ek){
  6323.                   if(tastatur.kategorie(i,j) == kategorie_t::Handwechsel){
  6324.                      // ek == 0: bbb in T1 und T2; ek == 1: bbs in T5 und T6
  6325.                      hi2[tastatur.kategorie(i,t3[ek])] += h3[ek];
  6326.                   }else{
  6327.                      if(tastatur.kategorie(i,t3[ek]) !=
  6328.                         kategorie_t::Handwechsel){
  6329.                         if(tastatur.istWippe(i,j,t3[ek])) hwippe += h3[ek];
  6330.                         hi0[tastatur.kategorie(i,t3[ek])] += h3[ek];
  6331.                      }
  6332.                   }
  6333.  
  6334.                   const std::vector<int> tri = {i, j, t3[ek] };
  6335.                   for(const int ub : tastatur.benutzerkategorie(tri))
  6336.                      ht_benutzer[ub] += h3[ek];
  6337.  
  6338.                   const int wdh =
  6339.                      (tastatur.kategorie(i,j) == kategorie_t::Handwechsel)+
  6340.                      (tastatur.kategorie(j,t3[ek]) == kategorie_t::Handwechsel);
  6341.                   if(h3[ek] <= 0 || wdh == 1) continue;
  6342.  
  6343.                   if(wdh == 2) hdoppelhw += h3[ek];
  6344.                   else hkeinhw += h3[ek];
  6345.  
  6346.                   if(ngrammakkumlimit[2] > 0){
  6347.                      std::string tri_typ = kodierung.txt(zi,0)
  6348.                         +kodierung.txt(zj,0)+kodierung.txt(zk,ek)+" : ";
  6349.                      if(wdh == 2){
  6350.                         // doppelter Handwechsel: Charakterisiert durch erste
  6351.                         // und erste und letzte Taste
  6352.                         tri_typ += SPRACHE("Indirekt-", "indirect ")
  6353.                            +tastatur.kategorie_lang(i, t3[ek]);
  6354.                      }else{
  6355.                         // Sonst durch die beiden Bigramme charakterisiert.
  6356.                         tri_typ += tastatur.kategorie_lang(i, j)
  6357.                            +" + "+tastatur.kategorie_lang(j, t3[ek]);
  6358.                         if(tastatur.istWippe(i,j,t3[ek]))
  6359.                            tri_typ += SPRACHE(" (Wippe)", " (seesaw)");
  6360.                      }
  6361.                      const int s = wdh/2;
  6362.                      const std::pair<haeufigkeit_t, std::string> tri(h3[ek],
  6363.                                                                      tri_typ);
  6364.                      hrel[2][s] += h3[ek];
  6365.                      ngramm[2][s].insert(tri);
  6366.                   }
  6367.                }
  6368.             }
  6369.          }
  6370.       }
  6371.    }
  6372.  
  6373.    const akkumuations_t sk1 = 100./h1tot;
  6374.    hlinks *= sk1;
  6375.    hrechts *= sk1;
  6376.    hslinks *= sk1;
  6377.    hsrechts *= sk1;
  6378.    for(int s = 0; s < 2; ++s) for(auto& x : hpos[s]) x *= sk1;
  6379.    for(int i = 0; i < nfinger; ++i) hfinger[i] *= sk1;
  6380.  
  6381.    const akkumuations_t sk2 = 100./h2tot;
  6382.    hnachbar *= sk2;
  6383.    for(auto& x : hk)  x *= sk2;
  6384.    for(auto& i : hk_benutzer) i.second *= sk2;
  6385.    for(int i = 0; i < nfinger; ++i){
  6386.       hkollision1[i] *= sk2;
  6387.       hkollision2[i] *= sk2;
  6388.       hnachbar1[i]   *= sk2;
  6389.       hnachbar2[i]   *= sk2;
  6390.    }
  6391.  
  6392.    // ssk2 == 0 kommt durchaus vor; vemeide NaNs.
  6393.    const akkumuations_t ssk2 = hs2tot > 0 ? 100./hs2tot : 0;
  6394.    hsnachbar *= ssk2;
  6395.    for(auto& x : hs)  x *= ssk2;
  6396.    for(auto& i : hs_benutzer) i.second *= ssk2;
  6397.    for(int i = 0; i < nshift; ++i){
  6398.       hskollision1[i] *= ssk2;
  6399.       hskollision2[i] *= ssk2;
  6400.       hsnachbar1[i]   *= ssk2;
  6401.       hsnachbar2[i]   *= ssk2;
  6402.    }
  6403.  
  6404.    const akkumuations_t sk3 = h3tot > 0 ? 100./h3tot : 0;
  6405.    hkeinhw *= sk3;
  6406.    hdoppelhw *= sk3;
  6407.    hwippe *= sk3;
  6408.    for(auto& x : hi0) x *= sk3;
  6409.    for(auto& x : hi2) x *= sk3;
  6410.    for(auto& i : ht_benutzer) i.second *= sk3;
  6411. }
  6412.  
  6413. //--------------- src/Unicode.cc ---------------
  6414. //#include "Unicode.hh"
  6415.  
  6416. namespace {
  6417. // Um vom eingestellten Locale unabhängig zu werden, definieren wir unsere
  6418. // Zeichenklassifikation selber.
  6419. constexpr char32_t kleinbuchstaben[] =
  6420.    U"\u0061\u0062\u0063\u0064\u0065\u0066\u0067\u0068\u0069\u006A\u006B"
  6421.    U"\u006C\u006D\u006E\u006F\u0070\u0071\u0072\u0073\u0074\u0075\u0076"
  6422.    U"\u0077\u0078\u0079\u007A\u00E0\u00E1\u00E2\u00E3\u00E4\u00E5\u00E6"
  6423.    U"\u00E7\u00E8\u00E9\u00EA\u00EB\u00EC\u00ED\u00EE\u00EF\u00F0\u00F1"
  6424.    U"\u00F2\u00F3\u00F4\u00F5\u00F6\u00F8\u00F9\u00FA\u00FB\u00FC\u00FD"
  6425.    U"\u00FE\u0101\u0103\u0105\u0107\u0109\u010B\u010D\u010F\u0111\u0113"
  6426.    U"\u0115\u0117\u0119\u011B\u011D\u011F\u0121\u0123\u0125\u0127\u0129"
  6427.    U"\u012B\u012D\u012F\u0069\u0133\u0135\u0137\u013A\u013C\u013E\u0140"
  6428.    U"\u0142\u0144\u0146\u0148\u014B\u014D\u014F\u0151\u0153\u0155\u0157"
  6429.    U"\u0159\u015B\u015D\u015F\u0161\u0163\u0165\u0167\u0169\u016B\u016D"
  6430.    U"\u016F\u0171\u0173\u0175\u0177\u00FF\u017A\u017C\u017E\u0253\u0183"
  6431.    U"\u0185\u0254\u0188\u0256\u0257\u018C\u01DD\u0259\u025B\u0192\u0260"
  6432.    U"\u0263\u0269\u0268\u0199\u026F\u0272\u0275\u01A1\u01A3\u01A5\u0280"
  6433.    U"\u01A8\u0283\u01AD\u0288\u01B0\u028A\u028B\u01B4\u01B6\u0292\u01B9"
  6434.    U"\u01BD\u01C6\u01C9\u01CC\u01CE\u01D0\u01D2\u01D4\u01D6\u01D8\u01DA"
  6435.    U"\u01DC\u01DF\u01E1\u01E3\u01E5\u01E7\u01E9\u01EB\u01ED\u01EF\u01F3"
  6436.    U"\u01F5\u0195\u01BF\u01F9\u01FB\u01FD\u01FF\u0201\u0203\u0205\u0207"
  6437.    U"\u0209\u020B\u020D\u020F\u0211\u0213\u0215\u0217\u0219\u021B\u021D"
  6438.    U"\u021F\u019E\u0223\u0225\u0227\u0229\u022B\u022D\u022F\u0231\u0233"
  6439.    U"\u2C65\u023C\u019A\u2C66\u0242\u0180\u0289\u028C\u0247\u0249\u024B"
  6440.    U"\u024D\u024F\u0371\u0373\u0377\u03F3\u03AC\u03AD\u03AE\u03AF\u03CC"
  6441.    U"\u03CD\u03CE\u03B1\u03B2\u03B3\u03B4\u03B5\u03B6\u03B7\u03B8\u03B9"
  6442.    U"\u03BA\u03BB\u03BC\u03BD\u03BE\u03BF\u03C0\u03C1\u03C3\u03C4\u03C5"
  6443.    U"\u03C6\u03C7\u03C8\u03C9\u03CA\u03CB\u03D7\u03D9\u03DB\u03DD\u03DF"
  6444.    U"\u03E1\u03E3\u03E5\u03E7\u03E9\u03EB\u03ED\u03EF\u03B8\u03F8\u03F2"
  6445.    U"\u03FB\u037B\u037C\u037D\u0450\u0451\u0452\u0453\u0454\u0455\u0456"
  6446.    U"\u0457\u0458\u0459\u045A\u045B\u045C\u045D\u045E\u045F\u0430\u0431"
  6447.    U"\u0432\u0433\u0434\u0435\u0436\u0437\u0438\u0439\u043A\u043B\u043C"
  6448.    U"\u043D\u043E\u043F\u0440\u0441\u0442\u0443\u0444\u0445\u0446\u0447"
  6449.    U"\u0448\u0449\u044A\u044B\u044C\u044D\u044E\u044F\u0461\u0463\u0465"
  6450.    U"\u0467\u0469\u046B\u046D\u046F\u0471\u0473\u0475\u0477\u0479\u047B"
  6451.    U"\u047D\u047F\u0481\u048B\u048D\u048F\u0491\u0493\u0495\u0497\u0499"
  6452.    U"\u049B\u049D\u049F\u04A1\u04A3\u04A5\u04A7\u04A9\u04AB\u04AD\u04AF"
  6453.    U"\u04B1\u04B3\u04B5\u04B7\u04B9\u04BB\u04BD\u04BF\u04CF\u04C2\u04C4"
  6454.    U"\u04C6\u04C8\u04CA\u04CC\u04CE\u04D1\u04D3\u04D5\u04D7\u04D9\u04DB"
  6455.    U"\u04DD\u04DF\u04E1\u04E3\u04E5\u04E7\u04E9\u04EB\u04ED\u04EF\u04F1"
  6456.    U"\u04F3\u04F5\u04F7\u04F9\u04FB\u04FD\u04FF\u0501\u0503\u0505\u0507"
  6457.    U"\u0509\u050B\u050D\u050F\u0511\u0513\u0515\u0517\u0519\u051B\u051D"
  6458.    U"\u051F\u0521\u0523\u0525\u0527\u0529\u052B\u052D\u052F\u0561\u0562"
  6459.    U"\u0563\u0564\u0565\u0566\u0567\u0568\u0569\u056A\u056B\u056C\u056D"
  6460.    U"\u056E\u056F\u0570\u0571\u0572\u0573\u0574\u0575\u0576\u0577\u0578"
  6461.    U"\u0579\u057A\u057B\u057C\u057D\u057E\u057F\u0580\u0581\u0582\u0583"
  6462.    U"\u0584\u0585\u0586\u2D00\u2D01\u2D02\u2D03\u2D04\u2D05\u2D06\u2D07"
  6463.    U"\u2D08\u2D09\u2D0A\u2D0B\u2D0C\u2D0D\u2D0E\u2D0F\u2D10\u2D11\u2D12"
  6464.    U"\u2D13\u2D14\u2D15\u2D16\u2D17\u2D18\u2D19\u2D1A\u2D1B\u2D1C\u2D1D"
  6465.    U"\u2D1E\u2D1F\u2D20\u2D21\u2D22\u2D23\u2D24\u2D25\u2D27\u2D2D\uAB70"
  6466.    U"\uAB71\uAB72\uAB73\uAB74\uAB75\uAB76\uAB77\uAB78\uAB79\uAB7A\uAB7B"
  6467.    U"\uAB7C\uAB7D\uAB7E\uAB7F\uAB80\uAB81\uAB82\uAB83\uAB84\uAB85\uAB86"
  6468.    U"\uAB87\uAB88\uAB89\uAB8A\uAB8B\uAB8C\uAB8D\uAB8E\uAB8F\uAB90\uAB91"
  6469.    U"\uAB92\uAB93\uAB94\uAB95\uAB96\uAB97\uAB98\uAB99\uAB9A\uAB9B\uAB9C"
  6470.    U"\uAB9D\uAB9E\uAB9F\uABA0\uABA1\uABA2\uABA3\uABA4\uABA5\uABA6\uABA7"
  6471.    U"\uABA8\uABA9\uABAA\uABAB\uABAC\uABAD\uABAE\uABAF\uABB0\uABB1\uABB2"
  6472.    U"\uABB3\uABB4\uABB5\uABB6\uABB7\uABB8\uABB9\uABBA\uABBB\uABBC\uABBD"
  6473.    U"\uABBE\uABBF\u13F8\u13F9\u13FA\u13FB\u13FC\u13FD\u10D0\u10D1\u10D2"
  6474.    U"\u10D3\u10D4\u10D5\u10D6\u10D7\u10D8\u10D9\u10DA\u10DB\u10DC\u10DD"
  6475.    U"\u10DE\u10DF\u10E0\u10E1\u10E2\u10E3\u10E4\u10E5\u10E6\u10E7\u10E8"
  6476.    U"\u10E9\u10EA\u10EB\u10EC\u10ED\u10EE\u10EF\u10F0\u10F1\u10F2\u10F3"
  6477.    U"\u10F4\u10F5\u10F6\u10F7\u10F8\u10F9\u10FA\u10FD\u10FE\u10FF\u1E01"
  6478.    U"\u1E03\u1E05\u1E07\u1E09\u1E0B\u1E0D\u1E0F\u1E11\u1E13\u1E15\u1E17"
  6479.    U"\u1E19\u1E1B\u1E1D\u1E1F\u1E21\u1E23\u1E25\u1E27\u1E29\u1E2B\u1E2D"
  6480.    U"\u1E2F\u1E31\u1E33\u1E35\u1E37\u1E39\u1E3B\u1E3D\u1E3F\u1E41\u1E43"
  6481.    U"\u1E45\u1E47\u1E49\u1E4B\u1E4D\u1E4F\u1E51\u1E53\u1E55\u1E57\u1E59"
  6482.    U"\u1E5B\u1E5D\u1E5F\u1E61\u1E63\u1E65\u1E67\u1E69\u1E6B\u1E6D\u1E6F"
  6483.    U"\u1E71\u1E73\u1E75\u1E77\u1E79\u1E7B\u1E7D\u1E7F\u1E81\u1E83\u1E85"
  6484.    U"\u1E87\u1E89\u1E8B\u1E8D\u1E8F\u1E91\u1E93\u1E95\u00DF\u1EA1\u1EA3"
  6485.    U"\u1EA5\u1EA7\u1EA9\u1EAB\u1EAD\u1EAF\u1EB1\u1EB3\u1EB5\u1EB7\u1EB9"
  6486.    U"\u1EBB\u1EBD\u1EBF\u1EC1\u1EC3\u1EC5\u1EC7\u1EC9\u1ECB\u1ECD\u1ECF"
  6487.    U"\u1ED1\u1ED3\u1ED5\u1ED7\u1ED9\u1EDB\u1EDD\u1EDF\u1EE1\u1EE3\u1EE5"
  6488.    U"\u1EE7\u1EE9\u1EEB\u1EED\u1EEF\u1EF1\u1EF3\u1EF5\u1EF7\u1EF9\u1EFB"
  6489.    U"\u1EFD\u1EFF\u1F00\u1F01\u1F02\u1F03\u1F04\u1F05\u1F06\u1F07\u1F10"
  6490.    U"\u1F11\u1F12\u1F13\u1F14\u1F15\u1F20\u1F21\u1F22\u1F23\u1F24\u1F25"
  6491.    U"\u1F26\u1F27\u1F30\u1F31\u1F32\u1F33\u1F34\u1F35\u1F36\u1F37\u1F40"
  6492.    U"\u1F41\u1F42\u1F43\u1F44\u1F45\u1F51\u1F53\u1F55\u1F57\u1F60\u1F61"
  6493.    U"\u1F62\u1F63\u1F64\u1F65\u1F66\u1F67\u1FB0\u1FB1\u1F70\u1F71\u1F72"
  6494.    U"\u1F73\u1F74\u1F75\u1FD0\u1FD1\u1F76\u1F77\u1FE0\u1FE1\u1F7A\u1F7B"
  6495.    U"\u1FE5\u1F78\u1F79\u1F7C\u1F7D\u03C9\u006B\u00E5\u214E\u2184\u2C30"
  6496.    U"\u2C31\u2C32\u2C33\u2C34\u2C35\u2C36\u2C37\u2C38\u2C39\u2C3A\u2C3B"
  6497.    U"\u2C3C\u2C3D\u2C3E\u2C3F\u2C40\u2C41\u2C42\u2C43\u2C44\u2C45\u2C46"
  6498.    U"\u2C47\u2C48\u2C49\u2C4A\u2C4B\u2C4C\u2C4D\u2C4E\u2C4F\u2C50\u2C51"
  6499.    U"\u2C52\u2C53\u2C54\u2C55\u2C56\u2C57\u2C58\u2C59\u2C5A\u2C5B\u2C5C"
  6500.    U"\u2C5D\u2C5E\u2C61\u026B\u1D7D\u027D\u2C68\u2C6A\u2C6C\u0251\u0271"
  6501.    U"\u0250\u0252\u2C73\u2C76\u023F\u0240\u2C81\u2C83\u2C85\u2C87\u2C89"
  6502.    U"\u2C8B\u2C8D\u2C8F\u2C91\u2C93\u2C95\u2C97\u2C99\u2C9B\u2C9D\u2C9F"
  6503.    U"\u2CA1\u2CA3\u2CA5\u2CA7\u2CA9\u2CAB\u2CAD\u2CAF\u2CB1\u2CB3\u2CB5"
  6504.    U"\u2CB7\u2CB9\u2CBB\u2CBD\u2CBF\u2CC1\u2CC3\u2CC5\u2CC7\u2CC9\u2CCB"
  6505.    U"\u2CCD\u2CCF\u2CD1\u2CD3\u2CD5\u2CD7\u2CD9\u2CDB\u2CDD\u2CDF\u2CE1"
  6506.    U"\u2CE3\u2CEC\u2CEE\u2CF3\uA641\uA643\uA645\uA647\uA649\uA64B\uA64D"
  6507.    U"\uA64F\uA651\uA653\uA655\uA657\uA659\uA65B\uA65D\uA65F\uA661\uA663"
  6508.    U"\uA665\uA667\uA669\uA66B\uA66D\uA681\uA683\uA685\uA687\uA689\uA68B"
  6509.    U"\uA68D\uA68F\uA691\uA693\uA695\uA697\uA699\uA69B\uA723\uA725\uA727"
  6510.    U"\uA729\uA72B\uA72D\uA72F\uA733\uA735\uA737\uA739\uA73B\uA73D\uA73F"
  6511.    U"\uA741\uA743\uA745\uA747\uA749\uA74B\uA74D\uA74F\uA751\uA753\uA755"
  6512.    U"\uA757\uA759\uA75B\uA75D\uA75F\uA761\uA763\uA765\uA767\uA769\uA76B"
  6513.    U"\uA76D\uA76F\uA77A\uA77C\u1D79\uA77F\uA781\uA783\uA785\uA787\uA78C"
  6514.    U"\u0265\uA791\uA793\uA797\uA799\uA79B\uA79D\uA79F\uA7A1\uA7A3\uA7A5"
  6515.    U"\uA7A7\uA7A9\u0266\u025C\u0261\u026C\u026A\u029E\u0287\u029D\uAB53"
  6516.    U"\uA7B5\uA7B7\uA7B9\uA7BB\uA7BD\uA7BF\uA7C3\uA794\u0282\u1D8E\uFF41"
  6517.    U"\uFF42\uFF43\uFF44\uFF45\uFF46\uFF47\uFF48\uFF49\uFF4A\uFF4B\uFF4C"
  6518.    U"\uFF4D\uFF4E\uFF4F\uFF50\uFF51\uFF52\uFF53\uFF54\uFF55\uFF56\uFF57"
  6519.    U"\uFF58\uFF59\uFF5A\U00010428\U00010429\U0001042A\U0001042B\U0001042C"
  6520.    U"\U0001042D\U0001042E\U0001042F\U00010430\U00010431\U00010432\U00010433"
  6521.    U"\U00010434\U00010435\U00010436\U00010437\U00010438\U00010439\U0001043A"
  6522.    U"\U0001043B\U0001043C\U0001043D\U0001043E\U0001043F\U00010440\U00010441"
  6523.    U"\U00010442\U00010443\U00010444\U00010445\U00010446\U00010447\U00010448"
  6524.    U"\U00010449\U0001044A\U0001044B\U0001044C\U0001044D\U0001044E\U0001044F"
  6525.    U"\U000104D8\U000104D9\U000104DA\U000104DB\U000104DC\U000104DD\U000104DE"
  6526.    U"\U000104DF\U000104E0\U000104E1\U000104E2\U000104E3\U000104E4\U000104E5"
  6527.    U"\U000104E6\U000104E7\U000104E8\U000104E9\U000104EA\U000104EB\U000104EC"
  6528.    U"\U000104ED\U000104EE\U000104EF\U000104F0\U000104F1\U000104F2\U000104F3"
  6529.    U"\U000104F4\U000104F5\U000104F6\U000104F7\U000104F8\U000104F9\U000104FA"
  6530.    U"\U000104FB\U00010CC0\U00010CC1\U00010CC2\U00010CC3\U00010CC4\U00010CC5"
  6531.    U"\U00010CC6\U00010CC7\U00010CC8\U00010CC9\U00010CCA\U00010CCB\U00010CCC"
  6532.    U"\U00010CCD\U00010CCE\U00010CCF\U00010CD0\U00010CD1\U00010CD2\U00010CD3"
  6533.    U"\U00010CD4\U00010CD5\U00010CD6\U00010CD7\U00010CD8\U00010CD9\U00010CDA"
  6534.    U"\U00010CDB\U00010CDC\U00010CDD\U00010CDE\U00010CDF\U00010CE0\U00010CE1"
  6535.    U"\U00010CE2\U00010CE3\U00010CE4\U00010CE5\U00010CE6\U00010CE7\U00010CE8"
  6536.    U"\U00010CE9\U00010CEA\U00010CEB\U00010CEC\U00010CED\U00010CEE\U00010CEF"
  6537.    U"\U00010CF0\U00010CF1\U00010CF2\U000118C0\U000118C1\U000118C2\U000118C3"
  6538.    U"\U000118C4\U000118C5\U000118C6\U000118C7\U000118C8\U000118C9\U000118CA"
  6539.    U"\U000118CB\U000118CC\U000118CD\U000118CE\U000118CF\U000118D0\U000118D1"
  6540.    U"\U000118D2\U000118D3\U000118D4\U000118D5\U000118D6\U000118D7\U000118D8"
  6541.    U"\U000118D9\U000118DA\U000118DB\U000118DC\U000118DD\U000118DE\U000118DF"
  6542.    U"\U00016E60\U00016E61\U00016E62\U00016E63\U00016E64\U00016E65\U00016E66"
  6543.    U"\U00016E67\U00016E68\U00016E69\U00016E6A\U00016E6B\U00016E6C\U00016E6D"
  6544.    U"\U00016E6E\U00016E6F\U00016E70\U00016E71\U00016E72\U00016E73\U00016E74"
  6545.    U"\U00016E75\U00016E76\U00016E77\U00016E78\U00016E79\U00016E7A\U00016E7B"
  6546.    U"\U00016E7C\U00016E7D\U00016E7E\U00016E7F\U0001E922\U0001E923\U0001E924"
  6547.    U"\U0001E925\U0001E926\U0001E927\U0001E928\U0001E929\U0001E92A\U0001E92B"
  6548.    U"\U0001E92C\U0001E92D\U0001E92E\U0001E92F\U0001E930\U0001E931\U0001E932"
  6549.    U"\U0001E933\U0001E934\U0001E935\U0001E936\U0001E937\U0001E938\U0001E939"
  6550.    U"\U0001E93A\U0001E93B\U0001E93C\U0001E93D\U0001E93E\U0001E93F\U0001E940"
  6551.    U"\U0001E941\U0001E942\U0001E943";
  6552.  
  6553. constexpr char32_t grossbuchstaben[] =
  6554.    U"\u0041\u0042\u0043\u0044\u0045\u0046\u0047\u0048\u0049\u004A\u004B"
  6555.    U"\u004C\u004D\u004E\u004F\u0050\u0051\u0052\u0053\u0054\u0055\u0056"
  6556.    U"\u0057\u0058\u0059\u005A\u00C0\u00C1\u00C2\u00C3\u00C4\u00C5\u00C6"
  6557.    U"\u00C7\u00C8\u00C9\u00CA\u00CB\u00CC\u00CD\u00CE\u00CF\u00D0\u00D1"
  6558.    U"\u00D2\u00D3\u00D4\u00D5\u00D6\u00D8\u00D9\u00DA\u00DB\u00DC\u00DD"
  6559.    U"\u00DE\u0100\u0102\u0104\u0106\u0108\u010A\u010C\u010E\u0110\u0112"
  6560.    U"\u0114\u0116\u0118\u011A\u011C\u011E\u0120\u0122\u0124\u0126\u0128"
  6561.    U"\u012A\u012C\u012E\u0130\u0132\u0134\u0136\u0139\u013B\u013D\u013F"
  6562.    U"\u0141\u0143\u0145\u0147\u014A\u014C\u014E\u0150\u0152\u0154\u0156"
  6563.    U"\u0158\u015A\u015C\u015E\u0160\u0162\u0164\u0166\u0168\u016A\u016C"
  6564.    U"\u016E\u0170\u0172\u0174\u0176\u0178\u0179\u017B\u017D\u0181\u0182"
  6565.    U"\u0184\u0186\u0187\u0189\u018A\u018B\u018E\u018F\u0190\u0191\u0193"
  6566.    U"\u0194\u0196\u0197\u0198\u019C\u019D\u019F\u01A0\u01A2\u01A4\u01A6"
  6567.    U"\u01A7\u01A9\u01AC\u01AE\u01AF\u01B1\u01B2\u01B3\u01B5\u01B7\u01B8"
  6568.    U"\u01BC\u01C4\u01C7\u01CA\u01CD\u01CF\u01D1\u01D3\u01D5\u01D7\u01D9"
  6569.    U"\u01DB\u01DE\u01E0\u01E2\u01E4\u01E6\u01E8\u01EA\u01EC\u01EE\u01F1"
  6570.    U"\u01F4\u01F6\u01F7\u01F8\u01FA\u01FC\u01FE\u0200\u0202\u0204\u0206"
  6571.    U"\u0208\u020A\u020C\u020E\u0210\u0212\u0214\u0216\u0218\u021A\u021C"
  6572.    U"\u021E\u0220\u0222\u0224\u0226\u0228\u022A\u022C\u022E\u0230\u0232"
  6573.    U"\u023A\u023B\u023D\u023E\u0241\u0243\u0244\u0245\u0246\u0248\u024A"
  6574.    U"\u024C\u024E\u0370\u0372\u0376\u037F\u0386\u0388\u0389\u038A\u038C"
  6575.    U"\u038E\u038F\u0391\u0392\u0393\u0394\u0395\u0396\u0397\u0398\u0399"
  6576.    U"\u039A\u039B\u039C\u039D\u039E\u039F\u03A0\u03A1\u03A3\u03A4\u03A5"
  6577.    U"\u03A6\u03A7\u03A8\u03A9\u03AA\u03AB\u03CF\u03D8\u03DA\u03DC\u03DE"
  6578.    U"\u03E0\u03E2\u03E4\u03E6\u03E8\u03EA\u03EC\u03EE\u03F4\u03F7\u03F9"
  6579.    U"\u03FA\u03FD\u03FE\u03FF\u0400\u0401\u0402\u0403\u0404\u0405\u0406"
  6580.    U"\u0407\u0408\u0409\u040A\u040B\u040C\u040D\u040E\u040F\u0410\u0411"
  6581.    U"\u0412\u0413\u0414\u0415\u0416\u0417\u0418\u0419\u041A\u041B\u041C"
  6582.    U"\u041D\u041E\u041F\u0420\u0421\u0422\u0423\u0424\u0425\u0426\u0427"
  6583.    U"\u0428\u0429\u042A\u042B\u042C\u042D\u042E\u042F\u0460\u0462\u0464"
  6584.    U"\u0466\u0468\u046A\u046C\u046E\u0470\u0472\u0474\u0476\u0478\u047A"
  6585.    U"\u047C\u047E\u0480\u048A\u048C\u048E\u0490\u0492\u0494\u0496\u0498"
  6586.    U"\u049A\u049C\u049E\u04A0\u04A2\u04A4\u04A6\u04A8\u04AA\u04AC\u04AE"
  6587.    U"\u04B0\u04B2\u04B4\u04B6\u04B8\u04BA\u04BC\u04BE\u04C0\u04C1\u04C3"
  6588.    U"\u04C5\u04C7\u04C9\u04CB\u04CD\u04D0\u04D2\u04D4\u04D6\u04D8\u04DA"
  6589.    U"\u04DC\u04DE\u04E0\u04E2\u04E4\u04E6\u04E8\u04EA\u04EC\u04EE\u04F0"
  6590.    U"\u04F2\u04F4\u04F6\u04F8\u04FA\u04FC\u04FE\u0500\u0502\u0504\u0506"
  6591.    U"\u0508\u050A\u050C\u050E\u0510\u0512\u0514\u0516\u0518\u051A\u051C"
  6592.    U"\u051E\u0520\u0522\u0524\u0526\u0528\u052A\u052C\u052E\u0531\u0532"
  6593.    U"\u0533\u0534\u0535\u0536\u0537\u0538\u0539\u053A\u053B\u053C\u053D"
  6594.    U"\u053E\u053F\u0540\u0541\u0542\u0543\u0544\u0545\u0546\u0547\u0548"
  6595.    U"\u0549\u054A\u054B\u054C\u054D\u054E\u054F\u0550\u0551\u0552\u0553"
  6596.    U"\u0554\u0555\u0556\u10A0\u10A1\u10A2\u10A3\u10A4\u10A5\u10A6\u10A7"
  6597.    U"\u10A8\u10A9\u10AA\u10AB\u10AC\u10AD\u10AE\u10AF\u10B0\u10B1\u10B2"
  6598.    U"\u10B3\u10B4\u10B5\u10B6\u10B7\u10B8\u10B9\u10BA\u10BB\u10BC\u10BD"
  6599.    U"\u10BE\u10BF\u10C0\u10C1\u10C2\u10C3\u10C4\u10C5\u10C7\u10CD\u13A0"
  6600.    U"\u13A1\u13A2\u13A3\u13A4\u13A5\u13A6\u13A7\u13A8\u13A9\u13AA\u13AB"
  6601.    U"\u13AC\u13AD\u13AE\u13AF\u13B0\u13B1\u13B2\u13B3\u13B4\u13B5\u13B6"
  6602.    U"\u13B7\u13B8\u13B9\u13BA\u13BB\u13BC\u13BD\u13BE\u13BF\u13C0\u13C1"
  6603.    U"\u13C2\u13C3\u13C4\u13C5\u13C6\u13C7\u13C8\u13C9\u13CA\u13CB\u13CC"
  6604.    U"\u13CD\u13CE\u13CF\u13D0\u13D1\u13D2\u13D3\u13D4\u13D5\u13D6\u13D7"
  6605.    U"\u13D8\u13D9\u13DA\u13DB\u13DC\u13DD\u13DE\u13DF\u13E0\u13E1\u13E2"
  6606.    U"\u13E3\u13E4\u13E5\u13E6\u13E7\u13E8\u13E9\u13EA\u13EB\u13EC\u13ED"
  6607.    U"\u13EE\u13EF\u13F0\u13F1\u13F2\u13F3\u13F4\u13F5\u1C90\u1C91\u1C92"
  6608.    U"\u1C93\u1C94\u1C95\u1C96\u1C97\u1C98\u1C99\u1C9A\u1C9B\u1C9C\u1C9D"
  6609.    U"\u1C9E\u1C9F\u1CA0\u1CA1\u1CA2\u1CA3\u1CA4\u1CA5\u1CA6\u1CA7\u1CA8"
  6610.    U"\u1CA9\u1CAA\u1CAB\u1CAC\u1CAD\u1CAE\u1CAF\u1CB0\u1CB1\u1CB2\u1CB3"
  6611.    U"\u1CB4\u1CB5\u1CB6\u1CB7\u1CB8\u1CB9\u1CBA\u1CBD\u1CBE\u1CBF\u1E00"
  6612.    U"\u1E02\u1E04\u1E06\u1E08\u1E0A\u1E0C\u1E0E\u1E10\u1E12\u1E14\u1E16"
  6613.    U"\u1E18\u1E1A\u1E1C\u1E1E\u1E20\u1E22\u1E24\u1E26\u1E28\u1E2A\u1E2C"
  6614.    U"\u1E2E\u1E30\u1E32\u1E34\u1E36\u1E38\u1E3A\u1E3C\u1E3E\u1E40\u1E42"
  6615.    U"\u1E44\u1E46\u1E48\u1E4A\u1E4C\u1E4E\u1E50\u1E52\u1E54\u1E56\u1E58"
  6616.    U"\u1E5A\u1E5C\u1E5E\u1E60\u1E62\u1E64\u1E66\u1E68\u1E6A\u1E6C\u1E6E"
  6617.    U"\u1E70\u1E72\u1E74\u1E76\u1E78\u1E7A\u1E7C\u1E7E\u1E80\u1E82\u1E84"
  6618.    U"\u1E86\u1E88\u1E8A\u1E8C\u1E8E\u1E90\u1E92\u1E94\u1E9E\u1EA0\u1EA2"
  6619.    U"\u1EA4\u1EA6\u1EA8\u1EAA\u1EAC\u1EAE\u1EB0\u1EB2\u1EB4\u1EB6\u1EB8"
  6620.    U"\u1EBA\u1EBC\u1EBE\u1EC0\u1EC2\u1EC4\u1EC6\u1EC8\u1ECA\u1ECC\u1ECE"
  6621.    U"\u1ED0\u1ED2\u1ED4\u1ED6\u1ED8\u1EDA\u1EDC\u1EDE\u1EE0\u1EE2\u1EE4"
  6622.    U"\u1EE6\u1EE8\u1EEA\u1EEC\u1EEE\u1EF0\u1EF2\u1EF4\u1EF6\u1EF8\u1EFA"
  6623.    U"\u1EFC\u1EFE\u1F08\u1F09\u1F0A\u1F0B\u1F0C\u1F0D\u1F0E\u1F0F\u1F18"
  6624.    U"\u1F19\u1F1A\u1F1B\u1F1C\u1F1D\u1F28\u1F29\u1F2A\u1F2B\u1F2C\u1F2D"
  6625.    U"\u1F2E\u1F2F\u1F38\u1F39\u1F3A\u1F3B\u1F3C\u1F3D\u1F3E\u1F3F\u1F48"
  6626.    U"\u1F49\u1F4A\u1F4B\u1F4C\u1F4D\u1F59\u1F5B\u1F5D\u1F5F\u1F68\u1F69"
  6627.    U"\u1F6A\u1F6B\u1F6C\u1F6D\u1F6E\u1F6F\u1FB8\u1FB9\u1FBA\u1FBB\u1FC8"
  6628.    U"\u1FC9\u1FCA\u1FCB\u1FD8\u1FD9\u1FDA\u1FDB\u1FE8\u1FE9\u1FEA\u1FEB"
  6629.    U"\u1FEC\u1FF8\u1FF9\u1FFA\u1FFB\u2126\u212A\u212B\u2132\u2183\u2C00"
  6630.    U"\u2C01\u2C02\u2C03\u2C04\u2C05\u2C06\u2C07\u2C08\u2C09\u2C0A\u2C0B"
  6631.    U"\u2C0C\u2C0D\u2C0E\u2C0F\u2C10\u2C11\u2C12\u2C13\u2C14\u2C15\u2C16"
  6632.    U"\u2C17\u2C18\u2C19\u2C1A\u2C1B\u2C1C\u2C1D\u2C1E\u2C1F\u2C20\u2C21"
  6633.    U"\u2C22\u2C23\u2C24\u2C25\u2C26\u2C27\u2C28\u2C29\u2C2A\u2C2B\u2C2C"
  6634.    U"\u2C2D\u2C2E\u2C60\u2C62\u2C63\u2C64\u2C67\u2C69\u2C6B\u2C6D\u2C6E"
  6635.    U"\u2C6F\u2C70\u2C72\u2C75\u2C7E\u2C7F\u2C80\u2C82\u2C84\u2C86\u2C88"
  6636.    U"\u2C8A\u2C8C\u2C8E\u2C90\u2C92\u2C94\u2C96\u2C98\u2C9A\u2C9C\u2C9E"
  6637.    U"\u2CA0\u2CA2\u2CA4\u2CA6\u2CA8\u2CAA\u2CAC\u2CAE\u2CB0\u2CB2\u2CB4"
  6638.    U"\u2CB6\u2CB8\u2CBA\u2CBC\u2CBE\u2CC0\u2CC2\u2CC4\u2CC6\u2CC8\u2CCA"
  6639.    U"\u2CCC\u2CCE\u2CD0\u2CD2\u2CD4\u2CD6\u2CD8\u2CDA\u2CDC\u2CDE\u2CE0"
  6640.    U"\u2CE2\u2CEB\u2CED\u2CF2\uA640\uA642\uA644\uA646\uA648\uA64A\uA64C"
  6641.    U"\uA64E\uA650\uA652\uA654\uA656\uA658\uA65A\uA65C\uA65E\uA660\uA662"
  6642.    U"\uA664\uA666\uA668\uA66A\uA66C\uA680\uA682\uA684\uA686\uA688\uA68A"
  6643.    U"\uA68C\uA68E\uA690\uA692\uA694\uA696\uA698\uA69A\uA722\uA724\uA726"
  6644.    U"\uA728\uA72A\uA72C\uA72E\uA732\uA734\uA736\uA738\uA73A\uA73C\uA73E"
  6645.    U"\uA740\uA742\uA744\uA746\uA748\uA74A\uA74C\uA74E\uA750\uA752\uA754"
  6646.    U"\uA756\uA758\uA75A\uA75C\uA75E\uA760\uA762\uA764\uA766\uA768\uA76A"
  6647.    U"\uA76C\uA76E\uA779\uA77B\uA77D\uA77E\uA780\uA782\uA784\uA786\uA78B"
  6648.    U"\uA78D\uA790\uA792\uA796\uA798\uA79A\uA79C\uA79E\uA7A0\uA7A2\uA7A4"
  6649.    U"\uA7A6\uA7A8\uA7AA\uA7AB\uA7AC\uA7AD\uA7AE\uA7B0\uA7B1\uA7B2\uA7B3"
  6650.    U"\uA7B4\uA7B6\uA7B8\uA7BA\uA7BC\uA7BE\uA7C2\uA7C4\uA7C5\uA7C6\uFF21"
  6651.    U"\uFF22\uFF23\uFF24\uFF25\uFF26\uFF27\uFF28\uFF29\uFF2A\uFF2B\uFF2C"
  6652.    U"\uFF2D\uFF2E\uFF2F\uFF30\uFF31\uFF32\uFF33\uFF34\uFF35\uFF36\uFF37"
  6653.    U"\uFF38\uFF39\uFF3A\U00010400\U00010401\U00010402\U00010403\U00010404"
  6654.    U"\U00010405\U00010406\U00010407\U00010408\U00010409\U0001040A\U0001040B"
  6655.    U"\U0001040C\U0001040D\U0001040E\U0001040F\U00010410\U00010411\U00010412"
  6656.    U"\U00010413\U00010414\U00010415\U00010416\U00010417\U00010418\U00010419"
  6657.    U"\U0001041A\U0001041B\U0001041C\U0001041D\U0001041E\U0001041F\U00010420"
  6658.    U"\U00010421\U00010422\U00010423\U00010424\U00010425\U00010426\U00010427"
  6659.    U"\U000104B0\U000104B1\U000104B2\U000104B3\U000104B4\U000104B5\U000104B6"
  6660.    U"\U000104B7\U000104B8\U000104B9\U000104BA\U000104BB\U000104BC\U000104BD"
  6661.    U"\U000104BE\U000104BF\U000104C0\U000104C1\U000104C2\U000104C3\U000104C4"
  6662.    U"\U000104C5\U000104C6\U000104C7\U000104C8\U000104C9\U000104CA\U000104CB"
  6663.    U"\U000104CC\U000104CD\U000104CE\U000104CF\U000104D0\U000104D1\U000104D2"
  6664.    U"\U000104D3\U00010C80\U00010C81\U00010C82\U00010C83\U00010C84\U00010C85"
  6665.    U"\U00010C86\U00010C87\U00010C88\U00010C89\U00010C8A\U00010C8B\U00010C8C"
  6666.    U"\U00010C8D\U00010C8E\U00010C8F\U00010C90\U00010C91\U00010C92\U00010C93"
  6667.    U"\U00010C94\U00010C95\U00010C96\U00010C97\U00010C98\U00010C99\U00010C9A"
  6668.    U"\U00010C9B\U00010C9C\U00010C9D\U00010C9E\U00010C9F\U00010CA0\U00010CA1"
  6669.    U"\U00010CA2\U00010CA3\U00010CA4\U00010CA5\U00010CA6\U00010CA7\U00010CA8"
  6670.    U"\U00010CA9\U00010CAA\U00010CAB\U00010CAC\U00010CAD\U00010CAE\U00010CAF"
  6671.    U"\U00010CB0\U00010CB1\U00010CB2\U000118A0\U000118A1\U000118A2\U000118A3"
  6672.    U"\U000118A4\U000118A5\U000118A6\U000118A7\U000118A8\U000118A9\U000118AA"
  6673.    U"\U000118AB\U000118AC\U000118AD\U000118AE\U000118AF\U000118B0\U000118B1"
  6674.    U"\U000118B2\U000118B3\U000118B4\U000118B5\U000118B6\U000118B7\U000118B8"
  6675.    U"\U000118B9\U000118BA\U000118BB\U000118BC\U000118BD\U000118BE\U000118BF"
  6676.    U"\U00016E40\U00016E41\U00016E42\U00016E43\U00016E44\U00016E45\U00016E46"
  6677.    U"\U00016E47\U00016E48\U00016E49\U00016E4A\U00016E4B\U00016E4C\U00016E4D"
  6678.    U"\U00016E4E\U00016E4F\U00016E50\U00016E51\U00016E52\U00016E53\U00016E54"
  6679.    U"\U00016E55\U00016E56\U00016E57\U00016E58\U00016E59\U00016E5A\U00016E5B"
  6680.    U"\U00016E5C\U00016E5D\U00016E5E\U00016E5F\U0001E900\U0001E901\U0001E902"
  6681.    U"\U0001E903\U0001E904\U0001E905\U0001E906\U0001E907\U0001E908\U0001E909"
  6682.    U"\U0001E90A\U0001E90B\U0001E90C\U0001E90D\U0001E90E\U0001E90F\U0001E910"
  6683.    U"\U0001E911\U0001E912\U0001E913\U0001E914\U0001E915\U0001E916\U0001E917"
  6684.    U"\U0001E918\U0001E919\U0001E91A\U0001E91B\U0001E91C\U0001E91D\U0001E91E"
  6685.    U"\U0001E91F\U0001E920\U0001E921";
  6686. }
  6687.  
  6688. Unicode::Unicode(){
  6689.    for(size_t i = 0; i < sizeof(grossbuchstaben)/sizeof(char32_t)-1; ++i){
  6690.       gross_in_klein[grossbuchstaben[i]] = kleinbuchstaben[i];
  6691.       buchstaben.insert(grossbuchstaben[i]);
  6692.       buchstaben.insert(kleinbuchstaben[i]);
  6693.    }
  6694. }
  6695.  
  6696. char32_t
  6697. Unicode::kleinbuchstabe(char32_t c) const {
  6698.    const auto i = gross_in_klein.find(c);
  6699.    return i == gross_in_klein.end() ? c : i->second;
  6700. }
  6701. bool
  6702. Unicode::ist_buchstabe(char32_t c) const {
  6703.    return buchstaben.find(c) != buchstaben.end();
  6704. }
  6705.  
  6706. //--------------- src/utfhilfe.cc ---------------
  6707. //#include "utfhilfe.hh"
  6708.  
  6709. #include <vector>
  6710.  
  6711. namespace {
  6712.    int
  6713.    utf8_laenge(const char* s, bool& fehler)
  6714.    {
  6715.       if(*s == 0) return 0;
  6716.       const unsigned c = *s & 0xff;
  6717.  
  6718.       if(c < 0x80) return 1;
  6719.       if(c >= 0xc2){
  6720.          if(c < 0xe0) return 2;
  6721.          if(c < 0xf0) return 3;
  6722.          if(c < 0xf5) return 4; // nicht alle 4-Zeichen-Sequenzen sind erlaubt.
  6723.          // 5- und 6-Zeichen-Sequenzen sind nicht erlaubt.
  6724.       }
  6725.       fehler = true;
  6726.       return 0;
  6727.    }
  6728.  
  6729.    char*
  6730.    utf32_in_utf8_l(char32_t c, char* acc) {
  6731.       if(c < 0x80){
  6732.          *acc = c & 0x7f;
  6733.          return acc;
  6734.       }
  6735.  
  6736.       char32_t hochwertig = 0x80, add = 0x40;
  6737.       do{
  6738.          char r = c & 0x3f;
  6739.          c = (c-r) >> 6;
  6740.          *acc-- = r+0x80;
  6741.          hochwertig = hochwertig+add;
  6742.          add = add >> 1;
  6743.       }while(c >= add);
  6744.       *acc = hochwertig+c;
  6745.       return acc;
  6746.    }
  6747.  
  6748.    inline char32_t
  6749.    folgezeichen(const char c, bool& fehler){
  6750.       const char32_t z = c & 0xff;
  6751.       if(z < 0x80 || z >= 0xc0) fehler = true;
  6752.       return z;
  6753.    }
  6754. }
  6755.  
  6756. std::string
  6757. utf32_in_utf8(char32_t c) {
  6758.    char acc[5], *e = acc+4, *p = utf32_in_utf8_l(c, acc+3);
  6759.    *e = 0;
  6760.    return std::string(p, e-p);
  6761. }
  6762.  
  6763. std::string
  6764. utf32_in_utf8(const std::u32string& s){
  6765.    const size_t l = s.length();
  6766.    std::vector<char> res(4*l+1);
  6767.    char *e = &res[4*l], *p = e;
  6768.    *e = 0;
  6769.    for(size_t i = l; i-- > 0;) p = utf32_in_utf8_l(s[i], --p);
  6770.    return std::string(p, e-p);
  6771. }
  6772. std::string
  6773. utf32_in_utf8(uint64_t u){
  6774.    char res[17], *e = res+16, *p = e;
  6775.    *e = 0;
  6776.    while(u){
  6777.       p = utf32_in_utf8_l(u & utf_maske, --p);
  6778.       u >>= 21;
  6779.    }
  6780.    return std::string(p, e-p);
  6781. }
  6782.  
  6783. char32_t
  6784. utf8_in_utf32(const char*& s, bool& fehler){
  6785.    if(*s == 0) return 0;
  6786.    const int l = utf8_laenge(s, fehler);
  6787.    char32_t sum = *s++ & 0xff;
  6788.    if(l < 2) return sum;
  6789.  
  6790.    sum &= (1 << (7-l))-1;
  6791.    for(int i = 1; !fehler && i < l; ++i)
  6792.       sum = (sum << 6)+(folgezeichen(*s++, fehler)-0x80);
  6793.  
  6794.    return sum;
  6795. }
  6796.  
  6797. std::u32string zahl_in_utf32(int z){
  6798.    const std::string s = std::to_string(z);
  6799.    const size_t l = s.length();
  6800.    std::u32string u;
  6801.    for(size_t i = 0; i < l; ++i) u.push_back(s[i] & 0xff);
  6802.    return u;
  6803. }
  6804.  
  6805.  
  6806. #ifdef AUSGABE_8BIT
  6807.  
  6808. std::string
  6809. utf32_in_ausgabe(char32_t c){
  6810.    char acc[2]; acc[0] = c; acc[1] = 0;
  6811.    return std::string(acc, 1);
  6812. }
  6813.  
  6814. std::string
  6815. utf32_in_ausgabe(const std::u32string& s){
  6816.    const size_t l = s.length();
  6817.    std::string res; res.reserve(l);
  6818.    for(size_t i = 0; i < l; ++i) res.push_back(s[i]);
  6819.    return res;
  6820. }
  6821.  
  6822. #else
  6823.  
  6824. std::string utf32_in_ausgabe(char32_t c)
  6825. { return utf32_in_utf8(c); }
  6826.  
  6827. std::string utf32_in_ausgabe(const std::u32string& s)
  6828. { return utf32_in_utf8(s);}
  6829.  
  6830. #endif // !AUSGABE_8BIT
  6831.  
  6832. bool ist_ziffer(char32_t c){ return c >= U'0' && c <= U'9'; }
  6833. bool ist_zwischenraum(char32_t c){ return c == U' ' || c == U'\t'; }
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
 
Top