/*************************************************************************/
/*                                                                       */
/*                  Language Technologies Institute                      */
/*                     Carnegie Mellon University                        */
/*                         Copyright (c) 2007                            */
/*                        All Rights Reserved.                           */
/*                                                                       */
/*  Permission is hereby granted, free of charge, to use and distribute  */
/*  this software and its documentation without restriction, including   */
/*  without limitation the rights to use, copy, modify, merge, publish,  */
/*  distribute, sublicense, and/or sell copies of this work, and to      */
/*  permit persons to whom this work is furnished to do so, subject to   */
/*  the following conditions:                                            */
/*   1. The code must retain the above copyright notice, this list of    */
/*      conditions and the following disclaimer.                         */
/*   2. Any modifications must be clearly marked as such.                */
/*   3. Original authors' names are not deleted.                         */
/*   4. The authors' names are not used to endorse or promote products   */
/*      derived from this software without specific prior written        */
/*      permission.                                                      */
/*                                                                       */
/*  CARNEGIE MELLON UNIVERSITY AND THE CONTRIBUTORS TO THIS WORK         */
/*  DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING      */
/*  ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT   */
/*  SHALL CARNEGIE MELLON UNIVERSITY NOR THE CONTRIBUTORS BE LIABLE      */
/*  FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES    */
/*  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN   */
/*  AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,          */
/*  ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF       */
/*  THIS SOFTWARE.                                                       */
/*                                                                       */
/*************************************************************************/
/*             Author:  Alan W Black (awb@cs.cmu.edu)                    */
/*               Date:  November 2007                                    */
/*************************************************************************/
/*                                                                       */
/*  Some language independent features                                   */
/*                                                                       */

#include "cst_hrg.h"
#include "cst_phoneset.h"
#include "cst_regex.h"

static const cst_val *word_break(const cst_item *word);
static const cst_val *word_punc(const cst_item *word);
static const cst_val *word_numsyls(const cst_item *word);
static const cst_val *ssyl_in(const cst_item *syl);
static const cst_val *syl_in(const cst_item *syl);
static const cst_val *syl_out(const cst_item *syl);
static const cst_val *syl_break(const cst_item *syl);
static const cst_val *syl_codasize(const cst_item *syl);
static const cst_val *syl_onsetsize(const cst_item *syl);
static const cst_val *accented(const cst_item *p);

DEF_STATIC_CONST_VAL_STRING(val_string_onset,"onset");
DEF_STATIC_CONST_VAL_STRING(val_string_coda,"coda");
DEF_STATIC_CONST_VAL_STRING(val_string_initial,"initial");
DEF_STATIC_CONST_VAL_STRING(val_string_single,"single");
DEF_STATIC_CONST_VAL_STRING(val_string_final,"final");
DEF_STATIC_CONST_VAL_STRING(val_string_mid,"mid");
DEF_STATIC_CONST_VAL_STRING(val_string_empty,"");

const cst_val *ph_vc(const cst_item *p)
{
    return phone_feature(item_phoneset(p),item_name(p),"vc");
}
const cst_val *ph_vlng(const cst_item *p)
{
    return phone_feature(item_phoneset(p),item_name(p),"vlng");
}
const cst_val *ph_vheight(const cst_item *p)
{
   return phone_feature(item_phoneset(p),item_name(p),"vheight");
}
const cst_val *ph_vrnd(const cst_item *p)
{
    return phone_feature(item_phoneset(p),item_name(p),"vrnd");
}
const cst_val *ph_vfront(const cst_item *p)
{
    return phone_feature(item_phoneset(p),item_name(p),"vfront");
}
const cst_val *ph_ctype(const cst_item *p)
{
    return phone_feature(item_phoneset(p),item_name(p),"ctype");
}
const cst_val *ph_cplace(const cst_item *p)
{
    return phone_feature(item_phoneset(p),item_name(p),"cplace");
}
const cst_val *ph_cvox(const cst_item *p)
{
    return phone_feature(item_phoneset(p),item_name(p),"cvox");
}

const cst_val *cg_duration(const cst_item *p)
{
    /* Note this constructs float vals, these will be freed when the */
    /* cart cache is freed, so this should only be used in carts     */
    if (!p)
        return float_val(0.0);
    else if (!item_prev(p))
        return item_feat(p,"end");
    else
        return float_val(item_feat_float(p,"end")
			 - item_feat_float(item_prev(p),"end"));
}

