package syntax

import (
	
	
	
	
	
	
	
	
)

// CharSet combines start-end rune ranges and unicode categories representing a set of characters
type CharSet struct {
	ranges     []SingleRange
	categories []Category
	sub        *CharSet //optional subtractor
	negate     bool
	anything   bool

	ascii *asciiBitmap
}

type asciiBitmap struct {
	bits [2]uint64
}

type Category struct {
	Negate bool
	Cat    string
}

type SingleRange struct {
	First rune
	Last  rune
}

const (
	SpaceCategoryText = " "
	WordCategoryText  = "W"
)

var (
	ecmaSpace = []rune{0x0009, 0x000e, 0x0020, 0x0021, 0x00a0, 0x00a1, 0x1680, 0x1681, 0x2000, 0x200b, 0x2028, 0x202a, 0x202f, 0x2030, 0x205f, 0x2060, 0x3000, 0x3001, 0xfeff, 0xff00}
	ecmaWord  = []rune{0x0030, 0x003a, 0x0041, 0x005b, 0x005f, 0x0060, 0x0061, 0x007b}
	ecmaDigit = []rune{0x0030, 0x003a}

	re2Space = []rune{0x0009, 0x000b, 0x000c, 0x000e, 0x0020, 0x0021}
)

var (
	AnyClass          = getCharSetFromOldString([]rune{0}, false)
	ECMAAnyClass      = getCharSetFromOldString([]rune{0, 0x000a, 0x000b, 0x000d, 0x000e}, false)
	NoneClass         = getCharSetFromOldString(nil, false)
	ECMAWordClass     = getCharSetFromOldString(ecmaWord, false)
	NotECMAWordClass  = getCharSetFromOldString(ecmaWord, true)
	ECMASpaceClass    = getCharSetFromOldString(ecmaSpace, false)
	NotECMASpaceClass = getCharSetFromOldString(ecmaSpace, true)
	ECMADigitClass    = getCharSetFromOldString(ecmaDigit, false)
	NotECMADigitClass = getCharSetFromOldString(ecmaDigit, true)

	WordClass     = getCharSetFromCategoryString(false, false, WordCategoryText)
	NotWordClass  = getCharSetFromCategoryString(true, false, WordCategoryText)
	SpaceClass    = getCharSetFromCategoryString(false, false, SpaceCategoryText)
	NotSpaceClass = getCharSetFromCategoryString(true, false, SpaceCategoryText)
	DigitClass    = getCharSetFromCategoryString(false, false, "Nd")
	NotDigitClass = getCharSetFromCategoryString(false, true, "Nd")

	RE2SpaceClass    = getCharSetFromOldString(re2Space, false)
	NotRE2SpaceClass = getCharSetFromOldString(re2Space, true)

	NotNewLineClass = getCharSetFromOldString([]rune{0x0a, 0x0b}, true)
)

var unicodeCategories = func() map[string]*unicode.RangeTable {
	 := make(map[string]*unicode.RangeTable)
	for ,  := range unicode.Scripts {
		[] = 
	}
	for ,  := range unicode.Categories {
		[] = 
	}
	// aliases are just pointers to the original keys
	for ,  := range unicode.CategoryAliases {
		[] = unicode.Categories[]
	}
	for ,  := range unicode.Properties {
		[] = 
	}
	for ,  := range unicodeAliasCategories {
		[] = 
	}
	return 
}()

func getCharSetFromCategoryString( bool,  bool,  ...string) func() *CharSet {
	if  &&  {
		panic("BUG!  You should only negate the set OR the category in a constant setup, but not both")
	}

	 := CharSet{negate: }

	.categories = make([]Category, len())
	for ,  := range  {
		.categories[] = Category{Cat: , Negate: }
	}
	return func() *CharSet {
		//make a copy each time
		 := 
		//return that address
		return &
	}
}

func getCharSetFromOldString( []rune,  bool) func() *CharSet {
	 := CharSet{}
	if len() > 0 {
		 := false
		 := len()
		if  {
			if [0] == 0 {
				 = [1:]
			} else {
				++
				 = true
			}
		}

		if %2 == 0 {
			.ranges = make([]SingleRange, /2)
		} else {
			.ranges = make([]SingleRange, /2+1)
		}

		 := true
		if  {
			.ranges[0] = SingleRange{First: 0}
			 = false
		}

		 := 0
		for ,  := range  {
			if  {
				// lower bound in a new range
				.ranges[] = SingleRange{First: }
				 = false
			} else {
				.ranges[].Last =  - 1
				++
				 = true
			}
		}
		if ! {
			.ranges[].Last = utf8.MaxRune
		}
		if len(.ranges) == 1 && .ranges[0].First == 0 && .ranges[0].Last >= unicode.MaxRune {
			// this is anything...or nothing
			.anything = !
		}
	}

	return func() *CharSet {
		 := 
		return &
	}
}

// Copy makes a deep copy to prevent accidental mutation of a set
func ( CharSet) () CharSet {
	 := CharSet{
		anything: .anything,
		negate:   .negate,
	}

	.ranges = append(.ranges, .ranges...)
	.categories = append(.categories, .categories...)

	if .sub != nil {
		 := .sub.()
		.sub = &
	}

	return 
}

// gets a human-readable description for a set string
func ( CharSet) () string {
	 := &bytes.Buffer{}
	.WriteRune('[')

	if .IsNegated() {
		.WriteRune('^')
	}

	for ,  := range .ranges {

		.WriteString(CharDescription(.First))
		if .First != .Last {
			if .Last-.First != 1 {
				//groups that are 1 char apart skip the dash
				.WriteRune('-')
			}
			.WriteString(CharDescription(.Last))
		}
	}

	for ,  := range .categories {
		.WriteString(.String())
	}

	if .sub != nil {
		.WriteRune('-')
		.WriteString(.sub.())
	}

	.WriteRune(']')

	return .String()
}

func b2i( bool) byte {
	if  {
		return 1
	}
	return 0
}

