/*
 * Passogva 1.0 - A random password generator based on FIPS-181
 *
 * Copyright (c) 2004 Steven Hazel
 *
 * Permission to use, copy, modify, distribute, and sell this software
 * and its documentation for any purpose is hereby granted without
 * fee, provided that the above copyright notice appear in all copies
 * and that both that copyright notice and this permission notice
 * appear in supporting documentation.  No representations are made
 * about the suitability of this software for any purpose.  It is
 * provided "as is" without express or implied warranty.
 *
 * See Also:
 * FIPS 181 - (APG), Automated Password Generator:
 * http://www.itl.nist.gov/fipspubs/fip181.htm
 *
 * This is a C language implementation of the Automated Password
 * Generator standard, like the program described in "A Random Word
 * Generator For Pronounceable Passwords".  This code is based on the
 * program contained in Appendix A of FIPS Publication 181, "Standard
 * for Automated Password Generator".  In accordance with the
 * standard, the results obtained from this program are logically
 * equivalent to those produced by the standard.
 *
 * Deviations From Standard:
 *
 * This implementation deviates in one critical way from the standard
 * upon which it is based: the random number generator in this
 * implementation does not use DES.  Instead, it uses the rand(3)
 * system call.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <time.h>

#define PASSOGVA_DEBUG
#define B1

#define TRUE  1
#define FALSE 0

#define NUM_RULES 34

#define ALLOWED(flag) (digram[units_in_syllable[current_unit - 1]][unit]\
                       & (flag))

#define MAX_UNACCEPTABLE      20
#define MAX_RETRIES           (4 * (int) pwlen + NUM_RULES)

#define NOT_BEGIN_SYLLABLE    010
#define NO_FINAL_SPLIT        004
#define VOWEL                 002
#define ALTERNATE_VOWEL       001
#define NO_SPECIAL_RULE       000

#define BEGIN                 0200
#define NOT_BEGIN             0100
#define BREAK                 0040
#define PREFIX                0020
#define ILLEGAL_PAIR          0010
#define SUFFIX                0004
#define END                   0002
#define NOT_END               0001
#define ANY_COMBINATION       0000

typedef int boolean;


static int get_word (char *word,
                     char *hyphenated_word,
                     unsigned short pwlen);
static boolean have_initial_y (unsigned short *units,
                               unsigned short unit_size);
static boolean illegal_placement (unsigned short *units,
                                  unsigned short pwlen);
static boolean improper_word (unsigned short *units,
                              unsigned short word_size);
static boolean have_final_split (unsigned short *units,
                                 unsigned short unit_size);
static char *get_syllable (char *syllable,
                           unsigned short pwlen,
                           unsigned short *units_in_syllable,
                           unsigned short *syllable_length);
static unsigned short random_unit (unsigned short type);
static unsigned int randint(int n);
static unsigned short get_random (unsigned short minlen,
                                  unsigned short maxlen);
static void set_seed(long seed);
int randomchars (char *string,
                 unsigned short minlen,
                 unsigned short maxlen,
                 boolean restrict,
                 long seed);
int randomletters (char *string,
                   unsigned short minlen,
                   unsigned short maxlen,
                   boolean restrict,
                   long seed);
int randomword (char *word,
                char *hyphenated_word,
                unsigned short minlen,
                unsigned short maxlen,
                boolean restrict,
                long seed);


struct unit {
  char unit_code[5];
  unsigned short flags;
};

static struct unit rules[NUM_RULES] = {
  {"a", VOWEL},
  {"b", NO_SPECIAL_RULE},
  {"c", NO_SPECIAL_RULE},
  {"d", NO_SPECIAL_RULE},
  {"e", NO_FINAL_SPLIT | VOWEL},
  {"f", NO_SPECIAL_RULE},
  {"g", NO_SPECIAL_RULE},
  {"h", NO_SPECIAL_RULE},
  {"i", VOWEL},
  {"j", NO_SPECIAL_RULE},
  {"k", NO_SPECIAL_RULE},
  {"l", NO_SPECIAL_RULE},
  {"m", NO_SPECIAL_RULE},
  {"n", NO_SPECIAL_RULE},
  {"o", VOWEL},
  {"p", NO_SPECIAL_RULE},
  {"r", NO_SPECIAL_RULE},
  {"s", NO_SPECIAL_RULE},
  {"t", NO_SPECIAL_RULE},
  {"u", VOWEL},
  {"v", NO_SPECIAL_RULE},
  {"w", NO_SPECIAL_RULE},
  {"x", NOT_BEGIN_SYLLABLE},
  {"y", ALTERNATE_VOWEL | VOWEL},
  {"z", NO_SPECIAL_RULE},
  {"ch", NO_SPECIAL_RULE},
  {"gh", NO_SPECIAL_RULE},
  {"ph", NO_SPECIAL_RULE},
  {"rh", NO_SPECIAL_RULE},
  {"sh", NO_SPECIAL_RULE},
  {"th", NO_SPECIAL_RULE},
  {"wh", NO_SPECIAL_RULE},
  {"qu", NO_SPECIAL_RULE},
  {"ck", NOT_BEGIN_SYLLABLE}
};

static int digram[NUM_RULES][NUM_RULES] = {
  {
    /* aa */ ILLEGAL_PAIR,
    /* ab */ ANY_COMBINATION,
    /* ac */ ANY_COMBINATION,
    /* ad */ ANY_COMBINATION,
    /* ae */ ILLEGAL_PAIR,
    /* af */ ANY_COMBINATION,
    /* ag */ ANY_COMBINATION,
    /* ah */ NOT_BEGIN | BREAK | NOT_END,
    /* ai */ ANY_COMBINATION,
    /* aj */ ANY_COMBINATION,
    /* ak */ ANY_COMBINATION,
    /* al */ ANY_COMBINATION,
    /* am */ ANY_COMBINATION,
    /* an */ ANY_COMBINATION,
    /* ao */ ILLEGAL_PAIR,
    /* ap */ ANY_COMBINATION,
    /* ar */ ANY_COMBINATION,
    /* as */ ANY_COMBINATION,
    /* at */ ANY_COMBINATION,
    /* au */ ANY_COMBINATION,
    /* av */ ANY_COMBINATION,
    /* aw */ ANY_COMBINATION,
    /* ax */ ANY_COMBINATION,
    /* ay */ ANY_COMBINATION,
    /* az */ ANY_COMBINATION,
    /* ach */ ANY_COMBINATION,
    /* agh */ ILLEGAL_PAIR,
    /* aph */ ANY_COMBINATION,
    /* arh */ ILLEGAL_PAIR,
    /* ash */ ANY_COMBINATION,
    /* ath */ ANY_COMBINATION,
    /* awh */ ILLEGAL_PAIR,
    /* aqu */ BREAK | NOT_END,
    /* ack */ ANY_COMBINATION
  },
  {
    /* ba */ ANY_COMBINATION,
    /* bb */ NOT_BEGIN | BREAK | NOT_END,
    /* bc */ NOT_BEGIN | BREAK | NOT_END,
    /* bd */ NOT_BEGIN | BREAK | NOT_END,
    /* be */ ANY_COMBINATION,
    /* bf */ NOT_BEGIN | BREAK | NOT_END,
    /* bg */ NOT_BEGIN | BREAK | NOT_END,
    /* bh */ NOT_BEGIN | BREAK | NOT_END,
    /* bi */ ANY_COMBINATION,
    /* bj */ NOT_BEGIN | BREAK | NOT_END,
    /* bk */ NOT_BEGIN | BREAK | NOT_END,
    /* bl */ BEGIN | SUFFIX | NOT_END,
    /* bm */ NOT_BEGIN | BREAK | NOT_END,
    /* bn */ NOT_BEGIN | BREAK | NOT_END,
    /* bo */ ANY_COMBINATION,
    /* bp */ NOT_BEGIN | BREAK | NOT_END,
    /* br */ BEGIN | END,
    /* bs */ NOT_BEGIN,
    /* bt */ NOT_BEGIN | BREAK | NOT_END,
    /* bu */ ANY_COMBINATION,
    /* bv */ NOT_BEGIN | BREAK | NOT_END,
    /* bw */ NOT_BEGIN | BREAK | NOT_END,
    /* bx */ ILLEGAL_PAIR,
    /* by */ ANY_COMBINATION,
    /* bz */ NOT_BEGIN | BREAK | NOT_END,
    /* bch */ NOT_BEGIN | BREAK | NOT_END,
    /* bgh */ ILLEGAL_PAIR,
    /* bph */ NOT_BEGIN | BREAK | NOT_END,
    /* brh */ ILLEGAL_PAIR,
    /* bsh */ NOT_BEGIN | BREAK | NOT_END,
    /* bth */ NOT_BEGIN | BREAK | NOT_END,
    /* bwh */ ILLEGAL_PAIR,
    /* bqu */ NOT_BEGIN | BREAK | NOT_END,
    /* bck */ ILLEGAL_PAIR
  },
  {
    /* ca */ ANY_COMBINATION,
    /* cb */ NOT_BEGIN | BREAK | NOT_END,
    /* cc */ NOT_BEGIN | BREAK | NOT_END,
    /* cd */ NOT_BEGIN | BREAK | NOT_END,
    /* ce */ ANY_COMBINATION,
    /* cf */ NOT_BEGIN | BREAK | NOT_END,
    /* cg */ NOT_BEGIN | BREAK | NOT_END,
    /* ch */ NOT_BEGIN | BREAK | NOT_END,
    /* ci */ ANY_COMBINATION,
    /* cj */ NOT_BEGIN | BREAK | NOT_END,
    /* ck */ NOT_BEGIN | BREAK | NOT_END,
    /* cl */ SUFFIX | NOT_END,
    /* cm */ NOT_BEGIN | BREAK | NOT_END,
    /* cn */ NOT_BEGIN | BREAK | NOT_END,
    /* co */ ANY_COMBINATION,
    /* cp */ NOT_BEGIN | BREAK | NOT_END,
    /* cr */ NOT_END,
    /* cs */ NOT_BEGIN | END,
    /* ct */ NOT_BEGIN | PREFIX,
    /* cu */ ANY_COMBINATION,
    /* cv */ NOT_BEGIN | BREAK | NOT_END,
    /* cw */ NOT_BEGIN | BREAK | NOT_END,
    /* cx */ ILLEGAL_PAIR,
    /* cy */ ANY_COMBINATION,
    /* cz */ NOT_BEGIN | BREAK | NOT_END,
    /* cch */ ILLEGAL_PAIR,
    /* cgh */ ILLEGAL_PAIR,
    /* cph */ NOT_BEGIN | BREAK | NOT_END,
    /* crh */ ILLEGAL_PAIR,
    /* csh */ NOT_BEGIN | BREAK | NOT_END,
    /* cth */ NOT_BEGIN | BREAK | NOT_END,
    /* cwh */ ILLEGAL_PAIR,
    /* cqu */ NOT_BEGIN | SUFFIX | NOT_END,
    /* cck */ ILLEGAL_PAIR
  },
  {
    /* da */ ANY_COMBINATION,
    /* db */ NOT_BEGIN | BREAK | NOT_END,
    /* dc */ NOT_BEGIN | BREAK | NOT_END,
    /* dd */ NOT_BEGIN,
    /* de */ ANY_COMBINATION,
    /* df */ NOT_BEGIN | BREAK | NOT_END,
    /* dg */ NOT_BEGIN | BREAK | NOT_END,
    /* dh */ NOT_BEGIN | BREAK | NOT_END,
    /* di */ ANY_COMBINATION,
    /* dj */ NOT_BEGIN | BREAK | NOT_END,
    /* dk */ NOT_BEGIN | BREAK | NOT_END,
    /* dl */ NOT_BEGIN | BREAK | NOT_END,
    /* dm */ NOT_BEGIN | BREAK | NOT_END,
    /* dn */ NOT_BEGIN | BREAK | NOT_END,
    /* do */ ANY_COMBINATION,
    /* dp */ NOT_BEGIN | BREAK | NOT_END,
    /* dr */ BEGIN | NOT_END,
    /* ds */ NOT_BEGIN | END,
    /* dt */ NOT_BEGIN | BREAK | NOT_END,
    /* du */ ANY_COMBINATION,
    /* dv */ NOT_BEGIN | BREAK | NOT_END,
    /* dw */ NOT_BEGIN | BREAK | NOT_END,
    /* dx */ ILLEGAL_PAIR,
    /* dy */ ANY_COMBINATION,
    /* dz */ NOT_BEGIN | BREAK | NOT_END,
    /* dch */ NOT_BEGIN | BREAK | NOT_END,
    /* dgh */ NOT_BEGIN | BREAK | NOT_END,
    /* dph */ NOT_BEGIN | BREAK | NOT_END,
    /* drh */ ILLEGAL_PAIR,
    /* dsh */ NOT_BEGIN | NOT_END,
    /* dth */ NOT_BEGIN | PREFIX,
    /* dwh */ ILLEGAL_PAIR,
    /* dqu */ NOT_BEGIN | BREAK | NOT_END,
    /* dck */ ILLEGAL_PAIR
  },
  {
    /* ea */ ANY_COMBINATION,
    /* eb */ ANY_COMBINATION,
    /* ec */ ANY_COMBINATION,
    /* ed */ ANY_COMBINATION,
    /* ee */ ANY_COMBINATION,
    /* ef */ ANY_COMBINATION,
    /* eg */ ANY_COMBINATION,
    /* eh */ NOT_BEGIN | BREAK | NOT_END,
    /* ei */ NOT_END,
    /* ej */ ANY_COMBINATION,
    /* ek */ ANY_COMBINATION,
    /* el */ ANY_COMBINATION,
    /* em */ ANY_COMBINATION,
    /* en */ ANY_COMBINATION,
    /* eo */ BREAK,
    /* ep */ ANY_COMBINATION,
    /* er */ ANY_COMBINATION,
    /* es */ ANY_COMBINATION,
    /* et */ ANY_COMBINATION,
    /* eu */ ANY_COMBINATION,
    /* ev */ ANY_COMBINATION,
    /* ew */ ANY_COMBINATION,
    /* ex */ ANY_COMBINATION,
    /* ey */ ANY_COMBINATION,
    /* ez */ ANY_COMBINATION,
    /* ech */ ANY_COMBINATION,
    /* egh */ NOT_BEGIN | BREAK | NOT_END,
    /* eph */ ANY_COMBINATION,
    /* erh */ ILLEGAL_PAIR,
    /* esh */ ANY_COMBINATION,
    /* eth */ ANY_COMBINATION,
    /* ewh */ ILLEGAL_PAIR,
    /* equ */ BREAK | NOT_END,
    /* eck */ ANY_COMBINATION
  },
  {
    /* fa */ ANY_COMBINATION,
    /* fb */ NOT_BEGIN | BREAK | NOT_END,
    /* fc */ NOT_BEGIN | BREAK | NOT_END,
    /* fd */ NOT_BEGIN | BREAK | NOT_END,
    /* fe */ ANY_COMBINATION,
    /* ff */ NOT_BEGIN,
    /* fg */ NOT_BEGIN | BREAK | NOT_END,
    /* fh */ NOT_BEGIN | BREAK | NOT_END,
    /* fi */ ANY_COMBINATION,
    /* fj */ NOT_BEGIN | BREAK | NOT_END,
    /* fk */ NOT_BEGIN | BREAK | NOT_END,
    /* fl */ BEGIN | SUFFIX | NOT_END,
    /* fm */ NOT_BEGIN | BREAK | NOT_END,
    /* fn */ NOT_BEGIN | BREAK | NOT_END,
    /* fo */ ANY_COMBINATION,
    /* fp */ NOT_BEGIN | BREAK | NOT_END,
    /* fr */ BEGIN | NOT_END,
    /* fs */ NOT_BEGIN,
    /* ft */ NOT_BEGIN,
    /* fu */ ANY_COMBINATION,
    /* fv */ NOT_BEGIN | BREAK | NOT_END,
    /* fw */ NOT_BEGIN | BREAK | NOT_END,
    /* fx */ ILLEGAL_PAIR,
    /* fy */ NOT_BEGIN,
    /* fz */ NOT_BEGIN | BREAK | NOT_END,
    /* fch */ NOT_BEGIN | BREAK | NOT_END,
    /* fgh */ NOT_BEGIN | BREAK | NOT_END,
    /* fph */ NOT_BEGIN | BREAK | NOT_END,
    /* frh */ ILLEGAL_PAIR,
    /* fsh */ NOT_BEGIN | BREAK | NOT_END,
    /* fth */ NOT_BEGIN | BREAK | NOT_END,
    /* fwh */ ILLEGAL_PAIR,
    /* fqu */ NOT_BEGIN | BREAK | NOT_END,
    /* fck */ ILLEGAL_PAIR
  },
  {
    /* ga */ ANY_COMBINATION,
    /* gb */ NOT_BEGIN | BREAK | NOT_END,
    /* gc */ NOT_BEGIN | BREAK | NOT_END,
    /* gd */ NOT_BEGIN | BREAK | NOT_END,
    /* ge */ ANY_COMBINATION,
    /* gf */ NOT_BEGIN | BREAK | NOT_END,
    /* gg */ NOT_BEGIN,
    /* gh */ NOT_BEGIN | BREAK | NOT_END,
    /* gi */ ANY_COMBINATION,
    /* gj */ NOT_BEGIN | BREAK | NOT_END,
    /* gk */ ILLEGAL_PAIR,
    /* gl */ BEGIN | SUFFIX | NOT_END,
    /* gm */ NOT_BEGIN | BREAK | NOT_END,
    /* gn */ NOT_BEGIN | BREAK | NOT_END,
    /* go */ ANY_COMBINATION,
    /* gp */ NOT_BEGIN | BREAK | NOT_END,
    /* gr */ BEGIN | NOT_END,
    /* gs */ NOT_BEGIN | END,
    /* gt */ NOT_BEGIN | BREAK | NOT_END,
    /* gu */ ANY_COMBINATION,
    /* gv */ NOT_BEGIN | BREAK | NOT_END,
    /* gw */ NOT_BEGIN | BREAK | NOT_END,
    /* gx */ ILLEGAL_PAIR,
    /* gy */ NOT_BEGIN,
    /* gz */ NOT_BEGIN | BREAK | NOT_END,
    /* gch */ NOT_BEGIN | BREAK | NOT_END,
    /* ggh */ ILLEGAL_PAIR,
    /* gph */ NOT_BEGIN | BREAK | NOT_END,
    /* grh */ ILLEGAL_PAIR,
    /* gsh */ NOT_BEGIN,
    /* gth */ NOT_BEGIN,
    /* gwh */ ILLEGAL_PAIR,
    /* gqu */ NOT_BEGIN | BREAK | NOT_END,
    /* gck */ ILLEGAL_PAIR
  },
  {
    /* ha */ ANY_COMBINATION,
    /* hb */ NOT_BEGIN | BREAK | NOT_END,
    /* hc */ NOT_BEGIN | BREAK | NOT_END,
    /* hd */ NOT_BEGIN | BREAK | NOT_END,
    /* he */ ANY_COMBINATION,
    /* hf */ NOT_BEGIN | BREAK | NOT_END,
    /* hg */ NOT_BEGIN | BREAK | NOT_END,
    /* hh */ ILLEGAL_PAIR,
    /* hi */ ANY_COMBINATION,
    /* hj */ NOT_BEGIN | BREAK | NOT_END,
    /* hk */ NOT_BEGIN | BREAK | NOT_END,
    /* hl */ NOT_BEGIN | BREAK | NOT_END,
    /* hm */ NOT_BEGIN | BREAK | NOT_END,
    /* hn */ NOT_BEGIN | BREAK | NOT_END,
    /* ho */ ANY_COMBINATION,
    /* hp */ NOT_BEGIN | BREAK | NOT_END,
    /* hr */ NOT_BEGIN | BREAK | NOT_END,
    /* hs */ NOT_BEGIN | BREAK | NOT_END,
    /* ht */ NOT_BEGIN | BREAK | NOT_END,
    /* hu */ ANY_COMBINATION,
    /* hv */ NOT_BEGIN | BREAK | NOT_END,
    /* hw */ NOT_BEGIN | BREAK | NOT_END,
    /* hx */ ILLEGAL_PAIR,
    /* hy */ ANY_COMBINATION,
    /* hz */ NOT_BEGIN | BREAK | NOT_END,
    /* hch */ NOT_BEGIN | BREAK | NOT_END,
    /* hgh */ NOT_BEGIN | BREAK | NOT_END,
    /* hph */ NOT_BEGIN | BREAK | NOT_END,
    /* hrh */ ILLEGAL_PAIR,
    /* hsh */ NOT_BEGIN | BREAK | NOT_END,
    /* hth */ NOT_BEGIN | BREAK | NOT_END,
    /* hwh */ ILLEGAL_PAIR,
    /* hqu */ NOT_BEGIN | BREAK | NOT_END,
    /* hck */ ILLEGAL_PAIR
  },
  {
    /* ia */ ANY_COMBINATION,
    /* ib */ ANY_COMBINATION,
    /* ic */ ANY_COMBINATION,
    /* id */ ANY_COMBINATION,
    /* ie */ NOT_BEGIN,
    /* if */ ANY_COMBINATION,
    /* ig */ ANY_COMBINATION,
    /* ih */ NOT_BEGIN | BREAK | NOT_END,
    /* ii */ ILLEGAL_PAIR,
    /* ij */ ANY_COMBINATION,
    /* ik */ ANY_COMBINATION,
    /* il */ ANY_COMBINATION,
    /* im */ ANY_COMBINATION,
    /* in */ ANY_COMBINATION,
    /* io */ BREAK,
    /* ip */ ANY_COMBINATION,
    /* ir */ ANY_COMBINATION,
    /* is */ ANY_COMBINATION,
    /* it */ ANY_COMBINATION,
    /* iu */ NOT_BEGIN | BREAK | NOT_END,
    /* iv */ ANY_COMBINATION,
    /* iw */ NOT_BEGIN | BREAK | NOT_END,
    /* ix */ ANY_COMBINATION,
    /* iy */ NOT_BEGIN | BREAK | NOT_END,
    /* iz */ ANY_COMBINATION,
    /* ich */ ANY_COMBINATION,
    /* igh */ NOT_BEGIN,
    /* iph */ ANY_COMBINATION,
    /* irh */ ILLEGAL_PAIR,
    /* ish */ ANY_COMBINATION,
    /* ith */ ANY_COMBINATION,
    /* iwh */ ILLEGAL_PAIR,
    /* iqu */ BREAK | NOT_END,
    /* ick */ ANY_COMBINATION
  },
  {
    /* ja */ ANY_COMBINATION,
    /* jb */ NOT_BEGIN | BREAK | NOT_END,
    /* jc */ NOT_BEGIN | BREAK | NOT_END,
    /* jd */ NOT_BEGIN | BREAK | NOT_END,
    /* je */ ANY_COMBINATION,
    /* jf */ NOT_BEGIN | BREAK | NOT_END,
    /* jg */ ILLEGAL_PAIR,
    /* jh */ NOT_BEGIN | BREAK | NOT_END,
    /* ji */ ANY_COMBINATION,
    /* jj */ ILLEGAL_PAIR,
    /* jk */ NOT_BEGIN | BREAK | NOT_END,
    /* jl */ NOT_BEGIN | BREAK | NOT_END,
    /* jm */ NOT_BEGIN | BREAK | NOT_END,
    /* jn */ NOT_BEGIN | BREAK | NOT_END,
    /* jo */ ANY_COMBINATION,
    /* jp */ NOT_BEGIN | BREAK | NOT_END,
    /* jr */ NOT_BEGIN | BREAK | NOT_END,
    /* js */ NOT_BEGIN | BREAK | NOT_END,
    /* jt */ NOT_BEGIN | BREAK | NOT_END,
    /* ju */ ANY_COMBINATION,
    /* jv */ NOT_BEGIN | BREAK | NOT_END,
    /* jw */ NOT_BEGIN | BREAK | NOT_END,
    /* jx */ ILLEGAL_PAIR,
    /* jy */ NOT_BEGIN,
    /* jz */ NOT_BEGIN | BREAK | NOT_END,
    /* jch */ NOT_BEGIN | BREAK | NOT_END,
    /* jgh */ NOT_BEGIN | BREAK | NOT_END,
    /* jph */ NOT_BEGIN | BREAK | NOT_END,
    /* jrh */ ILLEGAL_PAIR,
    /* jsh */ NOT_BEGIN | BREAK | NOT_END,
    /* jth */ NOT_BEGIN | BREAK | NOT_END,
    /* jwh */ ILLEGAL_PAIR,
    /* jqu */ NOT_BEGIN | BREAK | NOT_END,
    /* jck */ ILLEGAL_PAIR
  },
  {
    /* ka */ ANY_COMBINATION,
    /* kb */ NOT_BEGIN | BREAK | NOT_END,
    /* kc */ NOT_BEGIN | BREAK | NOT_END,
    /* kd */ NOT_BEGIN | BREAK | NOT_END,
    /* ke */ ANY_COMBINATION,
    /* kf */ NOT_BEGIN | BREAK | NOT_END,
    /* kg */ NOT_BEGIN | BREAK | NOT_END,
    /* kh */ NOT_BEGIN | BREAK | NOT_END,
    /* ki */ ANY_COMBINATION,
    /* kj */ NOT_BEGIN | BREAK | NOT_END,
    /* kk */ NOT_BEGIN | BREAK | NOT_END,
    /* kl */ SUFFIX | NOT_END,
    /* km */ NOT_BEGIN | BREAK | NOT_END,
    /* kn */ BEGIN | SUFFIX | NOT_END,
    /* ko */ ANY_COMBINATION,
    /* kp */ NOT_BEGIN | BREAK | NOT_END,
    /* kr */ SUFFIX | NOT_END,
    /* ks */ NOT_BEGIN | END,
    /* kt */ NOT_BEGIN | BREAK | NOT_END,
    /* ku */ ANY_COMBINATION,
    /* kv */ NOT_BEGIN | BREAK | NOT_END,
    /* kw */ NOT_BEGIN | BREAK | NOT_END,
    /* kx */ ILLEGAL_PAIR,
    /* ky */ NOT_BEGIN,
    /* kz */ NOT_BEGIN | BREAK | NOT_END,
    /* kch */ NOT_BEGIN | BREAK | NOT_END,
    /* kgh */ NOT_BEGIN | BREAK | NOT_END,
    /* kph */ NOT_BEGIN | PREFIX,
    /* krh */ ILLEGAL_PAIR,
    /* ksh */ NOT_BEGIN,
    /* kth */ NOT_BEGIN | BREAK | NOT_END,
    /* kwh */ ILLEGAL_PAIR,
    /* kqu */ NOT_BEGIN | BREAK | NOT_END,
    /* kck */ ILLEGAL_PAIR
  },
  {
    /* la */ ANY_COMBINATION,
    /* lb */ NOT_BEGIN | PREFIX,
    /* lc */ NOT_BEGIN | BREAK | NOT_END,
    /* ld */ NOT_BEGIN | PREFIX,
    /* le */ ANY_COMBINATION,
    /* lf */ NOT_BEGIN | PREFIX,
    /* lg */ NOT_BEGIN | PREFIX,
    /* lh */ NOT_BEGIN | BREAK | NOT_END,
    /* li */ ANY_COMBINATION,
    /* lj */ NOT_BEGIN | PREFIX,
    /* lk */ NOT_BEGIN | PREFIX,
    /* ll */ NOT_BEGIN | PREFIX,
    /* lm */ NOT_BEGIN | PREFIX,
    /* ln */ NOT_BEGIN | BREAK | NOT_END,
    /* lo */ ANY_COMBINATION,
    /* lp */ NOT_BEGIN | PREFIX,
    /* lr */ NOT_BEGIN | BREAK | NOT_END,
    /* ls */ NOT_BEGIN,
    /* lt */ NOT_BEGIN | PREFIX,
    /* lu */ ANY_COMBINATION,
    /* lv */ NOT_BEGIN | PREFIX,
    /* lw */ NOT_BEGIN | BREAK | NOT_END,
    /* lx */ ILLEGAL_PAIR,
    /* ly */ ANY_COMBINATION,
    /* lz */ NOT_BEGIN | BREAK | NOT_END,
    /* lch */ NOT_BEGIN | PREFIX,
    /* lgh */ NOT_BEGIN | BREAK | NOT_END,
    /* lph */ NOT_BEGIN | PREFIX,
    /* lrh */ ILLEGAL_PAIR,
    /* lsh */ NOT_BEGIN | PREFIX,
    /* lth */ NOT_BEGIN | PREFIX,
    /* lwh */ ILLEGAL_PAIR,
    /* lqu */ NOT_BEGIN | BREAK | NOT_END,
    /* lck */ ILLEGAL_PAIR
  },
  {
    /* ma */ ANY_COMBINATION,
    /* mb */ NOT_BEGIN | BREAK | NOT_END,
    /* mc */ NOT_BEGIN | BREAK | NOT_END,
    /* md */ NOT_BEGIN | BREAK | NOT_END,
    /* me */ ANY_COMBINATION,
    /* mf */ NOT_BEGIN | BREAK | NOT_END,
    /* mg */ NOT_BEGIN | BREAK | NOT_END,
    /* mh */ NOT_BEGIN | BREAK | NOT_END,
    /* mi */ ANY_COMBINATION,
    /* mj */ NOT_BEGIN | BREAK | NOT_END,
    /* mk */ NOT_BEGIN | BREAK | NOT_END,
    /* ml */ NOT_BEGIN | BREAK | NOT_END,
    /* mm */ NOT_BEGIN,
    /* mn */ NOT_BEGIN | BREAK | NOT_END,
    /* mo */ ANY_COMBINATION,
    /* mp */ NOT_BEGIN,
    /* mr */ NOT_BEGIN | BREAK | NOT_END,
    /* ms */ NOT_BEGIN,
    /* mt */ NOT_BEGIN,
    /* mu */ ANY_COMBINATION,
    /* mv */ NOT_BEGIN | BREAK | NOT_END,
    /* mw */ NOT_BEGIN | BREAK | NOT_END,
    /* mx */ ILLEGAL_PAIR,
    /* my */ ANY_COMBINATION,
    /* mz */ NOT_BEGIN | BREAK | NOT_END,
    /* mch */ NOT_BEGIN | PREFIX,
    /* mgh */ NOT_BEGIN | BREAK | NOT_END,
    /* mph */ NOT_BEGIN,
    /* mrh */ ILLEGAL_PAIR,
    /* msh */ NOT_BEGIN,
    /* mth */ NOT_BEGIN,
    /* mwh */ ILLEGAL_PAIR,
    /* mqu */ NOT_BEGIN | BREAK | NOT_END,
    /* mck */ ILLEGAL_PAIR
  },
  {
    /* na */ ANY_COMBINATION,
    /* nb */ NOT_BEGIN | BREAK | NOT_END,
    /* nc */ NOT_BEGIN | BREAK | NOT_END,
    /* nd */ NOT_BEGIN,
    /* ne */ ANY_COMBINATION,
    /* nf */ NOT_BEGIN | BREAK | NOT_END,
    /* ng */ NOT_BEGIN | PREFIX,
    /* nh */ NOT_BEGIN | BREAK | NOT_END,
    /* ni */ ANY_COMBINATION,
    /* nj */ NOT_BEGIN | BREAK | NOT_END,
    /* nk */ NOT_BEGIN | PREFIX,
    /* nl */ NOT_BEGIN | BREAK | NOT_END,
    /* nm */ NOT_BEGIN | BREAK | NOT_END,
    /* nn */ NOT_BEGIN,
    /* no */ ANY_COMBINATION,
    /* np */ NOT_BEGIN | BREAK | NOT_END,
    /* nr */ NOT_BEGIN | BREAK | NOT_END,
    /* ns */ NOT_BEGIN,
    /* nt */ NOT_BEGIN,
    /* nu */ ANY_COMBINATION,
    /* nv */ NOT_BEGIN | BREAK | NOT_END,
    /* nw */ NOT_BEGIN | BREAK | NOT_END,
    /* nx */ ILLEGAL_PAIR,
    /* ny */ NOT_BEGIN,
    /* nz */ NOT_BEGIN | BREAK | NOT_END,
    /* nch */ NOT_BEGIN | PREFIX,
    /* ngh */ NOT_BEGIN | BREAK | NOT_END,
    /* nph */ NOT_BEGIN | PREFIX,
    /* nrh */ ILLEGAL_PAIR,
    /* nsh */ NOT_BEGIN,
    /* nth */ NOT_BEGIN,
    /* nwh */ ILLEGAL_PAIR,
    /* nqu */ NOT_BEGIN | BREAK | NOT_END,
    /* nck */ NOT_BEGIN | PREFIX
  },
  {
    /* oa */ ANY_COMBINATION,
    /* ob */ ANY_COMBINATION,
    /* oc */ ANY_COMBINATION,
    /* od */ ANY_COMBINATION,
    /* oe */ ILLEGAL_PAIR,
    /* of */ ANY_COMBINATION,
    /* og */ ANY_COMBINATION,
    /* oh */ NOT_BEGIN | BREAK | NOT_END,
    /* oi */ ANY_COMBINATION,
    /* oj */ ANY_COMBINATION,
    /* ok */ ANY_COMBINATION,
    /* ol */ ANY_COMBINATION,
    /* om */ ANY_COMBINATION,
    /* on */ ANY_COMBINATION,
    /* oo */ ANY_COMBINATION,
    /* op */ ANY_COMBINATION,
    /* or */ ANY_COMBINATION,
    /* os */ ANY_COMBINATION,
    /* ot */ ANY_COMBINATION,
    /* ou */ ANY_COMBINATION,
    /* ov */ ANY_COMBINATION,
    /* ow */ ANY_COMBINATION,
    /* ox */ ANY_COMBINATION,
    /* oy */ ANY_COMBINATION,
    /* oz */ ANY_COMBINATION,
    /* och */ ANY_COMBINATION,
    /* ogh */ NOT_BEGIN,
    /* oph */ ANY_COMBINATION,
    /* orh */ ILLEGAL_PAIR,
    /* osh */ ANY_COMBINATION,
    /* oth */ ANY_COMBINATION,
    /* owh */ ILLEGAL_PAIR,
    /* oqu */ BREAK | NOT_END,
    /* ock */ ANY_COMBINATION
  },
  {
    /* pa */ ANY_COMBINATION,
    /* pb */ NOT_BEGIN | BREAK | NOT_END,
    /* pc */ NOT_BEGIN | BREAK | NOT_END,
    /* pd */ NOT_BEGIN | BREAK | NOT_END,
    /* pe */ ANY_COMBINATION,
    /* pf */ NOT_BEGIN | BREAK | NOT_END,
    /* pg */ NOT_BEGIN | BREAK | NOT_END,
    /* ph */ NOT_BEGIN | BREAK | NOT_END,
    /* pi */ ANY_COMBINATION,
    /* pj */ NOT_BEGIN | BREAK | NOT_END,
    /* pk */ NOT_BEGIN | BREAK | NOT_END,
    /* pl */ SUFFIX | NOT_END,
    /* pm */ NOT_BEGIN | BREAK | NOT_END,
    /* pn */ NOT_BEGIN | BREAK | NOT_END,
    /* po */ ANY_COMBINATION,
    /* pp */ NOT_BEGIN | PREFIX,
    /* pr */ NOT_END,
    /* ps */ NOT_BEGIN | END,
    /* pt */ NOT_BEGIN | END,
    /* pu */ NOT_BEGIN | END,
    /* pv */ NOT_BEGIN | BREAK | NOT_END,
    /* pw */ NOT_BEGIN | BREAK | NOT_END,
    /* px */ ILLEGAL_PAIR,
    /* py */ ANY_COMBINATION,
    /* pz */ NOT_BEGIN | BREAK | NOT_END,
    /* pch */ NOT_BEGIN | BREAK | NOT_END,
    /* pgh */ NOT_BEGIN | BREAK | NOT_END,
    /* pph */ NOT_BEGIN | BREAK | NOT_END,
    /* prh */ ILLEGAL_PAIR,
    /* psh */ NOT_BEGIN | BREAK | NOT_END,
    /* pth */ NOT_BEGIN | BREAK | NOT_END,
    /* pwh */ ILLEGAL_PAIR,
    /* pqu */ NOT_BEGIN | BREAK | NOT_END,
    /* pck */ ILLEGAL_PAIR
  },
  {
    /* ra */ ANY_COMBINATION,
    /* rb */ NOT_BEGIN | PREFIX,
    /* rc */ NOT_BEGIN | PREFIX,
    /* rd */ NOT_BEGIN | PREFIX,
    /* re */ ANY_COMBINATION,
    /* rf */ NOT_BEGIN | PREFIX,
    /* rg */ NOT_BEGIN | PREFIX,
    /* rh */ NOT_BEGIN | BREAK | NOT_END,
    /* ri */ ANY_COMBINATION,
    /* rj */ NOT_BEGIN | PREFIX,
    /* rk */ NOT_BEGIN | PREFIX,
    /* rl */ NOT_BEGIN | PREFIX,
    /* rm */ NOT_BEGIN | PREFIX,
    /* rn */ NOT_BEGIN | PREFIX,
    /* ro */ ANY_COMBINATION,
    /* rp */ NOT_BEGIN | PREFIX,
    /* rr */ NOT_BEGIN | PREFIX,
    /* rs */ NOT_BEGIN | PREFIX,
    /* rt */ NOT_BEGIN | PREFIX,
    /* ru */ ANY_COMBINATION,
    /* rv */ NOT_BEGIN | PREFIX,
    /* rw */ NOT_BEGIN | BREAK | NOT_END,
    /* rx */ ILLEGAL_PAIR,
    /* ry */ ANY_COMBINATION,
    /* rz */ NOT_BEGIN | PREFIX,
    /* rch */ NOT_BEGIN | PREFIX,
    /* rgh */ NOT_BEGIN | BREAK | NOT_END,
    /* rph */ NOT_BEGIN | PREFIX,
    /* rrh */ ILLEGAL_PAIR,
    /* rsh */ NOT_BEGIN | PREFIX,
    /* rth */ NOT_BEGIN | PREFIX,
    /* rwh */ ILLEGAL_PAIR,
    /* rqu */ NOT_BEGIN | PREFIX | NOT_END,
    /* rck */ NOT_BEGIN | PREFIX
  },
  {
    /* sa */ ANY_COMBINATION,
    /* sb */ NOT_BEGIN | BREAK | NOT_END,
    /* sc */ NOT_END,
    /* sd */ NOT_BEGIN | BREAK | NOT_END,
    /* se */ ANY_COMBINATION,
    /* sf */ NOT_BEGIN | BREAK | NOT_END,
    /* sg */ NOT_BEGIN | BREAK | NOT_END,
    /* sh */ NOT_BEGIN | BREAK | NOT_END,
    /* si */ ANY_COMBINATION,
    /* sj */ NOT_BEGIN | BREAK | NOT_END,
    /* sk */ ANY_COMBINATION,
    /* sl */ BEGIN | SUFFIX | NOT_END,
    /* sm */ SUFFIX | NOT_END,
    /* sn */ PREFIX | SUFFIX | NOT_END,
    /* so */ ANY_COMBINATION,
    /* sp */ ANY_COMBINATION,
    /* sr */ NOT_BEGIN | NOT_END,
    /* ss */ NOT_BEGIN | PREFIX,
    /* st */ ANY_COMBINATION,
    /* su */ ANY_COMBINATION,
    /* sv */ NOT_BEGIN | BREAK | NOT_END,
    /* sw */ BEGIN | SUFFIX | NOT_END,
    /* sx */ ILLEGAL_PAIR,
    /* sy */ ANY_COMBINATION,
    /* sz */ NOT_BEGIN | BREAK | NOT_END,
    /* sch */ BEGIN | SUFFIX | NOT_END,
    /* sgh */ NOT_BEGIN | BREAK | NOT_END,
    /* sph */ NOT_BEGIN | BREAK | NOT_END,
    /* srh */ ILLEGAL_PAIR,
    /* ssh */ NOT_BEGIN | BREAK | NOT_END,
    /* sth */ NOT_BEGIN | BREAK | NOT_END,
    /* swh */ ILLEGAL_PAIR,
    /* squ */ SUFFIX | NOT_END,
    /* sck */ NOT_BEGIN
  },
  {
    /* ta */ ANY_COMBINATION,
    /* tb */ NOT_BEGIN | BREAK | NOT_END,
    /* tc */ NOT_BEGIN | BREAK | NOT_END,
    /* td */ NOT_BEGIN | BREAK | NOT_END,
    /* te */ ANY_COMBINATION,
    /* tf */ NOT_BEGIN | BREAK | NOT_END,
    /* tg */ NOT_BEGIN | BREAK | NOT_END,
    /* th */ NOT_BEGIN | BREAK | NOT_END,
    /* ti */ ANY_COMBINATION,
    /* tj */ NOT_BEGIN | BREAK | NOT_END,
    /* tk */ NOT_BEGIN | BREAK | NOT_END,
    /* tl */ NOT_BEGIN | BREAK | NOT_END,
    /* tm */ NOT_BEGIN | BREAK | NOT_END,
    /* tn */ NOT_BEGIN | BREAK | NOT_END,
    /* to */ ANY_COMBINATION,
    /* tp */ NOT_BEGIN | BREAK | NOT_END,
    /* tr */ NOT_END,
    /* ts */ NOT_BEGIN | END,
    /* tt */ NOT_BEGIN | PREFIX,
    /* tu */ ANY_COMBINATION,
    /* tv */ NOT_BEGIN | BREAK | NOT_END,
    /* tw */ BEGIN | SUFFIX | NOT_END,
    /* tx */ ILLEGAL_PAIR,
    /* ty */ ANY_COMBINATION,
    /* tz */ NOT_BEGIN | BREAK | NOT_END,
    /* tch */ NOT_BEGIN,
    /* tgh */ NOT_BEGIN | BREAK | NOT_END,
    /* tph */ NOT_BEGIN | END,
    /* trh */ ILLEGAL_PAIR,
    /* tsh */ NOT_BEGIN | END,
    /* tth */ NOT_BEGIN | BREAK | NOT_END,
    /* twh */ ILLEGAL_PAIR,
    /* tqu */ NOT_BEGIN | BREAK | NOT_END,
    /* tck */ ILLEGAL_PAIR
  },
  {
    /* ua */ NOT_BEGIN | BREAK | NOT_END,
    /* ub */ ANY_COMBINATION,
    /* uc */ ANY_COMBINATION,
    /* ud */ ANY_COMBINATION,
    /* ue */ NOT_BEGIN,
    /* uf */ ANY_COMBINATION,
    /* ug */ ANY_COMBINATION,
    /* uh */ NOT_BEGIN | BREAK | NOT_END,
    /* ui */ NOT_BEGIN | BREAK | NOT_END,
    /* uj */ ANY_COMBINATION,
    /* uk */ ANY_COMBINATION,
    /* ul */ ANY_COMBINATION,
    /* um */ ANY_COMBINATION,
    /* un */ ANY_COMBINATION,
    /* uo */ NOT_BEGIN | BREAK,
    /* up */ ANY_COMBINATION,
    /* ur */ ANY_COMBINATION,
    /* us */ ANY_COMBINATION,
    /* ut */ ANY_COMBINATION,
    /* uu */ ILLEGAL_PAIR,
    /* uv */ ANY_COMBINATION,
    /* uw */ NOT_BEGIN | BREAK | NOT_END,
    /* ux */ ANY_COMBINATION,
    /* uy */ NOT_BEGIN | BREAK | NOT_END,
    /* uz */ ANY_COMBINATION,
    /* uch */ ANY_COMBINATION,
    /* ugh */ NOT_BEGIN | PREFIX,
    /* uph */ ANY_COMBINATION,
    /* urh */ ILLEGAL_PAIR,
    /* ush */ ANY_COMBINATION,
    /* uth */ ANY_COMBINATION,
    /* uwh */ ILLEGAL_PAIR,
    /* uqu */ BREAK | NOT_END,
    /* uck */ ANY_COMBINATION
  },
  {
    /* va */ ANY_COMBINATION,
    /* vb */ NOT_BEGIN | BREAK | NOT_END,
    /* vc */ NOT_BEGIN | BREAK | NOT_END,
    /* vd */ NOT_BEGIN | BREAK | NOT_END,
    /* ve */ ANY_COMBINATION,
    /* vf */ NOT_BEGIN | BREAK | NOT_END,
    /* vg */ NOT_BEGIN | BREAK | NOT_END,
    /* vh */ NOT_BEGIN | BREAK | NOT_END,
    /* vi */ ANY_COMBINATION,
    /* vj */ NOT_BEGIN | BREAK | NOT_END,
    /* vk */ NOT_BEGIN | BREAK | NOT_END,
    /* vl */ NOT_BEGIN | BREAK | NOT_END,
    /* vm */ NOT_BEGIN | BREAK | NOT_END,
    /* vn */ NOT_BEGIN | BREAK | NOT_END,
    /* vo */ ANY_COMBINATION,
    /* vp */ NOT_BEGIN | BREAK | NOT_END,
    /* vr */ NOT_BEGIN | BREAK | NOT_END,
    /* vs */ NOT_BEGIN | BREAK | NOT_END,
    /* vt */ NOT_BEGIN | BREAK | NOT_END,
    /* vu */ ANY_COMBINATION,
    /* vv */ NOT_BEGIN | BREAK | NOT_END,
    /* vw */ NOT_BEGIN | BREAK | NOT_END,
    /* vx */ ILLEGAL_PAIR,
    /* vy */ NOT_BEGIN,
    /* vz */ NOT_BEGIN | BREAK | NOT_END,
    /* vch */ NOT_BEGIN | BREAK | NOT_END,
    /* vgh */ NOT_BEGIN | BREAK | NOT_END,
    /* vph */ NOT_BEGIN | BREAK | NOT_END,
    /* vrh */ ILLEGAL_PAIR,
    /* vsh */ NOT_BEGIN | BREAK | NOT_END,
    /* vth */ NOT_BEGIN | BREAK | NOT_END,
    /* vwh */ ILLEGAL_PAIR,
    /* vqu */ NOT_BEGIN | BREAK | NOT_END,
    /* vck */ ILLEGAL_PAIR
  },
  {
    /* wa */ ANY_COMBINATION,
    /* wb */ NOT_BEGIN | PREFIX,
    /* wc */ NOT_BEGIN | BREAK | NOT_END,
    /* wd */ NOT_BEGIN | PREFIX | END,
    /* we */ ANY_COMBINATION,
    /* wf */ NOT_BEGIN | PREFIX,
    /* wg */ NOT_BEGIN | PREFIX | END,
    /* wh */ NOT_BEGIN | BREAK | NOT_END,
    /* wi */ ANY_COMBINATION,
    /* wj */ NOT_BEGIN | BREAK | NOT_END,
    /* wk */ NOT_BEGIN | PREFIX,
    /* wl */ NOT_BEGIN | PREFIX | SUFFIX,
    /* wm */ NOT_BEGIN | PREFIX,
    /* wn */ NOT_BEGIN | PREFIX,
    /* wo */ ANY_COMBINATION,
    /* wp */ NOT_BEGIN | PREFIX,
    /* wr */ BEGIN | SUFFIX | NOT_END,
    /* ws */ NOT_BEGIN | PREFIX,
    /* wt */ NOT_BEGIN | PREFIX,
    /* wu */ ANY_COMBINATION,
    /* wv */ NOT_BEGIN | PREFIX,
    /* ww */ NOT_BEGIN | BREAK | NOT_END,
    /* wx */ NOT_BEGIN | PREFIX,
    /* wy */ ANY_COMBINATION,
    /* wz */ NOT_BEGIN | PREFIX,
    /* wch */ NOT_BEGIN,
    /* wgh */ NOT_BEGIN | BREAK | NOT_END,
    /* wph */ NOT_BEGIN,
    /* wrh */ ILLEGAL_PAIR,
    /* wsh */ NOT_BEGIN,
    /* wth */ NOT_BEGIN,
    /* wwh */ ILLEGAL_PAIR,
    /* wqu */ NOT_BEGIN | BREAK | NOT_END,
    /* wck */ NOT_BEGIN
  },
  {
    /* xa */ NOT_BEGIN,
    /* xb */ NOT_BEGIN | BREAK | NOT_END,
    /* xc */ NOT_BEGIN | BREAK | NOT_END,
    /* xd */ NOT_BEGIN | BREAK | NOT_END,
    /* xe */ NOT_BEGIN,
    /* xf */ NOT_BEGIN | BREAK | NOT_END,
    /* xg */ NOT_BEGIN | BREAK | NOT_END,
    /* xh */ NOT_BEGIN | BREAK | NOT_END,
    /* xi */ NOT_BEGIN,
    /* xj */ NOT_BEGIN | BREAK | NOT_END,
    /* xk */ NOT_BEGIN | BREAK | NOT_END,
    /* xl */ NOT_BEGIN | BREAK | NOT_END,
    /* xm */ NOT_BEGIN | BREAK | NOT_END,
    /* xn */ NOT_BEGIN | BREAK | NOT_END,
    /* xo */ NOT_BEGIN,
    /* xp */ NOT_BEGIN | BREAK | NOT_END,
    /* xr */ NOT_BEGIN | BREAK | NOT_END,
    /* xs */ NOT_BEGIN | BREAK | NOT_END,
    /* xt */ NOT_BEGIN | BREAK | NOT_END,
    /* xu */ NOT_BEGIN,
    /* xv */ NOT_BEGIN | BREAK | NOT_END,
    /* xw */ NOT_BEGIN | BREAK | NOT_END,
    /* xx */ ILLEGAL_PAIR,
    /* xy */ NOT_BEGIN,
    /* xz */ NOT_BEGIN | BREAK | NOT_END,
    /* xch */ NOT_BEGIN | BREAK | NOT_END,
    /* xgh */ NOT_BEGIN | BREAK | NOT_END,
    /* xph */ NOT_BEGIN | BREAK | NOT_END,
    /* xrh */ ILLEGAL_PAIR,
    /* xsh */ NOT_BEGIN | BREAK | NOT_END,
    /* xth */ NOT_BEGIN | BREAK | NOT_END,
    /* xwh */ ILLEGAL_PAIR,
    /* xqu */ NOT_BEGIN | BREAK | NOT_END,
    /* xck */ ILLEGAL_PAIR
  },
  {
    /* ya */ ANY_COMBINATION,
    /* yb */ NOT_BEGIN,
    /* yc */ NOT_BEGIN | NOT_END,
    /* yd */ NOT_BEGIN,
    /* ye */ ANY_COMBINATION,
    /* yf */ NOT_BEGIN | NOT_END,
    /* yg */ NOT_BEGIN,
    /* yh */ NOT_BEGIN | BREAK | NOT_END,
    /* yi */ BEGIN | NOT_END,
    /* yj */ NOT_BEGIN | NOT_END,
    /* yk */ NOT_BEGIN,
    /* yl */ NOT_BEGIN | NOT_END,
    /* ym */ NOT_BEGIN,
    /* yn */ NOT_BEGIN,
    /* yo */ ANY_COMBINATION,
    /* yp */ NOT_BEGIN,
    /* yr */ NOT_BEGIN | BREAK | NOT_END,
    /* ys */ NOT_BEGIN,
    /* yt */ NOT_BEGIN,
    /* yu */ ANY_COMBINATION,
    /* yv */ NOT_BEGIN | NOT_END,
    /* yw */ NOT_BEGIN | BREAK | NOT_END,
    /* yx */ NOT_BEGIN,
    /* yy */ ILLEGAL_PAIR,
    /* yz */ NOT_BEGIN,
    /* ych */ NOT_BEGIN | BREAK | NOT_END,
    /* ygh */ NOT_BEGIN | BREAK | NOT_END,
    /* yph */ NOT_BEGIN | BREAK | NOT_END,
    /* yrh */ ILLEGAL_PAIR,
    /* ysh */ NOT_BEGIN | BREAK | NOT_END,
    /* yth */ NOT_BEGIN | BREAK | NOT_END,
    /* ywh */ ILLEGAL_PAIR,
    /* yqu */ NOT_BEGIN | BREAK | NOT_END,
    /* yck */ ILLEGAL_PAIR
  },
  {
    /* za */ ANY_COMBINATION,
    /* zb */ NOT_BEGIN | BREAK | NOT_END,
    /* zc */ NOT_BEGIN | BREAK | NOT_END,
    /* zd */ NOT_BEGIN | BREAK | NOT_END,
    /* ze */ ANY_COMBINATION,
    /* zf */ NOT_BEGIN | BREAK | NOT_END,
    /* zg */ NOT_BEGIN | BREAK | NOT_END,
    /* zh */ NOT_BEGIN | BREAK | NOT_END,
    /* zi */ ANY_COMBINATION,
    /* zj */ NOT_BEGIN | BREAK | NOT_END,
    /* zk */ NOT_BEGIN | BREAK | NOT_END,
    /* zl */ NOT_BEGIN | BREAK | NOT_END,
    /* zm */ NOT_BEGIN | BREAK | NOT_END,
    /* zn */ NOT_BEGIN | BREAK | NOT_END,
    /* zo */ ANY_COMBINATION,
    /* zp */ NOT_BEGIN | BREAK | NOT_END,
    /* zr */ NOT_BEGIN | NOT_END,
    /* zs */ NOT_BEGIN | BREAK | NOT_END,
    /* zt */ NOT_BEGIN,
    /* zu */ ANY_COMBINATION,
    /* zv */ NOT_BEGIN | BREAK | NOT_END,
    /* zw */ SUFFIX | NOT_END,
    /* zx */ ILLEGAL_PAIR,
    /* zy */ ANY_COMBINATION,
    /* zz */ NOT_BEGIN,
    /* zch */ NOT_BEGIN | BREAK | NOT_END,
    /* zgh */ NOT_BEGIN | BREAK | NOT_END,
    /* zph */ NOT_BEGIN | BREAK | NOT_END,
    /* zrh */ ILLEGAL_PAIR,
    /* zsh */ NOT_BEGIN | BREAK | NOT_END,
    /* zth */ NOT_BEGIN | BREAK | NOT_END,
    /* zwh */ ILLEGAL_PAIR,
    /* zqu */ NOT_BEGIN | BREAK | NOT_END,
    /* zck */ ILLEGAL_PAIR
  },
  {
    /* cha */ ANY_COMBINATION,
    /* chb */ NOT_BEGIN | BREAK | NOT_END,
    /* chc */ NOT_BEGIN | BREAK | NOT_END,
    /* chd */ NOT_BEGIN | BREAK | NOT_END,
    /* che */ ANY_COMBINATION,
    /* chf */ NOT_BEGIN | BREAK | NOT_END,
    /* chg */ NOT_BEGIN | BREAK | NOT_END,
    /* chh */ NOT_BEGIN | BREAK | NOT_END,
    /* chi */ ANY_COMBINATION,
    /* chj */ NOT_BEGIN | BREAK | NOT_END,
    /* chk */ NOT_BEGIN | BREAK | NOT_END,
    /* chl */ NOT_BEGIN | BREAK | NOT_END,
    /* chm */ NOT_BEGIN | BREAK | NOT_END,
    /* chn */ NOT_BEGIN | BREAK | NOT_END,
    /* cho */ ANY_COMBINATION,
    /* chp */ NOT_BEGIN | BREAK | NOT_END,
    /* chr */ NOT_END,
    /* chs */ NOT_BEGIN | BREAK | NOT_END,
    /* cht */ NOT_BEGIN | BREAK | NOT_END,
    /* chu */ ANY_COMBINATION,
    /* chv */ NOT_BEGIN | BREAK | NOT_END,
    /* chw */ NOT_BEGIN | NOT_END,
    /* chx */ ILLEGAL_PAIR,
    /* chy */ ANY_COMBINATION,
    /* chz */ NOT_BEGIN | BREAK | NOT_END,
    /* chch */ ILLEGAL_PAIR,
    /* chgh */ NOT_BEGIN | BREAK | NOT_END,
    /* chph */ NOT_BEGIN | BREAK | NOT_END,
    /* chrh */ ILLEGAL_PAIR,
    /* chsh */ NOT_BEGIN | BREAK | NOT_END,
    /* chth */ NOT_BEGIN | BREAK | NOT_END,
    /* chwh */ ILLEGAL_PAIR,
    /* chqu */ NOT_BEGIN | BREAK | NOT_END,
    /* chck */ ILLEGAL_PAIR
  },
  {
    /* gha */ ANY_COMBINATION,
    /* ghb */ NOT_BEGIN | BREAK | PREFIX | NOT_END,
    /* ghc */ NOT_BEGIN | BREAK | PREFIX | NOT_END,
    /* ghd */ NOT_BEGIN | BREAK | PREFIX | NOT_END,
    /* ghe */ ANY_COMBINATION,
    /* ghf */ NOT_BEGIN | BREAK | PREFIX | NOT_END,
    /* ghg */ NOT_BEGIN | BREAK | PREFIX | NOT_END,
    /* ghh */ NOT_BEGIN | BREAK | PREFIX | NOT_END,
    /* ghi */ BEGIN | NOT_END,
    /* ghj */ NOT_BEGIN | BREAK | PREFIX | NOT_END,
    /* ghk */ NOT_BEGIN | BREAK | PREFIX | NOT_END,
    /* ghl */ NOT_BEGIN | BREAK | PREFIX | NOT_END,
    /* ghm */ NOT_BEGIN | BREAK | PREFIX | NOT_END,
    /* ghn */ NOT_BEGIN | BREAK | PREFIX | NOT_END,
    /* gho */ BEGIN | NOT_END,
    /* ghp */ NOT_BEGIN | BREAK | NOT_END,
    /* ghr */ NOT_BEGIN | BREAK | PREFIX | NOT_END,
    /* ghs */ NOT_BEGIN | PREFIX,
    /* ght */ NOT_BEGIN | PREFIX,
    /* ghu */ NOT_BEGIN | BREAK | PREFIX | NOT_END,
    /* ghv */ NOT_BEGIN | BREAK | PREFIX | NOT_END,
    /* ghw */ NOT_BEGIN | BREAK | PREFIX | NOT_END,
    /* ghx */ ILLEGAL_PAIR,
    /* ghy */ NOT_BEGIN | BREAK | PREFIX | NOT_END,
    /* ghz */ NOT_BEGIN | BREAK | PREFIX | NOT_END,
    /* ghch */ NOT_BEGIN | BREAK | PREFIX | NOT_END,
    /* ghgh */ ILLEGAL_PAIR,
    /* ghph */ NOT_BEGIN | BREAK | PREFIX | NOT_END,
    /* ghrh */ ILLEGAL_PAIR,
    /* ghsh */ NOT_BEGIN | BREAK | PREFIX | NOT_END,
    /* ghth */ NOT_BEGIN | BREAK | PREFIX | NOT_END,
    /* ghwh */ ILLEGAL_PAIR,
    /* ghqu */ NOT_BEGIN | BREAK | PREFIX | NOT_END,
    /* ghck */ ILLEGAL_PAIR
  },
  {
    /* pha */ ANY_COMBINATION,
    /* phb */ NOT_BEGIN | BREAK | NOT_END,
    /* phc */ NOT_BEGIN | BREAK | NOT_END,
    /* phd */ NOT_BEGIN | BREAK | NOT_END,
    /* phe */ ANY_COMBINATION,
    /* phf */ NOT_BEGIN | BREAK | NOT_END,
    /* phg */ NOT_BEGIN | BREAK | NOT_END,
    /* phh */ NOT_BEGIN | BREAK | NOT_END,
    /* phi */ ANY_COMBINATION,
    /* phj */ NOT_BEGIN | BREAK | NOT_END,
    /* phk */ NOT_BEGIN | BREAK | NOT_END,
    /* phl */ BEGIN | SUFFIX | NOT_END,
    /* phm */ NOT_BEGIN | BREAK | NOT_END,
    /* phn */ NOT_BEGIN | BREAK | NOT_END,
    /* pho */ ANY_COMBINATION,
    /* php */ NOT_BEGIN | BREAK | NOT_END,
    /* phr */ NOT_END,
    /* phs */ NOT_BEGIN,
    /* pht */ NOT_BEGIN,
    /* phu */ ANY_COMBINATION,
    /* phv */ NOT_BEGIN | NOT_END,
    /* phw */ NOT_BEGIN | NOT_END,
    /* phx */ ILLEGAL_PAIR,
    /* phy */ NOT_BEGIN,
    /* phz */ NOT_BEGIN | BREAK | NOT_END,
    /* phch */ NOT_BEGIN | BREAK | NOT_END,
    /* phgh */ NOT_BEGIN | BREAK | NOT_END,
    /* phph */ ILLEGAL_PAIR,
    /* phrh */ ILLEGAL_PAIR,
    /* phsh */ NOT_BEGIN | BREAK | NOT_END,
    /* phth */ NOT_BEGIN | BREAK | NOT_END,
    /* phwh */ ILLEGAL_PAIR,
    /* phqu */ NOT_BEGIN | BREAK | NOT_END,
    /* phck */ ILLEGAL_PAIR
  },
  {
    /* rha */ BEGIN | NOT_END,
    /* rhb */ ILLEGAL_PAIR,
    /* rhc */ ILLEGAL_PAIR,
    /* rhd */ ILLEGAL_PAIR,
    /* rhe */ BEGIN | NOT_END,
    /* rhf */ ILLEGAL_PAIR,
    /* rhg */ ILLEGAL_PAIR,
    /* rhh */ ILLEGAL_PAIR,
    /* rhi */ BEGIN | NOT_END,
    /* rhj */ ILLEGAL_PAIR,
    /* rhk */ ILLEGAL_PAIR,
    /* rhl */ ILLEGAL_PAIR,
    /* rhm */ ILLEGAL_PAIR,
    /* rhn */ ILLEGAL_PAIR,
    /* rho */ BEGIN | NOT_END,
    /* rhp */ ILLEGAL_PAIR,
    /* rhr */ ILLEGAL_PAIR,
    /* rhs */ ILLEGAL_PAIR,
    /* rht */ ILLEGAL_PAIR,
    /* rhu */ BEGIN | NOT_END,
    /* rhv */ ILLEGAL_PAIR,
    /* rhw */ ILLEGAL_PAIR,
    /* rhx */ ILLEGAL_PAIR,
    /* rhy */ BEGIN | NOT_END,
    /* rhz */ ILLEGAL_PAIR,
    /* rhch */ ILLEGAL_PAIR,
    /* rhgh */ ILLEGAL_PAIR,
    /* rhph */ ILLEGAL_PAIR,
    /* rhrh */ ILLEGAL_PAIR,
    /* rhsh */ ILLEGAL_PAIR,
    /* rhth */ ILLEGAL_PAIR,
    /* rhwh */ ILLEGAL_PAIR,
    /* rhqu */ ILLEGAL_PAIR,
    /* rhck */ ILLEGAL_PAIR
  },
  {
    /* sha */ ANY_COMBINATION,
    /* shb */ NOT_BEGIN | BREAK | NOT_END,
    /* shc */ NOT_BEGIN | BREAK | NOT_END,
    /* shd */ NOT_BEGIN | BREAK | NOT_END,
    /* she */ ANY_COMBINATION,
    /* shf */ NOT_BEGIN | BREAK | NOT_END,
    /* shg */ NOT_BEGIN | BREAK | NOT_END,
    /* shh */ ILLEGAL_PAIR,
    /* shi */ ANY_COMBINATION,
    /* shj */ NOT_BEGIN | BREAK | NOT_END,
    /* shk */ NOT_BEGIN,
    /* shl */ BEGIN | SUFFIX | NOT_END,
    /* shm */ BEGIN | SUFFIX | NOT_END,
    /* shn */ BEGIN | SUFFIX | NOT_END,
    /* sho */ ANY_COMBINATION,
    /* shp */ NOT_BEGIN,
    /* shr */ BEGIN | SUFFIX | NOT_END,
    /* shs */ NOT_BEGIN | BREAK | NOT_END,
    /* sht */ SUFFIX,
    /* shu */ ANY_COMBINATION,
    /* shv */ NOT_BEGIN | BREAK | NOT_END,
    /* shw */ SUFFIX | NOT_END,
    /* shx */ ILLEGAL_PAIR,
    /* shy */ ANY_COMBINATION,
    /* shz */ NOT_BEGIN | BREAK | NOT_END,
    /* shch */ NOT_BEGIN | BREAK | NOT_END,
    /* shgh */ NOT_BEGIN | BREAK | NOT_END,
    /* shph */ NOT_BEGIN | BREAK | NOT_END,
    /* shrh */ ILLEGAL_PAIR,
    /* shsh */ ILLEGAL_PAIR,
    /* shth */ NOT_BEGIN | BREAK | NOT_END,
    /* shwh */ ILLEGAL_PAIR,
    /* shqu */ NOT_BEGIN | BREAK | NOT_END,
    /* shck */ ILLEGAL_PAIR
  },
  {
    /* tha */ ANY_COMBINATION,
    /* thb */ NOT_BEGIN | BREAK | NOT_END,
    /* thc */ NOT_BEGIN | BREAK | NOT_END,
    /* thd */ NOT_BEGIN | BREAK | NOT_END,
    /* the */ ANY_COMBINATION,
    /* thf */ NOT_BEGIN | BREAK | NOT_END,
    /* thg */ NOT_BEGIN | BREAK | NOT_END,
    /* thh */ NOT_BEGIN | BREAK | NOT_END,
    /* thi */ ANY_COMBINATION,
    /* thj */ NOT_BEGIN | BREAK | NOT_END,
    /* thk */ NOT_BEGIN | BREAK | NOT_END,
    /* thl */ NOT_BEGIN | BREAK | NOT_END,
    /* thm */ NOT_BEGIN | BREAK | NOT_END,
    /* thn */ NOT_BEGIN | BREAK | NOT_END,
    /* tho */ ANY_COMBINATION,
    /* thp */ NOT_BEGIN | BREAK | NOT_END,
    /* thr */ NOT_END,
    /* ths */ NOT_BEGIN | END,
    /* tht */ NOT_BEGIN | BREAK | NOT_END,
    /* thu */ ANY_COMBINATION,
    /* thv */ NOT_BEGIN | BREAK | NOT_END,
    /* thw */ SUFFIX | NOT_END,
    /* thx */ ILLEGAL_PAIR,
    /* thy */ ANY_COMBINATION,
    /* thz */ NOT_BEGIN | BREAK | NOT_END,
    /* thch */ NOT_BEGIN | BREAK | NOT_END,
    /* thgh */ NOT_BEGIN | BREAK | NOT_END,
    /* thph */ NOT_BEGIN | BREAK | NOT_END,
    /* thrh */ ILLEGAL_PAIR,
    /* thsh */ NOT_BEGIN | BREAK | NOT_END,
    /* thth */ ILLEGAL_PAIR,
    /* thwh */ ILLEGAL_PAIR,
    /* thqu */ NOT_BEGIN | BREAK | NOT_END,
    /* thck */ ILLEGAL_PAIR
  },
  {
    /* wha */ BEGIN | NOT_END,
    /* whb */ ILLEGAL_PAIR,
    /* whc */ ILLEGAL_PAIR,
    /* whd */ ILLEGAL_PAIR,
    /* whe */ BEGIN | NOT_END,
    /* whf */ ILLEGAL_PAIR,
    /* whg */ ILLEGAL_PAIR,
    /* whh */ ILLEGAL_PAIR,
    /* whi */ BEGIN | NOT_END,
    /* whj */ ILLEGAL_PAIR,
    /* whk */ ILLEGAL_PAIR,
    /* whl */ ILLEGAL_PAIR,
    /* whm */ ILLEGAL_PAIR,
    /* whn */ ILLEGAL_PAIR,
    /* who */ BEGIN | NOT_END,
    /* whp */ ILLEGAL_PAIR,
    /* whr */ ILLEGAL_PAIR,
    /* whs */ ILLEGAL_PAIR,
    /* wht */ ILLEGAL_PAIR,
    /* whu */ ILLEGAL_PAIR,
    /* whv */ ILLEGAL_PAIR,
    /* whw */ ILLEGAL_PAIR,
    /* whx */ ILLEGAL_PAIR,
    /* why */ BEGIN | NOT_END,
    /* whz */ ILLEGAL_PAIR,
    /* whch */ ILLEGAL_PAIR,
    /* whgh */ ILLEGAL_PAIR,
    /* whph */ ILLEGAL_PAIR,
    /* whrh */ ILLEGAL_PAIR,
    /* whsh */ ILLEGAL_PAIR,
    /* whth */ ILLEGAL_PAIR,
    /* whwh */ ILLEGAL_PAIR,
    /* whqu */ ILLEGAL_PAIR,
    /* whck */ ILLEGAL_PAIR
  },
  {
    /* qua */ ANY_COMBINATION,
    /* qub */ ILLEGAL_PAIR,
    /* quc */ ILLEGAL_PAIR,
    /* qud */ ILLEGAL_PAIR,
    /* que */ ANY_COMBINATION,
    /* quf */ ILLEGAL_PAIR,
    /* qug */ ILLEGAL_PAIR,
    /* quh */ ILLEGAL_PAIR,
    /* qui */ ANY_COMBINATION,
    /* quj */ ILLEGAL_PAIR,
    /* quk */ ILLEGAL_PAIR,
    /* qul */ ILLEGAL_PAIR,
    /* qum */ ILLEGAL_PAIR,
    /* qun */ ILLEGAL_PAIR,
    /* quo */ ANY_COMBINATION,
    /* qup */ ILLEGAL_PAIR,
    /* qur */ ILLEGAL_PAIR,
    /* qus */ ILLEGAL_PAIR,
    /* qut */ ILLEGAL_PAIR,
    /* quu */ ILLEGAL_PAIR,
    /* quv */ ILLEGAL_PAIR,
    /* quw */ ILLEGAL_PAIR,
    /* qux */ ILLEGAL_PAIR,
    /* quy */ ILLEGAL_PAIR,
    /* quz */ ILLEGAL_PAIR,
    /* quch */ ILLEGAL_PAIR,
    /* qugh */ ILLEGAL_PAIR,
    /* quph */ ILLEGAL_PAIR,
    /* qurh */ ILLEGAL_PAIR,
    /* qush */ ILLEGAL_PAIR,
    /* quth */ ILLEGAL_PAIR,
    /* quwh */ ILLEGAL_PAIR,
    /* ququ */ ILLEGAL_PAIR,
    /* quck */ ILLEGAL_PAIR
  },
  {
    /* cka */ NOT_BEGIN | BREAK | NOT_END,
    /* ckb */ NOT_BEGIN | BREAK | NOT_END,
    /* ckc */ NOT_BEGIN | BREAK | NOT_END,
    /* ckd */ NOT_BEGIN | BREAK | NOT_END,
    /* cke */ NOT_BEGIN | BREAK | NOT_END,
    /* ckf */ NOT_BEGIN | BREAK | NOT_END,
    /* ckg */ NOT_BEGIN | BREAK | NOT_END,
    /* ckh */ NOT_BEGIN | BREAK | NOT_END,
    /* cki */ NOT_BEGIN | BREAK | NOT_END,
    /* ckj */ NOT_BEGIN | BREAK | NOT_END,
    /* ckk */ NOT_BEGIN | BREAK | NOT_END,
    /* ckl */ NOT_BEGIN | BREAK | NOT_END,
    /* ckm */ NOT_BEGIN | BREAK | NOT_END,
    /* ckn */ NOT_BEGIN | BREAK | NOT_END,
    /* cko */ NOT_BEGIN | BREAK | NOT_END,
    /* ckp */ NOT_BEGIN | BREAK | NOT_END,
    /* ckr */ NOT_BEGIN | BREAK | NOT_END,
    /* cks */ NOT_BEGIN,
    /* ckt */ NOT_BEGIN | BREAK | NOT_END,
    /* cku */ NOT_BEGIN | BREAK | NOT_END,
    /* ckv */ NOT_BEGIN | BREAK | NOT_END,
    /* ckw */ NOT_BEGIN | BREAK | NOT_END,
    /* ckx */ ILLEGAL_PAIR,
    /* cky */ NOT_BEGIN,
    /* ckz */ NOT_BEGIN | BREAK | NOT_END,
    /* ckch */ NOT_BEGIN | BREAK | NOT_END,
    /* ckgh */ NOT_BEGIN | BREAK | NOT_END,
    /* ckph */ NOT_BEGIN | BREAK | NOT_END,
    /* ckrh */ ILLEGAL_PAIR,
    /* cksh */ NOT_BEGIN | BREAK | NOT_END,
    /* ckth */ NOT_BEGIN | BREAK | NOT_END,
    /* ckwh */ ILLEGAL_PAIR,
    /* ckqu */ NOT_BEGIN | BREAK | NOT_END,
    /* ckck */ ILLEGAL_PAIR
  }
};