DEF_STATIC_CONST_VAL_STRING(val_string_pos_b,"b");
DEF_STATIC_CONST_VAL_STRING(val_string_pos_m,"m");
DEF_STATIC_CONST_VAL_STRING(val_string_pos_e,"e");

const cst_val *cg_state_pos(const cst_item *p)
{
    const char *name;
    name = item_feat_string(p,"name");
    if (!cst_streq(name,ffeature_string(p,"p.name")))
        return (cst_val *)&val_string_pos_b;
    if (cst_streq(name,ffeature_string(p,"n.name")))
        return (cst_val *)&val_string_pos_m;
    else
        return (cst_val *)&val_string_pos_e;
}

const cst_val *cg_state_place(const cst_item *p)
{
    float start, end;
    int this;
    start = (float)ffeature_int(p,"R:mcep_link.parent.daughter1.frame_number");
    end = (float)ffeature_int(p,"R:mcep_link.parent.daughtern.frame_number");
    this = item_feat_int(p,"frame_number");
    if ((end-start) == 0.0)
        return float_val(0.0);
    else
        return float_val((this-start)/(end-start));
}

const cst_val *cg_state_index(const cst_item *p)
{
    float start;
    int this;
    start = (float)ffeature_int(p,"R:mcep_link.parent.daughter1.frame_number");
    this = item_feat_int(p,"frame_number");
    return float_val(this-start);
}

const cst_val *cg_state_rindex(const cst_item *p)
{
    float end;
    int this;
    end = (float)ffeature_int(p,"R:mcep_link.parent.daughtern.frame_number");
    this = item_feat_int(p,"frame_number");
    return float_val(end-this);
}

const cst_val *cg_phone_place(const cst_item *p)
{
    float start, end;
    int this;
    start = (float)ffeature_int(p,"R:mcep_link.parent.R:segstate.parent.daughter1.R:mcep_link.daughter1.frame_number");
    end = (float)ffeature_int(p,"R:mcep_link.parent.R:segstate.parent.daughtern.R:mcep_link.daughtern.frame_number");
    this = item_feat_int(p,"frame_number");
    if ((end-start) == 0.0)
        return float_val(0.0);
    else
        return float_val((this-start)/(end-start));
}

const cst_val *cg_phone_index(const cst_item *p)
{
    float start;
    int this;
    start = (float)ffeature_int(p,"R:mcep_link.parent.R:segstate.parent.daughter1.R:mcep_link.daughter1.frame_number");
    this = item_feat_int(p,"frame_number");
    return float_val(this-start);
}

const cst_val *cg_phone_rindex(const cst_item *p)
{
    float end;
    int this;
    end = (float)ffeature_int(p,"R:mcep_link.parent.R:segstate.parent.daughtern.R:mcep_link.daughtern.frame_number");
    this = item_feat_int(p,"frame_number");
    return float_val(end-this);
}

const cst_val *cg_is_pau(const cst_item *p)
{
    if (p && cst_streq("pau",item_feat_string(p,"name")))
        return &val_int_1;
    else
        return &val_int_0;
}

const cst_val *cg_find_phrase_number(const cst_item *p)
{
    const cst_item *v;
    int x = 0;

    for (v=item_prev(p); v; v=item_prev(v))
        x++;

    return val_int_n(x);
}

const cst_val *cg_position_in_phrasep(const cst_item *p)
{
    float pstart, pend, phrasenumber;
    float x;
    #define CG_FRAME_SHIFT 0.005 

    pstart = ffeature_float(p,"R:mcep_link.parent.R:segstate.parent.R:SylStructure.parent.parent.R:Phrase.parent.daughter1.R:SylStructure.daughter1.daughter1.R:Segment.p.end");
    pend = ffeature_float(p,"R:mcep_link.parent.R:segstate.parent.R:SylStructure.parent.parent.R:Phrase.parent.daughtern.R:SylStructure.daughtern.daughtern.R:Segment.end");
    phrasenumber = ffeature_float(p,"R:mcep_link.parent.R:segstate.parent.R:SylStructure.parent.parent.R:Phrase.parent.lisp_cg_find_phrase_number");
    if ((pend - pstart) == 0.0)
        return float_val(-1.0);
    else
    {
        x = phrasenumber +
            ((CG_FRAME_SHIFT*item_feat_float(p,"frame_number"))-pstart) /
            (pend - pstart);
        return float_val(x);
    }
}

