package syntax

import (
	
	
	
	
	
	

	
)

// Arbitrary number of repetitions of the same character when we'd prefer to represent that as a repeater of that character rather than a string.
const MultiVsRepeaterLimit = 64

type RegexTree struct {
	Root              *RegexNode
	Caps              map[int]int
	Capnumlist        []int
	Captop            int
	Capnames          map[string]int
	Caplist           []string
	Options           RegexOptions
	FindOptimizations *FindOptimizations
}

// It is built into a parsed tree for a regular expression.

// Implementation notes:
//
// Since the node tree is a temporary data structure only used
// during compilation of the regexp to integer codes, it's
// designed for clarity and convenience rather than
// space efficiency.
//
// RegexNodes are built into a tree, linked by the n.children list.
// Each node also has a n.parent and n.ichild member indicating
// its parent and which child # it is in its parent's list.
//
// RegexNodes come in as many types as there are constructs in
// a regular expression, for example, "concatenate", "alternate",
// "one", "rept", "group". There are also node types for basic
// peephole optimizations, e.g., "onerep", "notsetrep", etc.
//
// Because perl 5 allows "lookback" groups that scan backwards,
// each node also gets a "direction". Normally the value of
// boolean n.backward = false.
//
// On the parse stack, each tree has a "role" - basically, the
// nonterminal in the grammar that the parser has currently
// assigned to the tree. That code is stored in n.role.
//
// Finally, some of the different kinds of nodes have data.
// Two integers (for the looping constructs) are stored in
// n.operands, an an object (either a string or a set)
// is stored in n.data
type RegexNode struct {
	T        NodeType
	Children []*RegexNode
	Str      []rune
	Set      *CharSet
	Ch       rune
	M        int
	N        int
	Options  RegexOptions
	Parent   *RegexNode
}

type NodeType int32

const (
	// The following are leaves, and correspond to primitive operations
	NtUnknown NodeType = -1
	//NtOnerep      NodeType = 0  // lef,back char,min,max    a {n}
	//NtNotonerep   NodeType = 1  // lef,back char,min,max    .{n}
	//NtSetrep      NodeType = 2  // lef,back set,min,max     [\d]{n}
	NtOneloop     NodeType = 3  // lef,back char,min,max    a {,n}
	NtNotoneloop  NodeType = 4  // lef,back char,min,max    .{,n}
	NtSetloop     NodeType = 5  // lef,back set,min,max     [\d]{,n}
	NtOnelazy     NodeType = 6  // lef,back char,min,max    a {,n}?
	NtNotonelazy  NodeType = 7  // lef,back char,min,max    .{,n}?
	NtSetlazy     NodeType = 8  // lef,back set,min,max     [\d]{,n}?
	NtOne         NodeType = 9  // lef      char            a
	NtNotone      NodeType = 10 // lef      char            [^a]
	NtSet         NodeType = 11 // lef      set             [a-z\s]  \w \s \d
	NtMulti       NodeType = 12 // lef      string          abcd
	NtRef         NodeType = 13 // lef      group           \#
	NtBol         NodeType = 14 //                          ^
	NtEol         NodeType = 15 //                          $
	NtBoundary    NodeType = 16 //                          \b
	NtNonboundary NodeType = 17 //                          \B
	NtBeginning   NodeType = 18 //                          \A
	NtStart       NodeType = 19 //                          \G
	NtEndZ        NodeType = 20 //                          \Z
	NtEnd         NodeType = 21 //                          \Z

	// Interior nodes do not correspond to primitive operations, but
	// control structures compositing other operations

	// Concat and alternate take n children, and can run forward or backwards

	NtNothing     NodeType = 22 //          []
	NtEmpty       NodeType = 23 //          ()
	NtAlternate   NodeType = 24 //          a|b
	NtConcatenate NodeType = 25 //          ab
	NtLoop        NodeType = 26 // m,x      * + ? {,}
	NtLazyloop    NodeType = 27 // m,x      *? +? ?? {,}?
	NtCapture     NodeType = 28 // n        ()
	NtGroup       NodeType = 29 //          (?:)
	NtPosLook     NodeType = 30 //          (?=) (?<=)
	NtNegLook     NodeType = 31 //          (?!) (?<!)
	NtAtomic      NodeType = 32 //          (?>) (?<)
	NtBackRefCond NodeType = 33 //          (?(n) | )
	NtExprCond    NodeType = 34 //          (?(...) | )

	NtECMABoundary    NodeType = 41 //                          \b
	NtNonECMABoundary NodeType = 42 //                          \B

	// Atomic loop of the specified character.
	// Operand 0 is the character. Operand 1 is the max iteration count.
	NtOneloopatomic NodeType = 43
	// Atomic loop of a single character other than the one specified.
	// Operand 0 is the character. Operand 1 is the max iteration count.
	NtNotoneloopatomic NodeType = 44
	// Atomic loop of a single character matching the specified set
	// Operand 0 is index into the strings table of the character class description. Operand 1 is the repetition count.
	NtSetloopatomic NodeType = 45
	// Updates the bumpalong position to the current position.
	NtUpdateBumpalong NodeType = 46
	// Matches one Unicode extended grapheme cluster (\X).
	NtGrapheme NodeType = 47
)

func newRegexNode( NodeType,  RegexOptions) *RegexNode {
	return &RegexNode{
		T:       ,
		Options: ,
	}
}

func newRegexNodeCh( NodeType,  RegexOptions,  rune) *RegexNode {
	return nodeWithCaseConversion(&RegexNode{
		T:       ,
		Options: ,
		Ch:      ,
	})
}

func newRegexNodeStr( NodeType,  RegexOptions,  []rune) *RegexNode {
	return &RegexNode{
		T:       ,
		Options: ,
		Str:     ,
	}
}

func newRegexNodeSet( NodeType,  RegexOptions,  *CharSet) *RegexNode {
	return nodeWithCaseConversion(&RegexNode{
		T:       ,
		Options: ,
		Set:     ,
	})
}

func newRegexNodeM( NodeType,  RegexOptions,  int) *RegexNode {
	return &RegexNode{
		T:       ,
		Options: ,
		M:       ,
	}
}
func newRegexNodeMN( NodeType,  RegexOptions, ,  int) *RegexNode {
	return &RegexNode{
		T:       ,
		Options: ,
		M:       ,
		N:       ,
	}
}

func nodeWithCaseConversion( *RegexNode) *RegexNode {
	// if opts are ignore case and our rune is impacted by casing
	// then we need to switch our type to the set version
	// NtOne = NtSet
	if .Options&IgnoreCase == 0 {
		return 
	}

	if .Ch > 0 {
		 := .Ch
		if ,  := unicode.IsLower(), unicode.IsUpper();  ||  {
			/*var upper, lower rune
			// it's a capitalizable char
			if isUp {
				upper = ch
				lower = unicode.ToLower(ch)
			} else {
				lower = ch
				upper = unicode.ToUpper(ch)
			}*/
			 := &CharSet{}
			.addChar()
			.addCaseEquivalences()
			 := NtSet

			switch .T {
			case NtOneloop, NtNotoneloop:
				 = NtSetloop
			case NtOnelazy, NtNotonelazy:
				 = NtSetlazy
			}
			.negate = .IsNotoneFamily()

			return &RegexNode{
				T:       ,
				Options: .Options & ^IgnoreCase,
				Set:     ,
			}
		}
	} else if .Set != nil {
		// just to be safe we don't modify the original set pointer
		// just in case it's used in a case-sensitive area
		 := .Set.Copy()
		.addCaseEquivalences()
		.Set = &
		.Options &= ^IgnoreCase
	}

	return 
}

func ( *RegexNode) () bool {
	return .T == NtSet || .T == NtSetloop || .T == NtSetlazy || .T == NtSetloopatomic
}
func ( *RegexNode) () bool {
	return .T == NtOne || .T == NtOneloop || .T == NtOnelazy || .T == NtOneloopatomic
}
func ( *RegexNode) () bool {
	return .T == NtNotone || .T == NtNotoneloop || .T == NtNotonelazy || .T == NtNotoneloopatomic
}

func ( *RegexNode) () bool {
	return .T == NtSetloop || .T == NtSetlazy || .T == NtSetloopatomic
}
func ( *RegexNode) () bool {
	return .T == NtOneloop || .T == NtOnelazy || .T == NtOneloopatomic
}
func ( *RegexNode) () bool {
	return .T == NtNotoneloop || .T == NtNotonelazy || .T == NtNotoneloopatomic
}

func ( *RegexNode) () bool {
	return .T == NtOneloopatomic || .T == NtNotoneloopatomic || .T == NtSetloopatomic
}

func ( *RegexNode) ( *bytes.Buffer) {
	for  := 0;  < len(.Str); ++ {
		.WriteRune(.Str[])
	}
}

func ( *RegexNode) ( *RegexNode) {
	.Parent = 
	 := .reduce()
	.Parent = 
	.Children = append(.Children, )
	.Parent = 
}

func ( *RegexNode) ( int,  []*RegexNode) {
	for ,  := range  {
		.Parent = 
	}
	.Children = slices.Insert(.Children, , ...)
	//newChildren := make([]*RegexNode, 0, len(n.Children)+len(nodes))
	//n.Children = append(append(append(newChildren, n.Children[:afterIndex]...), nodes...), n.Children[afterIndex:]...)
}

// removes children including the start but not the end index
func ( *RegexNode) (,  int) {
	.Children = append(.Children[:], .Children[:]...)
}

func ( *RegexNode) ( int,  *RegexNode) {
	.Parent =  // so that the child can see its parent while being reduced
	 = .reduce()
	.Parent =  // in case Reduce returns a different node that needs to be reparented

	.Children[] = 
}

// Pass type as OneLazy or OneLoop
func ( *RegexNode) ( NodeType, ,  int) {
	.T += ( - NtOne)
	.M = 
	.N = 
}