#ifdef    PASSOGVA_DEBUG
int main (int argc, char **argv)
{
  int argno;
  long seed;
  unsigned short pwlen;
  unsigned short minimum;
  int number_of_words;
  boolean no_legal_words;
  char *unhyphenated_word;
  char *hyphenated_word;
  time_t ltime;
  pid_t lpid;

#ifdef B1
  int algorithm = 0;
#endif

  number_of_words = 1;
  no_legal_words = FALSE;
  seed = 0L;
  pwlen = 8;
  minimum = 6;

  for (argno = 0; argno < argc; argno++) {
    if (argv[argno][0] == '-') {
      switch (argv[argno][1]) {
#ifdef B1
        case 'a':
          algorithm = atoi (&argv[argno][2]);
          break;
#endif
        case 's':
          seed = atol (&argv[argno][2]);
          if (seed == 0L)
            seed = 1L;
          set_seed(seed);
          break;
        case 'l':
          pwlen = abs (atoi (&argv[argno][2]));
          if (pwlen < 1)
            pwlen = 8;
          break;
        case 'm':
          minimum = abs (atoi (&argv[argno][2]));
          if (minimum < 1)
            minimum = 1;
          break;
        case 'n':
          no_legal_words = TRUE;
          break;
      }
    } else {
      number_of_words = atoi (argv[argno]);
    }

    if (number_of_words < 1) {
      number_of_words = 1;
    }
  }

  /*
   * During debugging (PASSOGVA_DEBUG is set), we generate the seed from
   * here rather than the first entry to randomword() .
   */
  if (seed == 0L) {
    time(&ltime);
    lpid = getpid();
    set_seed((long) ltime ^ (lpid + (lpid << 15)));
  }

  if (minimum > pwlen) {
    fflush(stdout);
    fprintf (stderr, "minimum (%u) new password length cannot exceed maximum (%u)\n", (uint) minimum, (uint) pwlen);
    fflush(stderr);
    exit (1);
  }

  for (argno = 1; argno <= number_of_words; argno++) {
    unhyphenated_word = (char *)calloc (sizeof (char), pwlen + 1);
    hyphenated_word = (char *)calloc (sizeof (char), 2 * pwlen);
#ifdef B1
    switch (algorithm)  {
      default:
      case 0:
        randomword(unhyphenated_word,
                   hyphenated_word,
                   minimum,
                   pwlen,
                   no_legal_words,
                   0L);
        fflush(stderr);
        fprintf(stdout, "%s (%s)\n", unhyphenated_word, hyphenated_word);
        break;
      case 1:
        randomchars(unhyphenated_word, minimum, pwlen, no_legal_words, 0L);
        fflush(stderr);
        fprintf(stdout, "%s\n", unhyphenated_word);
        break;
      case 2:
        randomletters(unhyphenated_word, minimum, pwlen, no_legal_words, 0L);
        fflush(stderr);
        fprintf(stdout, "%s\n", unhyphenated_word);
        break;
    }
#else
    randomword(unhyphenated_word,
               hyphenated_word,
               minimum,
               pwlen,
               no_legal_words,
               0L);
    fflush(stderr);
    fprintf(stdout, "%s (%s)\n", unhyphenated_word, hyphenated_word);
#endif
    fflush(stdout);
    free(unhyphenated_word);
    free(hyphenated_word);
  }

  return 0;
}
#endif


