/*
 * PlowRandom.c --
 *
 * Plowing.
 * Random testing: generate a whole bunch of random plows
 * and make sure each one produces a design-rule-correct
 * result that has the same extracted circuit (minus the
 * parasitics) as the initial cell.
 *
 *     *********************************************************************
 *     * 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/plow/PlowRandom.c,v 1.1.1.1 2008/02/03 20:43:50 tim Exp $";
#endif  /* not lint */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <unistd.h>
#if defined(SYSV) || defined(EMSCRIPTEN)
#include <fcntl.h>
#endif

#include "utils/magic.h"
#include "utils/geometry.h"
#include "tiles/tile.h"
#include "utils/hash.h"
#include "database/database.h"
#include "plow/plowInt.h"
#include "textio/textio.h"
#include "utils/undo.h"
#include "utils/signals.h"
#include "extract/extract.h"

/* C99 compat */
#include "drc/drc.h"

/* Imports from PlowMain.c */
extern CellDef *plowYankDef;

/* Forward declarations */
int plowFindFirstError();
void plowGenRect();


/*
 * ----------------------------------------------------------------------------
 *
 * PlowRandomTest --
 *
 * Random testing: generate a whole bunch of random plows
 * and make sure each one produces a design-rule-correct
 * result that has the same extracted circuit (minus the
 * parasitics) as the initial cell.
 *
 * Output the coordinates and direction of each plow as
 * we go, flagging it as one producing a DRC violation
 * or a connectivity violation as we go.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Writes a whole bunch of information via TxPrintf(),
 *	as described above.
 *
 * ----------------------------------------------------------------------------
 */

void
PlowRandomTest(def)
    CellDef *def;
{
#ifdef	notdef
    static char *tempgood = "/tmp/PlowGoodaXXXXX";
    static char *temptemp = "/tmp/PlowTempaXXXXX";
    char goodName[64], tempName[64], goodExt[64], tempExt[64];
    char command[256];
#endif	/* notdef */
    static int dirs[] = { GEO_NORTH, GEO_SOUTH, GEO_EAST, GEO_WEST };
    static char *dirnames[] = { "up", "down", "right", "left" };
    Rect plowRect;
    int dir, plowDir;
    Plane *savePlane;

#ifdef	notdef
    strcpy(goodName, tempgood);
    strcpy(tempName, temptemp);
    mkstemp(goodName);
    mkstemp(tempName);
    sprintf(goodExt, "%s.ext", goodName);
    sprintf(tempExt, "%s.ext", tempName);

    /* Generate "good" extracted file */
    savePlane = ExtCell(def, goodName, FALSE);
    ExtRevertSubstrate(def, savePlane);
    (void) sprintf(command, "sedplow %s", goodExt);
    system(command);
#endif	/* notdef */

    /* Repeatedly try to break plowing */
    while (!SigInterruptPending)
    {
	/* Generate a random plow direction and rectangle */
	dir = plowGenRandom(0, 3);
	plowDir = dirs[dir];
	plowGenRect(&def->cd_bbox, &plowRect);
	(void) Plow(def, &plowRect, &DBAllTypeBits, plowDir);
	TxPrintf("%s %d %d %d %d\n", dirnames[dir],
			plowRect.r_xbot, plowRect.r_ybot,
			plowRect.r_xtop, plowRect.r_ytop);
	TxFlush();

	/* Finish the design-rule check and count any errors */
	DRCCatchUp();
	if (DBSrPaintArea((Tile *) NULL, def->cd_planes[PL_DRC_ERROR],
		    &def->cd_bbox, &DBAllButSpaceBits,
		    plowFindFirstError, (ClientData) NULL))
	{
	    TxPrintf("%s %d %d %d %d: DRC error\n", dirnames[dir],
			plowRect.r_xbot, plowRect.r_ybot,
			plowRect.r_xtop, plowRect.r_ytop);
	    TxFlush();
	}

	/* Turn off the modified bit so we get the same timestamp */
	def->cd_flags &= ~CDMODIFIED;

#ifdef	notdef
	/* Extract to the temp file */
	savePlane = ExtCell(def, tempName, FALSE);
	ExtRevertSubstrate(def, savePlane);
	(void) sprintf(command, "sedplow %s", tempExt);
	system(command);

	/* Check for any differences */
	if (plowFileDiff(goodExt, tempExt))
	{
	    TxPrintf("%s %d %d %d %d: EXT error\n", dirnames[dir],
			plowRect.r_xbot, plowRect.r_ybot,
			plowRect.r_xtop, plowRect.r_ytop);
	    TxFlush();
	}
#endif	/* notdef */

	/* Make sure there's always something to undo */
	DBPutLabel(def, &def->cd_bbox, -1, "dummylabel", TT_SPACE, 0, 0);

	/* Undo */
	UndoBackward(1);
    }

#ifdef	notdef
    (void) unlink(goodExt);
    (void) unlink(tempExt);
#endif	/* notdef */
}

