/*
 * DBtechpaint2.c --
 *
 * Default composition rules.
 * Pretty complicated, unfortunately, so it's in a separate file.
 *
 *     *********************************************************************
 *     * Copyright (C) 1985, 1990 Regents of the University of California. *
 *     * Permission to use, copy, modify, and distribute this              *
 *     * software and its documentation for any purpose and without        *
 *     * fee is hereby granted, provided that the above copyright          *
 *     * notice appear in all copies.  The University of California        *
 *     * makes no representations about the suitability of this            *
 *     * software for any purpose.  It is provided "as is" without         *
 *     * express or implied warranty.  Export of this software outside     *
 *     * of the United States of America may require an export license.    *
 *     *********************************************************************
 */

#ifndef lint
static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/database/DBtpaint2.c,v 1.4 2009/12/30 13:42:33 tim Exp $";
#endif  /* not lint */

#include <stdio.h>
#include <ctype.h>

#include "utils/magic.h"
#include "utils/geometry.h"
#include "utils/utils.h"
#include "tiles/tile.h"
#include "utils/hash.h"
#include "database/database.h"
#include "database/databaseInt.h"
#include "utils/tech.h"
#include "textio/textio.h"

#define	SETPAINT(have,paint,plane,get) \
	if (IsDefaultPaint((have), (paint)) \
		&& TTMaskHasType(&DBPlaneTypes[(plane)], (have))) \
	    dbSetPaintEntry((have), (paint), (plane), (get))
#define	SETERASE(have,erase,plane,get) \
	if (IsDefaultErase((have), (erase)) \
		&& TTMaskHasType(&DBPlaneTypes[(plane)], (have))) \
	    dbSetEraseEntry((have), (erase), (plane), (get))

LayerInfo *dbTechLpPaint;

/* Forward declarations */

extern void dbTechPaintErasePlanes();
extern void dbComposePaintAllImages();
extern void dbComposeResidues();
extern void dbComposeContacts();
extern void dbComposePaintContact();
extern void dbComposeEraseContact();
extern void dbComposeSavedRules();
extern void dbComposeCompose();
extern void dbComposeDecompose();


/*
 * ----------------------------------------------------------------------------
 *
 * DBTechFinalCompose --
 *
 * Process all the contact erase/compose rules saved by DBTechAddCompose
 * when it was reading in the "compose" section of a technology file.
 * Also sets up the default paint/erase rules for contacts.
 *
 * Since by the end of this section we've processed all the painting
 * rules, we initialize the tables that say which planes get affected
 * by painting/erasing a given type.
 *
 * There's a great deal of work done here, so it's broken up into a
 * number of separate procedures, each of which implements a single
 * operation or default rule.  Most of the work deals with painting
 * and erasing contacts.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Modifies the paint/erase tables.
 *	Initializes DBTypePaintPlanesTbl[] and DBTypeErasePlanesTbl[].
 *
 * ----------------------------------------------------------------------------
 */

void
DBTechFinalCompose()
{
    TileType i;
    TileTypeBitMask testmask, *rMask;

    /* Default rules for painting/erasing contacts */
    dbComposePaintAllImages();
    dbComposeResidues();
    dbComposeContacts();

    /* Process rules saved from reading the "compose" section */
    dbComposeSavedRules();

    /* Build up exported tables */
    dbTechPaintErasePlanes();

    /* Adjust paint tables for any locked layers */
    for (i = TT_TECHDEPBASE; i < DBNumUserLayers; i++)
	if (!TTMaskHasType(&DBActiveLayerBits, i))
	    if (DBIsContact(i))
		DBLockContact(i);
    for (i = DBNumUserLayers; i < DBNumTypes; i++)
    {
	rMask = DBResidueMask(i);
	TTMaskAndMask3(&testmask, &DBActiveLayerBits, rMask);
	if (!TTMaskEqual(&testmask, rMask))
	{
	    TTMaskClearType(&DBActiveLayerBits, i);
	    DBLockContact(i);
	}
    }

    /* Diagnostic */
    /* dbTechPrintPaint("DBTechFinalCompose", TRUE, FALSE); */
    /* dbTechPrintPaint("DBTechFinalCompose", FALSE, FALSE); */
}