#ifdef B1
/*
 * Randomchars will generate a random string and place it in the
 * buffer word.  The word must be pre-allocated.  The words generated
 * will have sizes between minlen and maxlen.  If restrict is TRUE,
 * words will not be generated that appear as login names or as
 * entries in the on-line dictionary.  The seed is used on first use
 * of the routine.  The length of the word is returned, or -1 if there
 * were an error (length settings are wrong or dictionary checking
 * could not be done).  The seed is used on first use of the routine.
 */
int randomchars (char *string,
                 unsigned short minlen,
                 unsigned short maxlen,
                 boolean restrict,
                 long seed)
{
  int loop_count;
  unsigned short string_size;
  unsigned short build;
  static int been_here_before = FALSE;

  /*
   * Execute this upon startup.  This initializes the environment,
   * including seed'ing the random number generator and loading the
   * on-line dictionary.
   */
  if (!been_here_before) {
    been_here_before = TRUE;

#ifndef PASSOGVA_DEBUG
    set_seed(seed);
#endif
  }

  /*
   * Check for minlen > maxlen.  This is an error.
   */
  if (minlen > maxlen) {
    return (-1);
  }


  loop_count = 0;
  string_size = get_random(minlen, maxlen);

  do {
    for (build = 0; build < string_size; build++) {
      string[build] = (char) get_random((unsigned short) '!',
                                        (unsigned short) '~');
    }

    restrict = 0;

    loop_count++;
  } while (restrict && (loop_count <= MAX_UNACCEPTABLE));

  string[string_size] = '\0';

  return string_size;
}