/*
 * ----------------------------------------------------------------------------
 *
 * plowFindFirstError --
 *
 * Filter procedure called via DBSrPaintArea by PlowRandomTest()
 * above, on the PL_DRC_ERROR plane, looking for non-space tiles.
 *
 * Results:
 *	Returns 1 always, to force DBSrPaintArea to abort and
 *	return 1.
 *
 * Side effects:
 *	None.
 *
 * ----------------------------------------------------------------------------
 */

int
plowFindFirstError(Tile *tile, TileType dinfo, ClientData clientdata)
{
    return (1);
}

/*
 * ----------------------------------------------------------------------------
 *
 * plowGenRect --
 *
 * Generate a random plow rectangle.
 * This rectangle is guaranteed to lie within the bounding box of
 * the cell.  The four coordinates are chosen randomly from a uniform
 * distribution, and then the rectangle is flipped to insure that its
 * upper-right is above and to the right of its lower-left.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Fills in the rectangle pointed to by 'r'.
 *
 * ----------------------------------------------------------------------------
 */

void
plowGenRect(bbox, r)
    Rect *bbox;	/* Bounding box of the cell being plowed */
    Rect *r;		/* Fill in this rectangle */
{
    int temp;

    r->r_xbot = plowGenRandom(bbox->r_xbot, bbox->r_xtop);
    r->r_xtop = plowGenRandom(bbox->r_xbot, bbox->r_xtop);
    r->r_ybot = plowGenRandom(bbox->r_ybot, bbox->r_ytop);
    r->r_ytop = plowGenRandom(bbox->r_ybot, bbox->r_ytop);
    if (r->r_xbot == r->r_xtop) r->r_xtop = r->r_xbot + 1;
    if (r->r_ybot == r->r_ytop) r->r_ytop = r->r_ybot + 1;
    if (r->r_xbot > r->r_xtop)
    {
	temp = r->r_xtop;
	r->r_xtop = r->r_xbot;
	r->r_xbot = temp;
    }
    if (r->r_ybot > r->r_ytop)
    {
	temp = r->r_ytop;
	r->r_ytop = r->r_ybot;
	r->r_ybot = temp;
    }
}

/*
 * ----------------------------------------------------------------------------
 *
 * plowGenRandom --
 *
 * Generate a random integer chosen from the integers lo, lo+1, ..., hi,
 * with each integer having equal probability.
 *
 * Results:
 *	Returns the integer described above.
 *
 * Side effects:
 *	None.
 *
 * ----------------------------------------------------------------------------
 */

int
plowGenRandom(lo, hi)
    int lo, hi;		/* Inclusive bounds for the integer we'll generate */
{
    int range = hi - lo + 1;
#if defined(SYSV) || defined(EMSCRIPTEN)
    int r = rand();
#else
    int r = random();
#endif	/* SYSV */

    return ((r % range) + lo);
}

/*
 * ----------------------------------------------------------------------------
 *
 * plowFileDiff --
 *
 * Compare two files for equality.
 *
 * Results:
 *	Returns TRUE if they are identical and the same length,
 *	FALSE if they differ.
 *
 * Side effects:
 *	None.
 *
 * ----------------------------------------------------------------------------
 */

bool
plowFileDiff(file1, file2)
    char *file1;
    char *file2;
{
    char b1[BUFSIZ], b2[BUFSIZ];
    int f1 = -1, f2 = -1;
    int n1, n2;
    bool ret = FALSE;

    if ((f1 = open(file1, O_RDONLY, 0)) < 0) goto done;
    if ((f2 = open(file2, O_RDONLY, 0)) < 0) goto done;

    while ((n1 = read(f1, b1, BUFSIZ)) > 0)
    {
	n2 = read(f2, b2, BUFSIZ);
	if (n1 != n2 || bcmp(b1, b2, n1) != 0)
	    goto done;
    }
    ret = TRUE;

done:
    if (f1 >= 0)
        (void) close(f1);
    if (f2 >= 0)
        (void) close(f2);
    return (ret);
}