/*
 * ----------------------------------------------------------------------------
 *
 * dbTechPaintErasePlanes --
 *
 * Fill in the tables telling which planes get affected
 * by painting and erasing.  One may take the naive view that only
 * the planes on which a type is defined can be affected, but then
 * one can't define arbitrary composite types.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Fills in DBTypePaintPlanesTbl[] and DBTypeErasePlanesTbl[].
 *
 * ----------------------------------------------------------------------------
 */

void
dbTechPaintErasePlanes()
{
    TileType t, s;
    int pNum;

    /* Space tiles are special: they may appear on any plane except router */
    DBTypePaintPlanesTbl[TT_SPACE] = ~(PlaneNumToMaskBit(PL_ROUTER));
    DBTypeErasePlanesTbl[TT_SPACE] = ~(PlaneNumToMaskBit(PL_ROUTER));

    /* Skip TT_SPACE */
    for (t = 1; t < DBNumTypes; t++)
    {
	DBTypePaintPlanesTbl[t] = DBTypeErasePlanesTbl[t] = 0;

	for (pNum = PL_PAINTBASE; pNum < DBNumPlanes; pNum++)
	    for (s = 0; s < DBNumTypes; s++)
	    {
		if (DBStdPaintEntry(s, t, pNum) != s)
		    DBTypePaintPlanesTbl[t] |= PlaneNumToMaskBit(pNum);
		if (DBStdEraseEntry(s, t, pNum) != s)
		    DBTypeErasePlanesTbl[t] |= PlaneNumToMaskBit(pNum);
	    }
    }
}

/*
 * ----------------------------------------------------------------------------
 *
 * dbComposePaintAllImages --
 *
 * Painting the primary type of a contact layer paints its image
 * over all types on each image's plane.  (The only types that
 * should ever be painted are primary types.)
 *
 * This rule is called first because it may be overridden by later
 * rules, or by explicit composition rules.
 *
 * Only affects paint entries that haven't already been set to other
 * values by explicit paint rules.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Modifies paint/erase tables.
 *
 * ----------------------------------------------------------------------------
 */

void
dbComposePaintAllImages()
{
    TileType tPaint, s, res;
    LayerInfo *lp;
    int p, n;

    /* Iterate over primary types only */
    for (n = 0; n < dbNumContacts; n++)
    {
        lp = dbContactInfo[n];
        tPaint = lp->l_type;
	if (tPaint >= DBNumUserLayers) continue;
	for (res = TT_TECHDEPBASE; res < DBNumTypes; res++)
	{
	    if (TTMaskHasType(&lp->l_residues, res))
	    {
		p = DBPlane(res);
		for (s = TT_TECHDEPBASE; s < DBNumTypes; s++)
		    if (DBPlane(s) == p)
			SETPAINT(s, tPaint, p, tPaint);

		if (IsDefaultPaint(TT_SPACE, tPaint))
		    dbSetPaintEntry(TT_SPACE, tPaint, p, tPaint);
	    }
	}
    }
}


/*
 * ----------------------------------------------------------------------------
 *
 * dbComposeResidues --
 *
 * The behavior of a contact type when other, non-contact types are
 * painted over it or erased from it is derived from the behavior of
 * its residue types.
 *
 *  1.	If painting doesn't affect a contact's residue on a plane,
 *	it doesn't affect the contact's image on that plane either.
 *	This allows, for example, painting metal1 over a contact
 *	"containing" metal1 without breaking the contact.
 *
 *  2.	If painting or erasing a type affects a residue of a
 *	contact, the image's connectivity to adjacent planes
 *	is broken and the image is replaced by the result of
 *	painting or erasing over the residue.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Modifies paint/erase tables.
 *
 * ----------------------------------------------------------------------------
 */

void
dbComposeResidues()
{
    LayerInfo *lp;
    TileType s, res;
    int n;

    /* Painting that doesn't affect the residue doesn't affect the contact. */

    for (n = 0; n < dbNumContacts; n++)
    {
	lp = dbContactInfo[n];

	for (res = TT_TECHDEPBASE; res < DBNumUserLayers; res++)
	{
	    if (TTMaskHasType(&lp->l_residues, res))
	    {
		for (s = TT_TECHDEPBASE; s < DBNumUserLayers; s++)
		    if (!PAINTAFFECTS(res, s))
			SETPAINT(lp->l_type, s, DBPlane(res), lp->l_type);
	    }
	}
    }
}