/*
 * Randomletters will generate a random string of letters and place it
 * in the buffer word.  The word must be pre-allocated.  The words
 * generated will have sizes between minlen and maxlen.  If restrict
 * is TRUE, words will not be generated that appear as login names or
 * as entries in the on-line dictionary.  The seed is used on first
 * use of the routine.  The length of the word is returned, or -1 if
 * there were an error (length settings are wrong or dictionary
 * checking could not be done).  The seed is used on first use of the
 * routine.
 */
int randomletters (char *string,
                   unsigned short minlen,
                   unsigned short maxlen,
                   boolean restrict,
                   long seed)
{
  int loop_count;
  unsigned short string_size;
  unsigned short build;
  static int been_here_before = FALSE;

  /*
   * Execute this upon startup.  This initializes the environment,
   * including seed'ing the random number generator and loading the
   * on-line dictionary.
   */
  if (!been_here_before) {
    been_here_before = TRUE;

#ifndef PASSOGVA_DEBUG
    set_seed(seed);
#endif
  }

  /*
   * Check for minlen > maxlen.  This is an error.
   */
  if (minlen > maxlen) {
    return (-1);
  }


  loop_count = 0;
  string_size = get_random(minlen, maxlen);

  do {
    for (build = 0; build < string_size; build++) {
      string[build] = (char) get_random((unsigned short) 'a',
                                        (unsigned short) 'z');
    }

    restrict = 0;

    loop_count++;
  } while (restrict && (loop_count <= MAX_UNACCEPTABLE));

  string[string_size] = '\0';

  return string_size;
}
#endif