// Performs additional optimizations on an entire tree prior to being used.
//
// Some optimizations are performed by the parser while parsing, and others are performed
// as nodes are being added to the tree.  The optimizations here expect the tree to be fully
// formed, as they inspect relationships between nodes that may not have been in place as
// individual nodes were being processed/added to the tree.
func ( *RegexNode) () *RegexNode {
	 := 

	// Only apply optimization when LTR to avoid needing additional code for the much rarer RTL case.
	// Also only apply these optimizations when not using NonBacktracking, as these optimizations are
	// all about avoiding things that are impactful for the backtracking engines but nops for non-backtracking.
	if .Options&RightToLeft == 0 {
		// Optimization: eliminate backtracking for loops.
		// For any single-character loop (Oneloop, Notoneloop, Setloop), see if we can automatically convert
		// that into its atomic counterpart (Oneloopatomic, Notoneloopatomic, Setloopatomic) based on what
		// comes after it in the expression tree.
		.findAndMakeLoopsAtomic()

		// Optimization: backtracking removal at expression end.
		// If we find backtracking construct at the end of the regex, we can instead make it non-backtracking,
		// since nothing would ever backtrack into it anyway.  Doing this then makes the construct available
		// to implementations that don't support backtracking.
		.eliminateEndingBacktracking()

		// Optimization: unnecessary re-processing of starting loops.
		// If an expression is guaranteed to begin with a single-character unbounded loop that isn't part of an alternation (in which case it
		// wouldn't be guaranteed to be at the beginning) or a capture (in which case a back reference could be influenced by its length), then we
		// can update the tree with a temporary node to indicate that the implementation should use that node's ending position in the input text
		// as the next starting position at which to start the next match. This avoids redoing matches we've already performed, e.g. matching
		// "\w+@dot.net" against "is this a valid address@dot.net", the \w+ will initially match the "is" and then will fail to match the "@".
		// Rather than bumping the scan loop by 1 and trying again to match at the "s", we can instead start at the " ".  For functional correctness
		// we can only consider unbounded loops, as to be able to start at the end of the loop we need the loop to have consumed all possible matches;
		// otherwise, you could end up with a pattern like "a{1,3}b" matching against "aaaabc", which should match, but if we pre-emptively stop consuming
		// after the first three a's and re-start from that position, we'll end up failing the match even though it should have succeeded.  We can also
		// apply this optimization to non-atomic loops: even though backtracking could be necessary, such backtracking would be handled within the processing
		// of a single starting position.  Lazy loops similarly benefit, as a failed match will result in exploring the exact same search space as with
		// a greedy loop, just in the opposite order (and a successful match will overwrite the bumpalong position); we need to avoid atomic lazy loops,
		// however, as they will only end up as a repeater for the minimum length and thus will effectively end up with a non-infinite upper bound, which
		// we've already outlined is problematic.
		 := .Children[0] // skip implicit root capture node
		 := true     // the root is implicitly atomic because nothing comes after it (same for the implicit root capture)
		for {
			if .T == NtAtomic {
				 = .Children[0]
				continue
			} else if .T == NtConcatenate {
				 = false
				 = .Children[0]
				continue
			} else if .N == math.MaxInt32 &&
				((.T == NtOneloop || .T == NtOneloopatomic || .T == NtNotoneloop || .T == NtNotoneloopatomic || .T == NtSetloop || .T == NtSetloopatomic) ||
					((.T == NtOnelazy || .T == NtNotonelazy || .T == NtSetlazy) && !)) {

				if .Parent != nil && .Parent.T == NtConcatenate {
					.Parent.Children = slices.Insert(.Parent.Children, 1, &RegexNode{T: NtUpdateBumpalong, Options: .Options, Parent: .Parent})
				}
			}

			break
		}

	}

	//debug helper
	//rootNode.ValidateFinalTreeInvariants()

	// Done optimizing.  Return the final tree.
	return 
}

// Finds {one/notone/set}loop nodes in the concatenation that can be automatically upgraded
// to {one/notone/set}loopatomic nodes.  Such changes avoid potential useless backtracking.
// e.g. A*B (where sets A and B don't overlap) => (?>A*)B.
func ( *RegexNode) () {
	if .Options&RightToLeft != 0 {
		// RTL is so rare, we don't need to spend additional time/code optimizing for it.
		return
	}

	// For all node types that have children, recur into each of those children.
	for  := 0;  < len(.Children); ++ {
		.Children[].()
	}

	// If this isn't a concatenation, nothing more to do.
	if .T != NtConcatenate {
		return
	}

	// This is a concatenation.  Iterate through each pair of nodes in the concatenation seeing whether we can
	// make the first node (or its right-most child) atomic based on the second node (or its left-most child).
	for  := 0;  < len(.Children)-1; ++ {
		.Children[].processNode(.Children[+1])
	}
}

func ( *RegexNode) ( *RegexNode) {
	// Skip down the node past irrelevant nodes.
	for {
		// We can always recur into captures and into the last node of concatenations.
		if .T == NtCapture || .T == NtConcatenate {
			 = .Children[len(.Children)-1]
			continue
		}

		// For loops with at least one guaranteed iteration, we can recur into them, but
		// we need to be careful not to just always do so; the ending node of a loop can only
		// be made atomic if what comes after the loop but also the beginning of the loop are
		// compatible for the optimization.
		if .T == NtLoop {
			 := .FindLastExpressionInLoopForAutoAtomic()
			if  != nil {
				 = 
				continue
			}
		}

		// Can't skip any further.
		break
	}

	// If the node can be changed to atomic based on what comes after it, do so.
	switch .T {
	case NtOneloop, NtNotoneloop, NtSetloop:
		if .canBeMadeAtomic(, true, false) {
			// The greedy loop doesn't overlap with what comes after it, which means giving anything it matches back will not
			// help the overall match to succeed, which means it can simply become atomic to match as much as possible. The call
			// to CanBeMadeAtomic passes iterateNullableSubsequent=true because, in a pattern like a*b*c*, when analyzing a*, we
			// want to examine the b* and the c* rather than just giving up after seeing that b* is nullable; in order to make
			// the a* atomic, we need to know that anything that could possibly come after the loop doesn't overlap.
			.makeLoopAtomic()
		}

	case NtOnelazy, NtNotonelazy, NtSetlazy:
		if .canBeMadeAtomic(, false, true) {
			// The lazy loop doesn't overlap with what comes after it, which means it needs to match as much as its allowed
			// to match in order for there to be a possibility that what comes next matches (if it doesn't match as much
			// as it's allowed and there was still more it could match, then what comes next is guaranteed to not match,
			// since it doesn't match any of the same things the loop matches).  We don't want to just make the lazy loop
			// atomic, as an atomic lazy loop matches as little as possible, not as much as possible.  Instead, we want to
			// make the lazy loop into an atomic greedy loop.  Note that when we check CanBeMadeAtomic, we need to set
			// "iterateNullableSubsequent" to false so that we only inspect non-nullable subsequent nodes.  For example,
			// given a pattern like a*?b, we want to upgrade that loop to being greedy atomic, e.g. (?>a*)b.  But given a
			// pattern like a*?b*, the subsequent node is nullable, which means it doesn't have to be part of a match, which
			// means the a*? could match by itself, in which case as it's lazy it needs to match as few a's as possible, e.g.
			// a+?b* against the input "aaaab" should match "a", not "aaaa" nor "aaaab". (Technically for lazy, we only need to prevent
			// walking off the end of the pattern, but it's not currently worth complicating the implementation for that case.)
			// allowLazy is set to true so that the implementation will analyze rather than ignore this node; generally lazy nodes
			// are ignored due to making them atomic not generally being a sound change, but here we're explicitly choosing to
			// given the circumstances.
			.T -= NtOnelazy - NtOneloop // lazy to greedy
			.makeLoopAtomic()
		}

	case NtAlternate, NtBackRefCond, NtExprCond:
		// In the case of alternation, we can't change the alternation node itself
		// based on what comes after it (at least not with more complicated analysis
		// that factors in all branches together), but we can look at each individual
		// branch, and analyze ending loops in each branch individually to see if they
		// can be made atomic.  Then if we do end up backtracking into the alternation,
		// we at least won't need to backtrack into that loop.  The same is true for
		// conditionals, though we don't want to process the condition expression
		// itself, as it's already considered atomic and handled as part of ReduceExpressionConditional.
		 := 0
		if .T == NtExprCond {
			 = 1
		}
		for ;  < len(.Children); ++ {
			.Children[].()
		}

	}
}

func ( *RegexNode) () *RegexNode {
	// Remove IgnoreCase option from everything except a Backreference
	if .T != NtRef {
		.Options &= ^IgnoreCase
	}
	switch .T {
	case NtAlternate:
		return .reduceAlternation()
	case NtAtomic:
		return .reduceAtomic()
	case NtConcatenate:
		return .reduceConcatenation()
	case NtGroup:
		return .reduceGroup()
	case NtLoop, NtLazyloop:
		return .reduceRep()
	case NtPosLook, NtNegLook:
		return .reduceLookaround()
	case NtSet, NtSetloop, NtSetlazy, NtSetloopatomic:
		return .reduceSet()
	case NtExprCond:
		return .reduceExpressionConditional()
	case NtBackRefCond:
		return .reduceBackreferenceConditional()
	default:
		return 
	}
}

// / <summary>Optimizations for positive and negative lookaheads/behinds.</summary>
func ( *RegexNode) () *RegexNode {
	// A lookaround is a zero-width atomic assertion.
	// As it's atomic, nothing will backtrack into it, and we can
	// eliminate any ending backtracking from it.
	.eliminateEndingBacktracking()

	// A positive lookaround wrapped around an empty is a nop, and we can reduce it
	// to simply Empty.  A developer typically doesn't write this, but rather it evolves
	// due to optimizations resulting in empty.

	// A negative lookaround wrapped around an empty child, i.e. (?!), is
	// sometimes used as a way to insert a guaranteed no-match into the expression,
	// often as part of a conditional. We can reduce it to simply Nothing.

	if .Children[0].T == NtEmpty {
		if .T == NtPosLook {
			.T = NtEmpty
		} else {
			.T = NtNothing
		}
		.Children = nil
	}

	return 
}

// Optimizations for backreference conditionals.
func ( *RegexNode) () *RegexNode {
	// This isn't so much an optimization as it is changing the tree for consistency. We want
	// all engines to be able to trust that every backreference conditional will have two children,
	// even though it's optional in the syntax.  If it's missing a "not matched" branch,
	// we add one that will match empty.
	if len(.Children) == 1 {
		.addChild(&RegexNode{T: NtEmpty, Options: .Options})
	}

	return 
}