/*
 * ----------------------------------------------------------------------------
 *
 * dbComposeContacts --
 *
 * This procedure handles the rules for composition of contact types.
 * We look at the results of painting each type of contact in
 * dbContactInfo[] over all other contact types.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Modifies paint/erase tables.
 *
 * ----------------------------------------------------------------------------
 */

void
dbComposeContacts()
{
    LayerInfo *lpImage, *lpPaint;
    int m, pNum;
    TileTypeBitMask *rmask;
    TileType n, ttype, itype, presult, eresult;

    for (m = 0; m < dbNumContacts; m++)
    {
        lpImage = dbContactInfo[m];	/* Existing contact image */
	for (n = TT_TECHDEPBASE; n < DBNumUserLayers; n++)
	{
	    lpPaint = &dbLayerInfo[n];	/* Layer being painted or erased */
	    if (lpImage->l_type != n)
		dbComposePaintContact(lpImage, lpPaint);
	    dbComposeEraseContact(lpImage, lpPaint);
	}
    }

    /* For stacking types, determine the result of painting or erasing	*/
    /* each stacking type by applying the paint and erase results of	*/
    /* each of its residues in sequence.				*/

    for (itype = 0; itype < DBNumTypes; itype++)
    {
	for (n = DBNumUserLayers; n < DBNumTypes; n++)
	{
	    lpPaint = &dbLayerInfo[n];	/* Layer being painted or erased */
	    rmask = &lpPaint->l_residues;
	    for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
	    {
		presult = eresult = itype;
		for (ttype = TT_TECHDEPBASE; ttype < DBNumUserLayers; ttype++)
		    if (TTMaskHasType(rmask, ttype))
		    {
			presult = DBStdPaintEntry(presult, ttype, pNum);
			eresult = DBStdEraseEntry(eresult, ttype, pNum);
		    }
		SETPAINT(itype, n, pNum, presult);
		SETERASE(itype, n, pNum, eresult);
	    }
	}
    }
}


/*
 * ----------------------------------------------------------------------------
 * dbComposePaintContact --
 *
 * Construct the painting rules for painting type lpPaint over
 * the contact image lpImage.
 * ----------------------------------------------------------------------------
 */