// mapHashFill converts a charset into a buffer for use in maps
func ( CharSet) ( *bytes.Buffer) {
	.WriteByte(b2i(.negate) + b2i(.anything)*2)

	_ = binary.Write(, binary.LittleEndian, int32(len(.ranges)))
	_ = binary.Write(, binary.LittleEndian, int32(len(.categories)))
	for ,  := range .ranges {
		.WriteRune(.First)
		.WriteRune(.Last)
	}
	for ,  := range .categories {
		// write the length of the cat and indicate if it's negated
		if .Negate {
			_ = binary.Write(, binary.LittleEndian, int8(-1*len(.Cat)))
		} else {
			_ = binary.Write(, binary.LittleEndian, int8(len(.Cat)))
		}
		.WriteString(.Cat)
	}

	if .sub != nil {
		.sub.()
	}
}

func ( string) CharSet {
	 := CharSet{}
	 := bytes.NewBufferString()
	,  := .ReadByte()
	//1s bit == negate, 2s bit == anything
	.negate = (&0x1 == 0x1)
	.anything = (&0x2 == 0x2)
	var ,  int32
	_ = binary.Read(, binary.LittleEndian, &)
	_ = binary.Read(, binary.LittleEndian, &)

	.ranges = make([]SingleRange, )
	for  := 0;  < int(); ++ {
		 := SingleRange{}
		.First, _, _ = .ReadRune()
		.Last, _, _ = .ReadRune()
		.ranges[] = 
	}

	.categories = make([]Category, )
	for  := 0;  < int(); ++ {
		var  int8
		 := Category{}
		_ = binary.Read(, binary.LittleEndian, &)
		if  < 0 {
			.Negate = true
			 *= -1
		}
		.Cat = string(.Next(int()))
		.categories[] = 
	}

	//sub
	if .Len() > 0 {
		 := (.String())
		.sub = &
	}

	return 
}

// CharIn returns true if the rune is in our character set (either ranges or categories).
// It handles negations and subtracted sub-charsets.
func ( CharSet) ( rune) bool {
	if  >= 0 &&  < 128 && .ascii != nil {
		return (.ascii.bits[/64] & (1 << (uint() % 64))) != 0
	}
	return .charInSlow()
}

func ( CharSet) ( rune) bool {
	 := false
	// in s && !s.subtracted

	//check ranges -- binary search for sets with many ranges, linear for small sets
	 := len(.ranges)
	if  > 0 {
		if  <= 4 {
			for ,  := range .ranges {
				if  < .First {
					break
				}
				if  <= .Last {
					 = true
					break
				}
			}
		} else {
			,  := 0, 
			for  <  {
				 := int(uint(+) >> 1)
				if .ranges[].First <=  {
					 =  + 1
				} else {
					 = 
				}
			}
			if  > 0 &&  <= .ranges[-1].Last {
				 = true
			}
		}
	}

	//check categories if we haven't already found a range
	if ! && len(.categories) > 0 {
		 = .charInCategories()
	}

	// negate the whole char set
	if .negate {
		 = !
	}

	// get subtracted recurse
	if  && .sub != nil {
		 = !.sub.CharIn()
	}

	//log.Printf("Char '%v' in %v == %v", string(ch), c.String(), val)
	return 
}

func ( *CharSet) () {
	if  == nil || .ascii != nil {
		return
	}
	if .sub != nil {
		.sub.()
	}
	 := &asciiBitmap{}
	for  := range rune(128) {
		if .charInSlow() {
			.bits[/64] |= 1 << (uint() % 64)
		}
	}
	.ascii = 
}

func ( *CharSet) ( rune) bool {
	for ,  := range .categories {
		// special categories...then unicode
		if .Cat == SpaceCategoryText {
			if unicode.IsSpace() {
				// we found a space so we're done
				// negate means this is a "bad" thing
				return !.Negate
			} else if .Negate {
				return true
			}
		} else if .Cat == WordCategoryText {
			if IsWordChar() {
				return !.Negate
			} else if .Negate {
				return true
			}
		} else if unicode.Is(unicodeCategories[.Cat], ) {
			// if we're in this unicode category then we're done
			// if negate=true on this category then we "failed" our test
			// otherwise we're good that we found it
			return !.Negate
		} else if .Negate {
			return true
		}
	}
	return false
}

func ( Category) () string {
	switch .Cat {
	case SpaceCategoryText:
		if .Negate {
			return "\\S"
		}
		return "\\s"
	case WordCategoryText:
		if .Negate {
			return "\\W"
		}
		return "\\w"
	}
	if ,  := unicodeCategories[.Cat];  {

		if .Negate {
			return "\\P{" + .Cat + "}"
		}
		return "\\p{" + .Cat + "}"
	}
	return "Unknown category: " + .Cat
}

// CharDescription Produces a human-readable description for a single character.
func ( rune) string {
	/*if ch == '\\' {
		return "\\\\"
	}

	if ch > ' ' && ch <= '~' {
		return string(ch)
	} else if ch == '\n' {
		return "\\n"
	} else if ch == ' ' {
		return "\\ "
	}*/

	 := &bytes.Buffer{}
	escape(, , false) //fmt.Sprintf("%U", ch)
	return .String()
}

// According to UTS#18 Unicode Regular Expressions (http://www.unicode.org/reports/tr18/)
// RL 1.4 Simple Word Boundaries  The class of <word_character> includes all Alphabetic
// values from the Unicode character database, from UnicodeData.txt [UData], plus the U+200C
// ZERO WIDTH NON-JOINER and U+200D ZERO WIDTH JOINER.
func ( rune) bool {
	//"L", "Mn", "Nd", "Pc"
	return unicode.In(,
		unicode.Categories["L"], unicode.Categories["Mn"],
		unicode.Categories["Nd"], unicode.Categories["Pc"]) ||  == '\u200D' ||  == '\u200C'
	//return 'A' <= r && r <= 'Z' || 'a' <= r && r <= 'z' || '0' <= r && r <= '9' || r == '_'
}