/* Spam specific features, but may be useful for others */
const cst_val *pos_in_word(const cst_item *p)
{
	const cst_item *s;
	int i=0;
	p=item_as(p,"Syllable");
	s=item_as(path_to_item(p,"R:SylStructure.parent.daughter1"),"Syllable");
	for (;s && !item_equal(p,s);s=item_next(s),i++){}
	return val_string_n(i);
}

const cst_val *syllable_duration(const cst_item *p)
{
	return float_val(ffeature_float(p,"R:SylStructure.daughtern.R:Segment.end") - ffeature_float(p,"R:SylStructure.daughter1.R:Segment.p.end"));
}

const cst_val *syl_vowel(const cst_item *p)
{
	const cst_item *s,*ls;
	s=item_as(path_to_item(p,"R:SylStructure.daughter1"),"Segment");
	ls=item_as(path_to_item(p,"R:SylStructure.daughtern"),"Segment");
	for(;s && !item_equal(s,ls);s=item_next(s))
	{
            if (cst_streq("+",val_string(ph_vc(s))))
            { 
                return string_val(item_name(s));
            }
	}
	if (cst_streq("+",val_string(ph_vc(s))))
        { 
            return string_val(item_name(s));
        }
	return (cst_val *) NULL;
}

const cst_val *syl_numphones(const cst_item *p)
{
	int i;
	const cst_item *s,*ls;
	s=item_as(path_to_item(p,"R:SylStructure.daughter1"),"Segment");
	ls=item_as(path_to_item(p,"R:SylStructure.daughtern"),"Segment");
	for(i=1;s && !item_equal(s,ls);s=item_next(s)){i++;}
	return val_string_n(i);
}


const cst_val *pos_in_phrase(const cst_item *p)
{
	const cst_item *s;
	int i=0;
	p=item_as(p,"Word");
	s=item_as(path_to_item(p,"R:SylStructure.R:Phrase.parent.daughter1"),"Word");
	for (;s && !item_equal(p,s);s=item_next(s),i++){}
	return val_string_n(i);
}

const cst_val *cg_syl_ratio(const cst_item *p)
{
	return float_val (( 1 + ffeature_float(p,"syl_in"))/(1 + ffeature_float(p,"syl_in") + ffeature_float(p,"syl_out")));
}


const cst_val *cg_phrase_ratio(const cst_item *p)
{
	const cst_item *lp=p;
	while(item_next(lp)){lp=item_next(lp);}
	return float_val ((1 + ffeature_float(p,"lisp_cg_find_phrase_number"))/(1 + ffeature_float(lp,"lisp_cg_find_phrase_number")));
}

const cst_val *cg_syls_in_phrase(const cst_item *p)
{
	cst_item *s=item_as(item_daughter(p),"Word");
	return float_val(1 + ffeature_float(s,"R:SylStructure.daughter1.R:Syllable.syl_out"));
}


static const cst_val *accented(const cst_item *syl)
{
    if ((item_feat_present(syl,"accent")) ||
	(item_feat_present(syl,"endtone")))
	return VAL_STRING_1;
    else
	return VAL_STRING_0;
}

static const cst_val *seg_coda_ctype(const cst_item *seg, const char *ctype)
{
    const cst_item *s;
    const cst_phoneset *ps = item_phoneset(seg);
    
    for (s=item_last_daughter(item_parent(item_as(seg,"SylStructure")));
	 s;
	 s=item_prev(s))
    {
	if (cst_streq("+",phone_feature_string(ps,item_feat_string(s,"name"),
					       "vc")))
	    return VAL_STRING_0;
	if (cst_streq(ctype,phone_feature_string(ps,item_feat_string(s,"name"),
					       "ctype")))
	    return VAL_STRING_1;
    }

    return VAL_STRING_0;
}