void
dbComposePaintContact(
    LayerInfo *lpImage,
    LayerInfo *lpPaint)
{
    int pNum;
    PlaneMask pmask, pshared;
    LayerInfo *lp;
    TileTypeBitMask rmask, cmask;
    TileType newtype, ptype, itype;
    bool overlap;

    /*
     * If the residues of lpImage and lpPaint can be merged without
     * affecting any of the layers, then we merge them, and look for
     * any contact matching the merged residues.  If none is found,
     * and the planes do not overlap, then just paint the new
     * contact.  If they cannot be merged, then the original contact
     * is dissolved and replaced by its residues.
     */

    pmask = lpImage->l_pmask & lpPaint->l_pmask;
    overlap = (pmask == 0) ? FALSE : TRUE;

    if (overlap)
    {
	for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
	{
	    if (PlaneMaskHasPlane(pmask, pNum))
	    {
		ptype = DBPlaneToResidue(lpPaint->l_type, pNum);
		itype = DBPlaneToResidue(lpImage->l_type, pNum);
		if (ptype != itype)
		    break;
	    }
	}
	if (pNum == DBNumPlanes)
	{
	    /* Residues are compatible; check for a contact type with	*/
	    /* the merged residues (explicitly defined stacked contact	*/
	    /* type)							*/

	    TTMaskZero(&rmask);
	    TTMaskSetMask3(&rmask, &lpImage->l_residues, &lpPaint->l_residues);
	    dbTechMatchResidues(&rmask, &cmask, TRUE);

	    /* Implicitly-defined stacking types override any	*/
	    /* explicitly-defined types, or havoc results.	*/
	    /* This allows one to create a type such as "pad"	*/
	    /* having the same residues as "m123c" without	*/
	    /* magic confusing them in the paint tables.	*/

	    newtype = DBTechFindStacking(lpImage->l_type, lpPaint->l_type);

	    if (TTMaskIsZero(&cmask) || (newtype != -1))
	    {
		/* If there is a stacking contact type, use it */

		if (newtype >= DBNumUserLayers)
		{
		    pshared = lpImage->l_pmask & lpPaint->l_pmask;
		    for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
			if (PlaneMaskHasPlane(pshared, pNum))
			    SETPAINT(lpImage->l_type, lpPaint->l_type,
					pNum, newtype);
		}

		else if (lpPaint->l_isContact && (lpImage->l_type < DBNumUserLayers))
		{
		    /* Original contact is replaced by the new one where
		     * the planes overlap, and is dissolved into its
		     * residues where they don't.
		     * In this condition, the residues of image must be
		     * non-contact types.
		     */

		    for (itype = TT_TECHDEPBASE; itype < DBNumUserLayers; itype++)
		    {
			if (TTMaskHasType(&lpImage->l_residues, itype))
			{
		            if (TTMaskHasType(&lpPaint->l_residues, itype))
			    {
				SETPAINT(lpImage->l_type, lpPaint->l_type,
						DBPlane(itype), lpPaint->l_type);
			    }
			    else
			    {
				SETPAINT(lpImage->l_type, lpPaint->l_type,
						DBPlane(itype), itype);
			    }
			}
		    }
		}
		else if (lpPaint->l_isContact &&
			!TTMaskHasType(&lpImage->l_residues, lpPaint->l_type))
		{
		    /* Original contact is replaced by the new one where
		     * the planes overlap, and is dissolved into its
		     * residues where they don't.
		     * In this condition, the residues of image are contact
		     * types.
		     */

		    for (itype = TT_TECHDEPBASE; itype < DBNumUserLayers; itype++)
		    {
			if (TTMaskHasType(&lpImage->l_residues, itype))
			{
		            if (TTMaskHasType(&lpPaint->l_residues, itype))
			    {
				SETPAINT(lpImage->l_type, lpPaint->l_type,
						DBPlane(itype), lpPaint->l_type);
			    }
			}
		    }
		}
		else
		{
		    /* Painting a residue type on top of a contact	*/
		    /* with a compatible residue does nothing, as does	*/
		    /* painting one of the types of a stacked contact	*/
		    /* on the stacking contact type.  In the plane of	*/
		    /* the image type, this is non-default behavior.	*/

		    SETPAINT(lpImage->l_type, lpPaint->l_type,
				DBPlane(lpImage->l_type), lpImage->l_type);
		}
	    }
	    else
	    {
		/* Presumably there is at most one contact type here	*/
		for (newtype = TT_TECHDEPBASE; newtype < DBNumUserLayers; newtype++)
		{
		    if (TTMaskHasType(&cmask, newtype))
		    {
			lp = &dbLayerInfo[newtype];
			for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
			    if (PlaneMaskHasPlane(lp->l_pmask, pNum))
				SETPAINT(lpImage->l_type, lpPaint->l_type,
					pNum, newtype);
			break;
		    }
		}
	    }
	}
	else
	{
	    /* Image and paint types overlap in a plane, but	*/
	    /* residues are not compatible.  Replace image with the	*/
	    /* residues of image, except on the overlapping plane.	*/

	    for (ptype = TT_TECHDEPBASE; ptype < DBNumUserLayers; ptype++)
		if (TTMaskHasType(&lpImage->l_residues, ptype))
		    if (ptype != itype)
			SETPAINT(lpImage->l_type, lpPaint->l_type,
					DBPlane(ptype), ptype);
	}
    }
    else if (lpPaint->l_isContact)
    {
	/* No overlapping planes, and both paint & image types are contacts */

	TTMaskZero(&rmask);
	TTMaskSetMask3(&rmask, &lpImage->l_residues, &lpPaint->l_residues);
	dbTechMatchResidues(&rmask, &cmask, TRUE);
	if (!TTMaskIsZero(&cmask))
	{
	    /* Replace image with the new contact type */
	    for (newtype = TT_TECHDEPBASE; newtype < DBNumUserLayers; newtype++)
	    {
		if (TTMaskHasType(&cmask, newtype))
		{
		    lp = &dbLayerInfo[newtype];
		    for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
			if (PlaneMaskHasPlane(lp->l_pmask, pNum))
			    SETPAINT(lpImage->l_type, lpPaint->l_type,
					pNum, newtype);
		}
	    }
	}
	/* else default paint/erase behavior */
    }
}