// / <summary>Optimizations for expression conditionals.</summary>
func ( *RegexNode) () *RegexNode {
	// This isn't so much an optimization as it is changing the tree for consistency. We want
	// all engines to be able to trust that every expression conditional will have three children,
	// even though it's optional in the syntax.  If it's missing a "not matched" branch,
	// we add one that will match empty.
	if len(.Children) == 2 {
		.addChild(&RegexNode{T: NtEmpty, Options: .Options})
	}

	// It's common for the condition to be an explicit positive lookahead, as specifying
	// that eliminates any ambiguity in syntax as to whether the expression is to be matched
	// as an expression or to be a reference to a capture group.  After parsing, however,
	// there's no ambiguity, and we can remove an extra level of positive lookahead, as the
	// engines need to treat the condition as a zero-width positive, atomic assertion regardless.
	 := .Children[0]
	if .T == NtPosLook && (.Options&RightToLeft) == 0 {
		.ReplaceChild(0, .Children[0])
	}

	// We can also eliminate any ending backtracking in the condition, as the condition
	// is considered to be a positive lookahead, which is an atomic zero-width assertion.
	 = .Children[0]
	.eliminateEndingBacktracking()

	return 
}

// Remove unnecessary atomic nodes, and make appropriate descendents of the atomic node themselves atomic.
// e.g. (?>(?>(?>a*))) => (?>a*)
// e.g. (?>(abc*)*) => (?>(abc(?>c*))*)
func ( *RegexNode) () *RegexNode {
	 := 
	 := .Children[0]
	for .T == NtAtomic {
		 = 
		 = .Children[0]
	}

	switch .T {
	// If the child is empty/nothing, there's nothing to be made atomic so the Atomic
	// node can simply be removed.
	case NtEmpty, NtNothing:
		return 

	// If the child is already atomic, we can just remove the atomic node.
	case NtOneloopatomic, NtNotoneloopatomic, NtSetloopatomic:
		return 

	// If an atomic subexpression contains only a {one/notone/set}{loop/lazy},
	// change it to be an {one/notone/set}loopatomic and remove the atomic node.
	case NtOneloop, NtNotoneloop, NtSetloop, NtOnelazy, NtNotonelazy, NtSetlazy:
		.makeLoopAtomic()
		return 

	// Alternations have a variety of possible optimizations that can be applied
	// iff they're atomic.
	case NtAlternate:
		if (.Options & RightToLeft) == 0 {
			 := .Children

			// If an alternation is atomic and its first branch is Empty, the whole thing
			// is a nop, as Empty will match everything trivially, and no backtracking
			// into the node will be performed, making the remaining branches irrelevant.
			if [0].T == NtEmpty {
				return &RegexNode{T: NtEmpty, Options: .Options}
			}

			// Similarly, we can trim off any branches after an Empty, as they'll never be used.
			// An Empty will match anything, and thus branches after that would only be used
			// if we backtracked into it and advanced passed the Empty after trying the Empty...
			// but if the alternation is atomic, such backtracking won't happen.
			for  := 1;  < len()-1; ++ {
				if [].T == NtEmpty {
					 = slices.Delete(, +1, len())
					break
				}
			}

			// If an alternation is atomic, we won't ever backtrack back into it, which
			// means order matters but not repetition.  With backtracking, it would be incorrect
			// to convert an expression like "hi|there|hello" into "hi|hello|there", as doing
			// so could then change the order of results if we matched "hi" and then failed
			// based on what came after it, and both "hello" and "there" could be successful
			// with what came later.  But without backtracking, we can reorder "hi|there|hello"
			// to instead be "hi|hello|there", as "hello" and "there" can't match the same text,
			// and once this atomic alternation has matched, we won't try another branch. This
			// reordering is valuable as it then enables further optimizations, e.g.
			// "hi|there|hello" => "hi|hello|there" => "h(?:i|ello)|there", which means we only
			// need to check the 'h' once in case it's not an 'h', and it's easier to employ different
			// code gen that, for example, switches on first character of the branches, enabling faster
			// choice of branch without always having to walk through each.
			 := false
			for  := 0;  < len(); ++ {
				// Get the node that may start our range.  If it's a one, multi, or concat of those, proceed.
				 := []
				if .findBranchOneOrMultiStart() == nil {
					continue
				}

				// Find the contiguous range of nodes from this point that are similarly one, multi, or concat of those.
				 :=  + 1
				for  < len() && [].findBranchOneOrMultiStart() != nil {
					++
				}

				// If there's at least 3, there may be something to reorder (we won't reorder anything
				// before the starting position, and so only 2 items is considered ordered).
				if - >= 3 {
					 := 
					for  <  {
						// Get the starting character
						 := [].findBranchOneOrMultiStart().FirstCharOfOneOrMulti()

						// Move compare to point to the last branch that has the same starting value.
						for  <  && [].findBranchOneOrMultiStart().FirstCharOfOneOrMulti() ==  {
							++
						}

						// Compare now points to the first node that doesn't match the starting node.
						// If we've walked off our range, there's nothing left to reorder.
						if  <  {
							// There may be something to reorder.  See if there are any other nodes that begin with the same character.
							for  :=  + 1;  < ; ++ {
								 := []
								if .findBranchOneOrMultiStart().FirstCharOfOneOrMulti() ==  {
									 = slices.Delete(, , +1)
									 = slices.Insert(, , )
									++
									 = true
								}
							}
						}
					}
				}

				// Move to the end of the range we've now explored. endExclusive is not a viable
				// starting position either, and the start++ for the loop will thus take us to
				// the next potential place to start a range.
				 = 
			}
			.Children = 
			// If anything was reordered, there may be new optimization opportunities inside
			// of the alternation, so reduce it again.
			if  {
				.ReplaceChild(0, )
				 = .Children[0]
			}
		}
		fallthrough

	// For everything else, try to reduce ending backtracking of the last contained expression.
	default:
		.eliminateEndingBacktracking()
		if .T == NtAlternate && .hasDisjointStartingSets() && .hasOnlyIntrinsicallyAtomicBranches() {
			return 
		}
		return 
	}
}

// hasDisjointStartingSets reports whether every branch is non-nullable and
// begins with a character set that cannot overlap any other branch. In that
// case, once one branch has matched, no sibling branch could match the same
// starting position.
func ( *RegexNode) () bool {
	,  := .disjointStartingSets()
	return 
}

// disjointStartingSets returns the non-nullable first-character set for each
// branch when all of those sets are pairwise disjoint.
func ( *RegexNode) () ([]*CharSet, bool) {
	 := make([]*CharSet, 0, len(.Children))
	for ,  := range .Children {
		var  *CharSet
		if tryFindFirstCharClass(, &) != 1 ||  == nil {
			return nil, false
		}
		for ,  := range  {
			if .MayOverlap() {
				return nil, false
			}
		}
		 = append(, )
	}
	return , true
}

// disjointAtomicBranchSets returns the first-character sets when an
// alternation can select a branch without leaving a backtracking choice.
func ( *RegexNode) () ([]*CharSet, bool) {
	if .T != NtAlternate || !.hasOnlyIntrinsicallyAtomicBranches() {
		return nil, false
	}
	return .disjointStartingSets()
}

// dispatchCandidates returns disjoint branch sets and the leading nodes that
// can be folded into a consuming Dispatch instruction.
func ( *RegexNode) () ([]*CharSet, []*RegexNode, []bool, bool) {
	if len(.Children) >= 1<<16 {
		return nil, nil, nil, false
	}
	,  := .disjointAtomicBranchSets()
	if ! {
		return nil, nil, nil, false
	}

	 := make([]*RegexNode, len(.Children))
	 := make([]bool, len(.Children))
	for  := range  {
		 := .Children[]
		 := 
		if .T == NtConcatenate && len(.Children) > 0 {
			 = .Children[0]
		}
		switch .T {
		case NtOne, NtNotone, NtSet, NtMulti:
			[] = 
		default:
			return nil, nil, nil, false
		}
		[] =  ==  && (.T != NtMulti || len(.Str) == 1)
	}
	return , , , true
}

func ( *RegexNode) () bool {
	for ,  := range .Children {
		if !.isIntrinsicallyAtomic() {
			return false
		}
	}
	return true
}

// isIntrinsicallyAtomic reports whether a successful match of this node has no
// alternative input-consuming path to explore if something later fails.
func ( *RegexNode) () bool {
	switch .T {
	case NtOne, NtNotone, NtSet, NtMulti, NtRef, NtGrapheme,
		NtBol, NtEol, NtBoundary, NtNonboundary, NtECMABoundary, NtNonECMABoundary,
		NtBeginning, NtStart, NtEndZ, NtEnd, NtNothing, NtEmpty, NtUpdateBumpalong,
		NtOneloopatomic, NtNotoneloopatomic, NtSetloopatomic,
		NtPosLook, NtNegLook, NtAtomic:
		return true

	case NtOneloop, NtNotoneloop, NtSetloop, NtOnelazy, NtNotonelazy, NtSetlazy:
		return .M == .N

	case NtCapture, NtGroup:
		return len(.Children) == 1 && .Children[0].()

	case NtConcatenate:
		for ,  := range .Children {
			if !.() {
				return false
			}
		}
		return true
	}
	return false
}

func ( *RegexNode) () {

	switch .T {
	case NtOneloop, NtNotoneloop, NtSetloop:
		// For loops, we simply change the Type to the atomic variant.
		// Atomic greedy loops should consume as many values as they can.
		.T += NtOneloopatomic - NtOneloop

	case NtOnelazy, NtNotonelazy, NtSetlazy:
		// For lazy, we not only change the Type, we also lower the max number of iterations
		// to the minimum number of iterations, creating a repeater, as they should end up
		// matching as little as possible.
		.T += NtOneloopatomic - NtOnelazy
		.N = .M
		if .N == 0 {
			// If moving the max to be the same as the min dropped it to 0, there's no
			// work to be done for this node, and we can make it Empty.
			.T = NtEmpty
			.Str = nil
			.Ch = 0x0
		} else if .T == NtOneloopatomic && .N >= 2 && .N <= MultiVsRepeaterLimit {
			// If this is now a One repeater with a small enough length,
			// make it a Multi instead, as they're better optimized down the line.
			.T = NtMulti
			.Str = []rune(strings.Repeat(string(.Ch), .N))
			.Ch = 0x0
			.M = 0
			.N = 0
		}
	}
}