static const cst_val *seg_onset_ctype(const cst_item *seg, const char *ctype)
{
    const cst_item *s;
    const cst_phoneset *ps = item_phoneset(seg);
    
    for (s=item_daughter(item_parent(item_as(seg,"SylStructure")));
	 s;
	 s=item_next(s))
    {
	if (cst_streq("+",phone_feature_string(ps,item_feat_string(s,"name"),
					       "vc")))
	    return VAL_STRING_0;
	if (cst_streq(ctype,phone_feature_string(ps,item_feat_string(s,"name"),
						 "ctype")))
	    return VAL_STRING_1;
    }

    return VAL_STRING_0;
}

static const cst_val *seg_coda_fric(const cst_item *seg)
{
    return seg_coda_ctype(seg,"f");
}

static const cst_val *seg_onset_fric(const cst_item *seg)
{
    return seg_onset_ctype(seg,"f");
}

static const cst_val *seg_coda_stop(const cst_item *seg)
{
    return seg_coda_ctype(seg,"s");
}

static const cst_val *seg_onset_stop(const cst_item *seg)
{
    return seg_onset_ctype(seg,"s");
}

static const cst_val *seg_coda_nasal(const cst_item *seg)
{
    return seg_coda_ctype(seg,"n");
}

static const cst_val *seg_onset_nasal(const cst_item *seg)
{
    return seg_onset_ctype(seg,"n");
}

static const cst_val *seg_coda_glide(const cst_item *seg)
{
    if (seg_coda_ctype(seg,"r") == VAL_STRING_0)
	    return seg_coda_ctype(seg,"l");
    return VAL_STRING_1;
}

static const cst_val *seg_onset_glide(const cst_item *seg)
{
    if (seg_onset_ctype(seg,"r") == VAL_STRING_0)
	    return seg_onset_ctype(seg,"l");
    return VAL_STRING_1;
}

static const cst_val *seg_onsetcoda(const cst_item *seg)
{
    const cst_item *s;
    const cst_phoneset *ps = item_phoneset(seg);

    if (!seg) return VAL_STRING_0;
    for (s=item_next(item_as(seg,"SylStructure"));
	 s;
	 s=item_next(s))
    {
	if (cst_streq("+",phone_feature_string(ps,item_feat_string(s,"name"),
					       "vc")))
	    return (cst_val *)&val_string_onset;
    }
    return (cst_val *)&val_string_coda;
}

static const cst_val *pos_in_syl(const cst_item *seg)
{
    const cst_item *s;
    int c;
    
    for (c=-1,s=item_as(seg,"SylStructure");
	 s;
	 s=item_prev(s),c++);

    return val_string_n(c);
}

static const cst_val *position_type(const cst_item *syl)
{
    const cst_item *s = item_as(syl,"SylStructure");

    if (s == 0)
	return (cst_val *)&val_string_single;
    else if (item_next(s) == 0)
    {
	if (item_prev(s) == 0)
	    return (cst_val *)&val_string_single;
	else
	    return (cst_val *)&val_string_final;
    }
    else if (item_prev(s) == 0)
	return (cst_val *)&val_string_initial;
    else
	return (cst_val *)&val_string_mid;
}

static const cst_val *sub_phrases(const cst_item *syl)
{
    const cst_item *s;
    int c;
    
    for (c=0,s=path_to_item(syl,"R:SylStructure.parent.R:Phrase.parent.p");
	 s && (c < CST_CONST_INT_MAX); 
	 s=item_prev(s),c++);

    return val_string_n(c);
}

static const cst_val *last_accent(const cst_item *syl)
{
    const cst_item *s;
    int c;
    
    for (c=0,s=item_as(syl,"Syllable");
	 s && (c < CST_CONST_INT_MAX); 
	 s=item_prev(s),c++)
    {
	if (val_int(accented(s)))
	    return val_string_n(c);
    }

    return val_string_n(c);
}

static const cst_val *next_accent(const cst_item *syl)
{
    const cst_item *s;
    int c;

    s=item_as(syl,"Syllable");
    if (s)
        s = item_next(s);
    else
        return val_string_n(0);
    for (c=0;
	 s && (c < CST_CONST_INT_MAX); 
	 s=item_next(s),c++)
    {
	if (val_int(accented(s)))
	    return val_string_n(c);
    }

    return val_string_n(c);
}

