/*****************************************************************************/
/* Klavaro - a flexible touch typing tutor */
/* Copyright (C) 2005, 2006, 2007, 2008 Felipe Castro */
/* Copyright (C) 2009, 2010, 2011 Free Software Foundation */
/* */
/* This program is free software, licensed under the terms of the GNU */
/* General Public License as published by the Free Software Foundation, */
/* either version 3 of the License, or (at your option) any later version. */
/* You should have received a copy of the GNU General Public License */
/* along with this program. If not, see . */
/*****************************************************************************/
#include
#include
#include
#include
#include
#include
#include "main.h"
#include "auxiliar.h"
#include "keyboard.h"
#include "adaptability.h"
#include "accuracy.h"
static struct
{
gunichar uchr;
gulong wrong;
gulong correct;
} terror[MAX_CHARS_EVALUATED];
static gint terror_n;
/* Simple reset
*/
void
accur_reset ()
{
terror_n = 0;
}
gint
accur_n_get (void)
{
return (terror_n);
}
gchar *
accur_char_utf8 (gint i)
{
gchar *utf8;
gint n;
if (i < 0 || i >= terror_n)
return (g_strdup (" "));
utf8 = g_malloc (UTF8_BUFFER);
n = g_unichar_to_utf8 (terror[i].uchr, utf8);
if (n < 1)
return (g_strdup (" "));
utf8[n] = '\0';
return (utf8);
}
gulong
accur_wrong_get (gint i)
{
if (i < 0 || i >= terror_n)
return -1;
return (terror[i].wrong);
}
/**********************************************************************
* Open the accuracy accumulator
*/
void
accur_init ()
{
const gchar delim[] = "\t\n\r";
gint i;
gchar *tmp;
gchar *data;
gboolean success;
gunichar uchr;
gulong wrong;
gulong correct;
memset (&terror, 0, sizeof (terror));
terror_n = 0;
tmp = g_build_filename (main_path_stats (), ACCUR_LOG_FILE, NULL);
success = g_file_get_contents (tmp, &data, NULL, NULL);
g_free (tmp);
if (!success)
return;
tmp = strtok (data, delim);
for (i = 0; i < MAX_CHARS_EVALUATED; i++)
{
if (tmp == NULL)
break;
uchr = g_utf8_get_char_validated (tmp, -1);
if (uchr == (gunichar)-1 || uchr == (gunichar)-2)
break;
tmp = strtok (NULL, delim);
if (tmp == NULL)
break;
wrong = strtoul (tmp, NULL, 10);
tmp = strtok (NULL, delim);
if (tmp == NULL)
break;
correct = strtoul (tmp, NULL, 10);
if (wrong > 0 && correct <= ERROR_INERTIA)
{
terror[i].uchr = uchr;
terror[i].wrong = wrong;
terror[i].correct = correct;
terror_n = i + 1;
}
else
break;
tmp = strtok (NULL, delim);
}
g_free (data);
}
/**********************************************************************
* Accumulates correctly typed characters
*/
void
accur_correct (gunichar uchr)
{
gint i;
if (uchr == L' ' || uchr == UPSYM || uchr == L'\t' || uchr == L'\b')
return;
for (i = 0; i < terror_n; i++)
{
if (uchr == terror[i].uchr)
{
if (terror[i].correct > ERROR_INERTIA)
{
terror[i].wrong -= (terror[i].wrong == 0 ? 0 : 1);
terror[i].correct = 1;
}
else
terror[i].correct++;
return;
}
}
}
/**********************************************************************
* Accumulates mistyped characters
*/
void
accur_wrong (gunichar uchr)
{
gint i;
gint i_min = -1;
gdouble correct_min = 1e9;
gdouble hlp;
if (uchr == L' ' || uchr == UPSYM || uchr == L'\t' || uchr == L'\b')
return;
for (i = 0; i < terror_n; i++)
{
if (uchr == terror[i].uchr)
{
terror[i].wrong++;
return;
}
hlp = ((gdouble) terror[i].wrong) / ((gdouble) terror[i].correct + terror[i].wrong + 1);
if (hlp <= correct_min)
{
correct_min = hlp;
i_min = i;
}
}
if (terror_n < MAX_CHARS_EVALUATED)
{
i = terror_n;
terror_n++;
}
else
{
i = (i_min > -1 ? i_min : terror_n - 1);
}
terror[i].uchr = uchr;
terror[i].wrong = 1;
terror[i].correct = 1;
}
gulong
accur_error_total ()
{
gint i;
gulong n = 0;
for (i = 0; i < terror_n; i++)
n += (terror[i].wrong < 12345 ? terror[i].wrong : 0);
return n;
}
/*******************************************************************************
* Increasing order, first: wrongness, second: correctness
*/
void
accur_sort ()
{
gint i, j;
gunichar uchr;
gulong correct;
gulong wrong;
for (i = 1; i < terror_n; i++)
{
for (j = i; j > 0; j--)
{
if (terror[j].correct < terror[j-1].correct)
{
uchr = terror[j].uchr;
terror[j].uchr = terror[j-1].uchr;
terror[j-1].uchr = uchr;
wrong = terror[j].wrong;
terror[j].wrong = terror[j-1].wrong;
terror[j-1].wrong = wrong;
correct = terror[j].correct;
terror[j].correct = terror[j-1].correct;
terror[j-1].correct = correct;
}
}
}
for (i = 1; i < terror_n; i++)
{
for (j = i; j > 0; j--)
{
if (terror[j].wrong > terror[j-1].wrong)
{
uchr = terror[j].uchr;
terror[j].uchr = terror[j-1].uchr;
terror[j-1].uchr = uchr;
wrong = terror[j].wrong;
terror[j].wrong = terror[j-1].wrong;
terror[j-1].wrong = wrong;
correct = terror[j].correct;
terror[j].correct = terror[j-1].correct;
terror[j-1].correct = correct;
}
}
}
}
/*******************************************************************************
* Creates a random weird word based on error profile
*/
gboolean
accur_create_word (gunichar word[MAX_WORD_LEN + 1])
{
gint i, j;
gint ind;
gint n;
gunichar vowels[20];
gunichar last = 0;
gint vlen;
if (terror_n < 10)
return FALSE;
vlen = keyb_get_vowels (vowels);
n = rand () % (MAX_WORD_LEN) + 1;
for (i = 0; i < n; i++)
{
for (j = 0; j < 100; j++)
{
ind = rand () % terror_n;
if (terror[ind].uchr == last)
continue;
if (rand () % terror[0].wrong < terror[ind].wrong)
break;
}
word[i] = terror[ind].uchr;
last = word[i];
/* Avoid double diacritics
*/
if (i > 0)
if (keyb_is_diacritic (word[i - 1]) && keyb_is_diacritic (word[i]))
{
word[i] = vowels[rand () % vlen];
last = word[i];
}
}
/*
* Null terminated unistring
*/
word[n] = L'\0';
return TRUE;
}
/*******************************************************************************
* Saves the accuracy accumulator
*/
void
accur_close ()
{
gint i;
gchar *tmp;
gchar *utf8;
FILE *fh;
tmp = g_build_filename (main_path_stats (), ACCUR_LOG_FILE, NULL);
fh = g_fopen (tmp, "wb");
g_free (tmp);
if (!fh)
{
g_message ("Could not save the accuracy log file at %s", main_path_stats ());
return;
}
accur_sort ();
for (i = 0; i < terror_n; i++)
{
if (terror[i].wrong == 0 || terror[i].correct > ERROR_INERTIA)
continue;
utf8 = accur_char_utf8 (i);
g_fprintf (fh, "%s\t%lu\t%lu\n", utf8, terror[i].wrong, terror[i].correct);
g_free (utf8);
}
fclose (fh);
}