func ( rune) bool {
	return unicode.In(,
		unicode.Categories["L"], unicode.Categories["Mn"],
		unicode.Categories["Nd"], unicode.Categories["Pc"])

	//return 'A' <= r && r <= 'Z' || 'a' <= r && r <= 'z' || '0' <= r && r <= '9' || r == '_'
}

func ( rune) bool {
	return  == '$' ||  == '_' || unicode.In(, unicode.L, unicode.Nl, unicode.Other_ID_Start)
}

func ( rune) bool {
	return IsECMAIdentifierStartChar() ||  == '\u200C' ||  == '\u200D' ||
		unicode.In(, unicode.Mn, unicode.Mc, unicode.Nd, unicode.Pc, unicode.Other_ID_Continue)
}

// SingletonChar will return the char from the first range without validation.
// It assumes you have checked for IsSingleton or IsSingletonInverse and will panic given bad input
func ( CharSet) () rune {
	return .ranges[0].First
}

func ( CharSet) () bool {
	return !.negate && //negated is multiple chars
		len(.categories) == 0 && len(.ranges) == 1 && // multiple ranges and unicode classes represent multiple chars
		.sub == nil && // subtraction means we've got multiple chars
		.ranges[0].First == .ranges[0].Last // first and last equal means we're just 1 char
}

func ( CharSet) () bool {
	return .negate && //same as above, but requires negated
		len(.categories) == 0 && len(.ranges) == 1 && // multiple ranges and unicode classes represent multiple chars
		.sub == nil && // subtraction means we've got multiple chars
		.ranges[0].First == .ranges[0].Last // first and last equal means we're just 1 char
}

func ( CharSet) () bool {
	return !.IsNegated() && !.HasSubtraction()
}

func ( CharSet) () bool {
	return .negate
}

func ( CharSet) () bool {
	return .sub != nil
}

func ( CharSet) () bool {
	return len(.ranges) == 0 && len(.categories) == 0 && .sub == nil
}

func ( CharSet) () bool {
	return .anything
}

func ( *CharSet) (,  bool) {
	if  {
		if  {
			.addRanges(NotECMADigitClass().ranges)
		} else {
			.addRanges(ECMADigitClass().ranges)
		}
	} else {
		.addCategories(Category{Cat: "Nd", Negate: })
	}
}

func ( *CharSet) ( rune) {
	.addRange(, )
}

func ( *CharSet) (, ,  bool) {
	if  {
		if  {
			.addRanges(NotECMASpaceClass().ranges)
		} else {
			.addRanges(ECMASpaceClass().ranges)
		}
	} else if  {
		if  {
			.addRanges(NotRE2SpaceClass().ranges)
		} else {
			.addRanges(RE2SpaceClass().ranges)
		}
	} else {
		.addCategories(Category{Cat: SpaceCategoryText, Negate: })
	}
}

func ( *CharSet) (,  bool) {
	if  {
		if  {
			.addRanges(NotECMAWordClass().ranges)
		} else {
			.addRanges(ECMAWordClass().ranges)
		}
	} else {
		.addCategories(Category{Cat: WordCategoryText, Negate: })
	}
}

// Add set ranges and categories into ours -- no deduping or anything
func ( *CharSet) ( CharSet) {
	if .anything {
		return
	}
	if .anything {
		.makeAnything()
		return
	}
	// just append here to prevent double-canon
	.ranges = append(.ranges, .ranges...)
	.addCategories(.categories...)
	.canonicalize()
}

func ( *CharSet) () {
	.anything = true
	.categories = []Category{}
	.ranges = []SingleRange{{First: 0, Last: unicode.MaxRune}}
}

func ( *CharSet) ( ...Category) {
	// don't add dupes and remove positive+negative
	if .anything {
		// if we've had a previous positive+negative group then
		// just return, we're as broad as we can get
		return
	}

	for ,  := range  {
		 := false
		for ,  := range .categories {
			if .Cat == .Cat {
				if .Negate != .Negate {
					// oposite negations...this mean we just
					// take us as anything and move on
					.makeAnything()
					return
				}
				 = true
				break
			}
		}

		if ! {
			.categories = append(.categories, )
		}
	}
}

// Merges new ranges to our own
func ( *CharSet) ( []SingleRange) {
	if .anything {
		return
	}
	.ranges = append(.ranges, ...)
	.canonicalize()
}

// Merges everything but the new ranges into our own
func ( *CharSet) ( []SingleRange) {
	if .anything {
		return
	}

	var  rune

	// convert incoming ranges into opposites, assume they are in order
	for ,  := range  {
		if  < .First {
			.ranges = append(.ranges, SingleRange{, .First - 1})
		}
		 = .Last + 1
	}

	if  < utf8.MaxRune {
		.ranges = append(.ranges, SingleRange{, utf8.MaxRune})
	}

	.canonicalize()
}

func normalizeUnicodeCategoryAlias( string) string {
	var  strings.Builder
	.Grow(len())
	for ,  := range  {
		switch  {
		case '_', '-', ' ':
			continue
		default:
			.WriteRune(unicode.ToLower())
		}
	}
	return .String()
}

func canonicalUnicodeCatName( string) (string, bool) {
	if ,  := unicodeCategories[];  {
		return , true
	}

	 := normalizeUnicodeCategoryAlias()
	if ,  := unicodeSupportedPropertyAliases[];  {
		return , true
	}
	if ,  := unicodeBarePropertyValueAliases[];  {
		return , true
	}

	if  := strings.IndexRune(, '=');  >= 0 {
		 := [:]
		 := [+1:]
		,  := unicodeSupportedPropertyAliases[normalizeUnicodeCategoryAlias()]
		if ! {
			return "", false
		}
		 := unicodeSupportedPropertyValueAliases[]
		if  == nil {
			return "", false
		}
		,  := [normalizeUnicodeCategoryAlias()]
		if ! {
			return "", false
		}
		 :=  + "=" + 
		if ,  := unicodeCategories[];  {
			return , true
		}
	}

	return "", false
}