/*
 * ----------------------------------------------------------------------------
 *
 * dbComposeSubsetResidues --
 *
 *	Create a mask of all contact types whose residues are subsets
 *	of the "have" type but are not supersets of the "erase" type.
 *
 * Results:
 *	True if residues of types in outMask overlap, False if not.
 *
 * Side Effects:
 *	outMask is filled with the mask of types.
 *
 * ----------------------------------------------------------------------------
 */

bool
dbComposeSubsetResidues(
    LayerInfo *lpImage,
    LayerInfo *lpErase,
    TileTypeBitMask *outMask)
{
    TileTypeBitMask ires;
    TileTypeBitMask smask, overlapmask;
    LayerInfo *li;
    int n;
    bool rval = FALSE;

    /* The residues of lpImage must themselves be decomposed if lpImage	*/
    /* is a stacking type.						*/

    TTMaskZero(&ires);
    if (lpImage->l_type >= DBNumUserLayers)
    {
	for (n = 0; n < dbNumContacts; n++)
	{
	    li = dbContactInfo[n];
	    if (TTMaskHasType(&lpImage->l_residues, li->l_type))
		TTMaskSetMask(&ires, &li->l_residues);
	}
    }
    else
	TTMaskSetMask(&ires, &lpImage->l_residues);

    /*
     * Generate a mask of all contact types whose residue masks are
     * subsets of the residues of lpImage.  These are all types
     * which contain no residues that are not part of lpImage.
     */

    TTMaskZero(outMask);
    TTMaskZero(&overlapmask);
    for (n = 0; n < dbNumContacts; n++)
    {
        li = dbContactInfo[n];
	TTMaskAndMask3(&smask, &li->l_residues, &ires);
	if (TTMaskEqual(&smask, &li->l_residues))
	{
	    /* The residues of type cannot be a superset of the */
	    /* residues of the erase type			*/

	    TTMaskAndMask3(&smask, &li->l_residues, &lpErase->l_residues);
	    if (!TTMaskEqual(&smask, &lpErase->l_residues))
	    {
		TTMaskSetType(outMask, li->l_type);

		/* Check if the residues of type overlap one of	*/
		/* the types already generated.			*/

		TTMaskAndMask3(&smask, &overlapmask, &li->l_residues);
		if (TTMaskIsZero(&smask))
		    TTMaskSetMask(&overlapmask, &li->l_residues);
		else
		    rval = TRUE;
	    }
	}
    }
    return rval;
}


/*
 * ----------------------------------------------------------------------------
 * dbComposeEraseContact --
 *
 * Construct the erasing rules for erasing type lpErase from
 * the contact image lpImage.
 * ----------------------------------------------------------------------------
 */

