Insert typos into text I wrote some text, but it looks too professional. I want to make it look like I was really tired when I wrote it. I need you to insert some typos.

Your challenge is to take an arbitrary single line of text, and add typos. This means that for each character, there will be a 10% chance for it to be typofied.

The definition of "typofied" is that you must choose (randomly) one of the following:


Duplicate the character.
Delete the character.
Shift the character one keyboard space. The "keyboard" is defined as:

qwertyuiop
asdfghjkl
 zxcvbnm


For the character shift, you must go one space up, down, left, or right. This must be chosen randomly. The shift option only applies to alphabetic characters. Case must be preserved. Be careful with edge-cases, like m!


The definition of "random" is that the result must not be predictable (by looking at previous results). For example, you can't typofy every tenth character. Furthermore, the randomness must have an even distribution. For example, you can't do 30% duplicate, 30% delete, and 40% shift; it has to be a 1/3 chance for each (1/2 for each if it's a nonalphabetic character).

Example input:

This is some correct text. It is too correct. Please un-correctify it.


Example output:

This iissome xorreect tex..  It is too coteect. Please jn-corretify it.


This is code-golf, so the shortest code in bytes will win.
C, 358 bytes
(There are only three lines of code, but I've broken up line 3 for legibility)
#define x putchar
#define y random()
c,n;main(){char*s[26]={&quot;QS&quot;,&quot;HNV&quot;,&quot;FVX&quot;,&quot;EFSX&quot;,&quot;DRW&quot;,&quot;CDGR&quot;,&quot;FHTV&quot;,&quot;BGJY&quot;,&quot;KOU&quot;,&quot;HKNU&quot;,
&quot;IJLM&quot;,&quot;KO&quot;,&quot;KN&quot;,&quot;BJM&quot;,&quot;ILP&quot;,&quot;OO&quot;,&quot;AW&quot;,&quot;EFT&quot;,&quot;ADWZ&quot;,&quot;GRY&quot;,&quot;IJY&quot;,&quot;BCG&quot;,&quot;ESQ&quot;,&quot;CDZ&quot;,&quot;HTU&quot;,
&quot;SX&quot;};while((c=getchar())&gt;0){if(y%10&gt;0&amp;&amp;x(c))continue;if(isalpha(c)&amp;&amp;y%3&lt;1){n=(c&amp;31)-1;
x(s[n][y%strlen(s[n])]|(c&amp;32));continue;}if (y&amp;1)x(x(c));}}

The array of strings at the beginning lists the possible adjacent keys for each letter of the alphabet. I had to double up the &quot;O&quot; (adjacent to &quot;P&quot;) to avoid calculating random()%1 when selecting a shifted character.
Test run:
$ echo &quot;This is some correct text. It is too correct. Please un-correctify it.&quot; |./a.out
This is some  cofrect teext. It is too correct.. Plleaase un-correctify it..


Update:
Here's an expanded and commented version of the same source code:
#include &lt;stdio.h&gt;
#include &lt;string.h&gt;
/* ^^ These should be included, but the code compiles without them */

int c,n;

void main() {

  /* Adjacent keys for each letter of the alphabet (A, B, C, ..., Z): */
  char *s[26] = { &quot;QS&quot;,&quot;HNV&quot;,&quot;FVX&quot;,&quot;EFSX&quot;,&quot;DRW&quot;,&quot;CDGR&quot;,&quot;FHTV&quot;,&quot;BGJY&quot;,&quot;KOU&quot;,&quot;HKNU&quot;,
                  &quot;IJLM&quot;,&quot;KO&quot;,&quot;KN&quot;,&quot;BJM&quot;,&quot;ILP&quot;,&quot;OO&quot;,&quot;AW&quot;,&quot;EFT&quot;,&quot;ADWZ&quot;,&quot;GRY&quot;,&quot;IJY&quot;,
                  &quot;BCG&quot;,&quot;ESQ&quot;,&quot;CDZ&quot;,&quot;HTU&quot;,&quot;SX&quot; };
  
  /* Fetch input until null character or EOF reached */
  while ((c=getchar())&gt;0) {
  
    /* For 90% of the time, just echo the character unchanged */
    if (random()%10&gt;0 &amp;&amp; putchar(c)) continue;
    
    /* If it's a letter of the alphabet, shift with 33% probability */
    if (isalpha(c) &amp;&amp; random()%3&lt;1) {
      /* Calculate offset to adjacent keys data */
      n=(c&amp;31)-1;
      /* Choose a random adjacent key, and change to lower case if needed */
      putchar(s[n][random()%strlen(s[n])]|(c&amp;32));
      continue;
    }
    
    /* If we reach this point, either we didn't fetch an alphabet character, or   */
    /* we did but chose not to shift it. Either way, we now need to either repeat */
    /* the character or delete it, with 50% probability for each. */
    
    /* This repeats the character by printing the return value from putchar() */
    if (random()&amp;1) putchar(putchar(c));
    
    /* To delete the character, we don't have to do anything. */
  }
}