func ( *CharSet) ( string, ,  bool) {
	var  bool
	,  = canonicalUnicodeCatName()
	if ! {
		// unknown unicode category, script, or property "blah"
		panic(fmt.Errorf("unknown unicode category, script, or property '%v'", ))

	}

	if  && ( == "Ll" ||  == "Lu" ||  == "Lt") {
		// when RegexOptions.IgnoreCase is specified then {Ll} {Lu} and {Lt} cases should all match
		.addCategories(
			Category{Cat: "Ll", Negate: },
			Category{Cat: "Lu", Negate: },
			Category{Cat: "Lt", Negate: })
	}
	.addCategories(Category{Cat: , Negate: })
}

// Adds to the class any case-equivalence versions of characters already
// in the class. Used for case-insensitivity.
func ( *CharSet) () {
	// we already have all case equiv
	if .anything {
		return
	}
	 := len(.ranges)
	for  := 0;  < ; ++ {
		 := .ranges[]
		// For a single range that's in the set, adds any additional ranges
		// necessary to ensure that lowercase equivalents are also included.
		for  := .First;  <= .Last; ++ {
			 := tryFindCaseEquivalences()
			for ,  := range  {
				.ranges = append(.ranges, SingleRange{First: , Last: })
			}
		}
	}
	.canonicalize()
}

// Performs a fast lookup which determines if a character is involved in case conversion, as well as
// returns the OTHER characters that should be considered equivalent in case it does participate in case conversion.
func tryFindCaseEquivalences( rune) []rune {
	 := unicode.SimpleFold()
	if  ==  {
		// no case support
		return nil
	}
	 := []rune{}
	for {
		 = unicode.SimpleFold()
		if  ==  {
			return 
		}
		 = append(, )
	}
}

func ( *CharSet) ( *CharSet) {
	.sub = 
}

func ( *CharSet) (,  rune) {
	.ranges = append(.ranges, SingleRange{First: , Last: })
	.canonicalize()
}

func ( *CharSet) ( string,  bool) bool {
	var  []SingleRange

	switch  {
	case "alnum":
		 = []SingleRange{{'0', '9'}, {'A', 'Z'}, {'a', 'z'}}
	case "alpha":
		 = []SingleRange{{'A', 'Z'}, {'a', 'z'}}
	case "ascii":
		 = []SingleRange{{0, 0x7f}}
	case "blank":
		 = []SingleRange{{'\t', '\t'}, {' ', ' '}}
	case "cntrl":
		 = []SingleRange{{0, 0x1f}, {0x7f, 0x7f}}
	case "digit":
		.addDigit(false, )
	case "graph":
		 = []SingleRange{{'!', '~'}}
	case "lower":
		 = []SingleRange{{'a', 'z'}}
	case "print":
		 = []SingleRange{{' ', '~'}}
	case "punct": //[!-/:-@[-`{-~]
		 = []SingleRange{{'!', '/'}, {':', '@'}, {'[', '`'}, {'{', '~'}}
	case "space":
		.addSpace(true, false, )
	case "upper":
		 = []SingleRange{{'A', 'Z'}}
	case "word":
		.addWord(true, )
	case "xdigit":
		 = []SingleRange{{'0', '9'}, {'A', 'F'}, {'a', 'f'}}
	default:
		return false
	}

	if len() > 0 {
		if  {
			.addNegativeRanges()
		} else {
			.addRanges()
		}
	}

	return true
}

type singleRangeSorter []SingleRange

func ( singleRangeSorter) () int           { return len() }
func ( singleRangeSorter) (,  int) bool { return [].First < [].First }
func ( singleRangeSorter) (,  int)      { [], [] = [], [] }

// Logic to reduce a character class to a unique, sorted form.
func ( *CharSet) () {
	var ,  int
	var  rune

	if len(.ranges) == 0 {
		return
	}

	//
	// Find and eliminate overlapping or abutting ranges
	//

	if len(.ranges) > 1 {
		sort.Sort(singleRangeSorter(.ranges))

		 := false

		for ,  = 1, 0; ; ++ {
			for  = .ranges[].Last; ; ++ {
				if  == len(.ranges) ||  >= unicode.MaxRune {
					 = true
					break
				}

				 := .ranges[]
				if .First > +1 {
					break
				}

				if  < .Last {
					 = .Last
				}
			}

			.ranges[] = SingleRange{First: .ranges[].First, Last: }

			++

			if  {
				break
			}

			if  <  {
				.ranges[] = .ranges[]
			}
		}

		.ranges = append(.ranges[:], .ranges[len(.ranges):]...)
	}

	// If the class now represents a single negated range, but does so by including every
	// other character, invert it to produce a normalized form with a single range.  This
	// is valuable for subsequent optimizations in most of the engines.
	if !.negate && .sub == nil && len(.categories) == 0 {
		if len(.ranges) == 2 {
			// There are two ranges in the list.  See if there's one missing range between them.
			// Such a range might be as small as a single character.
			if .ranges[0].First == 0 &&
				.ranges[1].Last >= unicode.MaxRune &&
				.ranges[0].Last < .ranges[1].First-1 {
				.ranges = []SingleRange{{.ranges[0].Last + 1, .ranges[1].First - 1}}
				.negate = true
			}
		} else if len(.ranges) == 1 {
			switch .ranges[0].First {
			case 0:
				// There's only one range in the list.  Does it include everything but the last char?
				if .ranges[0].Last == unicode.MaxRune-1 {
					.ranges[0] = SingleRange{unicode.MaxRune, unicode.MaxRune}
					.negate = true
				}
			case 1:
				// Or everything but the first char?
				if .ranges[0].Last >= unicode.MaxRune {
					.ranges[0] = SingleRange{'\x00', '\x00'}
					.negate = true
				}
			}
		}
	}

	// If the class now has a range that includes everything, and if it doesn't have subtraction,
	// we can remove all of its categories, as they're duplicative (the set already includes everything).
	if !.negate &&
		.sub == nil &&
		len(.ranges) == 1 && .ranges[0].First == 0 && .ranges[0].Last >= unicode.MaxRune {

		.makeAnything()
	}

	// If there's only a single character omitted from ranges, if there's no subtractor, and if there are categories,
	// see if that character is in the categories.  If it is, then we can replace whole thing with a complete "any" range.
	// If it's not, then we can remove the categories, as they're only duplicating the rest of the range, turning the set
	// into a "not one". This primarily helps in the case of a synthesized set from analysis that ends up combining '.' with
	// categories, as we want to reduce that set down to either [^\n] or [\0-\uFFFF]. (This can be extrapolated to any number
	// of missing characters; in fact, categories in general are superfluous and the entire set can be represented as ranges.
	// But categories serve as a space optimization, and we strike a balance between testing many characters and the time/complexity
	// it takes to do so.  Thus, we limit this to the common case of a single missing character.)
	if !.negate && .sub == nil && len(.categories) > 0 &&
		len(.ranges) == 2 && .ranges[0].First == 0 && .ranges[0].Last+2 == .ranges[1].First && .ranges[1].Last == unicode.MaxRune {

		if .charInCategories(.ranges[0].Last + 1) {
			//c.ranges = []SingleRange{{'\x00', unicode.MaxRune}}
			.makeAnything()
		} else {
			.negate = true
			.ranges = []SingleRange{{.ranges[0].Last + 1, .ranges[0].Last + 1}}
			.categories = []Category{}
		}
	}
}