void
dbComposeEraseContact(
    LayerInfo *lpImage,
    LayerInfo *lpErase)
{
    int pNum;
    PlaneMask pmask;
    LayerInfo *lp;
    TileTypeBitMask cmask;
    TileType itype;
    bool overlap;

    /* The erased planes generally end up with space, so we generate	*/
    /* space as default behavior.  This may be altered in specific	*/
    /* cases, below.							*/

    /* The specific check for lpImage as a stacking contact is not	*/
    /* necessary, but prevents generation of non-default rules in	*/
    /* cases which cannot occur.					*/

    for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
	if (PlaneMaskHasPlane(lpErase->l_pmask, pNum))
	    if ((lpImage->l_type < DBNumUserLayers)
			|| (pNum == DBPlane(lpImage->l_type)))
		SETERASE(lpImage->l_type, lpErase->l_type, pNum, TT_SPACE);

    /* Erasing self should always leave space; otherwise    */
    /* the "undo" records aren't symmetric, which screws    */
    /* everything up.					    */

    if (lpImage->l_type == lpErase->l_type) return;

    /* If planes of HAVE and ERASE types don't overlap, we can erase	*/
    /* the ERASE type without affecting the image, so we're done.	*/

    pmask = lpImage->l_pmask & lpErase->l_pmask;
    overlap = (pmask == 0) ? FALSE : TRUE;
    if (!overlap) return;

    /* Find what contacts are subsets of this one and generate a mask	*/
    /* of the types that might be left over after erasing this one.	*/
    /* If those types overlap, leave the existing type alone.	If they	*/
    /* don't, then paint them.						*/

    overlap = dbComposeSubsetResidues(lpImage, lpErase, &cmask);

    if (overlap)
    {
	for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
	    if (PlaneMaskHasPlane(lpImage->l_pmask, pNum))
//		SETERASE(lpImage->l_type, lpErase->l_type, pNum, TT_SPACE);
		SETERASE(lpImage->l_type, lpErase->l_type, pNum, lpImage->l_type);
    }
    else
    {
	pmask = lpImage->l_pmask & (~(lpErase->l_pmask));
	for (itype = TT_TECHDEPBASE; itype < DBNumTypes; itype++)
	    if (TTMaskHasType(&cmask, itype))
	    {
		lp = &dbLayerInfo[itype];
		pmask &= ~(lp->l_pmask);
		for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
		    if (PlaneMaskHasPlane(lp->l_pmask, pNum))
			if ((lpImage->l_type < DBNumUserLayers)
				|| (pNum == DBPlane(lpImage->l_type)))
			    SETERASE(lpImage->l_type, lpErase->l_type, pNum, itype);
	    }

	/* If there are any planes in the image which have not been	*/
	/* accounted for, then we decompose these into the residues	*/
	/* of the image.						*/

	for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
	    if (PlaneMaskHasPlane(pmask, pNum))
	    {
		itype = DBPlaneToResidue(lpImage->l_type, pNum);
		SETERASE(lpImage->l_type, lpErase->l_type, pNum, itype);
	    }

    }

    /* Diagnostic (to be removed or commented out) */

/*
    if (!TTMaskIsZero(&cmask))
    {
	TxPrintf("Have %s, Erase %s: ", DBTypeLongNameTbl[lpImage->l_type],
		DBTypeLongNameTbl[lpErase->l_type]);
	for (itype = TT_TECHDEPBASE; itype < DBNumTypes; itype++)
	    if (TTMaskHasType(&cmask, itype))
		TxPrintf("%s ", DBTypeLongNameTbl[itype]);
	if (overlap)
	    TxPrintf("overlapping");
	TxPrintf("\n");
    }
*/

}

/*
 * ----------------------------------------------------------------------------
 * DBLockContact --
 *
 * This procedure modifies the erase tables so that the specified contact
 * type cannot be erased by erasing one of its residues, which is the default
 * behavior.
 * ----------------------------------------------------------------------------
 */

void
DBLockContact(
    TileType ctype)
{
    LayerInfo *lpImage, *lpPaint;
    TileType c, n, itype, eresult;
    TileTypeBitMask *rmask;
    int m, pNum;

    /* Have type, Erase * --> Result is type */

    lpPaint = &dbLayerInfo[ctype];
    for (n = TT_TECHDEPBASE; n < DBNumTypes; n++)
    {
	if (n == ctype) continue;

	/* Avoid the case, e.g., if ctype is pc+v, then pc+v - pc = v	*/
	/* is still valid if pc is an active layer.			*/

	if (ctype >= DBNumUserLayers)
	{
	    rmask = DBResidueMask(ctype);
	    if (TTMaskHasType(rmask, n))
		if (TTMaskHasType(&DBActiveLayerBits, n))
		    continue;
	}

	for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
	    if (PlaneMaskHasPlane(lpPaint->l_pmask, pNum))
		SETERASE(ctype, n, pNum, ctype);
    }
}

/*
 * ----------------------------------------------------------------------------
 * DBUnlockContact --
 *
 * This procedure reverses the operation of DBLockContact, allowing a contact
 * to be erased by erasing one of its residues.  This is the same code as
 * dbComposeContacts(), for a single contact type.
 * ----------------------------------------------------------------------------
 */