/*
 * Randomword will generate a random word and place it in the buffer
 * word.  Also, the hyphenated word will be placed into the buffer
 * hyphenated_word.  Both word and hyphenated_word must be
 * pre-allocated.  The words generated will have sizes between minlen
 * and maxlen.  If restrict is TRUE, words will not be generated that
 * appear as login names or as entries in the on-line dictionary.
 * This algorithm was initially worded out by Morrie Gasser in 1975.
 * Any changes here are minimal so that as many word combinations can
 * be produced as possible (and thus keep the words random).  The seed
 * is used on first use of the routine.  The length of the
 * unhyphenated word is returned, or -1 if there were an error (length
 * settings are wrong or dictionary checking could not be done.
 */
int randomword (char *word,
                char *hyphenated_word,
                unsigned short minlen,
                unsigned short maxlen,
                boolean restrict,
                long seed)
{
  int pwlen;
  int loop_count;
  static int been_here_before = FALSE;

  /*
   * Execute this upon startup.  This initializes the environment,
   * including seed'ing the random number generator and loading the
   * on-line dictionary.
   */
  if (!been_here_before) {
    been_here_before = TRUE;


#ifndef PASSOGVA_DEBUG
    set_seed(seed);
#endif
  }

  /*
   * Check for minlen>maxlen.  This is an error.
   */
  if (minlen > maxlen) {
    return (-1);
  }

  /*
   * Check for zero length words.  This is technically not an error,
   * so we take the short cut and return a null word and a length of
   * 0.
   */
  if (maxlen == 0) {
    word[0] = '\0';
    hyphenated_word[0] = '\0';
    return (0);
  }

  /*
   * Continue finding words until the criteria are satisfied.  The
   * criteria are, if restrict is set, that if the word appears as
   * either a login name or as part of the on-line dictionary, throw
   * out the word and look for another.
   */
  loop_count = 0;

  do {
    /*
     * Get a random word.  Its length is a random quantity from with
     * the limits specified in the call to randomword().
     */
    pwlen = get_word(word, hyphenated_word, get_random (minlen, maxlen));

    restrict = 0;

    loop_count++;
  } while (restrict && (loop_count <= MAX_UNACCEPTABLE));

  if (restrict) {
    fflush(stdout);
    fprintf(stderr, "could not find acceptable random password\n");
    fflush(stderr);
    exit(1);
  }

  return (pwlen);
}


