You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
842 lines
24 KiB
842 lines
24 KiB
/**
|
|
@file
|
|
|
|
@ingroup mtr
|
|
|
|
@brief Functions to support group specification for reordering.
|
|
|
|
@see cudd package
|
|
|
|
@author Fabio Somenzi
|
|
|
|
@copyright@parblock
|
|
Copyright (c) 1995-2015, Regents of the University of Colorado
|
|
|
|
All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions
|
|
are met:
|
|
|
|
Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
|
|
Redistributions in binary form must reproduce the above copyright
|
|
notice, this list of conditions and the following disclaimer in the
|
|
documentation and/or other materials provided with the distribution.
|
|
|
|
Neither the name of the University of Colorado nor the names of its
|
|
contributors may be used to endorse or promote products derived from
|
|
this software without specific prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
|
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
|
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
POSSIBILITY OF SUCH DAMAGE.
|
|
@endparblock
|
|
|
|
*/
|
|
|
|
#include "util.h"
|
|
#include "mtrInt.h"
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Constant declarations */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Stucture declarations */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Type declarations */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Variable declarations */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Macro declarations */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
/** \cond */
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Static function prototypes */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
static int mtrShiftHL (MtrNode *node, int shift);
|
|
|
|
/** \endcond */
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Definition of exported functions */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
|
|
/**
|
|
@brief Allocate new tree.
|
|
|
|
@details Allocate new tree with one node, whose low and size
|
|
fields are specified by the lower and size parameters.
|
|
|
|
@return pointer to tree root.
|
|
|
|
@sideeffect None
|
|
|
|
@see Mtr_InitTree Mtr_FreeTree
|
|
|
|
*/
|
|
MtrNode *
|
|
Mtr_InitGroupTree(
|
|
int lower,
|
|
int size)
|
|
{
|
|
MtrNode *root;
|
|
|
|
root = Mtr_InitTree();
|
|
if (root == NULL) return(NULL);
|
|
root->flags = MTR_DEFAULT;
|
|
root->low = lower;
|
|
root->size = size;
|
|
return(root);
|
|
|
|
} /* end of Mtr_InitGroupTree */
|
|
|
|
|
|
/**
|
|
@brief Makes a new group with size leaves starting at low.
|
|
|
|
@details If the new group intersects an existing group, it must
|
|
either contain it or be contained by it. This procedure relies on
|
|
the low and size fields of each node. It also assumes that the
|
|
children of each node are sorted in order of increasing low. In
|
|
case of a valid request, the flags of the new group are set to the
|
|
value passed in `flags.'
|
|
|
|
@return the pointer to the root of the new group upon successful
|
|
termination; NULL otherwise. If the group already exists, the
|
|
pointer to its root is returned.
|
|
|
|
@sideeffect None
|
|
|
|
@see Mtr_DissolveGroup Mtr_ReadGroups Mtr_FindGroup
|
|
|
|
*/
|
|
MtrNode *
|
|
Mtr_MakeGroup(
|
|
MtrNode * root /**< root of the group tree */,
|
|
unsigned int low /**< lower bound of the group */,
|
|
unsigned int size /**< size of the group */,
|
|
unsigned int flags /**< flags for the new group */)
|
|
{
|
|
MtrNode *node,
|
|
*first,
|
|
*last,
|
|
*previous,
|
|
*newn;
|
|
|
|
/* Sanity check. */
|
|
if (size == 0)
|
|
return(NULL);
|
|
|
|
/* Check whether current group includes new group. This check is
|
|
** necessary at the top-level call. In the subsequent calls it is
|
|
** redundant. */
|
|
if (low < (unsigned int) root->low ||
|
|
low + size > (unsigned int) (root->low + root->size))
|
|
return(NULL);
|
|
|
|
/* At this point we know that the new group is contained
|
|
** in the group of root. We have two possible cases here:
|
|
** - root is a terminal node;
|
|
** - root has children. */
|
|
|
|
/* Root has no children: create a new group. */
|
|
if (root->child == NULL) {
|
|
newn = Mtr_AllocNode();
|
|
if (newn == NULL) return(NULL); /* out of memory */
|
|
newn->low = low;
|
|
newn->size = size;
|
|
newn->flags = flags;
|
|
newn->parent = root;
|
|
newn->elder = newn->younger = newn->child = NULL;
|
|
root->child = newn;
|
|
return(newn);
|
|
}
|
|
|
|
/* Root has children: Find all children of root that are included
|
|
** in the new group. If the group of any child entirely contains
|
|
** the new group, call Mtr_MakeGroup recursively. */
|
|
previous = NULL;
|
|
first = root->child; /* guaranteed to be non-NULL */
|
|
while (first != NULL && low >= (unsigned int) (first->low + first->size)) {
|
|
previous = first;
|
|
first = first->younger;
|
|
}
|
|
if (first == NULL) {
|
|
/* We have scanned the entire list and we need to append a new
|
|
** child at the end of it. Previous points to the last child
|
|
** of root. */
|
|
newn = Mtr_AllocNode();
|
|
if (newn == NULL) return(NULL); /* out of memory */
|
|
newn->low = low;
|
|
newn->size = size;
|
|
newn->flags = flags;
|
|
newn->parent = root;
|
|
newn->elder = previous;
|
|
previous->younger = newn;
|
|
newn->younger = newn->child = NULL;
|
|
return(newn);
|
|
}
|
|
/* Here first is non-NULL and low < first->low + first->size. */
|
|
if (low >= (unsigned int) first->low &&
|
|
low + size <= (unsigned int) (first->low + first->size)) {
|
|
/* The new group is contained in the group of first. */
|
|
newn = Mtr_MakeGroup(first, low, size, flags);
|
|
return(newn);
|
|
} else if (low + size <= first->low) {
|
|
/* The new group is entirely contained in the gap between
|
|
** previous and first. */
|
|
newn = Mtr_AllocNode();
|
|
if (newn == NULL) return(NULL); /* out of memory */
|
|
newn->low = low;
|
|
newn->size = size;
|
|
newn->flags = flags;
|
|
newn->child = NULL;
|
|
newn->parent = root;
|
|
newn->elder = previous;
|
|
newn->younger = first;
|
|
first->elder = newn;
|
|
if (previous != NULL) {
|
|
previous->younger = newn;
|
|
} else {
|
|
root->child = newn;
|
|
}
|
|
return(newn);
|
|
} else if (low < (unsigned int) first->low &&
|
|
low + size < (unsigned int) (first->low + first->size)) {
|
|
/* Trying to cut an existing group: not allowed. */
|
|
return(NULL);
|
|
} else if (low > first->low) {
|
|
/* The new group neither is contained in the group of first
|
|
** (this was tested above) nor contains it. It is therefore
|
|
** trying to cut an existing group: not allowed. */
|
|
return(NULL);
|
|
}
|
|
|
|
/* First holds the pointer to the first child contained in the new
|
|
** group. Here low <= first->low and low + size >= first->low +
|
|
** first->size. One of the two inequalities is strict. */
|
|
last = first;
|
|
while (last->younger != NULL &&
|
|
(unsigned int) (last->younger->low + last->younger->size) <= low + size) {
|
|
last = last->younger;
|
|
}
|
|
if (last == NULL) {
|
|
/* All the chilren of root from first onward become children
|
|
** of the new group. */
|
|
newn = Mtr_AllocNode();
|
|
if (newn == NULL) return(NULL); /* out of memory */
|
|
newn->low = low;
|
|
newn->size = size;
|
|
newn->flags = flags;
|
|
newn->child = first;
|
|
newn->parent = root;
|
|
newn->elder = previous;
|
|
newn->younger = NULL;
|
|
first->elder = NULL;
|
|
if (previous != NULL) {
|
|
previous->younger = newn;
|
|
} else {
|
|
root->child = newn;
|
|
}
|
|
last = first;
|
|
while (last != NULL) {
|
|
last->parent = newn;
|
|
last = last->younger;
|
|
}
|
|
return(newn);
|
|
}
|
|
|
|
/* Here last != NULL and low + size <= last->low + last->size. */
|
|
if (low + size - 1 >= (unsigned int) last->low &&
|
|
low + size < (unsigned int) (last->low + last->size)) {
|
|
/* Trying to cut an existing group: not allowed. */
|
|
return(NULL);
|
|
}
|
|
|
|
/* First and last point to the first and last of the children of
|
|
** root that are included in the new group. Allocate a new node
|
|
** and make all children of root between first and last chidren of
|
|
** the new node. Previous points to the child of root immediately
|
|
** preceeding first. If it is NULL, then first is the first child
|
|
** of root. */
|
|
newn = Mtr_AllocNode();
|
|
if (newn == NULL) return(NULL); /* out of memory */
|
|
newn->low = low;
|
|
newn->size = size;
|
|
newn->flags = flags;
|
|
newn->child = first;
|
|
newn->parent = root;
|
|
if (previous == NULL) {
|
|
root->child = newn;
|
|
} else {
|
|
previous->younger = newn;
|
|
}
|
|
newn->elder = previous;
|
|
newn->younger = last->younger;
|
|
if (last->younger != NULL) {
|
|
last->younger->elder = newn;
|
|
}
|
|
last->younger = NULL;
|
|
first->elder = NULL;
|
|
for (node = first; node != NULL; node = node->younger) {
|
|
node->parent = newn;
|
|
}
|
|
|
|
return(newn);
|
|
|
|
} /* end of Mtr_MakeGroup */
|
|
|
|
|
|
/**
|
|
@brief Merges the children of `group' with the children of its
|
|
parent.
|
|
|
|
@details Disposes of the node pointed by group. If group is the root
|
|
of the group tree, this procedure leaves the tree unchanged.
|
|
|
|
@return the pointer to the parent of `group' upon successful
|
|
termination; NULL otherwise.
|
|
|
|
@sideeffect None
|
|
|
|
@see Mtr_MakeGroup
|
|
|
|
*/
|
|
MtrNode *
|
|
Mtr_DissolveGroup(
|
|
MtrNode * group /**< group to be dissolved */)
|
|
{
|
|
MtrNode *parent;
|
|
MtrNode *last;
|
|
|
|
parent = group->parent;
|
|
|
|
if (parent == NULL) return(NULL);
|
|
if (MTR_TEST(group,MTR_TERMINAL) || group->child == NULL) return(NULL);
|
|
|
|
/* Make all children of group children of its parent, and make
|
|
** last point to the last child of group. */
|
|
for (last = group->child; last->younger != NULL; last = last->younger) {
|
|
last->parent = parent;
|
|
}
|
|
last->parent = parent;
|
|
|
|
last->younger = group->younger;
|
|
if (group->younger != NULL) {
|
|
group->younger->elder = last;
|
|
}
|
|
|
|
group->child->elder = group->elder;
|
|
if (group == parent->child) {
|
|
parent->child = group->child;
|
|
} else {
|
|
group->elder->younger = group->child;
|
|
}
|
|
|
|
Mtr_DeallocNode(group);
|
|
return(parent);
|
|
|
|
} /* end of Mtr_DissolveGroup */
|
|
|
|
|
|
/**
|
|
@brief Finds a group with size leaves starting at low, if it exists.
|
|
|
|
@details This procedure relies on the low and size fields of each
|
|
node. It also assumes that the children of each node are sorted in
|
|
order of increasing low.
|
|
|
|
@return the pointer to the root of the group upon successful
|
|
termination; NULL otherwise.
|
|
|
|
@sideeffect None
|
|
|
|
*/
|
|
MtrNode *
|
|
Mtr_FindGroup(
|
|
MtrNode * root /**< root of the group tree */,
|
|
unsigned int low /**< lower bound of the group */,
|
|
unsigned int size /**< upper bound of the group */)
|
|
{
|
|
MtrNode *node;
|
|
|
|
#ifdef MTR_DEBUG
|
|
/* We cannot have a non-empty proper subgroup of a singleton set. */
|
|
assert(!MTR_TEST(root,MTR_TERMINAL));
|
|
#endif
|
|
|
|
/* Sanity check. */
|
|
if (size < 1) return(NULL);
|
|
|
|
/* Check whether current group includes the group sought. This
|
|
** check is necessary at the top-level call. In the subsequent
|
|
** calls it is redundant. */
|
|
if (low < (unsigned int) root->low ||
|
|
low + size > (unsigned int) (root->low + root->size))
|
|
return(NULL);
|
|
|
|
if (root->size == size && root->low == low)
|
|
return(root);
|
|
|
|
if (root->child == NULL)
|
|
return(NULL);
|
|
|
|
/* Find all chidren of root that are included in the new group. If
|
|
** the group of any child entirely contains the new group, call
|
|
** Mtr_MakeGroup recursively. */
|
|
node = root->child;
|
|
while (low >= (unsigned int) (node->low + node->size)) {
|
|
node = node->younger;
|
|
}
|
|
if (low + size <= (unsigned int) (node->low + node->size)) {
|
|
/* The group is contained in the group of node. */
|
|
node = Mtr_FindGroup(node, low, size);
|
|
return(node);
|
|
} else {
|
|
return(NULL);
|
|
}
|
|
|
|
} /* end of Mtr_FindGroup */
|
|
|
|
|
|
/**
|
|
@brief Swaps two children of a tree node.
|
|
|
|
@details Adjusts the high and low fields of the two nodes and their
|
|
descendants. The two children must be adjacent. However, first may
|
|
be the younger sibling of second.
|
|
|
|
@return 1 in case of success; 0 otherwise.
|
|
|
|
@sideeffect None
|
|
|
|
*/
|
|
int
|
|
Mtr_SwapGroups(
|
|
MtrNode * first /**< first node to be swapped */,
|
|
MtrNode * second /**< second node to be swapped */)
|
|
{
|
|
MtrNode *node;
|
|
MtrNode *parent;
|
|
int sizeFirst;
|
|
int sizeSecond;
|
|
|
|
if (second->younger == first) { /* make first first */
|
|
node = first;
|
|
first = second;
|
|
second = node;
|
|
} else if (first->younger != second) { /* non-adjacent */
|
|
return(0);
|
|
}
|
|
|
|
sizeFirst = first->size;
|
|
sizeSecond = second->size;
|
|
|
|
/* Swap the two nodes. */
|
|
parent = first->parent;
|
|
if (parent == NULL || second->parent != parent) return(0);
|
|
if (parent->child == first) {
|
|
parent->child = second;
|
|
} else { /* first->elder != NULL */
|
|
first->elder->younger = second;
|
|
}
|
|
if (second->younger != NULL) {
|
|
second->younger->elder = first;
|
|
}
|
|
first->younger = second->younger;
|
|
second->elder = first->elder;
|
|
first->elder = second;
|
|
second->younger = first;
|
|
|
|
/* Adjust the high and low fields. */
|
|
if (!mtrShiftHL(first,sizeSecond)) return(0);
|
|
if (!mtrShiftHL(second,-sizeFirst)) return(0);
|
|
|
|
return(1);
|
|
|
|
} /* end of Mtr_SwapGroups */
|
|
|
|
|
|
/**
|
|
@brief Fix variable tree at the end of tree sifting.
|
|
|
|
@details Fix the levels in the variable tree sorting siblings
|
|
according to them. It should be called on a non-NULL tree. It then
|
|
maintains this invariant. It applies insertion sorting to the list of
|
|
siblings The order is determined by permutation, which is used to find
|
|
the new level of the node index. Index must refer to the first variable
|
|
in the group.
|
|
|
|
@sideeffect The tree is modified.
|
|
|
|
*/
|
|
void
|
|
Mtr_ReorderGroups(
|
|
MtrNode *treenode,
|
|
int *permutation)
|
|
{
|
|
MtrNode *auxnode;
|
|
/* Initialize sorted list to first element. */
|
|
MtrNode *sorted = treenode;
|
|
sorted->low = permutation[sorted->index];
|
|
if (sorted->child != NULL)
|
|
Mtr_ReorderGroups(sorted->child, permutation);
|
|
|
|
auxnode = treenode->younger;
|
|
while (auxnode != NULL) {
|
|
MtrNode *rightplace;
|
|
MtrNode *moving = auxnode;
|
|
auxnode->low = permutation[auxnode->index];
|
|
if (auxnode->child != NULL)
|
|
Mtr_ReorderGroups(auxnode->child, permutation);
|
|
rightplace = auxnode->elder;
|
|
/* Find insertion point. */
|
|
while (rightplace != NULL && auxnode->low < rightplace->low)
|
|
rightplace = rightplace->elder;
|
|
auxnode = auxnode->younger;
|
|
if (auxnode != NULL) {
|
|
auxnode->elder = moving->elder;
|
|
auxnode->elder->younger = auxnode;
|
|
} else {
|
|
moving->elder->younger = NULL;
|
|
}
|
|
if (rightplace == NULL) { /* Move to head of sorted list. */
|
|
sorted->elder = moving;
|
|
moving->elder = NULL;
|
|
moving->younger = sorted;
|
|
sorted = moving;
|
|
} else { /* Splice. */
|
|
moving->elder = rightplace;
|
|
moving->younger = rightplace->younger;
|
|
if (rightplace->younger != NULL)
|
|
rightplace->younger->elder = moving;
|
|
rightplace->younger = moving;
|
|
}
|
|
}
|
|
/* Fix parent. */
|
|
if (sorted->parent != NULL)
|
|
sorted->parent->child = sorted;
|
|
|
|
} /* end of Mtr_ReorderGroups */
|
|
|
|
|
|
/**
|
|
@brief Prints the groups as a parenthesized list.
|
|
|
|
@details After each group, the group's flag are printed, preceded by a `|'.
|
|
For each flag (except MTR_TERMINAL) a character is printed.
|
|
<ul>
|
|
<li>F: MTR_FIXED
|
|
<li>N: MTR_NEWNODE
|
|
<li>S: MTR_SOFT
|
|
</ul>
|
|
The second argument, silent, if different from 0, causes
|
|
Mtr_PrintGroups to only check the syntax of the group tree.
|
|
|
|
@sideeffect None
|
|
|
|
@see Mtr_PrintTree
|
|
|
|
*/
|
|
void
|
|
Mtr_PrintGroups(
|
|
MtrNode const * root /**< root of the group tree */,
|
|
int silent /**< flag to check tree syntax only */)
|
|
{
|
|
MtrNode *node;
|
|
|
|
assert(root != NULL);
|
|
assert(root->younger == NULL || root->younger->elder == root);
|
|
assert(root->elder == NULL || root->elder->younger == root);
|
|
#if SIZEOF_VOID_P == 8
|
|
if (!silent) (void) printf("(%u",root->low);
|
|
#else
|
|
if (!silent) (void) printf("(%hu",root->low);
|
|
#endif
|
|
if (MTR_TEST(root,MTR_TERMINAL) || root->child == NULL) {
|
|
if (!silent) (void) printf(",");
|
|
} else {
|
|
node = root->child;
|
|
while (node != NULL) {
|
|
assert(node->low >= root->low && (int) (node->low + node->size) <= (int) (root->low + root->size));
|
|
assert(node->parent == root);
|
|
Mtr_PrintGroups(node,silent);
|
|
node = node->younger;
|
|
}
|
|
}
|
|
if (!silent) {
|
|
#if SIZEOF_VOID_P == 8
|
|
(void) printf("%u", (MtrHalfWord) (root->low + root->size - 1));
|
|
#else
|
|
(void) printf("%hu", (MtrHalfWord) (root->low + root->size - 1));
|
|
#endif
|
|
if (root->flags != MTR_DEFAULT) {
|
|
(void) printf("|");
|
|
if (MTR_TEST(root,MTR_FIXED)) (void) printf("F");
|
|
if (MTR_TEST(root,MTR_NEWNODE)) (void) printf("N");
|
|
if (MTR_TEST(root,MTR_SOFT)) (void) printf("S");
|
|
}
|
|
(void) printf(")");
|
|
if (root->parent == NULL) (void) printf("\n");
|
|
}
|
|
assert((root->flags &~(MTR_TERMINAL | MTR_SOFT | MTR_FIXED | MTR_NEWNODE)) == 0);
|
|
return;
|
|
|
|
} /* end of Mtr_PrintGroups */
|
|
|
|
|
|
/**
|
|
@brief Prints the variable order as a parenthesized list.
|
|
|
|
@details After each group, the group's flag are printed, preceded by a `|'.
|
|
For each flag (except MTR_TERMINAL) a character is printed.
|
|
<ul>
|
|
<li>F: MTR_FIXED
|
|
<li>N: MTR_NEWNODE
|
|
<li>S: MTR_SOFT
|
|
</ul>
|
|
The second argument, gives the map from levels to variable indices.
|
|
|
|
@return 1 if successful; 0 otherwise.
|
|
|
|
@sideeffect None
|
|
|
|
@see Mtr_PrintGroups
|
|
|
|
*/
|
|
int
|
|
Mtr_PrintGroupedOrder(
|
|
MtrNode const * root /**< root of the group tree */,
|
|
int const *invperm /**< map from levels to indices */,
|
|
FILE *fp /**< output file */)
|
|
{
|
|
MtrNode *child;
|
|
MtrHalfWord level;
|
|
int retval;
|
|
|
|
assert(root != NULL);
|
|
assert(root->younger == NULL || root->younger->elder == root);
|
|
assert(root->elder == NULL || root->elder->younger == root);
|
|
retval = fprintf(fp,"(");
|
|
if (retval == EOF) return(0);
|
|
level = root->low;
|
|
child = root->child;
|
|
while (child != NULL) {
|
|
assert(child->low >= root->low && (child->low + child->size) <= (root->low + root->size));
|
|
assert(child->parent == root);
|
|
while (level < child->low) {
|
|
retval = fprintf(fp,"%d%s", invperm[level], (level < root->low + root->size - 1) ? "," : "");
|
|
if (retval == EOF) return(0);
|
|
level++;
|
|
}
|
|
retval = Mtr_PrintGroupedOrder(child,invperm,fp);
|
|
if (retval == 0) return(0);
|
|
level += child->size;
|
|
if (level < root->low + root->size - 1) {
|
|
retval = fprintf(fp,",");
|
|
if (retval == EOF) return(0);
|
|
}
|
|
child = child->younger;
|
|
}
|
|
while (level < root->low + root->size) {
|
|
retval = fprintf(fp,"%d%s", invperm[level], (level < root->low + root->size - 1) ? "," : "");
|
|
if (retval == EOF) return(0);
|
|
level++;
|
|
}
|
|
if (root->flags != MTR_DEFAULT) {
|
|
retval = fprintf(fp,"|");
|
|
if (retval == EOF) return(0);
|
|
if (MTR_TEST(root,MTR_FIXED)) {
|
|
retval = fprintf(fp,"F");
|
|
if (retval == EOF) return(0);
|
|
}
|
|
if (MTR_TEST(root,MTR_NEWNODE)) {
|
|
retval = fprintf(fp,"N");
|
|
if (retval == EOF) return(0);
|
|
}
|
|
if (MTR_TEST(root,MTR_SOFT)) {
|
|
retval = fprintf(fp,"S");
|
|
if (retval == EOF) return(0);
|
|
}
|
|
}
|
|
retval = fprintf(fp,")");
|
|
if (retval == EOF) return(0);
|
|
if (root->parent == NULL) {
|
|
retval = fprintf(fp,"\n");
|
|
if (retval == EOF) return(0);
|
|
}
|
|
assert((root->flags &~(MTR_SOFT | MTR_FIXED | MTR_NEWNODE)) == 0);
|
|
return(1);
|
|
|
|
} /* end of Mtr_PrintGroupedOrder */
|
|
|
|
|
|
/**
|
|
@brief Reads groups from a file and creates a group tree.
|
|
|
|
@details Each group is specified by three fields:
|
|
|
|
low size flags.
|
|
|
|
Low and size are (short) integers. Flags is a string composed of the
|
|
following characters (with associated translation):
|
|
<ul>
|
|
<li>D: MTR_DEFAULT
|
|
<li>F: MTR_FIXED
|
|
<li>N: MTR_NEWNODE
|
|
<li>S: MTR_SOFT
|
|
<li>T: MTR_TERMINAL
|
|
</ul>
|
|
Normally, the only flags that are needed are D and F. Groups and
|
|
fields are separated by white space (spaces, tabs, and newlines).
|
|
|
|
@return a pointer to the group tree if successful; NULL otherwise.
|
|
|
|
@sideeffect None
|
|
|
|
@see Mtr_InitGroupTree Mtr_MakeGroup
|
|
|
|
*/
|
|
MtrNode *
|
|
Mtr_ReadGroups(
|
|
FILE * fp /**< file pointer */,
|
|
int nleaves /**< number of leaves of the new tree */)
|
|
{
|
|
int low;
|
|
int size;
|
|
int err;
|
|
unsigned int flags;
|
|
MtrNode *root;
|
|
MtrNode *node;
|
|
char attrib[8*sizeof(unsigned int)+1];
|
|
char *c;
|
|
|
|
root = Mtr_InitGroupTree(0,nleaves);
|
|
if (root == NULL) return NULL;
|
|
|
|
while (! feof(fp)) {
|
|
/* Read a triple and check for consistency. */
|
|
err = fscanf(fp, "%d %d %s", &low, &size, attrib);
|
|
if (err == EOF) {
|
|
break;
|
|
} else if (err != 3) {
|
|
Mtr_FreeTree(root);
|
|
return(NULL);
|
|
} else if (low < 0 || low+size > nleaves || size < 1) {
|
|
Mtr_FreeTree(root);
|
|
return(NULL);
|
|
} else if (strlen(attrib) > 8 * sizeof(MtrHalfWord)) {
|
|
/* Not enough bits in the flags word to store these many
|
|
** attributes. */
|
|
Mtr_FreeTree(root);
|
|
return(NULL);
|
|
}
|
|
|
|
/* Parse the flag string. Currently all flags are permitted,
|
|
** to make debugging easier. Normally, specifying NEWNODE
|
|
** wouldn't be allowed. */
|
|
flags = MTR_DEFAULT;
|
|
for (c=attrib; *c != 0; c++) {
|
|
switch (*c) {
|
|
case 'D':
|
|
break;
|
|
case 'F':
|
|
flags |= MTR_FIXED;
|
|
break;
|
|
case 'N':
|
|
flags |= MTR_NEWNODE;
|
|
break;
|
|
case 'S':
|
|
flags |= MTR_SOFT;
|
|
break;
|
|
case 'T':
|
|
flags |= MTR_TERMINAL;
|
|
break;
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
node = Mtr_MakeGroup(root, (MtrHalfWord) low, (MtrHalfWord) size,
|
|
flags);
|
|
if (node == NULL) {
|
|
Mtr_FreeTree(root);
|
|
return(NULL);
|
|
}
|
|
}
|
|
|
|
return(root);
|
|
|
|
} /* end of Mtr_ReadGroups */
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Definition of internal functions */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Definition of static functions */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
|
|
/**
|
|
@brief Adjusts the low fields of a node and its descendants.
|
|
|
|
@details Adds shift to low of each node. Checks that no
|
|
out-of-bounds values result.
|
|
|
|
@return 1 in case of success; 0 otherwise.
|
|
|
|
@sideeffect None
|
|
|
|
*/
|
|
static int
|
|
mtrShiftHL(
|
|
MtrNode * node /**< group tree node */,
|
|
int shift /**< amount by which low should be changed */)
|
|
{
|
|
MtrNode *auxnode;
|
|
int low;
|
|
|
|
low = (int) node->low;
|
|
|
|
|
|
low += shift;
|
|
|
|
if (low < 0 || low + (int) (node->size - 1) > (int) MTR_MAXHIGH) return(0);
|
|
|
|
node->low = (MtrHalfWord) low;
|
|
|
|
if (!MTR_TEST(node,MTR_TERMINAL) && node->child != NULL) {
|
|
auxnode = node->child;
|
|
do {
|
|
if (!mtrShiftHL(auxnode,shift)) return(0);
|
|
auxnode = auxnode->younger;
|
|
} while (auxnode != NULL);
|
|
}
|
|
|
|
return(1);
|
|
|
|
} /* end of mtrShiftHL */
|