// Converts nodes at the end of the node tree to be atomic.
// The correctness of this optimization depends on nothing being able to backtrack into
// the provided node.  That means it must be at the root of the overall expression, or
// it must be an Atomic node that nothing will backtrack into by the very nature of Atomic.
func ( *RegexNode) () {
	// Walk the tree starting from the current node.
	 := 
	for {
		switch .T {
		// {One/Notone/Set}loops can be upgraded to {One/Notone/Set}loopatomic nodes, e.g. [abc]* => (?>[abc]*).
		// And {One/Notone/Set}lazys can similarly be upgraded to be atomic, which really makes them into repeaters
		// or even empty nodes.
		case NtOneloop, NtNotoneloop, NtSetloop, NtOnelazy, NtNotonelazy, NtSetlazy:
			.makeLoopAtomic()

		// Just because a particular node is atomic doesn't mean all its descendants are.
		// Process them as well. Lookarounds are implicitly atomic.
		case NtAtomic, NtPosLook, NtNegLook:
			 = .Children[0]
			continue

		case NtCapture, NtConcatenate:
			// For Capture and Concatenate, we just recur into their last child (only child in the case
			// of Capture).  However, if the child is an alternation or loop, we can also make the
			// node itself atomic by wrapping it in an Atomic node. Since we later check to see whether a
			// node is atomic based on its parent or grandparent, we don't bother wrapping such a node in
			// an Atomic one if its grandparent is already Atomic.
			// e.g. [xyz](?:abc|def) => [xyz](?>abc|def)

			// validate grandparent isn't atomic
			 := .Children[len(.Children)-1]
			if (.T == NtAlternate || .T == NtBackRefCond ||
				.T == NtExprCond || .T == NtLoop ||
				.T == NtLazyloop) &&
				(.Parent == nil || .Parent.T != NtAtomic) {

				 := &RegexNode{T: NtAtomic, Options: .Options}
				.addChild()
				.ReplaceChild(len(.Children)-1, )
			}
			 = 
			continue

		// For alternate, we can recur into each branch separately.  We use this iteration for the first branch.
		// Conditionals are just like alternations in this regard.
		// e.g. abc*|def* => ab(?>c*)|de(?>f*)
		case NtAlternate, NtBackRefCond, NtExprCond:

			 := len(.Children)
			for  := 1;  < ; ++ {
				.Children[].()
			}

			// ReduceExpressionConditional will have already applied ending backtracking removal
			if .T != NtExprCond {
				 = .Children[0]
				continue
			}

		// For {Lazy}Loop, we search to see if there's a viable last expression, and iff there
		// is we recur into processing it.  Also, as with the single-char lazy loops, LazyLoop
		// can have its max iteration count dropped to its min iteration count, as there's no
		// reason for it to match more than the minimal at the end; that in turn makes it a
		// repeater, which results in better code generation.
		// e.g. (?:abc*)* => (?:ab(?>c*))*
		// e.g. (abc*?)+? => (ab){1}
		case NtLazyloop:
			.N = .M
			fallthrough
		case NtLoop:
			if .N == 1 {
				// If the loop has a max iteration count of 1 (e.g. it's an optional node),
				// there's no possibility for conflict between multiple iterations, so
				// we can process it.
				 = .Children[0]
				continue
			}

			 := .FindLastExpressionInLoopForAutoAtomic()
			if  != nil {
				 = 
				continue // loop around to process node
			}

		}

		break
	}
}

// Recurs into the last expression of a loop node, looking to see if it can find a node
// that could be made atomic _assuming_ the conditions exist for it with the loop's ancestors.
// Returns The found node that should be explored further for auto-atomicity; null if it doesn't exist.
func ( *RegexNode) () *RegexNode {
	 := 

	// Start by looking at the loop's sole child.
	 = .Children[0]

	// Skip past captures.
	for .T == NtCapture {
		 = .Children[0]
	}

	// If the loop's body is a concatenate, we can skip to its last child iff that
	// last child doesn't conflict with the first child, since this whole concatenation
	// could be repeated, such that the first node ends up following the last.  For
	// example, in the expression (a+[def])*, the last child is [def] and the first is
	// a+, which can't possibly overlap with [def].  In contrast, if we had (a+[ade])*,
	// [ade] could potentially match the starting 'a'.
	if .T == NtConcatenate {
		 := len(.Children)
		 := .Children[-1]
		if .canBeMadeAtomic(.Children[0], false, false) {
			return 
		}
	}

	// Otherwise, the loop has nothing that can participate in auto-atomicity.
	return nil
}

// Determines whether a node can be switched to an atomic loop.
//
// The node following is subsequent, used to determine whether it overlaps.
// iterateNullableSubsequent is whether to allow examining nodes beyond subsequent.
// allowLazy is whether lazy loops in addition to greedy loops should be considered for atomicity.
func ( *RegexNode) ( *RegexNode, ,  bool) bool {
	// In most case, we'll simply check the node against whatever subsequent is.  However, in case
	// subsequent ends up being a loop with a min bound of 0, we'll also need to evaluate the node
	// against whatever comes after subsequent.  In that case, we'll walk the tree to find the
	// next subsequent, and we'll loop around against to perform the comparison again.
	for {
		// Skip the successor down to the closest node that's guaranteed to follow it.
		 := len(.Children)
		for ;  > 0;  = len(.Children) {
			if .T == NtConcatenate || .T == NtCapture ||
				.T == NtAtomic ||
				(.T == NtPosLook && .Options&RightToLeft == 0) ||
				((.T == NtLoop || .T == NtLazyloop) && .M > 0) {
				 = .Children[0]
				continue
			}

			break
		}

		// If the current node's options don't match the subsequent node, then we cannot make it atomic.
		// This applies to RightToLeft for lookbehinds, as well as patterns that enable/disable global flags in the middle of the pattern.
		if .Options != .Options {
			return false
		}

		// If the successor is an alternation, all of its children need to be evaluated, since any of them
		// could come after this node.  If any of them fail the optimization, then the whole node fails.
		// This applies to expression conditionals as well, as long as they have both a yes and a no branch (if there's
		// only a yes branch, we'd need to also check whatever comes after the conditional).  It doesn't apply to
		// backreference conditionals, as the condition itself is unknown statically and could overlap with the
		// loop being considered for atomicity.
		if .T == NtAlternate || (.T == NtExprCond &&  == 3) {
			// condition, yes, and no branch
			for  := 0;  < ; ++ {
				if !.(.Children[], , false) {
					return false
				}
			}
			return true
		}

		// If this node is a {one/notone/set}loop, see if it overlaps with its successor in the concatenation.
		// If it doesn't, then we can upgrade it to being a {one/notone/set}loopatomic.
		// Doing so avoids unnecessary backtracking.
		if .T == NtOneloop || (.T == NtOnelazy && ) {

			if (.T == NtOne && .Ch != .Ch) ||
				(.T == NtNotone && .Ch == .Ch) ||
				(.T == NtSet && !.Set.CharIn(.Ch)) ||
				(.IsOneFamily() && .M > 0 && .Ch != .Ch) ||
				(.IsNotoneFamily() && .M > 0 && .Ch == .Ch) ||
				(.IsSetFamily() && .M > 0 && !.Set.CharIn(.Ch)) ||
				(.T == NtMulti && .Ch != .Str[0]) ||
				(.T == NtEnd) ||
				(.T == NtEndZ && .Ch != '\n') ||
				(.T == NtEol && .Ch != '\n') {
				return true
			}

			// The loop can be made atomic based on this subsequent node, but we'll need to evaluate the next one as well.
			if (.IsOneloopFamily() && .M == 0 && .Ch != .Ch) ||
				(.IsNotoneloopFamily() && .M == 0 && .Ch == .Ch) ||
				(.IsSetloopFamily() && .M == 0 && !.Set.CharIn(.Ch)) ||
				(.T == NtBoundary && .M > 0 && IsWordChar(.Ch)) ||
				(.T == NtNonboundary && .M > 0 && !IsWordChar(.Ch)) ||
				(.T == NtECMABoundary && .M > 0 && IsECMAWordChar(.Ch)) ||
				(.T == NtNonECMABoundary && .M > 0 && !IsECMAWordChar(.Ch)) {
				goto 
			}

			return false

		} else if .T == NtNotoneloop || (.T == NtNotonelazy && ) {
			if (.T == NtOne && .Ch == .Ch) ||
				(.IsOneFamily() && .M > 0 && .Ch == .Ch) ||
				(.T == NtMulti && .Ch == .Str[0]) ||
				(.T == NtEnd) {
				return true
			}

			// The loop can be made atomic based on this subsequent node, but we'll need to evaluate the next one as well.
			if .IsOneloopFamily() && .M == 0 && .Ch == .Ch {
				goto 
			}

			return false
		} else if .T == NtSetloop || (.T == NtSetlazy && ) {
			if (.T == NtOne && !.Set.CharIn(.Ch)) ||
				(.T == NtSet && !.Set.MayOverlap(.Set)) ||
				(.IsOneloopFamily() && .M > 0 && !.Set.CharIn(.Ch)) ||
				(.IsSetloopFamily() && .M > 0 && !.Set.MayOverlap(.Set)) ||
				(.T == NtMulti && !.Set.CharIn(.Str[0])) ||
				(.T == NtEnd) ||
				(.T == NtEndZ && !.Set.CharIn('\n')) ||
				(.T == NtEol && !.Set.CharIn('\n')) {
				return true
			}

			if (.IsOneloopFamily() && .M == 0 && !.Set.CharIn(.Ch)) ||
				(.IsSetloopFamily() && .M == 0 && .Set.MayOverlap(.Set)) ||
				(.T == NtBoundary && .M > 0 && (.Set.Equals(WordClass()) || .Set.Equals(DigitClass()))) ||
				(.T == NtNonboundary && .M > 0 && (.Set.Equals(NotWordClass()) || .Set.Equals(NotDigitClass()))) ||
				(.T == NtECMABoundary && .M > 0 && (.Set.Equals(ECMAWordClass()) || .Set.Equals(ECMADigitClass()))) ||
				(.T == NtNonECMABoundary && .M > 0 && (.Set.Equals(NotECMAWordClass()) || .Set.Equals(NotDigitClass()))) {
				// The loop can be made atomic based on this subsequent node, but we'll need to evaluate the next one as well.
				goto 
			}
			return false
		} else {
			return false
		}

	:
		// We only get here if the node could be made atomic based on subsequent but subsequent has a lower bound of zero
		// and thus we need to move subsequent to be the next node in sequence and loop around to try again.
		if ! {
			return false
		}

		// To be conservative, we only walk up through a very limited set of constructs (even though we may have walked
		// down through more, like loops), looking for the next concatenation that we're not at the end of, at
		// which point subsequent becomes whatever node is next in that concatenation.
		for {
			 := .Parent
			if  == nil {
				// If we hit the root, we're at the end of the expression, at which point nothing could backtrack
				// in and we can declare success.
				return true
			}

			switch .T {
			case NtAtomic, NtAlternate, NtCapture:
				 = 
				continue

			case NtConcatenate:
				 := .Children
				 := slices.Index(, )

				if +1 == len() {
					 = 
					continue
				} else {
					 = [+1]
				}

			default:
				// Anything else, we don't know what to do, so we have to assume it could conflict with the loop.
				return false
			}

			break
		}
	}
}