/*
 * This is the routine that returns a random word -- as yet unchecked
 * against the passwd file or the dictionary.  It collects random
 * syllables until a predetermined word length is found.  If a retry
 * threshold is reached, another word is tried.  Given that the random
 * number generator is uniformly distributed, eventually a word will
 * be found if the retry limit is adequately large enough.
 */
static int get_word (char *word,
                     char *hyphenated_word,
                     unsigned short pwlen)
{
  unsigned short word_length;
  unsigned short syllable_length;
  char *new_syllable;
  unsigned short *syllable_units;
  unsigned short word_size;
  unsigned short word_place;
  unsigned short *word_units;
  unsigned short syllable_size;
  unsigned int tries;

  /*
   * Keep count of retries.
   */
  tries = 0;

  /*
   * The length of the word in characters.
   */
  word_length = 0;

  /*
   * The length of the word in character units (each of which is one
   * or two characters long).
   */
  word_size = 0;

  /*
   * Initialize the array storing the word units.  Since we know the
   * length of the word, we only need one of that length.  This method
   * is preferable to a static array, since it allows us flexibility
   * in choosing arbitrarily long word lengths.  Since a word can
   * contain one syllable, we should make syllable_units, the array
   * holding the analogous units for an individual syllable, the same
   * length.  No explicit rule limits the length of syllables, but
   * digram rules and heuristics do so indirectly.
   */
  word_units =
    (unsigned short *)
    calloc (pwlen, sizeof (unsigned short));
  syllable_units =
    (unsigned short *)
    calloc (pwlen, sizeof (unsigned short));
  new_syllable =
    (char *)
    calloc (pwlen, sizeof (unsigned short));

  /*
   * Find syllables until the entire word is constructed.
   */
  while (word_length < pwlen) {
    /*
     * Get the syllable and find its length.
     */
    get_syllable(new_syllable,
                 pwlen - word_length,
                 syllable_units,
                 &syllable_size);

    syllable_length = strlen(new_syllable);

    /*
     * Append the syllable units to the word units.
     */
    for (word_place = 0; word_place <= syllable_size; word_place++) {
      word_units[word_size + word_place] = syllable_units[word_place];
    }
    word_size += syllable_size + 1;

    /*
     * If the word has been improperly formed, throw out the syllable.
     * The checks performed here are those that must be formed on a
     * word basis.  The other tests are performed entirely within the
     * syllable.  Otherwise, append the syllable to the word and
     * append the syllable to the hyphenated version of the word.
     */
    if (improper_word(word_units, word_size) ||
        ((word_length == 0) &&
         have_initial_y(syllable_units, syllable_size)) ||
        ((word_length + syllable_length == pwlen) &&
         have_final_split(syllable_units, syllable_size))) {
      word_size -= syllable_size + 1;
    } else {
      if (word_length == 0) {
        strcpy(word, new_syllable);
        strcpy(hyphenated_word, new_syllable);
      } else {
        strcat(word, new_syllable);
        strcat(hyphenated_word, "-");
        strcat(hyphenated_word, new_syllable);
      }
      word_length += syllable_length;
    }

    /*
     * Keep track of the times we have tried to get syllables.  If we
     * have exceeded the threshold, reinitialize the pwlen and
     * word_size variables, clear out the word arrays, and start from
     * scratch.
     */
    tries++;
    if (tries > MAX_RETRIES) {
      word_length = 0;
      word_size = 0;
      tries = 0;
      strcpy (word, "");
      strcpy (hyphenated_word, "");
    }
  }

  /*
   * The units arrays and syllable storage are internal to this
   * routine.  Since the caller has no need for them, we release the
   * space.
   */
  free((char *)new_syllable);
  free((char *)syllable_units);
  free((char *)word_units);

  return ((int)word_length);
}