static const cst_val *syl_final(const cst_item *seg)
{   /* last segment in a syllable */
    const cst_item *s = item_as(seg,"SylStructure");

    if (!s || (item_next(s) == NULL))
	return VAL_STRING_1;
    else
	return VAL_STRING_0;
}

static const cst_val *word_punc(const cst_item *word)
{
    cst_item *ww;
    const cst_val *v;

    ww = item_as(word,"Token");

    if ((ww != NULL) && (item_next(ww) != 0))
	v = &val_string_empty;
    else
	v = ffeature(item_parent(ww),"punc");

/*    printf("word_punc word %s punc %s\n",
	   item_feat_string(ww,"name"),
	   val_string(v)); */

    return v;

}

static const cst_val *word_break(const cst_item *word)
{
    cst_item *ww,*pp;
    const char *pname;

    ww = item_as(word,"Phrase");

    if ((ww == NULL) || (item_next(ww) != 0))
	return VAL_STRING_1;
    else
    {
	pp = item_parent(ww);
	pname = val_string(item_feat(pp,"name"));
	if (cst_streq("BB",pname))
	    return VAL_STRING_4;
	else if (cst_streq("B",pname))
	    return VAL_STRING_3;
	else 
	    return VAL_STRING_1;
    }
}

static const cst_val *word_numsyls(const cst_item *word)
{
    cst_item *d;
    int c;
    
    for (c=0,d=item_daughter(item_as(word,"SylStructure"));
	 d;
	 d=item_next(d),c++);

    return val_int_n(c);
}

static const cst_val *ssyl_in(const cst_item *syl)
{
    /* Number of stressed syllables since last major break */
    const cst_item *ss,*p,*fs;
    int c;

    ss = item_as(syl,"Syllable");

    fs = path_to_item(syl,"R:SylStructure.parent.R:Phrase.parent.daughter.R:SylStructure.daughter");

#if 1 /* fix by uratec */
    if(item_equal(ss,fs))
      return val_string_n(0);
#else
    /* This should actually include the first syllable, but Festival's
       doesn't. */
#endif
    for (c=0, p=item_prev(ss); 
	 p && (!item_equal(p,fs)) && (c < CST_CONST_INT_MAX);
	 p=item_prev(p))
    {
	if (cst_streq("1",ffeature_string(p,"stress")))
	    c++;
    }
    
    return val_string_n(c);  /* its used randomly as int and float */
}

static const cst_val *ssyl_out(const cst_item *syl)
{
    /* Number of stressed syllables until last major break */
    const cst_item *ss,*p,*fs;
    int c;

    ss = item_as(syl,"Syllable");

    fs = path_to_item(syl,"R:SylStructure.parent.R:Phrase.parent.daughtern.R:SylStructure.daughtern");

#if 1 /* fix by uratec */
    if(item_equal(ss,fs))
      return val_string_n(0);
#endif
    for (c=0, p=item_next(ss); 
	 p && (c < CST_CONST_INT_MAX); 
	 p=item_next(p))
    {
	if (cst_streq("1",ffeature_string(p,"stress")))
	    c++;
	if (item_equal(p,fs))
	    break;
    }
    
    return val_string_n(c);  /* its used randomly as int and float */
}

static const cst_val *syl_in(const cst_item *syl)
{
    /* Number of syllables since last major break */
    const cst_item *ss,*p,*fs;
    int c;

    ss = item_as(syl,"Syllable");

    fs = path_to_item(syl,"R:SylStructure.parent.R:Phrase.parent.daughter.R:SylStructure.daughter");

    for (c=0, p=ss; 
	 p && (c < CST_CONST_INT_MAX); 
	 p=item_prev(p),c++)
	if (item_equal(p,fs))
	    break;
    return val_string_n(c);
}

static const cst_val *syl_out(const cst_item *syl)
{
    /* Number of syllables until next major break */
    cst_item *ss,*p;
    const cst_item *fs;
    int c;

    ss = item_as(syl,"Syllable");

    fs = path_to_item(syl,"R:SylStructure.parent.R:Phrase.parent.daughtern.R:SylStructure.daughtern");

    for (c=0, p=ss; 
	 p && (c < CST_CONST_INT_MAX); 
	 p=item_next(p),c++)
	if (item_equal(p,fs))
	    break;
    return val_string_n(c);
}