// Adds to the class any lowercase versions of characters already
// in the class. Used for case-insensitivity.
func ( *CharSet) () {
	if .anything {
		return
	}
	 := []SingleRange{}
	for  := 0;  < len(.ranges); ++ {
		 := .ranges[]
		if .First == .Last {
			 := unicode.ToLower(.First)
			.ranges[] = SingleRange{First: , Last: }
		} else {
			 = append(, )
		}
	}

	for ,  := range  {
		.addLowercaseRange(.First, .Last)
	}
	.canonicalize()
}

/**************************************************************************
    Let U be the set of Unicode character values and let L be the lowercase
    function, mapping from U to U. To perform case insensitive matching of
    character sets, we need to be able to map an interval I in U, say

        I = [chMin, chMax] = { ch : chMin <= ch <= chMax }

    to a set A such that A contains L(I) and A is contained in the union of
    I and L(I).

    The table below partitions U into intervals on which L is non-decreasing.
    Thus, for any interval J = [a, b] contained in one of these intervals,
    L(J) is contained in [L(a), L(b)].

    It is also true that for any such J, [L(a), L(b)] is contained in the
    union of J and L(J). This does not follow from L being non-decreasing on
    these intervals. It follows from the nature of the L on each interval.
    On each interval, L has one of the following forms:

        (1) L(ch) = constant            (LowercaseSet)
        (2) L(ch) = ch + offset         (LowercaseAdd)
        (3) L(ch) = ch | 1              (LowercaseBor)
        (4) L(ch) = ch + (ch & 1)       (LowercaseBad)

    It is easy to verify that for any of these forms [L(a), L(b)] is
    contained in the union of [a, b] and L([a, b]).
***************************************************************************/

const (
	LowercaseSet = 0 // Set to arg.
	LowercaseAdd = 1 // Add arg.
	LowercaseBor = 2 // Bitwise or with 1.
	LowercaseBad = 3 // Bitwise and with 1 and add original.
)

type lcMap struct {
	chMin, chMax rune
	op, data     int32
}