// Basic optimization. Single-letter alternations can be replaced
// by faster set specifications, and nested alternations with no
// intervening operators can be flattened:
//
// a|b|c|def|g|h -> [a-c]|def|[gh]
// apple|(?:orange|pear)|grape -> apple|orange|pear|grape
func ( *RegexNode) () *RegexNode {
	if len(.Children) == 0 {
		return newRegexNode(NtNothing, .Options)
	}
	if len(.Children) == 1 {
		return .Children[0]
	}
	.reduceSingleLetterAndNestedAlternations()

	 := .replaceNodeIfUnnecessary()
	if .T == NtAlternate {
		 = .extractCommonPrefixText()
		if .T == NtAlternate {
			 = .extractCommonPrefixOneNotoneSet()
			if .T == NtAlternate {
				 = .removeRedundantEmptiesAndNothings()
			}
		}
	}
	return 
}

// Analyzes all the branches of the alternation for text that's identical at the beginning
// of every branch.  That text is then pulled out into its own one or multi node in a
// concatenation with the alternation (whose branches are updated to remove that prefix).
// This is valuable for a few reasons.  One, it exposes potentially more text to the
// expression prefix analyzer used to influence FindFirstChar.  Second, it exposes more
// potential alternation optimizations, e.g. if the same prefix is followed in two branches
// by sets that can be merged.  Third, it reduces the amount of duplicated comparisons required
// if we end up backtracking into subsequent branches.
// e.g. abc|ade => a(?bc|de)
func ( *RegexNode) () *RegexNode {
	// To keep things relatively simple, we currently only handle:
	// - Left to right (e.g. we don't process alternations in lookbehinds)
	// - Branches that are one or multi nodes, or that are concatenations beginning with one or multi nodes.
	// - All branches having the same options.

	// Only extract left-to-right prefixes.
	if (.Options & RightToLeft) != 0 {
		return 
	}

	for  := 0;  < len(.Children)-1; ++ {
		// Process the first branch to get the maximum possible common string.
		 := .Children[].findBranchOneOrMultiStart()
		if  == nil {
			return 
		}

		 := .Options
		 := .Str
		if .T == NtOne {
			 = []rune{.Ch}
		}

		// Now compare the rest of the branches against it.
		 :=  + 1
		for ;  < len(.Children); ++ {
			// Get the starting node of the next branch.
			 = .Children[].findBranchOneOrMultiStart()
			if  == nil || .Options !=  {
				break
			}

			// See if the new branch's prefix has a shared prefix with the current one.
			// If it does, shorten to that; if it doesn't, bail.
			if .T == NtOne {
				if [0] != .Ch {
					break
				}

				if len() != 1 {
					 = [0:1]
				}
			} else {
				 := len()
				if len(.Str) <  {
					 = len(.Str)
				}
				 := 0
				for  <  && [] == .Str[] {
					++
				}

				if  == 0 {
					break
				}

				 = [0:]
			}
		}

		// When we get here, we have a starting string prefix shared by all branches
		// in the range [startingIndex, endingIndex).
		if - <= 1 {
			// There's nothing to consolidate for this starting node.
			continue
		}

		// We should be able to consolidate something for the nodes in the range [startingIndex, endingIndex).

		// Create a new node of the form:
		//     Concatenation(prefix, Alternation(each | node | with | prefix | removed))
		// that replaces all these branches in this alternation.
		var  *RegexNode
		if len() == 1 {
			 = &RegexNode{T: NtOne, Options: , Ch: [0]}
		} else {
			 = &RegexNode{T: NtMulti, Options: , Str: slices.Clone()}
		}

		 := &RegexNode{T: NtAlternate, Options: }
		for  := ;  < ; ++ {
			 := .Children[]
			if .T == NtConcatenate {
				.Children[0].processOneOrMulti()
			} else {
				.processOneOrMulti()
			}
			 = .reduce()
			.addChild()
		}

		if .Parent != nil && .Parent.T == NtAtomic {
			var  = &RegexNode{T: NtAtomic, Options: }
			.addChild()
			 = 
		}

		 := &RegexNode{T: NtConcatenate, Options: }
		.addChild()
		.addChild()
		.ReplaceChild(, )
		.Children = slices.Delete(.Children, +1, )
	}

	if len(.Children) == 1 {
		return .Children[0]
	}

	return 
}

// This function optimizes out prefix nodes from alternation branches that are
// the same across multiple contiguous branches.
// e.g. \w12|\d34|\d56|\w78|\w90 => \w12|\d(?:34|56)|\w(?:78|90)
func ( *RegexNode) () *RegexNode {
	// Only process left-to-right prefixes.
	if (.Options & RightToLeft) != 0 {
		return 
	}

	// Only handle the case where each branch is a concatenation
	for ,  := range .Children {
		if .T != NtConcatenate || len(.Children) < 2 {
			return 
		}
	}

	for  := 0;  < len(.Children)-1; ++ {
		// Only handle the case where each branch begins with the same One, Notone, or Set (individual or loop).
		// Note that while we can do this for individual characters, fixed length loops, and atomic loops, doing
		// it for non-atomic variable length loops could change behavior as each branch could otherwise have a
		// different number of characters consumed by the loop based on what's after it.
		 := .Children[].Children[0]

		if (!.IsOneFamily() && !.IsNotoneFamily() && !.IsSetFamily()) ||
			.M != .N {
			// skip if it's not one of these scenarios
			continue
		}

		// Only handle the case where each branch begins with the exact same node value
		 :=  + 1
		for ;  < len(.Children); ++ {
			 := .Children[].Children[0]
			if .T != .T ||
				.Options != .Options ||
				.M != .M ||
				.N != .N ||
				.Ch != .Ch ||
				!slices.Equal(.Str, .Str) ||
				!.Set.Equals(.Set) {
				break
			}
		}

		if - <= 1 {
			// Nothing to extract from this starting index.
			continue
		}

		// Remove the prefix node from every branch, adding it to a new alternation
		 := &RegexNode{T: NtAlternate, Options: .Options}
		for  := ;  < ; ++ {
			.Children[].Children = slices.Delete(.Children[].Children, 0, 1)
			.addChild(.Children[])
		}

		// If this alternation is wrapped as atomic, we need to do the same for the new alternation.
		if .Parent != nil && .Parent.T == NtAtomic {
			 := &RegexNode{T: NtAtomic, Options: .Options}
			.addChild()
			 = 
		}

		// Now create a concatenation of the prefix node with the new alternation for the combined
		// branches, and replace all of the branches in this alternation with that new concatenation.
		 := &RegexNode{T: NtConcatenate, Options: .Options}
		.addChild()
		.addChild()
		.ReplaceChild(, )
		.Children = slices.Delete(.Children, +1, )
	}

	return .replaceNodeIfUnnecessary()
}

// Removes unnecessary Empty and Nothing nodes from the alternation. A Nothing will never
// match, so it can be removed entirely, and an Empty can be removed if there's a previous
// Empty in the alternation: it's an extreme case of just having a repeated branch in an
// alternation, and while we don't check for all duplicates, checking for empty is easy.
func ( *RegexNode) () *RegexNode {
	 := .Children

	,  := 0, 0
	 := false
	for  < len() {
		 := []
		if .T == NtNothing || (.T == NtEmpty && ) {
			++
			continue
		}
		if .T == NtEmpty {
			 = true
		}
		[] = []
		++
		++
	}

	.Children = slices.Delete(, , len())
	return .replaceNodeIfUnnecessary()
}

// Remove the starting text from the one or multi node.  This may end up changing
// the type of the node to be Empty if the starting text matches the node's full value.
func ( *RegexNode) ( []rune) {
	if .T == NtOne {
		.T = NtEmpty
		.Ch = 0x0
	} else {
		if len(.Str) == len() {
			.T = NtEmpty
			.Str = nil
		} else if len(.Str)-1 == len() {
			.T = NtOne
			.Ch = .Str[len(.Str)-1]
			.Str = nil
		} else {
			.Str = .Str[len():]
		}
	}
}

// Finds the starting one or multi of the branch, if it has one; otherwise, returns null.
// For simplicity, this only considers branches that are One or Multi, or a Concatenation
// beginning with a One or Multi.  We don't traverse more than one level to avoid the
// complication of then having to later update that hierarchy when removing the prefix,
// but it could be done in the future if proven beneficial enough.
func ( *RegexNode) () *RegexNode {
	 := 
	if .T == NtConcatenate {
		 = .Children[0]
	}
	if .T == NtOne || .T == NtMulti {
		return 
	}
	return nil
}

