#help_index "AutoComplete/Dictionary"
public U8 *ACDDefGet(U8 *st, I64 def_num=1)
{//MAlloc str holding single dict definition of word.
    CFile        *f;
    CHashGeneric *tmph;
    U8           *res = NULL, *buf, *in_ptr, *st2 = MStrUtil(st, SUF_TO_UPPER);

    tmph = HashFind(st2, ac.hash_table, HTT_DICT_WORD);
    Free(st2);
    if (tmph)
    {
        if (f = FOpen(ACD_DEF_FILENAME, "r"))
        {
            buf = MAlloc(ACD_BLK_SIZE * 2 + 1);
            buf[ACD_BLK_SIZE * 2] = 0; //terminate
            FBlkRead(f, buf, tmph->user_data0 * ACD_BLK_SIZE / BLK_SIZE, ACD_BLK_SIZE * 2 / BLK_SIZE);
            FClose(f);
            in_ptr = buf;
            while (in_ptr < buf + ACD_BLK_SIZE * 2)
            {
                while (*in_ptr != ACD_WORD_CHAR && in_ptr < buf + ACD_BLK_SIZE * 2)
                    in_ptr++;
                if (*in_ptr++ == ACD_WORD_CHAR)
                {
                    if (!StrICompare(st, in_ptr))
                    {
                        while (def_num && *in_ptr != ACD_WORD_CHAR && in_ptr < buf + ACD_BLK_SIZE * 2)
                        {
                            if (*in_ptr == ACD_DEF_CHAR)
                            {
                                if (!--def_num)
                                    break;
                                else
                                    in_ptr++;
                            }
                            else
                                in_ptr++;
                        }
                        if (*in_ptr++ == ACD_DEF_CHAR)
                        {
                            res = StrNew(in_ptr);
                            break;
                        }
                    }
                }
            }
            Free(buf);
        }
    }
    return res;
}

public U8 *ACDDefsGet(U8 *st)
{//MAlloc str with all dict definitions of word.
    CFile        *f;
    CHashGeneric *tmph;
    U8           *res = NULL, *buf, *in_ptr, *in_ptr2, *st2 = MStrUtil(st, SUF_TO_UPPER);

    tmph = HashFind(st2, ac.hash_table, HTT_DICT_WORD);
    Free(st2);

    if (tmph)
    {
        if (f = FOpen(ACD_DEF_FILENAME, "r"))
        {
            buf = MAlloc(ACD_BLK_SIZE * 2 + 1);
            buf[ACD_BLK_SIZE * 2] = 0; //terminate
            FBlkRead(f, buf, tmph->user_data0 * ACD_BLK_SIZE / BLK_SIZE, ACD_BLK_SIZE * 2 / BLK_SIZE);
            FClose(f);
            in_ptr = buf;
            while (in_ptr < buf + ACD_BLK_SIZE * 2)
            {
                while (*in_ptr != ACD_WORD_CHAR && in_ptr < buf + ACD_BLK_SIZE * 2)
                    in_ptr++;
                if (*in_ptr++ == ACD_WORD_CHAR)
                {
                    if (!StrICompare(st, in_ptr))
                    {
                        in_ptr2 = in_ptr;
                        in_ptr--;
                        while (*in_ptr2 != ACD_WORD_CHAR && in_ptr2 < buf + ACD_BLK_SIZE * 2)
                        {
                            in_ptr2++;
                        }
                        res = MAlloc(in_ptr2 + 1 - in_ptr);
                        MemCopy(res, in_ptr, in_ptr2 - in_ptr);
                        res[in_ptr2 - in_ptr] = ACD_END_CHAR;
                        break;
                    }
                }
            }
            Free(buf);
        }
    }
    return res;
}

