diff --git a/src/backend/regex/regc_nfa.c b/src/backend/regex/regc_nfa.c
index 20660b4..b39c699 100644
*** a/src/backend/regex/regc_nfa.c
--- b/src/backend/regex/regc_nfa.c
*************** destroystate(struct nfa * nfa,
*** 321,326 ****
--- 321,329 ----
  
  /*
   * newarc - set up a new arc within an NFA
+  *
+  * This function checks to make sure that no duplicate arcs are created.
+  * In general we never want duplicates.
   */
  static void
  newarc(struct nfa * nfa,
*************** newarc(struct nfa * nfa,
*** 344,354 ****
  		return;
  	}
  
! 	/* check for duplicates */
! 	for (a = from->outs; a != NULL; a = a->outchain)
! 		if (a->to == to && a->co == co && a->type == t)
! 			return;
  
  	a = allocarc(nfa, from);
  	if (NISERR())
  		return;
--- 347,386 ----
  		return;
  	}
  
! 	/* check for duplicate arc, using whichever chain is shorter */
! 	if (from->nouts <= to->nins)
! 	{
! 		for (a = from->outs; a != NULL; a = a->outchain)
! 			if (a->to == to && a->co == co && a->type == t)
! 				return;
! 	}
! 	else
! 	{
! 		for (a = to->ins; a != NULL; a = a->inchain)
! 			if (a->from == from && a->co == co && a->type == t)
! 				return;
! 	}
  
+ 	/* no dup, so create the arc */
+ 	createarc(nfa, t, co, from, to);
+ }
+ 
+ /*
+  * createarc - create a new arc within an NFA
+  *
+  * This function must *only* be used after verifying that there is no existing
+  * identical arc (same type/color/from/to).
+  */
+ static void
+ createarc(struct nfa * nfa,
+ 		  int t,
+ 		  pcolor co,
+ 		  struct state * from,
+ 		  struct state * to)
+ {
+ 	struct arc *a;
+ 
+ 	/* the arc is physically allocated within its from-state */
  	a = allocarc(nfa, from);
  	if (NISERR())
  		return;
*************** newarc(struct nfa * nfa,
*** 360,373 ****
  	a->from = from;
  
  	/*
! 	 * Put the new arc on the beginning, not the end, of the chains. Not only
! 	 * is this easier, it has the very useful side effect that deleting the
! 	 * most-recently-added arc is the cheapest case rather than the most
! 	 * expensive one.
  	 */
  	a->inchain = to->ins;
  	to->ins = a;
  	a->outchain = from->outs;
  	from->outs = a;
  
  	from->nouts++;
--- 392,410 ----
  	a->from = from;
  
  	/*
! 	 * Put the new arc on the beginning, not the end, of the chains; it's
! 	 * simpler here, and freearc() is the same cost either way.  See also the
! 	 * logic in moveins() and its cohorts, as well as fixempties().
  	 */
  	a->inchain = to->ins;
+ 	a->inchainRev = NULL;
+ 	if (to->ins)
+ 		to->ins->inchainRev = a;
  	to->ins = a;
  	a->outchain = from->outs;
+ 	a->outchainRev = NULL;
+ 	if (from->outs)
+ 		from->outs->outchainRev = a;
  	from->outs = a;
  
  	from->nouts++;
*************** freearc(struct nfa * nfa,
*** 433,439 ****
  {
  	struct state *from = victim->from;
  	struct state *to = victim->to;
! 	struct arc *a;
  
  	assert(victim->type != 0);
  
--- 470,476 ----
  {
  	struct state *from = victim->from;
  	struct state *to = victim->to;
! 	struct arc *predecessor;
  
  	assert(victim->type != 0);
  
*************** freearc(struct nfa * nfa,
*** 443,487 ****
  
  	/* take it off source's out-chain */
  	assert(from != NULL);
! 	assert(from->outs != NULL);
! 	a = from->outs;
! 	if (a == victim)			/* simple case:  first in chain */
  		from->outs = victim->outchain;
  	else
  	{
! 		for (; a != NULL && a->outchain != victim; a = a->outchain)
! 			continue;
! 		assert(a != NULL);
! 		a->outchain = victim->outchain;
  	}
  	from->nouts--;
  
  	/* take it off target's in-chain */
  	assert(to != NULL);
! 	assert(to->ins != NULL);
! 	a = to->ins;
! 	if (a == victim)			/* simple case:  first in chain */
  		to->ins = victim->inchain;
  	else
  	{
! 		for (; a != NULL && a->inchain != victim; a = a->inchain)
! 			continue;
! 		assert(a != NULL);
! 		a->inchain = victim->inchain;
  	}
  	to->nins--;
  
! 	/* clean up and place on free list */
  	victim->type = 0;
  	victim->from = NULL;		/* precautions... */
  	victim->to = NULL;
  	victim->inchain = NULL;
  	victim->outchain = NULL;
  	victim->freechain = from->free;
  	from->free = victim;
  }
  
  /*
   * hasnonemptyout - Does state have a non-EMPTY out arc?
   */
  static int
--- 480,583 ----
  
  	/* take it off source's out-chain */
  	assert(from != NULL);
! 	predecessor = victim->outchainRev;
! 	if (predecessor == NULL)
! 	{
! 		assert(from->outs == victim);
  		from->outs = victim->outchain;
+ 	}
  	else
  	{
! 		assert(predecessor->outchain == victim);
! 		predecessor->outchain = victim->outchain;
! 	}
! 	if (victim->outchain != NULL)
! 	{
! 		assert(victim->outchain->outchainRev == victim);
! 		victim->outchain->outchainRev = predecessor;
  	}
  	from->nouts--;
  
  	/* take it off target's in-chain */
  	assert(to != NULL);
! 	predecessor = victim->inchainRev;
! 	if (predecessor == NULL)
! 	{
! 		assert(to->ins == victim);
  		to->ins = victim->inchain;
+ 	}
  	else
  	{
! 		assert(predecessor->inchain == victim);
! 		predecessor->inchain = victim->inchain;
! 	}
! 	if (victim->inchain != NULL)
! 	{
! 		assert(victim->inchain->inchainRev == victim);
! 		victim->inchain->inchainRev = predecessor;
  	}
  	to->nins--;
  
! 	/* clean up and place on from-state's free list */
  	victim->type = 0;
  	victim->from = NULL;		/* precautions... */
  	victim->to = NULL;
  	victim->inchain = NULL;
+ 	victim->inchainRev = NULL;
  	victim->outchain = NULL;
+ 	victim->outchainRev = NULL;
  	victim->freechain = from->free;
  	from->free = victim;
  }
  
  /*
+  * changearctarget - flip an arc to have a different to state
+  *
+  * Caller must have verified that there is no pre-existing duplicate arc.
+  *
+  * Note that because we store arcs in their from state, we can't easily have
+  * a similar changearcsource function.
+  */
+ static void
+ changearctarget(struct arc * a, struct state * newto)
+ {
+ 	struct state *oldto = a->to;
+ 	struct arc *predecessor;
+ 
+ 	assert(oldto != newto);
+ 
+ 	/* take it off old target's in-chain */
+ 	assert(oldto != NULL);
+ 	predecessor = a->inchainRev;
+ 	if (predecessor == NULL)
+ 	{
+ 		assert(oldto->ins == a);
+ 		oldto->ins = a->inchain;
+ 	}
+ 	else
+ 	{
+ 		assert(predecessor->inchain == a);
+ 		predecessor->inchain = a->inchain;
+ 	}
+ 	if (a->inchain != NULL)
+ 	{
+ 		assert(a->inchain->inchainRev == a);
+ 		a->inchain->inchainRev = predecessor;
+ 	}
+ 	oldto->nins--;
+ 
+ 	a->to = newto;
+ 
+ 	/* prepend it to new target's in-chain */
+ 	a->inchain = newto->ins;
+ 	a->inchainRev = NULL;
+ 	if (newto->ins)
+ 		newto->ins->inchainRev = a;
+ 	newto->ins = a;
+ 	newto->nins++;
+ }
+ 
+ /*
   * hasnonemptyout - Does state have a non-EMPTY out arc?
   */
  static int
*************** cparc(struct nfa * nfa,
*** 561,587 ****
  }
  
  /*
   * moveins - move all in arcs of a state to another state
   *
   * You might think this could be done better by just updating the
!  * existing arcs, and you would be right if it weren't for the desire
   * for duplicate suppression, which makes it easier to just make new
   * ones to exploit the suppression built into newarc.
   */
  static void
  moveins(struct nfa * nfa,
  		struct state * oldState,
  		struct state * newState)
  {
- 	struct arc *a;
- 
  	assert(oldState != newState);
  
! 	while ((a = oldState->ins) != NULL)
  	{
! 		cparc(nfa, a, a->from, newState);
! 		freearc(nfa, a);
  	}
  	assert(oldState->nins == 0);
  	assert(oldState->ins == NULL);
  }
--- 657,903 ----
  }
  
  /*
+  * sortins - sort the in arcs of a state by from/color/type
+  */
+ static void
+ sortins(struct nfa * nfa,
+ 		struct state * s)
+ {
+ 	struct arc **sortarray;
+ 	struct arc *a;
+ 	int			n = s->nins;
+ 	int			i;
+ 
+ 	if (n <= 1)
+ 		return;					/* nothing to do */
+ 	/* make an array of arc pointers ... */
+ 	sortarray = (struct arc **) MALLOC(n * sizeof(struct arc *));
+ 	if (sortarray == NULL)
+ 	{
+ 		NERR(REG_ESPACE);
+ 		return;
+ 	}
+ 	i = 0;
+ 	for (a = s->ins; a != NULL; a = a->inchain)
+ 		sortarray[i++] = a;
+ 	assert(i == n);
+ 	/* ... sort the array */
+ 	qsort(sortarray, n, sizeof(struct arc *), sortins_cmp);
+ 	/* ... and rebuild arc list in order */
+ 	/* it seems worth special-casing first and last items to simplify loop */
+ 	a = sortarray[0];
+ 	s->ins = a;
+ 	a->inchain = sortarray[1];
+ 	a->inchainRev = NULL;
+ 	for (i = 1; i < n - 1; i++)
+ 	{
+ 		a = sortarray[i];
+ 		a->inchain = sortarray[i + 1];
+ 		a->inchainRev = sortarray[i - 1];
+ 	}
+ 	a = sortarray[i];
+ 	a->inchain = NULL;
+ 	a->inchainRev = sortarray[i - 1];
+ 	FREE(sortarray);
+ }
+ 
+ static int
+ sortins_cmp(const void *a, const void *b)
+ {
+ 	const struct arc *aa = *((const struct arc * const *) a);
+ 	const struct arc *bb = *((const struct arc * const *) b);
+ 
+ 	/* we check the fields in the order they are most likely to be different */
+ 	if (aa->from->no < bb->from->no)
+ 		return -1;
+ 	if (aa->from->no > bb->from->no)
+ 		return 1;
+ 	if (aa->co < bb->co)
+ 		return -1;
+ 	if (aa->co > bb->co)
+ 		return 1;
+ 	if (aa->type < bb->type)
+ 		return -1;
+ 	if (aa->type > bb->type)
+ 		return 1;
+ 	return 0;
+ }
+ 
+ /*
+  * sortouts - sort the out arcs of a state by to/color/type
+  */
+ static void
+ sortouts(struct nfa * nfa,
+ 		 struct state * s)
+ {
+ 	struct arc **sortarray;
+ 	struct arc *a;
+ 	int			n = s->nouts;
+ 	int			i;
+ 
+ 	if (n <= 1)
+ 		return;					/* nothing to do */
+ 	/* make an array of arc pointers ... */
+ 	sortarray = (struct arc **) MALLOC(n * sizeof(struct arc *));
+ 	if (sortarray == NULL)
+ 	{
+ 		NERR(REG_ESPACE);
+ 		return;
+ 	}
+ 	i = 0;
+ 	for (a = s->outs; a != NULL; a = a->outchain)
+ 		sortarray[i++] = a;
+ 	assert(i == n);
+ 	/* ... sort the array */
+ 	qsort(sortarray, n, sizeof(struct arc *), sortouts_cmp);
+ 	/* ... and rebuild arc list in order */
+ 	/* it seems worth special-casing first and last items to simplify loop */
+ 	a = sortarray[0];
+ 	s->outs = a;
+ 	a->outchain = sortarray[1];
+ 	a->outchainRev = NULL;
+ 	for (i = 1; i < n - 1; i++)
+ 	{
+ 		a = sortarray[i];
+ 		a->outchain = sortarray[i + 1];
+ 		a->outchainRev = sortarray[i - 1];
+ 	}
+ 	a = sortarray[i];
+ 	a->outchain = NULL;
+ 	a->outchainRev = sortarray[i - 1];
+ 	FREE(sortarray);
+ }
+ 
+ static int
+ sortouts_cmp(const void *a, const void *b)
+ {
+ 	const struct arc *aa = *((const struct arc * const *) a);
+ 	const struct arc *bb = *((const struct arc * const *) b);
+ 
+ 	/* we check the fields in the order they are most likely to be different */
+ 	if (aa->to->no < bb->to->no)
+ 		return -1;
+ 	if (aa->to->no > bb->to->no)
+ 		return 1;
+ 	if (aa->co < bb->co)
+ 		return -1;
+ 	if (aa->co > bb->co)
+ 		return 1;
+ 	if (aa->type < bb->type)
+ 		return -1;
+ 	if (aa->type > bb->type)
+ 		return 1;
+ 	return 0;
+ }
+ 
+ /*
+  * Common decision logic about whether to use arc-by-arc operations or
+  * sort/merge.  If there's just a few source arcs we cannot recoup the
+  * cost of sorting the destination arc list, no matter how large it is.
+  * Otherwise, limit the number of arc-by-arc comparisons to about 1000
+  * (a somewhat arbitrary choice, but the breakeven point would probably
+  * be machine dependent anyway).
+  */
+ #define BULK_ARC_OP_USE_SORT(nsrcarcs, ndestarcs) \
+ 	((nsrcarcs) < 4 ? 0 : ((nsrcarcs) > 32 || (ndestarcs) > 32))
+ 
+ /*
   * moveins - move all in arcs of a state to another state
   *
   * You might think this could be done better by just updating the
!  * existing arcs, and you would be right if it weren't for the need
   * for duplicate suppression, which makes it easier to just make new
   * ones to exploit the suppression built into newarc.
+  *
+  * However, if we have a whole lot of arcs to deal with, retail duplicate
+  * checks become too slow.  In that case we proceed by sorting and merging
+  * the arc lists, and then we can indeed just update the arcs in-place.
   */
  static void
  moveins(struct nfa * nfa,
  		struct state * oldState,
  		struct state * newState)
  {
  	assert(oldState != newState);
  
! 	if (!BULK_ARC_OP_USE_SORT(oldState->nins, newState->nins))
  	{
! 		/* With not too many arcs, just do them one at a time */
! 		struct arc *a;
! 
! 		while ((a = oldState->ins) != NULL)
! 		{
! 			cparc(nfa, a, a->from, newState);
! 			freearc(nfa, a);
! 		}
! 	}
! 	else
! 	{
! 		/*
! 		 * With many arcs, use a sort-merge approach.  Note changearctarget()
! 		 * will put the arc onto the front of newState's chain, so it does not
! 		 * break our walk through the sorted part of the chain.
! 		 */
! 		struct arc *oa;
! 		struct arc *na;
! 
! 		/*
! 		 * Because we bypass newarc() in this code path, we'd better include a
! 		 * cancel check.
! 		 */
! 		if (CANCEL_REQUESTED(nfa->v->re))
! 		{
! 			NERR(REG_CANCEL);
! 			return;
! 		}
! 
! 		sortins(nfa, oldState);
! 		sortins(nfa, newState);
! 		if (NISERR())
! 			return;				/* might have failed to sort */
! 		oa = oldState->ins;
! 		na = newState->ins;
! 		while (oa != NULL && na != NULL)
! 		{
! 			struct arc *a = oa;
! 
! 			switch (sortins_cmp(&oa, &na))
! 			{
! 				case -1:
! 					/* newState does not have anything matching oa */
! 					oa = oa->inchain;
! 
! 					/*
! 					 * Rather than doing createarc+freearc, we can just unlink
! 					 * and relink the existing arc struct.
! 					 */
! 					changearctarget(a, newState);
! 					break;
! 				case 0:
! 					/* match, advance in both lists */
! 					oa = oa->inchain;
! 					na = na->inchain;
! 					/* ... and drop duplicate arc from oldState */
! 					freearc(nfa, a);
! 					break;
! 				case +1:
! 					/* advance only na; oa might have a match later */
! 					na = na->inchain;
! 					break;
! 				default:
! 					assert(NOTREACHED);
! 			}
! 		}
! 		while (oa != NULL)
! 		{
! 			/* newState does not have anything matching oa */
! 			struct arc *a = oa;
! 
! 			oa = oa->inchain;
! 			changearctarget(a, newState);
! 		}
  	}
+ 
  	assert(oldState->nins == 0);
  	assert(oldState->ins == NULL);
  }
*************** copyins(struct nfa * nfa,
*** 597,610 ****
  		struct state * newState,
  		int all)
  {
- 	struct arc *a;
- 
  	assert(oldState != newState);
  
! 	for (a = oldState->ins; a != NULL; a = a->inchain)
  	{
! 		if (all || a->type != EMPTY)
! 			cparc(nfa, a, a->from, newState);
  	}
  }
  
--- 913,1099 ----
  		struct state * newState,
  		int all)
  {
  	assert(oldState != newState);
  
! 	if (!BULK_ARC_OP_USE_SORT(oldState->nins, newState->nins))
  	{
! 		/* With not too many arcs, just do them one at a time */
! 		struct arc *a;
! 
! 		for (a = oldState->ins; a != NULL; a = a->inchain)
! 			if (all || a->type != EMPTY)
! 				cparc(nfa, a, a->from, newState);
! 	}
! 	else
! 	{
! 		/*
! 		 * With many arcs, use a sort-merge approach.  Note that createarc()
! 		 * will put new arcs onto the front of newState's chain, so it does
! 		 * not break our walk through the sorted part of the chain.
! 		 */
! 		struct arc *oa;
! 		struct arc *na;
! 
! 		/*
! 		 * Because we bypass newarc() in this code path, we'd better include a
! 		 * cancel check.
! 		 */
! 		if (CANCEL_REQUESTED(nfa->v->re))
! 		{
! 			NERR(REG_CANCEL);
! 			return;
! 		}
! 
! 		sortins(nfa, oldState);
! 		sortins(nfa, newState);
! 		if (NISERR())
! 			return;				/* might have failed to sort */
! 		oa = oldState->ins;
! 		na = newState->ins;
! 		while (oa != NULL && na != NULL)
! 		{
! 			struct arc *a = oa;
! 
! 			if (!all && a->type == EMPTY)
! 			{
! 				oa = oa->inchain;
! 				continue;
! 			}
! 
! 			switch (sortins_cmp(&oa, &na))
! 			{
! 				case -1:
! 					/* newState does not have anything matching oa */
! 					oa = oa->inchain;
! 					createarc(nfa, a->type, a->co, a->from, newState);
! 					break;
! 				case 0:
! 					/* match, advance in both lists */
! 					oa = oa->inchain;
! 					na = na->inchain;
! 					break;
! 				case +1:
! 					/* advance only na; oa might have a match later */
! 					na = na->inchain;
! 					break;
! 				default:
! 					assert(NOTREACHED);
! 			}
! 		}
! 		while (oa != NULL)
! 		{
! 			/* newState does not have anything matching oa */
! 			struct arc *a = oa;
! 
! 			if (!all && a->type == EMPTY)
! 			{
! 				oa = oa->inchain;
! 				continue;
! 			}
! 
! 			oa = oa->inchain;
! 			createarc(nfa, a->type, a->co, a->from, newState);
! 		}
! 	}
! }
! 
! /*
!  * mergeins - merge a list of inarcs into a state
!  *
!  * This is much like copyins, but the source arcs are listed in an array,
!  * and are not guaranteed unique.  It's okay to clobber the array contents.
!  */
! static void
! mergeins(struct nfa * nfa,
! 		 struct state * s,
! 		 struct arc ** arcarray,
! 		 int arccount)
! {
! 	struct arc *na;
! 	int			i;
! 	int			j;
! 
! 	if (arccount <= 0)
! 		return;
! 
! 	/*
! 	 * Because we bypass newarc() in this code path, we'd better include a
! 	 * cancel check.
! 	 */
! 	if (CANCEL_REQUESTED(nfa->v->re))
! 	{
! 		NERR(REG_CANCEL);
! 		return;
! 	}
! 
! 	/* Sort existing inarcs as well as proposed new ones */
! 	sortins(nfa, s);
! 	if (NISERR())
! 		return;					/* might have failed to sort */
! 
! 	qsort(arcarray, arccount, sizeof(struct arc *), sortins_cmp);
! 
! 	/*
! 	 * arcarray very likely includes dups, so we must eliminate them.  (This
! 	 * could be folded into the next loop, but it's not worth the trouble.)
! 	 */
! 	j = 0;
! 	for (i = 1; i < arccount; i++)
! 	{
! 		switch (sortins_cmp(&arcarray[j], &arcarray[i]))
! 		{
! 			case -1:
! 				/* non-dup */
! 				arcarray[++j] = arcarray[i];
! 				break;
! 			case 0:
! 				/* dup */
! 				break;
! 			default:
! 				/* trouble */
! 				assert(NOTREACHED);
! 		}
! 	}
! 	arccount = j + 1;
! 
! 	/*
! 	 * Now merge into s' inchain.  Note that createarc() will put new arcs
! 	 * onto the front of s's chain, so it does not break our walk through the
! 	 * sorted part of the chain.
! 	 */
! 	i = 0;
! 	na = s->ins;
! 	while (i < arccount && na != NULL)
! 	{
! 		struct arc *a = arcarray[i];
! 
! 		switch (sortins_cmp(&a, &na))
! 		{
! 			case -1:
! 				/* s does not have anything matching a */
! 				createarc(nfa, a->type, a->co, a->from, s);
! 				i++;
! 				break;
! 			case 0:
! 				/* match, advance in both lists */
! 				i++;
! 				na = na->inchain;
! 				break;
! 			case +1:
! 				/* advance only na; array might have a match later */
! 				na = na->inchain;
! 				break;
! 			default:
! 				assert(NOTREACHED);
! 		}
! 	}
! 	while (i < arccount)
! 	{
! 		/* s does not have anything matching a */
! 		struct arc *a = arcarray[i];
! 
! 		createarc(nfa, a->type, a->co, a->from, s);
! 		i++;
  	}
  }
  
*************** moveouts(struct nfa * nfa,
*** 616,630 ****
  		 struct state * oldState,
  		 struct state * newState)
  {
- 	struct arc *a;
- 
  	assert(oldState != newState);
  
! 	while ((a = oldState->outs) != NULL)
  	{
! 		cparc(nfa, a, newState, a->to);
! 		freearc(nfa, a);
  	}
  }
  
  /*
--- 1105,1189 ----
  		 struct state * oldState,
  		 struct state * newState)
  {
  	assert(oldState != newState);
  
! 	if (!BULK_ARC_OP_USE_SORT(oldState->nouts, newState->nouts))
  	{
! 		/* With not too many arcs, just do them one at a time */
! 		struct arc *a;
! 
! 		while ((a = oldState->outs) != NULL)
! 		{
! 			cparc(nfa, a, newState, a->to);
! 			freearc(nfa, a);
! 		}
  	}
+ 	else
+ 	{
+ 		/*
+ 		 * With many arcs, use a sort-merge approach.  Note that createarc()
+ 		 * will put new arcs onto the front of newState's chain, so it does
+ 		 * not break our walk through the sorted part of the chain.
+ 		 */
+ 		struct arc *oa;
+ 		struct arc *na;
+ 
+ 		/*
+ 		 * Because we bypass newarc() in this code path, we'd better include a
+ 		 * cancel check.
+ 		 */
+ 		if (CANCEL_REQUESTED(nfa->v->re))
+ 		{
+ 			NERR(REG_CANCEL);
+ 			return;
+ 		}
+ 
+ 		sortouts(nfa, oldState);
+ 		sortouts(nfa, newState);
+ 		if (NISERR())
+ 			return;				/* might have failed to sort */
+ 		oa = oldState->outs;
+ 		na = newState->outs;
+ 		while (oa != NULL && na != NULL)
+ 		{
+ 			struct arc *a = oa;
+ 
+ 			switch (sortouts_cmp(&oa, &na))
+ 			{
+ 				case -1:
+ 					/* newState does not have anything matching oa */
+ 					oa = oa->outchain;
+ 					createarc(nfa, a->type, a->co, newState, a->to);
+ 					freearc(nfa, a);
+ 					break;
+ 				case 0:
+ 					/* match, advance in both lists */
+ 					oa = oa->outchain;
+ 					na = na->outchain;
+ 					/* ... and drop duplicate arc from oldState */
+ 					freearc(nfa, a);
+ 					break;
+ 				case +1:
+ 					/* advance only na; oa might have a match later */
+ 					na = na->outchain;
+ 					break;
+ 				default:
+ 					assert(NOTREACHED);
+ 			}
+ 		}
+ 		while (oa != NULL)
+ 		{
+ 			/* newState does not have anything matching oa */
+ 			struct arc *a = oa;
+ 
+ 			oa = oa->outchain;
+ 			createarc(nfa, a->type, a->co, newState, a->to);
+ 			freearc(nfa, a);
+ 		}
+ 	}
+ 
+ 	assert(oldState->nouts == 0);
+ 	assert(oldState->outs == NULL);
  }
  
  /*
*************** copyouts(struct nfa * nfa,
*** 638,651 ****
  		 struct state * newState,
  		 int all)
  {
- 	struct arc *a;
- 
  	assert(oldState != newState);
  
! 	for (a = oldState->outs; a != NULL; a = a->outchain)
  	{
! 		if (all || a->type != EMPTY)
! 			cparc(nfa, a, newState, a->to);
  	}
  }
  
--- 1197,1283 ----
  		 struct state * newState,
  		 int all)
  {
  	assert(oldState != newState);
  
! 	if (!BULK_ARC_OP_USE_SORT(oldState->nouts, newState->nouts))
  	{
! 		/* With not too many arcs, just do them one at a time */
! 		struct arc *a;
! 
! 		for (a = oldState->outs; a != NULL; a = a->outchain)
! 			if (all || a->type != EMPTY)
! 				cparc(nfa, a, newState, a->to);
! 	}
! 	else
! 	{
! 		/*
! 		 * With many arcs, use a sort-merge approach.  Note that createarc()
! 		 * will put new arcs onto the front of newState's chain, so it does
! 		 * not break our walk through the sorted part of the chain.
! 		 */
! 		struct arc *oa;
! 		struct arc *na;
! 
! 		/*
! 		 * Because we bypass newarc() in this code path, we'd better include a
! 		 * cancel check.
! 		 */
! 		if (CANCEL_REQUESTED(nfa->v->re))
! 		{
! 			NERR(REG_CANCEL);
! 			return;
! 		}
! 
! 		sortouts(nfa, oldState);
! 		sortouts(nfa, newState);
! 		if (NISERR())
! 			return;				/* might have failed to sort */
! 		oa = oldState->outs;
! 		na = newState->outs;
! 		while (oa != NULL && na != NULL)
! 		{
! 			struct arc *a = oa;
! 
! 			if (!all && a->type == EMPTY)
! 			{
! 				oa = oa->outchain;
! 				continue;
! 			}
! 
! 			switch (sortouts_cmp(&oa, &na))
! 			{
! 				case -1:
! 					/* newState does not have anything matching oa */
! 					oa = oa->outchain;
! 					createarc(nfa, a->type, a->co, newState, a->to);
! 					break;
! 				case 0:
! 					/* match, advance in both lists */
! 					oa = oa->outchain;
! 					na = na->outchain;
! 					break;
! 				case +1:
! 					/* advance only na; oa might have a match later */
! 					na = na->outchain;
! 					break;
! 				default:
! 					assert(NOTREACHED);
! 			}
! 		}
! 		while (oa != NULL)
! 		{
! 			/* newState does not have anything matching oa */
! 			struct arc *a = oa;
! 
! 			if (!all && a->type == EMPTY)
! 			{
! 				oa = oa->outchain;
! 				continue;
! 			}
! 
! 			oa = oa->outchain;
! 			createarc(nfa, a->type, a->co, newState, a->to);
! 		}
  	}
  }
  
