[Question]
  [
The [major system](http://en.wikipedia.org/wiki/Mnemonic_major_system) is a [mnemonic](http://en.wikipedia.org/wiki/Mnemonic) device for converting numbers into words so they can be memorized more easily.
It is based on how words sound phonetically, but to keep things simple for the challenge we'll only be concerned with how words are spelled. This means there will be some incorrect conversions, but that's alright.
> 
> To convert a number into a word using our simplified major system:
> 
> 
> * Replace each `0` with `s` or `z`. (Some could be `s` and some could be `z`. Same goes below.)
> * Replace each `1` with `t` or `d` or `th`.
> * Replace each `2` with `n`.
> * Replace each `3` with `m`.
> * Replace each `4` with `r`.
> * Replace each `5` with `l`.
> * Replace each `6` with `j` or `sh` or `ch`.
> * Replace each `7` with `k` or `c` or `g` or `q`.
> * Replace each `8` with `f` or `v`.
> * Replace each `9` with `p` or `b`.
> * Add the letters `aehiouwxy` anywhere in any amounts to make a real English word, *if possible*.  
> 
> **The only exception is that `h` may not be inserted after an `s` or `c`.**
> 
> 
> The number may actually be any string of the digits 0-9 (no decimals or commas or signs).  
> 
> The word can only contain the lowercase letters a-z.
> 
> 
> 
## Examples
The number `32` must be converted as `?m?n?`, where `?` represents any finite string made from the letters `aehiouwxy` (a string from the [free monoid](http://en.wikipedia.org/wiki/Free_monoid) if you prefer). There are many ways this could be made into a real English word: `mane`, `moon`, `yeoman`, etc.
The number `05` could be converted as `?s?l?` or `?z?l?`. Some possibilities are `easily`, `hassle`, and `hazel`. The word `shawl` is not allowed because `h` may not be placed after `s`; it would be incorrectly read as `65`.
# Challenge
Write a program or function that takes in a string of the digits 0-9 and finds all the words that it could be converted into using the simplified major system mnemonic.
Your program has access to a word list text file that defines what all the "real" English words are. There is one lowercase a-z word on each line of this file, and you may optionally assume it has a trailing newline. [Here is a list of real words you can use for testing.](http://www-01.sil.org/linguistics/wordlists/english/wordlist/wordsEn.txt) You can assume this word list file is called `f` (or something longer) and lies in any convenient directory.
For a 35 byte penalty (add 35 to your score) you may assume the word list is already loaded into a variable as a list of strings. This is mainly for languages that can't read files, but any submission may take advantage of it.
Your program must output **all** the words in the word list that the input number can be converted to. They should be printed to stdout (or similar), one per line (with an optional trailing newline), or they can be returned as a list of strings if you chose to write a function. The word list is not necessarily alphabetized and the output doesn't need to be either.
If there are no possible words then the output (or the list) will be empty. The output is also empty if the empty string is input.
Take input via stdin, command line, or as a string argument to a function. The word list, or its file name, should not be part of the input, only the digit string.
You are only matching single words in the word list, not sequences of words. The word `noon` would probably be one of the results for `22`, but the word sequence `no one` wouldn't.
## Test Cases
Suppose this is the word list:
```
stnmrljkfp
zthnmrlshqfb
asatanamaralajakafapa
aizxydwwwnhimouooraleshhhcavabe
zdnmrlshcvb
zdnmrlshchvb
sthnmrlchgvb
shthnmrlchgvb
bob
pop
bop
bopy
boppy
```
The input `0123456789` should give all the long words except `zdnmrlshchvb` and `shthnmrlchgvb`:
```
stnmrljkfp
zthnmrlshqfb
asatanamaralajakafapa
aizxydwwwnhimouooraleshhhcavabe
zdnmrlshcvb
sthnmrlchgvb
```
The input `99` should give:
```
bob
pop
bop
bopy
```
(The output words may be in any order.)
## Scoring
The shortest submission [in bytes](https://mothereff.in/byte-counter) wins. Tiebreaker goes to the submission posted first.
*Nifty related site: [numzi.com](http://numzi.com/numzi/).*
  
[Answer]
# Perl, ~~87~~ 84
```
open A,f;"@ARGV"eq s/[cs]h/j/gr=~y/stnmrljkfpzdcgqvb\0-z/0-90177789/dr&&print for<A>
```
Takes input in as the command line parameter:
```
$perl m.pl 23940
```
Can be made somewhat shorter if the word list would be allowed on the standard input:
```
$perl -lnE'INIT{$;=pop}$;eq s/[cs]h/j/gr=~y/stnmrljkfpzdcgqvba-z/0-90177789/dr&&say' 99 <f
```
[Answer]
# Python 2, ~~215~~ 208 bytes
This Python solution builds a regex from parts indexed by the command line argument, then tests each word with that (rather large) regex.
```
import re,sys
a='[sz] (d|th?) n m r l (j|sh|ch) [kcgq] [fv] [pb]'.split()
b=z='((?<![sc])h|[aeiouwxy])*'
for i in sys.argv[1]:b+=a[int(i)]+z
for d in open('f'):
 d=d.strip()
 if re.match('^'+b+'$',d):print d
```
Original source before the minifier:
```
import re,sys
regexbits = '[sz] (d|th?) n m r l (j|sh|ch) [kcgq] [fv] [pb]'.split()
regex = other = '((?<![sc])h|[aeiouwxy])*'
for i in sys.argv[1] :
    regex += regexbits[int(i)] + other
print regex     # DEBUG
for word in open('f'):
    word = word.strip()
    if re.match('^'+regex+'$', word) :
        print word
```
For example, the regex for the test `99` is:
```
^((?<![sc])h|[aeiouwxy])*[pb]((?<![sc])h|[aeiouwxy])*[pb]((?<![sc])h|[aeiouwxy])*$
```
The `(?<![sc])h` bit is a "look behind negative assertion" component that makes sure a `h` does not follow a `s` or `c` in the general filler parts.
Thanks Calvin. This challenge motivated me to brush up on my rusty regex skills.
[Answer]
# Python 3, 170
```
import sys,re
t=str.maketrans('sztdnmrljkcgqfvpb','00112345677778899','aehiouwxy\n')
for s in open('f'):re.sub('sh|ch','j',s).translate(t)!=sys.argv[1] or print(s,end='')
```
Readable version:
```
import sys, re
table = str.maketrans('sztdnmrljkcgqfvpb', '00112345677778899', 'aehiouwxy\n')
for line in open('f'):
    line = re.sub('sh|ch', 'j', line)
    if line.translate(table) == sys.argv[1]:
        print(line, end='')
```
The code makes use of the fact that `th` is redundant (since it maps to the same number as `t`, and `h` is a padding character).
The static [`maketrans`](https://docs.python.org/3/library/stdtypes.html#str.maketrans) function creates a table mapping the characters of the first argument to those of the second argument, and the characters of the third argument to `None` (which will result in those characters being deleted).
The final code could be made a few bytes shorter by creating the table as a direct argument of `translate`.
[Answer]
# sed,paste,grep,cut - 109
```
sed -e 's/[sc]h/6/g;s/[aehiouwxy]//g;y/sztdnmrljkcqgfvpb/00112345677778899/' w|paste w -|grep " $1$"|cut -f1
```
Takes a file "w", converts each word to its number, paste back to the original, grep for the number and return the word matched. Note that the whitespace after the quote after grep is a tab, paste's default delimiter.
I know Perl is way ahead, just wanted a better shell version as an example.
Oh yeah, the $1 part means that this is supposed to be run from a shell script, (most shells should work), so it takes a command-line argument.
[Answer]
# Bash + coreutils, 216
```
sed -n "$(sed 's/[aeiouwxy]//g
:l
s/\([^sc]\)h/\1/g
tl'<w|grep -nf <(eval printf '%s\\n' `sed 's/0/{s,z}/g
s/1/{t,th,d}/g
y/2345/nmrl/
s/6/{j,sh,ch}/g
s/7/{k,c,g,q}/g
s/8/{f,v}/g
s/9/{p,b}/g'<<<$1`)|sed s/:.\*/p/)" w
```
* The word list in a file called `w`
* The innermost `sed` replaces digits with their possible replacements
* The `eval printf` uses shell brace expansions to expand out all the possible substitutions
* The second `sed` on the 1st line removes `aeiouwxy` and `h` (when not preceded by `[sc]`) from the word list
* The grep prints out all matches, with line numbers
* Since we have stripped out `aeiouwxy` and `h` from the word list, the last `sed` turns the results of grep (line numbers of each match) to another `sed` expression, which is processed by the outermost `sed` to reveal all possible words from the wordlist.
### Output:
The word list file is specified as a command-line arg, followed by the number to mnemonicize:
```
ubuntu@ubuntu:~$ ./numzi.sh 99
bob
pop
bop
bopy
boppy
$ ./numzi.sh 0123456789
stnmrljkfp
zthnmrlshqfb
asatanamaralajakafapa
aizxydwwwnhimouooraleshhhcavabe
zdnmrlshcvb
sthnmrlchgvb
$ 
```
[Answer]
**tr, sed, grep, xargs, sh, 77**
```
tr 0123456789 ztnmrljkfp|sed 's/ */[aehiouwxy]*/g'|xargs sh -c 'grep -x $0 f'
```
Expects the number in stdin and the word list should be stored in the file `f`.
Does not uses all replacements (1 will always be z, 7 will always be k), so it may be called a lazy solution, but it finds at least one mnemonic for 95 numbers in [1-100].
]