func ( *RegexNode) () {

	 := false
	 := false
	var  RegexOptions
	var ,  int

	for ,  = 0, 0;  < len(.Children); ,  = +1, +1 {
		 := .Children[]

		if  <  {
			.Children[] = 
		}

		for {
			if .T == NtAlternate {
				.insertChildren(+1, .Children)
				--
			} else if .T == NtSet || .T == NtOne {
				// Cannot merge sets if L or I options differ, or if either are negated.
				 := .Options & (RightToLeft | IgnoreCase)

				if .T == NtSet {
					if ! ||  !=  ||  || !.Set.IsMergeable() {
						 = true
						 = !.Set.IsMergeable()
						 = 
						break
					}
				} else if ! ||  !=  ||  {
					 = true
					 = false
					 = 
					break
				}

				// The last node was a Set or a One, we're a Set or One and our options are the same.
				// Merge the two nodes.
				--
				 := .Children[]

				var  *CharSet
				if .T == NtOne {
					 = &CharSet{}
					.addChar(.Ch)
				} else {
					 = .Set
				}

				if .T == NtOne {
					.addChar(.Ch)
				} else {
					.addSet(*.Set)
				}

				.T = NtSet
				.Set = 
				if .Options&IgnoreCase != 0 {
					.Options &= ^IgnoreCase
				}
			} else if .T == NtNothing {
				--
			} else {
				 = false
				 = false
			}
			break
		}
	}

	if  <  {
		.removeChildren(, )
	}
}

func ( *RegexNode) () *RegexNode {
	// Eliminate empties and concat adjacent strings/chars

	if len(.Children) == 0 {
		return newRegexNode(NtEmpty, .Options)
	}
	// remove concat
	if len(.Children) == 1 {
		return .Children[0]
	}

	// If any node in the concatenation is a Nothing, the concatenation itself is a Nothing.
	for  := 0;  < len(.Children); ++ {
		 := .Children[]
		if .T == NtNothing {
			return 
		}
	}

	// Coalesce adjacent loops.  This helps to minimize work done by the interpreter, minimize code gen,
	// and also help to reduce catastrophic backtracking.
	.reduceConcatenationWithAdjacentLoops()

	// Coalesce adjacent characters/strings.  This is done after the adjacent loop coalescing so that
	// a One adjacent to both a Multi and a Loop prefers being folded into the Loop rather than into
	// the Multi.  Doing so helps with auto-atomicity when it's later applied.
	.reduceConcatenationWithAdjacentStrings()

	// If the concatenation is now empty, return an empty node, or if it's got a single child, return that child.
	// Otherwise, return this.
	return .replaceNodeIfUnnecessary()
}

func addMinLength(,  int) int {
	const  = math.MaxInt32 - 1
	if  >=  ||  >=  ||  > - {
		return 
	}
	return  + 
}

func multiplyMinLength(,  int) int {
	const  = math.MaxInt32 - 1
	if  == 0 ||  == 0 {
		return 0
	}
	if  >=  ||  >=  ||  > / {
		return 
	}
	return  * 
}

func addMaxLength(,  int) int {
	if  < 0 ||  < 0 ||  >= math.MaxInt32 ||  >= math.MaxInt32 ||  > (math.MaxInt32-1)- {
		return -1
	}
	return  + 
}

func multiplyMaxLength(,  int) int {
	if  < 0 ||  < 0 {
		return -1
	}
	if  == 0 ||  == 0 {
		return 0
	}
	if  >= math.MaxInt32 ||  >= math.MaxInt32 ||  > (math.MaxInt32-1)/ {
		return -1
	}
	return  * 
}

func maxLessThanTwiceMin(,  int) bool {
	if  <= math.MaxInt32/2 {
		return  < *2
	}
	return  != math.MaxInt32
}

func canCombineCounts(, , ,  int) bool {
	// We shouldn't have an infinite minimum; bail if we find one. Also check for the
	// degenerate case where we'd make the min overflow or go infinite when it wasn't already.
	if  == math.MaxInt32 ||
		 == math.MaxInt32 ||
		addMaxLength(, ) < 0 {
		return false
	}

	// Similar overflow / go infinite check for max (which can be infinite).
	if  != math.MaxInt32 &&
		 != math.MaxInt32 &&
		addMaxLength(, ) < 0 {
		return false
	}

	return true
}

// Combine adjacent loops.
// e.g. a*a*a* => a*
// e.g. a+ab => a{2,}b
func ( *RegexNode) () {
	, ,  := 0, 1, 1

	for  < len(.Children) {
		 := .Children[]
		 := .Children[]

		if .Options == .Options {
			// Coalescing a loop with its same type
			if ((.IsOneloopFamily() || .IsNotoneloopFamily()) && .T == .T && .Ch == .Ch) ||
				(.IsSetloopFamily() && .T == .T && .Set.Equals(.Set)) {
				if .M > 0 && .IsAtomicloopFamily() {
					// Atomic loops can only be combined if the second loop has no lower bound, as if it has a lower bound,
					// combining them changes behavior. Uncombined, the first loop can consume all matching items;
					// the second loop might then not be able to meet its minimum and fail.  But if they're combined, the combined
					// minimum of the sole loop could now be met, introducing matches where there shouldn't have been any.
					goto 
				}

				if !canCombineCounts(.M, .N, .M, .N) {
					goto 
				}
				.M += .M
				if .N != math.MaxInt32 {
					if .N == math.MaxInt32 {
						.N = math.MaxInt32
					} else {
						.N += .N
					}
				}
				++
				continue

			} else if ((.T == NtOneloop || .T == NtOnelazy) && .T == NtOne && .Ch == .Ch) ||
				((.T == NtNotoneloop || .T == NtNotonelazy) && .T == NtNotone && .Ch == .Ch) ||
				((.T == NtSetloop || .T == NtSetlazy) && .T == NtSet && .Set.Equals(.Set)) {
				// Coalescing a loop with an additional item of the same type
				if canCombineCounts(.M, .N, 1, 1) {
					.M++
					if .N != math.MaxInt32 {
						.N++
					}
					++
					continue
				}
			} else if (.T == NtOneloop || .T == NtOnelazy) && .T == NtMulti && .Ch == .Str[0] {
				// Coalescing a loop with a subsequent string
				// Determine how many of the multi's characters can be combined.
				// We already checked for the first, so we know it's at least one.
				 := 1
				for  < len(.Str) && .Ch == .Str[] {
					++
				}

				if canCombineCounts(.M, .N, , ) {
					// Update the loop's bounds to include those characters from the multi
					.M += 
					if .N != math.MaxInt32 {
						.N += 
					}

					// If it was the full multi, skip/remove the multi and continue processing this loop.
					if len(.Str) ==  {
						++
						continue
					}

					// Otherwise, trim the characters from the multiple that were absorbed into the loop.
					// If it now only has a single character, it becomes a One.
					if len(.Str)- == 1 {
						.T = NtOne
						.Ch = .Str[len(.Str)-1]
						.Str = nil
					} else {
						.Str = .Str[:]
					}
				}

				// NOTE: We could add support for coalescing a string with a subsequent loop, but the benefits of that
				// are limited. Pulling a subsequent string's prefix back into the loop helps with making the loop atomic,
				// but if the loop is after the string, pulling the suffix of the string forward into the loop may actually
				// be a deoptimization as those characters could end up matching more slowly as part of loop matching.

			} else if (.T == NtOne && .IsOneloopFamily() && .Ch == .Ch) ||
				(.T == NtNotone && .IsNotoneloopFamily() && .Ch == .Ch) ||
				(.T == NtSet && .IsSetloopFamily() && .Set.Equals(.Set)) {
				// Coalescing an individual item with a loop.
				if canCombineCounts(1, 1, .M, .N) {
					.T = .T
					.M = .M + 1
					if .N == math.MaxInt32 {
						.N = math.MaxInt32
					} else {
						.N = .N + 1
					}
					++
					continue
				}
			} else if (.T == NtNotone && .T == NtNotone && .Ch == .Ch) ||
				(.T == NtSet && .T == NtSet && .Set.Equals(.Set)) {
				// Coalescing an individual item with another individual item.
				// We don't coalesce adjacent One nodes into a Oneloop as we'd rather they be joined into a Multi.
				.makeRep(NtOneloop, 2, 2)
				++
				continue
			}

		:
		}

		.Children[] = .Children[]
		++
		 = 
		++
	}

	if  < len(.Children) {
		.Children = slices.Delete(.Children, , len(.Children))
	}
}

// Basic optimization. Adjacent strings can be concatenated.
//
// (?:abc)(?:def) -> abcdef
func ( *RegexNode) () {
	var  RegexOptions
	var  RegexOptions
	var ,  int

	 := false

	for ,  = 0, 0;  < len(.Children); ,  = +1, +1 {
		var ,  *RegexNode

		 = .Children[]

		if  <  {
			.Children[] = 
		}

		if .T == NtConcatenate &&
			((.Options & RightToLeft) == (.Options & RightToLeft)) {
			for  := 0;  < len(.Children); ++ {
				.Children[].Parent = 
			}

			//insert at.children at i+1 index in n.children
			.insertChildren(+1, .Children)

			--
		} else if .T == NtMulti || .T == NtOne {
			// Cannot merge strings if L or I options differ
			 = .Options & (RightToLeft | IgnoreCase)

			if ! ||  !=  {
				 = true
				 = 
				continue
			}

			--
			 = .Children[]

			if .T == NtOne {
				.T = NtMulti
				.Str = []rune{.Ch}
			}

			if ( & RightToLeft) == 0 {
				if .T == NtOne {
					.Str = append(.Str, .Ch)
				} else {
					.Str = append(.Str, .Str...)
				}
			} else {
				if .T == NtOne {
					// insert at the front by expanding our slice, copying the data over, and then setting the value
					.Str = append(.Str, 0)
					copy(.Str[1:], .Str)
					.Str[0] = .Ch
				} else {
					//insert at the front...this one we'll make a new slice and copy both into it
					 := make([]rune, len(.Str)+len(.Str))
					copy(, .Str)
					copy([len(.Str):], .Str)
					.Str = 
				}
			}
		} else if .T == NtEmpty {
			--
		} else {
			 = false
		}
	}

	if  <  {
		// remove indices j through i from the children
		.removeChildren(, )
	}
}

