void test(string lhss, string op, string rhss, string exps) {
    char* l1 = strdup(lhss.c_str());
    char* r1 = strdup(rhss.c_str());
    char* e1 = strdup(exps.c_str());

    V3Number lhnum (new FileLine ("ck",__LINE__), l1);
    V3Number rhnum (new FileLine ("ck",__LINE__), r1);
    V3Number expnum (new FileLine("ck",__LINE__), e1);

    V3Number gotnum (new FileLine("ck",__LINE__), expnum.width());

    if (op=="redOr")	 	gotnum.opRedOr		(lhnum);
    else if (op=="redAnd")	gotnum.opRedAnd		(lhnum);
    else if (op=="redXor")	gotnum.opRedXor		(lhnum);
    else if (op=="redXnor")	gotnum.opRedXnor	(lhnum);
    else if (op=="concat")	gotnum.opConcat		(lhnum,rhnum);
    else if (op=="repl")	gotnum.opRepl		(lhnum,rhnum);
    else if (op=="~")	 	gotnum.opNot		(lhnum);
    else if (op=="!")	 	gotnum.opLogNot		(lhnum);
    else if (op=="negate") 	gotnum.opNegate		(lhnum);
    else if (op=="+")	 	gotnum.opAdd		(lhnum,rhnum);
    else if (op=="-")	 	gotnum.opSub		(lhnum,rhnum);
    else if (op=="*")	 	gotnum.opMul		(lhnum,rhnum);
    else if (op=="/")	 	gotnum.opDiv		(lhnum,rhnum);
    else if (op=="%")	 	gotnum.opModDiv		(lhnum,rhnum);
    else if (op=="&")	 	gotnum.opAnd		(lhnum,rhnum);
    else if (op=="|")	 	gotnum.opOr		(lhnum,rhnum);
    else if (op=="<")	 	gotnum.opLt		(lhnum,rhnum);
    else if (op==">")	 	gotnum.opGt		(lhnum,rhnum);
    else if (op==">>")	 	gotnum.opShiftR		(lhnum,rhnum);
    else if (op=="<<")	 	gotnum.opShiftL		(lhnum,rhnum);
    else if (op=="==")	 	gotnum.opEq		(lhnum,rhnum);
    else if (op=="===")	 	gotnum.opCaseEq		(lhnum,rhnum);
    else if (op=="==?")	 	gotnum.opWildEq		(lhnum,rhnum);
    else if (op=="!=")	 	gotnum.opNeq		(lhnum,rhnum);
    else if (op=="!==")	 	gotnum.opCaseNeq	(lhnum,rhnum);
    else if (op=="!=?")	 	gotnum.opWildNeq	(lhnum,rhnum);
    else if (op=="<=")	 	gotnum.opLte		(lhnum,rhnum);
    else if (op==">=")	 	gotnum.opGte		(lhnum,rhnum);
    else if (op=="&&")	 	gotnum.opLogAnd		(lhnum,rhnum);
    else if (op=="||")	 	gotnum.opLogOr		(lhnum,rhnum);
    else v3fatalSrc("Bad opcode: "<<op);

    UINFO(0,"------- Test:\n"
	  <<"       "<<lhnum<<" "<<op<<endl
	  <<"       "<<rhnum<<endl
	  <<"     = "<<expnum<<endl
	  <<"    =? "<<gotnum<<endl);

    V3Number ok (new FileLine("ck",__LINE__), 1);
    ok.opCaseEq(expnum,gotnum);
    if (ok.toUInt()!=1) {
	v3fatalSrc("%Error:Test FAILED\n");
    }
}
    virtual void visit(AstGenCase* nodep, AstNUser*) {
	UINFO(9,"  GENCASE "<<nodep<<endl);
	AstNode* keepp = NULL;
	nodep->exprp()->iterateAndNext(*this);
	V3Case::caseLint(nodep);
	V3Width::widthParamsEdit(nodep);  // Param typed widthing will NOT recurse the body,
					  // don't trigger errors yet.
	V3Const::constifyParamsEdit(nodep->exprp());  // exprp may change
	AstConst* exprp = nodep->exprp()->castConst();
	// Constify
	for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=itemp->nextp()->castCaseItem()) {
	    for (AstNode* ep = itemp->condsp(); ep; ) {
		AstNode* nextp = ep->nextp(); //May edit list
		ep->iterateAndNext(*this);
		V3Const::constifyParamsEdit(ep); ep=NULL; // ep may change
		// cppcheck-suppress redundantAssignment
		ep = nextp;
	    }
	}
	// Item match
	for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=itemp->nextp()->castCaseItem()) {
	    if (!itemp->isDefault()) {
		for (AstNode* ep = itemp->condsp(); ep; ep=ep->nextp()) {
		    if (AstConst* ccondp = ep->castConst()) {
			V3Number match (nodep->fileline(), 1);
			match.opEq(ccondp->num(), exprp->num());
			if (!keepp && match.isNeqZero()) {
			    keepp = itemp->bodysp();
			}
		    } else {
			itemp->v3error("Generate Case item does not evaluate to constant");
		    }
		}
	    }
	}
	// Else default match
	for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=itemp->nextp()->castCaseItem()) {
	    if (itemp->isDefault()) {
		if (!keepp) keepp=itemp->bodysp();
	    }
	}
	// Replace
	if (keepp) {
	    keepp->unlinkFrBackWithNext();
	    nodep->replaceWith(keepp);
	}
	else nodep->unlinkFrBack();
	nodep->deleteTree(); nodep=NULL;
    }