"Fossies" - the Fresh Open Source Software Archive

Member "klavaro-3.13/src/accuracy.c" (18 Apr 2021, 13729 Bytes) of package /linux/privat/klavaro-3.13.tar.bz2:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "accuracy.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 3.12_vs_3.13.

    1 /**************************************************************************/
    2 /*  Klavaro - a flexible touch typing tutor                               */
    3 /*  Copyright (C) 2005-2021 Felipe Emmanuel Ferreira de Castro            */
    4 /*                                                                        */
    5 /*  This file is part of Klavaro, which is a free software: you can       */
    6 /*  redistribute it and/or modify it under the terms of the GNU General   */
    7 /*  Public License as published by the Free Software Foundation, either   */
    8 /*  version 3 of the License, or (at your option) any later version.      */
    9 /*                                                                        */
   10 /*  Klavaro is distributed in the hope that it will be useful,            */
   11 /*  but WITHOUT ANY WARRANTY; without even the implied warranty of        */
   12 /*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         */
   13 /*  GNU General Public License for more details (in the file COPYING).    */
   14 /*  You should have received a copy of the GNU General Public License     */
   15 /*  along with Klavaro.  If not, see <https://www.gnu.org/licenses/>      */
   16 /**************************************************************************/
   17 
   18 #include <stdio.h>
   19 #include <stdlib.h>
   20 #include <math.h>
   21 #include <string.h>
   22 #include <glib.h>
   23 #include <glib/gstdio.h>
   24 
   25 #include "main.h"
   26 #include "auxiliar.h"
   27 #include "keyboard.h"
   28 #include "adaptability.h"
   29 #include "accuracy.h"
   30 #include "plot.h"
   31 
   32 /* Touch errors
   33  */
   34 struct
   35 {
   36     gunichar uchr;
   37     gulong wrong;
   38     gulong correct;
   39 } terror[MAX_CHARS_EVALUATED];
   40 gint terror_n = 0;
   41 
   42 /* Touch times
   43  */
   44 struct
   45 {
   46     gunichar uchr;
   47     gdouble dt[MAX_TT_SAVED + 2];
   48     gint idx;
   49 } ttime[MAX_CHARS_EVALUATED];
   50 gint ttime_n = 0;
   51 
   52 /* Simple reset
   53  */
   54 void
   55 accur_terror_reset ()
   56 {
   57     memset (&terror, 0, sizeof (terror));
   58     terror_n = 0;
   59 }
   60 
   61 void
   62 accur_ttime_reset ()
   63 {
   64     memset (&ttime, 0, sizeof (ttime));
   65     ttime_n = 0;
   66 }
   67 
   68 void
   69 accur_reset ()
   70 {
   71     accur_terror_reset ();
   72     accur_ttime_reset ();
   73 }
   74 
   75 gint
   76 accur_terror_n_get (void)
   77 {
   78     return (terror_n);
   79 }
   80 
   81 gint
   82 accur_ttime_n_get (void)
   83 {
   84     return (ttime_n);
   85 }
   86 
   87 gchar *
   88 accur_terror_char_utf8 (gint i)
   89 {
   90     gchar *utf8;
   91     gint n;
   92 
   93     if (i < 0 || i >= terror_n)
   94         return (g_strdup (" "));
   95 
   96     utf8 = g_malloc (UTF8_BUFFER);
   97     n = g_unichar_to_utf8 (terror[i].uchr, utf8);
   98     if (n < 1)
   99         return (g_strdup (" "));
  100     utf8[n] = '\0';
  101 
  102     return (utf8);
  103 }
  104 
  105 gchar *
  106 accur_ttime_char_utf8 (gint i)
  107 {
  108     gchar *utf8;
  109     gint n;
  110 
  111     if (i < 0 || i >= ttime_n)
  112         return (g_strdup (" "));
  113 
  114     utf8 = g_malloc (UTF8_BUFFER);
  115     n = g_unichar_to_utf8 (ttime[i].uchr, utf8);
  116     if (n < 1)
  117         return (g_strdup (" "));
  118     utf8[n] = '\0';
  119 
  120     return (utf8);
  121 }
  122 
  123 gulong
  124 accur_wrong_get (gint i)
  125 {
  126     if (i < 0 || i >= terror_n)
  127         return -1;
  128 
  129     return (terror[i].wrong);
  130 }
  131 
  132 void
  133 accur_ttime_print (void)
  134 {
  135     gint i, j;
  136 
  137     g_printf ("\n");
  138     for (i = 0; i < ttime_n; i++)
  139     {
  140         g_printf ("%2i - %C - (%02i)", i, ttime[i].uchr, ttime[i].idx);
  141         for (j = 0; j < ttime[i].idx; j+=1)
  142             g_printf (" %2i - %.2lf", j, ttime[i].dt[j]);
  143         g_printf ("\n");
  144     }
  145 }
  146 
  147 /**********************************************************************
  148  * Open the accuracy accumulators
  149  */
  150 void
  151 accur_init ()
  152 {
  153     gint i, j;
  154     gchar *tmp;
  155     gchar tmpchr[UTF8_BUFFER];
  156     gchar *kb_name;
  157     gchar *data;
  158     gchar *dtp;
  159     gint nok;
  160     gboolean success;
  161     gunichar uchr;
  162     gulong wrong;
  163     gulong correct;
  164     gdouble dt, dummy;
  165     gsize len;
  166 
  167     accur_reset ();
  168 
  169     kb_name = g_strdup (keyb_get_name ());
  170     for (i=0; kb_name[i]; i++)
  171         kb_name[i] = (kb_name[i] == ' ') ? '_' : kb_name[i];
  172 
  173     /*
  174      * First, the accuracy log
  175      */
  176     tmp = g_strconcat (main_path_stats (), G_DIR_SEPARATOR_S, ACCUR_LOG_FILE, "_", kb_name, NULL);
  177     success = g_file_get_contents (tmp, &data, &len, NULL);
  178     if (!success)
  179         g_message ("Empty accuracy log: %s", tmp);
  180     else if (len > 0)
  181     {
  182         dtp = data;
  183         for (i = 0; i < MAX_CHARS_EVALUATED; i++)
  184         {
  185             if (3 != sscanf (dtp, "%6s\t%lu\t%lu\n", tmpchr, &wrong, &correct)) 
  186             {
  187                 g_warning ("Accuracy file input error!");
  188                 break;
  189             }
  190             uchr = g_utf8_get_char_validated (tmpchr, -1);
  191             if (uchr == (gunichar)-1 || uchr == (gunichar)-2) 
  192             {
  193                 g_warning ("Accuracy file UTF-8 input error!");
  194                 break;
  195             }
  196 
  197             if (1234 > wrong && wrong > 0 && correct <= ERROR_INERTIA)
  198             {
  199                 terror[i].uchr = uchr;
  200                 terror[i].wrong = wrong;
  201                 terror[i].correct = correct;
  202                 terror_n = i + 1;
  203             }
  204             else
  205                 break;
  206             while (dtp[0] != '\0' && dtp[0] != '\n') dtp++;
  207             if (dtp[0] == '\n') dtp++;
  208             if (dtp[0] == '\0') break;
  209         }
  210         g_free (data);
  211     }
  212     g_free (tmp);
  213 
  214     /*
  215      * Second, the proficiency log
  216      */
  217     tmp = g_strconcat (main_path_stats (), G_DIR_SEPARATOR_S, PROFI_LOG_FILE, "_", kb_name, NULL);
  218     success = g_file_get_contents (tmp, &data, &len, NULL);
  219     if (!success)
  220         g_message ("Empty proficiency log: %s", tmp);
  221     else if (len > 0)
  222     {
  223         dtp = data;
  224         for (i = 0; i < MAX_CHARS_EVALUATED; i++)
  225         {
  226             if (3 != sscanf (dtp, "%6s\t%lf\t%lf\n", tmpchr, &dt, &dummy)) 
  227             {
  228                 g_warning ("Proficiency file input error!");
  229                 break;
  230             }
  231 
  232             uchr = g_utf8_get_char_validated (tmpchr, -1);
  233             if (uchr == (gunichar)-1 || uchr == (gunichar)-2) 
  234             {
  235                 g_warning ("Proficiency file UTF-8 input error!");
  236                 break;
  237             }
  238 
  239             if (dt > 0)
  240             {
  241                 ttime[i].uchr = uchr;
  242                 for (j = 0; j < 3; j++)
  243                     ttime[i].dt[j] = dt;
  244                 ttime[i].idx = 3;
  245                 ttime_n = i + 1;
  246             }
  247             else
  248                 break;
  249 
  250             while (dtp[0] != '\0' && dtp[0] != '\n') dtp++;
  251             if (dtp[0] == '\n') dtp++;
  252             if (dtp[0] == '\0') break;
  253         }
  254         g_free (data);
  255     }
  256     g_free (tmp);
  257     g_free (kb_name);
  258     /* accur_ttime_print (); */
  259 }
  260 
  261 /**********************************************************************
  262  * Accumulates correctly typed characters
  263  */
  264 void
  265 accur_correct (gunichar uchr, double touch_time)
  266 {
  267     gint i, j;
  268 
  269     if (uchr == L' ' || uchr == UPSYM || uchr == L'\t' || uchr == L'\b')
  270         return;
  271     if (!keyb_is_inset (uchr))
  272         return;
  273 
  274     /*
  275      * First, accuracy
  276      */
  277     for (i = 0; i < terror_n; i++)
  278     {
  279         if (uchr == terror[i].uchr)
  280         {
  281             if (terror[i].correct > ERROR_INERTIA)
  282             {
  283                 terror[i].wrong -= (terror[i].wrong == 0 ? 0 : 1);
  284                 terror[i].correct = 1;
  285             }
  286             else
  287                 terror[i].correct++;
  288             break;
  289         }
  290     }
  291 
  292     /*
  293      * Second, proficiency
  294      */
  295     if (touch_time < 0.001)
  296         return;
  297 
  298     /* If uchar is in the pool, add the touch time to it and return */
  299     uchr = g_unichar_tolower (uchr);
  300     for (i = 0; i < ttime_n; i++)
  301     {
  302         if (uchr == ttime[i].uchr)
  303         {
  304             ttime[i].dt[ttime[i].idx] = touch_time;
  305             ttime[i].idx++;
  306             if (ttime[i].idx >= (MAX_TT_SAVED - 1))
  307                 ttime[i].idx = 0;
  308             return;
  309         }
  310     }
  311 
  312     /* else, if there is room, */ 
  313     if (ttime_n >= MAX_CHARS_EVALUATED)
  314         return;
  315 
  316     /* include a new key in the pool */
  317     ttime[i].uchr = uchr;
  318     ttime[i].dt[0] = touch_time;
  319     for (j = 1; j < MAX_TT_SAVED; j++)
  320         ttime[i].dt[j] = 0.0;
  321     ttime[i].idx = 1;
  322     ttime_n++;
  323 }
  324 
  325 /**********************************************************************
  326  * Accumulates mistyped characters
  327  */
  328 void
  329 accur_wrong (gunichar uchr)
  330 {
  331     gint i;
  332     gint i_min = -1;
  333     gdouble correct_min = 1e9;
  334     gdouble hlp;
  335 
  336     if (uchr == L' ' || uchr == UPSYM || uchr == L'\t' || uchr == L'\b')
  337         return;
  338     if (!keyb_is_inset (uchr))
  339         return;
  340 
  341     /*
  342      * Only for accuracy
  343      */
  344     for (i = 0; i < terror_n; i++)
  345     {
  346         if (uchr == terror[i].uchr)
  347         {
  348             terror[i].wrong++;
  349             return;
  350         }
  351         hlp = ((gdouble) terror[i].wrong) / ((gdouble) terror[i].correct + terror[i].wrong + 1);
  352         if (hlp <= correct_min)
  353         {
  354             correct_min = hlp;
  355             i_min = i;
  356         }
  357     }
  358 
  359     if (terror_n < MAX_CHARS_EVALUATED)
  360     {
  361         i = terror_n;
  362         terror_n++;
  363     }
  364     else
  365     {
  366         i = (i_min > -1 ? i_min : terror_n - 1);
  367     }
  368 
  369     terror[i].uchr = uchr;
  370     terror[i].wrong = 1;
  371     terror[i].correct = 1;
  372 }
  373 
  374 gulong
  375 accur_error_total ()
  376 {
  377     gint i;
  378     gulong n = 0;
  379 
  380     for (i = 0; i < terror_n; i++)
  381         n += (terror[i].wrong < 12345 ? terror[i].wrong : 0);
  382 
  383     return n;
  384 }
  385 
  386 gdouble
  387 accur_profi_aver (gint idx)
  388 {
  389     gint i, n;
  390     gdouble sum;
  391 
  392     if (idx < 0)
  393         return -10;
  394 
  395     /* Simple average */
  396     n = 0; sum = 0;
  397     for (i = 0; i < MAX_TT_SAVED; i++)
  398     {
  399         if (ttime[idx].dt[i] > 0.0 && ttime[idx].dt[i] < 3.0)
  400         {
  401             sum += ttime[idx].dt[i];
  402             n++;
  403         }
  404     }
  405     if (n == 0)
  406         return 0.0;
  407     
  408     return (sum / n);
  409 }
  410 
  411 gint
  412 accur_profi_aver_norm (gint idx)
  413 {
  414     gint norm;
  415 
  416     if (idx < 0)
  417         return -1;
  418     if (accur_profi_aver (ttime_n-1) == 0)
  419         return -3;
  420 
  421     norm = rint (accur_profi_aver (idx) / accur_profi_aver (ttime_n-1));
  422     
  423     return norm;
  424 }
  425 
  426 /*******************************************************************************
  427  * Sorting first: decreasing wrongness; second: increasing correctness
  428  */
  429 void
  430 accur_terror_sort ()
  431 {
  432     gint i, j;
  433     gunichar uchr;
  434     gulong correct;
  435     gulong wrong;
  436 
  437     for (i = 1; i < terror_n; i++)
  438     {
  439         for (j = i; j > 0; j--)
  440         {
  441             if (terror[j].correct < terror[j-1].correct)
  442             {
  443                 uchr = terror[j].uchr;
  444                 terror[j].uchr = terror[j-1].uchr;
  445                 terror[j-1].uchr = uchr;
  446 
  447                 wrong = terror[j].wrong;
  448                 terror[j].wrong = terror[j-1].wrong;
  449                 terror[j-1].wrong = wrong;
  450 
  451                 correct = terror[j].correct;
  452                 terror[j].correct = terror[j-1].correct;
  453                 terror[j-1].correct = correct;
  454             }
  455         }
  456     }
  457     for (i = 1; i < terror_n; i++)
  458     {
  459         for (j = i; j > 0; j--)
  460         {
  461             if (terror[j].wrong > terror[j-1].wrong)
  462             {
  463                 uchr = terror[j].uchr;
  464                 terror[j].uchr = terror[j-1].uchr;
  465                 terror[j-1].uchr = uchr;
  466 
  467                 wrong = terror[j].wrong;
  468                 terror[j].wrong = terror[j-1].wrong;
  469                 terror[j-1].wrong = wrong;
  470 
  471                 correct = terror[j].correct;
  472                 terror[j].correct = terror[j-1].correct;
  473                 terror[j-1].correct = correct;
  474             }
  475         }
  476     }
  477 }
  478 
  479 /*******************************************************************************
  480  * Decreasing order, touch time 
  481  */
  482 void
  483 accur_ttime_sort ()
  484 {
  485     gint i, j, k;
  486     gunichar uchr;
  487     gdouble dt;
  488     gint idx;
  489 
  490     for (i = 1; i < ttime_n; i++)
  491     {
  492         for (j = i; j > 0; j--)
  493         {
  494             if (accur_profi_aver (j) > accur_profi_aver (j-1))
  495             {
  496                 uchr = ttime[j].uchr;
  497                 ttime[j].uchr = ttime[j-1].uchr;
  498                 ttime[j-1].uchr = uchr;
  499 
  500                 idx = ttime[j].idx;
  501                 ttime[j].idx = ttime[j-1].idx;
  502                 ttime[j-1].idx = idx;
  503 
  504                 for (k = 0; k < MAX_TT_SAVED; k++)
  505                 {
  506                     if (ttime[j].dt[k] == ttime[j-1].dt[k])
  507                         continue;
  508                     dt = ttime[j].dt[k];
  509                     ttime[j].dt[k] = ttime[j-1].dt[k];
  510                     ttime[j-1].dt[k] = dt;
  511                 }
  512             }
  513         }
  514     }
  515 
  516     for (i = ttime_n - 1; i > -1; i--)
  517     {
  518         if (accur_profi_aver (i) < 0.001)
  519         {
  520             ttime[i].uchr = 0;
  521             for (j = 0; j < MAX_TT_SAVED; j++)
  522                 ttime[i].dt[j] = 0;
  523             ttime[i].idx = 0;
  524             ttime_n--;
  525         }
  526         else
  527             break;
  528     }
  529     /* accur_ttime_print (); */
  530 }
  531 
  532 void
  533 accur_sort ()
  534 {
  535     accur_terror_sort ();
  536     accur_ttime_sort ();
  537 }
  538 
  539 /*******************************************************************************
  540  * Creates a random weird word based on error profile
  541  */
  542 gboolean
  543 accur_create_word (gunichar word[MAX_WORD_LEN + 1])
  544 {
  545     gint i, j;
  546     gint ind;
  547     gint n;
  548     gunichar vowels[20];
  549     gunichar last = 0;
  550     gint vlen;
  551     gboolean ptype_terror;
  552     gboolean ptype_ttime;
  553     static gint profile_type = 0;
  554 
  555     if (terror_n < 10 && ttime_n < 5)
  556         return FALSE;
  557 
  558     ptype_terror = accur_error_total () >= ERROR_LIMIT;
  559     if (ttime_n < 40)
  560         ptype_ttime = 0;
  561     else
  562         ptype_ttime = ((accur_profi_aver(9)/accur_profi_aver(39)) > PROFI_LIMIT);
  563     vlen = keyb_get_vowels (vowels);
  564     n = rand () % (MAX_WORD_LEN) + 1;
  565     for (i = 0; i < n; i++)
  566     {
  567         if (profile_type == 0)
  568             profile_type = ptype_ttime ? 1 : 0;
  569         else
  570             profile_type = ptype_terror ? 0 : 1;
  571 
  572         if (profile_type == 0) /* Error */
  573         {
  574             for (j = 0; j < 100; j++)
  575             {
  576                 ind = rand () % terror_n;
  577                 if (terror[ind].uchr == last)
  578                     continue;
  579                 if (rand () % terror[0].wrong < terror[ind].wrong)
  580                     break;
  581             }
  582             word[i] = terror[ind].uchr;
  583         }
  584         else /* Time */
  585         {
  586             for (j = 0; j < 100; j++)
  587             {
  588                 ind = rand () % ttime_n;
  589                 if (ttime[ind].uchr == last)
  590                     continue;
  591                 if ((rand () % accur_profi_aver_norm (0)) < accur_profi_aver_norm (ind))
  592                     break;
  593             }
  594             word[i] = ttime[ind].uchr;
  595         }
  596         last = word[i];
  597 
  598         /* Avoid double diacritics
  599          */
  600         if (i > 0)
  601             if (keyb_is_diacritic (word[i - 1]) && keyb_is_diacritic (word[i]))
  602             {
  603                 word[i] = vowels[rand () % vlen];
  604                 last = word[i];
  605             }
  606     }
  607     /*
  608      * Null terminated unistring
  609      */
  610     word[n] = L'\0';
  611 
  612     return TRUE;
  613 }
  614 
  615 /*******************************************************************************
  616  * Saves the accuracy accumulator
  617  */
  618 void
  619 accur_close ()
  620 {
  621     gint i;
  622     gchar *kb_name;
  623     gchar *tmp;
  624     gchar *utf8;
  625     FILE *fh;
  626 
  627     accur_sort ();
  628 
  629     kb_name = g_strdup (keyb_get_name ());
  630     for (i=0; kb_name[i]; i++)
  631         kb_name[i] = (kb_name[i] == ' ') ? '_' : kb_name[i];
  632 
  633     /*
  634      * First, the accuracy log
  635      */
  636     tmp = g_strconcat (main_path_stats (), G_DIR_SEPARATOR_S, ACCUR_LOG_FILE, "_", kb_name, NULL);
  637     fh = g_fopen (tmp, "wb");
  638     g_free (tmp);
  639     if (fh)
  640     {
  641         for (i = 0; i < terror_n; i++)
  642         {
  643             if (terror[i].wrong == 0 || terror[i].correct > ERROR_INERTIA)
  644                 continue;
  645 
  646             utf8 = accur_terror_char_utf8 (i);
  647             g_fprintf (fh, "%s\t%2lu\t%2lu\n", utf8, terror[i].wrong, terror[i].correct);
  648             g_free (utf8);
  649         }
  650         fclose (fh);
  651     }
  652     else
  653         g_message ("Could not save an accuracy log file at %s", main_path_stats ());
  654 
  655     /*
  656      * Second, the proficiency log
  657      */
  658     tmp = g_strconcat (main_path_stats (), G_DIR_SEPARATOR_S, PROFI_LOG_FILE, "_", kb_name, NULL);
  659     fh = g_fopen (tmp, "wb");
  660     g_free (tmp);
  661     if (fh)
  662     {
  663         for (i = 0; i < ttime_n; i++)
  664         {
  665             utf8 = accur_ttime_char_utf8 (i);
  666             g_fprintf (fh, "%s", utf8);
  667             //g_printf ("%s", utf8);
  668             g_free (utf8);
  669             g_fprintf (fh, "\t%0.6f\t%0.4f\n",
  670                     accur_profi_aver (i),
  671                     accur_profi_aver (i) / accur_profi_aver (ttime_n-1));
  672             //g_printf ("\t%0.6f\t%0.4f\n", accur_profi_aver (i), accur_profi_aver (i) / accur_profi_aver (ttime_n-1));
  673         }
  674         fclose (fh);
  675     }
  676     else
  677         g_message ("Could not save a proficiency log file at %s", main_path_stats ());
  678 
  679     g_free (kb_name);
  680 }