// Nested repeaters just get multiplied with each other if they're not
// too lumpy
func ( *RegexNode) () *RegexNode {
	// Repeating a literal empty expression has no observable effect. In addition to
	// being unnecessary, retaining the loop would require one backtracking frame per
	// mandatory iteration even though no input is consumed.
	if len(.Children) == 1 && .Children[0].T == NtEmpty {
		return .Children[0]
	}

	 := 
	 := .T
	 := .M
	 := .N

	for len(.Children) > 0 {
		 := .Children[0]

		// multiply reps of the same type only
		if .T !=  {
			 := false
			if  == NtLoop {
				switch .T {
				case NtOneloop, NtOneloopatomic, NtNotoneloop,
					NtNotoneloopatomic, NtSetloop, NtSetloopatomic:
					 = true
				}
			} else {
				switch .T {
				case NtOnelazy, NtNotonelazy, NtSetlazy:
					 = true
				}
			}
			if ! {
				break
			}
		}

		// child can be too lumpy to blur, e.g., (a {100,105}) {3} or (a {2,})?
		// [but things like (a {2,})+ are not too lumpy...]
		if .M == 0 && .M > 1 || maxLessThanTwiceMin(.N, .M) {
			break
		}

		 = 
		if .M > 0 {
			if (math.MaxInt32-1)/.M <  {
				.M = math.MaxInt32
			} else {
				.M *= 
			}
		}
		if .N > 0 {
			if (math.MaxInt32-1)/.N <  {
				.N = math.MaxInt32
			} else {
				.N *= 
			}
		}
	}

	if  == math.MaxInt32 {
		return newRegexNode(NtNothing, .Options)
	}

	// If the Loop or Lazyloop now only has one child node and its a Set, One, or Notone,
	// reduce to just Setloop/lazy, Oneloop/lazy, or Notoneloop/lazy.  The parser will
	// generally have only produced the latter, but other reductions could have exposed
	// this.
	if len(.Children) == 1 {
		 := .Children[0]
		switch .T {
		case NtOne, NtNotone, NtSet:
			if .T == NtLazyloop {
				.makeRep(NtOnelazy, .M, .N)
			} else {
				.makeRep(NtOneloop, .M, .N)
			}
			 = 
		}
	}

	return 

}

// Simple optimization. If a concatenation or alternation has only
// one child strip out the intermediate node. If it has zero children,
// turn it into an empty.
func ( *RegexNode) () *RegexNode {
	switch len(.Children) {
	case 0:
		 := NtEmpty
		if .T == NtAlternate {
			 = NtNothing
		}
		return newRegexNode(, .Options)
	case 1:
		return .Children[0]
	default:
		return 
	}
}

func ( *RegexNode) () *RegexNode {
	 := 

	for .T == NtGroup {
		 = .Children[0]
	}

	return 
}

// Simple optimization. If a set is a singleton, an inverse singleton,
// or empty, it's transformed accordingly.
func ( *RegexNode) () *RegexNode {
	// Extract empty-set, one and not-one case as special

	if .Set == nil {
		.T = NtNothing
	} else if .Set.IsSingleton() {
		.Ch = .Set.SingletonChar()
		.Set = nil
		.T += (NtOne - NtSet)
	} else if .Set.IsSingletonInverse() {
		.Ch = .Set.SingletonChar()
		.Set = nil
		.T += (NtNotone - NtSet)
	}

	return 
}

func ( *RegexNode) () *RegexNode {
	if .Options&RightToLeft != 0 && .T == NtConcatenate && len(.Children) > 0 {
		//reverse children order
		for ,  := 0, len(.Children)-1;  < ; ,  = +1, -1 {
			.Children[], .Children[] = .Children[], .Children[]
		}
	}

	return 
}

func ( *RegexNode) ( bool, ,  int) *RegexNode {
	// Certain cases of repeaters (min == max) can be handled specially
	if  == 0 &&  == 0 {
		// The node is repeated 0 times, so it's actually empty.
		return newRegexNode(NtEmpty, .Options)
	}

	if  == 1 &&  == 1 {
		// The node is repeated 1 time, so it's not actually a repeater.
		return 
	}

	if  ==  &&  <= MultiVsRepeaterLimit && .T == NtOne {
		// The same character is repeated a fixed number of times, so it's actually a multi.
		// While this could remain a repeater, multis are more readily optimized later in
		// processing. The counts used here in real-world expressions are invariably small (e.g. 4),
		// but we set an upper bound just to avoid creating really large strings.
		.T = NtMulti
		.Str = []rune(strings.Repeat(string(.Ch), ))
		.Ch = 0
		return 
	}

	switch .T {
	case NtOne, NtNotone, NtSet:
		if  {
			.makeRep(NtOnelazy, , )
		} else {
			.makeRep(NtOneloop, , )
		}
		return 

	default:
		var  NodeType
		if  {
			 = NtLazyloop
		} else {
			 = NtLoop
		}
		 := newRegexNodeMN(, .Options, , )
		.addChild()
		return 
	}
}

// Computes a min bound on the required length of any string that could possibly match.
// If the result is 0, there is no minimum we can enforce.
func ( *RegexNode) () int {
	switch .T {
	case NtOne, NtNotone, NtSet, NtGrapheme:
		// single char
		return 1
	case NtMulti:
		// Every character in the string needs to match.
		return len(.Str)
	case NtNotonelazy, NtNotoneloop, NtNotoneloopatomic,
		NtOnelazy, NtOneloop, NtOneloopatomic, NtSetlazy, NtSetloop, NtSetloopatomic:
		// One character repeated at least M times.
		return .M
	case NtLazyloop, NtLoop:
		// A node graph repeated at least M times.
		return multiplyMinLength(.M, .Children[0].())
	case NtAlternate:
		// The minimum required length for any of the alternation's branches.
		 := len(.Children)
		 := .Children[0].()
		for  := 1;  <  &&  > 0; ++ {
			 := .Children[].()
			if  <  {
				 = 
			}
		}
		return 
	case NtBackRefCond:
		// Minimum of its yes and no branches.  The backreference doesn't add to the length.
		 := .Children[0].()
		if len(.Children) == 1 {
			return 
		}
		 := .Children[1].()
		if  <  {
			return 
		}
		return 
	case NtExprCond:
		// Minimum of its yes and no branches.  The condition is a zero-width assertion.
		if len(.Children) == 2 {
			return .Children[1].()
		}
		 := .Children[1].()
		 := .Children[2].()
		if  <  {
			return 
		}
		return 
	case NtConcatenate:
		// The sum of all of the concatenation's children.
		 := 0
		for  := 0;  < len(.Children); ++ {
			 = addMinLength(, .Children[].())
		}
		return 
	case NtAtomic, NtCapture, NtGroup:
		// For groups, we just delegate to the sole child.
		return .Children[0].()
	case NtEmpty, NtNothing,
		NtBeginning, NtBol, NtBoundary, NtECMABoundary, NtEnd, NtEndZ, NtEol,
		NtNonboundary, NtNonECMABoundary, NtStart, NtNegLook, NtPosLook, NtRef:
		// Nothing to match. In the future, we could potentially use Nothing to say that the min length
		// is infinite, but that would require a different structure, as that would only apply if the
		// Nothing match is required in all cases (rather than, say, as one branch of an alternation).
	}
	return 0
}

// Computes a maximum length of any string that could possibly match.
// or -1 if the length may not always be the same.
func ( *RegexNode) () int {
	switch .T {
	case NtOne, NtNotone, NtSet:
		return 1
	case NtGrapheme:
		return -1
	case NtMulti:
		return len(.Str)
	case NtNotonelazy, NtNotoneloop, NtNotoneloopatomic,
		NtOnelazy, NtOneloop, NtOneloopatomic,
		NtSetlazy, NtSetloop, NtSetloopatomic:
		// Return the max number of iterations if there's an upper bound, or null if it's infinite
		if .N == math.MaxInt32 {
			return -1
		}
		return .N
	case NtLazyloop, NtLoop:
		if .N == math.MaxInt32 {
			return -1
		}
		// A node graph repeated a fixed number of times
		if  := .Children[0].();  >= 0 {
			return multiplyMaxLength(.N, )
		}
	case NtAlternate:
		// The maximum length of any child branch, as long as they all have one.
		 := .Children[0].()

		if  < 0 {
			return -1
		}
		for  := 1;  < len(.Children); ++ {
			 := .Children[].()
			if  < 0 {
				return -1
			}

			 = max(, )
		}
		return 
	case NtBackRefCond:
		// The maximum length of either child branch, as long as they both have one.
		 := .Children[0].()
		if  < 0 {
			return -1
		}
		 := .Children[1].()
		if  < 0 {
			return -1
		}
		return max(, )

	case NtExprCond:
		// The condition for an expression conditional is a zero-width assertion.
		 := .Children[1].()
		if  < 0 {
			return -1
		}
		 := .Children[2].()
		if  < 0 {
			return -1
		}
		return max(, )

	case NtConcatenate:
		// The sum of all of the concatenation's children's max lengths, as long as they all have one.
		 := 0
		for  := 0;  < len(.Children); ++ {
			 := .Children[].()
			if  < 0 {
				return -1
			}
			 = addMaxLength(, )
			if  < 0 {
				return -1
			}
		}
		return 

	case NtAtomic, NtCapture:
		// For groups, we just delegate to the sole child.
		return .Children[0].()
	case NtEmpty, NtNothing, NtUpdateBumpalong,
		NtBeginning, NtBol, NtBoundary, NtECMABoundary, NtEnd, NtEndZ, NtEol,
		NtNonboundary, NtNonECMABoundary, NtStart, NtNegLook, NtPosLook:
		//zero-width
		return 0

	case NtRef:
		// Requires matching data available only at run-time.  In the future, we could choose to find
		// and follow the capture group this aligns with, while being careful not to end up in an
		// infinite cycle.
		return -1
	}

	return -1
}

// debug functions