void
DBUnlockContact(
    TileType ctype)
{
    LayerInfo *lpImage, *lpPaint;
    TileType n, itype, eresult;
    TileTypeBitMask *rmask;
    int m, pNum;

    lpImage = &dbLayerInfo[ctype];

    for (n = TT_TECHDEPBASE; n < DBNumUserLayers; n++)
    {
	    lpPaint = &dbLayerInfo[n];
	    dbComposeEraseContact(lpImage, lpPaint);
    }

    /* To be done (maybe):  revert rules for stacked contact types */
}

/*
 * ----------------------------------------------------------------------------
 *
 * dbComposeSavedRules --
 *
 * Process all the contact compose/decompose rules saved
 * when the compose section of the tech file was read.
 * Each pair on the RHS of one of these rules must contain
 * exactly one contact type that spans the same set of planes
 * as the image type.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Modifies entries in the paint and erase tables.
 *
 * ----------------------------------------------------------------------------
 */

void
dbComposeSavedRules()
{
    LayerInfo *lpContact;
    TileType imageType;
    TypePair *pair;
    Rule *rule;
    int n;

    for (n = 0; n < dbNumSavedRules; n++)
    {
	rule = &dbSavedRules[n];
	lpContact = &dbLayerInfo[rule->r_result];
	imageType = lpContact->l_type;

	for (pair = rule->r_pairs; pair < &rule->r_pairs[rule->r_npairs];
		    	pair++)
    	{
	    dbComposeDecompose(imageType, pair->rp_a, pair->rp_b);
	    dbComposeDecompose(imageType, pair->rp_b, pair->rp_a);
	    if (rule->r_ruleType == RULE_COMPOSE)
	    {
	        dbComposeCompose(imageType, pair->rp_a, pair->rp_b);
	        dbComposeCompose(imageType, pair->rp_b, pair->rp_a);
	    }
	}
    }
}

/*
 * ----------------------------------------------------------------------------
 * dbComposeDecompose --
 *
 * Painting componentType over imageType is a no-op.
 * Erasing componentType from imageType gives either the image of
 * remainingType, if one exists on DBPlane(imageType), or else
 * gives the residue of imageType.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Modifies the paint/erase tables as described above.
 *	Indicates that these modifications are not default
 *	rules by setting the corresponding bits in the tables
 *	dbNotDefaultPaintTbl[] and dbNotDefaultEraseTbl[].
 * ----------------------------------------------------------------------------
 */

void
dbComposeDecompose(
    TileType imageType,
    TileType componentType,
    TileType remainingType)
{
    int pNum = DBPlane(imageType);
    TileType resultType;

    /* Painting componentType is a no-op */
    dbSetPaintEntry(imageType, componentType, pNum, imageType);
    TTMaskSetType(&dbNotDefaultPaintTbl[imageType], componentType);

    /* Which residue belongs to the plane pNum? */
    resultType = DBPlaneToResidue(imageType, pNum);

    /*
     * Erasing componentType gives remainingType or breaks
     * imageType's contact.
     */
    dbSetEraseEntry(imageType, componentType, pNum, resultType);
    TTMaskSetType(&dbNotDefaultEraseTbl[imageType], componentType);
}

/*
 * ----------------------------------------------------------------------------
 * dbComposeCompose --
 *
 * Painting paintType over existingType gives imageType.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Modifies the paint/erase tables as described above.
 *	Indicates that these modifications are not default
 *	rules by setting the corresponding bits in the tables
 *	dbNotDefaultPaintTbl[] and dbNotDefaultEraseTbl[].
 * ----------------------------------------------------------------------------
 */

void
dbComposeCompose(
    TileType imageType,
    TileType existingType,
    TileType paintType)
{
    int pNum = DBPlane(imageType);

    if (PlaneMaskHasPlane(LayerPlaneMask(existingType), pNum))
    {
	dbSetPaintEntry(existingType, paintType, pNum, imageType);
	TTMaskSetType(&dbNotDefaultPaintTbl[existingType], paintType);
    }
}