static const cst_val *syl_break(const cst_item *syl)
{
    /* Break level after this syllable */
    cst_item *ss;

    ss = item_as(syl,"SylStructure");

    if (ss == NULL)
	return VAL_STRING_1;  /* hmm, no sylstructure */
    else if (item_next(ss) != NULL)
	return VAL_STRING_0;  /* word internal */
    else if (item_parent(ss) == NULL)  /* no parent */
	return VAL_STRING_1;
    else
	return word_break(item_parent(ss));
}

static const cst_val *cg_break(const cst_item *syl)
{
    /* phrase prediction is so different between flite and festival */
    /* we go with this more robust technique */
    cst_item *ss;

    ss = item_as(syl,"SylStructure");

    if (ss == NULL)
	return VAL_STRING_0;  /* hmm, no sylstructure */
    else if (item_next(ss) != NULL)
	return VAL_STRING_0;  /* word internal */
    else if (path_to_item(ss,"R:SylStructure.parent.R:Word.n") == NULL)
        return VAL_STRING_4;  /* utterance final */
    else if (path_to_item(ss,"R:SylStructure.parent.R:Phrase.n") == NULL)
        return VAL_STRING_3;  /* phrase final */
    else
	return VAL_STRING_1;  /* word final */
}

static const cst_val *syl_codasize(const cst_item *syl)
{
    cst_item *d;
    int c;

    for (c=1,d=item_last_daughter(item_as(syl,"SylStructure"));
	 d;
	 d=item_prev(d),c++)
    {
	if (cst_streq("+",val_string(ph_vc(d))))
	    break;
    }

    return val_string_n(c);
}

static const cst_val *syl_onsetsize(const cst_item *syl)
{
    cst_item *d;
    int c;
    
    for (c=0,d=item_daughter(item_as(syl,"SylStructure"));
	 d;
	 d=item_next(d),c++)
    {
	if (cst_streq("+",val_string(ph_vc(d))))
	    break;
    }

    return val_string_n(c);
}