var typeStr = []string{
	"Onerep", "Notonerep", "Setrep",
	"Oneloop", "Notoneloop", "Setloop",
	"Onelazy", "Notonelazy", "Setlazy",
	"One", "Notone", "Set",
	"Multi", "Ref",
	"Bol", "Eol", "Boundary", "Nonboundary",
	"Beginning", "Start", "EndZ", "End",
	"Nothing", "Empty",
	"Alternate", "Concatenate",
	"Loop", "Lazyloop",
	"Capture", "Group", "PosLook", "NegLook", "Atomic",
	"BackRefCond", "ExprCond",
	"Unknown", "Unknown", "Unknown",
	"Unknown", "Unknown", "Unknown",
	"ECMABoundary", "NonECMABoundary",
	"OneloopAtomic", "NotoneloopAtomic", "SetloopAtomic",
	"UpdateBumpalong",
	"Grapheme",
}

func ( *RegexNode) () string {
	 := &bytes.Buffer{}

	.WriteString(typeStr[.T])

	if (.Options & ExplicitCapture) != 0 {
		.WriteString("-C")
	}
	if (.Options & IgnoreCase) != 0 {
		.WriteString("-I")
	}
	if (.Options & RightToLeft) != 0 {
		.WriteString("-L")
	}
	if (.Options & Multiline) != 0 {
		.WriteString("-M")
	}
	if (.Options & Singleline) != 0 {
		.WriteString("-S")
	}
	if (.Options & IgnorePatternWhitespace) != 0 {
		.WriteString("-X")
	}
	if (.Options & ECMAScript) != 0 {
		.WriteString("-E")
	}

	switch .T {
	case NtOneloop, NtOneloopatomic, NtNotoneloop, NtOnelazy, NtNotonelazy, NtOne, NtNotone, NtNotoneloopatomic:
		.WriteString("(Ch = " + CharDescription(.Ch) + ")")
	case NtCapture:
		.WriteString("(index = " + strconv.Itoa(.M) + ", unindex = " + strconv.Itoa(.N) + ")")
	case NtRef, NtBackRefCond:
		.WriteString("(index = " + strconv.Itoa(.M) + ")")
	case NtMulti:
		fmt.Fprintf(, "(String = %#v)", string(.Str))
	case NtSet, NtSetloop, NtSetlazy, NtSetloopatomic:
		.WriteString("(Set = " + .Set.String() + ")")
	}

	switch .T {
	case NtOneloop, NtNotoneloop, NtOnelazy, NtNotonelazy, NtSetloop, NtSetlazy, NtLoop, NtLazyloop,
		NtOneloopatomic, NtNotoneloopatomic, NtSetloopatomic:

		.WriteString("(Min = ")
		.WriteString(strconv.Itoa(.M))
		.WriteString(", Max = ")
		if .N == math.MaxInt32 {
			.WriteString("inf")
		} else {
			.WriteString(strconv.Itoa(.N))
		}
		.WriteString(")")
	}

	return .String()
}

var padSpace = []byte("                                ")

func ( *RegexTree) () string {
	return .Root.dump()
}

func ( *RegexNode) () string {
	var  []int
	 := 
	 := 0

	 := bytes.NewBufferString(.Description())
	.WriteRune('\n')

	for {
		if .Children != nil &&  < len(.Children) {
			 = append(, +1)
			 = .Children[]
			 = 0

			 := len()
			if  > 32 {
				 = 32
			}
			.Write(padSpace[:])
			.WriteString(.Description())
			.WriteRune('\n')
		} else {
			if len() == 0 {
				break
			}

			 = [len()-1]
			 = [:len()-1]
			 = .Parent
		}
	}
	return .String()
}

// Determines whether the specified child index of a concatenation begins a sequence whose values
// should be used to perform an ordinal case-insensitive comparison.
//
// When consumeZeroWidthNodes is false, the consumer needs the semantics of matching the produced string to fully represent
// the semantics of all the consumed nodes, which means nodes can be consumed iff they produce text that's represented
// by the resulting string. When true, the resulting string needs to fully represent all valid matches at that position,
// but it can have false positives, which means the resulting string doesn't need to fully represent all zero-width nodes
// consumed. true is only valid when used as part of a search to determine where to try a full match, not as part of
// actual matching logic.
// consumeZeroWidthNodes = false
func ( *RegexNode) ( int,  int,  bool) ( bool,  int,  string) {
	 := &strings.Builder{}

	// We're looking in particular for sets of ASCII characters, so we focus only on sets with two characters in them, e.g. [Aa].
	//twoChars := make([]rune, 0, 2)

	// Iterate from the child index to the exclusive upper bound.
	var  int
	for  = ;  < ; ++ {
		 := .Children[]

		if .T == NtOne {
			// We only want to include ASCII characters, and only if they don't participate in case conversion
			// such that they only case to themselves and nothing other cases to them.  Otherwise, including
			// them would potentially cause us to match against things not allowed by the pattern.
			if .Ch >= unicode.MaxASCII || participatesInCaseConversion(.Ch) {
				break
			}

			.WriteRune(.Ch)
		} else if .T == NtMulti {
			// As with NtOne, the string needs to be composed solely of ASCII characters that
			// don't participate in case conversion.
			 := slices.ContainsFunc(.Str, func( rune) bool { return  > unicode.MaxASCII })
			if  || anyParticipatesInCaseConversion(string(.Str)) {
				break
			}

			.WriteString(string(.Str))
		} else if .T == NtSet ||
			((.T == NtSetloop || .T == NtSetlazy || .T == NtSetloopatomic) && .M == .N) {
			// In particular we want to look for sets that contain only the upper and lowercase variant
			// of the same ASCII letter.
			,  := .Set.containsAsciiIgnoreCaseCharacter()
			if ! {
				break
			}

			 := .M
			if .T == NtSet {
				 = 1
			}
			.WriteString(strings.Repeat(string([0]|0x20), ))
		} else if .T == NtEmpty {
			// Skip over empty nodes, as they're pure nops. They would ideally have been optimized away,
			// but can still remain in some situations.
		} else if  &&
			// anchors
			(.T == NtBeginning || .T == NtBol || .T == NtStart ||
				// boundaries
				.T == NtBoundary || .T == NtECMABoundary || .T == NtNonboundary || .T == NtNonECMABoundary ||
				// lookarounds
				.T == NtNegLook || .T == NtPosLook ||
				// logic
				.T == NtUpdateBumpalong) {
			// Skip over zero-width nodes that might be reasonable at the beginning of or within a substring.
			// We can only do these if consumeZeroWidthNodes is true, as otherwise we'd be producing a string that
			// may not fully represent the semantics of this portion of the pattern.
		} else {
			break
		}
	}

	// If we found at least two characters, consider it a sequence found.  It's possible
	// they all came from the same node, so this could be a sequence of just one node.
	if .Len() >= 2 {
		return true,  - , .String()
	}

	// No sequence found.
	return false, 0, ""
}

func ( *RegexNode) () bool {
	if .T == NtOne || .T == NtNotone || .T == NtSet || .T == NtMulti {
		return true
	}
	if (.IsSetloopFamily() || .IsNotoneloopFamily() || .IsOneloopFamily()) &&
		.M == .N {
		return true
	}

	return false
}

// Determine whether the specified child node is the beginning of a sequence that can
// trivially have length checks combined in order to avoid bounds checks.
// requiredLength is The sum of all the fixed lengths for the nodes in the sequence.</param>
// exclusiveEnd is The index of the node just after the last one in the sequence.</param>
// returns true if more than one node can have their length checks combined; otherwise, false.</returns>
//
// There are additional node types for which we can prove a fixed length, e.g. examining all branches
// of an alternation and returning true if all their lengths are equal.  However, the primary purpose
// of this method is to avoid bounds checks by consolidating length checks that guard accesses to
// strings/spans for which the JIT can see a fixed index within bounds, and alternations employ
// patterns that defeat that (e.g. reassigning the span in question).  As such, the implementation
// remains focused on only a core subset of nodes that are a) likely to be used in concatenations and
// b) employ simple patterns of checks.
func ( *RegexNode) ( int,  *int,  *int) bool {

	 := .Children[]
	if .canJoinLengthCheck() {
		* = .ComputeMinLength()

		for * =  + 1; * < len(.Children); *++ {
			 = .Children[*]
			if !.canJoinLengthCheck() {
				break
			}

			* += .ComputeMinLength()
		}

		if *- > 1 {
			return true
		}
	}

	* = 0
	* = 0
	return false
}

type StartingLiteral struct {
	Range    SingleRange
	String   []rune
	SetChars []rune
	Negated  bool
}

func ( *RegexNode) () *StartingLiteral {
	 := .FindStartingLiteralNode(true)
	if  == nil {
		return nil
	}

	if .IsOneFamily() {
		return &StartingLiteral{Range: SingleRange{.Ch, .Ch}, Negated: false}
	}
	if .IsNotoneFamily() {
		return &StartingLiteral{Range: SingleRange{.Ch, .Ch}, Negated: true}
	}
	if .IsSetFamily() {
		 := .Set.GetIfNRanges(1)
		if len() == 1 && [0].Last-[0].First > 1 {
			return &StartingLiteral{Range: [0], Negated: .Set.IsNegated()}
		}
		 := .Set.GetSetChars(128)
		if len() > 0 {
			return &StartingLiteral{SetChars: , Negated: .Set.IsNegated()}
		}
	}
	if .T == NtMulti {
		return &StartingLiteral{String: .Str}
	}

	return nil
}

// Finds the guaranteed beginning literal(s) of the node, or null if none exists.
// allowZeroWidth = true
func ( *RegexNode) ( bool) *RegexNode {
	 := 
	for {
		if  != nil && .Options&RightToLeft == 0 {
			switch .T {
			case NtOne, NtNotone, NtMulti, NtSet:
				return 

			case NtOneloop, NtOneloopatomic, NtOnelazy,
				NtNotoneloop, NtNotoneloopatomic, NtNotonelazy,
				NtSetloop, NtSetloopatomic, NtSetlazy:
				if .M > 0 {
					return 
				}

			case NtAtomic, NtConcatenate, NtCapture, NtGroup:
				 = .Children[0]
				continue
			case NtLoop, NtLazyloop:
				 = .Children[0]
				continue
			case NtPosLook:
				if  {
					 = .Children[0]
					continue
				}
			}
		}

		return nil
	}
}

// Gets the character that begins a One or Multi.
func ( *RegexNode) () rune {
	if .IsOneFamily() {
		return .Ch
	}
	return .Str[0]
}