/*
 * Check that the word does not contain illegal combinations that may
 * span syllables.  Specifically, these are:
 *   1. An illegal pair of units between syllables.
 *   2. Three consecutive vowel units.
 *   3. Three consecutive consonant units.
 * The checks are made against units (1 or 2 letters), not against the
 * individual letters, so three consecutive units can have the length
 * of 6 at most.
 */
static boolean improper_word (unsigned short *units,
                              unsigned short word_size)
{
  unsigned short unit_count;
  boolean failure;

  failure = FALSE;

  for (unit_count = 0; !failure && (unit_count < word_size); unit_count++) {
    /*
     * Check for ILLEGAL_PAIR.  This should have been caught for units
     * within a syllable, but in some cases it would have gone
     * unnoticed for units between syllables (e.g., when saved_unit's
     * in get_syllable() were not used).
     */
    if ((unit_count != 0) &&
        (digram[units[unit_count - 1]][units[unit_count]] &
         ILLEGAL_PAIR)) {
      failure = TRUE;
    }

    /*
     * Check for consecutive vowels or consonants.  Because the
     * initial y of a syllable is treated as a consonant rather than
     * as a vowel, we exclude y from the first vowel in the vowel
     * test.  The only problem comes when y ends a syllable and two
     * other vowels start the next, like fly-oint.  Since such words
     * are still pronounceable, we accept this.
     */
    if (!failure && (unit_count >= 2)) {
      /*
       * Vowel check.
       */
      if ((((rules[units[unit_count - 2]].flags & VOWEL) &&
            !(rules[units[unit_count - 2]].flags &
              ALTERNATE_VOWEL)) &&
           (rules[units[unit_count - 1]].flags & VOWEL) &&
           (rules[units[unit_count]].flags & VOWEL)) ||
          /*
           * Consonant check.
           */
          (!(rules[units[unit_count - 2]].flags & VOWEL) &&
           !(rules[units[unit_count - 1]].flags & VOWEL) &&
           !(rules[units[unit_count]].flags & VOWEL))) {
        failure = TRUE;
      }
    }
  }

  return (failure);
}


/*
 * Treating y as a vowel is sometimes a problem.  Some words get
 * formed that look irregular.  One special group is when y starts a
 * word and is the only vowel in the first syllable.  The word ycl is
 * one example.  We discard words like these.
 */
static boolean have_initial_y (unsigned short *units,
                               unsigned short unit_size)
{
  unsigned short unit_count;
  unsigned short vowel_count;
  unsigned short normal_vowel_count;

  vowel_count = 0;
  normal_vowel_count = 0;

  for (unit_count = 0; unit_count <= unit_size; unit_count++) {
    /*
     * Count vowels.
     */
    if (rules[units[unit_count]].flags & VOWEL) {
      vowel_count++;

      /*
       * Count the vowels that are not: 1. y, 2. at the start of the
       * word.
       */
      if (!(rules[units[unit_count]].flags & ALTERNATE_VOWEL)
          ||
          (unit_count != 0)) {
        normal_vowel_count++;
      }
    }
  }

  return ((vowel_count <= 1) && (normal_vowel_count == 0));
}


/*
 * Besides the problem with the letter y, there is one with a silent e
 * at the end of words, like face or nice.  We allow this silent e,
 * but we do not allow it as the only vowel at the end of the word or
 * syllables like ble will be generated.
 */
static boolean have_final_split (unsigned short *units,
                                 unsigned short unit_size)
{
  unsigned short unit_count;
  unsigned short vowel_count;

  vowel_count = 0;

  /*
   *    Count all the vowels in the word.
   */
  for (unit_count = 0; unit_count <= unit_size; unit_count++) {
    if (rules[units[unit_count]].flags & VOWEL) {
      vowel_count++;
    }
  }

  /*
   * Return TRUE iff the only vowel was e, found at the end if the
   * word.
   */
  return ((vowel_count == 1) &&
          (rules[units[unit_size]].flags & NO_FINAL_SPLIT));
}


/*
 * Generate next unit to password, making sure that it follows
 * these rules:
 *   1. Each syllable must contain exactly 1 or 2 consecutive
 *      vowels, where y is considered a vowel.
 *   2. Syllable end is determined as follows:
 *        a. Vowel is generated and previous unit is a
 *           consonant and syllable already has a vowel.  In
 *           this case, new syllable is started and already
 *           contains a vowel.
 *        b. A pair determined to be a "break" pair is encountered.
 *           In this case new syllable is started with second unit
 *           of this pair.
 *        c. End of password is encountered.
 *        d. "begin" pair is encountered legally.  New syllable is
 *           started with this pair.
 *        e. "end" pair is legally encountered.  New syllable has
 *           nothing yet.
 *   3. Try generating another unit if:
 *        a. third consecutive vowel and not y.
 *        b. "break" pair generated but no vowel yet in current
 *           or previous 2 units are "not_end".
 *        c. "begin" pair generated but no vowel in syllable
 *           preceding begin pair, or both previous 2 pairs are
 *          designated "not_end".
 *        d. "end" pair generated but no vowel in current syllable
 *           or in "end" pair.
 *        e. "not_begin" pair generated but new syllable must
 *           begin (because previous syllable ended as defined in
 *           2 above).
 *        f. vowel is generated and 2a is satisfied, but no syllable
 *           break is possible in previous 3 pairs.
 *        g. Second and third units of syllable must begin, and
 *           first unit is "alternate_vowel".
 */