/*Format of word list entry:
    U8 ACD_WORD_CHAR
    U8 word[] with terminating zero
    I16 block;
*/
public U8 *ACDWordPtAt(U8 *st)
{//Point to word in word list.
    I64 i;
    U8 *start = acd.word_list, *r = start, *end = acd.word_list + acd.word_list_size;

    if (!st || !*st)
        return acd.word_list;
    if (acd.word_list_size)
    {
        while (start + 3 < end)
        {
            r = (start + end) >> 1;
            while (TRUE)
            {
                while (*r != ACD_WORD_CHAR && r > acd.word_list)
                    r--;
                if ((r[2] == ACD_WORD_CHAR || r[1] == ACD_WORD_CHAR)&& r - 3 > acd.word_list)
                    r--;
                else
                    break;
            }
            if (*r == ACD_WORD_CHAR)
            {
                i = StrICompare(st, r + 1);
                if (i < 0)
                    end = r - 1;
                else if (i > 0)
                    start = r + StrLen(r) + 3;
                else
                    return r;
            }
            else
                break;
        }
        r = (start + end) >> 1;
        while (TRUE)
        {
            while (*r != ACD_WORD_CHAR && r > acd.word_list)
                r--;
            if ((r[2] == ACD_WORD_CHAR || r[1] == ACD_WORD_CHAR) && r - 3 > acd.word_list)
                r--;
            else
                break;
        }
        if (*r == ACD_WORD_CHAR && StrICompare(st, r + 1) > 0)
            r += StrLen(r) + 3;
    }
    if (*r == ACD_WORD_CHAR)
        return r;
    else
        return acd.word_list;
}

U0 ACDFillin(I64 n)
{
    U8 *s;
    I64 len;

    if (0 <= n < acd.num_fillins)
    {
        s = acd.fillins[n] + 1;
        len = StrLen(s);
        if (len > ac.partial_len)
            In(s + ac.partial_len);
    }
}

public U0 ACDDefsPut(CDoc *doc=NULL, U8 *st, I64 num=-1)
{//Put to doc a dictionary definition(s) of a word.
    U8 *st2, *st3;
    I64 ch, i = 0;

    if (!st)
        return;
    if (*st == ACD_WORD_CHAR)
        st++;
    DocPrint(doc, "$WW,1$$RED$%s:$FG$\n\n", st);
    if (num < 0)
    {
        if (st3 = ACDDefsGet(st))
        {
            st2 = st3;
            while (ch = *st2++)
            {
                switch (ch)
                {
                    case ACD_WORD_CHAR:
                        break;

                    case ACD_DEF_CHAR:
                        DocPrint(doc, "$GREEN$(%d)$FG$ %s\n", ++i, st2);
                        break;

                    case ACD_PRONUNCIATION_CHAR:
                        DocPrint(doc, "$LTGREEN$%s$FG$\n", st2);
                        break;

                    case ACD_POS_CHAR:
                        DocPrint(doc, "$BLACK$%s$FG$\n", st2);
                        break;

                    case ACD_EXTRA_CHAR:
                        DocPrint(doc, "$LTBLUE$%s$FG$\n", st2);
                        break;
                }
                st2 += StrLen(st2) + 1;
            }
            Free(st3);
        }
    }
    else
    {
        while (st2 = ACDDefGet(st, ++i))
        {
            if (i == num)
                DocPrint(doc, "$GREEN$(%d)$FG$ %s\n", i, st2);
            Free(st2);
        }
    }
}

U0 ACDPopUpDef(U8 *st, I64 num=-1, CTask *parent=NULL)
{
    U8 *buf;

    buf = MStrPrint("ACDDefsPut(DocPut,\"%s\",%d);View;", st, num);
    PopUp(buf, parent);
    Free(buf);
}

U0 ACDDef(I64 n, CTask *parent=NULL)
{
    if (0 <= n < acd.num_fillins)
        ACDPopUpDef(acd.fillins[n], -1, parent);
}

#help_index "AutoComplete"
U0 ACFillIn(I64 n)
{
    U8 *s;

    if (0 <= --n < ac.num_fillins)
    {
        s = ac.fillin_matches[n]->str;
        if (StrLen(s) > ac.partial_len)
            In(s + ac.partial_len);
    }
}

U0 ACMan(I64 n, CTask *parent_task=NULL)
{
    CHashAC *tmpw;
    CHashSrcSym *tmph;

    if (0 <= --n < ac.num_fillins &&
            (tmpw = ac.fillin_matches[n]) && (tmph = HashFind(tmpw->str, Fs->hash_table, HTG_SRC_SYM)) && tmph->src_link)
        PopUpEd(tmph->src_link, parent_task);
}