*************** compact(struct nfa * nfa,
*** 2217,2223 ****
  					NERR(REG_ASSERT);
  					break;
  			}
! 		carcsort(first, ca - 1);
  		ca->co = COLORLESS;
  		ca->to = 0;
  		ca++;
--- 2849,2855 ----
  					NERR(REG_ASSERT);
  					break;
  			}
! 		carcsort(first, ca - first);
  		ca->co = COLORLESS;
  		ca->to = 0;
  		ca++;
*************** compact(struct nfa * nfa,
*** 2233,2263 ****
  
  /*
   * carcsort - sort compacted-NFA arcs by color
-  *
-  * Really dumb algorithm, but if the list is long enough for that to matter,
-  * you're in real trouble anyway.
   */
  static void
! carcsort(struct carc * first,
! 		 struct carc * last)
  {
! 	struct carc *p;
! 	struct carc *q;
! 	struct carc tmp;
  
! 	if (last - first <= 1)
! 		return;
  
! 	for (p = first; p <= last; p++)
! 		for (q = p; q <= last; q++)
! 			if (p->co > q->co ||
! 				(p->co == q->co && p->to > q->to))
! 			{
! 				assert(p != q);
! 				tmp = *p;
! 				*p = *q;
! 				*q = tmp;
! 			}
  }
  
  /*
--- 2865,2893 ----
  
  /*
   * carcsort - sort compacted-NFA arcs by color
   */
  static void
! carcsort(struct carc * first, size_t n)
  {
! 	if (n > 1)
! 		qsort(first, n, sizeof(struct carc), carc_cmp);
! }
  
! static int
! carc_cmp(const void *a, const void *b)
! {
! 	const struct carc *aa = (const struct carc *) a;
! 	const struct carc *bb = (const struct carc *) b;
  
! 	if (aa->co < bb->co)
! 		return -1;
! 	if (aa->co > bb->co)
! 		return +1;
! 	if (aa->to < bb->to)
! 		return -1;
! 	if (aa->to > bb->to)
! 		return +1;
! 	return 0;
  }
  
  /*
*************** dumparcs(struct state * s,
*** 2337,2370 ****
  		 FILE *f)
  {
  	int			pos;
  
! 	assert(s->nouts > 0);
! 	/* printing arcs in reverse order is usually clearer */
! 	pos = dumprarcs(s->outs, s, f, 1);
! 	if (pos != 1)
! 		fprintf(f, "\n");
! }
! 
! /*
!  * dumprarcs - dump remaining outarcs, recursively, in reverse order
!  */
! static int						/* resulting print position */
! dumprarcs(struct arc * a,
! 		  struct state * s,
! 		  FILE *f,
! 		  int pos)				/* initial print position */
! {
! 	if (a->outchain != NULL)
! 		pos = dumprarcs(a->outchain, s, f, pos);
! 	dumparc(a, s, f);
! 	if (pos == 5)
  	{
  		fprintf(f, "\n");
- 		pos = 1;
- 	}
- 	else
- 		pos++;
- 	return pos;
  }
  
  /*
--- 2967,2994 ----
  		 FILE *f)
  {
  	int			pos;
+ 	struct arc *a;
  
! 	/* printing oldest arcs first is usually clearer */
! 	a = s->outs;
! 	assert(a != NULL);
! 	while (a->outchain != NULL)
! 		a = a->outchain;
! 	pos = 1;
! 	do
  	{
+ 		dumparc(a, s, f);
+ 		if (pos == 5)
+ 		{
+ 			fprintf(f, "\n");
+ 			pos = 1;
+ 		}
+ 		else
+ 			pos++;
+ 		a = a->outchainRev;
+ 	} while (a != NULL);
+ 	if (pos != 1)
  		fprintf(f, "\n");
  }
  
  /*
diff --git a/src/backend/regex/regcomp.c b/src/backend/regex/regcomp.c
index bc38192..ad9a797 100644
*** a/src/backend/regex/regcomp.c
--- b/src/backend/regex/regcomp.c
*************** static void dropstate(struct nfa *, stru
*** 124,138 ****
--- 124,145 ----
  static void freestate(struct nfa *, struct state *);
  static void destroystate(struct nfa *, struct state *);
  static void newarc(struct nfa *, int, pcolor, struct state *, struct state *);
+ static void createarc(struct nfa *, int, pcolor, struct state *, struct state *);
  static struct arc *allocarc(struct nfa *, struct state *);
  static void freearc(struct nfa *, struct arc *);
+ static void changearctarget(struct arc *, struct state *);
  static int	hasnonemptyout(struct state *);
  static int	nonemptyouts(struct state *);
  static int	nonemptyins(struct state *);
  static struct arc *findarc(struct state *, int, pcolor);
  static void cparc(struct nfa *, struct arc *, struct state *, struct state *);
+ static void sortins(struct nfa *, struct state *);
+ static int	sortins_cmp(const void *, const void *);
+ static void sortouts(struct nfa *, struct state *);
+ static int	sortouts_cmp(const void *, const void *);
  static void moveins(struct nfa *, struct state *, struct state *);
  static void copyins(struct nfa *, struct state *, struct state *, int);
+ static void mergeins(struct nfa *, struct state *, struct arc **, int);
  static void moveouts(struct nfa *, struct state *, struct state *);
  static void copyouts(struct nfa *, struct state *, struct state *, int);
  static void cloneouts(struct nfa *, struct state *, struct state *, struct state *, int);
*************** static void markreachable(struct nfa *, 
*** 168,174 ****
  static void markcanreach(struct nfa *, struct state *, struct state *, struct state *);
  static long analyze(struct nfa *);
  static void compact(struct nfa *, struct cnfa *);
! static void carcsort(struct carc *, struct carc *);
  static void freecnfa(struct cnfa *);
  static void dumpnfa(struct nfa *, FILE *);
  
--- 175,182 ----
  static void markcanreach(struct nfa *, struct state *, struct state *, struct state *);
  static long analyze(struct nfa *);
  static void compact(struct nfa *, struct cnfa *);
! static void carcsort(struct carc *, size_t);
! static int	carc_cmp(const void *, const void *);
  static void freecnfa(struct cnfa *);
  static void dumpnfa(struct nfa *, FILE *);
  
diff --git a/src/include/regex/regguts.h b/src/include/regex/regguts.h
index 3277752..357891a 100644
*** a/src/include/regex/regguts.h
--- b/src/include/regex/regguts.h
*************** struct arc
*** 289,296 ****
  	struct state *from;			/* where it's from (and contained within) */
  	struct state *to;			/* where it's to */
  	struct arc *outchain;		/* link in *from's outs chain or free chain */
! #define  freechain	 outchain
  	struct arc *inchain;		/* link in *to's ins chain */
  	struct arc *colorchain;		/* link in color's arc chain */
  	struct arc *colorchainRev;	/* back-link in color's arc chain */
  };
--- 289,298 ----
  	struct state *from;			/* where it's from (and contained within) */
  	struct state *to;			/* where it's to */
  	struct arc *outchain;		/* link in *from's outs chain or free chain */
! 	struct arc *outchainRev;	/* back-link in *from's outs chain */
! #define  freechain	outchain	/* we do not maintain "freechainRev" */
  	struct arc *inchain;		/* link in *to's ins chain */
+ 	struct arc *inchainRev;		/* back-link in *to's ins chain */
  	struct arc *colorchain;		/* link in color's arc chain */
  	struct arc *colorchainRev;	/* back-link in color's arc chain */
  };