var lcTable = []lcMap{
	{'\u0041', '\u005A', LowercaseAdd, 32},
	{'\u00C0', '\u00DE', LowercaseAdd, 32},
	{'\u0100', '\u012E', LowercaseBor, 0},
	{'\u0130', '\u0130', LowercaseSet, 0x0069},
	{'\u0132', '\u0136', LowercaseBor, 0},
	{'\u0139', '\u0147', LowercaseBad, 0},
	{'\u014A', '\u0176', LowercaseBor, 0},
	{'\u0178', '\u0178', LowercaseSet, 0x00FF},
	{'\u0179', '\u017D', LowercaseBad, 0},
	{'\u0181', '\u0181', LowercaseSet, 0x0253},
	{'\u0182', '\u0184', LowercaseBor, 0},
	{'\u0186', '\u0186', LowercaseSet, 0x0254},
	{'\u0187', '\u0187', LowercaseSet, 0x0188},
	{'\u0189', '\u018A', LowercaseAdd, 205},
	{'\u018B', '\u018B', LowercaseSet, 0x018C},
	{'\u018E', '\u018E', LowercaseSet, 0x01DD},
	{'\u018F', '\u018F', LowercaseSet, 0x0259},
	{'\u0190', '\u0190', LowercaseSet, 0x025B},
	{'\u0191', '\u0191', LowercaseSet, 0x0192},
	{'\u0193', '\u0193', LowercaseSet, 0x0260},
	{'\u0194', '\u0194', LowercaseSet, 0x0263},
	{'\u0196', '\u0196', LowercaseSet, 0x0269},
	{'\u0197', '\u0197', LowercaseSet, 0x0268},
	{'\u0198', '\u0198', LowercaseSet, 0x0199},
	{'\u019C', '\u019C', LowercaseSet, 0x026F},
	{'\u019D', '\u019D', LowercaseSet, 0x0272},
	{'\u019F', '\u019F', LowercaseSet, 0x0275},
	{'\u01A0', '\u01A4', LowercaseBor, 0},
	{'\u01A7', '\u01A7', LowercaseSet, 0x01A8},
	{'\u01A9', '\u01A9', LowercaseSet, 0x0283},
	{'\u01AC', '\u01AC', LowercaseSet, 0x01AD},
	{'\u01AE', '\u01AE', LowercaseSet, 0x0288},
	{'\u01AF', '\u01AF', LowercaseSet, 0x01B0},
	{'\u01B1', '\u01B2', LowercaseAdd, 217},
	{'\u01B3', '\u01B5', LowercaseBad, 0},
	{'\u01B7', '\u01B7', LowercaseSet, 0x0292},
	{'\u01B8', '\u01B8', LowercaseSet, 0x01B9},
	{'\u01BC', '\u01BC', LowercaseSet, 0x01BD},
	{'\u01C4', '\u01C5', LowercaseSet, 0x01C6},
	{'\u01C7', '\u01C8', LowercaseSet, 0x01C9},
	{'\u01CA', '\u01CB', LowercaseSet, 0x01CC},
	{'\u01CD', '\u01DB', LowercaseBad, 0},
	{'\u01DE', '\u01EE', LowercaseBor, 0},
	{'\u01F1', '\u01F2', LowercaseSet, 0x01F3},
	{'\u01F4', '\u01F4', LowercaseSet, 0x01F5},
	{'\u01FA', '\u0216', LowercaseBor, 0},
	{'\u0386', '\u0386', LowercaseSet, 0x03AC},
	{'\u0388', '\u038A', LowercaseAdd, 37},
	{'\u038C', '\u038C', LowercaseSet, 0x03CC},
	{'\u038E', '\u038F', LowercaseAdd, 63},
	{'\u0391', '\u03AB', LowercaseAdd, 32},
	{'\u03E2', '\u03EE', LowercaseBor, 0},
	{'\u0401', '\u040F', LowercaseAdd, 80},
	{'\u0410', '\u042F', LowercaseAdd, 32},
	{'\u0460', '\u0480', LowercaseBor, 0},
	{'\u0490', '\u04BE', LowercaseBor, 0},
	{'\u04C1', '\u04C3', LowercaseBad, 0},
	{'\u04C7', '\u04C7', LowercaseSet, 0x04C8},
	{'\u04CB', '\u04CB', LowercaseSet, 0x04CC},
	{'\u04D0', '\u04EA', LowercaseBor, 0},
	{'\u04EE', '\u04F4', LowercaseBor, 0},
	{'\u04F8', '\u04F8', LowercaseSet, 0x04F9},
	{'\u0531', '\u0556', LowercaseAdd, 48},
	{'\u10A0', '\u10C5', LowercaseAdd, 48},
	{'\u1E00', '\u1EF8', LowercaseBor, 0},
	{'\u1F08', '\u1F0F', LowercaseAdd, -8},
	{'\u1F18', '\u1F1F', LowercaseAdd, -8},
	{'\u1F28', '\u1F2F', LowercaseAdd, -8},
	{'\u1F38', '\u1F3F', LowercaseAdd, -8},
	{'\u1F48', '\u1F4D', LowercaseAdd, -8},
	{'\u1F59', '\u1F59', LowercaseSet, 0x1F51},
	{'\u1F5B', '\u1F5B', LowercaseSet, 0x1F53},
	{'\u1F5D', '\u1F5D', LowercaseSet, 0x1F55},
	{'\u1F5F', '\u1F5F', LowercaseSet, 0x1F57},
	{'\u1F68', '\u1F6F', LowercaseAdd, -8},
	{'\u1F88', '\u1F8F', LowercaseAdd, -8},
	{'\u1F98', '\u1F9F', LowercaseAdd, -8},
	{'\u1FA8', '\u1FAF', LowercaseAdd, -8},
	{'\u1FB8', '\u1FB9', LowercaseAdd, -8},
	{'\u1FBA', '\u1FBB', LowercaseAdd, -74},
	{'\u1FBC', '\u1FBC', LowercaseSet, 0x1FB3},
	{'\u1FC8', '\u1FCB', LowercaseAdd, -86},
	{'\u1FCC', '\u1FCC', LowercaseSet, 0x1FC3},
	{'\u1FD8', '\u1FD9', LowercaseAdd, -8},
	{'\u1FDA', '\u1FDB', LowercaseAdd, -100},
	{'\u1FE8', '\u1FE9', LowercaseAdd, -8},
	{'\u1FEA', '\u1FEB', LowercaseAdd, -112},
	{'\u1FEC', '\u1FEC', LowercaseSet, 0x1FE5},
	{'\u1FF8', '\u1FF9', LowercaseAdd, -128},
	{'\u1FFA', '\u1FFB', LowercaseAdd, -126},
	{'\u1FFC', '\u1FFC', LowercaseSet, 0x1FF3},
	{'\u2160', '\u216F', LowercaseAdd, 16},
	{'\u24B6', '\u24D0', LowercaseAdd, 26},
	{'\uFF21', '\uFF3A', LowercaseAdd, 32},
}

func ( *CharSet) (,  rune) {
	var , ,  int
	var ,  rune
	var  lcMap

	for ,  = 0, len(lcTable);  < ; {
		 = ( + ) / 2
		if lcTable[].chMax <  {
			 =  + 1
		} else {
			 = 
		}
	}

	for ;  < len(lcTable); ++ {
		 = lcTable[]
		if .chMin >  {
			return
		}
		 = .chMin
		if  <  {
			 = 
		}

		 = .chMax
		if  >  {
			 = 
		}

		switch .op {
		case LowercaseSet:
			 = rune(.data)
			 = rune(.data)
		case LowercaseAdd:
			 += .data
			 += .data
		case LowercaseBor:
			 |= 1
			 |= 1
		case LowercaseBad:
			 += ( & 1)
			 += ( & 1)
		}

		if  <  ||  >  {
			.ranges = append(.ranges, SingleRange{First: , Last: })
		}
	}
}