static const cst_val *asyl_in(const cst_item *syl)
{
    /* Number of accented syllables since last major break */
    const cst_item *ss,*p,*fs;
    int c;

    ss = item_as(syl,"Syllable");

    fs = path_to_item(syl,"R:SylStructure.parent.R:Phrase.parent.daughter.R:SylStructure.daughter");

#if 1 /* fix by uratec */
    if(item_equal(ss,fs))
      return val_string_n(0);
    for (c=0, p=item_prev(ss); 
#else
    for (c=0, p=ss; 
#endif
	 p && (c < CST_CONST_INT_MAX); 
	 p=item_prev(p))
    {
	if (val_int(accented(p)) == 1)
	    c++;
	if (item_equal(p,fs))
	    break;
    }
    
    return val_string_n(c);
}

static const cst_val *asyl_out(const cst_item *syl)
{
    /* Number of accented syllables until next major break */
    cst_item *ss,*p;
    const cst_item *fs;
    int c;

    ss = item_as(syl,"Syllable");

    fs = path_to_item(syl,"R:SylStructure.parent.R:Phrase.parent.daughtern.R:SylStructure.daughtern");

#if 1 /* fix by uratec */
    if(item_equal(ss,fs))
      return val_string_n(0);
    for (c=0, p=item_next(ss); 
#else
    for (c=0, p=ss; 
#endif
	 p && (c < CST_CONST_INT_MAX); 
	 p=item_next(p))
    {
	if (val_int(accented(p)) == 1)
	    c++;
	if (item_equal(p,fs))
	    break;
    }
    return val_string_n(c);
}

static const cst_val *segment_duration(const cst_item *seg)
{
    const cst_item *s = item_as(seg,"Segment");

    if (!s)
	return VAL_STRING_0;
    else if (item_prev(s) == NULL)
	return item_feat(s,"end");
    else
    {
	/* It should be okay to construct this as it will get
           dereferenced when the CART interpreter frees its feature
           cache. */
	return float_val(item_feat_float(s,"end")
			 - item_feat_float(item_prev(s),"end"));
    }
}


void basic_ff_register(cst_features *ffunctions)
{
    ff_register(ffunctions, "ph_vc",ph_vc);
    ff_register(ffunctions, "ph_vlng",ph_vlng);
    ff_register(ffunctions, "ph_vheight",ph_vheight);
    ff_register(ffunctions, "ph_vfront",ph_vfront);
    ff_register(ffunctions, "ph_vrnd",ph_vrnd);
    ff_register(ffunctions, "ph_ctype",ph_ctype);
    ff_register(ffunctions, "ph_cplace",ph_cplace);
    ff_register(ffunctions, "ph_cvox",ph_cvox);

    ff_register(ffunctions, "lisp_cg_duration", cg_duration);
    ff_register(ffunctions, "lisp_cg_state_pos", cg_state_pos);
    ff_register(ffunctions, "lisp_cg_state_place", cg_state_place);
    ff_register(ffunctions, "lisp_cg_state_index", cg_state_index);
    ff_register(ffunctions, "lisp_cg_state_rindex", cg_state_rindex);
    ff_register(ffunctions, "lisp_cg_phone_place", cg_phone_place);
    ff_register(ffunctions, "lisp_cg_phone_index", cg_phone_index);
    ff_register(ffunctions, "lisp_cg_phone_rindex", cg_phone_rindex);

    ff_register(ffunctions, "lisp_cg_position_in_phrasep", cg_position_in_phrasep);
    ff_register(ffunctions, "lisp_cg_find_phrase_number", cg_find_phrase_number);
    ff_register(ffunctions, "lisp_is_pau", cg_is_pau);

    ff_register(ffunctions, "word_numsyls",word_numsyls);
    ff_register(ffunctions, "word_break",word_break);
    ff_register(ffunctions, "word_punc",word_punc);
    ff_register(ffunctions, "ssyl_in",ssyl_in);
    ff_register(ffunctions, "ssyl_out",ssyl_out);
    ff_register(ffunctions, "syl_in",syl_in);
    ff_register(ffunctions, "syl_out",syl_out);
    ff_register(ffunctions, "syl_break",syl_break);
    ff_register(ffunctions, "lisp_cg_break",cg_break);
    ff_register(ffunctions, "old_syl_break",syl_break);
    ff_register(ffunctions, "syl_onsetsize",syl_onsetsize);
    ff_register(ffunctions, "syl_codasize",syl_codasize);
    ff_register(ffunctions, "accented",accented);
    ff_register(ffunctions, "asyl_in",asyl_in);
    ff_register(ffunctions, "asyl_out",asyl_out);
    ff_register(ffunctions, "lisp_coda_fric",seg_coda_fric);
    ff_register(ffunctions, "lisp_onset_fric",seg_onset_fric);
    ff_register(ffunctions, "lisp_coda_stop",seg_coda_stop);
    ff_register(ffunctions, "lisp_onset_stop",seg_onset_stop);
    ff_register(ffunctions, "lisp_coda_nasal",seg_coda_nasal);
    ff_register(ffunctions, "lisp_onset_nasal",seg_onset_nasal);
    ff_register(ffunctions, "lisp_coda_glide",seg_coda_glide);
    ff_register(ffunctions, "lisp_onset_glide",seg_onset_glide);
    ff_register(ffunctions, "seg_onsetcoda",seg_onsetcoda);
    ff_register(ffunctions, "pos_in_syl",pos_in_syl);
    ff_register(ffunctions, "position_type",position_type);
    ff_register(ffunctions, "sub_phrases",sub_phrases);
    ff_register(ffunctions, "last_accent",last_accent);
    ff_register(ffunctions, "next_accent",next_accent);
    ff_register(ffunctions, "syl_final",syl_final);
    ff_register(ffunctions, "segment_duration",segment_duration);
    ff_register(ffunctions, "lisp_cg_syl_ratio",cg_syl_ratio);
    ff_register(ffunctions, "lisp_cg_phrase_ratio",cg_phrase_ratio);
    ff_register(ffunctions, "lisp_cg_syls_in_phrase",cg_syls_in_phrase);
    ff_register(ffunctions, "pos_in_phrase",pos_in_phrase);
    ff_register(ffunctions, "pos_in_word",pos_in_word);
    ff_register(ffunctions, "syllable_duration",syllable_duration);
    ff_register(ffunctions, "syl_vowel",syl_vowel);
    ff_register(ffunctions, "syl_numphones",syl_numphones);
}
