462 lines
11 KiB
C++
462 lines
11 KiB
C++
|
/*************************************************************************/
|
||
|
/* */
|
||
|
/* Centre for Speech Technology Research */
|
||
|
/* University of Edinburgh, UK */
|
||
|
/* Copyright (c) 1997,1998 */
|
||
|
/* 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. */
|
||
|
/* THE UNIVERSITY OF EDINBURGH 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 THE UNIVERSITY OF EDINBURGH 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: Richard Caley (rjc@cstr.ed.ac.uk) */
|
||
|
/* Date: Mon Jul 21 1997 */
|
||
|
/* -------------------------------------------------------------------- */
|
||
|
/* Untyped list used as the basis of the TList class */
|
||
|
/* */
|
||
|
/*************************************************************************/
|
||
|
#include <EST_UList.h>
|
||
|
|
||
|
void EST_UList::clear_and_free(void (*item_free)(EST_UItem *p))
|
||
|
{
|
||
|
EST_UItem *q, *np;
|
||
|
|
||
|
for (q=head(); q != 0; q = np)
|
||
|
{
|
||
|
np=q->next();
|
||
|
if (item_free)
|
||
|
item_free(q);
|
||
|
else
|
||
|
delete q;
|
||
|
}
|
||
|
h = t = 0;
|
||
|
}
|
||
|
|
||
|
int EST_UList::length() const
|
||
|
{
|
||
|
EST_UItem *ptr;
|
||
|
int n = 0;
|
||
|
|
||
|
for (ptr = head(); ptr != 0; ptr = ptr->next())
|
||
|
++n;
|
||
|
return n;
|
||
|
}
|
||
|
|
||
|
int EST_UList::index(EST_UItem *item) const
|
||
|
{
|
||
|
EST_UItem *ptr;
|
||
|
int n = 0;
|
||
|
|
||
|
for (ptr = head(); ptr != 0; ptr = ptr->next(), ++n)
|
||
|
if (item == ptr)
|
||
|
return n;
|
||
|
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
EST_UItem *EST_UList::nth_pointer(int n) const
|
||
|
{
|
||
|
EST_UItem *ptr;
|
||
|
int i;
|
||
|
|
||
|
for (i = 0, ptr = head(); ptr != 0; ptr = ptr->next(), ++i)
|
||
|
if (i == n)
|
||
|
return ptr;
|
||
|
|
||
|
cerr << "Requested item #" << n << " off end of list" << endl;
|
||
|
return head();
|
||
|
}
|
||
|
|
||
|
EST_UItem * EST_UList::remove(EST_UItem *item,
|
||
|
void (*free_item)(EST_UItem *item))
|
||
|
{
|
||
|
if (item == 0)
|
||
|
return 0;
|
||
|
|
||
|
EST_UItem *prev = item->p;
|
||
|
if (item->p == 0) // at start
|
||
|
h = item->n;
|
||
|
else
|
||
|
item->p->n = item->n;
|
||
|
if (item->n == 0) // at end
|
||
|
t = item->p;
|
||
|
else
|
||
|
item->n->p = item->p;
|
||
|
|
||
|
if (free_item)
|
||
|
free_item(item);
|
||
|
else
|
||
|
delete item;
|
||
|
|
||
|
return prev;
|
||
|
}
|
||
|
|
||
|
EST_UItem * EST_UList::remove(int n,
|
||
|
void (*item_free)(EST_UItem *item))
|
||
|
{
|
||
|
return remove(nth_pointer(n), item_free);
|
||
|
}
|
||
|
|
||
|
// This should check if the incoming prev_item actually is in the list
|
||
|
|
||
|
EST_UItem *EST_UList::insert_after(EST_UItem *prev_item, EST_UItem *new_item)
|
||
|
{
|
||
|
if (new_item == 0)
|
||
|
return new_item;
|
||
|
if (prev_item == 0) // insert it at start of list
|
||
|
{
|
||
|
new_item->n = h;
|
||
|
h = new_item;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
new_item->n = prev_item->n;
|
||
|
prev_item->n = new_item;
|
||
|
}
|
||
|
new_item->p = prev_item;
|
||
|
if (new_item->n == 0)
|
||
|
t = new_item;
|
||
|
else
|
||
|
new_item->n->p = new_item;
|
||
|
|
||
|
return new_item;
|
||
|
}
|
||
|
|
||
|
EST_UItem *EST_UList::insert_before(EST_UItem *next_item, EST_UItem *new_item)
|
||
|
{
|
||
|
if (new_item == 0)
|
||
|
return new_item;
|
||
|
if (next_item == 0) // put it on the end of the list
|
||
|
{
|
||
|
new_item->p = t;
|
||
|
t = new_item;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
new_item->p = next_item->p;
|
||
|
next_item->p = new_item;
|
||
|
}
|
||
|
new_item->n = next_item;
|
||
|
if (new_item->p == 0)
|
||
|
h = new_item;
|
||
|
else
|
||
|
new_item->p->n = new_item;
|
||
|
|
||
|
return next_item;
|
||
|
}
|
||
|
|
||
|
void EST_UList::exchange(EST_UItem *a, EST_UItem *b)
|
||
|
{
|
||
|
|
||
|
if (a==b)
|
||
|
return;
|
||
|
|
||
|
if ((a==0) || (b==0))
|
||
|
{
|
||
|
cerr << "EST_UList:exchange: can't exchange NULL items" << endl;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// I know this isn't very readable but there are eight pointers
|
||
|
// that need to be changed, and half of them are trivial back pointers
|
||
|
// care need only be taken when b and a are adjacent, this actual
|
||
|
// sets p and n twice if they are adjacent but still gets the right answer
|
||
|
EST_UItem *ap=a->p,*an=a->n,*bn=b->n,*bp=b->p;
|
||
|
|
||
|
a->n = bn == a ? b : bn;
|
||
|
if (a->n)
|
||
|
a->n->p = a;
|
||
|
a->p = bp == a ? b : bp;
|
||
|
if (a->p)
|
||
|
a->p->n = a;
|
||
|
|
||
|
b->n = an == b ? a : an;
|
||
|
if (b->n)
|
||
|
b->n->p = b;
|
||
|
b->p = ap == b ? a : ap;
|
||
|
if (b->p)
|
||
|
b->p->n = b;
|
||
|
|
||
|
// Fix t and h
|
||
|
if (a == h)
|
||
|
h = b;
|
||
|
else if (b == h)
|
||
|
h = a;
|
||
|
else if (a == t)
|
||
|
t = b;
|
||
|
else if (b == t)
|
||
|
t = a;
|
||
|
|
||
|
}
|
||
|
|
||
|
void EST_UList::exchange(int i, int j)
|
||
|
{
|
||
|
|
||
|
EST_UItem *p;
|
||
|
EST_UItem *a=0,*b=0;
|
||
|
int k;
|
||
|
|
||
|
for (k=0,p = head(); p != 0; p = p->next(),k++)
|
||
|
{
|
||
|
if(i==k)
|
||
|
a = p;
|
||
|
if(j==k)
|
||
|
b = p;
|
||
|
}
|
||
|
|
||
|
if ((a==0) || (b==0))
|
||
|
{
|
||
|
cerr << "EST_UList:exchange: can't exchange items " << i <<
|
||
|
" and " << j << " (off end of list)" << endl;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
exchange(a,b);
|
||
|
}
|
||
|
|
||
|
void EST_UList::reverse()
|
||
|
{
|
||
|
EST_UItem *p,*q;
|
||
|
|
||
|
for (p=head(); p != 0; p=q)
|
||
|
{
|
||
|
q = p->n;
|
||
|
p->n = p->p;
|
||
|
p->p = q;
|
||
|
}
|
||
|
q = h;
|
||
|
h = t;
|
||
|
t = q;
|
||
|
}
|
||
|
|
||
|
void EST_UList::append(EST_UItem *new_item)
|
||
|
{
|
||
|
|
||
|
if (new_item == 0) return;
|
||
|
|
||
|
new_item->n = 0;
|
||
|
new_item->p = t;
|
||
|
if (t == 0)
|
||
|
h = new_item;
|
||
|
else
|
||
|
t->n = new_item;
|
||
|
t = new_item;
|
||
|
}
|
||
|
|
||
|
void EST_UList::prepend(EST_UItem *new_item)
|
||
|
{
|
||
|
if (new_item == 0) return;
|
||
|
|
||
|
new_item->p = 0;
|
||
|
new_item->n = h;
|
||
|
if (h == 0)
|
||
|
t = new_item;
|
||
|
else
|
||
|
h->p = new_item;
|
||
|
h = new_item;
|
||
|
}
|
||
|
|
||
|
bool EST_UList::operator_eq(const EST_UList &a,
|
||
|
const EST_UList &b,
|
||
|
bool (*eq)(const EST_UItem *item1, const EST_UItem *item2))
|
||
|
{
|
||
|
EST_UItem *p,*q;
|
||
|
q=b.head();
|
||
|
for (p = a.head(); p != NULL; p = p->next()){
|
||
|
if(q == NULL)
|
||
|
return false;
|
||
|
if(eq(q, p))
|
||
|
q=q->next();
|
||
|
else
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if(q == NULL)
|
||
|
return true;
|
||
|
else
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
int EST_UList::index(const EST_UList &l,
|
||
|
const EST_UItem &val,
|
||
|
bool (*eq)(const EST_UItem *item1, const EST_UItem *item2))
|
||
|
{
|
||
|
|
||
|
EST_UItem *ptr;
|
||
|
int n = 0;
|
||
|
|
||
|
for (ptr = l.head(); ptr != 0; ptr = ptr->next(), ++n)
|
||
|
if (eq(&val,ptr))
|
||
|
return n;
|
||
|
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
void EST_UList::sort(EST_UList &l,
|
||
|
bool (*gt)(const EST_UItem *item1,
|
||
|
const EST_UItem *item2))
|
||
|
{
|
||
|
|
||
|
// just bubble sort for now
|
||
|
// use EST_String::operator > for comparisons
|
||
|
|
||
|
EST_UItem *l_ptr,*m_ptr;
|
||
|
bool sorted=false;
|
||
|
|
||
|
while(!sorted){
|
||
|
sorted=true;
|
||
|
|
||
|
for(l_ptr=l.head(); l_ptr != 0; l_ptr=l_ptr->next()){
|
||
|
|
||
|
m_ptr=l_ptr->next();
|
||
|
if(m_ptr != 0)
|
||
|
if(gt(l_ptr, m_ptr)){
|
||
|
l.exchange(l_ptr,m_ptr);
|
||
|
sorted=false;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
// quicksort from 'Algorithms'
|
||
|
// by Cormen, Leiserson & Rivest
|
||
|
|
||
|
static EST_UItem *partition(EST_UItem *p, EST_UItem *r,
|
||
|
bool (*gt)(const EST_UItem *item1, const EST_UItem *item2),
|
||
|
void (*exchange)(EST_UItem *item1, EST_UItem *item2))
|
||
|
{
|
||
|
// this can be tidied up / sped up
|
||
|
|
||
|
EST_UItem *i,*j,*i2,*j2;
|
||
|
EST_UItem *x = p;
|
||
|
|
||
|
i = p;
|
||
|
j = r;
|
||
|
|
||
|
while(true){
|
||
|
|
||
|
while(gt(j, x) )
|
||
|
j = j->prev();
|
||
|
|
||
|
while(gt(x, i))
|
||
|
i = i->next();
|
||
|
|
||
|
if((i != j) && (i->prev() != j)){
|
||
|
i2=i;
|
||
|
j2=j;
|
||
|
i=i->next();
|
||
|
j=j->prev();
|
||
|
exchange(i2,j2);
|
||
|
|
||
|
}else
|
||
|
return j;
|
||
|
|
||
|
}
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
|
||
|
static void qsort_sub(EST_UList &l, EST_UItem *p, EST_UItem *r,
|
||
|
bool (*gt)(const EST_UItem *item1, const EST_UItem *item2),
|
||
|
void (*exchange)(EST_UItem *item1, EST_UItem *item2))
|
||
|
{
|
||
|
EST_UItem *q;
|
||
|
if(p != r){
|
||
|
q = partition(p,r, gt, exchange);
|
||
|
qsort_sub(l,p,q, gt, exchange);
|
||
|
qsort_sub(l,q->next(),r, gt, exchange);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void EST_UList::qsort(EST_UList &l,
|
||
|
bool (*gt)(const EST_UItem *item1, const EST_UItem *item2),
|
||
|
void (*exchange)(EST_UItem *item1, EST_UItem *item2))
|
||
|
{
|
||
|
qsort_sub(l,l.head(),l.tail(), gt, exchange);
|
||
|
}
|
||
|
|
||
|
|
||
|
void EST_UList::sort_unique(EST_UList &l,
|
||
|
bool (*eq)(const EST_UItem *item1, const EST_UItem *item2),
|
||
|
bool (*gt)(const EST_UItem *item1, const EST_UItem *item2),
|
||
|
void (*item_free)(EST_UItem *item))
|
||
|
{
|
||
|
// as sort(..) but delete any repeated items
|
||
|
|
||
|
EST_UItem *l_ptr,*m_ptr;
|
||
|
bool sorted=false;
|
||
|
|
||
|
while(!sorted){
|
||
|
sorted=true;
|
||
|
|
||
|
for(l_ptr=l.head(); l_ptr != 0; l_ptr=l_ptr->next()){
|
||
|
|
||
|
m_ptr=l_ptr->next();
|
||
|
if(m_ptr != 0)
|
||
|
{
|
||
|
if(gt(l_ptr, m_ptr)){
|
||
|
l.exchange(l_ptr,m_ptr);
|
||
|
sorted=false;
|
||
|
} else if(eq(l_ptr, m_ptr)){
|
||
|
l.remove(m_ptr, item_free);
|
||
|
sorted=false;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void EST_UList::merge_sort_unique(EST_UList &l, EST_UList &m,
|
||
|
bool (*eq)(const EST_UItem *item1, const EST_UItem *item2),
|
||
|
bool (*gt)(const EST_UItem *item1, const EST_UItem *item2),
|
||
|
void (*item_free)(EST_UItem *item))
|
||
|
{
|
||
|
// keep all unique items in l, and add any new items from m to l
|
||
|
|
||
|
EST_UItem *l_ptr,*m_ptr;
|
||
|
bool flag;
|
||
|
|
||
|
// make sure
|
||
|
sort_unique(l, eq, gt, item_free);
|
||
|
|
||
|
for(m_ptr=m.head(); m_ptr != 0; m_ptr=m_ptr->next()){
|
||
|
|
||
|
// try and put item from m in list
|
||
|
flag=false;
|
||
|
for(l_ptr=l.head(); l_ptr != 0; l_ptr=l_ptr->next()){
|
||
|
if( gt(l_ptr, m_ptr) ){
|
||
|
l.insert_before(l_ptr, m_ptr);
|
||
|
flag=true;
|
||
|
break;
|
||
|
}else if( eq(m_ptr, l_ptr) ){
|
||
|
flag=true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
// or try and append it
|
||
|
if(!flag && ( gt(m_ptr, l.tail()) ) )
|
||
|
l.append(m_ptr);
|
||
|
}
|
||
|
}
|