// Determines whether two sets could overlap.
func ( *CharSet) ( *CharSet) bool {
	// If the sets are identical, there's obviously overlap.
	if .Equals() {
		return true
	}

	// If either set is all-inclusive, there's overlap by definition (unless
	// the other set is empty, but that's so rare it's not worth checking.)
	if .IsAnything() || .IsAnything() {
		return true
	}

	// If one set is negated and the other one isn't, we're in one of two situations:
	// - The remainder of the sets are identical, in which case these are inverses of
	//   each other, and they don't overlap.
	// - The remainder of the sets aren't identical, in which case there's very likely
	//   overlap, and it's not worth spending more time investigating.
	 := .IsNegated()
	 := .IsNegated()
	if  !=  {

		return !.equals(, true)
	}

	// If the sets are negated, since they're not equal, there's almost certainly overlap.
	if  {
		return true
	}

	// Special-case some known, common classes that don't overlap.
	if knownDistinctSets(, ) || knownDistinctSets(, ) {
		return false
	}

	// If set2 can be easily enumerated (e.g. no unicode categories), then enumerate it and
	// check if any of its members are in set1.  Otherwise, the same for set1.
	if !.HasSubtraction() && len(.categories) == 0 {
		return mayOverlapByEnumeration(, )
	} else if !.HasSubtraction() && len(.categories) == 0 {
		return mayOverlapByEnumeration(, )
	}

	// Assume that everything else might overlap.  In the future if it proved impactful, we could be more accurate here,
	// at the exense of more computation time.
	return true
}

func knownDistinctSets(,  *CharSet) bool {

	return (.Equals(SpaceClass()) || .Equals(ECMASpaceClass())) &&
		(.Equals(DigitClass()) || .Equals(WordClass()) ||
			.Equals(ECMADigitClass()) || .Equals(ECMAWordClass()))

}

func mayOverlapByEnumeration(,  *CharSet) bool {
	for  := 0;  < len(.ranges); ++ {
		for  := .ranges[].First;  <= .ranges[].Last; ++ {
			if .CharIn() {
				return true
			}
		}
	}

	return false
}

// Gets all of the characters in the specified set, storing them into the provided span.
//
// Only considers character classes that only contain sets (no categories),
// just simple sets containing starting/ending pairs (subtraction from those pairs
// is factored in, however).The returned characters may be negated: if IsNegated(set)
// is false, then the returned characters are the only ones that match; if it returns
// true, then the returned characters are the only ones that don't match.
func ( *CharSet) ( int) []rune {
	// don't support categories, just ranges
	if len(.categories) > 0 || len(.ranges) >  {
		return nil
	}

	// Negation with subtraction is too cumbersome to reason about efficiently.
	if .IsNegated() && .HasSubtraction() {
		return nil
	}

	 := make([]rune, 0, )
	 := 0
	// Iterate through the pairs of ranges, storing each value in each range
	// into the supplied span.  If they all won't fit, we give up and return 0.
	// Otherwise we return the number found.  Note that we don't bother to handle
	// the corner case where the last range's upper bound is LastChar (\uFFFF),
	// based on it a) complicating things, and b) it being really unlikely to
	// be part of a small set.
	for ,  := range .ranges {
		// loop through each char in the range
		for  := .First;  <= .Last; ++ {

			// Keep track of how many characters we've checked. This could work
			// just comparing count rather than evaluated, but we also want to
			// limit how much work is done here, which we can do by constraining
			// the number of checks to the size of the storage provided.
			++
			if  >  {
				return nil
			}

			// If the set is all ranges but has a subtracted class,
			// validate the char is actually in the set prior to storing it:
			// it might be in the subtracted range.
			if .HasSubtraction() && !.CharIn() {
				continue
			}
			 = append(, )
		}
	}
	return 
}

func ( *CharSet) () []byte {
	 := &bytes.Buffer{}
	.mapHashFill()
	return .Bytes()
}

func ( *CharSet) ( *CharSet) bool {
	return .equals(, false)
}

func ( *CharSet) ( *CharSet,  bool) bool {
	if  == nil &&  == nil {
		return true
	}
	if  == nil &&  != nil ||  == nil &&  != nil {
		return false
	}

	if ! {
		if .negate != .negate {
			return false
		}
	}
	if .anything != .anything {
		return false
	}

	if !slices.Equal(.ranges, .ranges) {
		return false
	}
	if !slices.Equal(.categories, .categories) {
		return false
	}

	return .sub.(.sub, false)
}

var whitespaceChars = []rune{'\u0009', '\u000A', '\u000B', '\u000C', '\u000D',
	'\u0020', '\u0085', '\u00A0', '\u1680', '\u2000',
	'\u2001', '\u2002', '\u2003', '\u2004', '\u2005',
	'\u2006', '\u2007', '\u2008', '\u2009', '\u200A',
	'\u2028', '\u2029', '\u202F', '\u205F', '\u3000'}

// Gets whether the specified set is a named set with a reasonably small count
// of Unicode characters. Designed to help the regexp code generator choose a better
// search algo for finding chars
// Description is a short name that can be used as part of a var name in code gen
func ( *CharSet) () ( bool,  []rune,  bool,  string) {
	// figure out if we're SpaceClass, RE2SpaceClass, ECMASpaceClass or inverse
	// "hash" ourselves -- this is actually fully serialized, not just hashed
	if .IsSingleton() {
		return true, []rune{.SingletonChar()}, false, ""
	}
	if .IsSingletonInverse() {
		return true, []rune{.SingletonChar()}, true, ""
	}

	if .Equals(SpaceClass()) {
		// we're SpaceClass
		return true, whitespaceChars, false, "whitespace"
	}
	if .Equals(NotSpaceClass()) {
		// we're NotSpaceClass
		return true, whitespaceChars, true, "whitespace"
	}

	return false, nil, false, ""
}

// Gets whether the set description string is for two ASCII letters that case
// to each other under IgnoreCase rules.
func ( *CharSet) () (bool, []rune) {
	if .IsNegated() {
		return false, nil
	}
	// get up to 3 chars, just to be able to error on both "too many" and "too few"
	 := .GetSetChars(3)
	return len() == 2 && [0] < unicode.MaxASCII && [1] < unicode.MaxASCII &&
		([0]|0x20) == ([1]|0x20) &&
		unicode.IsLetter([0]) && unicode.IsLetter([1]), 
}