static char *get_syllable (char *syllable,
                           unsigned short pwlen,
                           unsigned short *units_in_syllable,
                           unsigned short *syllable_size)
{
  unsigned short unit;
  short current_unit;
  unsigned short vowel_count;
  boolean rule_broken;
  boolean want_vowel;
  boolean want_another_unit;
  unsigned int tries;
  unsigned short last_unit;
  short length_left;
  unsigned short hold_saved_unit;
  static unsigned short saved_unit;
  static unsigned short saved_pair[2];

  /*
   * This is needed if the saved_unit is tries and the syllable then
   * discarded because of the retry limit. Since the saved_unit is OK
   * and fits in nicely with the preceding syllable, we will always
   * use it.
  */
  hold_saved_unit = saved_unit;

  /*
   * Loop until valid syllable is found.
   */
  do {
    /*
     * Try for a new syllable.  Initialize all pertinent syllable
     * variables.
     */
    tries = 0;
    saved_unit = hold_saved_unit;
    strcpy(syllable, "");
    *syllable_size = 0;
    vowel_count = 0;
    current_unit = 0;
    length_left = (short) pwlen;
    want_another_unit = TRUE;

    /*
     * This loop finds all the units for the syllable.
     */
    do {
      want_vowel = FALSE;

      /*
       * This loop continues until a valid unit is found for the
       * current position within the syllable.
       */
      do {
        /*
         * If there are saved_unit's from the previous syllable, use
         * them up first.
         */
        if (saved_unit != 0) {
          /*
           * If there were two saved units, the first is guaranteed
           * (by checks performed in the previous syllable) to be
           * valid.  We ignore the checks and place it in this
           * syllable manually.
           */
          if (saved_unit == 2) {
            units_in_syllable[0] = saved_pair[1];

            if (rules[saved_pair[1]].flags & VOWEL) {
              vowel_count++;
            }

            current_unit++;

            strcpy(syllable, rules[saved_pair[1]].unit_code);
            length_left -= strlen(syllable);
          }

          /*
           * The unit becomes the last unit checked in the previous
           * syllable.
           */
          unit = saved_pair[0];

          /*
           * The saved units have been used.  Do not try to reuse them
           * in this syllable (unless this particular syllable is
           * rejected at which point we start to rebuild it with these
           * same saved units.
           */
          saved_unit = 0;
        } else {
          /*
           * If we don't have to scoff the saved units, we generate a
           * random one.  If we know it has to be a vowel, we get one
           * rather than looping through until one shows up.
           */
          if (want_vowel) {
            unit = random_unit(VOWEL);
          } else {
            unit = random_unit(NO_SPECIAL_RULE);
          }
        }

        length_left -= (short) strlen(rules[unit].unit_code);

        /*
         * Prevent having a word longer than expected.
         */
        if (length_left < 0) {
          rule_broken = TRUE;
        } else {
          rule_broken = FALSE;
        }

        /*
         * First unit of syllable.  This is special because the digram
         * tests require 2 units and we don't have that yet.
         * Nevertheless, we can perform some checks.
         */
        if (current_unit == 0) {
          /*
           * If the shouldn't begin a syllable, don't use it.
           */
          if (rules[unit].flags & NOT_BEGIN_SYLLABLE) {
            rule_broken = TRUE;
          } else if (length_left == 0) {
            /*
             * If this is the last unit of a word, we have a one unit
             * syllable.  Since each syllable must have a vowel, we
             * make sure the unit is a vowel.  Otherwise, we discard
             * it.
             */
            if (rules[unit].flags & VOWEL) {
              want_another_unit = FALSE;
            } else {
              rule_broken = TRUE;
            }
          }
        } else {
          /*
           * We are not at the start of a syllable.  Save the previous
           * unit for later tests.
           */
          last_unit = units_in_syllable[current_unit - 1];

          /*
           * There are some digram tests that are universally true.
           * We test them out.
           */

          /*
           * Reject ILLEGAL_PAIRS of units.
           */
          if ((ALLOWED (ILLEGAL_PAIR)) ||

              /*
               * Reject units that will be split between syllables
               * when the syllable has no vowels in it.
               */
              (ALLOWED (BREAK) && (vowel_count == 0)) ||

              /*
               * Reject a unit that will end a syllable when no
               * previous unit was a vowel and neither is this one.
               */
              (ALLOWED (END) && (vowel_count == 0) &&
               !(rules[unit].flags & VOWEL))) {
            rule_broken = TRUE;
          }

          if (current_unit == 1) {
            /* Set last_unit to be 0, which is "a", which is a vowel,
               which is a hack.  This is what was happening anyway
               when it was never set in this case.  --sah */
            last_unit = 0;

            /*
             * Reject the unit if we are at te starting digram of a
             * syllable and it does not fit.
             */
            if (ALLOWED (NOT_BEGIN)) {
              rule_broken = TRUE;
            }
          } else {
            /*
             * Do not allow syllables where the first letter is y and
             * the next pair can begin a syllable.  This may lead to
             * splits where y is left alone in a syllable.  Also, the
             * combination does not sound to good even if not split.
             */
            if (((current_unit == 2) &&
                 (ALLOWED (BEGIN)) &&
                 (rules[units_in_syllable[0]].flags &
                  ALTERNATE_VOWEL)) ||

                /*
                 * If this is the last unit of a word, we should
                 * reject any digram that cannot end a syllable.
                 */
                (ALLOWED (NOT_END) &&
                 (length_left == 0)) ||

                /*
                 * Reject the unit if the digram it forms wants to
                 * break the syllable, but the resulting digram that
                 * would end the syllable is not allowed to end a
                 * syllable.
                 */
                (ALLOWED (BREAK) &&
                 (digram[units_in_syllable
                         [current_unit - 2]]
                  [last_unit] &
                  NOT_END)) ||

                /*
                 * Reject the unit if the digram it forms expects a
                 * vowel preceding it and there is none.
                 */
                (ALLOWED (PREFIX) &&
                 !(rules[units_in_syllable
                         [current_unit - 2]].flags &
                   VOWEL))) {
              rule_broken = TRUE;
            }

            /*
             * The following checks occur when the current unit is a
             * vowel and we are not looking at a word ending with an
             * e.
             */
            if (!rule_broken &&
                (rules[unit].flags & VOWEL) &&
                ((length_left > 0) ||
                 !(rules[last_unit].flags &
                   NO_FINAL_SPLIT))) {

              /*
               * Don't allow 3 consecutive vowels in a syllable.
               * Although some words formed like this are OK, like
               * beau, most are not.
               */
              if ((vowel_count > 1) &&
                  (rules[last_unit].flags & VOWEL)) {
                rule_broken = TRUE;
              } else if ((vowel_count != 0) &&
                         !(rules[last_unit].flags & VOWEL)) {
                /*
                 * Check for the case of vowels-consonants-vowel,
                 * which is only legal if the last vowel is an e and
                 * we are the end of the word (wich is not happening
                 * here due to a previous check.
                 */

                /*
                 * Try to save the vowel for the next syllable, but if
                 * the syllable left here is not proper (i.e., the
                 * resulting last digram cannot legally end it), just
                 * discard it and try for another.
                 */
                if (digram[units_in_syllable
                           [current_unit - 2]]
                    [last_unit] &
                    NOT_END) {
                  rule_broken = TRUE;
                } else {
                  saved_unit = 1;
                  saved_pair[0] = unit;
                  want_another_unit = FALSE;
                }
              }
            }
          }

          /*
           * The unit picked and the digram formed are legal.  We now
           * determine if we can end the syllable.  It may, in some
           * cases, mean the last unit(s) may be deferred to the next
           * syllable.  We also check here to see if the digram formed
           * expects a vowel to follow.
           */
          if (!rule_broken && want_another_unit) {
            /*
             * This word ends in a silent e.
             */
            if (((vowel_count != 0) &&
                 (rules[unit].flags & NO_FINAL_SPLIT) &&
                 (length_left == 0) &&
                 !(rules[last_unit].flags &
                   VOWEL)) ||

                /*
                 * This syllable ends either because the digram is an
                 * END pair or we would otherwise exceed the length of
                 * the word.
                 */
                (ALLOWED (END) || (length_left == 0))) {
              want_another_unit = FALSE;
            } else if ((vowel_count != 0) && (length_left > 0)) {
              /*
               * Since we have a vowel in the syllable already, if the
               * digram calls for the end of the syllable, we can
               * legally split it off. We also make sure that we are
               * not at the end of the dangerous because that syllable
               * may not have vowels, or it may not be a legal
               * syllable end, and the retrying mechanism will loop
               * infinitely with the same digram.
               */

              /*
               * If we must begin a syllable, we do so if the only
               * vowel in THIS syllable is not part of the digram we
               * are pushing to the next syllable.
               */
              if (ALLOWED (BEGIN) &&
                  (current_unit > 1) &&
                  !((vowel_count == 1) &&
                    (rules[last_unit].flags &
                     VOWEL))) {
                saved_unit = 2;
                saved_pair[0] = unit;
                saved_pair[1] = last_unit;
                want_another_unit = FALSE;
              } else if (ALLOWED (BREAK)) {
                saved_unit = 1;
                saved_pair[0] = unit;
                want_another_unit = FALSE;
              }
            } else if (ALLOWED (SUFFIX)) {
              want_vowel = TRUE;
            }
          }
        }

        tries++;

        /*
         * If this unit was illegal, redetermine the amount of letters
         * left to go in the word.
         */
        if (rule_broken) {
          length_left += (short) strlen (rules[unit].unit_code);
        }
      } while (rule_broken && (tries <= MAX_RETRIES));

      /*
       * The unit fit OK.
       */
      if (tries <= MAX_RETRIES) {
        /*
         * If the unit were a vowel, count it in.  However, if the
         * unit were a y and appear at the start of the syllable,
         * treat it like a constant (so that words like year can
         * appear and not conflict with the 3 consecutive vowel rule.
         */
        if ((rules[unit].flags & VOWEL) &&
            ((current_unit > 0) ||
             !(rules[unit].flags & ALTERNATE_VOWEL))) {
          vowel_count++;
        }

        /*
         * If a unit or units were to be saved, we must adjust the
         * syllable formed.  Otherwise, we append the current unit to
         * the syllable.
         */
        switch (saved_unit) {
          case 0:
            units_in_syllable[current_unit] = unit;
            strcat(syllable, rules[unit].unit_code);
            break;
          case 1:
            current_unit--;
            break;
          case 2:
            strcpy(&syllable[strlen (syllable) -
                             strlen (rules[last_unit].
                                     unit_code)],
                   "");
            length_left += (short) strlen(rules[last_unit].unit_code);
            current_unit -= 2;
            break;
        }
      } else {
        /*
         * Whoops!  Too many tries.  We set rule_broken so we can loop
         * in the outer loop and try another syllable.
         */
        rule_broken = TRUE;

        /*
         * ...and the syllable length grows.
         */
        *syllable_size = current_unit;
      }

      current_unit++;
    } while ((tries <= MAX_RETRIES) && want_another_unit);
  } while (rule_broken ||
           illegal_placement (units_in_syllable, *syllable_size));

  return (syllable);
}


/*
 * This routine goes through an individual syllable and checks for
 * illegal combinations of letters that go beyond looking at digrams.
 * We look at things like 3 consecutive vowels or consonants, or
 * syllables with consonants between vowels (unless one of them is the
 * final silent e).
 */
static boolean illegal_placement (unsigned short *units,
                                  unsigned short pwlen)
{
  unsigned short vowel_count;
  unsigned short unit_count;
  boolean failure;

  vowel_count = 0;
  failure = FALSE;

  for (unit_count = 0; !failure && (unit_count <= pwlen); unit_count++) {
    if (unit_count >= 1) {
      /*
       * Don't allow vowels to be split with consonants in a single
       * syllable.  If we find such a combination (except for the
       * silent e) we have to discard the syllable).
       */
      if ((!(rules[units[unit_count - 1]].flags & VOWEL) &&
           (rules[units[unit_count]].flags & VOWEL) &&
           !((rules[units[unit_count]].flags &
              NO_FINAL_SPLIT) &&
             (unit_count == pwlen)) &&
           (vowel_count != 0)) ||

          /*
           * Perform these checks when we have at least 3 units.
           */
          ((unit_count >= 2) &&

           /*
            * Disallow 3 consecutive consonants.
            */
           ((!(rules[units[unit_count - 2]].flags & VOWEL) &&
             !(rules[units[unit_count - 1]].flags &
               VOWEL) &&
             !(rules[units[unit_count]].flags &
               VOWEL)) ||

            /*
             * Disallow 3 consecutive vowels, where the first is not a
             * y.
             */
            (((rules[units[unit_count - 2]].flags &
               VOWEL) &&
              !((rules[units[0]].flags &
                 ALTERNATE_VOWEL) &&
                (unit_count == 2))) &&
             (rules[units[unit_count - 1]].flags &
              VOWEL) &&
             (rules[units[unit_count]].flags &
              VOWEL)))))
        failure = TRUE;
    }

    /*
     * Count the vowels in the syllable.  As mentioned somewhere
     * above, exclude the initial y of a syllable.  Instead, treat it
     * as a consonant.
     */
    if ((rules[units[unit_count]].flags & VOWEL) &&
        !((rules[units[0]].flags & ALTERNATE_VOWEL) &&
          (unit_count == 0) && (pwlen != 0)))
      vowel_count++;
  }

  return (failure);
}



/*
 * This is the standard random unit generating routine for
 * get_syllable().  It does not reference the digrams, but assumes
 * that it contains 34 units in a particular order.  This routine
 * attempts to return unit indexes with a distribution approaching
 * that of the distribution of the 34 units in English.  In order to
 * do this, a random number (supposedly uniformly distributed) is used
 * to do a table lookup into an array containing unit indices.  There
 * are 211 entries in the array for the random_unit entry point.  The
 * probability of a particular unit being generated is equal to the
 * fraction of those 211 entries that contain that unit index.  For
 * example, the letter `a' is unit number 1.  Since unit index 1
 * appears 10 times in the array, the probability of selecting an `a'
 * is 10/211.
 *
 * Changes may be made to the digram table without affect to this
 * procedure providing the letter-to-number correspondence of the
 * units does not change.  Likewise, the distribution of the 34 units
 * may be altered (and the array size may be changed) in this
 * procedure without affecting the digram table or any other programs
 * using the random_word subroutine.
 */
static unsigned short numbers[] = {
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  1, 1, 1, 1, 1, 1, 1, 1,
  2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
  3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
  4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
  5, 5, 5, 5, 5, 5, 5, 5,
  6, 6, 6, 6, 6, 6, 6, 6,
  7, 7, 7, 7, 7, 7,
  8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
  9, 9, 9, 9, 9, 9, 9, 9,
  10, 10, 10, 10, 10, 10, 10, 10,
  11, 11, 11, 11, 11, 11,
  12, 12, 12, 12, 12, 12,
  13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
  14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
  15, 15, 15, 15, 15, 15,
  16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
  17, 17, 17, 17, 17, 17, 17, 17,
  18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
  19, 19, 19, 19, 19, 19,
  20, 20, 20, 20, 20, 20, 20, 20,
  21, 21, 21, 21, 21, 21, 21, 21,
  22,
  23, 23, 23, 23, 23, 23, 23, 23,
  24,
  25,
  26,
  27,
  28,
  29, 29,
  30,
  31,
  32,
  33
};


/*
 * This structure has a typical English frequency of vowels.  The
 * value of an entry is the vowel position (a=0, e=4, i=8, o=14, u=19,
 * y=23) in the rules array.  The number of times the value appears is
 * the frequency.  Thus, the letter "a" is assumed to appear 2/12 =
 * 1/6 of the time.  This array may be altered if better data is
 * obtained.  The routines that use vowel_numbers will adjust to the
 * size difference automatically.
*/
static unsigned short vowel_numbers[] = {
  0, 0, 4, 4, 4, 8, 8, 14, 14, 19, 19, 23
};


/*
 * Select a unit (a letter or a consonant group).  If a vowel is
 * expected, use the vowel_numbers array rather than looping through
 * the numbers array until a vowel is found.
 */
static unsigned short random_unit (unsigned short type)
{
  unsigned short number;

  /*
   * Sometimes, we are asked to explicitly get a vowel (i.e., if a
   * digram pair expects one following it).  This is a shortcut to do
   * that and avoid looping with rejected consonants.
   */
  if (type & VOWEL) {
    number = vowel_numbers[get_random(0, (sizeof (vowel_numbers) /
                                          sizeof (unsigned short)))];
  } else {
    /*
     * Get any letter according to the English distribution.
     */
    number = numbers[get_random(0, (sizeof (numbers) /
                                    sizeof (unsigned short)))];
  }

  return (number);
}


/*
 * Return a random number between minlen and maxlen inclusive.
 */
static unsigned short get_random (unsigned short minlen,
                                  unsigned short maxlen)
{
  return minlen + (unsigned short) randint((int) (maxlen - minlen + 1));
}


/*
 * Produces a random number from 0 to n-1 .
 */
static unsigned int randint(int n)
{
  return (rand() % n);
}


/*
 * Set the seed.  This routine will only set the seed once, even if
 * called from multiple sources.
 */
static void set_seed(long seed)
{
  int been_here_before = 0;

  if (!been_here_before)  {
    been_here_before = 1;
    srand(seed);
  }
}

/*
 * answer takes the array out and creates variable sum by adding
 * certain values within the array together.  To get a number from 0
 * to n-1, it returns sum mod n (sum%n)
 */
int answer (unsigned char *out, int n)
{
  unsigned int sum;
  /*
   * every time this function is called, it adds the first three
   * positions of out to get sum.
   */

  sum = out[0] + out[1] +out[2];
  return (sum % n);
}
