/* * Copyright 2001-2018 Adrian Thurston * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ /* * Setting conditions and merging states with conditions are similar activities * when expressed in code. The critical difference is that a merge is a union * of multiple paths. We have to take both paths. Setting a condition, however, * is a restriction. We have to expand the transition to follow both values of * the condition, then remove the one that is not set. */ #include "fsmgraph.h" #include "mergesort.h" #include "parsedata.h" #include #include long TransAp::condFullSize() { return condSpace == 0 ? 1 : condSpace->fullSize(); } void FsmAp::expandCondKeys( CondKeySet &condKeys, CondSpace *fromSpace, CondSpace *mergedSpace ) { CondSet fromCS, mergedCS; if ( fromSpace != 0 ) fromCS.insert( fromSpace->condSet ); if ( mergedSpace != 0 ) mergedCS.insert( mergedSpace->condSet ); /* Need to transform condition element to the merged set. */ for ( int cti = 0; cti < condKeys.length(); cti++ ) { long origVal = condKeys[cti]; long newVal = 0; /* Iterate the bit positions in the from set. */ for ( CondSet::Iter csi = fromCS; csi.lte(); csi++ ) { /* If set, find it in the merged set and flip the bit to 1. */ if ( origVal & (1 << csi.pos()) ) { /* The condition is set. Find the bit position in the merged * set. */ Action **cim = mergedCS.find( *csi ); long bitPos = (cim - mergedCS.data); newVal |= 1 << bitPos; } } if ( origVal != newVal ) condKeys[cti] = newVal; } /* Need to double up the whole transition list for each condition test in * merged that is not in from. The one we add has the bit in question set. * */ for ( CondSet::Iter csi = mergedCS; csi.lte(); csi++ ) { Action **cim = fromCS.find( *csi ); if ( cim == 0 ) { CondKeySet newItems; newItems.append( condKeys ); for ( int cti = 0; cti < condKeys.length(); cti++ ) { int key = condKeys[cti] | (1 << csi.pos()); newItems.insert( key ); } condKeys.setAs( newItems ); } } } void FsmAp::expandConds( StateAp *fromState, TransAp *trans, CondSpace *fromSpace, CondSpace *mergedSpace ) { CondSet fromCS, mergedCS; if ( fromSpace != 0 ) fromCS.insert( fromSpace->condSet ); if ( mergedSpace != 0 ) mergedCS.insert( mergedSpace->condSet ); /* Need to transform condition element to the merged set. */ for ( CondList::Iter cti = trans->tcap()->condList; cti.lte(); cti++ ) { long origVal = cti->key.getVal(); long newVal = 0; /* Iterate the bit positions in the from set. */ for ( CondSet::Iter csi = fromCS; csi.lte(); csi++ ) { /* If set, find it in the merged set and flip the bit to 1. */ if ( origVal & (1 << csi.pos()) ) { /* The condition is set. Find the bit position in the merged * set. */ Action **cim = mergedCS.find( *csi ); long bitPos = (cim - mergedCS.data); newVal |= 1 << bitPos; } } if ( origVal != newVal ) cti->key = newVal; } /* Need to double up the whole transition list for each condition test in * merged that is not in from. The one we add has the bit in question set. * */ for ( CondSet::Iter csi = mergedCS; csi.lte(); csi++ ) { Action **cim = fromCS.find( *csi ); if ( cim == 0 ) { CondList newItems; for ( CondList::Iter cti = trans->tcap()->condList; cti.lte(); cti++ ) { /* Sub-transition for conditions. */ CondAp *cond = new CondAp( trans ); /* Attach only if our caller wants the expanded transitions * attached. */ attachTrans( fromState, cti->toState, cond ); /* Call the user callback to add in the original source transition. */ addInTrans( cond, cti.ptr ); cond->key = cti->key.getVal() | (1 << csi.pos()); newItems.append( cond ); } /* Merge newItems in. Both the condList and newItems are sorted. Make * a sorted list out of them. */ CondAp *dest = trans->tcap()->condList.head; while ( dest != 0 && newItems.head != 0 ) { if ( newItems.head->key.getVal() > dest->key.getVal() ) { dest = dest->next; } else { /* Pop the item for insertion. */ CondAp *ins = newItems.detachFirst(); trans->tcap()->condList.addBefore( dest, ins ); } } /* Append the rest of the items. */ trans->tcap()->condList.append( newItems ); } } } CondSpace *FsmAp::expandCondSpace( TransAp *destTrans, TransAp *srcTrans ) { CondSet destCS, srcCS; CondSet mergedCS; if ( destTrans->condSpace != 0 ) destCS.insert( destTrans->condSpace->condSet ); if ( srcTrans->condSpace != 0 ) srcCS.insert( srcTrans->condSpace->condSet ); mergedCS.insert( destCS ); mergedCS.insert( srcCS ); return addCondSpace( mergedCS ); } StateAp *FsmAp::copyStateForExpansion( StateAp *srcState ) { StateAp *newState = new StateAp(); newState->outCondSpace = srcState->outCondSpace; newState->outCondKeys = srcState->outCondKeys; return newState; } void FsmAp::mergeOutConds( StateAp *destState, StateAp *srcState, bool leaving ) { if ( destState == srcState ) return; bool bothFinal = destState->isFinState() && srcState->isFinState(); bool unionOp = !leaving; CondSet destCS, srcCS; CondSet mergedCS; if ( destState->outCondSpace != 0 ) destCS.insert( destState->outCondSpace->condSet ); if ( srcState->outCondSpace != 0 ) srcCS.insert( srcState->outCondSpace->condSet ); mergedCS.insert( destCS ); mergedCS.insert( srcCS ); if ( mergedCS.length() > 0 ) { CondSpace *mergedSpace = addCondSpace( mergedCS ); CondSpace *srcSpace = srcState->outCondSpace; CondKeySet srcKeys = srcState->outCondKeys; if ( srcSpace != mergedSpace ) { /* Prep the key list with zero item if necessary. */ if ( srcSpace == 0 ) srcKeys.append( 0 ); expandCondKeys( srcKeys, srcSpace, mergedSpace ); } if ( destState->outCondSpace != mergedSpace ) { /* Prep the key list with zero item if necessary. */ if ( destState->outCondSpace == 0 ) destState->outCondKeys.append( 0 ); /* Now expand the dest. */ expandCondKeys( destState->outCondKeys, destState->outCondSpace, mergedSpace ); } destState->outCondSpace = mergedSpace; if ( unionOp && bothFinal ) { /* Keys can come from either. */ for ( CondKeySet::Iter c = srcKeys; c.lte(); c++ ) destState->outCondKeys.insert( *c ); } else { /* Keys need to be in both sets. */ for ( long c = 0; c < destState->outCondKeys.length(); ) { if ( !srcKeys.find( destState->outCondKeys[c] ) ) destState->outCondKeys.CondKeyVect::remove( c, 1 ); else c++; } } } } CondSpace *FsmAp::addCondSpace( const CondSet &condSet ) { CondSpace *condSpace = ctx->condData->condSpaceMap.find( condSet ); if ( condSpace == 0 ) { condSpace = new CondSpace( condSet ); ctx->condData->condSpaceMap.insert( condSpace ); } return condSpace; } TransDataAp *FsmAp::convertToTransAp( StateAp *from, CondAp *cond ) { TransDataAp *newTrans = new TransDataAp(); newTrans->lowKey = cond->transAp->lowKey; newTrans->highKey = cond->transAp->highKey; newTrans->lmActionTable.setActions( cond->lmActionTable ); newTrans->actionTable.setActions( cond->actionTable ); newTrans->priorTable.setPriors( cond->priorTable ); attachTrans( from, cond->toState, newTrans ); /* Detach in list. */ detachTrans( from, cond->toState, cond ); delete cond->transAp; delete cond; return newTrans; } TransCondAp *FsmAp::convertToCondAp( StateAp *from, TransDataAp *trans ) { TransCondAp *newTrans = new TransCondAp(); newTrans->lowKey = trans->lowKey; newTrans->highKey = trans->highKey; newTrans->condSpace = trans->condSpace; CondAp *newCond = new CondAp( newTrans ); newCond->key = 0; newTrans->condList.append( newCond ); newCond->lmActionTable.setActions( trans->lmActionTable ); newCond->actionTable.setActions( trans->actionTable ); newCond->priorTable.setPriors( trans->priorTable ); attachTrans( from, trans->toState, newCond ); /* Detach in list. */ detachTrans( from, trans->toState, trans ); delete trans; return newTrans; } void FsmAp::convertToCondAp( StateAp *state ) { /* First replace TransDataAp with cond versions. */ TransList destList; for ( TransList::Iter tr = state->outList; tr.lte(); ) { TransList::Iter next = tr.next(); if ( tr->plain() ) { TransCondAp *newTrans = convertToCondAp( state, tr->tdap() ); destList.append( newTrans ); } else { destList.append( tr ); } tr = next; } state->outList.abandon(); state->outList.transfer( destList ); } void FsmAp::doEmbedCondition( StateAp *state, const CondSet &set, const CondKeySet &vals ) { convertToCondAp( state ); for ( TransList::Iter tr = state->outList; tr.lte(); tr++ ) { /* The source (being embedded). */ CondSpace *srcSpace = addCondSpace( set ); CondKeySet srcVals = vals; /* Extract cond key set from the condition list. We will use this to * compute the intersection of the cond keys. */ CondSpace *trSpace = tr->condSpace; CondKeySet trVals; if ( tr->condSpace == 0 ) trVals.append( 0 ); else { for ( CondList::Iter cti = tr->tcap()->condList; cti.lte(); cti++ ) { long key = cti->key.getVal(); trVals.append( key ); } } /* Construct merged. */ CondSet mergedCS; if ( tr->condSpace != 0 ) mergedCS.insert( tr->condSpace->condSet ); mergedCS.insert( set ); CondSpace *mergedSpace = addCondSpace( mergedCS ); if ( srcSpace != mergedSpace ) { /* Prep the key list with zero item if necessary. */ if ( srcSpace == 0 ) srcVals.append( 0 ); expandCondKeys( srcVals, srcSpace, mergedSpace ); } if ( trSpace != mergedSpace ) { /* Don't need to prep the key list with zero item, will be there * (see above). */ expandCondKeys( trVals, trSpace, mergedSpace ); } /* Implement AND, in two parts. */ CondKeySet newItems; for ( CondKeySet::Iter c = srcVals; c.lte(); c++ ) { if ( trVals.find( *c ) ) newItems.insert( *c ); } for ( CondKeySet::Iter c = trVals; c.lte(); c++ ) { if ( srcVals.find( *c ) ) newItems.insert( *c ); } /* Expand the transitions, then we remove anything not in the computed * list of keys. This approach allows us to embed combinations of * senses, rather than cond-sense pairs. Necessary for out conditions. */ CondSpace *orig = tr->condSpace; tr->condSpace = mergedSpace; expandConds( state, tr, orig, mergedSpace ); /* After expansion, remove anything not in newItems. */ for ( CondList::Iter cti = tr->tcap()->condList; cti.lte(); ) { long key = cti->key.getVal(); if ( !newItems.find( key ) ) { /* Delete. */ CondList::Iter next = cti.next(); CondAp *cond = cti; detachTrans( state, cond->toState, cond ); tr->tcap()->condList.detach( cond ); delete cond; cti = next; } else { /* Leave alone. */ cti++; } } } } FsmRes FsmAp::embedCondition( FsmAp *fsm, StateAp *state, const CondSet &set, const CondKeySet &vals ) { /* Turn on misfit accounting to possibly catch the old start state. */ fsm->setMisfitAccounting( true ); /* Worker. */ fsm->doEmbedCondition( state, set, vals ); /* Fill in any states that were newed up as combinations of others. */ FsmRes res = fillInStates( fsm ); if ( !res.success() ) return res; /* Remove the misfits and turn off misfit accounting. */ fsm->removeMisfits(); fsm->setMisfitAccounting( false ); return res; } void FsmAp::addOutCondition( StateAp *state, Action *condAction, bool sense ) { CondSet origCS; if ( state->outCondSpace != 0 ) origCS.insert( state->outCondSpace->condSet ); CondSet mergedCS; mergedCS.insert( origCS ); bool added = mergedCS.insert( condAction ); if ( !added ) { /* Already exists in the cond set. For every transition, if the * sense is identical to what we are embedding, leave it alone. If * the sense is opposite, delete it. */ /* Find the position. */ long pos = 0; for ( CondSet::Iter csi = mergedCS; csi.lte(); csi++ ) { if ( *csi == condAction ) pos = csi.pos(); } for ( int cti = 0; cti < state->outCondKeys.length(); ) { long key = state->outCondKeys[cti]; bool set = ( key & ( 1 << pos ) ) != 0; if ( sense xor set ) { /* Delete. */ state->outCondKeys.CondKeyVect::remove( cti, 1 ); } else { /* Leave alone. */ cti++; } } } else { /* Does not exist in the cond set. We will add it. */ if ( state->outCondSpace == 0 ) { /* Note that unlike transitions, we start here with an empty key * list. Add the item */ state->outCondKeys.append( 0 ); } /* Allocate a cond space for the merged set. */ CondSpace *mergedCondSpace = addCondSpace( mergedCS ); state->outCondSpace = mergedCondSpace; /* FIXME: assumes one item always. */ /* Translate original condition values, making space for the new bit * (possibly) introduced by the condition embedding. */ for ( int cti = 0; cti < state->outCondKeys.length(); cti++ ) { long origVal = state->outCondKeys[cti]; long newVal = 0; /* For every set bit in the orig, find it's position in the merged * and set the bit appropriately. */ for ( CondSet::Iter csi = origCS; csi.lte(); csi++ ) { /* If set, find it in the merged set and flip the bit to 1. If * not set, there is nothing to do (convenient eh?) */ if ( origVal & (1 << csi.pos()) ) { /* The condition is set. Find the bit position in the * merged set. */ Action **cim = mergedCS.find( *csi ); long bitPos = (cim - mergedCS.data); newVal |= 1 << bitPos; } } if ( origVal != newVal ) state->outCondKeys[cti] = newVal; /* Now set the new bit appropriately. Since it defaults to zero we * only take action if sense is positive. */ if ( sense ) { Action **cim = mergedCS.find( condAction ); int pos = cim - mergedCS.data; state->outCondKeys[cti] = state->outCondKeys[cti] | (1 << pos); } } } }