func anyParticipateInCaseConversion( []rune) bool {
	for ,  := range  {
		if participatesInCaseConversion() {
			return true
		}
	}
	return false
}

func participatesInCaseConversion( rune) bool {
	/*
		case UnicodeCategory.ClosePunctuation:
		case UnicodeCategory.ConnectorPunctuation:
		case UnicodeCategory.Control:
		case UnicodeCategory.DashPunctuation:
		case UnicodeCategory.DecimalDigitNumber:
		case UnicodeCategory.FinalQuotePunctuation:
		case UnicodeCategory.InitialQuotePunctuation:
		case UnicodeCategory.LineSeparator:
		case UnicodeCategory.OpenPunctuation:
		case UnicodeCategory.OtherNumber:
		case UnicodeCategory.OtherPunctuation:
		case UnicodeCategory.ParagraphSeparator:
		case UnicodeCategory.SpaceSeparator:
	*/
	return !unicode.In(, unicode.Pe, unicode.Pc, unicode.Cc, unicode.Pd, unicode.Nd, unicode.Pf,
		unicode.Pi, unicode.Zl, unicode.Ps, unicode.No, unicode.Po, unicode.Zp, unicode.Zs)
}

func anyParticipatesInCaseConversion( string) bool {
	for ,  := range  {
		if participatesInCaseConversion() {
			return true
		}
	}
	return false
}

func isAsciiRunes( []rune) bool {
	for  := 0;  < len(); ++ {
		if [] > unicode.MaxASCII {
			return false
		}
	}
	return true
}

func ( CharSet) ( int) []SingleRange {
	if len(.categories) > 0 {
		return nil
	}
	if .sub != nil {
		return nil
	}
	if len(.ranges) ==  {
		return .ranges[:]
	}
	return nil
}

func ( *CharSet) () ( []Category,  bool) {
	if .sub != nil {
		return nil, false
	}
	if len(.ranges) > 0 {
		return nil, false
	}
	// all cats need to be rationalized to the same negation or not
	if len(.categories) == 0 {
		return nil, false
	}

	 := .categories[0].Negate
	for ,  := range .categories {
		if  != .Negate || .Cat == SpaceCategoryText || .Cat == WordCategoryText {
			// negate some and not others...a problem
			// or one of our non-unicode categories
			return nil, false
		}
	}

	// tell the caller to negate if either all the categories
	// are negated or the set as a whole is negated, but not
	// both
	return .categories,  != .negate
}

type CharClassAnalysisResults struct {
	// true if the set contains only ranges; false if it contains Unicode categories and/or subtraction.
	OnlyRanges bool
	// true if we know for sure that the set contains only ASCII values; otherwise, false.
	// This can only be true if OnlyRanges is true.
	ContainsOnlyAscii bool
	// true if we know for sure that the set doesn't contain any ASCII values; otherwise, false.
	// This can only be true if OnlyRanges is true.
	ContainsNoAscii bool
	// true if we know for sure that all ASCII values are in the set; otherwise, false.
	// This can only be true if OnlyRanges is true.
	AllAsciiContained bool
	// true if we know for sure that all non-ASCII values are in the set; otherwise, false.
	// This can only be true if OnlyRanges is true.
	AllNonAsciiContained bool
	// The inclusive lower bound.
	// This is only valid if OnlyRanges is true.
	LowerBoundInclusiveIfOnlyRanges rune
	// The exclusive upper bound.
	// This is only valid if OnlyRanges is true.
	UpperBoundExclusiveIfOnlyRanges rune
}

// <summary>Analyzes the set to determine some basic properties that can be used to optimize usage.
func ( *CharSet) () CharClassAnalysisResults {
	// The analysis is performed based entirely on ranges contained within the set.
	// Thus, we require that it can be "easily enumerated", meaning it contains only
	// ranges (and more specifically those with both the lower inclusive and upper
	// exclusive bounds specified). We also permit the set to contain a subtracted
	// character class, as for non-negated sets, that can only narrow what's permitted,
	// and the analysis can be performed on the overestimate of the set prior to subtraction.
	// However, negation is performed before subtraction, which means we can't trust
	// the ranges to inform AllNonAsciiContained and AllAsciiContained, as the subtraction
	// could create holes in those.  As such, while we can permit subtraction for non-negated
	// sets, for negated sets, we need to bail.
	if (.IsNegated() && .HasSubtraction()) || len(.categories) > 0 || len(.ranges) == 0 {
		// We can't make any strong claims about the set.
		return CharClassAnalysisResults{}
	}

	 := .ranges[0].First
	 := .ranges[len(.ranges)-1].Last + 1

	if .IsNegated() {
		// We're negated: if the upper bound of the range is ASCII, that means everything
		// above it is actually included, meaning all non-ASCII are in the class.
		// Similarly if the lower bound is non-ASCII, that means in a negated world
		// everything ASCII is included.
		return CharClassAnalysisResults{
			OnlyRanges:                      true,
			AllNonAsciiContained:             <= unicode.MaxASCII,
			AllAsciiContained:                >= unicode.MaxASCII,
			ContainsNoAscii:                  == 0 && .ranges[0].Last >= unicode.MaxASCII,
			ContainsOnlyAscii:               false,
			LowerBoundInclusiveIfOnlyRanges: ,
			UpperBoundExclusiveIfOnlyRanges: ,
		}
	}

	// If the upper bound is ASCII, that means everything included in the class is ASCII.
	// Similarly if the lower bound is non-ASCII, that means no ASCII is in the class.
	return CharClassAnalysisResults{
		OnlyRanges:                      true,
		AllNonAsciiContained:            false,
		AllAsciiContained:                == 0 && .ranges[0].Last >= unicode.MaxASCII && !.HasSubtraction(),
		ContainsOnlyAscii:                <= unicode.MaxASCII,
		ContainsNoAscii:                  >= unicode.MaxASCII,
		LowerBoundInclusiveIfOnlyRanges: ,
		UpperBoundExclusiveIfOnlyRanges: ,
	}
}